Index: /trunk/ippTests/ippCheck/Makefile.am
===================================================================
--- /trunk/ippTests/ippCheck/Makefile.am	(revision 42085)
+++ /trunk/ippTests/ippCheck/Makefile.am	(revision 42085)
@@ -0,0 +1,26 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
+
+module_files = \
+  tap.pro   \
+  truth.pro     \
+  cammask.pro   \
+  camstats.pro  \
+  diffmask.pro  \
+  warpmask.pro  \
+  warpstats.pro \
+  smfcheck.pro
+
+ippchecksdir = $(datadir)/dvo/modules
+
+ippchecks_DATA = $(module_files)
+
+bin_SCRIPTS = ippCheck prcol
+
+install-data-hook:
+	chmod 0755 $(ippchecksdir)
+
+EXTRA_DIST = $(module_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: /trunk/ippTests/ippCheck/autogen.sh
===================================================================
--- /trunk/ippTests/ippCheck/autogen.sh	(revision 42085)
+++ /trunk/ippTests/ippCheck/autogen.sh	(revision 42085)
@@ -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=ippCheck
+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: /trunk/ippTests/ippCheck/cammask.pro
===================================================================
--- /trunk/ippTests/ippCheck/cammask.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/cammask.pro	(revision 42085)
@@ -0,0 +1,395 @@
+#!/usr/bin/env dvo
+
+# this module generates a table of mask fractions and matched detections for known sources
+# in the field of an exposure.  mask fractions are measured on the chip (or camera?) mask files
+# while the detections are derived from the camera-stage smf file
+
+# this module provides the following functions
+# get.camera.eff (camera) (expname) (camDgroup) (refsrcs.fits) : 
+# get.chip.detect.stats (chipname) (chipnseq)
+# get.chip.mask.stats (cammask)
+# get.chip.mask.files (camPath)
+# get.chip.metadata (camera) (chipID)
+# get.camera.metadata (camera) (expname) (camDgroup)
+
+# init.radec.range
+# set.radec.range
+
+$VERBOSE    = 1	       
+$COPYMASKS  = 0
+$OUTDIR     = .
+$OUTVERSION = XXX
+
+if (not($?MATCH_RADIUS)) set MATCH_RADIUS = 2.0
+
+$SMF_FIELDS_CAM = X_PSF Y_PSF RA_PSF DEC_PSF CAL_PSF_MAG PSF_QF PSF_QF_PERFECT
+$REF_FIELDS_CAM = Rref Dref Mref 
+$XTR_FIELDS_CAM = Xref Yref Nref Q1ref
+
+macro get.camera.eff
+ if ($0 != 5)
+  echo "USAGE: get.camera.eff (camera) (expname) (camDgroup) (refsrcs.fits)"
+  echo "EX:    get.camera.eff gpc1 o9322g0253o EU.nightlyscience filename.fits"
+  break
+ end
+
+ local i dbname expname camdgrp output
+ $dbname   = $1
+ $expname  = $2
+ $camdgrp  = $3
+ $refsrcs  = $4
+
+ $OUTDIR = $expname.stats
+ mkdir $OUTDIR
+
+ # I have a table of sources which may be in the exposure
+ # I need to generate a matched table of all sources in the smf tables
+ # AND the mask fraction (roughly psf_qf) for each detection 
+
+ # get the name of the smf file:
+ get.camera.metadata $dbname $expname $camdgrp
+ $smffile = $camPath.smf     
+ $smfreal = `neb-locate -p $smffile`
+ 
+ get.chip.metadata $dbname $chipID
+ get.chip.mask.files $camPath
+ 
+ for i 0 $camMask:n
+   echo $chipName:$i $camMask:$i
+ end
+
+ # load the reference source table
+ # NOTE: Mref is chosen to match filter for this exposure
+ data $refsrcs; 
+ read -fits DATA -sizes
+ if ($table:Ny == 0)
+  echo "ERROR: no reference sources found in $refsrcs"
+  break
+ end
+ 
+ read -fits DATA $REF_FIELDS_CAM
+ set Nref = ramp(Rref)
+
+ # read in the PHU header
+ rd PHU $smfreal
+
+ # create the chipName list from the smffile and calculate the index numbers.
+ # NOTE: if a chip is missing from the smf, this may be inconsistent with chipMask:n
+ # XXX for now, we fail is this happens
+ list chipsRaw -x "ftable -list $smfreal | grep -n hdr | prcol 1 | sed s/.hdr//"
+ for i 0 $chipsRaw:n
+  list word -splitbychar : $chipsRaw:$i
+  $chipNseq:$i = $word:0 - 3; # this number depends on the number of leading tables 
+  if ("$chipName:$i" != "$word:1")
+    echo "ERROR: missing chip from smffile: " $chipName:$i
+    break
+  end
+ end
+
+  # delete any existing output vectors
+  foreach field $SMF_FIELDS_CAM $REF_FIELDS_CAM $XTR_FIELDS_CAM
+    delete -q $field\_out
+  end
+
+ # find matching entries in smf file 
+ for i 0 $chipName:n
+   get.chip.detect.stats $chipName:$i $chipNseq:$i
+   get.chip.mask.stats $camMask:$i
+
+   foreach field $SMF_FIELDS_CAM
+     concat $field\_found $field\_out
+     delete $field\_found
+   end
+   foreach field $REF_FIELDS_CAM $XTR_FIELDS_CAM
+     concat $field\_onchp $field\_out
+     delete $field\_onchp
+   end
+ end
+
+ $OUT_FIELDS = 
+ foreach field $SMF_FIELDS_CAM $REF_FIELDS_CAM $XTR_FIELDS_CAM
+   $OUT_FIELDS = $OUT_FIELDS $field\_out
+ end
+
+ sprintf output "%s/%s.%s.cam.stf" $OUTDIR $expname $OUTVERSION
+ write -fits DATA $output $OUT_FIELDS
+
+ # save this name for warp and diff analysis, if desired
+ $CAMSOURCE = $output
+end
+
+# We can read the data by EXTNAME or sequence number. Reading by
+# EXTNAME is more robust against format changes but slow
+$USE_EXTNAME = 0
+
+macro get.chip.detect.stats
+  if ($0 != 3) 
+   echo "USAGE: get.chip.detect.stats (chipname) (chipnseq)"
+   echo "  assumes existence of $smfreal vectors (Rref, Dref, Mref) and buffer (PHU)"
+   echo "  matches known sources to detections in the smf file extensions for each chip"
+   echo "  calculates detection fraction as a function of magnitude"
+   echo "  also astrometric and photometric scatter vs magnitude for good detections"
+   break
+  end
+
+  local chipname chipnseq field NAXIS1 NAXIS2
+  $chipname = $1
+  $chipnseq = $2
+
+  # read in the CHIP header
+
+  if ($USE_EXTNAME) 
+    sprintf extname "%s.hdr" $chipname
+    rd -n $extname hdr $smfreal
+  else
+    rd -x $chipnseq hdr $smfreal
+  end
+
+  keyword hdr NAXIS1 NAXIS1
+  keyword hdr NAXIS2 NAXIS2
+
+  coords -q hdr -mosaic PHU -p       0       0; init.radec.range
+  coords -q hdr -mosaic PHU -p $NAXIS1       0; set.radec.range
+  coords -q hdr -mosaic PHU -p $NAXIS1 $NAXIS2; set.radec.range
+  coords -q hdr -mosaic PHU -p       0 $NAXIS2; set.radec.range
+
+  # generate a subset which could be on this chip
+  # do we need to grow the range at all?
+  set keep = (Rref >= $minR) && (Rref <= $maxR) && (Dref >= $minD) && (Dref <= $maxD) 
+  foreach field $REF_FIELDS_CAM Nref
+    subset $field\_tmp = $field if keep
+  end
+
+  # convert the subset coordinates to pixels
+  set Xref_tmp = Rref_tmp
+  set Yref_tmp = Dref_tmp
+  coords -q hdr -mosaic PHU -c Xref_tmp Yref_tmp
+
+  # generate a subset of sources which are within the boundaries of the chip
+  set keep = (Xref_tmp >= 0) && (Xref_tmp <= $NAXIS1) && (Yref_tmp >= 0) && (Yref_tmp <= $NAXIS2)
+  foreach field $REF_FIELDS_CAM Nref Xref Yref
+    subset $field\_onchp = $field\_tmp if keep
+    delete $field\_tmp
+  end
+
+  # now read the smf detections
+  data $smfreal
+
+  if ($USE_EXTNAME) 
+    sprintf extname "%s.psf" $chipname
+    read -fits $extname $SMF_FIELDS_CAM
+  else
+    read -fits {$chipnseq+1} -extnum $SMF_FIELDS_CAM
+  end
+
+  # match radius currently needs to be larger for MPC objects
+  match2d Xref_onchp Yref_onchp X_PSF Y_PSF $MATCH_RADIUS -closest
+  foreach field $SMF_FIELDS_CAM
+    reindex $field\_found = $field using index1 -keep-unmatched
+  end
+end
+
+macro get.chip.mask.stats
+  if ($0 != 2) 
+   echo "USAGE: get.chip.mask.stats (cammask)"
+   echo "  assumes existence of source chip positions (Xref_onchp, Yref_onchp)"
+   break
+  end
+
+  local cammask camreal
+  $cammask = $1
+
+  $camreal = `neb-locate -p $cammask`
+  rd -x 0 mk $camreal 
+
+  # I have a mask file, now I need to calculate something equiv to psfQf
+  set mkPerfect = (mk == 0) ? zero(mk) + 1 : zero(mk)
+
+  # I want to set an image based on the bit values but I fear
+  # the mask has been converted to a float when read
+  forcedphot mkPerfect Xref_onchp Yref_onchp -output Q1ref_onchp
+end
+
+### utilities
+macro init.radec.range
+  $minR = $RA
+  $minD = $DEC
+  $maxR = $RA
+  $maxD = $DEC
+end
+
+macro set.radec.range
+  $minR = min($minR, $RA)
+  $minD = min($minD, $DEC)
+  $maxR = max($maxR, $RA)
+  $maxD = max($maxD, $DEC)
+end
+
+# get the file names of the chip mask files.
+# these are actually the masks from the camera stage which
+# has the dynamic masking applied
+macro get.chip.mask.files
+ if ($0 != 2)
+  echo "USAGE: get.chip.mask.files (camPath)"
+  echo " uses chipName:0 - chipName:n"
+  echo " sets camMask:0  - camMask:n"
+  break
+ end
+
+ local pathbase i j mychip Nch
+ $pathbase = $1
+
+ # get the full list from nebulous (faster than getting each one at a time)
+ list tmpName -x "neb-ls $pathbase.%.mk.fits"
+
+ # the list of masks might not match the list of chips
+ $j = 0
+ for i 0 $chipName:n
+   list word -splitbychar . $tmpName:$j
+   $Nch = $word:n - 3
+   $mychip = $word:$Nch
+   if ($mychip != $chipName:$i) 
+    $camMask:$i = NONE
+    continue
+   end
+   $camMask:$i = $tmpName:$j
+   if ($VERBOSE) echo "found chipMask $camMask:$i"
+   if ($COPYMASKS) exec neb-copy $camMask:$i $OUTDIR
+   $j ++
+ end
+ $camMask:n = $chipName:n
+end
+
+macro get.chip.metadata
+ if ($0 != 3)
+  echo "USAGE: get.chip.metadata (camera) (chipID)"
+  echo "EX:    get.chip.metadata gpc1 NNNN"
+  echo "  sets camPath, chipID"
+  break
+ end
+
+ local dbname chipID
+ $dbname  = $1
+ $chipID  = $2
+
+ $command = chiptool -dbname $dbname -processedimfile -chip_id $chipID
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  return
+ end
+ 
+ local i
+ for i 0 $npages 
+   sprint pagename page.%03d $i
+   book getword mybook $pagename class_id -var chipName:$i
+ end
+ $chipName:n = $npages
+
+ book delete mybook
+end
+
+macro get.camera.metadata
+ if ($0 != 4)
+  echo "USAGE: get.camera.metadata (camera) (expname) (camDgroup)"
+  echo "EX:    get.camera.metadata gpc1 o9322g0253o EU.nightlyscience"
+  echo "  sets camPath, chipID"
+  break
+ end
+
+ local dbname expname camdgrp
+ $dbname  = $1
+ $expname = $2
+ $camdgrp = $3
+
+# need to generically handle the question of which version / label
+# this will be a problem for old exposures without a useful label (cleanup)
+# instead of label, we could specify the cam_id (which we might know)
+
+ $command = camtool -dbname $dbname -processedexp -exp_name $expname -data_group $camdgrp
+#$command = camtool -dbname $dbname -processedexp -exp_name $expname
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ $camPath = NONE
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  next
+ end
+ 
+ if ($npages > 1) 
+  echo "ERROR: too many exposures : $command"
+  book delete mybook
+  next
+ end
+ 
+ sprint pagename page.%03d 0
+ book getword mybook $pagename path_base -var camPath
+ book getword mybook $pagename chip_id   -var chipID
+ book getword mybook $pagename cam_id    -var camID
+ book delete mybook
+end
+
+
+#### this test macro generates the match table without measuring the mask fraction
+#### XXX the output formats are not consistent with the above version
+
+macro get.camera.matches
+ if ($0 != 5)
+  echo "USAGE: get.camera.matches (smfreal) (refsrcs.fits) (outroot) (outversion)"
+  break
+ end
+
+ local i refsrcs 
+ $smfreal = $1
+ $refsrcs = $2
+ $outroot = $3
+ $OUTVERSION = $4
+
+ dirname $outroot -var OUTDIR
+ mkdir $OUTDIR
+
+ # load the reference source table
+ # NOTE: Mref is chosen to match filter for this exposure
+ data $refsrcs; read -fits DATA $REF_FIELDS_CAM
+ set Nref = ramp(Rref)
+
+ # read in the PHU header
+ rd PHU $smfreal
+
+ # create the chipName list from the smffile and calculate the index numbers
+ list chipsRaw -x "ftable -list $smfreal | grep -n hdr | prcol 1 | sed s/.hdr//"
+ for i 0 $chipsRaw:n
+  list word -splitbychar : $chipsRaw:$i
+  $chipNseq:$i = $word:0 - 3
+  $chipName:$i = $word:1
+ end
+ $chipName:n = $chipsRaw:n
+ $chipNseq:n = $chipsRaw:n
+
+ # find matching entries in smf file 
+ for i 0 $chipName:n
+   echo $chipName:$i
+   get.chip.detect.stats $chipName:$i $chipNseq:$i
+   # if we do not have masks, we have not measured Q1refSS, just set to 1.0
+   set Q1ref_onchp = zero(Rref_onchp) + 1.0
+
+ end
+
+ $REF_FIELDS_OUT = $REF_FIELDS Xref Yref Q1ref
+
+ sprintf output "%s.%s.%s.cam.stf" $outroot $chipName:$i $OUTVERSION
+ write -fits DATA $output RrefSS DrefSS MrefSS XrefSS YrefSS Q1refSS X_PSF_found Y_PSF_found M_PSF_found RA_PSF_found DEC_PSF_found Q1_PSF_found Q2_PSF_found 
+
+end
+
Index: /trunk/ippTests/ippCheck/camstats.pro
===================================================================
--- /trunk/ippTests/ippCheck/camstats.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/camstats.pro	(revision 42085)
@@ -0,0 +1,419 @@
+# load.camstats (glob) : load data from a set of stf files and calculate detection histograms
+# plot.camstats.resid  : plot photometric and astrometric residuals
+# plot.camstats.fracs  : plot detection fraction histograms vs magnitude
+
+# calc.camstats : calculate detection histograms for a single set of data
+
+# UTILITIES:
+# legend.init
+# legend.line
+
+macro load.camstats
+ if ($0 != 2)
+   echo "USAGE: load.camstats (glob)"
+   break
+ end
+ 
+ # accumulate these fields for comparison plots
+ $FIELDS = RA_PSF DEC_PSF CAL_PSF_MAG PSF_QF Rref Dref Mref
+
+ # delete accumulation vectors
+ delete -q $FIELDS
+
+ list name -glob $1
+ if ($name:n == 0)
+   echo "ERROR: no files found matching $1"
+   break
+ end
+
+ for i 0 $name:n
+
+   echo "reading from $name:$i"
+   data $name:$i
+   read -fits DATA -sizes
+   if ($table:Ny == 0) continue
+   read -fits DATA -list-fields -q; list tfields -join allfields; read -fits DATA $allfields
+
+   # assumes data has been loaded 
+   # results in magFound, fracFound, numFound
+   calc.camstats
+   if ($i == 0)
+     set numTotal_sum = numTotal
+     set numFound_sum = numFound
+     set numPoss_sum  = numPoss
+     set numGood_sum  = numGood
+   else
+     set numTotal_sum = numTotal + numTotal_sum
+     set numFound_sum = numFound + numFound_sum
+     set numPoss_sum  = numPoss  + numPoss_sum
+     set numGood_sum  = numGood  + numGood_sum
+   end
+   foreach field $FIELDS
+     concat $field\_out $field
+   end
+
+   # cleanup by deleting the loaded vectors
+   delete -q $allfields
+ end
+   
+ set numTotal = numTotal_sum
+ set numFound = numFound_sum
+ set numPoss  = numPoss_sum
+ set numGood  = numGood_sum
+ delete numTotal_sum numFound_sum numPoss_sum numGood_sum
+
+ set fracFound = (numTotal > 0) ? numFound / numTotal : zero(numTotal)
+ set fracPoss  = (numTotal > 0) ? numPoss  / numTotal : zero(numTotal)
+ set fracGood  = (numTotal > 0) ? numGood  / numTotal : zero(numTotal)
+end
+
+if (not($?HISTBIN)) set HISTBIN = 0.01
+if (not($?BRIGHTMIN)) set BRIGHTMIN = 15.0
+if (not($?BRIGHTMAX)) set BRIGHTMAX = 17.0
+macro plot.camstats.resid
+
+  clear -s
+  resize 1800 1800
+  label -fn helvetica 18
+  $BST1 = -tickpad 0.2 -xpad 3.5 -labelpadx 3.0 -ypad 4.5 -labelpady 4.0 +ypad 0.5 +labelpady 0.0
+  $BST2 = -tickpad 0.2 -xpad 3.5 -labelpadx 3.0 -ypad 0.5 -labelpady 0.0 +ypad 4.5 +labelpady 4.0 -labels 1001
+  
+  section aM 0.00 0.66 0.65 0.33
+  section aR 0.00 0.33 0.65 0.33
+  section aD 0.00 0.00 0.65 0.33
+
+  section bM 0.65 0.66 0.35 0.33
+  section bR 0.65 0.33 0.35 0.33
+  section bD 0.65 0.00 0.35 0.33
+
+  set dR = 3600*(RA_PSF - Rref)*dcos(Dref)
+  set dD = 3600*(DEC_PSF - Dref)
+  set dM = CAL_PSF_MAG - Mref
+
+  set keepGood = (PSF_QF > 0.85)
+  set keepBright = keepGood && (Mref > $BRIGHTMIN) && (Mref < $BRIGHTMAX)
+
+  subset dRs = dR where keepBright
+  subset dDs = dD where keepBright
+  subset dMs = dM where keepBright
+
+  # allow mag and pos histograms to have different bin sizes?
+  histogram dRs NdRs {-50*$HISTBIN} {50*$HISTBIN} $HISTBIN -range dx
+  histogram dDs NdDs {-50*$HISTBIN} {50*$HISTBIN} $HISTBIN -range dx
+  histogram dMs NdMs {-50*$HISTBIN} {50*$HISTBIN} $HISTBIN -range dx
+
+  NdRs[0] = 0; NdRs[-1] = 0
+  NdDs[0] = 0; NdDs[-1] = 0
+  NdMs[0] = 0; NdMs[-1] = 0
+
+  $C3 = 0.0
+
+  peak -q dx NdRs; $C0 = $peakpos; $C2 = $peaknum; $C1 = 2*$HISTBIN; vgauss -q dx NdRs con NdRf; $SigmaRA  = abs($C1)
+  peak -q dx NdDs; $C0 = $peakpos; $C2 = $peaknum; $C1 = 2*$HISTBIN; vgauss -q dx NdDs con NdDf; $SigmaDec = abs($C1)
+  peak -q dx NdMs; $C0 = $peakpos; $C2 = $peaknum; $C1 = 2*$HISTBIN; vgauss -q dx NdMs con NdMf; $SigmaMag = abs($C1)
+
+  section aM; lim 13 23 {-50*$HISTBIN} {50*$HISTBIN}; box $BST1
+  plot CAL_PSF_MAG dM -pt box -sz 0.5 -op 0.2 where keepGood
+  label -x mag -y "&sd&h mag"
+  
+  section aR; lim 13 23 {-50*$HISTBIN} {50*$HISTBIN}; box $BST1
+  plot CAL_PSF_MAG dR -pt box -sz 0.5 -op 0.2 where keepGood
+  label -x mag -y "&sd&h R.A."
+  
+  section aD; lim 13 23 {-50*$HISTBIN} {50*$HISTBIN}; box $BST1
+  plot CAL_PSF_MAG dD -pt box -sz 0.5 -op 0.2 where keepGood
+  label -x mag -y "&sd&h Dec"
+  
+  section bM; lim dx NdMs; box $BST2
+  plot dx NdMs -x hist -lw 3
+  plot dx NdMf -x line -c red -lw 2
+  label -x "&sd&h mag" +y "N ($BRIGHTMIN < mag < $BRIGHTMAX)"
+  sprintf line "&ss&h (mag) = %.0f mmag" {1000*$SigmaMag}; textline -frac 0.02 0.95 -justify 5 "$line"
+  
+  section bR; lim dx NdRs; box $BST2
+  plot dx NdRs -x hist -lw 3
+  plot dx NdRf -x line -c red -lw 2
+  label -x "&sd&h R.A" +y "N ($BRIGHTMIN < mag < $BRIGHTMAX)"
+  sprintf line "&ss&h (mag) = %.0f mas" {1000*$SigmaRA}; textline -frac 0.02 0.95 -justify 5 "$line"
+  
+  section bD; lim dx NdDs; box $BST2
+  plot dx NdDs -x hist -lw 3
+  plot dx NdDf -x line -c red -lw 2
+  label -x "&sd&h Dec" +y "N ($BRIGHTMIN < mag < $BRIGHTMAX)"
+  sprintf line "&ss&h (Dec) = %.0f mas" {1000*$SigmaDec}; textline -frac 0.02 0.95 -justify 5 "$line"
+end
+
+# assumes we have loaded values from the stf tables
+# magBin, numTotal, numFound, numPoss, numGood
+macro plot.camstats.fracs
+
+ clear -s
+ resize 1800 1200
+ label -fn helvetica 18
+ $BSTY = -tickpad 0.2 -xpad 3.5 -labelpadx 3.0 -ypad 4.5 -labelpady 4.0 +ypad 4.5 +labelpady 4.0
+
+ ## calculate the rough detection threshold (50%)
+ # find the bin with the most detections
+ peak -q magBin numFound
+
+ # search backwards along the fracFound curve until it crosses 50%
+ break -auto off
+ $threshold = 0.0
+ threshold -q -r magBin fracFound 0.5 -range $peaknum {magBin[]-1}
+ break -auto on
+ $MagLim = $threshold
+
+ # fraction error bars
+ set dfracFound = sqrt(numFound) / numTotal
+ set dfracGood  = sqrt(numGood)  / numTotal
+
+ # plot the detection fractions, using left axis
+ section a 0 0 1 1
+ lim magBin -0.02 1.02; box $BSTY -ticks 1110;
+
+ plot -x hist magBin fracPoss  -lw 3 -c blue70
+ plot -x hist magBin fracFound -lw 3 -c red70
+ plot -x hist magBin fracGood  -lw 3 -c red
+
+ subset tmp = fracPoss  where (magBin > 15) && (magBin < 20); vstat -q tmp; $FracBrightPoss  = $MEDIAN
+ subset tmp = fracFound where (magBin > 15) && (magBin < 20); vstat -q tmp; $FracBrightFound = $MEDIAN
+ subset tmp = fracGood  where (magBin > 15) && (magBin < 20); vstat -q tmp; $FracBrightGood  = $MEDIAN
+
+ line -c blue70 -lt dot -lw 2 100 $FracBrightPoss  to 0 $FracBrightPoss
+ line -c red70  -lt dot -lw 2 100 $FracBrightFound to 0 $FracBrightFound
+ line -c red    -lt dot -lw 2 100 $FracBrightGood  to 0 $FracBrightGood
+
+ fprintf "%.1f-pct of all bright sources were detected unmasked" {100*$FracBrightGood} 
+ fprintf "%.1f-pct of unmasked bright sources were found" {100*$FracBrightFound / $FracBrightPoss}
+ fprintf "%.1f-pct of unmasked bright sources were detected unmasked" {100*$FracBrightGood / $FracBrightPoss}
+
+ fprintf "mag limit %.1f" $MagLim
+ sprintf line "mag limit: %.1f" $MagLim
+ textline -frac 0.95 0.95 -justify 3 "$line"
+
+ label -x mag -y "detected fraction"
+
+ # plot the total number of detected sources, using the right axis
+ section b 0 0 1 1
+ lim magBin numTotal; box $BSTY -ticks 1011 -labels 0001
+
+ plot -x hist magBin numFound -c grey50 -lw 2
+ plot -x hist magBin numTotal -c grey80 -lw 2
+
+ line -c red -lt dot -lw 2 $MagLim -100 to $MagLim 10000
+ label +y "detected number"
+
+ vstat -q numTotal; $Ntotal = $TOTAL
+ vstat -q numFound; $Nfound = $TOTAL
+ vstat -q numGood;  $Ngood  = $TOTAL
+ 
+ fprintf "%d of %d total sources detected, %d unmasked" $Nfound $Ntotal $Ngood
+
+ legend.init 0.02 0.95 0.03 0.02
+ sprintf line "unmasked source positions (psfqf > 0.85) [%.1f pct]" {100*$FracBrightPoss};  legend.line blue  3 solid "$line"
+ sprintf line "all sources detected [%.1f pct]"                     {100*$FracBrightFound}; legend.line red70 3 solid "$line"
+ sprintf line "sources detected with psfqf > 0.85 [%.1f pct]"	    {100*$FracBrightGood};  legend.line red   3 solid "$line"
+end
+
+macro legend.init
+ if ($0 != 5)
+  echo "USAGE: legend.init (X) (Y) (dX) (dY)"
+  break
+ end
+ 
+ $legend:X = $1; $legend:dX = $3
+ $legend:Y = $2; $legend:dY = $4
+end
+
+macro legend.line
+ if ($0 != 5)
+  echo "USAGE: legend.init (color) (lweight) (ltype) (label)"
+  break
+ end
+
+ local color myline mylt mylw
+ 
+ $color  = $1
+ $mylt   = $2
+ $mylw   = $3
+ $myline = $4
+
+ line -frac $legend:X $legend:Y to {$legend:X + $legend:dX} $legend:Y -c $color -lw $mylw -lt $mylt
+ textline -frac -justify 5 {$legend:X + $legend:dX + 0.01} $legend:Y "$myline"
+ $legend:Y -= $legend:dY
+end
+
+# generate histograms as a function of magnitude for the following:
+# number of sources (numTotal)
+# number of sources which were not masked, possibly detected (numPoss)
+# number detected (numFound)
+# number of detections with good quality (numGood)
+
+# relies on the following vectors: Mref_out, Q1ref_out, PSF_QF_out, X_PSF_out
+macro calc.camstats
+ $Mmin = 10
+ $Mmax = 25
+ $Mdel = 0.25
+ 
+ delete -q  magBin numTotal numPoss numFound numGood
+ for mag $Mmin $Mmax $Mdel
+   concat $mag magBin
+
+   # sources in mag bin
+   set keepSrc = (Mref_out > $mag - 0.5*$Mdel) && (Mref_out <= $mag + 0.5*$Mdel)
+   subset tmp = X_PSF_out if keepSrc
+   concat tmp[] numTotal
+
+   # NOTE: X_PSF_out is NAN if the source is not found (detected in smf), finite otherwise
+   # vstat ignores NAN values, so NPTS is count of non-NAN values
+   vstat -q tmp
+   concat $NPTS numFound
+
+   # what fraction of the sources could have been detected (mask fraction > 0.85 at position)?
+   set keepMsk = keepSrc && (Q1ref_out > 0.85)
+   subset tmp = X_PSF_out if keepMsk
+   concat tmp[] numPoss
+
+   # what fraction of the sources were actually detected with good quality?
+   set keepDet = keepSrc && (PSF_QF_out > 0.85) && not(isnan(PSF_QF_out))
+   subset tmp = X_PSF_out if keepDet
+   concat tmp[] numGood
+ end
+
+ delete -q tmp keepSrc keepMsk keepDet
+end
+
+############ macros used to test mpcorb_predict
+
+macro plot.camsky
+
+  dev -n sky; 
+  resize 1800 1800
+  region 194.650035  22.219991 2.5;
+  imbox o9401g0123o.stats/o9401g0123o.1808871.cm.2432793.smf
+
+  set RrefCam = Rref
+  set DrefCam = Dref
+  set Q1refCam = Q1ref
+  cplot -c blue -op 1.0 -pt cir -sz 2 RrefCam DrefCam;
+  cplot -c red  -op 0.5 -pt cir -sz 2 RrefCam DrefCam where (Q1refCam > 0.85)
+
+  data o9401g0123o.stats/o9401g0123o.mpc.fits
+  read -fits DATA Rref Dref
+  cplot -c black -pt box -sz 1 -op 0.3 Rref Dref
+
+  cplot -c blue -op 1.0 -pt box -sz 3.0 RrefCam DrefCam where (not(isnan(Xpsf)))
+  cplot -c red  -op 1.0 -pt box -sz 2.5 RrefCam DrefCam where (not(isnan(Xpsf)) && (Q1psf > 0.85))
+end
+
+macro test.mpcfine
+  if ($0 != 2)
+    echo "USAGE: test.mpcfine (file)"
+    break
+  end
+
+  data $1
+  read Rr 1 Dr 2 Rc 4 Dc 5 Rp 7 Dp 8 Ra 10 Da 11 Rd 13 Dd 14
+  set dRc = 3600*(Rc - Rr)*dcos(Dr)
+  set dDc = 3600*(Dc - Dr)
+  set dRp = 3600*(Rp - Rc)*dcos(Dr)
+  set dDp = 3600*(Dp - Dc)
+  set dRa = 3600*(Ra - Rp)*dcos(Dr)
+  set dDa = 3600*(Da - Dp)
+  set dRd = 3600*(Rd - Ra)*dcos(Dr)
+  set dDd = 3600*(Dd - Da)
+
+  vstat dRc
+  vstat dRp
+  vstat dRa
+  vstat dRd
+
+end
+
+macro test.mpcorb
+  if ($0 != 3)
+    echo "USAGE: test.mpcorb (version) (options)"
+    break
+  end
+
+  local Version
+  $Version = $1
+  $Options = $2
+
+  echo mpcorb_predict moment trange.59396.v2.fits 59375.31398 {200.738197150704-2} {200.738197150704+2} {6.13117829483755-2} {6.13117829483755+2} o9375g0065o.am$Version.fits
+  exec mpcorb_predict moment trange.59396.v2.fits 59375.31398 {200.738197150704-2} {200.738197150704+2} {6.13117829483755-2} {6.13117829483755+2} o9375g0065o.am$Version.fits
+  get.camera.matches o9375g0065o.1795013.cm.2420887.smf o9375g0065o.am$Version.fits o9375g0065o.t1/o9375g0065o.t1 am$Version
+  load.camstats.glob o9375g0065o.t1/o9375g0065o.t1.XY*.am$Version.cam.stf
+
+  set dX$Version = Xpsf - Xref
+  set dY$Version = Ypsf - Yref
+
+  set dR$Version = 3600*(Rpsf - Rref)*dcos(Dref)
+  set dD$Version = 3600*(Dpsf - Dref)
+
+  plot -pt cir -sz 2 -op 0.3 dR$Version dD$Version
+end
+
+macro replot.mpcorb
+  if ($0 != 3)
+    echo "USAGE: test.mpcorb (version) (color)"
+    break
+  end
+
+  plot -pt cir -sz 2 -op 0.3 dR$1 dD$1 -c $2
+end
+
+macro load.camstats.glob.old
+ if ($0 != 2)
+   echo "USAGE: load.camstats.glob (glob)"
+   break
+ end
+ 
+ delete -q Rref Dref Mref Xref Yref Q1ref Xpsf Ypsf Rpsf Dpsf Mpsf Q1psf
+
+ list name -glob $1
+ for i 0 $name:n
+
+   data $name:$i
+   read -fits DATA -sizes
+   if ($table:Ny == 0) continue
+   read -fits DATA -list-fields -q; list tfields -join allfields; read -fits DATA $allfields
+
+   concat RrefSS Rref
+   concat DrefSS Dref
+   concat XrefSS Xref
+   concat YrefSS Yref
+   concat MrefSS Mref
+   concat Q1refSS Q1ref
+   concat X_PSF_found Xpsf
+   concat Y_PSF_found Ypsf
+   concat RA_PSF_found Rpsf
+   concat DEC_PSF_found Dpsf
+   concat M_PSF_found Mpsf
+   concat Q1_PSF_found Q1psf
+ end
+end
+
+# relies on the following vectors: Mref_out, Q1ref_out, PSF_QF_out, X_PSF_out
+macro test.camstats
+ $Mmin = 10
+ $Mmax = 25
+ $Mdel = 0.25
+ 
+ delete -q  magBin numTotal numFound
+ for mag $Mmin $Mmax $Mdel
+   concat $mag magBin
+
+   # sources in mag bin
+   set keepSrc = (MrefDiff_onchp > $mag - 0.5*$Mdel) && (MrefDiff_onchp <= $mag + 0.5*$Mdel)
+   subset tmp = RpsfDiff_onchp if keepSrc
+   concat tmp[] numTotal
+
+   # NOTE: X_PSF_out is NAN if the source is not found (detected in smf), finite otherwise
+   # vstat ignores NAN values, so NPTS is count of non-NAN values
+   vstat -q tmp
+   concat $NPTS numFound
+ end
+
+ delete -q tmp keepSrc
+end
Index: /trunk/ippTests/ippCheck/configure.ac
===================================================================
--- /trunk/ippTests/ippCheck/configure.ac	(revision 42085)
+++ /trunk/ippTests/ippCheck/configure.ac	(revision 42085)
@@ -0,0 +1,32 @@
+AC_PREREQ(2.59)
+
+AC_INIT([ippCheck], [1.1.0], [ipp-dev@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([ippCheck])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+dnl Not sure what this provides:
+IPP_STDLDFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl we use PSLIB, but not PSMODULE or any other libraries
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+dnl Not sure what this provides:
+IPP_STDOPTS
+IPP_STDCFLAGS
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /trunk/ippTests/ippCheck/diffmask.pro
===================================================================
--- /trunk/ippTests/ippCheck/diffmask.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/diffmask.pro	(revision 42085)
@@ -0,0 +1,335 @@
+#!/usr/bin/env dvo
+
+$VERBOSE    = 1	       
+$COPYMASKS  = 0
+$OUTDIR     = .
+if (not($?OUTVERSION))
+  $OUTVERSION = XXX
+end
+
+$DIFF_MODE_WW = 1
+$DIFF_MODE_WS = 2
+
+if (not($?MATCH_RADIUS)) set MATCH_RADIUS = 2.0
+if (not($?USE_CAMSOURCE)) set USE_CAMSOURCE = 0
+
+$SMF_FIELDS_DIF = X_PSF Y_PSF RA_PSF DEC_PSF CAL_PSF_MAG PSF_QF PSF_QF_PERFECT
+$REF_FIELDS_DIF = Rref Dref Mref 
+$XTR_FIELDS_DIF = Xref Yref Nref Q1ref
+
+macro get.diff.eff
+ if ($0 != 5)
+  echo "USAGE: get.diff.eff (camera) (expname) (warp_id) (refsrcs.fits)"
+  echo "EX:    get.diff.eff gpc1 o9322g0253o 2367603 filename.fits"
+
+  # o9395g0075o : warp_id : 2399439, diff_id : 2156044
+  break
+ end
+
+ local dbname expname camID
+ $dbname   = $1
+ $expname  = $2
+ $warpID   = $3
+ $refsrcs  = $4
+
+  $OUTDIR = $expname.stats
+  mkdir $OUTDIR
+
+  # I have a table of sources which may be in the exposure
+  # I need to measure the mask fraction for each detection in the diffs
+  # also measure forced photometry in the diffs?  We do not actually generate a table of diff detections
+
+  # difftool -listrun to get the diff_id
+  # difftool -diffed -diff_id diffID to get the list of skyfile paths
+
+  # get the diffID for the appropriate run:
+  get.diff.run $dbname $expname $warpID $DIFF_MODE_WW
+  get.diff.skyfiles $dbname $diffID
+
+  get.diff.files
+  
+  # load the reference source table
+  # how to choose the name of the filter?
+  data $refsrcs; 
+  if ($USE_CAMSOURCE) 
+   read -fits DATA Rref_out Dref_out Mref_out
+   set Rref = Rref_out
+   set Dref = Dref_out
+   set Mref = Mref_out
+  else
+    read -fits DATA $REF_FIELDS_DIF
+  end
+
+  set Nref = ramp(Rref)
+  if ($VERBOSE) echo "loaded Rref[] sources from reference $refsrcs"
+
+  # delete any existing output vectors
+  foreach field $SMF_FIELDS_DIF $REF_FIELDS_DIF $XTR_FIELDS_DIF
+    delete -q $field\_out
+  end
+
+  # find matching entries in cmf files and measure mask fraction
+  for i 0 $diffMask:n
+    if ("$diffCMF:$i" == "") 
+      echo "no CMF file found for $i, skipping" 
+      continue
+    end
+    get.diff.detect.stats $diffCMF:$i
+
+    if ("$diffMask:$i" == "") 
+      echo "no mask file found for $i, skipping maskstats" 
+      set Q1ref_onchp = zero(Xref_onchp) + NAN
+    else
+      get.diff.mask.stats $diffMask:$i
+    end
+
+   foreach field $SMF_FIELDS_DIF
+     concat $field\_found $field\_out
+     delete $field\_found
+   end
+   foreach field $REF_FIELDS_DIF $XTR_FIELDS_DIF
+     concat $field\_onchp $field\_out
+     delete $field\_onchp
+   end
+  end
+
+  $OUT_FIELDS = 
+  foreach field $SMF_FIELDS_DIF $REF_FIELDS_DIF $XTR_FIELDS_DIF
+    $OUT_FIELDS = $OUT_FIELDS $field\_out
+  end
+
+  sprintf output "%s/%s.%s.dif.stf" $OUTDIR $expname $OUTVERSION
+  write -fits DATA $output $OUT_FIELDS
+end
+
+macro get.diff.detect.stats
+  if ($0 != 2) 
+   echo "USAGE: get.diff.detect.stats (cmffile)"
+   echo "  assumes existence of vectors (Rref, Dref, Mref)"
+   echo "  matches known sources to detections in the cmf file"
+   echo "  calculates detection fraction as a function of magnitude"
+   echo "  also astrometric and photometric scatter vs magnitude for good detections"
+   break
+  end
+
+  local filename NAXIS1 NAXIS2
+  $filename = $1
+  if ("$filename" == "") 
+    echo "missing CMF file $filename, skipping"
+    return
+  end
+
+  # read in the image header portion
+  # rd -n SkyChip.hdr hdr $filename
+  # XXX apparently the diff cmf files have the hdr as the PHU?
+  rd hdr $filename
+  keyword hdr IMNAXIS1 NAXIS1
+  keyword hdr IMNAXIS2 NAXIS2
+
+  coords -q hdr -p       0       0; init.radec.range
+  coords -q hdr -p $NAXIS1       0; set.radec.range
+  coords -q hdr -p $NAXIS1 $NAXIS2; set.radec.range
+  coords -q hdr -p       0 $NAXIS2; set.radec.range
+
+  # generate a subset which could be on this skycell
+  # do we need to grow the range at all?
+  set keep = (Rref >= $minR) && (Rref <= $maxR) && (Dref >= $minD) && (Dref <= $maxD) 
+  foreach field $REF_FIELDS_DIF Nref
+    subset $field\_tmp = $field if keep
+  end
+
+  # convert the subset coordinates to pixels
+  set Xref_tmp = Rref_tmp
+  set Yref_tmp = Dref_tmp
+  coords -q hdr -c Xref_tmp Yref_tmp
+
+  # generate a subset of sources which are within the boundaries of the skycell
+  set keep = (Xref_tmp >= 0) && (Xref_tmp <= $NAXIS1) && (Yref_tmp >= 0) && (Yref_tmp <= $NAXIS2)
+  foreach field $REF_FIELDS_DIF Nref Xref Yref
+    subset $field\_onchp = $field\_tmp if keep
+    delete $field\_tmp
+  end
+
+  # now read the smf detections
+  data $filename
+  read -fits SkyChip.psf $SMF_FIELDS_DIF
+
+  match2d Xref_onchp Yref_onchp X_PSF Y_PSF $MATCH_RADIUS -closest
+  foreach field $SMF_FIELDS_DIF
+    reindex $field\_found = $field using index1 -keep-unmatched
+  end
+end
+
+macro get.diff.mask.stats
+  if ($0 != 2) 
+   echo "USAGE: get.diff.mask.stats (diffmask)"
+   echo "  assumes existence of vectors XrefSS YrefSS RrefSS DrefSS MrefSS" 
+   echo "  those are calculated by get.diff.detect.stats"
+   break
+  end
+
+  # NOTE: diffmask must be a real file path, not a neb path
+  local diffmask 
+  $diffmask = $1
+
+  # load the mask file
+  rd -x 0 mk $diffmask
+
+  # NOTE: assumes existence of vectors XrefSS YrefSS RrefSS DrefSS MrefSS
+  # those vectors are generated by get.diff.detect.stats
+
+  # I have a mask file, now I need to calculate something equiv to psfQf
+  set mkPerfect = (mk == 0) ? zero(mk) + 1 : zero(mk)
+
+  # I want to set an image based on the bit values but I fear
+  # the mask has been converted to a float when read
+  forcedphot mkPerfect Xref_onchp Yref_onchp -output Q1ref_onchp
+
+  subset tmp = Q1ref_onchp where (Q1ref_onchp > 0.85)
+  if ($VERBOSE) echo tmp[] of Q1ref_onchp[] are unmasked
+end
+
+## utilities
+
+macro init.radec.range
+  $minR = $RA
+  $minD = $DEC
+  $maxR = $RA
+  $maxD = $DEC
+end
+
+macro set.radec.range
+  $minR = min($minR, $RA)
+  $minD = min($minD, $DEC)
+  $maxR = max($maxR, $RA)
+  $maxD = max($maxD, $DEC)
+end
+
+macro get.diff.files
+ if ($0 != 1)
+  echo "USAGE: get.diff.files"
+  echo " uses diffPath:0 - diffPath:n"
+  echo " sets diffMask:0 - diffMask:n"
+  echo "  and diffCMF:0  - diffCMF:n"
+  break
+ end
+
+ # get the non-neb paths for the masks
+ for i 0 $diffPath:n
+   sprintf filename "%s.mask.fits" $diffPath:$i
+   break -auto off   
+   $diffMask:$i = `neb-locate -p $filename`
+   break -auto on
+   if ("$diffMask:$i" == "")
+     echo "skipping $filename"
+   else 
+     if ($COPYMASKS) exec neb-copy $filename $OUTDIR
+   end
+
+   if ($diffOrder == 0)
+     sprintf filename "%s.cmf" $diffPath:$i
+   else
+     sprintf filename "%s.inv.cmf" $diffPath:$i
+   end
+   $diffCMF:$i  = `neb-locate -p $filename`
+   if ($VERBOSE) echo "found mask: $diffMask:$i CMF: $diffCMF:$i"
+ end
+ $diffMask:n = $diffPath:n
+end
+
+macro get.diff.skyfiles
+ if ($0 != 3)
+  echo "USAGE: get.diff.skyfiles (camera) (diffID)"
+  echo "EX:    get.diff.skyfiles gpc1 2367603"
+  echo "  sets diffPath:0 - diffPath:n"
+  break
+ end
+
+ local dbname diffID
+ $dbname  = $1
+ $diffID  = $2
+
+ $command = difftool -dbname $dbname -diffskyfile -diff_id $diffID
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  return
+ end
+ 
+ local i
+ for i 0 $npages 
+   sprint pagename page.%03d $i
+   book getword mybook $pagename path_base -var diffPath:$i
+   book getword mybook $pagename skycell_id -var skycell:$i
+   if ($VERBOSE) echo "found diffPath $diffPath:$i"
+ end
+ $diffPath:n = $npages
+ $skycell:n = $npages
+
+ book delete mybook
+end
+
+macro get.diff.run
+ if ($0 != 5)
+  echo "USAGE: get.diff.run (camera) (expname) (warpID) (diffMode)"
+  echo "EX:    get.diff.run gpc1 o9322g0253o 2367603 1"
+  echo "  diffMode: 1 = warp-warp, 2 = warp-stack"
+  echo "  sets diffID"
+  break
+ end
+
+ local dbname expname warpID diffMode
+ $dbname   = $1
+ $expname  = $2
+ $warpID   = $3
+ $diffMode = $4
+
+ $diffID    = 0
+ $diffOrder = 0; # 0 : A of A-B, 1 : B of A-B
+
+ # we do not know if the exposure was used as A or B in the A-B diff.  First try A:
+#$command = difftool -dbname $dbname -listrun -exp_name $expname -warp_id $warpID -dist_group $distGroup -diff_mode $diffMode
+ $command = difftool -dbname $dbname -listrun -exp_name $expname -warp_id $warpID -diff_mode $diffMode
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  # try the template versoin
+  book delete mybook
+ 
+  $command = difftool -dbname $dbname -listrun -exp_name $expname -warp_id $warpID -diff_mode $diffMode -template
+  $diffOrder = 1
+  
+  queueload  myqueue -x "$command"
+  queue2book myqueue mybook
+  
+  book npages mybook -var npages
+  
+  if ($npages < 1) 
+   echo "ERROR: missing exposure : $command"
+   book delete mybook
+   return
+  end
+ end
+ 
+ if ($npages > 1) 
+  echo "ERROR: too many exposures : $command"
+  book delete mybook
+  return
+ end
+ 
+ sprint pagename page.%03d 0
+ book getword mybook $pagename diff_id -var diffID
+ book delete mybook
+
+ if ($VERBOSE) echo "found diffID $diffID"
+end
Index: /trunk/ippTests/ippCheck/ippCheck
===================================================================
--- /trunk/ippTests/ippCheck/ippCheck	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippCheck	(revision 42085)
@@ -0,0 +1,434 @@
+#!/usr/bin/env dvo
+
+# XXX need alternate source for camID (if !cam) and warpID (if !warp)
+
+# .ippcheckrc defines smf.compare books, dvo databases, and other references
+input $HOME/.ippcheckrc
+
+module truth.pro
+module cammask.pro
+module camstats.pro
+module warpmask.pro
+module warpstats.pro
+module diffmask.pro
+module smfcheck.pro
+
+#####
+macro check.exposure.dvo
+  local SKIP_PLOTS CAMERA
+
+  $CAMERA = gpc1  
+
+  # strip out optional arguments
+  getargs -no-cam   BOOL  -var SKIP_CAM
+  getargs -no-warp  BOOL  -var SKIP_WARP
+  getargs -no-diff  BOOL  -var SKIP_DIFF
+  getargs -no-plots BOOL  -var SKIP_PLOTS
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 3)
+    echo "USAGE: check.exposure.dvo (expname) (dgroup)"
+    echo " -no-cam, -no-warp, -no-diff, -no-plots"
+    echo " [-camera gpc2]"
+    break
+  end
+
+  local expname camdgrp
+  $expname = $1
+  $camdgrp = $2
+ 
+  $OUTVERSION = DVO; # used to define output names
+  $REFMODE = DVO;    # used by plot.exposure.user for input
+
+  make.truth.sources $CAMERA $expname $PV3_CATDIR DVO
+  if (not($SKIP_CAM))  get.camera.eff $CAMERA $expname $camdgrp $REFSOURCE
+  if (not($SKIP_WARP)) get.warp.eff   $CAMERA $expname $camID   $REFSOURCE
+  if (not($SKIP_DIFF)) get.diff.eff   $CAMERA $expname $warpID  $REFSOURCE
+
+  if (not($SKIP_PLOTS)) plot.exposure.real $expname
+end
+
+#####
+macro check.exposure.mpc
+  local SKIP_PLOTS CAMERA
+
+  $CAMERA = gpc1  
+
+  # strip out optional arguments
+  getargs -no-cam   BOOL  -var SKIP_CAM
+  getargs -no-warp  BOOL  -var SKIP_WARP
+  getargs -no-diff  BOOL  -var SKIP_DIFF
+  getargs -do-plots BOOL  -var DO_PLOTS;   
+  getargs -use-refs BOOL  -var USE_REFSOURCE
+  getargs -camera   VALUE -var CAMERA;
+
+  if ($0 != 4)
+    echo "USAGE: check.exposure.mpc (expname) (dgroup) (mpcfile)"
+    break
+  end
+
+  local expname camdgrp mpcfile
+  $expname = $1
+  $camdgrp = $2
+  $mpcfile = $3
+ 
+  # use a larger match radius for the asteroids
+  # MATCH_RADIUS is in pixels 
+  $MATCH_RADIUS = 8.0; # XXX add option to set the radius?
+  $OUTVERSION = MPC
+  $COPYMASKS = 0
+
+  make.truth.sources $CAMERA $expname $mpcfile MPC; # sets the value of REFSOURCE 
+
+  if (not($SKIP_CAM)) get.camera.eff     $CAMERA $expname $camdgrp $REFSOURCE
+
+  # CAMSOURCE lists all REFSOURCE entries which actually fall on a chip, while REFSOURCE includes
+  # the objects which are in the larger field.  The latter is not accurate for the detection fraction
+  # but, if we did not measure the camera sources, we must use REFSOURCE
+
+  if ($USE_REFSOURCE || $SKIP_CAM)
+    if (not($SKIP_WARP)) get.warp.eff    $CAMERA $expname $camID    $REFSOURCE
+    if (not($SKIP_DIFF)) get.diff.eff    $CAMERA $expname $warpID   $REFSOURCE
+  else
+    # load data from cam stage output:
+    $USE_CAMSOURCE = 1; # this is used by get.warp.eff to choose the correct fields
+    if (not($SKIP_WARP)) get.warp.eff    $CAMERA $expname $camID    $CAMSOURCE
+    if (not($SKIP_DIFF)) get.diff.eff    $CAMERA $expname $warpID   $CAMSOURCE
+  end
+
+  if ($DO_PLOTS) plot.exposure.real $expname
+end
+
+# XXX uses fixed name for orbit file (MPCORB.DAT)
+macro make.mpc.refsources
+  local GET_ORBITS
+
+  # strip out optional arguments
+  getargs -get-orbits BOOL -var GET_ORBITS
+
+  if ($0 != 7)
+    echo "USAGE: make.mpc.refsources (Tmin) (Tmax) (Rmin) (Rmax) (Dmin) (Dmax) [-get-orbits]"
+    break
+  end
+
+  local Tmin Tmax Rmin Rmax Dmin Dmax
+  $Tmin = $1
+  $Tmax = $2
+  $Rmin = $3
+  $Rmax = $4
+  $Dmin = $5
+  $Dmax = $6
+
+  if ($GET_ORBITS)
+    # download the most current MPCORB.DAT file with:
+    exec rm -f MPCORB.DAT
+    exec wget https://www.minorplanetcenter.net/iau/MPCORB/MPCORB.DAT
+  end
+
+  $Tref = int($Tmin)
+  exec mpcorb_predict trange MPCORB.DAT $Tmin $Tmax $Rmin $Rmax $Dmin $Dmax mpcorb.trange.$Tref.fits
+end
+
+#####
+macro plot.exposure.user
+  $REFMODE = DVO
+
+  # strip out optional arguments
+  getargs -no-cam   BOOL  -var SKIP_CAM
+  getargs -no-warp  BOOL  -var SKIP_WARP
+  getargs -no-diff  BOOL  -var SKIP_DIFF
+  getargs -refmode  VALUE -var REFMODE
+
+  if ($0 != 2)
+    echo "USAGE: plot.exposure.user (expname)"
+    echo " -no-cam, -no-warp, -no-diff"
+    echo " -refmode MODE : MODE = MPC, DVO"
+    break
+  end
+
+  local expname; $expname = $1
+  plot.exposure.real $expname
+end
+
+##### plot.exposure.user strips optional arguments and calls plot.exposure.real
+macro plot.exposure.real
+  if ($0 != 2)
+    echo "USAGE: plot.exposure.real (expname)"
+    break
+  end
+  local expname; $expname = $1
+
+  $KAPA = kapa -noX; # XXX make this optional (default: do not display)
+  antialias 0.5
+
+  if (not($SKIP_CAM))
+    load.camstats  $expname.stats/$expname.$REFMODE.cam.stf
+    plot.camstats.fracs;  png -name $expname.stats/$expname.$REFMODE.camfracs.png
+    plot.camstats.resid;  png -name $expname.stats/$expname.$REFMODE.camresid.png
+  end
+  
+  if (not($SKIP_WARP))
+    load.warpstats $expname.stats/$expname.$REFMODE.wrp.stf
+    plot.warpstats.fracs; png -name  $expname.stats/$expname.$REFMODE.wrpfracs.png
+  end
+
+  if (not($SKIP_DIFF))
+    load.camstats  $expname.stats/$expname.$REFMODE.dif.stf
+    plot.camstats.fracs;  png -name  $expname.stats/$expname.$REFMODE.diffracs.png
+    plot.camstats.resid;  png -name  $expname.stats/$expname.$REFMODE.difresid.png
+  end
+end
+
+##### plot.exposure.glob
+macro plot.exposure.glob
+  $REFMODE = DVO
+
+  # strip out optional arguments
+  getargs -no-cam   BOOL  -var SKIP_CAM
+  getargs -no-warp  BOOL  -var SKIP_WARP
+  getargs -no-diff  BOOL  -var SKIP_DIFF
+  getargs -refmode  VALUE -var REFMODE
+
+  if ($0 != 3)
+    echo "USAGE: plot.exposure.glob (expglob) (outroot)"
+    echo " -no-cam, -no-warp, -no-diff"
+    echo " -refmode MODE : MODE = MPC, DVO"
+    break
+  end
+
+  local expglob outroot
+  $expglob = $1
+  $outroot = $2
+
+  dirname $outroot -var outdir; mkdir $outdir
+
+  $KAPA = kapa -noX; # XXX make this optional (default: do not display)
+  antialias 0.5
+
+  if (not($SKIP_CAM))
+    load.camstats  $expglob.$REFMODE.cam.stf
+    plot.camstats.fracs;  png -name $outroot.$REFMODE.camfracs.png
+    plot.camstats.resid;  png -name $outroot.$REFMODE.camresid.png
+  end
+  
+  if (not($SKIP_WARP))
+    load.warpstats $expglob.$REFMODE.wrp.stf
+    plot.warpstats.fracs; png -name  $outroot.$REFMODE.wrpfracs.png
+  end
+
+  if (not($SKIP_DIFF))
+    load.camstats  $expglob.$REFMODE.dif.stf
+    plot.camstats.fracs;  png -name  $outroot.$REFMODE.diffracs.png
+    plot.camstats.resid;  png -name  $outroot.$REFMODE.difresid.png
+  end
+end
+
+# The ippCheck class specifies the group of validation table to use for the analysis.
+# When comparing a specific processing, planned or expected deviations from previous 
+# results can be encoded uniquely in the class
+macro init.class
+
+  if (not($?IPPCHECK_CLASS))
+    $IPPCHECK_CLASS = default
+  end
+
+  # look for command-line argument of the form -class ()
+  local i
+
+# for i 0 $argv:n
+# NOTE: the opihi for-loop construct evaluates the loop conditions
+# only once before the loop starts.  Changing the value of argv:n
+# during the loop will cause problems in that context.  Also, we 
+# need to avoid incrementing $i when we strip off arguments
+
+# the opihi 'while' loop correctly reevaluates the condition on each loop
+
+  $i = 0
+  while ($i < $argv:n)
+    if ("$argv:$i" == "-class")
+      if ($argv:n < $i + 2)
+        echo "ERROR: incorrect usage: -class is missing classname: -class (classname)"
+        break
+      end
+      list argv -del -class
+      $IPPCHECK_CLASS = $argv:$i
+      list argv -del $IPPCHECK_CLASS
+    else
+      $i ++
+    end
+  end
+
+  # class-specific macros to define the class tables 
+  # should be listed in ~/.ippcheckrc 
+  ippcheck.class.$IPPCHECK_CLASS
+end
+
+macro init.stage
+  if ($0 < 2) 
+    echo "USAGE: init.stage (stage)"
+    break
+  end
+
+  if (("$1" == "cam") || ("$1" == "camera")) init.camera
+  if (("$1" == "wrp") || ("$1" == "warp"))   init.warp
+  if (("$1" == "dif") || ("$1" == "diff"))   init.diff
+  
+  if (not($?PSF_FIELDS))
+    echo "ERROR : invalid stage $1 or PSF_FIELDS not defined"
+    break
+  end
+  if (not($?PSF_KEYS))
+    echo "ERROR : PSF_KEYS not defined"
+    break
+  end
+  if (not($?HDR_KEYS))
+    echo "ERROR : HDR_KEYS not defined"
+    break
+  end
+
+  # Load PASS/FAIL criteria definitions
+  smf.compare.parse.book $PSF_FIELDS  psfFields
+  smf.compare.parse.book $PSF_KEYS    psfKeys
+  smf.compare.parse.book $HDR_KEYS    hdrKeys
+
+  # determine the number of tests per chip and cmf/smf file
+  local Nfields
+  book npages psfFields -var Nfields
+  $NTESTS_CHIP = $Nfields + 4
+  $NTESTS_CMF  = $NTESTS_CHIP * $NCHIPS + 2
+end
+
+macro show.usage
+  echo "USAGE: ippCheck (mode) ..."
+  echo "   EG: ippCheck mpc  (expname) (dgroup) [mpcfile]"
+  echo "   EG: ippCheck dvo  (expname) (dgroup)"
+  echo "   EG: ippCheck smf (stage) (outroot) (smf1) (smf2) [+plot]"
+  echo "   EG: ippCheck cmf (stage) (outroot) (smf1) (smf2) [+plot]"
+  echo "   EG: ippCheck smf.dgrp.exp (stage) (outroot) (expname) (dgroup1) (dgroup2) [+plot]"
+  echo "   EG: ippCheck smf.dgrp.all (stage) (outroot) (dgroup1) (dgroup2) [+plot]"
+  echo "   EG: ippCheck plot (expname)"
+  echo "   EG: ippCheck plot.glob (expglob) (outroot)"
+  echo "   EG: ippCheck mpcrefs (Tmin) (Tmax) (Rmin) (Rmax) (Dmin) (Dmax)"
+
+  echo " also equivalent:"
+  echo "   EG: ippCheck smf.compare.exp.by.datagroup (stage) (outroot) (expname) (dgroup1) (dgroup2) [+plot]"
+  echo "   EG: ippCheck smf.compare.all.by.datagroup (stage) (outroot) (dgroup1) (dgroup2) [+plot]"
+end
+
+if ($SCRIPT)
+  if ($argv:n < 1)
+    show.usage
+    break
+  end
+
+  # loads the requested class (finds and removes -class CLASSNAME) argument
+  init.class
+
+  # arguments to the script are given as $argv:0 - $argv:n
+  # argument lists are passed to macros called below with $arglist
+  list argv -join arglist -start 1
+
+  if ("$argv:0" == "dvo")
+    check.exposure.dvo $arglist
+    exit 0
+  end
+
+  if ("$argv:0" == "mpc")
+    check.exposure.mpc $arglist
+    exit 0
+  end
+
+  if ("$argv:0" == "mpcrefs")
+    make.mpc.refsources $arglist
+    exit 0
+  end
+
+  # compare output products for a single exposure
+  if (("$argv:0" == "smf") || ("$argv:0" == "cmf"))
+    if ($argv:n < 2) 
+      echo "USAGE: ippCheck smf (stage) (outroot) (smf1) (smf2) [+plot]"
+      echo "USAGE: ippCheck cmf (stage) (outroot) (smf1) (smf2) [+plot]"
+      echo "   EG: ippCheck smf camera  (cctest) neb:///gpc1/ipc/o6370g0351o.2133651.smf neb:///gpc1/ipc/o6370g0351o.2497362.smf [+plot]"
+      echo "   EG: ippCheck cmf warp    (wwtest) neb:///gpc1/ipc/o6370g0351o.wrp.2109044.skycell.1278.000.cmf neb:///gpc1/ipc/o6370g0351o.wrp.2465875.skycell.1278.000.cmf [+plot]"
+      exit 2
+    end
+
+    init.stage $argv:1
+    list argv -join arglist -start 2
+
+    # If we run on a single file, define the TAP test plan here.  The
+    # value of NTESTS_CHIP is set by the init.stage call based on psfFields
+    # The number of chips depends on the camera and stage
+    # For warps and diffs, the number of chips is 1 since a single cmf holds a single table
+    # there are 2 tests at the end of smf.compare.all.extensions
+    tap.plan $NTESTS_CMF
+    smf.compare.all.extensions $arglist
+    tap.done
+
+    exit 0
+  end
+
+  # compare output products for a single exposure by data_groups
+  if (("$argv:0" == "smf.compare.exp.by.datagroups") || ("$argv:0" == "smf.dgrp.exp"))
+    if ($argv:n < 2) 
+      echo "USAGE: ippCheck smf.dgrp.exp (stage) (outroot) (expname) (dgroup1) (dgroup2) [+plot]"
+      echo "   EG: ippCheck smf.dgrp.exp camera  (cctest) o6370g0351o MOPS.dailytestset.20190516ops40745  eamtest.20220208"
+      echo "   EG: ippCheck smf.dgrp.exp warp    (wwtest) o6370g0351o MOPS.dailytestset.20190516ops40745  eamtest.20220208"
+    end
+
+    init.stage $argv:1
+    list argv -join arglist -start 2
+
+    if (("$argv:1" == "cam") || ("$argv:1" == "camera"))
+      smf.compare.cam.exp.by.datagroups $arglist
+      exit 0
+    end
+    if (("$argv:1" == "wrp") || ("$argv:1" == "warp"))
+      smf.compare.wrp.exp.by.datagroups $arglist
+      exit 0
+    end
+    if (("$argv:1" == "dif") || ("$argv:1" == "diff"))
+      smf.compare.dif.exp.by.datagroups $arglist
+      exit 0
+    end
+  end
+
+  # compare all exposures for a pair of data_groups
+  if (("$argv:0" == "smf.compare.all.by.datagroups") || ("$argv:0" == "smf.dgrp.all"))
+    if ($argv:n < 2) 
+      echo "USAGE: ippCheck smf.dgrp.all (stage) (outroot) (dgroup1) (dgroup2) [+plot]"
+      echo "   EG: ippCheck smf.dgrp.all camera  (cctest) MOPS.dailytestset.20190516ops40745  eamtest.20220208"
+      echo "   EG: ippCheck smf.dgrp.all warp    (wwtest) MOPS.dailytestset.20190516ops40745  eamtest.20220208"
+    end
+
+    init.stage $argv:1
+    list argv -join arglist -start 2
+
+    if (("$argv:1" == "cam") || ("$argv:1" == "camera"))
+      smf.compare.cam.all.by.datagroups $arglist
+      exit 0
+    end
+    if (("$argv:1" == "wrp") || ("$argv:1" == "warp"))
+      smf.compare.wrp.all.by.datagroups $arglist
+      exit 0
+    end
+    if (("$argv:1" == "dif") || ("$argv:1" == "diff"))
+      smf.compare.dif.all.by.datagroups $arglist
+      exit 0
+    end
+  end
+
+  if ("$argv:0" == "plot")
+    plot.exposure.user $arglist
+    exit 0
+  end
+
+  if ("$argv:0" == "plot.glob")
+    plot.exposure.glob $arglist
+    exit 0
+  end
+
+  show.usage
+  exit 2
+end
+
Index: /trunk/ippTests/ippCheck/ippcheck.rc
===================================================================
--- /trunk/ippTests/ippCheck/ippcheck.rc	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippcheck.rc	(revision 42085)
@@ -0,0 +1,42 @@
+
+# uncomment to get lots of debug info
+# opihi verbose on
+
+$CONFDIR = /home/panstarrs/eugene/ippconfig/ippcheck
+
+$PV3_CATDIR = /data/ipp105.0/eugene/3pi.dvo.masters/3pi.pv3.20170919
+
+macro init.camera
+  $PSF_FIELDS = $CONFDIR/psfFields.v1.bk
+  $PSF_KEYS   = $CONFDIR/psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/hdrKeys.bk
+  $NTESTS_CMF = 3065
+end
+
+macro init.warp
+   $PSF_FIELDS = $CONFDIR/warp.psfFields.v1.bk
+   $PSF_KEYS   = $CONFDIR/warp.psfKeys.bk
+   $HDR_KEYS   = $CONFDIR/warp.hdrKeys.bk
+   $NTESTS_CMF = 28
+end
+
+# use these for a specific case (t0)
+macro init.warp
+#  $PSF_FIELDS = $CONFDIR/warp.psfFields.v1.bk
+   $PSF_FIELDS = $CONFDIR/warp.psfFields.sn.bk
+   $PSF_KEYS   = $CONFDIR/warp.psfKeys.t0.bk
+   $HDR_KEYS   = $CONFDIR/warp.hdrKeys.t0.bk
+   $NTESTS_CMF = 28
+end
+
+macro init.diff
+  $PSF_FIELDS = $CONFDIR/diff.psfFields.sn.bk
+  $PSF_KEYS   = $CONFDIR/diff.psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/diff.hdrKeys.bk
+  $NTESTS_CMF = 28
+end
+
+# leave these default values here?
+$PSF_FIELDS = $CONFDIR/psfFields.v1.bk
+$PSF_KEYS   = $CONFDIR/psfKeys.bk
+$HDR_KEYS   = $CONFDIR/hdrKeys.bk
Index: /trunk/ippTests/ippCheck/ippconfig/cam.hdrKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/cam.hdrKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/cam.hdrKeys.bk	(revision 42085)
@@ -0,0 +1,139 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   D100  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   D100
+IMAGE_V			   	   IGNORE   D100
+PSLIB_V			   	   IGNORE   D100
+MODULE_V			   IGNORE   D100
+STATS_V			   	   IGNORE   D100
+ASTRO_V			   	   IGNORE   D100
+	  			      
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
Index: /trunk/ippTests/ippCheck/ippconfig/cam.psfFields.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/cam.psfFields.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/cam.psfFields.bk	(revision 42085)
@@ -0,0 +1,66 @@
+
+# definition of the pages:
+# STAT: what statistic to use to compare (VALUE, RVALUE, RATIO)
+# SCALE : multiply the stat by this value before comparing to MIN,MAX
+# MIN_GOOD: 
+# MAX_GOOD:
+# MAG_MAX : clip values fainter than this PSF_INST_MAG
+LAYOUT                   STAT   MIN_GOOD MAX_GOOD SCALE  MIN_MAG MAX_MAG MIN_ERR MAX_ERR  
+								          
+                 AP_FLUX RVALUE -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01
+             AP_FLUX_SIG RVALUE -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01
+                  AP_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01
+           AP_MAG_RADIUS NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+              AP_MAG_RAW VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                 AP_NPIX IVALUE -5.0     +5.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+             CAL_PSF_MAG VALUE  -0.04    +0.04	 1.0	   -15.0    -9.0  0.001	   0.01
+         CAL_PSF_MAG_SIG VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # field has only a single value (estimate of systematic error?)	  
+               CR_NSIGMA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # field is all NAN
+                 DEC_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec
+              EXT_NSIGMA VALUE  -2.0     +2.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # range seems quite large (nearly 1 sigma)
+                   FLAGS NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01
+                  FLAGS2 NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                IPP_IDET NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01	  
+               KRON_FLUX RVALUE -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+           KRON_FLUX_ERR RVALUE -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+         KRON_FLUX_INNER RVALUE -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous (but scatter is large compared to KRON_FLUX)
+         KRON_FLUX_OUTER NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # most objects have a NAN value
+             MOMENTS_M3C VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01	  
+             MOMENTS_M3S VALUE  -1e-2    +1e-2 	 1.0	   -15.0    -9.0  0.001	   0.01	  
+             MOMENTS_M4C VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01	  
+             MOMENTS_M4S VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01	  
+              MOMENTS_R1 VALUE  -0.10    +0.10 	 1.0	   -15.0    -9.0  0.001	   0.01 # R1 dispersion is surprisingly high given low value for RH
+              MOMENTS_RH VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # R1 dispersion is surprisingly high given low value for RH
+              MOMENTS_XX VALUE  -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+              MOMENTS_XY VALUE  -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+              MOMENTS_YY VALUE  -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+                N_FRAMES NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                 PADDING NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01      
+        PEAK_FLUX_AS_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # this scatter must come from the background model
+                PLTSCALE VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 # differences should be due to different astrometry solutions
+                POSANGLE VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution has a strange cutoff at small positive value?
+               PSF_CHISQ RVALUE -0.05    +0.06 	 1.0	   -15.0    -9.0  0.001	   0.01 # 0.05 is a bit generous
+                PSF_CORE VALUE  -0.1     +0.1  	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a good prior on what this value should be
+            PSF_FWHM_MAJ VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a good prior on what this value should be
+            PSF_FWHM_MIN VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a good prior on what this value should be
+           PSF_INST_FLUX RVALUE -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01
+       PSF_INST_FLUX_SIG RVALUE -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01
+            PSF_INST_MAG VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01
+        PSF_INST_MAG_SIG VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               PSF_MAJOR VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a good prior on what this value should be
+               PSF_MINOR VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a good prior on what this value should be
+                PSF_NDOF NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                PSF_NPIX NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                  PSF_QF VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+          PSF_QF_PERFECT VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+               PSF_THETA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # a small difference in the PSF model could make a bit diff here; not well constrained
+                  RA_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec, but NOT cos-dec corrected
+                     SKY RVALUE -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # this value probably depends on the sky level?
+          SKY_LIMIT_FLUX NONE   -1.0     +1.0  	 3.0	   -15.0    -9.0  0.001	   0.01 # 
+           SKY_LIMIT_RAD NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution is odd
+         SKY_LIMIT_SLOPE NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # many values are NAN
+               SKY_SIGMA VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # probably depends on the sky level?	  
+                   X_PSF VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # the bright objects are more scattered than I expect
+               X_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+                   Y_PSF VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               Y_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
Index: /trunk/ippTests/ippCheck/ippconfig/cam.psfKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/cam.psfKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/cam.psfKeys.bk	(revision 42085)
@@ -0,0 +1,17 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+NAXIS2                             IGNORE   D001
+	  			      
Index: /trunk/ippTests/ippCheck/ippconfig/class.default
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/class.default	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/class.default	(revision 42085)
@@ -0,0 +1,21 @@
+
+echo "loading class default"
+
+macro init.camera
+  $PSF_FIELDS = $CONFDIR/cam.psfFields.bk
+  $PSF_KEYS   = $CONFDIR/cam.psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/cam.hdrKeys.bk
+  $NCHIPS     = 60; # this needs to be camera-dependent
+end
+macro init.warp
+  $PSF_FIELDS = $CONFDIR/warp.psfFields.bk
+  $PSF_KEYS   = $CONFDIR/warp.psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/warp.hdrKeys.bk
+  $NCHIPS     = 1; # this needs to be camera-dependent
+end
+macro init.diff
+  $PSF_FIELDS = $CONFDIR/diff.psfFields.bk
+  $PSF_KEYS   = $CONFDIR/diff.psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/diff.hdrKeys.bk
+   $NCHIPS     = 1; # this needs to be camera-dependent
+end
Index: /trunk/ippTests/ippCheck/ippconfig/class.t0
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/class.t0	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/class.t0	(revision 42085)
@@ -0,0 +1,21 @@
+
+echo "loading class t0"
+
+macro init.camera
+  $PSF_FIELDS = $CONFDIR/cam.psfFields.bk
+  $PSF_KEYS   = $CONFDIR/cam.psfKeys.bk
+  $HDR_KEYS   = $CONFDIR/cam.hdrKeys.bk
+  $NCHIPS     = 60; # this needs to be camera-dependent
+end
+macro init.warp
+   $PSF_FIELDS = $CONFDIR/warp.psfFields.bk
+   $PSF_KEYS   = $CONFDIR/warp.psfKeys.t0.bk
+   $HDR_KEYS   = $CONFDIR/warp.hdrKeys.t0.bk
+   $NCHIPS     = 1; # this needs to be camera-dependent
+end
+macro init.diff
+  $PSF_FIELDS = $CONFDIR/diff.psfFields.bk
+  $PSF_KEYS   = $CONFDIR/diff.psfKeys.t0.bk
+  $HDR_KEYS   = $CONFDIR/diff.hdrKeys.t0.bk
+  $NCHIPS     = 1; # this needs to be camera-dependent
+end
Index: /trunk/ippTests/ippCheck/ippconfig/classes.pro
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/classes.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/classes.pro	(revision 42085)
@@ -0,0 +1,9 @@
+
+macro ippcheck.class.default 
+  input $CONFDIR/class.default
+end
+
+macro ippcheck.class.t0
+  input $CONFDIR/class.t0
+end
+
Index: /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.bk	(revision 42085)
@@ -0,0 +1,178 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   STR  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   STR
+IMAGE_V			   	   IGNORE   STR
+PSLIB_V			   	   IGNORE   STR
+MODULE_V			   IGNORE   STR
+STATS_V			   	   IGNORE   STR
+ASTRO_V			   	   IGNORE   STR
+WARP_V			   	   IGNORE   STR
+PPSUB_V			   	   IGNORE   STR
+MD5_SkyChip_SkyCell_0		   IGNORE   STR
+	  			      
+PPSUB.INPUT                        IGNORE   STR # the file names will differ
+PPSUB.REFERENCE			   IGNORE   STR # the file names will differ
+
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# warp-to-chip terms (depend on astrometry)
+MPX_0000			   IGNORE   STR
+MPX_0001		   	   IGNORE   STR
+MPX_0002		   	   IGNORE   STR
+MPX_0003		   	   IGNORE   STR
+MPX_0004		   	   IGNORE   STR
+MPX_0005		   	   IGNORE   STR
+MPX_0006		   	   IGNORE   STR
+MPY_0000		   	   IGNORE   STR
+MPY_0001		   	   IGNORE   STR
+MPY_0002		   	   IGNORE   STR
+MPY_0003		   	   IGNORE   STR
+MPY_0004		   	   IGNORE   STR
+MPY_0005		   	   IGNORE   STR
+MPY_0006		   	   IGNORE   STR
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+CHIP.SEEING                        IGNORE   D000
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
+FPA.ZP                             IGNORE   D000 # make this a test?
+
+# convolution terms:
+PPSUB.KERNEL			   IGNORE   STR # the kernel terms can be slightly different
+SUBTRACTION.MODE		   IGNORE   D010
+SUBTRACTION.CONVOL.MAX		   IGNORE   D010
+SUBTRACTION.DEV.MEAN		   IGNORE   D010
+SUBTRACTION.DEV.RMS		   IGNORE   D010
+SUBTRACTION.MX			   IGNORE   D010
+SUBTRACTION.MXX			   IGNORE   D010
+SUBTRACTION.MXY			   IGNORE   D010
+SUBTRACTION.MY			   IGNORE   D010
+SUBTRACTION.MYY			   IGNORE   D010
+SUBTRACTION.NORM		   IGNORE   D010
+SUBTRACTION.REGION		   IGNORE   D010
+SUBTRACTION.STAMPS                 IGNORE   D010
Index: /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.t0.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.t0.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/diff.hdrKeys.t0.bk	(revision 42085)
@@ -0,0 +1,191 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   STR  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   STR
+IMAGE_V			   	   IGNORE   STR
+PSLIB_V			   	   IGNORE   STR
+MODULE_V			   IGNORE   STR
+STATS_V			   	   IGNORE   STR
+ASTRO_V			   	   IGNORE   STR
+WARP_V			   	   IGNORE   STR
+WARP_V			   	   IGNORE   STR
+MD5_SkyChip_SkyCell_0		   IGNORE   STR
+	  			      
+PPSUB.INPUT                        IGNORE   STR # the file names will differ
+PPSUB.REFERENCE			   IGNORE   STR # the file names will differ
+
+### For this comparison, the mask values have been changed:
+MSKNAM13                           IGNORE   STR
+MSKNAM14                           IGNORE   STR
+MSKNAM15                           IGNORE   STR
+MSKNAM16                           IGNORE   STR
+MSKNAM17                           IGNORE   STR
+MSKVAL13                           IGNORE   D010
+MSKVAL14                           IGNORE   D010
+MSKVAL15                           IGNORE   D010
+MSKVAL16                           IGNORE   D010
+MSKVAL17                           IGNORE   D010
+MSKNUM                             IGNORE   D001
+
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# warp-to-chip terms (depend on astrometry)
+MPX_0000			   IGNORE   STR
+MPX_0001		   	   IGNORE   STR
+MPX_0002		   	   IGNORE   STR
+MPX_0003		   	   IGNORE   STR
+MPX_0004		   	   IGNORE   STR
+MPX_0005		   	   IGNORE   STR
+MPX_0006		   	   IGNORE   STR
+MPY_0000		   	   IGNORE   STR
+MPY_0001		   	   IGNORE   STR
+MPY_0002		   	   IGNORE   STR
+MPY_0003		   	   IGNORE   STR
+MPY_0004		   	   IGNORE   STR
+MPY_0005		   	   IGNORE   STR
+MPY_0006		   	   IGNORE   STR
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+CHIP.SEEING                        IGNORE   D000
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
+FPA.ZP                             IGNORE   D000 # make this a test?
+
+# convolution terms:
+PPSUB.KERNEL			   IGNORE   STR # the kernel terms can be slightly different
+SUBTRACTION.CONVOL.MAX		   IGNORE   D010
+SUBTRACTION.DEV.MEAN		   IGNORE   D010
+SUBTRACTION.DEV.RMS		   IGNORE   D010
+SUBTRACTION.MODE		   IGNORE   D010
+SUBTRACTION.MX			   IGNORE   D010
+SUBTRACTION.MXX			   IGNORE   D010
+SUBTRACTION.MXY			   IGNORE   D010
+SUBTRACTION.MY			   IGNORE   D010
+SUBTRACTION.MYY			   IGNORE   D010
+SUBTRACTION.NORM		   IGNORE   D010
+SUBTRACTION.REGION		   IGNORE   D010
+SUBTRACTION.STAMPS                 IGNORE   D010
Index: /trunk/ippTests/ippCheck/ippconfig/diff.psfFields.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/diff.psfFields.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/diff.psfFields.bk	(revision 42085)
@@ -0,0 +1,66 @@
+
+# definition of the pages:
+# STAT: what statistic to use to compare (VALUE, RVALUE, RATIO)
+# SCALE : multiply the stat by this value before comparing to MIN,MAX
+# MIN_GOOD: 
+# MAX_GOOD:
+# MAG_MAX : clip values fainter than this PSF_INST_MAG
+LAYOUT                   STAT   MIN_GOOD MAX_GOOD SCALE  MIN_MAG MAX_MAG MIN_ERR MAX_ERR  
+								          
+                 AP_FLUX NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             AP_FLUX_SIG NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                  AP_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01	
+           AP_MAG_RADIUS VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+              AP_MAG_RAW NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                 AP_NPIX IVALUE -5.0     +5.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+             CAL_PSF_MAG VALUE  -0.04    +0.04	 1.0	   -15.0    -9.0  0.001	   0.01	
+         CAL_PSF_MAG_SIG VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # field has only a single value (estimate of systematic error?)	  
+               CR_NSIGMA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                 DEC_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec
+              EXT_NSIGMA VALUE  -2.0     +2.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # range seems quite large (nearly 1 sigma)
+                   FLAGS NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # diffcult to compare statistically
+                  FLAGS2 NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # diffcult to compare statistically
+                IPP_IDET NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01 # detection ID sequence is not critical
+               KRON_FLUX NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+           KRON_FLUX_ERR NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+         KRON_FLUX_INNER NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+         KRON_FLUX_OUTER NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+             MOMENTS_M3C NONE   -0.03    +0.03   1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+             MOMENTS_M3S NONE   -0.30    +0.30 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             MOMENTS_M4C NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             MOMENTS_M4S NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_R1 NONE   -0.10    +0.10 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_RH NONE   -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_XX NONE   -0.10    +0.10 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_XY NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_YY NONE   -0.20    +0.20 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                N_FRAMES IVALUE -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+                 PADDING NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01 # comparison does not make sense
+        PEAK_FLUX_AS_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # this scatter must come from the background model
+                PLTSCALE VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 # differences should be due to different astrometry solutions
+                POSANGLE VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution has a strange cutoff at small positive value?
+               PSF_CHISQ RVALUE -0.50    +0.50 	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a great prior on this
+                PSF_CORE NONE   -0.1     +0.1  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+            PSF_FWHM_MAJ NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # POPULATED VALUES ARE BOGUS
+            PSF_FWHM_MIN NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # POPULATED VALUES ARE BOGUS
+           PSF_INST_FLUX NONE   -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+       PSF_INST_FLUX_SIG NONE   -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+            PSF_INST_MAG VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01	
+        PSF_INST_MAG_SIG VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               PSF_MAJOR NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+               PSF_MINOR NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                PSF_NDOF NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	
+                PSF_NPIX NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 	  
+                  PSF_QF VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+          PSF_QF_PERFECT VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+               PSF_THETA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                  RA_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec, but NOT cos-dec corrected
+                     SKY RVALUE -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # this value probably depends on the sky level?
+          SKY_LIMIT_FLUX NONE   -1.0     +1.0  	 3.0	   -15.0    -9.0  0.001	   0.01 # 
+           SKY_LIMIT_RAD NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution is odd
+         SKY_LIMIT_SLOPE NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # many values are NAN
+               SKY_SIGMA VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # probably depends on the sky level?	  
+                   X_PSF VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # the bright objects are more scattered than I expect
+               X_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+                   Y_PSF VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               Y_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
Index: /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.bk	(revision 42085)
@@ -0,0 +1,17 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+NAXIS2                             IGNORE   D001
+	  			      
Index: /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.t0.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.t0.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/diff.psfKeys.t0.bk	(revision 42085)
@@ -0,0 +1,21 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+NAXIS2                             IGNORE   D001 # we do not always have the same number of detections
+	  			      
+# old vs new FITS I/O has difference in layout for empty tables
+NAXIS                              IGNORE   D001 
+BITPIX                             IGNORE   D001 
+XTENSION                           IGNORE   D001 
Index: /trunk/ippTests/ippCheck/ippconfig/hdrKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/hdrKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/hdrKeys.bk	(revision 42085)
@@ -0,0 +1,139 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   D100  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   D100
+IMAGE_V			   	   IGNORE   D100
+PSLIB_V			   	   IGNORE   D100
+MODULE_V			   IGNORE   D100
+STATS_V			   	   IGNORE   D100
+ASTRO_V			   	   IGNORE   D100
+	  			      
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
Index: /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.bk	(revision 42085)
@@ -0,0 +1,159 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   STR  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   STR
+IMAGE_V			   	   IGNORE   STR
+PSLIB_V			   	   IGNORE   STR
+MODULE_V			   IGNORE   STR
+STATS_V			   	   IGNORE   STR
+ASTRO_V			   	   IGNORE   STR
+WARP_V			   	   IGNORE   STR
+MD5_SkyChip_SkyCell_0		   IGNORE   STR
+	  			      
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# warp-to-chip terms (depend on astrometry)
+MPX_0000			   IGNORE   STR
+MPX_0001		   	   IGNORE   STR
+MPX_0002		   	   IGNORE   STR
+MPX_0003		   	   IGNORE   STR
+MPX_0004		   	   IGNORE   STR
+MPX_0005		   	   IGNORE   STR
+MPX_0006		   	   IGNORE   STR
+MPY_0000		   	   IGNORE   STR
+MPY_0001		   	   IGNORE   STR
+MPY_0002		   	   IGNORE   STR
+MPY_0003		   	   IGNORE   STR
+MPY_0004		   	   IGNORE   STR
+MPY_0005		   	   IGNORE   STR
+MPY_0006		   	   IGNORE   STR
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+CHIP.SEEING                        IGNORE   D000
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
+FPA.ZP                             IGNORE   D000 # make this a test?
Index: /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.t0.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.t0.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/warp.hdrKeys.t0.bk	(revision 42085)
@@ -0,0 +1,172 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+IMAGEID                            IGNORE   STR  # IMAGEID could be any value for two analyses of the same data
+PHOT_V				   IGNORE   STR
+IMAGE_V			   	   IGNORE   STR
+PSLIB_V			   	   IGNORE   STR
+MODULE_V			   IGNORE   STR
+STATS_V			   	   IGNORE   STR
+ASTRO_V			   	   IGNORE   STR
+WARP_V			   	   IGNORE   STR
+MD5_SkyChip_SkyCell_0		   IGNORE   STR
+	  			      
+### For this comparison, the mask values have been changed:
+MSKNAM13                           IGNORE   STR
+MSKNAM14                           IGNORE   STR
+MSKNAM15                           IGNORE   STR
+MSKNAM16                           IGNORE   STR
+MSKNAM17                           IGNORE   STR
+MSKVAL13                           IGNORE   D010
+MSKVAL14                           IGNORE   D010
+MSKVAL15                           IGNORE   D010
+MSKVAL16                           IGNORE   D010
+MSKVAL17                           IGNORE   D010
+MSKNUM                             IGNORE   D001
+
+# astrometric quality checks:
+AST_CDX                            IGNORE   D000
+AST_CDY                            IGNORE   D000
+AST_CRX                            IGNORE   D001
+AST_CRY                            IGNORE   D000
+AST_CSX                            IGNORE   D001
+AST_CSY                            IGNORE   D000
+AST_MDX                            IGNORE   D001
+AST_MDY                            IGNORE   D000
+AST_MRX                            IGNORE   D000
+AST_MRY                            IGNORE   D001
+AST_MSX                            IGNORE   D001
+AST_MSY                            IGNORE   D000
+CERROR                             IGNORE   D001
+PERROR                             IGNORE   D001
+CPRECISE                           IGNORE   D001
+
+# astromety calibration 
+CRVAL1                             IGNORE   D000
+CRVAL2                             IGNORE   D000
+CDELT1                             IGNORE   D000
+CDELT2                             IGNORE   D000
+PC001001                           IGNORE   D000
+PC001002                           IGNORE   D010
+PC002001                           IGNORE   D001
+PC002002                           IGNORE   D000
+PCA1X0Y2                           IGNORE   D010
+PCA1X0Y3                           IGNORE   D010
+PCA1X1Y1                           IGNORE   D001
+PCA1X1Y2                           IGNORE   D001
+PCA1X2Y0                           IGNORE   D010
+PCA1X2Y1                           IGNORE   D010
+PCA1X3Y0                           IGNORE   D010
+PCA2X0Y2                           IGNORE   D000
+PCA2X0Y3                           IGNORE   D001
+PCA2X1Y1                           IGNORE   D100
+PCA2X1Y2                           IGNORE   D010
+PCA2X2Y0                           IGNORE   D010
+PCA2X2Y1                           IGNORE   D010
+PCA2X3Y0                           IGNORE   D001
+
+# warp-to-chip terms (depend on astrometry)
+MPX_0000			   IGNORE   STR
+MPX_0001		   	   IGNORE   STR
+MPX_0002		   	   IGNORE   STR
+MPX_0003		   	   IGNORE   STR
+MPX_0004		   	   IGNORE   STR
+MPX_0005		   	   IGNORE   STR
+MPX_0006		   	   IGNORE   STR
+MPY_0000		   	   IGNORE   STR
+MPY_0001		   	   IGNORE   STR
+MPY_0002		   	   IGNORE   STR
+MPY_0003		   	   IGNORE   STR
+MPY_0004		   	   IGNORE   STR
+MPY_0005		   	   IGNORE   STR
+MPY_0006		   	   IGNORE   STR
+
+# detection limit
+DETEFF.MAGREF                      IGNORE   D000
+
+# timing:
+DT_ASTR                            IGNORE   D010
+DT_PHOT                            IGNORE   D010
+
+# PSF & IQ information
+CHIP.SEEING                        IGNORE   D000
+FWHM_MAJ                           IGNORE   D000
+FWHM_MIN                           IGNORE   D001
+FW_MJ_LQ                           IGNORE   D001
+FW_MJ_SG                           IGNORE   D010
+FW_MJ_UQ                           IGNORE   D001
+FW_MN_LQ                           IGNORE   D001
+FW_MN_SG                           IGNORE   D010
+FW_MN_UQ                           IGNORE   D000
+IQ_FW1                             IGNORE   D000
+IQ_FW1_E                           IGNORE   D000
+IQ_FW2                             IGNORE   D000
+IQ_FW2_E                           IGNORE   D000
+IQ_M2                              IGNORE   D000
+IQ_M2C                             IGNORE   D001
+IQ_M2C_E                           IGNORE   D000
+IQ_M2C_L                           IGNORE   D001
+IQ_M2C_U                           IGNORE   D000
+IQ_M2S                             IGNORE   D001
+IQ_M2S_E                           IGNORE   D000
+IQ_M2S_L                           IGNORE   D000
+IQ_M2S_U                           IGNORE   D000
+IQ_M2_ER                           IGNORE   D001
+IQ_M2_LQ                           IGNORE   D000
+IQ_M2_UQ                           IGNORE   D000
+IQ_M3                              IGNORE   D000
+IQ_M3_ER                           IGNORE   D000
+IQ_M3_LQ                           IGNORE   D000
+IQ_M3_UQ                           IGNORE   D000
+IQ_M4                              IGNORE   D000
+IQ_M4_ER                           IGNORE   D000
+IQ_M4_LQ                           IGNORE   D000
+IQ_M4_UQ                           IGNORE   D000
+IQ_NSTAR                           IGNORE   D000
+SIGNIFICANCE_SCALE_FACTOR          IGNORE   D001
+EFFECTIVE_AREA			   IGNORE   D001
+ANGLE				   IGNORE   D100
+
+# aperture correction
+APLOSS                             IGNORE   D001
+APMIFIT                            IGNORE   D010
+APREFOFF                           IGNORE   D001
+DAPMIFIT                           IGNORE   D001
+PSPHOT.PSF.APRESID                 IGNORE   D001
+PSPHOT.PSF.APRESID.SYSERR          IGNORE   D010
+
+# background model stats
+MSKY_DEV                           IGNORE   D000
+MSKY_DQ                            IGNORE   D010
+MSKY_MAX                           IGNORE   D000
+MSKY_MIN                           IGNORE   D000
+MSKY_MN                            IGNORE   D000
+MSKY_SIG                           IGNORE   D000
+
+# numbers of stars / detections
+NAPMIFIT                           IGNORE   D000
+NASTRO                             IGNORE   D001
+NDET                               IGNORE   D000
+NDET_CR                            IGNORE   D001
+NDET_EXT                           IGNORE   D001
+NPSFSTAR                           IGNORE   D001
+NSTARS                             IGNORE   D000
+
+# photometry zero points:
+ZPT_ERR                            IGNORE   D001
+ZPT_NREF                           IGNORE   D000
+ZPT_OBS                            IGNORE   D000
+ZPT_OFF                            IGNORE   D000
+FPA.ZP                             IGNORE   D000 # make this a test?
Index: /trunk/ippTests/ippCheck/ippconfig/warp.psfFields.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/warp.psfFields.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/warp.psfFields.bk	(revision 42085)
@@ -0,0 +1,66 @@
+
+# definition of the pages:
+# STAT: what statistic to use to compare (VALUE, RVALUE, RATIO)
+# SCALE : multiply the stat by this value before comparing to MIN,MAX
+# MIN_GOOD: 
+# MAX_GOOD:
+# MAG_MAX : clip values fainter than this PSF_INST_MAG
+LAYOUT                   STAT   MIN_GOOD MAX_GOOD SCALE  MIN_MAG MAX_MAG MIN_ERR MAX_ERR  
+								          
+                 AP_FLUX NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             AP_FLUX_SIG NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                  AP_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01
+           AP_MAG_RADIUS VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+              AP_MAG_RAW NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                 AP_NPIX IVALUE -5.0     +5.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+             CAL_PSF_MAG VALUE  -0.04    +0.04	 1.0	   -15.0    -9.0  0.001	   0.01
+         CAL_PSF_MAG_SIG VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # field has only a single value (estimate of systematic error?)	  
+               CR_NSIGMA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                 DEC_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec
+              EXT_NSIGMA VALUE  -2.0     +2.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # range seems quite large (nearly 1 sigma)
+                   FLAGS NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # diffcult to compare statistically
+                  FLAGS2 NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # diffcult to compare statistically
+                IPP_IDET NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01 # detection ID sequence is not critical
+               KRON_FLUX NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+           KRON_FLUX_ERR NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+         KRON_FLUX_INNER NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+         KRON_FLUX_OUTER NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+             MOMENTS_M3C NONE   -0.03    +0.03   1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED	  
+             MOMENTS_M3S NONE   -0.30    +0.30 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             MOMENTS_M4C NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+             MOMENTS_M4S NONE   -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_R1 NONE   -0.10    +0.10 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_RH NONE   -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_XX NONE   -0.10    +0.10 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_XY NONE   -0.05    +0.05 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+              MOMENTS_YY NONE   -0.20    +0.20 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                N_FRAMES IVALUE -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # largely good agreement
+                 PADDING NONE   -1.0     +1.0    1.0       -15.0    -9.0  0.001	   0.01 # comparison does not make sense
+        PEAK_FLUX_AS_MAG VALUE  -0.02    +0.02 	 1.0	   -15.0    -9.0  0.001	   0.01 # this scatter must come from the background model
+                PLTSCALE VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 # differences should be due to different astrometry solutions
+                POSANGLE VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution has a strange cutoff at small positive value?
+               PSF_CHISQ RVALUE -0.50    +0.50 	 1.0	   -15.0    -9.0  0.001	   0.01 # I do not have a great prior on this
+                PSF_CORE NONE   -0.1     +0.1  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+            PSF_FWHM_MAJ NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # POPULATED VALUES ARE BOGUS
+            PSF_FWHM_MIN NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # POPULATED VALUES ARE BOGUS
+           PSF_INST_FLUX NONE   -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+       PSF_INST_FLUX_SIG NONE   -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+            PSF_INST_MAG VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01
+        PSF_INST_MAG_SIG VALUE  -1e-3    +1e-3 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               PSF_MAJOR NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+               PSF_MINOR NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                PSF_NDOF NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 
+                PSF_NPIX NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01	  
+                  PSF_QF VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+          PSF_QF_PERFECT VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+               PSF_THETA NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # NOT CURRENTLY POPULATED
+                  RA_PSF VALUE  -0.1     +0.1  	 3600.0	   -15.0    -9.0  0.001	   0.01 # delta is in arcsec, but NOT cos-dec corrected
+                     SKY RVALUE -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # this value probably depends on the sky level?
+          SKY_LIMIT_FLUX NONE   -1.0     +1.0  	 3.0	   -15.0    -9.0  0.001	   0.01 # 
+           SKY_LIMIT_RAD NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # distribution is odd
+         SKY_LIMIT_SLOPE NONE   -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # many values are NAN
+               SKY_SIGMA VALUE  -1.0     +1.0  	 1.0	   -15.0    -9.0  0.001	   0.01 # probably depends on the sky level?	  
+                   X_PSF VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 # the bright objects are more scattered than I expect
+               X_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
+                   Y_PSF VALUE  -0.04    +0.04 	 1.0	   -15.0    -9.0  0.001	   0.01 
+               Y_PSF_SIG VALUE  -0.01    +0.01 	 1.0	   -15.0    -9.0  0.001	   0.01 # too generous?
Index: /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.bk	(revision 42085)
@@ -0,0 +1,17 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+NAXIS2                             IGNORE   D001
+	  			      
Index: /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.t0.bk
===================================================================
--- /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.t0.bk	(revision 42085)
+++ /trunk/ippTests/ippCheck/ippconfig/warp.psfKeys.t0.bk	(revision 42085)
@@ -0,0 +1,21 @@
+
+## keys listed in thie file should be ignored if they differ
+## (for now we do not enforce an allowed range)
+
+# definition of the pages:
+# ACTION : what behavior for this keyword (IGNORE : currently the only allowed option)
+# SIZE   : example scale of fractional difference seen
+#          D100 : > 100% difference
+#          D010 : 10% - 100% difference
+#          D001 : 1% - 10% difference
+#          D000 : < 1% difference
+
+LAYOUT                             ACTION   SIZE
+
+# general metadata
+NAXIS2                             IGNORE   D001 # we do not always have the same number of detections
+	  			      
+# old vs new FITS I/O has difference in layout for empty tables
+NAXIS                              IGNORE   D001 
+BITPIX                             IGNORE   D001 
+XTENSION                           IGNORE   D001 
Index: /trunk/ippTests/ippCheck/prcol
===================================================================
--- /trunk/ippTests/ippCheck/prcol	(revision 42085)
+++ /trunk/ippTests/ippCheck/prcol	(revision 42085)
@@ -0,0 +1,22 @@
+#!/bin/csh -f
+
+if ($#argv == 0) then
+ echo "USAGE: prcol start [end]"
+ echo "  prints only start column if no end"
+ exit 2;
+endif
+
+if ("$2" != "") then
+ set range=1
+ set startcol=$1
+ set endcol=$2
+else 
+ set range=0
+ set coln=$1
+endif
+
+if ($range) then
+ awk -v n1=$startcol -v n2=$endcol '{for (i=n1; i<=n2; i++) {printf "%s ", $i}}{printf "\n"}'
+else
+ awk -v n=$coln '{print $n}'
+endif
Index: /trunk/ippTests/ippCheck/smfcheck.pro
===================================================================
--- /trunk/ippTests/ippCheck/smfcheck.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/smfcheck.pro	(revision 42085)
@@ -0,0 +1,1197 @@
+#!/usr/bin/env dvo
+# compare smf-to-smf
+# opihi verbose on
+
+if (not($?SAVEPLOT)) set SAVEPLOT = 0
+if (not($?SAVEDATA)) set SAVEDATA = 0
+
+module tap.pro
+
+###########
+macro smf.compare.cam.all.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 4)
+    echo "USAGE: smf.compare.cam.all.by.datagroups (outdir) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local outdir dgrp1 dgrp2 command i npages expname
+  $outdir = $1
+  $dgrp1 = $2
+  $dgrp2 = $3
+
+  # identify the list of exposures in the data groups
+  $command = camtool -dbname $CAMERA -processedexp -data_group $dgrp2
+  
+  queueload  myqueue -x "$command"
+  queue2book myqueue mybook
+  
+  book npages mybook -var npages
+  
+  if ($npages < 1) 
+   echo "ERROR: missing exposures for $dgrp2 : $command"
+   book delete mybook
+   next
+  end
+  
+  $options =
+  setargs +plot   BOOL  $SAVEPLOT
+  setargs +dump   BOOL  $SAVEDATA
+  setargs -camera VALUE $CAMERA
+
+  mkdir $outdir
+
+  for i 0 $npages  
+    sprint pagename page.%03d $i
+    book getword mybook $pagename exp_name -var expname
+
+    echo ippCheck smf.dgrp.exp camera $outdir/$expname.cam/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.cam.log
+    exec ippCheck smf.dgrp.exp camera $outdir/$expname.cam/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.cam.log
+  end
+  book delete mybook
+end
+
+###########
+macro smf.compare.wrp.all.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 4)
+    echo "USAGE: smf.compare.wrp.all.by.datagroups (outdir) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local outdir dgrp1 dgrp2 command i npages expname
+  $outdir = $1
+  $dgrp1 = $2
+  $dgrp2 = $3
+
+  # identify the list of exposures in the data groups
+  $command = warptool -dbname $CAMERA -listrun -data_group $dgrp2
+  
+  queueload  myqueue -x "$command"
+  queue2book myqueue mybook
+  
+  book npages mybook -var npages
+  
+  if ($npages < 1) 
+   echo "ERROR: missing exposures for $dgrp2 : $command"
+   book delete mybook
+   next
+  end
+  
+  $options =
+  setargs +plot   BOOL  $SAVEPLOT
+  setargs +dump   BOOL  $SAVEDATA
+  setargs -camera VALUE $CAMERA
+
+  mkdir $outdir
+
+  for i 0 $npages  
+    sprint pagename page.%03d $i
+    book getword mybook $pagename exp_name -var expname
+
+    echo ippCheck smf.dgrp.exp warp $outdir/$expname.wrp/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.wrp.log
+    exec ippCheck smf.dgrp.exp warp $outdir/$expname.wrp/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.wrp.log
+  end
+  book delete mybook
+end
+
+###########
+macro smf.compare.dif.all.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 4)
+    echo "USAGE: smf.compare.dif.all.by.datagroups (outdir) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local outdir dgrp1 dgrp2 command i npages expname
+  $outdir = $1
+  $dgrp1 = $2
+  $dgrp2 = $3
+
+  # identify the list of exposures in the data groups
+  $command = difftool -dbname $CAMERA -listrun -data_group $dgrp2
+  
+  queueload  myqueue -x "$command"
+  queue2book myqueue mybook
+  
+  $camPath = NONE
+  book npages mybook -var npages
+  
+  if ($npages < 1) 
+   echo "ERROR: missing exposures for $dgrp2 : $command"
+   book delete mybook
+   next
+  end
+  
+  $options =
+  setargs +plot   BOOL  $SAVEPLOT
+  setargs +dump   BOOL  $SAVEDATA
+  setargs -camera VALUE $CAMERA
+
+  mkdir $outdir
+
+  # we are selecting exp_name_1 so we know the exposure is the positive image (not template)
+  for i 0 $npages  
+    sprint pagename page.%03d $i
+    book getword mybook $pagename exp_name_1 -var expname
+
+    echo ippCheck smf.dgrp.exp diff $outdir/$expname.dif/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.dif.log
+    exec ippCheck smf.dgrp.exp diff $outdir/$expname.dif/$expname $expname $dgrp1 $dgrp2 $options >& $outdir/$expname.dif.log
+  end
+  book delete mybook
+end
+
+###########
+macro smf.compare.cam.exp.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 5)
+    echo "USAGE: smf.compare.cam.exp.by.datagroups (output) (expname) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local output expname dgrp1 dgrp2 saveplot
+  $output  = $1
+  $expname = $2
+  $dgrp1   = $3
+  $dgrp2   = $4
+
+  # output goes to $camPath
+  get.camera.metadata.by.datagroup $CAMERA $expname $dgrp1; $smfBase1 = $camPath
+  get.camera.metadata.by.datagroup $CAMERA $expname $dgrp2; $smfBase2 = $camPath
+
+  tap.plan $NTESTS_CMF
+
+  $options =
+  setargs +plot   BOOL  $SAVEPLOT
+  setargs +dump   BOOL  $SAVEDATA
+  echo smf.compare.all.extensions $output $smfBase1.smf $smfBase2.smf $options
+       smf.compare.all.extensions $output $smfBase1.smf $smfBase2.smf $options
+  tap.done
+end
+
+# XXX needs some error handling
+macro smf.compare.wrp.exp.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 5)
+    echo "USAGE: smf.compare.wrp.exp.by.datagroups (output) (expname) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local output expname dgrp1 dgrp2 saveplot npages1 npages2 cmfBase1 cmfBase2 i
+  $output  = $1
+  $expname = $2
+  $dgrp1   = $3
+  $dgrp2   = $4
+
+  # XXX ensure warpBook1 & warpBook2 are deleted / empty first
+  get.warp.metadata.by.datagroup warpBook1 $CAMERA $expname $dgrp1
+  get.warp.metadata.by.datagroup warpBook2 $CAMERA $expname $dgrp2
+
+  book npages warpBook1 -var npages1
+  if ($npages1 < 1) 
+    echo "ERROR: missing warps for exposure : $expname ($dgrp1)"
+    book delete warpBook1 
+    book delete warpBook2
+    next
+  end
+ 
+  book npages warpBook2 -var npages2
+  if ($npages1 != $npages2) 
+    echo "WARNING: inconsistent number of warps for exposure : $expname ($npages1 vs $npages2)"
+  end
+ 
+  tap.plan {$npages1 * $NTESTS_CMF}
+
+  for i 0 $npages1
+    book getpage warpBook1 $i -var mypage1
+    book getword warpBook1 $mypage1 skycell_id -var skycellID
+    book getword warpBook1 $mypage1 path_base  -var cmfBase1
+
+    # select the page with the same skycell_id as cmfFile1
+    # XXX if this page is not found, raise an error / warning and skip the rest
+    book getpage warpBook2 0  -key skycell_id $skycellID -var mypage2
+    book getword warpBook2 $mypage2 path_base  -var cmfBase2
+
+    $options =
+    setargs +plot   BOOL  $SAVEPLOT
+    setargs +dump   BOOL  $SAVEDATA
+    echo smf.compare.all.extensions $output.$skycellID $cmfBase1.cmf $cmfBase2.cmf $options
+         smf.compare.all.extensions $output.$skycellID $cmfBase1.cmf $cmfBase2.cmf $options
+  end
+  
+  book delete warpBook1
+  book delete warpBook2
+  
+  tap.done
+end
+
+# XXX needs some error handling
+macro smf.compare.dif.exp.by.datagroups
+  $CAMERA = gpc1  
+
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+  getargs -camera   VALUE -var CAMERA
+
+  if ($0 != 5)
+    echo "USAGE: smf.compare.dif.exp.by.datagroups (output) (expname) (DG1) (DG2) [+plot] [-camera gpc2]"
+    break
+  end
+
+  local i output expname dgrp1 dgrp2 saveplot npages1 npages2 cmfBase1 cmfBase2 bothways
+  $output  = $1
+  $expname = $2
+  $dgrp1   = $3
+  $dgrp2   = $4
+
+  get.diff.metadata.by.datagroup diffBook1 $CAMERA $expname $dgrp1
+  get.diff.metadata.by.datagroup diffBook2 $CAMERA $expname $dgrp2
+
+  book npages diffBook1 -var npages1
+  if ($npages1 < 1) 
+    echo "ERROR: missing diffs for exposure : $expname ($dgrp1)"
+    book delete diffBook1 
+    book delete diffBook2
+    next
+  end
+ 
+  book npages diffBook2 -var npages2
+  if ($npages1 != $npages2) 
+    echo "WARNING: inconsistent number of diffs for exposure : $expname ($npages1 vs $npages2)"
+  end
+ 
+  tap.plan {2 * $npages1 * $NTESTS_CMF}
+
+  for i 0 $npages1
+    book getpage diffBook1 $i -var mypage1
+    book getword diffBook1 $mypage1 skycell_id -var skycellID
+    book getword diffBook1 $mypage1 path_base  -var cmfBase1
+
+    # select the page with the same skycell_id as cmfFile1
+    # XXX if this page is not found, raise an error / warning and skip the rest
+    book getpage diffBook2 0  -key skycell_id $skycellID -var mypage2
+    book getword diffBook2 $mypage2 path_base  -var cmfBase2
+    book getword diffBook2 $mypage2 bothways   -var bothways
+
+    $options =
+    setargs +plot   BOOL  $SAVEPLOT
+    setargs +dump   BOOL  $SAVEDATA
+    echo smf.compare.all.extensions $output.$skycellID $cmfBase1.cmf $cmfBase2.cmf $options
+         smf.compare.all.extensions $output.$skycellID $cmfBase1.cmf $cmfBase2.cmf $options
+
+    if ("$bothways" == "T")
+      echo smf.compare.all.extensions $output.$skycellID.inv $cmfBase1.inv.cmf $cmfBase2.inv.cmf $options
+           smf.compare.all.extensions $output.$skycellID.inv $cmfBase1.inv.cmf $cmfBase2.inv.cmf $options
+    else
+      tap.skip $NTESTS_CMF
+    end
+ end
+
+ book delete diffBook1
+ book delete diffBook2
+end
+
+## this function can be used for camera smf or warp & diff cmf files
+macro smf.compare.all.extensions
+  # defaults to 'false' if +plot is not provided
+  getargs +plot     BOOL  -var SAVEPLOT
+  getargs +dump     BOOL  -var SAVEDATA
+
+  if ($0 != 4)
+    echo "USAGE: smf.compare.all.extensions (output) (smf1) (smf2)"
+    echo " NOTE: smf files may be nebulous keys"
+    break
+  end
+
+  local outroot smfFile1 smfFile2 extname mydir
+  $outroot = $1
+  $smfFile1 = $2
+  $smfFile2 = $3
+
+  # NOTE: smf1 and smf2 cannot be local variables (names are used in convert.neb.name)
+  convert.neb.name $smfFile1 smf1
+  convert.neb.name $smfFile2 smf2
+
+  if ("$smf1" == "") 
+    echo "ERROR: missing smf/cmf file : $smfFile1"
+    tap.skip $NTESTS_CMF
+    next
+  end
+  if ("$smf2" == "") 
+    echo "ERROR: missing smf/cmf file : $smfFile2"
+    tap.skip $NTESTS_CMF
+    next
+  end
+
+  dirname $outroot -var mydir
+  mkdir $mydir
+
+  # does the smf have the right number of extensions?
+  # generate a list of just the extension names:
+  list tablelines -x "ftable -list $smf1"
+  if ($tablelines:n == 0)
+    echo "ERROR failed to read FITS extensions in file $smf1"
+    next
+  end
+  smf.compare.extract.extnames ext0 tablelines
+
+  list tablelines -x "ftable -list $smf2"
+  if ($tablelines:n == 0)
+    echo "ERROR failed to read FITS extensions in file $smf1"
+    next
+  end
+  smf.compare.extract.extnames ext1 tablelines
+
+  # generate matched lists of extensions $match1:N == $match2:N
+  smf.compare.match.extlists ext0 ext1
+
+  # track the number of chips for which the XYnn.hdr keys failed
+  $NfailHdr = 0
+  $NfailPsf = 0
+
+  # compare the matched extensions $ext1:N == $ext2:($match2:N)
+  for Next1 0 $ext0:n
+    $Next2 = $match1:$Next1
+    if ($Next2 == -1) 
+      echo "not ok : missing extenion $ext0:$Next1"
+      # XXX add a tap.skip here?
+      next
+    end
+
+    # tap.ok ($ext0:$Next1 == $ext1:$Next2) "matching ext names"
+    $extname = $ext0:$Next1
+    # NOTE: analysis below uses ext sequence number (i,j)
+    # extname is only used to name output products
+
+    # ippCheckKeys uses 0 == PHU, 1 = 1st extension
+    # smf.compare.onechip uses 0 = 1st extension 
+
+    # if extname is XYnn.hdr, compare the headers
+    strstr -q $extname .hdr -start s1
+    if ($s1 == 4)
+      echo "##### check keys for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.keys               #####"
+      exec ippCheckKeys $smf1 $Next1 $smf2 $Next2 >& $outroot.$extname.keys
+      smf.compare.keys $outroot.$extname.keys hdrKeys; # TAP: 1
+      if ($NfailKeys) set NfailHdr = $NfailHdr + 1
+    end
+
+    # if extname is XYnn.psf, compare the headers AND compare the table values
+    strstr -q $extname .psf -start s1
+    if ($s1 == 4)
+      echo "##### check keys for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.keys               #####"
+      exec ippCheckKeys $smf1 $Next1 $smf2 $Next2 >& $outroot.$extname.keys 
+      smf.compare.keys $outroot.$extname.keys psfKeys; # TAP: 1
+      if ($NfailKeys) set NfailPsf = $NfailPsf + 1
+
+      echo "##### check fields for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.txt                  #####"
+      smf.compare.onechip $outroot.$extname $smf1 {$Next1-1} $smf2 {$Next2-1}; # Note smf.compare.onechip counts from 0 = first extension
+      # TAP: 2 + Nfields
+    end
+
+    # if extname is SkyChip.hdr, compare the headers
+    strstr -q $extname .hdr -start s1
+    if ($s1 == 7)
+      echo "##### check keys for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.keys               #####"
+      exec ippCheckKeys $smf1 $Next1 $smf2 $Next2 >& $outroot.$extname.keys
+      smf.compare.keys $outroot.$extname.keys hdrKeys; # TAP: 1
+      if ($NfailKeys) set NfailHdr = $NfailHdr + 1
+    end
+
+    # if extname is SkyChip.psf, compare the headers AND compare the table values
+    strstr -q $extname .psf -start s1
+    if ($s1 == 7)
+      echo "##### check keys for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.keys               #####"
+      exec ippCheckKeys $smf1 $Next1 $smf2 $Next2 >& $outroot.$extname.keys 
+      smf.compare.keys $outroot.$extname.keys psfKeys; # TAP: 1
+      if ($NfailKeys) set NfailPsf = $NfailPsf + 1
+
+      echo "##### check fields for $extname == FITS entry number $Next1,$Next2 #####"
+      echo "##### comparison results in $outroot.$extname.txt                  #####"
+      smf.compare.onechip $outroot.$extname $smf1 {$Next1-1} $smf2 {$Next2-1}; # Note smf.compare.onechip counts from 0 = first extension
+    end
+  end
+  tap.ok ($NfailHdr == 0) "$NfailHdr HDR headers failed comparison" ; # TAP: each file
+  tap.ok ($NfailPsf == 0) "$NfailPsf PSF headers failed comparison" ; # TAP: each file
+end
+
+# generate plot and comparison txt file for one chip (extension)
+macro smf.compare.onechip
+  if ($0 != 6)
+    echo "USAGE: smf.compare.onechip (output) (smf1) (ext1) (smf2) (ext2)"
+    break
+  end
+
+  local output smf1 smf2 ext1 ext2
+  $output = $1
+  $smf1 = $2
+  $ext1 = $3
+  $smf2 = $4
+  $ext2 = $5
+
+  dirname $output -var mydir
+  mkdir $mydir
+
+  # XXX TEMP: remove existing output file 
+  exec rm -f $output.txt
+
+  # load in the full set of data for this extension
+  # NOTE : t0 and t1 are used to label the sets of vectors
+  smf.load.fields $smf1 $ext1 t1
+  $smf1_has_data = ($tfields:n > 0) && ($table:Ny > 0)
+
+  smf.load.fields $smf2 $ext2 t2
+  $smf2_has_data = ($tfields:n > 0) && ($table:Ny > 0)
+
+  tap.ok ($smf1_has_data == $smf2_has_data) "tables both have (or both lack) data" ; # TAP: each chip
+  if (not($smf1_has_data) || not($smf2_has_data))
+    echo "no data in tables $smf1 $ext1 or $smf2 $ext2, skipping"
+    # I should account for the number of skipped tests
+    local Nfields
+    book npages psfFields -var Nfields
+    tap.skip {$Nfields + 1}
+    return
+  end
+
+  # generate the matched lists (index1, index2)
+  smf.compare.positions $output t1 t2
+
+  # compare the values in the matched pairs for each field
+  smf.compare.fields $output t1 t2
+end
+
+# load in the data for a pair of smf fields for a single chip 
+macro smf.load.fields
+  if ($0 != 4)
+    echo "USAGE: smf.load.fields (smffile) (extension) (version)"
+    echo "  loads all fields and saves them with version appended to the names"
+    break
+  end
+
+  local version extnum filename
+  $filename = $1
+  $extnum = $2
+  $version = $3
+
+  data $filename
+  read -fits $extnum -extnum -list-fields -q
+  if ($tfields:n == 0) 
+    echo "FITS table in $filename is empty" 
+    return
+  end
+  if ($table:Ny == 0) 
+    echo "FITS table in $filename is empty" 
+    return
+  end
+  list tfields -join allfields
+  read -fits $extnum -extnum $allfields
+  
+  foreach f $allfields
+    set $f\_$version = $f
+    delete $f
+  end
+end
+
+$MATCH_RADIUS = 1; # match radius in pixels
+macro smf.compare.positions
+  if ($0 != 4)
+    echo "USAGE: smf.compare.positions (output) (v1) (v2)"
+    echo "generates matched position lists & stats"
+    break
+  end
+
+  local output v1 v2 i
+  $output = $1
+  $v1     = $2
+  $v2     = $3
+
+  match2d X_PSF_$v1 Y_PSF_$v1 X_PSF_$v2 Y_PSF_$v2 $MATCH_RADIUS -closest
+
+  # initial questions to answer:
+  # * how many sources from #1 are not in #2?
+  # * how many sources from #2 are not in #1?
+  reindex Xm_$v1 = X_PSF_$v1 using index2 -keep-unmatched
+  reindex Ym_$v1 = Y_PSF_$v1 using index2 -keep-unmatched
+
+  reindex Xm_$v2 = X_PSF_$v2 using index1 -keep-unmatched
+  reindex Ym_$v2 = Y_PSF_$v2 using index1 -keep-unmatched
+ 
+  vstat -q Xm_$v1
+  $N1_keep = $NPTS
+  $N1_miss = Xm_$v1[] - $N1_keep
+  
+  vstat -q Xm_$v2
+  $N2_keep = $NPTS
+  $N2_miss = Xm_$v2[] - $N2_keep
+ 
+  # 
+  output $output.txt
+  echo "PSF FIELDS: $N1_keep & $N1_miss vs $N2_keep & $N2_miss"
+  output stdout
+end
+
+macro smf.compare.fields
+  if ($0 != 4)
+    echo "USAGE: smf.pos.compare (output) (v1) (v2)"
+    echo "generates matched position lists & stats"
+    break
+  end
+
+  local output v1 v2 i
+  $output = $1
+  $v1     = $2
+  $v2     = $3
+
+  smf.fields.magplot.init
+  exec rm -f $output.txt
+
+  $NfailFieldsPSF = 0
+
+  # XXX this will fail if the two smfs do not have the same fields
+  list fields -split $allfields
+  for i 0 $fields:n
+    smf.compare.onefield    $output $fields:$i $v1 $v2
+    if (not($PASS)) set $NfailFieldsPSF = $NfailFieldsPSF + 1
+    smf.fields.magplot.show $output $fields:$i $v1 $v2
+    smf.fields.match.dump   $output $fields:$i $v1 $v2
+  end  
+
+  tap.ok ($NfailFieldsPSF == 0) "$NfailFieldsPSF fields failed comparison"; # TAP: each chip
+end
+
+macro smf.fields.magplot.init
+  if (not($SAVEPLOT)) return
+
+  clear -s
+  antialias 0.4
+  label -fn helvetica 24
+  resize 2000 800
+  section a 0.0 0.0 0.7 1.0
+  section b 0.7 0.0 0.3 1.0
+  $BSTY = -tickpad 0.1 -labelpadx 3.0 -xpad 3.5 +xpad 1.0
+end
+
+macro smf.fields.match.dump
+  if ($0 != 5)
+    echo "USAGE: smf.fields.match.dump (output) (field) (v1) (v2)"
+    echo " NOTE: run smf.pos.compare (or equiv) first to generate index1, index2"
+    break
+  end
+  
+  local output field v1 v2
+  $output = $1
+  $field  = $2
+  $v1     = $3
+  $v2     = $4
+
+  if (not($SAVEDATA)) return
+  write $output.$field.dat PSF_INST_MAG_$v1 PSF_INST_MAG_SIG_$v1 $field\_$v1 tmp_$v2 
+end
+
+macro smf.fields.magplot.show
+  if ($0 != 5)
+    echo "USAGE: smf.fields.magplot.show (output) (field) (v1) (v2)"
+    echo " NOTE: run smf.pos.compare (or equiv) first to generate index1, index2"
+    break
+  end
+  
+  local output field v1 v2
+  $output = $1
+  $field  = $2
+  $v1     = $3
+  $v2     = $4
+
+  if (not(dtmp?[])) return; # this happens if the field is not listed in the fields table (or stat is NONE)
+  if (not($SAVEPLOT)) return
+  if (isnan($hist:median)) return
+
+  clear
+  section a
+  lim PSF_INST_MAG_$v1 $hist:minPlt $hist:maxPlt; box $BSTY; plot PSF_INST_MAG_$v1 dtmp -pt cir -op 0.3
+  section b
+  if (dx[] == 1)
+    lim dx Ntmp; box $BSTY; plot -pt cir dx Ntmp; label -x $field
+  else
+    lim dx Ntmp; box $BSTY; plot -x hist dx Ntmp; label -x $field
+  end
+ 
+  png -name $output.$field.png
+end
+
+macro smf.compare.onefield
+  if ($0 != 5)
+    echo "USAGE: smf.fields.onefield (output) (field) (v1) (v2)"
+    echo " NOTE: run smf.compare.position (or equiv) first to generate index1, index2"
+    break
+  end
+  
+  local output field v1 v2
+  $output = $1
+  $field  = $2
+  $v1     = $3
+  $v2     = $4
+
+  $PASS = 1; # if we hit any errors below, 
+
+  # confirm that this field exists in the list of fields:
+  # XXX missing functions to test for book or page existence
+
+  # generate the comparison statistic
+  reindex tmp_$v2 = $field\_$v2 using index1 -keep-unmatched
+
+  # what statistic do we use to compare?
+  book getword psfFields $field STAT     -var useStat
+  book getword psfFields $field SCALE    -var statScale
+  book getword psfFields $field MIN_GOOD -var minGood
+  book getword psfFields $field MAX_GOOD -var maxGood
+  book getword psfFields $field MIN_MAG  -var minMag
+  book getword psfFields $field MAX_MAG  -var maxMag
+  book getword psfFields $field MIN_ERR  -var minErr
+  book getword psfFields $field MAX_ERR  -var maxErr
+
+  # this variable tells the name of the vector which 
+  # should be used to select the values to test 
+  # the choice of minErr / maxErr depends on this value
+  $keepFieldName = PSF_INST_MAG_SIG
+
+  delete -q dtmp
+  if ("$useStat" == "VALUE")
+    set dtmp = $statScale*(tmp_$v2 - $field\_$v1)
+    subset dtmpS = dtmp where (($keepFieldName\_$v1 > $minErr) && ($keepFieldName\_$v1 < $maxErr))
+  end
+  if ("$useStat" == "IVALUE")
+    set dtmp = tmp_$v2 - $field\_$v1
+    subset dtmpS = dtmp where (($keepFieldName\_$v1 > $minErr) && ($keepFieldName\_$v1 < $maxErr) && (tmp_$v2 != -4096) && ($field\_$v1 != -4096))
+  end
+  if ("$useStat" == "RATIO")
+    set dtmp = $statScale(tmp_$v2 / $field\_$v1)
+    subset dtmpS = dtmp where (($keepFieldName\_$v1 > $minErr) && ($keepFieldName\_$v1 < $maxErr))
+  end
+  if ("$useStat" == "RVALUE")
+    set dtmp = $statScale*(tmp_$v2 - $field\_$v1) / $field\_$v1
+    subset dtmpS = dtmp where (($keepFieldName\_$v1 > $minErr) && ($keepFieldName\_$v1 < $maxErr))
+  end
+  if (("$useStat" == "NONE") || ("$useStat" == "NULL"))
+    output $output.txt
+    fprintf "%24s :  $PASS : SKIP" $field
+    output stdout
+    tap.skip 1
+    return
+  end
+  if (dtmp?[] == 0)
+    echo "unknown STAT value $useStat"
+    break
+  end
+  
+  # measure the stats on the subset for the defined mag range
+  hist.range dtmpS Ntmp dx
+  vstat -q dtmpS
+
+  if ($hist:minVal < $minGood) set PASS = 0
+  if ($hist:maxVal > $maxGood) set PASS = 0
+  if (isinf($hist:maxVal)) set PASS = 0
+  if (isinf($hist:minVal)) set PASS = 0
+
+  # save the summary line to be written to a file by the calling function
+  output $output.txt
+  fprintf "%24s : %2d : %12.6g %12.6g : %12.6g : %12.6g - %12.6g" $field $PASS $MEAN $SIGMA $hist:median $hist:minVal $hist:maxVal
+  output stdout
+
+  tap.ok ($PASS) "PSF table field $field comparison"; # TAP: each field
+end
+
+macro smf.compare.keys
+  if ($0 != 3)
+    echo "USAGE: smf.compare.keys (file) (book)"
+    break
+  end
+
+  local filename bookname i tmpline newline Ns Ne keyword keymode myAction
+  $filename = $1
+  $bookname = $2
+
+  # read in all lines of the file to be checked
+  # NOTE: this list parsing strips out extra spaces and empty lines
+  # ALSO: the code below handles quotes in the input file poorly
+  list tmplines -x "cat $filename"
+
+  $NfailKeys = 0
+
+  for i 0 $tmplines:n
+    $tmpline = $tmplines:$i
+
+    # remove any trailing comments:
+    strstr -q "$tmpline" # -start Ns -length Ne
+
+    if ($Ns < 0)
+      # if no hash-mark is found, take the whole line
+      $newline = $tmpline
+    else
+      substr "$tmpline" 0 $Ns newline
+    end
+    if ("$newline" == "") continue; # if line contains only a comment, skip it
+
+    list words -split $newline
+
+    # keyword file should have lines of the form:
+    # MODE KEYWORD : VAL1 vs VAL2 : DFRAC
+    if ($words:n < 2)
+      echo "ERROR: no information on line $i" 
+      echo "$tmpline"
+      break
+    end
+
+    $keymode = $words:0
+    $keyword = $words:1
+
+    # keywords where the MODE is MISS are always an error (missing keyword)
+    if ("$keymode" == "MISS")
+      echo "FAILURE: keyword $keyword MISSING"
+      $NfailKeys ++
+      continue
+    end
+
+    # what about BOOL & STR (mismatched values)
+
+    book getword $bookname $keyword ACTION -var myAction
+    if ("$myAction" == "IGNORE") continue
+    
+    echo "FAILURE: keyword $keyword differs between files"
+    $NfailKeys ++
+  end
+  tap.ok ($NfailKeys == 0) "$NfailKeys keys failed comparison" ; # TAP: called 2x per chip
+end
+
+macro smf.compare.parse.book
+  if ($0 != 3)
+    echo "USAGE: smf.compare.parse.book (file) (book)"
+    break
+  end
+
+  local filename bookname pagename i j tmpline myChar Ns Ne
+  $filename = $1
+  $bookname = $2
+
+  # the fields table has row-by-row pages with values:
+  # LAYOUT   KEY   KEY   KEY
+  # PAGENAME VALUE VALUE VALUE
+
+  file $filename isFound
+  if (not($isFound)) 
+    echo "missing ippCheck config file $filename"
+    exit 2
+  end
+
+  # NOTE: this list parsing strips out extra spaces and empty lines
+  # ALSO: the code below handles quotes in the input file poorly
+  list tmplines -x "cat $filename"
+
+  book init $bookname; # creates the book if it does not exist
+
+  $myWords:n = 0
+
+  for i 0 $tmplines:n
+    $tmpline = $tmplines:$i
+
+    # remove any trailing comments:
+    strstr -q "$tmpline" # -start Ns -length Ne
+
+    if ($Ns < 0)
+      # if no hash-mark is found, take the whole line
+      $newline = $tmpline
+    else
+      substr "$tmpline" 0 $Ns newline
+    end
+    if ("$newline" == "") continue; # if line contains only a comment, skip it
+
+    list words -split $newline
+
+    if ($words:n < 2)
+      echo "ERROR: no information on line $i" 
+      echo "$tmpline"
+      break
+    end
+
+    $pagename = $words:0
+
+    if ($myWords:n == 0)
+     # the first uncommented line should define the list of words
+     if ("$pagename" != "LAYOUT")
+       echo "ERROR: invalid page layout on first line" 
+       echo "$newline"
+       break
+     end
+
+     # save these words on the list of words
+     list myWords -copy words
+     next
+    end
+
+    if ($words:n != $myWords:n)
+       echo "ERROR: inconsistent number of words for page on line $i ($words:n vs $myWords:n)" 
+       echo "$newline"
+       break
+    end
+
+    # the first word on the line gives the page name
+    book newpage $bookname $pagename
+
+    for j 1 $words:n
+      book setword $bookname $pagename $myWords:$j $words:$j
+    end
+  end
+end
+
+macro hist.range
+  if ($0 != 4)
+    echo "USAGE: hist.range (value) (Nvalue) (dvalue)"
+    break
+  end
+
+  # assume we have a small enough vector to sort and select
+  # the min and max percentile explicitly
+
+  local Nm Np No value10 value50 value90 range
+
+  $hist:minPlt = NAN
+  $hist:maxPlt = NAN
+  $hist:minVal = NAN
+  $hist:maxVal = NAN
+  $hist:delVal = NAN
+  $hist:median = NAN
+
+  if (not($1?[]))
+    echo "invalid vector $1"
+    break
+  end
+
+  subset _tmp_sort = $1 where not(isnan($1))
+  if (_tmp_sort[] < 2) return
+
+  sort _tmp_sort
+  $Nm = 0.1*_tmp_sort[]
+  $Np = 0.9*_tmp_sort[]
+  $No = 0.5*_tmp_sort[]
+
+  # 10 and 90 percentile values
+  $value10 = _tmp_sort[$Nm]
+  $value90 = _tmp_sort[$Np]
+  $value50 = _tmp_sort[$No]
+
+  # save the 10 and 90 percentile values
+  $hist:minVal = $value10
+  $hist:maxVal = $value90
+
+  # set the plotting range to be 3x the 10 - 90 percentile range
+  $range = 3*($value90 - $value10)
+  $hist:minPlt = $value50 - 0.5*$range
+  $hist:maxPlt = $value50 + 0.5*$range
+
+  $hist:delVal = $range / 250
+  $hist:median = $value50
+
+  if ($range == 0.0)
+    # if all values are the same, generate a vector with a single value of all points
+    delete -q $3
+    concat $hist:median $3
+    set $2 = zero($3) + _tmp_sort[]
+    return    
+  end
+
+  if (isnan($range))
+    # if all values are the same, generate a vector with a single value of all points
+    create $3 0 1
+    set $2 = zero($3) + _tmp_sort[]
+    return    
+  end
+
+  # XXX add histogram option to ignore out-of-range values
+  histogram $1 $2 $hist:minVal $hist:maxVal $hist:delVal -range $3
+  $2[0] = 0; $2[-1] = 0
+end
+
+macro get.camera.metadata.by.datagroup
+ if ($0 != 4)
+  echo "USAGE: get.camera.metadata.by.datagroup (camera) (expname) (data_group)"
+  echo "EX:    get.camera.metadata.by.datagroup gpc1 o9322g0253o MOPS.dailytestset.20190516ops40745"
+  echo "  sets camPath, chipID"
+  break
+ end
+
+ local dbname expname camdgrp
+ $dbname   = $1
+ $expname  = $2
+ $camdgrp  = $3
+
+ book init mybook
+
+ # the camera data_group should select a unique exposure
+ $command = camtool -dbname $dbname -processedexp -exp_name $expname -data_group $camdgrp
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ $camPath = NONE
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  next
+ end
+ 
+ if ($npages > 1) 
+  echo "ERROR: too many exposures : $command"
+  book delete mybook
+  next
+ end
+ 
+ sprint pagename page.%03d 0
+ book getword mybook $pagename path_base -var camPath
+ book getword mybook $pagename chip_id   -var chipID
+ book getword mybook $pagename cam_id    -var camID
+ book delete mybook
+end
+
+################
+macro get.warp.metadata.by.datagroup
+ if ($0 != 5)
+  echo "USAGE: get.warp.metadata.by.datagroup (bookname) (camera) (expname) (data_group)"
+  echo "EX:    get.warp.metadata.by.datagroup warpBook gpc1 o9322g0253o MOPS.dailytestset.20190516ops40745"
+  echo "  sets warp book"
+  break
+ end
+
+ local warpbook dbname expname warpdgrp
+ $warpbook = $1
+ $dbname   = $2
+ $expname  = $3
+ $warpdgrp = $4
+
+ book init $warpbook
+
+ # the warp data_group should select a unique exposure
+ $command = warptool -dbname $dbname -warped -exp_name $expname -data_group $warpdgrp
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue $warpbook -key skycell_id
+end
+
+################
+macro get.diff.metadata.by.datagroup
+ if ($0 != 5)
+  echo "USAGE: get.diff.metadata.by.datagroup (bookname) (camera) (expname) (data_group)"
+  echo "EX:    get.diff.metadata.by.datagroup diffBook gpc1 o9322g0253o MOPS.dailytestset.20190516ops40745"
+  echo "  sets diff book"
+  break
+ end
+
+ local diffbook dbname expname diffdgrp length
+ $diffbook = $1
+ $dbname   = $2
+ $expname  = $3
+ $diffdgrp = $4
+
+ book init $diffbook
+
+ # get the complete list of skyfiles for this exposure
+ # the diff data_group should select a unique exposure
+ # however, we do not know if the exposure was used as the positive or negative image:
+ $command = difftool -dbname $dbname -diffskyfile -exp_name $expname -data_group $diffdgrp 
+ queueload myqueue -x "$command"
+
+ queuesize myqueue -var length
+ if ($length == 0)
+   $command = difftool -dbname $dbname -diffskyfile -exp_name $expname -data_group $diffdgrp -template
+   queueload myqueue -x "$command"
+ end
+
+ queue2book myqueue $diffbook -key skycell_id
+end
+
+######## generate a list of the entries in list2 which correspond to the entries in list 1
+macro smf.compare.match.extlists
+ if ($0 != 3)
+  echo "USAGE: smf.compare.match.extlists (list1) (list2)"
+  break
+ end
+
+ # if I can assume the two lists have the same order (with just inserts or deletions in one or the other)
+ # then I can step through the list only once
+ # otherwise, I need to do N^2 comparisons?
+
+ $list1 = $1
+ $list2 = $2
+
+ $N1 = $$list1:n  
+ $N2 = $$list2:n  
+ 
+ for i 0 $N1
+   $match1:$i = -1
+ end
+ $match1:n = $N1
+
+ for i 0 $N2
+   $match2:$i = -1
+ end
+ $match2:n = $N2
+
+ for i 0 $N1
+   for j 0 $N2
+     if ($match2:$j != -1) continue
+     if ($$list1:$i == $$list2:$j)
+       $match1:$i = $j
+       $match2:$j = $i
+       last
+     end
+   end
+   if ($match1:$i == -1)
+     echo "ERROR: missing extension $$list1:$i in file 2"
+   end
+ end
+
+ for j 0 $N2
+   if ($match2:$j == -1)
+     echo "WARNING: unexpected extension $$list2:$j in file 2"
+   end
+ end
+end
+
+#######
+macro smf.compare.extract.extnames
+  if ($0 != 3)
+    echo "USAGE: smf.compare.extract.extnames (outlist) (inlist)"
+    break
+  end
+
+  local i j inlist outlist
+  $outlist = $1
+  $inlist = $2
+
+  # NOTE: inlist:0 is the title line
+  for i 1 $$inlist:n
+    $j = $i - 1
+    list words -split $$inlist:$i
+    $$outlist:$j = $words:0
+  end
+  $$outlist:n = $tablelines:n - 1
+end
+
+## XXX this macro is sort of the opposite of getargs: it
+## appends values to the variable options
+macro setargs
+  if ("$2" == "BOOL")
+    if ($3)
+      $options = $options $1
+    end
+  end
+
+  if ("$2" == "VALUE")
+    $options = $options $1 $3
+  end
+end
+
+macro convert.neb.name
+  if ($0 != 3)
+    echo "USAGE: convert.neb.name (inname) (outname)"
+    echo " replaces outname with either real filename for inname (from nebulous if appropriate)"
+    break
+  end
+
+  local inname outname prefix
+  $inname = $1
+  $outname = $2
+
+  # is this a nebulous key?
+  substr $inname 0 5 prefix
+  if ("$prefix" == "neb:/")
+    $$outname = `neb-locate -p $inname`
+    if ("$$outname" == "") 
+      echo "ERROR: no nebulous entry for file : $inname"
+      next
+    end
+  else 
+    $$outname = $inname
+  end
+end
+
+########## EXAMPLES:
+
+### example of plotting all outputs for a single chip:
+macro go.sample
+  $SAVEPLOT = 1
+  $KAPA = kapa -noX
+  smf.compare.parse.book fields.bk psfFields
+  smf.compare.onechip o9451g0319o/o9451g0319o.XY01 o9451g0319o.1833521.cm.2467375.smf o9451g0319o.1833521.cm.2467377.smf 2
+end
+
+### example of plotting all outputs for a single chip:
+macro go.warp
+  $SAVEPLOT = 1
+  $KAPA = kapa
+  smf.compare.parse.book fields.bk psfFields
+  smf.compare.onechip w1.phot.rnd w1.phot.v0.fpa.cmf w1.phot.v1.fpa.cmf 0
+end
+
+### example of plotting all outputs for a single chip:
+macro go.test
+  $SAVEPLOT = 1
+  $KAPA = kapa -noX
+#  smf.compare.parse.book test.bk psfFields
+#  smf.compare.onechip test o9451g0319o.1833521.cm.2467375.smf o9451g0319o.1833521.cm.2467377.smf 2
+  smf.compare.onechip o9451g0319o/o9451g0319o.XY01 o9451g0319o.1833521.cm.2467375.smf o9451g0319o.1833521.cm.2467377.smf 2
+end
+
+### example of plotting all outputs for a single chip:
+macro full.test
+  if ($0 != 2)
+    echo "USAGE: full.test (version)"
+    break
+  end
+    
+  $SAVEPLOT = 0
+  $KAPA = kapa -noX
+  check.extensions o9451g0319o.$1/o9451g0319o.$1 o9451g0319o.1833521.cm.2467375.smf o9451g0319o.1833521.cm.2467377.smf
+end
+#########
+
+## if ($SCRIPT)
+##   if ($argv:n != 3)
+##     echo "USAGE: smfcheck.pro (output) (smf1) (smf1) [+plot]"
+##     break
+##   end
+##   
+##   list argv -join arglist
+## 
+##   smf.compare.all.extensions $arglist
+##   exit 0
+## end
Index: /trunk/ippTests/ippCheck/src/Makefile.am
===================================================================
--- /trunk/ippTests/ippCheck/src/Makefile.am	(revision 42085)
+++ /trunk/ippTests/ippCheck/src/Makefile.am	(revision 42085)
@@ -0,0 +1,15 @@
+bin_PROGRAMS = ippCheckKeys
+
+ippCheckKeys_CFLAGS  = $(DVOCORR_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ippCheckKeys_LDFLAGS = $(DVOCORR_LIBS)   $(PSMODULE_LIBS)   $(PSLIB_LIBS)
+ippCheckKeys_SOURCES = \
+	ippCheckKeys.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /trunk/ippTests/ippCheck/src/ippCheckKeys.c
===================================================================
--- /trunk/ippTests/ippCheck/src/ippCheckKeys.c	(revision 42085)
+++ /trunk/ippTests/ippCheck/src/ippCheckKeys.c	(revision 42085)
@@ -0,0 +1,370 @@
+// # include <ippCheckKeys.h>
+# include <pslib.h>
+
+bool PrintMismatchedKeys (psMetadata *list, psMetadata *v1, psMetadata *v2, char *prefix);
+bool PrintMissingKeys (psMetadata *list, char *prefix);
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    if (argc != 5) {
+	fprintf (stderr, "USAGE: ippCheckKeys file1 ext1 file2 ext2\n");
+	exit (2);
+    }
+
+    // this is the reference file
+    psFits *file_1 = psFitsOpen (argv[1], "r");
+
+    int ext_1 = atoi (argv[2]);
+    psFitsMoveExtNum(file_1, ext_1, false);
+
+    psMetadata *head_1 = psFitsReadHeader (NULL, file_1);
+
+    // this is the test file
+    psFits *file_2 = psFitsOpen (argv[3], "r");
+
+    int ext_2 = atoi (argv[4]);
+    psFitsMoveExtNum(file_2, ext_2, false);
+
+    psMetadata *head_2 = psFitsReadHeader (NULL, file_2);
+
+    // I want to save a few lists of issues:
+    // 1) missing keywords
+    // 2) mismatch types
+    // 3) mismatch booleans
+    // 4) values differ by 100%
+    // 5) values differ by 10%
+    // 6) values differ by 1%
+
+    psMetadata *missing = psMetadataAlloc ();
+    psMetadata *badtype = psMetadataAlloc ();
+    psMetadata *badbool = psMetadataAlloc ();
+    psMetadata *offstr  = psMetadataAlloc ();
+    psMetadata *off100  = psMetadataAlloc ();
+    psMetadata *off010   = psMetadataAlloc ();
+    psMetadata *off001    = psMetadataAlloc ();
+    psMetadata *offlow  = psMetadataAlloc ();
+
+    // compare keywords
+    psMetadataIterator *iter = psMetadataIteratorAlloc (head_1, PS_LIST_HEAD, NULL);
+    psMetadataItem *item_1 = NULL;
+    while ((item_1 = psMetadataGetAndIncrement (iter)) != NULL) {
+
+	// skip standard duplicate entries:
+	if (!strcmp(item_1->name, "HISTORY")) continue;
+	if (!strcmp(item_1->name, "COMMENT")) continue;
+
+        psMetadataItem *item_2 = psMetadataLookup(head_2, item_1->name);
+	if (!item_2) {
+	    // fprintf (stdout, "key %s in 1 not in 2\n", item_1->name);
+	    psMetadataItem *newItem = psMetadataItemCopy(item_1); // Copied item
+	    psMetadataAddItem (missing, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+	    continue;
+	}
+
+	// char *type_1 = NULL; PS_TYPE_NAME (type_1, item_1->type);
+	// char *type_2 = NULL; PS_TYPE_NAME (type_2, item_2->type);
+
+	if (item_1->type != item_2->type) {
+	    // fprintf (stdout, "key %s type mismatch: %s vs %s\n", item_1->name, type_1, type_2);
+	    psMetadataItem *newItem = psMetadataItemCopy(item_1); // Copied item
+	    psMetadataAddItem (badtype, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+	    continue;
+	}
+
+# define SAVE_ITEM_TWO							\
+	if (fabs(ratio) > 1.0)    {					\
+	    psMetadataItem *newItem = psMetadataItemCopy(item_2);	\
+	    psMetadataAddItem (off100, newItem, PS_LIST_TAIL, PS_META_REPLACE); \
+	    continue;							\
+	}								\
+	if (fabs(ratio) > 0.1)    {					\
+	    psMetadataItem *newItem = psMetadataItemCopy(item_2);	\
+	    psMetadataAddItem (off010, newItem, PS_LIST_TAIL, PS_META_REPLACE); \
+	    continue;							\
+	}								\
+	if (fabs(ratio) > 0.01)    {					\
+	    psMetadataItem *newItem = psMetadataItemCopy(item_2);	\
+	    psMetadataAddItem (off001, newItem, PS_LIST_TAIL, PS_META_REPLACE); \
+	    continue;							\
+	}								\
+	if (fabs(ratio) <= 0.01)    {					\
+	    psMetadataItem *newItem = psMetadataItemCopy(item_2);	\
+	    psMetadataAddItem (offlow, newItem, PS_LIST_TAIL, PS_META_REPLACE); \
+	    continue;							\
+	}
+
+	switch (item_1->type) {
+	  case PS_DATA_BOOL:
+	    if (item_1->data.B != item_2->data.B) {
+		// fprintf (stdout, "key %s value mismatch: %c vs %c\n", item_1->name, (item_1->data.B ? 'T' : 'F'), (item_2->data.B ? 'T' : 'F'));
+		psMetadataItem *newItem = psMetadataItemCopy(item_2); // Copied item
+		psMetadataAddItem (badbool, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+	    }
+	    break;
+
+	  case PS_DATA_S32:
+	    if (item_1->data.S32 != item_2->data.S32) {
+		// fprintf (stdout, "key %s value mismatch: %d vs %d\n", item_1->name, item_1->data.S32, item_2->data.S32);
+		psS32 delta = (item_1->data.S32 - item_2->data.S32);
+		float ratio = delta / (float) item_1->data.S32;
+		SAVE_ITEM_TWO;
+	    }
+	    break;
+
+	  case PS_DATA_S64:
+	    if (item_1->data.S64 != item_2->data.S64) {
+		psS64 delta = (item_1->data.S64 - item_2->data.S64);
+		float ratio = delta / (float) item_1->data.S64;
+		SAVE_ITEM_TWO;
+	    }
+	    break;
+		
+	  case PS_DATA_F32:
+	    if (item_1->data.F32 != item_2->data.F32) {
+		psF32 delta = (item_1->data.F32 - item_2->data.F32);
+		psF32 ratio = delta / (float) item_1->data.F32;
+		SAVE_ITEM_TWO;
+	    }
+	    break;
+		
+	  case PS_DATA_F64:
+	    if (item_1->data.F64 != item_2->data.F64) {
+		psF64 delta = (item_1->data.F64 - item_2->data.F64);
+		psF64 ratio = delta / (float) item_1->data.F64;
+		SAVE_ITEM_TWO;
+	    }
+	    break;
+		
+	  case PS_DATA_STRING:
+	    // allow mis-matched case?
+	    if (strcmp (item_1->data.str, item_2->data.str)) {
+		// fprintf (stdout, "key %s value mismatch: %s vs %s\n", item_1->name, item_1->data.str, item_2->data.str);
+		psMetadataItem *newItem = psMetadataItemCopy(item_2); // Copied item
+		psMetadataAddItem (offstr, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+	    }
+	    break;
+
+	  default:
+	    fprintf (stdout, "key %s unexpected type: %d\n", item_1->name, item_1->type);
+	    break;
+	}
+	// fprintf (stdout, "name: %s, type: %s\n", item->name, myType);
+    }
+
+    if (psListLength(missing->list)) {
+	fprintf (stdout, "\n# --- Keys missing from file 2 ---\n");
+	PrintMissingKeys (missing, "MISS");
+    }
+
+    if (psListLength(badtype->list)) {
+	fprintf (stdout, "\n# --- Keys with inconsistent types ---\n");
+	PrintMissingKeys (badtype, "TYPE");
+    }
+
+    if (psListLength(offstr->list)) {
+	fprintf (stdout, "\n# --- Keys with mismatched strings ---\n");
+	PrintMismatchedKeys (offstr, head_1, head_2, "STR ");
+    }
+
+    if (psListLength(badbool->list)) {
+	fprintf (stdout, "\n# --- Keys with mismatched boolean values ---\n");
+	PrintMismatchedKeys (badbool, head_1, head_2, "BOOL");
+    }
+
+    if (psListLength(off100->list)) {
+	fprintf (stdout, "\n# --- Keys inconsistent by 100%% or more ---\n");
+	PrintMismatchedKeys (off100, head_1, head_2, "D100");
+    }
+
+    if (psListLength(off010->list)) {
+	fprintf (stdout, "\n# --- Keys inconsistent by 10 - 100%% or more ---\n");
+	PrintMismatchedKeys (off010, head_1, head_2, "D010");
+    }
+
+    if (psListLength(off001->list)) {
+	fprintf (stdout, "\n# --- Keys inconsistent by 1 - 10%% or more ---\n");
+	PrintMismatchedKeys (off001, head_1, head_2, "D001");
+    }
+    
+    if (psListLength(offlow->list)) {
+	fprintf (stdout, "\n# --- Keys inconsistent by < 1%% ---\n");
+	PrintMismatchedKeys (offlow, head_1, head_2, "D000");
+    }
+
+    exit (0);
+}
+
+// we only need to support 4 types: BOOL, STRING, S32, S64, F32, F64:
+bool PrintMismatchedKeys (psMetadata *list, psMetadata *v1, psMetadata *v2, char *prefix) {
+
+    // compare keywords
+    psMetadataIterator *iter = psMetadataIteratorAlloc (list, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+
+        psMetadataItem *i1 = psMetadataLookup(v1, item->name);
+	psAssert (i1, "is this possible?");
+
+        psMetadataItem *i2 = psMetadataLookup(v2, item->name);
+	psAssert (i2, "is this possible?");
+
+	int longName = (strlen(item->name) > 8);
+	longName = true;
+	
+	switch (i1->type) {
+	  case PS_DATA_BOOL:
+	    if (longName) {
+		fprintf (stdout, "%4s %32s : %12c vs %12c\n", prefix, item->name, (i1->data.B ? 'T' : 'F'), (i2->data.B ? 'T' : 'F'));
+	    } else {
+		fprintf (stdout, "%4s %8s : %12c vs %12c\n", prefix, item->name, (i1->data.B ? 'T' : 'F'), (i2->data.B ? 'T' : 'F'));
+	    }
+	    break;
+	  case PS_DATA_S32: {
+	      psS32 delta = i1->data.S32 - i2->data.S32;
+	      float ratio = delta / (float) i1->data.S32;
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12d vs %12d : %12.5g\n", prefix, item->name, i1->data.S32, i2->data.S32, ratio);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12d vs %12d : %12.5g\n", prefix, item->name, i1->data.S32, i2->data.S32, ratio);
+	      }
+	      break; }
+	  case PS_DATA_S64: {
+	      psS64 delta = i1->data.S64 - i2->data.S64;
+	      float ratio = delta / (float) i1->data.S64;
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12ld vs %12ld : %12.5g\n", prefix, item->name, i1->data.S64, i2->data.S64, ratio);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12ld vs %12ld : %12.5g\n", prefix, item->name, i1->data.S64, i2->data.S64, ratio);
+	      }
+	      break; }
+	  case PS_DATA_F32: {
+	      double delta = i1->data.F32 - i2->data.F32;
+	      double ratio = delta / (double) i1->data.F32;
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12.5g vs %12.5g : %12.5g\n", prefix, item->name, i1->data.F32, i2->data.F32, ratio);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12.5g vs %12.5g : %12.5g\n", prefix, item->name, i1->data.F32, i2->data.F32, ratio);
+	      }
+	      break; }
+	  case PS_DATA_F64: {
+	      psF64 delta = i1->data.F64 - i2->data.F64;
+	      psF64 ratio = delta / (psF64) i1->data.F64;
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12.5g vs %12.5g : %12.5g\n", prefix, item->name, i1->data.F64, i2->data.F64, ratio);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12.5g vs %12.5g : %12.5g\n", prefix, item->name, i1->data.F64, i2->data.F64, ratio);
+	      }
+	      break; }
+	  case PS_DATA_STRING:
+	    if (longName) {
+		fprintf (stdout, "%4s %32s : %60s vs %60s\n", prefix, item->name, i1->data.str, i2->data.str); 
+	    } else {
+		fprintf (stdout, "%4s %8s : %60s vs %60s\n", prefix, item->name, i1->data.str, i2->data.str); 
+	    }
+	    break;
+	  default:
+	    psAbort ("impossible!");
+	}
+    }  
+    return true;
+}
+
+// we only need to support 4 types: BOOL, STRING, S32, S64, F32, F64:
+bool PrintMissingKeys (psMetadata *list, char *prefix) {
+
+    // compare keywords
+    psMetadataIterator *iter = psMetadataIteratorAlloc (list, PS_LIST_HEAD, NULL);
+    psMetadataItem *item = NULL;
+    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
+
+	int longName = (strlen(item->name) > 8);
+	longName = true;
+	
+	switch (item->type) {
+	  case PS_DATA_BOOL:
+	    if (longName) {
+		fprintf (stdout, "%4s %32s : %12c\n", prefix, item->name, (item->data.B ? 'T' : 'F'));
+	    } else {
+		fprintf (stdout, "%4s %8s : %12c\n", prefix, item->name, (item->data.B ? 'T' : 'F'));
+	    }
+	    break;
+	  case PS_DATA_S32: {
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12d\n", prefix, item->name, item->data.S32);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12d\n", prefix, item->name, item->data.S32);
+	      }
+	      break; }
+	  case PS_DATA_S64: {
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12ld\n", prefix, item->name, item->data.S64);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12ld\n", prefix, item->name, item->data.S64);
+	      }
+	      break; }
+	  case PS_DATA_F32: {
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12.5g\n", prefix, item->name, item->data.F32);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12.5g\n", prefix, item->name, item->data.F32);
+	      }
+	      break; }
+	  case PS_DATA_F64: {
+	      if (longName) {
+		  fprintf (stdout, "%4s %32s : %12.5g\n", prefix, item->name, item->data.F64);
+	      } else {
+		  fprintf (stdout, "%4s %8s : %12.5g\n", prefix, item->name, item->data.F64);
+	      }
+	      break; }
+	  case PS_DATA_STRING:
+	    if (longName) {
+		fprintf (stdout, "%4s %32s : %60s\n", prefix, item->name, item->data.str); 
+	    } else {
+		fprintf (stdout, "%4s %8s : %60s\n", prefix, item->name, item->data.str); 
+	    }
+	    break;
+	  default:
+	    psAbort ("impossible!");
+	}
+    }  
+    return true;
+}
+
+# if (0)
+case PS_DATA_BOOL:	fprintf (stdout, "name %s, type: %s, value: %c\n", item_1->name, myType, (item_1->data.B ? 'T' : 'F')
+	  case PS_DATA_S32:    fprintf (stdout, "name: %s, type: %s, value: %d\n", item->name, myType, item->data.S32); break;
+	  case PS_DATA_F64:    fprintf (stdout, "name: %s, type: %s, value: %f\n", item->name, myType, item->data.F64); break;
+	  case PS_DATA_STRING: fprintf (stdout, "name: %s, type: STRING, value: %s\n", item->name, item->data.str); break;
+
+    // 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;
+}
+
+# endif
Index: /trunk/ippTests/ippCheck/tap.pro
===================================================================
--- /trunk/ippTests/ippCheck/tap.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/tap.pro	(revision 42085)
@@ -0,0 +1,74 @@
+
+# TAP_VERBOSE levels: 0 : only errors, 1 : major diagnostics, 2 : details
+if (not($?TAP_VERBOSE)) set TAP_VERBOSE = 1
+
+macro tap.plan
+ if ($0 != 2)
+  echo "USAGE: plan (Ntest)"
+  break
+ end
+
+ $tap_Nplan = $1
+ $tap_Npass = 0
+ $tap_Nfail = 0
+ $tap_Nskip = 0
+ $tap_Ntest = 0
+end
+
+macro tap.skip
+ if ($0 != 2)
+  echo "USAGE skip (Nskip)"
+  break
+ end
+
+  $tap_Nskip += $1
+end
+
+macro tap.ok
+ if ($0 != 3)
+  echo "USAGE: ok (condition) (label)"
+  break
+ end
+ 
+ if (not($?tap_Nplan))
+  echo "ERROR: tap tests not planned"
+  break
+ end
+ 
+ if ($1)
+  if ($TAP_VERBOSE > 0) echo "ok: $2"
+  $tap_Npass ++
+ else     
+  echo "not ok: $2"
+  $tap_Nfail ++
+ end
+ $tap_Ntest ++
+end
+
+macro tap.done
+ if (not($?tap_Nplan))
+  echo "ERROR: tap tests not planned"
+  break
+ end
+
+ echo "planned $tap_Nplan tests, ran $tap_Ntest, skipped $tap_Nskip"
+ echo "passed $tap_Npass tests, failed $tap_Nfail tests"
+
+ if (($tap_Nfail == 0) && ($tap_Ntest + $tap_Nskip == $tap_Nplan) && ($tap_Npass == $tap_Ntest))
+   echo "PASS: all tests run and passed"
+   return 1
+ end
+
+ if ($tap_Nfail > 0)
+   echo "FAIL: $tap_Nfail tests failed"
+ end
+
+ if ($tap_Ntest + $tap_Nskip != $tap_Nplan)
+   echo "FAIL: not all tests run"
+ end
+
+ if ($tap_Ntest != $tap_Npass + $tap_Nfail)
+   echo "FAIL: inconsistent count: $tap_Ntest tests != $tap_Npass pass + $tap_Nfail fail"
+ end
+end
+
Index: /trunk/ippTests/ippCheck/truth.pro
===================================================================
--- /trunk/ippTests/ippCheck/truth.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/truth.pro	(revision 42085)
@@ -0,0 +1,184 @@
+#!/usr/bin/env dvo
+
+# provides the following:
+# make.truth.sources (camera) (expname) (source) (type)
+# make.truth.sources.mpc (camera) (expname) (source)
+# make.truth.sources.dvo (camera) (expname) (source)
+# get.exposure.metadata (camera) (expname)
+
+$VERBOSE = 1	       
+$COPYMASKS = 1
+$OUTDIR = .
+
+macro make.truth.sources
+ if ($0 != 5)
+  echo "USAGE: make.truth.sources (camera) (expname) (source) (type)"
+  echo "EX:    make.truth.sources gpc1 o9322g0253o /data/ipp060.0/ipp/ippRefs/catdir.refcat.20170919.v0 DVO"
+  echo "EX:    make.truth.sources gpc1 o9322g0253o track.table.fits MPC"
+  break
+ end
+
+ if ("$4" == "MPC") 
+  make.truth.sources.mpc $1 $2 $3
+  return
+ end
+
+ if ("$4" == "DVO") 
+  make.truth.sources.dvo $1 $2 $3
+  return
+ end
+
+ echo "ERROR: unknown data source $3 ($4)"
+ break
+end
+
+# some basic parameters
+$RADIUS = 1.75; # camera radius in degrees (over-estimate)
+
+macro make.truth.sources.dvo
+ if ($0 != 4)
+  echo "USAGE: make.truth.sources.dvo (camera) (expname) (catdir)"
+  echo "EX:    make.truth.sources.dvo gpc1 o9322g0253o /data/ipp060.0/ipp/ippRefs/catdir.refcat.20170919.v0"
+  break
+ end
+
+ local dbname expname catdir
+ $dbname = $1
+ $expname = $2
+ $catdir = $3
+
+ $OUTDIR = $expname.stats
+ mkdir $OUTDIR
+
+ get.exposure.metadata $dbname $expname 
+ substr $FILTER 0 1 avecode
+ if ("$avecode" == "w")
+   $avecode = r
+ end
+
+ fprintf "%11s %10.6f %10.6f %12.6f : %s = %s" $expname $Ro $Do $MJD $FILTER $avecode
+
+ # calculate the exposure limits
+ $Dmin = $Do - $RADIUS
+ $Dmax = $Do + $RADIUS
+ 
+ if ($Do > 85)
+   echo "ERROR: exposure footprint is poorly estimated for Dec > 85 ($Do)"
+   break
+ end
+
+ $Rmin = $Ro - $RADIUS / dcos($Do)
+ $Rmax = $Ro + $RADIUS / dcos($Do)
+ 
+ catdir $catdir
+ skyregion $Rmin $Rmax $Dmin $Dmax
+
+ # -parallel needs to be set based on the catdir
+ avextract -parallel ra dec $avecode where ($avecode\:ncode > 5)
+ # avextract ra dec $avecode where (i:ncode > 5)
+ set Rref = ra
+ set Dref = dec
+ set Mref = $avecode
+
+ # the DVO truth file is specific to the exposure
+ sprintf REFSOURCE "%s/%s.DVO.fits" $OUTDIR $expname 
+ write -fits DATA $REFSOURCE Rref Dref Mref
+end
+
+macro make.truth.sources.mpc
+ if ($0 != 4)
+  echo "USAGE: make.truth.sources.mpc (camera) (expname) (mpcrange.fits)"
+  echo "EX:    make.truth.sources.mpc gpc1 o9395g0315o mpcorb.59395.fits"
+  break
+ end
+
+ local dbname expname mpcfile
+ $dbname  = $1
+ $expname = $2
+ $mpcfile = $3
+
+ $OUTDIR = $expname.stats
+ mkdir $OUTDIR
+
+ get.exposure.metadata $dbname $expname 
+ substr $FILTER 0 1 avecode
+ if ("$avecode" == "w")
+   $avecode = r
+ end
+
+ fprintf "%11s %10.6f %10.6f %12.6f : %s = %s" $expname $Ro $Do $MJD $FILTER $avecode
+
+ # for the moment, mpcorb_predict ignores precession for the first past test, 
+ # so we need to bump up the radius substantially
+ $RADIUS_BUMP = $RADIUS + 0.5
+
+ # calculate the exposure limits
+ $Dmin = $Do - $RADIUS_BUMP 
+ $Dmax = $Do + $RADIUS_BUMP
+ 
+ if ($Do > 85)
+   echo "ERROR: exposure footprint is poorly estimated for Dec > 85 ($Do)"
+   break
+ end
+
+ $Rmin = $Ro - $RADIUS_BUMP / dcos($Do)
+ $Rmax = $Ro + $RADIUS_BUMP / dcos($Do)
+ 
+ # mpcorb_predict generates a FITS table specific to the exposure with fields (Rref, Dref, Mref)
+ sprintf REFSOURCE "%s/%s.MPC.fits" $OUTDIR $expname 
+ echo mpcorb_predict moment $mpcfile $MJD $Rmin $Rmax $Dmin $Dmax $REFSOURCE
+ exec mpcorb_predict moment $mpcfile $MJD $Rmin $Rmax $Dmin $Dmax $REFSOURCE
+end
+
+macro get.exposure.metadata
+ if ($0 != 3)
+  echo "USAGE: get.exposure.metadata (camera) (expname)"
+  echo "EX:    get.exposure.metadata gpc1 o9322g0253o"
+  echo " sets Ro, Do, MJD, FILTER"
+  break
+ end
+
+ local dbname expname catdir regcommand pagename Rrad Drad
+ $dbname = $1
+ $expname = $2
+
+ $regcommand = regtool -dbname $dbname -processedexp -exp_name $expname 
+
+ queueload -trap regqueue -x "$regcommand"
+
+ queue2book regqueue regbook
+
+ $myPath = NONE
+ book npages regbook -var npages
+
+ if ($npages > 1) 
+   echo "ERROR: too many exposures for $expname $1 : $regcommand"
+   book delete regbook
+   next
+ end
+
+ if ($npages < 1) 
+   echo "ERROR: missing exposure for $expname $1 : $regcommand"
+   book delete regbook
+   next
+ end
+
+ sprint pagename page.%03d 0
+ book getword regbook $pagename ra       -var Rrad
+ book getword regbook $pagename decl     -var Drad
+ book getword regbook $pagename dateobs  -var dateraw
+ book getword regbook $pagename exp_time -var exptime
+ book getword regbook $pagename filter   -var FILTER
+
+ $TIMEREF = 2000/01/01,00:00:00
+ $TIMEFORMAT = mjd
+
+ # shift to the midpoint of the exposure
+ ctimes -abs $dateraw -var MJD
+ $MJD += 0.5*$exptime/86400.0
+
+ $Ro = $Rrad * 180 / $M_PI
+ $Do = $Drad * 180 / $M_PI
+
+ book delete regbook
+end
Index: /trunk/ippTests/ippCheck/warpmask.pro
===================================================================
--- /trunk/ippTests/ippCheck/warpmask.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/warpmask.pro	(revision 42085)
@@ -0,0 +1,260 @@
+#!/usr/bin/env dvo
+
+$VERBOSE    = 1	       
+$COPYMASKS  = 0
+$OUTDIR     = .
+$OUTVERSION = XXX
+
+if (not($?USE_CAMSOURCE)) set USE_CAMSOURCE = 0
+
+$REF_FIELDS_WRP = Rref Dref Mref 
+$XTR_FIELDS_WRP = Xref Yref Nref Q1ref
+
+macro get.warp.eff
+ if ($0 != 5)
+  echo "USAGE: get.warp.eff (camera) (expname) (cam_id) (refsrcs.fits)"
+  echo "EX:    get.warp.eff gpc1 o9322g0253o 2398363 filename.fits"
+  break
+ end
+
+ local dbname expname camID
+ $dbname   = $1
+ $expname  = $2
+ $camID    = $3
+ $refsrcs  = $4
+
+  $OUTDIR = $expname.stats
+  mkdir $OUTDIR
+
+  # I have a table of sources which may be in the exposure
+  # I need to measure the mask fraction for each detection in the warps
+  # We do not actually generate a table of warp detections
+
+  # warptool -listrun to get the warp_id
+  # warptool -warped -warp_id warpID to get the list of skyfile paths
+
+  # get the warpID for the appropriate run:
+  get.warp.run $dbname $expname $camID
+  get.warp.skyfiles $dbname $warpID
+
+  get.warp.mask.files
+  
+  # load the reference source table
+  # how to choose the name of the filter?
+  # NOTE: Mref is chosen to match filter for this exposure
+  data $refsrcs                 ;
+  if ($USE_CAMSOURCE) 
+   read -fits DATA Rref_out Dref_out Mref_out
+   set Rref = Rref_out
+   set Dref = Dref_out
+   set Mref = Mref_out
+  else
+    read -fits DATA $REF_FIELDS_WRP
+  end
+
+  set Nref = ramp(Rref)
+  if ($VERBOSE) echo "loaded Rref[] sources from reference $refsrcs"
+
+  # delete any existing output vectors
+  foreach field $REF_FIELDS_WRP $XTR_FIELDS_WRP
+    delete -q $field\_out
+  end
+
+  # find matching entries in smf file 
+  for i 0 $warpMask:n
+    get.warp.mask.stats $warpMask:$i
+
+    foreach field $REF_FIELDS_WRP $XTR_FIELDS_WRP
+      concat $field\_onchp $field\_out
+      delete $field\_onchp
+    end
+  end
+
+  $OUT_FIELDS = 
+  foreach field $REF_FIELDS_WRP $XTR_FIELDS_WRP
+    $OUT_FIELDS = $OUT_FIELDS $field\_out
+  end
+
+  sprintf output "%s/%s.%s.wrp.stf" $OUTDIR $expname $OUTVERSION
+  write -fits DATA $output $OUT_FIELDS
+end
+
+macro get.warp.mask.stats
+  if ($0 != 2) 
+   echo "USAGE: get.warp.mask.stats (warpmask)"
+   echo "  assumes existence of $smfreal vectors (Rref, Dref, Mref) and buffer (PHU)"
+   break
+  end
+
+  # NOTE: warpmask must be a real file path, not a neb path
+  local warpmask 
+  $warpmask = $1
+
+  # load the mask file
+  rd -x 0 mk $warpmask
+
+  # select the sources which are within the bounds of this image
+  # output is Xref_onchp Yref_onchp Rref_onchp Dref_onchp Mref_onchp
+  get.warp.detect.coords mk
+
+  # I have a mask file, now I need to calculate something equiv to psfQf
+  set mkPerfect = (mk == 0) ? zero(mk) + 1 : zero(mk)
+
+  # I want to set an image based on the bit values but I fear
+  # the mask has been converted to a float when read
+  forcedphot mkPerfect Xref_onchp Yref_onchp -output Q1ref_onchp
+
+  subset tmp = Q1ref_onchp where (Q1ref_onchp > 0.85)
+  if ($VERBOSE) echo tmp[] of Q1ref_onchp[] are unmasked
+end
+
+macro get.warp.detect.coords
+  if ($0 != 2) 
+   echo "USAGE: get.warp.detect.coords (buffer)"
+   echo "  assumes existence of $smfreal vectors (Rref, Dref, Mref) and buffer (PHU)"
+   echo "  matches known sources to detections in the smf file extensions for each chip"
+   echo "  calculates detection fraction as a function of magnitude"
+   echo "  also astrometric and photometric scatter vs magnitude for good detections"
+   break
+  end
+
+  local buffname NAXIS1 NAXIS2
+  $buffname = $1
+
+  keyword $buffname NAXIS1 NAXIS1
+  keyword $buffname NAXIS2 NAXIS2
+
+  coords -q $buffname -p       0       0; init.radec.range
+  coords -q $buffname -p $NAXIS1       0; set.radec.range
+  coords -q $buffname -p $NAXIS1 $NAXIS2; set.radec.range
+  coords -q $buffname -p       0 $NAXIS2; set.radec.range
+
+  # generate a subset which could be on this chip
+  # do we need to grow the range at all?
+  set keep = (Rref >= $minR) && (Rref <= $maxR) && (Dref >= $minD) && (Dref <= $maxD) 
+  foreach field $REF_FIELDS_WRP Nref
+    subset $field\_tmp = $field if keep
+  end
+
+  # convert the subset coordinates to pixels
+  set Xref_tmp = Rref_tmp
+  set Yref_tmp = Dref_tmp
+  coords -q $buffname -c Xref_tmp Yref_tmp
+
+  # generate a subset of sources which are within the boundaries of the chip
+  set keep = (Xref_tmp >= 0) && (Xref_tmp <= $NAXIS1) && (Yref_tmp >= 0) && (Yref_tmp <= $NAXIS2)
+  foreach field $REF_FIELDS_WRP Nref Xref Yref
+    subset $field\_onchp = $field\_tmp if keep
+    delete $field\_tmp
+  end
+end
+
+macro init.radec.range
+  $minR = $RA
+  $minD = $DEC
+  $maxR = $RA
+  $maxD = $DEC
+end
+
+macro set.radec.range
+  $minR = min($minR, $RA)
+  $minD = min($minD, $DEC)
+  $maxR = max($maxR, $RA)
+  $maxD = max($maxD, $DEC)
+end
+
+macro get.warp.mask.files
+ if ($0 != 1)
+  echo "USAGE: get.warp.mask.files"
+  echo " uses warpPath:0 - warpPath:n"
+  break
+ end
+
+ # get the non-neb paths for the masks
+ for i 0 $warpPath:n
+   sprintf filename "%s.mask.fits" $warpPath:$i
+   $warpMask:$i = `neb-locate -p $filename`
+   if ($VERBOSE) echo "found warpPath $warpMask:$i"
+   if ($COPYMASKS) exec neb-copy $filename $OUTDIR
+ end
+ $warpMask:n = $warpPath:n
+end
+
+macro get.warp.skyfiles
+ if ($0 != 3)
+  echo "USAGE: get.warp.skyfiles (camera) (warpID)"
+  echo "EX:    get.warp.skyfiles gpc1 2367603"
+  echo "  sets warpPath:0 - warpPath:n"
+  echo "  also skycell:0  - skycell:n"
+  break
+ end
+
+ local dbname warpID
+ $dbname  = $1
+ $warpID  = $2
+
+ $command = warptool -dbname $dbname -warped -warp_id $warpID
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  return
+ end
+ 
+ local i
+ for i 0 $npages 
+   sprint pagename page.%03d $i
+   book getword mybook $pagename path_base -var warpPath:$i
+   book getword mybook $pagename skycell_id -var skycell:$i
+   if ($VERBOSE) echo "found warpPath $warpPath:$i"
+ end
+ $warpPath:n = $npages
+ $skycell:n = $npages
+
+ book delete mybook
+end
+
+macro get.warp.run
+ if ($0 != 4)
+  echo "USAGE: get.warp.run (camera) (expname) (camID)"
+  echo "EX:    get.warp.run gpc1 o9322g0253o 2398363"
+  echo "  sets warpID"
+  break
+ end
+
+ local dbname expname camID
+ $dbname   = $1
+ $expname  = $2
+ $camID    = $3
+
+ $warpID = 0
+ $command = warptool -dbname $dbname -listrun -exp_name $expname -cam_id $camID
+ 
+ queueload  myqueue -x "$command"
+ queue2book myqueue mybook
+ 
+ book npages mybook -var npages
+ 
+ if ($npages < 1) 
+  echo "ERROR: missing exposure : $command"
+  book delete mybook
+  next
+ end
+ 
+ if ($npages > 1) 
+  echo "ERROR: too many exposures : $command"
+  book delete mybook
+  next
+ end
+ 
+ sprint pagename page.%03d 0
+ book getword mybook $pagename warp_id -var warpID
+ book delete mybook
+
+ if ($VERBOSE) echo "found warpID $warpID"
+end
Index: /trunk/ippTests/ippCheck/warpstats.pro
===================================================================
--- /trunk/ippTests/ippCheck/warpstats.pro	(revision 42085)
+++ /trunk/ippTests/ippCheck/warpstats.pro	(revision 42085)
@@ -0,0 +1,107 @@
+
+macro load.warpstats
+ if ($0 != 2)
+   echo "USAGE: load.warpstats (glob)"
+   break
+ end
+ 
+ # accumulate these fields for comparison plots
+ $FIELDS = Rref Dref Mref
+
+ # delete accumulation vectors
+ delete -q $FIELDS
+
+ list name -glob $1
+ for i 0 $name:n
+
+   echo "reading from $name:$i"
+   data $name:$i
+   read -fits DATA -sizes
+   if ($table:Ny == 0) continue
+   read -fits DATA -list-fields -q; list tfields -join allfields; read -fits DATA $allfields
+
+   # assumes data has been loaded 
+   # results in magFound, fracFound, numFound
+   calc.warpstats
+   if ($i == 0)
+     set numTotal_sum = numTotal
+     set numPoss_sum  = numPoss
+   else
+     set numTotal_sum = numTotal + numTotal_sum
+     set numPoss_sum  = numPoss  + numPoss_sum
+   end
+   foreach field $FIELDS
+     concat $field\_out $field
+   end
+
+   # cleanup by deleting the loaded vectors
+   delete -q $allfields
+ end
+   
+ set numTotal = numTotal_sum
+ set numPoss  = numPoss_sum
+ delete numTotal_sum numPoss_sum
+
+ set fracPoss  = (numTotal > 0) ? numPoss  / numTotal : zero(numTotal)
+end
+
+# assumes we have loaded values from the stf tables
+# magBin, numTotal, numFound, numPoss, numGood
+macro plot.warpstats.fracs
+
+ clear -s
+ resize 1800 1200
+ label -fn helvetica 18
+ $BSTY = -tickpad 0.2 -xpad 3.5 -labelpadx 3.0 -ypad 4.5 -labelpady 4.0 +ypad 4.5 +labelpady 4.0
+
+ # plot the detection fractions, using left axis
+ section a 0 0 1 1
+ lim magBin -0.02 1.02; box $BSTY -ticks 1110;
+
+ plot -x hist magBin fracPoss  -lw 3 -c blue70
+
+ subset tmp = fracPoss  where (magBin > 15) && (magBin < 20); vstat -q tmp; $FracBrightPoss  = $MEDIAN
+ line -c blue -lt dot -lw 2 100 $FracBrightPoss to 0 $FracBrightPoss
+
+ fprintf "%.1f-pct of bright sources were detectable (unmasked)" {100*$FracBrightPoss}
+
+ label -x mag -y "detected fraction"
+
+ # plot the total number of detected sources, using the right axis
+ section b 0 0 1 1
+ lim magBin numTotal; box $BSTY -ticks 1011 -labels 0001
+
+ plot -x hist magBin numTotal -c grey80 -lw 2
+ label +y "detected number"
+
+ vstat -q numTotal; $Ntotal = $TOTAL
+ 
+ fprintf "%d total sources" $Ntotal
+
+ legend.init 0.02 0.95 0.03 0.02
+ sprintf line "unmasked source positions (psfqf > 0.85) [%.1f pct]" {100*$FracBrightPoss};  legend.line blue  3 solid "$line"
+end
+
+# relies on the following vectors: Mref_out, Q1ref_out, PSF_QF_out, X_PSF_out
+macro calc.warpstats
+ $Mmin = 10
+ $Mmax = 25
+ $Mdel = 0.25
+ 
+ delete -q  magBin numTotal numPoss
+ for mag $Mmin $Mmax $Mdel
+   concat $mag magBin
+
+   # sources in mag bin
+   set keepSrc = (Mref_out > $mag - 0.5*$Mdel) && (Mref_out <= $mag + 0.5*$Mdel)
+   subset tmp = Mref_out if keepSrc
+   concat tmp[] numTotal
+
+   # what fraction of the sources could have been detected (mask fraction > 0.85 at position)?
+   set keepMsk = keepSrc && (Q1ref_out > 0.85)
+   subset tmp = Mref_out if keepMsk
+   concat tmp[] numPoss
+ end
+
+ delete -q tmp keepSrc keepMsk keepDet
+end
