Index: /tags/ipp-2-5-1/Ohana/src/addstar/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/.cvsignore	(revision 17422)
@@ -0,0 +1,1 @@
+bin
Index: /tags/ipp-2-5-1/Ohana/src/addstar/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/Makefile	(revision 17422)
@@ -0,0 +1,259 @@
+default: addstar
+help:
+	@echo "make options: addstar (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/addstar
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -Wall -Werror
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+addstar     : $(BIN)/addstar.$(ARCH)
+addstard    : $(BIN)/addstard.$(ARCH)
+addstart    : $(BIN)/addstart.$(ARCH)
+addstarc    : $(BIN)/addstarc.$(ARCH)
+mkacc-2mass : $(BIN)/mkacc-2mass.$(ARCH)
+sedstar     : $(BIN)/sedstar.$(ARCH)
+load2mass   : $(BIN)/load2mass.$(ARCH)
+gztest      : $(BIN)/gztest.$(ARCH)
+
+all: addstar addstarc addstard addstart sedstar load2mass skycells
+
+ADDSTAR = \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/addstar.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/args.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/dump.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/fakeimage.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/grefstars.$(ARCH).o \
+$(SRC)/LoadStars.$(ARCH).o \
+$(SRC)/LoadHeaders.$(ARCH).o \
+$(SRC)/MatchHeaders.$(ARCH).o \
+$(SRC)/LoadData.$(ARCH).o \
+$(SRC)/LoadDataSDSS.$(ARCH).o \
+$(SRC)/LoadDataPMM.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/parse_time.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/resort_catalog.$(ARCH).o \
+$(SRC)/ReadStarsFITS.$(ARCH).o \
+$(SRC)/ReadStarsTEXT.$(ARCH).o \
+$(SRC)/ReadStarsSDSS.$(ARCH).o \
+$(SRC)/FilterStars.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/GetFileMode.$(ARCH).o \
+$(SRC)/ReadImageHeader.$(ARCH).o \
+$(SRC)/UpdateImageIDs.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o
+
+ADDSTARD = \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/NewImage.$(ARCH).o \
+$(SRC)/NewRefcat.$(ARCH).o \
+$(SRC)/NewReflist.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/UpdateDatabase_Image.$(ARCH).o \
+$(SRC)/UpdateDatabase_Refcat.$(ARCH).o \
+$(SRC)/UpdateDatabase_Reflist.$(ARCH).o \
+$(SRC)/addstard.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/args_server.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o
+
+ADDSTART = \
+$(SRC)/addstart.$(ARCH).o \
+$(SRC)/ImageOptions.$(ARCH).o \
+$(SRC)/DatasetOps.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/args_server.$(ARCH).o \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/UpdateDatabase_Image.$(ARCH).o \
+$(SRC)/UpdateDatabase_Reflist.$(ARCH).o \
+$(SRC)/UpdateDatabase_Refcat.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/calibrate.$(ARCH).o \
+$(SRC)/find_matches.$(ARCH).o \
+$(SRC)/find_matches_closest.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/find_subset.$(ARCH).o \
+$(SRC)/get2mass.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o \
+$(SRC)/get2mass_as_raw.$(ARCH).o \
+$(SRC)/get2mass_dr2.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/getgsc.$(ARCH).o \
+$(SRC)/getusno.$(ARCH).o \
+$(SRC)/getusnob.$(ARCH).o \
+$(SRC)/gettycho.$(ARCH).o \
+$(SRC)/greference.$(ARCH).o \
+$(SRC)/in_image.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/SkyRegionUtils.$(ARCH).o \
+$(SRC)/SkyListForStars.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/edge_check.$(ARCH).o \
+$(SRC)/opening_angle.$(ARCH).o \
+$(SRC)/SkyTableFromTychoIndex.$(ARCH).o \
+$(SRC)/ListenClients_Thread.$(ARCH).o \
+$(SRC)/NewImage_Thread.$(ARCH).o \
+$(SRC)/NewReflist_Thread.$(ARCH).o \
+$(SRC)/NewRefcat_Thread.$(ARCH).o 
+
+ADDSTARC = \
+$(SRC)/addstarc.$(ARCH).o \
+$(SRC)/airmass.$(ARCH).o \
+$(SRC)/SocketOps.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/LoadStars.$(ARCH).o \
+$(SRC)/LoadHeaders.$(ARCH).o \
+$(SRC)/MatchHeaders.$(ARCH).o \
+$(SRC)/LoadData.$(ARCH).o \
+$(SRC)/LoadDataSDSS.$(ARCH).o \
+$(SRC)/LoadDataPMM.$(ARCH).o \
+$(SRC)/grefstars.$(ARCH).o \
+$(SRC)/load_subpix.$(ARCH).o \
+$(SRC)/parse_time.$(ARCH).o \
+$(SRC)/ReadStarsFITS.$(ARCH).o \
+$(SRC)/GetFileMode.$(ARCH).o \
+$(SRC)/ReadImageHeader.$(ARCH).o \
+$(SRC)/ReadStarsTEXT.$(ARCH).o \
+$(SRC)/ReadStarsSDSS.$(ARCH).o \
+$(SRC)/UpdateImageIDs.$(ARCH).o \
+$(SRC)/FilterStars.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/args_client.$(ARCH).o
+
+### this program may need to be repaired (EAM 2006.08)
+MKACC-2MASS = \
+$(SRC)/mkacc-2mass.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/get2mass_as.$(ARCH).o
+
+LOAD-2MASS = \
+$(SRC)/load2mass.$(ARCH).o \
+$(SRC)/load2mass_as_rawdata.$(ARCH).o \
+$(SRC)/load2mass_catalog.$(ARCH).o \
+$(SRC)/get2mass_ops.$(ARCH).o \
+$(SRC)/get2mass_full.$(ARCH).o \
+$(SRC)/find_matches_refstars.$(ARCH).o \
+$(SRC)/args_load2mass.$(ARCH).o \
+$(SRC)/replace_match.$(ARCH).o \
+$(SRC)/update_coords.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o
+
+SEDSTAR = \
+$(SRC)/sedstar.$(ARCH).o \
+$(SRC)/SEDtableLoad.$(ARCH).o \
+$(SRC)/SEDfit.$(ARCH).o \
+$(SRC)/SEDops.$(ARCH).o \
+$(SRC)/args_sedstar.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o
+
+SKYCELLS = \
+$(SRC)/skycells.$(ARCH).o \
+$(SRC)/args_skycells.$(ARCH).o \
+$(SRC)/ConfigInit_skycells.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/sky_tessalation.$(ARCH).o
+
+$(ADDSTARC) : $(INC)/addstar.h
+$(ADDSTARD) : $(INC)/addstar.h
+$(ADDSTART) : $(INC)/addstar.h
+$(ADDSTAR)  : $(INC)/addstar.h
+$(SKYCELLS) : $(INC)/addstar.h
+
+$(BIN)/addstar.$(ARCH) : $(ADDSTAR)
+$(BIN)/addstard.$(ARCH) : $(ADDSTARD)
+$(BIN)/addstart.$(ARCH) : $(ADDSTART)
+$(BIN)/addstarc.$(ARCH) : $(ADDSTARC)
+$(BIN)/mkacc-2mass.$(ARCH) : $(MKACC-2MASS)
+$(BIN)/sedstar.$(ARCH)   : $(SEDSTAR)
+$(BIN)/load2mass.$(ARCH) : $(LOAD-2MASS)
+$(BIN)/skycells.$(ARCH) : $(SKYCELLS)
+
+INSTALL = addstar addstarc addstard addstart sedstar load2mass skycells
+
+# dependancy rules for binary code #########################
+$(INSTALL): % : $(BIN)/%.$(ARCH)
+
+%.clean :
+	rm -f $(BIN)/$*.$(ARCH)
+
+%.install:
+	make $(DESTBIN)/$*
+
+install:
+	for i in $(INSTALL); do make $$i.install || exit; done
Index: /tags/ipp-2-5-1/Ohana/src/addstar/bin/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/bin/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/bin/.cvsignore	(revision 17422)
@@ -0,0 +1,5 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin
+*.darwin_x86
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/2mass.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/2mass.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/2mass.txt	(revision 17422)
@@ -0,0 +1,182 @@
+
+2MASS PSC fields:
+
+ra, dec : obvious
+
+m       -> mag
+cmsig   -> dM
+msigcom -> dMcal (dMcal^2 + dM^2 = msigcom^2)
+stdap   -> Map 
+snr     : skipped
+
+err_maj -> fhwm_x
+err_min -> fhwm_y
+err_ang -> angle
+
+the following 2MASS values are used to set bits in the phot_flags field:
+
+ph_qual:
+
+X = 0x0000  There is a detection at this location, but no valid brightness estimate can be extracted using any algorithm. rd_flg="9" and default magnitude is null.
+U = 0x0001  Upper limit on magnitude. Source is not detected in this band (rd_flg="0"), or it is detected, but not resolved in a consistent fashion with other bands (rd_flg="6"). A value of ph_qual="U" does not necessarily mean that there is no flux detected in this band at the location. Whether or not flux has been detected can be determined from the value of rd_flg. When rd_flg="0", no flux has been detected. When rd_flg="6", flux has been detected at the location where the images were not deblended consistently in all three bands (JHKs).
+F = 0x0002  This category includes rd_flg="1" or rd_flg="3" sources where a reliable estimate of the photometric error, [jhk]_cmsig, could not be determined. The uncertainties reported for these sources in [jhk]_cmsig and [jhk]_msigcom are flags and have numeric values >8.0.
+E = 0x0003  This category includes detections where the goodness-of-fit quality of the profile-fit photometry was very poor (rd_flg=2 and [jhk]psf_chi>10.0), or detections where psf fit photometry did not converge and an aperture magnitude is reported (rd_flg=4), or detections where the number of frames was too small in relation to the number of frames in which a detection was geometrically possible (rd_flg="1" or rd_flg="2").
+A = 0x0004  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>10 AND [jhk]_cmsig<0.10857.
+B = 0x0005  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>7 AND [jhk]_cmsig<0.15510.
+C = 0x0006  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>5 AND [jhk]_cmsig<0.21714.
+D = 0x0007  Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with no [jhk]_snr or [jhk]_cmsig requirement.
+
+rd_flag:
+
+0 = 0x0000  Source is not detected in this band. The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20".
+1 = 0x0010  The default magnitude is derived from aperture photometry measurements on the 51 ms "Read_1" exposures. The aperture radius is 4", with the sky background measured in an annulus with an inner radius of 14" and an outer radius of 20". Used for sources that saturate one or more of the 1.3s "Read_2" exposures, but are not saturated on at least one of the 51 ms "Read_1" frames.
+2 = 0x0020  The default magnitude is derived from a profile-fitting measurement made on the 1.3 sec "Read_2" exposures. The profile-fit magnitudes are normalized to curve-of-growth-corrected aperture magnitudes. This is the most common type in the PSC, and is used for sources that have no saturated pixels in any of the 1.3 sec exposures.
+3 = 0x0030  The default magnitude is derived from a 1-d radial profile fitting measurement made on the 51 ms "Read_1" exposures. Used for very bright sources that saturate all of the 51 ms "Read 1" exposures.
+4 = 0x0040  The default magnitude is derived from curve-of-growth-corrected 4" radius aperture photometry measurements on the 1.3 s "Read_2" exposures. This is used for sources that are not saturated in any of the Read_2 frames, but where the profile-fitting measurements fail to converge to a solution. These magnitudes are the same as the standard aperture magnitudes (j_m_stdap, h_m_stdap, k_m_stdap), but when they are the default magnitudes, it generally implies that they are low quality measurements.
+6 = 0x0050  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for pairs of sources which are detected and resolved in another band, but are detected and not resolved in this band. This differs from a rd_flg="0" because in this case there is a detection of the source in this band, but it is not consistently resolved across all bands.
+9 = 0x0060  The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for sources that were nominally detected in this band, but which could not have a useful brightness measurement from either profile fitting or aperture photometry. This often occurs in highly confused regions, or very near Tile edges where a significant fraction of the measurement aperture of sky annulus falls off the focal plane.
+
+cc_flag:
+
+p = 0x0000  Persistence. Source may be contaminated by a latent image left by a nearby bright star.
+c = 0x0100  Photometric Confusion. Source photometry is biased by a nearby star that has contaminated the background estimation. This is very common in high source density regions.
+d = 0x0200  Diffraction spike confusion. Source may be contaminated by a diffraction spike from a nearby star.
+s = 0x0300  Electronic stripe. Source measurement may be contaminated by a stripe from a nearby bright star.
+b = 0x0400  Bandmerge confusion. In the process of merging detections in the different bands for this source, there was more than one possible match between the different band components. This occurs in regions of very high source density, or when multiple sources were split in one band but not another.
+0 = 0x0500  Source is unaffected by known artifacts, or is not detected in the band.
+
+bl_flag: (0,1 : not blended)
+
+anything except 0 or 1 -> 0x0008
+
+gal_flag: (0,1 : not extended)
+
+anything except 0 or 1 -> 0x0080
+
+mp_flag: 
+
+anything except 0 or 1 -> 0x0800
+
+dup_flag:
+
+anything except 0 or 1 -> 0x1000
+
+use_flag:
+
+anything except 0 or 1 -> 0x2000
+
+
+2MASS PSC fields:
+
+ra, dec : obvious
+
+err_maj, err_min, err_ang : I only have ra_err and dec_err.  should I upgrade my tables?
+(put these in FHx, FWy, theta)
+
+designation (2mass ID) : ignore this?  potentially derivable from position
+
+j_m -> M
+j_cmsig -> dM
+j_msigcom : I don't yet have a systematic error term; add this in
+j_snr : another way of representing the error : the implied error is the MODE of dM for the scan
+
+(repeat for h, k)
+
+ph_qual[j] : using letters to represent 8 states: (3bit needed)
+
+0x0000 * "X" - There is a detection at this location, but no valid brightness estimate can be extracted using any algorithm. rd_flg="9" and default magnitude is null.
+0x0001 * "U" - Upper limit on magnitude. Source is not detected in this band (rd_flg="0"), or it is detected, but not resolved in a consistent fashion with other bands (rd_flg="6"). A value of ph_qual="U" does not necessarily mean that there is no flux detected in this band at the location. Whether or not flux has been detected can be determined from the value of rd_flg. When rd_flg="0", no flux has been detected. When rd_flg="6", flux has been detected at the location where the images were not deblended consistently in all three bands (JHKs).
+0x0002 * "F" - This category includes rd_flg="1" or rd_flg="3" sources where a reliable estimate of the photometric error, [jhk]_cmsig, could not be determined. The uncertainties reported for these sources in [jhk]_cmsig and [jhk]_msigcom are flags and have numeric values >8.0.
+0x0003 * "E" - This category includes detections where the goodness-of-fit quality of the profile-fit photometry was very poor (rd_flg=2 and [jhk]psf_chi>10.0), or detections where psf fit photometry did not converge and an aperture magnitude is reported (rd_flg=4), or detections where the number of frames was too small in relation to the number of frames in which a detection was geometrically possible (rd_flg="1" or rd_flg="2").
+0x0004 * "A" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>10 AND [jhk]_cmsig<0.10857.
+0x0005 * "B" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>7 AND [jhk]_cmsig<0.15510.
+0x0006 * "C" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with [jhk]_snr>5 AND [jhk]_cmsig<0.21714.
+0x0007 * "D" - Detections in any brightness regime where valid measurements were made (rd_flg="1","2" or "3") with no [jhk]_snr or [jhk]_cmsig requirement.
+
+rd_flag[j] : using numbers to represent 7 states:
+
+0x0000 * "0" - Source is not detected in this band. The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20".
+0x0010 * "1" - The default magnitude is derived from aperture photometry measurements on the 51 ms "Read_1" exposures. The aperture radius is 4", with the sky background measured in an annulus with an inner radius of 14" and an outer radius of 20". Used for sources that saturate one or more of the 1.3s "Read_2" exposures, but are not saturated on at least one of the 51 ms "Read_1" frames.
+0x0020 * "2" - The default magnitude is derived from a profile-fitting measurement made on the 1.3 sec "Read_2" exposures. The profile-fit magnitudes are normalized to curve-of-growth-corrected aperture magnitudes. This is the most common type in the PSC, and is used for sources that have no saturated pixels in any of the 1.3 sec exposures.
+0x0030 * "3" - The default magnitude is derived from a 1-d radial profile fitting measurement made on the 51 ms "Read_1" exposures. Used for very bright sources that saturate all of the 51 ms "Read 1" exposures.
+0x0040 * "4" - The default magnitude is derived from curve-of-growth-corrected 4" radius aperture photometry measurements on the 1.3 s "Read_2" exposures. This is used for sources that are not saturated in any of the Read_2 frames, but where the profile-fitting measurements fail to converge to a solution. These magnitudes are the same as the standard aperture magnitudes (j_m_stdap, h_m_stdap, k_m_stdap), but when they are the default magnitudes, it generally implies that they are low quality measurements.
+0x0050 * "6" - The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for pairs of sources which are detected and resolved in another band, but are detected and not resolved in this band. This differs from a rd_flg="0" because in this case there is a detection of the source in this band, but it is not consistently resolved across all bands.
+0x0060 * "9" - The default magnitude is the 95% confidence upper limit derived from a 4" radius aperture measurement taken at the position of the source on the Atlas Image. The sky background is estimated in an annular region with inner radius of 14" and outer radius of 20". This is used for sources that were nominally detected in this band, but which could not have a useful brightness measurement from either profile fitting or aperture photometry. This often occurs in highly confused regions, or very near Tile edges where a significant fraction of the measurement aperture of sky annulus falls off the focal plane.
+
+bl_flag[j] : 3 states:
+
+    * "0" - Source is not detected, or is inconsistently deblended in that band.
+    * "1" - One component was fit to the source in R_2 profile-fitting photometry (rd_flg="2"), or default magnitudes are from aperture photometry (rd_flg="1" or "4") or saturated star 1-d radial profile-fitting (rd_flg="3").
+    * ">1" - More than one component was fit simultaneously during R2 profile-fit photometry, where the value of the field is the number of components simultaneously fit. The maximum number of components is 7 in any band for the PSC, so this bl_flg is always a three character flag. Multi-component fitting occurs only for profile-fitting, and only when more than one detection is found within ~5". Single detections that are not well-fit by a single PSF are not split.
+
+cc_flag[j] : 6 states:
+
+    * "p" = Persistence. Source may be contaminated by a latent image left by a nearby bright star.
+    * "c" = Photometric Confusion. Source photometry is biased by a nearby star that has contaminated the background estimation. This is very common in high source density regions.
+    * "d" = Diffraction spike confusion. Source may be contaminated by a diffraction spike from a nearby star.
+    * "s" = Electronic stripe. Source measurement may be contaminated by a stripe from a nearby bright star.
+    * "b" = Bandmerge confusion. In the process of merging detections in the different bands for this source, there was more than one possible match between the different band components. This occurs in regions of very high source density, or when multiple sources were split in one band but not another.
+    * "0" = Source is unaffected by known artifacts, or is not detected in the band.
+
+ndet : number of frames detected, number of frames available (carry this somehow?)
+
+prox : distance to nearest neighbor (unneeded)
+pxpa : direction to nearest neighbor (unneeded)
+pxcntr : id of nearest neighbor
+
+gal_contam : is source close to or part of an extended source? (3 states)
+
+mp_flg : associated with know Solar System object
+
+pts_key/cntr : 2MASS internal object ID
+
+hemis : (which observatory -- replace with a photcode?)
+
+date -> ignore (not accurate)
+
+scan : scan in which it was detected (unneeded)
+
+glon, glat : derivable
+
+x_scan : pixel location in x direction -> Xccd
+
+jdate : time stamp with ~30 sec accuracy
+
+j_psfchi : fit chi sqare -> psfProb (needs to be changed in measure.d anyway)
+
+j_m_stdap : aperture magnitude -> Mgal (change to Map anyway)
+
+j_msig_stdap : aperture magnitude error (keep ??)
+
+dist_edge_ns, ew, flg : information about distance to scan edge (ns or ew)
+
+dup_src : duplicate scan 
+use_src : ??
+
+(ignore the fields for cross-correlation with other surveys)
+
+counting the flag bits (no attempt to account for any duplicate info; ignoring dist_edge info):
+
+ph_qual	   : 3 bits
+rd_flag	   : 3 bits
+bl_flag	   : 2 bits 
+cc_flag	   : 3 bits
+gal_contam : 2 bits
+mp_flag	   : 1 bit
+dup_src	   : 1 bit
+use_src	   : 1 bit
+
+total      : 17 bit (1 too many...)
+
+counting the flag bits (trying to minimize duplicate info; ignoring dist_edge info):
+
+ph_qual	   : 3 bits 0x0001 - 0x0007
+bl_flag	   : 1 bit  0x0008 (bl_flag = 0 or 1 -> 0; bl_flag > 1 -> 1)
+rd_flag	   : 3 bits 0x0010 - 0x0070
+gal_contam : 1 bit  0x0080 (gal_contam = 0 or 1 -> 0; gal_contam = 2 -> 1) (set extNsigma to a high value if gal_contam = 1) 
+cc_flag	   : 3 bits 0x0100 - 0x0700
+mp_flag	   : 1 bit  0x0800
+dup_src	   : 1 bit  0x1000 (0 or 1 -> 0; > 1 -> 1)
+use_src	   : 1 bit  0x2000 
+
+total      : 14 bits
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/ChangeLog.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/ChangeLog.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/ChangeLog.txt	(revision 17422)
@@ -0,0 +1,77 @@
+
+- addstar 1.7 (2006.10.04)
+  * converted to gfits APIs (forces libfits 1.6)
+  * added the -resort option
+  * added timeout for large operations
+  * dropped old versions now not needed
+  * converted to the new DVO APIs (forces libdvo 1.3)
+  * added 'sedstar'
+  * added 'load2mass'
+  * more error checks on config
+  * converted to 'check_dir_access' 
+  * check that catalog stars fall in image (or skip catalog)
+  * fixed memory leak issues in find_matches
+  * changed match projection to ZEA
+  * fixed some 0,360 boundary errors
+
+- addstar 1.6
+  * added the 'fakeimage' mode
+
+- addstar 1.5
+  * fixed up skyregion errors
+  * added untested threaded server version
+  * added Tycho catalog
+  * added USNO-B catalog
+  * added proper motion and parallax
+  * fixed 2MASS to work with skyregions correctly
+  * dropped IMAGE_CATALOG from config
+  * changed to used dvo_image_lock,unlock
+  * cleaned up excess verbosity for timing tests
+  * added -photcode (supplement -p)
+  * added PMTEST format
+
+- addstar 1.4
+  * major work to support SkyRegions
+  * cleaned up signed/unsigned inconsistencies
+  * fixed 2MASS time to use JD 
+
+2005.10.20 : addstar-1.3
+
+	This release incorportates many substantial improvements
+	needed to handle the panstarrs data problems:
+	- internal data representation now high precision
+	- multiple data storage formats (LONEOS, ELIXIR, PANSTARRS)
+	- multiple data storage modes (RAW, MEF, SPLIT)
+	- alternative matching scheme (-closest)
+	- sorted output tables now optional
+	- incremental updates possible for inserts
+	- client / server set now defined
+
+2005.08.15 : addstar-1.2
+
+        This is a snapshot release before I begin serious work on the
+	code to handle alternate formats and so forth needed for the
+	panstarrs support.  Minor updates since v1.1, mostly to fix
+	2MASS issues and to stay in sync with the libs.  
+
+2005.07.06 : addstar-1.1
+
+	I have made a variety of fairly substantial changes since
+        v1.0.  I have added autocode for the basic DVO data types.  I
+        have merged the functionality of addspphot, addrefs, and
+        addusno into the single addstar function.  There are now
+        options to add an ascii text table or stars from known
+        external reference catalogs (HST GSC, USNO, 2MASS).  Also, the
+        functions added for skyprobe (in-flight Mcal calculation,
+        color terms) are also incorporated.  I also substantially
+        re-organized the basic code to make the different modes more
+        sensible.  There are still a few changes needed for addstar
+        v2.0 (which will be able to handle the pan-starrs bandwidth).
+        specifically, addstar needs to handle the catalog files using
+        the TBD sky.db interfaces, it needs to interact with a DVO
+        server in a client/server way (to divide up the sky), it needs
+        to handle multiple versions of the table formats, and it might
+        need to know about the mysql backend interface.  
+
+2005.02.24 : I am adding the mosaic two-level astrometry information to the Image table.
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/camera-sample.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/camera-sample.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/camera-sample.txt	(revision 17422)
@@ -0,0 +1,81 @@
+# example camera layout file
+
+# arcsec per mm
+PLATE_SCALE  25.0
+
+DPLATE_X     1.0e-5
+DPLATE_Y     1.0e-5
+
+NCHIPS        64
+NAXIS1      4800
+NAXIS2      4800
+
+# mm per pixel
+PIXEL_SCALE   0.010
+
+# for now, we force the chip rotation to be 0 and parity to be 1,1
+# ref      name  dX      dY
+CHIP.000   x0y0 -19200  -19200
+CHIP.001   x1y0 -14400  -19200
+CHIP.002   x2y0  -9600  -19200
+CHIP.003   x3y0  -4800  -19200
+CHIP.004   x4y0      0  -19200
+CHIP.005   x5y0   4800  -19200
+CHIP.006   x6y0   9600  -19200
+CHIP.007   x7y0  14400  -19200
+CHIP.008   x0y1 -19200  -14400
+CHIP.009   x1y1 -14400  -14400
+CHIP.010   x2y1  -9600  -14400
+CHIP.011   x3y1  -4800  -14400
+CHIP.012   x4y1      0  -14400
+CHIP.013   x5y1   4800  -14400
+CHIP.014   x6y1   9600  -14400
+CHIP.015   x7y1  14400  -14400
+CHIP.016   x0y2 -19200   -9600
+CHIP.017   x1y2 -14400   -9600
+CHIP.018   x2y2  -9600   -9600
+CHIP.019   x3y2  -4800   -9600
+CHIP.020   x4y2      0   -9600
+CHIP.021   x5y2   4800   -9600
+CHIP.022   x6y2   9600   -9600
+CHIP.023   x7y2  14400   -9600
+CHIP.024   x0y3 -19200   -4800
+CHIP.025   x1y3 -14400   -4800
+CHIP.026   x2y3  -9600   -4800
+CHIP.027   x3y3  -4800   -4800
+CHIP.028   x4y3      0   -4800
+CHIP.029   x5y3   4800   -4800
+CHIP.030   x6y3   9600   -4800
+CHIP.031   x7y3  14400   -4800
+CHIP.032   x0y4 -19200       0
+CHIP.033   x1y4 -14400       0
+CHIP.034   x2y4  -9600       0
+CHIP.035   x3y4  -4800       0
+CHIP.036   x4y4      0       0
+CHIP.037   x5y4   4800       0
+CHIP.038   x6y4   9600       0
+CHIP.039   x7y4  14400       0
+CHIP.040   x0y5 -19200    4800
+CHIP.041   x1y5 -14400    4800
+CHIP.042   x2y5  -9600    4800
+CHIP.043   x3y5  -4800    4800
+CHIP.044   x4y5      0    4800
+CHIP.045   x5y5   4800    4800
+CHIP.046   x6y5   9600    4800
+CHIP.047   x7y5  14400    4800
+CHIP.048   x0y6 -19200    9600
+CHIP.049   x1y6 -14400    9600
+CHIP.050   x2y6  -9600    9600
+CHIP.051   x3y6  -4800    9600
+CHIP.052   x4y6      0    9600
+CHIP.053   x5y6   4800    9600
+CHIP.054   x6y6   9600    9600
+CHIP.055   x7y6  14400    9600
+CHIP.056   x0y7 -19200   14400
+CHIP.057   x1y7 -14400   14400
+CHIP.058   x2y7  -9600   14400
+CHIP.059   x3y7  -4800   14400
+CHIP.060   x4y7      0   14400
+CHIP.061   x5y7   4800   14400
+CHIP.062   x6y7   9600   14400
+CHIP.063   x7y7  14400   14400
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/config.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/config.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/config.txt	(revision 17422)
@@ -0,0 +1,68 @@
+
+addstar is expecting to get the following information from the header.
+
+Required keywords:
+
+NAXIS1, NAXIS2
+NASTRO (non-zero for valid astrometry solution)
+NSTARS 
+CERROR (astrometric error)
+
+a valid WCS:
+
+CTYPE1,2
+CRVAL1,2
+CRPIX1,2
+CDELT1,2
+PC001001
+PC001002
+PC002001
+PC002002
+
+PHOTCODE
+
+optional keywords:
+
+APMIFIT, dAPMIFIT
+FLIMIT, FSATUR
+FWHM_X, FWHM_Y, 
+TRATE (for driftscanning)
+ZERO_PT 
+
+Note that these entries are used to define the actual keywords:
+
+(eg, in the dvo.config file, DATE-KEYWORD tells addstar what keyword
+to use for the DATE).
+
+For Date/Time, one of the following three sets is needed:
+
+ * DATE-KEYWORD (date in format specified below)
+ * DATE-MODE (YYYY/MM/DD, MM/DD/YYYY, DD/MM/YYYY)
+ * UT-KEYWORD (HH:MM:SS for UT)
+
+ or:
+
+ * MJD-KEYWORD (MJD)
+
+ or: 
+
+ * JD-KEYWORD (JD)
+
+For Exposure time:
+
+ * EXPTIME-KEYWORD (seconds)
+
+For Airmass:
+
+ * AIRMASS-KEYWORD
+
+CCDNUM-KEYWORD??
+
+For sidereal time (optional):
+
+ * ST-KEYWORD
+
+For alternate EXTNAME:
+
+ * EXTNAME-KEYWORD
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/notes.txt	(revision 17422)
@@ -0,0 +1,532 @@
+
+2008.02.23
+
+  features I need to add / finish in dvo:
+
+  * detID / objID / imageID
+
+    some questions:
+
+      db_id
+      table_id (chip / warp / stack / diff)
+      chip_id / warp_id / stack_id / diff_id
+      
+      detections from stand-alone analysis would require an alternate sequence?
+      detections without an associated image get image_id = 0
+      
+    * imageID : we will define a new, unique running ID within dvo
+      against which joins are performed.  in addition, I will keep the
+      externally supplied ids as a reference (32bit id + 16bit source)
+
+    * detID : the detection (measure) should carry the imageID + detID as a
+      unique value. 
+
+    * objID : objects will get a unique ID when they are created
+      (sequence in table + table ID? or use the same bits as Maria?)
+
+  * extended measurement tables
+  * SDSS ingest
+  * 2MASS flags
+  
+
+2008.01.31
+
+  Adding extended sources:
+
+  * create a libautocode extended source table description (DONE)
+  * load the extended source measurements:
+    this could be done within LoadStars or as a separate pass over the file, loading the extended source tables
+    * we can call this function only optionally
+    * need to make the association between the psf and ext measurements via det ID
+  * in find_matches, we could/should record the reference from Stars to the matched average entry
+    that would let us use the PSF position to get the average object match:
+
+  * find_matches_extended
+    * loop over extended sources
+    * use ID to find the corresponding Stars entry
+    * use the averef to find the corresponding Average entry
+    * update the extended source table
+
+  * Write out ext sources with the rest of the data
+
+  where do we put the table? require split?
+
+  (160 byte per detection; PS1 will have 5e8 => 400 GB)
+
+2007.01.04
+
+  Use of options.photcode:
+    - M_REFLIST: used to assign a photcode value to the loaded data
+    - M_REFCAT: used to select data from the reference of the given photcode
+    - M_FAKEIMAGE: used to assign a photcode value to the generated data
+    - M_RESORT: unused
+    - M_IMAGE: is used to override or supply the image header photcode
+	       * only valid for single chip runs
+
+  Re: MOSAIC_PHU
+      - the mosaic phu needs to have the time to enable the image/mosaic match
+
+      - NX,NY are used for plotting the mosaic outline.  the center of
+        the mosaic is 0,0, and the mosaic outline is drawn from
+        -NX/2,-NY/2 to NX/2,NY/2.  A problem: NX,NY in Image is using
+        a short int.  If the units of the FPA/TPA coords are microns, this overflows.
+	* change NX,NY to int?
+	* require the use of mm not microns?
+	* for GPC, NX,NY ~ 384000 um excluding gaps
+
+      - only the astrometry, time, and NX,NY are loaded for MOSAIC_PHU headers.
+
+2007.01.03
+
+  I am working on adding the ability to read MEF files containing
+  multiple tables and multiple image headers.  Here are the details:
+
+  SIMPLE CMP:
+    PHU:
+      NAXIS = 2 or
+      SIMPLE = FALSE or
+      TEXTMODE = TRUE (use option; global variable)
+
+  SIMPLE CMF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 exists, > 0
+      NAXIS2 exists, > 0
+    EXT:
+      EXTNAME = SMPDATA, PS1DATA, etc (defines layout)
+
+  SIMPLE MEF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is not "-DIS"
+      EXTEND is true
+    EXT HEAD:
+      EXTTYPE = IMAGE
+      EXTNAME = image.name
+      EXTDATA = table.name
+    EXT DATA:
+      EXTTYPE = SMPDATA, PS1DATA, etc (defines layout)
+      EXTNAME = table.name
+      EXTHEAD = image.name
+
+  MOSAIC MEF:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is "-DIS"
+      EXTEND is true
+    EXT HEAD:
+      EXTTYPE = IMAGE
+      EXTNAME = image.name
+      EXTDATA = table.name
+    EXT DATA:
+      EXTTYPE = SMPDATA, PS1DATA, etc (defines layout)
+      EXTNAME = table.name
+      EXTHEAD = image.name
+
+  MOSAIC PHU:
+    PHU: 
+      NAXIS = 0
+      NAXIS1 does not exist
+      NAXIS2 does not exist
+      &CTYPE[4] is "-DIS"
+      NEXTEND = 0 or  
+      EXTEND is false
+
+    
+
+2006.04.02
+
+  I am considering an option to insert fake camera images into the
+  database.  The camera would be described with a file specifying the
+  dimensions of each chip (Nx,Ny,pixel scale), the location of each
+  chip in the focal plane, and the optical parameters of the camera
+  (plate scale at center, x,y distortion terms).  Each call to addstar
+  would specify the boresite center:
+
+  addstar -fakeimage ra dec theta
+
+  the camera parameters should be loaded via the config information.
+
+2005.10.24
+
+  I have changed addstar to use the new, more flexible, SkyRegion
+  concept to define the sky layout rather than just the GSCRegion
+  files.  This includes the ability to subdivide the existing
+  GSCRegion concept, and to have a more flexible sky table
+  arrangement.  
+
+  Names of catalog files are a bit tricky.  The SkyRegion functions
+  return base names without fullpath or extensions (ie, n0000/0012).
+  There are a few places in addstar which need the fullname:
+
+  SkyListExitingSubset () requires the path and adds .cpt
+
+  load_pt_catalog () takes the path and adds .cpt.  it also allocates
+  a filename entry for catalog, which is freed later in the loop in
+  addstars
+
+  mkcatalog uses the name in the catalog structure and replaces .cpt
+  with .cpm, .cpn, .cps as needed.
+
+  *** I need to fix the name method: common function to set it once 
+  in the dvo functions.  perhaps a pointer in the SkyRegion structure.
+  This needs some 32/64 bit care, though.  use a long long (or other 
+  type guaranteed to be 64 bit), and case to the appropriate type (size_t) 
+  when it is used?  also needs to be freed.
+
+todo:
+
+  * sky db tools 
+  * parallel servers
+  * threaded addstard
+  * dvo_load daemon
+  * getstar (program and c-client)
+  * parallal / proper-motion data
+  * other additional Average / Measure columns
+  * deal with Myyyy as sky value
+  * better way to interpret flips header info for sky?
+  * fix unlink empty file problem
+  * programs like relphot and dvo need to be provided 
+    with sorted measure tables; add this as a feature 
+    of the load_catalog API?
+
+done:
+
+  o move addstar/gstars to AddstarClientOptions
+  o add thiscode to AddstarClientOptions
+  o address MOSAIC somehow
+  o replace skyprobe mode
+  o complete server db interactions
+  o address the cat & ref modes in client/server
+  o add airmass quality to options
+
+2005.10.19
+
+	I have added the client / server mode, and tested it out to a
+	fair degree.  It handles all of the available addstar modes,
+	including -ref and -cat.  A handful of things still need to be
+	done, including pushing some of the logic to enforce sorting
+	into the dvo load_catalog functions.  Also, I need to correct
+	the idiosyncratic problem with sky vs Myyyy for the skyprobe
+	data.  There are a handful of other improvements that are
+	needed before addstar / DVO is ready for IPP release.
+	However, at this point, it is ready for internal use by the
+	grad students, but not yet ready for CFHT use with Elixir.
+
+2005.10.14
+
+  the current db interactions are somewhat in efficient wrt the image
+  table.  the basic outline is as follows:
+
+  - load image overlaps (needs entire image table)
+  - find detection matches (uses overlap information for missed)
+  - save new image (needs calculated calibration value from detection
+    matches)
+  
+  currently, I access the image table twice, and both times I load the
+  entire thing.  I really only need to append in the second case.  To
+  get to that point, I need to:
+
+  - split image overlaps from image db load
+  - define image partial load (analogous with catalog LOAD_MEAS_META)
+  
+  for the moment, I'm being wasteful so I can defer this issue.
+
+2005.10.13
+
+  I am working to minimize usage of globals and cleanup addstar
+  functions to make them more appropriate for the client / server
+  model.  
+
+  I have dropped save_pt_catalog, and modified load_pt_catalog, as
+  well as the load_catalog functions.  Now, the choice of elements to
+  load is made by setting flags in the catalog structure itself
+  (catalog.catflags).  Thus this information is carried around by the
+  catalog variable.  the load_pt_catalog function now only returns
+  true.  it is now necessary to check the state of the disk file by
+  examining Nave_disk to decide if the file is empty or not.  This is
+  perhaps too limiting: it prevents us from using only one of the
+  catalog tables (say measure without average).  The concern I have is
+  to manage the process of cleaning up the catalogs if no data is
+  added to them:  I don't want addstar to leave behind empty files
+  (plus, I think this breaks the lock/load state logic).  Thus,
+  whenever addstar unlocks / closes a file, if the Nave_disk +
+  Naverage is 0, the file should be unlinked.
+
+2005.10.12
+
+  By friday (2005.10.07), I managed to get the update process
+  working well, and tested it with some example data to demonstrate
+  that the partial update improved speed hugely.  Saturday, I started
+  to work on the client/server separation of addstar.  The first step
+  was to test out socket communication issues.  I set up a simple
+  client/server pair, in which one program listens on a socket while
+  the client connects to the socket and sends some command.  I set up
+  two autocode functions to send and receive complete structures
+  (Send_*, Recv_*) and added these to libautocode.  I have been able
+  to demonstrate sending Stars, Image, and Options to the server.
+
+  The next difficulty is in pulling out the command-line and
+  configuration options.  My original, somewhat lazy, design over-used
+  global variables to pass information between program sections.  This
+  needs to be cleaned up, especially if the server needs to have
+  multiple threads interacting with the db tables.  
+
+  Of the global variables that get set, some simply carry options on
+  how gstars should behave when it loads the data (including the
+  header interpretation information).  This can probably stay in the
+  globals for now.  Another group are configuration information used
+  only by the server-related functions, such as defining the location
+  of the database files and their format, etc.  These can also stay as
+  global, at least for now.  Another set of variables basically pass
+  information from the client to the server on how to behave *this
+  time*.  I have defined the AddstarClientOptions structure to carry
+  these, and will pass them from the client to the server along with
+  the stars and the image data.
+
+  Some outstanding issues:
+
+  sidereal time / latitude
+
+  I am carrying these as globals simply because the image structure
+  did not have them.  This is silly: I can define them as part of the
+  internal image structure without breaking any of the I/O issues.  
+
+  thiscode
+
+  I have been using this as a static photcode structure.  The
+  Send_*/Recv_* mechanisms are better suited to simple structures.  I
+  probably need to change this to just the code, and use the lookup
+  functions to the full structures where needed.  It can be part of
+  the options, and I should re-write functions which use it to accept
+  the options as an argument rather than rely on it as a global.
+
+  mosaic
+
+  this stores the coords structure for a needed mosaic.  this should
+  be kept, filled as needed by the client, and sent to the server with
+  the image.  perhaps it can be moved out of global.
+
+2005.10.06
+
+  I am getting seg faults from find_matches_closest, apparently
+  related to the partial Measure load.  They occur when trying to
+  realloc secfilt.  I tried to debug using the ohana_allocate stuff,
+  but it causes problems because fitsio does not fall under the ohana
+  memory system.  This is fairly bad; I should probably deal with that
+  issue before pursuing the other stuff.  Do I have to make libfits
+  depend on libohana, in which case I should probably split off libdvo
+  from libohana.  That is probably not a bad plan in any case...
+
+2005.10.07
+
+	I have finished the basic implementation of the update mode.
+	I have been able to demonstrate substantial improvements in
+	speed when the number of existing measurements dominates the
+	total number of measurements and the number of averages is
+	typically small compared to the number of measurements (ie,
+	most objects are real, detected in most images, and each new
+	image supplies many new measurements of objects which exist
+	and not many of objects which don't exist already).  The speed
+	gain is significant in this context because the average table
+	is small compared to the measure table; since both update and
+	full-load methods require the complete average table, there is
+	no difference in the load time for the average table.  
+
+	I was having some memory collision problems, and attempting to
+	use the ohana_allocate functions reminded me that the libFITS
+	functions were not supported under ohana_allocate.  This was
+	unhelpful.  I bit the bullet and split libohana into libohana
+	(base functions only, including ohana_allocate) and libdvo
+	(functions based on the libautocode structures).  Doing this
+	allowed me to make libFITS depend on libohana (including
+	ohana_allocate).  BUT, this forced me to change all LDFLAGS
+	entries in ohana to swap -lohana -lFITS for -lFITS -lohana,
+	and to add include <fitsio.h> in some cases.
+
+2005.10.06
+	split / nosort / update
+	I have added a few new concepts to addstar recently: split
+	catalog files, nosort for the measurement table, and
+	update-only.
+
+	split mode
+
+	The split mode is quite straightforeward.  In this mode, each
+	catalog is represented by a set of four files: *.cpt, *.cpm,
+	*.cpn, *.cps.  Each file contains only one FITS table of the
+	data, along with basic header and empty matrix.  Having
+	individual tables for each component of the database lets me
+	add entries without re-writing the entire table.  This should
+	save on I/O operations in the long run.  
+
+	The first file contains the table of averages, and is the file
+	normally identified in the table lookup functions.  The header
+	of this file contains the names of the other table files
+	(paths relative to the directory containing the cpt file).
+	The names and extensions are specified in 'mkcatalog.c'; all
+	other functions use the defined filename references, rather
+	than expecting a naming convention.
+
+	The additional files contain the measures (cpm), missings
+	(cpn), and secfilt (cps) elements of the catalog tables.  
+
+	To facilitate the handling of the additional filenames, file
+	pointers, and headers, Catalog was extended to include
+	pointers to the measure, missing, and secfilt files as
+	additional catalogs.  When the data are loaded into memory,
+	these catalogs are locked (as usual), and file information is
+	stored in the individual Catalog entries; the data segements
+	are all loaded into the main catalog pointers (eg, measures
+	are loaded into catalog[0].measure, rather than
+	catalog[0].measure_catalog[0].measure).
+	
+	The function 'load_catalog' auto-recognizes the SPLIT format
+	by looking for the header keyword MEASURE, identifying the
+	file containing the measures.  The identification of the RAW
+	format and the SPLIT format are not cross-checked: if the
+	NAXIS keyword is set to 2, the file is assumed to be RAW, even
+	if the MEASURE keyword is present.  Careful with this (though
+	there is no reason the main matrix should be used in a basic
+	database table).
+
+	nosort 
+
+	the nosort option by itself provides a minor processing
+	speed-up by deferring the re-sorting of the measurement table
+	until after multiple addstar processes are run.  addstar
+	should not require the measurements to be sorted, so this step
+	can be safetly deferred if only addstars are being performed.
+	the other DVO operations require the sorted table, so the sort
+	must be performed before they are run (either as part of the
+	catalog load, not implemented yet, or with a call to addstar
+	without the -nosort option set.  the real goal of the nosort
+	option is to enable the -update concept in addstar, in which
+	only the new rows are written out; this will only work if
+	addstar can handle unsorted measures. 
+
+	the nosort option required the addition of a 'sorted' element
+	in the Catalog structure to track if the data are sorted or
+	not.  On load, this flag is set based on the value of the
+	header keyword SORTED; if the data is sorted during addstar,
+	the flag is appropriately set, otherwise it is set FALSE be
+	default.  
+
+	The nosort option requires a function which can generate the
+	'next_meas' link sequence based on the measure table.  there
+	is now a function called 'build_meas_link' which generates a
+	correct link list; there is also the pair of functions
+	'init_meas_links' and'init_miss_links' to generate the links
+	in the event that the table is sorted (should be must
+	quicker).
+
+	The 'missing' table is problematic: the LONEOS and ELIXIR
+	formats do not carry an averef entry, thus they do not have
+	enough information to define the links based only on the
+	missing table.  This means we are forced to write out a sorted
+	missing table; the nosort option is invalid for the missing
+	table.  One future upgrade path is to add the averef entry to
+	the PANSTARRS format and then only require the missing table
+	to be sorted if the format is old and does not support
+	-nosort.  (Note also that, for the moment, the missing table
+	has only a single valid format).
+
+	In the process of defining the nosort option, I also cleaned
+	up a bit the find_matches functions to use clearer functions
+	for the links.  
+	
+	update
+
+	The 'update' process in principle allows addstar to
+	substantially reduce the amount of I/O it needs to perform by
+	only requiring addstar to write out new measures and new
+	average/secfilt entries.
+
+	The 'missing' table is problematic: since the format does not
+	support the 'nosort' option, it is not possible to use update
+	with the missing table.  This means we are forced to write out
+	a complete, sorted missing table.  This is currently
+	implemented in update_catalog_split by simply writing out the
+	complete missing table.  In fact, this choice is still flawed
+	because the average table, since it is not written out in full
+	each time, is inconsistent with the missing table: the Nn
+	entries for each average, which identifies the number of
+	missing entries, are not updated.  In practice, this means
+	that the -update option forces the use of the -missed option,
+	though at the moment, this is not forced or checked in any
+	way.
+
+	Note that the 'missed' table contains duplicate information
+	and can, in principle, be completely regenerated at any time.
+	This should be an addstar option: to re-construct the missing
+	table, potentially with constraints on the images which are
+	searched for matches.  
+
+2005.10.04
+	- moved measure/missing list manipulation to separate functions
+	- added concept of sorted / unsorted measure catalog
+	- defined build_meas_links and reorder_measure,missing
+	- some cleanup of both find_matches.c and find_matches_closest.c
+
+2005.10.03:
+	- dropping GSCRegion *region entry from find_matches (unused!)
+	- adding function find_matches_closest (alternate matches)
+
+2005.08.19:
+	changed load_photcode to handle CATMODE and CATFORMAT variations
+	- addstar.h: added CATMODE and CATFORMAT globals
+	- ConfigInit: read CATMODE and CATFORMAT from config
+	- gcatalog: set catalog.catmode from CATMODE
+	- mkcatalog: set CATFORMAT and CATMODE for new catalog
+	- wcatalog: set CATFORMAT for new catalog
+	
+	using full photometry conversions in find_matches
+	added SetZeroPoint to gstars to enable phot conversions
+	
+2005.08.15:
+	cleanup of the minor Wall,Werror messages
+
+2005.03.07 : notes related to new version of addstar
+
+- USNO seems to work fine now:
+
+  addstar -region 9.0 12.0 9.0 12.0 -cat usno -p USNO_RED
+  addstar -region 9.0 12.0 9.0 12.0 -cat usno -p USNO_BLUE
+
+- 2MASS seems to work as well:
+
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_J
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_H
+  addstar -region 3.0 12.0 12.0 14.0 -cat 2mass -p 2MASS_K
+
+- both of these are somewhat inefficient since they need to search
+  through the (larger) USNO/2MASS data files for each output ptolemy
+  region file.
+
+- GSC seems to work fine now:
+
+  addstar -region 3.0 12.0 12.0 14.0 -cat gsc 
+  (photcode is not needed here since there is only one)
+
+- images seems to work fine now
+
+- general concerns:
+
+  - RA 0,360 boundary is likely to have problems, especially with
+    region selection
+  - mosaic astrometry concepts still need to be added.
+
+  - average magnitudes are being set incorrectly in addstar (not
+    taking account of the zero-points).  I don't really want to apply
+    the colors here, but I need to do something, perhaps?
+
+    one option is to only apply the correction for the first
+    measurement of an object, unlike the way astrometry works
+    currently.  both of these should be updated based on
+    relphot/uniphot and eqiuvalent relastro/uniastro independently
+    from the addstar step.
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/pmm.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/pmm.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/pmm.txt	(revision 17422)
@@ -0,0 +1,164 @@
+	
+--
+
+As usual, I have totally forgotten the previous discussion with respect
+to rummaging around in the USNO-B database, but that is just normal for me.
+I think that there are two ways to proceed, and both stem from the exercise
+of attaching WCS headers to the individual PMM camera footprints that we
+did for Google.  I should note that Andy Connolly swears that Google
+will soon serve the 10 TPixels that we gave them, but I am not going
+to hold my breath.
+
+The PMM scanned each Schmidt plate as 588 individual footprints.  The
+scale is about 0.9 arcsec/pixel (2X optics, 6.8 micron camera pixels,
+67.5 arcsec/mm for Schmidt plates).  The readout format was 1394x1037
+(not even an integer multiple of 4!) of which we claimed that the
+useful area was from (67:1378 and 3:1035).  There are no dead pixels
+in either camera, the bias frames are zero, and we have the flats.
+Each image covers 17.8 by 14.0 millimeters but the step-and-stop
+was done on a 16 by 12 millimeter grid giving about 2 millimeters
+(about 2.2 arcminutes) overlap between footprints.
+
+I wrote software that extracted each footprint from each plate,
+made a short, paired list of (X,Y) and (RA,Dec), fed this to imwcs(),
+and saved the image+WCS as separate FITS files.  We scanned 12070
+Schmidt plates of which we saved the pixels for all but the 897 UJ
+(3 minute exposure blue plates from POSS-II).  So far as I know,
+We have 588 times 11,173 FITS files spinning on our disks somewhere.
+These files have been given to Google and SDSC, and they are available
+to anybody else that could seriously use them.  We do not have the
+bandwidth to make soft copies, so we must negotiate for reasonable
+ways to copy the 10 TPixels involved.  We have yet to play the same
+game with the Yale Astrograph plates, but will eventually add these
+3 TPixels of FITS images to the archive.
+
+So Option 1 would be to just hand you guys the FITS WCS images and let you
+play.  You can figure out whether IPP or SExtractor works on photographic
+pixels, how to do photometry, how to handle the overlap zones, and
+all of the other unpleasant things I had to do for PMM for which I
+have been so roundly criticized.
+
+Option 2 would require a small hack in the pixel processing pipeline
+that would save the PMM detection lists and the WCS transformations
+in some reasonable way.  This would save you the work and bandwidth
+of dealing with the images, but would then rely on the PMM's image
+processing software for computation of the detection parameters.
+
+Depending on which, if either, seems to be a reasonable way to
+proceed, I think that we have enough bandwidth (or could use DVDs)
+so send a small sample of what the data would look like.  Did I send
+you the flat ASCII file of the nominal pointings and epochs (12070 lines)?
+
+Let me know.  I will be in Flagstaff for the next couple of weeks,
+but then my travel schedule goes nuts.
+
+-Dave
+
+--
+
+I had thought that I passed you this file before.  The files
+I would be passing you would start with the 7 character root
+in the third column, SSnnnnn where SS is a unique survey
+identifier, and nnnnn is a unique plate number from that
+observatory.  You can safely ignore the first 2 columns.
+The plate is the third, followed by DDMonYYYY HH:MM
+HHMMSS sDDMMSS Emulsion Filter Exposure H:MM.  It is
+believed that the UT is for the start of the exposure,
+but it might be for the middle.  Many things are lost in
+the cobwebs of antiquity.  I can give you a table of
+nominal wavelengths for various Emulsion+Filter combinations.
+The final column is the hour angle of mid-exposure.
+
+-Dave
+
+example:
+
+0         1         2         3         4         5         6         7         8         9         0         
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+so0001 /nofs/leo1/sel/jb0/se100/so0001a.inf PO00570 23AUG1952 09:22 194300 +900000 103AO NONE    10 0:13E
+so0002 /nofs/leo1/sel/jb0/se100/so0002a.inf PO00567 22AUG1952 08:21 223100 +900000 103AO NONE    10 1:14E
+so0003 /nofs/leo1/sel/jb0/se100/so0003a.inf PO00568 22AUG1952 10:53 000624 +843140 103AO NONE    10 1:10W
+so0004 /nofs/leo1/sel/jb0/se100/so0004a.inf PO01277 23NOV1954 06:45 025031 +842224 103AO NONE    12 0:29W
+so0005 /nofs/leo1/sel/jb0/se100/so0005a.inf PO01328 29JAN1955 06:42 052420 +840404 103AO NONE    12 2:19W
+
+
+emulsions:
+098
+098-0
+103AD
+103AE
+103AO
+IIIAF
+IIIAJ
+IVN
+
+filters:
+#12
+AMB2
+AMB3
+AMB4
+AMB5
+AMB6
+AMB7
+AMB8
+GG358
+GG385
+GG395
+MULTI
+NONE
+OG590
+RED
+RED66
+RED67
+RED68
+RED69
+RED70
+RED71
+RED73
+RG2444
+RG600
+RG610
+RG630
+RG715
+RG9
+RP2444
+WR88A
+YEL3
+YEL8
+
+combinations:
+098 RED 
+098-0 RED70 
+098-0 RG630 
+103AD MULTI 
+103AD YEL3 
+103AD YEL8 
+103AE #12 
+103AE AMB2 
+103AE AMB3 
+103AE AMB4 
+103AE AMB5 
+103AE AMB6 
+103AE AMB7 
+103AE AMB8 
+103AE NONE 
+103AE RED66 
+103AE RED67 
+103AE RED68 
+103AE RED69 
+103AE RED70 
+103AE RED71 
+103AE RED73 
+103AE RG2444 
+103AE RP2444 
+103AO NONE 
+IIIAF OG590 
+IIIAF RG600 
+IIIAF RG610 
+IIIAF RG630 
+IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss-tsObj.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss-tsObj.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss-tsObj.txt	(revision 17422)
@@ -0,0 +1,686 @@
+.
+  Calibrated Objects
+
+*File Format:* FITS binary table
+
+*Name:* /tsObj-rrrrrr-c-v-ffff.fit/, where /rrrrrr/ is the imaging run
+number, /c/ is the camera column (1-6), and /v/ is the rerun number and
+ffff is the first field number of the segment.
+
+*Produced by:* ts
+
+*Used by:* ts, sx
+
+*Size:* 7 * 2880 + (2240 * Nstars) {rounded up to nearest multiple of
+2880) * Nfields. For a single pipeline run with 1000 fields, 400 objects
+per field, 887.06 Mb.
+
+*Archived?* Yes, by sx.
+
+
+    Description
+
+Calibrated version of the object lists put out by the /frames/ pipeline.
+
+
+    Primary Header
+
+SIMPLE  =                    T
+BITPIX  =                    8
+NAXIS   =                    0
+EXTEND  =                    T
+RUN     =                  XXX / Imaging run number.
+CAMCOL  =                  XXX / Column in the imaging camera.
+RERUN   =                  XXX / Rerun number
+FIELD0  =                  XXX / First field reduced.
+NFIELDS =                  XXX / Number of fields reduced.
+STRIPE  =                  XXX / Stripe number
+STRIP   = 'X       '           / N - north, S - south
+EQUINOX =          XXXX.XXXXXX / Equinox of great circle scanned (Julian years)
+NODE    =           XXX.XXXXXX / Ascending node of great circle scanned (deg)
+INCL    =           XXX.XXXXXX / Inclincation of great circle scanned (deg)
+C_OBS   =            XXXXX.XXX / CCD clock rate (usec/unbinned-row)
+TRACKING=            XXXXX.XXX / Tracking rate (arcsec/TAI-sec)
+PHOTO_ID= 'XXX     '           / Photometric pipeline run id.
+TASTR_ID= 'XXX     '           / Astrometric pipeline run id for target.
+EASTR_ID= 'XXX     '           / Astrometric pipeline run id for export.
+TFCAL_ID= 'XXX     '           / Photometric calibration run id for target.
+EFCAL_ID= 'XXX     '           / Photometric calibration run id for export.
+PHOT_VER= 'XXX     '           / Version of frames pipeline used.
+TAST_VER= 'XXX     '           / Version of astrometric pipeline for target.
+EAST_VER= 'XXX     '           / Version of astrometric pipeline for export.
+TFCA_VER= 'XXX     '           / Version of fcalib for target.
+EFCA_VER= 'XXX     '           / Version of fcalib for export.
+TARG_VER= 'XXX     '           / Version of target pipeline used.
+EXPO_VER= 'XXX     '           / Version of export pipeline used.
+FILTERS = 'XXX XXX ...'        / Filter order in array fields
+OBJ_SRC = 'XXX     '           / Source of objects:
+COMMENT                        /    OPDB_PRELIM = OPDB, preliminary data
+COMMENT                        /    OPDB_EXPORT = OPDB, exported to SX
+COMMENT                        /    SX          = Science Database
+COMMENT                        /    FLATFILES   = Flat files
+TARG_SRC= 'XXX     '           / Source of target info:
+COMMENT                        /    OPDB_PRELIM = OPDB, preliminary data
+COMMENT                        /    OPDB_EXPORT = OPDB, exported to SX
+COMMENT                        /    SX          = Science Database
+COMMENT                        /    FLATFILES   = Flat files
+RESOLVED=                    T / Data from a resolved segment
+EXPORTED=                  XXX / Type of objects exported in this file:
+COMMENT                        /    1 = spectroscopic targets only
+COMMENT                        /    2 = primaries only
+COMMENT                        /    3 = primaries and secondaries
+COMMENT                        /    4 = all objects
+FAMILIES=                    T / Contains family members of included objects
+REF_BAND=                    2 / band used for canonical centre
+DARK_VAR= '4 4 4 4 4'          / Per-pixel variances at zero DN (DN^2)
+GAIN    = '3.5 3.5 3.5 3.5 3.5' / Mean gains of amplifiers (e/DN)
+END                         
+
+
+    Table Header
+
+There is one HDU of this type in the file for each field, ordered by
+field number. The "QUALITY" keyword records the quality of data in the
+field, in terms of its acceptance for the survey, and can take the
+following meanings:
+
+  'BAD      '   /* Not acceptable for the survey */
+  'ACCEPTABLE'  /* Acceptable for the survey, but we'd better data */
+  'GOOD     '   /* Fully acceptable --- no desire for better data */
+  'MISSING  '   /* No objects in this field, because data is missing, but we
+                 * accept the field into the survey as a hole in the survey */
+  'HOLE     '   /* Data in this field is not acceptable, but we will accept the
+                 * field into the survey as a hole in the survey, meaning none
+                 * of the objects in the field are part of the survey */
+
+The "STATUS_X" keywords record the status of the PSF fit for that field
+in that filter, and can take the following meanings:
+
+  'UNKNOWN  '    /* ??? */
+  'OK       '    /* PSF fitted by 2nd order parabolas */
+  'PSF22    '    /* PSF fitted by linear functions */
+  'PSF11    '    /* PSF fitted by a constant */
+  'NOPSF    '    /* PSF fit from previous frame used*/
+  'ABORTED  '    /* ??? */
+  'MISSING  '    /* ??? */
+
+PSP_STAT is the maximum value of STATUS_X over all 5 filters.
+
+The /objc_type/, /objc_flags/, /objc_flags2/, /flags/, /flags2/, and
+/type/ fields are enumerated types or bit masks, with the same set of
+legal values as found for the same fields in the fpObjc <fpObjc.html> file.
+
+The /status/ field is a bit mask specifying the status of the object in
+the survey. The bits are defined as:
+
+  AR_OBJECT_STATUS_SET        = 0x1,    /* This object's status has been set */
+					/* in reference to its own run.      */
+  AR_OBJECT_STATUS_GOOD       = 0x2,    /* Good as determined by its         */
+					/* object flags.  Absence implies    */
+                                        /* bad.  This flag is set by         */
+                                        /* "setObjectStatus".                */
+  AR_OBJECT_STATUS_DUPLICATE  = 0x4,    /* This object has one or more       */
+					/* duplicate detections in an        */
+					/* adjacent field of the same Frames */
+					/* Pipeline Run.  This is set by     */
+					/* "setObjectStatus".                */
+  AR_OBJECT_STATUS_OK_RUN     = 0x10,   /* Located within the primary range  */
+					/* of rows for this field.  This is  */
+                                        /* usable object.  This flag is set  */
+                                        /* by "setObjectStatus".             */
+  AR_OBJECT_STATUS_RESOLVED   = 0x20,   /* This object has been resolved     */
+					/* against other runs.               */
+  AR_OBJECT_STATUS_PSEGMENT   = 0x40,   /* Belongs to a PRIMARY segment.     */
+					/* This does not imply that this is a*/
+					/* primary object.                   */
+  AR_OBJECT_STATUS_FIRST_FIELD= 0x100,  /* Belongs to the first field in its */
+					/* segment.  Used to distinguish     */
+					/* objects in fields shared by two   */
+					/* segments.                         */
+  AR_OBJECT_STATUS_OK_SCANLINE= 0x200,  /* Lies within valid nu range        */
+					/* for its scanline.                 */
+  AR_OBJECT_STATUS_OK_STRIPE  = 0x400,  /* Lies within valid eta range for   */
+					/* its stripe.                       */
+  AR_OBJECT_STATUS_SECONDARY  = 0x1000, /* This is a secondary survey object.*/
+  AR_OBJECT_STATUS_PRIMARY    = 0x2000, /* This is a primary survey object.  */
+  AR_OBJECT_STATUS_TARGET     = 0x4000  /* This is a spectroscopic target.   */
+
+The /primTarget/ field is a bit mask specifying which primary target
+catagories the object was selected in. The bits are defined as:
+
+  AR_TARGET_QSO_HIZ           = 0x1,
+  AR_TARGET_QSO_CAP           = 0x2,
+  AR_TARGET_QSO_SKIRT         = 0x4,
+  AR_TARGET_QSO_FIRST_CAP     = 0x8,
+  AR_TARGET_QSO_FIRST_SKIRT   = 0x10,
+  AR_TARGET_QSO_MAG_OUTLIER  = 0x2000000,
+  AR_TARGET_QSO_REJECT	      = 0x20000000,
+  AR_TARGET_GALAXY_RED        = 0x20,
+  AR_TARGET_GALAXY_RED_II     = 0x4000000,
+  AR_TARGET_GALAXY            = 0x40,
+  AR_TARGET_GALAXY_BIG        = 0x80,
+  AR_TARGET_GALAXY_BRIGHT_CORE= 0x100,
+  AR_TARGET_ROSAT_A           = 0x200,
+  AR_TARGET_ROSAT_B           = 0x400,
+  AR_TARGET_ROSAT_C           = 0x800,
+  AR_TARGET_ROSAT_D           = 0x1000,
+  AR_TARGET_ROSAT_E           = 0x8000000,
+  AR_TARGET_STAR_BHB          = 0x2000,
+  AR_TARGET_STAR_CARBON       = 0x4000,
+  AR_TARGET_STAR_BROWN_DWARF  = 0x8000,
+  AR_TARGET_STAR_SUB_DWARF    = 0x10000,
+  AR_TARGET_STAR_CATY_VAR     = 0x20000,
+  AR_TARGET_STAR_RED_DWARF    = 0x40000,
+  AR_TARGET_STAR_WHITE_DWARF  = 0x80000,
+  AR_TARGET_STAR_PN           = 0x10000000,
+  AR_TARGET_SERENDIP_BLUE     = 0x100000,
+  AR_TARGET_SERENDIP_FIRST    = 0x200000,
+  AR_TARGET_SERENDIP_RED      = 0x400000,
+  AR_TARGET_SERENDIP_DISTANT  = 0x800000,
+  AR_TARGET_SERENDIP_MANUAL   = 0x1000000
+
+The /secTarget/ field is a bit mask specifying which secondary target
+catagories the object was selected in. The bits are defined as:
+
+  TAR_TARGET_LIGHT_TRAP        = 0x1,
+  TAR_TARGET_REDDEN_STD        = 0x2,
+  TAR_TARGET_TEST_TARGET       = 0x4,
+  TAR_TARGET_QA                = 0x8,
+  TAR_TARGET_SKY               = 0x10,
+  TAR_TARGET_SPECTROPHOTO_STD  = 0x20,
+  TAR_TARGET_GUIDE_STAR        = 0x40,
+  TAR_TARGET_BUNDLE_HOLE       = 0x80,
+  TAR_TARGET_QUALITY_HOLE      = 0x100,
+  TAR_TARGET_HOT_STD           = 0x200
+
+The /CULLED/ keyword is a bit mask with the same bit values as the
+/primTarget/ field. If a bit is set, then all targets of that target
+type were culled during target selection, and thus this field is
+excluded from that science sample.
+
+XTENSION= 'BINTABLE'          
+BITPIX  =                    8
+NAXIS   =                    2
+NAXIS1  =                 2732
+NAXIS2  =                  XXX
+PCOUNT  =                    0
+GCOUNT  =                    1
+TFIELDS =                  146
+FIELD   =                  XXX / Field sequence number within the run.
+QUALITY = 'XXX     '           / Quality of field in terms of survey acceptance
+CULLED  =                  XXX / Culling bit mask
+MJD_U   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of u' frame was read
+MJD_G   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of g' frame was read
+MJD_R   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of r' frame was read
+MJD_I   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of i' frame was read
+MJD_Z   =         XXXXX.XXXXXX / MJD(TAI) when row 0 of z' frame was read
+SEEING_U=                 XX.X / Seeing on the u' frame (FWHM, arcsecs)
+SEEING_G=                 XX.X / Seeing on the g' frame (FWHM, arcsecs)
+SEEING_R=                 XX.X / Seeing on the r' frame (FWHM, arcsecs)
+SEEING_I=                 XX.X / Seeing on the i' frame (FWHM, arcsecs)
+SEEING_Z=                 XX.X / Seeing on the z' frame (FWHM, arcsecs)
+STATUS_U= 'XXX     '           / PSF fit status on u' frame
+STATUS_G= 'XXX     '           / PSF fit status on g' frame
+STATUS_R= 'XXX     '           / PSF fit status on r' frame
+STATUS_I= 'XXX     '           / PSF fit status on i' frame
+STATUS_Z= 'XXX     '           / PSF fit status on z' frame
+PSP_STAT= 'XXX     '           / Maximum of STATUS value on all 5 frames
+PSFERR_U=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_G=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_R=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_I=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+PSFERR_Z=               XXX.XX / Photometric err due to imperfect PSF model(mag)
+SKY_U   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_G   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_R   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_I   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+SKY_Z   =               XXX.XX / Global sky value after obj sub (mag/arcsec^2)
+TFORM1  = '1J      '          
+TTYPE1  = 'run     '           / Run ID
+TUNIT1  = 'unitless'          
+TFORM2  = '1J      '          
+TTYPE2  = 'camCol  '           / Camera column
+TUNIT2  = 'unitless'          
+TFORM3  = '1J      '          
+TTYPE3  = 'rerun   '           / Rerun number
+TUNIT3  = 'unitless'          
+TFORM4  = '1J      '          
+TTYPE4  = 'field   '           / Field ID
+TUNIT4  = 'unitless'          
+TFORM5  = '1J      '          
+TTYPE5  = 'parent  '           / Parent
+TUNIT5  = 'unitless'          
+TFORM6  = '1J      '          
+TTYPE6  = 'id      '           / Object ID, unique within its field (1 indexed)
+TUNIT6  = 'unitless'          
+TFORM7  = '1J      '          
+TTYPE7  = 'nchild  '           / Number of Children
+TUNIT7  = 'unitless'          
+TFORM8  = '1J      '          
+TTYPE8  = 'objc_type'          / Classification
+TUNIT8  = 'unitless'          
+TFORM9  = '1E      '
+TTYPE9  = 'objc_prob_psf'      / Probablity object is a star
+TUNIT9  = 'unitless'          
+TFORM10 = '1J      '          
+TTYPE10 = 'catID   '           / Catalog ID
+TUNIT10 = 'unitless'          
+TFORM11 = '1J      '          
+TTYPE11 = 'objc_flags'         / Object flags
+TUNIT11 = 'unitless'          
+TFORM12 = '1J      '          
+TTYPE12 = 'objc_flags2'        / More object flags
+TUNIT12 = 'unitless'          
+TFORM13 = '1E      '          
+TTYPE13*= 'objc_rowc'          / Nominal row position (r' coordinates)
+TUNIT13 = 'pixels  '          
+TFORM14 = '1E      '          
+TTYPE14*= 'objc_rowcErr'       / Nominal row position (r' coordinates) error
+COMMENT                        / This is the centroiding error only.  It does
+COMMENT                        / not include errors from the TRANS structures.
+TUNIT14 = 'pixels  '          
+TFORM15 = '1E      '          
+TTYPE15*= 'objc_colc'          / Nominal column position (r' coordinates)
+TUNIT15 = 'pixels  '          
+TFORM16 = '1E      '          
+TTYPE16*= 'objc_colcErr'       / Nominal column position (r' coordinates) error
+COMMENT                        / This is the centroiding error only.  It does
+COMMENT                        / not include errors from the TRANS structures.
+TUNIT16 = 'pixels  '          
+TFORM17 = '1E      '          
+TTYPE17 = 'rowv    '           / Row-component of object's velocity
+TUNIT17 = 'deg/day '          
+TFORM18 = '1E      '          
+TTYPE18 = 'rowvErr '           / Row-component of object's velocity error
+TUNIT18 = 'deg/day '          
+TFORM19 = '1E      '          
+TTYPE19 = 'colv'               / Column-component of object's velocity 
+TUNIT19 = 'deg/day'          
+TFORM20 = '1E      '          
+TTYPE20 = 'colvErr'            / Column-component of obejct's velocity error
+TUNIT20 = 'deg/day'          
+TFORM21 = '5E      '          
+TTYPE21 = 'rowc    '           / Row center
+TUNIT21 = 'pixels  '          
+TFORM22 = '5E      '          
+TTYPE22 = 'rowcErr '           / Row center error
+TUNIT22 = 'pixels  '          
+TFORM23 = '5E      '          
+TTYPE23 = 'colc    '           / Center colum 
+TUNIT23 = 'pixels  '          
+TFORM24 = '5E      '          
+TTYPE24 = 'colcErr '           / Center column error
+TUNIT24 = 'pixels  '          
+TFORM25 = '5E      '          
+TTYPE25 = 'sky     '           / Sky flux
+TUNIT25 = 'asinh mag/arcsec^2'         
+TFORM26 = '5E      '          
+TTYPE26 = 'skyErr  '           / Sky flux error
+TUNIT26 = 'asinh mag/arcsec^2'         
+TFORM27 = '5E      '          
+TTYPE27 = 'psfCounts'          / PSF flux
+TUNIT27 = 'asinh mag     '          
+TFORM28 = '5E      '          
+TTYPE28 = 'psfCountsErr'       / PSF flux error
+TUNIT28 = 'asinh mag     '          
+TFORM29 = '5E      '          
+TTYPE29 = 'fiberCounts'        / Flux in 3 arcsec diameter fiber radius
+TUNIT29 = 'asinh mag     '          
+TFORM30 = '5E      '          
+TTYPE30 = 'fiberCountsErr'     / Flux in 3 arcsec diameter fiber radius error
+TUNIT30 = 'asinh mag     '          
+TFORM31 = '5E      '          
+TTYPE31 = 'petroCounts'        / Petrosian flux
+TUNIT31 = 'asinh mag     '          
+TFORM32 = '5E      '          
+TTYPE32 = 'petroCountsErr'     / Petrosian flux error
+TUNIT32 = 'asinh mag     '          
+TFORM33 = 5E      '          
+TTYPE33 = 'petroRad'           / Petrosian radius
+TUNIT33 = 'arcsec '          
+TFORM34 = '5E      '          
+TTYPE34 = 'petroRadErr'        / Petrosian radius error
+TUNIT34 = 'arcsec '          
+TFORM35 = '5E      '          
+TTYPE35 = 'petroR50'           / Radius with 50 percent of Petrosian light
+TUNIT35 = 'arcsec '          
+TFORM36 = '5E      '          
+TTYPE36 = 'petroR50Err'        / Radius with 50 percent of Petrosian light error
+TUNIT36 = 'arcsec '          
+TFORM37 = '5E      '          
+TTYPE37 = 'petroR90'           / Radius with 90 percent of Petrosian light
+TUNIT37 = 'arcsec '          
+TFORM38 = '5E      '          
+TTYPE38 = 'petroR90Err'        / Radius with 90 percent of Petrosian light error
+TUNIT38 = 'arcsec  '          
+TFORM39 = '5E      '          
+TTYPE39 = 'Q       '           / Stokes Q parameter
+TUNIT39 = 'unitless'          
+TFORM40 = '5E      '          
+TTYPE40 = 'QErr    '           / Stokes Q parameter error
+TUNIT40 = 'unitless'          
+TFORM41 = '5E      '          
+TTYPE41 = 'U       '           / Stokes U parameter
+TUNIT41 = 'unitless'          
+TFORM42 = '5E      '          
+TTYPE42 = 'UErr    '           / Stokes U parameter error
+TUNIT42 = 'unitless'          
+TFORM43 = '5E      '
+TTYPE43 = 'M_e1    '           / Adaptive E1 shape measure
+TUNIT43 = 'unitless'          
+TFORM44 = '5E      '
+TTYPE44 = 'M_e2    '           / Adaptive E2 shape measure
+TUNIT44 = 'unitless'          
+TFORM45 = '5E      '
+TTYPE45 = 'M_e1e1Err'          / Covariance in E1-E1 shape measure
+TUNIT45 = 'unitless'          
+TFORM46 = '5E      '
+TTYPE46 = 'M_e1e2Err'          / Covariance in E1-E2 shape measure
+TUNIT46 = 'unitless'          
+TFORM47 = '5E      '
+TTYPE47 = 'M_e2e2Err'          / Covariance in E2-E2 shape measure
+TUNIT47 = 'unitless'          
+TFORM48 = '5E      '
+TTYPE48 = 'M_rr_cc '           / Adaptive ( + )
+TUNIT48 = 'unitless'          
+TFORM49 = '5E      '
+TTYPE49 = 'M_rr_ccErr'         / Error in adaptive ( + )
+TUNIT49 = 'unitless'          
+TFORM50 = '5E      '
+TTYPE50 = 'M_cr4   '           / Adaptive fourth moment
+TUNIT50 = 'unitless'          
+TFORM51 = '5E      '
+TTYPE51 = 'M_e1_psf'           / Adaptive E1 for PSF
+TUNIT51 = 'unitless'          
+TFORM52 = '5E      '
+TTYPE52 = 'M_e2_psf'           / Adaptive E2 for PSF
+TUNIT52 = 'unitless'          
+TFORM53 = '5E      '
+TTYPE53 = 'M_rr_cc_psf'        / Adaptive () for PSF
+TUNIT53 = 'unitless'          
+TFORM54 = '5E      '
+TTYPE54 = 'M_cr4_psf'          / Adaptive fourth moment for PSF
+TUNIT54 = 'unitless'          
+TFORM55 = '5E      '          
+TTYPE55 = 'iso_rowc'           / Isophotal row centroid
+TUNIT55 = 'pixels  '          
+TFORM56 = '5E      '          
+TTYPE56 = 'iso_rowcErr'        / Isophotal row centroid error
+TUNIT56 = 'pixels  '          
+TFORM57 = '5E      '          
+TTYPE57 = 'iso_rowcGrad'       / Gradient in row centroid with isophote
+TUNIT57 = 'pixels/mag/arcsec^2'
+TFORM58 = '5E      '          
+TTYPE58 = 'iso_colc'           / Isophotal column centroid
+TUNIT58 = 'pixels  '          
+TFORM59 = '5E      '          
+TTYPE59 = 'iso_colcErr'        / Isophotal column centroid error
+TUNIT59 = 'pixels  '          
+TFORM60 = '5E      '          
+TTYPE60 = 'iso_colcGrad'       / Gradient in column centroid with isophote
+TUNIT60 = 'pixels/mag/arcsec^2'
+TFORM61 = '5E      '          
+TTYPE61 = 'iso_a   '           / Isophotal major axis
+TUNIT61 = 'arcsec  '          
+TFORM62 = '5E      '          
+TTYPE62 = 'iso_aErr'           / Isophotal major axis error
+TUNIT62 = 'arcsec  '          
+TFORM63 = '5E      '          
+TTYPE63 = 'iso_aGrad'          / Gradient in major axis with isophote
+TUNIT63 = 'arcsec/mag/arcsec^2'
+TFORM64 = '5E      '          
+TTYPE64 = 'iso_b   '           / Isophotal minor axis
+TUNIT64 = 'arcsec  '          
+TFORM65 = '5E      '          
+TTYPE65 = 'iso_bErr'           / Isophotal minor axis error
+TUNIT65 = 'arcsec  '          
+TFORM66 = '5E      '          
+TTYPE66 = 'iso_bGrad'          / Gradient in minor axis with isophote
+TUNIT66 = 'arcsec/mag/arcsec^2'
+TFORM67 = '5E      '          
+TTYPE67 = 'iso_phi '           / Isophotal position angle
+TUNIT67 = 'degrees (+N thru E)'          
+TFORM68 = '5E      '          
+TTYPE68 = 'iso_phiErr'         / Isophotal position angle error
+TUNIT68 = 'degrees '          
+TFORM69 = '5E      '          
+TTYPE69 = 'iso_phiGrad'        / Gradient in positional angle with isophote
+TUNIT69 = 'degrees/mag/arcsec^2'
+TFORM70 = '5E      '          
+TTYPE70 = 'r_deV   '           / De Vaucouleurs fit scale radius
+TUNIT70 = 'arcsec  '          
+TFORM71 = '5E      '          
+TTYPE71 = 'r_deVErr'           / De Vaucouleurs fit scale radius error
+TUNIT71 = 'arcsec  '          
+TFORM72 = '5E      '          
+TTYPE72 = 'ab_deV  '           / De Vaucouleurs fit a/b
+TUNIT72 = 'unitless'          
+TFORM73 = '5E      '          
+TTYPE73 = 'ab_deVErr'          / De Vaucouleurs fit a/b error
+TUNIT73 = 'unitless'          
+TFORM74 = '5E      '          
+TTYPE74 = 'phi_deV '           / De Vaucouleurs fit position angle
+TUNIT74 = 'degrees (+N thru E)'          
+TFORM75 = '5E      '          
+TTYPE75 = 'phi_deVErr'         / De Vaucouleurs fit position angle error
+TUNIT75 = 'degrees '          
+TFORM76 = '5E      '          
+TTYPE76 = 'counts_deV'         / De Vaucouleurs magnitude fit
+TUNIT76 = 'asinh mag '          
+TFORM77 = '5E      '          
+TTYPE77 = 'counts_deVErr'      / De Vaucouleurs magnitude fit error
+TUNIT77 = 'asinh mag '          
+TFORM78 = '5E      '          
+TTYPE78 = 'r_exp   '           / Exponetial fit scale radius
+TUNIT78 = 'arcsec  '          
+TFORM79 = '5E      '          
+TTYPE79 = 'r_expErr'           / Exponetial fit scale radius error
+TUNIT79 = 'arcsec '          
+TFORM80 = '5E      '          
+TTYPE80 = 'ab_exp  '           / Exponential fit a/b
+TUNIT80 = 'unitless'          
+TFORM81 = '5E      '          
+TTYPE81 = 'ab_expErr'          / Exponential fit a/b error
+TUNIT81 = 'unitless'          
+TFORM82 = '5E      '          
+TTYPE82 = 'phi_exp '           / Exponential fit position angle
+TUNIT82 = 'degrees (+N thru E)'          
+TFORM83 = '5E      '          
+TTYPE83 = 'phi_expErr'         / Exponential fit position angle error
+TUNIT83 = 'degrees '          
+TFORM84 = '5E      '          
+TTYPE84 = 'counts_exp'         / Exponential fit
+TUNIT84 = 'asinh mag '          
+TFORM85 = '5E      '          
+TTYPE85 = 'counts_expErr'      / Exponential fit error
+TUNIT85 = 'asinh mag '          
+TFORM86 = '5E      '          
+TTYPE86 = 'counts_model'       / Better of DeV/Exp magnitude fit
+TUNIT86 = 'asinh mag '          
+TFORM87 = '5E      '          
+TTYPE87 = 'counts_modelErr'    / Better of DeV/Exp magnitude fit error
+TUNIT87 = 'asinh mag '          
+TFORM88 = '5E      '          
+TTYPE88 = 'texture '           / Texture parameter
+TUNIT88 = 'unitless'          
+TFORM89 = '5E      '          
+TTYPE89 = 'star_L  '           / Star likelihood
+TUNIT89 = 'unitless'          
+TFORM90 = '5E      '          
+TTYPE90 = 'star_lnL'           / Star ln(likelihood)
+TUNIT90 = 'unitless'          
+TFORM91 = '5E      '          
+TTYPE91 = 'exp_L   '           / Exponential disk fit likelihood
+TUNIT91 = 'unitless'          
+TFORM92 = '5E      '          
+TTYPE92 = 'exp_lnL '           / Exponential disk fit ln(likelihood)
+TUNIT92 = 'unitless'          
+TFORM93 = '5E      '          
+TTYPE93 = 'deV_L   '           / De Vaucouleurs fit likelihood
+TUNIT93 = 'unitless'          
+TFORM94 = '5E      '          
+TTYPE94 = 'deV_lnL '           / De Vaucouleurs fit ln(likelihood)
+TUNIT94 = 'unitless'          
+TFORM95 = '5E      '          
+TTYPE95 = 'fracPSF '           / Fraction of light in PSF
+TUNIT95 = 'unitless'          
+TFORM96 = '5J      '          
+TTYPE96 = 'flags   '           / Object flags
+TUNIT96 = 'unitless'          
+TFORM97 = '5J      '          
+TTYPE97 = 'flags2  '           / More object flags
+TUNIT97 = 'unitless'          
+TFORM98 = '5J      '          
+TTYPE98 = 'type    '           / Object type
+TUNIT98 = 'unitless'          
+TFORM99 = '5E      '
+TTYPE99 = 'prob_psf'           / Probablity object is a star in each filter
+TUNIT99 = 'unitless'          
+TFORM100= '5J      '          
+TTYPE100= 'nprof   '           / Number of radial bins
+TUNIT100= 'unitless'          
+TFORM101= '75E     '          
+TTYPE101= 'profMean'           / Mean pixel flux in annulus
+TUNIT101= 'maggies/arcsec^2'         
+TDIM101 = '(15,5)  '          
+TFORM102= '75E     '          
+TTYPE102= 'profErr '           / Standard deviation of profMean
+TUNIT102= 'maggies/arcsec^2'        
+TDIM102 = '(15,5)  '          
+TFORM103= '1J      '          
+TTYPE103= 'status  '           / Survey status
+TUNIT103= 'unitless'          
+TFORM104= '1D      '          
+TTYPE104= 'ra      '           / J2000 right ascension (r')
+TUNIT104= 'degrees '          
+TFORM105= '1D      '          
+TTYPE105= 'dec     '           / J2000 declination (r')
+TUNIT105= 'degrees '          
+TFORM106= '1D      '          
+TTYPE106= 'lambda  '           / Survey longitude (r')
+TUNIT106= 'degrees '          
+TFORM107= '1D      '          
+TTYPE107= 'eta     '           / Survey latitude (r')
+TUNIT107= 'degrees '          
+TFORM108= '1D      '          
+TTYPE108= 'l       '           / Galactic longitude (lII, r')
+TUNIT108= 'degrees '          
+TFORM109= '1D      '          
+TTYPE109= 'b       '           / Galactic latitude (bII, r')
+TUNIT109= 'degrees '          
+TFORM110= '5E      '          
+TTYPE110= 'offsetRa'           / Filter position ra minus final ra (* cos(dec))
+TUNIT110= 'arcsec  '          
+TFORM111= '5E      '          
+TTYPE111= 'offsetDec'          / Filter position dec minus final dec
+TUNIT111= 'arcsec  '          
+TFORM112= '1J      '
+TTYPE112= 'primTarget'         / Target selection flags
+TUNIT112= 'unitless'          
+TFORM113= '1J      '
+TTYPE113= 'secTarget'          / Additional Target selection flags
+TUNIT113= 'unitless'          
+TFORM114= '5E      '
+TTYPE114= 'reddening'          / Reddening in each filter
+TUNIT114= 'mag   '
+TFORM115= '1J      '
+TTYPE115= 'propermotionmatch'  / 1= match to USNO catatalog
+TUNIT115= 'unitless'          
+TFORM116= '1E      '
+TTYPE116= 'propermotiondelta'  / Difference in positions between USNO and
+COMMENT                        / photo object (r' band)
+TUNIT116= 'arcsec'
+TFORM117= '1E      '
+TTYPE117= 'propermotion'       / Proper motion of object
+TUNIT117= 'arcsec/century'
+TFORM118= '1E      '
+TTYPE118= 'propermotionangle'  / Direction of proper motion
+TUNIT118= 'degrees (+N thru E)'
+TFORM119= '1E      '
+TTYPE119= 'usnoBlue'           / Blue magntiude of matching USNO object
+TUNIT119= 'mag     '
+TFORM120= '1E      '
+TTYPE120= 'usnoRed '           / Red magntiude of matching USNO object
+TUNIT120= 'mag     '
+TFORM121= '1J      '
+TTYPE121= 'firstMatch'         / Number of FIRST sources matched (0=no match)
+TUNIT121= 'unitless'          
+TFORM122= '1J      '
+TTYPE122= 'firstId '           / FIRST catalog id of one matching source
+TUNIT122= 'unitless'          
+TFORM123= '1D      '
+TTYPE123= 'firstLambda'        / Composite radio source survey longitude
+TUNIT123= 'degrees '
+TFORM124= '1D      '
+TTYPE124= 'firstEta'           / Composite radio source survey latitude
+TUNIT124= 'degrees '
+TFORM125= '1E      '
+TTYPE125= 'firstDelta'         / Position difference between first and photo object (r' band)
+TUNIT125= 'arcsec'
+TFORM126= '1E      '
+TTYPE126= 'firstPeak'          / Peak first radio flux
+TUNIT126= 'mJy'
+TFORM127= '1E      '
+TTYPE127= 'firstInt'           / Integrated first radio flux
+TUNIT127= 'mJy'
+TFORM128= '1E      '
+TTYPE128= 'firstRms'           / Rms error in flux
+TUNIT128= 'mJy'
+TFORM129= '1E      '
+TTYPE129= 'firstMajor'         / Major axis (deconvolved)
+TUNIT129= 'arcsec'
+TFORM130= '1E      '
+TTYPE130= 'firstMinor'         / Minor axis (deconvolved)
+TUNIT130= 'arcsec'
+TFORM131= '1E      '
+TTYPE131= 'firstPa'            / Position angle (east of north)
+TUNIT131= 'degrees'
+TFORM132= '1J      '
+TTYPE132= 'rosatMatch'         / ID of matching ROSAT source (0 = no match)
+TUNIT132= 'unitless'          
+TFORM133= '1E      '
+TTYPE133= 'rosatDelta'         / Position difference between ROSAT and photo object (r' band)
+TUNIT133= 'arcsec'
+TFORM134= '1E      '                                                           
+TTYPE134= 'rosatPosErr'        / Error in ROSAT position
+TUNIT134= 'arcsec '
+TFORM135= '1E      '
+TTYPE135= 'rosatCps'           / Integrated ROSAT counts
+TUNIT135= 'counts/sec'
+TFORM136= '1E      '
+TTYPE136= 'rosatCpsErr'        / Error in integrated ROSAT counts
+TUNIT136= 'counts/sec'
+TFORM137= '1E      '
+TTYPE137= 'rosatHr1'           / ROSAT hardness ratio 1
+TUNIT137= 'unitless'          
+TFORM138= '1E      '
+TTYPE138= 'rosatHr1Err'        / Error in ROSAT hardness ratio 1
+TUNIT138= 'unitless'          
+TFORM139= '1E      '
+TTYPE139= 'rosatHr2'           / ROSAT hardness ratio 2
+TUNIT139= 'unitless'          
+TFORM140= '1E      '
+TTYPE140= 'rosatHr2Err'        / Error in ROSAT hardness ratio 2
+TUNIT140= 'unitless'          
+TFORM141= '1E      '
+TTYPE141= 'rosatExt'           / ROSAT extent parameter
+TUNIT141= 'arcsec'
+TFORM142= '1E      '
+TTYPE142= 'rosatExtLike'       / ROSAT extent parameter likelihood
+TUNIT142= 'unitless'          
+TFORM143= '1E      '
+TTYPE143= 'rosatDetectLike'    / ROSAT detection likelihood
+TUNIT143= 'unitless'          
+TFORM144= '1E      '
+TTYPE144= 'rosatExposure'      / ROSAT exposure time
+TUNIT144= 'seconds '
+TFORM145= '1J      '
+TTYPE145= 'priority'           / Priority bits
+TSCAL145= 1.00000000000000E+00
+TZERO145= 2.14748364800000E+09
+TUNIT145= 'unitless'          
+TFORM146= '50J     '
+TTYPE146= 'matchid '           / Link ids (run,camCol,rerun,field,id)
+TUNIT146= 'unitless'          
+TDIM146 = '(5,10)  '
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/doc/sdss.txt	(revision 17422)
@@ -0,0 +1,60 @@
+
+adding SDSS object tables:
+
+* autodetect the SDSS files from the header?
+* do I need to read multiple files for a single data set?
+* how are we going to determine photcodes?
+  * do we need to set the photcode on a per-object basis in LoadData or equivalent?
+
+* some re-org might be needed for LoadStars
+
+
+** fields to keep:
+
+name          sdss 
+ X            'objc_colc' (or colc)
+ Y            'objc_rowc' (or rowc)
+ dX           'objc_colcErr' (or colcErr)
+ dY           'objc_rowcErr' (or rowcErr)
+ R            ra  + offset Ra (get cos des right)
+ D            dec + offset Dec
+ dR           (calculate from dX * plate scale?)
+ dD           (calculate from dX * plate scale?)
+ uR           (from 'propermotion' and 'propermotionangle')
+ uD           (from 'propermotion' and 'propermotionangle')
+ duR          ?
+ duD          ?
+ P            X
+ dP           X
+ M            psfCounts (is already a mag?)
+ dM           psfCountsErr (is already a mag?)
+ sky          sky (convert to flux?)
+ dsky         skyErr (convert to flux?)
+ fx           ?
+ fy           ?
+ df           ?
+ Mgal         X
+ Map          X
+ Mpeak        X
+ detID        X
+ found        -1
+ t            MJD_U + Y*rate
+ dt           fixed? NY*rate
+ psfChisq     
+ crNsigma     
+ extNsigma    
+ psfQual      
+ Mcal         
+ airmass      
+ az            
+ code         
+ nFrames      
+ flags        
+ dophot       
+ dummy        
+
+we can use these header fields to get the exp times, and I suppose alt, az, airmass, etc:
+NODE    =           XXX.XXXXXX / Ascending node of great circle scanned (deg)
+INCL    =           XXX.XXXXXX / Inclincation of great circle scanned (deg)
+C_OBS   =            XXXXX.XXX / CCD clock rate (usec/unbinned-row)
+TRACKING=            XXXXX.XXX / Tracking rate (arcsec/TAI-sec)
Index: /tags/ipp-2-5-1/Ohana/src/addstar/etc/phottemp.cat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/etc/phottemp.cat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/etc/phottemp.cat	(revision 17422)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/ipp-2-5-1/Ohana/src/addstar/etc/template.cat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/etc/template.cat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/etc/template.cat	(revision 17422)
@@ -0,0 +1,296 @@
+SIMPLE  =                    F  / ASTROCAM PHOTOMETRY FILE                    \
+BITPIX  =                   16 /                                              \
+NAXIS   =                    2  / NUMBER OF AXES                              \
+NAXIS1  =                 1106  / NUMBER OF COLUMNS                           \
+NAXIS2  =                 1024  / NUMBER OF ROWS                              \
+BSCALE  =             1.000000 /                                              \
+BZERO   =             0.000000 /                                              \
+DATE    = '19/12/94'            / UT Date of file creation (DD/MM/YY)         \
+ORIGIN  = 'MDM Observatory'     / Michigan-Dartmouth-MIT                      \
+LATITUDE=              31.9500  / Latitude (degrees N)                        \
+LONGITUD=            -111.6150  / Longitude (degrees E)                       \
+OBSERVER= 'Metzger '            / Name of observer                            \
+TELESCOP= '1.3m McGraw-Hill'    / Telescope used for observation              \
+INSTRUME= 'Charlotte Direct'    / Instrument used for observation             \
+DETECTOR= 'Charlotte/Tek 1024^2 CCD'  / Detector used for observation         \
+FRAME   =                   32  / Frame number of observation                 \
+CCDPICNO=                   32  / Frame number of observation                 \
+OBJECT  = 'ocl0327 '            / Name of object                              \
+IMAGETYP= 'OBJECT  '            / Type of observation                         \
+EXPTIME =              250.000  / Integration time (seconds)                  \
+DARKTIME=              250.067  / Dark current time (seconds)                 \
+DATE-OBS= '19/12/94'            / UT Date of observation (DD/MM/YY)           \
+UT      = ' 05:25:44.00'        / Universal time (UTC) at exposure start      \
+JD      =       2449705.726204                                                \
+RA      =           50.700      / Right Ascension                             \
+DEC     =           89.000      / Declination                                 \
+DIRECTN =                 -1    / Moving South                                \
+EQUINOX =             1950.000  / Equinox of RA and DEC                       \
+HA      = ' 02:14:06.72'        / Hour angle at start                         \
+ST      = ' 03:49:36.85'        / Sidereal time at start                      \
+ZD      = ' 36:26:07.17'        / Zenith distance (degrees)                   \
+AIRMASS =                1.243  / Airmass at start                            \
+FILTER  = 'I KP    '            / Filter description                          \
+GAIN    =                3.350  / Nominal gain (e-/ADU)                       \
+SECPIX1 =                0.508  / Arcseconds per pixel in fast dir            \
+SECPIX2 =                0.508  / Arcseconds per pixel in slow dir            \
+CCDBIN1 =                    1  / On-chip column binning (fast dir)           \
+CCDBIN2 =                    1  / On-chip row binning (slow dir)              \
+GPROBE  = '  5000.00   7000.00'  / Guide probe X Y                            \
+DATASEC = '[51:1074,1:1024]'    / Image area of frame                         \
+CCDSEC  = '[1:1074,1:1024]'     / Image area relative to full chip            \
+BIASSEC = '[1080:1106,1:1023]'  / Overscan area of frame                      \
+UNSIGN  =                    T /                                              \
+NSTARS  =                    0  / NUMBER OF stars                             \
+CTYPE1  = 'RA---SIN          ' /                                               
+CTYPE2  = 'DEC--SIN          ' /                                               
+CDELT1  =             0.000733 /                                               
+CDELT2  =             0.000733 /                                               
+CRVAL1  =            50.191418 /                                               
+CRVAL2  =            88.998663 /                                               
+CRPIX1  =          1028.644173 /                                               
+CRPIX2  =           503.129830 /                                               
+PC001001=             0.999906 /                                               
+PC001002=             0.012081 /                                               
+PC002001=            -0.011936 /                                               
+PC002002=             0.999950 /                                               
+END                                                                           \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+                                                                              \
+ 246.5  352.1 13.000 020 1 3.2
+ 246.5  352.1 14.100 020 1 3.2
+ 279.4  328.9 13.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 187.3  238.8  9.800 020 1 3.2
+ 436.6  413.5 14.300 020 1 3.2
+   9.3  856.5 13.000 020 1 3.2
+   9.3  856.2 13.600 020 1 3.2
+ 319.7  697.9 12.000 020 1 3.2
+ 319.7  697.7 12.100 020 1 3.2
+ 244.3    9.5 14.100 020 1 3.2
+ 284.7  334.4 14.000 020 1 3.2
+ 127.1  122.8 14.200 020 1 3.2
+ 140.2  791.1 13.700 020 1 3.2
+ 140.2  790.8 14.400 020 1 3.2
+ 131.7  636.3 13.800 020 1 3.2
+ 423.8  700.6 13.800 020 1 3.2
+ 423.7  700.7 12.500 020 1 3.2
+ 266.2  290.7 13.800 020 1 3.2
+ 266.0  290.7 13.100 020 1 3.2
+ 570.7  721.6 13.600 020 1 3.2
+ 570.7  721.6 13.100 020 1 3.2
+ 548.7  673.0 13.700 020 1 3.2
+ 264.2  219.7 12.800 020 1 3.2
+ 264.0  219.7 12.100 020 1 3.2
+ 161.9  797.4 11.900 020 1 3.2
+ 162.0  797.2 12.200 020 1 3.2
+ 505.8  851.8 13.700 020 1 3.2
+  88.2  419.0 11.400 020 1 3.2
+  88.2  419.0 11.800 020 1 3.2
+ 426.9  613.2  9.400 020 1 3.2
+ 427.4  613.5  7.600 020 1 3.2
+ 326.1  595.2 12.100 020 1 3.2
+ 326.2  595.1 12.600 020 1 3.2
+ 444.4  845.2 14.200 020 1 3.2
+ 254.1  916.8 14.500 020 1 3.2
+ 502.6  882.4 14.200 020 1 3.2
+ 128.3  275.7 12.300 020 1 3.2
+ 128.3  275.7 12.900 020 1 3.2
+ 230.9  554.8 13.300 020 1 3.2
+ 230.9  554.7 14.200 020 1 3.2
+  47.5  685.2 13.500 020 1 3.2
+  47.5  684.9 14.200 020 1 3.2
+ 382.6  658.7 14.000 020 1 3.2
+ 182.2  330.3 14.200 020 1 3.2
+ 245.1  548.3 12.600 020 1 3.2
+ 245.3  548.0 13.200 020 1 3.2
+  50.3  849.0 13.700 020 1 3.2
+  50.5  848.8 14.300 020 1 3.2
+  17.9  772.7 14.200 020 1 3.2
+ 499.8  712.8 13.500 020 1 3.2
+ 499.8  712.6 14.400 020 1 3.2
+ 294.5  349.4 14.000 020 1 3.2
+ 497.7  747.8 11.000 020 1 3.2
+ 497.5  747.8 10.900 020 1 3.2
+ 109.7  323.5 14.000 020 1 3.2
+  59.5  917.6 14.300 020 1 3.2
+  59.4  917.8 13.700 020 1 3.2
+  82.2  880.5 13.800 020 1 3.2
+ 145.1  341.2 14.500 020 1 3.2
+ 452.7  592.4 14.400 020 1 3.2
+ 623.4  820.0 13.700 020 1 3.2
+ 609.7  829.3 13.500 020 1 3.2
+ 481.8  938.3 14.400 020 1 3.2
+ 617.0  792.7 13.500 020 1 3.2
+ 183.2  539.7 14.400 020 1 3.2
+ 133.9  679.6 14.400 020 1 3.2
+ 423.7  667.8  6.500 020 1 3.2
+ 434.7  111.9 13.200 020 1 3.2
+ 434.5  111.9 13.200 020 1 3.2
+ 434.6  111.9 14.200 020 1 3.2
+1222.3  684.2 13.900 020 1 3.2
+1077.6  282.5 14.000 020 1 3.2
+1077.8  282.5 13.400 020 1 3.2
+ 720.8  562.1 14.100 020 1 3.2
+ 721.1  561.9 14.400 020 1 3.2
+1182.1  944.4 12.100 020 1 3.2
+1181.9  944.4 11.800 020 1 3.2
+1181.9  944.4 12.300 020 1 3.2
+ 761.2  423.1 14.300 020 1 3.2
+ 761.0  423.1 14.100 020 1 3.2
+ 926.7  436.8 13.400 020 1 3.2
+ 926.6  436.8 14.200 020 1 3.2
+1209.1   45.0 13.200 020 1 3.2
+1209.2   43.6 12.800 020 1 3.2
+1135.2  653.1  8.100 020 1 3.2
+1135.6  653.4  8.200 020 1 3.2
+1123.5  735.8 12.000 020 1 3.2
+1123.6  735.8 11.800 020 1 3.2
+1123.7  735.7 11.600 020 1 3.2
+ 730.7   28.6 12.100 020 1 3.2
+ 730.7   28.6 12.200 020 1 3.2
+ 783.1  267.5 14.200 020 1 3.2
+ 783.2  267.5 13.400 020 1 3.2
+ 876.2  462.7 13.800 020 1 3.2
+ 876.4  462.7 14.200 020 1 3.2
+ 917.2  399.9 13.700 020 1 3.2
+ 917.3  399.9 13.200 020 1 3.2
+1227.5  218.4 10.400 020 1 3.2
+1227.7  218.4 10.500 020 1 3.2
+1022.7  146.0 13.300 020 1 3.2
+1022.5  146.0 14.100 020 1 3.2
+1324.7   72.3 12.900 020 1 3.2
+1324.6   72.3 13.200 020 1 3.2
+ 743.2  379.4 14.300 020 1 3.2
+ 742.9  379.4 13.900 020 1 3.2
+1255.9  544.0 12.600 020 1 3.2
+1256.0  543.9 12.300 020 1 3.2
+ 848.9  251.1 13.200 020 1 3.2
+ 849.0  251.1 12.700 020 1 3.2
+ 495.6  147.4 11.400 020 1 3.2
+ 495.6  147.4 11.300 020 1 3.2
+ 495.5  147.4 11.000 020 1 3.2
+1016.4  735.5 14.200 020 1 3.2
+1016.3  735.7 13.900 020 1 3.2
+ 795.2  675.9 14.400 020 1 3.2
+ 887.2  968.8 10.800 020 1 3.2
+ 626.8  726.9 14.400 020 1 3.2
+ 718.6  800.1  9.700 020 1 3.2
+ 675.4  301.6 14.400 020 1 3.2
+ 801.6  436.8 14.200 020 1 3.2
+1066.4  870.0 13.700 020 1 3.2
+1066.5  869.9 13.800 020 1 3.2
+ 644.5  662.9  9.900 020 1 3.2
+ 639.1  760.4 13.000 020 1 3.2
+ 822.7  663.1 12.800 020 1 3.2
+ 822.7  663.1 13.100 020 1 3.2
+ 677.2  716.2 12.900 020 1 3.2
+ 765.6  641.5 13.200 020 1 3.2
+ 765.7  641.4 13.500 020 1 3.2
+ 998.3  425.8 14.400 020 1 3.2
+ 713.7  716.2 13.800 020 1 3.2
+ 814.7  887.6  2.000 020 1 3.2
+ 808.7  884.1  8.200 020 1 3.2
+1812.3  259.3 14.500 020 1 3.2
+1655.9  655.4 12.200 020 1 3.2
+1655.9  655.4 12.100 020 1 3.2
+1577.2  839.6 11.500 020 1 3.2
+1577.2  839.6 11.400 020 1 3.2
+1643.7  597.0 14.100 020 1 3.2
+1552.8  251.1 10.100 020 1 3.2
+1552.8  251.1 10.300 020 1 3.2
+1410.0  999.9 13.900 020 1 3.2
+1335.6  687.0 13.000 020 1 3.2
+1335.8  686.8 12.700 020 1 3.2
+1471.4  262.0 14.200 020 1 3.2
+1479.6  608.3 14.200 020 1 3.2
+1478.1  211.5 12.000 020 1 3.2
+1478.3  211.5 12.000 020 1 3.2
+1552.5   46.4 14.500 020 1 3.2
+2042.4  548.3 10.800 020 1 3.2
+2042.6  548.3 11.000 020 1 3.2
+1448.0  915.7 14.100 020 1 3.2
+1462.3  989.2 13.000 020 1 3.2
+1462.3  989.0 12.700 020 1 3.2
+1316.9  787.3 14.100 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1787.6  883.5 12.200 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1979.1  300.3 12.900 020 1 3.2
+1677.2  106.4 13.100 020 1 3.2
+1677.0  106.4 13.300 020 1 3.2
+1324.2  429.9  9.200 020 1 3.2
+1324.2  429.9  9.000 020 1 3.2
+1462.8  214.3 13.700 020 1 3.2
+1462.8  214.3 14.200 020 1 3.2
+1405.4   95.5 11.700 020 1 3.2
+1405.2   95.5 11.600 020 1 3.2
+1604.8   90.0 11.200 020 1 3.2
+1604.8   90.0 11.300 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1921.5  738.1 13.500 020 1 3.2
+1459.8  473.6 13.400 020 1 3.2
+1459.7  473.6 13.800 020 1 3.2
+1438.7   77.8 12.200 020 1 3.2
+1438.6   77.8 12.200 020 1 3.2
+1355.0  114.6 13.000 020 1 3.2
+1354.9  114.6 13.200 020 1 3.2
+1941.4  940.7 13.000 020 1 3.2
+1941.6  940.7 12.900 020 1 3.2
+1994.0  800.8 13.900 020 1 3.2
+1993.8  800.8 14.000 020 1 3.2
+2024.4  907.0 13.700 020 1 3.2
+2024.4  907.0 13.800 020 1 3.2
+2035.7  881.6 13.900 020 1 3.2
+2035.9  881.6 13.800 020 1 3.2
+1994.0  924.2 12.100 020 1 3.2
+1994.0  924.2 12.000 020 1 3.2
Index: /tags/ipp-2-5-1/Ohana/src/addstar/include/2mass.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/include/2mass.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/include/2mass.h	(revision 17422)
@@ -0,0 +1,55 @@
+
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double RA[2];
+  int Nrec;
+} TM_Region;
+
+typedef struct {
+  double Rmin, Rmax, Dmin, Dmax;
+  int index[20];
+  int Nindex;
+} TMBands;
+
+typedef struct {
+  double R, D;
+  int offset;
+  int flag;
+} TMStars;
+
+short TM_J, TM_H, TM_K;
+
+SkyTable *get2mass_acc (SkyRegion *patch, char *path, char *accel);
+Stars    *get2mass_2DR_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars);
+Stars    *get2mass_AS_data (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+Stars    *get2mass_AS_rawdata (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+
+SkyTable *scan2mass_acc (char *path, char *accel);
+int       scan2mass_as_data (char *filename);
+
+char     *skipNbounds (char *line, char bound, int Nbound, int Nbyte);
+e_time    get2mass_time (char *ptr, int Nbound, int Nbyte);
+e_time    get2mass_date (char *ptr, int Nbound, int Nmax);
+
+int       load2mass_as_rawdata (SkyTable *skytable, char *filename, AddstarClientOptions options);
+SkyTable *load2mass_acc (char *path, char *accel);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+int       load2mass_catalog (Catalog *catalog, Stars *stars, int Nstars);
+
+int       get2mass_setup (int photcode);
+int       get2mass_coords (char *line, double *R, double *D, int Nmax);
+int       get2mass_star (Stars *star, char *line, int Nmax);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+
+int get2mass_3star_full (Stars *star, char *line, int Nmax);
+char *next2MASSfield (char *line);
+int set2MASS_ph_qual (Stars *star, char qual);
+int set2MASS_rd_flag (Stars *star, char qual);
+int set2MASS_cc_flag (Stars *star, char qual);
+int set2MASS_bl_flag (Stars *star, char qual);
+int set2MASS_gal_flag (Stars *star, char qual);
+int set2MASS_mp_flag (Stars *star, char qual);
+int set2MASS_dup_flag (Stars *star, char qual);
+int set2MASS_use_flag (Stars *star, char qual);
+int get2mass_sortStars (TMStars *tstars, int Ntstars);
Index: /tags/ipp-2-5-1/Ohana/src/addstar/include/addstar.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/include/addstar.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/include/addstar.h	(revision 17422)
@@ -0,0 +1,275 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* solaris requires both of these instead of ip.h:
+   # include <sys/socket.h>
+   # include <netinet/in.h>
+*/
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+/* used in find_matches, find_matches_refstars */
+# define IN_REGION(R,D) ( \
+((D) >= region[0].Dmin) && ((D) < region[0].Dmax) && \
+((R) >= region[0].Rmin)  && ((R) < region[0].Rmax))
+
+/* grab named photcode */
+# define NAMED_PHOTCODE(CODE,NAME) \
+  CODE = GetPhotcodeCodebyName (NAME); \
+  if (!CODE) { \
+    fprintf (stderr, "ERROR:  photcode %s not found in photcode table\n", NAME); \
+    exit (0); }
+
+typedef struct {
+  int Nstars;
+  Stars *stars;
+  int Nimages;
+  Image *images;
+  Coords *mosaic;
+  SkyRegion *patch;
+  AddstarClientOptions *options;
+  char *refcat;
+} DVO_DATA;
+
+typedef struct {
+  char *exthead;
+  char *extdata;
+  char *exttype;
+  int extnum_head;
+  int extnum_data;
+} HeaderSet;
+
+typedef struct sockaddr_in SockAddress;
+
+enum {M_IMAGE, M_REFLIST, M_REFCAT, M_FAKEIMAGE, M_RESORT};
+enum {NONE, SIMPLE_CMP, SIMPLE_CMF, SIMPLE_MEF, MOSAIC_CMP, MOSAIC_CMF, MOSAIC_MEF, MOSAIC_PHU, SDSS_OBJ};
+/* note: MEF implies CMF */
+
+/* globals which define database info / data sources (KEEP) */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   TWO_MASS_DIR_AS[256];
+char   TWO_MASS_DIR_DR2[256];
+char   GSCDIR[256];
+char   USNO_A_DIR[256];
+char   USNO_B_DIR[256];
+char   TYCHO_DIR[256];
+char   SubpixDatafile[256];
+char   PASSWORD[80];
+char   HOSTNAME[80];
+int    NVALID, *VALID_IP;
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+char   CameraLayout[256];
+SkyTable *ServerSky;
+char  *PMM_CCD_TABLE;
+
+/* used to select entries from header (gstars or parse_time) (KEEP) */
+char   DateKeyword[64];
+char   DateMode[64];
+char   UTKeyword[64];
+char   MJDKeyword[64];
+char   JDKeyword[64];
+char   ExptimeKeyword[64];
+char   AirmassKeyword[64];
+char   CCDNumKeyword[64];
+char   STKeyword[64];
+char   ExtnameKeyword[64];
+
+/* these globals modify the behavior of gstars (KEEP) */
+double 	SNLIMIT;
+int    	ACCEPT_ASTROM;  // accept even bad astrometry solutions (NASTRO == 0)
+int    	ACCEPT_MOTION;  // accept reference proper motion measurements
+int    	ACCEPT_TIME;    // accept time stamp (or 0)
+int    	NO_STARS;       // ignore the stars
+int    	TEXTMODE;       // force input file to be loaded as RAW
+int     SUBPIX;         // apply a subpix correction
+char   *DUMP;           // dump out intermediate results
+int    	XOVERSCAN;      // used to modify stored image dimensions 
+int    	YOVERSCAN;      // used to modify stored image dimensions 
+int    	XMIN;           // used to filter loaded star list 
+int    	XMAX;           // used to filter loaded star list 
+int    	YMIN;           // used to filter loaded star list 
+int    	YMAX;           // used to filter loaded star list 
+double 	Latitude;       // carried into image structure from config
+double 	Longitude;      // carried into image structure from config
+double  FAKE_RA;        // boresite coords for fake images
+double  FAKE_DEC;       // boresite coords for fake images
+double  FAKE_THETA;     // boresite angle for fake images
+
+// carries the mosaic into gstars
+
+/* these globals are used separately by both client and server (KEEP) */
+double CAL_INSTMAG_MAX;
+double CAL_INSTMAG_MIN;
+int    VERBOSE;
+int    PLOT;
+double MAX_CERROR;
+
+/* modify server behavior (make this an addstar cleanup mode?) */
+int    FORCE_READ;
+
+// XXX this should be replaced with 
+// 1) an airmass accuracy option
+// 2) an alternative CATFORMAT with the sky element correctly defined.
+
+/* these depend on HOW we implement the client/server interaction for CAT/REF modes */
+time_t    TIMEREF;    // used by MODE REF
+SkyRegion UserPatch;  // used by MODE CAT
+char     *SELECT_2MASS_QUALITY;  // used only by get2mass_as
+
+/*** addstar prototypes ***/
+
+void       AddToCalibration       PROTO((Average *average, SecFilt *secfilt, Measure *measure, Measure *new, int *next, int Nstar));
+AddstarClientOptions ConfigInit   PROTO((int *argc, char **argv));
+void       FindCalibration        PROTO((Image *image));
+FILE      *GetDB                  PROTO((int *state));
+void       InitCalibration        PROTO(());
+void       SaveCalibration        PROTO((float Mo, float dMo, float Mr, float dMr, float Mi, int N));
+void       SetProtect             PROTO((int mode));
+int        SetSignals             PROTO(());
+int        Shutdown               PROTO((char *message, ...); ) 
+void       TrapSignal             PROTO((int sig));
+float      airmass                PROTO((float secz_image, double ra, double dec, double st, double latitude));
+void       SetAirmassQuality      PROTO((int quality));
+SkyTable  *SkyTableFromTychoIndex PROTO((char *filename, int VERBOSE));
+AddstarClientOptions args         PROTO((int argc, char **argv, AddstarClientOptions options));
+void       check_permissions      PROTO((char *basefile));
+int        dump_rawstars          PROTO((Stars *stars, int Nstars));
+int        edge_check             PROTO((double *x1, double *y1, double *x2, double *y2));
+Image     *fakeimage              PROTO((char *rootname, int *Nimage, int photcode));
+int        find_matches           PROTO((SkyRegion *region, Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+int        find_matches_closest   PROTO((SkyRegion *region, Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+int        find_matches_refstars  PROTO((SkyRegion *region, Stars **stars, int Nstars, Catalog *catalog, AddstarClientOptions options));
+Stars    **find_subset            PROTO((SkyRegion *region, Stars *stars, int Nstars, int *NSTARS));
+int        gcatalog               PROTO((Catalog *catalog));
+Stars     *get2mass               PROTO((SkyRegion *patch, int photcode, int mode, int *NSTARS));
+double     get_subpix             PROTO((double x, double y));
+Stars     *getgsc                 PROTO((SkyRegion *patch, int *NSTARS));
+Stars     *gettycho               PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Stars     *getusno                PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Stars     *getusnob               PROTO((SkyRegion *catstats, int photcode, int *Nstars));
+Image     *gimages                PROTO((FITS_DB *db, Image *image, Coords *mosaic, int *Npimage));
+Stars     *grefcat                PROTO((char *Refcat, SkyRegion *catstats, int photcode, int *nstars));
+Stars     *grefstars              PROTO((char *file, int photcode, int *Nstars));
+
+Stars     *LoadStars              PROTO((char *file, int *Nstars, Image **images, int *Nimages, AddstarClientOptions *options));
+Header   **LoadHeaders            PROTO((FILE *f, int *mode, int *Nheader));
+HeaderSet *MatchHeaders           PROTO((int **extsize, int *nimage, int mode, Header **headers, int Nheaders));
+int        LoadData               PROTO((FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int NheaderSets));
+
+int        in_image               PROTO((double r, double d, Image *image));
+int        load_pt_catalog        PROTO((Catalog *catalog, SkyRegion *region));  /*** choose new name ***/
+void       load_subpix            PROTO(());
+void       lock_image_db          PROTO((FITS_DB *db, char *filename));
+int        main                   PROTO((int argc, char **argv));
+void       make_backup            PROTO((char *filename));
+int        match_refstars         PROTO((Stars *stars, int Nstars));
+void       mkcatalog              PROTO((SkyRegion *region, Catalog *catalog));
+double     opening_angle          PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+int        parse_time             PROTO((Header *header));
+Stars     *rd_gsc                 PROTO((char *filename, int *nstars));
+int        replace_match          PROTO((Average *average, Measure *measure, Stars *star));
+void       resort_catalog         PROTO((Catalog *catalog));
+Stars     *rfits                  PROTO((FILE *f, unsigned int *nstars));
+Stars     *rtext                  PROTO((FILE *f, unsigned int *nstars));
+Stars     *ReadStarsFITS          PROTO((FILE *f, Header *header, Header *in_theader, unsigned int *nstars));
+Stars     *ReadStarsTEXT          PROTO((FILE *f, unsigned int *nstars));
+Stars     *ReadStarsSDSS          PROTO((FILE *f, char *name, Header *header, Header *in_theader, Image *images, int *nimages, unsigned int *nstars));
+int        ReadImageHeader        PROTO((Header *header, Image *image, int photcode));
+Stars     *FilterStars            PROTO((Stars *instars, Image *image, unsigned int imageID));
+Stars     *MergeStars             PROTO((Stars *stars, int *Nstars, Stars *instars, int Ninstars));
+void       save_pt_catalog        PROTO((Catalog *catalog));  /*** choose new name ***/
+double     scat_subpix            PROTO((double x, double y));
+time_t     short_date_to_sec      PROTO((char *date));
+void       sort_lists             PROTO((float *X, float *Y, int *S, int N));
+void       sort_stars             PROTO((Stars *stars, int N));
+int        str_to_radec           PROTO((double *ra, double *dec, char *str1, char *str2));
+void       unlock_image_db        PROTO((FITS_DB *db));
+void       update_coords          PROTO((Average *average, Measure *measure, int *next));
+void       wcatalog               PROTO((Catalog *catalog));
+void       free_catalog           PROTO((Catalog *catalog));
+void       wimage                 PROTO((FITS_DB *db, Image *image));
+void       create_image_db        PROTO((FITS_DB *db));
+void       set_db                 PROTO((FITS_DB *in));
+void       uppercase              PROTO((char *string));
+void       fsort                  PROTO((float *X, int N));
+void       fsort2                 PROTO((float *X, float *Y, int N));
+int 	  *init_measure_links     PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure));
+int 	  *init_missing_links     PROTO((Average *average, int Naverage, Missing *missing, int Nmissing));
+int 	   add_meas_link     	  PROTO((Average *average, int *next, int Nmeasure, int NMEASURE));
+int 	   add_miss_link     	  PROTO((Average *average, int *next, int Nmissing));
+int 	  *build_measure_links    PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure));
+Measure   *sort_measure     	  PROTO((Average *average, int Naverage, Measure *measure, int Nmeasure, int *next));
+Missing   *sort_missing     	  PROTO((Average *average, int Naverage, Missing *missing, int Nmissing, int *next_miss));
+int        ImageOptions		  PROTO((AddstarClientOptions *options, Image *images, int Nimages));
+int        GetFileMode		  PROTO((Header *header));
+AddstarClientOptions args_client  PROTO((int argc, char **argv, AddstarClientOptions options));
+AddstarClientOptions args_load2mass PROTO((int argc, char **argv, AddstarClientOptions options));
+AddstarClientOptions args_sedstar PROTO((int argc, char **argv, AddstarClientOptions options));
+void	   args_server		  PROTO((int argc, char **argv));
+int 	   CheckPassword	  PROTO((int BindSocket));
+int 	   NewImage		  PROTO((int BindSocket));
+int 	   NewReflist		  PROTO((int BindSocket));
+int 	   NewRefcat		  PROTO((int BindSocket));
+int 	   InitServerSocket	  PROTO((SockAddress *Address));
+int 	   WaitServerSocket	  PROTO((int InitSocket, SockAddress *Address, int *validIP, int Nvalid));
+int 	   GetClientSocket	  PROTO((char *hostname));
+int 	   UpdateDatabase_Image	  PROTO((AddstarClientOptions *options, Image *images, int Nimages, Coords *mosaic, Stars *stars, int Nstars));
+int 	   UpdateDatabase_Reflist PROTO((AddstarClientOptions *options, Stars *stars, int Nstars));
+int 	   UpdateDatabase_Refcat  PROTO((AddstarClientOptions *options, SkyRegion *UserPatch, char *refcat));
+SkyList   *SkyListForStars	  PROTO((SkyTable *table, int depth, Stars *stars, int Nstars));
+SkyList   *SkyListExistingSubset  PROTO((SkyList *input, char *path));
+int        SkyListSetPath	  PROTO((SkyList *list, char *path));
+SkyTable  *SkyTableLoadOptimal	  PROTO(());
+int 	   InitDataset		  PROTO(());
+int 	   PushDataset		  PROTO((DVO_DATA *data));
+DVO_DATA  *PopDataset		  PROTO((void));
+void	  *ListenClients_Thread	  PROTO((void *data));
+int 	   NewImage_Thread	  PROTO((int BindSocket));
+int 	   NewRefcat_Thread	  PROTO((int BindSocket));
+int 	   NewReflist_Thread	  PROTO((int BindSocket));
+
+Stars     *Convert_SMPDATA	  PROTO((FTable *table, int *nstars));
+Stars     *Convert_PS1_DEV_0	  PROTO((FTable *table, int *nstars));
+Stars     *Convert_PS1_DEV_1	  PROTO((FTable *table, int *nstars));
+
+int args_skycells (int argc, char **argv);
+int ConfigInit_skycells (int *argc, char **argv);
+int UpdateImageIDs (Stars *stars, int Nstars, Image *images, int Nimages);
+int LoadDataSDSS (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages);
+int altaz (double *alt, double *az, double ha, double dec, double latitude);
+
+int LoadDataPMM (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars);
+PhotCode *LoadMetadataPMM (char *datafile, Image *image);
+time_t pmm_date_to_sec (char *date, char *time);
+double pmm_get_ra (char *RA);
+double pmm_get_dec (char *DEC);
+PhotCode *pmm_get_photcode (char *emulsion, char *filter);
+
+// this is a gnu extension?? caution!
+void *memrchr(const void *s, int c, size_t n);
+
+/** 
+    there is an inconsistency to be resolved: fixed structures (like Image)
+    need a fixed bit-length time (e_time), but these functions all use the
+    UNIX time_t types, which may be 32 or 64 bits, depending on the machine.
+    This can be resolved by using time_t with these functions, but casting 
+    between e_time and time_t when necessary (ie, cannot return data to an
+    e_time pointer from one of these functions)
+**/
+
+
+/** function for client / server **/ 
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/include/sedstar.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/include/sedstar.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/include/sedstar.h	(revision 17422)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+# include "kapa.h"
+
+typedef enum {
+  SED_FIT,
+  SED_REQ,
+  SED_MODEL,
+  SED_SAMPLE,
+} SEDtableModes;
+
+typedef struct {
+  float *mags;
+  float color;
+  float Temp;
+  float Av;
+} SEDtableRow;
+
+typedef struct {
+  float chisq;
+  float Md;
+  int row;
+} SEDfit;
+
+typedef struct {
+  int Nfilter;
+  float *wavecode;
+  float *vegaToAB;
+  int *mode;
+  int *hashcode;
+  int *code;
+  int codeP;
+  int codeM;
+  SEDtableRow **row;
+  int Nrow;
+} SEDtable;
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N);
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter);
+SEDtable *SEDtableLoad (char *filename);
+int SEDcolorBracket (SEDtable *table, float color, float delta);
+int SEDfitInit (SEDtable *table);
+int SEDfitPlot (SEDtable *table, double R, double D, SEDfit *minFit, SEDtableRow *sourceValue, SEDtableRow *sourceError);
+int SEDfitClear ();
+int SEDfitCatalog (Catalog *outcat, Catalog *incat, SEDtable *table);
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode);
Index: /tags/ipp-2-5-1/Ohana/src/addstar/include/skycells.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/include/skycells.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/include/skycells.h	(revision 17422)
@@ -0,0 +1,92 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <signal.h>
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+/* linux is happy with this, not solaris */
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <glob.h>
+
+enum {SQUARES, TRIANGLES};
+
+typedef struct {
+  double x, y, z;
+} Point;
+
+typedef struct {
+  Point vertex[3];	      // triangle vertices (3d)
+  Point center;		      // triangle center (3d)
+  double r, d;		      // triangle center (2d)
+  double rv[3], dv[3];	      // triangle center (2d)
+} SkyTriangle;
+
+typedef struct {
+  Coords coords;
+  int NX;
+  int NY;
+  int code;
+  char name[64];
+} SkyRectangle;
+
+/* globals which define database info / data sources (KEEP) */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   PASSWORD[80];
+char   HOSTNAME[80];
+int    NVALID, *VALID_IP;
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+char   CameraLayout[256];
+SkyTable *ServerSky;
+
+/* used to select entries from header (gstars or parse_time) (KEEP) */
+char   DateKeyword[64];
+char   DateMode[64];
+char   UTKeyword[64];
+char   MJDKeyword[64];
+char   JDKeyword[64];
+char   ExptimeKeyword[64];
+char   AirmassKeyword[64];
+char   CCDNumKeyword[64];
+char   STKeyword[64];
+
+int    VERBOSE;
+int    MODE;
+int    FIX_NS;
+int    NMAX;
+int    NX_SUB, NY_SUB;
+double SCALE;
+double PADDING;
+
+double EULER_A;
+double EULER_B;
+
+void         SetProtect                PROTO((int mode));
+int          SetSignals                PROTO(());
+int          Shutdown                  PROTO((char *message, ...); ) 
+void         TrapSignal                PROTO((int sig));
+
+int 	     args_skycells 	       PROTO((int argc, char **argv));
+int 	     ConfigInit_skycells       PROTO((int *argc, char **argv));
+int 	     sky_tessalation 	       PROTO((FITS_DB *db, int level, int Nmax, int mode, double scale));
+int          sky_tessalation_init      PROTO((double scale));
+int 	     sky_tessalation_triangles PROTO((FITS_DB *db, int level, int Nmax));
+int 	     sky_tessalation_squares   PROTO((FITS_DB *db, int level, int Nmax));
+int 	     sky_triangle_to_image     PROTO((Image *image, SkyTriangle *triangle));
+int 	     sky_triangle_to_rectangle PROTO((SkyRectangle *image, SkyTriangle *triangle));
+int 	     sky_subdivide_image       PROTO((Image *output, SkyRectangle *input, int Nx, int Ny));
+int 	     sky_triangle_coords       PROTO((SkyTriangle *triangle));
+SkyTriangle *sky_divide_triangles      PROTO((SkyTriangle *in, int *ntriangles));
+SkyTriangle *sky_base_triangles        PROTO((int *ntriangles));
+int          sky_base_rotation         PROTO((SkyTriangle *base, int Nbase));
+Point        sky_divide_edge           PROTO((Point v1, Point v2));
+
+// XXX migrate to libdvo eventually
+int dvo_image_clear_vtable             PROTO((FITS_DB *db));
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/CheckPassword.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/CheckPassword.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/CheckPassword.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "addstar.h"
+
+int CheckPassword (int BindSocket) {
+
+  IOBuffer message;
+  int status;
+
+  status = ExpectCommand (BindSocket, strlen(PASSWORD), 0.1, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (strncmp (message.buffer, PASSWORD, strlen(PASSWORD))) {
+    if (VERBOSE) fprintf (stderr, "invalid password\n");
+    close (BindSocket);
+    return (FALSE);
+  }
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit.c	(revision 17422)
@@ -0,0 +1,185 @@
+# include "addstar.h"
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+
+AddstarClientOptions ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char *config, *file;
+  char RadiusWord[80];
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+  AddstarClientOptions options;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+
+  /* exclude overscan region from the dB image boundaries */
+  XOVERSCAN = YOVERSCAN = 0;
+  ScanConfig (config, "XOVERSCAN",              "%d",  0, &XOVERSCAN);
+  ScanConfig (config, "YOVERSCAN",              "%d",  0, &YOVERSCAN);
+
+  /* only upload stars within region; a value of 0 means ignore the limit */
+  XMIN = XMAX = YMIN = YMAX = 0;
+  ScanConfig (config, "ADDSTAR_XMIN",           "%d",  0, &XMIN);
+  ScanConfig (config, "ADDSTAR_XMAX",           "%d",  0, &XMAX);
+  ScanConfig (config, "ADDSTAR_YMIN",           "%d",  0, &YMIN);
+  ScanConfig (config, "ADDSTAR_YMAX",           "%d",  0, &YMAX);
+
+  /* exclude stars with SN > SNLIMIT (ADDSTAR_SNLIMIT overrides old name MIN_SN_FSTAT) */
+  SNLIMIT = 0;
+  ScanConfig (config, "MIN_SN_FSTAT",           "%lf", 0, &SNLIMIT);
+  ScanConfig (config, "ADDSTAR_SNLIMIT",        "%lf", 0, &SNLIMIT);
+
+  MAX_CERROR = 0.5; // arcseconds
+  ScanConfig (config, "ADDSTAR_MAX_CERROR",     "%lf", 0, &MAX_CERROR);
+
+  /* used by parse_time to find time-related keywords */
+  strcpy (DateKeyword, "NONE");
+  strcpy (DateMode, "NONE");
+  strcpy (UTKeyword, "NONE");
+  strcpy (JDKeyword, "NONE");
+  strcpy (MJDKeyword, "NONE");
+  ScanConfig (config, "DATE-KEYWORD",           "%s",  0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",              "%s",  0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",             "%s",  0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",            "%s",  0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",             "%s",  0, JDKeyword);
+
+  ScanConfig (config, "EXPTIME-KEYWORD",        "%s",  0, ExptimeKeyword);
+  ScanConfig (config, "AIRMASS-KEYWORD",        "%s",  0, AirmassKeyword);
+  ScanConfig (config, "CCDNUM-KEYWORD",         "%s",  0, CCDNumKeyword);
+  ScanConfig (config, "ST-KEYWORD",             "%s",  0, STKeyword);
+  ScanConfig (config, "OBSERVATORY-LATITUDE",   "%lf", 0, &Latitude);
+  ScanConfig (config, "OBSERVATORY-LONGITUDE",  "%lf", 0, &Longitude);
+  ScanConfig (config, "SUBPIX_DATAFILE",        "%s",  0, SubpixDatafile);
+
+  if (!ScanConfig (config, "EXTNAME-KEYWORD",        "%s",  0, ExtnameKeyword)) {
+      strcpy (ExtnameKeyword, "EXTNAME"); 
+  }
+
+  /* instrumental magnitude range for calibration mode */
+  CAL_INSTMAG_MAX =  -9.0;
+  CAL_INSTMAG_MIN = -13.0;
+  ScanConfig (config, "CAL_INSTMAG_MAX",        "%lf", 0, &CAL_INSTMAG_MAX);
+  ScanConfig (config, "CAL_INSTMAG_MIN",        "%lf", 0, &CAL_INSTMAG_MIN);
+
+  /* location of needed data sources */
+  ScanConfig (config, "2MASS_DIR_AS",           "%s",  0, TWO_MASS_DIR_AS);
+  ScanConfig (config, "2MASS_DIR_DR2",          "%s",  0, TWO_MASS_DIR_DR2);
+  ScanConfig (config, "GSCDIR",                 "%s",  0, GSCDIR);
+
+  if (!ScanConfig (config, "USNO_A_DIR",        "%s",  0, USNO_A_DIR)) {
+    ScanConfig (config, "USNO_CDROM",           "%s",  0, USNO_A_DIR);
+  }
+  ScanConfig (config, "USNO_B_DIR",             "%s",  0, USNO_B_DIR);
+
+  ScanConfig (config, "TYCHO_DIR",             	"%s",  0, TYCHO_DIR);
+
+  GetConfig (config, "GSCFILE",                	"%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = SKY_DEPTH_HST;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  GetConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+  SetZeroPoint (ZERO_POINT);
+
+  if (!ScanConfig (config, "IMAGE_TABLE",       "%s",  0, ImageCat)) {
+    if (!ScanConfig (config, "IMAGE_CATALOG",   "%s",  0, ImageCat)) {
+      sprintf (ImageCat, "%s/Images.dat", CATDIR);
+    }
+  }
+
+  ScanConfig (config, "CAMERA_LAYOUT",          "%s",  0, CameraLayout);
+
+  /* used by client/server setup */
+  ScanConfig (config, "PASSWORD",               "%s",  0, PASSWORD);
+  ScanConfig (config, "HOSTNAME",               "%s",  0, HOSTNAME);
+  
+  /* load valid ip list */
+  {
+    int i, Nvalid, ip1, ip2, ip3, ip4, test, status;
+    char string[80];
+
+    Nvalid = 0;
+    NVALID = 10;
+    ALLOCATE (VALID_IP, int, NVALID);
+    for (i = 0; ScanConfig (config, "VALID_IP", "%s", i, string) != NULL; i++) {
+      status = sscanf (string, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+      test = TRUE;
+      test &= (status == 4);
+      test &= ((ip1 > 0) && (ip1 < 256)); 
+      test &= ((ip2 > 0) && (ip2 < 256)); 
+      test &= ((ip3 > 0) && (ip3 < 256)); 
+      test &= ((ip4 >=0) && (ip4 < 256)); 
+      if (!test) {
+	fprintf (stderr, "invalid IP address %s\n", string);
+	exit (2);
+      }
+      VALID_IP[Nvalid] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+      Nvalid ++;
+      CHECK_REALLOCATE (VALID_IP, int, NVALID, Nvalid, 10);
+    }
+    NVALID = Nvalid;
+    REALLOCATE (VALID_IP, int, NVALID);
+    if (NVALID == 0) {
+      free (VALID_IP);
+      VALID_IP = NULL;
+    }
+  }
+
+  /* set the default search radius */
+  if (!ScanConfig (config, "ADDSTAR_RADIUS", "%s", 0, RadiusWord)) {
+    GetConfig (config, "RADIUS", "%s", 0, RadiusWord);
+  }
+  /* XXX this does not work for refcat and reflist modes... */
+  if (!strcasecmp (RadiusWord, "header")) {
+    options.radius = 0;
+    if (!ScanConfig (config, "ADDSTAR_NSIGMA", "%lf", 0, &options.Nsigma)) {
+      GetConfig (config, "NSIGMA", "%lf", 0, &options.Nsigma);
+    }
+  } else {
+    options.radius = atof (RadiusWord);
+  }
+
+  /* default mode, format, if not specified */
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+  return (options);
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit_skycells.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ConfigInit_skycells.c	(revision 17422)
@@ -0,0 +1,77 @@
+# include "skycells.h"
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr);
+
+int ConfigInit_skycells (int *argc, char **argv) {
+
+  char *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  /* used by parse_time to find time-related keywords */
+  strcpy (DateKeyword, "NONE");
+  strcpy (DateMode, "NONE");
+  strcpy (UTKeyword, "NONE");
+  strcpy (JDKeyword, "NONE");
+  strcpy (MJDKeyword, "NONE");
+  ScanConfig (config, "DATE-KEYWORD",           "%s",  0, DateKeyword);
+  ScanConfig (config, "DATE-MODE",              "%s",  0, DateMode);
+  ScanConfig (config, "UT-KEYWORD",             "%s",  0, UTKeyword);
+  ScanConfig (config, "MJD-KEYWORD",            "%s",  0, MJDKeyword);
+  ScanConfig (config, "JD-KEYWORD",             "%s",  0, JDKeyword);
+
+  ScanConfig (config, "EXPTIME-KEYWORD",        "%s",  0, ExptimeKeyword);
+  ScanConfig (config, "AIRMASS-KEYWORD",        "%s",  0, AirmassKeyword);
+  ScanConfig (config, "CCDNUM-KEYWORD",         "%s",  0, CCDNumKeyword);
+  ScanConfig (config, "ST-KEYWORD",             "%s",  0, STKeyword);
+
+  GetConfig (config, "GSCFILE",                	"%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 	"%s",  0, CATDIR);
+  GetConfig (config, "PHOTCODE_FILE",          	"%s",  0, MasterPhotcodeFile);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = SKY_DEPTH_HST;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  /* default mode, format, if not specified */
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+
+  free (config);
+  free (file);
+  return (TRUE);
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/DatasetOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/DatasetOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/DatasetOps.c	(revision 17422)
@@ -0,0 +1,70 @@
+# include "addstar.h"
+# include <pthread.h>
+
+/* the init function is an alternative */
+/* pthread_mutex_init(&mutex, const pthread_mutexattr_t *mutexattr); */
+pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int Ndataset = 0;
+static int NDATASET = 0;
+static DVO_DATA **dataset = NULL;
+
+int InitDataset () {
+
+  int i;
+
+
+  Ndataset = 0;
+  NDATASET = 100;
+  ALLOCATE (dataset, DVO_DATA *, NDATASET);
+  for (i = 0; i < NDATASET; i++) {
+    dataset[i] = NULL;
+  }
+  return (TRUE);
+}
+
+/* data a set dataset to the end of the stack */
+int PushDataset (DVO_DATA *data) {
+
+  int N;
+
+  pthread_mutex_lock(&fastmutex);
+
+  N = Ndataset;
+  Ndataset ++;
+  CHECK_REALLOCATE (dataset, DVO_DATA *, NDATASET, Ndataset, 100);
+
+  dataset[N] = data;
+
+  pthread_mutex_unlock(&fastmutex);
+  return (TRUE);
+}
+
+/* remove and return first dataset */
+DVO_DATA *PopDataset (void) {
+
+  int i;
+  DVO_DATA *data;
+
+  pthread_mutex_lock(&fastmutex);
+
+  if (Ndataset == 0) {
+    pthread_mutex_unlock(&fastmutex);
+    return (NULL);
+  }
+
+  data = dataset[0];
+  
+  for (i = 0; i < Ndataset - 1; i++) {
+    dataset[i] = dataset[i+1];
+  }
+  Ndataset --;
+  if ((Ndataset < NDATASET / 2) && (Ndataset > 50)) {
+    NDATASET = Ndataset / 2;
+    REALLOCATE (dataset, DVO_DATA *, NDATASET);
+  }
+
+  pthread_mutex_unlock(&fastmutex);
+  return (data);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/FilterStars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/FilterStars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/FilterStars.c	(revision 17422)
@@ -0,0 +1,103 @@
+# include "addstar.h"
+
+Stars *FilterStars (Stars *instars, Image *image, unsigned int imageID) {
+
+  int j, N;
+  float MTIME, dMs, dMx;
+  Stars *stars;
+  float RMIN, RMAX, DMIN, DMAX;
+
+  /* correct instrumental mags for exposure time */
+  MTIME = (image[0].exptime > 0) ? 2.500*log10(image[0].exptime) : 0.0;
+
+  RMIN = 360.0;
+  RMAX =   0.0;
+  DMIN = +90.0;
+  DMAX = -90.0;
+
+  /* modify resulting star list */
+  ALLOCATE (stars, Stars, image[0].nstar);
+  for (N = j = 0; j < image[0].nstar; j++) {
+    /* allow for some dynamic filtering of star list */
+    if (SNLIMIT && instars[j].dM > SNLIMIT) continue;
+    if (XMAX && (instars[j].X > XMAX)) continue;
+    if (XMIN && (instars[j].X < XMIN)) continue;
+    if (YMAX && (instars[j].Y > YMAX)) continue;
+    if (YMIN && (instars[j].Y < YMIN)) continue;
+    stars[N] = instars[j];
+
+    XY_to_RD (&stars[N].R, &stars[N].D, stars[N].X, stars[N].Y, &image[0].coords);
+    while (stars[N].R <    0.0) stars[N].R += 360.0;
+    while (stars[N].R >= 360.0) stars[N].R -= 360.0;
+    stars[N].found = -1;
+    stars[N].code = image[0].photcode;
+
+    RMIN = MIN (RMIN, stars[N].R);
+    RMAX = MAX (RMAX, stars[N].R);
+    DMIN = MIN (DMIN, stars[N].D);
+    DMAX = MAX (DMAX, stars[N].D);
+    /** additional quantities to supply to Stars based on the image data **/
+
+    /* calculate accurate per-star airmass */
+    stars[N].airmass = airmass (image[0].secz, stars[N].R, stars[N].D, image[0].sidtime, image[0].latitude);
+    stars[N].Mcal    = image[0].Mcal;
+    stars[N].t       = image[0].tzero + 1e-4*stars[N].Y*image[0].trate;  /* trate is in 0.1 msec / row */
+    stars[N].dt      = MTIME;
+
+    if ((stars[N].M > 25.0) && (stars[N].M < 32.0)) {
+      fprintf (stderr, "*");
+    }
+
+    // stars->M is either NAN or a valid inst magnitude
+    // stars->dM is either NAN or a valid error
+
+    dMs  = 0.0;
+    dMx = 0.0;
+    if (SUBPIX) {
+      dMs = get_subpix (stars[N].X, stars[N].Y);
+      dMx = scat_subpix (stars[N].X, stars[N].Y);
+      if (!isnan(stars[N].dM)) {
+	stars[N].dM = hypot (stars[N].dM, dMx);
+      }
+    }
+
+    if (!isnan(stars[N].M)) {
+      stars[N].M += MTIME - dMs;
+    }
+    if (!isnan(stars[N].Map)) {
+      stars[N].Map += MTIME - dMs;
+    }
+    
+    // XXX currently, this ID is internal only; 
+    // should we use the psphot / other external ID, if available?
+    stars[N].detID = N; // sequence number within image
+    stars[N].imageID = imageID; // does this need to be updated?
+
+    N ++;
+  }
+  image[0].nstar = N;
+  REALLOCATE (stars, Stars, image[0].nstar);
+  free (instars);
+
+  if (VERBOSE) fprintf (stderr, "read %d stars from target file\n", image[0].nstar);
+  if (VERBOSE) fprintf (stderr, "stars cover region %f,%f - %f,%f\n", RMIN, DMIN, RMAX, DMAX);
+  return (stars);
+}
+
+Stars *MergeStars (Stars *stars, int *Nstars, Stars *instars, int Ninstars) {
+
+  int i, j;
+
+  if (stars == NULL) {
+    ALLOCATE (stars, Stars, Ninstars);
+  } else {
+    REALLOCATE (stars, Stars, *Nstars + Ninstars);
+  }
+
+  for (j = 0, i = *Nstars; i < *Nstars + Ninstars; i++, j++) {
+    stars[i] = instars[j];
+  }
+  
+  *Nstars += Ninstars;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/GetFileMode.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/GetFileMode.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/GetFileMode.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "addstar.h"
+
+// examine the PHU of this file and determine the file mode
+int GetFileMode (Header *header) {
+
+  char ctype[80];
+  int Naxis, Nx, Ny;
+  int simple, extend, haveNaxis, haveNx, haveNy, haveCTYPE;
+
+  gfits_scan (header, "SIMPLE", "%t", 1, &simple);
+  haveNaxis = gfits_scan (header, "NAXIS",  "%d", 1, &Naxis);
+  haveNx    = gfits_scan (header, "NAXIS1", "%d", 1, &Nx);
+  haveNy    = gfits_scan (header, "NAXIS2", "%d", 1, &Ny);
+  haveCTYPE = gfits_scan (header, "CTYPE1", "%s", 1, ctype);
+
+  gfits_scan (header, "EXTEND", "%t", 1, &extend);
+    
+  { 
+    int tmp, haveCAMCOL, haveSTRIPE;
+    
+    // SDSS tsObj files have CAMCOL & STRIP keywords present in the header
+    haveCAMCOL = gfits_scan (header, "CAMCOL",  "%d", 1, &tmp);
+    haveSTRIPE = gfits_scan (header, "STRIPE",  "%d", 1, &tmp);
+    if (haveCAMCOL && haveSTRIPE) return SDSS_OBJ;
+  }
+
+  if ((Naxis == 2) || TEXTMODE || !simple) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMP;
+    }
+    return SIMPLE_CMP;
+  }
+
+  if (!extend && strcmp (&ctype[4], "-DIS")) {
+    if (!strcmp (&ctype[4], "-WRP")) {
+      return MOSAIC_CMF;
+    }
+    return SIMPLE_CMF;
+  }
+
+  if (!extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_PHU;
+  }
+
+  if (extend && strcmp (&ctype[4], "-DIS")) {
+    return SIMPLE_MEF;
+  }
+
+  if (extend && !strcmp (&ctype[4], "-DIS")) {
+    return MOSAIC_MEF;
+  }
+
+  return (NONE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ImageOptions.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ImageOptions.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ImageOptions.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "addstar.h"
+
+/* set specific options based on the image collection */
+int ImageOptions (AddstarClientOptions *options, Image *images, int Nimages) {
+
+  int i, equivPhotcode, consistent;
+  float maxError;
+  PhotCode *photcode;
+
+  // set the radius to the maximum error circle
+  if (options[0].radius == 0) {
+    maxError = 0;
+    for (i = 0; i < Nimages; i++) {
+      maxError = MAX (maxError, 0.02 * images[i].cerror);
+    }
+    options[0].radius = options[0].Nsigma * maxError;
+  }     
+
+  // check that all images have the same equiv photcode and save it
+  // XXX this is only used to allow use to calculate an average mag
+  // if we have mis-matched photcodes, leave this as 0;
+  options[0].photcode = 0;
+  equivPhotcode = 0;
+  consistent = TRUE;
+
+  for (i = 0; i < Nimages; i++) {
+    /* MOSAIC_PHU images do not have a photcode */
+    if (!strcmp (&images[0].coords.ctype[4], "-DIS")) continue;
+
+    photcode = GetPhotcodebyCode (images[i].photcode);
+
+    if (equivPhotcode) {
+      if (equivPhotcode != photcode[0].equiv) {
+	consistent = FALSE;
+	break;
+      }
+    } else {
+      equivPhotcode = photcode[0].equiv;
+    }
+  }
+  if (consistent) {
+    options[0].photcode = equivPhotcode;
+  }
+
+  options[0].imageID = 0;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ListenClients_Thread.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ListenClients_Thread.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ListenClients_Thread.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "addstar.h"
+
+/* wait for incoming messages from clients */
+void *ListenClients_Thread (void *data) {
+
+  int status, InitSocket, BindSocket;
+  SockAddress Address;
+  IOBuffer message;
+  
+  /* if we have multiple threads, each one creates its own socket? */
+  InitSocket = InitServerSocket (&Address);
+  
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address, VALID_IP, NVALID);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    /* accept command : XXX EAM : long-enough timeout? */
+    status = ExpectCommand (BindSocket, 5, 0.1, &message);
+    if (status != 0) {
+      if (VERBOSE) fprintf (stderr, "failed connection\n");
+      FreeIOBuffer (&message);
+      close (BindSocket);
+      continue;
+    }
+
+    /* message options */
+    if (!strcmp (message.buffer, "IMAGE")) {
+      fprintf (stderr, "Image\n");
+      NewImage_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFLS")) {
+      fprintf (stderr, "Reflist\n");
+      NewReflist_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFCT")) {
+      fprintf (stderr, "Refcat\n");
+      NewRefcat_Thread (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "EXIT")) {
+      /* need to send this signal to the main thread */
+      fprintf (stderr, "Exit\n");
+      exit (2);
+    }
+  }    
+  exit (1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadData.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadData.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadData.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "addstar.h"
+
+// XXX this function is somewhat specific to the elixir format output files 
+// it is capable of distinguishing several format versions defined for elixir/psphot
+
+// examine the header sets and set the Image entries for the the valid images
+int LoadData (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages) {
+
+  char *name;
+  int i, j, Nvalid, Nhead, Ndata, Nskip;
+  Stars *inStars;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 10;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid + 10;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // now run through the images, interpret the headers and read the stars
+  for (i = 0; i < Nimages; i++) {
+    Nhead = headerSets[i].extnum_head;
+
+    if (VERBOSE) fprintf (stderr, "reading header for %s (%s)\n", headerSets[i].exthead, headerSets[i].extdata);
+    if (!ReadImageHeader (headers[Nhead], &images[0][Nvalid], 0)) {
+      fprintf (stderr, "skipping %s\n", headerSets[i].exthead);
+      continue;
+    }
+    images[0][Nvalid].imageID = Nvalid;
+    images[0][Nvalid].externID = 0;
+    images[0][Nvalid].sourceID = 0;
+
+    // XXX EAM : I seemed to have dropped the ability to support TEXT (old-style cmp format files).
+    // I need to detect them here and load them with ReadStarsTEXT instead of calling the code
+    // below.
+
+    // XXX use something to set the chip name? EXTNAME?
+    if (!strcmp(headerSets[i].exthead, "PHU") && (Nimages == 1)) {
+      snprintf (images[0][Nvalid].name, 64, "%s", name);
+    } else {
+      snprintf (images[0][Nvalid].name, 64, "%s[%s]", name, headerSets[i].exthead);
+    }
+
+    // skip the table if there is no data segment (eg, mosaic WRP image)
+    if (!strcmp(headerSets[i].extdata, "NONE")) {
+      Nvalid++;
+      CHECK_REALLOCATE (images[0], Image, NVALID, Nvalid, 10);
+      continue;
+    }
+
+    // advance the pointer to the start of the corresponding table block
+    Ndata = headerSets[i].extnum_data;
+    Nskip = 0;
+    for (j = 0; j < Ndata; j++) {
+      Nskip += extsize[j];
+    }
+    fseek (f, Nskip, SEEK_SET); 
+	 
+    inStars = ReadStarsFITS (f, headers[Nhead], headers[Ndata], &images[0][Nvalid].nstar);
+    inStars = FilterStars (inStars, &images[0][Nvalid], Nvalid);
+    *stars = MergeStars (*stars, Nstars, inStars, images[0][Nvalid].nstar);
+    Nvalid++;
+    CHECK_REALLOCATE (images[0], Image, NVALID, Nvalid, 10);
+  }
+  free (name);
+  *nvalid = Nvalid;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataPMM.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataPMM.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataPMM.c	(revision 17422)
@@ -0,0 +1,445 @@
+# include "addstar.h"
+
+/* .asc files look like:
+0         1         2         3
+0123456789012345678901234567890123456789
+187.498117^  2.659253^21.06$
+187.498672^  2.713833^17.80$
+(^ = tab char)
+*/
+
+# define NLINE_ASC 10000
+# define NBYTE_ASC_TABLE 28
+# define RA_INDEX_ASC 0
+# define DEC_INDEX_ASC 11
+# define MAG_INDEX_ASC 22
+
+int LoadDataPMM (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars) {
+
+  char *name, *buffer;
+  int i, fd, Nbyte, Nline, Nvalid, code;
+  double ra, dec, mag, airmass, az, ZeroPoint, ZeroPt;
+  unsigned int Ninstars, NINSTARS;
+  Stars *inStars;
+  PhotCode *photcode;
+  gzFile gz;
+
+  double minR0, minR1, maxR0, maxR1, minD, maxD;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 1;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid ++;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // there is only one PMM image per file
+  if (VERBOSE) fprintf (stderr, "reading data for %s\n", file);
+
+  ZeroPt = GetZeroPoint();
+
+  // need to get the metadata from the PMM_CCD_TABLE
+  photcode = LoadMetadataPMM (name, &images[0][Nvalid]);
+  code = photcode[0].code;
+  ZeroPoint = 0.001*photcode[0].C;
+  // XXX NOTE : as of 2008.02.27, the zero point is still carried internally in millimags
+
+  ALLOCATE (buffer, char, NLINE_ASC*NBYTE_ASC_TABLE);
+
+  // use the following to get alt, az:
+  // altaz (&alt, &az, 15.0*images[N].sidtime - images[N].coords.crval1, images[N].coords.crval2, Latitude);
+  // these two can be calculated from HA and LATITUDE (need a table of observatory LAT)
+  airmass = 1.0;
+  az = 0.0;
+
+  NINSTARS = 10000;
+  ALLOCATE (inStars, Stars, NINSTARS);
+
+  minR0 = minR1 = 360.0;
+  maxR0 = maxR1 =   0.0;
+  minD = +90.0;
+  maxD = -90.0;
+
+  fd = fileno (f);
+  gz = gzdopen (dup(fd), "rb");
+
+  // read in a big chunk at a time, parse the lines assuming fixes line sizes and fields
+  Ninstars = 0;
+  while (1) {
+
+    Nbyte = gzread (gz, buffer, NLINE_ASC*NBYTE_ASC_TABLE);
+    if (Nbyte == 0) break;
+
+    assert (Nbyte % NBYTE_ASC_TABLE == 0);
+    Nline = Nbyte / NBYTE_ASC_TABLE;
+
+    for (i = 0; i < Nline; i++) {
+      // fscanf (f, "%lf %lf %lf", &ra, &dec, &mag) != EOF) {
+
+      dparse (&ra,  1, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&dec, 2, &buffer[i*NBYTE_ASC_TABLE]);
+      dparse (&mag, 3, &buffer[i*NBYTE_ASC_TABLE]);
+
+      memset (&inStars[Ninstars], 0, sizeof(Stars));
+
+      if (ra > 180) {
+	minR1 = MIN(minR1, ra);
+	maxR1 = MAX(maxR1, ra);
+      } else {
+	minR0 = MIN(minR0, ra);
+	maxR0 = MAX(maxR0, ra);
+      }
+      minD = MIN(minD, dec);
+      maxD = MAX(maxD, dec);
+
+      inStars[Ninstars].M       = mag - ZeroPoint + ZeroPt;
+      inStars[Ninstars].R       = ra;
+      inStars[Ninstars].D       = dec;
+      inStars[Ninstars].t       = images[0][0].tzero;
+      inStars[Ninstars].dt      = images[0][0].exptime;
+      inStars[Ninstars].code    = code;
+      inStars[Ninstars].airmass = airmass;
+      inStars[Ninstars].az      = az;
+      inStars[Ninstars].found   = -1; // found starts at -1 == not yet found
+      Ninstars++;
+      CHECK_REALLOCATE (inStars, Stars, NINSTARS, Ninstars, 10000);
+    }
+  }
+
+  fprintf (stderr, "ra ranges: %f - %f, %f - %f; dec ranges: %f - %f\n", minR0, maxR0, minR1, maxR1, minD, maxD);
+
+  images[0][0].nstar = Ninstars;
+  images[0][0].imageID = 0;
+
+  *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
+
+  free (inStars);
+  free (name);
+  *nvalid = Nvalid + 1;
+
+  gzclose (gz);
+  free (buffer);
+  return (TRUE);
+}
+
+# define NBYTE_PMM_TABLE 106
+# define FILE_ID_INDEX  44
+# define DATE_INDEX     52
+# define TIME_INDEX     62
+# define RA_INDEX       68
+# define DEC_INDEX      75
+# define EMULSION_INDEX 83
+# define FILTER_INDEX   89
+# define EXPTIME_INDEX  89
+
+// these are a guess...
+# define PLATE_NX 26500
+# define PLATE_NY 26500
+PhotCode *LoadMetadataPMM (char *datafile, Image *image) {
+
+  PhotCode *photcode;
+  char fileID[8], date[10], timestr[6], RA[7], DEC[8], emulsion[6], filter[7], EXPTIME[4];
+  char line[NBYTE_PMM_TABLE+1];
+  FILE *f;
+
+  if (!PMM_CCD_TABLE) abort ();
+
+  strncpy (fileID, datafile, 7);
+  fileID[7] = 0;
+
+  f = fopen (PMM_CCD_TABLE, "r");
+  if (f == NULL) {
+    fprintf (stderr, "unable to open PMM table: %s\n", PMM_CCD_TABLE);
+    exit (2);
+  }
+  
+  while (fread (line, 1, NBYTE_PMM_TABLE, f) == NBYTE_PMM_TABLE) {
+    line[NBYTE_PMM_TABLE] = 0;
+    
+    if (strncmp (fileID, &line[FILE_ID_INDEX], 7)) continue;
+    
+    strncpy (date, &line[DATE_INDEX], 9);
+    date[9] = 0;
+
+    strncpy (timestr, &line[TIME_INDEX], 5);
+    timestr[5] = 0;
+
+    strncpy (RA, &line[RA_INDEX], 6);
+    RA[6] = 0;
+
+    strncpy (DEC, &line[DEC_INDEX], 7);
+    DEC[7] = 0;
+
+    strncpy (emulsion, &line[EMULSION_INDEX], 5);
+    emulsion[5] = 0;
+
+    strncpy (filter, &line[FILTER_INDEX], 6);
+    filter[6] = 0;
+
+    strncpy (EXPTIME, &line[EXPTIME_INDEX], 3);
+    EXPTIME[3] = 0;
+
+    image[0].tzero   = pmm_date_to_sec (date, timestr);
+    image[0].exptime = atof(EXPTIME)*60.0;
+
+    photcode = pmm_get_photcode (emulsion, filter);
+    image[0].photcode = photcode[0].code;
+
+    // XXX for now, we define a totally fake coordinate system centered on the plate center
+    strcpy (image[0].coords.ctype, "RA---TAN");
+    
+    image[0].coords.crval1  = pmm_get_ra (RA);
+    image[0].coords.crval2  = pmm_get_dec (DEC);
+
+    coords_precess (&image[0].coords.crval1, &image[0].coords.crval2, 1950.0, 2000.0);
+
+    image[0].coords.crpix1 = 0.5*PLATE_NX;
+    image[0].coords.crpix2 = 0.5*PLATE_NY;
+    image[0].coords.cdelt1 = image[0].coords.cdelt2 = 0.9 / 3600.0;
+
+    image[0].coords.pc1_1 = 0.0;
+    image[0].coords.pc1_2 = 1.0;
+    image[0].coords.pc2_1 = 1.0;
+    image[0].coords.pc2_2 = 0.0;
+
+    image[0].coords.Npolyterms = 0;
+    memset (image[0].coords.polyterms, 0, 2*7*sizeof(float));
+
+    image[0].NX = PLATE_NX;
+    image[0].NY = PLATE_NY;
+
+    image[0].cerror = 0.0;
+ 
+    image[0].apmifit = 0.0;
+    image[0].dapmifit = 0.0;
+    image[0].detection_limit = 0.0; 
+    image[0].saturation_limit = 0.0;
+    image[0].fwhm_x = 0.0;
+    image[0].fwhm_y = 0.0;
+
+    // XXX need to determine long & lat for observatories
+    // jd = ohana_sec_to_jd (image[0].tzero);
+    // image[0].sidtime  = ohana_lst (jd, Longitude);
+    // image[0].latitude = Latitude;
+    // altaz (&alt, &az, 15.0*image[0].sidtime - image[0].coords.crval1, image[0].coords.crval2, Latitude);
+
+    image[0].trate = 0.0;
+    image[0].secz = 1.0;
+    image[0].ccdnum = 0;
+
+    // secz is in units milli-airmass
+    image[0].Mcal = 0.0;
+    image[0].Xm   = NAN_S_SHORT;
+    image[0].code = 0;
+
+    image[0].nstar = 0;
+  
+    image[0].imageID  = 0;
+    image[0].externID = 0;
+    image[0].sourceID = 0;
+
+    // save the filename
+    snprintf (image[0].name, 64, "%s", datafile);
+    return photcode;
+  }
+  fprintf (stderr, "failed to match image!\n");
+  abort ();
+}
+
+/* emulsion / filter combinations:
+   098 RED 
+   098-0 RED70 
+   098-0 RG630 
+   103AD MULTI 
+   103AD YEL3 
+   103AD YEL8 
+   103AE #12 
+   103AE AMB2 
+   103AE AMB3 
+   103AE AMB4 
+   103AE AMB5 
+   103AE AMB6 
+   103AE AMB7 
+   103AE AMB8 
+   103AE NONE 
+   103AE RED66 
+   103AE RED67 
+   103AE RED68 
+   103AE RED69 
+   103AE RED70 
+   103AE RED71 
+   103AE RED73 
+   103AE RG2444 
+   103AE RP2444 
+   103AO NONE 
+   IIIAF OG590 
+   IIIAF RG600 
+   IIIAF RG610 
+   IIIAF RG630 
+   IIIAJ GG358 
+IIIAJ GG385 
+IIIAJ GG395 
+IVN RG715 
+IVN RG9 
+IVN WR88A 
+*/
+
+// date in format DDMonYYYY
+// time in format HH:MM
+time_t pmm_date_to_sec (char *date, char *time) {
+  
+  time_t second;
+  double jd;
+  struct tm now;
+  char *p1, *p2;
+  
+  bzero (&now, sizeof(now));
+
+  p1 = date;
+  now.tm_mday = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+  
+  // month runs from 0 - 11
+  p1 = date + 2;
+  if (!strncasecmp (p1, "JAN", 3)) { now.tm_mon =  0; goto got_month; }
+  if (!strncasecmp (p1, "FEB", 3)) { now.tm_mon =  1; goto got_month; }
+  if (!strncasecmp (p1, "MAR", 3)) { now.tm_mon =  2; goto got_month; }
+  if (!strncasecmp (p1, "APR", 3)) { now.tm_mon =  3; goto got_month; }
+  if (!strncasecmp (p1, "MAY", 3)) { now.tm_mon =  4; goto got_month; }
+  if (!strncasecmp (p1, "JUN", 3)) { now.tm_mon =  5; goto got_month; }
+  if (!strncasecmp (p1, "JUL", 3)) { now.tm_mon =  6; goto got_month; }
+  if (!strncasecmp (p1, "AUG", 3)) { now.tm_mon =  7; goto got_month; }
+  if (!strncasecmp (p1, "SEP", 3)) { now.tm_mon =  8; goto got_month; }
+  if (!strncasecmp (p1, "OCT", 3)) { now.tm_mon =  9; goto got_month; }
+  if (!strncasecmp (p1, "NOV", 3)) { now.tm_mon = 10; goto got_month; }
+  if (!strncasecmp (p1, "DEC", 3)) { now.tm_mon = 11; goto got_month; }
+  fprintf (stderr, "error interpretting month: %s\n", date);
+  abort ();
+
+got_month:
+  p1 = date + 5;
+  now.tm_year = strtod (p1, &p2);
+  assert (p2 == p1 + 4);
+
+  p1 = time;
+  now.tm_hour = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  p1 = time + 3;
+  now.tm_min = strtod (p1, &p2);
+  assert (p2 == p1 + 2);
+
+  jd = now.tm_mday - 32075 + (int)(1461*(now.tm_year + 4800 + (int)(((now.tm_mon+1)-14)/12))/4)
+    + (int)(367*((now.tm_mon+1) - 2 - (int)(((now.tm_mon+1) - 14)/12)*12)/12)
+    - (int)(3*(int)((1900 + now.tm_year + 4900 + (int)(((now.tm_mon+1) - 14)/12))/100)/4) - 0.5;
+  
+  second = (jd - 2440587.5)*86400 + 3600.0*now.tm_hour + now.tm_min*60.0 + now.tm_sec;
+
+  return (second);
+}
+
+// RA in format HHMMSS
+double pmm_get_ra (char *RA) {
+  
+  char tmp[3];
+  double h, m, s, ra;
+  
+  strncpy (tmp, &RA[0], 2);
+  tmp[2] = 0;
+  h = atof (tmp);
+
+  strncpy (tmp, &RA[2], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &RA[4], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  ra = 15.0 * (h + m / 60.0 + s / 3600.0);
+  return (ra);
+}
+
+// DEC in format sDDMMSS
+double pmm_get_dec (char *DEC) {
+
+  char tmp[3];
+  double d, m, s, dec;
+
+  strncpy (tmp, &DEC[1], 2);
+  tmp[2] = 0;
+  d = atof (tmp);
+
+  strncpy (tmp, &DEC[3], 2);
+  tmp[2] = 0;
+  m = atof (tmp);
+
+  strncpy (tmp, &DEC[5], 2);
+  tmp[2] = 0;
+  s = atof (tmp);
+
+  dec = d + m / 60.0 + s / 3600.0;
+
+  if (tmp[0] == '-') dec *= -1.0;
+  return (dec);
+}
+
+PhotCode *pmm_get_photcode (char *emulsion, char *filter) {
+
+  PhotCode *photcode;
+  char codename[32];
+
+  /* emulsion / filter combinations */
+  if (!strcmp(emulsion, "098  ") && !strcmp(filter, "RED   ")) { strcpy (codename, "USNO.098.RED");      goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.098-0.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "098-0") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.098-0.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "MULTI ")) { strcpy (codename, "USNO.103AD.MULTI");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL3  ")) { strcpy (codename, "USNO.103AD.YEL3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AD") && !strcmp(filter, "YEL8  ")) { strcpy (codename, "USNO.103AD.YEL8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "#12   ")) { strcpy (codename, "USNO.103AE.#12");    goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB2  ")) { strcpy (codename, "USNO.103AE.AMB2");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB3  ")) { strcpy (codename, "USNO.103AE.AMB3");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB4  ")) { strcpy (codename, "USNO.103AE.AMB4");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB5  ")) { strcpy (codename, "USNO.103AE.AMB5");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB6  ")) { strcpy (codename, "USNO.103AE.AMB6");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB7  ")) { strcpy (codename, "USNO.103AE.AMB7");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "AMB8  ")) { strcpy (codename, "USNO.103AE.AMB8");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AE.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED66 ")) { strcpy (codename, "USNO.103AE.RED66");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED67 ")) { strcpy (codename, "USNO.103AE.RED67");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED68 ")) { strcpy (codename, "USNO.103AE.RED68");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED69 ")) { strcpy (codename, "USNO.103AE.RED69");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED70 ")) { strcpy (codename, "USNO.103AE.RED70");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED71 ")) { strcpy (codename, "USNO.103AE.RED71");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RED73 ")) { strcpy (codename, "USNO.103AE.RED73");  goto got_photcode; }
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RG2444")) { strcpy (codename, "USNO.103AE.RG2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AE") && !strcmp(filter, "RP2444")) { strcpy (codename, "USNO.103AE.RP2444"); goto got_photcode; } 
+  if (!strcmp(emulsion, "103AO") && !strcmp(filter, "NONE  ")) { strcpy (codename, "USNO.103AO.NONE");   goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "OG590 ")) { strcpy (codename, "USNO.IIIAF.OG590");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG600 ")) { strcpy (codename, "USNO.IIIAF.RG600");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG610 ")) { strcpy (codename, "USNO.IIIAF.RG610");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAF") && !strcmp(filter, "RG630 ")) { strcpy (codename, "USNO.IIIAF.RG630");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG358 ")) { strcpy (codename, "USNO.IIIAJ.GG358");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG385 ")) { strcpy (codename, "USNO.IIIAJ.GG385");  goto got_photcode; }
+  if (!strcmp(emulsion, "IIIAJ") && !strcmp(filter, "GG395 ")) { strcpy (codename, "USNO.IIIAJ.GG395");  goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG715 ")) { strcpy (codename, "USNO.IVN.RG715");    goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "RG9   ")) { strcpy (codename, "USNO.IVN.RG9");      goto got_photcode; }
+  if (!strcmp(emulsion, "IVN  ") && !strcmp(filter, "WR88A ")) { strcpy (codename, "USNO.IVN.WR88A");    goto got_photcode; }
+  fprintf (stderr, "error interpretting emulsion and filter: %s, %s\n", emulsion, filter);
+  abort ();
+
+got_photcode:
+  photcode = GetPhotcodebyName (codename);
+  if (photcode == NULL) {
+    fprintf (stderr, "unknown photcode %s\n", codename);
+    abort ();
+  }    
+
+  return photcode;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataSDSS.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadDataSDSS.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "addstar.h"
+
+// examine the header sets and set the Image entries for the the valid images
+// there should only be a single data set (phu + table) in this file
+// each SDSS data set corresponds to 5 images (ugriz)
+int LoadDataSDSS (FILE *f, char *file, Image **images, int *nvalid, Stars **stars, int *Nstars, Header **headers, int *extsize, HeaderSet *headerSets, int Nimages) {
+
+  char *name;
+  int j, Nvalid, Nhead, Ndata, Nskip;
+  unsigned int Ninstars;
+  Stars *inStars;
+
+  if (images[0] == NULL) {
+    Nvalid = 0;
+    NVALID = 5;
+    ALLOCATE (images[0], Image, NVALID);
+  } else {
+    Nvalid = *nvalid;
+    NVALID = Nvalid + 5;
+    REALLOCATE (images[0], Image, NVALID);
+  }    
+
+  // find image rootname
+  name = filebasename (file);
+
+  // there is only one SDSS image per file (TRUE?)
+  Nhead = headerSets[0].extnum_head;
+
+  // XXX parse the information needed from the PHU header
+  if (VERBOSE) fprintf (stderr, "reading header for %s (%s)\n", headerSets[0].exthead, headerSets[0].extdata);
+
+  // advance the pointer to the start of the corresponding table block
+  Ndata = headerSets[0].extnum_data;
+  Nskip = 0;
+  for (j = 0; j < Ndata; j++) {
+    Nskip += extsize[j];
+  }
+  fseek (f, Nskip, SEEK_SET); 
+	 
+  // XXX I think this is an error?  should this be &images[0][Nvalid] ??
+  inStars = ReadStarsSDSS (f, name, headers[Nhead], headers[Ndata], images[0], &Nvalid, &Ninstars);
+  *stars = MergeStars (*stars, Nstars, inStars, Ninstars);
+
+  free (name);
+  *nvalid = Nvalid;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadHeaders.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadHeaders.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadHeaders.c	(revision 17422)
@@ -0,0 +1,40 @@
+# include "addstar.h"
+
+// load all of the headers, jump in file to skip data segments
+Header **LoadHeaders (FILE *f, int *mode, int *Nheaders) {
+
+  int i, status, Nskip, NHEADERS;
+  Header **headers;
+
+  /* we need to examine the extensions to determine the headers and the data */
+  NHEADERS = 10;
+  ALLOCATE (headers, Header *, NHEADERS);
+
+  // load all headers into memory
+  for (i = 0;; i++) {
+    ALLOCATE (headers[i], Header, 1);
+    status = gfits_fread_header (f, headers[i]);
+    if (!status) { 
+      *Nheaders = i;
+      return (headers);
+    }
+
+    // check the mode for this file
+    if (i == 0) {
+      *mode = GetFileMode (headers[0]);
+      if ((*mode == SIMPLE_CMP) || (*mode == MOSAIC_CMP)) {
+	*Nheaders = i;
+	return (headers);
+      }
+    }
+
+    // advance to the next header
+    Nskip = gfits_data_size (headers[i]);
+    fseek (f, Nskip, SEEK_CUR); 
+    if (i == NHEADERS - 1) {
+      NHEADERS += 10;
+      REALLOCATE (headers, Header *, NHEADERS);
+    }
+  }
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadStars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadStars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/LoadStars.c	(revision 17422)
@@ -0,0 +1,118 @@
+# include "addstar.h"
+
+Stars *LoadStars (char *filename, int *Nstars, Image **images, int *Nimages, AddstarClientOptions *options) {
+
+  int i, Nfile, NFILE, Nheaders, NheaderSets, mode, *extsize;
+  char **file, line[1024];
+  FILE *f;
+  glob_t globList;
+  Header **headers;
+  Stars *stars;
+  HeaderSet *headerSets;
+
+  if (options[0].filelist) {
+    // read the list of input files from the supplied file
+    f = fopen (filename, "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read input list %s, giving up\n", filename);
+      exit (1);
+    }
+
+    NFILE = 10;
+    ALLOCATE (file, char *, NFILE);
+    for (i = 0; (fscanf (f, "%s", line) != EOF); i++) {
+      // filename limited to 1024 chars
+      fprintf (stderr, "file: %s\n", line);
+      file[i] = strcreate (line);
+      if (i == NFILE - 1) {
+	NFILE += 10;
+	REALLOCATE (file, char *, NFILE);
+      }
+    }
+    Nfile = i;
+  } else {
+    // parse the filename as a glob
+    globList.gl_offs = 0;
+    glob (filename, 0, NULL, &globList);
+
+    // if the glob does not match, save the literal word:
+    // otherwise save all glob matches
+    if (globList.gl_pathc == 0) {
+      Nfile = 1;
+      ALLOCATE (file, char *, Nfile);
+      file[0] = strcreate (filename);
+    } else {
+      Nfile = globList.gl_pathc;
+      ALLOCATE (file, char *, Nfile);
+      for (i = 0; i < Nfile; i++) {
+	file[i] = strcreate (globList.gl_pathv[i]);
+      }
+    }
+  }
+
+  *Nimages = 0;
+  *Nstars = 0;
+  *images = NULL;
+  stars = NULL;
+
+  for (i = 0; i < Nfile; i++) {
+    f = fopen (file[i], "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read file %s, skipping\n", file[i]);
+      continue;
+    }
+
+    if (PMM_CCD_TABLE != NULL) {
+      LoadDataPMM (f, file[i], images, Nimages, &stars, Nstars);
+      continue;
+    }
+
+    headers = LoadHeaders (f, &mode, &Nheaders);
+    headerSets = MatchHeaders (&extsize, &NheaderSets, mode, headers, Nheaders);
+    if (headerSets == NULL) {
+      fprintf (stderr, "ERROR: can't read headers for %s\n", file[i]);
+      continue;
+    }
+    if (NheaderSets == 0) {
+      fprintf (stderr, "no object data in file %s, skipping\n", file[i]);
+      continue;
+    }
+    if (VERBOSE) fprintf (stderr, "file %s has %d headers, including %d images\n", file[i], Nheaders, NheaderSets);
+
+    /* supplied photcode is incompatible with multi-chip images */
+    if ((NheaderSets > 1) && options[0].photcode) {
+      fprintf (stderr, "ERROR: photcode cannot be supplied to multi-chip images -- manually adjust the headers\n");
+      exit (1);
+    }
+
+    if (headerSets[0].exttype && !strcmp (headerSets[0].exttype, "SDSS_OBJ")) {
+      LoadDataSDSS (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+      continue;
+    }
+
+    LoadData (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+
+    // XXX add a function to (optionally) load the extended source measurements
+    # if (0)
+    if (extSources) {
+      // not sure how to link the measurements here to the psf measurements above (though there is an ID in the det list)
+      LoadDataXSRC (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+    }
+    if (extFits) {
+      LoadDataXFIT (f, file[i], images, Nimages, &stars, Nstars, headers, extsize, headerSets, NheaderSets);
+    }
+    # endif
+
+  }
+
+  if (*Nimages == 0) {
+    if (Nfile == 1) 
+      fprintf (stderr, "no valid image data in any of these files, giving up\n");
+    else 
+      fprintf (stderr, "no valid image data in this file, giving up\n");
+    exit (0);
+  }
+
+  return stars;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/MatchHeaders.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/MatchHeaders.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/MatchHeaders.c	(revision 17422)
@@ -0,0 +1,104 @@
+# include "addstar.h"
+
+// XXX largely psphot specific
+
+HeaderSet *MatchHeaders (int **extsize, int *nimage, int mode, Header **headers, int Nheaders) {
+
+  int i, j, Nimage, NIMAGE;
+  char extname[80], exttype[80], exthead[80];
+  HeaderSet *headerSets;
+
+  ALLOCATE (extsize[0], int, Nheaders);
+
+  Nimage = 0;
+  NIMAGE = 10;
+  ALLOCATE (headerSets, HeaderSet, NIMAGE);
+
+  // what is the mode of the first header (ie, do we have a PHU DIS image?)
+  mode = GetFileMode (headers[0]);
+
+  if (mode == MOSAIC_MEF) {
+    headerSets[Nimage].exthead     = strcreate ("PHU");
+    headerSets[Nimage].extdata     = strcreate ("NONE");
+    headerSets[Nimage].extnum_data = -1;
+    headerSets[Nimage].extnum_head =  0;
+    Nimage ++;
+  }
+
+  if (mode == SDSS_OBJ) {
+    // XXX these should have two headers (phu + table)
+    assert (Nheaders == 2);
+    headerSets[0].extdata     = strcreate ("SDSS_OBJ");
+    headerSets[0].exttype     = strcreate ("SDSS_OBJ");
+    headerSets[0].exthead     = strcreate ("PHU");
+    headerSets[0].extnum_head = 0;
+    headerSets[0].extnum_data = 1;
+    extsize[0][0] = headers[0][0].size;
+    *nimage = 1;
+    return headerSets;
+  }
+
+  // now examine the headers, count the table entries, find corresponding headers
+  for (i = 0; i < Nheaders; i++) {
+    if (mode == SIMPLE_CMP) {
+      extsize[0][i] = headers[i][0].size;
+    } else {
+      extsize[0][i] = headers[i][0].size + gfits_data_size (headers[i]);
+    }
+
+    gfits_scan (headers[i], "EXTTYPE", "%s", 1, exttype);
+    if (!strcmp (exttype, "SMPDATA")) goto keep;
+    if (!strcmp (exttype, "PS1_DEV_0")) goto keep;
+    if (!strcmp (exttype, "PS1_DEV_1")) goto keep;
+    continue;
+
+  keep:
+    headerSets[Nimage].exttype = strcreate (exttype);
+
+    gfits_scan (headers[i], ExtnameKeyword, "%s", 1, extname);
+    gfits_scan (headers[i], "EXTHEAD", "%s", 1, exthead);
+
+    headerSets[Nimage].extdata     = strcreate (extname);
+    headerSets[Nimage].exthead     = strcreate (exthead);
+    headerSets[Nimage].extnum_data = i;
+    headerSets[Nimage].extnum_head = -1;
+
+    // find the matching exthead entry
+    for (j = 0; j < Nheaders; j++) {
+      if (!gfits_scan (headers[j], ExtnameKeyword, "%s", 1, extname)) continue;
+      if (strcmp (extname, headerSets[Nimage].exthead)) continue;
+      headerSets[Nimage].extnum_head = j;
+      break;
+    }
+
+    // skip or crash on table with missing matching header?
+    if (headerSets[Nimage].extnum_head == -1) {
+      return NULL;
+    }
+    Nimage ++;
+    if (Nimage == NIMAGE) {
+      NIMAGE += 10;
+      REALLOCATE (headerSets, HeaderSet, NIMAGE);
+    }
+  }
+
+  // some old format files did not write EXTTYPE.  they have a single table in the first
+  // extension matched to the header in the PHU
+  if (Nimage == 0) {
+    extsize[0][0] = headers[0][0].size + gfits_data_size (headers[0]);
+    extsize[0][1] = headers[1][0].size + gfits_data_size (headers[1]);
+    gfits_scan (headers[1], ExtnameKeyword, "%s", 1, extname);
+    if (!strcmp (extname, "SMPFILE")) {
+      headerSets[Nimage].extdata     = strcreate (extname);
+      headerSets[Nimage].exttype     = strcreate ("SMPDATA");
+      headerSets[Nimage].exthead     = strcreate ("PHU");
+      headerSets[Nimage].extnum_head = 0;
+      headerSets[Nimage].extnum_data = 1;
+      Nimage = 1;
+    }
+  }
+  
+  *nimage = Nimage;
+  return (headerSets);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "addstar.h"
+
+int NewImage (int BindSocket) {
+
+  int N, Nstars, Nimages;
+  Stars *stars;
+  Image *images;
+  Coords *mosaic;
+  AddstarClientOptions *options;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Image (BindSocket, &images, &Nimages)) {
+    fprintf (stderr, "error: problem receiving image data\n");
+    goto reject;
+  }
+
+  if (options[0].mosaic) {
+    if (!Recv_Coords (BindSocket, &mosaic, &N)) {
+      fprintf (stderr, "error: problem receiving mosaic coordinates\n");
+      goto reject;
+    }
+    if (N != 1) {
+      fprintf (stderr, "error: invalid number of mosaic coords (%d)\n", N);
+      goto reject;
+    }
+  }    
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d images, %d stars\n", Nimages, Nstars);
+
+  /* add to db */
+  UpdateDatabase_Image (options, images, Nimages, mosaic, stars, Nstars);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage_Thread.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage_Thread.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewImage_Thread.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "addstar.h"
+
+int NewImage_Thread (int BindSocket) {
+
+  int N, Nstars, Nimages;
+  Stars *stars;
+  Image *images;
+  Coords *mosaic;
+  AddstarClientOptions *options;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Image (BindSocket, &images, &Nimages)) {
+    fprintf (stderr, "error: problem receiving image data\n");
+    goto reject;
+  }
+
+  if (options[0].mosaic) {
+    if (!Recv_Coords (BindSocket, &mosaic, &N)) {
+      fprintf (stderr, "error: problem receiving mosaic coordinates\n");
+      goto reject;
+    }
+    if (N != 1) {
+      fprintf (stderr, "error: invalid number of mosaic coords (%d)\n", N);
+      goto reject;
+    }
+  }    
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d, %d stars\n", Nimages, Nstars);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = NULL;
+  dataset[0].refcat  = NULL;
+  dataset[0].images  = images;
+  dataset[0].Nimages = Nimages;
+  dataset[0].mosaic  = mosaic;
+  dataset[0].stars   = stars;
+  dataset[0].Nstars  = Nstars;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  /* close connection, return */
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+
+int NewRefcat (int BindSocket) {
+
+  int N, status;
+  AddstarClientOptions *options;
+  IOBuffer message;
+  SkyRegion *patch;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_SkyRegion (BindSocket, &patch, &N)) {
+    fprintf (stderr, "error: problem receiving patch\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many patches (%d)\n", N);
+    goto reject;
+  }
+
+  status = ExpectMessage (BindSocket, 0.25, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    goto reject;
+  }
+
+  /* add to db */
+  UpdateDatabase_Refcat (options, patch, message.buffer);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat_Thread.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat_Thread.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewRefcat_Thread.c	(revision 17422)
@@ -0,0 +1,59 @@
+# include "addstar.h"
+
+int NewRefcat_Thread (int BindSocket) {
+
+  int N, status;
+  AddstarClientOptions *options;
+  IOBuffer message;
+  SkyRegion *patch;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_SkyRegion (BindSocket, &patch, &N)) {
+    fprintf (stderr, "error: problem receiving patch\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many patches (%d)\n", N);
+    goto reject;
+  }
+
+  status = ExpectMessage (BindSocket, 0.25, &message);
+  if (status != 0) {
+    if (VERBOSE) fprintf (stderr, "failed connection\n");
+    FreeIOBuffer (&message);
+    goto reject;
+  }
+
+  /* add to db */
+  UpdateDatabase_Refcat (options, patch, message.buffer);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = patch;
+  dataset[0].refcat  = message.buffer;
+  dataset[0].images  = NULL;
+  dataset[0].mosaic  = NULL;
+  dataset[0].stars   = NULL;
+  dataset[0].Nstars  = 0;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "addstar.h"
+
+int NewReflist (int BindSocket) {
+
+  int N, Nstars;
+  Stars *stars;
+  AddstarClientOptions *options;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d stars\n", Nstars);
+
+  /* add to db */
+  UpdateDatabase_Reflist (options, stars, Nstars);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist_Thread.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist_Thread.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/NewReflist_Thread.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "addstar.h"
+
+int NewReflist_Thread (int BindSocket) {
+
+  int N, Nstars;
+  Stars *stars;
+  AddstarClientOptions *options;
+  DVO_DATA *dataset;
+
+  /* accept incoming data set */
+  if (!Recv_AddstarClientOptions (BindSocket, &options, &N)) {
+    fprintf (stderr, "error: problem receiving options\n");
+    goto reject;
+  }
+  if (N != 1) {
+    fprintf (stderr, "error: too many option sets (%d)\n", N);
+    goto reject;
+  }
+
+  if (!Recv_Stars (BindSocket, &stars, &Nstars)) {
+    fprintf (stderr, "error: problem receiving star data\n");
+    goto reject;
+  }
+  fprintf (stderr, "accepted %d stars\n", Nstars);
+
+  /* create new dataset to store the incoming data */
+  ALLOCATE (dataset, DVO_DATA, 1);
+  dataset[0].options = options;
+  dataset[0].patch   = NULL;
+  dataset[0].refcat  = NULL;
+  dataset[0].images  = NULL;
+  dataset[0].mosaic  = NULL;
+  dataset[0].stars   = stars;
+  dataset[0].Nstars  = Nstars;
+
+  /* place on dataset stack */
+  PushDataset (dataset);
+
+  close (BindSocket);
+  return (TRUE);
+
+reject:
+  close (BindSocket);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadImageHeader.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadImageHeader.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadImageHeader.c	(revision 17422)
@@ -0,0 +1,171 @@
+# include "addstar.h"
+
+/* read an image header corresponding to a CMF / CMP data block */
+// XXX need to pass AddstarClientOptions?
+int ReadImageHeader (Header *header, Image *image, int photcode) {
+
+  int Nastro, ccdnum, hour, min, Nx, Ny;
+  double tmp, sec, Cerror, ZeroPt;
+  char *c, photname[64], line[80];
+
+  // zero out the entire image structure
+  memset (image, 0, sizeof(Image));
+
+  /* get astrometry information */
+  if (!GetCoords (&image[0].coords, header)) {
+    fprintf (stderr, "no astrometric solution in header\n");
+    return (FALSE);
+  }
+  // XXX currently, image uses an unsigned short for NX,XY. this is rather restrictive
+  // and needs to be at least checked.
+  gfits_scan (header, "NAXIS1",   "%d", 1, &Nx);
+  gfits_scan (header, "NAXIS2",   "%d", 1, &Ny);
+  if ((Nx < 0) || (Nx > 0xffff)) {
+    fprintf (stderr, "WARNING: NX, NY out of range : image boundary will be wrong\n");
+  }
+  image[0].NX = Nx;
+  image[0].NY = Ny;
+
+  if (!gfits_scan (header, "TZERO",   "%d",  1, &image[0].tzero) && !ACCEPT_TIME) {
+    image[0].tzero = parse_time (header);
+  }
+
+  /* only load astrometry, NAXIS1,2, and time if this is a MOSAIC_PHU (ctype is ....-DIS) */
+  if (!strcmp (&image[0].coords.ctype[4], "-DIS")) {
+    RegisterMosaic (&image[0].coords);
+    return (TRUE);
+  }
+
+  /* require Nastro > 0 unless or ACCEPT_ASTROM */
+  Nastro = 0;
+  gfits_scan (header, "NASTRO", "%d", 1, &Nastro);
+  Cerror = 0;
+  gfits_scan (header, "CERROR", "%lf", 1, &Cerror);
+  if (((Nastro == 0) || (Cerror > MAX_CERROR)) && !ACCEPT_ASTROM) {
+    fprintf (stderr, "bad astrometric solution in header\n");
+    return (FALSE);
+  }
+  if (!strcmp (&image[0].coords.ctype[4], "-WRP")) {
+    if (!isRegisteredMosaic()) {
+      fprintf (stderr, "no mosaic for WRP image (use -mosaic)\n");
+      return (FALSE);
+    }
+  } else {
+    /* force image to lie in 0-360 range */
+    while (image[0].coords.crval1 < 0) image[0].coords.crval1 += 360.0;
+    while (image[0].coords.crval1 > 360.0) image[0].coords.crval1 -= 360.0;
+  }
+
+  { 
+    double R, D;
+    /* sanity check on the image coordinates */
+    XY_to_RD (&R, &D, 0.5*Nx, 0.5*Ny, &image[0].coords);
+    if (!finite(R) || !finite(D)) {
+      fprintf (stderr, "corrupted header coordinates, skipping\n");
+      return (FALSE);
+    }
+  }
+    
+  /* CERROR in data file is in arcsec */
+  if (!gfits_scan (header, "CERROR",   "%lf", 1, &tmp)) tmp = 1.0;
+  image[0].cerror = tmp * 50.0;
+ 
+  /* get photcode from header */
+  if (photcode == 0) {
+    if (!gfits_scan (header, "PHOTCODE", "%s", 1, photname)) {
+      fprintf (stderr, "photcode not supplied in header\n");
+      return (FALSE);
+    }
+    photcode = GetPhotcodeCodebyName (photname);
+    if (photcode == 0) {
+      fprintf (stderr, "photcode %s not found in photcode table\n", photname);
+      return (FALSE);
+    }
+  }
+  if (photcode == 0) { 
+    fprintf (stderr, "no valid photcode is supplied\n");
+    return (FALSE);
+  }
+  image[0].photcode = photcode;
+
+  image[0].NX -= XOVERSCAN;
+  image[0].NY -= YOVERSCAN;
+  gfits_scan (header, ExptimeKeyword,  "%lf", 1, &tmp);
+  image[0].exptime = tmp;
+  
+  /*** why are we no longer using APMIFIT?? ***/
+  tmp = 0;
+  /* gfits_scan (header, "APMIFIT",  "%lf", 1, &tmp); */
+  image[0].apmifit = tmp;
+
+  tmp = 0;
+  /* gfits_scan (header, "dAPMIFIT", "%lf", 1, &tmp); */
+  image[0].dapmifit = tmp;
+
+  tmp = 0;
+  gfits_scan (header, "FLIMIT",   "%lf", 1, &tmp);
+  image[0].detection_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FSATUR",   "%lf", 1, &tmp);
+  image[0].saturation_limit = tmp * 10.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_X",   "%lf", 1, &tmp);
+  image[0].fwhm_x = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  tmp = 0;
+  gfits_scan (header, "FWHM_Y",   "%lf", 1, &tmp);
+  image[0].fwhm_y = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
+
+  if (STKeyword[0]) {
+    /* get ST (used for airmass calculation) */
+    gfits_scan (header, STKeyword, "%s", 1, line);
+    /* remove ':' characters */
+    for (c = strchr (line, ':'); c != (char *) NULL; c = strchr (line, ':')) { *c = ' '; }
+    sscanf (line, "%d %d %lf", &hour, &min, &sec);
+    image[0].sidtime = hour + min/60.0 + sec/3600.0;
+  } else {
+    double jd;
+    jd = ohana_sec_to_jd (image[0].tzero);
+    image[0].sidtime  = ohana_lst (jd, Longitude);
+  }
+  image[0].latitude = Latitude;
+
+  tmp = 0;
+  if (gfits_scan (header, "TRATE",   "%lf", 1, &tmp)) {
+    image[0].trate = 10000 * tmp;
+  } else {
+    image[0].trate = 0.0;
+  }
+
+  image[0].secz = NAN;
+  if (gfits_scan (header, AirmassKeyword, "%lf", 1, &tmp)) {
+    image[0].secz = tmp;
+  } 
+
+  if (!gfits_scan (header, CCDNumKeyword, "%d", 1, &ccdnum)) {
+    image[0].ccdnum = 0xff;
+  } else {
+    image[0].ccdnum = ccdnum;
+  }
+
+  gfits_scan (header, "ZERO_PT", "%lf", 1, &ZeroPt);
+  if (ZeroPt != GetZeroPoint()) {
+      fprintf (stderr, "inconsistent zero point values: image: %f, config: %f\n", ZeroPt, GetZeroPoint()); 
+      return (FALSE);
+  }
+
+  /* secz is in units milli-airmass */
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+
+  /* find expected number of stars */
+  if (!gfits_scan (header, "NSTARS", "%d", 1, &image[0].nstar) && !NO_STARS) {
+    fprintf (stderr, "can't get NSTARS from header\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadSDSSHeader.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadSDSSHeader.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadSDSSHeader.c	(revision 17422)
@@ -0,0 +1,58 @@
+# include "addstar.h"
+
+// determine relevant image information from the PHU header
+int ReadSDSSHeader (Header *header, Image *image, int photcode) {
+
+  int Nastro, ccdnum, hour, min, Nx, Ny;
+  double tmp, sec, Cerror, ZeroPt;
+  char *c, photname[64], line[80];
+
+  // I need to convert a single obj file into a set of 5 images
+
+  // XXX how do I define the image boundaries?
+  // image[0].coords
+  // image[0].NX, NY
+
+  // image[0].tzero : MJD_U,G,R,I,Z in table header
+
+  // test astrometry quality? 
+  // image[0].cerror : ??
+ 
+  // set photcodes for the 5 images (SDSS_U,G,R,I,Z)
+  photcode = GetPhotcodeCodebyName ("SDSS_U");
+  if (photcode == 0) {
+    fprintf (stderr, "photcode %s not found in photcode table\n", photname);
+    return (FALSE);
+  }
+  image[0].photcode = photcode;
+
+  // calculate this from : C_OBS, TRACKING, and NY
+  image[0].exptime = tmp;
+  
+  // image[0].apmifit = tmp;
+  // image[0].dapmifit = tmp;
+  // image[0].detection_limit 
+  // image[0].saturation_limit
+  // image[0].fwhm_x : SEEING_U, etc in table header
+  // image[0].fwhm_y : SEEING_U, etc in table header
+
+  // XXX longitude and latitude are known for SDSS
+  // jd = ohana_sec_to_jd (image[0].tzero);
+  // image[0].sidtime  = ohana_lst (jd, Longitude);
+  // image[0].latitude = Latitude;
+
+  // image[0].trate : from C_OBS
+  // image[0].secz : ??
+  // image[0].ccdnum : COLNUM?
+
+  // secz is in units milli-airmass
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+  memset (image[0].dummy, 0, sizeof(image[0].dummy));
+
+  // NAXIS2 for table:
+  // image[0].nstar
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsFITS.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 17422)
@@ -0,0 +1,228 @@
+# include "addstar.h"
+
+// given a file with the pointer at the start of the table block and the 
+// corresponding image header, load the stars from the table
+Stars *ReadStarsFITS (FILE *f, Header *header, Header *in_theader, unsigned int *nstars) {
+
+  int Nskip, Nstars;
+  char type[80];
+  Header theader;
+  FTable table;
+  Stars *stars;
+  
+  if (in_theader == NULL) {
+    table.header = &theader;
+    if (!gfits_fread_header (f, table.header)) Shutdown ("ERROR: can't read table header");
+  } else {
+    table.header = in_theader;
+    Nskip = in_theader[0].size;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+
+  /* load the table data */
+  if (!gfits_fread_ftable_data (f, &table)) {
+    fprintf (stderr, "ERROR: can't read table header\n");
+    exit (1);
+  }
+
+  if (!gfits_scan (table.header, "EXTTYPE", "%s", 1, type)) {
+    strcpy (type, "SMPDATA");
+  }
+
+  stars = NULL;
+  if (!strcmp (type, "SMPDATA")) {
+    stars = Convert_SMPDATA (&table, &Nstars);
+  }
+  if (!strcmp (type, "PS1_DEV_0")) {
+    stars = Convert_PS1_DEV_0 (&table, &Nstars);
+  }
+  if (!strcmp (type, "PS1_DEV_1")) {
+    stars = Convert_PS1_DEV_1 (&table, &Nstars);
+  }
+  if (stars == NULL) {
+    fprintf (stderr, "ERROR: invalid table type %s\n", type);
+    exit (1);
+  }
+  // Nstars is not necessarily == *nstars (The former is the number of detections, the
+  // latter are the 'good' detections reported by the photometry system.
+  *nstars = Nstars;
+
+  return stars;
+}
+
+Stars *Convert_SMPDATA (FTable *table, int *nstars) {
+
+  int i, Nstars, swapped;
+  double ZeroPt;
+  Stars *stars = NULL;
+  SMPData *smpdata = NULL;
+
+  swapped = FALSE;
+  smpdata = gfits_table_get_SMPData (table, &Nstars, &swapped);
+  ZeroPt = GetZeroPoint();
+
+  /* XXX we need to check at least the size of the loaded table */
+  // XXX use memset to zero-out the elements before setting
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X      = smpdata[i].X;
+    stars[i].Y      = smpdata[i].Y;
+
+    if ((smpdata[i].M >= ZeroPt) || isnan(smpdata[i].M)) {
+      stars[i].M    = NAN;
+      stars[i].Map  = NAN;
+    } else {
+      stars[i].M    = smpdata[i].M;
+      stars[i].Map  = smpdata[i].M;
+    }
+
+    stars[i].dM     = smpdata[i].dM*0.001;
+    stars[i].dophot = smpdata[i].dophot;
+
+    stars[i].fx     = smpdata[i].fx;
+    stars[i].fy     = smpdata[i].fy;
+    stars[i].df     = smpdata[i].df;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
+
+Stars *Convert_PS1_DEV_0 (FTable *table, int *nstars) {
+
+  int i, Nstars;
+  double ZeroPt;
+  Stars *stars;
+  PS1_DEV_0 *ps1data;
+
+  ps1data = gfits_table_get_PS1_DEV_0 (table, &Nstars, NULL);
+  ZeroPt = GetZeroPoint();
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X       = ps1data[i].X;
+    stars[i].Y       = ps1data[i].Y;
+    stars[i].dX      = ps1data[i].dX;
+    stars[i].dY      = ps1data[i].dY;
+    if ((ps1data[i].M >= 0.0) || isnan(ps1data[i].M)) {
+      stars[i].M     = NAN;
+    } else {
+      stars[i].M     = ps1data[i].M + ZeroPt;
+    }
+    stars[i].dM      = ps1data[i].dM;
+    stars[i].Mpeak   = ps1data[i].Mpeak;
+
+    stars[i].sky     = ps1data[i].sky;
+    stars[i].dsky    = ps1data[i].dSky;
+
+    stars[i].fx      = ps1data[i].fx;
+    stars[i].fy      = ps1data[i].fy;
+    stars[i].df      = ps1data[i].df;
+
+    stars[i].psfChisq = ps1data[i].psfChisq;
+    stars[i].psfQual  = ps1data[i].psfQual;
+
+    stars[i].detID   = ps1data[i].detID;
+
+    /* these are set elsewhere */
+    stars[i].R       = 0.0;
+    stars[i].D       = 0.0;
+    stars[i].dR      = 0.0;
+    stars[i].dD      = 0.0;
+
+    stars[i].uR      = 0.0;
+    stars[i].uD      = 0.0;
+    stars[i].duR     = 0.0;
+    stars[i].duD     = 0.0;
+
+    stars[i].P       = 0.0;
+    stars[i].dP      = 0.0;
+
+    stars[i].Mcal    = 0;
+    stars[i].t       = 0;
+    stars[i].dt      = 0;
+    stars[i].airmass = 0;
+    stars[i].code    = 0;
+    stars[i].found   = 0;
+
+    /* these are not used */
+    stars[i].Map     = NAN;
+    stars[i].dophot  = 0;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
+
+// XXX I need to make the IPP I/O functions and these functions
+// consistent wrt ZERO_POINT....
+Stars *Convert_PS1_DEV_1 (FTable *table, int *nstars) {
+
+  int i, Nstars;
+  double ZeroPt;
+  Stars *stars;
+  PS1_DEV_1 *ps1data;
+
+  ps1data = gfits_table_get_PS1_DEV_1 (table, &Nstars, NULL);
+  ZeroPt = GetZeroPoint();
+
+  ALLOCATE (stars, Stars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X       = ps1data[i].X;
+    stars[i].Y       = ps1data[i].Y;
+    stars[i].dX      = ps1data[i].dX;
+    stars[i].dY      = ps1data[i].dY;
+    if ((ps1data[i].M >= 0.0) || isnan(ps1data[i].M)) {
+	stars[i].M   = NAN;
+    } else {
+	stars[i].M   = ps1data[i].M + ZeroPt;
+    }
+    stars[i].dM      = ps1data[i].dM;
+    stars[i].Mpeak   = ps1data[i].Mpeak;
+
+    stars[i].sky     = ps1data[i].sky;
+    stars[i].dsky    = ps1data[i].dSky;
+
+    stars[i].fx      = ps1data[i].fx;
+    stars[i].fy      = ps1data[i].fy;
+    stars[i].df      = ps1data[i].df;
+
+    stars[i].psfChisq  = ps1data[i].psfChisq;
+    stars[i].psfQual   = ps1data[i].psfQual;
+    stars[i].crNsigma  = ps1data[i].crNsigma;
+    stars[i].extNsigma = ps1data[i].extNsigma;
+
+    stars[i].detID     = ps1data[i].detID;
+    stars[i].flags     = ps1data[i].flags;
+
+    // XXX not defined anyway
+    // stars[i].stargal   = ps1data[i].stargal;
+
+    /* these are set elsewhere */
+    stars[i].R       = 0.0;
+    stars[i].D       = 0.0;
+    stars[i].dR      = 0.0;
+    stars[i].dD      = 0.0;
+
+    stars[i].uR      = 0.0;
+    stars[i].uD      = 0.0;
+    stars[i].duR     = 0.0;
+    stars[i].duD     = 0.0;
+
+    stars[i].P       = 0.0;
+    stars[i].dP      = 0.0;
+
+    stars[i].Mcal    = 0;
+    stars[i].t       = 0;
+    stars[i].dt      = 0;
+    stars[i].airmass = 0;
+    stars[i].az      = 0;
+    stars[i].code    = 0;
+    stars[i].found   = 0;
+
+    /* these are not used */
+    stars[i].Map     = NAN;
+    stars[i].dophot  = 0;
+  }    
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsSDSS.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 17422)
@@ -0,0 +1,310 @@
+# include "addstar.h"
+int SetSDSSFlags (Stars *star, unsigned int flags1, unsigned int flags2);
+
+# define NFILTER 5
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+# define GET_COLUMN_5(NAME,TYPE) \
+  TYPE *NAME; \
+  NAME = (TYPE *) gfits_get_bintable_column_data (table.header, &table, #NAME, type, &Nrow, &Ncol); \
+  assert (NAME); assert (!strcmp (type, #TYPE)); assert (Nrow == Nstars); assert (Ncol == NFILTER);
+
+# define GET_COLUMN_1(NAME,TYPE) \
+  TYPE *NAME; \
+  NAME = (TYPE *) gfits_get_bintable_column_data (table.header, &table, #NAME, type, &Nrow, &Ncol); \
+  assert (NAME); assert (!strcmp (type, #TYPE)); assert (Nrow == Nstars); assert (Ncol == 1);
+
+/* grab named photcode */
+# define NAMED_PHOTCODE_AND_ZP(CODE,ZP,NAME) { \
+  PhotCode *code; \
+  code = GetPhotcodebyName (NAME); \
+  if (code == NULL) { \
+    fprintf (stderr, "ERROR:  photcode %s not found in photcode table\n", NAME); \
+    exit (0); } \
+  CODE = code[0].code; \
+  ZP = 0.001*code[0].C; }
+
+// XXX NOTE : as of 2008.02.27, the zero point is still carried internally in millimags
+
+// given a file with the pointer at the start of the table block and the 
+// corresponding image header, load the stars from the table
+Stars *ReadStarsSDSS (FILE *f, char *name, Header *header, Header *in_theader, Image *images, int *nimages, unsigned int *nstars) {
+
+  int i, j, N, Nskip, Nstars, camcol;
+  char type[80];
+  Header theader;
+  FTable table;
+  Stars *stars;
+  double clockRate, mjd[5], jd, sidtime, alt, az;
+  float seeing[5], photErr[5], zeropt[5], ZeroPt;
+  time_t tzero[5];
+  char filtname[16][5];
+  int photcode[5];
+  int Nrow, Ncol; // used in the GET_COLUMN_1,5 macros above
+  
+  if (in_theader == NULL) {
+    table.header = &theader;
+    if (!gfits_fread_header (f, table.header)) Shutdown ("ERROR: can't read table header");
+  } else {
+    table.header = in_theader;
+    Nskip = in_theader[0].size;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+
+  /* load the table data */
+  if (!gfits_fread_ftable_data (f, &table)) {
+    fprintf (stderr, "ERROR: can't read table header\n");
+    exit (1);
+  }
+
+  strcpy (filtname[0], "u");
+  strcpy (filtname[1], "g");
+  strcpy (filtname[2], "r");
+  strcpy (filtname[3], "i");
+  strcpy (filtname[4], "z");
+
+  NAMED_PHOTCODE_AND_ZP (photcode[0], zeropt[0], "U_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[1], zeropt[1], "G_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[2], zeropt[2], "R_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[3], zeropt[3], "I_SDSS");
+  NAMED_PHOTCODE_AND_ZP (photcode[4], zeropt[4], "Z_SDSS");
+
+  // various header values needed to calculate per-star data below
+  gfits_scan (header, "C_OBS", "%lf", 1, &clockRate); // value in header is usec / unbinned row
+  clockRate *= 1e-6; // convert to seconds / unbinned row
+
+  gfits_scan (table.header, "MJD_U", "%lf", 1, &mjd[0]);
+  gfits_scan (table.header, "MJD_G", "%lf", 1, &mjd[1]);
+  gfits_scan (table.header, "MJD_R", "%lf", 1, &mjd[2]);
+  gfits_scan (table.header, "MJD_I", "%lf", 1, &mjd[3]);
+  gfits_scan (table.header, "MJD_Z", "%lf", 1, &mjd[4]);
+  tzero[0] = ohana_mjd_to_sec (mjd[0]);
+  tzero[1] = ohana_mjd_to_sec (mjd[1]);
+  tzero[2] = ohana_mjd_to_sec (mjd[2]);
+  tzero[3] = ohana_mjd_to_sec (mjd[3]);
+  tzero[4] = ohana_mjd_to_sec (mjd[4]);
+
+  gfits_scan (table.header, "SEEING_U", "%f", 1, &seeing[0]);
+  gfits_scan (table.header, "SEEING_G", "%f", 1, &seeing[1]);
+  gfits_scan (table.header, "SEEING_R", "%f", 1, &seeing[2]);
+  gfits_scan (table.header, "SEEING_I", "%f", 1, &seeing[3]);
+  gfits_scan (table.header, "SEEING_Z", "%f", 1, &seeing[4]);
+
+  gfits_scan (table.header, "PSFERR_U", "%f", 1, &photErr[0]);
+  gfits_scan (table.header, "PSFERR_G", "%f", 1, &photErr[1]);
+  gfits_scan (table.header, "PSFERR_R", "%f", 1, &photErr[2]);
+  gfits_scan (table.header, "PSFERR_I", "%f", 1, &photErr[3]);
+  gfits_scan (table.header, "PSFERR_Z", "%f", 1, &photErr[4]);
+
+  gfits_scan (header, "CAMCOL", "%d", 1, &camcol); // value in header is usec / unbinned row
+
+  ZeroPt = GetZeroPoint();
+
+  // create a Star entry for each filter and detection
+  Nstars = table.header[0].Naxis[1];
+  ALLOCATE (stars, Stars, NFILTER*Nstars);
+
+  GET_COLUMN_5 (rowc, float);
+  GET_COLUMN_5 (colc, float);
+  GET_COLUMN_5 (sky, float);
+  GET_COLUMN_5 (psfCounts, float);
+  GET_COLUMN_5 (fiberCounts, float);
+  GET_COLUMN_5 (offsetRa, float);
+  GET_COLUMN_5 (offsetDec, float);
+  GET_COLUMN_5 (flags, int);
+  GET_COLUMN_5 (flags2, int);
+
+  GET_COLUMN_5 (prob_psf, float);
+
+  GET_COLUMN_1 (ra, double);
+  GET_COLUMN_1 (dec, double);
+
+  GET_COLUMN_5 (rowcErr, float);
+  GET_COLUMN_5 (colcErr, float);
+  GET_COLUMN_5 (skyErr, float);
+  GET_COLUMN_5 (psfCountsErr, float);
+
+  // the value of stars[].M is supposed to be the instrumental magnitude offset by the
+  // default zero point 25.0 (-2.5*log_10(counts/sec) + ZeroPt).  The magnitude reported
+  // by SDSS is the calibrated mag: -2.5*log_10(counts/sec) + C_0.  Adjust magnitudes to
+  // compensate for the difference.
+
+  for (i = 0; i < Nstars; i++) {
+    for (j = 0; j < NFILTER; j++) {
+      N = NFILTER*i + j;
+      stars[N].X         = colc[N];
+      stars[N].Y         = rowc[N];
+      stars[N].dX        = colcErr[N];
+      stars[N].dY        = rowcErr[N];
+      stars[N].M         = psfCounts[N] + ZeroPt - zeropt[j];
+      stars[N].dM        = psfCountsErr[N];
+      stars[N].Map       = fiberCounts[N] + ZeroPt - zeropt[j];
+      stars[N].Mpeak     = NAN;
+      stars[N].sky       = sky[N]; // adjust this to counts?
+      stars[N].dsky      = skyErr[N];
+      stars[N].fx        = seeing[j]; // reported in arcsec?
+      stars[N].fy        = seeing[j];
+      stars[N].df        = 0.0;
+      stars[N].psfChisq  = prob_psf[N]; // XXX not really the correct value...
+      stars[N].psfQual   = 0.0;
+      stars[N].crNsigma  = 0.0;
+      stars[N].extNsigma = 0.0;
+      stars[N].detID     = N;
+      stars[N].R         = ra[i] + dCOS(dec[i]) * offsetRa[N] / 3600.0;
+      stars[N].D         = dec[i] + offsetDec[N] / 3600.0;
+      stars[N].dR        = NAN;
+      stars[N].dD        = NAN;
+      stars[N].uR        = 0.0; // I don't trust the SDSS proper motions
+      stars[N].uD        = 0.0; // I don't trust the SDSS proper motions
+      stars[N].duR       = 0.0;
+      stars[N].duD       = 0.0;
+      stars[N].P         = 0.0;
+      stars[N].dP        = 0.0;
+      stars[N].Mcal      = 0.0;
+      stars[N].t         = tzero[j] + clockRate*rowc[N]; // time since row 0
+      stars[N].dt        = 53.907456; // is this 2048*clockRate ?
+
+      SetSDSSFlags (&stars[N], flags[N], flags2[N]);
+
+      // longitude and latitude for SDSS:
+      // Latitude 32° 46' 49.30" N, Longitude 105° 49' 13.50" W
+      // longitude = 105.820419312 deg = 7.05469417
+      // latitude = 32.7803611755 deg
+
+      double Longitude = 7.05469417;   // hours (+ = W)
+      double Latitude = 32.7803611755; // degrees
+
+      jd = ohana_sec_to_jd (stars[N].t);
+      sidtime  = 15.0*ohana_lst (jd, Longitude); // sidtime in degrees
+      altaz (&alt, &az, sidtime - stars[N].R, stars[N].D, Latitude);
+
+      stars[N].airmass   = 1.0 / dCOS(90.0 - alt);
+      stars[N].az        = az;
+
+      stars[N].code      = photcode[j];
+      stars[N].found     = -1;
+      stars[N].dophot    = 0;
+    }
+  }    
+
+  for (i = 0; i < NFILTER; i++) {
+
+    N = i + *nimages;
+    
+    // XXX for now, we define a totally fake coordinate system centered on the first listed star
+    strcpy (images[N].coords.ctype, "RA---TAN");
+    
+    images[N].coords.crval1 = stars[0].R;
+    images[N].coords.crval2 = stars[0].D;
+    
+    images[N].coords.crpix1 = stars[0].X;
+    images[N].coords.crpix2 = stars[0].Y;
+    images[N].coords.cdelt1 = images[N].coords.cdelt2 = 0.4 / 3600.0;
+
+    images[N].coords.pc1_1 = 0.0;
+    images[N].coords.pc1_2 = 1.0;
+    images[N].coords.pc2_1 = 1.0;
+    images[N].coords.pc2_2 = 0.0;
+
+    images[N].coords.Npolyterms = 0;
+    memset (images[N].coords.polyterms, 0, 2*7*sizeof(float));
+
+    images[N].NX = 2048;
+    images[N].NY = 1490;
+
+    images[N].tzero = tzero[i];
+    images[N].cerror = 0.0;
+ 
+    // set photcodes for the 5 images (SDSS_U,G,R,I,Z)
+    images[N].photcode = photcode[i];
+
+    // calculate this from : C_OBS, TRACKING, and NY
+    images[N].exptime = 2048*clockRate;
+  
+    images[N].apmifit = 0.0;
+    images[N].dapmifit = 0.0;
+    images[N].detection_limit = 0.0; 
+    images[N].saturation_limit = 0.0;
+    images[N].fwhm_x = seeing[i];
+    images[N].fwhm_y = seeing[i];
+
+    // XXX longitude and latitude are known for SDSS
+    // SDSS is at : Latitude 32° 46' 49.30" N, Longitude 105° 49' 13.50" W
+    // longitude = 105.820419312 deg = 7.05469417
+    // latitude = 32.7803611755 deg
+
+    double Longitude = 7.05469417;   // hours (+ = W)
+    double Latitude = 32.7803611755; // degrees
+
+    jd = ohana_sec_to_jd (images[N].tzero);
+    images[N].sidtime  = ohana_lst (jd, Longitude);
+    images[N].latitude = Latitude;
+
+    altaz (&alt, &az, 15.0*images[N].sidtime - images[N].coords.crval1, images[N].coords.crval2, Latitude);
+
+    images[N].trate = clockRate * 1e-4;
+    images[N].secz = stars[0].airmass;
+    images[N].ccdnum = camcol;
+
+    // secz is in units milli-airmass
+    images[N].Mcal = 0.0;
+    images[N].Xm   = NAN_S_SHORT;
+    images[N].code = 0;
+
+    images[N].nstar = Nstars;
+  
+    images[N].imageID = N;
+    images[N].externID = 0;
+    images[N].sourceID = 0;
+
+    // save the filename
+    snprintf (images[N].name, 64, "%s[%s]", name, filtname[i]);
+  }
+
+  *nimages += NFILTER;
+  *nstars = Nstars*NFILTER;
+  return (stars);
+}
+
+// ha/dec -> alt/az
+int altaz (double *alt, double *az, double ha, double dec, double latitude) {
+
+  double sind, sinh, cosh;
+
+  sind = dSIN (dec) * dSIN (latitude) + dCOS (dec) * dCOS (ha) * dCOS (latitude);
+  *alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (latitude) - dCOS (dec) * dCOS (ha) * dSIN (latitude);
+
+  *az = DEG_RAD * atan2 (sinh, cosh);
+
+  // I may need to use the parallactic angle to get the right plate rotation...
+  // sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(latitude) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(latitude);
+  // cosh = -dSIN(az) * dSIN(ha) * dSIN(latitude) - dCOS(az) * dCOS(ha);
+  // rot = -DEG_RAD * atan2 (sinh, cosh);
+  return TRUE;
+}
+
+int SetSDSSFlags (Stars *star, unsigned int flags1, unsigned int flags2) {
+
+  // XXX this is wrong, need to roll left to set the correct bit 
+  if (flags1 & 0x00000002) star[0].flags |= 0x0001; // BRIGHT            - 1  1
+  if (flags1 & 0x00000004) star[0].flags |= 0x0002; // EDGE              - 1  2
+  if (flags1 & 0x00000008) star[0].flags |= 0x0004; // BLENDED           - 1  3
+  if (flags1 & 0x00000010) star[0].flags |= 0x0008; // CHILD             - 1  4
+  if (flags1 & 0x00000020) star[0].flags |= 0x0010; // PEAKCENTER        - 1  5
+  if (flags1 & 0x00000040) star[0].flags |= 0x0020; // NODEBLEND         - 1  6
+  if (flags1 & 0x00040000) star[0].flags |= 0x0040; // SATUR             - 1 18
+  if (flags1 & 0x00080000) star[0].flags |= 0x0080; // NOTCHECKED        - 1 19
+  if (flags1 & 0x10000000) star[0].flags |= 0x0100; // BINNED1           - 1 28
+  if (flags1 & 0x20000000) star[0].flags |= 0x0200; // BINNED2           - 1 29
+  if (flags1 & 0x40000000) star[0].flags |= 0x0400; // BINNED4           - 1 30
+  if (flags2 & 0x00000040) star[0].flags |= 0x0800; // LOCAL_EDGE        - 2  7
+  if (flags2 & 0x00000800) star[0].flags |= 0x1000; // INTERP_CENTER     - 2 12
+  if (flags2 & 0x00002000) star[0].flags |= 0x2000; // DEBLEND_NOPEAK    - 2 14
+  if (flags2 & 0x02000000) star[0].flags |= 0x4000; // NOTCHECKED_CENTER - 2 26
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsTEXT.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsTEXT.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/ReadStarsTEXT.c	(revision 17422)
@@ -0,0 +1,97 @@
+# include "addstar.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+Stars *ReadStarsTEXT (FILE *f, unsigned int *nstars) {
+
+  int j, N, Nextra, Ninstar, Nskip, Nbytes, nbytes;
+  int done;
+  char *buffer, *c, *c2;
+  double tmp;
+  double ZeroPt;
+  Stars *stars;
+  
+  ZeroPt = GetZeroPoint();
+
+  /* load in stars by blocks of 1000 */
+  N = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+
+  ALLOCATE (stars, Stars, *nstars);
+
+  while (N < *nstars) {
+    /* load next data block */
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      *nstars = N;
+      return (stars);
+    }
+    nbytes += Nextra;
+
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	memset (buffer + nbytes, 0, Nskip);
+	if (VERBOSE) fprintf (stderr, "deleted %d extra return chars\n", Nskip);
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	memset (buffer + nbytes, 0, Nskip);
+	if (VERBOSE) fprintf (stderr, "deleted line, %d extra chars\n", Nskip);
+      } else {
+	c = c2;
+      }
+    }
+
+    /* extract data for stars */
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (j = 0; (j < Ninstar) && (N < *nstars); j++, N++) {
+      dparse (&stars[N].X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].M,  3, &buffer[j*BYTES_STAR]);
+      if ((stars[N].M > ZeroPt) || isnan(stars[N].M)) {
+	stars[N].M = NAN;
+      }
+
+      /* cmp files carry dM in millimags */
+      dparse (&tmp, 4, &buffer[j*BYTES_STAR]);
+      stars[N].dM = 0.001*tmp;
+
+      dparse (&tmp,         5, &buffer[j*BYTES_STAR]);
+      stars[N].dophot = tmp;
+
+      // XXX I've removed the Mgal field from the measure.d table, and am using Map
+      // instead.  DVO has not to date been used to track and study objects which are
+      // extended, but it is about to.  Related to this, I have created the concept of two
+      // extended source attribute tables, to carry the information being measured by the
+      // IPP.
+
+      // dparse (&stars[N].Mgal, 7, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Map,  8, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fx,   9, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fy,  10, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].df,  11, &buffer[j*BYTES_STAR]);
+    }
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDfit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDfit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDfit.c	(revision 17422)
@@ -0,0 +1,221 @@
+# include "sedstar.h"
+
+// XXX a couple of fixes should speed this up a bit: test
+
+int SEDfitCatalog (Catalog *outcat, Catalog *incat, SEDtable *table) {
+  
+  int i, j, m, n, idx, start, done, row, Nsec, Nfit, Nphot;
+  int Nave, Nmeas, NAVE, NMEAS, Nmodel, Nreq;
+  unsigned short USNOred, USNOblu;
+  float color;
+  int *found, valid, *modelRow, *reqRow;
+
+  SEDtableRow sourceValue, sourceError;
+  SEDfit minFit, testFit;
+
+  Nmodel = Nreq = 0;
+  for (i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_REQ) Nreq ++;
+    if (table[0].mode[i] == SED_MODEL) Nmodel ++;
+  }
+  if (Nmodel < 1) {
+    fprintf (stderr, "no model filter defined\n!");
+    exit (2);
+  }
+  if (Nreq < 1) {
+    fprintf (stderr, "no required filter defined\n!");
+    exit (2);
+  }
+
+  ALLOCATE (modelRow, int, Nmodel);
+  for (j = i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_MODEL) { 
+      modelRow[j] = i;
+      j++;
+    }
+  }
+  ALLOCATE (reqRow, int, Nreq);
+  for (j = i = 0; i < table[0].Nfilter; i++) {
+    if (table[0].mode[i] == SED_REQ) { 
+      reqRow[j] = i;
+      j++;
+    }
+  }
+
+  sourceValue.mags = NULL;
+  sourceError.mags = NULL;
+
+  Nsec = GetPhotcodeNsecfilt ();
+  Nave = outcat[0].Naverage;
+  Nmeas = outcat[0].Nmeasure;
+
+  NAVE = 100;
+  NMEAS = 100;
+  REALLOCATE (outcat[0].average, Average, NAVE);
+  REALLOCATE (outcat[0].secfilt, SecFilt, NAVE*Nsec);
+  REALLOCATE (outcat[0].measure, Measure, NMEAS);
+
+  // artificially set USNOred and blu errors to 0.3
+  USNOred = GetPhotcodeCodebyName ("USNO_RED");
+  USNOblu = GetPhotcodeCodebyName ("USNO_BLUE");
+
+  // create holder for the source data
+  ALLOCATE (sourceValue.mags, float, table[0].Nfilter);
+  ALLOCATE (sourceError.mags, float, table[0].Nfilter);
+  ALLOCATE (found, int, table[0].Nfilter);
+
+  if (PLOT) SEDfitInit (table);
+
+  // perform the fit to all sources
+  for (i = 0; i < incat[0].Naverage; i++) {
+
+    // blank out the source array
+    for (j = 0; j < table[0].Nfilter; j++) {
+      sourceValue.mags[j] = 100;
+      found[j] = FALSE;
+    }	
+
+    // load the measurements for this source
+    m = incat[0].average[i].measureOffset;
+    Nphot = 0;
+    for (j = 0; j < incat[0].average[i].Nmeasure; j++) {
+      idx = table[0].hashcode[incat[0].measure[m+j].photcode];
+      if (idx == -1) continue;
+      // only fit the selected photcodes (mode == "fit")
+      if (table[0].mode[idx] == SED_MODEL) continue; 
+      if (table[0].mode[idx] == SED_SAMPLE) continue; 
+      // XXX do something more clever if more than one value exists per photcode
+      sourceValue.mags[idx] = incat[0].measure[m+j].M + table[0].vegaToAB[idx];
+      sourceError.mags[idx] = incat[0].measure[m+j].dM;
+      if (incat[0].measure[m+j].photcode == USNOred) sourceError.mags[idx] = 0.3;
+      if (incat[0].measure[m+j].photcode == USNOblu) sourceError.mags[idx] = 0.3;
+      found[idx] = TRUE;
+      Nphot ++;
+    }
+    if (Nphot < 3) continue;
+
+    // XXX pre-select list of REQ entries; loop over only those?
+    valid = TRUE;
+    for (j = 0; valid && (j < Nreq); j++) {
+      if ((table[0].mode[reqRow[j]] == SED_REQ) && !found[reqRow[j]]) valid = FALSE;
+    }
+    if (!valid) continue;
+
+    // skip sources without ref color
+    if (sourceValue.mags[table[0].codeP] > 50) continue;
+    if (sourceValue.mags[table[0].codeM] > 50) continue;
+    color = sourceValue.mags[table[0].codeP] - sourceValue.mags[table[0].codeM];
+
+    // find tableRow within 0.1 mag of color 
+    // XXX : check on the delta value
+    start = SEDcolorBracket (table, color, 0.05);
+    minFit = SEDchisq (table[0].row[start], &sourceValue, &sourceError, table[0].Nfilter);
+    minFit.row = start;
+
+    // search for min chisq backwards
+    // XXX : check on the delta value
+    done = FALSE;
+    row = start - 1;
+    while (!done && (row > 0)) {
+      testFit = SEDchisq (table[0].row[row], &sourceValue, &sourceError, table[0].Nfilter);
+      if (testFit.chisq < minFit.chisq) {
+	minFit = testFit;
+	minFit.row = row;
+      }
+      if (fabs(table[0].row[row][0].color - color) > 0.25) done = TRUE;
+      row --;
+    }
+
+    // search for min chisq forwards
+    // XXX : check on the delta value
+    done = FALSE;
+    row = start + 1;
+    while (!done && (row < table[0].Nrow)) {
+      testFit = SEDchisq (table[0].row[row], &sourceValue, &sourceError, table[0].Nfilter);
+      if (testFit.chisq < minFit.chisq) {
+	minFit = testFit;
+	minFit.row = row;
+      }
+      if (fabs(table[0].row[row][0].color - color) > 0.25) done = TRUE;
+      row ++;
+    }
+
+    Nfit ++;
+    // create the vectors for the example plots
+    if (PLOT) SEDfitPlot (table, incat[0].average[i].R, incat[0].average[i].D, &minFit, &sourceValue, &sourceError);
+
+    // construct an average object for this object
+    // XXX for now, the output objects will have limited astrometric interpretation...
+    outcat[0].average[Nave].R         = incat[0].average[i].R;
+    outcat[0].average[Nave].D         = incat[0].average[i].D;
+    outcat[0].average[Nave].dR    = 0;
+    outcat[0].average[Nave].dD    = 0;
+    outcat[0].average[Nave].uR    = 0;
+    outcat[0].average[Nave].uD    = 0;
+    outcat[0].average[Nave].duR   = 0;
+    outcat[0].average[Nave].duD   = 0;
+    outcat[0].average[Nave].P     = 0;
+    outcat[0].average[Nave].dP    = 0;
+
+    // XXX for now, set the average mag data to NULL
+    outcat[0].average[Nave].Nmeasure  	  = 0;
+    outcat[0].average[Nave].Nmissing  	  = 0;
+    outcat[0].average[Nave].Xp        	  = NAN_S_SHORT;
+    outcat[0].average[Nave].measureOffset = Nmeas;
+    outcat[0].average[Nave].missingOffset = -1;
+    outcat[0].average[Nave].code          = 0;
+
+    for (j = 0; j < Nsec; j++) {
+      outcat[0].secfilt[Nave*Nsec+j].M  = NAN;
+      outcat[0].secfilt[Nave*Nsec+j].dM = NAN;
+      outcat[0].secfilt[Nave*Nsec+j].Xm = NAN_S_SHORT;
+    }
+
+    // we now have the min chisq row. use this to supply the other filter values....
+    // XXX pre-select the SED_MODEL rows...
+    if (Nmeas + table[0].Nfilter >= NMEAS) {
+	NMEAS += 100 + table[0].Nfilter;
+	REALLOCATE (outcat[0].measure, Measure, NMEAS);
+    }
+
+    for (j = 0; valid && (j < Nmodel); j++) {
+      n = modelRow[j];
+      outcat[0].measure[Nmeas].dR       = 0.0;
+      outcat[0].measure[Nmeas].dD       = 0.0;
+      outcat[0].measure[Nmeas].M        = table[0].row[minFit.row][0].mags[n] + minFit.Md;
+      outcat[0].measure[Nmeas].dM       = 0.0;
+      outcat[0].measure[Nmeas].Mcal     = 0;
+      outcat[0].measure[Nmeas].t        = TIMEREF;
+      outcat[0].measure[Nmeas].averef   = Nave;
+      outcat[0].measure[Nmeas].photcode = table[0].code[n];
+      outcat[0].measure[Nmeas].dophot   = 0;
+      outcat[0].measure[Nmeas].dbFlags  = 0;
+      outcat[0].measure[Nmeas].dt       = 0xffff;
+
+      outcat[0].measure[Nmeas].airmass  = 0;
+      outcat[0].measure[Nmeas].FWx      = NAN_S_SHORT;
+      outcat[0].measure[Nmeas].FWy      = NAN_S_SHORT;
+      outcat[0].measure[Nmeas].theta    = NAN_S_SHORT;
+
+      outcat[0].average[Nave].Nmeasure++;
+      Nmeas ++;
+    }
+
+    Nave ++;
+    if (Nave >= NAVE) {
+      NAVE += 100;
+      REALLOCATE (outcat[0].average, Average, NAVE);
+      REALLOCATE (outcat[0].secfilt, SecFilt, NAVE*Nsec);
+    }
+  }
+  outcat[0].Naverage  = Nave;
+  outcat[0].Nmeasure  = Nmeas;
+  outcat[0].Nsecf_mem = Nave*Nsec;
+  
+  free (sourceValue.mags);
+  free (sourceError.mags);
+  free (found);
+
+  SEDfitClear ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDops.c	(revision 17422)
@@ -0,0 +1,249 @@
+# include "sedstar.h"
+
+// fit the data (with errors) to the given table row
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter) {
+
+  int i;
+  double Sm, Sd, S2, wt, dM;
+  SEDfit fit;
+
+  Sm = Sd = S2 = 0.0;
+
+  for (i = 0; i < Nfilter; i++) {
+    if (data[0].mags[i] > 50.0) continue;
+
+    if (error[0].mags[i] == 0.0) {
+      wt = 1.0;
+    } else {
+      wt = 1.0 / SQ(error[0].mags[i]);
+    }
+
+    dM = data[0].mags[i] - ref[0].mags[i];
+    S2 += SQ(dM) * wt;
+    Sm += dM * wt;
+    Sd += wt;
+  }
+    
+  // row is assigned after fit
+  fit.row = -1;
+  fit.Md = Sm / Sd;
+  fit.chisq = S2 + SQ(fit.Md) * Sd - 2*fit.Md*Sm;
+
+  return (fit);
+}
+
+// find the first table row within 0.1 mag of the requested color (or within 10)
+int SEDcolorBracket (SEDtable *table, float color, float delta) {
+
+  int Nlo, Nhi, N;
+  float tcolor;
+
+  N = Nlo = 0; Nhi = table[0].Nrow;
+  tcolor = table[0].row[Nlo][0].color;
+  while ((Nhi - Nlo > 10) && (fabs(tcolor-color) > delta)) {
+    N = 0.5*(Nlo + Nhi);
+    N = MAX (N, 0);
+    N = MIN (N, table[0].Nrow - 1);
+    tcolor = table[0].row[N][0].color;
+    if (tcolor < color) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  return (N);
+}
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N) {
+
+  int i;
+
+  SEDtableRow **value;
+  
+  if (N <= 0) return (NULL);
+
+  ALLOCATE (value, SEDtableRow *, N);
+  for (i = 0; i < N; i++) {
+    value[i] = &raw[i];
+  }
+
+# define SWAPFUNC(A,B){ SEDtableRow *temp = value[A]; value[A] = value[B]; value[B] = temp; }
+# define COMPARE(A,B)(value[A][0].color < value[B][0].color)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  return (value);
+}
+
+static Graphdata graphdata;
+static KapaSection magSection, resSection;
+static int Xgraph;
+static float *fitmags, *fiterrs;
+
+int SEDfitInit (SEDtable *table) {
+
+  Xgraph = KiiOpen ("kapa", "sedstar");
+  KapaInitGraph (&graphdata);
+  SetLimitsRaw (table[0].wavecode, NULL, table[0].Nfilter, &graphdata);
+  graphdata.style = 2;
+  graphdata.ptype = 2;
+  KapaClearSections (Xgraph);
+  magSection.name = strcreate ("mag");
+  magSection.x  = 0;
+  magSection.dx = 1;
+  magSection.y  = 0.5;
+  magSection.dy = 0.5;
+  resSection.name = strcreate ("res");
+  resSection.x  = 0.0;
+  resSection.dx = 1.0;
+  resSection.y  = 0.0;
+  resSection.dy = 0.5;
+    
+  KiiResize (Xgraph, 900, 500);
+  KapaSetFont (Xgraph, "helvetica", 14);
+  ALLOCATE (fitmags, float, table[0].Nfilter);
+  ALLOCATE (fiterrs, float, table[0].Nfilter);
+  return (TRUE);
+}
+
+int SEDfitClear () {
+
+  free (fitmags);
+  free (fiterrs);
+  KiiClose (Xgraph);
+  return (TRUE);
+}
+
+int SEDfitPlot (SEDtable *table, double R, double D, SEDfit *minFit, SEDtableRow *sourceValue, SEDtableRow *sourceError) {
+
+  int j, minRow, Nfilter;
+  double X, Y, Z, RA, DEC;
+  char line[1024], key[20];
+
+  minRow = minFit[0].row;
+  Nfilter = table[0].Nfilter;
+
+  // we want to plot the OBSERVED magnitudes
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = table[0].row[minRow][0].mags[j] + minFit[0].Md;
+  }
+
+  // find plot range
+  SetLimitsRaw (NULL, fitmags, Nfilter, &graphdata);
+  SWAP (graphdata.ymin, graphdata.ymax);
+
+  KapaClearSections (Xgraph);
+  KapaSetSection (Xgraph, &magSection);
+  KapaSetLimits (Xgraph, &graphdata);
+  KapaBox (Xgraph, &graphdata);
+  graphdata.color = KapaColorByName ("blue");
+  graphdata.etype = 0;
+  graphdata.ptype = 7;
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "y");
+
+  graphdata.color = KapaColorByName ("red");
+  graphdata.etype = 1;
+  graphdata.ptype = 2;
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = 100;
+    fiterrs[j] = 0;
+    if (sourceValue[0].mags[j] > 50) continue;
+    fitmags[j] = sourceValue[0].mags[j];
+    fiterrs[j] = sourceError[0].mags[j];
+  }
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "x");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dym");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dyp");
+  KapaSendLabel (Xgraph, "model,fit (mags)", 1);
+
+  sprintf (line, "star: %10.6f %10.6f  T: %5.0fK  A_V|: %4.2f  M_D|: %5.2f  &sc&h^2|: %5.2f", 
+	   R, D, 
+	   table[0].row[minRow][0].Temp, 
+	   table[0].row[minRow][0].Av, 
+	   minFit[0].Md, minFit[0].chisq);
+  KapaSendLabel (Xgraph, line, 2);
+  KapaSendLabel (Xgraph, "model,fit (mags)", 1);
+
+  KapaSetSection (Xgraph, &resSection);
+  graphdata.ymin = -1.0;
+  graphdata.ymax = +1.0;
+  KapaSetLimits (Xgraph, &graphdata);
+  KapaBox (Xgraph, &graphdata);
+  graphdata.color = KapaColorByName ("red");
+  graphdata.etype = 1;
+
+  for (j = 0; j < Nfilter; j++) {
+    fitmags[j] = 100;
+    fiterrs[j] = 0;
+    if (sourceValue[0].mags[j] > 50) continue;
+    fitmags[j] = sourceValue[0].mags[j] - minFit[0].Md - table[0].row[minRow][0].mags[j];
+    fiterrs[j] = sourceError[0].mags[j];
+  }
+  KapaPrepPlot (Xgraph, Nfilter, &graphdata);
+  KapaPlotVector (Xgraph, Nfilter, table[0].wavecode, "x");
+  KapaPlotVector (Xgraph, Nfilter, fitmags, "y");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dym");
+  KapaPlotVector (Xgraph, Nfilter, fiterrs, "dyp");
+  KapaSendLabel (Xgraph, "wavelength (nm)", 0);
+  KapaSendLabel (Xgraph, "resid (mags)", 1);
+
+  KiiCursorOn (Xgraph);
+  while (KiiCursorRead (Xgraph, &X, &Y, &Z, &RA, &DEC, key)) {
+    // fprintf (stderr, "window: %f %f (%s)\n", X, Y, key);
+    if (!strcasecmp (key, "Q")) {
+      KiiCursorOff (Xgraph);
+      break;
+    }
+    if (!strcasecmp (key, "ESCAPE")) {
+      KiiCursorOff (Xgraph);
+      PLOT = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (key, "X")) {
+      KiiCursorOff (Xgraph);
+      Shutdown ("quitting sedstar");
+    }
+  }
+  return (TRUE);
+}
+
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(xvec[i])) continue;
+      maxX = MAX (maxX, xvec[i]);
+      minX = MIN (minX, xvec[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(yvec[i])) continue;
+      maxY = MAX (maxY, yvec[i]);
+      minY = MIN (minY, yvec[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDtableLoad.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDtableLoad.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SEDtableLoad.c	(revision 17422)
@@ -0,0 +1,83 @@
+# include "sedstar.h"
+
+// XXX where do the colorP and colorM codes come from?
+SEDtable *SEDtableLoad (char *filename) {
+
+  FILE *f;
+  char line[1024], name[64], mode[64];
+  char colornameP[64], colornameM[64];
+  int colorP, colorM, code;
+  int i, Nrow, NROW;
+  SEDtable *table;
+  SEDtableRow *raw;
+
+  ALLOCATE (table, SEDtable, 1);
+
+  // load SED table
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("failure to open SED table");
+
+
+  // XXX add error checks for header data
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %d", &table[0].Nfilter);
+
+  // load SED table photcodes, generate the photcode hashtable
+  ALLOCATE (table[0].hashcode, int, 0x10000);
+  ALLOCATE (table[0].wavecode, float, table[0].Nfilter);
+  ALLOCATE (table[0].vegaToAB, float, table[0].Nfilter);
+  ALLOCATE (table[0].mode,     int,   table[0].Nfilter);
+  ALLOCATE (table[0].code,     int,   table[0].Nfilter);
+
+  for (i = 0; i < 0x10000; i++) table[0].hashcode[i] = -1;
+  for (i = 0; i < table[0].Nfilter; i++) {
+    scan_line (f, line);
+    sscanf (line, "%*s %s %f %f %s", name, &table[0].wavecode[i], &table[0].vegaToAB[i], mode);
+    code = GetPhotcodeCodebyName (name);
+    table[0].code[i] = code;
+    if (code == 0) Shutdown ("undefined photcode in SED table");
+    table[0].hashcode[code] = i;
+
+    table[0].mode[i] = -1;     
+    if (!strcasecmp(mode, "fit")) table[0].mode[i] = SED_FIT; 
+    if (!strcasecmp(mode, "req")) table[0].mode[i] = SED_REQ; 
+    if (!strcasecmp(mode, "model")) table[0].mode[i] = SED_MODEL; 
+    if (!strcasecmp(mode, "sample")) table[0].mode[i] = SED_SAMPLE; 
+    if (table[0].mode[i] == -1) Shutdown ("invalid photcode mode in SED table");
+  }
+
+  // load color key
+  scan_line (f, line);
+
+  // define the selection color codes
+  sscanf (line, "# %s - %s", colornameP, colornameM);
+  colorP = GetPhotcodeCodebyName (colornameP);
+  colorM = GetPhotcodeCodebyName (colornameM);
+  table[0].codeP = table[0].hashcode[colorP];
+  table[0].codeM = table[0].hashcode[colorM];
+  if (table[0].codeP == -1) Shutdown ("missing positive color filter");
+  if (table[0].codeM == -1) Shutdown ("missing positive color filter");
+    
+  // load the SED raw table rows
+  Nrow = 0;
+  NROW = 100;
+  ALLOCATE (raw, SEDtableRow, NROW);
+  while (scan_line(f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == '#') continue;
+    fparse (&raw[Nrow].Temp, 1, line);
+    fparse (&raw[Nrow].Av, 2, line);
+    ALLOCATE (raw[Nrow].mags, float, table[0].Nfilter);
+    for (i = 0; i < table[0].Nfilter; i++) {
+      fparse (&raw[Nrow].mags[i], i + 3, line);
+    }
+    raw[Nrow].color = raw[Nrow].mags[table[0].codeP] - raw[Nrow].mags[table[0].codeM];
+    Nrow ++;
+    CHECK_REALLOCATE (raw, SEDtableRow, NROW, Nrow, 100);
+  }      
+
+  // sort the SEDtable by the reference colors
+  table[0].row = sort_SEDtable (raw, Nrow);
+  table[0].Nrow = Nrow;
+  return (table);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SetSignals.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SetSignals.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SetSignals.c	(revision 17422)
@@ -0,0 +1,122 @@
+# include "addstar.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect these signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored (POSIX.1-1990) */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored (POSIX.1-1990) */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore (POSIX.1-1990) */
+    case SIGCONT:    /* continue - maintain this action (POSIX.1-1990) */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? (POSIX.1-1990) */
+    case SIGURG:     /* socket signal, ignore this (POSIX.1-2001) */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/Shutdown.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/Shutdown.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/Shutdown.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "addstar.h"
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format) + 2);
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyListForStars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyListForStars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyListForStars.c	(revision 17422)
@@ -0,0 +1,58 @@
+# include "addstar.h"
+
+SkyList *SkyListForStars (SkyTable *table, int depth, Stars *stars, int Nstars) {
+  
+  int i, j, Nr, NR;
+  SkyList *here;
+  SkyList *list;
+  
+  Nr = 0;
+  NR = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, NR);
+  ALLOCATE (list[0].filename, char *, NR);
+  list[0].Nregions = Nr;
+  list[0].ownElements = FALSE; // free these elements when freeing the list
+
+  sort_stars_ra (stars, Nstars); /* sort by RA */
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].found) continue;
+    here = SkyRegionByPoint (table, depth, stars[i].R, stars[i].D);
+    stars[i].found = TRUE;
+    /* search forward for all contained stars */
+    for (j = i; j < Nstars; j++) {
+      if (stars[j].R >= here[0].regions[0][0].Rmax) break;
+      if (stars[j].R <  here[0].regions[0][0].Rmin) break;
+      if (stars[j].D <  here[0].regions[0][0].Dmin) continue;
+      if (stars[j].D >= here[0].regions[0][0].Dmax) continue;
+      stars[j].found = TRUE;
+    }
+    list[0].regions[Nr] = here[0].regions[0];
+    list[0].filename[Nr] = here[0].filename[0];
+    SkyListFree (here); 
+    Nr ++;
+    if (Nr >= NR) {
+	NR += 32;
+	REALLOCATE (list[0].regions, SkyRegion *, NR);
+	REALLOCATE (list[0].filename, char *, NR);
+    }
+    list[0].Nregions = Nr;
+  }
+
+  /* reset to -1 for all stars: required start for find_match_refstars */
+  for (i = 0; i < Nstars; i++) stars[i].found = -1; 
+  return (list);
+}
+
+/* given a list of stars, find all region files which contain them 
+
+   - sort by ra
+   - loop over stars
+     - find region file which contains star
+     - go forwards until ra > Rmax
+       - mark all stars in this region file
+     
+   - use DEC band information?
+   - would like to minimize the number of disk reads
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyRegionUtils.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyRegionUtils.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyRegionUtils.c	(revision 17422)
@@ -0,0 +1,27 @@
+# include "addstar.h"
+
+SkyList *SkyListExistingSubset (SkyList *input, char *path) {
+  
+  int i, status, Nsubset, NSUBSET;
+  SkyList *subset;
+  struct stat filestats;
+  
+  Nsubset = 0;
+  NSUBSET = 100;
+  ALLOCATE (subset, SkyList, 1);
+  ALLOCATE (subset[0].regions, SkyRegion *, NSUBSET);
+  subset[0].ownElements = FALSE; // free these elements when freeing the list
+
+  /* match the basename against the GSCRegion file names */
+  for (i = 0; i < input[0].Nregions; i++) {
+    status = stat (input[0].filename[i], &filestats);
+    if ((status == -1) && (errno == ENOENT)) continue;
+    /* give an error for other conditions? */
+
+    subset[0].regions[Nsubset] = input[0].regions[i];
+    Nsubset ++;
+    CHECK_REALLOCATE (subset[0].regions, SkyRegion *, NSUBSET, Nsubset, 100);
+    subset[0].Nregions = Nsubset;
+  }
+  return (subset);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyTableFromTychoIndex.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 17422)
@@ -0,0 +1,152 @@
+# include "dvo.h"
+# define NBANDS 24
+# define NDIV 4
+
+static double DecBands[] = {0.0, +7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, +90.0,
+			    0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+
+static char *DecNames[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", "n5230", "n6000", "n6730", "n7500", "n8230", "none",
+			   "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", "s5230", "s6000", "s6730", "s7500", "s8230", "none"};
+
+SkyTable *SkyTableFromTychoIndex (char *filename, int VERBOSE) {
+
+  int i, No, Nr, NR, Ntycho;
+  double Dmin, Dmax;
+  char line[256];
+  FILE *f;
+  SkyTable *skytable;
+  SkyRegion *regions;
+  SkyRegion *tycho;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find Tycho Index file %s\n", filename);
+    return (NULL);
+  }
+
+  /* load in table data */
+  ALLOCATE (tycho, SkyRegion, 10000);
+  for (i = 0; i < 10000; i++) {
+    if (scan_line (f, line) == EOF) break;
+    tycho[i].Rmin = atof (&line[15]);
+    tycho[i].Rmax = atof (&line[22]);
+    tycho[i].Dmin = atof (&line[29]);
+    tycho[i].Dmax = atof (&line[36]);
+
+    memset (tycho[i].name, 0, 21);
+    strncpy (tycho[i].name, line, 7);
+    tycho[i].name[7] = 0;
+  }
+  Ntycho = i;
+  fclose (f);
+
+  /* build supporting level 0 and 1 regions */
+  Nr = 0;
+  NR = 100;
+  ALLOCATE (regions, SkyRegion, 100);
+  
+  /* level 0 : full sky */
+  regions[Nr].Rmin 	=   0;
+  regions[Nr].Rmax 	= 360;
+  regions[Nr].Dmin 	= -90;
+  regions[Nr].Dmax 	= +90;
+  regions[Nr].index  	=  0;
+  regions[Nr].depth  	=  0;
+  regions[Nr].parent 	= -1;
+  regions[Nr].child  	=  TRUE;
+  regions[Nr].table  	=  FALSE;
+  memset (regions[Nr].name, 0, 21);
+  strcpy (regions[Nr].name, "fullsky");
+  
+  No = Nr;
+  Nr ++;
+
+  /* level 1 : add the dec bands */
+  regions[No].childS = Nr;
+  /* first north */
+  for (i = 0; i < 12; i++, Nr++) {
+    regions[Nr].Rmin   	  =   0;
+    regions[Nr].Rmax   	  = 360;
+    regions[Nr].Dmin   	  = DecBands[i];
+    regions[Nr].Dmax   	  = DecBands[i+1];
+    regions[Nr].index  	  =  i+1;
+    regions[Nr].depth  	  =  1;
+    regions[Nr].parent 	  =  0;
+    regions[Nr].child  	  =  TRUE;
+    regions[Nr].table  	  =  FALSE;
+    memset (regions[Nr].name, 0, 21);
+    strcpy (regions[Nr].name, DecNames[i]);
+  }
+  /* now south */
+  for (i = 0; i < 12; i++, Nr++) {
+    regions[Nr].Rmin   	  =   0;
+    regions[Nr].Rmax   	  = 360;
+    regions[Nr].Dmin   	  = DecBands[i+14];
+    regions[Nr].Dmax   	  = DecBands[i+13];
+    regions[Nr].index  	  =  i+1;
+    regions[Nr].depth  	  =  1;
+    regions[Nr].parent 	  =  0;
+    regions[Nr].child  	  =  TRUE;
+    regions[Nr].table  	  =  FALSE;
+    memset (regions[Nr].name, 0, 21);
+    strcpy (regions[Nr].name, DecNames[i+13]);
+  }
+  regions[No].childE = Nr;
+
+  CHECK_REALLOCATE (regions, SkyRegion, NR, Nr, 100);
+
+  /* level 2 : copy the data from the GSC Region files */
+  No = 1;
+  Dmin = regions[No].Dmin - 0.2;
+  Dmax = regions[No].Dmax + 0.2;
+  regions[No].childS = Nr;
+
+  for (i = 0; i < Ntycho; i++) {
+    /* if we are outside of current region, go to the next one */
+    if ((tycho[i].Dmin < Dmin) || (tycho[i].Dmax > Dmax)) {
+      regions[No].childE = Nr;
+
+      No++;
+      Dmin = regions[No].Dmin - 0.2;
+      Dmax = regions[No].Dmax + 0.2;
+      regions[No].childS = Nr;
+
+      if ((tycho[i].Dmin < Dmin) || (tycho[i].Dmax > Dmax)) {
+	fprintf (stderr, "ERROR: tycho index is not in order!\n");
+	exit (1);
+      }
+    }
+
+    /* set the values for this region */
+    regions[Nr].Rmin = tycho[i].Rmin;
+    regions[Nr].Rmax = tycho[i].Rmax;
+    regions[Nr].Dmin = tycho[i].Dmin;
+    regions[Nr].Dmax = tycho[i].Dmax;
+    strcpy (regions[Nr].name, tycho[i].name);
+
+    regions[Nr].index    =  Nr;
+    regions[Nr].depth    =  2;
+    regions[Nr].parent   =  No;
+    regions[Nr].child    =  FALSE;
+    regions[Nr].table    =  FALSE;
+    regions[Nr].childS   =  0;
+    regions[Nr].childE   =  0;
+
+    Nr ++;
+    CHECK_REALLOCATE (regions, SkyRegion, NR, Nr, 100);
+  }
+
+  free (tycho);
+
+  ALLOCATE (skytable, SkyTable, 1);
+  skytable[0].regions = regions;
+  skytable[0].Nregions = Nr;
+
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+  }
+  if (VERBOSE) fprintf (stderr, "loaded %d tables from tycho index\n", skytable[0].Nregions);
+
+  return (skytable);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/SocketOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/SocketOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/SocketOps.c	(revision 17422)
@@ -0,0 +1,211 @@
+# include "addstar.h"
+
+# define MY_PORT 2000
+# define MY_WAIT 500
+
+int InitServerSocket (SockAddress *Address) {
+
+  int status, InitSocket, length;
+
+# if (0)
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+# endif
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+# if (0)  
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+# endif
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (2);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (2);
+  }
+
+  // if (VERBOSE) fprintf (stderr, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
+int WaitServerSocket (int InitSocket, SockAddress *Address, int *validIP, int Nvalid) {
+
+  int i, BindSocket;
+  socklen_t length;
+  SockAddress Address_in;
+  u_int32_t addr;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  /* this is a blocking wait; use in a separate thread */
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  fprintf (stderr, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (2);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (VERBOSE) {
+    fprintf (stderr, "incoming connection from: ");
+    fprintf (stderr, " %u", (0xff & (addr >>  0)));
+    fprintf (stderr, ".%u", (0xff & (addr >>  8)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 16)));
+    fprintf (stderr, ".%u", (0xff & (addr >> 24)));
+    fprintf (stderr, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (validIP[i] >> 24)) != 0) {
+      if (addr == validIP[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (validIP[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == validIP[i]) goto accepted;
+    }
+  }
+
+  if (VERBOSE) fprintf (stderr, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (VERBOSE) fprintf (stderr, "connection accepted\n");
+  fcntl (BindSocket, F_SETFL, O_NONBLOCK); 
+  return (BindSocket);
+}
+
+int GetClientSocket (char *hostname) {
+
+  int i, status, InitSocket, length;
+  SockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  if (VERBOSE) {
+    fprintf (stderr, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    perror ("connect: ");
+    exit (2);
+  }
+
+  if (VERBOSE) fprintf (stderr, "connected\n");
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int InitServerSocket_Named (char *hostname, SockAddress *Address) {
+
+  int i, status, InitSocket, length;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  memset (hostip, 0, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    fprintf (stderr, "invalid address\n");
+    exit (2);
+  }
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (2);
+  }
+
+  fprintf (stderr, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (2);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (2);
+  }
+
+  if (VERBOSE) fprintf (stderr, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Image.c	(revision 17422)
@@ -0,0 +1,121 @@
+# include "addstar.h"
+
+int UpdateDatabase_Image (AddstarClientOptions *options, Image *images, int Nimages, Coords *mosaic, Stars *stars, int Nstars) {
+
+  int i, status;
+  Catalog catalog;
+  SkyList *skylist, *newlist;
+
+  if (options[0].mode != M_IMAGE) {
+    fprintf (stderr, "error: expecting only IMAGE mode\n");
+    return (FALSE);
+  }
+
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+  if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+  
+  // XXX this is probably not needed anymore
+  SetAirmassQuality (options[0].quality_airmass);
+
+  /*** update catalog: average, measure, etc ***/
+
+  /* find correpsonding regions for image */
+  RegisterMosaic (mosaic);
+  skylist = NULL;
+  newlist = NULL;
+  for (i = 0; i < Nimages; i++) {
+      newlist = SkyListByImage (ServerSky, -1, &images[i]);
+      SkyListMerge (&skylist, newlist);
+      SkyListFree (newlist);
+  }
+
+  ImageOptions (options, images, Nimages);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs 
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    if (options[0].closest) {
+      find_matches_closest (skylist[0].regions[i], stars, Nstars, &catalog, options[0]);
+    } else {
+      find_matches (skylist[0].regions[i], stars, Nstars, &catalog, options[0]);
+    }
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  if (options[0].calibrate) { FindCalibration (&images[0]); }
+  
+  /*** load image db, save new image ***/
+  { 
+    FITS_DB db;
+
+    /*** update the image table ***/
+    /* setup image table format and lock */
+    db.mode   = dvo_catalog_catmode (CATMODE);
+    db.format = dvo_catalog_catformat (CATFORMAT);
+    status = dvo_image_lock (&db, ImageCat, 60.0, LCK_XCLD);
+    if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+    /* load or create the image table */
+    if (db.dbstate == LCK_EMPTY) {
+      if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+      dvo_image_create (&db, GetZeroPoint());
+    } else {
+      if (!dvo_image_load (&db, VERBOSE, FORCE_READ)) {
+	Shutdown ("can't read image catalog %s", db.filename);
+      }
+    }
+
+    /* write out new image */
+    dvo_image_addrows (&db, images, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_unlock (&db);
+  }
+  free (mosaic);
+  free (images);
+  free (stars);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Refcat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Refcat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Refcat.c	(revision 17422)
@@ -0,0 +1,77 @@
+# include "addstar.h"
+
+int UpdateDatabase_Refcat (AddstarClientOptions *options, SkyRegion *patch, char *refcat) {
+
+  int i, Nstars, Nsubset;
+  Catalog catalog;
+  Stars *stars, **subset;
+  SkyList *skylist;
+
+  if (options[0].mode != M_REFCAT) {
+    fprintf (stderr, "error: expecting only REFCAT mode\n");
+    return (FALSE);
+  }
+
+  if (!check_dir_access (CATDIR, VERBOSE)) exit (1);
+
+  /*** update catalog: average, measure, etc ***/
+  
+  /* find correpsonding regions for image */
+  skylist = SkyListByPatch (ServerSky, -1, patch);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+  
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // load stars from corresponding catalog 
+    stars = grefcat (refcat, skylist[0].regions[i], options[0].photcode, &Nstars);
+
+    subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+    find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options[0]);
+    if (Nsubset) free (subset);
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+    free (stars);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Reflist.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Reflist.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateDatabase_Reflist.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "addstar.h"
+
+int UpdateDatabase_Reflist (AddstarClientOptions *options, Stars *stars, int Nstars) {
+
+  int i, Nsubset;
+  Catalog catalog;
+  Stars **subset;
+  SkyList *skylist;
+
+  if (options[0].mode != M_REFLIST) {
+    fprintf (stderr, "error: expecting only REFLIST mode\n");
+    return (FALSE);
+  }
+
+  if (!check_dir_access (CATDIR, VERBOSE)) exit (1);
+
+  /*** update catalog: average, measure, etc ***/
+  
+  /* find correpsonding regions for image */
+  skylist = SkyListForStars (ServerSky, -1, stars, Nstars);
+
+  /* reduce regions to existing subset, if necessary */
+  if (options[0].only_match || options[0].existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    if (options[0].update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+
+    /* for only_match, skip empty catalogs XXX EAM : this leaves behind empty files */
+    if ((catalog.Naves_disk == 0) && options[0].only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+    find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options[0]);
+    if (Nsubset) free (subset);
+
+    if (!options[0].only_images) {
+      SetProtect (TRUE);
+      if (options[0].update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+
+  free (stars);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateImageIDs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "addstar.h"
+
+int UpdateImageIDs (Stars *stars, int Nstars, Image *images, int Nimages) {
+
+  int i, status, isEmpty;
+  unsigned int imageID;
+  FITS_DB db;
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, GetZeroPoint());
+    isEmpty = TRUE;
+  } else {
+    /* position to start of file */
+    Fseek (db.f, 0, SEEK_SET);
+    if (!gfits_fread_header (db.f, &db.header)) {
+      Shutdown ("can't read image table phu %s", db.filename);
+    }
+    isEmpty = FALSE;
+  }
+
+  // note that imageID is unsigned int
+  status = gfits_scan (&db.header, "IMAGEID", "%u", 1, &imageID);
+  if (!status) {
+    status = gfits_scan (&db.header, "NIMAGES", "%u", 1, &imageID);
+  }
+
+  // XXX should the first image ID be 1, not 0?
+  if (imageID == 0) imageID = 1;
+
+  // update the image IDs already written to stars and images:
+  for (i = 0; i < Nimages; i++) {
+    images[0].imageID += imageID;
+  }
+
+  for (i = 0; i < Nstars; i++) {
+    stars[0].imageID += imageID;
+  }
+
+  imageID += Nimages;
+  status = gfits_modify (&db.header, "IMAGEID", "%u", 1, imageID);
+ 
+
+  if (isEmpty) {
+    dvo_image_addrows (&db, NULL, 0);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+  } else {
+    // write just the PHU
+    // position to start of file
+    Fseek (db.f, 0, SEEK_SET);
+    SetProtect (TRUE);
+    if (!gfits_fwrite_header (db.f, &db.header)) {
+      Shutdown ("can't write image table phu %s", db.filename);
+    }
+    SetProtect (FALSE);
+  }
+
+  dvo_image_unlock (&db);
+
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/addstar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/addstar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/addstar.c	(revision 17422)
@@ -0,0 +1,205 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int Nmatch, status, loadObjects;
+  int i, Nstars, Nimages, Nsubset;
+  unsigned long long Naverage, Nmeasure;
+  Stars *stars, **subset;
+  Image *images;
+  Catalog catalog;
+  FITS_DB db;
+  AddstarClientOptions options;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  SkyList *newlist = NULL;
+
+  double dtime;
+  struct timeval start, stop;
+
+  gettimeofday (&start, NULL);
+
+  SetSignals ();
+  options = ConfigInit (&argc, argv);
+  options = args (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  if (sky == NULL) {
+      fprintf (stderr, "ERROR: unable to load sky table data\n");
+      exit (2);
+  }
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  stars = NULL;
+
+  /*** load in the new data (images, stars) ***/
+  switch (options.mode) {
+    case M_IMAGE:
+      stars = LoadStars (argv[1], &Nstars, &images, &Nimages, &options);
+
+      // set and update the imageID sequence
+      UpdateImageIDs (stars, Nstars, images, Nimages);
+
+      if ((DUMP != NULL) && !strcmp (DUMP, "rawstars")) dump_rawstars (stars, Nstars);
+      for (i = 0; i < Nimages; i++) {
+	newlist = SkyListByImage (sky, -1, &images[i]);
+	SkyListMerge (&skylist, newlist);
+	if (VERBOSE) fprintf (stderr, "added %d regions to yield %d total\n", newlist[0].Nregions, skylist[0].Nregions);
+	SkyListFree (newlist);
+      }
+      ImageOptions (&options, images, Nimages);
+      break;
+    case M_REFLIST:
+      stars = grefstars (argv[1], options.photcode, &Nstars);
+      skylist = SkyListForStars (sky, -1, stars, Nstars);
+      break;
+    case M_RESORT:
+    case M_REFCAT:
+      skylist = SkyListByPatch (sky, -1, &UserPatch);
+      break;
+    case M_FAKEIMAGE:
+      images = fakeimage (argv[1], &Nimages, options.photcode);
+      ALLOCATE (skylist, SkyList, 1);
+      skylist[0].Nregions = 0;
+      skylist[0].ownElements = FALSE;
+      break;
+
+    default:
+      fprintf (stderr, "ERROR: invalid mode\n");
+      exit (2);
+  }
+
+  // in these cases, limit the sky catalogs to an existing subset
+  if (options.only_match || options.existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+  if (VERBOSE) fprintf (stderr, "writing to %d regions\n", skylist[0].Nregions);
+
+  /* don't load the object tables for only_images, unless we are getting the calibration. */
+  loadObjects = !options.only_images || options.calibrate;
+
+  /* match stars to existing catalog data (or otherwise manipulate catalog data) */
+  Nmatch = Naverage = Nmeasure = 0;
+  for (i = 0; loadObjects && (i < skylist[0].Nregions); i++) {
+
+    // set the parameters which guide catalog open/load/create
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    if (options.update) catalog.catflags = LOAD_AVES | LOAD_MISS | LOAD_SECF;
+
+    // open as read or write, depending on desire
+    if (options.only_images && options.calibrate) {
+      if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "r")) {
+	continue;
+      }
+    } else {
+      // an error exit status here is a significant error (disk I/O or file access)
+      // XXX should this be "a" for options.update?
+      if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+	fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	exit (2);
+      }
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    // for only_match, skip empty catalogs
+    if ((catalog.Naves_disk == 0) && options.only_match) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    switch (options.mode) {
+      case M_IMAGE:
+	Nsubset = Nstars;
+	if (options.closest) {
+	  Nmatch += find_matches_closest (skylist[0].regions[i], stars, Nstars, &catalog, options);
+	} else {
+	  Nmatch += find_matches (skylist[0].regions[i], stars, Nstars, &catalog, options);
+	}
+	break;
+      case M_REFCAT:
+	stars = grefcat (argv[1], skylist[0].regions[i], options.photcode, &Nstars);
+      case M_REFLIST:
+	subset = find_subset (skylist[0].regions[i], stars, Nstars, &Nsubset);
+	Nmatch += find_matches_refstars (skylist[0].regions[i], subset, Nsubset, &catalog, options);
+	if (Nsubset) free (subset);
+	break;
+      case M_RESORT:
+	if (options.nosort == 3) catalog.sorted = FALSE;
+	resort_catalog (&catalog);
+	Nsubset = 1;
+	break;
+    }
+    /* report total updated values */
+    Naverage += catalog.Naverage;
+    Nmeasure += catalog.Nmeasure;
+
+    // write out catalog, if appropriate
+    if (Nsubset && !options.only_images) {
+      SetProtect (TRUE);
+      if (options.update) {
+	catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	dvo_catalog_update (&catalog, VERBOSE);
+      } else {
+	dvo_catalog_save (&catalog, VERBOSE);
+      }
+      SetProtect (FALSE);
+    }
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+
+    if (options.mode == M_REFCAT) free (stars);
+  }
+
+  // We only measure a single value for the entire mosaic (add all images to this function)
+  if (options.calibrate) { FindCalibration (&images[0]); }
+
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, GetZeroPoint());
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FORCE_READ)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+
+  /* add the new images and save */
+  if (options.mode == M_IMAGE) {
+    dvo_image_addrows (&db, images, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (&db, VERBOSE);
+    SetProtect (FALSE);
+  }
+  dvo_image_unlock (&db); /* unlock? */
+
+  gettimeofday (&stop, NULL);
+  dtime = DTIME (stop, start);
+  fprintf (stderr, "SUCCESS: elapsed time %9.4f sec for %5d stars (%5d matches), %6lld average, %7lld measure\n", dtime, Nstars, Nmatch, Naverage, Nmeasure);
+
+  exit (0);
+}
+
+/* names:
+   catalog - existing object db table
+   regions - sky area which may or may not contain data
+   patch   - RA,DEC bounded portion of sky
+*/
+
+// add in case of failures:
+// ohana_memcheck (FALSE);
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/addstarc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/addstarc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/addstarc.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int Nstars, Nimages;
+  int BindSocket;
+  AddstarClientOptions options;
+  Stars *stars;
+  Image *images;
+  Coords MOSAIC;
+
+  /* load config and options */
+  options = ConfigInit (&argc, argv);
+  options = args_client (argc, argv, options);
+
+  /* set up server connection */
+  BindSocket = GetClientSocket (HOSTNAME);
+  SendCommand (BindSocket, strlen(PASSWORD), PASSWORD);
+
+  /* send new data to server */
+  switch (options.mode) {
+    case M_IMAGE:
+      /* load data */
+      stars = LoadStars (argv[1], &Nstars, &images, &Nimages, &options);
+
+      // set and update the imageID sequence
+      UpdateImageIDs (stars, Nstars, images, Nimages);
+
+      /* send data to server */
+      SendCommand (BindSocket, 5, "IMAGE");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_Image (BindSocket, images, Nimages, FALSE);
+      if (options.mosaic) {
+	GetRegisteredMosaic (&MOSAIC);
+	Send_Coords (BindSocket, &MOSAIC, 1, FALSE);
+      }
+      Send_Stars (BindSocket, stars, Nstars, FALSE);
+      break;
+
+    case M_REFLIST:
+      /* load data */
+      stars = grefstars (argv[1], options.photcode, &Nstars);
+      
+      /* send data to server */
+      SendCommand (BindSocket, 5, "REFLS");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_Stars (BindSocket, stars, Nstars, FALSE);
+      break;
+
+    case M_REFCAT:
+      /* send data to server */
+      SendCommand (BindSocket, 5, "REFCT");
+      Send_AddstarClientOptions (BindSocket, &options, 1, TRUE);
+      Send_SkyRegion (BindSocket, &UserPatch, 1, TRUE);
+      SendMessage (BindSocket, argv[1]);
+      break;
+
+    default:
+      fprintf (stderr, "unknown addstar mode\n");
+      exit (1);
+  }
+  exit (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/addstard.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/addstard.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/addstard.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "addstar.h"
+
+int main (int argc, char **argv) {
+
+  int status, InitSocket, BindSocket;
+  SockAddress Address;
+  IOBuffer message;
+  AddstarClientOptions options;
+
+  options = ConfigInit (&argc, argv);
+  args_server (argc, argv);
+
+  /* store the sky table in a global for internal use */
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
+
+  /* if we separate the incoming data from db update, spawn db thread here */
+
+  VERBOSE = TRUE;
+  InitSocket = InitServerSocket (&Address);
+  
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address, VALID_IP, NVALID);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    /* accept command : XXX EAM : long-enough timeout? */
+    status = ExpectCommand (BindSocket, 5, 0.1, &message);
+    if (status != 0) {
+      if (VERBOSE) fprintf (stderr, "failed connection\n");
+      FreeIOBuffer (&message);
+      close (BindSocket);
+      continue;
+    }
+
+    /* message options */
+    if (!strcmp (message.buffer, "IMAGE")) {
+      fprintf (stderr, "Image\n");
+      NewImage (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFLS")) {
+      fprintf (stderr, "Reflist\n");
+      NewReflist (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "REFCT")) {
+      fprintf (stderr, "Refcat\n");
+      NewRefcat (BindSocket);
+      continue;
+    }
+    if (!strcmp (message.buffer, "EXIT")) {
+      fprintf (stderr, "Exit\n");
+      exit (2);
+    }
+  }    
+  exit (1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/addstart.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/addstart.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/addstart.c	(revision 17422)
@@ -0,0 +1,50 @@
+# include "addstar.h"
+# include <pthread.h>
+
+int main (int argc, char **argv) {
+
+  AddstarClientOptions options;
+  pthread_t thread;
+  DVO_DATA *dataset;
+
+  options = ConfigInit (&argc, argv);
+  args_server (argc, argv);
+
+  /* store the sky table in a global for internal use */
+  ServerSky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (ServerSky, CATDIR, "cpt");
+
+  VERBOSE = TRUE;
+
+  InitDataset ();
+
+  // launch thread to listen for client data
+  pthread_create (&thread, NULL, &ListenClients_Thread, NULL);
+
+  // XXX need to watch for shutdown message
+  while (1) {
+
+    dataset = PopDataset ();
+    if (dataset == NULL) {
+      usleep (50000);
+    }
+
+    switch (dataset[0].options[0].mode) {
+      case M_IMAGE:
+	UpdateDatabase_Image (dataset[0].options, dataset[0].images, dataset[0].Nimages, dataset[0].mosaic, dataset[0].stars, dataset[0].Nstars);
+	continue;
+
+      case M_REFLIST:
+	UpdateDatabase_Reflist (dataset[0].options, dataset[0].stars, dataset[0].Nstars);
+	continue;
+
+      case M_REFCAT:
+	UpdateDatabase_Refcat (dataset[0].options, dataset[0].patch, dataset[0].refcat);
+	continue;
+
+      default:
+	fprintf (stderr, "error: unexpected dataset\n");
+    }
+  }    
+  exit (1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/airmass.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/airmass.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/airmass.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "addstar.h"
+
+static int AirmassQuality = FALSE;
+
+void SetAirmassQuality (int quality) {
+  AirmassQuality = quality;
+}
+
+float airmass (float secz_image, double ra, double dec, double st, double latitude) {
+
+  double hour, cosz, secz;
+  double rdec, rlat;
+
+  if (!AirmassQuality) return (secz_image);
+
+  /*** make this optional? we may not have ST... ***/
+  /* ra, dec, latitude in dec deg; st in dec hours */
+
+  /* hour : hour angle in degrees */
+  rdec = RAD_DEG*dec;
+  rlat = RAD_DEG*latitude;
+  hour = 15.0*st - ra;
+  cosz = sin (rdec) * sin (rlat) + cos (rdec) * cos (RAD_DEG*hour) * cos (rlat);
+  secz = 1.000 / cosz;
+  return (secz);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args.c	(revision 17422)
@@ -0,0 +1,371 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args (int argc, char **argv, AddstarClientOptions options) {
+  
+  int i, N;
+  int QUALITY_AIRMASS;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_IMAGE;
+  if ((N = get_argument (argc, argv, "-ref"))) {
+    options.mode = M_REFLIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-cat"))) {
+    options.mode = M_REFCAT;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-fakeimage"))) {
+    options.mode = M_FAKEIMAGE;
+    remove_argument (N, &argc, argv);
+    FAKE_RA = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    FAKE_DEC = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    FAKE_THETA = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-resort"))) {
+    options.mode = M_RESORT;
+    remove_argument (N, &argc, argv);
+  }
+
+  options.filelist = FALSE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    options.filelist = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PMM_CCD_TABLE = NULL;
+  if ((N = get_argument (argc, argv, "-pmm"))) {
+    remove_argument (N, &argc, argv);
+    PMM_CCD_TABLE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+      if (options.mode == M_IMAGE) goto allow;
+      if (options.mode == M_FAKEIMAGE) goto allow;
+      if (options.mode == M_REFLIST) goto allow;
+      if (options.mode == M_REFCAT) {
+	  fprintf (stderr, "you have requested uploading from a catalog to the entire sky in one pass\n");
+      }
+      if (options.mode == M_RESORT) {
+	  fprintf (stderr, "you have requested resorting the entire sky in one pass\n");
+      }
+      fprintf (stderr, "this could be a time consuming operation.  type Ctrl-C within 5 seconds to cancel\n");
+      for (i = 5; i > 0; i--) {
+	  fprintf (stderr, "%d.. ", i);
+	  usleep (1000000);
+      }
+      fprintf (stderr, "\n");
+  }
+allow:
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a time for dataset */
+  TIMEREF = 0; 
+  if ((N = get_argument (argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    TIMEREF = tmp;
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a mosaic for distortion */
+  options.mosaic = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    Header header;
+    Coords MOSAIC;
+
+    remove_argument (N, &argc, argv);
+    if (!gfits_read_header (argv[N], &header)) {
+      fprintf (stderr, "ERROR: can't read header for mosaic %s\n", argv[N]);
+      exit (1);
+    }
+    if (!GetCoords (&MOSAIC, &header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    if (strcmp(&MOSAIC.ctype[4], "-DIS")) {
+      fprintf (stderr, "ERROR: not a mosaic distortion header\n");
+      exit (1);
+    }
+    RegisterMosaic (&MOSAIC);
+    remove_argument (N, &argc, argv);
+    gfits_free_header (&header);
+    options.mosaic = TRUE;
+  }
+  
+  /*** modify behavior ***/
+  /* only add to existing objects */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* don't add missed pts to Missed table (image only) */
+  options.skip_missed = TRUE;
+  if ((N = get_argument (argc, argv, "-missed"))) {
+    options.skip_missed = TRUE;
+    remove_argument (N, &argc, argv);
+    fprintf (stderr, "ERROR: addstar no longer supports -missed\n");
+    exit (2);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* use 'closest star' matching by default */
+  options.closest = TRUE;
+  if ((N = get_argument (argc, argv, "-closest"))) {
+    options.closest = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-all-matches"))) {
+    options.closest = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* don't re-sort the measure sequence */
+  options.nosort = FALSE;
+  if ((N = get_argument (argc, argv, "-nosort"))) {
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add new rows (-update) or re-write complete measure table (forces -nosort) */
+  options.update = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    options.update = TRUE;
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-force-sort"))) {
+    options.nosort = 3;  // temporary mode to mean 'force-sort'
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add image potion to image table */
+  options.only_images = FALSE;
+  if ((N = get_argument (argc, argv, "-image"))) {
+    options.only_images = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* apply average zpt offset calibration (image only) */
+  options.calibrate = FALSE;
+  if ((N = get_argument (argc, argv, "-cal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (FALSE);
+  }
+  if ((N = get_argument (argc, argv, "-excal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (FALSE);
+  }
+  if ((N = get_argument (argc, argv, "-incal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+    InitCalibration (TRUE);
+  }
+
+  /*** optional situations ***/
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  QUALITY_AIRMASS = FALSE;
+  if ((N = get_argument (argc, argv, "-quality-airmass"))) {
+    remove_argument (N, &argc, argv);
+    QUALITY_AIRMASS = TRUE;
+  }
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  SUBPIX = FALSE;
+  if ((N = get_argument (argc, argv, "-subpix"))) {
+    remove_argument (N, &argc, argv);
+    SUBPIX = TRUE;
+  }
+  /* skyprobe means: subpix correction and quality airmass */ 
+  if ((N = get_argument (argc, argv, "-skyprobe"))) {
+    remove_argument (N, &argc, argv);
+    QUALITY_AIRMASS = TRUE;
+    SUBPIX = TRUE;
+  }
+  SetAirmassQuality (QUALITY_AIRMASS);
+  if (SUBPIX) load_subpix ();
+
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-accept"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-accept-astrom"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* accept proper-motion data from reference */
+  ACCEPT_MOTION = FALSE;
+  if ((N = get_argument (argc, argv, "-accept-motion"))) {
+    ACCEPT_MOTION = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_TIME = FALSE;
+  if ((N = get_argument (argc, argv, "-accept-time"))) {
+    ACCEPT_TIME = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* skip the stars */
+  NO_STARS = FALSE;
+  if ((N = get_argument (argc, argv, "-no-stars"))) {
+    NO_STARS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  TEXTMODE = FALSE;
+  if ((N = get_argument (argc, argv, "-textmode"))) {
+    TEXTMODE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  DUMP = NULL;
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    remove_argument (N, &argc, argv);
+    DUMP = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((options.mode == M_RESORT) && (argc == 1)) return (options);
+  if (argc == 2) return (options);
+
+  if ((options.mode == M_REFLIST) && (options.photcode == 0)) {
+    fprintf (stderr, "photcode must be specified for -ref\n");
+    exit (2);
+  }
+
+  fprintf (stderr, "USAGE: addstar (filename)\n");
+  fprintf (stderr, "USAGE: addstar -cat (catalog)\n");
+  fprintf (stderr, "USAGE: addstar -ref (filename)\n");
+  fprintf (stderr, "USAGE: addstar -fakeimage (ra) (dec) (theta) (name)\n");
+  fprintf (stderr, "USAGE: addstar -resort (SkyRegion)\n");
+  exit (2);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstar (filename)\n");
+  fprintf (stderr, "     add specified image (cmp format) to database\n\n");
+  fprintf (stderr, "  addstar -ref (filename)");
+  fprintf (stderr, "     add ASCII data (ra dec mag dmag) to database\n\n");
+  fprintf (stderr, "  addstar -cat (catalog)");
+  fprintf (stderr, "     add data from catalog (USNO/2MASS/GSC) to database\n\n");
+  fprintf (stderr, "  addstar -resort (SkyRegion)");
+  fprintf (stderr, "     perform measure sorting for the specified catalog\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region ra ra dec dec 	  : only add data in specified region (-ref mode only)\n");
+  fprintf (stderr, "  -p (photcode)         	  : specify photcode (-ref / -cat / or override header)\n");
+  fprintf (stderr, "  -photcode (photcode)    	  : specify photcode (-ref / -cat / or override header)\n");
+  fprintf (stderr, "  -time (YYYY/MM/DD,HH:MM:SS) : specify date/time (override header)\n");
+  fprintf (stderr, "  -mosaic (filename)    	  : identify associated mosaic frame for chip image\n");
+  fprintf (stderr, "  -textmode                	  : input file is RAW TEXT table, not FITS table\n");
+  fprintf (stderr, "  -existing-regions           : only add measurements to existing catalog files\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -missed               	  : skipped 'missed' entries\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -closest             	  : use closest-star algorith\n");
+  fprintf (stderr, "  -nosort             	  : don't re-sort the measure entries (improves speed)\n");
+  fprintf (stderr, "  -update             	  : only update the new rows (forces -nosort)\n");
+  fprintf (stderr, "  -image                	  : only insert image data\n");
+  fprintf (stderr, "  -cal                  	  : perform zero-point calibration\n");
+  fprintf (stderr, "  -skyprobe             	  : specify skyprobe mode\n");
+  fprintf (stderr, "  -accept               	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -accept-astrom          	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -accept-motion           	  : accept proper-motion data from reference\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -dump (mode)          	  : output test data\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+/** addstar modes:
+ 
+    addstar (image.smp)  - add cmp/smp image data to db
+    addstar -ref (file.dat) (photcode) 
+    addstar -cat (USNO/2MASS/GSC) -region (ra dec - ra dec)
+
+    -replace : ref/cat - replace existing match (photcode/time)
+    -match   : ref/cat - only add measures to existing averages
+
+    ref types: 
+    ASCII - RA,DEC,M,dM in a table
+
+    addstar 
+
+**/
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args_client.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args_client.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args_client.c	(revision 17422)
@@ -0,0 +1,268 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_client (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_IMAGE;
+  if ((N = get_argument (argc, argv, "-ref"))) {
+    options.mode = M_REFLIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-cat"))) {
+    options.mode = M_REFCAT;
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a time for dataset */
+  options.timeref = 0; 
+  if ((N = get_argument (argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    options.timeref = tmp;
+    remove_argument (N, &argc, argv);
+  }
+  /* provide a mosaic for distortion */
+  options.mosaic = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    Header header;
+    Coords MOSAIC;
+
+    remove_argument (N, &argc, argv);
+    if (!gfits_read_header (argv[N], &header)) {
+      fprintf (stderr, "ERROR: can't read header for mosaic %s\n", argv[N]);
+      exit (1);
+    }
+    if (!GetCoords (&MOSAIC, &header)) {
+      fprintf (stderr, "ERROR: no astrometric solution in header\n");
+      exit (1);
+    }
+    if (strcmp(&MOSAIC.ctype[4], "-DIS")) {
+      fprintf (stderr, "ERROR: not a mosaic distortion header\n");
+      exit (1);
+    }
+    RegisterMosaic (&MOSAIC);
+    remove_argument (N, &argc, argv);
+    gfits_free_header (&header);
+    options.mosaic = TRUE;
+  }
+  
+  /*** modify behavior ***/
+  /* only add to existing objects */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* don't add missed pts to Missed table (image only) */
+  options.skip_missed = FALSE;
+  if ((N = get_argument (argc, argv, "-missed"))) {
+    options.skip_missed = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* use 'closest star' matching by default */
+  options.closest = TRUE;
+  if ((N = get_argument (argc, argv, "-closest"))) {
+    options.closest = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-all-matches"))) {
+    options.closest = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* don't re-sort the measure sequence */
+  options.nosort = FALSE;
+  if ((N = get_argument (argc, argv, "-nosort"))) {
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* only add new rows (-update) or re-write complete measure table (forces -nosort) */
+  options.update = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    options.update = TRUE;
+    options.nosort = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add image potion to image table */
+  options.only_images = FALSE;
+  if ((N = get_argument (argc, argv, "-image"))) {
+    options.only_images = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* apply average zpt offset calibration (image only) */
+  options.calibrate = FALSE;
+  if ((N = get_argument (argc, argv, "-cal"))) {
+    options.calibrate = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /*** optional situations ***/
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  options.quality_airmass = FALSE;
+  if ((N = get_argument (argc, argv, "-quality-airmass"))) {
+    remove_argument (N, &argc, argv);
+    options.quality_airmass = TRUE;
+  }
+  /* choose high quality airmass vs low quality airmass (per-star vs per-image) */
+  SUBPIX = FALSE;
+  if ((N = get_argument (argc, argv, "-subpix"))) {
+    remove_argument (N, &argc, argv);
+    SUBPIX = TRUE;
+  }
+  /* skyprobe means: subpix correction and quality airmass */ 
+  if ((N = get_argument (argc, argv, "-skyprobe"))) {
+    remove_argument (N, &argc, argv);
+    options.quality_airmass = TRUE;
+    SUBPIX = TRUE;
+  }
+  if (SUBPIX) load_subpix ();
+
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* accept bad header astrometry */
+  ACCEPT_ASTROM = FALSE;
+  if ((N = get_argument (argc, argv, "-accept"))) {
+    ACCEPT_ASTROM = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* over-ride autointerpretation of input data format */ 
+  TEXTMODE = FALSE;
+  if ((N = get_argument (argc, argv, "-textmode"))) {
+    TEXTMODE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  DUMP = NULL;
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    remove_argument (N, &argc, argv);
+    DUMP = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: addstarc (filename)\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstar (filename)\n");
+  fprintf (stderr, "     add specified image (cmp format) to database\n\n");
+  fprintf (stderr, "  addstar -ref (filename)");
+  fprintf (stderr, "     add ASCII data (ra dec mag dmag) to database\n\n");
+  fprintf (stderr, "  addstar -cat (catalog)");
+  fprintf (stderr, "     add data from catalog (USNO/2MASS/GSC) to database\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region ra ra dec dec 	  : only add data in specified region (-ref mode only)\n");
+  fprintf (stderr, "  -p (photcode)         	  : specify photcode (override header)\n");
+  fprintf (stderr, "  -time (YYYY/MM/DD,HH:MM:SS) : specify date/time (override header)\n");
+  fprintf (stderr, "  -mosaic (filename)    	  : identify associated mosaic frame for chip image\n");
+  fprintf (stderr, "  -fits                 	  : input file is FITS table, not TEXT table\n");
+  fprintf (stderr, "  -existing-regions           : only add measurements to existing catalog files\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -missed               	  : skipped 'missed' entries\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -closest             	  : use closest-star algorith\n");
+  fprintf (stderr, "  -nosort             	  : don't re-sort the measure entries (improves speed)\n");
+  fprintf (stderr, "  -update             	  : only update the new rows (foreces -nosort)\n");
+  fprintf (stderr, "  -image                	  : only insert image data\n");
+  fprintf (stderr, "  -cal                  	  : perform zero-point calibration\n");
+  fprintf (stderr, "  -skyprobe             	  : specify skyprobe mode\n");
+  fprintf (stderr, "  -accept               	  : accept bad astrometry from header\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -dump (mode)          	  : output test data\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+/** addstar modes:
+ 
+    addstar (image.smp)  - add cmp/smp image data to db
+    addstar -ref (file.dat) (photcode) 
+    addstar -cat (USNO/2MASS/GSC) -region (ra dec - ra dec)
+
+    -replace : ref/cat - replace existing match (photcode/time)
+    -match   : ref/cat - only add measures to existing averages
+
+    ref types: 
+    ASCII - RA,DEC,M,dM in a table
+
+    addstar 
+
+**/
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args_load2mass.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args_load2mass.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args_load2mass.c	(revision 17422)
@@ -0,0 +1,115 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_load2mass (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_REFCAT;
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* other addstar options which cannot be used in load2mass */
+  options.photcode = 0;
+  options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.existing_regions = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: load2mass\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE: load2mass [options]\n");
+  fprintf (stderr, "  add data from 2MASS catalog to fullsky\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region Rmin Rmax Dmin Dmax : update only this portion of the sky\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
+
+# if (0)
+  /* select quality flags */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+# endif
+
+# if (0)  
+  /* override any header PHOTCODE values */
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    options.photcode = GetPhotcodeCodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+# endif
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args_sedstar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args_sedstar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args_sedstar.c	(revision 17422)
@@ -0,0 +1,89 @@
+# include "addstar.h"
+static void help (void);
+
+AddstarClientOptions args_sedstar (int argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /*** check for command line options ***/
+
+  /* basic mode: image, list, refcat */
+  options.mode = M_REFCAT;
+
+  /*** provide additional data ***/ 
+  /* restrict to a portion of the sky? (UNUSED) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  PLOT = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* other defaults */
+  options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.existing_regions = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.only_match = FALSE;
+  options.replace = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: sedstar (sedtable) (outcatalog) [-region Rmin Rmax Dmin Dmax]\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  sedstar (SEDtable)");
+  fprintf (stderr, "     fit objects to stellar SEDs\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -region Rmin Rmax Dmin Dmax : sky region for analysis\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args_server.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args_server.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args_server.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "addstar.h"
+static void help (void);
+
+void args_server (int argc, char **argv) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* restrict to a portion of the sky? (REFCAT only) */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  /* define 2MASS quality flags to keep */
+  SELECT_2MASS_QUALITY = NULL;
+  if ((N = get_argument (argc, argv, "-2massquality"))) {
+    remove_argument (N, &argc, argv);
+    SELECT_2MASS_QUALITY = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* force read of image database with mismatched NSTARS & size */ 
+  FORCE_READ = FALSE;
+  if ((N = get_argument (argc, argv, "-force"))) {
+    FORCE_READ = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: addstard\n");
+    exit (2);
+  }
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  addstard\n");
+  fprintf (stderr, "  -force                	  : force read of database with inconsistent info\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/args_skycells.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/args_skycells.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/args_skycells.c	(revision 17422)
@@ -0,0 +1,109 @@
+# include "skycells.h"
+static void help (void);
+
+int args_skycells (int argc, char **argv) {
+  
+  int N;
+  char *ptr;
+
+  /* check for help request */
+  if (get_argument (argc, argv, "-help") ||
+      get_argument (argc, argv, "-h")) {
+    help ();
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* what type of output files? */
+  MODE = SQUARES;
+  if ((N = get_argument (argc, argv, "-triangles"))) {
+    MODE = TRIANGLES;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* what type of output files? */
+  FIX_NS = FALSE;
+  if ((N = get_argument (argc, argv, "-fix-ns"))) {
+    FIX_NS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  SCALE = 1.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    SCALE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  EULER_A = EULER_B = 0.0;
+  if ((N = get_argument (argc, argv, "-euler"))) {
+    remove_argument (N, &argc, argv);
+    EULER_A = RAD_DEG*atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    EULER_B = RAD_DEG*atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  PADDING = 0.0;
+  if ((N = get_argument (argc, argv, "-padding"))) {
+    remove_argument (N, &argc, argv);
+    PADDING = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* max number of skycells kept in memory */
+  NMAX = 200000;
+  if ((N = get_argument (argc, argv, "-nmax"))) {
+    remove_argument (N, &argc, argv);
+    NMAX = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* max number of skycells kept in memory */
+  NX_SUB = NY_SUB = 1;
+  if ((N = get_argument (argc, argv, "-nx"))) {
+    remove_argument (N, &argc, argv);
+    NX_SUB = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-ny"))) {
+    remove_argument (N, &argc, argv);
+    NY_SUB = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc == 2) {
+    strtol (argv[1], &ptr, 10);
+    if (*ptr == 0) return (TRUE);
+  }
+
+  fprintf (stderr, "USAGE: skycells (level) [-scale arcsec/pix] [-nx (Nx cells)] [-ny (Ny cells)]\n");
+  fprintf (stderr, "  [-h for details and other options]\n");
+  exit (2);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE\n");
+  fprintf (stderr, "  skycells\n\n");
+
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -triangles                  : save base triangles instead of skycells\n");
+  fprintf (stderr, "  -fix-ns                     : orient skycells with y-axis aligned with Dec\n");
+  fprintf (stderr, "  -scale                      : set pixel scale (default 1.0 arcsec / pixel)\n");
+  fprintf (stderr, "  -padding                    : pad skycells by this fraction in each dimension\n");
+  fprintf (stderr, "  -nmax                    	  : only keep nmax skycells in memory\n");
+  fprintf (stderr, "  -nx                    	  : subdivide skycell projection in x\n");
+  fprintf (stderr, "  -ny                    	  : subdivide skycell projection in y\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/build_links.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/build_links.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/build_links.c	(revision 17422)
@@ -0,0 +1,200 @@
+# include "addstar.h"
+
+/* build the initial links assuming the table is sorted, 
+   not partial, and has a correct set of average[].measureOffset,Nmeasure values */
+int *init_measure_links (Average *average, int Naverage, Measure *measure, int Nmeasure) {
+
+  int i, j, N;
+  int *next;
+
+  N = 0;
+
+  ALLOCATE (next, int, Nmeasure);
+  for (i = 0; i < Naverage; i++, N++) {
+    for (j = 0; j < average[i].Nmeasure - 1; j++, N++) {
+      next[N] = N + 1;
+      if (N >= Nmeasure) {
+	fprintf (stderr, "WARNING: N out of bounds (1)\n");
+      }
+    }
+    next[N] = -1;
+    if (N >= Nmeasure) {
+      fprintf (stderr, "WARNING: N out of bounds (2)\n");
+    }
+
+    if (N >= Nmeasure) {
+      fprintf (stderr, "overflow in init_measure_links\n");
+      abort ();
+    }
+  }
+  return (next);
+}
+
+/* build the initial links assuming the table is sorted */
+int *init_missing_links (Average *average, int Naverage, Missing *missing, int Nmissing) {
+
+  int i, j, N;
+  int *next;
+
+  N = 0;
+
+  ALLOCATE (next, int, Nmissing);
+  for (i = 0; i < Naverage; i++) {
+    for (j = 0; j < average[i].Nmissing - 1; j++, N++) {
+      next[N] = N + 1;
+    }
+    if (average[i].Nmissing > 0) {
+      next[N] = -1;
+      if (N >= Nmissing) {
+	fprintf (stderr, "overflow in init_missing_links");
+	abort ();
+      }
+      N++;
+    }
+
+  }
+  return (next);
+}
+
+/* average[].measureOffset, average[].Nmeasure are valid within an addstar run */
+int add_meas_link (Average *average, int *next, int Nmeasure, int NMEASURE) {
+
+  int k, m;
+
+  /* if we have trouble, check validity of next[m] : m < Nmeasure */
+  m = average[0].measureOffset;  
+
+  for (k = 0; k < average[0].Nmeasure - 1; k++)  {
+    m = next[m];
+    if (m >= NMEASURE) {
+      fprintf (stderr, "WARNING: m out of bounds (3)\n");
+    }
+  }
+
+  /* set up references */
+  next[Nmeasure] = -1;
+  if (Nmeasure >= NMEASURE) {
+    fprintf (stderr, "WARNING: Nmeasure out of bounds (1)\n");
+  }
+
+  if (m == -1) {
+    average[0].measureOffset = Nmeasure;
+  } else {
+    next[m] = Nmeasure;
+    if (m >= NMEASURE) {
+      fprintf (stderr, "WARNING: m out of bounds (4)\n");
+    }
+  }
+
+  return (TRUE);
+}
+
+int add_miss_link (Average *average, int *next, int Nmissing) {
+
+  int k, m;
+
+  /* there may be 0 Nmiss; this is not true for Nmeas */
+  if (average[0].Nmissing < 1) {
+    average[0].missingOffset = Nmissing;
+    next[Nmissing] = -1;
+    return (TRUE);
+  }
+
+  m = average[0].missingOffset;  
+  for (k = 0; k < average[0].Nmissing - 1; k++) m = next[m];
+  /* set up references */
+  next[Nmissing] = -1;
+  next[m] = Nmissing;
+  return (TRUE);
+}
+
+/* construct measure links which are valid FOR THIS LOAD
+ * - if we have a full load, we will get links which can
+ *   be used by other programs (eg, relphot, etc)
+ * - if we have a partial load, the links are only valid
+ *   for that partial load
+ */ 
+
+int *build_measure_links (Average *average, int Naverage, Measure *measure, int Nmeasure) {
+
+  int i, m, k, Nm, averef;
+  int *next;
+
+  ALLOCATE (next, int, Nmeasure);
+
+  /* reset the Nm, offset values for average */
+  for (i = 0; i < Naverage; i++) {
+    average[i].measureOffset = -1;
+    average[i].Nmeasure     =  0;
+  }
+
+  for (Nm = 0; Nm < Nmeasure; Nm++) {
+    averef = measure[Nm].averef;
+    m = average[averef].measureOffset;  
+    next[Nm] = -1;
+
+    if (m == -1) { /* no links yet for source */
+      average[averef].measureOffset = Nm;
+      average[averef].Nmeasure     = 1;
+      continue;
+    }
+
+    for (k = 0; next[m] != -1; k++) {
+      m = next[m];
+      if (m >= Nmeasure) {
+	fprintf (stderr, "WARNING: m out of bounds (1)\n");
+      }
+    }
+
+    average[averef].Nmeasure = k + 2;
+    next[m] = Nm;
+    if (m >= Nmeasure) {
+      fprintf (stderr, "WARNING: m out of bounds (2)\n");
+    }
+  }
+  return (next);
+}
+
+/* Missing does not carry enough information to reconstruct the links
+   we must always save the missing table, if it exists */
+
+Measure *sort_measure (Average *average, int Naverage, Measure *measure, int Nmeasure, int *next) {
+
+  int i, k, n, N;
+  Measure *tmpmeasure;
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeasure);
+  for (i = 0; i < Naverage; i++) {
+    n = average[i].measureOffset;
+    average[i].measureOffset = N;
+    for (k = 0; k < average[i].Nmeasure; k++, N++) {
+      tmpmeasure[N] = measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+    }
+  }
+  free (measure);
+  return (tmpmeasure);
+}
+
+Missing *sort_missing (Average *average, int Naverage, Missing *missing, int Nmissing, int *next) {
+
+  int i, k, n, N;
+  Missing *tmpmissing;
+
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmissing);
+  for (i = 0; i < Naverage; i++) {
+    n = average[i].missingOffset;
+    average[i].missingOffset = N;
+    for (k = 0; k < average[i].Nmissing; k++, N++) {
+      tmpmissing[N] = missing[n]; 
+      n = next[n];
+    }
+  }
+  free (missing);
+  return (tmpmissing);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/calibrate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/calibrate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/calibrate.c	(revision 17422)
@@ -0,0 +1,233 @@
+# include "addstar.h"
+
+static int InternalCal;
+static int Ncal, NCAL, *Nstar;
+static float *Mobs, *dMobs, *Mref, *dMref, *Minst;
+
+void InitCalibration (int mode) {
+
+  InternalCal = mode;
+
+  fprintf (stderr, "calibrating the image...\n");
+  Ncal = 0;
+  NCAL = 1000;
+  ALLOCATE (Mobs,  float,  NCAL);
+  ALLOCATE (dMobs, float, NCAL);
+  ALLOCATE (Mref,  float,  NCAL);
+  ALLOCATE (dMref, float,  NCAL);
+  ALLOCATE (Minst, float,  NCAL);
+  ALLOCATE (Nstar, int, NCAL);
+}
+  
+void SaveCalibration (float Mo, float dMo, float Mr, float dMr, float Mi, int N) {
+
+  Mobs[Ncal]  = Mo;
+  dMobs[Ncal] = dMo;
+  Mref[Ncal]  = Mr;
+  dMref[Ncal] = dMr;
+  Minst[Ncal] = Mi;
+  Nstar[Ncal] = N;
+  Ncal ++;
+
+  if (Ncal == NCAL) {
+    NCAL += 1000;
+    REALLOCATE (Mobs,  float, NCAL);
+    REALLOCATE (dMobs, float, NCAL);
+    REALLOCATE (Mref,  float, NCAL);
+    REALLOCATE (dMref, float, NCAL);
+    REALLOCATE (Minst, float, NCAL);
+    REALLOCATE (Nstar, int,   NCAL);
+  }
+}
+
+/* use the linked list to navigate the measures; safe for unsorted measures */
+void AddToCalibration (Average *average, SecFilt *secfilt, Measure *measure, Measure *new, int *next, int Nstar) {
+
+  int i, j, m, Nsec, found0, found1, found2;
+  float CalM0, CalM1, CalM2, dCalM;
+  float Mcal, color, factor, Minst;
+  short CalC0, CalC1, CalC2;
+
+  PhotCode *mycode;  // photcode of this measurement
+  PhotCode *incode;  // mycode.equiv (internal reference)
+  PhotCode *excode;  // incode.equiv (external reference)
+
+  found0 = found1 = found2 = FALSE;
+  CalM0 = CalM1 = CalM2 = dCalM = NAN;
+
+  // we have two options here: 
+  //  - calibrate to internal system (Mcal)
+  //  - calibrate to external system (Mref)
+
+  mycode = GetPhotcodebyCode (new[0].photcode);
+  incode = GetPhotcodebyCode (mycode[0].equiv);
+  excode = GetPhotcodebyCode (incode[0].equiv);
+
+  if (InternalCal) {
+    CalC0 = incode[0].code;
+    Nsec  = GetPhotcodeNsec (CalC0);
+  } else {
+    CalC0 = excode[0].code;
+    Nsec  = GetPhotcodeNsec (CalC0);
+  }
+  /* check if this reference code is an average magnitude */
+  if (Nsec != -1) {
+    CalM0 = secfilt[Nsec].M;
+    dCalM = secfilt[Nsec].dM;
+    found0 = TRUE;
+  }
+
+  CalC1 = mycode[0].c1;
+  Nsec  = GetPhotcodeNsec (CalC1);
+  if (Nsec != -1) {
+    CalM1 = secfilt[Nsec].M;
+    found1 = TRUE;
+  }
+
+  CalC2 = mycode[0].c2;
+  Nsec  = GetPhotcodeNsec (CalC2);
+  if (Nsec != -1) {
+    CalM2 = secfilt[Nsec].M;
+    found2 = TRUE;
+  }
+
+  if (!CalC1 && !CalC2) {
+    found1 = found2 = TRUE;
+    CalM1 = CalM2 = 0.0;
+  }
+
+  m = average[0].measureOffset;
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[m].photcode == CalC0) { 
+      found0 = TRUE; 
+      CalM0  = measure[m].M; 
+      dCalM  = measure[m].dM; 
+    }
+    if (measure[m].photcode == CalC1) { 
+      found1 = TRUE; 
+      CalM1  = measure[m].M; 
+    }
+    if (measure[m].photcode == CalC2) { 
+      found2 = TRUE; 
+      CalM2  = measure[m].M; 
+    }
+    if (found0 && found1 && found2) {
+      Mcal   = new[0].M + 0.001*mycode[0].C + mycode[0].K*(new[0].airmass - 1.0) - GetZeroPoint();
+      color  = CalM1 - CalM2 - 0.001*mycode[0].dX;
+      factor = color;
+      for (j = 0; j < mycode[0].Nc; j++) {
+	Mcal += mycode[0].X[j]*factor;
+	factor *= color;
+      }
+      if (!InternalCal) {
+	Mcal += 0.001*incode[0].C;
+      }
+      // if we want to apply a Mcal -> Mref color correction, we need the additional color term
+      Minst = new[0].M - GetZeroPoint() - new[0].dt;
+      SaveCalibration (Mcal, new[0].dM, CalM0, dCalM, Minst, Nstar);
+      if ((DUMP != NULL) && !strcmp (DUMP, "cal")) {
+	fprintf (stdout, "cal-match : %10.6f %10.6f : %7.4f %6.4f  %7.4f %6.4f   %7.4f : %7.4f %7.4f\n", average[0].R, average[0].D, Mcal, new[0].dM, CalM0, dCalM, Minst, new[0].airmass, color);
+      }
+      return;
+    }
+    m = next[m];
+  }
+  return;
+}
+
+void FindCalibration (Image *image) {
+
+  int i, MaxN, *Nlist, Nkeep;
+  float N, M1, M2, *Dmag, *dDmag;
+  float dMo, dMr, Mw, Dmed, W1, W2, NSigma;
+
+  /* reject multiple matched-stars */
+  /* find maximum value of Nstar[] */
+  MaxN = -1;
+  for (i = 0; i < Ncal; i++) {
+    MaxN = MAX (Nstar[i], MaxN);
+  }
+  if (MaxN == -1) {
+    fprintf (stderr, "no clean stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    return;
+  }
+  /* create a hash array from Nstar[] entries */
+  ALLOCATE (Nlist, int, MaxN + 1);
+  memset (Nlist, 0, MAX (0, MaxN*sizeof(int)));
+  for (i = 0; i < Ncal; i++) {
+    Nlist[Nstar[i]] ++;
+  }
+  
+  /* accumulate delta mags */
+  ALLOCATE (Dmag, float, Ncal);
+  ALLOCATE (dDmag, float, Ncal);
+  Nkeep = 0;
+  for (i = 0; i < Ncal; i++) {
+    /* if this entry has too many (or two few?) matches, skip it */
+    if (Nlist[Nstar[i]] != 1) continue;
+
+    /* clip by instrumental magnitude */
+    if (Minst[i] > CAL_INSTMAG_MAX) continue;
+    if (Minst[i] < CAL_INSTMAG_MIN) continue;
+    
+    /* XXX EAM: note the artificial 0.005 dmag here */
+    dMr = MAX (0.005, dMref[i]);
+    dMo = MAX (0.005, dMobs[i]);
+
+    Dmag[Nkeep] = (Mobs[i] - Mref[i]);
+    dDmag[Nkeep] = (dMr*dMr + dMo*dMo);
+    Nkeep ++;
+  }
+
+  if (Nkeep < 5) {
+    fprintf (stderr, "too few stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    return;
+  }
+  fsortpair (Dmag, dDmag, Nkeep);
+
+  /* take sort list of Dmag, find median */
+  Dmed = Dmag[(int)(0.5*Nkeep)];
+
+  /* exclude points with abs(Dmag - Dmed) / dDmag > 2.5 */
+
+  /* accumulate delta mags (25% - 75% of clipped range) */
+  W1 = 0.0;
+  W2 = 0.0;
+  M1 = 0.0;
+  M2 = 0.0;
+  N  = 0.0;
+  for (i = 0; i < Nkeep; i++) {
+    NSigma = fabs (Dmag[i] - Dmed) / sqrt (dDmag[i]);
+    if (NSigma > 2.5) continue;
+    W1 += Dmag[i] / dDmag[i];
+    W2 += 1 / dDmag[i];
+    M1 += Dmag[i];
+    M2 += SQ (Dmag[i]);
+    N  += 1.0; 
+  }
+
+  if (N > 1) {
+    M1 = M1 / N;
+    M2 = sqrt (fabs(M2/N - M1*M1));
+    Mw = W1 / W2;
+    fprintf (stdout, "STATUS: SUCCESS\n");
+    fprintf (stdout, "ZERO_POINT_MEAN      = %7.4f\n", M1);
+    fprintf (stdout, "ZERO_POINT_WTMEAN    = %7.4f\n", Mw);
+    fprintf (stdout, "ZERO_POINT_STDEV     = %7.4f\n", M2);
+    fprintf (stdout, "ZERO_POINT_PRECISION = %7.4f\n", M2 / sqrt (N));
+    fprintf (stdout, "ZERO_POINT_NSTARS    =    %4.0f\n", N);
+    // fprintf (stderr, "N: %.0f, mean: %f, wt mean: %f, stdev: %f, precision: %f\n", N, M1, Mw, M2, M2 / sqrt (N));
+    image[0].Mcal = M1;
+    image[0].dMcal = M2 / sqrt (N);
+    image[0].Mxxxx = N;
+  } else {
+    fprintf (stderr, "too few stars\n");
+    image[0].Mcal = 10.000;
+    image[0].dMcal = 10.000;
+    image[0].Mxxxx = 0;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/dump.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/dump.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/dump.c	(revision 17422)
@@ -0,0 +1,21 @@
+# include "addstar.h"
+
+int dump_rawstars (Stars *stars, int Nstars) {
+
+  int i;
+  FILE *f;
+
+  f = fopen ("stars.dat", "w");
+
+  for (i = 0; i < Nstars; i++) {
+    fprintf (f, "%4d  %10.6f %10.6f  %8.2f %8.2f  %7.2f %7.2f\n", 
+	     i, 
+	     stars[i].R, stars[i].D,
+	     stars[i].X, stars[i].Y,
+	     stars[i].M, stars[i].dM);
+  }
+
+  fclose (f);
+  exit (1);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/edge_check.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/edge_check.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/edge_check.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "addstar.h"
+
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/fakeimage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/fakeimage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/fakeimage.c	(revision 17422)
@@ -0,0 +1,199 @@
+# include "addstar.h"
+
+Image *fakeimage (char *rootname, int *Nimage, int photcode) {
+
+  int i, j, Nx, Ny, Nchips;
+  double pltscale, pixscale;
+  double Rmin, Rmax, Dmin, Dmax;
+  // double Xmin, Xmax, Ymin, Ymax;
+  double dX, dY, r, d;
+  char chipname[80], chipdata[256], name[80];
+  char *config;
+  Image *image;
+  e_time MosaicTime;
+  Coords MOSAIC;
+
+  /* this is a somewhat bogus method to set a time for the exposure */
+  struct timeval now;
+  long int seedval;
+
+  gettimeofday (&now, NULL);
+  seedval = now.tv_sec + now.tv_usec;
+  srand48(seedval);
+
+  MosaicTime = 0xffffffff * drand48();
+  fprintf (stderr, "time: %x\n", MosaicTime);
+
+  /* load in the camera layout file */
+  config = LoadConfigFile (CameraLayout);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera layout file %s\n", CameraLayout);
+    exit (1);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded camera layout file: %s\n", CameraLayout);
+
+  /* create a mosaic distortion structure */
+  strcpy (MOSAIC.ctype, "RA---DIS");
+  MOSAIC.crval1 = FAKE_RA;
+  MOSAIC.crval2 = FAKE_DEC;
+  
+  MOSAIC.crpix1 = MOSAIC.crpix2 = 0.0;
+  
+  /* mosaic 'pixels' are millimeters */
+  ScanConfig (config, "PLATE_SCALE",   "%lf", 0, &pltscale);
+  MOSAIC.cdelt1 = MOSAIC.cdelt2 = pltscale / 3600.0;
+
+  MOSAIC.pc1_1 =  cos(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc1_2 = -sin(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc2_1 =  sin(FAKE_THETA*RAD_DEG);
+  MOSAIC.pc2_2 =  cos(FAKE_THETA*RAD_DEG);
+
+  MOSAIC.Npolyterms = 3;
+  for (i = 0; i < 7; i++) {
+    MOSAIC.polyterms[i][0] = 0;
+    MOSAIC.polyterms[i][1] = 0;
+  }
+  ScanConfig (config, "DPLATE_X",   "%lf", 0, &pltscale);
+  MOSAIC.polyterms[3][0] = pltscale;  // L : X^3 Y^0
+  MOSAIC.polyterms[5][0] = pltscale;  // L : X^1 Y^2
+  ScanConfig (config, "DPLATE_Y",   "%lf", 0, &pltscale);
+  MOSAIC.polyterms[4][1] = pltscale;  // M : X^0 Y^0
+  MOSAIC.polyterms[6][1] = pltscale;  // M : X^2 Y^0
+
+  RegisterMosaic (&MOSAIC);
+
+  /* some basic data about the chisp */
+  ScanConfig (config, "NCHIPS", "%d", 0, &Nchips);
+  ScanConfig (config, "NAXIS1", "%d", 0, &Nx);
+  ScanConfig (config, "NAXIS2", "%d", 0, &Ny);
+  ScanConfig (config, "PIXEL_SCALE", "%lf", 0, &pixscale);
+
+  ALLOCATE (image, Image, Nchips + 1);
+  
+  Rmin = Rmax = Dmin = Dmax = 0;
+
+  /* define the chip images (1 - Nchips) */
+  for (i = 0; i < Nchips; i++) {
+    /* this is the addstar name for the chip in the camera */
+    sprintf (chipname, "CHIP.%03d", i);
+    ScanConfig (config, chipname, "%s", 0, chipdata);
+
+    sscanf (chipdata, "%s %lf %lf", chipname, &dX, &dY);
+    // if (VERBOSE) fprintf (stderr, "chip %s (%f,%f)\n", chipname, dX, dY);
+
+    /* this is the camera-specific name of a chip */
+    sprintf (name, "%s.%s", rootname, chipname);
+    strcpy (image[i+1].name, name);
+
+    strcpy (image[i+1].coords.ctype, "RA---WRP");
+    
+    image[i+1].coords.crval1 = dX*pixscale;
+    image[i+1].coords.crval2 = dY*pixscale;
+    
+    image[i+1].coords.crpix1 = image[i+1].coords.crpix2 = 0.0;
+    image[i+1].coords.cdelt1 = image[i+1].coords.cdelt2 = pixscale;
+
+    image[i+1].coords.pc1_1 = 1.0;
+    image[i+1].coords.pc1_2 = 0.0;
+    image[i+1].coords.pc2_1 = 0.0;
+    image[i+1].coords.pc2_2 = 1.0;
+
+    image[i+1].coords.Npolyterms = 0;
+    for (j = 0; j < 7; j++) {
+      image[i+1].coords.polyterms[j][0] = 0;
+      image[i+1].coords.polyterms[j][1] = 0;
+    }
+
+    image[i+1].sidtime  = 0.0;
+    image[i+1].latitude = 0.0;
+
+    image[i+1].cerror = 0.0;
+    
+    image[i+1].NX = Nx;
+    image[i+1].NY = Ny;
+
+    image[i+1].photcode = photcode;
+
+    image[i+1].exptime = 0.0;
+  
+    image[i+1].apmifit = 0.0;
+    image[i+1].dapmifit = 0.0;
+
+    image[i+1].detection_limit = 0.0;
+    image[i+1].saturation_limit = 0.0;
+    image[i+1].fwhm_x = 0.0;
+    image[i+1].fwhm_y = 0.0;
+    image[i+1].tzero = MosaicTime;
+    image[i+1].trate = 0;
+    image[i+1].secz = 1.0;
+    image[i+1].ccdnum = 0xff;
+
+    image[i+1].Mcal = 0.0;
+    image[i+1].Xm   = NAN_S_SHORT;
+    image[i+1].code = 0;
+
+    image[i+1].nstar = 0;
+    image[i+1].Myyyy = 0;
+
+    /* check if chip hits outer bounds of mosaic */
+    XY_to_RD (&r, &d, 0, 0, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, Nx, 0, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, 0, Ny, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+    XY_to_RD (&r, &d, Nx, Ny, &image[i+1].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+  }
+
+  /* define the mosaic image */
+  strcpy (image[0].name, rootname);
+
+  image[0].coords = MOSAIC;
+  strcpy (image[0].coords.ctype, MOSAIC.ctype);
+
+  image[0].sidtime  = 0.0;
+  image[0].latitude = 0.0;
+  image[0].cerror = 0.0;
+    
+  // RD_to_XY (&Xmax, &Ymax, Rmax, Dmax, MOSAIC);
+  // RD_to_XY (&Xmin, &Ymin, Rmin, Dmin, MOSAIC);
+  image[0].NX = Rmax - Rmin;
+  image[0].NY = Dmax - Dmin;
+
+  image[0].photcode = photcode;
+
+  image[0].exptime = 0.0;
+  image[0].apmifit = 0.0;
+  image[0].dapmifit = 0.0;
+  image[0].detection_limit = 0.0;
+  image[0].saturation_limit = 0.0;
+  image[0].fwhm_x = 0.0;
+  image[0].fwhm_y = 0.0;
+  image[0].tzero = MosaicTime;
+  image[0].trate = 0;
+  image[0].secz = 1.0;
+  image[0].ccdnum = 0xff;
+  image[0].Mcal = 0.0;
+  image[0].Xm   = NAN_S_SHORT;
+  image[0].code = 0;
+  image[0].nstar = 0;
+  image[0].Myyyy = 0;
+
+  // XXX need to set the imageID here
+
+  *Nimage = Nchips + 1;
+  return (image);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches.c	(revision 17422)
@@ -0,0 +1,396 @@
+# include "addstar.h"
+
+int find_matches (SkyRegion *region, Stars *stars, int NstarsIn, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, n, N, J, status, Nstars;
+  double RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next_meas;
+  int Nave, NAVE, Nmeas, NMEAS, Nmatch;
+  int Nsecfilt, Nsec;
+  unsigned int objID, catID;
+  Coords tcoords;
+
+  /* photcode data - must by of type DEP; options.photcode is equiv photcode for all input
+     images this function requires incoming stars to have the same photcode.equiv value.  if
+     this value is not a valid photcode (ie, 0), then no modification is made to the average 
+     magnitudes (Nsec will be -1) */
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  Nsec     = GetPhotcodeNsec (options.photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, NstarsIn);
+  ALLOCATE (Y1, double, NstarsIn);
+  ALLOCATE (N1, int,   NstarsIn);
+
+  /** allocate local arrays (catalog) **/
+  NAVE = Nave = catalog[0].Naverage;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int,   NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  Nmatch = 0;
+  NMEAS = Nmeas = catalog[0].Nmeasure;
+  
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ZEA projection has the
+   * advantage that every point in R,D has a mapping to a unique X,Y.  However, note that not all
+   * possible X,Y points map back to R,D and the local plate scale changes substantially far from
+   * the projection pole.  a better mapping might be ARC, not yet implemented (see
+   * coordops.update.c).  We use the center of the region (catalog) for crval1,2. 
+   */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  /* build spatial index (RA sort) */
+  Nstars = 0;
+  for (i = 0; i < NstarsIn; i++) {
+    status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].R, stars[i].D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", catalog[0].filename);
+    free (catalog[0].found);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return (0);
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+  
+  /* build spatial index (RA sort) */
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure */
+  if (catalog[0].sorted && (catalog[0].Nmeasure == catalog[0].Nmeas_disk)) {
+    next_meas = init_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  }    
+
+  /* choose a radius for matches (defined in args.c or ImageOptions.c) */
+  RADIUS = options.radius;
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+        if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+
+    /* negative dX: j is too large */
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    /* within match range; look for matches */
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+
+      /* make sure there is space for next entry */
+      if (Nmeas >= NMEAS) {
+	NMEAS = Nmeas + 1000;
+	REALLOCATE (next_meas, int, NMEAS);
+	REALLOCATE (catalog[0].measure, Measure, NMEAS);
+      }
+
+      Nmatch ++;
+      n = N2[J];
+      N = N1[i];
+
+      /* add to end of measurement list */
+      add_meas_link (&catalog[0].average[n], next_meas, Nmeas, NMEAS);
+
+      /** add measurements for this star **/
+      /** dR,dD now represent arcsec **/
+      catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      if (catalog[0].measure[Nmeas].dR > +180.0*3600.0) {
+	  // average on high end of boundary, move star up
+	  stars[N].R += 360.0;
+	  catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      }
+      if (catalog[0].measure[Nmeas].dR < -180.0*3600.0) {
+	  // average on low end of boundary, move star down
+	  stars[N].R -= 360.0;
+	  catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+      }
+      if (fabs(catalog[0].measure[Nmeas].dR) > 10*RADIUS) {
+	  fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+		   catalog[0].average[n].R, catalog[0].average[n].D, 
+		   stars[N].R, stars[N].D,
+		   X1[i], X2[J], 
+		   Y1[i], Y2[J]);
+      }
+      catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N].D);
+
+      catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+      catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+      catalog[0].measure[Nmeas].M        = stars[N].M;
+      catalog[0].measure[Nmeas].dM       = stars[N].dM;  /* error in input files stored in thousandths of mag */
+      catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+      catalog[0].measure[Nmeas].t        = stars[N].t;
+      catalog[0].measure[Nmeas].averef   = n;              /* this must be an absolute sequence number, if partial average is loaded */
+      catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+      catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+      catalog[0].measure[Nmeas].dbFlags  = 0;
+      catalog[0].measure[Nmeas].dt       = stars[N].dt;
+      catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+      catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+      catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+      catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+      catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+      catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+      catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+      catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+      catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+      catalog[0].measure[Nmeas].detID     = stars[N].detID;
+      catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+      catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+      catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+      catalog[0].measure[Nmeas].Map      = stars[N].Map;
+      catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+      catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+      catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+	
+      /* adds the measurement to the calibration if appropriate color terms are found */
+      /* we call this before (optionally) setting the average magnitude to avoid auto-correlations */
+      if (options.calibrate) {
+	AddToCalibration (&catalog[0].average[n], &catalog[0].secfilt[n*Nsecfilt], catalog[0].measure, &catalog[0].measure[Nmeas], next_meas, N);
+      }
+
+      /* set the average magnitude if not already set and if photcode.equiv is not 0 */
+      /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+      if (Nsec > -1) { 
+	if (isnan(catalog[0].secfilt[n*Nsecfilt+Nsec].M)) {
+	  catalog[0].secfilt[n*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+	}
+      }
+
+      /*** flag multiple stars */
+      /* this image star matches more than one catalog star */
+      if (stars[N].found > -1) {
+	catalog[0].measure[stars[N].found].dbFlags |= ID_MEAS_BLEND_MEAS;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS;
+      } 
+      if (stars[N].found == -2) { /* this image star matches a catalog star on a neighboring catalog */
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS_X;
+      } 
+      if (stars[N].found == -1) { /* this image star matches only this catalog star */
+	stars[N].found = Nmeas;  /* save first match, in case coincidences are found */
+      }
+      /* this catalog star matches more than one image star */
+      if (catalog[0].found[n] > -1) {
+	catalog[0].measure[catalog[0].found[n]].dbFlags |= ID_MEAS_BLEND_OBJ;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_OBJ;
+      } else {
+	catalog[0].found[n] = Nmeas;
+      }
+      /* Nm is updated, but not written out in -update mode (for existing entries)
+	 Nm is recalculated in build_meas_links if loaded table is not sorted */
+      catalog[0].average[n].Nmeasure ++;
+      Nmeas ++;
+
+      if (!options.update) {
+	/* in UPDATE mode, newly calculated coordinates are not saved */
+	update_coords (&catalog[0].average[n], &catalog[0].measure[0], next_meas);
+      }
+    }
+    i++;
+  }
+
+  /* incorporate unmatched image stars, if this star is in field of this catalog */
+  /* these new entries are all written out in UPDATE mode */ 
+  for (i = 0; (i < Nstars) && !options.only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+
+    N = N1[i];
+    if (stars[N].found >= 0) continue;
+    if (!IN_REGION (stars[N].R, stars[N].D)) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N].R;
+    catalog[0].average[Nave].D         	   = stars[N].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure      = 1;
+    catalog[0].average[Nave].Nmissing      = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    catalog[0].average[Nave].dR        = 0;
+    catalog[0].average[Nave].dD        = 0;
+    catalog[0].average[Nave].uR        = 0;
+    catalog[0].average[Nave].uD        = 0;
+    catalog[0].average[Nave].duR       = 0;
+    catalog[0].average[Nave].duD       = 0;
+    catalog[0].average[Nave].P         = 0;
+    catalog[0].average[Nave].dP        = 0;
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].dR       	= 0.0;
+    catalog[0].measure[Nmeas].dD       	= 0.0;
+    catalog[0].measure[Nmeas].M        	= stars[N].M;
+    catalog[0].measure[Nmeas].dM       	= stars[N].dM;
+    catalog[0].measure[Nmeas].Mcal  	= stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        	= stars[N].t;
+    catalog[0].measure[Nmeas].averef   	= Nave;           /* XXX EAM : must be absolute Nave if partial read */
+    catalog[0].measure[Nmeas].photcode  = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   	= stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags 	= 0;
+    catalog[0].measure[Nmeas].dt    	= stars[N].dt;
+    catalog[0].measure[Nmeas].airmass   = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = stars[N].imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map  	= stars[N].Map;
+    catalog[0].measure[Nmeas].FWx      	= 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      	= 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    	= (0xffff/360.0)*stars[N].df;
+    /* XXX replace df here with theta, right? */
+
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+	catalog[0].secfilt[Nave*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+    }
+
+    /* next[Nmeas] should always be -1 in this context (it is always the only
+       measurement for the star) */
+    stars[N].found = Nmeas;
+    next_meas[Nmeas] = -1;
+    Nmeas ++;
+    Nave ++;
+  }
+      
+  REALLOCATE (catalog[0].average, Average, Nave);
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+ 
+  if (options.nosort) {
+    catalog[0].sorted = FALSE;
+  } else {
+    catalog[0].sorted = TRUE;
+    catalog[0].measure = sort_measure (catalog[0].average, Nave, catalog[0].measure, Nmeas, next_meas);
+  }
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < NstarsIn; i++) {
+    if (stars[i].found > -1) {
+      stars[i].found = -2;
+    } else {
+      stars[i].found = -3;
+    }
+  }
+
+  /* check if the catalog has changed?  if no change, no need to write */
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmatch);
+
+  free (catalog[0].found);
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a catalog star is in an image or an image star is in the catalog:
+   
+   catalogs have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   catalog[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_closest.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_closest.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_closest.c	(revision 17422)
@@ -0,0 +1,399 @@
+# include "addstar.h"
+
+int find_matches_closest (SkyRegion *region, Stars *stars, int NstarsIn, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, n, N, J, Jmin, status, Nstars;
+  double RADIUS, RADIUS2, Rmin;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next_meas, *next_miss;
+  int Nave, NAVE, Nmeas, NMEAS, Nmiss, NMISS, Nmatch;
+  int Nsecfilt, Nsec;
+  unsigned int objID, catID;
+  Coords tcoords;
+
+  /* photcode data - must by of type DEP; options.photcode is equiv photcode for all input
+     images this function requires incoming stars to have the same photcode.equiv value.  if
+     this value is not a valid photcode (ie, 0), then no modification is made to the average 
+     magnitudes (Nsec will be -1) */
+
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  Nsec     = GetPhotcodeNsec (options.photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, NstarsIn);
+  ALLOCATE (Y1, double, NstarsIn);
+  ALLOCATE (N1, int,   NstarsIn);
+
+  /** allocate local arrays (catalog) **/
+  NAVE = Nave = catalog[0].Naverage;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int,   NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  Nmatch = 0;
+  NMEAS = Nmeas = catalog[0].Nmeasure;
+  NMISS = Nmiss = catalog[0].Nmissing;
+
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ZEA projection has the
+   * advantage that every point in R,D has a mapping to a unique X,Y.  However, note that not all
+   * possible X,Y points map back to R,D and the local plate scale changes substantially far from
+   * the projection pole.  a better mapping might be ARC, not yet implemented (see
+   * coordops.update.c).  We use the center of the region (catalog) for crval1,2. 
+   */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  Nstars = 0;
+  for (i = 0; i < NstarsIn; i++) {
+    status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].R, stars[i].D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", catalog[0].filename);
+    free (catalog[0].found);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return (0);
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+
+  /* build spatial index (RA sort) */
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure, missing */
+  if (catalog[0].sorted && (catalog[0].Nmeasure == catalog[0].Nmeas_disk)) {
+    // this version is only valid if we have done a full catalog load, and if the catalog
+    // is sorted while processed
+    next_meas = init_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (catalog[0].average, Nave, catalog[0].measure, Nmeas);
+  }    
+  next_miss = init_missing_links (catalog[0].average, Nave, catalog[0].missing, Nmiss);
+  /* missing MUST be written 'sorted', or not at all */
+
+  /* choose a radius for matches */
+  RADIUS = options.radius; /* provided by config */
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+    if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+    
+    /* negative dX: j is too large */
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    Jmin = -1;
+    Rmin = RADIUS2;
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      /* find closest match for this detection */
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+      if (dR < Rmin) {
+	Rmin = dR;
+	Jmin  = J;
+      }
+    }
+
+    /* no match, try next detection */ 
+    if (Jmin == -1) {
+      i++;
+      continue;
+    }
+
+    /*** a match is found, add to average, measure ***/
+
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+
+    Nmatch ++;
+    n = N2[Jmin];
+    N = N1[i];
+
+    /* add to end of measurement list */
+    add_meas_link (&catalog[0].average[n], next_meas, Nmeas, NMEAS);
+	
+    /** add measurements for this star **/
+    /** dR,dD now represent arcsec **/
+    catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    if (catalog[0].measure[Nmeas].dR > +180.0*3600.0) {
+      // average on high end of boundary, move star up
+      stars[N].R += 360.0;
+      catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    }
+    if (catalog[0].measure[Nmeas].dR < -180.0*3600.0) {
+      // average on low end of boundary, move star down
+      stars[N].R -= 360.0;
+      catalog[0].measure[Nmeas].dR = 3600.0*(catalog[0].average[n].R - stars[N].R);
+    }
+    if (fabs(catalog[0].measure[Nmeas].dR) > 10*RADIUS) {
+      fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+	       catalog[0].average[n].R, catalog[0].average[n].D, 
+	       stars[N].R, stars[N].D,
+	       X1[i], X2[Jmin], 
+	       Y1[i], Y2[Jmin]);
+    }
+    catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N].D);
+
+    /* XXX need to add dX, dY : need to load into stars[N].dX,dY */
+    /* XXX need to add stargal, Sky, dSky, qPSF, detID, imageID */
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].M        = stars[N].M;
+    catalog[0].measure[Nmeas].dM       = stars[N].dM;  /* error in input files stored in thousandths of mag */
+    catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        = stars[N].t;
+    catalog[0].measure[Nmeas].averef   = n;
+    catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags  = 0;
+    catalog[0].measure[Nmeas].dt       = stars[N].dt;
+    catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0;
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map       = stars[N].Map;
+
+    // XXX saturate range for FWx, FWy, theta
+    // XXX convert to unsigned int for these...
+    catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+	
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+      if (isnan(catalog[0].secfilt[n*Nsecfilt+Nsec].M)) {
+	catalog[0].secfilt[n*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+      }
+    }
+
+    /* adds the measurement to the calibration if appropriate color terms are found */
+    if (options.calibrate) {
+      AddToCalibration (&catalog[0].average[n], &catalog[0].secfilt[n*Nsecfilt], catalog[0].measure, &catalog[0].measure[Nmeas], next_meas, N);
+    }
+
+    /* if we choose to flag close encounters, see find_matches.c */
+    /* if we choose to calculate RA,DEC averages, see update_coords.c */
+
+    /* Nm is updated, but not written out in -update mode (for existing entries)
+       Nm is recalculated in build_meas_links if loaded table is not sorted */
+    stars[N].found = Nmeas;
+    catalog[0].found[n] = Nmeas;
+    catalog[0].average[n].Nmeasure ++;
+    Nmeas ++;
+    i++;
+  }
+
+  /** incorporate unmatched image stars, if this star is in field of this catalog **/
+  /* these new entries are all written out in UPDATE mode */ 
+  for (i = 0; (i < Nstars) && !options.only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next_meas, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+
+    N = N1[i];
+    if (stars[N].found >= 0) continue;
+    if (!IN_REGION (stars[N].R, stars[N].D)) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N].R;
+    catalog[0].average[Nave].D         	   = stars[N].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure  	   = 1;
+    catalog[0].average[Nave].Nmissing  	   = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    catalog[0].average[Nave].dR        = 0;
+    catalog[0].average[Nave].dD        = 0;
+    catalog[0].average[Nave].uR        = 0;
+    catalog[0].average[Nave].uD        = 0;
+    catalog[0].average[Nave].duR       = 0;
+    catalog[0].average[Nave].duD       = 0;
+    catalog[0].average[Nave].P         = 0;
+    catalog[0].average[Nave].dP        = 0;
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    catalog[0].measure[Nmeas].Xccd     = stars[N].X;
+    catalog[0].measure[Nmeas].Yccd     = stars[N].Y;
+
+    catalog[0].measure[Nmeas].dR       = 0.0;
+    catalog[0].measure[Nmeas].dD       = 0.0;
+    catalog[0].measure[Nmeas].M        = stars[N].M;
+    catalog[0].measure[Nmeas].dM       = stars[N].dM;
+    catalog[0].measure[Nmeas].Mcal     = stars[N].Mcal;
+    catalog[0].measure[Nmeas].t        = stars[N].t;
+    catalog[0].measure[Nmeas].averef   = Nave;
+    catalog[0].measure[Nmeas].photcode = stars[N].code;  /* photcode */
+    catalog[0].measure[Nmeas].dophot   = stars[N].dophot;  
+    catalog[0].measure[Nmeas].dbFlags  = 0;
+    catalog[0].measure[Nmeas].dt       = stars[N].dt;
+    catalog[0].measure[Nmeas].airmass  = stars[N].airmass;
+
+    catalog[0].measure[Nmeas].photFlags = stars[N].flags;
+    catalog[0].measure[Nmeas].qPSF      = stars[N].psfQual;
+    catalog[0].measure[Nmeas].psfChisq  = stars[N].psfChisq;
+    catalog[0].measure[Nmeas].crNsigma  = stars[N].crNsigma;
+    catalog[0].measure[Nmeas].extNsigma = stars[N].extNsigma;
+    catalog[0].measure[Nmeas].Sky       = stars[N].sky;
+    catalog[0].measure[Nmeas].dSky      = stars[N].dsky;
+
+    catalog[0].measure[Nmeas].stargal   = 0;
+
+    catalog[0].measure[Nmeas].detID     = stars[N].detID;
+    catalog[0].measure[Nmeas].imageID   = options.imageID;
+
+    catalog[0].measure[Nmeas].dXccd     = stars[N].dX;
+    catalog[0].measure[Nmeas].dYccd     = stars[N].dY;
+
+    catalog[0].measure[Nmeas].Map      = stars[N].Map;
+    catalog[0].measure[Nmeas].FWx      = 100*stars[N].fx;
+    catalog[0].measure[Nmeas].FWy      = 100*stars[N].fy;
+    catalog[0].measure[Nmeas].theta    = (0xffff/360.0)*stars[N].df;
+
+    /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+    /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+    if (Nsec > -1) { 
+	catalog[0].secfilt[Nave*Nsecfilt+Nsec].M = PhotCat (&catalog[0].measure[Nmeas]);
+    }
+
+    /* next[Nmeas] should always be -1 in this context (it is always the only
+       measurement for the star) */
+    stars[N].found = Nmeas;
+    next_meas[Nmeas] = -1;
+    Nmeas ++;
+    Nave ++;
+  }
+      
+  REALLOCATE (catalog[0].average, Average, Nave);
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+ 
+  if (options.nosort) {
+    catalog[0].sorted = FALSE;
+  } else {
+    catalog[0].sorted = TRUE;
+    catalog[0].measure = sort_measure (catalog[0].average, Nave, catalog[0].measure, Nmeas, next_meas);
+  }
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < NstarsIn; i++) {
+    if (stars[i].found > -1) {
+      stars[i].found = -2;
+    } else {
+      stars[i].found = -3;
+    }
+  }
+
+  /* check if the catalog has changed?  if no change, no need to write */
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmatch);
+
+  free (catalog[0].found);
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a catalog star is in an image or an image star is in the catalog:
+   
+   catalogs have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   catalog[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_refstars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_refstars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_matches_refstars.c	(revision 17422)
@@ -0,0 +1,387 @@
+# include "addstar.h"
+
+int find_matches_refstars (SkyRegion *region, Stars **stars, int Nstars, Catalog *catalog, AddstarClientOptions options) {
+
+  int i, j, k, n, m, N, J;
+  double RADIUS, RADIUS2;
+  double *X1, *Y1, *X2, *Y2;
+  double dX, dY, dR;
+  int *N1, *N2,  *next, *next_miss, last, last_miss;
+  int Nave, NAVE, Nmeas, NMEAS, Nmiss, NMISS, Nmatch;
+  unsigned int objID, catID;
+  Measure *tmpmeasure;
+  Missing *tmpmissing;
+  Coords tcoords;
+  int Nsecfilt;
+
+  /* photcode data -- should not have to modify secfilt / average */
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE (X1, double, Nstars);
+  ALLOCATE (Y1, double, Nstars);
+  ALLOCATE (N1, int,   Nstars);
+
+  /** allocate local arrays (catalog) **/
+  Nave = catalog[0].Naverage;
+  NAVE = Nave + 1000;
+  ALLOCATE (X2, double, NAVE);
+  ALLOCATE (Y2, double, NAVE);
+  ALLOCATE (N2, int, NAVE);
+  ALLOCATE (catalog[0].found, int, NAVE);
+  REALLOCATE (catalog[0].average, Average, NAVE);
+  REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  Nmatch = 0;
+  Nmeas = catalog[0].Nmeasure;
+  NMEAS = Nmeas + 1000;
+  ALLOCATE (next, int, NMEAS);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+  
+  Nmiss = catalog[0].Nmissing;
+  NMISS = Nmiss + 1000;
+  if ((NMISS < 1) || (NMISS > 1e10)) {
+      fprintf (stderr, "weird value for NMISS: %d\n", NMISS);
+  }
+  ALLOCATE (next_miss, int, NMISS);
+  REALLOCATE (catalog[0].missing, Missing, NMISS);
+  
+  // current max obj ID for this catalog
+  objID = catalog[0].objID;
+  catID = catalog[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels, sort by X */
+  /* reference for coords is catalog center */
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---ZEA");
+  tcoords.Npolyterms = 0;
+  
+  for (i = 0; i < Nstars; i++) {
+    RD_to_XY (&X1[i], &Y1[i], stars[i][0].R, stars[i][0].D, &tcoords);
+    N1[i] = i;
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+  
+  for (i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N2[i] = i;
+    catalog[0].found[N2[i]] = -1;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measurements */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+
+  for (i = 0; i < Nmiss - 1; i++) {
+    next_miss[i] = i+1;
+  }
+  next_miss[i] = -1;
+  last_miss = i;
+
+  /* choose a radius for matches */
+  if (options.radius == 0) {
+    RADIUS = 2.0; /* hardwired default for refstars */
+  } else {
+    RADIUS = options.radius; /* provided by config */
+  }
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nstars) && (j < Nave); ) {
+    
+    dX = X1[i] - X2[j];
+    if (dX <= -2*RADIUS) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*RADIUS) {
+      j++;
+      continue;
+    }
+
+    /* negative dX: j is too large, positive dX, i is too large */
+    for (J = j; (dX > -2*RADIUS) && (J < Nave); J++) {
+      dX = X1[i] - X2[J];
+      dY = Y1[i] - Y2[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+
+      Nmatch ++;
+      n = N2[J];
+      N = N1[i];
+      m = catalog[0].average[n].measureOffset;  
+
+      /** in replace mode, search for entry and replace values M, dM, R, D */
+      if (options.replace && replace_match (&catalog[0].average[n], &catalog[0].measure[m], stars[N])) continue;
+
+      /** insert star in measurement list */
+      /* find last measurement of this star */
+      for (k = 0; k < catalog[0].average[n].Nmeasure - 1; k++) m = next[m];
+      /* set up references */
+      next[Nmeas] = next[m];
+      next[m] = Nmeas;
+      /* last just was moved */
+      if (next[Nmeas] == -1) last = Nmeas;
+	
+      /** add measurements for this star **/
+      /** *** dR,dD now in arcsec *** **/
+      catalog[0].measure[Nmeas].dR       = 3600.0*(catalog[0].average[n].R - stars[N][0].R);
+      catalog[0].measure[Nmeas].dD       = 3600.0*(catalog[0].average[n].D - stars[N][0].D);
+      catalog[0].measure[Nmeas].M        = stars[N][0].M;
+      catalog[0].measure[Nmeas].dM       = stars[N][0].dM;
+      catalog[0].measure[Nmeas].Mcal     = 0;
+      catalog[0].measure[Nmeas].t        = (TIMEREF == 0) ? stars[N][0].t : TIMEREF; /** careful : time_t vs e_time **/
+      catalog[0].measure[Nmeas].averef   = n;
+      catalog[0].measure[Nmeas].photcode = stars[N][0].code;
+      catalog[0].measure[Nmeas].dophot   = 0;
+      catalog[0].measure[Nmeas].dbFlags  = 0;
+      catalog[0].measure[Nmeas].dt       = 0xffff;
+
+      catalog[0].measure[Nmeas].Map      = NAN;
+      catalog[0].measure[Nmeas].airmass  = 0;
+      catalog[0].measure[Nmeas].FWx      = NAN_S_SHORT;
+      catalog[0].measure[Nmeas].FWy      = NAN_S_SHORT;
+      catalog[0].measure[Nmeas].theta    = NAN_S_SHORT;
+	
+      catalog[0].measure[Nmeas].photFlags = 0;
+      catalog[0].measure[Nmeas].qPSF      = 0;
+      catalog[0].measure[Nmeas].psfChisq  = 0;
+      catalog[0].measure[Nmeas].crNsigma  = 0;
+      catalog[0].measure[Nmeas].extNsigma = 0;
+      catalog[0].measure[Nmeas].Sky       = 0;
+      catalog[0].measure[Nmeas].dSky      = 0;
+
+      catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+      catalog[0].measure[Nmeas].detID     = 0;
+      catalog[0].measure[Nmeas].imageID   = 0;
+
+      catalog[0].measure[Nmeas].dXccd     = 0;
+      catalog[0].measure[Nmeas].dYccd     = 0;
+
+      catalog[0].measure[Nmeas].Xccd     = 0.0;
+      catalog[0].measure[Nmeas].Yccd     = 0.0;
+
+      if (ACCEPT_MOTION) {
+	catalog[0].average[n].uR         = stars[N][0].uR;
+	catalog[0].average[n].uD         = stars[N][0].uD;
+	catalog[0].average[n].duR        = stars[N][0].duR;
+	catalog[0].average[n].duD        = stars[N][0].duD;
+	catalog[0].average[n].P          = stars[N][0].P;
+	catalog[0].average[n].dP         = stars[N][0].dP;
+      }
+
+      /** don't update average / secfilt values for REF photcodes **/
+
+      /*** handle multiple stars */
+      /* this image star matches more than one catalog star */
+      if (stars[N][0].found > -1) {
+	catalog[0].measure[stars[N][0].found].dbFlags |= ID_MEAS_BLEND_MEAS;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS;
+      } 
+      if (stars[N][0].found == -2) { /* this image star matches a catalog star on a neighboring catalog */
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_MEAS_X;
+      } 
+      if (stars[N][0].found == -1) { /* this image star matches only this catalog star */
+	stars[N][0].found = Nmeas;  /* save first match, in case coincidences are found */
+      }
+      /* this catalog star matches more than one image star */
+      if (catalog[0].found[n] > -1) {
+	catalog[0].measure[catalog[0].found[n]].dbFlags |= ID_MEAS_BLEND_OBJ;
+	catalog[0].measure[Nmeas].dbFlags |= ID_MEAS_BLEND_OBJ;
+      } else {
+	catalog[0].found[n] = Nmeas;
+      }
+
+      catalog[0].average[n].Nmeasure ++;
+      Nmeas ++;
+      if (Nmeas == NMEAS) {
+	NMEAS = Nmeas + 1000;
+	REALLOCATE (next, int, NMEAS);
+	REALLOCATE (catalog[0].measure, Measure, NMEAS);
+      }
+
+      update_coords (&catalog[0].average[n], &catalog[0].measure[0], next);
+    }
+    i++;
+  }
+
+  /* we don't add missed entries for refcat
+     (already in database, not refcat) */
+
+  /* incorporate unmatched refcat stars */
+  /* skip if we want to require matches
+     combined with -replace, this lets us keep 
+     the reference up-to-date with known stars only */
+
+  for (i = 0; (i < Nstars) && !options.only_match; i++) {
+    N = N1[i];
+    if (stars[N][0].found >= 0) continue;
+
+    catalog[0].average[Nave].R         	   = stars[N][0].R;
+    catalog[0].average[Nave].D         	   = stars[N][0].D;
+    catalog[0].average[Nave].Xp        	   = 0;
+    catalog[0].average[Nave].Nmeasure      = 1;
+    catalog[0].average[Nave].Nmissing      = 0;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    if (ACCEPT_MOTION) {
+      catalog[0].average[Nave].dR    = stars[N][0].dR;
+      catalog[0].average[Nave].dD    = stars[N][0].dD;
+      catalog[0].average[Nave].uR    = stars[N][0].uR;
+      catalog[0].average[Nave].uD    = stars[N][0].uD;
+      catalog[0].average[Nave].duR   = stars[N][0].duR;
+      catalog[0].average[Nave].duD   = stars[N][0].duD;
+      catalog[0].average[Nave].P     = stars[N][0].P;
+      catalog[0].average[Nave].dP    = stars[N][0].dP;
+    } else {
+      catalog[0].average[Nave].dR    = 0;
+      catalog[0].average[Nave].dD    = 0;
+      catalog[0].average[Nave].uR    = 0;
+      catalog[0].average[Nave].uD    = 0;
+      catalog[0].average[Nave].duR   = 0;
+      catalog[0].average[Nave].duD   = 0;
+      catalog[0].average[Nave].P     = 0;
+      catalog[0].average[Nave].dP    = 0;
+    }
+
+    catalog[0].average[Nave].objID     = objID;
+    catalog[0].average[Nave].catID     = catID;
+    objID ++;
+
+    for (j = 0; j < Nsecfilt; j++) {
+      catalog[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
+    }
+
+    catalog[0].measure[Nmeas].dR       = 0.0;
+    catalog[0].measure[Nmeas].dD       = 0.0;
+    catalog[0].measure[Nmeas].M        = stars[N][0].M;
+    catalog[0].measure[Nmeas].dM       = stars[N][0].dM;
+    catalog[0].measure[Nmeas].Mcal     = 0;
+    catalog[0].measure[Nmeas].t        = (stars[N][0].t == 0) ? TIMEREF : stars[N][0].t; /** careful : time_t vs e_time **/
+    catalog[0].measure[Nmeas].averef   = Nave;
+    catalog[0].measure[Nmeas].photcode = stars[N][0].code;
+    catalog[0].measure[Nmeas].dophot   = 0;
+    catalog[0].measure[Nmeas].dbFlags  = 0;
+    catalog[0].measure[Nmeas].dt       = 0xffff;
+
+    catalog[0].measure[Nmeas].photFlags = 0;
+    catalog[0].measure[Nmeas].qPSF      = 0;
+    catalog[0].measure[Nmeas].psfChisq  = 0;
+    catalog[0].measure[Nmeas].crNsigma  = 0;
+    catalog[0].measure[Nmeas].extNsigma = 0;
+    catalog[0].measure[Nmeas].Sky       = 0;
+    catalog[0].measure[Nmeas].dSky      = 0;
+
+    catalog[0].measure[Nmeas].stargal   = 0; // XXX not yet set
+
+    catalog[0].measure[Nmeas].detID     = 0;
+    catalog[0].measure[Nmeas].imageID   = 0;
+
+    catalog[0].measure[Nmeas].dXccd     = 0;
+    catalog[0].measure[Nmeas].dYccd     = 0;
+
+    catalog[0].measure[Nmeas].Xccd     = 0.0;
+    catalog[0].measure[Nmeas].Yccd     = 0.0;
+
+    catalog[0].measure[Nmeas].airmass  = 0;
+    catalog[0].measure[Nmeas].Map      = NAN;
+    catalog[0].measure[Nmeas].FWx      = NAN_S_SHORT;
+    catalog[0].measure[Nmeas].FWy      = NAN_S_SHORT;
+    catalog[0].measure[Nmeas].theta    = NAN_S_SHORT;
+
+    catalog[0].measure[Nmeas].Xccd     = 0.0;
+    catalog[0].measure[Nmeas].Yccd     = 0.0;
+
+    stars[N][0].found = Nmeas;
+    next[last] = Nmeas;
+    next[Nmeas] = -1;
+    last = Nmeas;
+    Nmeas ++;
+    if (Nmeas == NMEAS) {
+      NMEAS = Nmeas + 1000;
+      REALLOCATE (next, int, NMEAS);
+      REALLOCATE (catalog[0].measure, Measure, NMEAS);
+    }
+    Nave ++;
+    if (Nave == NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*catalog[0].Nsecfilt);
+    }
+  }
+      
+  free (catalog[0].found);
+  REALLOCATE (catalog[0].average, Average, Nave);
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeas);
+  for (i = 0; i < Nave; i++) {
+    n = catalog[0].average[i].measureOffset;
+    catalog[0].average[i].measureOffset = N;
+    for (k = 0; k < catalog[0].average[i].Nmeasure; k++, N++) {
+      tmpmeasure[N] = catalog[0].measure[n]; 
+      tmpmeasure[N].averef = i;
+      n = next[n];
+    }
+  }
+  free (catalog[0].measure);
+  catalog[0].measure = tmpmeasure;
+    
+  /* fix order of Missing (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmissing, Missing, Nmiss);
+  for (i = 0; i < Nave; i++) {
+    if (catalog[0].average[i].Nmissing > 0) {
+      n = catalog[0].average[i].missingOffset;
+      catalog[0].average[i].missingOffset = N;
+      for (k = 0; k < catalog[0].average[i].Nmissing; k++, N++) {
+	tmpmissing[N] = catalog[0].missing[n]; 
+	n = next_miss[n];
+      }
+    }
+  }
+  free (catalog[0].missing);
+  catalog[0].missing = tmpmissing;
+
+  /* note stars which have been found in this catalog */
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i][0].found > -1) {
+      stars[i][0].found = -2;
+    } else {
+      stars[i][0].found = -3;
+    }
+  }
+
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  free (next);
+  free (next_miss);
+
+  catalog[0].objID    = objID; // new max value, save on catalog close
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nmissing = Nmiss;
+  catalog[0].Nsecf_mem = Nave*Nsecfilt;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas, Nmiss: %d %d %d %d, (%d matches)\n", Nstars, Nave, Nmeas, Nmiss, Nmatch);
+  return (Nmatch);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_missing.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_missing.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_missing.c	(revision 17422)
@@ -0,0 +1,52 @@
+
+/* XXX I have dropped the -missed capability of addstar. This is an expensive operation which
+ * is only rarely needed.  It is more efficient to perform this operation as a crawler like
+ * relphot.  I am saving in this file the code which was used in find_matches to perform the
+ * missing matches.
+ */
+
+/** code to add references from all previous non-detection observations of this spot on the sky */
+for (j = 0; (j < Noverlap) && !options.skip_missed; j++) {
+  /* make sure there is space for next entry */
+  if (Nmiss >= NMISS) {
+    NMISS = Nmiss + 1000;
+    REALLOCATE (next_miss, int, NMISS);
+    REALLOCATE (catalog[0].missing, Missing, NMISS);
+  }
+  if (!FindMosaicForImage (overlap, Noverlap, j)) continue;
+  if (!in_image (catalog[0].average[Nave].R, catalog[0].average[Nave].D, &overlap[j])) continue;
+  add_miss_link (&catalog[0].average[Nave], next_miss, Nmiss);
+
+  /* get time of exposure of this portion of the image */
+  RD_to_XY (&X, &Y, catalog[0].average[Nave].R, catalog[0].average[Nave].D, &overlap[j].coords);	  
+  catalog[0].missing[Nmiss].t  = overlap[j].tzero + 1e-4*Y*overlap[j].trate;  /* rough guess at time */
+  catalog[0].average[Nave].Nn ++;
+  Nmiss ++;
+}
+
+/* add reference for undetected catalog stars */
+/* XXX allow this option only for single images? */
+if (!strcmp (&image[0].coords.ctype[4], "-WRP")) RegisterMosaic (mosaic);
+for (j = 0; (j < Nave) && !options.skip_missed; j++) {
+  n = N2[j];
+  if (catalog[0].found[n] < 0) { 
+    /* make sure there is space for next entry */
+    if (Nmiss >= NMISS) {
+      NMISS = Nmiss + 1000;
+      REALLOCATE (next_miss, int, NMISS);
+      REALLOCATE (catalog[0].missing, Missing, NMISS);
+    }
+
+    /* should the catalog star be on this image? project into image coords */
+    if (!in_image (catalog[0].average[n].R, catalog[0].average[n].D, image)) continue;
+    add_miss_link (&catalog[0].average[n], next_miss, Nmiss);
+
+    /* calculate time of exposure for this coordinate in the image */
+    RD_to_XY (&X, &Y, catalog[0].average[n].R, catalog[0].average[n].D, &image[0].coords);	  
+    catalog[0].missing[Nmiss].t  = image[0].tzero + 1e-4*Y*image[0].trate;  /* trate is in 0.1 msec / row */
+    catalog[0].average[n].Nn ++;
+    Nmiss ++;
+  }
+}
+  catalog[0].missing = sort_missing (catalog[0].average, Nave, catalog[0].missing, Nmiss, next_miss);
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_proper.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_proper.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_proper.c	(revision 17422)
@@ -0,0 +1,158 @@
+# include "addusno.h"
+
+find_proper (catstats, catalog, usnostats, usno, Nusno)
+CatStats catstats[];
+Catalog catalog[];
+USNOstats usnostats[];
+USNOdata usno[];
+int Nusno;
+{
+
+  int i, j, k, n, m, N, first_j;
+  double RADIUS2, PROPER2;
+  float *X1, *Y1, *X2, *Y2, *usnodist;
+  float dX, dY, dR, dR2;
+  int *N1, *N2,  *next, last;
+  int Nave, Nmeas, NMEAS, Nmatch;
+  unsigned int flags;
+  Measure *tmpmeasure;
+  Coords *tcoords;
+  int already_matched, far_enough;
+
+  X1 = catstats[0].X;
+  Y1 = catstats[0].Y;
+  N1 = catstats[0].N;
+  Nave = catalog[0].Naverage;
+
+  /* no need to do this twice!! */
+  ALLOCATE (usnodist, float, Nave);
+  memset (usnodist, 0, Nave*sizeof(float));
+
+  X2 = usnostats[0].X;
+  Y2 = usnostats[0].Y;
+  N2 = usnostats[0].N;
+
+ /* set up link listed pointers for new measurements */
+  Nmatch = 0;
+  Nmeas = catalog[0].Nmeasure;
+  NMEAS = Nmeas + 1000;
+  ALLOCATE (next, int, NMEAS);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+  /* set up pointers for linked list of measurements */
+  for (i = 0; i < Nmeas - 1; i++) {
+    next[i] = i+1;
+  }
+  next[i] = -1;
+  last = i;
+  
+  /* choose a radius for matches */
+  PROPER2 = PROPER*PROPER;
+  RADIUS2 = RADIUS*RADIUS;
+
+  /** find matched stars **/
+  for (i = j = 0; (i < Nave) && (j < Nusno); ) {
+    if (catalog[0].average[N1[i]].code & ID_MOVING) { 
+      /* this is not a star, skip */
+      i++;
+      continue;
+    }
+    if (catalog[0].average[N1[i]].Nm < 3) { 
+      /* may just be a noise spike, skip */
+      i++;
+      continue;
+    }
+    if (catalog[0].average[N1[i]].code & ID_USNO) {
+      /* already matched with USNO, skip this one */
+      i++;
+      continue;
+    }
+    dX = X1[i] - X2[j];
+    if (dX <= -2*PROPER) {
+      i++;
+      continue;
+    }
+    if (dX >= 2*PROPER) {
+      j++;
+      continue;
+    }
+    /* negative dX: j is too large, positive dX, i is too large */
+    first_j = j;
+    for (; (dX > -2*PROPER) && (j < Nusno); j++) {
+      dX = X1[i] - X2[j];
+      dY = Y1[i] - Y2[j];
+      dR = dX*dX + dY*dY;
+      if (dR < PROPER2) {  
+	n = N1[i];  /* N1 refers to the average[] list */
+	N = N2[j];  /* N2 refers to the usno[] list */
+	if (usnostats[0].match[N] > -1) 
+	  continue;
+	if ((catalog[0].average[n].code & ID_USNO) && (usnodist[i] < dR)) {
+	  /* existing USNO match is closer than this new one, skip this one */
+	  continue;
+	}
+	usnodist[i] = dR;
+	m = catalog[0].average[n].offset;  /* first measurement of this star */
+	for (k = 0; k < catalog[0].average[n].Nm - 1; k++)
+	  m = next[m];
+	next[Nmeas+1] = next[m]; /* insert 2 measurements in linked list */
+	next[Nmeas] = Nmeas + 1;
+	next[m] = Nmeas;
+	if (next[Nmeas+1] == -1) { /* last just was moved */
+	  last = Nmeas+1;
+	}
+	Nmatch ++;
+	
+	/** add measurements for this star **/
+	catalog[0].measure[Nmeas].dR  = 360000.0*(catalog[0].average[n].R - usno[N].R);
+	catalog[0].measure[Nmeas].dD  = 360000.0*(catalog[0].average[n].D - usno[N].D);
+	catalog[0].measure[Nmeas].M   = 1000.0*fabs(usno[N].r);
+	catalog[0].measure[Nmeas].Mcal= 0;    /* above measurement is exact */
+	catalog[0].measure[Nmeas].dM  = 100;  /* error in input files stored in thousandths of mag */
+	catalog[0].measure[Nmeas].t   = 0;    /* a flag: if 0, image is not in database */
+	catalog[0].measure[Nmeas].averef  = n;
+	catalog[0].measure[Nmeas].photcode = USNO_RED; 
+	catalog[0].measure[Nmeas+1].dR  = catalog[0].measure[Nmeas].dR;
+	catalog[0].measure[Nmeas+1].dD  = catalog[0].measure[Nmeas].dD;
+	catalog[0].measure[Nmeas+1].M   = 1000.0*fabs(usno[N].b);
+	catalog[0].measure[Nmeas+1].Mcal= 0;    /* above measurement is exact */
+	catalog[0].measure[Nmeas+1].dM  = 100;  /* error in input files stored in thousandths of mag */
+	catalog[0].measure[Nmeas+1].t   = 0;    /* a flag: if 0, image is not in database */
+	catalog[0].measure[Nmeas+1].averef  = n;
+	catalog[0].measure[Nmeas+1].photcode = USNO_BLUE; 
+	/* add flag in average to mark as matched with the USNO catalog */
+	catalog[0].average[n].code |= (ID_PROPER | ID_USNO);
+	
+	/* we add two measurement for each star: 1 in r, 1 in b */
+	catalog[0].average[n].Nm +=2;
+	Nmeas +=2;  
+	if (Nmeas == NMEAS - 2) {  /* just to be safe... */
+	  NMEAS = Nmeas + 1000;
+	  REALLOCATE (next, int, NMEAS);
+	  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  
+  REALLOCATE (catalog[0].measure, Measure, Nmeas);
+
+  /* fix order of Measure (memory intensive, but fast) */
+  N = 0; 
+  ALLOCATE (tmpmeasure, Measure, Nmeas);
+  for (i = 0; i < Nave; i++) {
+    n = catalog[0].average[i].offset;
+    catalog[0].average[i].offset = N;
+    for (k = 0; k < catalog[0].average[i].Nm; k++, N++) {
+      tmpmeasure[N] = catalog[0].measure[n]; 
+      n = next[n];
+    }
+  }
+  free (catalog[0].measure);
+  catalog[0].measure = tmpmeasure;
+    
+  catalog[0].Nmeasure = Nmeas;
+  if (VERBOSE) fprintf (stderr, "Nusno, Nave, Nmeas: %d %d %d, (%d matches)\n", Nusno, Nave, Nmeas, Nmatch);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/find_subset.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/find_subset.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/find_subset.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "addstar.h"
+
+/* find stars within this region */
+Stars **find_subset (SkyRegion *region, Stars *stars, int Nstars, int *Nsubset) {
+
+  int i, N, NSUBSET;
+  Stars **subset;
+  double RA0, RA1, DEC0, DEC1;
+
+  NSUBSET = 1000;
+  ALLOCATE (subset, Stars *, NSUBSET);
+
+  RA0  = region[0].Rmin;
+  RA1  = region[0].Rmax;
+  DEC0 = region[0].Dmin;
+  DEC1 = region[0].Dmax;
+
+  if (VERBOSE) fprintf (stderr, "subset for %f - %f, %f - %f\n", RA0, RA1, DEC0, DEC1);
+
+  /* find stars within ra,dec region */
+  for (i = N = 0; i < Nstars; i++) {
+    if (stars[i].R <  RA0)  continue;
+    if (stars[i].R >= RA1)  continue;
+    if (stars[i].D <  DEC0) continue;
+    if (stars[i].D >= DEC1) continue;
+
+    subset[N] = &stars[i];
+    N++;
+    if (N == NSUBSET - 1) {
+      NSUBSET += 1000;
+      REALLOCATE (subset, Stars *, NSUBSET);
+    }
+  }
+  *Nsubset = N;
+  return (subset);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass.c	(revision 17422)
@@ -0,0 +1,121 @@
+# include "addstar.h"
+# include "2mass.h"
+
+Stars *get2mass (SkyRegion *patch, int photcode, int mode, int *NSTARS) {
+  
+  char *path;
+  char gzname[1024];
+  int i, status, Nstars, Nrefcat; 
+  Stars    *stars;
+  Stars    *refcat;
+  SkyTable *sky;
+  struct stat filestat;
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+  if (photcode == TM_J) goto good_code;
+  if (photcode == TM_H) goto good_code;
+  if (photcode == TM_K) goto good_code;
+  Shutdown ("2MASS photcode not specified");
+
+good_code:
+  path = TWO_MASS_DIR_AS;
+  if (mode == 1) path = TWO_MASS_DIR_DR2;
+
+  // the accel.dat file has the raw filenames
+  // test if the file exists, or else try the .gz version
+  sky = get2mass_acc (patch, path, "accel.dat");
+  
+  Nstars = 0;
+  ALLOCATE (stars, Stars, 1);
+
+  for (i = 0; i < sky[0].Nregions; i++) {
+    refcat = NULL;
+    switch (mode) {
+      case 0:
+	// XXX put filename from table here
+	status = stat (sky[0].filename[i], &filestat);
+	if ((status == -1) && (errno == ENOENT)) {
+	  sprintf (gzname, "%s.gz", sky[0].filename[i]);
+	  refcat = get2mass_AS_data (&sky[0].regions[i], gzname, patch, photcode, &Nrefcat);
+	} else {
+	  refcat = get2mass_AS_rawdata (&sky[0].regions[i], sky[0].filename[i], patch, photcode, &Nrefcat);
+	}
+	if (VERBOSE) fprintf (stderr, "loaded %d stars from 2MASS (allsky) : %s\n", Nrefcat, sky[0].filename[i]);
+	break;
+      case 1:
+	refcat = get2mass_2DR_data (&sky[0].regions[i], sky[0].filename[i], patch, photcode, &Nrefcat);
+	if (VERBOSE) fprintf (stderr, "loaded %d stars from 2MASS (dr2)\n", Nrefcat);
+	break;
+    }
+
+    REALLOCATE (stars, Stars, MAX (1, Nstars + Nrefcat));
+    memcpy (&stars[Nstars], refcat, Nrefcat*sizeof(Stars));
+    Nstars += Nrefcat;
+
+    free (refcat);
+  }
+  
+  if (VERBOSE) fprintf (stderr, "loaded total %d stars from 2MASS\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+/* watch for patches which cross 0,360 boundary */
+SkyTable *get2mass_acc (SkyRegion *patch, char *path, char *accel) {
+
+  int Nregions, NREGIONS, Nrec;
+  char accelfile[1024], line[256], filename[128], datafile[256], **filenames;
+  FILE *f;
+  double Rs, Re, Ds, De;
+
+  SkyTable *sky;
+  SkyRegion *regions;
+
+  sprintf (accelfile, "%s/%s", path, accel);
+  f = fopen (accelfile, "r");
+  if (f == NULL) Shutdown ("can't read data from accelerator %s", accelfile);
+
+  Nregions = 0;
+  NREGIONS = 200;
+  ALLOCATE (regions, SkyRegion, NREGIONS);
+  ALLOCATE (filenames, char *, NREGIONS);
+
+  /* read in stars line-by-line */
+  while (scan_line (f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    sscanf (line, "%s %lf %lf %lf %lf %d", filename, &Rs, &Re, &Ds, &De, &Nrec);
+    Rs *= 15.0;
+    Re *= 15.0;
+    if (Rs > patch[0].Rmax) continue;
+    if (Re < patch[0].Rmin) continue;
+    if (Ds > patch[0].Dmax) continue;
+    if (De < patch[0].Dmin) continue;
+    regions[Nregions].Rmin = Rs;
+    regions[Nregions].Rmax = Re;
+    regions[Nregions].Dmin = Ds;
+    regions[Nregions].Dmax = De;
+    regions[Nregions].childE = Nrec; // a cheat since 2MASS only has one depth
+    fprintf (stderr, "choosing: %10.6f - %10.6f, %10.6f - %10.6f\n", Rs, Re, Ds, De);
+
+    sprintf (datafile, "%s/%s", path, filename);
+    filenames[Nregions] = strcreate (datafile);
+
+    Nregions ++;
+    if (Nregions >= NREGIONS) {
+	NREGIONS += 20;
+	REALLOCATE (regions, SkyRegion, NREGIONS);
+	REALLOCATE (filenames, char *, NREGIONS);
+    }
+  }    
+  fclose (f);
+
+  ALLOCATE (sky, SkyTable, 1);
+  sky[0].regions = regions;
+  sky[0].filename = filenames;
+  sky[0].Nregions = Nregions;
+  return (sky);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as.c	(revision 17422)
@@ -0,0 +1,128 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~16MB */
+# define NBYTE 0x1000000
+
+Stars *get2mass_AS_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int Nstars, NSTARS, Nbyte, Nextra;
+  Stars *stars;
+  gzFile gf;
+  char *buffer;
+  char *p, *q, *tmp;
+  double RA, DEC;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  get2mass_setup (photcode);
+
+  gf = gzopen (filename, "rb");
+  if (gf == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  /* I want to add a seek-ahead test to find a good starting position in the file
+     this is very expensive using gzseek / gzread.  */
+
+  Nextra = 0;
+  while ((Nbyte = gzread (gf, &buffer[Nextra], NBYTE-Nextra)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from gzipped file %s", filename);
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, ".");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+
+    while (1) {
+
+      get2mass_coords (p, &RA, &DEC, Nbyte - (p - buffer));
+
+      /* skip stars which are outside desired region */
+      if (DEC > DEC1) goto skip_star;
+      if (DEC < DEC0) goto skip_star;
+      if (RA <  RA0)  goto skip_star;
+      if (RA >  RA1)  goto skip_star;
+
+      get2mass_star (&stars[Nstars], p, Nbyte - (p - buffer));
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+
+    skip_star:
+      /* start of the next line */
+      tmp = p;
+      p = q + 1;
+      if (p - buffer == Nbyte) {
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - (p - buffer));
+      if (q == NULL) {
+	Nextra = Nbyte - (p - buffer);
+	memmove (buffer, p, Nextra);
+	break;
+      } 
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  gzclose (gf);
+  free (buffer);
+  *nstars = Nstars;
+  return (stars);
+}
+
+/* this just scans along in the file.  file is sorted by dec, so we 
+   should be skipping large chunks - but we would need to have
+   the size from the accel file (won't fit in SkyRegion) and need
+   to use gzseek, if it exists.
+*/
+/* don't bother to seek ahead : position is not sufficiently predictable 
+   and gzseek is as expensive as gzread */
+/* 
+   Noffset = region[0].Nrec * (patch[0].DEC[0] + 90) / 180.0;
+   gzseek (gf, Noffset * NBYTE, SEEK_SET);
+   Nbyte = gzread (gf, buffer, NLINE*NBYTE);
+*/
+
+# if (0)
+    /** need to re-think this test **/
+    if (0) {
+      /* search for end of last complete line */
+      p = memrchr (buffer, '\n', Nbyte);
+      if (p == NULL) Shutdown ("incomplete line in at end of file\n");
+
+      /* search for start of last complete line */
+      /* last block may be only one line */
+      q = memrchr (buffer, '\n', (p - buffer - 1));
+      if (q == NULL) {
+	q = buffer;
+      } else {
+	q ++;
+      }
+
+      /* skip past block not yet in range */
+      RA = strtod (q, NULL);
+      ptr = skipNbounds (q, '|', 1, Nbyte - (q - buffer));
+      DEC = strtod (ptr, NULL);
+      if (DEC < DEC0) continue;
+    }
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as_raw.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as_raw.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_as_raw.c	(revision 17422)
@@ -0,0 +1,89 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~16MB */
+# define NBYTE 0x1000000
+# define NBREC 330
+
+Stars *get2mass_AS_rawdata (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int Nstars, NSTARS, Nbyte, Nextra;
+  Stars *stars;
+  FILE *f;
+  char *buffer;
+  char *p, *q, *tmp;
+  double RA, DEC;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  get2mass_setup (photcode);
+
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+  // test if this is a raw datafile or gzipped...
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  Nextra = 0;
+  while ((Nbyte = fread (&buffer[Nextra], 1, NBYTE-Nextra, f)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from raw file %s", filename);
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, ".");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+
+    while (1) {
+
+      get2mass_coords (p, &RA, &DEC, Nbyte - (p - buffer));
+
+      /* skip stars which are outside desired region */
+      if (DEC > DEC1) goto skip_star;
+      if (DEC < DEC0) goto skip_star;
+      if (RA <  RA0)  goto skip_star;
+      if (RA >  RA1)  goto skip_star;
+
+      get2mass_star (&stars[Nstars], p, Nbyte - (p - buffer));
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+
+    skip_star:
+      /* start of the next line */
+      tmp = p;
+      p = q + 1;
+      if (p - buffer == Nbyte) {
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - (p - buffer));
+      if (q == NULL) {
+	Nextra = Nbyte - (p - buffer);
+	memmove (buffer, p, Nextra);
+	break;
+      } 
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  fclose (f);
+  free (buffer);
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_dr2.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_dr2.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_dr2.c	(revision 17422)
@@ -0,0 +1,111 @@
+# include "addstar.h"
+# include "2mass.h"
+
+# define NBYTE 302
+# define NLINE 30000
+
+Stars *get2mass_2DR_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars) {
+  
+  int i, Nstars, NSTARS, Nbyte, Nline;
+  Stars *stars;
+  gzFile gf;
+  char *buffer;
+  char line[303];
+  double RA, DEC, J, H, K, dJ, dH, dK;
+  double RA0, RA1, DEC0, DEC1;
+
+  ALLOCATE (buffer, char, (NBYTE*NLINE));
+
+  if (region == NULL) exit (2);
+  if (patch == NULL) exit (3);
+
+  RA0  = MAX (patch[0].Rmin, UserPatch.Rmin);
+  RA1  = MIN (patch[0].Rmax, UserPatch.Rmax);
+  DEC0 = MAX (patch[0].Dmin, UserPatch.Dmin);
+  DEC1 = MIN (patch[0].Dmax, UserPatch.Dmax);
+
+  fprintf (stderr, "overlap: %f - %f, %f - %f\n", RA0, RA1, DEC0, DEC1);
+
+  gf = gzopen (filename, "rb");
+  if (gf == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+
+  Nstars = 0;
+  NSTARS = 10000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  while ((Nbyte = gzread (gf, buffer, NLINE*NBYTE)) != 0) {
+    if (Nbyte ==  0) Shutdown ("error reading from gzipped file %s", filename);
+    if (Nbyte == -1) Shutdown ("error reading from gzipped file %s", filename);
+    if (Nbyte % NBYTE) Shutdown ("error reading complete line from gzipped file %s", filename);
+    Nline = Nbyte / NBYTE;
+
+    /* skip past block not yet in range */
+    sscanf (&buffer[NBYTE*(Nline - 1)], "%lf %lf", &RA, &DEC);
+    if (DEC < DEC0) continue;
+
+    memcpy (line, &buffer[NBYTE*(Nline-1)], NBYTE);
+    line[302] = 0;
+
+    for (i = 0; i < Nline; i++) {
+      
+      dparse (&RA,  1, &buffer[NBYTE*i+  0]);
+      dparse (&DEC, 2, &buffer[NBYTE*i+  0]);
+
+      /* dr2 is nicely sorted in dec order */
+      if (DEC > DEC1) goto finished;
+      if (DEC < DEC0) continue;
+      if (RA <  RA0) continue;
+      if (RA >  RA1) continue;
+
+      stars[Nstars].R  	  = RA;
+      stars[Nstars].D  	  = DEC;
+      stars[Nstars].t  	  = short_date_to_sec (&buffer[NBYTE*i + 164]);
+      stars[Nstars].found = -1;
+      stars[Nstars].detID   = 0;
+      stars[Nstars].imageID = 0;
+
+      if (photcode == TM_J) {
+	dparse (&J,  1, &buffer[NBYTE*i + 53]);
+	dparse (&dJ, 2, &buffer[NBYTE*i + 53]);
+	stars[Nstars].M    = J;
+	stars[Nstars].dM   = dJ;
+	stars[Nstars].code = TM_J;
+      }
+      if (photcode == TM_H) {
+	dparse (&H,  1, &buffer[NBYTE*i + 72]);
+	dparse (&dH, 2, &buffer[NBYTE*i + 72]);
+	stars[Nstars].M    = H;
+	stars[Nstars].dM   = dH;
+	stars[Nstars].code = TM_H;
+      }
+      if (photcode == TM_K) {
+	dparse (&K,  1, &buffer[NBYTE*i + 91]);
+	dparse (&dK, 2, &buffer[NBYTE*i + 91]);
+	stars[Nstars].M    = K;
+	stars[Nstars].dM   = dK;
+	stars[Nstars].code = TM_K;
+      }
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 5000);
+    }
+  }  
+finished:
+  gzclose (gf);
+  free (buffer);
+
+  *nstars = Nstars;
+  return (stars);
+}
+
+/* this just scans along in the file.  file is sorted by dec, so we should be
+   skipping large chunks - but we would need to have the size from the accel
+   file in (and need to use gzseek, if the data is compressed)
+*/
+/* don't bother to seek ahead : position is not sufficiently predictable 
+   and gzseek is as expensive as gzread */
+/* 
+   Noffset = region[0].Nrec * (patch[0].Dmin + 90) / 180.0;
+   gzseek (gf, Noffset * NBYTE, SEEK_SET);
+   Nbyte = gzread (gf, buffer, NLINE*NBYTE);
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_full.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_full.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_full.c	(revision 17422)
@@ -0,0 +1,276 @@
+# include "addstar.h"
+# include "2mass.h"
+
+// fill in the data for a JHK triplet star.  takes a pointer to the start of the line the
+// RA and DEC have already been set
+int get2mass_3star_full (Stars *star, char *line, int Nmax) {
+
+  char *ptr;
+  double dMfull;
+  double jd;
+
+  if (line == NULL) Shutdown ("format error in 2mass");
+
+  ptr = line;		      // ra  (assumed to be already set)
+  ptr = next2MASSfield (ptr); // dec (assumed to be already set)
+  ptr = next2MASSfield (ptr); // err_maj
+  star[0].fx = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // err_min
+  star[0].fy = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // err_ang
+  star[0].df = strtod (ptr, NULL);
+
+  star[2].fx = star[2].fx = star[0].fx;
+  star[2].fy = star[2].fy = star[0].fy;
+  star[2].df = star[2].df = star[0].df;
+
+  ptr = next2MASSfield (ptr); // designation (skip)
+
+  ptr = next2MASSfield (ptr); // j_m
+  star[0].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_cmsig
+  star[0].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[0].dMcal = sqrt (SQ(dMfull) - SQ(star[0].dM));
+  ptr = next2MASSfield (ptr); // j_snr (skip)
+
+  ptr = next2MASSfield (ptr); // h_m
+  star[1].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_cmsig
+  star[1].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[1].dMcal = sqrt (SQ(dMfull) - SQ(star[1].dM));
+  ptr = next2MASSfield (ptr); // h_snr (skip)
+
+  ptr = next2MASSfield (ptr); // k_m
+  star[2].M  = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_cmsig
+  star[2].dM = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_msigcom
+  dMfull = strtod (ptr, NULL);
+  star[2].dMcal = sqrt (SQ(dMfull) - SQ(star[2].dM));
+  ptr = next2MASSfield (ptr); // k_snr (skip)
+
+  star[2].flags = star[1].flags = star[0].flags = 0;
+
+  ptr = next2MASSfield (ptr); // ph_qual
+  set2MASS_ph_qual (&star[0], ptr[0]);
+  set2MASS_ph_qual (&star[1], ptr[1]);
+  set2MASS_ph_qual (&star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // rd_flg
+  set2MASS_rd_flag (&star[0], ptr[0]);
+  set2MASS_rd_flag (&star[1], ptr[1]);
+  set2MASS_rd_flag (&star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // bl_flg
+  set2MASS_bl_flag (&star[0], ptr[0]);
+  set2MASS_bl_flag (&star[1], ptr[1]);
+  set2MASS_bl_flag (&star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // cc_flg
+  set2MASS_cc_flag (&star[0], ptr[0]);
+  set2MASS_cc_flag (&star[1], ptr[1]);
+  set2MASS_cc_flag (&star[2], ptr[2]);
+
+  ptr = next2MASSfield (ptr); // ndet (skip for now, XXX use somehow?)
+  ptr = next2MASSfield (ptr); // prox (skip)
+  ptr = next2MASSfield (ptr); // pxpa (skip)
+  ptr = next2MASSfield (ptr); // pxcntr (skip)
+
+  ptr = next2MASSfield (ptr); // gal_contam (one flag for all filters)
+  set2MASS_gal_flag (&star[0], ptr[0]);
+  set2MASS_gal_flag (&star[1], ptr[0]);
+  set2MASS_gal_flag (&star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // mp_flg (one flag for all filters)
+  set2MASS_mp_flag (&star[0], ptr[0]);
+  set2MASS_mp_flag (&star[1], ptr[0]);
+  set2MASS_mp_flag (&star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // pts_key (skip for now, XXX use somehow?)
+  ptr = next2MASSfield (ptr); // hemis (skip)
+  ptr = next2MASSfield (ptr); // date (skip)
+  ptr = next2MASSfield (ptr); // scan (skip)
+  ptr = next2MASSfield (ptr); // glon (skip)
+  ptr = next2MASSfield (ptr); // glat (skip)
+
+  ptr = next2MASSfield (ptr); // x_scan
+  star[0].X = strtod (ptr, NULL);
+  star[2].X = star[1].X = star[0].X;
+
+  ptr = next2MASSfield (ptr); // jdate (julian date)
+  jd = strtod (ptr, NULL);
+  star[0].t = ohana_jd_to_sec (jd);
+  star[2].t = star[1].t = star[0].t;
+
+  ptr = next2MASSfield (ptr); // j_psfchi
+  star[0].psfChisq = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_psfchi
+  star[1].psfChisq = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_psfchi
+  star[2].psfChisq = strtod (ptr, NULL);
+
+  ptr = next2MASSfield (ptr); // j_m_stdap
+  star[0].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // j_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // h_m_stdap
+  star[1].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // h_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // k_m_stdap
+  star[2].Map = strtod (ptr, NULL);
+  ptr = next2MASSfield (ptr); // k_msig_stdap (skip?)
+
+  ptr = next2MASSfield (ptr); // dist_edge_ns (skip)
+  ptr = next2MASSfield (ptr); // dist_edge_ew (skip)
+  ptr = next2MASSfield (ptr); // dist_edge_flg (skip)
+
+  ptr = next2MASSfield (ptr); // dup_src (one flag for all filters)
+  set2MASS_dup_flag (&star[0], ptr[0]);
+  set2MASS_dup_flag (&star[1], ptr[0]);
+  set2MASS_dup_flag (&star[2], ptr[0]);
+
+  ptr = next2MASSfield (ptr); // use_src (one flag for all filters)
+  set2MASS_use_flag (&star[0], ptr[0]);
+  set2MASS_use_flag (&star[1], ptr[0]);
+  set2MASS_use_flag (&star[2], ptr[0]);
+
+  star[0].code  = TM_J;
+  star[0].found = -1;
+  star[0].detID   = 0;
+  star[0].imageID = 0;
+
+  star[1].code  = TM_H;
+  star[1].found = -1;
+  star[1].detID   = 0;
+  star[1].imageID = 0;
+
+  star[2].code  = TM_K;
+  star[2].found = -1;
+  star[2].detID   = 0;
+  star[2].imageID = 0;
+
+  return TRUE;
+}
+
+/* return a pointer to the first char after the next field separator (|) */
+char *next2MASSfield (char *line) {
+
+  char *p, *q;
+
+  p = line;
+  q = strchr (p, '|');
+  if (q == NULL) return (NULL);
+  p = q + 1;
+  if (*p == 0) return (NULL);
+  return (p);
+}
+
+int set2MASS_ph_qual (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'X': star[0].flags |= 0x0000; break;
+    case 'U': star[0].flags |= 0x0001; break;
+    case 'F': star[0].flags |= 0x0002; break;
+    case 'E': star[0].flags |= 0x0003; break;
+    case 'A': star[0].flags |= 0x0004; break;
+    case 'B': star[0].flags |= 0x0005; break;
+    case 'C': star[0].flags |= 0x0006; break;
+    case 'D': star[0].flags |= 0x0007; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_rd_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags |= 0x0000; break;
+    case '1': star[0].flags |= 0x0010; break;
+    case '2': star[0].flags |= 0x0020; break;
+    case '3': star[0].flags |= 0x0030; break;
+    case '4': star[0].flags |= 0x0040; break;
+    case '6': star[0].flags |= 0x0050; break;
+    case '9': star[0].flags |= 0x0060; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_cc_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'p': star[0].flags |= 0x0000; break;
+    case 'c': star[0].flags |= 0x0100; break;
+    case 'd': star[0].flags |= 0x0200; break;
+    case 's': star[0].flags |= 0x0300; break;
+    case 'b': star[0].flags |= 0x0400; break;
+    case '0': star[0].flags |= 0x0500; break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int set2MASS_bl_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0008; break;
+    case '1': star[0].flags &= ~0x0008; break;
+    default:  star[0].flags |=  0x0008; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_gal_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0080; break;
+    case '1': star[0].flags &= ~0x0080; break;
+    default:  
+      star[0].flags |= 0x0080; 
+      star[0].extNsigma = 100.0;
+      break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_mp_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x0800; break;
+    case '1': star[0].flags &= ~0x0800; break;
+    default:  star[0].flags |=  0x0800; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_dup_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x1000; break;
+    case '1': star[0].flags &= ~0x1000; break;
+    default:  star[0].flags |=  0x1000; break;
+  }      
+  return (TRUE);
+}
+
+int set2MASS_use_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].flags &= ~0x2000; break;
+    case '1': star[0].flags |=  0x2000; break;
+    default:  abort();
+  }      
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_ops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_ops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/get2mass_ops.c	(revision 17422)
@@ -0,0 +1,270 @@
+# include "addstar.h"
+# include "2mass.h"
+
+static int FilterSkip;
+static int TimeSkip;
+static int Qentry;
+static int Photcode;
+
+int get2mass_setup (int photcode) {
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+
+  FilterSkip = TimeSkip = Qentry = 0;
+  Photcode = photcode;
+  if (photcode == -1) return TRUE;
+
+  if (photcode == TM_J) {
+      FilterSkip = 6;
+      TimeSkip = 28;
+      Qentry   = 0;
+  }
+  if (photcode == TM_H) {
+      FilterSkip = 10;
+      TimeSkip = 24;
+      Qentry   = 1;
+  }
+  if (photcode == TM_K) {
+      FilterSkip = 14;
+      TimeSkip = 20;
+      Qentry   = 2;
+  }
+  if (!FilterSkip) Shutdown ("invalid photcode %s", GetPhotcodeNamebyCode(photcode));
+  return TRUE;
+}
+
+// fill in the data for a single star.  takes a pointer to the start of the line
+int get2mass_star (Stars *star, char *line, int Nmax) {
+
+  char *ptr, qc;
+  double M, dM;
+  e_time time;
+
+  ptr = skipNbounds (line, '|', FilterSkip, Nmax);
+  if (ptr == NULL) Shutdown ("format error in 2mass");
+  M  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dM = strtod (ptr, NULL);
+  time = get2mass_time (ptr, TimeSkip, Nmax - (ptr - line));
+
+  /* filter on the ph_qual flag for this filter (field 19) */
+  if (SELECT_2MASS_QUALITY != NULL) {
+    ptr = skipNbounds (line, '|', 18, Nmax);
+    qc  = ptr[Qentry];
+    if (strchr (SELECT_2MASS_QUALITY, qc) == NULL) return (FALSE);
+  }
+
+  star[0].M       = M;
+  star[0].dM      = dM;
+  star[0].code    = Photcode;
+  star[0].t       = time;
+  star[0].found   = -1;
+  star[0].detID   = 0;
+  star[0].imageID = 0;
+
+  return TRUE;
+}
+
+// fill in the data for a JHK triplet star.  takes a pointer to the start of the line
+int get2mass_3star (Stars *star, char *line, int Nmax) {
+
+  char *ptr;
+  char Jquality, Hquality, Kquality;
+  double J, dJ, H, dH, K, dK;
+  e_time time;
+
+  ptr = line;
+  if (ptr == NULL) Shutdown ("format error in 2mass");
+
+  ptr = skipNbounds (ptr, '|', 6, Nmax - (ptr - line));
+  J  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dJ = strtod (ptr, NULL);
+
+  ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+  H  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dH = strtod (ptr, NULL);
+
+  ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+  K  = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax - (ptr - line));
+  dK = strtod (ptr, NULL);
+
+  /* filter on the ph_qual flag for this filter (field 19) */
+  if (SELECT_2MASS_QUALITY != NULL) {
+    ptr = skipNbounds (ptr, '|', 3, Nmax - (ptr - line));
+    Jquality = (strchr (SELECT_2MASS_QUALITY, ptr[0]) != NULL);
+    Hquality = (strchr (SELECT_2MASS_QUALITY, ptr[1]) != NULL);
+    Kquality = (strchr (SELECT_2MASS_QUALITY, ptr[2]) != NULL);
+    time = get2mass_time (ptr, 18, Nmax - (ptr - line));
+  } else {
+    time = get2mass_time (ptr, 20, Nmax - (ptr - line));
+  }
+
+  // how many bits are being used for the 2mass flags; can we just set photFlags based on them?
+
+  star[0].M     = J;
+  star[0].dM    = dJ;
+  star[0].code  = TM_J;
+  star[0].t     = time;
+  star[0].found = -1;
+  star[0].detID   = 0;
+  star[0].imageID = 0;
+
+  star[1].M     = H;
+  star[1].dM    = dH;
+  star[1].code  = TM_H;
+  star[1].t     = time;
+  star[1].found = -1;
+  star[1].detID   = 0;
+  star[1].imageID = 0;
+
+  star[2].M     = K;
+  star[2].dM    = dK;
+  star[2].code  = TM_K;
+  star[2].t     = time;
+  star[2].found = -1;
+  star[2].detID   = 0;
+  star[2].imageID = 0;
+
+  return TRUE;
+}
+
+// fill in the coords for a single star.  takes a pointer to the start of the line
+int get2mass_coords (char *line, double *R, double *D, int Nmax) {
+
+  char *ptr;
+
+  *R = strtod (line, NULL);
+  ptr = skipNbounds (line, '|', 1, Nmax);
+  *D = strtod (ptr, NULL);
+  if (*D > 90) Shutdown ("weird DEC value: something is wrong");
+
+  return TRUE;
+}
+
+// this function retrieves the time from the DATE field
+e_time get2mass_date (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in 2mass");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in 2mass");
+  *end = 0;
+  time = ohana_date_to_sec (ptr);
+  *end = '|';
+
+  return (time);
+}
+
+// this function retrieves the time from the JDATE field (%12.4f)
+e_time get2mass_time (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  double jd;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in 2mass");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in 2mass");
+  *end = 0;
+  jd = strtod (p, NULL);
+  time = ohana_jd_to_sec (jd);
+  *end = '|';
+
+  return (time);
+}
+
+/* return a pointer to the first char after Nbound of value bound */
+char *skipNbounds (char *line, char bound, int Nbound, int Nbyte) {
+
+  int i;
+  char *p, *q;
+
+  p = line;
+  for (i = 0; i < Nbound; i++) {
+    q = memchr (p, bound, Nbyte - (p - line));
+    if (q == NULL) return (NULL);
+    p = q + 1;
+    if (p - line == Nbyte) return (NULL);
+  }
+  return (p);
+}
+  
+/* watch for patches which cross 0,360 boundary */
+SkyTable *load2mass_acc (char *path, char *accel) {
+
+  int Nregions, NREGIONS, Nrec;
+  char accelfile[1024], line[256], filename[128], datafile[256], **filenames;
+  FILE *f;
+  double Rs, Re, Ds, De;
+
+  SkyTable *sky;
+  SkyRegion *regions;
+
+  sprintf (accelfile, "%s/%s", path, accel);
+  f = fopen (accelfile, "r");
+  if (f == NULL) Shutdown ("can't read data from accelerator %s", accelfile);
+
+  Nregions = 0;
+  NREGIONS = 200;
+  ALLOCATE (regions, SkyRegion, NREGIONS);
+  ALLOCATE (filenames, char *, NREGIONS);
+
+  /* read in stars line-by-line */
+  while (scan_line (f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    sscanf (line, "%s %lf %lf %lf %lf %d", filename, &Rs, &Re, &Ds, &De, &Nrec);
+    Rs *= 15.0;
+    Re *= 15.0;
+
+    // don't restrict by RA, but limit by DEC
+    if (De < UserPatch.Dmin) continue;
+    if (Ds > UserPatch.Dmax) continue;
+
+    regions[Nregions].Rmin = Rs;
+    regions[Nregions].Rmax = Re;
+    regions[Nregions].Dmin = Ds;
+    regions[Nregions].Dmax = De;
+    regions[Nregions].childE = Nrec; // a cheat since 2MASS only has one depth
+
+    sprintf (datafile, "%s/%s", path, filename);
+    filenames[Nregions] = strcreate (datafile);
+
+    Nregions ++;
+    if (Nregions >= NREGIONS) {
+	NREGIONS += 20;
+	REALLOCATE (regions, SkyRegion, NREGIONS);
+	REALLOCATE (filenames, char *, NREGIONS);
+    }
+  }    
+  fclose (f);
+
+  ALLOCATE (sky, SkyTable, 1);
+  sky[0].regions = regions;
+  sky[0].filename = filenames;
+  sky[0].Nregions = Nregions;
+  return (sky);
+}
+
+int get2mass_sortStars (TMStars *tstars, int Ntstars) {
+
+# define SWAPFUNC(A,B){ TMStars temp = tstars[A]; tstars[A] = tstars[B]; tstars[B] = temp; }
+# define COMPARE(A,B)(tstars[A].R < tstars[B].R)
+
+  OHANA_SORT (Ntstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/getgsc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/getgsc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/getgsc.c	(revision 17422)
@@ -0,0 +1,95 @@
+# include "addstar.h"
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+static short GSC_M;
+static SkyTable *sky = NULL;
+
+Stars *getgsc (SkyRegion *patch, int *NSTARS) {
+  
+  int i, Ngsc, Nstars; 
+  Stars *gsc;
+  Stars *stars;
+  SkyList *skylist;
+
+  NAMED_PHOTCODE (GSC_M, "GSC");
+
+  /* load regions from GSC table, restrict to patch */
+  if (!sky) {
+    sky = SkyTableFromGSC (GSCFILE, SKY_DEPTH_HST, VERBOSE);
+    SkyTableSetFilenames (sky, GSCDIR, "cpt");
+  }
+  skylist = SkyListByPatch (sky, -1, patch);
+  
+  Nstars = 0;
+  ALLOCATE (stars, Stars, 1);
+  
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gsc = rd_gsc (skylist[0].filename[i], &Ngsc);
+
+    REALLOCATE (stars, Stars, MAX (1, Nstars + Ngsc));
+    memcpy (&stars[Nstars], gsc, Ngsc*sizeof(Stars));
+    Nstars += Ngsc;
+
+    free (gsc);
+  }
+  
+  // XXX don't free because we only load it once and save it
+  SkyListFree (skylist);
+  // SkyTableFree (sky);
+
+  if (VERBOSE) fprintf (stderr, "%d stars from HST GSC\n", Nstars);
+  *NSTARS = Nstars;
+  return (stars);
+}  
+
+Stars *rd_gsc (char *filename, int *nstars) {
+  
+  int i, NSTARS, Nstars, Nbytes, nbytes, Nline, Nbyte;
+  char *buffer;
+  FILE *f;
+  Stars *stars;
+
+  Nstars = 0;
+  NSTARS = 1000;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: can't find catalog file %s\n", filename);
+    exit (2);
+  }
+  
+  Nbytes = BLOCK*BYTES_STAR;
+  ALLOCATE (buffer, char, Nbytes);
+  while ((nbytes = fread (buffer, 1, Nbytes, f)) > 0) {
+    Nline = nbytes / BYTES_STAR;
+    for (i = 0; i < Nline; i++) {
+      Nbyte = i*BYTES_STAR;
+      dparse (&stars[Nstars].R, 1, &buffer[Nbyte]);
+      dparse (&stars[Nstars].D, 2, &buffer[Nbyte]);
+      if (stars[Nstars].R < UserPatch.Rmin) continue;
+      if (stars[Nstars].R > UserPatch.Rmax) continue;
+      if (stars[Nstars].D < UserPatch.Dmin) continue;
+      if (stars[Nstars].D > UserPatch.Dmax) continue;
+
+      dparse (&stars[Nstars].M, 3, &buffer[Nbyte]);
+      stars[Nstars].dM 	  = NAN;
+      stars[Nstars].t  	  = 0;
+      stars[Nstars].code  = GSC_M;
+      stars[Nstars].found = -1;
+
+      stars[Nstars].detID   = 0;
+      stars[Nstars].imageID = 0;
+
+      Nstars ++;
+      CHECK_REALLOCATE (stars, Stars, NSTARS, Nstars, 1000);
+    }
+  }
+  fclose (f);
+  free (buffer);
+  REALLOCATE (stars, Stars, Nstars);
+  *nstars = Nstars;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/gettycho.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/gettycho.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/gettycho.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "addstar.h"
+# define NZONE 180
+# define NBYTE 207
+# define NITEM 100
+
+static SkyTable *tychoTable = NULL;
+
+Stars *gettycho (SkyRegion *catstats, int photcode, int *nstars) {
+
+  int i, j, nitems;
+  char *buffer;
+  char filename[128], *line;
+  FILE *f;
+  int NTYCHO, Ntycho;
+  int firstRow, firstByte;
+  short int TYCHO_B, TYCHO_V;
+  e_time TychoEpoch;
+  SkyRegion *region;
+  SkyList  *skylist;
+  Stars *stars;
+
+  /* require photcode */
+  NAMED_PHOTCODE (TYCHO_B, "TYCHO_B");
+  NAMED_PHOTCODE (TYCHO_V, "TYCHO_V");
+  if (photcode == TYCHO_B) goto good_code;
+  if (photcode == TYCHO_V) goto good_code;
+  Shutdown ("TYCHO photcode not specified");
+good_code:
+
+  fprintf (stderr, "loading TYCHO catalog\n");
+
+  /* load tycho index file into sky table */
+  if (tychoTable == NULL) {
+    sprintf (filename, "%s/index.dat", TYCHO_DIR);
+    tychoTable = SkyTableFromTychoIndex (filename, VERBOSE);
+    if (tychoTable == NULL) {
+      fprintf (stderr, "ERROR: problem loading tycho data\n");
+      exit (2);
+    }
+  }
+
+  /* identify tycho region files overlapping the requested region */
+  skylist = SkyListByPatch (tychoTable, 2, catstats);
+
+  /* open the Tycho catalog file */
+  sprintf (filename, "%s/tycho.dat", TYCHO_DIR);
+  f = fopen (filename, "r");
+
+  Ntycho = 0;
+  NTYCHO = 5000;
+  ALLOCATE (stars, Stars, NTYCHO);
+
+  ALLOCATE (buffer, char, NITEM*NBYTE);
+
+  TychoEpoch = ohana_date_to_sec ("1991/04/02,07:30:00");
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    region = skylist[0].regions[i];
+    if (VERBOSE) fprintf (stderr, "section %d (%f - %f, %f - %f)...", i, 
+			  region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+    firstRow  = atoi (region[0].name);
+    firstByte = firstRow * NBYTE;
+    fseek (f, firstByte, SEEK_SET);
+
+    while (1) {
+      nitems = fread (buffer, NBYTE, NITEM, f);
+      if (nitems == 0) break;
+
+      for (j = 0; j < nitems; j++) {
+	line = &buffer[j*NBYTE];
+	stars[Ntycho].R = atof (&line[15]);
+	stars[Ntycho].D = atof (&line[28]);
+
+	if (stars[Ntycho].R < region[0].Rmin) goto next_section;
+	if (stars[Ntycho].R > region[0].Rmax) goto next_section;
+	if (stars[Ntycho].D < region[0].Dmin) goto next_section;
+	if (stars[Ntycho].D > region[0].Dmax) goto next_section;
+
+	if (stars[Ntycho].R < UserPatch.Rmin) continue;
+	if (stars[Ntycho].R > UserPatch.Rmax) continue;
+	if (stars[Ntycho].D < UserPatch.Dmin) continue;
+	if (stars[Ntycho].D > UserPatch.Dmax) continue;
+
+	stars[Ntycho].dR  = 1000 * atof (&line[57]);
+	stars[Ntycho].dD  = 1000 * atof (&line[64]);
+
+	/* XXX : we need to apply uR,uD to R,D to advance to 2000.0 */
+	stars[Ntycho].uR  = atof (&line[41]);
+	stars[Ntycho].uD  = atof (&line[49]);
+
+	stars[Ntycho].duR = atof (&line[69]);
+	stars[Ntycho].duD = atof (&line[75]);
+	
+	stars[Ntycho].P   = 0;
+	stars[Ntycho].dP  = 0;
+
+	stars[Ntycho].detID   = 0;
+	stars[Ntycho].imageID = 0;
+
+	/* Tycho uses J2000 equinox and 1991.25 epoch for coordinates */
+	/* the magnitudes have no temporal information */ 
+	stars[Ntycho].t   = TychoEpoch;
+	stars[Ntycho].found = -1;
+      
+	/* one pass of addstar does either r or b */
+	if (photcode == TYCHO_B) {
+	  stars[Ntycho].M     = atof (&line[110]);
+	  stars[Ntycho].dM    = atof (&line[117]);
+	  stars[Ntycho].code  = TYCHO_B;
+	} else {
+	  stars[Ntycho].M     = atof (&line[123]);
+	  stars[Ntycho].dM    = atof (&line[130]);
+	  stars[Ntycho].code  = TYCHO_V;
+	}	
+      
+	Ntycho ++;
+	CHECK_REALLOCATE (stars, Stars, NTYCHO, Ntycho, 5000);
+      }
+
+      if (nitems != NITEM) break;
+    }
+  next_section:
+    if (VERBOSE) fprintf (stderr, "%d stars\n", Ntycho);
+  }
+  fclose (f);
+
+  SkyListFree (skylist);
+
+  free (buffer);
+  REALLOCATE (stars, Stars, MAX (1, Ntycho));
+
+  *nstars = Ntycho;
+  if (VERBOSE) fprintf (stderr, "%d stars from Tycho\n", Ntycho);
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/getusno.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/getusno.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/getusno.c	(revision 17422)
@@ -0,0 +1,162 @@
+# include "addstar.h"
+# define NZONE 24
+# define NBYTE  4
+# define NELEM  3
+
+Stars *getusno (SkyRegion *catstats, int photcode, int *nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins, Nstars;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  int iRA0, iRA1, iDEC0, iDEC1;
+  double dec;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno;
+  Stars *stars;
+  short int USNO_RED, USNO_BLUE;
+
+  /* require photcode */
+  NAMED_PHOTCODE (USNO_RED, "USNO_RED");
+  NAMED_PHOTCODE (USNO_BLUE, "USNO_BLUE");
+  if (photcode == USNO_RED) goto good_code;
+  if (photcode == USNO_BLUE) goto good_code;
+  Shutdown ("USNO photcode not specified");
+good_code:
+
+  /* identify ra & dec range of interest */
+  /* note: the use of UserPatch to restrict here limits general utility of function */
+  iRA0  =  MAX (catstats[0].Rmin, UserPatch.Rmin) * 360000.0;
+  iRA1  =  MIN (catstats[0].Rmax, UserPatch.Rmax) * 360000.0;
+  iDEC0 = (MAX (catstats[0].Dmin, UserPatch.Dmin) + 90.0) * 360000.0;
+  iDEC1 = (MIN (catstats[0].Dmax, UserPatch.Dmax) + 90.0) * 360000.0;
+  
+  /* data is organized in south-pole distance zones */
+  spd_start = (int)((catstats[0].Dmin + 90) / 7.5) * 75.0;
+  dec = (catstats[0].Dmax + 90) / 7.5;
+  if (dec > (int)(dec)) {
+    spd_end =   (int)(1 + (catstats[0].Dmax + 90) / 7.5) * 75.0;
+  } else {
+    spd_end =   (int)(0 + (catstats[0].Dmax + 90) / 7.5) * 75.0;
+  }
+
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, Stars, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd += 75) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/zone%04d.acc", USNO_A_DIR, spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].Rmin / 3.75;
+    if ((catstats[0].Rmax / 3.75) == (int) (catstats[0].Rmax / 3.75)) 
+      last  = catstats[0].Rmax / 3.75;
+    else 
+      last  = 1 + catstats[0].Rmax / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/zone%04d.cat", USNO_A_DIR, spd);
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-A consists of 3 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    Nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      Nstars += number[bin];
+    }
+    Nitems = NELEM*Nstars;  
+    /* number of blocks to read -- we need to use Fread for byte-swapping read */
+
+    /* read stars from catalog */
+    ALLOCATE (buffer, int, Nitems);
+    nitems = Fread (buffer, NBYTE, Nitems, f, "int");
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    /* extract the data of interest from segment (in RA and DEC range) */
+    buf = buffer;
+    for (i = 0; i < Nstars; i++, buf += NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+
+      memset (&stars[Nusno], 0, sizeof(Stars));
+      stars[Nusno].R     = buf[0]/360000.0;
+      stars[Nusno].D     = buf[1]/360000.0 - 90.0;
+      stars[Nusno].dM    = NAN;
+      stars[Nusno].t     = 0;
+      stars[Nusno].found = -1;
+
+      stars[Nusno].detID   = 0;
+      stars[Nusno].imageID = 0;
+
+      /* one pass of addstar does either r or b */
+      if (photcode == USNO_RED) {
+	stars[Nusno].code  = USNO_RED;
+	stars[Nusno].M     = fabs (0.1*(buf[2] - 1000*((int)(buf[2]/1000))));
+      } 
+      if (photcode == USNO_BLUE) {	
+	stars[Nusno].code  = USNO_BLUE;
+	stars[Nusno].M     = fabs (0.1*((int)(buf[2] - 1000000*((int)(buf[2]/1000000))) / 1000));
+      }
+      Nusno ++;
+      CHECK_REALLOCATE (stars, Stars, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    fclose (f);
+  }
+  REALLOCATE (stars, Stars, Nusno);
+
+  *nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO 1.0\n", Nusno);
+  return (stars);
+}
+
+
+
+
+/* these entries are legacy code incase you want to read from one of the USNO CDRoms
+int SPDzone[]  = {0, 75, 450, 375, 1500, 1650, 300, 1425, 1725, 525, 1275, 225, 675, 150, 600, 1575, 750, 975, 900, 1050, 1125, 1200, 825, 1350};
+int USNOdisk[] = {1,  1,   1,   2,    2,    2,   3,    3,    3,   4,    4,   5,   5,   6,   6,    6,   7,   7,   8,    8,    9,    9,  10,   10};
+
+    disk = -1;
+    for (i = 0; i < NZONE; i++) {
+      if (spd == SPDzone[i]) 
+	disk = USNOdisk[i];
+    }
+    if (disk < 0) {
+      fprintf (stderr, "ERROR: can't find cdrom for spd %d\n",  spd);
+      exit (1);
+    }
+
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/getusnob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/getusnob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/getusnob.c	(revision 17422)
@@ -0,0 +1,193 @@
+# include "addstar.h"
+# define NBYTE   4
+# define NELEM  20
+
+Stars *getusnob (SkyRegion *catstats, int photcode, int *nstars) {
+
+  long int offset;
+  int i, bin, first, last, nitems, Nitems, Nbins, Nitemsum;
+  float hours[100];
+  int start[100], number[100], *buffer, *buf;
+  char filename[128];
+  FILE *f;
+  double dec;
+  float m1, m2, mag;
+  int iDEC0, iDEC1, iRA0, iRA1;
+  int spd, spd_start, spd_end;
+  int NUSNO, Nusno, Nstars;
+  short int USNO_RED, USNO_BLUE, USNO_N;
+  e_time USNOepoch;
+  Stars *stars;
+
+  m1 = m2 = 0.0;
+
+  /* require photcode */
+  NAMED_PHOTCODE (USNO_RED,  "USNO_RED");
+  NAMED_PHOTCODE (USNO_BLUE, "USNO_BLUE");
+  NAMED_PHOTCODE (USNO_N,    "USNO_N");
+  if (photcode == USNO_RED)  goto good_code;
+  if (photcode == USNO_BLUE) goto good_code;
+  if (photcode == USNO_N)    goto good_code;
+  Shutdown ("USNO photcode not specified");
+good_code:
+
+  fprintf (stderr, "loading USNO-B 1.0\n");
+
+  /* identify ra & dec range of interest */
+  /* note: the use of UserPatch to restrict here limits general utility of function */
+  iRA0  =  MAX (catstats[0].Rmin, UserPatch.Rmin) * 360000.0;
+  iRA1  =  MIN (catstats[0].Rmax, UserPatch.Rmax) * 360000.0;
+  iDEC0 = (MAX (catstats[0].Dmin, UserPatch.Dmin) + 90.0) * 360000.0;
+  iDEC1 = (MIN (catstats[0].Dmax, UserPatch.Dmax) + 90.0) * 360000.0;
+  /* note that iDECn is in SPD, while both have units of 0.01 degrees */
+  
+  /* data is organized in south-pole distance zones, 1 deg per direction, 0.1 deg per file */
+  spd_start = (int)(10*(catstats[0].Dmin + 90));
+  dec = 10*(catstats[0].Dmax + 90);
+  if (dec > (int)(dec)) {
+    spd_end =   (int)(1 + 10*(catstats[0].Dmax + 90));
+  } else {
+    spd_end =   (int)(0 + 10*(catstats[0].Dmax + 90));
+  }
+
+  Nitemsum = 0;
+  Nusno = 0;
+  NUSNO = 5000;
+  ALLOCATE (stars, Stars, NUSNO);
+
+  for (spd = spd_start; spd < spd_end; spd ++) {
+    
+    /* load accelerator file */
+    sprintf (filename, "%s/%03d/b%04d.acc", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open accelerator file %s\n", filename);
+      exit (1);  
+    }
+    for (i = 0; fscanf (f, "%f %d %d", &hours[i], &start[i], &number[i]) != EOF; i++);
+    Nbins = i;
+    fclose (f);
+    
+    first = catstats[0].Rmin / 3.75;
+    if ((catstats[0].Rmax / 3.75) == (int) (catstats[0].Rmax / 3.75)) 
+      last  = catstats[0].Rmax / 3.75;
+    else 
+      last  = 1 + catstats[0].Rmax / 3.75;
+
+    if ((first > Nbins) || (last > Nbins)) {
+      fprintf (stderr, "ERROR: RA out of range\n");
+      exit (1);
+    }
+    
+    /* open data file */
+    sprintf (filename, "%s/%03d/b%04d.cat", USNO_B_DIR, (int)(spd/10), spd); 
+    if (VERBOSE) fprintf (stderr, "reading from %s\n", filename);
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      fprintf (stderr, "ERROR: can't open file %s\n", filename);
+      exit (1);
+    }
+
+    /** USNO-B consists of 20 x 4byte (int) records **/
+    /* advance file pointer to first slice */
+    offset = NELEM*NBYTE*(start[first] - 1);
+    fseek (f, offset, SEEK_SET);
+
+    /* sum the number of stars in data segment of interest */
+    Nstars = 0;
+    for (bin = first; bin < last; bin++) {
+      Nstars += number[bin];
+    }
+    Nitems = NELEM*Nstars;  
+    /* number of blocks to read -- we need to use Fread for byte-swapping read */
+
+    /* allocate space for stars in segment and read */
+    ALLOCATE (buffer, int, Nitems);
+    // data has the WRONG byte order?
+    // nitems = Fread (buffer, sizeof(int), Nitems, f, "int");
+    nitems = fread (buffer, NBYTE, Nitems, f);
+    if (nitems != Nitems) {
+      fprintf (stderr, "ERROR: failure reading data from file %s\n", filename);
+      exit (1);
+    }
+
+    USNOepoch = ohana_date_to_sec ("2000/01/01,00:00:00");
+
+    buf = buffer;
+    /* print out data from slice within RA and DEC range */
+    for (i = 0; i < Nstars; i++, buf += NELEM) {
+      if (buf[0] < iRA0) continue;
+      if (buf[0] > iRA1) continue;
+      if (buf[1] < iDEC0) continue;
+      if (buf[1] > iDEC1) continue;
+      
+      /* USNO coords are reported for J2000 / epoch 2000.0 */
+      /* extract the basic stellar data */
+      memset (&stars[Nusno], 0, sizeof(Stars));
+      stars[Nusno].R   = buf[0]/360000.0;
+      stars[Nusno].D   = buf[1]/360000.0 - 90.0;
+
+      /* XXX uR cos(D) or just uR ??? */
+      stars[Nusno].uR  = 2.0 * ((buf[2]       % 10000) - 5000);
+      stars[Nusno].uD  = 2.0 * ((buf[2]/10000 % 10000) - 5000);
+
+      stars[Nusno].duR = (buf[3]      % 1000);
+      stars[Nusno].duD = (buf[3]/1000 % 1000);
+
+      stars[Nusno].dR  = 0.001 * (buf[4]      % 1000);
+      stars[Nusno].dD  = 0.001 * (buf[4]/1000 % 1000);
+
+      stars[Nusno].P   = 0;
+      stars[Nusno].dP  = 0;
+
+      /* USNO magnitude errors are reported as a fixed 0.3 mag */
+      stars[Nusno].dM    = 0.3;
+      stars[Nusno].found = -1;
+      
+      stars[Nusno].detID   = 0;
+      stars[Nusno].imageID = 0;
+
+      /* USNO-B uses J2000 equinox and 2000.0 epoch for coordinates */
+      /* the magnitudes have no temporal information */ 
+      stars[Nusno].t     = USNOepoch;
+
+      /* one pass of addstar does r, b, or n */
+      if (photcode == USNO_BLUE) {
+	  m1 = fabs(0.01 * (buf[5] % 10000)); /* 1st blue mag */
+	  m2 = fabs(0.01 * (buf[7] % 10000)); /* 1st blue mag */
+      }
+      if (photcode == USNO_RED) {
+	  m1 = fabs(0.01 * (buf[6] % 10000)); /* 1st red mag */
+	  m2 = fabs(0.01 * (buf[8] % 10000)); /* 2nd red mag */
+      }
+      if (photcode == USNO_N) {
+	  m1 = fabs(0.01 * (buf[9] % 10000)); /* N (IR) survey */
+	  m2 = 0;
+      }
+      stars[Nusno].code = photcode;
+
+      /* if two mags are available, get an average */
+      if (m1 && m2) {
+	mag = 0.5*(m1 + m2);
+      } else {
+	mag = (m1) ? m1 : m2;
+      }
+      stars[Nusno].M = (mag == 0.0) ? 32.0 : mag;
+
+      Nusno ++;
+      CHECK_REALLOCATE (stars, Stars, NUSNO, Nusno, 5000);
+    }
+    free (buffer);
+    Nitemsum += Nitems;
+    fprintf (stderr, "Nusno: %d, Nitems: %d, Nitemsum : %d, Nbytes stars: %d\n",
+	     Nusno, Nitems, Nitemsum, (unsigned int) (Nusno*sizeof(Stars)));
+    fclose (f);
+  }
+
+  REALLOCATE (stars, Stars, MAX (1, Nusno));
+
+  *nstars = Nusno;
+  if (VERBOSE) fprintf (stderr, "%d stars from USNO-B 1.0\n", Nusno);
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/greference.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/greference.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/greference.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "addstar.h"
+# define LOAD_ALLSKY 0
+# define LOAD_DR2    1
+
+Stars *grefcat (char *Refcat, SkyRegion *region, int photcode, int *nstars) {
+
+  int Nstars;
+  Stars *stars;
+
+  if (VERBOSE) fprintf (stderr, "loading reference catalog data from %s\n", Refcat); 
+  if (VERBOSE) fprintf (stderr, "full region: %f - %f, %f - %f\n", region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+  Nstars = 0;
+  stars = NULL;
+
+  /* get stars from USNO for the given region */
+  if (!strcasecmp (Refcat, "USNO")) {
+    stars = getusno (region, photcode, &Nstars);
+  }
+
+  /* get stars from the USNO B catalog for the given region */
+  if (!strcasecmp (Refcat, "USNOB")) {
+    stars = getusnob (region, photcode, &Nstars);
+  }
+
+  /* get stars from the USNO B catalog for the given region */
+  if (!strcasecmp (Refcat, "TYCHO")) {
+    stars = gettycho (region, photcode, &Nstars);
+  }
+
+  /* get stars from HST GSC for the given region */
+  if (!strcasecmp (Refcat, "GSC")) {
+    stars = getgsc (region, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS")) {
+    stars = get2mass (region, photcode, LOAD_ALLSKY, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS-ALLSKY")) {
+    stars = get2mass (region, photcode, LOAD_ALLSKY, &Nstars);
+  }
+  
+  /* get stars from 2MASS for the given region */
+  if (!strcasecmp (Refcat, "2MASS-DR2")) {
+    stars = get2mass (region, photcode, LOAD_DR2, &Nstars);
+  }
+  
+  if (VERBOSE && (Nstars == 0)) fprintf (stderr, "no ref objects in region %s\n", region[0].name);
+
+  *nstars = Nstars;
+  return (stars);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/grefstars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/grefstars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/grefstars.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "addstar.h"
+
+/* read ASCII file with ref star data */
+Stars *grefstars (char *file, int photcode, int *Nstars) {
+
+  FILE *f;
+  int N, NSTARS;
+  Stars *stars;
+  char line[256];
+
+  /* open file */
+  f = fopen (file, "r");
+  if (f == NULL) Shutdown ("can't read data from %s", file);
+
+  NSTARS = 100;
+  ALLOCATE (stars, Stars, NSTARS);
+
+  /* read in stars line-by-line */
+  for (N = 0; scan_line (f, line) != EOF; N++) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    dparse (&stars[N].R,  1, line);
+    dparse (&stars[N].D,  2, line);
+    dparse (&stars[N].M,  3, line);
+    dparse (&stars[N].dM, 4, line);
+    while (stars[N].R < 0.0) stars[N].R += 360.0;
+    while (stars[N].R >= 360.0) stars[N].R -= 360.0;
+    stars[N].t = 0;
+    stars[N].code = photcode;
+    stars[N].found = FALSE;
+    stars[N].detID   = 0;
+    stars[N].imageID = 0;
+    CHECK_REALLOCATE (stars, Stars, NSTARS, N+1, 100);
+  }
+  *Nstars = N;
+  return (stars);
+}
+
+/* stars.found is set here to FALSE.
+   find_match_refstars uses stars.found to identify the seq number of the
+   star which is found.  it requires an initial value of -1 XXX
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/gztest.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/gztest.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/gztest.c	(revision 17422)
@@ -0,0 +1,99 @@
+# include "ohana.h"
+# include "zlib.h"
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+main (int argc, char **argv) {
+
+  int N, Nread, Nseek, Nback;
+  char *filename, *buffer;
+  gzFile gf;
+  FILE *f;
+  struct timeval start, stop;
+
+  if (argc != 6) {
+    fprintf (stderr, "USAGE: gztest (mode) (file) (Nseek) (Nread) (Nback)\n");
+    exit (2);
+  }
+
+  filename = argv[2];
+  Nseek = atoi (argv[3]);
+  Nread = atoi (argv[4]);
+  Nback = -1 * atoi (argv[5]);
+  ALLOCATE (buffer, char, Nread);
+
+  if (!strcmp (argv[1], "gz")) {
+    gf = gzopen (filename, "rb");
+    if (gf == NULL) {
+      fprintf (stderr, "can't read data file: %s", filename);
+      exit (1);
+    }
+
+    gettimeofday (&start, NULL);
+    N = gzseek (gf, Nseek, SEEK_SET);
+    gettimeofday (&stop, NULL);
+    if (N != Nseek) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "seek: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = gzread (gf, buffer, Nread);
+    if (N != Nread) {
+      fprintf (stderr, "error reading\n");
+      exit (1);
+    }
+    gettimeofday (&stop, NULL);
+    fprintf (stdout, "read: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = gzseek (gf, Nback, SEEK_CUR);
+    gettimeofday (&stop, NULL);
+    if (N == -1) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "back: %f\n", DTIME (stop, start));
+    exit (0);
+  } 
+
+  if (!strcmp (argv[1], "raw")) {
+    f = fopen (filename, "r");
+    if (f == NULL) {
+      fprintf (stderr, "can't read data file: %s", filename);
+      exit (1);
+    }
+
+    gettimeofday (&start, NULL);
+    N = fseek (f, Nseek, SEEK_SET);
+    gettimeofday (&stop, NULL);
+    if (N) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "seek: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = fread (buffer, 1, Nread, f);
+    gettimeofday (&stop, NULL);
+    if (N != Nread) {
+      fprintf (stderr, "error reading\n");
+      exit (1);
+    }
+    fprintf (stdout, "read: %f\n", DTIME (stop, start));
+
+    gettimeofday (&start, NULL);
+    N = fseek (f, Nback, SEEK_CUR);
+    gettimeofday (&stop, NULL);
+    if (N) {
+      fprintf (stderr, "error seeking\n");
+      exit (1);
+    }
+    fprintf (stdout, "back: %f\n", DTIME (stop, start));
+    exit (0);
+  }
+    
+  
+  fprintf (stderr, "unknown mode\n");
+  exit (1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/in_image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/in_image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/in_image.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "addstar.h"
+
+int in_image (double r, double d, Image *image) {
+
+  double X, Y;
+
+  RD_to_XY (&X, &Y, r, d, &image[0].coords);
+  if (X < 0) return (FALSE);
+  if (Y < 0) return (FALSE);
+  if (X >= image[0].NX) return (FALSE);
+  if (Y >= image[0].NY) return (FALSE);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "addstar.h"
+# include "2mass.h"
+
+int main (int argc, char **argv) {
+
+  char *path;
+  int i;
+  SkyTable *sky, *sky2mass;
+  AddstarClientOptions options;
+
+  // need to construct these options with args_load2mass...
+  options = ConfigInit (&argc, argv);
+  options = args_load2mass (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  path = TWO_MASS_DIR_AS;
+
+  // the accel.dat file has the raw filenames
+  // test if the file exists, or else try the .gz version
+  sky2mass = load2mass_acc (path, "accel.dat");
+  
+  for (i = 0; i < sky2mass[0].Nregions; i++) {
+    fprintf (stderr, "loading %s\n", sky2mass[0].filename[i]);
+    load2mass_as_rawdata (sky, sky2mass[0].filename[i], options);
+  }
+  exit (0);
+}  
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_as_rawdata.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_as_rawdata.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_as_rawdata.c	(revision 17422)
@@ -0,0 +1,222 @@
+# include "addstar.h"
+# include "2mass.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~64MB */
+# define NBYTE 0x4000000
+# define DEBUG 0
+
+int load2mass_as_rawdata (SkyTable *skytable, char *filename, AddstarClientOptions options) {
+  
+  int i, j, verbose;
+  int Nstars, NSTARS, Ntstars, NTSTARS;
+  int Nbyte, Nextra, Ntotal, offset;
+
+  double Rmin, Rmax, Dmin, Dmax;
+
+  FILE *f;
+  char *buffer, *p, *q;
+
+  Stars *stars;
+  TMStars *tstars;
+  SkyList *skylist;
+  SkyRegion *region;
+  Catalog catalog;
+
+  get2mass_setup (-1);
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  // scan through the entire 2MASS file
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read 2mass data file: %s", filename);
+  // test if this is a raw datafile or gzipped...
+
+  Nextra = 0;  // number excess bytes from lsat partial row
+  Ntotal = 0;  // track the total number of bytes read 
+  while ((Nbyte = fread (&buffer[Nextra], 1, NBYTE-Nextra, f)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from raw file %s", filename);
+    if (DEBUG) fprintf (stderr, "read %d bytes", Nbyte);
+
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, "read .. ");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+    offset = p - buffer; // offset within this scan
+
+    Ntstars = 0;
+    NTSTARS = 10000;
+    ALLOCATE (tstars, TMStars, NTSTARS);
+
+    Rmin = 360.0;
+    Rmax =   0.0;
+    Dmin = +90.0;
+    Dmax = -90.0;
+
+    // scan through entire buffer for star coords
+    while (1) {
+      get2mass_coords (p, &tstars[Ntstars].R, &tstars[Ntstars].D, Nbyte - offset);
+      tstars[Ntstars].offset = offset; // offset within scan
+      tstars[Ntstars].flag = FALSE;
+
+      if (VERBOSE) {
+	Rmin = MIN (Rmin, tstars[Ntstars].R);
+	Rmax = MAX (Rmax, tstars[Ntstars].R);
+	Dmin = MIN (Dmin, tstars[Ntstars].D);
+	Dmax = MAX (Dmax, tstars[Ntstars].D);
+      }
+
+      Ntstars ++;
+      CHECK_REALLOCATE (tstars, TMStars, NTSTARS, Ntstars, 10000);
+
+      /* start of the next line */
+      p = q + 1;
+      offset = p - buffer; // offset within this scan
+      if (offset == Nbyte) {
+	// last line in buffer is a complete line
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - offset);
+      if (q == NULL) {
+	// last, incomplete line in buffer
+	Nextra = Nbyte - offset;
+	break;
+      } 
+    }
+    if (VERBOSE) fprintf (stderr, "scan %d stars (%10.6f - %10.6f, %10.6f - %10.6f) .. ", Ntstars, Rmin, Rmax, Dmin, Dmax);
+
+    // sort the tstars by RA
+    get2mass_sortStars (tstars, Ntstars);
+
+    // scan through the stars, loading the containing catalogs
+    // skip through table for unsaved stars
+    for (i = 0; i < Ntstars; i++) {
+      if (tstars[i].flag) continue;
+
+      // scan forward until we read the UserPatch
+      if (tstars[i].R < UserPatch.Rmin) continue;
+      if (tstars[i].R > UserPatch.Rmax) break;
+      if (tstars[i].D < UserPatch.Dmin) continue;
+      if (tstars[i].D > UserPatch.Dmax) continue;
+
+      // collect stars in a new output catalog
+      Nstars = 0;
+      NSTARS = 3000;
+      ALLOCATE (stars, Stars, NSTARS);
+
+      // identify the relevant catalog
+      skylist = SkyRegionByPoint (skytable, -1, tstars[i].R, tstars[i].D);
+      region = skylist[0].regions[0];
+      if (DEBUG) fprintf (stderr, "writing to %s\n", skylist[0].filename[0]);
+
+      for (j = i; j < Ntstars; j++) {
+	if (tstars[j].flag) continue;
+
+	// check if in skyregion
+	if (tstars[j].R < region[0].Rmin) continue;
+	if (tstars[j].R > region[0].Rmax) break;
+	if (tstars[j].D < region[0].Dmin) continue;
+	if (tstars[j].D > region[0].Dmax) continue;
+	  
+	// check if in UserPatch
+	if (tstars[j].R < UserPatch.Rmin) continue;
+	if (tstars[j].R > UserPatch.Rmax) break;
+	if (tstars[j].D < UserPatch.Dmin) continue;
+	if (tstars[j].D > UserPatch.Dmax) continue;
+	  
+	offset = tstars[j].offset;
+
+	stars[Nstars+0].R = tstars[j].R;
+	stars[Nstars+0].D = tstars[j].D;
+	stars[Nstars+1].R = tstars[j].R;
+	stars[Nstars+1].D = tstars[j].D;
+	stars[Nstars+2].R = tstars[j].R;
+	stars[Nstars+2].D = tstars[j].D;
+	get2mass_3star_full (&stars[Nstars], &buffer[offset], Nbyte - offset);
+	// get2mass_star (&stars[Nstars], &buffer[offset], Nbyte - offset);
+
+	tstars[j].flag = TRUE;
+
+	Nstars += 3;
+	if (Nstars >= NSTARS - 3) {
+	  NSTARS += 3000;
+	  REALLOCATE (stars, Stars, NSTARS);
+	}
+      }
+
+      if (!Nstars) {
+	free (stars);
+	continue;
+      }
+
+      if (DEBUG) fprintf (stderr, "selected %d stars (%10.6f - %10.6f, %10.6f - %10.6f\n", Nstars, 
+			  region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+      if (1) {
+	verbose = VERBOSE;
+	VERBOSE = FALSE;
+
+	// now we have all of the loaded stars in this catalog
+	catalog.filename = skylist[0].filename[0];
+	catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+	catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+	// an error exit status here is a significant error
+	if (!dvo_catalog_open (&catalog, skylist[0].regions[0], VERBOSE, "w")) {
+	  fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	  exit (2);
+	}
+
+	// assume no input star matches an existing star 
+	// simply add to the existing table
+	load2mass_catalog (&catalog, stars, Nstars);
+
+	dvo_catalog_save (&catalog, VERBOSE);
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	// free (catalog.filename);
+	// XXX don't free this! it points to an element of the skytable
+      }
+
+      SkyListFree (skylist);
+      free (stars);
+      VERBOSE = verbose;
+    }
+    free (tstars);
+    if (VERBOSE) fprintf (stderr, "done\n");
+
+    // at end, p points at the start of last, partial line
+    if (Nextra) memmove (buffer, p, Nextra);
+  }
+
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  fclose (f);
+  free (buffer);
+  return (TRUE);
+}
+
+/*
+  for each 2mass file:
+  for each data block
+  generate a table of: R, D, byte, flag
+  for each unsaved star
+  find containing catalog
+  load catalog
+  find all contained stars
+  add to catalog
+  save catalog 
+  mark all contained stars
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_catalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_catalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/load2mass_catalog.c	(revision 17422)
@@ -0,0 +1,88 @@
+# include "addstar.h"
+
+int load2mass_catalog (Catalog *catalog, Stars *stars, int Nstars) {
+
+  int i, j, Nsec, Nmeas, Nave, NMEAS, NAVE;
+
+  Nsec = GetPhotcodeNsecfilt ();
+  Nave = catalog[0].Naverage;
+  Nmeas = catalog[0].Nmeasure;
+   
+  NAVE = Nave + 100;
+  NMEAS = Nmeas + 100;
+  REALLOCATE (catalog[0].average, Average, NAVE);
+  REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*Nsec);
+  REALLOCATE (catalog[0].measure, Measure, NMEAS);
+
+  for (i = 0; i < Nstars; i+=3) {
+
+    // construct an average object for this object
+    // XXX for now, the output objects will have limited astrometric interpretation...
+    // XXX every 3 stars represents 3 measurements and 1 average
+    catalog[0].average[Nave].R     = stars[i].R;
+    catalog[0].average[Nave].D     = stars[i].D;
+    catalog[0].average[Nave].dR    = 0;
+    catalog[0].average[Nave].dD    = 0;
+    catalog[0].average[Nave].uR    = 0;
+    catalog[0].average[Nave].uD    = 0;
+    catalog[0].average[Nave].duR   = 0;
+    catalog[0].average[Nave].duD   = 0;
+    catalog[0].average[Nave].P     = 0;
+    catalog[0].average[Nave].dP    = 0;
+
+    // XXX for now, set the average mag data to NULL
+    catalog[0].average[Nave].Nmeasure  	   = 0;
+    catalog[0].average[Nave].Nmissing  	   = 0;
+    catalog[0].average[Nave].Xp        	   = NAN_S_SHORT;
+    catalog[0].average[Nave].measureOffset = Nmeas;
+    catalog[0].average[Nave].missingOffset = -1;
+    catalog[0].average[Nave].code          = 0;
+
+    for (j = 0; j < Nsec; j++) {
+      catalog[0].secfilt[Nave*Nsec+j].M  = NAN;
+      catalog[0].secfilt[Nave*Nsec+j].dM = NAN;
+      catalog[0].secfilt[Nave*Nsec+j].Xm = NAN_S_SHORT;
+    }
+
+    // we now have the min chisq row. use this to supply the other filter values....
+    for (j = 0; j < 3; j++) {
+      catalog[0].measure[Nmeas].dR        = 0.0;
+      catalog[0].measure[Nmeas].dD        = 0.0;
+      catalog[0].measure[Nmeas].Xccd      = stars[i+j].X;
+      catalog[0].measure[Nmeas].Yccd      = stars[i+j].Y;
+      catalog[0].measure[Nmeas].dXccd     = 0.0;
+      catalog[0].measure[Nmeas].dYccd     = 0.0;
+      catalog[0].measure[Nmeas].M         = stars[i+j].M;
+      catalog[0].measure[Nmeas].dM        = stars[i+j].dM;
+      catalog[0].measure[Nmeas].Mcal      = 0;
+      catalog[0].measure[Nmeas].dMcal     = stars[i+j].dMcal;
+      catalog[0].measure[Nmeas].t         = stars[i+j].t;
+      catalog[0].measure[Nmeas].averef    = Nave;
+      catalog[0].measure[Nmeas].photcode  = stars[i+j].code;
+      catalog[0].measure[Nmeas].dophot    = 0;
+      catalog[0].measure[Nmeas].photFlags = stars[i+j].flags;
+      catalog[0].measure[Nmeas].dbFlags   = 0;
+      catalog[0].measure[Nmeas].dt        = 0xffff;
+					  
+      catalog[0].measure[Nmeas].airmass   = 0;
+      catalog[0].measure[Nmeas].FWx       = stars[i+j].fx;
+      catalog[0].measure[Nmeas].FWy       = stars[i+j].fy;
+      catalog[0].measure[Nmeas].theta     = stars[i+j].df;
+
+      catalog[0].average[Nave].Nmeasure++;
+      Nmeas ++;
+      CHECK_REALLOCATE (catalog[0].measure, Measure, NMEAS, Nmeas, 100);
+    }
+
+    Nave ++;
+    if (Nave >= NAVE) {
+      NAVE += 100;
+      REALLOCATE (catalog[0].average, Average, NAVE);
+      REALLOCATE (catalog[0].secfilt, SecFilt, NAVE*Nsec);
+    }
+  }
+  catalog[0].Naverage = Nave;
+  catalog[0].Nmeasure = Nmeas;
+  catalog[0].Nsecf_mem = Nave*Nsec;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/load_subpix.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/load_subpix.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/load_subpix.c	(revision 17422)
@@ -0,0 +1,51 @@
+# include "addstar.h"
+
+typedef struct {
+  double Amp;
+  double Phase;
+  double dM;
+} SubPixFix;
+
+static int Nsubpix;
+static SubPixFix *Subpix;
+
+void load_subpix () {
+
+  int i;
+  FILE *f;
+
+  Nsubpix = 40;
+  ALLOCATE (Subpix, SubPixFix, Nsubpix);
+
+  f = fopen (SubpixDatafile, "r");
+  if (f == NULL) Shutdown ("can't load subpix datafile %s", SubpixDatafile);
+
+  for (i = 0; i < Nsubpix; i++) {
+    fscanf (f, "%*s %*s %lf %lf %lf %*s\n", 
+	    &Subpix[i].Amp, &Subpix[i].Phase, &Subpix[i].dM);
+  }
+  fclose (f);
+
+}
+
+double get_subpix (double x, double y) {
+
+  int bin;
+  double dy, dM;
+
+  dy = y - (int)(y);
+  bin = 5 * (int)(x/100) + (int)(y/100);
+  dM = Subpix[bin].Amp*sin(2*3.14159*dy + Subpix[bin].Phase);
+  return (dM);
+}
+
+double scat_subpix (double x, double y) {
+
+  int bin;
+  double dy, dM;
+
+  dy = y - (int)(y);
+  bin = 5 * (int)(x/100) + (int)(y/100);
+  dM = Subpix[bin].dM;
+  return (dM);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/mkacc-2mass.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/mkacc-2mass.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/mkacc-2mass.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "addstar.h"
+# include "2mass.h"
+
+int main (int argc, char **argv) {
+
+  int i, N, Nrefcat;
+  Stars *refcat;
+  SkyRegion *regions, accregion;
+
+  ConfigInit (&argc, argv);
+
+  /* override any header PHOTCODE values */
+  thiscode = NULL;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    thiscode = GetPhotcodebyName (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: mkacc.2mass (file)\n");
+    exit (2);
+  }
+
+  NAMED_PHOTCODE (TM_J, "2MASS_J");
+  NAMED_PHOTCODE (TM_H, "2MASS_H");
+  NAMED_PHOTCODE (TM_K, "2MASS_K");
+  if (thiscode == NULL) Shutdown ("photcode not specified");
+  if (thiscode[0].code == TM_J) goto good_code;
+  if (thiscode[0].code == TM_H) goto good_code;
+  if (thiscode[0].code == TM_K) goto good_code;
+  Shutdown ("2MASS photcode not specified");
+
+good_code:
+  UserPatch.RAmin = 0;
+  UserPatch.RAmax = 360;
+  UserPatch.DECmin = -90;
+  UserPatch.DECmax = +90;
+
+  ALLOCATE (regions, SkyRegion, 1);
+  strcpy (regions[0].filename, argv[1]);
+  regions[0].RAmin = 0;
+  regions[0].RAmax = 360;
+  regions[0].DECmin = -90;
+  regions[0].DECmax = +90;
+
+  refcat = get2mass_AS_data (regions, &UserPatch, &Nrefcat);
+
+  /* find upper and lower file limits in RA and DEC */
+
+  strcpy (accregion.filename, regions[0].filename);
+  accregion.RAmin  = 360;
+  accregion.RAmax  =   0;
+  accregion.DECmin = +90;
+  accregion.DECmax = -90;
+
+  for (i = 0; i < Nrefcat; i++) {
+    accregion.RAmin  = MIN (refcat[i].R, accregion.RAmin);
+    accregion.RAmax  = MAX (refcat[i].R, accregion.RAmax);
+    accregion.DECmin = MIN (refcat[i].D, accregion.DECmin);
+    accregion.DECmax = MAX (refcat[i].D, accregion.DECmax);
+  }
+
+  fprintf (stderr, "%s %10.6f %10.6f  %10.6f %10.6f  %d\n", 
+	   accregion.filename, accregion.RAmin/15.0, accregion.RAmax/15.0, accregion.DECmin, accregion.DECmax, Nrefcat);
+
+  exit (0);
+}
+
+/* XXX update this function to create an additional accelerator file for each 2mass file
+   each file: one row per DEC band
+   each row: Rmin, Rmax, Dmin, Dmax, Nbyte(i)
+   where Nbyte(i) = byte for each Rmin + i*30 deg transition
+*/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/opening_angle.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/opening_angle.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/opening_angle.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "addstar.h"
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/parse_time.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/parse_time.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/parse_time.c	(revision 17422)
@@ -0,0 +1,146 @@
+# include "addstar.h"
+
+int parse_time (Header *header) {
+
+  double jd;
+  int Ny, Nf, mode;
+  int Nsec, hour, min, sec, year, month, day;
+  char *py, *pm, *pd, *c;
+  char line[256];
+
+  /* we want to find JD or MJD to get Nsec (seconds since 01/01/1970) */
+
+  /* try JD first */
+  if (strcasecmp (JDKeyword, "NONE")) {
+    uppercase (JDKeyword);
+    if (!gfits_scan (header, JDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing JD Keyword %s\n", JDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 2440587.5)*86400;
+    return (Nsec);
+  }
+
+  /* try MJD next */
+  if (strcasecmp (MJDKeyword, "NONE")) {
+    uppercase (MJDKeyword);
+    if (!gfits_scan (header, MJDKeyword, "%lf", 1, &jd)) {
+      fprintf (stderr, "ERROR: missing MJD Keyword %s\n", MJDKeyword);
+      exit (1);
+    }
+    Nsec = (jd - 40587.0)*86400;
+    return (Nsec);
+  }
+    
+  if (!strcasecmp (UTKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateKeyword, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+  if (!strcasecmp (DateMode, "NONE")) {
+      fprintf (stderr, "ERROR: no valid Date/Time keywords\n");
+      exit (1);
+  }
+
+  /* get UT and DATE */
+  uppercase (UTKeyword);
+  if (!gfits_scan (header, UTKeyword, "%s", 1, line)) {
+      fprintf (stderr, "ERROR: missing UT Keyword %s\n", UTKeyword);
+      exit (1);
+    }
+  /* remove ':' characters */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  sscanf (line, "%d %d %d", &hour, &min, &sec);
+
+  /* parse mode line */
+  uppercase (DateMode);
+  for (Ny = 0, c = strchr (DateMode, 'Y'); c != (char *) NULL; c = strchr (c + 1, 'Y'), Ny++);
+  if ((Ny != 2) && (Ny != 4)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  py = strchr (DateMode, 'Y');
+  pm = strchr (DateMode, 'M');
+  pd = strchr (DateMode, 'D');
+  if ((py == (char *) NULL) || (pm == (char *) NULL) || (pd == (char *) NULL)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pm) && (py < pd)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  if ((py > pd) && (py < pm)) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+  mode = 0;
+  if ((py < pm) && (pm < pd)) { mode = 1; }  /* yyyy-mm-dd */
+  if ((py < pm) && (pm > pd)) { mode = 2; }  /* yyyy-dd-mm */
+  if ((py > pm) && (pm < pd)) { mode = 3; }  /* mm-dd-yyyy */
+  if ((py > pm) && (pm > pd)) { mode = 4; }  /* dd-mm-yyyy */
+  if (!mode) {
+    fprintf (stderr, "error in DATE-MODE format: %s\n", DateMode);
+    exit (1);
+  }
+
+  /* parse date entry */
+  uppercase (DateKeyword);
+  if (!gfits_scan (header, DateKeyword, "%s",  1, line)) {
+    fprintf (stderr, "ERROR: missing DATE Keyword %s\n", DateKeyword);
+    exit (1);
+  }
+  /* remove possible separators: ':', '/' '.', '-' */
+  for (c = strchr (line, 0x3a); c != (char *) NULL; c = strchr (line, 0x3a)) { *c = ' '; }
+  for (c = strchr (line, 0x2f); c != (char *) NULL; c = strchr (line, 0x2f)) { *c = ' '; }
+  for (c = strchr (line, 0x2e); c != (char *) NULL; c = strchr (line, 0x2e)) { *c = ' '; }
+  for (c = strchr (line, 0x2d); c != (char *) NULL; c = strchr (line, 0x2d)) { *c = ' '; }
+
+  Nf = 0;
+  switch (mode) {
+  case 1:
+    Nf = sscanf (line, "%d %d %d", &year, &month, &day);
+    break;
+  case 2:
+    Nf = sscanf (line, "%d %d %d", &year, &day, &month);
+    break;
+  case 3:
+    Nf = sscanf (line, "%d %d %d", &month, &day, &year);
+    break;
+  case 4:
+    Nf = sscanf (line, "%d %d %d", &day, &month, &year);
+    break;
+  }
+  if (Nf != 3) {
+    fprintf (stderr, "error in date entry (%s) or DATE-MODE format (%s)\n", line, DateMode);
+    exit (1);
+  }
+
+  if (year > 1000) {
+    if (Ny == 2) {
+      fprintf (stderr, "warning: mode line claims 2 digit year, but 4 digit year found\n");
+    }
+  } else {
+    if (Ny == 4) {
+      fprintf (stderr, "warning: mode line claims 4 digit year, but 2 digit year found\n");
+    }
+    if (year < 50) year += 100;
+    year += 1900;
+  }    
+
+  /* this should probably use localtime */
+
+  /* convert yy.mm.dd hh.mm.ss to Nsec since 1970 (jd = 2440587.5) */
+  /* note that in this section, tm_mon has range 1-12, unlike for gmtime () */
+  jd = day - 32075 + (int)(1461*(year + 4800 + (int)(((month)-14)/12))/4)
+    + (int)(367*((month) - 2 - (int)(((month) - 14)/12)*12)/12)
+    - (int)(3*(int)((year + 4900 + (int)(((month) - 14)/12))/100)/4) - 0.5;
+  /* jd is the julian day of the whole day only not the time */
+  Nsec = (jd - 2440587.5)*86400 + 3600.0*hour + min*60.0 + sec;
+  
+  return (Nsec);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/replace_match.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/replace_match.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/replace_match.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "addstar.h"
+
+int replace_match (Average *average, Measure *measure, Stars *star) {
+
+  int i;
+
+  /* search for entry and replace values M, dM, R, D */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[i].photcode != star[0].code) continue;
+    measure[i].dR = 3600.0*(average[0].R - star[0].R);
+    measure[i].dD = 3600.0*(average[0].D - star[0].D);
+    measure[i].M  = star[0].M;
+    measure[i].dM = star[0].dM;
+    star[0].found = average[0].measureOffset + i;
+    return (TRUE);
+  }
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/resort_catalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/resort_catalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/resort_catalog.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "addstar.h"
+
+void resort_catalog (Catalog *catalog) {
+
+  int *next_meas;
+  int Naves, Nmeas;
+  double dtime;
+  struct timeval start, stop;
+
+  if (catalog[0].sorted == TRUE) return;
+
+  gettimeofday (&start, NULL);
+
+  /* internal counters */
+  Nmeas = catalog[0].Nmeasure;
+  Naves = catalog[0].Naverage;
+  
+  /* set up pointers for linked list of measure, missing */
+  next_meas = build_measure_links (catalog[0].average, Naves, catalog[0].measure, Nmeas);
+
+  catalog[0].sorted = TRUE;
+  catalog[0].measure = sort_measure (catalog[0].average, Naves, catalog[0].measure, Nmeas, next_meas);
+
+  gettimeofday (&stop, NULL);
+  dtime = DTIME (stop, start);
+  fprintf (stderr, "  match time %9.4f sec for %7d measures, %6d average\n", dtime, Nmeas, Naves);
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/sedstar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/sedstar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/sedstar.c	(revision 17422)
@@ -0,0 +1,85 @@
+# include "sedstar.h"
+
+int main (int argc, char **argv) {
+
+  char *root, *ext, tmp;
+  int i, Nbytes;
+  SkyList *skylist;
+  SkyTable *sky;
+  AddstarClientOptions options;
+  Catalog incatalog, outcatalog;
+  SEDtable *sedtable;
+
+  // need to construct these options with args_load2mass...
+  options = ConfigInit (&argc, argv);
+  options = args_sedstar (argc, argv, options);
+
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // select regions of interest
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load the SED data table
+  sedtable = SEDtableLoad (argv[1]);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    incatalog.filename = skylist[0].filename[i];
+    incatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&incatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", incatalog.filename);
+      exit (2);
+    }
+
+    // Naves_disk == 0 implies an empty catalog file
+    if ((incatalog.Naves_disk == 0) && options.only_match) {
+      if (VERBOSE) fprintf (stderr, "skipping empty region\n");
+      dvo_catalog_unlock (&incatalog);
+      dvo_catalog_free (&incatalog);
+      continue;
+    }
+
+    // create output catalog filename
+    root = strstr (incatalog.filename, CATDIR);
+    if (root == NULL) Shutdown ("error with input catalog name");
+    ext = incatalog.filename + strlen(CATDIR);
+    while (*ext == '/') ext++;
+    Nbytes = snprintf (&tmp, 0, "%s/%s", argv[2], ext);
+    ALLOCATE (outcatalog.filename, char, Nbytes + 1);
+    snprintf (outcatalog.filename, Nbytes + 1, "%s/%s", argv[2], ext);
+
+    outcatalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+    outcatalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+    outcatalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    outcatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&outcatalog, skylist[0].regions[i], VERBOSE, "w")) {
+      Shutdown ("ERROR: failure to open/create catalog file %s\n", outcatalog.filename);
+    }
+
+    SEDfitCatalog (&outcatalog, &incatalog, sedtable);
+    
+    dvo_catalog_save (&outcatalog, VERBOSE);
+    dvo_catalog_unlock (&outcatalog);
+    dvo_catalog_free (&outcatalog);
+
+    dvo_catalog_unlock (&incatalog);
+    dvo_catalog_free (&incatalog);
+    // XXX free filename or not?
+  }
+  exit (0);
+}  
+
+/**  sedstar: 
+
+* load in the SED data table
+* load in the catalog file (by region)
+* fit stars in the catalog file
+* load output catalog file?
+* construct output catalog file (optional)
+* save output catalog file
+
+**/
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/sky_tessalation.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/sky_tessalation.c	(revision 17422)
@@ -0,0 +1,675 @@
+# include "skycells.h"
+# include "assert.h"
+# define iSWAP(X,Y) {int tmp=(X); (X) = (Y); (Y) = tmp;}
+
+// we use a static refcoords structure to avoid multiple alloc / init steps
+static Coords *refcoords = NULL;
+
+int sky_tessalation (FITS_DB *db, int level, int Nmax, int mode, double scale) {
+
+  sky_tessalation_init (scale);
+
+  if (mode == SQUARES) {
+    sky_tessalation_squares (db, level, Nmax);
+    return TRUE;
+  }
+
+  if (mode == TRIANGLES) {
+    sky_tessalation_triangles (db, level, Nmax);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+int sky_tessalation_triangles (FITS_DB *db, int level, int Nmax) {
+
+  int i, j, Ndigit, Ntriangles, Nbase, Ntotal, Ltop, Nout, Nimages;
+  double Ntop, fLtop;
+  SkyTriangle *base, *tri, *new;
+  Image *image;
+  char format[16];
+
+  // generate the initial base set
+  base = sky_base_triangles (&Nbase);
+
+  sky_base_rotation (base, Nbase);
+
+  // how many triangles total for this level?
+  Ntotal = Nbase*pow(4.0, level);
+  Ndigit = (int)(log10(Ntotal)) + 1 ;
+  snprintf (format, 16, "skytri.%%0%dd", Ndigit);
+
+  // to what depth do we need to go to have only Nmax foreach subcell?
+  Ntop = Ntotal / Nmax;
+  if (Ntop > Nbase) {
+    fLtop = log10(Ntop / Nbase) / log10(4.0);
+    if (fLtop > (int)(fLtop)) {
+      Ltop = fLtop + 1;
+    } else {
+      Ltop = fLtop;
+    }
+  } else {
+    Ltop = 0;
+  }
+
+  // subdivide the base set to Ltop level
+  for (i = 0; i < Ltop; i++) {
+    new = sky_divide_triangles (base, &Nbase);
+    free (base);
+    base = new;
+  }
+
+  // for each base triangle, subdivide the rest of the way and save
+  Nout = 0;
+  for (i = 0; i < Nbase; i++) {
+    ALLOCATE (tri, SkyTriangle, 1);
+    tri[0] = base[i];
+    Ntriangles = 1;
+    for (j = Ltop; j < level; j++) {
+      new = sky_divide_triangles (tri, &Ntriangles);
+      free (tri);
+      tri = new;
+    }
+
+    // convert the SkyTriangles to Image
+    ALLOCATE (image, Image, Ntriangles);
+    for (j = 0; j < Ntriangles; j++) {
+      sky_triangle_to_image (&image[j], &tri[j]);
+      snprintf (image[j].name, 64, format, Nout);
+      Nout++;
+    }  
+    Nimages = Ntriangles;
+
+    /* add the new images and save */
+    dvo_image_addrows (db, image, Nimages);
+    SetProtect (TRUE);
+    dvo_image_update (db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_clear_vtable (db);
+
+    free (image);
+    free (tri);
+  }
+  return (TRUE);
+}
+
+int sky_tessalation_squares (FITS_DB *db, int level, int Nmax) {
+
+  int i, j, Nname, Ndigit, Ntriangles, Nbase, Nimage, Ntotal, Ntop, Ltop, Nsubset, Nx, Ny;
+  double fLtop;
+  SkyTriangle *base, *tri, *new;
+  SkyRectangle *rectangle, *subset;
+  Image *image;
+  char format[16];
+
+  Nx = NX_SUB;
+  Ny = NY_SUB;
+
+  // generate the initial base set
+  base = sky_base_triangles (&Nbase);
+
+  sky_base_rotation (base, Nbase);
+
+  // how many total cells for this level (multiply by subdivisions, if used)?
+  Ntotal = Nbase*pow(4.0, level);
+  Ndigit = (int)(log10(Ntotal)) + 1 ;
+  snprintf (format, 16, "skycell.%%0%dd", Ndigit);
+
+  // to what depth do we need to go to have only Nmax foreach subcell?
+  Ntop = Ntotal / (Nmax*Nx*Ny) ;
+  if (Ntop > Nbase) {
+    fLtop = log10(Ntotal / (double)(Ntop * Nbase)) / log10(4.0);
+    if (fLtop > (int)(fLtop)) {
+      Ltop = fLtop + 1;
+    } else {
+      Ltop = fLtop;
+    }
+  } else {
+    Ltop = 0;
+  }
+
+  // subdivide the base set to Ltop level
+  for (i = 0; i < Ltop; i++) {
+    new = sky_divide_triangles (base, &Nbase);
+    free (base);
+    base = new;
+  }
+
+  // for each base triangle, subdivide the rest of the way and save
+  Nname = 0;
+  for (i = 0; i < Nbase; i++) {
+    ALLOCATE (tri, SkyTriangle, 1);
+    tri[0] = base[i];
+    Ntriangles = 1;
+    for (j = Ltop; j < level; j++) {
+      new = sky_divide_triangles (tri, &Ntriangles);
+      free (tri);
+      tri = new;
+    }
+
+    // convert the SkyTriangles to SkyRectangles
+    ALLOCATE (rectangle, SkyRectangle, Ntriangles);
+    for (j = 0; j < Ntriangles; j++) {
+      sky_triangle_to_rectangle (&rectangle[j], &tri[j]);
+    }  
+
+    // drop the appropriate subset
+    ALLOCATE (subset, SkyRectangle, Ntriangles);
+    for (j = Nsubset = 0; j < Ntriangles; j++) {
+      if (!strcmp(rectangle[j].coords.ctype, "DROP")) continue;
+      memcpy (&subset[Nsubset], &rectangle[j], sizeof(SkyRectangle));
+      snprintf (subset[Nsubset].name, 64, format, Nname);
+      Nname++;
+      Nsubset++;
+    }  
+    free (rectangle);
+
+    // subdivide each image (Nx x Ny subcells)
+    Nimage = Nx*Ny*Nsubset;
+    ALLOCATE (image, Image, Nimage);
+    for (j = 0; j < Nsubset; j++) {
+      // convert the SkyRectangles to Images for output
+      sky_subdivide_image (&image[j*Nx*Ny], &subset[j], Nx, Ny);
+    }
+
+    /* add the new images and save */
+    dvo_image_addrows (db, image, Nimage);
+    SetProtect (TRUE);
+    dvo_image_update (db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_clear_vtable (db);
+
+    free (subset);
+    free (image);
+    free (tri);
+  }
+  return (TRUE);
+}
+
+// an allocated image is supplied, we fill in the values
+int sky_triangle_to_image (Image *image, SkyTriangle *triangle) {
+
+  int i, NX, NY;
+  double xv[3], yv[3];	      // coordinates of the vertex in the reference projection 
+  double scale;
+  double Xmin, Xmax, Ymin, Ymax;
+
+  // calculate the triangle coordinates in r,d
+  sky_triangle_coords (triangle);
+
+  // we will project to the triangle center position
+  refcoords[0].crval1 = triangle[0].r;
+  refcoords[0].crval2 = triangle[0].d;
+
+  // project the vertices to this projection, find bounds
+  Xmin = Xmax = Ymin = Ymax = 0.0; // 0,0 is center of triangle
+  for (i = 0; i < 3; i++) {
+    RD_to_XY (&xv[i], &yv[i], triangle[0].rv[i], triangle[0].dv[i], refcoords);
+    Xmin = MIN (xv[i], Xmin);
+    Xmax = MAX (xv[i], Xmax);
+    Ymin = MIN (yv[i], Ymin);
+    Ymax = MAX (yv[i], Ymax);
+  }
+
+  // set NX, NY to the roughly full-width box (centered at 0,0)
+  NX = Xmax - Xmin;
+  NY = Ymax - Ymin;
+
+  memset (image, 0, sizeof(Image));
+  image[0].coords = *refcoords;
+  image[0].coords.pc1_1 = image[0].coords.pc2_2 = 1.0;
+  image[0].coords.pc1_2 = image[0].coords.pc2_1 = 0.0;
+
+  // We cannot use the correction below if we want to set cdelt1,2 to our desired pixel scale
+  // use this test to raise an error (60000 x 60000 is a very large image...)
+  strcpy (image[0].coords.ctype, "TRI--TAN");
+  scale = 0;
+  for (i = 0; i < 3; i++) {
+    scale = MAX (abs(xv[i]), scale);
+    scale = MAX (abs(yv[i]), scale);
+  }
+  if (scale > 32000) {
+    scale /= 30000.0;
+    NX /= scale;
+    NY /= scale;
+    image[0].coords.cdelt1 *= scale;
+    image[0].coords.cdelt2 *= scale;
+    for (i = 0; i < 3; i++) {
+      xv[i] /= scale;
+      yv[i] /= scale;
+    }
+  }
+  image[0].NX = NX;
+  image[0].NY = NY;
+
+  image[0].code = 1; // this needs to be set more sensibly
+
+  image[0].Mx   = xv[0];  image[0].My   = yv[0];
+  image[0].Mxxx = xv[1];  image[0].Mxyy = yv[1];
+  image[0].Mxxy = xv[2];  image[0].Myyy = yv[2];
+
+  return (TRUE);
+}
+
+// an allocated image is supplied, we fill in the values
+// we are only keeping ~half of the images
+int sky_triangle_to_rectangle (SkyRectangle *rectangle, SkyTriangle *triangle) {
+
+  int i, parity, peak, b1, b2, NX, NY, right;
+  double xv[3], yv[3];	      // coordinates of the vertex in the reference projection 
+  double xo, yo, xc, yc, xcr, ycr, angle;
+  double dB, dP, s1, s2, r1, r2, dr, dx, dy;
+  double angle_b1, angle_b2, slope;
+
+  // calculate the triangle coordinates in r,d
+  sky_triangle_coords (triangle);
+
+  // we will project to the triangle center position
+  refcoords[0].crval1 = triangle[0].r;
+  refcoords[0].crval2 = triangle[0].d;
+
+  // find the size, rotation, and parity of the image
+  // project the vertices and find the image parity
+  parity = 1;
+  for (i = 0; i < 3; i++) {
+    RD_to_XY (&xv[i], &yv[i], triangle[0].rv[i], triangle[0].dv[i], refcoords);
+    parity *= SIGN(yv[i]);
+  }
+
+  // choose the peak vertex
+  peak = -1;
+  for (i = 0; (peak == -1) && (i < 3); i++) {
+    if (parity == SIGN(yv[i])) {
+      peak = i;
+    }
+  }
+  assert (peak != -1);
+
+  // angle is from the center to the peak corner
+  angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+
+  // find the base and height
+  b1 = (peak + 1) % 3;
+  b2 = (peak + 2) % 3;
+
+  // angle is from the center to the peak corner
+  angle_b1 = DEG_RAD*atan(yv[b1] / xv[b1]);
+  angle_b2 = DEG_RAD*atan(yv[b2] / xv[b2]);
+
+  // if one of the base-center angles is very small, the parity is marginal.  Use additional
+  // information to choose the parity.  note that both angle_b1 and angle_b2 cannot be close to
+  // zero.
+  if (fabs(angle_b1) < 10.0) {
+    right = (xv[b1] > 0);     // pointing left or right?
+    slope = (xv[peak] -  xv[b2]) / (yv[peak] - yv[b2]);
+    if ( right && (slope >= 0.0)) parity = +1;
+    if ( right && (slope <  0.0)) parity = -1;
+    if (!right && (slope <= 0.0)) parity = +1;
+    if (!right && (slope >  0.0)) parity = -1;
+    if (parity > 0) {
+      if (yv[peak] < yv[b2]) iSWAP(peak, b2); // require peak to be top (bottom) point
+    } else {
+      if (yv[peak] > yv[b2]) iSWAP(peak, b2); // require peak to be top (bottom) point
+    }
+    angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+  }
+  if (fabs(angle_b2) < 10.0) {
+    right = (xv[b2] > 0);     // pointing left or right?
+    slope = (xv[peak] - xv[b1]) / (yv[peak] - yv[b1]); // tilt of opposite line
+    if ( right && (slope >= 0.0)) parity = +1;
+    if ( right && (slope <  0.0)) parity = -1;
+    if (!right && (slope <= 0.0)) parity = +1;
+    if (!right && (slope >  0.0)) parity = -1;
+    if (parity > 0) {
+      if (yv[peak] < yv[b1]) iSWAP(peak, b1); // require peak to be top (bottom) point
+    } else {
+      if (yv[peak] > yv[b1]) iSWAP(peak, b1); // require peak to be top (bottom) point
+    }
+    angle = atan2(parity*xv[peak], parity*yv[peak]); // note that this is x/y not y/x (and in radians)
+  }
+
+  // xo, yo is the center of the baseline
+  xo = 0.5*(xv[b2] + xv[b1]);
+  yo = 0.5*(yv[b2] + yv[b1]);
+
+  // find the max perpendicular distance from the peak
+
+  // dB[b1] == dB[b2] (since xo,yo is the midpoint of [b1] to [b2]
+  dB = hypot(xo      -xv[b1], yo      -yv[b1]);
+  dP = hypot(xv[peak]-xo,     yv[peak] -yo);
+	     
+  // XXX we could just choose the point based on s1 vs s2...
+  s1 = hypot(xv[peak]-xv[b1], yv[peak]-yv[b1]);
+  s2 = hypot(xv[peak]-xv[b2], yv[peak]-yv[b2]);
+
+  r1 = (SQ(s1) - SQ(dB) - SQ(dP)) / (2*dP);
+  r2 = (SQ(s2) - SQ(dB) - SQ(dP)) / (2*dP);
+
+  // dr >= 0
+  dr = MAX (r1, r2);
+
+  dx = -parity*dr*sin(angle);
+  dy = -parity*dr*cos(angle);
+
+  xo += dx;
+  yo += dy;
+
+  // xc, yc is the true image center
+  xc = 0.5*(xv[peak] + xo);
+  yc = 0.5*(yv[peak] + yo);
+
+  // NX,NY are the size of the circumscribed square, expanded by PADDING
+  NX = hypot((xv[b2]-xv[b1]),(yv[b2]-yv[b1])) * (1 + PADDING);
+  NY = hypot((xv[peak]-xo),(yv[peak]-yo)) * (1 + PADDING);
+
+  memset (rectangle, 0, sizeof(SkyRectangle));
+  rectangle[0].coords = *refcoords;
+
+  if (FIX_NS) {
+    rectangle[0].coords.pc1_1 = +1.0;
+    rectangle[0].coords.pc1_2 = +0.0;
+    rectangle[0].coords.pc2_1 = -0.0;
+    rectangle[0].coords.pc2_2 = +1.0;
+    xcr = xc*cos(angle) - yc*sin(angle); 
+    ycr = yc*cos(angle) + xc*sin(angle); 
+  } else {
+    rectangle[0].coords.pc1_1 = +cos(angle);
+    rectangle[0].coords.pc1_2 = +sin(angle);
+    rectangle[0].coords.pc2_1 = -sin(angle);
+    rectangle[0].coords.pc2_2 = +cos(angle);
+    xcr = xc;
+    ycr = yc;
+  }
+  
+  // crpix1,crpix2 is the projection center
+  rectangle[0].coords.crpix1 = 0.5*NX - xcr;
+  rectangle[0].coords.crpix2 = 0.5*NY - ycr;
+
+  // only keep one of the parity rectangles
+  if (((triangle[0].d >= 0) && (parity == +1)) || ((triangle[0].d < 0) && (parity == -1))) {
+    strcpy (rectangle[0].coords.ctype, "DEC--TAN");
+  } else {
+    strcpy (rectangle[0].coords.ctype, "DROP");
+  }
+
+  rectangle[0].NX = NX;
+  rectangle[0].NY = NY;
+  rectangle[0].code = 1; // this needs to be set more sensibly
+
+  return (TRUE);
+}
+
+// an allocated image set is supplied, we fill in the values
+int sky_subdivide_image (Image *output, SkyRectangle *input, int Nx, int Ny) {
+
+  int i, j, N, NX, NY, Ndigit;
+  char format[24];
+
+  NX = input[0].NX/(double)Nx + 0.5;
+  NY = input[0].NY/(double)Ny + 0.5;
+
+  // image[0].NX,NY are unsigned short: abort is we overflow
+  if ((NX > 0xffff) || (NY > 0xffff)) {
+    fprintf (stderr, "error: NX,NY too big for DVO limits; modify pixel scale\n");
+    fprintf (stderr, "NX: %d, NY: %d\n", NX, NY);
+    exit (1);
+  }
+
+  Ndigit = (int)(log10(Nx*Ny)) + 1 ;
+  snprintf (format, 24, "%s.%%0%dd", input[0].name, Ndigit);
+
+  N = 0;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+
+      memset (&output[N], 0, sizeof(Image));
+      memcpy (&output[N].coords, &input[0].coords, sizeof(Coords));
+
+      snprintf (output[N].name, 64, format, N);
+      output[N].NX = NX;
+      output[N].NY = NY;
+      output[N].code = input[0].code;
+
+      output[N].coords.crpix1 = input[0].coords.crpix1 - i*NX;
+      output[N].coords.crpix2 = input[0].coords.crpix2 - j*NY;
+      N++;
+    }
+  }
+  return (TRUE);
+}
+
+int sky_triangle_coords (SkyTriangle *triangle) {
+
+  int i;
+  double r;
+
+  // calculate the triangle center
+  triangle[0].center.x = (triangle[0].vertex[0].x + triangle[0].vertex[1].x + triangle[0].vertex[2].x)/3.0;
+  triangle[0].center.y = (triangle[0].vertex[0].y + triangle[0].vertex[1].y + triangle[0].vertex[2].y)/3.0;
+  triangle[0].center.z = (triangle[0].vertex[0].z + triangle[0].vertex[1].z + triangle[0].vertex[2].z)/3.0;
+
+  // renormalize
+  r = 1.0 / sqrt (SQ(triangle[0].center.x) + SQ(triangle[0].center.y) + SQ(triangle[0].center.z));
+
+  triangle[0].center.x *= r;
+  triangle[0].center.y *= r;
+  triangle[0].center.z *= r;
+
+  triangle[0].d = DEG_RAD * asin(triangle[0].center.z);
+  triangle[0].r = DEG_RAD * atan2(triangle[0].center.y, triangle[0].center.x);
+
+  for (i = 0; i < 3; i++) {
+    triangle[0].dv[i] = DEG_RAD * asin(triangle[0].vertex[i].z);
+    triangle[0].rv[i] = DEG_RAD * atan2(triangle[0].vertex[i].y, triangle[0].vertex[i].x);
+  }
+
+  return TRUE;
+}
+
+// take a list of triangles from one level and return a list of triangles in the next level
+// we are doing basic edge division, always yielding 4x as many new triangles as old;
+SkyTriangle *sky_divide_triangles (SkyTriangle *in, int *ntriangles) {
+
+  int i, j, Ntriangles, Nt;
+  SkyTriangle *out;
+
+  Ntriangles = *ntriangles * 4;
+  ALLOCATE (out, SkyTriangle, Ntriangles);
+
+  Nt = 0;
+  for (i = 0; i < *ntriangles; i++) {
+    for (j = 0; j < 3; j++) {
+      out[4*i + j].vertex[0] = in[i].vertex[j];
+      out[4*i + j].vertex[1] = sky_divide_edge (in[i].vertex[j], in[i].vertex[(j+1)%3]);
+    }
+    for (j = 0; j < 3; j++) {
+      out[4*i + j].vertex[2] = out[4*i + (j+2)%3].vertex[1];
+    }
+    for (j = 0; j < 3; j++) {
+      out[4*i + 3].vertex[j] = out[4*i + j].vertex[1];
+    }
+    Nt += 4;
+  }
+  *ntriangles = Nt;
+  return (out);
+}
+
+// take a list of triangles from one level and return a list of triangles in the next level
+// we are doing basic edge division, always yielding 4x as many new triangles as old;
+Point sky_divide_edge (Point v1, Point v2) {
+
+  double r;
+  Point out;
+
+  out.x = v1.x + v2.x;
+  out.y = v1.y + v2.y;
+  out.z = v1.z + v2.z;
+
+  r = 1.0 / sqrt (SQ(out.x) + SQ(out.y) + SQ(out.z));
+
+  out.x *= r;
+  out.y *= r;
+  out.z *= r;
+
+  return (out);
+}
+
+# define THETA RAD_DEG*26.565
+# define D_PSI RAD_DEG*360.0/5.0
+
+SkyTriangle *sky_base_triangles (int *ntriangles) {
+
+  int i;
+  double ctht, stht, psi;
+  SkyTriangle *tri;
+
+  // generate 0-level triangles
+  ALLOCATE (tri, SkyTriangle, 20);
+
+  for (i = 0; i < 20; i++) {
+    memset (&tri[i], 0, sizeof(SkyTriangle));
+  }
+
+  ctht = cos(THETA);
+  stht = sin(THETA);
+
+  for (i = 0; i < 5; i++) {
+    tri[i].vertex[0].x = +0;
+    tri[i].vertex[0].y = +0;
+    tri[i].vertex[0].z = +1;
+
+    psi = (i + 0.0)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = +stht;
+    
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = +stht;
+  }    
+  
+  for (i = 5; i < 10; i++) {
+    psi = (i + 0.0)*D_PSI;
+    tri[i].vertex[0].x = +ctht*cos(psi);
+    tri[i].vertex[0].y = +ctht*sin(psi);
+    tri[i].vertex[0].z = +stht;
+    
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = -stht;
+
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = +stht;
+  }    
+  
+  for (i = 10; i < 15; i++) {
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[0].x = +ctht*cos(psi);
+    tri[i].vertex[0].y = +ctht*sin(psi);
+    tri[i].vertex[0].z = -stht;
+
+    psi = (i + 1.0)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = +stht;
+    
+    psi = (i + 1.5)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = -stht;
+  }    
+  
+  for (i = 15; i < 20; i++) {
+    psi = (i + 0.5)*D_PSI;
+    tri[i].vertex[1].x = +ctht*cos(psi);
+    tri[i].vertex[1].y = +ctht*sin(psi);
+    tri[i].vertex[1].z = -stht;
+    
+    psi = (i + 1.5)*D_PSI;
+    tri[i].vertex[2].x = +ctht*cos(psi);
+    tri[i].vertex[2].y = +ctht*sin(psi);
+    tri[i].vertex[2].z = -stht;
+
+    tri[i].vertex[0].x = +0;
+    tri[i].vertex[0].y = +0;
+    tri[i].vertex[0].z = -1;
+  }    
+  
+  *ntriangles = 20;
+  return tri;
+}
+
+int sky_base_rotation (SkyTriangle *base, int Nbase) {
+
+  // apply the three euler angles (A, B, C)
+  // XXX for now, just apply A and B
+
+  int i, j, ix;
+  float rot[3][3], v[3];
+
+  rot[0][0] = +cos(EULER_A)*cos(EULER_B);
+  rot[1][0] = +sin(EULER_A)*cos(EULER_B);
+  rot[2][0] = +sin(EULER_B);
+
+  rot[0][1] = -sin(EULER_A);
+  rot[1][1] = +cos(EULER_A);
+  rot[2][1] = +0.0;
+
+  rot[0][2] = -cos(EULER_A)*sin(EULER_B);
+  rot[1][2] = -sin(EULER_A)*sin(EULER_B);
+  rot[2][2] = +cos(EULER_B);
+
+  for (i = 0; i < Nbase; i++) {
+    for (j = 0; j < 3; j++) {
+      for (ix = 0; ix < 3; ix++) {
+	v[ix] = 0.0;
+	v[ix] += base[i].vertex[j].x * rot[0][ix];
+	v[ix] += base[i].vertex[j].y * rot[1][ix];
+	v[ix] += base[i].vertex[j].z * rot[2][ix];
+      }
+      base[i].vertex[j].x = v[0];
+      base[i].vertex[j].y = v[1];
+      base[i].vertex[j].z = v[2];
+    }
+  }
+  return (TRUE);
+}
+
+int sky_tessalation_init (double scale) {
+
+  ALLOCATE (refcoords, Coords, 1);
+  refcoords[0].crval1 = refcoords[0].crval2 = 0.0;
+  refcoords[0].crpix1 = refcoords[0].crpix2 = 0.0;
+  refcoords[0].cdelt1 = refcoords[0].cdelt2 = scale / 3600;
+  refcoords[0].pc1_1 = refcoords[0].pc2_2 = 1.0;
+  refcoords[0].pc1_2 = refcoords[0].pc2_1 = 0.0;
+  refcoords[0].Npolyterms = 0;
+  memset (refcoords[0].polyterms, 0, 14*sizeof(float));
+  strcpy (refcoords[0].ctype, "RA---TAN");
+  return (TRUE);
+}
+
+// free the space used by the current vtable entries
+int dvo_image_clear_vtable (FITS_DB *db) {
+
+  int i;
+
+  // free memory used by the current vtable rows
+  for (i = 0; i < db[0].vtable.Nrow; i++) {
+    free (db[0].vtable.buffer[i]);
+  }
+  REALLOCATE (db[0].vtable.buffer, char *, 1);
+  REALLOCATE (db[0].vtable.row, int, 1);
+  db[0].vtable.Nrow   = 0;
+
+  // reset db[0].theader(NAXIS1) to match Image
+  gfits_modify (&db[0].theader, "NAXIS1", "%d", 1, sizeof(Image));
+  db[0].theader.Naxis[0] = sizeof(Image);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/skycells.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/skycells.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/skycells.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "skycells.h"
+
+int main (int argc, char **argv) {
+
+  int status, level;
+  FITS_DB db;
+
+  SetSignals ();
+  ConfigInit_skycells (&argc, argv);
+  args_skycells (argc, argv);
+  level = atoi (argv[1]);
+  
+  /*** update the image table ***/
+  /* setup image table format and lock */
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) {
+    if (VERBOSE) fprintf (stderr, "can't find %s, creating a new one\n", ImageCat);
+    dvo_image_create (&db, 25.0);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) {
+      Shutdown ("can't read image catalog %s", db.filename);
+    }
+  }
+
+  // we have to put the database update calls deep down in the sky_tessalation code so we
+  // can write out the skycells in limited-sized chunks.
+  sky_tessalation (&db, level, NMAX, MODE, SCALE);
+
+  dvo_image_unlock (&db);
+  exit (0);
+}
+
+/*
+ * - we start with the image database loaded into memory (db)
+ * - we convert this to an empty vtable 
+ * - add groups of images to the vtable
+ * - we write out the vtable data
+ * - zero-out the saved entries, 
+ */
+
Index: /tags/ipp-2-5-1/Ohana/src/addstar/src/update_coords.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/addstar/src/update_coords.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/addstar/src/update_coords.c	(revision 17422)
@@ -0,0 +1,53 @@
+# include "addstar.h"
+
+/* find the average and scatter for R and D - no outlier rejection */
+void update_coords (Average *average, Measure *measure, int *next) {
+
+  int i, m, Npt;
+  double R, D, r, d;
+  double r2, d2, dR2, dD2;
+
+  Npt = r = d = r2 = d2 = 0;
+
+  if (average[0].Nmeasure < 2) return;
+
+  /* find the average & sum-square (does not use reference coordinates) */
+  m = average[0].measureOffset;  /* first measurement of this star */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    if (measure[m].t == 0) {
+      m = next[m];
+      continue;
+    }
+    R = measure[m].dR;
+    D = measure[m].dD;
+    r += R;
+    d += D;
+    r2 += R*R;
+    d2 += D*D;
+    m = next[m];
+    Npt ++;
+  }
+  if (Npt < 1) return;
+
+  /* apply average offset */
+  r = r / Npt;  /* these are corrections in 1/100 arcsec to RA and DEC */
+  d = d / Npt;
+  average[0].R -= r / 3600.0;
+  average[0].D -= d / 3600.0;
+  m = average[0].measureOffset;  /* first measurement of this star */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    measure[m].dR -= r;
+    measure[m].dD -= d;
+    m = next[m];
+  }
+  
+  /* measure scatter, if possible */
+  if (Npt < 2) return;
+
+  dR2 = r2 / Npt - r*r;
+  dD2 = d2 / Npt - d*d;
+  average[0].Xp = sqrt (dD2 + dR2 / SQ(cos(d*RAD_DEG)));
+  /* Xp is scatter in position in hundredths of arcsec */
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/.cvsignore	(revision 17422)
@@ -0,0 +1,1 @@
+bin
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/Makefile	(revision 17422)
@@ -0,0 +1,94 @@
+default: kapa
+help:
+	@echo "make options: kapa (default)"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/kapa2
+LIB	=	$(HOME)/lib
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+INC	=	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+LDDEPS  = $(DESTLIB)/libkapa.a $(DESTLIB)/libdvo.a $(DESTLIB)/libFITS.a $(DESTLIB)/libohana.a
+INDEPS  = $(DESTINC)/kapa.h $(DESTINC)/dvo.h $(DESTINC)/gfitsio.h $(DESTINC)/ohana.h
+
+kapa: $(BIN)/kapa.$(ARCH)
+install: $(DESTBIN)/kapa
+
+KAPA = \
+$(SRC)/kapa.$(ARCH).o                     $(SRC)/args.$(ARCH).o               \
+$(SRC)/SetUpGraphic.$(ARCH).o             $(SRC)/NameWindow.$(ARCH).o         \
+$(SRC)/CheckColors.$(ARCH).o              $(SRC)/MakeGC.$(ARCH).o             \
+$(SRC)/CheckDisplayName.$(ARCH).o  	  $(SRC)/MapWindow.$(ARCH).o          \
+$(SRC)/CheckGeometry.$(ARCH).o     	  $(SRC)/OpenDisplay.$(ARCH).o        \
+$(SRC)/CloseDisplay.$(ARCH).o      	  $(SRC)/QuitX.$(ARCH).o              \
+$(SRC)/CreateWindow.$(ARCH).o      	  $(SRC)/SetNormalHints.$(ARCH).o     \
+$(SRC)/GetColor.$(ARCH).o          	  $(SRC)/SetWMHints.$(ARCH).o         \
+$(SRC)/LoadFont.$(ARCH).o          	  $(SRC)/TopWindow.$(ARCH).o          \
+$(SRC)/MakeCursor.$(ARCH).o		  $(SRC)/FlushDisplay.$(ARCH).o       \
+$(SRC)/LoadObject.$(ARCH).o		  $(SRC)/LoadFrame.$(ARCH).o	      \
+$(SRC)/LoadTextlines.$(ARCH).o		  $(SRC)/LoadLabels.$(ARCH).o         \
+$(SRC)/DrawObjects.$(ARCH).o              $(SRC)/DrawFrame.$(ARCH).o          \
+$(SRC)/DrawTextlines.$(ARCH).o		  $(SRC)/DrawLabels.$(ARCH).o         \
+$(SRC)/PSObjects.$(ARCH).o		  $(SRC)/PSFrame.$(ARCH).o            \
+$(SRC)/PSLabels.$(ARCH).o		  $(SRC)/PSTextlines.$(ARCH).o        \
+$(SRC)/bDrawObjects.$(ARCH).o	  	  $(SRC)/bDrawFrame.$(ARCH).o         \
+$(SRC)/bDrawLabels.$(ARCH).o              $(SRC)/bDrawIt.$(ARCH).o            \
+$(SRC)/PNGit.$(ARCH).o                    $(SRC)/PPMit.$(ARCH).o	      \
+$(SRC)/PSit.$(ARCH).o                     $(SRC)/CrossHairs.$(ARCH).o         \
+$(SRC)/CheckPipe.$(ARCH).o                $(SRC)/EventLoop.$(ARCH).o          \
+$(SRC)/Reconfig.$(ARCH).o                 $(SRC)/Refresh.$(ARCH).o            \
+$(SRC)/Layout.$(ARCH).o		  	  $(SRC)/Sections.$(ARCH).o	      \
+$(SRC)/Graphs.$(ARCH).o                   $(SRC)/SetGraphSize.$(ARCH).o       \
+$(SRC)/Resize.$(ARCH).o                   $(SRC)/ErasePlots.$(ARCH).o         \
+$(SRC)/EraseImage.$(ARCH).o         	  $(SRC)/SetToolbox.$(ARCH).o         \
+$(SRC)/EraseCurrentPlot.$(ARCH).o         $(SRC)/EraseSections.$(ARCH).o      \
+$(SRC)/SetSection.$(ARCH).o		  $(SRC)/DefineSection.$(ARCH).o      \
+$(SRC)/SetLimits.$(ARCH).o                $(SRC)/SetFont.$(ARCH).o            \
+$(SRC)/Center.$(ARCH).o                   $(SRC)/Remap.$(ARCH).o              \
+$(SRC)/Remap8.$(ARCH).o                   $(SRC)/Remap16.$(ARCH).o            \
+$(SRC)/Remap24.$(ARCH).o                  $(SRC)/Remap32.$(ARCH).o            \
+$(SRC)/LoadPicture.$(ARCH).o              $(SRC)/Image.$(ARCH).o              \
+$(SRC)/SetImageSize.$(ARCH).o             $(SRC)/GetPixelCount.$(ARCH).o      \
+$(SRC)/StatusBox.$(ARCH).o                $(SRC)/UpdateStatusBox.$(ARCH).o    \
+$(SRC)/DrawButton.$(ARCH).o               $(SRC)/DrawBitmap.$(ARCH).o         \
+$(SRC)/hh_hms.$(ARCH).o	                  $(SRC)/CreateZoom.$(ARCH).o         \
+$(SRC)/CreatePicture.$(ARCH).o            $(SRC)/CreateColorbar.$(ARCH).o     \
+$(SRC)/MakeColormap.$(ARCH).o		  $(SRC)/SetColormap.$(ARCH).o        \
+$(SRC)/PaintOverlay.$(ARCH).o		  $(SRC)/SetGraphData.$(ARCH).o       \
+$(SRC)/LoadOverlay.$(ARCH).o		  $(SRC)/EraseOverlay.$(ARCH).o       \
+$(SRC)/SaveOverlay.$(ARCH).o              $(SRC)/SetImageData.$(ARCH).o       \
+$(SRC)/CSaveOverlay.$(ARCH).o		  $(SRC)/EraseOverlay.$(ARCH).o       \
+$(SRC)/CheckVisual.$(ARCH).o              $(SRC)/CursorOps.$(ARCH).o          \
+$(SRC)/InterpretKeys.$(ARCH).o            $(SRC)/InterpretPresses.$(ARCH).o   \
+$(SRC)/DragColorbar.$(ARCH).o             $(SRC)/Reorient.$(ARCH).o           \
+$(SRC)/InButton.$(ARCH).o                 $(SRC)/InPicture.$(ARCH).o          \
+$(SRC)/CheckButtons.$(ARCH).o             $(SRC)/InvertButton.$(ARCH).o       \
+$(SRC)/UpdatePointer.$(ARCH).o            $(SRC)/JPEGit24.$(ARCH).o           \
+$(SRC)/bDrawOverlay.$(ARCH).o             $(SRC)/ButtonFunctions.$(ARCH).o    \
+$(SRC)/PSimage.$(ARCH).o                  $(SRC)/PSPixmap.$(ARCH).o           \
+$(SRC)/PSOverlay.$(ARCH).o                $(SRC)/SetChannel.$(ARCH).o         \
+$(SRC)/SetColorScale.$(ARCH).o            $(SRC)/ColorCube.$(ARCH).o          \
+$(SRC)/ColorHistogram.$(ARCH).o           $(SRC)/CreateWide.$(ARCH).o
+
+#$(SRC)/CreateZoom8.$(ARCH).o              $(SRC)/CreateZoom16.$(ARCH).o       \
+#$(SRC)/CreateZoom24.$(ARCH).o             $(SRC)/CreateZoom32.$(ARCH).o       \
+
+OBJ  =  $(KAPA)
+
+# dependancy rules for include files ########################
+$(OBJ): $(INC)/Ximage.h \
+	$(INC)/constants.h \
+	$(INC)/structures.h \
+	$(INC)/prototypes.h
+
+$(OBJ): $(INDEPS) $(LDDEPS)
+
+$(BIN)/kapa.$(ARCH): $(OBJ)
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/doc/UpgradeConcepts.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/doc/UpgradeConcepts.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/doc/UpgradeConcepts.txt	(revision 17422)
@@ -0,0 +1,54 @@
+
+2007.05.08
+
+  User Commands:
+  * Add a section (position, type, name)
+  * Add a section element (add graph, add image)
+  * Del a section (by name)
+  * Del a section element (del graph, del image)
+  * Resize window (resizes each section)
+  * Set active section
+  * Get active section
+
+2007.05.06
+
+I am examining plans to upgrade the Kapa / Kii programs.  I have several ideas on this topic:
+
+* Completely merge the code base.  There are many common features, and
+  the merge will be easy.  The biggest difference between them from a
+  programming point of view is that Kapa uses global variables for the
+  layout and graphic (and other minor data), while Kii passes around
+  the Graphic / Layout elements.
+
+  The more object-oriented way to do this is to have private static
+  data for these concepts and to provide accessor functions to operate
+  on or retreive these pointers.  
+
+* Allow an arbitrary number of the graph / image widgets.  The image
+  widget should be considered a single item, which includes the main
+  image window, the zoom box, the status box, the buttons, etc.  The
+  positioning of the multiple graph / image elements in a single
+  window could be equivalent to the 'section' command for the graph
+  sub-boxes.  An image or a graph could be place at any X,Y,dX,dY
+  position with X,Y having a range of 0-1.  In addition, a graph
+  could be optionally tied to a specific image, with the location
+  determined by the image window location (ie, outer box of the image
+  window matched to bounding box of the graph).
+
+* Event parsing would need to go through the list of graph / image
+  elements to determine if the corresond to any of them.  
+
+* Drawing should be done in the order: images first, graphs second.
+  The sequence for the images and / or graphs should be adjustable.
+
+* create an equivalent to the zoom box which is a wide full-view of
+  the current image.
+
+* add the image flipping; this may be easiest using multiple versions
+  of the code with the correct sign flips...
+
+* carry the image data as a full 32 bit buffer
+
+* (optionally?) listed on an IP socket?
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/Ximage.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/Ximage.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/Ximage.h	(revision 17422)
@@ -0,0 +1,15 @@
+# include <X11/Xatom.h>
+# include <X11/Xlib.h>
+# include <X11/Xresource.h>
+# include <X11/Xutil.h>
+# include <X11/cursorfont.h>
+# include <X11/keysym.h>
+# include <X11/keysymdef.h>
+
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include "constants.h"
+# include "structures.h"
+# include "prototypes.h"
+# include "globals.h"
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/alphabet.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/alphabet.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/alphabet.h	(revision 17422)
@@ -0,0 +1,53 @@
+# include "../rotfont/times8.h"
+# include "../rotfont/times12.h"
+# include "../rotfont/times14.h"
+# include "../rotfont/times18.h"
+# include "../rotfont/times24.h"
+
+# include "../rotfont/courier8.h"
+# include "../rotfont/courier12.h"
+# include "../rotfont/courier14.h"
+# include "../rotfont/courier18.h"
+# include "../rotfont/courier24.h"
+
+# include "../rotfont/helvetica8.h"
+# include "../rotfont/helvetica12.h"
+# include "../rotfont/helvetica14.h"
+# include "../rotfont/helvetica18.h"
+# include "../rotfont/helvetica24.h"
+
+# include "../rotfont/symbol8.h"
+# include "../rotfont/symbol12.h"
+# include "../rotfont/symbol14.h"
+# include "../rotfont/symbol18.h"
+# include "../rotfont/symbol24.h"
+
+# define DEFFONT 1
+static FontSet HardwiredFonts[] = {
+  {times8font,  "times", 8},
+  {times12font, "times", 12},
+  {times14font, "times", 14},
+  {times18font, "times", 18},
+  {times24font, "times", 24},
+  
+  {courier8font,  "courier", 8},
+  {courier12font, "courier", 12},
+  {courier14font, "courier", 14},
+  {courier18font, "courier", 18},
+  {courier24font, "courier", 24},
+  
+  {helvetica8font,  "helvetica", 8},
+  {helvetica12font, "helvetica", 12},
+  {helvetica14font, "helvetica", 14},
+  {helvetica18font, "helvetica", 18},
+  {helvetica24font, "helvetica", 24},
+  
+  {symbol8font,  "symbol", 8},
+  {symbol12font, "symbol", 12},
+  {symbol14font, "symbol", 14},
+  {symbol18font, "symbol", 18},
+  {symbol24font, "symbol", 24}
+};
+
+/* put these as static in RotFont.c with accessor functions */
+# define NROT 256
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/buttons.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/buttons.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/buttons.h	(revision 17422)
@@ -0,0 +1,144 @@
+#define PS_width 25
+#define PS_height 25
+static char PS_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x81, 0x3f, 0x00,
+   0x82, 0x67, 0x60, 0x00, 0x02, 0x26, 0x40, 0x00, 0x02, 0x24, 0x40, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00,
+   0x02, 0x24, 0x00, 0x00, 0x02, 0x64, 0x00, 0x00, 0x02, 0xc6, 0x00, 0x00,
+   0x82, 0x07, 0x07, 0x00, 0xfe, 0x01, 0x0c, 0x00, 0x02, 0x00, 0x18, 0x00,
+   0x02, 0x00, 0x20, 0x00, 0x02, 0x00, 0x60, 0x00, 0x02, 0x00, 0x40, 0x00,
+   0x02, 0x00, 0xc0, 0x00, 0x02, 0x10, 0x80, 0x00, 0x02, 0x10, 0xc0, 0x00,
+   0x02, 0x60, 0x60, 0x00, 0x02, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00}; 
+#define grey_width 25
+#define grey_height 25
+static char grey_bits[] = {
+   0x90, 0x40, 0xf7, 0x01, 0x04, 0xaa, 0xad, 0x01, 0x68, 0xd5, 0xfe, 0x01,
+   0x82, 0x28, 0xdb, 0x01, 0x10, 0xd2, 0x75, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xac, 0x50, 0xbf, 0x01, 0x00, 0xaa, 0xf9, 0x01, 0x52, 0xd5, 0xd6, 0x01,
+   0x80, 0x28, 0x6f, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x44, 0xad, 0xba, 0x01,
+   0x90, 0x50, 0xf7, 0x01, 0x02, 0xaa, 0xcd, 0x01, 0x68, 0xd5, 0x7e, 0x01,
+   0x80, 0x28, 0xfb, 0x01, 0x14, 0xd2, 0xb5, 0x01, 0x40, 0xad, 0xee, 0x01,
+   0xaa, 0x50, 0xdf, 0x01, 0x00, 0xaa, 0x79, 0x01, 0x50, 0xd5, 0xf6, 0x01,
+   0x84, 0x28, 0xaf, 0x01, 0x28, 0xd2, 0xfd, 0x01, 0x40, 0xad, 0xda, 0x01,
+   0x90, 0x50, 0x77, 0x01};
+#define rainbow_width 25
+#define rainbow_height 25
+static char rainbow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x80, 0xff, 0x00, 0x00, 0x70, 0x00, 0x01, 0x00, 0x0c, 0x7f, 0x00,
+   0x00, 0xe3, 0x80, 0x01, 0xc0, 0x9c, 0xff, 0x00, 0x20, 0x62, 0x00, 0x01,
+   0x90, 0x99, 0xff, 0x00, 0x48, 0x76, 0x00, 0x01, 0x24, 0x09, 0x00, 0x00,
+   0x94, 0x06, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
+   0xa5, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+   0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+   0x15, 0x00, 0x00, 0x00};
+#define recenter_width 25
+#define recenter_height 25
+static char recenter_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x30, 0x00, 0x18, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x18, 0x00, 0x30, 0x00, 0x08, 0x00, 0x20, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x0c, 0x7c, 0x60, 0x00,
+   0x04, 0x7c, 0x40, 0x00, 0x0c, 0x7c, 0x60, 0x00, 0x0c, 0x38, 0x60, 0x00,
+   0x0c, 0x00, 0x60, 0x00, 0x08, 0x00, 0x20, 0x00, 0x18, 0x00, 0x30, 0x00,
+   0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x18, 0x00, 0xe0, 0x00, 0x0e, 0x00,
+   0x80, 0xef, 0x03, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define heat_width 25
+#define heat_height 25
+static char heat_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0xd8, 0x01,
+   0x00, 0x0c, 0x37, 0x00, 0xf8, 0xc9, 0x6f, 0x00, 0x0c, 0xde, 0xf0, 0x00,
+   0x06, 0x5b, 0x1f, 0x00, 0x7b, 0xd9, 0x0c, 0x00, 0xc9, 0x70, 0x06, 0x00,
+   0x81, 0x71, 0x03, 0x00, 0x78, 0xb9, 0x39, 0x00, 0xc8, 0xdb, 0x0f, 0x00,
+   0x84, 0xf7, 0x7f, 0x00, 0x04, 0xff, 0xc3, 0x00, 0xe6, 0xfe, 0x8e, 0x00,
+   0x9a, 0x7d, 0x0b, 0x00, 0x08, 0xbc, 0x01, 0x00, 0x08, 0xfc, 0x3f, 0x00,
+   0x0c, 0xff, 0x78, 0x00, 0x84, 0x39, 0x60, 0x00, 0x40, 0x38, 0x40, 0x00,
+   0x60, 0x38, 0x40, 0x00, 0x20, 0x38, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+   0x00, 0xd6, 0x00, 0x00};
+#define red_width 25
+#define red_height 25
+static char red_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfc, 0x03, 0x00,
+   0x20, 0x07, 0x06, 0x00, 0xe0, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x06, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0xe0, 0x00, 0x00, 0x20, 0x7e, 0x00, 0x00, 0xe0, 0xc3, 0x00, 0x00,
+   0x20, 0xc0, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0x80, 0x01, 0x00,
+   0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
+   0x20, 0x00, 0x06, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x18, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define green_width 25
+#define green_height 25
+static char green_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00,
+   0x00, 0x06, 0x04, 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0x40, 0x80, 0x0f, 0x00, 0x40, 0xc0, 0x30, 0x00,
+   0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,
+   0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x08, 0x00,
+   0x00, 0x03, 0x08, 0x00, 0x00, 0x7e, 0x0e, 0x00, 0x00, 0xc0, 0x03, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define blue_width 25
+#define blue_height 25
+static char blue_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xfe, 0x00, 0x00,
+   0xc0, 0x83, 0x07, 0x00, 0xc0, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0x40, 0x80, 0x07, 0x00, 0x40, 0xe0, 0x0c, 0x00, 0xc0, 0x3f, 0x08, 0x00,
+   0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x40, 0x00, 0x02, 0x00,
+   0xc0, 0x80, 0x03, 0x00, 0x40, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define yellow_width 25
+#define yellow_height 25
+static char yellow_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
+   0x80, 0x01, 0x0c, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x02, 0x02, 0x00,
+   0x00, 0x84, 0x01, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+   0x00, 0x30, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00};
+#define hms_width 25
+#define hms_height 25
+static char hms_bits[] = {
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+   0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0xf0, 0x00, 0x1e, 0xef, 0x89, 0x00,
+   0x22, 0x11, 0x09, 0x00, 0x22, 0x11, 0x71, 0x00, 0x22, 0x11, 0x81, 0x00,
+   0x22, 0x11, 0x89, 0x00, 0x22, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x1e, 0xc7, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0x80, 0x00,
+   0x40, 0x80, 0x80, 0x00, 0x5c, 0xb8, 0xb8, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x42, 0x84, 0x84, 0x00, 0x42, 0x84, 0x84, 0x00, 0x62, 0xc4, 0xc4, 0x00,
+   0x5c, 0xb9, 0xb8, 0x00};
+#define flipx_width 25
+#define flipx_height 25
+static char flipx_bits[] = {
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x80, 0x10, 0x02, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x20, 0x10, 0x08, 0x00, 0x10, 0x10, 0x10, 0x00,
+   0x08, 0x00, 0x20, 0x00, 0x04, 0x10, 0x40, 0x00, 0x02, 0x10, 0x80, 0x00,
+   0x01, 0x00, 0x00, 0x01, 0x02, 0x10, 0x80, 0x00, 0x04, 0x10, 0x40, 0x00,
+   0x08, 0x00, 0x20, 0x00, 0x10, 0x10, 0x10, 0x00, 0x20, 0x10, 0x08, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x80, 0x10, 0x02, 0x00, 0x00, 0x11, 0x01, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00 };
+#define flipy_width 25
+#define flipy_height 25
+static char flipy_bits[] = {
+   0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
+   0x00, 0x82, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x00, 0x02, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x20, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0xb7, 0x6d, 0xdb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x00, 0x08, 0x00,
+   0x40, 0x00, 0x04, 0x00, 0x80, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00,
+   0x00, 0x82, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
+   0x00, 0x10, 0x00, 0x00 };
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/constants.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/constants.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/constants.h	(revision 17422)
@@ -0,0 +1,93 @@
+/* hardwired values for some window parameters */
+
+# define EVENT_MASK (long) \
+(ButtonPressMask \
+ | ClientMessage \
+ | ButtonReleaseMask \
+ | KeyPressMask \
+ | ExposureMask \
+ | StructureNotifyMask \
+ | PointerMotionMask)
+
+# define NCHANNELS 3
+# define NPIXELS_DYNAMIC 128
+
+// XXX for the moment, this is set to match the values in SetColorScale3D_CC
+# define NPIXELS_STATIC 4600
+
+# define PAD1  3
+# define PAD2  5
+# define TEXTPAD 25
+# define COLORPAD 10
+# define TEXT_Y 15
+# define ZOOM_X 152
+# define ZOOM_Y 152
+# define NOVERLAYS 4
+# define BUTTON_WIDTH 28
+# define BUTTON_HEIGHT 28
+
+# define DEFAULT_CURSOR XC_crosshair
+# define BORDER_WIDTH 2
+# define MIN_WIDTH 50
+# define MIN_HEIGHT 50
+# define LABEL_MAXLEN 128
+
+typedef enum {
+    KAPA_SCALE_1D,
+    KAPA_SCALE_3D_RUFF,
+    KAPA_SCALE_3D_FULL
+} KapaColorScaleMode;
+
+/* label names */
+typedef enum {
+  LABELX0,
+  LABELY0,
+  LABELX1,
+  LABELY1,
+  LABELUL,
+  LABELUR,
+  LABELLL,
+  LABELLR
+} KapaLabelMode;
+
+typedef enum {
+  KAPA_CHANNEL_RED,
+  KAPA_CHANNEL_GREEN,
+  KAPA_CHANNEL_BLUE,
+} KapaChannels;
+
+// use an enum to identify the 3 dimensions:
+typedef enum {
+  CC_X,
+  CC_Y,
+  CC_Z,
+} CCDimen;
+
+/* EVENT_MASK consists of:
+
+ExposureMask        : Expose
+StructureNotifyMask : CirculateNotify | 
+                      ConfigureNotify | 
+                      DestroyNotify   | 
+		      GravityNotify   | 
+		      MapNotify       |
+		      ReparentNotify  |
+		      UnmapNotify
+ButtonPressMask     : ButtonPress
+ButtonReleaseMask   : ButtonRelease
+KeyPressMask        : KeyPress
+PointerMotionMask   : MotionNotify
+(always)            : ClientMessage 
+(always)            : MappingNotify
+
+*/
+
+# define NOVERLAYS 4
+/* number of overlays is defined here.
+   the number is also crucial in the following files:
+   PositionPictures.c
+   MakeColormap.c
+   prototypes.h
+*/
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/globals.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/globals.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/globals.h	(revision 17422)
@@ -0,0 +1,28 @@
+
+int   ACTIVE_CURSOR;
+int   HAVE_BACKING;
+int   DEBUG;
+int   USE_XWINDOW;
+int   MAP_WINDOW;
+int   FOREGROUND;
+char *NAME_WINDOW;
+
+/* these should be absorbed into KapaImageWidget 
+int OVERLAY[NOVERLAYS];
+int MOVE_POINTER;
+int DECIMAL_DEG;
+*/
+
+/* file descriptor for socket connection to mana */
+// int sock; 
+
+/* each layout / section defines one of the active portions of the graphics window
+   there may be an arbitrary number of sections, and each section may fill an arbitrary
+   rectangle in the X window.  The startup configuration assumes 1 section filling the 
+   entire X window. */
+
+// Layout *section;
+// int    Nsection, TheSection;
+
+/* graphic defines the basic details of the X window */
+// Graphic graphic;
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/icons.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/icons.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/icons.h	(revision 17422)
@@ -0,0 +1,43 @@
+
+#define icon_width 29
+#define icon_height 29
+static unsigned char icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04,
+   0x04, 0x41, 0x10, 0x04, 0x04, 0x41, 0x10, 0x04, 0xfc, 0xff, 0xff, 0x07,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+/* *** icons for buttons, not used ***
+#define arrow_width 6
+#define arrow_height 9
+static char arrow_bits[] = {
+   0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define back_width 16
+#define back_height 16
+static char back_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x3c, 0x00, 0x3f, 0xc0, 0x3f,
+   0xf0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f,
+   0x00, 0x3c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+#define fore_width 16
+#define fore_height 16
+static char fore_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03,
+   0xfc, 0x0f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x0f, 0xfc, 0x03, 0xfc, 0x00,
+   0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00};
+#define stop_width 16
+#define stop_height 16
+static char stop_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
+   0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+*/
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.proto.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.proto.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.proto.h	(revision 17422)
@@ -0,0 +1,124 @@
+
+/* top-level program */
+int           args                PROTO((int *argc, char **argv));
+void          SetUpDisplay        PROTO((Graphic *, int *, char **));
+void          SetUpWindow         PROTO((Graphic *, int *, char **));
+void          DefineLayout        PROTO((Layout *, Graphic *, int, char **));
+int           EventLoop           PROTO((Graphic *, Layout *));
+void          CloseDisplay        PROTO((Graphic *));
+
+/* SetUpDisplay */
+void          CheckDisplayName    PROTO((int *, char **, char *));
+Display      *OpenDisplay         PROTO((char *, int *));
+void	      CheckVisual	  PROTO((Graphic *graphic, int *argc, char **argv));
+void          CheckColors         PROTO((Graphic *, int *, char **));
+
+/* SetUpWindow */
+void          CheckGeometry       PROTO((int *, char **, Graphic *));
+void          TopWindow           PROTO((Graphic *, Icon *));
+void           CreateWindow       PROTO((Graphic *, Window, int, long));
+void           MakeGC             PROTO((Graphic *));
+void           MakeCursor         PROTO((Graphic *, unsigned int));
+void          LoadFont            PROTO((Graphic *, int *, char **, char *));
+void           CheckFontName      PROTO((int *, char **, char *));
+void          SetNormalHints      PROTO((Graphic *));
+void          SetWMHints          PROTO((Graphic *, Icon *));
+void          NameWindow          PROTO((Graphic *, char *));
+void          MapWindow           PROTO((Graphic *));
+
+/* DefineLayout - fns below may be called if layout is changed? */
+void          PositionPictures    PROTO((Layout *, Graphic *));
+
+void          MakeColormap        PROTO((Graphic *, Layout *, int, char **));
+int	      SetColormap	  PROTO((Graphic *graphic, Layout *layout, char *name));
+void          CreateColorbar      PROTO((Layout *, Graphic *));
+void          CreatePicture       PROTO((Layout *, Graphic *));
+
+/* EventLoop */
+int           CheckPipe           PROTO((Graphic *, Layout *));
+int           Reconfig            PROTO((Graphic *, Layout *, XEvent *));
+void          Refresh             PROTO((Graphic *, Layout *, int));
+int           UpdatePointer       PROTO((Graphic *, Layout *, XMotionEvent *));
+int           InterpretKeys       PROTO((Graphic *, Layout *, XEvent *));
+int           InterpretPresses    PROTO((Graphic *, Layout *, XButtonEvent *));
+
+/* CheckPipe */
+int           NewPicture          PROTO((Graphic *, Layout *));
+int           EraseOverlay        PROTO((Graphic *, Layout *));
+int           LoadOverlay         PROTO((Graphic *, Layout *));
+int	      LoadTickmarks	  PROTO((Graphic *graphic, Layout *layout));
+int           SaveOverlay         PROTO((Graphic *, Layout *));
+int           CSaveOverlay        PROTO((Graphic *, Layout *));
+int	      PSit		  PROTO((Graphic *graphic, Layout *layout, int Raw));
+int	      JPEGit		  PROTO((Graphic *graphic, Layout *layout));
+int	      JPEGit24		  PROTO((Graphic *graphic, Layout *layout));
+int	      Resize		  PROTO((Graphic *graphic, Layout *layout));
+int	      Center		  PROTO((Graphic *graphic, Layout *layout));
+
+/* imagezoom functions */
+void          CreateZoom          PROTO((Layout *, Graphic *, double, double));
+void	      CreateZoom8	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom16	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom24	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      CreateZoom32	  PROTO((Layout *layout, Graphic *graphic, double x, double y));
+void	      UpdateStatusBox	  PROTO((Graphic *graphic, Layout *layout, double x, double y, double z, int mode));
+void          CrossHairs          PROTO((Graphic *, Layout *));
+
+/* X image drawing functions */
+void          Reorient            PROTO((Graphic *, Layout *, double, double, int));
+void          ReorientOnButton    PROTO((Graphic *, Layout *, XButtonEvent *));
+int	      Recenter		  PROTO((Graphic *graphic, Layout *layout));
+void          PaintOverlay        PROTO((Graphic *, Layout *, int));
+void	      PaintTickmarks	  PROTO((Graphic *graphic, Layout *layout));
+void          Remap               PROTO((Graphic *, Layout *, Matrix  *));
+void	      Remap8		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap16		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap24		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+void	      Remap32		  PROTO((Graphic *graphic, Layout *layout, Matrix *matrix));
+
+/* PS image drawing functions */
+void	      ConvertPixmap8	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap16	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap24	  PROTO((Layout *layout, FILE *f));
+void	      ConvertPixmap32	  PROTO((Layout *layout, FILE *f));
+void	      DrawOverlay	  PROTO((Graphic *graphic, Layout *layout, int N, FILE *f, int extra));
+
+/* JPEG image drawing functions */
+void	      bDrawOverlay	  PROTO((Layout *layout, int N));
+
+/***** Prototypes for image ***************/
+void          StatusBox           PROTO((Graphic *, Layout *));
+
+/***** Prototypes for action ***************/
+int           InPicture           PROTO((XButtonEvent *, Picture *));
+int           Stop                PROTO((Graphic *, Layout *));
+void          DragColorbar        PROTO((Graphic *, Layout *, XButtonEvent *));
+void          ResetColorbar       PROTO((Graphic *, Layout *, double, double));
+
+/***** Prototypes for button ***************/
+Button       *CheckButtons        PROTO((XButtonEvent *, Layout *));
+void          DrawButton          PROTO((Graphic *, Button *));
+void          FlashButton         PROTO((Graphic *, Button *));
+int           InButton            PROTO((XButtonEvent *, Button *));
+void          InvertButton        PROTO((Graphic *, Button *));
+int           greycolors          PROTO((Graphic *, Layout *));
+int           rainbow             PROTO((Graphic *, Layout *));
+int           puns                PROTO((Graphic *, Layout *));
+int           Recenter            PROTO((Graphic *, Layout *));
+int           RecenterRescale     PROTO((Graphic *, Layout *));
+int           Rescale             PROTO((Graphic *, Layout *));
+int           ToggleDEG           PROTO((Graphic *, Layout *));
+int           Overlay0            PROTO((Graphic *, Layout *));
+int           Overlay1            PROTO((Graphic *, Layout *));
+int           Overlay2            PROTO((Graphic *, Layout *));
+int           Overlay3            PROTO((Graphic *, Layout *));
+
+/***** Prototypes for xtools ***************/
+void	      Screen_to_Image     PROTO((double *x1, double *y1, double x2, double y2, Layout *layout));
+void	      Image_to_Screen	  PROTO((double *x1, double *y1, double x2, double y2, Layout *layout));
+unsigned long GetColor            PROTO((Display *, char *, Colormap, unsigned long));
+void          QuitX               PROTO((Display *, char *, char *));
+void          hh_hms              PROTO((char *, double, double, char));
+void          DrawBitmap          PROTO((Graphic *, int, int, int, int, char *, int));
+int	      cursor		  PROTO((Graphic *graphic, Layout *layout));
+void	      FlushDisplay	  PROTO((Display *display));
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.struct.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.struct.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/kii.struct.h	(revision 17422)
@@ -0,0 +1,129 @@
+
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;
+  int            screen;
+  int            depth;
+  Window         window;
+  Visual        *visual;
+  int            visualclass;
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  int            dx, dy;
+  Colormap       colormap;
+  unsigned long  Npixels, pixels[256];
+  int            Nbits;
+  /*
+  unsigned long  fore;
+  unsigned long  back;
+  */
+  unsigned long  black, white;
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      text;              /* does this have a picture or text? */
+  int      width, height;
+  char    *bitmap;          /* picture on button */
+  int    (*function_1) ();  /* mouse_button 1 function */
+  int    (*function_2) ();  /* mouse_button 2 function */
+  int    (*function_3) ();  /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  char  *bits;
+} Icon;
+
+typedef struct {
+  int      dx, dy, x, y;
+  XImage  *pix;
+  char *data;
+} Picture;
+
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int Nobjects;
+  unsigned long color;
+  Object *objects;
+} Overlay;
+  
+/******** Here we define the Layout struct specific to this program  *******/
+typedef struct {
+  /* objects on the mana window */
+  Picture  picture;
+  Picture  cmapbar;
+  Picture  zoom;
+  Button   PS_button;
+  Button   recenter_button;
+  Button   hms_button; /* toggle HMS/DECIMAL mode (DECIMAL_DEG macro) */
+  Button   grey_button;
+  Button   rainbow_button;
+  Button   puns_button;
+  Button   overlay_button[NOVERLAYS];
+  Overlay  overlay[NOVERLAYS];
+  Overlay  tickmarks;
+  int      text_x, text_y;
+
+  /* file descriptor for socket connection to mana */
+  int Ximage; 
+
+  /* data mana needs */
+  Matrix   matrix;         /* data for picture */
+  double   X, Y;           /* image pixel at screen center */
+  int      expand;         /* zoomscale */
+  double   zero, range;    /* zero, range for picture to cmap */
+  double   max, min;       /* zero, range for data to z-value */
+  double   start, slope;   /* zero, range for cmap to pixels */
+  double   x, y, z;        /* last pointer coords */
+  Coords   coords;
+  char     file[1024];     /* name of file */
+  char     buffer_name[1024];  /* name of buffer */
+
+  /* fundamental pieces */
+  unsigned long  black;
+  unsigned long  white;
+  XColor   cmap[256];
+  int      Npixels;
+
+} Layout;
+
+/* this routine is independent of the number of overlays */
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/prototypes.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/prototypes.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/prototypes.h	(revision 17422)
@@ -0,0 +1,252 @@
+
+/* top-level program */
+int	      args		  PROTO((int *argc, char **argv));
+void          SetUpGraphic        PROTO((int *argc, char **argv));
+void          DefineLayout        PROTO((int, char **));
+int	      EventLoop		  PROTO(());
+void	      CloseDisplay	  PROTO(());
+
+/* SetUpGraphic */
+char         *CheckDisplayName    PROTO((int *argc, char **argv));
+Display      *OpenDisplay         PROTO((char *name, int *screen));
+void          CheckColors         PROTO((Graphic *graphic, int *argc, char **argv));
+
+/* SetUpWindow */
+void          CheckGeometry       PROTO((Graphic *graphic, int *argc, char **argv));
+void          TopWindow           PROTO((Graphic *graphic, Icon *icon));
+void          CreateWindow        PROTO((Graphic *graphic, Window parent, int border, long events));
+void	      MakeGC	    	  PROTO((Graphic *graphic));
+void          MakeCursor          PROTO((Graphic *graphic, unsigned int cursor));
+void          LoadFont            PROTO((Graphic *graphic, int *argc, char **argv, char *default_name));
+void          SetNormalHints      PROTO((Graphic *graphic));
+void          SetWMHints          PROTO((Graphic *graphic, Icon *icon));
+void          NameWindow          PROTO((Graphic *graphic, char *name));
+void          MapWindow           PROTO((Graphic *graphic));
+void          CheckVisual         PROTO((Graphic *graphic, int *argc, char **argv));
+
+/* X drawing utilities */
+void	      DrawFrame		  PROTO((KapaGraphWidget *graph));
+int           DrawObjects         PROTO((KapaGraphWidget *graph));
+void          DrawLabels          PROTO((KapaGraphWidget *graph));
+void	      DrawTextlines	  PROTO((KapaGraphWidget *graph));
+void          DrawConnect         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawHistogram       PROTO((KapaGraphWidget *graph, Gobjects *objects));
+int           DrawObjectN         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawPoints          PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawXErrors         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void          DrawYErrors         PROTO((KapaGraphWidget *graph, Gobjects *objects));
+void	      DrawTick		  PROTO((int fx, int fy, int dfx, int dfy, int P, double min, double max, double value, int mode, int naxis));
+void	      AxisTickScale	  PROTO((Axis *axis, double *major, double *minor));
+
+/* EventLoop */
+int           PScommand           PROTO((int sock));
+int           CheckPipe           PROTO(());
+int           Reconfig            PROTO((XEvent *event));
+void          Refresh             PROTO(());
+
+/* CheckPipe */
+int           PNGit               PROTO((int sock));
+int           PPMit               PROTO((int sock));
+int           LoadFrame           PROTO((int sock));
+int           LoadObject          PROTO((int sock));
+int           LoadLabels          PROTO((int sock));
+int           LoadTextlines       PROTO((int sock));
+int           Resize              PROTO((int sock));
+int           GetLimits           PROTO((int sock));
+int           SetLimits           PROTO((int sock));
+int           SetSection          PROTO((int sock));
+int           ListSection         PROTO((int sock));
+int           MoveSection         PROTO((int sock));
+int           DefineSection       PROTO((int sock));
+int           SetFont             PROTO((int sock));
+int           EraseCurrentPlot    PROTO((void));
+int           ErasePlots          PROTO((void));
+int           EraseSections       PROTO((void));
+int           EraseImage          PROTO((void));
+int           SetGraphData        PROTO((int sock));
+int           GetGraphData        PROTO((int sock));
+int           SetImageData        PROTO((int sock));
+int           GetImageData        PROTO((int sock));
+int           SetImageCoords      PROTO((int sock));
+int           GetImageCoords      PROTO((int sock));
+int           GetImageRange       PROTO((int sock));
+int           SetChannel          PROTO((int sock));
+int           SetColormapFromPipe PROTO((int sock));
+
+int           LoadVectorData      PROTO((int sock, KapaGraphWidget *graph, int N, char *type));
+
+/* Section Utilities */
+Section      *InitSection	  PROTO(());
+void          FreeSection	  PROTO((Section *section));
+void          FreeSections	  PROTO(());
+Section      *AddSection	  PROTO((char *name, float x, float y, float dx, float dy));
+int           DelSection	  PROTO((char *name));
+int           GetSectionByName	  PROTO((char *name));
+int           GetNumberOfSections PROTO(());
+Section      *GetSectionByNumber  PROTO((int N));
+Section      *GetActiveSection	  PROTO(());
+int           SetActiveSectionByNumber PROTO((int N));
+int           ListSection         PROTO((int sock));
+void          SetSectionSizes     PROTO((Section *section));
+
+KapaGraphWidget *InitGraph        PROTO(());
+void          DrawGraph           PROTO((KapaGraphWidget *graph));
+void          SetGraphSize        PROTO((Section *section));
+void          FreeGraph           PROTO((KapaGraphWidget *graph));
+
+void          InitLayout          PROTO((int argc, char **argv));
+
+/* PS drawing utilities */
+int           PSit                PROTO((char *filename, char *pagename, int scaleMode, int pageMode));
+int           PSFrame             PROTO((KapaGraphWidget *graph, FILE *f));
+int           PSObjects           PROTO((KapaGraphWidget *graph, FILE *f));
+void          PSLabels            PROTO((KapaGraphWidget *graph, FILE *f));
+void	      PSTextlines	  PROTO((KapaGraphWidget *graph, FILE *f));
+void          PSConnect           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSHistogram         PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSPoints            PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSXErrors           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void          PSYErrors           PROTO((KapaGraphWidget *graph, Gobjects *objects, FILE *f));
+void	      PSTick		  PROTO((FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis));
+void          ClipLinePS          PROTO((double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0, FILE *f));
+int           PSimage    	  PROTO((KapaImageWidget *image, FILE *f));
+void 	      PSOverlay  	  PROTO((KapaImageWidget *image, int N, FILE *f, int extra));
+void 	      PSPixmap8  	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap16 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap24 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void 	      PSPixmap32 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+
+/* kapa bDraw Functions */
+int	      bDrawFrame	  PROTO((KapaGraphWidget *graph));
+int	      bDrawObjects	  PROTO((KapaGraphWidget *graph));
+void	      bDrawLabels	  PROTO((KapaGraphWidget *graph));
+void	      bDrawTextlines	  PROTO((KapaGraphWidget *graph));
+void	      bDrawConnect	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawHistogram	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawPoints	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawXErrors	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawYErrors	  PROTO((KapaGraphWidget *graph, Gobjects *object));
+void	      bDrawTick		  PROTO((double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis));
+void	      bDrawClipLine	  PROTO((double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0));
+bDrawBuffer  *bDrawIt		  PROTO(());
+void          bDrawGraph          PROTO((KapaGraphWidget *graph));
+
+/* misc support */
+int           LastEvent           PROTO((Display *display, int type, XEvent *event));
+void	      FreeObjectData	  PROTO((Gobjects *object));
+void	      FlushDisplay	  PROTO(());
+unsigned long GetColor            PROTO((Display *display, char *name, Colormap colormap, unsigned long default_color));
+void          QuitX               PROTO((Display *display, char *message));
+Graphic      *GetGraphic          PROTO(());
+int           GetPixelCount       PROTO((int sock));
+
+int           Center              PROTO(());
+void          SetColorScale       PROTO((Graphic *graphic, KapaImageWidget *image));
+void          SetColorScale1D     PROTO((Graphic *graphic, KapaImageWidget *image));
+int           SetColorScale3D     PROTO((Graphic *graphic, KapaImageWidget *image));
+void          Remap               PROTO((Graphic *graphic, KapaImageWidget *image));
+void          Remap8              PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap16             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap24             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+void          Remap32             PROTO((Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix));
+int           LoadPicture         PROTO((int sock));
+
+KapaImageWidget *InitImageWidget  PROTO(());
+int           InitImageChannel    PROTO((KapaImageChannel *channel));
+void          FreeImage           PROTO((KapaImageWidget *image));
+void          SetImageSize        PROTO((Section *section));
+
+void          InitButtonSize      PROTO((Button *button, int width, int height, char *bitmap));
+void          InitButtonFunc      PROTO((Button *button, int (*function)()));
+void          DrawImage           PROTO((KapaImageWidget *image));
+void          DrawButton          PROTO((Graphic *graphic, Button *button));
+void          DrawBitmap          PROTO((Graphic *graphic, int x, int y, int dx, int dy, char *bitmap, int mode));
+void          CrossHairs          PROTO((Graphic *graphic, Picture *image));
+void          hh_hms              PROTO((char *line, double ra, double dec, char sep));
+
+int           SetColormap         PROTO((char *name));
+void          MakeColormap        PROTO((int argc, char **argv));
+
+void          PaintOverlay        PROTO((Graphic *graphic, KapaImageWidget *image, int N));
+void          PaintTickmarks      PROTO((Graphic *graphic, KapaImageWidget *image));
+
+void          Picture_to_Image    PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Screen_to_Image     PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Image_to_Picture    PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Image_to_Screen     PROTO((double *x1, double *y1, double x2, double y2, Picture *picture));
+void          Picture_Lower 	  PROTO((int *i_start, int *j_start, Matrix *matrix, Picture *picture));
+void          Picture_Upper 	  PROTO((int *i_end, int *j_end, Matrix *matrix, Picture *picture));
+
+void          DragColorbar        PROTO((Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event));
+void          ResetColorbar       PROTO((Graphic *graphic, double start, double slope));
+void          ReorientOnButton    PROTO((Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event));
+void          Reorient            PROTO((Graphic *graphic, KapaImageWidget *image, double X, double Y, int mode));
+
+Button       *CheckButtons        PROTO((XButtonEvent *event, KapaImageWidget *image));
+int           InButton            PROTO((XButtonEvent *event, Button *button));
+int           InPicture           PROTO((XButtonEvent *event, Picture *picture));
+
+/* Button Functions */
+int           greycolors	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      heat		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      rainbow		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Recenter		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Rescale		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      RecenterRescale	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      ToggleDEG		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      FlipImageX	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      FlipImageY	  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay0		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay1		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay2		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      Overlay3		  PROTO((Graphic *graphic, KapaImageWidget *image));
+int	      PSfunction	  PROTO((Graphic *graphic, KapaImageWidget *image));
+
+/* misc functions */
+void 	      SetToolbox   (int sock);
+int  	      EraseOverlay (int sock);
+int  	      LoadOverlay  (int sock);
+int  	      SaveOverlay  (int sock);
+int  	      CSaveOverlay (int sock);
+int  	      JPEGit24     (int sock);
+
+int  	      UpdatePointer (Graphic *graphic, XMotionEvent *event);
+int  	      InterpretPresses (Graphic *graphic, XButtonEvent *event);
+int  	      InterpretKeys (Graphic *graphic, XKeyEvent *event);
+void 	      InitPipe (char *namedSocket);
+void 	      EraseGraph (KapaGraphWidget *graph);
+void 	      StatusBox (Graphic *graphic, KapaImageWidget *image);
+
+void 	      CreatePicture (KapaImageWidget *image, Graphic *graphic);
+void 	      CreateColorbar (KapaImageWidget *image, Graphic *graphic);
+void 	      CreateZoom (Graphic *graphic, KapaImageWidget *image);
+void 	      CreateWide (Graphic *graphic, KapaImageWidget *image);
+void 	      UpdateStatusBox (Graphic *graphic, KapaImageWidget *image, double x, double y, double z, int mode);
+
+// void 	      CreateZoom8  (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom16 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom24 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+// void 	      CreateZoom32 (KapaImageWidget *image, Graphic *graphic, double x, double y);
+
+int  	      GetActiveSocket ();
+void 	      InvertButton (Graphic *graphic, Button *button);
+void 	      bDrawOverlay (KapaImageWidget *image, int N);
+
+/* color cube tools */
+CCNode *CCNodeAlloc ();
+CCNode *CCFindChild (CCNode *node, float x, float y, float z);
+int CCSplitNode (CCNode *node);
+int CCSplitNodeIterate (CCNode *node, int current, int max);
+void CCNodeFree (CCNode *node);
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z);
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES);
+int CCNodeDivideLimit (CCNode *node, float minValue);
+int CCNodeInitCounts (CCNode *node, float value);
+
+/* 3D color histogram */
+void ColorHistogram (KapaImageWidget *image, CCNode *cube);
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube);
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current);
+
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image);
+int SetColorCubeHistogram ();
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/include/structures.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/include/structures.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/include/structures.h	(revision 17422)
@@ -0,0 +1,211 @@
+
+/*** 3C color cube histogram tree thingy ***/
+typedef struct CCNode {
+  // for the moment, this structure is specific to the color-histogram analysis.  if
+  // this were a void *pointer and we defined some additional apis, we could use this
+  // structure to track any 3D space
+  int count;    
+  int pixel;
+  char bottom;
+  float min[3]; // min value for each of the 3 dimensions for this node
+  float mid[3]; // mid-point for each of the 3 dimensions for this node
+  float max[3]; // max value for each of the 3 dimensions for this node
+  struct CCNode *sub[2][2][2];
+} CCNode;
+
+/**************** Graphic carries X info around ****************/
+typedef struct {
+  Display       *display;     // X display pointer
+  int            screen;      // X screen number
+  int            depth;
+  Window         window;
+  Visual        *visual;
+  int            visualclass; // is visual dynamic? (XXX change name?)
+  int            Nbits;	      // pixel depth in bits (8, 16, 24, 32)
+  GC             gc;
+  XFontStruct   *font;
+  Cursor         cursor;
+  int            x,  y;
+  unsigned int   dx, dy;
+  CCNode        *cube;
+  XColor        *cmap;
+  Colormap       colormap;
+  unsigned long *color;      // graph plotting colors
+  int            Ncolors;
+
+  unsigned long *pixels;      // image pixel colors
+  int Npixels;		      // number of pixels
+
+  int            ColorScaleMode; // single colormap for all images??
+  int nRed;
+  int nBlue;
+  int nGreen;
+
+  unsigned long  fore;	      // basic foreground color 
+  unsigned long  back;	      // basic background color
+
+  unsigned long  overlay_color[NOVERLAYS]; // image plotting colors 
+} Graphic;
+
+/**************** X related "widget" structures ****************/
+typedef struct {
+  int      x, y, dx, dy;      /* position and size */
+  int      width, height;     /* size of the bitmap */
+  char    *bitmap;            /* picture on button */
+  int    (*function_1) ();    /* mouse_button 1 function */
+  int    (*function_2) ();    /* mouse_button 2 function */
+  int    (*function_3) ();    /* mouse_button 3 function */
+} Button;
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  char    *label;          /* label on TextLine */
+  char     text[1024];     /* words of TextLine */
+  char     old_text[1024]; /* words of TextLine */
+  int      outline;        /* draw an outline?  */ 
+  int      cursor;         /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextLine;
+
+typedef char STRING[1024];
+
+typedef struct {
+  int      x, y, dx, dy;   /* position and size */
+  STRING  *text;           /* words of TextLine */
+  int      Nlines;
+  int      outline;        /* draw an outline?  */ 
+  int      cursor_line;    /* cursor line */
+  int      cursor_x;       /* location of cursor (if selected) */
+  int      cursor_y;       /* location of cursor (if selected) */
+  int    (*function) ();   /* textline function */
+} TextBox;
+
+/**************** general structures ****************/
+typedef struct {
+  Pixmap pixmap;
+  int    width;
+  int    height;
+  unsigned char *bits;
+} Icon;
+
+typedef struct {
+  int      x, y;	      // location of picture in graphic
+  int      dx, dy;	      // size of picture
+  int      DX, DY;	      // size of displayed picture (must be updated with new images...)
+  int      expand;	      // zoomscale
+  double   X,  Y;	      // center of image in picture
+  char     flipx, flipy;      // parity (0 = +; 1 = -)
+  XImage  *pix;
+  char    *data;
+} Picture;
+
+// objects associated with a KiiImage
+// XXX rename thi
+typedef struct {
+  char type[10];
+  double x, y;
+  double dx, dy;
+  double angle;
+  char  *text;
+} Object;
+
+typedef struct {
+  int x, y;
+  int dx, dy;
+  double angle;
+  int  size;
+  char font[64]; 
+  char text[LABEL_MAXLEN];
+} Label;
+
+typedef struct {
+  int active;
+  int Nobjects;
+  unsigned long color;
+  KiiOverlay *objects;
+} Overlay;
+  
+typedef struct {
+  float *x, *y, *z, *dxp, *dxm, *dyp, *dym;
+  int Npts;
+  int style, ptype, ltype, color, etype, ebar;
+  double lweight, size;
+  double x0, x1, y0, y1;  /* limits for this object */
+} Gobjects;
+
+typedef struct {
+  double min, max;
+  char isaxis, areticks, islabel, islog;
+  double fx, dfx, fy, dfy;  /* axis location on graphic */
+} Axis;
+
+// a single graph in the display window
+typedef struct {
+  Axis      axis[4];    /* coordinate axes */
+  Label     label[8];   /* fixed axis labels */
+  Graphdata data;       /* current graph data */
+
+  Gobjects *objects;    /* graphic objects */    
+  int      Nobjects;    
+
+  Label    *textline;      /* placed text labels */
+  int      Ntextline;      
+} KapaGraphWidget;
+
+typedef struct {
+  // data associated with this image element
+  Matrix   matrix;         /* data for picture */
+  double   zero, range;    /* zero, range for picture to cmap */
+  double   max, min;       /* zero, range for data to z-value */
+  double   start, slope;   /* zero, range for cmap to pixels */
+  Coords   coords;
+  char     file[1024];     /* name of file */
+  char     name[1024];     /* name of buffer */
+} KapaImageChannel;
+
+// a single image in the display window
+typedef struct {
+  // picture components of the image element
+  Picture  picture;	      // the primary view of the image
+  Picture  zoom;	      // the zoom window
+  Picture  wide;	      // the wide-view window
+  Picture  cmapbar;	      // the colormap bar
+
+  // the control buttons
+  Button   PS_button;
+  Button   recenter_button;
+  Button   hms_button; /* toggle HMS/DECIMAL mode (DECIMAL_DEG macro) */
+  Button   grey_button;
+  Button   rainbow_button;
+  Button   heat_button;
+  Button   overlay_button[NOVERLAYS];
+  Button   flipx_button;
+  Button   flipy_button;
+
+  // location of the status box
+  int      text_x, text_y;
+  int      text_dx, text_dy, text_dyo;
+  int      location;	      // position of the zoom/status widgets (0 = none, 1-4 = bottom,left,top,right)
+  int      MovePointer;
+  int      DecimalDegrees;
+
+  // double   X, Y;           /* image pixel at screen center */
+  // double   x, y, z;        /* last pointer coords */
+
+  Overlay  overlay[NOVERLAYS];
+  Overlay  tickmarks;
+
+  unsigned short   *pixmap;   // lookup table for image pixel value to pixel index
+  int      nPixels;
+ 
+  KapaImageChannel *image;
+  KapaImageChannel channel[NCHANNELS];
+} KapaImageWidget;
+
+typedef struct {
+  KapaGraphWidget *graph;
+  KapaImageWidget *image;
+  float  x,  y;
+  float dx, dy;
+  char *name;
+} Section;
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/ButtonFunctions.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/ButtonFunctions.c	(revision 17422)
@@ -0,0 +1,144 @@
+# include "Ximage.h"
+
+int PSfunction (Graphic *graphic, KapaImageWidget *image) {
+
+  int status;
+
+  status = PSit ("kapa.ps", "default", TRUE, KAPA_PS_NEWPLOT);
+  return (status);
+}
+
+int greycolors (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("greyscale");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int heat (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("heat");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int rainbow (Graphic *graphic, KapaImageWidget *image) {
+  SetColormap ("rainbow");
+  CreateColorbar (image, graphic);
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateZoom (graphic, image); 
+  CreateWide (graphic, image); 
+  Refresh ();
+  return (TRUE);
+}
+
+int Recenter (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+ 
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Rescale (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.expand = 1;
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int RecenterRescale (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.X = 0.5*image[0].image[0].matrix.Naxis[0];
+  image[0].picture.Y = 0.5*image[0].image[0].matrix.Naxis[1];
+  image[0].picture.expand = 1;
+ 
+  Remap (graphic, image);
+  Refresh ();
+  return (TRUE);
+
+}
+
+int FlipImageX (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.flipx = !image[0].picture.flipx;
+  image[0].zoom.flipx 	 = !image[0].zoom.flipx;
+  image[0].wide.flipx 	 = !image[0].wide.flipx;
+ 
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  Refresh ();
+  return (TRUE);
+}
+
+int FlipImageY (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].picture.flipy = !image[0].picture.flipy;
+  image[0].zoom.flipy 	 = !image[0].zoom.flipy;
+  image[0].wide.flipy 	 = !image[0].wide.flipy;
+ 
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  Refresh ();
+  return (TRUE);
+}
+
+int ToggleDEG (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].DecimalDegrees = image[0].DecimalDegrees ^ TRUE;
+  StatusBox (graphic, image);
+  FlushDisplay (graphic[0].display);
+  return (TRUE);
+
+}
+
+/*********** overlay_button functions ************/
+int Overlay0 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[0].active = image[0].overlay[0].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay1 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[1].active = image[0].overlay[1].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay2 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[2].active = image[0].overlay[2].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+int Overlay3 (Graphic *graphic, KapaImageWidget *image) {
+
+  image[0].overlay[3].active = image[0].overlay[3].active ^ TRUE;
+  Refresh ();
+  return (TRUE);
+
+}
+
+/* this routine is NOT independent of the number of overlays */
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CSaveOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CSaveOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CSaveOverlay.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+
+int CSaveOverlay (int sock) {
+
+  char filename[256], *type;
+  int i, N;
+  double ra, dec, ra1, dec1, x1, y1, dra, ddec;
+  FILE *f;
+  Section *section;
+  KapaImageWidget *image;
+
+  KiiScanMessage (sock, "%*s %d %s", &N, filename);
+  
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    if (image[0].overlay[N].objects[i].type == KII_OVERLAY_LINE) {
+      XY_to_RD (&ra, &dec, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].image[0].coords);
+      x1 = image[0].overlay[N].objects[i].x + image[0].overlay[N].objects[i].dx;
+      y1 = image[0].overlay[N].objects[i].y + image[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      dra = (ra1 - ra);
+      ddec = (dec1 - dec);
+    } else {
+      XY_to_RD (&ra, &dec, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].image[0].coords);
+      x1 = image[0].overlay[N].objects[i].x;
+      y1 = image[0].overlay[N].objects[i].y + image[0].overlay[N].objects[i].dy;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      ddec = fabs (dec1 - dec);
+      x1 = image[0].overlay[N].objects[i].x + image[0].overlay[N].objects[i].dx;
+      y1 = image[0].overlay[N].objects[i].y;
+      XY_to_RD (&ra1, &dec1, x1, y1, &image[0].image[0].coords);
+      dra = cos (dec*RAD_DEG) * fabs (ra1 - ra);
+    }
+    type = KiiOverlayTypeByNumber (image[0].overlay[N].objects[i].type);
+    if (type == NULL) continue;
+    fprintf (f, "%s %lf %lf %lf %lf\n", type, ra, dec, dra, ddec);
+   }
+  fclose (f);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Center.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Center.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Center.c	(revision 17422)
@@ -0,0 +1,32 @@
+# include "Ximage.h"
+
+int Center (int sock) {
+
+  int zoom;
+  double X, Y;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  KiiScanMessage (sock, "%lf %lf %d", &X,  &Y, &zoom);
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  image[0].picture.X = X;
+  image[0].picture.Y = Y;
+  if ((zoom != 0) && (zoom != -1)) {
+    image[0].picture.expand = zoom;
+  }
+
+  if (USE_XWINDOW) {
+    Remap (graphic, image);
+    Refresh ();
+    XFlush (graphic[0].display);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckButtons.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckButtons.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckButtons.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+/******** Here we test the Buttons specific to this program  *******/
+Button *CheckButtons (XButtonEvent  *event, KapaImageWidget *image) {
+
+  int i;
+  Button *button;
+  button = (Button *) NULL;
+
+  if (InButton (event, &image[0].recenter_button)) 
+    button = &image[0].recenter_button;
+
+  if (InButton (event, &image[0].grey_button)) 
+    button = &image[0].grey_button;
+
+  if (InButton (event, &image[0].rainbow_button)) 
+    button = &image[0].rainbow_button;
+
+  if (InButton (event, &image[0].heat_button)) 
+    button = &image[0].heat_button;
+
+  if (InButton (event, &image[0].PS_button)) 
+    button = &image[0].PS_button;
+
+  if (InButton (event, &image[0].hms_button)) 
+    button = &image[0].hms_button;
+
+  if (InButton (event, &image[0].flipx_button)) 
+    button = &image[0].flipx_button;
+
+  if (InButton (event, &image[0].flipy_button)) 
+    button = &image[0].flipy_button;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (InButton (event, &image[0].overlay_button[i])) 
+      button = &image[0].overlay_button[i];
+  }
+
+  return (button);
+
+}
+
+/* To define a button, you must:
+
+   0) add the button to the Image structure in structures.h
+   1) place the info about the button in PositionPicture.c
+   2) (make any bitmaps needed and put them in buttons.h
+   3) place an entry in CheckButtons.c
+   4) Add the button to Refresh.c
+   5) create the button's function
+   6) add it to the Makefile
+   7) add it to the prototypes
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckColors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckColors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckColors.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "Ximage.h"
+
+/************** CheckColors *************/
+void CheckColors (Graphic *graphic, int *argc, char **argv) {
+
+  char *temp_name;
+  int N;
+
+  graphic->fore = BlackPixel (graphic->display, graphic->screen);
+  temp_name = XGetDefault (graphic->display, argv[0], "Foreground");
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -fg color\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  } 
+  if (temp_name != NULL) {
+    graphic->fore = GetColor (graphic->display, temp_name, graphic->colormap, graphic->fore);
+  }
+
+  graphic->back = WhitePixel (graphic->display, graphic->screen);
+  temp_name = XGetDefault (graphic->display, argv[0], "Background");
+  if ((N = get_argument (*argc, argv, "-bg"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -bg color\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  } 
+  if (temp_name != NULL) {
+    graphic->back = GetColor (graphic->display, temp_name, graphic->colormap, graphic->back);
+  }
+  return;
+}
+
+  /* here we define the values for foreground and background
+     if -fg, or -bg exist, or if Foreground or Background are set in .Xdefaults, 
+     use those.  foreground defaults to black, background defaults to white. */
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckDisplayName.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckDisplayName.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckDisplayName.c	(revision 17422)
@@ -0,0 +1,31 @@
+# include "Ximage.h"
+
+/************** CheckDisplayName *************/
+char *CheckDisplayName (int *argc, char **argv) {
+
+  char *name = NULL;
+  int N;
+
+  if ((N = get_argument (*argc, argv, "-d"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+    return (name);
+  }
+
+  if ((N = get_argument (*argc, argv, "-display"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is [-display/-d] DisplayName\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+    return (name);
+  }
+  return (NULL);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckGeometry.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckGeometry.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckGeometry.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "Ximage.h"
+
+/************** CheckGeometry *************/
+void CheckGeometry (Graphic *graphic, int *argc, char **argv) {
+
+  int status, N;
+  char *temp_name;
+  
+  temp_name = XGetDefault (graphic->display, argv[0], "geometry");
+  if ((N = get_argument (*argc, argv, "-geom"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -geom DisplayName\n");
+      exit (0);
+    }
+    remove_argument (N, argc, argv);
+    temp_name = argv[N];
+    remove_argument (N, argc, argv);
+  }
+  if (temp_name == NULL) return;
+
+  status = XParseGeometry (temp_name, &graphic->x, &graphic->y, &graphic->dx, &graphic->dy);
+  if (status & XNegative) graphic->x += DisplayWidth  (graphic->display, graphic->screen) - graphic->dx;
+  if (status & YNegative) graphic->y += DisplayHeight (graphic->display, graphic->screen) - graphic->dy;
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckPipe.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckPipe.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckPipe.c	(revision 17422)
@@ -0,0 +1,315 @@
+# include "Ximage.h"
+# define STRCONST(A) ((int)(0x1000000*A[0] + 0x10000*A[1] + 0x100*A[2] + 0x1*A[3]))
+
+static KapaSockAddress Address;
+static int InitSocket = -1;
+static int sock = -1;
+
+// we can supply a port here, with only small changes
+void InitPipe (char *namedSocket) {
+
+  if (namedSocket == NULL) {
+    InitSocket = KapaServerInit (&Address);
+  } else {
+    sock = KapaWaitNamedSocket (namedSocket);
+  }
+  return;
+}
+
+int GetActiveSocket () {
+  return (sock);
+}
+
+// after we have processed the command, we unblock the socket
+# define FINISHED(A) { fcntl (sock, F_SETFL, O_NONBLOCK); return (A); }
+
+int CheckPipe () {
+
+  int status;
+  char word[5];
+
+  // check if we have a valid connection. if not, see if we can get one
+  if (sock == -1) {
+    sock = KapaServerWait (InitSocket, &Address);
+    if (sock == -1) return (TRUE);
+    close (InitSocket); /* stop listening for new connections */
+    fcntl (sock, F_SETFL, O_NONBLOCK);  
+  }
+
+  /***** read (4 byte) message word from socket ****/
+  status = read (sock, word, 4);
+  word[4] = 0;
+  switch (status) {
+  case -1:                          /* no input from pipe: continue */
+    return (TRUE);
+    break;
+
+  case 0:
+    fprintf (stderr, "pipe has died!\n");
+    return (FALSE);
+    break;
+
+  case 4:
+    break;
+
+  default:
+    fprintf (stderr, "weird signal: too many or few bytes!  %d\n", status);
+    return (TRUE);
+    break;
+  }
+  
+  /* once we get a command, we block to ensure we get complete messages */
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+
+  /***** handle different messages ****/
+  if (ACTIVE_CURSOR) {
+    if (strcmp (word, "NCUR")) {
+      fprintf (stderr, "wrong end message %s\n", word);
+      KiiSendCommand (sock, 4, "DONE");
+      FINISHED (TRUE);
+    }
+    ACTIVE_CURSOR = FALSE;
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  } 
+
+  if (!strcmp (word, "QUIT")) FINISHED (FALSE);
+  
+  if (!strcmp (word, "CURS")) {
+    ACTIVE_CURSOR = TRUE;
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "PSIT")) {
+    status = PScommand (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PNGF")) {
+    status = PNGit (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PPMF")) {
+    status = PPMit (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "DBOX")) {
+    status = LoadFrame (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "PLOT")) {
+    status = LoadObject (sock);
+    // LoadObject sends its own handshake
+    // KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  if (!strcmp (word, "LABL")) {
+    status = LoadLabels (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "PTXT")) {
+    status = LoadTextlines (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "RSIZ")) {
+    status = Resize (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "GLIM")) {
+    GetLimits (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SLIM")) {
+    status = SetLimits (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GSTY")) {
+    GetGraphData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SSTY")) {
+    status = SetGraphData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SIMC")) {
+    SetImageCoords (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMC")) {
+    GetImageCoords (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMR")) {
+    GetImageRange (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "GIMD")) {
+    GetImageData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SIMD")) {
+    status = SetImageData (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "TOOL")) {
+    SetToolbox (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "SSEC")) {
+    status = SetSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "LSEC")) {
+    status = ListSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "DSEC")) {
+    status = DefineSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "MSEC")) {
+    status = MoveSection (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  if (!strcmp (word, "FONT")) {
+    status = SetFont (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+  
+  /* Erase Section */
+  if (!strcmp (word, "ERSC")) {
+    status = EraseCurrentPlot ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  /* Erase Plots */
+  if (!strcmp (word, "ERSP")) {
+    status = ErasePlots ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+  
+  /* Erase Sections */
+  if (!strcmp (word, "ERSS")) {
+    status = EraseSections ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  /* Erase Image */
+  if (!strcmp (word, "ERSI")) {
+    status = EraseImage ();
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  /* Erase Overlay for this section */
+  if (!strcmp (word, "ERSO")) {
+    status = EraseOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "READ")) {
+    status = LoadPicture (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "LOAD")) {
+    status = LoadOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CHAN")) {
+    status = SetChannel (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CMAP")) {
+    status = SetColormapFromPipe (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "SAVE")) {
+    status = SaveOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CSVE")) {
+    status = CSaveOverlay (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "JPEG")) {
+    status = JPEGit24 (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "CENT")) {
+    status = Center (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (status);
+  }
+
+  if (!strcmp (word, "NPIX")) {
+    GetPixelCount (sock);
+    KiiSendCommand (sock, 4, "DONE");
+    FINISHED (TRUE);
+  }
+
+  fprintf (stderr, "unknown signal %s\n", word);
+  KiiSendCommand (sock, 4, "DONE");
+  FINISHED (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckVisual.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckVisual.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CheckVisual.c	(revision 17422)
@@ -0,0 +1,196 @@
+# include "Ximage.h"
+
+/* DirectColor doesn't seem to work, even though it is available:
+   I cannot use XAllocColorCells to get pixels under DirectColor
+*/
+
+/* static int try_visual[] = {5, 3, 1, 4, 2, 0}; */
+
+void CheckVisual (Graphic *graphic, int *argc, char **argv) {
+
+  int i, Nfound, N;
+  int isColor, isDefault, isDynamic;
+  XVisualInfo *visual_list, visual_temp;
+  unsigned long planes[3];
+  XPixmapFormatValues *pixmaps;
+  int Npixmaps, Npixels;
+
+  if (DEBUG) {
+    fprintf (stderr, "DirectColor: %d\n", DirectColor);
+    fprintf (stderr, "PseudoColor: %d\n", PseudoColor);
+    fprintf (stderr, "TrueColor:   %d\n", TrueColor);
+    fprintf (stderr, "GrayScale:   %d\n", GrayScale);
+    fprintf (stderr, "StaticColor: %d\n", StaticColor);
+    fprintf (stderr, "StaticGray:  %d\n", StaticGray);
+  }
+
+  visual_temp.screen = graphic[0].screen;
+  
+  /* find available visuals */
+  visual_list = XGetVisualInfo (graphic[0].display, VisualScreenMask, &visual_temp, &Nfound);
+  if (Nfound == 0) {
+    fprintf (stderr, "error finding useful visual\n");
+    exit (0);
+  }
+  
+  /* check default visual first */ 
+  for (i = 0; (i < Nfound) && (graphic[0].visual != visual_list[i].visual); i++);
+  if (i == Nfound) {
+    fprintf (stderr, "default visual not found??\n");
+    exit (0);
+  }
+
+  // set these based on selected visual
+  isColor = isDefault = isDynamic = FALSE;
+
+  // attempt to select the most desirable type of visual: Default as PseudoColor (XXX is it still true?)
+  if (DEBUG) fprintf (stderr, "default visual class is %d\n", visual_list[i].class);
+  if (visual_list[i].class == PseudoColor) {
+    isColor = isDefault = isDynamic = TRUE;
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: PseudoColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != PseudoColor); i++);
+  if (i != Nfound) {
+    isColor = isDynamic = TRUE;
+    if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: GrayScale (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != GrayScale); i++);
+  if (i != Nfound) {
+    isDynamic = TRUE;
+    if (DEBUG) fprintf (stderr, "selected visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = TRUE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != TrueColor); i++);
+  if (i != Nfound) {
+    isColor = TRUE;
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticColor); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  // attempt to select the most desirable type of visual: TrueColor (XXX is it still true?)
+  for (i = 0; (i < Nfound) && (visual_list[i].class != StaticGray); i++);
+  if (i != Nfound) {
+    if (DEBUG) fprintf (stderr, "visual class is %d\n", visual_list[i].class);
+    isDefault = (graphic[0].visual == visual_list[i].visual);
+    graphic[0].visual = visual_list[i].visual;
+    graphic[0].visualclass = FALSE;
+    goto test_pixels;
+  }
+
+  test_pixels:
+
+  /* need to make a colormap if 
+     1) the selected visual is not the default 
+     2) there are not enough colors available */
+
+  /* NEED TO ADD A COUPLE LINES DEFINING THE VARIOUS HARD COLORS (BLACK, WHITE, AND THE OVERLAYS) */
+
+  // allow user to force a private colormap
+  if ((N = get_argument (*argc, argv, "-private"))) {
+    remove_argument(N, argc, argv);
+    isDefault = FALSE;
+  } 
+
+  // allocate a private colormap, if needed
+  if (!isDefault) {
+    if (DEBUG) fprintf (stderr, "allocated private colormap\n");
+    graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+  }
+
+  if (isDynamic) {
+    Npixels = NPIXELS_DYNAMIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    /* allocate color cells */
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    for (graphic[0].Npixels = Npixels; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+      if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+      if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+	break;
+      }
+    }
+
+    /* insufficient cells, can we make a private colormap? */
+    if (graphic[0].Npixels < 16) {
+      if (!isDefault) {
+	fprintf (stderr, "can't allocate enough cells in private colormap\n");
+	exit (0);
+      }
+      if (DEBUG) fprintf (stderr, "can't allocate enough cells, using private colormap\n");
+      graphic[0].colormap = XCreateColormap (graphic[0].display, RootWindow (graphic[0].display, graphic[0].screen), graphic[0].visual, AllocNone);
+
+      for (graphic[0].Npixels = NPIXELS_DYNAMIC; graphic[0].Npixels >= 16; graphic[0].Npixels -= 4) {
+	if (DEBUG) fprintf (stderr, "trying %d colors\n", (int) graphic[0].Npixels);
+	if (XAllocColorCells (graphic[0].display, graphic[0].colormap, FALSE, planes, 1, graphic[0].pixels, graphic[0].Npixels)) {
+	  break;
+	}
+      }
+
+      if ((N = get_argument (*argc, argv, "-colorcount"))) {
+	fprintf (stderr, "kapa can grab %d colors\n", graphic[0].Npixels);
+	exit (0);
+      } 
+      if (graphic[0].Npixels < 16) {
+	fprintf (stderr, "can't even allocate enough cells in private colormap\n");
+	exit (0);
+      }
+    }
+  } else {
+    Npixels = NPIXELS_STATIC;
+    if ((N = get_argument (*argc, argv, "-ncolors"))) {
+      remove_argument(N, argc, argv);
+      Npixels = atoi (argv[N]);
+      remove_argument(N, argc, argv);
+    } 
+
+    // XXX allocate the unsigned long *pixels here and above
+    ALLOCATE (graphic[0].pixels, unsigned long, Npixels);
+    ALLOCATE (graphic[0].cmap,   XColor,        Npixels);
+    graphic[0].Npixels = Npixels;
+  }
+
+  if ((N = get_argument (*argc, argv, "-colorcount"))) {
+    fprintf (stderr, "kii can grab %d colors\n", graphic[0].Npixels);
+    exit (0);
+  } 
+  
+  pixmaps = XListPixmapFormats (graphic[0].display, &Npixmaps);
+  for (i = 0; i < Npixmaps; i++) {
+    if (pixmaps[i].depth == graphic[0].depth) {
+      graphic[0].Nbits = pixmaps[i].bits_per_pixel;
+    }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CloseDisplay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CloseDisplay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CloseDisplay.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "Ximage.h"
+
+/************** CloseDisplay *************/
+void CloseDisplay () {
+
+  Graphic *graphic;
+
+  if (!USE_XWINDOW) return;
+
+  graphic = GetGraphic();
+  XFreeFont (graphic->display, graphic->font); 
+  XFreeGC (graphic->display, graphic->gc);
+  XDestroySubwindows (graphic->display, graphic->window);
+  XDestroyWindow (graphic->display, graphic->window);
+  XFlush (graphic->display);
+  XCloseDisplay (graphic->display);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorCube.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorCube.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorCube.c	(revision 17422)
@@ -0,0 +1,225 @@
+# include "Ximage.h"
+
+/* these functions are used to generate an appropriately-sampled color map for a given image.
+   we assume that only a limited number of colors can be allocated (start with, eg, 1024, grow to
+   64k or so).  the RGB image is constructed from the 32bit images, each with their own zero and
+   scale.  We want to allocate set of color cells which sample the actual colors available in the
+   image data.  We generate a 3D histogram of the actual data values.  The histogram is generated
+   using a k-d tree. */
+
+// the k-d tree is built of a bunch of nodes, each of which represents a cubic region in the 3D
+// color space.  each node may be occupied, or may point to a set of 8 children, subdividing the
+// cube into equal cubes.
+
+// NOTE : nothing in this file specifies a relationship between x,y,z and red,blue,green
+
+// set of operations needed:
+
+// allocate empty, bottom-level node
+CCNode *CCNodeAlloc () {
+
+  CCNode *node;
+  int ix, iy, iz;
+
+  ALLOCATE (node, CCNode, 1);
+  node->count = 0;
+  node->pixel = 0;
+  node->bottom = TRUE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	node->sub[ix][iy][iz] = NULL;
+      }
+    }
+  }
+  return node;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindChild (CCNode *node, float x, float y, float z) {
+
+  int ix, iy, iz;
+
+  assert (x >= node->min[CC_X]);
+  assert (y >= node->min[CC_Y]);
+  assert (z >= node->min[CC_Z]);
+
+  assert (x <= node->max[CC_X]);
+  assert (y <= node->max[CC_Y]);
+  assert (z <= node->max[CC_Z]);
+
+  if (node->bottom) return NULL; // already at the bottom
+
+  ix = (x < node->mid[CC_X]) ? 0 : 1;
+  iy = (y < node->mid[CC_Y]) ? 0 : 1;
+  iz = (z < node->mid[CC_Z]) ? 0 : 1;
+  
+  return node->sub[ix][iy][iz];
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+int CCSplitNode (CCNode *node) {
+
+  CCNode *child;
+  int ix, iy, iz;
+
+  // trying to split an already split node
+  if (!node->bottom) return FALSE;
+
+  node->bottom = FALSE;
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	child = CCNodeAlloc();
+	child->min[CC_X] = (ix == 0) ? node->min[CC_X] : node->mid[CC_X];
+	child->min[CC_Y] = (iy == 0) ? node->min[CC_Y] : node->mid[CC_Y];
+	child->min[CC_Z] = (iz == 0) ? node->min[CC_Z] : node->mid[CC_Z];
+	child->max[CC_X] = (ix == 0) ? node->mid[CC_X] : node->max[CC_X];
+	child->max[CC_Y] = (iy == 0) ? node->mid[CC_Y] : node->max[CC_Y];
+	child->max[CC_Z] = (iz == 0) ? node->mid[CC_Z] : node->max[CC_Z];
+
+	child->mid[CC_X] = 0.5*(child->min[CC_X] + child->max[CC_X]);
+	child->mid[CC_Y] = 0.5*(child->min[CC_Y] + child->max[CC_Y]);
+	child->mid[CC_Z] = 0.5*(child->min[CC_Z] + child->max[CC_Z]);
+
+	node->sub[ix][iy][iz] = child;
+      }
+    }
+  }
+  return TRUE;
+}
+
+int CCSplitNodeIterate (CCNode *node, int current, int max) {
+
+  int ix, iy, iz;
+
+  if (current == max) return TRUE;
+  current ++;
+
+  CCSplitNode (node);
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCSplitNodeIterate (node->sub[ix][iy][iz], current, max);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// allocate empty, bottom-level node
+void CCNodeFree (CCNode *node) {
+
+  int ix, iy, iz;
+
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	if (node->sub[ix][iy][iz]) CCNodeFree (node->sub[ix][iy][iz]);
+      }
+    }
+  }
+  free (node);
+  return;
+}
+
+// given a coordinate and a containing node, find the corresponding child node (if any)
+CCNode *CCFindBottom (CCNode *top, float x, float y, float z) {
+
+  CCNode *node;
+  CCNode *next;
+
+  assert (x >= top->min[CC_X]);
+  assert (y >= top->min[CC_Y]);
+  assert (z >= top->min[CC_Z]);
+
+  assert (x <= top->max[CC_X]);
+  assert (y <= top->max[CC_Y]);
+  assert (z <= top->max[CC_Z]);
+
+  node = top;
+  next = CCFindChild (node, x, y, z);
+  while (next != NULL) {
+    node = next;
+    next = CCFindChild (node, x, y, z);
+  }
+
+  return node;
+}
+
+// iterate over the CCNode tree to generate a single, 1d vector of all data values
+// XXX this function is specific to the color histogram concept
+int CCNodeExtractCounts (CCNode *node, float **values, int *nvalues, int *NVALUES) {
+
+  int ix, iy, iz;
+
+  if (*values == NULL) {
+    nvalues[0] = 0;
+    NVALUES[0] = 32;
+    ALLOCATE (*values, float, NVALUES[0]);
+  }
+
+  if (node->bottom) {
+    values[0][nvalues[0]] = node->count;
+    nvalues[0] ++;
+    if (nvalues[0] >= NVALUES[0]) {
+      NVALUES[0] += 32;
+      REALLOCATE (*values, float, NVALUES[0]);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeExtractCounts (node->sub[ix][iy][iz], values, nvalues, NVALUES);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeDivideLimit (CCNode *node, float minValue) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    if (node->count > minValue) {
+      CCSplitNode (node);
+    }
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeDivideLimit (node->sub[ix][iy][iz], minValue);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+int CCNodeInitCounts (CCNode *node, float value) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    node->count = value;
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeInitCounts (node->sub[ix][iy][iz], value);
+      }
+    }
+  }
+  return TRUE;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorHistogram.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorHistogram.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/ColorHistogram.c	(revision 17422)
@@ -0,0 +1,188 @@
+# include "Ximage.h"
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void ColorHistogram (KapaImageWidget *image, CCNode *cube) {
+
+  int i, DX, DY, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+    if (isnan(rValue)) rValue = 0.0;
+    if (isinf(rValue)) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+    if (isnan(bValue)) bValue = 0.0;
+    if (isinf(bValue)) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+    if (isnan(gValue)) gValue = 0.0;
+    if (isinf(gValue)) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    node->count ++;
+  }
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+void CCNodeSetColorPixels (KapaImageWidget *image, CCNode *cube) {
+
+  int i, DX, DY, nPixels;
+  float *rData, *bData, *gData, rValue, gValue, bValue;
+  unsigned short *oData;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+  CCNode *node;
+
+  // Input images are scaled to 0.0 - 1.0 floating point range.  These values are accumulated in the
+  // 3d histogram.  Use the histogram to allocate colors or subdivide the top-populated histogram
+  // cells and re-evaluated.   
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = 1.0 / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = 1.0 / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = 1.0 / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  DX = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1];
+  // XXX check on equal size for all three channels
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  // loop over pixels, convert data values to range values (0.0 - 1.0), increment 3D histogram cell
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  oData = image[0].pixmap;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++, oData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0.0) rValue = 0.0;
+    if (rValue > 1.0) rValue = 1.0;
+    if (isnan(rValue)) rValue = 0.0;
+    if (isinf(rValue)) rValue = 1.0;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0.0) bValue = 0.0;
+    if (bValue > 1.0) bValue = 1.0;
+    if (isnan(bValue)) bValue = 0.0;
+    if (isinf(bValue)) bValue = 1.0;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0.0) gValue = 0.0;
+    if (gValue > 1.0) gValue = 1.0;
+    if (isnan(gValue)) gValue = 0.0;
+    if (isinf(gValue)) gValue = 1.0;
+
+    // XXX at the moment, we are saturating before supplying to this function
+    // NOTE : x,y,z = red,green,blue 
+    node = CCFindBottom (cube, rValue, gValue, bValue);
+    *oData = node->pixel;
+  }
+}
+
+// iterate over the CCNode tree and subdivide any bottom level nodes with value > minValue
+// XXX this function is specific to the color histogram concept
+// NOTE : x,y,z = red,green,blue 
+int CCNodeSetColorMap (CCNode *node, XColor *cmap, int Npixels, int *current) {
+
+  int ix, iy, iz;
+
+  if (node->bottom) {
+    cmap[current[0]].red   = node->mid[CC_X]*0xffff;
+    cmap[current[0]].green = node->mid[CC_Y]*0xffff;
+    cmap[current[0]].blue  = node->mid[CC_Z]*0xffff;
+    cmap[current[0]].flags = DoRed | DoGreen | DoBlue;
+    node->pixel = current[0];
+    current[0] ++;
+    assert (current[0] <= Npixels);
+    return TRUE;
+  }
+  
+  for (ix = 0; ix < 2; ix++) {
+    for (iy = 0; iy < 2; iy++) {
+      for (iz = 0; iz < 2; iz++) {
+	CCNodeSetColorMap (node->sub[ix][iy][iz], cmap, Npixels, current);
+      }
+    }
+  }
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateColorbar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateColorbar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateColorbar.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+
+void CreateColorbar (KapaImageWidget *image, Graphic *graphic) {
+
+  int i, j, dx, dy, extra, start;
+  unsigned long  pixvalue;
+  unsigned int  *out24;
+  unsigned char *out8;
+
+  dx = image[0].cmapbar.dx;
+  dy = image[0].cmapbar.dy;
+
+  /* create the cmap scale */
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (image[0].cmapbar.data, char, dx*dy);
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out8[j*dx + i] = pixvalue;
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (image[0].cmapbar.data, char, 2*dy*dx);
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = 2*j*dx + 2*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (dx * 3) % 4;
+    REALLOCATE (image[0].cmapbar.data, char, dy*(3*dx + extra));
+    out8 = (unsigned char *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	start = j*(3*dx+extra) + 3*i;
+	out8[start + 0] = 0x0000ff & pixvalue;
+	out8[start + 1] = 0x0000ff & (pixvalue >> 8);
+	out8[start + 2] = 0x0000ff & (pixvalue >> 16);
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+
+  case 32:
+    REALLOCATE (image[0].cmapbar.data, char, 4*dx*dy);
+    out24 = (unsigned int *) image[0].cmapbar.data;
+    for (i = 0; i < dx; i++) {
+      pixvalue = graphic[0].cmap[(int)(i*graphic[0].Npixels/dx)].pixel;
+      for (j = 0; j < dy; j++) {
+	out24[j*dx + i] = pixvalue;
+      }
+    }
+    image[0].cmapbar.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					 image[0].cmapbar.data, dx, dy, 32, 0);
+    break;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreatePicture.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreatePicture.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreatePicture.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "Ximage.h"
+
+void CreatePicture (KapaImageWidget *image, Graphic *graphic) {
+
+  int i, j, extra;
+  unsigned char *c;
+  unsigned int *l;
+  unsigned int start, start1, start2, start3;
+
+  start = graphic[0].back;
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    REALLOCATE (image[0].wide.data,    char, image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, image[0].picture.dx*image[0].picture.dy);
+    c = (unsigned char *) image[0].picture.data;
+    for (i = 0; i < (image[0].picture.dx*image[0].picture.dy); i++, c++)
+      *c = start;
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 8, 0);
+    break;
+
+  case 16:
+    REALLOCATE (image[0].wide.data,    char, 2*image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, 2*image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, 2*image[0].picture.dy*image[0].picture.dx);
+    c = (unsigned char *) image[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    for (i = 0; i < image[0].picture.dy; i++) {
+      for (j = 0; j < image[0].picture.dx; j++, c+=2) {
+	c[0] = start1;
+	c[1] = start2;
+      }
+    }
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 16, 0);
+    break;
+
+  case 24:
+    extra = 4 - (image[0].picture.dx * 3) % 4;
+    REALLOCATE (image[0].wide.data, char, image[0].wide.dy*(3*image[0].wide.dx+extra));
+    REALLOCATE (image[0].zoom.data, char, image[0].zoom.dy*(3*image[0].zoom.dx+extra));
+    REALLOCATE (image[0].picture.data, char, image[0].picture.dy*(3*image[0].picture.dx+extra));
+    c = (unsigned char *) image[0].picture.data;
+    start1 = 0x0000ff & (start);
+    start2 = 0x0000ff & (start >> 8);
+    start3 = 0x0000ff & (start >> 16);
+    for (i = 0; i < image[0].picture.dy; i++) {
+      for (j = 0; j < image[0].picture.dx; j++, c+=3) {
+	c[0] = start1;
+	c[1] = start2;
+	c[2] = start3;
+      }
+      c+=extra;
+    }
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 24, 0);
+    break;
+
+  case 32:
+    REALLOCATE (image[0].wide.data,    char, 4*image[0].wide.dx*image[0].wide.dy);
+    REALLOCATE (image[0].zoom.data,    char, 4*image[0].zoom.dx*image[0].zoom.dy);
+    REALLOCATE (image[0].picture.data, char, 4*image[0].picture.dx*image[0].picture.dy);
+    l = (unsigned int *) image[0].picture.data;
+    for (i = 0; i < (image[0].picture.dx*image[0].picture.dy); i++, l++)
+      *l = start;
+    image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					  image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 32, 0);
+    break;
+  }
+    
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWide.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWide.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWide.c	(revision 17422)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+void CreateWide (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->wide, &image->image->matrix);
+    break;
+  }    
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWindow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWindow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateWindow.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "Ximage.h"
+
+/************** CreateWindow *************/
+void CreateWindow (Graphic *graphic, Window parent, int border, long events) {
+
+  XSetWindowAttributes attributes;
+  unsigned long attribute_mask;
+  Visual *visual = CopyFromParent;
+
+  HAVE_BACKING = (DoesBackingStore (ScreenOfDisplay(graphic->display, graphic->screen)) == Always);
+  HAVE_BACKING = FALSE;
+
+  if (HAVE_BACKING) {
+    attributes.backing_store = Always;
+    attribute_mask = CWBackingStore | CWBackPixel | CWBorderPixel | CWEventMask;
+  } else {
+    attribute_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+  }
+
+  attributes.background_pixel = graphic->back;
+  attributes.border_pixel     = graphic->fore;
+  attributes.event_mask       = events;
+
+  graphic->window = XCreateWindow (graphic->display, parent, 
+				     graphic->x, graphic->y, 
+				     graphic->dx, graphic->dy, 
+				     border, CopyFromParent,
+				     InputOutput, visual, 
+				     attribute_mask, &attributes);
+
+  if (graphic->window == (Window) None)
+    QuitX (graphic->display, "error: could not open window");
+
+  XSelectInput (graphic->display, graphic->window, events);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+void CreateZoom (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->zoom, &image->image->matrix);
+    break;
+  }    
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom16.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom16.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom16.c	(revision 17422)
@@ -0,0 +1,144 @@
+# include "Ximage.h"
+
+void CreateZoom16 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  float *imdata, *in_pix,  *in_pix2;
+  unsigned char pixel1[256], pixel2[256];
+  unsigned char pixvalue1, pixvalue2;
+  unsigned long back;
+  unsigned char back1, back2;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, 2*dy*dx);
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+  }
+  back = graphic[0].back;
+  back1 = 0x0000ff & back;
+  back2 = 0x0000ff & (back >> 8);
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+    }
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[2*j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2+=2) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+      }
+    }
+    out_pix += 2*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=2) {
+	pixelN = PixelLookup(*in_pix2);
+	out_pix[0] = pixel1[pixelN];
+	out_pix[1] = pixel2[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 2*expand_out) { 
+	pixelN = PixelLookup(*in_pix2);
+	pixvalue1 = pixel1[pixelN];
+	pixvalue2 = pixel2[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=2*(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=2) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	  }
+	}
+      }
+    }
+    out_pix -= 2*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + 2*jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2+=2) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+      }
+    }
+  } 
+  
+  out_pix = &data[2*j_end*dx];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=2) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+    }
+  }
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 16, 0);
+  
+# endif
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom24.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom24.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom24.c	(revision 17422)
@@ -0,0 +1,178 @@
+# include "Ximage.h"
+
+void CreateZoom24 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj, extra;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  float *imdata, *in_pix, *in_pix2;
+  unsigned char pixel1[256], pixel2[256], pixel3[256];
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned long back;
+  unsigned char back1, back2, back3;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  extra = 4 - (dx * 3) % 4;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, dy*(3*dx+extra));
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
+  }
+  back = graphic[0].back;
+  back1 = 0x0000ff & back;
+  back2 = 0x0000ff & (back >> 8);
+  back3 = 0x0000ff & (back >> 16);
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix += extra;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	pixelN = PixelLookup(*in_pix2);
+	out_pix[0] = pixel1[pixelN];
+	out_pix[1] = pixel2[pixelN];
+	out_pix[2] = pixel3[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixelN   = PixelLookup(*in_pix2);
+	pixvalue1 = pixel1[pixelN];
+	pixvalue2 = pixel2[pixelN];
+	pixvalue3 = pixel3[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+  } 
+  
+  /*
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  */
+
+  out_pix = &data[j_end*(3*dx+extra)];
+  /**** fill in top area ****/
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix+=extra;
+  }
+
+  /*
+  out_pix = (unsigned char *) image[0].zoom.data;
+  for (i = 0; i < dx*dy; i++) {
+    out_pix[3*i + 0] = back1;
+    out_pix[3*i + 1] = back2;
+    out_pix[3*i + 2] = back3;
+  }
+  for (j = 0; j < dy/2; j++) {
+    for (i = 0; i < dx; i++) {
+      out_pix[j*(extra + 3*dx) + 3*i + 0] = pixel1[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 1] = pixel2[10];
+      out_pix[j*(extra + 3*dx) + 3*i + 2] = pixel3[10];
+    }
+
+  }
+  */
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, 24, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+  
+# endif
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom32.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom32.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom32.c	(revision 17422)
@@ -0,0 +1,138 @@
+# include "Ximage.h"
+
+void CreateZoom32 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2;
+  unsigned short *in_pix, *in_pix2;
+  unsigned long *pixel, pixvalue;
+  unsigned long back;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, 4*image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+    return;
+  }
+
+  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { 
+    pixel[i] = graphic[0].cmap[i].pixel;
+  }
+  back = graphic[0].back;
+
+  // set up expansions
+  assert ((image[0].zoom.expand >= 1) || (image[0].zoom.expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (image[0].zoom.expand > 0) {
+    expand = 1 / (1.0*image[0].zoom.expand);
+    expand_out = image[0].zoom.expand;
+    expand_in  = 1;
+  }
+  if (image[0].zoom.expand < 0) {
+    expand = fabs((double)image[0].zoom.expand);
+    expand_out = 1;
+    expand_in  = -image[0].zoom.expand;
+  }
+
+  // define the image sizes
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].zoom);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].zoom);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].zoom);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) image[0].zoom.data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  // XXX can we just drop this?
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      // equiv to : out_pix += (i_end - i_start) + [expand_out - (i_end - i_start) % expand_out]
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue; 
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+  } 
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= (expand_out - (j_end - j_start) % expand_out);
+  
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)image[0].zoom.data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+  assert (out_pix - (unsigned int *)image[0].zoom.data <= dx*dy);
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 32, 0);
+  
+  free (pixel);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom8.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom8.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CreateZoom8.c	(revision 17422)
@@ -0,0 +1,130 @@
+# include "Ximage.h"
+
+void CreateZoom8 (KapaImageWidget *image, Graphic *graphic, double x, double y) {
+
+# if (0)
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, pixelN;
+  double expand, zoomscale, Rx, Ry;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2;
+  float *imdata, *in_pix, *in_pix2;
+  unsigned long pixel[256], pixvalue;
+  unsigned long back;
+
+  if (image[0].image[0].matrix.size == 0) {  /* create a test pattern */
+    REALLOCATE (image[0].zoom.data, char, image[0].zoom.dx*image[0].zoom.dy);
+    bzero (image[0].zoom.data, image[0].zoom.dx*image[0].zoom.dy);
+    image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+				       image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
+    return;
+  }
+
+  for (i = 0; i < 256; i++) { /* set up pixel array */
+    pixel[i] = graphic[0].cmap[i].pixel;
+  }
+  back = graphic[0].back;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  zoomscale = MAX (5, image[0].expand + 5);
+  expand = 1 / zoomscale;
+  expand_out = (int) zoomscale;
+  expand_in  = 1;
+
+  dx = image[0].zoom.dx;
+  dy = image[0].zoom.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+  Rx = x - expand*(int)(0.5*(dx + 1)) + 1;
+  Ry = y - expand*(int)(0.5*(dy + 1)) + 1;
+
+  i_start = MIN (MAX (-Rx / expand, (1 - FRAC(Rx)) / expand), dx - expand_out + 1);
+  j_start = MIN (MAX (-Ry / expand, (1 - FRAC(Ry)) / expand), dy - expand_out + 1);
+  i_end   = MAX (MIN ((DX-Rx) / expand, dx - expand_out + 1), 0);
+  j_end   = MAX (MIN ((DY-Ry) / expand, dy - expand_out + 1), 0);
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned char *) image[0].zoom.data;
+  imdata  = (float *) image[0].image[0].matrix.buffer;
+  in_pix  = &imdata[DX*(int)MAX(Ry,0) + (int)MAX(Rx,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  if ((i_end < i_start) || (j_end < j_start)) {
+    for (j = 0; j < dx*dy; j++, out_pix++) 
+      *out_pix = back;
+    return;
+  } 
+  
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) 
+    *out_pix = back;
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	pixelN = PixelLookup(*in_pix2);
+	*out_pix = pixel[pixelN];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixelN   = PixelLookup(*in_pix2);
+	pixvalue = pixel[pixelN];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *) image[0].zoom.data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+
+  image[0].zoom.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					image[0].zoom.data, image[0].zoom.dx, image[0].zoom.dy, 8, 0);
+  
+# endif
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CrossHairs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CrossHairs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CrossHairs.c	(revision 17422)
@@ -0,0 +1,67 @@
+# include "Ximage.h"
+
+void CrossHairs (Graphic *graphic, Picture *image) {
+
+  int x0, x1, x5, x7;
+  int y0, y2, y4, y5;
+  double zoomscale;
+
+  // is this totally wrong??
+  zoomscale = image[0].expand;
+  x0 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 2.5) + image[0].x;
+  x5 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale - 0.5) + image[0].x;
+  x7 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 0.5) + image[0].x;
+  x1 = (zoomscale) * (0.5 * (ZOOM_X + 1) / zoomscale + 2.5) + image[0].x;
+						    	 
+  y4 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 2.5) + image[0].y;
+  y2 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale - 0.5) + image[0].y;
+  y0 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 0.5) + image[0].y;
+  y5 = (zoomscale) * (0.5 * (ZOOM_Y + 1) / zoomscale + 2.5) + image[0].y;
+
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0, x5, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y0, x5, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y5, x7, y0);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y0, x1, y0);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2, x5, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5, y2, x5, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y4, x7, y2);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7, y2, x1, y2);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y0+1, x5-1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y0+1, x5-1, y5);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y5, x7+1, y0+1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y0+1, x1, y0+1);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x0, y2-1, x5-1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x5-1, y2-1, x5-1, y4);
+
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y4, x7+1, y2-1);
+  XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	     x7+1, y2-1, x1, y2-1);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/CursorOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/CursorOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/CursorOps.c	(revision 17422)
@@ -0,0 +1,121 @@
+# include "Ximage.h"
+
+// input coordinates are relative to the picture bounding box
+void Picture_to_Image (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double expand, dx, dy;
+
+  expand = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+  } else {
+    expand = fabs((double)picture[0].expand);
+  }
+  
+  // pixel coordinates in picture frame
+  dx = expand*(x2 - 0.5*picture[0].dx);
+  dy = expand*(y2 - 0.5*picture[0].dy);
+
+  // picture[0].X,Y is the image coordinate in the center of the picture
+  *x1 = picture[0].flipx ? picture[0].X - dx : picture[0].X + dx;
+  *y1 = picture[0].flipy ? picture[0].Y - dy : picture[0].Y + dy;
+}
+
+// input coordinates are relative to the X window
+void Screen_to_Image (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double xp, yp;
+
+  // pixel coordinates in picture frame
+  xp = x2 - picture[0].x;
+  yp = y2 - picture[0].y;
+
+  Picture_to_Image (x1, y1, xp, yp, picture);
+}
+
+void Image_to_Picture (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double expand, dx, dy;
+
+  /* notice that here, expand is the reciprocal of the expand above */
+  expand = 1.0;
+  if (picture[0].expand > 0) {
+    expand = picture[0].expand;
+  } else {
+    expand = 1 / fabs((double)picture[0].expand);
+  }
+  
+  // pixel coordinates in picture frame
+  dx = picture[0].flipx ? picture[0].X - x2 : x2 - picture[0].X;
+  dy = picture[0].flipy ? picture[0].Y - y2 : y2 - picture[0].Y;
+    
+  *x1 = expand*dx + 0.5*picture[0].dx;
+  *y1 = expand*dy + 0.5*picture[0].dy;
+}
+
+void Image_to_Screen (double *x1, double *y1, double x2, double y2, Picture *picture) {
+
+  double xp, yp;
+
+  Image_to_Picture (&xp, &yp, x2, y2, picture);
+
+  // pixel coordinates in screen frame
+  *x1 = xp + picture[0].x;
+  *y1 = yp + picture[0].y;
+}
+
+// input coordinates are relative to the picture bounding box
+void Picture_Lower (int *i_start, int *j_start, Matrix *matrix, Picture *picture) {
+
+  double Ix, Iy, Sx, Sy;
+
+  // Ix, Iy are the image coordinates of the specified screen pixel
+  Picture_to_Image (&Ix, &Iy, 0.0, 0.0, picture);
+
+  // Ix, Iy are now limited to valid image coordinates
+  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
+  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
+
+  // round up (down) to nearest pixel boundary
+  if (Ix > (int)(Ix)) {
+    Ix = picture[0].flipx ? (int)(Ix) : (int)(Ix) + 1;
+  }
+  if (Iy > (int)(Iy)) {
+    Iy = picture[0].flipy ? (int)(Iy) : (int)(Iy) + 1;
+  }
+
+  // Sx, Sy are the screen coordinates of the Ix,Iy pixel
+  Image_to_Picture (&Sx, &Sy, Ix, Iy, picture);
+
+  // i_start, j_start are now limited to valid screen coordinates
+  *i_start = MIN (MAX (Sx, 0), picture[0].dx);
+  *j_start = MIN (MAX (Sy, 0), picture[0].dy);
+}
+  
+// input coordinates are relative to the picture bounding box
+void Picture_Upper (int *i_end, int *j_end, Matrix *matrix, Picture *picture) {
+
+  double Ix, Iy, Sx, Sy;
+
+  // Ix, Iy are the image coordinates of the specified screen pixel
+  Picture_to_Image (&Ix, &Iy, picture[0].dx, picture[0].dy, picture);
+
+  // Ix, Iy are now limited to valid image coordinates
+  Ix = MIN (MAX (Ix, 0), matrix[0].Naxis[0]);
+  Iy = MIN (MAX (Iy, 0), matrix[0].Naxis[1]);
+
+  // round down (up) to nearest pixel boundary
+  if (Ix > (int)(Ix)) {
+    Ix = picture[0].flipx ? (int)(Ix) + 1 : (int)(Ix);
+  }
+  if (Iy > (int)(Iy)) {
+    Iy = picture[0].flipy ? (int)(Iy) + 1: (int)(Iy);
+  }
+
+  // Sx, Sy are the screen coordinates of the Ix,Iy pixel
+  Image_to_Picture (&Sx, &Sy, Ix, Iy, picture);
+
+  // i_start, j_start are now limited to valid screen coordinates
+  *i_end = MIN (MAX (Sx, 0), picture[0].dx);
+  *j_end = MIN (MAX (Sy, 0), picture[0].dy);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DefineSection.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DefineSection.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DefineSection.c	(revision 17422)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+// define the section, but do not create a graph or image 
+int DefineSection (int sock) {
+  
+  int N, MoveSection;
+  char name[128];
+  double x, y, dx, dy;
+  Section *section;
+
+  KiiScanMessage (sock, "%s %lf %lf %lf %lf", name, &x, &y, &dx, &dy);
+
+  MoveSection = FALSE;
+
+  N = GetSectionByName (name);
+  if (N < 0) {
+    section = AddSection (name, x, y, dx, dy);
+    MoveSection = TRUE;
+  } else {
+    section = GetSectionByNumber (N);
+    SetActiveSectionByNumber (N);
+
+    if (section[0].x != x)   MoveSection = TRUE;
+    if (section[0].y != y)   MoveSection = TRUE;
+    if (section[0].dx != dx) MoveSection = TRUE;
+    if (section[0].dy != dy) MoveSection = TRUE;
+
+    section[0].x = x;
+    section[0].y = y;
+    section[0].dx = dx;
+    section[0].dy = dy;
+  }
+
+  if (MoveSection) {
+    SetSectionSizes (section);
+    Refresh (1);
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DragColorbar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DragColorbar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DragColorbar.c	(revision 17422)
@@ -0,0 +1,106 @@
+# include "Ximage.h"
+/* future expansion: warp mouse to the right spot for the current settings */
+
+void DragColorbar (Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event) {
+
+  double          frac_x, oldfrac_x, frac_y, oldfrac_y, start, slope;
+  int             X, Y;
+  XEvent          event;
+  XMotionEvent   *move;
+  int             xstatus, Npix, center;
+
+  if (!graphic[0].visualclass) return;
+
+  X = mouse_event[0].x;
+  Y = mouse_event[0].y;
+  Npix = graphic[0].Npixels;
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  oldfrac_x = oldfrac_y = 10;
+  
+  while (1) {
+    frac_x = X / (1.0*graphic[0].dx);
+    frac_x = MAX (0, frac_x);
+    frac_x = MIN (1, frac_x);
+    frac_y = Y / (1.0*graphic[0].dy);
+    frac_y = MAX (0, frac_y);
+    frac_y = MIN (1, frac_y);
+    switch (mouse_event[0].button) {
+    case 1:
+      center = frac_x*Npix;
+      slope = Npix / (1 + 4*frac_y*frac_y*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    case 2:
+      start = 0;
+      slope = 1;
+      break;
+    case 3:
+      center = (Npix/2.0 - start) / slope;
+      slope = Npix / (1 + 4*frac_x*frac_x*Npix);
+      start = Npix/2.0 - center*slope;
+      break;
+    }
+
+    if ((frac_x != oldfrac_x) || (frac_y != oldfrac_y)) {
+      ResetColorbar (graphic, start, slope);
+    }
+    oldfrac_x = frac_x;
+    oldfrac_y = frac_y;
+      
+    if ((xstatus = XCheckMaskEvent (graphic[0].display, EVENT_MASK, &event))) {
+      
+      switch (event.type)  {
+	
+      case MotionNotify:
+	if ((xstatus = XPending (graphic[0].display)) < 2) {
+	  move = (XMotionEvent *) &event;
+	  X = move[0].x;
+	  Y = move[0].y;
+	}
+	break;
+	
+      case ButtonPress:
+      case ButtonRelease:
+	image[0].image[0].start = start;	
+	image[0].image[0].slope = slope;
+	return;
+	break;
+	
+      }
+    }
+  }
+  
+}
+
+void ResetColorbar (Graphic *graphic, double start, double slope) {
+
+  int i, j;
+  XColor cmap[256];
+
+  if (!graphic[0].visualclass) return;
+
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    cmap[i] = graphic[0].cmap[i];
+    j = start + i * slope;
+    if (j < 0) {
+      cmap[i].red = graphic[0].cmap[0].red;
+      cmap[i].blue = graphic[0].cmap[0].blue;
+      cmap[i].green = graphic[0].cmap[0].green;
+    }
+    else {
+      if (j >= graphic[0].Npixels) {
+	cmap[i].red = graphic[0].cmap[graphic[0].Npixels-1].red;
+	cmap[i].blue = graphic[0].cmap[graphic[0].Npixels-1].blue;
+	cmap[i].green = graphic[0].cmap[graphic[0].Npixels-1].green;
+      }
+      else {
+	cmap[i].red = graphic[0].cmap[j].red;
+	cmap[i].blue = graphic[0].cmap[j].blue;
+	cmap[i].green = graphic[0].cmap[j].green;
+      }
+    }
+  }
+
+  XStoreColors(graphic[0].display, graphic[0].colormap, cmap, graphic[0].Npixels);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawBitmap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawBitmap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawBitmap.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+
+void DrawBitmap (Graphic *graphic, int x, int y, int dx, int dy, char *bitmap, int mode) {
+
+  int i, j, byte_line, byte, bit, flag;
+  unsigned long int fore, back;
+
+  fore = graphic[0].fore;
+  back = graphic[0].back;
+
+  if (mode == 0) {
+    fore = graphic[0].fore;
+    back = graphic[0].back;
+    graphic[0].fore = back;
+    graphic[0].back = back;
+  }
+    
+  if (mode == 2) {
+    fore = graphic[0].fore;
+    back = graphic[0].back;
+    graphic[0].fore = back;
+    graphic[0].back = fore;
+  }
+    
+  
+  byte_line = (int) ((dx + 7) / 8);
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      byte = byte_line * i + (j / 8);
+      bit = j % 8;
+      flag = 0x01 & (bitmap[byte] >> bit);
+      if (flag)
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+      else 
+	XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+      XDrawPoint (graphic[0].display, graphic[0].window, 
+		  graphic[0].gc, x + j, y + i);
+    }
+  }
+  if (mode == 0) {
+    graphic[0].fore = fore;
+    graphic[0].back = back;
+  }
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawButton.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawButton.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawButton.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "Ximage.h"
+
+void DrawButton (Graphic *graphic, Button *button) {
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  XDrawRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  button[0].x,  button[0].y,
+		  button[0].dx, button[0].dy);
+
+  DrawBitmap (graphic, 
+	      button[0].x + (button[0].dx - button[0].width) / 2 + 1, 
+	      button[0].y + (button[0].dy - button[0].height) / 2 + 1, 
+	      button[0].width, button[0].height, 
+	      button[0].bitmap, 1);
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
+
+# if (0)
+  if (button[0].text) {
+    dX = XTextWidth (graphic[0].font, button[0].bitmap, strlen(button[0].bitmap));
+    y = button[0].y + (button[0].dy + graphic[0].font[0].ascent)/2;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 button[0].x + (button[0].dx - dX) / 2, y,
+		 button[0].bitmap, strlen(button[0].bitmap));
+  } else {
+    DrawBitmap (graphic, 
+		button[0].x + (button[0].dx - button[0].width) / 2 + 1, 
+		button[0].y + (button[0].dy - button[0].height) / 2 + 1, 
+		button[0].width, button[0].height, 
+		button[0].bitmap, 1);
+  }
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawFrame.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawFrame.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawFrame.c	(revision 17422)
@@ -0,0 +1,179 @@
+# include "Ximage.h"
+# define MIN_RANGE 1e-10
+# define DrawLine(X,Y,DX,DY) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X), (int)(Y), (int)(X+DX), (int)(Y+DY)))
+  
+static Graphic *graphic;
+
+void DrawFrame (KapaGraphWidget *graph) {
+  
+  int i, fx, fy, dfx, dfy, P, IsLabel;
+  double range, major, minor, first, next;
+
+  graphic = GetGraphic();
+
+  // XXX why is this called for every redraw?
+  DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->fore, graphic->back);
+
+  /* each axis is drawn independently, but ticks and labels are placed according to
+     perpendicular distance. */
+  for (i = 0; i < 4; i++) {
+    fx = graph[0].axis[i].fx;
+    fy = graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    if (graph[0].axis[i].isaxis) {
+      DrawLine (fx, fy, dfx, dfy);
+    }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+      if (fabs(range) < MIN_RANGE) continue;
+
+      if (fabs(range) < MIN_RANGE) {
+	range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
+      }
+
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = graph[0].axis[i].islabel;
+	DrawTick (fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+}
+
+void DrawTick (int fx, int fy, int dfx, int dfy, 
+	       int P, double min, double max, 
+	       double value, int mode, int naxis) {
+  
+  int x, y, dx, dy, pos, dir, fontsize;
+  double size, n;
+  char string[64], *fontname;
+
+  if (mode) { 
+    size = MAX (0.02, 7.0 / P); 
+  } else {
+    size = MAX (0.01, 4.0 / P); 
+  }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+# ifdef IN_AND_OUT_TICKS
+# define GAP 0.03
+# else
+# define GAP 0.01
+# endif
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+    pos = 0;
+    
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value/(max-min)) < 0.001) { value = 0.0; }
+    sprintf (string, "%4g", value);
+    DrawRotText (x, y, string, pos, 0.0);
+  }
+  
+}
+
+
+void AxisTickScale (Axis *axis, double *major, double *minor) {
+
+  double range, lrange, factor, mantis, fmantis, power;
+
+  range = axis[0].max - axis[0].min;
+  if (fabs(range) < MIN_RANGE) {
+    range = (range < 0) ? -MIN_RANGE : +MIN_RANGE;
+  }
+
+  lrange = log10(MAX(fabs(range), MIN_RANGE));
+  mantis = modf (lrange, &factor);
+  if (mantis < 0.0) {
+    mantis += 1.0;
+    factor -= 1.0;
+  }
+  
+  power = MAX(pow(10.0, factor), MIN_RANGE);
+  fmantis = pow(10.0, mantis);
+  *major = MAX(0.5 * power, MIN_RANGE);
+  *minor = MAX(0.1 * power, MIN_RANGE);
+  
+  if ((fmantis >= 1.0) && (fmantis <  1.999)) {
+    *major = 0.5 * power;
+    *minor = 0.1 * power;
+    if (axis[0].areticks == 1) {
+      *major = 1.0 * power;
+      *minor = 0.2 * power;
+    }	  
+  }
+  if ((fmantis >= 1.999) && (fmantis <  3.999)) {
+    *major = 1.0 * power;
+    *minor = 0.2 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 3.999) && (fmantis <  5.999)) {
+    *major = 1.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.0 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 5.999) && (fmantis <   7.999)) {
+    *major = 2.0 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 2.5 * power;
+      *minor = 0.5 * power;
+    }	  
+  }
+  if ((fmantis >= 7.999) && (fmantis <  10.000)) {
+    *major = 2.5 * power;
+    *minor = 0.5 * power;
+    if (axis[0].areticks == 1) {
+      *major = 5.0 * power;
+      *minor = 1.0 * power;
+    }	  
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawLabels.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawLabels.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawLabels.c	(revision 17422)
@@ -0,0 +1,60 @@
+# include "Ximage.h"
+  
+void DrawLabels (KapaGraphWidget *graph) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->fore, graphic->back);
+
+  /* each label is drawn independently */
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size);
+      DrawRotText (x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+          
+ 6____7___8 
+  |       | 
+  |       | 
+ 3|   4   |5
+  |       |
+  |       |
+  ---------
+  0   1   2
+
+  */
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawObjects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawObjects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawObjects.c	(revision 17422)
@@ -0,0 +1,624 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,X2,Y2) (XDrawLine (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawRectangle(X1,Y1,X2,Y2) (XDrawRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define FillRectangle(X1,Y1,X2,Y2) (XFillRectangle (graphic->display, graphic->window, graphic->gc, (int)(X1+0.5), (int)(Y1+0.5), (int)(X2+0.5), (int)(Y2+0.5)))
+# define DrawCircle(X,Y,R) (XDrawArc (graphic->display, graphic->window, graphic->gc, (int)(X-R+0.5), (int)(Y-R+0.5), abs(2*R+0.5), abs(2*R+0.5), 0, 23040))
+
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y0, double X1, double Y1);
+
+/* draw all objects for this Graph */
+int DrawObjects (KapaGraphWidget *graph) {
+  
+  int i;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    if (DEBUG) fprintf (stderr, "object: %d\n", i);
+    if (DEBUG) fprintf (stderr, "Npts: %d\n", graph[0].objects[i].Npts);
+    DrawObjectN (graph, &graph[0].objects[i]);
+  }    
+  return (TRUE);
+}
+
+/* Draw a specific object in the graph */
+int DrawObjectN (KapaGraphWidget *graph, Gobjects *object) {
+  
+  static char dash[2] = {5,10};
+  static char dot[2] = {3,3};
+  int lweight;
+  
+  // this function calls all of the supporting Draw... functions below
+  graphic = GetGraphic();
+
+  lweight = MAX (0, MIN (10, object[0].lweight));
+
+  /* set line type */
+  switch (object[0].ltype) {
+  case 0:
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  case 1:
+    XSetDashes (graphic->display, graphic->gc, 100, dash, 2);
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  case 2:
+    XSetDashes (graphic->display, graphic->gc, 10, dot, 2);
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineOnOffDash, CapNotLast, JoinMiter);
+    break;
+  default:
+    XSetLineAttributes (graphic->display, graphic->gc, lweight, LineSolid, CapNotLast, JoinMiter);
+    break;
+  }
+
+  XSetForeground (graphic->display, graphic->gc, graphic->color[object[0].color]);
+
+  switch (object[0].style) {
+  case CONNECT: 
+    DrawConnect (graph, object);
+    break;
+  case HISTOGRAM:
+    DrawHistogram (graph, object);
+    break;
+  case POINTS:
+    DrawPoints (graph, object);
+    break;
+  }
+    
+  if (object[0].etype & 0x01) {
+    DrawYErrors (graph, object);
+  }
+  if (object[0].etype & 0x02) {
+    DrawXErrors (graph, object);
+  }
+
+  XSetLineAttributes (graphic->display, graphic->gc, 0, LineSolid, CapNotLast, JoinMiter);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  return (TRUE);
+}
+
+/******/
+void DrawConnect (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    
+    ClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+  
+}
+
+void ClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+  
+
+/******/
+void DrawHistogram (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/******/
+void DrawPoints (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d*z[i], sy - d*z[i], 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i] + 1, sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i] + 1, sy - d*z[i] - 1, sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i] + 1, sy + d*z[i] + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d*z[i];  points[0].y = sy + 0.58*d*z[i];  
+	    points[1].x = sx + d*z[i];  points[1].y = sy + 0.58*d*z[i];  
+	    points[2].x = sx;           points[2].y = sy - 1.15*d*z[i];  
+	    points[3].x = sx - d*z[i];  points[3].y = sy + 0.58*d*z[i];  
+	    XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	if (!(finite(x[i+1]) && finite(y[i+1]))) continue;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } 
+  /**** points are not scaled by object.z ***/
+  else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx - d, sy - d, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d + 1, sy);
+	    DrawLine (sx, sy - d, sx, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    /* XDrawLine is a little funny, this is needed to fix endpost errors */
+	    DrawLine (sx + d + 1, sy - d - 1, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d + 1, sy + d + 1);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      XPoint points[4];
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    points[0].x = sx - d;  points[0].y = sy + 0.58*d;  
+	    points[1].x = sx + d;  points[1].y = sy + 0.58*d;  
+	    points[2].x = sx;      points[2].y = sy - 1.15*d;  
+	    points[3].x = sx - d;  points[3].y = sy + 0.58*d;  
+	    XFillPolygon (graphic->display, graphic->window, graphic->gc, points, 4, Convex, CoordModeOrigin);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy + 0.58*d);
+	    DrawLine (sx, sy, sx,     sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/******/
+void DrawXErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy))) 
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/******/
+void DrawYErrors (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = sx0 + dyp[i]*mxj;
+    sy1 = sy0 + dyp[i]*myj;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawTextlines.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawTextlines.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/DrawTextlines.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+  
+void DrawTextlines (KapaGraphWidget *graph) {
+  
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  fontname = GetRotFont (&size);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  DrawRotTextInit (graphic->display, graphic->window, graphic->gc, graphic->fore, graphic->back);
+
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      DrawRotText (x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseCurrentPlot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseCurrentPlot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseCurrentPlot.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "Ximage.h"
+
+// erase just the current plot
+int EraseCurrentPlot () {
+  
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) return (TRUE);
+
+  EraseGraph (section->graph);
+  
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseImage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseImage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseImage.c	(revision 17422)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+int EraseImage () {
+
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  FreeImage (image);
+  section->image = NULL;
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseOverlay.c	(revision 17422)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+
+int EraseOverlay (int sock) {
+
+  int i, N;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image = section->image;
+  if (image == NULL) return (TRUE);
+
+  KiiScanCommand (sock, 16, "%*s %d", &N);
+
+  if (N > NOVERLAYS) {
+    REALLOCATE (image[0].tickmarks.objects, KiiOverlay, 1);
+    image[0].tickmarks.Nobjects = 0;
+  } else {
+    for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+      if (image[0].overlay[N].objects[i].type == KII_OVERLAY_TEXT) {
+	free (image[0].overlay[N].objects[i].text);
+      }
+    }
+    REALLOCATE (image[0].overlay[N].objects, KiiOverlay, 1);
+    image[0].overlay[N].Nobjects = 0;
+    image[0].overlay[N].active = FALSE;
+  }
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/ErasePlots.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/ErasePlots.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/ErasePlots.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+// erase all plots, keep the sections
+int ErasePlots () {
+  
+  int i, Nsection;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      EraseGraph (section->graph);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseSections.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseSections.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/EraseSections.c	(revision 17422)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+int EraseSections () {
+  
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+  
+  FreeSections ();
+  AddSection ("default", 0.0, 0.0, 1.0, 1.0);
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/EventLoop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/EventLoop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/EventLoop.c	(revision 17422)
@@ -0,0 +1,141 @@
+# include "Ximage.h"
+
+/* list events being selected below, all other masks are ignored */ 
+# define IgnoreMask (long) (~(StructureNotifyMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask))
+
+int LastEvent (Display *display, int type, XEvent *event) {
+
+  int found;
+
+  found = FALSE;
+  while (XCheckTypedEvent (display, type, event)) {
+    found = TRUE;
+  }
+  return (found);
+}
+
+int EventLoop () {
+  
+  int      status;
+  XEvent   event;
+  Display *display;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+  display = graphic->display;
+  
+  if (USE_XWINDOW) Refresh (1);
+
+  status = TRUE;
+  while (status) {
+    
+    if (!CheckPipe ()) return (FALSE);
+    
+    if (!USE_XWINDOW) {
+      usleep (50000);
+      continue;
+    }
+
+    if (XEventsQueued (display, QueuedAfterFlush) < 1) {
+      /* fprintf (stderr, "."); */
+      usleep (50000);
+      continue;
+    }
+
+    /* grab the last entry for these events */
+    if (LastEvent (display, ConfigureNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, CirculateNotify, &event)) Reconfig (&event);
+    if (LastEvent (display, Expose,          &event)) Refresh (1);
+    if (LastEvent (display, MappingNotify,   &event)) XRefreshKeyboardMapping ((XMappingEvent *) &event);
+    if (LastEvent (display, MotionNotify,    &event)) UpdatePointer (graphic, (XMotionEvent *) &event);
+    if (LastEvent (display, ButtonPress,     &event)) InterpretPresses (graphic, (XButtonEvent *) &event);
+    if (LastEvent (display, KeyPress,        &event)) InterpretKeys (graphic, (XKeyEvent *) &event);
+
+    /* drop and ignore the following StructureNotifyMask events */
+    LastEvent (display, GravityNotify, &event);
+    LastEvent (display, ReparentNotify, &event);
+    LastEvent (display, MapNotify, &event);
+    LastEvent (display, UnmapNotify, &event);
+
+    /* remove those events we will ignore */
+    while (XCheckMaskEvent (display, IgnoreMask, &event)) continue;
+
+    /* events to remove which have no mask component */
+    while (XCheckTypedEvent (display, MappingNotify, &event)) continue;
+    while (XCheckTypedEvent (display, ClientMessage, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionClear, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionNotify, &event)) continue;
+    while (XCheckTypedEvent (display, SelectionRequest, &event)) continue;
+  }
+  return (status);
+}
+
+# if (0)
+
+/* all masks from X.h for reference: */
+
+#define NoEventMask                     0L
+#define KeyPressMask                    (1L<<0)
+#define KeyReleaseMask                  (1L<<1)
+#define ButtonPressMask                 (1L<<2)
+#define ButtonReleaseMask               (1L<<3)
+#define EnterWindowMask                 (1L<<4)
+#define LeaveWindowMask                 (1L<<5)
+#define PointerMotionMask               (1L<<6)
+#define PointerMotionHintMask           (1L<<7)
+#define Button1MotionMask               (1L<<8)
+#define Button2MotionMask               (1L<<9)
+#define Button3MotionMask               (1L<<10)
+#define Button4MotionMask               (1L<<11)
+#define Button5MotionMask               (1L<<12)
+#define ButtonMotionMask                (1L<<13)
+#define KeymapStateMask                 (1L<<14)
+#define ExposureMask                    (1L<<15)
+#define VisibilityChangeMask            (1L<<16)
+#define StructureNotifyMask             (1L<<17)
+#define ResizeRedirectMask              (1L<<18)
+#define SubstructureNotifyMask          (1L<<19)
+#define SubstructureRedirectMask        (1L<<20)
+#define FocusChangeMask                 (1L<<21)
+#define PropertyChangeMask              (1L<<22)
+#define ColormapChangeMask              (1L<<23)
+#define OwnerGrabButtonMask             (1L<<24)
+
+/* all events from X.h for reference: */
+
+#define KeyPress                2
+#define KeyRelease              3
+#define ButtonPress             4
+#define ButtonRelease           5
+#define MotionNotify            6
+#define EnterNotify             7
+#define LeaveNotify             8
+#define FocusIn                 9
+#define FocusOut                10
+#define KeymapNotify            11
+#define Expose                  12
+#define GraphicsExpose          13
+#define NoExpose                14
+#define VisibilityNotify        15
+#define CreateNotify            16
+#define DestroyNotify           17
+#define UnmapNotify             18
+#define MapNotify               19
+#define MapRequest              20
+#define ReparentNotify          21
+#define ConfigureNotify         22
+#define ConfigureRequest        23
+#define GravityNotify           24
+#define ResizeRequest           25
+#define CirculateNotify         26
+#define CirculateRequest        27
+#define PropertyNotify          28
+#define SelectionClear          29
+#define SelectionRequest        30
+#define SelectionNotify         31
+#define ColormapNotify          32
+#define ClientMessage           33
+#define MappingNotify           34
+#define LASTEvent               35      /* must be bigger than any event # */
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/FlushDisplay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/FlushDisplay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/FlushDisplay.c	(revision 17422)
@@ -0,0 +1,37 @@
+# include "Ximage.h"
+
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+void FlushDisplay () {
+
+  struct timeval now;
+  int flush;
+  double dtime;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  if (!USE_XWINDOW) return;
+
+  flush = FALSE;
+  if (!reftimeset) {
+    flush = TRUE;
+    gettimeofday (&reftime, NULL);
+  } 
+
+  gettimeofday (&now, NULL);
+  dtime = DTIME (now, reftime);
+
+  if (dtime > 0.1) {
+    flush = TRUE;
+  }
+
+  if (flush) {
+    XFlush (graphic->display);
+    reftime = now;
+  }
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetColor.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetColor.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetColor.c	(revision 17422)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+/************** GetColor *************/
+unsigned long GetColor (Display *display, char *name, Colormap colormap, unsigned long default_color) {
+
+  int status;
+  XColor rgbcolor, hardwarecolor;
+
+  status = XLookupColor (display, colormap, name, &rgbcolor, &hardwarecolor);
+  if (!status) return (default_color);
+
+  status = XAllocColor (display, colormap, &hardwarecolor);
+  if (!status) return (default_color);
+
+  return (hardwarecolor.pixel);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetPixelCount.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetPixelCount.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/GetPixelCount.c	(revision 17422)
@@ -0,0 +1,12 @@
+# include "Ximage.h"
+
+int GetPixelCount (int sock) {
+  
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  KiiSendMessage (sock, "NPIX: %8d", graphic->Npixels);
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Graphs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Graphs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Graphs.c	(revision 17422)
@@ -0,0 +1,118 @@
+# include "Ximage.h"
+
+/* initialize graph data */
+KapaGraphWidget *InitGraph () {
+
+  int i;
+  KapaGraphWidget *graph;
+
+  ALLOCATE (graph, KapaGraphWidget, 1);
+
+  /* set up axis positions */
+  for (i = 0; i < 4; i++) {
+    graph[0].axis[i].min = 0.0;
+    graph[0].axis[i].max = 1.0;
+    graph[0].axis[i].isaxis = FALSE;
+    graph[0].axis[i].areticks = FALSE;
+    graph[0].axis[i].islabel = FALSE;
+  }    
+  for (i = 0; i < 8; i++) {
+    strcpy (graph[0].label[i].text, "");
+  }
+
+  graph[0].data.xmin = 0.0;
+  graph[0].data.xmax = 1.0;
+  graph[0].data.ymin = 0.0;
+  graph[0].data.ymax = 1.0;
+
+  graph[0].data.style 	= 2; 		// points
+  graph[0].data.ptype 	= 2;		// + for points
+  graph[0].data.ltype 	= 0;		// solid line
+  graph[0].data.etype 	= 0;		// no error bars
+  graph[0].data.ebar  	= 0;		// no cross bar
+  graph[0].data.color 	= 0;		// black
+  graph[0].data.lweight = 0.5;		// line weight of 0.5
+  graph[0].data.size    = 1.0;		// point size of 1.0
+
+  graph[0].Nobjects = 0;
+  graph[0].Ntextline = 0;
+
+  ALLOCATE (graph[0].objects, Gobjects, 1);  /* allocate so later free will not crash! */
+  ALLOCATE (graph[0].textline, Label, 1);    /* allocate so later free will not crash! */
+
+  graph[0].objects[0].x   = graph[0].objects[0].y   = graph[0].objects[0].z = NULL;
+  graph[0].objects[0].dxm = graph[0].objects[0].dxp = NULL;
+  graph[0].objects[0].dym = graph[0].objects[0].dyp = NULL;
+
+  return (graph);
+}
+
+void DrawGraph (KapaGraphWidget *graph) {
+  if (graph == NULL) return;
+  DrawFrame    (graph);
+  DrawObjects  (graph);
+  DrawLabels   (graph);
+  DrawTextlines(graph);
+}
+
+/* remove objects */
+void EraseGraph (KapaGraphWidget *graph) {
+
+  int i;
+
+  if (graph == NULL) return;
+
+  /* free data objects, then re-alloc those needed */
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    FREE (graph[0].objects[i].x);
+    FREE (graph[0].objects[i].y);
+    FREE (graph[0].objects[i].z);
+    FREE (graph[0].objects[i].dxm);
+    FREE (graph[0].objects[i].dxp);
+    FREE (graph[0].objects[i].dym);
+    FREE (graph[0].objects[i].dyp);
+  }
+    
+  /* reset axes and labels */
+  for (i = 0; i < 4; i++) {
+    graph[0].axis[i].isaxis = FALSE;
+    graph[0].axis[i].islabel = FALSE;
+    graph[0].axis[i].areticks = FALSE;
+  }
+  for (i = 0; i < 8; i++) {
+    strcpy (graph[0].label[i].text, "");
+  }
+    
+  graph[0].Nobjects = 0;
+  graph[0].Ntextline = 0;
+  REALLOCATE (graph[0].objects, Gobjects, 1);
+  REALLOCATE (graph[0].textline, Label, 1);
+
+  graph[0].objects[0].x   = graph[0].objects[0].y   = graph[0].objects[0].z = NULL;
+  graph[0].objects[0].dxm = graph[0].objects[0].dxp = NULL;
+  graph[0].objects[0].dym = graph[0].objects[0].dyp = NULL;
+}
+
+/* remove objects */
+void FreeGraph (KapaGraphWidget *graph) {
+
+  int i;
+
+  if (graph == NULL) return;
+
+  /* free data objects, then re-alloc those needed */
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    FREE (graph[0].objects[i].x);
+    FREE (graph[0].objects[i].y);
+    FREE (graph[0].objects[i].z);
+    FREE (graph[0].objects[i].dxm);
+    FREE (graph[0].objects[i].dxp);
+    FREE (graph[0].objects[i].dym);
+    FREE (graph[0].objects[i].dyp);
+  }
+    
+  FREE (graph[0].objects);
+  FREE (graph[0].textline);
+  free (graph);
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Image.c	(revision 17422)
@@ -0,0 +1,214 @@
+# include "Ximage.h"
+# include "buttons.h"
+
+int InitImageChannel (KapaImageChannel *channel) {
+
+  /** set up a bunch of default things **/
+  channel->zero = 0;
+  channel->range = 1;
+  channel->start = 0;
+  channel->slope = 1;
+
+  channel->coords.Npolyterms = 0;
+  channel->matrix.size = 0; /* a flag to show there is no data in the matrix */
+  ALLOCATE (channel->matrix.buffer, char, 1);  /* allocate so later free will not crash! */
+
+  return (TRUE);
+}
+
+/* initialization for things not specific to X */
+KapaImageWidget *InitImageWidget () {
+
+  int i;
+  KapaImageWidget *image;
+  Graphic *graphic;
+
+  graphic = GetGraphic ();
+
+  ALLOCATE (image, KapaImageWidget, 1);
+  memset (image, 0, sizeof(KapaImageWidget));
+
+  for (i = 0; i < NCHANNELS; i++) {
+    InitImageChannel (&image[0].channel[i]);
+  }
+  image[0].image = &image[0].channel[0];
+
+  image[0].nPixels = 0;
+  ALLOCATE (image[0].pixmap, unsigned short, 1);  /* allocate so later free will not crash! */
+
+  // XXXX this has been moved to graphic, which may be wrong
+  // image[0].ColorScaleMode = KAPA_SCALE_1D;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    image[0].overlay[i].Nobjects = 0;
+    ALLOCATE (image[0].overlay[i].objects, KiiOverlay, 1);  /* allocate so later free will not crash! */
+    image[0].overlay[i].active = FALSE;
+    image[0].overlay[i].color = graphic[0].overlay_color[i];
+  }
+
+  // set the center and expansion for the pictures:
+  image[0].picture.X      = 0.0;
+  image[0].picture.Y      = 0.0;
+  image[0].picture.expand = 1;
+  image[0].picture.flipx  = FALSE;
+  image[0].picture.flipy  = FALSE;
+
+  image[0].zoom.X      	  = 0.0;
+  image[0].zoom.Y      	  = 0.0;
+  image[0].zoom.expand 	  = +5;
+  image[0].zoom.flipx  	  = FALSE;
+  image[0].zoom.flipy  	  = FALSE;
+
+  image[0].wide.X      	  = 0.0;
+  image[0].wide.Y      	  = 0.0;
+  image[0].wide.expand 	  = -5;
+  image[0].wide.flipx  	  = FALSE;
+  image[0].wide.flipy  	  = FALSE;
+
+  image[0].location = 4;
+
+  image[0].MovePointer = TRUE;
+  image[0].DecimalDegrees  = TRUE;
+  ALLOCATE (image[0].picture.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (image[0].cmapbar.data, char, 1);   /* allocate so later free will not crash! */
+  ALLOCATE (image[0].zoom.data, char, 1);      /* allocate so later free will not crash! */
+  ALLOCATE (image[0].wide.data, char, 1);      /* allocate so later free will not crash! */
+
+  InitButtonSize (&image[0].PS_button, PS_width, PS_height, PS_bits);
+  InitButtonFunc (&image[0].PS_button, PSfunction);
+
+  InitButtonSize (&image[0].grey_button, grey_width, grey_height, grey_bits);
+  InitButtonFunc (&image[0].grey_button, greycolors);
+
+  InitButtonSize (&image[0].rainbow_button, rainbow_width, rainbow_height, rainbow_bits);
+  InitButtonFunc (&image[0].rainbow_button, rainbow);
+
+  InitButtonSize (&image[0].heat_button, heat_width, heat_height, heat_bits);
+  InitButtonFunc (&image[0].heat_button, heat);
+
+  InitButtonSize (&image[0].recenter_button, recenter_width, recenter_height, recenter_bits);
+  image[0].recenter_button.function_1 = Recenter;
+  image[0].recenter_button.function_2 = RecenterRescale;
+  image[0].recenter_button.function_3 = Rescale;
+
+  InitButtonSize (&image[0].overlay_button[0], red_width, red_height, red_bits);
+  InitButtonFunc (&image[0].overlay_button[0], Overlay0);
+
+  InitButtonSize (&image[0].overlay_button[1], green_width, green_height, green_bits);
+  InitButtonFunc (&image[0].overlay_button[1], Overlay1);
+
+  InitButtonSize (&image[0].overlay_button[2], blue_width, blue_height, blue_bits);
+  InitButtonFunc (&image[0].overlay_button[2], Overlay2);
+
+  InitButtonSize (&image[0].overlay_button[3], yellow_width, yellow_height, yellow_bits);
+  InitButtonFunc (&image[0].overlay_button[3], Overlay3);
+
+  InitButtonSize (&image[0].hms_button, hms_width, hms_height, hms_bits);
+  InitButtonFunc (&image[0].hms_button, ToggleDEG);
+
+  InitButtonSize (&image[0].flipx_button, flipx_width, flipx_height, flipx_bits);
+  InitButtonFunc (&image[0].flipx_button, FlipImageX);
+
+  InitButtonSize (&image[0].flipy_button, flipy_width, flipy_height, flipy_bits);
+  InitButtonFunc (&image[0].flipy_button, FlipImageY);
+
+  return (image);
+}
+
+void InitButtonSize (Button *button, int width, int height, char *bitmap) {
+  button->dx = BUTTON_WIDTH;
+  button->dy = BUTTON_HEIGHT;
+  button->width = width;
+  button->height = height;
+  button->bitmap = bitmap;
+}
+
+void InitButtonFunc (Button *button, int (*function)()) {
+  button->function_1 = function;
+  button->function_2 = function;
+  button->function_3 = function;
+}
+
+void DrawImage (KapaImageWidget *image) {
+
+  int i;
+  Graphic *graphic;
+
+  if (image == NULL) return;
+
+  graphic = GetGraphic ();
+
+  XSetForeground (graphic[0].display,  graphic[0].gc, graphic[0].fore);
+  XDrawRectangle (graphic[0].display,  graphic[0].window, graphic[0].gc, 
+		  image[0].picture.x,  image[0].picture.y, 
+		  image[0].picture.dx+1, image[0].picture.dy+1);
+  
+  XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	     image[0].picture.pix, 0, 0, 
+	     image[0].picture.x + 1, image[0].picture.y + 1, 
+	     image[0].picture.dx, image[0].picture.dy);
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      PaintOverlay (graphic, image, i);
+    }
+  }
+
+  if (image[0].location) {
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].cmapbar.pix, 0, 0, 
+	       image[0].cmapbar.x, image[0].cmapbar.y, 
+	       image[0].cmapbar.dx, image[0].cmapbar.dy);
+
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].wide.pix, 0, 0, 
+	       image[0].wide.x, image[0].wide.y, 
+	       image[0].wide.dx, image[0].wide.dy);
+
+    CrossHairs (graphic, &image[0].zoom);
+  
+    /* erase everything below zoom box, then draw */
+    /*
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    image[0].text_x, image[0].text_x, image[0].text_dx, image[0].text_dy); 
+    */    
+    DrawButton (graphic, &image[0].PS_button);
+    DrawButton (graphic, &image[0].recenter_button);
+    DrawButton (graphic, &image[0].grey_button);
+    DrawButton (graphic, &image[0].rainbow_button);
+    DrawButton (graphic, &image[0].heat_button);
+    DrawButton (graphic, &image[0].hms_button);
+
+    DrawButton (graphic, &image[0].flipx_button);
+    DrawButton (graphic, &image[0].flipy_button);
+
+    for (i = 0; i < NOVERLAYS; i++) {
+      DrawButton (graphic, &image[0].overlay_button[i]);
+    }
+    StatusBox (graphic, image);
+  }
+
+  FlushDisplay (graphic[0].display);
+}
+
+void FreeImage (KapaImageWidget *image) {
+
+  int i;
+
+  if (image == NULL) return;
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    free (image[0].overlay[i].objects);
+  }
+  for (i = 0; i < NCHANNELS; i++) {
+    free (image[0].channel[i].matrix.buffer);
+  }
+  free (image[0].pixmap);
+  free (image[0].picture.data);
+  free (image[0].cmapbar.data);
+  free (image[0].zoom.data);
+  free (image[0].wide.data);
+
+  free (image);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/InButton.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/InButton.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/InButton.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "Ximage.h"
+
+int InButton (XButtonEvent *button_event, Button *button) {
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= button[0].x) && (x <= button[0].x + button[0].dx) &&
+	    (y >= button[0].y) && (y <= button[0].y + button[0].dy));
+
+  return (answer);
+
+}
+
+/*** make this a macro? ***/
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/InPicture.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/InPicture.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/InPicture.c	(revision 17422)
@@ -0,0 +1,16 @@
+# include "Ximage.h"
+
+int InPicture (XButtonEvent *button_event, Picture *picture) {
+
+  int answer;
+  int x, y;
+
+  x = button_event[0].x;
+  y = button_event[0].y;
+  
+  answer = ((x >= picture[0].x) && (x <= picture[0].x + picture[0].dx) &&
+	    (y >= picture[0].y) && (y <= picture[0].y + picture[0].dy));
+
+  return (answer);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretKeys.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretKeys.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretKeys.c	(revision 17422)
@@ -0,0 +1,169 @@
+# include "Ximage.h"
+
+int InterpretKeys (Graphic *graphic, XKeyEvent *event) {
+
+  float           *imdata;
+  double 	   X, Y, Z, R, D, offset;
+  int    	   sock, DX, DY, modstate;
+  char   	  *name, string[16];
+  KeySym           keysym;
+  XComposeStatus   composestatus;
+  Section         *section;
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  graph   = section->graph;
+
+  XLookupString (event, string, 9, &keysym, &composestatus);
+  modstate = event[0].state;
+
+  // return graph coords by default, image if graph does not exist
+  // XXX allow user to choose graph or image coords
+  if (ACTIVE_CURSOR) {
+
+    sock = GetActiveSocket ();
+    if (sock == -1) goto skip_cursor;
+
+    name = XKeysymToString (keysym);
+
+    // skip the following keys: 
+    if (name == NULL) goto skip_cursor;
+    if (!strcmp (name, "Shift_L")) goto skip_cursor;
+    if (!strcmp (name, "Shift_R")) goto skip_cursor;
+    if (!strcmp (name, "Control_L")) goto skip_cursor;
+    if (!strcmp (name, "Control_R")) goto skip_cursor;
+    if (!strcmp (name, "Alt_L")) goto skip_cursor;
+    if (!strcmp (name, "Alt_R")) goto skip_cursor;
+    if (!strcmp (name, "Super_L")) goto skip_cursor;
+    if (!strcmp (name, "Super_R")) goto skip_cursor;
+    if (!strcmp (name, "Caps_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Pause")) goto skip_cursor;
+    if (!strcmp (name, "Continue")) goto skip_cursor;
+    if (!strcmp (name, "Num_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Scroll_Lock")) goto skip_cursor;
+    if (!strcmp (name, "Print")) goto skip_cursor;
+    if (!strcmp (name, "(null)")) goto skip_cursor;
+
+    Z = -1;
+
+    if (graph) {
+      X = (event[0].x - graph[0].axis[0].fx)*(graph[0].axis[0].max - graph[0].axis[0].min)/graph[0].axis[0].dfx + graph[0].axis[0].min;
+      Y = (event[0].y - graph[0].axis[1].fy)*(graph[0].axis[1].max - graph[0].axis[1].min)/graph[0].axis[1].dfy + graph[0].axis[1].min;
+      XY_to_RD (&R, &D, X, Y, &graph[0].data.coords);
+    } 
+    if (image && !graph) {
+      if (event[0].x < image[0].picture.x) goto skip_cursor;
+      if (event[0].y < image[0].picture.y) goto skip_cursor;
+      if (event[0].x > image[0].picture.x + image[0].picture.dx) goto skip_cursor;
+      if (event[0].y > image[0].picture.y + image[0].picture.dy) goto skip_cursor;
+      Screen_to_Image (&X, &Y, (double)event[0].x, (double)event[0].y, &image[0].picture);
+      XY_to_RD (&R, &D, X, Y, &image[0].image[0].coords);
+
+      DX = image[0].image[0].matrix.Naxis[0];
+      DY = image[0].image[0].matrix.Naxis[1];
+
+      if (X < 0) goto off_image;
+      if (Y < 0) goto off_image;
+      if (X >= DX) goto off_image;
+      if (Y >= DY) goto off_image;
+      imdata = (float *) image[0].image[0].matrix.buffer;
+      Z      = imdata[DX*(int)(Y) + (int)(X)];
+    }
+  off_image:
+    KiiSendMessage (sock, "%12s %12.6f %12.6f %12.6f %12.6f %12.6f", name, X, Y, Z, R, D);
+  }
+
+skip_cursor:
+  if (image == NULL) return (TRUE);
+
+  // offset is in image pixels: 
+  // 0.5 image pixels is 1 screen pixel for expand == +2
+  // 2.0 image pixels is 1 screen pixel for expand == -2
+  if (image[0].picture.expand == 0) image[0].picture.expand = 1;
+  offset = (image[0].picture.expand > 0) ? 1.0 / image[0].picture.expand : -image[0].picture.expand;
+  if (modstate & ControlMask) offset *= 100;
+
+  switch (keysym) {
+
+    case XK_F1:
+      image[0].image = &image[0].channel[0];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_F2:
+      image[0].image = &image[0].channel[1];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_F3:
+      image[0].image = &image[0].channel[2];
+      SetColorScale (graphic, image);
+      Remap (graphic, image);
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_KP_Home:
+    case XK_Home:
+      image[0].picture.expand = 1;
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+    case XK_KP_End:
+    case XK_End:
+      image[0].picture.expand = 1;
+      Reorient (graphic, image, 0.5*image[0].image[0].matrix.Naxis[0], 0.5*image[0].image[0].matrix.Naxis[1], 0);
+      break;
+    case XK_KP_Enter:
+    case XK_KP_Begin:
+    case XK_Return:
+      Screen_to_Image (&X, &Y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+      Reorient (graphic, image, X, Y, 0);
+      break;
+    case XK_Prior:
+    case XK_KP_Prior:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, +1);
+      break;
+    case XK_Next:
+    case XK_KP_Next:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, -1);
+      break;
+    case XK_Up:
+    case XK_KP_Up:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y + offset, 0);
+      break;
+    case XK_Down:
+    case XK_KP_Down:
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y - offset, 0);
+      break;
+    case XK_Left:
+    case XK_KP_Left:
+      Reorient (graphic, image, image[0].picture.X + offset, image[0].picture.Y, 0);
+      break;
+    case XK_Right:
+    case XK_KP_Right:
+      Reorient (graphic, image, image[0].picture.X - offset, image[0].picture.Y, 0);
+      break;
+
+    case XK_KP_Add:
+      image[0].image[0].zero += 0.1*image[0].image[0].range;
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+    case XK_KP_Subtract:
+      image[0].image[0].zero -= 0.1*image[0].image[0].range;
+      Reorient (graphic, image, image[0].picture.X, image[0].picture.Y, 0);
+      break;
+
+    case XK_Tab:
+      image[0].MovePointer = image[0].MovePointer ^ TRUE;
+      if (image[0].MovePointer) UpdatePointer (graphic, (XMotionEvent *)event);
+      break;
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretPresses.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretPresses.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/InterpretPresses.c	(revision 17422)
@@ -0,0 +1,118 @@
+# include "Ximage.h"
+
+int InterpretPresses (Graphic *graphic, XButtonEvent *event) {
+
+  int              sock, DX, DY, status, done, this_button;
+  char             name[16];
+  double           X, Y, Z, R, D;
+  float           *imdata;
+  Button          *button;
+  Section         *section;
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  graph   = section->graph;
+
+  // return graph coords by default, image if graph does not exist
+  // XXX allow user to choose graph or image coords
+  if (ACTIVE_CURSOR) {
+    sock = GetActiveSocket ();
+    if (sock == -1) goto skip_cursor;
+
+    sprintf (name, "Button%d", event[0].button);
+    if (graph) {
+      X = (event[0].x - graph[0].axis[0].fx)*(graph[0].axis[0].max - graph[0].axis[0].min)/graph[0].axis[0].dfx + graph[0].axis[0].min;
+      Y = (event[0].y - graph[0].axis[1].fy)*(graph[0].axis[1].max - graph[0].axis[1].min)/graph[0].axis[1].dfy + graph[0].axis[1].min;
+      XY_to_RD (&R, &D, X, Y, &graph[0].data.coords);
+    } 
+    if (image && !graph) {
+      if (event[0].x < image[0].picture.x) goto skip_cursor;
+      if (event[0].y < image[0].picture.y) goto skip_cursor;
+      if (event[0].x > image[0].picture.x + image[0].picture.dx) goto skip_cursor;
+      if (event[0].y > image[0].picture.y + image[0].picture.dy) goto skip_cursor;
+      Screen_to_Image (&X, &Y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+
+      XY_to_RD (&R, &D, X, Y, &image[0].image[0].coords);
+
+      DX = image[0].image[0].matrix.Naxis[0];
+      DY = image[0].image[0].matrix.Naxis[1];
+
+      if (X < 0) goto off_image;
+      if (Y < 0) goto off_image;
+      if (X >= DX) goto off_image;
+      if (Y >= DY) goto off_image;
+      imdata = (float *) image[0].image[0].matrix.buffer;
+      Z      = imdata[DX*(int)(Y) + (int)(X)];
+    }
+  off_image:
+    KiiSendMessage (sock, "%12s %12.6f %12.6f %12.6f %12.6f %12.6f", name, X, Y, Z, R, D);
+  }
+
+skip_cursor:
+  status = TRUE;
+  this_button = event[0].button;
+
+  // XXX add graph buttons here
+  if (image == NULL) return (TRUE);
+  
+  if ((event[0].type == ButtonPress) && InPicture (event, &image[0].picture)) {
+    ReorientOnButton (graphic, image, event);
+  }
+
+  if ((event[0].type == ButtonPress) && InPicture (event, &image[0].cmapbar)) {
+    DragColorbar (graphic, image, event);
+  }
+
+  /* if on an exisiting button, Invert, wait for release, then go (or not) */
+  if ((button = CheckButtons (event, image)) != (Button *) NULL) {
+    InvertButton (graphic, button); 
+    done = FALSE;
+    while (!done) { /* wait for release of this button */
+      XNextEvent (graphic[0].display, (XEvent *) event);
+      if ((event[0].type == ButtonRelease) && (event[0].button == this_button)) {
+	done = TRUE;
+      }
+    }
+    DrawButton (graphic, button);
+    if (InButton (event, button)) {
+      switch (event[0].button) {
+      case 1:
+	status = button[0].function_1(graphic, image);
+	break;
+      case 2:
+	status = button[0].function_2(graphic, image);
+	break;
+      case 3:
+	status = button[0].function_3(graphic, image);
+	break;
+      }
+    } else {
+      return (status);
+    }
+  }
+  return (status);
+}
+
+void ReorientOnButton (Graphic *graphic, KapaImageWidget *image, XButtonEvent *mouse_event) {
+
+  double X, Y;
+
+  Screen_to_Image (&X, &Y, mouse_event[0].x + 0.5, mouse_event[0].y + 0.5, &image[0].picture);
+
+  switch (mouse_event[0].button) {
+    case 1:
+      Reorient (graphic, image, X, Y, 0);
+      break;
+    case 2:
+      Reorient (graphic, image, X, Y, -1);
+      break;
+    case 3:
+      Reorient (graphic, image, X, Y, +1);
+      break;
+    default:
+      break;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/InvertButton.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/InvertButton.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/InvertButton.c	(revision 17422)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+void InvertButton (Graphic *graphic, Button *button) {
+
+  unsigned long fore, back;
+
+  fore =  graphic[0].fore;
+  back =  graphic[0].back;
+
+  graphic[0].fore = back;
+  graphic[0].back = fore;
+
+  DrawButton (graphic, button);
+  FlushDisplay (graphic[0].display);
+  
+  graphic[0].fore = fore;
+  graphic[0].back = back;
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/JPEGit24.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/JPEGit24.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/JPEGit24.c	(revision 17422)
@@ -0,0 +1,216 @@
+# include "Ximage.h"
+# include "jpeglib.h"
+
+# define WHITE_R 255
+# define WHITE_G 255
+# define WHITE_B 255
+
+// XXX this currently writes out the jpeg for just the active image
+int JPEGit24 (int sock) {
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+  JSAMPLE *line_buffer;	        /* Points to data for current line */
+  Section *section;
+  Graphic *graphic;
+  KapaImageWidget *image;
+
+  int ii, i, j;
+  int i_start, i_end, j_start, j_end;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY;
+  int quality;
+  int expand_in, expand_out;
+  double expand, Ix, Iy;
+  unsigned char *out_pix;
+  unsigned short *in_pix, *in_pix_ref;
+  unsigned char *pixel1, *pixel2, *pixel3;
+  char filename[1024];
+  FILE *f;
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (sock, "%s", filename);
+
+  graphic = GetGraphic();
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  /***** JPEG init calls */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "Kii: failed to open %s for output\n", filename);
+    return (TRUE);
+  }
+  jpeg_stdio_dest(&cinfo, f);
+  
+  quality = 75;
+  cinfo.image_width = image[0].picture.dx; 	/* image width and height, in pixels */
+  cinfo.image_height = image[0].picture.dy;
+# ifdef GREYSCALE
+  cinfo.input_components = 1;		        /* # of color components per pixel */
+  cinfo.in_color_space = JCS_GRAYSCALE; 	/* colorspace of input image */
+# else 
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+# endif
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, quality, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);
+
+  /** cmap[i].pixel must be defined even if X is not used **/
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel1[i] = graphic[0].cmap[i].red >> 8;
+    pixel2[i] = graphic[0].cmap[i].green >> 8;
+    pixel3[i] = graphic[0].cmap[i].blue >> 8;
+  }
+
+  assert ((image[0].picture.expand >= 1) || (image[0].picture.expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+    expand_out = image[0].picture.expand;
+    expand_in  = 1;
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+    expand_out = 1;
+    expand_in  = -image[0].picture.expand;
+  }
+
+  dx = image[0].picture.dx;
+  dy = image[0].picture.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, &image[0].image[0].matrix, &image[0].picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, &image[0].image[0].matrix, &image[0].picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, &image[0].picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  /* output line buffer */
+  ALLOCATE (image_buffer, JSAMPLE, 3*dx*dy);
+  ALLOCATE (line_buffer, JSAMPLE, 3*dx);
+
+  in_pix_ref  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) {
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = 0; j < j_start; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+  
+  /*** fill in the image data region ***/
+  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += expand_in*DX) {
+    
+    /* create one output image line */
+    in_pix = in_pix_ref;
+    out_pix = line_buffer;
+
+    /**** fill in area to the left of the picture ****/
+    for (i = 0; i < i_start; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+    
+    /*** fill in the picture region ***/
+    for (i = i_start; i < i_end; i+=expand_out, in_pix+=expand_in) {
+      for (ii = 0; ii < expand_out; ii++, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix];
+	out_pix[1] = pixel2[*in_pix];
+	out_pix[2] = pixel3[*in_pix];
+      }
+    }
+    
+    /**** fill in area to the right of the picture ****/
+    for (i = i_end; i < dx; i++, out_pix+=3) {
+      out_pix[0] = WHITE_R;
+      out_pix[1] = WHITE_G;
+      out_pix[2] = WHITE_B;
+    }
+
+    /* write out the image line expand_out times */
+    for (i = 0; i < expand_out; i++) {
+      memcpy (&image_buffer[(j + i)*3*dx], line_buffer, 3*dx);
+    }
+  }
+
+  /**** fill in top area ****/
+  out_pix = line_buffer;
+  for (i = 0; i < dx; i++, out_pix+=3) { 
+    out_pix[0] = WHITE_R;
+    out_pix[1] = WHITE_G;
+    out_pix[2] = WHITE_B;
+  }
+  for (j = j_end; j < dy; j++) {
+    memcpy (&image_buffer[j*3*dx], line_buffer, 3*dx);
+  }
+
+
+  /* I need to write the overlay objects on the jpeg image.
+     if i can write / overwrite data in jpeg buffer, then do it here,
+     otherwise i need to create a temporary image buffer, then write the 
+     scanlines to that buffer */
+
+  {
+    int Npalette;
+    png_color *palette;
+    bDrawColor white, color;
+    bDrawBuffer *buffer;
+
+    palette = KapaPNGPalette (&Npalette);
+
+    buffer = bDrawBufferCreate (dx, dy);
+    bDrawSetBuffer (buffer);
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (image[i].overlay[i].active) bDrawOverlay (image, i);
+    }
+
+    white = KapaColorByName ("white");
+    for (j = 0; j < dy; j++) {
+      for (i = 0; i < dx; i++) {
+	color = buffer[0].pixels[j][i];
+	if (color == white) continue;
+	image_buffer[j*3*dx + 3*i + 0] = palette[color].red;
+	image_buffer[j*3*dx + 3*i + 1] = palette[color].green;
+	image_buffer[j*3*dx + 3*i + 2] = palette[color].blue;
+      }
+    }
+    bDrawBufferFree (buffer);
+  }
+
+  for (i = 0; i < dy; i++) {
+    row_pointer[0] = &image_buffer[i*3*dx];
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Layout.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Layout.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Layout.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+static char default_colormap[] = "grayscale";
+
+void InitLayout (int argc, char **argv) {
+
+  int N;
+  Section *section;
+  char *namedSocket = NULL;
+  
+  namedSocket = NULL;
+  if ((N = get_argument (argc, argv, "-socket"))) { 
+    remove_argument (N, &argc, argv);
+    namedSocket = argv[N];
+    remove_argument (N, &argc, argv);
+  }
+
+  // if we specify a named socket, wait until we are contacted
+  // otherwise, open an INET sock and check occasionally for requests
+  InitPipe (namedSocket);
+
+  ACTIVE_CURSOR = FALSE;
+
+  /* get the display colors */
+  if (USE_XWINDOW) {
+    MakeColormap (argc, argv);
+  } else {
+    char *colormap;
+    colormap = default_colormap;
+    if ((N = get_argument (argc, argv, "-cm"))) {
+      remove_argument (N, &argc, argv);
+      colormap = argv[N];
+    }
+    SetColormap (colormap);
+  }
+
+  /* move this out of here... */
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: kapa\n");
+    exit (0);
+  }
+
+  InitRotFonts ();
+
+  /* create basic section, empty of image or graph */
+  section = AddSection ("default", 0.0, 0.0, 1.0, 1.0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFont.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFont.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFont.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "Ximage.h"
+
+/************** LoadFont *************/
+void LoadFont (Graphic *graphic, int *argc, char **argv, char *default_name) {
+
+  int N;
+  char *name;
+
+  name = XGetDefault (graphic->display, argv[0], "Font");
+  if (name == NULL) name = default_name;
+
+  /* check for command-line options */
+  if ((N = get_argument (*argc, argv, "-font"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -font fontname\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = argv[N];
+    remove_argument(N, argc, argv);
+  }   
+  if ((N = get_argument (*argc, argv, "-fn"))) {
+    if (*argc <= N + 1) {
+      fprintf (stderr, "error: usage is -fn fontname\n");
+      exit (0);
+    }
+    remove_argument(N, argc, argv);
+    name = argv[N];
+    remove_argument(N, argc, argv);
+  } 
+
+  graphic->font = XLoadQueryFont (graphic->display, name);
+  if ((graphic->font == NULL) && (name != default_name)) {
+    fprintf (stderr, "Could not load requested font %s, trying %s\n", name, default_name);
+    graphic->font = XLoadQueryFont (graphic->display, default_name);
+  }
+  if (graphic->font == NULL) {
+    fprintf (stderr, "Could not load font %s, using %s\n", default_name, "fixed");
+    graphic->font = XLoadQueryFont (graphic->display, "fixed");
+  }
+  if (graphic->font == NULL) {
+    QuitX (graphic->display, "Error: could not load font");
+  }
+
+  XSetFont (graphic->display, graphic->gc, graphic->font[0].fid);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFrame.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFrame.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadFrame.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "Ximage.h"
+
+int LoadFrame (int sock) {
+  
+  int i;
+  char Axis[16], Labels[16], Ticks[16];
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].axis[0].min, &graph[0].axis[0].max, 
+		  &graph[0].axis[1].min, &graph[0].axis[1].max);
+
+  graph[0].axis[3].min = graph[0].axis[1].min;
+  graph[0].axis[3].max = graph[0].axis[1].max;
+  graph[0].axis[2].min = graph[0].axis[0].min;
+  graph[0].axis[2].max = graph[0].axis[0].max;
+  
+  KiiScanMessage (sock, "%s %s %s", Axis, Labels, Ticks);
+
+  for (i = 0; i < 4; i++) {
+    switch (Axis[i]) {
+    case '0':
+      graph[0].axis[i].isaxis = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].isaxis = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].isaxis = TRUE;
+      break;
+    }
+    switch (Ticks[i]) {
+    case '0':
+      graph[0].axis[i].areticks = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].areticks = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].areticks = 2;
+      break;
+    }
+    switch (Labels[i]) {
+    case '0':
+      graph[0].axis[i].islabel = FALSE;
+      break;
+    case '1':
+      graph[0].axis[i].islabel = TRUE;
+      break;
+    case '2':
+      graph[0].axis[i].islabel = (i < 2);
+      break;
+    }
+  }
+
+  SetSectionSizes (section);
+  if (USE_XWINDOW) DrawFrame (graph);
+  FlushDisplay ();
+
+  /* XXX why did I do this??? */
+# if (0)
+  status = TRUE;
+  if (status) {
+    for (i = 0; i < Nsection; i++) {
+      PositionPicture (&section[i]);
+    }
+    if (USE_XWINDOW) XClearWindow (graphic.display, graphic.window);
+    Refresh (1);
+  } else {
+    FlushDisplay ();
+  } 
+# endif
+
+  return (TRUE);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadLabels.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadLabels.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadLabels.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "Ximage.h"
+
+int LoadLabels (int sock) {
+  
+  char *c, *label;
+  int mode, size, Nbytes;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  KiiScanMessage (sock, "%d", &mode);
+  label = KiiRecvData (sock);
+
+  bzero (graph[0].label[mode].text, LABEL_MAXLEN);
+
+  Nbytes = MIN (strlen(label), LABEL_MAXLEN - 1);
+  strncpy (graph[0].label[mode].text, label, Nbytes);
+  label[Nbytes] = 0;
+  
+  c = GetRotFont (&size);
+  graph[0].label[mode].size = size;
+  strcpy (graph[0].label[mode].font, c);
+  if (USE_XWINDOW) DrawLabels (graph);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadObject.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadObject.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadObject.c	(revision 17422)
@@ -0,0 +1,231 @@
+# include "Ximage.h"
+# include <errno.h>
+# define DEBUG 0
+
+int LoadObject (int sock) {
+  
+  int N;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  N = graph[0].Nobjects;
+  graph[0].Nobjects ++;
+  REALLOCATE (graph[0].objects, Gobjects, graph[0].Nobjects);
+  graph[0].objects[N].x = graph[0].objects[N].y = graph[0].objects[N].z = (float *) NULL;
+  graph[0].objects[N].dxm = graph[0].objects[N].dxp = (float *) NULL;
+  graph[0].objects[N].dym = graph[0].objects[N].dyp = (float *) NULL;
+  
+  KiiScanMessage (sock, "%d %d %d %d %d %d %d %lf %lf",
+		  &graph[0].objects[N].Npts, &graph[0].objects[N].style, 
+		  &graph[0].objects[N].ptype, &graph[0].objects[N].ltype, 
+		  &graph[0].objects[N].etype, &graph[0].objects[N].ebar, 
+		  &graph[0].objects[N].color, 
+		  &graph[0].objects[N].lweight, &graph[0].objects[N].size);
+  
+  if (DEBUG) fprintf (stderr, "%d %d %d %d %d %d %d %lf %lf\n",
+		      graph[0].objects[N].Npts, graph[0].objects[N].style, 
+		      graph[0].objects[N].ptype, graph[0].objects[N].ltype, 
+		      graph[0].objects[N].etype, graph[0].objects[N].ebar, 
+		      graph[0].objects[N].color, 
+		      graph[0].objects[N].lweight, graph[0].objects[N].size);
+  
+  /* force valid ranges */
+  if ((graph[0].objects[N].color > KapaColormapSize()) || (graph[0].objects[N].color < 0))
+    graph[0].objects[N].color = 0;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf",
+		  &graph[0].objects[N].x0, &graph[0].objects[N].x1, 
+		  &graph[0].objects[N].y0, &graph[0].objects[N].y1);
+
+  // acknowledge receipt of the metadata
+  KiiSendCommand (sock, 4, "DONE"); 
+  
+  // XXX Currently, I require these in a special order.  The data includes a message defining the
+  // object type.  This could be made more flexible by using the information (though we still need
+  // to know how many items will be sent)
+
+  if (!LoadVectorData (sock, graph, N, "x")) {
+    FreeObjectData (&graph[0].objects[N]);
+    graph[0].Nobjects --;
+    REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+  }
+    
+  if (!LoadVectorData (sock, graph, N, "y")) {
+    FreeObjectData (&graph[0].objects[N]);
+    graph[0].Nobjects --;
+    REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+  }
+  if (graph[0].objects[N].size < 0.0) {
+    if (!LoadVectorData (sock, graph, N, "z")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+  if (graph[0].objects[N].etype & 0x01) {
+    if (!LoadVectorData (sock, graph, N, "dym")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+    if (!LoadVectorData (sock, graph, N, "dyp")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+  if (graph[0].objects[N].etype & 0x02) {
+    if (!LoadVectorData (sock, graph, N, "dxm")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+    if (!LoadVectorData (sock, graph, N, "dxp")) {
+      FreeObjectData (&graph[0].objects[N]);
+      graph[0].Nobjects --;
+      REALLOCATE (graph[0].objects, Gobjects, MAX (1, graph[0].Nobjects));
+    }
+  }
+
+  if (DEBUG) fprintf (stderr, "loaded %d objects, using object %d\n", graph[0].objects[N].Npts, N);
+
+  if (USE_XWINDOW) DrawObjectN (graph, &graph[0].objects[graph[0].Nobjects-1]);
+  FlushDisplay ();
+
+  return (TRUE);
+  
+}
+
+int LoadVectorData (int sock, KapaGraphWidget *graph, int N, char *type) {
+  
+  int i, Npts, Ninpts, status, Ntry;
+  int bytes_left;
+  char *byte, *buffer, type_send[16], tmp;
+  int Npts_send, Nbytes_send, swap_client, swap_host;
+
+  buffer = NULL;
+  Npts = graph[0].objects[N].Npts;
+
+  KiiWaitAnswer (sock, "PLOB");
+  KiiScanMessage (sock, "%s %d %d %d", type_send, &Npts_send, &Nbytes_send, &swap_client);
+  if (strcmp (type, type_send)) {
+    fprintf (stderr, "Kapa Communication error: unexpected data type %s vs %s\n", type_send, type);
+  }
+  if (Npts_send != Npts) {
+    fprintf (stderr, "Kapa Communication error: unexpected number of points %d vs %d\n", Npts_send, Npts);
+  }
+  if (Nbytes_send != Npts_send*sizeof(float)) {
+    fprintf (stderr, "Kapa Communication error: unexpected data size %d vs %ld\n", Nbytes_send, Npts_send*sizeof(float));
+  }
+
+  status = 1;
+  if (!strcmp (type, "x")) {
+    ALLOCATE (graph[0].objects[N].x, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].x;
+  }
+  if (!strcmp (type, "y")) {
+    ALLOCATE (graph[0].objects[N].y, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].y;
+  }
+  if (!strcmp (type, "z")) {
+    ALLOCATE (graph[0].objects[N].z, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].z;
+  }
+  if (!strcmp (type, "dxm")) {
+    ALLOCATE (graph[0].objects[N].dxm, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dxm;
+  }
+  if (!strcmp (type, "dxp")) {
+    ALLOCATE (graph[0].objects[N].dxp, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dxp;
+  }
+  if (!strcmp (type, "dym")) {
+    ALLOCATE (graph[0].objects[N].dym, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dym;
+  }
+  if (!strcmp (type, "dyp")) {
+    ALLOCATE (graph[0].objects[N].dyp, float, MAX (1, Npts));
+    buffer = (char *) graph[0].objects[N].dyp;
+  }
+
+  bytes_left = Npts*sizeof (float);
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+
+  // read the vector data as raw binary in client machine byte order (floats)
+  Ntry = 0;
+  if (DEBUG) fprintf (stderr, "starting vector load\n");
+  Ninpts = 0;
+  byte = buffer;
+  while (bytes_left > 0) {
+    status = read (sock, byte, bytes_left);
+    if (DEBUG) fprintf (stderr, "status: %d, %d\n", status, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      Ninpts += status;
+      bytes_left -= status;
+      byte = (char *)(byte + status);
+      Ntry = 0;
+      continue;
+    }
+    if (errno == EAGAIN) {
+      Ntry ++;
+      if (Ntry > 100) {
+	fprintf (stderr, "kapa communication error\n");
+	return (FALSE);
+      }
+      usleep (10000);
+      continue;
+    }
+    perror ("kapa load");
+  }
+
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock, 4, "DONE"); 
+
+# ifdef BYTE_SWAP
+  swap_host = 1;
+# else 
+  swap_host = 0;
+# endif  
+
+  // if host and client have opposite swap parities, word swap the incoming data
+  // SWAP_WORD is strangely defined... it takes the number of the start byte in a
+  // buffer called 'byte'
+  if ((swap_host && !swap_client) || (!swap_host && swap_client)) {
+    byte = buffer;
+    for (i = 0; i < Nbytes_send; i+=4) {
+      SWAP_WORD (i);
+    }
+  }
+
+  if (Ninpts != Npts*sizeof(float)) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", Ninpts, (unsigned int)(Npts*sizeof(float)));
+    return (FALSE);
+  }
+  if (DEBUG) fprintf (stderr, "done vector load\n");
+  return (TRUE);
+
+}
+
+void FreeObjectData (Gobjects *object) {
+
+  if (object[0].x != (float *) NULL) free (object[0].x);
+  if (object[0].y != (float *) NULL) free (object[0].y);
+  if (object[0].z != (float *) NULL) free (object[0].z);
+
+  if (object[0].dxm != (float *) NULL) free (object[0].dxm);
+  if (object[0].dxp != (float *) NULL) free (object[0].dxp);
+  if (object[0].dym != (float *) NULL) free (object[0].dym);
+  if (object[0].dyp != (float *) NULL) free (object[0].dyp);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadOverlay.c	(revision 17422)
@@ -0,0 +1,128 @@
+# include "Ximage.h"
+
+int LoadOverlay (int sock) {
+  
+  int i, j, Ntotal, Nbytes, Nread, Nfound, Ntext, Nobjects, overnum;
+  int Noverlay, Ntextdata;
+  char *textdata, *buffer, *p, *q;
+  Section *section;
+  KapaImageWidget *image;
+  Graphic *graphic;
+  KiiOverlayBase *overlay;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%d %d %d %d", &overnum, &Noverlay, &Ntext, &Ntextdata);
+
+  // XXX need to validate overnum 
+  if ((overnum < 0) || (overnum >= NOVERLAYS)) overnum = 0;
+
+  // read the overlay data as binary 
+  Ntotal = 0;
+  Nbytes = Noverlay*sizeof(KiiOverlayBase);
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  ALLOCATE (overlay, KiiOverlayBase, Noverlay);
+  buffer = (char *) overlay;
+  while (Nbytes > 0) { 
+    Nread = read (sock, &buffer[Ntotal], Nbytes);
+    // fprintf (stderr, "read: %d of %d remaining, %d so far, %d expected\n", Nread, Nbytes, Ntotal, Noverlay*sizeof(KiiOverlayBase));
+    if (Nread == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      free (overlay);
+      fcntl (sock, F_SETFL, !O_NONBLOCK);  
+      return (FALSE);
+    }
+    if (Nread != -1) { /* pipe has data */
+      Nbytes -= Nread;
+      Ntotal += Nread;
+    }
+  }
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock,  4, "DONE");
+
+  // read the textdata as binary
+  Ntotal = 0;
+  Nbytes = Ntextdata;
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+  ALLOCATE (textdata, char, Ntextdata);
+  while (Nbytes > 0) { 
+    Nread = read (sock, &textdata[Ntotal], Nbytes);
+    if (Nread == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      free (textdata);
+      free (overlay);
+      fcntl (sock, F_SETFL, !O_NONBLOCK);  
+      return (FALSE);
+    }
+    if (Nread != -1) { /* pipe has data */
+      Nbytes -= Nread;
+      Ntotal += Nread;
+    }
+  }
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+  KiiSendCommand (sock,  4, "DONE");
+
+  // add new overlay objects to existing data
+  Nobjects = image[0].overlay[overnum].Nobjects + Noverlay;
+  REALLOCATE (image[0].overlay[overnum].objects, KiiOverlay, Nobjects);
+
+  j = image[0].overlay[overnum].Nobjects;
+  for (i = 0; i < Noverlay; i++, j++) {
+    image[0].overlay[overnum].objects[j].x     = overlay[i].x;
+    image[0].overlay[overnum].objects[j].y     = overlay[i].y;
+    image[0].overlay[overnum].objects[j].dx    = overlay[i].dx;
+    image[0].overlay[overnum].objects[j].dy    = overlay[i].dy;
+    image[0].overlay[overnum].objects[j].angle = overlay[i].angle;
+    image[0].overlay[overnum].objects[j].type  = overlay[i].type;
+    image[0].overlay[overnum].objects[j].text  = NULL;
+  }
+
+  // parse the text data : text lines are separated by '\n', one per text entry
+  p = textdata;
+  Nfound = 0;
+  for (i = 0; i < Noverlay; i++) {
+    if (overlay[i].type != KII_OVERLAY_TEXT) continue;
+    if (Nfound >= Ntext) {
+      fprintf (stderr, "inconsistent number of text lines\n");
+      break;
+    }
+    if (! *p) {
+      fprintf (stderr, "inconsistent number of text lines\n");
+      break;
+    }
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      fprintf (stderr, "inconsistent text line\n");
+      break;
+    }
+    j = image[0].overlay[overnum].Nobjects + i;
+    image[0].overlay[overnum].objects[j].text = strncreate (p, q-p);
+    p = q + 1;
+    Nfound ++;
+  }
+  if (Nfound != Ntext) {
+    fprintf (stderr, "read %d text lines, expected %d\n", Nfound, Ntext);
+  }
+
+  free (textdata);
+  free (overlay);
+
+  image[0].overlay[overnum].Nobjects = Nobjects;
+  image[0].overlay[overnum].active = TRUE;
+
+  if (USE_XWINDOW) {
+    for (i = 0; i < NOVERLAYS; i++) {
+      if (image[0].overlay[i].active) {
+	PaintOverlay (graphic, image, i);
+      }
+    }
+    XFlush (graphic[0].display);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadPicture.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadPicture.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadPicture.c	(revision 17422)
@@ -0,0 +1,97 @@
+# include "Ximage.h"
+
+int LoadPicture (int sock) {
+
+  Header header;
+  char *buff;
+  int status, bytes_left;
+  Section *section;
+  KapaImageWidget *image;
+  Graphic *graphic;
+  double Xoffset, Yoffset, wx;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  KiiSendMessage (sock, "%d", graphic->Npixels);
+
+  // when we load a new picture, use the same orientation as the old picture
+  Xoffset = 0.0;
+  Yoffset = 0.0;
+  if (image[0].image[0].matrix.size) {
+    Xoffset = image[0].picture.X - 0.5*image[0].image[0].matrix.Naxis[0];
+    Yoffset = image[0].picture.Y - 0.5*image[0].image[0].matrix.Naxis[1];
+  }
+
+  gfits_init_header (&header);
+  header.Naxes = 2;
+  KiiScanMessage (sock, "%d %d %d %d %lf %lf", &header.Naxis[0], &header.Naxis[1]);
+  KiiScanMessage (sock, "%d %d %d %d %lf %lf", &header.bitpix, &header.unsign, &header.bzero, &header.bscale);
+  KiiScanMessage (sock, "%lf %lf %s %s",  &image[0].image[0].zero, &image[0].image[0].range, image[0].image[0].name, image[0].image[0].file);
+  KiiScanMessage (sock, "%lf %lf %d", &image[0].image[0].min,  &image[0].image[0].max, &header.size);
+  KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval1, &image[0].image[0].coords.crpix1, &image[0].image[0].coords.cdelt1, &image[0].image[0].coords.pc1_1, &image[0].image[0].coords.pc1_2);
+  KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval2, &image[0].image[0].coords.crpix2, &image[0].image[0].coords.cdelt2, &image[0].image[0].coords.pc2_1, &image[0].image[0].coords.pc2_2);
+  KiiScanMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  gfits_free_matrix (&image[0].image[0].matrix);
+  gfits_create_matrix (&header, &image[0].image[0].matrix);
+
+  // reference point for image is the center pixel
+  image[0].picture.X = 0.5*header.Naxis[0] + Xoffset;
+  image[0].picture.Y = 0.5*header.Naxis[1] + Yoffset;
+
+  // choose expand for wide to guarantee we fit:
+  wx = MAX ((header.Naxis[0] / (float) image[0].wide.dx), (header.Naxis[1] / (float) image[0].wide.dy));
+  if (wx > 1.0) {
+    image[0].wide.expand = -wx;
+  } else {
+    image[0].wide.expand = 1.0 / wx;
+  }    
+  image[0].wide.X = 0.5*header.Naxis[0];
+  image[0].wide.Y = 0.5*header.Naxis[1];
+
+  fcntl (sock, F_SETFL, O_NONBLOCK);  
+
+  status = 1;
+  buff = image[0].image[0].matrix.buffer;
+  bytes_left = header.size;
+  image[0].image[0].matrix.size = 0;
+  while (bytes_left > 0) {
+    status = read (sock, buff, bytes_left);
+    if (status == 0) {  /* No more pipe */
+      fprintf (stderr, "error: pipe closed\n");
+      return (FALSE);
+    }
+    if (status != -1) { /* pipe has data */
+      image[0].image[0].matrix.size += status;
+      bytes_left -= status;
+      buff = (char *)(buff + status);
+    }
+  }
+
+  fcntl (sock, F_SETFL, !O_NONBLOCK);  
+
+  if (DEBUG) fprintf (stderr, "read %d bytes\n", image[0].image[0].matrix.size);
+  /* it it not obvious this condition should kill kii, but ... */
+  if (image[0].image[0].matrix.size != header.size) {  
+    fprintf (stderr, "error: expected %d bytes, but got only %d\n", header.size, image[0].image[0].matrix.size);
+    return (FALSE);
+  }
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  SetColorScale (graphic, image);
+  Remap (graphic, image);
+  CreateWide (graphic, image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTextlines.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTextlines.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTextlines.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+
+int LoadTextlines (int sock) {
+  
+  char *string;
+  int N, size;
+  double tX, tY, tT, L;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  graph = section->graph;
+    if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+
+  graph[0].Ntextline = MAX (graph[0].Ntextline, 0);
+  N = graph[0].Ntextline;
+  graph[0].Ntextline++;
+  REALLOCATE (graph[0].textline, Label, graph[0].Ntextline);
+
+  KiiScanMessage (sock, "%lf %lf %lf", &tX, &tY, &tT);
+
+  L = graph[0].axis[0].dfx;
+  graph[0].textline[N].x = L * (tX - graph[0].axis[0].min) / (graph[0].axis[0].max - graph[0].axis[0].min) + graph[0].axis[0].fx;
+
+  L = graph[0].axis[1].dfy;
+  graph[0].textline[N].y = L * (tY - graph[0].axis[1].min) / (graph[0].axis[1].max - graph[0].axis[1].min) + graph[0].axis[1].fy;
+
+  graph[0].textline[N].angle = tT;
+
+  bzero (graph[0].textline[N].text, LABEL_MAXLEN);
+
+  string = KiiRecvData (sock);
+  strcpy (graph[0].textline[N].text, string);
+  free (string);
+  
+  string = GetRotFont (&size);
+  graph[0].textline[N].size = size;
+  strcpy (graph[0].textline[N].font, string);
+
+  if (USE_XWINDOW) DrawTextlines (graph);
+  
+  FlushDisplay ();
+  
+  return (TRUE);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTickmarks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTickmarks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/LoadTickmarks.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+
+int LoadTickmarks (int sock) {
+  
+  char line[129], type[16];
+  double x, y, dx, dy;
+  int status, NOBJECTS, Nobjects, done;
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image   = section->image;
+
+  Nobjects = image[0].tickmarks.Nobjects;
+  NOBJECTS = Nobjects + 100;
+  REALLOCATE (image[0].tickmarks.objects, KiiOverlay, NOBJECTS);
+  
+  done = FALSE;
+  while (!done) {
+    status = read (sock, line, 128); 
+    line[128] = 0; 
+
+    if (!strncmp (line, "DONE", 4)) {
+      done = TRUE;
+      break;
+    }
+    
+    sscanf (line, "%s %lf %lf %lf %lf\n", type, &x, &y, &dx, &dy);
+    
+    if (strcmp (type, "TEXT") && strcmp (type, "LINE") && strcmp (type, "BOX") && strcmp (type, "CIRCLE")) {  /* skip */
+      fprintf (stderr, "don't know %s, skipping\n", type);
+      continue;
+    }
+    
+    strcpy (image[0].tickmarks.objects[Nobjects].type, type);
+    image[0].tickmarks.objects[Nobjects].x = x;
+    image[0].tickmarks.objects[Nobjects].y = y;
+    image[0].tickmarks.objects[Nobjects].dx = dx;
+    image[0].tickmarks.objects[Nobjects].dy = dy;
+    
+    if (!strcmp (type, "TEXT")) { /* dx = Nchar, dy = angle (not yet used) */
+      status = read (sock, line, 128); 
+      line[128] = 0; 
+      ALLOCATE (image[0].tickmarks.objects[Nobjects].text, char, (int) dx + 1);
+      strncpy (image[0].tickmarks.objects[Nobjects].text, line, (int) dx);
+      image[0].tickmarks.objects[Nobjects].text[(int)dx] = 0;
+    }      
+    
+    Nobjects++;
+    if (Nobjects >= NOBJECTS) {
+      NOBJECTS = Nobjects + 100;
+      REALLOCATE (image[0].tickmarks.objects, KiiOverlay, NOBJECTS);
+    }
+
+  }
+
+  REALLOCATE (image[0].tickmarks.objects, KiiOverlay, MAX(Nobjects, 1));
+  image[0].tickmarks.Nobjects = Nobjects;
+
+  if (USE_XWINDOW) Refresh ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeColormap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeColormap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeColormap.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "Ximage.h"
+
+static char default_cmap[] = "grayscale";
+
+void MakeColormap (int argc, char **argv) {
+
+  int i, N, status;
+  char *temp_name;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* hardwired colors - white, black -- for drawing text and overlay graphics */
+  temp_name = XGetDefault (graphic[0].display, argv[0], "ROverlay");
+  graphic[0].overlay_color[0] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "red"    : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "GOverlay");
+  graphic[0].overlay_color[1] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "green"  : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "BOverlay");
+  graphic[0].overlay_color[2] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "blue"   : temp_name), graphic[0].colormap, graphic[0].fore);
+  temp_name = XGetDefault (graphic[0].display, argv[0], "YOverlay");
+  graphic[0].overlay_color[3] = GetColor (graphic[0].display, (temp_name == (char *) NULL ? "yellow" : temp_name), graphic[0].colormap, graphic[0].fore);
+
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    graphic[0].cmap[i].pixel = graphic[0].pixels[i];
+  }
+
+  /* decide on a color map */
+  temp_name = XGetDefault (graphic[0].display, argv[0], "Colormap");
+  if ((N = get_argument (argc, argv, "-cm"))) {
+    if (N + 1 < argc) {
+      temp_name = argv[N+1];
+    } else {
+      fprintf (stderr, "error: usage is -cm ColormapName\n");
+      exit (1);
+    }
+  }
+  if (temp_name == (char *) NULL) temp_name = default_cmap;
+
+  status = SetColormap (temp_name);
+  if (!status) {
+    fprintf (stderr, "invalid colormap, using default %s\n", default_cmap);
+    temp_name = default_cmap;
+    status = SetColormap (temp_name);
+    if (!status) {
+      fprintf (stderr, "problem with default colormap\n");
+      exit (1);
+    }
+  }
+}
+
+/* this routine is NOT independent of the number of overlays */
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeCursor.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeCursor.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeCursor.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** MakeCursor *************/
+void MakeCursor (Graphic *graphic, unsigned int cursor) {
+
+  graphic->cursor = XCreateFontCursor (graphic->display, (unsigned) cursor);
+
+  if (graphic->cursor != (Cursor) None) XDefineCursor (graphic->display, graphic->window, graphic->cursor);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeGC.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeGC.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/MakeGC.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "Ximage.h"
+
+/************** MakeGC *************/
+void MakeGC (Graphic *graphic) {
+
+  XGCValues gcvalues;
+
+  gcvalues.foreground = graphic->fore;
+  gcvalues.background = graphic->back;
+  graphic->gc = XCreateGC (graphic->display, graphic->window, 
+			     GCForeground | GCBackground, &gcvalues);
+  if (graphic->gc == 0)
+    QuitX (graphic->display, "Error in creating a Graphics Context");
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/MapWindow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/MapWindow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/MapWindow.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** MapWindow *************/
+void MapWindow (Graphic *graphic) {
+
+  XMapRaised (graphic->display, graphic->window);
+  XMapSubwindows (graphic->display, graphic->window);
+  FlushDisplay (graphic);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/NameWindow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/NameWindow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/NameWindow.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+/************** NameWindow *************/
+void NameWindow (Graphic *graphic, char *Name) {
+
+  char       *name;
+  char       *class_name;
+  char       *class_type;
+  XClassHint *classhints;
+
+  name = strrchr (Name, '/');
+  if (name != NULL) 
+    name ++;
+  else 
+    name = Name;
+
+  class_type = class_name = name;
+  classhints = XAllocClassHint ();
+
+  if (classhints != (XClassHint *) NULL)  {
+    classhints[0].res_name = class_name;
+    classhints[0].res_class = class_type;
+    XSetClassHint (graphic->display, graphic->window, classhints);
+    XFree (classhints);
+  }
+  
+  XStoreName (graphic->display, graphic->window, name);
+  XSetIconName (graphic->display, graphic->window, name);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/OpenDisplay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/OpenDisplay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/OpenDisplay.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+/************** OpenDisplay *************/
+Display *OpenDisplay (char *name, int *screen) {
+
+  Display *display;
+  
+  display = XOpenDisplay (name);
+  if (display == NULL) {
+    fprintf (stderr, "Error could not open X display to %s\n", XDisplayName (name));
+    exit (0);
+  }
+  *screen = DefaultScreen (display);
+  return (display);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PNGit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PNGit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PNGit.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "Ximage.h"
+
+int PNGit (int sock) {
+
+  FILE *f;
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_color *palette;
+  int Npalette;
+  char filename[1024];
+  int Nbytes, status;
+  bDrawBuffer *buffer;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", "Xgraph.png");
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, (png_voidp) NULL, (png_voidp) NULL);
+  if (!png_ptr) {
+    fprintf (stderr, "can't get png structure\n");
+    fclose (f);
+    return (TRUE);
+  }
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if (!info_ptr) {
+    fprintf (stderr, "can't get png info structure\n");
+    png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+    fclose (f);
+    return (TRUE);
+  }
+
+  if (setjmp (png_ptr[0].jmpbuf)) {
+    fprintf (stderr, "can't get png return\n");
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    fclose (f);
+    return (TRUE);
+  }
+
+  png_init_io (png_ptr, f);
+
+  /* see docs for write-row-callback to provide progress info */
+
+  png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 
+
+  /* png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); */
+
+  palette = KapaPNGPalette (&Npalette);
+  png_set_PLTE (png_ptr, info_ptr, palette, Npalette); 
+ 
+  png_write_info (png_ptr, info_ptr);
+
+  buffer = bDrawIt ();
+
+  png_write_image (png_ptr, buffer[0].pixels);
+  png_write_end (png_ptr, info_ptr);
+  png_destroy_write_struct (&png_ptr, &info_ptr);
+  
+  bDrawBufferFree (buffer);
+  free (palette);
+  fclose (f);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PPMit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PPMit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PPMit.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+int PPMit (int sock) {
+
+  FILE *f;
+  char *line, filename[1024];
+  int Nbytes, status, dx, dy, i, j, Npalette, color;
+  png_color *palette;
+  bDrawBuffer *buffer;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* expect a line telling the number of bytes and a filename */
+  status = read (sock, filename, 16);
+  filename[16] = 0;
+  sscanf (filename, "%*s %d", &Nbytes);
+  status = read (sock, filename, Nbytes);
+  filename[status] = 0; /* make the string easy to parse */
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+  
+  palette = KapaPNGPalette (&Npalette);
+
+  dx = graphic->dx;
+  dy = graphic->dy;
+  
+  fprintf (f, "P6\n");
+  fprintf (f, "%d %d\n", dx, dy);
+  fprintf (f, "255\n");
+
+  buffer = bDrawIt ();
+
+  ALLOCATE (line, char, 3*dx);
+
+  for (i = 0; i < dy; i++) {
+    for (j = 0; j < dx; j++) {
+      color = buffer[0].pixels[i][j];
+      line[3*j + 0] = palette[color].red;
+      line[3*j + 1] = palette[color].green;
+      line[3*j + 2] = palette[color].blue;
+    }
+    fwrite (line, 3, dx, f);
+  }
+  fclose (f);
+  
+  bDrawBufferFree (buffer);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSFrame.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSFrame.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSFrame.c	(revision 17422)
@@ -0,0 +1,105 @@
+# include "Ximage.h"
+# define DrawLine(X1,Y1,DX,DY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, Y1, X1+DX, Y1+DY))
+
+int PSFrame (KapaGraphWidget *graph, FILE *f) {
+  
+  int i, P, IsLabel, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  fprintf (f, "1 setlinewidth\n");
+  for (i = 0; i < 4; i++) {
+    fx = graph[0].axis[i].fx;
+    fy = graphic->dy - graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = -graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    if (graph[0].axis[i].isaxis) { DrawLine (fx, fy, dfx, dfy); }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = graph[0].axis[i].islabel;
+	PSTick (f, fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void PSTick (FILE *f, double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (mode == 1) { size = MAX (0.02, 7.0 / P); }
+  if (mode == 0) { size = MAX (0.01, 4.0 / P); }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = -1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  DrawLine (x, y, dx, dy);
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    PSRotText (f, (int)x, (int)y, string, pos, 0.0);
+  }
+}
+
+  /* 
+  dx = size*dfy*n;	
+  dy = size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  
+  dx = -size*dfy*n;	
+  dy = -size*dfx*n;
+  DrawLine (x, y, dx, dy);
+  */
+  
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSLabels.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSLabels.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSLabels.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "Ximage.h"
+  
+void PSLabels (KapaGraphWidget *graph, FILE *f) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graphic->dy - graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size); 
+      PSRotText (f, x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSObjects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSObjects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSObjects.c	(revision 17422)
@@ -0,0 +1,600 @@
+# include "Ximage.h"
+
+# define DrawLine(X1,Y1,X2,Y2) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f L\n", X1, graphic->dy - Y1, X2, graphic->dy - Y2))
+# define DrawCircle(X1,Y1,R) (fprintf (f, " %6.2f %6.2f %6.2f C\n", (X1), (graphic->dy - Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f B\n", (dX), (dY), (X-0.5*dX), (graphic->dy-Y-0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f F\n", (dX), (dY), (X-0.5*dX), (graphic->dy-Y-0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2, X3, Y3) (fprintf (f, " %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f TF\n", (X1), (graphic->dy-Y1), (X2), (graphic->dy-Y2), (X3), (graphic->dy-Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+int PSObjects (KapaGraphWidget *graph, FILE *f) {
+  
+  int i;
+  double lweight;
+  static char dash[] = "5";
+  static char dot[] = "3";
+  
+  graphic = GetGraphic();
+
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    switch (graph[0].objects[i].ltype) {
+    case 0:
+      break;
+    case 1:
+      fprintf (f, "[%s] 0 setdash\n", dash);
+      break;
+    case 2:
+      fprintf (f, "[%s] 0 setdash\n", dot);
+      break;
+    default:
+      break;
+    }
+    
+    lweight = MAX (0, MIN (10, graph[0].objects[i].lweight));
+    fprintf (f, "%.1f setlinewidth\n", lweight);
+    fprintf (f, "%s setrgbcolor\n", KapaColorRGBString(graph[0].objects[i].color));
+
+    switch (graph[0].objects[i].style) {
+    case CONNECT: 
+      PSConnect (graph, &graph[0].objects[i], f);
+      break;
+    case HISTOGRAM:
+      PSHistogram (graph, &graph[0].objects[i], f);
+      break;
+    case POINTS:
+      PSPoints (graph, &graph[0].objects[i], f);
+      break;
+    }
+
+    if (graph[0].objects[i].etype & 0x01) {
+      PSYErrors (graph, &graph[0].objects[i], f);
+    }
+    if (graph[0].objects[i].etype & 0x02) {
+      PSXErrors (graph, &graph[0].objects[i], f);
+    }
+    fprintf (f, "[] 0 setdash\n");
+    fprintf (f, "0.00 0.00 0.00 setrgbcolor\n");
+  }
+  return (TRUE);
+}
+
+/*******/
+void PSConnect (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    ClipLinePS (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1, f);
+    /* DrawLine (sx0, sy0, sx1, sy1); */
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void ClipLinePS (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0, FILE *f) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void PSHistogram (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void PSPoints (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	    /*
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	    */
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy + 0.58*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx + d*z[i], sy + 0.58*d*z[i], sx,          sy - 1.15*d*z[i]);
+	    DrawLine (sx,          sy - 1.15*d*z[i], sx - d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    /*
+	    DrawLine (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d);
+	    DrawLine (sx + d, sy + 0.58*d, sx,     sy - 1.15*d);
+	    DrawLine (sx,     sy - 1.15*d, sx - d, sy + 0.58*d);
+	    */
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d);
+	    DrawLine (sx + d, sy - 0.58*d, sx,     sy + 1.15*d);
+	    DrawLine (sx,     sy + 1.15*d, sx - d, sy - 0.58*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void PSXErrors (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+
+    
+/*******/
+void PSYErrors (KapaGraphWidget *graph, Gobjects *object, FILE *f) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSOverlay.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "Ximage.h"
+
+void PSOverlay (KapaImageWidget *image, int N, FILE *f, int extra) {
+
+  int i;
+  double X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax;
+  double expand, X0, Y0;
+ 
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+  }
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, &image[0].picture);
+  X0 -= image[0].picture.x;
+  Y0 -= image[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = image[0].picture.dx;
+  Ymax = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    X  = (image[0].overlay[N].objects[i].x)/expand + X0;
+    Y =  Ymax - (image[0].overlay[N].objects[i].y)/expand - Y0;
+    dX = (image[0].overlay[N].objects[i].dx)/expand;
+    dY = (image[0].overlay[N].objects[i].dy)/expand;
+    
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	    ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, " %6.1f %6.1f %6.1f %6.1f L\n", X + extra, Y + extra, (X+dX + extra), (Y-dY + extra));
+	break;
+      case KII_OVERLAY_TEXT:
+	if (((X < Xmin) && (X + dX < Xmin)) || ((X > Xmax) && (X + dX > Xmax)) ||
+	    ((Y < Ymin) && (Y + dY < Ymin)) || ((Y > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, "(%s) %6.1f %6.1f T\n", image[0].overlay[N].objects[i].text, X + extra, Y + extra); 
+	break;
+      case KII_OVERLAY_BOX:
+	if (((X - 0.5*dX < Xmin) && (X + 0.5*dX < Xmin)) || ((X - 0.5*dX > Xmax) && (X + 0.5*dX > Xmax)) ||
+	    ((Y - 0.5*dY < Ymin) && (Y + 0.5*dY < Ymin)) || ((Y - 0.5*dY > Ymax) && (Y + 0.5*dY > Ymax))) {
+	  break;
+	}
+	fprintf (f, " %6.1f %6.1f %6.1f %6.1f B\n", (dX + 2*extra), (dY + 2*extra), (X - 0.5*dX - extra), (Y - 0.5*dY - extra));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	if (((X - dX < Xmin) && (X + dX < Xmin)) || ((X - dX > Xmax) && (X + dX > Xmax)) ||
+	    ((Y - dY < Ymin) && (Y + dY < Ymin)) || ((Y - dY > Ymax) && (Y + dY > Ymax))) {
+	  break;
+	}
+	// XXX add in the rotated ellipse version (rotate in PS space)
+	fprintf (f, " %6.1f %6.1f %6.1f C\n", X, Y, fabs(dX + extra));
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSPixmap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSPixmap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSPixmap.c	(revision 17422)
@@ -0,0 +1,185 @@
+# include "Ximage.h"
+
+// XXX this stuff has been broken by the conversion to the pixmap stuff
+
+void PSPixmap8 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned char *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+void PSPixmap16 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned short *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned short *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+void PSPixmap24 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, dx, dy, val, extra;
+  unsigned char *buff;
+  unsigned long color, byte;
+  double Nchar, Npix, start, slope, frac;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  dx = image[0].picture.dx;
+  dy = image[0].picture.dy;
+  extra = 4 - (dx * 3) % 4;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned char *)&image[0].picture.data[(dy - 1)*(3*dx + extra)];
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < dy; i++) {
+    for (k = 0; k < dx; k++, buff+=3) {
+      if (*buff == back) {
+	val = Nchar;
+      } else {
+	color = 0;
+	byte = buff[2];
+	color = (byte << 16);
+	byte = buff[1];
+	color |= (byte << 8);
+	byte = buff[0];
+	color |= byte;
+	for (m = 0; (graphic[0].cmap[m].pixel != color) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*3*dx + extra;
+  }
+  return;
+}
+
+void PSPixmap32 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned int *buff;
+  unsigned long back;
+
+  Nchar = 255.0;
+  Npix = graphic[0].Npixels;
+  frac = Nchar / Npix;
+  /* start at the last line, print lines in decending order */
+  buff = (unsigned int *)image[0].picture.data + image[0].picture.dx*(image[0].picture.dy - 1);
+  slope = image[0].image[0].slope;
+  start = image[0].image[0].start;
+  back  = graphic[0].back;
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+
+# if (0)
+// XXX needs work!
+void PSPixmap32_RGB (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, k, m, val;
+  double Nchar, Npix, start, slope, frac;
+  unsigned int *buff;
+  unsigned long back;
+  unsigned char 
+
+  ALLOCATE (pixelR, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixelG, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixelB, unsigned char, graphic[0].Npixels);
+
+  /** cmap[i].pixel must be defined even if X is not used **/
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixelR[i] = graphic[0].cmap[i].red >> 8;
+    pixelG[i] = graphic[0].cmap[i].green >> 8;
+    pixelB[i] = graphic[0].cmap[i].blue >> 8;
+  }
+
+  for (i = 0; i < image[0].picture.dy; i++) {
+    for (k = 0; k < image[0].picture.dx; k++, buff++) {
+      if (*buff == back) 
+	val = Nchar;
+      else {
+	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
+	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
+      }
+      fprintf (f, "%02x", val);
+      if (!((k+1) % 40)) fprintf (f, "\n"); 
+    }
+    fprintf (f, "\n");
+    buff -= 2*image[0].picture.dx;
+  }
+  return;
+}
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSTextlines.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSTextlines.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSTextlines.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+  
+void PSTextlines (KapaGraphWidget *graph, FILE *f) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graphic->dy - graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      PSRotText (f, x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+  /* pos values
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|   8   |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+  */
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSimage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSimage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSimage.c	(revision 17422)
@@ -0,0 +1,61 @@
+# include "Ximage.h"
+
+int PSimage (KapaImageWidget *image, FILE *f) {
+  
+  int i;
+  Graphic *graphic;
+
+  if (!USE_XWINDOW) {
+    fprintf (stderr, "PSimage not working yet for no X mode\n");
+    return (TRUE);
+  }
+
+  graphic = GetGraphic();
+
+  fprintf (f, " newpath 0 0 moveto %d 0 lineto %d %d lineto 0 %d lineto closepath clip\n\n", 
+	   image[0].picture.dx, image[0].picture.dx, image[0].picture.dy, image[0].picture.dy);
+  fprintf (f, "gsave %% encloses image\n");
+  fprintf (f, "%d %d 8\n", image[0].picture.dx, image[0].picture.dy);
+  fprintf (f, "[1 0 0 1 0 0]\n");
+  fprintf (f, "{currentfile %d string readhexstring pop} image\n\n", image[0].picture.dx);
+
+  /******** First we draw the picture itself ********/
+  /* in !USE_XWINDOW, we'll have to change this to use the JPEG function */
+  switch (graphic[0].Nbits) {
+  case 8:
+    PSPixmap8 (graphic, image, f);
+    break;
+  case 16:
+    PSPixmap16 (graphic, image, f);
+    break;
+  case 24:
+    PSPixmap24 (graphic, image, f);
+    break;
+  case 32:
+    PSPixmap32 (graphic, image, f);
+    break;
+  }
+
+  fprintf (f, "grestore %% end of image\n");
+  fprintf (f, "stroke\n");
+
+  // should we have a 'gsave / grestore' container here?
+  fprintf (f, "%% plot overlay objects\n");
+  fprintf (f, "1 setgray\n");
+
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      fprintf (f, "%% overlay %d\n", i);
+      PSOverlay (image, i, f, 0);
+    }
+  }
+  fprintf (f, "0 setgray\n");
+  for (i = 0; i < NOVERLAYS; i++) {
+    if (image[0].overlay[i].active) {
+      fprintf (f, "%% overlay %d\n", i);
+      PSOverlay (image, i, f, 1);
+    }
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PSit.c	(revision 17422)
@@ -0,0 +1,107 @@
+# include "Ximage.h"
+# define XOFFSET 60
+# define YOFFSET 60
+
+static char *name = "$Name: not supported by cvs2svn $";
+static Graphic *graphic;
+
+int PScommand (int sock) {
+
+  int status, scaleMode, pageMode;
+  char filename[1024], pagename[1024];
+
+  /* expect a line telling the number of bytes and a filename */
+  KiiScanMessage (sock, "%s %s %d %d", filename, pagename, &scaleMode, &pageMode);
+  status = PSit (filename, pagename, scaleMode, pageMode);
+  return (status);
+}
+
+int PSit (char *filename, char *pagename, int scaleMode, int pageMode) {
+
+  int i, Nsection;
+  double scale;
+  FILE *f;
+  char *version;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  if (pageMode == KAPA_PS_NEWPAGE) {
+    f = fopen (filename, "a+");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == NULL) {
+    fprintf (stderr, "can't open output file %s\n", filename);
+    return (TRUE);  /* true because otherwise it quits kapa! */
+  }
+
+  /* two scaling options: expand to fit page / keep absolute size */ 
+  if (scaleMode) {
+    scale = MIN (fabs(500.0 / graphic->dx), fabs (700.0 / graphic->dy));
+  } else {
+    scale = 72.0 / 96.0; /* ratio of screen pixels to points */
+  }
+
+  switch (pageMode) {
+    case KAPA_PS_NEWPLOT:
+      fprintf (f, "%%!PS-Adobe-2.0 EPSF-2.0\n");
+      fprintf (f, "%%%%Title: %s\n", filename);
+      version = strip_version (name);
+      fprintf (f, "%%%%Creator: Kapa (%s)\n", version);
+      free (version);
+      fprintf (f, "%%%%BoundingBox: %d %d %.0f %.0f\n", 
+	       XOFFSET, YOFFSET, XOFFSET + scale*graphic->dx, YOFFSET + scale*graphic->dy);
+      fprintf (f, "%%%%Pages: 1\n");
+      fprintf (f, "%%%%DocumentFonts:\n");
+      fprintf (f, "%%%%EndComments\n");
+      fprintf (f, "%%%%EndProlog\n");
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_NEWPAGE:
+      fprintf (f, "%%%%Page: %s\n\n", pagename);
+      break;
+
+    case KAPA_PS_RAWPAGE:
+      break;
+  } 
+  fprintf (f, "gsave %% encloses picture\n");
+  fprintf (f, "%% local abbreviations\n");
+  fprintf (f, "/Times-Roman findfont 14 scalefont setfont\n");
+  fprintf (f, "/T {moveto show stroke} def\n");
+  fprintf (f, "/B { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath stroke } def\n");
+  fprintf (f, "/F { newpath moveto dup 0 exch rlineto exch dup 0 rlineto exch -1 mul\n");
+  fprintf (f, " 0 exch rlineto -1 mul 0 rlineto closepath fill stroke } def\n");
+  fprintf (f, "/C {0 360 arc stroke} def\n");
+  fprintf (f, "/L {newpath moveto lineto stroke} def\n\n");
+  fprintf (f, "/TF {newpath moveto lineto lineto fill stroke} def\n\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) {
+    fprintf (f, " %d %d translate\n", XOFFSET, YOFFSET);
+    fprintf (f, "  %f  %f scale\n", scale, scale);
+  }
+
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+    section = GetSectionByNumber (i);
+    if (section->graph) {
+      PSFrame (section->graph, f); 
+      PSObjects (section->graph, f);
+      PSLabels (section->graph, f);
+      PSTextlines (section->graph, f);
+    }
+    if (section->image) {
+      PSimage (section->image, f);
+    }
+  }
+  
+  fprintf (f, "grestore %% end of picture\n");
+
+  if (pageMode != KAPA_PS_RAWPAGE) fprintf (f, "showpage\n");
+
+  fclose (f);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintOverlay.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "Ximage.h"
+
+void PaintOverlay (Graphic *graphic, KapaImageWidget *image, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int x, y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double t, expand, X, Y;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, image[0].overlay[N].color);
+  
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = image[0].picture.expand;
+  }
+  if (image[0].picture.expand < 0) {
+    expand = 1.0 / fabs((double)image[0].picture.expand);
+  }
+
+  Xmin = image[0].picture.x;
+  Ymin = image[0].picture.y;
+  Xmax = image[0].picture.x + image[0].picture.dx;
+  Ymax = image[0].picture.y + image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    Image_to_Screen (&X, &Y, image[0].overlay[N].objects[i].x, image[0].overlay[N].objects[i].y, &image[0].picture);
+    dX = image[0].overlay[N].objects[i].dx * expand;
+    dY = image[0].overlay[N].objects[i].dy * expand;
+    if (image[0].picture.flipx) dX *= -1;
+    if (image[0].picture.flipy) dY *= -1;
+
+    if (X + dX < Xmin) continue;
+    if (X - dX > Xmax) continue;
+    if (Y + dY < Ymin) continue; 
+    if (Y - dY > Ymax);
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	// the angle makes no sense for a line...
+	XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	// XXX currently we ignore the rectangle angle
+	XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].overlay[N].objects[i].text, strlen(image[0].overlay[N].objects[i].text));
+	break;
+      case KII_OVERLAY_BOX:
+	dx = MAX (abs(dX),2) / 2;
+	dy = MAX (abs(dY),2) / 2;
+	// XXX currently we ignore the rectangle angle
+	XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy);
+	break;
+      case KII_OVERLAY_CIRCLE:
+	dx = MAX (abs(dX),2);
+	dy = MAX (abs(dY),2);
+	if (image[0].overlay[N].objects[i].angle == 0.0) {
+	  XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, (X - dx), (Y - dy), 2*dx, 2*dy, 0, 23040);
+	} else {
+	  // very stupid rotate ellipse drawing:
+	  double angle = image[0].overlay[N].objects[i].angle * RAD_DEG;
+	  double cs = cos(angle);
+	  double sn = sin(angle);
+	  // XXX dt should be based on the size of the ellipse...
+	  for (t = 0; t < 2*M_PI; t+=0.05) {
+	    x = X + dx*cos(t)*cs + dy*sin(t)*sn;
+	    y = Y - dx*cos(t)*sn + dy*sin(t)*cs;
+	    XDrawPoint (graphic[0].display, graphic[0].window, graphic[0].gc, x, y);
+	  }
+	}
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintTickmarks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintTickmarks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/PaintTickmarks.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "Ximage.h"
+
+void PaintTickmarks (Graphic *graphic, KapaImageWidget *image) {
+
+  int i;
+  int X, Y, dX, dY;
+  int Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+ 
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+
+  Xmin = image[0].picture.x;
+  Ymin = image[0].picture.y;
+  Xmax = image[0].picture.x + image[0].picture.dx;
+  Ymax = image[0].picture.y + image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].tickmarks.Nobjects; i++) {
+    X  = image[0].tickmarks.objects[i].x * Xrange + Xmin;
+    Y  = image[0].tickmarks.objects[i].y * Yrange + Ymin;
+    dX = image[0].tickmarks.objects[i].dx * Xrange;
+    dY = image[0].tickmarks.objects[i].dy * Yrange;
+
+    switch (image[0].overlay[0].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	XDrawLine (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	if (image[0].tickmarks.objects[i].dy == 0) {
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+	    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-11, 6*strlen(image[0].tickmarks.objects[i].text), 11); 
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+	    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].tickmarks.objects[i].text, strlen(image[0].tickmarks.objects[i].text));
+	}
+	if (image[0].tickmarks.objects[i].dy == 90) {
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+	    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y-6*strlen(image[0].tickmarks.objects[i].text), 11, 6*strlen(image[0].tickmarks.objects[i].text)); 
+	    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+	    /* XDrawRotString (graphic[0].display, graphic[0].window, graphic[0].gc, X, Y, image[0].tickmarks.objects[i].text, strlen(image[0].tickmarks.objects[i].text)); */
+	}
+	break;
+      case KII_OVERLAY_BOX:
+	XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, (int)(X - 0.5*dX), (int)(Y - 0.5*dY), abs(dX), abs(dY));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	XDrawArc (graphic[0].display, graphic[0].window, graphic[0].gc, X - dX, Y - dY, abs(2*dX), abs(2*dY), 0, 23040);
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/QuitX.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/QuitX.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/QuitX.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "Ximage.h"
+
+/************** QuitX *************/
+void QuitX (Display *display, char *error_message) {
+
+  fprintf (stderr, "Error: %s\n", error_message);
+  XCloseDisplay (display);
+  exit (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reconfig.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reconfig.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reconfig.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+
+// XXX merge this code with Resize
+int Reconfig (XEvent *event) {
+
+  int i, Nsection, NX, NY;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  // XXX keep this min limit (or modify for !USE_XWINDOW)?
+  NX = MAX(event[0].xconfigure.width, MIN_WIDTH); 
+  NY = MAX(event[0].xconfigure.height, MIN_HEIGHT); 
+
+  // if the new size is the same as the old size, do nothing.
+  if ((graphic->dx == NX) && (graphic->dy == NY)) return (TRUE);
+
+  // set the new window size
+  graphic->dx = NX; 
+  graphic->dy = NY; 
+
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      SetSectionSizes (section);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Refresh.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Refresh.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Refresh.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "Ximage.h"
+
+void Refresh () {
+
+  int i, Nsection;
+  Graphic *graphic;
+  Section *section;
+
+  if (!USE_XWINDOW) return;
+  // if (HAVE_BACKING) return;
+
+  graphic = GetGraphic();
+  
+  /* XClearWindow   (graphic.display, graphic.window); */
+  XSetForeground (graphic->display, graphic->gc, graphic->back);
+  XFillRectangle (graphic->display, graphic->window, graphic->gc, 0, 0, graphic->dx, graphic->dy);
+  XSetForeground (graphic->display, graphic->gc, graphic->fore);
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      DrawImage (section->image);
+      DrawGraph (section->graph);
+  }
+
+  FlushDisplay ();
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap.c	(revision 17422)
@@ -0,0 +1,21 @@
+# include "Ximage.h"
+
+void Remap (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].Nbits) {
+  case 8:
+    Remap8  (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 16:
+    Remap16 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 24:
+    Remap24 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  case 32:
+    Remap32 (graphic, image, &image->picture, &image->image->matrix);
+    break;
+  }
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap16.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap16.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap16.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "Ximage.h"
+
+# define MY_SWAP_BYTE(W) { \
+  char tmp, *X; \
+  X = (char *) &W; \
+  tmp = X[0]; X[0] = X[1]; X[1] = tmp; }
+
+void Remap16 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned short *out_pix, *out_pix2, *data;
+  unsigned short *in_pix, *in_pix2;
+  unsigned short *pixel, pixvalue;
+  unsigned short back;
+  int swap_client, swap_server, swap_bytes;
+
+  ALLOCATE (pixel, unsigned short, graphic[0].Npixels);
+
+# ifdef BYTE_SWAP
+  swap_client = 1;
+# else 
+  swap_client = 0;
+# endif  
+
+  swap_server = ImageByteOrder (graphic[0].display);
+  swap_bytes = !(swap_client ^ swap_server);
+
+  // local array for pixel values (is this working??)
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel[i] = 0xffff &  graphic[0].cmap[i].pixel;
+    if (swap_bytes) MY_SWAP_BYTE (pixel[i]);
+  }
+  back = 0xffff & graphic[0].back;
+  if (swap_bytes) MY_SWAP_BYTE (back);
+  // XXX not certain this is the correct solution...
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image boundaries
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  data = out_pix = (unsigned short *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*dx];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue; 
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+  } 
+  
+  /**** fill in top area ****/
+  out_pix = &data[j_end*dx];
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix++) { 
+      *out_pix = back;
+    }
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 16, 0);
+
+  free (pixel);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap24.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap24.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap24.c	(revision 17422)
@@ -0,0 +1,149 @@
+# include "Ximage.h"
+
+void Remap24 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback, extra;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2, *data;
+  unsigned short *in_pix, *in_pix2;
+  unsigned char *pixel1, *pixel2, *pixel3;
+  unsigned char pixvalue1, pixvalue2, pixvalue3;
+  unsigned char back1, back2, back3;
+
+  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);
+
+  // local arrays for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
+    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
+    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
+    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
+  }
+  back1 = 0x0000ff & (graphic[0].back >>  0);
+  back2 = 0x0000ff & (graphic[0].back >>  8);
+  back3 = 0x0000ff & (graphic[0].back >> 16);
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand == 0) /* set up expansions */
+    picture[0].expand = 1;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // each row is padded to a 4-byte word
+  extra = 4 - (dx * 3) % 4;
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+  
+  data = out_pix = (unsigned char *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) {
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix += extra;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
+    out_pix = &data[j*(3*dx+extra)];
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*(3*dx + extra);
+      for (i = 0; i < i_start; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+    out_pix += 3*i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
+	out_pix[0] = pixel1[*in_pix2];
+	out_pix[1] = pixel2[*in_pix2];
+	out_pix[2] = pixel3[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
+	pixvalue1 = pixel1[*in_pix2];
+	pixvalue2 = pixel2[*in_pix2];
+	pixvalue3 = pixel3[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
+	    out_pix2[0] = pixvalue1; 
+	    out_pix2[1] = pixvalue2; 
+	    out_pix2[2] = pixvalue3; 
+	  }
+	}
+      }
+    }
+    out_pix -= 3*dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*(3*dx+extra);
+      for (i = i_end; i < dx; i++, out_pix2+=3) {
+	out_pix2[0] = back1;
+	out_pix2[1] = back2;
+	out_pix2[2] = back3;
+      }
+    }
+  } 
+  
+  /**** fill in top area ****/
+  out_pix = &data[j_end*(3*dx+extra)];
+  for (j = 0; j < (dy - j_end); j++) {
+    for (i = 0; i < dx; i++, out_pix+=3) { 
+      out_pix[0] = back1;
+      out_pix[1] = back2;
+      out_pix[2] = back3;
+    }
+    out_pix+=extra;
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
+
+  free (pixel1);
+  free (pixel2);
+  free (pixel3);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap32.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap32.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap32.c	(revision 17422)
@@ -0,0 +1,161 @@
+# include "Ximage.h"
+
+# define MY_SWAP_INT(A,B) { int tmp; tmp = A; A = B; B = tmp; }
+
+# define MY_SWAP_WORD(W) { \
+  char tmp, *X; \
+  X = (char *) &W; \
+  tmp = X[0]; X[0] = X[3]; X[3] = tmp; \
+  tmp = X[1]; X[1] = X[2]; X[2] = tmp; }
+
+void Remap32 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback, inDX, inDY;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned int *out_pix, *out_pix2;
+  unsigned short *in_pix, *in_pix2;
+  unsigned long *pixel, pixvalue;
+  unsigned long back;
+  int swap_client, swap_server, swap_bytes;
+
+  ALLOCATE (pixel, unsigned long, graphic[0].Npixels);
+
+# ifdef BYTE_SWAP
+  swap_client = 1;
+# else 
+  swap_client = 0;
+# endif  
+
+  swap_server = ImageByteOrder (graphic[0].display);
+  swap_bytes = !(swap_client ^ swap_server);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) { 
+    pixel[i] = graphic[0].cmap[i].pixel;
+    if (swap_bytes) MY_SWAP_WORD(pixel[i]);
+  }
+  back = graphic[0].back;
+  if (swap_bytes) MY_SWAP_WORD(back);
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -1));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image sizes
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  if (i_end < i_start) MY_SWAP_INT (i_start, i_end);
+  if (j_end < j_start) MY_SWAP_INT (j_start, j_end);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+  Ix = MIN (MAX (0, Ix), DX - 1);
+  Iy = MIN (MAX (0, Iy), DY - 1);
+  // XXX not completely consistent with the i_start, i_end range...
+
+  // we need to offset because i_start points to the bottom edge of Ix
+  // if (picture[0].flipx) Ix -= 1.0;
+  // if (picture[0].flipy) Iy -= 1.0;
+
+  inDX = picture[0].flipx ? -1 : +1;
+  inDY = picture[0].flipy ? -1 : +1;
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned int *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += inDY*expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2 += inDX*expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2 += inDX, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    // assert (in_pix2 - image[0].pixmap <= DX*DY);
+    // assert (in_pix2 - image[0].pixmap >= 0);
+    // assert (in_pix - image[0].pixmap <= DX*DY);
+    // assert (in_pix - image[0].pixmap >= 0);
+
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
+    // assert (out_pix - (unsigned int *)picture[0].data >= 0);
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  // assert (out_pix - (unsigned int *)picture[0].data <= dx*dy);
+  // assert (out_pix - (unsigned int *)picture[0].data >= 0);
+
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned int *)picture[0].data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 32, 0);
+
+  free (pixel);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap8.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap8.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Remap8.c	(revision 17422)
@@ -0,0 +1,119 @@
+# include "Ximage.h"
+
+void Remap8 (Graphic *graphic, KapaImageWidget *image, Picture *picture, Matrix *matrix) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int dropback;
+  int dx, dy, DX, DY;
+  double expand, Ix, Iy;
+  int expand_in, expand_out;
+  unsigned char *out_pix, *out_pix2;
+  unsigned short *in_pix,  *in_pix2;
+  unsigned char *pixel, pixvalue;
+  unsigned char back;
+
+  ALLOCATE (pixel, unsigned char, graphic[0].Npixels);
+
+  // local array for pixel values
+  for (i = 0; i < graphic[0].Npixels; i++) {
+    pixel[i] = 0xff & graphic[0].cmap[i].pixel;
+  }
+  back = 0xff & graphic[0].back;
+
+  // set up expansions
+  assert ((picture[0].expand >= 1) || (picture[0].expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (picture[0].expand == 0) /* set up expansions */
+    picture[0].expand = 1;
+  if (picture[0].expand > 0) {
+    expand = 1 / (1.0*picture[0].expand);
+    expand_out = picture[0].expand;
+    expand_in  = 1;
+  }
+  if (picture[0].expand < 0) {
+    expand = fabs((double)picture[0].expand);
+    expand_out = 1;
+    expand_in  = -picture[0].expand;
+  }
+
+  // define the image boundaries
+  dx = picture[0].dx;
+  dy = picture[0].dy;
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  // x, y are the closest lit screen pixel to 0,0
+  Picture_Lower (&i_start, &j_start, matrix, picture);
+
+  // x, y are the closest lit screen pixel to dx, dy
+  Picture_Upper (&i_end, &j_end, matrix, picture);
+
+  // Ix,Iy are the first displayed image pixel
+  Picture_to_Image (&Ix, &Iy, i_start, j_start, picture);
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  out_pix = (unsigned char *) picture[0].data;
+  in_pix  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < dx*j_start; j++, out_pix++) {
+    *out_pix = back;
+  }
+  
+  for (j = j_start; j < j_end; j+= expand_out, out_pix+=(expand_out-1)*dx, in_pix += expand_in*DX) {
+
+    /**** fill in area to the left of the picture ****/
+    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
+      out_pix2 = out_pix + jj*dx;
+      for (i = 0; i < i_start; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += i_start;
+    
+    /*** fill in the picture region ***/
+    in_pix2 = in_pix;
+    if (expand_out == 1) {
+      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix++) {
+	*out_pix = pixel[*in_pix2];
+      }
+    } else {
+      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= expand_out) { 
+	pixvalue = pixel[*in_pix2];
+	out_pix2 = out_pix;
+	for (jj = 0; jj < expand_out; jj++, out_pix2+=(dx-expand_out)) {
+	  for (ii = 0; ii < expand_out; ii++, out_pix2++) {
+	    *out_pix2 = pixvalue;
+	  }
+	}
+      }
+    }
+    out_pix -= dropback;
+    
+    /**** fill in area to the right of the picture ****/
+    for (jj = 0; jj < expand_out; jj++) {
+      out_pix2 = out_pix + jj*dx;
+      for (i = i_end; i < dx; i++, out_pix2++) {
+	*out_pix2 = back;
+      }
+    }
+    out_pix += (dx - i_end);
+    
+  } 
+  
+  if ((j_end - j_start) % expand_out > 0)
+    out_pix -= expand_out - (j_end - j_start) % expand_out;
+  
+  /**** fill in top area ****/
+  for (j = 0; (j < dx*(dy - j_end)) && (out_pix - (unsigned char *)picture[0].data < dx*dy); j++, out_pix++) { 
+    *out_pix = back;
+  }
+  picture[0].pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
+					picture[0].data, picture[0].dx, picture[0].dy, 8, 0);
+  free (pixel);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reorient.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reorient.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Reorient.c	(revision 17422)
@@ -0,0 +1,42 @@
+# include "Ximage.h"
+
+void Reorient (Graphic *graphic, KapaImageWidget *image, double X, double Y, int mode) {
+
+  Picture *picture;
+
+  picture = &image[0].picture;
+
+  if (picture[0].expand == 0) picture[0].expand = 1;
+
+  if ((picture[0].X == X) && (picture[0].Y == Y) && (mode == 0)) {
+    Refresh ();
+    XFlush (graphic[0].display);
+    return;
+  }
+
+  switch (mode) {
+  case 0:
+    if ((picture[0].X != X) || (picture[0].Y != Y)) {
+      picture[0].X = X;
+      picture[0].Y = Y;
+    }
+    break;
+  case -1: 
+    picture[0].expand--;
+    if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = -2;
+    picture[0].X = X;
+    picture[0].Y = Y;
+    break;
+  case +1:
+    picture[0].expand++;
+    if ((picture[0].expand == 0) || (picture[0].expand == -1)) picture[0].expand = 1;
+    picture[0].X = X;
+    picture[0].Y = Y;
+    break;
+  }
+
+  Remap (graphic, image);
+  Refresh ();
+ 
+  XFlush (graphic[0].display);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Rescale.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Rescale.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Rescale.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "Ximage.h"
+
+void Rescale (Graphic *graphic, KapaImageWidget *image, Matrix *matrix) {
+
+  int i, j, DX, DY, value;
+  float *iData;
+  unsigned short *oData;
+  float slope;
+  float start;
+  unsigned short MaxValue;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  oData = graphic[0].pixmap;
+  iData = (float *) matrix[0].buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < DX; i++) {
+    for (j = 0; j < DY; j++, iData++, oData++) {
+      value = slope * iData - start;
+      if (value < 0) value = 0;
+      if (value > MaxValue) value = MaxValue;
+      *oData = value;
+    }
+  }
+}
+
+/** in order to call this function, graphic[0].pixmap must be allocated to match the
+ * current matrix (current channel) 
+ */
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Resize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Resize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Resize.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+
+// XXX Should there be a base command + KiiMessage command?
+int Resize (int sock) {
+ 
+  int i, Nsection;
+  unsigned int NX, NY;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  KiiScanMessage (sock, "%d %d", &NX, &NY);
+
+  // XXX keep this min limit (or modify for !USE_XWINDOW)?
+  NX = MAX(NX, MIN_WIDTH); 
+  NY = MAX(NY, MIN_HEIGHT); 
+
+  // if the new size is the same as the old size, do nothing.
+  if ((graphic->dx == NX) && (graphic->dy == NY)) return (TRUE);
+
+  // set the new window size
+  graphic->dx = NX; 
+  graphic->dy = NY; 
+
+  if (USE_XWINDOW) XResizeWindow (graphic->display, graphic->window, NX, NY);
+
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      SetSectionSizes (section);
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SaveOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SaveOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SaveOverlay.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "Ximage.h"
+
+int SaveOverlay (int sock) {
+
+  int i, N;
+  char filename[256], *type;
+  FILE *f;
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+
+  KiiScanMessage (sock, "%*s %d %s", &N, filename);
+  
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "could not open %s\n", filename);
+    return (TRUE);
+  }
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    type = KiiOverlayTypeByNumber (image[0].overlay[N].objects[i].type);
+    if (type == NULL) continue;
+    fprintf (f, "%s %lf %lf %lf %lf\n", 
+	     type,
+	     image[0].overlay[N].objects[i].x,
+	     image[0].overlay[N].objects[i].y,
+	     image[0].overlay[N].objects[i].dx,
+	     image[0].overlay[N].objects[i].dy);
+  }
+  fclose (f);
+  return (TRUE);
+}
+
+/* this is asymmetric with LoadOverlay.c.  In that case, the client reads the file and sends
+ * the overlay objects to kapa.  In this case, kapa writes the file directly... 
+ */
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/Sections.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/Sections.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/Sections.c	(revision 17422)
@@ -0,0 +1,218 @@
+# include "Ximage.h"
+
+static int  ActiveSection  = 0;
+static int       Nsections = 0;
+static Section **sections = NULL;
+
+Section *InitSection () {
+
+  Section *section;
+  ALLOCATE (section, Section, 1);
+
+  section[0].graph = NULL;
+  section[0].image = NULL;
+  section[0].x = 0;
+  section[0].y = 0;
+  section[0].dx = 0;
+  section[0].dy = 0;
+
+  section[0].name = NULL;
+  return (section);
+}
+
+void FreeSection (Section *section) {
+
+  if (section == NULL) return;
+  FreeGraph (section[0].graph);
+  FreeImage (section[0].image);
+  FREE (section[0].name);
+  free (section);
+}
+
+void FreeSections () {
+
+  int i;
+
+  for (i = 0; i < Nsections; i++) {
+    FreeSection (sections[i]);
+  }
+  free (sections);
+  Nsections = 0;
+  sections = NULL;
+}
+
+Section *AddSection (char *name, float x, float y, float dx, float dy) {
+
+  int N;
+
+  N = Nsections;
+
+  if (sections == NULL) {
+    Nsections = 1;
+    ALLOCATE (sections, Section *, Nsections);
+  } else {
+    Nsections ++;
+    REALLOCATE (sections, Section *, MAX (1, Nsections));
+  }
+  sections[N] = InitSection();
+  sections[N][0].name = strcreate (name);
+  sections[N][0].x = x;
+  sections[N][0].y = y;
+  sections[N][0].dx = dx;
+  sections[N][0].dy = dy;
+  ActiveSection = N;
+  return (sections[N]);
+}
+
+int DelSection (char *name) {
+
+  int i, j;
+
+  for (i = 0; i < Nsections; i++) {
+    if (strcmp (name, sections[i][0].name)) continue;
+    FreeSection (sections[i]);
+    for (j = i; j < Nsections - 1; j++) {
+      sections[j] = sections[j+1];
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int GetSectionByName (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nsections; i++) {
+    if (strcmp (name, sections[i][0].name)) continue;
+    return (i);
+  }
+  return (-1);
+}
+
+int GetNumberOfSections () {
+  return (Nsections);
+}
+
+Section *GetSectionByNumber (int N) {
+  if (N >= Nsections) return NULL;
+  if (N < 0) return NULL;
+  return sections[N];
+}
+
+Section *GetActiveSection () {
+  int N;
+  N = ActiveSection;
+  return (sections[N]);
+}
+
+int SetActiveSectionByNumber (int N) {
+  if (N >= Nsections) return FALSE;
+  if (N < 0) return FALSE;
+  ActiveSection = N;
+  return TRUE;
+}
+
+int ListSection (int sock) {
+  
+  int i, ThisSection;
+  char name[128];
+
+  KiiScanMessage (sock, "%s", name);
+
+  if (!strcmp (name, "*")) {
+    for (i = 0; i < Nsections; i++) {
+      fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	       sections[i][0].name, sections[i][0].x, sections[i][0].y, sections[i][0].dx, sections[i][0].dy);
+    }
+    return (TRUE);
+  }
+
+  ThisSection = -1;
+  for (i = 0; i < Nsections; i++) {
+    if (!strcmp (name, sections[i][0].name)) {
+      ThisSection = i;
+      break;
+    }
+  }
+  if (ThisSection == -1) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  fprintf (stderr, "%s: %6.3f %6.3f %6.3f %6.3f\n", 
+	   sections[ThisSection][0].name, 
+	   sections[ThisSection][0].x,  sections[ThisSection][0].y, 
+	   sections[ThisSection][0].dx, sections[ThisSection][0].dy);
+  return (TRUE);
+}
+
+void SetSectionSizes (Section *section) {
+
+    SetGraphSize (section);
+    SetImageSize (section);
+    return;
+}
+
+// return TRUE even for nonsense cases to avoid quitting kapa
+int MoveSection (int sock) {
+
+  int i, N;
+  char name[128];
+  char direction[16];
+  Section *tmpSection = NULL;
+
+  KiiScanMessage (sock, "%s %s", name, direction);
+  
+  N = GetSectionByName (name);
+  if (N < 0) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "up")) {
+      if (N < 0) return (TRUE);
+      if (N > Nsections - 2) return (TRUE);
+      tmpSection = sections[N];
+      sections[N] = sections[N+1];
+      sections[N+1] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "down")) {
+      if (N < 1) return (TRUE);
+      if (N > Nsections - 1) return (TRUE);
+      tmpSection = sections[N];
+      sections[N] = sections[N-1];
+      sections[N-1] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "top")) {
+      if (N < 0) return (TRUE);
+      if (N > Nsections - 2) return (TRUE);
+      tmpSection = sections[N];
+      for (i = N; i < Nsections - 1; i++) {
+	sections[i] = sections[i+1];
+      }
+      sections[i] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+
+  if (!strcasecmp (direction, "bottom")) {
+      if (N < 1) return (TRUE);
+      if (N > Nsections - 1) return (TRUE);
+      tmpSection = sections[N];
+      for (i = N; i >= 1; i--) {
+	sections[i] = sections[i-1];
+      }
+      sections[i] = tmpSection;
+      Refresh (1);
+      return (TRUE);
+  }
+  fprintf (stderr, "unknown direction %s for MoveSection\n", direction);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetChannel.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetChannel.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetChannel.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "Ximage.h"
+
+int SetChannel (int sock) {
+  
+  int Nchannel;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%d", &Nchannel);
+
+  if (Nchannel <  0) return (TRUE);
+  if (Nchannel >= NCHANNELS) return (TRUE);
+  
+  image[0].image = &image[0].channel[Nchannel];
+  SetColorScale (graphic, image);
+
+  if (!USE_XWINDOW) return (TRUE);
+
+  Remap (graphic, image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
+
+int SetColormapFromPipe (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+  char colormap[256];
+  int status;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiScanMessage (sock, "%s", colormap);
+
+  status = SetColormap (colormap);
+  if (!status) fprintf (stderr, "unknown colormap %s\n", colormap);
+
+  SetColorScale (graphic, image);
+  if (!USE_XWINDOW) return (TRUE);
+
+  Remap (graphic, image);
+  Refresh ();
+  XFlush (graphic[0].display);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColorScale.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColorScale.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColorScale.c	(revision 17422)
@@ -0,0 +1,243 @@
+# include "Ximage.h"
+
+void SetColorScale (Graphic *graphic, KapaImageWidget *image) {
+
+  switch (graphic[0].ColorScaleMode) {
+    case KAPA_SCALE_1D:
+      SetColorScale1D (graphic, image);
+      break;
+    case KAPA_SCALE_3D_RUFF:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
+      break;
+    case KAPA_SCALE_3D_FULL:
+      // fall-back on 1D colors (ie, if images are mis-matched)
+      if (!SetColorScale3D_CC (graphic, image)) {
+	graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+	SetColorScale1D (graphic, image);
+      }
+      break;
+    default:
+      fprintf (stderr, "programming error in kapa: unknown color scale mode\n");
+      return;
+  }
+  return;
+}
+
+void SetColorScale1D (Graphic *graphic, KapaImageWidget *image) {
+  int i, DX, DY, value, nPixels;
+  float *iData;
+  unsigned short *oData;
+  float slope;
+  float start;
+  unsigned short MaxValue;
+  Matrix *matrix;
+
+  // define the color transform parameters
+  MaxValue = graphic[0].Npixels - 1;
+  if (image[0].image[0].range != 0.0) {
+    slope = graphic[0].Npixels / image[0].image[0].range;
+    start = graphic[0].Npixels * image[0].image[0].zero / image[0].image[0].range;
+  } else {
+    slope = 1.0;
+    start = image[0].image[0].zero;
+  }
+
+  matrix = &image->image->matrix;
+
+  DX = matrix[0].Naxis[0];
+  DY = matrix[0].Naxis[1];
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  oData = image[0].pixmap;
+  iData = (float *) matrix[0].buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, iData++, oData++) {
+    value = *iData * slope - start;
+    if (value < 0) value = 0;
+    if (value > MaxValue) value = MaxValue;
+    *oData = value;
+  }
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+// XXX this uses a crude, uniform-spacing cube
+int SetColorScale3D (Graphic *graphic, KapaImageWidget *image) {
+
+  int i, DX, DY, nPixels, rValue, gValue, bValue;
+  float *rData, *bData, *gData;
+  unsigned short *oData;
+  float redSlope, blueSlope, greenSlope;
+  float redStart, blueStart, greenStart;
+
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
+  // define the color transform parameters
+  unsigned short maxRed = graphic[0].nRed - 1;
+  unsigned short maxBlue = graphic[0].nBlue - 1;
+  unsigned short maxGreen = graphic[0].nGreen - 1;
+
+  // set start & slope for red (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_RED].range != 0.0) {
+    redSlope = graphic[0].nRed / image[0].channel[KAPA_CHANNEL_RED].range;
+    redStart = graphic[0].nRed * image[0].channel[KAPA_CHANNEL_RED].zero / image[0].channel[KAPA_CHANNEL_RED].range;
+  } else {
+    redSlope = 1.0;
+    redStart = image[0].channel[KAPA_CHANNEL_RED].zero;
+  }
+  // set start & slope for blue (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_BLUE].range != 0.0) {
+    blueSlope = graphic[0].nBlue / image[0].channel[KAPA_CHANNEL_BLUE].range;
+    blueStart = graphic[0].nBlue * image[0].channel[KAPA_CHANNEL_BLUE].zero / image[0].channel[KAPA_CHANNEL_BLUE].range;
+  } else {
+    blueSlope = 1.0;
+    blueStart = image[0].channel[KAPA_CHANNEL_BLUE].zero;
+  }
+  // set start & slope for green (channel 0)
+  if (image[0].channel[KAPA_CHANNEL_GREEN].range != 0.0) {
+    greenSlope = graphic[0].nGreen / image[0].channel[KAPA_CHANNEL_GREEN].range;
+    greenStart = graphic[0].nGreen * image[0].channel[KAPA_CHANNEL_GREEN].zero / image[0].channel[KAPA_CHANNEL_GREEN].range;
+  } else {
+    greenSlope = 1.0;
+    greenStart = image[0].channel[KAPA_CHANNEL_GREEN].zero;
+  }
+
+  nPixels = DX*DY;
+  if (image[0].nPixels != nPixels) {
+    REALLOCATE (image[0].pixmap, unsigned short, nPixels);
+    image[0].nPixels = nPixels;
+  }
+
+  oData = image[0].pixmap;
+  rData = (float *) image[0].channel[KAPA_CHANNEL_RED].matrix.buffer;
+  bData = (float *) image[0].channel[KAPA_CHANNEL_BLUE].matrix.buffer;
+  gData = (float *) image[0].channel[KAPA_CHANNEL_GREEN].matrix.buffer;
+
+  // convert pixel data values to pixel index values (0 - Npixel)
+  for (i = 0; i < nPixels; i++, rData++, bData++, gData++, oData++) {
+    rValue = *rData * redSlope   - redStart;
+    if (rValue < 0) rValue = 0;
+    if (rValue > maxRed) rValue = maxRed;
+
+    bValue = *bData * blueSlope  - blueStart;
+    if (bValue < 0) bValue = 0;
+    if (bValue > maxBlue) bValue = maxBlue;
+
+    gValue = *gData * greenSlope - greenStart;
+    if (gValue < 0) gValue = 0;
+    if (gValue > maxGreen) gValue = maxGreen;
+
+    *oData = gValue + bValue * (maxGreen + 1) + rValue * (maxGreen + 1) * (maxBlue + 1);
+  }
+  return TRUE;
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+int SetColorCubeHistogram () {
+
+  int i, DX, DY, Nvalues, NVALUES, Npixels, Nmin;
+  float *values;
+  CCNode *cube;
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) return (FALSE);
+  image = section->image;
+
+  DX = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[0];
+  DY = image[0].channel[KAPA_CHANNEL_RED].matrix.Naxis[1];
+  if (DX != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_GREEN].matrix.Naxis[1]) return FALSE;
+  if (DX != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[0]) return FALSE;
+  if (DY != image[0].channel[KAPA_CHANNEL_BLUE].matrix.Naxis[1]) return FALSE;
+
+  // create the top-level cube
+  if (graphic[0].cube != NULL) {
+    CCNodeFree (graphic[0].cube);
+  }
+  graphic[0].cube = CCNodeAlloc();
+
+  cube = graphic[0].cube;
+  cube->min[CC_X] = cube->min[CC_Y] = cube->min[CC_Z] = 0.0;
+  cube->max[CC_X] = cube->max[CC_Y] = cube->max[CC_Z] = 1.0;
+  cube->mid[CC_X] = cube->mid[CC_Y] = cube->mid[CC_Z] = 0.5;
+
+  // subdivide cube into first, uniform division (4x4x4)
+  CCSplitNodeIterate (cube, 0, 2);
+
+  // need to allocate at least 64 + NPASS*7*NSPLIT pixels (overestimate if 64 < NSPLIT)
+# define NPASS 5
+# define NSPLIT 128
+
+  for (i = 0; i < NPASS; i++) {
+    // generate histogram
+    ColorHistogram (image, cube);
+
+    // extract histogram values
+    values  = NULL;
+    Nvalues = 0;
+    NVALUES = 0;
+    CCNodeExtractCounts (cube, &values, &Nvalues, &NVALUES);
+  
+    // select top N cells
+    fsort (values, Nvalues);
+
+    // split nodes with more than value[n]
+    Nmin = MAX (0, Nvalues - NSPLIT);
+    CCNodeDivideLimit (cube, values[Nmin]);
+    free (values);
+
+    CCNodeInitCounts (cube, 0.0);
+  }
+
+  // generate histogram
+  ColorHistogram (image, cube);
+  
+  // convert histogram 3D values to cmap colors
+  Npixels = 0;
+  CCNodeSetColorMap (cube, graphic[0].cmap, graphic[0].Npixels, &Npixels);
+
+  // store the colors
+  if (USE_XWINDOW) {
+    if (graphic[0].visualclass) {
+      XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, Npixels);
+    } else {
+      for (i = 0; i < Npixels; i++) {
+	if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+	  fprintf (stderr, "error on %d\n", i);
+	}
+      }
+    }
+  }
+  return TRUE;
+}
+
+// in 3D we use channels 0,1,2 to choose the pixel from the cube
+int SetColorScale3D_CC (Graphic *graphic, KapaImageWidget *image) {
+
+  CCNode *cube;
+
+  // create the top-level cube
+  cube = graphic[0].cube;
+  CCNodeSetColorPixels (image, cube);
+  return TRUE;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColormap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColormap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetColormap.c	(revision 17422)
@@ -0,0 +1,159 @@
+# include "Ximage.h"
+
+# define SETVALUE(VAR,VALUE,MINVAL,MAXVAL) { \
+  float tmp = (VALUE); \
+  if (tmp < MINVAL) { \
+    VAR = MINVAL; \
+  } else if (tmp > MAXVAL) { \
+    VAR = MAXVAL; \
+  } else { \
+    VAR = tmp; \
+  } }   
+
+int SetColormap (char *name) {
+
+  int i, red, blue, green;
+  float scale, blueRef, redRef, greenRef;
+  Graphic *graphic;
+
+  graphic = GetGraphic();
+
+  // the "fullcolor" colormap is uniquely defined for each image;
+  // defer to the 'SetColorScale' step
+  if (!strcasecmp (name, "fullcolor")) {
+    graphic[0].ColorScaleMode = KAPA_SCALE_3D_FULL;
+    if (SetColorCubeHistogram ()) return TRUE;
+  }
+
+  // very simple color model: evenly spaced cube 
+  if (!strcasecmp (name, "ruffcolor")) {
+      graphic[0].nRed  = pow (graphic[0].Npixels, 0.333);
+      graphic[0].nBlue = pow (graphic[0].Npixels, 0.333);
+      graphic[0].nGreen = graphic[0].Npixels / (graphic[0].nRed * graphic[0].nBlue);
+
+      // red,green,blue are values in range 0x0000 to 0xffff
+      float redScale = 0xffff / (graphic[0].nRed - 1);
+      float blueScale = 0xffff / (graphic[0].nBlue - 1);
+      float greenScale = 0xffff / (graphic[0].nGreen - 1);
+
+      i = 0;
+      for (red = 0; red < graphic[0].nRed; red++) {  
+	  for (blue = 0; blue < graphic[0].nBlue; blue++) {  
+	      for (green = 0; green < graphic[0].nGreen; green++, i++) {  
+		  SETVALUE (graphic[0].cmap[i].red,   red*redScale, 0, 0xffff);
+		  SETVALUE (graphic[0].cmap[i].blue,  blue*blueScale, 0, 0xffff);
+		  SETVALUE (graphic[0].cmap[i].green, green*greenScale, 0, 0xffff);
+		  graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+	      }
+	  }
+      }
+
+      // all other modes are 1D: set the flag:
+      graphic[0].ColorScaleMode = KAPA_SCALE_3D_RUFF;
+      goto store_colors;
+  }
+
+  // all other modes are 1D: set the flag:
+  graphic[0].ColorScaleMode = KAPA_SCALE_1D;
+
+  // red,green,blue are values in range 0x0000 to 0xffff
+  scale = 0xffff / (graphic[0].Npixels - 1);
+
+  /* greyscale */
+  if ((!strcasecmp (name, "grayscale")) || (!strcasecmp (name, "greyscale"))) {
+    for (i = 0; i < graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   0xffff - i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, 0xffff - i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  0xffff - i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* -grayscale */
+  if ((!strcasecmp (name, "-grayscale")) || (!strcasecmp (name, "-greyscale"))) {
+    for (i = 0; i < graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, i*scale, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* heat */
+  if (!strcasecmp (name, "Heat")) {
+    greenRef = 0.25*graphic[0].Npixels*scale*2.0;
+    blueRef  = 0.50*graphic[0].Npixels*scale*2.0;
+    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   2*i*scale, 0, 0xffff);
+      graphic[0].cmap[i].green = 0;
+      graphic[0].cmap[i].blue  = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = 0.25*graphic[0].Npixels; i < 0.50*graphic[0].Npixels; i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   2*i*scale,            0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
+      graphic[0].cmap[i].blue = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red = 0xffff;
+      SETVALUE (graphic[0].cmap[i].green, 2*i*scale - greenRef, 0, 0xffff);
+      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
+      graphic[0].cmap[i].red   = 0xffff;
+      graphic[0].cmap[i].green = 0xffff;
+      SETVALUE (graphic[0].cmap[i].blue,  2*i*scale - blueRef,  0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  /* rainbow */
+  if (!strcasecmp (name, "Rainbow")) {
+    redRef   = 0.25*graphic[0].Npixels*scale*4.0;
+    greenRef = 0.50*graphic[0].Npixels*scale*4.0;
+    blueRef  = 0.50*graphic[0].Npixels*scale*4.0;
+    for (i = 0; i < (int)(0.25*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red   = 0;
+      graphic[0].cmap[i].green = 0;
+      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale,           0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.25*graphic[0].Npixels); i < (int)(0.50*graphic[0].Npixels); i++) {  
+      SETVALUE (graphic[0].cmap[i].red,   4*i*scale - redRef,  0, 0xffff);
+      graphic[0].cmap[i].green = 0;
+      SETVALUE (graphic[0].cmap[i].blue,  blueRef - 4*i*scale, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    for (i = (int)(0.50*graphic[0].Npixels); i < (int)(0.75*graphic[0].Npixels); i++) {  
+      graphic[0].cmap[i].red  = 0xffff;
+      SETVALUE (graphic[0].cmap[i].green,  4*i*scale - greenRef, 0, 0xffff);
+      graphic[0].cmap[i].blue = 0;
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    blueRef  = 0.75*graphic[0].Npixels*scale*4.0;
+    for (i = (int)(0.75*graphic[0].Npixels); i < graphic[0].Npixels; i++) {  
+      graphic[0].cmap[i].red   = 0xffff;
+      graphic[0].cmap[i].green = 0xffff;
+      SETVALUE (graphic[0].cmap[i].blue,  4*i*scale - blueRef, 0, 0xffff);
+      graphic[0].cmap[i].flags = DoRed | DoGreen | DoBlue;
+    }
+    goto store_colors;
+  }
+  return (FALSE);
+
+ store_colors:
+  if (!USE_XWINDOW) return (TRUE);
+  if (graphic[0].visualclass) {
+    XStoreColors(graphic[0].display, graphic[0].colormap, graphic[0].cmap, graphic[0].Npixels);
+  } else {
+    for (i = 0; i < graphic[0].Npixels; i++) {
+      if (XAllocColor (graphic[0].display, graphic[0].colormap, &graphic[0].cmap[i]) == 0) {
+	fprintf (stderr, "error on %d\n", i);
+      }
+    }
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetFont.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetFont.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetFont.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "Ximage.h"
+
+int SetFont (int sock) {
+  
+  char name[64];
+  int size;
+  
+  KiiScanCommand (sock, 16, "%s", name);
+  KiiScanCommand (sock, 16, "%d", &size);
+
+  SetRotFont (name, size);
+  
+  return (TRUE);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphData.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphData.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphData.c	(revision 17422)
@@ -0,0 +1,119 @@
+# include "Ximage.h"
+
+int SetGraphData (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  // get graph data from client 
+  KiiScanMessage (sock, "%d %d %d %d %d %d %lf %lf", 
+		  &graph[0].data.style, 
+		  &graph[0].data.ptype, 
+		  &graph[0].data.ltype, 
+		  &graph[0].data.etype, 
+		  &graph[0].data.ebar, 
+		  &graph[0].data.color, 
+		  &graph[0].data.lweight, 
+		  &graph[0].data.size);
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].data.xmin, 
+		  &graph[0].data.xmax, 
+		  &graph[0].data.ymin, 
+		  &graph[0].data.ymax);
+
+  KiiScanMessage (sock, "%f %f %f %f", 
+		  &graph[0].data.coords.pc1_1, &graph[0].data.coords.pc2_2,
+		  &graph[0].data.coords.pc1_2, &graph[0].data.coords.pc2_1);
+
+  KiiScanMessage (sock, "%d %d %s", 
+		  &graph[0].data.flipeast, &graph[0].data.flipnorth,
+		  graph[0].data.coords.ctype);
+
+  KiiScanMessage (sock, "%lf %lf %f %f %f %f", 
+		  &graph[0].data.coords.crval1,
+		  &graph[0].data.coords.crval2,
+		  &graph[0].data.coords.crpix1,
+		  &graph[0].data.coords.crpix2,
+		  &graph[0].data.coords.cdelt1,
+		  &graph[0].data.coords.cdelt2);
+
+
+
+  xmin = graph[0].data.xmin;
+  xmax = graph[0].data.xmax;
+  ymin = graph[0].data.ymin;
+  ymax = graph[0].data.ymax;
+
+  // XXX there are now two things which track the graph limits
+  // make sure these are kept in sync (or drop one!)
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetGraphData (int sock) {
+  
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiSendMessage (sock, "%8d %d %d %d %d %d %f %f", 
+		  graph[0].data.style, 
+		  graph[0].data.ptype, graph[0].data.ltype, 
+		  graph[0].data.etype, graph[0].data.ebar, graph[0].data.color, 
+		  graph[0].data.lweight, graph[0].data.size);
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].data.xmin, graph[0].data.xmax, 
+		  graph[0].data.ymin, graph[0].data.ymax);
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].data.coords.pc1_1, graph[0].data.coords.pc2_2,
+		  graph[0].data.coords.pc1_2, graph[0].data.coords.pc2_1);
+
+  KiiSendMessage (sock, "%d %d %s", 
+		  graph[0].data.flipeast, graph[0].data.flipnorth,
+		  graph[0].data.coords.ctype);
+
+  KiiSendMessage (sock, "%g %g %g %g %g %g", 
+		  graph[0].data.coords.crval1,
+		  graph[0].data.coords.crval2,
+		  graph[0].data.coords.crpix1,
+		  graph[0].data.coords.crpix2,
+		  graph[0].data.coords.cdelt1,
+		  graph[0].data.coords.cdelt2);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphSize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphSize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphSize.c	(revision 17422)
@@ -0,0 +1,128 @@
+# include "Ximage.h"
+
+/* Set the dimensions of the specific graph based on the current window size.  The graph
+   is placed within the window at the fractional position defined by the section */
+
+void SetGraphSize (Section *section) {
+
+  int fontsize, bump, Nc;
+  int textpad, textdY, WdY; 
+  double PADx, PADy, Dx, Dy;
+  double PXm, PXp, PYm, PYp;
+  double X0, Y0, dX, dY;
+  char string[64], *fontname;
+  KapaGraphWidget *graph;
+  Graphic *graphic;
+
+  if (section == NULL) return;
+  graph = section->graph;
+  if (graph == NULL) return;
+
+  graphic = GetGraphic ();
+  fontname = GetRotFont (&fontsize);
+
+  PADx = MAX (graphic->dx / 20.0, fontsize);
+  PADy = MAX (graphic->dy / 20.0, fontsize);
+  Dx = graphic->dx - 2*PADx;
+  Dy = graphic->dy - 2*PADy;
+
+  /* each graph has a padding PXm, PXp, PYm, PYp */
+  PXm = (graph[0].axis[1].islabel) ? 4*fontsize : 0;
+  PXp = (graph[0].axis[3].islabel) ? 4*fontsize : 0;
+  PYm = (graph[0].axis[0].islabel) ? 4*fontsize : 0;
+  PYp = (graph[0].axis[2].islabel) ? 4*fontsize : 0;
+ 
+  /* basic size of the graph in Xwindow coordinates */
+  X0 = PADx + PXm + (Dx * section[0].x);
+  Y0 = PADy + PYm + (Dy * section[0].y);
+  dX = (Dx * section[0].dx) - PXp - PXm;
+  dY = (Dy * section[0].dy) - PYp - PYm;
+
+  // if we are tied to an image, make mods as needed
+  if (section->image) {
+    textpad = USE_XWINDOW ? graphic[0].font[0].ascent : 10;
+    textdY = 6*textpad + 7*PAD1;
+    WdY = MAX (ZOOM_Y, textdY + 2*BUTTON_HEIGHT + PAD1);
+
+    switch (section->image->location) {
+      case 1:
+	Y0 = graphic[0].dy * section[0].y + 2*PAD1 + WdY + 2; // tied to image in Y
+	dY = graphic[0].dy * section[0].dy - 5*PAD1 - WdY - COLORPAD + 1;
+	break;
+      case 3:
+	dY = graphic[0].dy * section[0].dy - 5*PAD1 - WdY - COLORPAD - PADy - PYm;
+	break;
+      case 2:
+	X0 = graphic[0].dx * section[0].x  + 2*PAD1 + ZOOM_X;
+	dX = graphic[0].dx * section[0].dx - 3*PAD1 - ZOOM_X;
+	break;
+      case 4:
+	dX = graphic[0].dx * section[0].dx - 3*PAD1 - ZOOM_X - PADx - PXm;
+	break;
+    }
+  }
+
+  /* define locations of coordinate axes */
+  graph[0].axis[0].fx  = X0;
+  graph[0].axis[0].fy  = graphic->dy - Y0;
+  graph[0].axis[0].dfx = dX;
+  graph[0].axis[0].dfy = 0;
+  
+  graph[0].axis[1].fx  = X0;
+  graph[0].axis[1].fy  = graphic->dy - Y0;
+  graph[0].axis[1].dfx = 0;
+  graph[0].axis[1].dfy = -dY;
+
+  graph[0].axis[2].fx  = X0;
+  graph[0].axis[2].fy  = graphic->dy - Y0 - dY;
+  graph[0].axis[2].dfx = dX;
+  graph[0].axis[2].dfy = 0;
+
+  graph[0].axis[3].fx  = X0 + dX;
+  graph[0].axis[3].fy  = graphic->dy - Y0;
+  graph[0].axis[3].dfx = 0;
+  graph[0].axis[3].dfy = -dY;
+
+  PADx = 0.8*fontsize + 2;
+  PADy = 3.0*fontsize + 4;
+
+  /* define locations of axis labels */
+  graph[0].label[LABELX0].x = graph[0].axis[0].fx + 0.5*graph[0].axis[0].dfx;
+  bump = (graph[0].axis[0].islabel) ? PADy : PADx;
+  graph[0].label[LABELX0].y = graph[0].axis[0].fy + bump;
+
+  graph[0].label[LABELX1].x = graph[0].axis[2].fx + 0.5*graph[0].axis[2].dfx;
+  bump = (graph[0].axis[2].islabel) ? PADy : PADx;
+  graph[0].label[LABELX1].y = graph[0].axis[2].fy - bump;
+
+  /* check for the max size of the axis label */
+  sprintf (string, "%4g", graph[0].axis[1].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", graph[0].axis[1].max);
+  Nc = MAX (Nc, strlen (string));
+
+  graph[0].label[LABELY0].y = graph[0].axis[1].fy + 0.5*graph[0].axis[1].dfy;
+  bump = (graph[0].axis[1].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  graph[0].label[LABELY0].x = graph[0].axis[1].fx - bump;
+
+  sprintf (string, "%4g", graph[0].axis[3].min);
+  Nc = strlen (string);
+  sprintf (string, "%4g", graph[0].axis[3].max);
+  Nc = MAX (Nc, strlen (string));
+
+  graph[0].label[LABELY1].y = graph[0].axis[3].fy + 0.5*graph[0].axis[3].dfy;
+  bump = (graph[0].axis[3].islabel) ? (0.8*Nc*fontsize + 1) : PADx;
+  graph[0].label[LABELY1].x = graph[0].axis[3].fx + bump;
+  
+  /* these are wrong and have to be adjusted to sit in the corners */
+
+  graph[0].label[LABELUL].x = graph[0].axis[2].fx - PADx;
+  graph[0].label[LABELUL].y = graph[0].axis[2].fy - PADx;
+  graph[0].label[LABELUR].x = graph[0].axis[2].fx + graph[0].axis[2].dfx + PADx;
+  graph[0].label[LABELUR].y = graph[0].axis[2].fy - PADx;
+  graph[0].label[LABELLL].x = graph[0].axis[0].fx - PADx;
+  graph[0].label[LABELLL].y = graph[0].axis[0].fy + PADx;
+  graph[0].label[LABELLR].x = graph[0].axis[0].fx + graph[0].axis[0].dfx + PADx;
+  graph[0].label[LABELLR].y = graph[0].axis[0].fy + PADx;
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphStyle.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphStyle.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetGraphStyle.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "Ximage.h"
+
+int SetGraphStyle (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  // get graph style from client 
+  KiiScanMessage (sock, "%d %d %d %d %d %d %lf %lf", 
+		  &graph[0].style.style, 
+		  &graph[0].style.ptype, 
+		  &graph[0].style.ltype, 
+		  &graph[0].style.etype, 
+		  &graph[0].style.ebar, 
+		  &graph[0].style.color, 
+		  &graph[0].style.lweight, 
+		  &graph[0].style.size);
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", 
+		  &graph[0].style.xmin, 
+		  &graph[0].style.xmax, 
+		  &graph[0].style.ymin, 
+		  &graph[0].style.ymax);
+
+  xmin = graph[0].style.xmin;
+  xmax = graph[0].style.xmax;
+  ymin = graph[0].style.ymin;
+  ymax = graph[0].style.ymax;
+
+  // XXX there are now two things which track the graph limits
+  // make sure these are kept in sync (or drop one!)
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetGraphStyle (int sock) {
+  
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+
+  KiiSendMessage (sock, "%8d %d %d %d %d %d %f %f", 
+		  graph[0].style.style, 
+		  graph[0].style.ptype, graph[0].style.ltype, 
+		  graph[0].style.etype, graph[0].style.ebar, graph[0].style.color, 
+		  graph[0].style.lweight, graph[0].style.size);
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  graph[0].style.xmin, graph[0].style.xmax, 
+		  graph[0].style.ymin, graph[0].style.ymax);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageData.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageData.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageData.c	(revision 17422)
@@ -0,0 +1,134 @@
+# include "Ximage.h"
+
+int SetImageData (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  // get image data from client 
+  KiiScanMessage (sock, "%lf %lf %s", 
+		  &image[0].image[0].zero, 
+		  &image[0].image[0].range, 
+		  image[0].image[0].name, 
+		  image[0].image[0].file);
+
+  // XXX when we go to 32bit, this should remap the image
+  // if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  // Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetImageData (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiSendMessage (sock, "%g %g %s %s", 
+		  image[0].image[0].zero,
+		  image[0].image[0].range,
+		  image[0].image[0].name,
+		  image[0].image[0].file);
+
+  return (TRUE);
+}
+
+int SetImageCoords (int sock) {
+  
+  Graphic *graphic;
+  Section *section;
+  KapaImageWidget *image;
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+  
+  KiiScanMessage (sock, "%f %f %f %f", 
+		  &image[0].image[0].coords.pc1_1, &image[0].image[0].coords.pc2_2,
+		  &image[0].image[0].coords.pc1_2, &image[0].image[0].coords.pc2_1);
+
+  KiiScanMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  KiiScanMessage (sock, "%lf %lf %f %f %f %f", 
+		  &image[0].image[0].coords.crval1,
+		  &image[0].image[0].coords.crval2,
+		  &image[0].image[0].coords.crpix1,
+		  &image[0].image[0].coords.crpix2,
+		  &image[0].image[0].coords.cdelt1,
+		  &image[0].image[0].coords.cdelt2);
+
+  return (TRUE);  
+}
+
+int GetImageCoords (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  KiiSendMessage (sock, "%g %g %g %g", 
+		  image[0].image[0].coords.pc1_1, image[0].image[0].coords.pc2_2,
+		  image[0].image[0].coords.pc1_2, image[0].image[0].coords.pc2_1);
+
+  KiiSendMessage (sock, "%s", image[0].image[0].coords.ctype);
+
+  KiiSendMessage (sock, "%g %g %g %g %g %g", 
+		  image[0].image[0].coords.crval1,
+		  image[0].image[0].coords.crval2,
+		  image[0].image[0].coords.crpix1,
+		  image[0].image[0].coords.crpix2,
+		  image[0].image[0].coords.cdelt1,
+		  image[0].image[0].coords.cdelt2);
+
+  return (TRUE);
+}
+
+int GetImageRange (int sock) {
+  
+  Section *section;
+  KapaImageWidget *image;
+  double Xmin, Xmax, Ymin, Ymax;
+
+  section = GetActiveSection();
+  if (section->image == NULL) {
+    section->image = InitImageWidget ();
+    SetSectionSizes (section);
+  }
+  image = section->image;
+
+  Picture_to_Image (&Xmin, &Ymin, 0.0, 0.0, &image[0].picture);
+  Picture_to_Image (&Xmax, &Ymax, image[0].picture.dx, image[0].picture.dy, &image[0].picture);
+
+  KiiSendMessage (sock, "%g %g %g %g", Xmin, Xmax, Ymin, Ymax);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageSize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageSize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetImageSize.c	(revision 17422)
@@ -0,0 +1,380 @@
+# include "Ximage.h"
+
+/* Set the dimensions of the specific image based on the current window size.  The image
+   is placed within the window at the fractional position defined by the section */
+
+// XXX currently only set to fill the window; does not adjust for section range
+void SetImageSize (Section *section) {
+
+  int Xs, Ys, dX, dY;
+  int textpad, textdY, WdY; 
+  KapaImageWidget *image;
+  KapaGraphWidget *graph;
+  Graphic *graphic;
+
+  if (section == NULL) return;
+  image = section->image;
+  if (image == NULL) return;
+  graph = section->graph;
+
+  graphic = GetGraphic ();
+
+  /* the image is placed within the graphic window in region specified by section */
+  Xs = graphic[0].dx * section[0].x;
+  Ys = graphic[0].dy * (1 - section[0].y - section[0].dy);
+  dX = graphic[0].dx * section[0].dx;
+  dY = graphic[0].dy * section[0].dy;
+
+  textpad = USE_XWINDOW ? graphic[0].font[0].ascent : 10;
+  textdY = 6*textpad + 7*PAD1;
+  WdY = MAX (ZOOM_Y, textdY + 2*BUTTON_HEIGHT + PAD1);
+
+  switch (image[0].location) {
+
+    case 0: // no zoom / status / wide
+      if (section->graph) {
+	  image[0].picture.x  = graph[0].axis[0].fx;
+	  image[0].picture.y  = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	  image[0].picture.dx = MAX(graph[0].axis[0].dfx + 1, 1);
+	  image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	  image[0].picture.x  = Xs + PAD1;
+	  image[0].picture.y  = Ys + PAD1;
+	  image[0].picture.dx = dX - 2*PAD1; 
+	  image[0].picture.dy = dY - 2*PAD1;
+      }
+      if (USE_XWINDOW) CreatePicture (image, graphic);
+      Remap (graphic, image);
+      return;
+
+    case 1: // zoom / status / wide on bottom
+
+      if (section->graph) {
+	  image[0].picture.x = graph[0].axis[0].fx;
+	  image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+	  image[0].picture.dx = graph[0].axis[0].dfx;
+	  image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+      } else {
+	  image[0].picture.x  = Xs + PAD1;
+	  image[0].picture.y  = Ys + 2*PAD1 + COLORPAD;
+	  image[0].picture.dx = dX - 2*PAD1; 
+	  image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = image[0].picture.y + image[0].picture.dy + PAD1;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x + image[0].zoom.dx + PAD1;
+      image[0].text_y = image[0].zoom.y;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].text_x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].recenter_button.x + image[0].recenter_button.dx + PAD1;
+      image[0].flipx_button.y = image[0].recenter_button.y;
+
+      image[0].flipy_button.x = image[0].hms_button.x + image[0].hms_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].hms_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].wide.y = image[0].zoom.y;
+      break;
+
+    case 3: // zoom / status / wide on top
+
+      if (section->graph) {
+	image[0].picture.x = graph[0].axis[0].fx;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = MAX(graph[0].axis[0].dfx, 1);
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 2*PAD1; 
+	image[0].picture.dy = dY - 5*PAD1 - WdY - COLORPAD;
+	image[0].picture.x = Xs + PAD1;
+	image[0].picture.y = Ys + 4*PAD1 + COLORPAD + WdY;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = Ys + 2*PAD1 + COLORPAD;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x + image[0].zoom.dx + PAD1;
+      image[0].text_y = image[0].zoom.y;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].text_x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].recenter_button.x + image[0].recenter_button.dx + PAD1;
+      image[0].flipx_button.y = image[0].recenter_button.y;
+
+      image[0].flipy_button.x = image[0].hms_button.x + image[0].hms_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].hms_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].wide.y = image[0].zoom.y;
+      break;
+
+    case 2: // zoom / status / wide on left
+
+      if (section->graph) {
+	image[0].picture.x = Xs + 2*PAD1 + ZOOM_X;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = dY - 3*PAD1 - COLORPAD;
+	image[0].picture.x = Xs + 2*PAD1 + ZOOM_X;
+	image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = Xs + PAD1;
+      image[0].zoom.y = image[0].picture.y;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x;
+      image[0].text_y = image[0].zoom.y + image[0].zoom.dy + PAD1;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].zoom.x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].PS_button.x;
+      image[0].flipx_button.y = image[0].PS_button.y + BUTTON_HEIGHT + PAD1;
+
+      image[0].flipy_button.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].flipx_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x;
+      image[0].wide.y = image[0].flipx_button.y + BUTTON_HEIGHT + PAD1;
+      break;
+
+    case 4:  // zoom / status / wide on right
+
+      if (section->graph) {
+	image[0].picture.x = graph[0].axis[0].fx;
+	image[0].picture.y = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X - graph[0].axis[0].fx; 
+	image[0].picture.dy = MAX(fabs(graph[0].axis[1].dfy) - 1, 1);
+      } else {
+	image[0].picture.dx = dX - 3*PAD1 - ZOOM_X; 
+	image[0].picture.dy = dY - 3*PAD1 - COLORPAD;
+	image[0].picture.x = Xs + PAD1;
+	image[0].picture.y = Ys + 2*PAD1 + COLORPAD;
+      }
+
+      image[0].cmapbar.dx = dX - 2*PAD1; 
+      image[0].cmapbar.dy = COLORPAD;
+      image[0].cmapbar.x = Xs + PAD1;
+      image[0].cmapbar.y = Ys + PAD1;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].zoom.dx = ZOOM_X; 
+      image[0].zoom.dy = ZOOM_Y;
+      image[0].zoom.x = image[0].picture.x + image[0].picture.dx + PAD1;
+      image[0].zoom.y = image[0].picture.y;
+
+      /** everything below is tied in x-dir to the zoom box **/
+      image[0].text_x = image[0].zoom.x;
+      image[0].text_y = image[0].zoom.y + image[0].zoom.dy + PAD1;
+      image[0].text_dx = ZOOM_X;
+      image[0].text_dy = 6*textpad + 7*PAD1;
+      image[0].text_dyo = 3*textpad + 4*PAD1;
+
+      image[0].overlay_button[0].x = image[0].text_x;
+      image[0].overlay_button[0].y = image[0].text_y + image[0].text_dy + PAD1;
+   
+      image[0].overlay_button[1].x = image[0].overlay_button[0].x + image[0].overlay_button[0].dx + PAD1;
+      image[0].overlay_button[1].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[2].x = image[0].overlay_button[1].x + image[0].overlay_button[1].dx + PAD1;
+      image[0].overlay_button[2].y = image[0].overlay_button[0].y;
+
+      image[0].overlay_button[3].x = image[0].overlay_button[2].x + image[0].overlay_button[2].dx + PAD1;
+      image[0].overlay_button[3].y = image[0].overlay_button[0].y;
+
+      image[0].hms_button.x = image[0].overlay_button[3].x + image[0].overlay_button[3].dx + PAD1;
+      image[0].hms_button.y = image[0].overlay_button[0].y;
+
+      image[0].PS_button.x = image[0].zoom.x;
+      image[0].PS_button.y = image[0].overlay_button[0].y + BUTTON_HEIGHT + PAD1;
+
+      /** everything below is tied to the PS_button in y-dir + the neighbor in x-dir **/
+      image[0].grey_button.x = image[0].PS_button.x + image[0].PS_button.dx + PAD1;
+      image[0].grey_button.y = image[0].PS_button.y;
+
+      image[0].rainbow_button.x = image[0].grey_button.x + image[0].grey_button.dx + PAD1;
+      image[0].rainbow_button.y = image[0].PS_button.y;
+
+      image[0].heat_button.x = image[0].rainbow_button.x + image[0].rainbow_button.dx + PAD1;
+      image[0].heat_button.y = image[0].PS_button.y;
+
+      image[0].recenter_button.x = image[0].heat_button.x + image[0].heat_button.dx + PAD1;
+      image[0].recenter_button.y = image[0].PS_button.y;
+
+      // add just below
+      image[0].flipx_button.x = image[0].zoom.x;
+      image[0].flipx_button.y = image[0].PS_button.y + BUTTON_HEIGHT + PAD1;
+
+      image[0].flipy_button.x = image[0].flipx_button.x + image[0].flipx_button.dx + PAD1;
+      image[0].flipy_button.y = image[0].flipx_button.y;
+
+      // XXX zoom should scale somewhat with the image? (with a min and a max)
+      // XXX actually, it is limited by the buttons and status region
+      image[0].wide.dx = ZOOM_X; 
+      image[0].wide.dy = ZOOM_Y;
+      image[0].wide.x = image[0].flipx_button.x;
+      image[0].wide.y = image[0].flipx_button.y + BUTTON_HEIGHT + PAD1;
+      break;
+
+    default:
+      abort ();
+      break;
+  }
+
+  if (USE_XWINDOW) {
+    CreatePicture (image, graphic);
+    CreateColorbar (image, graphic);
+    CreateZoom (graphic, image); 
+    CreateWide (graphic, image); 
+  }
+  Remap (graphic, image);
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetLimits.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetLimits.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetLimits.c	(revision 17422)
@@ -0,0 +1,60 @@
+# include "Ximage.h"
+
+int SetLimits (int sock) {
+  
+  int i;
+  double xmin, xmax, ymin, ymax;
+  Graphic *graphic;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  KiiScanMessage (sock, "%lf %lf %lf %lf", &xmin, &xmax, &ymin, &ymax);
+
+  graphic = GetGraphic();
+
+  section = GetActiveSection();
+  if (section->graph == NULL) {
+    section->graph = InitGraph ();
+    SetSectionSizes (section);
+  }
+  graph = section->graph;
+  
+  graph[0].axis[2].min = graph[0].axis[0].min = xmin;
+  graph[0].axis[2].max = graph[0].axis[0].max = xmax;
+  graph[0].axis[3].min = graph[0].axis[1].min = ymin; 
+  graph[0].axis[3].max = graph[0].axis[1].max = ymax;
+  
+  for (i = 0; i < graph[0].Nobjects; i++) {
+    graph[0].objects[i].x0 = xmin;
+    graph[0].objects[i].x1 = xmax;
+    graph[0].objects[i].y0 = ymin;
+    graph[0].objects[i].y1 = ymax;
+  }
+
+  if (USE_XWINDOW) XClearWindow (graphic->display, graphic->window);
+  Refresh (1);
+
+  return (TRUE);  
+}
+
+int GetLimits (int sock) {
+  
+  double dX, dY;
+  Section *section;
+  KapaGraphWidget *graph;
+
+  section = GetActiveSection();
+  graph = section->graph;
+
+  if (graph == NULL) {
+    dX = 0.0;
+    dY = 0.0;
+  } else {
+    dX = graph[0].axis[0].dfx;
+    dY = graph[0].axis[1].dfy;
+  }
+
+  KiiSendMessage (sock, "%8.1f %8.1f ", dX, dY);
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetNormalHints.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetNormalHints.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetNormalHints.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/************** SetNormalHints  *************/
+void SetNormalHints (Graphic *graphic) {
+
+  XSizeHints *sizehints;
+
+  sizehints = XAllocSizeHints ();
+  if (sizehints == NULL) return;
+
+  sizehints[0].x = graphic->x;
+  sizehints[0].y = graphic->x;
+  sizehints[0].width = graphic->dx;
+  sizehints[0].height = graphic->dy;
+  sizehints[0].min_width = MIN_WIDTH;
+  sizehints[0].min_height = MIN_HEIGHT;    
+
+  // XXX : can we drop the position flag 
+  // sizehints[0].flags = USPosition | USSize | PMinSize;
+  sizehints[0].flags = USSize | PMinSize;
+
+  sizehints[0].base_width = graphic->dx;
+  sizehints[0].base_height = graphic->dy;
+  sizehints[0].flags |= PBaseSize;
+    
+  XSetWMNormalHints (graphic->display, graphic->window, sizehints);
+  XFree (sizehints);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetSection.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetSection.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetSection.c	(revision 17422)
@@ -0,0 +1,19 @@
+# include "Ximage.h"
+
+// set active section
+int SetSection (int sock) {
+  
+  int N;
+  char name[128];
+
+  KiiScanMessage (sock, "%s", name);
+  
+  N = GetSectionByName (name);
+  if (N < 0) {
+    fprintf (stderr, "section %s not found\n", name);
+    return (TRUE);
+  }
+
+  SetActiveSectionByNumber (N);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetToolbox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetToolbox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetToolbox.c	(revision 17422)
@@ -0,0 +1,33 @@
+# include "Ximage.h"
+
+// set the position of the image toolbox
+void SetToolbox (int sock) {
+
+  int location;
+  Section *section;
+  Graphic *graphic;
+
+  KiiScanMessage (sock, "%d", &location);
+  if ((location < 0) || (location > 4)) {
+    fprintf (stderr, "invalid toolbox location %d\n", location);
+    return;
+  }
+
+  graphic = GetGraphic ();
+  section = GetActiveSection();
+  if (section->image == NULL) { 
+    section->image = InitImageWidget ();
+  }
+  section->image->location = location;
+  SetSectionSizes (section);
+
+  if (!USE_XWINDOW) return;
+
+  Remap (graphic, section->image);
+  if (DEBUG) fprintf (stderr, "remapped image\n");
+  Refresh ();
+  if (DEBUG) fprintf (stderr, "refreshed\n");
+  XFlush (graphic->display);
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetUpGraphic.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "Ximage.h"
+# include "icons.h"
+
+static Graphic *graphic = NULL;
+
+/************** SetUpDisplay *************/
+void SetUpGraphic (int *argc, char **argv) {
+
+  Icon icon;
+  char *name;
+  int Ncolors;
+
+  ALLOCATE (graphic, Graphic, 1);
+  graphic->x  = 10;
+  graphic->y  = 10;
+  graphic->dx = 512;
+  graphic->dy = 512; 
+
+  if (!USE_XWINDOW) {
+    ALLOCATE (graphic[0].pixels, unsigned long, NPIXELS_STATIC);
+    ALLOCATE (graphic[0].cmap,   XColor,        NPIXELS_STATIC);
+    graphic[0].Npixels = NPIXELS_STATIC;
+    graphic[0].cube = NULL;
+    return;
+  }
+
+  name = CheckDisplayName (argc, argv);
+
+  graphic->display  = OpenDisplay     (name,    &graphic->screen);
+  graphic->colormap = DefaultColormap (graphic->display, graphic->screen);
+  graphic->depth    = DefaultDepth    (graphic->display, graphic->screen);
+  graphic->visual   = DefaultVisual   (graphic->display, graphic->screen);
+  if (name != NULL) free (name);
+
+  CheckVisual (graphic, argc, argv);
+  CheckColors (graphic, argc, argv);
+
+  icon.width = icon_width;
+  icon.height = icon_height;
+  icon.bits = icon_bits;
+
+  CheckGeometry (graphic, argc, argv);
+  TopWindow (graphic, &icon);
+  LoadFont (graphic, argc, argv, "fixed"); 
+
+  // FlushDisplay (graphic);
+  XSetWindowBackground (graphic->display, graphic->window, graphic->back);
+
+  SetNormalHints (graphic);
+  SetWMHints (graphic, &icon);
+
+  if (NAME_WINDOW == NULL) {
+    NameWindow (graphic, "Kapa");
+  } else {
+    ALLOCATE (name, char, strlen(NAME_WINDOW) + 10);
+    sprintf (name, "Kapa %s", NAME_WINDOW);
+    NameWindow (graphic, name);
+    free (name);
+  }
+
+  graphic->color = KapaX11colors (graphic->display, graphic->colormap, graphic->fore, &Ncolors);
+  if (MAP_WINDOW) MapWindow (graphic);
+  return;
+}
+
+Graphic *GetGraphic () {
+  return graphic;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetWMHints.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetWMHints.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/SetWMHints.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+/************** SetWMHints  *************/
+void SetWMHints (Graphic *graphic, Icon *icon) {
+
+  XWMHints *wmhints;
+
+  wmhints = XAllocWMHints ();
+  if (wmhints == NULL) return;
+
+  wmhints[0].initial_state = NormalState;
+  wmhints[0].input = True;
+  if (icon[0].pixmap != (Pixmap) None) {
+    wmhints[0].icon_pixmap = icon[0].pixmap;
+    wmhints[0].icon_mask = icon[0].pixmap;
+    wmhints[0].flags = StateHint | InputHint | IconPixmapHint | IconMaskHint;
+  } else {
+    wmhints[0].flags = StateHint | InputHint;
+  }
+    
+  XSetWMHints (graphic->display, graphic->window, wmhints);
+  XFree (wmhints);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/StatusBox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/StatusBox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/StatusBox.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "Ximage.h"
+
+void StatusBox (Graphic *graphic, KapaImageWidget *image) {
+
+  double  x, y, z;
+
+  z = -1;
+
+  if (image[0].MovePointer) {
+    x = 0.5*image[0].image[0].matrix.Naxis[0];
+    y = 0.5*image[0].image[0].matrix.Naxis[1];
+    // z = -1;
+    image[0].zoom.X = x;
+    image[0].zoom.Y = y;
+    // image[0].z = z;
+  } else {
+    x = image[0].zoom.X;
+    y = image[0].zoom.Y;
+    // z = image[0].z;
+  }
+  
+  UpdateStatusBox (graphic, image, x, y, z, 1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/TopWindow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/TopWindow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/TopWindow.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "Ximage.h"
+
+/************** TopWindow *************/
+void TopWindow (Graphic *graphic, Icon *icon) {
+
+  Window rootwindow;
+
+  rootwindow = RootWindow (graphic->display, graphic->screen);
+
+  CreateWindow (graphic, rootwindow, BORDER_WIDTH, EVENT_MASK);
+  MakeGC (graphic);
+
+  icon[0].pixmap = XCreateBitmapFromData (graphic->display, graphic->window, (char *) icon[0].bits, icon[0].width, icon[0].height);
+
+  MakeCursor (graphic, DEFAULT_CURSOR);
+  XFreeCursor (graphic->display, graphic->cursor);
+  // FlushDisplay (graphic);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdatePointer.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdatePointer.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdatePointer.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "Ximage.h"
+
+int UpdatePointer (Graphic *graphic, XMotionEvent *event) {
+
+  int textpad;
+  double  x, y, z;
+  float *data;
+  char line[100];
+  Section *section;
+  KapaImageWidget *image;
+
+  // XXX select the window element which contains the event
+  section = GetActiveSection();
+  image   = section->image;
+  if (image == NULL) return (TRUE);
+  if (!image[0].location) return (TRUE);
+
+  if (image[0].MovePointer && InPicture ((XButtonEvent *)event, &image[0].picture)) {
+
+    data = (float *) image[0].image[0].matrix.buffer;
+    Screen_to_Image (&x, &y, event[0].x + 0.5, event[0].y + 0.5, &image[0].picture);
+
+    z = -1;
+    if (x < 0) goto skip;
+    if (x >= image[0].image[0].matrix.Naxis[0]) goto skip;
+    if (y < 0) goto skip;
+    if (y >= image[0].image[0].matrix.Naxis[1]) goto skip;
+    z = data[(int)(y)*image[0].image[0].matrix.Naxis[0] + (int)(x)];
+
+  skip:
+    image[0].zoom.X = x;
+    image[0].zoom.Y = y;
+    
+    UpdateStatusBox (graphic, image, x, y, z, 0);
+    XFlush (graphic[0].display);
+      
+    CreateZoom (graphic, image);  
+    XPutImage (graphic[0].display, graphic[0].window, graphic[0].gc,
+	       image[0].zoom.pix, 0, 0, 
+	       image[0].zoom.x, image[0].zoom.y, 
+	       image[0].zoom.dx, image[0].zoom.dy);
+    CrossHairs (graphic, &image[0].zoom);
+    XFlush (graphic[0].display);
+  }
+  
+  if (InPicture ((XButtonEvent *)event, &image[0].cmapbar)) {
+    z = image[0].image[0].zero  + image[0].image[0].range * (event[0].x - image[0].cmapbar.x) / image[0].cmapbar.dx;
+    textpad = graphic[0].font[0].ascent;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+                  image[0].text_x + 1, image[0].text_y + 1, ZOOM_X - 2, textpad + PAD1 + 1);
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+    bzero (line, 100);
+    sprintf (line, "%22.3f", z);
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+                 image[0].text_x + PAD1, image[0].text_y + textpad + PAD1, 
+		 line, strlen(line));
+    
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdateStatusBox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/UpdateStatusBox.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "Ximage.h"
+
+void UpdateStatusBox (Graphic *graphic, KapaImageWidget *image, double x, double y, double z, int mode) {
+
+  int textpad;
+  double ra, dec; 
+  char line[100];
+
+  XY_to_RD (&ra, &dec, x, y, &image[0].image[0].coords);
+
+  textpad = graphic[0].font[0].ascent;
+
+  if (mode) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x, image[0].text_y, image[0].text_dx, image[0].text_dy);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+    XDrawRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x, image[0].text_y, image[0].text_dx, image[0].text_dy);
+  
+    bzero (line, 100);
+    sprintf (line, "(%d x %d) @ %d                                         ", 
+	     image[0].picture.dx, image[0].picture.dy, image[0].picture.expand); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 4*textpad + 4*PAD1, line, 25);
+    
+    bzero (line, 100);
+    sprintf (line, "%-25s", image[0].image[0].file); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 5*textpad + 5*PAD1, line, strlen(line));
+    
+    bzero (line, 100);
+    sprintf (line, "(%s)                                          ", image[0].image[0].name); 
+    XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		 image[0].text_x + PAD1, image[0].text_y + 6*textpad + 6*PAD1, line, 25);
+    
+  } else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].back);
+    // XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].overlay_color[1]);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc,
+		    image[0].text_x+1, image[0].text_y+1, image[0].text_dx-1, image[0].text_dyo-2);  
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].fore);
+  }
+  bzero (line, 100);
+  sprintf (line, "%22.3f", z);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + textpad + PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  sprintf (line, "%10.1f %10.1f", x, y);
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + 2*textpad + 2*PAD1, line, strlen(line));
+  
+  bzero (line, 100);
+  if (image[0].DecimalDegrees) {
+    sprintf (line, "%10.6f %10.6f", ra, dec); 
+  } else {
+    hh_hms (line, ra, dec, ':');
+  }
+  XDrawString (graphic[0].display, graphic[0].window, graphic[0].gc, 
+	       image[0].text_x + PAD1, image[0].text_y + 3*textpad + 3*PAD1, line, strlen(line));
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/args.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/args.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/args.c	(revision 17422)
@@ -0,0 +1,40 @@
+# include "Ximage.h"
+
+int args (int *argc, char **argv) {
+
+  int N;
+
+  MAP_WINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-nomap"))) {
+    remove_argument(N, argc, argv);
+    MAP_WINDOW = FALSE;
+  }
+
+  NAME_WINDOW = NULL;
+  if ((N = get_argument (*argc, argv, "-name"))) {
+    remove_argument(N, argc, argv);
+    NAME_WINDOW = strcreate (argv[N]);
+    remove_argument(N, argc, argv);
+  }
+
+  USE_XWINDOW = TRUE;
+  if ((N = get_argument (*argc, argv, "-noX"))) {
+    remove_argument(N, argc, argv);
+    USE_XWINDOW = FALSE;
+  }
+
+  if ((N = get_argument (*argc, argv, "-debug"))) {
+    remove_argument(N, argc, argv);
+    DEBUG = TRUE;
+  } else {
+    DEBUG = FALSE;
+  }
+
+  FOREGROUND = FALSE;
+  if ((N = get_argument (*argc, argv, "-fg"))) {
+    remove_argument(N, argc, argv);
+    FOREGROUND = TRUE;
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawFrame.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawFrame.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawFrame.c	(revision 17422)
@@ -0,0 +1,92 @@
+# include "Ximage.h"
+
+int bDrawFrame (KapaGraphWidget *graph) {
+  
+  int i, P, IsLabel, fontsize;
+  double fx, fy, dfx, dfy, range, major, minor, first, next;
+  char *fontname;
+
+  /* each axis is drawn independently */
+  fontname = GetRotFont (&fontsize);
+  for (i = 0; i < 4; i++) {
+    fx  = graph[0].axis[i].fx;
+    fy  = graph[0].axis[i].fy;
+    dfx = graph[0].axis[i].dfx;
+    dfy = graph[0].axis[i].dfy;
+    P = hypot ((double)graph[0].axis[(i+1)%2].dfx, (double)graph[0].axis[(i+1)%2].dfy);
+
+    if (graph[0].axis[i].isaxis) { 
+      bDrawLine (fx, fy, fx+dfx, fy+dfy); 
+    }
+    
+    if (graph[0].axis[i].areticks) {
+      if (isnan(graph[0].axis[i].min) || isinf(graph[0].axis[i].min)) continue;
+      if (isnan(graph[0].axis[i].max) || isinf(graph[0].axis[i].max)) continue;
+
+      range = graph[0].axis[i].max - graph[0].axis[i].min;
+
+      AxisTickScale (&graph[0].axis[i], &major, &minor);
+
+      first = minor*((int)(graph[0].axis[i].min/minor));
+      if ((range > 0) && (first < graph[0].axis[i].min)) {
+	first += minor;
+      }
+      if ((range < 0) && (first > graph[0].axis[i].min)) {
+	first -= minor;
+      }
+      for (next = first; ((range > 0) && (next <= graph[0].axis[i].max)) || ((range < 0) && (next >= graph[0].axis[i].max));) {
+	IsLabel = FALSE;
+	if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || 
+	    (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)) ||
+	    (fabs ((int)((next - 0.5*minor)/major) - (next/major)) < 0.5*(minor/major)))
+	  IsLabel = graph[0].axis[i].islabel;
+	bDrawTick (fx, fy, dfx, dfy, P, graph[0].axis[i].min, graph[0].axis[i].max, next, IsLabel, i);
+	if (range > 0) 
+	  next += minor;
+	else 
+	  next -= minor;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void bDrawTick (double fx, double fy, double dfx, double dfy, int P, double min, double max, double value, int mode, int naxis) {
+  
+  int pos, dir, fontsize;
+  double size, n, x, y, dx, dy;
+  char string[64], *fontname;
+
+  pos = size = 0;
+
+  if (mode == 1) { size = MAX (0.02, 7.0 / P); }
+  if (mode == 0) { size = MAX (0.01, 4.0 / P); }
+  
+  n = P / sqrt ((double)(dfx*dfx + dfy*dfy));
+  x = fx + (value-min)*dfx/(max - min);
+  y = fy + (value-min)*dfy/(max - min);
+
+  dir = +1;
+  if ((naxis == 0) || (naxis == 1)) dir *= -1;
+  dx = dir*size*dfy*n;	
+  dy = dir*size*dfx*n;
+  
+  bDrawLine (x, y, x+dx, y+dy);
+
+  if (mode == 1) {
+    fontname = GetRotFont (&fontsize);
+
+    /* temporarily assume rectilinear axes */
+    if (naxis == 0) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 1; }
+    if (naxis == 2) { dx = 0; dy = -dir*(0.8*fontsize + 1); pos = 7; }
+
+    if (naxis == 1) { dy = 0; dx = -(0.8*fontsize + 1); pos = 3; }
+    if (naxis == 3) { dy = 0; dx = +(0.8*fontsize + 1); pos = 5; }
+
+    x = fx + (value-min)*dfx/(max - min) + dx;
+    y = fy + (value-min)*dfy/(max - min) + dy;
+    if (fabs(value) < 1e-14) { value = 0.0; }
+    sprintf (string, "%g", value);
+    bDrawRotText ((int)x, (int)y, string, pos, 0.0);
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawIt.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawIt.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawIt.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "Ximage.h"
+
+bDrawBuffer *bDrawIt () {
+
+  int i, Nsection;
+  bDrawBuffer *buffer;
+  bDrawColor black;
+  Graphic *graphic;
+  Section *section;
+
+  graphic = GetGraphic();
+
+  black = KapaColorByName ("black");
+
+  buffer = bDrawBufferCreate (graphic->dx, graphic->dy);
+  bDrawSetBuffer (buffer);
+  bDrawSetStyle (black, 0, 0);
+  
+  // reset the sizes for all sections
+  Nsection = GetNumberOfSections ();
+  for (i = 0; i < Nsection; i++) {
+      section = GetSectionByNumber (i);
+      bDrawGraph (section->graph);
+      // bDrawImage (section->image);
+  }
+
+  return (buffer);
+}
+
+void bDrawGraph (KapaGraphWidget *graph) {
+  if (graph == NULL) return;
+  bDrawFrame (graph); 
+  bDrawObjects (graph);
+  bDrawLabels (graph);
+  bDrawTextlines (graph);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawLabels.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawLabels.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawLabels.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "Ximage.h"
+  
+void bDrawLabels (KapaGraphWidget *graph) {
+  
+  int i, pos, x, y, size;
+  double angle;
+  char *fontname;
+
+  pos = 0;
+  fontname = GetRotFont (&size);
+  for (i = 0; i < 8; i++) {
+    if (strcmp (graph[0].label[i].text, "")) {
+      angle = 0;
+      switch (i) {
+      case 0: pos = 7; break;
+      case 1: pos = 1; angle = -90; break;
+      case 2: pos = 1; break;
+      case 3: pos = 1; angle =  90; break;
+      case 4: pos = 2; break;
+      case 5: pos = 0; break;
+      case 6: pos = 8; break;
+      case 7: pos = 6; break;
+      }	
+      x = graph[0].label[i].x;
+      y = graph[0].label[i].y;
+      SetRotFont (graph[0].label[i].font, graph[0].label[i].size); 
+      bDrawRotText (x, y, graph[0].label[i].text, pos, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+void bDrawTextlines (KapaGraphWidget *graph) {
+
+  int i, x, y, size;
+  double angle;
+  char *fontname;
+
+  fontname = GetRotFont (&size);
+  for (i = 0; i < graph[0].Ntextline; i++) {
+    if (strcmp (graph[0].textline[i].text, "")) {
+      angle = graph[0].textline[i].angle;
+      x = graph[0].textline[i].x;
+      y = graph[0].textline[i].y;
+      SetRotFont (graph[0].textline[i].font, graph[0].textline[i].size);
+      bDrawRotText (x, y, graph[0].textline[i].text, 5, angle);
+    }
+  }
+  SetRotFont (fontname, size);
+}
+
+/*
+            
+ 4____2___5 
+  |       | 
+  |       | 
+ 1|       |3
+  |       |
+  |       |
+  ---------
+  6   0   7
+
+*/
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawObjects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawObjects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawObjects.c	(revision 17422)
@@ -0,0 +1,574 @@
+# include "Ximage.h"
+
+# define DrawLine(X1,Y1,X2,Y2) (bDrawLine ((X1), (Y1), (X2), (Y2)))
+# define DrawCircle(X1,Y1,R) (bDrawCircle ((X1), (Y1), (R)))
+# define DrawRectangle(X,Y,dX,dY) (bDrawRectOpen ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillRectangle(X,Y,dX,dY) (bDrawRectFill ((X-0.5*dX), (Y-0.5*dY), (X+0.5*dX), (Y+0.5*dY)))
+# define FillTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriFill ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define OpenTriangle(X1,Y1,X2,Y2,X3,Y3) (bDrawTriOpen ((X1), (Y1), (X2), (Y2), (X3), (Y3)))
+# define CONNECT 0
+# define HISTOGRAM 1
+# define POINTS 2
+
+static Graphic *graphic;
+
+int bDrawObjects (KapaGraphWidget *graph) {
+  
+  int i;
+  int type;
+  int weight;
+  bDrawColor color;
+  bDrawColor black;
+  
+  graphic = GetGraphic();
+
+  black = KapaColorByName ("black");
+  for (i = 0; i < graph[0].Nobjects; i++) {
+
+    weight = MAX (0, MIN (10, graph[0].objects[i].lweight));
+    type = graph[0].objects[i].ltype;    
+    color = graph[0].objects[i].color;
+    bDrawSetStyle (color, weight, type);
+
+    switch (graph[0].objects[i].style) {
+    case CONNECT: 
+      bDrawConnect (graph, &graph[0].objects[i]);
+      break;
+    case HISTOGRAM:
+      bDrawHistogram (graph, &graph[0].objects[i]);
+      break;
+    case POINTS:
+      bDrawPoints (graph, &graph[0].objects[i]);
+      break;
+    }
+
+    if (graph[0].objects[i].etype & 0x01) {
+      bDrawYErrors (graph, &graph[0].objects[i]);
+    }
+    if (graph[0].objects[i].etype & 0x02) {
+      bDrawXErrors (graph, &graph[0].objects[i]);
+    }
+  }
+  bDrawSetStyle (black, 0, 0);
+  return (TRUE);
+}
+
+void bDrawConnect (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    bDrawClipLine (sx0, sy0, sx1, sy1, X0, Y0, X1, Y1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+void bDrawClipLine (double x0, double y0, double x1, double y1, double X0, double Y1, double X1, double Y0) {
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x0,y0 if outside box */
+  if ((x0 < X0) && (x1 >= X0)) {
+    y0 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X0;
+  }
+  if ((x0 > X1) && (x1 <= X1)) {
+    y0 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x0 = X1;
+  }
+  if ((y0 < Y0) && (y1 >= Y0)) {
+    x0 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y0;
+  }
+  if ((y0 > Y1) && (y1 <= Y1)) {
+    x0 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y0 = Y1;
+  }
+
+  /* skip line segement if both points are beyond box */
+  if ((x0 <= X0) && (x1 <= X0)) return;
+  if ((x0 >= X1) && (x1 >= X1)) return;
+  if ((y0 <= Y0) && (y1 <= Y0)) return;
+  if ((y0 >= Y1) && (y1 >= Y1)) return;
+
+  /* replace x1,y1 if outside box */
+  if ((x1 < X0) && (x0 >= X0)) {
+    y1 = y0 + (X0 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X0;
+  }
+  if ((x1 > X1) && (x0 <= X1)) {
+    y1 = y0 + (X1 - x0)*(y1 - y0)/(x1 - x0);
+    x1 = X1;
+  }
+  if ((y1 < Y0) && (y0 >= Y0)) {
+    x1 = x0 + (Y0 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y0;
+  }
+  if ((y1 > Y1) && (y0 <= Y1)) {
+    x1 = x0 + (Y1 - y0)*(x1 - x0)/(y1 - y0);
+    y1 = Y1;
+  }
+  DrawLine (x0, y0, x1, y1);
+}
+
+/*******/
+void bDrawHistogram (KapaGraphWidget *graph, Gobjects *object) {
+
+  int i;
+  float *x, *y;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sxa;
+  double X0, X1, Y0, Y1;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0); 
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0); 
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  X0 = graph[0].axis[0].fx;
+  X1 = graph[0].axis[0].fx + graph[0].axis[0].dfx;
+  Y0 = graph[0].axis[1].fy;
+  Y1 = graph[0].axis[1].fy + graph[0].axis[1].dfy;
+
+  /* find the first valid datapoint */
+  x = object[0].x; y = object[0].y;
+  for (i = 0; (i < object[0].Npts) && !(finite(x[i]) && finite(y[i])); i++);
+  if (i >= object[0].Npts) return;
+  sx0 = x[i]*mxi + y[i]*mxj + bx;
+  sy0 = x[i]*myi + y[i]*myj + by;
+  sx0 = MIN (MAX (sx0, X0), X1);
+  sy0 = MAX (MIN (sy0, Y0), Y1);
+  
+  /* continue with rest of points */
+  for (i++; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]))) continue;
+    sx1 = x[i]*mxi + y[i]*mxj + bx;
+    sy1 = x[i]*myi + y[i]*myj + by;
+    sx1 = MIN (MAX (sx1, X0), X1);
+    sy1 = MAX (MIN (sy1, Y0), Y1);
+    sxa = 0.5*(sx0 + sx1);
+    DrawLine (sx0, sy0, sxa, sy0);
+    DrawLine (sxa, sy0, sxa, sy1);
+    DrawLine (sxa, sy1, sx1, sy1);
+    sx0 = sx1; sy0 = sy1;
+  }
+}
+
+/*******/
+void bDrawPoints (KapaGraphWidget *graph, Gobjects *object) {
+ 
+  int i;
+  float *x, *y, *z;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx, sy, d, sx1, sy1, sx2, sy2;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);  
+ 
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);  
+
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  /* below here, code is identical with DrawObjects */
+  /**** points are scaled by object.z ***/
+  if (object[0].size < 0) {
+    d = 0.5 * (graphic->dx + graphic->dy) * 0.01;
+    x = object[0].x; y = object[0].y; z = object[0].z;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {	/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d*z[i], 2*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d*z[i], sy, sx + d*z[i], sy);
+	    DrawLine (sx, sy - d*z[i], sx, sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {	/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d*z[i], sy - d*z[i], sx - d*z[i], sy + d*z[i]);
+	    DrawLine (sx - d*z[i], sy - d*z[i], sx + d*z[i], sy + d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d*z[i], sy - 0.58*d*z[i], sx + d*z[i], sy - 0.58*d*z[i], sx, sy + 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d*z[i], sy + 0.58*d*z[i], sx, sy - 1.15*d*z[i], sx + d*z[i], sy + 0.58*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx + d*z[i], sy + 0.58*d*z[i]);
+	    DrawLine (sx, sy, sx,          sy - 1.15*d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d*z[i]);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	/* connect a pair of points */
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  } else {
+    d = object[0].size * 0.5 * (graphic->dx + graphic->dy) * 0.003;
+    x = object[0].x; y = object[0].y;
+    if (object[0].ptype == 0) {	/* filled box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 1) {		/* open box */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+   	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawRectangle (sx, sy, 2*d, 2*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 2) { /* cross */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx - d, sy, sx + d, sy);
+	    DrawLine (sx, sy - d, sx, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 3) {		/* x */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx + d, sy - d, sx - d, sy + d);
+	    DrawLine (sx - d, sy - d, sx + d, sy + d);
+	  }
+      }
+    }
+    if (object[0].ptype == 4) {	/* filled triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    FillTriangle (sx - d, sy - 0.58*d, sx + d, sy - 0.58*d, sx, sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 5) {	/* open triangle */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    OpenTriangle (sx - d, sy + 0.58*d, sx + d, sy + 0.58*d, sx, sy - 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 6) {	/* Y */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawLine (sx, sy, sx - d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx + d, sy - 0.58*d);
+	    DrawLine (sx, sy, sx,     sy + 1.15*d);
+	  }
+      }
+    }
+    if (object[0].ptype == 7) {	/* 0 */
+      for (i = 0; i < object[0].Npts; i++) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx = x[i]*mxi + y[i]*mxj + bx;
+	sy = x[i]*myi + y[i]*myj + by;
+	if ((sx > graph[0].axis[0].fx) && (sx < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	    (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy))
+	  {
+	    DrawCircle (sx, sy, d);
+	  }
+      }
+    }
+    if (object[0].ptype == 100) {	
+      for (i = 0; i + 1 < object[0].Npts; i+=2) {
+	if (!(finite(x[i]) && finite(y[i]))) continue;
+	sx1 = x[i]*mxi + y[i]*mxj + bx;
+	sy1 = x[i]*myi + y[i]*myj + by;
+	sx2 = x[i+1]*mxi + y[i+1]*mxj + bx;
+	sy2 = x[i+1]*myi + y[i+1]*myj + by;
+	DrawLine (sx1, sy1, sx2, sy2);
+      }
+    }
+  }
+}
+    
+/*******/
+void bDrawXErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dxm, *dxp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dxp = object[0].dxp; dxm = object[0].dxm; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[1].dfy*0.03;
+   
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] + dxp[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] + dxp[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dxm[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = (x[i] - dxm[i])*mxi + y[i]*mxj + bx;
+    sy1 = (x[i] - dxm[i])*myi + y[i]*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sy1 - sz;
+	  sx11 = sy1 + sz;
+	  DrawLine (sx1, sx10, sx1, sx11);
+	}
+      }
+  }
+}
+    
+/*******/
+void bDrawYErrors (KapaGraphWidget *graph, Gobjects *object) {
+  
+  int i, bar;
+  float *x, *y, *dym, *dyp;
+  double mxi, mxj, myi, myj, bxi, bxj, byi, byj, bx, by;
+  double sx0, sy0, sx1, sy1, sz, sx10, sx11;
+  
+  mxi = graph[0].axis[0].dfx / (object[0].x1 - object[0].x0);
+  mxj = graph[0].axis[1].dfx / (object[0].y1 - object[0].y0);
+  myi = graph[0].axis[0].dfy / (object[0].x1 - object[0].x0);
+  myj = graph[0].axis[1].dfy / (object[0].y1 - object[0].y0);
+  
+  bxi  =  graph[0].axis[0].fx - object[0].x0*graph[0].axis[0].dfx/(object[0].x1 - object[0].x0);
+  bxj  =  -object[0].y0*graph[0].axis[1].dfx/(object[0].y1 - object[0].y0);
+  byi  =  -object[0].x0*graph[0].axis[0].dfy/(object[0].x1 - object[0].x0);
+  byj  =  graph[0].axis[1].fy - object[0].y0*graph[0].axis[1].dfy/(object[0].y1 - object[0].y0);
+  
+  bx = bxi + bxj;
+  by = byi + byj;
+  
+  x = object[0].x; y = object[0].y; dyp = object[0].dyp; dym = object[0].dym; 
+  bar = object[0].ebar; sz = object[0].size*graph[0].axis[0].dfx*0.03;
+  
+  for (i = 0; i < object[0].Npts; i++) {
+    if (!(finite(x[i]) && finite(y[i]) && finite(dyp[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] + dyp[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] + dyp[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+    if (!(finite(x[i]) && finite(y[i]) && finite(dym[i]))) continue;
+    sx0 = x[i]*mxi + y[i]*mxj + bx;
+    sy0 = x[i]*myi + y[i]*myj + by;
+    sx1 = x[i]*mxi + (y[i] - dym[i])*mxj + bx;
+    sy1 = x[i]*myi + (y[i] - dym[i])*myj + by;
+    if (((sx0 > graph[0].axis[0].fx) && (sx0 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy0 < graph[0].axis[1].fy) && (sy0 > graph[0].axis[1].fy + graph[0].axis[1].dfy)) ||
+	((sx1 > graph[0].axis[0].fx) && (sx1 < graph[0].axis[0].fx + graph[0].axis[0].dfx) &&
+	 (sy1 < graph[0].axis[1].fy) && (sy1 > graph[0].axis[1].fy + graph[0].axis[1].dfy)))
+      {
+	DrawLine (sx0, sy0, sx1, sy1);
+	if (bar) {
+	  sx10 = sx1 - sz;
+	  sx11 = sx1 + sz;
+	  DrawLine (sx10, sy1, sx11, sy1);
+	}
+      }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawOverlay.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/bDrawOverlay.c	(revision 17422)
@@ -0,0 +1,101 @@
+# include "Ximage.h"
+# define INFRONT 4
+
+static char name[4][16] = {"red", "green", "blue", "yellow"};
+
+void bDrawOverlay (KapaImageWidget *image, int N) {
+
+  int i;
+  int dX, dY, dx, dy;
+  int X, Y, Xmin, Ymin, Xmax, Ymax, Xrange, Yrange;
+  double expand, X0, Y0;
+  bDrawColor color;
+ 
+  /* translate color to bDrawColors : image[0].overlay[N].color */
+  color = KapaColorByName (name[N]);
+  bDrawSetStyle (color, 0, 0);
+  
+  expand = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+  }
+
+  Image_to_Screen (&X0, &Y0, 0.0, 0.0, &image[0].picture);
+  X0 -= image[0].picture.x;
+  Y0 -= image[0].picture.y;
+
+  Xmin = 0;
+  Ymin = 0;
+  Xmax = image[0].picture.dx;
+  Ymax = image[0].picture.dy;
+  Xrange = image[0].picture.dx;
+  Yrange = image[0].picture.dy;
+
+  for (i = 0; i < image[0].overlay[N].Nobjects; i++) {
+    if (N == INFRONT) {
+      X  = image[0].overlay[N].objects[i].x * Xrange;
+      Y  = image[0].overlay[N].objects[i].y * Yrange;
+      dX = image[0].overlay[N].objects[i].dx * Xrange;
+      dY = image[0].overlay[N].objects[i].dy * Yrange;
+    } else {
+      X  = image[0].overlay[N].objects[i].x/expand + X0;
+      Y =  image[0].overlay[N].objects[i].y/expand + Y0;
+      dX = image[0].overlay[N].objects[i].dx/expand;
+      dY = image[0].overlay[N].objects[i].dy/expand;
+    }
+    if ((X + dX < Xmin) || (X - dX > Xmax) ||
+	(Y + dY < Ymin) || (Y - dY > Ymax)) {
+      continue;
+    }
+
+    /* for a LINE, (x, y) is the start, (dx, dy) is the distance to end
+       for a CIRCLE (x, y) is the center, (dx, dy) is the radius 
+       for a BOX (x, y) is the center, (dx, dy) is the width */
+
+    switch (image[0].overlay[N].objects[i].type) {
+      case KII_OVERLAY_LINE:
+	bDrawLine (X, Y, (X+dX), (Y+dY));
+	break;
+      case KII_OVERLAY_TEXT:
+	bDrawRotText (X, Y, image[0].overlay[N].objects[i].text, 8, 0.0);
+	break;
+      case KII_OVERLAY_BOX:
+	dx = MAX (abs(dX),2) / 2;
+	dy = MAX (abs(dY),2) / 2;
+	bDrawRectOpen ((X-dx), (Y-dy), (X+dx), (Y+dy));
+	// bDrawRectOpen ((X-dx), (Y-dy), (X), (Y));
+	break;
+      case KII_OVERLAY_CIRCLE:
+	dx = MAX (abs(dX),2);
+	dy = MAX (abs(dY),2);
+	if (image[0].overlay[N].objects[i].angle == 0.0) {
+	  bDrawArc (X, Y, dx, dy, 0, 360);
+	} else {
+	  // very stupid rotate ellipse drawing:
+	  double x, y, t;
+	  double angle = image[0].overlay[N].objects[i].angle * RAD_DEG;
+	  double cs = cos(angle);
+	  double sn = sin(angle);
+	  // XXX dt should be based on the size of the ellipse...
+	  for (t = 0; t < 2*M_PI; t+=0.05) {
+	    x = X + dx*cos(t)*cs + dy*sin(t)*sn;
+	    y = Y - dx*cos(t)*sn + dy*sin(t)*cs;
+	    bDrawPoint (x, y);
+	  }
+	}
+	break;
+      default:
+	fprintf (stderr, "skipping unknown object\n");
+	break;
+    }
+  }
+  
+  /* translate color to bDrawColors : image[0].overlay[N].color */
+  bDrawSetStyle (color, 0, 0);
+}
+
+/* this routine is independent of the number of overlays */
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/hh_hms.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/hh_hms.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/hh_hms.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+void hh_hms (char *line, double ra, double dec, char sep) {
+
+  int h, m, flag;
+  double s;
+  
+  ra /= 15.0;  /* convert from degrees to hours */
+  flag = SIGN(ra);
+  ra *= flag;
+  h = ra;
+  m = 60.000001*(ra - h);
+  s = 3600*(ra - h - m / 60.0);
+  if (flag > 0)
+    sprintf (line, " %02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  else
+    sprintf (line, "-%02d%c%02d%c%04.1f  ", h, sep, m, sep, s);
+  
+  flag = SIGN(dec);
+  dec *= flag;
+  h = dec;
+  m = 60.000001*(dec - h);
+  s = 3600*(dec - h - m / 60.0);
+  if (flag > 0)
+    sprintf (&line[13], " %02d%c%02d%c%04.1f", h, sep, m, sep, s);
+  else
+    sprintf (&line[13], "-%02d%c%02d%c%04.1f", h, sep, m, sep, s);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/src/kapa.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/src/kapa.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/src/kapa.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "Ximage.h"
+
+int main (int argc, char **argv) {
+  
+  args (&argc, argv);
+
+  SetUpGraphic (&argc, argv);
+
+  InitLayout (argc, argv);
+  EventLoop ();
+
+  CloseDisplay ();
+  exit (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/test/input
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/test/input	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/test/input	(revision 17422)
@@ -0,0 +1,38 @@
+
+macro test1
+  mcreate a 512 512
+  set x = xramp(a)
+  tv x 0 512
+end
+
+macro go
+  mcreate a 512 512
+  set x = xramp(a)
+  set y = yramp(a)
+  set z = sqrt((x-256)^2 + (y-256)^2)
+  tvchannel 1
+  tv x 0 512
+  tvchannel 2
+  tv y 0 512
+  tvchannel 3
+  tv z 0 512
+end
+
+macro test
+  rd r dub.red.fits
+  rd g dub.green.fits
+  rd b dub.blue.fits
+  set R = r*(r >= 0) + (r + 256)*(r < 0)
+  set G = g*(g >= 0) + (g + 256)*(g < 0)
+  set B = b*(b >= 0) + (b + 256)*(b < 0)
+  tvchannel 1
+  tv R 0 256
+  tvchannel 2
+  tv G 0 256
+  tvchannel 3
+  tv B 0 256
+
+  tvcolor ruffcolor
+  sleep 1
+  tvcolor fullcolor
+end
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/CheckTextLines.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/CheckTextLines.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/CheckTextLines.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "Ximage.h"
+
+/******** Here we test the TextLines specific to this program  ****
+TextLine *
+CheckTextLines (graphic, event, layout)
+Graphic         graphic[];
+XEvent          event[];
+Layout          layout[];
+{
+
+  TextLine *textline;
+  textline = (TextLine *) NULL;
+
+  if (InTextLine (graphic, event, &layout[0].zero))
+    textline = &layout[0].zero;
+  if (InTextLine (graphic, event, &layout[0].range))
+    textline = &layout[0].range;
+  if (InTextLine (graphic, event, &layout[0].effects))
+    textline = &layout[0].effects;
+  if (InTextLine (graphic, event, &layout[0].command))
+    textline = &layout[0].command;
+
+  return (textline);
+
+}
+
+
+***/
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DownArrow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DownArrow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DownArrow.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+DownArrow (button_event, height, width, SB_x, SB_y)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (width - SB_y)));
+    return (answer);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawCursor.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawCursor.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawCursor.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+
+DrawCursor (graphic, textline)    
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int dx, label_width, DX, text_width, i;
+
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+    dx  = XTextWidth (graphic[0].font, &textline[0].text[i - 1], 
+		      textline[0].cursor - i + 1) + arrow_width;
+  }
+  else 
+    dx  = XTextWidth (graphic[0].font, textline[0].text, textline[0].cursor);
+
+  XDrawLine (graphic[0].display, 
+	     graphic[0].window, 
+	     graphic[0].gc, 
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + YBorder,
+	     textline[0].x + label_width + XBorder + dx, 
+	     textline[0].y + textline[0].dy - YBorder);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextBox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextBox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextBox.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* DrawTextBox ****************/
+DrawTextBox (graphic, textbox)
+Graphic      graphic[];
+TextBox      textbox[];
+{
+
+  int X, Y, y, dy, Nlines, i;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+  XFillRectangle (graphic[0].display, 
+		  graphic[0].window,
+		  graphic[0].gc,
+		  textbox[0].x  + 1,  textbox[0].y + 1,
+		  XBorder - 2, textbox[0].dy - 2);
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textbox[0].x,  textbox[0].y,
+		    textbox[0].dx, textbox[0].dy);
+  }
+
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  Nlines = (textbox[0].dy - 2*YBorder) / dy;
+
+  for (i = 0; (i < Nlines) && (i < textbox[0].Nlines); i++) {
+    y = textbox[0].y + graphic[0].font[0].ascent + dy*i + YBorder;
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textbox[0].x + XBorder, y,
+		 textbox[0].text[i], 
+		 strlen(textbox[0].text[i]));
+  }
+
+  if (Nlines < textbox[0].Nlines) {
+    DrawBitmap (graphic, 
+		textbox[0].x + textbox[0].dx - down_width - 2,
+		textbox[0].y + textbox[0].dy - down_height - 2,
+		down_width, down_height, down_bits, 1);
+  }
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textbox[0].cursor_line != -1) {
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+  }
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextLine.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextLine.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/DrawTextLine.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+DrawTextLine (graphic, textline)
+Graphic  graphic[];
+TextLine textline[];
+{
+
+  int y;
+
+  XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+  if (textline[0].outline) {
+    XDrawRectangle (graphic[0].display, 
+		    graphic[0].window,
+		    graphic[0].gc,
+		    textline[0].x,  textline[0].y,
+		    textline[0].dx, textline[0].dy);
+  }
+
+  y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+  XDrawString (graphic[0].display, 
+	       graphic[0].window, 
+	       graphic[0].gc, 
+	       textline[0].x + XBorder, y,
+	       textline[0].label, 
+	       strlen(textline[0].label));
+  RedrawString (graphic, textline);
+
+  if (textline[0].cursor != -1)
+    DrawCursor (graphic, textline);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextBox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextBox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextBox.c	(revision 17422)
@@ -0,0 +1,57 @@
+# include "Ximage.h"
+# define XBorder 10
+# define YBorder 2
+# define arrow_width 6
+# define arrow_height 9
+static char arrow_bits[] = {
+  0x00, 0x02, 0x06, 0x0e, 0x1e, 0x0e, 0x06, 0x02, 0x00};
+#define down_width 9
+#define down_height 6
+static char down_bits[] = {
+   0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00};
+
+/******************* InTextBox ****************/
+int
+InTextBox (graphic, event, textbox)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextBox          textbox[];
+{
+
+  int answer;
+  int i, x, y, Y, X, dy;
+  int minX, maxX, minY, maxY;
+  
+  x = event[0].x;
+  y = event[0].y;
+  dy = graphic[0].font[0].ascent + graphic[0].font[0].descent;
+  
+  minX = textbox[0].x;
+  maxX = textbox[0].x + textbox[0].dx;
+  minY = textbox[0].y;
+  maxY = textbox[0].y + textbox[0].dy;
+  if (dy*textbox[0].Nlines + 2*YBorder < textbox[0].dy)
+    maxY = textbox[0].y + dy*textbox[0].Nlines + 2*YBorder;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+  
+  /* find the cursor position, erase the old arrow and draw a new arrow */
+  if (answer) {
+    if (textbox[0].cursor_line != -1) {
+      Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+      X = textbox[0].x + (XBorder - arrow_width) / 2;
+      DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 0);
+    }
+    textbox[0].cursor_line = (y - textbox[0].y - YBorder)/dy;
+    X = textbox[0].x + (XBorder - arrow_width) / 2;
+    Y = textbox[0].y + dy*textbox[0].cursor_line + YBorder + (dy - arrow_height)/2;
+    DrawBitmap (graphic, X, Y, arrow_width, arrow_height, arrow_bits, 1);
+    textbox[0].cursor_x = x;
+    textbox[0].cursor_y = y;
+    XFlush (graphic[0].display);
+  }
+  
+  return (answer);
+  
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextLine.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextLine.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/InTextLine.c	(revision 17422)
@@ -0,0 +1,58 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int
+InTextLine (graphic, event, textline)
+Graphic          graphic[];
+XButtonEvent    *event;
+TextLine         textline[];
+{
+
+  int answer, done;
+  int i, x, dx, y, text_width, label_width;
+  int minX, maxX, minY, maxY;
+  char testline[1000];
+
+  x = event[0].x;
+  y = event[0].y;
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  minX = textline[0].x + XBorder + label_width;
+  maxX = textline[0].x + textline[0].dx;
+  minY = textline[0].y;
+  maxY = textline[0].y + textline[0].dy;
+
+  answer = ((x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY));
+
+  /* find the cursor position and draw a vertical bar */
+  if (answer) {
+    text_width  = XTextWidth (graphic[0].font, 
+			      textline[0].text,
+			      strlen(textline[0].text));
+    
+    if (x >= minX + text_width) {
+      textline[0].cursor = strlen (textline[0].text);
+    }
+    else {
+      for (i = 1, done = FALSE; (i <= strlen (textline[0].text)) && !done; i++) {
+	dx  = XTextWidth (graphic[0].font, 
+			  textline[0].text, i);
+	if (x < minX + dx) {
+	  textline[0].cursor = i - 1;
+	  done = TRUE;
+	}
+      }
+    }
+    DrawCursor (graphic, textline);
+  }    
+  XFlush (graphic[0].display);
+  
+  return (answer);
+  
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/LeftArrow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/LeftArrow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/LeftArrow.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+LeftArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && (m_y < SB_x));
+  return (answer);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RedrawString.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RedrawString.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RedrawString.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+
+#define arrow_width 6
+#define arrow_height 9
+RedrawString (graphic, textline)
+Graphic       graphic[];
+TextLine      textline[];
+{
+
+  int label_width, text_width, y, i, DX;
+  static char arrow_bits[] = {
+    0x00, 0x10, 0x18, 0x1c, 0x1e, 0x1c, 0x18, 0x10, 0x00};
+  
+  label_width  = XTextWidth (graphic[0].font, 
+			     textline[0].label,
+			     strlen(textline[0].label));
+  
+  text_width  = XTextWidth (graphic[0].font, 
+			    textline[0].text,
+			    strlen(textline[0].text));
+
+  if (label_width + text_width + 2*XBorder > textline[0].dx) {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    DX = textline[0].dx - label_width - 2*XBorder - arrow_width;
+    for (i = 1; (i < strlen (textline[0].text)) && (text_width > DX); i++) {
+      text_width  = XTextWidth (graphic[0].font, 
+				&textline[0].text[i],
+				strlen(&textline[0].text[i]));
+    }
+
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    DrawBitmap (graphic, 
+		textline[0].x + XBorder + label_width,
+		textline[0].y + YBorder + (textline[0].dy - arrow_height)/2,
+		arrow_width, arrow_height, arrow_bits, 1);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width + arrow_width, y,
+		 &textline[0].text[i - 1], 
+		 strlen(&textline[0].text[i - 1]));
+    XFlush (graphic[0].display);
+  }
+  else {
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].white);
+    XFillRectangle (graphic[0].display, graphic[0].window, graphic[0].gc, 
+		    textline[0].x + XBorder + label_width, 
+		    textline[0].y + YBorder, 
+		    textline[0].dx - XBorder - label_width, 
+		    textline[0].dy - 2*YBorder + 1);
+    
+    y = textline[0].y + (textline[0].dy + graphic[0].font[0].ascent) / 2;
+    
+    XSetForeground (graphic[0].display, graphic[0].gc, graphic[0].black);
+    XDrawString (graphic[0].display, 
+		 graphic[0].window, 
+		 graphic[0].gc, 
+		 textline[0].x + XBorder + label_width, y,
+		 textline[0].text, 
+		 strlen(textline[0].text));
+  }
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RightArrow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RightArrow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/RightArrow.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+RightArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > (height - SB_x - SB_y)) && 
+	    (m_y < (height - SB_y)));
+  return (answer);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/TextLineEntry.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/TextLineEntry.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/TextLineEntry.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "Ximage.h"
+# define XBorder 5
+# define YBorder 2
+
+int 
+TextLineEntry (graphic, textline, keyevent)
+Graphic        graphic[];
+TextLine       textline[];
+XKeyEvent     *keyevent;
+{
+
+  int i, status, N;
+  char dummy[3];
+  XComposeStatus compose_status;
+  KeySym keysym;
+
+  status = TRUE;
+  strcpy (textline[0].old_text, textline[0].text);
+  bzero (&textline[0].text[strlen(textline[0].text)], 
+	 1024 - strlen(textline[0].text));
+  i = textline[0].cursor;
+
+
+  N = XLookupString (keyevent, dummy, 1, &keysym, &compose_status);
+  switch (keysym) {
+  case XK_BackSpace:
+  case XK_Delete:
+    if (textline[0].cursor > 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i - 1], 
+	     strlen(&textline[0].text[i]) + 1);
+      RedrawString (graphic, textline);
+      textline[0].cursor --;
+      DrawCursor (graphic, textline);
+    }
+    break;
+  case XK_Left:
+    RedrawString (graphic, textline);
+    textline[0].cursor --;
+    if (textline[0].cursor < 0)
+      textline[0].cursor = 0;
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Right:
+    RedrawString (graphic, textline);
+    textline[0].cursor ++;
+    if (textline[0].cursor > strlen (textline[0].text))
+      textline[0].cursor = strlen (textline[0].text);
+    DrawCursor (graphic, textline);
+    break;
+  case XK_Return:
+    RedrawString (graphic, textline);
+    textline[0].cursor = -1;
+    status = FALSE;
+    break;
+  case XK_d:
+  case XK_D: 
+    if (dummy[0] == 4) { /* a control-d was typed! */
+      if (textline[0].cursor < strlen(textline[0].text)) {
+	bcopy (&textline[0].text[i + 1], &textline[0].text[i], 
+	       strlen(&textline[0].text[i]));
+	RedrawString (graphic, textline);
+	DrawCursor (graphic, textline);
+      }
+      break;  /* WARNING: this case MUST come before default.
+		 anything between this and default will be executed
+		 if no cntl-d is typed! */
+    }
+  default:
+    if (N != 0) {
+      bcopy (&textline[0].text[i], &textline[0].text[i + 1], 
+	     strlen(&textline[0].text[i]));
+      textline[0].text[i] = dummy[0];
+      RedrawString (graphic, textline);
+      textline[0].cursor ++;
+      DrawCursor (graphic, textline);
+    }
+  }
+  return (status);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/UpArrow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/UpArrow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/UpArrow.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+UpArrow (button_event, height, width, SB_x, SB_y) 
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+{
+
+  int answer, m_x, m_y;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > SB_x) && 
+	    (m_x < (SB_x + SB_y)));
+  return (answer);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/xScrollBar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/xScrollBar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/xScrollBar.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int 
+xScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_x < SB_x) && 
+	    (m_y > SB_x) && 
+	    (m_y < (height - SB_x - SB_y)));
+  f1 = (double)(m_y - SB_x);
+  f2 = (double)(height - 2*SB_x - SB_y);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/yScrollBar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/yScrollBar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/kapa2/textbox/yScrollBar.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "Ximage"
+# include "ScrollBars.h"
+
+int
+yScrollBar (button_event, height, width, SB_x, SB_y, f)
+XButtonEvent *button_event;
+int height, width, SB_x, SB_y;
+double *f;
+{
+
+  int answer, m_x, m_y;
+  double f1, f2;
+
+  m_x = button_event -> x;
+  m_y = button_event -> y;
+  answer = ((m_y > (height - SB_y)) && 
+	    (m_x > (SB_x + SB_y)) && 
+	    (m_x < (width - SB_y)));
+  f1 = (double)(m_x - SB_x - SB_y);
+  f2 = (double)(width - 2*SB_y - SB_x);
+  *f = f1 / f2;
+  return (answer);
+
+}
+
+	
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/.cvsignore	(revision 17422)
@@ -0,0 +1,1 @@
+lib
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/Makefile	(revision 17422)
@@ -0,0 +1,117 @@
+default: install
+help:
+	@echo "make options: install libdvo clean dist"
+
+include ../../Makefile.System
+HOME 	=	$(ROOT)/src/libdvo
+AUTO	=	$(ROOT)/src/libautocode
+LIB	= 	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	=	$(HOME)/include
+ASRC	=	$(AUTO)/src
+AINC	=	$(AUTO)/include
+ADEF	=	$(AUTO)/def
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS) -lFITS -lohana
+
+default: install
+install: $(DESTLIB)/libdvo.a $(DESTLIB)/libdvo.$(DLLTYPE)
+libdvo: $(LIB)/libdvo.$(ARCH).a $(LIB)/libdvo.$(ARCH).$(DLLTYPE)
+
+DEFS = \
+$(DESTINC)/loneos_defs.h \
+$(DESTINC)/elixir_defs.h \
+$(DESTINC)/panstarrs_dev_0_defs.h \
+$(DESTINC)/panstarrs_dev_1_defs.h \
+$(DESTINC)/ps1_dev_1_defs.h \
+$(DESTINC)/ps1_dev_2_defs.h \
+$(DESTINC)/ps1_dev_3_defs.h
+
+INCS = $(DEFS) $(DESTINC)/dvo.h $(DESTINC)/autocode.h
+
+OBJS = \
+$(SRC)/version.$(ARCH).o	 \
+$(SRC)/coordops.$(ARCH).o	 \
+$(SRC)/dvosorts.$(ARCH).o	 \
+$(SRC)/dvo_photcode_ops.$(ARCH).o \
+$(SRC)/LoadPhotcodes.$(ARCH).o   \
+$(SRC)/LoadPhotcodesText.$(ARCH).o   \
+$(SRC)/LoadPhotcodesFITS.$(ARCH).o   \
+$(SRC)/SavePhotcodesText.$(ARCH).o   \
+$(SRC)/SavePhotcodesFITS.$(ARCH).o   \
+$(SRC)/imreg_datatypes.$(ARCH).o \
+$(SRC)/mosaic_astrom.$(ARCH).o   \
+$(SRC)/fits_db.$(ARCH).o	 \
+$(SRC)/photfits.$(ARCH).o        \
+$(SRC)/dvo_image.$(ARCH).o       \
+$(SRC)/dvo_image_raw.$(ARCH).o   \
+$(SRC)/dvo_catalog.$(ARCH).o     \
+$(SRC)/dvo_catalog_raw.$(ARCH).o       \
+$(SRC)/dvo_catalog_mef.$(ARCH).o       \
+$(SRC)/dvo_catalog_split.$(ARCH).o     \
+$(SRC)/dvo_catalog_create.$(ARCH).o    \
+$(SRC)/dvo_catalog_chipcoords.$(ARCH).o \
+$(SRC)/dvo_convert.$(ARCH).o           \
+$(SRC)/dvo_convert_elixir.$(ARCH).o    \
+$(SRC)/dvo_convert_loneos.$(ARCH).o    \
+$(SRC)/dvo_convert_panstarrs_DEV_0.$(ARCH).o \
+$(SRC)/dvo_convert_panstarrs_DEV_1.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_1.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_2.$(ARCH).o \
+$(SRC)/dvo_convert_PS1_DEV_3.$(ARCH).o \
+$(SRC)/skyregion_io.$(ARCH).o    \
+$(SRC)/skyregion_gsc.$(ARCH).o    \
+$(SRC)/skyregion_ops.$(ARCH).o
+
+# $(SRC)/dvo_convert_panstarrs.$(ARCH).o \
+# $(SRC)/dvo_convert_pmtest.$(ARCH).o \
+
+include ../libautocode/Makefile.Targets
+
+$(OBJS): $(INCS)
+
+$(LIB)/libdvo.$(ARCH).a:  $(AOBJS) $(OBJS)
+$(LIB)/libdvo.$(ARCH).$(DLLTYPE): $(AOBJS) $(OBJS)
+
+$(DESTLIB)/libdvo.a:  $(LIB)/libdvo.$(ARCH).a
+$(DESTLIB)/libdvo.$(DLLTYPE): $(LIB)/libdvo.$(ARCH).$(DLLTYPE)
+
+# dependency rules for the autocode files
+
+$(AOBJS): $(ADEF)/autocode.c $(DESTINC)/autocode.h
+
+$(DESTINC)/autocode.h: $(AINC)/autocode.h
+	@if [ ! -d $(DESTINC) ]; then mkdir -p $(DESTINC); fi
+	rm -f $@
+	cp $< $@
+
+$(AINC)/autocode.h: $(AINCS) $(ADEF)/autocode.h $(ADEF)/common.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for AINCS
+$(AINC)/%.h : $(ADEF)/%.d $(ADEF)/autocode.h $(ADEF)/common.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for ASRC
+$(ASRC)/%.c : $(ADEF)/%.d $(ADEF)/autocode.c $(AINC)/%.h
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+# generic rule for AOBJS
+$(ASRC)/%.$(ARCH).o : $(ASRC)/%.c
+	@echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+tabletest: install
+	gcc -L$(LIBDIR) -I$(INCDIR) -o tabletest $(SRC)/tabletest.c -ldvo -lFITS -lohana -lm
+
+clean: cleandef
+cleandef:
+	cd $(AUTO) && make clean
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/ChangeLog.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 17422)
@@ -0,0 +1,20 @@
+
+- libdvo 1.3 : 2006.08.23
+  * converted to gfits APIs (forces libfits 1.6)
+  * unified DVO APIs
+  * fixed DVO API naming (dvo_catalog..., dvo_image...)
+  * fixes to polynomial conversions in coords.c
+  * less restrictive ctypes for polynomials (coords.c)
+  * fixes to dvo locking strategy
+
+- libdvo 1.2
+  * added PMTEST format
+  * added dvo_image_lock,unlock functions
+  * fixes to the skyregion support
+  * dropped _PS from average.R,D,M
+  * fixed bug in coord interpretation of CROTA2
+  * fixed reallocation logic in skyregions
+
+- libdvo 1.1
+  * added skyregion support functions and structures
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-catalogs.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 17422)
@@ -0,0 +1,70 @@
+
+// APIs related to the DVO catalogs:
+
+load_catalog (Catalog *catalog, int VERBOSE);
+
+dvo_catalog_lock (Catalog *catalog, int lockmode);
+// lockmode: SOFT, XCLD, HARD
+
+dvo_catalog_unlock (Catalog *catalog);
+
+dvo_catalog_load (Catalog *catalog, int VERBOSE, int mode);
+// format: INTERNAL, LONEOS, ELIXIR, PANSTARRS, PMTEST, etc.
+// layout: RAW, MEF, SPLIT
+// mode:   READ, WRITE
+
+// returns
+//   DVO_CAT_OPEN_FAIL:  failure to lock catalog
+//   DVO_CAT_OPEN_OK:    success: file is locked and opened
+//   DVO_CAT_OPEN_EMPTY: success: file is locked but empty
+
+ the catalog is opened and the data for each of the elements (average,
+ measure, etc) is loaded if requested.  Whether or not the data is
+ loaded, the values of catalog.Naves_disk,Naves_off,Naverage are set
+ to match the segment of data loaded.  this API always loads either
+ the entire data set, or none.  If none, the offset value is set to
+ point at the end of the table.
+
+dvo_catalog_save (Catalog *catalog, int VERBOSE);
+// format: INTERNAL, LONEOS, ELIXIR, PANSTARRS, PMTEST, etc.
+// layout: RAW, MEF, SPLIT
+// mode:   READ, WRITE
+
+dvo_catalog_init (Catalog *catalog);
+
+dvo_catalog_create (Catalog *catalog);
+
+dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+// check an existing catalog against the identified Nsecfilt value
+// if (Nsecfilt == catalog[0].Nsecfilt) OK
+// if (Nsecfilt <  catalog[0].Nsecfilt) ERROR
+// if (Nsecfilt >  catalog[0].Nsecfilt) && extend) extend
+// if (Nsecfilt >  catalog[0].Nsecfilt) && !extend) ERROR
+
+
+several possible cases:
+
+- open and read an existing catalog / create if missing
+- open and read an existing catalog / error if missing (can be ignored)
+
+dvo_catalog_open (Catalog *catalog, int mode); 
+
+dvo_catalog_load_segment_average (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Average and SecFilt tables only.  It is possible within the
+ DVO framework to examine the average values in slices.  
+
+dvo_catalog_load_segment_measure (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Measure table only.  It is not guaranteed to be possible to
+ examine the measure values in slices if reference is needed to their
+ average values.
+
+dvo_catalog_load_segment_missing (Catalog *catalog, int start, int Nrows)
+
+ this function reads in the requested number of rows from the catalog
+ for the Missing table only.  It is not guaranteed to be possible to
+ examine the missing values in slices if reference is needed to their
+ average values.
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-flags.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-flags.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-flags.txt	(revision 17422)
@@ -0,0 +1,39 @@
+
+There are several types of quality flags used within DVO.  There are
+flags associated with the measurements, the average objects, and the
+images.  
+
+measure->photFlags : these flags are supplied by the photometry
+		     analysis program which produced the data.  They
+		     may be directly defined by the program, as in the
+		     case of psphot, or they may be translations of
+		     detection metadata supplied by the data source
+		     (eg, 2MASS or SDSS).  These flags are read only:
+		     the are set by addstar, and not modified by any
+		     of the DVO database manipulation programs.  
+
+measure->dbFlags : these flags are used within DVO to note the ways in
+		   which the detetion is determined to be special by
+		   the database analysis programs.  These flags may be
+		   set by addstar on ingest, or by relphot, relastro,
+		   or equivalent programs.  Here are the possible
+		   values for dbFlags:
+
+NOTE: update these names and use an enum:		   
+# define ID_MEAS_NOCAL        0x0001 : detection ignored for this analysis (photcode, time range) -- internal only
+# define ID_MEAS_POOR_PHOTOM  0x0002 : detection is photometry outlier
+# define ID_MEAS_SKIP_PHOTOM  0x0004 : detection was ignored for photometry measurement
+# define ID_MEAS_AREA         0x0008 : detection near image edge
+# define ID_MEAS_POOR_ASTROM  0x0010 : detection is astrometry outlier
+# define ID_MEAS_SKIP_ASTROM  0x0020 : detection was ignored for astrometry measurement
+# define ID_MEAS_UNDEF_1      0x0040 : unused
+# define ID_MEAS_UNDEF_2      0x0080 : unused
+# define BLEND_IMAGE          0x0100 : detection is within radius of multiple objects
+# define BLEND_CATALOG        0x0200 : multiple detections within radius of object
+# define ID_MEAS_UNDEF_3      0x0400 : unused
+# define ID_MEAS_UNDEF_4      0x0800 : unused
+# define BLEND_IMAGE_NEIGHBOR 0x1000 : detection is within radius of multiple objects across catalogs
+# define ID_ARTIFACT          0x2000 : detection is thought to be non-astronomical
+# define ID_MEAS_UNDEF_5      0x4000 : unused
+# define ID_MEAS_UNDEF_6      0x8000 : unused
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-images.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-images.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-images.txt	(revision 17422)
@@ -0,0 +1,7 @@
+
+the dvo image table is not extremely efficient.  it is unsorted, and
+spatial searches consist of complete scans of the table.
+
+a spatial index could consist of five cardinal points for each square
+image (4 corners and the center) or a central point and max radius.
+table would consist each would be
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-int-nan.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-int-nan.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-int-nan.txt	(revision 17422)
@@ -0,0 +1,14 @@
+
+we have a variety of data fields which use char, short, int or long as
+a data type.  we need a value which can mean NAN in that context.
+these are defined in dvo.h as elements of the enum DVO_INT_NAN.  
+
+2007.11.07 : I am going through the code base and replacing the
+        deprecated values NO_ERR and NO_MAG with their equivalent
+        DVO_INT_NAN names.  At the same time, where ever the internal
+        value is a float or double, these must be converted to real
+        NAN values.  These may once have used char, short, or int
+        internally, and were not updated correctly.  
+
+	* watch out for Xm : PhotXm returns -1, not NAN for invalid entry,
+	  secfilt.Xm is short (use NAN_S_SHORT?)
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-structures.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 17422)
@@ -0,0 +1,178 @@
+
+2008.01.31
+
+  For testing the quality of the extended source measurements, I need
+  to include them somehow within DVO.  My plan is to have a set of
+  extended source tables linked to the average tables, parallel to the
+  measure tables.  One issue is: how many rows in this table?  I think
+  I need to have room for all fields measured by psphot  Another
+  question: do I use a fixed layout I/O file, or do I use the header
+  to read only the columns of interest.  I can do the latter, and it
+  is only a little slower.
+
+2007.02.22
+
+I have several DVO improvements to implement.  I need to plan them a
+bit carefully.  Here are my thoughts on these updates.
+
+- add chip X,Y to measure table
+
+  this is a fairly simple addition.  In addstar/find_matches, I just need
+  to assign the X,Y value from the incoming star data.  For loading
+  old catalogs which don't include X,Y in the table, I need to
+  calculate the X,Y position based on the R,D after finding the
+  matching image.  I have code to do this calculation currently in
+  dvo/photometry.c for looking up X,Y on the fly.  once this is moved
+  into the load functions, it will not be needed in photometry.c
+
+- remove PRI/SEC and only use secfilt table for mags
+
+  this is not a very difficult change, conceptually, but it may be a
+  fair amount of work.  all of the functions which currently switch on
+  the case of PRI/SEC just need to look in the secfilt location.  the
+  value of Nsec needs to increase by 1.  the load from tables which
+  used PRI/SEC need to move the PRI values to the correct location in
+  secfilt
+
+  eventually, rename 'secfilt' to a better name choice
+
+- add image, average, measure IDs
+
+  for PS1, the image and measure need to be generated by the external
+  software.  they would just be part of the input stream.  for the
+  case were the IDs are not supplied, DVO needs to generate them in a
+  unique way.  I probably need to understand / define that mechanism
+  before tackling this problem.
+
+  one point: I need to keep the old averef index.  how do this index
+  and the objID work together?
+
+- link measure to image
+
+  temporariliy use an index like averef?
+
+- move photcode table to catdir (also zero points)
+
+  should be fairly easy: just like the SkyTable, look in the catdir
+  first, then generate from the default text table if it is missing.
+
+- color term as a function of mosaic position
+ 
+- mextract field,field,field 
+
+  this should not be a very difficult job.  just add a loop to the
+  avextract / mextract functions.
+
+- mextract field(s) where condition
+
+  this is a bit trickier, and could make use of the code in the dvo
+  math functions
+
+- dvo select from mysql
+
+  not very difficult: need to define the commands to select the
+  database and set up a connection, then parse the select line into
+  the appropriate format.  just need to as the db for the type of the
+  fields that are requested: must be numerical.
+
+  select field,field,field from table where condition
+
+- change vectors to doubles
+
+  probably not a terrible job.  this will depend on how the union is
+  defined.  I don't want this to explode the size of an image!
+
+---------
+
+I now have the ability to load and save DVO databases in old formats,
+with automatic conversion to the current 'internal' representations
+for the average, measure, and image tables.  an important concern with
+this process is ensuring that we track the old layout names and avoid
+breaking existing databases.  Here is a list of old databases, some of
+which may not have been written with the right names, or with
+inaccurate names.  these can be fixed by setting the FORMAT and MODE
+header keywords with the 'fits_insert' command:
+
+- LONEOS : old-style cmp files (ie, RAW), with the 'LONEOS' format.
+- CFHT Elixir databases: cmp files, ELIXIR format
+- Brandon's Taurus db: incorrect 'PANSTARRS' format, should be called
+  'PSTEST1'.  drop support for this eventually? have brandon migrate to
+  the new panstarrs formats?
+
+I am going to use the following naming convention for future db table
+updates:
+
+- PANSTARRS.DEV.0, PANSTARRS.DEV.1, etc
+- PANSTARRS.PS1.0, PANSTARRS.PS1.1, etc
+- PANSTARRS.PS4.0, PANSTARRS.PS4.1, etc
+
+The next version of the DVO format needs several major changes:
+
+  - unique IDs in the measure and average tables.
+  - reference to the source image in the measure table.
+  - chip X,Y coordinates and errors in the measure table
+
+  - remove primary average magnitude from average.d and only use
+    the secfilt.d table for the average magnitudes
+
+These are fairly substantial modifications.  Can I do these changes on
+an appropriate timescale to get Dave Monet working with relastro?  Is
+it necessary to do these all at once?
+
+------
+
+DVO has undergone at least two bit-incompatible versions of the data
+structures to date: the 'loneos' version and the 'elixir' version.  
+
+the 'loneos' version was used for the loneos database currently still
+in my dataspace.
+
+the 'elixir' version, which extended some of the structures and added
+the Secfilt concept, was added at CFHT and is used for all of the
+existing CFHT elixir databases.
+
+For DVO-2 development, I will need to make a few modifications to
+those tables.  It will be useful to allow versions of DVO which
+interact with these different versions correctly.  At one level, this
+simply means changing the structures in the included file.  More
+difficult is that certain functions use elements of the structures
+from one version which are not available in the other versions.  
+
+I am going to explore this process briefly to get DVO-2 started,
+particularly so I can solve the astrometric problems which are
+currently limited by the use of floats to represent RA & DEC.  
+
+I am going to make the selection of the structures depend on #define
+values in the header files.  Currently, everybody which works with the
+dvo files includes loneos.h.  This should be broken out to a loneos.h,
+elixir.h, panstarrs.h, etc, all independently included from within
+dvo.h, which will be the include file called within the user-level
+programs.
+
+Translations
+
+/* average data as stored in the LONEOS database -- must be translated on load */
+
+Loneos:Measure -> Elixir:Measure
+/* OLD LONEOS VERSION.  NOT BYTE COMPATIBLE WITH NEW VERSION 
+   (can be loaded with translation, losing the exptime,
+   and limited the number of Naverage to 0xffffff - in load_catalog) */
+
+------
+
+the DVO structure Catalog contains the data from a single sky patch, including:
+    average, measure, missing, and secfilt.  
+
+the catalog may be saved in one of 4 modes (catalog.catmode):
+    raw   : the old Elixir style which is a header + 4 binary tables
+    mef   : catalog is a single file with header and 4 fits tables
+    split : catalog is 5 files, one with the header and one for each table
+    mysql : catalog is 4 (or 5?) mysql tables
+
+    in split mode, the filename is the file of the descriptive header
+    and the averages, the measure, missing, and secfilt data are
+    stored in additional files referenced in the header and named
+    based on the filename (replacing extension .cpa with .cpb, .cpc,
+    .cpd, for example)
+
+    
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/fits_db.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/fits_db.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/fits_db.txt	(revision 17422)
@@ -0,0 +1,5 @@
+
+FITS_DB represents a FITS table file, including the primary header,
+any existing matrix, the table header, and the table data as either an
+ftable (real) or vtable (virtual) FITS table.  
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/doc/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/doc/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/doc/notes.txt	(revision 17422)
@@ -0,0 +1,55 @@
+
+Adding a new dvo catalog format.  Assume the new format name is 'foo'.
+
+1) create the autocode definition files for average, measure, secfilt, image
+   these files must be a subset of the internal versions of these same
+   tables.  if you intend to add fields which don't exist in the
+   internal tables, you must update the internal tables as well.  the
+   naming convention is: average-foo.d, etc.
+
+2) add the new definition files to libautocode/Makefile.Targets (both
+   .o and .h lists).
+
+3) set the STRUCT and EXTNAME for these definitions files to have a
+   unique value. the naming convention for EXTNAME is DVO_AVERAGE_FOO
+   and for STRUCT is AverageFoo (add version info with underscores and
+   uppercase, eg: AveragePanstarrs_DEV_0, AveragePanstarrs_PS1_2).
+
+4) create a DVOTableFormat entry for the new format
+   (libdvo/include/dvo.h).  the naming convetion is DVO_FORMAT_FOO.
+
+5) add an entry for the new format in dvo_catalog_catformat
+ (libdvo/src/dvo_catalog.c).  The name should be "FOO".
+
+6) add entry for structure size in dvo_catalog_load_raw
+   (libdvo/src/dvo_catalog_raw.c), since this is not available from
+   the header.
+
+7) add entries in ReadRawAverage, WriteRawAverage, ReadRawMeasure,
+   WriteRawMeasure, ReadRawSecFilt, WriteRawSecFilt.  Make sure to use
+   the new STRUCT names. the conversion function gfit_convert_Foo will
+   be automatically generated.
+
+8) add entry in WriteRawAverage, making sure to use the new STRUCT
+   name. the conversion function gfit_convert_Foo will be
+   automatically generated.  
+
+9) create a new conversion file dvo_convert_foo.c and define the
+   internal to Foo conversions.
+
+* Note some esoteric format issues:  
+
+  - LONEOS and ELIXIR lack chip coordinates, and must apply an
+    on-the-fly conversion, implemented in dvo_catalog_chipcoords. 
+
+  - LONEOS and ELIXIR use the old primary/secondary photcode concept,
+    and must have one photcode dataset extracted from the average
+    table on read and resupplied on write.  this happens in
+    dvo_catalog_save_mef, dvo_catalog_save_raw,
+    dvo_catalog_save_split, dvo_catalog_update_split
+
+  - LONEOS and ELIXIR have old versions where the header does not
+    specify the type explicitly.  the functions dvo_catalog_load_raw
+    and dvo_image_load_raw identify these types based on special
+    keywords.
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.h	(revision 17422)
@@ -0,0 +1,448 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <autocode.h>
+
+# ifndef DVO_H
+# define DVO_H
+
+/* DVO table modes */
+typedef enum {DVO_MODE_UNDEF, DVO_MODE_RAW, DVO_MODE_MEF, DVO_MODE_SPLIT, DVO_MODE_MYSQL} DVOTableMode;
+
+/* DVO table formats */
+typedef enum {DVO_FORMAT_UNDEF, 
+	      DVO_FORMAT_INTERNAL, 
+	      DVO_FORMAT_ELIXIR, 
+	      DVO_FORMAT_LONEOS, 
+	      DVO_FORMAT_PANSTARRS_DEV_0,
+	      DVO_FORMAT_PANSTARRS_DEV_1,
+	      DVO_FORMAT_PS1_DEV_1,
+	      DVO_FORMAT_PS1_DEV_2
+} DVOTableFormat;
+
+/* image data modes in RegImage */
+typedef enum {T_UNDEF = -1, T_NONE, T_OBJECT, T_DARK, T_BIAS, T_FLAT, T_MASK, T_FRINGE, T_SCATTER, T_MODES, T_FRINGEPTS, T_ANY, N_TYPE} ElixirDetrendTypes;
+typedef enum {M_UNDEF = -1, M_NONE, M_MEF, M_SPLIT, M_SINGLE, M_CUBE, M_SLICE, M_MODES, N_MODE} ElixirDetrendModes;
+
+typedef enum {DVO_CAT_OPEN_FAIL, DVO_CAT_OPEN_OK, DVO_CAT_OPEN_EMPTY} DVOCatalogOpenModes;
+
+typedef enum {
+  PROJ_NONE, // undefined
+  PROJ_ZEA, // zenithal
+  PROJ_ZPL, // zenithal
+  PROJ_ARC, // zenithal
+  PROJ_STG, // zenithal
+  PROJ_SIN, // zenithal
+  PROJ_TAN, // zenithal
+  PROJ_TNX, // zenithal
+  PROJ_DIS, // zenithal (TAN + polyterms)
+  PROJ_LIN, // cartesian
+  PROJ_PLY, // cartesian
+  PROJ_WRP, // cartesian
+  PROJ_AIT, // pseudocyl
+  PROJ_GLS, // pseudocyl
+  PROJ_PAR, // pseudocyl
+} OhanaProjection;
+
+typedef enum {
+  PROJ_MODE_NONE,
+  PROJ_MODE_CARTESIAN,
+  PROJ_MODE_ZENITHAL,
+  PROJ_MODE_PSEUDOCYL,
+} OhanaProjectionMode;
+
+// these are used as NAN for types of int values
+typedef enum {
+    NAN_S_CHAR  = 0x7f,
+    NAN_U_CHAR  = 0xff,   // was NO_ERR
+    NAN_S_SHORT = 0x7fff, // was NO_MAG
+    NAN_U_SHORT = 0xffff, 
+    NAN_S_INT   = 0x7fffffff,
+    NAN_U_INT   = 0xffffffff,
+} DVO_INT_NAN;
+
+# ifndef NAN
+# ifndef BYTE_SWAP
+#  define __nan_bytes           { 0x7f, 0xc0, 0, 0 }
+# else
+#  define __nan_bytes           { 0, 0, 0xc0, 0x7f }
+# endif
+static union { unsigned char __c[4]; float __d; } __nan_union
+    __attribute_used__ = { __nan_bytes };
+# define NAN    (__nan_union.__d)
+# endif
+
+/* RegImage.flag values */
+# define IMREG_DIST  0x01 /* image distributed, only imregister-3.0 */
+
+/* catalog values to be loaded */
+# define LOAD_NONE 	0x00
+# define LOAD_AVES 	0x01
+# define LOAD_MEAS 	0x02
+# define LOAD_MISS 	0x04
+# define LOAD_SECF 	0x08 
+# define LOAD_MEAS_META 0x10
+
+/* photometry code types */
+// # define PHOT_PRI 0x01
+# define PHOT_SEC 0x02
+# define PHOT_DEP 0x03
+# define PHOT_REF 0x04
+# define PHOT_ALT 0x05  /* never stored, only for look-ups */
+# define PHOT_MAG 0x06  /* generic magnitude; never stored */
+
+/* Image.code values.  these are codes to note bad images */
+# define ID_IMAGE_NEW   0x0000  /* no nrphot attempted */
+# define ID_IMAGE_NOCAL 0x0001  /* used within nrphot to mean "don't apply fit" */
+# define ID_IMAGE_POOR  0x0002  /* relphot says image is bad */
+# define ID_IMAGE_SKIP  0x0004  /* external information image is bad */
+# define ID_IMAGE_FEW   0x0008  /* currently too few measurements for good value */
+
+/* Measure.flags values */
+// XXX replace the # defines with typedef enum
+# define ID_MEAS_NOCAL        0x0001  /* detection ignored for this analysis (photcode, time range) -- internal only */
+# define ID_MEAS_POOR_PHOTOM  0x0002  /* detection is photometry outlier					     */	  
+# define ID_MEAS_SKIP_PHOTOM  0x0004  /* detection was ignored for photometry measurement			     */	  
+# define ID_MEAS_AREA         0x0008  /* detection near image edge						     */
+# define ID_MEAS_POOR_ASTROM  0x0010  /* detection is astrometry outlier					     */	  
+# define ID_MEAS_SKIP_ASTROM  0x0020  /* detection was ignored for astrometry measurement			     */	  
+# define ID_MEAS_UNDEF_1      0x0040  /* unused									     */
+# define ID_MEAS_UNDEF_2      0x0080  /* unused									     */
+# define ID_MEAS_BLEND_MEAS   0x0100  /* detection is within radius of multiple objects				     */
+# define ID_MEAS_BLEND_OBJ    0x0200  /* multiple detections within radius of object				     */
+# define ID_MEAS_UNDEF_3      0x0400  /* unused									     */
+# define ID_MEAS_UNDEF_4      0x0800  /* unused									     */
+# define ID_MEAS_BLEND_MEAS_X 0x1000  /* detection is within radius of multiple objects across catalogs		     */
+# define ID_MEAS_ARTIFACT     0x2000  /* detection is thought to be non-astronomical				     */
+# define ID_MEAS_UNDEF_5      0x4000  /* unused									     */
+# define ID_MEAS_UNDEF_6      0x8000  /* unused									     */
+
+// XXX we used these names previously in markstar: replace with ID_MEAS_ARTIFACT
+// # define ID_MEAS_TRAIL        0x2000
+// # define ID_MEAS_GHOST        0x4000
+
+/* some subtle distinctions between the blend flags:
+   BLEND_IMAGE: the star on an image is matched with more 
+   than one star in the catalog (image has worse seeing than catalog)
+   BLEND_CATALOG: the star in the catalog is matched with more 
+   than one star on the image (image has better seeing than catalog)
+   CALIBRATED: relative photometry has been performed on this measurement
+   BLEND_IMAGE_NEIGHBOR: the star on an image is matched with more 
+   than one star in the catalog, but not in the same catalog file.
+*/
+
+/* Average.code values */
+# define ID_STAR_FEW   0x0001 /* used within relphot: skip star */
+# define ID_STAR_POOR  0x0002 /* used within relphot: skip star */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_VARIABLE   0x2000 /* not currently set? */
+# define ID_ASTEROID   0x2000 /* identified with an asteroid */
+# define ID_BAD_OBJECT 0x4000 /* if all measurements are bad, set this bit */
+# define ID_MOVING     0x8000
+# define ID_ROCK       0xa000 /* 0x8000 + 0x2000 */
+# define ID_GHOST      0xc001 /* 0x8000 + 0x4000 + 0x0001 */
+# define ID_TRAIL      0xc002 /* 0x8000 + 0x4000 + 0x0002 */
+# define ID_BLEED      0xc003 /* 0x8000 + 0x4000 + 0x0003 */ 
+# define ID_COSMIC     0xc004 /* 0x8000 + 0x4000 + 0x0004 */ 
+
+/*** general dvo structures (internal use only / not IO) ***/
+
+/* FITS DB structure */
+typedef struct {
+  FILE  *f;
+  char  *filename;
+  int    dbstate;
+  int    lockstate;
+  double timeout;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  VTable vtable;
+  int    mode;          /* what data storage mode is used for disk file? */
+  int    format;        /* what data format is used for disk file? */
+  int    virtual;       /* is table in ftable or vtable? */
+  int    swapped;       /* is table in internal byte-order? */
+} FITS_DB;
+
+/* the basic HST GSC layout corresponds to a depth of 3 */
+# define SKY_DEPTH_HST 3
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  int ownElements; 				  /* does this list own filename, regions? */
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+# if (0)
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double DEC[2], RA[2];
+} GSCRegion;
+# endif
+
+typedef struct {
+  int Ncode;					  // number of photcodes
+  int Nsecfilt;					  // number of average magnitudes
+  int hashcode[0x10000];		  // index from photcode value to sequence
+  int hashNsec[0x10000];		  // index from photcode value to Nsec seq
+  int codeNsec[0x10000];		  // index from Nsec seq to photcode value
+  PhotCode *code;
+} PhotCodeData;
+
+/* a catalog contains this data */
+typedef struct Catalog {
+  char *filename;			/* catalog file */
+  FILE *f;  				/* file descriptor */
+  Header  header;
+
+  /* data in the catalog file */
+  Average *average;
+  Measure *measure; 
+  Missing *missing; 
+  SecFilt *secfilt;
+
+  int Nsecfilt;  /* number of secfilt entries for each average entry */
+  int Naverage,   Nmeasure,   Nmissing,   Nsecf_mem;  /* current number of each component in memory */
+  int Naves_disk, Nmeas_disk, Nmiss_disk, Nsecf_disk; /* current number of each component on disk */
+  int Naves_off,  Nmeas_off,  Nmiss_off,  Nsecf_off;  /* index of first loaded data value */
+
+  /* note the different counting for Nsecfilt:
+     number of secfilt rows on disk is: Nave_disk * Nsecfilt
+     number of secfilt rows in mem  is: Naverage * Nsecfilt
+     *** that is just silly, and bad: convert to using Nsec_mem, Nsec_disk, Nsec_off.
+     *** unless we always require the secfilt and average entries to be loaded sychronously.
+   */
+
+  /* pointers to split data files */
+  struct Catalog *measure_catalog;		/* measure catalog data (split) */
+  struct Catalog *missing_catalog;		/* missing catalog data (split) */
+  struct Catalog *secfilt_catalog;		/* secfilt catalog data (split) */
+
+  unsigned int objID;
+  unsigned int catID;
+
+  /* extra catalog information */
+  int lockmode;
+  int catmode;				/* storage mode (raw, mef, split, mysql) */
+  int catformat;			/* storage format (elixir, panstarrs, etc) */
+  int catflags;				/* choices to be loaded */
+  int sorted;				/* is measure table average-sorted? */
+  
+  /* pointers for data manipulation */
+  int *found;
+  int *image;
+  int *mosaic;
+  float *X;
+  float *Y;
+
+} Catalog;
+
+/*** prototypes ***/
+
+/* in gfits_db.c */
+int   gfits_db_init                PROTO((FITS_DB *db));
+int   gfits_db_create              PROTO((FITS_DB *db));
+int   gfits_db_lock                PROTO((FITS_DB *db, char *filename));
+int   gfits_db_load                PROTO((FITS_DB *db));
+int   gfits_db_load_segment        PROTO((FITS_DB *db, int start, int Nrows));
+int   gfits_db_save                PROTO((FITS_DB *db));
+int   gfits_db_update              PROTO((FITS_DB *db));
+int   gfits_db_close               PROTO((FITS_DB *db));
+int   gfits_db_free                PROTO((FITS_DB *db));
+
+/* in coords.c, using libautocode/def/coords.d */
+int  XY_to_LM (double *L, double *M, double x,  double y,   Coords *coords);
+int  LM_to_XY (double *x,  double *y,   double L, double M, Coords *coords);
+int  RD_to_LM (double *L, double *M, double ra,  double dec,   Coords *coords);
+int  LM_to_RD (double *ra, double *dec,   double L, double M, Coords *coords);
+int  XY_to_RD (double *ra, double *dec, double x,  double y,   Coords *coords);
+int  RD_to_XY (double *x,  double *y,   double ra, double dec, Coords *coords);
+int  fXY_to_RD (float *ra, float *dec, double x,  double y,   Coords *coords);
+int  fRD_to_XY (float *x,  float *y,   double ra, double dec, Coords *coords);
+int  GetCoords (Coords *coords, Header *header);
+int  PutCoords (Coords *coords, Header *header);
+void RegisterMosaic (Coords *coords);
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch);
+OhanaProjection GetProjection (char *ctype);
+int SetProjection (char *ctype, OhanaProjection proj);
+OhanaProjectionMode GetProjectionMode (OhanaProjection proj);
+
+char *libdvo_version ();
+
+int isRegisteredMosaic ();
+int GetRegisteredMosaic ();
+int GetMosaicCoords (Coords *coords);
+int FindMosaicForImage (Image *images, int Nimages, int entry);
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry);
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry);
+int BuildChipMatch (Image *images, int Nimages);
+void SetImageCorners (double *X, double *Y, Image *image);
+
+short int putMi (double value);
+double getMi (short int value);
+void returnMcal (Image *image, double *c);
+void assignMcal (Image *image, double *c, int order);
+double applyMcal (Image *image, double x, double y);
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order);
+
+PhotCode *GetPhotcodebyName (char *name);
+PhotCode *GetPhotcodeEquivbyName (char *name);
+PhotCode *GetPhotcodebyCode (int code);
+PhotCode *GetPhotcodebyNsec (int Nsec);
+PhotCode *GetPhotcodeEquivbyCode (int code);
+char     *GetPhotcodeNamebyCode (int code);
+
+float PhotInst (Measure *measure);
+float PhotCat (Measure *measure);
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+
+float PhotColorForCode (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color);
+
+PhotCodeData *GetPhotcodeTable ();
+
+int LoadPhotcodes (char *catdir_file, char *master_file);
+int LoadPhotcodesText (char *filename);
+int LoadPhotcodesFITS (char *filename);
+int SavePhotcodesText (char *filename);
+int SavePhotcodesFITS (char *filename);
+
+void PrintPhotcodeNamebyCode (FILE *f, char *format, int code);
+
+int GetPhotcodeCodebyName (char *name);
+int GetPhotcodeEquivCodebyName (char *name);
+int GetPhotcodeEquivCodebyCode (int code);
+int GetPhotcodeNsec (int code);
+int GetPhotcodeNsecfilt ();
+void SetZeroPoint (double ZP);
+double GetZeroPoint ();
+int *GetPhotcodeEquivList (int code, int *nlist);
+void ParseColorTerms (char *terms, float *X, int *N);
+
+int get_image_type (char *name);
+char *get_type_name (int type);
+int get_image_mode (char *name);
+char *get_mode_name (int mode);
+
+/* these functions refer to the DVO structures defined in the includes above
+ *  they are being replaced with autocode entries (drop when totally autocoded)
+ */
+
+int   Fread (void *ptr, int size, int nitems, FILE *f, char *type);
+int   Fwrite (void *ptr, int size, int nitems, FILE *f, char *type);
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+int   ConvertStruct (char *buffer, int size, int Nbytes, char *type);
+
+/** dvo_catalog APIs */
+void dvo_catalog_init (Catalog *catalog, int complete);
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog);
+void dvo_catalog_free (Catalog *catalog);
+void dvo_catalog_free_data (Catalog *catalog);
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+int dvo_catalog_lock (Catalog *catalog, int lockmode);
+int dvo_catalog_unlock (Catalog *catalog);
+int dvo_catalog_load (Catalog *catalog, int VERBOSE);
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode);
+int dvo_catalog_save (Catalog *catalog, char VERBOSE);
+int dvo_catalog_save_complete (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update (Catalog *catalog, char VERBOSE);
+int dvo_catalog_catformat (char *catformat);
+int dvo_catalog_catmode (char *catmode);
+void dvo_catalog_test (Catalog *catalog, int halt);
+
+/* catmode-specific APIs */
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_save_split_complete (Catalog *catalog, char VERBOSE);
+
+int dvo_catalog_load_segment (Catalog *catalog, int VERBOSE, int start, int Nrows);
+int dvo_catalog_load_segment_split (Catalog *catalog, int VERBOSE, int start, int Nrows);
+
+/*** conversion functions / I/O conversions ***/
+Average *ReadRawAverage (FILE *f, int Naverage, int format, SecFilt **primary);
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format);
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format);
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format, SecFilt *primary);
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format);
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format);
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format, SecFilt **primary);
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format);
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format);
+int FtableToImage (FTable *ftable, Header *theader, int *format);
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format, SecFilt *primary);
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format);
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format);
+int ImageToFtable (FTable *ftable, Header *theader, int format);
+int ImageToVtable (VTable *vtable, Header *theader, int format);
+
+# include "loneos_defs.h"
+# include "elixir_defs.h"
+# include "panstarrs_dev_0_defs.h"
+# include "panstarrs_dev_1_defs.h"
+# include "ps1_dev_1_defs.h"
+# include "ps1_dev_2_defs.h"
+# include "ps1_dev_3_defs.h"
+
+/*** DVO image db I/O Functions ***/
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate);
+int dvo_image_unlock (FITS_DB *db);
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_save (FITS_DB *db, int VERBOSE);
+int dvo_image_update (FITS_DB *db, int VERBOSE);
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew);
+void dvo_image_create (FITS_DB *db, double ZeroPoint);
+
+/* skyregion APIs */
+int        SkyTableSave        	   PROTO((SkyTable *table, char *filename));
+SkyTable  *SkyTableLoad        	   PROTO((char *filename, int VERBOSE));
+SkyTable  *SkyTableFromGSC     	   PROTO((char *filename, int depth, int VERBOSE));
+SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int depth, int VERBOSE));
+int        SkyTableSetDepth    	   PROTO((SkyTable *sky, int depth));
+SkyList   *SkyRegionByPoint    	   PROTO((SkyTable *table, int depth, double ra, double dec));
+SkyList   *SkyListByPoint      	   PROTO((SkyTable *table, double ra, double dec));
+SkyList   *SkyListByRadius     	   PROTO((SkyTable *table, int depth, double RA, double DEC, double radius));
+SkyList   *SkyListByPatch      	   PROTO((SkyTable *table, int depth, SkyRegion *patch));
+SkyList   *SkyListByName      	   PROTO((SkyTable *table, char *name));
+SkyList   *SkyListByImage      	   PROTO((SkyTable *table, int depth, Image *image));
+SkyList   *SkyListByBounds     	   PROTO((SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+SkyList   *SkyListChildrenByBounds PROTO((SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+int        SkyListMerge     	   PROTO((SkyList **outlist, SkyList *newlist));
+int        SkyListFree             PROTO((SkyList *list));
+int        SkyTableFree            PROTO((SkyTable *table));
+int        SkyListSetFilenames     PROTO((SkyList *list, char *path, char *ext));
+int        SkyTableSetFilenames    PROTO((SkyTable *sky, char *path, char *ext));
+
+/* dvo-specific sorting functions */
+void sortave (Average *ave, int N);
+void sort_image_subset (Image *image, int *subset, int N);
+void sort_coords_index (double *X, double *Y, int *S, int N);
+void sort_stars_ra (Stars *stars, int N);
+void sort_regions (SkyRegion *region, int N);
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.update.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.update.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/dvo.update.h	(revision 17422)
@@ -0,0 +1,399 @@
+# include <ohana.h>
+# include <gfitsio.h>
+# include <autocode.h>
+
+# ifndef DVO_H
+# define DVO_H
+
+/*
+  # define ELIXIR 1
+  # define PANSTARRS 0
+  # define LONEOS 0
+*/
+
+/*** named data values (convert all to enums?) ***/
+
+/* DVO table modes */
+enum {DVO_MODE_UNDEF, DVO_MODE_RAW, DVO_MODE_MEF, DVO_MODE_SPLIT, DVO_MODE_MYSQL} DVOTableMode;
+
+/* DVO table formats */
+enum {DVO_FORMAT_UNDEF, DVO_FORMAT_INTERNAL, DVO_FORMAT_ELIXIR, DVO_FORMAT_LONEOS, DVO_FORMAT_PANSTARRS, DVO_FORMAT_PMTEST} DVOTableFormat;
+
+/* image data modes in RegImage */
+enum {T_UNDEF = -1, T_NONE, T_OBJECT, T_DARK, T_BIAS, T_FLAT, T_MASK, T_FRINGE, T_SCATTER, T_MODES, T_FRINGEPTS, T_ANY, N_TYPE};
+enum {M_UNDEF = -1, M_NONE, M_MEF, M_SPLIT, M_SINGLE, M_CUBE, M_SLICE, M_MODES, N_MODE};
+
+typedef enum {
+    PROJ_NONE, // undefined
+    PROJ_ZEA, // zenithal
+    PROJ_ZPL, // zenithal
+    PROJ_ARC, // zenithal
+    PROJ_STG, // zenithal
+    PROJ_SIN, // zenithal
+    PROJ_TAN, // zenithal
+    PROJ_DIS, // zenithal (TAN + polyterms)
+    PROJ_LIN, // cartesian
+    PROJ_PLY, // cartesian
+    PROJ_WRP, // cartesian
+    PROJ_AIT, // pseudocyl
+    PROJ_GLS, // pseudocyl
+    PROJ_PAR, // pseudocyl
+} OhanaProjections;
+
+typedef enum {
+  PROJ_MODE_NONE,
+  PROJ_MODE_CARTESIAN,
+  PROJ_MODE_ZENITHAL,
+  PROJ_MODE_PSEUDOCYL,
+} OhanaProjectionModes;
+
+/* RegImage.flag values */
+# define IMREG_DIST  0x01 /* image distributed, only imregister-3.0 */
+
+/* catalog values to be loaded */
+# define LOAD_AVES 	0x01
+# define LOAD_MEAS 	0x02
+# define LOAD_MISS 	0x04
+# define LOAD_SECF 	0x08 
+# define LOAD_MEAS_META 0x10
+
+/* invalid mag value */
+# define NO_MAG    0x7fff
+# define NO_ERR    0xff
+
+/* photometry code types */
+# define PHOT_PRI 0x01
+# define PHOT_SEC 0x02
+# define PHOT_DEP 0x03
+# define PHOT_REF 0x04
+# define PHOT_ALT 0x05  /* never stored, only for look-ups */
+
+/* Image.code values.  these are codes to note bad images */
+# define ID_IMAGE_NEW   0x0000  /* no nrphot attempted */
+# define ID_IMAGE_NOCAL 0x0001  /* used within nrphot to mean "don't apply fit" */
+# define ID_IMAGE_POOR  0x0002  /* relphot says image is bad */
+# define ID_IMAGE_SKIP  0x0004  /* external information image is bad */
+# define ID_IMAGE_FEW   0x0008  /* currently too few measurements for good value */
+
+/* Measure.flags values */
+# define ID_MEAS_NOCAL        0x0001
+# define ID_MEAS_POOR         0x0002
+# define ID_MEAS_SKIP         0x0004
+# define ID_MEAS_AREA         0x0008
+# define BLEND_IMAGE          0x0100 
+# define BLEND_CATALOG        0x0200
+# define BLEND_IMAGE_NEIGHBOR 0x1000
+# define ID_MEAS_TRAIL        0x2000
+# define ID_MEAS_GHOST        0x4000
+
+/* some subtle distinctions between the blend flags:
+   BLEND_IMAGE: the star on an image is matched with more 
+   than one star in the catalog (image has worse seeing than catalog)
+   BLEND_CATALOG: the star in the catalog is matched with more 
+   than one star on the image (image has better seeing than catalog)
+   CALIBRATED: relative photometry has been performed on this measurement
+   BLEND_IMAGE_NEIGHBOR: the star on an image is matched with more 
+   than one star in the catalog, but not in the same catalog file.
+*/
+
+/* Average.code values */
+# define ID_STAR_FEW   0x0001 /* used within relphot: skip star */
+# define ID_STAR_POOR  0x0002 /* used within relphot: skip star */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_VARIABLE   0x2000 /* not currently set? */
+# define ID_ASTEROID   0x2000 /* identified with an asteroid */
+# define ID_BAD_OBJECT 0x4000 /* if all measurements are bad, set this bit */
+# define ID_MOVING     0x8000
+# define ID_ROCK       0xa000 /* 0x8000 + 0x2000 */
+# define ID_GHOST      0xc001 /* 0x8000 + 0x4000 + 0x0001 */
+# define ID_TRAIL      0xc002 /* 0x8000 + 0x4000 + 0x0002 */
+# define ID_BLEED      0xc003 /* 0x8000 + 0x4000 + 0x0003 */ 
+# define ID_COSMIC     0xc004 /* 0x8000 + 0x4000 + 0x0004 */ 
+
+/*** general dvo structures (internal use only / not IO) ***/
+
+/* FITS DB structure */
+typedef struct {
+  FILE  *f;
+  char  *filename;
+  int    dbstate;
+  int    lockstate;
+  double timeout;
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  VTable vtable;
+  int    mode;          /* what data storage mode is used for disk file? */
+  int    format;        /* what data format is used for disk file? */
+  int    virtual;       /* is table in ftable or vtable? */
+  int    swapped;       /* is table in internal byte-order? */
+} FITS_DB;
+
+/* the basic HST GSC layout corresponds to a depth of 3 */
+# define SKY_DEPTH_HST 3
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+# if (0)
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double DEC[2], RA[2];
+} GSCRegion;
+# endif
+
+typedef struct {
+  int Ncode;
+  int Nsecfilt;
+  int hashcode[0x10000];
+  int hashNsec[0x10000];
+  PhotCode *code;
+} PhotCodeData;
+
+/* a catalog contains this data */
+typedef struct Catalog {
+  char *filename;			/* catalog file */
+  FILE *f;  				/* file descriptor */
+  Header  header;
+
+  /* data in the catalog file */
+  Average *average;
+  Measure *measure; 
+  Missing *missing; 
+  SecFilt *secfilt;
+  int Naverage, Nmeasure, Nmissing, Nsecfilt;   /* current number of each component */
+  int Nave_disk, Nmeas_disk, Nmiss_disk;        /* number of component on disk */
+  int Nmeas_off;			        /* dist seq of first loaded data value */
+  /* note the different counting for Nsecfilt */
+
+  /* pointers to split data files */
+  struct Catalog *measure_catalog;		/* measure catalog data (split) */
+  struct Catalog *missing_catalog;		/* missing catalog data (split) */
+  struct Catalog *secfilt_catalog;		/* secfilt catalog data (split) */
+
+  /* extra catalog information */
+  int lockmode;
+  int catmode;				/* storage mode (raw, mef, split, mysql) */
+  int catformat;			/* storage format (elixir, panstarrs, etc) */
+  int catflags;				/* choices to be loaded */
+  int sorted;				/* is measure table average-sorted? */
+  
+  /* pointers for data manipulation */
+  int *found;
+  int *image;
+  int *mosaic;
+  float *X;
+  float *Y;
+
+} Catalog;
+
+/*** prototypes ***/
+
+/* in gfits_db.c */
+int   gfits_db_init                PROTO((FITS_DB *db));
+int   gfits_db_create              PROTO((FITS_DB *db));
+int   gfits_db_lock                PROTO((FITS_DB *db, char *filename));
+int   gfits_db_load                PROTO((FITS_DB *db));
+int   gfits_db_load_segment        PROTO((FITS_DB *db, int start, int Nrows));
+int   gfits_db_save                PROTO((FITS_DB *db));
+int   gfits_db_update              PROTO((FITS_DB *db));
+int   gfits_db_close               PROTO((FITS_DB *db));
+int   gfits_db_free                PROTO((FITS_DB *db));
+
+/* in coords.c, using libautocode/def/coords.d */
+int  XY_to_RD (double *ra, double *dec, double x,  double y,   Coords *coords);
+int  RD_to_XY (double *x,  double *y,   double ra, double dec, Coords *coords);
+int  fXY_to_RD (float *ra, float *dec, double x,  double y,   Coords *coords);
+int  fRD_to_XY (float *x,  float *y,   double ra, double dec, Coords *coords);
+int  GetCoords (Coords *coords, Header *header);
+int  PutCoords (Coords *coords, Header *header);
+void RegisterMosaic (Coords *coords);
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch);
+
+int FindMosaicForImage (Image *images, int Nimages, int entry);
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry);
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry);
+int BuildChipMatch (Image *images, int Nimages);
+void SetImageCorners (double *X, double *Y, Image *image);
+
+short int putMi (double value);
+double getMi (short int value);
+void returnMcal (Image *image, double *c);
+void assignMcal (Image *image, double *c, int order);
+double applyMcal (Image *image, double x, double y);
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order);
+
+PhotCode *GetPhotcodebyName (char *name);
+PhotCode *GetPhotcodeEquivbyName (char *name);
+PhotCode *GetPhotcodebyCode (int code);
+PhotCode *GetPhotcodebyNsec (int Nsec);
+PhotCode *GetPhotcodeEquivbyCode (int code);
+char     *GetPhotcodeNamebyCode (int code);
+
+float PhotInst (Measure *measure);
+float PhotCat (Measure *measure);
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+
+# if (0)
+short iPhotInst (Measure *measure);
+short iPhotAbs (Measure *measure);
+short iPhotCat (Measure *measure);
+short iPhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+short iPhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+short iPhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+short iPhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+short iPhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+short iPhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
+short iPhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
+# endif
+
+float iPhotColor (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color);
+
+int LoadPhotcodes (char *filename);
+int GetPhotcodeCodebyName (char *name);
+int GetPhotcodeEquivCodebyName (char *name);
+int GetPhotcodeEquivCodebyCode (int code);
+int GetPhotcodeNsec (int code);
+int GetPhotcodeNsecfilt ();
+void SetZeroPoint (double ZP);
+int *GetPhotcodeEquivList (int code, int *nlist);
+void ParseColorTerms (char *terms, float *X, int *N);
+
+int get_image_type (char *name);
+char *get_type_name (int type);
+int get_image_mode (char *name);
+char *get_mode_name (int mode);
+
+/* these functions refer to the DVO structures defined in the includes above
+ *  they are being replaced with autocode entries (drop when totally autocoded)
+ */
+
+int   Fread (void *ptr, int size, int nitems, FILE *f, char *type);
+int   Fwrite (void *ptr, int size, int nitems, FILE *f, char *type);
+int   ByteSwap (char *ptr, int size, int nitems, char *type);
+int   ConvertStruct (char *buffer, int size, int Nbytes, char *type);
+
+/** dvo_catalog APIs */
+void dvo_catalog_init (Catalog *catalog, int complete);
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog);
+void dvo_catalog_free (Catalog *catalog);
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend);
+int dvo_catalog_lock (Catalog *catalog, int lockmode);
+int dvo_catalog_unlock (Catalog *catalog);
+int dvo_catalog_load (Catalog *catalog, int VERBOSE);
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode);
+int dvo_catalog_save (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update (Catalog *catalog, char VERBOSE);
+int dvo_catalog_catformat (char *catformat);
+int dvo_catalog_catmode (char *catmode);
+void dvo_catalog_test (Catalog *catalog, int halt);
+
+/* catmode-specific APIs */
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE);
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE);
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE);
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE);
+
+/*** conversion functions / I/O conversions ***/
+Average *ReadRawAverage (FILE *f, int Naverage, int format);
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format);
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format);
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format);
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format);
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format);
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format);
+Average *AverageLoneosToInternal (AverageLoneos *in, int Nvalues);
+Average *AverageElixirToInternal (AverageElixir *in, int Nvalues);
+Average *AveragePanstarrsToInternal (AveragePanstarrs *in, int Nvalues);
+Average *AveragePMtestToInternal (AveragePMtest *in, int Nvalues);
+AverageLoneos *AverageInternalToLoneos (Average *in, int Nvalues);
+AverageElixir *AverageInternalToElixir (Average *in, int Nvalues);
+AveragePanstarrs *AverageInternalToPanstarrs (Average *in, int Nvalues);
+AveragePMtest *AverageInternalToPMtest (Average *in, int Nvalues);
+
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format);
+Measure *MeasureLoneosToInternal (MeasureLoneos *in, int Nvalues);
+Measure *MeasureElixirToInternal (MeasureElixir *in, int Nvalues);
+Measure *MeasurePanstarrsToInternal (MeasurePanstarrs *in, int Nvalues);
+MeasureLoneos *MeasureInternalToLoneos (Measure *in, int Nvalues);
+MeasureElixir *MeasureInternalToElixir (Measure *in, int Nvalues);
+MeasurePanstarrs *MeasureInternalToPanstarrs (Measure *in, int Nvalues);
+
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format);
+SecFilt *SecFiltLoneosToInternal (SecFiltLoneos *in, int Nvalues);
+SecFilt *SecFiltElixirToInternal (SecFiltElixir *in, int Nvalues);
+SecFilt *SecFiltPanstarrsToInternal (SecFiltPanstarrs *in, int Nvalues);
+SecFiltLoneos *SecFiltInternalToLoneos (SecFilt *in, int Nvalues);
+SecFiltElixir *SecFiltInternalToElixir (SecFilt *in, int Nvalues);
+SecFiltPanstarrs *SecFiltInternalToPanstarrs (SecFilt *in, int Nvalues);
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format);
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format);
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format);
+
+/*** DVO image db I/O Functions ***/
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate);
+int dvo_image_unlock (FITS_DB *db);
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_save (FITS_DB *db, int VERBOSE);
+int dvo_image_update (FITS_DB *db, int VERBOSE);
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ);
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE);
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew);
+void dvo_image_create (FITS_DB *db, double ZeroPoint);
+
+int FtableToImage (FTable *ftable, Header *theader, int *format);
+int ImageToFtable (FTable *ftable, Header *theader, int format);
+int ImageToVtable (VTable *vtable, Header *theader, int format);
+Image *ImageElixirToInternal (ImageElixir *in, int Nvalues);
+ImageElixir *ImageInternalToElixir (Image *in, int Nvalues);
+Image *ImageLoneosToInternal (ImageLoneos *in, int Nvalues);
+ImageLoneos *ImageInternalToLoneos (Image *in, int Nvalues);
+Image *ImagePanstarrsToInternal (ImagePanstarrs *in, int Nvalues);
+ImagePanstarrs *ImageInternalToPanstarrs (Image *in, int Nvalues);
+
+/* skyregion APIs */
+int        SkyTableSave        	   PROTO((SkyTable *table, char *filename));
+SkyTable  *SkyTableLoad        	   PROTO((char *filename, int VERBOSE));
+SkyTable  *SkyTableFromGSC     	   PROTO((char *filename, int depth, int VERBOSE));
+SkyTable  *SkyTableLoadOptimal 	   PROTO((char *catdir, char *SKYFILE, char *GSCFILE, int depth, int VERBOSE));
+int        SkyTableSetDepth    	   PROTO((SkyTable *sky, int depth));
+SkyList   *SkyRegionByPoint    	   PROTO((SkyTable *table, int depth, double ra, double dec));
+SkyList   *SkyListByPoint      	   PROTO((SkyTable *table, double ra, double dec));
+SkyList   *SkyListByRadius     	   PROTO((SkyTable *table, int depth, double RA, double DEC, double radius));
+SkyList   *SkyListByPatch      	   PROTO((SkyTable *table, int depth, SkyRegion *patch));
+SkyList   *SkyListByName      	   PROTO((SkyTable *table, char *name));
+SkyList   *SkyListByImage      	   PROTO((SkyTable *table, int depth, Image *image));
+SkyList   *SkyListByBounds     	   PROTO((SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+SkyList   *SkyListChildrenByBounds PROTO((SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax));
+int        SkyListFree             PROTO((SkyList *list, int ELEMENTS));
+int        SkyTableFree            PROTO((SkyTable *table));
+int        SkyListSetFilenames     PROTO((SkyList *list, char *path, char *ext));
+int        SkyTableSetFilenames    PROTO((SkyTable *sky, char *path, char *ext));
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/elixir_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/elixir_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/elixir_defs.h	(revision 17422)
@@ -0,0 +1,11 @@
+Image 		       	*Image_Elixir_ToInternal (Image_Elixir *in, int Nvalues);
+Image_Elixir 	       	*ImageInternalTo_Elixir (Image *in, int Nvalues);
+Average 	       	*Average_Elixir_ToInternal (Average_Elixir *in, int Nvalues, SecFilt **primary);
+Average_Elixir 	        *AverageInternalTo_Elixir (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Elixir_ToInternal (Measure_Elixir *in, int Nvalues);
+Measure_Elixir 	        *MeasureInternalTo_Elixir (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Elixir_ToInternal (SecFilt_Elixir *in, int Nvalues);
+SecFilt_Elixir 	       	*SecFiltInternalTo_Elixir (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_Elixir_To_Internal (PhotCode_Elixir *in, int Nvalues);
+PhotCode_Elixir *PhotCode_Internal_To_Elixir (PhotCode *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/loneos_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/loneos_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/loneos_defs.h	(revision 17422)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Loneos_ToInternal (Image_Loneos *in, int Nvalues);
+Image_Loneos 	       	*ImageInternalTo_Loneos (Image *in, int Nvalues);
+Average                	*Average_Loneos_ToInternal (Average_Loneos *in, int Nvalues, SecFilt **primary);
+Average_Loneos 	        *AverageInternalTo_Loneos (Average *in, int Nvalues, SecFilt *primary);
+Measure                	*Measure_Loneos_ToInternal (Measure_Loneos *in, int Nvalues);
+Measure_Loneos 	        *MeasureInternalTo_Loneos (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Loneos_ToInternal (SecFilt_Loneos *in, int Nvalues);
+SecFilt_Loneos 	       	*SecFiltInternalTo_Loneos (SecFilt *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_0_defs.h	(revision 17422)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Panstarrs_DEV_0_ToInternal (Image_Panstarrs_DEV_0 *in, int Nvalues);
+Image_Panstarrs_DEV_0  	*ImageInternalTo_Panstarrs_DEV_0 (Image *in, int Nvalues);
+Average 	       	*Average_Panstarrs_DEV_0_ToInternal (Average_Panstarrs_DEV_0 *in, int Nvalues, SecFilt **primary);
+Average_Panstarrs_DEV_0 *AverageInternalTo_Panstarrs_DEV_0 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Panstarrs_DEV_0_ToInternal (Measure_Panstarrs_DEV_0 *in, int Nvalues);
+Measure_Panstarrs_DEV_0 *MeasureInternalTo_Panstarrs_DEV_0 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Panstarrs_DEV_0_ToInternal (SecFilt_Panstarrs_DEV_0 *in, int Nvalues);
+SecFilt_Panstarrs_DEV_0 *SecFiltInternalTo_Panstarrs_DEV_0 (SecFilt *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/panstarrs_dev_1_defs.h	(revision 17422)
@@ -0,0 +1,8 @@
+Image 		       	*Image_Panstarrs_DEV_1_ToInternal (Image_Panstarrs_DEV_1 *in, int Nvalues);
+Image_Panstarrs_DEV_1  	*ImageInternalTo_Panstarrs_DEV_1 (Image *in, int Nvalues);
+Average 	       	*Average_Panstarrs_DEV_1_ToInternal (Average_Panstarrs_DEV_1 *in, int Nvalues, SecFilt **primary);
+Average_Panstarrs_DEV_1 *AverageInternalTo_Panstarrs_DEV_1 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_Panstarrs_DEV_1_ToInternal (Measure_Panstarrs_DEV_1 *in, int Nvalues);
+Measure_Panstarrs_DEV_1 *MeasureInternalTo_Panstarrs_DEV_1 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_Panstarrs_DEV_1_ToInternal (SecFilt_Panstarrs_DEV_1 *in, int Nvalues);
+SecFilt_Panstarrs_DEV_1 *SecFiltInternalTo_Panstarrs_DEV_1 (SecFilt *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_1_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_1_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_1_defs.h	(revision 17422)
@@ -0,0 +1,11 @@
+Image 		       	*Image_PS1_DEV_1_ToInternal (Image_PS1_DEV_1 *in, int Nvalues);
+Image_PS1_DEV_1  	*ImageInternalTo_PS1_DEV_1 (Image *in, int Nvalues);
+Average 	       	*Average_PS1_DEV_1_ToInternal (Average_PS1_DEV_1 *in, int Nvalues, SecFilt **primary);
+Average_PS1_DEV_1       *AverageInternalTo_PS1_DEV_1 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_PS1_DEV_1_ToInternal (Measure_PS1_DEV_1 *in, int Nvalues);
+Measure_PS1_DEV_1       *MeasureInternalTo_PS1_DEV_1 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_PS1_DEV_1_ToInternal (SecFilt_PS1_DEV_1 *in, int Nvalues);
+SecFilt_PS1_DEV_1       *SecFiltInternalTo_PS1_DEV_1 (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_PS1_DEV_1_To_Internal (PhotCode_PS1_DEV_1 *in, int Nvalues);
+PhotCode_PS1_DEV_1 *PhotCode_Internal_To_PS1_DEV_1 (PhotCode *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_2_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_2_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_2_defs.h	(revision 17422)
@@ -0,0 +1,11 @@
+Image 		       	*Image_PS1_DEV_2_ToInternal (Image_PS1_DEV_2 *in, int Nvalues);
+Image_PS1_DEV_2  	*ImageInternalTo_PS1_DEV_2 (Image *in, int Nvalues);
+Average 	       	*Average_PS1_DEV_2_ToInternal (Average_PS1_DEV_2 *in, int Nvalues, SecFilt **primary);
+Average_PS1_DEV_2       *AverageInternalTo_PS1_DEV_2 (Average *in, int Nvalues, SecFilt *primary);
+Measure 	       	*Measure_PS1_DEV_2_ToInternal (Measure_PS1_DEV_2 *in, int Nvalues);
+Measure_PS1_DEV_2       *MeasureInternalTo_PS1_DEV_2 (Measure *in, int Nvalues);
+SecFilt 	       	*SecFilt_PS1_DEV_2_ToInternal (SecFilt_PS1_DEV_2 *in, int Nvalues);
+SecFilt_PS1_DEV_2       *SecFiltInternalTo_PS1_DEV_2 (SecFilt *in, int Nvalues);
+
+PhotCode *PhotCode_PS1_DEV_2_To_Internal (PhotCode_PS1_DEV_2 *in, int Nvalues);
+PhotCode_PS1_DEV_2 *PhotCode_Internal_To_PS1_DEV_2 (PhotCode *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_3_defs.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_3_defs.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/include/ps1_dev_3_defs.h	(revision 17422)
@@ -0,0 +1,2 @@
+PhotCode *PhotCode_PS1_DEV_3_To_Internal (PhotCode_PS1_DEV_3 *in, int Nvalues);
+PhotCode_PS1_DEV_3 *PhotCode_Internal_To_PS1_DEV_3 (PhotCode *in, int Nvalues);
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/lib/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/lib/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/lib/.cvsignore	(revision 17422)
@@ -0,0 +1,3 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.darwin.dylib
+*.darwin_x86.dylib
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include <dvo.h>
+
+int LoadPhotcodes (char *catdir_file, char *master_file) {
+
+  /* first try to load the photcodes from the specified CATDIR location */
+  if (LoadPhotcodesFITS (catdir_file)) return TRUE;
+  
+  /* next try to load the photcodes from the master text photcode file */
+  /* automatically (or on demand?) save the text file to the FITS version */
+  if (LoadPhotcodesText (master_file)) { 
+      if (!check_file_access (catdir_file, TRUE, TRUE)) return TRUE;
+      SavePhotcodesFITS (catdir_file);
+      return TRUE;
+  }
+
+  return FALSE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesFITS.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 17422)
@@ -0,0 +1,135 @@
+# include <dvo.h>
+
+/* this is a read-only function to load the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int LoadPhotcodesFITS (char *filename) {
+
+  PhotCodeData *table = NULL;
+  PhotCode *photcode;
+  FITS_DB db;
+  char extname[80];
+
+  int i, code, Ncode, Nsec, Nsecfilt;
+
+  /* XXX choose more sensible lock timeouts! */
+  db.lockstate = LCK_SOFT;
+  db.timeout   = 10.0;
+  gfits_db_init (&db);
+
+  /* does this mean the db is empty, non-existent, or has access errors? */
+  // XXX we need better error handling here
+  if (!gfits_db_lock (&db, filename)) {
+    // fprintf (stderr, "ERROR: failure to lock db\n");
+    gfits_db_close (&db);
+    return FALSE;
+  }
+  if (db.dbstate == LCK_EMPTY) {
+    // fprintf (stderr, "ERROR: db is empty\n");
+    gfits_db_close (&db);
+    return FALSE;
+  } 
+  if (!gfits_db_load (&db)) {
+    // fprintf (stderr, "ERROR: failure to load db\n");
+    gfits_db_close (&db);
+    return FALSE;
+  }
+  gfits_db_close (&db);
+
+  /* convert FITS format data to internal format (just byteswaps) */
+  gfits_scan (&db.theader, "EXTNAME", "%s", 1, extname);
+
+  if (!strcmp (extname, "DVO_PHOTCODE") || !strcmp (extname, "DVO_PHOTCODE_ELIXIR")) {
+    PhotCode_Elixir *photcode_elixir = gfits_table_get_PhotCode_Elixir (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_Elixir_To_Internal (photcode_elixir, Ncode);
+    free (photcode_elixir);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_1")) {
+    PhotCode_PS1_DEV_1 *photcode_ps1_dev_1 = gfits_table_get_PhotCode_PS1_DEV_1 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_1_To_Internal (photcode_ps1_dev_1, Ncode);
+    free (photcode_ps1_dev_1);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_2")) {
+    PhotCode_PS1_DEV_2 *photcode_ps1_dev_2 = gfits_table_get_PhotCode_PS1_DEV_2 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_2_To_Internal (photcode_ps1_dev_2, Ncode);
+    free (photcode_ps1_dev_2);
+  } 
+
+  if (!strcmp (extname, "DVO_PHOTCODE_PS1_DEV_3")) {
+    PhotCode_PS1_DEV_3 *photcode_ps1_dev_3 = gfits_table_get_PhotCode_PS1_DEV_3 (&db.ftable, &Ncode, &db.swapped);
+    photcode = PhotCode_PS1_DEV_3_To_Internal (photcode_ps1_dev_3, Ncode);
+    free (photcode_ps1_dev_3);
+  } 
+
+  table = GetPhotcodeTable ();
+  if (table[0].code != NULL) free (table[0].code);
+  /* we are using a 16-bit int for the photcodes, so these indexes can be fixed-length */
+  /* XXX if we need to go with a larger photcode, we'll need to use a sequenced index and a
+     binary search to get to a given value (0x100000000 ints would take quite a few
+     bytes...) */
+  for (i = 0; i < 0x10000; i++) {
+    table[0].hashcode[i] = -1;
+    table[0].hashNsec[i] = -1;
+    table[0].codeNsec[i] = -1;
+  }
+
+  /* set up photcode indexes (see dvo_photcode_ops.c) */
+  Nsecfilt = 0;
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_ALT) continue; /* no hashcode for ALT codes */
+    if (table[0].hashcode[photcode[i].code] != -1) {
+      fprintf (stderr, "duplicate photcodes in file\n");
+      code = table[0].hashcode[photcode[i].code];
+      fprintf (stderr, "conflict between %s (%d) and %s (%d)\n",
+	       photcode[i].name, photcode[i].code, photcode[code].name, photcode[code].code);
+      free (photcode);
+      return FALSE;
+    }
+    table[0].hashcode[photcode[i].code] = i;
+    if (photcode[i].type == PHOT_SEC) {
+      table[0].hashNsec[photcode[i].code] = Nsecfilt;
+      table[0].codeNsec[Nsecfilt] = photcode[i].code;
+      Nsecfilt ++;
+    }
+  }
+
+  // validity check for references
+  // photcode.equiv of 0 means "undefined"
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_DEP) {
+      if (photcode[i].equiv == 0) continue;
+      Nsec = table[0].hashcode[photcode[i].equiv];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for dependent photcode is not in photcodes\n");
+	free (photcode);
+	return FALSE;
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for dependent photcode is not an average photcode\n");
+	free (photcode);
+	return FALSE;
+      }
+    }
+    if (photcode[i].type == PHOT_ALT) {
+      Nsec = table[0].hashcode[photcode[i].code];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for alternate photcode is not in photcodes\n");
+	free (photcode);
+	return FALSE;
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for alternate photcode is not an average photcode\n");
+	free (photcode);
+	return FALSE;
+      }
+    }
+  }
+
+  table[0].code     = photcode;
+  table[0].Ncode    = Ncode;
+  table[0].Nsecfilt = Nsecfilt;
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesText.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 17422)
@@ -0,0 +1,275 @@
+# include <dvo.h>
+
+static int PhotcodeNumberOrName (char *word) {
+
+  int value;
+  char *endpt;
+
+  if (!strcmp (word, "-")) return (0);
+
+  value = strtol (word, &endpt, 10);
+  if (endpt != word + strlen(word)) {
+    value = GetPhotcodeCodebyName (word);
+  }
+  return (value);
+}
+
+/* load the text photcode table */
+int LoadPhotcodesText (char *filename) {
+  
+  /* XXX fix these */
+  PhotCodeData *table = NULL;
+  PhotCode *photcode;
+
+  FILE *f;
+  int i, Nsecfilt, Nsec, Ncode, NPHOTCODE, Nfield;
+  int code;
+  char *c;
+  char line[256], **c1_names, **c2_names, **eq_names;
+  char name[32], type[32], Zero[32], Airmass[32], Offset[32];
+  char C1[32], C2[32], Slope[32], Color[32], Primary[32];
+  char astromErrSys[32], astromErrScale[32], astromErrMagScale[32], photomErrSys[32];
+  char astromPoorMask[32], astromBadMask[32], photomPoorMask[32], photomBadMask[32];
+
+  table = GetPhotcodeTable ();
+  if (table[0].code != NULL) free (table[0].code);
+  /* we are using a 16-bit int for the photcodes, so these indexes can be fixed-length */
+  /* XXX if we need to go with a larger photcode, we'll need to use a sequenced index and a
+     binary search to get to a given value (0x100000000 ints would take quite a few
+     bytes...) */
+  for (i = 0; i < 0x10000; i++) {
+    table[0].hashcode[i] = -1;
+    table[0].hashNsec[i] = -1;
+    table[0].codeNsec[i] = -1;
+  }
+
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    table[0].Ncode    = 0;
+    table[0].Nsecfilt = 0;
+    table[0].code     = (PhotCode *) NULL;
+    return (FALSE);
+  }
+
+  Ncode = 0;
+  NPHOTCODE = 10;
+  ALLOCATE (photcode, PhotCode, NPHOTCODE);
+  ALLOCATE (eq_names, char *,   NPHOTCODE);
+  ALLOCATE (c1_names, char *,   NPHOTCODE);
+  ALLOCATE (c2_names, char *,   NPHOTCODE);
+
+  while (scan_line (f, line) != EOF) {
+    for (c = line; isspace (*c); c++);
+    if (*c == '#') continue;
+    Nfield = sscanf (c, "%d %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", 
+		     &code, name, type, Zero, Airmass, Offset, C1, C2, Slope, Color, Primary, astromErrSys, astromErrScale, astromErrMagScale, photomErrSys, astromPoorMask, astromBadMask, photomPoorMask, photomBadMask);
+
+    switch (Nfield) {
+      case 11: 
+	// minimum number of fields : original elixir layout
+	strcpy (astromErrSys,      "0.0");
+	strcpy (astromErrScale,    "0.0");
+	strcpy (astromErrMagScale, "0.0");
+	strcpy (photomErrSys,      "0.0");
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 14: 
+	// allow only astrom elements
+	strcpy (photomErrSys,      "0.0");
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 15: 
+	strcpy (astromPoorMask,    "0");
+	strcpy (astromBadMask,     "0");
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 17: 
+	strcpy (photomPoorMask,    "0");
+	strcpy (photomBadMask,     "0");
+	break;
+      case 19: 
+	// all fields defined
+	break;
+      default:
+	// skip unknown layouts
+	continue;
+    }
+
+    if (!code) {
+	fprintf (stderr, "photcode values may not be 0: fix %s\n", name);
+	return (FALSE);
+    }
+
+    photcode[Ncode].type = 0;
+    photcode[Ncode].code = code;
+    memset (photcode[Ncode].name, 0, 32);
+    strcpy (photcode[Ncode].name, name);
+
+    if (!strncasecmp (type, "pri", 3)) {
+      photcode[Ncode].type  = PHOT_SEC;
+    }
+    if (!strncasecmp (type, "sec", 3)) {
+      photcode[Ncode].type  = PHOT_SEC;
+    }
+    if (!strncasecmp (type, "dep", 3)) {
+      photcode[Ncode].type  = PHOT_DEP;
+    }
+    if (!strncasecmp (type, "ref", 3)) {
+      photcode[Ncode].type  = PHOT_REF;
+    }
+    if (!strncasecmp (type, "alt", 3)) {
+      /* alt photcodes are a little different: they have the SAME photcode as an existing
+	 pri/sec photcode, but define an alternate transformation for that code */
+      photcode[Ncode].type  = PHOT_ALT;
+    }
+
+    photcode[Ncode].astromErrSys      = atof (astromErrSys);
+    photcode[Ncode].astromErrScale    = atof (astromErrScale);
+    photcode[Ncode].astromErrMagScale = atof (astromErrMagScale);
+    photcode[Ncode].photomErrSys      = atof (photomErrSys);
+
+    photcode[Ncode].astromPoorMask    = strtol (astromPoorMask, NULL, 0);
+    photcode[Ncode].astromBadMask     = strtol (astromBadMask, NULL, 0);
+    photcode[Ncode].photomPoorMask    = strtol (photomPoorMask, NULL, 0);
+    photcode[Ncode].photomBadMask     = strtol (photomBadMask, NULL, 0);
+
+    switch (photcode[Ncode].type) {
+      case PHOT_SEC:
+      case PHOT_ALT:
+      case PHOT_DEP:
+	photcode[Ncode].C     = 1000*atof (Zero);
+	photcode[Ncode].K     = atof (Airmass);
+	photcode[Ncode].dC    = 1000*atof (Offset);
+	photcode[Ncode].dX    = 1000*atof (Color);
+	c1_names[Ncode]       = strcreate (C1);
+	c2_names[Ncode]       = strcreate (C2);
+	eq_names[Ncode]       = strcreate (Primary);
+	ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+	break;
+
+      case PHOT_REF:
+	photcode[Ncode].C     = 0;
+	photcode[Ncode].K     = 0;
+	photcode[Ncode].dC    = 0;
+	photcode[Ncode].dX    = 0;
+	c1_names[Ncode]       = strcreate ("0");
+	c2_names[Ncode]       = strcreate ("0");
+	eq_names[Ncode]       = strcreate (Primary);
+	photcode[Ncode].X[0]  = 0;
+	photcode[Ncode].Nc    = 0;
+	break;
+
+      default:
+	fprintf (stderr, "error: invalid photcode type\n");
+	exit (2);
+    }      
+
+    if (!photcode[Ncode].type) {
+      fprintf (stderr, "error in Photfile: unknown type %s\n", type);
+    }
+
+    Ncode++;
+    if (Ncode == NPHOTCODE) {
+      NPHOTCODE += 10;
+      REALLOCATE (photcode, PhotCode, NPHOTCODE);
+      REALLOCATE (eq_names, char *,   NPHOTCODE);
+      REALLOCATE (c1_names, char *,   NPHOTCODE);
+      REALLOCATE (c2_names, char *,   NPHOTCODE);
+    }
+  }
+  fclose (f);
+  
+  // convert the named references (c1, c2, equiv) to code values
+  for (i = 0; i < Ncode; i++) {
+    photcode[i].c1    = PhotcodeNumberOrName (c1_names[i]);
+    photcode[i].c2    = PhotcodeNumberOrName (c2_names[i]);
+    photcode[i].equiv = PhotcodeNumberOrName (eq_names[i]);
+    free (c1_names[i]);
+    free (c2_names[i]);
+    free (eq_names[i]);
+  }
+  free (c1_names);
+  free (c2_names);
+  free (eq_names);
+
+  /* set up photcode indexes (see dvo_photcode_ops.c) */
+  Nsecfilt = 0;
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_ALT) continue; /* no hashcode for ALT codes */
+    if (table[0].hashcode[photcode[i].code] != -1) {
+      fprintf (stderr, "duplicate photcodes in file\n");
+      code = table[0].hashcode[photcode[i].code];
+      fprintf (stderr, "conflict between %s (%d) and %s (%d)\n",
+	       photcode[i].name, photcode[i].code, photcode[code].name, photcode[code].code);
+      free (photcode);
+      return (FALSE);
+    }
+    table[0].hashcode[photcode[i].code] = i;
+    if (photcode[i].type == PHOT_SEC) {
+      table[0].hashNsec[photcode[i].code] = Nsecfilt;
+      table[0].codeNsec[Nsecfilt] = photcode[i].code;
+      Nsecfilt ++;
+    }
+  }
+
+  // validity check for references
+  // photcode.equiv of 0 means "undefined"
+  for (i = 0; i < Ncode; i++) {
+    if (photcode[i].type == PHOT_DEP) {
+      if (photcode[i].equiv == 0) continue;
+      Nsec = table[0].hashcode[photcode[i].equiv];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for dependent photcode is not a valid photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for dependent photcode is not an average photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+    }
+    if (photcode[i].type == PHOT_ALT) {
+      Nsec = table[0].hashcode[photcode[i].code];
+      if ((Nsec >= Ncode) || (Nsec < 0)) {
+	fprintf (stderr, "reference for alternate photcode is not in photcodes\n");
+	free (photcode);
+	return (FALSE);
+      }
+      if (photcode[Nsec].type != PHOT_SEC) {
+	fprintf (stderr, "reference for alternate photcode is not an average photcode\n");
+	free (photcode);
+	return (FALSE);
+      }
+    }
+  }
+  table[0].code     = photcode;
+  table[0].Ncode    = Ncode;
+  table[0].Nsecfilt = Nsecfilt;
+
+  return (TRUE);
+}
+
+# define NCTERMS 4
+void ParseColorTerms (char *terms, float *X, int *N) {
+
+  int i;
+  char *p;
+
+  p = terms;
+
+  for (i = 0; (p != NULL) && (i < NCTERMS); i++) {
+    X[i] = atof (p);
+    p = strchr (p, ',');
+    if (p == (char *) NULL) continue;
+    p ++;
+  }
+  *N = i;
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesFITS.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 17422)
@@ -0,0 +1,42 @@
+# include <dvo.h>
+
+/* this function saves the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int SavePhotcodesFITS (char *filename) {
+
+  PhotCodeData *table = NULL;
+  FITS_DB db;
+
+  table = GetPhotcodeTable ();
+  if (table == NULL) {
+    fprintf (stderr, "ERROR: no internal photcode table is defined\n");
+    return FALSE;
+  }
+
+  /* XXX choose more sensible lock timeouts! */
+  db.lockstate = LCK_XCLD;
+  db.timeout   = 10.0;
+  gfits_db_init (&db);
+
+  /* does this mean the db is empty, non-existent, or has access errors? */
+  if (!gfits_db_lock (&db, filename)) {
+    fprintf (stderr, "ERROR: failure to lock db, cannot save photcode table to %s\n", filename);
+    gfits_db_close (&db);
+    return FALSE;
+  }
+
+  // for the moment, we simply support the latest photcode format for output
+  // XXX update this as needed as new formats are defined
+  PhotCode_PS1_DEV_3 *photcode_output = PhotCode_Internal_To_PS1_DEV_3 (table[0].code, table[0].Ncode);
+
+  /* convert FITS format data to internal format (byteswaps & EXTNAME) */
+  gfits_db_create (&db);
+  gfits_table_set_PhotCode_PS1_DEV_3 (&db.ftable, photcode_output, table[0].Ncode);
+  gfits_db_save (&db);
+  gfits_db_close (&db);
+
+  free (photcode_output);
+
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesText.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 17422)
@@ -0,0 +1,111 @@
+# include <dvo.h>
+# define SCALE 0.001
+
+static char *PHOT_SEC_NAME = "sec";
+static char *PHOT_ALT_NAME = "alt";
+static char *PHOT_REF_NAME = "ref";
+static char *PHOT_DEP_NAME = "dep";
+
+/* this function saves the FITS photcode file into the internal photcode table */
+/* locking is used to avoid collisions with programs trying to update the photcodes values */
+/* XXX better distinction between NOT FOUND and FAILURE */
+int SavePhotcodesText (char *filename) {
+
+  PhotCodeData *table = NULL;
+  struct stat filestat;
+  char *type;
+  int i, j, status;
+  FILE *f;
+
+  table = GetPhotcodeTable ();
+  if (table == NULL) {
+    fprintf (stderr, "ERROR: no internal photcode table is defined\n");
+    return FALSE;
+  }
+
+  /* check if file exists */
+  status = stat (filename, &filestat);
+  if (status == -1) {
+    if (errno != ENOENT) {
+      fprintf (stderr, "ERROR: problem accessing output path for%s\n", filename);
+      return FALSE;
+    }
+  } else {
+    make_backup (filename);
+  } 
+
+  f = fopen (filename, "w");
+  if (f == NULL) {
+    fprintf (stderr, "ERROR: problem creating photcode file %s\n", filename);
+    return FALSE;
+  }
+
+  fprintf (f, "#                                           airmass      color                         astrometry  mag    photom  astrom mask    photom mask\n");
+  fprintf (f, "# code  name                type    zero  slope offset c1    c2   slope   zero  equiv  sys scale   scale  sys     poor   bad     poor   bad\n");
+  //          "  1     g                    sec   0.000  0.000 0.000  1003  1004 0.0160     0  1051   0.000 0.000 0.000  0.000   0xffff 0xffff  0xffff 0xffff
+
+  for (i = 0; i < table[0].Ncode; i++) {
+    switch (table[0].code[i].type) {
+      case PHOT_SEC:
+	type = PHOT_SEC_NAME;
+	break;
+      case PHOT_ALT:
+	type = PHOT_ALT_NAME;
+	break;
+      case PHOT_REF:
+	type = PHOT_REF_NAME;
+	break;
+      case PHOT_DEP:
+	type = PHOT_DEP_NAME;
+	break;
+      default:
+	fprintf (stderr, "ERROR: problem with photcode type for %s\n", GetPhotcodeNamebyCode(table[0].code[i].code));
+	return FALSE;
+    }
+
+    fprintf (f, "  %-5d %-18s  %4s  %6.3f %6.3f %5.3f ",
+	     table[0].code[i].code,
+	     GetPhotcodeNamebyCode (table[0].code[i].code),
+	     type,
+	     table[0].code[i].C*SCALE, 
+	     table[0].code[i].K*SCALE, 
+	     table[0].code[i].dC*SCALE);
+
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].c1);
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].c2);
+
+    for (j = 0; j < table[0].code[i].Nc - 1; j++) {
+      fprintf (f, " %6.4f,", table[0].code[i].X[j]);
+    }
+    fprintf (f, "%6.4f %5d ", table[0].code[i].X[j], table[0].code[i].dX);
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].equiv);
+
+    fprintf (f, "  %5.3f %5.3f %5.3f  %5.3f", 
+	     table[0].code[i].astromErrSys, 
+	     table[0].code[i].astromErrScale, 
+	     table[0].code[i].astromErrMagScale, 
+	     table[0].code[i].photomErrSys); 
+
+    fprintf (f, "   0x%04x 0x%04x  0x%04x 0x%04x", 
+	     table[0].code[i].astromPoorMask, 
+	     table[0].code[i].astromBadMask, 
+	     table[0].code[i].photomPoorMask, 
+	     table[0].code[i].photomBadMask); 
+
+    fprintf (f, "\n");
+  }
+  fclose (f);
+  return TRUE;
+}
+
+void PrintPhotcodeNamebyCode (FILE *f, char *format, int code) {
+
+  char *name;
+  
+  name = GetPhotcodeNamebyCode (code);
+  if (name == NULL) {
+    fprintf (f, "    - ");
+  } else {
+    fprintf (f, format, code);
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/coordops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/coordops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/coordops.c	(revision 17422)
@@ -0,0 +1,758 @@
+# include <dvo.h>
+
+static Coords mosaic;
+static int gotMosaic = FALSE;
+
+void RegisterMosaic (Coords *coords) {
+  mosaic = *coords;
+  gotMosaic = TRUE;
+}
+
+int isRegisteredMosaic () {
+  return (gotMosaic);
+}
+
+int GetMosaicCoords (Coords *coords) {
+  *coords = mosaic;
+  return (TRUE);
+}
+
+int XY_to_LM (double *L, double *M, double x, double y, Coords *coords) {
+
+  double X, Y;
+
+  /** convert pixel coordinates to cartesian system **/
+  X = coords[0].cdelt1*(x - coords[0].crpix1);
+  Y = coords[0].cdelt2*(y - coords[0].crpix2);
+
+  *L = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+  *M = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+
+  /** extra polynomial terms **/
+  if (coords[0].Npolyterms > 1) {
+    *L += X*X*coords[0].polyterms[0][0] + X*Y*coords[0].polyterms[1][0] + Y*Y*coords[0].polyterms[2][0];
+    *M += X*X*coords[0].polyterms[0][1] + X*Y*coords[0].polyterms[1][1] + Y*Y*coords[0].polyterms[2][1];
+  }
+  if (coords[0].Npolyterms > 2) {
+    *L += X*X*X*coords[0].polyterms[3][0] + X*X*Y*coords[0].polyterms[4][0] + X*Y*Y*coords[0].polyterms[5][0] + Y*Y*Y*coords[0].polyterms[6][0];
+    *M += X*X*X*coords[0].polyterms[3][1] + X*X*Y*coords[0].polyterms[4][1] + X*Y*Y*coords[0].polyterms[5][1] + Y*Y*Y*coords[0].polyterms[6][1];
+  }
+
+  return (TRUE);
+}
+
+int LM_to_RD (double *ra, double *dec, double L, double M, Coords *coords) {
+
+  OhanaProjection proj;
+  OhanaProjectionMode mode;
+  double R, T, Z, Z2, sphi, cphi, stht, ctht;
+  double alpha, delta, salp, calp, sdel, sdp, cdp;
+
+  proj = GetProjection (coords[0].ctype);
+  mode = GetProjectionMode (proj);
+  if (proj == PROJ_NONE) return (FALSE);
+  if (proj == PROJ_MODE_NONE) return (FALSE);
+
+  stht = ctht = 1;
+
+  /** Locally Cartesian Projections **/
+  if (mode == PROJ_MODE_CARTESIAN) {
+    *ra  = L + coords[0].crval1;
+    *dec = M + coords[0].crval2;
+
+    /* mosaic astrometry : WRP is chip astrometry; apply mosaic (DIS) term */
+    if (proj == PROJ_WRP) {
+      if (!gotMosaic) return (FALSE);
+      XY_to_RD (ra, dec, *ra, *dec, &mosaic);
+    }
+    return (TRUE);
+  }
+  
+  /** Zenithal Projections **/
+  if (mode == PROJ_MODE_ZENITHAL) {
+    R = hypot (L,M);
+    if ((L == 0) && (M == 0)) {
+      sphi = 0;
+      cphi = 1;
+    } else {
+      sphi =  L / R;
+      cphi = -M / R;
+    }
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_TNX:
+      case PROJ_DIS:
+	// R = cot (theta) = cos(theta) / sin(theta)
+	if (R == 0) {
+	  stht = 1.0;
+	  ctht = 0.0;
+	} else {
+	  T = DEG_RAD / R;
+	  stht =   T / sqrt ( 1.0 + T*T);
+	  ctht = 1.0 / sqrt ( 1.0 + T*T);
+	}
+	break;
+      case PROJ_STG:
+	// R = (180/pi) [2 cos (theta)  / 1 + sin (theta)]
+	stht = (4 - RAD_DEG*R) / (4 + RAD_DEG*R);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      case PROJ_SIN:
+	// R = (180/pi) cos (theta)
+	ctht = RAD_DEG * R;
+	stht = sqrt (1 - ctht*ctht);
+	break;
+      case PROJ_ARC:
+	// R = 90 - theta (degrees)
+	ctht = sin (RAD_DEG * R);
+	stht = cos (RAD_DEG * R);
+	break;
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	if (R > 2*DEG_RAD) {
+	  *ra = L;
+	  *dec = M;
+	  return (FALSE);
+	}
+	stht = 1 - 0.5*SQ(R*RAD_DEG);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      default:
+	return (FALSE);
+    }
+    sdp  = sin(RAD_DEG*coords[0].crval2);
+    cdp  = cos(RAD_DEG*coords[0].crval2);
+    
+    sdel = stht*sdp - ctht*cphi*cdp;
+    salp = ctht*sphi;
+    calp = stht*cdp + ctht*cphi*sdp;
+    alpha = atan2 (salp, calp);
+    delta = asin (sdel);
+    
+    *ra  = DEG_RAD*alpha + coords[0].crval1;
+    *dec = DEG_RAD*delta;
+
+    /* rationalize ra range 0 - 360.0 */
+    while (*ra < 0.0)   *ra += 360.0;
+    while (*ra > 360.0) *ra -= 360.0;
+
+    return (TRUE);
+  }
+  
+  /**** Other Conventional Projections ****/
+  if (mode == PROJ_MODE_PSEUDOCYL) {
+    switch (proj) {
+      case PROJ_AIT:
+      Z2 = (1.0 - SQ(RAD_DEG*0.25*L) - SQ(RAD_DEG*0.5*M));
+      if (Z2 < 0) return (FALSE);
+      Z = sqrt (Z2);
+      alpha = 2.0 * DEG_RAD * atan2 (RAD_DEG*0.5*Z*L, 2.0*Z2 - 1.0);
+      delta = DEG_RAD * asin (RAD_DEG*M*Z);
+      break;
+      
+      case PROJ_GLS:
+	/* L,M in degrees, alpha,delta in degrees */
+	alpha = L / cos (RAD_DEG * M);
+	delta = M;
+	break;
+      case PROJ_PAR:
+	/* L,M in degrees, alpha,delta in degrees */
+	alpha = L / (1.0 - SQ(2.0*M/180));
+	delta = 3 * DEG_RAD * asin (M/180.0);
+	break;
+      default:
+	return (FALSE);
+    }
+    *ra  = alpha + coords[0].crval1;
+    *dec = delta + coords[0].crval2;
+
+    /* rationalize ra range 0 - 360.0 */
+    while (*ra < 0.0)   *ra += 360.0;
+    while (*ra > 360.0) *ra -= 360.0;
+
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int RD_to_LM (double *L, double *M, double ra, double dec, Coords *coords) {
+
+  double phi, theta;
+  double Lo, Mo;
+  double sphi, cphi, stht, ctht;
+  double salp, calp, sdel, cdel, sdp, cdp;
+  double P, A, Rc;
+  OhanaProjection proj;
+  OhanaProjectionMode mode;
+
+  *L = *M = 0;
+
+  proj = GetProjection (coords[0].ctype);
+  mode = GetProjectionMode (proj);
+  if (proj == PROJ_NONE) return (FALSE);
+  if (proj == PROJ_MODE_NONE) return (FALSE);
+
+  /**** Locally Cartesian Projections ****/
+  if (mode == PROJ_MODE_CARTESIAN) {
+    if (proj == PROJ_WRP) {
+      if (!gotMosaic) return (FALSE);
+      RD_to_XY (&Lo, &Mo, ra, dec, &mosaic);
+      *L = (Lo - coords[0].crval1);
+      *M = (Mo - coords[0].crval2);
+      return (TRUE);
+    }
+    *L = (ra  - coords[0].crval1);
+    *M = (dec - coords[0].crval2);
+    return (TRUE);
+  }
+  
+  /**** Zenithal Projections ****/
+  if (mode == PROJ_MODE_ZENITHAL) {
+    sdp  = sin(RAD_DEG*coords[0].crval2);
+    cdp  = cos(RAD_DEG*coords[0].crval2);
+    salp = sin(RAD_DEG*(ra - coords[0].crval1));
+    calp = cos(RAD_DEG*(ra - coords[0].crval1));
+    sdel = sin(RAD_DEG*dec);
+    cdel = cos(RAD_DEG*dec);
+
+    stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+    sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+    cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+
+    // L = +R sin(phi)
+    // M = -R cos(phi)
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_TNX:
+      case PROJ_DIS:
+	// R = cot (theta) = cos(theta) / sin(theta)
+	Rc = hypot(sphi, cphi);
+	*L = (stht == 0) ? 180.0 * sphi / Rc :  +DEG_RAD * sphi / stht;
+	*M = (stht == 0) ? 180.0 * cphi / Rc :  -DEG_RAD * cphi / stht;
+	return (stht > 0);
+      case PROJ_STG:
+	// R = 2 cos(theta) / [1 + sin(theta)]
+	Rc = DEG_RAD * 2 / (1 + stht);
+	*L = +Rc * sphi;
+	*M = -Rc * cphi;
+	return (stht > 0);
+      case PROJ_SIN:
+	// R = cos(theta)
+	*L = +DEG_RAD * sphi;
+	*M = -DEG_RAD * cphi;
+	return (stht > 0);
+      case PROJ_ARC:
+	// R = 90 - theta
+	ctht = hypot(sphi, cphi);
+	theta = atan2 (stht, ctht);
+	Rc = 90 - DEG_RAD * theta;
+	*L = (ctht == 0.0) ? 0.0 : +Rc * sphi / ctht ;
+	*M = (ctht == 0.0) ? 0.0 : -Rc * cphi / ctht ;
+	return (TRUE);
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	// R = 90 - theta
+	Rc = DEG_RAD * M_SQRT2 / sqrt (1 + stht);
+	*L =  Rc * sphi;
+	*M = -Rc * cphi;
+	return (stht > 0);
+      default:
+	return (FALSE);
+    }
+    return (FALSE);
+  }
+
+  /**** Other Standard Projections ****/
+  if (mode == PROJ_MODE_PSEUDOCYL) {
+    switch (proj) {
+      case PROJ_AIT:
+	phi = RAD_DEG*(ra - coords[0].crval1);
+	theta = RAD_DEG*(dec - coords[0].crval2);
+	P = 1.0 + cos (theta) * cos (0.5*phi);
+	if (P == 0.0) {
+	  *L =  0.0;
+	  *M =  0.0;
+	  return (TRUE);
+	} 
+	A =  DEG_RAD * sqrt (2.0 / P);
+	*L =  2.0 * A * cos (theta) * sin (0.5*phi);
+	*M =  A * sin (theta);
+	return (TRUE);
+      case PROJ_GLS:
+	phi = ra - coords[0].crval1;
+	theta = dec - coords[0].crval2;
+	*L = phi * cos(RAD_DEG * theta);
+	*M = theta;
+	return (TRUE);
+      case PROJ_PAR:
+	phi = ra - coords[0].crval1;
+	theta = dec - coords[0].crval2;
+	*L = phi * (2.0*cos(2*RAD_DEG*theta/3.0) - 1);
+	*M = 180.0 * sin (RAD_DEG*theta/3.0);
+	return (TRUE);
+      default:
+	return (FALSE);
+    }
+    return (FALSE);
+  }
+  return (FALSE);
+}
+
+int LM_to_XY (double *x, double *y, double L, double M, Coords *coords) {
+
+  int i;
+  double determ;
+  double X, Y, Lo, Mo, dL, dM;
+
+  *x = 0;
+  *y = 0;
+
+  /* convert L,M to X,Y */
+  determ = 1.0 / (coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1);
+  X = determ * (coords[0].pc2_2*L - coords[0].pc1_2*M);
+  Y = determ * (coords[0].pc1_1*M - coords[0].pc2_1*L);
+
+  /** extra polynomial terms **/
+  if (coords[0].Npolyterms > 1) {
+    for (i = 0; i < 10; i++) {
+      Lo = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+      Mo = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+      if (coords[0].Npolyterms > 1) {
+	Lo += X*X*coords[0].polyterms[0][0] + X*Y*coords[0].polyterms[1][0] + Y*Y*coords[0].polyterms[2][0];
+	Mo += X*X*coords[0].polyterms[0][1] + X*Y*coords[0].polyterms[1][1] + Y*Y*coords[0].polyterms[2][1];
+      }
+      if (coords[0].Npolyterms > 2) {
+	Lo += X*X*X*coords[0].polyterms[3][0] + X*X*Y*coords[0].polyterms[4][0] + X*Y*Y*coords[0].polyterms[5][0] + Y*Y*Y*coords[0].polyterms[6][0];
+	Mo += X*X*X*coords[0].polyterms[3][1] + X*X*Y*coords[0].polyterms[4][1] + X*Y*Y*coords[0].polyterms[5][1] + Y*Y*Y*coords[0].polyterms[6][1];
+      }
+      dL = (L - Lo);
+      dM = (M - Mo);
+
+      X += determ * (coords[0].pc2_2*dL - coords[0].pc1_2*dM);
+      Y += determ * (coords[0].pc1_1*dM - coords[0].pc2_1*dM);
+    }
+  }
+  /* check for correct size (iterate?) */
+
+  *x = X / coords[0].cdelt1 + coords[0].crpix1;
+  *y = Y / coords[0].cdelt2 + coords[0].crpix2;
+
+  return (TRUE);
+}
+
+int XY_to_RD (double *ra, double *dec, double x, double y, Coords *coords) {
+
+  double L, M;
+  int status;
+
+  status = XY_to_LM (&L, &M, x, y, coords);
+  if (!status) return FALSE;
+
+  status = LM_to_RD (ra, dec, L, M, coords);
+  return (status);
+}
+
+int RD_to_XY (double *x, double *y, double ra, double dec, Coords *coords) {
+
+  double L, M;
+  int status;
+
+  status = RD_to_LM (&L, &M, ra, dec, coords);
+
+  if (finite(L) && finite(M)) {
+      LM_to_XY (x, y, L, M, coords);
+  } else {
+      *x = L;
+      *y = M;
+  }
+  return (status);
+}
+
+int fRD_to_XY (float *x, float *y, double ra, double dec, Coords *coords) {
+
+  int status;
+  double tmpx, tmpy;
+
+  status = RD_to_XY (&tmpx, &tmpy, ra, dec, coords);
+  *x = tmpx;
+  *y = tmpy;
+  
+  return (status);
+
+}
+
+int fXY_to_RD (float *ra, float *dec, double x, double y, Coords *coords) {
+
+  int status;
+  double tmpr, tmpd;
+
+  status = XY_to_RD (&tmpr, &tmpd, x, y, coords);
+  *ra = tmpr;
+  *dec = tmpd;
+  
+  return (status);
+
+}
+
+int GetCoords (Coords *coords, Header *header) {
+  
+  int status, itmp, Polynomial, Polyterm;
+  double Lambda, rotate, scale;
+  double equinox;
+  char *ctype;
+  
+  rotate = 0.0;
+  coords[0].crval1 = coords[0].crpix1 = coords[0].cdelt1 = 0.0;
+  coords[0].crval2 = coords[0].crpix2 = coords[0].cdelt2 = 0.0;
+  coords[0].pc1_1 = coords[0].pc2_2 = 1.0;
+  coords[0].pc2_1 = coords[0].pc1_2 = 0.0;
+  coords[0].Npolyterms = 1;
+  strcpy (coords[0].ctype, "NONE");
+  
+  status = FALSE; 
+  if (gfits_scan (header, "CTYPE2", "%s", 1, coords[0].ctype)) {
+    status  = gfits_scan (header, "CRVAL1", "%lf", 1, &coords[0].crval1);
+    status &= gfits_scan (header, "CRPIX1", "%f", 1, &coords[0].crpix1);
+    status &= gfits_scan (header, "CRVAL2", "%lf", 1, &coords[0].crval2);  
+    status &= gfits_scan (header, "CRPIX2", "%f", 1, &coords[0].crpix2);
+
+    if (gfits_scan (header, "CDELT1", "%f", 1, &coords[0].cdelt1)) {
+      status &= gfits_scan (header, "CDELT2", "%f", 1, &coords[0].cdelt2);
+      if (gfits_scan (header, "CROTA2", "%lf", 1, &rotate)) {
+	Lambda = coords[0].cdelt2 / coords[0].cdelt1;
+	coords[0].pc1_1 =  cos(rotate*RAD_DEG);
+	coords[0].pc1_2 = -sin(rotate*RAD_DEG) * Lambda;
+	coords[0].pc2_1 =  sin(rotate*RAD_DEG) / Lambda;
+	coords[0].pc2_2 =  cos(rotate*RAD_DEG);
+      }
+      if (gfits_scan (header, "PC001001", "%f", 1, &coords[0].pc1_1)) {
+	status &= gfits_scan (header, "PC001002", "%f", 1, &coords[0].pc1_2);
+	status &= gfits_scan (header, "PC002001", "%f", 1, &coords[0].pc2_1);
+	status &= gfits_scan (header, "PC002002", "%f", 1, &coords[0].pc2_2);
+      }
+
+      /* set NPLYTERM based on header.  if NPLYTERM is missing, it should have a 
+	 value of 0, unless the projection type is one of PLY, DIS, WRP, in which
+	 case it should be set to 3 */
+      ctype = &coords[0].ctype[4];
+      Polynomial = !strcmp (ctype, "-PLY") || !strcmp (ctype, "-DIS") || !strcmp (ctype, "-WRP");
+      Polyterm = gfits_scan (header, "NPLYTERM", "%d", 1, &itmp);
+
+      coords[0].Npolyterms = 0;
+      if (Polynomial && !Polyterm) coords[0].Npolyterms = 1;
+      if (Polyterm) coords[0].Npolyterms = itmp;
+
+      switch (coords[0].Npolyterms) {
+	case 3:
+	  status &= gfits_scan (header, "PCA1X3Y0", "%f", 1, &coords[0].polyterms[3][0]);
+	  status &= gfits_scan (header, "PCA1X2Y1", "%f", 1, &coords[0].polyterms[4][0]);
+	  status &= gfits_scan (header, "PCA1X1Y2", "%f", 1, &coords[0].polyterms[5][0]);
+	  status &= gfits_scan (header, "PCA1X0Y3", "%f", 1, &coords[0].polyterms[6][0]);
+	  status &= gfits_scan (header, "PCA2X3Y0", "%f", 1, &coords[0].polyterms[3][1]);
+	  status &= gfits_scan (header, "PCA2X2Y1", "%f", 1, &coords[0].polyterms[4][1]);
+	  status &= gfits_scan (header, "PCA2X1Y2", "%f", 1, &coords[0].polyterms[5][1]);
+	  status &= gfits_scan (header, "PCA2X0Y3", "%f", 1, &coords[0].polyterms[6][1]);
+	case 2:
+	  status &= gfits_scan (header, "PCA1X2Y0", "%f", 1, &coords[0].polyterms[0][0]);
+	  status &= gfits_scan (header, "PCA1X1Y1", "%f", 1, &coords[0].polyterms[1][0]);
+	  status &= gfits_scan (header, "PCA1X0Y2", "%f", 1, &coords[0].polyterms[2][0]);
+	  status &= gfits_scan (header, "PCA2X2Y0", "%f", 1, &coords[0].polyterms[0][1]);
+	  status &= gfits_scan (header, "PCA2X1Y1", "%f", 1, &coords[0].polyterms[1][1]);
+	  status &= gfits_scan (header, "PCA2X0Y2", "%f", 1, &coords[0].polyterms[2][1]);
+	case 0:
+	case 1:
+	  break;
+      }
+    } else {
+      if (gfits_scan (header, "CD1_1", "%f", 1, &coords[0].pc1_1)) {
+	status &= gfits_scan (header, "CD1_2", "%f", 1, &coords[0].pc1_2);
+	status &= gfits_scan (header, "CD2_1", "%f", 1, &coords[0].pc2_1);
+	status &= gfits_scan (header, "CD2_2", "%f", 1, &coords[0].pc2_2);
+	/* renormalize */
+	scale = hypot (coords[0].pc1_1, coords[0].pc1_2);
+	coords[0].cdelt1 = coords[0].cdelt2 = scale;
+	coords[0].pc1_1 /= scale;
+	coords[0].pc1_2 /= scale;
+	coords[0].pc2_1 /= scale;
+	coords[0].pc2_2 /= scale;
+      } else {
+	status = FALSE;
+      }
+    }
+  } else {
+    /* some of my thesis data uses this simple linear model - convert on read? */
+    if (gfits_scan (header, "RA_O", "%lf", 1, &coords[0].crval1)) {
+      status  = gfits_scan (header, "RA_X", "%f", 1, &coords[0].pc1_1);
+      status &= gfits_scan (header, "RA_Y", "%f", 1, &coords[0].pc1_2);
+      status &= gfits_scan (header, "DEC_O", "%lf", 1, &coords[0].crval2);  
+      status &= gfits_scan (header, "DEC_X", "%f", 1, &coords[0].pc2_1);
+      status &= gfits_scan (header, "DEC_Y", "%f", 1, &coords[0].pc2_2);
+      coords[0].crpix1 = coords[0].crpix2 = 0.0;
+      coords[0].cdelt1 = coords[0].cdelt2 = 1.0;
+      strcpy (coords[0].ctype, "GENE");
+    }
+  }
+  if (status) {
+    if (!gfits_scan (header, "EQUINOX", "%lf", 1, &equinox)) {
+      if (!gfits_scan (header, "EPOCH", "%lf", 1, &equinox)) {
+	equinox = 2000.0;
+      }
+    }
+    if (fabs (equinox - 2000.0) > 0.1) {
+      coords_precess (&coords[0].crval1, &coords[0].crval2, equinox, 2000.0);
+    } 
+  }
+  if (!status) {
+    fprintf (stderr, "error getting all elements for coordinate mode %s\n", coords[0].ctype);
+    coords[0].crval1 = coords[0].crpix1 = coords[0].cdelt1 = 0.0;
+    coords[0].crval2 = coords[0].crpix2 = coords[0].cdelt2 = 0.0;
+    coords[0].pc1_1 = coords[0].pc2_2 = 1.0;
+    coords[0].pc2_1 = coords[0].pc1_2 = 0.0;
+    strcpy (coords[0].ctype, "NONE");
+  }
+  return (status);
+}
+
+int PutCoords (Coords *coords, Header *header) {
+  
+  int OldAIPS;
+  char csys[16], ctype[16];
+  double rotate, Lambda;
+
+  /* modifications to the ctype? */
+  OldAIPS = FALSE;
+  gfits_modify (header, "CTYPE2",   "%s",  1, coords[0].ctype);
+  if (!strcmp(coords[0].ctype, "MM")) {
+    gfits_modify (header, "CTYPE1",   "%s",  1, "LL");
+    OldAIPS = TRUE;
+  } else {
+    strcpy (csys, "NONE");
+    if (!strncmp (coords[0].ctype, "DEC-", 4)) strcpy (csys, "RA--");
+    if (!strncmp (coords[0].ctype, "GLAT", 4)) strcpy (csys, "GLON");
+    if (!strncmp (coords[0].ctype, "ELAT", 4)) strcpy (csys, "ELON");
+    if (!strncmp (coords[0].ctype, "HLAT", 4)) strcpy (csys, "HLON");
+    if (!strncmp (coords[0].ctype, "SLAT", 4)) strcpy (csys, "SLON");
+    if (!strcmp (csys, "NONE")) return (FALSE);
+    sprintf (ctype, "%s-%s", csys, &coords[0].ctype[5]);
+    gfits_modify (header, "CTYPE1",   "%s",  1, ctype);
+  }    
+
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+  if (OldAIPS) {
+    Lambda = coords[0].cdelt2 / coords[0].cdelt1;
+    rotate = DEG_RAD*atan2 (coords[0].pc2_1*Lambda, coords[0].pc1_1);
+    gfits_modify (header, "CROTA1", "%f", 1, rotate);
+    gfits_modify (header, "CROTA2", "%f", 1, rotate);
+    return (TRUE);
+  } 
+
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+  gfits_modify (header, "NPLYTERM", "%d",  1, coords[0].Npolyterms);
+
+  /* RA Terms */
+  if (coords[0].Npolyterms > 1) {
+    gfits_modify (header, "PCA1X2Y0", "%le", 1, coords[0].polyterms[0][0]);   /* polyterms[0]); */
+    gfits_modify (header, "PCA1X1Y1", "%le", 1, coords[0].polyterms[1][0]);   /* polyterms[1]); */
+    gfits_modify (header, "PCA1X0Y2", "%le", 1, coords[0].polyterms[2][0]);   /* polyterms[2]); */
+  }
+  if (coords[0].Npolyterms > 2) {
+    gfits_modify (header, "PCA1X3Y0", "%le", 1, coords[0].polyterms[3][0]);   /* polyterms[3]); */
+    gfits_modify (header, "PCA1X2Y1", "%le", 1, coords[0].polyterms[4][0]);   /* polyterms[4]); */
+    gfits_modify (header, "PCA1X1Y2", "%le", 1, coords[0].polyterms[5][0]);   /* polyterms[5]); */
+    gfits_modify (header, "PCA1X0Y3", "%le", 1, coords[0].polyterms[6][0]);   /* polyterms[6]); */
+  }
+
+  /* Dec Terms */
+  if (coords[0].Npolyterms > 1) {
+    gfits_modify (header, "PCA2X2Y0", "%le", 1, coords[0].polyterms[0][1]);   /* polyterms[7]); */
+    gfits_modify (header, "PCA2X1Y1", "%le", 1, coords[0].polyterms[1][1]);   /* polyterms[8]); */
+    gfits_modify (header, "PCA2X0Y2", "%le", 1, coords[0].polyterms[2][1]);   /* polyterms[9]); */
+  }
+  if (coords[0].Npolyterms > 2) {
+    gfits_modify (header, "PCA2X3Y0", "%le", 1, coords[0].polyterms[3][1]);   /* polyterms[10]); */
+    gfits_modify (header, "PCA2X2Y1", "%le", 1, coords[0].polyterms[4][1]);   /* polyterms[11]); */
+    gfits_modify (header, "PCA2X1Y2", "%le", 1, coords[0].polyterms[5][1]);   /* polyterms[12]); */
+    gfits_modify (header, "PCA2X0Y3", "%le", 1, coords[0].polyterms[6][1]);   /* polyterms[13]); */
+  }
+  return (TRUE);
+}
+
+void coords_precess (double *ra, double *dec, double in_epoch, double out_epoch) {
+
+  double T;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+  
+  A = *ra;
+  D = *dec;
+  SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+  CD = sqrt (1 - SD*SD);
+  SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+  CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+  
+  DEC = DEG_RAD*asin(SD);
+  RA  = DEG_RAD*atan2(SA, CA) + z;
+  
+  if (RA < 0)
+    RA += 360;
+  
+  *ra = RA;
+  *dec = DEC; 
+}
+
+/* -PLY projection is an extrapolation of the -TAN projection. 
+   In addition to the usual linear terms of CRPIXi, PC00i00j, there are 
+   higher order polynomial terms (up to 3rd order):
+   Axis 1 terms:
+   PCA1X2Y0 = coords.polyterm[0][0] = x^2                                             
+   PCA1X1Y1 = coords.polyterm[1][0] = xy                                          
+   PCA1X0Y2 = coords.polyterm[2][0] = y^2                                             
+   PCA1X3Y0 = coords.polyterm[3][0] = x^3                                             
+   PCA1X2Y1 = coords.polyterm[4][0] = x^2 y                                             
+   PCA1X1Y2 = coords.polyterm[5][0] = x y^2                                             
+   PCA1X0Y3 = coords.polyterm[6][0] = y^2                                              
+   Axis 2 terms:
+   PCA2X2Y0 = coords.polyterm[0][1] = x^2                                               
+   PCA2X1Y1 = coords.polyterm[1][1] = xy                                                
+   PCA2X0Y2 = coords.polyterm[2][1] = y^2                                               
+   PCA2X3Y0 = coords.polyterm[3][1] = x^3                                               
+   PCA2X2Y1 = coords.polyterm[4][1] = x^2 y                                             
+   PCA2X1Y2 = coords.polyterm[5][1] = x y^2                                             
+   PCA2X0Y3 = coords.polyterm[6][1] = y^2                                               
+*/
+
+# if (0)
+
+  /** convert pixel coordinates to cartesian system **/
+  X = coords[0].cdelt1*(x - coords[0].crpix1);
+  Y = coords[0].cdelt2*(y - coords[0].crpix2);
+  if (Polynomi) {
+    if (coords[0].Npolyterms > 2) {
+      X += coords[0].cdelt1*(x*x*coords[0].polyterms[0][0] + x*y*coords[0].polyterms[1][0] + y*y*coords[0].polyterms[2][0]);
+      Y += coords[0].cdelt2*(x*x*coords[0].polyterms[0][1] + x*y*coords[0].polyterms[1][1] + y*y*coords[0].polyterms[2][1]);
+    }
+    if (coords[0].Npolyterms > 2) {
+      X += coords[0].cdelt1*(x*x*x*coords[0].polyterms[3][0] + x*x*y*coords[0].polyterms[4][0] + x*y*y*coords[0].polyterms[5][0] + y*y*y*coords[0].polyterms[6][0]);
+      Y += coords[0].cdelt2*(x*x*x*coords[0].polyterms[3][1] + x*x*y*coords[0].polyterms[4][1] + x*y*y*coords[0].polyterms[5][1] + y*y*y*coords[0].polyterms[6][1]);
+    }
+  }
+
+  L = (X*coords[0].pc1_1 + Y*coords[0].pc1_2);
+  M = (X*coords[0].pc2_1 + Y*coords[0].pc2_2);
+/** this code is the old method used for higher order terms.  they
+    are essentially 6th order, with weird coupled terms.
+    I don't think any real data used these terms, but they should 
+    be re-calculated, I would think 
+**/
+
+# endif
+
+/*
+
+Projections and Transformations
+
+The Coords structure is used to represent the standard FITS header representations of the WCS terms.
+The FITS WCS encapsulates several concepts in one system: coordinate frame, projection, and
+transformations.  The ctype variable defines both the frame (ie, RA/DEC, GLON/GLAT, etc) and the
+projection (ie, AIT, SIN, TAN, etc).  The associated terms (PC00i00j) define transformations from
+the projection system to another linear coordinate system.  I have extended the basic WCS
+transformation terms to include higher-order polynomial terms.  The presence of higher-order terms
+is indicated in the header by the NPLYTERM keyword, with a value greater than 1 (a value of 0 or 1
+implies no higher-order coefficients).  The coefficients have keywords of the form PCAnXiYj where
+'n' is the axis corresponding to the dependent variable, 'i' is the order of the X component and 'j'
+is the order of the Y component.  A value of 2 indicates that the second-order coefficients are
+defined (PCAnX2Y0, PCAnX1Y1, PCAnX0Y2); A value of 3 indicates that the third-order coefficients are
+also defined (PCAnX3Y0, PCAnX2Y1, PCAnX1Y2, PCAnX0Y3).  Some headers in the past were generated
+without the NPLYTERM keyword.  a value of PLY, DIS, or WRP for the projection without a
+corresponding value for NPLYTERM implies that the value should be interpreted as '3'.
+
+*/
+
+  /* PLY is equiv to LIN with higher order terms
+     ZPL is equiv to ZEA with higher order terms
+     DIS is equiv to TAN with higher order terms
+     WRP is equiv to PLY, with implied mosaic */
+
+OhanaProjection GetProjection (char *ctype) {
+  if (!strcmp(&ctype[4], "-ZEA")) return PROJ_ZEA;
+  if (!strcmp(&ctype[4], "-ZPL")) return PROJ_ZPL;
+  if (!strcmp(&ctype[4], "-ARC")) return PROJ_ARC;
+  if (!strcmp(&ctype[4], "-STG")) return PROJ_STG;
+  if (!strcmp(&ctype[4], "-SIN")) return PROJ_SIN;
+  if (!strcmp(&ctype[0], "MM"))   return PROJ_SIN; // note ctype[0]
+  if (!strcmp(&ctype[4], "-TAN")) return PROJ_TAN;
+  if (!strcmp(&ctype[4], "-TNX")) return PROJ_TNX;
+  if (!strcmp(&ctype[4], "-DIS")) return PROJ_DIS;
+  if (!strcmp(&ctype[4], "-LIN")) return PROJ_LIN;
+  if (!strcmp(&ctype[0], "GENE")) return PROJ_LIN; // note ctype[0]
+  if (!strcmp(&ctype[4], "-PLY")) return PROJ_PLY;
+  if (!strcmp(&ctype[4], "-WRP")) return PROJ_WRP;
+  if (!strcmp(&ctype[4], "-AIT")) return PROJ_AIT;
+  if (!strcmp(&ctype[4], "-GLS")) return PROJ_GLS;
+  if (!strcmp(&ctype[4], "-PAR")) return PROJ_PAR;
+  return PROJ_NONE;
+}
+  
+int SetProjection (char *ctype, OhanaProjection proj) {
+  switch (proj) {
+    case PROJ_ZEA: strcpy(&ctype[4], "-ZEA"); return TRUE;
+    case PROJ_ZPL: strcpy(&ctype[4], "-ZPL"); return TRUE;
+    case PROJ_ARC: strcpy(&ctype[4], "-ARC"); return TRUE;
+    case PROJ_STG: strcpy(&ctype[4], "-STG"); return TRUE;
+    case PROJ_SIN: strcpy(&ctype[4], "-SIN"); return TRUE;
+    case PROJ_TAN: strcpy(&ctype[4], "-TAN"); return TRUE;
+    case PROJ_TNX: strcpy(&ctype[4], "-TNX"); return TRUE;
+    case PROJ_DIS: strcpy(&ctype[4], "-DIS"); return TRUE;
+    case PROJ_LIN: strcpy(&ctype[4], "-LIN"); return TRUE;
+    case PROJ_PLY: strcpy(&ctype[4], "-PLY"); return TRUE;
+    case PROJ_WRP: strcpy(&ctype[4], "-WRP"); return TRUE;
+    case PROJ_AIT: strcpy(&ctype[4], "-AIT"); return TRUE;
+    case PROJ_GLS: strcpy(&ctype[4], "-GLS"); return TRUE;
+    case PROJ_PAR: strcpy(&ctype[4], "-PAR"); return TRUE;
+    case PROJ_NONE: return FALSE;
+  }
+  return FALSE;
+}  
+
+OhanaProjectionMode GetProjectionMode (OhanaProjection proj) {
+  switch (proj) {
+    case PROJ_ZEA:
+    case PROJ_ZPL:
+    case PROJ_ARC:
+    case PROJ_STG:
+    case PROJ_SIN:
+    case PROJ_TAN:
+    case PROJ_TNX:
+    case PROJ_DIS:
+      return PROJ_MODE_ZENITHAL;
+    case PROJ_LIN: 
+    case PROJ_PLY: 
+    case PROJ_WRP: 
+      return PROJ_MODE_CARTESIAN;
+    case PROJ_AIT:
+    case PROJ_GLS:
+    case PROJ_PAR:
+      return PROJ_MODE_PSEUDOCYL;
+    default: 
+      return PROJ_MODE_NONE;
+  }
+  return PROJ_MODE_NONE;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog.c	(revision 17422)
@@ -0,0 +1,446 @@
+# include <dvo.h>
+# define DEBUG 1
+
+void dvo_catalog_test (Catalog *catalog, int halt) {
+
+  Catalog *subcat;
+
+  // fprintf (stderr, "catalog: Naverage = %d, average = %zx\n", catalog[0].Naverage, (size_t) catalog[0].average);
+  // fprintf (stderr, "catalog: Nmeasure = %d, measure = %zx\n", catalog[0].Nmeasure, (size_t) catalog[0].measure);
+  // fprintf (stderr, "catalog: Nmissing = %d, missing = %zx\n", catalog[0].Nmissing, (size_t) catalog[0].missing);
+  // fprintf (stderr, "catalog: Nsecfilt = %d, secfilt = %zx\n", catalog[0].Nsecfilt, (size_t) catalog[0].secfilt);
+
+  if (!catalog[0].measure || !catalog[0].secfilt) {
+    fprintf (stderr, "error: %s\n", catalog[0].filename);
+    if (halt) abort ();
+  }
+
+  // XXX test that things are correctly initialized
+  if (catalog[0].catmode != DVO_MODE_SPLIT) return;
+
+  subcat = catalog[0].measure_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+  subcat = catalog[0].missing_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+  subcat = catalog[0].secfilt_catalog;
+  if (subcat) {
+    if (subcat[0].measure_catalog || subcat[0].secfilt_catalog || subcat[0].missing_catalog) {
+      fprintf (stderr, "error in init\n");
+      abort ();
+    }
+  }
+}
+
+int dvo_catalog_catformat (char *catformat) {
+  
+  /* set the specified CATFORMAT */
+  if (!strcasecmp (catformat, "INTERNAL"))        return (DVO_FORMAT_INTERNAL);
+  if (!strcasecmp (catformat, "LONEOS"))    	  return (DVO_FORMAT_LONEOS);
+  if (!strcasecmp (catformat, "ELIXIR"))    	  return (DVO_FORMAT_ELIXIR);
+  if (!strcasecmp (catformat, "PANSTARRS_DEV_0")) return (DVO_FORMAT_PANSTARRS_DEV_0);
+  if (!strcasecmp (catformat, "PANSTARRS_DEV_1")) return (DVO_FORMAT_PANSTARRS_DEV_1);
+  if (!strcasecmp (catformat, "PS1_DEV_1"))       return (DVO_FORMAT_PS1_DEV_1);
+  if (!strcasecmp (catformat, "PS1_DEV_2"))       return (DVO_FORMAT_PS1_DEV_2);
+  return (DVO_FORMAT_UNDEF);
+}
+
+int dvo_catalog_catmode (char *catmode) {
+
+  /* set the specified CATMODE */
+  if (!strcasecmp (catmode, "RAW"))   return (DVO_MODE_RAW);
+  if (!strcasecmp (catmode, "MEF"))   return (DVO_MODE_MEF);
+  if (!strcasecmp (catmode, "SPLIT")) return (DVO_MODE_SPLIT);
+  return (DVO_MODE_UNDEF);
+}
+
+// init all data, or just catalog data
+void dvo_catalog_init (Catalog *catalog, int complete) {
+
+  // the following are used to guide open/create/load
+  if (complete) {
+    catalog[0].f = NULL;
+    catalog[0].filename = NULL;
+
+    catalog[0].lockmode = 0;
+    catalog[0].catmode  = 0;
+    catalog[0].catformat = 0;
+    catalog[0].catflags = 0;
+    catalog[0].sorted = 0;
+    catalog[0].Nsecfilt = 0;
+  }
+
+  gfits_init_header (&catalog[0].header);
+
+  // the following describe the catalog files on disk
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL; 
+  catalog[0].missing = NULL; 
+  catalog[0].secfilt = NULL;
+  
+  catalog[0].Naverage = 0;
+  catalog[0].Nmeasure = 0;
+  catalog[0].Nmissing = 0;
+  catalog[0].Nsecf_mem = 0;
+
+  catalog[0].Naves_disk = 0;
+  catalog[0].Nmeas_disk = 0;
+  catalog[0].Nmiss_disk = 0;
+  catalog[0].Nsecf_disk = 0;
+
+  catalog[0].Naves_off  = 0;
+  catalog[0].Nmeas_off  = 0;
+  catalog[0].Nmiss_off  = 0;
+  catalog[0].Nsecf_off  = 0;
+
+  /* pointers to SPLIT data files */
+  catalog[0].measure_catalog = NULL;
+  catalog[0].missing_catalog = NULL;
+  catalog[0].secfilt_catalog = NULL;
+
+  /* pointers for data manipulation */
+  catalog[0].found = NULL;
+  catalog[0].image = NULL;
+  catalog[0].mosaic = NULL;
+  catalog[0].X = NULL;
+  catalog[0].Y = NULL;
+}
+
+/* possible exit status for lock_catalog: 
+   DVO_CAT_OPEN_FAIL - failure (including lock failure)
+   DVO_CAT_OPEN_OK - success
+   DVO_CAT_OPEN_EMPTY - empty file (file may be open or closed!) 
+*/
+int dvo_catalog_lock (Catalog *catalog, int lockmode) {
+
+  int dbstate;
+
+  /* record lockmode choice */
+  catalog[0].lockmode = lockmode;
+
+  /* set lock on database, create stream f */
+  // fprintf (stderr, "locking: %s\n", catalog[0].filename);
+  catalog[0].f = fsetlockfile (catalog[0].filename, 3600.0, catalog[0].lockmode, &dbstate);
+
+  if (dbstate == LCK_MISSING) return (DVO_CAT_OPEN_EMPTY);
+  if (dbstate == LCK_EMPTY)   return (DVO_CAT_OPEN_EMPTY);
+  if (catalog[0].f == NULL)   return (DVO_CAT_OPEN_FAIL);
+
+  fseek (catalog[0].f, 0, SEEK_SET);
+  return (DVO_CAT_OPEN_OK);
+}
+
+int dvo_catalog_unlock (Catalog *catalog) {
+
+  int fd, dbstate;
+  struct stat filestat;
+
+  if (catalog[0].f == (FILE *) NULL) return (2);
+  fflush (catalog[0].f);
+  // fprintf (stderr, "unlocking: %s\n", catalog[0].filename);
+
+  // attempt to unlink an empty file
+  fd = fileno (catalog[0].f);
+  if (!fstat (fd, &filestat)) {
+    if (filestat.st_size == 0) {
+      unlink (catalog[0].filename);
+    }
+  }
+
+  fclearlockfile (catalog[0].filename, catalog[0].f, catalog[0].lockmode, &dbstate);
+
+  if (catalog[0].catmode == DVO_MODE_SPLIT) {
+    if (catalog[0].measure_catalog != NULL) dvo_catalog_unlock (catalog[0].measure_catalog);
+    if (catalog[0].missing_catalog != NULL) dvo_catalog_unlock (catalog[0].missing_catalog);
+    if (catalog[0].secfilt_catalog != NULL) dvo_catalog_unlock (catalog[0].secfilt_catalog);
+  }
+  return (1);
+}
+
+// open an existing catalog file or or create a new one as needed (iomode == "w")
+// returns FALSE if a catalog could not be opened
+// returns TRUE if the catalog was empty
+// XXX allow stdout as destination (don't lock)
+enum {DVO_OPEN_NONE, DVO_OPEN_READ, DVO_OPEN_WRITE, DVO_OPEN_UPDATE};
+int dvo_catalog_open (Catalog *catalog, SkyRegion *region, int VERBOSE, char *iomode) {
+
+  int Nsecfilt, mode;
+
+  mode = DVO_OPEN_NONE;
+  if (!strcasecmp (iomode, "r")) mode = DVO_OPEN_READ;
+  if (!strcasecmp (iomode, "w")) mode = DVO_OPEN_WRITE;
+  if (!strcasecmp (iomode, "a")) mode = DVO_OPEN_UPDATE;
+  if (mode == DVO_OPEN_NONE) return (FALSE);
+
+  // save the expected Nsecfilt value for testing
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  dvo_catalog_init (catalog, FALSE);
+
+  catalog[0].lockmode  = LCK_XCLD;
+  if (mode == DVO_OPEN_READ) catalog[0].lockmode  = LCK_SOFT;
+  
+  // XXX make a backup?  always?
+  if (!check_file_access (catalog[0].filename, TRUE, VERBOSE)) {
+    if (VERBOSE) fprintf (stderr, "no permission to access %s\n", catalog[0].filename);
+    return (FALSE);
+  }
+
+  if (VERBOSE) fprintf (stderr, "opening %s\n", catalog[0].filename);
+  
+  switch (dvo_catalog_lock (catalog, catalog[0].lockmode)) {
+  case DVO_CAT_OPEN_FAIL:
+    if (VERBOSE) fprintf (stderr, "can't lock file %s\n", catalog[0].filename);
+    return (FALSE);
+  case DVO_CAT_OPEN_OK:
+    if (!dvo_catalog_load (catalog, VERBOSE)) {
+      if (VERBOSE) fprintf (stderr, "failure loading catalog\n");
+      return (FALSE);
+    }
+    if (!dvo_catalog_check (catalog, Nsecfilt, TRUE)) {
+      if (VERBOSE) fprintf (stderr, "can't reduce number of secondary filters\n");
+      return (FALSE);
+    }
+    if (VERBOSE) fprintf (stderr, "loaded existing file %s\n", catalog[0].filename);
+    break;
+  case DVO_CAT_OPEN_EMPTY:
+    if ((mode == DVO_OPEN_READ) || (mode == DVO_OPEN_UPDATE)) return (TRUE);
+    catalog[0].Nsecfilt = Nsecfilt;
+    dvo_catalog_create (region, catalog); /* fills in new header info */
+    if (VERBOSE) fprintf (stderr, "creating new file %s\n", catalog[0].filename);
+    break;
+  }
+  return (TRUE);
+}
+
+// load an existing dvo_catalog currenly, the catmode information is carried per
+// catalog.  the user-set value of catmode will set the output mode for new
+// tables an old table will keep its mode.
+int dvo_catalog_load (Catalog *catalog, int VERBOSE) {
+  
+  int Naxis, split, status;
+  char measure[80];
+
+  dvo_catalog_init (catalog, FALSE);
+
+  // load the main catalog header, determine characteristics
+  if (!gfits_fread_header (catalog[0].f, &catalog[0].header)) {
+    if (VERBOSE) fprintf (stderr, "failure to read main catalog header\n");
+    return (FALSE);
+  }
+  // check if the table has been sorted or not 
+  status = gfits_scan (&catalog[0].header, "SORTED", "%t", 1, &catalog[0].sorted);
+  if (!status) catalog[0].sorted = TRUE;
+
+  // determine catmode
+  if (!gfits_scan (&catalog[0].header, "NAXIS", "%d", 1, &Naxis)) {
+    if (VERBOSE) fprintf (stderr, "can't determine catalog db mode\n");
+    return (FALSE);
+  }
+  catalog[0].catmode = DVO_MODE_MEF;
+  split = gfits_scan (&catalog[0].header, "MEASURE", "%s", 1, measure);
+  if (split) catalog[0].catmode = DVO_MODE_SPLIT;
+  if (Naxis == 2) catalog[0].catmode = DVO_MODE_RAW;
+  /* don't use Naxis == 2 and split mode! */
+
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_RAW)\n");
+      dvo_catalog_load_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_MEF)\n");
+      dvo_catalog_load_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_SPLIT)\n");
+      dvo_catalog_load_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error getting catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the data, unlink if empty?  'save' means: write out all data currently in
+// memory.  NOTE: this is currently not always possible: for non-SPLIT mode files, this
+// operation may require expanding the file size, which does not automatically happen
+int dvo_catalog_save (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  // XXX handle return status
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      dvo_catalog_save_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      dvo_catalog_save_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_save_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the data, assuming the in-memory data represents all data for the table.
+// NOTE: This operation may truncate the file and delete entries.
+int dvo_catalog_save_complete (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  // XXX handle return status
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      dvo_catalog_save_raw (catalog, VERBOSE);
+      break;
+    case DVO_MODE_MEF:
+      dvo_catalog_save_mef (catalog, VERBOSE);
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_save_split_complete (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+// write out the in-memory data which extends beyond the data on disk.  NOTE: this is
+// currently only possible for the SPLIT mode.
+int dvo_catalog_update (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  catalog[0].sorted = FALSE;
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  /* update is only valid for catmode SPLIT */
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      fprintf (stderr, "not allowed for RAW mode\n");
+      break;
+    case DVO_MODE_MEF:
+      fprintf (stderr, "not allowed for MEF mode\n");
+      break;
+    case DVO_MODE_SPLIT:
+      dvo_catalog_update_split (catalog, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "failure updating catalog\n");
+      fprintf (stderr, "invalid catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+int dvo_catalog_check (Catalog *catalog, int Nsecfilt, int extend) {
+
+  int i, j, Nextra, in, out;
+  SecFilt *insec, *outsec;
+
+  if (Nsecfilt == 0) return (TRUE); // if Nsecfilt is not set, don't do this check
+  if (catalog[0].Nsecfilt == Nsecfilt) return (TRUE);
+  if (catalog[0].Nsecfilt > Nsecfilt) return (FALSE);
+  if ((catalog[0].Nsecfilt < Nsecfilt) && !extend) return (FALSE);
+
+  if ((catalog[0].Nsecfilt < Nsecfilt) && extend) {
+    Nextra = Nsecfilt - catalog[0].Nsecfilt;
+    insec = catalog[0].secfilt;
+    ALLOCATE (outsec, SecFilt, catalog[0].Naverage * Nsecfilt);
+    for (in = out = i = 0; i < catalog[0].Naverage; i++) {
+      for (j = 0; j < catalog[0].Nsecfilt; j++, in++, out++) {
+	outsec[out].M  = insec[in].M;
+	outsec[out].dM = insec[in].dM;
+	outsec[out].Xm = insec[in].Xm;
+      }
+      for (j = 0; j < Nextra; j++, out++) {
+	outsec[out].M  = NAN;
+	outsec[out].dM = NAN;
+	outsec[out].Xm = NAN_S_SHORT;
+      }
+    }
+    free (catalog[0].secfilt);
+    catalog[0].secfilt = outsec;
+    catalog[0].Nsecfilt = Nsecfilt;
+    catalog[0].Nsecf_mem = Nsecfilt * catalog[0].Naverage;
+  }
+  return (TRUE);
+}
+
+void dvo_catalog_free (Catalog *catalog) {
+  dvo_catalog_free_data (catalog);
+  gfits_free_header (&catalog[0].header);
+}
+
+void dvo_catalog_free_data (Catalog *catalog) {
+
+  /* free, initialize data structures */
+  if (catalog[0].average != NULL) {
+    free (catalog[0].average); 
+    catalog[0].Naverage = 0;
+    catalog[0].average = NULL;
+  }
+  if (catalog[0].measure != NULL) {
+    free (catalog[0].measure); 
+    catalog[0].Nmeasure = 0;
+    catalog[0].measure = NULL;
+  }
+  if (catalog[0].missing != NULL) {
+    free (catalog[0].missing); 
+    catalog[0].Nmissing = 0;
+    catalog[0].missing = NULL;
+  }
+  if (catalog[0].secfilt != NULL) {
+    free (catalog[0].secfilt); 
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].secfilt = NULL;
+  }
+}
+
+/*
+  mode   : items to read (LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF)
+  format : what table structure on disk (INTERNAL, LONEOS, etc, )
+  style  : raw, mef, split, mysql
+*/
+
+int dvo_catalog_load_segment (Catalog *catalog, int VERBOSE, int start, int Nrows) {
+
+  switch (catalog[0].catmode) {
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_RAW)\n");
+      fprintf (stderr, "cannot do this in raw mode\n");
+      // dvo_catalog_load_segment_raw (catalog, VERBOSE, start, Nrows);
+      break;
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_MEF)\n");
+      fprintf (stderr, "cannot do this in mef mode\n");
+      // dvo_catalog_load_segment_mef (catalog, VERBOSE, start, Nrows);
+      break;
+    case DVO_MODE_SPLIT:
+      if (VERBOSE) fprintf (stderr, "reading catalog (mode DVO_MODE_SPLIT)\n");
+      dvo_catalog_load_segment_split (catalog, VERBOSE, start, Nrows);
+      break;
+    default:
+      fprintf (stderr, "error getting catalog mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include <dvo.h>
+
+int dvo_match_image (Image *image, int Nimage, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[N].tzero == T) && (image[N].photcode == S)) {
+      return (N);
+    }
+  }
+  return (-1);
+}
+
+// if necessary, determine chip coordinates for all measures
+int dvo_catalog_chipcoords (Catalog *catalog, Image *image, int Nimage) {
+
+  int i, j, m, N;
+  double ra, dec, x, y;
+  Average *average;
+  Measure *measure;
+
+  if (catalog[0].catformat == DVO_FORMAT_LONEOS) goto do_convert;  // special conversion for LONEOS
+  if (catalog[0].catformat == DVO_FORMAT_ELIXIR) goto do_convert;  // special conversion for ELIXIR
+  return TRUE;
+
+do_convert:
+  average = catalog[0].average;
+  measure = catalog[0].measure;
+  
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    m = average[i].measureOffset;
+    for (j = 0; j < average[i].Nmeasure; j++, m++) {
+      ra  = average[i].R - measure[m].dR / 3600.0;
+      dec = average[i].D - measure[m].dD / 3600.0;
+      N = dvo_match_image (image, Nimage, measure[m].t, measure[m].photcode);
+      if (N == -1) continue;
+      RD_to_XY (&x, &y, ra, dec, &image[N].coords);
+      measure[m].Xccd = x;
+      measure[m].Yccd = y;
+    }
+  }
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_create.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 17422)
@@ -0,0 +1,139 @@
+# include <dvo.h>
+# define DEBUG 0
+
+// create a new dvo catalog file (if split, lock extra files as well?)
+// catalog internals which must be set:
+// catalog[0].filename
+// catalog[0].catmode
+// catalog[0].catformat (used by dvo_catalog_save)
+// catalog[0].lockmode
+void dvo_catalog_create (SkyRegion *region, Catalog *catalog) {
+
+  int length;
+  char *path, *root, *file, *line;
+  time_t now;
+
+  if (DEBUG) fprintf (stderr, "new catalog file: %s\n", catalog[0].filename);
+
+  // init the data values, not the internals listed above
+  dvo_catalog_init (catalog, FALSE);
+
+  /* for RAW mode, make header a fake image */
+  if (catalog[0].catmode == DVO_MODE_RAW) {
+    catalog[0].header.bitpix   = 16;
+    catalog[0].header.Naxes    = 2;
+    catalog[0].header.Naxis[0] = 1;
+    catalog[0].header.Naxis[1] = 1;
+  }
+  gfits_create_header (&catalog[0].header);
+  
+  if (catalog[0].catmode == DVO_MODE_SPLIT) {
+    path = pathname (catalog[0].filename);
+    root = filerootname (catalog[0].filename);
+    length = strlen(path) + strlen(root) + 6;
+
+    /* define measure catalog file */
+    ALLOCATE (catalog[0].measure_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].measure_catalog, TRUE);
+
+    /* create basic data for measure catalog file */
+    gfits_create_header (&catalog[0].measure_catalog[0].header);
+    ALLOCATE (catalog[0].measure_catalog[0].filename, char, length);
+    sprintf (catalog[0].measure_catalog[0].filename, "%s/%s.cpm", path, root);
+    file = filebasename (catalog[0].measure_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "MEASURE", "%s", 1, file);
+    free (file);
+
+    /* define missing catalog file */
+    ALLOCATE (catalog[0].missing_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].missing_catalog, TRUE);
+
+    /* create basic data for missing catalog file */
+    gfits_create_header (&catalog[0].missing_catalog[0].header);
+    ALLOCATE (catalog[0].missing_catalog[0].filename, char, length);
+    sprintf (catalog[0].missing_catalog[0].filename, "%s/%s.cpn", path, root);
+    file = filebasename (catalog[0].missing_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "MISSING", "%s", 1, file);
+    free (file);
+
+    /* define secfilt catalog file */
+    ALLOCATE (catalog[0].secfilt_catalog, Catalog, 1);
+    dvo_catalog_init (catalog[0].secfilt_catalog, TRUE);
+
+    /* create basic data for secfilt catalog file */
+    gfits_create_header (&catalog[0].secfilt_catalog[0].header);
+    ALLOCATE (catalog[0].secfilt_catalog[0].filename, char, length);
+    sprintf (catalog[0].secfilt_catalog[0].filename, "%s/%s.cps", path, root);
+    file = filebasename (catalog[0].secfilt_catalog[0].filename);
+    gfits_modify (&catalog[0].header, "SECFILT", "%s", 1, file);
+    free (file);
+    free (path);
+    free (root);
+
+    // lock the additional split files
+    // XXX clear residual locks if we fail
+    if (dvo_catalog_lock (catalog[0].measure_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].missing_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].secfilt_catalog, catalog[0].lockmode) != DVO_CAT_OPEN_EMPTY) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+
+  }    
+
+  /* write RA,DEC range in header */
+  if (region) {
+    gfits_modify (&catalog[0].header, "RA0",  "%lf", 1, region[0].Rmin);
+    gfits_modify (&catalog[0].header, "DEC0", "%lf", 1, region[0].Dmin);
+    gfits_modify (&catalog[0].header, "RA1",  "%lf", 1, region[0].Rmax);
+    gfits_modify (&catalog[0].header, "DEC1", "%lf", 1, region[0].Dmax);
+  }
+
+  /* write creation date in header */
+  ohana_str_to_time ("now", &now);
+  line = ohana_sec_to_date (now);
+  gfits_modify (&catalog[0].header, "DATE", "%s", 1, line);
+  free (line);
+
+  /* dummy allocation so realloc will succeed */
+  ALLOCATE (catalog[0].average, Average, 1);
+  ALLOCATE (catalog[0].measure, Measure, 1);
+  ALLOCATE (catalog[0].missing, Missing, 1);
+  ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+
+  /* setup secondary filters to match photcodes:
+   * Nsecfilt is number of filters.  Number of entries in array is
+   * Nsecfilt * Naverage.  At this point, N entries == 0
+   */
+}
+  
+int dvo_catalog_set_range (Catalog *catalog) {
+
+  int i;
+  double Rmin, Rmax, Dmin, Dmax;
+
+  /* determine RA,DEC range */
+  Rmin = 360.0;
+  Rmax =   0.0;
+  Dmin = +90.0;
+  Dmax = -90.0;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Rmin = MIN (Rmin, catalog[0].average[i].R);
+    Rmax = MAX (Rmax, catalog[0].average[i].R);
+    Dmin = MIN (Dmin, catalog[0].average[i].D);
+    Dmax = MAX (Dmax, catalog[0].average[i].D);
+  }
+
+  /* write RA,DEC range in header */
+  gfits_modify (&catalog[0].header, "RA0",  "%lf", 1, Rmin);
+  gfits_modify (&catalog[0].header, "DEC0", "%lf", 1, Dmin);
+  gfits_modify (&catalog[0].header, "RA1",  "%lf", 1, Rmax);
+  gfits_modify (&catalog[0].header, "DEC1", "%lf", 1, Dmax);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_mef.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 17422)
@@ -0,0 +1,333 @@
+# include <dvo.h>
+
+int dvo_catalog_load_mef (Catalog *catalog, int VERBOSE) {
+  
+  int Nitems, Nbytes, Nexpect, Naverage, Nmeasure, Nmissing, Nsecfilt;
+  FILE *f;
+
+  Header header;
+  Matrix matrix;
+  FTable ftable;
+  SecFilt *primary;
+
+  f = catalog[0].f;
+  ftable.header = &header;
+
+  /* move pointer past header -- must be already read (load_catalog) */
+  fseek (f, catalog[0].header.size, SEEK_SET);
+
+  /* matrix should be empty */
+  if (!gfits_fread_matrix (f, &matrix, &catalog[0].header)) {
+    if (VERBOSE) fprintf (stderr, "can't read primary matrix");
+    return (FALSE);
+  }
+  /* get the components from the header */
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &Nsecfilt)) Nsecfilt = 0;
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = Naverage;
+  catalog[0].Nmeas_disk = Nmeasure;
+  catalog[0].Nmiss_disk = Nmissing;
+  catalog[0].Nsecf_disk = Naverage * Nsecfilt;
+
+  /**  Nsecfilt is unusual: it does not list the number of data items in the table
+       instead, the number of items is Nsecfilt * Naverage.  **/
+  catalog[0].Nsecfilt = Nsecfilt;
+
+  /* default values, but we will assign these a valid value before we exit (even if empty) */
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL;
+  catalog[0].missing = NULL;
+  catalog[0].secfilt = NULL;
+
+  /* validate table mode */
+
+  /* read Average table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table average header");
+    return (FALSE);
+  }
+  /* read Average table data (or skip) */
+  if (catalog[0].catflags & LOAD_AVES) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+    /* old versions of DVO stored one of the average magnitudes in Average. we save this if needed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naves_disk) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naves_disk);
+    }
+    catalog[0].Naverage = catalog[0].Naves_disk;
+    catalog[0].Naves_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].average, Average, 1);
+    catalog[0].Naverage = 0;
+    catalog[0].Naves_off = catalog[0].Naves_disk;
+  }
+  gfits_free_header (&header);
+  /** free the ftable or not? data is being used still..? **/
+
+  /* read Measure table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table measure header");
+    return (FALSE);
+  }
+  /* read Measure table data */
+  if (catalog[0].catflags & LOAD_MEAS) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+    catalog[0].measure = FtableToMeasure (&ftable, &catalog[0].Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != catalog[0].Nmeas_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeas_disk);
+    }
+    catalog[0].Nmeasure = catalog[0].Nmeas_disk;
+    catalog[0].Nmeas_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].measure, Measure, 1);
+    catalog[0].Nmeasure = 0;
+    catalog[0].Nmeas_off = catalog[0].Nmeas_disk;
+  }
+
+  /* read Missing table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table missing header");
+    return (FALSE);
+  }
+  /* read Missing table data */
+  if (catalog[0].catflags & LOAD_MISS) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data");
+      return (FALSE);
+    }
+    /* no conversions currently defined */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &catalog[0].Nmissing, NULL);
+    if (Nmissing != catalog[0].Nmiss_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmiss_disk);
+    }
+    catalog[0].Nmissing = catalog[0].Nmiss_disk;
+    catalog[0].Nmiss_off = 0;
+  } else {
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    ALLOCATE (catalog[0].missing, Missing, 1);
+    catalog[0].Nmissing = 0;
+    catalog[0].Nmiss_off = catalog[0].Nmiss_disk;
+  }
+
+  /* read secfilt table header */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read table secfilt header");
+    return (FALSE);
+  }
+  /* read secfilt table data */
+  if (catalog[0].catflags & LOAD_SECF) {
+    if (!gfits_fread_ftable_data (f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt data");
+      return (FALSE);
+    }
+
+    /* how many entries do we expect from the secfilt table? */
+    Nexpect = catalog[0].Nsecfilt * catalog[0].Naverage;
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nexpect != Nitems) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nexpect, Nitems);
+    }
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      int Ntmpfilt, Ntotal, i, j;
+      SecFilt *tmpfilt;
+      tmpfilt  = catalog[0].secfilt;
+      Ntmpfilt = catalog[0].Nsecfilt;
+      Nsecfilt = catalog[0].Nsecfilt + 1;
+      Ntotal = Nsecfilt * catalog[0].Naves_disk;
+      ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+      for (i = 0; i < catalog[0].Naves_disk; i++) {
+	catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+	for (j = 0; j < Ntmpfilt; j++) {
+	  catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+	}
+      }		
+      catalog[0].Nsecfilt = Nsecfilt;
+      catalog[0].Nsecf_disk = Ntotal;
+      free (primary);
+    } 
+    catalog[0].Nsecf_mem = catalog[0].Nsecf_disk;
+    catalog[0].Nsecf_off = 0;
+  } else {
+    /* no real need to skip the data array here... */
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    if (primary != NULL) {
+      free (primary);
+      catalog[0].Nsecfilt ++;
+      catalog[0].Nsecf_disk =  catalog[0].Nsecfilt * catalog[0].Naves_disk;
+    }
+    ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].Nsecf_off = catalog[0].Nsecf_disk;
+  }
+
+  return (TRUE);
+}
+
+/* XXX need to decompose the primary and secfilt entries for Elixir and Loneos */
+/* save_catalog_mef writes a complete new file from scratch */
+int dvo_catalog_save_mef (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  FILE *f;
+  Matrix matrix;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+  SecFilt *secfilt;
+  int i, j, Nsecfilt, Nallfilt, Ntotal;
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  /* for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary */
+  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR 
+      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
+    Nallfilt = catalog[0].Nsecfilt;
+    Nsecfilt = catalog[0].Nsecfilt - 1;
+    Ntotal = Nsecfilt * catalog[0].Naverage;
+    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+    ALLOCATE (secfilt, SecFilt, Ntotal);
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      primary[i] = catalog[0].secfilt[i*Nallfilt + 0];
+      for (j = 0; j < Nsecfilt; j++) {
+	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+      }
+    }		
+  } else {
+    primary = NULL;
+    secfilt = catalog[0].secfilt;
+    Nsecfilt = catalog[0].Nsecfilt;
+  }
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  f = catalog[0].f;
+  /* rewind file pointers and truncate */
+  fseek (f, 0, SEEK_SET);
+  ftruncate (fileno (catalog[0].f), 0);
+  ftable.header = &header;
+
+  /* write table PHU header */
+  if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+    fprintf (stderr, "can't write primary header");
+    goto failure;
+  }
+
+  /* this is probably a NOP, do I have to keep it in? */
+  gfits_create_matrix (&catalog[0].header, &matrix);
+  if (!gfits_fwrite_matrix  (catalog[0].f, &matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    goto failure;
+  }
+  gfits_free_matrix (&matrix);
+
+  /* write out Average table (convert to FITS table format) */
+  AverageToFtable (&ftable, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out Measure table (convert to FITS table format) */
+  MeasureToFtable (&ftable, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out Missing table (convert to FITS table format) */
+  gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* write out SecFilt table (convert to FITS table format) */
+  Nitems = catalog[0].Naverage * Nsecfilt;
+  SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
+  if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+    fprintf (stderr, "can't write table header");
+    goto failure;
+  }
+  if (!gfits_fwrite_table (catalog[0].f, &ftable)) {
+    fprintf (stderr, "can't write table data");
+    goto failure;
+  }
+  gfits_free_table (&ftable);
+  gfits_free_header (&header);
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/*
+  catalog data is:
+  header (bitpix == 8)
+  matrix (empty)
+  average header
+  average table
+  measure header
+  measure table
+  missing header
+  missing table
+  secfilt header
+  secfilt table
+*/
+   
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_raw.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 17422)
@@ -0,0 +1,602 @@
+# include <dvo.h>
+
+/* read data from raw-style catalog file; set data format values based on file */
+
+int dvo_catalog_load_raw (Catalog *catalog, int VERBOSE) {
+  
+  int Nitems, nitems;
+  int i, Nmeas, Nmiss, size;
+  int NewMeasure, Nskip;
+  int AverageSize, MeasureSize, MissingSize, SecFiltSize;
+  FILE *f;
+  struct stat filestatus;
+  char format[80], telescope[80];
+  SecFilt *primary;
+
+  f = catalog[0].f;
+
+  /* move pointer past header -- must be already read (load_catalog) */
+  fseek (f, catalog[0].header.size, SEEK_SET);
+
+  /* get the components from the header */
+  catalog[0].Naverage = catalog[0].Nmeasure = catalog[0].Nmissing = catalog[0].Nsecfilt = 0;
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &catalog[0].Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &catalog[0].Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &catalog[0].Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &catalog[0].Nsecfilt)) catalog[0].Nsecfilt = 0;
+
+  /* determine catalog format */
+  catalog[0].catformat = DVO_FORMAT_UNDEF;
+  if (gfits_scan (&catalog[0].header, "FORMAT",  "%s", 1, format)) {
+    catalog[0].catformat = dvo_catalog_catformat (format);
+    if (catalog[0].catformat != DVO_FORMAT_UNDEF) goto got_format;
+  }
+  /* special cases: old versions of the DB tables which were poorly identified */
+  if (gfits_scan (&catalog[0].header, "NEWMEAS",  "%t", 1, &NewMeasure)) {
+    catalog[0].catformat = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+    goto got_format;
+  }
+  if (gfits_scan (&catalog[0].header, "TELESCOP",  "%s", 1, telescope)) {
+    if (!strncmp (telescope, "LONEOS", strlen("LONEOS"))) {
+      catalog[0].catformat = DVO_FORMAT_LONEOS; // special case for LONEOS
+      goto got_format;
+    }
+    if (!strncmp (telescope, "1.3m McGraw-Hill", strlen("1.3m McGraw-Hill"))) {
+      catalog[0].catformat = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+      goto got_format;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "cannot determine catalog format\n");
+  return (FALSE);
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+  case DVO_FORMAT_##NAME: { \
+    AverageSize = sizeof(Average_##TYPE); \
+    MeasureSize = sizeof(Measure_##TYPE); \
+    SecFiltSize = sizeof(SecFilt_##TYPE); \
+    break; }
+
+got_format:
+  /* determine datatype sizes */
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_INTERNAL: {
+      AverageSize = sizeof(Average);
+      MeasureSize = sizeof(Measure);
+      SecFiltSize = sizeof(SecFilt);
+      break;
+    }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "programming error in phot_catalog_raw\n");
+      exit (2);
+  }
+# undef FORMAT_CASE
+
+  MissingSize = sizeof (Missing);
+
+  /* predicted file size - for double checking data validity */
+  size = catalog[0].header.size;
+  size += AverageSize * catalog[0].Naverage;
+  size += MeasureSize * catalog[0].Nmeasure;
+  size += MissingSize * catalog[0].Nmissing;
+  size += SecFiltSize * catalog[0].Nsecfilt * catalog[0].Naverage;
+
+  /* check that file size makes sense */
+  if (stat (catalog[0].filename, &filestatus) == -1) {
+    if (VERBOSE) fprintf (stderr, "failed to get status of catalog\n");
+    return (FALSE);
+  }
+  if (size > filestatus.st_size) {
+    if (VERBOSE) {
+      fprintf (stderr, "star catalog has inconsistent size\n");
+      fprintf (stderr, "average: %d = %d bytes\n", catalog[0].Naverage, catalog[0].Naverage*AverageSize);
+      fprintf (stderr, "measure: %d = %d bytes\n", catalog[0].Nmeasure, catalog[0].Nmeasure*MeasureSize);
+      fprintf (stderr, "missing: %d = %d bytes\n", catalog[0].Nmissing, catalog[0].Nmissing*MissingSize);
+      fprintf (stderr, "secfilt: %d = %d bytes\n", catalog[0].Nsecfilt, catalog[0].Nsecfilt*SecFiltSize*catalog[0].Naverage);
+      fprintf (stderr, "expect: %d, found: %d\n", size, (int)filestatus.st_size);
+    }
+    return (FALSE);
+  } 
+  if (size < filestatus.st_size) {
+    if (VERBOSE) fprintf (stderr, "warning: file larger than expected\n");
+  } 
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars yet in catalog %s\n", catalog[0].filename);
+    return (TRUE);
+  }
+
+  /* read and convert the averages (use a macro to clean this up?) */
+  /* old versions of DVO stored one of the average magnitudes in Average. we save this if needed */
+  if (catalog[0].catflags & LOAD_AVES) {
+    catalog[0].average = ReadRawAverage (catalog[0].f, catalog[0].Naverage, catalog[0].catformat, &primary);
+  } else {
+    /* skip over averages */
+    Nskip = catalog[0].Naverage * AverageSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }    
+  
+  /* read and convert the measures (use a macro to clean this up?) */
+  if (catalog[0].catflags & LOAD_MEAS) {
+    catalog[0].measure = ReadRawMeasure (catalog[0].f, catalog[0].Nmeasure, catalog[0].catformat);
+  } else {
+    /* skip over measures */
+    Nskip = catalog[0].Nmeasure * MeasureSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }    
+
+  /* read and convert missing */
+  if (catalog[0].catflags & LOAD_MISS) {
+    ALLOCATE (catalog[0].missing, Missing, MAX (catalog[0].Nmissing, 1));
+    Nitems = catalog[0].Nmissing;
+    nitems = fread (catalog[0].missing, MissingSize, Nitems, f);
+    if (nitems != Nitems) {
+      if (VERBOSE) fprintf (stderr, "failed to read missing from catalog file %s (%d vs %d)\n", catalog[0].filename, nitems, Nitems);
+      return (FALSE);
+    }
+    gfits_convert_Missing (catalog[0].missing, MissingSize, Nitems);
+  } else {
+    /* skip over missings */
+    Nskip = catalog[0].Nmissing * MissingSize;
+    fseek (f, Nskip, SEEK_CUR); 
+  }
+  
+  /* read and convert secfilt */
+  if (catalog[0].catflags & LOAD_SECF) {
+    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].secfilt = ReadRawSecFilt (catalog[0].f, Nitems, catalog[0].catformat);
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      int Ntmpfilt, Nsecfilt, Ntotal, i, j;
+      SecFilt *tmpfilt;
+      tmpfilt  = catalog[0].secfilt;
+      Ntmpfilt = catalog[0].Nsecfilt;
+      Nsecfilt = catalog[0].Nsecfilt + 1;
+      Ntotal = Nsecfilt * catalog[0].Naverage;
+      ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+      for (i = 0; i < catalog[0].Naverage; i++) {
+	catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+	for (j = 0; j < Ntmpfilt; j++) {
+	  catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+	}
+      }		
+      catalog[0].Nsecfilt = Nsecfilt;
+      catalog[0].Nsecf_mem = Ntotal;
+      free (primary);
+    } 
+
+  } else {
+    /* skip over secfilts */
+    Nskip = catalog[0].Nsecfilt * catalog[0].Naverage * SecFiltSize;
+    fseek (f, Nskip, SEEK_CUR); 
+    if (primary != NULL) free (primary);
+  }
+
+  if (VERBOSE) fprintf (stderr, "read %d stars from catalog file %s (%d measurements, %d missing, %d secondary filters)\n", 
+			catalog[0].Naverage, catalog[0].filename, catalog[0].Nmeasure, catalog[0].Nmissing, catalog[0].Nsecfilt);
+
+  /* check data integrity */
+  if (catalog[0].catflags & LOAD_AVES) {
+    for (i = Nmeas = Nmiss = 0; i < catalog[0].Naverage; i++) {
+      Nmeas += catalog[0].average[i].Nmeasure; 
+      Nmiss += catalog[0].average[i].Nmissing; 
+    }
+    if ((Nmeas != catalog[0].Nmeasure) || (Nmiss != catalog[0].Nmissing)) {
+      if (VERBOSE) {
+	fprintf (stderr, "****** data in catalog %s is corrupt, sums don't check\n", catalog[0].filename);
+	fprintf (stderr, "****** Nmeas: %d, %d\n", Nmeas, catalog[0].Nmeasure);
+	fprintf (stderr, "****** Nmiss: %d, %d\n", Nmiss, catalog[0].Nmissing);
+      }
+      return (FALSE);
+    }
+  }
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = catalog[0].Naverage;
+  catalog[0].Nmeas_disk = catalog[0].Nmeasure;
+  catalog[0].Nmiss_disk = catalog[0].Nmissing;
+
+  return (TRUE);
+}
+
+int dvo_catalog_save_raw (Catalog *catalog, char VERBOSE) {
+
+  int Nitems, nitems;
+  FILE *f;
+  SecFilt *primary;
+  SecFilt *secfilt;
+  int i, j, Nsecfilt, Nallfilt, Ntotal;
+
+  if (catalog[0].Naverage == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  /* for the appropriate types, pull out the first secfilt and pass to WriteRawAverage as primary */
+  if ((catalog[0].catformat == DVO_FORMAT_ELIXIR) || // special case for ELIXIR
+      (catalog[0].catformat == DVO_FORMAT_LONEOS)) { // special case for LONEOS
+    Nallfilt = catalog[0].Nsecfilt;
+    Nsecfilt = catalog[0].Nsecfilt - 1;
+    Ntotal = Nsecfilt * catalog[0].Naverage;
+    ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+    ALLOCATE (secfilt, SecFilt, Ntotal);
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      primary[i] = catalog[0].secfilt[i*Nallfilt + 0];
+      for (j = 0; j < Nsecfilt; j++) {
+	secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+      }
+    }		
+  } else {
+    primary = NULL;
+    secfilt = catalog[0].secfilt;
+    Nsecfilt = catalog[0].Nsecfilt;
+  }
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, catalog[0].Nsecfilt);
+
+  /* specify the appropriate data format */
+  if (catalog[0].catformat == DVO_FORMAT_INTERNAL)  	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "INTERNAL");
+  if (catalog[0].catformat == DVO_FORMAT_LONEOS)    	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "LONEOS");
+  if (catalog[0].catformat == DVO_FORMAT_ELIXIR)    	  gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "ELIXIR");
+  if (catalog[0].catformat == DVO_FORMAT_PANSTARRS_DEV_0) gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_0");
+  if (catalog[0].catformat == DVO_FORMAT_PANSTARRS_DEV_1) gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_1");
+  if (catalog[0].catformat == DVO_FORMAT_PS1_DEV_1)       gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PS1_DEV_1");
+  if (catalog[0].catformat == DVO_FORMAT_PS1_DEV_2)       gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PS1_DEV_2");
+
+  /* rewind file pointers and truncate file */
+  f = catalog[0].f;
+  fseek (f, 0, SEEK_SET);
+  ftruncate (fileno (catalog[0].f), 0);
+
+  /* write header data (use gfits_write_header?) */
+  nitems = fwrite (catalog[0].header.buffer, 1, catalog[0].header.size, f);
+  if (nitems != catalog[0].header.size) {
+    if (VERBOSE) fprintf (stderr, "failed to write header\n");
+    goto failure;
+  }
+
+  /* write averages and measures */
+  WriteRawAverage (f, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
+  WriteRawMeasure (f, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
+
+  /* write missing data */
+  Nitems = catalog[0].Nmissing;
+  gfits_convert_Missing (catalog[0].missing, sizeof(Missing), Nitems);
+  nitems = fwrite (catalog[0].missing, sizeof(Missing), Nitems, f);
+  if (nitems != Nitems) {
+    if (VERBOSE) fprintf (stderr, "failed to write catalog file missing %s\n", catalog[0].filename);
+    goto failure;
+  }
+
+  Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+  WriteRawSecFilt (f, catalog[0].secfilt, Nitems, catalog[0].catformat);
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/*** 
+     some warnings:
+     - need to be wary of exit condition errors
+***/
+
+/** Average / Raw Table conversions **/
+
+/* in the Elixir and Loneos cases, we are supplied with an average magnitude in Average
+   in these cases, save these values in primary; otherwise set primary to NULL */
+
+Average *ReadRawAverage (FILE *f, int Naverage, int format, SecFilt **primary) {
+
+  Average *average;
+
+  *primary = NULL;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Average_##TYPE *tmpAverage; \
+      ALLOCATE (tmpAverage, Average_##TYPE, MAX (Naverage, 1)); \
+      nitems = fread (tmpAverage, sizeof(Average_##TYPE), Naverage, f); \
+      if (nitems != Naverage) { \
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage); \
+	return (NULL); \
+      } \
+      gfits_convert_Average_##TYPE (tmpAverage, sizeof(Average_##TYPE), Naverage); \
+      average = Average_##TYPE##_ToInternal (tmpAverage, Naverage, primary); \
+      free (tmpAverage); \
+      break; } \
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (average, Average, MAX (Naverage, 1));
+      nitems = fread (average, sizeof(Average), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_Average (average, sizeof(Average), Naverage);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (average);
+}
+
+/* accepts and converts internal average formats and outputs 
+   raw data in the specified format */
+int WriteRawAverage (FILE *f, Average *average, int Naverage, int format, SecFilt *primary) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Average_##TYPE *tmpAverage; \
+      tmpAverage = AverageInternalTo_##TYPE (average, Naverage, primary); \
+      gfits_convert_Average_##TYPE (tmpAverage, sizeof(Average_##TYPE), Naverage); \
+      nitems = fwrite (tmpAverage, sizeof(Average_##TYPE), Naverage, f); \
+      free (tmpAverage); \
+      if (nitems != Naverage) { \
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_Average (average, sizeof(Average), Naverage);
+      nitems = fwrite (average, sizeof(Average), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing averages\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
+/** Average / Raw Table conversions **/
+
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format) {
+
+  Measure *measure;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Measure_##TYPE *tmpMeasure; \
+      ALLOCATE (tmpMeasure, Measure_##TYPE, MAX (Nmeasure, 1)); \
+      nitems = fread (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure, f); \
+      if (nitems != Nmeasure) { \
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure); \
+	return (NULL); \
+      } \
+      gfits_convert_Measure_##TYPE (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure); \
+      measure = Measure_##TYPE##_ToInternal (tmpMeasure, Nmeasure); \
+      free (tmpMeasure); \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (measure, Measure, MAX (Nmeasure, 1));
+      nitems = fread (measure, sizeof(Measure), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_Measure (measure, sizeof(Measure), Nmeasure);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (measure);
+}
+
+/* accepts and converts internal measure formats and outputs 
+   raw data in the specified format */
+int WriteRawMeasure (FILE *f, Measure *measure, int Nmeasure, int format) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      Measure_##TYPE *tmpMeasure; \
+      tmpMeasure = MeasureInternalTo_##TYPE (measure, Nmeasure); \
+      gfits_convert_Measure_##TYPE (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure); \
+      nitems = fwrite (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure, f); \
+      free (tmpMeasure); \
+      if (nitems != Nmeasure) { \
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_Measure (measure, sizeof(Measure), Nmeasure);
+      nitems = fwrite (measure, sizeof(Measure), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing measures\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
+/** SecFilt / Raw Table conversions **/
+
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format) {
+
+  SecFilt *secfilt;
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      SecFilt_##TYPE *tmpSecFilt; \
+      ALLOCATE (tmpSecFilt, SecFilt_##TYPE, MAX (Nsecfilt, 1)); \
+      nitems = fread (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt, f); \
+      if (nitems != Nsecfilt) { \
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt); \
+	return (NULL); \
+      } \
+      gfits_convert_SecFilt_##TYPE (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt); \
+      secfilt = SecFilt_##TYPE##_ToInternal (tmpSecFilt, Nsecfilt); \
+      free (tmpSecFilt); \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      ALLOCATE (secfilt, SecFilt, MAX (Nsecfilt, 1));
+      nitems = fread (secfilt, sizeof(SecFilt), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFilt (secfilt, sizeof(SecFilt), Nsecfilt);
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+# undef FORMAT_CASE
+
+  return (secfilt);
+}
+
+/* accepts and converts internal secfilt formats and outputs 
+   raw data in the specified format */
+int WriteRawSecFilt (FILE *f, SecFilt *secfilt, int Nsecfilt, int format) {
+
+// this macro generates the case statements for each type
+# define FORMAT_CASE(NAME,TYPE) \
+    case DVO_FORMAT_##NAME: { \
+      int nitems; \
+      SecFilt_##TYPE *tmpSecFilt; \
+      tmpSecFilt = SecFiltInternalTo_##TYPE (secfilt, Nsecfilt); \
+      gfits_convert_SecFilt_##TYPE (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt); \
+      nitems = fwrite (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt, f); \
+      free (tmpSecFilt); \
+      if (nitems != Nsecfilt) { \
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt); \
+	return (FALSE); \
+      } \
+      break; }
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      int nitems;
+      gfits_convert_SecFilt (secfilt, sizeof(SecFilt), Nsecfilt);
+      nitems = fwrite (secfilt, sizeof(SecFilt), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+
+      FORMAT_CASE (LONEOS, Loneos);
+      FORMAT_CASE (ELIXIR, Elixir);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1, PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2, PS1_DEV_2);
+
+    default:
+      fprintf (stderr, "error writing secfilts\n");
+      return (FALSE);
+  }
+# undef FORMAT_CASE
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_split.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 17422)
@@ -0,0 +1,1049 @@
+# include <dvo.h>
+
+// return options: 
+// * error (cannot lock, open, read, etc)
+// * empty (file is not found)
+// * ok
+
+int dvo_catalog_secfilt_to_primary (Catalog *catalog, SecFilt **myPrimary, SecFilt **mySecfilt, int *myNsecfilt) {
+
+  int i, j, Nallfilt, Nsecfilt, Ntotal;
+
+  SecFilt *primary;
+  SecFilt *secfilt;
+
+  if (catalog[0].secfilt == NULL) {       
+    fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c)\n");
+    exit (1);
+  }
+  secfilt = catalog[0].secfilt;
+
+  // XXX this translation only works if we have loaded / created a matched average/secfilt set
+  assert (catalog[0].Nsecf_mem == catalog[0].Nsecfilt*catalog[0].Naverage);
+
+  Nallfilt = catalog[0].Nsecfilt;
+  Nsecfilt = catalog[0].Nsecfilt - 1;
+  Ntotal = Nsecfilt * catalog[0].Naverage;
+  ALLOCATE (primary, SecFilt, catalog[0].Naverage);
+  ALLOCATE (secfilt, SecFilt, Ntotal);
+
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    primary[i] = secfilt[i*Nallfilt + 0];
+    for (j = 0; j < Nsecfilt; j++) {
+      secfilt[i*Nsecfilt + j] = catalog[0].secfilt[i*Nallfilt + j + 1];
+    }
+  }		
+  catalog[0].Nsecfilt --;
+  catalog[0].Nsecf_mem = catalog[0].Naverage*catalog[0].Nsecfilt;
+
+  *myPrimary = primary;
+  *mySecfilt = secfilt;
+  *myNsecfilt = Nsecfilt;
+  return (TRUE);
+}
+
+int dvo_catalog_primary_to_secfilt (Catalog *catalog, SecFilt *primary, int Naves) {
+
+  int Ntmpfilt, Nsecfilt, Ntotal, i, j;
+  SecFilt *tmpfilt;
+
+  tmpfilt  = catalog[0].secfilt;
+  Ntmpfilt = catalog[0].Nsecfilt;
+
+  // we do NOT modify Nsecf_disk; this operation only modifies in in-memory values
+
+  catalog[0].Nsecfilt ++;
+  Nsecfilt = catalog[0].Nsecfilt;
+  Ntotal = Nsecfilt * Naves;
+
+  catalog[0].Nsecf_mem = Ntotal;
+
+  ALLOCATE (catalog[0].secfilt, SecFilt, Ntotal);
+  for (i = 0; i < Naves; i++) {
+    catalog[0].secfilt[i*Nsecfilt + 0] = primary[i];
+    for (j = 0; j < Ntmpfilt; j++) {
+      catalog[0].secfilt[i*Nsecfilt + j + 1] = tmpfilt[i*Ntmpfilt + j];
+    }
+  }		
+  free (tmpfilt);
+  free (primary);
+  return (TRUE);
+}
+
+int dvo_catalog_save_subcat (Catalog *catalog, FTable *ftable, int start, int Nrows, int Ndisk, int Ntotal) {
+
+  Matrix matrix;
+
+  /* rewind file pointers and truncate (file is still open) */
+  fseek (catalog->f, 0, SEEK_SET);
+
+  // write PHU header
+  if (!gfits_fwrite_header  (catalog->f, &catalog->header)) {
+    fprintf (stderr, "can't write primary header");
+    return (FALSE);
+  }
+
+  // write the PHU matrix; this is probably a NOP, do I have to keep it in?
+  gfits_create_matrix (&catalog->header, &matrix);
+  if (!gfits_fwrite_matrix  (catalog->f, &matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    gfits_free_matrix (&matrix);
+    return (FALSE);
+  }
+  gfits_free_matrix (&matrix);
+
+  // write the table data
+  if (!gfits_fwrite_ftable_range (catalog->f, ftable, start, Nrows, Ndisk, Ntotal)) {
+    fprintf (stderr, "can't write table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int dvo_catalog_open_subcat (Catalog *catalog, Catalog **Subcat, Header *header, char *name, int VERBOSE) {
+
+  int Nskip, status;
+  char *path, string[80];
+  Catalog *subcat;
+
+  /* in split mode, we need to init & open the corresponding measure file (even if we do not read
+   * any data in at this stage) */
+  ALLOCATE (subcat, Catalog, 1);
+  dvo_catalog_init (subcat, TRUE);
+
+  *Subcat = subcat;
+
+  /* needed to find the split files below */
+  path = pathname (catalog[0].filename);
+
+  /* get split filename from main header (paths relative to cpt file) */
+  if (!gfits_scan (&catalog[0].header, name,  "%s", 1, string)) {
+    free (path);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  ALLOCATE (subcat[0].filename, char, strlen(path) + strlen(string) + 2);
+  sprintf (subcat[0].filename, "%s/%s", path, string);
+  free (path);
+
+  /* lock & open catalog file */
+  status = dvo_catalog_lock (subcat, catalog[0].lockmode);
+  if (status != DVO_CAT_OPEN_OK) {
+    if (VERBOSE) {
+      if (status == DVO_CAT_OPEN_EMPTY) {
+	fprintf (stderr, "%s (%s) is empty\n", name, subcat[0].filename);
+      } else {
+	fprintf (stderr, "failure to lock %s (%s)\n", name, subcat[0].filename);
+      }
+    }
+    return (status);
+  }
+
+  /* read PHU */
+  if (!gfits_load_header (subcat[0].f, &subcat[0].header)) {
+    if (VERBOSE) fprintf (stderr, "error reading %s header: %s\n", name, subcat[0].filename);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  Nskip = gfits_data_size (&subcat[0].header);
+  fseek (subcat[0].f, Nskip, SEEK_CUR);
+
+  /* read Measure table header */
+  if (!gfits_fread_header (subcat[0].f, header)) {
+    if (VERBOSE) fprintf (stderr, "can't read %s PHU header\n", name);
+    gfits_free_header (&subcat[0].header);
+    return (DVO_CAT_OPEN_FAIL);
+  }
+  return (DVO_CAT_OPEN_OK);
+}
+
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE) {
+
+  int Nbytes, Nitems, Naverage, Nmeasure, Nmissing, Nsecfilt, status;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+
+  /* ftable header storage for below */
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+
+  /* get the components from the header - these duplicate information in the split files (NAXIS2) */
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%d", 1, &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%d", 1, &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    "%d", 1, &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d", 1, &Nsecfilt)) Nsecfilt = 0;
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Naves_disk = Naverage;
+  catalog[0].Nmeas_disk = Nmeasure;
+  catalog[0].Nmiss_disk = Nmissing;
+  catalog[0].Nsecf_disk = Naverage * Nsecfilt;
+
+  /**  Nsecfilt is unusual: it does not list the number of data items in the table
+       instead, the number of items is Nsecfilt * Naverage.  **/
+  catalog[0].Nsecfilt  = Nsecfilt;
+
+  /* default values, but we will assign these a valid value before we exit (even if empty) */
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL;
+  catalog[0].missing = NULL;
+  catalog[0].secfilt = NULL;
+
+  /*** Average Table ***/
+  if (catalog[0].catflags & LOAD_AVES) {
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = catalog[0].header.size + gfits_data_size (&catalog[0].header);
+    fseek (catalog[0].f, Nbytes, SEEK_SET);
+    /* read Average table header */
+    if (!gfits_fread_header (catalog[0].f, &header)) { 
+      if (VERBOSE) fprintf (stderr, "can't read table average header");
+      return (FALSE);
+    }
+    /* read Average table data : format is irrelevant here */
+    if (!gfits_fread_ftable_data (catalog[0].f, &ftable)) { 
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+    /* convert the saved version of the table to the internal version.  Old versions of DVO stored
+     * one of the average magnitudes in Average.  We save this in case it is needed below.  NOTE:
+     * primary is only used if we read in the secfilt table, otherwise it should be freed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naves_disk) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naves_disk);
+    }
+    gfits_free_header (&header);
+    catalog[0].Naverage = catalog[0].Naves_disk;
+    catalog[0].Naves_off = 0;
+  } else {
+    ALLOCATE (catalog[0].average, Average, 1);
+    catalog[0].Naverage = 0;
+    catalog[0].Naves_off = catalog[0].Naves_disk;
+  }
+
+  /*** Measure Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].measure_catalog, ftable.header, "MEASURE", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nmeas_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_MEAS)) {
+    // XXX this allows an empty Measure catalog with non-empty Average catalog : is that OK?
+    /* read Measure table data */
+    if (!gfits_fread_ftable_data (catalog[0].measure_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data\n");
+      return (FALSE);
+    }
+    /* convert data format to internal : returns number of row read in Nmeasure */
+    catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != catalog[0].Nmeas_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeas_disk);
+    }
+    catalog[0].Nmeasure = catalog[0].Nmeas_disk;
+    catalog[0].Nmeas_off = 0;
+  } else {
+    // XXX is it necessary to generate a template header here?
+    gfits_create_header (&catalog[0].measure_catalog[0].header);
+    ALLOCATE (catalog[0].measure, Measure, 1);
+    catalog[0].Nmeasure = 0;
+    catalog[0].Nmeas_off = catalog[0].Nmeas_disk;
+  }
+  gfits_free_header (&header);
+
+  /*** Missing Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].missing_catalog, ftable.header, "MISSING", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nmiss_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_MISS)) {
+    /* read Missing table data */
+    if (!gfits_fread_ftable_data (catalog[0].missing_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data\n");
+      return (FALSE);
+    }
+    /* no conversions currently defined : this just does the byte swap */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
+    if (Nmissing != catalog[0].Nmiss_disk) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmiss_disk);
+    }
+    catalog[0].Nmissing = catalog[0].Nmiss_disk;
+    catalog[0].Nmiss_off = 0;
+  } else {
+    // XXX is it necessary to generate a template header here?
+    gfits_create_header (&catalog[0].missing_catalog[0].header);
+    ALLOCATE (catalog[0].missing, Missing, 1);
+    catalog[0].Nmissing = 0;
+    catalog[0].Nmiss_off = catalog[0].Nmiss_disk;
+  }
+  gfits_free_header (ftable.header);
+
+  /*** Secfilt Table ***/
+  status = dvo_catalog_open_subcat (catalog, &catalog[0].secfilt_catalog, ftable.header, "SECFILT", VERBOSE);
+  if (status == DVO_CAT_OPEN_FAIL) {
+    return (FALSE);
+  }
+  if ((status == DVO_CAT_OPEN_EMPTY) && (catalog[0].Nsecf_disk > 0)) {
+    return (FALSE);
+  }
+  if ((status != DVO_CAT_OPEN_EMPTY) && (catalog[0].catflags & LOAD_SECF)) {
+    /* read secfilt table data */
+    if (!gfits_fread_ftable_data (catalog[0].secfilt_catalog[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt data\n");
+      return (FALSE);
+    }
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nitems != catalog[0].Nsecf_disk) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nitems, catalog[0].Nsecf_disk);
+    }
+    catalog[0].Nsecf_mem = catalog[0].Nsecf_disk;
+    catalog[0].Nsecf_off = 0;
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      // this modifies catalog.Nsecf_mem,Nsecfilt
+      dvo_catalog_primary_to_secfilt (catalog, primary, catalog[0].Naves_disk);
+    } 
+  } else {
+    if (primary != NULL) {
+      free (primary);
+      catalog[0].Nsecfilt ++;
+    }
+    gfits_create_header (&catalog[0].secfilt_catalog[0].header);
+    ALLOCATE (catalog[0].secfilt, SecFilt, 1);
+    catalog[0].Nsecf_mem = 0;
+    catalog[0].Nsecf_off = catalog[0].Nsecf_disk;
+  }
+  gfits_free_header (ftable.header);
+
+  return (TRUE);
+}
+
+// I need to always read both average and secfilt at the same time to correctly manage the
+// primary secfilt values...
+int dvo_catalog_load_segment_split (Catalog *catalog, int VERBOSE, int start, int Nrows) {
+
+  int Nbytes, Naverage, Nexpect, Nitems, Nmeasure, Nmissing;
+  Header header;
+  FTable ftable;
+  SecFilt *primary;
+
+  /* ftable header storage for below */
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+
+  /*** Average (& SecFilt) Table ***/
+  if (catalog[0].catflags & LOAD_AVES) {
+
+    /*** load the Average data ***/
+    /* move pointer past header and matrix -- must be already read (load_catalog) */
+    Nbytes = catalog[0].header.size + gfits_data_size (&catalog[0].header);
+    fseek (catalog[0].f, Nbytes, SEEK_SET);
+    /* read Average table header */
+    if (!gfits_fread_header (catalog[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average header");
+      return (FALSE);
+    }
+    /* read Average table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (catalog[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table average data");
+      return (FALSE);
+    }
+
+    /* convert the saved version of the table to the internal version.  Old versions of DVO stored
+     * one of the average magnitudes in Average.  We save this in case it is needed below.  NOTE:
+     * primary is only used if we read in the secfilt table, otherwise it should be freed */
+    catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != Nrows) {
+      // XXX this condition denotes the eof has been reached; not an error or a warning
+      // fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Naverage = Naverage;
+    catalog[0].Naves_off = start;
+
+    /*** load the secfilt data ***/
+    Catalog *subcat = catalog[0].secfilt_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Secfilt table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure header");
+      return (FALSE);
+    }
+    /* read Secfilt table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start*catalog[0].Nsecfilt, catalog[0].Naverage*catalog[0].Nsecfilt)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+
+    Nexpect = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
+    if (Nitems != Nexpect) {
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%d vs %d)\n", Nitems, Nexpect);
+    }
+    catalog[0].Nsecf_mem = catalog[0].Naverage * catalog[0].Nsecfilt;
+    catalog[0].Nsecf_off = start               * catalog[0].Nsecfilt;
+
+    /* if primary is defined, we were supplied with one additional average magnitude from Average
+       we need to interleave these magnitudes with the secfilt entries just loaded */
+    if (primary != NULL) {
+      dvo_catalog_primary_to_secfilt (catalog, primary, Nrows);
+    } 
+    gfits_free_header (&header);
+  }
+
+  // XXX check the open status of the catalog
+  if (catalog[0].catflags & LOAD_MEAS) {
+
+    Catalog *subcat = catalog[0].measure_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Measure table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure header");
+      return (FALSE);
+    }
+    /* read Measure table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data");
+      return (FALSE);
+    }
+
+    /* convert data format to internal : returns number of row read in Nmeasure */
+    catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != Nrows) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Nmeasure = Nmeasure;
+    catalog[0].Nmeas_off = start;
+  }
+
+  // XXX check the open status of the catalog?
+  if (catalog[0].catflags & LOAD_MISS) {
+
+    Catalog *subcat = catalog[0].missing_catalog;
+
+    /* move pointer past header -- must be already read (load_catalog) */
+    Nbytes = subcat[0].header.size + gfits_data_size (&subcat[0].header);
+    fseek (subcat[0].f, Nbytes, SEEK_SET);
+
+    /* read Missing table header */
+    if (!gfits_fread_header (subcat[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing header");
+      return (FALSE);
+    }
+    /* read Missing table data : format is irrelevant here */
+    if (!gfits_fread_ftable_range (subcat[0].f, &ftable, start, Nrows)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data");
+      return (FALSE);
+    }
+
+    /* no conversions currently defined : this just does the byte swap */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
+    if (Nmissing != Nrows) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, Nrows);
+    }
+    gfits_free_header (&header);
+    catalog[0].Nmissing = Nmissing;
+    catalog[0].Nmiss_off = start;
+  }
+  return (TRUE);
+}
+
+/* save_catalog_split writes all data currently in memory to disk */
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+  
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  Naves_disk_new = MAX (catalog[0].Naves_disk, catalog[0].Naverage + catalog[0].Naves_off);
+  if (Naves_disk_new == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = MAX (catalog[0].Nmeas_disk, catalog[0].Nmeasure + catalog[0].Nmeas_off);
+  Nmiss_disk_new = MAX (catalog[0].Nmiss_disk, catalog[0].Nmissing + catalog[0].Nmiss_off);
+  Nsecf_disk_new = MAX (catalog[0].Nsecf_disk, catalog[0].Naverage*Nsecfilt + catalog[0].Nsecf_off);
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if ((catalog[0].catflags & LOAD_AVES) && (catalog[0].average != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Naves_off; // first disk row to write
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if ((catalog[0].catflags & LOAD_MEAS) && (catalog[0].measure != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nmeas_off; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Missing Table ***/
+  if ((catalog[0].catflags & LOAD_MISS) && (catalog[0].missing != NULL)) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if ((catalog[0].catflags & LOAD_SECF) && (catalog[0].secfilt != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nsecf_off; // first disk row to write
+    Nitems = catalog[0].Nsecf_mem;
+    Nrows  = Nitems - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= Nitems);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+    // XXX check these for consistency...
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  gfits_free_header (&header);
+  gfits_free_table (&ftable);
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* save_catalog_split writes all data currently in memory to disk, truncating if the disk file was larger */
+int dvo_catalog_save_split_complete (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+  primary = NULL;
+  
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  if (catalog[0].Naves_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Average) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nmeas_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Measure) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nmiss_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Missing) was loaded\n");
+    goto failure;
+  }
+  if (catalog[0].Nsecf_off > 0) {
+    fprintf (stderr, "ERROR: only partial catalog (Secfilt) was loaded\n");
+    goto failure;
+  }
+
+  if ((catalog[0].Naverage > 0) && (Naves_disk_new == 0)) {
+    Naves_disk_new = catalog[0].Naverage;
+    if (VERBOSE) fprintf (stderr, "resulting catalog is empty; delete it\n");
+    // unlink ();
+    return (TRUE);
+  }
+
+  Naves_disk_new = catalog[0].Naverage;
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = catalog[0].Nmeasure;
+  Nmiss_disk_new = catalog[0].Nmissing;
+  Nsecf_disk_new = catalog[0].Naverage*Nsecfilt;
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if ((catalog[0].catflags & LOAD_AVES) && (catalog[0].average != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Naves_off; // first disk row to write
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if ((catalog[0].catflags & LOAD_MEAS) && (catalog[0].measure != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nmeas_off; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Missing Table ***/
+  if ((catalog[0].catflags & LOAD_MISS) && (catalog[0].missing != NULL)) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if ((catalog[0].catflags & LOAD_SECF) && (catalog[0].secfilt != NULL)) {
+
+    first  = 0;                    // first row in memory to write
+    start  = catalog[0].Nsecf_off; // first disk row to write
+    Nitems = catalog[0].Nsecf_mem;
+    Nrows  = Nitems - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= Nitems);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+    // XXX check these for consistency...
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  gfits_free_header (&header);
+  gfits_free_table (&ftable);
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* update_catalog_split only writes new lines to file. */
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE) {
+
+  Header header;
+  FTable ftable;
+  SecFilt *primary, *secfilt;
+  int Nsecfilt;
+  int Naves_disk_new, Nmeas_disk_new, Nmiss_disk_new, Nsecf_disk_new;
+  int first, start, Nrows;
+
+  ftable.header = &header;
+  ftable.buffer = NULL;
+  header.buffer = NULL;
+
+  // skip empty catalogs: it is illegal to have Measures without corresponding Averages
+  Naves_disk_new = MAX (catalog[0].Naves_disk, catalog[0].Naverage + catalog[0].Naves_off);
+  if (Naves_disk_new == 0) {
+    if (VERBOSE) fprintf (stderr, "no stars in catalog, skipping\n");
+    return (TRUE);
+  }
+
+  // for the appropriate types, pull out the first secfilt and pass to AverageToFtable as primary 
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_ELIXIR: // special case for ELIXIR
+    case DVO_FORMAT_LONEOS: // special case for LONEOS
+      dvo_catalog_secfilt_to_primary (catalog, &primary, &secfilt, &Nsecfilt);
+      break;
+    default:
+      primary = NULL;
+      secfilt = catalog[0].secfilt;
+      Nsecfilt = catalog[0].Nsecfilt;
+      break;
+  }
+
+  Nmeas_disk_new = MAX (catalog[0].Nmeas_disk, catalog[0].Nmeasure + catalog[0].Nmeas_off);
+  Nmiss_disk_new = MAX (catalog[0].Nmiss_disk, catalog[0].Nmissing + catalog[0].Nmiss_off);
+  Nsecf_disk_new = MAX (catalog[0].Nsecf_disk, catalog[0].Naverage*Nsecfilt + catalog[0].Nsecf_off);
+
+  /* make sure header is consistent with data */
+  gfits_modify (&catalog[0].header, "NSTARS",   "%d", 1, Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    "%d", 1, Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    "%d", 1, Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
+  gfits_modify (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+  if (catalog[0].average != NULL) {
+
+    first  = catalog[0].Naves_disk - catalog[0].Naves_off; // first row to write (memory)
+    start  = catalog[0].Naves_disk;                        // first row to write (disk)
+    Nrows  = catalog[0].Naverage - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Naverage);
+    assert (catalog[0].Naves_disk >= catalog[0].Naves_off);
+
+    /* convert internal to external format */
+    if (!AverageToFtable (&ftable, &catalog[0].average[first], Nrows, catalog[0].catformat, primary)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    if (!dvo_catalog_save_subcat (catalog, &ftable, start, Nrows, catalog[0].Naves_disk, Naves_disk_new)) {
+      fprintf (stderr, "failure writing Average table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  } else {
+    // even if we do not save the average table, we need to keep the header in sync
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catalog[0].f, 0, SEEK_SET);
+
+    /* write table PHU header - always write this out */
+    /* XXX EAM : check if disk file size has changed */
+    if (!gfits_fwrite_header  (catalog[0].f, &catalog[0].header)) {
+      fprintf (stderr, "can't write primary header");
+      goto failure;
+    }
+  }
+
+  /*** Measure Table ***/
+  if (catalog[0].measure != NULL) {
+
+    first  = catalog[0].Nmeas_disk - catalog[0].Nmeas_off;  // first row in memory to write
+    start  = catalog[0].Nmeas_disk; // first disk row to write
+    Nrows  = catalog[0].Nmeasure - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (first <= catalog[0].Nmeasure);
+    assert (catalog[0].Nmeas_disk >= catalog[0].Nmeas_off);
+
+    // convert to external table format
+    if (!MeasureToFtable (&ftable, &catalog[0].measure[first], Nrows, catalog[0].catformat)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Measure table
+    if (!dvo_catalog_save_subcat (catalog[0].measure_catalog, &ftable, start, Nrows, catalog[0].Nmeas_disk, Nmeas_disk_new)) {
+      fprintf (stderr, "trouble writing Measure table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* missing table CANNOT be written unsorted, thus it is always written 
+     out in full */
+
+  /*** Missing Table ***/
+
+  if (catalog[0].missing != NULL) {
+
+    if (catalog[0].Nmiss_off != 0) {
+      fprintf (stderr, "inconsistency: Missing table cannot be written in segments\n");
+      goto failure;
+    }
+
+    // convert to external table format
+    if (!gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble converting format\n");
+      goto failure;
+    }
+
+    // write out Missing table (must write out entire table)
+    if (!dvo_catalog_save_subcat (catalog[0].missing_catalog, &ftable, 0, catalog[0].Nmissing, catalog[0].Nmissing, catalog[0].Nmissing)) {
+      fprintf (stderr, "trouble writing Missing Table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /*** Secfilt Table ***/
+  if (catalog[0].secfilt != NULL) {
+
+    first  = catalog[0].Nsecf_disk - catalog[0].Nsecf_off;  // first row in memory to write
+    start  = catalog[0].Nsecf_disk; // first disk row to write
+    Nrows  = catalog[0].Nsecf_mem - first;
+
+    assert (Nrows >= 0);
+    assert (first >= 0);
+    assert (catalog[0].Nsecf_disk >= catalog[0].Nsecf_off);
+
+    // convert to external table format
+    SecFiltToFtable (&ftable, &secfilt[first], Nrows, catalog[0].catformat);
+
+    // write out SecFilt table
+    if (!dvo_catalog_save_subcat (catalog[0].secfilt_catalog, &ftable, start, Nrows, catalog[0].Nsecf_disk, Nsecf_disk_new)) {
+      fprintf (stderr, "failure writing SecFilt table\n");
+      goto failure;
+    }
+    gfits_free_header (&header);
+    gfits_free_table (&ftable);
+  }
+
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+
+  return (TRUE);
+
+failure:
+  /* free temp storage */
+  if (primary != NULL) {
+    free (primary);
+    free (secfilt);
+  }
+  return (FALSE);
+}
+
+/* in split mode, extra files are linked to catalog->measure_catalog, etc.  Each
+   has a valid filename, f, header.  The primary catalog data pointers are set
+   to point at the corresponding entries from the elements on the chain. An
+   unloaded entry has a null pointer here.
+*/
+
+/* XXX EAM : this file needs work on the error exit conditions and memory leaks, esp under errors */
+
+/* XXX EAM : update is not efficient.  MeasureToFtable should only 
+   convert the new rows (Nmeas_disk to Nmeasure). the resulting
+   table represents the end rows of the ftable.  we need to define
+   the vtable based on the ftable, but with Ny = Nmeasure */  
+  
+
+
+    // * convert to an ftable
+    // * optionally write the PHU header/matrix
+    // * advance to the start of the output data block:
+    // ** Nx * catalog[0].Nmeas_off
+    // * write out the ftable data block
+    // * if Nmeas_off + Nmeasure >= Nmeas_disk, update padding
+    // ** start = Nmeas_off
+    // ** Nrows = Nmeasure
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert.c	(revision 17422)
@@ -0,0 +1,380 @@
+# include <dvo.h>
+
+/* The Ftable-TYPE conversion functions determine the format of table based on EXTNAME in header.
+   they convert the table to the internal format, and set 'format'.  
+
+   The TYPE-Ftable conversions functions create output tables in the format requested
+   by the 'format' function parameter.
+*/
+
+/** this file might be more readable if I use macros for the repetative
+    constructions below **/
+
+/*** Average / FTable conversion functions ***/
+
+Average *FtableToAverage (FTable *ftable, int *Naverage, int *format, SecFilt **primary) {
+
+  Average *average;
+  char extname[80];
+
+  /* in the Elixir and Loneos cases, we are supplied with an average magnitude in Average
+     in these cases, save these values in primary; otherwise set primary to NULL */
+  *primary = NULL;
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for average table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Average_##TYPE *tmpAverage; \
+    tmpAverage = gfits_table_get_Average_##TYPE (ftable, Naverage, NULL); \
+    average = Average_##TYPE##_ToInternal (tmpAverage, *Naverage, primary); \
+    free (tmpAverage); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (average); }
+
+  if (!strcmp (extname, "DVO_AVERAGE")) {
+    average = gfits_table_get_Average (ftable, Naverage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (average);
+  }
+
+  CONVERT_FORMAT ("DVO_AVERAGE_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_AVERAGE_LONEOS", 	 LONEOS, 	  Loneos);
+  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Naverage = 0;
+  return (NULL);
+}
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format, SecFilt *primary) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Average_##TYPE *tmpAverage; \
+      tmpAverage = AverageInternalTo_##TYPE (average, Naverage, primary); \
+      gfits_table_set_Average_##TYPE (ftable, tmpAverage, Naverage); \
+      free (tmpAverage); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Average (ftable, average, Naverage);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (average)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** Measure / FTable conversion functions ***/
+
+Measure *FtableToMeasure (FTable *ftable, int *Nmeasure, int *format) {
+
+  Measure *measure;
+  char extname[80];
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for measure table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Measure_##TYPE *tmpMeasure; \
+    tmpMeasure = gfits_table_get_Measure_##TYPE (ftable, Nmeasure, NULL); \
+    measure = Measure_##TYPE##_ToInternal (tmpMeasure, *Nmeasure); \
+    free (tmpMeasure); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (measure); }
+
+  if (!strcmp (extname, "DVO_MEASURE")) {
+    measure = gfits_table_get_Measure (ftable, Nmeasure, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (measure);
+  }
+
+  CONVERT_FORMAT ("DVO_MEASURE_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_MEASURE_LONEOS", 	 LONEOS,          Loneos);
+  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nmeasure = 0;
+  return (NULL);
+}
+
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Measure_##TYPE *tmpMeasure; \
+      tmpMeasure = MeasureInternalTo_##TYPE (measure, Nmeasure); \
+      gfits_table_set_Measure_##TYPE (ftable, tmpMeasure, Nmeasure); \
+      free (tmpMeasure); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Measure (ftable, measure, Nmeasure);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (measure)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** SecFilt / FTable conversion functions ***/
+
+SecFilt *FtableToSecFilt (FTable *ftable, int *Nsecfilt, int *format) {
+
+  SecFilt *secfilt;
+  char extname[80];
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for secfilt table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    SecFilt_##TYPE *tmpSecFilt; \
+    tmpSecFilt = gfits_table_get_SecFilt_##TYPE (ftable, Nsecfilt, NULL); \
+    secfilt = SecFilt_##TYPE##_ToInternal (tmpSecFilt, *Nsecfilt); \
+    free (tmpSecFilt); \
+    *format = DVO_FORMAT_##FORMAT; \
+    return (secfilt); }
+
+  if (!strcmp (extname, "DVO_SECFILT")) {
+    secfilt = gfits_table_get_SecFilt (ftable, Nsecfilt, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (secfilt);
+  }
+
+  CONVERT_FORMAT ("DVO_SECFILT_ELIXIR", 	 ELIXIR, 	  Elixir);
+  CONVERT_FORMAT ("DVO_SECFILT_LONEOS", 	 LONEOS, 	  Loneos);
+  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nsecfilt = 0;
+  return (NULL);
+}
+
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format) {
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      SecFilt_##TYPE *tmpSecFilt; \
+      tmpSecFilt = SecFiltInternalTo_##TYPE (secfilt, Nsecfilt); \
+      gfits_table_set_SecFilt_##TYPE (ftable, tmpSecFilt, Nsecfilt); \
+      free (tmpSecFilt); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_SecFilt (ftable, secfilt, Nsecfilt);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (secfilt)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*** Image Conversions ***/
+
+int FtableToImage (FTable *ftable, Header *theader, int *format) {
+
+  int Nimage;
+  char extname[80];
+
+  /* extname may be set from outside if the source is RAW not MEF */
+  if (*format == DVO_FORMAT_ELIXIR) {		  // special case for ELIXIR
+    Image_Elixir *tmpimage;
+    tmpimage = gfits_table_get_Image_Elixir (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) Image_Elixir_ToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    gfits_free_header (theader);
+    gfits_table_mkheader_Image (theader);
+    gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+    theader[0].Naxis[1] = Nimage;
+    ftable[0].size = gfits_data_size (theader);
+    return (TRUE);
+  }
+
+  /* convert to the internal format */
+  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
+    fprintf (stderr, "EXTNAME missing for image table\n");
+    return (FALSE);
+  }
+
+# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
+  if (!strcmp (extname, NAME)) { \
+    Image_##TYPE *tmpimage; \
+    *format = DVO_FORMAT_##FORMAT; \
+    tmpimage = gfits_table_get_Image_##TYPE (ftable, &Nimage, NULL); \
+    ftable[0].buffer = (char *) Image_##TYPE##_ToInternal (tmpimage, Nimage); \
+    free (tmpimage); \
+    gfits_free_header (theader); \
+    gfits_table_mkheader_Image (theader); \
+    gfits_modify (theader, "NAXIS2", "%d", 1, Nimage); \
+    theader[0].Naxis[1] = Nimage; \
+    ftable[0].size = gfits_data_size (theader); \
+    return (TRUE); }
+
+  if (!strcmp (extname, "DVO_IMAGE")) {
+    Image *image;
+    image = gfits_table_get_Image (ftable, &Nimage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (TRUE);
+  }
+
+  CONVERT_FORMAT ("DVO_IMAGE_ELIXIR", 	       ELIXIR,          Elixir);
+  CONVERT_FORMAT ("DVO_IMAGE_LONEOS", 	       LONEOS,          Loneos);
+  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
+  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
+  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
+  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
+# undef CONVERT_FORMAT
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+  return (FALSE);
+}
+
+int ImageToFtable (FTable *ftable, Header *theader, int format) {
+
+  int Nimage;
+
+  Nimage = theader[0].Naxis[1];
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+    case DVO_FORMAT_##FORMAT: { \
+      Image_##TYPE *tmpImage; \
+      tmpImage = ImageInternalTo_##TYPE ((Image *) ftable[0].buffer, Nimage); \
+      free (ftable[0].buffer); \
+      gfits_table_set_Image_##TYPE (ftable, tmpImage, Nimage); \
+      free (tmpImage); \
+      break; }
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_convert_Image ((Image *) ftable[0].buffer, sizeof(Image), Nimage);
+      break; }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      fprintf (stderr, "table format unknown (image ftable)\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+int ImageToVtable (VTable *vtable, Header *theader, int format) {
+
+  int i, Nrow, Nimage;
+
+  Nrow = vtable[0].Nrow;
+
+# define FORMAT_CASE(FORMAT, TYPE) \
+  case DVO_FORMAT_##FORMAT: { \
+      Image_##TYPE *tmpImage; \
+      /* convert table rows from internal to external format */ \
+      for (i = 0; i < Nrow; i++) { \
+	  tmpImage = ImageInternalTo_##TYPE ((Image *) vtable[0].buffer[i], 1); \
+	  gfits_convert_Image_##TYPE (tmpImage, sizeof(Image_##TYPE), 1); \
+	  free (vtable[0].buffer[i]); \
+	  vtable[0].buffer[i] = (char *) tmpImage; \
+      } \
+      /* convert header from old format to new format */ \
+      gfits_scan (theader, "NAXIS2", "%d", 1, &Nimage); \
+      gfits_free_header (theader); \
+      gfits_table_mkheader_Image_##TYPE (theader); \
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage); \
+      theader[0].Naxis[1] = Nimage; \
+      vtable[0].size = gfits_data_size (theader); \
+      return (TRUE); }
+
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      for (i = 0; i < Nrow; i++) {
+	gfits_convert_Image ((Image *) vtable[0].buffer[i], sizeof(Image), 1);
+      }
+      return (TRUE); }
+
+      FORMAT_CASE (ELIXIR, 	    Elixir);
+      FORMAT_CASE (LONEOS, 	    Loneos);
+      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
+      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
+      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
+      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
+# undef FORMAT_CASE
+
+    default:
+      break;
+  }
+  fprintf (stderr, "table format unknown (image vtable)\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_1.c	(revision 17422)
@@ -0,0 +1,362 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_1 formats to internal formats */
+
+Measure *Measure_PS1_DEV_1_ToInternal (Measure_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Mgal;
+    out[i].dM         = in[i].dM;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+Measure_PS1_DEV_1 *MeasureInternalTo_PS1_DEV_1 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Measure_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_PS1_DEV_1_ToInternal (Average_PS1_DEV_1 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average_PS1_DEV_1 *AverageInternalTo_PS1_DEV_1 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Average_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+SecFilt *SecFilt_PS1_DEV_1_ToInternal (SecFilt_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+SecFilt_PS1_DEV_1 *SecFiltInternalTo_PS1_DEV_1 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_PS1_DEV_1 *out;
+
+  ALLOCATE (out, SecFilt_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+Image *Image_PS1_DEV_1_ToInternal (Image_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_PS1_DEV_1 *ImageInternalTo_PS1_DEV_1 (Image *in, int Nvalues) {
+
+  int i;
+  Image_PS1_DEV_1 *out;
+
+  ALLOCATE (out, Image_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_PS1_DEV_1 *PhotCode_Internal_To_PS1_DEV_1 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_1 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code = in[i].code;         
+    out[i].type = in[i].type;         
+    out[i].C  	= in[i].C;            
+    out[i].dC 	= in[i].dC;           
+    out[i].dX 	= in[i].dX;           
+    out[i].K  	= in[i].K;            
+    out[i].c1 	= in[i].c1;           
+    out[i].c2 	= in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrMagScale = out[i].astromErrMagScale;
+    out[i].photomErrSys      = out[i].photomErrSys;
+
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_1_To_Internal (PhotCode_PS1_DEV_1 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+
+    // not defined in PS1_DEV_1
+    out[i].astromErrSys      = 0.0;
+    out[i].astromErrScale    = 0.0;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_2.c	(revision 17422)
@@ -0,0 +1,370 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_2 formats to internal formats */
+
+Measure *Measure_PS1_DEV_2_ToInternal (Measure_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dMcal      = in[i].dMcal;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+Measure_PS1_DEV_2 *MeasureInternalTo_PS1_DEV_2 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Measure_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Map;
+    out[i].dM         = in[i].dM;
+    out[i].dMcal      = in[i].dMcal;
+    out[i].dt         = in[i].dt;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].qPSF       = in[i].qPSF;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].photcode   = in[i].photcode;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+    out[i].stargal    = in[i].stargal;
+    out[i].dophot     = in[i].dophot;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_PS1_DEV_2_ToInternal (Average_PS1_DEV_2 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nmeasure;     
+    out[i].Nmissing      = in[i].Nmissing;     
+    out[i].Nextend       = in[i].Nextend;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].measureOffset; 
+    out[i].missingOffset = in[i].missingOffset;
+    out[i].extendOffset  = in[i].extendOffset;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average_PS1_DEV_2 *AverageInternalTo_PS1_DEV_2 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Average_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nmeasure;     
+    out[i].Nmissing      = in[i].Nmissing;     
+    out[i].Nextend       = in[i].Nextend;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].measureOffset; 
+    out[i].missingOffset = in[i].missingOffset;
+    out[i].extendOffset  = in[i].extendOffset;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+SecFilt *SecFilt_PS1_DEV_2_ToInternal (SecFilt_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+SecFilt_PS1_DEV_2 *SecFiltInternalTo_PS1_DEV_2 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_PS1_DEV_2 *out;
+
+  ALLOCATE (out, SecFilt_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+Image *Image_PS1_DEV_2_ToInternal (Image_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+Image_PS1_DEV_2 *ImageInternalTo_PS1_DEV_2 (Image *in, int Nvalues) {
+
+  int i;
+  Image_PS1_DEV_2 *out;
+
+  ALLOCATE (out, Image_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID;
+    out[i].externID	    = in[i].externID;
+    out[i].sourceID	    = in[i].sourceID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_PS1_DEV_2 *PhotCode_Internal_To_PS1_DEV_2 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_2 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_2, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_2_To_Internal (PhotCode_PS1_DEV_2 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_PS1_DEV_3.c	(revision 17422)
@@ -0,0 +1,77 @@
+# include <dvo.h>
+
+/* convert PS1_DEV_3 formats to internal formats */
+
+// XXX I am not yet ready to commit to a full PS1_DEV_3 release, but we can add photcode here for now
+
+PhotCode_PS1_DEV_3 *PhotCode_Internal_To_PS1_DEV_3 (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_PS1_DEV_3 *out;
+
+  ALLOCATE (out, PhotCode_PS1_DEV_3, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = in[i].photomPoorMask;
+    out[i].photomBadMask       = in[i].photomBadMask;
+    out[i].astromPoorMask      = in[i].astromPoorMask;
+    out[i].astromBadMask       = in[i].astromBadMask;
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_PS1_DEV_3_To_Internal (PhotCode_PS1_DEV_3 *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C     = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    out[i].astromErrSys      = in[i].astromErrSys;
+    out[i].astromErrScale    = in[i].astromErrScale;
+    out[i].astromErrMagScale = in[i].astromErrMagScale;
+    out[i].photomErrSys      = in[i].photomErrSys;
+
+    out[i].photomPoorMask      = in[i].photomPoorMask;
+    out[i].photomBadMask       = in[i].photomBadMask;
+    out[i].astromPoorMask      = in[i].astromPoorMask;
+    out[i].astromBadMask       = in[i].astromBadMask;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_elixir.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 17422)
@@ -0,0 +1,364 @@
+# include <dvo.h>
+
+/* convert elixir-format measures to internal measures */
+Measure *Measure_Elixir_ToInternal (Measure_Elixir *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR       = (in[i].dR      == NAN_S_SHORT) ? NAN : in[i].dR     * 0.01;
+    out[i].dD       = (in[i].dD      == NAN_S_SHORT) ? NAN : in[i].dD     * 0.01;
+    out[i].M        = (in[i].M       == NAN_S_SHORT) ? NAN : in[i].M      * 0.001;
+    out[i].dM       = (in[i].dM      == NAN_U_CHAR)  ? NAN : in[i].dM     * 0.001;
+    out[i].dt       = (in[i].dt      == NAN_S_SHORT) ? NAN : in[i].dt     * 0.001;
+    out[i].Mcal     = (in[i].Mcal    == NAN_S_SHORT) ? NAN : in[i].Mcal   * 0.001;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map      = (in[i].Mgal    == NAN_S_SHORT) ? NAN : in[i].Mgal   * 0.001;
+    out[i].airmass  = (in[i].airmass == NAN_S_SHORT) ? NAN : in[i].airmass* 0.001;
+    out[i].FWx 	    = in[i].FWx;
+    out[i].FWy 	    = in[i].fwy * in[i].FWx * 0.01;
+    out[i].theta    = in[i].theta*(0x10000 / 0x100);
+    out[i].dophot   = in[i].dophot;
+    out[i].photcode = in[i].source;
+    out[i].t        = in[i].t;
+    out[i].averef   = in[i].averef;
+    out[i].dbFlags  = in[i].flags;
+    
+    /* these can be determined if needed / desired */
+    out[i].Xccd     = 0;
+    out[i].Yccd     = 0;
+    out[i].dXccd    = 0;
+    out[i].dYccd    = 0;
+
+    /* these do not have a corresponding value */
+    out[i].az        = 0;
+    out[i].stargal   = 0;
+    out[i].Sky       = 0;
+    out[i].dSky      = 0;
+    out[i].qPSF      = 0;
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+
+    /* XXX add these later */
+    out[i].detID = 0;
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to elixir-format measures */
+Measure_Elixir *MeasureInternalTo_Elixir (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Elixir *out;
+
+  ALLOCATE (out, Measure_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR       = isnan(in[i].dR     ) ? NAN_S_SHORT : in[i].dR      *  100.0;
+    out[i].dD       = isnan(in[i].dD     ) ? NAN_S_SHORT : in[i].dD      *  100.0;
+    out[i].M        = isnan(in[i].M      ) ? NAN_S_SHORT : in[i].M       * 1000.0;
+    out[i].dM       = isnan(in[i].dM     ) ? NAN_U_CHAR  : in[i].dM      * 1000.0;
+    out[i].dt       = isnan(in[i].dt     ) ? NAN_S_SHORT : in[i].dt      * 1000.0;
+    out[i].Mcal     = isnan(in[i].Mcal   ) ? NAN_S_SHORT : in[i].Mcal    * 1000.0;
+    out[i].Mgal     = isnan(in[i].Map    ) ? NAN_S_SHORT : in[i].Map     * 1000.0;
+    out[i].airmass  = isnan(in[i].airmass) ? NAN_S_SHORT : in[i].airmass * 1000.0;
+
+    if (out[i].M < 0) {
+	fprintf (stderr, ".");
+    }
+
+    out[i].FWx 	   = in[i].FWx;
+    out[i].fwy 	   = in[i].FWy * 100.0 / in[i].FWx;
+    out[i].theta   = in[i].theta*(0x100/ 0x10000);
+    out[i].dophot  = in[i].dophot;
+    out[i].source  = in[i].photcode;
+    out[i].t       = in[i].t;
+    out[i].averef  = in[i].averef;
+    out[i].flags   = in[i].dbFlags;
+  }
+  return (out);
+}
+
+/* convert elixir-format averages to internal averages */
+Average *Average_Elixir_ToInternal (Average_Elixir *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+  ALLOCATE (*primary, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       	 = in[i].R;      
+    out[i].D       	 = in[i].D;      
+    out[i].Xp      	 = in[i].Xp;     
+    out[i].Nmeasure 	 = in[i].Nm;     
+    out[i].Nmissing 	 = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+
+    /* these don't exist in Elixir */
+    out[i].dR      = 0;
+    out[i].dD      = 0;
+    out[i].uR      = 0;
+    out[i].uD      = 0;
+    out[i].duR     = 0;
+    out[i].duD     = 0;
+    out[i].P       = 0;
+    out[i].dP      = 0;
+
+    /* XXX add these later */
+    out[i].objID   = 0;
+    out[i].catID   = 0;
+
+    primary[0][i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M  * 0.001;      
+    primary[0][i].dM    = (in[i].dM == NAN_S_SHORT) ? NAN : in[i].dM * 0.001;      
+    primary[0][i].Xm    = in[i].Xm;     
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal averages to elixir-format averages */
+Average_Elixir *AverageInternalTo_Elixir (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Elixir *out;
+
+  ALLOCATE (out, Average_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       = in[i].R;      
+    out[i].D       = in[i].D;      
+    out[i].Xp      = in[i].Xp;     
+    out[i].Nm      = in[i].Nmeasure;     
+    out[i].Nn      = in[i].Nmissing;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].measureOffset; 
+    out[i].missing = in[i].missingOffset;
+
+    out[i].M       = isnan(primary[i].M)  ? NAN_S_SHORT : primary[i].M   * 1000.0;
+    out[i].dM      = isnan(primary[i].dM) ? NAN_S_SHORT : primary[i].dM  * 1000.0;
+    out[i].Xm      = primary[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert elixir-format secfilts to internal secfilts */
+SecFilt *SecFilt_Elixir_ToInternal (SecFilt_Elixir *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M   * 0.001;
+    out[i].dM    = (in[i].dM == NAN_S_SHORT) ? NAN : in[i].dM  * 0.001;
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = 0;
+    out[i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal secfilts to elixir-format secfilts */
+SecFilt_Elixir *SecFiltInternalTo_Elixir (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Elixir *out;
+
+  ALLOCATE (out, SecFilt_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = isnan(in[i].M)  ? NAN_S_SHORT : in[i].M   * 1000.0;
+    out[i].dM    = isnan(in[i].dM) ? NAN_S_SHORT : in[i].dM  * 1000.0;
+    out[i].Xm    = in[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert elixir-format images to internal images */
+Image *Image_Elixir_ToInternal (Image_Elixir *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name); // out[64], in[32]
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].secz    	    = (in[i].secz     == NAN_S_SHORT) ? NAN : in[i].secz     * 0.001;
+    out[i].apmifit  	    = (in[i].apmifit  == NAN_S_SHORT) ? NAN : in[i].apmifit  * 0.001;
+    out[i].dapmifit  	    = (in[i].dapmifit == NAN_S_SHORT) ? NAN : in[i].dapmifit * 0.001;
+    out[i].Mcal   	    = (in[i].Mcal     == NAN_S_SHORT) ? NAN : in[i].Mcal     * 0.001;
+    out[i].dMcal   	    = (in[i].dMcal    == NAN_S_SHORT) ? NAN : in[i].dMcal    * 0.001;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].source;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+
+    /* XXX add these later */
+    out[i].imageID = 0;
+
+    // not available in ELIXIR
+    out[i].sidtime  	    = NAN;
+    out[i].latitude  	    = NAN;
+  }
+  return (out);
+}
+
+/* convert internal images to elixir-format images */
+Image_Elixir *ImageInternalTo_Elixir (Image *in, int Nvalues) {
+
+  int i;
+  Image_Elixir *out;
+
+  ALLOCATE (out, Image_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[64]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].secz    	    = isnan(in[i].secz    ) ? NAN_S_SHORT : in[i].secz     * 1000.0;
+    out[i].apmifit    	    = isnan(in[i].apmifit ) ? NAN_S_SHORT : in[i].apmifit  * 1000.0;
+    out[i].dapmifit    	    = isnan(in[i].dapmifit) ? NAN_S_SHORT : in[i].dapmifit * 1000.0;
+    out[i].Mcal    	    = isnan(in[i].Mcal    ) ? NAN_S_SHORT : in[i].Mcal     * 1000.0;
+    out[i].dMcal    	    = isnan(in[i].dMcal   ) ? NAN_S_SHORT : in[i].dMcal    * 1000.0;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].source   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+PhotCode_Elixir *PhotCode_Internal_To_Elixir (PhotCode *in, int Nvalues) {
+
+  int i;
+  PhotCode_Elixir *out;
+
+  ALLOCATE (out, PhotCode_Elixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+  }
+  return (out);
+}
+
+PhotCode *PhotCode_Elixir_To_Internal (PhotCode_Elixir *in, int Nvalues) {
+
+  int i;
+  PhotCode *out;
+
+  ALLOCATE (out, PhotCode, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[32]
+    out[i].name[31] = 0; // force termination
+
+    out[i].code  = in[i].code;         
+    out[i].type  = in[i].type;         
+    out[i].C  	 = in[i].C;            
+    out[i].dC 	 = in[i].dC;           
+    out[i].dX 	 = in[i].dX;           
+    out[i].K  	 = in[i].K;            
+    out[i].c1 	 = in[i].c1;           
+    out[i].c2 	 = in[i].c2;           
+    out[i].equiv = in[i].equiv;        
+    out[i].Nc    = in[i].Nc;           
+    memcpy (out[i].X, in[i].X, 4*sizeof(float));            
+
+    // not defined in Elixir:
+    out[i].astromErrSys      = 0.0;
+    out[i].astromErrScale    = 0.0;
+    out[i].astromErrMagScale = 0.0;
+    out[i].photomErrSys      = 0.0;
+
+    out[i].photomPoorMask      = 0;
+    out[i].photomBadMask       = 0;
+    out[i].astromPoorMask      = 0;
+    out[i].astromBadMask       = 0;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_loneos.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 17422)
@@ -0,0 +1,289 @@
+# include <dvo.h>
+
+/* convert loneos-format measures to internal measures */
+Measure *Measure_Loneos_ToInternal (Measure_Loneos *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR        = (in[i].dR      == NAN_S_SHORT) ? NAN : in[i].dR     * 0.01;
+    out[i].dD        = (in[i].dD      == NAN_S_SHORT) ? NAN : in[i].dD     * 0.01;
+    out[i].M         = (in[i].M       == NAN_S_SHORT) ? NAN : in[i].M      * 0.001;
+    out[i].dM        = (in[i].dM      == NAN_U_CHAR)  ? NAN : in[i].dM     * 0.001;
+    out[i].Mcal      = (in[i].Mcal    == NAN_S_SHORT) ? NAN : in[i].Mcal   * 0.001;
+    out[i].dophot    = in[i].dophot;
+    out[i].photcode  = in[i].source;
+    out[i].t         = in[i].t;
+
+    /* flags and averef are now split */
+    out[i].averef    =  in[i].averef & 0x00ffffff;
+    out[i].dbFlags   = (in[i].averef & 0xff000000) >> 24;
+
+    /* these values don't exist in the Loneos format */
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map       = out[i].M;
+    out[i].dt        = 0;
+    out[i].airmass   = 0;
+    out[i].az        = 0;
+    out[i].FWx 	     = 0;
+    out[i].FWy 	     = 0;
+    out[i].theta     = 0;
+    out[i].stargal   = 0;
+    out[i].Sky       = 0;
+    out[i].dSky      = 0;
+    out[i].qPSF      = 0;
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+
+    /* these can be determined if needed / desired */
+    out[i].Xccd     = 0;
+    out[i].Yccd     = 0;
+    out[i].dXccd    = 0;
+    out[i].dYccd    = 0;
+
+    /* XXX add these later */
+    out[i].detID = 0;
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to loneos-format measures */
+Measure_Loneos *MeasureInternalTo_Loneos (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Loneos *out;
+
+  ALLOCATE (out, Measure_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR     = isnan(in[i].dR     ) ? NAN_S_SHORT : in[i].dR      *  100.0;
+    out[i].dD     = isnan(in[i].dD     ) ? NAN_S_SHORT : in[i].dD      *  100.0;
+    out[i].M      = isnan(in[i].M      ) ? NAN_S_SHORT : in[i].M       * 1000.0;
+    out[i].dM     = isnan(in[i].dM     ) ? NAN_U_CHAR  : in[i].dM      * 1000.0;
+    out[i].Mcal   = isnan(in[i].Mcal   ) ? NAN_S_SHORT : in[i].Mcal    * 1000.0;
+    out[i].dophot = in[i].dophot;
+    out[i].source = in[i].photcode;
+    out[i].t      = in[i].t;
+
+    /* flags and averef are merged in Loneos */
+    out[i].averef =  (in[i].averef & 0x00ffffff) | (in[i].dbFlags << 24);
+  }
+  return (out);
+}
+
+/* convert loneos-format averages to internal averages */
+Average *Average_Loneos_ToInternal (Average_Loneos *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+  ALLOCATE (*primary, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       	 = in[i].R;      
+    out[i].D       	 = in[i].D;      
+    out[i].Xp      	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code    	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+
+    /* these don't exist in Loneos */
+    out[i].dR      = 0;
+    out[i].dD      = 0;
+    out[i].uR      = 0;
+    out[i].uD      = 0;
+    out[i].duR     = 0;
+    out[i].duD     = 0;
+    out[i].P       = 0;
+    out[i].dP      = 0;
+
+    /* XXX add these later */
+    out[i].objID   = 0;
+    out[i].catID   = 0;
+
+    primary[0][i].M  = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M  * 0.001;      
+    primary[0][i].Xm = in[i].Xm;     
+    primary[0][i].dM = NAN;
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal averages to loneos-format averages */
+Average_Loneos *AverageInternalTo_Loneos (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Loneos *out;
+
+  ALLOCATE (out, Average_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       = in[i].R;      
+    out[i].D       = in[i].D;      
+    out[i].Xp      = in[i].Xp;     
+    out[i].Nm      = in[i].Nmeasure;     
+    out[i].Nn      = in[i].Nmissing;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].measureOffset; 
+    out[i].missing = in[i].missingOffset;
+
+    out[i].M       = isnan(primary[i].M)  ? NAN_S_SHORT : primary[i].M   * 1000.0;
+    out[i].Xm      = primary[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert loneos-format secfilts to internal secfilts */
+SecFilt *SecFilt_Loneos_ToInternal (SecFilt_Loneos *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = (in[i].M  == NAN_S_SHORT) ? NAN : in[i].M * 0.001;
+    out[i].Xm    = in[i].Xm;      
+    out[i].dM    = NAN;
+    out[i].Ncode = 0;
+    out[i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal secfilts to loneos-format secfilts */
+SecFilt_Loneos *SecFiltInternalTo_Loneos (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Loneos *out;
+
+  ALLOCATE (out, SecFilt_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M    = isnan(in[i].M)  ? NAN_S_SHORT : in[i].M * 1000.0;
+    out[i].Xm   = in[i].Xm;      
+  }
+  return (out);
+}
+
+/* convert loneos-format images to internal images */
+Image *Image_Loneos_ToInternal (Image_Loneos *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name); // out[64], in[32]
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+
+    out[i].secz    	    = (in[i].secz     == NAN_S_SHORT) ? NAN : in[i].secz     * 0.001;
+    out[i].apmifit  	    = (in[i].apmifit  == NAN_S_SHORT) ? NAN : in[i].apmifit  * 0.001;
+    out[i].dapmifit  	    = (in[i].dapmifit == NAN_S_SHORT) ? NAN : in[i].dapmifit * 0.001;
+    out[i].Mcal   	    = (in[i].Mcal     == NAN_S_SHORT) ? NAN : in[i].Mcal     * 0.001;
+    out[i].dMcal   	    = (in[i].dMcal    == NAN_S_SHORT) ? NAN : in[i].dMcal    * 0.001;
+
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].source;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+
+    /* XXX add these later */
+    out[i].imageID = 0;
+  }
+  return (out);
+}
+
+/* convert internal images to loneos-format images */
+Image_Loneos *ImageInternalTo_Loneos (Image *in, int Nvalues) {
+
+  int i;
+  Image_Loneos *out;
+
+  ALLOCATE (out, Image_Loneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[64]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+
+    out[i].secz    	    = isnan(in[i].secz    ) ? NAN_S_SHORT : in[i].secz     * 1000.0;
+    out[i].apmifit    	    = isnan(in[i].apmifit ) ? NAN_S_SHORT : in[i].apmifit  * 1000.0;
+    out[i].dapmifit    	    = isnan(in[i].dapmifit) ? NAN_S_SHORT : in[i].dapmifit * 1000.0;
+    out[i].Mcal    	    = isnan(in[i].Mcal    ) ? NAN_S_SHORT : in[i].Mcal     * 1000.0;
+    out[i].dMcal    	    = isnan(in[i].dMcal   ) ? NAN_S_SHORT : in[i].dMcal    * 1000.0;
+
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].source   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 17422)
@@ -0,0 +1,304 @@
+# include <dvo.h>
+
+/* convert panstarrs-format measures to internal measures */
+Measure *Measure_Panstarrs_DEV_0_ToInternal (Measure_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map        = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].dbFlags    = in[i].flags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = (in[i].qPSF == NAN_S_SHORT) ? NAN : in[i].qPSF;
+    out[i].detID      = in[i].detID_lo;
+    out[i].imageID    = in[i].imageID_lo;
+
+    // these don't have a correspondence
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+Measure_Panstarrs_DEV_0 *MeasureInternalTo_Panstarrs_DEV_0 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Measure_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = in[i].dbFlags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = isnan(in[i].qPSF) ? NAN_S_SHORT : in[i].qPSF;
+    out[i].detID_hi   = 0;
+    out[i].detID_lo   = in[i].detID;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = in[i].imageID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_Panstarrs_DEV_0_ToInternal (Average_Panstarrs_DEV_0 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code          = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average_Panstarrs_DEV_0 *AverageInternalTo_Panstarrs_DEV_0 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Average_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFilt_Panstarrs_DEV_0_ToInternal (SecFilt_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+/* convert internal secfilts to panstarrs-format secfilts */
+SecFilt_Panstarrs_DEV_0 *SecFiltInternalTo_Panstarrs_DEV_0 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, SecFilt_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format images to internal images */
+Image *Image_Panstarrs_DEV_0_ToInternal (Image_Panstarrs_DEV_0 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name); // out[64], in[32]
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID_lo;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_Panstarrs_DEV_0 *ImageInternalTo_Panstarrs_DEV_0 (Image *in, int Nvalues) {
+
+  int i;
+  Image_Panstarrs_DEV_0 *out;
+
+  ALLOCATE (out, Image_Panstarrs_DEV_0, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strncpy (out[i].name, in[i].name, 31); // out[32], in[64]
+    out[i].name[31] = 0; // force termination
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID_hi	    = 0;
+    out[i].imageID_lo	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_1.c	(revision 17422)
@@ -0,0 +1,303 @@
+# include <dvo.h>
+
+/* convert panstarrs-format measures to internal measures */
+Measure *Measure_Panstarrs_DEV_1_ToInternal (Measure_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+
+    // 2008.02.26 : I've renamed Mgal to Map, and intend it to be used per aperture
+    // magnitudes.  Most uses of Mgal in the past were actually aperture (isophotal) mags
+    out[i].Map        = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].dbFlags    = in[i].flags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = (in[i].qPSF == NAN_S_SHORT) ? NAN : in[i].qPSF;
+    out[i].detID      = in[i].detID_lo;
+    out[i].imageID    = in[i].imageID_lo;
+
+    // these don't have a correspondence
+    out[i].psfChisq  = 0;
+    out[i].crNsigma  = 0;
+    out[i].extNsigma = 0;
+    out[i].photFlags = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+Measure_Panstarrs_DEV_1 *MeasureInternalTo_Panstarrs_DEV_1 (Measure *in, int Nvalues) {
+
+  int i;
+  Measure_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Measure_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].dM         = in[i].dM;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Map;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].dt         = in[i].dt;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].dophot     = in[i].dophot;
+    out[i].photcode   = in[i].photcode;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = in[i].dbFlags;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].stargal    = in[i].stargal;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].qPSF       = isnan(in[i].qPSF) ? NAN_S_SHORT : in[i].qPSF;
+    out[i].detID_hi   = 0;
+    out[i].detID_lo   = in[i].detID;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = in[i].imageID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_Panstarrs_DEV_1_ToInternal (Average_Panstarrs_DEV_1 *in, int Nvalues, SecFilt **primary) {
+
+  int i;
+  Average *out;
+
+  ALLOCATE (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        	 = in[i].R;      
+    out[i].D        	 = in[i].D;      
+    out[i].Xp       	 = in[i].Xp;     
+    out[i].Nmeasure      = in[i].Nm;     
+    out[i].Nmissing      = in[i].Nn;     
+    out[i].code     	 = in[i].code;   
+    out[i].measureOffset = in[i].offset; 
+    out[i].missingOffset = in[i].missing;
+    out[i].dR       	 = in[i].dR;
+    out[i].dD       	 = in[i].dD;
+    out[i].uR       	 = in[i].uR;
+    out[i].uD       	 = in[i].uD;
+    out[i].duR      	 = in[i].duR;
+    out[i].duD      	 = in[i].duD;
+    out[i].P        	 = in[i].P;
+    out[i].dP       	 = in[i].dP;
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+// 'primary is needed to conform with the API for Loneos and Elixir, but is not used
+Average_Panstarrs_DEV_1 *AverageInternalTo_Panstarrs_DEV_1 (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  Average_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Average_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R        = in[i].R;      
+    out[i].D        = in[i].D;      
+    out[i].Xp       = in[i].Xp;     
+    out[i].Nm       = in[i].Nmeasure;     
+    out[i].Nn       = in[i].Nmissing;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].measureOffset; 
+    out[i].missing  = in[i].missingOffset;
+    out[i].dR       = in[i].dR;
+    out[i].dD       = in[i].dD;
+    out[i].uR       = in[i].uR;
+    out[i].uD       = in[i].uD;
+    out[i].duR      = in[i].duR;
+    out[i].duD      = in[i].duD;
+    out[i].P        = in[i].P;
+    out[i].dP       = in[i].dP;
+    out[i].objID    = in[i].objID;
+    out[i].catID    = in[i].catID;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFilt_Panstarrs_DEV_1_ToInternal (SecFilt_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+ }
+  return (out);
+}
+
+/* convert internal secfilts to panstarrs-format secfilts */
+SecFilt_Panstarrs_DEV_1 *SecFiltInternalTo_Panstarrs_DEV_1 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFilt_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, SecFilt_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M;      
+    out[i].dM    = in[i].dM;      
+    out[i].Xm    = in[i].Xm;     
+    out[i].Ncode = in[i].Ncode;
+    out[i].Nused = in[i].Nused;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format images to internal images */
+Image *Image_Panstarrs_DEV_1_ToInternal (Image_Panstarrs_DEV_1 *in, int Nvalues) {
+
+  int i;
+  Image *out;
+
+  ALLOCATE (out, Image, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID	    = in[i].imageID_lo;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+Image_Panstarrs_DEV_1 *ImageInternalTo_Panstarrs_DEV_1 (Image *in, int Nvalues) {
+
+  int i;
+  Image_Panstarrs_DEV_1 *out;
+
+  ALLOCATE (out, Image_Panstarrs_DEV_1, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    strcpy (out[i].name, in[i].name);
+
+    out[i].tzero    	    = in[i].tzero;
+    out[i].nstar    	    = in[i].nstar;
+    out[i].secz	    	    = in[i].secz;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit;
+    out[i].dapmifit 	    = in[i].dapmifit;
+    out[i].Mcal	    	    = in[i].Mcal;
+    out[i].dMcal    	    = in[i].dMcal;
+    out[i].Xm	    	    = in[i].Xm;
+    out[i].photcode   	    = in[i].photcode;
+    out[i].exptime  	    = in[i].exptime;
+    out[i].sidtime  	    = in[i].sidtime;
+    out[i].latitude  	    = in[i].latitude;
+    out[i].detection_limit  = in[i].detection_limit;
+    out[i].saturation_limit = in[i].saturation_limit;
+    out[i].cerror	    = in[i].cerror;
+    out[i].fwhm_x	    = in[i].fwhm_x;
+    out[i].fwhm_y	    = in[i].fwhm_y;
+    out[i].trate	    = in[i].trate;
+    out[i].code		    = in[i].code;
+    out[i].ccdnum	    = in[i].ccdnum;
+    out[i].imageID_hi	    = 0;
+    out[i].imageID_lo	    = in[i].imageID;
+
+    out[i].order	    = in[i].order;
+    out[i].Mx		    = in[i].Mx;
+    out[i].My		    = in[i].My;
+    out[i].Mxx		    = in[i].Mxx;
+    out[i].Mxy		    = in[i].Mxy;
+    out[i].Myy		    = in[i].Myy;
+    out[i].Mxxx		    = in[i].Mxxx;
+    out[i].Mxxy		    = in[i].Mxxy;
+    out[i].Mxyy		    = in[i].Mxyy;
+    out[i].Myyy		    = in[i].Myyy;
+    out[i].Mxxxx	    = in[i].Mxxxx;
+    out[i].Mxxxy	    = in[i].Mxxxy;
+    out[i].Mxxyy	    = in[i].Mxxyy;
+    out[i].Mxyyy	    = in[i].Mxyyy;
+    out[i].Myyyy	    = in[i].Myyyy;
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image.c	(revision 17422)
@@ -0,0 +1,199 @@
+# include <dvo.h>
+
+/* lock the image table */
+int dvo_image_lock (FITS_DB *db, char *filename, double timeout, int lockstate) {
+
+  /* lock the image catalog */
+  if (!check_file_access (filename, FALSE, TRUE)) return (FALSE);
+
+  db[0].lockstate = lockstate;
+  db[0].timeout   = timeout;
+  gfits_db_init (db);
+
+  if (!gfits_db_lock (db, filename)) {
+    fprintf (stderr, "can't lock image catalog\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int dvo_image_unlock (FITS_DB *db) {
+
+  mode_t mode;
+
+  gfits_db_close (db);
+
+  /* force permissions to 666 */
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (db[0].filename, mode);
+  return (TRUE);
+}
+
+/* determine db mode (RAW/MEF) and read the complete table */
+int dvo_image_load (FITS_DB *db, int VERBOSE, int FORCE_READ) {
+
+  int Naxis, Nread, status;
+  char buffer[241];
+
+  /* database name must be set first */
+  if (db == NULL) return (FALSE);
+  if (db[0].f == NULL) return (FALSE);
+
+  /* test for NAXIS == 2 in line 3 */
+  bzero (buffer, 241);
+  fseek (db[0].f, 0, SEEK_SET);
+  Nread = fread (buffer, 1, 240, db[0].f);
+  if (Nread != 240) {
+    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
+    return (FALSE);
+  }
+  Nread = sscanf (&buffer[160], "NAXIS   = %d", &Naxis);
+  if (Nread != 1) {
+    if (VERBOSE) fprintf (stderr, "can't determine image db mode\n");
+    return (FALSE);
+  }
+  fseek (db[0].f, 0, SEEK_SET);
+
+  /* default values */
+  db[0].mode = DVO_MODE_MEF;
+  db[0].format = DVO_FORMAT_UNDEF;
+  if (Naxis == 2) db[0].mode = DVO_MODE_RAW; /* image table can only be RAW or MEF */
+
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_MEF)\n");
+      status = gfits_db_load (db);
+      break;
+    case DVO_MODE_RAW:
+      if (VERBOSE) fprintf (stderr, "reading images (mode DVO_MODE_RAW)\n");
+      status = dvo_image_load_raw (db, VERBOSE, FORCE_READ);
+      break;
+    default:
+      fprintf (stderr, "error getting image mode\n");
+      exit (2);
+  }
+  if (!status) return (FALSE);
+  FtableToImage (&db[0].ftable, &db[0].theader, &db[0].format);
+  db[0].swapped = TRUE;  /* table has internal byte-order */
+  return (TRUE);
+}
+
+/* write the complete image table to disk in the requested mode */
+int dvo_image_save (FITS_DB *db, int VERBOSE) {
+
+  int status;
+
+  /* convert from internal to requested external format */
+  ImageToFtable (&db[0].ftable, &db[0].theader, db[0].format);
+  db[0].swapped = FALSE;
+
+  /* write data in appropriate mode */
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      status = gfits_db_save (db);
+      break;
+    case DVO_MODE_RAW:
+      status = dvo_image_save_raw (db, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error writing image mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+int dvo_image_addrows (FITS_DB *db, Image *new, int Nnew) {
+
+  int Nimages;
+
+  /* adjust header */
+  Nimages = 0;
+  gfits_scan (&db[0].header, "NIMAGES", "%d", 1, &Nimages);
+  Nimages += Nnew;
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, Nimages);
+
+  gfits_table_to_vtable (&db[0].ftable, &db[0].vtable, 0, 0);
+  gfits_vadd_rows (&db[0].vtable, (char *) new, Nnew, sizeof(Image));
+
+  /* check that primary header and table header agree */
+  if (Nimages != db[0].theader.Naxis[1]) {
+    fprintf (stderr, "header / table length mismatch!\n");
+  }
+  return (TRUE);
+}  
+
+/* write the vtable data to disk in the requested mode */
+int dvo_image_update (FITS_DB *db, int VERBOSE) {
+
+  int status;
+
+  /* convert from internal to requested external format */
+  /* theader is modified to match output format */
+  ImageToVtable (&db[0].vtable, &db[0].theader, db[0].format);
+  db[0].swapped = FALSE;
+
+  /* write data in appropriate mode */
+  switch (db[0].mode) {
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      status = gfits_db_update (db);
+      break;
+    case DVO_MODE_RAW:
+      status = dvo_image_update_raw (db, VERBOSE);
+      break;
+    default:
+      fprintf (stderr, "error writing image mode\n");
+      exit (2);
+  }
+  return (TRUE);
+}
+
+void dvo_image_create (FITS_DB *db, double ZeroPoint) {
+
+  /* create new header */
+  gfits_init_header (&db[0].header);
+
+  /* check the mode */
+  switch (db[0].mode) {
+    case DVO_MODE_RAW:
+      /* make header a fake image */
+      db[0].header.bitpix   = 16;
+      db[0].header.Naxes    = 2;
+      db[0].header.Naxis[0] = 1;
+      db[0].header.Naxis[1] = 1;
+      gfits_create_header (&db[0].header);
+      break;
+    case DVO_MODE_MEF:
+    case DVO_MODE_SPLIT:
+      db[0].header.extend   = TRUE;
+      gfits_create_header (&db[0].header);
+      db[0].mode = DVO_MODE_MEF;
+      break;
+    default:
+      fprintf (stderr, "invalid output catalog mode\n");
+      exit (1);
+  }
+
+  /* check the format */
+  if (db[0].format == DVO_FORMAT_UNDEF) {
+    fprintf (stderr, "invalid output catalog format\n");
+    exit (1);
+  }
+
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  gfits_table_set_Image (&db[0].ftable, NULL, 0);
+
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, 0);
+  gfits_modify (&db[0].header, "ZERO_PT", "%lf", 1, ZeroPoint);
+
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "INTERNAL");
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "LONEOS");
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "ELIXIR");
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_0");
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS_DEV_1");
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_1");
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PS1_DEV_2");
+  
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image_raw.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 17422)
@@ -0,0 +1,157 @@
+# include <dvo.h>
+
+/* the old Image.dat files used a fake FITS header defining a finite data block
+ * this function reads in the header and data, and creates an ftable and theader to match
+ */
+
+int dvo_image_load_raw (FITS_DB *db, int VERBOSE, int FORCE_READ) {
+
+  int Nimage, Ndata, size, ImageSize, nbytes, Nbytes;
+  struct stat filestatus;
+  char format[80], telescope[80];
+
+  /* read fits header from file - return FALSE on error */
+  if (!gfits_fread_header (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't read primary header\n"); 
+    return (FALSE);
+  }
+
+  /* determine image table format */
+  db[0].format = DVO_FORMAT_UNDEF;
+  if (gfits_scan (&db[0].header, "FORMAT",  "%s", 1, format)) {
+    db[0].format = dvo_catalog_catformat (format);
+    if (db[0].format != DVO_FORMAT_UNDEF) goto got_format;
+  }
+  if (gfits_scan (&db[0].header, "TELESCOP",  "%s", 1, telescope)) {
+    if (!strncmp (telescope, "LONEOS", strlen("LONEOS"))) {
+      db[0].format = DVO_FORMAT_LONEOS; // special case for LONEOS
+      goto got_format;
+    }
+    if (!strncmp (telescope, "1.3m McGraw-Hill", strlen("1.3m McGraw-Hill"))) {
+      db[0].format = DVO_FORMAT_ELIXIR; // special case for ELIXIR
+      goto got_format;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "cannot determine image table format\n");
+  return (FALSE);
+
+got_format:
+  /* find number of images */
+  Nimage = 0;
+  gfits_scan (&db[0].header, "NIMAGES", "%d", 1, &Nimage);
+  if (stat (db[0].filename, &filestatus) == -1) {
+    if (VERBOSE) fprintf (stderr, "ERROR: failed to get status of image catalog\n");
+    exit (1);
+  }
+
+  /* get datatype size */
+  ImageSize = 0;
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  ImageSize = sizeof(Image);
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  ImageSize = sizeof(Image_Loneos);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  ImageSize = sizeof(Image_Elixir);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) ImageSize = sizeof(Image_Panstarrs_DEV_0);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) ImageSize = sizeof(Image_Panstarrs_DEV_1);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       ImageSize = sizeof(Image_PS1_DEV_1);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       ImageSize = sizeof(Image_PS1_DEV_2);
+
+  /* check that filesize makes sense */
+  size = Nimage*ImageSize + db[0].header.size;
+  if (size != filestatus.st_size) {
+    Ndata = (filestatus.st_size - db[0].header.size) / ImageSize;
+    if (VERBOSE) fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
+    if (VERBOSE) fprintf (stderr, "header: %d, data: %d\n", Nimage, Ndata);
+    if (!FORCE_READ) exit (1);
+    Nimage = Ndata;
+  } 
+
+  /* create a dummy ftable, theader set for this data */
+  /* (original table has NAXIS = 2, change to 0) */
+  /* gfits_modify (&db[0].header, "NAXIS", "%d", 1, 0); */
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  db[0].ftable.header = &db[0].theader;
+
+  if (db[0].format == DVO_FORMAT_INTERNAL)  	  gfits_table_mkheader_Image (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_LONEOS)    	  gfits_table_mkheader_Image_Loneos (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_table_mkheader_Image_Elixir (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_table_mkheader_Image_Panstarrs_DEV_0 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_1) gfits_table_mkheader_Image_Panstarrs_DEV_1 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_1)       gfits_table_mkheader_Image_PS1_DEV_1 (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PS1_DEV_2)       gfits_table_mkheader_Image_PS1_DEV_2 (&db[0].theader);
+    
+  /* read data from file */
+  Nbytes = ImageSize*Nimage;
+  ALLOCATE (db[0].ftable.buffer, char, Nbytes);
+  nbytes = fread (db[0].ftable.buffer, 1, Nbytes, db[0].f);
+  if (nbytes != Nbytes) {
+    if (VERBOSE) fprintf (stderr, "ERROR: problem loading image catalog\n");
+    exit (1);
+  } 
+
+  gfits_modify (&db[0].theader, "NAXIS2", "%d", 1, Nimage);
+  db[0].theader.Naxis[1] = Nimage;
+  db[0].ftable.size = gfits_data_size (&db[0].theader);
+  db[0].swapped = FALSE;  /* table does not have internal byte-order */
+  return (TRUE);
+}
+
+/* write out image db elements from vtable */
+int dvo_image_update_raw (FITS_DB *db, int VERBOSE) {
+
+  int i, Nx, Ny, Nrow, Nbytes;
+  int status, start, offset;
+  int *row;
+
+  if (VERBOSE) fprintf (stderr, "writing out %d images\n", db[0].vtable.Nrow);
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+  
+  /* write out complete header (no check on disk size?) */
+  status = fwrite (db[0].header.buffer, 1, db[0].header.size, db[0].f);
+  if (status != db[0].header.size) {
+    fprintf (stderr, "ERROR: failed writing data to image header\n");
+    exit (1);
+  }
+
+  /** this code is identical to gfits_fwrite_vtable, except without padding */
+  Nrow = db[0].vtable.Nrow;
+  row = db[0].vtable.row;
+  gfits_scan (db[0].vtable.header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (db[0].vtable.header, "NAXIS2", "%d", 1, &Ny);
+
+  /* file pointer is at beginning of desired table data */
+  start = ftell (db[0].f);
+  
+  for (i = 0; i < Nrow; i++) {
+    offset = start + Nx*row[i];
+    fseek (db[0].f, offset, SEEK_SET);
+    Nbytes = fwrite (db[0].vtable.buffer[i], sizeof (char), Nx, db[0].f);
+    if (Nbytes != Nx) { return (FALSE); }
+  }
+  return (TRUE);
+}
+
+/* write out complete image db table from ftable */
+int dvo_image_save_raw (FITS_DB *db, int VERBOSE) {
+
+  int status, Nx, Ny, size, Nbytes;
+
+  if (VERBOSE) fprintf (stderr, "writing out %d images\n", db[0].theader.Naxis[1]);
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+  
+  /* write out complete header (no check on disk size?) */
+  status = fwrite (db[0].header.buffer, 1, db[0].header.size, db[0].f);
+  if (status != db[0].header.size) {
+    fprintf (stderr, "ERROR: failed writing data to image header\n");
+    exit (1);
+  }
+
+  gfits_scan (db[0].ftable.header, "NAXIS1", "%d", 1, &Nx);
+  gfits_scan (db[0].ftable.header, "NAXIS2", "%d", 1, &Ny);
+  size = Nx * Ny;
+  Nbytes = fwrite (db[0].ftable.buffer, sizeof(char), size, db[0].f);
+  if (Nbytes != size) return (FALSE);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_photcode_ops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 17422)
@@ -0,0 +1,537 @@
+# include <dvo.h>
+
+/* we use a static variable to save the pre-loaded the photcodes.
+   photcode conversion functions refer to this table for ref values */
+
+/* We have three indexes:
+
+   table[0].hashcode provides the table photcode sequence for a photcode value
+   table[0].hashNsec provides the Nsec sequence for a photcode value
+   table[0].codeNsec provides the photcode value for an Nsec sequence
+
+   given photcode = table[0].code[i] and code = photcode[0].code
+
+   hashcode[code] = i
+   hashNsec[code] = Nsec or -1 if not a PRI/SEC code
+   codeNsec[Nsec] = code
+*/
+
+static PhotCodeData *photcodes = NULL;
+
+PhotCodeData *GetPhotcodeTable () {
+
+  /* allocate space to photcode table, free existing data */
+  if (photcodes == NULL) {
+    ALLOCATE (photcodes, PhotCodeData, 1);
+    photcodes[0].code = NULL;
+  }
+  return photcodes;
+}
+
+/* the static ZERO_POINT is used by programs as an approximate nominal */
+static double ZERO_POINT = 0.0;
+
+void SetZeroPoint (double ZP) {
+  ZERO_POINT = ZP;
+}
+
+double GetZeroPoint () {
+  return ZERO_POINT;
+}
+
+# define SCALE 0.001
+
+/********** photcode lookup functions **********/
+
+/* return photcode for given name */
+PhotCode *GetPhotcodebyName (char *name) {
+  
+  int i;
+
+  if (name == NULL) return (NULL);
+  
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      return (&photcodes[0].code[i]);
+    }
+  }
+  return (NULL);
+}
+/* return photcode.code for given name */
+int GetPhotcodeCodebyName (char *name) {
+  
+  int i;
+  
+  if (name == NULL) return (0);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      return (photcodes[0].code[i].code);
+    }
+  }
+  return (0);
+}
+/* return equivalent photcode for given name */
+PhotCode *GetPhotcodeEquivbyName (char *name) {
+  
+  int i, equiv;
+  
+  if (name == NULL) return (NULL);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      if (photcodes[0].code[i].equiv == 0) return (NULL);
+      equiv = photcodes[0].hashcode[photcodes[0].code[i].equiv];
+      if (equiv == -1) return (NULL);
+      return (&photcodes[0].code[equiv]);
+    }
+  }
+  return (NULL);
+}
+/* return equivalent photcode.code for given name */
+int GetPhotcodeEquivCodebyName (char *name) {
+  
+  int i, equiv;
+  
+  if (name == NULL) return (0);
+
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (!strcmp (photcodes[0].code[i].name, name)) {
+      if (photcodes[0].code[i].equiv == 0) return (0);
+      equiv = photcodes[0].hashcode[photcodes[0].code[i].equiv];
+      if (equiv == -1) return (0);
+      return (photcodes[0].code[equiv].code);
+    }
+  }
+  return (0);
+}
+
+/* return photcode for given code */
+PhotCode *GetPhotcodebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  return (&photcodes[0].code[entry]);
+}
+/* return photcode.code for given code */
+char *GetPhotcodeNamebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  return (photcodes[0].code[entry].name);
+}
+/* return equivalent photcode for given code */
+PhotCode *GetPhotcodeEquivbyCode (int code) {
+  
+  int entry, equiv;
+  
+  if (code < 0) return (NULL);
+  if (code > 0x10000) return (NULL);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (NULL);
+
+  if (photcodes[0].code[entry].equiv == 0) return (NULL);
+  equiv = photcodes[0].hashcode[photcodes[0].code[entry].equiv];
+
+  if (equiv == -1) return (NULL);
+  return (&photcodes[0].code[equiv]);
+}
+/* return equivalent photcode.code for given code */
+int GetPhotcodeEquivCodebyCode (int code) {
+  
+  int entry;
+  
+  if (code < 0) return (0);
+  if (code > 0x10000) return (0);
+
+  entry = photcodes[0].hashcode[code];
+  if (entry == -1) return (0);
+  return (photcodes[0].code[entry].equiv);
+}
+
+int GetPhotcodeNsec (int code) {
+  
+  int Nsec;
+  
+  if (code < 0) return (-1);
+  if (code > 0x10000) return (-1);
+
+  Nsec = photcodes[0].hashNsec[code];
+  return (Nsec);
+}
+
+/* Nsec of 0 is PRI */
+PhotCode *GetPhotcodebyNsec (int Nsec) {
+  
+  int Ncode;
+  int Nseq;
+
+  if (Nsec > photcodes[0].Nsecfilt) return (NULL);
+  if (Nsec < 0) return (NULL);
+  
+  Ncode = photcodes[0].codeNsec[Nsec];
+  if (Ncode < 0) return (NULL);
+
+  Nseq = photcodes[0].hashcode[Ncode];
+  return (&photcodes[0].code[Nseq]);
+}
+
+int GetPhotcodeNsecfilt () {
+  return (photcodes[0].Nsecfilt);
+}
+
+/* ALLOCATE and return list of all photcodes
+   with photcode.equiv == code */
+int *GetPhotcodeEquivList (int code, int *nlist) {
+
+  int i, Nlist;
+  int *list;
+
+  ALLOCATE (list, int, MAX (1, photcodes[0].Ncode));
+  Nlist = 0;
+  for (i = 0; i < photcodes[0].Ncode; i++) {
+    if (photcodes[0].code[i].equiv != code) continue;
+    list[Nlist] = photcodes[0].code[i].code;
+    Nlist ++;
+  }
+  REALLOCATE (list, int, MAX (1, Nlist));
+
+  *nlist = Nlist;
+  return (list);
+}
+
+/******** photometry conversion functions *********/
+float PhotInst (Measure *measure) {
+
+  int Np;
+  float M;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    M = measure[0].M;
+    return (M);
+  }
+
+  M = measure[0].M - measure[0].dt - ZERO_POINT;
+	  
+  return (M);
+
+}
+
+float PhotCat (Measure *measure) {
+
+  int Np;
+  float Mcat;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].M;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+  
+  return (Mcat);
+}
+
+float PhotSys (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Msys, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Msys = measure[0].M;
+    return (Msys);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mcat);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Msys = Mcat + Mcol;
+  return (Msys);
+}
+
+float PhotRel (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Mrel, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].M;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mrel = measure[0].M - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C - measure[0].Mcal;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mrel);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mrel += Mcol;
+  return (Mrel);
+}
+
+/* return calibrated magnitude from measure for given photcode */
+float PhotCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Np; 
+  float Mcal, Mrel, Mcol, mc, Mc;
+
+  if (code == NULL) return NAN;
+
+  /* code must be the matching PRI/SEC code for this measurement or an equivalent ALT */
+  Np = photcodes[0].hashcode[thisone[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mrel = thisone[0].M;
+    return (Mrel);
+  }
+  if (code[0].code != photcodes[0].code[Np].equiv) return (NAN);
+
+  Mcal = PhotRel (thisone, average, secfilt) + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mcal);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Mcal += Mcol;
+  return (Mcal);
+}
+
+/* color term may not use DEP magnitude */
+float PhotColorForCode (Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Ns1, Ns2, Ns;
+  float m1, m2, mc;
+  PhotCode *color;
+
+  m1 = m2 = NAN;
+
+  if (measure == NULL) {
+    Ns1 = photcodes[0].hashNsec[code[0].c1];
+    Ns2 = photcodes[0].hashNsec[code[0].c2];
+  
+    m1 = (Ns1 == -1) ? NAN : secfilt[Ns1].M;
+    m2 = (Ns2 == -1) ? NAN : secfilt[Ns2].M;
+    mc = (isnan(m1) || isnan(m2)) ? NAN : (m1 - m2);
+    return (mc);
+  }
+
+  /* find magnitude matching first color term */
+  color = GetPhotcodebyCode (code[0].c1);
+  if (color == NULL) return (NAN);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nmeasure) && (isnan(m1)); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m1 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m1 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+
+  /* find magnitude matching second color term */
+  color = GetPhotcodebyCode (code[0].c2);
+  if (color == NULL) return (NAN);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nmeasure) && (isnan(m2)); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m2 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m2 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+  mc = (isnan(m1) || isnan(m2)) ? NAN : (m1 - m2);
+  return (mc);
+}
+
+/* return calibrated magnitude from average/secfilt for given photcode */
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Ns;
+  float Mave, Mref, Mcol, mc;
+  double Mc;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mref);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mref += Mcol;
+  return (Mref);
+}
+
+/***/
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float Mave;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  return (Mave);
+}
+
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float dM;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  dM  = (Ns == -1) ? NAN : secfilt[Ns].dM;
+  return (dM);
+}
+
+// XXX return NAN or NAN_S_SHORT? (secfilt->Xm is short)
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  short Mi;
+  float Xm;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mi = (Ns == -1) ? NAN : secfilt[Ns].Xm;
+  Xm = (isnan(Mi)) ? -1.0 : pow (10.0, 0.01*Mi);
+  return (Xm);
+}
+
+/* given a photcode pair c1 & c2, return the color of this star (NaN if not found) */
+int PhotColor (Average *average, SecFilt *secfilt, Measure *measure, int c1, int c2, double *color) {
+
+  int i, Ns;
+  double M1, M2, dM;
+  PhotCode *code;
+  
+  code = GetPhotcodebyCode (c1);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_REF) {
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      if (measure[i].photcode == c1) {
+	M1 = measure[i].M;
+	goto filter1;
+      }
+    }	
+    return (FALSE);
+  } else {
+    Ns = photcodes[0].hashNsec[code[0].code];
+    M1 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+
+filter1:
+  code = GetPhotcodebyCode (c2);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_REF) {
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      if (measure[i].photcode == c2) {
+	M2 = measure[i].M;
+	goto filter2;
+      }
+    }	
+    return (FALSE);
+  } else {
+    Ns = photcodes[0].hashNsec[code[0].code];
+    M2 = (Ns == -1) ? NAN : secfilt[Ns].M;
+  }	
+  
+filter2:
+
+  dM = M1 - M2;
+  *color = dM;
+  
+  return (TRUE);
+}
+
+/* photcode table should have the following format: 
+
+# code name     type  zero  airmass  offset  c1 c2  slope  <color>  primary
+1    B        pri   24.0  0.15     -       -  -   -      -        -
+2    B        pri   24.0  0.15     -       -  -   -      -        -
+3    B1       sec   22.5  0.18     0.15    1  2   0.10   0.50     1
+1000 USNO_B   ref   -     -        -       -  -   -      -        -
+
+*/
+
+
+/*
+  Nc1 = photcodes[0].code[Np].c1;
+  Ns1 = photcodes[0].hashNsec[Nc1];
+
+  Nc2 = photcodes[0].code[Np].c2;
+  Ns2 = photcodes[0].hashNsec[Nc2];
+
+  Xlam = photcodes[0].code[Np].X[0];
+  Klam = photcodes[0].code[Np].K;
+  
+  m1 = (Ns1 == -1) ? average[0].M : secfilt[Ns1].M;
+  m2 = (Ns2 == -1) ? average[0].M : secfilt[Ns2].M;
+*/
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvosorts.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvosorts.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/dvosorts.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include <dvo.h>
+
+/* several dvo-specific sorting functions used in a number of locations */
+
+/* values are ave[i].R, ave[i].D, ave[i].M */
+void sortave (Average *ave, int N) {
+
+# define SWAPFUNC(A,B){ Average tmp; tmp = ave[A]; ave[A] = ave[B]; ave[B] = tmp; }
+# define COMPARE(A,B)(ave[A].R < ave[B].R)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort subset by image[subset[i]].tzero */
+void sort_image_subset (Image *image, int *subset, int N) {
+
+# define SWAPFUNC(A,B){ int tmp; tmp = subset[A]; subset[A] = subset[B]; subset[B] = tmp; }
+# define COMPARE(A,B)(image[subset[A]].tzero < image[subset[B]].tzero)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort a coordinate pair (X,Y) and the associated index (S) */
+void sort_coords_index (double *X, double *Y, int *S, int N) {
+  
+# define SWAPFUNC(A,B){ double dtmp; int itmp; \
+  dtmp = X[A]; X[A] = X[B]; X[B] = dtmp; \
+  dtmp = Y[A]; Y[A] = Y[B]; Y[B] = dtmp; \
+  itmp = S[A]; S[A] = S[B]; S[B] = itmp; \
+}
+# define COMPARE(A,B)(X[A] < X[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sort_stars_ra (Stars *stars, int N) {
+
+# define SWAPFUNC(A,B){ Stars tmp; tmp = stars[A]; stars[A] = stars[B]; stars[B] = tmp; }
+# define COMPARE(A,B)(stars[A].R < stars[B].R)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+void sort_regions (SkyRegion *region, int N) {
+
+# define SWAPFUNC(A,B){ SkyRegion tmp; tmp = region[A]; region[A] = region[B]; region[B] = tmp; }
+# define COMPARE(A,B)(region[A].Dmin < region[B].Dmin)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/fits_db.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/fits_db.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/fits_db.c	(revision 17422)
@@ -0,0 +1,234 @@
+# include <dvo.h>
+
+/* init the db structure */
+int gfits_db_init (FITS_DB *db) {
+
+  db[0].f             = NULL;
+  db[0].filename      = NULL;
+  db[0].header.buffer = NULL;
+  db[0].matrix.buffer = NULL;
+  db[0].theader.buffer = NULL;
+  db[0].ftable.buffer = NULL;
+  db[0].ftable.header = &db[0].theader;
+
+  db[0].vtable.header = NULL;
+  db[0].vtable.buffer = NULL;
+  db[0].vtable.row    = NULL;
+  return (TRUE);
+}
+
+/* create an empty db */
+int gfits_db_create (FITS_DB *db) {
+  gfits_init_header (&db[0].header);    
+  db[0].header.extend = TRUE;
+  gfits_create_header (&db[0].header);
+  gfits_create_matrix (&db[0].header, &db[0].matrix);
+  gfits_print (&db[0].header, "NEXTEND", "%d", 1, 1);
+  db[0].ftable.header = &db[0].theader;
+  return (TRUE);
+}
+
+int gfits_db_lock (FITS_DB *db, char *filename) {
+  
+  /* database name must be set first */
+  if (filename == NULL) {
+    fprintf (stderr, "db file is not set\n");
+    return (FALSE);
+  }
+
+  /* database handle must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* lock & open database */
+  db[0].f = fsetlockfile (filename, db[0].timeout, db[0].lockstate, &db[0].dbstate);
+  if (db[0].f == NULL) {
+    if (db[0].dbstate == LCK_MISSING) {
+      fprintf (stderr, "no data in db %s\n", filename);
+    } else {
+      fprintf (stderr, "cannot set lock on db file %s\n", filename);
+    }
+    return (FALSE);
+  }
+  
+  db[0].filename = strcreate (filename);
+  return (TRUE);
+}
+
+/* load the complete db table into memory - load first extension, do not validate EXTNAME */
+int gfits_db_load (FITS_DB *db) {
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* init & load in FITS table data - return FALSE on error */
+  if (!gfits_fread_header (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't read primary header\n"); 
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+    fprintf (stderr, "can't read primary matrix");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't read table header");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  if (!gfits_fread_ftable_data (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't read table data");
+    gfits_db_free (db);
+    return (FALSE);
+  }
+  db[0].swapped = FALSE;  /* table does not have internal byte-order */
+  return (TRUE);
+}
+
+/* load the data from Nrows from table starting at start; load header, etc if needed */
+int gfits_db_load_segment (FITS_DB *db, int start, int Nrows) {
+
+  int Nskip;
+
+  /* database name must be set first */
+  if (db == NULL) {
+    fprintf (stderr, "db handle is not set\n");
+    return (FALSE);
+  }
+
+  /* database name must be opened */
+  if (db[0].f == NULL) {
+    fprintf (stderr, "db is not opened\n");
+    return (FALSE);
+  }
+
+  /* position to start of file */
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  /* load or skip header */
+  if (db[0].header.buffer == NULL) {
+    if (!gfits_fread_header (db[0].f, &db[0].header)) {
+      fprintf (stderr, "can't read primary header\n"); 
+      return (FALSE);
+    }
+  } else {
+    Nskip = db[0].header.size;
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* load or skip matrix */
+  if (db[0].matrix.buffer == NULL) {
+    if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+      fprintf (stderr, "can't read primary matrix");
+      return (FALSE);
+    }
+  } else {
+    Nskip = gfits_data_size (&db[0].header);
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* load or skip table header */
+  if (db[0].theader.buffer == NULL) {
+    if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+      fprintf (stderr, "can't read table header");
+      return (FALSE);
+    }
+  } else {
+    Nskip = db[0].header.size;
+    Fseek (db[0].f, Nskip, SEEK_CUR);
+  }
+
+  /* read table segment into vtable */
+  if (!gfits_fread_vtable_range (db[0].f, &db[0].vtable, start, Nrows)) {
+    fprintf (stderr, "can't read table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* write complete db file */
+int gfits_db_save (FITS_DB *db) {
+
+  /* write all data to file */
+  make_backup (db[0].filename);
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  if (!gfits_fwrite_header  (db[0].f, &db[0].header)) {
+    fprintf (stderr, "can't write primary header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_matrix  (db[0].f, &db[0].matrix)) {
+    fprintf (stderr, "can't write primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_Theader (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't write table header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_table   (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't write table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* write vtable to db file (also appends rows to the end of the table) */
+int gfits_db_update (FITS_DB *db) {
+
+  /* this section is not valid if we have changed the size of header, matrix, theader */
+
+  /* write subset to file */
+  // make_backup (db[0].filename);  /*** drop this!!?? ***/
+  Fseek (db[0].f, 0, SEEK_SET);
+
+  /* do we revert to the old version if this fails? */
+  if (!gfits_fwrite_header   (db[0].f, &db[0].header))  {
+    fprintf (stderr, "can't update primary header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_matrix   (db[0].f, &db[0].matrix))  {
+    fprintf (stderr, "can't update primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_Theader  (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't update table header");
+    return (FALSE);
+  }
+  if (!gfits_fwrite_vtable   (db[0].f, &db[0].vtable))  {
+    fprintf (stderr, "can't update table data");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* free memory associated with db handle */
+int gfits_db_free (FITS_DB *db) {
+  gfits_free_header (&db[0].header);
+  gfits_free_matrix (&db[0].matrix);
+  gfits_free_header (&db[0].theader);
+  gfits_free_table  (&db[0].ftable);
+  gfits_free_vtable (&db[0].vtable);
+  if (db[0].filename != NULL) {
+    free (db[0].filename);
+    db[0].filename = NULL;
+  }
+  return (TRUE);
+}
+
+/* close the db files (close open file & unlock) */
+int gfits_db_close (FITS_DB *db) {
+  if (db[0].f == NULL) return (TRUE);
+  fclearlockfile (db[0].filename, db[0].f, db[0].lockstate, &db[0].dbstate);
+  db[0].f = NULL;
+  return (TRUE);
+}  
+
+/* for this to be atomic, need to unlink before we unlock */
+/* unlink file here if resulting file is empty? */
+/* if (db[0].Nrow == 0) && (lockstate != LCK_SOFT)) unlink (dBFile); */
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/imreg_datatypes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 17422)
@@ -0,0 +1,50 @@
+# include <dvo.h>
+
+char typename[N_TYPE][32] = {"none", "object", "dark", "bias", "flat", "mask", "fringe", "scatter", "modes", "frpts", "any"};
+char typecode[N_TYPE]     = {'x',    'o',      'd',    'b',    'f',    'm',    'r',      's',       'M',     'F'};
+char modename[N_MODE][32] = {"none", "MEF", "SPLIT", "SINGLE", "CUBE", "SLICE", "MODES"};
+
+int get_image_type (char *name) {
+
+  int i;
+  
+  for (i = 0; i < N_TYPE; i++) {
+    if (!strncasecmp (name, typename[i], strlen(typename[i]))) {
+      return (i);
+    }
+  }
+
+  /* special cases */
+  if (!strncasecmp (name, "SKYFLAT", strlen("SKYFLAT"))) {
+    return (T_FLAT);
+  }
+
+  return (T_UNDEF);
+}
+
+char *get_type_name (int type) {
+
+  if (type < T_NONE) return ("undef");
+  if (type >= N_TYPE) return ("undef");
+  return (typename[type]);
+}
+
+int get_image_mode (char *name) {
+
+  int i;
+  
+  for (i = 0; i < N_MODE; i++) {
+    if (!strncasecmp (name, modename[i], strlen(modename[i]))) {
+      return (i);
+    }
+  }
+  return (M_UNDEF);
+}
+
+char *get_mode_name (int mode) {
+
+  if (mode < M_NONE) return ("undef");
+  if (mode >= N_MODE) return ("undef");
+  return (modename[mode]);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/mosaic_astrom.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 17422)
@@ -0,0 +1,131 @@
+# include <dvo.h>
+
+/* chip-match table: j = ChipMatch[i], images[j] is DIS for images[i] WRP */
+
+static int    iDIS = -1;      // DIS entry in ChipMatch[]
+static int    Ndis = 0;
+static int    *DISentry = NULL;
+static e_time *DIStzero = NULL;
+static int    *ChipMatch = NULL;
+
+/* what is the currently registered mosaic image? */
+int GetRegisteredMosaic () {
+  return (iDIS);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage (Image *images, int Nimages, int entry) {
+
+  int status;
+
+  if (ChipMatch == NULL) {
+    status = FindMosaicForImage_TableSearch (images, Nimages, entry);
+    return (status);
+  }
+  status = FindMosaicForImage_MatchSearch (images, Nimages, entry);
+  return (status);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage_TableSearch (Image *images, int Nimages, int entry) {
+
+  e_time tzero;
+  int i;
+
+  /* search backwards for image with same time and type DIS */
+
+  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
+    /* not a wrp image, do nothing */
+    return (TRUE); /* error or not */
+  }
+
+  tzero = images[entry].tzero;
+  
+  for (i = entry - 1; i >= 0; i--) {
+    if (images[i].tzero != tzero) continue;
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+
+    /* found a valid match */
+    RegisterMosaic (&images[i].coords);
+    iDIS = i;
+    return (TRUE);
+  }
+
+  for (i = entry + 1; i < Nimages; i++) {
+    if (images[i].tzero != tzero) continue;
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+
+    /* found a valid match */
+    RegisterMosaic (&images[i].coords);
+    iDIS = i;
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+/* given an image array and a current entry of type WRP, find matching DIS & Register it */
+int FindMosaicForImage_MatchSearch (Image *images, int Nimages, int entry) {
+
+  int N;
+
+  if (strcmp(&images[entry].coords.ctype[4], "-WRP")) {
+    /* not a wrp image, do nothing */
+    return (TRUE); /* error or not */
+  }
+
+  if (ChipMatch == NULL) return (FALSE);
+
+  N = ChipMatch[entry];
+  if (N < 0) return (FALSE);
+  if (N >= Nimages) return (FALSE);
+
+  RegisterMosaic (&images[N].coords);
+  iDIS = N;
+  return (TRUE);
+}
+
+int BuildChipMatch (Image *images, int Nimages) {
+
+  int i, j, NDIS;
+
+  if (DISentry != NULL) free (DISentry);
+  if (DIStzero != NULL) free (DIStzero);
+  if (ChipMatch != NULL) free (ChipMatch);
+
+  /* find all DIS images, save tzero (& instrument) */
+  Ndis = 0;
+  NDIS = 100;
+  ALLOCATE (DISentry, int, NDIS);
+  ALLOCATE (DIStzero, e_time, NDIS);
+
+  for (i = 0; i < Nimages; i++) {
+    if (strcmp(&images[i].coords.ctype[4], "-DIS")) continue;
+    DISentry[Ndis] = i;
+    DIStzero[Ndis] = images[i].tzero;
+    Ndis ++;
+    if (Ndis >= NDIS) {
+      NDIS += 100;
+      REALLOCATE (DISentry, int, NDIS);
+      REALLOCATE (DIStzero, e_time, NDIS);
+    }
+  }
+
+  /* find all matched WRP images */
+  ALLOCATE (ChipMatch, int, Nimages);
+  for (i = 0; i < Nimages; i++) {
+    ChipMatch[i] = -1;
+    if (strcmp(&images[i].coords.ctype[4], "-WRP")) continue;
+    for (j = 0; j < Ndis; j++) {
+      if (DIStzero[j] != images[i].tzero) continue;
+      ChipMatch[i] = DISentry[j];
+      goto got_match;
+    }
+
+    fprintf (stderr, "WARNING: can't find matching mosaic \n");
+    ChipMatch[i] = -2;
+
+  got_match:
+    continue;
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/photfits.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/photfits.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/photfits.c	(revision 17422)
@@ -0,0 +1,258 @@
+# include <dvo.h>
+# define MAX_ORDER 3
+
+/**** XXXX warning: these functions have not been corrected to handle 
+      the change of Mcal from millimags to mags
+      I don't think most functions are using the Mcal polynomial terms in 
+      any case... ****/
+
+/* all terms of order > 0 are stored as 16bit floats 
+   the zero-order term is stored as a short int (-32k,+32k) */
+
+/* convert double to low-precision short int */
+short int putMi (double value) {
+  
+  double T;
+  unsigned int I;
+
+  if (value > 0) {
+    T = 1000 * log10 (value);
+    I = MAX (MIN (T, 16383), -16382) + 0x3fff;
+    return (I);
+  }
+  if (value < 0) {
+    T = -value;
+    T = 1000 * log10 (T);
+    I = MAX (MIN (T, 16383), -16382) + 0xbfff;
+    return (I);
+  }
+  if (value == 0) {
+    I = 0;
+    return (0);
+  }
+  return (NAN_S_SHORT);
+}
+
+/* convert low-precision short int to double */
+double getMi (short int value) {
+  
+  double T, V, sign;
+  short int I;
+
+  if (value == 0) {
+    return (0.0);
+  }
+  if (value & 0x8000) {
+    sign = -1;
+    I = value & 0x7fff;
+  } else {
+    sign = +1;
+    I = value;
+  }
+    
+  T = (I - 0x3fff) / 1000.0;
+  V = sign * pow (10.0, T);
+  return (V);
+}
+
+/* convert image parameters to c[i] coeffs */
+void returnMcal (Image *image, double *c) {
+
+  switch (image[0].order) {
+  case 0:
+    c[0] = image[0].Mcal;
+    return;
+  case 1:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].My);
+    return;
+  case 2:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].My);
+    c[4] = getMi (image[0].Mxy);
+    c[5] = getMi (image[0].Myy);
+    return;
+  case 3:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].Mxxx);
+    c[4] = getMi (image[0].My);
+    c[5] = getMi (image[0].Mxy);
+    c[6] = getMi (image[0].Mxxy);
+    c[7] = getMi (image[0].Myy);
+    c[8] = getMi (image[0].Mxyy);
+    c[9] = getMi (image[0].Myyy);
+    return;
+  case 4:
+    c[0] = image[0].Mcal;
+    c[1] = getMi (image[0].Mx);
+    c[2] = getMi (image[0].Mxx);
+    c[3] = getMi (image[0].Mxxx);
+    c[4] = getMi (image[0].Mxxxx);
+    c[5] = getMi (image[0].My);
+    c[6] = getMi (image[0].Mxy);
+    c[7] = getMi (image[0].Mxxy);
+    c[8] = getMi (image[0].Mxxxy);
+    c[9] = getMi (image[0].Myy);
+    c[10] = getMi (image[0].Mxyy);
+    c[11] = getMi (image[0].Mxxyy);
+    c[12] = getMi (image[0].Myyy);
+    c[13] = getMi (image[0].Mxyyy);
+    c[14] = getMi (image[0].Myyyy);
+    return;
+  default:
+    c[0] = 0;
+    return;
+  }
+}
+
+void assignMcal (Image *image, double *c, int order) {
+
+  image[0].order = order;
+
+  switch (order) {
+  case 0:
+    image[0].Mcal = c[0];
+    return;
+  case 1:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].My    = putMi(c[2]);
+    return;
+  case 2:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].My    = putMi(c[3]);
+    image[0].Mxy   = putMi(c[4]);
+    image[0].Myy   = putMi(c[5]);
+    return;
+  case 3:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].Mxxx  = putMi(c[3]);
+    image[0].My    = putMi(c[4]);
+    image[0].Mxy   = putMi(c[5]);
+    image[0].Mxxy  = putMi(c[6]);
+    image[0].Myy   = putMi(c[7]);
+    image[0].Mxyy  = putMi(c[8]);
+    image[0].Myyy  = putMi(c[9]);
+    return;
+  case 4:
+    image[0].Mcal = c[0];
+    image[0].Mx    = putMi(c[1]);
+    image[0].Mxx   = putMi(c[2]);
+    image[0].Mxxx  = putMi(c[3]);
+    image[0].Mxxxx = putMi(c[4]);
+    image[0].My    = putMi(c[5]);
+    image[0].Mxy   = putMi(c[6]);
+    image[0].Mxxy  = putMi(c[7]);
+    image[0].Mxxxy = putMi(c[8]);
+    image[0].Myy   = putMi(c[9]);
+    image[0].Mxyy  = putMi(c[10]);
+    image[0].Mxxyy = putMi(c[11]);
+    image[0].Myyy  = putMi(c[12]);
+    image[0].Mxyyy = putMi(c[13]);
+    image[0].Myyyy = putMi(c[14]);
+    return;
+  default:
+    image[0].Mcal = 0.0;
+    image[0].order = 0;
+    return;
+  }
+}
+
+/* return value of image fit at x,y */
+double applyMcal (Image *image, double x, double y) {
+
+  double Mcal, c[15];
+
+  returnMcal (image, c);  /* convert image parameters to c[i] coeffs */
+  Mcal = 0.0;
+  switch (image[0].order) {
+  case 0:
+    Mcal = c[0];
+    break;
+  case 1:
+    Mcal = c[0] + x*c[1] + y*c[2];
+    break;
+  case 2:
+    Mcal = c[0] + x*(c[1] + x*c[2]) + y*(c[3] + x*c[4] + y*c[5]);
+    break;
+  case 3:
+    Mcal = c[0] + x*(c[1] + x*(c[2] + x*c[3])) + y*(c[4] + x*(c[5] + x*c[6]) + y*(c[7] + x*c[8] + y*c[9]));
+    break;
+  case 4:
+    Mcal = c[0] + x*(c[1] + x*(c[2] + x*(c[3] + x*c[4]))) + y*(c[5] + x*(c[6] + x*(c[7] + x*c[8])) + y*(c[9] + x*(c[10] + x*c[11]) + y*(c[12] + x*c[13] + y*c[14])));
+    break;
+  }
+  return (Mcal);
+}
+
+double findscatter (double *X, double *Y, double *M, double *dM, int N, double *c, int order) {
+  
+  int i;
+  double *x, *y, *m, *dm;
+  double S, S2, s, dS;
+
+  S2 = S = 0;
+  if (N < 2) {
+    return (0.0);
+  }
+  
+  x = X; y = Y; m = M; dm = dM;
+  switch (order) {
+  case 0:
+    for (i = 0; i < N; i++, m++, dm++) {
+      dS  = *m - c[0];
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 1:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*c[1] + *y*c[2];
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 2:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*c[2]) + *y*(c[3] + *x*c[4] + *y*c[5]);
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 3:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*(c[2] + *x*c[3])) + *y*(c[4] + *x*(c[5] + *x*c[6]) + *y*(c[7] + *x*c[8] + *y*c[9]));
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  case 4:
+    for (i = 0; i < N; i++, m++, x++, y++, dm++) {
+      s   = c[0] + *x*(c[1] + *x*(c[2] + *x*(c[3] + *x*c[4]))) + *y*(c[5] + *x*(c[6] + *x*(c[7] + *x*c[8])) + *y*(c[9] + *x*(c[10] + *x*c[11]) + *y*(c[12] + *x*c[13] + *y*c[14])));
+      dS  = *m - s;
+      *dm = dS;
+      S  += dS;
+      S2 += dS*dS;
+    }
+    break;
+  }
+  S = S / N;
+  S2 = sqrt (S2 / N - S*S);
+  return (S2);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/skydb.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/skydb.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/skydb.c	(revision 17422)
@@ -0,0 +1,400 @@
+
+typedef struct {
+  float r, d;
+} SkyCoord;
+
+/* each region is bounded on the sky by lines of constant RA & DEC.
+   the region represents a specified depth, and it may or may not have
+   children.  The start and end of the children in the array are childS and childE 
+   If the region at the given depth is populated with an object table, then 'object' is TRUE.
+   If the region at the given depth is populated with an image table, then 'image' is TRUE.
+*/   
+
+typedef struct {
+  char  name[16];
+  float Rmin, Rmax;
+  float Dmin, Dmax;
+  int   childS, childE;
+  char  depth, child;
+  char  object, image;
+} SkyRegion; /* 48 bytes */ 
+
+/* I have defined no accelerators other than the table hierarchy */
+
+/* find region which overlaps c at given depth (-1 : max depth) */
+SkyRegion *SkyFindPoint (SkyRegion *db, SkyCoord c, int depth) {
+  
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (No == -1) && (i < Ne); i++) {
+      if (c.r < db[i].Rmin) continue;
+      if (c.r > db[i].Rmax) continue;
+      if (c.d < db[i].Dmin) continue;
+      if (c.d > db[i].Dmax) continue;
+      // got the needed region at this depth
+      No = i;
+    }
+    if (No == -1) return ((SkyRegion *) NULL);
+    if (depth == db[No].depth) return (&db[No]);
+    if ((depth > db[No].depth) && !db[No].child) return ((SkyRegion *) NULL);
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    Ns = db[No].childS;
+    Ne = db[No].childE;
+
+    if ((depth > db[No].depth) && db[No].child) continue;
+    return (&db[No]);
+  }
+}
+
+/* find regions at all levels which overlap c */
+SkyRegion **SkyFindLevels (SkyRegion *db, SkyCoord c, int *Nregion) {
+
+  SkyRegion **region;
+  
+  Ns = 0;
+  Ne = 1;
+
+  N = 0;
+  NREGION = 10;
+  ALLOCATE (region, SkyRegion *, NREGION);
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (No == -1) && (i < Ne); i++) {
+      if (c.r < db[i].Rmin) continue;
+      if (c.r > db[i].Rmax) continue;
+      if (c.d < db[i].Dmin) continue;
+      if (c.d > db[i].Dmax) continue;
+      // got the needed region at this depth
+      No = i;
+    }
+    if (No == -1) return ((SkyRegion *) NULL);
+
+    region[N] = &db[No];
+    N++;
+    if (N == NREGION) {
+      NREGION += 10;
+      REALLOCATE (region, SkyRegion *, NREGION);
+    }      
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    if (db[No].child) {
+      Ns = db[No].childS;
+      Ne = db[No].childE;
+    } else {
+      *Nregion = N; 
+      return (region);
+    }
+  }
+}
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, *nlist) {
+
+  int Nlist;
+  SkyRegion *list, *new, *sub;
+
+  while (c1.r > 360.0) c1.r -= 360.0;
+  while (c1.r <   0.0) c1.r += 360.0;
+  while (c2.r > 360.0) c2.r -= 360.0;
+  while (c2.r <   0.0) c2.r += 360.0;
+
+  /* check on c1.r > c2.r : cross boundary */
+  if (c1.r > c2.r) {
+    c0 = c2; c0.r = 360.0;
+    list1 = SkyFindArea (db, c1, c0, &Nlist1);
+    c0 = c1; c0.r = 0.0;
+    list2 = SkyFindArea (db, c0, c2, &Nlist2);
+    Nlist = Nlist1 + Nlist2;
+    ALLOCATE (list, SkyRegion *, Nlist);
+    memcpy (&list[0], list1, Nlist1*sizeof());
+    memcpy (&list[Nlist1], list2, Nlist2*sizeof());
+    free (list1);
+    free (list2);
+    *nlist = Nlist;
+    return (list);
+  }    
+
+  Nlist = 1;
+  ALLOCATE (list, SkyRegion *, 1);
+  list[0] = &db[0];
+  getchild = db[0].child;
+
+  while (getchild) {
+
+    getchild = FALSE;
+    Nnew = 0;
+    NNEW = Nlist + 100;
+    ALLOCATE (new, SkyRegion *, NNEW);
+
+    for (i = 0; i < Nlist; i++) {
+      if (list[i][0].child) {
+	sub = SkyFindAreaDB(db, list[i], c1, c2, &Nsub);
+	if (Nnew + Nsub == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, SkyRegion *, NNEW);
+	}
+	for (i = 0; i < Nsub; i++) {
+	  getchild |= sub[i][0].child;
+	  new[Nnew] = sub[i];
+	  Nnew++;
+	}
+	free (sub);
+      } else {
+	new[Nnew] = list[i];
+	Nnew ++;
+	if (Nnew == NNEW) {
+	  NNEW += 100;
+	  REALLOCATE (new, SkyRegion *, NNEW);
+	}
+      }
+    }
+
+    free (list);
+    list = new;
+    Nlist = Nnew;
+  }
+
+  return (list);
+  *nlist = Nlist;
+
+}
+
+/* ?? */
+SkyRegion *SkyFindAreaDB (SkyRegion *db, SkyRegion *ref, SkyCoord c1, SkyCoord c2, int *Nregion) {
+
+  SkyRegion **region;
+  
+  Ns = ref[0].childS;
+  Ne = ref[0].childE;
+
+  N = 0;
+  NREGION = 100;
+  ALLOCATE (region, SkyRegion *, NREGION);
+
+  /* c1 min, c2 max */
+
+  for (i = Ns; (No == -1) && (i < Ne); i++) {
+    if (c2.r < db[i].Rmin) continue;
+    if (c1.r > db[i].Rmax) continue;
+    if (c2.d < db[i].Dmin) continue;
+    if (c1.d > db[i].Dmax) continue;
+
+    region[N] = &db[i];
+    N++;
+    if (N == NREGION) {
+      NREGION += 100;
+      REALLOCATE (region, SkyRegion *, NREGION);
+    }      
+  }
+  *Nregion = N; 
+  return (region);
+}
+
+SkyRegion *SkyBuildTable (SkyRegion *seed, int Nseed, int level, int depth) {
+
+  /* given a table of Rmin, Dmin, Rmax, Dmax, name, at level 3, generate
+     all higher levels and 'depth' extra levels */
+
+  /* levels:
+     0 - fullsky.cpt
+     1 - n????.cpt / s????.cpt
+     2 - r????.cpt
+     3 - ????.cpt
+     4 - ????.??.cpt
+  */
+  
+  SkyRegion *db;
+
+  /* allocate at least 30 for levels 0 & 1 */
+  NREGION = 100;
+  ALLOCATE (db, SkyRegion, NREGION);
+
+  /* full sky */
+  strcpy (db[0].name, "fullsky.cpt");
+  db[0].Rmin =   0; db[0].Rmax = 360;
+  db[0].Dmin = -90; db[0].Dmax =  90;
+  db[0].depth = 0;
+  db[0].child = FALSE;
+  N = 1;
+
+  db[0].childS = N;
+  /* north dec bands */
+  for (dec = 0; dec < 90; dec += 7.5) {
+    sprintf (db[N].name, "n%04d.cpt", (int) 100*dec);
+    db[N].Rmin =   0; db[N].Rmax = 360;
+    db[N].Dmin = dec; db[N].Dmax = dec + 7.5;
+    db[N].depth = 1;
+    db[N].child = FALSE;
+    N++;
+  }
+  /* south dec bands */
+  for (dec = 0; dec > -90; dec -= 7.5) {
+    sprintf (db[N].name, "s%04d.cpt", (int) 100*dec);
+    db[N].Rmin =   0;       db[N].Rmax = 360;
+    db[N].Dmin = dec - 7.5; db[N].Dmax = dec;
+    db[N].depth = 1;
+    db[N].child = FALSE;
+    N++;
+  }
+  db[0].childE = N;
+
+  /* subdivide dec bands based on seed */
+  Rnumber = 0;
+  childS = db[0].childS;
+  childE = db[0].childE;
+  for (i = childS; i < childE; i++) {
+
+    Nnew = 0;
+    NNEW = 100;
+    ALLOCATE (new, SkyRegion, NNEW);
+
+    for (j = 0; j < Nseed; j++) {
+      if (seed[j].Rmin > db[i].Rmax) continue;
+      if (seed[j].Rmax < db[i].Rmin) continue;
+      if (seed[j].Dmin > db[i].Dmax) continue;
+      if (seed[j].Dmax < db[i].Dmin) continue;
+      
+      for (k = 0; k < Nnew; k++) {
+	if ((seed[j].Rmin == new[k].Rmin) && 
+	    (seed[j].Rmax == new[k].Rmax)) { 
+	  goto next_seed;
+	}
+	if ((seed[j].Rmin == new[k].Rmin) ^^ 
+	    (seed[j].Rmax != new[k].Rmax)) { 
+	  fprintf (stderr, "inconsistent blocks in seed file\n");
+	  return (NULL);
+	}
+      }	
+      new[Nnew].Rmin = seed[j].Rmin;
+      new[Nnew].Rmax = seed[j].Rmax;
+      new[Nnew].Dmin = db[i].Dmin;
+      new[Nnew].Dmax = db[i].Dmax;
+      new[Nnew].depth = 2;
+      new[Nnew].child = FALSE;
+      strncpy (root, db[i].name, 5); root[5] = 0;
+      sprintf (new[Nnew].name, "%s/r%04d.cpt", root, Rnumber);
+      Rnumber ++;      
+      Nnew ++;
+      if (Nnew == NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, SkyRegion, NNEW);
+      }
+    next_seed:
+    }
+    /* update db list */
+    db[i].childS = N;
+    db[i].childE = N + Nnew;
+    NREGION = N + Nnew + 100;
+    REALLOCATE (db, SkyRegion, NREGION);
+    memcpy (&db[N], new, Nnew*sizeof(SkyRegion));
+    free (new);
+    N += Nnew;
+  }
+
+  /* subdivide ra strips based on seed */
+  childS = db[db[0].childS].childS;
+  childE = db[db[0].childE - 1].childE;
+  nextS = N;
+  for (i = childS; i < childE; i++) {
+
+    Nnew = 0;
+    NNEW = 100;
+    ALLOCATE (new, SkyRegion, NNEW);
+
+    for (j = 0; j < Nseed; j++) {
+      if (seed[j].Rmin > db[i].Rmax) continue;
+      if (seed[j].Rmax < db[i].Rmin) continue;
+      if (seed[j].Dmin > db[i].Dmax) continue;
+      if (seed[j].Dmax < db[i].Dmin) continue;
+      
+      for (k = 0; k < Nnew; k++) {
+	if ((seed[j].Dmin == new[k].Dmin) && 
+	    (seed[j].Dmax == new[k].Dmax)) { 
+	  goto next_seed;
+	}
+	if ((seed[j].Dmin == new[k].Dmin) ^^ 
+	    (seed[j].Dmax != new[k].Dmax)) { 
+	  fprintf (stderr, "inconsistent blocks in seed file\n");
+	  return (NULL);
+	}
+      }	
+      new[Nnew].Dmin = seed[j].Dmin;
+      new[Nnew].Dmax = seed[j].Dmax;
+      new[Nnew].Rmin = db[i].Rmin;
+      new[Nnew].Rmax = db[i].Rmax;
+      new[Nnew].depth = 3;
+      new[Nnew].child = FALSE;
+      strcpy (new[Nnew].name, seed[j].name);
+      Nnew ++;
+      if (Nnew == NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, SkyRegion, NNEW);
+      }
+    next_seed:
+    }
+    
+    /* update db list */
+    db[i].childS = N;
+    db[i].childE = N + Nnew;
+    NREGION = N + Nnew + 100;
+    REALLOCATE (db, SkyRegion, NREGION);
+    memcpy (&db[N], new, Nnew*sizeof(SkyRegion));
+    free (new);
+    N += Nnew;
+  }
+  nextE = N;
+
+  /* subdivide entries once more */
+  childS = nextS;
+  childE = nextE;
+  for (i = childS; i < childE; i++) {
+    Rnumber = 0;
+    db[i].childS = N;
+    dDec = (db[i].Dmax - db[i].Dmin) / 5.0;
+    dRa  = (db[i].Rmax - db[i].Rmin) / 5.0;
+    for (nx = 0; nx < 5; nx++) {
+      for (ny = 0; ny < 5; ny++) {
+	db[N].Dmin = db[i].Dmin + dDec * (nx + 0);
+	db[N].Dmax = db[i].Dmax + dDec * (nx + 1);
+	db[N].Rmin = db[i].Rmin + dDec * (nx + 0);
+	db[N].Rmax = db[i].Rmax + dDec * (nx + 1);
+	db[N].depth = 3;
+	db[N].child = FALSE;
+	strncpy (root, db[i].name, 10); root[10] = 0;
+	sprintf (db[N].name, "%s.%02d.cpt", root, Rnumber);
+	Rnumber ++;
+	N ++;
+	if (N == NREGION) {
+	  NREGION += 100;
+	  REALLOCATE (new, SkyRegion, NREGION);
+	}
+      }
+    }
+  }
+
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyExtend (SkyRegion *db, SkyRegion *region) {
+
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyContract (SkyRegion *db, SkyRegion *region) {
+
+}
+
+SkyRegion *SkyFindGCircle (SkyRegion *db, SkyCoord c1, SkyCoord c2) {
+
+}
+
+
+/* is entire db in memory, or do we load from disk for each operation? 
+   10,000 at level 3, 100,000 @ 4 : 0.5MB, 5MB */
+
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_gsc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 17422)
@@ -0,0 +1,564 @@
+# include "dvo.h"
+# define NDECBANDS 24
+# define NDIV 4
+# define DEBUG 0
+
+static int DecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 25, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+static double DecMin[] = {0.0, +7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+static double DecMax[] = {+7.5, +15.0, +22.5, +30.0, +37.5, +45.0, +52.5, +60.0, +67.5, +75.0, +82.5, +90.0, 0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, -52.5, -60.0, -67.5, -75.0, -82.5};
+static char *DecNames[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", "n5230", "n6000", "n6730", "n7500", "n8230", "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", "s5230", "s6000", "s6730", "s7500", "s8230"};
+
+// L0 : full sky
+// L1 : Dec bands
+// L2 : RA segments
+// L3 : GSC regions
+// L4 : GSC subdivisions
+
+// a zone contains a set of L3 regions of the same size
+typedef struct {
+  int L1band;   // parent band
+  int L3start;  // start of zone in L1 band
+  int L3end;    // end of zone in L1 band (last + 1)
+  float dDec;   // height of L3 region in zone
+  float Rmin;
+  float Rmax;
+  float Dmin;
+  float Dmax;
+  int Nset;
+} SkyRegionZone;
+
+SkyTable *SkyRegionForDecBand (char *buffer, int Nregions, char *DecName, float Dmin, float Dmax);
+SkyRegionZone *SkyRegionFindZones (SkyTable *band, int *Nzones, int parent);
+void SkyTableL2fromZone (SkyTable *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, SkyRegionZone *zone, int parent);
+void SkyTableL3fromL2 (SkyRegion *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, int Ns, int Ne);
+void SkyTableL4fromL3 (SkyRegion *L3, SkyTable *L4);
+
+void SkyTableSort (SkyTable *table);
+void SkyTableAppend (SkyTable *old, SkyTable *new, int Nprev);
+int NsetForDecRange (float dDec);
+void SkyRegionPrint (SkyRegion *region);
+
+SkyTable *SkyTableFromGSC (char *filename, int depth, int VERBOSE) {
+
+  int i, j, skipLines, Nzones;
+
+  Header theader;
+  FTable ftable;
+  SkyTable *skytable;
+  SkyTable *band;
+  SkyTable L0, L1, L2, L3, L4;
+  SkyRegionZone *zones;
+
+  FILE *f;
+  
+  // ohana_memdump (0);
+
+  if (filename == NULL) {
+    if (VERBOSE) fprintf (stderr, "template GSC Region file not defined\n");
+    return (NULL);
+  }
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find GSC Region file %s\n", filename);
+    return (NULL);
+  }
+
+/* load in table data */
+  ftable.header = &theader;
+  if (!gfits_fread_ftable (f, &ftable, "REGIONS")) {
+    if (VERBOSE) fprintf (stderr, "can't read GSC Region table\n");
+    fclose (f);
+    return (NULL);
+  }
+
+/* generate the regions for each level independently. indices (parent,childE,childS) 
+   refer to the value within the independent group.  at the end, we combine and adjust
+   the indices */
+
+/* L0 : full sky */
+  L0.Nregions = 1;
+  ALLOCATE (L0.regions, SkyRegion, L0.Nregions);
+  L0.regions[0].Rmin 	=   0;
+  L0.regions[0].Rmax 	= 360;
+  L0.regions[0].Dmin 	= -90;
+  L0.regions[0].Dmax 	= +90;
+  L0.regions[0].index  	=  0;
+  L0.regions[0].depth  	=  0;
+  L0.regions[0].parent 	= -1;
+  L0.regions[0].child  	=  TRUE;
+  L0.regions[0].table  	= -1;
+  L0.regions[0].childS  =  0;
+  L0.regions[0].childE  =  NDECBANDS;
+  strcpy (L0.regions[0].name, "fullsky");
+  if (DEBUG) SkyRegionPrint (&L0.regions[0]);
+
+  /* allocate space for all levels */
+  L1.Nregions = L2.Nregions = L3.Nregions = L4.Nregions = 0;
+  ALLOCATE (L1.regions, SkyRegion, 1);
+  ALLOCATE (L2.regions, SkyRegion, 1);
+  ALLOCATE (L3.regions, SkyRegion, 1);
+  ALLOCATE (L4.regions, SkyRegion, 1);
+
+  // skipLines = 0;
+  // for (i = 0; i < 16; i++) skipLines += DecLines[i];
+  // for (i = 16; i < 17; i++) {
+
+  /* L1 : dec bands */
+  skipLines = 0;
+  for (i = 0; i < NDECBANDS; i++) {
+    L1.regions[i].Rmin     = 0;
+    L1.regions[i].Rmax     = 360;
+    L1.regions[i].Dmin     = DecMin[i];
+    L1.regions[i].Dmax     = DecMax[i];
+    L1.regions[i].index    = i;
+    L1.regions[i].depth    = 1;
+    L1.regions[i].parent   = 0;
+    L1.regions[i].child    = TRUE;
+    L1.regions[i].table    = -1;
+    strcpy (L1.regions[i].name, DecNames[i]);
+    if (DEBUG) SkyRegionPrint (&L1.regions[i]);
+
+    /* build the L2 regions for this L1 region (based on zones) */
+    L1.regions[i].childS   = L2.Nregions;
+
+    /* load all GSC Regions in this band */
+    band = SkyRegionForDecBand (&ftable.buffer[skipLines*48], DecLines[i], DecNames[i], L1.regions[i].Dmin, L1.regions[i].Dmax);
+    skipLines += DecLines[i];
+
+    /* sort the band regions by Rmin */
+    SkyTableSort (band);
+    
+    // free zones below
+    zones = SkyRegionFindZones (band, &Nzones, i);
+
+    /* subdivide each zone */
+    for (j = 0; j < Nzones; j++) {
+      if (DEBUG >= 1) fprintf (stderr, "zone: %d : %f - %f : %f - %f\n", j, zones[j].Rmin, zones[j].Rmax, zones[j].Dmin, zones[j].Dmax);
+      SkyTableL2fromZone (&L2, &L3, &L4, band, &zones[j], i);
+    }
+    free (zones);
+    SkyTableFree (band);
+
+    /* at end of loop, L2.Nregions has been updated to end of L2.regions for L1.region */
+    L1.regions[i].childE   = L2.Nregions;
+
+    L1.Nregions ++;
+    REALLOCATE (L1.regions, SkyRegion, L1.Nregions + 1);
+  }
+
+  /* merge L1 into L0 */
+  ALLOCATE (skytable, SkyTable, 1);
+  ALLOCATE (skytable[0].regions, SkyRegion, 1);
+  skytable[0].Nregions = 0;
+
+  SkyTableAppend (skytable, &L0, 0);
+  SkyTableAppend (skytable, &L1, skytable[0].Nregions - L0.Nregions);
+  SkyTableAppend (skytable, &L2, skytable[0].Nregions - L1.Nregions);
+  SkyTableAppend (skytable, &L3, skytable[0].Nregions - L2.Nregions);
+  SkyTableAppend (skytable, &L4, skytable[0].Nregions - L3.Nregions);
+
+  free (L0.regions);
+  free (L1.regions);
+  free (L2.regions);
+  free (L3.regions);
+  free (L4.regions);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  /* fix the depth elements and create the filename array */
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+    skytable[0].regions[i].table = (skytable[0].regions[i].depth == depth);
+  }
+
+  // ohana_memdump (0);
+  return (skytable);
+}
+
+// allocates band, band->regions
+SkyTable *SkyRegionForDecBand (char *buffer, int Nregions, char *DecName, float DminBand, float DmaxBand) {
+
+  int i;
+  double Rmin, Rmax, Dmin, Dmax;
+  char temp[80], name[80];
+  SkyTable *band;
+  SkyRegion *regions;
+
+  ALLOCATE (band, SkyTable, 1);
+  ALLOCATE (regions, SkyRegion, Nregions);
+  for (i = 0; i < Nregions; i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&Rmin, &Rmax, &Dmin, &Dmax, &temp[7]);
+    if (Dmax < Dmin) SWAP (Dmin, Dmax);
+
+    /* check that we are in correct Dec band */
+    if ((Dmax < DminBand) || (Dmin > DmaxBand)) {
+      fprintf (stderr, "error with raw table: table mis-match\n");
+      fprintf (stderr, "line: %s\n", temp);
+      fprintf (stderr, "region %d: %f - %f vs %f - %f\n", i, Dmin, Dmax, DminBand, DmaxBand);
+      exit (2);
+    }
+
+    /* regions near 0,360 boundary have Rmax == 0.0 */
+    if (Rmax < Rmin) Rmax += 360.0;
+
+    regions[i].Dmin = Dmin;
+    regions[i].Dmax = Dmax;
+    regions[i].Rmin = Rmin;
+    regions[i].Rmax = Rmax;
+
+    temp[5] = 0;
+    sprintf (name, "%s/%s", DecName, &temp[1]);
+    strcpy (regions[i].name, name);
+  }
+  band[0].filename = NULL;
+  band[0].regions = regions;
+  band[0].Nregions = Nregions;
+  return (band);
+}
+
+/* sort skytable by Rmin */
+void SkyTableSort (SkyTable *table) {
+
+  SkyRegion *regions;
+  char **filename = NULL;
+
+  regions = table[0].regions;
+  filename = table[0].filename;
+
+# define SWAPFUNC(A,B){ \
+  if (table[0].filename) { \
+    char     *tempfile = filename[A]; filename[A] = filename[B]; filename[B] = tempfile; \
+  } \
+  SkyRegion tempregion = regions[A]; regions[A] = regions[B]; regions[B] = tempregion; \
+}
+# define COMPARE(A,B)(regions[A].Rmin < regions[B].Rmin)
+
+  OHANA_SORT (table[0].Nregions, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+/* given a complete set of regions in a Dec band, subdivide into zones of equal-sized regions.
+ * this function assumes the DEC band is sliced first by lines of constant RA, then subdivided
+ * with lines of constant DEC.  Groups ('zones') of these smaller boxes have the same size in
+ * delta-Dec.  We are trying to fine the boundaries of these zones.  The initial assumption
+ * breaks down for the pole region (86.25 < DEC < 90), where the entire range is divided into a
+ * single circular patch.  we need to treat it differently.
+ */
+
+// allocates zones
+SkyRegionZone *SkyRegionFindZones (SkyTable *band, int *Nzones, int parent) {
+  
+  float Dmin, Dmax, dDec, dDecZone;
+  int i, j, Nz, NZ, Nregions, poleRegion;
+  SkyRegion *regions;
+  SkyRegion tempregion;
+  SkyRegionZone *zones;
+  char basename[64];
+  
+  regions = band[0].regions;
+  Nregions = band[0].Nregions;
+
+  NZ = 10;
+  ALLOCATE (zones, SkyRegionZone, NZ);
+ 
+  /* the pole regions need to be separately handled.  skip in analysis below */
+  poleRegion = 0;
+
+  /* search for transitions in the region dDec, find the max Dec range */
+  Nz = -1;
+  dDecZone = -1;
+  Dmin = +90;
+  Dmax = -90;
+  for (i = 0; i < Nregions; i++) {
+    if ((regions[i].Dmin >= +86.00) || (regions[i].Dmax <= -86.00)) {
+      /* drop the pole region (re-generated below) */
+      poleRegion = SIGN(regions[i].Dmin);
+      Nregions --;
+      tempregion = regions[i];
+      for (j = i; j < Nregions; j++) {
+	regions[j] = regions[j+1];
+      }
+      i--;
+      continue;
+    }
+    /* is this region in the current zone? */
+    Dmin = MIN (regions[i].Dmin, Dmin);
+    Dmax = MAX (regions[i].Dmax, Dmax);
+    dDec = regions[i].Dmax - regions[i].Dmin;
+    if (fabs(dDec - zones[Nz].dDec) > 0.001) {
+      /* we've found the end of the current zone; set the ending info */
+      if (Nz >= 0) {
+	zones[Nz].L3end = i;
+	zones[Nz].Rmax = regions[i-1].Rmax;
+      }
+
+      /* go to the next zone */
+      Nz++;
+      CHECK_REALLOCATE (zones, SkyRegionZone, NZ, Nz, 10);
+
+      /* start info for the new zone */
+      dDecZone = dDec;
+      zones[Nz].Rmin = regions[i].Rmin;
+      zones[Nz].dDec = dDecZone;
+      zones[Nz].L3start = i;
+      zones[Nz].L1band = parent;
+      zones[Nz].Nset = NsetForDecRange (zones[Nz].dDec);
+    }
+  }
+  zones[Nz].L3end = i;
+  zones[Nz].Rmax = regions[i-1].Rmax;
+  Nz ++;
+
+  for (i = 0; i < Nz; i++) {
+    zones[i].Dmin = Dmin;
+    zones[i].Dmax = Dmax;
+  }
+  
+  /* the pole region is mis-allocated in the gsc table to L3.  elevate it 
+     to L2 (zone) and subdivide */
+  if (poleRegion) {
+    CHECK_REALLOCATE (zones, SkyRegionZone, NZ, Nz, 10);
+    /* pole region @ L2 */
+    zones[Nz].Rmin = 0.0;
+    zones[Nz].Rmax = 360.0;
+    if (poleRegion > 0) {
+      zones[Nz].Dmin = +86.25;
+      zones[Nz].Dmax = +90.00;
+      strcpy (basename, "n8230/pole");
+    } else {
+      zones[Nz].Dmin = -90.00;
+      zones[Nz].Dmax = -86.25;
+      strcpy (basename, "s8230/pole");
+    }
+    zones[Nz].dDec = 90.00 - 86.25;
+    zones[Nz].L3start = Nregions;
+    zones[Nz].L3end = Nregions + 5;
+    zones[Nz].L1band = parent;
+
+    band[0].Nregions += 4;
+    REALLOCATE (band[0].regions, SkyRegion, band[0].Nregions);
+    for (i = 0; i < 4; i++) {
+      band[0].regions[Nregions + i].Rmin = i * 90.0;
+      band[0].regions[Nregions + i].Rmax = i * 90.0 + 90.0;
+      if (poleRegion > 0) {
+	band[0].regions[Nregions + i].Dmin = +86.250;
+	band[0].regions[Nregions + i].Dmax = +88.125;
+      } else {
+	band[0].regions[Nregions + i].Dmin = -88.125;
+	band[0].regions[Nregions + i].Dmax = -86.250;
+      }
+      sprintf (band[0].regions[Nregions + i].name, "%s.%d", basename, i);
+    }
+    band[0].regions[Nregions + i].Rmin = 0.0;
+    band[0].regions[Nregions + i].Rmax = 360.0;
+    if (poleRegion > 0) {
+      band[0].regions[Nregions + i].Dmin = +88.125;
+      band[0].regions[Nregions + i].Dmax = +90.000;
+    } else {
+      band[0].regions[Nregions + i].Dmin = -90.000;
+      band[0].regions[Nregions + i].Dmax = -88.125;
+    }
+    sprintf (band[0].regions[Nregions + i].name, "%s.%d", basename, i);
+
+    zones[0].Nset = 6;
+    zones[1].Nset = 5;
+
+    Nz ++;
+  }
+
+  *Nzones = Nz;
+  return (zones);
+}
+
+int NsetForDecRange (float dDec) {
+
+  int Ndiv, Nset;
+  
+  /* max segment size is ~7.5 x 15 degrees */
+  Ndiv = (int) (0.5 + 7.5 / dDec);
+  Nset = 2*Ndiv*Ndiv;
+  return (Nset);
+}
+  
+// memory neutral
+void SkyTableL2fromZone (SkyTable *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, SkyRegionZone *zone, int parent) {
+
+  int i, Nr, Ns, Ne;
+  char *p, name[80];
+
+  Nr = L2[0].Nregions;
+  REALLOCATE (L2[0].regions, SkyRegion, Nr + 1);
+  
+  /* divide this zone into L2 regions with Nset L3 regions each (fewer on ends) */
+  for (i = zone[0].L3start; i < zone[0].L3end; i += zone[0].Nset) {
+    Ns = i; 
+    Ne = MIN (i + zone[0].Nset, zone[0].L3end);
+
+    L2[0].regions[Nr].Rmin = band[0].regions[Ns].Rmin;
+    L2[0].regions[Nr].Rmax = band[0].regions[Ne - 1].Rmax;
+    L2[0].regions[Nr].Dmin = zone[0].Dmin;
+    L2[0].regions[Nr].Dmax = zone[0].Dmax;
+    
+    L2[0].regions[Nr].index    =  Nr;
+    L2[0].regions[Nr].depth    =  2;
+    L2[0].regions[Nr].table    =  -1;
+    L2[0].regions[Nr].parent   =  parent;
+    L2[0].regions[Nr].child    =  FALSE;
+    L2[0].regions[Nr].childS   =  0;
+    L2[0].regions[Nr].childE   =  0;
+
+    /* define names for L2 regions */
+    p = strchr (band[0].regions[Ns].name, '/');
+    if (p == NULL) {
+      fprintf (stderr, "programming error in SkyTableL2fromZone\n");
+      exit (2);
+    }
+    *p = 0;
+    sprintf (name, "%s/z%03d", band[0].regions[Ns].name, Nr);
+    *p = '/';
+    strcpy (L2[0].regions[Nr].name, name);
+    if (DEBUG >= 2) SkyRegionPrint (&L2[0].regions[Nr]);
+
+    /* childS and childE are set in SkyTableL3fromL2 */
+    SkyTableL3fromL2 (&L2[0].regions[Nr], L3, L4, band, Ns, Ne);
+
+    Nr++;
+    REALLOCATE (L2[0].regions, SkyRegion, Nr + 1);
+  }
+  L2[0].Nregions = Nr;
+}
+
+// memory neutral
+void SkyTableL3fromL2 (SkyRegion *L2, SkyTable *L3, SkyTable *L4, SkyTable *band, int Ns, int Ne) {
+
+  int i, Nr;
+
+  Nr = L3[0].Nregions;
+  L3[0].Nregions += Ne - Ns;
+  REALLOCATE (L3[0].regions, SkyRegion, L3[0].Nregions);
+
+  L2[0].child  = TRUE;
+  L2[0].childS = Nr;
+  L2[0].childE = L3[0].Nregions;
+
+  /* copy the band entries corresponding to this region in the L3 table */
+  for (i = Ns; i < Ne; i++) {
+
+    L3[0].regions[Nr] = band[0].regions[i];
+    
+    L3[0].regions[Nr].index    =  Nr;
+    L3[0].regions[Nr].depth    =  3;
+    L3[0].regions[Nr].table    =  -1;
+    L3[0].regions[Nr].parent   =  L2[0].index;
+    L3[0].regions[Nr].child    =  FALSE;
+    L3[0].regions[Nr].childS   =  0;
+    L3[0].regions[Nr].childE   =  0;
+
+    if (DEBUG >= 3) SkyRegionPrint (&L3[0].regions[Nr]);
+    /* name is set for the band in SkyRegionForDecBand */
+
+    /* childS and childE are set in SkyTableL3fromL3 */
+    SkyTableL4fromL3 (&L3[0].regions[Nr], L4);
+    Nr++;
+  }
+
+  return;
+}
+
+// memory neutral
+void SkyTableL4fromL3 (SkyRegion *L3, SkyTable *L4) {
+
+  int nx, ny, Nr, Nbox;
+  double Rmin, Dmin, dR, dD;
+  char name[80];
+
+  Nr = L4[0].Nregions;
+  L4[0].Nregions += NDIV*NDIV;
+  REALLOCATE (L4[0].regions, SkyRegion, L4[0].Nregions);
+
+  L3[0].child  = TRUE;
+  L3[0].childS = Nr;
+  L3[0].childE = L4[0].Nregions;
+
+  // XXX handle the pole regions just a bit differently...
+
+  /* subdivide L3 into NDIV boxes */
+  Rmin = L3[0].Rmin;
+  Dmin = L3[0].Dmin;
+  dR = (L3[0].Rmax - L3[0].Rmin) / NDIV;
+  dD = (L3[0].Dmax - L3[0].Dmin) / NDIV;
+
+  Nbox = 0;
+  for (ny = 0; ny < NDIV; ny ++) {
+    for (nx = 0; nx < NDIV; nx ++) {
+      L4[0].regions[Nr].Rmin     = Rmin  + (nx + 0)*dR;
+      L4[0].regions[Nr].Rmax     = Rmin  + (nx + 1)*dR;
+      L4[0].regions[Nr].Dmin     = Dmin + (ny + 0)*dD;
+      L4[0].regions[Nr].Dmax     = Dmin + (ny + 1)*dD;
+
+      L4[0].regions[Nr].index    =  Nr;
+      L4[0].regions[Nr].depth    =  4;
+      L4[0].regions[Nr].table    =  -1;
+      L4[0].regions[Nr].parent   =  L3[0].index;
+      L4[0].regions[Nr].child    =  FALSE;
+      L4[0].regions[Nr].childS   =  0;
+      L4[0].regions[Nr].childE   =  0;
+
+      sprintf (name, "%s.%02d", L3[0].name, Nbox);
+      strcpy (L4[0].regions[Nr].name, name);
+      if (DEBUG >= 4) SkyRegionPrint (&L4[0].regions[Nr]);
+
+      Nr ++;
+      Nbox ++;
+    }
+  }
+  return;
+}
+
+// memory neutral
+void SkyTableAppend (SkyTable *old, SkyTable *new, int Nprev) {
+
+  int i, Nold;
+
+  Nold = old[0].Nregions;
+
+  old[0].Nregions += new[0].Nregions;
+  REALLOCATE (old[0].regions, SkyRegion, old[0].Nregions);
+
+  for (i = Nprev; i < Nold; i++) {
+    old[0].regions[i].childS += Nold;
+    old[0].regions[i].childE += Nold;
+  }
+
+  for (i = 0; i < new[0].Nregions; i++) {
+    old[0].regions[i + Nold] = new[0].regions[i];
+    old[0].regions[i + Nold].parent += Nprev;
+  }
+  return;
+}
+
+// memory neutral
+void SkyRegionPrint (SkyRegion *region) {
+
+  int i;
+
+  fprintf (stderr, "L%d:", region[0].depth);
+  for (i = 0; i < region[0].depth; i++) {
+    fprintf (stderr, " ");
+  } 
+  fprintf (stderr, "%s : %f - %f : %f - %f\n", region[0].name, region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+  return;
+}
+
+/***
+    notes and questions:
+    1) is the regions.index value used?
+
+
+***/
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_io.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_io.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_io.c	(revision 17422)
@@ -0,0 +1,170 @@
+# include "dvo.h"
+
+SkyTable *SkyTableLoad (char *filename, int VERBOSE) {
+
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  SkyTable *skytable;
+  FILE *f;
+  int i;
+  
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    if (VERBOSE) fprintf (stderr, "can't find Sky Region file %s\n", filename);
+    return (NULL);
+  }
+
+  /* load in table data */
+  ftable.header = &theader;
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region header\n");
+    fclose (f);
+    return (NULL);
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return (NULL);
+  }
+  if (!gfits_fread_ftable (f, &ftable, "SKY_REGION")) {
+    if (VERBOSE) fprintf (stderr, "can't read Sky Region table\n");
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    fclose (f);
+    return (NULL);
+  }
+
+  ALLOCATE (skytable, SkyTable, 1);
+  skytable[0].regions = gfits_table_get_SkyRegion (&ftable, &skytable[0].Nregions, NULL);
+  ALLOCATE (skytable[0].filename, char *, skytable[0].Nregions);
+  for (i = 0; i < skytable[0].Nregions; i++) {
+    skytable[0].filename[i] = NULL;
+  }
+  
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+
+  return (skytable);
+}
+
+int SkyTableSave (SkyTable *skytable, char *filename) {
+
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+  FILE *f;
+
+  /* make phu header (no matrix needed) */
+  ftable.header = &theader;
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_table_set_SkyRegion (&ftable, skytable[0].regions, skytable[0].Nregions);
+
+  f = fopen (filename, "w");
+  if (f == (FILE *) NULL) { 
+    fprintf (stderr, "cannot open %s for output\n", filename);
+    return (FALSE);
+  }
+  
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+  fclose (f);
+
+  return (TRUE);
+}
+
+SkyTable *SkyTableLoadOptimal (char *catdir, char *skyfile, char *gscfile, int depth, int verbose) {
+
+  char filename[256];
+  struct stat filestat;
+  SkyTable *sky;
+
+  /* first option: CATDIR/SkyTable.fits */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  if (stat (filename, &filestat)) goto SKYFILE;
+  if (!check_file_access (filename, FALSE, verbose)) goto SKYFILE;
+  sky = SkyTableLoad (filename, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    exit (1);
+  }
+  return (sky);
+
+SKYFILE:
+  /* second option: SKYFILE */
+  if (skyfile == NULL) goto GSCFILE;
+  if (skyfile[0] != 0) goto GSCFILE;
+  if (stat (skyfile, &filestat)) goto GSCFILE;
+  if (!check_file_access (skyfile, FALSE, verbose)) goto GSCFILE;
+  sky = SkyTableLoad (skyfile, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    return (NULL);
+  }
+  /* set the depths to the default depth */
+  SkyTableSetDepth (sky, depth);
+
+  /* create CATDIR copy */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  check_file_access (filename, FALSE, verbose);
+  if (!SkyTableSave (sky, filename)) return NULL;
+
+  gfits_convert_SkyRegion (sky[0].regions, sizeof (SkyTable), sky[0].Nregions);
+  return (sky);
+
+GSCFILE:
+
+  /* third option: GSCFILE */
+  sky = SkyTableFromGSC (gscfile, depth, verbose);
+  if (sky == NULL) {
+    fprintf (stderr, "error loading sky table\n");
+    return (NULL);
+  }
+
+  /* create CATDIR copy */
+  sprintf (filename, "%s/SkyTable.fits", catdir);
+  check_file_access (filename, FALSE, verbose);
+  if (!SkyTableSave (sky, filename)) return NULL;
+
+  gfits_convert_SkyRegion (sky[0].regions, sizeof (SkyRegion), sky[0].Nregions);
+  return (sky);
+}
+
+int SkyListSetFilenames (SkyList *list, char *path, char *ext) {
+
+  int i;
+  char line[256];
+
+  // this generates the names, be sure to free when not needed
+  for (i = 0; i < list[0].Nregions; i++) {
+    sprintf (line, "%s/%s.%s", path, list[0].regions[i][0].name, ext);
+    list[0].filename[i] = strcreate (line);
+  }
+
+  return (TRUE);
+}
+
+int SkyTableSetFilenames (SkyTable *sky, char *path, char *ext) {
+
+  int i;
+  char line[256];
+
+  // this generates the names, be sure to free when not needed
+  for (i = 0; i < sky[0].Nregions; i++) {
+    sprintf (line, "%s/%s.%s", path, sky[0].regions[i].name, ext);
+    sky[0].filename[i] = strcreate (line);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_ops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_ops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/skyregion_ops.c	(revision 17422)
@@ -0,0 +1,418 @@
+# include "dvo.h"
+
+/* each region is bounded on the sky by lines of constant RA & DEC.
+   the region represents a specified depth, and it may or may not have
+   children.  The start and end of the children in the array are childS and childE 
+   If the region at the given depth is populated with an object table, then 'object' is TRUE.
+   If the region at the given depth is populated with an image table, then 'image' is TRUE.
+   I have defined no accelerators other than the table hierarchy */
+
+/* find region which overlaps c at given depth (-1 : populated ) */
+SkyList *SkyRegionByPoint (SkyTable *table, int depth, double ra, double dec) {
+  
+  int i, Ns, Ne, No;
+  SkyRegion *region;
+  SkyList *list;
+
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, 1);
+  ALLOCATE (list[0].filename,  char *, 1);
+  list[0].Nregions = 0;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+      if (ra  < region[i].Rmin) continue;
+      if (ra  > region[i].Rmax) continue;
+      if (dec < region[i].Dmin) continue;
+      if (dec > region[i].Dmax) continue;
+      No = i;
+      break;
+    }
+    if (No == -1) return (list);
+    if ((depth == -1) && (region[No].table)) break;
+    if (depth == region[No].depth) break;
+    if ((depth > region[No].depth) && !region[No].child) return (list);
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    Ns = region[No].childS;
+    Ne = region[No].childE;
+  }
+
+  list[0].regions[0] = &region[No];
+  list[0].filename[0] = table[0].filename[No];
+  list[0].Nregions = 1;
+  return (list);
+}
+
+/* find regions at all levels which match name */
+/* XXX : need to add support for selected level / populated level */
+SkyList *SkyListByName (SkyTable *table, char *name) {
+
+  int i, Nchar, N, NREGIONS;
+  SkyList *list;
+  SkyRegion *region;
+  
+  N = 0;
+  NREGIONS = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+  ALLOCATE (list[0].filename, char *, NREGIONS);
+  list[0].Nregions = N;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Nchar = strlen (name);
+
+  for (i = 0; i < table[0].Nregions; i++) {
+    if (strncasecmp (region[i].name, name, Nchar)) continue;
+
+    list[0].regions[N] = &region[i];
+    list[0].filename[N] = table[0].filename[i];
+    N++;
+    if (N >= NREGIONS) {
+	NREGIONS += 10;
+	REALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+	REALLOCATE (list[0].filename, char *, NREGIONS);
+    }
+  }
+  list[0].Nregions = N;
+  return (list);
+}
+
+/* find regions at all levels which overlap c */
+SkyList *SkyListByPoint (SkyTable *table, double ra, double dec) {
+
+  int i, Ns, Ne, No, N, NREGIONS;
+  SkyList *list;
+  SkyRegion *region;
+  
+  N = 0;
+  NREGIONS = 10;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions,  SkyRegion *, NREGIONS);
+  ALLOCATE (list[0].filename,  char *, NREGIONS);
+  list[0].Nregions = N;
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  Ns = 0;
+  Ne = 1;
+
+  while (1) {
+    No = -1;
+    for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+      if (ra  < region[i].Rmin) continue;
+      if (ra  > region[i].Rmax) continue;
+      if (dec < region[i].Dmin) continue;
+      if (dec > region[i].Dmax) continue;
+      No = i;
+      break;
+    }
+    if (No == -1) return (list);
+
+    list[0].regions[N] = &region[No];
+    list[0].filename[N] = table[0].filename[No];
+    N++;
+    if (N >= NREGIONS) {
+	NREGIONS += 10;
+	REALLOCATE (list[0].regions, SkyRegion *, NREGIONS);
+	REALLOCATE (list[0].filename, char *, NREGIONS);
+    }
+    list[0].Nregions = N;
+
+    /* need to check Ns, Ne, or guarantee valid range */
+    if (region[No].child) {
+      Ns = region[No].childS;
+      Ne = region[No].childE;
+    } else {
+      return (list);
+    }
+  }
+}
+
+SkyList *SkyListByRadius (SkyTable *table, int depth, double RA, double DEC, double radius) {
+
+  double rad;
+  double Rmin, Rmax, Dmin, Dmax;
+  SkyList *list;
+
+  Dmin = DEC - radius;
+  Dmax = DEC + radius;
+
+  if ((Dmin <= -89) || (Dmax >= 89)) {
+    Rmin = 0;
+    Rmax = 360;
+  } else {
+    rad = MAX (radius / (cos(Dmin*RAD_DEG)), radius / (cos(Dmax*RAD_DEG)));
+    Rmin = RA - rad;
+    Rmax = RA + rad;
+  }
+
+  list = SkyListByBounds (table, depth, Rmin, Rmax, Dmin, Dmax);
+  return (list);
+}
+
+SkyList *SkyListByPatch (SkyTable *table, int depth, SkyRegion *patch) {
+
+  SkyList *list;
+
+  list = SkyListByBounds (table, depth, patch[0].Rmin, patch[0].Rmax, patch[0].Dmin, patch[0].Dmax);
+  return (list);
+}
+
+/* user must be careful about mosaic registration */
+SkyList *SkyListByImage (SkyTable *table, int depth, Image *image) {
+
+  int j;
+  SkyList *list;
+  double r, d, X[4], Y[4];
+  double Rmin, Rmax, Dmin, Dmax;
+  
+  // XXX EAM : image/mosaic MUST be registered (if WRP) 
+  SetImageCorners (X, Y, image);
+
+  Rmin = 360.0;
+  Rmax =   0.0;
+  Dmin = +90.0;
+  Dmax = -90.0;
+
+  /* does this work at 0,360 boundary? XY_to_RD must return 
+     coord offsets relative to CRVAL1,2 (ie, NOT renormalize) */
+  for (j = 0; j < 4; j++) {
+    /* XY_to_RD is two-level if ctype == WRP */
+    XY_to_RD (&r, &d, X[j], Y[j], &image[0].coords);
+    Rmin = MIN (Rmin, r);
+    Rmax = MAX (Rmax, r);
+    Dmin = MIN (Dmin, d);
+    Dmax = MAX (Dmax, d);
+  }
+
+  list = SkyListByBounds (table, depth, Rmin, Rmax, Dmin, Dmax);
+  return (list);
+}
+
+SkyList *SkyListByBounds (SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax) {
+
+  int i, j, Ns;
+  SkyList *list, *extra;
+
+  /* rationalize Rmin, Rmax */
+  while (Rmin <   0.0) Rmin += 360;
+  while (Rmin > 360.0) Rmin -= 360;
+  while (Rmax <   0.0) Rmax += 360;
+  while (Rmax > 360.0) Rmax -= 360;
+
+  /* handle 0,360 boundary requests */
+  /* this is probably wrong: I will get duplicates for all Dec bands... */
+  if (Rmin > Rmax) {
+    list = SkyListChildrenByBounds (table, -1, depth, 0.0, Rmax, Dmin, Dmax);
+
+    extra = SkyListChildrenByBounds (table, -1, depth, Rmin, 360.0, Dmin, Dmax);
+    REALLOCATE (list[0].regions, SkyRegion *, list[0].Nregions + extra[0].Nregions);
+    REALLOCATE (list[0].filename, char *, list[0].Nregions + extra[0].Nregions);
+    Ns = list[0].Nregions;
+    for (i = 0; i < extra[0].Nregions; i++) {
+      // search for pre-existing match
+      for (j = 0; j < list[0].Nregions; j++) {
+	if (list[0].regions[j] == extra[0].regions[i]) {
+	  goto skip;
+	}
+      }
+      list[0].regions[Ns] = extra[0].regions[i];
+      list[0].filename[Ns] = extra[0].filename[i];
+      Ns ++;
+    skip:
+      continue;
+    }
+    list[0].Nregions = Ns;
+    SkyListFree (extra);
+  } else {
+    list = SkyListChildrenByBounds (table, -1, depth, Rmin, Rmax, Dmin, Dmax);
+  }
+
+  return (list);
+}
+
+SkyList *SkyListChildrenByBounds (SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax) {
+
+  int i, j, Ns, Ne, Nnew, NNEW;
+  int append;
+  SkyList *children;
+  SkyList *list;
+  SkyRegion *region;
+
+  Nnew = 0;
+  NNEW = 50;
+  ALLOCATE (list, SkyList, 1);
+  ALLOCATE (list[0].regions, SkyRegion *, NNEW);
+  ALLOCATE (list[0].filename, char *, NNEW);
+  list[0].ownElements = FALSE; // this list is only holding a view to the elements
+
+  region = table[0].regions;
+
+  if (No == -1) {
+    Ns = 0;
+    Ne = 1;
+  } else {
+    Ns = region[No].childS;
+    Ne = region[No].childE;
+  }
+
+  for (i = Ns; (i < Ne) && (i < table[0].Nregions); i++) {
+    if (Rmax < region[i].Rmin) continue;
+    if (Rmin > region[i].Rmax) continue;
+    if (Dmax < region[i].Dmin) continue;
+    if (Dmin > region[i].Dmax) continue;
+
+    if ((depth > region[i].depth) && (region[i].child == FALSE)) continue;
+
+    append = FALSE;
+    append |= (depth > region[i].depth) && (region[i].child == TRUE);
+    append |= (depth ==             -1) && (region[i].table == FALSE);
+
+    if (append) {
+      /* append children to new */
+      children = SkyListChildrenByBounds (table, i, depth, Rmin, Rmax, Dmin, Dmax);
+      if (Nnew + children[0].Nregions >= NNEW) {
+	NNEW = Nnew + children[0].Nregions + 50;
+	REALLOCATE (list[0].regions, SkyRegion *, NNEW);
+	REALLOCATE (list[0].filename, char *, NNEW);
+      }
+      for (j = 0; j < children[0].Nregions; j++) {
+	list[0].regions[Nnew + j] = children[0].regions[j];
+	list[0].filename[Nnew + j] = children[0].filename[j];
+      }
+      Nnew += children[0].Nregions;
+      SkyListFree (children);
+    } else {
+      list[0].regions[Nnew] = &region[i];
+      list[0].filename[Nnew] = table[0].filename[i];
+      Nnew ++;
+      if (Nnew >= NNEW) {
+	  NNEW += 50;
+	  REALLOCATE (list[0].regions, SkyRegion *, NNEW);
+	  REALLOCATE (list[0].filename, char *, NNEW);
+      }
+    }
+  }
+
+  list[0].Nregions = Nnew;
+  return (list);
+}
+
+/* XXX EAM : this should go elsewhere (libdvo?) */
+void SetImageCorners (double *X, double *Y, Image *image) {
+
+  if (!strcmp(&image[0].coords.ctype[4], "-DIS")) {
+    X[0] = -0.5*image[0].NX; Y[0] = -0.5*image[0].NY;
+    X[1] = +0.5*image[0].NX; Y[1] = -0.5*image[0].NY;
+    X[2] = +0.5*image[0].NX; Y[2] = +0.5*image[0].NY;
+    X[3] = -0.5*image[0].NX; Y[3] = +0.5*image[0].NY;
+  } else {
+    X[0] = 0;           Y[0] = 0;
+    X[1] = image[0].NX; Y[1] = 0;
+    X[2] = image[0].NX; Y[2] = image[0].NY;
+    X[3] = 0;           Y[3] = image[0].NY;
+  }
+}
+
+int SkyTableSetDepth (SkyTable *sky, int depth) {
+
+  int i;
+
+  for (i = 0; i < sky[0].Nregions; i++) {
+    sky[0].regions[i].table = (sky[0].regions[i].depth == depth);
+  }
+  return (TRUE);
+}
+
+int SkyListFree (SkyList *list) {
+
+  int i;
+
+  /* XXX do we need to free the filename array as well? */
+  if (list == NULL) return (TRUE);
+  if (list[0].regions != NULL) {
+    if (list[0].ownElements) {
+      for (i = 0; i < list[0].Nregions; i++) {
+	if (list[0].filename[i] != NULL) {
+	  free (list[0].filename[i]);
+	}
+	free (list[0].regions[i]);
+      }
+    }
+    free (list[0].regions);
+  }
+  free (list);
+  list = NULL;
+  return (TRUE);
+}
+
+int SkyTableFree (SkyTable *table) {
+
+  int i;
+
+  if (table == NULL) return (TRUE);
+  if (table[0].filename != NULL) {
+    for (i = 0; i < table[0].Nregions; i++) {
+      if (table[0].filename[i] != NULL) {
+	free (table[0].filename[i]);
+      }
+    }
+    free (table[0].filename);
+  }
+  if (table[0].regions != NULL) {
+    free (table[0].regions);
+  }
+  free (table);
+  table = NULL;
+  return (TRUE);
+}
+
+int SkyListMerge (SkyList **outlist, SkyList *newlist) {
+
+    int i, j, skip, Nlist, Ntotal;
+    SkyList *list;
+    
+    if (*outlist == NULL) {
+	ALLOCATE (list, SkyList, 1);
+	ALLOCATE (list[0].regions, SkyRegion *, 1);
+	ALLOCATE (list[0].filename, char *, 1);
+	list[0].ownElements = FALSE; // this list is only holding a view to the elements
+	list[0].Nregions = 0;
+	*outlist = list;
+    } else {
+	list = *outlist;
+    }
+
+    Ntotal = list[0].Nregions + newlist[0].Nregions;
+    REALLOCATE (list[0].regions, SkyRegion *, Ntotal);
+    REALLOCATE (list[0].filename, char *, Ntotal);
+
+    Nlist = list[0].Nregions;
+    for (i = 0; i < newlist[0].Nregions; i++) {
+	/* check existing list to see if we already have this region */
+	skip = FALSE;
+	for (j = 0; j < list[0].Nregions; j++) {
+	    if (list[0].regions[j] == newlist[0].regions[i]) skip = TRUE;
+	}
+	if (skip) continue;
+	list[0].regions[Nlist] = newlist[0].regions[i];
+	list[0].filename[Nlist] = newlist[0].filename[i];
+	Nlist++;
+    }
+    list[0].Nregions = Nlist;
+    REALLOCATE (list[0].regions, SkyRegion *, Nlist);
+    REALLOCATE (list[0].filename, char *, Nlist);
+
+    return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/tabletest.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/tabletest.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/tabletest.c	(revision 17422)
@@ -0,0 +1,10 @@
+# include "dvo.h"
+
+int main (int argc, char **argv) {
+
+  SkyTable *table;
+
+  SkyTableFromGSC (argv[1], 2, TRUE);
+
+  exit (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/libdvo/src/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/libdvo/src/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/libdvo/src/version.c	(revision 17422)
@@ -0,0 +1,6 @@
+# include <dvo.h>
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *libdvo_version () {
+  return (name);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/.cvsignore	(revision 17422)
@@ -0,0 +1,2 @@
+lib
+bin
Index: /tags/ipp-2-5-1/Ohana/src/opihi/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/Makefile	(revision 17422)
@@ -0,0 +1,81 @@
+
+### this is the top-level makefile for the opihi collection of programs
+
+default: all
+
+### the include file dependencies need to be cleaned up still
+
+mana:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dimm:     	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+dvo:      	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks: 	 lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_server: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pantasks_client: lib.data lib.shell cmd.basic cmd.data cmd.astro
+pclient:  	 lib.data lib.shell cmd.basic
+pcontrol: 	 lib.data lib.shell cmd.basic
+
+LIBS = lib.data lib.shell cmd.basic cmd.data cmd.astro
+
+PROGRAM = mana dvo pantasks pclient pcontrol
+SPECIAL = pantasks_client pantasks_server
+
+EXTRAS = dimm
+
+all:
+	for i in $(PROGRAM) $(SPECIAL); do make $$i || exit; done
+	make pantasks_client
+	make pantasks_server
+
+libs:
+	for i in $(LIBS); do make $$i || exit; done
+
+extras:
+	for i in $(EXTRAS); do make $$i || exit; done
+
+install:
+	for i in $(PROGRAM) $(SPECIAL); do make $$i.install || exit; done
+	make pantasks_client.install
+	make pantasks_server.install
+
+extras-install:
+	for i in $(EXTRAS); do make $$i.install || exit; done
+
+clean:
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do make $$i.clean || exit; done
+
+libs-uninstall:
+	for i in $(LIBS); do make $$i.uninstall || exit; done
+
+dist: clean
+	for i in $(PROGRAM) $(SPECIAL) $(EXTRAS) $(LIBS); do make $$i.dist || exit; done
+	rm -rf bin
+	rm -rf lib
+
+#############################################################
+
+pantasks_client pantasks_server:
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+pantasks_client.install: pantasks_client
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+pantasks_server.install: pantasks_server
+	if [ -d pantasks ]; then (cd pantasks && make $@); fi
+
+$(PROGRAM) $(LIBS) $(EXTRAS):
+	if [ -d "$@" ]; then (cd $@ && make); fi
+
+%.install:
+	if [ -d "$*" ]; then make $*; fi
+	if [ -d "$*" ]; then (cd $* && make install); fi
+
+%.clean:
+	if [ -d "$*" ]; then (cd $* && make clean); fi
+
+%.dist:
+	if [ -d "$*" ]; then (cd $* && make dist); fi
+
+%.uninstall:
+	if [ -d "$*" ]; then (cd $* && make uninstall); fi
+
+.PHONY: $(PROGRAM) $(LIBS) pantasks_server pantasks_client
Index: /tags/ipp-2-5-1/Ohana/src/opihi/Makefile.Common
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/Makefile.Common	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/Makefile.Common	(revision 17422)
@@ -0,0 +1,88 @@
+
+# utilities #################################################
+# .SUFFIXES: .$(ARCH).o
+
+BASE_CFLAGS   =	$(CFLAGS)
+BASE_CPPFLAGS =	$(CPPFLAGS) -I$(INC) -I$(DESTINC) $(INCDIRS) -D$(ARCH)
+BASE_LDFLAGS  = $(LDFLAGS) -L$(LIB) -L$(DESTLIB) $(LIBDIRS) $(LIBFLAGS)
+
+%.c : %.c.in
+	sed "s|@DATADIR@|$(DATA)|" $< > $@
+
+%.$(ARCH).o : %.c
+	$(CC) $(FULL_CFLAGS) $(FULL_CPPFLAGS) -c $< -o $@
+
+$(BIN)/%.$(ARCH):
+	@if [ ! -d $(BIN) ]; then mkdir -p $(BIN); fi
+	$(CC) $(FULL_CFLAGS) -o $@ $^ $(FULL_LDFLAGS)
+	@echo "compiled $*"
+	@echo ""
+
+$(DESTBIN)/%: $(BIN)/%.$(ARCH) 
+	@if [ ! -d $(DESTBIN) ]; then mkdir -p $(DESTBIN); fi
+	rm -f $(DESTBIN)/$*
+	cp $(BIN)/$*.$(ARCH) $(DESTBIN)/$*
+	@echo "installed $*"
+	@echo ""
+
+$(LIB)/%.$(ARCH).a:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	ar rcv $@ $^ 
+	$(RANLIB) $@
+	@echo "compiled library $*"
+	@echo ""
+
+$(DESTLIB)/%.a: $(LIB)/%.$(ARCH).a
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+$(LIB)/%.$(ARCH).so:
+	@if [ ! -d $(LIB) ]; then mkdir -p $(LIB); fi
+	rm -f $@
+	gcc -shared -Wl,-soname,$*.so -o $@ $^ -lc
+
+$(DESTLIB)/%.so: $(LIB)/%.$(ARCH).so
+	@if [ ! -d $(DESTLIB) ]; then mkdir -p $(DESTLIB); fi
+	rm -f $@
+	cp $< $@
+
+lib%.clean:
+	rm -f $(LIB)/lib$*.$(ARCH).a
+	rm -f $($*)
+	@echo ""
+
+%.help:
+	@echo "installing help files for $*"
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	@rm -f $(HOME)/$*/help/*~
+	@rm -f $(HOME)/$*/help/#*
+	@for i in `find $(HOME)/$*/help -maxdepth 1 -type f`; do cp -f $$i $(DATA)/help; done
+
+%.modules:
+	@echo "installing modules for $*"
+	@if [ ! -d $(DATA)/modules ]; then mkdir -p $(DATA)/modules; fi
+	@if [ ! -d $(HOME)/modules/$* ]; then echo "no modules for $*"; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/*~; fi
+	@if [   -d $(HOME)/modules/$* ]; then rm -f $(HOME)/modules/$*/#*; fi
+	@if [   -d $(HOME)/modules/$* ]; then for i in `find $(HOME)/modules/$* -name CVS -prune -o -type f -print`; do cp -f $$i $(DATA)/modules; done; fi
+
+%.clean:
+	rm -f $(BIN)/$*.$(ARCH)
+	@echo ""
+
+clean:
+	rm -f $(BIN)/*.$(ARCH)
+	rm -f $(LIB)/*.$(ARCH).a
+	rm -f `find . -name "*.o"`
+	rm -f `find . -name "*~"`
+	rm -f `find . -name "#*"`
+
+dist: clean
+	rm -rf $(BIN)/*
+	rm -rf $(LIB)/*
+
+clean-help:
+	@if [ ! -d $(DATA)/help ]; then mkdir -p $(DATA)/help; fi
+	rm -f $(DATA)/help/*
Index: /tags/ipp-2-5-1/Ohana/src/opihi/bin/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/bin/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/bin/.cvsignore	(revision 17422)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/Makefile	(revision 17422)
@@ -0,0 +1,84 @@
+default: libastrocmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/cmd.astro
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# astro user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o             \
+$(SRC)/altaz.$(ARCH).o	           \
+$(SRC)/biassub.$(ARCH).o	   \
+$(SRC)/cgrid.$(ARCH).o		   \
+$(SRC)/coords.$(ARCH).o	   \
+$(SRC)/cplot.$(ARCH).o		   \
+$(SRC)/csystem.$(ARCH).o	   \
+$(SRC)/coord_systems.$(ARCH).o	   \
+$(SRC)/ctimes.$(ARCH).o	   \
+$(SRC)/cval.$(ARCH).o		   \
+$(SRC)/czplot.$(ARCH).o	   \
+$(SRC)/drizzle.$(ARCH).o	   \
+$(SRC)/flux.$(ARCH).o		   \
+$(SRC)/fixwrap.$(ARCH).o	   \
+$(SRC)/gauss.$(ARCH).o		   \
+$(SRC)/getvel.$(ARCH).o	   \
+$(SRC)/getlst.$(ARCH).o	   \
+$(SRC)/medianmap.$(ARCH).o	   \
+$(SRC)/mkgauss.$(ARCH).o	   \
+$(SRC)/multifit.$(ARCH).o	   \
+$(SRC)/objload.$(ARCH).o	   \
+$(SRC)/outline.$(ARCH).o	   \
+$(SRC)/polar.$(ARCH).o		   \
+$(SRC)/precess.$(ARCH).o	   \
+$(SRC)/profile.$(ARCH).o	   \
+$(SRC)/region.$(ARCH).o	   \
+$(SRC)/rotcurve.$(ARCH).o	   \
+$(SRC)/scale.$(ARCH).o		   \
+$(SRC)/sexigesimal.$(ARCH).o	   \
+$(SRC)/spec.$(ARCH).o		   \
+$(SRC)/star.$(ARCH).o		   \
+$(SRC)/transform.$(ARCH).o        \
+$(SRC)/imsub.$(ARCH).o		   \
+$(SRC)/imfit.$(ARCH).o		   \
+$(SRC)/imfit-fgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss.$(ARCH).o	   \
+$(SRC)/imfit-pgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qgauss.$(ARCH).o	   \
+$(SRC)/imfit-qgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-sgauss.$(ARCH).o	   \
+$(SRC)/imfit-sgauss-psf.$(ARCH).o	   \
+$(SRC)/imfit-qfgauss.$(ARCH).o	   \
+$(SRC)/imfit-qrgauss.$(ARCH).o	   
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libastrocmd.$(ARCH).a: $(srcs)
+$(LIB)/libastrocmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libastrocmd.a: $(LIB)/libastrocmd.$(ARCH).a
+$(DESTLIB)/libastrocmd.$(DLLTYPE): $(LIB)/libastrocmd.$(ARCH).$(DLLTYPE)
+
+libastrocmd: $(DESTLIB)/libastrocmd.a $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libastrocmd.a
+	rm -f $(DESTLIB)/libastrocmd.$(DLLTYPE)
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/altaz.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/altaz.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/altaz.c	(revision 17422)
@@ -0,0 +1,88 @@
+# include "astro.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  double alt, az, lat, rot;
+  double ha, dec;
+  double sind, sinh, cosh;
+  char *latstr;
+
+  if (argc != 6) goto usage;
+
+  if (!strcmp (argv[1], "-h")) goto radec;
+  if (!strcmp (argv[1], "-c")) goto altaz;
+
+ radec:
+  /* ha/dec -> alt/az */
+  ha  = atof (argv[2]);
+  dec = atof (argv[3]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+ 
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  az = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[4], alt);
+  set_variable (argv[5], az);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ altaz:
+  /* alt/az -> ha/dec */
+  alt = atof (argv[4]);
+  az  = atof (argv[5]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  ha = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[2], ha);
+  set_variable (argv[3], dec);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -h (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -c (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "       -h alt/az to ha/dec, -c ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables provided\n");
+  return (FALSE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/biassub.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/biassub.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/biassub.c	(revision 17422)
@@ -0,0 +1,126 @@
+# include "astro.h"
+
+int biassub (int argc, char **argv) {
+  
+  int i, j, k, N, dir, nlong, nwide, start;
+  int sx, sy, nx, ny, NX, NY, NoVector, Nval;
+  float *V, *DV, dV, *vect, *segment, val;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  xvec = yvec = NULL;
+  NoVector = TRUE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    NoVector = FALSE;
+    remove_argument (N, &argc, argv);
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: biassub <buffer> sx sy nx ny dir [-v N V]\n");
+    gprint (GP_ERR, "  optional storage of vector and sequence in N and V\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  dir = atof (argv[6]);
+  if ((dir != 0) && (dir != 1)) {
+    gprint (GP_ERR, " dir must be either 0 (x) or 1 (y)\n");
+    return (FALSE);
+  }
+  if (dir) {
+    start = sy;
+    nwide = nx;
+    nlong = ny;
+  } else {
+    start = sx;
+    nwide = ny;
+    nlong = nx;
+  }    
+  gprint (GP_LOG, "start: %d %d  size: %d %d\n", sx, sy, nx, ny);
+
+    if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (vect, float, nlong);
+  ALLOCATE (segment, float, nwide);
+
+  NX = buf[0].matrix.Naxis[0];
+  NY = buf[0].matrix.Naxis[1];
+  if (dir) {
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	segment[i] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[j-sy] = val / Nval;
+    }
+  } else {
+    for (i = 0; i < nx; i++) {
+      V = (float *)(buf[0].matrix.buffer) + sy*NX + sx + i; 
+      for (j = 0; j < ny; j++, V+=NX) {
+	segment[j] = *V;
+      }
+      fsort (segment, nwide);
+      val = Nval = 0;
+      for (k = 0.25*nwide; k <=0.75*nwide; k++) {
+	val += segment[k];
+	Nval ++;
+      }
+      vect[i] = val / Nval;
+    }
+  }
+
+  if (!NoVector) {
+    xvec[0].Nelements = yvec[0].Nelements = nlong;
+    REALLOCATE (xvec[0].elements, float, nlong);
+    REALLOCATE (yvec[0].elements, float, nlong);
+    for (i = 0; i < nlong; i++) {
+      xvec[0].elements[i] = i + start;
+      yvec[0].elements[i] = vect[i];
+    }
+  }
+
+  if (dir) {
+    /* here we run all the way across in X for the defined Y range */
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX; 
+      dV = vect[j];
+      for (i = 0; i < NX; i++, V++) {
+	*V -= dV;
+      }
+    }
+  } else {
+    /* here we run all the way across in Y for the defined X range */
+    for (j = 0; j < NY; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*NX + sx; 
+      DV = vect;
+      for (i = 0; i < nx; i++, V++, DV++) {
+	*V -= *DV;
+      }
+    }
+  }
+
+  free (segment);
+  free (vect);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cgrid.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 17422)
@@ -0,0 +1,399 @@
+# include "astro.h"
+# define CHECKELEMENTS \
+  if (N == NELEMENTS) { \
+    NELEMENTS +=200; \
+    REALLOCATE (Xvec.elements, float, NELEMENTS); \
+    REALLOCATE (Yvec.elements, float, NELEMENTS); \
+  }
+
+int cgrid (int argc, char **argv) {
+  
+  double range, lrange, factor, mantis, power, fmantis;
+  double firstRA, firstDEC, minorRA, minorDEC;
+  double r, d, dR, dD, D;
+  double x, y;
+  Vector Xvec, Yvec;
+  int kapa, NorthPole, SouthPole, N, OnPic, LOnPic, status, NELEMENTS, First;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: cgrid [style]\n");
+    return (FALSE);
+  }
+
+  /* are we plotting one of the poles? */
+  NorthPole = SouthPole = FALSE;
+  status = RD_to_XY (&x, &y, 0.0, 90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    NorthPole = TRUE;
+  status = RD_to_XY (&x, &y, 0.0, -90.0, &graphmode.coords);
+  if ((x >= graphmode.xmin) && (x <= graphmode.xmax) && 
+      (y >= graphmode.ymin) && (y <= graphmode.ymax) && status)
+    SouthPole = TRUE;
+
+  /* set spacings for RA */
+  minorRA = minorDEC = 0.1;
+  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  if (NorthPole || SouthPole) range = 360;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    minorRA = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    minorRA = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    minorRA = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    minorRA = 0.5 * power;
+  }
+  dR = range / 100.0;
+  /* set spacings for DEC */
+  range = MIN (fabs(graphmode.xmax-graphmode.xmin), fabs(graphmode.ymax-graphmode.ymin));
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    minorDEC = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    minorDEC = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    minorDEC = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    minorDEC = 0.5 * power;
+  }
+  dD = range / 100.0;
+
+  /* choose a starting point */
+  if ((int)(graphmode.coords.crval1/minorRA) == (graphmode.coords.crval1/minorRA)) {
+    firstRA = graphmode.coords.crval1;
+  } else {
+    firstRA = minorRA + minorRA*((int)(graphmode.coords.crval1/minorRA));
+  }
+  if ((int)(graphmode.coords.crval2/minorDEC) == (graphmode.coords.crval2/minorDEC)) {
+    firstDEC = graphmode.coords.crval2;
+  } else {
+    firstDEC = minorDEC + minorDEC*((int)(graphmode.coords.crval2/minorDEC));
+  }
+  if (SouthPole) firstDEC = -90;
+  if (NorthPole) firstDEC = 90;
+  
+  /* prepare vectors to hold data */
+  NELEMENTS = 200;
+  ALLOCATE (Xvec.elements, float, NELEMENTS);
+  ALLOCATE (Yvec.elements, float, NELEMENTS);
+  N = 0;
+  
+  /***  do consecutive RA lines, first increasing **/
+  OnPic = TRUE;
+  for (r = firstRA; (r <= firstRA + 180) && (OnPic); r += minorRA) {
+    /* first, DEC increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (d = firstDEC; (d < 90 + dD) && (LOnPic || NorthPole || SouthPole); d += dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, DEC decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (d = firstDEC; (d > -90 - dD) && (LOnPic || NorthPole || SouthPole); d -= dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      } 
+    }
+  }
+
+  /***  do consecutive RA lines, decreasing **/
+  OnPic = TRUE;
+  for (r = firstRA; (r >=  firstRA - 180) && (OnPic); r -= minorRA) {
+    /* first, DEC increasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    for (d = firstDEC; (d < 90 + dD) && (LOnPic || NorthPole || SouthPole); d += dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, DEC decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (d = firstDEC; (d > -90 - dD) && (LOnPic || NorthPole || SouthPole); d -= dD) {
+      D = MAX (-90, MIN(90, d));
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+
+  /***  do consecutive DEC lines, first increasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d < 90 + dD) && (OnPic); d += minorDEC) {
+    D = MAX (-90, MIN(90, d));
+    /* first, RA increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (r = firstRA; (r < firstRA + 180) && (LOnPic || NorthPole || SouthPole); r += dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, RA decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (r = firstRA; (r > firstRA - 180) && (LOnPic || NorthPole || SouthPole); r -= dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+
+  /***  do consecutive DEC lines, decreasing **/
+  OnPic = TRUE;
+  for (d = firstDEC; (d > -90 - dD) && (OnPic); d -= minorDEC) {
+    D = MAX (-90, MIN(90, d));
+    /* first, RA increasing */
+    LOnPic = TRUE;
+    OnPic = FALSE;
+    First = TRUE;
+    for (r = firstRA; (r < firstRA + 180) && (LOnPic || NorthPole || SouthPole); r += dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+    /* next, RA decreasing */
+    First = TRUE;
+    LOnPic = TRUE;
+    for (r = firstRA; (r > firstRA - 180) && (LOnPic || NorthPole || SouthPole); r -= dR) {
+      status = fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, D, &graphmode.coords);
+      /*
+      if ((fabs(Xvec.elements[N] - Xvec.elements[N-1]) > 10) && (fabs(Yvec.elements[N] - Yvec.elements[N-1]) > 10))
+	First = TRUE;
+	*/
+      if ((Xvec.elements[N] >= graphmode.xmin) && (Xvec.elements[N] <= graphmode.xmax) && 
+	  (Yvec.elements[N] >= graphmode.ymin) && (Yvec.elements[N] <= graphmode.ymax) && status) {
+	N++;
+	CHECKELEMENTS;
+	OnPic = TRUE;
+	if (!First) {
+	  Xvec.elements[N] = Xvec.elements[N-1];
+	  Yvec.elements[N] = Yvec.elements[N-1];
+	  N++;
+	  CHECKELEMENTS;
+	} else {
+	  if (N > 1) {
+	    Xvec.elements[N-2] = Xvec.elements[N-1];
+	    Yvec.elements[N-2] = Yvec.elements[N-1];
+	    N--;
+	  }
+	  First = FALSE;
+	}
+      } else {
+	LOnPic = FALSE;
+	First = TRUE;
+      }
+    }
+  }
+  
+  /* send the line segments as connect-points */
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coord_systems.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coord_systems.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coord_systems.c	(revision 17422)
@@ -0,0 +1,141 @@
+# include "astro.h"
+    
+CoordTransform *InitTransform (CoordTransformSystem input, CoordTransformSystem output) {
+
+  CoordTransform *transform;
+  struct timeval now;
+  struct tm *local;
+  double T;
+
+  ALLOCATE (transform, CoordTransform, 1);
+  transform->isIdentity = FALSE;
+  
+  switch (input) {
+    case COORD_CELESTIAL:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	case COORD_GALACTIC:
+	  transform->phi = -62.60*RAD_DEG;
+	  transform->Xo  = 282.25*RAD_DEG;
+	  transform->xo  =  33.00;
+	  break;
+	case COORD_ECLIPTIC:
+	  gettimeofday (&now, NULL);
+	  local = localtime (&now.tv_sec);
+	  T = local[0].tm_year / 100.0;
+	  transform->phi = -1.0*RAD_DEG*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  break;
+	default:
+	  abort();
+      }
+      break;
+    case COORD_ECLIPTIC:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  gettimeofday (&now, (struct timezone *) NULL);
+	  local = localtime (&now.tv_sec);
+	  T = local[0].tm_year / 100.0;
+	  transform->phi = RAD_DEG*(23.452294 - 0.013013*T - 0.000001639*T*T + 0.000000503*T*T*T);
+	  transform->Xo = 0.0;
+	  transform->xo = 0.0;
+	  break;
+	case COORD_GALACTIC:
+	  return NULL;
+	  break;
+	case COORD_ECLIPTIC:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	default:
+	  abort();
+      }
+    case COORD_GALACTIC:
+      switch (output) {
+	case COORD_CELESTIAL:
+	  transform->phi =  62.60*RAD_DEG;
+	  transform->Xo  =  33.00*RAD_DEG;
+	  transform->xo  = 282.25;
+	  break;
+	case COORD_GALACTIC:
+	  transform->phi = 0.0;
+	  transform->Xo  = 0.0;
+	  transform->xo  = 0.0;
+	  transform->isIdentity = TRUE;
+	  break;
+	case COORD_ECLIPTIC:
+	  return NULL;
+	default:
+	  abort();
+      }
+    default:
+      abort();
+  }
+ 
+  // pre-calculated constants:
+  transform->sin_phi_cos_Xo = sin(transform->phi)*cos(transform->Xo);
+  transform->sin_phi_sin_Xo = sin(transform->phi)*sin(transform->Xo);
+  transform->cos_phi        = cos(transform->phi);
+  
+  transform->cos_phi_cos_Xo = cos(transform->phi)*cos(transform->Xo);
+  transform->cos_phi_sin_Xo = cos(transform->phi)*sin(transform->Xo);
+  transform->sin_phi        = sin(transform->phi);
+  transform->cos_Xo 	    = cos(transform->Xo);
+  transform->sin_Xo 	    = sin(transform->Xo);
+
+  return transform;
+}
+
+// input and output coordinates are in degrees
+int ApplyTransform (double *x, double *y, double X, double Y, CoordTransform *transform) {
+
+  double sin_x, sin_y, cos_x, cos_y;
+
+
+  if (transform == NULL) return (FALSE);
+
+  if (transform->isIdentity) {
+    *x = X;
+    *y = Y;
+    return (TRUE);
+  }
+
+  X *= RAD_DEG;
+  Y *= RAD_DEG;
+
+  // recast with constants extracted:
+  sin_y = cos(Y)*sin(X)*transform->sin_phi_cos_Xo - cos(Y)*cos(X)*transform->sin_phi_sin_Xo + sin(Y)*transform->cos_phi;
+  cos_y = sqrt (1 - sin_y*sin_y);
+
+  sin_x = cos(Y)*sin(X)*transform->cos_phi_cos_Xo - cos(Y)*cos(X)*transform->cos_phi_sin_Xo - sin(Y)*transform->sin_phi;
+  cos_x = cos(Y)*cos(X)*transform->cos_Xo + cos(Y)*sin(X)*transform->sin_Xo;
+      
+  // atan2 returns -pi : +pi
+  *x = DEG_RAD * atan2 (sin_x, cos_x) + transform->xo;
+  if ((*x) < 0.0) (*x) += 360;
+
+  // should be in range -pi/2 : +pi/2
+  *y = DEG_RAD * atan2 (sin_y, cos_y);
+
+  return (TRUE);
+}
+
+// sin_y = cos(Y)*sin(X - Xo)*sin(phi) + sin(Y)*cos(phi);
+
+// sin_x = (cos(Y)*sin(X - Xo)*cos(phi) - sin(Y)*sin(phi)) /  cos_y;
+// cos_x = cos(Y)*cos(X - Xo) / cos_y;
+
+// multiplying both sides by cos_y:
+// sin_x = (cos(Y)*sin(X - Xo)*cos(phi) - sin(Y)*sin(phi));
+// cos_x = cos(Y)*cos(X - Xo);
+      
+// sin(a - b) = sin(a)*cos(b) - sin(b)*cos(a);
+// cos(a - b) = sin(a)*sin(b) + cos(a)*cos(b);
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coords.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coords.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/coords.c	(revision 17422)
@@ -0,0 +1,109 @@
+# include "astro.h"
+
+enum {NONE, SKY, PIXEL, VECTOR, SCALAR};
+
+int coords (int argc, char **argv) {
+
+  int i, mode, form, N, Quiet;
+  double Xin, Yin, Xout, Yout;
+  char *MOSAIC;
+  Coords coords, moscoords;
+  Buffer *buf, *mosbuffer;
+  Vector *xvec, *yvec;
+
+  MOSAIC = NULL;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  form = NONE;
+  mode = NONE;
+  if ((N = get_argument (argc, argv, "-p"))) {
+    remove_argument (N, &argc, argv);
+    mode = SKY;
+  }
+  if ((N = get_argument (argc, argv, "-c"))) {
+    if (mode == SKY) goto syntax;
+    remove_argument (N, &argc, argv);
+    mode = PIXEL;
+  }
+  if (mode == NONE) goto syntax;
+  if (argc != 4) goto syntax;
+
+  if (SelectScalar (argv[2], &Xin)) {
+    if (!SelectScalar (argv[3], &Yin)) {
+      gprint (GP_ERR, "syntax error: mixed vector and scalar?\n");
+      return (FALSE);
+    }
+    form = SCALAR;
+  } else {
+    if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if (xvec[0].Nelements != yvec[0].Nelements) {
+      fprintf (stderr, "mis-matched vector lengths\n");
+      return (FALSE);
+    }
+    form = VECTOR;
+  }      
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto escape;
+  GetCoords (&coords, &buf[0].header);
+  if (!strcmp(&coords.ctype[4], "-WRP")) {
+    if (MOSAIC == NULL) {
+      gprint (GP_ERR, "must supply mosaic for WRP coords\n");
+      return (FALSE);
+    }
+    if ((mosbuffer = SelectBuffer (MOSAIC, OLDBUFFER, TRUE)) == NULL) goto escape;
+    GetCoords (&moscoords, &mosbuffer[0].header);
+    RegisterMosaic (&moscoords);
+  }
+  
+  if (form == SCALAR) {
+    if (mode == SKY) {
+      XY_to_RD (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%10.6f %10.6f\n", Xout, Yout);
+      set_variable ("RA", Xout);
+      set_variable ("DEC", Yout);
+      return (TRUE);
+    }
+    if (mode == PIXEL) {
+      RD_to_XY (&Xout, &Yout, Xin, Yin, &coords);
+      if (!Quiet) gprint (GP_LOG, "%7.2f %7.2f\n", Xout, Yout);
+      set_variable ("Xc", Xout);
+      set_variable ("Yc", Yout);
+      return (TRUE);
+    }
+  }
+  if (mode == SKY) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      fXY_to_RD (&xvec[0].elements[i], &yvec[0].elements[i], xvec[0].elements[i], yvec[0].elements[i], &coords);
+    }
+    return (TRUE);
+  }
+  if (mode == PIXEL) {
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      fRD_to_XY (&xvec[0].elements[i], &yvec[0].elements[i], xvec[0].elements[i], yvec[0].elements[i], &coords);
+    }
+    return (TRUE);
+  }
+  return (FALSE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: coords [buffer] (-c R D) | (-p X Y)\n");
+  gprint (GP_ERR, "only one of -p or -c can be used\n");
+ escape:
+  if (MOSAIC != NULL) free (MOSAIC);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cplot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cplot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cplot.c	(revision 17422)
@@ -0,0 +1,85 @@
+# include "astro.h"
+
+int cplot (int argc, char **argv) {
+  
+  int i, kapa, Npts, status, leftside;
+  float *x, *y, *r, *d, Rmin, Rmax, Rmid;
+  Vector Xvec, Yvec, *xvec, *yvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cplot <ra> <dec> [style]\n");
+    return (FALSE);
+  }
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  ALLOCATE (Xvec.elements, float, xvec[0].Nelements);
+  ALLOCATE (Yvec.elements, float, xvec[0].Nelements);
+    
+  Xvec.Nelements = xvec[0].Nelements;
+  Yvec.Nelements = xvec[0].Nelements;
+  
+  r = xvec[0].elements;
+  d = yvec[0].elements;
+  x = Xvec.elements;
+  y = Yvec.elements;
+  
+  Npts = 0;
+  for (i = 0; i < Xvec.Nelements; i++, r++, d++) {
+    while (*r < Rmin) *r += 360.0;
+    while (*r > Rmax) *r -= 360.0;
+
+    // for pair-by-pair connections, check on second point if we straddle the back midline
+    if ((graphmode.ptype == 100) && (i % 2)) {
+      leftside = (r[-1] < Rmid); // if first of the pair is left, second must be as well
+      if ( leftside && (r[0] > Rmid + 90)) { r[0] -= 360.0; }
+      if (!leftside && (r[0] < Rmid - 90)) { r[0] += 360.0; }
+    }
+    status = fRD_to_XY (x, y, *r, *d, &graphmode.coords);
+
+    // if we fail on one of the points, drop the corresponding pair
+    if (!status) {
+      if (graphmode.ptype == 100) {
+	if (i % 2) {
+	  // for odd points, skip previous also
+	  x--;
+	  y--;
+	  Npts--;
+	} else {
+	  // for even points, skip previous also
+	  i++;
+	  r++;
+	  d++;
+	}
+      }
+      continue;
+    }
+    x++;
+    y++;
+    Npts++;
+  }
+  Xvec.Nelements = Npts;
+
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  
+  free (Xvec.elements);
+  free (Yvec.elements);
+    
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/csystem.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/csystem.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/csystem.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "astro.h"
+
+int csystem (int argc, char **argv) {
+
+  /* USAGE: csystem [C/G/E/H] [C/G/E/H] [epoch] */
+  int i;
+  double X, Y, x, y;
+  float *xptr, *yptr;
+  Vector *xvec, *yvec;
+  CoordTransformSystem input, output;
+  CoordTransform *transform;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: csystems [C/G/E/H] [C/G/E/H] X Y\n");
+    return (FALSE);
+  }
+
+  switch (argv[1][0]) {
+    case 'C': input = COORD_CELESTIAL; break;
+    case 'G': input = COORD_GALACTIC; break;
+    case 'E': input = COORD_ECLIPTIC; break;
+    default: abort();
+  }
+
+  switch (argv[2][0]) {
+    case 'C': output = COORD_CELESTIAL; break;
+    case 'G': output = COORD_GALACTIC; break;
+    case 'E': output = COORD_ECLIPTIC; break;
+    default: abort();
+  }
+
+  transform = InitTransform (input, output);
+  if (transform == NULL) {
+    gprint (GP_ERR, "transform %c to %c is not yet defined\n", argv[1][0], argv[2][0]);
+    return (FALSE);
+  }
+    
+  if (SelectScalar (argv[3], &X)) {
+    if (!SelectScalar (argv[4], &Y)) return (FALSE);
+      
+    ApplyTransform (&x, &y, X, Y, transform);
+
+    gprint (GP_LOG, "%10.6f %10.6f\n", x, y);
+    return (TRUE);
+  }
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  xptr = xvec[0].elements;
+  yptr = yvec[0].elements;
+
+  for (i = 0; i < xvec[0].Nelements; i++, xptr++, yptr++) {
+    // ApplyTransform takes (double *), but xptr, yptr are (float *)
+    ApplyTransform (&x, &y, *xptr, *yptr, transform);
+    *xptr = x;
+    *yptr = y;
+  }
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/ctimes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/ctimes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/ctimes.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "astro.h"
+
+int ctimes (int argc, char **argv) {
+
+  int Reference, TimeFormat, N;
+  double value;
+  time_t time, TimeReference;
+  char *date, *Variable;
+
+  Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ctimes [-ref (value) / -abs (date)] [-var name]\n");
+    return (FALSE);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Reference = FALSE;
+  if (!strcmp (argv[1], "-ref")) Reference = TRUE;
+
+  if (Reference) {
+
+    value = atof (argv[2]);
+    time = TimeRef (value, TimeReference, TimeFormat);
+    date = ohana_sec_to_date (time);
+    
+    if (Variable != (char *) NULL) {
+      set_str_variable (Variable, date);
+      free (Variable);
+    } else {
+      gprint (GP_ERR, "time: %s\n", date);
+    }
+
+    free (date);
+    return (TRUE);
+
+  } else {
+
+    if (strcmp (argv[1], "-abs")) {
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+
+    if (!ohana_str_to_time (argv[2], &time)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    
+    value = TimeValue (time, TimeReference, TimeFormat);
+    
+    if (Variable != (char *) NULL) {
+      set_variable (Variable, value);
+      free (Variable);
+      return (TRUE);
+    }
+    gprint (GP_ERR, "time: %f\n", value);
+    return (TRUE);
+  }
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cval.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cval.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/cval.c	(revision 17422)
@@ -0,0 +1,51 @@
+# include "astro.h"
+
+int cval (int argc, char **argv) {
+  
+  int i, j, Nx;
+  int sx, sy, nx, ny, xo, yo, dx, dy;
+  float *V, cval, val, sn, sky;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: cval <buffer> x y dx dy sky\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  xo = atof (argv[2]);
+  yo = atof (argv[3]);
+  dx = atof (argv[4]);
+  dy = atof (argv[5]);
+  sky = atof (argv[6]);
+
+  sx = xo - dx;
+  sy = yo - dy;
+  nx = 2*dx + 1;
+  ny = 2*dy + 1;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  V = (float *)buf[0].matrix.buffer;
+  Nx = buf[0].matrix.Naxis[0];
+  val = V[xo + yo*Nx];
+
+  sn = 0;
+  cval = 0;
+  for (j = sy; j < sy + ny; j++) {
+    for (i = sx; i < sx + nx; i++) {
+      cval += (val - V[i + j*Nx]) / sqrt(V[i + j*Nx]);
+      sn += SQ (V[i + j*Nx] - sky) / V[i + j*Nx];
+    }
+  }
+
+  gprint (GP_ERR, "cval: %f  sn: %f\n", cval, sqrt(sn));
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/czplot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/czplot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/czplot.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "astro.h"
+
+int czplot (int argc, char **argv) {
+  
+  int i, kapa, Npts;
+  double min, range, Rmin, Rmax;
+  float *in, *out, *r, *d, *x, *y;
+  Vector Xvec, Yvec, Zvec, *xvec, *yvec, *zvec;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: czplot <x> <y> <z> min max\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  Xvec.Nelements = xvec[0].Nelements;
+  Yvec.Nelements = xvec[0].Nelements;
+  Zvec.Nelements = zvec[0].Nelements;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+  
+  r   = xvec[0].elements;
+  d   = yvec[0].elements;
+  in  = zvec[0].elements;
+  x   = Xvec.elements;
+  y   = Yvec.elements;
+  out = Zvec.elements;
+  for (i = 0; i < Zvec.Nelements; i++, in++, out++, r++, d++, x++, y++) {
+    *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+    while (*r < Rmin) *r += 360.0;
+    while (*r > Rmax) *r -= 360.0;
+    fRD_to_XY (x, y, *r, *d, &graphmode.coords);
+  }
+
+  graphmode.style = 2;
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0;
+  Npts = Xvec.Nelements;
+  PlotVectorTriplet (kapa, Npts, Xvec.elements, Yvec.elements, Zvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (Zvec.elements);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/drizzle.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/drizzle.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/drizzle.c	(revision 17422)
@@ -0,0 +1,302 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+static int ZERO;
+
+static int ROTATE;
+static double rot_phi, rot_alpha, rot_delta;
+static double rot_cdp, rot_sdp;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt, *mask;
+
+int drizzle (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ROTATE = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    /* -roll phi alpha_pole delta_pole */
+    /* XXX need to clarify the meaning of phi, alpha, delta */
+    ROTATE = TRUE;
+    remove_argument (N, &argc, argv);
+    rot_phi   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_alpha = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    rot_delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+
+    rot_cdp = cos(RAD_DEG*rot_delta);
+    rot_sdp = sin(RAD_DEG*rot_delta);
+  }
+
+  mask = NULL;
+  if ((N = get_argument (argc, argv, "-mask"))) {
+    remove_argument (N, &argc, argv);
+    if ((mask = SelectBuffer (argv[N], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping output to input\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_out, &coords_in, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt, *Vmk;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+  Vmk  = NULL;
+  Vmk = (mask == NULL) ? NULL : (float *) mask[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  gprint (GP_ERR, "mapping input to output\n");
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      if (!set_linear_terms (&coords_in, &coords_out, i, j, Npix)) continue;
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  if (Vmk && Vmk[Ni]) continue;
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+int rotate_coords (double *phi, double *theta, double alpha, double delta) {
+
+  double sda, cda, cd, sd, sth;
+  double x, y;
+  
+  sda = sin(RAD_DEG*(alpha - rot_alpha));
+  cda = cos(RAD_DEG*(alpha - rot_alpha));
+  sd = sin(RAD_DEG*delta);
+  cd = cos(RAD_DEG*delta);
+  
+  sth = -cd*sda*rot_cdp + sd*rot_sdp;
+  y   = +cd*sda*rot_sdp + sd*rot_cdp;
+  x   = +cd*cda;
+
+  *theta = DEG_RAD*asin(sth);
+  *phi   = DEG_RAD*atan2(y,x) + rot_phi;
+  
+  while (*phi <   0.0) *phi += 360.0;
+  while (*phi > 360.0) *phi -= 360.0;
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+int set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d, phi, theta;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    if (!XY_to_RD (&r, &d, Xin, Yin, in)) return (FALSE);
+    if (ROTATE) { 
+      rotate_coords (&phi, &theta, r, d);
+      r = phi;
+      d = theta;
+    }
+    if (!RD_to_XY (&Xout, &Yout, r, d, out)) return (FALSE);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+  return (TRUE);
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/fixwrap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/fixwrap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/fixwrap.c	(revision 17422)
@@ -0,0 +1,110 @@
+# include "astro.h"
+
+int fixwrap (int argc, char **argv) {
+  
+  int i, j, Nflip, n, Ny, Nx, flip, sat, rowfix;
+  float *Vin, *outf, *outb, dO;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: fixwrap <in> (rowfix)\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  rowfix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (outf, float, MAX(Nx, Ny));
+  ALLOCATE (outb, float, MAX(Nx, Ny));
+
+  for (j = 0; j < Ny; j++) {
+    Vin  = (float *)(in[0].matrix.buffer)  + j*Nx;
+
+    /* measure forward flips */ 
+    sat = FALSE;
+    for (i = 0; i < Nx; i++) {
+      if ((i < 1056) || (i > 2079)) {
+	outf[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outf[i-1] - outf[i-2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i+1]  > 32766.5)) sat  = TRUE;
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-Nx] > 65534.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i-1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outf[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* measure backward flips */ 
+    sat = FALSE;
+    for (i = Nx - 1; i >= 0; i--) {
+      if ((i < 1056) || (i > 2077)) {
+	outb[i] = Vin[i];
+	continue;
+      }
+
+      dO = 2*outb[i+1] - outb[i+2] - Vin[i];
+      flip = (fabs(dO - 0x8000) < fabs(dO));
+
+      /* going onto saturation */
+      if (!sat && (Vin[i] > 32766.5) && (Vin[i-1] > 32766.5)) sat  = TRUE;
+
+      /* exiting saturation region */
+      if ( sat && (Vin[i] < 32766.5) && (Vin[i+1] < 32766.5)) sat = FALSE;
+
+      if (sat) flip = TRUE;
+
+      outb[i] = flip ? (Vin[i] + 0x8000) : Vin[i];
+    }
+
+    /* compare forward and backward flips: where they disagree, use column to predict */
+    for (i = 0; (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+      if (outf[i] != outb[i]) {
+	/* use this column to predict, not the row */
+	dO = 2*Vin[i - Nx] - Vin[i-2*Nx] - Vin[i];
+	flip = (fabs(dO - 0x8000) < fabs(dO));
+	outf[i] = flip ? Vin[i] + 0x8000 : Vin[i];
+	outb[i] = outf[i];  /* save this for the row segments below */
+      }
+    }
+
+    /* compare this row and previous (now fixed) row. if large segments are flipped, fix them */
+    for (i = 0; rowfix && (j > 1) && (i < Nx); i++) {
+      if ((i < 1056) || (i > 2077)) continue;
+
+      if (fabs(outf[i] - Vin[i-Nx]) > 15000) {
+	Nflip = 0;
+	for (n = i - 8; n < i + 9; n++) {
+	  if (fabs(outb[n] - Vin[n-Nx]) > 15000) {
+	    Nflip ++;
+	  }
+	}
+	if (Nflip > 5) {
+	  if (outf[i] - Vin[i-Nx] > 15000) {
+	    outf[i] -= 0x8000;
+	  } else {
+	    outf[i] += 0x8000;
+	  }	    
+	}
+      }
+    }
+    for (i = 0; i < Nx; i++) {
+      Vin[i] = outf[i];
+    }
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/flux.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/flux.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/flux.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "astro.h"
+
+int flux (int argc, char **argv) {
+  
+  int i, j, k, xmin, ymin, xmax, ymax;
+  void *oldsignal;
+  double ax, ay, s, S, flux;
+  double bx[5], by[5], x[5], y[5], bb[5];
+  float *V;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: flux <buffer> (region)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  xmin = buf[0].matrix.Naxis[0];
+  xmax = 0;
+  ymin = buf[0].matrix.Naxis[1];
+  ymax = 0;
+  for (i = 0; i < 4; i++) {
+    fscanf (f, "%lf %lf", &x[i], &y[i]);
+    xmin = MAX (0, MIN (xmin, x[i] - 1));
+    ymin = MAX (0, MIN (ymin, y[i] - 1));
+    xmax = MIN (MAX (xmax, x[i] + 1), buf[0].matrix.Naxis[0]);
+    ymax = MIN (MAX (ymax, y[i] + 1), buf[0].matrix.Naxis[1]);
+  }
+  fclose (f);
+
+  x[4] = x[0]; y[4] = y[0];
+  for (i = 0; i < 4; i++) {
+    bx[i] = x[i+1] - x[i];
+    by[i] = y[i+1] - y[i];
+  }
+  bx[4] = bx[0]; by[4] = by[0];
+  for (i = 0; i < 4; i++) {
+    bb[i] = hypot (bx[i], by[i]) * SIGN (bx[i]*by[i+1] - bx[i+1]*by[i]);
+  }
+  gprint (GP_ERR, "%f %f %f %f\n", bb[0], bb[1], bb[2], bb[3]);
+
+  /* this only works for convex contours --
+   we have to add up the angles for concave contours */
+  flux = 0;
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (j = ymin; (j < ymax) && !interrupt; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xmin; 
+    for (i = xmin; (i < xmax) && !interrupt; i++, V++) {
+      S = 1.0;
+      for (k = 0; k < 4; k++) {
+	ax = i - x[k];
+	ay = j - y[k];
+	s = (ay*bx[k] - ax*by[k]) / bb[k] + 0.5;
+	/* s = b x a / |b|, with the correct sign (above) so inside is positive */
+	s = MAX (0.0, MIN (1.0, s));  /* s is between 0.0 and 1.0 */
+	S *= s;
+      }
+      flux += S * (*V);
+    }
+  }
+  signal (SIGINT, oldsignal);
+
+  gprint (GP_LOG, "flux: %f\n", flux);
+  set_variable ("FLUX", flux);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/gauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/gauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/gauss.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "astro.h"
+
+int gauss (int argc, char **argv) {
+
+  char key[20];
+  int i, N, Npix, Nborder, Nspot;
+  double X, Y, Z, ZP, RA, DEC, max;
+  int kapa;
+  char *name;
+  Buffer *buf;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: gauss Npix [Nspots] [-border N] [-sat cnts]\n");
+    return (FALSE);
+  }
+  
+  if (kapa < 1) {
+    gprint (GP_ERR, "no active TV\n");
+    return (FALSE);
+  }
+
+  Nspot = 0;
+  Npix = atof (argv[1]);
+  if (argc == 3) {
+    Nspot = atof (argv[2]);
+  }
+
+  if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  KiiCursorOn (kapa);
+
+  for (i = 0; (i < Nspot) || (Nspot == 0); i++) {
+    KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key);
+    if (!strcasecmp (key, "Q")) break;
+    Z = get_aperture_stats (&buf[0].matrix, (int)(X+0.5), (int)(Y+0.5), Npix, Nborder, max);
+  }
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getlst.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getlst.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getlst.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "astro.h"
+
+int getlst (int argc, char **argv) {
+
+  int N;
+  time_t time;
+  double jd, lst, longitude;
+  char *Variable;
+
+  Variable = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (argc != 3) goto syntax;
+
+  if (!ohana_str_to_time (argv[1], &time)) {
+      if (Variable != NULL) free (Variable);
+      return (FALSE);
+  }
+
+  longitude = atof (argv[2]);
+
+  jd = ohana_sec_to_jd (time);
+  fprintf (stderr, "jd: %f\n", jd);
+
+  lst = ohana_lst (jd, longitude);
+
+  if (Variable != NULL) {
+      set_variable (Variable, lst);
+      free (Variable);
+      return (TRUE);
+  }
+  gprint (GP_ERR, "lst: %f\n", lst);
+  return (TRUE);
+
+ syntax:
+  gprint (GP_ERR, "USAGE: getlst (time) (longitude) [-var name]\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getvel.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getvel.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/getvel.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "astro.h"
+
+int getvel (int argc, char **argv) {
+  
+  int i, n, Ncurve;
+  int nx, ny;
+  double L, V, Vo, dV, Bo, dB;
+  double xo, yo;
+  double sl, cl, wo, Ro, Rs, wr, r, fr, d, x;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve buf X Y curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&buf[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&buf[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&buf[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&buf[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&buf[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  while (L >= 360) {L -= 360.0;}
+  while (L < 0.0)  {L += 360.0;}
+  gprint (GP_ERR, "L: %f\n", L);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  x = atof (argv[2]);
+  /* this method depends on wr monotonically decreasing */
+
+  V = (x - xo) * dV + Vo;
+  wr = V/Rs + wo;
+  for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+  if ((n == 0) || (n == Ncurve)) {
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f\n", V, wr, W[0], W[Ncurve-1]);
+    return (TRUE);
+  }
+  r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+  fr = (Ro/r);
+  if (r < fabs(Rs)) { /* can't be on rotation curve */
+    gprint (GP_ERR, "velocity out of reasonable range\n");
+    gprint (GP_ERR, "%f %f %f %f %f %f %f\n", V, wr, W[0], W[Ncurve-1], r, fr, Rs);
+    return (TRUE);
+  }
+  if (r < Ro)
+    d = Ro*cl - sqrt(r*r - Rs*Rs);
+  else 
+    d = Ro*cl + sqrt(r*r - Rs*Rs);
+  
+  gprint (GP_ERR, "dist: %f, vel: %f\n", d, V);
+
+  return (TRUE);
+
+} 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/biassub
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/biassub	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/biassub	(revision 17422)
@@ -0,0 +1,9 @@
+
+  biassub buffer sx sy nx ny dir [-v N V]
+
+  Apply a bias correction.  The region defines a vector 
+  (median is taken perpendicular to the dir direction) which 
+  is subtracted from the image.
+
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/cgrid
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/cgrid	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/cgrid	(revision 17422)
@@ -0,0 +1,6 @@
+
+  cgrid
+
+  draws a grid in RA and DEC on window 0 approriate for the current
+  region.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/coords
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/coords	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/coords	(revision 17422)
@@ -0,0 +1,19 @@
+
+  coords <buffer> (filename)
+
+  "coords" loads astrometric parameters for the given buffer from the
+header of the named file.  The parameters are applied to the buffer,
+and will register in the Ki'i window if the buffer it tv'ed again.
+This is particularly convenient for intercomparing coordinate system
+or in the event the astrometry is stored in a different file (eg, a
+stripped off header).  Currently, the only format of coordinate
+information that is looked for are gene's linear transformation: RA_O,
+RA_X, RA_Y, DEC_O, DEC_X, DEC_Y such that the RA,DEC of an object is
+given by:
+
+RA  = RA_O  + x*RA_X  + y*RA_Y
+DEC = DEC_O + x*DEC_X + y*DEC_Y.
+
+
+  See also: rd, tv
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/flux
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/flux	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/flux	(revision 17422)
@@ -0,0 +1,7 @@
+
+   flux buffer (file)
+
+   calculate the flux enclosed by the contour given in the file.  The
+   file contains the coordinates of the polygon corners.
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/gauss
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/gauss	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/gauss	(revision 17422)
@@ -0,0 +1,15 @@
+
+   gauss Npix [Nspots] [-border N] [-sat cnts]
+
+   calculate statistics on stars, assuming a Gaussian profile.  The
+   user types a key on a star in the Kii window, and the resulting
+   information is printed on the screen.  This is really aperture
+   photometry.  Npix defines the aperture width (square aperture), the
+   optional -border defines the number of pixels in an annulus used to
+   find the background, -sat defines the value of a saturated pixel.
+   If the Nspots option is given, exactly Nspots stars are chosen.
+   Otherwise, the user may type on stars until the 'q' is typed.  The
+   results of the last star are stored the a set of Mana variables.
+
+   See also: star, cursor
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/profile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/profile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/profile	(revision 17422)
@@ -0,0 +1,6 @@
+
+   profile <buffer> <X vector> <Y vector> x y N
+
+   Find the radial profile of an image at the location x, y.  the
+   radius is placed in the X vector and the pixel value is placed in
+   the Y vector.   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/region
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/region	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/help/region	(revision 17422)
@@ -0,0 +1,18 @@
+
+  region RA DEC Radius [projection]
+
+  define the current sky region and optionally the projection.  
+  RA, DEC and Radius are all values in decimal degrees.  The possible 
+  values for "projection" are:
+
+  TAN - a tangent plane projection (default, and typical for optical images)
+  SIN - a sine plane projection (more appropriate for polar regions)
+  AIT - aitoff projection (good for the full sky)
+  GLS - ?? (also good for the full sky)
+
+  optional orientation flags:
+
+  -ew : display with East Left (default)
+  +ew : display with East Right 
+  -ns : display with North Down
+  +ns : display with North Up (default)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-fgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-fgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-fgauss.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+float fgaussTD (float, float, float *, int, float *);
+void  fgaussCL ();
+
+void fgauss_setup (char *name) {
+
+  if (strcmp(name, "fgauss")) return;
+
+  fitfunc = fgaussTD;
+  imfit_cleanup = fgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void fgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* real 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+float fgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = exp (-z);
+  q = par[5]*r;
+  f = q + par[6];
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss-psf.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "imfit.h"
+
+float pgauss_psfTD (float, float, float *, int, float *);
+void  pgauss_psfCL ();
+
+void  pgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "pgauss_psf")) return;
+
+  fitfunc = pgauss_psfTD;
+  imfit_cleanup = pgauss_psfCL;
+  Npar = 4;
+  Nfpar = 3;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+  sky = &par[3];
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 2.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 2.0);
+  fpar[2] = get_variable_default ("SXYg", 0);
+}
+
+void pgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* pseudo 2D gaussian -- x, y, (sx), (sy), (sxy), I, sky */
+float pgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  /* par -> fpar: (2,0), (3,1), (4,2) */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[2]*r + par[3];
+  q = par[2]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-pgauss.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "imfit.h"
+
+float pgaussTD (float, float, float *, int, float *);
+void  pgaussCL ();
+
+void  pgauss_setup (char *name) {
+
+  if (strcmp(name, "pgauss")) return;
+
+  fitfunc = pgaussTD;
+  imfit_cleanup = pgaussCL;
+  Npar = 7;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0.0);
+  par[1] = get_variable_default ("Yg", 0.0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0.0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  sky = &par[6];
+}
+
+void pgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* pseudo 2D gaussian -- x, y, sx, sy, sxy, I, sky */
+float pgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  r = 1.0 / (1 + z + 0.5*z*z*(1 + z/3)); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + z + 0.5*z*z);
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qfgauss.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float qfgaussTD (float, float, float *, int, float *);
+void  qfgaussCL ();
+
+void qfgauss_setup (char *name) {
+
+  if (strcmp(name, "qfgauss")) return;
+
+  fitfunc = qfgaussTD;
+  imfit_cleanup = qfgaussCL;
+  Npar = 7;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1]  = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qfgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky */
+float qfgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[1]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[1] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+  }
+  return (f);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss-psf.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "imfit.h"
+
+float qgauss_psfTD (float, float, float *, int, float *);
+void  qgauss_psfCL ();
+
+void qgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "qgauss_psf")) return;
+
+  fitfunc = qgauss_psfTD;
+  imfit_cleanup = qgauss_psfCL;
+  Npar = 4;
+  Nfpar = 5;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = get_variable_default ("Zpk", 10000);
+  par[3]  = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = get_variable_default ("Sr", 1.0);
+  fpar[4] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void qgauss_psfCL () {
+  set_variable ("Xg",  par[0]);
+  set_variable ("Yg",  par[1]);
+  set_variable ("Zpk", par[2]);
+  set_variable ("Sg",  par[3]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = fpar[0]*X;
+  py = fpar[1]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + fpar[2]*X*Y;
+
+  r = 1.0 / (1 + fpar[3]*z + pow(z,fpar[4]));
+  f = par[2]*r + par[3];
+  q = par[2]*SQ(r)*(fpar[3] + fpar[4]*pow(z,(fpar[4]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*fpar[0] + fpar[2]*Y);
+    dpar[1] = q*(2*py*fpar[1] + fpar[2]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qgauss.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "imfit.h"
+
+float qgaussTD (float, float, float *, int, float *);
+void  qgaussCL ();
+
+void qgauss_setup (char *name) {
+
+  if (strcmp(name, "qgauss")) return;
+
+  fitfunc = qgaussTD;
+  imfit_cleanup = qgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Sr", 1.0);
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+void qgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
+
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + par[7]*z + pow(z,fpar[0]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(par[7] + fpar[0]*pow(z,(fpar[0]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -par[5]*SQ(r)*z;
+  }
+  return (f);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-qrgauss.c	(revision 17422)
@@ -0,0 +1,110 @@
+# include "imfit.h"
+
+float qrgaussTD (float, float, float *, int, float *);
+void  qrgaussCL ();
+
+void qrgauss_setup (char *name) {
+
+  if (strcmp(name, "qrgauss")) return;
+
+  fitfunc = qrgaussTD;
+  imfit_cleanup = qrgaussCL;
+  Npar = 8;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0]  = get_variable_default ("Xg", 0);
+  par[1]  = get_variable_default ("Yg", 0);
+  par[2]  = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3]  = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4]  = get_variable_default ("SXYg", 0);
+  par[5]  = get_variable_default ("Zpk", 10000);
+  par[6]  = get_variable_default ("Sg", 0.0);
+  par[7]  = get_variable_default ("Npow", 2.25);
+
+  fpar[0] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+void qrgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Npow", par[7]);
+}
+
+float qrgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]));
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)));
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+
+
+# if (0)
+/* one component, two slopes: (1 + z^M + z^N)^(-1) -- x, y, sx, sy, sxy, I, sky, sr */
+float qrgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f;
+
+  /* if (par[7] < 1.0) par[7] = 1.0; */
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+
+  r = 1.0 / (1 + fpar[0]*z + pow(z,par[7]) + 0.05*z*z*z);
+  f = par[5]*r + par[6];
+  q = par[5]*SQ(r)*(fpar[0] + par[7]*pow(z,(par[7]-1)) + 0.15*z*z);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X*2;
+    dpar[3] = -2*q*py*Y*2;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -5*par[5]*SQ(r)*log(z)*pow(z,par[7]);
+  }
+  return (f);
+}
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-rgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-rgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-rgauss.c	(revision 17422)
@@ -0,0 +1,86 @@
+# include "imfit.h"
+
+float rgaussTD (float, float, float *, int, float *);
+void  rgaussCL ();
+
+void rgauss_setup (char *name) {
+
+  if (strcmp(name, "rgauss")) return;
+
+  fitfunc = rgaussTD;
+  cleanup = rgaussCL;
+  Npar = 10;
+  Nfpar = 1;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1 + 0.5*z_1^2 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float rgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + 0.5*SQ(z1)+ pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*(1 + z1);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int rgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf", 2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-serbulge.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-serbulge.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-serbulge.c	(revision 17422)
@@ -0,0 +1,102 @@
+# include "imfit.h"
+
+float serbulgeTD (float, float, float *, int, float *);
+void  serbulgeCL ();
+
+void serbulge_setup (char *name) {
+
+  if (strcmp(name, "serbulge")) return;
+
+  fitfunc = serbulgeTD;
+  cleanup = serbulgeCL;
+  Npar = 12;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[6] = get_variable_default ("Sg", 0.0);
+
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+  par[10] = get_variable_default ("Zpk", 10000) / 2.0;
+
+  par[11] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/*                                  0  1    2   3   4      5    6     7   8   9       10  11 */
+/* sersic galaxy model w/ bulge: -- x, y, (sx, sy, sxy)_1, I_1, sky, (sx, sy, sxy)_2, I_2, n */
+/* exp (-b (r/r_e)^(1/n)) + pgauss (r) */
+float serbulgeTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, px2, py1, py2;
+  float z1, z2, r1, r2, t, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  /* bulge component */
+  r1 = 1.0 / (1 + z1 + 0.5*z1*z1*(1 + z1/3)); /* ~ exp (-Z) */
+
+  /* disk component */
+  t = pow (z2, par[11]);
+  r2 = exp (-t);
+
+  f = par[5]*r1 + par[10]*r2 + par[6];
+
+  q1 = par[5]*r1*r1*(1 + z1 + 0.5*z1*z1);
+  q2 = par[10]*r2*par[11]*pow(z2, par[11]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X;
+    dpar[3] = -2*q1*py1*Y;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r1;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X*50;
+    dpar[8] = -2*q2*py2*Y*50;
+    dpar[9] = -q2*X*Y*50;
+    dpar[10] = +r2*50;
+    dpar[11] = -q2*log(z2)*t*50;
+  }
+  return (f);
+}
+
+int serbulgeCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zb",   par[5]);
+  set_variable ("Sg",   par[6]);
+
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+  set_variable ("Zd",   par[10]);
+  set_variable ("Sr",   par[11]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sersic.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sersic.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sersic.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float sersicTD (float, float, float *, int, float *);
+void  sersicCL ();
+
+void sersic_setup (char *name) {
+
+  if (strcmp(name, "sersic")) return;
+
+  fitfunc = sersicTD;
+  cleanup = sersicCL;
+  Npar = 8;
+  Nfpar = 0;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = get_variable_default ("Sr", 1.0);
+
+  sky = &par[6];
+}
+
+/* sersic galaxy model -- x, y, sx, sy, sxy, I, sky, n */
+/* exp (-b (r/r_e)^(1/n)) */
+float sersicTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, t, q, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  t = pow (z, par[7]);
+  r = exp (-t);
+  f = par[5]*r + par[6];
+  q = par[5]*r*par[7]*pow(z, par[7]-1);
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -q*log(z)*t;
+  }
+  return (f);
+}
+
+int sersicCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("Sr", par[7]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss-psf.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "imfit.h"
+
+float sgauss_psfTD (float, float, float *, int, float *);
+void  sgauss_psfCL ();
+
+void sgauss_psf_setup (char *name) {
+
+  if (strcmp(name, "sgauss_psf")) return;
+
+  fitfunc = sgauss_psfTD;
+  imfit_cleanup = sgauss_psfCL;
+  Npar = 4;
+  Nfpar = 7;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = get_variable_default ("Zpk", 10000);
+  par[3] = get_variable_default ("Sg", 0.0);
+
+  fpar[0] = 2.35 / get_variable_default ("SXg", 15.0);
+  fpar[1] = 2.35 / get_variable_default ("SYg", 15.0);
+  fpar[2] = get_variable_default ("SXYg", 0.0);
+  fpar[3] = 2.35 / get_variable_default ("SXf", 15.0);
+  fpar[4] = 2.35 / get_variable_default ("SYf", 15.0);
+  fpar[5] = get_variable_default ("SXYf", 0.0);
+  fpar[6] = get_variable_default ("Npow", 2.25);
+
+  sky = &par[3];
+}
+
+void sgauss_psfCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("Zpk",  par[2]);
+  set_variable ("Sg",   par[3]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float sgauss_psfTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = fpar[0]*X;
+  py1 = fpar[1]*Y;
+  px2 = fpar[3]*X;
+  py2 = fpar[4]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + fpar[2]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + fpar[5]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[6]));
+  f = par[2]*r + par[3];
+
+  q1 = par[2]*SQ(r);
+  q2 = par[2]*SQ(r)*fpar[6]*pow(z2,(fpar[6]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*fpar[0] + fpar[2]*Y) + q2*(2*px2*fpar[3] + fpar[5]*Y);
+    dpar[1] = q1*(2*py1*fpar[1] + fpar[2]*X) + q2*(2*py2*fpar[4] + fpar[5]*X);
+    dpar[2] = +r;
+    dpar[3] = +1;
+  }
+  return (f);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-sgauss.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "imfit.h"
+# define FFACTOR 200
+# define FSCALE 1.2
+
+float sgaussTD (float, float, float *, int, float *);
+void  sgaussCL ();
+
+void sgauss_setup (char *name) {
+
+  if (strcmp(name, "sgauss")) return;
+
+  fitfunc = sgaussTD;
+  imfit_cleanup = sgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  /* allocate free and fixed parameters */
+  ALLOCATE (par, float, MAX (Npar, 1));
+  bzero (par, Npar*sizeof(float));
+  ALLOCATE (fpar, float, MAX (Nfpar, 1));
+  bzero (fpar, Nfpar*sizeof(float));
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 / get_variable_default ("SYg", 2.0);
+  par[4] = get_variable_default ("SXYg", 0);
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+void sgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 / par[2]);
+  set_variable ("SYg",  2.35 / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", 2.35 / par[7]);
+  set_variable ("SYf", 2.35 / par[8]);
+  set_variable ("SXYf", par[9]);
+}
+
+/* two components: (1 + z_1 + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float sgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f, f1, f2;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + z1 + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r);
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+
+    /* these fudge factors impede the growth of par[2] beyond par[7] */
+    f1 = fabs(par[7]) / fabs(par[2]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[2] = -2*q1*px1*X*f2;
+
+    f1 = fabs(par[8]) / fabs(par[3]);
+    f2 = (f1 < FSCALE) ? 1 : FFACTOR*(f1 - FSCALE) + 1;
+    dpar[3] = -2*q1*py1*Y*f2;
+
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-test.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-test.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-test.c	(revision 17422)
@@ -0,0 +1,314 @@
+  if (ShapeVariation) {
+    /* find dChi/dSx and dChi/dSy given by increasing shape terms by 5% */
+    float tp1, tp2, chix, chiy;
+    chix = chiy = 0;
+    if (fitfunc == sgaussTD) {
+      tp1 = par[2];
+      tp2 = par[7];
+      par[2] = par[2]*1.05;
+      par[7] = par[7]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+      par[7] = tp2;
+
+      tp1 = par[3];
+      tp2 = par[8];
+      par[3] = par[3]*1.05;
+      par[8] = par[8]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+      par[8] = tp2;
+    }
+    if (fitfunc == pgaussTD) {
+      tp1 = par[2];
+      par[2] = par[2]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[2] = tp1;
+
+      tp1 = par[3];
+      par[3] = par[3]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[3] = tp1;
+    }
+    if (fitfunc == sgauss_psfTD) {
+      tp1 = par[0];
+      tp2 = par[3];
+      par[0] = par[0]*1.05;
+      par[3] = par[3]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+      par[3] = tp2;
+
+      tp1 = par[1];
+      tp2 = par[4];
+      par[1] = par[1]*1.05;
+      par[4] = par[4]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+      par[4] = tp2;
+    }
+    if (fitfunc == pgauss_psfTD) {
+      tp1 = par[0];
+      par[0] = par[0]*1.05;
+      chix = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[0] = tp1;
+
+      tp1 = par[1];
+      par[1] = par[1]*1.05;
+      chiy = mrq2dchi (x, y, z, dz, Npts, par, Npar, fitfunc) - chisq;
+      par[1] = tp1;
+    }
+    set_variable ("dChiX", chix/chisq);
+    set_variable ("dChiY", chiy/chisq);
+  }
+
+# if (0)
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fgalaxyTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = sqrt (0.5*p2*X + X*Y*par[4] + 0.5*p3*Y);                 /* R */
+  E = 1.0 / (1 + Z);   
+
+  q = par[5] * E;
+  R = q*E;
+  F = 0.5 / Z;
+  
+  f = q + par[6];
+
+  dpar[0] = F*R*(p2 + par[4]*Y);
+  dpar[1] = F*R*(p3 + par[4]*X);
+  dpar[2] = F*0.5*R*p2*p2;
+  dpar[3] = F*0.5*R*p3*p3;
+  dpar[4] = -R*X*Y*F;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* pars: x, y, sx, sy, sxy, I, sky */
+float fbarTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, Z, E, F, q, R, f, p2, p3;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  p2 = X / par[2];
+  p3 = Y / par[3];
+
+  Z = 0.5*p2*X + X*Y*par[4] + 0.5*p3*Y;                 /* R */
+  E = 1.0 / (1 + Z*Z*Z);   
+
+  q = par[5] * E;
+  F = 3*Z*Z;
+  R = q*E*F;
+  
+  f = q + par[6];
+
+  dpar[0] = R*(p2 + par[4]*Y);
+  dpar[1] = R*(p3 + par[4]*X);
+  dpar[2] = 0.5*R*p2*p2;
+  dpar[3] = 0.5*R*p3*p3;
+  dpar[4] = -R*X*Y;
+    
+  dpar[5] = E;
+  dpar[6] = 1;
+  return (f);
+}
+
+/* convert from x,y to major,minor */
+void fix_ellipsegauss_pars (float *par, int Npar) {
+
+  float p2, p4, angle, t1, t2, tmp, area;
+
+  /* par[0], par[1] = Xo, Yo - stay the same */
+
+  p2 = 1/par[2];
+  p4 = 1/par[3];
+
+  angle = 0.5 * atan2 (-2*par[4], p4 - p2); 
+
+  tmp = sqrt (SQ(p2 - p4) + 4*SQ(par[4]));
+  t1 = (p2 + p4 + tmp) / 2;
+  t2 = t1 - tmp;
+
+  par[2] = 2.35482*sqrt(1/t2);
+  par[3] = 2.35482*sqrt(1/t1);
+  par[4] = DEG_RAD * angle;
+
+  area = 2*M_PI/sqrt(t1*t2);
+
+  par[5] *= area;
+
+}
+# endif
+
+/***  options for later
+
+  Subtract = FALSE;
+  if ((N = get_argument (argc, argv, "-sub"))) {
+    remove_argument (N, &argc, argv);
+    Subtract  = TRUE;
+  }
+
+  DFact = 1;
+  if ((N = get_argument (argc, argv, "-D"))) {
+    remove_argument (N, &argc, argv);
+    DFact  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  fitfunc = fgaussTD;
+  if ((N = get_argument (argc, argv, "-gal"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fgalaxyTD; 
+  }
+  if ((N = get_argument (argc, argv, "-bar"))) {
+    remove_argument (N, &argc, argv);
+    fitfunc = fbarTD; 
+  }
+
+
+  f1 = 1;
+  if ((c = get_variable ("BETA1")) != (char *) NULL) f1 = atof (c);
+
+  f2 = 1;
+  if ((c = get_variable ("BETA2")) != (char *) NULL) f2 = atof (c);
+
+  if (Subtract) {
+    tmpsky = par[6];
+    par[6] = 0;
+    for (N = j = 0; j < ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++, N++) {
+	dx = i + sx;
+	dy = j + sy;
+	*V -= fitfunc (dx, dy, par, Npar, (float *) NULL);
+      }
+    }
+    par[6] = tmpsky;
+  }
+
+***/
+
+# if (0)
+
+/* these two tests were not very succcessful.  the first did not model the shape well because 
+   it could not match the change in roundness with radius.  the second did not work because the 
+   parameters were degenerate (amplitude and slope of second component) */
+
+/* test: fixed, non-integer higher-order term -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+float qgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = pow(z,1.75*par[8]);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  q = par[5]*r*r*(1 + 1.75*par[7]*par[8]*pow(z,1.75*par[8]-1));
+  /* note difference from gaussian: q = par[5]*r */
+  f = par[5]*r + par[6];
+
+  dpar[0] = q*(2*px*par[2] + par[4]*Y);
+  dpar[1] = q*(2*py*par[3] + par[4]*X);
+  dpar[2] = -2*q*px*X;
+  dpar[3] = -2*q*py*Y;
+  dpar[4] = -q*X*Y;
+  dpar[5] = +r;
+  dpar[6] = +1;
+  dpar[7] = -10*par[5]*r*r*k;
+  dpar[8] = -10*par[5]*r*r*par[7]*k*1.75*log(z);
+
+  return (f);
+}
+
+/* test: two component model: inner pseudo gaussian with outer z^1.75 x, y, sx, sy, sxy, I, sky */
+float rgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r1, r2, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[8]*X;
+  py2 = par[9]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[10]*X*Y;
+
+  r1 = 1.0 / (1 + z1 + 0.5*SQ(z1)*(1 + z1/3)); /* ~ exp (-Z) */
+  r2 = 1.0 / (1 + pow(z2,1.75));
+
+  f = par[5]*r1 + par[6] + par[7]*r2;
+
+  q1 = par[5]*SQ(r1)*(1 + z1 + 0.5*SQ(z1));
+  q2 = par[7]*SQ(r2)*(1.75*pow(z2,0.75));
+
+  dpar[	0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[8] + par[10]*Y);
+  dpar[	1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[9] + par[10]*X);
+  dpar[	2] = -2*q1*px1*X;
+  dpar[	3] = -2*q1*py1*Y;
+  dpar[	4] = -q1*X*Y;
+  dpar[	5] = +r1;
+  dpar[	6] = +1;
+  dpar[	7] = +r2*2;
+  dpar[	8] = -2*q2*px2*X*2;
+  dpar[	9] = -2*q2*py2*Y*2;
+  dpar[10] = -q2*X*Y;
+
+  return (f);
+}
+
+# endif
+
+  /* forcing values to have a rational range
+  ALLOCATE (parmin, float, Npar);
+  ALLOCATE (parmax, float, Npar);
+  bzero (parmin, Npar*sizeof(float));
+  bzero (parmax, Npar*sizeof(float));
+  parmin[0] = parmin[1] = 0;
+  parmax[0] = buf[0].matrix.Naxis[0];
+  parmax[1] = buf[0].matrix.Naxis[1];
+
+  parmin[2] = parmin[3] = 0.01;
+  parmax[2] = parmax[3] = 100.0;
+  parmin[4] = -1000;
+  parmax[4] = -1000;
+  
+  parmin[5] = 1;
+  parmax[5] = 1e5;
+
+  parmin[6] = 0.0;
+  parmax[6] = 1e5;
+
+  if (Npar == 9) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+  }
+  if (Npar == 10) {
+    parmin[7] = parmin[8] = 0.01;
+    parmax[7] = parmax[8] = 10.0;
+    parmin[9] = -1000;
+    parmax[9] = -1000;
+  }
+  */
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-tgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-tgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-tgauss.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "imfit.h"
+
+float tgaussTD (float, float, float *, int, float *);
+void  tgaussCL ();
+
+void tgauss_setup (char *name) {
+
+  if (strcmp(name, "tgauss")) return;
+
+  fitfunc = tgaussTD;
+  cleanup = tgaussCL;
+  Npar = 10;
+  Nfpar = 2;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 2.35 * sqrt(2.0) / get_variable_default ("SXf", 15.0);
+  par[8] = 2.35 * sqrt(2.0) / get_variable_default ("SYf", 15.0);
+  par[9] = get_variable_default ("SXYf", 0.0);
+
+  fpar[0] = get_variable_default ("Npow", 2.25);
+  fpar[1] = get_variable_default ("Npin", 1.00); // drop this?
+
+  sky = &par[6];
+}
+
+/* two components: (1 + z_1^M + z_2^N)^(-1) -- x, y, sx1, sy1, sxy1, I, sky, sx2, sy2, sxy2 */
+float tgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px1, py1, px2, py2;
+  float z1, z2, r, q1, q2, f;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px1 = par[2]*X;
+  py1 = par[3]*Y;
+  px2 = par[7]*X;
+  py2 = par[8]*Y;
+
+  z1 = 0.5*SQ(px1) + 0.5*SQ(py1) + par[4]*X*Y;
+  z2 = 0.5*SQ(px2) + 0.5*SQ(py2) + par[9]*X*Y;
+
+  r = 1.0 / (1 + pow(z1,fpar[1]) + pow(z2,fpar[0]));
+  f = par[5]*r + par[6];
+
+  q1 = par[5]*SQ(r)*fpar[1]*pow(z1,(fpar[1]-1));
+  q2 = par[5]*SQ(r)*fpar[0]*pow(z2,(fpar[0]-1));
+
+  if (dpar != NULL) {
+    dpar[0] = q1*(2*px1*par[2] + par[4]*Y) + q2*(2*px2*par[7] + par[9]*Y);
+    dpar[1] = q1*(2*py1*par[3] + par[4]*X) + q2*(2*py2*par[8] + par[9]*X);
+    dpar[2] = -2*q1*px1*X*2;
+    dpar[3] = -2*q1*py1*Y*2;
+    dpar[4] = -q1*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -2*q2*px2*X;
+    dpar[8] = -2*q2*py2*Y;
+    dpar[9] = -q2*X*Y;
+  }
+  return (f);
+}
+
+int tgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf",  2.35 * sqrt(2.0) / par[7]);
+  set_variable ("SYf",  2.35 * sqrt(2.0) / par[8]);
+  set_variable ("SXYf", par[9]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-vgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-vgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit-vgauss.c	(revision 17422)
@@ -0,0 +1,70 @@
+# include "imfit.h"
+
+float vgaussTD (float, float, float *, int, float *);
+void  vgaussCL ();
+
+void  vgauss_setup (char *name) {
+
+  if (strcmp(name, "vgauss")) return;
+
+  fitfunc = vgaussTD;
+  cleanup = vgaussCL;
+  Npar = 9;
+  Nfpar = 0;
+
+  par[0] = get_variable_default ("Xg", 0);
+  par[1] = get_variable_default ("Yg", 0);
+  par[2] = 2.35 * sqrt(2.0) / get_variable_default ("SXg", 2.0);
+  par[3] = 2.35 * sqrt(2.0) / get_variable_default ("SYg", 2.0);
+  par[4] = 0.0;
+  par[5] = get_variable_default ("Zpk", 10000);
+  par[6] = get_variable_default ("Sg", 0.0);
+  par[7] = 1;
+  par[8] = 1;
+  sky = &par[6];
+}
+
+/* pseudo 2D gaussian with floating 2nd and 3rd order terms -- x, y, sx, sy, sxy, I, sky, f1, f2 */
+float vgaussTD (float x, float y, float *par, int Npar, float *dpar) {
+
+  float X, Y, px, py;
+  float z, r, q, f, k;
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  px = par[2]*X;
+  py = par[3]*Y;
+
+  z = 0.5*SQ(px) + 0.5*SQ(py) + par[4]*X*Y;
+  k = 0.5*z*z*(1 + par[8]*z/3);
+  r = 1.0 / (1 + z + par[7]*k); /* ~ exp (-Z) */
+  f = par[5]*r + par[6];
+  q = par[5]*r*r*(1 + par[7]*z*(1 + par[8]*z/2));
+  /* note difference from gaussian: q = par[5]*r */
+
+  if (dpar != NULL) {
+    dpar[0] = q*(2*px*par[2] + par[4]*Y);
+    dpar[1] = q*(2*py*par[3] + par[4]*X);
+    dpar[2] = -2*q*px*X;
+    dpar[3] = -2*q*py*Y;
+    dpar[4] = -q*X*Y;
+    dpar[5] = +r;
+    dpar[6] = +1;
+    dpar[7] = -100*par[5]*r*r*k;
+    dpar[8] = -100*par[5]*r*r*par[7]*(z*z*z)/6;
+  }
+  return (f);
+}
+
+int vgaussCL () {
+  set_variable ("Xg",   par[0]);
+  set_variable ("Yg",   par[1]);
+  set_variable ("SXg",  2.35 * sqrt(2.0) / par[2]);
+  set_variable ("SYg",  2.35 * sqrt(2.0) / par[3]);
+  set_variable ("SXYg", par[4]);
+  set_variable ("Zpk",  par[5]);
+  set_variable ("Sg",   par[6]);
+  set_variable ("SXf", par[7]);
+  set_variable ("SYf", par[8]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imfit.c	(revision 17422)
@@ -0,0 +1,185 @@
+# include "imfit.h"
+
+int imfit (int argc, char **argv) {
+
+  int i, j, N, Npts, Save, VERBOSE, ShapeVariation;
+  int sx, sy, nx, ny, Nx, Ny;
+  float chisq, ochisq, dchisq, Gain, RDnoise, SatThreshold;
+  float *x, *y, *z, *dz, *V;
+  Buffer *buf;
+
+  Save = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    Save = TRUE;
+  }
+
+  ShapeVariation = FALSE;
+  if ((N = get_argument (argc, argv, "-shapes"))) {
+    remove_argument (N, &argc, argv);
+    ShapeVariation = TRUE;
+  }
+
+  SatThreshold = 0xffff;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    SatThreshold = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* Gain in e/DN */
+  Gain = 1.0;
+  if ((N = get_argument (argc, argv, "-gain"))) {
+    remove_argument (N, &argc, argv);
+    Gain = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* RD noise in DN */
+  RDnoise = 0.0;
+  if ((N = get_argument (argc, argv, "-rdnoise"))) {
+    remove_argument (N, &argc, argv);
+    RDnoise = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx + 0.5*nx < 0) goto range;
+  if (sy + 0.5*ny < 0) goto range;
+  if (sx + 0.5*nx >= Nx) goto range;
+  if (sy + 0.5*ny >= Ny) goto range;
+
+  /* convert array z[x,y] to x[i], y[i], z[i] */
+  N = 0;
+  Npts = nx*ny;
+  ALLOCATE (x,  float, 2*Npts);
+  ALLOCATE (y,  float, 2*Npts);
+  ALLOCATE (z,  float, 2*Npts);
+  ALLOCATE (dz, float, 2*Npts);
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      if (*V > SatThreshold) goto next;
+      dz[N] = (SQ(RDnoise) + *V/Gain);
+      if (dz[N] <= 0) goto next;
+      dz[N] = 1.0 / dz[N];
+      x[N] = i + sx;
+      y[N] = j + sy;
+      z[N] = *V;
+      N++;
+    next:
+      V++;
+    }
+  }
+  Npts = N;
+
+  /* run fit routine */
+  ochisq = mrq2dinit (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+  dchisq = ochisq;
+  chisq  = ochisq;
+  for (i = 0; (i < 25) && ((dchisq <= 0.0) || (dchisq > 0.01*(Npts - Npar))); i++) {
+    chisq = mrq2dmin (x, y, z, dz, Npts, par, Npar, fitfunc, VERBOSE);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+  }  
+  set_int_variable ("Niter",  i);
+
+  /** create output image (keep in sky) **/
+  if (Save) {
+    Buffer *out;
+    float *Vi, *Vo, vr, vf;
+
+    if ((out = SelectBuffer ("out",   ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    free (out[0].header.buffer);
+    free (out[0].matrix.buffer);
+
+    strcpy (out[0].file, "(empty)");
+    CreateBuffer (out, 2*nx, 2*ny, -32, 0.0, 1.0);
+
+    /* four panels: 1) raw image. 2) fit  3) raw - fit   4) ?? */
+    Vi = (float *)buf[0].matrix.buffer;
+    Vo = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny; j++) {
+      for (i = 0; i < nx; i++) {
+	vf = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+	vr = Vi[(i+sx)+(j+sy)*Nx];
+	Vo[(i   )+(j   )*2*nx] = vr;
+	Vo[(i+nx)+(j   )*2*nx] = vf;
+	Vo[(i   )+(j+ny)*2*nx] = vr - vf + *sky;
+	Vo[(i+nx)+(j+ny)*2*nx] = fabs(vr-vf) + *sky;
+      }
+    }
+  }
+
+  /* save parameters to opihi variables */
+  imfit_cleanup ();
+
+  set_variable ("ChiSq", chisq/(Npts - Npar));
+
+  if (VERBOSE) {
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%g ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  free (x);
+  free (y);
+  free (z);
+  free (dz);
+  free (par);
+  free (fpar);
+
+  mrq2dfree (Npar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imsub.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imsub.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/imsub.c	(revision 17422)
@@ -0,0 +1,79 @@
+# include "imfit.h"
+
+int imsub (int argc, char **argv) {
+
+  int i, j, N, VERBOSE;
+  int sx, sy, nx, ny, Nx, Ny;
+  float value;
+  float *V;
+  Buffer *buf;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  /* set fitting function */
+  fgauss_setup ("fgauss");
+  if ((N = get_argument (argc, argv, "-func"))) {
+    fitfunc = NULL;
+    remove_argument (N, &argc, argv);
+    fgauss_setup (argv[N]);
+    pgauss_setup (argv[N]);
+    pgauss_psf_setup (argv[N]);
+    sgauss_setup (argv[N]);
+    sgauss_psf_setup (argv[N]);
+    qgauss_setup (argv[N]);
+    qgauss_psf_setup (argv[N]);
+    qfgauss_setup (argv[N]);
+    qrgauss_setup (argv[N]);
+    if (fitfunc == NULL) {
+      gprint (GP_ERR, "unknown function %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  /* non-optional arguments */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  /* check if region is valid */
+  if (sx >= Nx) goto range;
+  if (sy >= Ny) goto range;
+  if (sx + nx < 0) goto range;
+  if (sy + ny < 0) goto range;
+
+  /* subtract model fit, but not local sky */
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= Ny) continue;
+    V = (float *)(buf[0].matrix.buffer) + (j+sy)*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= Nx) continue;
+      value = fitfunc ((float)(i+sx), (float)(j+sy), par, Npar, NULL);
+      *V -= value;
+    }
+  }
+
+  free (par);
+  free (fpar);
+  return (TRUE);
+
+range:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/init.c	(revision 17422)
@@ -0,0 +1,90 @@
+# include "astro.h"
+
+int altaz                   PROTO((int, char **));
+int biassub                 PROTO((int, char **));
+int cgrid                   PROTO((int, char **));
+int coords                  PROTO((int, char **));
+int cplot                   PROTO((int, char **));
+int csystem                 PROTO((int, char **));
+int ctimes                  PROTO((int, char **));
+int cval                    PROTO((int, char **));
+int czplot                  PROTO((int, char **));
+int drizzle                 PROTO((int, char **));
+int flux                    PROTO((int, char **));
+int fixwrap                 PROTO((int, char **));
+int gauss                   PROTO((int, char **));
+int gaussfit                PROTO((int, char **));
+int getvel                  PROTO((int, char **));
+int getlst                  PROTO((int, char **));
+int imfit                   PROTO((int, char **));
+int imsub                   PROTO((int, char **));
+int medianmap               PROTO((int, char **));
+int mkgauss                 PROTO((int, char **));
+int multifit                PROTO((int, char **));
+int objload                 PROTO((int, char **));
+int outline                 PROTO((int, char **));
+int polar                   PROTO((int, char **));
+int precess                 PROTO((int, char **));
+int profile                 PROTO((int, char **));
+int region                  PROTO((int, char **));
+int rotcurve                PROTO((int, char **));
+int scale                   PROTO((int, char **));
+int sexigesimal             PROTO((int, char **));
+int spec                    PROTO((int, char **));
+int star                    PROTO((int, char **));
+int times                   PROTO((int, char **));
+int transform               PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "altaz",       altaz,        "convert alt/az to/from ra/dec"},
+  {1, "biassub",     biassub,      "subtract medianed overscan row or column"},
+  {1, "cgrid",       cgrid,        "plot sky coordinate grid"},
+  {1, "coords",      coords,       "load coordinates for buffer from file"},
+  {1, "cplot",       cplot,        "plot vectors in sky coordinates"},
+  {1, "csystem",     csystem,      "convert between coordinate systems"},
+  {1, "ctimes",      ctimes,       "convert between time formats"},
+  {1, "cval",        cval,         "cosmic ray flux?"},
+  {1, "czplot",      czplot,       "plot scaled vectors in sky coordinates"},
+  {1, "drizzle",     drizzle,      "transform image to image"},
+  {1, "flux",        flux,         "flux in a convex contour"},
+  {1, "fixwrap",     fixwrap,      "fix megacam over-wrapped pixels"},
+  {1, "gauss",       gauss,        "get statistics on a star, assuming gaussian profile"},
+  {1, "getvel",      getvel,       "rotcurve to velocities"},
+  {1, "getlst",      getlst,       "return LST given time and longitude"},
+  {1, "imfit",       imfit,        "fit function"},
+  {1, "imsub",       imsub,        "subtract function"},
+  {1, "medianmap",   medianmap,    "small median image"},
+  {1, "mkgauss",     mkgauss,      "generate a 2-D gaussian centered in image"},
+  {1, "multifit",    multifit,     "fit multi-order spectrum"},
+  {1, "objload",     objload,      "plot obj data on Ximage "},
+  {1, "outline",     outline,      "fit outline region"},
+  {1, "polar",       polar,        "convert polar image to cartesian"},
+  {1, "precess",     precess,      "precess coordinates"},
+  {1, "profile",     profile,      "radial profile at X, Y"},
+  {1, "region",      region,       "define sky region for plot"},
+  {1, "rotcurve",    rotcurve,     "convert CO images to polar coords"},
+  {1, "scale",       scale,        "get / set real bzero / bscale values"},
+  {1, "sexigesimal", sexigesimal,  "convert to/from sexigesimal/decimal"},
+  {1, "spec",        spec,         "extract a spectrum"},
+  {1, "star",        star,         "star stats at rough coords"},
+  {1, "transform",   transform,    "geometric transformation of image"},
+}; 
+
+/* not currently implemented 
+  {"gaussfit",    gaussfit,     "fit a gaussian to pixels in a region"},
+  {"testfit",     testfit, ""},
+  {"times", , ""},
+*/
+
+void InitAstro () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeAstro () {
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/medianmap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/medianmap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/medianmap.c	(revision 17422)
@@ -0,0 +1,114 @@
+# include "astro.h"
+
+int medianmap (int argc, char **argv) {
+  
+  float *temp, *tp;
+  int i, j, k, I0, I1, J0, J1, I, J, n, N;
+  int nx, ny, Nx, Ny, NX, NY, Ignore;
+  float Mv, Nv, Mv2, value, min, max, IgnoreValue;
+  float *In, *Out, *ip;
+  float fx, fy;
+  Buffer *in, *out;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = 0.45;
+  max = 0.55;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: medianmap (in) (out) Nx Ny [-range min max]\n");
+    gprint (GP_ERR, "       Nx, Ny specify dimensions of output image\n");
+    gprint (GP_ERR, "       min, max specify fractional range for sorted average\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  NX = in[0].header.Naxis[0];
+  NY = in[0].header.Naxis[1];
+
+  /* duplicate the (in) buffer to the (out), with different size */
+  /* this should probably be a function in misc */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+  out[0].header.Naxis[0] = Nx;
+  out[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  In = (float *) in[0].matrix.buffer;
+  Out = (float *) out[0].matrix.buffer;
+
+  fx = (float) Nx / NX;
+  fy = (float) Ny / NY;
+
+  nx = 1 + 1/fx;
+  ny = 1 + 1/fy;
+
+  ALLOCATE (temp, float, 2*nx*ny);
+
+  Nv = Mv = Mv2 = 0.0;
+
+  for (j = 0; j < Ny; j++) {
+    J0 = j / fy;
+    J1 = (j + 1) / fy;
+    for (i = 0; i < Nx; i++) {
+      
+      I0 = i / fx;
+      I1 = (i + 1) / fx;
+
+      n = 0;
+      tp = temp;
+      for (J = J0; J < J1; J++) {
+	ip = &In[J*NX + I0];
+	for (I = I0; I < I1; I++, tp++, ip++) {
+	  if (Ignore && (fabs (*ip - IgnoreValue) < 0.01)) continue;
+	  if (isnan (*ip)) continue;
+	  if (isinf (*ip)) continue;
+	  *tp = *ip;
+	  n++;
+	}
+      }
+
+      fsort (temp, n);
+
+      value = 0;
+      N = 0;
+      for (k = min*n; k < max*n; k++) {
+	value += temp[k];
+	N ++;
+      }
+      if (N == 0)
+	Out[j*Nx + i] = 0;
+      else 
+	Out[j*Nx + i] = value / N;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/mkgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/mkgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/mkgauss.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "astro.h"
+
+int mkgauss (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, N;
+  float *in;
+  double Sig_x, Sig_y, Theta;
+  double root1, root2, R, A1, A2, A3;
+  double Sx, Sy, Sxy;
+  double x, y, r, f, Xo, Yo;
+  Buffer *buf;
+
+  Xo = Yo = 0;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    Xo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc < 3) || (argc > 5)) {
+    gprint (GP_ERR, "USAGE: mkgauss (buffer) (sigma) [[sy/sx] angle]\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  
+  /* gaussian parameters */
+  Sig_x = atof (argv[2]);
+  Sig_y = Sig_x;
+  Theta = 0.0;
+  if (argc > 3) {
+    Sig_y = Sig_x*atof (argv[3]);
+    if (argc == 5) {
+      Theta = atof (argv[4]);
+    }
+  }
+
+  /* given Sig_x, Sig_y, Theta, find Sx, Sy, Sxy */
+  root1 = SQ(1.0 / Sig_y);
+  root2 = SQ(1.0 / Sig_x);
+
+  R = 0.5 * (root1 - root2);
+  A1 = 0.25*(root1 + root2) - 0.5*R*cos(2*RAD_DEG*Theta);
+  A2 = 0.25*(root1 + root2) + 0.5*R*cos(2*RAD_DEG*Theta);
+  A3 = -R*sin(2*RAD_DEG*Theta);
+
+  Sx = 0.5/A1;
+  Sy = 0.5/A2;
+  Sxy = A3;
+
+  /* f = exp (-r), r = (x^2 / 2Sx) + (y^2 / 2Sy) + Sxy*x*y */
+
+  in = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, in++) {
+
+      x = i - Xo;
+      y = j - Yo;
+      r = 0.5*x*x/Sx + 0.5*y*y/Sy + x*y*Sxy;
+      f = exp (-r);
+      *in += f;
+    }
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/multifit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/multifit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/multifit.c	(revision 17422)
@@ -0,0 +1,179 @@
+# include "astro.h"
+
+int multifit (int argc, char **argv) {
+  
+  char *p, name[64];
+  double **a, **b, v;
+  int i, j, I, J, n, valid;
+  Vector **Nc, **Nmb, **NMb, **Nwb, **Nmh, **Nml, **Nwo;
+  int *nterm;
+  int Ndim, Norder, Nx, Ny;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: multifit (Norder)\n");
+    return (FALSE);
+  }
+
+  Ndim = 0;
+  Norder = atoi (argv[1]);
+  ALLOCATE (nterm, int, Norder);
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "nterm:%d", i);
+    p = get_variable (name);
+    nterm[i] = atoi (p); 
+    Ndim += nterm[i];
+    free (p);
+  }
+
+  ALLOCATE (a, double *, Ndim);
+  ALLOCATE (b, double *, Ndim);
+  for (i = 0; i < Ndim; i++) {
+    ALLOCATE (a[i], double, Ndim);
+    ALLOCATE (b[i], double, 1);
+    bzero (a[i], Ndim*sizeof(double));
+    bzero (b[i], sizeof(double));
+  }
+ 
+  ALLOCATE (Nc,  Vector *, Norder);
+  ALLOCATE (NMb, Vector *, Norder);
+  ALLOCATE (Nmb, Vector *, Norder);
+  ALLOCATE (Nwb, Vector *, Norder);
+  ALLOCATE (Nmh, Vector *, Norder - 1);
+  ALLOCATE (Nml, Vector *, Norder - 1);
+  ALLOCATE (Nwo, Vector *, Norder - 1);
+  
+  for (i = 0; i < Norder; i++) {
+    sprintf (name, "c%d", i);
+    if ((Nc[i]  = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "Mb%d", i);
+    if ((NMb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "mb%d", i);
+    if ((Nmb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "wb%d", i);
+    if ((Nwb[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+  }
+  for (i = 0; i < Norder - 1; i++) {
+    sprintf (name, "ml%d", i);
+    if ((Nml[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "mh%d", i);
+    if ((Nmh[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+    sprintf (name, "wo%d", i);
+    if ((Nwo[i] = SelectVector (name, OLDVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nx = 0;
+      valid = FALSE;
+      for (I = 0; I < Norder; I++) {
+	if (I == i - 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	      v += -pow (Nwo[i-1][0].elements[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i + 1) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	      v += -pow (Nwo[i][0].elements[n], (double)(j+J));
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (I == i) { 
+	  for (J = 0; J < nterm[I]; J++) {
+	    v = 0;
+	    for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	      v += pow (Nwb[i][0].elements[n], (double)(j+J));
+	    }
+	    if (i > 0) {
+	      for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+		v += pow (Nwo[i-1][0].elements[n], (double)(j+J));
+	      }
+	    }
+	    if (i < Norder - 1) {
+	      for (n = 0; n < Nwo[i][0].Nelements; n++) {
+		v += pow (Nwo[i][0].elements[n], (double)(j+J));
+	      }
+	    }
+	    a[Ny][Nx] = v;
+	    Nx ++;
+	  }
+	  valid = TRUE;
+	}
+	if (!valid) {
+	  Nx += nterm[I];
+	}
+      }
+    }
+  }
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      v = 0;
+      for (n = 0; n < Nwb[i][0].Nelements; n++) {
+	v += NMb[i][0].elements[n]*pow (Nwb[i][0].elements[n], (double)j);
+	v -= Nmb[i][0].elements[n]*pow (Nwb[i][0].elements[n], (double)j);
+      }
+      if (i > 0) {
+	for (n = 0; n < Nwo[i-1][0].Nelements; n++) {
+	  v += Nmh[i-1][0].elements[n] * pow (Nwo[i-1][0].elements[n], (double)j);
+	  v -= Nml[i-1][0].elements[n] * pow (Nwo[i-1][0].elements[n], (double)j);
+	}
+      }
+      if (i < Norder - 1) {
+	for (n = 0; n < Nwo[i][0].Nelements; n++) {
+	  v += Nml[i][0].elements[n] * pow (Nwo[i][0].elements[n], (double)j);
+	  v -= Nmh[i][0].elements[n] * pow (Nwo[i][0].elements[n], (double)j);
+	}
+      }
+      b[Ny][0] = v;
+    }
+  }
+  dgaussjordan (a, b, Ndim, 1);
+
+  Ny = 0;
+  for (i = 0; i < Norder; i++) {
+    Nc[i][0].Nelements = nterm[i];
+    REALLOCATE (Nc[i][0].elements, float, nterm[i]);
+    for (j = 0; j < nterm[i]; j++, Ny++) {
+      Nc[i][0].elements[j] = b[Ny][0];
+    }
+  }
+
+  for (i = 0; i < Ndim; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+  free (nterm);
+  free (Nc);
+  free (NMb);
+  free (Nmb);
+  free (Nwb);
+  free (Nmh);
+  free (Nml);
+  free (Nwo);
+  
+  return (TRUE);
+  
+ escape: 
+  gprint (GP_ERR, "syntax error\n");
+  return (FALSE);
+  
+}
+
+
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/objload.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/objload.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/objload.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "dvoshell.h"
+# define CHAR_LINE 104
+# define NBLOCK 100
+
+int objload (int argc, char **argv) {
+  
+  int i, N, Objtype, type, Nline, status;
+  FILE *f;
+  char *buffer, *line, *name;
+  int kapa, Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: objload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't find object file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* read average values from first line */
+  ALLOCATE (line, char, 129);
+  scan_line (f, line);
+
+  ALLOCATE (buffer, char, CHAR_LINE*NBLOCK);
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* read in data from obj file */
+  while ((Nline = fread (buffer, CHAR_LINE, NBLOCK, f)) > 0) {
+    for (i = 0; i < Nline; i++) {
+      /* we are now using all entries on the *.obj line */
+      status = sscanf (&buffer[i*CHAR_LINE], "%d %f %f",  &type, &overlay[Noverlay].x, &overlay[Noverlay].y);
+      if (Objtype && (Objtype != type)) continue;
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+  free (buffer);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  free (overlay);
+  free (buffer);
+  free (line);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline.c	(revision 17422)
@@ -0,0 +1,289 @@
+# include "astro.h"
+
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *, int, int, float *);
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, Npar, BigChange, ABigChange;
+  float Io, *in, ochisq, dchi, chisq, chisq_p, chisq_m, dp, tmp_par;
+  float curve, frac;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io (buffer) Npar\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[7], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  Npar = atof (argv[8]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+  gprint (GP_ERR, "chisq (1): %f\n", chisq);
+  
+  for (j = 0; j < 15; j++) {
+
+    /*
+    if (!(j % 3)) {
+      chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+      for (k = 0; (k < 3) && (fabs (frac) > 0.3); k++) {
+	tmp1 = par[2];
+	tmp2 = par[3];
+	par[2] *= 1 + 0.1*frac;
+	par[3] *= 1 + 0.1*frac;
+	nchisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	if (nchisq > chisq) {
+	  par[2] = tmp1;
+	  par[3] = tmp2;
+	  k = 3;
+	} else {
+	  chisq = nchisq;
+	}
+	gprint (GP_ERR, "frac: %f  %f %f   %f\n", frac, par[2], par[3], chisq);
+      }
+    }
+    */
+    
+    ABigChange = FALSE;
+    ochisq = chisq;
+    for (i = 4; i >= 0; i--) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in, buf[0].matrix.Naxis[0], buf[0].matrix.Naxis[1], &frac);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	/*
+	gprint (GP_ERR, "try: %d  %f   ", i, chisq);
+	for (k = 0; k < 5; k++) {
+	  gprint (GP_ERR, "%f ", par[k]);
+	}
+	gprint (GP_ERR, "\n");
+	*/
+	ochisq = chisq;
+	ABigChange |= BigChange;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    if (ABigChange) {
+      for (i = 0; i < 5; i++) {
+	dpar[i] = Dpar[i];
+      }
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+
+    /* code to draw dots on Ximage */
+    {
+      int kapa;
+      float xp, yp, x, y;
+      float dx, dy, theta, t, dt;
+      int Noverlay, NOVERLAY;
+      KiiOverlay *overlay;
+      
+      if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+      
+      Noverlay = 0;
+      NOVERLAY = 1000;
+      ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+      dx = par[2];
+      dy = par[3];
+      dt = 1 / MAX (dx, dy);
+      theta = par[4];
+      
+      for (t = 0; t < 6.3; t += dt) {
+	xp = dx * cos (t);
+	yp = dy * sin (t);
+	
+	x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+	y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+	
+	overlay[Noverlay].type = KII_OVERLAY_BOX;
+	overlay[Noverlay].x = x;
+	overlay[Noverlay].y = y;
+	overlay[Noverlay].dx = 1.0;
+	overlay[Noverlay].dy = 1.0;
+
+	Noverlay ++;
+	CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+      }
+      KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+      free (overlay);
+    }
+
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+   ellipse is:  
+
+   ((X-x)/dx)^2 + ((Y-y)/dy)^2 + (X-x)(Y-y)dxy = 1
+  
+   (yp/dy)^2 + xp yp dxy + (xp/dx)^2 - 1 = 0
+
+   yp^2 + yp xp dxy dy^2 + xp^2 (dy/dx)^2 - dy^2 = 0
+
+*/
+
+float outline_chi (float Io, float *in, int Nx, int Ny, float *frac) {
+
+  int npts, xo, yo, x, y;
+  float xp, yp, dx, dy, theta;
+  float t, dt, dv, Dv;
+  float chisq, v, Frac;
+
+  /* 
+  if (!SelectVector (&Nvec, "diffs", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "angle", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "xdif", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "ydif", ANYVECTOR)) return (FALSE);
+  */
+
+  dx = par[2];
+  dy = par[3];
+  theta = par[4];
+  dt = 1 / MAX (dx, dy);
+
+  Frac = 0;
+  chisq = 0;
+  npts = 0;
+  xo = -1; yo = -1;  /* an impossible coordinate */
+
+  /*
+  Npts = 1000;
+  REALLOCATE (vectors[Nvec].elements, float, Npts);
+  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+  */
+
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    if ((x == xo) && (y == yo)) continue;
+    xo = x; yo = y;
+
+    if ((x >= 0) && (x < Nx) && (y >= 0) && (y < Ny)) {
+      v = in[y*Nx + x];
+      if (v > 0) {
+	Dv = v - Io;
+	dv = v + 0.2 * fabs (Dv);
+	chisq += Dv * Dv / dv;
+	if (Dv > sqrt(dv)) Frac += 1.0;
+	if (Dv < sqrt(dv)) Frac -= 1.0;
+	/*
+	vectors[Nvec].elements[npts] = Dv;
+	vectors[Nvec2].elements[npts] = t*DEG_RAD;
+	vectors[Nvecx].elements[npts] = x;
+	vectors[Nvecy].elements[npts] = y;
+	*/
+	npts ++;
+	/* 
+	if (npts == Npts - 1) {
+	  Npts += 1000;
+	  REALLOCATE (vectors[Nvec].elements, float, Npts);
+	  REALLOCATE (vectors[Nvec2].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecx].elements, float, Npts);
+	  REALLOCATE (vectors[Nvecy].elements, float, Npts);
+	}
+	*/
+      }
+    }
+  }
+  /* 
+  vectors[Nvec].Nelements = npts;
+  vectors[Nvec2].Nelements = npts;
+  vectors[Nvecx].Nelements = npts;
+  vectors[Nvecy].Nelements = npts;
+  */
+
+  chisq = chisq / npts;
+  *frac = Frac / npts;
+  if (npts == 0) {
+    chisq = 1e8;
+    *frac = -1.0;
+  }
+
+  return (chisq);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline2.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline2.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/outline2.c	(revision 17422)
@@ -0,0 +1,341 @@
+# include "astro.h"
+
+int Npts;
+float *xs, *ys, *zs;
+float par[5];
+float dpar[5];
+float Dpar[5];
+float outline_chi (float, float *);
+int plot_outline ();
+
+int outline (int argc, char **argv) {
+  
+  int i, j, k, Nx, Ny, NPTS, BigChange;
+  float dIo, Io, ochisq, dchi, chisq, chisq_p, chisq_m, dp;
+  float tmp_par, curve, value;
+  float *in;
+  Buffer *buf;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: outline x y dx dy dxy Io dIo (buffer)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[8], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  par[0] = atof(argv[1]);
+  par[1] = atof(argv[2]);
+  par[2] = atof(argv[3]);
+  par[3] = atof(argv[4]);
+  par[4] = atof(argv[5]);
+  Io = atof(argv[6]);
+  dIo = atof(argv[7]);
+
+  dpar[0] = 10;
+  dpar[1] = 10;
+  dpar[2] = 10;
+  dpar[3] = 10;
+  dpar[4] = 10;
+
+  Dpar[0] = 10;
+  Dpar[1] = 10;
+  Dpar[2] = 10;
+  Dpar[3] = 10;
+  Dpar[4] = 10;
+
+  in = (float *) buf[0].matrix.buffer;
+
+  /* find all pixels within range Io-dIo : Io+dIo, in region about center guess */
+
+  Nx = buf[0].matrix.Naxis[0];  
+  Ny = buf[0].matrix.Naxis[1];
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (xs, float, NPTS);
+  ALLOCATE (ys, float, NPTS);
+  ALLOCATE (zs, float, NPTS);
+  for (j = par[1]-2*par[3]; j < par[1]+2*par[3]; j++) {
+    if (j < 0) continue;
+    if (j >= buf[0].matrix.Naxis[1]) continue;
+    for (i = par[0]-2*par[2]; i < par[0]+2*par[2]; i++) {
+      if (i < 0) continue;
+      if (i >= buf[0].matrix.Naxis[0]) continue;
+      value = in[i + Nx*j];
+      if (fabs (value - Io) < dIo) {
+	xs[Npts] = i;
+	ys[Npts] = j;
+	zs[Npts] = value;
+	Npts ++;
+	if (Npts == NPTS) {
+	  NPTS += 1000;
+	  REALLOCATE (xs, float, NPTS);
+	  REALLOCATE (ys, float, NPTS);
+	  REALLOCATE (zs, float, NPTS);
+	}
+      }
+    }
+  }
+
+  if (Npts == 0) {
+    gprint (GP_ERR, "no valid points in box, try again\n");
+    free (xs);
+    free (ys);
+    free (zs);
+    return (FALSE);
+  }
+
+  plot_outline ();
+  chisq = outline_chi (Io, in);
+  gprint (GP_ERR, "starting chisq: %f for %d pts\n", chisq, Npts);
+
+# if (1)
+  for (j = 0; j < 15; j++) {
+    
+    ochisq = chisq;
+    for (i = 0; i < 5; i++) {
+      /* find +chisq, -chisq for this par & adjust par as needed */
+
+      for (k = 0, BigChange = TRUE; (k < 3) && BigChange; k++) {
+	tmp_par = par[i];
+	par[i] = tmp_par + dpar[i];
+	chisq_p = outline_chi (Io, in);
+	par[i] = tmp_par - dpar[i];
+	chisq_m = outline_chi (Io, in);
+	
+	/* have we braketted a minimum? (curve < 0) */
+	curve = (chisq_p - chisq) * (chisq - chisq_m);
+	if (curve > 0) {
+	  dp = 2*dpar[i];
+	} else {
+	  dp = 0.5 * dpar[i] * (chisq_m - chisq_p) / (chisq_m + chisq_p - 2*chisq);
+	}      
+	if (chisq_m + chisq_p - 2*chisq == 0) dp = 0;
+	/* don't let extrapolation go too far */
+	if (fabs (dp) > 2*fabs(dpar[i])) { dp = SIGN(dp) * fabs (2*dpar[i]); }
+	
+	par[i] = tmp_par + dp;
+	chisq = outline_chi (Io, in);
+	
+	BigChange = FALSE;
+	if (chisq <= 1.001*ochisq) {
+	  /* got better */
+	  dchi = (ochisq - chisq) / ochisq; 
+	  if ((dchi > 0.03) || (curve > 0)) BigChange = TRUE;
+	} else {
+	  par[i] = tmp_par;
+	  chisq = ochisq;
+	  if (chisq_m < chisq) {
+	    chisq = chisq_m;
+	    par[i] = tmp_par - dpar[i];
+	  }	
+	  if (chisq_p < chisq) {
+	    chisq = chisq_p;
+	    par[i] = tmp_par + dpar[i];
+	  }	
+	}	
+	ochisq = chisq;
+      }
+      if (!BigChange) dpar[i] *= 0.8;
+    }
+
+    gprint (GP_ERR, "try: %d  %f   ", j, chisq);
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n          ");
+    for (i = 0; i < 5; i++) {
+      gprint (GP_ERR, "%f ", dpar[i]);
+    }
+    gprint (GP_ERR, "\n");
+    dchi -= chisq;
+
+  }
+# endif
+
+  free (xs);
+  free (ys);
+  free (zs);
+  
+  plot_outline ();
+  return (TRUE);
+
+}
+
+/* par[0] = x
+   par[1] = y
+   par[2] = dx
+   par[3] = dy
+   par[4] = dxy
+   
+    xp = par[2] * cos (t);
+    yp = par[3] * sin (t);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+*/
+
+# if (1)
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, phi;
+  float xp, yp, x, y;
+  float chisq, R2;
+
+  chisq = 0;
+
+  for (i = 0; i < Npts; i++) {
+    
+    phi = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    /* find a point:
+
+       xp, yp such that atan (r2 sin(phi), r1 cos(phi)) == theta 
+
+       tan (theta) = r2 sin(phi) / r1 cos (phi)
+
+       (r1/r2) tan(theta) = sin(phi) / cos (phi);
+       (r1/r2) tan(theta) = tan (phi)
+
+       phi = atan2 (r1 sin(theta), r2 cos(theta))
+    */
+
+    theta = atan2 (par[2]*sin(phi), par[3]*cos(phi));
+
+    /* this is the point on the ellipse at the same angle as ref point */
+    /* this is wrong, but close -- tends to make ellipses too fat */
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = sqrt (SQ (x - xs[i]) + SQ (y - ys[i]));
+
+    /*
+    Dv = zs[i] - Io;
+    dv = fabs(zs[i]);
+    F2 = Dv * Dv / dv;
+    */
+
+    chisq += R2;
+
+  }
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+
+# else 
+
+float outline_chi (float Io, float *in) {
+
+  int i;
+  float theta, theta1, theta2;
+  float xp, yp, x, y;
+  float chisq, dv, Dv, R2, F2, R, dR;
+
+  int Nvec, Nvec2, Nvecx, Nvecy, Nv, nv;
+
+  chisq = 0;
+
+  nv = 0;
+  Nv = 1000;
+  if (!SelectVector (&Nvec, "dR", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvec2, "dF", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecx, "x", ANYVECTOR)) return (FALSE);
+  if (!SelectVector (&Nvecy, "y", ANYVECTOR)) return (FALSE);
+  REALLOCATE (vectors[Nvec].elements, float, Nv);
+  REALLOCATE (vectors[Nvec2].elements, float, Nv);
+  REALLOCATE (vectors[Nvecx].elements, float, Nv);
+  REALLOCATE (vectors[Nvecy].elements, float, Nv);
+
+  for (i = 0; i < Npts; i++) {
+    
+    theta1 = atan2 (ys[i] - par[1], xs[i] - par[0]) - RAD_DEG * par[4];
+    theta = atan2 (par[2]*sin(theta1), par[3]*cos(theta1));
+
+    xp = par[2] * cos (theta);
+    yp = par[3] * sin (theta);
+    
+    x = xp * cos (par[4] * RAD_DEG) - yp * sin (par[4] * RAD_DEG) + par[0];
+    y = xp * sin (par[4] * RAD_DEG) + yp * cos (par[4] * RAD_DEG) + par[1];
+
+    R2 = SQ (x - xs[i]) + SQ (y - ys[i]);
+
+    /* 
+    Dv = fabs (zs[i] - Io) + 1;
+    dv = zs[i] + 0.2 * fabs (Dv);
+    F2 = Dv * Dv / dv;
+    */
+
+    vectors[Nvec].elements[nv] = x;
+    vectors[Nvec2].elements[nv] = y;
+    vectors[Nvecx].elements[nv] = xs[i];
+    vectors[Nvecy].elements[nv] = ys[i];
+    nv ++;
+    if (nv == Nv - 1) {
+      Nv += 1000;
+      REALLOCATE (vectors[Nvec].elements, float, Nv);
+      REALLOCATE (vectors[Nvec2].elements, float, Nv);
+      REALLOCATE (vectors[Nvecx].elements, float, Nv);
+      REALLOCATE (vectors[Nvecy].elements, float, Nv);
+    }
+
+    /* typical distance might be 1 - 10 pix,
+       typical z error might be 100 cts */
+    chisq += R2; 
+
+  }
+  vectors[Nvec].Nelements = nv;
+  vectors[Nvec2].Nelements = nv;
+  vectors[Nvecx].Nelements = nv;
+  vectors[Nvecy].Nelements = nv;
+
+  chisq = chisq / Npts;
+  return (chisq);
+
+}
+# endif
+
+int plot_outline () {
+  
+  int kapa;
+  float xp, yp, x, y;
+  float dx, dy, theta, t, dt;
+  int Noverlay, NOVERLAY;
+  KiiOverlay *overlay;
+  
+  if (!GetImage (NULL, &kapa, NULL)) return (FALSE);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  dx = par[2];
+  dy = par[3];
+  dt = 1 / MAX (dx, dy);
+  theta = par[4];
+  
+  for (t = 0; t < 6.3; t += dt) {
+    xp = dx * cos (t);
+    yp = dy * sin (t);
+    
+    x = xp * cos (theta * RAD_DEG) - yp * sin (theta * RAD_DEG) + par[0];
+    y = xp * sin (theta * RAD_DEG) + yp * cos (theta * RAD_DEG) + par[1];
+    
+    overlay[Noverlay].type = KII_OVERLAY_BOX;
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = 1.0;
+    overlay[Noverlay].dy = 1.0;
+
+    Noverlay ++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+  KiiLoadOverlay (kapa, overlay, Noverlay, "red");
+  free (overlay);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/polar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/polar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/polar.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "astro.h"
+
+int polar (int argc, char **argv) {
+  
+  double Lo, dL, Do, dD, Mo, dM, No, dN;
+  double xo, yo, Xo, Yo;
+  double x, y, r, t;
+  float *Vin, *Vout, *Vmask;
+  int i, j, nx, ny, Nx, Ny;
+  int X, Y;
+  Buffer *in, *out, *mask;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: polar in out\n");
+    return (FALSE);
+  }
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+
+  /* we expect the input image to have units of distance X and Y */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Mo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dM);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &No);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dN);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vout++, Vmask++) {
+      x = (i - Xo) * dM + Mo;
+      y = (j - Yo) * dN + No;
+      r = hypot(x, y);
+      t = DEG_RAD*atan2 (y, x);
+      while (t < 360.0) {t += 360.0;}
+      while (t > 360.0) {t -= 360.0;}
+      X = (t - Lo) / dL + xo;
+      Y = (r - Do) / dD + yo;
+      if ((X >= 0) && (X < nx) && (Y >= 0) && (Y < ny)) {
+	*Vout += Vin[Y*nx + X];
+	*Vmask += 1;
+      }
+    }
+  }
+
+ return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/precess.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/precess.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/precess.c	(revision 17422)
@@ -0,0 +1,112 @@
+# include "astro.h"
+
+int precess (int argc, char **argv) {
+
+  int i, Julian, Besselian;
+  double T, in_epoch, out_epoch;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  Vector *xvec, *yvec;
+
+  Besselian = Julian = 0;
+  Besselian = get_argument (argc, argv, "B");
+  Julian    = get_argument (argc, argv, "J");
+
+  in_epoch = out_epoch = 2000.0;
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE:  precess (from) (to) RA DEC \n");
+    gprint (GP_ERR, "   you may use B for B1950.0 or J for J2000.0\n");
+    return (FALSE);
+  }
+
+  if (!Julian && !Besselian) { /* assume Julian! */
+    in_epoch  = get_epoch (argv[1], 'J');
+    out_epoch = get_epoch (argv[2], 'J');
+  }
+
+  if ((Julian == 1) && !Besselian) {
+    in_epoch  = 2000.0;
+    out_epoch = get_epoch(argv[2], 'J');
+  }
+
+  if ((Julian == 2) && !Besselian) {
+    in_epoch  = get_epoch(argv[1], 'J');
+    out_epoch = 2000.0;
+  }
+
+  if ((Besselian == 1) && !Julian) {
+    in_epoch  = BtoJ(1950.0); 
+    out_epoch = get_epoch(argv[2], 'B'); 
+  }
+
+  if ((Besselian == 2) && !Julian) {
+    in_epoch  = get_epoch(argv[1], 'B'); 
+    out_epoch = BtoJ(1950.0); 
+  }
+  
+  if (Julian && Besselian) {
+    if (Julian > Besselian) {
+      in_epoch  = BtoJ(1950.0); 
+      out_epoch = 2000.0;
+    }
+    else {
+      in_epoch  = 2000.0;
+      out_epoch = BtoJ(1950.0); 
+    }
+  }
+
+  gprint (GP_ERR, "converting from J%f to J%f\n", in_epoch, out_epoch);
+
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+
+  if (ISNUM(argv[3][0]) && ISNUM(argv[4][0])) {
+    A = atof (argv[3]);
+    D = atof (argv[4]);
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    gprint (GP_LOG, "%f %f -> %f %f\n", A, D, RA, DEC);
+    return (TRUE);
+  }    
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[3], argv[4]);
+    return (FALSE);
+  }
+  
+  for (i = 0; i < xvec[0].Nelements; i++) {
+    A = xvec[0].elements[i];
+    D = yvec[0].elements[i];
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+
+    if (RA < 0)
+      RA += 360;
+    
+    xvec[0].elements[i] = RA;
+    yvec[0].elements[i] = DEC; 
+  }
+
+  return (TRUE);
+
+}  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/profile.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/profile.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/profile.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "astro.h"
+
+int profile (int argc, char **argv) {
+  
+  int i, j, N, Nx, Npt;
+  float *V;
+  double sx, sy;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: profile <buffer> <X vector> <Y vector> x y N\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  sx = atof (argv[4]);
+  sy = atof (argv[5]);
+  N  = atof (argv[6]);
+
+  if (sx - N < 0) goto range_error;
+  if (sy - N < 0) goto range_error;
+  if (sx + N > buf[0].matrix.Naxis[0]) goto range_error;
+  if (sy + N > buf[0].matrix.Naxis[1]) goto range_error;
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  xvec[0].Nelements = yvec[0].Nelements = (int)SQ(2*N+1);
+  REALLOCATE (xvec[0].elements, float, 5*(int)SQ(2*N+1));
+  REALLOCATE (yvec[0].elements, float, 5*(int)SQ(2*N+1));
+  bzero (yvec[0].elements, (int)SQ(2*N+1)*sizeof(float)+1);
+  V = (float *)(buf[0].matrix.buffer); 
+  Npt = 0;
+  Nx = buf[0].matrix.Naxis[0];
+  for (i = sx - N; i < sx + N; i++) {
+    for (j = sy - N; j < sy + N; j++, Npt++) {
+      yvec[0].elements[Npt] = V[i + j*Nx];
+      xvec[0].elements[Npt] = hypot (i - sx, j - sy);
+    }
+  }
+
+  fsortpair (xvec[0].elements, yvec[0].elements, xvec[0].Nelements);
+
+  return (TRUE);
+
+range_error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/region.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/region.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/region.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "astro.h"
+
+int region (int argc, char **argv) {
+  
+  char string[256];
+  double Ra, Dec, Radius;
+  float dx, dy;
+  int N, kapa;
+  char *name;
+  Graphdata graphmode;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((N = get_argument (argc, argv, "-image"))) {
+    remove_argument (N, &argc, argv);
+    KapaGetImageCoords (kapa, &graphmode.coords);
+    KapaGetImageRange (kapa, &graphmode.xmin, &graphmode.xmax, &graphmode.ymax, &graphmode.ymin);
+    SetGraph (&graphmode);
+    return (TRUE);
+    // Set Region based on image
+  }
+
+  if ((N = get_argument (argc, argv, "-ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ew"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipeast = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "+ns"))) {
+    remove_argument (N, &argc, argv);
+    graphmode.flipnorth = FALSE;
+  }
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: region Ra Dec Radius [projection] [orientation]\n");
+    gprint (GP_ERR, " current: %f %f (%f x %f) (%s)\n", 
+	     graphmode.coords.crval1, graphmode.coords.crval2, 
+	     fabs(graphmode.xmax - graphmode.xmin), 
+	     fabs(graphmode.ymax - graphmode.ymin), 
+	     &graphmode.coords.ctype[5]);
+    return (FALSE);
+  }
+  
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  Radius = atof (argv[3]);
+  strcpy (graphmode.coords.ctype, "RA---TAN");
+  if (argc == 5) {
+    if (!strcasecmp (argv[4], "TAN")) 
+      strcpy (graphmode.coords.ctype, "RA---TAN");
+    if (!strcasecmp (argv[4], "SIN")) 
+      strcpy (graphmode.coords.ctype, "RA---SIN");
+    if (!strcasecmp (argv[4], "ARC")) 
+      strcpy (graphmode.coords.ctype, "RA---ARC");
+    if (!strcasecmp (argv[4], "STG")) 
+      strcpy (graphmode.coords.ctype, "RA---STG");
+    if (!strcasecmp (argv[4], "ZEA"))
+      strcpy (graphmode.coords.ctype, "RA---ZEA");
+    if (!strcasecmp (argv[4], "AIT")) 
+      strcpy (graphmode.coords.ctype, "RA---AIT");
+    if (!strcasecmp (argv[4], "GLS")) 
+      strcpy (graphmode.coords.ctype, "RA---GLS");
+    if (!strcasecmp (argv[4], "PAR")) 
+      strcpy (graphmode.coords.ctype, "RA---PAR");
+  }
+
+  
+  /* ask kapa for coordinate limits, to get the right aspect ratio */
+  KapaGetLimits (kapa, &dx, &dy);
+  dx = fabs (dx);
+  dy = fabs (dy); 
+
+  /* define limits for Ra, Dec at center, grid in degrees */
+  if (dy < dx) {
+    graphmode.xmin = -(dx/dy)*Radius;
+    graphmode.ymin = -Radius;
+    graphmode.xmax = (dx/dy)*Radius;
+    graphmode.ymax = Radius;
+  } else {
+    graphmode.xmin = -Radius;
+    graphmode.ymin = -(dy/dx)*Radius;
+    graphmode.xmax = Radius;
+    graphmode.ymax = (dy/dx)*Radius;
+  } 
+
+  set_variable ("XMIN", graphmode.xmin);
+  set_variable ("XMAX", graphmode.xmax);
+  set_variable ("YMIN", graphmode.ymin);
+  set_variable ("YMAX", graphmode.ymax);
+
+  set_variable ("RMIN", Ra  + graphmode.xmin);
+  set_variable ("RMAX", Ra  + graphmode.xmax);
+  set_variable ("DMIN", Dec + graphmode.ymin);
+  set_variable ("DMAX", Dec + graphmode.ymax);
+
+  set_int_variable ("EAST_RIGHT", !graphmode.flipeast);
+  set_int_variable ("NORTH_UP", !graphmode.flipnorth);
+
+  graphmode.coords.pc1_1 = (graphmode.flipeast) ? -1 : 1;
+  graphmode.coords.pc2_2 = (graphmode.flipnorth) ? -1 : 1;
+
+  graphmode.coords.pc1_2 = graphmode.coords.pc2_1 = 0.0;
+  graphmode.coords.crval1 = Ra;
+  graphmode.coords.crval2 = Dec;
+  graphmode.coords.crpix1 = 0.0;
+  graphmode.coords.crpix2 = 0.0;
+  graphmode.coords.cdelt1 = graphmode.coords.cdelt2 = 1.0;
+
+  KapaClearSections (kapa);
+  KapaSetLimits (kapa, &graphmode);
+
+  /* drop this? */
+  sprintf (string, "%8.4f %8.4f (%f)", Ra, Dec, Radius);
+  KapaSendLabel (kapa, string, 2);
+
+  // XXX is this the right thing to be doing?
+  SetGraph (&graphmode);
+  return (TRUE);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/rotcurve.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/rotcurve.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/rotcurve.c	(revision 17422)
@@ -0,0 +1,120 @@
+# include "astro.h"
+
+int rotcurve (int argc, char **argv) {
+  
+  int i, j, X, Y, n, Ncurve;
+  float *Vin, *Vout, *Vmask;
+  int nx, ny, Nx, Ny, N;
+  double L, dL, Lo, V, Vo, dV, Bo, dB, Do, dD;
+  double xo, yo, Xo, Yo;
+  double sl, cl, wo, Ro, Rs, wr, r, fr, d, min;
+  double R[100], T[100], W[100];
+  FILE *f;
+  Buffer *in, *out, *mask;
+
+  min = -1000;
+  if ((N = get_argument (argc, argv, "-min"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: rotcurve in out mask curve.txt\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[4], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't find rotation curve data file %s\n", argv[4]);
+    return (FALSE);
+  }
+  for (i = 0; fscanf (f, "%lf %lf", &R[i], &T[i]) != EOF; i++) {
+    W[i] = T[i] / R[i];
+  }  
+  fclose (f);
+  Ncurve = i;
+
+  if ((in   = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((mask = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = out[0].matrix.Naxis[0];
+  Ny = out[0].matrix.Naxis[1];
+  nx = mask[0].matrix.Naxis[0];
+  ny = mask[0].matrix.Naxis[1];
+  if ((Nx != nx) && (Ny != ny)) {
+    gprint (GP_ERR, "output and mask must have same dimensions\n");
+    return (FALSE);
+  }
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  /* we expect the input image to have units of velocity, lattitude, and longitude */
+  gfits_scan (&in[0].header, "CRVAL1", "%lf", 1, &Vo);
+  gfits_scan (&in[0].header, "CDELT1", "%lf", 1, &dV);
+  gfits_scan (&in[0].header, "CRPIX1", "%lf", 1, &xo);
+  gfits_scan (&in[0].header, "CRVAL2", "%lf", 1, &Bo);
+  gfits_scan (&in[0].header, "CDELT2", "%lf", 1, &dB);
+  gfits_scan (&in[0].header, "CRPIX2", "%lf", 1, &yo);
+  gfits_scan (&in[0].header, "CRVAL3", "%lf", 1, &L);
+  Vo *= 0.001;
+  dV *= 0.001;
+
+  /* we expect the output image to have units of longitude and distance */
+  gfits_scan (&out[0].header, "CRVAL1", "%lf", 1, &Lo);
+  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dL);
+  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&out[0].header, "CRVAL2", "%lf", 1, &Do);
+  gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dD);
+  gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &Yo);
+
+  while (L >= 360) {L -= 360.0;}
+  while (L < 0.0)  {L += 360.0;}
+  X = (L - Lo) / dL + Xo;
+  if ((X >= Nx) || (X < 0)) {
+    gprint (GP_ERR, "X out of range\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "L: %f (%d)\n", L, X);
+
+  cl = cos (L*RAD_DEG);
+  sl = sin (L*RAD_DEG);
+  wo = 25.0;
+  Ro = 10.0;
+  Rs = Ro*sl;
+  /* this method depends on wr monotonically decreasing */
+
+  Vin  = (float *)in[0].matrix.buffer;
+  Vout = (float *)out[0].matrix.buffer;
+  Vmask = (float *)mask[0].matrix.buffer;
+  for (j = 0; j < ny; j++) {
+    for (i = 0; i < nx; i++, Vin++) {
+      if (*Vin <= min) continue;
+      V = (i - xo) * dV + Vo;
+      wr = V/Rs + wo;
+      for (n = 0; (n < Ncurve) && (wr < W[n]); n++);
+      if ((n == 0) || (n == Ncurve)) {
+	continue;
+      }
+      r = (wr - W[n]) *  (R[n-1] - R[n]) / (W[n-1] - W[n]) + R[n];
+      fr = (Ro/r);
+      if (r < fabs(Rs)) { /* can't be on rotation curve */
+	continue;
+      }
+      if (r < Ro)
+	d = Ro*cl - sqrt(r*r - Rs*Rs);
+      else 
+	d = Ro*cl + sqrt(r*r - Rs*Rs);
+      Y = (d - Do) / dD + Yo;
+      if ((Y < Ny) && (Y >= 0)) {
+	Vout[Y*Nx + X] += *Vin;
+	Vmask[Y*Nx + X] += 1.0;
+      }
+    }
+  }
+
+
+  return (TRUE);
+
+} 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/scale.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/scale.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/scale.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "astro.h"
+
+int scale (int argc, char **argv) {
+
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: scale (buffer) (key) [-r/-w] (value)\n");
+    return (FALSE);
+  }  
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[2], "bzero") && strcasecmp (argv[2], "bscale")) {
+    gprint (GP_ERR, "use bzero or bscale only\n");
+    return (FALSE);
+  }
+    
+  if (strcmp (argv[3], "-r") && strcmp (argv[3], "-w")) {
+    gprint (GP_ERR, "use -r or -w only\n");
+    return (FALSE);
+  }
+    
+  if (!strcmp (argv[3], "-r")) {
+    if (!strcasecmp (argv[2], "bzero")) {
+      set_variable (argv[4], (double) buf[0].bzero);
+    } else {
+      set_variable (argv[4], (double) buf[0].bscale);
+    }      
+  } else {
+    if (!strcasecmp (argv[2], "bzero")) {
+      buf[0].bzero = atof (argv[4]);
+    } else {
+      buf[0].bscale = atof (argv[4]);
+    }      
+  }
+
+  return (TRUE);
+}
+
+/* get or set external bzero / bscale values 
+   (these keywords are set to 0,1 internally, 
+   so we can't just manipulate them like other keywords */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/sexigesimal.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/sexigesimal.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/sexigesimal.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "astro.h"
+
+int sexigesimal (int argc, char **argv) {
+  
+  int HMS, N;
+  double value;
+  char string[80];
+
+  HMS = TRUE;
+  if ((N = get_argument (argc, argv, "-hms"))) {
+    HMS = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-hh"))) {
+    HMS = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: sexigesimal (from) [to]\n");
+    return (FALSE);
+  }
+
+  if (HMS) {
+    if (!ohana_dms_to_ddd (&value, argv[1])) {
+      gprint (GP_ERR, "syntax error in input\n");
+      return (FALSE);
+    }
+    if (argc == 3) {
+      set_variable (argv[2], value);
+    } else {
+      gprint (GP_LOG, "%10.6f\n", value);
+    }
+    return (TRUE);
+  } else {
+    value = atof (argv[1]);
+    hms_format (string, value);
+    if (argc == 3) {
+      set_str_variable (argv[2], string);
+    } else {
+      gprint (GP_LOG, "%s\n", string);
+    }
+    return (TRUE);
+  }      
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/spec.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/spec.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/spec.c	(revision 17422)
@@ -0,0 +1,112 @@
+# include "astro.h"
+
+int spec (int argc, char **argv) {
+
+  int i, j, Xo, X1, y1, y2, Nx, Ny;
+  int Nlong, Ngap, Nrow, N, Nring;
+  float *buffer, *V;
+  double sky, sky2, S, SX, F, R, Npts;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Nlong = 31;
+  if ((N = get_argument (argc, argv, "-Nlong"))) {
+    remove_argument (N, &argc, argv);
+    Nlong  = 0.5*atof(argv[N]);
+    Nlong = 2*Nlong + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Ngap = 15;
+  if ((N = get_argument (argc, argv, "-Ngap"))) {
+    remove_argument (N, &argc, argv);
+    Ngap  = 0.5*atof(argv[N]);
+    Ngap = 2*Ngap + 1;  /* force an odd number */
+    remove_argument (N, &argc, argv);
+  }
+  
+  Nrow = 1;
+  if ((N = get_argument (argc, argv, "-Nrow"))) {
+    remove_argument (N, &argc, argv);
+    Nrow  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: spec buffer x y1 y2 X Y [-Nlong N] [-Ngap N] [-Nrow N]\n");
+    return (FALSE);
+  }
+  
+  if ((Nrow < 1) || (Nlong < 2) || (Ngap < 1) || (Nlong - Ngap < 2)) {
+    gprint (GP_ERR, "bad values for options\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  Xo = atof (argv[2]);
+  y1 = atof (argv[3]);
+  y2 = atof (argv[4]);
+
+  if ((xvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = y2 - y1;
+  REALLOCATE (xvec[0].elements, float, N);
+  REALLOCATE (yvec[0].elements, float, N);
+  xvec[0].Nelements = N;
+  yvec[0].Nelements = N;
+  
+  ALLOCATE (buffer, float, Nlong);
+
+  for (j = 0; j < y2 - y1; j++) {
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + Xo - (int)(0.5*Nlong);
+    /* find sky on edge */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[i] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+    }
+    sky = sky / Npts;
+    /* find center column for this row */
+    for (S = SX = i = 0, Nring = 0; i < Ngap; i++, V++, Nring++) {
+      S += (*V - sky);
+      SX += (*V - sky)*(i + Xo - 0.5*Ngap);
+    }
+    X1 = SX / S;
+    gprint (GP_ERR, "%4d %4d %5.1f ", j+y1, X1, sky);
+    /*    X1 = MAX (MIN (X1, Xo + 0.5+Ngap), Xo - 0.5+Ngap); */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Nlong);
+    /* find sky on edges */
+    for (i = 0, Nring = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 + (int)(0.5*Ngap);
+    for (i = 0; i < 0.5*(Nlong - Ngap); i++, V++, Nring++) {
+      buffer[Nring] = *V;
+    }
+    fsort (buffer, Nring);
+    for (Npts = sky = sky2 = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+      sky += buffer[i];
+      sky2 += buffer[i]*buffer[i];
+    }
+    sky = sky / Npts;
+    sky2 = (sky2 / Npts - sky*sky);
+    /* find weighted flux */
+    V = (float *) (buf[0].matrix.buffer) + Nx*(y1 + j) + X1 - (int)(0.5*Ngap);
+    for (F = R = i = 0; i < Ngap; i++, V++) {
+      F += (*V - sky) / sky2;
+      R += 1.0 / sky2;
+    }
+    xvec[0].elements[j] = j + y1; 
+    yvec[0].elements[j] = F / R; 
+    gprint (GP_ERR, " %5.1f %7.1f  %6.2f\n", sky, sky2, (F/R));
+  }    
+
+  free (buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/star.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/star.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/star.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "astro.h"
+
+int star (int argc, char **argv) {
+
+  int x, y, N, dx, Nborder;
+  double Z, max;
+  Buffer *buf;
+
+  Nborder = 3;
+  if ((N = get_argument (argc, argv, "-border"))) {
+    remove_argument (N, &argc, argv);
+    Nborder  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Nborder = MAX (Nborder, 1);
+  
+  max = 60000;
+  if ((N = get_argument (argc, argv, "-sat"))) {
+    remove_argument (N, &argc, argv);
+    max  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: star (buffer) x y [dx] [-border N] [-sat cnts]\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = 11;
+  x = atof (argv[2]);
+  y = atof (argv[3]);
+  if (argc == 5) {
+    dx = atof (argv[4]);
+  }
+
+  Z = get_aperture_stats (&buf[0].matrix, x, y, dx, Nborder, max);
+  
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/testfit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/testfit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/testfit.c	(revision 17422)
@@ -0,0 +1,161 @@
+# include "astro.h"
+
+/* local private functions */
+float fgaussOD (float, float *, int, float *);
+
+int imfit (int argc, char **argv) {
+
+  float par[4], *v1, *v2, *dy, chisq, **covar;
+  int i, Npts, Npar;
+  Vector *xvec, *yvec, *svec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imfit <x> <y> <dy>\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  v1 = svec[0].elements;
+  v2 = dy;
+  
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  par[0] = 7;
+  par[1] = 2;
+  par[2] = 6;
+  par[3] = 1;
+  Npar = 4;
+
+  mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+
+  for (i = 0; i < 10; i++) {
+
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD);
+    gprint (GP_ERR, "chisq: %f, %f %f %f %f\n", chisq, par[0], par[1], par[2], par[3]);
+
+  }  
+
+  covar = mrqcovar (Npar);
+
+  for (i = 0; i < Npar; i++) {
+    gprint (GP_ERR, "%d  %f  %f\n", i, par[i], covar[i][i]);
+  }
+
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussOD (float x, float *par, int Npar, float *dpar) {
+
+  float X, S, Z, R, f;
+
+  X = x - par[0];
+  S = 1.0 / (par[1]*par[1]);
+  Z = -0.5*X*X*S;
+  R = exp (Z);
+  f = par[2]*R + par[3];
+
+  dpar[0] = par[2]*R*X*S;
+  dpar[1] = dpar[0]*X/par[1];
+  dpar[2] = R;
+  dpar[3] = 1;
+  
+  return (f);
+
+}
+
+# if (0)
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float testF (float x, float *par, int Npar, float *dpar) {
+
+  float f;
+
+  f = par[0]*x + par[1];
+
+  dpar[0] = x;
+  dpar[1] = 1;
+  
+  return (f);
+
+}
+
+
+/* pars: x, y, sx, sy, sxy, sky I, */
+float fgaussTD (float x, float y, float *par, int Npar) {
+
+  X = x - par[0];
+  Y = y - par[1];
+  
+  t1 = X / par[2];
+  t2 = Y * Y / par[3];
+  t3 = Y * par[4] * 2.0;
+
+  r = 0.5 * ((t1 + t3)*X + t2);
+  f = par[5] + par[6] / (1.0 + r*(1.0 + 0.5*r*(1.0 + 0.33333333*r)));
+  
+  return (f);
+
+}
+
+float chisq (float *buf, float *sig, int Nx, int Ny, float (func)(), float *par, int Npar) {
+
+  float *ptr;
+
+  X = 0;
+  ptr = buf;
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++, ptr++, sig++) {
+      f = *ptr - func ((float) i, (float) j, par, Npar);
+      X += (f * f) / *sig;
+    }
+  }    
+  return (X);
+}
+
+  int i, j, Nbuf, status;
+  char *string;
+  double Npix, N1, N2, max, min, range, median;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: imfit <buffer> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buffers[Nbuf].matrix.Naxis[0]) || 
+      (sy+ny > buffers[Nbuf].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  Npix = nx*ny;
+
+  ALLOCATE (tempbuf, float, Npix);
+
+  buf = tempbuf;
+  for (j = 0; j < ny; j++) {
+    V = (float *)(buffers[Nbuf].matrix.buffer) + (j+sy)*buffers[Nbuf].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      *buf = *V;
+    }
+  }
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/transform.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/transform.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/transform.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "astro.h"
+
+int transform (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  Coords coords_in, coords_out;
+  double scale_in, scale_out;
+  int X, Y;
+  double x, y, r, d, dx, dy;
+  double frac;
+  char *Sout, *S;
+  float *Vin, *Vout;
+  Buffer *in, *out;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: transform <from> <to>\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+  bzero (Vout, Nx*Ny*sizeof(float));
+  ALLOCATE (S, char, Nx*Ny);
+  Sout = S;
+  bzero (Sout, Nx*Ny*sizeof(char));
+  frac = 0.333;
+
+  /* if (scale_in < scale_out) { */
+
+  for (j = 0; j < in[0].header.Naxis[1]; j++) {
+    gprint (GP_ERR, ".");
+    for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+      for (dx = 0.0 + 0.5*frac; dx < 1.0 - 0.5*frac; dx += frac) {
+	for (dy = 0.0 + 0.5*frac; dy < 1.0 - 0.5*frac; dy += frac) {
+	  XY_to_RD (&r, &d, i + dx, j + dy, &coords_in);
+	  RD_to_XY (&x, &y, r, d, &coords_out);
+	  X = x; Y = y;
+	  if ((X > -1) && (X < Nx) && (Y > -1) && (Y < Ny)) {
+	    Vout[X + Y*Nx] += *Vin;
+	    Sout[X + Y*Nx] ++;
+	  }
+	}
+      }
+    }
+  }
+
+  Sout = S;
+  Vout = (float *) out[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++, Vout++, Sout++) {
+    *Vout = *Vout / *Sout;
+  }
+
+  free (S);
+    
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/warp.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/warp.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.astro/warp.c	(revision 17422)
@@ -0,0 +1,237 @@
+# include "astro.h"
+
+/*** needs mosaic astrometry ***/
+
+static double XO, XX, XY;
+static double YO, YX, YY;
+int ZERO;
+
+int map_output_to_input (int Npix, double df);
+int map_input_to_output (int Npix, double df);
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix);
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin);
+
+Coords coords_in, coords_out;
+Buffer *in, *out, *wt;
+
+int warp (int argc, char **argv) {
+
+  int Nlinear, Np, N;
+  double scale_in, scale_out, df;
+
+  ZERO = FALSE;
+  if ((N = get_argument (argc, argv, "-zero"))) {
+    ZERO = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: transform <from> <to> <weight> (Nlinear)\n");
+    gprint (GP_ERR, "  output buffer must exist with target astrometry header\n");
+    gprint (GP_ERR, "  Nlinear is the pixel scale for linear astrometric transformation\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((wt  = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Nlinear = atoi (argv[4]);
+
+  GetCoords (&coords_in, &in[0].header);
+  GetCoords (&coords_out, &out[0].header);
+
+  /* for the moment, disable WRP / DIS */
+  if (!strcmp(&coords_in.ctype[4], "-WRP") || !strcmp(&coords_out.ctype[4], "-WRP")) {
+    gprint (GP_ERR, "WRP mode not implemented for astrom\n");
+    return (FALSE);
+  }
+  
+  scale_in = sqrt(fabs(coords_in.cdelt1*coords_in.cdelt2*(coords_in.pc1_1*coords_in.pc2_2 - coords_in.pc1_2*coords_in.pc2_1)));
+  scale_out = sqrt(fabs(coords_out.cdelt1*coords_out.cdelt2*(coords_out.pc1_1*coords_out.pc2_2 - coords_out.pc1_2*coords_out.pc2_1)));
+  
+  gprint (GP_ERR, "%f - %f\n", scale_in, scale_out);
+
+  if (scale_in > scale_out) {
+    Np = MAX (1, 3*scale_out / scale_in);
+    df = 1.0 / Np;
+    map_output_to_input (Nlinear, df);
+  } else {
+    Np = MAX (1, 3*scale_in / scale_out);
+    df = 1.0 / Np;
+    map_input_to_output (Nlinear, df);
+  }
+  return (TRUE);
+}
+
+/* mode 1: input pixels >> output pixels: loop over output pixels */
+/* mode 2: input pixels << output pixels: loop over input pixels */
+/* mode 3: input pixels ~= output pixels: drizzle input to output */
+
+/* loop over the input pixels, map input output image */
+int map_output_to_input (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  nx = in[0].header.Naxis[0];
+  ny = in[0].header.Naxis[1];
+  Nx = out[0].header.Naxis[0];
+  Ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, Nx*Ny*sizeof(float));
+    bzero (Vwt,  Nx*Ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_out, &coords_in, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  No = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  Ni = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* loop over the input pixels, map input output image */
+int map_input_to_output (int Npix, double df) {
+
+  int i, j, Ni, No, Nx, Ny, nx, ny;
+  float *Vin, *Vout, *Vwt;
+  double x, y, X, Y;
+
+  /* loop over output pixels */
+  /* set up pointers for buffers */
+  Vin  = (float *) in[0].matrix.buffer;
+  Vout = (float *) out[0].matrix.buffer;
+  Vwt  = (float *) wt[0].matrix.buffer;
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = out[0].header.Naxis[0];
+  ny = out[0].header.Naxis[1];
+
+  if (ZERO) {
+    bzero (Vout, nx*ny*sizeof(float));
+    bzero (Vwt,  nx*ny*sizeof(float));
+  }
+
+  for (j = 0; j < Ny; j+=Npix) {
+    for (i = 0; i < Nx; i+=Npix) {
+      
+      /* define linear transformation in region */
+      set_linear_terms (&coords_in, &coords_out, i, j, Npix);
+
+      for (X = i; (X < i + Npix) && (X < Nx); X += df) {
+	for (Y = j; (Y < j + Npix) && (Y < Ny); Y += df) {
+	  
+	  Ni = (int)X + ((int)Y)*Nx;
+	  apply_terms (&x, &y, X, Y);
+	  if (x < 0) continue;
+	  if (x >= nx) continue;
+	  if (y < 0) continue;
+	  if (y >= ny) continue;
+	  No = (int)x + ((int)y)*nx;
+
+	  Vout[No] += Vin[Ni];
+	  Vwt[No] ++;
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
+
+/* find the linear astrometric fix between images at this location */
+void set_linear_terms (Coords *in, Coords *out, int i, int j, int Npix) {
+
+  int n;
+  double x, y, x2, y2, xy, X, Y, Xx, Xy, Yx, Yy;
+  double Xin, Yin, Xout, Yout;
+  double Sx2, Sy2, Sxy, SXx, SXy, SYx, SYy;
+  double N, r, d;
+
+  Xin = Yin = 0;
+  N = x = y = x2 = y2 = xy = X = Y = Xx = Xy = Yx = Yy = 0;
+
+  /* define several test points, fit a line to the input,output pairs */
+  for (n = 0; n < 3; n++) {
+
+    switch (n) {
+    case 0:
+      Xin = i;
+      Yin = j;
+      break;
+    case 1:
+      Xin = i + Npix;
+      Yin = j;
+      break;
+    case 2:
+      Xin = i;
+      Yin = j + Npix;
+      break;
+    }
+
+    XY_to_RD (&r, &d, Xin, Yin, in);
+    RD_to_XY (&Xout, &Yout, r, d, out);
+
+    x  += Xin;
+    y  += Yin;
+    x2 += Xin*Xin;
+    y2 += Yin*Yin;
+    xy += Xin*Yin;
+    X  += Xout;
+    Y  += Yout;
+    Xx += Xout*Xin;
+    Xy += Xout*Yin;
+    Yx += Yout*Xin;
+    Yy += Yout*Yin;
+    N  += 1.0;
+  }
+
+  Sx2 = x2 - x*x/N;
+  Sy2 = y2 - y*y/N;
+  Sxy = xy - x*y/N;
+  SXx = Xx - X*x/N;
+  SXy = Xy - X*y/N;
+  SYx = Yx - Y*x/N;
+  SYy = Yy - Y*y/N;
+  
+  XX = (SXx*Sy2 - SXy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XY = (SXy*Sx2 - SXx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  XO = X/N - XX*x/N - XY*y/N;
+
+  YX = (SYx*Sy2 - SYy*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YY = (SYy*Sx2 - SYx*Sxy) / (Sx2*Sy2 - Sxy*Sxy);
+  YO = Y/N - YX*x/N - YY*y/N;
+
+}
+
+
+void apply_terms (double *Xout, double *Yout, double Xin, double Yin) {
+  *Xout = XO + XX*Xin + XY*Yin;
+  *Yout = YO + YX*Xin + YY*Yin;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/Makefile	(revision 17422)
@@ -0,0 +1,78 @@
+default: libbasiccmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.basic
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# basic user commands ########################
+
+srcs = \
+$(SRC)/init.$(ARCH).o       \
+$(SRC)/break.$(ARCH).o	     \
+$(SRC)/cd.$(ARCH).o	     \
+$(SRC)/config.$(ARCH).o     \
+$(SRC)/continue.$(ARCH).o   \
+$(SRC)/dirname.$(ARCH).o     \
+$(SRC)/date.$(ARCH).o	     \
+$(SRC)/echo.$(ARCH).o	     \
+$(SRC)/file.$(ARCH).o	     \
+$(SRC)/getchr.$(ARCH).o     \
+$(SRC)/help.$(ARCH).o	     \
+$(SRC)/input.$(ARCH).o	     \
+$(SRC)/list.$(ARCH).o	     \
+$(SRC)/list_help.$(ARCH).o  \
+$(SRC)/list_vars.$(ARCH).o  \
+$(SRC)/local.$(ARCH).o	     \
+$(SRC)/macro.$(ARCH).o	     \
+$(SRC)/memory.$(ARCH).o     \
+$(SRC)/mkdir.$(ARCH).o     \
+$(SRC)/module.$(ARCH).o     \
+$(SRC)/output.$(ARCH).o     \
+$(SRC)/quit.$(ARCH).o	     \
+$(SRC)/run_for.$(ARCH).o    \
+$(SRC)/run_if.$(ARCH).o     \
+$(SRC)/run_while.$(ARCH).o  \
+$(SRC)/scan.$(ARCH).o	     \
+$(SRC)/shell.$(ARCH).o	     \
+$(SRC)/sprintf.$(ARCH).o    \
+$(SRC)/fprintf.$(ARCH).o    \
+$(SRC)/strlen.$(ARCH).o     \
+$(SRC)/substr.$(ARCH).o     \
+$(SRC)/strpop.$(ARCH).o     \
+$(SRC)/strsub.$(ARCH).o     \
+$(SRC)/usleep.$(ARCH).o     \
+$(SRC)/sleep.$(ARCH).o	     \
+$(SRC)/wait.$(ARCH).o	     \
+$(SRC)/which.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libbasiccmd.$(ARCH).a: $(srcs)
+$(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libbasiccmd.a: $(LIB)/libbasiccmd.$(ARCH).a
+$(DESTLIB)/libbasiccmd.$(DLLTYPE): $(LIB)/libbasiccmd.$(ARCH).$(DLLTYPE)
+
+libbasiccmd: $(DESTLIB)/libbasiccmd.a $(DESTLIB)/libbasiccmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libbasiccmd.a
+	rm -f $(DESTLIB)/libbasiccmd.$(DLLTYPE)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/break.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/break.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/break.c	(revision 17422)
@@ -0,0 +1,27 @@
+# include "basic.h"
+
+int exec_break (int argc, char **argv) {
+
+  int N, value;
+
+  if ((N = get_argument (argc, argv, "-auto"))) {
+    remove_argument (N, &argc, argv);
+    value = -1;
+    if (!strcasecmp (argv[N], "on")) value = TRUE;
+    if (!strcasecmp (argv[N], "off")) value = FALSE;
+    if (value == -1) {
+      gprint (GP_ERR, "USAGE: break -auto [on / off]\n");
+      if (auto_break) 
+	gprint (GP_ERR, "auto break on\n");
+      else 
+	gprint (GP_ERR, "auto break off\n");
+      return (FALSE);
+    }
+    auto_break = value;
+    return (TRUE);
+  }
+
+  loop_break = TRUE;
+  return (FALSE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/cd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/cd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/cd.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "basic.h"
+
+int cd (int argc, char **argv) {
+
+  int N, VERBOSE, status;
+  char *cwd;
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = FALSE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: cd <path>\n");
+    return (FALSE);
+  }
+
+  status = chdir (argv[1]);
+  if (!status) {
+    if ((cwd = getcwd (NULL, 64)) == NULL) {
+      gprint (GP_ERR, "error getting cwd\n");
+      return (FALSE);
+    }
+    if (VERBOSE) gprint (GP_LOG, "cwd: %s\n", cwd);
+    ohana_memregister (cwd);
+    free (cwd);
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "error changing to %s\n", argv[1]);
+  return (FALSE);
+
+}
+
+int pwd (int argc, char **argv) {
+
+  int N;
+  char *cwd, *var;
+
+  var = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: pwd [-var variable]\n");
+    return (FALSE);
+  }
+  
+  if ((cwd = getcwd(NULL, 64)) == NULL) {
+    gprint (GP_ERR, "error getting cwd\n");
+    if (var != NULL) free (var);
+    return (FALSE);
+  }
+  if (var == NULL) {
+      gprint (GP_LOG, "cwd: %s\n", cwd);
+  } else {
+      set_str_variable (var, cwd);
+      free (var);
+  }
+  ohana_memregister (cwd);
+  free (cwd);
+  return (TRUE);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/config.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/config.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/config.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "basic.h"
+
+int config (int argc, char **argv) {
+
+  if (!ConfigInit (&argc, argv)) return (FALSE);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/continue.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/continue.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/continue.c	(revision 17422)
@@ -0,0 +1,12 @@
+# include "basic.h"
+
+int exec_next (int argc, char **argv) {
+  loop_next = TRUE;
+  return (TRUE);
+}
+
+int exec_last (int argc, char **argv) {
+  loop_last = TRUE;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/date.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/date.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/date.c	(revision 17422)
@@ -0,0 +1,57 @@
+# include "basic.h"
+
+int date (int argc, char **argv) {
+  
+  int N, SECONDS, REFTIME;
+  struct timeval now;
+  char *tstring = NULL;
+  char *varName = NULL;
+
+  SECONDS = FALSE;
+  if ((N = get_argument (argc, argv, "-seconds"))) {
+    remove_argument (N, &argc, argv);
+    SECONDS = TRUE;
+  } else {
+    ALLOCATE (tstring, char, 32);
+  }
+
+  REFTIME = 0;
+  if ((N = get_argument (argc, argv, "-reftime"))) {
+    remove_argument (N, &argc, argv);
+    REFTIME = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: date [-var variable] [-seconds] [-reftime seconds]\n");
+    return (FALSE);
+  }
+
+  gettimeofday (&now, NULL);
+  if (SECONDS) {
+    if (varName) {
+      set_int_variable (varName, now.tv_sec - REFTIME);
+    } else {
+      gprint (GP_ERR, "%d\n", now.tv_sec - REFTIME);
+    }
+  } else {
+    ctime_r (&now.tv_sec, tstring);
+    N = strlen (tstring) - 1;
+    tstring[N] = 0;
+
+    if (varName) {
+      set_str_variable (varName, tstring);
+    } else {
+      gprint (GP_ERR, "%s\n", tstring);
+    }
+    free (tstring);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/dirname.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/dirname.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/dirname.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int dirname_opihi (int argc, char **argv) {
+
+  int N;
+  char *dirName, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: dirname (path) [-var name]\n");
+    return (FALSE);
+  }
+
+  dirName = pathname (argv[1]);
+
+  if (varName == NULL) {
+    gprint (GP_LOG, "%s\n", dirName);
+  } else {
+    set_str_variable (varName, dirName);
+    free (varName);
+  }    
+
+  free (dirName);
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/echo.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/echo.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/echo.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "basic.h"
+
+int echo (int argc, char **argv) {
+  
+  int i;
+
+  for (i = 1; i < argc - 1; i++) {
+    gprint (GP_LOG, "%s ", argv[i]);
+  }
+  if (argc >= 2) {
+      gprint (GP_LOG, "%s\n", argv[argc - 1]);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/file.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/file.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/file.c	(revision 17422)
@@ -0,0 +1,32 @@
+# include "basic.h"
+
+int file (int argc, char **argv) {
+  
+  /* usage: file (filename) [var] */
+
+  int status, vstat;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: file (filename) [var]\n");
+    return (FALSE);
+  }
+
+  status = stat (argv[1], &fstats);
+
+  vstat = !status;
+
+  if (argc == 3) {
+      
+    set_int_variable (argv[2], vstat);
+
+  } else {
+
+    gprint (GP_ERR, "file %s is ", argv[1]);
+    if (!vstat) gprint (GP_ERR, "not ");
+    gprint (GP_ERR, "found\n");
+    
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/fprintf.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/fprintf.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/fprintf.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function should ALLOCATE the output buffers
+int fprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: fprintf format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[1];
+  for (i = 2; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'f':
+	sprintf (tmp, fmt, atof(argv[i]));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	sprintf (tmp, fmt, atoi(argv[i]));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,s,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  gprint (GP_LOG, "%s\n", line);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/getchr.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/getchr.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/getchr.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: getchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 4) {
+    set_int_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "basic.h"
+
+int help (int argc, char **argv) {
+
+  int Nbytes;
+  FILE *f;
+  char *helpdir, *file, buff[512];
+
+  helpdir = get_variable ("HELPDIR");
+  if (helpdir == (char *) NULL) {
+    gprint (GP_ERR, "variable HELPDIR not found\n");
+    return (FALSE);
+  }
+
+  if (argc == 1) {
+    sprintf (buff, "ls %s", helpdir);
+    system (buff);
+    return (TRUE);
+  }
+
+  Nbytes = strlen(helpdir) + strlen(argv[1]) + 2;
+  ALLOCATE (file, char, Nbytes);
+  snprintf (file, Nbytes, "%s/%s", helpdir, argv[1]);
+
+  f = fopen (file, "r");
+  free (file);
+
+  if (f == NULL) {
+    gprint (GP_ERR, "No help for: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  while (scan_line (f, buff) != EOF)
+    gprint (GP_LOG, "%s\n", buff);
+
+  fclose (f);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/!
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/!	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/!	(revision 17422)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/?
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/?	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/?	(revision 17422)
@@ -0,0 +1,6 @@
+
+  exec (line)
+
+  perform a system call.  this can also be invoked with "!".  
+  don't use ! in a macro or input script as it is interpreted as a
+  comment character. 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Opihi
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Opihi	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Opihi	(revision 17422)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Shell
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Shell	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Shell	(revision 17422)
@@ -0,0 +1,5 @@
+
+
+    Opihi
+
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Variables
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Variables	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/Variables	(revision 17422)
@@ -0,0 +1,17 @@
+
+  Opihi shell variables (scalars):
+
+  The Opihi shell can have variables which are designated by a word
+following a dollar sign: $foo.  Certain Mana function create and set
+variables (ie, stats, cursor).  You can also set a variable to the
+result of an arithmetic expression or just a number like this:
+
+  $foo = 10.0
+  $bar = 5*$foo + 0.6
+
+Mana will replace the variables on a line before executing a command.
+See also "Math" for more description of arithmetic operations.
+
+  See Also:  Math
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/break
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/break	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/break	(revision 17422)
@@ -0,0 +1,10 @@
+
+  break [-auto on/off]
+
+  halts macro, if, or for processes and returns to the basic prompt.
+
+  -auto: enables or disables DVO from automatically breaking
+   when it encounters an error
+
+  See also: macro, if, for
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/cd
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/cd	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/cd	(revision 17422)
@@ -0,0 +1,9 @@
+
+   cd (path)
+
+   change current working directory. 
+
+   Note that Kii or Kapa will be launched in the original 
+   directory, which means PS files created by Kii or Kapa 
+   will land in that directory (perhaps an unexpected result).
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/continue
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/continue	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/continue	(revision 17422)
@@ -0,0 +1,4 @@
+
+   continue
+
+   Return to the start of a for-loop.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/date
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/date	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/date	(revision 17422)
@@ -0,0 +1,38 @@
+
+  date [-var variable] [-seconds] [-reftime seconds]
+
+  Return the date and place it into (variable).
+
+  Default: print to window
+
+* by itself, just echoes the date & time
+
+* to set a variable with the date, add -var name, where 'name' is the
+  name of your variable.  by default, this is a human readable date,
+  eg Wed Mar 12 08:30:11 2008
+
+* to calculate time differences, add the -seconds flag, and the value
+  will be in seconds since 1970 Jan 01 00:00:00.  NOTE: There is a bug
+  in that the variable is represented as a float, and the value is now
+  > 1.2e9 seconds.  The difference is thus not very accurate.  To get
+  around this, use the -reftime feature to report the seconds since a
+  more recent date.  A value of something like 1205340000 works at the
+  moment.
+
+examples:
+
+  dvo: date
+  Wed Mar 12 08:30:11 2008
+
+  dvo: date -var foo -seconds
+  dvo: echo $foo
+  1205346664
+
+  dvo: date -var foo -seconds -reftime 1205340000
+  dvo: echo $foo
+  6700
+  dvo: date -var bar -seconds -reftime 1205340000
+  dvo: echo $bar
+  6708
+  dvo: echo {$bar - $foo}
+  8
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/echo
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/echo	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/echo	(revision 17422)
@@ -0,0 +1,10 @@
+
+  echo [anything]
+
+  prints the rest of the line, after variables and math expressions
+are parsed.  
+
+
+  See Also:  Opihi, math
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/exec
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/exec	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/exec	(revision 17422)
@@ -0,0 +1,6 @@
+
+   exec (shell command)
+
+   Allows a shell command to be run while running DVO
+   On the command line, this can also be done by placing a "!" before the shell
+    command
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/file
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/file	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/file	(revision 17422)
@@ -0,0 +1,9 @@
+
+   file (filename) (variable)
+
+   Checks to see if a file exists
+
+   If a variable is specified, then it is set to 1 if the file is present
+    and set to 0 if it is not.
+
+   Default: output printed to the window
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/for
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/for	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/for	(revision 17422)
@@ -0,0 +1,19 @@
+
+   for var (start) (stop) [delta]
+
+    perform a loop, with $var as the loop variable. 
+    the value of $var runs from (start) to (stop) inclusive, 
+    and is incremented by [delta] (1 by default).
+
+    The loop commands are entered until the appropriate "end" is typed;
+    if there are nested loops, macro definitions, or logical blocks,
+    the correct "end" is used!
+
+    for may be used at the command line, in a macro, or in an input
+    file with no ill effects.
+
+    the variable $var may be accessed during the loop execution.  if
+    the variable $var is assigned a value beyond (stop) during the
+    loop, the loop execution will end.
+
+    See also: macro, if
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/fprintf
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/fprintf	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/fprintf	(revision 17422)
@@ -0,0 +1,15 @@
+
+   fprintf ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/getchr
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/getchr	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/getchr	(revision 17422)
@@ -0,0 +1,8 @@
+
+   getchr (string) (char) [variable]
+
+   Return the index of the string in the position of the specified character
+
+   Only the first instance of a character is returned.
+
+   If not found, the variable is set to -1.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/help
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/help	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/help	(revision 17422)
@@ -0,0 +1,7 @@
+
+   help (function)
+
+   print the contents of the help file.  
+   typing "help" without an argument will list the available help
+   files.  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/if
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/if	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/if	(revision 17422)
@@ -0,0 +1,22 @@
+
+   if (logic)
+     (commands)
+   end
+
+   or:
+
+   if (logic)
+     (commands)
+   else 
+     (commnds)
+   end
+
+   logical block.  The commands are performed subject to the logical
+   condition.  The syntax is simplified C:  
+   examples: (x = 6), (x < 6) , (x ! 6), ((x = 6) | (x > 10))
+
+   there are no delimiter characters.
+
+   See also: for, macro
+
+   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/input
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/input	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/input	(revision 17422)
@@ -0,0 +1,15 @@
+
+   input (filename)
+
+   execute a series of commands given in the file.
+
+   Two special examples:  
+   ~/.manarc is loaded on startup, which give the user a place to
+   define personal macros, or interesting variables, etc.
+
+   If mana is called in the form "mana file", the file is loaded
+   (after .manarc) before the user is given the command-line prompt.
+   This can be used to invoke mana in a shell script, but care should
+   be taken in a shell-script that the file ends with the word "exit"
+   or "quit", or strange things may happen...
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/list
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/list	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/list	(revision 17422)
@@ -0,0 +1,23 @@
+
+   list (name) (string1) (string2) ... end
+   OR
+   list (name) -split (string1) (string2) ...
+   OR
+   list (name) -x "(ls command)"
+   OR
+   list (name) -x "cat (file)" 
+
+   Create a list of strings that are zero indexed.
+
+   $(name):n = the number of elements in the list (name)
+
+   $(name):5 = the 6th element of the list (name)
+
+   -split option: Turn a list of strings (separated by white spaces)
+     into a list
+
+   -x option: turn an ls command or file into a list (one list element per
+     line in the file + 1 white space)
+
+   ***Note: the list ... end version of the command does not currently work
+             within macros
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/local
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/local	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/local	(revision 17422)
@@ -0,0 +1,7 @@
+
+   local (var)
+
+   Creates a variable that is accessible only within the macro
+    in which it is created.
+
+   Variable value is listed as (macro).(var) using the "??" command
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/macro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/macro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/macro	(revision 17422)
@@ -0,0 +1,11 @@
+
+   macro (name)
+
+   create a macro.  the commands are entered until the word 'end' is
+   reached.  nested if, for, and macro definitions use the
+   appropriately matched occurances of the 'end'.
+
+   Within a macro, the command-line arguments are refered to as $1,
+   $2, etc.  The number of command-line arguments is given by $0.
+
+   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/memory
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/memory	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/memory	(revision 17422)
@@ -0,0 +1,12 @@
+
+  memory [x]
+
+  list the currently used memory.  This gives a (possibly very) long
+  listing of the size of every allocated piece of memory.  Placing
+  anything after the word "memory" gives a summary also.  This is
+  useful to see just how close you are to filling your computer's
+  entire memory (Does not include memory allocated to Kii!)
+
+  See Also: buffers
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/output
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/output	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/output	(revision 17422)
@@ -0,0 +1,10 @@
+
+  output (filename or "stdout")
+
+  redirect the output to a file (concat to the end of the file).  This
+  can also be done with the UNIX ">".  However, this command is useful
+  to send information to a file for a part of a Mana session.  For
+  example, you can save results of a "stats" command.
+
+  See Also: input, stats
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/pwd
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/pwd	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/pwd	(revision 17422)
@@ -0,0 +1,7 @@
+
+   pwd -var (variable)
+
+   print current working directory
+
+   Using -var will place the output into the specified variable.
+   Default: print to window
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/quit
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/quit	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/quit	(revision 17422)
@@ -0,0 +1,4 @@
+
+   quit
+
+   Exit from DVO
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/scan
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/scan	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/scan	(revision 17422)
@@ -0,0 +1,8 @@
+
+   scan (filename) (variable) [Nline]
+
+   Place the Nth line of the file into the named variable
+
+   Default N value: 1 (the 1st line)
+
+   The variable recieves the line + 1 white space
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sleep
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sleep	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sleep	(revision 17422)
@@ -0,0 +1,4 @@
+
+   sleep (N)
+
+   Wait for (N) seconds before continuing
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sprintf
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sprintf	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/sprintf	(revision 17422)
@@ -0,0 +1,17 @@
+
+   sprintf (var) ("format codes") (value1 value2 ...)
+
+   Prints output according to a specified format
+
+   Works same as fprintf, except it places the output as a string into (var)
+
+   Fomat codes must match the order and number of values.
+
+   FORMAT CODES:
+    %x.yf : floating point number with x total places and y places after
+             the decimal point; further decimal places are rounded
+    %x.ye : number in scientific notation with x places and y places after
+             the decimal point; further decimal places are rounded
+    %xs : ASCII output with x places
+    %xd : Integers with x places
+    %x : Express number in hexidesimal
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strlen
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strlen	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strlen	(revision 17422)
@@ -0,0 +1,10 @@
+
+   strlen (string) [var]
+
+   Determines the number of characters (plus white spaces) in a string
+    and places it into var
+
+   Default: print to window
+
+   Note: If the string has white spaces, you need to place it within
+    double-quotes.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strpop
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strpop	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/strpop	(revision 17422)
@@ -0,0 +1,12 @@
+
+   strpop (string) [var]
+
+   Pops part of a string or a list element that can be placed
+    into [var]
+
+   Default: print to window
+
+   The output is the first set of characters bordered by white space
+
+   ***Note: Both (string) and [var] must be entered without the "$"
+    variable identifier for the program to work properly.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/substr
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/substr	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/substr	(revision 17422)
@@ -0,0 +1,11 @@
+
+   substr (string) (index) (length) [var]
+
+   Returns part of a string starting at the position (index)
+    and of size (length), which can then be placed into [var]
+
+   Default: print to window
+
+   The sub-string is not removed from the original string
+
+   ***Note: Character positions in strings are zero-indexed
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/usleep
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/usleep	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/usleep	(revision 17422)
@@ -0,0 +1,4 @@
+
+   usleep (N)
+
+   Wait for (N) microseconds before continuing
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/wait
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/wait	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/wait	(revision 17422)
@@ -0,0 +1,5 @@
+
+  wait [anything]
+
+  wait echos the rest of the line and waits for <return> to be typed.
+Very useful for pausing the processing of an input file or a macro.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/which
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/which	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/which	(revision 17422)
@@ -0,0 +1,5 @@
+
+   which (command)
+
+   give a short summary info line on a command
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/while
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/while	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/help/while	(revision 17422)
@@ -0,0 +1,5 @@
+
+   while (condition) ... end
+
+   Perform a set of actions until the condition is true
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/init.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "basic.h"
+
+int config          PROTO((int, char **));
+int exec_sleep      PROTO((int, char **));
+int exec_usleep     PROTO((int, char **));
+int cd              PROTO((int, char **));
+int date            PROTO((int, char **));
+int dirname_opihi   PROTO((int, char **));
+int echo            PROTO((int, char **));
+int exec_last       PROTO((int, char **));
+int exec_next       PROTO((int, char **));
+int exec_break      PROTO((int, char **));
+int file            PROTO((int, char **));
+int getchr_func     PROTO((int, char **));
+int help            PROTO((int, char **));
+int input           PROTO((int, char **));
+int list            PROTO((int, char **));
+int list_help       PROTO((int, char **));
+int list_vars       PROTO((int, char **));
+int local           PROTO((int, char **)); /* data? */
+int macro           PROTO((int, char **));
+int memory          PROTO((int, char **));
+int mkdir_opihi     PROTO((int, char **));
+int module          PROTO((int, char **));
+int output          PROTO((int, char **));
+int pwd             PROTO((int, char **));
+int quit            PROTO((int, char **));
+int run_for         PROTO((int, char **));
+int run_if          PROTO((int, char **));
+int run_while       PROTO((int, char **));
+int scan            PROTO((int, char **));
+int shell           PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int fprintf_opihi   PROTO((int, char **));
+int strlen_func     PROTO((int, char **));
+int substr_func     PROTO((int, char **));
+int strpop          PROTO((int, char **));
+int strsub          PROTO((int, char **));
+int wait_func       PROTO((int, char **));
+int which           PROTO((int, char **));
+
+/** mapping of the command names to command functions **/
+static Command cmds[] = {  
+  {1, "config",        config,             "(re)load config file?"},
+  {1, "sleep",         exec_sleep,         "sleep for N seconds"},
+  {1, "usleep",        exec_usleep,        "sleep for N microseconds"},
+  {1, "cd",            cd,                 "change directory"},
+  {1, "date",          date,               "get current date"},
+  {1, "dirname",       dirname_opihi,      "built-in dirname function"},
+  {1, "echo",          echo,               "type this line *"},
+  {1, "break",         exec_break,         "escape from function *"},
+  {1, "continue",      exec_next,          "next loop iteration"},
+  {1, "next",          exec_next,          "next loop iteration"},
+  {1, "last",          exec_last,          "last loop iteration"},
+  {1, "return",        exec_last,          "exit from macro"},
+  {1, "file",          file,               "test file existence"},
+  {1, "getchr",        getchr_func,        "find character in string"},
+  {1, "help",          help,               "get help on a function *"},
+  {1, "input",         input,              "read command lines from a file *"},
+  {1, "list",          list,               "get variable list"},
+  {1, "?",             list_help,          "list commands *"},
+  {1, "??",            list_vars,          "list variables *"},
+  {1, "local",         local,              "define local variables"},
+  {1, "macro",         macro,              "deal with the macros *"}, 
+  {1, "memory",        memory,             "long listing of the allocated memory"},
+  {1, "mkdir",         mkdir_opihi,        "built-in mkdir command"},
+  {1, "module",        module,             "load script file from the modules directories"},
+  {1, "output",        output,             "redirect output to file"},
+  {1, "pwd",           pwd,                "print current working directory"},
+  {1, "exit",          quit,               "exit program *"}, 
+  {1, "quit",          quit,               "exit program *"},
+  {1, "for",           run_for,            "for loop"}, 
+  {1, "if",            run_if,             "logical cases *"}, 
+  {1, "while",         run_while,          "while loop"}, 
+  {1, "scan",          scan,               "scan line from keyboard or file to variable *"},
+  {1, "!",             shell,              "system call"},
+  {1, "exec",          shell,              "system call"},
+  {1, "sprintf",       sprintf_opihi,      "formatted print to a variable"},
+  {1, "fprintf",       fprintf_opihi,      "formatted print to standard output"},
+  {1, "strlen",        strlen_func,        "string length"},
+  {1, "substr",        substr_func,        "substring"},
+  {1, "strpop",        strpop,             "pop a string"},
+  {1, "strsub",        strsub,             "replace instances of a key in a string"},
+  {1, "wait",          wait_func,          "wait until return is typed"},
+  {1, "which",         which,              "show command *"}
+};
+
+void InitBasic () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  
+}
+
+void InitBasic_PantasksClient () {
+  
+  int i;
+
+  InitCommands ();
+  InitMacros ();
+  InitBuffers ();
+  InitVectors ();
+  InitVariables ();
+  InitLists ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    if (!strcmp (cmds[i].name, "quit")) goto valid;
+    if (!strcmp (cmds[i].name, "exit")) goto valid;
+    if (!strcmp (cmds[i].name, "exec")) goto valid;
+    if (!strcmp (cmds[i].name, "!")) goto valid;
+    continue;
+
+  valid:
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeBasic () {
+
+  FreeCommands ();
+  FreeMacros ();
+  FreeBuffers ();
+  FreeVectors ();
+  FreeVariables ();
+  FreeLists ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/input.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/input.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/input.c	(revision 17422)
@@ -0,0 +1,53 @@
+# include "basic.h"
+# define D_NLINES 100
+
+int input (int argc, char **argv) {
+  
+  int i, NLINES, status;
+  FILE *infile;
+  Macro inlist;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: input <filename>\n");
+    return (FALSE);
+  }
+
+  infile = fopen (argv[1], "r");
+  if (infile == NULL) {
+    gprint (GP_ERR, "no file %s\n", argv[1]); 
+    return (FALSE);
+  }
+
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 1024);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 1024);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list.c	(revision 17422)
@@ -0,0 +1,201 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int list (int argc, char **argv) {
+
+  int ThisList, depth, i, done, found;
+  char *input, line[1024];
+  int N, Nbytes, NBYTES, Nread, status;
+  int RunCommand;
+  char *A, *B, *val, *Cmd;
+  FILE *f;
+
+  Cmd = NULL;
+  RunCommand = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Cmd = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    RunCommand = TRUE;
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-split"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -split (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+
+    return (TRUE);
+  }
+
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-add"))) {
+    if (argc == 3) {
+      gprint (GP_ERR, "USAGE: list (root) -add (word) (word) ...\n");
+      return (FALSE);
+    }
+    
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < argc - 3; i++) {
+      sprintf (line, "%s:%d", argv[1], N + i);
+      set_str_variable (line, argv[i+3]);
+    }
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, N + i);
+
+    return (TRUE);
+  }
+
+  // remove the single named entry from the list (finds entry with given name, reduces list length by one)
+  // return an error if -add is given with no other args
+  if ((argc > 2) && (!strcmp (argv[2], "-del"))) {
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: list (root) -del (word)\n");
+      return (FALSE);
+    }
+    
+    int j;
+    char *value, *next_value;
+    char line2[1024];
+
+    sprintf (line, "%s:n", argv[1]);
+    N = get_int_variable (line, &found);
+    for (i = 0; i < N; i++) {
+      sprintf (line, "%s:%d", argv[1], i);
+      value = get_variable (line);
+      if (value == NULL) continue;
+      if (!strcmp (value, argv[3])) {
+	free (value);
+	for (j = i + 1; j < N; j++) {
+	  sprintf (line2, "%s:%d", argv[1], j);
+	  next_value = get_variable (line2);
+	  set_str_variable (line, next_value);
+	  strcpy (line, line2);
+	}
+	DeleteNamedScalar (line);
+	sprintf (line2, "%s:n", argv[1]);
+	set_int_variable (line2, N - 1);
+	return (TRUE);
+      }
+      free (value);
+    }      
+    gprint (GP_ERR, "value %s not found in list\n", argv[3]);
+    return (FALSE);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: list (root)                : supply list data, terminate with 'END'\n");
+    gprint (GP_ERR, "USAGE: list (root) -x (command)   : create list from shell output\n");
+    gprint (GP_ERR, "USAGE: list (root) -split (words) : create list from words\n");
+    gprint (GP_ERR, "USAGE: list (root) -add (words)   : extend a list\n");
+    return (FALSE);
+  }
+
+  if (RunCommand) {
+    
+    /* val will hold the result */
+    NBYTES = 1024;
+    ALLOCATE (val, char, NBYTES);
+    
+    /* need to loop until command produces no more output, 
+       REALLOCATING as needed. */
+    f = popen (Cmd, "r");
+    done = FALSE;
+    Nbytes = 0;
+    while (!done) {
+      Nread = fread (&val[Nbytes], 1, 1023, f);
+      if (Nread < 0) { 
+	gprint (GP_ERR, "error reading from command\n");
+	done = TRUE;
+      }
+      if (Nread > 0) {
+	Nbytes += Nread;
+	NBYTES = 1024 + Nbytes;
+	REALLOCATE (val, char, NBYTES);
+      }
+      if (Nread == 0) {
+	done = TRUE;
+      }
+      
+    }
+    val[Nbytes] = 0;
+    status = pclose (f);
+    free (Cmd);
+    
+    if (status) {
+      gprint (GP_ERR, "warning: exit status of command %d\n", status);
+    }
+      
+    A = B = val;
+    for (i = 0; B != (char *) NULL;) {
+      while (isspace (*A) && (*A != 0)) A++;
+      B = strchr (A, '\n');
+      if (B != (char *) NULL) { *B = 0; }
+      if (*A != 0) {
+	sprintf (line, "%s:%d", argv[1], i);
+	set_str_variable (line, A);
+	A = B + 1;
+	i++;
+      }
+    }      
+    free (val);
+    
+    sprintf (line, "%s:n", argv[1]);
+    set_int_variable (line, i);
+    return (TRUE);
+  }
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) {
+      input = readline (prompt);
+    } else {
+      input = get_next_listentry (ThisList);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end list with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in list\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", loop is done */
+	sprintf (line, "%s:n", argv[1]);
+	set_int_variable (line, i);
+	free (input);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      sprintf (line, "%s:%d", argv[1], i);
+      set_str_variable (line, input);
+      free (input);
+      i++;
+   }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_help.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_help.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_help.c	(revision 17422)
@@ -0,0 +1,24 @@
+# include "basic.h"
+
+int list_help (int argc, char **argv) {
+
+  FILE *f;
+  int fd;
+  char filename[128], line[256];
+
+  sprintf (filename, "/tmp/status.XXXXXX");
+  if ((fd = mkstemp (filename)) == -1) {
+    gprint (GP_ERR, "error opening output\n");
+    return (FALSE);
+  }
+  f = fdopen (fd, "w");
+  if (f == (FILE *) NULL) f = stdout;
+  print_commands (f);
+  if (f != stdout) {
+    fclose (f);
+    sprintf (line, "more %s", filename);
+    system (line);
+  }
+  unlink (filename);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_vars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_vars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/list_vars.c	(revision 17422)
@@ -0,0 +1,8 @@
+# include "basic.h"
+
+int list_vars (int argc, char **argv) {
+
+  ListVariables ();
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/local.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/local.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/local.c	(revision 17422)
@@ -0,0 +1,33 @@
+# include "basic.h"
+
+int local (int argc, char **argv) {
+
+  int i, N, STATIC;
+  char *p;
+
+  /* create a variable named MacroDepth.argv[1] */
+
+  STATIC = FALSE;
+  if ((N = get_argument (argc, argv, "-static"))) {
+    remove_argument (N, &argc, argv);
+    STATIC = TRUE;
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: local (variable)\n");
+    return (FALSE);
+  }
+  
+  for (i = 1; i < argc; i++) {
+    if (STATIC) {
+      p = get_local_variable_ptr (argv[i]);
+      if (p == NULL) {
+	set_local_variable (argv[i], "NULL");
+      }
+    } else {
+      set_local_variable (argv[i], "NULL");
+    }      
+  }
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/macro.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/macro.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/macro.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "basic.h"
+
+int macro (int argc, char **argv) {
+
+  int status;
+  CommandF *cmd;
+
+  if ((argc != 2) && (argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: macro (cmd)\n");
+    gprint (GP_ERR, "  (cmd) can be one of:\n");
+    gprint (GP_ERR, "    (name)         -- create macro (name)\n");
+    gprint (GP_ERR, "    create (name)  -- create macro (name)\n");
+    gprint (GP_ERR, "    delete (name)  -- delete macro (name)\n");
+    gprint (GP_ERR, "    list   (name)  -- list macro (name)\n");
+    gprint (GP_ERR, "    edit   (name)  -- edit macro (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    read   (name)  -- read macro(s) from file (name) <not working yet!> *\n");
+    gprint (GP_ERR, "    write  (name)  -- write macro (name) to a file <not working yet!> *\n");
+    return (FALSE);
+  }
+
+  cmd = find_macro_command (argv[1]);
+  if (cmd != NULL) {
+    status = (*cmd) (argc - 1, argv + 1);
+  } else {
+    /* sub-command was not found, pass argv[1..N] to macro_create */
+    status = macro_create (argc, argv);
+  }
+
+  return (status);
+}
+
+/* macro is called with the command "macro".  
+   the command line word "macro" is meant to be followed the one of several 
+   possible options:
+   
+   macro create
+   macro delete
+   macro list
+   macro edit
+   macro read
+   macro write
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/memory.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/memory.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/memory.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "basic.h"
+
+// XXX add a warning if ohana_memory is not compiled in 
+int memory (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "all")) {
+    ohana_memdump (1);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "leaks")) {
+    ohana_memdump (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "check")) {
+    ohana_memcheck (0);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "checkfree")) {
+    ohana_memcheck (1);
+    return (TRUE);
+  }
+  if (!strncasecmp ("variables", argv[1], strlen(argv[1]))) {
+    ListVariables ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("vectors", argv[1], strlen(argv[1]))) {
+    ListVectors ();
+    return (TRUE);
+  }
+  if (!strncasecmp ("buffers", argv[1], strlen(argv[1]))) {
+    PrintBuffers (0);
+    return (TRUE);
+  }
+  if (!strncasecmp ("macros", argv[1], strlen(argv[1]))) {
+    ListMacros();
+    return (TRUE);
+  }
+  if (!strncasecmp ("commands", argv[1], strlen(argv[1]))) {
+    print_commands (stderr);
+    return (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: memory (all/leaks)\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/mkdir.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/mkdir.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/mkdir.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "basic.h"
+
+int mkdir_opihi (int argc, char **argv) {
+
+  int mode, status;
+  struct stat fstats;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: mkdir (path)\n");
+    return (FALSE);
+  }
+
+  mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
+
+  status = stat (argv[1], &fstats);
+  if (!status) {
+    // argv[1] exists, is it a directory?
+    if (!S_ISDIR(fstats.st_mode)) {
+      gprint (GP_ERR, "cannot create directory %s: is an existing file\n", argv[1]);
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  status = mkdirhier (argv[1], mode);
+  if (status == -1) {
+    gprint (GP_ERR, "cannot create directory %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+// XXX need to add mode option
+// XXX need to respect umask (need umask command?)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/module.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/module.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/module.c	(revision 17422)
@@ -0,0 +1,77 @@
+# include "basic.h"
+# define D_NLINES 100
+
+/* module loads an opihi script files from the installed module tree */
+int module (int argc, char **argv) {
+  
+  int i, NLINES, Nmodules, Nbytes, status;
+  Macro inlist;
+  char modname[16], *modpath, *filename;
+  FILE *infile = NULL;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: module <filename>\n");
+    return (FALSE);
+  }
+
+  Nmodules = get_int_variable ("MODULES:n", &status);
+  if (!status) {
+    gprint (GP_ERR, "MODULES list not found\n");
+    return (FALSE);
+  }
+
+  /* search for requested file in MODULES:0 - MODULES:n */
+  for (i = 0; i < Nmodules; i++) {
+    snprintf (modname, 16, "MODULES:%d", i);
+    modpath = get_variable (modname);
+    if (modpath == NULL) {
+      gprint (GP_ERR, "MODULES list element %d not found\n", i);
+      return (FALSE);
+    }
+
+    Nbytes = strlen(modpath) + strlen(argv[1]) + 2;
+    ALLOCATE (filename, char, Nbytes);
+    snprintf (filename, Nbytes, "%s/%s", modpath, argv[1]);
+    
+    infile = fopen (filename, "r");
+    free (filename);
+
+    if (infile != NULL) break;
+  }
+  if (infile == NULL) {
+    gprint (GP_ERR, "module %s not found\n", argv[1]); 
+    return (FALSE);
+  }
+    
+  /* read file into the current list */
+  NLINES = D_NLINES;
+  ALLOCATE (inlist.line, char *, NLINES);
+  ALLOCATE (inlist.line[0], char, 1024);
+  for (i = 0; (scan_line (infile, inlist.line[i]) != EOF);) {
+    stripwhite (inlist.line[i]);
+    if (inlist.line[i][0] == 0) continue;
+    if (inlist.line[i][0] == '#') continue;
+    if (inlist.line[i][0] == '!') continue;
+
+    REALLOCATE (inlist.line[i], char, strlen(inlist.line[i]) + 1);
+    if (i == NLINES - 1) {
+      NLINES += D_NLINES;
+      REALLOCATE (inlist.line, char *, NLINES)
+    }
+    i++;
+    ALLOCATE (inlist.line[i], char, 1024);
+  }
+  inlist.Nlines = i;
+  fclose (infile);
+
+  /* process this list */
+  status = exec_loop (&inlist);
+
+  /* cleanup list */
+  for (i = 0; i < inlist.Nlines; i++) {
+    free (inlist.line[i]);
+  }
+  free (inlist.line[i]); /* note that we always alloc one extra line */
+  free (inlist.line);
+  return (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/output.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/output.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/output.c	(revision 17422)
@@ -0,0 +1,58 @@
+# include "basic.h"
+
+int output (int argc, char **argv) {
+  
+  int N, Noutput;
+  gpDest dest;
+  IOBuffer *buffer;
+  char *output, *current;
+
+  dest = GP_LOG;
+  if ((N = get_argument (argc, argv, "-err"))) {
+    dest = GP_ERR;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-current"))) {
+    current = gprintGetName (dest);
+    remove_argument (N, &argc, argv);
+    set_str_variable (argv[N], current);
+    remove_argument (N, &argc, argv);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-buffer"))) {
+    remove_argument (N, &argc, argv);
+    gprintSetBuffer (dest);
+    return (TRUE);
+  }
+    
+  /* set the output target and dump the current buffer there */
+  if ((N = get_argument (argc, argv, "-dump"))) {
+    buffer = gprintGetBuffer (dest);
+    if (buffer == NULL) return (FALSE);
+
+    /* save the current buffer contents */
+    Noutput = buffer[0].Nbuffer;
+    ALLOCATE (output, char, Noutput);
+    memcpy (output, buffer[0].buffer, Noutput);
+    
+    /* set the output target to the specified name */
+    remove_argument (N, &argc, argv);
+    gprintSetFileAllThreads (dest, argv[N]);
+    remove_argument (N, &argc, argv);
+
+    /* send the output to the appropriate destination */
+    gwrite (output, 1, Noutput, dest);
+    free (output);
+    return (TRUE);
+  }
+    
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: output <filename> [-err] [-buffer] [-current var] [-dump filename]\n");
+    return (FALSE);
+  }
+
+  gprintSetFileAllThreads (dest, argv[1]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/quit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/quit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/quit.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int quit (int argc, char **argv) {
+
+  int state;
+
+  cleanup ();
+
+  state = 0;
+  if (argc > 1) {
+    state = atof (argv[1]);
+  } 
+
+// the libedit version of readline does not support an incremental write to history file
+# ifndef RL_READLINE_VERSION
+  history = get_variable("HISTORY");
+  if (history != NULL) write_history (history);
+# endif
+
+  exit (state);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_for.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_for.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_for.c	(revision 17422)
@@ -0,0 +1,127 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_for (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, found, NLINES, j;
+  double start, end, delta;
+  char *input;
+  double value, sign;
+  Macro loop;
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: for (var) (start) (stop) [delta] -- terminate with 'END'\n");
+    return (FALSE);
+  }
+
+  start = atof (argv[2]);
+  end   = atof (argv[3]);
+  delta = 1.0;
+  if (argc == 5) delta = atof (argv[4]);
+  sign = SIGN(delta);
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	free (input);
+	break;
+      }
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  status = TRUE;
+  interrupt = FALSE;
+  for (value = start; (sign*value < sign*end) && !interrupt; value += delta) {
+    if ((int)value == value) 
+      set_int_variable (argv[1], (int) value);
+    else 
+      set_variable (argv[1], value);
+    status = exec_loop (&loop);
+    value = get_double_variable (argv[1], &found);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/*
+  If we are entering at the keyboard (ThisList == 0), use readline.
+  Otherwise, read from the current list, remove list lines.
+  execute when we hit the final "END" (the true END -- we count macro defines!!) 
+*/
+     
+/* while processing the loop, the loop status variables may be set
+   by the loop commands, or the loop may quite on a failed command,
+   setting the exec_loop return status to false. the loop status variables
+   are:
+   loop_next : stop this loop, but try another loop
+   loop_last : stop loop processing, but return true so external loop may continue
+   loop_break : stop loop processing, and return false so external loop will break
+   interrupt : external interrupt signal
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_if.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_if.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_if.c	(revision 17422)
@@ -0,0 +1,127 @@
+# include "basic.h"
+# define prompt    "if: "
+
+int run_if (int argc, char **argv) {
+
+  int ThisList, depth, done, status, InlineCommand;
+  int i, length, logic;
+  char *input, *val, *line;
+  int nloop, size;
+
+  InlineCommand = FALSE;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: if (conditional) : follow with commands, end with the word 'END'\n");
+    gprint (GP_ERR, "   OR: if (conditional) command\n");
+    return (FALSE);
+  }
+  if (argc > 2) {
+    InlineCommand = TRUE;
+  }
+
+  /* determine value of conditional expression */
+  val = dvomath (1, &argv[1], &size, 0);
+  if (val == NULL) {
+    gprint (GP_ERR, "syntax error in logic: %s\n", argv[1]);
+    return (FALSE);
+  }
+  logic = atof (val); /* is round-off error a danger? */ 
+  free (val);
+
+  if (InlineCommand) {
+    if (logic) {
+      /* re-build a command line from the remaining strings */
+      length = 0;
+      for (i = 2; i < argc; i++) {
+	length += strlen(argv[i]) + 1;
+      }
+      length++;
+      ALLOCATE (line, char, length);
+      memset (line, 0, length);
+      for (i = 2; i < argc; i++) {
+	if (i == 2) {
+	  strcpy (line, argv[i]);
+	} else {
+	  strcat (line, " ");
+	  strcat (line, argv[i]);
+	}
+      }
+      status = multicommand (line);
+      free (line);
+      return (status);
+    } else {
+      return (TRUE);
+    }
+  }    
+
+  /* read in if-list */
+  nloop = 0;
+  depth = 0;
+  ThisList = current_list_depth();
+
+  done = FALSE;
+  while (!done) {
+
+    nloop ++;
+    /* get the next line (from correct place) */
+    if (ThisList == 0) {
+      input = readline (prompt);
+    } else {
+      input = get_next_listentry (ThisList);
+    }
+
+    if ((ThisList == 0) && (input == NULL)) {
+      gprint (GP_ERR, "end if-block with 'END'\n");
+      continue;
+    }
+    if ((ThisList >  0) && (input == NULL)) {
+      gprint (GP_ERR, "missing 'END' in if-block\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for new macro, search for "end" statement */
+    if (!logic && is_list (input)) {
+      depth ++;
+      free (input);
+      continue;
+    }
+    
+    /* check for an "else", invert logic */
+    if ((depth == 0) && !strncasecmp (input, "ELSE", 4)) {
+      logic ^= TRUE;
+      free (input);
+      continue;
+    }
+
+    /* test for end of nested block -- if not nested, END refers to this if */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	/* we hit the last "END", if-block is done */
+	free (input);
+	return (TRUE);	
+      }
+      free (input);  /* a do-nothing line */
+      continue;
+    }
+
+    if (logic) {
+      if (*input) { 
+	status = multicommand (input);
+	if (ThisList == 0) add_history (input);
+	if (auto_break && !status) return (FALSE);
+      }
+    } 
+    free (input);
+  }
+  return (TRUE);
+}
+
+/*
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list, remove list lines.
+     End when we hit the final "END" (the true END -- we count macro defines!!) 
+     */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_while.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_while.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/run_while.c	(revision 17422)
@@ -0,0 +1,117 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_while (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, NLINES, j;
+  char *input, *val, *logic_line;
+  int logic, size;
+  Macro loop;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: while (condition)\n");
+    return (FALSE);
+  }
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == (char *) NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end 'while' loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced 'while' loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) break;
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  free (input);
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  // test the logic once before running the loop
+  logic_line = strcreate (argv[1]);
+  logic_line = expand_vars (logic_line);
+  val = dvomath (1, &logic_line, &size, 0);
+  free (logic_line);
+
+  // if we have a parse failure, return FALSE
+  if (val == NULL) return (FALSE);
+  logic = atof (val); /* warning: round-off error is a danger */
+  free (val);
+
+  /* execute for loop */
+  while (logic) { 
+    status = exec_loop (&loop);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+
+    logic_line = strcreate (argv[1]);
+    logic_line = expand_vars (logic_line);
+    val = dvomath (1, &logic_line, &size, 0);
+    free (logic_line);
+
+    logic = FALSE;
+    if (val) {
+      logic = atof (val); /* warning: round-off error is a danger */
+      free (val);
+    }
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/scan.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/scan.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/scan.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "basic.h"
+
+int scan (int argc, char **argv) {
+
+  int i, N, status;
+  char *line;
+  FILE *f;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: scan <filename> <var> [N]\n");
+    return (FALSE);
+  }
+
+  f = stdin;
+  if (strcmp (argv[1], "stdin")) {
+    f = fopen (argv[1], "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", argv[1]);
+      return (FALSE);
+    }
+  }
+  
+  ALLOCATE (line, char, 1024);
+  N = 1;
+  if (argc == 4) {
+    N = atof(argv[3]);
+    if (N < 1) {
+      gprint (GP_ERR, "scan: line numbers must start at 1\n");
+      return (FALSE);
+    }
+  }
+
+  for (i = 0; (i < N) && ((status = scan_line (f, line)) != EOF); i++);
+  if (i < N) {
+    set_str_variable (argv[2], "EOF");
+  } else {
+    set_str_variable (argv[2], line);
+  }
+  free (line);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+ 
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/shell.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/shell.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/shell.c	(revision 17422)
@@ -0,0 +1,129 @@
+# include "basic.h"
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+static char *defshell = "/bin/sh";
+static char *cmdflag = "-c";
+
+// XXX add the ability to exec without a separate shell
+// XXX add an option to modify the timeout
+
+int shell (int argc, char **argv) {
+  
+  int i, pid, N;
+  int exit_status;
+  int wait_status;
+  int result, length;
+  char **args, *shell;
+  struct timeval start, now;
+  float timeout;
+
+  timeout = 0;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    timeout = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  shell = getenv ("SHELL");
+  if (shell == NULL) shell = defshell;
+
+  // we are creating a command of the form /bin/sh -c argv[1] argv[2] etc, where
+  // the argv[1], etc elements are concatenated into a single string
+  ALLOCATE (args, char *, 4);
+  args[0] = shell;
+  args[1] = cmdflag;
+
+  length = 0;
+  for (i = 1; i < argc; i++) {
+    length += strlen(argv[i]) + 1;
+  }
+  
+  ALLOCATE (args[2], char, length);
+  args[2][0] = 0;
+  for (i = 1; i < argc; i++) {
+    strcat (args[2], argv[i]);
+    if (i < argc - 1) strcat (args[2], " ");
+  }
+  args[3] = NULL;
+
+  // send the commands to the shell specified in the env variable SHELL, or else /bin/sh
+
+  gettimeofday (&start, NULL);
+
+  // use execvp to enable a timeout on the system call 
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    execvp (shell, args);
+    exit (1);
+  }
+  free (args[2]);
+  free (args);
+  
+  // wait for process to finish or timeout
+  // loop forever if desired, but catch C-C and stop the process on interrupt
+  interrupt = FALSE;
+  while (!interrupt) {
+    result = waitpid (pid, &wait_status, WNOHANG);
+    switch (result) {
+      case -1:   // error on waitpid
+	switch (errno) {
+	  case ECHILD:
+	    gprint (GP_ERR, "unknown PID, not a child process: %d\n", pid);
+	    return (FALSE);
+	  default:
+	    gprint (GP_ERR, "unexpected response to waitpid: %d\n", result);
+	    abort();
+	}
+	break;
+
+      case 0:   // child not yet exited
+	usleep (10000);
+	if (timeout > 0.0) {
+	  gettimeofday (&now, NULL);
+	  if (DTIME(now, start) > timeout) {
+	    gprint (GP_ERR, "timeout on %s (pid %d)\n", argv[1], pid);
+	    return (FALSE);
+	  }
+	}
+	continue;
+
+      default:
+	if (result != pid) {
+	  gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	  abort();
+	}
+	if (WIFEXITED(wait_status)) {
+	  exit_status = WEXITSTATUS(wait_status);
+	  if (exit_status) {
+	    return FALSE;
+	  } else {
+	    return TRUE;
+	  }
+	}
+	if (WIFSIGNALED(wait_status)) {
+	  gprint (GP_ERR, "job %d exited on signal %d\n", pid, WTERMSIG(wait_status));
+	  return (FALSE);
+	}
+	if (WIFSTOPPED(wait_status)) {
+	  gprint (GP_ERR, "waitpid returns 'stopped' programming error\n");
+	  abort();
+	}
+    }
+  }
+  gprint (GP_ERR, "caught interrupt, killing %s (%d)\n", argv[1], pid);
+
+  // user hit interrupt: kill the process and return
+  kill (pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (pid, &wait_status, WNOHANG);
+  }
+  if (!result) {
+    gprint (GP_ERR, "trouble killing %s (pid %d)\n", argv[1], pid);
+  } else {
+    gprint (GP_ERR, "killed %s (pid %d)\n", argv[1], pid);
+  }
+
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sleep.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sleep.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sleep.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_sleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: sleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  sleep (i);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sprintf.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sprintf.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/sprintf.c	(revision 17422)
@@ -0,0 +1,83 @@
+# include "basic.h"
+# define NCHAR 1024
+
+// XXX this function uses fixed string lengths....
+/* convert line, tmp, fmt to dynamic strings? */
+
+int sprintf_opihi (int argc, char **argv) {
+
+  int i;
+  char line[NCHAR], tmp[NCHAR], fmt[NCHAR];
+  char *p1, *p2, *q;
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: sprintf var format value value ...\n");
+    return (FALSE);
+  }
+
+  q  = line;
+  bzero (line, NCHAR);
+
+  p1 = argv[2];
+  for (i = 3; i < argc; i++) {
+    bzero (tmp, NCHAR);
+    bzero (fmt, NCHAR);
+
+    /* find next format character */
+    p2 = strchr (p1, '%');
+    if (p2 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      return (FALSE);
+    }
+    if (strlen(q) + p2 - p1 > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, p1, p2-p1);
+    q = line + strlen(line);
+    
+    /* identify type (%NNNs %NNNNd %NNNNf) */
+    for (p1 = p2 + 1; (*p1 == '.') || (*p1 == '-') || (*p1 == '+') || (*p1 == ' ') || isdigit(*p1); p1++);
+    memcpy (fmt, p2, p1 - p2 + 1);
+    switch (*p1) {
+      case 'e':
+      case 'f':
+	sprintf (tmp, fmt, atof(argv[i]));
+	break;
+      case 's':
+	sprintf (tmp, fmt, argv[i]);
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	sprintf (tmp, fmt, atoi(argv[i]));
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,s,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    if (strlen(q) + strlen(tmp) > NCHAR) {
+      gprint (GP_ERR, "line too long");
+      return (FALSE);
+    }
+    memcpy (q, tmp, strlen(tmp));
+    q = line + strlen(line);
+    p1++;
+  }
+  p2 = strchr (p1, '%');
+  if (p2 != (char *) NULL) {
+    gprint (GP_ERR, "mismatch between format and values\n");
+    return (FALSE);
+  }
+  
+  p2 = p1 + strlen (p1);
+  if (strlen(q) + p2 - p1 > NCHAR) {
+    gprint (GP_ERR, "line too long");
+    return (FALSE);
+  }
+  memcpy (q, p1, p2-p1);
+  set_str_variable (argv[1], line);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strchr.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strchr.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strchr.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "basic.h"
+
+int getchr_func (int argc, char **argv) {
+
+  /* returns position of the first given character */ 
+  char *c;
+  int pos;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: strchr (string) (char) [var]\n");
+    return (FALSE);
+  }
+
+  c = strchr (argv[1], argv[2][0]);
+
+  if (c == (char *) NULL) {
+    pos = -1;
+  } else {
+    pos = c - argv[1];
+  }
+
+  if (argc == 5) {
+    set_variable (argv[3], pos);
+  } else {
+    gprint (GP_ERR, "%d\n", pos);
+  }
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strlen.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strlen.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strlen.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "basic.h"
+
+int strlen_func (int argc, char **argv) {
+
+  /* returns length of the given string */ 
+  int len;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strlen (string) [var]\n");
+    return (FALSE);
+  }
+
+  len = strlen (argv[1]);
+
+  if (argc == 3) {
+    set_int_variable (argv[2], len);
+  } else {
+    gprint (GP_ERR, "%d\n", len);
+  }
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strpop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strpop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strpop.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "basic.h"
+
+int strpop (int argc, char **argv) {
+
+  char *p, *q, *string;
+
+  if ((argc != 2) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: strpop (var) [out]\n");
+    return (FALSE);
+  }
+
+  /* string is a copy of the value on the variable stack */
+  string = get_variable (argv[1]);
+  if (string == NULL) return (FALSE);
+  
+  /* thisword is an allocated string */
+  p = thisword (string);
+
+  q = nextword (string);
+  if (q == NULL) {
+    set_str_variable (argv[1], "NULL");
+  } else {
+    set_str_variable (argv[1], q);
+  }
+  
+  if (argc == 3) {
+    set_str_variable (argv[2], p);
+  } else {
+    gprint (GP_LOG, "%s\n", p);
+  }
+
+  free (p);
+  free (string);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strsub.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strsub.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/strsub.c	(revision 17422)
@@ -0,0 +1,59 @@
+# include "basic.h"
+
+int strsub (int argc, char **argv) {
+
+  int N, Nkey, Noutput;
+  char *varName;
+  char *p, *q, *input, *output, *key, *value;
+
+  // clear all sections
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: strsub (string) (key) (value) [-var out]\n");
+    gprint (GP_ERR, "  replace instances of 'key' with the 'value'\n");
+    return (FALSE);
+  }
+
+  // input string 
+  input = argv[1];
+  
+  // find this in the string
+  key = argv[2];
+  Nkey = strlen(key);
+
+  // replace with this
+  value = argv[3];
+
+  Noutput = MAX (16, strlen(input));
+  ALLOCATE (output, char, Noutput);
+  memset (output, 0, Noutput);
+
+  // 0123456789
+  // wordKEYnew
+  // p = 0, q = 4, q-p = 4 -> p = 7
+
+  p = input;
+  while (*p && ((q = strstr (p, key)) != NULL)) {
+    output = opihi_append (output, &Noutput, p, q);
+    output = opihi_append (output, &Noutput, value, value + strlen(value));
+    p = q + Nkey;
+  }
+  if (*p) {
+    output = opihi_append (output, &Noutput, p, p + strlen(p));
+  }
+  
+  if (varName) {
+    set_str_variable (varName, output);
+  } else {
+    gprint (GP_LOG, "%s\n", output);
+  }
+
+  free (output);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/substr.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/substr.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/substr.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "basic.h"
+
+int substr_func (int argc, char **argv) {
+
+  int N1, N2, len;
+  char *c, *string;
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: substr (string) N1 N2 [var]\n");
+    return (FALSE);
+  }
+
+  N1 = atof (argv[2]);
+  N2 = atof (argv[3]);
+
+  len = strlen (argv[1]);
+  if ((N1 >= len) || (N1 + N2 > len)) {
+    c = (char *) NULL;
+  } else {
+    c = strncreate (&argv[1][N1], N2);
+  }
+
+  if (c == (char *) NULL) {
+    string = strcreate ("");
+  } else {
+    string = strcreate (c);
+  }
+
+  if (argc == 5) {
+    set_str_variable (argv[4], string);
+  } else {
+    gprint (GP_ERR, "%s\n", string);
+  }
+  free (c);
+  free (string);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/break.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/break.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/break.sh	(revision 17422)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that break will halt operation
+macro test1
+ $PASS = 1
+ break -auto off
+ for i 0 10
+   if ($i == 5)
+     break
+   end
+ end
+ if ($i != 5)
+   $PASS = 0
+ end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/cd.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/cd.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/cd.sh	(revision 17422)
@@ -0,0 +1,34 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that cd will go into a new directory
+macro test1
+ $PASS = 1
+ exec mkdir test.dir
+ output /dev/null
+ cd test.dir
+ exec touch foo.test
+ cd ..
+ output stdout
+ file test.dir/foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f test.dir/foo.test
+ exec rmdir test.dir
+end
+
+# test that pwd output is correct
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ pwd -var testdir
+ file $testdir\/foo.test exists
+ if ($exists != 1)
+  $PASS = 0
+ end
+ exec rm foo.test
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/config.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/config.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/config.sh	(revision 17422)
@@ -0,0 +1,14 @@
+
+list tests
+ test1
+end
+
+# test that config does not return an error
+macro test1
+ $PASS = 1
+ config
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/continue.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/continue.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/continue.sh	(revision 17422)
@@ -0,0 +1,18 @@
+
+list tests
+ test1
+end
+
+# test that continue skips within a loop
+macro test1
+ $PASS = 1
+ for i 0 10
+   if ($i > 5)
+      continue
+   end
+   $j = $i
+ end
+ if ($j != 5)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/date.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/date.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/date.sh	(revision 17422)
@@ -0,0 +1,33 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that date does not return an error
+macro test1
+ $PASS = 1
+ date -var var
+ if ($STATUS == 0)
+   $PASS = 0
+ end
+end
+
+# test that date constructs a reasonable sample date
+macro test2
+ $PASS = 1
+ date -var date1
+ $date2 = `date`
+ list w1 -split $date1
+ list w2 -split $date2
+ # check the first 3 entries (day, month, date)
+ if ($w1:0 != $w2:0)
+   $PASS = 0
+ end
+ if ($w1:1 != $w2:1)
+   $PASS = 0
+ end
+ if ($w1:2 != $w2:2)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/echo.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/echo.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/echo.sh	(revision 17422)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that echo actually echoes
+macro test1
+ $PASS = 1
+ exec rm -f test.dat
+ output test.dat
+ echo foobar
+ output stdout
+ $line = `cat test.dat`
+ # exec rm -f test.dat
+ if ($line != foobar)
+   $PASS = 0
+ end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/file.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/file.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/file.sh	(revision 17422)
@@ -0,0 +1,19 @@
+
+list tests
+ test1
+end
+
+# test that the file test function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/for.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/for.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/for.sh	(revision 17422)
@@ -0,0 +1,216 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ memtest1
+ memtest2
+ memtest3
+end
+
+# do we loop up correctly?
+macro test1
+
+  $PASS = 0
+
+  local i
+
+  for i 0 100
+  end    
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do we loop down correctly?
+macro test2
+
+  $PASS = 0
+
+  local i
+
+  for i 100 0 -1
+  end    
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# do we loop up in small steps correctly?
+macro test3
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 0 100 0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 99.9) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we loop down in small steps correctly?
+macro test4
+
+  $PASS = 0
+
+  local i N
+
+  $N = 0
+  for i 100 0 -0.1
+   $N = $N + 1
+  end    
+  
+ if (($i == 0.1) && ($N == 1000))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+   echo "N: $N"
+ end
+
+end
+
+# do we break from a loop correctly
+macro test5
+
+  $PASS = 0
+
+  break -auto off
+
+  local i N
+
+  $N = 0
+  for i 0 100
+   $N = $N + 1
+   if ($i == 30)
+     break
+   end
+  end    
+
+  $PASS = 1
+  
+  if (($i != 30) || ($N != 31))
+    $PASS = 0
+    echo "i: $i"
+    echo "N: $N"
+  end
+end
+
+# check memleaks
+macro memtest1
+
+ $PASS = 0
+
+ local i N
+
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks with many loop lines
+macro memtest2
+
+ $PASS = 0
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+  echo "test line in loop"
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks on break
+macro memtest3
+
+ $PASS = 0
+
+ local i N
+
+ break -auto off
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  for j 0 5
+    break
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+    echo "test line in loop"
+  end
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($i != 9999)
+   $PASS = 0
+   echo "break jumped outer loop: i = $i" 
+ end
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/fprintf.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/fprintf.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/fprintf.sh	(revision 17422)
@@ -0,0 +1,84 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+macro test1
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %03d" 50
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 050")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test2
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %6.3f" 123.45678
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 123.457")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test3
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %x" 32
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test 20")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+macro test4
+ exec rm -f test.dat
+ output test.dat
+ fprintf "test %10s" foobar
+ output stdout
+ $line = `cat test.dat`
+ if ("$line" == "test     foobar")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+# check for memory leaks
+macro test5
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 1000
+   fprintf "test %10s" foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo growth: {$endmem - $startmem}
+   echo kB/loop: {($endmem - $startmem)/1000}
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/getchr.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/getchr.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/getchr.sh	(revision 17422)
@@ -0,0 +1,23 @@
+
+list tests
+ test1
+ test2
+end
+
+# index test
+macro test1
+ $PASS = 1
+ getchr "a long string.string" . var
+ if ($var != 13)
+   $PASS = 0
+ end
+end
+
+# null test
+macro test2
+ $PASS = 1
+ getchr "a long string.string" x var
+ if ($var != -1)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/if.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/if.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/if.sh	(revision 17422)
@@ -0,0 +1,321 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13a
+ test13
+ test14
+end
+
+# basic logical test
+macro test1
+
+ local a
+
+ $a = 1
+
+ if ($a == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test2
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 1) && ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test3
+
+ local a b
+
+ $a = 1
+ $b = 5
+
+ if (($a == 1) && ($b < 10))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test4
+
+ local a
+
+ $a = 0
+
+ if ($a == 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test5
+
+ local a
+
+ $a = 1
+
+ if ($a > 0)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test6
+
+ local a
+
+ $a = 1
+
+ if ($a < 2)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test7
+
+ local a
+
+ $a = test
+
+ if ("$a" == "test")
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test8
+
+ local a
+
+ $a = foobar
+
+ if ("$a" == "test")
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test9
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 2))
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+end
+
+
+# basic logical test
+macro test10
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a == 0) || ($b == 1))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test11
+
+ local a b
+
+ $a = 1
+ $b = 2
+
+ if (($a <= 0) || ($b >= 3))
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# basic logical test
+macro test12
+
+ local a
+
+ $a = 1
+
+ if ($a != 1)
+   $PASS = 0
+ else
+   $PASS = 1
+ end
+end
+
+
+# check memleaks
+macro test13a
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ for i 0 1000
+   if ($a == 1)
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# check memleaks
+macro test13
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test14
+
+ local a b i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   if (($a == 1) && ($b == 2))
+    continue
+    echo "run"    
+   end
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
+
+# memory test using continue
+macro test15
+
+ local a b i N bool
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ $a = 1
+ $b = 2
+ for i 0 1000
+   $bool = (($a == 1) && ($b == 2))
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/list.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/list.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/list.sh	(revision 17422)
@@ -0,0 +1,174 @@
+list tests
+ test1
+ test2
+ test3
+ test4
+ memtest2
+ memtest3
+ memtest4
+end
+
+list check
+  this
+  is
+  a
+  list
+end
+
+# Does list work?
+macro test1
+ $PASS = 1
+ if ($check:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check:n"
+ end
+ if (("$check:0" != "this") || ("$check:1" != "is") || ("$check:2" != "a") || ("$check:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test split option
+macro test2
+ $PASS = 1
+ list check2 -split This is a list
+ if ($check2:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check2:n"
+ end
+ if (("$check2:0" != "This") || ("$check2:1" != "is") || ("$check2:2" != "a") || ("$check2:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x option for the ls command
+macro test3
+ $PASS = 1
+ list check3 -x "ls /dev/null"
+ if ($check3:n != 1)
+  $PASS = 0
+  echo "Number of list elements: $check3:n"
+ end
+ if ("$check3:0" != "/dev/null")
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+end
+
+# Test -x for files
+macro test4
+ $PASS = 1
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+ list check4 -x "cat list_test.txt"
+ if ($check4:n != 4)
+  $PASS = 0
+  echo "Number of list elements: $check4:n"
+ end
+ if (("$check4:0" != "This") || ("$check4:1" != "is") || ("$check4:2" != "a") || ("$check4:3" != "list"))
+  $PASS = 0
+  echo "List element does not return correctly!"
+ end
+ if ($PASS)
+  exec rm list_test.txt
+ end
+end
+
+# Memory test for list -split
+macro memtest2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list check2 -split This is a list
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check3 -x "ls /dev/null"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+end
+
+# Memory test for list -x
+macro memtest4
+
+ file list_test.txt fchk
+ if ($fchk)
+  exec rm list_test.txt
+ end
+ output list_test.txt
+ echo This
+ echo is
+ echo a
+ echo list
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  list check4 -x "cat list_test.txt"
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+
+ if ($PASS)
+  exec rm list_test.txt
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/local.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/local.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/local.sh	(revision 17422)
@@ -0,0 +1,51 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does local work?
+macro test1
+
+ $PASS = 1
+
+ local lvar
+
+ $lvar = 5
+
+ if ($?lvar != 1)
+  $PASS = 0
+  echo "Local variable failed to be created!"
+ end
+
+ if ($lvar != 5)
+  $PASS = 0
+  echo "Local variable value not assigned!"
+ end
+
+end
+
+
+# Memory test for local
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  local lvar2
+  $lvar2 = 9
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/macro.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/macro.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/macro.sh	(revision 17422)
@@ -0,0 +1,48 @@
+list tests
+ test_prep
+ test1
+ memtest1
+end
+
+# Is the macro working?
+macro test_prep
+ $test_var1 = check1
+ $test_var2 = check2
+ $var_count = $0
+end
+
+macro test1
+ $PASS = 1
+ $var_count = 0
+ $test_var1 = blank
+ $test_var2 = blank
+ test_prep var1 var2 var3
+ if ($var_count != 4)
+  $PASS = 0
+  echo "Number of parameters (should be 4): $var_count"
+ end
+ if (("$test_var1" != "check1") || ("$test_var2" != "check2"))
+  $PASS = 0
+  echo "Paramaters not assigned correctly!: $test_var1 $test_var2"
+ end
+end
+
+# Memory Test for macro
+macro memtest1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  test_prep
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/math.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/math.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/math.sh	(revision 17422)
@@ -0,0 +1,149 @@
+
+list tests
+ test1
+ test2
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+end
+
+# test subtraction
+macro test1
+
+  $PASS = 1
+  local i
+
+  exec rm -f tmp.txt
+  output tmp.txt
+  echo {99 - 98}
+  output stdout
+
+  $i = `cat tmp.txt` 
+ 
+ if ($i != 1)
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+# test addition, division, and multiplication
+macro test2
+
+ $PASS = 1
+ local a b c
+
+ $a = {2 + 4}
+ $b = {12 / 2}
+ $c = {2 * 3}
+
+ if (($a != 6) || ($b != 6) || ($c != 6))
+   $PASS = 0
+   echo "ALL VALUES NOT 6: a=$a\ b=$b\ c=$c\"
+ end
+
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo {99 - 98}
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem2
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99 - 98
+   $N = 1 + 1
+   $N = 2 * 2
+   $N = 22/7
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem3
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = 99
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set global)
+macro testmem4
+
+ $PASS = 1
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $N = word
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/module.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/module.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/module.sh	(revision 17422)
@@ -0,0 +1,16 @@
+
+list tests
+ test1
+end
+
+# Test if the $MODULES list is available
+macro test1
+
+ $PASS = 1
+
+ if ($?MODULES:0 != 1)
+  $PASS = 0
+  echo "Modules list not loaded!"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/output.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/output.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/output.sh	(revision 17422)
@@ -0,0 +1,63 @@
+
+list tests
+ test1
+ testmem1
+end
+
+# test subtraction
+macro test1
+
+ $PASS = 1
+
+ output testout.txt
+
+ file testout.txt fchk
+ output stdout
+ if ($fchk != 1)
+  $PASS = 0
+  echo "Output did not create test file!"
+ end
+
+ output testout.txt
+ echo "This is a test."
+ output stdout
+ $line = `cat testout.txt`
+ if ("$line" == "This is a test.")
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "Output: $line"
+ end
+
+ exec rm testout.txt
+
+end
+
+
+# check memleaks
+macro testmem1
+
+ $PASS = 1
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+  output testout.txt
+  echo "This is a test."
+  output stdout
+  exec rm testout.txt
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/scan.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/scan.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/scan.sh	(revision 17422)
@@ -0,0 +1,117 @@
+list tests
+ test1
+ memtest1
+ memtest2
+end
+
+# Does scan work?
+macro test1
+ $PASS = 1
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+ scan test_file.txt fscan
+ if ("$fscan" != "This")
+  $PASS = 0
+  echo "Default not working!"
+ end
+ scan test_file.txt fscan 4
+ if ("$fscan" != "test")
+  $PASS = 0
+  echo "Scan failure!"
+ end
+ if ($PASS)
+  exec rm test_file.txt
+ end
+end
+
+# Memory test for scan (default)
+macro memtest1
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
+
+
+# Memory test for scan (specified)
+macro memtest2
+
+ file test_file.txt fchk
+ if ($fchk)
+  exec rm test_file.txt
+ end
+ output test_file.txt
+ echo This
+ echo is
+ echo a
+ echo test
+ echo file
+ output stdout
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  scan test_file.txt fscan 5
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+ if ($PASS)
+  exec rm test_file.txt
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/shell.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/shell.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/shell.sh	(revision 17422)
@@ -0,0 +1,35 @@
+
+list tests
+ test1
+ test2
+end
+
+# test that a shell function works at all
+macro test1
+ $PASS = 1
+ exec touch foo.test
+ file foo.test exists
+ if ($exists != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ file foo.test exists
+ if ($exists != 0)
+   $PASS = 0
+ end
+end
+
+# test that the shell status is returned
+macro test2
+ $PASS = 1
+ exec touch foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 1)
+   $PASS = 0
+ end
+ exec rm -f foo.test
+ exec ls foo.test >& /dev/null
+ if ($STATUS != 0)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sleep.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sleep.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sleep.sh	(revision 17422)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does sleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ sleep 3
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sprintf.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sprintf.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/sprintf.sh	(revision 17422)
@@ -0,0 +1,43 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does sprintf work?
+macro test1
+ $PASS = 1
+
+ local test_var
+
+ sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+
+ if ("$test_var" != "  float 34.50 1.263e+07")
+  $PASS = 0
+ end
+
+end
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  sprint test_var "%7s %5.2f %9.3e" float 34.5 12630000
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strlen.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strlen.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strlen.sh	(revision 17422)
@@ -0,0 +1,47 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does strlen work?
+macro test1
+ 
+ $PASS = 1
+
+ $tstr = "Test string"
+
+ strlen $tstr len
+
+ if ($len != 11)
+  $PASS = 0
+  echo "Incorrect length: $len"
+ end
+
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ $tstr = "Test string"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  strlen $tstr len
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strpop.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strpop.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/strpop.sh	(revision 17422)
@@ -0,0 +1,105 @@
+list tests
+ test1
+ test2
+ memtest1
+ memtest2
+end
+
+# Does strpop work with variables?
+macro test1
+
+ $PASS = 1
+
+ $tstr = a test string
+
+ local a b c
+
+ strpop tstr a
+ strpop tstr b
+ strpop tstr c
+
+ if (("$a" != "a") || ("$b" != "test") || ("$c" != "string"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Does strpop work with lists?
+macro test2
+
+ $PASS = 1
+
+ list tlis -split list of strings
+
+ local a b c
+
+ strpop tlis:0 a
+ strpop tlis:1 b
+ strpop tlis:2 c
+
+ if (("$a" != "list") || ("$b" != "of") || ("$c" != "strings"))
+  $PASS = 0
+  echo "Incorrect value returned!"
+ end
+end
+
+
+# Memory test for variables
+macro memtest1
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $tstr = one two three
+  strlen tstr a
+  strlen tstr b
+  strlen tstr c
+  strlen tstr d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
+
+
+# Memory test for lists
+macro memtest2
+
+ local i tstr a b c d
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  list tstr -split one two three
+  strlen tstr:0 a
+  strlen tstr:1 b
+  strlen tstr:2 c
+  strlen tstr:0 d
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/substr.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/substr.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/substr.sh	(revision 17422)
@@ -0,0 +1,49 @@
+list tests
+ test1
+ memtest1
+end
+
+# Does substr work?
+macro test1
+
+ $PASS = 1
+
+ local tstr ss
+
+ $tstr = "riddle me this"
+
+ substr $tstr 4 8 ss
+
+ if ("$ss" != "le me th")
+  $PASS = 0
+  echo "Incorrect substring returned!"
+ end
+end
+
+
+# Memory test
+macro memtest1
+
+ local i tstr ss
+
+ $tstr = "riddle me this"
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+  substr $tstr 4 8 ss
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/test.dat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/test.dat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/test.dat	(revision 17422)
@@ -0,0 +1,1 @@
+test     foobar
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/usleep.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/usleep.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/usleep.sh	(revision 17422)
@@ -0,0 +1,25 @@
+list tests
+ test1
+end
+
+# Does usleep work?
+macro test1
+ $PASS = 1
+
+ list tstart -x "date +%S"
+
+ usleep 3000000
+
+ list tend -x "date +%S"
+
+ if ($tstart:0 >= 57)
+  $tend:0 = $tend:0 + 60
+ end
+
+ $diff = abs (3 - abs($tstart:0 - $tend:0))
+
+ if ($diff > 1.1)
+  $PASS = 0
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/variable.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/variable.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/variable.sh	(revision 17422)
@@ -0,0 +1,506 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+ test9
+ test10
+ test11
+ test12
+ test13
+ test14
+ testmem1
+ testmem2
+ testmem3
+ testmem4
+ testmem5
+ testmem6
+ testmem7
+ testmem8
+ testmem9
+ testmem10
+end
+
+# do we set variables correctly?
+macro test1
+
+ local i
+
+ $i = 99
+  
+ if ($i == 99)
+   $PASS = 1
+ else
+   $PASS = 0
+ end
+
+end
+
+# do math expressions assign the correct value (test2 -> test8)
+macro test2
+
+  local i
+
+  $i = 99 - 98
+  
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i : $i"
+ end
+
+end
+
+macro test3
+
+  local i
+
+  $i = 2*3
+  
+ if ($i == 6)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test4
+
+  local i
+
+  $i = 2^3
+
+ if ($i == 8)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test5
+
+  local i
+
+  $i = 2+3
+
+ if ($i == 5)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+macro test6
+
+  local i
+
+  $i = 6/3
+
+ if ($i == 2)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing operation priority
+macro test7
+
+  local i j
+
+  $i = 2 - 3*2
+  $j = 10/2*5+5
+
+ if (($i == -4) && ($j == 30))
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i\ j: $j"
+ end
+
+end
+
+# testing math on negative numbers
+macro test8
+
+  local i
+
+  $i = -2 - -3
+
+ if ($i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+
+end
+
+# testing the existance of variables
+macro test9
+
+  local i
+
+  $i = 0
+
+ if ($?i == 1)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "i: $i"
+ end
+end
+
+# test increment
+macro test10
+
+  local i N
+
+  $N = 0
+  for i 0 100
+    $N ++
+  end
+
+  if ($N == 100)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test decrement
+macro test11
+
+  local i N
+
+  $N = 100
+  for i 0 100
+    $N --
+  end
+
+  if ($N == 0)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test command assign
+macro test12
+
+  local i N
+
+  $N = `ls -dF /etc`
+
+  if ("$N" == "/etc/")
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "N: $N"
+ end
+end
+
+# test vector assign
+macro test13
+
+  local i N
+
+  create v1 0 100
+  v1[5] = 10
+  set v2 = v1 + 20
+
+  if (v2[5] == 30)
+   $PASS = 1
+  else
+   $PASS = 0
+   echo "v1\[5\]: v1[5]"
+   echo "v2\[5\]: v2[5]"
+ end
+end
+
+# test local variables
+macro test14
+
+ $PASS = 1
+
+ # a slightly weak test: depends on no prior macros defining variables
+ # with the names below
+ if (($?very_obscure_name == 1) || ($?another_obscure_name == 1))
+  $PASS = 0
+ end
+end
+
+# check memleaks (set global)
+macro testmem1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set string)
+macro testmem2
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   $Nvar = test line
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set double)
+macro testmem3
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $Nvar = 5.212
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (set local)
+macro testmem4
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   $N = 10
+ end    
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (existence)
+macro testmem5
+
+ local i N
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   echo $?N
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (get variable)
+macro testmem6
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 5
+ output /dev/null
+ for i 0 10000
+   echo $Nvar
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (increment)
+macro testmem7
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 0
+ output /dev/null
+ for i 0 10000
+   $Nvar ++
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (decrement)
+macro testmem8
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ $Nvar = 10000
+ output /dev/null
+ for i 0 10000
+   $Nvar --
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# check memleaks (command)
+macro testmem9
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 100
+   $Nvar = `ls -d /etc`
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
+
+# check memleaks (vector assign)
+macro testmem10
+
+ local i
+
+ create v1 0 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   v1[5] = $i
+ end    
+ output stdout
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/while.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/while.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/test/while.sh	(revision 17422)
@@ -0,0 +1,19 @@
+list tests
+ test1
+end
+
+# Does while work?
+macro test1
+ $PASS = 1
+ local i
+ $i = 0
+ while ($i <= 10)
+  if ($i == 11)
+   $PASS = 0
+   echo "While loop failure!"
+   echo "i: $i"
+   break
+  end
+  $i++
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/usleep.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/usleep.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/usleep.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "basic.h"
+
+int exec_usleep (int argc, char **argv) {
+
+  int i;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "usage: usleep N\n");
+    return (FALSE);
+  }
+
+  i = atof (argv[1]);
+  usleep (i);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/wait.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/wait.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/wait.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "basic.h"
+
+int wait_func (int argc, char **argv) {
+
+  char buff[1024];
+  int i;
+
+  for (i = 1; i < argc; i++) {
+    gprint (GP_ERR, "%s ", argv[i]);
+  }
+  gprint (GP_ERR, "\n");
+  scan_line (stdin, buff);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/which.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/which.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.basic/which.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "basic.h"
+
+int which (int argc, char **argv) {
+
+  Command *cmd;
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: which <filename>\n");
+    return (FALSE);
+  }
+
+  cmd = MatchCommand (argv[1], TRUE, TRUE);
+  if (cmd == NULL) return (FALSE);
+
+  gprint (GP_ERR, "%-25s -- %s\n", cmd[0].name, cmd[0].help);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/Makefile	(revision 17422)
@@ -0,0 +1,159 @@
+default: libdatacmd
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/cmd.data
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# data user commands ########################
+srcs = \
+$(SRC)/init.$(ARCH).o          \
+$(SRC)/accum.$(ARCH).o		\
+$(SRC)/applyfit2d.$(ARCH).o	\
+$(SRC)/applyfit.$(ARCH).o	\
+$(SRC)/box.$(ARCH).o		\
+$(SRC)/book.$(ARCH).o		\
+$(SRC)/book_commands.$(ARCH).o	\
+$(SRC)/center.$(ARCH).o	\
+$(SRC)/clear.$(ARCH).o		\
+$(SRC)/clip.$(ARCH).o		\
+$(SRC)/close.$(ARCH).o		\
+$(SRC)/concat.$(ARCH).o	\
+$(SRC)/contour.$(ARCH).o	\
+$(SRC)/create.$(ARCH).o	\
+$(SRC)/cumulative.$(ARCH).o	\
+$(SRC)/cursor.$(ARCH).o	\
+$(SRC)/cut.$(ARCH).o		\
+$(SRC)/delete.$(ARCH).o	\
+$(SRC)/device.$(ARCH).o	\
+$(SRC)/dimendown.$(ARCH).o	\
+$(SRC)/dimenup.$(ARCH).o	\
+$(SRC)/dbconnect.$(ARCH).o	\
+$(SRC)/dbselect.$(ARCH).o	\
+$(SRC)/erase.$(ARCH).o		\
+$(SRC)/extract.$(ARCH).o	\
+$(SRC)/fft1d.$(ARCH).o		\
+$(SRC)/fft2d.$(ARCH).o		\
+$(SRC)/fit2d.$(ARCH).o		\
+$(SRC)/fit.$(ARCH).o		\
+$(SRC)/gaussj.$(ARCH).o	\
+$(SRC)/gaussdeviate.$(ARCH).o	\
+$(SRC)/grid.$(ARCH).o		\
+$(SRC)/gridify.$(ARCH).o       \
+$(SRC)/grow.$(ARCH).o		\
+$(SRC)/ungridify.$(ARCH).o     \
+$(SRC)/histogram.$(ARCH).o	\
+$(SRC)/imcut.$(ARCH).o	 	\
+$(SRC)/imhist.$(ARCH).o	\
+$(SRC)/imsmooth.$(ARCH).o	\
+$(SRC)/integrate.$(ARCH).o	\
+$(SRC)/interpolate.$(ARCH).o	\
+$(SRC)/jpeg.$(ARCH).o		\
+$(SRC)/kern.$(ARCH).o		\
+$(SRC)/keyword.$(ARCH).o	\
+$(SRC)/labels.$(ARCH).o	\
+$(SRC)/limits.$(ARCH).o	\
+$(SRC)/line.$(ARCH).o		\
+$(SRC)/list_buffers.$(ARCH).o	\
+$(SRC)/list_header.$(ARCH).o	\
+$(SRC)/list_vectors.$(ARCH).o	\
+$(SRC)/load.$(ARCH).o		\
+$(SRC)/lookup.$(ARCH).o	\
+$(SRC)/mkrgb.$(ARCH).o	\
+$(SRC)/mcreate.$(ARCH).o	\
+$(SRC)/medacc.$(ARCH).o	\
+$(SRC)/mget.$(ARCH).o		\
+$(SRC)/minterpolate.$(ARCH).o	\
+$(SRC)/mset.$(ARCH).o		\
+$(SRC)/peak.$(ARCH).o		\
+$(SRC)/periodogram.$(ARCH).o	\
+$(SRC)/plot.$(ARCH).o		\
+$(SRC)/dot.$(ARCH).o		\
+$(SRC)/point.$(ARCH).o		\
+$(SRC)/ps.$(ARCH).o		\
+$(SRC)/queuedelete.$(ARCH).o	\
+$(SRC)/queuedrop.$(ARCH).o	\
+$(SRC)/queuelist.$(ARCH).o	\
+$(SRC)/queueload.$(ARCH).o	\
+$(SRC)/queuesize.$(ARCH).o	\
+$(SRC)/queuepush.$(ARCH).o	\
+$(SRC)/queuepop.$(ARCH).o	\
+$(SRC)/queueprint.$(ARCH).o	\
+$(SRC)/queueinit.$(ARCH).o	\
+$(SRC)/radial.$(ARCH).o	\
+$(SRC)/rd.$(ARCH).o		\
+$(SRC)/rdseg.$(ARCH).o		\
+$(SRC)/read_vectors.$(ARCH).o	\
+$(SRC)/rebin.$(ARCH).o		\
+$(SRC)/resize.$(ARCH).o	\
+$(SRC)/roll.$(ARCH).o		\
+$(SRC)/rotate.$(ARCH).o	\
+$(SRC)/save.$(ARCH).o		\
+$(SRC)/section.$(ARCH).o	\
+$(SRC)/select.$(ARCH).o	\
+$(SRC)/set.$(ARCH).o		\
+$(SRC)/shift.$(ARCH).o		\
+$(SRC)/sort.$(ARCH).o		\
+$(SRC)/spline_apply.$(ARCH).o	\
+$(SRC)/spline_construct.$(ARCH).o \
+$(SRC)/stats.$(ARCH).o		   \
+$(SRC)/style.$(ARCH).o		   \
+$(SRC)/subraster.$(ARCH).o	   \
+$(SRC)/subset.$(ARCH).o	   \
+$(SRC)/svd.$(ARCH).o		   \
+$(SRC)/swapbytes.$(ARCH).o	   \
+$(SRC)/textline.$(ARCH).o	   \
+$(SRC)/tv.$(ARCH).o		   \
+$(SRC)/tvchannel.$(ARCH).o	   \
+$(SRC)/tvcolors.$(ARCH).o	   \
+$(SRC)/tvcontour.$(ARCH).o	   \
+$(SRC)/tvgrid.$(ARCH).o	   \
+$(SRC)/uniq.$(ARCH).o		   \
+$(SRC)/unsign.$(ARCH).o	   \
+$(SRC)/vbin.$(ARCH).o		   \
+$(SRC)/vclip.$(ARCH).o		   \
+$(SRC)/vgauss.$(ARCH).o           \
+$(SRC)/vmaxwell.$(ARCH).o           \
+$(SRC)/vgrid.$(ARCH).o		   \
+$(SRC)/vload.$(ARCH).o		   \
+$(SRC)/vzload.$(ARCH).o		   \
+$(SRC)/vpop.$(ARCH).o		   \
+$(SRC)/vroll.$(ARCH).o		   \
+$(SRC)/vsmooth.$(ARCH).o	\
+$(SRC)/vstat.$(ARCH).o		   \
+$(SRC)/wd.$(ARCH).o		   \
+$(SRC)/write_vectors.$(ARCH).o	   \
+$(SRC)/zap.$(ARCH).o		   \
+$(SRC)/zplot.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdatacmd.$(ARCH).a: $(srcs)
+$(LIB)/libdatacmd.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdatacmd.a: $(LIB)/libdatacmd.$(ARCH).a
+$(DESTLIB)/libdatacmd.$(DLLTYPE): $(LIB)/libdatacmd.$(ARCH).$(DLLTYPE)
+
+libdatacmd: $(DESTLIB)/libdatacmd.a  $(DESTLIB)/libdatacmd.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdatacmd.a
+	rm -f $(DESTLIB)/libdatacmd.$(DLLTYPE)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/accum.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/accum.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/accum.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "data.h"
+
+int accum (int argc, char **argv) {
+  
+  int i, Nbins, bin, N, Normalize;
+  float start, end, delta;
+  float *V, *K, *O, *NV;
+  Vector *val, *key, *out;
+
+  NV = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: accum <value> <vector> <key> start end [delta]\n");
+    gprint (GP_ERR, "  sum <value> in bins corresponding to the value of <key>\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+
+  out[0].Nelements = Nbins;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+  bzero (out[0].elements, sizeof(float)*out[0].Nelements);
+  if (Normalize) {
+    ALLOCATE (NV, float, Nbins);
+    bzero (NV, sizeof(float)*Nbins);
+  }
+
+  V = val[0].elements;
+  K = key[0].elements;
+  O = out[0].elements;
+
+  for (i = 0; i < val[0].Nelements; i++, V++, K++) {
+    bin = MIN (MAX (0, (*K - start) / delta), Nbins - 1);
+    O[bin] += *V;
+    if (Normalize) NV[bin] ++;
+  }      
+
+  if (Normalize) {
+    for (i = 0; i < Nbins; i++) {
+      O[i] /= (float) NV[i];
+    }
+    free (NV);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit.c	(revision 17422)
@@ -0,0 +1,57 @@
+# include "data.h"
+
+char *get_variable (char *);
+
+int applyfit (int argc, char **argv) {
+  
+  int i, j, order;
+  char *c, name[64];
+  double *C, X;
+  float *x, *y;
+  Vector *xvec, *yvec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: applyfit x y\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double, order+1);
+  for (i = 0; i < order + 1; i++) {
+    sprintf (name, "C%d", i);
+    c = get_variable (name);
+    if (c == NULL) {
+      gprint (GP_ERR, "missing fit term %d\n", i);
+      return (FALSE);
+    }
+    C[i] = atof (c);
+    free (c);
+  }
+  yvec[0].Nelements = xvec[0].Nelements;
+  REALLOCATE (yvec[0].elements, float, yvec[0].Nelements);
+  bzero (yvec[0].elements, sizeof(float)*yvec[0].Nelements);
+  x = xvec[0].elements;
+  y = yvec[0].elements;
+
+  for (j = 0; j < xvec[0].Nelements; j++, x++, y++) {
+    X = 1;
+    for (i = 0; i < order + 1; i++) {
+      *y += C[i]*X;
+      X = X * (*x);
+    }
+  }
+
+  free (C);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit2d.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit2d.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/applyfit2d.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+char *get_variable (char *);
+
+int applyfit2d (int argc, char **argv) {
+  
+  int i, j, n, order;
+  char *c, name[64];
+  double **C, X, Y;
+  float *x, *y, *z;
+  Vector *xvec, *yvec, *zvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: applyfit x y z\n");
+    return (FALSE);
+  }
+
+  c = get_variable ("Cnn");
+  if (c == NULL) {
+    gprint (GP_ERR, "no fit available\n");
+    return (FALSE);
+  }
+  order = atof (c);
+  free (c);
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  ALLOCATE (C, double *, order+1);
+  for (i = 0; i < order + 1; i++) {
+    ALLOCATE (C[i], double, order+1);
+    for (j = 0; j < order + 1 - i; j++) {
+      sprintf (name, "CX%dY%d", i, j);
+      c = get_variable (name);
+      if (c == NULL) {
+	gprint (GP_ERR, "missing fit term %d,%d\n", i, j);
+	for (j = 0; j < i; j++) free (C[j]);
+	free (C);
+	return (FALSE);
+      }
+      C[i][j] = atof (c);
+      free (c);
+    }
+  }
+
+  zvec[0].Nelements = xvec[0].Nelements;
+  REALLOCATE (zvec[0].elements, float, zvec[0].Nelements);
+  bzero (zvec[0].elements, sizeof(float)*zvec[0].Nelements);
+  x = xvec[0].elements;
+  y = yvec[0].elements;
+  z = zvec[0].elements;
+
+  for (n = 0; n < xvec[0].Nelements; n++, x++, y++, z++) {
+    Y = X = 1;
+    for (j = 0; j < order + 1; j++) {
+      X = Y;
+      for (i = 0; i < order + 1 - j; i++) {
+	*z += C[i][j]*X;
+	X = X * (*x);
+      }
+      Y = Y * (*y);
+    }
+  }
+  for (i = 0; i < order + 1; i++) free (C[i]);
+  free (C);
+
+  return (TRUE);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "data.h"
+
+int book_list (int argc, char **argv);
+int book_init (int argc, char **argv);
+int book_create (int argc, char **argv);
+int book_delete (int argc, char **argv);
+int book_getbook (int argc, char **argv);
+int book_listbook (int argc, char **argv);
+int book_shuffle (int argc, char **argv);
+int book_npages (int argc, char **argv);
+int book_newpage (int argc, char **argv);
+int book_getpage (int argc, char **argv);
+int book_delpage (int argc, char **argv);
+int book_listpage (int argc, char **argv);
+int book_setword (int argc, char **argv);
+int book_getword (int argc, char **argv);
+
+static Command book_commands[] = {
+  {1, "list",     book_list,     "list books"},
+  {1, "init",     book_init,     "initialize a book"},
+  {1, "create",   book_create,   "create a book"},
+  {1, "delete",   book_delete,   "delete a book"},
+  {1, "getbook",  book_getbook,  "get book name by location"},
+  {1, "listbook", book_listbook, "list pages in a book"},
+  {1, "shuffle",  book_shuffle,  "shuffle pages in a book"},
+  {1, "npages",   book_npages,   "return number of pages in a book"},
+  {1, "newpage",  book_newpage,  "create a new page in a book"},
+  {1, "getpage",  book_getpage,  "get page name by location"},
+  {1, "delpage",  book_delpage,  "delete a page in a book"},
+  {1, "listpage", book_listpage, "list a page in a book"},
+  {1, "setword",  book_setword,  "set the value of a word in a page"},
+  {1, "getword",  book_getword,  "set the value of a word from a page"},
+};
+
+int book_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: book (command)\n");
+    gprint (GP_ERR, "    book list                                  : list books\n");
+    gprint (GP_ERR, "    book init     (book)                       : removes all pages from book\n");
+    gprint (GP_ERR, "    book create   (book)                       : create a book\n");
+    gprint (GP_ERR, "    book delete   (book)                       : delete a book\n");
+    gprint (GP_ERR, "    book getbook  (where) [-var var]           : get book name\n");
+    gprint (GP_ERR, "    book listbook (book)                       : list a book\n");
+    gprint (GP_ERR, "    book shuffle  (book)                       : randomize pages in a book\n");
+    gprint (GP_ERR, "    book npages   (book) [-var var] [-key key] : return number of pages in a book\n");
+    gprint (GP_ERR, "    book newpage  (book) (page)                : create a new page in a book\n");
+    gprint (GP_ERR, "    book getpage  (book) (where) [-var var] [-key key value] : get page name in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) (page) [-key key]     : delete a page in a book\n");
+    gprint (GP_ERR, "    book delpage  (book) -key name value       : delete a page in a book\n");
+    gprint (GP_ERR, "    book listpage (book) (page)                : list a page in a book\n");
+    gprint (GP_ERR, "    book setword  (book) (page) (word) (value) : set the value of a word in a page\n");
+    gprint (GP_ERR, "    book getword  (book) (page) (word) [-var var] : set the value of a word from a page\n");
+    return (FALSE);
+  }
+
+  N = sizeof (book_commands) / sizeof (Command);
+
+  /* find the book sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (book_commands[i].name, argv[1])) {
+      status = (*book_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown book command %s\n", argv[1]);
+  return (FALSE);
+}
+
+/* book is called with the command "book".  
+   the command line word "book" is meant to be followed the one of several 
+   possible options:
+   
+   book create
+   book delete
+   book list
+   book edit
+   book read
+   book write
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book_commands.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book_commands.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/book_commands.c	(revision 17422)
@@ -0,0 +1,489 @@
+# include "data.h"
+
+int book_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: book list\n");
+    return FALSE;
+  }
+
+  ListBooks();
+  return TRUE;
+}
+
+int book_create (int argc, char **argv) {
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book create (book)\n");
+    return FALSE;
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_delete (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book delete (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteBook (book);
+  if (!status) abort ();
+  return TRUE;
+}
+
+int book_init (int argc, char **argv) {
+
+  int status;
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book init (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book != NULL) {
+      status = DeleteBook (book);
+      if (!status) abort ();
+  }
+
+  CreateBook (argv[1]);
+  return TRUE;
+}
+
+int book_listbook (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book listbook (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ListPages (book);
+  return TRUE;
+}
+
+int book_npages (int argc, char **argv) {
+
+  int i, N, Npages;
+  Book *book;
+  char *varName;
+  char *Key, *Value, *value;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book npages (book) [-var result] [-key name value]\n");
+    gprint (GP_ERR, "  reports the number of pages (optionally limited to those matching the key)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  Npages = book[0].Npages;
+  if (Key) {
+    /* count only matching key */
+    Npages = 0;
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) {
+	if (!strcmp(Value, "NULL")) { 
+	  Npages ++;
+	} else {
+	  continue;
+	}
+      } else {
+	if (!strcmp(value, Value)) Npages ++;
+      }
+    }
+  }
+
+  if (varName) {
+    set_int_variable (varName, Npages);
+  } else {
+    gprint (GP_ERR, "%d pages\n", Npages);
+  }
+  FREE (Key);
+  FREE (Value);
+  FREE (varName);
+  return TRUE;
+}
+
+int book_getbook (int argc, char **argv) {
+
+  int where, N;
+  char *varName;
+  Book *book;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book getbook (where) [-var var]\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  where = atoi (argv[1]);
+  book = GetBook (where);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, book[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", book[0].name);
+  }
+  FREE (varName);
+  return TRUE;
+}
+
+int book_newpage (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book newpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  CreatePage (book, argv[2]);
+  return TRUE;
+}
+
+int book_delpage (int argc, char **argv) {
+
+  int i, N;
+  Page *page;
+  Book *book;
+  char *Key, *Value, *value;
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 3) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: book delpage (book) (page)\n");
+    gprint (GP_ERR, "USAGE: book delpage (book) -key name value\n");
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+  
+  if (Key) {
+    /* delete by matching key */
+    for (i = 0; i < book[0].Npages; i++) {
+      value = BookGetWord (book[0].pages[i], Key);
+      if (value == NULL) continue;
+      if (!strcmp(value, Value)) {
+	DeletePage (book, book[0].pages[i]);
+	i--; /* if we delete this page, don't advance the counter */
+      }
+    }
+    FREE (Key);
+    FREE (Value);
+    return TRUE;
+  }
+
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (Key);
+    FREE (Value);
+    return FALSE;
+  }
+
+  DeletePage (book, page);
+  FREE (Key);
+  FREE (Value);
+  return TRUE;
+}
+
+int book_listpage (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book listpage (book) (page)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  ListWords (page);
+  return TRUE;
+}
+
+int book_getpage (int argc, char **argv) {
+
+  int where, N;
+  char *varName, *keyName, *keyValue;
+  Book *book;
+  Page *page;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  keyValue = keyName = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    keyName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    keyValue = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: book getpage (book) (where) [-var var] [-key key value]\n");
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  where = atoi (argv[2]);
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (keyName == NULL) {
+    page = GetPage (book, where);
+  } else {
+    page = GetPageRestricted (book, where, keyName, keyValue);
+  }
+
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      FREE (keyName);
+      FREE (keyValue);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %d not found in %s\n", where, argv[1]);
+    FREE (varName);
+    FREE (keyName);
+    FREE (keyValue);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, page[0].name);
+  } else {
+    gprint (GP_LOG, "%s\n", page[0].name);
+  }
+  FREE (varName);
+  FREE (keyName);
+  FREE (keyValue);
+  return TRUE;
+}
+
+int book_shuffle (int argc, char **argv) {
+
+  Book *book;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: book shuffle (book)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ShufflePages (book);
+  return TRUE;
+}
+
+int book_setword (int argc, char **argv) {
+
+  Page *page;
+  Book *book;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: book setword (book) (page) (word) (value)\n");
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    return FALSE;
+  }
+
+  BookSetWord (page, argv[3], argv[4]);
+  return TRUE;
+}
+
+int book_getword (int argc, char **argv) {
+
+  int N;
+  Page *page;
+  Book *book;
+  char *value, *varName;
+
+  varName = NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    varName = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: book getword (book) (page) (word)\n");
+    FREE (varName);
+    return FALSE;
+  }
+
+  book = FindBook (argv[1]);
+  if (book == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "book %s not found\n", argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+  
+  page = FindPage (book, argv[2]);
+  if (page == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    } 
+    gprint (GP_ERR, "page %s in book %s not found\n", argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  value = BookGetWord (page, argv[3]);
+  if (value == NULL) {
+    if (varName) {
+      set_str_variable (varName, "NULL");
+      FREE (varName);
+      return TRUE;
+    }
+    gprint (GP_ERR, "value %s on page %s in book %s not found\n", argv[3], argv[2], argv[1]);
+    FREE (varName);
+    return FALSE;
+  }
+
+  if (varName) {
+    set_str_variable (varName, value);
+  } else {
+    gprint (GP_LOG, "%s\n", value);
+  }
+    
+  FREE (varName);
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/box.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/box.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/box.c	(revision 17422)
@@ -0,0 +1,91 @@
+# include "data.h"
+
+int box (int argc, char **argv) {
+  
+  int i, N, kapa;
+  char *name;
+  Graphdata graphmode;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  strcpy (graphmode.ticks, "2222");
+  if ((N = get_argument (argc, argv, "-ticks"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.ticks, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.ticks) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.ticks); i++) {
+      if ((graphmode.ticks[i] != '0') && (graphmode.ticks[i] != '1') && (graphmode.ticks[i] != '2')) { goto usage; }
+    }
+  }
+  
+  strcpy (graphmode.labels, "2222");
+  if ((N = get_argument (argc, argv, "-labels"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.labels, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.labels) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.labels); i++) {
+      if ((graphmode.labels[i] != '0') && (graphmode.labels[i] != '1') && (graphmode.labels[i] != '2')) { goto usage; }
+    }
+  }
+
+  strcpy (graphmode.axis, "2222");
+  if ((N = get_argument (argc, argv, "-axis"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (graphmode.axis, argv[N]);
+    remove_argument (N, &argc, argv);
+    if (strlen (graphmode.axis) != 4) { goto usage; }
+    for (i = 0; i < strlen (graphmode.axis); i++) {
+      if ((graphmode.axis[i] != '0') && (graphmode.axis[i] != '1') && (graphmode.axis[i] != '2')) { goto usage; }
+    }
+  }
+
+  if (argc != 1) goto usage;
+
+  KapaBox (kapa, &graphmode);
+  return (TRUE);
+      
+ usage:
+  gprint (GP_ERR, "USAGE: box [-ticks NNNN] [-axis NNNN] [-labels NNNN]\n");
+  return (FALSE);
+}
+
+
+/* box has:
+   axis
+   labels
+   ticks
+
+   assign like this:   
+   -axis 0000
+   -labels 0000
+   -ticks 0000
+
+   default:
+   -axis 1111
+   -labels 1100
+   -ticks 1111
+
+   messages to kapa:
+
+   DBOX
+   (xmin) (xmax) (ymin) (ymax)
+   AAAA LLLL TTTT
+
+   A = axis
+   L = label
+   T = ticks
+   
+   0 = off
+   1 = on
+   2 = default / maintain
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/center.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/center.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/center.c	(revision 17422)
@@ -0,0 +1,31 @@
+# include "data.h"
+
+int center (int argc, char **argv) {
+  
+  double x, y;
+  int zoom;
+  int kapa, N;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: center x y [zoom]\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+  zoom = 0;
+  if (argc == 4) zoom = atof (argv[3]);
+
+  KiiCenter (kapa, x, y, zoom);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clear.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clear.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clear.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "data.h"
+
+// default is to clear all plots, but not the sections or the images
+int clear (int argc, char **argv) {
+  
+  int N;
+  int kapa;
+  char *name;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-s")) || 
+      (N = get_argument (argc, argv, "-section"))) {
+      KapaClearSections (kapa);
+      return (TRUE);
+  }
+
+  // clear all sections
+  if ((N = get_argument (argc, argv, "-graph"))) {
+      KapaClearCurrentPlot (kapa);
+      return (TRUE);
+  }
+
+  // clear image
+  if ((N = get_argument (argc, argv, "-image"))) {
+      KapaClearImage (kapa);
+      return (TRUE);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: clear [-n Xgraph] [-s|-section] [-image] [-graph]\n");
+    gprint (GP_ERR, "       [-s|-section] : clear all sections\n");
+    gprint (GP_ERR, "       [-graph]      : clear current graph\n");
+    gprint (GP_ERR, "       [-image]      : clear current image\n");
+    return (FALSE);
+  }
+
+  KapaClearPlots (kapa);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clip.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clip.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/clip.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int clip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  float *in;
+  Buffer *buf;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+  }
+
+  Npix = buf[0].matrix.Naxis[0]*buf[0].matrix.Naxis[1];
+  in = (float *) buf[0].matrix.buffer;
+
+  if (argc == 6) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (*in < min) 
+	*in = Vmin;
+      if (*in > max)
+	*in = Vmax;
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_NAN) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (isnan (*in)) {
+	*in = nan_val;
+      }
+    }
+  }
+  in = (float *) buf[0].matrix.buffer;
+  if (DO_INF) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (!finite (*in)) {
+	*in = inf_val;
+      }
+    }
+  }
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/close.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/close.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/close.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int close_device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;
+  /* close current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+
+  close_kapa (name); 
+  FREE (name);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/concat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/concat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/concat.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "data.h"
+
+int concat (int argc, char **argv) {
+
+  int  i, j, Nin;
+  double value;
+  Vector *ivec, *ovec;
+
+  /** check basic syntax **/
+  if (argc != 3) {
+    gprint (GP_ERR, "SYNTAX: concat (vector/value) vector\n");
+    gprint (GP_ERR, "  concatanate (vector/value) to vector\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (SelectScalar (argv[1], &value)) {
+    Nin = ovec[0].Nelements;
+    ovec[0].Nelements++;
+    REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+    ovec[0].elements[Nin] = value;
+    return (TRUE);
+  } 
+  
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements + ivec[0].Nelements);
+  for (j = ovec[0].Nelements, i = 0; i < ivec[0].Nelements; i++, j++) {
+    ovec[0].elements[j] = ivec[0].elements[i];
+  }
+  ovec[0].Nelements += ivec[0].Nelements;
+  
+  return (TRUE);
+    
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/contour.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/contour.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/contour.c	(revision 17422)
@@ -0,0 +1,262 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+Vector *xv, *yv;
+int N, NVEC;
+
+void DUMP (float x, float y, float dx, float dy) {
+  
+  xv[0].elements[N]   = x;
+  xv[0].elements[N+1] = x+dx;
+  
+  yv[0].elements[N]   = y;
+  yv[0].elements[N+1] = y+dy;
+  
+  N+=2;
+
+  if (N >= NVEC - 2) {
+    NVEC += 100;
+    REALLOCATE (xv[0].elements, float, NVEC);
+    REALLOCATE (yv[0].elements, float, NVEC);
+  }
+}
+
+int contour (int argc, char **argv) {
+
+  int i, j, Nx, Ny;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *matrix;
+  Buffer *buf;
+  
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: contour <buffer> X Y level\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xv = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[4]);
+  matrix = (float *)(buf[0].matrix.buffer);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+
+  N = 0;
+  NVEC = 100;
+  REALLOCATE (xv[0].elements, float, NVEC);
+  REALLOCATE (yv[0].elements, float, NVEC);
+
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP (x,y,dx,dy);
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP (x,y,dx,dy);
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP (x,y,dx,dy);
+    }
+  }
+  
+/* free anything? */
+
+  xv[0].Nelements = N;
+  yv[0].Nelements = N;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/create.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/create.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/create.c	(revision 17422)
@@ -0,0 +1,37 @@
+# include "data.h"
+
+int create (int argc, char **argv) {
+  
+  int i;
+  float start, end, delta;
+  Vector *vec;
+  
+  if ((argc != 5) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: create vector start end [delta]\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[2]);
+  end   = atof (argv[3]);
+  delta = 1;
+  if (argc == 5) delta = atof (argv[4]);
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+
+  vec[0].Nelements = (end - start) / delta;
+  REALLOCATE (vec[0].elements, float, vec[0].Nelements);
+
+  for (i = 0; i < vec[0].Nelements; i++) {
+    vec[0].elements[i] = start + i*delta;
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cumulative.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cumulative.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cumulative.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int cumulative (int argc, char **argv) {
+  
+  int i;
+  float *Vi, *Vo;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cumulative invec outvec\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  ovec[0].Nelements = ivec[0].Nelements;
+    
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+  bzero (ovec[0].elements, sizeof(float)*ovec[0].Nelements);
+
+  Vi = ivec[0].elements;
+  Vo = ovec[0].elements;
+  *Vo = *Vi;
+  for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
+    *Vo = Vo[-1] + *Vi;
+  }      
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cursor.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cursor.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cursor.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int cursor (int argc, char **argv) {
+
+  char string[20], key[20], *name;
+  int i, N, kapa;
+  double X, Y, R, D, Z;
+  void *oldsignal;
+
+  // XXX need to be able to specify graph vs image coords
+  // currently, if only one exists, that frame will be used
+  // if both exist, defaults to ??
+  // if ((N = get_argument (argc, argv, "-g"))) {
+  // if ((N = get_argument (argc, argv, "-i"))) {
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraphData (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  N = 0;
+  if (argc == 2) N = atof (argv[1]);
+
+  if ((argc != 1) && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: cursor [Npts] [-n window] [-g | -i]\n");
+    return (FALSE);
+  }
+  
+  KiiCursorOn (kapa);
+  
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  for (i = 0; ((i < N) || (N == 0)) && !interrupt; i++) {
+
+    KiiCursorRead (kapa, &X, &Y, &Z, &R, &D, key);
+
+    sprintf (string, "X%s", key);
+    set_variable (string, X);
+    sprintf (string, "Y%s", key);
+    set_variable (string, Y);
+    sprintf (string, "Z%s", key);
+    set_variable (string, Z);
+    sprintf (string, "R%s", key);
+    set_variable (string, R);
+    sprintf (string, "D%s", key);
+    set_variable (string, D);
+
+    set_str_variable ("KEY", key);
+    
+    gprint (GP_LOG, "%s %f %f %f %f %f\n", key, X, Y, Z, R, D);
+
+    if (!strcasecmp (key, "Q")) break;
+  }
+
+  signal (SIGINT, oldsignal);
+  KiiCursorOff (kapa);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cut.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cut.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/cut.c	(revision 17422)
@@ -0,0 +1,122 @@
+# include "data.h"
+
+enum {SUM, MEAN, MEDIAN};
+
+int cut (int argc, char **argv) {
+  
+  int i, j, N, Nx, Ny, Mode;
+  float *Vin, *Vbuf, value;
+  int sx, sy, nx, ny;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  Mode = SUM;
+  if ((N = get_argument (argc, argv, "-median"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEDIAN;
+  }
+  if ((N = get_argument (argc, argv, "-mean"))) {
+    remove_argument (N, &argc, argv);
+    Mode = MEAN;
+  }
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> <X|Y> sx sy nx ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+ 
+  sx = atof (argv[5]);
+  sy = atof (argv[6]);
+  nx = atof (argv[7]);
+  ny = atof (argv[8]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((sx < 0) || (sy < 0) || (sx+nx > Nx) || (sy+ny > Ny)) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  switch (argv[4][0]) {
+  case 'x':
+  case 'X':
+    /* create output vectors */
+    xvec[0].Nelements = yvec[0].Nelements = nx;
+    REALLOCATE (xvec[0].elements, float, MAX (nx, 1));
+    REALLOCATE (yvec[0].elements, float, MAX (nx, 1));
+    bzero (yvec[0].elements, nx*sizeof(float));
+    for (i = 0; i < nx; i++) {
+      xvec[0].elements[i] = i + sx; 
+    }
+    ALLOCATE (Vbuf, float, MAX (ny, 1));
+
+    for (i = 0; i < nx; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + sy*Nx + sx + i; 
+      for (j = 0; j < ny; j++, Vin += Nx) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, ny);
+	value = Vbuf[(int)(0.5*ny)];
+      } else {
+	value = 0;
+	for (j = 0; j < ny; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= ny; }
+      }
+      yvec[0].elements[i] = value;
+    }
+    free (Vbuf);
+    break;
+    
+  case 'y':
+  case 'Y':
+    xvec[0].Nelements = yvec[0].Nelements = ny;
+    REALLOCATE (xvec[0].elements, float, ny);
+    REALLOCATE (yvec[0].elements, float, ny);
+    bzero (yvec[0].elements, ny*sizeof(float));
+    for (i = 0; i < ny; i++) {
+      xvec[0].elements[i] = i + sy; 
+    }
+    ALLOCATE (Vbuf, float, MAX (nx, 1));
+
+    for (i = 0; i < ny; i++) {
+      /* accumulate values */
+      Vin = (float *)(buf[0].matrix.buffer) + (sy + i)*Nx + sx; 
+      for (j = 0; j < nx; j++, Vin ++) {
+	Vbuf[j] = *Vin;
+      }
+      /* apply stat of choice */
+      if (Mode == MEDIAN) {
+	fsort (Vbuf, nx);
+	value = Vbuf[(int)(0.5*nx)];
+      } else {
+	value = 0;
+	for (j = 0; j < nx; j++) {
+	  value += Vbuf[j];
+	}
+	if (Mode == MEAN) { value /= nx; }
+      }
+      yvec[0].elements[i] = value;
+    }
+    free (Vbuf);
+    break;
+
+  default:
+    gprint (GP_ERR, "<dir> can be X or Y\n");
+    return (FALSE);
+    break;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbconnect.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 17422)
@@ -0,0 +1,117 @@
+# include "data.h"
+# if (HAVE_MYSQL_H)
+# include "mysql.h"
+
+MYSQL mysql;
+MYSQL *connection = NULL;
+
+int dbconnect (int argc, char **argv) {
+  
+  char query[256];
+  char password[1024];
+  MYSQL_RES *result;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: dbconnect (hostname) (username) (database)\n");
+    return FALSE;
+  }
+
+  fprintf (stdout, "Enter password: ");
+  scan_line (stdin, password);
+
+# if (0)
+  int i;
+  char c;
+
+  initscr();
+  noecho();
+  i = 0;
+  while (((c = getch()) != EOF) && (c != '\n')) {
+    password[i] = c;
+    i++;
+  }
+  password[i] = 0;
+# endif
+
+  mysql_init (&mysql);
+  connection = mysql_real_connect (&mysql, argv[1], argv[2], password, argv[3], 0, 0, 0);
+
+  if (connection == NULL) {
+    gprint (GP_ERR, "failed to connect to database\n");
+    gprint (GP_ERR, "%s\n", mysql_error (&mysql));
+    return (FALSE);
+  }
+    
+  sprintf (query, "set @@interactive_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set interactive timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+  sprintf (query, "set @@wait_timeout = 30000");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to set wait timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  mysql_free_result (result);
+    
+# if (0)
+  int Nrows;
+  MYSQL_ROW row;
+
+  sprintf (query, "select @@interactive_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "interactive timeout: %s\n", row[0]);
+  mysql_free_result (result);
+
+  sprintf (query, "select @@wait_timeout");
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "failed to get timout\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+  result = mysql_store_result (connection);
+  Nrows = mysql_num_rows(result);
+  row = mysql_fetch_row(result);
+  fprintf (stderr, "wait timeout: %s\n", row[0]);
+  mysql_free_result (result);
+# endif
+    
+  return (TRUE);
+}
+
+// XXX do I need to close the connection before opening a new one? 
+
+void *db_getConnection () {
+  return connection;
+}
+
+# else 
+
+int dbconnect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+void *db_getConnection () {
+  return NULL;
+}
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbselect.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbselect.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dbselect.c	(revision 17422)
@@ -0,0 +1,100 @@
+# include "data.h"
+# if (HAVE_MYSQL_H) 
+# include "mysql.h"
+
+int dbselect (int argc, char **argv) {
+  
+  int i, j, Nbytes, Ncols, Nrows;
+  char *query;
+  Vector **vec;
+
+  MYSQL_RES *result;
+  MYSQL_ROW row;
+  MYSQL_FIELD *fields;
+  MYSQL *connection = NULL;
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: dbselect (fields) from (table) [where]\n");
+    return FALSE;
+  }
+
+  connection = db_getConnection ();
+  if (connection == NULL) {
+    gprint (GP_ERR, "database not defined; use dbconnect\n");
+    return (FALSE);
+  }
+
+  // generate the query line (concat the argv[i] entries)
+  Nbytes = 0;
+  for (i = 1; i < argc; i++) {
+    Nbytes += strlen(argv[i]) + 1;
+  }
+  Nbytes += 10;
+
+  ALLOCATE (query, char, Nbytes);
+  bzero (query, Nbytes);
+  strcat (query, "select ");
+  for (i = 1; i < argc; i++) {
+    strcat (query, argv[i]);
+    strcat (query, " ");
+  }
+  // strcat (query, ";");
+  // fprintf (stderr, "query: %s\n", query);
+
+  if (mysql_query (connection, query)) {
+    gprint (GP_ERR, "problem with query\n");
+    gprint (GP_ERR, "%s\n", mysql_error (connection));
+    free (query);
+    return (FALSE);
+  }
+    
+  result = mysql_store_result (connection);
+
+  Nrows = mysql_num_rows(result);
+  Ncols = mysql_num_fields(result);
+  fields = mysql_fetch_fields (result);
+
+  // fprintf (stderr, "Nrows: %d\n", Nrows);
+  // fprintf (stderr, "Ncols: %d\n", Ncols);
+
+  ALLOCATE (vec, Vector *, Ncols);
+  for (i = 0; i < Ncols; i++) {
+    if ((vec[i] = SelectVector (fields[i].name, ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "trouble creating vector named %s\n", fields[i].name);
+      free (query);
+      free (vec);
+      return (FALSE);
+    }
+    REALLOCATE (vec[i][0].elements, float, Nrows);
+    vec[i][0].Nelements = Nrows;
+  }
+
+  for (j = 0; j < Nrows; j++) {
+    row = mysql_fetch_row(result);
+    if (row == NULL) {
+      gprint (GP_ERR, "inconsistent row count: expected %d, got %d\n", Nrows, j);
+      free (query);
+      free (vec);
+      mysql_free_result (result);
+      return (FALSE);
+    }
+    for (i = 0; i < Ncols; i++) {
+      vec[i][0].elements[j] = atof (row[i]);
+    }
+  }
+
+  free (query);
+  free (vec);
+  mysql_free_result (result);
+  return (TRUE);
+}
+
+# else 
+
+int dbselect (int argc, char **argv) {
+
+  gprint (GP_ERR, "mysql library is not available\n");
+  return FALSE;
+}
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/delete.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/delete.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/delete.c	(revision 17422)
@@ -0,0 +1,31 @@
+# include "data.h"
+
+int delete (int argc, char **argv) {
+  
+  int i, N, Quiet;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete <obiect> [<object> ..]\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (DeleteNamedBuffer (argv[i])) continue; 
+    if (DeleteNamedVector (argv[i])) continue;
+    if (DeleteNamedScalar (argv[i])) continue; 
+    if (!Quiet) gprint (GP_ERR, "can't find object %s\n", argv[i]);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/device.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/device.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/device.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int device (int argc, char **argv) {
+
+  int N, kapa;
+  char *name;;
+  /* set / get current graphics device */
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (name == NULL) {
+    name = GetKapaName ();
+    if (name == NULL) {
+      gprint (GP_ERR, "no device defined\n");
+      return (FALSE);
+    }
+  } else {
+    if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  }
+  gprint (GP_ERR, "kapa %s\n", name); 
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimendown.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimendown.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimendown.c	(revision 17422)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+enum {VALUE, XCOORD, YCOORD};
+
+int dimendown (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, N, mode;
+  float *in, *out;
+  Vector *vec;
+  Buffer *buf;
+
+  mode = VALUE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    mode = XCOORD;
+  }
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    mode = YCOORD;
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Npix = Nx * Ny;
+
+  vec[0].Nelements = Npix;
+  REALLOCATE (vec[0].elements, float, Npix);
+
+  in = (float *) buf[0].matrix.buffer;
+  out = vec[0].elements;
+
+  switch (mode) {
+    case VALUE:
+      for (i = 0; i < Npix; i++, in++, out++) {
+	*out = *in;
+      }
+      break;
+
+    case XCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i % Nx;
+      }
+      break;
+
+    case YCOORD:
+      for (i = 0; i < Npix; i++, out++) {
+	*out = i / Nx;
+      }
+      break;
+  }
+      
+  return (TRUE);
+
+ usage:
+    gprint (GP_ERR, "USAGE: dimendown <buffer> <vector>\n");
+    gprint (GP_ERR, "  -x : fill vector with buffer x-coords\n");
+    gprint (GP_ERR, "  -y : fill vector with buffer y-coords\n");
+    return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimenup.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimenup.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dimenup.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int dimenup (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix;
+  float *in, *out;
+  Vector *vec;
+  Buffer *buf;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: dimenup <vector> <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((buf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Npix = vec[0].Nelements;
+  Nx = atof (argv[3]);
+  Ny = atof (argv[4]);
+  if (Npix != Nx * Ny) {
+    gprint (GP_ERR, "dimensions don't match\n");
+    return (FALSE);
+  }
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+
+  out = (float *) buf[0].matrix.buffer;
+  in = vec[0].elements;
+
+  for (i = 0; i < Npix; i++, in++, out++) {
+    *out = *in;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/dot.c	(revision 17422)
@@ -0,0 +1,27 @@
+# include "data.h"
+
+int dot (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata graphmode;
+  float x, y;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dot <x> <y>\n");
+    return (FALSE);
+  }
+  x = atof(argv[1]);
+  y = atof(argv[2]);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 2;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 1, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 1, &x, "x");
+  KapaPlotVector (kapa, 1, &y, "y");
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/erase.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/erase.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/erase.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int erase (int argc, char **argv) {
+  
+  int i, N;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: erase (overlay) [overlay, overlay, ..] \n");
+    gprint (GP_ERR, " (overlay) may be: red (0), green (1), blue (2), yellow (3) or all\n");
+    return (FALSE);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (!(strcasecmp (argv[i], "all"))) {
+      KiiEraseOverlay (kapa, "red");
+      KiiEraseOverlay (kapa, "green");
+      KiiEraseOverlay (kapa, "blue");
+      KiiEraseOverlay (kapa, "yellow");
+      continue;
+    }
+    if (!KiiSelectOverlay (argv[i], &N)) {
+      gprint (GP_ERR, "%s is not a valid overlay\n", argv[i]);
+      return (FALSE);
+    }
+    KiiEraseOverlay (kapa, argv[i]);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/extract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/extract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/extract.c	(revision 17422)
@@ -0,0 +1,92 @@
+# include "data.h"
+
+int extract (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny, NX, NY;
+  int Sx, Sy, Nx, Ny;
+  Buffer *in, *out;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((in = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = in[0].matrix.Naxis[0];
+  NY = in[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > in[0].matrix.Naxis[0]) || 
+      (sy > in[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((out = SelectBuffer (argv[2], OLDBUFFER, FALSE)) == NULL) {
+    if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    gfits_free_matrix (&out[0].matrix);
+    gfits_free_header (&out[0].header);
+
+    out[0].bitpix = in[0].bitpix;
+    out[0].unsign = in[0].unsign;
+    out[0].bscale = in[0].bscale;
+    out[0].bzero  = in[0].bzero;
+    gfits_copy_header (&in[0].header, &out[0].header);
+    gfits_modify (&out[0].header, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&out[0].header, "NAXIS2", "%d", 1, Ny);
+    out[0].header.Naxis[0] = Nx;
+    out[0].header.Naxis[1] = Ny;
+    gfits_create_matrix (&out[0].header, &out[0].matrix);
+  } else {
+    if ((out[0].header.Naxis[1] != Ny) || (out[0].header.Naxis[0] != Nx)) {
+      gprint (GP_ERR, "matrix sizes mis-matched\n");
+      gprint (GP_ERR, "%d x %d  vs  %d x %d\n", Nx, Ny, 
+	       out[0].header.Naxis[0], out[0].header.Naxis[1]);
+      return (FALSE);
+    }
+  }
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(in[0].matrix.buffer) + (j + sy)*in[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(out[0].matrix.buffer) + (j + Sy)*out[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.c	(revision 17422)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int fft1d (int argc, char **argv) {
+  
+  int N, Npix, Nbit, ZeroImaginary, forward;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  // check the input data (match lengths? binary length?)
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector size mismatch\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Npix, &Nbit)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+
+  // select or create the output vectors
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // allocate sufficient output space
+  Ore[0].Nelements = Npix;
+  Oim[0].Nelements = Npix;
+  REALLOCATE (Ore[0].elements, float, Npix);
+  REALLOCATE (Oim[0].elements, float, Npix);
+ 
+  // copy data to output vectors (fft is done in place)
+  memcpy (Ore[0].elements, Ire[0].elements, Npix*sizeof(float));
+
+  // copy imaginary vector or create a zero vector
+  if (ZeroImaginary) {
+    memset (Oim[0].elements, 0, Npix*sizeof(float));
+  } else {
+    memcpy (Oim[0].elements, Iim[0].elements, Npix*sizeof(float));
+  }    
+
+  fft1D (Ore[0].elements, Oim[0].elements, Npix, Nbit, forward); 
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.old.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.old.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft1d.old.c	(revision 17422)
@@ -0,0 +1,70 @@
+# include "data.h"
+
+int fft1dold (int argc, char **argv) {
+  
+  int i, Npix, ZeroImaginary;
+  float *t1, *t2, *temp;
+  Vector *Ire, *Iim, *Ore, *Oim;
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft1d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  Iim = NULL;
+  ZeroImaginary = TRUE;
+  if (strcmp (argv[2], "0")) {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npix = Ire[0].Nelements;
+  if (!ZeroImaginary && (Npix != Iim[0].Nelements)) {
+    gprint (GP_ERR, "vector mismatch in size\n");
+    return (FALSE);
+  }
+
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "Npix is not a binary number!\n");
+    return (FALSE);
+  }
+  
+  ALLOCATE (temp, float, 2*Npix);
+  if (ZeroImaginary) {
+    t1 = Ire[0].elements;
+    for (i = 0; i < Npix; i++, t1++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = 0;
+    }
+  } else {
+    t1 = Ire[0].elements;
+    t2 = Iim[0].elements;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      temp[2*i  ] = *t1;
+      temp[2*i+1] = *t2;
+    }
+  }    
+    
+  fftold (temp, Npix, 1); 
+
+  Ore[0].Nelements = Npix;
+  Oim[0].Nelements = Npix;
+  REALLOCATE (Ore[0].elements, float, Npix);
+  REALLOCATE (Oim[0].elements, float, Npix);
+ 
+  t1 = Ore[0].elements;
+  t2 = Oim[0].elements;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = temp[2*i  ] / Npix;
+    *t2 = temp[2*i+1] / Npix;
+  }    
+  
+  free (temp);
+  
+  return (TRUE);
+}
+
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.c	(revision 17422)
@@ -0,0 +1,79 @@
+# include "data.h"
+
+int fft2d (int argc, char **argv) {
+  
+  int ZeroImaginary, forward;
+  int N, Nx, Ny;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  forward = TRUE;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    forward = FALSE;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+
+  // check input image dimensions (match lengths? binary lengths?)
+  if (!ZeroImaginary) {
+    if ((Nx != Iim[0].header.Naxis[0]) || 
+	(Ny != Iim[0].header.Naxis[1])) {
+      gprint (GP_ERR, "image size mismatch\n");
+      return (FALSE);
+    }
+  }
+  if (!IsBinary (Nx, NULL)) {
+    gprint (GP_ERR, "Nx is not a binary number!\n");
+    return (FALSE);
+  }
+  if (!IsBinary (Ny, NULL)) {
+    gprint (GP_ERR, "Ny is not a binary number!\n");
+    return (FALSE);
+  }
+
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+  
+  /* fix up output headers (real) & allocate data buffer */
+  CreateBuffer (Ore, Nx, Ny, -32, 0.0, 1.0);
+  CreateBuffer (Oim, Nx, Ny, -32, 0.0, 1.0);
+
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+
+  // copy data to output buffers (fft is done in place)
+  memcpy (Ore[0].matrix.buffer, Ire[0].matrix.buffer, Nx*Ny*sizeof(float));
+
+  if (ZeroImaginary) {
+    memset (Oim[0].matrix.buffer, 0, Nx*Ny*sizeof(float));
+  } else {
+    memcpy (Oim[0].matrix.buffer, Iim[0].matrix.buffer, Nx*Ny*sizeof(float));
+  }
+
+  /* run the fft */
+  fftND ((float *)Ore[0].matrix.buffer, (float *)Oim[0].matrix.buffer, 2, Ire[0].header.Naxis, forward);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.old.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.old.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fft2d.old.c	(revision 17422)
@@ -0,0 +1,113 @@
+# include "data.h"
+
+int fft2dold (int argc, char **argv) {
+  
+  int i, N, Nx, Ny, Naxis[2];
+  int Npix, ZeroImaginary, isign;
+  float *t1, *t2, *out, *temp;
+  Buffer *Ire, *Iim, *Ore, *Oim;;
+
+  isign = 1;
+  if ((N = get_argument (argc, argv, "-inverse"))) {
+    remove_argument (N, &argc, argv);
+    isign = -1;
+  }
+
+  if ((argc != 6) || (strcmp (argv[3], "to"))) {
+    gprint (GP_ERR, "USAGE: fft2d (real) (imag) to (real) (imag)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  Iim = NULL;
+  ZeroImaginary = TRUE; /* Input(imaginary) may be 0, in which case we create a 0 filled image */
+  if (!strcmp (argv[2], "0")) { 
+  } else {
+    ZeroImaginary = FALSE;
+    if ((Iim = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  }    
+  if ((Ire = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Ore = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Oim = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* free up output space */
+  gfits_free_matrix (&Ore[0].matrix);
+  gfits_free_header (&Ore[0].header);
+  gfits_free_matrix (&Oim[0].matrix);
+  gfits_free_header (&Oim[0].header);
+
+  /* get image dimensions, check value */
+  Npix = Ire[0].header.Naxis[0]*Ire[0].header.Naxis[1];
+  Nx = Ire[0].header.Naxis[0];
+  Ny = Ire[0].header.Naxis[1];
+  Naxis[0] = Ny; Naxis[1] = Nx;
+  if (!IsBinaryOld (Npix)) {
+    gprint (GP_ERR, "dimensions are not binary!\n");
+    return (FALSE);
+  }
+  
+  /* create working space */
+  t1 = (float *) Ire[0].matrix.buffer;
+  ALLOCATE (temp, float, 2*Npix);
+  out = temp;
+
+  /* copy input to working space */
+  if (ZeroImaginary) {
+    for (i = 0; i < Npix; i++, t1++) {
+      *out = *t1;
+      out++;
+      *out = 0;
+      out++;
+    }
+  } else {
+    t2 = (float *) Iim[0].matrix.buffer;
+    for (i = 0; i < Npix; i++, t1++, t2++) {
+      *out = *t1;
+      out++;
+      *out = *t2;
+      out++;
+    }
+  } 
+    
+  /* run the fft */
+  fftNold (temp, Naxis, 2, isign);
+
+  /* fix up output headers (real) */
+  gfits_copy_header (&Ire[0].header, &Ore[0].header);
+  gfits_modify (&Ore[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Ore[0].header, "NAXIS2", "%d", 1, Ny);
+  Ore[0].header.Naxis[0] = Nx;
+  Ore[0].header.Naxis[1] = Ny;
+  Ore[0].bitpix = Ire[0].bitpix;
+  Ore[0].unsign = Ire[0].unsign;
+  Ore[0].bscale = Ire[0].bscale;
+  Ore[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Ore[0].header, &Ore[0].matrix);
+
+  /* fix up output headers (imaginary) */
+  gfits_copy_header (&Ire[0].header, &Oim[0].header);
+  gfits_modify (&Oim[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&Oim[0].header, "NAXIS2", "%d", 1, Ny);
+  Oim[0].header.Naxis[0] = Nx;
+  Oim[0].header.Naxis[1] = Ny;
+  Oim[0].bitpix = Ire[0].bitpix;
+  Oim[0].unsign = Ire[0].unsign;
+  Oim[0].bscale = Ire[0].bscale;
+  Oim[0].bzero  = Ire[0].bzero;
+  gfits_create_matrix (&Oim[0].header, &Oim[0].matrix);
+
+  /* move data from working space to output buffers */
+  out = temp;
+  t1 = (float *) Ore[0].matrix.buffer;
+  t2 = (float *) Oim[0].matrix.buffer;
+  for (i = 0; i < Npix; i++, t1++, t2++) {
+    *t1 = *out / Npix;
+    out ++;
+    *t2 = *out / Npix;
+    out ++;
+  }    
+
+  free (temp);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit.c	(revision 17422)
@@ -0,0 +1,227 @@
+# include "data.h"
+
+int fit (int argc, char **argv) {
+  
+  double **c, **b, *s, X, Y, dY, dY2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int i, j, nterm, mterm, order, Npt, Nmask;
+  int N, Weight, Quiet, ClipNiter;
+  Vector *xvec, *yvec, *dyvec;
+  float *x, *y, *dy, *yf, *yfit;
+  char name[64], *mask;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dy = NULL;
+  dyvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dyvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: fit x y order [-dy wt] [-quiet/-q] [-clip Nsigma Niter]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (Weight && xvec[0].Nelements != dyvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  
+  /* nterm is number of polynomial terms, starting at x^0 */
+  order = atof (argv[3]);
+  nterm = order + 1;
+  mterm = 2*nterm;
+
+  ALLOCATE (yfit, float, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  ALLOCATE (s, double, mterm);
+  ALLOCATE (b, double *, nterm);
+  ALLOCATE (c, double *, nterm);
+  for (i = 0; i < nterm; i++) {
+    ALLOCATE (c[i], double, nterm);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  Nmask = 0;
+  sigma = 0.0;
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    memset (s, 0, mterm*sizeof(double));
+    for (i = 0; i < nterm; i++) {
+      memset (c[i], 0, nterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+
+    /* perform linear fit */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    if (Weight) dy = dyvec[0].elements;
+
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!(finite(*x) && finite(*y))) continue;
+      dY = 1.0;
+      if (Weight) { 
+	dY = 1.0 / SQ(*dy);
+	dy ++;
+      }
+      X = 1*dY;
+      Y = *y*dY;
+      for (j = 0; j < nterm; j++) {
+	s[j] += X;
+	b[j][0] += Y;
+	X = X * (*x);
+	Y = Y * (*x);
+      }
+      for (j = nterm; j < mterm; j++) {
+	s[j] += X;
+	X = X * (*x);
+      }
+    }
+    for (i = 0; i < nterm; i++) {
+      for (j = 0; j < nterm; j++) {
+	c[i][j] = s[i + j];
+      }
+    }
+    if (!dgaussjordan (c, b, nterm, 1)) goto escape;
+
+    /* generate fitted values */
+    x = xvec[0].elements;
+    yf = yfit;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, yf++) {
+      if (!finite(*x)) continue;
+      *yf = 0;
+      X = 1;
+      for (j = 0; j < order + 1; j++) {
+	*yf += b[j][0]*X;
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    yf = yfit;
+    dY = dY2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, yf++) {
+      if (mask[i]) continue;
+      if (!finite(*x)) continue;
+      dY  += (*y - *yf);
+      dY2 += SQ(*y - *yf);
+      Npt ++;
+    }
+    mean  = dY / Npt;
+    sigma = sqrt (fabs(dY2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    /* mask outlier points */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    yf = yfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, yf++) {
+      dY = (*y - *yf);
+      if (fabs(dY) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+  }
+      
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "y = ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, b[i][0]);
+    if (!Quiet) gprint (GP_ERR, "%f x^%d ", b[i][0], i);
+  }
+  sprintf (name, "Cn");
+  set_variable (name, (double) order);
+  
+  /* print & save basic fit parameters */
+  if (!Quiet) gprint (GP_ERR, "\n");
+  if (!Quiet) gprint (GP_ERR, "    ");
+  for (i = 0; i < nterm; i++) {
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(c[i][i]));
+    if (!Quiet) gprint (GP_ERR, "%f     ", sqrt(c[i][i]));
+  }
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  set_variable ("dC", sigma);
+  set_variable ("Cnv", (xvec[0].Nelements - Nmask));
+
+  /* save mask and yfit for testing? */
+  if (1) { 
+    Vector *fvec, *mvec;
+    if ((fvec = SelectVector ("yfit", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    if ((mvec = SelectVector ("mask", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    free (fvec[0].elements);
+    fvec[0].elements = yfit;
+    fvec[0].Nelements = xvec[0].Nelements;
+    mvec[0].Nelements = xvec[0].Nelements;
+
+    REALLOCATE (mvec[0].elements, float, xvec[0].Nelements);
+    for (i = 0; i < xvec[0].Nelements; i++) {
+      mvec[0].elements[i] = mask[i];
+    }
+  } else {
+    free (yfit);
+  }
+  free (mask);
+
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (TRUE);
+
+escape:
+  for (i = 0; i < nterm; i++) {
+    free (b[i]);
+    free (c[i]);
+  }
+  free (b);
+  free (c);
+  free (s);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit2d.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit2d.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/fit2d.c	(revision 17422)
@@ -0,0 +1,265 @@
+# include "data.h"
+
+int fit2d (int argc, char **argv) {
+  
+  double **c, **b, **s, X, Y, dZ, dZ2;
+  double ClipNSigma, mean, sigma, maxsigma;
+  int k, K, i, j, n, Npt, Nmask, nx, ny, nterm, mterm, wterm, order;
+  int N, Weight, Quiet, ClipNiter, VERBOSE;
+  float *x, *y, *z, *dz, *zfit, *zf; 
+  char name[64], *mask;
+  Vector *xvec, *yvec, *zvec, *dzvec;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  ClipNSigma = 0;
+  ClipNiter  = 1;
+  if ((N = get_argument (argc, argv, "-clip"))) {
+    remove_argument (N, &argc, argv);
+    ClipNSigma = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    ClipNiter  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  dz = NULL;
+  dzvec = NULL;
+  Weight = FALSE;
+  if ((N = get_argument (argc, argv, "-dz"))) {
+    remove_argument (N, &argc, argv);
+    if ((dzvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+    Weight = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: fit x y z order [-dz wt]\n");
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  if (Weight && xvec[0].Nelements != dzvec[0].Nelements) {
+    gprint (GP_ERR, "vectors must have same length\n");
+    return (FALSE);
+  }
+  
+  order = atof (argv[4]);
+  nterm = order + 1;
+  wterm = nterm*(nterm + 1)/2;
+  mterm = 2*order + 1;
+
+  ALLOCATE (zfit, float, xvec[0].Nelements);
+  ALLOCATE (mask, char, xvec[0].Nelements);
+  memset (mask, 0, xvec[0].Nelements);
+
+  /* allocate the summation matrices */
+  ALLOCATE (s, double *, mterm);
+  ALLOCATE (b, double *, wterm);
+  ALLOCATE (c, double *, wterm);
+  for (i = 0; i < wterm; i++) {
+    ALLOCATE (c[i], double, wterm);
+    ALLOCATE (b[i], double, 1);
+  }
+  for (i = 0; i < mterm; i++) {
+    ALLOCATE (s[i], double, mterm);
+  }
+
+  for (N = 0; N < ClipNiter; N++) {
+
+    /* init registers for current pass */
+    // XXX this was incorrectly using nterm (missing 1 row)
+    for (i = 0; i < wterm; i++) {
+      memset (c[i], 0, wterm*sizeof(double));
+      memset (b[i], 0, sizeof(double));
+    }
+    for (i = 0; i < mterm; i++) {
+      memset (s[i], 0, mterm*sizeof(double));
+    }
+
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    z = zvec[0].elements;
+    if (Weight) dz = dzvec[0].elements;
+
+    /* add up the x,y values */
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = 1*dZ;
+      for (ny = 0; ny < mterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < mterm - ny; nx++) {
+	  s[nx][ny] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* add up the z values */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    z = zvec[0].elements;
+    for (i = 0; i < xvec[0].Nelements; i++, x++, y++, z++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ = 1.0;
+      if (Weight) { 
+	dZ = 1.0 / SQ(*dz);
+	dz ++;
+      }
+      Y = X = *z*dZ;
+      for (j = 0, ny = 0; ny < nterm; ny++) {
+	X = Y;
+	for (nx = 0; nx < nterm - ny; nx++, j++) {
+	  b[j][0] += X;
+	  X = X * (*x);
+	}
+	Y = Y * (*y);
+      }
+    }
+
+    /* re-sort mterm x mterm matrix to wterm matrix */
+    for (k = j = 0; j < nterm; j++) {
+      for (i = 0; i < nterm - j; i++, k++) {
+	for (K = ny = 0; ny < nterm; ny++) {
+	  for (nx = 0; nx < nterm - ny; nx++, K++) {
+	    c[K][k] = s[nx+i][ny+j];
+	  }
+	}
+      }
+    }
+
+    dgaussjordan (c, b, wterm, 1);
+
+    /** test print **/
+    if (VERBOSE) {
+      for (i = ny = 0; ny < nterm; ny++) {
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  gprint (GP_ERR, "x^%d y^%d: %g\n", nx, ny, b[i][0]);
+	}
+      }
+    }
+
+    /* the b[][0] terms are in the following order:
+       y^0 x^0, y^0 x^1, ... y^0 x^N
+       y^1 x^0, y^1 x^1, ... y^1 x^N
+       ...
+       y^N x^0, y^N x^1, ... y^N x^N
+    */
+    /* generate fitted values */
+    x = xvec[0].elements;
+    y = yvec[0].elements;
+    zf = zfit;
+    for (n = 0; n < xvec[0].Nelements; n++, x++, y++, zf++) {
+      if (!finite(*x) || !finite(*y)) continue;
+      *zf = 0;
+      Y = X = 1;
+      for (i = ny = 0; ny < nterm; ny++) {
+	Y = X;
+	for (nx = 0; nx < nterm - ny; nx++, i++) {
+	  *zf += b[i][0]*Y;
+	  Y = Y * (*y);
+	}
+	X = X * (*x);
+      }
+    }
+
+    /* measure fit residual scatter */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    z  = zvec[0].elements;
+    zf = zfit;
+    dZ = dZ2 = 0;
+    for (i = Npt = 0; i < xvec[0].Nelements; i++, x++, y++, z++, zf++) {
+      if (mask[i]) continue;
+      if (!finite(*x) || !finite(*y)) continue;
+      dZ  += (*z - *zf);
+      dZ2 += SQ(*z - *zf);
+      Npt ++;
+    }
+    mean  = dZ / Npt;
+    sigma = sqrt (fabs(dZ2/Npt - SQ(mean)));
+    maxsigma = ClipNSigma * sigma;
+
+    if (VERBOSE) gprint (GP_ERR, "mean: %g, sigma: %g, maxsigma: %g\n", mean, sigma, maxsigma);
+
+    /* mask outlier points */
+    x  = xvec[0].elements;
+    y  = yvec[0].elements;
+    z  = zvec[0].elements;
+    zf = zfit;
+    Nmask = 0;
+    for (i = 0; ClipNSigma && (i < xvec[0].Nelements); i++, x++, y++, z++, zf++) {
+      dZ = (*z - *zf);
+      if (fabs(dZ) > maxsigma) {
+	mask[i] = TRUE;
+	Nmask ++;
+      } else {
+	mask[i] = FALSE;
+      }	
+    }
+    if (VERBOSE) gprint (GP_ERR, "pass: %d, Nmask: %d\n", N, Nmask);
+  }
+
+  if (!Quiet) gprint (GP_ERR, "z = ");
+  for (N = ny = 0; ny < nterm; ny++) {
+    for (nx = 0; nx < nterm - ny; nx++, N++) {
+      sprintf (name, "CX%dY%d", nx, ny);
+      set_variable (name, b[N][0]);
+      if (!Quiet) gprint (GP_ERR, "%f x^%d y^%d  ", b[N][0], nx, ny);
+    }
+  }
+  sprintf (name, "Cnn");
+  set_variable (name, (double) order);
+  
+  if (!Quiet) gprint (GP_ERR, "\n");
+
+  /* free internal data */
+  free (zfit);
+  free (mask);
+
+  for (i = 0; i < wterm; i++) {
+    free (c[i]);
+    free (b[i]);
+  }
+  free (b);
+  free (c);
+
+  for (i = 0; i < mterm; i++) {
+    free (s[i]);
+  }
+  free (s);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussdeviate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussdeviate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussdeviate.c	(revision 17422)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int gaussdeviate (int argc, char **argv) {
+  
+  int i, Npts;
+  double mean, sigma;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  mean = atof (argv[3]);
+  sigma = atof (argv[4]);
+
+  vec[0].Nelements = Npts;
+  REALLOCATE (vec[0].elements, float, Npts);
+
+  gauss_init (2048);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements[i] = rnd_gauss (mean, sigma);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussdeviate (vector) Npts mean sigma\n");
+  return (FALSE);
+    
+}
+
+double int_gauss (int i);
+
+int gaussintegral (int argc, char **argv) {
+  
+  int i, Npts;
+  double mean, sigma;
+  Vector *vec;
+
+  if (argc != 5) goto usage;
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  Npts = atoi (argv[2]);
+  mean = atof (argv[3]);
+  sigma = atof (argv[4]);
+
+  vec[0].Nelements = Npts;
+  REALLOCATE (vec[0].elements, float, Npts);
+
+  gauss_init (Npts);
+  for (i = 0; i < Npts; i++) {
+    vec[0].elements[i] = int_gauss (i);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussintegral Npts mean sigma\n");
+  return (FALSE);
+    
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussj.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussj.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gaussj.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "data.h"
+
+int gaussjordan (int argc, char **argv) {
+  
+  float *m, *v;
+  double **a, **b;
+  int i, j, N, status, QUIET;
+  Vector *B;
+  Buffer *A;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) goto usage;
+
+  if ((A = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);    
+  if ((B = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  N = B[0].Nelements;
+  if (A[0].matrix.Naxis[0] != N) goto usage;
+  if (A[0].matrix.Naxis[1] != N) goto usage;
+  
+  ALLOCATE (a, double *, N);
+  ALLOCATE (b, double *, N);
+  for (i = 0; i < N; i++) {
+    ALLOCATE (a[i], double, N);
+    ALLOCATE (b[i], double, 1);
+  }
+
+  m = (float *) A[0].matrix.buffer;
+  v = B[0].elements;
+  for (i = 0; i < N; i++) {
+    for (j = 0; j < N; j++) {
+      a[i][j] = m[i+j*N];
+    }
+    b[i][0] = v[i]; 
+  }
+
+  status = dgaussjordan (a, b, N, 1);
+
+  for (i = 0; i < N; i++) {
+    for (j = 0; j < N; j++) {
+       m[i+j*N] = a[i][j];
+    }
+    v[i] = b[i][0]; 
+  }
+
+  for (i = 0; i < N; i++) {
+    free (a[i]);
+    free (b[i]);
+  }
+  free (a);
+  free (b);
+
+  if (!status && !QUIET) {
+      gprint (GP_ERR, "failure in matrix solution\n");
+  }
+  return (status);
+
+ usage:
+  gprint (GP_ERR, "USAGE: gaussj A B\n");
+  gprint (GP_ERR, "  solves Ax = B, returns 1/A in A and x in B\n");
+  gprint (GP_ERR, "  A must be square, B same dimensions\n");
+  return (FALSE);
+    
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grid.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grid.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grid.c	(revision 17422)
@@ -0,0 +1,203 @@
+# include "data.h"
+
+int grid (int argc, char **argv) {
+  
+  int j, kapa, N, MinorTick, MajorTick;
+  Vector Xvec, Yvec;
+  double range, lrange, factor, mantis, fmantis, power, major, minor, first, next;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  MajorTick = TRUE;
+  MinorTick = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    MinorTick = TRUE;
+  }
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: grid [-n graph]\n");
+    return (FALSE);
+  }
+
+  N = 0;
+  Xvec.Nelements = 200;
+  Yvec.Nelements = 200;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+
+  major = minor = 1;
+  range = graphmode.xmax - graphmode.xmin;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    major = 0.5 * power;
+    minor = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    major = 1.0 * power;
+    minor = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    major = 1.0 * power;
+    minor = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    major = 2.0 * power;
+    minor = 0.5 * power;
+  }
+  if (graphmode.xmin > 0)
+    first = minor + minor*((int)(graphmode.xmin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.xmin/minor));
+  if (minor*((int)(graphmode.xmin/minor)) == graphmode.xmin) {
+    first = graphmode.xmin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.xmax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymin;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = next;
+	Yvec.elements[N] = graphmode.ymax;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  range = graphmode.ymax - graphmode.ymin;
+  lrange = log10(MAX(fabs(range), 1e-30));
+  factor = (int) (lrange);
+  if (lrange < 0) { factor -= 1; }
+  mantis = lrange - factor;
+  power = pow(10.0, factor);
+  fmantis = pow(10.0, mantis);
+  if ((fmantis >= 1.0) && (fmantis <=  2.0)) {
+    major = 0.5 * power;
+    minor = 0.1 * power;
+  }
+  if ((fmantis > 2.0) && (fmantis <=  4.0)) {
+    major = 1.0 * power;
+    minor = 0.2 * power;
+  }
+  if ((fmantis > 4.0) && (fmantis <=  6.0)) {
+    major = 1.0 * power;
+    minor = 0.5 * power;
+  }
+  if ((fmantis > 6.0) && (fmantis <=  10.0)) {
+    major = 2.0 * power;
+    minor = 0.5 * power;
+  }
+  if (graphmode.ymin > 0)
+    first = minor + minor*((int)(graphmode.ymin/minor));
+  else 
+    first = -minor + minor*((int)(graphmode.ymin/minor));
+  if (minor*((int)(graphmode.ymin/minor)) == graphmode.ymin) {
+    first = graphmode.ymin;
+  }
+  
+  for (j = 0, next = first; next <= graphmode.ymax; j++) {
+    if ((fabs((int)(next/major) - (next/major)) < 0.5*(minor/major)) || (fabs ((int)((next + 0.5*minor)/major) - (next/major)) < 0.5*(minor/major))) {
+      if (MajorTick) {
+	/* major tick */
+	Xvec.elements[N] = graphmode.xmin;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = graphmode.xmax;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    } else {
+      if (MinorTick) {
+	/* minor tick */
+	Xvec.elements[N] = graphmode.xmin;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+	Xvec.elements[N] = graphmode.xmax;
+	Yvec.elements[N] = next;
+	N++;
+	if (N == Xvec.Nelements) {
+	  Xvec.Nelements += 200;
+	  Yvec.Nelements += 200;
+	  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+	  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+	}
+      }
+    }
+    next += minor;
+  }
+
+  Xvec.Nelements = Yvec.Nelements = N;
+  graphmode.style = 2; /* points */
+  graphmode.ptype = 100; /* connect a pair */
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gridify.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gridify.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/gridify.c	(revision 17422)
@@ -0,0 +1,82 @@
+# include "data.h"
+
+int gridify (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb, Normalize, N;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *buf, *val, *x, *y, *z;
+  int *Nval;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  Normalize = TRUE;
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = FALSE;
+  }
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: gridify x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+    return (FALSE);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  Xmin = atof (argv[5]);
+  Xmax = atof (argv[6]);
+  dX   = atof (argv[7]);
+
+  Ymin = atof (argv[8]);
+  Ymax = atof (argv[9]);
+  dY   = atof (argv[10]);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 1;
+  
+  gfits_free_matrix (&bf[0].matrix);
+  gfits_free_header (&bf[0].header);
+  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (bf[0].file, "(empty)");
+
+  ALLOCATE (val, float, Nx*Ny);
+  bzero (val, Nx*Ny*sizeof(float));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] += *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  buf = (float *) bf[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++) {
+    if (Normalize) {
+      if (Nval[i] == 0) {
+	buf[i] = 0;
+	continue;
+      }
+      buf[i] = val[i] / Nval[i];
+    } else {
+      buf[i] = val[i];
+    }
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grow.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grow.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/grow.c	(revision 17422)
@@ -0,0 +1,50 @@
+# include "data.h"
+
+int grow (int argc, char **argv) {
+  
+  int i, j, nx, ny, npix, nsum, Nx, Ny;
+  float *input, *output;
+  Buffer *in;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: grow (input) (npix)\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  npix = atoi (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (output, float, Nx*Ny);
+  memset (output, 0, Nx*Ny*sizeof(float));
+
+  input = (float *) in[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      nsum = 0;
+      for (ny = -1; ny <= +1; ny++) {
+	if (j + ny < 0) continue;
+	if (j + ny >= Ny) continue;
+	for (nx = -1; nx <= +1; nx++) {
+	  if (i + nx < 0) continue;
+	  if (i + nx >= Nx) continue;
+	  if (!nx && !ny) continue;
+	  if (input[i + nx + Nx*(j + ny)] == 0) continue;
+	  nsum ++;
+	}
+      }
+      if (nsum >= npix) {
+	output[i + j*Nx] = 1;
+      } else {
+	output[i + j*Nx] = input[i + j*Nx];
+      }
+    }
+  }
+  
+  free (in[0].matrix.buffer);
+  in[0].matrix.buffer = (char *) output;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Graphics
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Graphics	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Graphics	(revision 17422)
@@ -0,0 +1,20 @@
+
+  Kapa, the graphics window.
+
+  Kapa is the program which Status uses to display plots on an X
+terminal.  There are actually 5 graphics windows available to Status,
+though several functions write to specific windows by default.  All
+plots of sky coordinates are displayed on window 0, while 
+functions which plot other types of data use window 1.  Most functions
+have an option -n which allows the user to specify which Kapa window.
+If it is not specified, the last window used will receive the action.
+
+  Below are the available Resources.  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*geometry:				540x540+10+10
+
+  See Also: box, clear, limits, plot
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Kii
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Kii	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Kii	(revision 17422)
@@ -0,0 +1,57 @@
+
+
+  Kii (= "picture" in Hawaiian) is the program which Mana uses to
+display images on an X terminal.  The window consists of a main region
+where the image is displayed, a small "zoom box" where a magnified
+view of the region around the cursor is shown, a colorbar showing the
+current color map across the top of the window, a status box where the
+coordinates and pixel values are displayed, and several buttons.
+Clicking with the mounse in the different regions produces different
+effects.
+
+  Image Window:  Clicking with the left mouse button (mouse-1)
+recenters the image at the specified location.  Clicking with the
+right button (mouse-3) also recenters, but increases the zoom factor
+by one.  Clicking with the middle button centers and decreases the
+zoom factor.  
+
+  Colorbar: Clicking and draging with the left button (mouse-1) alters
+the color mapping.  Moving left-to-right slides the center of the
+color scale with the mouse.  Moving up-and-down squeezes or expands
+the color scale.  The middle button resets the color mapping.
+Clicking and dragging side-to-side with the right button expands or
+squeezes the color mapping.  **Note: the dynamic colormap cannot be
+used with a 24 or 32 bit visual.  The standard Linux XFree86 server
+does not support 8 bit visuals in 24 or 32 bit/pixel mode.  In this
+case, the Kii window must resort to a static colormap with the
+colorbar disabled.  In Linux, it is possible to run startx with the
+following command-line options to force the 8 bit/pixel mode:
+# startx -- -bpp 8 
+This assumes that you have an 8bpp Display mode set in the XF86Config
+file.  If not, you can just duplicate the 24 or 32 bpp Display modes
+which will enable this option.
+
+  Buttons:
+    PS: this button creates a PostScript version of the image.
+    Grey: this button makes the colormap a greyscale.
+    Rainbow: this button makes the colormap a rainbow.
+    Puns: this button make the colormap a blue and yellow mapping.
+    Recenter: this button recenters the image and resets the zoom.
+
+  Kii also recognizes several X Resource values:
+
+  Below are the available Resources (with Gene's default values).  Unlike
+most standard X programs, the geometry resource specifies the size of
+the Image Window, not the borders of the entire window.  Valid values
+for the colormap are: greyscale, grayscale (photonegative), -greyscale,
+-grayscale (photopositive), Puns, GoBears (buff 'n blue colormap),
+Rainbow (a rainbow map).  
+
+  kii*Foreground:			white
+  kii*Background:			maroon
+  kii*Colormap:				greyscale
+  kii*geometry:				540x540+10+10
+  
+
+  See Also: tv, cursor
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Math
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Math	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/Math	(revision 17422)
@@ -0,0 +1,58 @@
+
+  Opihi shell math operations:
+
+  Mana will evaluate scalar (1D) arithmetic expresions in two
+situations.  To set a variable equal to a math operation, use the
+syntax is:
+
+  $foo = expression
+
+where "expression" is an arithmetic expression on numbers and
+variables, including a variety of functions (see below).  After
+performing the arithmetic and setting the variable to the resulting
+value, mana returns the prompts without performing any further
+command.  Mana will also evaluate any arithmetic expression enclosed
+in curly brackets before attempting to perform the given command.  For
+example, if the variables $MEAN and $SIGMA contain the mean and
+standard deviation of an image, it would be possible to display the
+image with a zero and range based on $MEAN and $SIGMA like this:
+
+  tv image {$MEAN - 1.5*$SIGMA} {5*$SIGMA}
+
+
+The two expressions in curly brackets are evaluated and the resulting
+numbers passed as arguments to the "tv" command.
+
+  See Also:  Variables, tv
+
+
+list of valid math operations:
+
+  binary operators:
+  * (times)
+  / (divided by)
+  + (plus)
+  - (minus)
+  ^ (to the power of: 2^3 = 8)
+
+  unary operators:
+  exp (e to the power of)
+  ten (10 to the power of)
+  ln  (natural logarithm)
+  log (log base 10)
+
+  angles in radians:
+  sin (sine)
+  cos (cosine)
+  tan (tangent)
+  asin (arcsin)
+  acos (arccos)
+  atan (arctan)
+
+  angles in degrees:
+  dsin (sine)
+  dcos (cosine)
+  dtan (tangent)
+  dasin (arcsin)
+  dacos (arccos)
+  datan (arctan)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/applyfit
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/applyfit	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/applyfit	(revision 17422)
@@ -0,0 +1,10 @@
+
+  applyfit x y
+
+  apply the results of a polynomial fit (stored in variables 
+  $C0, $C1, etc, with $Cn representing the order of the fit).
+  The second vector will contain the function value at the 
+  corresponding x coordinates of the first vector.
+
+  See also: fit
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/box
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/box	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/box	(revision 17422)
@@ -0,0 +1,11 @@
+
+  box [-n Nwindow]
+
+  draw a coordinate box on the current kapa window, or on the window
+specified by -n (0 - 4).
+
+  See also: Kapa
+
+
+
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/buffers
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/buffers	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/buffers	(revision 17422)
@@ -0,0 +1,9 @@
+
+  buffers
+
+  buffers gives information about the currently allocated buffers.  
+A * next to the filename means the data has been altered from the
+originally read in image.
+
+  See also: memory
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/center
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/center	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/center	(revision 17422)
@@ -0,0 +1,10 @@
+
+  center (xpix) (ypix) [magnification]
+
+  "center" centers the Kii window at the specified pixel coordinates.
+Specifying a magnification will change the image scale to the
+specified magnification.  If you do not specify a magnification
+(default), the image scale will remain the same.
+
+  See also: Kii, tv
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clear
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clear	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clear	(revision 17422)
@@ -0,0 +1,5 @@
+
+   clear
+
+   clear the Kapa window
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clip
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clip	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/clip	(revision 17422)
@@ -0,0 +1,8 @@
+
+  clip (buffer) [min Vmin max Vmax] [-inf val] [-nan val]
+
+  clip values in an image.  A min/max clip can be applied, 
+  in which case all values above max are set to Vmax and all
+  below min are set to Vmin.  -nan and -inf flags will set all 
+  instances of NaN or any non-finite values to the given value.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/close
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/close	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/close	(revision 17422)
@@ -0,0 +1,14 @@
+
+
+   close [-n win] [-g Kapa win] [-i Kii win]
+
+   Closes the specified graphics window
+
+   Default: the most recently activated window (only works
+    for the first call)
+
+   [win]: the index of the window to close
+
+   -n: any graphics or plotting window
+   -g: the Kapa plotting window
+   -i: the Kii image window
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/concat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/concat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/concat	(revision 17422)
@@ -0,0 +1,7 @@
+
+   concat v1 v2
+
+   Concatenates the values of vector v1 to the end of vector
+    v2, increasing the length of v2.
+
+   See also: subset, set
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/contour
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/contour	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/contour	(revision 17422)
@@ -0,0 +1,13 @@
+
+  contour <buffer> (overlay) level [Npix]
+
+  "contour" makes a contour plot from the given <buffer> at the
+specified level.  If Npix is specified, the image is rebinned by a
+factor of Npix in each direction.  The contour is drawn as a series of
+lines on the specified overlay.  The new lines are added to any
+existing shapes.  (Use "erase" first to erase all objects if desired).
+
+Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+  See also: load, save, erase
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/create
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/create	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/create	(revision 17422)
@@ -0,0 +1,9 @@
+
+   create (vector) (start) (end) [delta]
+
+   Create a vector of uniformly spaced values, starting at
+   (start) and going to (end).  By default the spacing is 1, but
+   may be chosen with the [delta] option.
+
+   Note: A vector cannot be created if an image buffer already
+    exists with the same name
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cumulative
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cumulative	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cumulative	(revision 17422)
@@ -0,0 +1,8 @@
+
+
+   cumulative (invec) (outvec)
+
+   Creates a vector (outvec) where the element N is the sum of the elements
+    0-N of (invec)
+
+   Note: The final element of (outvec) is always 0
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cursor
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cursor	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cursor	(revision 17422)
@@ -0,0 +1,13 @@
+
+  cursor
+
+  place cursor coordinates (and values ??) into Mana variables.  to do
+this, type cursor, then place the cursor on the desired spot in the
+Ki'i window and type a digit (0 - 9).  The coordinates of the cursor
+are then placed in the variables $Xn and $Yn (where n is the digit you
+typed).  To exit the cursor mode, type "q" (or "Q") in the Ki'i
+window.  
+
+  See Also: Kii, tv, center
+
+ 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cut
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cut	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/cut	(revision 17422)
@@ -0,0 +1,9 @@
+
+   cut buffer <X vector> <Y vector> <X|Y> sx sy nx ny
+
+   take a cut from an image and place it in a pair of vectors.
+   the <X vector> gets the pixel coordinate in the given direction,
+   the <Y vector> gets the pixel values.  <X|Y> specifies the 
+   direction of the cut.  the region sx, sy, nx, ny specifies the
+   region for the cut, with summation in the cross-direction.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/datafile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/datafile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/datafile	(revision 17422)
@@ -0,0 +1,6 @@
+
+   datafile (filename)
+
+   define a data file for subsequent vector reads.
+
+   See also: read
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/delete
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/delete	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/delete	(revision 17422)
@@ -0,0 +1,10 @@
+
+  delete (buffer/vector/variable)
+
+  Delete the named buffer/vector/variable.
+
+  Warning: no second chances are given!
+
+  Note: Delete will remove the buffer/vector of a
+   given name first, then delete the variable of
+   the same name upon the second call.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimendown
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimendown	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimendown	(revision 17422)
@@ -0,0 +1,7 @@
+
+
+   dimendown (buffer) (vector) [-x/-y]
+
+   Compresses an image into a vector
+
+   The vector can be given either the x or y coordinates of the image
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimenup
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimenup	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dimenup	(revision 17422)
@@ -0,0 +1,5 @@
+
+
+   dimenup (vector) (buffer) (Nx) (Ny)
+
+   Expands a vector into an image with dimensions Nx,Ny
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dot
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dot	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/dot	(revision 17422)
@@ -0,0 +1,7 @@
+
+
+   dot (x) (y)
+
+   Plots a single point
+
+   Uses the same style options as plot
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/erase
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/erase	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/erase	(revision 17422)
@@ -0,0 +1,8 @@
+
+  erase (overlay)
+
+  "erase" erases all objects on the specified overlay.  
+  Valid overlays may be: all, red, green, blue, yellow.
+
+  See also: load, save, contour
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/extract
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/extract	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/extract	(revision 17422)
@@ -0,0 +1,12 @@
+
+  extract <from> <to> sx sy nx ny sx sy nx ny
+
+  extract takes a portion of an image (buffer <from>) and creates a
+new image (buffer <to>).  The source region is defined by the first
+set of (sx sy nx ny), the resulting image and the location of the
+extracted image are defined by the second (sx sy nx ny).  This allows
+a portion of an image to be overlayed at a particular location in a
+larger image.
+
+  See also: Kii, tv
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/fit
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/fit	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/fit	(revision 17422)
@@ -0,0 +1,15 @@
+
+   fit (-q) x y (order) (-dy wt) (-clip sig N)
+
+   perform a lease-square polynomial fit to the data defined 
+   by vectors x and y.  the coefficients are placed in the variables
+   $C0, $C1, ..., and the order is placed in $Cn.
+
+   order: order of fit
+   -q: quiets the fit (no output to window)
+   -dy: take into account the error vector wt
+   -clip: remove outliers beyond sig*sigma from the fit;
+           perform N iterations of this
+
+   See also: applyfit
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/grid
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/grid	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/grid	(revision 17422)
@@ -0,0 +1,6 @@
+
+   grid (overlay) (buffer)
+
+   draw a coordinate grid for the given buffer in the 
+   given overlay in Kii.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/header
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/header	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/header	(revision 17422)
@@ -0,0 +1,8 @@
+
+   header (buffer)
+
+   print the header information for the given buffer.  The header
+   information is Meta-information associated with an image.  It is
+   well-defined for the FITS images, but may not exist for some of the
+   other possible data types.  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/histogram
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/histogram	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/histogram	(revision 17422)
@@ -0,0 +1,8 @@
+
+   histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max]
+
+   calculate a histogram of the image pixel values in the given
+   buffer, optionally constrained to the given region, with optional
+   max and min values.  the results are placed in the vectors x and y,
+   which contain the pixel values and the number of occurences.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/integrate
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/integrate	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/integrate	(revision 17422)
@@ -0,0 +1,10 @@
+
+   integrate (x) (y) (start) (end)
+
+   Perform an integration of (y) as a function of (x) from (start) to (end)
+
+   The result is placed into the variable: $sum
+
+   Caution!: Program uses the front end of each x bin to approximate the integral.
+    Very coarse bins (delta x = 1) over small regions (x=1->5) can introduce
+    errors as large as 20%. Fine bins are reccommended.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/interpolate
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/interpolate	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/interpolate	(revision 17422)
@@ -0,0 +1,5 @@
+
+   interpolate (xin) (yin) (xout) (yout)
+
+   Performs an interpolation of (xin) and (yin) onto the new grid (xout),
+    producing the corresponding y values in (yout)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/kern
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/kern	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/kern	(revision 17422)
@@ -0,0 +1,4 @@
+
+   kern buffer (kernel file or -)
+
+   apply a 3x3 kernel to the image.  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/keyword
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/keyword	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/keyword	(revision 17422)
@@ -0,0 +1,11 @@
+
+  keyword <buffer> (KEYWORD) [variable] [-w value]
+
+  "keyword" extracts the specified keyword from the header of the
+   specified buffer.  If a third word is listed, the value of the
+   keyword is stored in a variable with the given name.  If the -w
+   option is given, the value is written to the header keyword.
+
+   The header Meta-data is well-defined for FITS, but not necessarily
+   for other data types.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/labels
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/labels	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/labels	(revision 17422)
@@ -0,0 +1,23 @@
+
+   labels 
+
+   write a label on the Kapa window.  there are many options:
+
+   -fn font size -- define the font (may be times, helvetica, courier)
+
+   -x "a long line" -- label on the bottom x-axis
+
+   +x "a long line" -- label on the top x-axis
+
+   -y "a long line" -- label on the left y-axis
+
+   +y "a long line" -- label on the right y-axis
+
+   -ul "a long line" -- label on the upper left of plot
+
+   -ll "a long line" -- label on the lower left of plot
+
+   -ur "a long line" -- label on the upper right of plot
+
+   -lr "a long line" -- label on the lower right of plot
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/limits
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/limits	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/limits	(revision 17422)
@@ -0,0 +1,12 @@
+
+   limits min max min max
+   limits x y
+   limits x min max
+   limits min max y
+   
+   set Kapa plot limits.  The x and y axis limits may be explicitly
+   set (first example), or they may be assigned based on the range of
+   values in a pair of vectors (second examples), or one range may be
+   explicit and the other range assigned from a vector.
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/load
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/load	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/load	(revision 17422)
@@ -0,0 +1,20 @@
+
+   load (overlay) (filename)
+
+  "load" reads a file with objects in SAOimage style into the
+  specified overlay.  If the specified overlay is already used, the
+  new objects are added to the old.  If you do not want this, use
+  "erase" to erase all objects in the overlay first.  Valid overlays
+  may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: save, erase, contour
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/peak
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/peak	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/peak	(revision 17422)
@@ -0,0 +1,12 @@
+
+   peak [-q] (x) (y) [startx endx]
+
+   Determines the peak of the y vector between starx and endx
+    or between the extrema of the x vector (default)
+
+   -q: quiets the script (no output to the window)
+
+   Outputs:
+    $peakval: Value of the peak
+    $peakpos: X value corresponding to the peak value
+    $peaknum: Index of the peak value
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/periodogram
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/periodogram	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/periodogram	(revision 17422)
@@ -0,0 +1,6 @@
+
+   periodogram (time) (flux) (minP) (maxP) (period) (power)
+
+   creates a periodogram from the vectors (time) and (flux)
+    between (minP) and (maxP) and outputs the results to
+    (period) and (power)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/plot
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/plot	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/plot	(revision 17422)
@@ -0,0 +1,13 @@
+
+   plot <x> <y> [-dx dx] [-dy dy] [+dx dx] [+dy dy]
+
+   plot a pair of vectors.  the options allow for errorbars.  If only
+   one of -dy or +dy is given, the given vector is used for the
+   errorbar.  If both are given, -dy defines the lower errorbar, while
+   +dy defines the upper errorbar.  This allows for assymetric errors
+   in a trivial fashion.  The same applies to the -dx, +dx values.  
+
+   The vectors are plotted with the current plot style.  See style for
+   all of the options.
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/ps
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/ps	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/ps	(revision 17422)
@@ -0,0 +1,16 @@
+
+   ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage]
+
+   create a PostScript file from the current graphics window (Kii or
+   Kapa).  The target graphics window may be selected with the -g or
+   -i options (to choose between Kii or Kapa) in combination the -n
+   option (to specify which instance).
+
+   With no additional arguments, Kii produces an encapsulated
+   postscript file called Ximage.ps, while Kapa produces a file called
+   Xgraph.ps.  The output name may be set with the -name (file.ps)
+   option. 
+
+   By default, the output is scaled to fit on a letter page 
+
+   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuedelete
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuedelete	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuedelete	(revision 17422)
@@ -0,0 +1,4 @@
+
+   queuedelete (name)
+
+   Deletes the entire queue (name)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queueinit
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queueinit	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queueinit	(revision 17422)
@@ -0,0 +1,4 @@
+
+   queueinit (name)
+
+   Creates a queue (name) with an initial size of 0
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuepush
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuepush	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuepush	(revision 17422)
@@ -0,0 +1,8 @@
+
+   queuepush (queue) (value) [-key N] [-uniq] [-replace]
+
+   Pushes (value) onto the back of (queue)
+
+   -key N:
+   -uniq:
+   -replace:
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuesize
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuesize	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/queuesize	(revision 17422)
@@ -0,0 +1,5 @@
+
+   queuesize (name) [-var variable]
+
+   Returns the size of the queue (name), either to variable
+    or to the window (default)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rd
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rd	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rd	(revision 17422)
@@ -0,0 +1,10 @@
+
+  rd (buffer) (filename)
+
+  "rd" reads a file into the specified buffer, creating the buffer if
+  none exists.  A buffer name may consist of any letters, numbers, and
+  some limited other characters.  However, the name may not start with
+  a digit.
+
+  See also: wd, tv
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/read
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/read	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/read	(revision 17422)
@@ -0,0 +1,10 @@
+
+   read vect col [vect col, ...]
+
+   read vector values from a file.  An arbitrary number of vectors can
+   be specified, and the (whitespace-separated) field number given for
+   each vector.  Data is read from the file defined by "datafile".
+
+   See also: datafile
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rebin
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rebin	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rebin	(revision 17422)
@@ -0,0 +1,9 @@
+
+  rebin <from> <to> scale
+
+  "rebin" rebins the specified buffer by the given scale factor and
+  places the result in the <to> buffer.  Negative integer values imply
+  expansion, positive numbers imply compression.
+
+  See also: extract
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/resize
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/resize	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/resize	(revision 17422)
@@ -0,0 +1,6 @@
+
+   resize Nx Ny
+
+   change the size of the Kii window to have image dimensions Nx x
+   Ny.  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rotate
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rotate	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/rotate	(revision 17422)
@@ -0,0 +1,10 @@
+
+   rotate (buffer) (angle) [-center x y]
+
+   rotate the buffer by the given angle.  The angle may also be one of
+   the following special words:
+
+   -flipx - flip in the x direction
+   -flipy - flip in the y direction
+
+   if the optional center is given the rotation is about this position.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/save
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/save	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/save	(revision 17422)
@@ -0,0 +1,16 @@
+  save (overlay) (filename)
+
+  "save" stores the objects from the specified overlay into the named
+file.  Valid overlays may be: 0, red, 1, green, 2, blue, 3, yellow.
+
+Examples of SAOimage style objects:
+BOX   300  200  100 50  : draws a box centered at pixel 300,200 that
+			  is 100 pixels wide, and 50 pixels high
+
+CIRCLE 500 400 200 : draws a circle centered at pixel 500,400 with a
+                     radius of 200 pixels
+
+LINE 100 100 200 200 : draws a line from (100,100) with length (200,200)
+
+  See also: load, erase, contour
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/set
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/set	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/set	(revision 17422)
@@ -0,0 +1,41 @@
+
+  set (buffer) = expression..
+
+  perform math operations on images.  there are several allowed
+  operators.  the standard math functions are + - * /.  Exponentiation
+  is performed with ^ (set c = a ^ b).  There are several "logic"
+  operators which need some explanation.
+
+  set c = a < b  -- c is 1 if a < b, 0 otherwise
+  set c = a > b  -- c is 1 if a > b, 0 otherwise
+  set c = m1 & m1 - c is 1 if m1 AND m2 true (non-zero is true)
+  set c = m1 | m1 - c is 1 if m1 OR m2 true (non-zero is true)
+  set c = a << b -- c is the minimum of a and b
+  set c = a >> b -- c is maximum of a and b
+
+  complex operations may be performed:
+
+  set c = a*(a < b) + c*((a < c) | (c < d))
+
+  unary operators also exist:
+
+  exp(a) - e to the power of a
+  ten(a) - 10 to the power of a
+  ln(a) - log base e of a
+  log(a) - log base 10 of a
+  sqrt(a) - square root of a
+  sin(a) - sin of a
+  cos(a) - sin of a
+  tan(a) - sin of a
+  not(a) - logical inverse of a
+  abs(a) - absolute value of a
+  int(a) - integer value of a
+
+  Examples:
+  
+  set a = b + 10  (add 10 to every pixel in b and put the answer in a)
+  set a = a / b   (divide every pixel in a by the corresponding pixel
+                     b and put the answer in a)
+  set b = 10 / a  (divide 10 by every pixel in a and put the answer
+                     in b)
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/shift
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/shift	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/shift	(revision 17422)
@@ -0,0 +1,6 @@
+
+   shift buffer dx dy
+
+   shift image by dx,dy pixels (may be fractional values).
+
+   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/stats
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/stats	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/stats	(revision 17422)
@@ -0,0 +1,10 @@
+
+  stats (buffer) (x) (y) (nx) (ny)
+
+  report the statistics on a portion of an image.  (x) and (y) specify
+the starting corner of the region, (nx) and (ny) specify the width and
+height of the region.  stats reports the mean, sigma, and number of
+pixels in the region.
+
+  See Also: ??
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/style
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/style	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/style	(revision 17422)
@@ -0,0 +1,36 @@
+
+   style -- many options
+
+   define or check the Kapa plotting style.  without any command-line arguments,
+   style prints the current style.  
+
+   option definitions:
+
+   -n	window number (0-4), can also be used to change active window.
+   -pt	point style: 
+		0 = filled box
+		1 = open box
+		2 = +
+		3 = X
+		4 = filled triangle
+		5 = blank
+		6 = open triangle 
+		7 = open circle
+		100 = connect pair of points
+   -x	plotting method:
+		0 = connect
+		1 = histogram
+		2 = points
+   -lt	line type:
+		0 = solid
+		1 = dashed
+		2 = dotted??
+   -lw	line weight (0-10)
+   -sz	point size
+   -c   color:
+	black, white, red, orange, 
+	yellow, green, blue, indigo, violet
+   -eb  errorbar with no top
+   +eb  errorbar with top
+
+   See also: plot, box
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/subset
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/subset	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/subset	(revision 17422)
@@ -0,0 +1,12 @@
+
+   subset vec = vec if (logic expression)
+
+   reduce the length of a vector based on logical expression.  The
+   logic expression is some function of vectors of equal length to the
+   main vector.
+
+   for example, we have vectors x and y of the same length:
+   
+   subset X = x if ((y > 100) | (x < 10))
+
+    
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/textline
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/textline	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/textline	(revision 17422)
@@ -0,0 +1,7 @@
+
+   textline x y (line) [-fn (font) size] [-rot angle]
+
+   write the string at the given coordinates and rotation,
+   in the given font.
+
+   (enclose long strings with spaces in quotes)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/tv
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/tv	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/tv	(revision 17422)
@@ -0,0 +1,13 @@
+
+  tv [-log] (buffer) [zero range]
+
+  display an image in the Ki'i window (X display program).  zero and
+  range specify the data values corresponding to the color mapping.
+  If they are not specified, the old values are used (default is 0,
+  1024).
+
+  If -log is specified the color mapping scale will be logarithmic.  
+
+
+  See Also: Kii, rd, cursor
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/unsign
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/unsign	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/unsign	(revision 17422)
@@ -0,0 +1,20 @@
+  unsign
+
+  "unsign" toggles the status of the UNSIGN mode.  Warning: This is a
+non-FITS standard implementation.  The FITS standard does not allow
+for unsigned integer data values, but many sites write CCD images with
+16 bit UNSIGNED data.  Mana looks for a FITS keyword "UNSIGN" to
+determine if an image which is read in has signed or unsigned
+integers.  However, since this is a non-standard concept, many sites
+do not use the UNSIGN flag, yet still write unsigned data.  If the
+user knows the data in the file is unsigned (ie, the user must have
+apriori knowledge -- a FITS no-no!), then the user should set the
+UNSIGN mode to true.  In the event that the UNSIGN mode is set, if a
+FITS file is encountered which contains integers, but which does not
+contain the UNSIGN keyword, Mana will make the default assumption that
+the data is unsigned.  If the SIGNED mode is set, Mana will assume all
+integer FITS files contain signed integers.  
+
+((( should be able to say "unsign <buffer>" to convert the status of a
+buffer to UNSIGN or to toggle the buffer's status, but not yet
+implemented)))
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/vectors
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/vectors	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/vectors	(revision 17422)
@@ -0,0 +1,5 @@
+
+   vectors
+
+   list the currently defined vectors and lengths
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/wd
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/wd	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/wd	(revision 17422)
@@ -0,0 +1,29 @@
+
+  wd (buffer) (filename) [BITPIX]
+
+  wd writes a buffer to the specified file in FITS format.  (You may
+need quotes around the filename if there are any /'s in the path).
+The FITS keyword BITPIX may be specified.  Valid values are 8, 16, 32
+(integer formats) and -32, -64 (floating point formats).  If BITPIX is
+unspecified, the original value of the image is used.  The image is
+converted blindly to the format and written out.  This may mean that
+values are rounded (integers), or wrapped by the range of the number
+of bits.  Also beware of the issue of signed vs unsigned images.
+
+  See Also: rd
+
+bitpix  bzero  bscale  file range     data range
+16      0      1       -32k : 32k     -32k : 32k
+16	32k    1       -32k : 32k        0 : 64k
+16	0      2       -32k : 32k     -64k : 64k
+16	0      0.001   -32k : 32k     -32. : 32.
+32	0      1       -2e9 : 2e9     -2e9 : 2e9
+8	0      1       -128 : 128     -128 : 128
+
+so for example, a file with a data range of -128k to +128k written
+with bitpix 16, bzero 0, bscale 1 will have all data outside -32k :
++32k wrapped back in that range.
+
+note: on write, bzero is subtracted from the data before the data is
+      divided by bscale.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/zplot
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/zplot	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/help/zplot	(revision 17422)
@@ -0,0 +1,7 @@
+
+   zplot x y z min max
+
+   plot the vector pair x and y as a series of points with size scaled
+   by the values in the vector z, with the smallest point having
+   values <= min and the largest having values >= max.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/histogram.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/histogram.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/histogram.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "data.h"
+
+int histogram (int argc, char **argv) {
+  
+  int i, bin, Nbins;
+  float *V, start, end, delta;
+  Vector *xvec, *yvec;
+
+  if ((argc != 6) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: hist invec outvec start end [delta]\n");
+    return (FALSE);
+  }
+
+  delta = 1;
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+  if (argc == 6) delta = atof (argv[5]);
+ 
+  if ((start == end) || (delta == 0)) {
+    gprint (GP_ERR, "error in value: %f to %f, %f\n", start, end, delta);
+    return (FALSE);
+  }
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = (end - start) / delta;
+  /* number here should match number in create.c */
+
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  yvec[0].Nelements = Nbins;
+    
+  REALLOCATE (yvec[0].elements, float, yvec[0].Nelements);
+  bzero (yvec[0].elements, sizeof(float)*yvec[0].Nelements);
+
+  V = xvec[0].elements;
+  for (i = 0; i < xvec[0].Nelements; i++, V++) {
+    if (isnan(*V)) continue;
+    bin = MIN (MAX (0, (*V - start) / delta), Nbins - 1);
+    yvec[0].elements[bin] += 1.0;
+  }      
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imcut.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imcut.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imcut.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int imcut (int argc, char **argv) {
+  
+  int i, Nx, Ny, xi, yi, L;
+  double xs, ys, xe, ye, dX, dY;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+  float *V;
+
+  if (argc != 8) {
+    gprint (GP_ERR, "USAGE: cut <buffer> <X vector> <Y vector> xs ys xe ye\n");
+    return (FALSE);
+  }
+
+  if ((buf  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+  if ((xvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto usage;
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) goto usage;
+ 
+  xs = atof (argv[4]);
+  ys = atof (argv[5]);
+  xe = atof (argv[6]);
+  ye = atof (argv[7]);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if ((xs < 0) || (xs > Nx)) goto range;
+  if ((ys < 0) || (ys > Ny)) goto range;
+  if ((xe < 0) || (xe > Nx)) goto range;
+  if ((ye < 0) || (ye > Ny)) goto range;
+
+  dX = xe - xs;
+  dY = ye - ys;
+  L = hypot (dX, dY);
+  dX = dX / L;
+  dY = dY / L;
+
+  REALLOCATE (xvec[0].elements, float, MAX (L, 1));
+  REALLOCATE (yvec[0].elements, float, MAX (L, 1));
+  xvec[0].Nelements = L;
+  yvec[0].Nelements = L;
+
+  V = (float *)buf[0].matrix.buffer;
+  for (i = 0; i < L; i++) {
+    xi = xs + i*dX - 0.5;
+    yi = ys + i*dY - 0.5;
+    xvec[0].elements[i] = i;
+    yvec[0].elements[i] = V[xi + Nx*yi];
+  }
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imhist.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imhist.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imhist.c	(revision 17422)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+int imhist (int argc, char **argv) {
+  
+  int i, j, N, Nbins, Quiet;
+  int sx, sy, nx, ny, bin;
+  float *V, delta;
+  double max, min, dx;
+  Vector *vec1, *vec2;
+  Buffer *buf;
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  delta = 0;
+  if ((N = get_argument (argc, argv, "-delta"))) {
+    remove_argument (N, &argc, argv);
+    delta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  min = max = 0.0;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    min = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    max = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  sx = sy = 0.0;
+  nx = ny = 0.0;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    sx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    sy = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    nx = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ny = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max] [-delta binsize]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  
+  /* if either range is set to zero, use the rest of the chip */
+  if (nx == 0)
+    nx = buf[0].matrix.Naxis[0] - sx;
+  if (ny == 0)
+    ny = buf[0].matrix.Naxis[1] - sy;
+
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  if ((vec1 = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vec2 = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* unfortunately, we must do this in two passes:
+     first pass finds the max and min and defines the bin size
+     second pass counts the pixes in each bin 
+     */
+
+  if ((max == 0) && (min == 0)) {
+    max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+    gprint (GP_ERR, "sx: %d, sy: %d, first: %f\n", sx, sy, max);
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	max = MAX (max, *V);
+	min = MIN (min, *V);
+      }
+    }
+  }
+
+  if (delta == 0) {
+    Nbins = 1024;
+    dx = (max - min) / Nbins;
+  } else {
+    dx = delta;
+    Nbins = (max - min) / dx;
+  }
+  if (Quiet) {
+    set_variable ("MIN", min);
+    set_variable ("MAX", max);
+    set_variable ("DX",  dx);
+  } else {
+    gprint (GP_LOG, "max %f, min %f, dx %f\n", max, min, dx);
+  }  
+
+  vec1[0].Nelements = Nbins + 1;
+  vec2[0].Nelements = Nbins + 1;
+  REALLOCATE (vec1[0].elements, float, vec1[0].Nelements);
+  bzero (vec1[0].elements, vec1[0].Nelements*sizeof(float));
+  REALLOCATE (vec2[0].elements, float, vec2[0].Nelements);
+  bzero (vec2[0].elements, vec2[0].Nelements*sizeof(float));
+  
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      bin = MAX (MIN (Nbins, (*V - min) / dx), 0);
+      vec2[0].elements[bin] += 1.0;
+    }
+  }
+  for (i = 0; i < Nbins + 1; i++, V++) {
+    vec1[0].elements[i] = i*dx + min;
+  }
+  
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imsmooth.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imsmooth.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/imsmooth.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+int imsmooth (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny, Ns, Ngauss;
+  float *vi, *vo, *gauss, *gaussnorm;
+  float g, s, sigma, Nsigma;
+  Buffer *in;
+  float *temp;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (temp, float, Nx*Ny);
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, float, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  /* smooth in X direction */
+  for (j = 0; j < Ny; j++) {
+    vi = (float *) in[0].matrix.buffer + j*Nx;
+    vo = &temp[j*Nx];
+    for (i = 0; i < Nx; i++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (i+n < 0) continue;
+	if (i+n >= Nx) continue;
+	s += gauss[n]*vi[i+n];
+	g += gauss[n];
+      }
+      vo[i] = s / g;
+    }
+  }
+
+  /* smooth in Y direction */
+  for (i = 0; i < Nx; i++) {
+    vi = &temp[i];
+    vo = (float *)in[0].matrix.buffer + i;
+    for (j = 0; j < Ny; j++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (j+n < 0) continue;
+	if (j+n >= Ny) continue;
+	s += gauss[n]*vi[(n+j)*Nx];
+	g += gauss[n];
+      }
+      vo[j*Nx] = s / g;
+    }
+  }
+
+  free (temp);
+  free (gaussnorm);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/init.c	(revision 17422)
@@ -0,0 +1,263 @@
+# include "data.h"
+
+int accum            PROTO((int, char **));
+int applyfit         PROTO((int, char **));
+int applyfit2d       PROTO((int, char **));
+int box              PROTO((int, char **));
+int book_command     PROTO((int, char **));
+int center           PROTO((int, char **));
+int circstats        PROTO((int, char **));
+int clear            PROTO((int, char **));
+int clip             PROTO((int, char **));
+int close_device     PROTO((int, char **));
+int concat           PROTO((int, char **));
+int contour          PROTO((int, char **));
+int create           PROTO((int, char **));
+int cumulative       PROTO((int, char **));
+int cursor           PROTO((int, char **));
+int cut              PROTO((int, char **));
+int datafile         PROTO((int, char **));
+int dbconnect        PROTO((int, char **));
+int dbselect         PROTO((int, char **));
+int delete           PROTO((int, char **));
+int device           PROTO((int, char **));
+int dimendown        PROTO((int, char **));
+int dimenup          PROTO((int, char **));
+int erase            PROTO((int, char **));
+int extract          PROTO((int, char **));
+int fft1d            PROTO((int, char **));
+int fft2d            PROTO((int, char **));
+int fit2d            PROTO((int, char **));
+int fit              PROTO((int, char **));
+int gaussjordan      PROTO((int, char **));
+int gaussdeviate     PROTO((int, char **));
+int gaussintegral    PROTO((int, char **));
+int grid             PROTO((int, char **));
+int gridify          PROTO((int, char **));
+int grow             PROTO((int, char **));
+int ungridify        PROTO((int, char **));
+int histogram        PROTO((int, char **));
+int imcut            PROTO((int, char **));
+int imhist           PROTO((int, char **));
+int imsmooth         PROTO((int, char **));
+int integrate        PROTO((int, char **));
+int interpolate      PROTO((int, char **));
+int jpeg             PROTO((int, char **));
+int kern             PROTO((int, char **));
+int keyword          PROTO((int, char **));
+int labels           PROTO((int, char **));
+int limits           PROTO((int, char **));
+int line             PROTO((int, char **));
+int list_buffers     PROTO((int, char **));
+int header           PROTO((int, char **));
+int list_vectors     PROTO((int, char **));
+int load             PROTO((int, char **));
+int lookup           PROTO((int, char **));
+int mkrgb            PROTO((int, char **));
+int mcreate          PROTO((int, char **));
+int medacc           PROTO((int, char **));
+int mget             PROTO((int, char **));
+int minterp          PROTO((int, char **));
+int mset             PROTO((int, char **));
+int peak             PROTO((int, char **));
+int periodogram      PROTO((int, char **));
+int plot             PROTO((int, char **));
+int dot              PROTO((int, char **));
+int point            PROTO((int, char **));
+int ps               PROTO((int, char **));
+int queuelist        PROTO((int, char **));
+int queueload        PROTO((int, char **));
+int queueinit        PROTO((int, char **));
+int queuedelete      PROTO((int, char **));
+int queuedrop        PROTO((int, char **));
+int queuepop         PROTO((int, char **));
+int queueprint       PROTO((int, char **));
+int queuepush        PROTO((int, char **));
+int queuesize        PROTO((int, char **));
+int rd               PROTO((int, char **));
+int rdseg            PROTO((int, char **));
+int read_vectors     PROTO((int, char **));
+int rebin            PROTO((int, char **));
+int resize           PROTO((int, char **));
+int roll             PROTO((int, char **));
+int rotate           PROTO((int, char **));
+int save             PROTO((int, char **));
+int section          PROTO((int, char **));
+int set              PROTO((int, char **));
+int shift            PROTO((int, char **));
+int sort_vectors     PROTO((int, char **));
+int spline_apply_cmd PROTO((int, char **));
+int spline_construct_cmd PROTO((int, char **));
+int stats            PROTO((int, char **));
+int style            PROTO((int, char **));
+int subraster        PROTO((int, char **));
+int subset           PROTO((int, char **));
+int svd              PROTO((int, char **));
+int swapbytes        PROTO((int, char **));
+int textline         PROTO((int, char **));
+int tv               PROTO((int, char **));
+int tvchannel        PROTO((int, char **));
+int tvcolors         PROTO((int, char **));
+int tvcontour        PROTO((int, char **));
+int tvgrid           PROTO((int, char **));
+int uniq             PROTO((int, char **));
+int unsign           PROTO((int, char **));
+int vbin             PROTO((int, char **));
+int vclip            PROTO((int, char **));
+int vect_select      PROTO((int, char **));
+int vgrid            PROTO((int, char **));
+int vgauss           PROTO((int, char **));
+int vmaxwell         PROTO((int, char **));
+int vload            PROTO((int, char **));
+int vzload           PROTO((int, char **));
+int vstat            PROTO((int, char **));
+int vroll            PROTO((int, char **));
+int vpop             PROTO((int, char **));
+int vsmooth          PROTO((int, char **));
+int wd               PROTO((int, char **));
+int write_vectors    PROTO((int, char **));
+int zap              PROTO((int, char **));
+int zplot            PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "accum",        accum,            "accumulate vector values in another vector"},
+  {1, "applyfit",     applyfit,         "apply fit to new vector"},
+  {1, "applyfit2d",   applyfit2d,       "apply 2-d fit to new vector"},
+  {1, "box",          box,              "draw a box on the plot"},
+  {1, "book",         book_command,     "commands to manipulate book/page/word data"},
+  {1, "center",       center,           "center image on coords"},
+  {1, "circstats",    circstats,        "circular statistics"},
+  {1, "clear",        clear,            "erase plot"},
+  {1, "clip",         clip,             "clip values in a buffer to be within a range"},
+  {1, "close",        close_device,     "close the current display device"},
+  {1, "concat",       concat,           "reduce vector dimension"},
+  {1, "contour",      contour,          "create contour from image"},
+  {1, "create",       create,           "create a new vector"},
+  {1, "cumulative",   cumulative,       "build a cumulative histogram from a specific histogram"},
+  {1, "cursor",       cursor,           "get coords from cursor"},
+  {1, "cut",          cut,              "extract a cut across an image"},
+  {1, "datafile",     datafile,         "define file to read vectors"},
+  {1, "dbconnect",    dbconnect,        "setup mysql db connection"},
+  {1, "dbselect",     dbselect,         "extract vectors from mysql database table"},
+  {1, "delete",       delete,           "delete vectors or matrices"},
+  {1, "device",       device,           "set / get current graphics device"},
+  {1, "dimendown",    dimendown,        "convert matrix to vector"},
+  {1, "dimenup",      dimenup,          "convert vector to matrix"},
+  {1, "erase",        erase,            "erase objects on an overlay"},
+  {1, "extract",      extract,          "extract a portion of a buffer into another buffer"},
+  {1, "fft1d",        fft1d,            "fft on the pixel-stream in an image"},
+  {1, "fft2d",        fft2d,            "fft on an image"},
+  {1, "fit",          fit,              "fit polynomial to vector pair"},
+  {1, "fit2d",        fit2d,            "fit 2-d polynomial to vector triplet"},
+  {1, "gaussj",       gaussjordan,      "solve Ax = B (N-D)"},
+  {1, "gaussdev",     gaussdeviate,     "generate a gaussian deviate vector"},
+  {1, "gaussint",     gaussintegral,    "return the integrated gaussian vector"},
+  {1, "grid",         grid,             "plot cartesian grid"},
+  {1, "gridify",      gridify,          "convert vector triplet to buffer"},
+  {1, "grow",         grow,             "grow a mask"},
+  {1, "ungridify",    ungridify,        "convert buffer region to vector triplet"},
+  {1, "header",       header,           "print buffer header"},
+  {1, "histogram",    histogram,        "generate histogram from vector"},
+  {1, "imcut",        imcut,            "linear image cut between arbitrary coords"},
+  {1, "imhist",       imhist,           "histogram of an image region"},
+  {1, "imsmooth",     imsmooth,         "circular gaussian smoothing"},
+  {1, "integrate",    integrate,        "integrate a vector"},
+  {1, "interpolate",  interpolate,      "interpolate between vector pairs"},
+  {1, "jpeg",         jpeg,             "write text line on graph"},
+  {1, "png",          jpeg,             "write text line on graph"},
+  {1, "ppm",          jpeg,             "write text line on graph"},
+  {1, "kern",         kern,             "convolve with 3x3 kernel"},
+  {1, "keyword",      keyword,          "extract a FITS keyword from buffer header"},
+  {1, "labels",       labels,           "define labels for plot"},
+  {1, "limits",       limits,           "define plot limits"},
+  {1, "line",         line,             "plot line"},
+  {1, "buffers",      list_buffers,     "list the currently allocated buffers"},
+  {1, "vectors",      list_vectors,     "list vectors"},
+  {1, "load",         load,             "load an SAOimage style overlay"},
+  {1, "lookup",       lookup,           "convert vector via lookup table (vector pair)"},
+  {1, "mkrgb",        mkrgb,            "convert 3 images to rgb jpeg"},
+  {1, "mcreate",      mcreate,          "create a matrix"},
+  {1, "medacc",       medacc,           "accumulate vector values in another vector"},
+  {1, "mget",         mget,             "extract a vector from a matrix"},
+  {1, "minterp",      minterp,          "interpolate image pixels"},
+  {1, "mset",         mset,             "insert a vector in a matrix"},
+  {1, "peak",         peak,             "find vector peak in range"},
+  {1, "periodogram",  periodogram,      "measure periods in unevenly sampled data"},
+  {1, "plot",         plot,             "plot a pair of vectors"},
+  {1, "dot",          dot,              "plot a single point"},
+  {1, "point",        point,            "load overlay with single point"},
+  {1, "ps",           ps,               "define labels for plot"},
+  {1, "queuepop",     queuepop,         "pop value from queue to variable"},
+  {1, "queuedrop",    queuedrop,        "drop values from queue matching a key"},
+  {1, "queueprint",   queueprint,       "print the contents of a queue"},
+  {1, "queuepush",    queuepush,        "push value onto queue"},
+  {1, "queueinit",    queueinit,        "create an empty queue"},
+  {1, "queuedelete",  queuedelete,      "delete a queue"},
+  {1, "queuelist",    queuelist,        "list defined queues"},
+  {1, "queueload",    queueload,        "load queue from command"},
+  {1, "queuesize",    queuesize,        "show queue size"},
+  {1, "rd",           rd,               "load fits image"},
+  {1, "rdseg",        rdseg,            "read a segment of an image from a file"},
+  {1, "read",         read_vectors,     "read vectors from datafile"},
+  {1, "rebin",        rebin,            "rebin data by factor of N"},
+  {1, "resize",       resize,           "set graphics/image window size"},
+  {1, "roll",         roll,             "roll image to new start point"},
+  {1, "rotate",       rotate,           "rotate image"},
+  {1, "save",         save,             "save an SAOimage style overlay"},
+  {1, "section",      section,          "define section of graph"},
+  {1, "set",          set,              "vector math"},
+  {1, "shift",        shift,            "shift data in an image"},
+  {1, "sort",         sort_vectors,     "sort list of vectors"},
+  {1, "spline.apply", spline_apply_cmd, "apply spline fit to generate an image"},
+  {1, "spline.const", spline_construct_cmd, "create spline 2nd deriv. terms"},
+  {1, "stats",        stats,            "give statistics on a portion of a buffer"},
+  {1, "style",        style,            "set the style for graph plots"},
+  {1, "subraster",    subraster,        "subraster of fits image"},
+  {1, "subset",       subset,           "expand vector dimension"},
+  {1, "svd",          svd,              "singular value decomposition of a matrix"},
+  {1, "swapbytes",    swapbytes,        "byte swap thing"},
+  {1, "textline",     textline,         "write text line on graph"},
+  {1, "tv",           tv,               "display an image on the Kii window"},
+  {1, "tvchannel",    tvchannel,        "set the current tv channel"},
+  {1, "tvcolors",     tvcolors,         "set the tv colormap"},
+  {1, "tvcontour",    tvcontour,        "send contour to overlay"},
+  {1, "tvgrid",       tvgrid,           "wait until return is typed"},
+  {1, "uniq",         uniq,             "create a uniq vector subset from a vector"},
+  {1, "unsign",       unsign,           "toggle the UNSIGN status"},
+  {1, "vbin",         vbin,             "bin values in a vector to be within a range"},
+  {1, "vclip",        vclip,            "clip values in a vector to be within a range"},
+  {1, "select",       vect_select,      "selective vector assignment"},
+  {1, "vgauss",       vgauss,           ""},
+  {1, "vmaxwell",     vmaxwell,         ""},
+  {1, "vgrid",        vgrid,            ""},
+  {1, "vload",        vload,            "load vectors on Kii"},
+  {1, "vzload",       vzload,           "load vectors on Kii"},
+  {1, "vstat",        vstat,            "get info from imreg database"},
+  {1, "vsmooth",      vsmooth,          "gaussian smooth of a vector"},
+  {1, "vroll",        vroll,            "roll vector elements"},
+  {1, "vpop",         vpop,             "remove first element"},
+  {1, "wd",           wd,               "write an image to a file"},
+  {1, "write",        write_vectors,    "write vectors to datafile"},
+  {1, "zap",          zap,              "delete pixels"},
+  {1, "zplot",        zplot,            "plot x y with size scaled by z"},
+}; 
+
+void InitData () {
+  
+  int i;
+
+  InitKapa ();
+  InitQueues ();
+  InitBooks ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeData () {
+
+  FreeKapa ();
+  FreeQueues ();
+  FreeBooks ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/integrate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/integrate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/integrate.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "data.h"
+
+int integrate (int argc, char **argv) {
+  
+  int i, N, VERBOSE;
+  float *X, *Y;
+  double start, end, value, range;
+  Vector *vecx, *vecy;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: integrate <x> <y> start end\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[3]);
+  end   = atof (argv[4]);
+
+  X = vecx[0].elements;
+  Y = vecy[0].elements;
+
+  value = 0;
+  range = 0;
+  for (i = 0; i < vecx[0].Nelements-1; i++, X++, Y++) {
+    if ((*X >= start) && (*X <= end)) {
+      value += *Y * (X[1] - X[0]);
+      range += (X[1] - X[0]);
+    }
+  }      
+
+  set_variable ("sum", value); 
+  set_variable ("range", range);
+  if (VERBOSE) gprint (GP_LOG, "sum: %f\n", value);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/interpolate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/interpolate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/interpolate.c	(revision 17422)
@@ -0,0 +1,53 @@
+# include "data.h"
+
+int interpolate (int argc, char **argv) {
+
+  int  i, j;
+  double x0, x1, dx, dy, y0;
+  Vector *xout, *yout, *xin, *yin;
+
+  /** check basic syntax **/
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: interpolate Xi Yi Xo Yo\n");
+    gprint (GP_ERR, "  Xi Yi - sorted reference vectors\n");
+    gprint (GP_ERR, "  Xo    - output positions\n");
+    gprint (GP_ERR, "  Yo    - output values\n");
+    return (FALSE);
+  }
+
+  if ((xin  = SelectVector (argv[1],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yin  = SelectVector (argv[2],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xout = SelectVector (argv[3],  OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yout = SelectVector (argv[4],  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  yout[0].Nelements = xout[0].Nelements;
+  REALLOCATE (yout[0].elements, float, yout[0].Nelements);
+
+  dx = xin[0].elements[1] - xin[0].elements[0];
+  dy = yin[0].elements[1] - yin[0].elements[0];
+  x0 = xin[0].elements[0];
+  y0 = yin[0].elements[0];
+  
+  /* in vectors are sorted, out vectors are not */
+  for (j = 0; j < xin[0].Nelements - 1; j++) {
+    dx = xin[0].elements[j+1] - xin[0].elements[j];
+    dy = yin[0].elements[j+1] - yin[0].elements[j];
+    x0 = xin[0].elements[j];
+    y0 = yin[0].elements[j];
+    x1 = xin[0].elements[j+1];
+    for (i = 0; i < xout[0].Nelements; i++) {
+      if ((xout[0].elements[i] >= x0) && (xout[0].elements[i] < x1)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+      if ((j == 0) && (xout[0].elements[i] < x0)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+      if ((j == xin[0].Nelements - 2) && (xout[0].elements[i] >= x1)) {
+	yout[0].elements[i] = (dy/dx)*(xout[0].elements[i] - x0) + y0;
+      }
+    }    
+  }
+
+  return (TRUE);
+    
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/jpeg.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/jpeg.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/jpeg.c	(revision 17422)
@@ -0,0 +1,67 @@
+# include "data.h"
+
+int jpeg (int argc, char **argv) {
+
+  char filename[1024];
+  int N, kapa, IsPNG, IsPPM;
+  char *name;
+  
+  if ((N = get_argument (argc, argv, "--help"))) {
+    gprint (GP_ERR, "USAGE: jpeg [-name file] [-g | -i] [-n device] [-ppm]\n");
+    return (FALSE);
+  }
+
+  /* image type */
+  IsPPM = IsPNG = FALSE;
+  if ((N = get_argument (argc, argv, "-ppm"))) {
+    remove_argument (N, &argc, argv);
+    IsPPM = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-png"))) {
+    remove_argument (N, &argc, argv);
+    IsPNG = TRUE;
+  }
+  if (!strcmp (argv[0], "png")) IsPNG = TRUE;
+  if (!strcmp (argv[0], "ppm")) IsPPM = TRUE;
+
+  /* file name */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX output png / jpeg needs to include both graph and image
+     if available.  this is a poor mix of data representations 
+     (png for image / jpeg for plots)
+  if ((N = get_argument (argc, argv, "-g"))) {
+  if ((N = get_argument (argc, argv, "-i"))) {
+  */
+
+  if (!GetGraph (NULL, &kapa, name)) return (FALSE);
+  if (!IsPNG && !IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.jpg");
+    KiiJPEG (kapa, filename);
+  }
+  if (IsPNG) {
+    if (!filename[0]) strcpy (filename, "kapa.png");
+    KapaPNG (kapa, filename);
+  } 
+  if (IsPPM) {
+    if (!filename[0]) strcpy (filename, "kapa.ppm");
+    KapaPPM (kapa, filename);
+  }
+  return (TRUE);
+}
+
+/* jpeg converts graph to png or ppm
+   jpeg converts image to jpeg */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/kern.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/kern.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/kern.c	(revision 17422)
@@ -0,0 +1,123 @@
+# include "data.h"
+
+/** need to allow larger kernels (5x5, 7x7, etc) **/
+int kern (int argc, char **argv) {
+
+  int i, n, m;
+  int NX, NY, status;
+  FILE *f;
+  float *in_buff, *out_buff, *ib, *ob;
+  char line[256], *list;
+  double kernel[3][3], val;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: kern buffer (file)\n");
+    gprint (GP_ERR, "USAGE: kern buffer (list)\n");
+    gprint (GP_ERR, "USAGE: kern buffer -\n");
+    gprint (GP_ERR, "kernel file contains a 3x3 matrix for convolution\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  /* open file to read in kernel */
+  if (!strcmp (argv[2], "-")) {
+    for (i = 0; i < 3; i++) {
+      status = scan_line (stdin, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	return (FALSE);
+      }
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    goto have_kernel;
+  }
+  /* test list */
+  sprintf (line, "%s:n", argv[2]);
+  if ((list = get_variable (line)) == (char *) NULL) {
+    /* file */
+    f = fopen (argv[2], "r");
+    if (f == NULL) {
+      gprint (GP_ERR, "file not found: %s\n", argv[2]);
+      return (FALSE);
+    }
+    for (i = 0; i < 3; i++) {
+      status = scan_line (f, line);
+      if (status == EOF) {
+	gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+	fclose (f);
+      }
+      return (FALSE);
+      dparse (&kernel[0][i], 1, line);
+      dparse (&kernel[1][i], 2, line);
+      dparse (&kernel[2][i], 3, line);
+    }
+    fclose (f);
+    goto have_kernel;
+  }
+  if (atoi (list) != 9) {
+    gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+    return (FALSE);
+  }
+  free (list);
+  for (i = 0; i < 9; i++) {
+    sprintf (line, "%s:%d", argv[2], i);
+    list = get_variable (line);
+    if (list == (char *) NULL) {
+      gprint (GP_ERR, "kernel should be a 3x3 matrix...\n");
+      return (FALSE);
+    }
+    kernel[(int)(i/3)][i%3] = atof (list);
+    free (list);
+  }
+  goto have_kernel;
+
+ have_kernel:
+  /* normalize kernel */
+  val = 0;
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      val += kernel[n][m];
+    }
+  }
+  if (val == 0) {
+    gprint (GP_ERR, "kernel has zero power, not renormalizing...");
+  } else {
+    for (n = 0; n < 3; n++) {
+      for (m = 0; m < 3; m++) {
+	kernel[n][m] /= val;
+      }
+    }
+  }
+
+  gprint (GP_ERR, "working...");
+  
+  /* create output buffer */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *)buf[0].matrix.buffer;
+  ALLOCATE (buf[0].matrix.buffer, char, sizeof(float)*NX*NY);
+  out_buff = (float *)buf[0].matrix.buffer;
+  
+  /* do the convolution (on all but outer rows) */
+  
+  for (n = 0; n < 3; n++) {
+    for (m = 0; m < 3; m++) {
+      gprint (GP_ERR, "%d", n*3 + m + 1);
+      val = kernel[n][m];
+      ob = out_buff + NX + 1;
+      ib = in_buff + m*NX + n;
+      for (i = 0; i < (NX-2)*(NY-2); i++, ob++, ib++) {
+	*ob += *ib*val;
+      }
+    }
+  }
+  gprint (GP_ERR, "(done)\n");
+
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/keyword.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/keyword.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/keyword.c	(revision 17422)
@@ -0,0 +1,158 @@
+# include "data.h"
+
+enum {NONE, STRING, FLOAT, INT, BOOLEAN, KEYCOMMENT, COMMENT};
+
+/** WARNING: no error checking on variable validity **/
+int keyword (int argc, char **argv) {
+
+  int ivalue, status, N, ascomment, asfloat, delete, soft, Wmode;
+  char line[80];
+  double value;
+  Buffer *buf;
+
+  asfloat = FALSE;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    asfloat = TRUE;
+  }
+  
+  ascomment = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ascomment = TRUE;
+  }
+  
+  delete = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    delete = TRUE;
+  }
+  
+  /* return TRUE for missing keyword if soft */
+  soft = FALSE;
+  if ((N = get_argument (argc, argv, "-soft"))) {
+    remove_argument (N, &argc, argv);
+    soft = TRUE;
+  }
+  
+  /* identify write modes */
+  Wmode = NONE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = STRING;
+  }
+  if ((N = get_argument (argc, argv, "-wf"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = FLOAT;
+  }
+  if ((N = get_argument (argc, argv, "-wd"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = INT;
+  }
+  if ((N = get_argument (argc, argv, "-wc"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = KEYCOMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-ws"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = COMMENT;
+  }
+  if ((N = get_argument (argc, argv, "-wb"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (line, argv[N]);
+    remove_argument (N, &argc, argv);
+    Wmode = BOOLEAN;
+  }
+
+  if (!((argc == 3) || (!N && argc == 4))) {
+    gprint (GP_ERR, "USAGE: keyword <buffer> (KEYWORD) [variable] [-d] [-w(mode) value]\n");
+    gprint (GP_ERR, " -w modes: \n");
+    gprint (GP_ERR, "  -w  - string\n");
+    gprint (GP_ERR, "  -wf - float\n");
+    gprint (GP_ERR, "  -wd - int\n");
+    gprint (GP_ERR, "  -wb - boolean\n");
+    gprint (GP_ERR, "  -wc - comment\n");
+    gprint (GP_ERR, "  -ws - full string comment\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (Wmode != NONE) {
+    switch (Wmode) {
+    case STRING:
+      gfits_modify (&buf[0].header, argv[2], "%s", 1, line);
+      return (TRUE);
+    case FLOAT:
+      value = atof(line);
+      gfits_modify (&buf[0].header, argv[2], "%lf", 1, value);
+      return (TRUE);
+    case INT:
+      gfits_modify (&buf[0].header, argv[2], "%d", 1, atoi(line));
+      return (TRUE);
+    case BOOLEAN:
+      if (strcasecmp (line, "T") && strcasecmp (line, "TRUE") && strcasecmp (line, "F") && strcasecmp (line, "FALSE")) {
+	gprint (GP_ERR, "syntax error in boolean value\n");
+	return (FALSE);
+      }
+      ivalue = !strcasecmp (line, "T");
+      gfits_modify (&buf[0].header, argv[2], "%t", 1, ivalue);
+      return (TRUE);
+    case KEYCOMMENT:
+      gfits_modify (&buf[0].header, argv[2], "%C", 1, line);
+      return (TRUE);
+    case COMMENT:
+      gfits_modify (&buf[0].header, argv[2], "%S", 0, line);
+      return (TRUE);
+    }
+  }
+  
+  if (delete) {
+    gfits_delete (&buf[0].header, argv[2], -1);
+    return (TRUE);
+  }
+  
+  /* grab the value in the given format, either a string or a digit */
+  if (asfloat) {
+    status = gfits_scan (&buf[0].header, argv[2], "%lf", 1, &value);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_variable (argv[3], value);
+    else 
+      gprint (GP_LOG, "%s: %f\n", argv[2], value);
+    return (TRUE);
+  } 
+
+  if (ascomment) {
+    status = gfits_scan (&buf[0].header, argv[2], "%C", 1, line);
+    if (!status) goto failure;
+    if (argc == 4) 
+      set_str_variable (argv[3], line);
+    else 
+      gprint (GP_LOG, "%s: %s\n", argv[2], line);
+    return (TRUE);
+  }    
+
+  /* not-specified */
+  status = gfits_scan (&buf[0].header, argv[2], "%s", 1, line);
+  if (!status) goto failure;
+  if (argc == 4) 
+    set_str_variable (argv[3], line);
+  else 
+    gprint (GP_LOG, "%s: %s\n", argv[2], line);
+  return (TRUE);
+
+ failure: 
+  if (!soft) gprint (GP_ERR, "keyword %s not found\n", argv[2]);
+  return (soft);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/labels.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/labels.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/labels.c	(revision 17422)
@@ -0,0 +1,95 @@
+# include "data.h"
+
+int labels (int argc, char **argv) {
+  
+  char name[64];
+  int N, size, kapa;
+
+  if (!GetGraph (NULL, &kapa, NULL)) return (FALSE);
+
+  if (get_argument (argc, argv, "-h")) {
+    gprint (GP_ERR, "label options: \n");
+    gprint (GP_ERR, " -x : bottom-center\n");
+    gprint (GP_ERR, " +x : top-center\n");
+    gprint (GP_ERR, " -y : right-side\n");
+    gprint (GP_ERR, " +y : left-side\n\n");
+
+    gprint (GP_ERR, " -ul : upper-left corner\n");
+    gprint (GP_ERR, " -ll : lower-left corner\n");
+    gprint (GP_ERR, " -ur : upper-right corner\n");
+    gprint (GP_ERR, " -lr : lower-right corner\n\n");
+
+    gprint (GP_ERR, " -fn (font) (size) : set font and size\n");
+    gprint (GP_ERR, "   (font) : courier, helvetica, times, symbol\n\n");
+    gprint (GP_ERR, " label special characters:\n");
+    gprint (GP_ERR, " ^ : superscript\n");
+    gprint (GP_ERR, " _ : subscript\n");
+    gprint (GP_ERR, " | : default script \n");
+    gprint (GP_ERR, " &c, &h, &t, &s : set font\n\n");
+    return (FALSE);
+  }
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 0);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 1);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+x"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 2);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "+y"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 3);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ul"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 4);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ur"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 5);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-ll"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 6);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-lr"))) {
+    remove_argument (N, &argc, argv);
+    KapaSendLabel (kapa, argv[N], 7);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: labels [-x] [-y] [+x] [+y] [-ul] [-ur] [-ll] [-lr]\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/limits.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/limits.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/limits.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "data.h"
+
+int limits (int argc, char **argv) {
+
+  int N, APPLY;
+  int kapa;
+  char *name;
+  Graphdata graphmode;
+  Vector *xvec, *yvec;
+
+  xvec = yvec = NULL;
+
+  APPLY = FALSE;
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    APPLY = TRUE;
+  }
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc == 1) {
+    gprint (GP_ERR, "limits: %f %f %f %f [-a] [-n device]\n",
+	     graphmode.xmin, graphmode.xmax,
+	     graphmode.ymin, graphmode.ymax);
+    goto success;
+  }
+
+  if (argc == 3) { /* expect to see: limits x y */
+    if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+    goto success;
+  }
+    
+  if (argc == 4) { /* expect to see: limits x num num or limits num num y */
+    if (ISNUM(argv[1][0]) && ISNUM(argv[2][0])) {
+      if ((yvec = SelectVector (argv[3], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.xmin = atof (argv[1]);
+      graphmode.xmax = atof (argv[2]);
+      goto success;
+    }
+    if (ISNUM(argv[2][0]) && ISNUM(argv[3][0])) {
+      if ((xvec = SelectVector (argv[1], OLDVECTOR, FALSE)) == NULL) goto error;
+      graphmode.ymin = atof (argv[2]);
+      graphmode.ymax = atof (argv[3]);
+      goto success;
+    }
+    goto error;
+  }
+  
+  if (argc == 5) {
+    graphmode.xmin = atof (argv[1]);
+    graphmode.xmax = atof (argv[2]);
+    graphmode.ymin = atof (argv[3]);
+    graphmode.ymax = atof (argv[4]);
+    goto success;
+  }
+
+  gprint (GP_ERR, "USAGE: limits [xrange] [yrange]\n");
+  gprint (GP_ERR, " [range] may be either [min max] or a vector\n");
+  return (FALSE);
+
+ error:
+  gprint (GP_ERR, "error in vectors\n");
+  return (FALSE);
+
+ success:
+  SetLimits (xvec, yvec, &graphmode);
+  if (APPLY) KapaSetLimits (kapa, &graphmode);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/line.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/line.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/line.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int line (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata graphmode;
+  float x[2], y[2];
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: line <x> <y> to <x> <y>\n");
+    return (FALSE);
+  }
+  x[0] = atof(argv[1]);
+  y[0] = atof(argv[2]);
+  x[1] = atof(argv[4]);
+  y[1] = atof(argv[5]);
+
+  /* set point style and errorbar mode (these are NOT sticky) */
+  graphmode.style = 0;
+  graphmode.etype = 0;
+
+  if (!KapaPrepPlot (kapa, 2, &graphmode)) return (FALSE);
+  KapaPlotVector (kapa, 2, x, "x");
+  KapaPlotVector (kapa, 2, y, "y");
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_buffers.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 17422)
@@ -0,0 +1,10 @@
+# include "data.h"
+
+int list_buffers (int argc, char **argv) {
+
+  if (argc == 3) PrintBuffers (TRUE);
+  else PrintBuffers (FALSE);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_header.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_header.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_header.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int header (int argc, char **argv) {
+  
+  int j, N, nlines, nbytes, Nbytes, LOADHEAD, status, bitpix, unsign;
+  char *p, filename[128];
+  FILE *f;
+  double bscale, bzero;
+  Buffer *buf;
+
+  LOADHEAD = FALSE;
+  if ((N = get_argument (argc, argv, "-w"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+    LOADHEAD = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: header <buffer> [-w filename]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (LOADHEAD) {
+    f = fopen (filename, "r");
+    if (f == (FILE *) NULL) {
+      gprint (GP_ERR, "file %s not found\n", filename);
+      return (FALSE);
+    }
+    fclose (f);
+    
+    bitpix = buf[0].header.bitpix;
+    bzero  = buf[0].header.bzero;
+    bscale = buf[0].header.bscale;
+    unsign = buf[0].header.unsign;
+    gfits_free_header (&buf[0].header);
+    
+    strcpy (filename, buf[0].file);
+    strcpy (buf[0].file, "*");
+    strcat (buf[0].file, filename);
+    status = gfits_read_header (argv[2], &buf[0].header);
+    buf[0].header.bitpix = bitpix;     
+    buf[0].header.bzero  = bzero;      
+    buf[0].header.bscale = bscale;     
+    buf[0].header.unsign = unsign;     
+    gfits_modify (&buf[0].header, "BITPIX", "%d",  1, bitpix);
+    gfits_modify (&buf[0].header, "BSCALE", "%lf", 1, bscale);
+    gfits_modify (&buf[0].header, "BZERO",  "%lf", 1, bzero);
+    gfits_modify (&buf[0].header, "UNSIGN", "%t",  1, unsign);
+    
+  } else {
+
+    f = popen ("more", "w");
+    
+    p = gfits_header_field (&buf[0].header, "END", 1);
+    nlines = (p - buf[0].header.buffer) / 80;
+    nbytes = 81*nlines;
+
+    /* duplicate the header, add in the <return> chars, send to more */
+    ALLOCATE (p, char, nbytes);
+    for (j = 0; j < nlines; j++) {
+      memcpy (&p[81*j], &buf[0].header.buffer[80*j], 80);
+      p[81*j+80] = 10;
+    }
+    Nbytes = fwrite (p, sizeof(char), nbytes, f);
+    if (Nbytes != nbytes) {
+      gprint (GP_ERR, "warning: not all printed...\n");
+    }
+    free (p);
+
+    pclose (f);
+
+  }
+
+  return (TRUE);
+
+}
+
+/* XXX this function is not written in the context of the output file/buffer */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_vectors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 17422)
@@ -0,0 +1,8 @@
+# include "data.h"
+
+int list_vectors (int argc, char **argv) {
+
+  ListVectors ();
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/load.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/load.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/load.c	(revision 17422)
@@ -0,0 +1,144 @@
+# include "data.h"
+
+int load (int argc, char **argv) {
+  
+  int i, N, n, ISCEL;
+  int kapa, Noverlay, NOVERLAY;
+  char *c, type[10], string[128], line[1024];
+  double x, y, dx, dy, x1, y1;
+  double dra, ddec, ra1, dec1, ra, dec;
+  FILE *f;
+  char *buffer, *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  KapaImageData data;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  ISCEL = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    ISCEL = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: load (overlay) <filename>\n [-c] [-n]");
+    gprint (GP_ERR, "  -c: read overlay in celestial coords\n");
+    return (FALSE);
+  }
+  
+  if (!strcmp (argv[2], "-")) {
+    f = stdin;
+  } else {
+    f = fopen (argv[2], "r");
+  }
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find file %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if (ISCEL) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  ALLOCATE (buffer, char, 65536);  /* space for 512 lines of 128 bytes */
+  bzero (buffer, 65536);
+
+  dx = dy = 0;
+  for (n = 0; scan_line (f, line) != EOF;) {
+    c = strchr (line, '#');
+    if (c != (char *) NULL) 
+      *c = 0;  /* force end of line at comment! */
+    while ((c = strchr (line, '(')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ')')) != (char *) NULL) 
+      *c = ' ';
+    while ((c = strchr (line, ',')) != (char *) NULL) 
+      *c = ' ';
+    /* we could use some syntactial checks here */
+    /* have to get all three for this to be any valid object, if the line is commented out,
+     we should get none, so check that N == 3 before continuing: */
+    N = sscanf (line, "%s %lf %lf %lf %lf", type, &ra, &dec, &dra, &ddec);
+    switch (N) {
+    case 0:
+    case -1:
+      continue;
+    case 1:
+    case 2:
+    case 3:
+      if (strcmp (type, "TEXT")) {
+	gprint (GP_ERR, "syntax error in line:\n   %s\n", line);
+	continue;
+      }
+      sscanf (line, "%s %lf %lf %127s", type, &ra, &dec, string);
+    case 4:
+      ddec = dra;
+    case 5:
+      x = ra;
+      y = dec;
+      dx = dra;
+      dy = ddec;
+      if (ISCEL) {
+	if (!strcmp (type, "LINE")) {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra + dra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (y1 - y);
+	  dx = (x1 - x);
+	} else {
+	  RD_to_XY (&x, &y, ra, dec, &coords);
+	  ra1 = ra;
+	  dec1 = dec + ddec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dy = (fabs(x1 - x) + fabs(y1 - y));
+	  ra1 = ra + dra/cos(dec*RAD_DEG);;
+	  dec1 = dec;
+	  RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+	  dx = (fabs(x1 - x) + fabs(y1 - y));
+	}
+      }
+    }
+    overlay[Noverlay].type = KiiOverlayTypeByName (type);
+    if (overlay[Noverlay].type == KII_OVERLAY_TEXT) {
+      overlay[Noverlay].text = strcreate (string);
+    } else {
+      overlay[Noverlay].text = NULL;
+    }
+    overlay[Noverlay].x = x;
+    overlay[Noverlay].y = y;
+    overlay[Noverlay].dx = dx;
+    overlay[Noverlay].dy = dy;
+    Noverlay++;
+    CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+
+  for (i = 0; i < Noverlay; i++) {
+    if (overlay[i].text == NULL) continue;
+    free (overlay[i].text);
+  }
+  free (overlay);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+
+  if (f != stdin) {
+    fclose (f);
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/lookup.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/lookup.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/lookup.c	(revision 17422)
@@ -0,0 +1,40 @@
+# include "data.h"
+
+int lookup (int argc, char **argv) {
+  
+  int i, j;
+  float *ip, *op, *xp, *yp;
+  Vector *in, *out, *xv, *yv;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: lookup (input) (output) (x) (y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((xv  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yv  = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xv[0].Nelements != yv[0].Nelements) {
+      gprint (GP_ERR, "unmatched lookup table lengths\n");
+      return (FALSE);
+  }
+
+  out[0].Nelements = in[0].Nelements;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+
+  ip = in[0].elements;
+  op = out[0].elements;
+
+  for (i = 0; i < in[0].Nelements; i++, ip++, op++) {
+    // re-write this using bisection
+    xp = xv[0].elements;
+    yp = yv[0].elements;
+
+    for (j = 0; (*ip < *xp) && (j < yv[0].Nelements); j++);
+    *op = j;
+  }      
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mcreate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mcreate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mcreate.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "data.h"
+
+int mcreate (int argc, char **argv) {
+  
+  int Nx, Ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: mcreate <buffer> Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  Nx = atof (argv[2]);
+  Ny = atof (argv[3]);
+
+  /* I should encapsulate this in a create_default_buffer */
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 1.0, 0.0);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/medacc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/medacc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/medacc.c	(revision 17422)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int medacc (int argc, char **argv) {
+  
+  int i, j, Nbins, Nvalues, N, N0, N1;
+  double start, end, delta, k0, k1, fn;
+  float *V, *K, *V1, *K1, *O, *tmpvec, *tmpkey;
+  Vector *val, *key, *out;
+
+  if ((argc != 6) && (argc != 7)) {
+    gprint (GP_ERR, "USAGE: medacc <value> <vector> <key> start end [delta]\n");
+    return (FALSE);
+  }
+
+  if ((val = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((key = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (val[0].Nelements != key[0].Nelements) {
+    gprint (GP_ERR, "key and value don't match\n");
+    return (FALSE);
+  }
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  start = atof (argv[4]);
+  end   = atof (argv[5]);
+  if (argc == 7) 
+    delta = atof (argv[6]);
+  else 
+    delta = 1;
+
+  delta = fabs (delta);
+  if (end - start < 0) {
+    delta = -1.0 * delta;
+  }
+  Nbins = 1 + (int)((end - start) / delta);
+
+  out[0].Nelements = Nbins;
+  REALLOCATE (out[0].elements, float, out[0].Nelements);
+  bzero (out[0].elements, sizeof(float)*out[0].Nelements);
+
+  /* copy vec and key to temp vectors */
+  ALLOCATE (tmpvec, float, val[0].Nelements);
+  ALLOCATE (tmpkey, float, val[0].Nelements);
+
+  V = val[0].elements;
+  K = key[0].elements;
+  V1 = tmpvec;
+  K1 = tmpkey;
+  Nvalues = val[0].Nelements;
+  for (i = 0; i < Nvalues; i++, V++, K++, V1++, K1++) {
+    *V1 = *V;
+    *K1 = *K;
+  }      
+
+  /* sort vec and key by key */
+  fsortpair (tmpkey, tmpvec, Nvalues);
+
+  O = out[0].elements;
+  /* find the start and end key for each range */
+  N0 = 0;
+  N1 = 0;
+  for (i = 0; i < Nbins; i++) {
+    k0 = i*delta + start;
+    k1 = (i+1)*delta + start;
+    for (j = N1; (j < Nvalues) && (tmpkey[j] < k0); j++);
+    N0 = j;
+    for (j = N0; (j < Nvalues) && (tmpkey[j] < k1); j++);
+    N1 = j;
+    N = N1 - N0;
+    fsort (&tmpvec[N0], N);
+    fn = O[i] = 0;
+    for (j = N0 + 0.25*N; j < N0 + 0.75*N; j++) {
+      O[i] += tmpvec[j];
+      fn += 1.0;
+    }
+    if (fn > 0) O[i] /= fn;
+  }      
+  
+  free (tmpvec);
+  free (tmpkey);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mget.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mget.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mget.c	(revision 17422)
@@ -0,0 +1,61 @@
+# include "data.h"
+
+int mget (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *in, *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mget <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    vec[0].Nelements = Npix = Nx;
+    REALLOCATE (vec[0].elements, float, Npix);
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nx*Nset;
+    out = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  } else {
+    vec[0].Nelements = Npix = Ny;
+    REALLOCATE (vec[0].elements, float, Npix);
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    in  = (float *) buf[0].matrix.buffer + Nset;
+    out = vec[0].elements;
+    for (i = 0; i < Npix; i++, in+=Nx, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  }    
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/minterpolate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/minterpolate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/minterpolate.c	(revision 17422)
@@ -0,0 +1,159 @@
+# include "data.h"
+
+int minterp (int argc, char **argv) {
+  
+  int i, j, status, nx, ny, Nx, Ny, N, Extrapolate;
+  int ic, jc, dx, dy, Npix;
+  char temp[1024];
+  double scale, scale2, dX, dY;
+  float *V00, *V01, *V10, *V11, *Vout, dV1, dV2, dV3;
+  float *buf, I, J, x, y, xs, xe, ys, ye;
+  Buffer *in, *out;
+
+  /* choose the appropriate graphing window */
+  Extrapolate = FALSE;
+  if ((N = get_argument (argc, argv, "-extrapolate"))) {
+    remove_argument (N, &argc, argv);
+    Extrapolate = TRUE;
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: minterpolate <from> <to> scale [-extrapolate]\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  scale2 = scale*scale;
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  nx = Nx * scale;
+  ny = Ny * scale;
+
+  /* create new matrix */
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  /* fix astrometric terms */
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  dX /= scale;
+  dY /= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  dX *= scale;
+  dY *= scale;
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  /* adjust filename */
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  dX = dY = scale;
+
+  buf = (float *)in[0].matrix.buffer;
+  Npix = 0;
+
+  if (Extrapolate) {
+    for (j = 0; j < Ny - 1; j++) {
+      for (i = 0; i < Nx - 1; i++) {
+	V00 = buf + i + j*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == 0)      { xs = -0.5*scale; }
+	if (i == Nx - 2) { xe =  1.5*scale; }
+
+	if (j == 0)      { ys = -0.5*scale; }
+	if (j == Ny - 2) { ye =  1.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  } else {
+     for (j = -1; j < Ny; j++) {
+      for (i = -1; i < Nx; i++) {
+	ic = MIN (MAX (i, 0), Nx-1);  /* we never actually reach Nx, Ny */
+	jc = MIN (MAX (j, 0), Ny-1);
+	V00 = buf + ic + jc*Nx;
+	V10 = V00 + 1;
+	V01 = V00 + Nx;
+	V11 = V01 + 1;
+
+	if ((i == -1) || (i == Nx - 1)) { V10 = V00; } else { V10 = V00 + 1; }
+	if ((j == -1) || (j == Ny - 1)) { V01 = V00; } else { V01 = V00 + Nx; }
+	if ((i == -1) || (i == Nx - 1)) { V11 = V01; } else { V11 = V01 + 1; }
+
+	dV1 = (*V11 + *V00 - *V01 - *V10) / scale2;
+	dV2 = (*V01 - *V00) / scale;
+	dV3 = (*V10 - *V00) / scale;
+
+	x = (i + 0.5) * scale;
+	y = (j + 0.5) * scale;
+
+	xs = ys = 0;
+	xe = ye = scale;
+
+	if (i == -1)     { xs = 0.5*scale; }
+	if (i == Nx - 1) { xe = 0.5*scale; }
+
+	if (j == -1)     { ys = 0.5*scale; }
+	if (j == Ny - 1) { ye = 0.5*scale; }
+
+	for (J = ys; J < ye; J += 1.0) {
+	  dx = (x + xs);
+	  dy = (y + J);
+	  Vout = (float *)(out[0].matrix.buffer) + dy*nx + dx;
+	  for (I = xs; I < xe; I += 1.0, Vout++) {
+	    *Vout = dV1 * (I*J) + dV2 * J + dV3 * I + *V00;
+	    Npix ++;
+	  }
+	}
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mkrgb.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mkrgb.c	(revision 17422)
@@ -0,0 +1,78 @@
+# include "data.h"
+# include "jpeglib.h"
+
+int mkrgb (int argc, char **argv) {
+ 
+  int i, j, Nx, Ny;
+  FILE *f;
+  Buffer *red, *green, *blue;
+  float *Vr, *Vg, *Vb;
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
+  JSAMPLE *image_buffer;	/* Points to data for current line */
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mkrgb (red) (green) (blue) (output)\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((red    = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((green  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((blue   = SelectBuffer (argv[3], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = red[0].matrix.Naxis[0];
+  Ny = red[0].matrix.Naxis[1];
+  if (Nx != blue[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != blue[0].matrix.Naxis[1]) return (FALSE);
+  if (Nx != green[0].matrix.Naxis[0]) return (FALSE);
+  if (Ny != green[0].matrix.Naxis[1]) return (FALSE);
+
+  f = fopen (argv[4], "w");
+  if (f == (FILE *) NULL) {
+    fprintf (stderr, "failed to open %s for output\n", argv[4]);
+    return (FALSE);
+  }
+
+  /* set up the error handler , initialize the JPEG compression object. */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_compress (&cinfo);
+  jpeg_stdio_dest(&cinfo, f);
+  
+  // set up the basic jpeg output file
+  cinfo.image_width = Nx; 	/* image width and height, in pixels */
+  cinfo.image_height = Ny;
+  cinfo.input_components = 3;		        
+  cinfo.in_color_space = JCS_RGB; 	
+  jpeg_set_defaults (&cinfo);
+  jpeg_set_quality (&cinfo, 75, TRUE       /* limit to baseline-JPEG values */);
+  jpeg_start_compress (&cinfo, TRUE);
+
+  ALLOCATE (image_buffer, JSAMPLE, 3*Nx);
+
+  // ??
+  // && (cinfo.next_scanline < cinfo.image_height)
+
+  Vr = (float *) red[0].matrix.buffer;
+  Vg = (float *) green[0].matrix.buffer;
+  Vb = (float *) blue[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++, Vr++, Vg++, Vb++) {
+      image_buffer[3*i+0] = MAX (0.0, MIN (255.0, *Vr));
+      image_buffer[3*i+1] = MAX (0.0, MIN (255.0, *Vg));
+      image_buffer[3*i+2] = MAX (0.0, MIN (255.0, *Vb));
+    }
+    row_pointer[0] = image_buffer;
+    (void) jpeg_write_scanlines (&cinfo, row_pointer, 1);
+  }
+
+  jpeg_finish_compress (&cinfo);
+  fclose (f);
+
+  jpeg_destroy_compress (&cinfo);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mset.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mset.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/mset.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int mset (int argc, char **argv) {
+  
+  int i, Nx, Ny, Npix, xdir, Nset;
+  float *in, *out;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (strcasecmp (argv[3], "-x") && strcasecmp (argv[3], "-y")) {
+    gprint (GP_ERR, "USAGE: mset <buffer> <vector> <-x/-y> <N>\n");
+    return (FALSE);
+  }
+  xdir = TRUE;
+  if (!strcasecmp (argv[3], "-y")) xdir = FALSE;
+
+  Npix = vec[0].Nelements;
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  Nset = atof (argv[4]);
+  if (Nset < 0) {
+    gprint (GP_ERR, "selection out of range\n");
+    return (FALSE);
+  }
+
+  if (xdir) {
+    if (Nx != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Ny) {
+      gprint (GP_ERR, "row out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nx*Nset;
+    in = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out++) {
+      *out = *in;
+    }
+    return (TRUE);
+  } else {
+    if (Ny != Npix) {
+      gprint (GP_ERR, "dimensions don't match\n");
+      return (FALSE);
+    }
+    if (Nset >= Nx) {
+      gprint (GP_ERR, "column out of range\n");
+      return (FALSE);
+    }
+    out = (float *) buf[0].matrix.buffer + Nset;
+    in = vec[0].elements;
+    for (i = 0; i < Npix; i++, in++, out+=Nx) {
+      *out = *in;
+    }
+    return (TRUE);
+  }    
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/peak.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/peak.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/peak.c	(revision 17422)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int peak (int argc, char **argv) {
+  
+  int i, N, imax, QUIET;
+  double start, end, xmax, ymax;
+  float *X, *Y;
+  Vector *vecx, *vecy;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 5) && (argc != 3)) {
+    gprint (GP_ERR, "USAGE: peak <x> <y> [start end]\n");
+    return (FALSE);
+  }
+
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 5) {
+    start = atof (argv[3]);
+    end   = atof (argv[4]);
+  } else {
+    start = vecx[0].elements[0];
+    end   = vecx[0].elements[vecx[0].Nelements - 1];
+  }
+
+  X = vecx[0].elements;
+  Y = vecy[0].elements;
+
+  imax = -1;
+  xmax = *X;
+  ymax = *Y;
+  for (i = 1; i < vecx[0].Nelements-1; i++, X++, Y++) {
+    if ((*X >= start) && (*X <= end)) {
+      if ((imax == -1) || (*Y > ymax)) {
+	xmax = *X;
+	ymax = *Y;
+	imax = i;
+      }
+    }
+  }      
+
+  set_variable ("peakval", ymax);
+  set_variable ("peakpos", xmax);
+  set_variable ("peaknum", imax);
+
+  if (!QUIET) gprint (GP_LOG, "peak %f @ %f (%d)\n", ymax, xmax, imax);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/periodogram.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/periodogram.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/periodogram.c	(revision 17422)
@@ -0,0 +1,114 @@
+# include "data.h"
+
+int periodogram (int argc, char **argv) {
+  
+  int i, N, Npt, Np, NP, VERBOSE;
+  float *tv, *fv;
+  float minP, maxP, minT, maxT, dTime;
+  float mean, var, w, tau, P, Pc, Ps, Po;
+  float C, S, cs, sn, cs2, sn2, ratio;
+  Vector *time, *flux, *power, *period;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: periodogram (time) (flux) (minP) (maxP) (period) (power)\n");
+    return (FALSE);
+  }
+  
+  if ((time = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((flux = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  minP = atof(argv[3]);
+  maxP = atof(argv[4]);
+  if ((period = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((power = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* find the max baseline, mean, and variance */
+  minT = maxT = time[0].elements[0];
+  Npt = time[0].Nelements;
+  tv = time[0].elements;
+  fv = flux[0].elements;
+  mean = var = 0;
+  for (i = 0; i < Npt; i++, tv++, fv++) {
+    minT = MIN (minT, *tv);
+    maxT = MAX (maxT, *tv);
+    mean += *fv;
+  }
+  mean = mean / Npt;
+  fv = flux[0].elements;
+  for (i = 0; i < Npt; i++, fv++) {
+    var += SQ(*fv - mean);
+  }
+  var = var / (Npt - 1);
+
+  if (VERBOSE) gprint (GP_ERR, "mean: %f, var: %f, minT: %f, maxT: %f\n", mean, var, minT, maxT);
+
+  dTime = maxT - minT;
+  if (dTime == 0) {
+    gprint (GP_ERR, "ERROR: time range is zero\n");
+    return (FALSE);
+  }
+
+  Np = 0;
+  NP = 100;
+  REALLOCATE (power[0].elements, float, NP);
+  REALLOCATE (period[0].elements, float, NP);
+
+  P = minP;
+  while (P < maxP) {
+    w = 2*M_PI/P;
+    
+    /* find the period offset tau  */
+    tv = time[0].elements;
+    cs = sn = 0;
+    for (i = 0; i < Npt; i++, tv++) {
+      cs += cos (*tv*w*2);
+      sn += sin (*tv*2*2);
+    }
+    tau = 0.5*atan2 (sn, cs) / w;
+      
+    /* find the power at this period */
+    tv = time[0].elements;
+    fv = flux[0].elements;
+    cs = sn = cs2 = sn2 = 0;
+    for (i = 0; i < Npt; i++, tv++, fv++) {
+      C = cos (w*(*tv-tau));
+      S = sin (w*(*tv-tau));
+      // C = cos (w**tv);
+      // S = sin (w**tv);
+      cs += (*fv - mean) * C;
+      sn += (*fv - mean) * S;
+      cs2 += SQ(C);
+      sn2 += SQ(S);
+    }
+    Pc = SQ(cs) / cs2;
+    Ps = SQ(sn) / sn2;
+    Po = (Pc + Ps) / (2*var);
+
+    power[0].elements[Np] = Po;
+    period[0].elements[Np] = P;
+    Np ++;
+    if (Np >= NP) {
+      NP += 100;
+      REALLOCATE (power[0].elements, float, NP);
+      REALLOCATE (period[0].elements, float, NP);
+    }
+
+    ratio = 1 + 0.1*P/dTime;
+
+    if (VERBOSE) gprint (GP_ERR, "tau: %f, P: %f, ratio: %f, dTime: %f, nextP: %f\n", tau, P, ratio, dTime, P*ratio);
+
+    P *= ratio;
+  }
+
+  power[0].Nelements = Np;
+  period[0].Nelements = Np;
+  REALLOCATE (power[0].elements, float, Np);
+  REALLOCATE (period[0].elements, float, Np);
+ 
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/plot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/plot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/plot.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int plot (int argc, char **argv) {
+  
+  int kapa, N, Npts;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *dxmvec, *dxpvec, *dymvec, *dypvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* decide on error bars */
+  dxmvec = dxpvec = dymvec = dypvec = NULL;
+  if ((N = get_argument (argc, argv, "-dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxmvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dx"))) {
+    remove_argument (N, &argc, argv);
+    if ((dxpvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dymvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "+dy"))) {
+    remove_argument (N, &argc, argv);
+    if ((dypvec = SelectVector (argv[N], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: plot <x> <y> [style]\n");
+    return (FALSE);
+  }
+
+  /* set errorbar mode (these are NOT sticky) */
+  graphmode.etype = 0;
+  if ((dymvec != NULL) && (dypvec == NULL)) dypvec = dymvec;
+  if ((dypvec != NULL) && (dymvec == NULL)) dymvec = dypvec;
+  if ((dypvec != NULL) || (dymvec != NULL)) graphmode.etype |= 0x01;
+  if ((dxmvec != NULL) && (dxpvec == NULL)) dxpvec = dxmvec;
+  if ((dxpvec != NULL) && (dxmvec == NULL)) dxmvec = dxpvec;
+  if ((dxpvec != NULL) || (dxmvec != NULL)) graphmode.etype |= 0x02;
+  
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  Npts = xvec[0].Nelements;
+  if (Npts == 0) return (TRUE);
+
+  if (!KapaPrepPlot (kapa, Npts, &graphmode)) return (FALSE);
+  
+  KapaPlotVector (kapa, Npts, xvec[0].elements, "x");
+  KapaPlotVector (kapa, Npts, yvec[0].elements, "y");
+  if (graphmode.etype & 0x01) {
+    KapaPlotVector (kapa, Npts, dymvec[0].elements, "dym");
+    KapaPlotVector (kapa, Npts, dypvec[0].elements, "dyp");
+  }
+  if (graphmode.etype & 0x02) {
+    KapaPlotVector (kapa, Npts, dxmvec[0].elements, "dxm");
+    KapaPlotVector (kapa, Npts, dxpvec[0].elements, "dxp");
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/point.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/point.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/point.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+int point (int argc, char **argv) {
+  
+  int N, celestial, pixscale;
+  int kapa;
+  double ra, dec, dra, ddec, angle;
+  double x1, y1, ra1, dec1;
+  char *name;
+  Coords coords;
+  Buffer *buf;
+  KiiOverlay overlay;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImageData (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+  
+  pixscale = FALSE;
+  if ((N = get_argument (argc, argv, "-pixscale"))) {
+    remove_argument (N, &argc, argv);
+    pixscale = TRUE;
+  }
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-angle"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: point (overlay) TYPE x y dx dy [-c]\n");
+    return (FALSE);
+  }
+  
+  if (celestial) {
+    if ((buf = SelectBuffer (data.name, OLDBUFFER, TRUE)) == NULL) return (FALSE);
+    GetCoords (&coords, &buf[0].header);
+  }
+
+  if (celestial) {
+    ra   = atof(argv[3]);
+    dec  = atof(argv[4]);
+    dra  = atof(argv[5]);
+    ddec = atof(argv[6]);
+
+    fRD_to_XY (&overlay.x, &overlay.y, ra, dec, &coords);
+    if (pixscale) {
+      overlay.dx = atof(argv[5]);
+      overlay.dy = atof(argv[6]);
+    } else {
+      ra1 = ra + dra;
+      dec1 = dec + ddec;
+      RD_to_XY (&x1, &y1, ra1, dec1, &coords);
+      overlay.dx = x1 - overlay.x;
+      overlay.dy = y1 - overlay.y;
+    }
+  }
+  else {
+    overlay.x  = atof(argv[3]);
+    overlay.y  = atof(argv[4]);
+    overlay.dx = atof(argv[5]);
+    overlay.dy = atof(argv[6]);
+  }
+  overlay.angle = angle;
+  overlay.type = KiiOverlayTypeByName (argv[2]);
+  KiiLoadOverlay (kapa, &overlay, 1, argv[1]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ps.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int ps (int argc, char **argv) {
+
+  char filename[1024], pagename[1024], *name;
+  int N, kapa, scaleMode, pageMode;
+  
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+
+  pageMode = KAPA_PS_NEWPLOT;
+
+  /* new page? */
+  if ((N = get_argument (argc, argv, "-newpage"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_NEWPAGE;
+  }
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    pageMode = KAPA_PS_RAWPAGE;
+  }
+
+  /* scale image? */
+  scaleMode = TRUE;
+  if ((N = get_argument (argc, argv, "-noscale"))) {
+    remove_argument (N, &argc, argv);
+    scaleMode = TRUE;
+  }
+
+  /* what file? */
+  filename[0] = 0;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (filename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  /* pagename ? */
+  strcpy (pagename, "default");
+  if ((N = get_argument (argc, argv, "-pagename"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (pagename, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* which tool */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc > 1) && filename[0]) goto help;
+
+  if (argc > 1) strcpy (filename, argv[1]);
+
+  // get the connection to kapa, false if none available
+  if (!GetGraphData (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (!filename[0]) strcpy (filename, "kapa.ps");
+  
+  /* tell Ximage/Xgraph to ps the image */
+  KiiPS (kapa, filename, scaleMode, pageMode, pagename);
+  return (TRUE);
+
+help:
+  gprint (GP_ERR, "USAGE: ps [-name file.ps] [-g | -i] [-n device] [-raw] [-noscale] [-newpage] [-pagename (name]\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedelete.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedelete.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedelete.c	(revision 17422)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queuedelete (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuedelete (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  DeleteQueue (queue);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedrop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedrop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuedrop.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "data.h"
+
+int queuedrop (int argc, char **argv) {
+  
+  int N;
+  char *Key;
+  char *line;
+  char *Value;
+  Queue *queue;
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) || (Key == NULL)) {
+    gprint (GP_ERR, "USAGE: queuedrop (queue) [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  /* drop all matching entries, if any exist */
+  while ((line = PopQueueMatch (queue, Key, Value)) != NULL) {
+    free (line);
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueinit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueinit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueinit.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "data.h"
+
+int queueinit (int argc, char **argv) {
+
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueinit (name)\n");
+    return (FALSE);
+  }
+
+  queue = CreateQueue (argv[1]);
+  InitQueue (queue);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuelist.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuelist.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuelist.c	(revision 17422)
@@ -0,0 +1,12 @@
+# include "data.h"
+
+int queuelist (int argc, char **argv) {
+  
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: queuelist\n");
+    return (FALSE);
+  }
+
+  ListQueues ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueload.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueload.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueload.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "data.h"
+
+int queueload (int argc, char **argv) {
+  
+  char *A, *B, *val;
+  int i, status;
+  int Nread, Nbytes, NBYTES;
+  FILE *f;
+  Queue *queue;
+
+  if (argc != 4) goto usage;
+  if (strcmp(argv[2], "-x")) goto usage;
+  
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  /* val will hold the result of the command */
+  NBYTES = 1024;
+  ALLOCATE (val, char, NBYTES);
+    
+  /* loop until command produces no more output,  REALLOCATE as needed. */
+  f = popen (argv[3], "r");
+  Nbytes = 0;
+  Nread = 1;
+  while (Nread > 0) {
+    Nread = fread (&val[Nbytes], 1, 1023, f);
+    if (Nread < 0) { 
+      gprint (GP_ERR, "error reading from command\n");
+    }
+    if (Nread > 0) {
+      Nbytes += Nread;
+      NBYTES = 1024 + Nbytes;
+      REALLOCATE (val, char, NBYTES);
+    }
+  }
+  val[Nbytes] = 0;
+  status = pclose (f);
+    
+  if (status) {
+    gprint (GP_ERR, "warning: exit status of command %d\n", status);
+  }
+      
+  A = B = val;
+  for (i = 0; B != (char *) NULL;) {
+    while (isspace (*A) && (*A != 0)) A++;
+    B = strchr (A, '\n');
+    if (B != (char *) NULL) { *B = 0; }
+    if (*A != 0) {
+      PushQueue (queue, A);
+      A = B + 1;
+      i++;
+    }
+  }      
+  free (val);
+    
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: queueload (queue) -x (command)\n");
+  return (FALSE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepop.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int queuepop (int argc, char **argv) {
+  
+  int N;
+  char *Key;
+  char *var;
+  char *line;
+  char *Value;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Key = NULL;
+  Value = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Value = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuepop (queue) [-var variable] [-key N value]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (Key == NULL) {
+    line = PopQueue (queue);
+  } else {
+    line = PopQueueMatch (queue, Key, Value);
+  }
+
+  if (var == NULL) {
+    if (line == NULL) {
+      gprint (GP_ERR, "queue %s is empty or match not found\n", argv[1]);
+      return (FALSE);
+    } else {
+      gprint (GP_LOG, "%s\n", line);
+      free (line);
+      return (TRUE);
+    }
+  }
+
+  if (line == NULL) {
+    set_str_variable (var, "NULL");
+  } else {
+    set_str_variable (var, line);
+    free (line);
+  }
+
+  free (var);
+  if (Key != NULL) free (Key);
+  if (Value != NULL) free (Value);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueprint.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueprint.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queueprint.c	(revision 17422)
@@ -0,0 +1,21 @@
+# include "data.h"
+
+int queueprint (int argc, char **argv) {
+  
+  Queue *queue;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queueprint (name)\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  PrintQueue (queue);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepush.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepush.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuepush.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int queuepush (int argc, char **argv) {
+  
+  char *Key;
+  int N, Unique, Replace;
+  Queue *queue;
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Replace = FALSE;
+  if ((N = get_argument (argc, argv, "-replace"))) {
+    remove_argument (N, &argc, argv);
+    Replace = TRUE;
+  }
+
+  Key = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+    Key = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: queuepush (queue) (value) [-key N] [-uniq] [-replace]\n");
+    return (FALSE);
+  }
+
+  /* will create a queue if none exists */
+  queue = CreateQueue (argv[1]);
+
+  if (Unique) {
+    PushQueueUnique (queue, argv[2], Key);
+  }
+  if (Replace) {
+    PushQueueReplace (queue, argv[2], Key);
+  }
+  if (!Unique && !Replace) {
+    PushQueue (queue, argv[2]);
+  }
+
+  if (Key != NULL) free (Key);
+  return (TRUE);
+}
+
+
+/* 
+ * -key only needed for replace or unique : give an error otherwise
+ * -uniq searched for a match and does NOT replace if matched
+ * -replace searches for a match and replaces if matched
+ * should trigger an error if -uniq and -replace...
+ */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuesize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuesize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/queuesize.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "data.h"
+
+int queuesize (int argc, char **argv) {
+  
+  int N;
+  char *var;
+  Queue *queue;
+
+  var = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    var = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: queuesize (name) [-var variable]\n");
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "ERROR: queue %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (var == (char *) NULL) {
+    gprint (GP_ERR, "Nlines: %d\n", queue[0].Nlines);
+    return (TRUE);
+  }
+
+  set_int_variable (var, queue[0].Nlines);
+  free (var);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/radial.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/radial.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/radial.c	(revision 17422)
@@ -0,0 +1,97 @@
+# include "data.h"
+
+int circstats (int argc, char **argv) {
+  
+  int i, j;
+  double Npix, S1, S2, max, min, Sum, Mean, Stdev, IgnoreValue; 
+  double xc, yc, radius, R2, r;
+  float *V;
+  int xs, ys, xe, ye;
+  int Ignore, Quiet, N, Nx, Ny;
+  Buffer *buf;
+
+  Ignore = FALSE;
+  IgnoreValue = 0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) goto usage;
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) goto missed;
+
+  xc = atof (argv[2]);
+  yc = atof (argv[3]);
+  radius = atof (argv[4]);
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  if (xc < 0) goto range;
+  if (yc < 0) goto range;
+  if (xc >= Nx) goto range;
+  if (yc >= Ny) goto range;
+
+  xs = MAX (0, xc - radius);
+  ys = MAX (0, yc - radius);
+  xe = MIN (Nx, xc + radius + 1);
+  ye = MAX (Ny, yc + radius + 1);
+  R2 = radius*radius;
+
+  S1 = S2 = Npix = 0;
+  min = max = *(float *)(buf[0].matrix.buffer) + (int)(yc)*buf[0].matrix.Naxis[0] + (int)(xc); 
+  for (j = ys; j < ye; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + xs; 
+    for (i = xs; i < xe; i++, V++) {
+      r = SQ(i - xc) + SQ(j - yc);
+      if (r > R2) continue;
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      S1 += *V;
+      S2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  Mean = S1 / Npix;
+  Sum  = Mean * M_PI * R2;
+  Stdev = sqrt (S2/Npix - Mean*Mean);
+
+  if (!Quiet) {
+    gprint (GP_LOG, "     mean     stdev       min       max    Npix     Total\n");
+    gprint (GP_LOG, "%9.4g %9.4g %9.4g %9.4g %7.0f %9.4g\n", Mean, Stdev, min, max, Npix, Sum);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEAN",   Mean);
+  set_variable ("SUM",    Sum);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  Stdev);
+
+  return (TRUE);
+
+ usage: 
+  gprint (GP_ERR, "USAGE: circstats <buffer> x y radius\n");
+  return (FALSE);
+
+ range:
+  gprint (GP_ERR, "ERROR: coordinates out of range\n");
+  return (FALSE);
+
+ missed:
+  gprint (GP_ERR, "ERROR: buffer not found\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rd.c	(revision 17422)
@@ -0,0 +1,227 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rd (int argc, char **argv) {
+  
+  int i, N, status, plane, Nplane, extend, Nextend, Nskip, JustHead;
+  int ccdsel, done, Nword, IsCompressed;
+  char region[512], *ccdid, *filename;
+  FILE *f;
+  Buffer *buf;
+
+  JustHead = FALSE;
+  if ((N = get_argument (argc, argv, "-head"))) {
+    remove_argument (N, &argc, argv);
+    JustHead = TRUE;
+  }
+
+  plane = 1;
+  if ((N = get_argument (argc, argv, "-plane"))) {
+    remove_argument (N, &argc, argv);
+    plane  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  extend = FALSE;
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Nextend  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    extend = TRUE;
+  }
+
+  ccdsel = FALSE;
+  ccdid = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    ccdid  = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    ccdsel = TRUE;
+  }
+
+  if (extend && ccdsel) {
+    gprint (GP_ERR, "only specify one of -n and -x\n");
+    return (FALSE);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rd <buffer> <filename> [-head] [-plane N] [-n ccdid] [-x extnum]\n");
+    return (FALSE);
+  }
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) {
+      fclose (f);
+      return (FALSE);
+  }
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = FALSE;
+  IsCompressed = FALSE;
+
+  /*** advance to the correct FITS extension ***/
+
+  /* FITS extension by number */
+  if (extend) {
+    /* load in appropriate header */
+    Nskip = gfits_fread_Xheader (f, &buf[0].header, Nextend);
+    if (!Nskip) {
+      gprint (GP_ERR, "entry in %s not found\n", argv[2]);
+      DeleteBuffer (buf);
+      fclose (f);
+      return (FALSE);
+    }
+    if (gfits_extension_is_compressed (&buf[0].header)) {
+	IsCompressed = TRUE;
+    }
+  } 
+
+  /* FITS extension by name */
+  if (ccdsel) {
+    char *CCDKeyword, ID[64];
+
+    CCDKeyword = get_variable ("CCDKEYWORD");
+    if (CCDKeyword == (char *) NULL) {
+      // gprint (GP_ERR, "CCDKEYWORD variable is not set; ");
+      // gprint (GP_ERR, "using EXTNAME as default\n");
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+    done = FALSE;
+    for (i = 0; !done; i++) {
+      Nskip = gfits_fread_Xheader (f, &buf[0].header, i);
+      if (!Nskip) {
+	gprint (GP_ERR, "extension %s in %s not found\n", ccdid, argv[2]);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      // for compressed data tables, EXTNAME may be duplicated, with the first one containing the
+      // word 'COMPRESSED_IMAGE'.  in this case, check the second EXTNAME, if CCDKeyword == EXTNAME 
+      // this may have to be a more obscure test specifically for 'imcopy' data...
+      // need to check each header, since file may contain a mix
+      
+      Nword = 1;
+      IsCompressed = FALSE;
+      if (gfits_extension_is_compressed (&buf[0].header)) {
+	if (!strcmp (CCDKeyword, "EXTNAME")) Nword = 1;
+	IsCompressed = TRUE;
+      }
+      if (!gfits_scan (&buf[0].header, CCDKeyword, "%s", Nword, ID)) {
+	gprint (GP_ERR, "%s not in header\n", CCDKeyword);
+	DeleteBuffer (buf);
+	free (CCDKeyword);
+	fclose (f);
+	return (FALSE);
+      }
+
+      /* compare as numbers if both are pure numbers, else as strings */
+      done = strnumcmp (ccdid, ID);
+      if (!done) gfits_free_header (&buf[0].header);
+    }
+    free (CCDKeyword);
+  }
+
+  /* fix up header, if needed */
+  if (extend || ccdsel) {
+    if (!IsCompressed) {
+      gfits_extended_to_primary (&buf[0].header, TRUE, "Standard FITS");
+    }
+  } else {
+      gfits_fread_header (f, &buf[0].header);
+  }
+
+  /* for JustHead, we skip reading the data segment */ 
+  // XXX for compressed data, we need to convert the header to the equivalent uncompressed version
+  if (JustHead) {
+    // XXX what are we doing here exactly?
+    buf[0].header.Naxes = 0;
+    ALLOCATE (buf[0].matrix.buffer, char, 1);
+    buf[0].matrix.size = 0;
+    buf[0].bitpix = 16;
+    buf[0].bzero = 0;
+    buf[0].bscale = 1;
+    buf[0].matrix.bitpix = 16;
+    buf[0].matrix.bzero = 0;
+    buf[0].matrix.bscale = 1;
+    buf[0].header.bitpix = 16;
+    buf[0].header.bzero = 0;
+    buf[0].header.bscale = 1;
+    fclose (f);
+    return (TRUE);
+  }
+
+  /* check for valid plane */
+  Nplane = buf[0].header.Naxis[2];
+  if (Nplane == 0) Nplane = 1;
+  if (plane > Nplane) {
+    gprint (GP_ERR, "-plane is too large: %d total planes\n", Nplane);
+    DeleteBuffer (buf);
+    fclose (f);
+    return (FALSE);
+  }
+
+  /* load matrix data */
+  if (IsCompressed) {
+    FTable ftable;
+    Header theader;
+    ftable.header = &theader;
+    gfits_copy_header (&buf[0].header, ftable.header);
+    status = gfits_fread_ftable_data (f, &ftable);  // this just reads the bytes (not even a SWAP)
+    status = gfits_uncompress_image (&buf[0].header, &buf[0].matrix, &ftable);
+    gfits_free_table (&ftable);
+    // XXX this currently does not work for a cube (we get a cube back, not a specific plane)
+  } else {
+    sprintf (region, "-1 -1 -1 -1 %d %d", (plane - 1), plane);
+    status = gfits_fread_matrix_segment (f, &buf[0].matrix, &buf[0].header, region);
+    fclose (f);
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+
+  /* adjust buffer to represent 2D data */
+  if (Nplane > 1) {
+    buf[0].header.Naxis[2] = 0;
+    buf[0].header.Naxes = 2;
+    gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
+    gfits_delete (&buf[0].header, "NAXIS3", 1);
+  }
+
+  /* we need to return a 2D array, convert 1D images to 2D (Naxis[1] = 1) */
+  if (buf[0].header.Naxes == 1) {
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+  gprint (GP_LOG, "read %d bytes from %s into buffer %s\n", 
+	   buf[0].header.size + buf[0].matrix.size, argv[2], argv[1]);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rdseg.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rdseg.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rdseg.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+/* there is some confusion in this function with the several possible options */
+int rdseg (int argc, char **argv) {
+  
+  int x, y, nx, ny, status;
+  char region[512], *filename;
+  FILE *f;
+  Buffer *buf;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: rdseg <buffer> <filename> x y nx ny\n");
+    return (FALSE);
+  }
+  x = atoi (argv[3]);
+  y = atoi (argv[4]);
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  /* test if file exists */
+  f = fopen (argv[2], "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "file %s not found\n", argv[2]);
+    return (FALSE);
+  }
+  fclose (f);
+
+  /* find matrix, free old data */
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+
+  /* save file name */
+  filename = filebasename (argv[2]);
+  strcpy (buf[0].file, filename);
+  free (filename);
+
+  status = gfits_read_header (argv[2], &buf[0].header);
+  sprintf (region, "%d %d %d %d 0 1", x, nx + x, y, ny + y);
+  status = gfits_read_matrix_segment (argv[2], &buf[0].matrix, region);
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, ny);
+  buf[0].header.Naxis[0] = nx;
+  buf[0].header.Naxis[1] = ny;
+  buf[0].matrix.Naxis[0] = nx;
+  buf[0].matrix.Naxis[1] = ny;
+
+  if (!status) {
+    gprint (GP_ERR, "problem reading file, buffer not opened\n");
+    DeleteBuffer (buf);
+    return (FALSE);
+  }
+  if (buf[0].header.Naxes == 1) {
+    /* we need to return an array, so make Naxis[1] = 1 */
+    buf[0].header.Naxes = 2;
+    buf[0].header.Naxis[1] = 1;
+    buf[0].matrix.Naxis[1] = 1;
+  }    
+
+  buf[0].bitpix = buf[0].header.bitpix;    /* store the original values */
+  buf[0].bscale = buf[0].header.bscale;    /* store the original values */
+  buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
+  buf[0].unsign = buf[0].header.unsign;
+  gprint (GP_LOG, "read %d bytes from %s into buffer %s\n", 
+	   buf[0].header.size + buf[0].matrix.size, argv[2], argv[1]);
+
+  /** now - convert the matrix values to floats for internal use **/
+  gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+  
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 17422)
@@ -0,0 +1,321 @@
+# include "data.h"
+
+FILE *f = (FILE *) NULL;
+char filename[256];
+
+int datafile (int argc, char **argv) {
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: datafile (filename)\n");
+    return (FALSE);
+  }
+  
+  strcpy (filename, argv[1]);
+  if (f != (FILE *) NULL) { fclose (f); }
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "failed to open file %s\n", argv[1]);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int read_vectors (int argc, char **argv) {
+  
+  int i, j, Nskip, Nvec, *col, done, status;
+  int Nbytes, nbytes, Nstart, NELEM, N, nread;
+  char *colstr, *c0, *c1, *buffer, *extname;
+  double value;
+  Vector **vec;
+
+  /* auto-sense table type */
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    extname = strcreate (argv[N]);
+    if (extname == (char *) NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    status = read_table_vectors (argc, argv, extname);
+    free (extname);
+    return (status);
+  }
+
+  Nskip = 0;
+  if ((N = get_argument (argc, argv, "-skip"))) {
+    remove_argument (N, &argc, argv);
+    Nskip = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc < 3) || !(argc % 2)) {
+    gprint (GP_ERR, "USAGE: read name N name N ...\n");
+    return (FALSE);
+  }
+  /* read name N name N  */
+
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "no open file for read\n");
+    return (FALSE);
+  }
+  fseek (f, 0, SEEK_SET);
+
+  Nvec = (argc - 1) / 2;
+  ALLOCATE (vec, Vector *, Nvec);
+  ALLOCATE (col, int, Nvec);
+
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, "USAGE: read name N name N ...\n");
+      free (vec);
+      free (col);
+      return (FALSE);    
+    }
+    colstr = argv[2*i+2];
+    for (j = 0; j < strlen (colstr); j++) {
+      if (!isdigit(colstr[j])) {
+	gprint (GP_ERR, "USAGE: read name N name N ...\n");
+	free (vec);
+	free (col);
+	return (FALSE);    
+      }
+    }
+    col[i] = atof (colstr);
+  }
+  
+  NELEM = 1000;
+  for (i = 0; i < Nvec; i++) {
+    REALLOCATE (vec[i][0].elements, float, NELEM);
+  }
+  
+  ALLOCATE (buffer, char, 0x10001);
+  bzero (buffer, 0x10001);
+  for (i = 0; i < Nskip; i++) {
+    scan_line (f, buffer);
+  }
+
+  Nstart = 0;
+  N = 0;
+  done = FALSE;
+  while (!done) {
+    Nbytes = 0x10000 - Nstart;
+    bzero (&buffer[Nstart], Nbytes);
+    nread = fread (&buffer[Nstart], 1, Nbytes, f);
+    if (ferror (f)) {
+      perror ("error reading data file");
+      break;
+    }
+    if (nread == 0) break;
+    nbytes = nread + Nstart;
+    
+    status = TRUE;
+    c0 = buffer; 
+    while (status) {
+      c1 = strchr (c0, '\n');
+      if (c1 == (char *) NULL) {
+	Nstart = strlen (c0);
+	memmove (buffer, c0, Nstart);
+	status = FALSE;
+      } else {
+	*c1 = 0;
+      }      
+      if ((*c0 != '#') && (*c0 != '!')) {
+	for (i = 0; (i < Nvec) && status; i++) {
+	  status = dparse (&value, col[i], c0);
+	  vec[i][0].elements[N] = value;
+	  if (!status) vec[i][0].elements[N] = 0.0/0.0;
+	}
+	if (status) N++;
+      }
+      c0 = c1 + 1;
+      if (N == NELEM) {
+	NELEM += 1000;
+	for (i = 0; i < Nvec; i++) {
+	  REALLOCATE (vec[i][0].elements, float, NELEM);
+	}
+      }
+	
+    }
+  }
+  for (i = 0; i < Nvec; i++) {
+    REALLOCATE (vec[i][0].elements, float, MAX (N,1));
+    vec[i][0].Nelements = N;
+  }
+  
+  free (vec);
+  free (col);
+  free (buffer);
+  return (TRUE);
+
+}
+
+# define ESCAPE(MSG) { \
+  gprint (GP_ERR, "%s\n", MSG); \
+  if (CCDKeyword != NULL) free (CCDKeyword); \
+  gfits_free_table  (&table); \
+  gfits_free_header (&header); \
+  return (FALSE);  }
+
+int read_table_vectors (int argc, char **argv, char *extname) {
+
+  int i, j, k, N, Nbytes, Nextend, Ny, Binary;
+  char type[16], ID[80], *CCDKeyword;
+  FTable table;
+  Header header;
+  Vector **vec;
+
+  table.buffer = NULL;
+  header.buffer = NULL;
+
+  CCDKeyword = NULL;
+  if ((N = get_argument (argc, argv, "-keyword"))) {
+    remove_argument (N, &argc, argv);
+    CCDKeyword = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Nextend = -1;
+  if ((N = get_argument (argc, argv, "-extnum"))) {
+    remove_argument (N, &argc, argv);
+    Nextend = atoi (extname);
+  }
+
+  if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...");
+
+  if (f == NULL) ESCAPE ("file not found");
+  fseek (f, 0, SEEK_SET);
+  table.header = &header;
+
+  /* load appropriate extension (if extname is a number, use count) */
+  if (Nextend > -1) {
+    // first extension is PHU, cannot be a table. 
+    // Nextend counts from 0 for first extension
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading primary header for file");
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    gfits_free_header (&header);
+
+    for (i = 0; i < Nextend; i++) {
+      if (!gfits_load_header (f, &header)) ESCAPE ("extension not found");
+      Nbytes = gfits_data_size (&header);
+      /* skip the prior data buffers */
+      fseek (f, Nbytes, SEEK_CUR);
+      gfits_free_header (&header);
+    }
+    if (!gfits_load_header (f, &header)) ESCAPE ("error reading header for extension");
+    if (!gfits_fread_ftable_data (f, &table)) ESCAPE ("error reading table for extension");
+
+  } else {
+    if (CCDKeyword == NULL) {
+      CCDKeyword = get_variable ("CCDKEYWORD");
+    }
+    if (CCDKeyword == NULL) {
+      CCDKeyword = strcreate ("EXTNAME");
+    }
+
+    while (1) {
+      if (!gfits_load_header (f, &header)) ESCAPE ("extensio not found in file");
+      Nbytes = gfits_data_size (&header);
+
+      if (!gfits_scan (&header, CCDKeyword, "%s", 1, ID)) {
+	fseek (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (strcmp (ID, extname)) {
+	fseek (f, Nbytes, SEEK_CUR);
+	gfits_free_header (&header);
+	continue;
+      }
+      if (!gfits_fread_ftable_data (f, &table)) ESCAPE ("error reading table for extension");
+      break;
+    }
+  }
+
+  /* identify table type (ascii / binary) */
+  Binary = FALSE;
+  gfits_scan (&header, "XTENSION", "%s", 1, type);
+  if (strcmp (type, "BINTABLE") && strcmp (type, "TABLE")) {
+    ESCAPE ("specified extension is not a table\n");
+  }
+  Binary = !strcmp (type, "BINTABLE");
+  Ny = header.Naxis[1];
+
+  /* find columns which match requested vectors */
+  for (i = 1; i < argc; i++) {
+    void   *data;
+    char   *Pc;
+    short  *Ps;
+    int    *Pi;
+    float  *Pf;
+    double *Pd;
+    int Nval;
+    char name[80];
+      
+    Nval = 0;
+    if (Binary) {
+      if (!gfits_get_bintable_column_type (&header, argv[i], type, &Nval)) ESCAPE ("requested field not found");
+      if (!gfits_get_bintable_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    } else {
+      if (!gfits_get_table_column_type (&header, argv[i], type)) ESCAPE ("requested field not found");
+      if (!gfits_get_table_column (&header, &table, argv[i], &data)) ESCAPE ("error reading data from specified field");
+    }
+    if (Nval == 0) ESCAPE ("no data for field in table");
+
+    ALLOCATE (vec, Vector *, Nval);
+    for (j = 0; j < Nval; j++) {
+      if (Nval == 1) 
+	sprintf (name, "%s", argv[i]);
+      else
+	sprintf (name, "%s:%d", argv[i], j);
+      if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+      REALLOCATE (vec[j][0].elements, float, MAX (Ny,1));
+      vec[j][0].Nelements = Ny;
+    }
+
+    if (!strcmp (type, "double")) {
+      Pd = (double *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pd++) {
+	  vec[k][0].elements[j] = *Pd;
+	}
+      }
+    }
+    if (!strcmp (type, "float")) {
+      Pf = (float *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pf++) {
+	  vec[k][0].elements[j] = *Pf;
+	}
+      }
+    }
+    if (!strcmp (type, "int")) {
+      Pi = (int *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pi++) {
+	  vec[k][0].elements[j] = *Pi;
+	}
+      }
+    }
+    if (!strcmp (type, "short")) {
+      Ps = (short *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ps++) {
+	  vec[k][0].elements[j] = *Ps;
+	}
+      }
+    }
+    if (!strcmp (type, "char")) {
+      Pc = (char *) data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Pc++) {
+	  vec[k][0].elements[j] = *Pc;
+	}
+      }
+    }
+    free (data);
+    free (vec);
+  }
+  if (CCDKeyword != NULL) free (CCDKeyword);
+  gfits_free_table (&table);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rebin.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rebin.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rebin.c	(revision 17422)
@@ -0,0 +1,228 @@
+# include "data.h"
+
+int rebin (int argc, char **argv) {
+  
+  int i, j, status, n, nx, ny, Nx, Ny, x, y, N, *Npix, *Vn;
+  int Ignore, IgnoreValue, VERBOSE, Normalize, ExactScale;
+  char temp[1024];
+  float *Vout, *Vin, *Out, *In;
+  double scale, scale2, fx, fy, dX, dY;
+  Buffer *in, *out;
+
+  Vn = Npix = NULL;
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: rebin <from> <to> scale \n");
+    gprint (GP_ERR, "  negative integer scale expands image\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  scale  = atof (argv[3]);
+  if ((scale == (int) scale) || ((1.0/scale) == (int)(1.0/scale))) {
+    ExactScale = TRUE;
+    if (scale > 0) {
+      nx = in[0].header.Naxis[0] / scale;
+      ny = in[0].header.Naxis[1] / scale;
+    } else {
+      nx = in[0].header.Naxis[0] * fabs(scale);
+      ny = in[0].header.Naxis[1] * fabs(scale);
+    }    
+  } else {
+    ExactScale = FALSE;
+    if (scale > 0) {
+      nx = (int) (in[0].header.Naxis[0] / scale) + 1;
+      ny = (int) (in[0].header.Naxis[1] / scale) + 1;
+    } else {
+      nx = (int) (in[0].header.Naxis[0] * fabs(scale)) + 1;
+      ny = (int) (in[0].header.Naxis[1] * fabs(scale)) + 1;
+    }      
+  }
+  if (VERBOSE) gprint (GP_LOG, "rebin %s to %s (%d,%d to %d,%d)\n", argv[1], argv[2], in[0].header.Naxis[0], in[0].header.Naxis[1], nx, ny);
+
+  Nx = in[0].header.Naxis[0];
+  Ny = in[0].header.Naxis[1];
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  status =  gfits_scan (&out[0].header, "CDELT1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CDELT2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX *= scale;
+    dY *= scale;
+  } else {
+    dX /= fabs(scale);
+    dY /= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CDELT1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CDELT2", "%lf", 1, dY);
+  }
+
+  status =  gfits_scan (&out[0].header, "CRPIX1", "%lf", 1, &dX);
+  status &= gfits_scan (&out[0].header, "CRPIX2", "%lf", 1, &dY);
+  if (scale > 0) {
+    dX /= scale;
+    dY /= scale;
+  } else {
+    dX *= fabs(scale);
+    dY *= fabs(scale);
+  }    
+  if (status) {
+    gfits_modify (&out[0].header, "CRPIX1", "%lf", 1, dX);
+    gfits_modify (&out[0].header, "CRPIX2", "%lf", 1, dY);
+  }
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  temp[0] = 0;
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    strcpy (temp, "*");
+  }
+  strcat (temp, in[0].file);
+  strcpy (out[0].file, temp);
+
+  if (Normalize) {
+    ALLOCATE (Npix, int, nx*ny);
+    bzero (Npix, nx*ny*sizeof(int));
+  }
+
+  if (ExactScale) {
+    n = scale;
+    if (n > 0) {
+      for (j = 0; j < ny; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + j*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + (j*n + y)*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < nx; i++, Vout++) {
+	    for (x = 0; x < n; x++, Vin++) {
+	      if (Ignore && (*Vin == IgnoreValue)) continue;
+	      *Vout += *Vin;
+	      if (Normalize) {(*Vn) ++;}
+	    }
+	    if (Normalize) {Vn ++;}
+	  }
+	}
+      }
+    } else {
+      n = fabs (n);
+      for (j = 0; j < in[0].header.Naxis[1]; j++) {
+	for (y = 0; y < n; y++) {
+	  Vout = (float *)(out[0].matrix.buffer) + (j*n + y)*nx;
+	  Vin  = (float *)(in[0].matrix.buffer)  + j*in[0].header.Naxis[0];
+	  if (Normalize) { Vn = Npix + j*nx; }
+	  for (i = 0; i < in[0].header.Naxis[0]; i++, Vin++) {
+	    if (Ignore && (*Vin == IgnoreValue)) { 
+	      Vout += n; 
+	      if (Normalize) Vn += n; 
+	      continue; 
+	    }
+	    for (x = 0; x < n; x++, Vout++) {
+	      *Vout = *Vin;
+	      if (Normalize) {(*Vn) ++; Vn ++;}
+	    }
+	  }
+	}
+      }
+    }
+    if (Normalize) {
+      Vn = Npix;
+      Vout = (float *)out[0].matrix.buffer;
+      for (i = 0; i < nx*ny; i++, Vout++, Vn++) {
+	if (*Vn) { 
+	  *Vout /= *Vn; 
+	} else {
+	  *Vout = 0;
+	}
+      }
+    }
+  } else {
+    
+    /* normalization is broken.  repair please */
+    if (Normalize) { gprint (GP_ERR, "normalize not enabled for fractional scaling\n"); }
+
+    if (scale < 0) scale = 1.0 / fabs(scale);
+    In = (float *)in[0].matrix.buffer;
+    Out = (float *)out[0].matrix.buffer;
+    scale2 = scale*scale;
+    if (scale > 1) {
+      for (i = 0; i < Ny; i++) {
+	y = 0.5 + (i - 0.5) / scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) * scale - i);
+	for (j = 0; j < Nx; j++, In++) {
+	  x = 0.5 + (j - 0.5) / scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) * scale - j);
+	  Vout = Out + y*nx + x;
+	  *Vout += fx*fy*(*In);
+	  if (fx < 1) {
+	    *(Vout+1)    += (1-fx)*fy*(*In);
+	  }
+	  if (fy < 1) {
+	    *(Vout+nx) += fx*(1-fy)*(*In);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *(Vout+1+nx) += (1-fx)*(1-fy)*(*In);
+	  }
+	}
+      }
+    } else {
+      for (i = 0; i < ny; i++) {
+	y = 0.5 + (i - 0.5) * scale;
+	fy = 0.5 + MIN (0.5, (y + 0.5) / scale - i);
+	for (j = 0; j < nx; j++, Out++) {
+	  x = 0.5 + (j - 0.5) * scale;
+	  fx = 0.5 + MIN (0.5, (x + 0.5) / scale - j);
+	  Vin = In + y*Nx + x;
+	  *Out += *Vin*fx*fy;
+	  if (fx < 1) {
+	    *Out += *(Vin+1)*(1-fx)*fy;
+	  }
+	  if (fy < 1) {
+	    *Out += *(Vin+Nx)*fx*(1-fy);
+	  }
+	  if ((fx < 1) && (fy < 1)) {
+	    *Out += *(Vin+1+Nx)*(1-fx)*(1-fy);
+	  }
+	  *Out = *Out * scale2;
+	}
+      }
+    }
+  }
+
+  if (Normalize) free (Npix);
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/resize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/resize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/resize.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int resize (int argc, char **argv) {
+
+  char *end;
+  double NX, NY;
+  int N, kapa;
+  char *name;
+  
+  /* display source */
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: resize NX NY [-n] [-g | -i]\n");
+    return (FALSE);
+  }
+
+  /* NX & NY are pixels for the screen & points for PS 
+     convert units to points (1in = 72pt, 1cm = 28pt) */
+
+  /* have kapa convert physical units to screen units 
+     for now, fixed at 96 pix / in == 38 pix / cm
+  */
+
+  NX = strtod (argv[1], &end);
+  if (!strcmp (end, "in")) { NX *= 96; }
+  if (!strcmp (end, "cm")) { NX *= 38; }
+
+  NY = strtod (argv[2], &end);
+  if (!strcmp (end, "in")) { NY *= 96; }
+  if (!strcmp (end, "cm")) { NY *= 38; }
+
+  KiiResize (kapa, NX, NY);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/roll.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/roll.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/roll.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "data.h"
+
+int roll (int argc, char **argv) {
+  
+  int Nbytes, Nextra;
+  int dX, dx, dy, nx, ny;
+  Buffer *buf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: roll <buffer> dx dy\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  dx = atof (argv[2]);
+  dy = atof (argv[3]);
+  if (dy != 0) {
+    gprint (GP_ERR, "only x rolls implemented for now\n");
+  }
+
+  /* if (dx < 0), we are moving the start position back by dx pixels,
+     if (dx > 0), we are moving the start position forward by dx pixels */
+  
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  dX = abs(dx);
+  Nbytes = nx * ny * sizeof (float);
+  Nextra = (nx * ny + dX) * sizeof (float);
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nextra);
+
+  if (dx < 0) {
+    memmove (buf[0].matrix.buffer, &buf[0].matrix.buffer[dX*sizeof(float)], Nbytes);
+  } else {
+    memmove (&buf[0].matrix.buffer[dX*sizeof(float)], buf[0].matrix.buffer, Nbytes);
+  }
+
+  REALLOCATE (buf[0].matrix.buffer, char, Nbytes);
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rotate.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rotate.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/rotate.c	(revision 17422)
@@ -0,0 +1,298 @@
+# include "data.h"
+
+int rotate (int argc, char **argv) {
+  
+  int i, j, NX, NY, X, Y, Lx, Ly, N, newCenter;
+  float *in_buff, *out_buff, *c;
+  double angle, CosAngle, SinAngle, Xo, Yo, dX, dY, fx, fy, x, y, X1, Y1;
+  double pc11, pc12, pc21, pc22, PC11, PC12, PC21, PC22;
+  Buffer *buf;
+
+  Xo = 0;
+  Yo = 0;
+  if ((N = get_argument (argc, argv, "-center"))) {
+    newCenter = TRUE;
+    remove_argument (N, &argc, argv);
+    Xo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Yo  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  else 
+    newCenter  = FALSE;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: rotate <buffer> <angle>\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if ((atof (argv[1]) < -180) || (atof (argv[1]) > 180)) {
+    gprint (GP_ERR, "valid rotate angle between -180 and +180 degrees\n");
+    return (FALSE);
+  }
+
+  /* save starting values */
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+  in_buff = (float *) buf[0].matrix.buffer;  /* don't lose reference */
+
+  if (!strcasecmp (argv[2], "LEFT") || (atof (argv[2]) == -90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = NX - 1; i > -1; i--) {
+      for (j = 0; j < NY; j++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Yo;
+    Y1 = NX - Xo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc21;
+    PC12 = pc22;
+    PC21 = -pc11;
+    PC22 = -pc12;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "RIGHT") || (atof (argv[2]) == 90)) {
+    buf[0].header.Naxis[0] = NY;
+    buf[0].header.Naxis[1] = NX;
+    gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, NY);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, NX);
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (i = 0; i < NX; i++) {
+      for (j = NY - 1; j > -1; j--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NY - Yo;
+    Y1 = Xo;
+    gprint (GP_ERR, "%f %f -> %f %f\n", Xo, Yo, X1, Y1);
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc21;
+    PC12 = -pc22;
+    PC21 = pc11;
+    PC22 = pc12;
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc11, pc12, PC11, PC12);
+    gprint (GP_ERR, "%f %f  ->  %f %f\n", pc21, pc22, PC21, PC22);
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "UPSIDE") || (atof (argv[2]) == -180) || (atof (argv[2]) == 180)) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = -pc12;
+    PC21 = -pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPY")) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = NY - 1; j > -1; j--) {
+      for (i = 0; i < NX; i++, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = Xo;
+    Y1 = NY - Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = pc11;
+    PC12 = -pc12;
+    PC21 = pc21;
+    PC22 = -pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[2], "FLIPX")) {
+    gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+    gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+    out_buff = (float *)buf[0].matrix.buffer;
+    for (j = 0; j < NY; j++) {
+      for (i = NX - 1; i > -1; i--, out_buff++) {
+	*out_buff = in_buff[i + j*NX];
+      }
+    }
+    /* fix reference pixel */
+    gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+    gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+    X1 = NX - Xo;
+    Y1 = Yo;
+    gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+    gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+    
+    /* fix rotate matrix */
+    gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+    gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+    gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+    gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+    PC11 = -pc11;
+    PC12 = pc12;
+    PC21 = -pc21;
+    PC22 = pc22;
+    gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+    gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+    gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+    gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+    free (in_buff);
+    return (TRUE);
+  }
+
+  angle = atof (argv[2]);
+  CosAngle = cos (angle*RAD_DEG);
+  SinAngle = sin (angle*RAD_DEG);
+  
+  gprint (GP_ERR, "rotating: %f %f %f\n", angle, CosAngle, SinAngle);
+
+  Lx = NX*fabs(CosAngle) + NY*fabs(SinAngle);
+  Ly = NX*fabs(SinAngle) + NY*fabs(CosAngle);
+  dX = MAX(0,NY*SinAngle);
+  dY = MAX(0,-NX*SinAngle);
+  /*
+  gprint (GP_ERR, "%f %f  -->  ", Xo, Yo);
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gprint (GP_ERR, "%f %f\n", X1, Y1);
+  */
+
+  /* fix reference pixel */
+  gfits_scan (&buf[0].header, "CRPIX1", "%lf", 1, &Xo);
+  gfits_scan (&buf[0].header, "CRPIX2", "%lf", 1, &Yo);
+  /*
+  X1 = (Xo - dX)*CosAngle - (Yo - dY)*SinAngle;
+  Y1 = (dX - Xo)*SinAngle + (Yo - dY)*CosAngle;
+  */
+  X1 = Xo*CosAngle - Yo*SinAngle + dX;
+  Y1 =  Xo*SinAngle + Yo*CosAngle + dY;
+  gfits_modify (&buf[0].header, "CRPIX1", "%lf", 1, X1);
+  gfits_modify (&buf[0].header, "CRPIX2", "%lf", 1, Y1);
+
+  /* fix rotate matrix */
+  gfits_scan (&buf[0].header, "PC001001", "%lf", 1, &pc11);
+  gfits_scan (&buf[0].header, "PC001002", "%lf", 1, &pc12);
+  gfits_scan (&buf[0].header, "PC002001", "%lf", 1, &pc21);
+  gfits_scan (&buf[0].header, "PC002002", "%lf", 1, &pc22);
+  PC11 = pc11*CosAngle - pc21*SinAngle;
+  PC12 = pc12*CosAngle - pc22*SinAngle;
+  PC21 = pc21*CosAngle + pc11*SinAngle;
+  PC22 = pc22*CosAngle + pc12*SinAngle;
+  gfits_modify (&buf[0].header, "PC001001", "%le", 1, PC11);
+  gfits_modify (&buf[0].header, "PC001002", "%le", 1, PC12);
+  gfits_modify (&buf[0].header, "PC002001", "%le", 1, PC21);
+  gfits_modify (&buf[0].header, "PC002002", "%le", 1, PC22);
+
+  buf[0].header.Naxis[0] = Lx;
+  buf[0].header.Naxis[1] = Ly;
+  gfits_modify (&buf[0].header, "NAXIS1", "%d", 1, Lx);
+  gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, Ly);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+  gfits_print  (&buf[0].header, "HISTORY", "%S", 1, "WARNING: rotated image!");
+  out_buff = (float *)buf[0].matrix.buffer;
+  for (j = 0; j < Ly; j++) {
+    for (i = 0; i < Lx; i++, out_buff++) {
+
+      x = (i - dX)*CosAngle + (j - dY)*SinAngle;
+      y = (dX - i)*SinAngle + (j - dY)*CosAngle;
+      X = (int) x;
+      Y = (int) y;
+
+      if (X < 0) continue;
+      if (X >= NX - 1) continue;
+      if (Y < 0) continue;
+      if (Y >= NY - 1) continue;
+
+      c = &in_buff[X * NX*Y];
+      fx = x - X;
+      fy = y - Y;
+      *out_buff = (c[0]*(1-fx) + c[1]*fx)*(1-fy) + (c[NX+1]*fx + c[NX]*(1-fx))*fy;
+    }
+  }
+  free (in_buff);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/save.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/save.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/save.c	(revision 17422)
@@ -0,0 +1,32 @@
+# include "data.h"
+
+int save (int argc, char **argv) {
+  
+  int N, celestial;
+  int kapa;
+  char *name;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  celestial = FALSE;
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    celestial = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: save (overlay) <filename> [-c]\n");
+    gprint (GP_ERR, "  -c: write contour in celestial coords\n");
+    return (FALSE);
+  }
+
+  KiiSaveOverlay (kapa, celestial, argv[1], argv[2]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/section.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/section.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/section.c	(revision 17422)
@@ -0,0 +1,133 @@
+# include "data.h"
+
+enum {NONE, LIST, UP, DOWN, TOP, BOTTOM, TOOL};
+
+int section (int argc, char **argv) {
+  
+  int N, action, kapa;
+  char *name, *location;
+  Graphdata graphmode;
+  KapaSection section;
+
+  action = NONE;
+  if ((N = get_argument (argc, argv, "-list"))) {
+    action = LIST;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-up"))) {
+    action = UP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-down"))) {
+    action = DOWN;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-top"))) {
+    action = TOP;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-bottom"))) {
+    action = BOTTOM;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-imtool"))) {
+    action = TOOL;
+    remove_argument (N, &argc, argv);
+    location = argv[N];
+    remove_argument (N, &argc, argv);
+  }
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetGraph (&graphmode, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* list sections */
+  if ((argc == 1) && (action == NONE)) {
+    KapaGetSection (kapa, "*");
+    gprint (GP_ERR, "USAGE: section name [x y dx dy] [options]\n");
+    gprint (GP_ERR, "OPTIONS: -list   : show properties of all sections\n");
+    gprint (GP_ERR, "         -up     : move section up in display stack\n");
+    gprint (GP_ERR, "         -down   : move section down in display stack\n");
+    gprint (GP_ERR, "         -top    : move section to top of display stack\n");
+    gprint (GP_ERR, "         -bottom : move section to bottom of display stack\n");
+    gprint (GP_ERR, "         -imtool (position) : set location of image zoom / status box\n");
+    gprint (GP_ERR, "                 (position may be: -x, +x, -y, +y, none)\n");
+    return (TRUE);
+  } 
+  
+  if (argc == 2) {
+    /* select / show section */
+    switch (action) {
+      case NONE:
+	KapaSelectSection (kapa, argv[1]);
+	break;
+
+      case UP:
+	KapaMoveSection (kapa, argv[1], "up");
+	break;
+      case DOWN:
+	KapaMoveSection (kapa, argv[1], "down");
+	break;
+      case TOP:
+	KapaMoveSection (kapa, argv[1], "top");
+	break;
+      case BOTTOM:
+	KapaMoveSection (kapa, argv[1], "bottom");
+	break;
+
+      case TOOL:
+	if (!strcmp(location, "-x")) {
+	  KapaSetToolbox (kapa, 1);
+	  break;
+	}
+	if (!strcmp(location, "+x")) {
+	  KapaSetToolbox (kapa, 3);
+	  break;
+	}
+	if (!strcmp(location, "-y")) {
+	  KapaSetToolbox (kapa, 2);
+	  break;
+	}
+	if (!strcmp(location, "+y")) {
+	  KapaSetToolbox (kapa, 4);
+	  break;
+	}
+	if (!strcmp(location, "none")) {
+	  KapaSetToolbox (kapa, 0);
+	  break;
+	}
+	gprint (GP_ERR, "unknown toolbox location %s\n", location);
+	gprint (GP_ERR, "valid values: -x, +x, -y, +y, none\n");
+	return (FALSE);
+
+      case LIST:
+	KapaGetSection (kapa, argv[1]);
+	break;
+    }
+    return (TRUE);
+  } 
+  
+  if (argc == 6) {
+    /* set section */
+    section.name = argv[1];
+    section.x = atof (argv[2]);
+    section.y = atof (argv[3]);
+    section.dx = atof (argv[4]);
+    section.dy = atof (argv[5]);
+    KapaSetSection (kapa, &section);
+    return (TRUE);
+  }
+  gprint (GP_ERR, "USAGE: section name [x y dx dy]\n");
+  gprint (GP_ERR, "USAGE: section name [-list] [-up] [-down] [-top] [-bottom]\n");
+  return (FALSE);
+}
+
+/* should do some range checking on x y dx dy
+   should be between 0.0 and 1.0, precision of 0.001
+   is sufficient
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/select.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/select.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/select.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "data.h"
+
+int vect_select (int argc, char **argv) {
+  
+  int  i, size;
+  char *out;
+  Vector *in1, *in2, *tvec, *ovec;
+
+  out = NULL;
+  in1 = in2 = ovec = tvec = NULL;
+
+  /** check basic syntax **/
+  if ((argc != 8) || strcmp(argv[2], "=") || strcmp (argv[4], "if") || strcmp (argv[6], "else")) {
+    gprint (GP_ERR, "SYNTAX: select vec = vec if (logic expression) else vec\n");
+    return (FALSE);
+  }
+  if ((in1  = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((in2  = SelectVector (argv[7], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of in1, in2, tvec: must match */
+
+  REALLOCATE (ovec[0].elements, float, MAX (tvec[0].Nelements, 1));
+  for (i = 0; i < tvec[0].Nelements; i++) {
+    ovec[0].elements[i] = tvec[0].elements[i] ? in1[0].elements[i] : in2[0].elements[i];
+  }
+  ovec[0].Nelements = tvec[0].Nelements;
+  REALLOCATE (ovec[0].elements, float, MAX (ovec[0].Nelements, 1));
+  
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+ error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/set.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/set.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/set.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "data.h"
+
+int set (int argc, char **argv) {
+  
+  int size;
+  char *out;
+
+  /** check basic form for line **/
+  if ((argc < 3) || strcmp(argv[2], "=")) {
+    gprint (GP_ERR, "%s = (matrix expression)\n", argv[0]);
+    return (FALSE);
+  }
+
+  out = dvomath (argc - 3, &argv[3], &size, -1);
+  if (out == NULL) {
+    print_error ();
+    return (FALSE);
+  }
+  
+  switch (size) {
+    case 0:
+      set_str_variable (argv[1], out);
+      free (out);
+      break;
+
+    case 1:
+      if (!MoveNamedVector (argv[1], out)) {
+	DeleteNamedVector (out);
+	free (out);
+	gprint (GP_ERR, "invalid output vector name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  
+    case 2:
+      if (!MoveNamedBuffer (argv[1], out)) {
+	DeleteNamedBuffer (out);
+	free (out);
+	gprint (GP_ERR, "invalid output matrix name\n");
+	return (FALSE);
+      }
+      free (out);
+      break;
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/shift.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/shift.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/shift.c	(revision 17422)
@@ -0,0 +1,110 @@
+# include "data.h"
+
+int shift (int argc, char **argv) {
+ 
+  int i, j, N, ROLL;
+  int nx, ny, dx, dy, DXin, DXot, DYin, DYot;
+  float *Vin, *Vot;
+  double Dx, Dy, fdx, fdy;
+  Buffer *in, *out;
+
+  ROLL = FALSE;
+  if ((N = get_argument (argc, argv, "-roll"))) {
+    remove_argument (N, &argc, argv);
+    ROLL = TRUE;
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: shift (input) (output) dx dy\n");
+    return (FALSE);
+  }
+
+  // define the input buffer and examine the shift
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Dx = atof (argv[3]);
+  Dy = atof (argv[4]);
+
+  dx = Dx;
+  dy = Dy;
+  fdx = Dx - dx;
+  fdy = Dy - dy;
+  if (fdx < -0.000001) {dx -= 1; fdx += 1;}
+  if (fdy < -0.000001) {dy -= 1; fdy += 1;}
+  // we always specify a positive fractional shift
+  // the above choice defines a minimum fractional shift of 1e-5
+
+  nx = in[0].matrix.Naxis[0];
+  ny = in[0].matrix.Naxis[1];
+
+  if ((dx > nx) || (dy > ny)) {
+    gprint (GP_ERR, "shifting data out of image\n");
+    return (FALSE);
+  }
+  
+  // define the output buffer
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+  CreateBuffer (out, nx, ny, -32, 0.0, 1.0);
+
+  DXin = (dx < 0) ? -dx : 0;
+  DXot = (dx < 0) ?   0 : dx;
+  DYin = (dy < 0) ? -dy : 0;
+  DYot = (dy < 0) ?   0 : dy;
+  
+  for (j = 0; j < ny - abs(dy); j++) {
+    Vin = (float *)(in[0].matrix.buffer)  + (j + DYin)*nx + DXin;  
+    Vot = (float *)(out[0].matrix.buffer) + (j + DYot)*nx + DXot; 
+    for (i = 0; i < nx - abs(dx); i++, Vin++, Vot++) {
+      *Vot = *Vin;
+    }
+    // fill in the exposed x-border with 0.0
+    Vot = (dx > 0) ? 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx : 
+      (float *)(out[0].matrix.buffer) + (j + DYot)*nx + nx - abs(dx);
+    for (i = 0; i < abs(dx); i++, Vot++) {
+      *Vot = 0.0;
+    }	
+  }
+
+  // fill in the exposed y-border with 0.0
+  Vot = (dy > 0) ? 
+    (float *)(out[0].matrix.buffer) :
+    (float *)(out[0].matrix.buffer) + (ny - abs(dy))*nx;
+
+  for (j = 0; j < nx * abs(dy); j++, Vot++) {
+    *Vot = 0.0;
+  }   
+
+  // apply the fractional shift 
+  gprint (GP_ERR, "%f %f\n", fdx, fdy);
+  if ((fdx > 0) || (fdy > 0)) {
+    double f00, f01, f10, f11;
+    float value;
+
+    f00 = (1-fdx)*(1-fdy);
+    f01 =    fdx *(1-fdy);
+    f10 = (1-fdx)*   fdy;
+    f11 =    fdx *   fdy;
+
+    Vin = (float *)out[0].matrix.buffer;
+    Vot = (float *)out[0].matrix.buffer;
+    for (j = 0; j < ny - 1; j++) {
+      for (i = 0; i < nx - 1; i++, Vin++, Vot++) {
+	value  = Vin[   0] * f00;
+	value += Vin[   1] * f01;
+	value += Vin[nx  ] * f10;
+	value += Vin[nx+1] * f11;
+	*Vot = value;
+      }
+      Vin ++;
+      Vot ++;
+    }
+  }   
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/sort.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/sort.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/sort.c	(revision 17422)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+int sort_vectors (int argc, char **argv) {
+  
+  int i, j, Nvec, Nval;
+  float *temp, *index, *T, *V, *I;
+  Vector **vec;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: sort (vector) [vectors ...] \n");
+    gprint (GP_ERR, "  first vector is sort key for others\n");
+    return (FALSE);
+  }
+
+  Nvec = (argc - 1);
+  ALLOCATE (vec, Vector *, Nvec);
+
+  Nval = 0;
+  /* find vectors and check sizes */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 1], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "USAGE: sort vector vector ...\n");
+      free (vec);
+      return (FALSE);    
+    }
+    if (i == 0) {
+      Nval = vec[i][0].Nelements;
+    } else {
+      if (Nval != vec[i][0].Nelements) {
+	free (vec);
+	gprint (GP_ERR, "vectors must all be same length\n");
+	return (FALSE);
+      }
+    }
+  }
+  
+  /* create index (use float to use sortpair) */
+  ALLOCATE (index, float, Nval);
+  for (i = 0; i < Nval; i++) index[i] = i;
+
+  /* sort key & index */
+  fsortpair (vec[0][0].elements, index, Nval);
+
+  ALLOCATE (temp, float, Nval);
+  for (i = 1; i < Nvec; i++) {
+    T = temp;
+    V = vec[i][0].elements;
+    I = index;
+    for (j = 0; j < Nval; j++, T++, I++) {
+      *T = V[(int)(*I)];
+    }
+    /* swap .elements (== V) and temp */ 
+    vec[i][0].elements = temp;
+    temp = V;
+  }
+  free (temp);
+  free (vec);
+  free (index);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_apply.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_apply.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_apply.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "data.h"
+
+// need to rename this as an image function
+int spline_apply_cmd (int argc, char **argv) {
+  
+  int i, j, I, J;
+  int nx, ny, Nx, Ny, xdir;
+  float rx, ry, x, y;
+  float *Tx1, *Tx2, *Txc, *Ty1, *Ty2, *Tyc, *V, *V1, *V2;
+  Buffer *out, *y1, *y2;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: spline_apply <Y> <Y2> <out> (x/y) Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((y1  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((y2  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  xdir = FALSE;
+  if (!strcmp (argv[4], "x")) xdir = TRUE; 
+
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  Nx = y1[0].matrix.Naxis[0];
+  Ny = y1[0].matrix.Naxis[1];
+
+  rx = Nx / (float) nx;
+  ry = Ny / (float) ny;
+
+  /* create an output matrix buffer with desired nx, ny */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  out[0].bitpix = y1[0].bitpix;
+  out[0].unsign = y1[0].unsign;
+  out[0].bscale = y1[0].bscale;
+  out[0].bzero  = y1[0].bzero;
+  gfits_copy_header (&y1[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  if ((y1[0].file[0] != '*') && (y1[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", y1[0].file);
+  } else {
+    sprintf (out[0].file, "%s", y1[0].file);
+  }
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty1, float, Ny);
+  ALLOCATE (Tyc, float, Ny);
+  for (i = 0; i < Ny; i++) { Tyc[i] = i; }
+
+  ALLOCATE (Tx1, float, Nx);
+  ALLOCATE (Tx2, float, Nx);
+  ALLOCATE (Txc, float, Nx);
+  for (i = 0; i < Nx; i++) { Txc[i] = i; }
+
+  V = (float *)(out[0].matrix.buffer);
+
+  for (J = 0; J < ny; J++) {
+    y = J * ry;
+
+    /* construct spline for each element in this row */
+    for (i = 0; i < Nx; i++) {
+      V1 = (float *)(y1[0].matrix.buffer) + i;
+      V2 = (float *)(y2[0].matrix.buffer) + i;
+      for (j = 0; j < Ny; j++, V1+=Nx, V2+=Nx) {
+	Ty1[j] = *V1;
+	Ty2[j] = *V2;
+      }
+      Tx1[i] = spline_apply (Tyc, Ty1, Ty2, Ny, y);
+    }
+    spline_construct (Txc, Tx1, Nx, Tx2);
+
+    /* apply x-dir spline to new image */
+    for (I = 0; I < nx; I++, V++) {
+      x = I * rx;
+      *V = spline_apply (Txc, Tx1, Tx2, Nx, x);
+    }
+  }
+
+  free (Ty1);
+  free (Ty2);
+  free (Tyc);
+  
+  free (Tx1);
+  free (Tx2);
+  free (Txc);
+  
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_construct.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_construct.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/spline_construct.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+// need to rename this as an image function
+int spline_construct_cmd (int argc, char **argv) {
+  
+  int i, j, Nx, Ny, xdir;
+  float *Tx, *Ty, *Ty2, *V;
+  Buffer *in, *out;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline_construct <in> <out> (x/y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  free (out[0].matrix.buffer);
+  free (out[0].header.buffer);
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", in[0].file);
+  } else {
+    sprintf (out[0].file, "%s", in[0].file);
+  }
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_matrix_info (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  xdir = FALSE;
+  if (!strcmp (argv[3], "x")) xdir = TRUE; 
+  /* ideally, the resulting image should carry this info (in header?) */
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty, float, Ny);
+  ALLOCATE (Tx, float, Ny);
+
+  /** for now only perform the operation for the ydir splines */
+
+  /* construct coordinate vector */
+  for (j = 0; j < Ny; j++) { Tx[j] = j; }
+  
+  for (i = 0; i < Nx; i++) {
+    
+    /* construct temp vector with values to spline */
+    V = (float *)(in[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      Ty[j] = *V;
+    }
+  
+    spline_construct (Tx, Ty, Ny, Ty2);
+  
+    /* copy derivatives to output buffer */
+    V = (float *)(out[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      *V = Ty2[j];
+    }
+  }
+
+  free (Tx);
+  free (Ty);
+  free (Ty2);
+  
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats-new.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats-new.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats-new.c	(revision 17422)
@@ -0,0 +1,259 @@
+# include "data.h"
+
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value);
+double sampleMedian (Matrix *matrix, int Nsubset);
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma);
+
+typedef struct {
+  int sx;
+  int sy;
+  int nx;
+  int ny;
+} Region;
+
+typedef struct {
+  float median;
+  float mean;
+  float mode;
+  float sigma;
+} Stats;
+
+int stats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+  sigma = sqrt (N2/Npix - N1*N1);
+
+  if (ROBUST) {
+    stats = robustMedian (matrix, min, max, sigma);
+    if (stats->sigma 
+  } 
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
+double sampleMedian (Matrix *matrix, int Nsubset) {
+
+  int i, Nsample, Npix;
+  long A, B;
+  float *values, *buffer, median;
+
+  /* Generate a vector of Nsample elements */
+  Npix = matrix[0].Naxis[0]*matrix[0].Naxis[1]
+    Nsample = Nsubset;
+  if (Nsubset == 0) {
+    Nsample = Npix;
+  }
+
+  ALLOCATE (values, float, Nsample);
+    
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  *buffer = (float *) matrix[0].buffer;
+
+  for (i = 0; i < Nsample; i++) {
+    if (Nsubset) {
+      j = MIN(Npix - 1, MAX(0, Npix*drand48()));
+    } else {
+      j = i;
+    }
+    values[i] = buffer[j];
+  }
+    
+  fsort (values, Nsample);
+
+  if (Nsample == 0) return 0.0;
+
+  // these are all covered by the logic below
+  // if (Nsample == 1) return values[0];
+  // if (Nsample == 2) return (0.5*(values[0] + values[1]));
+  // if (Nsample == 3) return (values[1]);
+
+  if (Nsample % 2) {
+    median = 0.5*(values[(int)(0.5*Nsample)] + values[(int)(0.5*Nsample) - 1]);
+  } else {
+    median = values[(int)(0.5*Nsample)];
+  }
+  return (median);
+}
+
+Stats *robustMedian (Matrix *matrix, Region *region, float minValue, float maxValue, float sigma) {
+
+  Stats *stats;
+
+  ALLOCATE (stats, Stats, 1);
+
+  // no data range:
+  if (maxValue - minValue < FLT_EPSILON) {
+    stats->median = minValue;
+    stats->mean   = minValue;
+    stats->mode   = minValue;
+    stats->sigma  = 0.0;
+    return stats;
+  }
+
+  // generate a histogram ranging from min to max with step size of sigma / 4
+  // no more than 0x1000 bins, no less than 0x10
+
+  Nbins = 4.0 * (maxValue - minValue) / sigma;
+  Nbins = MIN (0x1000, MAX (0x10, Nbins));
+
+  ALLOCATE (hist, int, Nbins);
+  memset (hist, 0, Nbins*sizeof(int));
+  
+  delta = Nbins / (max - min);
+  binsize = 1.0 / delta;
+
+  Ntotal = 0;
+  for (j = region->sy; j < region->sy + region->ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + region->sx; 
+    for (i = 0; i < region->nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      bin = MIN (MAX (0, (*V - min) * delta), Nbins - 1);
+      hist[bin] ++;
+      Ntotal ++;
+    }
+  }
+
+  // generate the cumulative histogram & find mode
+  ALLOCATE (cumu, int, Nbins);
+  memset (cumu, 0, Nbins*sizeof(int));
+
+  Nhist = 0;
+  Imode = -1;
+  Vmode = 0;
+  for (i = 0; i < Nbins; i++) {
+    Nhist += hist[i];
+    cumu[i] = Nhist;
+    if (hist[i] > Vmode) {
+      Imode = i;
+      Vmode = hist[i];
+    }
+  }
+
+  Ioo = ibracket (cumu, Nbins, 0.500000*Nhist, 1);
+  Im1 = ibracket (cumu, Nbins, 0.308538*Nhist, 1);
+  Ip1 = ibracket (cumu, Nbins, 0.691462*Nhist, 1);
+  Im2 = ibracket (cumu, Nbins, 0.022481*Nhist, 1);
+  Ip2 = ibracket (cumu, Nbins, 0.977519*Nhist, 1);
+
+  Voo = interpolateValue (cumu, Nbins, min, delta, Ioo, 0.500000*Nhist);
+  Vm1 = interpolateValue (cumu, Nbins, min, delta, Im1, 0.308538*Nhist);
+  Vp1 = interpolateValue (cumu, Nbins, min, delta, Ip1, 0.691462*Nhist);
+  Vm2 = interpolateValue (cumu, Nbins, min, delta, Im2, 0.022481*Nhist);
+  Vp2 = interpolateValue (cumu, Nbins, min, delta, Ip2, 0.977519*Nhist);
+
+  sigma1 = (Vp1 - Vm1);
+  sigma2 = (Vp2 - Vm2) / 4.0;
+
+  Vs = Vn = 0;
+  for (i = Im1; i < Ip1 + 1; i++) {
+    Vn += hist[i];
+    Vs += hist[i] * (i * binsize + min);
+  }
+
+  stats->median = Voo;
+  stats->mean = Vs / Vn;
+  stats->mode = Imode * delta + min;
+  stats->sigma = MIN (sigma1, sigma2);
+
+  free (hist);
+  free (cumu);
+
+  return (stats);
+}
+
+// what is the fractional bin position for the given value
+double interpolateValue (int *hist, int Nbins, float min, float binsize, int bin, float value) {
+
+  V0 = bin * binsize + min;
+  V1 = (bin + 1) * binsize + min;
+
+  // fbin = (value - V0) / (V1 - V0) + bin;
+  fbin = (value - V0) / binsize + bin;
+
+  return fbin;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/stats.c	(revision 17422)
@@ -0,0 +1,120 @@
+# include "data.h"
+
+int stats (int argc, char **argv) {
+  
+  int i, j, Nmode, Imode;
+  double Npix, N1, N2, max, min, range, median, mode, IgnoreValue;
+  float *V;
+  int sx, sy, nx, ny, *hist, Nhist, bin;
+  int Ignore, Quiet, N;
+  Buffer *buf;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((argc != 2) && (argc != 6)) {
+    gprint (GP_ERR, "USAGE: stats <buffer> sx sy nx ny\n");
+    gprint (GP_ERR, "OR:    stats <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    sx = strcmp (argv[2], "-") ? atof (argv[2]) : 0;
+    sy = strcmp (argv[3], "-") ? atof (argv[3]) : 0;
+    nx = strcmp (argv[4], "-") ? atof (argv[4]) : buf[0].matrix.Naxis[0];
+    ny = strcmp (argv[5], "-") ? atof (argv[5]) : buf[0].matrix.Naxis[1];
+  } else {
+    sx = 0;
+    sy = 0;
+    nx = buf[0].matrix.Naxis[0];
+    ny = buf[0].matrix.Naxis[1];
+  }
+
+  Npix = N1 = N2 = 0;
+  if ((sx < 0) || (sy < 0) || 
+      (sx+nx > buf[0].matrix.Naxis[0]) || 
+      (sy+ny > buf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region out of range\n");
+    return (FALSE);
+  }
+
+  max = min = *((float *)(buf[0].matrix.buffer) + sy*buf[0].matrix.Naxis[0] + sx);
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) {
+      if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+      N1 += *V;
+      N2 += (*V)*(*V);
+      Npix += 1.0;
+      max = MAX (max, *V);
+      min = MIN (min, *V);
+    }
+  }
+  N1 = N1 / Npix;
+
+/* calculate mode, median */
+  median = mode = 0.5*(max + min);
+  if ((max - min) != 0) {
+    range = 0xffff / (max - min);
+    ALLOCATE (hist, int, 0x10000);
+    bzero (hist, 0x10000*sizeof(int));
+    for (j = sy; j < sy + ny; j++) {
+      V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+      for (i = 0; i < nx; i++, V++) {
+	if (Ignore && (fabs (*V - IgnoreValue) < 1e-8)) continue;
+	bin = MIN (MAX (0, (*V - min) * range), 0xffff);
+	hist[bin] ++;
+      }
+    }
+    Nhist = 0;
+    for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+      Nhist += hist[i];
+    median = i / range + min;
+    Nmode = hist[0];
+    Imode = 0;
+    for (i = 1; i < 0x10000; i++) {
+      if (hist[i] > Nmode) {
+	Nmode = hist[i];
+	Imode = i;
+      }
+    }
+    mode = Imode / range + min;
+    free (hist);
+  }  
+  
+  if (!Quiet) {
+    gprint (GP_LOG, "  mean    stdev    min     max   median   Npix   Total\n");
+    gprint (GP_LOG, "%7.4g %7.4g %7.4g %7.4g %7.4g %7.0f %7.4g\n", N1, sqrt (N2/Npix - N1*N1), 
+	    min, max, median, Npix, Npix*N1);
+  }
+
+  set_variable ("MIN",    min);
+  set_variable ("MAX",    max);
+  set_variable ("MEDIAN", median);
+  set_variable ("MEAN",   N1);
+  set_variable ("MODE",   mode);
+  set_variable ("TOTAL",  N1*Npix);
+  set_variable ("NPIX",   Npix);
+  set_variable ("SIGMA",  sqrt (N2/Npix - N1*N1));
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/style.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/style.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/style.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "data.h"
+
+int style (int argc, char **argv) {
+  
+  int kapa;
+  Graphdata data;
+
+  if (!style_args (&data, &argc, argv, &kapa)) return FALSE;
+
+  if (argc > 1) {
+    gprint (GP_ERR, "USAGE: style [-n Ngraph] [-x plot style] [-c color] [-pt point type] [-lt line type] [-lw line width] [-sz size]\n");
+    return (FALSE);
+  }
+  KapaSetGraphData (kapa, &data);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subraster.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subraster.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subraster.c	(revision 17422)
@@ -0,0 +1,86 @@
+# include "data.h"
+
+int subraster (int argc, char **argv) {
+  
+  int i, j;
+  float *Vin, *Vout;
+  int sx, sy, nx, ny;
+  int Sx, Sy, Nx, Ny;
+  int NX, NY;
+  Buffer *ibuf, *obuf;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: extract <from> <to> sx sy nx ny Sx Sy Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((ibuf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  NX = ibuf[0].matrix.Naxis[0];
+  NY = ibuf[0].matrix.Naxis[1];
+
+  sx = atof (argv[3]);
+  sy = atof (argv[4]);
+  nx = atof (argv[5]);
+  ny = atof (argv[6]);
+
+  Sx = atof (argv[7]);
+  Sy = atof (argv[8]);
+  Nx = atof (argv[9]);
+  Ny = atof (argv[10]);
+
+  if ((Sy + ny > Ny) || (Sx + nx > Nx)) {
+    gprint (GP_ERR, "mismatch between source and dest regions\n");
+    gprint (GP_ERR, "%d + %d > %d or %d + %d > %d\n", Sy, ny, Ny, Sx, nx, Nx);
+    return (FALSE);
+  }
+
+  /* region is not on first image */
+  if ((sx + nx < 0) || (sy + ny < 0) || 
+      (sx > ibuf[0].matrix.Naxis[0]) || 
+      (sy > ibuf[0].matrix.Naxis[1])) {
+    gprint (GP_ERR, "region outside of source image\n");
+    return (FALSE);
+  }
+
+  if ((Sx + nx > Nx) || (Sy + ny > Ny)) {
+    gprint (GP_ERR, "source region larger than dest region\n");
+    return (FALSE);
+  }
+  if ((Sx < 0) || (Sy < 0)) {
+    gprint (GP_ERR, "dest region out of range\n");
+    return (FALSE);
+  }
+
+  if ((obuf = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  gfits_free_matrix (&obuf[0].matrix);
+  gfits_free_header (&obuf[0].header);
+
+  obuf[0].bitpix = ibuf[0].bitpix;
+  obuf[0].unsign = ibuf[0].unsign;
+  obuf[0].bscale = ibuf[0].bscale;
+  obuf[0].bzero  = ibuf[0].bzero;
+  /* strcpy (obuf[0].name, ibuf[0].name); */
+  strcpy (obuf[0].file, ibuf[0].file);
+  gfits_copy_header (&ibuf[0].header, &obuf[0].header);
+  gfits_modify (&obuf[0].header, "NAXIS1", "%d", 1, Nx);
+  gfits_modify (&obuf[0].header, "NAXIS2", "%d", 1, Ny);
+  obuf[0].header.Naxis[0] = Nx;
+  obuf[0].header.Naxis[1] = Ny;
+  gfits_create_matrix (&obuf[0].header, &obuf[0].matrix);
+
+  for (j = 0; j < ny; j++) {
+    if (j + sy < 0) continue;
+    if (j + sy >= NY) continue;
+    Vin = (float *)(ibuf[0].matrix.buffer) + (j + sy)*ibuf[0].matrix.Naxis[0] + sx;  
+    Vout = (float *)(obuf[0].matrix.buffer) + (j + Sy)*obuf[0].matrix.Naxis[0] + Sx;   
+    for (i = 0; i < nx; i++, Vin++, Vout++) {
+      if (i + sx < 0) continue;
+      if (i + sx >= NX) continue;
+      *Vout = *Vin;
+    }
+  }
+
+  return (TRUE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subset.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subset.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/subset.c	(revision 17422)
@@ -0,0 +1,51 @@
+# include "data.h"
+
+/* need to check dimensions of vectors */
+
+int subset (int argc, char **argv) {
+  
+  char *out;
+  int  i, j, size;
+  Vector *ivec, *ovec, *tvec;
+
+  out = NULL;
+  ivec = ovec = tvec = NULL;
+
+  if ((argc < 6) || strcmp(argv[2], "=") || strcmp (argv[4], "if")) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec if (logic expression)\n");
+    return (FALSE);
+  }
+
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto error;
+  if ((ivec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto error;
+
+  out = dvomath (argc - 5, &argv[5], &size, 1);
+  if (out == NULL) {
+    print_error ();
+    goto error;
+  }
+  if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) goto error;
+  /* check size of ivec, tvec: must match */
+
+  REALLOCATE (ovec[0].elements, float, MAX (tvec[0].Nelements, 1));
+  for (j = i = 0; i < tvec[0].Nelements; i++) {
+    if (tvec[0].elements[i]) {
+      ovec[0].elements[j] = ivec[0].elements[i];
+      j++;
+    }
+  }
+  ovec[0].Nelements = j;
+  REALLOCATE (ovec[0].elements, float, MAX (ovec[0].Nelements, 1));
+
+  DeleteVector (tvec);
+  free (out);
+  return (TRUE);
+
+ error:
+  DeleteVector (tvec);
+  DeleteVector (ovec);
+  DeleteNamedVector (out);
+  free (out);
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/svd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/svd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/svd.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int svd (int argc, char **argv) {
+  
+  int i, Nx, Ny, status;
+  float *in, *out, *A, *U, *W, *V;
+  Vector *Vw;
+  Buffer *Ma, *Mu, *Mv;
+
+  if (argc != 6) goto usage;
+  if (strcmp (argv[2], "=")) goto usage;
+
+  if ((Ma = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Mu = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((Vw = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((Mv = SelectBuffer (argv[5], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  Nx = Ma[0].header.Naxis[0];
+  Ny = Ma[0].header.Naxis[1];
+
+  /* U is Nx, Ny */
+  gfits_free_matrix (&Mu[0].matrix);
+  gfits_free_header (&Mu[0].header);
+  Mu[0].bitpix = Ma[0].bitpix;
+  Mu[0].unsign = Ma[0].unsign;
+  Mu[0].bscale = Ma[0].bscale;
+  Mu[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mu[0].header);
+  gfits_create_matrix (&Mu[0].header, &Mu[0].matrix);
+  
+  /* V is Nx, Nx */
+  gfits_free_matrix (&Mv[0].matrix);
+  gfits_free_header (&Mv[0].header);
+  Mv[0].bitpix = Ma[0].bitpix;
+  Mv[0].unsign = Ma[0].unsign;
+  Mv[0].bscale = Ma[0].bscale;
+  Mv[0].bzero  = Ma[0].bzero;
+  gfits_copy_header (&Ma[0].header, &Mv[0].header);
+  gfits_modify (&Mv[0].header, "NAXIS2", "%d", 1, Nx);
+  Mv[0].header.Naxis[1] = Nx;
+  gfits_create_matrix (&Mv[0].header, &Mv[0].matrix);
+
+  /* w is Nx */
+  Vw[0].Nelements = Nx;
+  REALLOCATE (Vw[0].elements, float, Nx);
+
+  /* pointers to the various arrays */
+  A = (float *) Ma[0].matrix.buffer;
+  U = (float *) Mu[0].matrix.buffer;
+  W = (float *) Vw[0].elements;
+  V = (float *) Mv[0].matrix.buffer;
+
+  /* copy A to U (svdcmp replaces A with U) */
+  in  = A;
+  out = U;
+  for (i = 0; i < Nx*Ny; i++, in++, out++) *out = *in;
+  /* use a bcopy instead? */
+
+  status = svdcmp (U, W, V, Nx, Ny);
+  if (!status) {
+    gprint (GP_ERR, "error running svdcmp\n");
+    return (FALSE);
+  }
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "USAGE: svd A = U w Vt\n");
+  return (FALSE);
+  
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/swapbytes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/swapbytes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/swapbytes.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "data.h"
+
+int swapbytes (int argc, char **argv) {
+  
+  int i, nx, ny;
+  char *V, tmp;
+  Buffer *buf;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: swapbytes <buffer>\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  nx = buf[0].matrix.Naxis[0];
+  ny = buf[0].matrix.Naxis[1];
+
+  gprint (GP_ERR, "npix: %d\n", nx*ny);
+
+  V = buf[0].matrix.buffer;
+  for (i = 0; i < nx*ny; i++, V+=4) {
+    tmp = V[0]; V[0] = V[3]; V[3] = tmp;
+    tmp = V[1]; V[1] = V[2]; V[2] = tmp;
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit.sh	(revision 17422)
@@ -0,0 +1,51 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit works
+macro test1
+
+ $PASS = 1
+
+ $Cn = 2
+ $C0 = 4
+ $C1 = -2
+ $C2 = 1
+
+ create x 0 10
+
+ applyfit x y
+
+ if (y[5] != 19)
+  $PASS = 0
+  echo "Value mismatch: y[5]"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit x y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit2d.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit2d.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/applyfit2d.sh	(revision 17422)
@@ -0,0 +1,55 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if applyfit2d works
+macro test1
+
+ $PASS = 1
+
+ $Cnn = 2
+ $CX0Y0 = 1
+ $CX1Y0 = -4
+ $CX2Y0 = 2
+ $CX1Y1 = -3
+ $CX0Y1 = 1.5
+ $CX0Y2 = -2.5
+
+ create x 0 5 0.01
+ set y = 3*cos(2*3.14159*x/2.25)
+
+ applyfit2d x y z
+
+ if (abs(z[300]-12.625) > 0.001)
+  $PASS = 0
+  echo "Value mismatch: z[300]"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  applyfit2d x y z
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/book.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/book.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/book.sh	(revision 17422)
@@ -0,0 +1,109 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test book commands
+macro test1
+
+ $PASS = 1
+
+ local bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+ book create testb01
+ book create testb02
+ book getbook 0 -var bname01
+ book getbook 1 -var bname02
+
+ if (("$bname01" != "testb01") || ("$bname02" != "testb02"))
+  $PASS = 0
+  echo "Books not created/listed correctly!"
+ end
+
+ book newpage testb01 tpage01
+ book newpage testb01 tpage02
+ book npages testb01 -var pcheck
+ 
+ if ($pcheck != 2)
+  $PASS = 0
+  echo "Book pages not added/recorded correctly!"
+ end
+
+ book getpage testb01 0 -var pname01
+ book getpage testb01 1 -var pname02
+
+ if (("$pname01" != "tpage01") || ("$pname02" != "tpage02"))
+  $PASS = 0
+  echo "Pages not created/listed correctly!"
+ end
+
+ book setword testb01 tpage01 w01 tword01
+ book setword testb01 tpage01 w02 tword02
+ book getword testb01 tpage01 w01 -var wcheck
+
+ if ("$wcheck" != "tword01")
+  $PASS = 0
+  echo "Words not created/listed correctly!"
+ end
+
+ book delpage testb01 tpage02
+ book getpage testb01 1 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by delpage)!"
+ end
+
+ book init testb01
+ book getpage testb01 0 -var pcheck
+
+ if ("$pcheck" != "NULL")
+  $PASS = 0
+  echo "Book pages not deleted correctly (by init)!"
+ end
+
+ book delete testb01
+ book delete testb02
+
+# delete bname01 bname02 pcheck pname01 pname02 tword01 tword02 wcheck
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i bcheck pcheck wcheck
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  book create testb
+  book getbook 0 -var bcheck
+  book newpage testb testp1
+  book newpage testb testp2
+  book npages testb -var pcheck
+  book getpage testb 0 -var pcheck
+  book setword testb testp1 testw tada
+  book getword testb testp1 testw -var wcheck
+  book delpage testb testp2
+  book init testb
+  book delete testb
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+#delete i bcheck pcheck wcheck
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/concat.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/concat.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/concat.sh	(revision 17422)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does concat work?
+macro test1
+
+ $PASS = 1
+
+ create a 0 10
+ set b = a
+
+ concat a b
+
+ if ((b[] != 20) || (b[10] != 0))
+  $PASS = 0
+  echo "Concat failed!: nelements: b[] b(10)= b[10]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 0 10
+  set b = a
+  concat a b
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/create.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/create.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/create.sh	(revision 17422)
@@ -0,0 +1,44 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test create function
+macro test1
+ 
+ $PASS = 1
+
+ create x 0 10 0.5
+
+ if ((x[1] != 0.5) || (x[9] != 4.5))
+  $PASS = 0
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create y 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cumulative.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cumulative.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cumulative.sh	(revision 17422)
@@ -0,0 +1,46 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Does cumulative work?
+macro test1
+
+ $PASS = 1
+
+ create a 5 15
+
+ cumulative a acum
+
+ if (acum[5] != 45)
+  $PASS = 0
+  echo "Cumulative failed!: nelements: acum[] acum(5)= acum[5]"
+ end
+end
+
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  create a 5 15
+  cumulative a acum
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cut.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cut.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/cut.sh	(revision 17422)
@@ -0,0 +1,38 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if cut works
+macro test1
+
+ $PASS = 1
+
+ mcreate tim 100 10
+ zap tim 0 0 100 10 -v 10
+
+ cut tim xdir imx X 40 4 60 6
+ cut tim ydir imy Y 40 4 60 6
+
+ if (xdir[] != 60)
+  $PASS = 0
+ end
+ if (imx[] != 60)
+  $PASS = 0
+ end
+
+ if (ydir[] != 6)
+  $PASS = 0
+ end
+ if (imy[] != 6)
+  $PASS = 0
+ end
+
+ if (imx[0] != 10*6)
+  $PASS = 0
+ end
+ if (imy[0] != 10*60)
+  $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/delete.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/delete.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/delete.sh	(revision 17422)
@@ -0,0 +1,50 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test delete
+macro test1
+
+ $PASS = 1
+
+ $v = 7
+
+# create v 0 10
+
+ delete v
+
+ if ($?v != 0)
+  $PASS = 0
+  echo "Variable not deleted!"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  $u = testing
+  delete u
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimendown.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimendown.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimendown.sh	(revision 17422)
@@ -0,0 +1,60 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ mcreate timg 100 10
+ zap timg 0 0 100 10 -v 10
+ dimendown timg val
+ dimendown timg xc -x
+ dimendown timg yc -y
+
+ if (val[777] != 10)
+  $PASS = 0
+  echo "Value mismatch: val[777] (should be 10)"
+ end
+
+ if (xc[777] != 77)
+  $PASS = 0
+  echo "X Coord mismatch: xc[777] (should be 77)"
+ end
+
+ if (yc[777] != 7)
+  $PASS = 0
+  echo "Y Coord mismatch: yc[77] (should be 7)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  dimendown timg val
+  dimendown timg xc -x
+  dimendown timg yc -y
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimenup.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimenup.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/dimenup.sh	(revision 17422)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if dimendown works
+macro test1
+
+ $PASS = 1
+
+ create tvec 0 1000
+
+ dimenup tvec timg 10 100
+
+ stats -q timg 6 34 1 1
+
+ if ($MEAN != 346)
+  $PASS = 0
+  echo "Value mismatch: $MEAN (should be 346)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  dimenup tvec timg 10 100
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft1d.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft1d.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft1d.sh	(revision 17422)
@@ -0,0 +1,17 @@
+
+list tests
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create t 0 4096 1.0
+ set f = dsin(20*t)
+
+ fft1d f 0 to Frn Fin
+
+ clear
+ section a 0.0 0.0 1.0 0.5
+ lim t Fro; box; plot -pt 7 -c blue t Fro; plot -pt 2 -c red t Frn
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft2d.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft2d.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fft2d.sh	(revision 17422)
@@ -0,0 +1,27 @@
+
+list tests
+# test1
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete x y t f Frn Fin Fro Fio dfi dfr
+ mcreate t 2048 2048
+ set x = xramp(t)
+ set y = yramp(t)
+
+ # set f = dsin(3*x)*dcos(5*y)
+ set f = exp(-0.5*((x-1024)^2 + (y-1024)^2)*0.01)
+
+ date; fft2d f 0 to Frn Fin; date
+
+ date; fft2dold f 0 to Fro Fio; date
+
+ set dfr = Frn - Fro
+ set dfi = Fin - Fio
+
+ stats dfr
+ stats dfi
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit.sh	(revision 17422)
@@ -0,0 +1,223 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+ test8
+end
+
+# fit a line without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ fit -q x y 1
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.01)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors and weights
+macro test3
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 1 -dy dy
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.02)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.02)
+   $PASS = 0
+ end
+end
+
+# fit a line with errors, weights, and outliers 
+macro test4
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+ fit -q x y 1 -dy dy -clip 3 3
+
+ if ($Cn != 1)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.02)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.02)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic without errors
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete -q x y
+
+ create x 0 100
+ set y = 3 + 5*x - 4*x^2
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 1e-5)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 1e-5)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors
+macro test6
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ fit -q x y 2
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors and weights
+macro test7
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ fit -q x y 2 -dy dy
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
+
+# fit a quadratic with errors, weights, and outliers 
+macro test8
+ $PASS = 1
+ break -auto off
+
+ delete -q x y dy
+
+ create x 0 100
+ set dy = 0.1*rnd(x) - 0.05
+ set y = 3 + 5*x - 4*x^2 + dy
+ set dy = 0.1 + zero(x)
+ y[5] = 23
+ y[20] = -10
+ y[50] = 0.0
+
+ # it takes 4 iterations to successfully reject the outliers above...
+ fit -q x y 2 -dy dy -clip 3 4
+
+ if ($Cn != 2)
+   $PASS = 0
+ end
+ if ($Cnv != 97)
+   $PASS = 0
+ end
+ if (abs($C0 - 3) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C1 - 5) > 0.05)
+   $PASS = 0
+ end
+ if (abs($C2 + 4) > 0.05)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit2d.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit2d.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/fit2d.sh	(revision 17422)
@@ -0,0 +1,118 @@
+
+list tests
+ test1
+ test2
+ memtest1
+end
+
+# fit a function without errors
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 1e-5)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 1e-5)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 1e-5)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# fit a function with errors
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ fit2d -q x y z 2
+
+ if ($Cnn != 2)
+   $PASS = 0
+   echo "Function Order Incorrect!"
+ end
+ if (abs($CX0Y0 - 5) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y0 Incorrect!"
+ end
+ if (abs($CX1Y0 + 4) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y0 Incorrect!"
+ end
+ if (abs($CX2Y0 - 1) > 0.01)
+   $PASS = 0
+   echo "Term CX2Y0 Incorrect!"
+ end
+ if (abs($CX0Y1 + 3) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y1 Incorrect!"
+ end
+ if (abs($CX1Y1 - 6) > 0.01)
+   $PASS = 0
+   echo "Term CX1Y1 Incorrect!"
+ end
+ if (abs($CX0Y2 + 2) > 0.01)
+   $PASS = 0
+   echo "Term CX0Y2 Incorrect!"
+ end
+end
+
+# Memory Test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ create x 0 5 0.01
+ set y = sin((2*3.14159*x)/2)
+ set dz = 0.1*rnd(x) - 0.05
+ set z = 5-4*x+x^2-3*y+6*x*y-2*y^2+dz
+
+ for i 0 1000
+  fit2d -q x y z 2
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/gaussj.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/gaussj.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/gaussj.sh	(revision 17422)
@@ -0,0 +1,294 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+end
+
+# a very simple diagonal matrix equation
+macro test1
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][1] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][2] = -1
+ A[1][0] = -1
+ A[2][1] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[1][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[0] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[1] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a very simple off-diagonal matrix equation
+macro test2
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][1] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][0] = -1
+ A[1][2] = -1
+ A[1][1] = -1
+ A[2][0] = -1
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ # echo A[0][0] A[0][1] A[0][2]
+ # echo A[1][0] A[1][1] A[1][2]
+ # echo A[2][0] A[2][1] A[2][2]
+
+ if (abs(A[1][0] - 0.75) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[1][2] - 0.25) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[0][0] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[0][2] - 0.50) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(A[2][0] - 0.25) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][1] - 0.50) > 0.01)
+  $PASS = 0
+ end
+ if (abs(A[2][2] - 0.75) > 0.01)
+  $PASS = 0
+ end
+
+ if (abs(B[1] - 1.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[0] - 2.00) > 0.01)
+  $PASS = 0
+ end
+ if (abs(B[2] - 2.00) > 0.01)
+  $PASS = 0
+ end
+end
+
+# a singular matrix equation
+macro test3
+ $PASS = 1
+ break -auto off
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ gaussj -q A B
+ if ($STATUS)
+   $PASS = 0
+ end
+end
+
+# a very large matrix equation
+macro test4
+ $PASS = 1
+ break -auto off
+
+ $Ndim = 50
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
+# a nearly singular matrix equation
+macro test5
+ $PASS = 1
+ break -auto off
+
+ delete A B inA inB meas
+
+ mcreate A 3 3
+ create B 0 3
+
+ A[0][0] = 2
+ A[1][0] = 2.00001
+ A[2][2] = 2
+
+ A[0][1] = -1
+ A[1][1] = -1
+ A[2][1] = -1
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-5)
+    $PASS = 0
+  end
+ end
+end
+
+# a very large matrix equation
+# timing on my laptop: 100 : ~1sec; 300 : ~7sec; 1000 : ~88sec
+# note: at Ndim = 1000, it failed (Ax - B was in the range -2..+2)
+macro test6
+ if ($0 != 2)
+  echo "USAGE: test6 (Ndim)"
+  break
+ end
+
+ $PASS = 1
+ break -auto off
+
+ $Ndim = $1
+ mcreate A $Ndim $Ndim
+ create B 0 $Ndim
+
+ # generate the diagonal + off-diagonal elements
+ for i 0 $Ndim
+   A[$i][$i] = 2.0
+   if ($i > 0)
+     A[$i][$i-1] = -1.0
+   end
+   if ($i < $Ndim - 1)
+     A[$i][$i+1] = -1.0
+   end
+ end
+
+ set inB = B
+ set inA = A
+
+ gaussj A B
+ if (not($STATUS))
+   $PASS = 0
+ end
+
+ set meas = zero(inB)
+ for i 0 B[]
+  for j 0 B[]
+   meas[$i] = meas[$i] + inA[$i][$j] * B[$j]
+  end
+ end
+
+ for i 0 inB[]
+  if (abs(inB[$i]-meas[$i]) > 1e-3)
+    $PASS = 0
+    echo inB[$i] meas[$i] {inB[$i]-meas[$i]}
+  end
+ end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/histogram.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/histogram.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/histogram.sh	(revision 17422)
@@ -0,0 +1,52 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if histogram works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ create x 0 10 0.1
+
+ for i 45 55
+  x[$i] = 4.5
+ end
+
+ histogram x xhis 0 10 0.1
+
+ if ((xhis[10] != 1) || (xhis[45] != 10))
+  $PASS = 0
+  echo "Value mismatch: xhis[10] xhis[45] (should be 1,10)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  histogram x xhis 0 10 0.1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imhist.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imhist.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imhist.sh	(revision 17422)
@@ -0,0 +1,58 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imhist works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ mcreate buff 100 10
+
+ zap buff 40 0 10 10 -v 17
+ zap buff 60 5 5 2 -v 6
+
+ imhist buff xvec yvec
+
+ if ((xvec[1024] != 17) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 17,100)"
+ end
+
+ imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+
+ if ((xvec[1024] != 10) || (yvec[1024] != 100))
+  $PASS = 0
+  echo "Value mismatch: xvec[1024] yvec[1024] (should be 10,100)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imsmooth.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imsmooth.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/imsmooth.sh	(revision 17422)
@@ -0,0 +1,59 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imsmooth works
+macro test1
+
+ $PASS = 1
+
+ local i j
+
+ mcreate buff 100 100
+
+ for i 0 99
+  for j 0 99
+   zap buff $i $j 1 1 -v {100*rnd($i)}
+  end
+ end
+
+ stats -q buff
+ $s1 = $SIGMA
+
+ imsmooth buff 10
+ stats -q buff
+ $s2 = $SIGMA
+
+ if ($s2/$s1 >= 0.1)
+  $PASS = 0
+  echo "Inadequate noise reduction: {$s2/$s1*100}\% (should be less than 10%)"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  imsmooth buff 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/integrate.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/integrate.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/integrate.sh	(revision 17422)
@@ -0,0 +1,47 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if integrate works
+macro test1
+
+ $PASS = 1
+
+ create x 0 10 0.01
+ set y = 1+2*x+3*x^2
+
+ integrate x y 1 5
+
+ if (abs ($sum-152) > 0.5)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  integrate x y 1 5
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/interpolate.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/interpolate.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/interpolate.sh	(revision 17422)
@@ -0,0 +1,48 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if interpolate works
+macro test1
+
+ $PASS = 1
+
+ create x0 0 10 0.1
+ set y0 = 1+2*x0+3*x0^2
+ create x1 0 10 0.001
+ interpolate x0 y0 x1 y1
+ integrate x1 y1 1 5
+
+ if (abs ($sum-152) > 0.08)
+  $PASS = 0
+  echo "Inaccurate result (should be 152): $sum"
+ end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  interpolate x0 y0 x1 y1
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/peak.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/peak.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/peak.sh	(revision 17422)
@@ -0,0 +1,103 @@
+
+list tests
+ test1
+ test2
+ test3
+ testmem1
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 0 100
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using auto range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[50] = 1
+
+ peak -q x y 
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test using constrained range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x 0 100
+ set y = zero (x)
+ y[60] = 2
+ y[50] = 1
+ y[40] = 2
+
+ peak -q x y 45 55
+
+ if ($peakpos != 50)
+   $PASS = 0
+ end
+ if ($peaknum != 51)
+   $PASS = 0
+ end
+ if ($peakval != 1)
+   $PASS = 0
+ end
+end
+
+# test memory usage
+macro testmem1
+ break -auto off
+
+ create x 0 1000
+ set y = zero (x)
+ y[500] = 100
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 10000
+   peak -q x y 400 600
+ end
+ 
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} < 10)
+   $PASS = 1
+ else
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/periodogram.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/periodogram.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/periodogram.sh	(revision 17422)
@@ -0,0 +1,143 @@
+
+list tests
+ test1
+ test2
+ test3
+ memtest1
+end
+
+# test using even samples
+macro test1
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q t f period power
+
+ create t 0 100
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+   echo "OFFSET: {$peakpos - $P}"
+ end
+end
+
+# test using random samples
+macro test2
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+ periodogram t f 5 50 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.5)
+   $PASS = 0
+ end
+end
+
+# test using random samples, higher frequency
+macro test3
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 2.0
+
+ delete -q x t f period power
+
+ create x 0 100
+ set t = 100 * rnd(x) 
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 1 10 period power
+
+ # lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+ # lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# test using random samples, offset start
+macro test4
+ $PASS = 1
+ break -auto off
+
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ periodogram t f 2 30 period power
+
+#  lim -n 0 t f; clear; box; plot -x 2 -pt 2 t f
+#  lim -n 1 period power; clear; box; plot period power
+
+ peak -q period power
+
+ if (abs ($peakpos - $P) > 0.05)
+   $PASS = 0
+ end
+end
+
+# Memory test
+macro memtest1
+
+ local i
+ local P PI
+ $PI = 3.14159265359
+ $P  = 15.0
+
+ delete -q x t f period power
+
+ create x 500 800
+ set t = 300 * rnd(x) + 500
+ set f = sin(2*$PI*t/$P)
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 100
+  periodogram t f 2 30 period power
+ end
+  
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/100}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/queues.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/queues.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/queues.sh	(revision 17422)
@@ -0,0 +1,347 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test4.1
+ test5
+ test5.1
+ testmem1
+ testmem2
+ testmem3
+ testmem4.0
+ testmem4.1
+ testmem4.2
+ testmem4.3
+ testmem5.0
+ testmem5.1
+end
+
+# test queueinit
+macro test1
+ $PASS = 1
+ queueinit dummy
+ queuesize dummy -var N
+
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queueinit memory
+macro testmem1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuesize
+macro test2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuesize memory
+macro testmem2
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuesize dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuedelete
+macro test3
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepush dummy foobar
+ queuepush dummy foobar
+
+ queuedelete dummy
+ queuepush dummy foobar
+ queuesize dummy -var N
+
+ if ($N != 1)
+   $PASS = 0
+ end
+end
+
+# test queuedelete memory
+macro testmem3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuedelete dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop
+macro test4
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foobar
+ queuepop  dummy -var N
+
+ if ("$N" != "foobar")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop
+macro test4.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy foo1
+ queuepush dummy foo2
+ queuepush dummy foo3
+
+ queuepop  dummy -var N
+ if ("$N" != "foo1")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo2")
+   $PASS = 0
+ end
+ queuepop  dummy -var N
+ if ("$N" != "foo3")
+   $PASS = 0
+ end
+ queuesize dummy -var N
+ if ($N != 0)
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.0
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy -var N
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.1
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.2
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queueinit dummy
+   queuepush dummy foobar
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop memory
+macro testmem4.3
+ $PASS = 1
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ queueinit dummy
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy foobar
+   queuepop dummy
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "test 3 word"
+ queuepop  dummy -var N -key 1 2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# test queuepush / queuepop with keys
+macro test5.1
+ $PASS = 1
+ queueinit dummy
+ queuepush dummy "test 1 word"
+ queuepush dummy "test 2 word"
+ queuepush dummy "bird 2 word"
+ queuepop  dummy -var N -key 0:1 test:2
+
+ if ("$N" != "test 2 word")
+   $PASS = 0
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.0
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepush dummy "test 2 word"
+   queuepush dummy "test 3 word"
+   queuepop dummy -var N -key 1 2
+   queuepop dummy -var N -key 1 1
+   queuepop dummy -var N -key 1 3
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+   queuelist
+ end
+end
+
+# memory test for queuepush / queuepop with keys
+macro testmem5.1
+ $PASS = 1
+ queueinit dummy
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ output /dev/null
+ for i 0 10000
+   queuepush dummy "test 1 word"
+   queuepop dummy -var N -key 1 1
+ end
+ output stdout
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ if ({$endmem - $startmem} > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/stats.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/stats.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/stats.sh	(revision 17422)
@@ -0,0 +1,156 @@
+
+$skip = 0
+
+list tests
+ test1
+ test2
+ test3
+end
+
+# test using full range
+macro test1
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test2
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf  - - - -
+ $npix = 100*100
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo $TOTAL {$MEAN*$npix} {abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
+
+# test using full range
+macro test3
+ $PASS = 1
+ break -auto off
+
+ gaussdev dev {100*100} 0.0 1.0
+ dimenup dev buf 100 100
+ stats -q buf 10 10 10 10
+ $npix = 10*10
+
+ if ($STATUS == 0)
+   echo "failed command"
+   $PASS = 0
+ end
+ if ($NPIX != $npix) 
+   echo "failed npix"
+   $PASS = 0
+ end
+ if (abs($MEAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed mean"
+   $PASS = 0
+ end
+ if (abs($MEDIAN - 0.0) > (2.0/sqrt($npix))) 
+   echo "failed median"
+   $PASS = 0
+ end
+ if (abs($SIGMA - 1.0) > (2.0/sqrt($npix))) 
+   echo "failed sigma"
+   $PASS = 0
+ end
+ if ($MIN < -4.0) 
+   echo "failed min"
+   $PASS = 0
+ end
+ if ($MAX > +4.0)
+   echo "failed max"
+   $PASS = 0
+ end
+# I don't this MODE is being correctly calculated.
+ if ($skip && (abs($MODE - 0.0) > (2.0/sqrt($npix))))
+   echo "MODE fails: known problem with mode"
+   $PASS = 0
+ end
+ if (abs($TOTAL - $MEAN*$npix) > (2.0/sqrt($npix))) 
+   echo "failed total"
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vgauss.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vgauss.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vgauss.sh	(revision 17422)
@@ -0,0 +1,99 @@
+
+list tests
+ test1
+ test2
+ test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+ set dy = (rnd(y) - 0.5)/0.5
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 0.1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.1)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.2)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vmaxwell.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vmaxwell.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/test/vmaxwell.sh	(revision 17422)
@@ -0,0 +1,103 @@
+
+list tests
+# test1
+ test2
+# test3
+end
+
+macro test1
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 5 * exp(-0.5*x^2/3^2)
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 10
+ $C3 = 1
+ set dy = sqrt(y)
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 5.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.01)
+   $PASS = 0
+ end
+end
+
+# noise of 0.1
+macro test2
+ $PASS = 1
+ break -auto off
+
+ create x 0 1000 1
+ set y = 1000 * (x-200)^2 * exp(-0.5*(x-300)^2/30^2)
+ set dy = (rnd(y) - 0.5)/0.5
+ set y = y + dy
+
+ lim x y; clear; box; plot -x 1 -c black x y
+
+ $C0 = 250
+ $C1 = 25
+ $C2 = 900
+ $C3 = 1
+ $C4 = 190
+
+ vmaxwell x y dy yfit
+ plot x yfit -c red
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 0.1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.1)
+   $PASS = 0
+ end
+end
+
+# poisson-distributed noise
+macro test3
+ $PASS = 1
+ break -auto off
+
+ create x -10 10 0.1
+ set y = 1000 * exp(-0.5*x^2/3^2)
+
+ gaussdev dY y[] 0.0 1.0
+ set dy = dY * sqrt(y)
+ set y = y + dy
+
+ $C0 = 0.5
+ $C1 = 2
+ $C2 = 900
+ $C3 = 1
+
+ vgauss -q x y dy yfit
+
+ if (abs($C0 - 0.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C1 - 3.0) > 0.01)
+   $PASS = 0
+ end
+ if (abs($C2 - 1000.0) > 1)
+   $PASS = 0
+ end
+ if (abs($C3 - 0.0) > 0.2)
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/teststats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/teststats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/teststats.c	(revision 17422)
@@ -0,0 +1,117 @@
+# include "data.h"
+
+int teststats (int argc, char **argv) {
+  
+  int i, N;
+  double max, min, sum, var, dvar, mean, stdev;
+  float *X, IgnoreValue;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed;
+  double dx, mode, median;
+  Vector *vec;
+
+  Ignore = FALSE;
+  if (N = get_argument (argc, argv, "-ignore")) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if (N = get_argument (argc, argv, "-q")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if (N = get_argument (argc, argv, "-quiet")) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: teststats (vector)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Mean = Stdev = 0; 
+  /* first pass: measure the sample statistics */
+  for (j = 0; j < 3; j++) {
+    /* calculate mean, npix */
+    X = vec[0].elements;
+    max = min = *X;
+    sum = N = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - Mean) > 3*stdev)) continue;
+      sum += *X;
+      N++;
+    }      
+    mean = sum / N;
+    /* calculate stdev */
+    X = vec[0].elements;
+    var = 0;
+    for (i = 0; i < vec[0].Nelements; i++, X++) {
+      if (!finite (*X)) continue;
+      if (Ignore && (*X == IgnoreValue)) continue;
+      if ((j > 0) && (fabs (*X - mean) > 3*stdev)) continue;
+      dvar = (*X - mean);
+      var += dvar*dvar;
+    }      
+    stdev = sqrt (var / N);
+    Mean = mean;
+  }
+  mean = Mean;
+  set_variable ("MEAN_C",     mean);
+  set_variable ("SIGMA_C",    stdev);
+
+  /* construct histogram with resolution of 0.1*stdev and range -1000*dx : 1000*dx centered on mean */
+  dx = 0.1*stdev;
+  min = mean - 1000*dx;
+  max = mean + 1000*dx;
+  NVAL = 1 + (int)((max - min) / dx);
+  ALLOCATE (Nval, int, NVAL);
+  bzero (Nval, NVAL*sizeof(int));
+  X = vec[0].elements;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    bin = MAX (0, MIN (NVAL, (*X - min) / dx));
+    Nval[bin] ++;
+  }      
+
+  /* find mode */
+  Nmode = 0;
+  mode = Nval[Nmode];
+  for (i = 0; i < NVAL; i++) {
+    if (mode < Nval[i]) {
+      Nmode = i;
+      mode = Nval[Nmode];
+    }
+  }
+  mode = Nmode * dx + min;
+
+  
+
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %f, stdev: %f, min: %f, max: %f, median: %f, mode: %f, Npts: %d\n", 
+	     mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_variable ("MEAN",     mean);
+  set_variable ("SIGMA",    stdev);
+
+  set_int_variable ("NPIX", N);
+
+  free (Nval);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/textline.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/textline.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/textline.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int textline (int argc, char **argv) {
+
+  int N, size, FracPositions;
+  char name[64];
+  double x, y, angle;
+  int kapa;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  if ((N = get_argument (argc, argv, "-fn"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    size = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    KapaSetFont (kapa, name, size);
+  } 
+
+  /* FracPositions uses coordinates of 0-1 relative to axis range */
+  FracPositions = FALSE;
+  if ((N = get_argument (argc, argv, "-frac"))) {
+    remove_argument (N, &argc, argv);
+    FracPositions = TRUE;
+  } 
+
+  angle = 0.0;
+  if ((N = get_argument (argc, argv, "-rot"))) {
+    remove_argument (N, &argc, argv);
+    angle = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } 
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: text x y (line) [-fn (font) size] [-rot angle]\n");
+    return (FALSE);
+  }
+
+  if (strlen (argv[3]) > 127) {
+    gprint (GP_ERR, "labels currently limited to 127 chars\n");
+    return (FALSE);
+  }
+
+  x = atof (argv[1]);
+  y = atof (argv[2]);
+  
+  if (FracPositions) {
+    x =  x * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y =  y * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
+
+  KapaSendTextline (kapa, argv[3], x, y, angle);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tv.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tv.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tv.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int tv (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name, *file;
+  Coords coords;
+  Buffer *buf;
+  KiiImage image;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  /* shell exits on pipe close, FIX */
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    KiiClose (kapa);
+    return (TRUE);
+  }
+
+  data.logflux = FALSE;
+  if ((N = get_argument (argc, argv, "-log"))) {
+    remove_argument (N, &argc, argv);
+    data.logflux = TRUE;
+  }
+
+  // use the currently-set zero,range values if not supplied
+  if ((argc != 2) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: tv <buffer> [zero range] [-n Nimage] [-log] [-kill]\n");
+    return (FALSE);
+  }
+
+  if (argc == 4) {
+    data.zero = atof (argv[2]);
+    data.range = atof (argv[3]);
+    if (data.range == 0.0) data.range = 0.001;
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+  
+  image.data1d = (float *) buf[0].matrix.buffer;
+  image.Nx = buf[0].matrix.Naxis[0];
+  image.Ny = buf[0].matrix.Naxis[1];
+
+  // send only the root of the file, not the full path
+  file = filerootname (buf[0].file);
+  strcpy (data.file, file);
+  free (file);
+
+  strcpy (data.name, argv[1]);
+  
+  KiiNewPicture1D (kapa, &image, &data, &coords);
+
+  set_str_variable ("TV", argv[1]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvchannel.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvchannel.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvchannel.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "data.h"
+
+int tvchannel (int argc, char **argv) {
+  
+  int N, kapa, Nchannel;
+  char *name;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvchannel (channel)\n");
+    return (FALSE);
+  }
+
+  Nchannel = atoi (argv[1]);
+  if (Nchannel == 0) {
+    // try the string values R/Red, G/Green, B/Blue
+    if (!strcasecmp (argv[1], "R") || !strcasecmp (argv[1], "RED")) {
+      Nchannel = 1;
+    }
+    if (!strcasecmp (argv[1], "G") || !strcasecmp (argv[1], "GREEN")) {
+      Nchannel = 2;
+    }
+    if (!strcasecmp (argv[1], "B") || !strcasecmp (argv[1], "BLUE")) {
+      Nchannel = 3;
+    }
+  }
+  if ((Nchannel < 1) || (Nchannel > 3)) {
+    gprint (GP_ERR, "invalid channel : use 1 - 3 or (R)ed, (G)reen, (B)lue\n");
+    return (FALSE);
+  }
+    
+  KiiSetChannel (kapa, Nchannel - 1);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcolors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcolors.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "data.h"
+
+int tvcolors (int argc, char **argv) {
+  
+  int N, kapa;
+  char *name;
+  KapaImageData data;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (&data, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  // use the currently-set zero,range values if not supplied
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: tvcolors (colormap)\n");
+    return (FALSE);
+  }
+
+  KiiSetColormap (kapa, argv[1]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcontour.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcontour.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvcontour.c	(revision 17422)
@@ -0,0 +1,287 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int tvcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, Npix, Nx, Ny;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *name;
+  int kapa, N, Noverlay, NOVERLAY;
+  Buffer *buf;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if ((argc != 4) && (argc != 5)) {
+    gprint (GP_ERR, "USAGE: contour <buffer> (overlay) level [Npix]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[3]);
+  if (argc == 5) 
+    Npix = (int) MAX (atof (argv[4]), 1); 
+  else 
+    Npix = 1;
+  level *= Npix*Npix;
+
+  /*** make rebinned image ***/
+  Nx = buf[0].header.Naxis[0]/Npix;
+  Ny = buf[0].header.Naxis[1]/Npix;
+  if (Npix != 1) {
+    gprint (GP_LOG, "rebin by a factor of %d (%d,%d)\n", Npix, Nx, Ny);
+    ALLOCATE (matrix, float, Nx*Ny);
+    bzero (matrix, Nx*Ny*sizeof(float));
+	
+    for (j = 0; j < Ny; j++) {
+      for (jj = 0; jj < Npix; jj++) {
+	Vout = matrix + j*Nx;
+	Vin  = (float *)(buf[0].matrix.buffer) + (j*Npix + jj)*buf[0].header.Naxis[0];
+	for (i = 0; i < Nx; i++, Vout++) {
+	  for (ii = 0; ii < Npix; ii++, Vin++) {
+	    *Vout += *Vin;
+	  }
+	}
+      }
+    }
+  } else {
+    gprint (GP_ERR, "using scale of 1\n");
+    matrix = (float *)(buf[0].matrix.buffer);
+  }
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v00 = matrix;
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+    }
+  }
+  
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvgrid.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvgrid.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/tvgrid.c	(revision 17422)
@@ -0,0 +1,152 @@
+# include "data.h"
+# define TEN(X) (pow(10.0, (double)(X)))
+
+int tvgrid (int argc, char **argv) {
+  
+  int ndig1, ndig2, NX, NY, connect;
+  int tDEC, tRA;
+  double ra, dec, ra0, dec0, ra1, dec1;
+  double x0, y0, x1, y1;
+  double dDEC, fDEC, dRA, fRA;
+  char format[16];
+  Coords coords;
+  int kapa, N, Noverlay, NOVERLAY;
+  char *name;
+  Buffer *buf;
+  KiiOverlay *overlay;
+
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: tvgrid (overlay) (buffer)\n");
+    gprint (GP_ERR, " (overlay) may be: red, green, blue, yellow\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  GetCoords (&coords, &buf[0].header);
+
+  XY_to_RD (&ra0, &dec0, 0.0, 0.0, &coords);
+  XY_to_RD (&ra1, &dec1, (double)buf[0].header.Naxis[0], (double)buf[0].header.Naxis[1], &coords);
+  gprint (GP_ERR, "%f %f  %f %f\n", ra0, dec0, ra1, dec1);
+  
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  dDEC = fabs(dec1 - dec0);
+  tDEC = log10(dDEC) - log10(5.0);
+  fDEC = log10(dDEC) - tDEC;
+  if ((fDEC > log10(0.5)) && (fDEC < log10(1.0))) {
+    dDEC = TEN (tDEC + log10(0.1));
+  }
+  if ((fDEC > log10(1.0)) && (fDEC < log10(2.0))) {
+    dDEC = TEN (tDEC + log10(0.2));
+  }
+  if ((fDEC > log10(2.0)) && (fDEC < log10(5.0))) {
+    dDEC = TEN (tDEC + log10(0.5));
+  }
+  if ((fDEC > log10(5.0)) && (fDEC < log10(10.0))) {
+    dDEC = TEN (tDEC + log10(1.0));
+  }
+  if ((fDEC > log10(10.0)) && (fDEC < log10(20.0))) {
+    dDEC = TEN (tDEC + log10(2.0));
+  }
+  if ((fDEC > log10(20.0)) && (fDEC < log10(50.0))) {
+    dDEC = TEN (tDEC + log10(5.0));
+  }
+  ndig2 = ((log10(dDEC) < 0) ? fabs(log10(dDEC)) : 0);
+  ndig1 = 3 + log10(MAX(dec0, dec1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  NX = buf[0].header.Naxis[0];
+  NY = buf[0].header.Naxis[1];
+
+  x0 = y0 = 0;
+  dRA = MAX (fabs(ra1 - ra0) / 100.0, 0.1);
+  connect = FALSE;
+  for (dec = dDEC * ((int)(MIN(dec0,dec1)/dDEC) + 1); dec < MAX(dec0,dec1); dec += dDEC) {
+    for (ra = 0; ra < 361; ra += dRA) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  dRA = fabs(ra1 - ra0);
+  tRA = log10(dRA) - log10(5.0);
+  fRA = log10(dRA) - tRA;
+  if ((fRA > log10(0.5)) && (fRA < log10(1.0))) {
+    dRA = TEN (tRA + log10(0.1));
+  }
+  if ((fRA > log10(1.0)) && (fRA < log10(2.0))) {
+    dRA = TEN (tRA + log10(0.2));
+  }
+  if ((fRA > log10(2.0)) && (fRA < log10(5.0))) {
+    dRA = TEN (tRA + log10(0.5));
+  }
+  if ((fRA > log10(5.0)) && (fRA < log10(10.0))) {
+    dRA = TEN (tRA + log10(1.0));
+  }
+  if ((fRA > log10(10.0)) && (fRA < log10(20.0))) {
+    dRA = TEN (tRA + log10(2.0));
+  }
+  if ((fRA > log10(20.0)) && (fRA < log10(50.0))) {
+    dRA = TEN (tRA + log10(5.0));
+  }
+  ndig2 = ((log10(dRA) < 0) ? fabs(log10(dRA)) : 0);
+  ndig1 = 3 + log10(MAX(ra0, ra1)) + ndig2;
+  sprintf (format, "%%%d.%df", ndig1, ndig2);
+  gprint (GP_ERR, "format: %s..\n", format);
+
+  dDEC = MAX (fabs(dec1 - dec0) / 100.0, 0.1);
+  connect = FALSE;
+  for (ra = dRA * ((int)(MIN(ra0,ra1)/dRA) + 1); ra < MAX(ra0,ra1); ra += dRA) {
+    for (dec = -90; dec < 90; dec += dDEC) {
+      RD_to_XY (&x1, &y1, ra, dec, &coords);
+      if ((x1 >= 0) && (x1 < NX) && (y1 >= 0) && (y1 < NY)) {
+	if (connect) {
+	  overlay[Noverlay].type = KII_OVERLAY_LINE;
+	  overlay[Noverlay].x = x0;
+	  overlay[Noverlay].y = y0;
+	  overlay[Noverlay].dx = x1 - x0;
+	  overlay[Noverlay].dy = y1 - y0;
+	  Noverlay ++;
+	  CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+	}
+	x0 = x1;
+	y0 = y1;
+	connect = TRUE;
+      } else {
+	connect = FALSE;
+      }
+    }
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ungridify.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ungridify.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/ungridify.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "data.h"
+
+int ungridify (int argc, char **argv) {
+
+  int i, j, n;
+  int Nx, Ny, NX, NY;
+  int Xmin, Xmax, Ymin, Ymax;
+  float *v, *x, *y, *z;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: ungridify buffer Xmin Xmax Ymin Ymax x y z\n");
+    return (FALSE);
+  }
+  
+  if ((bf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  Xmin = atof (argv[2]);
+  Xmax = atof (argv[3]);
+  Ymin = atof (argv[4]);
+  Ymax = atof (argv[5]);
+  Nx = Xmax - Xmin;
+  Ny = Ymax - Ymin;
+  
+  NX = bf[0].matrix.Naxis[0];
+  NY = bf[0].matrix.Naxis[1];
+
+  if ((vx = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  REALLOCATE (vx[0].elements, float, Nx*Ny);
+  REALLOCATE (vy[0].elements, float, Nx*Ny);
+  REALLOCATE (vz[0].elements, float, Nx*Ny);
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  n = 0;
+  v = (float *)bf[0].matrix.buffer;
+  for (j = Ymin; j < Ymax; j++) {
+    for (i = Xmin; i < Xmax; i++, x++, y++, z++, n++) {
+      vx[0].elements[n] = i;
+      vy[0].elements[n] = j;
+      if (i < 0) continue;
+      if (i >= NX) continue;
+      if (j < 0) continue;
+      if (j >= NY) continue;
+      vz[0].elements[n] = v[i+j*NX];
+    }
+  }
+  if (n != Nx*Ny) {
+    gprint (GP_ERR, "error in ungrid: %d vs %d (%d x %d)\n", n, Nx*Ny, Nx, Ny);
+  }
+  vx[0].Nelements = vy[0].Nelements = vz[0].Nelements = n;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/uniq.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/uniq.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/uniq.c	(revision 17422)
@@ -0,0 +1,39 @@
+# include "data.h"
+
+int uniq (int argc, char **argv) {
+  
+  int Nnew, i, j, found;
+  float *v1, *v2;
+  Vector *ivec, *ovec;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: uniq (in) (out)\n");
+    return (FALSE);
+  }
+
+  if ((ivec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* allocate the maximum possible needed */
+  ALLOCATE (ovec[0].elements, float, ivec[0].Nelements);
+
+  Nnew = 0;
+  v1 = ivec[0].elements;
+  for (i = 0; i < ivec[0].Nelements; i++, v1++) {
+    v2 = ovec[0].elements;
+    found = FALSE;
+    for (j = 0; !found && (j < Nnew); j++, v2++) {
+      if (*v1 == *v2) found = TRUE;
+    }
+    if (!found) {
+      ovec[0].elements[Nnew] = *v1;
+      Nnew ++;
+    }
+  }
+
+  ovec[0].Nelements = Nnew;
+  REALLOCATE (ovec[0].elements, float, ovec[0].Nelements);
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/unsign.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/unsign.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/unsign.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "data.h"
+
+int unsign (int argc, char **argv) {
+  
+  if (argc == 1) {
+    if (gfits_get_unsign_mode()) 
+      gprint (GP_ERR, "mode is now UNSIGNED int \n");
+    else
+      gprint (GP_ERR, "mode is now SIGNED int \n");
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcmp (argv[1], "1")) {
+      gfits_set_unsign_mode (TRUE);
+      set_int_variable ("UNSIGN", 1);
+      return (TRUE);
+    }
+    if (!strcmp (argv[1], "0")) {
+      gfits_set_unsign_mode (FALSE);
+      set_int_variable ("UNSIGN", 0);
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: unsign [0/1]\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vbin.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vbin.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vbin.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "data.h"
+
+int vbin (int argc, char **argv) {
+  
+  int i, j, n, N, Nin, Nout;
+  int Normalize, Ignore;
+  float *Vout, *Vin, IgnoreValue;
+  double scale;
+  Vector *in, *out;
+
+  Normalize = FALSE;
+  if ((N = get_argument (argc, argv, "-norm"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = TRUE;
+  }
+
+  Ignore = FALSE;
+  IgnoreValue = 0.0;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vbin <input> <output> scale \n");
+    gprint (GP_ERR, "  (use interpolate to expand)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  scale  = atof (argv[3]);
+  if ((int)(scale) != scale) {
+    gprint (GP_ERR, "integer binning only, please\n");
+    return (FALSE);
+  }
+
+  Nin  = in[0].Nelements;
+  Nout = Nin / scale;
+
+  REALLOCATE (out[0].elements, float, Nout);
+  out[0].Nelements = Nout;
+
+  Vin  = in[0].elements;
+  Vout = out[0].elements;
+  for (n = j = 0; j < Nout; j++, Vout++) {
+    *Vout = 0;
+    for (N = i = 0; (i < scale) && (n < Nin); n++, i++, Vin++) {
+      if (!finite (*Vin)) continue;
+      if (Ignore && (*Vin == IgnoreValue)) continue;
+      *Vout += *Vin;
+      N ++;
+    } 
+    if (Normalize) {
+      if (N > 0) { 
+	*Vout /= (float) N; 
+      } else {
+	*Vout = 0;
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vclip.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vclip.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vclip.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "data.h"
+
+int vclip (int argc, char **argv) {
+
+  int i, Npix, DO_NAN, DO_INF, N;
+  double min, Vmin, max, Vmax, nan_val, inf_val;
+  float *in;
+  Vector *vec;
+
+  inf_val = nan_val = min = Vmin = max = Vmax = 0;
+
+  DO_NAN = FALSE;
+  if ((N = get_argument (argc, argv, "-nan"))) {
+    remove_argument (N, &argc, argv);
+    nan_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_NAN = TRUE;
+  }
+
+  DO_INF = FALSE;
+  if ((N = get_argument (argc, argv, "-inf"))) {
+    remove_argument (N, &argc, argv);
+    inf_val  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    DO_INF = TRUE;
+  }
+
+  if ((argc != 6) && (!(DO_INF || DO_NAN))) {
+    gprint (GP_ERR, "USAGE: vclip (vector) [min Vmin max Vmax] [-inf val] [-nan val]\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (argc == 6) {
+    min = atof (argv[2]);
+    Vmin = atof (argv[3]);
+    max = atof (argv[4]);
+    Vmax = atof (argv[5]);
+  }
+
+  Npix = vec[0].Nelements;
+  in =   vec[0].elements;
+
+  if (argc == 6) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (*in < min) 
+	*in = Vmin;
+      if (*in > max)
+	*in = Vmax;
+    }
+  }
+  in = vec[0].elements;
+  if (DO_NAN) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (isnan (*in)) {
+	*in = nan_val;
+      }
+    }
+  }
+  in = vec[0].elements;
+  if (DO_INF) {
+    for (i = 0; i < Npix; i++, in++) {
+      if (!finite (*in)) {
+	*in = inf_val;
+      }
+    }
+  }
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vcontour.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vcontour.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vcontour.c	(revision 17422)
@@ -0,0 +1,278 @@
+# include "data.h"
+# define LL { \
+ dx =  d00 / (*v01 - *v00); \
+ dy = -d00 / (*v10 - *v00); \
+ x = i - 0.5; \
+ y = j - dy - 0.5; }
+
+# define UL { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = 1 - tmp; \
+ dx =  d10 / (*v11 - *v10); \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+      
+# define LR { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = 1 - tmp; \
+ dy = d01 / (*v11 - *v01); \
+ x = i + tmp - 0.5; \
+ y = j - 0.5; }
+
+# define UR { \
+ tmp = d10 / (*v11 - *v10); \
+ dx = 1 - tmp; \
+ dy = -d11 / (*v01 - *v11); \
+ x = i + tmp - 0.5; \
+ y = j + 1 - 0.5; }
+      
+# define HZ { \
+ tmp = d00 / (*v10 - *v00); \
+ dy = d01 / (*v11 - *v01) - tmp; \
+ dx = 1; \
+ x = i - 0.5; \
+ y = j + tmp - 0.5; }
+
+# define VT { \
+ tmp = d00 / (*v01 - *v00); \
+ dx = d10 / (*v11 - *v10) - tmp; \
+ x = i + tmp - 0.5; \
+ dy = 1; \
+ y = j - 0.5; }
+
+# define DUMP { \
+overlay[Noverlay].type = KII_OVERLAY_LINE; \
+overlay[Noverlay].x = Npix*x; \
+overlay[Noverlay].y = Npix*y; \
+overlay[Noverlay].dx = Npix*dx; \
+overlay[Noverlay].dy = Npix*dy; \
+Noverlay ++; \
+CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000); \
+}
+
+int vcontour (int argc, char **argv) {
+
+  int i, j, ii, jj, n, Nbuf, Npix, Nx, Ny, Nline;
+  float level, d00, d01, d10, d11, tmp;
+  float x, y, dx, dy;
+  float *v00, *v01, *v10, *v11;
+  float *Vout, *Vin, *matrix;
+  char *buffer, line[17];
+  int Ximage, Nimage, N;
+  Vector *xvec, *yvec, *zvec;
+
+  gprint (GP_ERR, "vcontour not working yet\n");
+  return (FALSE);
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vcontour x y z Xc Yc (level)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((foo = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  level = atof (argv[6]);
+
+  /* convert the x,y,z vectors to a matrix */
+  *V = xvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < xvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Xmax = max; Xmin = min;
+
+  *V = yvec[0].elements;
+  max = min = *V;
+  for (i = 0; i < yvec[0].Nelements; i++, V++) {
+    if (!finite (*V)) continue;
+    max = MAX (*V, max);
+    min = MIN (*V, min);
+  }      
+  Ymax = max; Ymin = min;
+
+  /* not really finished */
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, NOVERLAY);
+
+  v01 = matrix + 1;
+  v10 = matrix + Nx;
+  v11 = matrix + Nx + 1;
+  d01 = (level - *v00);
+  d11 = (level - *v10);
+  for (j = 1; j < Ny; j++) {
+    if (!(j%10)) gprint (GP_ERR, ".");
+    for (i = 1; i < Nx; i++, v00++, v01++, v10++, v11++) {
+
+      d00 = d01;
+      d10 = d11;
+      d01 = (level - *v01);
+      d11 = (level - *v11);
+
+      if (((d00 > 0) && (d01 > 0) && (d10 > 0) && (d11 > 0)) ||
+	  ((d00 < 0) && (d01 < 0) && (d10 < 0) && (d11 < 0)))
+	continue;
+
+      if ((d00 > 0) && (d10 <= 0)) { 
+	if ((d01 <= 0) && (d11 <= 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* \\  */
+	  LL;
+	  DUMP;
+	  UR;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 <= 0) && (d10 > 0)) {
+	if ((d01 > 0) && (d11 > 0)) { /* \  */
+	  LL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) { /* -  */
+	  HZ;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) { /* /  */
+	  UL;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) { /* //  */
+	  UL;
+	  DUMP;
+	  LR;
+	  DUMP;
+	  continue;
+	}
+      }
+      
+
+      if ((d00 <= 0) && (d10 <= 0)) { 
+	if ((d01 > 0) && (d11 <= 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 > 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 > 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+      if ((d00 > 0) && (d10 > 0)) { 
+	if ((d01 <= 0) && (d11 > 0)) {
+	  LR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 > 0) && (d11 <= 0)) {
+	  UR;
+	  DUMP;
+	  continue;
+	}
+	if ((d01 <= 0) && (d11 <= 0)) {
+	  VT;
+	  DUMP;
+	  continue;
+	}
+      }
+
+    }
+    /* skip left-hand edge */
+    v00++; v01++; v10++; v11++;
+    d01 = (level - *v00);
+    d11 = (level - *v10);
+  }
+
+  /****** bottom line *******/
+  v00 = matrix;  
+  v01 = matrix + 1;  
+  y = 0;
+  dx = 0;
+  dy = -0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /********** top line *******/
+  v00 = matrix + Nx*(Ny - 1);  
+  v01 = v00 + 1;
+  y = Ny - 1;
+  dx = 0;
+  dy = 0.5;
+  for (i = 1; i < Nx; i++, v00++, v01++) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      x = i + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** left line *********/
+  v00 = matrix; 
+  v01 = matrix + Nx;
+  x = 0;
+  dx = -0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+
+  /******** right line *********/
+  v00 = matrix + Nx - 1; 
+  v01 = v00 + Nx;
+  x = Nx - 1;
+  dx = 0.5;
+  dy = 0;
+  for (j = 1; j < Ny; j++, v00+=Nx, v01+=Nx) { /* do the edges */
+    if (((*v00 > level) && (*v01 <= level)) || ((*v00 <= level) && (*v01 > level))) {
+      y = j + (level - *v01)/(*v01 - *v00);
+      DUMP;
+      continue;
+    }
+  }
+  
+  KiiLoadOverlay (Ximage, overlay, Noverlay, argv[2]);
+  free (overlay);
+
+  if (Npix != 1) free (matrix);
+  gprint (GP_ERR, "\n");
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgauss.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgauss.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgauss.c	(revision 17422)
@@ -0,0 +1,106 @@
+# include "data.h"
+
+/* local private functions */
+float fgaussOD (float, float *, int, float *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vgauss (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  float par[4], *v1, *v2, *dy, **covar;
+  float chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vgauss <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  REALLOCATE (ovec[0].elements, float, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  Npar = 4;
+
+  v1 = svec[0].elements;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) {
+      *v2 = (*v1 == 0.0) ? 0.0 : 1.0 / (*v1 * *v1);
+  } 
+  
+  ochisq = mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 20) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fgaussOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements[i] = fgaussOD (xvec[0].elements[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, sigma, I, back */
+float fgaussOD (float x, float *par, int Npar, float *dpar) {
+
+  float z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  
+  return (f);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgrid.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgrid.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vgrid.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+int vgrid (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb;
+  float Xmin, Xmax, dX, Ymin, Ymax, dY;
+  float *buf, *val, *x, *y, *z;
+  int *Nval;
+  Buffer *bf;
+  Vector *vx, *vy, *vz;
+
+  if (argc != 11) {
+    gprint (GP_ERR, "USAGE: vgrid x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+    return (FALSE);
+  }
+  
+  if ((vx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vz = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((bf = SelectBuffer (argv[4], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (vx[0].Nelements != vy[0].Nelements) return (FALSE);
+  if (vx[0].Nelements != vz[0].Nelements) return (FALSE);
+
+  Xmin = atof (argv[5]);
+  Xmax = atof (argv[6]);
+  dX   = atof (argv[7]);
+
+  Ymin = atof (argv[8]);
+  Ymax = atof (argv[9]);
+  dY   = atof (argv[10]);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 1;
+  
+  gfits_free_matrix (&bf[0].matrix);
+  gfits_free_header (&bf[0].header);
+  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (bf[0].file, "(empty)");
+
+  ALLOCATE (val, float, Nx*Ny);
+  bzero (val, Nx*Ny*sizeof(float));
+  ALLOCATE (Nval, int, Nx*Ny);
+  bzero (Nval, Nx*Ny*sizeof(int));
+
+  x = vx[0].elements;
+  y = vy[0].elements;
+  z = vz[0].elements;
+  for (i = 0; i < vx[0].Nelements; i++, x++, y++, z++) {
+    Xb = (*x - Xmin) / dX;
+    Yb = (*y - Ymin) / dY;
+    if (Xb >= Nx) continue;
+    if (Yb >= Ny) continue;
+    val[Xb + Yb*Nx] = *z;
+    Nval[Xb + Yb*Nx]++;
+  }
+
+  buf = (float *) bf[0].matrix.buffer;
+  for (i = 0; i < Nx*Ny; i++) {
+    if (Nval[i] == 0) {
+      buf[i] = 0;
+      continue;
+    }
+    buf[i] = val[i] / Nval[i];
+  }
+
+  free (val);
+  free (Nval);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vload.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vload.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vload.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "data.h"
+
+int vload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double dx, dy, size;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  size = 1.0;
+  if ((N = get_argument (argc, argv, "-size"))) {
+    remove_argument (N, &argc, argv);
+    size = fabs(atof (argv[N]));
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vload (overlay) (xvec) (yvec) [-n]\n");
+    return (FALSE);
+  }
+  
+  if (type == KII_OVERLAY_CIRCLE) {
+    dx = dy = size / 2;
+  } else {
+    dx = dy = size;
+  }    
+
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = 0; i < Noverlay; i++) {
+    overlay[i].type = type;
+    overlay[i].text = NULL;
+    overlay[i].x = vecx[0].elements[i]+0.5;
+    overlay[i].y = vecy[0].elements[i]+0.5;
+    overlay[i].dx = dx;
+    overlay[i].dy = dy;
+    overlay[i].angle = 0.0;
+  }
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vmaxwell.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vmaxwell.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vmaxwell.c	(revision 17422)
@@ -0,0 +1,108 @@
+# include "data.h"
+
+/* local private functions */
+float fmaxwellOD (float, float *, int, float *);
+
+# define GET_VAR(V,A) \
+  c = get_variable (A); \
+  if (c == NULL) { \
+    gprint (GP_ERR, "missing fit parameter A\n"); \
+    return (FALSE); \
+  } \
+  V = atof (c); \
+  free (c);
+
+int vmaxwell (int argc, char **argv) {
+
+  int i, N, Npts, Npar, Quiet;
+  float par[5], *v1, *v2, *dy, **covar;
+  float chisq, ochisq, dchisq;
+  Vector *xvec, *yvec, *svec, *ovec;
+  char *c, name[16];
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: vmaxwell <x> <y> <dy> (out)\n");
+    gprint (GP_ERR, " uses guesses: C0 (mean), C1 (sigma), C2 (norm), C3 (sky), C4 (ref)\n");
+    return (FALSE);
+  }
+  
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((svec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Npts = xvec[0].Nelements;
+  ALLOCATE (dy, float, Npts);
+  REALLOCATE (ovec[0].elements, float, Npts);
+
+  GET_VAR (par[0], "C0");
+  GET_VAR (par[1], "C1");
+  GET_VAR (par[2], "C2");
+  GET_VAR (par[3], "C3");
+  GET_VAR (par[4], "C4");
+  Npar = 5;
+  /* careful of variable renomalization */
+
+  v1 = svec[0].elements;
+  v2 = dy;
+  for (i = 0; i < Npts; i++, v1++, v2++) *v2 = 1.0 / (*v1 * *v1);
+  
+  ochisq = mrqinit (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+  dchisq = ochisq + 2*Npts;
+
+  for (i = 0; (i < 20) && ((dchisq > 0.1*(Npts - Npar)) || (dchisq <= 0.0)); i++) {
+    chisq = mrqmin (xvec[0].elements, yvec[0].elements, dy, Npts, par, Npar, fmaxwellOD, !Quiet);
+    dchisq = ochisq - chisq;
+    ochisq = chisq;
+    if (!Quiet) gprint (GP_ERR, "dchisq: %f, Ndof: %d\n", dchisq, Npts - Npar);
+  }  
+  if (!Quiet) gprint (GP_ERR, "%d iterations\n", i); 
+
+  for (i = 0; i < Npts; i++) {
+    ovec[0].elements[i] = fmaxwellOD (xvec[0].elements[i], par, Npar, dy);
+  }
+  ovec[0].Nelements = Npts;
+  /* set output *before* variable renomalization */
+
+  covar = mrqcovar (Npar);
+  for (i = 0; i < Npar; i++) {
+    sprintf (name, "C%d", i);
+    set_variable (name, par[i]);
+    if (!Quiet) gprint (GP_ERR, "%d  %f  %f\n", i, par[i], sqrt(covar[i][i]));
+    sprintf (name, "dC%d", i);
+    set_variable (name, sqrt(covar[i][i]));
+  }
+
+  free (dy);
+  mrqfree (Npar);
+  return (TRUE);
+}
+
+/* pars: x_o, -0.5/sigma^2, I, back, ref */
+float fmaxwellOD (float x, float *par, int Npar, float *dpar) {
+
+  float z, r, f;
+
+  z = (x - par[0])/par[1];
+  r = SQ(x - par[4])*exp (-0.5*SQ(z));
+  f = par[2]*r + par[3];
+
+  dpar[0] = par[2]*r*z/par[1];
+  dpar[1] = par[2]*r*z*z/par[1];
+  dpar[2] = r;
+  dpar[3] = 1;
+  dpar[4] = -par[2]*(x - par[4])*exp(-0.5*SQ(z));
+  
+  return (f);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vpop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vpop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vpop.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "data.h"
+
+int vpop (int argc, char **argv) {
+
+  int Npix;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vpop (vector)\n");
+    gprint (GP_ERR, "  remove first element of vector\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 1) return (TRUE);
+
+  if (Npix > 1) {
+    memmove (&vec[0].elements[0], &vec[0].elements[1], Npix*sizeof(float));
+  }
+  vec[0].Nelements = Npix - 1;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vroll.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vroll.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vroll.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "data.h"
+
+int vroll (int argc, char **argv) {
+
+  int Npix;
+  float first;
+  Vector *vec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vroll (vector)\n");
+    gprint (GP_ERR, "  roll vector elements (first goes to end)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  Npix = vec[0].Nelements;
+  if (Npix < 2) return (TRUE);
+
+  first = vec[0].elements[0];
+  memmove (&vec[0].elements[0], &vec[0].elements[1], Npix*sizeof(float));
+  vec[0].elements[Npix-1] = first;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vsmooth.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vsmooth.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vsmooth.c	(revision 17422)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int vsmooth (int argc, char **argv) {
+  
+  int i, n, N, Nx, Ns, Ngauss;
+  float *vi, *vo, *gauss, *gaussnorm;
+  float g, s, sigma, Nsigma;
+  Vector *in;
+
+  Nsigma = 3;
+  if ((N = get_argument (argc, argv, "-Nsigma"))) {
+    remove_argument (N, &argc, argv);
+    Nsigma = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: vsmooth (input) sigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  sigma = atof (argv[2]);
+  Nx = in[0].Nelements;
+  vi = in[0].elements;
+
+  /* build a 1D gaussian */
+  Ns = (int) (Nsigma*sigma + 0.5);
+  Ngauss = 2*Ns + 1;
+  ALLOCATE (gaussnorm, float, Ngauss);
+  gauss = &gaussnorm[Ns];
+  for (i = -Ns; i < Ns + 1; i++) {
+    gauss[i] = exp ((i*i)/(-2*sigma*sigma));
+  }
+
+  ALLOCATE (vo, float, Nx);
+
+  for (i = 0; i < Nx; i++) {
+    g = s = 0;
+    for (n = -Ns; n < Ns + 1; n++) {
+      if (i+n < 0) continue;
+      if (i+n >= Nx) continue;
+      s += gauss[n]*vi[i+n];
+      g += gauss[n];
+    }
+    vo[i] = s / g;
+  }
+
+  free (in[0].elements);
+  free (gaussnorm);
+
+  in[0].elements = vo;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vstat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vstat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vstat.c	(revision 17422)
@@ -0,0 +1,115 @@
+# include "data.h"
+
+int vstat (int argc, char **argv) {
+  
+  int i, N;
+  double max, min, sum, var, dvar, mean, stdev;
+  float *X, IgnoreValue;
+  int Ignore, Quiet;
+
+  int *Nval, bin, Nmode, Nmed;
+  double dx, mode, median;
+  Vector *vec;
+
+  IgnoreValue = 0;
+  Ignore = FALSE;
+  if ((N = get_argument (argc, argv, "-ignore"))) {
+    Ignore = TRUE;
+    remove_argument (N, &argc, argv);
+    IgnoreValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Quiet = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    Quiet = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vstat (vector)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* we need two passes, one for max, min, mean, sum, one for median, stdev, etc */
+
+  /* calculate max, min, mean, sum, npix */
+  X = vec[0].elements;
+  max = -HUGE_VAL;
+  min = HUGE_VAL;
+  sum = N = 0;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    max = MAX (*X, max);
+    min = MIN (*X, min);
+    sum += *X;
+    N++;
+  }      
+  mean = sum / N;
+
+  /* calculate median and mode with resolution of (max - min) / 1000 */ 
+  dx = (max - min) / 1000;
+  if (dx == 0) {
+    median = mode = min;
+    stdev = 0.0;
+    goto skip;
+  }
+
+  ALLOCATE (Nval, int, 1002);
+  bzero (Nval, 1000*sizeof(int));
+  X = vec[0].elements;
+  var = 0;
+  for (i = 0; i < vec[0].Nelements; i++, X++) {
+    if (!finite (*X)) continue;
+    if (Ignore && (*X == IgnoreValue)) continue;
+    bin = MAX (0, MIN (1000, (*X - min) / dx));
+    Nval[bin] ++;
+    dvar = (*X - mean);
+    var += dvar*dvar;
+  }      
+  stdev = sqrt (var / N);
+
+  Nmode = 0;
+  mode = Nval[Nmode];
+  median = 0;
+  Nmed = -1;
+  for (i = 0; i < 1001; i++) {
+    if (Nmed == -1) {
+      median += Nval[i];
+      if (median >= N / 2.0) {
+	Nmed = i;
+	median = i * dx + min;
+      }
+    }
+    if (mode < Nval[i]) {
+      Nmode = i;
+      mode = Nval[Nmode];
+    }
+  }
+  mode = Nmode * dx + min;
+  free (Nval);
+
+skip:
+  if (!Quiet) {
+    gprint (GP_ERR, "mean: %g, stdev: %g, min: %g, max: %g, median: %g, mode: %g, Npts: %d\n", 
+	     mean, stdev, min, max, median, mode, N);
+  }
+
+  set_variable ("MIN",      min);
+  set_variable ("MAX",      max);
+  set_variable ("MEDIAN",   median);
+  set_variable ("MEAN",     mean);
+  set_variable ("MODE",     mode);
+  set_variable ("TOTAL",    sum);
+  set_int_variable ("NPIX", N);
+  set_variable ("SIGMA",    stdev);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vzload.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vzload.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/vzload.c	(revision 17422)
@@ -0,0 +1,83 @@
+# include "data.h"
+
+int vzload (int argc, char **argv) {
+  
+  int i, N, Noverlay;
+  int kapa, type;
+  char *name;
+  double size, min, range, MAX_OUTPUT_SIZE;
+  KiiOverlay *overlay;
+  Vector *vecx, *vecy, *vecz;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  MAX_OUTPUT_SIZE = 10.0;
+  if ((N = get_argument (argc, argv, "-max"))) {
+    remove_argument (N, &argc, argv);
+    MAX_OUTPUT_SIZE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  type = KII_OVERLAY_BOX;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    type = KiiOverlayTypeByName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!type) {
+      gprint (GP_ERR, "unknown Kii point type %s\n", argv[N]);
+      return (FALSE);
+    }
+  }
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: vzload (overlay) (xvec) (yvec) (zvec) (min) (max) [-n] [-type]\n");
+    return (FALSE);
+  }
+  
+  if ((vecx = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector (argv[4], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) {
+    gprint (GP_ERR, "mismatched vector lengths\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[5]);
+  range = (atof(argv[6]) - min) / MAX_OUTPUT_SIZE;
+  // renormalize to the max output size (output range is 0.1 - MAX_OUTPUT_SIZE)
+
+  Noverlay = vecx[0].Nelements;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+
+  for (i = N = 0; i < Noverlay; i++) {
+    size = MIN (MAX_OUTPUT_SIZE, (vecz[0].elements[i] - min) / range);
+    if (size < 0.1) continue;
+
+    overlay[N].type = type;
+    overlay[N].text = NULL;
+    overlay[N].x = vecx[0].elements[i]+0.5;
+    overlay[N].y = vecy[0].elements[i]+0.5;
+    overlay[N].angle = 0.0;
+
+    if (type == KII_OVERLAY_CIRCLE) {
+      overlay[N].dx = size / 2.0;
+      overlay[N].dy = size / 2.0;
+    } else {
+      overlay[N].dx = size;
+      overlay[N].dy = size;
+    }    
+    N++;
+  }
+
+  KiiLoadOverlay (kapa, overlay, N, argv[1]);
+  free (overlay);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/wd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/wd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/wd.c	(revision 17422)
@@ -0,0 +1,179 @@
+# include "data.h"
+
+int wd (int argc, char **argv) {
+  
+  int N, Extend;
+  int newUnsign, newBitpix, newScale, newZero;
+  int outUnsign, outBitpix;
+  double outScale, outZero;
+  Header temp_header;
+  Matrix temp_matrix;
+  Buffer *buf;
+
+  /* XXX I must have dropped the old 'newplane' option */
+  Extend  = FALSE;
+  if ((N = get_argument (argc, argv, "-extend"))) {
+    remove_argument (N, &argc, argv);
+    Extend  = TRUE;
+  }
+
+  outZero = 0;
+  newZero = FALSE;
+  if ((N = get_argument (argc, argv, "-bzero"))) {
+    remove_argument (N, &argc, argv);
+    outZero  = atof(argv[N]);
+    newZero  = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outScale = 1;
+  newScale = FALSE;
+  if ((N = get_argument (argc, argv, "-bscale"))) {
+    remove_argument (N, &argc, argv);
+    outScale = atof(argv[N]);
+    newScale = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outBitpix = 16;
+  newBitpix = FALSE;
+  if ((N = get_argument (argc, argv, "-bitpix"))) {
+    remove_argument (N, &argc, argv);
+    outBitpix = atof(argv[N]);
+    newBitpix = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  outUnsign = FALSE;
+  newUnsign = FALSE;
+  if ((N = get_argument (argc, argv, "-unsign"))) {
+    remove_argument (N, &argc, argv);
+    outUnsign = -1;
+    if (!strcasecmp (argv[N], "t") || !strcasecmp (argv[N], "true")) outUnsign = TRUE;
+    if (!strcasecmp (argv[N], "f") || !strcasecmp (argv[N], "false")) outUnsign = FALSE;
+    if (outUnsign == -1) {
+      gprint (GP_ERR, "-unsign options: t, f, true, false\n");
+      return (FALSE);
+    }
+    newUnsign = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: wd <buffer> <filename> [-bitpix N] [-bscale X] [-bzero X] [-extend] [-newplane]\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  if (!newBitpix) outBitpix = buf[0].bitpix;
+  if (!newScale) outScale = buf[0].bscale;
+  if (!newZero) outZero = buf[0].bzero;
+  if (!newUnsign) outUnsign = buf[0].unsign;
+
+  /* Convert the buffer from (float) to correct format */
+  /* save the (float) version, write out a temporary buffer */
+  temp_matrix = buf[0].matrix;
+  ALLOCATE (temp_matrix.buffer, char, MAX(1, temp_matrix.size));
+  memcpy (temp_matrix.buffer, buf[0].matrix.buffer, temp_matrix.size);
+  temp_header = buf[0].header;
+  ALLOCATE (temp_header.buffer, char, MAX(1, temp_header.size));
+  memcpy (temp_header.buffer, buf[0].header.buffer, temp_header.size);
+
+  if (temp_header.Naxes) gfits_convert_format (&temp_header, &temp_matrix, outBitpix, outScale, outZero, outUnsign);
+
+  if (Extend) {
+    Header Xhead;
+    FILE *f;
+    int status, Nextend;
+
+    /* assume failure means non-existent file */
+    if (!gfits_read_header (argv[2], &Xhead)) {
+
+      gfits_init_header (&Xhead);
+      Xhead.bitpix = 16;
+      Xhead.extend = TRUE;
+      gfits_create_header (&Xhead);
+
+      gfits_modify (&Xhead, "NEXTEND", "%d", 1, 0);
+      f = fopen (argv[2], "w");
+      fclose (f);
+    }
+
+    gfits_modify (&Xhead, "EXTEND", "%t", 1, TRUE);
+
+    Nextend = 0;
+    gfits_scan (&Xhead, "NEXTEND", "%d", 1, &Nextend);
+    Nextend ++;
+    gfits_modify (&Xhead, "NEXTEND", "%d", 1, Nextend);
+
+    /* write the main header to the start of the file */
+    f = fopen (argv[2], "r+");
+    if (f == NULL) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* position to begining of file to write header */
+    fseek (f, 0, SEEK_SET);
+    status = fwrite (Xhead.buffer, 1, Xhead.size, f);
+    if (status != Xhead.size) {
+      gprint (GP_ERR, "ERROR: failed writing data to image header\n");
+      status = FALSE;
+      goto done1;
+    }
+    
+    /* fix up header */
+    {
+      static char simple[] = "XTENSION= 'IMAGE  '            / Image extension";
+      int Ns, No;
+      Ns = strlen (simple);
+      No = 80 - Ns;
+      strncpy (temp_header.buffer, simple, Ns);
+      memset (&temp_header.buffer[Ns], ' ', No);
+    }
+
+    /* position to end of file to write new extend */
+    fseek (f, 0, SEEK_END);
+    status = fwrite (temp_header.buffer, 1, temp_header.size, f);
+    fclose (f);
+    if (status != temp_header.size) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    /* write the matrix buffer (automatically goes to end of file */
+    if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+      gprint (GP_ERR, "failed to write file\n");
+      status = FALSE;
+      goto done1;
+    }
+    status = TRUE;
+  done1:
+    gfits_free_header (&Xhead);
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (status);
+  }
+  
+  /* the actual write-to-disk goes here */
+  if (!gfits_write_header (argv[2], &temp_header)) {
+    gprint (GP_ERR, "failed to write header\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+  
+  if (!gfits_write_matrix (argv[2], &temp_matrix)) {
+    gprint (GP_ERR, "failed to write matrix\n");
+    gfits_free_header (&temp_header);
+    gfits_free_matrix (&temp_matrix);
+    return (FALSE);
+  }
+
+  gfits_free_header (&temp_header);
+  gfits_free_matrix (&temp_matrix);
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 17422)
@@ -0,0 +1,151 @@
+# include "data.h"
+
+int write_vectors (int argc, char **argv) {
+  
+  int append;
+  int i, j, Nvec, Ne, N;
+  FILE *f;
+  char **fmtlist, *fmttype;
+  char *p0, *p1, *p2, *format;
+  Vector **vec;
+
+  /* look for format option */
+  format = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-f"))) {
+    remove_argument (N, &argc, argv);
+    format = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  append = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    remove_argument (N, &argc, argv);
+    append = TRUE;
+  }
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: write [-append] [-f \"format\"] file vector vector ...\n");
+    return (FALSE);
+  }
+
+  /* open file for outuput */
+  if (append) {
+      f = fopen (argv[1], "a");
+  } else {
+      f = fopen (argv[1], "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write\n");
+    return (FALSE);
+  }
+
+  /* find number of output vectors */
+  Nvec = (argc - 2);
+  if (Nvec < 1) {
+      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+      fclose (f);
+      return (FALSE);
+  }
+  ALLOCATE (vec, Vector *, Nvec);
+
+  /* select/check vectors from list */
+  for (i = 0; i < Nvec; i++) {
+    if ((vec[i] = SelectVector (argv[i + 2], OLDVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "unknown vector %s\n", argv[i+2]);
+      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+      free (vec);
+      fclose (f);
+      return (FALSE);    
+    }
+  }
+  
+  /* select vector lengths */
+  Ne = vec[0][0].Nelements;
+  for (i = 0; i < Nvec; i++) {
+    if (vec[0][0].Nelements != Ne) {
+      gprint (GP_ERR, "error: vectors must all be the same size\n");
+      free (vec);
+      fclose (f);
+      return (FALSE);    
+    }
+  }
+
+  /* default output format */
+  if (format == (char *) NULL) {
+    for (i = 0; i < vec[0][0].Nelements; i++) {
+      for (j = 0; j < Nvec; j++) {
+	fprintf (f, "%.10g ", vec[j][0].elements[i]);
+      }
+      fprintf (f, "\n");
+    } 
+    fclose (f);
+    free (vec);
+    return (TRUE);
+  }
+
+  /* construct an array of format strings */
+  ALLOCATE (fmttype, char, Nvec);
+  ALLOCATE (fmtlist, char *, Nvec);
+  for (i = 0; i < Nvec; i++) {
+    ALLOCATE (fmtlist[i], char, 1024);
+    bzero (fmtlist[i], 1024);
+  }
+
+  p0 = format;
+  for (j = 0; j < Nvec; j++) {
+    /* find this format character */
+    p1 = strchr (p0, '%');
+    if (p1 == (char *) NULL) {
+      gprint (GP_ERR, "mismatch between format and values\n");
+      free (fmttype);
+      for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+      free (fmtlist);
+      free (format);
+      fclose (f);
+      return (FALSE);
+    }
+    
+    /* identify type (%NNNNd %NNNNf) */
+    for (p2 = p1 + 1; (*p2 == '.') || (*p2 == '-') || (*p2 == '+') || (*p2 == ' ') || isdigit(*p2); p2++);
+    strncpy (fmtlist[j], p0, p2 - p0 + 1);
+    switch (*p2) {
+      case 'e':
+      case 'f':
+	fmttype[j] = 'f';
+	break;
+      case 'd':
+      case 'c':
+      case 'x':
+	fmttype[j] = 'd';
+	break;
+      default:
+	gprint (GP_ERR, "syntax error in format (only e,f,d,c,x allowed)\n");
+	return (FALSE);
+    }
+    p0 = p2 + 1;
+  }
+  strcat (fmtlist[Nvec-1], p0);
+  
+  for (i = 0; i < vec[0][0].Nelements; i++) {
+    for (j = 0; j < Nvec; j++) {
+      if (fmttype[j] == 'd') {
+	fprintf (f, fmtlist[j], (int)(vec[j][0].elements[i]));
+      } 
+      if (fmttype[j] == 'f') {
+	fprintf (f, fmtlist[j], (float)(vec[j][0].elements[i]));
+      } 
+    }
+    fprintf (f, "\n");
+  }
+  fclose (f);
+
+  free (fmttype);
+  for (i = 0; i < Nvec; i++) free (fmtlist[i]);
+  free (fmtlist);
+  free (format);
+  
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zap.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zap.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zap.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "data.h"
+
+int zap (int argc, char **argv) {
+
+  int i, j, N;
+  int sx, sy, nx, ny;
+  float *V, value;
+  Buffer *buf;
+
+  value = 0;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    value  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: zap <buffer> sx sy nx ny [-v value]\n");
+    return (FALSE);
+  }
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  sx = atof (argv[2]);
+  sy = atof (argv[3]);
+  nx = atof (argv[4]);
+  ny = atof (argv[5]);
+
+  if (sx < 0) goto error;
+  if (sy < 0) goto error;
+  if (sx + nx > buf[0].matrix.Naxis[0]) goto error;
+  if (sy + ny > buf[0].matrix.Naxis[1]) goto error;
+
+  for (j = sy; j < sy + ny; j++) {
+    V = (float *)(buf[0].matrix.buffer) + j*buf[0].matrix.Naxis[0] + sx; 
+    for (i = 0; i < nx; i++, V++) *V = value;
+  }
+  return (TRUE);
+
+ error:
+  gprint (GP_ERR, "region out of range\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zplot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zplot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/cmd.data/zplot.c	(revision 17422)
@@ -0,0 +1,55 @@
+# include "data.h"
+
+int zplot (int argc, char **argv) {
+  
+  int i, kapa, Npts;
+  float *in, *out;
+  double min, range;
+  Graphdata graphmode;
+  Vector *xvec, *yvec, *zvec, Zvec;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: zplot <x> <y> <z> min max\n");
+    return (FALSE);
+  }
+
+  min = atof(argv[4]);
+  range = atof(argv[5]) - min;
+
+  /* find vectors */
+  if ((xvec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[2]);
+    return (FALSE);
+  }
+  if (xvec[0].Nelements != zvec[0].Nelements) {
+    gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  Zvec.Nelements = zvec[0].Nelements;
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+ 
+  in = zvec[0].elements;
+  out = Zvec.elements;
+  for (i = 0; i < Zvec.Nelements; i++, in++, out++) {
+    *out = MIN (1.0, MAX (0.01, (*in - min) / range));
+  }
+
+  /* point size determined by Zvec */
+  graphmode.style = 2; /* plot points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+  Npts = xvec[0].Nelements;
+  PlotVectorTriplet (kapa, Npts, xvec[0].elements, yvec[0].elements, Zvec.elements, &graphmode);
+
+  free (Zvec.elements);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Analysis.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Analysis.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Analysis.c	(revision 17422)
@@ -0,0 +1,112 @@
+# include "dimm.h"
+
+/* should this all be wrapped within an opihi implementation? */
+
+int subtractImage (Image *a, Image *b) {
+
+  if (a[0].Nx != b[0].Nx) return (FALSE);
+  if (a[0].Ny != b[0].Ny) return (FALSE);
+
+  Npix = a[0].Nx*a[0].Ny;
+  ap = (float *) a[0].buffer;
+  bp = (float *) b[0].buffer;
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    *ap -= *bp;
+  }
+  return (FALSE);
+}
+
+void statsImage (Image *image, Stats *stats) {
+
+  val = (float *)image[0].buffer;
+  max = min = val[0];
+  Npix = image[0].Nx*image[0].Ny;
+  for (i = 0; i < Npix; i++, val++) {
+    N1 += *val;
+    N2 += (*val)*(*val);
+    max = MAX (max, *val);
+    min = MIN (min, *val);
+  }
+  stats[0].mean  = N1 / Npix;
+  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
+  stats[0].min = min;
+  stats[0].max = max;
+
+  stats[0].median = stats[0].mean;
+  range = MAX (0.5, 0xffff / (max - min));
+  if (range == 0) return;
+
+  ALLOCATE (hist, int, 0x10000);
+  bzero (hist, 0x10000*sizeof(int));
+
+  val = (float *)image[0].buffer;
+  for (i = 0; i < Npix; i++) {
+    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
+    hist[bin] ++;
+  }
+
+  Nhist = 0;
+  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+    Nhist += hist[i];
+  stats[0].median = i / range + min;
+  free (hist);
+
+  return;
+}
+
+# if (0)
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
+
+  /* binarize @ threshold */
+
+  binimage = createImage (image[0].Nx, image[0].Ny);
+
+  Npix = image[0].Nx*image[0].Ny;
+  ap = image[0].buffer;
+  bp = binimage[0].buffer;
+  bzero (bp, Npix*sizeof (short));
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) * bp = 1;
+  }
+
+  clearpix ();
+
+  for (i = 0; i < Ny; i++) {
+    for (j = 0; j < Nx; j++) {
+      pix = j + i*Ny;
+      if (binimage.buffer[pix]) {
+	addpix (pix);
+	binimage.buffer[pix] = 0;
+	/* continue in row to end */
+	for (k = j + 1; k < Nx; k++) {
+	  pix = k + i*Nx;
+	  if (!binimage.buffer[pix]) { 
+	  }
+	}
+
+# endif
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+....x...xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Camera.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Camera.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Camera.c	(revision 17422)
@@ -0,0 +1,223 @@
+# include "dimm.h"
+
+static struct sbig_init info;
+
+int InitCamera (int port) {
+  
+  int i, state;
+
+  for (i = 0; i < 10; i++) {
+    state = sbig_init (port, SBIG_IMAGING_CCD, &info);
+    if (state == -6) state = 0;
+    if (state ==  0) {
+      DumpCameraInfo ();
+      return (TRUE);
+    }
+    gprint (GP_ERR, "retry...\n");
+  }
+  
+  gprint (GP_ERR, "failed to init sbig camera on %d\n", port);
+  gprint (GP_ERR, "%s\n", sbig_show_error (state));
+  return (FALSE);
+}
+
+void DumpCameraInfo () {
+
+      gprint (GP_ERR, "opened sbig camera:\n");
+      gprint (GP_ERR, "linux_version: %f\n",      info.linux_version);
+      gprint (GP_ERR, "nmbr_bad_columns: %d\n",   info.nmbr_bad_columns);
+      gprint (GP_ERR, "imaging_abg_type: %d\n",   info.imaging_abg_type);
+      gprint (GP_ERR, "serial_number: %s\n",      info.serial_number);
+      gprint (GP_ERR, "firmware_version: %d\n",   info.firmware_version);
+      gprint (GP_ERR, "camera_name: %s\n",        info.camera_name);
+      gprint (GP_ERR, "nmbr_readout_modes: %d\n", info.camera_info[0].nmbr_readout_modes);
+      gprint (GP_ERR, "mode: %d\n",               info.camera_info[0].readout_mode[0].mode);
+      gprint (GP_ERR, "width: %d\n",              info.camera_info[0].readout_mode[0].width);
+      gprint (GP_ERR, "height: %d\n",             info.camera_info[0].readout_mode[0].height);
+      gprint (GP_ERR, "gain: %d\n",               info.camera_info[0].readout_mode[0].gain);
+      gprint (GP_ERR, "pixel_width: %d\n",        info.camera_info[0].readout_mode[0].pixel_width);
+      gprint (GP_ERR, "pixel_height: %d\n",       info.camera_info[0].readout_mode[0].pixel_height);
+
+      gprint (GP_ERR, "ST5_AD_size: %d\n", info.ST5_AD_size);
+      gprint (GP_ERR, "ST5_filter_type: %d\n", info.ST5_filter_type);
+}      
+
+void CameraFullSize (int *x, int *y) {
+  *x = info.camera_info[0].readout_mode[0].width;
+  *y = info.camera_info[0].readout_mode[0].height;
+}
+
+int SetTemperature (double temp) {
+
+  int state;
+  struct sbig_cool cool;
+
+  if (temp < -50) return (FALSE);
+  if (temp > +20) return (FALSE);
+
+  cool.regulation = SBIG_TEMP_REGULATION_ON;
+  cool.temperature = (int) (10.0*temp + 0.5);
+  cool.direct_drive = 0;
+
+  state = sbig_set_cooling (&cool);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+double GetTemperature () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (-200.0);
+  }
+  temp = (status.ccd_temperature - 0.5) / 10.0;
+  return (temp);
+}
+
+int DumpCameraStatus () {
+
+  int state;
+  double temp;
+  struct sbig_status status;
+
+  state = sbig_get_status (&status);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "imaging_ccd_status: %d\n", status.imaging_ccd_status);
+  gprint (GP_ERR, "tracking_ccd_status: %d\n", status.tracking_ccd_status);
+  gprint (GP_ERR, "fan_on: %d\n", status.fan_on);
+  gprint (GP_ERR, "shutter_state: %d\n", status.shutter_state);
+  gprint (GP_ERR, "led_state: %d\n", status.led_state);
+  gprint (GP_ERR, "shutter_edge: %d\n", status.shutter_edge);
+  gprint (GP_ERR, "plus_x_relay: %d\n", status.plus_x_relay);
+  gprint (GP_ERR, "minus_x_relay: %d\n", status.minus_x_relay);
+  gprint (GP_ERR, "plus_y_relay: %d\n", status.plus_y_relay);
+  gprint (GP_ERR, "minus_y_relay: %d\n", status.minus_y_relay);
+  gprint (GP_ERR, "pulse_active: %d\n", status.pulse_active);
+  gprint (GP_ERR, "temperature_regulation: %d\n", status.temperature_regulation);
+  gprint (GP_ERR, "temperature_setpoint: %d\n", status.temperature_setpoint);
+  gprint (GP_ERR, "cooling_power: %d\n", status.cooling_power);
+  gprint (GP_ERR, "air_temperature: %d\n", status.air_temperature);
+  gprint (GP_ERR, "ccd_temperature: %d\n", status.ccd_temperature);
+
+  return (TRUE);
+}
+
+/* block until exposure is complete */
+int Exposure (double exptime) {
+
+  int i, state;
+  struct sbig_expose expose;
+  struct sbig_status status;
+
+  expose.ccd = SBIG_IMAGING_CCD;
+  expose.exposure_time = (int)(100.0*exptime);
+  expose.abg_state = SBIG_ABG_OFF;
+  expose.shutter = SBIG_EXPOSE_SHUTTER_NORMAL;  /* shuttermode = ? */
+
+  /* drop this ? */
+  /* usleep ((int)(exptime*1000000)); */
+  state = sbig_expose (&expose);
+  if (state < 0) {
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  
+  for (i = 0; i < expose.exposure_time + 10; ++i) {
+    state = sbig_get_status (&status);
+    /* gprint (GP_ERR, "%d\n", state); */
+    /* gprint (GP_ERR, "%d  %d\n", status.imaging_ccd_status, status.shutter_state); */
+    /*    if (state == 0) return (TRUE); */
+    if (status.imaging_ccd_status == -SBIG_NO_EXPOSURE_IN_PROGRESS) return (TRUE);
+    if (status.imaging_ccd_status == -SBIG_EXPOSURE_IN_PROGRESS) {
+      usleep (10000);
+      continue;
+    }
+    gprint (GP_ERR, "exposure error\n");
+    gprint (GP_ERR, "%s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  gprint (GP_ERR, "exposure timeout\n");
+  return (FALSE);
+}
+
+int   readout_abort;
+float readout_percent;
+static int readout_callback (float percent) {
+  /* return 1 to continue, 0 to abort */
+  if (((int)(percent) % 10) == 0) { gprint (GP_ERR, "."); }
+  readout_percent = percent;
+  if (readout_abort) return 0;
+  return 1;
+}
+
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer) {
+
+  int state, Nbytes;
+  static struct sbig_readout readout;
+
+  readout.x = x;
+  readout.y = y;
+  readout.width  = dx;
+  readout.height = dy;
+
+  Nbytes = readout.width*readout.height*sizeof(short);
+
+  /* for bin 2x2 or 3x3, need to adjust dx, dy above */
+  readout.ccd = SBIG_IMAGING_CCD;
+  readout.binning = SBIG_BIN_1X1;
+  readout.data = buffer;
+  readout.data_size_in_bytes = Nbytes;
+  readout.callback = readout_callback;
+    
+  gprint (GP_ERR, "%d, %d : %d x %d\n", readout.x, readout.y, readout.width, readout.height);
+  sync (); 
+  readout_abort = FALSE;
+  state = sbig_readout (&readout);
+  gprint (GP_ERR, "\n");
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int OpenShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_OPEN_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int CloseShutter () {
+
+  int state;
+  struct sbig_control control;
+
+  control.shutter = SBIG_CLOSE_SHUTTER;
+  state = sbig_control (&control);
+  if (state < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error (state));
+    return (FALSE);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Image.c	(revision 17422)
@@ -0,0 +1,85 @@
+# include "dimm.h"
+
+/*** this uses an Image structure from DIMM which 
+     is different from the Image structure in DVO ***/
+
+static Image *images = (Image *) NULL;
+static int   Nimages = 0;
+
+Image *createImage (int Nx, int Ny) {
+
+  int N;
+
+  if (Nx*Ny <= 0) return ((Image *) NULL);
+
+  N = Nimages;
+  Nimages ++;
+  if (images == (Image *) NULL) {
+    ALLOCATE (images, Image, MAX (1, Nimages));
+  } else {
+    REALLOCATE (images, Image, MAX (1, Nimages));
+  }
+
+  images[N].Nx = Nx;
+  images[N].Ny = Ny;
+  images[N].Nbytes = Nx*Ny*sizeof (short);
+  ALLOCATE (images[N].buffer, char, images[N].Nbytes);
+
+  return (&images[N]);
+}
+
+int freeImage (Image *entry) {
+
+  int i, j, N;
+
+  N = -1;
+  for (i = 0; (i < Nimages) && (N == -1) ; i++) {
+    if (&images[i] == entry) N = i;
+  }
+  if (N == -1) return (FALSE);
+
+  free (images[N].buffer);
+
+  for (j = N; j < Nimages - 1; j++) {
+    images[j] = images[j+1];
+  }
+
+  Nimages --;
+  REALLOCATE (images, Image, MAX (1, Nimages));
+  return (TRUE);
+}
+
+int writeImage (char *filename, Image *image) {
+
+  Header header;
+  Matrix matrix;
+
+  gfits_init_header (&header);
+
+  header.Naxes = 2;
+  header.Naxis[0] = image[0].Nx;
+  header.Naxis[1] = image[0].Ny;
+  header.bitpix = 16;
+
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  free (matrix.buffer);
+
+  matrix.buffer = image[0].buffer;
+  
+  /* write meta-data to header */
+  gfits_print (&header, "RA", "%lf", 1, image[0].ra);
+  gfits_print (&header, "DEC", "%lf", 1, image[0].dec);
+  gfits_print (&header, "EQUINOX", "%lf", 1, 2000.0);
+
+  gfits_print (&header, "AIRMASS", "%lf", 1, image[0].airmass);
+  gfits_print (&header, "CCDTEMP", "%lf", 1, image[0].ccdtemp);
+  gfits_print (&header, "AIRTEMP", "%lf", 1, image[0].airtemp);
+  gfits_print (&header, "EXPTIME", "%lf", 1, image[0].exptime);
+
+  gfits_write_header (filename, &header);
+  gfits_write_matrix (filename, &matrix);
+  
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Makefile	(revision 17422)
@@ -0,0 +1,69 @@
+default: dimm
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dimm
+DATA    =       $(DESTDATA)/dimm
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata -lsbig
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dimm user commands and support functions #####################
+funcs = \
+$(SRC)/Camera.$(ARCH).o		\
+$(SRC)/Serial.$(ARCH).o		\
+$(SRC)/Telescope.$(ARCH).o             
+
+cmds = \
+$(SRC)/init.$(ARCH).o		  	\
+$(SRC)/dimm.$(ARCH).o		  	\
+$(SRC)/camera.$(ARCH).o	  	\
+$(SRC)/findstars.$(ARCH).o	  	\
+$(SRC)/telescope.$(ARCH).o   \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a \
+$(DESTLIB)/libsbig.a
+
+dimm: sbig $(BIN)/dimm.$(ARCH)
+$(SRC)/dimm.$(ARCH).o : $(libs)
+$(BIN)/dimm.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/dimm help modules
+
+help: cmd.basic.help cmd.data.help cmd.astro.help dimm.help
+
+modules: dimm.modules
+
+# SBIG install functions
+sbig: $(DESTINC)/sbig.h $(DESTLIB)/libsbig.a
+	@echo sbig code installed
+
+$(DESTINC)/sbig.h:	$(SRC)/sbig/sbig.h
+	cp $(SRC)/sbig/sbig.h $(DESTINC)/sbig.h
+
+$(DESTLIB)/libsbig.a:	$(SRC)/sbig/sbig.a
+	cp $(SRC)/sbig/sbig.a $(DESTLIB)/libsbig.a
+
+$(SRC)/sbig/sbig.a: $(SRC)/sbig/sbig.a.src
+	cp $(SRC)/sbig/sbig.a.src $(SRC)/sbig/sbig.a
+
+.PHONY: dimm
+
+# are these used or replaced?
+# $(SRC)/analysis.$(ARCH).o	  	\
+# $(SRC)/Analysis.$(ARCH).o            \
+# $(SRC)/Image.$(ARCH).o		\
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Serial.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Serial.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Serial.c	(revision 17422)
@@ -0,0 +1,388 @@
+# include "dimm.h"
+# include <termios.h>
+
+# define CR   0x0D
+# define LF   0x0A
+# define BEEP 0x07
+# define OPENERR    -1   /* Port could not be opened */
+# define PORTERR    -2   /* Opened port is not a serial (tty) port */
+# define BADCMDERR  -3   /* Command sent to camera is not understood */
+# define TIMEOUTERR -4   /* No response */
+ 
+# define SER_VERBOSE 0
+# define SER_DEBUG   0
+
+# ifndef SER_VERBOSE
+# define SER_VERBOSE 1           /* Be verbose? */
+# endif
+# ifndef SER_DEBUG 
+# define SER_DEBUG 1
+# endif
+
+/* Defines for Serial Port  */
+typedef struct {
+  int f;
+  int rate;
+  int parity;
+  int bits;
+  int stpbit;
+  char port[64];
+} Serial;
+
+static Serial serial = {0, 0, 0, 0, 0};
+static int SER_ECHO = 0;
+
+int SerialOpen (char *);
+
+int SerialVerbose (int mode) {
+
+  SER_ECHO = mode;
+  return (TRUE);
+
+}
+
+int SerialInit (char *port) {
+  
+  strcpy (serial.port, port);
+  serial.rate = 2400;
+  serial.rate = 9600;
+  serial.parity = 0;
+  serial.bits = 8;
+  serial.stpbit = 1;
+  
+  serial.f = SerialOpen (serial.port);
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "Error opening serial port %s - error %d.\n", serial.port, serial.f);
+    return (FALSE);
+  }
+  if (SerialBaudRate (serial.f, serial.rate))   return (FALSE);
+  if (SerialParity   (serial.f, serial.parity)) return (FALSE);
+  if (SerialDataBits (serial.f, serial.bits))   return (FALSE);
+  if (SerialStopBit  (serial.f, serial.stpbit)) return (FALSE);
+  return (TRUE);
+}
+
+/********************************** Open *********************************/
+int SerialOpen (char *port) {
+  
+  int err = 0;
+  struct termios term;
+  char prefix[100];
+  int fdesc = -1;
+  int locked;
+  struct flock lock;
+   
+  /* open serial line */
+   if (SER_VERBOSE) printf ("Opening the serial port %s for read/write.\n", port);
+   fdesc = open (port, O_RDWR);
+   if (fdesc == -1) {
+     if (SER_VERBOSE) gprint (GP_ERR, "Cannot open %s for read/write.\n", port);
+     return OPENERR;
+   }
+   /* lock serial line */
+   lock.l_type = F_WRLCK;
+   lock.l_len = 0;
+   lock.l_start = 0;
+   lock.l_whence = 0;
+   locked = fcntl (fdesc, F_SETLK, &lock);
+   if (locked == -1) {
+     gprint (GP_ERR, "can't lock serial line\n");
+     close (fdesc);
+     return OPENERR;
+   }
+
+   /* Get the serial port's attributes... */
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   /* cfmakeraw (&term); */
+   term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   term.c_oflag &= ~OPOST;
+   term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   term.c_cflag &= ~(CSIZE|PARENB);
+   term.c_cflag |= CS8;
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 2;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+
+# if (0)   
+   term.c_lflag &= ~ICANON; /* Turn OFF canonical input 
+ 	                      (so it does it character by character) */
+   term.c_cc[VMIN] = 0;     /* MIN setting...if 0, wait only for timeout */
+   term.c_cc[VTIME] = 10;    /* TIME setting...wait at most 1 sec for response 
+		              (ignored if c_cc[VMIN]>0) */
+   term.c_lflag &= ~ECHO;   /* Turn OFF echoing... */
+   term.c_iflag &= ~ICRNL;   /* Don't map CR to NL on input */
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr (fdesc, TCSAFLUSH, &term);
+# endif
+
+   return fdesc;
+}
+
+/******************************* Baud Rate *******************************/
+int SerialBaudRate (int fdesc, int rate) {
+   int err = 0;
+   struct termios term;
+
+  /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's baud rate.\n");
+   if (tcgetattr (fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+ 
+   /* Set the input and output baud rates to 'rate'... */
+   switch (rate) { 
+   case 1200:         /* Set speed to 1200 */ 
+      if (cfgetospeed(&term) != B1200) cfsetospeed(&term, B1200);
+      if (cfgetispeed(&term) != B1200) cfsetispeed(&term, B1200);
+      break;
+   case 2400:         /* Set speed to 2400 */ 
+      if (cfgetospeed(&term) != B2400) cfsetospeed(&term, B2400);
+      if (cfgetispeed(&term) != B2400) cfsetispeed(&term, B2400);
+      break;
+   case 9600:         /* Set speed to 9600 */
+      if (cfgetospeed(&term) != B9600) cfsetospeed(&term, B9600);
+      if (cfgetispeed(&term) != B9600) cfsetispeed(&term, B9600);
+      break;
+   case 19200:        /* Set speed to 19200 */ 
+      if (cfgetospeed(&term) != B19200) cfsetospeed(&term, B19200);
+      if (cfgetispeed(&term) != B19200) cfsetispeed(&term, B19200);
+      break;
+   default:
+      printf ("ERROR:  Unknown Baud Rate\n"); 
+      break;
+   }	
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Parity ********************************/
+int SerialParity (int fdesc, int parity) { 
+   int err=0;
+   struct termios term;
+   char *prefix = "/dev/term/";
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's parity.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (parity == 0)              /* Turn off parity generation... */
+      term.c_cflag &= ~PARENB;     
+   else if (parity == 1) {            
+      term.c_cflag |= PARENB;     /* Turn on parity generation... */
+      term.c_cflag |= PARODD;     /* Sets parity to odd */ 
+   }	 
+   else if (parity == 2)         /* Turn on parity generation... */
+      term.c_cflag |= PARENB;     /* Defaults parity to even */ 
+   else 
+      printf ("ERROR:  Unknown parity specification\n");
+ 
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************Data Bit Size ***************************/
+int SerialDataBits(int fdesc, int bits) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's data bit size.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   switch (bits) {
+   case 5:  			/* Sets data bits to 5 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS5;
+      break; 
+   case 6:   			/* Sets data bits to 6 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS6;
+      break; 
+   case 7:     		/* Sets data bits to 7 */ 
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS7;
+      break; 
+   case 8:  			/* Sets data bits to 8 */
+      term.c_cflag &= ~CSIZE; 
+      term.c_cflag |= CS8;
+      break; 
+   default:
+      printf ("ERROR:  Illegal data bit size\n");
+      break; 
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/****************************** Stop Bit ****************************/
+int SerialStopBit(int fdesc, int stpbit) { 
+   int err=0;
+   struct termios term;
+
+   /* Get the serial port's attributes... */
+   if (SER_VERBOSE) printf("Setting the serial port's stop bit.\n");
+   if (tcgetattr(fdesc, &term) == -1) {
+      if (SER_VERBOSE) gprint (GP_ERR, "Port is not a tty\n");
+      return PORTERR;
+   }
+
+   if (stpbit == 1) {     		/* Sets stop bit to 1 */  
+      if (term.c_cflag & CSTOPB) term.c_cflag |= CSTOPB;
+   } else { 				/* Else stop bit to 2 */ 
+      term.c_cflag & CSTOPB;
+   }
+
+   /* Set port (terminal) to reflect the change...(flush first) */
+   tcsetattr(fdesc, TCSAFLUSH, &term);
+   return (FALSE);
+}
+
+/**************************** Stop ***********************************/
+void SerialStop (int fdesc) {
+   if (SER_VERBOSE) printf("Closing the serial port.\n");
+   close(fdesc); /* Close up shop... */
+}
+
+# define D_NREAD 1024
+
+/* send a string to the serial port, wait for an answer */
+/* answer is returned on the pointer provided */
+
+/**************************** Command ***********************************/
+int SerialCommand (char *in, char **out, int wait) {
+  
+  int i, j;
+  char *line;
+  int done, Nread, Nin, NREAD;
+  
+  if (SER_ECHO) gprint (GP_ERR, "command: %s\n", in); 
+
+  if (serial.f <= 0) {
+    gprint (GP_ERR, "serial line closed\n"); 
+    if (out != (char **) NULL) {
+      *out = strcreate ("SERIAL OFF");
+      /* return (char *) NULL instead? */
+    }
+    return (FALSE);
+  }
+
+  /* flush out the line */
+  tcflush (serial.f, TCIOFLUSH);
+
+# if (0)
+  for (i = 0; i < strlen(in); i++) {
+    gprint (GP_ERR, "%d %x\n", i, in[i]);
+  }
+# endif
+
+  /* send command to serial line */
+  Nin = write (serial.f, in, strlen(in));
+  if (Nin != strlen(in)) {
+    gprint (GP_ERR, "Serial Command not sent\n");
+    return (FALSE);
+   }
+  usleep (20000);
+  /* LX200 GPS requires some lead time (10msec) to check ready state */
+  
+  /* create space to store answer */
+  NREAD = D_NREAD;
+  ALLOCATE (line, char, NREAD);
+  bzero (line, NREAD);
+  Nread = Nin = 0;
+
+  /* read data back from serial line until no response (Nin == 0) 
+     or timeout (Nin == -1) && (i == wait) */
+
+  done = FALSE;
+  for (i = 0; !done && (i < wait); i++) {
+    Nin = read (serial.f, &line[Nread], 256);
+# if (SER_DEBUG)
+    gprint (GP_ERR, "%d ", Nin);
+# endif
+    if (Nin < 0) { /* error, check value */
+      gprint (GP_ERR, "error?");
+      continue;
+    }
+    if (Nin > 0) {
+      Nread += Nin;
+      line[Nread] = 0;
+      i = 0;
+    }
+    if (Nread > D_NREAD - 257) {
+      NREAD += D_NREAD;
+      REALLOCATE (line, char, NREAD);
+    }
+    if ((i > 0) && (Nin == 0)) done = TRUE;
+    usleep (2000);
+  }
+
+  if (SER_ECHO) gprint (GP_ERR, "answer: %s\n", line);
+
+  if (out == (char **) NULL) {
+    free (line);
+  } else {
+    *out = line;
+  }
+  return (TRUE);
+}
+
+
+/* raw:
+   cfmakeraw sets the terminal attributes as follows:
+   termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+   termios_p->c_oflag &= ~OPOST;
+   termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+   termios_p->c_cflag &= ~(CSIZE|PARENB);
+   termios_p->c_cflag |= CS8;
+
+   there conditions are turned off:
+   c_iflag:
+       IGNBRK ignore BREAK condition on input
+       BRKINT If IGNBRK is not set, generate SIGINT on BREAK condition, else read BREAK as
+              character \0.
+       PARMRK if  IGNPAR  is  not  set,  prefix a character with a parity error or framing
+              error with \377 \0.  If neither IGNPAR nor PARMRK is set, read  a  character
+              with a parity error or framing error as \0.
+       (IGNPAR ignore framing errors and parity errors.)
+
+       ISTRIP strip off eighth bit
+       INLCR  translate NL to CR on input
+       IGNCR  ignore carriage return on input
+       ICRNL  translate carriage return to newline on input (unless IGNCR is set)
+       IXON   enable XON/XOFF flow control on output
+
+   c_oflag:
+       OPOST  enable implementation-defined output processing
+
+   c_lflag:
+       ECHO   echo input characters.
+       ECHONL if ICANON is also set, echo the NL character even if ECHO is not set.
+       ICANON enable canonical mode.  This enables the special characters EOF, EOL,  EOL2,
+              ERASE, KILL, REPRINT, STATUS, and WERASE, and buffers by lines.
+       ISIG   when any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate
+              the corresponding signal.
+       IEXTEN enable implementation-defined input processing.
+
+   c_cflag:
+       CSIZE  character size mask.  Values are CS5, CS6, CS7, or CS8. (CS8 set).
+       PARENB enable parity generation on output and parity checking for input.
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Telescope.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Telescope.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/Telescope.c	(revision 17422)
@@ -0,0 +1,361 @@
+# include "dimm.h"
+
+# define SER_TIMEOUT 10
+# define SLEW_TIMEOUT 30
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double distSky (double r1, double r2, double d1, double d2) {
+
+  double x1, y1, z1;
+  double x2, y2, z2;
+  double cosT, dist;
+
+  x1 = dCOS (r1) * dCOS (d1);
+  y1 = dSIN (r1) * dCOS (d1);
+  z1 = dSIN (d1);
+
+  x2 = dCOS (r2) * dCOS (d2);
+  y2 = dSIN (r2) * dCOS (d2);
+  z2 = dSIN (d2);
+
+  cosT = x1*x2 + y1*y2 + z1*z2;
+  dist = DEG_RAD * acos (cosT);
+
+  return (dist);
+}
+
+int getRD (double *r, double *d) { 
+
+  int status;
+  char *rastr, *decstr;
+
+  status = SerialCommand (":GR#", &rastr, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  status = SerialCommand (":GD#", &decstr, SER_TIMEOUT); 
+  if (!status) return (FALSE);
+
+  status = ohana_str_to_radec (r, d, rastr, decstr);
+  if (!status) return (FALSE);
+
+  free (rastr);
+  free (decstr);
+
+  return (TRUE);
+}
+
+int gotoRD (double r, double d) {
+
+  double R, D, dist;
+  int Ntry, status;
+  char *str, *answer, cmd[64];
+
+  /* error on ra, dec means coords out of range */
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);   free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);  free (str);
+  status = SerialCommand (cmd, &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "1")) return (FALSE); 
+  free (answer);
+
+  Ntry = 0;
+  status = SerialCommand (":MS#", &answer, SER_TIMEOUT);   
+  if (!status) return (FALSE); 
+  if (answer == (char *) NULL) return (FALSE); 
+  if (strcmp (answer, "0")) {
+    gprint (GP_ERR, "error: %s\n", answer);
+    return (FALSE); 
+  }
+  free (answer);
+
+  /* watch for response? */
+  status = FALSE;
+  while (!status) {
+    getRD (&R, &D);
+    dist = distSky (R, r, D, d);
+    if (dist < 0.1) return (TRUE);
+    usleep (100000);
+    Ntry ++;
+    if (Ntry > SLEW_TIMEOUT) return (FALSE);
+  }
+  return (status);
+}
+
+/* actual offsets are x,y, convert to arcmin */
+int offset (char *direction, double distance) {
+
+  /* Four rate choices: 
+     slew   (RS) -  8 degree / sec : rate 1
+     find   (RM) - 30 arcmin / sec : rate 2
+     center (RC) -  4 arcmin / sec : rate 3
+     guide  (RG) - 15 arcsec / sec : rate 4
+
+     communication requires ~1.0 sec:
+     offset should use rate which gives shortest time > 2.0 sec 
+  */
+
+# define NRATE 4
+  static double delay[NRATE]  = {0.1, 0.1, 0.1, 0.1};
+  static double rate[NRATE]   = {480.0, 30.0, 4.0, 0.25};
+  static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "y")) dir = (distance > 0) ? 'n' : 's';
+  if (!strcasecmp (direction, "x")) dir = (distance > 0) ? 'w' : 'e';
+  if (!dir) return (FALSE);
+
+  /* distance is in arcmin */
+  distance = fabs (distance);
+
+  /* logic is bad -- does not catch too small distances */  
+  rsel = -1;
+  tsel = SLEW_TIMEOUT;
+  for (i = 0; i < NRATE; i++) {
+    dt = distance / rate[i] - delay[i];
+    if ((dt > 0) && (dt < tsel)) {
+      rsel = i;
+      tsel = dt;
+    }
+  }
+  if (tsel < 0) {
+    gprint (GP_ERR, "offset %f arcmin below minimum\n", distance);
+    return (FALSE);
+  }
+  if (tsel > SLEW_TIMEOUT) {
+    gprint (GP_ERR, "offset %f arcmin above maximum\n", distance);
+    return (FALSE);
+  }
+  gprint (GP_ERR, "offsetting %c for %f seconds\n", dir, tsel);
+  
+  sprintf (cmd, ":%s#", rcmd[rsel]);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(tsel*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+/* actual offsets are x,y, convert to arcmin */
+int toffset (char *direction, char *rate, double duration) {
+
+# define NRATE 6
+  /* static char rcmd[NRATE][16] = {"RS", "RM", "RC", "RG"};*/
+  static char rcmd[NRATE][64] = {"RS", "RM", "RC", "RG", "RA0.0085", "RE0.0085"};
+
+  int i, status, rsel;
+  char dir, cmd[32];
+  double tsel, dt;
+
+  dir = 0;
+  if (!strcasecmp (direction, "x")) dir = (duration > 0) ? 'w' : 'e';
+  if (!strcasecmp (direction, "y")) dir = (duration > 0) ? 'n' : 's';
+  if (!dir) return (FALSE);
+  duration = fabs (duration);
+  
+  status = FALSE;
+  for (i = 0; i < NRATE; i++) if (!strcmp (rcmd[i], rate)) status = TRUE;
+  if (!status) {
+    gprint (GP_ERR, "bad rate: %s\n", rate);
+    return (FALSE);
+  }
+
+  sprintf (cmd, ":%s#", rate);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  sprintf (cmd, ":M%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  usleep ((int)(duration*1000000));
+
+  sprintf (cmd, ":Q%c#", dir);
+  status = SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  if (!status) return (FALSE);
+
+  return (TRUE);
+}  
+
+int getXY (double *x, double *y) {
+
+  char *answer;
+
+  SerialCommand (":GA#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (x, answer);
+  free (answer);
+
+  SerialCommand (":GZ#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (y, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+/* need error checking on these */
+int setRD (double r, double d) {
+
+  char *str, *answer, cmd[64];
+
+  /* set telescope coords, send */
+  str = meade_ra_to_str (r);
+  sprintf (cmd, ":Sr%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  str = meade_dec_to_str (d);
+  sprintf (cmd, ":Sd%s#", str);
+  SerialCommand (cmd, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  SerialCommand (":CM#", &answer, SER_TIMEOUT);
+  gprint (GP_ERR, "result: %s\n", answer);
+  free (answer);
+  return (TRUE);
+}
+
+int setSite (char *sitename, double lon, double lat) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, line[32];
+
+  gprint (GP_ERR, "careful, this causes problems\n");
+  return (FALSE);
+
+  SerialCommand (":W1#", (char **) NULL, SER_TIMEOUT);
+
+  /* Set site name 1 */
+  sprintf (line, ":SM %s#", sitename); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set site long */
+  str = meade_deg_to_str (lon);
+  str[6] = 0;
+  sprintf (line, ":Sg%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* Set site lat */
+  str = meade_dec_to_str (lat);
+  sprintf (line, ":St%s#", str); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  free (str);
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+00#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = gmtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  return (TRUE);
+}
+
+int setTime (char *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char line[32], *answer;
+
+  /* set UTC offset to 0.0: offset + local = gmt */
+  sprintf (line, ":SG+10#");
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /* Set local */
+  gettimeofday (&now, (struct timezone *) NULL);
+  gmt = localtime (&now.tv_sec);
+  sprintf (line, ":SL%02d:%02d:%02d#", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+
+  /*
+  sprintf (line, ":SS%s#", lst);
+  SerialCommand (line, (char **) NULL, SER_TIMEOUT);
+  */
+
+  return (TRUE);
+}
+
+int getSite (double *lon, double *lat, double *lst) {
+
+  struct tm *gmt;
+  struct timeval now;
+  char *str, *answer, line[32];
+
+  /* : get latitude */
+  SerialCommand (":Gt#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lat, answer);
+  free (answer);
+
+  /* : get longitude */
+  SerialCommand (":Gg#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lon, answer);
+  free (answer);
+
+  /* : get LST */
+  SerialCommand (":GS#", &answer, SER_TIMEOUT);
+  ohana_dms_to_ddd (lst, answer);
+  free (answer);
+
+  return (TRUE);
+}
+
+int ParkScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hP#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
+
+int SleepScope() {
+
+  char *str, *answer, line[32];
+  
+  SerialCommand (":hN#", &answer, SER_TIMEOUT);
+  free (answer);
+  
+  return (TRUE);
+
+}
+
+int WakeScope() {
+
+  char *str, *answer, line[32];
+
+  SerialCommand (":hW#", &answer, SER_TIMEOUT);
+  free (answer);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/altaz.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/altaz.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/altaz.c	(revision 17422)
@@ -0,0 +1,88 @@
+# include "dimm.h"
+
+# define dCOS(A)   ((double) cos ((double)RAD_DEG*A))
+# define dSIN(A)   ((double) sin ((double)RAD_DEG*A))
+
+double atan2 (double y, double x);
+
+int altaz (int argc, char **argv) {
+  
+  double alt, az, lat, rot;
+  double ha, dec;
+  double sind, sinh, cosh;
+  char *latstr;
+
+  if (argc != 6) goto usage;
+
+  if (!strcmp (argv[1], "-h")) goto radec;
+  if (!strcmp (argv[1], "-c")) goto altaz;
+
+ radec:
+  /* ha/dec -> alt/az */
+  ha  = atof (argv[2]);
+  dec = atof (argv[3]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+ 
+  sind = dSIN (dec) * dSIN (lat) + dCOS (dec) * dCOS (ha) * dCOS (lat);
+  alt  = DEG_RAD * asin (sind);
+
+  sinh = - dCOS (dec) * dSIN (ha);
+  cosh =   dSIN (dec) * dCOS (lat) - dCOS (dec) * dCOS (ha) * dSIN (lat);
+
+  az = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[4], alt);
+  set_variable (argv[5], az);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ altaz:
+  /* alt/az -> ha/dec */
+  alt = atof (argv[4]);
+  az  = atof (argv[5]);
+
+  latstr = get_variable ("LATITUDE");
+  if (latstr == (char *) NULL) {
+    gprint (GP_ERR, "please define $LATITUDE\n");
+    return (FALSE);
+  }
+  lat = atof (latstr);
+  free (latstr);
+
+  sind = dSIN (alt) * dSIN (lat) + dCOS (alt) * dCOS (az) * dCOS (lat);
+  dec  = DEG_RAD * asin (sind);
+
+  sinh = -dCOS (alt) * dSIN (az);
+  cosh =  dSIN (alt) * dCOS (lat) - dCOS (alt) * dCOS (az) * dSIN (lat);
+
+  ha = DEG_RAD * atan2 (sinh, cosh);
+  set_variable (argv[2], ha);
+  set_variable (argv[3], dec);
+
+  sinh = -dCOS(az) * dSIN(alt) * dSIN(ha) * dSIN(lat) + dSIN(az) * dSIN(alt) * dCOS(ha) - dSIN(ha) * dCOS(alt) * dCOS(lat);
+  cosh = -dSIN(az) * dSIN(ha) * dSIN(lat) - dCOS(az) * dCOS(ha);
+  rot = -DEG_RAD * atan2 (sinh, cosh);
+  set_variable ("ROT", rot);
+
+  return (TRUE);
+  
+ usage:
+  gprint (GP_ERR, "USAGE: altaz -h (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "USAGE: altaz -c (ha) (dec) (alt) (az)\n");
+  gprint (GP_ERR, "       -h alt/az to ha/dec, -c ha/dec to alt/az\n");
+  gprint (GP_ERR, "       returned values in variables provided\n");
+  return (FALSE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/analysis.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/analysis.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/analysis.c	(revision 17422)
@@ -0,0 +1,184 @@
+
+/* should this all be wrapped within an opihi implementation? */
+
+typedef struct {
+
+  /* image data area */
+  int Nx, Ny;
+  char *buffer;
+  int Nbytes;
+
+  /* image metadata */
+  double ccdtemp;
+  double airtemp;
+  double ra, dec, airmass;
+  double exptime;
+  int binning;
+} Image;
+
+subtractImage (Image *a, Image *b) {
+
+  if (a[0].Nx != b[0].Nx) return (FALSE);
+  if (a[0].Ny != b[0].Ny) return (FALSE);
+
+  Npix = a[0].Nx*a[0].Ny;
+  ap = (float *) a[0].buffer;
+  bp = (float *) b[0].buffer;
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    *ap -= *bp;
+  }
+  return (FALSE);
+}
+
+statsImage (Image *image, Stats *stats) {
+
+  val = (float *)image[0].buffer;
+  max = min = val[0];
+  Npix = image[0].Nx*image[0].Ny;
+  for (i = 0; i < Npix; i++, val++) {
+    N1 += *val;
+    N2 += (*val)*(*val);
+    max = MAX (max, *val);
+    min = MIN (min, *val);
+  }
+  stats[0].mean  = N1 / Npix;
+  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
+  stats[0].min = min;
+  stats[0].max = max;
+
+  stats[0].median = stats[0].mean;
+  range = MAX (0.5, 0xffff / (max - min));
+  if (range == 0) return ();
+
+  ALLOCATE (hist, int, 0x10000);
+  bzero (hist, 0x10000*sizeof(int));
+
+  val = (float *)image[0].buffer;
+  for (i = 0; i < Npix; i++) {
+    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
+    hist[bin] ++;
+  }
+
+  Nhist = 0;
+  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
+    Nhist += hist[i];
+  stats[0].median = i / range + min;
+  free (hist);
+
+  return ();
+}
+
+findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
+
+  /* binarize @ threshold */
+
+  binimage = createImage (image[0].Nx, image[0].Ny);
+
+  Npix = image[0].Nx*image[0].Ny;
+  ap = image[0].buffer;
+  bp = binimage[0].buffer;
+  bzero (bp, Npix*sizeof (short));
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) * bp = 1;
+  }
+
+  clearpix ();
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (binimage.buffer[j*Nx + i]) {
+	status = fillrow (binimage.buffer, Nx, j*Nx, i, &xs, &xe);
+	for (J = j + 1; (J < Ny) && status; J++) {
+	  for (I = xs; !binimage.buffer[J*Nx + I] && (I < xe); I++);
+	  if (binimage.buffer[J*Nx + I]) {
+	    status = fillrow (binimage.buffer, Nx, J*Nx, I, &xs, &xe);
+	  } 
+	}  
+	/* we now have a stack of pixels, convert to a single star */
+	statpix (&x, &y, image[0].buffer, Nx);
+	addstar (x, y);
+	clearpix ();
+      }
+    }
+  }
+}
+
+/* find contiguous trigger pixels in row from starting point */
+
+int fillrow (float *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npix = 0;
+static int *Pix = (int *) NULL;
+
+addpix (int pix) {
+  Npix ++;
+  if (Pix == (int *) NULL) {
+    ALLOCATE (Pix, int, MAX (1, Npix));
+  } else {
+    REALLOCATE (Pix, int, MAX (1, Npix));
+  }    
+  Pix[Npix - 1] = pix;
+}
+
+clearpix () {
+  Npix = 0;
+  REALLOCATE (Pix, int, 1);
+}
+
+statpix (double *x, double *y, float *buffer, int Nx) {
+
+  int X, Y;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npix; i++) {
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+}
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+..........xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/camera.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/camera.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/camera.c	(revision 17422)
@@ -0,0 +1,130 @@
+# include "dimm.h"
+# define EXIT_STATUS(S) { seteuid (UID); return (S); }
+
+static uid_t UID, EUID;
+
+SetEUID () {
+
+  /* save the UID (ID of calling process) and EUID (should be root) */
+  UID = getuid ();
+  EUID = geteuid ();
+  seteuid (UID);
+}
+
+int camera (int argc, char **argv) {
+  
+  /* USAGE: 
+     camera init port
+     camera expose exptime
+     camera readout x y dx dy
+     camera temp set value
+     camera temp get var
+  */
+
+  if (argc < 2) goto usage;
+
+  seteuid (EUID);
+  
+  if (!strcasecmp (argv[1], "init")) {
+    int port, status;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera init (port)\n");
+      EXIT_STATUS (FALSE);
+    }
+    sscanf (argv[2], "%x", &port);
+    status = InitCamera (port);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "expose")) {
+
+    int status;
+    double exptime;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera expose (exptime)\n");
+      EXIT_STATUS (FALSE);
+    }
+    exptime = atof (argv[2]);
+    status = Exposure (exptime);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "temp")) {
+
+    int status;
+    double temp;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera temp (temperature)\n");
+      EXIT_STATUS (FALSE);
+    }
+    temp = atof (argv[2]);
+    status = SetTemperature (temp);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "status")) {
+
+    int status;
+    double temp;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: camera status\n");
+      EXIT_STATUS (FALSE);
+    }
+    DumpCameraStatus ();
+    EXIT_STATUS (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "readout")) {
+
+    int Nbuf, status;
+    double temp;
+    int x, y, dx, dy, NX, NY;
+    Buffer *buf;
+
+    if ((argc != 7) && (argc != 3)) {
+      gprint (GP_ERR, "USAGE: camera readout (buffer) x y dx dy\n");
+      EXIT_STATUS (FALSE);
+    }
+
+    if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) EXIT_STATUS (FALSE);
+
+    CameraFullSize (&NX, &NY);
+    x = y = 0;
+    dx = NX;
+    dy = NY;
+    if (argc == 7) {
+      x  = atof (argv[3]);
+      y  = atof (argv[4]);
+      dx = atof (argv[5]);
+      dy = atof (argv[6]);
+    } 
+
+    /* generate a buffer to store the image */
+    gfits_free_matrix (&buf[0].matrix);
+    gfits_free_header (&buf[0].header);
+    CreateBuffer (buf, dx, dy, -32, 0.0, 1.0);
+    strcpy (buf[0].file, "(empty)");
+
+    ReadOut (x, y, dx, dy, 1, buf[0].matrix.buffer);
+
+    gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, gfits_get_unsign_mode());
+
+    EXIT_STATUS (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "camera init port\n");
+  gprint (GP_ERR, "camera expose exptime\n");
+  gprint (GP_ERR, "camera readout x y dx dy\n");
+  gprint (GP_ERR, "camera temp set value\n");
+  gprint (GP_ERR, "camera temp get var\n");
+  seteuid (UID);
+  return (FALSE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/dimm.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/dimm.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/dimm.c.in	(revision 17422)
@@ -0,0 +1,68 @@
+# include "opihi.h"
+
+# define opihi_name "DIMM"
+# define opihi_prompt "dimm: "
+# define opihi_description "DIMM telescope controller\n"
+# define opihi_history ".dimm"
+# define opihi_rcfile ".dimmrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDIMM ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDIMM ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/findstars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/findstars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/findstars.c	(revision 17422)
@@ -0,0 +1,152 @@
+# include "dimm.h"
+
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe);
+int addpix (int pix);
+int clearpix ();
+int freepix ();
+int statpix (float *x, float *y, float *buffer, int Nx);
+int addstar (double x, double y);
+
+Vector *vecx, *vecy, *vecf, *vecn;
+
+int findstars (int argc, char **argv) {
+
+  int i, j, I, J, Npix, Nbuf, status;
+  int xs, xe, Nx, Ny;
+  char *binary, *bp;
+  float *ap, threshold, x, y;
+  Buffer *buf;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: findstars (buffer) (threshold)\n");
+    return (FALSE);
+  }
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("star_x", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("star_y", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecf = SelectVector ("star_f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecn = SelectVector ("star_n", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  vecx[0].Nelements = vecy[0].Nelements = 0;
+  vecf[0].Nelements = vecn[0].Nelements = 0;
+
+  REALLOCATE (vecx[0].elements, float, 1);
+  REALLOCATE (vecy[0].elements, float, 1);
+  REALLOCATE (vecf[0].elements, float, 1);
+  REALLOCATE (vecn[0].elements, float, 1);
+
+  /* binarize @ threshold */
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+  Npix = Nx*Ny;
+  ALLOCATE (binary, char, Npix);
+
+  ap = (float *) buf[0].matrix.buffer;
+  bp = binary;
+  bzero (bp, Npix);
+
+  for (i = 0; i < Npix; i++, ap++, bp++) {
+    if (*ap > threshold) *bp = 1;
+  }
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+      if (binary[j*Nx + i]) {
+	clearpix ();
+	status = fillrow (binary, Nx, j*Nx, i, &xs, &xe);
+	for (J = j + 1; (J < Ny) && status; J++) {
+	  for (I = xs; !binary[J*Nx + I] && (I <= xe); I++);
+	  status = fillrow (binary, Nx, J*Nx, I, &xs, &xe);
+	}  
+	/* we now have a stack of pixels, find geometric center */
+	statpix (&x, &y, (float *)buf[0].matrix.buffer, Nx);
+      }
+    }
+  }
+  freepix ();
+  return (TRUE);
+}
+
+/* find contiguous trigger pixels in row from starting point */
+int fillrow (char *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  int i, pix, trigger;
+
+  *xe = *xs = sx;
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npixlist = 0;
+static int *pixlist = (int *) NULL;
+
+addpix (int pix) {
+  Npixlist ++;
+  if (pixlist == (int *) NULL) {
+    ALLOCATE (pixlist, int, MAX (1, Npixlist));
+  } else {
+    REALLOCATE (pixlist, int, MAX (1, Npixlist));
+  }    
+  pixlist[Npixlist - 1] = pix;
+}
+
+clearpix () {
+  Npixlist = 0;
+  REALLOCATE (pixlist, int, 1);
+}
+
+freepix () {
+  Npixlist = 0;
+  free (pixlist);
+  pixlist = (int *) NULL;
+}
+
+statpix (float *x, float *y, float *buffer, int Nx) {
+
+  int i, X, Y, pix, Nv, No;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npixlist; i++) {
+    pix = pixlist[i];
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+  gprint (GP_ERR, "%f %f  %f %d\n", *x, *y, So, Npixlist);
+
+  No = vecx[0].Nelements;
+  Nv = No + 1;
+  vecx[0].Nelements = vecy[0].Nelements = Nv;
+  vecf[0].Nelements = vecn[0].Nelements = Nv;
+
+  REALLOCATE (vecx[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecf[0].elements, float, MAX (Nv, 1));
+  REALLOCATE (vecn[0].elements, float, MAX (Nv, 1));
+
+  vecx[0].elements[No] = *x;
+  vecy[0].elements[No] = *y;
+  vecf[0].elements[No] = So;
+  vecn[0].elements[No] = Npixlist;
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/init.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "dimm.h"
+
+int camera          PROTO((int, char **));
+int findstars       PROTO((int, char **));
+int telescope       PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "camera",    camera,    "camera functions"},
+  {1, "findstars", findstars, "find objects on image"},
+  {1, "telescope", telescope, "telescope communications"},
+  {1, "version",   version,   "show version information"},
+}; 
+
+void InitDIMM () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeDIMM () {
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sbig.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sbig.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sbig.h	(revision 17422)
@@ -0,0 +1,222 @@
+#ifndef _SBIG_H
+#define _SBIG_H
+
+#define VERSION  2.0
+
+struct sbig_init {
+    float linux_version;
+    int	 nmbr_bad_columns;	/* bad columns in imaging CCD */
+    int  bad_columns[4];
+    int  imaging_abg_type;	/* 0 no ABG, 1 ABG present */
+    char serial_number[10];
+    unsigned firmware_version;
+    char  camera_name[64];
+    struct camera_info {
+	int  nmbr_readout_modes;
+	struct readout_mode {
+	    int  mode;
+	    int  width;			/* pixels */
+	    int  height;		/* height */
+	    int  gain;			/* in 0.01 e-/ADU */
+	    unsigned pixel_width;	/* in nanometers */
+	    unsigned pixel_height;	/* in nanometers */
+	} readout_mode[12];
+    } camera_info[2];
+    int ST5_AD_size;
+    int ST5_filter_type;
+};
+extern int  sbig_init(int port, int options, struct sbig_init *);
+
+#define SBIG_IMAGING_CCD 0
+#define SBIG_TRACKING_CCD 1
+
+#define SBIG_CCD_STATUS_IDLE  0
+#define SBIG_CCD_STATUS_IN_PROGRESS  2
+#define SBIG_CCD_STATUS_COMPLETE  3
+
+#define SBIG_CCD_SHUTTER_OPENED  0
+#define SBIG_CCD_SHUTTER_CLOSED  1
+#define SBIG_CCD_SHUTTER_OPENING  2
+#define SBIG_CCD_SHUTTER_CLOSING  3
+
+struct sbig_status {
+    int  imaging_ccd_status;
+    int  tracking_ccd_status;
+    int  fan_on;
+    int  shutter_state;
+    int  led_state;
+    int  shutter_edge;
+    int  plus_x_relay;
+    int  minus_x_relay;
+    int  plus_y_relay;
+    int  minus_y_relay;
+    int  pulse_active;
+    int  temperature_regulation;
+    int  temperature_setpoint;
+    int  cooling_power;
+    int  air_temperature;
+    int  ccd_temperature;
+};
+extern int  sbig_get_status(struct sbig_status *);
+
+
+#define SBIG_LEAVE_SHUTTER  0
+#define SBIG_OPEN_SHUTTER  1
+#define SBIG_CLOSE_SHUTTER  2
+#define SBIG_INITIALIZE_SHUTTER  3
+
+#define SBIG_LED_OFF  0
+#define SBIG_LED_ON  1
+#define SBIG_LED_BLINK_SLOW 2
+#define SBIG_LED_BLINK_FAST 3
+
+struct sbig_control {
+    int  fan_on;
+    int  shutter;
+    int  led;
+};
+extern int  sbig_control(struct sbig_control *);
+
+struct sbig_pulse {
+    int nmbr_pulses;		/* 0 to 255 */
+    int pulse_width;		/* microsec, min 9 */
+    int pulse_interval;		/* microsec, min 27+pulse_width */
+};
+extern int  sbig_pulse(struct sbig_pulse *);
+
+struct sbig_relay {
+    int x_plus_time;
+    int x_minus_time;
+    int y_plus_time;
+    int y_minus_time;
+};
+extern int  sbig_activate_relay(struct sbig_relay *);
+
+
+/*
+ * Shutter control.  0 for no change in shutter (i.e. control of shutter
+ * using SBigControl class), 1 normal shutter (open during exposure,
+ * closed otherwise), 2 shutter closed (for taking dark frames).
+ */
+#define SBIG_EXPOSE_SHUTTER_UNCHANGED  0
+#define SBIG_EXPOSE_SHUTTER_NORMAL  1
+#define SBIG_EXPOSE_SHUTTER_CLOSED  2
+
+struct sbig_expose {
+    int ccd;
+    int exposure_time;
+    int abg_state;
+    int shutter;
+};
+extern int  sbig_expose(struct sbig_expose *);
+
+
+/*
+ *  Terminate the image and read it out.
+ *  Use width == 0 and height == 0 to abort an image with no readout (all
+ *   params other than ccd ignored)
+ */
+#define SBIG_BIN_1X1  0
+#define SBIG_BIN_2X2  1
+#define SBIG_BIN_3X3  2
+
+struct sbig_readout {
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout(struct sbig_readout *);
+
+#define SBIG_NO_SHUTTER_DELAY  0x0001	/* don't wait for shutter to close */
+struct sbig_readout2 {
+    int flags;				/* see immediately above */
+    int ccd;
+    int binning;
+    int x, y;
+    int width, height;
+    unsigned short *data;
+    int data_size_in_bytes;
+    int (*callback)(float percent_complete);
+};
+extern int  sbig_readout2(struct sbig_readout2 *);
+
+
+#define SBIG_TEMP_REGULATION_OFF  0
+#define SBIG_TEMP_REGULATION_ON  1
+#define SBIG_TEMP_REGULATION_DIRECT_DRIVE  2
+
+/*
+ * Antiblooming gate control values
+ */
+#define SBIG_ABG_OFF  0
+#define SBIG_ABG_LOW  1
+#define SBIG_ABG_MEDIUM  2
+#define SBIG_ABG_HIGH  3
+
+struct sbig_cool {
+    int regulation;		/* 0 off, 1 on, 2 direct_drive */
+    int temperature;		/* in 0.1 deg C, if 'on' */
+    int direct_drive;		/* power [0..255], direct_drive */
+};
+extern int  sbig_set_cooling(struct sbig_cool *);
+
+extern int  sbig_set_ao7_deflection(int x_deflection, int y_deflection);
+
+extern int  sbig_set_ao7_focus(int type);
+
+#define SBIG_AO7_FOCUS_SOFT_CENTER 4
+#define SBIG_AO7_FOCUS_HARD_CENTER  3
+#define SBIG_AO7_FOCUS_STEP_TOWARD_SCOPE 2
+#define SBIG_AO7_FOCUS_STEP_FROM_SCOPE 1
+
+/*
+ *	Return Error Codes
+ *
+ *	These are the error codes returned by the driver
+ *	function.  They are prefixed with CE_ to designate
+ *	them as camera errors.
+ *
+ *      The return codes from the sbig_xxx() routines will
+ *      be the NEGATIVE of these on an error.
+ *
+ */
+#define SBIG_CAMERA_NOT_FOUND	-1
+#define SBIG_EXPOSURE_IN_PROGRESS -2
+#define SBIG_NO_EXPOSURE_IN_PROGRESS -3
+#define SBIG_UNKNOWN_COMMAND	-4
+#define SBIG_BAD_CAMERA_COMMAND	-5
+#define SBIG_BAD_PARAMETER	-6
+#define SBIG_TX_TIMEOUT		-7
+#define SBIG_RX_TIMEOUT		-8
+#define SBIG_NAK_RESBIGIVED	-9
+#define SBIG_CAN_RESBIGIVED	-10
+#define SBIG_UNKNOWN_RESPONSE	-11
+#define SBIG_BAD_LENGTH		-12
+#define SBIG_AD_TIMEOUT		-13
+#define SBIG_CHECKSUM_ERROR	-14
+#define SBIG_EEPROM_ERROR	-15
+#define SBIG_SHUTTER_ERROR	-16
+#define SBIG_UNKNOWN_CAMERA	-17
+#define SBIG_DRIVER_NOT_FOUND	-18
+#define SBIG_DRIVER_NOT_OPEN	-19
+#define SBIG_DRIVER_NOT_CLOSED	-20
+#define SBIG_SHARE_ERROR	-21
+#define SBIG_TSBIG_NOT_FOUND	-22
+#define SBIG_NEXT_ERROR		-23
+#define SBIG_NOT_ROOT		-24
+char *sbig_show_error(int);
+
+/*
+ * IO strategy flags
+ */
+#define SBIG_DISABLE_INTERRUPTS 0x001
+#define SBIG_LOCK_ALL	0x002
+#define SBIG_SHORT_DELAYS 0x004
+#define SBIG_DEFAULT_STRATEGY (SBIG_DISABLE_INTERRUPTS|SBIG_LOCK_ALL)
+void sbig_set_linux_strategy(int);
+
+#endif /* S_BIG_H */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/sbig/sp_ccdcontrol.c	(revision 17422)
@@ -0,0 +1,472 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "sbig.h"
+#include "fh/fh.h" /* CFHT FITS Handling library */
+
+// XXX RHL claims this is not needed
+// #include <values.h>
+
+#define N_FITS_ENTRIES 33
+
+/*
+ * The following hack is needed if you want to use sbig.a w/ libc5
+ */
+#ifndef __bzero
+void __bzero(void* s, int n)
+{
+  memset(s, 0, n);
+}
+#endif
+
+static struct sbig_init info;
+
+static int write_fits(int width, int height, double etime, int binmode, unsigned short *data);
+
+static struct sbig_readout readout;
+
+/*
+ * This is the 'progress' callback
+ * passed to sbig_readout()
+ */
+static int readout_callback(float percent)
+{
+  gprint (GP_ERR, "progress: Reading %d x %d pixels (%d%%)\r",
+	  readout.width, readout.height, (int)percent);
+  /* return 1 to continue, 0 to abort */
+  return 1;
+}
+
+/*
+ *  Start an exposure, wait for it, then download from the camera
+ *  in bands.  This is somewhat complex, since it handles any size
+ *  subimage of the CCD, and bands too.
+ */
+static void take_picture(double etime, int binmode, int shuttermode, int x, int y, int w, int h)
+{
+  int  ret, i, needed;
+  static unsigned short  *buffer;
+  static int  buffer_size;
+  struct sbig_expose  expose;
+  struct sbig_status  status;
+  double  f;
+  
+  expose.ccd = 0; /* Always in imaging mode, not tracking 
+		    since CFHT PF does the tracking*/
+
+  if(binmode<0 || binmode>3){
+    gprint (GP_ERR, "Imaging binmode options: [0=1x1, *1=2x2, 2=3x3]\n");
+    exit(EXIT_FAILURE);
+  }
+  readout.binning=binmode;
+  
+  readout.x=x;
+  readout.y=y;
+  readout.width=w;
+  readout.height=h;
+  if (readout.width == 0) {
+    readout.width =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].width - x;
+  } else
+    if (readout.width + x >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].width) {
+    gprint (GP_ERR, "Raster X or Width parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (readout.height == 0) {
+    readout.height =
+      info.camera_info[expose.ccd].readout_mode[readout.binning].height - y;
+  } else
+    if (readout.height + y >
+	info.camera_info[expose.ccd].readout_mode[readout.binning].height) {
+    gprint (GP_ERR, "Raster Y or Height parameter out of range.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  f = etime;
+  expose.exposure_time = (int)(100.0*f);
+  expose.abg_state = SBIG_ABG_OFF; /* SBIG_ABG_MEDIUM */
+
+  expose.shutter = shuttermode;  /* No shutter present */
+
+  sync();
+  ret = sbig_expose(&expose);
+  if (ret < 0)
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+  
+  gprint (GP_ERR, "progress: Sleeping for %g seconds\r", 0.01*expose.exposure_time);
+
+  if (expose.exposure_time > 100) {
+    for (i = 0; i < expose.exposure_time; i += 100) {
+      sbig_get_status(&status);
+      gprint (GP_ERR, "progress: Exposure in progress; ccd_status=%d, shutter=%d (%d%%)\r",
+	      status.imaging_ccd_status, status.shutter_state,
+	      i * 100 / expose.exposure_time);
+      sleep(1);
+    }
+  } else
+    usleep(expose.exposure_time*10000);
+  
+  /* wait for exposure to complete */
+  gprint (GP_ERR, "progress: Waiting for controller                             (100%%)\r");
+
+  for (i = 0; i < 1000; ++i) {
+    ret = sbig_get_status(&status);
+    if (ret < 0) {
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+      break;
+    }
+    if (status.imaging_ccd_status != 3)
+      gprint (GP_ERR, "logonly: ccd_status=%d\n", status.imaging_ccd_status);
+
+    if (readout.ccd == 0) {
+      if (status.imaging_ccd_status != 2)
+	break;
+    } else {
+      if (status.tracking_ccd_status != 2)
+	break;
+    }
+    usleep(50000);
+  }
+  if (i)
+    gprint (GP_ERR, "warning: Exposure took an extra %g seconds\n", 0.050*i);
+
+  if (i == 1000)
+    gprint (GP_ERR, "error: EXPOSURE DIDN'T FINISH!\n");
+  else
+    gprint (GP_ERR, "progress: Exposure complete                                  (100%%)\n");
+
+  readout.ccd = expose.ccd;
+  readout.binning = readout.binning;
+  needed = readout.height*readout.width*sizeof(short);
+  if (buffer_size < needed) {
+    if (buffer != NULL)
+      free(buffer);
+    buffer = malloc(buffer_size = needed);
+  }
+  readout.data = buffer;
+  readout.data_size_in_bytes = buffer_size;
+  readout.callback = readout_callback;
+    
+  sync();
+  if ((ret = sbig_readout(&readout)) < 0) {
+    gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    return;
+  }
+
+  gprint (GP_ERR, "progress: Writing FITS data to stdout\n");
+
+  if(write_fits(readout.width, readout.height, etime, binmode, buffer)==-1)
+    gprint (GP_ERR, "error: Could not write FITS file to stdout.\n");
+  gprint (GP_ERR, "progress: sp_ccdcontrol done.\n");
+}
+
+static int write_fits(int w, int h, double etime, int binmode, unsigned short *data)
+{
+  HeaderUnit hu;
+  register int i;
+  double datamin = DBL_MAX, datamax = DBL_MIN;
+  unsigned short u;
+  char str[80]; /* For building string FITS headers */
+  time_t date;
+
+  int ret; /* Return value for sbig_status */
+  struct sbig_status sbstat; /* Status structure */
+  
+  /* Get status to add to FITS header */
+  sync();
+  ret = sbig_get_status(&sbstat);
+
+  if (ret < 0) {
+    gprint (GP_ERR, "error: sbig_get_status failed: %s\n", sbig_show_error(ret));
+    exit(EXIT_FAILURE);
+  }
+
+  /* shouldn't we scale this to fit, first? */
+  for (i = w*h; --i >= 0; )
+    {
+      u = data[i];
+      if (u > datamax) datamax = u;
+      if (u < datamin) datamin = u;
+      data[i] = ((u<<8)|(u>>8)) ^ 0x0080; /* -32768 */
+    }
+
+  time(&date);
+
+  hu = fh_create();
+  fh_reserve(hu, 50); /* Reserve space for 50 cards (TCS, Elixir?, etc.) */
+  fh_set_bool(hu, 0., "SIMPLE", 1, "Standard FITS");
+  fh_set_int(hu, 1.0, "BITPIX", 16,"16-bit data");
+  fh_set_int(hu, 2.0, "NAXIS", 2,  "Number of axes");
+  fh_set_int(hu, 2.1, "NAXIS1", w, "Number of pixel columns");
+  fh_set_int(hu, 2.2, "NAXIS2", h, "Number of pixel rows");
+  fh_set_int(hu, 5.0, "PCOUNT",	0, "No 'random' parameters");
+  fh_set_int(hu, 6.0, "GCOUNT",	1, "Only one group");
+
+  strftime(str, sizeof(str)-1, "%Y-%m-%dT%T", gmtime(&date));
+  fh_set_str(hu, 104, "DATE", str, "UTC Date of file creation");
+  strftime(str, sizeof(str)-1, "%a %b %d %H:%M:%S %Z %Y", localtime(&date));
+  fh_set_str(hu, 104.1, "HSTTIME", str, "Local time in Hawaii");
+
+  fh_set_str(hu, 105, "ORIGIN", "CFHT", "Canada-France-Hawaii Telescope");
+
+  fh_set_flt(hu, 141.,"BZERO",	32768.0, 6,	"Zero factor");
+  fh_set_flt(hu, 142.,"BSCALE",	1.0, 2, "Scale factor");
+  fh_set_flt(hu, 150, "DATAMIN", datamin, 6, "Minimum value of the data");
+  fh_set_flt(hu, 151, "DATAMAX", datamax, 6, "Maximum value of the data");
+  fh_set_flt(hu, 160, "SATURATE", 4016.0, 6, "Saturation value");
+
+  fh_set_flt(hu, 220, "EXPTIME", etime, 5, "Integration time (seconds)");
+  sprintf(str, "%d %d", binmode + 1, binmode + 1);
+  fh_set_str(hu, 230, "CCDSUM", str, "Binning factors");
+  fh_set_int(hu, 231, "CCDBIN1", binmode + 1, "Binning factor along first axis");
+  fh_set_int(hu, 232, "CCDBIN2", binmode + 1, "Binning factor along second axis");
+
+  fh_set_com(hu, 1400.0, "COMMENT", "");
+  fh_set_com(hu, 1400.1, "COMMENT", " SBIG status record:");
+  fh_set_com(hu, 1400.2, "COMMENT", "");
+  if (sbstat.imaging_ccd_status==0)
+    fh_set_str(hu, 1500, "DETSTAT", "ok", "(SBIG imaging_ccd_status is 0)");
+  else
+    fh_set_int(hu, 1500, "DETSTAT", sbstat.imaging_ccd_status, "SBIG error!");
+  fh_set_flt(hu, 1500.1, "DETTEM", sbstat.ccd_temperature/10., 3, "Detector temperature");
+  fh_set_int(hu, 1500.2, "DETTEMRG", sbstat.temperature_regulation, "1=set temp 0=set power");
+  fh_set_int(hu, 1500.3, "DETTEMSP", sbstat.temperature_setpoint, "Temperature setpoint");
+  fh_set_int(hu, 1500.4, "DETTEMCP", sbstat.cooling_power, "Cooling power");
+  fh_set_flt(hu, 1510,  "CAMTEM", sbstat.air_temperature/10., 3, "Outside air temperature");
+  fh_set_com(hu, 1600.0, "COMMENT", "");
+  fh_set_com(hu, 1600.1, "COMMENT", " SBIG info record:");
+  fh_set_com(hu, 1600.2, "COMMENT", "");
+  sprintf(str, "%u", info.firmware_version);
+  fh_set_str(hu, 1600.9, "CONSWV", str, "SBIG firmware version");
+  fh_set_str(hu, 1601,"DETECTOR", info.camera_name, info.serial_number);
+  fh_set_bool(hu, 1700, "ABGON", info.imaging_abg_type, "Antiblooming gate present");
+  fh_set_int(hu, 1701, "ABGMODE", info.imaging_abg_type, "Antiblooming gate control value");
+  fh_set_int(hu, 1800, "BADCOL", info.nmbr_bad_columns, "Number of bad columns");
+  for (i = 0; i < info.nmbr_bad_columns; ++i){
+    sprintf(str, "BADCOL%d", i);
+    fh_set_int(hu, 1800 + 0.1 + 0.1 * i, str, info.bad_columns[i], "Bad column");
+  }
+  fh_set_int(hu, 1901, "ST5ADSIZ", info.ST5_AD_size, "");
+  fh_set_int(hu, 1902, "ST5FLTYP", info.ST5_filter_type, "");
+
+  if (fh_write(hu, STDOUT_FILENO) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  if (fh_write_padded_image(hu, STDOUT_FILENO, data, w*h*sizeof(unsigned short), FH_TYPESIZE_RAW) != FH_SUCCESS)
+    exit(EXIT_FAILURE);
+
+  gprint (GP_ERR, "logonly: Wrote %d pixels, min=%g, max=%g\n", w*h, datamin, datamax);
+  sync();
+  return 0;
+}
+
+static void show_format(void){
+  gprint (GP_ERR, "Format for sp_ccdcontrol, SkyProbe CCD controller:\n"
+	  "Writes FITS file to stdout, status messages to stderr.\n"
+	  "NOTE: -port {hex} is an option for all forms of sp_ccdcontrol,\n"
+	  "  to specify a hex adress for the parallel port.\n"
+	  "sp_ccdcontrol expose -etime {Exp time}\n"
+          "   [-binmode {Binning mode}]\n"
+          "   [-shuttermode {Shutter mode}]\n"
+          "   [-raster X Y W H]\n"
+	  "Exp time: Exposure time given in seconds.\n"
+	  "Binning mode: 0=1x1, 1=2x2, 2=3x3.  Note, 3x3 not allowed in -track mode.\n"
+	  "   OR \n"
+	  "sp_ccdcontrol cool {coolmode} [temp|power]\n"
+	  "coolmode: 0=OFF, 1=ON, 2=Direct Drive\n"
+	  "For coolmode=0: Do not specify temp or power\n"
+	  "For coolmode=1: Specify temp in deg C\n"
+	  "For coolmode=2: Specify power (0-255)\n");
+  exit(EXIT_FAILURE);
+}
+
+
+int main(int argc, char *argv[])
+{
+  /*    char buffer[80], last_command[80]; */
+  int  ret = -1, i, port;
+  int  is_small_camera;
+    
+  /* Simon Kras--command line variables */
+  char cmd[80];
+  double etime;
+  int binmode;
+  int shuttermode;
+  int coolmode;
+  int raster_x, raster_y, raster_h, raster_w;
+  double cooltemp, coolpwr;
+
+  if(argc>1) strcpy(cmd, argv[1]);
+  else show_format();
+
+  port = 1; /* 0x378 */
+  for (i = 1; i < argc; ++i) {
+    if (!strcmp(argv[i], "-port")) {
+      sscanf(argv[++i], "%x", &port);
+      gprint (GP_ERR, "port override to 0x%x\n", port);
+    }
+  }
+
+  for (i = 0; i < 4; ++i) {
+    sync();
+    /* set our verbosity to 0 (range is 0 to 5) */
+    ret = sbig_init(port, 0, &info);
+    if (ret == -6) ret = 0; /* %%% It always returns this error, but actually it worked.
+			     * Since the library is binary only, we can't fix it.
+			     */
+    if (ret == 0) break;
+    gprint (GP_ERR,"logonly: sbig_init() failed: %s\n", sbig_show_error(ret));
+    usleep(100000);
+  }
+  if (ret < 0) {
+    gprint (GP_ERR, "error: cannot open camera, giving up\n");
+    exit(EXIT_FAILURE);
+  }
+
+  is_small_camera = (strstr(info.camera_name, "237") != NULL ||
+		     index(info.camera_name, '5') != NULL);
+  for (i = 0; i < info.camera_info[0].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+    rmp = &info.camera_info[0].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: ImagingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n", rmp->mode, rmp->width,
+	    rmp->height,  0.01*rmp->gain, 0.001*rmp->pixel_width, 
+	    0.001*rmp->pixel_height);
+#endif
+  }
+
+  for (i = 0; i < info.camera_info[1].nmbr_readout_modes; ++i) {
+    struct readout_mode  *rmp;
+
+    rmp = &info.camera_info[1].readout_mode[i];
+#ifdef DEBUG
+    gprint (GP_ERR, "debug: TrackingCCD mode %d: w=%d h=%d "
+	    "gain=%f "
+	    "pw=%g ph=%g\n"
+	    , rmp->mode, rmp->width, rmp->height,
+	    0.01*rmp->gain,
+	    0.001*rmp->pixel_width, 0.001*rmp->pixel_height);
+#endif
+  }
+  if(!strcmp(cmd, "expose")){
+    etime=-999.;
+    for(i=1;i<argc;++i){
+      if(!strcmp(argv[i], "-etime")){
+	if(argc>(i+1)) etime = atof(argv[++i]);
+	else
+	  gprint (GP_ERR, "No argument given to -etime\n");
+      }
+    }
+    if(etime==-999.){
+	gprint (GP_ERR, "Must specify exposure time.\n");
+	exit(EXIT_FAILURE);
+      }
+      
+      binmode=-999;
+      for(i=1;i<argc;++i){
+	if(!strcmp(argv[i], "-binmode")){
+	  if(argc>(i+1)) binmode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -binmode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      if(binmode==-999) binmode=0;
+
+      shuttermode=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-shuttermode")){
+	  if (argc>(i+1)) shuttermode = atoi(argv[++i]);
+	  else{
+	    gprint (GP_ERR, "No argument given for -shuttermode\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+
+      raster_x=0;
+      raster_y=0;
+      raster_w=0;
+      raster_h=0;
+      for (i=1; i<argc;++i){
+	if (!strcmp(argv[i], "-raster")){
+	  if (argc>(i+4)) {
+	    raster_x = atoi(argv[++i]);
+	    raster_y = atoi(argv[++i]);
+	    raster_w = atoi(argv[++i]);
+	    raster_h = atoi(argv[++i]);
+	  }
+	  else{
+	    gprint (GP_ERR, "-raster requires 4 arguments: x y w h\n");
+	    exit(EXIT_FAILURE);
+	  }
+	}
+      }
+      
+      take_picture(etime, binmode, shuttermode, raster_x, raster_y, raster_w, raster_h);
+
+  } else if(!strcmp(cmd, "cool")){
+    double  f;
+    struct sbig_cool  cool;
+    
+    if(argc<3){
+      gprint (GP_ERR, "Too few arguments for cool mode\n");
+      exit(EXIT_FAILURE);
+    } 
+    coolmode = atoi(argv[2]);
+    
+    if(coolmode<0 || coolmode>2){
+      gprint (GP_ERR, "Cooling mode values allowed: [0 Off, 1 *On, 2 DirectDrive]\n");
+      exit(EXIT_FAILURE);
+    }
+    cool.regulation = coolmode;
+    cool.direct_drive = 0;
+    if (cool.regulation == 2){
+      if(argc<4){
+	gprint (GP_ERR, "Must specify cooling power (0-255)\n");
+	exit(EXIT_FAILURE);
+      }
+      coolpwr=atof(argv[3]);
+      if(coolpwr<0 || coolpwr>255){
+	gprint (GP_ERR, "Cooling power must be 0-255.\n");
+	exit(EXIT_FAILURE);
+      }
+      cool.direct_drive = coolpwr;
+    }
+    else if (cool.regulation == 1) {
+      if(argc<4){
+	gprint (GP_ERR, "Must specify temperature in deg C\n");
+	exit(EXIT_FAILURE);
+      }
+      cooltemp=atof(argv[3]);
+      if(cooltemp<-50 || cooltemp>20){
+	gprint (GP_ERR, "Cooling temperature must be -50 to 20 deg C.\n");
+	exit(EXIT_FAILURE);
+      }
+      f = cooltemp;
+      i = 10.0*f + 0.5;
+      cool.temperature = i;
+    }
+    sync();
+    ret = sbig_set_cooling(&cool);
+    if (ret < 0){
+      gprint (GP_ERR, "sbig error: %s\n", sbig_show_error(ret));
+    }
+  }else{
+    gprint (GP_ERR, "Illegal command: '%s'\n", cmd);
+    show_format();
+  }
+  exit(EXIT_SUCCESS);
+}
+ 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/telescope.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/telescope.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/telescope.c	(revision 17422)
@@ -0,0 +1,236 @@
+# include "dimm.h"
+
+double distSky (double r1, double r2, double d1, double d2);
+
+int telescope (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "telescope on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "cmd")) {
+
+    int status;
+    char *answer = (char *) NULL;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    status = SerialCommand (argv[2], &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "ack")) {
+
+    int status;
+    char line[32], *answer;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    line[0] = 0x06;
+    line[1] = 0;
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "slew")) {
+
+    double ra, dec;
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope slew (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+    while (ra  < 360.0) ra += 360.0;   
+    while (ra  > 360.0) ra -= 360.0;   
+
+    status = gotoRD (ra, dec);
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "coords")) {
+
+    int status;
+    double ra, dec;
+    char line[64];
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope coords\n");
+      return (FALSE);
+    }
+
+    if (!getRD (&ra, &dec)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", ra, dec);
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    dms_format (line, (ra/15.0));
+    set_str_variable ("Rs", line);
+    dms_format (line, dec);
+    set_str_variable ("Ds", line);
+
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "altaz")) {
+
+    int status;
+    double x, y;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope altaz\n");
+      return (FALSE);
+    }
+
+    if (!getXY (&x, &y)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", x, y);
+    set_variable ("ALT", x);
+    set_variable ("AZ", y);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "site")) {
+
+    int status;
+    double lon, lat, LST;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope site\n");
+      return (FALSE);
+    }
+
+    if (!getSite (&lon, &lat, &LST)) return (FALSE);
+    gprint (GP_ERR, "%f %f  %f\n", lon, lat, LST);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    set_variable ("LST", LST);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setsite")) {
+
+    double lon, lat;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope setsite (name) (longitude) (latitude)\n");
+      return (FALSE);
+    }
+
+    lon = atof (argv[3]);
+    lat = atof (argv[4]);
+    if (!setSite (argv[2], lon, lat)) return (FALSE);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "settime")) {
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope settime (lst)\n");
+      return (FALSE);
+    }
+
+    if (!setTime (argv[2])) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "verbose")) {
+
+    int mode;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope verbose (mode)\n");
+      return (FALSE);
+    }
+
+    mode = atoi (argv[2]);
+    SerialVerbose (mode);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setcoords")) {
+
+    int status;
+    double ra, dec;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope setcoords (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+
+    if (!setRD (ra, dec)) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "offset")) {
+
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope offset (direction) (distance)\n");
+      gprint (GP_ERR, "  direction : x or y\n");
+      gprint (GP_ERR, "  distance  : +arcmin or -arcmin\n");
+      return (FALSE);
+    }
+    status = offset (argv[2], atof (argv[3]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "toffset")) {
+
+    int status;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope toffset (direction) (rate) (duration)\n");
+      gprint (GP_ERR, "example: telescope toffset x RC 1.5\n");
+      return (FALSE);
+    }
+    status = toffset (argv[2], argv[3], atof(argv[4]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "telescope init port - set serial port (eg, /dev/ttyS0)\n");
+  gprint (GP_ERR, "telescope site - get site information\n");
+  gprint (GP_ERR, "telescope altaz - g\n");
+  gprint (GP_ERR, "telescope setcoords (ra) (dec)\n");
+  gprint (GP_ERR, "telescope setsite (name) (longitute) (latitude)\n");
+  gprint (GP_ERR, "telescope coords\n");
+  gprint (GP_ERR, "telescope slew (ra) (dec)\n");
+  gprint (GP_ERR, "telescope offset (direction) (duration)\n");
+  gprint (GP_ERR, "telescope toffset (direction) (rate) (duration)\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dimm/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dimm/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dimm/version.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "dimm.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/ChangeLog.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/ChangeLog.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/ChangeLog.txt	(revision 17422)
@@ -0,0 +1,207 @@
+
+2006.10.04:
+  opihi-2-8    added output names, gprintGetName, '-current' to 'output'
+	       removed prototypes from old 'output' code
+	       fixed memory leak in evaluate_stack
+	       added global PID variable
+	       added quiet mode to coords
+	       added various memleak tests, other tests
+	       fixed error in buffer/vector selection for dimenup
+	       fixed counting error in fit2d (missing 1 row of matrix!)
+	       added normalization to kern
+	       cleaned CreateBuffer function, usage
+	       
+  mana-1-6     removed prototypes from old 'output' code
+	       cleaned CreateBuffer function, usage
+
+  pantasks-0-6 added TASK_STDOUT, TASK_STDERR to pantasks.h
+	       added stdout, stderr output targets for tasks/jobs
+	       cleaned CreateBuffer function, usage
+
+  dvo-0-6      cleaned CreateBuffer function, usage
+
+2006.08.23:
+  opihi-2-7    converted to gfits APIs (forces libfits 1.6)
+	       consolidated style argument parsing
+	       added style arguments to astro plotting
+	       added support for pantasks_client, pantasks_server
+	       added more complex keys to queue operations
+	       added gprint to allow buffered printing in threads
+	       updates to test framework for more flexibility
+	       added time conversions sec_to_hms, sec_to_day
+	       added SetLimitsRaw
+	       added isort_pair
+	       added VarConfigEntry
+	       added strip_version to clean version output
+	       moved gaussj to libohana
+	       moved main out of opihi to mana.c, etc
+
+  mana-1-5     converted to gfits APIs (forces libfits 1.6)
+	       added option for threaded shell/backend (not used)
+
+  dvo-0-5      converted to gfits APIs (forces libfits 1.6)
+      	       consolidated style argument parsing
+	       converted to new DVO APIs (dvo_catalog_xxx)
+	       added fitsed
+	       improvements to fitcolors
+	      
+  pantasks-0-5 converted to gfits APIs (forces libfits 1.6)
+	       added 'option' task command
+	       added limits on Njobs for time ranges
+	       fixed time ranges
+	       created client/server version
+	       added controller run/stop commands
+	       added controller help
+	       added server help
+
+  pcontrol-0-5 converted to gfits APIs (forces libfits 1.6)
+	       code re-org to make thread safe
+	       added multithread option (user/client threads)
+	       liberal use of ASSERTs
+
+  pclient-0-5  converted to gfits APIs (forces libfits 1.6)
+  dimm-0-4     converted to gfits APIs (forces libfits 1.6)
+
+2006.03.26:
+  opihi-2-6 : added 'close' function for devices
+	      added test framework  
+	      added tests to cmd.basic, cmd.data
+	      added $?var syntax (variable existence test)
+	      fixed broken a[3] = 5 assignment
+	      added PopQueueMatch function
+	      added periodogram.c
+	      improvements to dimendown, peak
+	      added fixwrap for roll-over on saturation
+
+  dvo-0-4     added proper-motion, parallax
+	      dropped _PS from Stars.R,D
+	      fixed configuration flags for readline/curses
+
+  mana-1-4     fixed configuration flags for readline/curses
+  pcontrol-0-4 fixed configuration flags for readline/curses
+  pclient-0-4  fixed configuration flags for readline/curses
+
+  pantasks-0-4 fixed configuration flags for readline/curses
+	       changed name from psched
+
+
+
+- opihi-2-5
+  * major work to switch to libkapa
+  * revised init sequence (general_init, program_init, startup)
+
+- mana 1.3
+  * changes to support new init sequence
+
+- dvo 0.3
+  * major changes to support SkyRegions
+  * changes to support libkapa
+  * changes to support new init sequence
+
+- psched 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pcontrol 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- pclient 0.3
+  * changes to support new init sequence
+  * changes to support libkapa
+
+- dimm 0.2
+  * changes to support new init sequence
+  * changes to support libkapa
+
+dimm-0-1: release 2005.10.20
+  first tagged dimm release
+
+pclient-0-2: release 2005.10.20
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+pcontrol-0-2: release 2005.10.20
+  added run/stop commands
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+psched-0-2: release 2005.10.20
+  work to improve / test throughput
+  minor cleanups
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+dvo-0-2:  release 2005.10.20
+  substantial changes to work with new dvo load functions
+  support for mode/format changes
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+mana-1-2:  release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+
+opihi-2-4: release 2005.10.20
+  minor changes to use new libohana (v1.5) / libfits (v1.4)
+  kapa/kii cursor interaction modified (needs kapa v1.3, kii v1.3)
+
+opihi-2-3: release 2005.08.15
+  first tagged release tarball
+  fixed up versions commands
+  added list types for psched 
+  moved to Makefile.Common method.
+
+opihi-2-2:
+  fixed up inclusion of dvo.h (conflicted names)
+  added Graph commands functions
+  added mosaic astrometry support
+  a variety of memory alloc bugfixes
+  added support for ohana_allocate functions
+  added real multicommand support (correct parsing of variables)
+  added some object modelling functions
+  added clipping to fits and stats
+  added gaussdeviate
+  added extrapolation to minterp
+  default of EXTNAME for extension keyword
+  added -cpt to various dvo functions
+  better help in dvo functions
+  fixed up lockfile APIs
+  added fitcolors
+  added lightcurve
+  some cleanups in dvo/photometry.c
+  added typefrac tests is photometry.c
+  added gain test for mrqmin
+  fixed up precedence in math
+  macro-ized binary/unary math ops
+  added modulo, erf
+
+opihi-2-1:
+  major reorg of imfit / added imfit functions
+  cleaned up get_argument calls so -Wall does not give errors
+  moved the 'elixir' command to dvo
+  tag added only to elixir implementations (mana + dvo)
+  dimm - need to coordinate with Olivier and Bernt
+  dvo2, pcontrol, pclient, sched - all under development
+
+2004.12.23
+
+  Moved typename/modename funcs to libohana.  this was motivated by
+  imregister, which would not compile cleanly using the old method.
+
+  Added library dependencies to the makefiles. previously, mana (etc)
+  would not re-build if the library was modified.
+
+  Fixed incorrect exit on non-tty.  this kept perl scripts with the
+  call "|mana" from running.
+
+  Fixed dvomath for strings.  since we moved to dvomath everywhere,
+  this broke complex string conditionals: (($a == hi) && ($b == lo)).
+  I added string types to the math functions, and tests to see if the
+  stack has strings which cannot be resolved as a vector or matrix.  
+
+2004.12.10:
+
+  fixed problem in lib.data/PlotVectors.c and lib.data/graphtools.c
+  that broke plotting for data with very large or small exponents (|x|
+  < 1e-12, |x| > 1e12).  also fixed cmd.data/vstats.c with same
+  problem.
+	
+opihi-2-0:
+  import to CVS 2004.12.03
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/changes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/changes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/changes.txt	(revision 17422)
@@ -0,0 +1,54 @@
+Oct 19  2003 misc/startup.c              . minor changes, DONE
+Oct 19  2003 misc/conversions.c		 . minor changes, DONE
+Oct 19  2003 misc/match_image.c		 . copied to dvo
+Apr 19 17:41 misc/ConfigInit.c		 . minor changes, kept dvo version
+May  6 16:13 misc/ImageOps.c		 . minor changes, DONE
+May 12 09:00 misc/photometry.c           . major changes : DONE
+
+Oct 15  2003 user/badimages.c            . no change
+Oct 15  2003 user/imlist.c		 . no change
+Oct 15  2003 user/precess.c		 . dvo version ok
+Oct 15  2003 user/delete.c		 . dvo version ok
+
+May  6 15:25 user/extract.c		 . mostly deprecated (transfer USNO/HST/etc versions)
+May 10 21:13 user/mextract.c		 . used status version + vectors
+May 11 12:06 user/avextract.c		 . used status version + vectors
+Oct 27  2003 user/imextract.c		 . used status version + vectors
+
+May 12 09:06 user/dmags.c		 . used status version + vectors
+May 12 09:12 user/dmagmeas.c		 . used status version + vectors
+May 12 09:13 user/dmagaves.c		 . used status version + vectors
+May 11 16:18 user/ddmags.c		 . used status version + vectors
+
+May  2 11:14 user/calextract.c		 . used status version + vectors 
+May  3 20:08 user/calmextract.c		 . used status version + vectors
+Oct 19  2003 user/cmd.c			 . fixed (photcodes vs vectors)
+Oct 19  2003 user/ccd.c			 . used status version + vectors
+
+Apr 19 19:48 user/gimages.c		 . minor changes, fixed
+Nov 14  2003 user/gstar.c		 . minor changes, fixed
+May  3 20:49 user/gtypes.c		 . dvo version ok
+May  6 09:37 user/images.c		 . copied to dvo
+May  3 20:49 user/imdata.c		 . dvo version ok
+May  3 20:50 user/imphot.c		 . dvo version ok
+May  3 20:51 user/imrough.c		 . dvo version ok
+May  3 20:51 user/imsearch.c		 . dvo version ok
+Nov  5  2003 user/photcodes.c		 . copied to dvo
+Nov  1  2003 user/pmeasure.c		 . dvo version ok
+Mar  3 11:25 user/showtile.c		 . copied to dvo
+Nov  1  2003 user/subpix.c		 . copied to dvo
+Oct 30  2003 user/dmt.c			 . minor changes & photcodes
+Nov  1  2003 user/lcurve.c		 . minor changes & photcodes
+
+May 18 09:50 user/cgrid.c		 . copied to dvo
+
+Nov  1  2003 user/abszero.c		 . deprecated
+May  3 20:51 user/zeropts.c		 . deprecated
+Nov 13  2003 user/resid.c		 . deprecated
+Nov 13  2003 user/photresid.c		 . deprecated
+
+May  2 11:20 user/cals.c		 . deprecated?
+May  3 20:37 user/ddmagextract.c	 . deprecated?
+May  3 20:11 user/dmagextract.c		 . deprecated?
+Oct 19  2003 user/ccdextract.c		 . deprecated?
+Oct 19  2003 user/cmdextract.c		 . deprecated?
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/command.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/command.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/command.txt	(revision 17422)
@@ -0,0 +1,24 @@
+
+readline: allocates a line and returns it; empty lines return NULL
+
+expand_vars: (recursively) expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+expand_vectors: expand expressions of the form $X
+	     frees the input line, allocates a new one.
+	     returns NULL on error (including NULL input)
+
+parse performs math expansion on the line, returning a new line (old one is freed)
+
+
+in newmath:
+
+isolate_elements converts argc,argv to cstack,Ncstack (argc,argv NOT freed)
+convert_to_RPN takes cstack,Nstack and converts to stack,Nstack; input cstack,Ncstack are freed
+check_stack puts pointers to data types on stack, no data allocated
+evaluate_stack 
+
+
+command parses a single input line (line) and expands
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/coords.list
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/coords.list	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/coords.list	(revision 17422)
@@ -0,0 +1,47 @@
+./cmd.astro/cgrid.c - OK (graph only)
+./cmd.astro/cplot.c  - OK (graph only)
+./cmd.astro/czplot.c - OK (graph only)
+./cmd.data/cursor.c - OK (graph only)
+./dvo/catalog.c - OK (graph only)
+./dvo/pcat.c - OK (graph)
+./dvo/pmeasure.c - OK (graph)
+./dvo/procks.c - OK (graph)
+./dvo/showtile.c - OK (graph)
+./cmd.astro/coords.c - fixed
+./dvo/gimages.c - fixed
+./dvo/ImageOps.c - fixed
+./dvo/images.c - fixed
+./dvo/imdense.c - fixed
+./dvo/imextract.c - fixed
+./dvo/imlist.c - fixed
+./dvo/imstats.c - fixed
+./dvo/photometry.c - fixed
+./dvo/subpix.c - fixed
+./dvo/get_regions.c - fixed
+
+./cmd.astro/drizzle.c - needs work
+./cmd.astro/transform.c - needs work
+./cmd.data/load.c - needs work
+./cmd.data/point.c - needs work
+./cmd.data/tvgrid.c - needs work
+./dvo/cmpread.c - needs work (single image)
+./dvo/imbox.c - needs work (single image)
+./dvo/simage.c - needs work (single image)
+
+dvo commands / functions affected by WRP / DIS astrometry
+
+LoadImage - no
+badimages.c - yes : also, use LoadImages
+get_regions.c - ? : OK if we register DIS image
+gimages.c
+ImageOps.c        : need to make association with DIS
+images.c
+imbox.c
+imdense.c
+imextract.c
+imlist.c
+imstats.c
+photometry.c
+showtile.c
+simage.c
+subpix.c
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/dmagoptions.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/dmagoptions.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/dmagoptions.txt	(revision 17422)
@@ -0,0 +1,143 @@
+
+mextract from value [options]
+avextract from value [options]
+
+imextract value [options]
+
+dmags F1 - F2 : F3 [options]
+dmagmeas F1 - F2 : value [options]
+dmagaves F1 - F2 : value [options]
+
+cmd F1 - F2 : F3 [options]
+ccd F1 - F2 : F3 - F4 [options]
+
+calextract F1 - F2 [options]
+calmextract F1 - F2 [options]
+
+options:
+
+ -photcode value
+  for avextract and dmagaves, -photcode defines the which code is used
+  to calculate certain values (mag, dmag, etc).  objects which lack a 
+  photcode return a logical NaN for that value.
+
+ -magrange min max
+
+this can apply to any photcode
+
+ -imaglim min 
+ -flag value
+ -time start stop
+ -fwhm (value)
+ -type (value)
+
+these naturally apply to the REF and DEP photcodes, but cannot apply to 
+to PRI and SEC.  (note that -type is interpretted a bit differently here
+from below. 
+
+ -chisq max
+ -errorlim max
+ -type (value)
+ -typefrac (type) (frac)
+ -nphot (value)
+ -ncode (value)
+ -fwhmfrac (value) (frac)
+
+these naturally apply to PRI and SEC photcodes, but cannot apply to
+REF.  For DEP, they could apply to the equivalent photcode.  eg
+
+  ccd I - 2MASS_J : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -chisq 2.0
+
+  this will exclude on chisq for the I value, and ignore the limit
+  for the 2MASS values, and exclude on R (equiv to CFH12K.R.00).
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop
+
+  by default, this will filter the CFH12K.R.00 and 2MASS measurements 
+  based on
+
+we need to be able to restrict the selections to a subset of the
+extraction values.  We can use a flag like: -apply YYNN:
+
+  ccd I - CFH12K.R.00 : 2MASS_J - 2MASS_K -time start stop -apply NYNN
+
+to restrict the application to only the CFH12K.R.00 entry.
+
+measure values:
+  ra
+  dec
+  mag
+  dmag
+  airmass
+  exptime
+  photcode
+  time
+  dR
+  dD
+  fwhm
+  dophot
+  FLAGS
+  XCCD
+  YCCD
+  XMOSAIC
+  YMOSAIC
+
+average values:
+  ra
+  dec
+  dmag
+  Nmeas
+  Nmiss
+  Xp
+  Xm
+  flag
+  type
+  typefrac
+  Nphot
+  Ncode
+
+
+
+- ExtractDMag
+  - ExtractMeasuresDMag
+    - TestAverage
+  - ExtractMagnitudes
+    - ExtractAverages
+    - ExtractMeasures
+
+mextract
+ - ExtractMeasures
+avextract
+ - ExtractAverages
+dmags
+ - ExtractDMag
+ - ExtractMagnitudes
+ddmags
+ - ExtractDMag
+ - ExtractDMag
+dmagmeas
+ - ExtractDMag
+ - ExtractMeasures
+dmagaves
+ - ExtractDMag
+ - ExtractAverages
+cmd
+ - ExtractDMag
+ - ExtractMagnitudes
+ccd
+ - ExtractDMag
+calextract
+ - ExtractAverages
+calmextract
+ - ExtractMeasures
+
+
+cmd B - V : B
+dmagmeas B - V : ra
+dmagmeas B - V : time
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-2.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-2.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-2.txt	(revision 17422)
@@ -0,0 +1,28 @@
+
+Region APIs
+
+Region table is a FITS table
+
+- load region data from FITS table
+- save region data to FITS table
+
+/* find region which overlaps c at given depth (-1 : max depth) */
+SkyRegion *SkyFindPoint (SkyRegion *db, SkyCoord c, int depth);
+
+/* find regions at all levels which overlap c */
+SkyRegion **SkyFindLevels (SkyRegion *db, SkyCoord c, int *Nregion);
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, *nlist);
+
+- I have created table generation functions which construct a
+  collection of table entries starting with the full sky and iterating
+  down N levels.  The function also fills out the names at the
+  appropriate levels.  This is currently ok up to level 6, but runs
+  out of memory at level 7.  This should not be the case, so I suspect
+  I'm not free'ing some memory.  
+
+- I need to create a function to take the GSC table list and construct
+  a set of SkyRegions that match the layout, starting with the full
+  sky and iterating down to and past the GSC tables.  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-apis.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-apis.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo-apis.txt	(revision 17422)
@@ -0,0 +1,89 @@
+
+skyregion APIs:
+
+// a single patch on the sky
+typedef struct {
+  float            Rmin;                 // 
+  float            Rmax;                 // 
+  float            Dmin;                 // 
+  float            Dmax;                 // 
+  int              childS;               // sequence number in full table of first child
+  int              childE;               // sequence number in full table of last child + 1
+  int              parent;               // sequence number in full table of parent
+  int              index;                // sequence number in full table of this entry
+  char             depth;                // depth of this entry
+  char             child;                // does this entry have children?
+  char             table;                // does this entry have a table?
+  char             name[21];             // name / filename
+} SkyRegion;
+
+/* SkyRegion : better implementation than GSCRegion */
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion *regions;
+} SkyTable;
+
+typedef struct {
+  int Nregions;
+  char **filename;
+  SkyRegion **regions;
+} SkyList;
+
+SkyList *SkyRegionByPoint (SkyTable *table, int depth, double ra, double dec);
+SkyList *SkyListByName (SkyTable *table, char *name);
+SkyList *SkyListByPoint (SkyTable *table, double ra, double dec);
+SkyList *SkyListByRadius (SkyTable *table, int depth, double RA, double DEC, double radius);
+SkyList *SkyListByPatch (SkyTable *table, int depth, SkyRegion *patch);
+SkyList *SkyListByImage (SkyTable *table, int depth, Image *image);
+SkyList *SkyListByBounds (SkyTable *table, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+SkyList *SkyListChildrenByBounds (SkyTable *table, int No, int depth, double Rmin, double Rmax, double Dmin, double Dmax);
+void SetImageCorners (double *X, double *Y, Image *image);
+int SkyTableSetDepth (SkyTable *sky, int depth);
+int SkyListFree (SkyList *list, int ELEMENTS);
+int SkyTableFree (SkyTable *table);
+int SkyListMerge (SkyList **outlist, SkyList *newlist);
+
+--- db query sky region ---
+
+// options for selecting the ra,dec limits of the db selections
+typedef struct {
+  char *name;
+  char *list;
+  int useDisplay;
+  int useSkyregion;
+} SkyRegionSelection;
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De);
+int set_skyregion (double Rs, double Re, double Ds, double De);
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection);
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv);
+
+
+--- photometry.c api ---
+
+* int GetTimeSelection (time_t *tz, time_t *te) {
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+int SetSelectionParam (int param) {
+int GetSelectionParam () {
+int GetMeasureParam (char *parname) {
+int GetAverageParam (char *parname) {
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+void GetAverageParamHelp () {
+int InitPhotcodes () {
+int ListPhotSelections () {
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+int GetMeasureTypeCode (Measure *measure) {
+int Quality (Measure *measure, int IsDophot) {
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n) {
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo.html
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo.html	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/dvo.html	(revision 17422)
@@ -0,0 +1,288 @@
+
+<h2> PA data requirements </h2>
+
+Counting the total size of the PA database for the PA survey, with
+roughly 20 pointings per location and 30 second exposures.  The depth
+for PA survey in P2 @ 20 \sigma is 21.3 (r).  Each detection uses 100
+bytes (<b>we need to flesh this out with more realistic numbers from
+our table of parameters</b>).  The access speed to a RAID disk is 100
+MB/sec.  The number density of stars in i' in the plane may be higher,
+but we reach saturation at ~1.4e6 detections per deg^2 (one object per
+100 pixels).  This is a factor of 5 higher than the raw numbers.
+
+<h3> stellar counts for PS-1 </h3>
+latitude                 90          30         0
+density (deg^{-2})      5e3         3e4       3e5
+N_det (FPA^{-1})        4e4         2e5       2e6
+Sum N_det (FPA^{-1})    8e5         4e6       4e7
+Sum Nbyte (FPA^{-1})  80 MB      400 MB      4 GB
+Nsec for 1 channel      0.8           4        40
+Nchannel for 2 sec        1           2        20
+
+The total number of bytes for the PA survey for P2 detections is ~12
+TB (30000 x 400MB).  The density of detections per FPA from P4 delta
+is roughly the same as P2 at 0 deg (2e5 vs 3e5 det deg^{-2}).  The
+total number of bytes needed to store the P4 Delta detections from all
+of PS-1 is 
+
+Fields in the plane will take longer to process
+
+<h2> addstar interactions for a distributed db </h2>
+
+addstar.client <---> addstar.daemon
+
+The sky is divided into hierarchical regions, each broken into smaller
+subregions.  Both the image and object tables are divided into
+subtables by region on the sky.  The density of image tables is
+smaller than the density of object tables.  A top-level table defines
+the distribution of the lower-level tables by defining the hierarchy
+of regions and subregions.  Each entry in this table contains:
+
+region ID  - an identifier for the subregion
+RAs, RAe   - RA range of region
+DECs, DECe - DEC range of region
+parent ID  - ID of the region which contains this region (NULL for allsky)
+Nchild     - number of children
+offset     - starting entry of first child
+images     - is this region used for images? (if FALSE, down one layer)
+objects    - is this region used for objects? (if FALSE, down one layer)
+
+use the machine name / file name as the indicator in the images and
+objects entries?
+
+The table defines the relationship between the subregions and provides
+a mechanism to find the subregions appropriate to a given sky
+location.  It also specified for a given depth if that depth is used
+for the image and/or the object table.  The image tables contain
+images whose reference coordinate is located in the given region.
+Other regions which the image overlaps contain entries in the image
+reference table which specify the primary image table in which they
+are stored.  
+
+The details of the sky region definitions do not matter for the
+structure of the region table.  The data structures can handle any
+arrangement of tables which meets the basic requirements that the
+boundaries be lines of constant RA & DEC and that each level defines
+all regions which cover the entire sky.  One implementation is as
+follows and is easy to generate:  
+
+Start with the complete sky as a single region (RAs = 0, RAe = 360;
+DECs = -90, DECe = 90).  To create a new region, always subdivide the
+region with the largest area (roughly (RAe - RAs)*(DECe -
+DECs)*cos((DECs + DECe)/2)).  To subdivide a region, define two
+possible subdivisions: RA = 0.5*(RAs + RAe) (which becomes RAs and RAe
+for the two new regions) or DEC = 0.5*(DECs + DECe).  Determine the
+length of these dividing lines (S1 = (DECe - DECs); S2 = (RAe - RAs) /
+cos (DEC)).  Choose the shorter of these two lines, and subdivide the
+region on that basis.  
+
+The number of subregions increases by a factor of 8 for
+each new level.  Approximate table size: Need at least 200,000
+lowest-level regions, leads to 2^18 regions (256k) in lowest-level.
+Total table size is roughly 300k rows.  Each row is roughly 32 bytes,
+for a table size of 10MB.
+
+There is one addstar.daemon per DVO server.  The addstar.daemons are
+responsible for serving the objects and images from the tables they
+contain.  
+
+addstar.client steps to load a new set of objects:
+- load region table (defined in config db)
+- find overlapping object regions
+- find overlapping image regions
+- identify primary image region
+- send image data to node for primary image region 
+- send image reference to nodes for secondary image regions
+- send detection data (with image ID) to corresponding object region nodes
+
+addstar.daemon responsibilities:
+- receive image data -> add to primary image table
+- receive detection data -> add to local region file
+- request objects in overlap regions from neighbor daemons
+- send detections to neighbor daemons if associated with object over
+  border
+- construct / update objects on basis of detections
+
+in DVO 1.0, addstar updates objects on every upload of the
+detections.  In fact, the construction of the objects need not be
+performed every time detections are added.  Do we need to construct
+objects for all single detections?  The current process is:
+
+- get list of new detections (from incoming image)
+- match each detection with each object
+- update object parameters
+
+another option:
+
+step 1:
+ - get list of new detections
+ - add to new.detection table
+
+step 2:
+ - match detections to object table
+ - update object parameters
+
+third option
+step 1:
+ - add new detections to new table
+
+step 2:
+ - compare detections to orphans 
+ - if found, promote to object
+ - if not, test against objects
+ - if found update object
+ - if not add to orphans
+
+<h2> DVO image organization </h2>
+
+DVO 1.0 uses a single image table to store all image data.  In this
+table, each image is a chip; that is, each entry represents a single
+astrometric system.  In principal, there is no reason this could not
+be an entire mosaic or (in the case of Pan-STARRS) an individual
+Cell. It is usually necessary to be able to define the relationship
+between the different detector / focal plane / etc coordinate
+systems.  If the data entity is a full mosaic, then it will be
+necessary to look up the chip (and cell) transformations.  If the data
+entity is the cell, the reverse conversions will be needed.  
+
+The image table should be distributed to multiple files to speed up
+the access.  One option is to do this by coordinate region.  In the
+plan for DVO 2.0, there will be a table of the region hierarchy for
+the object tables, and the image tables could be distibuted similarly,
+though with a different density.  In PS-1, for example, there will be
+around 250,000 images (FPAs).  If we store OTAs (chips) as the data
+entity in the image table, then we will have in the vicinity of 16M
+rows.  If each contains 256 bytes, the total data volume in the image
+table will be about 4 GB.  If we want to have typical access times to
+any image of 1 second, and we need to scan through the entire table to
+get to the image, then we will need to distribute the data across 40
+tables (note that they need not be distributed by machine).  If each
+table represents a region on the sky, this translates to 1000 - 500
+square degrees per table.  
+
+Some additional aspects are interesting.  First, the
+spatially-distributed tables correspond to specific regions on the
+sky, likely to be bounded by lines of constant RA and DEC, or
+something equivalent.  However, an image cannot be guaranteed to land
+in only one of these regions.  To mitigate this, each image (chip or
+FPA or whatever the data unit is) should have a single defined
+position with which the general location is identified and the choice
+of region is made.  In addition, there should be a table associated
+with each region which defines images in other tables (regions) which
+overlap the given region.  In general, these overlap tables will
+contain only a small fraction of the image entries (regions are much
+large than images) and they need only identify the image ID and the
+corresponding table.
+
+An additional accelerator table would include all images, their
+reference time and their reference coordinate, sorted by time, with an
+index for the name.
+
+The end result is three types of tables: images.db, overlaps.db,
+imagetimes.db.  images.db contains the bulk of the information (256
+byte / row).  overlaps.db contains only the image ID and the the RA
+and DEC of the reference position (not the actual table because that
+may change). imagetimes.db contains only image ID (8 byte? 16 byte?),
+time, RA, DEC, and and index.  This is a total of about 40 bytes per
+image, for a total of roughly 700 MB at the end of PS-1.
+
+<h3> Examples queries </h3>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h4> find a single, specific image by ID </h4>
+<ul>
+<li> open imagetimes.db
+<li> load block marker (ID,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+<li> determine appropriate image.db table (region)
+<li> open image.db 
+<li> load block marker (ra,block; every 10000 blocks)
+<li> find appropriate block
+<li> load block
+<li> find appropriate image entry
+</ul>
+
+<h3> dvo / object catalog scaling to massive collections </h3>
+
+DVO divides the sky into tables which represent specific areas on the
+sky.  Currently, these are pre-defined to match the HST GSC regions,
+roughly 1 - 4 square degree patches with fixed RA and DEC boundaries.
+A future extension will provide a mechanism to increaese or decrease
+the table density on the fly.
+
+The likely data rate under Pan-STARRS PS-1 is in the vicinity of 5 x
+10<sup>5</sup> stars per square degree.  The density is likely to vary
+by a factor of 30 for the bulk of the sky.  The Pan-STARRS camera
+consists of 1.44 x 10<sup>8</sup> pixels.  The confusion limit will
+likely be reached when each object encompases 25 pixels, resulting in
+a saturation of 6 x 10<sup>6</sup> stars per square degree.  The
+Pan-STARRS camera covers a total of 7.45 square degrees, and the
+expected exposure rate is roughly 1 per minute on average.  This
+amounts to a total of 3000 square degrees covered per night of
+observation, or a total of 1.5 x 10<sup>9</sup>detections per night,
+or a total of 5 x 10<sup>11</sup> over the course of one year.  Given
+this total number of detections, and the total sky coverage of 30,000
+square degrees, the average number of detections per square degree
+will be in the vicinity of 1.7 x 10<sup>7</sup>
+
+There are two main limitations to the DVO object storage model.
+First, the large data volume translates to a finite time to read the
+data from the disk.  Second, the large number of objects limits the
+rate at which new detections may be associated with the existing
+objects.  The first of these has generally proven to be the more
+significant limitation in applications to date.  We can easily
+calculate the time needed to load the data relevant to a random image
+pointing, as well as the maximum time based on the range of data
+volumes.  A necessary assumption in this calculation is the number of
+bytes used per detection.  We generously assume 100 bytes per
+detection, 3 times the existing data structures used by DVO.  
+
+Based on the numbers above, the total data volume represented by a
+year of observations is 50 TB, assuming the detection component
+dominates the total.  The average camera footprint translates to
+roughly 13 GB worth of detections.  Since each footprint is performed
+once per minute, it will be necessary to perform the read and write in
+each 10 seconds.  This in turn corresponds to 1.3 GB per second for
+the read and write portions.  With typical local hard-drive access
+speeds for RAIDs of 100 MB/sec, the data will need to be distributed
+across more than the machines in order to achieve these average
+rates.  
+
+The existing DVO system expects only one object table to be open at a
+time.  In addition, access to the tables is not controlled through
+separate machines.  Rather, a single collection of tables is expected
+and all queries are performed independently.  In order to handle the
+above load with the DVO software, the following changes will be
+needed:
+
+<ul>
+<li> tables must be distributed across multiple machines.  This is
+mandatory regardless of the underlying database engine based on the
+data I/O analysis above.
+
+<li> the 'addstar' front end needs to send the data for each table to
+the database backends on each of the corresponding machines.  
+
+<li> the addstar update for a single image can be done in series as is
+currently done.
+
+<li> write locking should be done on the tables rather than on the
+database as a whole (currently using the image table). 
+</ul>
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/ippc_commandserver.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/ippc_commandserver.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/ippc_commandserver.c	(revision 17422)
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include "ippc.h"
+#include "ippc_messages.h"
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "ippc_command.h"
+
+#define MAX_KIDS    256
+#define WAIT_FOR_KIDS    5
+
+/** \file ippc_commandserver.c
+ * \brief This is basically a big fat security problem.
+ */
+
+/* global variables */
+int    kids = 0;
+
+void  decrement_kids(int sig_num);
+
+int main(int argc, char *argv[])
+{
+  int    server_sockfd, client_sockfd, server_len, client_len;
+  int    result;
+  char    buffer[STRLEN], *portString;
+  uint16_t  netSafe;
+  uint32_t  auth_token;
+  unsigned short  status;
+  struct  sockaddr_in server_address;
+  struct  sockaddr_in client_address;
+
+  if (IPPC_DEFAULT_VERBOSITY > 1) {
+    gprint (GP_ERR, "%s : THIS PROGRAM _IS_ A REMOTE EXPLOIT!!!\n", argv[0]);
+  }
+
+  if (!(portString = getenv("IPPC_COMMANDSERVER_PORT"))) {
+    portString = "1035";
+  }
+
+  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (server_sockfd == 0) {
+                perror("socket");
+                exit(EXIT_FAILURE);
+        }
+
+  server_address.sin_family = AF_INET;
+  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
+  server_address.sin_port = htons(atoi(portString));
+  server_len = sizeof(server_address);
+
+  result = bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 
+        if(result == -1) {
+                perror("bind");
+                exit(EXIT_FAILURE);
+        }
+
+  result = listen(server_sockfd, 10);
+        if(result == -1) {
+                perror("listen");
+                exit(EXIT_FAILURE);
+        }
+  
+  /* decrement the kiddie count after a child exits */
+  signal(SIGCHLD, decrement_kids);
+
+  /* flush dem pesky chars outa my buffer */
+  memset(buffer, 0, sizeof(buffer));
+
+  while(1) {
+    if (IPPC_DEFAULT_VERBOSITY > 1) {
+      gprint (GP_ERR, "%s : pid: %d - waiting for connection...\n", argv[0], getpid());
+    }
+
+    if (kids < MAX_KIDS) {
+      client_len = sizeof(client_address);
+      client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
+    } else {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - kids: %d - child limit already reached, sleeping %ds...\n",
+          argv[0], getpid(), kids, WAIT_FOR_KIDS);
+      }
+      sleep(WAIT_FOR_KIDS);
+      continue;
+    }
+
+    /* increment kiddie count before forking */
+    kids++;
+
+    if (fork() == 0) {
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - new kid number: %d\n", argv[0], getpid(), kids);
+      }
+
+      /* a non-zero result doesn't always mean an error occured
+       * I need to find out more about how to handle error checking here
+       */
+
+      /* receive and check authentication key */
+      recv(client_sockfd, &auth_token, sizeof(auth_token), 0);
+
+      if (ntohl(auth_token) != COMMAND_KEY) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - invalid authentication key\n", argv[0], getpid());
+        }
+
+        netSafe = htons(-1);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+
+        close(client_sockfd);
+        exit(EXIT_FAILURE);
+      }
+
+      /* receive command */
+      recv(client_sockfd, buffer, STRLEN, 0);
+  
+      if (IPPC_DEFAULT_VERBOSITY > 1) {
+        gprint (GP_ERR, "%s : pid: %d - recieved: %s\n", argv[0], getpid(), buffer);
+      }
+  
+      /* oh man is this dangerous */
+      status = system(buffer);
+      if (status != 0) {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command failed - code: %d\n", argv[0], getpid(), status);
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(netSafe), 0);
+      } else {
+        if (IPPC_DEFAULT_VERBOSITY > 1) {
+          gprint (GP_ERR, "%s : pid: %d - command successful\n", argv[0], getpid());
+        }
+        netSafe = htons(status);
+        send(client_sockfd, &netSafe, sizeof(status), 0);
+      }
+
+      status = shutdown(client_sockfd, SHUT_RDWR);
+      if (status) {
+        perror("shutdown");
+        exit(EXIT_FAILURE);
+      }
+  
+      close(client_sockfd);
+      exit(EXIT_SUCCESS);
+    } else {
+      close(client_sockfd);
+    }
+  }
+}
+
+void  decrement_kids(int sig_num)
+{
+  /* sysv requires the handler to be reinstalled */
+  signal(SIGCHLD, decrement_kids);
+
+  /* reap child exit value */
+  while (waitpid(-1, NULL, WNOHANG) > 0) { kids--; }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/libs.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/libs.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/libs.txt	(revision 17422)
@@ -0,0 +1,17 @@
+
+I have divided the (non-user) functions into classes. 
+
+- conversions can be placed in a single library
+
+- graph and image (tv) functions can be placed in a library
+
+- the opihi shell functions can go into a third library
+
+- the dvo-related functions can go into a fourth
+
+- i need to clean up the mana and dimm misc functions, and put them in libraries 
+
+- the user functions also need to go into some libraries.
+
+- how do I have the library add its functions to the command list?
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/mana.updates.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/mana.updates.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/mana.updates.txt	(revision 17422)
@@ -0,0 +1,135 @@
+
+mana functions which existed in DVO from status:
+
+applyfit2d.c         : OK - vector changes only
+applyfit.c	     : OK - vector changes only
+box.c		     : OK - no diff
+center.c	     : OK - no diff
+clear.c		     : OK - no diff
+concat.c	     : OK - vector changes only
+contour.c	     : mana version writes to Ximage, dvo version writes to vectors.  mana/contour -> tvcontour.c
+create.c	     : OK - buffer changes only
+cursor.c	     : OK - buffer & minor changes only
+delete.c	     : OK - buffer & minor changes only
+device.c	     : OK - no diff
+extract.c	     : mana extract operates on buffers / status extract is db operation
+file.c		     : OK - no diff
+fit2d.c		     : OK - vector changes only
+fit.c		     : OK - added weights and parameter errors from mana version
+gaussj.c	     : OK - buffer changes only
+grid.c		     : mana version write to Ximage, dvo version writes to Xgraph. mana/grid -> tvgrid
+histogram.c	     : OK - vector changes only
+interpolate.c	     : OK - vector changes only
+jpeg.c		     : OK - no diff
+labels.c	     : OK - no diff
+limits.c	     : OK - vector & minor changes only
+list_buffers.c	     : OK - buffer changes only
+list_vectors.c	     : OK - vector & minor changes only
+mcreate.c	     : OK - buffer changes only
+mget.c		     : OK - buffer changes only
+mset.c		     : OK - buffer changes only
+ps.c		     : OK - no diff
+rd.c		     : OK - buffer changes only
+read_vectors.c	     : OK - included reading vectors from fits tables
+region.c	     : OK - no diff
+resize.c	     : OK - no diff
+section.c	     : OK - no diff
+set.c		     : OK - mana version out of date
+sprintf.c	     : OK - no diff
+stats.c		     : OK - included threshold comparison for IgnoreValue and min == max test
+style.c		     : OK - mana version out of date
+subset.c	     : OK - mana version out of date
+textline.c	     : OK - no diff
+tv.c		     : OK - buffer changes only
+uniq.c		     : OK - vector changes only
+vcontour.c	     : mana/vcontour equiv to dvo/contour
+vstat.c		     : OK - vector changes only
+wd.c		     : OK - buffer changes only
+write_vectors.c	     : OK - vector changes only
+zap.c		     : OK - buffer changes only
+zplot.c		     : OK - vector changes only
+
+mana functions not previously in DVO from status:
+
+accum.c			: fix vectors
+adc.c			: keep - specialized
+biassub.c		: fix buffers and vectors
+clip.c			: fix buffers and vectors
+cmpload.c		: keep
+coords.c		: fix buffers and vectors
+cut.c			: fix buffers and vectors
+cval.c			: ? superceeded by 'star'?
+demux.c			: keep - specialized
+dimendown.c		: fix buffers and vectors
+dimenup.c		: fix buffers and vectors
+drawline.c		: keep - unused
+drizzle.c		: fix buffers and vectors
+erase.c			: keep
+fft1d.c			: fix buffers and vectors
+fft2d.c			: fix buffers and vectors
+flux.c			: fix buffers and vectors
+gauss.c			: fix buffers and vectors
+gaussfit.c		: ? to correct?
+getchr.c		: keep
+getvel.c		: 
+imfit.c			: 
+imhist.c		: 
+integrate.c		: 
+kern.c			: 
+keyword.c		: 
+line.c			: 
+list_header.c		: 
+load.c			: 
+medacc.c		: 
+medianmap.c		: 
+memory.c		: 
+minterpolate.c		: 
+mkgauss.c		: 
+multifit.c		: 
+nextract.c		: 
+objload.c		: 
+outline2.c		: ??
+outline.c		: ??
+pause.c			: 
+peak.c			: 
+point.c			: 
+polar.c			: 
+profile.c		: 
+rdseg.c			: 
+rebin.c			: 
+roll.c			: 
+rotate.c		: 
+rotcurve.c		: 
+save.c			: 
+scale.c			: 
+select.c		: 
+sexigesimal.c		: 
+shape.c			: 
+shift.c			: 
+simsignal.c		: 
+sort_vectors.c		: 
+spec.c			: 
+spline_apply.c		: 
+spline_construct.c	: 
+star.c			: 
+strchr.c		: 
+strlen.c		: 
+substr.c		: 
+svd.c			: 
+swapbytes.c		: 
+testfit.c		: 
+transform.c		: 
+unsign.c		: 
+usleep.c		: 
+vbin.c			: 
+vclip.c			: 
+
+outdated or unneeded functions:
+
+dummy.c
+downdimen.c
+buftovec.c
+kernel.c
+updimen.c		: 
+stupidtest.c		: 
+plot2.c			: fixed GetColor in dvo/plot.c
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.dvo-data.html
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.dvo-data.html	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.dvo-data.html	(revision 17422)
@@ -0,0 +1,166 @@
+
+<h2> data structures in DVO </h2>
+
+DVO uses several data structures, defined in <tt>loneos.h</tt> to
+represent data in the database tables.  This document attempts to
+describe the actual usage, some of which may disagree with the comment
+entries in <tt>loneos.h</tt>.
+
+<b> Image </b>
+<table> 
+<tr><th> name               </th><th> type           </th><th> value                     </th><th> units              </th>
+<tr><td> coords             </td><td> Coords         </td><td> astrometry                </td><td>                    </td>
+<tr><td> tzero              </td><td> unsigned int   </td><td> readout time row 0        </td><td> unix time          </td>
+<tr><td> nstar              </td><td> unsigned int   </td><td> number of stars on image  </td><td>                    </td>
+<tr><td> secz               </td><td> short int      </td><td> airmass                   </td><td> 1000*airmass       </td>
+<tr><td> NX                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> NY                 </td><td> short int      </td><td> image size                </td><td> pixels             </td>
+<tr><td> apmifit            </td><td> short int      </td><td> aperture correction       </td><td> 1000*mag           </td>
+<tr><td> dapmifit           </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> source             </td><td> short int      </td><td> photcode                  </td><td>                    </td>
+<tr><td> Mcal               </td><td> short int      </td><td> relphot correciton        </td><td> 1000*mag           </td>
+<tr><td> dMcal              </td><td> short int      </td><td> error                     </td><td> 1000*mag           </td>
+<tr><td> Xm                 </td><td> short int      </td><td> image chi-square          </td><td> 100*log(chisq)     </td>
+<tr><td> name[32]           </td><td> char           </td><td> image name                </td><td>                    </td>
+<tr><td> detection_limit    </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> saturation_limit   </td><td> unsigned char  </td><td>                           </td><td> 10*mag             </td>
+<tr><td> cerror             </td><td> unsigned char  </td><td> astrometric error         </td><td> 50*arcsec          </td>
+<tr><td> fwhm_x             </td><td> unsigned char  </td><td> average FWHM X            </td><td> 25*arcsec          </td>
+<tr><td> fwhm_y             </td><td> unsigned char  </td><td> average FWHM Y            </td><td> 25*arcsec          </td>
+<tr><td> trate              </td><td> unsigned char  </td><td> drift rate                </td><td> 10000*sec/pix      </td>
+<tr><td> exptime            </td><td> float          </td><td> exposure time             </td><td> seconds            </td>
+<tr><td> code               </td><td> char           </td><td> data flags                </td><td>                    </td>
+<tr><td> ccdnum             </td><td> unsigned char  </td><td> mosaic CCD ID number      </td><td>                    </td>
+<tr><td> dummy[20]          </td><td> char           </td><td> extra                     </td><td>                    </td>
+<tr><td> order              </td><td> short int      </td><td> order of Mcal function    </td><td>                    </td>
+<tr><td> Mx                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> My                 </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxx                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myy                </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxx               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyy               </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxx              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxxy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxxyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Mxyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+<tr><td> Myyyy              </td><td> short int      </td><td> Mcal polynomial term      </td><td>                    </td>
+</table>
+
+<em> apmifit, dapmifit are needed for dophot data, but not sextractor data.  are these correctly handled? </em>
+<em> skyprobe used Mxxxx for Ncal and Myyyy for sky - can this be reconciled? </em>
+
+<h3> codes in Image</h3>
+<ul>
+<li> ID_IMAGE_NOCAL = 0x04 - internal to relphot 
+<li> ID_IMAGE_POOR  = 0x02 - set by relphot
+<li> ID_IMAGE_SKIP  = 0x04 - use by relphot (!!!)
+</ul>
+
+<b> Average </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units                 </th></tr>
+<tr><td> R          </td><td> float          </td><td> RA                  </td><td> decimal degrees J2000 </td></tr>
+<tr><td> D          </td><td> float          </td><td> DEC                 </td><td> decimal degrees J2000 </td></tr>
+<tr><td> M          </td><td> short int      </td><td>                     </td><td> 1000*mag		    </td></tr>
+<tr><td> Nm         </td><td> unsigned short </td><td> N measure	    </td><td> 			    </td></tr>
+<tr><td> Nn         </td><td> unsigned short </td><td> N missing	    </td><td> 			    </td></tr>
+<tr><td> Xp         </td><td> short int      </td><td> position scatter    </td><td> 100*arcsec	    </td></tr>
+<tr><td> Xm         </td><td> short int      </td><td> mag chisq           </td><td> 100*log(chisq)	    </td></tr>
+<tr><td> code       </td><td> unsigned short </td><td> data flags	    </td><td> 			    </td></tr>
+<tr><td> offset     </td><td> signed int     </td><td> measure offset	    </td><td> 			    </td></tr>
+<tr><td> missing    </td><td> signed int     </td><td> missing offset	    </td><td> 			    </td></tr>
+<tr><td> dM         </td><td> short int      </td><td> error on M          </td><td> 1000*log(value)	    </td></tr>
+<tr><td> Xg         </td><td> short int      </td><td> best chisq value    </td><td> ??		    </td></tr>
+</table>		  
+
+<h3> codes in Average</h3>
+<li> set to 0 by addstar, etc
+<li> ID_STAR_NOCAL        = 0x0001 - set by relphot
+<li> ID_PROPER            = 0x0400 - set by addusno, used by markrock
+<li> ID_BAD_DATE          = 0x0800 - deprecate (not currently set)
+<li> ID_TRANSIENT         = 0x1000 - not currently set? 
+<li> ID_VARIABLE          = 0x2000 - not currently set? 
+<li> ID_ROCK              = 0xa000 - set by markrock
+<li> ID_GHOST             = 0xc001 - set by fixcat, markstar
+<li> ID_TRAIL             = 0xc002 - set by fixcat, markstar
+<li> ID_BLEED             = 0xc003 - set by fixcat, markstar, markrock
+<li> ID_COSMIC            = 0xc004 - set by markrock
+</ul>
+
+<b> Measure </b>
+<table> 
+<tr><th> name       </th><th> type           </th><th> value               </th><th> units             </th>
+<tr><td> dR         </td><td> short int      </td><td> RA offset           </td><td> 100*arcsec        </td>
+<tr><td> dD         </td><td> short int      </td><td> DEC offset          </td><td> 100*arcsec        </td>
+<tr><td> M          </td><td> short int      </td><td> catalog mag         </td><td> 1000*mag          </td>
+<tr><td> Mcal       </td><td> short int      </td><td> image cal mag       </td><td> 1000*mag          </td>
+<tr><td> Mgal       </td><td> short int      </td><td> 'galaxy' mag,       </td><td> 1000*mag          </td>
+<tr><td> airmass    </td><td> short int      </td><td> (airmass - 1),      </td><td> 1000*airmass      </td>
+<tr><td> FWx        </td><td> short int      </td><td> fwhm major axis     </td><td> 100*arcsec        </td>
+<tr><td> dM         </td><td> unsigned char  </td><td> mag error           </td><td> 1000*mag          </td>
+<tr><td> fwy        </td><td> unsigned char  </td><td> minor/major ratio   </td><td>                   </td>
+<tr><td> theta      </td><td> unsigned char  </td><td> angle wrt ccd X dir </td><td> degree*(256/360)  </td>
+<tr><td> dophot     </td><td> char           </td><td> dophot type         </td><td>                   </td>
+<tr><td> source     </td><td> unsigned short </td><td> photcode            </td><td>                   </td>
+<tr><td> t          </td><td> unsigned int   </td><td> time                </td><td> unix time         </td>
+<tr><td> averef     </td><td> unsigned int   </td><td> average entry       </td><td>                   </td>
+<tr><td> dt         </td><td> short int      </td><td> exposure time       </td><td> 2500*log(exptime) </td>
+<tr><td> flags      </td><td> unsigned short </td><td> data flags          </td><td>                   </td>
+</table>
+
+<h3> flags in Measure</h3>
+<ul>
+<li> ID_MEAS_POOR         = 0x0001 - set by relphot, unset?
+<li> ID_MEAS_NOCAL        = 0x0002 - set by relphot, unset?
+<li> BLEND_IMAGE          = 0x0100 - set by addstar, etc
+<li> BLEND_CATALOG        = 0x0200 - set by addstar, etc
+<li> BLEND_IMAGE_NEIGHBOR = 0x1000 - set by addstar, etc
+<li> PART_OF_TRAIL        = 0x2000 - set by markstar, fixcat, used by markrock
+<li> GHOST_DATA           = 0x4000 - set by markstar, fixcat, used by markrock
+</ul>
+
+<em> relphot flag meanings: </em>
+<b> ID_IMAGE_NOCAL </b> : completely ignore image (internal only)
+<b> ID_IMAGE_SKIP  </b> : externally marked, don't include in Mrel, calculate Mcal (?)
+<b> ID_IMAGE_POOR  </b> : internally marked, don't include in Mrel, calculate Mcal (?)
+
+<b> ID_STAR_NOCAL  </b> : poor star, don't include in Mcal, calculate Mrel
+STAR_BAD == (average.Xm > STAR_CHISQ) || (average.dM > STAR_SCATTER) || (NVALID < MEAS_TOOFEW)
+
+<b> ID_MEAS_NOCAL  </b> : skip this measurement (temporary, internal)
+<b> ID_MEAS_POOR   </b> : poor star, don't include in Mcal, calculate Mrel
+MEAS_POOR == (3 sigma outlier) || (out of detector area)
+<em> currently NOT sticky!! </em>
+
+--
+
+uniphot.h:
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+relphot.h:
+# define ID_VARIABLE   0x2000
+# define ID_TRANSIENT  0x1000 /* is this mutually exclusive with USNO?  */
+# define ID_BAD_DATA   0x0800 /* stationary object with some bad data points */
+# define ID_PROPER     0x0400 /* star with large proper motion */
+
+# define MEAS_POOR     0x0001
+# define MEAS_NOCAL    0x0002
+# define ID_IMAGE_SKIP 0x0004
+
+<em> things to update, cleanup </em>
+<d> finish fix of photcode in imregister-3.0, etc </d>
+<d> move flag constants from relphot/uniphot/photdbc include -> loneos.h </d>
+<d> watch for use of Xp, Xg, code in photdbc & status.extract, status.gstar</d>
+<b> status.extract.NVALID : count !(measure.flag & MEAS_POOR) </b>
+<d> fix error with ID_IMAGE_NOCAL == ID_IMAGE_SKIP </d>
+<d> make sure ID_IMAGE_POOR is saved and ID_IMAGE_SKIP is kept, including with -mosaic </d>
+<d> should STAR_BAD be saved or not?  probably not: there is not one per photcode </d>
+<d> MEAS_POOR should be saved, MEAS_NOCAL should not </d>
+<b> recompile all dvo programs, test </b>
+
+<b> photdbc </b> <em> check on the use of flags, etc; consistent with relphot, uniphot? </em>
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.photcode.html
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.photcode.html	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.photcode.html	(revision 17422)
@@ -0,0 +1,221 @@
+
+<h2> A Primer on DVO Photcodes and Magnitude Representation </h2>
+
+<p>
+There are two types of DVO table which store magnitude data.  The
+Average table stores magnitudes which represent averages of multiple
+measurements.  The Measure table stores magnitudes which represent
+single measurements.  
+
+<p>
+Each type of entry is defined by a Photcode.  The individual Measure
+entries have their photcodes stored as part of the structure.  The
+Average entries have photcodes which are determined externally by
+their sequence.  For historical reasons, the Average photcode are
+divided into the Primary photcode (of which there may only be one) and
+Secondary (of which there may be several).  The average magnitude
+associated with the Primary photcode is stored with the Average
+structure while the Secondary photcodes are stored in a separate,
+associated table called Secfilt.  The layout of the Average and
+Secfilt tables, and the photcodes assigned to them, are defined when
+the database is created based on the layout of the photcode table.
+This table is defined with a single Primary and a number of Secondary
+photcodes.  When the database tables are created, primary photcode is
+automatically associated with the Average entry while the sequence of
+Secondary photcodes defines the sequence of the entries in the Secfilt
+table.  This is very fragile, but very fast for making the
+association.  Thus, changing the number or sequence of secondary
+photcodes would cause an existing database table to be
+mis-interpretted.  
+
+<p>
+The Measure entries are broken into two photcode types as well:
+internal and external measurements.  Internal measurements are those
+for which the data source is well understood; the instrumental
+magnitudes are available and the instrumental parameters which are
+required to determine a calibrated magnitude.  External measurements
+are provided by external sources: published catalogs, reference
+sources, etc.  For these measurements, there is no control over the
+instrumental parameters which determined the measurement, and in many
+cases the measurement represents an average of an ensemble.
+
+<p>
+The photcode table defines relationships between photcodes,
+representing different photometry systems.  Each photcode entry
+defines the target of the calibration (the equivalence), and it may
+potentially define the zero-point (Co), the airmass slope (Ko), the
+appropriate color terms (C1, C2), the reference color term (Color),
+and the polynomial coefficients of the color equation (starting with
+the 1st order term).  
+
+<p>
+I need to include operations to handle varying color terms between
+CCDs.  I have measured color terms between CCDs in the range +/- 0.1
+mag/mag.  This is an appropriate time to clean up some of the code
+that handles the photcodes, etc.  In particular, the photcode table
+should provide the relationship beween relate between filter systems.
+
+For all of the functions below, a REF photcode simply returns the
+stored magnitude without transformation.  
+
+PhotInst (measure)
+  returns the instrumental magnitude of the given Measure
+  Minst = measure[0].M - measure[0].dt - iZERO_POINT;
+
+PhotCat (measure)
+  returns the 'catalog' magnitude of the given Measure (best guess
+  given no other information like color):
+  Mcat = measure[0].M - iZERO_POINT + Klam*(measure[0].airmass - 1000) + photcodes[0].code[Np].C;
+
+PhotSys (measure, average, secfilt)
+  returns the 'system' magnitude of the given Measure (best guess
+  using existing color information, but without application of
+  relative photometry offsets):
+  Msys = Mcat + f(color)
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotRel (measure, average, secfilt)
+  returns the 'relative' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image).  This is the same photometry system as
+  the Average (PRI/SEC) measurement equivalent to this measurement
+  photcode:
+  Mrel = Msys + measure[0].Mcal;
+  color is derived from measure.photcode.c1,c2  (measure.source.c1,c2)
+
+PhotCal (measure, average, secfilt, allmeasures, code)
+  returns the 'calibrated' magnitude of the given Measure (best guess
+  using existing color information and current relative photometry
+  measurment for this image transformed to the appropriate reference
+  photometry system):
+  Mcal = Mref + f(color)
+  color is derived from code.c1,c2
+  the provided code must be either the measure.photcode.equiv or an
+  alternate for that code.
+
+<p>
+consider two filters, B & V, and two CCDs 00 & 01.  also, reference
+data in B_L92, V_L92
+
+<pre>
+    N  photcode     type  Co      Ko       X    C1    C2    Ao     Color  Equivalent
+  100  CFH12K.B.00  dep   26.000 -0.15     -    B     V     0.1    -      B
+  101  CFH12K.B.01  dep   26.000 -0.15     -    B     V     0.0    -      B
+
+  200  CFH12K.V.00  dep   26.100 -0.15     -    B     V     0.0    -      V
+  201  CFH12K.V.01  dep   26.100 -0.15     -    B     V     0.1    -      V
+
+    1  B            pri    0.000  -        -    B_L92 V_L92 0.016  -      B_L92
+    2  V            sec    0.000  -        -    B_L92 V_L92 0.008  -      V_L92
+
+  1003 B_L92        ref    -      -        -    -     -     -      -      -
+  1004 V_L92        ref    -      -        -    -     -     -      -      -
+  
+  B = m + Co + Ko*(secz - 1) + Ao*(B - V)
+  B_L92 = B + Co + color*A0 + color^2*A1 + color^3*A2
+</pre>
+
+<b>changes I'd like to make related to photcodes</b>
+
+<li> use structure value 'photcode', not 'source' everywhere
+<li> better abstraction / conceptualization of PhotMode values (Inst, Rel, Abs, Sys)
+<li> N-order polynomial for color-fits
+
+<b> relevant programs </b>
+
+<table>
+<tr><td>* addrefs    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addspphot  </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* addstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* delstar    </td><td> minor fixes, no conversions are used </td></tr> 
+<tr><td>* photcode   </td><td> probably ok, check is consistent with new (?) photcode structs </td></tr> 
+<tr><td>* photreg    </td><td> check consistency </td></tr> 
+<tr><td>* photsearch </td><td> check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>* relphot    </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>libohana     </td><td> update LoadPhotcodes funcs, check consistency, use photcode table to get Equivalent </td></tr> 
+<tr><td>photdbc      </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>status       </td><td> signficant changes to handle new structures </td></tr>
+<tr><td>dvo          </td><td> repeat fixes in status </td></tr> 
+
+<tr><td>lightcurve </td><td> unused? </td></tr>
+</table>
+
+<b> basic photcode APIs </b>
+
+int LoadPhotcodes (char *filename);
+void SetZeroPoint (double ZP);
+
+PhotCode *GetPhotcodebyName (name);
+int       GetPhotcodeCodebyName (name);
+PhotCode *GetPhotEquivbyName (name);
+int       GetPhotEquivCodebyName (name);
+
+PhotCode *GetPhotcodebyCode (code);
+char     *GetPhotcodeNamebyCode (code);
+
+PhotCode *GetPhotEquivbyCode (code);
+int       GetPhotEquivCodebyCode (code);
+
+double PhotInst (Measure *measure);
+double PhotAbs (Measure *measure);
+double PhotCat (Measure *measure);
+double PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
+double PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
+double PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+
+<b> examples </b>
+find measures which are of photcode equivalent to X
+if (GetPhotEquivCodebyCode (measure[0].source) == N1) { } 
+
+<b> code, entry, hashcode, hashNsec </b>
+entry = 1;
+photcode[entry].code = 100 
+photcode[entry].name = B
+hashcode[100] = 1;
+
+hashNsec is only valid for type PHOT_SEC:
+hashNsec[code] = Nsec entry in SecFilt table
+
+<b> note recursion relationships </b>
+hashcode[photcode[entry].code] = entry;
+photcode[hashcode[entry]].code = entry
+
+<b> outstanding questions / issues </b>
+
+<ul>
+<li> PhotSys needs to handle missing color values
+<li> define valid range for color?
+<li> double PhotFoo functions return what on NO_MAG?
+<li> PhotFoo functions need to check for valid input types:
+  <ul> 
+  <li> PhotInst - PHOT_DEP
+  <li> PhotAbs  - PHOT_DEP, PHOT_REF
+  <li> PhotCat  - PHOT_DEP, PHOT_REF
+  <li> PhotSys  - PHOT_DEP, PHOT_REF
+  <li> PhotRel  - PHOT_DEP, PHOT_REF
+  <li> PhotAve  - PHOT_PRI, PHOT_SEC
+  <li> PhotRef  - PHOT_PRI, PHOT_SEC
+  </ul>
+</ul>
+
+<b> phot definitions </b>
+PhotInst = Measure.M - dt - ZP 
+PhotCat  = Measure.M + K*(airmass-1) + C - ZP
+PhotSys  = Measure.M + K*(airmass-1) + C - ZP + X*color
+PhotRel  = Measure.M + K*(airmass-1) + C - ZP + X*color - Mcal  -> average.m
+PhotRef  = average.M + C + X*color 
+
+C: def ~ 26.0, pri ~ 0.0
+
+PhotAbs  = deprecate (measure.M + K*(airmass-1) + C - ZP
+
+<b> alt photcodes </b>
+
+photcodes of type ALT provide alternate calibration terms for a given
+photcode.  these codes are equivalent to primary/secondary codes, but
+no data is ever saved with this type.  There are only lookups between
+the photcode name and the photcode structure, never the photcode.code
+value, since that defines the photcode for which the given structure
+is an alternate relationship.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/notes.txt	(revision 17422)
@@ -0,0 +1,103 @@
+
+opihi improvements / fixes:
+
+* single math processing system : inline shell math used one set of
+  functions (shell/math.c), while the set = () used another set, and
+  logic used part of a third set.  This has been unified into a single
+  math handling function (newmath).  The new math expression handling
+  also returns temporary in-line vectors and matrix expressions.
+
+* global user variables (Buffers, Vectors) and other global variables
+  (outfile, tv parameters, graphic and image tool parameters) have
+  been encapsulated in fuctions to avoid the global data constructs.  
+
+* improved command exit status handling : in the past, a function
+  returning FALSE would halt all script executing, returning to the
+  top level of the command stack.  we now set the variable $STATUS
+  with the exit status and allow the the user to test the exit
+  status to choose an operation.  loops, macros, and input files can
+  use the 'break' and 'continue' functions to escape from the current
+  location.  calling break returns with an exit status of FALSE,
+  calling break returns immediately with an exit status of TRUE.
+
+* more extensive use of 'outfile' 
+
+* support for opihi scripts: there is now better interpretation of
+  command-line arguments to allow for embedded opihi scripts, ie
+  scripts with the dvo or mana interpretter named on the first line
+  with the prefix #! like a shell script.  The first command-line
+  argument is interpreted as a file to input while successive ones are
+  converted to variables $argv:0 - $argv:n  WARNING: in the past,
+  multiple input files could be specified on the command-line.  this
+  now must be specified with --load file arguments.
+
+* local variables: there is now support for local (vs global) opihi
+  variables.  any macro may have a variable declared as local with the
+  command 'local (var)', which may take a list of variables.  these
+  variables are stored internally in association with the macro name.
+  the command may include the option -static, in which case the
+  variable will retain its value on successive calls to the same
+  macro.
+
+- merge of all old opihi-type programs into a single code base 
+
+- group related functions into libraries
+
+- function names, etc, should come from a single function, now a
+  global include file.
+
+- update help files
+
+- update web pages
+
+--
+
+some notes: 
+
+- new vector interpretation is now somewhat slower, due to the multiple
+  assignments.
+
+- BinaryOp does not leak memory (tested)
+
+- 55 seconds for 1000 floating-pt operations on a 4Mpix (3 GHz) -> 0.2
+  seconds per FP OP on a 4k square image
+
+- loop / list functions:
+
+  opihi.c
+  run_loop.c
+  run_if.c
+  input.c
+  for_loop.c
+  macro_exec.c
+
+  I have added continue / break functionality to all loop operations:
+
+  * the variable $STATUS is set based on the exit status of each command
+  * each loop tests for break and continue
+  * in the case of break, the loop returns a status of FALSE
+  * in the case of continue, the loop returns a status of TRUE
+  * if break -auto is set to 'on', then any exit status of FALSE escapes to  
+    the top level.
+  * interrupts are carried up to the top level.
+
+- local variables
+
+  I have added local variables.  They have names of the form
+  MacroName.VarName, where MacroName is the macro in which they are
+  local.  They are declared local with the command 'local', which
+  takes a list of variables to declare local, and the optional -static
+  flag, which requires the shell to test for the existence of the
+  variable before creating it, and only create it if it does not
+  exist.  otherwise the existing value of the variable is maintained.
+
+- math
+
+  I have removed the last instances of math interpretation being
+  performed by math, not newmath, in expand_vectors (interpreting the
+  contents of the vector square brackets: x[5]), and in the two
+  locations where logic was used in the evaluation: run_in and
+  run_while.  there are still some error in the precedence of the
+  operators: logical operators >,<,==,!=, etc need to be lower
+  priority than other binary operators.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/pantasks.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/pantasks.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/pantasks.txt	(revision 17422)
@@ -0,0 +1,52 @@
+
+- task spawning speed
+
+  I have been examining things which affect the speed of the pantasks
+  processing. I have learned some interesting things:
+
+  * pcontrol was being slammed with requests for status by pantasks.
+    this may account for Paul's controller hang-ups.  I have added a
+    long (500ms) sleep to the controller thread to limit the rate at
+    which controller checks are run
+
+  * adding even a small usleep to the task_thread or job_thread puts
+    them to sleep for a long time (>> 10ms).  it seems longer than the
+    linux time slicer.  I have removed sleeps from the task and job
+    threads.
+
+  * the job submit rate is apparently limited by two things:
+
+    * when the job is submitted (SubmitJob) the interaction with
+      the controller seems to take ~30ms or more.
+
+    * some thread (controller thread? main readline thread?) seems to
+      introduce timeouts which are very long (up to 100ms).  These
+      introduce bit delays if when they happen during the task_thread
+      loop. 
+
+- updates for queues:
+
+  -key 1:2:4 (key is string with possibly multiple columns joined)
+  string function to drop first word
+
+
+- todo:
+
+  - create the processing threads:
+    - check tasks
+    - check jobs
+    - check controller  
+    - load inputs
+
+- additional issues:
+  - server input needs to place files for input on a stack which is 
+    actually loaded by the RunScheduler loop
+  - have the client run a special multicommand function which passes 
+    any unfound commands along to the server.  the server should not 
+    be running multicommand, but it probably does not hurt.
+    (the client is not allowed to send ';' to the server)
+
+  - pantasks input: this passes an 'input' command to the server, which
+    performs the input in a different thread.  is this the same thread
+    as the scheduler loop?  another option within the scheduler loop?
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/pcontrol.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/pcontrol.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/pcontrol.txt	(revision 17422)
@@ -0,0 +1,276 @@
+
+2006.08.18
+
+ Outstanding issues related to pcontrol:
+
+ * disposition of HUNG jobs?
+ * probably should not save the history for pcontrol or pclient
+   (these will be many lines long very quickly...)
+ * need to add options to run/stop for hosts and jobs independently
+
+2006.08.11
+
+I have nearly finished the conversion of pcontrol to use a background
+thread for monitoring the remote machines.  A few questions are still
+outstanding:  
+
+- currently, we are thread-safe for interactions with the stacks.  As
+  long as an operation is only working with a single job/host, and all
+  jobs/hosts are selected by pulling them from the stacks, there will
+  never be a contention between threads for the same job/host.
+  However, are there problems for commands which require a specific
+  job or host but are unable to find it because the job/host may be in
+  flight from one stack to another.  
+
+- The CheckIdleHost command needs to join a job and a host.  In this
+  case, it is necessary to lock the job PENDING stack while searching
+  for a job to give to a host.  The host is pulled off of the IDLE
+  stack before being past to CheckIdleHost.
+
+o CheckIdleHost currently does not have a way to send a WANTHOST job
+  to any host other than the WANTHOST.  What should the rule be by
+  which a job is run on an alternative machine? (partial fix)
+
+- we are not starting any of the job or host timers?
+
+2006.08.09
+
+working on pcontrol CheckSystem background thread.  One thread runs
+the readline interaction and performs all of the user commands.  The
+second thread runs the CheckSystem loop and tests the hosts and jobs.
+We need to be sure these two do not interfere with one another.  Here
+is a list of all of the user commands and the ways in which they
+interact with the Job / Host queues:
+
+
+
+2006.08.04
+
+pcontrol gets a large delay every time it tries to connect to a host.
+this is because the readline interrupt has to wait for the connection
+to complete.  I probably need to fix this by using a threaded model
+and running CheckSystem in a background thread.
+
+-----
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+typedef struct {
+  int argc; char **argv; /* a list of words that define this object */
+  struct timeval start, accum, timer;
+  int   status;
+  char *logfile;
+  char *lastproc;
+} Object;
+
+typedef struct {
+  Object **object;
+  int    Nobject;
+  int    NOBJECT;
+} Queue;
+
+typedef struct {
+  char   *hostname;
+  int     rsock, wsock;
+  int     status; /* idle, busy, etc... */
+  struct  timeval start, accum, timer;
+  Fifo    fifo;
+  int     code;
+  Object *object;
+} Machine;
+
+
+currently, the transport is /usr/bin/rsh, defined in InitMachines.c 
+
+the shell on the remote machines is /bin/tcsh, defined by rconnect.c
+
+---
+
+pcontrol.client:
+
+ - remote process initiated by pcontrol
+
+ - accepts jobs, returns status, stdout and stderr
+
+ - valid commands:
+
+   - job (argv)
+     returns PID or -1 (0?) on failure
+
+   - status
+     returns current job status:
+     BUSY
+     EXIT n 
+     CRASH n
+     
+   - stderr
+     returns the current stderr buffer:
+     NBYTES n
+     (DATA)
+
+   - stdout
+     returns the current stdout buffer:
+     NBYTES n
+     (DATA)
+
+   
+---
+
+the client needs to accept commands from the server (via
+stdin/stdout), but it also needs to monitor its process.  I can use
+the opihi structure to implement the command-line interpretation with
+readline.  I can use the readline function rl_event_hook to set the
+background functions to check and rl_set_keyboard_input_timeout to set
+the polling period.
+
+this same method can be used with the scheduler:  the command 'run'
+can set the CheckTask function to this hook (& unset it).
+
+---
+
+rl_event_hook -> CheckChild
+
+  - needs to handle the case when no child process yet exists
+  - needs to examine the child status,
+  - needs to read from child stderr and store
+  - needs to read from child stdout and store
+
+  * no warnings (will not do anything clever if buffers get too large)
+
+---
+
+ pcontrol commands:
+
+ job [options] argv0 argv1 argv2 ...
+  -host name : run job on specified host, or any other if not available
+  +host name : run job on specified host, error if not available (error when attempted, not when submitted)
+  -timeout N : seconds before controller gives up on job (once started)
+  -stdout name : redirect job stdout to file directly
+  -stderr name : redirect job stderr to file directly
+
+  * priority information?
+  * returns JobID
+  * adds job to pending queue
+
+ host (hostname) [-delete]
+ (may have multiple entries to the same machine, these are not distinguished)
+
+ stdout ID [-file name]
+ stderr ID [-file name]
+ delete ID
+
+ status -job ID
+ status -machine hostname
+ status -queues
+
+pcontrol may be given a timeout for each job.  pcontrol will monitor a
+job and kill/crash it if the timeout expires.  the timeout only
+governs how long it is allowed to execute, not how long it can sit in
+the queue.  (the scheduler / operator should decide if a job has been
+on pcontrol for too long -- this probably means there are no
+appropriate machines ).
+
+pcontrol currently does not distinguish between multiple instances of
+a single host.  all have the same name.  if you want to bring down a
+host, you need to issue N host -down commands.  perhaps this is
+silly.  a simple alternative would be for the host [-on -off -start
+-stop] commands to apply to all defined entries which match the
+hostname.  In this case, a command like 'host foo -off' would find and
+halt all connections to the machine 'foo', while 'host foo -on' would
+restart them all (or rather, given the functionality of pcontrol,
+would allow pcontrol to attempt to bring them on).
+
+It is not clear why a user should be able to execute 'start' (down ->
+idle) or 'stop' (idle -> down).  The transition down -> idle is
+automatically performed by pcontrol for any machines which are
+currently down, while the transition idle -> down is immediately
+followed by an attempt by pcontrol to move the host from down -> idle.
+This functionality can be used with non-automatic calling of
+CheckSystem to test the pcontrol host interface operations.
+
+does it makes sense to kill all jobs on a host?  this would only have
+the effect of clearing the host for a moment until pcontrol decided to
+start another job on that host.  the desired effect is gained putting
+the host to 'off'.
+ 
+currently the command 'host (hostname)' puts the host in 'down'
+state.  pcontrol then immediately tries to connect to the host, moving
+it to 'idle' state (and then 'busy' if any jobs are available).  it
+might be useful to be able to add a host in 'off' state as a starting
+point.  
+
+it is not obvious that the user should be able to run 'CheckHost',
+unless this gets expanded to return state information on the host.
+
+---
+
+Job States:
+
+PENDING
+BUSY
+EXIT
+CRASH
+NEW *
+DEL *
+
+* - invisible states 
+
+Job State Transitions:
+
+NEW     -> PENDING : AddJob
+PENDING -> BUSY    : StartJob
+PENDING -> DEL     : DelJob
+BUSY    -> DONE    : CheckBusyJob | KillJob
+DONE    -> EXIT    : CheckDoneJob
+DONE    -> CRASH   : CheckDoneJob
+BUSY    -> PENDING : CheckJob | CheckHost
+EXIT    -> DEL     : DelJob
+CRASH   -> DEL     : DelJob
+
+Host States:
+
+IDLE
+BUSY
+DOWN
+OFF
+NEW *
+DEL *
+
+* - invisible states 
+
+Host State Transitions:
+
+NEW      -> OFF      : AddHost
+OFF      -> DEL      : DelHost
+OFF      -> DOWN     : OnHost
+DOWN     -> OFF      : OffHost
+IDLE     -> OFF      : OffHost
+DOWN     -> IDLE     : StartHost
+IDLE     -> BUSY     : StartJob
+BUSY     -> IDLE     : CheckJob | KillJob
+BUSY     -> BUSY-OFF : OffHost
+BUSY     -> DOWN     : CheckJob | CheckHost
+BUSY-OFF -> OFF      : CheckJob
+
+AddJob    - U
+DelJob    - U
+StartJob  - P
+CheckJob  - P
+KillJob   - U
+
+AddHost   - U
+DelHost   - U
+OnHost    - U
+OffHost   - U
+StartHost - P
+CheckHost - P
+
+U - operation performed by the user
+P - operation performed by the program
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/psched.htm
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/psched.htm	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/psched.htm	(revision 17422)
@@ -0,0 +1,161 @@
+
+This article describes the concept, design, and operation of
+<tt>psched</tt>, the Pan-STARRS IPP task scheduler.  
+
+<h2> Basic Concept </h2>
+
+  <p>
+  The purpose of <tt>psched</tt> is to manage the automatic construction
+  and execution of inter-related (often repetative) operations.
+  <tt>psched</tt> uses a set of rules to define UNIX commands, and
+  their corresponding command-line arguments, to be performed on some
+  regular, repeated basis.  The utility of <tt>psched</tt> is that it
+  can easily define an analysis system which is completely
+  state-based, as opposed to an event-driven system.  
+
+  <p>
+  Consider, for example, a telescope which obtains a collection of
+  images over the course of a night.  Every minute or two, it takes an
+  image and writes the image to some disk.  An event-driven analysis
+  system would involve having the telescope initiate a process at the
+  end of the exposure.  This process would perform an analysis, write
+  some output, then send trigger another process.  This type of
+  operation works very well for a simple set up with reliable
+  hardware.  Such a system becomes more difficult to maintain when
+  hardware failures occur or when multiple systems need to interact
+  with each other.  When failures occur, the triggering information
+  (the events) is easily lost, thus some mechanisms are needed to
+  detect these failures and either re-send the trigger or send an
+  alternative failure-mode trigger.  Or, if two systems need to
+  interact, one or the other system must block for results from the
+  first.  Stopping and restarting such an analysis system is very
+  delicate since the appropriate triggers must be set up some how, eg
+  by noticing which images have not succeeded and restarting them at
+  the appropriate stage.  All of these types of methods of handling
+  complexity and failures are essentially state-based rules.
+  <tt>psched</tt> allows the easy definition of a totally state-based
+  analysis system.
+
+  <p>
+  In a state-based system, some mechanism examines the state of the
+  system and decides which actions to perform based on the current
+  state.  In the illustration above, the mechanism could examine the
+  images available (either by examining the disk or by examining the
+  state of a data table) and decide to perform an operation based on
+  what images are available.  This makes it very easy to handle
+  complexity and errors.  If an analysis fails, the state either is
+  not successfully updated or the error state is recorded, both
+  situations being easy to detect and easy to handle.  Restarting the
+  system simply involves starting the state-monitoring mechanism.
+  Combining results from multiple input sources simply involves
+  watching for the multiple inputs to be available.  <tt>psched</tt>
+  provides a mechanism to define state monitors, and to define the
+  actions which are performed when those states occur.
+  <tt>psched</tt> action consist of initiating UNIX commands, where
+  the arguments of those commands may depend on the results of the
+  state tests.
+
+  <h3> Tasks vs Jobs </h3>
+
+  <p>
+  The primary function of <tt>psched</tt> is to repeatedly perform
+  <b>tasks</b>, and execute <b>jobs</b> on the basis of those tasks.
+  A task consists of a set of rules which describe system state tests
+  to perform on a regular time scale.  Based on the results of those
+  state tests, the task will then choose whether or not to construct a
+  job.  The task also defines actions to perform upon the completion
+  of a job, based upon the output and exit status of the job.  A task
+  thus defines the repeat period.  It may optionally define valid or
+  invalid time ranges (eg, Mon-Fri or 10:00-17:00, etc).  The task may
+  also specify that the job be run locally (ie, in the background on
+  the same computer as psched) or remotely by the parallel process
+  controller (<tt>pcontrol</tt>).  A job may even be restricted to a
+  specific computer managed by <tt>pcontrol</tt>.
+
+  An example of a simple tasks is given below.  
+
+<pre>
+  task datalist
+    command ls /data/foo
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+    end
+ 
+    task.exit 1
+      queuepush failure "task failed"
+    end
+  end
+</pre>
+
+  <p>
+  This task does not perform any system state tests; it is simply
+  constructs a new job every 5.0 seconds.  The job in this case is
+  always the same: <tt> ls /data/foo </tt>.  When the job finished,
+  if the job exit status is 0 (normal UNIX success status), the
+  resulting output is printed to the screen.  If the job returns an
+  exit status of 1 (a failure), the failure queue receives a single
+  entry.  Although they are not defined in this case, it is also
+  possible to specify the action to be taken if the job crashes (does
+  not exit normally) or if it times out (runs beyond the specified
+  timeout period).
+
+  A slightly more complex task which performs a state test and
+  constructs a command based on that test is shown below
+
+<pre>
+  task datalist
+    periods -exec 5.0
+    periods -timeout 50.0
+    periods -poll 1.0
+
+    task.exec 
+      $file = `next.file`
+      if ($file == "none")
+        break
+      end
+      command cp /data/foo/$file /data/bar
+    end
+
+    task.exit 0
+      queueprint stdout
+      queuedelete stdout
+      queuepush copied $file
+    end
+ 
+    task.exit 1
+      queuepush failure $file
+    end
+  end
+</pre>
+
+  The <tt>task.exec</tt> macro is executed by psched every 5.0
+  seconds.  This macro executes a (hypothetical user-defined) UNIX
+  command (<tt>next.file</tt>) which examines the system state, return
+  either a filename or the word "none".  If the result of this test is
+  "none", the task does nothing: no job is constructed.  Otherwise, a
+  job is constructed using the name of the file returned by the state
+  test.  Successful jobs have the filename added to the 'copied'
+  queue, while failed jobs add the filename to the 'failure' queue.
+
+  <h3> Parallel vs Local Job Processing </h3>
+
+  <h3> Task Restrictions </h3>
+
+  <h3> Inter-Task and Inter-Job Communications </h3>
+
+<h2> psched Design </h2>
+
+  <h3> The Opihi Shell </h3>
+
+  <h3> Task List </h3>
+
+  <h3> Job List </h3>
+
+  <h3> pcontrol Interface </h3>
+
+  <h3> 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/rd-upgrades.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/rd-upgrades.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/rd-upgrades.txt	(revision 17422)
@@ -0,0 +1,10 @@
+
+rd a file.fits : read from PHU
+rd a file.fits -x N : read from extension N (0 - N)
+rd a file.fits -n name : read from extension name
+
+-plane : read only slice N
+
+* this function needs to check for compressed data:
+
+  * if PHU has been compressed, 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/region-image.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/region-image.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/region-image.txt	(revision 17422)
@@ -0,0 +1,13 @@
+
+I have done some work on getting region to use the image projection
+for the dvo display.  The pieces are almost there, except:
+
+* there is apparently a small offset between the two systems?
+
+* SelectRegions relies on the values of xmax and ymax being degrees
+  from a 0,0 coordinate.  this is a little tricky: this should not
+  fail for large AITOFF projections.
+
+* kapa does not do non-linear projections
+* kapa does not do multi-level projections
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/sample.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/sample.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/sample.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include <stdio.h>
+
+main (int argc, char **argv) {
+
+  int i;
+  char line[1024];
+
+  for (i = 0; i <= argc; i++) {
+    gprint (GP_ERR, "arg %2d: %s\n", i, argv[i]);
+  }
+  while (argv[i] != NULL) {
+    gprint (GP_ERR, "env %2d: %s\n", i, argv[i]);
+    i++;
+  } 
+  while (fscanf (stdin, "%s", line) != EOF) {
+    gprint (GP_ERR, "line: ...%s...\n", line);
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/scheduler.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/scheduler.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/scheduler.txt	(revision 17422)
@@ -0,0 +1,206 @@
+
+2005.07.15
+
+The controller sends messages to both stdout and stderr.  I can easily
+require the messages which are immediate responses to external
+commands (status, check, etc) go back on stdout.  other messages
+should go to stderr, or be suppressed.  I suppose i can regularly
+harvest the stderr messages?
+
+2005.07.14
+
+I am still exploring the scheduler / controller interactions.  the
+automatic interactions seem to work pretty well now.  The area of
+confusion is in the user interface, both in terms of checking on the
+status of things (both controller and scheduler) and in terms of
+having user control over aspects of the controller.
+
+I have defined user functions which execute the controller commands
+'status' and 'check'.  These are straightforeward since they simply
+send a command to the controller and echo the output (or give an error
+condition message).
+
+Should the user have the ability to define a job, independent of a
+task?  This could be implemented purely as a controller action: the
+controller commands 'job', 'kill', 'delete', 'stderr', 'stdout' would
+be available from the scheduler, and the commands simply passed
+along.  This adds a bit to the complexity: if the 'delete' command is
+passed along, nothing prevents the user from deleting a job scheduled
+by the scheduler from a task.  the scheduler may then get confused
+when it tries to interact with that job in the future from the
+automatic loop.
+
+Another option is to simply have these commands interact with the
+scheduler's job stack.  this has the advantage of limiting the
+scheduler / controller responsibility errors (scheduler, not user, is
+always responsible to sending/harvesting jobs to/from the
+controller, though we still need to handle the cases if a job is lost
+or dropped by the controller).  the diffficulty here is deciding how
+to handle the job completion.  we would need a way to define a set of
+exit macros, which could then do something useful with the output.  
+
+Another possibility is to define limits on how many times a task may
+spawn a job.  There would then be no 'job' function.  If we define
+this limitation, we will still need a way of killing and deleting a
+specific job.  Thus a 'kill' and 'delete' function would examine and
+modify the scheduler's job stack.  The stderr and stdout functions are
+then already part of the task commands.  
+
+Other task options might include: 
+
+- a list of allow / exclude time periods (which should be time-of-day
+  ranges and day-of-week ranges).
+
+- a function to delete an existing task (which would have to stop the
+  spawning of new jobs, at least until no more jobs for that task
+  remain).
+
+- allow the 'periods' command to define defaults when outside of a
+  task
+
+2005.07.05
+
+At this point, scheduler / pcontrol / pclient all work in a basic way.
+pclient is the most robust of the three, having the simplest
+responsibility.  pcontrol is generally pretty good, though I need to
+flesh out the user interface a bit and clean up the output warning / info
+messages.  scheduler will need the most attention, though it is
+already fairly reasonable.  I need to flesh out the user commands to
+check on the controller status (basically, these need to replicate the
+status commands available to the controller).  
+
+I also need to handle the case of timeout on the controller,
+independently of timeout for a local job on the scheduler.  currently,
+if a local job exceeds the timeout value, scheduler flags it.  but, it
+does not make sense to use the same timeout value for a controller
+job. I could pass the timeout to the controller when the job executes,
+in which case it has the same meaning, essentially, for the controller
+jobs as it does for the local jobs: once you start the function, it
+needs to complete within NN seconds.  However, I think I still need to
+have a scheduler concept of a job which the controller is unable to
+complete.  It should be possible to prevent a job from sitting pending
+on the controller forever.  What exactly you do if the controller is
+unwilling to execute a job is another story (possible reasons:
+controller overload, missing required host, missing any hosts,
+something hung somewhere?).  
+
+The scheduler does not do a good job of shutting down the controller
+when it (the scheduler) exits.  This works well for the
+pcontrol/pclient interface, so the solution lies there.  
+
+I need to decide how to behave if the scheduler asks for a job with a
+required host which the controller knows is currently down or
+non-existent.  Several options could be used.  The controller could
+simply hold the task until the scheduler notices it is not being
+executed (after all, the controller does not know if the machine is
+being serviced for a short time or a long time, but the scheduler
+could know).  The controller could immediately return a failure noting
+the current state of the machine (this would put the burden of
+deciding that the machine should be available on the scheduler).  The
+controller could try to execute the job a certain number of times, and
+then it could report the failure to the scheduler.  This is not so
+different from having a pending-timeout which the scheduler tracks
+(moves the timeout check to the controller, essentially).  
+
+There was some odd behavior with 'exec echo $stdout >> foo'.  This
+resulted in empty files 'foo'.  The following work fine, so something
+is just weird:
+exec echo foobar >> foo
+output foo
+echo $stdout
+output stdout
+
+Various error conditions should be checked
+
+What do we do if a task requests a host which is not available to the
+controller (ie, not defined)?  this is similar to the problem of
+requesting a host which is down.  I think the controller should either
+immediately refuse or accept in anticipation that such a host may
+eventually be defined.
+
+I need to be careful about jobs sent to the controller and not
+harvested before stopping the scheduler execution.
+
+---
+
+sched / pcontrol todo:
+
+- sched: validate task hosts with controller
+
+---
+
+scheduler commands:
+
+task (taskname)
+ - define a new task
+ - loads task-related commands from list / readline
+ - commands are parsed on load
+ - end with end (like if / for)
+
+task.exit (value)
+ - define a new task macro for this exit condition
+   (value) may be an exit status (number)
+   (value) may be 'timeout'
+   (value) may be 'crash' ?
+ - commands are parsed on execution (not on definition)
+
+task.exec
+ - define a task macro for exec condition
+ - commands are parsed on execution (not on definition)
+
+command (args) (args)
+ - defines command associated with task
+ - may be in task or in task.macro (exit/exec)
+   (in task, command line is static; in task.macro, command line is expanded for each instance)
+
+host (machine) [-required]
+ - defines preferred host
+ - may be in task or in task.macro (exit/exec)
+   (in task, value is static; in task.macro, value is defined for each instance)
+ - value of LOCAL runs job as local job (not on controller)
+ - value of NONE runs job on controller without specifying host
+
+stderr (file / variable)
+ - defines destination for stderr capture from task
+ - written to destination at end of execution?
+
+stdout (file / variable)
+ - defines destination for stdout capture from task
+ - written to destination at end of execution?
+
+periods -poll 1
+periods -exec 30
+periods -timeout 2
+ - defines relevant time-scale for the task
+
+run
+stop
+ - start or stop the scheduler loop, executing the various tasks
+
+---
+
+local jobs vs controller jobs
+
+a local job is run as background fork (ie, not on controller)
+a controller job is sent to the controller to run when it can
+
+---
+
+possible errors which the scheduler may encounter when executing a
+job:
+
+  - controller is not responding
+  - controller says machine is DOWN
+  - controller says command not found
+  - controller has too many processes
+  - controller takes to long to start job (pending timeout)
+  - controller says job timed out
+  - controller says job crashed
+  - controller says job exited with status
+
+---
+
+notes:
+
+ - watch for NFS lags / blocking.  if NFS has file visibility lags, we
+   may need to add blocking as an option to the job (-block filename)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/stds.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/stds.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/stds.txt	(revision 17422)
@@ -0,0 +1,40 @@
+
+notation:
+
+object : an astronomical source, may be observed multiple times
+measurement : a single observation of an object (one per object per exposure) 
+m_inst : instrumental magnitude measurement
+m_cat  : catalog magnitude measurement (m_inst + 25.0)
+m_sys  : nominal internal system magnitude measurement (m_inst + ZP(CCD,FILTER)
+m_rel  : transparency adjusted magnitude measurement (m_sys + offset)
+
+M_ave  : average (object) magnitude in internal system
+M_ref  : average (object) magnitude in reference system
+
+test for photometric consistency:
+
+for the output from dither.corr:
+
+M_ave - m_rel(ccd) vs x_ccd, y_ccd
+M_ave - m_rel vs x_mosaic, y_mosaic
+M_ave - m_rel vs airmass?
+
+
+
+  MEGACAM.g.00 Minst - MEGACAM.g.01 Mcal : time
+
+  MEGACAM.g.00 - g : time
+
+  MEGACAM.g.00,Mrel - MEGACAM.g.01,Mcal : MEGACAM.g.00 - MEGACAM.r.00
+
+- use relphot to flag stars in poor locations, etc
+
+- we can choose for entry if it is Minst, Mcat, Msys, Mvrel, Mcal, Mave, or Mref
+
+int GetMagnitudeType (char *name, PhotCode *code, int *MagMode);
+
+- should return photcode & value MEAS_MREL, AVE_MREF, or what ever
+- should provide an appropriate default for measures, etc
+- should check for consistency with photcode type:
+  PRI/SEC : AVE_MREF, AVE_MAVE
+  DEP     : MEAS_ETC
Index: /tags/ipp-2-5-1/Ohana/src/opihi/doc/todo.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/doc/todo.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/doc/todo.txt	(revision 17422)
@@ -0,0 +1,4 @@
+
+- convert and include all mana/dimm/status derived functions
+- local scalar variables
+- break / continue in: for, macro, 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageOps.c	(revision 17422)
@@ -0,0 +1,123 @@
+# include "dvoshell.h"
+
+void image_subset (Image *image, int Nimage, int **Subset, int *Nsubset,
+		   SkyRegionSelection *selection, 
+		   unsigned long int tzero, double trange, int TimeSelect) 
+{
+
+  int i, j, flipped, status, InPic;
+  int *subset;
+  int N, n, npts;
+  double r, d, X, Y, x[4], y[4], Rmin, Rmax, Rmid;
+  Graphdata graph;
+  SkyRegion patch;
+
+  Rmin = Rmax = Rmid = 0;
+
+  if (selection->useDisplay) {
+    if (!GetGraphData (&graph, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return;
+    }
+    Rmin = graph.coords.crval1 - 182.0;
+    Rmax = graph.coords.crval1 + 182.0;
+    Rmid = 0.5*(Rmin + Rmax);
+    BuildChipMatch (image, Nimage);
+  }
+
+  if (selection->useSkyregion) {
+    double Rs, Re, Ds, De;
+    get_skyregion (&Rs, &Re, &Ds, &De);
+    patch.Rmin = Rs;
+    patch.Rmax = Re;
+    patch.Dmin = Ds;
+    patch.Dmax = De;
+    Rmin = patch.Rmin - 182.0;
+    Rmax = patch.Rmax + 182.0;
+    Rmid = 0.5*(Rmin + Rmax);
+  }
+
+  if (trange < 0) {
+    tzero = tzero + trange;
+    trange = fabs (trange);
+  }
+
+  npts = 200;
+  ALLOCATE (subset, int, npts);
+  n = N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (selection->useDisplay) {
+      if (!FindMosaicForImage (image, Nimage, i)) continue;
+      /* project this image to screen display coords */
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+      InPic = flipped = FALSE;
+      for (j = 0; j < 4; j++) {
+	XY_to_RD (&r, &d, x[j], y[j], &image[i].coords);
+	/* use same side of 0,360 boundary for all corners */
+	if ((j == 0) && (r < Rmin)) flipped = TRUE; 
+	if ((j == 0) && (r > Rmax)) flipped = TRUE; 
+	while (flipped && (r < Rmid)) r+= 360.0;
+	while (flipped && (r > Rmid)) r-= 360.0;
+	status = RD_to_XY (&X, &Y, r, d, &graph.coords);
+	if (!status) continue;
+	if (X < graph.xmin) continue;
+	if (X > graph.xmax) continue;
+	if (Y < graph.ymin) continue;
+	if (Y > graph.ymax) continue;
+	goto in_region;
+	/** we miss any images which surround the region.  we are also
+	    missing the DIS images for which the corners don't touch
+	    the region, but which are needed for WRP images with
+	    corners touching the region **/
+      }
+      continue;
+    }
+    if (selection->useSkyregion) {
+      if (!FindMosaicForImage (image, Nimage, i)) continue;
+      /* project this image to screen display coords */
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+      InPic = flipped = FALSE;
+      for (j = 0; j < 4; j++) {
+	XY_to_RD (&r, &d, x[j], y[j], &image[i].coords);
+	/* use same side of 0,360 boundary for all corners */
+	if ((j == 0) && (r < Rmin)) flipped = TRUE; 
+	if ((j == 0) && (r > Rmax)) flipped = TRUE; 
+	while (flipped && (r < Rmid)) r+= 360.0;
+	while (flipped && (r > Rmid)) r-= 360.0;
+	if (r < patch.Rmin) continue;
+	if (r > patch.Rmax) continue;
+	if (d < patch.Dmin) continue;
+	if (d > patch.Dmax) continue;
+	goto in_region;
+	/** we miss any images which surround the region.  we are also
+	    missing the DIS images for which the corners don't touch
+	    the region, but which are needed for WRP images with
+	    corners touching the region **/
+      }
+      continue;
+    }
+  in_region:
+    subset[n] = i;
+    n++;
+    if (n > npts - 1) {
+      npts += 200;
+      REALLOCATE (subset, int, npts);
+    }
+  }
+
+  REALLOCATE (subset, int, MAX (n, 1));
+  *Subset = subset;
+  *Nsubset = n;
+  return;
+}
+
+/* this routine fills the subset index with the list of selected images.
+   images may be selected on the basis of the region or on a time range 
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageSelection.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageSelection.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ImageSelection.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "dvoshell.h"
+
+/* db image table */
+static Image *image = NULL;
+static int *subset = NULL;
+static int Nimage = 0;
+static int Nsubset = 0;
+static Coords mosaic;
+
+/* load images based on parameters and region, etc */
+int SetImageSelection (int mosaicMode, SkyRegionSelection *selection) {
+
+  int TimeSelect;
+  time_t tzero, tend;
+
+  image = NULL;
+  subset = NULL;
+  
+  TimeSelect = GetTimeSelection (&tzero, &tend);
+
+  if (mosaicMode) {
+      /* mosaic defines a frame with 0,0 at the mosaic center, and 1 arcsec / pixel */
+      mosaic.crpix1 = mosaic.crpix2 = 0.0;
+      mosaic.cdelt1 = mosaic.cdelt2 = 1.0 / 3600;
+      mosaic.pc1_1  = mosaic.pc2_2  = 1.0;
+      mosaic.pc1_2  = mosaic.pc2_1  = 0.0;
+      mosaic.Npolyterms = 0;
+      strcpy (mosaic.ctype, "RA---SIN");
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, (double) tend - tzero, TimeSelect);
+  sort_image_subset (image, subset, Nsubset);
+  return (TRUE);
+}
+
+/* free loaded images */
+void FreeImageSelection () {
+  if (image != NULL) free (image);
+  if (subset != NULL) free (subset);
+  image = NULL;
+  subset = NULL;
+  return;
+}
+
+Image *MatchImage (unsigned int time, short int source) { 
+
+  int m;
+
+  m = match_image_subset (image, subset, Nsubset, time, source);
+  if (m == -1) return (NULL);
+  if (!FindMosaicForImage (image, Nimage, m)) return (NULL);
+  return (&image[m]);
+}
+
+Coords *MatchMosaic (unsigned int time, short int source) { 
+
+  int m;
+
+  m = match_image_subset (image, subset, Nsubset, time, source);
+  if (m == -1) return (NULL);
+  mosaic.crval1 = image[m].coords.crval1;
+  mosaic.crval2 = image[m].coords.crval2;
+  return (&mosaic);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/LoadImages.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/LoadImages.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/LoadImages.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "dvoshell.h"
+
+Image *LoadImages (int *nimage) {
+
+  int status;
+  char *catdir, filename[256];
+  Image *image;
+  FITS_DB db;
+  
+  /* VarConfig ("IMAGE_CATALOG", "%s", filename); */
+
+  catdir = GetCATDIR ();
+  sprintf (filename, "%s/Images.dat", catdir);
+
+  gfits_db_init (&db);
+  db.lockstate = LCK_SOFT;
+  db.timeout   = 120.0;
+
+  if (!gfits_db_lock (&db, filename)) {
+    gprint (GP_ERR, "error opening image catalog %s (1)\n", filename);
+    return (NULL);
+  }
+
+  if (db.dbstate == LCK_EMPTY) {
+    gprint (GP_ERR, "note: image catalog is empty\n");
+    ALLOCATE (image, Image, 1);
+    *nimage = 1;
+    return (image);
+  }
+
+  status = dvo_image_load (&db, TRUE, FALSE);
+  gfits_db_close (&db);
+
+  if (!status) {
+    gprint (GP_ERR, "problem loading image database table\n");
+    return (NULL);
+  }
+
+  image = gfits_table_get_Image (&db.ftable, nimage, &db.swapped);
+  return (image);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/Makefile	(revision 17422)
@@ -0,0 +1,137 @@
+default: dvo
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/dvo
+DATA    =       $(DESTDATA)/dvo
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# dvo user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o            	\
+$(SRC)/ImageOps.$(ARCH).o		\
+$(SRC)/ImageSelection.$(ARCH).o	        \
+$(SRC)/LoadImages.$(ARCH).o		\
+$(SRC)/cmpReadFile.$(ARCH).o		\
+$(SRC)/compare.$(ARCH).o                \
+$(SRC)/match_image.$(ARCH).o		\
+$(SRC)/photometry.$(ARCH).o             \
+$(SRC)/dvomisc.$(ARCH).o		\
+$(SRC)/region_list.$(ARCH).o		\
+$(SRC)/dvomisc.$(ARCH).o		\
+$(SRC)/dbBooleanCond.$(ARCH).o		\
+$(SRC)/dbCheckStack.$(ARCH).o		\
+$(SRC)/dbCmdlineFields.$(ARCH).o	\
+$(SRC)/dbExtractAverages.$(ARCH).o	\
+$(SRC)/dbExtractMeasures.$(ARCH).o	\
+$(SRC)/dbFields.$(ARCH).o		\
+$(SRC)/dbRPN.$(ARCH).o			\
+$(SRC)/dbStackMath.$(ARCH).o		\
+$(SRC)/dbStackOps.$(ARCH).o		\
+$(SRC)/dvo.$(ARCH).o
+
+cmds = \
+$(SRC)/avextract.$(ARCH).o	  	\
+$(SRC)/badimages.$(ARCH).o	  	\
+$(SRC)/calextract.$(ARCH).o      	\
+$(SRC)/calmextract.$(ARCH).o     	\
+$(SRC)/catdir.$(ARCH).o             	\
+$(SRC)/ccd.$(ARCH).o             	\
+$(SRC)/cmatch.$(ARCH).o	  	\
+$(SRC)/cmd.$(ARCH).o             	\
+$(SRC)/cmpload.$(ARCH).o	  	\
+$(SRC)/cmpread.$(ARCH).o	  	\
+$(SRC)/ddmags.$(ARCH).o	  	\
+$(SRC)/detrend.$(ARCH).o	  	\
+$(SRC)/dmagaves.$(ARCH).o	  	\
+$(SRC)/dmagmeas.$(ARCH).o	  	\
+$(SRC)/dmags.$(ARCH).o		  	\
+$(SRC)/dmt.$(ARCH).o		  	\
+$(SRC)/elixir.$(ARCH).o                \
+$(SRC)/fitcolors.$(ARCH).o             \
+$(SRC)/fitsed.$(ARCH).o                \
+$(SRC)/gcat.$(ARCH).o		  	\
+$(SRC)/gimages.$(ARCH).o	  	\
+$(SRC)/gstar.$(ARCH).o		  	\
+$(SRC)/images.$(ARCH).o	  	\
+$(SRC)/imbox.$(ARCH).o		  	\
+$(SRC)/imdata.$(ARCH).o	  	\
+$(SRC)/imdense.$(ARCH).o	  	\
+$(SRC)/imextract.$(ARCH).o	  	\
+$(SRC)/imlist.$(ARCH).o	  	\
+$(SRC)/imphot.$(ARCH).o	  	\
+$(SRC)/imrough.$(ARCH).o	  	\
+$(SRC)/imsearch.$(ARCH).o	  	\
+$(SRC)/imstats.$(ARCH).o	  	\
+$(SRC)/lcat.$(ARCH).o		  	\
+$(SRC)/lcurve.$(ARCH).o	  	\
+$(SRC)/lightcurve.$(ARCH).o	  	\
+$(SRC)/mextract.$(ARCH).o	  	\
+$(SRC)/photcodes.$(ARCH).o	  	\
+$(SRC)/pmeasure.$(ARCH).o	  	\
+$(SRC)/paverage.$(ARCH).o	  	\
+$(SRC)/procks.$(ARCH).o	  	\
+$(SRC)/skycat.$(ARCH).o	  	\
+$(SRC)/skycoverage.$(ARCH).o	  	\
+$(SRC)/skyregion.$(ARCH).o	  	\
+$(SRC)/showtile.$(ARCH).o	  	\
+$(SRC)/simage.$(ARCH).o	  	\
+$(SRC)/subpix.$(ARCH).o  \
+$(SRC)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+dvo: $(BIN)/dvo.$(ARCH)
+$(SRC)/dvo.$(ARCH).o : $(libs)
+$(BIN)/dvo.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/dvo help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help dvo.help
+
+$(funcs) $(cmds) $(libs) : $(INC)/dvoshell.h
+
+modules: dvo.modules
+
+.PHONY: dvo
+
+# deprecated functions: verify & delete
+#$(SRC)/catalog.$(ARCH).o	  	\
+#$(SRC)/abszero.$(ARCH).o \
+#$(SRC)/cals.$(ARCH).o \
+#$(SRC)/dumpmags.$(ARCH).o \
+#$(SRC)/extract.$(ARCH).o \
+#$(SRC)/gtypes.$(ARCH).o \
+#$(SRC)/photresid.$(ARCH).o \
+#$(SRC)/resid.$(ARCH).o \
+#$(SRC)/zeropts.$(ARCH).o
+#$(SRC)/objload.$(ARCH).o \ - make sure we have vect to tv
+#$(SRC)/ccdextract.$(ARCH).o \
+#$(SRC)/cmdextract.$(ARCH).o \
+#$(SRC)/dmagextract.$(ARCH).o \
+#$(SRC)/ddmagextract.$(ARCH).o \
+#$(SRC)/pcat.$(ARCH).o		  	\
+
+# future functions, not fully implemented
+#$(SRC)/detrend.$(ARCH).o \
+#$(SRC)/getxtra.$(ARCH).o \
+#$(SRC)/addxtra.$(ARCH).o \
+
+# functions that need to be updated
+#$(SRC)/gregions.$(ARCH).o \
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/addxtra.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/addxtra.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/addxtra.c	(revision 17422)
@@ -0,0 +1,60 @@
+# include "dvoshell.h"
+
+typedef struct {
+  char type[64];
+  char name[64];
+  char source[256];
+  char mode[64];
+  char value[64];
+  char range[64];
+  double R, D;
+  int averef;
+} Xtras;
+
+int addxtra (int argc, char **argv) {
+  
+  Source = (char *) NULL;
+  if (N = get_argument (argc, argv, "-source")) {
+    remove_argument (N, &argc, argv);
+    Source = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Name = (char *) NULL;
+  if (N = get_argument (argc, argv, "-name")) {
+    remove_argument (N, &argc, argv);
+    Name = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+  addxtra R D dR type value -name (name) -source (source) -mode (mode) -range (range) 
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/aregion.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/aregion.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/aregion.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* find region file which contains ra, dec */
+void aregion (GSCRegion *region, FILE *f, double ra, double dec, char *path) {
+  
+  char buffer[28800], temp[50], file[50];
+  double RA0, RA1, DEC0, DEC1;
+  int i, NBigDec, NLINES, done;
+  
+  while (ra < 0) { ra += 360.0; }
+  while (ra >= 360.0) { ra -= 360.0; }
+
+  if (dec >= 86.25) {
+    sprintf (file, "%s/n8230/pole.cpt", path);
+    region[0].DEC[0] = 86.25;
+    region[0].DEC[1] = 93.75;
+    region[0].RA[0] =  0.0;
+    region[0].RA[1] =  360.0;
+    strcpy (region[0].filename, file);
+    return;
+  }
+    
+  NBigDec = -1;
+  for (i = 0; i < 12; i++) {
+    if ((dec >= BigDecBounds[i]) && (dec < BigDecBounds[i+1])) {
+      NBigDec = i;
+      break;
+    }
+  }
+  if (NBigDec < 0) {
+    for (i = 13; i < 24; i++) {
+      if ((dec < BigDecBounds[i]) && (dec >= BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "dec out of range: %f\n", dec);
+  }
+    
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+      
+  done = FALSE;
+  fread (buffer, 1, 48*NDecLines[NBigDec], f);
+  for (i = 0; !done && (i < NDecLines[NBigDec]); i++) {
+    strncpy (temp, &buffer[i*48], 48);
+    temp[49] = 0;
+    hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+    if (RA1 < RA0) RA1 += 360.0;
+    if ((dec >= 0) && (dec >= DEC0) && (dec < DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+    if ((dec < 0) && (dec < DEC0) && (dec >= DEC1) && (ra >= RA0) && (ra < RA1)) {
+      done = TRUE;
+    }
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error in search: %f %f\n", ra, dec);
+    exit (0);
+  }
+  temp[5] = 0;
+  sprintf (file, "%s/%s/%s.cpt", path, Dec2Sections[NBigDec],&temp[1]);
+  if (DEC0 < DEC1) {
+    region[0].DEC[0] = DEC0;
+    region[0].DEC[1] = DEC1;
+  } else {
+    region[0].DEC[0] = DEC1;
+    region[0].DEC[1] = DEC0;
+  }     
+  region[0].RA[0] = RA0;
+  region[0].RA[1] = RA1;
+  strcpy (region[0].filename, file);
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/avextract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/avextract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/avextract.c	(revision 17422)
@@ -0,0 +1,184 @@
+# include "dvoshell.h"
+
+int avextract (int argc, char **argv) {
+  
+  int i, j, n, m, N, Npts, NPTS, last, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, mode, VERBOSE;
+  char **cstack, name[1024];
+  float *values;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  PhotCode *code;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack;
+
+  /* defaults */
+  skylist = NULL;
+  selection = NULL;
+  code = NULL;
+  mode = MAG_AVE;
+  fields = NULL;
+  stack = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_AVERAGE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+
+  // parse the remainder of the line as a boolean math expression
+  cstack = isolate_elements (argc-last, &argv[last], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_AVERAGE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  dbCheckStack (stack, Nstack, DVO_TABLE_AVERAGE, &fields, &Nfields);
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* create output storage vectors */
+  ALLOCATE (values, float, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s (%d of %d)\n", catalog.filename, i, skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      // extract the relevant values
+      // XXX for measure values, this could be optimized for one loop over measures...
+
+      m = catalog.average[j].measureOffset;
+      dbExtractAveragesInit (); // reset counters for saved fields 
+      for (n = 0; n < Nfields; n++) {
+	values[n] = dbExtractAverages (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+      }
+      // test the conditional statement
+      if (!dbBooleanCond (stack, Nstack, values)) continue;
+      for (n = 0; n < Nreturn; n++) {
+	vec[n][0].elements[Npts] = values[n];
+      }
+      Npts++;
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	for (n = 0; n < Nreturn; n++) {
+	  REALLOCATE (vec[n][0].elements, float, NPTS);
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    REALLOCATE (vec[n][0].elements, float, MAX(1,Npts));
+  }
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+ escape:
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: avextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000)\n");
+    gprint (GP_ERR, "  DEC : declination \n");
+    gprint (GP_ERR, "  dRA : ra scatter \n");
+    gprint (GP_ERR, "  dDEC : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  flag : object flags\n");
+    gprint (GP_ERR, "  photocode:ave : average magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:ref : reference magnitude system for photcode\n");
+    gprint (GP_ERR, "  photocode:inst : first instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:cat : first catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:sys : first system magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:rel : first relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photocode:cal : first calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chipsq : chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  type : dophot type (unused)\n");
+    gprint (GP_ERR, "  typefrac : dophot type fraction (unused)\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode \n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for photcode average\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " avextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/badimages.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/badimages.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/badimages.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+static int badim_int = FALSE;
+void badim_escape () {
+  badim_int = TRUE;
+}
+
+int badimages (int argc, char **argv) {
+  
+  int i, Nimage, entry, First, Cross;
+  float *ptr;
+  double nominal, big, small, value;
+  Image *image;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: badimages entry value\n");
+    gprint (GP_ERR, "   OR: badimages -image N\n");
+    return (FALSE);
+  }
+  
+  image = LoadImages (&Nimage);
+
+  Cross = FALSE;
+  First = FALSE;
+  nominal = 1;
+  if (!strcmp (argv[1], "-image")) {
+    First = TRUE;
+    entry = atof(argv[2]);
+  } else {
+    entry = atof(argv[1]);
+    nominal = atof(argv[2]);
+    if (!strcasecmp (argv[1], "x")) {
+      Cross = TRUE;
+    }
+  }
+  
+  if (First) {
+    ptr = &image[entry].coords.crpix1;
+    for (i = 0; i < 22; i++) {
+      gprint (GP_LOG, "%2d: %g\n", i, ptr[i]);
+    }
+    value = image[entry].coords.pc1_1*image[entry].coords.pc2_2 + image[entry].coords.pc1_2*image[entry].coords.pc2_1;
+    gprint (GP_LOG, " x: %g\n", value);
+    return (TRUE);
+  }
+  
+  big = nominal * 1.05;
+  small = nominal / 1.05;
+  if (big < small) {
+    double tmp;
+    tmp = big; big = small; small = tmp;
+  }
+  
+  badim_int = FALSE;
+  if (Cross) {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      value = image[i].coords.pc1_1*image[i].coords.pc2_2 + image[i].coords.pc1_2*image[i].coords.pc2_1;
+      if ((value > big) || (value < small)) {
+	gprint (GP_LOG, "%5d %s: %d %g\n", i, image[i].name, image[i].tzero, value);
+      }
+    }
+  } else {
+    for (i = 0; (i < Nimage) && !badim_int; i++) {
+      ptr = &image[i].coords.crpix1;
+      if ((ptr[entry] > big) || (ptr[entry] < small)) {
+	gprint (GP_LOG, "%5d %s: %d %g\n", i, image[i].name, image[i].tzero, ptr[entry]);
+      }
+    }
+  }
+  
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calextract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calextract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calextract.c	(revision 17422)
@@ -0,0 +1,141 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm, NC, NR, ND, Np, Nc, Nt, Nx, Nd1, Nd2, NVEC};
+
+int calextract (int argc, char **argv) {
+  
+  int i, N, Nr, mode[2];
+  int Nsecfilt, NSTAR;
+
+  PhotCode *code[2];
+  Catalog catalog;
+  Vector **vec;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* command line arguments */
+  SetSelectionParam (0);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  /* code.type must be PHOT_REF */
+
+  /* one unique value per star */
+  N = 0;
+  NSTAR = 1;
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd] 	= SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm] 	= SelectVector ("cal:mag",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NC] 	= SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR] 	= SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND] 	= SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Np] 	= SelectVector ("cal:nphot",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc] 	= SelectVector ("cal:ncode",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  // if ((vec[Nt] 	= SelectVector ("cal:ncrit",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nx] 	= SelectVector ("cal:chisq",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (i && !(i % 10000)) { gprint (GP_ERR, ","); }
+      m = catalog.average[i].offset;
+
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (PHOT_REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]);
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+
+      /* find data for filter 1 */
+      M1 = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_MAG);
+      if (isnan(M1)) continue;
+
+      vec[Nd ][0].elements[N] = M1 - M2;
+      vec[Nm ][0].elements[N] = M2;
+      vec[NC ][0].elements[N] = color;
+      vec[NR ][0].elements[N] = catalog.average[i].R;
+      vec[ND ][0].elements[N] = catalog.average[i].D;
+      vec[Nd1][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_dMAG);
+      vec[Nd2][0].elements[N] = dM2;
+      vec[Nx ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_Xm);
+      vec[Nc ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCODE);
+      vec[Np ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NPHOT);
+      // vec[Nt ][0].elements[N] = ExtractAverages (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], AVE_NCRIT);
+      N ++;
+      if (N == NSTAR) {
+	NSTAR += 100;
+	for (j = 0; j < NVEC; j++) {
+	  REALLOCATE (vec[j][0].elements, float, NSTAR);
+	}
+      }
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: calextract F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calmextract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calmextract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/calmextract.c	(revision 17422)
@@ -0,0 +1,186 @@
+# include "dvoshell.h"
+
+enum {Nd, Nm1, Nm2, Nc, Ns, Nt, Nz, NR, ND, Nxc, Nyc, Nxm, Nym, NT, NP, Nd1, Nd2, NVEC};
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param, int Nin);
+
+int calmextract (int argc, char **argv) {
+  
+  int i, k, N, Nr, mode[2];
+  int NSTAR, Nstar, Nsecfilt;
+
+  Catalog catalog;
+  PhotCode *code[2];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector **vec;
+
+  /* these need to be freed in the end */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+  vec = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* command line arguments */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 2)) goto usage;
+
+  /* interpret required command-line arguments: calmextract F1 - F2 */
+  if (argc != 4) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* returned vectors are dmag, mag, color, time, airmass, ra, dec, x, y, exptime */
+  N = 0;
+  Nstar = 0;
+  NSTAR = 100;
+  ALLOCATE (vec, Vector *, NVEC);
+  if ((vec[Nd ] = SelectVector ("cal:dmag",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm1] = SelectVector ("cal:mag1",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nm2] = SelectVector ("cal:mag2",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nc ] = SelectVector ("cal:color",    ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Ns ] = SelectVector ("cal:star",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nt ] = SelectVector ("cal:time",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nz ] = SelectVector ("cal:airmass",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NR ] = SelectVector ("cal:ra",       ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[ND ] = SelectVector ("cal:dec",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxc] = SelectVector ("cal:xccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nyc] = SelectVector ("cal:yccd",     ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nxm] = SelectVector ("cal:xmosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nym] = SelectVector ("cal:ymosaic",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NT ] = SelectVector ("cal:exptime",  ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[NP ] = SelectVector ("cal:photcode", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd1] = SelectVector ("cal:dm1",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((vec[Nd2] = SelectVector ("cal:dm2",      ANYVECTOR, TRUE)) == NULL) goto escape;
+  for (k = 0; k < NVEC; k++) {
+    REALLOCATE (vec[k][0].elements, float, NSTAR);
+    vec[k][0].Nelements = 0;
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+  if (!SetImageSelection (TRUE, selection)) goto escape;
+
+  for (Nr = 0; Nr < skylist[0].Nregions; Nr++) {
+    if (Nr && !(Nr % 500)) { gprint (GP_ERR, "."); }
+
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[Nr];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    # if (0)
+    /* extract values, assign to vectors */
+    for (i = 0; i < catalog.Naverage; i++) {
+      m = catalog.average[i].offset;
+
+      /* PRI/SEC must have data for color term */
+      if (code[0][0].c1 && code[0][0].c2 && !PhotColor (&catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], code[0][0].c1, code[0][0].c2, &color)) continue;
+
+      /* find data for filter 2 (REF) */
+      M2 = NAN;
+      dM2 = NAN;
+      for (j = 0; j < catalog.average[i].Nm; j++) {
+	if (catalog.measure[m+j].photcode != code[1][0].code) continue;
+	M2 = PhotCat  (&catalog.measure[m+j]); 
+	dM2 = catalog.measure[m+j].dM;
+      }	
+      if (isnan(M2)) continue;
+      
+      /* find data for filter 1 */
+      M1 = ExtractMeasures (code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1, MEAS_MAG);
+      if (N1 == 0) goto skip;
+
+      /* extend storage vectors to take new data, if needed */
+      if (N + N1 >= NSTAR) {
+	NSTAR += N1 + 100;
+	for (k = 0; k < NVEC; k++) {
+	  REALLOCATE (vec[k][0].elements, float, NSTAR);
+	}
+      }
+
+      ConcatMeasures (vec[Nt ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_TIME); 
+      ConcatMeasures (vec[Nz ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_AIRMASS); 
+      ConcatMeasures (vec[NT ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_EXPTIME); 
+      ConcatMeasures (vec[NP ], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_PHOTCODE); 
+      // ConcatMeasures (vec[Nd1], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_dMAG); 
+      ConcatMeasures (vec[Nxc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XCCD); 
+      ConcatMeasures (vec[Nyc], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YCCD); 
+      ConcatMeasures (vec[Nxm], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_XMOSAIC); 
+      ConcatMeasures (vec[Nym], code[0], mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], N1, MEAS_YMOSAIC); 
+
+      for (j = 0; j < N1; j++, N++) {
+	vec[Nd ][0].elements[N] = M1[j] - M2;
+	vec[Nm1][0].elements[N] = M1[j];
+	vec[Nm2][0].elements[N] = M2;
+	vec[Nd2][0].elements[N] = dM2;
+	vec[Nc ][0].elements[N] = color;
+	vec[Ns ][0].elements[N] = Nstar;
+	vec[NR ][0].elements[N] = catalog.average[i].R;
+	vec[ND ][0].elements[N] = catalog.average[i].D;
+      }
+      Nstar ++; 
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    # endif
+    dvo_catalog_free (&catalog);
+  }
+
+  for (i = 0; i < NVEC; i++) {
+    vec[i][0].Nelements = N;
+  }
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  return (TRUE);
+  
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  FreeImageSelection ();
+  for (i = 0; i < NVEC; i++) {
+    DeleteVector (vec[i]);
+  }
+  free (vec);
+  return (FALSE);
+}
+
+int ConcatMeasures (Vector *vec, PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int Nin, int param) {
+
+  int i, Ns, N;
+  double *value;
+
+  value = ExtractMeasures (code, mode, average, secfilt, measure, &N, param); 
+  if (N != Nin) {
+    gprint (GP_ERR, "error!\n");
+    return (FALSE);
+  }
+
+  Ns = vec[0].Nelements;
+  for (i = 0; i < N; i++) {
+    vec[0].elements[Ns+i] = value[i];
+  }
+  vec[0].Nelements = Ns + N;
+
+  free (value);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catalog.c	(revision 17422)
@@ -0,0 +1,407 @@
+# include "dvoshell.h"
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+# define MAGSCALE 0
+# define NUMSCALE 1
+# define MISSCALE 2
+
+// XXX EAM : should this function be dropped? 
+int catlog (int argc, char **argv) {
+  
+  FILE *f;
+  Catalog catalog;
+  Vector Xvec, Yvec, Zvec;
+  int i, N, Nm, Nn, NN, Nbytes, nbytes, Bytes_Star;
+  int Ar, Ad, Am, InRegion, GSC, ASCII, DVO, FIXED;
+  char filename[128];
+  double Mz, Mr, Nz, Nr;
+  int clip, mode, IDclip, IDchoice, LimExclude;
+  RegionFile *regions;
+  int j, Nregions;
+  double Radius, Rmin, Rmax;
+  Graphdata graphmode;
+  double epoch, current_epoch;
+  char gscdir[256], catdir[256];
+  int Ngraph;
+
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  Mz = 17.0;
+  Mr = -5.0;
+  mode = MAGSCALE;
+  clip = FALSE;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  regions = (RegionFile *) NULL;
+  f = (FILE *) NULL;
+  Nz = Nr = Am = Ar = Ad = 0;
+  /* either MagScale or NumScale, whichever is first is scale */
+  Nm = get_argument (argc, argv, "-m");
+  Nn = get_argument (argc, argv, "+n");
+  NN = get_argument (argc, argv, "-n");
+  if (NN && Nn) {
+    gprint (GP_ERR, "can't mix meas and miss scaling\n");
+    return (FALSE);
+  }
+ 
+  if (Nm)
+    mode = MAGSCALE;
+  if (Nn)
+    mode = NUMSCALE;
+  if (NN)
+    mode = MISSCALE;
+    
+  if (Nm && Nn) {
+    clip = TRUE;
+    if (Nm < Nn) 
+      mode = MAGSCALE;
+    else 
+      mode = NUMSCALE;
+  }
+  if (Nm && NN) {
+    clip = TRUE;
+    if (Nm < NN) 
+      mode = MAGSCALE;
+    else 
+      mode = MISSCALE;
+  }
+   
+  current_epoch = 2000.0;
+  epoch = 2000.0;
+  if ((N = get_argument (argc, argv, "-e"))) {
+    remove_argument (N, &argc, argv);
+    epoch  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((N = get_argument (argc, argv, "-ID"))) {
+    remove_argument (N, &argc, argv);
+    IDchoice  = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  if ((Nm = get_argument (argc, argv, "-m"))) {
+    remove_argument (Nm, &argc, argv);
+    Mr  = atof(argv[Nm]);
+    remove_argument (Nm, &argc, argv);
+    Mz = atof(argv[Nm]);
+    Mr = Mr - Mz;
+    remove_argument (Nm, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "+n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  if ((Nn = get_argument (argc, argv, "-n"))) {
+    remove_argument (Nn, &argc, argv);
+    Nz  = atof(argv[Nn]);
+    remove_argument (Nn, &argc, argv);
+    Nr = atof(argv[Nn]) - Nz;
+    remove_argument (Nn, &argc, argv);
+  }
+
+  InRegion = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    InRegion = TRUE;
+  }
+
+  Bytes_Star = 0;
+  ASCII = FALSE;
+  DVO = TRUE;
+  GSC = FALSE;
+  FIXED = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    DVO = FALSE;
+  }
+
+  if ((N = get_argument (argc, argv, "-a"))) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    DVO = FALSE;
+    Ar = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Ad = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Am = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    if ((N = get_argument (argc, argv, "-f"))) {
+      remove_argument (N, &argc, argv);
+      FIXED = TRUE;
+      ASCII = FALSE;
+      Bytes_Star = atof(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+  }
+
+  
+  if ((InRegion || (argc != 2)) && (!InRegion || (argc != 1))) {
+    gprint (GP_ERR, "USAGE: catalog (filename / -all) [-m M M] [-n N N] [-g] [-a RA DEC MAG] \n");
+    return (FALSE);
+  }
+  
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  for (j = 0; j < Nregions; j++) {
+    catalog.average = 0;
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, 1024);
+      ALLOCATE (catalog.average, Average, NSTARS);
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&R, Ar, tbuffer);
+	dparse (&D, Ad, tbuffer);
+	dparse (&M, Am, tbuffer);
+	catalog.average[nstar].R = R;
+	catalog.average[nstar].D = D;
+	catalog.average[nstar].M = M;
+	nstar++;
+	if (nstar == NSTARS - 1) {
+	  NSTARS += DNSTARS;
+	  REALLOCATE (catalog.average, Average, NSTARS);
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (FIXED) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open catalog file: %s\n", argv[1]);
+	return (FALSE);
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*Bytes_Star));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*Bytes_Star;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / Bytes_Star; i++) {
+	  dparse (&R, Ar, &tbuffer[i*Bytes_Star]);
+	  dparse (&D, Ad, &tbuffer[i*Bytes_Star]);
+	  dparse (&M, Am, &tbuffer[i*Bytes_Star]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+
+      if (epoch != current_epoch) {
+	cprecess (catalog.average, catalog.Naverage, epoch, current_epoch);
+      }
+
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      int nstar, NSTARS;
+      double R, D, M;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+	/* return (FALSE); */
+      }
+      
+      nstar = 0;
+      NSTARS = DNSTARS;
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      ALLOCATE (catalog.average, Average, NSTARS);
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+	  dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+	  dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+	  catalog.average[nstar].R = R;
+	  catalog.average[nstar].D = D;
+	  catalog.average[nstar].M = M;
+	  nstar++;
+	  if (nstar == NSTARS - 1) {
+	    NSTARS += DNSTARS;
+	    REALLOCATE (catalog.average, Average, NSTARS);
+	  }
+	}
+      }
+      fclose (f);
+      free (tbuffer);
+      REALLOCATE (catalog.average, Average, nstar);
+      catalog.Naverage = nstar;
+    }
+  
+    /* load data from the photometry database files */
+    if (DVO) {
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s", catdir, argv[1]);
+      }
+      
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      catalog.catflags = LOAD_AVES;
+
+      // an error exit status here is a significant error
+      if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+	  fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+	  exit (2);
+      }
+      dvo_catalog_unlock (&catalog);
+    }
+    
+    /* data has been loaded, get ready to plot it */
+    Xvec.Nelements = catalog.Naverage;
+    Yvec.Nelements = catalog.Naverage;
+    Zvec.Nelements = catalog.Naverage;
+    ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+    ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+    ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+    /* project stars to screen display coords */
+    Xvec.Nelements = 0;
+    switch (mode) {
+    case (MAGSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].Nm < Nz) || (catalog.average[i].Nm > Nr+Nz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].M - Mz) / Mr));
+	if (LimExclude && (Zvec.elements[N] > 0.99)) continue;
+	if (Zvec.elements[N] < 0.011) continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N ++;
+      }
+      break;
+    case (NUMSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nm - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    case (MISSCALE):
+      for (N = i = 0; i < catalog.Naverage; i++) {
+	if (clip && ((catalog.average[i].M > Mz) || (catalog.average[i].M < Mr+Mz))) 
+	  continue;
+	if (IDclip && (catalog.average[i].code != IDchoice))
+	  continue;
+	Zvec.elements[N] = MIN (1.0, MAX (0.01, (catalog.average[i].Nn - Nz) / Nr));
+	if (LimExclude && (Zvec.elements[N] == 1.0)) continue;
+	if (Zvec.elements[N] == 0.01) 
+	  continue;
+	while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+	while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+	if (fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], catalog.average[i].R, catalog.average[i].D, &graphmode.coords)) N++;
+      }
+      break;
+    }
+
+    Zvec.Nelements = Yvec.Nelements = Xvec.Nelements = N;
+    REALLOCATE (Xvec.elements, float, MAX (Xvec.Nelements, 1));
+    REALLOCATE (Yvec.elements, float, MAX (Yvec.Nelements, 1));
+    REALLOCATE (Zvec.elements, float, MAX (Zvec.Nelements, 1));
+    
+    graphmode.style = 2; /* set style to points */
+    graphmode.size = -1; /* point size determined by Zvec */
+    graphmode.etype = 0; /* no errorbars */
+    PrepPlotting (N, &graphmode);
+    
+    PlotVector (N, Xvec.elements, "x");
+    PlotVector (N, Yvec.elements, "y");
+    PlotVector (N, Zvec.elements, "z");
+    
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+
+    if (catalog.average != 0) free (catalog.average);
+
+  }
+  return (TRUE);
+
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catdir.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catdir.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/catdir.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "dvoshell.h"
+
+int catdir_define (int argc, char **argv) {
+  
+  char *current;
+  int status, N, VERBOSE;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: catdir (name)\n");
+    gprint (GP_ERR, "       (name) may be a path or 'default'\n");
+    current = GetCATDIR ();
+    if (current == NULL) {
+      gprint (GP_ERR, "catdir not defined\n");
+    } else {
+      gprint (GP_ERR, "current: %s\n", current);
+    }
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "default")) {
+    status = SetCATDIR (NULL, VERBOSE);
+  } else {
+    status = SetCATDIR (argv[1], VERBOSE);
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "invalid / undefined CATDIR\n");
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ccd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ccd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ccd.c	(revision 17422)
@@ -0,0 +1,126 @@
+# include "dvoshell.h"
+
+int ccd (int argc, char **argv) {
+  
+  double *M1, *M2;
+  int i, m, k, Npts, NPTS, N;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ccd F - F : F - F\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmatch.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmatch.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmatch.c	(revision 17422)
@@ -0,0 +1,82 @@
+# include "dvoshell.h"
+
+int cmatch (int argc, char **argv) {
+  
+  Catalog catalog1, catalog2;
+  char filename[128];
+  double radius;
+  Vector *rvec, *dvec, *mvec, *drvec, *ddvec, *dmvec;
+
+  if (argc != 9) {
+    gprint (GP_ERR, "USAGE: cmatch file radius (RA) (DEC) (Mag) (dRA) (dDEC) (dMag)\n");
+    gprint (GP_ERR, "       match a set of object coordinates with a DVO db table\n");
+    return (FALSE);
+  }
+
+  /*** this function is not well-defined.  re-assess it and re-code it ***/
+  gprint (GP_ERR, "disabled for now\n");
+  return (FALSE);
+
+  radius = atof (argv[2]);
+
+  if ((rvec  = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dvec  = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec  = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((drvec = SelectVector (argv[6], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((ddvec = SelectVector (argv[7], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector (argv[8], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load data from the photometry database file */
+  catalog1.filename = filename;
+  catalog1.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog1, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog1.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog1);
+  gprint (GP_ERR, "read %d stars from phot catalog file %s\n", catalog1.Naverage, filename);
+
+  /* this is for loading from a text file, presumably hstgsc or usno
+     replace this with references to the ra and dec vectors?
+  nstar = 0;
+  NSTARS = DNSTARS;
+  ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+  ALLOCATE (catalog2.average, Average, NSTARS);
+  Nbytes = BLOCK*BYTES_STAR;
+  while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+    for (i = 0; i < nbytes / BYTES_STAR; i++) {
+      dparse (&R, 1, &tbuffer[i*BYTES_STAR]);
+      dparse (&D, 2, &tbuffer[i*BYTES_STAR]);
+      dparse (&M, 3, &tbuffer[i*BYTES_STAR]);
+      catalog2.average[nstar].R = R;
+      catalog2.average[nstar].D = D;
+      catalog2.average[nstar].M = M * 1000.0;
+      nstar++;
+      if (nstar == NSTARS - 1) {
+	NSTARS += DNSTARS;
+	REALLOCATE (catalog2.average, Average, NSTARS);
+      }
+    }
+  }
+  free (tbuffer);
+  REALLOCATE (catalog2.average, Average, MAX (nstar, 1));
+  catalog2.Naverage = nstar;
+  fclose (f);
+  */
+
+  /* sort data in order of RA */
+  sortave (catalog1.average, catalog1.Naverage);
+  sortave (catalog2.average, catalog2.Naverage);
+
+  /* data has been loaded, use gcompare algorithm to match */
+  compare (&catalog1, &catalog2, rvec, dvec, mvec, drvec, ddvec, dmvec, radius);
+
+  dvo_catalog_free (&catalog1);
+  free (catalog2.average);
+
+  return (TRUE);
+
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmd.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmd.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmd.c	(revision 17422)
@@ -0,0 +1,124 @@
+# include "dvoshell.h"
+
+int cmd (int argc, char **argv) { /* really need to think about upper limits & how to represent them */
+  
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  skylist = NULL;
+  selection = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+    
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].measureOffset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: cmd F - F : F\n");
+  return (FALSE);
+
+escape:
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (FALSE);
+}
+    
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpReadFile.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpReadFile.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpReadFile.c	(revision 17422)
@@ -0,0 +1,125 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+CMPstars *cmpReadFits (FILE *f, int *nstars) {
+
+  int i, Nstars;
+  Header theader;
+  FTable table;
+  CMPstars *stars;
+  SMPData *smpdata;
+
+  /* if no stars, no table */
+  if (*nstars == 0) return (NULL);
+
+  /* init & load in table data */
+  table.header   = &theader;
+  if (!gfits_fread_ftable (f, &table, "SMPFILE")) goto escape;
+
+  smpdata = gfits_table_get_SMPData (&table, &Nstars, NULL);
+
+  ALLOCATE (stars, CMPstars, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    stars[i].X      = smpdata[i].X;
+    stars[i].Y      = smpdata[i].Y;
+    stars[i].M      = smpdata[i].M;
+    stars[i].dM     = smpdata[i].dM;
+    stars[i].dophot = smpdata[i].dophot;
+
+    stars[i].Mgal   = smpdata[i].M;
+    stars[i].Map    = smpdata[i].dM;
+    stars[i].fx     = smpdata[i].fx;
+    stars[i].fy     = smpdata[i].fy;
+    stars[i].df     = smpdata[i].df;
+  }    
+  *nstars = Nstars;
+  return (stars);
+
+escape:
+  gprint (GP_ERR, "error reading file\n");
+  *nstars = 0;
+  return (NULL);
+}
+
+CMPstars *cmpReadText (FILE *f, int *nstars) {
+
+  int j, N, Nextra, Ninstar, Nskip, Nbytes, nbytes;
+  int done;
+  char *buffer, *c, *c2;
+  double tmp;
+  CMPstars *stars;
+  
+  /* load in stars by blocks of 1000 */
+  N = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+
+  ALLOCATE (stars, CMPstars, *nstars);
+
+  while (N < *nstars) {
+    /* load next data block */
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      *nstars = N;
+      return (stars);
+    }
+    nbytes += Nextra;
+
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted %d extra return chars\n", Nskip); */
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+	/* if (VERBOSE) gprint (GP_ERR, "deleted line, %d extra chars\n", Nskip); */
+      } else {
+	c = c2;
+      }
+    }
+
+    /* extract data for stars */
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (j = 0; (j < Ninstar) && (N < *nstars); j++, N++) {
+      dparse (&stars[N].X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].M,  3, &buffer[j*BYTES_STAR]);
+
+      /* cmp files carry dM in millimags */
+      dparse (&tmp, 4, &buffer[j*BYTES_STAR]);
+      stars[N].dM = 0.001*tmp;
+
+      dparse (&tmp,         5, &buffer[j*BYTES_STAR]);
+      stars[N].dophot = tmp;
+
+      dparse (&stars[N].Mgal, 7, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].Map,  8, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fx,   9, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].fy,  10, &buffer[j*BYTES_STAR]);
+      dparse (&stars[N].df,  11, &buffer[j*BYTES_STAR]);
+    }
+  }
+  *nstars = N;
+  return (stars);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpload.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpload.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpload.c	(revision 17422)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 66
+# define BLOCK 1000
+
+int cmpload (int argc, char **argv) {
+  
+  int i, Noverlay, NOVERLAY, Nstar, N, Nin, Nextra, Objtype, type;
+  int doneread, done, Nskip, Nbytes, nbytes, Ninstar;
+  char *c, *c2, *name;
+  double dtmp;
+  FILE *f;
+  char *buffer;
+  int kapa;
+  Header header;
+  KiiOverlay *overlay;
+  
+  name = NULL;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!GetImage (NULL, &kapa, name)) return (FALSE);
+  FREE (name);
+
+  Objtype = 0;
+  if ((N = get_argument (argc, argv, "-t"))) {
+    remove_argument (N, &argc, argv);
+    Objtype = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpload (overlay) <filename>\n");
+    return (FALSE);
+  }
+
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  /* find expected number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstar);
+  if (Nstar == 0) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  Noverlay = 0;
+  NOVERLAY = 1000;
+  ALLOCATE (overlay, KiiOverlay, Noverlay);
+  
+  /* load in stars by blocks of 1000 */
+  Nin = 0;
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR) + 1);
+  buffer[BLOCK*BYTES_STAR] = 0;
+  Nextra = 0;
+  doneread = FALSE;
+  while (!doneread) {
+    Nbytes = BYTES_STAR * BLOCK - Nextra;
+    nbytes = fread (&buffer[Nextra], 1, Nbytes, f);
+    if (nbytes == 0) {
+      doneread = TRUE;
+      continue;
+    }
+    nbytes += Nextra;
+    /* check line-by-line integrity */
+    c = buffer;
+    done = FALSE;
+    while ((c < buffer + nbytes) && (!done)) { 
+      for (c2 = c; *c2 == '\n'; c2++);
+      if (c2 > c) { /* extra return chars */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      }
+      c2 = strchr (c, '\n');
+      if (c2 == (char *) NULL) {
+	done = TRUE;	
+	continue;
+      }
+      c2++;
+      if ((c2 - c) != BYTES_STAR) { /* bad line, delete it */
+	memmove (c, c2, (int)(buffer + nbytes - c2));
+	Nskip = c2 - c;
+	nbytes -= Nskip;
+	bzero (buffer + nbytes, Nskip);
+      } else {
+	c = c2;
+      }
+    }
+    Ninstar = nbytes / BYTES_STAR;
+    Nextra = nbytes % BYTES_STAR;
+    for (i = 0; i < Ninstar; i++) {
+      if (Objtype) {
+	dparse (&dtmp, 5, &buffer[i*BYTES_STAR]);
+	type = dtmp;
+	if (type != Objtype) continue;
+      }
+      # if (0)
+      if (scale) {
+	dparse (&mag,  3, &buffer[i*BYTES_STAR]);
+	overlay[Noverlay].dx = mzero + mscale * mag;
+	overlay[Noverlay].dy = mzero + mscale * mag;
+      } else {
+	overlay[Noverlay].dx = 5.0;
+	overlay[Noverlay].dy = 5.0;
+      }      
+      # endif 
+
+      fparse (&overlay[Noverlay].x,  1, &buffer[i*BYTES_STAR]);
+      fparse (&overlay[Noverlay].y,  2, &buffer[i*BYTES_STAR]);
+      overlay[Noverlay].type = KII_OVERLAY_BOX;
+      overlay[Noverlay].dx = 5.0;
+      overlay[Noverlay].dy = 5.0;
+      Noverlay ++;
+      CHECK_REALLOCATE (overlay, KiiOverlay, NOVERLAY, Noverlay, 1000);
+    }
+  }
+  fclose (f);
+
+  KiiLoadOverlay (kapa, overlay, Noverlay, argv[1]);
+  free (overlay);
+
+  gfits_free_header (&header);
+  free (buffer);
+
+  gprint (GP_ERR, "loaded %d objects\n", Noverlay);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpread.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpread.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/cmpread.c	(revision 17422)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+
+/* add others as needed */
+enum {F_NONE, F_RA, F_DEC, F_X, F_Y, F_MAG, F_DMAG, F_TYPE, F_SKY, F_FX, F_FY, F_APMAG, F_GALMAG};
+
+int cmpread (int argc, char **argv) {
+  
+  int i, field, Naxis, Nbytes, Nstars;
+  double tR, tD;
+  float value;
+  FILE *f;
+  Vector *vec;
+  Header header;
+  Coords coords;
+  CMPstars *stars;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: cmpread field <filename>\n");
+    return (FALSE);
+  }
+
+  field = F_NONE;
+  if (!strcasecmp (argv[1], "ra"))   field = F_RA;
+  if (!strcasecmp (argv[1], "dec"))  field = F_DEC;
+  if (!strcasecmp (argv[1], "mag"))  field = F_MAG;
+  if (!strcasecmp (argv[1], "dmag")) field = F_DMAG;
+  if (!strcasecmp (argv[1], "x"))    field = F_X;
+  if (!strcasecmp (argv[1], "y"))    field = F_Y;
+  if (!strcasecmp (argv[1], "type")) field = F_TYPE;
+  if (!strcasecmp (argv[1], "sky"))  field = F_SKY;
+  if (!strcasecmp (argv[1], "fx"))   field = F_FX;
+  if (!strcasecmp (argv[1], "fy"))   field = F_FY;
+  if (!strcasecmp (argv[1], "apmag"))  field = F_APMAG;
+  if (!strcasecmp (argv[1], "galmag"))  field = F_GALMAG;
+  if (field == F_NONE) {
+    gprint (GP_ERR, "invalid cmp field: %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1],  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load FITS header */
+  if (!gfits_read_header (argv[2], &header)) {
+    gprint (GP_ERR, "ERROR: can't read header for %s\n", argv[2]);
+    return (FALSE);
+  }
+
+  if ((field == F_RA) || (field == F_DEC)) {
+    if (!GetCoords (&coords, &header)) {
+      gprint (GP_ERR, "can't get WCS info from header\n");
+      gfits_free_header (&header);
+      return (FALSE);
+    }
+  }
+
+  /* re-open file to load data */
+  f = fopen (argv[2], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't read data from %s\n", argv[2]);
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* find expected number of stars */
+  if (!gfits_scan (&header, "NSTARS", "%d", 1, &Nstars)) {
+    gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
+    gfits_free_header (&header);
+    return (FALSE);
+  }
+
+  /* read from FITS table or from text table */
+  Naxis = 0;
+  gfits_scan (&header, "NAXIS",  "%d", 1, &Naxis);
+  if (Naxis == 2) {
+    /* allocate space for stars */
+    gprint (GP_ERR, "reading from TEXT cmp file %s\n", argv[2]);
+    if (!gfits_scan (&header, "NSTARS", "%d", 1, &Nstars)) {
+      gprint (GP_ERR, "ERROR: failed to find NSTARS\n");
+      exit (1);
+    }
+    stars = cmpReadText (f, &Nstars);
+  } else {
+    gprint (GP_ERR, "reading from FITS cmp file %s\n", argv[2]);
+    Nbytes = gfits_data_size (&header);
+    fseek (f, Nbytes, SEEK_CUR); 
+    stars = cmpReadFits (f, &Nstars);
+  }
+  fclose (f);
+
+  REALLOCATE (vec[0].elements, float, Nstars);
+  vec[0].Nelements = Nstars;
+  bzero (vec[0].elements, Nstars*sizeof(float));
+
+  value = 0;
+  for (i = 0; i < Nstars; i++) {
+    switch (field) {
+      case F_RA:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tR;
+	break;
+      case F_DEC:
+	XY_to_RD (&tR, &tD, stars[i].X, stars[i].Y, &coords);
+	value = tD;
+	break;
+      case F_X:
+	value = stars[i].X;
+	break;
+      case F_Y:
+	value = stars[i].Y;
+	break;
+      case F_MAG:
+	value = stars[i].M;
+	break;
+      case F_APMAG:
+	value = stars[i].Map;
+	break;
+      case F_GALMAG:
+	value = stars[i].Mgal;
+	break;
+      case F_DMAG:
+	value = stars[i].dM;
+	break;
+      case F_TYPE:
+	value = stars[i].dophot;
+	break;
+      case F_SKY:
+	value = stars[i].sky;
+	break;
+      case F_FX:
+	value = stars[i].fx;
+	break;
+      case F_FY:
+	value = stars[i].fy;
+	break;
+    }
+    vec[0].elements[i] = value;
+  }      
+  free (stars);
+  gfits_free_header (&header);
+  gprint (GP_ERR, "loaded %d objects\n", Nstars);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/compare.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/compare.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/compare.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "dvoshell.h"
+# define D_NMATCH 500;
+
+void compare (Catalog *catlog1, Catalog *catlog2, 
+	      Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius) {
+
+  int i, j, first_j, Nmatch, NMATCH;
+  double dX, dY, dR;
+
+  Nmatch = 0;
+  NMATCH = D_NMATCH;
+  REALLOCATE (rvec[0].elements, float, NMATCH);
+  REALLOCATE (dvec[0].elements, float, NMATCH);
+  REALLOCATE (mvec[0].elements, float, NMATCH);
+  REALLOCATE (drvec[0].elements, float, NMATCH);
+  REALLOCATE (ddvec[0].elements, float, NMATCH);
+  REALLOCATE (dmvec[0].elements, float, NMATCH);
+
+  for (i = j = 0; (i < catlog1[0].Naverage) && (j < catlog2[0].Naverage);) {
+    
+    dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+
+    if (!(i % 100))
+      gprint (GP_ERR, ".");
+    
+    if (dX <= -radius)
+      i++;
+    if (dX >= radius)
+      j++;
+
+    if (fabs (dX) < radius) {
+      first_j = j;
+      for (j = first_j; (fabs (dX) < radius) && (j < catlog2[0].Naverage); j++) {
+	dX = catlog1[0].average[i].R - catlog2[0].average[j].R;
+	dY = catlog1[0].average[i].D - catlog2[0].average[j].D;
+	dR = hypot (dX, dY);
+	if (dR < radius) {
+	  rvec[0].elements[Nmatch] = catlog1[0].average[i].R;
+	  dvec[0].elements[Nmatch] = catlog1[0].average[i].D;
+	  // mvec[0].elements[Nmatch] = catlog1[0].average[i].M;
+	  drvec[0].elements[Nmatch] = dX;
+	  ddvec[0].elements[Nmatch] = dY;
+	  // dmvec[0].elements[Nmatch] = catlog1[0].average[i].M - catlog2[0].average[j].M;
+	  Nmatch ++;
+	  if (Nmatch == NMATCH - 1) {
+	    NMATCH += D_NMATCH;
+	    REALLOCATE ( rvec[0].elements, float, NMATCH);
+	    REALLOCATE ( dvec[0].elements, float, NMATCH);
+	    REALLOCATE ( mvec[0].elements, float, NMATCH);
+	    REALLOCATE (drvec[0].elements, float, NMATCH);
+	    REALLOCATE (ddvec[0].elements, float, NMATCH);
+	    REALLOCATE (dmvec[0].elements, float, NMATCH);
+	  }
+	}
+      }
+      j = first_j;
+      i++;
+    }
+  }
+
+  REALLOCATE ( rvec[0].elements, float, Nmatch);
+  REALLOCATE ( dvec[0].elements, float, Nmatch);
+  REALLOCATE ( mvec[0].elements, float, Nmatch);
+  REALLOCATE (drvec[0].elements, float, Nmatch);
+  REALLOCATE (ddvec[0].elements, float, Nmatch);
+  REALLOCATE (dmvec[0].elements, float, Nmatch);
+  
+  rvec[0].Nelements = Nmatch;
+  dvec[0].Nelements = Nmatch;
+  mvec[0].Nelements = Nmatch;
+  drvec[0].Nelements = Nmatch;
+  ddvec[0].Nelements = Nmatch;
+  dmvec[0].Nelements = Nmatch;
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbBooleanCond.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbBooleanCond.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbBooleanCond.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "dvoshell.h"
+
+// evaluate the expression in inStack as a boolean; necessary db field values are
+// supplied by fields, in order 0 - Nfields (validate before calling)
+int dbBooleanCond (dbStack *inStack, int NinStack, float *fields) {
+  
+  float value;
+  int i, j, N, Nstack;
+  dbStack **stack, *output;
+
+  // 'no stack' means 'no where statement'
+  if (NinStack == 0) return (TRUE);
+
+  Nstack = NinStack;
+  ALLOCATE (stack, dbStack *, NinStack);
+  for (i = 0; i < NinStack; i++) {
+    stack[i] = &inStack[i];
+  }
+
+  for (i = 0; i < Nstack; i++) {
+
+    /***** binary operators *****/
+    if ((stack[i][0].type >= 3) && (stack[i][0].type <= 8)) {
+
+      // pre-test that op and entries match
+      output = dbBinary (stack[i-2], stack[i-1], stack[i][0].name, fields); 
+
+      // free temporary stack items, drop external items
+      dbFreeTempEntry (stack[i-2]);
+      dbFreeTempEntry (stack[i-1]);
+
+      stack[i-2] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-2] = stack[j];
+      }
+
+      Nstack -= 2;
+      i -= 2;
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i][0].type == 9) {
+
+      // pre-test that op and entries match
+      output = dbUnary (stack[i-1], stack[i][0].name, fields); 
+
+      // free temporary stack items, drop external items
+      dbFreeTempEntry (stack[i-1]);
+
+      stack[i-1] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-1] = stack[j];
+      }
+
+      Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+
+  // the result here is a single stack entry with a value:
+  if (stack[0][0].type == 'F') {
+    N = stack[0][0].field;
+    value = fields[N];
+  } else {
+    value = stack[0][0].Float;
+  }
+    
+  for (i = 0; i < Nstack; i++) {
+    dbFreeEntry (stack[i]);
+  }
+  free (stack);
+
+  // XXX fix this limit
+  if (fabs(value) > 1e-7) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+
+  // pre-test that op and entries match
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCheckStack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCheckStack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCheckStack.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "dvoshell.h"
+
+int dbCheckStack (dbStack *stack, int Nstack, int table, dbField **inFields, int *inNfields) {
+
+  int i, j, status, NFIELDS, Nfields;
+  char *c;
+  dbField *fields;
+
+  fields = *inFields;
+  Nfields = *inNfields;
+
+  NFIELDS = Nfields + 10;
+  REALLOCATE (fields, dbField, NFIELDS);
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      // this must be a field : is it already in the list?
+      for (j = 0; (j < Nfields) && strcasecmp (stack[i].name, fields[j].name); j++);
+      if (j < Nfields) {
+	stack[i].field = j;
+	stack[i].type  = 'F';
+	stack[i].name  = NULL;
+	stack[i].Float = 0.0;
+	continue;
+      }
+
+      // this must be a field : is it a valid name?
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], stack[i].name);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], stack[i].name);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", stack[i].name);
+	return (FALSE);
+      }
+      stack[i].field = Nfields;
+      stack[i].type  = 'F';
+      stack[i].name  = NULL;
+      stack[i].Float = 0.0;
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dbField, NFIELDS, Nfields, 10);
+    }
+  }
+
+  *inNfields = Nfields;
+  *inFields = fields;
+
+  return (TRUE);
+}
+
+/* check stack identifies the data elements as plain scalars or table fields
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns FALSE
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCmdlineFields.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbCmdlineFields.c	(revision 17422)
@@ -0,0 +1,180 @@
+# include "dvoshell.h"
+
+// identify the fields to be extracted (test for where, check syntax)
+dbField *dbCmdlineFields (int argc, char **argv, int table, int *last, int *nfields) {
+
+  int i, status, Nfields, NFIELDS;
+  char *p, *q, *field;
+  dbField *fields;
+
+  *nfields = 0;
+  Nfields = 0;
+  NFIELDS = 10;
+  ALLOCATE (fields, dbField, NFIELDS);
+  dbInitField (&fields[0]);
+
+  // examine each argv[i] entry until we reach a where 
+  for (i = 1; (i < argc) && strcasecmp (argv[i], "where"); i++) {
+    // split the word by ","
+    p = argv[i];
+    while (*p) {
+      q = strchr (p, ',');
+      if (q == NULL) {
+	field = strcreate (p);
+	p = p + strlen(p);
+      } else {
+	field = strncreate (p, q-p);
+	p = q + 1;
+      }
+      // identify field for word
+      // need to know which type of fields to look for...
+      // xxx extend this more generally later
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], field);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], field);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", field);
+	free (field);
+	dbFreeFields (fields, Nfields);
+	return (NULL);
+      }
+      free (field);
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dbField, NFIELDS, Nfields, 10);
+      dbInitField (&fields[Nfields]);
+    }
+  }
+
+  // require 'where' before boolean math
+  if (i != argc) {
+    if (strcasecmp(argv[i], "where")) {
+      gprint (GP_ERR, "syntax error\n");
+      free (fields);
+      return (NULL);
+    }
+    if (i > argc - 2) {
+      gprint (GP_ERR, "missing boolean expression\n");
+      free (fields);
+      return (NULL);
+    }
+    i++; // skip over the 'where'
+  }
+
+  *last = i;
+  *nfields = Nfields;
+
+  return (fields);
+}
+
+char *strfloat (float value) {
+
+  int Nbyte;
+  char *output;
+  char tmp;
+
+  Nbyte = snprintf (&tmp, 0, "%f", value);
+  ALLOCATE (output, char, Nbyte + 1);
+  snprintf (output, Nbyte + 1, "%f", value);
+  return output;
+}
+
+// identify the fields to be extracted (test for where, check syntax)
+int dbAstroRegionLimits (dbStack **stack, int *nstack, SkyRegionSelection *selection, int table) {
+  
+  int N;
+  double Rmin, Rmax, Dmin, Dmax;
+  char *Rname, *Dname;
+
+  if (!selection->useDisplay && !selection->useSkyregion) return (TRUE);
+
+  // get the ra,dec limits...
+  if (selection->useDisplay) {
+    return (TRUE);
+    // XXX fix this: be more careful with the projection & limits
+
+    Graphdata graphsky;
+    if (!GetGraphData (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return (FALSE);
+    }
+    
+    // XXX the ra and dec range depend on the projection. 
+    // XXX this is wrong...
+    int status;
+    status = XY_to_RD (&Rmin, &Dmin, graphsky.xmin, graphsky.ymin, &graphsky.coords);
+    status = XY_to_RD (&Rmax, &Dmax, graphsky.xmax, graphsky.ymax, &graphsky.coords);
+  }
+
+  if (selection->useSkyregion) {
+    get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+  }    
+
+  N = *nstack;
+  REALLOCATE (*stack, dbStack, N + 20);
+
+  if (table == DVO_TABLE_AVERAGE) {
+    Rname = strcreate ("RA");
+    Dname = strcreate ("DEC");
+  } else {
+    Rname = strcreate ("RA:AVE");
+    Dname = strcreate ("DEC:AVE");
+  }
+
+  // add: ((ra > rmin) && (ra < rmax) && (dec > dmin) && (dec < dmax))
+  // prepend with && if *nstack > 0
+
+  stack[0][N +  0].name = strcreate (Rname);
+  stack[0][N +  0].type = 'X';
+  stack[0][N +  1].name = strfloat (Rmin);
+  stack[0][N +  1].type = 'X';
+  stack[0][N +  2].name = strcreate (">");
+  stack[0][N +  2].type = 4;
+  // stack[0][N +  3].name = strcreate ("A");
+  // stack[0][N +  3].type = 3;
+
+  stack[0][N +  3].name = strcreate (Rname);
+  stack[0][N +  3].type = 'X';
+  stack[0][N +  4].name = strfloat (Rmax);
+  stack[0][N +  4].type = 'X';
+  stack[0][N +  5].name = strcreate ("<");
+  stack[0][N +  5].type = 4;
+  stack[0][N +  6].name = strcreate ("A");
+  stack[0][N +  6].type = 3;
+
+  stack[0][N +  7].name = strcreate (Dname);
+  stack[0][N +  7].type = 'X';
+  stack[0][N +  8].name = strfloat (Dmin);
+  stack[0][N +  8].type = 'X';
+  stack[0][N +  9].name = strcreate (">");
+  stack[0][N +  9].type = 4;
+  stack[0][N + 10].name = strcreate ("A");
+  stack[0][N + 10].type = 3;
+
+  stack[0][N + 11].name = strcreate (Dname);
+  stack[0][N + 11].type = 'X';
+  stack[0][N + 12].name = strfloat (Dmax);
+  stack[0][N + 12].type = 'X';
+  stack[0][N + 13].name = strcreate ("<");
+  stack[0][N + 13].type = 4;
+  stack[0][N + 14].name = strcreate ("A");
+  stack[0][N + 14].type = 3;
+
+  if (N == 0) {
+    N += 15;
+  } else {
+    stack[0][N + 15].name = strcreate ("A");
+    stack[0][N + 15].type = 3;
+    N += 16;
+  }    
+
+  free (Rname);
+  free (Dname);
+
+  *nstack = N;
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractAverages.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractAverages.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractAverages.c	(revision 17422)
@@ -0,0 +1,209 @@
+# include "dvoshell.h"
+
+static CoordTransform *celestial_to_galactic = NULL;
+static CoordTransform *celestial_to_ecliptic = NULL;
+
+static int haveGalactic = FALSE;
+static double GLON = 0.0;
+static double GLAT = 0.0;
+
+static int haveEcliptic = FALSE;
+static double ELON = 0.0;
+static double ELAT = 0.0;
+
+// define a locally-static transform
+int dbExtractAveragesInitTransform (CoordTransformSystem target) {
+
+  // galactic transform is kept forever
+  if (target == COORD_GALACTIC) {
+    if (celestial_to_galactic != NULL) return (TRUE);
+    celestial_to_galactic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+
+  // ecliptic transform must be updated (is weakly time-dependent)
+  if (target == COORD_ECLIPTIC) {
+    if (celestial_to_ecliptic != NULL) {
+      free (celestial_to_ecliptic);
+    }
+    celestial_to_ecliptic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int dbExtractAveragesInit () {
+  haveGalactic = FALSE;
+  haveEcliptic = FALSE;
+  return (TRUE);
+}
+
+/* return average.field based on the selection */
+double dbExtractAverages (Average *average, SecFilt *secfilt, Measure *measure, dbField *field) {
+
+  int i, Nsec;
+  double value;
+
+  value = NAN;
+
+  /* assign vector values */
+  switch (field->ID) {
+    case AVE_RA:
+      value = average[0].R;
+      break;
+    case AVE_DEC:
+      value = average[0].D;
+      break;
+    case AVE_GLON:
+      if (!haveGalactic) {
+	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalactic = TRUE;
+      }
+      value = GLON;
+      break;
+    case AVE_GLAT:
+      if (!haveGalactic) {
+	ApplyTransform (&GLON, &GLAT, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalactic = TRUE;
+      }
+      value = GLAT;
+      break;
+    case AVE_ELON:
+      if (!haveEcliptic) {
+	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEcliptic = TRUE;
+      }
+      value = ELON;
+      break;
+    case AVE_ELAT:
+      if (!haveEcliptic) {
+	ApplyTransform (&ELON, &ELAT, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEcliptic = TRUE;
+      }
+      value = ELAT;
+      break;
+    case AVE_RA_ERR:
+      value = average[0].dR;
+      break;
+    case AVE_DEC_ERR:
+      value = average[0].dD;
+      break;
+
+    case AVE_U_RA:
+      value = average[0].uR;
+      break;
+    case AVE_U_DEC:
+      value = average[0].uD;
+      break;
+    case AVE_U_RA_ERR:
+      value = average[0].duR;
+      break;
+    case AVE_U_DEC_ERR:
+      value = average[0].duD;
+      break;
+
+    case AVE_PAR:
+      value = average[0].P;
+      break;
+    case AVE_PAR_ERR:
+      value = average[0].dP;
+      break;
+
+    case AVE_NMEAS:
+      value = average[0].Nmeasure;
+      break;
+    case AVE_NMISS:
+      value = average[0].Nmissing;
+      break;
+    case AVE_Xp:
+      value = 0.01*average[0].Xp;
+      break;
+    case AVE_FLAG:
+      value = average[0].code;
+      break;
+
+    case AVE_MAG:
+      switch (field->magMode) {
+	case MAG_AVE:
+	  value = PhotAve  (field->photcode, average, secfilt);
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (field->photcode, average, secfilt, measure);
+	  break;
+	case MAG_INST:
+	case MAG_CAT:
+	case MAG_SYS:
+	case MAG_REL:
+	case MAG_CAL:
+	  // XXX need to code this correctly: this returns just the first matching value
+	  value = NAN;
+	  for (i = 0; i < average[0].Nmeasure; i++) {
+	    if (field->photcode->code != measure[i].photcode) continue;
+	    value = measure[i].M;
+	    break;
+	  }
+	  break;
+	case MAG_CHISQ:
+	  // GetPhotcodeEquivCodebyCode (field->photcode)
+	  value = PhotXm (field->photcode, average, secfilt);
+	  break;
+	case MAG_ERR:
+	  // GetPhotcodeEquivCodebyCode (field->photcode)
+	  if (field->photcode->type == PHOT_REF) {
+	    for (i = 0; i < average[0].Nmeasure; i++) {
+	      if (field->photcode->code != measure[i].photcode) continue;
+	      value = measure[i].dM;
+	      break;
+	    }
+	  } else {
+	    value = PhotdM (field->photcode, average, secfilt);
+	  }
+	  break;
+	case MAG_NCODE:
+	  // XXX push these into dvo_photcode_ops APIs
+	  // XXX do I need to allow for conversion to equiv?
+	  Nsec = GetPhotcodeNsec (field->photcode->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Ncode;
+	  break;
+	case MAG_NPHOT:
+	  Nsec = GetPhotcodeNsec (field->photcode->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Nused;
+	  break;
+
+      }
+      break;
+    case AVE_dMAG:
+      value = PhotdM (field->photcode, average, secfilt);
+      break;
+    case AVE_Xm:
+      value = PhotXm (field->photcode, average, secfilt);
+      break;
+    case AVE_TYPE:
+      break;
+    case AVE_TYPEFRAC:
+      break;
+      /*
+    case AVE_NCODE:
+      value = 0;
+      for (i = 0; i < average[0].Nm; i++) {
+	if (field->photcode->code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NPHOT:
+      value = 0;
+      for (i = 0; i < average[0].Nm; i++) {
+	if (field->photcode->code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	if (measure[i].flags & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NCRIT:
+      break;
+      */
+  }
+  return (value);
+}  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractMeasures.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 17422)
@@ -0,0 +1,399 @@
+# include "dvoshell.h"
+
+// to add a new field to the extractions:
+// 1) add the selection for the field below
+// 2) add the field to the ParseMeasureField in dbFields.c
+// 3) add the field to the measure enum list in dvoshell.h
+
+/* time concepts */
+static time_t TimeReference;
+static int TimeFormat;
+
+static time_t TimeRefPM;
+
+static CoordTransform *celestial_to_galactic = NULL;
+static CoordTransform *celestial_to_ecliptic = NULL;
+
+static int haveGalacticAve = FALSE;
+static double GLON_AVE = 0.0;
+static double GLAT_AVE = 0.0;
+
+static int haveEclipticAve = FALSE;
+static double ELON_AVE = 0.0;
+static double ELAT_AVE = 0.0;
+
+static int haveGalacticMeas = FALSE;
+static double GLON_MEAS = 0.0;
+static double GLAT_MEAS = 0.0;
+
+static int haveEclipticMeas = FALSE;
+static double ELON_MEAS = 0.0;
+static double ELAT_MEAS = 0.0;
+
+void dbExtractMeasuresInit () {
+  TimeRefPM = ohana_date_to_sec ("2000/01/01");
+  GetTimeFormat (&TimeReference, &TimeFormat);
+}
+
+// define a locally-static transform
+int dbExtractMeasuresInitTransform (CoordTransformSystem target) {
+
+  // galactic transform is kept forever
+  if (target == COORD_GALACTIC) {
+    if (celestial_to_galactic != NULL) return (TRUE);
+    celestial_to_galactic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+
+  // ecliptic transform must be updated (is weakly time-dependent)
+  if (target == COORD_ECLIPTIC) {
+    if (celestial_to_ecliptic != NULL) {
+      free (celestial_to_ecliptic);
+    }
+    celestial_to_ecliptic = InitTransform (COORD_CELESTIAL, target);
+    return (TRUE);
+  }
+  return (FALSE);
+}
+
+int dbExtractMeasuresInitAve () {
+  haveGalacticAve = FALSE;
+  haveEclipticAve = FALSE;
+  return (TRUE);
+}
+
+int dbExtractMeasuresInitMeas () {
+  haveGalacticMeas = FALSE;
+  haveEclipticMeas = FALSE;
+  return (TRUE);
+}
+
+/* return measure.field based on the selection */
+double dbExtractMeasures (Average *average, SecFilt *secfilt, Measure *measure, dbField *field) {
+
+  int Nsec;
+  double value;
+  double ra, dec, x, y, dT;
+
+  Coords *mosaic;
+  PhotCode *equiv;
+
+  value = NAN;
+
+  switch (field->ID) {
+    case MEAS_MAG: /* magnitudes are already determined above */
+      equiv = GetPhotcodeEquivbyCode (measure[0].photcode);
+
+      // we return the magnitude for this measure if:
+      if (field->photcode->type == PHOT_MAG) goto valid_photcode;
+      if ((field->photcode->type == PHOT_REF) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;
+      if ((field->photcode->type == PHOT_DEP) && (measure[0].photcode == field->photcode->code)) goto valid_photcode;
+
+      if ((equiv != NULL) && (field->photcode->type == PHOT_SEC) && (equiv[0].code == field->photcode->code)) goto valid_photcode;
+      break;
+
+  valid_photcode:
+      switch (field->magMode) {
+	case MAG_INST:
+	  value = PhotInst (measure);  
+	  break;
+	case MAG_CAT:
+	  value = PhotCat  (measure); 
+	  break;
+	case MAG_SYS:
+	  value = PhotSys  (measure, average, secfilt); 
+	  break;
+	case MAG_REL:
+	  value = PhotRel  (measure, average, secfilt); 
+	  break;
+	case MAG_CAL:
+	  value = PhotCal  (measure, average, secfilt, measure, equiv); 
+	  break;
+	case MAG_AVE:
+	  value = PhotAve  (equiv, average, secfilt); 
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (equiv, average, secfilt, measure); 
+	  break;
+	case MAG_ERR:
+	  if (field->photcode->type == PHOT_SEC) {
+	    value = PhotdM  (equiv, average, secfilt); 
+	  } else {
+	    value = measure[0].dM;
+	  }
+	  break;
+	case MAG_CHISQ:
+	  value = PhotXm  (equiv, average, secfilt); 
+	  break;
+	case MAG_NCODE:
+	  if (equiv == NULL) {
+	    value = NAN;
+	    break;
+	  }
+	  Nsec = GetPhotcodeNsec (equiv->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Ncode;
+	  break;
+	case MAG_NPHOT:
+	  if (equiv == NULL) {
+	    value = NAN;
+	    break;
+	  }
+	  Nsec = GetPhotcodeNsec (equiv->code);
+	  if (Nsec == -1) break;
+	  value = secfilt[Nsec].Nused;
+	  break;
+      }
+      break;
+    case MEAS_RA: /* OK */
+      value = average[0].R - measure[0].dR / 3600.0;
+      break;
+    case MEAS_DEC: /* OK */
+      value = average[0].D - measure[0].dD / 3600.0;
+      break;
+    case MEAS_RA_AVE: /* OK */
+      value = average[0].R;
+      break;
+    case MEAS_DEC_AVE: /* OK */
+      value = average[0].D;
+      break;
+
+    case MEAS_GLON:
+      if (!haveGalacticMeas) {
+	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
+	haveGalacticMeas = TRUE;
+      }
+      value = GLON_MEAS;
+      break;
+    case MEAS_GLAT:
+      if (!haveGalacticMeas) {
+	ApplyTransform (&GLON_MEAS, &GLAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_galactic);
+	haveGalacticMeas = TRUE;
+      }
+      value = GLAT_MEAS;
+      break;
+    case MEAS_ELON:
+      if (!haveEclipticMeas) {
+	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
+	haveEclipticMeas = TRUE;
+      }
+      value = ELON_MEAS;
+      break;
+    case MEAS_ELAT:
+      if (!haveEclipticMeas) {
+	ApplyTransform (&ELON_MEAS, &ELAT_MEAS, average[0].R - measure[0].dR / 3600.0, average[0].D - measure[0].dD / 3600.0, celestial_to_ecliptic);
+	haveEclipticMeas = TRUE;
+      }
+      value = ELAT_MEAS;
+      break;
+
+    case MEAS_GLON_AVE:
+      if (!haveGalacticAve) {
+	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalacticAve = TRUE;
+      }
+      value = GLON_AVE;
+      break;
+    case MEAS_GLAT_AVE:
+      if (!haveGalacticAve) {
+	ApplyTransform (&GLON_AVE, &GLAT_AVE, average[0].R, average[0].D, celestial_to_galactic);
+	haveGalacticAve = TRUE;
+      }
+      value = GLAT_AVE;
+      break;
+    case MEAS_ELON_AVE:
+      if (!haveEclipticAve) {
+	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEclipticAve = TRUE;
+      }
+      value = ELON_AVE;
+      break;
+    case MEAS_ELAT_AVE:
+      if (!haveEclipticAve) {
+	ApplyTransform (&ELON_AVE, &ELAT_AVE, average[0].R, average[0].D, celestial_to_ecliptic);
+	haveEclipticAve = TRUE;
+      }
+      value = ELAT_AVE;
+      break;
+
+    case MEAS_RA_AVE_ERR: /* OK */
+      value = average[0].dR;
+      break;
+    case MEAS_DEC_AVE_ERR: /* OK */
+      value = average[0].dD;
+      break;
+    case MEAS_U_RA: /* OK */
+      value = average[0].uR;
+      break;
+    case MEAS_U_DEC: /* OK */
+      value = average[0].uD;
+      break;
+    case MEAS_U_RA_ERR: /* OK */
+      value = average[0].duR;
+      break;
+    case MEAS_U_DEC_ERR: /* OK */
+      value = average[0].duD;
+      break;
+    case MEAS_PAR: /* OK */
+      value = average[0].R;
+      break;
+    case MEAS_PAR_ERR: /* OK */
+      value = average[0].D;
+      break;
+    case MEAS_XP: /* OK */
+      value = average[0].Xp;
+      break;
+    case MEAS_NMEAS: /* OK */
+      value = average[0].Nmeasure;
+      break;
+    case MEAS_NMISS: /* OK */
+      value = average[0].Nmissing;
+      break;
+    case MEAS_OBJFLAGS: /* OK */
+      value = average[0].code;
+      break;
+    // note that these represent the ra displacement relative to the average, not 
+    // the error.
+    case MEAS_RA_OFFSET: /* OK */
+      value = measure[0].dR;
+      break;
+    case MEAS_DEC_OFFSET: /* OK */
+      value = measure[0].dD;
+      break;
+    case MEAS_RA_FIT_OFFSET: /* OK */
+      dT = (measure[0].t - TimeRefPM) / (86400*365.25);
+      value = average[0].uR * dT + measure[0].dR;
+      break;
+    case MEAS_DEC_FIT_OFFSET: /* OK */
+      dT = (measure[0].t - TimeRefPM) / (86400*365.25);
+      value = average[0].uD * dT + measure[0].dD;
+      break;
+    case MEAS_RA_OFFSET_ERR: /* OK */
+      value = NAN;
+      break;
+    case MEAS_DEC_OFFSET_ERR: /* OK */
+      value = NAN;
+      break;
+    case MEAS_AIRMASS: /* OK */
+      value = measure[0].airmass;
+      break;
+    case MEAS_AZ: /* OK */
+      value = measure[0].az;
+      break;
+    case MEAS_EXPTIME: /* OK */
+      value = pow (10.0, measure[0].dt * 0.4);
+      break;
+    case MEAS_PHOTCODE: /* OK */
+      value = measure[0].photcode;
+      break;
+    case MEAS_TIME: /* OK */
+      value = TimeValue (measure[0].t, TimeReference, TimeFormat);
+      break;
+    case MEAS_FWHM: /* OK */
+      value = 0.01*(measure[0].FWx + measure[0].FWy);
+      break;
+    case MEAS_FWHM_MAJ: /* OK */
+      value = 0.01*measure[0].FWx;
+      break;
+    case MEAS_FWHM_MIN: /* OK */
+      value = 0.01*measure[0].FWy;
+      break;
+    case MEAS_THETA: /* OK */
+      value = measure[0].theta;
+      break;
+    case MEAS_DOPHOT: /* OK */
+      value = measure[0].dophot;
+      break;
+    case MEAS_DB_FLAGS: /* ? */
+      value = measure[0].dbFlags;
+      break;
+    case MEAS_PHOT_FLAGS: /* ? */
+      value = measure[0].photFlags;
+      break;
+    case MEAS_XCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 1
+      value = measure[0].Xccd;
+# else
+      { 
+	Image *image;
+	ra  = average[0].R - measure[0].dR / 3600.0;
+	dec = average[0].D - measure[0].dD / 3600.0;
+	image = MatchImage (measure[0].t, measure[0].photcode);
+	if (image == NULL) break;
+	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+	value = x;
+      }
+# endif
+      break;
+    case MEAS_YCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 1
+      value = measure[0].Yccd;
+# else
+      {
+	Image *image;
+	ra  = average[0].R - measure[0].dR / 3600.0;
+	dec = average[0].D - measure[0].dD / 3600.0;
+	image = MatchImage (measure[0].t, measure[0].photcode);
+	if (image == NULL) break;
+	RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+	value = y;
+      }
+# endif
+      break;
+    case MEAS_XMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = x;
+      break;
+    case MEAS_YMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = y;
+      break;
+
+    case MEAS_SKY: /* OK */
+      value = measure[0].Sky;
+      break;
+    case MEAS_dSKY: /* OK */
+      value = measure[0].dSky;
+      break;
+    case MEAS_DET_ID: /* OK */
+      value = measure[0].detID;
+      break;
+    case MEAS_OBJ_ID: /* OK */
+      value = measure[0].averef;
+      break;
+    case MEAS_IMAGE_ID: /* OK */
+      value = measure[0].imageID;
+      break;
+    case MEAS_PSF_QF: /* OK */
+      value = measure[0].qPSF;
+      break;
+    case MEAS_PSF_CHISQ: /* OK */
+      value = measure[0].psfChisq;
+      break;
+    case MEAS_CR_NSIGMA: /* OK */
+      value = measure[0].crNsigma;
+      break;
+    case MEAS_EXT_NSIGMA: /* OK */
+      value = measure[0].extNsigma;
+      break;
+    case MEAS_STARGAL: /* OK */
+      value = measure[0].stargal;
+      break;
+  }
+  return (value);
+}
+
+/** the mosaic entries do not use the registered mosaic found 
+    by MatchImage (via FindMosaicForImage).  Rather, they use
+    a coordinate frame saved by SetImageSelection 
+**/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbFields.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbFields.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbFields.c	(revision 17422)
@@ -0,0 +1,268 @@
+# include "dvoshell.h"
+
+void dbFreeFields (dbField *fields, int Nfields) {
+
+  int i;
+
+  if (fields == NULL) return;
+
+  for (i = 0; i < Nfields; i++) {
+    if (fields[i].name != NULL) free (fields[i].name);
+    if (fields[i].photcode != NULL) {
+      if (fields[i].photcode[0].type == PHOT_MAG) {
+	free (fields[i].photcode);
+      }
+    }
+  }
+  free (fields);
+}
+
+void dbInitField (dbField *field) {
+  field->name = NULL;
+  field->extract = FALSE;
+  field->table = 0;
+  field->ID = 0;
+  field->magMode = 0;
+  field->photcode = NULL;
+}
+
+int GetMagMode (char *string) {
+
+  if (!strcasecmp (string, "inst"))  return (MAG_INST);
+  if (!strcasecmp (string, "cat"))   return (MAG_CAT);
+  if (!strcasecmp (string, "sys"))   return (MAG_SYS);
+  if (!strcasecmp (string, "rel"))   return (MAG_REL);
+  if (!strcasecmp (string, "cal"))   return (MAG_CAL);
+  if (!strcasecmp (string, "ave"))   return (MAG_AVE);
+  if (!strcasecmp (string, "ref"))   return (MAG_REF);
+  if (!strcasecmp (string, "err"))   return (MAG_ERR);
+  if (!strcasecmp (string, "chisq")) return (MAG_CHISQ);
+  if (!strcasecmp (string, "ncode")) return (MAG_NCODE);
+  if (!strcasecmp (string, "nphot")) return (MAG_NPHOT);
+  return (MAG_NONE);
+}
+
+PhotCode *ParsePhotcodeField (char *field, int *mode, int defMode) {
+
+  int useDefault;
+  char *tmpstring, *p;
+  PhotCode *code;
+
+  *mode = defMode;
+  useDefault = TRUE;
+
+  p = strchr (field, ':');
+  if (p != NULL) {
+    *mode = GetMagMode (p + 1);
+    useDefault = FALSE;
+    if (*mode == MAG_NONE) return (NULL);
+    tmpstring = strncreate (field, p - field);
+  } else {
+    tmpstring = strcreate (field);
+  }
+  if (!strcasecmp (tmpstring, "MAG")) {
+    ALLOCATE (code, PhotCode, 1);
+    code[0].code = 0;
+    strcpy (code[0].name, "MAG");
+    code[0].type = PHOT_MAG;
+    free (tmpstring);
+    return (code);
+  }
+  code = GetPhotcodebyName (tmpstring);
+  if (!code) {
+      return NULL;
+  }
+
+
+  // enforce compatibility
+  // XXX this is kind of poorly done
+  if ((code[0].type == PHOT_REF) && (*mode != MAG_ERR)) {
+    *mode = MAG_CAT;
+  }
+  if (code[0].type == PHOT_DEP) {
+    if (useDefault) {
+      *mode = MAG_REL;
+    }
+  } 
+  free (tmpstring);
+  return (code);
+}
+
+# define ESCAPE(F,M) { \
+  field->ID = (F); \
+  field->magMode = (M); \
+  field->photcode = NULL; \
+  return (TRUE); }
+
+int ParseMeasureField (dbField *field, char *fieldName) {
+
+  int mode;
+  PhotCode *code;
+
+  field->table = DVO_TABLE_MEASURE;
+  field->name  = strcreate (fieldName);
+
+  if (!strcasecmp (fieldName, "GLON"))       {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLAT, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLON:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLON_AVE, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_GALACTIC);
+    ESCAPE (MEAS_GLAT_AVE, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "ELON"))       {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELAT, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELON:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELON_AVE, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT:AVE")) {
+    dbExtractMeasuresInitTransform (COORD_ECLIPTIC);
+    ESCAPE (MEAS_ELAT_AVE, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "RA"))         ESCAPE (MEAS_RA,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))        ESCAPE (MEAS_DEC,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "RA:AVE"))     ESCAPE (MEAS_RA_AVE,      	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC:AVE"))    ESCAPE (MEAS_DEC_AVE,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "RA:ERR"))     ESCAPE (MEAS_RA_AVE_ERR,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC:ERR"))    ESCAPE (MEAS_DEC_AVE_ERR, 	MAG_NONE);
+
+  if (!strcasecmp (fieldName, "uRA"))        ESCAPE (MEAS_U_RA,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))       ESCAPE (MEAS_U_DEC,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))       ESCAPE (MEAS_U_RA_ERR,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC"))      ESCAPE (MEAS_U_DEC_ERR,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))        ESCAPE (MEAS_PAR,         	MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))       ESCAPE (MEAS_PAR_ERR,      MAG_NONE);
+  if (!strcasecmp (fieldName, "dR"))         ESCAPE (MEAS_RA_OFFSET,   	MAG_NONE);
+  if (!strcasecmp (fieldName, "dD"))         ESCAPE (MEAS_DEC_OFFSET,  	MAG_NONE);
+  if (!strcasecmp (fieldName, "dR:FIT"))     ESCAPE (MEAS_RA_FIT_OFFSET,  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD:FIT"))     ESCAPE (MEAS_DEC_FIT_OFFSET, MAG_NONE);
+  if (!strcasecmp (fieldName, "dR:ERR"))     ESCAPE (MEAS_RA_OFFSET_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD:ERR"))     ESCAPE (MEAS_DEC_OFFSET_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "xp"))         ESCAPE (MEAS_XP,          	MAG_NONE);
+  if (!strcasecmp (fieldName, "nmeas"))      ESCAPE (MEAS_NMEAS,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "nmiss"))      ESCAPE (MEAS_NMISS,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "objflags"))   ESCAPE (MEAS_OBJFLAGS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "AIRMASS"))    ESCAPE (MEAS_AIRMASS,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "ALT"))        ESCAPE (MEAS_ALT,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "AZ"))         ESCAPE (MEAS_AZ,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "EXPTIME"))    ESCAPE (MEAS_EXPTIME,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOTCODE"))   ESCAPE (MEAS_PHOTCODE,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "TIME"))       ESCAPE (MEAS_TIME,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM"))       ESCAPE (MEAS_FWHM,        	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM_MAJ"))   ESCAPE (MEAS_FWHM_MAJ,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM_MIN"))   ESCAPE (MEAS_FWHM_MIN,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "THETA"))      ESCAPE (MEAS_THETA,       	MAG_NONE);
+  if (!strcasecmp (fieldName, "DOPHOT"))     ESCAPE (MEAS_DOPHOT,      	MAG_NONE);
+  if (!strcasecmp (fieldName, "DB_FLAGS"))   ESCAPE (MEAS_DB_FLAGS,    	MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOT_FLAGS")) ESCAPE (MEAS_PHOT_FLAGS, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XCCD"))       ESCAPE (MEAS_XCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "YCCD"))       ESCAPE (MEAS_YCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XMOSAIC"))    ESCAPE (MEAS_XMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "YMOSAIC"))    ESCAPE (MEAS_YMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "XCHIP"))      ESCAPE (MEAS_XCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "YCHIP"))      ESCAPE (MEAS_YCCD, 	MAG_NONE);
+  if (!strcasecmp (fieldName, "XFPA"))       ESCAPE (MEAS_XMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "YFPA"))       ESCAPE (MEAS_YMOSAIC,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "DETID"))      ESCAPE (MEAS_DET_ID,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "OBJID"))      ESCAPE (MEAS_OBJ_ID,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "IMAGEID"))    ESCAPE (MEAS_IMAGE_ID,     MAG_NONE);
+  if (!strcasecmp (fieldName, "PSF_QF"))     ESCAPE (MEAS_PSF_QF,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "PSF_CHISQ"))  ESCAPE (MEAS_PSF_CHISQ,    MAG_NONE);
+  if (!strcasecmp (fieldName, "CR_NSIGMA"))  ESCAPE (MEAS_CR_NSIGMA,    MAG_NONE);
+  if (!strcasecmp (fieldName, "EXT_NSIGMA")) ESCAPE (MEAS_EXT_NSIGMA,   MAG_NONE);
+  if (!strcasecmp (fieldName, "SKY"))        ESCAPE (MEAS_SKY,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "SKY_ERR"))    ESCAPE (MEAS_dSKY,     	MAG_NONE);
+  if (!strcasecmp (fieldName, "STARGAL"))    ESCAPE (MEAS_STARGAL,     	MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (fieldName, &mode, MAG_REL);
+  if (code == NULL) return (FALSE);
+
+  field->ID = MEAS_MAG;
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
+  
+int ParseAverageField (dbField *field, char *fieldName) {
+
+  int mode;
+  PhotCode *code;
+
+  field->table = DVO_TABLE_AVERAGE;
+  field->name  = strcreate (fieldName);
+
+  // if either GLON or GLAT is requested, we set up a static tranformation 
+  // at prepare to calculate the values only once for each row
+  if (!strcasecmp (fieldName, "GLON"))  { 
+    dbExtractAveragesInitTransform (COORD_GALACTIC);
+    ESCAPE (AVE_GLON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "GLAT"))  {
+    dbExtractAveragesInitTransform (COORD_GALACTIC);
+    ESCAPE (AVE_GLAT, MAG_NONE);
+  }
+
+  // if either ELON or ELAT is requested, we set up a static tranformation 
+  // at prepare to calculate the values only once for each row
+  if (!strcasecmp (fieldName, "ELON"))  { 
+    dbExtractAveragesInitTransform (COORD_ECLIPTIC);
+    ESCAPE (AVE_ELON, MAG_NONE);
+  }
+  if (!strcasecmp (fieldName, "ELAT"))  {
+    dbExtractAveragesInitTransform (COORD_ECLIPTIC);
+    ESCAPE (AVE_ELAT, MAG_NONE);
+  }
+
+  if (!strcasecmp (fieldName, "RA"))    ESCAPE (AVE_RA,        MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))   ESCAPE (AVE_DEC,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dRA"))   ESCAPE (AVE_RA_ERR,    MAG_NONE);
+  if (!strcasecmp (fieldName, "dDEC"))  ESCAPE (AVE_DEC_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "uRA"))   ESCAPE (AVE_U_RA,      MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))  ESCAPE (AVE_U_DEC,     MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))  ESCAPE (AVE_U_RA_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC")) ESCAPE (AVE_U_DEC_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))   ESCAPE (AVE_PAR,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))  ESCAPE (AVE_PAR_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "Xp"))    ESCAPE (AVE_Xp,        MAG_NONE);
+  if (!strcasecmp (fieldName, "NMEAS")) ESCAPE (AVE_NMEAS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NMISS")) ESCAPE (AVE_NMISS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "FLAG"))  ESCAPE (AVE_FLAG,      MAG_NONE);
+  if (!strcasecmp (fieldName, "TYPE"))  ESCAPE (AVE_TYPE,      MAG_NONE);
+  if (!strcasecmp (fieldName, "OBJID")) ESCAPE (AVE_OBJID,     MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (fieldName, &mode, MAG_AVE);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_MAG) {
+    gprint (GP_ERR, "'mag' is ambiguous for avextract\n");
+    free (code);
+    return (FALSE);
+  }
+
+  field->ID = AVE_MAG;
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbRPN.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbRPN.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbRPN.c	(revision 17422)
@@ -0,0 +1,210 @@
+# include "dvoshell.h"
+# define DUMPSTACK 0
+
+dbStack *dbRPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  dbStack *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, dbStack, NSTACK);
+  ALLOCATE (op_stack, dbStack, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    dbInitStack (&stack[i]);
+    dbInitStack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	op_stack[Nop_stack].name = strcreate (argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  stack[Nstack] = op_stack[j];
+	  op_stack[j].name = NULL;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	stack[Nstack].name = strcreate (argv[i]);
+	stack[Nstack].type = 'X';
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    stack[Nstack] = op_stack[j];
+    op_stack[j].name = NULL;
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+  dbFreeStack (op_stack, NSTACK);
+  free (op_stack);
+  dbFreeStack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, dbStack, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL expression to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackMath.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackMath.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackMath.c	(revision 17422)
@@ -0,0 +1,161 @@
+# include "dvoshell.h"
+
+static int NallocBinary = 0;
+static int NallocUnary = 0;
+
+dbStack *dbBinary (dbStack *V1, dbStack *V2, char *op, float *fields) {
+
+  int N;
+  float M1, M2;
+  dbStack *OUT;
+  
+  if (V1->type == 'F') {
+    N = V1->field;
+    M1 = fields[N];
+  } else {
+    M1 = V1->Float;
+  }
+  
+  if (V2->type == 'F') {
+    N = V2->field;
+    M2 = fields[N];
+  } else {
+    M2 = V2->Float;
+  }
+
+  NallocBinary ++;
+  ALLOCATE (OUT, dbStack, 1);
+  OUT->type = 'T';
+  OUT->name = NULL;
+
+  // use an enum for the op...
+  switch (op[0]) { 
+  case '+': 
+    OUT->Float = M1 + M2;
+    break; 
+  case '-': 
+    OUT->Float = M1 - M2;
+    break; 
+  case '*': 
+    OUT->Float = M1 * M2;
+    break; 
+  case '/': 
+    OUT->Float = M1 / M2;
+    break; 
+  case '%': 
+    OUT->Float = (int) M1 % (int) M2;
+    break; 
+  case 0x5e: 
+    OUT->Float = pow (M1, M2);
+    break; 
+  case 'D': 
+    OUT->Float = MIN (M1, M2);
+    break; 
+  case 'U': 
+    OUT->Float = MAX (M1, M2);
+    break; 
+  case '<': 
+    OUT->Float = (M1 < M2) ? 1 : 0;
+    break; 
+  case '>': 
+    OUT->Float = (M1 > M2) ? 1 : 0;
+    break; 
+  case '&': 
+    OUT->Float = ((int)M1 & (int)M2);
+    break; 
+  case '|': 
+    OUT->Float = ((int)M1 | (int)M2);
+    break; 
+  case 'E': 
+    OUT->Float = (M1 == M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    OUT->Float = (M1 != M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    OUT->Float = (M1 <= M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    OUT->Float = (M1 >= M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    OUT->Float = (M1 && M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    OUT->Float = (M1 || M2) ? 1 : 0;
+    break; 
+  default:
+    return (NULL);
+  }
+
+  return (OUT);
+}
+
+dbStack *dbUnary (dbStack *V1, char *op, float *fields) {
+
+  int N;
+  float M1;
+  dbStack *OUT;
+
+  if (V1->type == 'F') {
+    N = V1->field;
+    M1 = fields[N];
+  } else {
+    M1 = V1->Float;
+  }
+  
+  NallocUnary ++;
+  ALLOCATE (OUT, dbStack, 1);
+  OUT->type = 'T';
+  OUT->name = NULL;
+
+  if (!strcmp (op, "="))      {   OUT->Float = M1;                 }
+  if (!strcmp (op, "abs"))    {   OUT->Float = fabs(M1);           }
+  if (!strcmp (op, "int"))    {   OUT->Float = (float)(int)(M1);   }
+  if (!strcmp (op, "exp"))    {   OUT->Float = exp (M1);           }
+  if (!strcmp (op, "ten"))    {   OUT->Float = pow (10.0,M1);      }
+  if (!strcmp (op, "log"))    {   OUT->Float = log10 (M1);         }
+  if (!strcmp (op, "ln"))     {   OUT->Float = log (M1);           }
+  if (!strcmp (op, "sqrt"))   {   OUT->Float = sqrt (M1);          }
+  if (!strcmp (op, "erf"))    {   OUT->Float = erf (M1);           }
+			      			      
+  if (!strcmp (op, "sinh"))   {   OUT->Float = sinh (M1);          }
+  if (!strcmp (op, "cosh"))   {   OUT->Float = cosh (M1);          }
+  if (!strcmp (op, "asinh"))  {   OUT->Float = asinh (M1);         }
+  if (!strcmp (op, "acosh"))  {   OUT->Float = acosh (M1);         }
+  if (!strcmp (op, "lgamma")) {   OUT->Float = lgamma (M1);        }
+
+  if (!strcmp (op, "sin"))    {   OUT->Float = sin (M1);           }
+  if (!strcmp (op, "cos"))    {   OUT->Float = cos (M1);           }
+  if (!strcmp (op, "tan"))    {   OUT->Float = tan (M1);           }
+  if (!strcmp (op, "dsin"))   {   OUT->Float = sin (M1*RAD_DEG);   }
+  if (!strcmp (op, "dcos"))   {   OUT->Float = cos (M1*RAD_DEG);   }
+  if (!strcmp (op, "dtan"))   {   OUT->Float = tan (M1*RAD_DEG);   }
+  if (!strcmp (op, "asin"))   {   OUT->Float = asin (M1);          }
+  if (!strcmp (op, "acos"))   {   OUT->Float = acos (M1);          }
+  if (!strcmp (op, "atan"))   {   OUT->Float = atan (M1);          }
+  if (!strcmp (op, "dasin"))  {   OUT->Float = asin (M1)*DEG_RAD;  }
+  if (!strcmp (op, "dacos"))  {   OUT->Float = acos (M1)*DEG_RAD;  }
+  if (!strcmp (op, "datan"))  {   OUT->Float = atan (M1)*DEG_RAD;  }
+  if (!strcmp (op, "rnd"))    {   OUT->Float = drand48();          }
+  if (!strcmp (op, "not"))    {   OUT->Float = !(M1);              }
+  if (!strcmp (op, "--"))     {   OUT->Float = - (M1);             }
+  if (!strcmp (op, "isinf"))  {   OUT->Float = !finite(M1);        }
+  if (!strcmp (op, "isnan"))  {   OUT->Float = isnan(M1);          } 
+
+  return (OUT);
+}
+
+int dbStackAllocPrint () {
+
+  fprintf (stderr, "dbAllocBinary: %d\n", NallocBinary);
+  fprintf (stderr, "dbAllocUnary:  %d\n", NallocUnary);
+  return (TRUE);
+}
+
+int dbStackAllocReset () {
+
+  NallocBinary = 0;
+  NallocUnary = 0;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dbStackOps.c	(revision 17422)
@@ -0,0 +1,51 @@
+# include "dvoshell.h"
+
+void dbInitStack (dbStack *stack) {
+  stack[0].type   = 0;
+  stack[0].name   = NULL;
+}
+
+// free data for stack entries (free stack explicitly)
+void dbFreeStack (dbStack *stack, int Nstack) {
+
+  int i;
+
+  if (stack == NULL) return;
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].name != NULL) {
+      free (stack[i].name);
+      stack[i].name = NULL;
+    }
+  }
+}
+
+static int NfreeStack = 0;
+
+/* delete name and data */
+void dbFreeEntry (dbStack *stack) {
+
+  if (stack[0].name != NULL)  free (stack[0].name);
+  free (stack);
+  NfreeStack ++;
+}
+
+/* delete name and data */
+void dbFreeTempEntry (dbStack *stack) {
+
+  if (stack->type != 'T') return;
+
+  if (stack[0].name != NULL)  free (stack[0].name);
+  free (stack);
+  NfreeStack ++;
+}
+
+void dbStackFreePrint () {
+
+  fprintf (stderr, "dbFreeStack: %d\n", NfreeStack);
+}
+
+void dbStackFreeReset () {
+
+  NfreeStack = 0;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_check_stack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_check_stack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_check_stack.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "opihi.h"
+
+int db_check_stack (dvoStack *stack, int Nstack, int table, dvoField *fields, int *Nfields) {
+
+  int i, Nx, Ny, Nv, size;
+  char *c;
+
+  NFIELDS = *Nfields + 10;
+  REALLOCATE (fields, dvoField, NFIELDS);
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      // this must be a field : is it already in the list?
+      for (j = 0; (j < *Nfields) && strcasecmp (stack[i].name, fields[j].name); j++);
+      if (j < *Nfields) {
+	stack[i].field = j;
+	stack[i].type  = 'F';
+	continue;
+      }
+
+      // this must be a field : is it a valid name?
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[*Nfields], stack[i].name);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[*Nfields], stack[i].name);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", stack[i].name);
+	return (FALSE);
+      }
+      stack[i].field = *Nfields;
+      stack[i].type  = 'F';
+
+      *Nfields ++;
+      CHECK_REALLOCATE (fields, dvoField, NFIELDS, *Nfields, 10);
+    }
+  }
+  return (TRUE);
+}
+
+/* check stack identifies the data elements as plain scalars or table fields
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns FALSE
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_convert_to_RPN.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_convert_to_RPN.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/db_convert_to_RPN.c	(revision 17422)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+# define DUMPSTACK 0
+
+dvoStack *convert_to_RPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  dvoStack *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, dvoStack, NSTACK);
+  ALLOCATE (op_stack, dvoStack, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    init_stack (&stack[i]);
+    init_stack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	strcpy (op_stack[Nop_stack].name, argv[i]);
+	op_stack[Nop_stack].type = type;
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  strcpy (stack[Nstack].name, op_stack[j].name);
+	  stack[Nstack].type = op_stack[j].type;
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	strcpy (stack[Nstack].name, argv[i]);
+	stack[Nstack].type = 'X';
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    strcpy (stack[Nstack].name, op_stack[j].name);
+    stack[Nstack].type = op_stack[j].type;
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+  clean_stack (op_stack, NSTACK);
+  free (op_stack);
+  clean_stack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, dvoStack, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL line to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ddmags.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ddmags.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/ddmags.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int ddmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M2;
+  int i, m, k, N, Npts, NPTS;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[4];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line options */
+  if (argc != 8) goto usage;
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (strcmp (argv[6], "-")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) return (FALSE);
+  if (!GetPhotcodeInfo (argv[7], &code[3], &mode[3])) return (FALSE);
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // gprint (GP_ERR, "seaching %s with %d stars\n", catalog.filename, catalog.Naverage);
+    // ListPhotSelections ();
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M2 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (&code[0], &mode[0], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractDMag (&code[2], &mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N2);
+      if (N2 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M2, double, 1);
+	  N2 = 1;
+	  M2[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i2 = 0; i2 < N2; i2++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M2[i2];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M2 != NULL) free (M2);
+    }
+    // gprint (GP_ERR, "selected %d stars\n", Npts);
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: ddmags F - F : measure.param\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/detrend.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/detrend.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/detrend.c	(revision 17422)
@@ -0,0 +1,207 @@
+# include "dvoshell.h"
+
+/* qualities to be extracted */
+enum {ZERO, DTIME, SKY, BIAS, FWHM, AIRM, TIME, TEMP};
+
+int detrend (int argc, char **argv) {
+ 
+  FILE *f;
+  int i, Nimage, status, N, TimeSelect;
+  char DataBase[256];
+  time_t tzero, tend;
+  double trange;
+  int TypeSelect, CCDSelect, FilterSelect;
+  char *Filter;
+  int Type, mode, CCD;
+  int NVALUE;
+  float *value;
+  time_t TimeReference;
+  int TimeFormat;
+  Header header;
+  RegImage *pimage;
+  Vector *vec;
+
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, "  this function is not well-defined.  re-work and re-code\n");
+  return (FALSE);
+  
+  /* identify selection */
+  mode = ZERO;
+  if (!strcasecmp (argv[1], "exptime")) mode = DTIME;
+  if (!strcasecmp (argv[1], "sky")) mode = SKY;
+  if (!strcasecmp (argv[1], "bias")) mode = BIAS;
+  if (!strcasecmp (argv[1], "fwhm")) mode = FWHM;
+  if (!strcasecmp (argv[1], "airmass")) mode = AIRM;
+  if (!strcasecmp (argv[1], "time")) mode = TIME;
+  if (!strcasecmp (argv[1], "temp")) mode = TEMP;
+  if (mode == ZERO) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " exptime, sky, bias, fwhm, airmass, time\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  N = 0;
+  NVALUE = 1000;
+  REALLOCATE (vec[0].elements, float, NVALUE);
+  value = vec[0].elements;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  gprint (GP_ERR, "%ld %d\n", TimeReference, TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    /* assign correct value */
+    switch (mode) {
+    case (DTIME):
+      value[N] = pimage[i].exptime;
+      break;
+    case (TIME):
+      value[N] = TimeValue (pimage[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (SKY):
+      value[N] = pimage[i].sky;
+      break;
+    case (BIAS):
+      value[N] = pimage[i].bias;
+      break;
+    case (FWHM):
+      value[N] = pimage[i].fwhm;
+      break;
+    case (AIRM):
+      value[N] = pimage[i].airmass;
+      break;
+    case (TEMP):
+      value[N] = pimage[i].teltemp_0;
+      break;
+    }
+    N++;
+    if (N >= NVALUE - 1) {
+      NVALUE += 1000;
+      REALLOCATE (vec[0].elements, float, NVALUE);
+      value = vec[0].elements;
+    }
+  }
+
+  REALLOCATE (vec[0].elements, float, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagaves.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagaves.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagaves.c	(revision 17422)
@@ -0,0 +1,113 @@
+# include "dvoshell.h"
+
+int dmagaves (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, M2;
+  int i, j, k, m, N1;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  /* interpret command-line options: dmagaves F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetAverageParam (argv[5])) == AVE_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], param)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M2 = ExtractAverages (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], param);
+
+      for (k = 0; k < N1; k++) {
+	xvec[0].elements[Npts] = M1[k];
+	yvec[0].elements[Npts] = M2;
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  REALLOCATE (xvec[0].elements, float, NPTS);
+	  REALLOCATE (yvec[0].elements, float, NPTS);
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagaves F - F : average.param\n");
+  return (FALSE);
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagmeas.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagmeas.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmagmeas.c	(revision 17422)
@@ -0,0 +1,135 @@
+# include "dvoshell.h"
+
+int dmagmeas (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, param, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  Catalog catalog;
+  PhotCode *code[3];
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+  code[2] = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret command-line arguments: dmagmeas F1 - F2 : (value) */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if ((param = GetMeasureParam (argv[5])) == MEAS_ZERO) goto usage;
+  if (!TestPhotSelections (&code[2], &mode[2], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+  if (!SetImageSelection (((param == MEAS_XMOSAIC) || (param == MEAS_YMOSAIC)), ((RegionName == NULL) && (RegionList == NULL)))) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMeasures (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3, param);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmagmeas F - F : measure.param\n");
+  return (FALSE);
+
+escape:
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmags.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmags.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmags.c	(revision 17422)
@@ -0,0 +1,136 @@
+# include "dvoshell.h"
+
+int dmags (int argc, char **argv) {
+  
+  gprint (GP_ERR, "this function is deprecated\n");
+  return (FALSE);
+
+} 
+
+# if (0) 
+  char *RegionName, *RegionList;
+  double *M1, *M3;
+  int i, j, m, i1, i3, N1, N3, N;
+  int Npts, NPTS, mode[3];
+  int Nsecfilt, KeepNulls;
+
+  PhotCode *code[3];
+  Catalog catalog;
+  SkyList *skylist;
+  Vector *xvec, *yvec;
+
+  /* defaults */
+  catalog.average = NULL; 
+  catalog.secfilt = NULL;
+  catalog.measure = NULL;
+  RegionName = NULL;
+  RegionList = NULL;
+  skylist = NULL;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if (!SetRegionSelection (&argc, argv, &RegionName, &RegionList)) goto escape;
+  if (!SetPhotSelections (&argc, argv, 3)) goto usage;
+
+  KeepNulls = FALSE;
+  if ((N = get_argument (argc, argv, "-nulls"))) {
+    KeepNulls = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  /* interpret required command-line arguments: dmags F1 - F2 : F3 */
+  if (argc != 6) { goto usage; }
+  if (strcmp (argv[2], "-")) goto usage;
+  if (strcmp (argv[4], ":")) goto usage;
+  if (!GetPhotcodeInfo (argv[1], &code[0], &mode[0])) goto usage;
+  if (!GetPhotcodeInfo (argv[3], &code[1], &mode[1])) goto usage;
+  if (!GetPhotcodeInfo (argv[5], &code[2], &mode[2])) goto usage;
+  if (!TestPhotSelections (&code[0], &mode[0], MEAS_ZERO)) goto escape;
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (RegionName, RegionList)) == NULL) goto escape;
+
+  /* init vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("xv", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("yv", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* get correct mags, convert to X,Y */
+    for (i = 0; i < catalog.Naverage; i++) {
+      M1 = M3 = NULL;
+      m = catalog.average[i].offset;
+
+      SetSelectionParam (0);
+      M1 = ExtractDMag (code, mode, &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N1);
+      if (N1 == 0) goto skip;
+
+      SetSelectionParam (2);
+      M3 = ExtractMagnitudes (code[2], mode[2], &catalog.average[i], &catalog.secfilt[i*Nsecfilt], &catalog.measure[m], &N3);
+      if (N3 == 0) {
+	if (KeepNulls) {
+	  ALLOCATE (M3, double, 1);
+	  N3 = 1;
+	  M3[0] = NAN;
+	} else {
+	  goto skip;
+	}
+      }
+
+      for (i1 = 0; i1 < N1; i1++) {
+	for (i3 = 0; i3 < N3; i3++) {
+	  xvec[0].elements[Npts] = M1[i1];
+	  yvec[0].elements[Npts] = M3[i3];
+	  Npts++;
+	  if (Npts >= NPTS) {
+	    NPTS += 2000;
+	    REALLOCATE (xvec[0].elements, float, NPTS);
+	    REALLOCATE (yvec[0].elements, float, NPTS);
+	  }
+	}
+      }
+    skip:
+      if (M1 != NULL) free (M1);
+      if (M3 != NULL) free (M3);
+    }
+    dvo_catalog_free (&catalog);
+  }
+
+  /* need to free SkyList / or free all regions as well */
+  SkyListFree (skylist);
+  xvec[0].Nelements = yvec[0].Nelements = Npts;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: dmags F - F : F\n");
+  gprint (GP_ERR, "    F : any photcodes with matched qualifiers:\n");
+  gprint (GP_ERR, "    pri: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    sec: F:inst, F:cat, F:sys, F:rel, F:cal, F:ave, F:ref\n");
+  gprint (GP_ERR, "    dep: F:inst, F:cat, F:sys, F:rel, F:cal\n");
+  gprint (GP_ERR, "    ref: F:cat\n");
+
+escape:
+  SkyListFree (skylist);
+  dvo_catalog_free (&catalog);
+  if (RegionName != NULL) free (RegionName);
+  if (RegionList != NULL) free (RegionList);
+  return (FALSE);
+}
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmt.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmt.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dmt.c	(revision 17422)
@@ -0,0 +1,157 @@
+# include "dvoshell.h"
+
+/* extract vectors giving delta mags for multiple measurements */ 
+int dmt (int argc, char **argv) {
+  
+  // XXX this needs to be fixed: how to access different graphs at once?
+  gprint (GP_ERR, "ERROR: this function is currently disabled\n");
+  return (FALSE);
+
+# if (0)
+
+  int i, m, k, N, kapa, SaveVectors;
+  int Nsec, Nsecfilt, NPTS;
+  double Radius;
+  float dt1, dt2, dmt1, dmt2;
+  float M0, M1, M2, M3;
+  PhotCode *code;
+  Catalog catalog;
+  Graphdata graphmode, graphsky;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector Xvec, Yvec, Zvec, Rvec, Dvec;
+  Vector *vec1, *vec2, *vec3, *vec4, *vec5;
+
+  Dvec.elements = Rvec.elements = Zvec.elements = NULL;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  vec1 = vec2 = vec3 = vec4 = vec5 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-vect"))) {
+    remove_argument (N, &argc, argv);
+    if ((vec1 = SelectVector ("dmtdmt", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("dmtvar", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("dmtmag", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("dmtra",  ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec5 = SelectVector ("dmtdec", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    SaveVectors = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: dmags filter\n");
+    return (FALSE);
+  }
+
+  if (!GetGraphData (&graphsky, &kapa, NULL)) return (FALSE);
+  if (!GetGraph (&graphmode, NULL, NULL)) return (FALSE);
+
+  if ((code = GetPhotcodebyName (argv[1])) == NULL) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+  if (code[0].type != PHOT_SEC) {
+    gprint (GP_ERR, "first filter must be a average photometry type\n");
+    return (FALSE);
+  }
+  Nsecfilt = GetPhotcodeNsecfilt();
+  Nsec = GetPhotcodeNsec (code[0].code);
+
+  Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+  
+  N = 0;
+  NPTS = catalog.Nmeasure;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  if (SaveVectors) {
+    ALLOCATE (Zvec.elements, float, NPTS);
+    ALLOCATE (Rvec.elements, float, NPTS);
+    ALLOCATE (Dvec.elements, float, NPTS);
+  }
+
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (catalog.average[i].Nm != 3) continue;
+      m = catalog.average[i].offset;
+      M0 = catalog.secfilt[i*Nsecfilt+Nsec].M;
+      M1 = PhotCat (&catalog.measure[m+0]);
+      M2 = PhotCat (&catalog.measure[m+1]);
+      M3 = PhotCat (&catalog.measure[m+2]);
+
+      dt1 = (catalog.measure[m+0].t < catalog.measure[m+1].t) ? catalog.measure[m+1].t - catalog.measure[m+0].t : -1 * ((float)(catalog.measure[m+1].t - catalog.measure[m+0].t));
+      dt2 = (catalog.measure[m+1].t < catalog.measure[m+2].t) ? catalog.measure[m+2].t - catalog.measure[m+1].t : -1 * ((float)(catalog.measure[m+2].t - catalog.measure[m+1].t));
+      dmt1 = (M2 - M1) / dt1;
+      dmt2 = (M3 - M2) / dt2;
+      Xvec.elements[N] = (dmt1 - dmt2) / (dmt1 + dmt2);
+      Yvec.elements[N] = (dmt1 + dmt2) / 2.0;
+      if (SaveVectors) {
+	Rvec.elements[N] = catalog.average[i].R;
+	Dvec.elements[N] = catalog.average[i].D;
+	Zvec.elements[N] = M0;
+      }
+      N++;
+      if (N == NPTS - 1) {
+	NPTS += 2000;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+	if (SaveVectors) {
+	  REALLOCATE (Zvec.elements, float, NPTS);
+	  REALLOCATE (Rvec.elements, float, NPTS);
+	  REALLOCATE (Dvec.elements, float, NPTS);
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  REALLOCATE (Xvec.elements, float, MAX (1, N));
+  REALLOCATE (Yvec.elements, float, MAX (1, N));
+  if (SaveVectors) {
+    Rvec.Nelements = Dvec.Nelements = Zvec.Nelements = N;
+    REALLOCATE (Zvec.elements, float, MAX (1, N));
+    REALLOCATE (Rvec.elements, float, MAX (1, N));
+    REALLOCATE (Dvec.elements, float, MAX (1, N));
+  }
+
+  if (SaveVectors) {
+    free (vec1[0].elements);
+    vec1[0].elements = Yvec.elements;
+    vec1[0].Nelements = Yvec.Nelements;
+    free (vec2[0].elements);
+    vec2[0].elements = Xvec.elements;
+    vec2[0].Nelements = Xvec.Nelements;
+    free (vec3[0].elements);
+    vec3[0].elements = Zvec.elements;
+    vec3[0].Nelements = Zvec.Nelements;
+    free (vec4[0].elements);
+    vec4[0].elements = Rvec.elements;
+    vec4[0].Nelements = Rvec.Nelements;
+    free (vec5[0].elements);
+    vec5[0].elements = Dvec.elements;
+    vec5[0].Nelements = Dvec.Nelements;
+  } else {
+    graphmode.style = 2; /* set style to points */
+    PlotVector (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+  }
+  return (TRUE);
+# endif 
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvo.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvo.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvo.c.in	(revision 17422)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+# define opihi_name "DVO"
+# define opihi_prompt "dvo: "
+# define opihi_description "desktop virtual observatory\n"
+# define opihi_history ".dvo"
+# define opihi_rcfile ".dvorc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitDVO ();
+
+  // XXX don't open the catdir until needed
+  // if (!SetCATDIR (NULL, FALSE)) {
+  //  gprint (GP_ERR, "CATDIR is not defined\n");
+  // }
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeDVO ();
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoBooleanElements.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoBooleanElements.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoBooleanElements.c	(revision 17422)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+/* local private functions */
+void InsertValue (char c);
+void EndOfString (void);
+int IsAnOp (char *c);
+int IsTwoOp (char *c);
+
+/* local private static variables */
+int Nchar, Nout, NOUT;
+char **out;
+
+// split up the input arguments into appropriate blocks
+char **dvoBooleanElements (int Nin, char **in, int *nout) {
+  
+  int i, j, minus, negate, plus, posate, OpStat, SciNotation;
+
+  NOUT = Nin;
+  Nchar = Nout = 0;
+  ALLOCATE (out, char *, NOUT);
+  ALLOCATE (out[Nout], char, NCHARS);
+
+  for (i = 0; i < Nin; i++) {
+    for (j = 0; j < strlen(in[i]); j++) {
+      SciNotation = FALSE;
+      /* identify 'negate' or 'minus' ops */
+      negate = minus = FALSE;
+      if (in[i][j] == '-') { 
+	minus = TRUE;  
+	/* if - is first thing on line, must be a negator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if - follows an operator, must be negator */
+	if (OpStat) {
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* if - follows 'e' is part of 1e-5 */
+	if (j == 0) goto skip1;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  negate = minus = FALSE;
+	}
+      }
+    skip1:
+      /* idenfity 'posate' or 'plus' ops */
+      posate = plus = FALSE;
+      if (in[i][j] == '+') { 
+	plus = TRUE;  
+	/* if + is first thing on line, must be a posator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (out[Nout]);
+	  if (out[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (out[Nout-1]);
+	  if (out[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if + follows an operator, must be posator */
+	if (OpStat) {
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* if + follows 'e' is part of 1e+5 */
+	if (j == 0) goto skip2;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  posate = plus = FALSE;
+	}
+      }
+    skip2:
+      /* operators */
+      if (negate || minus || posate || plus || (IsAnOp (&in[i][j]) && !SciNotation)) {
+	if (posate) continue;
+	EndOfString ();
+	/* copy operator to out[Nout] */
+	InsertValue (in[i][j]);
+	if (negate) InsertValue ('-');
+
+	if (IsTwoOp (&in[i][j])) {
+	  InsertValue (in[i][j+1]);
+	  j++;
+	} 
+	EndOfString ();
+	continue;
+      }
+      /* quoted string */
+      if (in[i][j] == '"') {
+	InsertValue (in[i][j]);
+	j++;
+	while ((j < strlen(in[i])) && (in[i][j] != '"')) {
+	  InsertValue (in[i][j]);
+	  j++;
+	}
+	if (in[i][j] != '"') continue;
+	/* 
+	  gprint (GP_ERR, "mismatched quotes\n");
+	  return (FALSE);
+	}
+	*/
+	InsertValue (in[i][j]);
+	EndOfString ();
+	continue;
+      }
+      /* not an operator, not a quoted string */
+      if (!OHANA_WHITESPACE (in[i][j])) {
+	InsertValue (in[i][j]);
+      } else {
+	EndOfString ();
+      }
+    }
+    EndOfString ();
+  }
+
+  /* one extra entry is allocated, free here */
+  free (out[Nout]);
+  *nout = Nout;
+  return (out);
+
+}
+
+void InsertValue (char c) {
+  out[Nout][Nchar] = c;
+  Nchar ++;
+  out[Nout][Nchar] = 0;
+}
+
+void EndOfString () {
+  if (Nchar > 0) {
+    out[Nout][Nchar] = 0;
+    Nout ++;
+    Nchar = 0;
+    
+    if (Nout >= NOUT - 1) {
+      NOUT += 10; 
+      REALLOCATE (out, char *, NOUT); 
+    } 
+    ALLOCATE (out[Nout], char, NCHARS); 
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoEvaluateStack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoEvaluateStack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvoEvaluateStack.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "opihi.h"
+
+int CheckBooleanCondition (dvoStack dbStack, int NdbStack, float *values, dvoFields *fields, int Nfields) {
+  
+  int i, j, Nstack;
+  dvoStack **stack, *output;
+
+  Nstack = NdbStack;
+  ALLOCATE (stack, dvoStack *, NdbStack);
+  for (i = 0; i < NdbStack; i++) {
+    stack[i] = &dbStack[i];
+  }
+
+  for (i = 0; i < Nstack; i++) {
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      // pre-test that op and entries match
+      output = db_binary (stack[i-2], stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      stack[i-2] = output;
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-2] = stack[j];
+      }
+
+      Nstack -= 2;
+      i -= 2;
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      // pre-test that op and entries match
+      output = db_unary (&stack[i-1], stack[i].name, fields, Nfields); 
+
+      // free temporary stack items, drop external items
+      clear_stack (stack[i-2]);
+      clear_stack (stack[i-1]);
+
+      for (j = i + 1; j < Nstack; j++) {
+	stack[j-1] = stack[j];
+      }
+
+      Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+
+  // pre-test that op and entries match
+  return (TRUE);
+}
+
+/* delete name and data */
+void clear_stack (dvoStack *stack) {
+
+  if (stack->type != 'T') return;
+  free (stack);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvodb.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvodb.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvodb.c	(revision 17422)
@@ -0,0 +1,77 @@
+
+/* dvo fields and boolean expressions
+   - evaluate the command line:
+     * avextract ra,dec,g,r,i where (uRA < 0.1)
+     1) identify all fields in extraction list
+     2) parse boolean expression
+     3) identify additional fields from expression
+*/
+
+enum {DVO_TABLE_AVERAGE, DVO_TABLE_MEASURE};
+
+// a single db field 
+typedef struct {
+  char *name;
+  int extract;
+  int table;
+  int ID;
+  int magMode;
+  PhotCode *photcode;
+} dvoField;
+
+// db boolean operations
+typedef struct {
+  char   *name;
+  char    type;
+  int     field;
+  float   Float;
+} dvoStack;
+
+dvoField *ParseCmdlineFields (int argc, char **argv, int table, int *last, int *nfields) {
+
+  int i, Nfields, NFIELDS, 
+
+  *nfields = 0;
+  Nfields = 0;
+  NFIELDS = 10;
+  ALLOCATE (fields, dvoField, NFIELDS);
+
+  // examine each argv[i] entry until we reach a where 
+  for (i = 1; (i < argc) && strcasecmp (argv[i], "where"); i++) {
+    // split the word by ","
+    p = argv[i];
+    while (*p) {
+      q = strchr (p, ',');
+      if (q == NULL) {
+	field = strcreate (p);
+	p = p + strlen(p);
+      } else {
+	field = strncreate (p, q-p);
+	p = q + 1;
+      }
+      // identify field for word
+      // need to know which type of fields to look for...
+      // xxx extend this more generally later
+      if (table == DVO_TABLE_MEASURE) {
+	status = ParseMeasureField (&fields[Nfields], field);
+      } 
+      if (table == DVO_TABLE_AVERAGE) {
+	status = ParseAverageField (&fields[Nfields], field);
+      } 
+      if (!status) {
+	gprint (GP_ERR, "unknown database field %s\n", field);
+	free (field);
+	free (fields);
+	return (NULL);
+      }
+      free (field);
+
+      Nfields ++;
+      CHECK_REALLOCATE (fields, dvoField, NFIELDS, Nfields, 10);
+    }
+  }
+
+  *last = i;
+  *nfields = Nfields;
+  return (fields);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvofields.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvofields.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvofields.c	(revision 17422)
@@ -0,0 +1,129 @@
+
+int GetMagMode (char *string) {
+
+  if (!strcasecmp (string, "inst"))  return (MAG_INST);
+  if (!strcasecmp (string, "cat"))   return (MAG_CAT);
+  if (!strcasecmp (string, "sys"))   return (MAG_SYS);
+  if (!strcasecmp (string, "rel"))   return (MAG_REL);
+  if (!strcasecmp (string, "cal"))   return (MAG_CAL);
+  if (!strcasecmp (string, "ave"))   return (MAG_AVE);
+  if (!strcasecmp (string, "ref"))   return (MAG_REF);
+  if (!strcasecmp (string, "err"))   return (MAG_ERR);
+  if (!strcasecmp (string, "chisq")) return (MAG_CHISQ);
+  return (MAG_NONE);
+}
+
+PhotCode *ParsePhotcodeField (char *field, int *mode, int default) {
+
+  char *tmpstring, *p;
+  PhotCode *code;
+
+  *mode = default;
+
+  p = strchr (field, ':');
+  if (p != NULL) {
+    *mode = GetMagMode (p + 1);
+    if (*mode == MAG_NONE) return (NULL);
+    tmpstring = strncreate (field, p - field);
+  } else {
+    tmpstring = strcreate (field);
+  }
+  code = GetPhotcodebyName (tmpstring);
+  free (tmpstring);
+
+  return (code);
+}
+
+# define ESCAPE(F,M) { \
+  field->ID = (F); \
+  field->magMode = (M); \
+  field->photcode = NULL; \
+  return (TRUE);
+
+int ParseMeasureField (dvoField *field, char *fieldName) {
+
+  PhotCode *code;
+
+  field->table = DVO_TABLE_MEASURE;
+  field->name  = strcreate (fieldName);
+
+  if (!strcasecmp (fieldName, "RA"))       ESCAPE (MEAS_RA,   	  MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))      ESCAPE (MEAS_DEC,  	  MAG_NONE);
+  if (!strcasecmp (fieldName, "MAG"))      ESCAPE (MEAS_MAG,  	  MAG_REL);
+  if (!strcasecmp (fieldName, "dMAG"))     ESCAPE (MEAS_dMAG, 	  MAG_ERR);
+  if (!strcasecmp (fieldName, "AIRMASS"))  ESCAPE (MEAS_AIRMASS,  MAG_NONE);
+  if (!strcasecmp (fieldName, "EXPTIME"))  ESCAPE (MEAS_EXPTIME,  MAG_NONE);
+  if (!strcasecmp (fieldName, "PHOTCODE")) ESCAPE (MEAS_PHOTCODE, MAG_NONE);
+  if (!strcasecmp (fieldName, "TIME"))     ESCAPE (MEAS_TIME,     MAG_NONE);
+  if (!strcasecmp (fieldName, "dR"))       ESCAPE (MEAS_dR, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "dD"))       ESCAPE (MEAS_dD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "FWHM"))     ESCAPE (MEAS_FWHM,     MAG_NONE);
+  if (!strcasecmp (fieldName, "DOPHOT"))   ESCAPE (MEAS_DOPHOT,   MAG_NONE);
+  if (!strcasecmp (fieldName, "FLAGS"))    ESCAPE (MEAS_FLAGS,    MAG_NONE);
+  if (!strcasecmp (fieldName, "XCCD"))     ESCAPE (MEAS_XCCD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "YCCD"))     ESCAPE (MEAS_YCCD, 	  MAG_NONE);
+  if (!strcasecmp (fieldName, "XMOSAIC"))  ESCAPE (MEAS_XMOSAIC,  MAG_NONE);
+  if (!strcasecmp (fieldName, "YMOSAIC"))  ESCAPE (MEAS_YMOSAIC,  MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (&mode, MAG_REL);
+  if (code == NULL) return (FALSE);
+
+  if (mode == MAG_ERR) {
+    field->ID = MEAS_dMAG;
+  } else {
+    field->ID = MEAS_MAG;
+  }    
+  
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
+  
+int ParseAverageField (char *fieldName) {
+
+  PhotCode *code;
+
+  field->table = DVO_TABLE_AVERAGE;
+
+  if (!strcasecmp (fieldName, "RA"))    ESCAPE (AVE_RA,        MAG_NONE);
+  if (!strcasecmp (fieldName, "DEC"))   ESCAPE (AVE_DEC,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dRA"))   ESCAPE (AVE_RA_ERR,    MAG_NONE);
+  if (!strcasecmp (fieldName, "dDEC"))  ESCAPE (AVE_DEC_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "uRA"))   ESCAPE (AVE_U_RA,      MAG_NONE);
+  if (!strcasecmp (fieldName, "uDEC"))  ESCAPE (AVE_U_DEC,     MAG_NONE);
+  if (!strcasecmp (fieldName, "duRA"))  ESCAPE (AVE_U_RA_ERR,  MAG_NONE);
+  if (!strcasecmp (fieldName, "duDEC")) ESCAPE (AVE_U_DEC_ERR, MAG_NONE);
+  if (!strcasecmp (fieldName, "PAR"))   ESCAPE (AVE_PAR,       MAG_NONE);
+  if (!strcasecmp (fieldName, "dPAR"))  ESCAPE (AVE_PAR_ERR,   MAG_NONE);
+  if (!strcasecmp (fieldName, "MAG"))   ESCAPE (AVE_MAG,       MAG_AVE);
+  if (!strcasecmp (fieldName, "dMAG"))  ESCAPE (AVE_dMAG,      MAG_AVE_ERR);
+  if (!strcasecmp (fieldName, "NMEAS")) ESCAPE (AVE_NMEAS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NMISS")) ESCAPE (AVE_NMISS,     MAG_NONE);
+  if (!strcasecmp (fieldName, "Xp"))    ESCAPE (AVE_Xp,        MAG_NONE);
+  if (!strcasecmp (fieldName, "Xm"))    ESCAPE (AVE_Xm,        MAG_CHISQ);
+  if (!strcasecmp (fieldName, "FLAG"))  ESCAPE (AVE_FLAG,      MAG_NONE);
+  if (!strcasecmp (fieldName, "TYPE"))  ESCAPE (AVE_TYPE,      MAG_NONE);
+  if (!strcasecmp (fieldName, "NPHOT")) ESCAPE (AVE_NPHOT,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NCODE")) ESCAPE (AVE_NCODE,     MAG_NONE);
+  if (!strcasecmp (fieldName, "NCRIT")) ESCAPE (AVE_NCRIT,     MAG_NONE);
+
+  // for words that don't parse, try a photcode
+
+  // check for code:mode in photcode name 
+  code = ParsePhotcodeField (&mode, MAG_AVE);
+  if (code == NULL) return (FALSE);
+
+  // need to distinguish phot, sys errors and scatter
+  if (mode == MAG_ERR) {
+    field->ID = MEAS_dMAG;
+  } else {
+    field->ID = MEAS_MAG;
+  }    
+  
+  field->magMode = mode;
+  field->photcode = code;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvomisc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvomisc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/dvomisc.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "dvoshell.h"
+
+void cprecess (Average *average, int Naverage, double in_epoch, double out_epoch) {
+
+  int i;
+  double T;
+  double A, D, RA, DEC, zeta, z, theta;
+  double SA, CA, SD, CD;
+  
+  T = (out_epoch - in_epoch) / 100.0;
+  
+  zeta  = RAD_DEG*(0.6406161*T + 0.0000839*T*T + 0.0000050*T*T*T);
+  theta = RAD_DEG*(0.5567530*T - 0.0001185*T*T - 0.0000116*T*T*T);
+  z     =          0.6406161*T + 0.0003041*T*T + 0.0000051*T*T*T;
+  
+  for (i = 0; i < Naverage; i++) {
+    A = average[i].R;
+    D = average[i].D;
+    SD =  cos(RAD_DEG*A + zeta)*sin(theta)*cos(RAD_DEG*D) + cos(theta)*sin(RAD_DEG*D);
+    CD = sqrt (1 - SD*SD);
+    SA =  sin(RAD_DEG*A + zeta)*cos(RAD_DEG*D)/CD;
+    CA = (cos(RAD_DEG*A + zeta)*cos(theta)*cos(RAD_DEG*D) - sin(theta)*sin(RAD_DEG*D))/CD;
+    
+    DEC = DEG_RAD*asin(SD);
+    RA  = DEG_RAD*atan2(SA, CA) + z;
+    
+    if (RA < 0)
+      RA += 360;
+    
+    average[i].R = RA;
+    average[i].D = DEC; 
+  }
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/elixir.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/elixir.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/elixir.c	(revision 17422)
@@ -0,0 +1,145 @@
+# include "dvoshell.h"
+
+int WriteMsg (char *fifo, char *message);
+int ReadMsg (char *fifo, char **message);
+
+int elixir (int argc, char **argv) {
+  
+  char message[256], cmd[256], ElixirBase[256];
+  char fifo[256], fifodir[256], msgfile[256];
+  char *answer;
+  int N;
+
+  sprintf (cmd, "STATUS");
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "TIMES");
+  }
+  if ((N = get_argument (argc, argv, "-live"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ALIVE");
+  }
+  if ((N = get_argument (argc, argv, "-stop"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "STOP");
+  }
+  if ((N = get_argument (argc, argv, "-kill"))) {
+    remove_argument (N, &argc, argv);
+    sprintf (cmd, "ABORT");
+  }
+ 
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: elixir (elixir) [-time] [-live]\n");
+    return (FALSE);
+  }
+
+  if (!VarConfig (argv[1], "%s", ElixirBase)) {
+    gprint (GP_ERR, "elixir %s not in config file\n", argv[1]);
+    return (FALSE);
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+  if (!VarConfig ("FIFOS", "%s", fifodir)) {
+    gprint (GP_ERR, "FIFOS not in config, using local /tmp\n");
+    strcpy (fifodir, "/tmp");
+  }
+  sprintf (fifo, "%s.msg", ElixirBase);
+
+  sprintf (msgfile, "%s/EMsg.XXXXXX", fifodir);
+  mkstemp (msgfile);
+  sprintf (message, "%s %s", cmd, msgfile);
+  unlink (msgfile);
+
+  if (!WriteMsg (fifo, message)) {
+    gprint (GP_ERR, "can't access fifo %s\n", fifo);
+    return (FALSE);
+  }
+
+  if (ReadMsg (msgfile, &answer)) {
+    gprint (GP_ERR, "%s\n", answer);
+  } 
+  unlink (msgfile);
+  return (TRUE);
+  
+}
+
+int WriteMsg (char *fifo, char *message) {
+
+  int state, mode;
+  FILE *f;
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) return (0);
+
+  /* write message to end of file */
+  fseek (f, 0, SEEK_END);
+  fprintf (f, "%s\n", message);
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+  return (1);
+}
+
+int ReadMsg (char *fifo, char **message) {
+
+  int i, nbytes, Nbytes, NBYTES;
+  char *buffer;
+  int state, mode;
+  FILE *f;
+  struct stat filestat;
+
+  /* wait (2 sec) for file to exist, then try to read it */
+  for (i = 0; (stat (fifo, &filestat) == -1) && (i < 20); i++) {
+    usleep (100000);
+  }
+  if (i >= 20) {
+    gprint (GP_ERR, "no response\n");
+    return (0);
+  }
+
+  /* check lockfile */
+  f = fsetlockfile (fifo, 2.0, LCK_XCLD, &state);
+  if (f == NULL) {
+    gprint (GP_ERR, "message locked (%d)\n", state);
+    return (0);
+  }
+
+  /* if file is empty, return 0 */
+  if (state == LCK_EMPTY) {
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    chmod (fifo, mode);
+    fclearlockfile (fifo, f, LCK_XCLD, &state);
+    return (0);
+  }  
+
+  Nbytes = 0;
+  NBYTES = 0x1000;
+  ALLOCATE (buffer, char, NBYTES);
+  while (TRUE) {
+    nbytes = fread (&buffer[Nbytes], 1, 0x1000, f);
+    if (nbytes < 0) { 
+      gprint (GP_ERR, "error in ReadMsg -- got -1 bytes\n");
+      return (0);
+    }
+    if (nbytes == 0) break;
+    Nbytes += nbytes;
+    NBYTES += 0x1000;
+    REALLOCATE (buffer, char, NBYTES);
+  }
+  buffer[Nbytes] = 0;
+
+  mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+  chmod (fifo, mode);
+  fclearlockfile (fifo, f, LCK_XCLD, &state);
+
+  if (Nbytes == 0) {
+    free (buffer);
+    return (0);
+  }
+
+  *message = buffer;
+  return (1);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/extract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/extract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/extract.c	(revision 17422)
@@ -0,0 +1,450 @@
+# include "dvoshell.h"
+// XXX EAM : this function is deprecated
+
+enum {ZERO, RA, DEC, MAG, dMAG, Xm, Xp, NMEAS, NMISS, REF, TYPE, NPHOT, NCODE, FLAG};
+
+# define NBYTES 160000
+# define BYTES_STAR 23
+# define BLOCK 1000
+# define DNSTARS 1000
+
+int extract (int argc, char **argv) {
+  
+  FILE *f;
+  int i, Col, N, Nbytes, nbytes, NPTS;
+  int InRegion, GSC, ASCII, LONEOS, mode, loadmode;
+  int j, k, m, Nregions;
+  float M0, m0;
+  char filename[128];
+  float Radius;
+  char catdir[256], gscdir[256];
+  int PhotcodeSelect;
+  char PhotCodeFile[256], code[64];
+  double ZERO_POINT;
+  int Ns, N1, n1, Nsec;
+  int Ngraph;
+  int value;
+  Vector *vec;
+  PhotCodeData photcodes;
+  Graphdata graphmode;
+  Catalog catalog;
+  RegionFile *regions;
+
+  if (!GetGraphData (&graphmode, NULL, NULL)) return (FALSE);
+  if (!InitPhotcodes ()) return (FALSE);
+
+  VarConfig ("GSCDIR", "%s", gscdir);
+  VarConfig ("CATDIR", "%s", catdir);
+
+  regions = (RegionFile *) NULL;
+  ASCII = FALSE;
+  LONEOS = TRUE;
+  GSC = FALSE;
+  if (N = get_argument (argc, argv, "-g")) {
+    remove_argument (N, &argc, argv);
+    GSC = TRUE;
+    ASCII = FALSE;
+    LONEOS = FALSE;
+  }
+
+  Col = 1;
+  if (N = get_argument (argc, argv, "-a")) {
+    remove_argument (N, &argc, argv);
+    ASCII = TRUE;
+    GSC = FALSE;
+    LONEOS = FALSE;
+    Col = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* check for region-based selection */
+  code = NULL;
+  PhotcodeSelect = FALSE;
+  if (N = get_argument (argc, argv, "-photcode")) {
+    PhotcodeSelect = True;
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: extract (filename) (value) [-g / -a Ncol] \n");
+    return (FALSE);
+  }
+  
+  InRegion = FALSE;
+  if (!strcmp (argv[1], "-all")) {
+    InRegion = TRUE;
+  }
+
+  /* identify selection */
+  if (LONEOS) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) mode = RA;
+    if (!strcasecmp (argv[2], "dec")) mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) mode = MAG;
+    if (!strcasecmp (argv[2], "Nmeas")) mode = NMEAS;
+    if (!strcasecmp (argv[2], "Nmiss")) mode = NMISS;
+    if (!strcasecmp (argv[2], "Xp")) mode = Xp;
+    if (!strcasecmp (argv[2], "Xm")) mode = Xm;
+    if (!strcasecmp (argv[2], "dM")) mode = dMAG;
+    if (!strcasecmp (argv[2], "flag")) mode = FLAG;
+    if (!strcasecmp (argv[2], "ref")) mode = REF;
+    if (!strcasecmp (argv[2], "type")) mode = TYPE;
+    if (!strcasecmp (argv[2], "Nphot")) mode = NPHOT;
+    if (!strcasecmp (argv[2], "Ncode")) mode = NCODE;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag Nmeas Nmiss Xp Xm ID\n");
+      return (FALSE);
+    }
+  }
+
+  if ((mode == REF) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Reference\n");
+    return (FALSE);
+  }
+  if ((mode == TYPE) && !PhotcodeSelect) {
+    gprint (GP_ERR, "must specify photcode for Type\n");
+    return (FALSE);
+  }
+
+  /* check photcode data / selection validity */
+  Ns = -1;
+  Nsec = GetPhotcodeNsecfilt ();
+  if (PhotcodeSelect) {
+    Ns = GetPhotcodeNsec (code[0].code);
+    if ((mode != REF) && (code[0].type != PHOT_SEC)) {
+      gprint (GP_ERR, "filter must be a average photometry type\n");
+      return (FALSE);
+    }
+    if ((mode == REF) && (code[0].type != PHOT_REF)) {
+      gprint (GP_ERR, "filter must be a REFERENCE photometry type\n");
+      return (FALSE);
+    }
+  }
+
+  if (GSC) {
+    mode = ZERO;
+    if (!strcasecmp (argv[2], "ra")) 
+      mode = RA;
+    if (!strcasecmp (argv[2], "dec")) 
+      mode = DEC;
+    if (!strcasecmp (argv[2], "mag")) 
+      mode = MAG;
+    if (mode == ZERO) {
+      gprint (GP_ERR, "for GSC, value may be one of the following:\n");
+      gprint (GP_ERR, " ra dec mag\n");
+      return (FALSE);
+    }
+  }
+
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (InRegion) {
+    Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+    regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+  } else {
+    Nregions = 1;
+  }
+  
+  /* create storage vector */
+  NPTS = 1000;
+  REALLOCATE (vec[0].elements, float, NPTS);
+  vec[0].Nelements = N = 0;
+
+  /* we loop over Nregions, but for ASCII Nregions = 1 */
+  for (j = 0; j < Nregions; j++) {
+    
+    /* Load in data from an ASCII file list of ra, dec, mag */
+    if (ASCII) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      f = fopen (argv[1], "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "ERROR: can't open data file: %s\n", argv[1]);
+	/* delete new vector! */
+	return (FALSE);
+      }
+      ALLOCATE (tbuffer, char, 1024);
+      
+      V = vec[0].elements;
+      while (scan_line (f, tbuffer) != EOF) {
+	dparse (&tmp, Col, tbuffer);
+	*V = tmp;
+	V++;
+	N++;
+	if (N == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (vec[0].elements, float, NPTS);
+	  V = &vec[0].elements[N];
+	}
+      }
+      free (tbuffer);
+      vec[0].Nelements = N;
+      REALLOCATE (vec[0].elements, float, MAX(1,N));
+      fclose (f);
+      return (TRUE);
+    }
+    
+    /* load data from the GSC files */
+    if (GSC) {
+      char *tbuffer;
+      double tmp;
+      float *V;
+      
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", gscdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", gscdir, argv[1]);
+      }
+      
+      f = fopen (filename, "r");
+      if (f == (FILE *) NULL) {
+	gprint (GP_ERR, "no stars in %s, skipping\n", filename);
+	continue;
+      }
+      ALLOCATE (tbuffer, char, (BLOCK*BYTES_STAR));
+      
+      V = &vec[0].elements[N];
+      Nbytes = BLOCK*BYTES_STAR;
+      while ((nbytes = fread (tbuffer, 1, Nbytes, f)) > 0) {
+	for (i = 0; i < nbytes / BYTES_STAR; i++) {
+	  if (mode == RA) {
+	    dparse (&tmp, 1, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == DEC) {
+	    dparse (&tmp, 2, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  if (mode == MAG) {
+	    dparse (&tmp, 3, &tbuffer[i*BYTES_STAR]);
+	    *V = tmp;
+	  }
+	  V++;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	    V = &vec[0].elements[N];
+	  }
+	}
+      }
+      free (tbuffer);
+      fclose (f);
+    }
+  
+    /* load data from the photometry database files */
+    if (LONEOS) {
+      /* find and open correct file */
+      if (InRegion) {
+	sprintf (filename, "%s/%s\0", catdir, regions[j].name);
+      } else {
+	sprintf (filename, "%s/%s\0", catdir, argv[1]);
+      }
+      catalog.average = (Average *) NULL;
+      catalog.measure = (Measure *) NULL;
+      catalog.secfilt = (SecFilt *) NULL;
+      loadmode = LOAD_AVES | LOAD_SECF;
+      if ((mode == REF) || (mode == TYPE) || (mode == NPHOT) || (mode == NCODE)) 
+	loadmode = loadmode | LOAD_MEAS;
+
+      /* lock, load, unlock catalog */
+      catalog.filename = filename;
+      switch (lock_catalog (&catalog, LCK_SOFT)) {
+      case 2:
+	unlock_catalog (&catalog);
+      case 0:
+	continue;
+      }
+      catalog.catflags = loadmode;
+      if (!load_catalog (&catalog, TRUE)) {
+	unlock_catalog (&catalog);
+	continue;
+      }
+      unlock_catalog (&catalog);
+
+      /* assign vector values */
+      switch (mode) {
+      case (RA):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].R;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (DEC):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].D;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (MAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].M : catalog.secfilt[i*Nsec+Ns].M;
+	  vec[0].elements[N] = M0 / 1000.0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMEAS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nm;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NMISS):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].Nn;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xp):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* Xp is scatter in 1/100 arcsec */
+	  vec[0].elements[N] = 0.01*catalog.average[i].Xp;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NCODE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (NPHOT):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  Ncode = 0;
+	  for (k = 0; k < catalog.average[i].Nm; k++, m++) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	    if (catalog.measure[m].photcode & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	    Ncode ++;
+	  }
+	  vec[0].elements[N] = Ncode;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (Xm):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  M0 = (Ns == -1) ? catalog.average[i].Xm : catalog.secfilt[i*Nsec+Ns].Xm;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : pow (10.0, 0.01*M0);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (dMAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  /* dM is 1000.0 * error */
+	  M0 = (Ns == -1) ? catalog.average[i].dM : catalog.secfilt[i*Nsec+Ns].dM;
+	  vec[0].elements[N] = (M0 == NO_MAG) ? -1.0 : 0.001*M0;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (FLAG):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  vec[0].elements[N] = catalog.average[i].code;
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (REF):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = -32;
+	  for (k = 0; k < catalog.average[i].Nm; k++) {
+	    if (catalog.measure[m+k].photcode == N1) {
+	      vec[0].elements[N] = PhotCat (&catalog.measure[m+k]);
+	      k = catalog.average[i].Nm;
+	    }
+	  }
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      case (TYPE):
+	for (i = 0; i < catalog.Naverage; i++) {
+	  m = catalog.average[i].offset;
+	  vec[0].elements[N] = DetermineTypeCode (&catalog.average[i], &catalog.measure[m], &photcodes, N1);
+	  N++;
+	  if (N == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (vec[0].elements, float, NPTS);
+	  }
+	}
+	break;
+      }
+      if (catalog.average != 0) free (catalog.average);
+      if (catalog.measure != 0) free (catalog.measure);
+      if (catalog.secfilt != 0) free (catalog.secfilt);
+    }
+  }
+
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, MAX(1,N));
+  return (TRUE);
+
+}
+  
+  /* USAGE: extract (what) (where) (from) [option] */
+  /* examples:
+     extract n0000/n0001 mag m 
+     extract -g n0000/n0020 ra r 
+     extract -all Xm xm
+     extract fred dec d -a 2 */
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/find_regions.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/find_regions.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/find_regions.c	(revision 17422)
@@ -0,0 +1,156 @@
+# include "dvoshell.h"
+# include "hstgsc.h"
+
+/* returns a list of region files within the desired RA, DEC region */
+RegionFile *find_regions (double Ra, double Dec, double radius, int *Nregions) {
+  
+  char filename[256];
+  char buffer[28800], temp[50];
+  RegionFile *regions;
+  FILE *f;
+  double minRa, maxRa, minDec, maxDec, rad;
+
+  double RA0, RA1, DEC0, DEC1;
+  int i, j, NBigDec;
+  int NLINES, done, NREGIONS, nregion;
+  
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find regions file %s\n", filename);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  NREGIONS = 50;
+  ALLOCATE (regions, RegionFile, NREGIONS);
+  nregion = 0;
+
+  while (Ra < 0) Ra += 360.0;
+  while (Ra >= 360) Ra -= 360.0;
+
+  minDec = Dec - radius;
+  maxDec = Dec + radius;
+
+  if ((minDec <= -90) || (maxDec >= 90)) {
+    minRa = 0;
+    maxRa = 360;
+  } else {
+    rad = MAX (radius / (cos(minDec*RAD_DEG)), radius / (cos(maxDec*RAD_DEG)));
+    minRa = Ra - rad;
+    maxRa = Ra + rad;
+  }
+  
+  /* use the pole regions, if near pole */
+  if (maxDec > 86.25) {
+    sprintf (regions[nregion].name, "n8230/pole.cpt");
+    regions[nregion].RA0 = 0;
+    regions[nregion].RA1 = 360;
+    regions[nregion].DEC0 = 86.25;
+    regions[nregion].DEC1 = 90.0;
+    nregion ++;
+    if (nregion == NREGIONS) {
+      NREGIONS += 50;
+      REALLOCATE (regions, RegionFile, NREGIONS);
+    }
+  }
+
+  if (minDec > 86.25) {
+    return (regions);
+  }
+    
+  if ((minDec < 0) && (maxDec > 0)) {
+    /* Search Both Sides */
+    NBigDec = 0;
+  } else {
+    /* find large DEC region (directory) */
+    NBigDec = -1;
+    for (i = 0; i < 12; i++) {
+      if ((minDec >= BigDecBounds[i]) && (minDec < BigDecBounds[i+1])) {
+	NBigDec = i;
+	break;
+      }
+    }
+    if (NBigDec < 0) {
+      for (i = 13; i < 24; i++) {
+	if ((maxDec < BigDecBounds[i]) && (maxDec >= BigDecBounds[i+1])) {
+	  NBigDec = i;
+	  break;
+	}
+      }
+    }
+  }
+  if (NBigDec < 0) {
+    gprint (GP_ERR, "ERROR: Dec out of range: %f\n", minDec);
+    *Nregions = 0;
+    return ((RegionFile *) NULL);
+  }
+  
+  /* count lines before section */
+  NLINES = 0;
+  for (i = 0; i < NBigDec; i++) {
+    NLINES += NDecLines[i];
+  }
+  fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+  
+  /* should be in this section.  if not, there is a problem counting... */
+  /* careful with the 0,360.0 boundary **/
+  done = FALSE;
+  for (j = 0; !done && (NBigDec + j < 25); j++) {
+    fread (buffer, 48*NDecLines[NBigDec + j], 1, f);
+    for (i = 0; (i < NDecLines[NBigDec + j]); i++) {
+      strncpy (temp, &buffer[i*48], 48);
+      temp[49] = 0;
+      hstgsc_hms_to_deg (&RA0, &RA1, &DEC0, &DEC1, &temp[7]);
+      if (RA1 < RA0) RA1 += 360.0;
+      if ((DEC1 > 0) && (minDec < DEC1) && (maxDec > DEC0) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if ((DEC1 < 0) && (minDec < DEC0) && (maxDec > DEC1) && (minRa < RA1) && (maxRa > RA0)) {
+	temp[5] = 0;
+	sprintf (regions[nregion].name, "%s/%s.cpt", Dec2Sections[NBigDec + j], &temp[1]);
+	regions[nregion].RA0 = RA0;
+	regions[nregion].RA1 = RA1;
+	regions[nregion].DEC0 = DEC0;
+	regions[nregion].DEC1 = DEC1;
+	nregion ++;
+	if (nregion == NREGIONS) {
+	  NREGIONS += 50;
+	  REALLOCATE (regions, RegionFile, NREGIONS);
+	}
+      }
+      if (((DEC1 > 0) && (maxDec <= DEC1)) || ((DEC1 < 0) && (minDec >= DEC1))) {
+	done = TRUE;
+      }
+    }
+    if (done && (minDec < 0) && (maxDec > 0) && (BigDecBounds[NBigDec + j + 1] > 0)) {
+      /* skip remaining north sections, try south sections */
+      /* count lines before section */
+      NLINES = 0;
+      for (i = 0; i < 13; i++) { 
+	NLINES += NDecLines[i];
+      }
+      fseek (f, 5*2880 + 48*NLINES, SEEK_SET);
+      done = FALSE;
+      j = 12;
+    }
+  }
+
+  REALLOCATE (regions, RegionFile, MAX(1,nregion));
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (regions);
+  
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitcolors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitcolors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitcolors.c	(revision 17422)
@@ -0,0 +1,390 @@
+# include "dvoshell.h"
+# define NMIN_PTS 100
+
+static void free_catalog (Catalog *catalog, int Ncatalog);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitcolors (int argc, char **argv) {
+  
+  int *list, Nlist;
+  int i, k, m, N, NP1, NP2, NP, Np, Npts, NPTS;
+  int N1, N2, i1, i2, mode[4];
+  int Nsecfilt, status;
+  void *oldsignal;
+  char *cmd, *outcmd, *camera;
+  char name[64], filename[64], plotname[64], label[64];
+  double *M1, *M2;
+  float *out, *colorFit, *deltaFit, dColor, C0, C1;
+  float minDelta, maxDelta, minColor, maxColor;
+  int kapa, Npx, Npy, NPX, NPY, Nplot, PLOT;
+  Graphdata graphdata;
+  KapaSection section;
+
+  Catalog *catalog;
+  PhotCode **codelist, *tcode, *code[4];
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+  Vector *xvec, *yvec;
+  Buffer *buf;
+
+  /* defaults */
+  catalog  = NULL;
+  skylist  = NULL;
+  selection = NULL;
+  codelist = NULL;
+  xvec = yvec = NULL;
+  colorFit = NULL;
+  deltaFit = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  // range for valid data points (exclude extreme outliers)
+  minDelta = -0.2;
+  maxDelta = +0.2;
+  minColor = -1.0;
+  maxColor = +3.0;
+  if ((N = get_argument (argc, argv, "-color-range"))) {
+    remove_argument (N, &argc, argv);
+    minColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxColor = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-delta-range"))) {
+    remove_argument (N, &argc, argv);
+    minDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    maxDelta = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOT = FALSE;
+  NPX = NPY = 0;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (plotname, argv[N]);
+    remove_argument (N, &argc, argv);
+    NPX = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    NPY = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 4) goto usage;
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    Nplot = 0;
+    Npx = Npy = 0;
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    KapaSetFont (kapa, "helvetica", 14);
+
+    ALLOCATE (colorFit, float, 11);
+    ALLOCATE (deltaFit, float, 11);
+    dColor = (maxColor - minColor) / 10.0;
+    for (i = 0; i < 11; i++) {
+      colorFit[i] = minColor + i*dColor;
+    }
+  }
+
+  /* determine relevant photcodes, colors */
+  if (!(Np = GetPhotcodeCodebyName (argv[2]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    goto usage;
+  }
+  camera = argv[3];
+
+  /* reduce the list of codes */
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  ALLOCATE (codelist, PhotCode *, Nlist);
+  for (i = NP = 0; i < Nlist; i++) {
+    tcode = GetPhotcodebyCode (list[i]);
+    if (strncmp (tcode[0].name, camera, strlen(camera))) continue;
+    codelist[NP] = tcode;
+    NP++;
+  }
+  mode[0] = mode[1] = MAG_REL;  /* we should be applying any relative photometry corrections here */
+  mode[2] = mode[3] = MAG_AVE;
+
+  /* set the reference colors */
+  code[2] = GetPhotcodebyCode (codelist[0][0].c1);
+  code[3] = GetPhotcodebyCode (codelist[0][0].c2);
+  if ((code[2] == NULL) || (code[3] == NULL)) goto color_missing;
+
+  /* all codes must have the same colors (validate) */
+  for (i = 0; i < NP; i++) {
+    if (codelist[i][0].c1 != codelist[0][0].c1) goto color_mismatch;
+    if (codelist[i][0].c2 != codelist[0][0].c2) goto color_mismatch;
+  }
+  gprint (GP_ERR, "using %d photcodes\n", NP);
+
+  /* output is a named buffer */
+  if ((buf = SelectBuffer (argv[1], ANYVECTOR, TRUE)) == NULL) goto usage;
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, NP, NP, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  out = (float *) buf[0].matrix.buffer;
+  /* we set a default flag value of -1 */
+  for (i = 0; i < NP*NP; i++) {
+    out[i] = -1;
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog[k].filename = skylist[0].filename[k];
+    catalog[k].catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog[k].Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog[k], NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog[k].filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog[k]);
+    // XXX make a subset catalog consisting of only Average and Measure values which meet
+    // the selection criteria
+  }
+  gprint (GP_ERR, "using %d possible regions\n", skylist[0].Nregions);
+
+  /* vectors to save data */
+  Npts = 0;
+  NPTS = 1;
+  if ((xvec = SelectVector ("tmp_x", ANYVECTOR, TRUE)) == NULL) goto escape;
+  if ((yvec = SelectVector ("tmp_y", ANYVECTOR, TRUE)) == NULL) goto escape;
+
+  // set up basic windows
+  if (PLOT) {
+    Nplot = 0;
+    Npx = Npy = 0;
+    NPX = NPY = 6;
+    KapaInitGraph (&graphdata);
+    graphdata.xmin = minColor;
+    graphdata.xmax = maxColor;
+    graphdata.ymin = minDelta;
+    graphdata.ymax = maxDelta;
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+  }
+
+  /*** generate the color-color vectors for the pairs ***/
+  // XXXX this function also needs to check for interrupts
+  // XXX exclude obvious outliers (eg, fabs(dM) > 0.2)
+  /* loop over chip photcode pairs */
+  for (NP1 = 0; NP1 < NP; NP1++) {
+    for (NP2 = NP1 + 1; NP2 < NP; NP2++) {
+      code[0] = codelist[NP1];
+      code[1] = codelist[NP2];
+      
+      /* extract all magnitude pairs from catalog tables */
+      Npts = 0;
+      for (k = 0; k < skylist[0].Nregions; k++) {
+	if (catalog[k].Naverage == 0) continue;
+
+	// gprint (GP_ERR, "seaching %s with %d stars\n", catalog[k].filename, catalog[k].Naverage);
+	// ListPhotSelections ();
+
+	/* get correct mags, convert to X,Y */
+	for (i = 0; i < catalog[k].Naverage; i++) {
+	  if (interrupt) goto escape;
+
+	  M1 = M2 = NULL;
+	  m = catalog[k].average[i].measureOffset;
+
+	  SetSelectionParam (0);
+	  M1 = ExtractDMag (&code[0], &mode[0], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N1);
+	  if (N1 == 0) goto skip_star;
+
+	  SetSelectionParam (2);
+	  M2 = ExtractDMag (&code[2], &mode[2], &catalog[k].average[i], &catalog[k].secfilt[i*Nsecfilt], &catalog[k].measure[m], &N2);
+	  if (N2 == 0) goto skip_star;
+
+	  for (i1 = 0; i1 < N1; i1++) {
+	    for (i2 = 0; i2 < N2; i2++) {
+	      if (M1[i1] < minDelta) continue;
+	      if (M1[i1] > maxDelta) continue;
+	      if (M2[i2] < minColor) continue;
+	      if (M2[i2] > maxColor) continue;
+	      yvec[0].elements[Npts] = M1[i1];
+	      xvec[0].elements[Npts] = M2[i2];
+	      Npts++;
+	      if (Npts >= NPTS) {
+		NPTS += 2000;
+		REALLOCATE (xvec[0].elements, float, NPTS);
+		REALLOCATE (yvec[0].elements, float, NPTS);
+	      }
+	    }
+	  }
+	skip_star:
+	  if (M1 != NULL) free (M1);
+	  if (M2 != NULL) free (M2);
+	}
+	// gprint (GP_ERR, "selected %d stars\n", Npts);
+      }
+
+      if (Npts < NMIN_PTS) continue;
+      xvec[0].Nelements = Npts;
+      yvec[0].Nelements = Npts;
+
+      /* perform robust fit on dmag vs color */
+      cmd = strcreate ("fit tmp_x tmp_y 1 -clip 3 3 -quiet");
+      status = command (cmd, &outcmd, TRUE);
+      if (outcmd != NULL) free (outcmd);
+      
+      C0 = get_double_variable ("C0", &status);
+      C1 = get_double_variable ("C1", &status);
+      
+      /* do something useful with the results (stored in Cn, C0, C1, etc) */
+      gprint (GP_LOG, "%s - %s : ", code[0][0].name, code[1][0].name);
+      gprint (GP_LOG, "%7.4f %7.4f   %7.4f   ", 
+	       C0, C1, get_double_variable ("dC", &status));
+      gprint (GP_LOG, "%5s of %5d\n", get_variable ("Cnv"), Npts);
+      out[NP1 + NP2*NP] = C1;
+
+      // make an illustrating plot of each chip vs each other chip
+      // each page should have, say, a 6x6 grid of plots. each one should show a single chip pair
+      // as the page fills up, it gets written and a new one created.  
+      if (PLOT) {
+	sprintf (name, "s%02d.%02d", Npx, Npy);
+	section.name = strcreate (name);
+	if (Npx || Npy) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0000");
+	} 
+	if (Npx == 0) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY;
+	  section.x = 0.0;
+	  section.y = 0.1 + Npy * section.dy;
+	  strcpy (graphdata.labels, "0100");
+	}
+	if (Npy == 0) {
+	  section.dx = 0.9 / NPX;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.1 + Npx * section.dx;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1000");
+	}
+	if (!Npx && !Npy) {
+	  section.dx = 0.9 / NPX + 0.1;
+	  section.dy = 0.9 / NPY + 0.1;
+	  section.x = 0.0;
+	  section.y = 0.0;
+	  strcpy (graphdata.labels, "1100");
+	}
+	KapaSetSection (kapa, &section);
+	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	KapaPrepPlot (kapa, Npts, &graphdata);
+	KapaPlotVector (kapa, Npts, xvec[0].elements, "x");
+	KapaPlotVector (kapa, Npts, yvec[0].elements, "y");
+
+	for (i = 0; i < 11; i++) {
+	  deltaFit[i] = C0 + C1*colorFit[i];
+	}
+	graphdata.style = 0;
+	graphdata.color = KapaColorByName ("red");
+	KapaPrepPlot (kapa, 11, &graphdata);
+	KapaPlotVector (kapa, 11, colorFit, "x");
+	KapaPlotVector (kapa, 11, deltaFit, "y");
+
+	KapaSetFont (kapa, "helvetica", 8);
+	sprintf (label, "%s", code[0][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.8*maxDelta + 0.2*minDelta, 0.0);
+	sprintf (label, "%s", code[1][0].name);
+	KapaSendTextline (kapa, label, 0.2*maxColor + 0.8*minColor, 0.2*maxDelta + 0.8*minDelta, 0.0);
+	KapaSetFont (kapa, "helvetica", 14);
+
+	graphdata.style = 2;
+	graphdata.color = KapaColorByName ("black");
+
+	free (section.name);
+
+	Npx++;
+	if (Npx == NPX) {
+	  Npx = 0;
+	  Npy ++;
+	  if (Npy == NPY) {
+	    Npy = 0;
+	    sprintf (filename, "%s.%02d.png", plotname, Nplot);
+	    KapaPNG (kapa, filename);
+	    KapaClearSections (kapa);
+	    Nplot++;
+	  }
+	}
+      }	
+    }
+  }
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  signal (SIGINT, oldsignal);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: chipcolors (output) (photcode) (camera)\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "error: chips are missing a color reference\n");
+  goto escape;
+
+color_mismatch:
+  gprint (GP_ERR, "error: all chips must have the same colors\n");
+  goto escape;
+
+escape:
+  if (skylist != NULL) free_catalog (catalog, skylist[0].Nregions);
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (codelist != NULL) free (codelist);
+  if (colorFit != NULL) free (colorFit);
+  if (deltaFit != NULL) free (deltaFit);
+  DeleteVector (xvec);
+  DeleteVector (yvec);
+  signal (SIGINT, oldsignal);
+  return (FALSE);
+}
+
+static void free_catalog (Catalog *catalog, int Ncatalog) {
+
+  int i;
+
+  if (catalog == NULL) return;
+  for (i = 0; i < Ncatalog; i++) {
+    dvo_catalog_free (&catalog[i]);
+  }
+  free (catalog);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitsed.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitsed.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/fitsed.c	(revision 17422)
@@ -0,0 +1,458 @@
+# include "dvoshell.h"
+
+typedef struct {
+  float *mags;
+  float color;
+  float Temp;
+  float Av;
+} SEDtableRow;
+
+typedef struct {
+  float chisq;
+  float Md;
+  int row;
+} SEDfit;
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N);
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color);
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter);
+
+/* this function takes a photcode (and camera name?) and measures the  *
+ * chip-to-chip slopes for all DEP photcodes equiv to the PRI/SEC code */
+
+int fitsed (int argc, char **argv) {
+  
+  int *hashcode;
+  int i, j, k, m, N, done, Nfit;
+  int Nsecfilt, status;
+  void *oldsignal;
+  char name[64], line[1024], key[20];
+  float *fitmags, *fiterrs, *wavecode, *vegaToAB;
+  float color;
+  double X, Y, ZP, RA, DEC;
+  int kapa, PLOT;
+  int Nrow, NROW, idx, Nfilter, start, row;
+  unsigned short colorP, colorM, code, USNOred, USNOblu;
+  int codeP, codeM;
+  FILE *f;
+
+  Graphdata graphdata;
+  KapaSection magSection, resSection;
+
+  Catalog catalog, outcat;
+  SkyList *skylist;
+  SkyRegionSelection *selection;
+
+  SEDtableRow *SEDtableRaw, **SEDtable;
+  SEDtableRow sourceValue, sourceError;
+  SEDfit minFit, testFit;
+
+  /* defaults */
+  skylist  = NULL;
+  selection = NULL;
+
+  catalog.average = NULL;
+  catalog.measure = NULL;
+  catalog.secfilt = NULL;
+
+  outcat.average = NULL;
+  outcat.measure = NULL;
+  outcat.secfilt = NULL;
+
+  SEDtable = NULL;
+  SEDtableRaw = NULL;
+  sourceValue.mags = NULL;
+  sourceError.mags = NULL;
+  wavecode = NULL;
+  hashcode = NULL;
+  magSection.name = NULL;
+  resSection.name = NULL;
+
+  Nrow = 0;
+
+  fiterrs = NULL;
+  fitmags = NULL;
+
+  oldsignal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  /* interpret command-line options */
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+  if (!SetPhotSelections (&argc, argv, 4)) goto usage;
+
+  PLOT = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    remove_argument (N, &argc, argv);
+    PLOT = TRUE;
+  }
+
+  /* interpret command-line options */
+  if (argc != 6) goto usage;
+
+  Nfit = 0;
+  colorP = GetPhotcodeCodebyName (argv[3]);
+  colorM = GetPhotcodeCodebyName (argv[5]);
+  if (!colorP || !colorM) goto color_undefined;
+
+  // artificially set USNOred and blu errors to 0.3
+  USNOred = GetPhotcodeCodebyName ("USNO_RED");
+  USNOblu = GetPhotcodeCodebyName ("USNO_BLUE");
+
+  // load SED table
+  f = fopen (argv[1], "r");
+  if (f == NULL) goto table_missing;
+
+  // XXX add error checks for header data
+  scan_line (f, line);
+  sscanf (line, "%*s %*s %d", &Nfilter);
+
+  // load SED table photcodes, generate the photcode hashtable
+  ALLOCATE (hashcode, int, 0x10000);
+  ALLOCATE (wavecode, float, Nfilter);
+  ALLOCATE (vegaToAB, float, Nfilter);
+
+  for (i = 0; i < 0x10000; i++) hashcode[i] = -1;
+  for (i = 0; i < Nfilter; i++) {
+    scan_line (f, line);
+    sscanf (line, "%*s %s %f %f", name, &wavecode[i], &vegaToAB[i]);
+    code = GetPhotcodeCodebyName (name);
+    if (code == 0) goto code_missing;
+    hashcode[code] = i;
+  }
+  codeP = hashcode[colorP];
+  codeM = hashcode[colorM];
+  if ((codeP == -1) || (codeM == -1)) goto color_missing;
+    
+  // skip remaining header lines
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  scan_line (f, line);
+  
+  // load the SED table data
+  Nrow = 0;
+  NROW = 100;
+  ALLOCATE (SEDtableRaw, SEDtableRow, NROW);
+  while (scan_line(f, line) != EOF) {
+    fparse (&SEDtableRaw[Nrow].Temp, 1, line);
+    fparse (&SEDtableRaw[Nrow].Av, 2, line);
+    ALLOCATE (SEDtableRaw[Nrow].mags, float, Nfilter);
+    for (i = 0; i < Nfilter; i++) {
+      fparse (&SEDtableRaw[Nrow].mags[i], i + 3, line);
+    }
+    SEDtableRaw[Nrow].color = SEDtableRaw[Nrow].mags[codeP] - SEDtableRaw[Nrow].mags[codeM];
+    Nrow ++;
+    CHECK_REALLOCATE (SEDtableRaw, SEDtableRow, NROW, Nrow, 100);
+  }      
+
+  // sort the SEDtable by the reference colors
+  SEDtable = sort_SEDtable (SEDtableRaw, Nrow);
+
+  // create holder for the source data
+  ALLOCATE (sourceValue.mags, float, Nfilter);
+  ALLOCATE (sourceError.mags, float, Nfilter);
+
+  if (PLOT) {
+    if (!GetGraph (&graphdata, &kapa, NULL)) return (FALSE);
+    SetLimitsRaw (wavecode, NULL, Nfilter, &graphdata);
+    graphdata.style = 2;
+    graphdata.ptype = 2;
+    KapaClearSections (kapa);
+    magSection.name = strcreate ("mag");
+    magSection.x  = 0;
+    magSection.dx = 1;
+    magSection.y  = 0.5;
+    magSection.dy = 0.5;
+    resSection.name = strcreate ("res");
+    resSection.x  = 0.0;
+    resSection.dx = 1.0;
+    resSection.y  = 0.0;
+    resSection.dy = 0.5;
+    
+    KapaSetFont (kapa, "helvetica", 14);
+    ALLOCATE (fitmags, float, Nfilter);
+    ALLOCATE (fiterrs, float, Nfilter);
+  }
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  /* loop over regions, extract data for each region */
+  // XXX add interrupt checks
+  gprint (GP_ERR, "using %d possible regions\n", skylist[0].Nregions);
+  for (k = 0; k < skylist[0].Nregions; k++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[k];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    // perform the fit to all sources
+    for (i = 0; i < catalog.Naverage; i++) {
+
+      // blank out the source array
+      for (j = 0; j < Nfilter; j++) {
+	sourceValue.mags[j] = 100;
+      }	
+
+      // load the measurements for this source
+      m = catalog.average[i].measureOffset;
+      for (j = 0; j < catalog.average[i].Nmeasure; j++) {
+	idx = hashcode[catalog.measure[m+j].photcode];
+	if (idx == -1) continue;
+	// XXX do something more clever if more than one value exists per photcode
+	sourceValue.mags[idx] = catalog.measure[m+j].M + vegaToAB[idx];
+	sourceError.mags[idx] = catalog.measure[m+j].dM;
+	if ((catalog.measure[m+j].photcode == USNOred) || (catalog.measure[m+j].photcode == USNOblu)) {
+	  sourceError.mags[idx] = 0.3;
+	}
+      }
+
+      // XXX for the moment, skip sources without ref color
+      if (sourceValue.mags[codeP] > 50) continue;
+      if (sourceValue.mags[codeM] > 50) continue;
+      color = sourceValue.mags[codeP] - sourceValue.mags[codeM];
+
+      // XXX find tableRow within 0.1 mag of color
+      start = SEDcolorBracket (SEDtable, Nrow, color);
+      minFit = SEDchisq (SEDtable[start], &sourceValue, &sourceError, Nfilter);
+      minFit.row = start;
+
+      // search for min chisq backwards
+      done = FALSE;
+      row = start - 1;
+      while (!done && (row > 0)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row --;
+      }
+
+      // search for min chisq forwards
+      done = FALSE;
+      row = start + 1;
+      while (!done && (row < Nrow)) {
+	testFit = SEDchisq (SEDtable[row], &sourceValue, &sourceError, Nfilter);
+	if (testFit.chisq < minFit.chisq) {
+	  minFit = testFit;
+	  minFit.row = row;
+	}
+	if (fabs(SEDtable[row][0].color - color) > 0.5) done = TRUE;
+	row ++;
+      }
+
+      Nfit ++;
+      // create the vectors for the example plots
+      if (PLOT) {
+	// find plot range
+	SetLimitsRaw (NULL, SEDtable[minFit.row][0].mags, Nfilter, &graphdata);
+	SWAP (graphdata.ymin, graphdata.ymax);
+
+	KapaClearSections (kapa);
+	KapaSetSection (kapa, &magSection);
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("blue");
+	graphdata.etype = 0;
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, SEDtable[minFit.row][0].mags, "y");
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md;
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	sprintf (line, "star: %10.6f %10.6f  T: %5.0fK  A_V|: %4.2f  M_D|: %5.2f  &sc&h^2|: %5.2f", 
+		 catalog.average[i].R, catalog.average[i].D, 
+		 SEDtable[minFit.row][0].Temp, SEDtable[minFit.row][0].Av, minFit.Md, minFit.chisq);
+	KapaSendLabel (kapa, line, 2);
+	KapaSendLabel (kapa, "model,fit (mags)", 1);
+
+	KapaSetSection (kapa, &resSection);
+	graphdata.ymin = -1.0;
+	graphdata.ymax = +1.0;
+    	KapaSetLimits (kapa, &graphdata);
+	KapaBox (kapa, &graphdata);
+	graphdata.color = KapaColorByName ("red");
+	graphdata.etype = 1;
+
+	for (j = 0; j < Nfilter; j++) {
+	  fitmags[j] = 100;
+	  fiterrs[j] = 0;
+	  if (sourceValue.mags[j] > 50) continue;
+	  fitmags[j] = sourceValue.mags[j] - minFit.Md - SEDtable[minFit.row][0].mags[j];
+	  fiterrs[j] = sourceError.mags[j];
+	}
+	KapaPrepPlot (kapa, Nfilter, &graphdata);
+	KapaPlotVector (kapa, Nfilter, wavecode, "x");
+	KapaPlotVector (kapa, Nfilter, fitmags, "y");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dym");
+	KapaPlotVector (kapa, Nfilter, fiterrs, "dyp");
+	KapaSendLabel (kapa, "wavelength (nm)", 0);
+	KapaSendLabel (kapa, "resid (mags)", 1);
+
+	KiiCursorOn (kapa);
+	while (KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key)) {
+	  gprint (GP_ERR, "window: %f %f (%s)\n", X, Y, key);
+	  if (!strcasecmp (key, "Q")) {
+	    KiiCursorOff (kapa);
+	    break;
+	  }
+	  if (!strcasecmp (key, "ESCAPE")) {
+	    KiiCursorOff (kapa);
+	    goto escape;
+	  }
+	}
+      }
+      // we now have the min chisq row. use this to supply the other filter values....
+    }
+    dvo_catalog_free (&catalog);
+  }
+  gprint (GP_ERR, "fitted %d stars\n", Nfit);
+  status = TRUE;
+  goto finish;
+  
+usage:
+  gprint (GP_ERR, "USAGE: fitset (sedtable) : (F) - (F)\n");
+  goto escape;
+
+table_missing:
+  gprint (GP_ERR, "ERROR: can't open the SED table\n");
+  goto escape;
+
+color_missing:
+  gprint (GP_ERR, "ERROR: reference color not in SED table\n");
+  goto escape;
+
+color_undefined:
+  gprint (GP_ERR, "ERROR: undefined photcode in reference color\n");
+  goto escape;
+
+code_missing:
+  gprint (GP_ERR, "ERROR: undefined photcode in SED table\n");
+  goto escape;
+
+escape:
+  status = FALSE;
+  goto finish;
+
+finish:
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  if (wavecode != NULL) free (wavecode);
+  if (hashcode != NULL) free (hashcode);
+  if (SEDtableRaw != NULL) {
+    for (i = 0; i < Nrow; i++) {
+      free (SEDtableRaw[i].mags);
+    }
+    free (SEDtableRaw);
+  }
+  if (SEDtable != NULL) free (SEDtable);
+  if (sourceValue.mags != NULL) free (sourceValue.mags);
+  if (sourceError.mags != NULL) free (sourceError.mags);
+
+  signal (SIGINT, oldsignal);
+  return (status);
+}
+
+// fit the data (with errors) to the given table row
+SEDfit SEDchisq (SEDtableRow *ref, SEDtableRow *data, SEDtableRow *error, int Nfilter) {
+
+  int i;
+  double Sm, Sd, S2, wt, dM;
+  SEDfit fit;
+
+  Sm = Sd = S2 = 0.0;
+
+  for (i = 0; i < Nfilter; i++) {
+    if (data[0].mags[i] > 50.0) continue;
+
+    if (error[0].mags[i] == 0.0) {
+      wt = 1.0;
+    } else {
+      wt = 1.0 / SQ(error[0].mags[i]);
+    }
+
+    dM = data[0].mags[i] - ref[0].mags[i];
+    S2 += SQ(dM) * wt;
+    Sm += dM * wt;
+    Sd += wt;
+  }
+    
+  // row is assigned after fit
+  fit.row = -1;
+  fit.Md = Sm / Sd;
+  fit.chisq = S2 + SQ(fit.Md) * Sd - 2*fit.Md*Sm;
+
+  return (fit);
+}
+
+// find the first table row within 0.1 mag of the requested color (or within 10)
+int SEDcolorBracket (SEDtableRow **table, int Ntable, float color) {
+
+  int Nlo, Nhi, N;
+  float tcolor;
+
+  N = Nlo = 0; Nhi = Ntable;
+  tcolor = table[Nlo][0].color;
+  while ((Nhi - Nlo > 10) && (fabs(tcolor-color) > 0.1)) {
+    N = 0.5*(Nlo + Nhi);
+    N = MAX (N, 0);
+    N = MIN (N, Ntable - 1);
+    tcolor = table[N][0].color;
+    if (tcolor < color) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  return (N);
+}
+
+SEDtableRow **sort_SEDtable (SEDtableRow *raw, int N) {
+
+  int i;
+  SEDtableRow **value;
+  
+  if (N <= 0) return (NULL);
+
+  ALLOCATE (value, SEDtableRow *, N);
+  for (i = 0; i < N; i++) {
+    value[i] = &raw[i];
+  }
+
+# define SWAPFUNC(A,B){ SEDtableRow *temp = value[A]; value[A] = value[B]; value[B] = temp; }
+# define COMPARE(A,B)(value[A][0].color < value[B][0].color)
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  return (value);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gcat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gcat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gcat.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "dvoshell.h"
+
+int gcat (int argc, char **argv) {
+  
+  int i;
+  struct stat filestat;
+  double Ra, Dec, Radius;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if ((argc != 3) && (argc != 4)) {
+    gprint (GP_ERR, "USAGE: gcat RA DEC [Radius]\n");
+    return (FALSE);
+  }
+
+  /* load sky from correct table */
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  if (argc == 4) 
+    Radius = atof(argv[3]);
+  else 
+    Radius = 0.0001;
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    if (stat (skylist[0].filename[i], &filestat) != -1) {
+      gprint (GP_ERR, "%3d %s *\n", i, skylist[0].regions[i][0].name);
+    } else {
+      gprint (GP_ERR, "%3d %s\n", i, skylist[0].regions[i][0].name);
+    } 
+    set_str_variable ("CATNAME", skylist[0].filename[i]);
+  }
+
+  return (TRUE);
+  SkyListFree (skylist);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/get_regions.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/get_regions.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/get_regions.c	(revision 17422)
@@ -0,0 +1,50 @@
+# include "dvoshell.h"
+
+/* return region files containing given image */
+GSCRegion *get_regions (Image *image, int *Nregions) {
+  
+  GSCRegion *region;
+  FILE *f;
+  double x, y;
+  double dec, ra;
+  int j, done, nregion, NREGION;
+  char filename[256], path[256];
+  
+  VarConfig ("CATDIR", "%s", path);
+  VarConfig ("GSCFILE", "%s", filename);
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find GSC region file %s\n", filename);
+    exit (0);
+  }
+  
+  /* find regions at image corners */
+  NREGION = 10;
+  ALLOCATE (region, GSCRegion, NREGION);
+  nregion = 0;
+
+  /* look for new regions on grid across image */ 
+  for (x = 0.0; x <= 1.0; x+=0.25) {
+    for (y = 0.0; y <= 1.0; y+=0.25) {
+      XY_to_RD (&ra, &dec, image[0].NX*(1.1*x - 0.05), image[0].NY*(1.1*y - 0.05), &image[0].coords);
+      aregion (&region[nregion], f, ra, dec, path);
+      done = FALSE;
+      for (j = 0; (j < nregion) && !done; j++) {
+	if (!strcmp (region[nregion].filename, region[j].filename)) {
+	  nregion --;
+	  done = TRUE;
+	}
+      }
+      nregion ++;
+      if (nregion == NREGION) {
+	NREGION += 10;
+	REALLOCATE (region, GSCRegion, NREGION);
+      }
+    }
+  }
+  *Nregions = nregion;
+  
+  fclose (f);
+  return (region);
+  
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/getxtra.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/getxtra.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/getxtra.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+int getxtra (int argc, char **argv) {
+  
+  SelectXtra = (char *) NULL;
+  if (N = get_argument (argc, argv, "-xtra")) {
+    SelectXtra = TRUE;
+    remove_argument (N, &argc, argv);
+    XtraType = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+    XtraValue = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  SelectRegion = (char *) NULL;
+  if (N = get_argument (argc, argv, "-region")) {
+    SelectRegion = TRUE;
+    remove_argument (N, &argc, argv);
+    RA = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Dec = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+    Radius = atof (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Mode = (char *) NULL;
+  if (N = get_argument (argc, argv, "-mode")) {
+    remove_argument (N, &argc, argv);
+    Mode = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Range = (char *) NULL;
+  if (N = get_argument (argc, argv, "-range")) {
+    remove_argument (N, &argc, argv);
+    Range = strcreate (atof[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: addxtra R D radius (type) (value)\n");
+    return (FALSE);
+  }
+  
+  
+
+  /*
+
+  validate the input values (type, defines the needed options)
+  find catalog (based on r,d)
+  load catalog (need to load measures?)
+  find the object
+  find the xtra entry (sorted by ra/dec? sorted by averef?)
+  add new entry
+  save catalog 
+  */
+}
+
+/* 
+   get specified values for specified objects 
+
+   getxtra type -xtra name K01.01 
+   getxtra type -region R D radius 
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gimages.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gimages.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gimages.c	(revision 17422)
@@ -0,0 +1,184 @@
+# include "dvoshell.h"
+
+// XXX this needs some help: it should define the region for image_subset or otherwise
+// limit the input selection
+int gimages (int argc, char **argv) {
+  
+  int i, j, N, Nimage, Nfound, *subset, Nsubset, status;
+  double ra, dec, Ra, Dec, X, Y, Yo;
+  double trange, t;
+  int TimeSelect, PixelCoords, TimeFormat, PhotCodeSelect;
+  time_t tzero, TimeReference;
+  char name[64], *date;
+  int typehash;
+  SkyRegionSelection *selection;
+
+  PhotCode *code;
+  Image *image;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-tref"))) {
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+
+    t = atof (argv[N]);
+    tzero = TimeRef (t, TimeReference, TimeFormat);
+    remove_argument (N, &argc, argv);
+
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PixelCoords = FALSE;
+  if ((N = get_argument (argc, argv, "-pix"))) {
+    remove_argument (N, &argc, argv);
+    PixelCoords = TRUE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: gimages RA DEC [-time t dt] [-pix]\n");
+    return (FALSE);
+  }
+
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  int DistortImage = wordhash ("-DIS");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+
+  Nfound = 0;
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (PhotCodeSelect) {
+      if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	if (code[0].code != image[i].photcode) continue;
+      } 
+      if (code[0].type == PHOT_SEC) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      } 
+    }      
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!finite(X)) continue;
+    if (!finite(Y)) continue;
+    if (!status) continue;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    if (typehash == DistortImage) {
+      if (X < -0.5*image[i].NX) continue;
+      if (Y < -0.5*image[i].NY) continue;
+      if (X > +0.5*image[i].NX) continue;
+      if (Y > +0.5*image[i].NY) continue;
+      goto got_spot;
+    } 
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      if (Y < -0.5*image[i].NY) continue;
+      Yo = +0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      Yo = +0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y > Yo) continue;
+      goto got_spot;
+    }
+    if (typehash == TriangleDn) {
+      if (Y > +0.5*image[i].NY) continue;
+      Yo = -0.5*image[i].NY + 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      Yo = -0.5*image[i].NY - 2.0*(image[i].NY/image[i].NX)*X;
+      if (Y < Yo) continue;
+      goto got_spot;
+    }
+
+    {
+      if (X < 0) continue;
+      if (Y < 0) continue;
+      if (X > image[i].NX) continue;
+      if (Y > image[i].NY) continue;
+    }
+
+    /*** XXX we need to re-introduce the use of applyMcal
+    Mcal = applyMcal (&image[i], 2048.0, 2048.0);
+    ***/
+
+  got_spot:
+    date = ohana_sec_to_date (image[i].tzero);
+
+    if (PixelCoords) {
+      gprint (GP_LOG, "%3d %s %6.1f %6.1f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x\n", 
+	       Nfound, image[i].name, X, Y, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].code);
+    } else {
+      XY_to_RD (&ra, &dec, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+      gprint (GP_LOG, "%3d %s %8.4f %8.4f %20s %5d %2d %4.2f %6.3f %5.3f %5.3f %4x\n", 
+	       Nfound, image[i].name, ra, dec, date, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal, image[i].exptime, image[i].code);
+    }
+    sprintf (name, "IMAGEx:%d", Nfound);
+    set_variable     (name, X);
+    sprintf (name, "IMAGEy:%d", Nfound);
+    set_variable     (name, Y);
+    sprintf (name, "IMAGEt:%d", Nfound);
+    set_str_variable (name, date);
+    sprintf (name, "IMAGEccd:%d", Nfound);
+    set_int_variable (name, image[i].ccdnum);
+    sprintf (name, "IMAGEname:%d", Nfound);
+    set_str_variable (name, image[i].name);
+    Nfound ++;
+    free (date);
+  }
+  set_int_variable ("IMAGEx:n", Nfound);
+  set_int_variable ("IMAGEy:n", Nfound);
+  set_int_variable ("IMAGEt:n", Nfound);
+  set_int_variable ("IMAGEccd:n", Nfound);
+  set_int_variable ("IMAGEname:n", Nfound);
+
+  free (image);
+  free (subset);
+
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gstar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gstar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/gstar.c	(revision 17422)
@@ -0,0 +1,356 @@
+# include "dvoshell.h"
+
+void initPhotcodeSequence (int Nsecfilt);
+void freePhotcodeSequence ();
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type);
+
+int gstar (int argc, char **argv) {
+  
+  char *date;
+  double Ra, Dec, Radius, Radius2, r, dec0, dec1;
+  double Mcat, Mrel;
+  double *RA, *DEC;
+  int i, j, k, m, N, *N1, Nsecfilt, NPTS, QUIET, FULL_OUTPUT;
+  int Nstars, found, GetMeasures, Nlo, Nhi;
+  int SaveVectors;
+  Vector *vec1, *vec2, *vec3, *vec4;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  int TimeFormat;
+  time_t TimeReference;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  NPTS = 0;
+  vec1 = vec2 = vec3 = vec4 = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((vec1 = SelectVector ("gs:m", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec2 = SelectVector ("gs:t", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec3 = SelectVector ("gs:z", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    if ((vec4 = SelectVector ("gs:f", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  }
+
+  FULL_OUTPUT = FALSE;
+  if ((N = get_argument (argc, argv, "-full"))) {
+    FULL_OUTPUT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  GetMeasures = FALSE;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-meas"))) {
+    GetMeasures = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: gstar RA DEC Radius [-m]\n");
+    return (FALSE);
+  }
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  while (Ra < 0.0) Ra += 360.0;
+  while (Ra > 360.0) Ra -= 360.0;
+  
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  /* bracket the RA range of interest */
+  dec0 = Dec - Radius;
+  dec1 = Dec + Radius;
+
+  Nlo = 0; Nhi = catalog.Naverage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (DEC[N] < dec0) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  /* DEC[Nlo] is guaranteed to be just lower than dec0 */
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+
+  /* data has been loaded, get ready to plot it */
+  if (SaveVectors) {
+    N = 0;
+    NPTS = 1000;
+    ALLOCATE (vec1[0].elements, float, NPTS);
+    ALLOCATE (vec2[0].elements, float, NPTS);
+    ALLOCATE (vec3[0].elements, float, NPTS);
+    ALLOCATE (vec4[0].elements, float, NPTS);
+  }
+
+  initPhotcodeSequence (Nsecfilt);
+
+  for (i = Nlo; (i < catalog.Naverage) && !found; i++) {
+
+    if (dec0 > DEC[i]) continue;
+    if (dec1 < DEC[i]) found = TRUE;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      k = N1[i];
+      if (!QUIET) {
+	gprint (GP_LOG, "star: %d\n", k);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].R);
+	gprint (GP_LOG, "%11.7f ", catalog.average[k].D);
+	gprint (GP_LOG, "%3d   ",  catalog.average[k].Nmeasure);
+	gprint (GP_LOG, "%4.1f ",  0.01*catalog.average[k].Xp);
+	gprint (GP_LOG, "%5d",     catalog.average[k].code);
+
+	if (FULL_OUTPUT) {
+	    gprint (GP_LOG, "%f",     catalog.average[k].dR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].dD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].uR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].uD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].duR);
+	    gprint (GP_LOG, "%f",     catalog.average[k].duD);
+	    gprint (GP_LOG, "%f",     catalog.average[k].P);
+	    gprint (GP_LOG, "%f",     catalog.average[k].dP);
+	    gprint (GP_LOG, "%x",     catalog.average[k].objID);
+	    gprint (GP_LOG, "%x",     catalog.average[k].catID);
+	}
+
+	gprint (GP_LOG, "\n");
+      
+	/* filter names */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 3);
+	gprint (GP_LOG, "\n");
+
+	/* average mags */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 0);
+	gprint (GP_LOG, "\n");
+
+	/* average mag errors */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 1);
+	gprint (GP_LOG, "\n");
+
+	/* average mag chisq */
+	for (j = 0; j < Nsecfilt; j++) printPhotcodeSequence (&catalog.average[k], &catalog.secfilt[Nsecfilt*k], j, 2);
+	gprint (GP_LOG, "\n");
+      }
+
+      if (GetMeasures || SaveVectors) {
+	m = catalog.average[k].measureOffset;
+	for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	  Mcat = PhotCat (&catalog.measure[m]);
+	  Mrel = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+
+	  if (GetMeasures && !QUIET) {
+	    date = ohana_sec_to_date (catalog.measure[m].t);
+	    gprint (GP_LOG, "%6.3f ",  Mcat);
+	    gprint (GP_LOG, "%6.3f ",  Mrel);
+	    gprint (GP_LOG, "%5.3f  ", catalog.measure[m].dM);
+	    gprint (GP_LOG, "%20s  ",  date);
+	    gprint (GP_LOG, "%7.4f ",  catalog.measure[m].dR);
+	    gprint (GP_LOG, "%7.4f",   catalog.measure[m].dD);
+	    gprint (GP_LOG, "%2d ",    catalog.measure[m].dophot);
+	    gprint (GP_LOG, "%3x ",    catalog.measure[m].dbFlags);
+	    gprint (GP_LOG, "%3d ",    catalog.measure[m].photcode);
+	    gprint (GP_LOG, "%-20s  ", GetPhotcodeNamebyCode (catalog.measure[m].photcode));
+
+	    if (FULL_OUTPUT) {
+		gprint (GP_LOG, "%f", catalog.measure[m].Mcal);
+		gprint (GP_LOG, "%f", catalog.measure[m].Map);
+		gprint (GP_LOG, "%f", pow(10.0, 0.4*catalog.measure[m].dt));
+		gprint (GP_LOG, "%f", 1.0 + catalog.measure[m].airmass);
+		gprint (GP_LOG, "%f", catalog.measure[m].az);
+		gprint (GP_LOG, "%f", catalog.measure[m].Xccd);
+		gprint (GP_LOG, "%f", catalog.measure[m].Yccd);
+		gprint (GP_LOG, "%d", catalog.measure[m].dXccd);
+		gprint (GP_LOG, "%d", catalog.measure[m].dYccd);
+		gprint (GP_LOG, "%f", catalog.measure[m].Sky);
+		gprint (GP_LOG, "%f", catalog.measure[m].dSky);
+		gprint (GP_LOG, "%d", catalog.measure[m].averef);
+		gprint (GP_LOG, "%d", catalog.measure[m].detID);
+		gprint (GP_LOG, "%d", catalog.measure[m].imageID);
+		gprint (GP_LOG, "%f", catalog.measure[m].qPSF);
+		gprint (GP_LOG, "%f", catalog.measure[m].psfChisq);
+		gprint (GP_LOG, "%f", catalog.measure[m].crNsigma);
+		gprint (GP_LOG, "%f", catalog.measure[m].extNsigma);
+		gprint (GP_LOG, "%f", 0.01*catalog.measure[m].FWx);
+		gprint (GP_LOG, "%f", 0.01*catalog.measure[m].FWy);
+		gprint (GP_LOG, "%f", (360.0/(float)0xffff)*catalog.measure[m].theta);
+
+		gprint (GP_LOG, "%x", catalog.measure[m].photFlags);
+		gprint (GP_LOG, "%d", catalog.measure[m].stargal);
+	    }
+	    gprint (GP_LOG, "\n");
+
+	    free (date);
+	  }
+	  
+	  if (SaveVectors) {
+	    vec1[0].elements[N] = Mcat;
+	    vec2[0].elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	    vec3[0].elements[N] = catalog.measure[m].airmass;
+	    vec4[0].elements[N] = catalog.measure[m].photcode;
+	    N ++;
+	    if (N == NPTS - 1) {
+	      NPTS += 2000;
+	      REALLOCATE (vec1[0].elements, float, NPTS);
+	      REALLOCATE (vec2[0].elements, float, NPTS);
+	      REALLOCATE (vec3[0].elements, float, NPTS);
+	      REALLOCATE (vec4[0].elements, float, NPTS);
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (SaveVectors) {
+    vec1[0].Nelements = N;
+    vec2[0].Nelements = N;
+    vec3[0].Nelements = N;
+    vec4[0].Nelements = N;
+  }
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+
+  freePhotcodeSequence ();
+  return (TRUE);
+
+}
+
+void print_double (double value) {
+  if (isnan(value)) 
+    gprint (GP_LOG, "NaN    ");
+  else 
+    gprint (GP_LOG, "%6.3f ", value);
+}
+
+void print_short (double value, short int ival) {
+  if (ival == NAN_S_SHORT) 
+    gprint (GP_LOG, "NaN    ");
+  else 
+    gprint (GP_LOG, "%6.3f ", value);
+}
+
+// XXX fix printing to be in photcode numerical order for PRI/SEC data
+static int *sequence = NULL;
+
+void initPhotcodeSequence (int Nsecfilt) {
+
+  int j;
+  int *codeNumber;
+  PhotCode *code;
+
+  // sequence contains, in desired order, secfilt number (0 == pri)
+  ALLOCATE (sequence, int, Nsecfilt);
+  ALLOCATE (codeNumber, int, Nsecfilt);
+  
+  /* filter names -- primary code is 0 in this function */
+  for (j = 0; j < Nsecfilt; j++) {
+    code = GetPhotcodebyNsec (j);
+    codeNumber[j] = code[0].code;
+    sequence[j] = j;
+  }
+
+  isortpair (codeNumber, sequence, Nsecfilt);
+  free (codeNumber);
+}
+
+void freePhotcodeSequence () {
+  free (sequence);
+}
+
+
+void printPhotcodeSequence (Average *average, SecFilt *secfilt, int entry, int type) {
+
+  int seq;
+  PhotCode *code;
+
+  seq = sequence[entry];
+
+  switch (type) {
+    case 0: /* average mags */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].M);
+      }
+      break;
+
+    case 1: /* average mags errors */
+      if (seq == -1) {
+	print_double (NAN);
+      } else {
+	print_double (secfilt[seq].dM);
+      }
+      break;
+
+    case 2: /* average mag chisq */
+      if (seq == -1) {
+	print_short (NAN_S_SHORT, NAN_S_SHORT);
+      } else {
+	print_short (pow (10.0, 0.01*secfilt[seq].Xm), secfilt[seq].Xm);
+      }
+      break;
+
+    case 3: /* filter names */
+      code = GetPhotcodebyNsec (seq);
+      gprint (GP_LOG, "%-6s ", code[0].name);
+      break;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/Photometry
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/Photometry	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/Photometry	(revision 17422)
@@ -0,0 +1,26 @@
+
+cals: Plot residuals for reference stars. 
+
+ This function takes the currently displayed region and looks for all
+ stars which have 'reference' photometry and plots the difference
+ between the reference magnitude (in the given filter) and the
+ observed magnitude (same filter) versus one of several options:  the
+ star color (based on the 'reference' photometry), the star magnitude,
+ the airmass, time, secqunce, etc.
+
+ These plots can use one of two options for the 'observed' magnitude.
+ Either, the magnitude for individual measurement, using the currently
+ set values for the CCD zeropoints (from the photometry database), or
+ using the relative photometry determined for the particular star.  
+
+
+dmags: plot difference between two measured magnitudes
+
+resid: 
+
+abszero
+
+zeropts
+
+cmd
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/catalog
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/catalog	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/catalog	(revision 17422)
@@ -0,0 +1,74 @@
+
+  catalog (filename/-all) [many options!]
+
+  plot the star positions from any of several possible catalogs.  By
+default, the photometry database (see Database) is used, but the HST
+Guide Star Catalog and a simple ASCII file may also be used.  The
+plotting is done on window 0, with the current point type.  The point
+size can be scaled by the magnitude of the star or by the number
+of observations (-n).  
+
+There are many options to this function.  Here they are all:
+
+-e epoch: given ASCII catalog has this epoch, plot in J2000
+
+-ID ID: plot only measurements with this ID number
+ ID may be one of:
+	0 - star
+	4 - variable
+	5 - transient
+      100 - ghost image
+      101 - trail (satellite)
+      102 - bright star garbage	
+      note that numbers < 50 are static objects, 
+	and numbers > 100 are bad data.
+
+-all:  plot all data for the current display region
+
+-g: use the HST GSC instead of the photometry database
+
+-a RA DEC MAG:  the given file is an ASCII file with 
+	RA, DEC, and MAG in columns given by the three numbers
+
+-v ra dec scale: save the values in vectors with the three names given
+	the ra and dec are always used, but the third is the size of 
+	the points plotted, based on whatever the scaling is.
+
+-m m1 m2: scale points by magnitude, with smallest points having
+	magnitude m2.
+
++n n1 n2: scale points by number of measurements, with smallest points
+	having number n1.
+
+-n n1 n2: scale points by number of missing values, with smallest points
+	having number n1.
+
+Note that if two of -m, -n, +n are given, the first determines the
+ scaling of the points.  the second provides an allowed range, so
+ points are only plotted if the second parameter has a value in the
+ given range.
+
+Examples: 
+
+  catalog -all -m 12 18  
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest.
+   
+  catalog -all -m 12 18 +n 2 4
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 2-4 measurements each
+   
+  catalog -all -m 12 18 +n 0 1 -ID 101
+    plot all stars in current region with magnitude 12 having the
+    biggest points and magnitude 18 having the smallest, but only
+    if the stars have 1 measurements each, and if the object is 
+    found to be a trail.
+   
+  catalog n2230/1819.cpt -n 2 8  
+    plot all stars in catalog n2230/1819.cpt with the biggest points
+    having 8 measurements and the smallest having only 2.
+
+  See Also: lcat, pcat, style, region
+
+   
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/images
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/images	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/images	(revision 17422)
@@ -0,0 +1,9 @@
+
+  images 
+
+  plot the outlines of images in the photometry database and list
+  along with relevant information.  An example of relevant data is the
+  image time, which can be used in other functions to define a specific
+  image or a range of images relevant for the function.
+
+  See also: Database, lcat, pcat
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/imstats
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/imstats	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/imstats	(revision 17422)
@@ -0,0 +1,5 @@
+
+  imstats 
+
+  list interesting statistics for a set of images.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcat	(revision 17422)
@@ -0,0 +1,10 @@
+
+  lcat [-all]
+
+  list photometry database files in the current region.  if the option
+  -all is given, all database in the region are listed, along with a
+  comment to show if there is data in the file or not.  if the option
+  is not given, only those files with data are listed.
+
+  See also: region, pcat
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcurve
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcurve	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/lcurve	(revision 17422)
@@ -0,0 +1,8 @@
+
+  lcurve RA DEC Radius [-l]
+
+  plot light curves for all stars within Radius of the given
+  coordinates.  The units for all three are decimal degrees.  
+  The option -l autoscales the limits of the plot.  
+
+  See also: limits
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/mextract
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/mextract	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/mextract	(revision 17422)
@@ -0,0 +1,12 @@
+
+ mextract (filename) (value) [-g / -a Ncol]
+
+ extracts a vector from the photometry database.  The name also
+ specifies the type of data extracted (and is case insensitive).  Only
+ certain names may be used.
+
+ The possible values are:
+ ra, dec, mag, dmag, Mcal, Mrel, source, time, dR, dD
+
+
+ Note: this routine extracts the individual measurements.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/pcat
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/pcat	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/pcat	(revision 17422)
@@ -0,0 +1,9 @@
+
+  pcat [-all]
+
+  plot outlines of photometry database files in the current region.
+  if the option -all is given, all database in the region are listed.
+  if the option is not given, only those files with data are listed.
+
+  See also: region, lcat
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/resid
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/resid	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/resid	(revision 17422)
@@ -0,0 +1,12 @@
+
+  resid [-im time] [-X] [-l] (photometry file)
+
+  plot the photometry residuals for a photometry database file. 
+  For every star in the database, the average magnitude is plotted 
+  vs the difference between the average magnitude and each existing 
+  measurement for that star.  
+
+  -l		automatically set the plot limits.
+  -X		plot residual divided by photometric error
+  -im time	only plot the residuals for one of the images 
+		in the database, specified by the obseravation time.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/simage
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/simage	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/help/simage	(revision 17422)
@@ -0,0 +1,7 @@
+
+  simage (filename)
+
+  plot the positions of stars in a CMP file.  This function is a
+  useful test for image comparisons, and astrometry must be determined
+  before the function is run.
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/images.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/images.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/images.c	(revision 17422)
@@ -0,0 +1,301 @@
+# include "dvoshell.h"
+# define BETA 0.41421
+
+int wordhash (char *word) {
+  int value;
+
+  value = *(int *)word;
+  return value;
+}
+
+int images (int argc, char **argv) {
+
+  int i, j, Nimage, status, InPic, leftside, *plist, TimeSelect, ByName;
+  int WITH_MOSAIC, SOLO_MOSAIC, HIDDEN;
+  time_t tzero, tend;
+  int N, NPTS, n, npts, Npts, kapa;
+  Vector Xvec, Yvec;
+  double r[8], d[8], x[8], y[8], Rmin, Rmax, Rmid, trange, Radius;
+  Image *image;
+  Graphdata graphmode;
+  char name[256];
+  int typehash;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) goto usage;
+
+  WITH_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "+mosaic"))) {
+    remove_argument (N, &argc, argv);
+    WITH_MOSAIC = TRUE;
+  }
+
+  SOLO_MOSAIC = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    SOLO_MOSAIC = TRUE;
+    WITH_MOSAIC = TRUE;
+  }
+
+  HIDDEN = FALSE;
+  if ((N = get_argument (argc, argv, "-hidden"))) {
+    remove_argument (N, &argc, argv);
+    HIDDEN = TRUE;
+  }
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  Radius = 45;
+  if ((N = get_argument (argc, argv, "-radius"))) {
+    remove_argument (N, &argc, argv);
+    Radius = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      goto usage;
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 1) goto usage;
+  
+  /* it is not an error for the database not to have any images */
+  if ((image = LoadImages (&Nimage)) == NULL) return (TRUE);
+  BuildChipMatch (image, Nimage);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  int DistortImage = wordhash ("-DIS");
+  int TriangleUp   = wordhash ("TRP-");
+  int TriangleDn   = wordhash ("TRM-");
+  int TrianglePts  = wordhash ("TRI-");
+
+  npts = NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  ALLOCATE (plist, int, NPTS);
+  n = N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strncmp (image[i].name, name, strlen(name))) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    Npts = 4;
+    status = TRUE;
+    leftside = FALSE;
+
+    typehash = wordhash (&image[i].coords.ctype[4]);
+
+    /* DIS images represent a field, not a chip */
+    if ((typehash == DistortImage) && !WITH_MOSAIC) continue;
+    if ((typehash != DistortImage) &&  SOLO_MOSAIC) continue;
+    if (typehash == DistortImage) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = +0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      x[3] = -0.5*image[i].NX; y[3] = +0.5*image[i].NY;
+      goto got_type;
+    }
+
+    typehash = wordhash (image[i].coords.ctype);
+    if (typehash == TriangleUp) {
+      Npts = 3;
+      x[0] =                0; y[0] = +0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = -0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = -0.5*image[i].NY;
+      goto got_type;
+    }
+    if (typehash == TriangleDn) {
+      Npts = 3;
+      x[0] =                0; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+      x[2] = -0.5*image[i].NX; y[2] = +0.5*image[i].NY;
+      goto got_type;
+    }
+    // For 'TrianglePts' (TRI-), we are using the Mx,My, etc terms to save the coordinates
+    // this means triangular images cannot carry photometric zero-point variations
+    if (typehash == TrianglePts) {
+      Npts = 3;
+      x[0] = image[i].Mx;   y[0] = image[i].My;
+      x[1] = image[i].Mxxx; y[1] = image[i].Mxyy;
+      x[2] = image[i].Mxxy; y[2] = image[i].Myyy;
+      goto got_type;
+    }
+
+    // default layout
+    {
+      x[0] = 0;           y[0] = 0;
+      x[1] = image[i].NX; y[1] = 0;
+      x[2] = image[i].NX; y[2] = image[i].NY;
+      x[3] = 0;           y[3] = image[i].NY;
+    }
+
+  got_type:
+
+    /* project this image to screen display coords */
+    // check for boundary overlap?
+    for (j = 0; j < Npts; j++) {
+      status = XY_to_RD (&r[j], &d[j], x[j], y[j], &image[i].coords);
+      if (!status) break;
+      while (r[j] < Rmin) { r[j] += 360.0; }
+      while (r[j] > Rmax) { r[j] -= 360.0; }
+      if (j == 0) {
+	leftside = (r[j] < Rmid);
+      } 
+      if (j > 0) { 
+	if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+      }
+    }
+
+    // extremely large-scale images with certain projections will have odd boundaries.
+    // eg, the ASCA images are essentially circles of radius ~60 degrees.  plot these as 
+    // octagons with some dummy size.
+    if (!status) {
+      int jp, xo, yo;
+      double rc, dc;
+      xo = 0.5*image[i].NX;
+      yo = 0.5*image[i].NY;
+      // is the image center on the screen?
+      status = XY_to_RD (&rc, &dc, 0.5*x[2], 0.5*y[2], &image[i].coords);
+      if (status && (image[i].NX * image[i].coords.cdelt2 > 90)) {
+	// draw an octagon with radius 45 degrees
+	double dX = Radius / image[i].coords.cdelt2;
+	x[0] = xo+dX;      y[0] = yo+BETA*dX;
+	x[1] = xo+BETA*dX; y[1] = yo+dX;
+	x[2] = xo-BETA*dX; y[2] = yo+dX;
+	x[3] = xo-dX;      y[3] = yo+BETA*dX;
+	x[4] = xo-dX;      y[4] = yo-BETA*dX;
+	x[5] = xo-BETA*dX; y[5] = yo-dX;
+	x[6] = xo+BETA*dX; y[6] = yo-dX;
+	x[7] = xo+dX;      y[7] = yo-BETA*dX;
+	Npts = 8;
+	j = 0;
+	for (jp = 0; jp < 8; jp++) {
+	  status = XY_to_RD (&r[j], &d[j], x[jp], y[jp], &image[i].coords);
+	  if (!status) continue;
+	  while (r[j] < Rmin) { r[j] += 360.0; }
+	  while (r[j] > Rmax) { r[j] -= 360.0; }
+	  if (j == 0) {
+	    leftside = (r[j] < Rmid);
+	  } 
+	  if (j > 0) { 
+	    if ( leftside && (r[j] > Rmid + 90)) { r[j] -= 360.0; }
+	    if (!leftside && (r[j] < Rmid - 90)) { r[j] += 360.0; }
+	  }
+	  j++;
+	}
+	Npts = j;
+      } else {
+	continue;
+      }
+    }
+    if (Npts == 0) continue;
+
+    status = FALSE;
+    for (j = 0; j < Npts; j++) {
+      status |= fRD_to_XY (&Xvec.elements[N+2*j], &Yvec.elements[N+2*j], r[j], d[j], &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements[N+2*j - 1] = Xvec.elements[N+2*j];
+	Yvec.elements[N+2*j - 1] = Yvec.elements[N+2*j];
+      }
+    }
+    Xvec.elements[N+2*Npts-1] = Xvec.elements[N];
+    Yvec.elements[N+2*Npts-1] = Yvec.elements[N];
+    if (!status) continue;
+    // if none of the points are on the visible side of the projection, do not plot the image
+
+    InPic = FALSE;
+    for (j = 0; j < 2*Npts; j+=2) {
+      if ((Xvec.elements[N+j] >= graphmode.xmin) && 
+	  (Xvec.elements[N+j] <= graphmode.xmax) && 
+	  (Yvec.elements[N+j] >= graphmode.ymin) && 
+	  (Yvec.elements[N+j] <= graphmode.ymax))
+	InPic = TRUE;
+    }
+    // if (!status && HIDDEN) status = TRUE;
+    if (!InPic) continue;
+
+    plist[n] = i;
+    n++;
+    if (n > npts - 1) {
+      npts += 200;
+      REALLOCATE (plist, int, npts);
+    }
+    N+=2*Npts;
+    if (N + 16 >= NPTS) {  /* need to leave room for 8 point image */
+      NPTS += 400;
+      REALLOCATE (Xvec.elements, float, NPTS);
+      REALLOCATE (Yvec.elements, float, NPTS);
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d images\n", n);
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (image);
+  return (TRUE);
+
+
+ usage:
+  gprint (GP_ERR, "USAGE: image [options]\n");
+  gprint (GP_ERR, "  +mosaic : show mosaic outline\n");
+  gprint (GP_ERR, "  -mosaic : only mosaic outline\n");
+  gprint (GP_ERR, "  -hidden : (deprecated)\n");
+  gprint (GP_ERR, "  -name   : only names matching (start of name)\n");
+  gprint (GP_ERR, "  -radius : display all-sky images with given radius octagon\n");
+  gprint (GP_ERR, "  -time (start) (range)  : show images for time and interval\n");
+  gprint (GP_ERR, "  -trange (start) (stop) : show images within time range\n");
+  return (FALSE);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imbox.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imbox.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imbox.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "dvoshell.h"
+
+int imbox (int argc, char **argv) {
+  
+  int j, kapa, Nskip, status, InPic, flipped, N, SOLO_PHU;
+  Vector Xvec, Yvec;
+  double r, d, x[4], y[4], Rmin, Rmax, Rmid;
+  Header header;
+  Coords coords;
+  Coords mosaic;
+  Graphdata graphmode;
+  FILE *f;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  SOLO_PHU = FALSE;
+  if ((N = get_argument (argc, argv, "-phu"))) {
+    SOLO_PHU = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imbox (filename)\n");
+    return (FALSE);
+  }
+
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "file not found\n");
+    return (FALSE);
+  }
+  
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  Rmid = 0.5*(Rmin + Rmax);
+  
+  /* project this image to screen display coords */
+  ALLOCATE (Xvec.elements, float, 8);
+  ALLOCATE (Yvec.elements, float, 8);
+
+  while (gfits_fread_header (f, &header)) {
+    if (!GetCoords (&coords, &header)) goto skip;
+    if (!SOLO_PHU && !strcmp (&coords.ctype[4], "-DIS")) {
+      mosaic = coords;
+      RegisterMosaic (&mosaic);
+      goto skip;
+    }
+    x[0] = 0;               y[0] = 0;
+    x[1] = header.Naxis[0]; y[1] = 0;
+    x[2] = header.Naxis[0]; y[2] = header.Naxis[1];
+    x[3] = 0;               y[3] = header.Naxis[1];
+    status = FALSE;
+    flipped = FALSE;
+    for (j = 0; j < 4; j++) {
+      XY_to_RD (&r, &d, x[j], y[j], &coords);
+      while ((j == 0) && (r < Rmin)) { flipped = TRUE; r += 360.0; }
+      while ((j == 0) && (r > Rmax)) { flipped = TRUE; r -= 360.0; }
+      if ((j > 0) && flipped) {
+	while (r < Rmid) r+= 360.0;
+	while (r > Rmid) r-= 360.0;
+      }
+      status |= fRD_to_XY (&Xvec.elements[2*j], &Yvec.elements[2*j], r, d, &graphmode.coords);
+      if (j > 0) {
+	Xvec.elements[2*j - 1] = Xvec.elements[2*j];
+	Yvec.elements[2*j - 1] = Yvec.elements[2*j];
+      }
+    }
+    Xvec.elements[7] = Xvec.elements[0];
+    Yvec.elements[7] = Yvec.elements[0];
+    InPic = FALSE;
+    for (j = 0; j < 8; j+=2) {
+      if ((Xvec.elements[j] >= graphmode.xmin) && 
+	  (Xvec.elements[j] <= graphmode.xmax) && 
+	  (Yvec.elements[j] >= graphmode.ymin) && 
+	  (Yvec.elements[j] <= graphmode.ymax))
+	InPic = TRUE;
+    }
+
+    Xvec.Nelements = Xvec.Nelements = 8;
+    if (InPic) {
+      graphmode.style = 2; /* points */
+      graphmode.ptype = 100; /* connect pairs of points */
+      graphmode.etype = 0;
+      PlotVectorPair (kapa, 8, Xvec.elements, Yvec.elements, &graphmode);
+    }
+  skip:
+    Nskip = gfits_data_size (&header);
+    fseek (f, Nskip, SEEK_CUR); 
+    gfits_free_header (&header);
+  }
+  fclose (f);
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdata.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdata.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdata.c	(revision 17422)
@@ -0,0 +1,219 @@
+# include "dvoshell.h"
+
+int imdata (int argc, char **argv) {
+  
+  int i, j, k, I;
+  int Nimage, N, NPTS, found, mode, TimeSelect;
+  int n, Nregions, NREGIONS, TimeFormat;
+  int *subset, Nsubset;
+  double trange;
+  time_t tzero, start, stop, TimeReference;
+  Image *image;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist, *skyset;
+  Vector *vec;
+  SkyRegionSelection *selection;
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  start = stop = 0;
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+    if (trange > 0) {
+      start = tzero;
+      stop  = tzero + trange;
+    } else {
+      stop = tzero;
+      start  = tzero + trange;
+    }      
+  }
+
+  gprint (GP_ERR, "function is poorly defined; disabled and may be removed\n");
+  return (FALSE);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imdata (value) [-time t dt]\n");
+    return (FALSE);
+  }
+
+  /* identify selection */
+  mode = 0;
+  if (!strcasecmp (argv[1], "ra")) 
+    mode = 1;
+  if (!strcasecmp (argv[1], "dec")) 
+    mode = 2;
+  if (!strcasecmp (argv[1], "mag")) 
+    mode = 3;
+  if (!strcasecmp (argv[1], "dmag")) 
+    mode = 4;
+  if (!strcasecmp (argv[1], "Mcal")) 
+    mode = 5;
+  if (!strcasecmp (argv[1], "Mrel")) 
+    mode = 6;
+  if (!strcasecmp (argv[1], "source")) 
+    mode = 7;
+  if (!strcasecmp (argv[1], "x")) 
+    mode = 8;
+  if (!strcasecmp (argv[1], "y")) 
+    mode = 9;
+  if (!strcasecmp (argv[1], "time")) 
+    mode = 10;
+  if (mode == 0) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal source time\n");
+    return (FALSE);
+  }
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+
+  Nregions = 0;
+  NREGIONS = 10;
+  ALLOCATE (skylist, SkyList, 1);
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  skylist[0].ownElements = FALSE;
+
+  /* for each image of interest, find the appropriate region files */
+  for (i = 0; i < Nsubset; i++) {
+    I = subset[i];
+
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    skyset = SkyListByImage (sky, -1, &image[I]);
+
+    for (j = 0; j < skyset[0].Nregions; j++) {
+      found = FALSE;
+      for (k = 0; (k < skylist[0].Nregions) && !found; k++) {
+	found = !strcmp (skylist[0].regions[k][0].name, skyset[0].regions[j][0].name);
+      }
+      if (found) continue;
+      skylist[0].regions[Nregions] = skyset[0].regions[j];
+      Nregions ++;
+      CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 10);
+    }
+    SkyListFree (skyset);
+  }	
+  free (subset);
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    gprint (GP_ERR, "try %s\n", skylist[0].regions[i][0].name);
+  } 
+  
+  /* create output vector */
+  NPTS = 1000;
+  REALLOCATE (vec[0].elements, float, NPTS);
+  vec[0].Nelements = N = 0;
+
+  /* for each region file, extract the data of interest in the right time range */
+  for (j = 0; j < skylist[0].Nregions; j++) {
+
+    /* get file name and open */
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* assign vector values */
+    switch (mode) {
+      case (1):  /* ra */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements[N] = catalog.average[n].R - catalog.measure[i].dR / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (2):  /* dec */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  vec[0].elements[N] = catalog.average[n].D - catalog.measure[i].dD / 3600.0;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (3):  /* mag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].M;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (4):  /* dmag */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].dM;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (5):  /* Mcal */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].Mcal;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (6):  /* Mrel */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  n = catalog.measure[i].averef;
+	  // vec[0].elements[N] = catalog.average[n].M;
+	  N++;
+	}
+	break;
+      case (7):  /* source */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = catalog.measure[i].photcode;
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+      case (10):  /* time */
+	for (i = 0; i < catalog.Nmeasure; i++) {
+	  if ((catalog.measure[i].t < start) || (catalog.measure[i].t > stop)) continue;
+	  vec[0].elements[N] = TimeValue (catalog.measure[i].t, TimeReference, TimeFormat);
+	  N++;
+	  CHECK_REALLOCATE (vec[0].elements, float, NPTS, N, 1000);
+	}
+	break;
+    }
+    dvo_catalog_free (&catalog);
+  }
+  
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, MAX(1,N));
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdense.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdense.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imdense.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "dvoshell.h"
+extern double drand48();
+
+int imdense (int argc, char **argv) {
+  
+  long A, B;
+  int i, kapa, N, Nimage, status, NPTS;
+  double r, d, x, y, Rmin, Rmax;
+  Vector Xvec, Yvec;
+  Image *image;
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* need options to list only images in region and only images in a time range */
+  /* also, option to list and not plot or plot and not list images */
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+  
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  N = 0;
+  for (i = 0; i < Nimage; i++) {
+    /* choose a position for point within image box */
+    x = (0.1 + 0.9*drand48()) * image[i].NX;
+    y = (0.1 + 0.9*drand48()) * image[i].NY;
+    /* project this image to screen display coords */
+    status = FALSE;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, x, y, &image[i].coords);
+    while (r < Rmin) r += 360.0; 
+    while (r > Rmax) r -= 360.0; 
+    status |= fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], r, d, &graphmode.coords);
+    if ((Xvec.elements[N] >= graphmode.xmin) && 
+	(Xvec.elements[N] <= graphmode.xmax) && 
+	(Yvec.elements[N] >= graphmode.ymin) && 
+	(Yvec.elements[N] <= graphmode.ymax) && status) {
+      N++;
+      if (N > NPTS - 1) { 
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (image);
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imextract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imextract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imextract.c	(revision 17422)
@@ -0,0 +1,320 @@
+# include "dvoshell.h"
+
+// need to upgrade this to support multiple fields and WHERE clauses
+
+enum {ZERO, 
+      RA, 
+      DEC, 
+      Xm, 
+      AIRMASS, 
+      MCAL, 
+      dMCAL, 
+      PHOTCODE, 
+      TIME, 
+      FWHM, 
+      EXPTIME, 
+      NSTAR, 
+      NCAL, 
+      SKY, 
+      FLAG, 
+      NX_PIX, 
+      NY_PIX, 
+      THETA, 
+      SKEW, 
+      SCALE, 
+      DSCALE, 
+      IMAGE_ID,
+      X_LL_CHIP,
+      X_LR_CHIP,
+      X_UL_CHIP,
+      X_UR_CHIP,
+      Y_LL_CHIP,
+      Y_LR_CHIP,
+      Y_UL_CHIP,
+      Y_UR_CHIP,
+      X_LL_FP,
+      X_LR_FP,
+      X_UL_FP,
+      X_UR_FP,
+      Y_LL_FP,
+      Y_LR_FP,
+      Y_UL_FP,
+      Y_UR_FP,
+};
+
+int imextract (int argc, char **argv) {
+  
+  int i, j, Nimage, mode, N, PhotcodeSelect;
+  int TimeSelect, *subset, Nsubset, TimeFormat, FlagSelect, FlagValue;
+  double x, y, ra, dec, t, trange;
+  time_t tzero, TimeReference;
+  SkyRegionSelection *selection;
+
+  PhotCode *code;
+  Image *image;
+  Vector *vec;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  /* check for time-based selection */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  /* check for region-based selection */
+  FlagValue = 0;
+  FlagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-flag"))) {
+    remove_argument (N, &argc, argv);
+    FlagValue = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    FlagSelect = TRUE;
+  }
+
+  /* check for photcode-based selection */
+  code = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      return (FALSE);
+    }
+    if ((code[0].type != PHOT_SEC) && (code[0].type != PHOT_DEP)) {
+      gprint (GP_ERR, "photcode must be primary, secondary, or dependent code\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imextract (value) [-region] [-time start range] [-photcode photcode]\n");
+    return (FALSE);
+  }
+  
+  /* identify selection */
+  mode = ZERO;
+  if (!strcasecmp (argv[1], "ra"       )) mode = RA;
+  if (!strcasecmp (argv[1], "dec"      )) mode = DEC;
+  if (!strcasecmp (argv[1], "Xm"       )) mode = Xm;
+  if (!strcasecmp (argv[1], "airmass"  )) mode = AIRMASS;
+  if (!strcasecmp (argv[1], "Mcal"     )) mode = MCAL;
+  if (!strcasecmp (argv[1], "dMcal"    )) mode = dMCAL;
+  if (!strcasecmp (argv[1], "photcode" )) mode = PHOTCODE;
+  if (!strcasecmp (argv[1], "time"     )) mode = TIME;
+  if (!strcasecmp (argv[1], "FWHM"     )) mode = FWHM;
+  if (!strcasecmp (argv[1], "exptime"  )) mode = EXPTIME;
+  if (!strcasecmp (argv[1], "nstar"    )) mode = NSTAR;
+  if (!strcasecmp (argv[1], "ncal"     )) mode = NCAL;
+  if (!strcasecmp (argv[1], "sky"      )) mode = SKY;
+  if (!strcasecmp (argv[1], "flag"     )) mode = FLAG;
+  if (!strcasecmp (argv[1], "NX"       )) mode = NX_PIX;
+  if (!strcasecmp (argv[1], "NY"       )) mode = NY_PIX;
+  if (!strcasecmp (argv[1], "theta"    )) mode = THETA;
+  if (!strcasecmp (argv[1], "skew"     )) mode = SKEW;
+  if (!strcasecmp (argv[1], "scale"    )) mode = SCALE;
+  if (!strcasecmp (argv[1], "dscale"   )) mode = DSCALE;
+  if (!strcasecmp (argv[1], "imageID"  )) mode = IMAGE_ID;
+  if (!strcasecmp (argv[1], "X_LL_CHIP")) mode = X_LL_CHIP;
+  if (!strcasecmp (argv[1], "X_LR_CHIP")) mode = X_LR_CHIP;
+  if (!strcasecmp (argv[1], "X_UL_CHIP")) mode = X_UL_CHIP;
+  if (!strcasecmp (argv[1], "X_UR_CHIP")) mode = X_UR_CHIP;
+  if (!strcasecmp (argv[1], "Y_LL_CHIP")) mode = Y_LL_CHIP;
+  if (!strcasecmp (argv[1], "Y_LR_CHIP")) mode = Y_LR_CHIP;
+  if (!strcasecmp (argv[1], "Y_UL_CHIP")) mode = Y_UL_CHIP;
+  if (!strcasecmp (argv[1], "Y_UR_CHIP")) mode = Y_UR_CHIP;
+  if (!strcasecmp (argv[1], "X_LL_FP"  )) mode = X_LL_FP;
+  if (!strcasecmp (argv[1], "X_LR_FP"  )) mode = X_LR_FP;
+  if (!strcasecmp (argv[1], "X_UL_FP"  )) mode = X_UL_FP;
+  if (!strcasecmp (argv[1], "X_UR_FP"  )) mode = X_UR_FP;
+  if (!strcasecmp (argv[1], "Y_LL_FP"  )) mode = Y_LL_FP;
+  if (!strcasecmp (argv[1], "Y_LR_FP"  )) mode = Y_LR_FP;
+  if (!strcasecmp (argv[1], "Y_UL_FP"  )) mode = Y_UL_FP;
+  if (!strcasecmp (argv[1], "Y_UR_FP"  )) mode = Y_UR_FP;
+  if (mode == ZERO) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dec airmass Mcal dMcal Xm photcode time fwhm exptime nstar ncal sky flag\n");
+    return (FALSE);
+  }
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  if ((mode == RA) || (mode == DEC)) BuildChipMatch (image, Nimage);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* create storage vector */
+  REALLOCATE (vec[0].elements, float, Nimage);
+  vec[0].Nelements = Nimage;
+  
+  N = 0;
+  /* assign vector values */
+  for (i = 0; i < Nsubset; i++) {
+    j = subset[i];
+    if (PhotcodeSelect) {
+      if (code[0].type == PHOT_DEP) {
+	if (code[0].code != image[j].photcode) continue;
+      } else {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (image[j].photcode)) continue;
+      }
+    }
+    if (FlagSelect && (FlagValue != image[j].code)) continue;
+    switch (mode) {
+      case RA:
+	if (!FindMosaicForImage (image, Nimage, j)) continue;
+	x = 0.5*image[j].NX;
+	y = 0.5*image[j].NY;
+	XY_to_RD (&ra, &dec, x, y, &image[j].coords);
+	vec[0].elements[N] = ra;
+	break;
+      case DEC:
+	if (!FindMosaicForImage (image, Nimage, j)) continue;
+	x = 0.5*image[j].NX;
+	y = 0.5*image[j].NY;
+	XY_to_RD (&ra, &dec, x, y, &image[j].coords);
+	vec[0].elements[N] = dec;
+	break;
+      case Xm:
+	vec[0].elements[N] = pow(10.0, 0.01*image[j].Xm);
+	break;
+      case AIRMASS:
+	vec[0].elements[N] = image[j].secz;
+	break;
+      case MCAL:
+	vec[0].elements[N] = image[j].Mcal;
+	break;
+      case dMCAL:
+	vec[0].elements[N] = image[j].dMcal;
+	break;
+      case PHOTCODE:
+	vec[0].elements[N] = image[j].photcode;
+	break;
+      case TIME:
+	t = image[j].tzero + 0.5*image[j].NY * image[j].trate / 10000;
+	vec[0].elements[N] = TimeValue (t, TimeReference, TimeFormat);
+	break;
+      case FWHM:
+	vec[0].elements[N] = image[j].fwhm_x / 25.0;
+	break;
+      case EXPTIME:
+	vec[0].elements[N] = image[j].exptime;
+	break;
+      case NSTAR:
+	vec[0].elements[N] = image[j].nstar;
+	break;
+      case NCAL:
+	vec[0].elements[N] = image[j].Mxxxx;
+	break;
+      case SKY:
+	vec[0].elements[N] = image[j].Myyyy + 0x8000;
+	break;
+      case FLAG:
+	vec[0].elements[N] = image[j].code;
+	break;
+      case NX_PIX:
+	vec[0].elements[N] = image[j].NX;
+	break;
+      case NY_PIX:
+	vec[0].elements[N] = image[j].NY;
+	break;
+      case IMAGE_ID:
+	vec[0].elements[N] = image[j].imageID;
+	break;
+      case THETA: {
+	double theta1, theta2, s1, s2;
+	s1 = SIGN(image[j].coords.pc1_1);
+	s2 = SIGN(image[j].coords.pc2_2);
+	theta1 = DEG_RAD*atan2 (+s1*image[j].coords.pc1_2, s1*image[j].coords.pc1_1);
+	theta2 = DEG_RAD*atan2 (-s2*image[j].coords.pc2_1, s2*image[j].coords.pc2_2);
+	vec[0].elements[N] = 0.5*(theta1+theta2);
+	break; }
+      case SKEW: {
+	double theta1, theta2, s1, s2;
+	s1 = SIGN(image[j].coords.pc1_1);
+	s2 = SIGN(image[j].coords.pc2_2);
+	theta1 = DEG_RAD*atan2 (+s1*image[j].coords.pc1_2, s1*image[j].coords.pc1_1);
+	theta2 = DEG_RAD*atan2 (-s2*image[j].coords.pc2_1, s2*image[j].coords.pc2_2);
+	vec[0].elements[N] = (theta1-theta2);
+	break; }
+      case SCALE: {
+	double scale1, scale2;
+	scale1 = fabs(image[j].coords.cdelt1);
+	scale2 = fabs(image[j].coords.cdelt2);
+	vec[0].elements[N] = 0.5*(scale1+scale2);
+	break; }
+      case DSCALE: {
+	double scale1, scale2;
+	scale1 = fabs(image[j].coords.cdelt1);
+	scale2 = fabs(image[j].coords.cdelt2);
+	vec[0].elements[N] = (scale1-scale2);
+	break; }
+
+    // reference pixel extractions
+    case X_LL_CHIP:
+    case Y_LL_CHIP:
+    case Y_LR_CHIP:
+    case X_UL_CHIP:
+      vec[0].elements[N] = 0.0;
+      break;
+    case X_LR_CHIP:
+    case X_UR_CHIP:
+      vec[0].elements[N] = image[j].NX;
+      break;
+    case Y_UL_CHIP:
+    case Y_UR_CHIP:
+      vec[0].elements[N] = image[j].NX;
+      break;
+
+    case X_LL_FP:
+    case Y_LL_FP:
+      XY_to_LM (&x, &y, 0.0, 0.0, &image[j].coords);
+      vec[0].elements[N] = (mode == X_LL_FP) ? x : y;
+      break;
+    case X_LR_FP:
+    case Y_LR_FP:
+      XY_to_LM (&x, &y, image[j].NX, 0.0, &image[j].coords);
+      vec[0].elements[N] = (mode == X_LR_FP) ? x : y;
+      break;
+    case X_UL_FP:
+    case Y_UL_FP:
+      XY_to_LM (&x, &y, 0.0, image[j].NY, &image[j].coords);
+      vec[0].elements[N] = (mode == X_UL_FP) ? x : y;
+      break;
+    case X_UR_FP:
+    case Y_UR_FP:
+      XY_to_LM (&x, &y, image[j].NX, image[j].NY, &image[j].coords);
+      vec[0].elements[N] = (mode == X_UR_FP) ? x : y;
+      break;
+    }
+    N++;
+  }
+  
+  vec[0].Nelements = N;
+  REALLOCATE (vec[0].elements, float, N);
+
+  free (subset);
+  free (image);
+  return (TRUE);
+  
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imlist.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imlist.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imlist.c	(revision 17422)
@@ -0,0 +1,112 @@
+# include "dvoshell.h"
+
+int imlist (int argc, char **argv) {
+  
+  int i, j, N, Nimage, *subset, Nsubset, TimeSelect, RegionSelect, TimeFormat, NameSelect;
+  int PhotcodeSelect;
+  time_t tzero, TimeReference;
+  double r, d, trange, t;
+  char *name;
+  Image *image;
+  PhotCode *PhotcodeValue;
+  SkyRegionSelection *selection;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "plotting in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  }
+
+  RegionSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    RegionSelect = TRUE;
+  }
+
+  PhotcodeValue = NULL;
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyName (argv[N]);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-Nphotcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    PhotcodeValue = GetPhotcodebyCode (atoi(argv[N]));
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  name = NULL;
+  NameSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    NameSelect = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: image [-time start range] [-region] [-name string]\n");
+    return (FALSE);
+  }
+  
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+  BuildChipMatch (image, Nimage);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (NameSelect && (strstr (image[i].name, name) == (char *) NULL)) continue;
+    if (PhotcodeSelect) {
+      if (PhotcodeValue[0].type == PHOT_DEP) {
+	if (PhotcodeValue[0].code != image[i].photcode) continue;
+      } else {
+	if (PhotcodeValue[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    t = TimeValue (image[i].tzero, TimeReference, TimeFormat);
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      XY_to_RD (&r, &d, 0.0, 0.0, &image[i].coords);
+    } else {
+      XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    }
+    gprint (GP_LOG, "%3d %s %8.4f %8.4f %f %5d %2d %4.2f %5.3f %5.3f\n", 
+	     i, image[i].name, r, d, t, image[i].nstar, image[i].photcode, image[i].secz, image[i].Mcal, image[i].dMcal);
+  }
+
+  free (subset);
+  free (image);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imphot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imphot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imphot.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "dvoshell.h"
+
+int imphot (int argc, char **argv) {
+  
+  time_t tzero;
+  double trange;
+  int N, GreyScale;
+  int i, j, Nimage, Nsubset, *subset;
+  char bufname[64];
+  float *p;
+  double fx, fy, x, y, applyMcal();
+  Image *image;
+  Buffer *buf;
+  SkyRegionSelection *selection;
+
+  GreyScale = FALSE;
+  if ((N = get_argument (argc, argv, "-g"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (bufname, argv[N]);
+    remove_argument (N, &argc, argv);
+    GreyScale = TRUE;
+  }
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imphot tzero trange [-g buffer]\n");
+    return (FALSE);
+  }
+
+  buf = NULL;
+  if (GreyScale) {
+    if ((buf = SelectBuffer (bufname, ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    CreateBuffer (buf, 100, 200, -32, 0.0, 1.0);
+  }
+
+  /* load image(s) in time range given */
+  if (!ohana_str_to_time (argv[1], &tzero)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_dtime (argv[2], &trange)) { 
+    gprint (GP_ERR, "syntax error\n");
+    return (FALSE);
+  }    
+  gprint (GP_ERR, "searching in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TRUE);
+
+  if ((Nsubset > 1) && GreyScale) {
+    gprint (GP_ERR, "more than one image selected, making GreyScale of first only\n");
+  }
+
+  if (GreyScale && Nsubset) {
+    fx = image[subset[0]].NX / 100;
+    fy = image[subset[0]].NY / 200;
+    p = (float *) buf[0].matrix.buffer;
+    for (y = 0; y < 200; y+=1.0) {
+      for (x = 0; x < 100; x+=1.0, p++) {
+	*p = applyMcal (&image[subset[0]], (fx*x), (fy*y));
+      }
+    }
+  }
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    switch (image[i].order) {
+    case 0:
+      gprint (GP_ERR, "%s: %d - %f\n", image[i].name, image[i].order, image[i].Mcal);
+      break;
+    case 1:
+      gprint (GP_ERR, "%s: %d - %f, %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My);
+      break;
+    case 2:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, image[i].Mxx, image[i].Mxy, image[i].Myy);
+      break;
+    case 3:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy);
+      break;
+    case 4:
+      gprint (GP_ERR, "%s: %d - %f, %d %d, %d %d %d, %d %d %d %d, %d %d %d %d %d\n", image[i].name, image[i].order, image[i].Mcal, image[i].Mx, image[i].My, 
+	       image[i].Mxx, image[i].Mxy, image[i].Myy, image[i].Mxxx, image[i].Mxxy, image[i].Mxyy, image[i].Myyy,
+	       image[i].Mxxxx, image[i].Mxxxy, image[i].Mxxyy, image[i].Mxyyy, image[i].Myyyy);
+      break;
+    }
+  }
+
+  free (image);
+  free (subset);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imrough.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imrough.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imrough.c	(revision 17422)
@@ -0,0 +1,310 @@
+# include "dvoshell.h"
+RegImage *load_imreg (char *DataBase, int *nimage);
+
+# define NVALUE 22
+enum {V_NONE, V_EXPTIME, V_CCDN, V_SKY, V_BIAS, V_FILTER, V_FWHM, V_AIRM, V_TIME, V_TEMP0, V_TEMP1, V_TEMP2, V_TEMP3, V_TELFOCUS, V_XPROBE, V_YPROBE, V_ZPROBE, V_RA, V_DEC, V_DETTEMP, V_ROTANGLE, V_REGTIME};
+static char valuename[NVALUE][32] = {"none", "exptime", "ccd", "sky", "bias", "filter", "fwhm", "airmass", "time", "temp0", "temp1", "temp2", "temp3", "telfocus", "xprobe", "yprobe", "zprobe", "ra", "dec", "dettemp", "rotangle", "regtime"};
+
+int imrough (int argc, char **argv) {
+ 
+  int i, N, Nimage, TimeSelect;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect, TimeFormat;
+  int type, value, mode, CCD, NVEC;
+  char DataBase[256], *Filter;
+  double trange;
+  float *Vec;
+  time_t tzero, tend, TimeReference;
+  RegImage *image;
+  Vector *vec;
+
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  type = T_UNDEF;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    TypeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    type = get_image_type (argv[N]);
+    if (type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  mode = M_NONE;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    ModeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+    mode = get_image_mode (argv[N]);
+    if (mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: imrough (value)\n");
+    gprint (GP_ERR, "       value options:\n");
+    for (i = 1; i < NVALUE; i++) {
+      gprint (GP_ERR, "%s\n", valuename[i]);
+    }
+    return (FALSE);
+  }
+ 
+  /* identify selection */
+  value = V_NONE;
+  for (i = 0; (i < NVALUE) && (value == V_NONE); i++) {
+    if (!strncasecmp (argv[1], valuename[i], strlen(argv[1]))) value = i;
+  }
+  if (value == V_NONE) {
+    gprint (GP_ERR, "ERROR: invalid image value %s\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  image = load_imreg (DataBase, &Nimage);
+  if (image == (RegImage *) NULL) return (FALSE);
+
+  N = 0;
+  NVEC = 1000;
+  REALLOCATE (vec[0].elements, float, NVEC);
+  Vec = vec[0].elements;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  /* get data */
+  for (i = 0; i < Nimage; i++) {
+    /* skip unmatched selections */
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (TimeSelect && ((image[i].obstime < tzero) || (image[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (image[i].filter, Filter))) continue;
+    if (CCDSelect && (image[i].ccd != CCD)) continue;
+    if (TypeSelect && (image[i].type != type)) continue;
+    if (ModeSelect && (image[i].mode != mode)) continue;
+
+    /* assign correct value */
+    switch (value) {
+    case (V_EXPTIME):
+      Vec[N] = image[i].exptime;
+      break;
+    case (V_CCDN):
+      Vec[N] = image[i].ccd;
+      break;
+    case (V_SKY):
+      Vec[N] = image[i].sky;
+      break;
+    case (V_BIAS):
+      Vec[N] = image[i].bias;
+      break;
+    case (V_FILTER):
+      Vec[N] = image[i].filter[0];
+      break;
+    case (V_FWHM):
+      Vec[N] = image[i].fwhm;
+      break;
+    case (V_AIRM):
+      Vec[N] = image[i].airmass;
+      break;
+    case (V_TIME):
+      Vec[N] = TimeValue (image[i].obstime, TimeReference, TimeFormat);
+      break;
+    case (V_TEMP0):
+      Vec[N] = image[i].teltemp_0;
+      break;
+    case (V_TEMP1):
+      Vec[N] = image[i].teltemp_1;
+      break;
+    case (V_TEMP2):
+      Vec[N] = image[i].teltemp_2;
+      break;
+    case (V_TEMP3):
+      Vec[N] = image[i].teltemp_3;
+      break;
+    case (V_TELFOCUS):
+      Vec[N] = image[i].telfocus;
+      break;
+    case (V_XPROBE):
+      Vec[N] = image[i].xprobe;
+      break;
+    case (V_YPROBE):
+      Vec[N] = image[i].yprobe;
+      break;
+    case (V_ZPROBE):
+      Vec[N] = image[i].zprobe;
+      break;
+    case (V_RA):
+      Vec[N] = image[i].ra;
+      break;
+    case (V_DEC):
+      Vec[N] = image[i].dec;
+      break;
+    case (V_DETTEMP):
+      Vec[N] = image[i].dettemp;
+      break;
+    case (V_ROTANGLE):
+      Vec[N] = image[i].rotangle;
+      break;
+    case (V_REGTIME):
+      Vec[N] = TimeValue (image[i].regtime, TimeReference, TimeFormat);
+      break;
+    }
+    N++;
+    if (N >= NVEC - 1) {
+      NVEC += 1000;
+      REALLOCATE (vec[0].elements, float, NVEC);
+      Vec = vec[0].elements;
+    }
+  }
+
+  REALLOCATE (vec[0].elements, float, MAX (1,N));
+  vec[0].Nelements = N;
+
+  free (image);
+  return (TRUE);
+
+}
+
+RegImage *load_imreg (char *DataBase, int *nimage) {
+
+  int Nimage, status;
+  char line[80];
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  FTable table;
+  RegImage *image;
+
+  *nimage = 0;
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* load in database header */
+  if (!gfits_fread_header (f, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* check for database v1, v2 */
+  gfits_scan (&header, "ORIGIN", "%s", 1, line);
+  if (!strcmp (line, "MDM Observatory")) {
+
+    fseek (f, header.size, SEEK_SET);
+    
+    /* load existing data from database */
+    gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+    ALLOCATE (image, RegImage, Nimage);
+    status = fread (image, sizeof(RegImage), Nimage, f);
+    fclose (f);
+    
+    if (status != Nimage) {
+      gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+      gfits_free_header (&header);
+      free (image);
+      return ((RegImage *) NULL);
+    }
+    gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+    *nimage = Nimage;
+    return (image);
+  }
+
+  /* we probably have v3 */
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database matrix\n");
+    return ((RegImage *) NULL);
+  }
+
+  table.header = &theader;
+  if (!gfits_fread_ftable  (f, &table, "IMAGE_DATABASE")) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    gprint (GP_ERR, "ERROR: trouble reading database table\n");
+    return ((RegImage *) NULL);
+  }
+
+  /* convert to internal format */
+  image = (RegImage *) table.buffer;
+  gfits_scan (table.header, "NAXIS2", "%d", 1, &Nimage);
+  gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+
+  *nimage = Nimage;
+  return (image);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imsearch.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imsearch.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imsearch.c	(revision 17422)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+int imsearch (int argc, char **argv) {
+ 
+  char DataBase[256], name[64];
+  FILE *f;
+  Header header;
+  RegImage *pimage;
+  int i, Nimage, status, N, TimeSelect, SaveNames;
+  int ModeSelect, TypeSelect, CCDSelect, FilterSelect;
+  char *Filter, *obstime;
+  int Type, Mode, CCD;
+  time_t tzero, obstime_sec;
+  double trange;
+   
+  VarConfig ("REGISTRATION_DATABASE", "%s", DataBase);
+
+  SaveNames = FALSE;
+  if ((N = get_argument (argc, argv, "-save"))) {
+    remove_argument (N, &argc, argv);
+    SaveNames = TRUE;
+  }
+ 
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+ 
+  Type = 0;
+  TypeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-type"))) {
+    remove_argument (N, &argc, argv);
+    Type = get_image_type (argv[N]);
+    if (Type == T_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image type %s\n", argv[N]);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TypeSelect = TRUE;
+  }
+ 
+  Mode = 0;
+  ModeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-mode"))) {
+    remove_argument (N, &argc, argv);
+    Mode = get_image_mode (argv[N]);
+    if (Mode == M_UNDEF) {
+      gprint (GP_ERR, "ERROR: invalid image mode %s\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+    ModeSelect = TRUE;
+  }
+ 
+  CCD = 0;
+  CCDSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-ccd"))) {
+    remove_argument (N, &argc, argv);
+    CCD = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    CCDSelect = TRUE;
+  }
+ 
+  Filter = NULL;
+  FilterSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-filter"))) {
+    remove_argument (N, &argc, argv);
+    Filter = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    FilterSelect = TRUE;
+    if (!strcasecmp (Filter, "X")) {
+      FilterSelect = FALSE;
+    }
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imsearch [-time start range] [-type type] [-mode mode] [-ccd N] [-filter name]\n");
+    exit (1);
+  }
+
+  /* load in database header */
+  if (!gfits_read_header (DataBase, &header)) {
+    gprint (GP_ERR, "ERROR: trouble reading database header\n");
+    return (FALSE);
+  }
+
+  /* open database */
+  f = fopen (DataBase, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "ERROR: can't open Registration Database\n");
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET);
+
+  /* load existing data from database */
+  gfits_scan (&header, "NIMAGES", "%d", 1, &Nimage);
+  ALLOCATE (pimage, RegImage, Nimage);
+  status = fread (pimage, sizeof(RegImage), Nimage, f);
+  fclose (f);
+  if (status != Nimage) {
+    gprint (GP_ERR, "ERROR: header and data in dB don't match (%d vs %d)\n", Nimage, status);
+    gfits_free_header (&header);
+    free (pimage);
+    return (FALSE);
+  }
+  gfits_convert_RegImage (pimage, sizeof (RegImage), Nimage);
+
+  /* print out all data */
+  N = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (TimeSelect && ((pimage[i].obstime < tzero) || (pimage[i].obstime > tzero + trange))) continue;
+    if (FilterSelect && (strcasecmp (pimage[i].filter, Filter))) continue;
+    if (ModeSelect && (pimage[i].mode != Mode)) continue;
+    if (CCDSelect && (pimage[i].ccd != CCD)) continue;
+    if (TypeSelect && (pimage[i].type != Type)) continue;
+
+    obstime_sec = (time_t) pimage[i].obstime;
+    obstime = ctime (&obstime_sec);
+    obstime[strlen(obstime)-1] = 0;
+
+    gprint (GP_LOG, "%5d %6s %6s %2d %2d   ", i, get_type_name(pimage[i].type), get_mode_name(pimage[i].mode), pimage[i].ccd, pimage[i].type);
+    gprint (GP_LOG, "%s %s  ", pimage[i].pathname, pimage[i].filename);
+    gprint (GP_LOG, "%s %s %f %s\n", pimage[i].filter, pimage[i].instrument, pimage[i].exptime, obstime);
+
+    if (SaveNames) {
+      sprintf (name, "IMAGEpath:%d", N);
+      set_str_variable (name, pimage[i].pathname);
+      sprintf (name, "IMAGEfile:%d", N);
+      set_str_variable (name, pimage[i].filename);
+      sprintf (name, "IMAGEmode:%d", N);
+      set_int_variable (name, pimage[i].mode);
+    }
+    N++;
+  }
+  if (SaveNames) {
+    set_int_variable ("IMAGEpath:n", N);
+    set_int_variable ("IMAGEfile:n", N);
+  }
+
+  free (pimage);
+  gfits_free_header (&header);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imstats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imstats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/imstats.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "dvoshell.h"
+
+int imstats (int argc, char **argv) {
+  
+  int i, kapa, Nimage, N;
+  int Mcal, AutoLimits;
+  double r, d;
+  Image *image;
+  Vector Xvec, Yvec;  
+  Graphdata graphmode;
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  Mcal = TRUE;
+  if ((N = get_argument (argc, argv, "-dM"))) {
+    remove_argument (N, &argc, argv);
+    Mcal = FALSE;
+  }
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: imstats [-dM] [-l]\n");
+    return (FALSE);
+  }
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  /* assign vector values */
+  Xvec.Nelements = Nimage;
+  Yvec.Nelements = Nimage;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  gprint (GP_LOG, "seq  ra (J2000) dec    time (s)   Nstars\n");
+  for (i = 0; i < Nimage; i++) {
+    Xvec.elements[i] = image[i].secz;
+    if (Mcal) 
+      Yvec.elements[i] = image[i].Mcal;
+    else 
+      Yvec.elements[i] = image[i].dMcal;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    XY_to_RD (&r, &d, 0.5*image[i].NX, 0.5*image[i].NY, &image[i].coords);
+    gprint (GP_ERR, "%d %8.4f %8.4f %10d %6d  %5.3f %6.3f %6.3f\n", 
+	     i, r, d, image[i].tzero, image[i].nstar, Xvec.elements[i], 
+	     image[i].Mcal, image[i].dMcal);
+  } 
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  graphmode.style = 2;
+  graphmode.etype = 0;
+  PlotVectorPair (kapa, Nimage, Xvec.elements, Yvec.elements, &graphmode);
+  
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/init.c	(revision 17422)
@@ -0,0 +1,120 @@
+# include "dvoshell.h"
+
+int avextract       PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int catdir_define   PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmpload         PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int detrend         PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int fitcolors       PROTO((int, char **));
+int fitsed          PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int getxtra         PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **));
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int imstats         PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int lightcurve      PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int paverage        PROTO((int, char **));
+int procks          PROTO((int, char **));
+int showtile        PROTO((int, char **));
+int skycat          PROTO((int, char **));
+int skycoverage     PROTO((int, char **));
+int skyregion       PROTO((int, char **));
+int simage          PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "avextract",   avextract,    "extract average data values"},
+  {1, "badimages",   badimages,    "look for images with anomalous astrometry"},
+  {1, "calextract",  calextract,   "extract photometry calibration"},
+  {1, "calmextract", calmextract,  "extract photometry calibration"},
+  {1, "catdir",      catdir_define,"re-define CATDIR"},
+  {1, "ccd",         ccd,          "plot color-color diagram"},
+  {1, "cmatch",      cmatch,       "match two catalogs"},
+  {1, "cmd",         cmd,          "plot cmd of stars in current region"},
+  {1, "cmpload",     cmpload,      "load cmp file into ?"},
+  {1, "cmpread",     cmpread,      "read data from cmp format files"},
+  {1, "ddmags",      ddmags,       "plot magnitude differences"},
+  {1, "detrend",     detrend,      "extract from detrend database?"},
+  {1, "dmagaves",    dmagaves,     "foo"},
+  {1, "dmagmeas",    dmagmeas,     "foo"},
+  {1, "dmags",       dmags,        "plot differential magnitudes between filters"},
+  {1, "dmt",         dmt,          "plot mag scatter"},
+  {1, "elixir",      elixir,       "talk to elixir"},
+  {1, "fitcolors",   fitcolors,    "fit chip-to-chip color terms"},
+  {1, "fitsed",      fitsed,       "fit stellar SEDs to objects"},
+  {1, "gcat",        gcat,         "get catalog at location"},
+  {1, "gimages",     gimages,      "get images at location"},
+  {1, "gstar",       gstar,        "get star statistics"},
+  {1, "images",      images,       "plot image boxes"},
+  {1, "imbox",       imbox,        "plot expected image box"},
+  {1, "imdata",      imdata,       "extract data for specific images"},
+  {1, "imdense",     imdense,      "image density plot"},
+  {1, "imextract",   imextract,    "extract vectors from catalogs"},
+  {1, "imlist",      imlist,       "list image info"},
+  {1, "imphot",      imphot,       "image photometry info"},
+  {1, "imrough",     imrough,      "get info from imruf database"},
+  {1, "imsearch",    imsearch,     "get info from imreg database"},
+  {1, "imstats",     imstats,      "plot image statistics"},
+  {1, "lcat",        lcat,         "list catalogs in region"},
+  {1, "lcurve",      lcurve,       "plot lightcurve for a star"},
+  {1, "lightcurve",  lightcurve,   "extract lightcurve for a star"},
+  {1, "mextract",    mextract,     "extract measure data values"},
+  {1, "pcat",        skycat,       "plot catalog boundaries"},
+  {1, "photcodes",   photcodes,    "list photometry codes"},
+  {1, "pmeasure",    pmeasure,     "plot individual measurements"},
+  {1, "paverage",    paverage,     "plot average magnitude"},
+  {1, "procks",      procks,       "plot rocks"},
+  {1, "showtile",    showtile,     "plot tile pattern"},
+  {1, "skycat",      skycat,       "show sky catalog boundaries"},
+  {1, "skycoverage", skycoverage,  "measure image union on sky"},
+  {1, "skyregion",   skyregion,    "set sky region for db queries"},
+  {1, "simage",      simage,       "plot stars in an image"},
+  {1, "subpix",      subpix,       "get subpixel positions"},
+  {1, "version",     version,      "show version information"},
+//{1, "addxtra",     addxtra,      "add extra data to object"},
+//{1, "getxtra",     getxtra,      "get extra data from object"},
+}; 
+
+/* move to astro */
+
+void InitDVO () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreeDVO () {
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcat.c	(revision 17422)
@@ -0,0 +1,46 @@
+# include "dvoshell.h"
+
+int lcat (int argc, char **argv) {
+  
+  double Radius;
+  int i, N, ShowAll;
+  char exists;
+  struct stat filestat;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!GetGraphData (&graphmode, NULL, NULL)) return (FALSE);
+
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: lcat [-all]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    exists = 'Y';
+    if (stat (skylist[0].filename[i], &filestat) == -1) exists = 'N';
+    if (ShowAll) {
+      gprint (GP_ERR, "%3d %s  %c\n", i, skylist[0].regions[i][0].name, exists);
+    } else {
+      if (exists == 'Y') {
+	gprint (GP_ERR, "%3d %s\n", i, skylist[0].regions[i][0].name);
+      }
+    }
+  }
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcurve.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcurve.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lcurve.c	(revision 17422)
@@ -0,0 +1,192 @@
+# include "dvoshell.h"
+
+int lcurve (int argc, char **argv) {
+  
+  char string[128], *p;
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  int kapa, TimeFormat;
+  int Nstars, found, AutoLimits, ErrorBars, GalMag, AbsPhot, SaveVectors;
+  int i, j, m, N, NPTS, *N1;
+  time_t TimeReference;
+  struct tm *timeptr;
+  Vector *xvec, *yvec;
+  Vector Xvec, Yvec, dYvec;
+  Catalog catalog;
+  Graphdata graphmode;
+  SkyTable *sky;
+  SkyList *skylist;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
+
+  AutoLimits = FALSE;
+  if ((N = get_argument (argc, argv, "-l"))) {
+    remove_argument (N, &argc, argv);
+    AutoLimits = TRUE;
+  }
+
+  xvec = yvec = NULL;
+  SaveVectors = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    SaveVectors = TRUE;
+    if ((xvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    if ((yvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+  }
+
+  AbsPhot = FALSE;
+  if ((N = get_argument (argc, argv, "-abs"))) {
+    remove_argument (N, &argc, argv);
+    AbsPhot = TRUE;
+  }
+
+  GalMag = FALSE;
+  if ((N = get_argument (argc, argv, "-gal"))) {
+    gprint (GP_ERR, "galaxy magnitudes currently disabled\n");
+    return (FALSE);
+  }
+
+  ErrorBars = FALSE;
+  if ((N = get_argument (argc, argv, "-d"))) {
+    remove_argument (N, &argc, argv);
+    ErrorBars = TRUE;
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  NPTS = 100;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  dYvec.elements = NULL;
+  if (ErrorBars) { ALLOCATE (dYvec.elements, float, NPTS); }
+  N = 0;
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      /* found star, extract measurements */
+      m = catalog.average[N1[i]].measureOffset;
+      for (j = 0; j < catalog.average[N1[i]].Nmeasure; j++, m++) {
+	if (ErrorBars) dYvec.elements[N] = catalog.measure[m].dM;
+	Xvec.elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	Yvec.elements[N] = PhotCat (&catalog.measure[m]);
+	/**** need to use PhotRel optionally here ****/
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (Xvec.elements, float, NPTS);
+	  REALLOCATE (Yvec.elements, float, NPTS);
+	  if (ErrorBars) { REALLOCATE (dYvec.elements, float, NPTS); }
+	}
+      }      
+    }
+  }
+  Xvec.Nelements = Yvec.Nelements = N;
+  if (ErrorBars) dYvec.Nelements = N;
+  
+  if (ErrorBars)
+    fsortthree (Xvec.elements, Yvec.elements, dYvec.elements, N);
+  else
+    fsortpair (Xvec.elements, Yvec.elements, N);
+
+  /* autoscale the plot */
+  if (AutoLimits) SetLimits (&Xvec, &Yvec, &graphmode);
+
+  if (ErrorBars) 
+    graphmode.etype = 1;  /* y errors only in lcurves */
+  else
+    graphmode.etype = 0;  
+
+  KapaPrepPlot (kapa, N, &graphmode);
+  KapaPlotVector (kapa, N, Xvec.elements, "x");
+  KapaPlotVector (kapa, N, Yvec.elements, "y");
+  if (ErrorBars) {
+    KapaPlotVector (kapa, N, dYvec.elements, "dym");
+    KapaPlotVector (kapa, N, dYvec.elements, "dyp");
+  }
+
+  timeptr = gmtime ((time_t *)&TimeReference);
+
+  if ((p = get_variable ("TIMEFORMAT")) == (char *) NULL) p = strcreate ("days");
+  sprintf (string, "%s since %02d/%02d/%02d UT", p,
+	   timeptr[0].tm_year, timeptr[0].tm_mon+1, timeptr[0].tm_mday);
+  free (p);
+  KapaSendLabel (kapa, string, 0);
+
+  free (RA);
+  free (DEC);
+  free (N1);
+
+  if (SaveVectors) {
+    free (xvec[0].elements);
+    free (yvec[0].elements);
+    xvec[0].elements = Xvec.elements;
+    yvec[0].elements = Yvec.elements;
+    xvec[0].Nelements = yvec[0].Nelements = Xvec.Nelements;
+  } else {
+    free (Xvec.elements);
+    free (Yvec.elements);
+  }
+
+  if (ErrorBars) free (dYvec.elements);
+  dvo_catalog_free (&catalog);
+
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lightcurve.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lightcurve.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/lightcurve.c	(revision 17422)
@@ -0,0 +1,145 @@
+# include "dvoshell.h"
+
+int lightcurve (int argc, char **argv) {
+  
+  double Ra, Dec, Radius, Radius2, r;
+  double *RA, *DEC;
+  int Nstars, found, PhotCodeSelect;
+  int i, j, k, m, N, NPTS, Nsecfilt, RELPHOT, *N1, TimeFormat;
+  time_t TimeReference;
+
+  PhotCode *code;
+  Catalog catalog;
+  SkyTable *sky;
+  SkyList *skylist;
+  Vector *tvec, *mvec, *dmvec;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if ((tvec = SelectVector ("tc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((mvec = SelectVector ("mc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((dmvec = SelectVector ("dmc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  RELPHOT = FALSE;
+  if ((N = get_argument (argc, argv, "-rel"))) {
+    remove_argument (N, &argc, argv);
+    RELPHOT = TRUE;
+  }
+
+  code = NULL;
+  PhotCodeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    if ((code = GetPhotcodebyName (argv[N])) == NULL) {
+      gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    PhotCodeSelect = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) {
+    gprint (GP_ERR, "USAGE: lightcurve RA DEC Radius\n");
+    return (FALSE);
+  }
+  
+  Ra = atof (argv[1]);
+  Dec = atof (argv[2]);
+  Radius = atof (argv[3]);
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* set filename, read in header */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (N1, int, Nstars);
+
+  /* find star(s) in RA, DEC list -- use a dumb algorithm for now, improve later */
+  /* stars are not guaranteed to be sorted in RA or in DEC, so first sort the list */
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    N1[i] = i;
+  }
+  /* sort list by DEC */
+  if (Nstars > 1) sort_coords_index (DEC, RA, N1, Nstars);
+  /* at this point, RA, DEC, and N1 are sorted by DEC.  
+     catalog.average[N1[i]].R = RA[i] */
+
+  N = 0;
+  NPTS = 100;
+  REALLOCATE (tvec[0].elements, float, NPTS);
+  REALLOCATE (mvec[0].elements, float, NPTS);
+  REALLOCATE (dmvec[0].elements, float, NPTS);
+  
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  Radius2 = Radius*Radius;
+  found = FALSE;
+  for (i = 0; (i < catalog.Naverage) && !found; i++) {
+
+    /* this can be improved by using a couple of jumps to get within range */
+    if (Dec > DEC[i] + Radius)
+      continue;
+    
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      k = N1[i];
+      /* found star, extract measurements */
+      m = catalog.average[k].measureOffset;
+      for (j = 0; j < catalog.average[k].Nmeasure; j++, m++) {
+
+	if (PhotCodeSelect) {
+	  if ((code[0].type == PHOT_REF) || (code[0].type == PHOT_DEP)) {
+	    if (code[0].code != catalog.measure[m].photcode) continue;
+	  } 
+	  if (code[0].type == PHOT_SEC) {
+	    if (code[0].code != GetPhotcodeEquivCodebyCode (catalog.measure[m].photcode)) continue;
+	  } 
+	}      
+
+	tvec[0].elements[N] = TimeValue (catalog.measure[m].t, TimeReference, TimeFormat);
+	dmvec[0].elements[N] = catalog.measure[m].dM;
+	if (RELPHOT) {
+	  mvec[0].elements[N] = PhotCat (&catalog.measure[m]);
+	} else {
+	  mvec[0].elements[N] = PhotRel (&catalog.measure[m], &catalog.average[k], &catalog.secfilt[k*Nsecfilt]);
+	}
+	N++; 
+	if (N == NPTS) {
+	  NPTS += 100;
+	  REALLOCATE (tvec[0].elements, float, NPTS);
+	  REALLOCATE (mvec[0].elements, float, NPTS);
+	  REALLOCATE (dmvec[0].elements, float, NPTS);
+	}
+      }      
+    }
+  }
+  fsortthree (tvec[0].elements, mvec[0].elements, dmvec[0].elements, N);
+  tvec[0].Nelements = mvec[0].Nelements = dmvec[0].Nelements = N;
+
+  free (RA);
+  free (DEC);
+  free (N1);
+  dvo_catalog_free (&catalog);
+  SkyListFree (skylist);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/match_image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/match_image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/match_image.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "dvoshell.h"
+
+int match_image (Image *image, int Nimage, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nimage;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[N].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[N].tzero == T) && (image[N].photcode == S)) {
+      return (N);
+    }
+  }
+  return (-1);
+}
+
+int match_image_subset (Image *image, int *subset, int Nsubset, unsigned int T, short int S) {
+
+  int N, Nlo, Nhi, N1, N2;
+
+  /* bracket first value of interest */
+  Nlo = 0; Nhi = Nsubset;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[subset[N]].tzero < T) {
+      Nlo = N;
+    } else {
+      Nhi = N + 1;
+    }
+  }
+  N1 = Nlo;
+
+  /* bracket last value of interest */
+  Nlo = 0; Nhi = Nsubset;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (image[subset[N]].tzero > T) {
+      Nhi = N;
+    } else {
+      Nlo = N - 1;
+    }
+  }
+  N2 = Nhi;
+
+  for (N = N1; N < N2; N++) {
+    if ((image[subset[N]].tzero == T) && (image[subset[N]].photcode == S)) {
+      return (subset[N]);
+    }
+  }
+  return (-1);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/mextract.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/mextract.c	(revision 17422)
@@ -0,0 +1,236 @@
+# include "dvoshell.h"
+
+int mextract (int argc, char **argv) {
+  
+  int i, j, k, m, n, N, Npts, NPTS, last, Nfields, Nreturn, Ncstack, Nstack;
+  int Nsecfilt, VERBOSE, loadImages, mosaicMode;
+  char **cstack, name[1024];
+  float *values;
+  void *Signal;
+
+  Catalog catalog;
+  SkyList *skylist;
+  PhotCode *code;
+  Vector **vec;
+  dbField *fields;
+  dbStack *stack;
+  SkyRegionSelection *selection;
+
+  /* defaults */
+  skylist = NULL;
+  code = NULL;
+  fields = NULL;
+  stack = NULL;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) goto escape;
+
+  // command-line is of the form: avextract field,field, field [where (field op value)...]
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) return (FALSE);
+
+  // XXX add the skyregion limits as if it were a where statement
+
+  // parse the remainder of the line as a boolean math expression
+  cstack = isolate_elements (argc-last, &argv[last], &Ncstack);
+  
+  // construct the db Boolean math stack (frees cstack)
+  stack = dbRPN (Ncstack, cstack, &Nstack);
+  if ((Ncstack > 0) && (Nstack < 1)) {
+    print_error ();
+    goto escape;
+  }
+
+  // add the skyregion limits to the where statement (or create)
+  dbAstroRegionLimits (&stack, &Nstack, selection, DVO_TABLE_MEASURE);
+
+  // parse stack elements into fields and scalars as needed
+  Nreturn = Nfields; 
+  dbCheckStack (stack, Nstack, DVO_TABLE_MEASURE, &fields, &Nfields);
+  // XXX handle errors
+
+  /* load region corresponding to selection above */
+  if ((skylist = SelectRegions (selection)) == NULL) goto escape;
+
+  // load image data if needed (for fields listed below)
+  loadImages = FALSE;
+  mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    if (fields[i].ID == MEAS_XMOSAIC) loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) loadImages = mosaicMode = TRUE;
+  }
+  if (loadImages && !SetImageSelection (mosaicMode, selection)) goto escape;
+
+  /* create storage vector */
+  ALLOCATE (values, float, Nfields);
+  ALLOCATE (vec, Vector *, Nreturn);
+  for (i = 0; i < Nreturn; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+  }
+
+  Npts = 0;
+  NPTS = 1;
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+    /* lock, load, unlock catalog */
+    catalog.filename = skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s (%d of %d)\n", catalog.filename, i, skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* XXX need to call dvo_catalog_chipcoords here passing the loaded images */
+
+    for (j = 0; (j < catalog.Naverage) && !interrupt; j++) {
+      m = catalog.average[j].measureOffset;
+      dbExtractMeasuresInitAve (); // reset counters for saved fields 
+
+      for (k = 0; (k < catalog.average[j].Nmeasure); k++, m++) {
+
+	// extract the relevant values for this measurement
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields 
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[j], &catalog.secfilt[j*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+	// fprintf (stderr, "object: ave: %f, cat: %f, averef %d\n", fields[n].name, values[2], values[3], catalog.measure[m].averef);
+
+	// test the conditional statement
+	if (!dbBooleanCond (stack, Nstack, values)) continue;
+	for (n = 0; n < Nreturn; n++) {
+	  vec[n][0].elements[Npts] = values[n];
+	  // fprintf (stderr, "keep : field: %s, value: %f\n", fields[n].name, values[n]);
+	}
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  for (n = 0; n < Nreturn; n++) {
+	    REALLOCATE (vec[n][0].elements, float, NPTS);
+	  }
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+    // dbStackAllocPrint ();
+    // dbStackAllocReset ();
+    // dbStackFreePrint ();
+    // dbStackFreeReset ();
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  for (n = 0; n < Nreturn; n++) {
+    vec[n][0].Nelements = Npts;
+    REALLOCATE (vec[n][0].elements, float, MAX(1,Npts));
+  }
+
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  return (TRUE);
+
+escape:
+  dbFreeFields (fields, Nfields);
+  dbFreeStack (stack, Nstack);
+  free (stack);
+  FreeImageSelection ();
+  SkyListFree (skylist);
+  FreeSkyRegionSelection (selection);
+  dvo_catalog_free (&catalog);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mextract field[,field,field...] where (expression)\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, " USAGE: avextract field[,field,field...] where (expression)\n");
+    gprint (GP_ERR, "  RA : right ascension (J2000) for detection\n");
+    gprint (GP_ERR, "  DEC : declination for detection\n");
+    gprint (GP_ERR, "  RA:ave : average right ascension (J2000) for object\n");
+    gprint (GP_ERR, "  DEC:ave : average declination for object\n");
+    gprint (GP_ERR, "  RA:err : ra scatter \n");
+    gprint (GP_ERR, "  DEC:err : dec scatter\n");
+    gprint (GP_ERR, "  uRA : proper motion in ra\n");
+    gprint (GP_ERR, "  uDEC : proper motion in dec\n");
+    gprint (GP_ERR, "  duRA : proper motion error in ra\n");
+    gprint (GP_ERR, "  duDEC : proper motion error in dec\n");
+    gprint (GP_ERR, "  PAR : parallax\n");
+    gprint (GP_ERR, "  dPAR : parallax error \n");
+    gprint (GP_ERR, "  nmeas : number of measurements\n");
+    gprint (GP_ERR, "  nmiss : number of non-detections\n");
+    gprint (GP_ERR, "  xp : positional chi-square\n");
+    gprint (GP_ERR, "  objflag : object flags\n");
+    gprint (GP_ERR, "  photcode:ave : average magnitude for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:ref : reference magnitude system for photcode (or equivalent)\n");
+    gprint (GP_ERR, "  photcode:inst : instrumental magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cat :  catalog magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:sys :  system magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:rel :  relative magnitude for photcode\n");
+    gprint (GP_ERR, "  photcode:cal :  calibrated magnitude for photcode \n");
+    gprint (GP_ERR, "  photcode:err : magnitude error for photcode\n");
+    gprint (GP_ERR, "  photcode:chisq : chi-square of magnitude fit\n");
+    gprint (GP_ERR, "  photcode:ncode : number of measurements in photcode\n");
+    gprint (GP_ERR, "  photcode:nphot : number of measurements used for average magnitude\n");
+    gprint (GP_ERR, "  airmass : airmass of detection\n");
+    gprint (GP_ERR, "  exptime : exposure time\n");
+    gprint (GP_ERR, "  photcode : photcode \n");
+    gprint (GP_ERR, "  time : time of exposure\n");
+    gprint (GP_ERR, "  dR : ra offset\n");
+    gprint (GP_ERR, "  dD : dec offset\n");
+    gprint (GP_ERR, "  fwhm : fwhm (average)\n");
+    gprint (GP_ERR, "  fwhm_maj : fwhm (major axis)\n");
+    gprint (GP_ERR, "  fwhm_min : fwhm (minor axis)\n");
+    gprint (GP_ERR, "  theta : position angle\n");
+    gprint (GP_ERR, "  flags : detection flags\n");
+    gprint (GP_ERR, "  xccd : ccd x position\n");
+    gprint (GP_ERR, "  yccd : ccd y position\n");
+    gprint (GP_ERR, "  xmosaic : mosaic x position\n");
+    gprint (GP_ERR, "  ymosaic : mosaic y position\n");
+    gprint (GP_ERR, "  xchip : chip x position\n");
+    gprint (GP_ERR, "  ychip : chip y position\n");
+    gprint (GP_ERR, "  xfpa : fpa x position\n");
+    gprint (GP_ERR, "  yfpa : fpa y position\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mextract --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/notes.txt	(revision 17422)
@@ -0,0 +1,10 @@
+
+avextract ra,dec,g,r,i where (ra > 5) && (dec < 3)
+
+
+U db_check_stack.c
+U db_convert_to_RPN.c
+U dvoBooleanElements.c
+U dvoEvaluateStack.c
+U dvodb.c
+U dvofields.c
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/paverage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/paverage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/paverage.c	(revision 17422)
@@ -0,0 +1,157 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+int paverage (int argc, char **argv) {
+  
+  FILE *f;
+  int i, j, kapa, Narg, Npts, NPTS, status, VERBOSE;
+  int Nsecfilt, Nsec;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D;
+  unsigned IDclip, IDchoice, LimExclude;
+  float *Xvec, *Yvec, *Zvec;
+
+  PhotCode *photcode;
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+  Average *average;
+  SecFilt *secfilt;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  // require a photcode?  default to 0?
+  Nsec = 0;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    photcode = GetPhotcodebyName (argv[Narg]);
+    if (!photcode) {
+	fprintf (stderr, "unknown photcode %s\n", argv[Narg]);
+	return (FALSE);
+    }
+    remove_argument (Narg, &argc, argv);
+    Nsec = GetPhotcodeNsec (photcode[0].code);
+    if (Nsec == -1) {
+	fprintf (stderr, "photcode %s is not an AVERAGE photcode\n", argv[Narg]);
+	return (FALSE);
+    }
+  }
+
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = atoi(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: paverage (-all) [-m M M] [-p photcode] [-ID ID] [-flag value] [-x]\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_SECF;
+    catalog.Nsecfilt = Nsecfilt;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    average = catalog.average;
+    secfilt = catalog.secfilt;
+
+    /* project stars to screen display coords */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (IDclip && (average[i].code != IDchoice)) continue;
+      while (average[i].R < Rmin) average[i].R += 360.0;
+      while (average[i].R > Rmax) average[i].R -= 360.0;
+
+      mag = secfilt[i*Nsecfilt+Nsec].M;
+      Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+      if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+      if (Zvec[Npts] < 0.011) continue;
+      R = average[i].R;
+      D = average[i].D;
+      status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+      if (!status) {
+	  fprintf (stderr, ".");
+	  continue;
+      }
+      Npts ++;
+
+      if (Npts == NPTS - 1) {
+	  NPTS += 1000;
+	  REALLOCATE (Xvec, float, NPTS);
+	  REALLOCATE (Yvec, float, NPTS);
+	  REALLOCATE (Zvec, float, NPTS);
+      }
+      if (Npts > NCHUNK) {
+	  PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+	  Npts = 0;
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pcat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pcat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pcat.c	(revision 17422)
@@ -0,0 +1,111 @@
+# include "dvoshell.h"
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside);
+
+// XXX EAM : this function is deprecated (now points at skycat)
+int pcat (int argc, char **argv) {
+  
+  double Radius;
+  int i, j, kapa, N, Nregions, ShowAll, NPTS, Npts, leftside;
+  RegionFile *regions;
+  char filename[128];
+  struct stat filestat;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+  double X[4], Y[4], Rmin, Rmax, Rmid;
+  char catdir[256];
+  int VERBOSE;
+
+  VarConfig ("CATDIR", "%s", catdir);
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: pcat [-all]\n");
+    return (FALSE);
+  }
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+  
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+  regions = find_regions (graphmode.coords.crval1, graphmode.coords.crval2, Radius, &Nregions);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  Npts = 0;
+   
+  for (i = 0; i < Nregions; i++) {
+    sprintf (filename, "%s/%s", catdir, regions[i].name);
+    if (ShowAll || (stat (filename, &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s\n", i, regions[i].name);
+
+      leftside = -1;
+      RD_to_XYpic (&X[0], &Y[0], regions[i].RA0, regions[i].DEC0, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[1], &Y[1], regions[i].RA0, regions[i].DEC1, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[2], &Y[2], regions[i].RA1, regions[i].DEC1, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[3], &Y[3], regions[i].RA1, regions[i].DEC0, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+
+      Xvec.elements[Npts] = X[0];
+      Yvec.elements[Npts] = Y[0];
+      for (j = 1; j < 4; j++) {
+	Xvec.elements[Npts + j*2 - 0] = X[j];
+	Yvec.elements[Npts + j*2 - 0] = Y[j];
+	Xvec.elements[Npts + j*2 - 1] = X[j];
+	Yvec.elements[Npts + j*2 - 1] = Y[j];
+      }
+      Xvec.elements[Npts+7] = Xvec.elements[Npts];
+      Yvec.elements[Npts+7] = Yvec.elements[Npts];
+      Npts += 8;
+      if (Npts > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d catalogs\n", Npts/8);
+  Xvec.Nelements = Xvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (regions);
+
+  return (TRUE);
+
+}
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside) {
+
+  while (r < Rmin) { r += 360.0; }
+  while (r > Rmax) { r -= 360.0; }
+
+  if (*leftside == -1) {
+    *leftside = (r < Rmid);
+  } else {
+    if (  *leftside && (r > Rmid + 90)) { r -= 360.0; }
+    if (! *leftside && (r < Rmid - 90)) { r += 360.0; }
+  }
+
+  RD_to_XY (x, y, r, d, coords);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photcodes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photcodes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photcodes.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "dvoshell.h"
+
+/* list or return all photcodes equivalent to the given filter */
+int photcodes (int argc, char **argv) {
+  
+  int i, Np;
+  int *list, Nlist;
+  char name[64];
+  PhotCode *code;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: photcodes (photcode)\n");
+    return (FALSE);
+  }
+
+  /* load photcodes, convert name to code */
+  if (!InitPhotcodes ()) return (FALSE);
+
+  if (!(Np = GetPhotcodeCodebyName (argv[1]))) {
+    gprint (GP_ERR, "ERROR: photcode not found in photcode table\n");
+    return (FALSE);
+  }
+
+  list = GetPhotcodeEquivList (Np, &Nlist);
+  
+  for (i = 0; i < Nlist; i++) {
+    code = GetPhotcodebyCode (list[i]);
+
+    sprintf (name, "photcode:name:%d", i);
+    set_str_variable (name, code[0].name);
+
+    sprintf (name, "photcode:C:%d", i);
+    set_variable (name, 0.001*code[0].C);
+
+    sprintf (name, "photcode:K:%d", i);
+    set_variable (name, code[0].K);
+
+    sprintf (name, "photcode:X:%d", i);
+    set_variable (name, code[0].X[0]);
+
+    sprintf (name, "photcode:dX:%d", i);
+    set_variable (name, 0.001*code[0].dX);
+
+    sprintf (name, "photcode:code:%d", i);
+    set_int_variable (name, code[0].code);
+
+    sprintf (name, "photcode:filter:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].equiv));
+
+    sprintf (name, "photcode:c1:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c1));
+
+    sprintf (name, "photcode:c2:%d", i);
+    set_str_variable (name, GetPhotcodeNamebyCode (code[0].c2));
+
+    gprint (GP_ERR, "%5d %s %7.4f %7.4f %7.4f\n", 
+	     code[0].code, code[0].name, 0.001*code[0].C, code[0].K, code[0].X[0]);
+  }
+  set_int_variable ("photcode:n", Nlist);
+  free (list);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photometry.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photometry.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/photometry.c	(revision 17422)
@@ -0,0 +1,1220 @@
+# include "dvoshell.h"
+
+/* match code to measure  */
+# define TESTCODE(C,M) \
+  if (C != NULL) { \
+    switch (C[0].type) { \
+    case PHOT_DEP: \
+    case PHOT_REF: \
+      if (C[0].code != M.photcode) continue; \
+      break; \
+    case PHOT_SEC: \
+      if (C[0].code != GetPhotcodeEquivCodebyCode (M.photcode)) continue; \
+      break; \
+    default: \
+      break; \
+  } }
+
+/* exclusions based on measure.params  */
+# define TESTMEASURE(M) \
+  if (ApplySelections[SelectionParam]) { \
+    if (TimeSelect && (M.t < tzero)) continue; \
+    if (TimeSelect && (M.t > tend)) continue; \
+    if (ErrSelect  && (M.dM > ErrValue)) continue; \
+    if (TypeSelect && (TypeValue != GetMeasureTypeCode (&M))) continue; \
+    if (iMagSelect && (PhotInst (&M) < iMagMin)) continue; \
+    if (FlagSelect && (M.dbFlags != FlagValue)) continue; \
+  }
+
+# define SETMAG(MOUT,MEAS,MODE) \
+  MOUT = NAN; \
+  if (MODE == MAG_INST) MOUT = PhotInst (&MEAS);  \
+  if (MODE == MAG_CAT)  MOUT = PhotCat  (&MEAS); \
+  if (MODE == MAG_SYS)  MOUT = PhotSys  (&MEAS, average, secfilt); \
+  if (MODE == MAG_REL)  MOUT = PhotRel  (&MEAS, average, secfilt); \
+  if (MODE == MAG_CAL)  MOUT = PhotCal  (&MEAS, average, secfilt, measure, GetPhotcodeEquivbyCode (MEAS.photcode)); \
+  if (MODE == MAG_AVE)  MOUT = PhotAve  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt); \
+  if (MODE == MAG_REF)  MOUT = PhotRef  (GetPhotcodeEquivbyCode (MEAS.photcode), average, secfilt, measure); \
+  if (ApplySelections[SelectionParam]) { \
+    if (MagSelect && (MOUT > MagMax)) continue; \
+    if (MagSelect && (MOUT < MagMin)) continue; \
+  }
+
+/* selection criteria */
+/* selections based on Measure quantities */
+static int TimeSelect;
+static time_t tzero, tend;
+static int MagSelect;
+static double MagMax, MagMin;
+static int TypeSelect, TypeValue;
+static int ErrSelect;
+static double ErrValue;
+static int iMagSelect;
+static double iMagMin;
+static int FlagSelect, FlagValue;
+static int TypefracSelect, TypefracType, TypefracSign;
+static double TypefracValue;
+
+/* apply selections or not */
+static int ApplySelections[4];
+static int SelectionParam;
+
+/* applied to Average quantities */
+static int PhotcodeSelect;
+static PhotCode *PhotcodeValue;
+static int PhotcodeMode;
+
+/* selections based on Average quantities */
+static int ChiSelect;
+static float ChiLimit;
+
+/* selections based on ensemble quantities */
+static int NphotSelect, NphotSign, NphotValue;
+static int NcodeSelect, NcodeSign, NcodeValue;
+static int FWHMSelect, FWHMsign;
+static double FWHMvalue, FWHMfrac;
+
+/* time concepts */
+static time_t TimeReference;
+static int TimeFormat;
+
+int GetTimeSelection (time_t *tz, time_t *te) {
+  *tz = tzero;
+  *te = tend;
+  return (TimeSelect);
+}
+
+int GetPhotcodeInfo (char *string, PhotCode **Code, int *Mode) {
+
+  PhotCode *code;
+  int mode, status;
+  char *p, *tmpstring;
+
+  /* save local copy */
+  tmpstring = strcreate (string);
+
+  /* check for code:mode in photcode name */
+  mode = MAG_NONE;
+  p = strchr (tmpstring, ':');
+  if (p != NULL) {
+    mode = GetMagMode (p + 1);
+    if (mode == MAG_NONE) {
+      gprint (GP_ERR, "syntax error in magnitude mode\n");
+      free (tmpstring);
+      return (FALSE);
+    }
+    *p = 0;
+  }
+
+  /* how do we handle this elsewhere? */
+  if (!strcasecmp (tmpstring, "mag")) {
+    /* need to validate mode */
+    *Mode = mode;
+    *Code = NULL;
+    free (tmpstring);
+    return (TRUE);
+  }
+
+  code = GetPhotcodebyName (tmpstring);
+  if (code == NULL) {
+    gprint (GP_ERR, "photcode not found in photcode table\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+
+  /* test allowable cases and/or set default values */
+  status = FALSE;
+  if (code[0].type == PHOT_DEP) {
+    if (mode == MAG_NONE) mode = MAG_REL;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_SEC) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_INST) status = TRUE;
+    if (mode == MAG_CAT)  status = TRUE;
+    if (mode == MAG_SYS)  status = TRUE;
+    if (mode == MAG_REL)  status = TRUE;
+    if (mode == MAG_CAL)  status = TRUE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }  
+  if (code[0].type == PHOT_ALT) {
+    if (mode == MAG_NONE) mode  = MAG_AVE;
+    if (mode == MAG_AVE)  status = TRUE;
+    if (mode == MAG_REF)  status = TRUE;
+  }
+
+  if (code[0].type == PHOT_REF) {
+    if (mode == MAG_NONE) mode  = MAG_CAT;
+    if (mode == MAG_CAT)  status = TRUE;
+  }
+
+  if (!status) {
+    gprint (GP_ERR, "mismatch in photcode and magmode\n");
+    free (tmpstring);
+    return (FALSE);
+  }
+  *Code = code;
+  *Mode = mode;
+  free (tmpstring);
+  return (TRUE);
+}
+ 
+int SetSelectionParam (int param) {
+  SelectionParam = param;
+  return (TRUE);
+}
+
+int GetSelectionParam () {
+  return (SelectionParam);
+}
+
+int GetMeasureParam (char *parname) {
+
+  int param;
+
+  param = MEAS_ZERO;
+  if (!strcasecmp (parname, "ra"))   	 param = MEAS_RA;
+  if (!strcasecmp (parname, "dec"))  	 param = MEAS_DEC;
+  if (!strcasecmp (parname, "mag")) 	 param = MEAS_MAG;
+  // if (!strcasecmp (parname, "dmag")) 	 param = MEAS_dMAG;
+  if (!strcasecmp (parname, "airmass"))  param = MEAS_AIRMASS;
+  if (!strcasecmp (parname, "exptime"))  param = MEAS_EXPTIME;
+  if (!strcasecmp (parname, "photcode")) param = MEAS_PHOTCODE;
+  if (!strcasecmp (parname, "time"))     param = MEAS_TIME;
+  if (!strcasecmp (parname, "dR")) 	 param = MEAS_RA_OFFSET;
+  if (!strcasecmp (parname, "dD")) 	 param = MEAS_DEC_OFFSET;
+  if (!strcasecmp (parname, "fwhm"))   	 param = MEAS_FWHM;
+  // if (!strcasecmp (parname, "dophot")) 	 param = MEAS_DOPHOT;
+  if (!strcasecmp (parname, "FLAGS"))    param = MEAS_DB_FLAGS;
+  if (!strcasecmp (parname, "XCCD"))   	 param = MEAS_XCCD;
+  if (!strcasecmp (parname, "YCCD"))   	 param = MEAS_YCCD;
+  if (!strcasecmp (parname, "XMOSAIC"))  param = MEAS_XMOSAIC;
+  if (!strcasecmp (parname, "YMOSAIC"))  param = MEAS_YMOSAIC;
+  if (!strcasecmp (parname, "help")) {
+    gprint (GP_ERR, "value may be one of the following:\n");
+    gprint (GP_ERR, " ra dR dec dD mag dmag Mrel Mcal photcode time fwhm dophot xccd yccd xmosaic ymosaic flags\n");
+    gprint (GP_ERR, "value may also be a valid photcode\n");
+    gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude type: mag,[Minst, Mcat, Msys, Mrel, Mcal]\n");
+  }
+  return (param);
+}
+  
+int GetAverageParam (char *parname) {
+
+  int param;
+
+  param = AVE_ZERO;
+  if (!strcasecmp (parname, "RA"))    param = AVE_RA;
+  if (!strcasecmp (parname, "DEC"))   param = AVE_DEC;
+
+  if (!strcasecmp (parname, "dRA"))   param = AVE_RA_ERR;
+  if (!strcasecmp (parname, "dDEC"))  param = AVE_DEC_ERR;
+
+  if (!strcasecmp (parname, "uRA"))   param = AVE_U_RA;
+  if (!strcasecmp (parname, "uDEC"))  param = AVE_U_DEC;
+  if (!strcasecmp (parname, "duRA"))  param = AVE_U_RA_ERR;
+  if (!strcasecmp (parname, "duDEC")) param = AVE_U_DEC_ERR;
+
+  if (!strcasecmp (parname, "par"))   param = AVE_PAR;
+  if (!strcasecmp (parname, "dpar"))  param = AVE_PAR_ERR;
+
+  if (!strcasecmp (parname, "dmag"))  param = AVE_dMAG;
+  if (!strcasecmp (parname, "mag"))   param = AVE_MAG;
+  if (!strcasecmp (parname, "Nmeas")) param = AVE_NMEAS;
+  if (!strcasecmp (parname, "Nmiss")) param = AVE_NMISS;
+  if (!strcasecmp (parname, "Xp"))    param = AVE_Xp;
+  if (!strcasecmp (parname, "Xm"))    param = AVE_Xm;
+  if (!strcasecmp (parname, "flag"))  param = AVE_FLAG;
+  if (!strcasecmp (parname, "type"))  param = AVE_TYPE;
+  if (!strcasecmp (parname, "typefrac")) {
+    if (!TypefracType) {
+      gprint (GP_ERR, "typefrac needs to specify type to use\n");
+      return (param);
+    }
+    param = AVE_TYPEFRAC;
+  }
+  if (!strcasecmp (parname, "Nphot")) param = AVE_NPHOT;
+  if (!strcasecmp (parname, "Ncode")) param = AVE_NCODE;
+  // if (!strcasecmp (parname, "Ncrit")) param = AVE_NCRIT;
+  return (param);
+}
+
+/* I've set some selections - if these require a photcode, check if I set one */
+int TestPhotSelections (PhotCode **code, int *mode, int param) {
+
+  int NeedPhotcode, Needcode;
+
+  /* if i've supplied a photcode (code != NULL), i'm not allowed to restrict it */
+  if (code[0] != NULL) {
+    if (PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection rules violated: cannot restrict photcode with a photcode\n");
+      return (FALSE);
+    } else {
+      return (TRUE);
+    }
+  }
+
+  /* for measure tests, supply MEAS_ZERO */
+
+  /* if I have an average or ensemble restriction, I must have a PRI/SEC photcode */
+  NeedPhotcode = FALSE;
+  NeedPhotcode |= ChiSelect;
+  NeedPhotcode |= NphotSelect;
+  NeedPhotcode |= ErrSelect;
+  NeedPhotcode |= TypeSelect;
+  NeedPhotcode |= TypefracSelect;
+  
+  NeedPhotcode |= (param == AVE_Xm);
+  NeedPhotcode |= (param == AVE_MAG);
+  NeedPhotcode |= (param == AVE_dMAG);
+  NeedPhotcode |= (param == AVE_TYPE);
+  NeedPhotcode |= (param == AVE_NPHOT);
+  Needcode = (param == AVE_NCODE);
+
+  if (NeedPhotcode || Needcode || NcodeSelect || PhotcodeSelect) {
+    if (!PhotcodeSelect) {
+      gprint (GP_ERR, "photcode selection problem: value requires photcode\n");
+      return (FALSE);
+    }
+    code[0] = PhotcodeValue;
+    mode[0] = PhotcodeMode;
+  }
+  if (NeedPhotcode) {
+    if (code[0][0].type == PHOT_SEC) return (TRUE);
+    if (code[0][0].type == PHOT_REF) return (TRUE);
+    gprint (GP_ERR, "photcode selection problem: average value requires average photcode\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void GetAverageParamHelp () {
+  gprint (GP_ERR, "value may be one of the following:\n");
+  gprint (GP_ERR, " ra dec dmag Nmeas Nmiss Xm Xp Nphot Ncode flag type typefrac\n\n");
+  gprint (GP_ERR, "value may also be a valid photcode\n");
+  gprint (GP_ERR, "photcodes or 'mag' may have optional magnitude mode: mag,[Mave, Mref]\n");
+  return;
+}
+
+/* (re)load photcodes from photcode table */
+int InitPhotcodes () {
+
+  double ZERO_POINT;
+  char MasterPhotcodeFile[256];
+  char CatdirPhotcodeFile[256];
+  char *catdir;
+
+  if (VarConfig ("ZERO_PT", "%lf", &ZERO_POINT) == (char *) NULL) {
+      gprint (GP_ERR, "ZERO_PT undefined in config\n");
+      return (FALSE);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  catdir = GetCATDIR();
+  if (catdir == NULL) {
+    CatdirPhotcodeFile[0] = 0;
+  } else {
+    sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", catdir);
+  }
+
+  if (VarConfig ("PHOTCODE_FILE", "%s", MasterPhotcodeFile) == (char *) NULL) {
+      gprint (GP_ERR, "PHOTCODE_FILE undefined in config\n");
+      return (FALSE);
+  }
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    gprint (GP_ERR, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+int ListPhotSelections () {
+
+  gprint (GP_ERR, "TimeSelect: %d, %d - %d\n",      TimeSelect, tzero, tend);
+  gprint (GP_ERR, "MagSelect: %d, %f - %f\n",       MagSelect, MagMax, MagMin);
+  gprint (GP_ERR, "TypeSelect: %d, %d\n",           TypeSelect, TypeValue);
+  gprint (GP_ERR, "ErrSelect: %d, %f\n",            ErrSelect, ErrValue);
+  gprint (GP_ERR, "iMagSelect: %d, %f\n",           iMagSelect, iMagMin);
+  gprint (GP_ERR, "FlagSelect: %d, %x\n",           FlagSelect, FlagValue);
+  gprint (GP_ERR, "TypefracSelect: %d, %d %d %f\n", TypefracSelect, TypefracType, TypefracSign, TypefracValue);
+  gprint (GP_ERR, "ApplySelections: %d,%d,%d,%d : %d\n", ApplySelections[0], ApplySelections[1], ApplySelections[2], ApplySelections[3], SelectionParam);
+  if (PhotcodeSelect) {
+    gprint (GP_ERR, "PhotcodeSelect: %d, %s\n",       PhotcodeSelect, PhotcodeValue[0].name);
+  } else {
+    gprint (GP_ERR, "PhotcodeSelect: %d, none\n",       PhotcodeSelect);
+  }
+  gprint (GP_ERR, "ChiSelect: %d, %f\n",            ChiSelect, ChiLimit);
+  gprint (GP_ERR, "NphotSelect: %d, %d - %d\n",     NphotSelect, NphotSign, NphotValue);
+  gprint (GP_ERR, "NcodeSelect: %d, %d - %d\n",     NcodeSelect, NcodeSign, NcodeValue);
+  gprint (GP_ERR, "FWHMSelect: %d, %d %f %f\n",     FWHMSelect, FWHMsign, FWHMvalue, FWHMfrac);
+  return (TRUE);
+}
+
+/* remove standard photometry filtering options, set selections */
+/* not all functions respect all selections... */
+int SetPhotSelections (int *argc, char **argv, int Nparams) {
+
+  int i, N;
+  double trange;
+
+  if ((N = get_argument (*argc, argv, "-phothelp"))) {
+    gprint (GP_ERR, "optional photometry selection criteria:\n");
+    gprint (GP_ERR, " -magrange min max\n");
+    gprint (GP_ERR, " -imaglim min\n");
+    gprint (GP_ERR, " -flag value\n");
+    gprint (GP_ERR, " -chisq value\n");
+    gprint (GP_ERR, " -photcode code\n");
+    gprint (GP_ERR, " -time start range\n");
+    gprint (GP_ERR, " -errorlim value\n");
+    gprint (GP_ERR, " -type type\n");
+    gprint (GP_ERR, " -nmeas [+/-]N\n");
+    gprint (GP_ERR, " -fwhm [+/-]fraction\n");
+    return (FALSE);
+  }
+
+  /* select based on measured mag (MEASURE ONLY) */
+  MagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-magrange"))) {
+    MagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    MagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    MagMax = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select based on instrument mag (MEASURE ONLY) */
+  iMagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-imaglim"))) {
+    iMagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    iMagMin = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of flag (MEASURE ONLY) */
+  FlagSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-flag"))) {
+    FlagSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FlagValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  SelectionParam = 0;
+  for (i = 0; i < 4; i++) ApplySelections[i] = TRUE;
+  if ((N = get_argument (*argc, argv, "-apply"))) {
+    remove_argument (N, argc, argv);
+    if (strlen(argv[N]) != Nparams) {
+      gprint (GP_ERR, "-apply selection must define all parameter choices\n");
+      return (FALSE);
+    }
+    for (i = 0; i < Nparams; i++) {
+      if (toupper(argv[N][i]) == 'Y') {
+	ApplySelections[i] = TRUE;
+      } else {
+	ApplySelections[i] = FALSE;
+      }
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on value of photcode */
+  PhotcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-photcode"))) {
+    PhotcodeSelect = TRUE;
+    remove_argument (N, argc, argv);
+    GetPhotcodeInfo (argv[N], &PhotcodeValue, &PhotcodeMode);
+    if (PhotcodeValue == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);;
+    }
+    remove_argument (N, argc, argv);
+  }
+
+  /* selection on basis of time range (MEASURE only) */
+  TimeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, argc, argv);
+    TimeSelect = TRUE;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tend = tzero;
+      tzero -= trange;
+    } else {
+      tend = tzero + trange;
+    }
+  }
+
+  /* select by error (on measure or average) */
+  ErrSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-errorlim"))) {
+    remove_argument (N, argc, argv);
+    ErrValue = atof (argv[N]);
+    remove_argument (N, argc, argv);
+    ErrSelect = TRUE;
+  }
+
+  /* select on value of Chisq (AVERAGE ONLY) */
+  ChiSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-chisq"))) {
+    ChiSelect = TRUE;
+    remove_argument (N, argc, argv);
+    ChiLimit = atof (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-type"))) {
+    remove_argument (N, argc, argv);
+    TypeValue = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypeSelect = TRUE;
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracType = 0;
+  if ((N = get_argument (*argc, argv, "-usetype"))) {
+    remove_argument (N, argc, argv);
+    TypefracType = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  /* select on measurement type: 1,2,3 (AVERAGE ONLY) */
+  TypefracSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-typefrac"))) {
+    remove_argument (N, argc, argv);
+    TypefracType  = atoi (argv[N]);
+    remove_argument (N, argc, argv);
+    TypefracValue = fabs (atof (argv[N]));
+    TypefracSign = 0;
+    if (argv[N][0] == '-') TypefracSign = -1;
+    if (argv[N][0] == '+') TypefracSign = +1;
+    remove_argument (N, argc, argv);
+    TypefracSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NphotSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-nphot"))) {
+    remove_argument (N, argc, argv);
+    NphotValue = abs (atoi (argv[N]));
+    NphotSign = 0;
+    if (argv[N][0] == '-') NphotSign = -1;
+    if (argv[N][0] == '+') NphotSign = +1;
+    remove_argument (N, argc, argv);
+    NphotSelect = TRUE;
+  }
+
+  /* select by number of measurements (AVERAGE ONLY) */
+  NcodeSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-ncode"))) {
+    remove_argument (N, argc, argv);
+    NcodeValue = abs (atoi (argv[N]));
+    NcodeSign = 0;
+    if (argv[N][0] == '-') NcodeSign = -1;
+    if (argv[N][0] == '+') NcodeSign = +1;
+    remove_argument (N, argc, argv);
+    NcodeSelect = TRUE;
+  }
+
+  /* -fwhm value frac (AVERAGE ONLY) */
+  FWHMSelect = FALSE;
+  if ((N = get_argument (*argc, argv, "-fwhm"))) {
+    remove_argument (N, argc, argv);
+    FWHMvalue = abs (atof (argv[N]));
+    FWHMsign = 0;
+    if (argv[N][0] == '-') FWHMsign = -1;
+    if (argv[N][0] == '+') FWHMsign = +1;
+    remove_argument (N, argc, argv);
+    FWHMSelect = TRUE;
+    remove_argument (N, argc, argv);
+    FWHMfrac = atof (argv[N]);
+  }
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+  return (TRUE);
+}
+
+/* extract a list of measure parameters from the specified average entry based on the pre-set selections */
+double *ExtractMeasures (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int i, Nlist, NLIST;
+  double M, *list;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* check selections based on averages & ensembles: chisq, Nphot, etc */
+  if (!TestAverage (code, average, secfilt, measure)) return (list); 
+
+  /* look for measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code, measure[i]);  /* skip measurements not matching photcode */
+    TESTMEASURE (measure[i]);     /* exclusions based on measure.params  */
+    SETMAG (M, measure[i], mode); /* set appropriate magnitude (also does MagSelect) */ 
+
+    /* assign value */
+    list[Nlist] = GetMeasure (param, &average[0], &measure[i], M);
+    Nlist ++;
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* return average.param based on the selection */
+double ExtractAverages (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param) {
+
+  int i;
+  double value;
+
+  value = NAN;
+
+  /* this function requires code set for certain value of param.  
+     use TestPhotSelectionsAverage to validate code/param choices */
+
+  /* filter by average quantities (eg, chisq, Nphot, etc) */
+  if (!TestAverage (code, average, secfilt, measure)) return (NAN);
+
+  /* assign vector values */
+  switch (param) {
+    case AVE_RA:
+      value = average[0].R;
+      break;
+    case AVE_DEC:
+      value = average[0].D;
+      break;
+    case AVE_RA_ERR:
+      value = average[0].dR;
+      break;
+    case AVE_DEC_ERR:
+      value = average[0].dD;
+      break;
+
+    case AVE_U_RA:
+      value = average[0].uR;
+      break;
+    case AVE_U_DEC:
+      value = average[0].uD;
+      break;
+    case AVE_U_RA_ERR:
+      value = average[0].duR;
+      break;
+    case AVE_U_DEC_ERR:
+      value = average[0].duD;
+      break;
+
+    case AVE_PAR:
+      value = average[0].P;
+      break;
+    case AVE_PAR_ERR:
+      value = average[0].dP;
+      break;
+
+
+    case AVE_NMEAS:
+      value = average[0].Nmeasure;
+      break;
+    case AVE_NMISS:
+      value = average[0].Nmissing;
+      break;
+    case AVE_Xp:
+      value = 0.01*average[0].Xp;
+      break;
+    case AVE_FLAG:
+      value = average[0].code;
+      break;
+    case AVE_MAG:
+      switch (mode) {
+	case MAG_AVE:
+	  value = PhotAve  (code, average, secfilt);
+	  break;
+	case MAG_REF:
+	  value = PhotRef  (code, average, secfilt, measure);
+	  break;
+	case MAG_INST:
+	case MAG_CAT:
+	case MAG_SYS:
+	case MAG_REL:
+	case MAG_CAL:
+	  value = NAN;
+	  for (i = 0; i < average[0].Nmeasure; i++) {
+	      if (code[0].code != measure[i].photcode) continue;
+	      value = measure[i].M;
+	  }
+	  break;
+      }
+      break;
+    case AVE_dMAG:
+      value = PhotdM (code, average, secfilt);
+      break;
+    case AVE_Xm:
+      value = PhotXm (code, average, secfilt);
+      break;
+    case AVE_TYPE:
+      value = DetermineTypeCode (average, measure, code[0].code);
+      break;
+    case AVE_TYPEFRAC:
+      value = DetermineTypefrac (average, measure, code);
+      break;
+    case AVE_NCODE:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	value ++;
+      }
+      break;
+    case AVE_NPHOT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode)) continue;
+	if (measure[i].dbFlags & (ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM)) continue;
+	value ++;
+      }
+      break;
+# if (0)
+    case AVE_NCRIT:
+      value = 0;
+      for (i = 0; i < average[0].Nmeasure; i++) {
+	if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[i].photcode))) continue;
+	if (ErrSelect && (measure[i].dM > ErrValue)) continue;
+	if (FlagSelect && (measure[i].dbFlags != FlagValue)) continue;
+	if (TypeSelect && (TypeValue != GetMeasureTypeCode (&measure[i]))) continue;
+	if (iMagSelect && (PhotInst (&measure[i]) < iMagMin)) continue;
+	value ++;
+      }
+      break;
+# endif
+  }
+  return (value);
+}  
+
+/* return fraction of measures (matching code) which have requested type */
+double DetermineTypefrac (Average *average, Measure *measure, PhotCode *code) {
+
+  double frac;
+  int k, Nc, Nt;
+  
+  Nt = Nc = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if ((code != NULL) && (code[0].code != GetPhotcodeEquivCodebyCode (measure[k].photcode))) continue;
+    Nc ++;
+    if (measure[k].dophot != TypefracType) continue;
+    Nt ++;
+  }
+  frac = (double) Nt / (double) Nc;
+  return (frac);
+}
+
+/* determine the representative dophot type for this photcode (must be PRI/SEC) */
+int DetermineTypeCode (Average *average, Measure *measure, int code) {
+
+  int k, N, Nt[3];
+  
+  Nt[0] = Nt[1] = Nt[2] = 0;
+  for (k = 0; k < average[0].Nmeasure; k++) {
+    if (code != GetPhotcodeEquivCodebyCode (measure[k].photcode)) continue;
+    N = GetMeasureTypeCode (&measure[k]);
+    Nt[N] ++;
+  }
+  if (Nt[0]) return (0);
+  if (Nt[1]) return (1);
+  if (Nt[2]) return (2);
+  return (3);
+}
+
+int GetMeasureTypeCode (Measure *measure) {
+  switch (measure[0].dophot) {
+    case 0:
+    case 1:
+    case 2:
+      return (0);
+      break;
+    case 3:
+    case 4:
+    case 5:
+    case 7:
+    case 9:
+      return (1);
+      break;
+    case 10:
+    default:
+      return (2);
+  }
+  return (2);
+}  
+
+int Quality (Measure *measure, int IsDophot) {
+
+  return (TRUE);
+  
+  if (IsDophot) {
+    
+    if (measure[0].dophot == 4) return (FALSE);
+    return (TRUE);
+  
+  } else {
+    
+    if (0.01 * measure[0].FWx < 3.0) return (FALSE);
+    if (0.01 * measure[0].FWx > 10.0) return (FALSE);
+
+    return (TRUE);
+
+  }
+}
+
+/* test if this average object meets the specified selection criteria.
+   for photcode-dependent quantities, only test for PRI/SEC photcodes */
+int TestAverage (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Nm, Type, Select;
+  double fwhm, typefrac, dM, Xm;
+
+  /** temporary special case for Jen Katz: exclude REF with Ncode > 1 */
+  if ((code != NULL) && (code[0].type == PHOT_REF)) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    if (Nm > 1) return (FALSE);
+  }
+
+  if (!ApplySelections[SelectionParam]) return (TRUE);
+
+  /* pass objects with more than FWHMfrac points with FWHM above / below FWHMvalue */ 
+  if (FWHMSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      fwhm = measure[i].FWx / 100.0;
+      switch (FWHMsign) {
+	case 0:
+	  if (fwhm == FWHMvalue) break;
+	  continue;
+	case +1:
+	  if (fwhm >= FWHMvalue) break;
+	  continue;
+	case -1:
+	  if (fwhm <= FWHMvalue) break;
+	  continue;
+      }
+      Nm++;
+    }
+    if (average[0].Nmeasure * FWHMfrac > Nm) return (FALSE);
+  }
+
+  /* all selections below require a valid photcode */
+  Select = ChiSelect || ErrSelect || NcodeSelect || NphotSelect || TypeSelect || TypefracSelect;
+  if (!Select) return (TRUE);
+
+  /* must have a valid code of some kind */
+  if (code == NULL) return (FALSE);
+
+  /* for NcodeSelect, count Nmeas for appropriate photcode */
+  if (NcodeSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      Nm++;
+    }
+    switch (NcodeSign) {
+      case 0:
+	if (Nm == NcodeValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NcodeValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NcodeValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* only PRI/SEC photcodes apply the filter */
+  if (code[0].type == PHOT_DEP) return (TRUE);
+  if (code[0].type == PHOT_REF) return (TRUE);
+
+  /* exclusions based on average.params  */
+  if (ChiSelect) {
+    Xm = PhotXm (code, average, secfilt);
+    if (Xm == -1) return (FALSE);
+    if (Xm > ChiLimit) return (FALSE);
+  }
+  
+  /* for ErrSelect, check average errors */
+  if (ErrSelect) {
+    dM = PhotdM (code, average, secfilt);
+    if (dM > ErrValue) return (NAN);
+  }
+  
+  /* for NphotSelect, count Nmeas for appropriate photcode */
+  if (NphotSelect) {
+    Nm = 0;
+    for (i = 0; i < average[0].Nmeasure; i++) {
+      TESTCODE (code, measure[i]);
+      if (measure[i].dbFlags && ID_MEAS_SKIP_PHOTOM) continue;
+      Nm++;
+    }
+    switch (NphotSign) {
+      case 0:
+	if (Nm == NphotValue) break;
+	return (FALSE);
+      case 1:
+	if (Nm >= NphotValue) break;
+	return (FALSE);
+      case -1:
+	if (Nm <= NphotValue) break;
+	return (FALSE);
+      default:
+	return (FALSE);
+    }
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypeSelect) {
+    Type = DetermineTypeCode (average, measure, code[0].code);
+    if (Type != TypeValue) return (FALSE);
+  }
+
+  /* for TypeSelect, check on TypeCode for this object */
+  if (TypefracSelect) {
+    typefrac = DetermineTypefrac (average, measure, code);
+    switch (TypefracSign) {
+      case 0:
+	if (typefrac == TypefracValue) break;
+	return (FALSE);	
+      case +1:
+	if (typefrac >= TypefracValue) break;
+	return (FALSE);	
+      case -1:
+	if (typefrac <= TypefracValue) break;
+	return (FALSE);	
+      default:
+	return (FALSE);
+    }
+  }
+
+  return (TRUE);
+}
+
+/* for this function, we don't need to call PhotRel, etc, but we
+   do need to multiply by 0.001:
+   average[].M is stored as 1000*mag where mag is PhotAbs
+   measure[].M for PHOT_REL is the same 
+   XXX EAM : note that we are transitioning away from millimag internal storage 
+*/ 
+
+/* send in:
+   Nphot - photcode number
+   Tphot - photcode type
+   Ns    - secfilt entry (-1 for PRI)
+   &catalog.average[i], 
+   &catalog.measure[catalog.average[i].measureOffset], 
+   &catalog.secfilt[i*Nsec] 
+*/
+
+
+double *ExtractMagnitudes (PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n) {
+  
+  double *M, mag;
+  int N;
+
+  if ((mode == MAG_AVE) || (mode == MAG_REF)) {
+    ALLOCATE (M, double, 1);
+    mag = ExtractAverages (code, mode, average, secfilt, measure, AVE_MAG);
+    if (isnan(mag)) {
+      N = 0;
+    } else {
+      N = 1;
+      M[0] = mag;
+    }
+  } else {
+    M = ExtractMeasures (code, mode, average, secfilt, measure, &N, MEAS_MAG);
+  }
+  
+  *n = N;
+  return (M);
+}
+
+/* extract delta-mag pairs applying specified selections */
+double *ExtractDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+
+  int i, j, A1, A2, N1, N2, Np, Nlist, NLIST;
+  double *M1, *M2, *list;
+
+  /* check for special case of measure-measure - this is needed to drop self-matches */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresDMag (code, mode, average, secfilt, measure, nlist);
+    return (list);
+  }
+
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+  M1 = M2 = NULL;
+
+  /* one of the two is an average, must do independently */
+  M1 = ExtractMagnitudes (code[0], mode[0], average, secfilt, measure, &N1);
+  if (N1 == 0) goto skip;
+  
+  Np = GetSelectionParam ();
+  SetSelectionParam (Np + 1);
+  M2 = ExtractMagnitudes (code[1], mode[1], average, secfilt, measure, &N2);
+  if (N2 == 0) goto skip;
+
+  /* magnitudes may be NAN : set delta to NAN */
+  for (i = 0; i < N1; i++) {
+    for (j = 0; j < N2; j++) {
+      if (isnan(M1[i]) || isnan(M2[j])) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1[i] - M2[j];
+      }
+      Nlist ++;
+    }
+  }
+
+skip: 
+  if (M1 != NULL) free (M1);
+  if (M2 != NULL) free (M2);
+  *nlist = Nlist;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the 
+   pre-set selections - does not return self-matched measurements */
+double *ExtractMeasuresDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist) {
+
+  int i, j, Np0, Np1, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values - drop this test? this is programming case, not a user case */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* exclude based on average parameters for both codes  */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  Np0 = GetSelectionParam ();
+  Np1 = Np0 + 1;
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    SetSelectionParam (Np0);
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      SetSelectionParam (Np1);
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      if (isnan(M1) || isnan(M2)) {
+	list[Nlist] = NAN;
+      } else {
+	list[Nlist] = M1 - M2;
+      }
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+/* extract a measurement list matching the number of dmag entries */
+double *ExtractByDMag (PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int A1, A2, N1;
+  double *list;
+
+  /* check for special case of measure-measure */
+  A1 = ((mode[0] == MAG_AVE) || (mode[0] == MAG_REF));
+  A2 = ((mode[1] == MAG_AVE) || (mode[1] == MAG_REF));
+  if (!A1 && !A2) {
+    list = ExtractMeasuresByDMag (code, mode, 1, average, secfilt, measure, nlist, param);
+    return (list);
+  }
+
+  /* one of the two entries results in a single element. extract the other */
+  if (A1) {
+    list = ExtractMeasures (code[1], mode[1], average, secfilt, measure, &N1, param);
+  } else {
+    list = ExtractMeasures (code[0], mode[0], average, secfilt, measure, &N1, param);
+  }
+  *nlist = N1;
+  return (list);
+}
+  
+/* extract a list of delta-measure-mags from the specified average entry based on the pre-set selections */
+double *ExtractMeasuresByDMag (PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param) {
+
+  int i, j, n, Nlist, NLIST;
+  double *list, M1, M2;
+  
+  *nlist = 0; 
+  Nlist = 0;
+  NLIST = MAX (1, average[0].Nmeasure*average[0].Nmeasure);
+  ALLOCATE (list, double, NLIST);
+
+  /* must have two code values */
+  if (code == NULL) return (list);
+  if (code[0] == NULL) return (list);
+  if (code[1] == NULL) return (list);
+
+  /* chisq, fwhm, Nphot, Ncode */
+  if (!TestAverage (code[0], average, secfilt, measure)) return (list);
+  if (!TestAverage (code[1], average, secfilt, measure)) return (list);
+
+  /* loop twice over all measures */
+  for (i = 0; i < average[0].Nmeasure; i++) {
+    TESTCODE (code[0], measure[i]);
+    TESTMEASURE (measure[i]);
+    SETMAG(M1, measure[i], mode[0]);
+    for (j = 0; j < average[0].Nmeasure; j++) {
+      if (i == j) continue;
+      TESTCODE (code[1], measure[j]);
+      TESTMEASURE (measure[j]);
+      SETMAG(M2, measure[j], mode[1]);
+      n = (use_first) ? i : j;
+
+      /* assign value */
+      list[Nlist] = GetMeasure (param, &average[0], &measure[n], (use_first ? M1 : M2));
+      Nlist ++;
+    }
+  }
+  *nlist = Nlist;
+  return (list);
+}
+
+double GetMeasure (int param, Average *average, Measure *measure, double mag) {
+
+  double ra, dec, x, y;
+  double value;
+  Image *image;
+  Coords *mosaic;
+
+  value = 0;
+  switch (param) {
+    case MEAS_MAG: /* magnitudes are already determined above */
+      value = mag;
+      break;
+    case MEAS_RA: /* OK */
+      value = average[0].R - measure[0].dR / 3600.0;
+      break;
+    case MEAS_DEC: /* OK */
+      value = average[0].D - measure[0].dD / 3600.0;
+      break;
+# if (0)
+    case MEAS_dMAG: /* OK */
+      value = measure[0].dM;
+      break;
+    case MEAS_DOPHOT: /* OK */
+      value = measure[0].dophot;
+      break;
+# endif
+    case MEAS_AIRMASS: /* OK */
+      value = measure[0].airmass;
+      break;
+    case MEAS_EXPTIME: /* OK */
+      value = pow (10.0, measure[0].dt * 0.4);
+      break;
+    case MEAS_PHOTCODE: /* OK */
+      value = measure[0].photcode;
+      break;
+    case MEAS_TIME: /* OK */
+      value = TimeValue (measure[0].t, TimeReference, TimeFormat);
+      break;
+    case MEAS_RA_OFFSET: /* OK */
+      value = measure[0].dR;
+      break;
+    case MEAS_DEC_OFFSET: /* OK */
+      value = measure[0].dD;
+      break;
+    case MEAS_FWHM: /* OK */
+      value = 0.01*measure[0].FWx;
+      break;
+    case MEAS_DB_FLAGS: /* ? */
+      value = measure[0].dbFlags;
+      break;
+    case MEAS_XCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Xccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImage (measure[0].t, measure[0].photcode);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = x;
+# endif
+      break;
+    case MEAS_YCCD: /* OK */
+/* I need to perform this conversion for ELIXIR and LONEOS formats on load */      
+# if 0
+      value = measure[0].Yccd;
+# else
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      image = MatchImage (measure[0].t, measure[0].photcode);
+      if (image == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, &image[0].coords);
+      value = y;
+# endif
+      break;
+    case MEAS_XMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = x;
+      break;
+    case MEAS_YMOSAIC: /* OK */
+      ra  = average[0].R - measure[0].dR / 3600.0;
+      dec = average[0].D - measure[0].dD / 3600.0;
+      mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+      if (mosaic == NULL) break;
+      RD_to_XY (&x, &y, ra, dec, mosaic);
+      value = y;
+      break;
+  }
+  return (value);
+}
+
+/** the mosaic entries do not use the registered mosaic found 
+    by MatchImage (via FindMosaicForImage).  Rather, they use
+    a coordinate frame saved by SetImageSelection 
+**/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pmeasure.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pmeasure.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/pmeasure.c	(revision 17422)
@@ -0,0 +1,243 @@
+# include "dvoshell.h"
+# define NCHUNK 10000
+
+enum {FLAG_IGNORE, FLAG_SKIP, FLAG_KEEP};
+
+int pmeasure (int argc, char **argv) {
+  
+  FILE *f;
+  int i, j, k, m, kapa, Narg, Npts, NPTS, status, VERBOSE, TimeSelect;
+  double Mz, Mr, mag;
+  double Radius, Rmin, Rmax, R, D, trange;
+  unsigned IDclip, IDchoice, LimExclude;
+  unsigned dbFlagChoice, dbFlagClip;
+  unsigned photFlagChoice, photFlagClip;
+  int PhotcodeClip;
+  float *Xvec, *Yvec, *Zvec;
+  time_t tzero, tend;
+
+  SkyTable *sky;
+  SkyList *skylist;
+  Catalog catalog;
+  Graphdata graphmode;
+
+  if (!InitPhotcodes ()) return (FALSE);
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  PhotcodeClip = -1;
+  if ((Narg = get_argument (argc, argv, "-p"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  if ((Narg = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (Narg, &argc, argv);
+    PhotcodeClip = GetPhotcodeCodebyName (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+  }
+  IDchoice = 0;
+  IDclip = FALSE;
+  if ((Narg = get_argument (argc, argv, "-ID"))) {
+    remove_argument (Narg, &argc, argv);
+    IDchoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    IDclip = TRUE;
+  }
+  dbFlagChoice = 0;
+  dbFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+dbflag"))) {
+    remove_argument (Narg, &argc, argv);
+    dbFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    dbFlagClip = FLAG_KEEP;
+  }
+  photFlagChoice = 0;
+  photFlagClip = FLAG_IGNORE;
+  if ((Narg = get_argument (argc, argv, "-photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_SKIP;
+  }
+  if ((Narg = get_argument (argc, argv, "+photflag"))) {
+    remove_argument (Narg, &argc, argv);
+    photFlagChoice  = strtol(argv[Narg], NULL, 0);
+    remove_argument (Narg, &argc, argv);
+    photFlagClip = FLAG_KEEP;
+  }
+
+  TimeSelect = FALSE;
+  if ((Narg = get_argument (argc, argv, "-time"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) {
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_dtime (argv[Narg], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((Narg = get_argument (argc, argv, "-trange"))) {
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    if (!ohana_str_to_time (argv[Narg], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return FALSE;
+    }
+    remove_argument (Narg, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+
+  LimExclude = FALSE;
+  if ((Narg = get_argument (argc, argv, "-x"))) {
+    remove_argument (Narg, &argc, argv);
+    LimExclude = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  if ((Narg = get_argument (argc, argv, "-m"))) {
+    remove_argument (Narg, &argc, argv);
+    Mr  = atof(argv[Narg]);
+    remove_argument (Narg, &argc, argv);
+    Mz = atof(argv[Narg]);
+    Mr = Mr - Mz;
+    remove_argument (Narg, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pmeasure (-all) [-m M M]\n");
+    gprint (GP_ERR, " options:\n");
+    gprint (GP_ERR, " [-p photcode]\n");
+    gprint (GP_ERR, " [-photcode photcode]\n");
+    gprint (GP_ERR, " [-ID ID]\n");
+    gprint (GP_ERR, " [-dbflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+dbflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-photflag value] : skip matches to these flags\n");
+    gprint (GP_ERR, " [+photflag value] : keep matches to these flags\n");
+    gprint (GP_ERR, " [-time (start) (duration)]\n");
+    gprint (GP_ERR, " [-trange (start) (stop)]\n");
+    gprint (GP_ERR, " [-x] : exclude points larger / smaller than mag limits\n");
+    gprint (GP_ERR, " [-v] : verbose mode\n");
+    return (FALSE);
+  }
+  graphmode.style = 2; /* set style to points */
+  graphmode.size = -1; /* point size determined by Zvec */
+  graphmode.etype = 0; /* no errorbars */
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  /* load sky from correct table */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  /* storage for plotting the points */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (Xvec, float, NPTS);
+  ALLOCATE (Yvec, float, NPTS);
+  ALLOCATE (Zvec, float, NPTS);
+
+  for (j = 0; j < skylist[0].Nregions; j++) {
+    catalog.filename = skylist[0].filename[j];
+    catalog.catflags = LOAD_AVES | LOAD_MEAS;
+    catalog.Nsecfilt = 0;
+
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);
+
+    /* project stars to screen display coords */
+    for (i = 0; i < catalog.Naverage; i++) {
+      if (IDclip && (catalog.average[i].code != IDchoice)) continue;
+      while (catalog.average[i].R < Rmin) catalog.average[i].R += 360.0;
+      while (catalog.average[i].R > Rmax) catalog.average[i].R -= 360.0;
+      m = catalog.average[i].measureOffset;
+      for (k = 0; k < catalog.average[i].Nmeasure; k++) {
+	if ((dbFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((dbFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].dbFlags & dbFlagChoice)) continue;
+	if ((photFlagClip == FLAG_SKIP) &&  (catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if ((photFlagClip == FLAG_KEEP) && !(catalog.measure[m+k].photFlags & photFlagChoice)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t < tzero)) continue;
+	if (TimeSelect && (catalog.measure[m+k].t > tzero + trange)) continue;
+	if ((PhotcodeClip != -1) && (catalog.measure[m+k].photcode != PhotcodeClip)) continue;
+	mag = PhotCat (&catalog.measure[m+k]);
+	Zvec[Npts] = MIN (1.0, MAX (0.01, (mag - Mz) / Mr));
+	if (LimExclude && (Zvec[Npts] > 0.99)) continue;
+	if (Zvec[Npts] < 0.011) continue;
+	R = catalog.average[i].R - catalog.measure[m+k].dR/3600.0;
+	D = catalog.average[i].D - catalog.measure[m+k].dD/3600.0;
+	// XXX drop this check
+	if ((R < Rmin) || (R > Rmax) || (D < -90.0) || (D > 90.0)) {
+	  char *date;
+	  date = ohana_sec_to_date (catalog.measure[m+k].t);
+	  gprint (GP_LOG, "out: %f, %f : %s : (%f, %f) + (%f, %f)\n", R, D, date, catalog.average[i].R, catalog.average[i].D, catalog.measure[m+k].dR/3600.0, catalog.measure[m+k].dD/3600.0);
+	  free (date);
+	}
+	status = fRD_to_XY (&Xvec[Npts], &Yvec[Npts], R, D, &graphmode.coords);
+	if (!status) {
+	  fprintf (stderr, ".");
+	  continue;
+	}
+	Npts ++;
+
+	if (Npts == NPTS - 1) {
+	    NPTS += 1000;
+	    REALLOCATE (Xvec, float, NPTS);
+	    REALLOCATE (Yvec, float, NPTS);
+	    REALLOCATE (Zvec, float, NPTS);
+	}
+	if (Npts > NCHUNK) {
+	    PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+	    Npts = 0;
+	}
+      }
+    }
+    dvo_catalog_free (&catalog);
+  }
+  if (Npts > 0) PlotVectorTriplet (kapa, Npts, Xvec, Yvec, Zvec, &graphmode);
+
+  free (Xvec);
+  free (Yvec);
+  free (Zvec);
+
+  return (TRUE);
+
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/procks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/procks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/procks.c	(revision 17422)
@@ -0,0 +1,153 @@
+# include "dvoshell.h"
+
+typedef struct {
+  double ra[3];
+  double dec[3];
+  double X[3];
+  double Y[3];
+  unsigned int t[3];
+  double mag[3];
+} Rocks;
+static Rocks *rocks = (Rocks *) NULL;
+static int   Nrocks;
+
+int procks (int argc, char **argv) {
+  
+  FILE *f;
+  Vector Xvec, Yvec;
+  int kapa, i, j, N, NROCKS;
+  int N0, N1, SpeedClip, Reload;
+  unsigned int t0, t1;
+  double Mz, Mr, S0, S1;
+  double Rmin, Rmax;
+  Graphdata graphmode;
+  char rockcat[256];
+
+  VarConfig ("ROCK_CATALOG", "%s", rockcat);
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  f = (FILE *) NULL;
+  Mz = 17.0;
+  Mr = -5.0;
+  Rmin = graphmode.coords.crval1 - 182.0;
+  Rmax = graphmode.coords.crval1 + 182.0;
+
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    Mr  = 1000*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    Mz = 1000*atof(argv[N]);
+    Mr = Mr - Mz;
+    remove_argument (N, &argc, argv);
+  }
+
+  S0 = S1 = 0;
+  SpeedClip = FALSE;
+  if ((N = get_argument (argc, argv, "-speed"))) {
+    SpeedClip = TRUE;
+    remove_argument (N, &argc, argv);
+    S0 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    S1 = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  Reload = FALSE;
+  if ((N = get_argument (argc, argv, "-reload"))) {
+    Reload = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: procks [-m M M] [-speed s s] \n");
+    return (FALSE);
+  }
+  
+  f = fopen (rockcat, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open rock catalog\n");
+    return (TRUE);
+  }
+
+  if ((rocks == (Rocks *) NULL) || Reload) {
+    if (rocks != (Rocks *) NULL) free (rocks);
+    NROCKS = 100;
+    ALLOCATE (rocks, Rocks, NROCKS);
+    for (i = 0; fscanf (f, "%lf %lf %d%lf%lf%lf %d%lf%lf%lf %d%lf%lf%lf", 
+			&rocks[i].X[0], &rocks[i].Y[0], 
+			&rocks[i].t[0], &rocks[i].ra[0], &rocks[i].dec[0], &rocks[i].mag[0], 
+			&rocks[i].t[1], &rocks[i].ra[1], &rocks[i].dec[1], &rocks[i].mag[1], 
+			&rocks[i].t[2], &rocks[i].ra[2], &rocks[i].dec[2], &rocks[i].mag[2]
+			) != EOF; i++) {
+      if (i == NROCKS - 1) {
+	NROCKS += 100;
+	REALLOCATE (rocks, Rocks, NROCKS);
+      }
+    }
+    Nrocks = i;
+  }
+      
+  if (Nrocks == 0) {
+    free (rocks);
+    gprint (GP_ERR, "no rocks in datafile\n");
+    return (TRUE);
+  }
+
+  /* data has been loaded, get ready to plot it */
+  Yvec.Nelements = Xvec.Nelements = 3*Nrocks;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    for (j = 0; j < 3; j++) {
+      while (rocks[i].ra[j] < Rmin) rocks[i].ra[j] += 360.0;
+      while (rocks[i].ra[j] > Rmax) rocks[i].ra[j] -= 360.0;
+      fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[j], rocks[i].dec[j], &graphmode.coords);
+      N ++;
+    }
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.etype = 0; /* no errorbars */
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  /* now plot vectors between two extrema */
+  Yvec.Nelements = Xvec.Nelements = 2*Nrocks;
+  REALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  REALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  
+  /* project stars to screen display coords */
+  for (N = i = 0; i < Nrocks; i++) {
+    if (SpeedClip && ((rocks[i].Y[0] < S0) || (rocks[i].Y[0] > S1))) continue;
+    N0 = N1 = 0;
+    t0 = t1 = rocks[i].t[0];
+    for (j = 1; j < 3; j++) {
+      if (rocks[i].t[j] < t0) { N0 = j; t0 = rocks[i].t[j]; }
+      if (rocks[i].t[j] > t1) { N1 = j; t1 = rocks[i].t[j]; }
+    }
+    while (rocks[i].ra[N0] < Rmin) rocks[i].ra[N0] += 360.0;
+    while (rocks[i].ra[N0] > Rmax) rocks[i].ra[N0] -= 360.0;
+    while (rocks[i].ra[N1] < Rmin) rocks[i].ra[N1] += 360.0;
+    while (rocks[i].ra[N1] > Rmax) rocks[i].ra[N1] -= 360.0;
+    fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[N0], rocks[i].dec[N0], &graphmode.coords);
+    N ++;
+    fRD_to_XY (&Xvec.elements[N], &Yvec.elements[N], rocks[i].ra[N1], rocks[i].dec[N1], &graphmode.coords);
+    N ++;
+  }
+  Yvec.Nelements = Xvec.Nelements = N;
+  
+  graphmode.style = 2; /* set style to points */
+  graphmode.ptype = 100; /* connect pairs */
+  graphmode.etype = 0; /* no errorbars */
+
+  PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/region_list.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/region_list.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/region_list.c	(revision 17422)
@@ -0,0 +1,215 @@
+# include "dvoshell.h"
+
+/* XXX note : for RegionName or RegionList, we need to free the skylist
+   elements, but not in the case of radius selection - this implies 
+   information carried back up */
+
+static char *CATDIR  = NULL;
+static SkyTable *sky = NULL;
+
+int SetCATDIR (char *path, int verbose) {
+
+  char *newpath;
+  char catdir_config[256];
+  char gscfile[256];
+  char skyfile[256];
+  int  skydepth;
+
+  /* find CATDIR in config system */
+  if (path == NULL) {
+    if (VarConfig ("CATDIR", "%s", catdir_config) == NULL) return (FALSE);
+    newpath = catdir_config;
+  } else {
+    newpath = path;
+  }
+
+  if (CATDIR != NULL) free (CATDIR);
+  CATDIR = strcreate (newpath);
+
+  if (VarConfig ("GSCFILE",  "%s", gscfile) == NULL) gscfile[0] = 0;
+  if (VarConfig ("SKYFILE",  "%s", skyfile) == NULL) skyfile[0] = 0;
+  if (VarConfig ("SKYDEPTH", "%d", &skydepth) == NULL) skydepth = 2;
+
+  if (verbose) {
+      gprint (GP_ERR, "CATDIR %s\n", CATDIR);
+      gprint (GP_ERR, "GSCFILE %s\n", gscfile);
+      gprint (GP_ERR, "SKYFILE %s\n", skyfile);
+      gprint (GP_ERR, "SKYDEPTH %d\n", skydepth);
+  }
+
+  /* load the SkyTable at this point */
+  /* set the image path as well */
+
+  if (sky != NULL) SkyTableFree (sky);
+  sky = SkyTableLoadOptimal (CATDIR, skyfile, gscfile, skydepth, verbose);
+  if (sky == NULL) return FALSE;
+
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  return (TRUE);
+}
+
+char *GetCATDIR () {
+  if (CATDIR == NULL) {
+    SetCATDIR (NULL, FALSE);
+  }
+  return (CATDIR);
+}
+
+SkyTable *GetSkyTable () {
+  if (sky == NULL) {
+    SetCATDIR (NULL, FALSE);
+  }
+  return (sky);
+}
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection) {
+
+  if (selection == NULL) return;
+  if (selection[0].name != NULL) free (selection[0].name);
+  if (selection[0].list != NULL) free (selection[0].list);
+  free (selection);
+}
+
+SkyRegionSelection *SetRegionSelection (int *argc, char **argv) {
+  
+  int N;
+  SkyRegionSelection *selection;
+
+  ALLOCATE (selection, SkyRegionSelection, 1);
+  selection[0].name = NULL;
+  selection[0].list = NULL;
+  selection[0].useDisplay = FALSE;
+  selection[0].useSkyregion = FALSE;
+
+  /* check for Region selection (named dvo catalog file) */
+  if ((N = get_argument (*argc, argv, "-cpt"))) {
+    remove_argument (N, argc, argv);
+    selection[0].name = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* check for Region list (file containing dvo catalog file list)*/
+  if ((N = get_argument (*argc, argv, "-cptlist"))) {
+    remove_argument (N, argc, argv);
+    selection[0].list = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+    return selection;
+  } 
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-region"))) {
+    remove_argument (N, argc, argv);
+    selection[0].useDisplay = TRUE;
+    return selection;
+  }    
+
+  /* check for Region selection from display */
+  if ((N = get_argument (*argc, argv, "-skyregion"))) {
+    if (N + 5 >= *argc) {
+      gprint (GP_ERR, "USAGE: -skyregion (RA) (RA) (DEC) (DEC)\n");
+      FreeSkyRegionSelection (selection);
+      return NULL;
+    }
+    remove_argument (N, argc, argv);
+    selection[0].useSkyregion = TRUE;
+    set_skyregion (atof(argv[N]), atof(argv[N+1]), atof(argv[N+2]), atof(argv[N+3]));
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    remove_argument (N, argc, argv);
+    return selection;
+  }    
+
+  /* default to pre-defined sky region */
+  selection[0].useSkyregion = TRUE;
+  return selection;
+}
+
+/* given possible options (by name, by list, by graph region), select SkyRegions */
+SkyList *SelectRegions (SkyRegionSelection *selection) {
+
+  SkyList *skylist;
+
+  /* determine region-file names */
+  if (selection->name != NULL) {
+    char filename[256];
+
+    ALLOCATE (skylist, SkyList, 1);
+    ALLOCATE (skylist[0].regions, SkyRegion *, 1);
+    ALLOCATE (skylist[0].regions[0], SkyRegion, 1);
+    ALLOCATE (skylist[0].filename, char *, 1);
+    skylist[0].ownElements = TRUE; // free these elements when freeing the list
+    
+    strcpy (skylist[0].regions[0][0].name, selection->name);
+    sprintf (filename, "%s/%s.cpt", CATDIR, selection->name);
+    skylist[0].filename[0] = strcreate (filename);
+    return (skylist);
+  } 
+
+  if (selection->list != NULL) {
+    skylist = SkyListLoadFile (selection->list);
+    return (skylist);
+  }
+
+  if (selection->useDisplay) {
+    double Radius;
+    Graphdata graphsky;
+
+    if (!GetGraphData (&graphsky, NULL, NULL)) {
+      gprint (GP_ERR, "region display not available\n");
+      return (NULL);
+    }
+
+    Radius = MAX (fabs(graphsky.xmax), fabs(graphsky.ymax));
+    skylist = SkyListByRadius (sky, -1, graphsky.coords.crval1, graphsky.coords.crval2, Radius);
+    return (skylist);
+  }
+
+  if (selection->useSkyregion) {
+    double Rmin, Rmax, Dmin, Dmax;
+
+    get_skyregion (&Rmin, &Rmax, &Dmin, &Dmax);
+    skylist = SkyListByBounds (sky, -1, Rmin, Rmax, Dmin, Dmax);
+    return (skylist);
+  }    
+
+  return NULL;
+}
+
+/* returns a list of region files names from file */
+SkyList *SkyListLoadFile (char *filename) {
+  
+  FILE *f;
+  int NREGIONS, Nregions;
+  SkyList *skylist;
+
+  ALLOCATE (skylist, SkyList, 1);
+
+  f = fopen (filename, "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "ERROR: can't find region list file %s\n", filename);
+    skylist[0].Nregions = 0;
+    skylist[0].regions = NULL;
+    return (skylist);
+  }
+  
+  Nregions = 0;
+  NREGIONS = 50;
+  ALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS);
+  ALLOCATE (skylist[0].filename, char *, NREGIONS);
+  skylist[0].ownElements = TRUE; // free these elements when freeing the list
+
+  while (fscanf (f, "%s", filename) != EOF) {
+    ALLOCATE (skylist[0].regions[Nregions], SkyRegion, 1);
+    strcpy (skylist[0].regions[Nregions][0].name, filename);
+    sprintf (filename, "%s/%s.cpt", CATDIR, skylist[0].regions[Nregions][0].name);
+    skylist[0].filename[Nregions] = strcreate (filename);
+    Nregions ++;
+    CHECK_REALLOCATE (skylist[0].regions, SkyRegion *, NREGIONS, Nregions, 50);
+  }
+  skylist[0].Nregions = Nregions;
+  return (skylist);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/scripts/navigate
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/scripts/navigate	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/scripts/navigate	(revision 17422)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/showtile.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/showtile.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/showtile.c	(revision 17422)
@@ -0,0 +1,95 @@
+# include "dvoshell.h"
+static float dr[] = {0.0, 1.0, 1.0, 0.0};
+static float dd[] = {0.0, 0.0, 1.0, 1.0};
+
+int showtile (int argc, char **argv) {
+
+  int kapa, Nd, N, NPTS, status, i, InPic;
+  Graphdata graphmode;
+  Coords coords;
+  Vector Xvec, Yvec;
+  float r, d, R, D;
+  float Ro[90], Do[90];
+
+  /* show tile pattern in viewed region */
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: showtile [option]\n");
+    return (FALSE);
+  }
+  
+  N = 0;
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+
+  /* starting position */
+
+  /* reference for coords is this image */
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.crval1 = coords.crval2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0;
+  coords.pc1_1  = coords.pc2_2  = 1.0;
+  coords.pc1_2  = coords.pc2_1  = 0.0;
+  coords.Npolyterms = 0;
+  strcpy (coords.ctype, "RA---TAN");
+  
+  /* fill in top-left region */
+  for (r = 0; r < 3; r += 1.0) {
+    gprint (GP_ERR, "r: %f\n", r);
+    for (Nd = d = 0; d < 90; Nd ++, d += 1.0) {
+      if (r == 0) {
+	coords.crval1 = r;
+	coords.crval2 = d;
+      } else {
+	coords.crval1 = Ro[Nd];
+	coords.crval2 = Do[Nd];
+      }
+      for (i = 0; i < 4; i++) {
+	fXY_to_RD (&R, &D, dr[i], dd[i], &coords);
+	status |= fRD_to_XY (&Xvec.elements[N+2*i], &Yvec.elements[N+2*i], R, D, &graphmode.coords);
+	if (i > 0) {
+	  Xvec.elements[N+2*i - 1] = Xvec.elements[N+2*i];
+	  Yvec.elements[N+2*i - 1] = Yvec.elements[N+2*i];
+	}
+	if (i == 1) {
+	  Ro[Nd] = R;
+	  Do[Nd] = D;
+	}
+      }
+      Xvec.elements[N+7] = Xvec.elements[N];
+      Yvec.elements[N+7] = Yvec.elements[N];
+
+      /* check if any corner is in plotting region */
+      InPic = FALSE;
+      for (i = 0; i < 8; i+=2) {
+	if ((Xvec.elements[N+i] >= graphmode.xmin) && 
+	    (Xvec.elements[N+i] <= graphmode.xmax) && 
+	    (Yvec.elements[N+i] >= graphmode.ymin) && 
+	    (Yvec.elements[N+i] <= graphmode.ymax))
+	  InPic = TRUE;
+      }
+      if (!InPic) continue;
+      N+=8;
+      if (N > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+  
+  Xvec.Nelements = Xvec.Nelements = N;
+  if (N > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, N, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/simage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/simage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/simage.c	(revision 17422)
@@ -0,0 +1,168 @@
+# include "dvoshell.h"
+# define D_NSTARS 1000
+# define BYTES_STAR 31
+# define BLOCK 1000
+
+int simage (int argc, char **argv) {
+
+  char *buffer;
+  Vector Xvec, Yvec, Zvec;
+  double R, D, X, Y, M, zero, range;
+  FILE *f;
+  Header header;
+  Coords coords;
+  int i, j, kapa, Nstars, nstars, Nbytes, nbytes, Npts, N;
+  Graphdata graphmode;
+
+  if (!GetGraph (&graphmode, &kapa, NULL)) return (FALSE);
+
+  zero = 17.0;
+  range = -5.0;
+  if ((N = get_argument (argc, argv, "-m"))) {
+    remove_argument (N, &argc, argv);
+    range = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    zero  = atof(argv[N]);
+    range = range - zero;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: image (filename)\n");
+    return (FALSE);
+  }
+
+  gprint (GP_ERR, "not working at the moment (cmp format)\n");
+  return (FALSE);
+  
+  /* read header */
+  if (!gfits_read_header (argv[1], &header)) {
+    gprint (GP_ERR, "ERROR: can't find image file %s\n", argv[1]);
+    return (FALSE);
+  }
+  /* get astrometry information */
+  strcpy (coords.ctype, "NONE");
+  gfits_scan (&header, "CTYPE1",   "%s",  1, coords.ctype);
+  if (strcmp (coords.ctype, "RA---PLY")) {
+    gprint (GP_ERR, "ERROR: wrong astrometric info in header\n");
+    return (FALSE);
+  }
+  gfits_scan (&header, "CDELT1",   "%f", 1, &coords.cdelt1); 
+  gfits_scan (&header, "CDELT2",   "%f", 1, &coords.cdelt2);
+  gfits_scan (&header, "CRVAL1",   "%lf", 1, &coords.crval1);
+  gfits_scan (&header, "CRVAL2",   "%lf", 1, &coords.crval2);  
+  gfits_scan (&header, "CRPIX1",   "%f", 1, &coords.crpix1);
+  gfits_scan (&header, "CRPIX2",   "%f", 1, &coords.crpix2);
+  gfits_scan (&header, "PC001001", "%f", 1, &coords.pc1_1);
+  gfits_scan (&header, "PC001002", "%f", 1, &coords.pc1_2);
+  gfits_scan (&header, "PC002001", "%f", 1, &coords.pc2_1);
+  gfits_scan (&header, "PC002002", "%f", 1, &coords.pc2_2);
+  /* RA Terms */
+  gfits_scan (&header, "PCA1X2Y0", "%f", 1, &coords.polyterms[0][0]);
+  gfits_scan (&header, "PCA1X1Y1", "%f", 1, &coords.polyterms[1][0]);
+  gfits_scan (&header, "PCA1X0Y2", "%f", 1, &coords.polyterms[2][0]);
+  gfits_scan (&header, "PCA1X3Y0", "%f", 1, &coords.polyterms[3][0]);
+  gfits_scan (&header, "PCA1X2Y1", "%f", 1, &coords.polyterms[4][0]);
+  gfits_scan (&header, "PCA1X1Y2", "%f", 1, &coords.polyterms[5][0]);
+  gfits_scan (&header, "PCA1X0Y3", "%f", 1, &coords.polyterms[6][0]);
+  /* Dec Terms */			    
+  gfits_scan (&header, "PCA2X2Y0", "%f", 1, &coords.polyterms[0][1]);
+  gfits_scan (&header, "PCA2X1Y1", "%f", 1, &coords.polyterms[1][1]);
+  gfits_scan (&header, "PCA2X0Y2", "%f", 1, &coords.polyterms[2][1]);
+  gfits_scan (&header, "PCA2X3Y0", "%f", 1, &coords.polyterms[3][1]);
+  gfits_scan (&header, "PCA2X2Y1", "%f", 1, &coords.polyterms[4][1]);
+  gfits_scan (&header, "PCA2X1Y2", "%f", 1, &coords.polyterms[5][1]);
+  gfits_scan (&header, "PCA2X0Y3", "%f", 1, &coords.polyterms[6][1]);
+  coords.Npolyterms = 2; /* how many do we use? */
+
+  /* find number of stars */
+  gfits_scan (&header, "NSTARS", "%d", 1, &Nstars);
+  if (Nstars == 0) {
+    gprint (GP_ERR, "no stars in file\n");
+    return (FALSE);
+  }
+
+  /* open file data */
+  f = fopen (argv[1], "r");
+  if (f == NULL) {
+    gprint (GP_ERR, "can't find data in file %s\n", argv[1]);
+    return (FALSE);
+  }
+  fseek (f, header.size, SEEK_SET); 
+
+  /* set up storage buffers */
+  Xvec.Nelements = Nstars;
+  Yvec.Nelements = Nstars;
+  Zvec.Nelements = Nstars;
+  ALLOCATE (Xvec.elements, float, Xvec.Nelements);
+  ALLOCATE (Yvec.elements, float, Yvec.Nelements);
+  ALLOCATE (Zvec.elements, float, Zvec.Nelements);
+  ALLOCATE (buffer, char, (BLOCK*BYTES_STAR));
+
+  /* load in stars by blocks of 1000 */
+  nstars = 0;
+  Nbytes = Nstars*BYTES_STAR;
+  for (i = 0; i < (int)(Nbytes / (BLOCK*BYTES_STAR)); i++) {
+    nbytes = fread (buffer, 1, (BLOCK*BYTES_STAR), f);
+    if (nbytes != BLOCK*BYTES_STAR) {
+      gprint (GP_ERR, "failed to read in stars (1)\n");
+      free (Xvec.elements);
+      free (Yvec.elements);
+      free (Zvec.elements);
+      free (buffer);
+      return (FALSE);
+    }
+    for (j = 0; j < BLOCK; j++, nstars++) {
+      dparse (&X,  1, &buffer[j*BYTES_STAR]);
+      dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+      dparse (&M,  3, &buffer[j*BYTES_STAR]);
+      XY_to_RD (&R, &D, X, Y, &coords);
+      fRD_to_XY (&Xvec.elements[nstars], &Yvec.elements[nstars], R, D, &graphmode.coords);
+      Zvec.elements[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+    }
+  }
+  /* left over fraction of a block */
+  nbytes = fread (buffer, 1, (Nbytes % (BLOCK*BYTES_STAR)), f);
+  if (nbytes != (Nbytes % (BLOCK*BYTES_STAR))) {
+    gprint (GP_ERR, "ERROR: failed to read in stars (2)\n");
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+    free (buffer);
+    return (FALSE);
+  }
+  for (j = 0; j < nbytes / BYTES_STAR; j++, nstars++) {
+    dparse (&X,  1, &buffer[j*BYTES_STAR]);
+    dparse (&Y,  2, &buffer[j*BYTES_STAR]);
+    dparse (&M,  3, &buffer[j*BYTES_STAR]);
+    XY_to_RD (&R, &D, X, Y, &coords);
+    fRD_to_XY (&Xvec.elements[nstars], &Yvec.elements[nstars], R, D, &graphmode.coords);
+    Zvec.elements[nstars] = MIN (1.0, MAX (0.01, (M - zero) / range));
+  }
+  
+  if (nstars != Nstars) {
+    gprint (GP_ERR, "ERROR: failed to read in all stars (%d of %d)\n", nstars, Nstars);
+    free (Xvec.elements);
+    free (Yvec.elements);
+    free (Zvec.elements);
+    free (buffer);
+    return (FALSE);
+  }
+
+  graphmode.style = 2;
+  graphmode.size = -1;
+  graphmode.etype = 0;
+  Npts = Xvec.Nelements;
+
+  PlotVectorTriplet (kapa, Npts, Xvec.elements, Yvec.elements, Zvec.elements, &graphmode);
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (Zvec.elements);
+  free (buffer);
+
+  return (TRUE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycat.c	(revision 17422)
@@ -0,0 +1,125 @@
+# include "dvoshell.h"
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside);
+
+int skycat (int argc, char **argv) {
+  
+  double Radius;
+  int i, j, N, Nregions, kapa, ShowAll, NPTS, Npts, leftside, Depth, VERBOSE;
+  struct stat filestat;
+  Vector Xvec, Yvec;
+  Graphdata graphmode;
+  double X[4], Y[4], Rmin, Rmax, Rmid;
+  SkyTable *sky;
+  SkyList *skylist;
+  SkyRegion **regions;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  ShowAll = FALSE;
+  if ((N = get_argument (argc, argv, "-all"))) {
+    remove_argument (N, &argc, argv);
+    ShowAll = TRUE;
+  }
+  Depth = -1;
+  if ((N = get_argument (argc, argv, "-depth"))) {
+    remove_argument (N, &argc, argv);
+    Depth = atoi (argv[N]);
+    remove_argument (N, &argc, argv);    
+  }
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: skycat [-all]\n");
+    return (FALSE);
+  }
+
+  Radius = MAX (fabs(graphmode.xmax), fabs(graphmode.ymax));
+
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, Depth, graphmode.coords.crval1, graphmode.coords.crval2, Radius);
+  
+  if (VERBOSE) gprint (GP_ERR, "region: %6.2f - %6.2f, %6.2f - %6.2f\n", 
+			graphmode.coords.crval1 - Radius, graphmode.coords.crval1 + Radius, 
+			graphmode.coords.crval2 - Radius, graphmode.coords.crval2 + Radius);
+
+  Rmin = graphmode.coords.crval1 - 180.0;
+  Rmax = graphmode.coords.crval1 + 180.0;
+  Rmid = 0.5*(Rmin + Rmax);
+
+  NPTS = 200;
+  ALLOCATE (Xvec.elements, float, NPTS);
+  ALLOCATE (Yvec.elements, float, NPTS);
+  Npts = 0;
+   
+  regions = skylist[0].regions;
+  Nregions = skylist[0].Nregions;
+
+  for (i = 0; i < Nregions; i++) {
+    if (ShowAll || (stat (skylist[0].filename[i], &filestat) != -1)) {
+      if (VERBOSE) gprint (GP_ERR, "%3d %s %6.2f - %6.2f, %6.2f - %6.2f\n", i, regions[i][0].name, 
+			    regions[i][0].Rmin, regions[i][0].Rmax, regions[i][0].Dmin, regions[i][0].Dmax);
+
+      leftside = -1;
+      RD_to_XYpic (&X[0], &Y[0], regions[i][0].Rmin, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[1], &Y[1], regions[i][0].Rmin, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[2], &Y[2], regions[i][0].Rmax, regions[i][0].Dmax, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+      RD_to_XYpic (&X[3], &Y[3], regions[i][0].Rmax, regions[i][0].Dmin, &graphmode.coords, Rmin, Rmax, Rmid, &leftside);
+
+      Xvec.elements[Npts] = X[0];
+      Yvec.elements[Npts] = Y[0];
+      for (j = 1; j < 4; j++) {
+	Xvec.elements[Npts + j*2 - 0] = X[j];
+	Yvec.elements[Npts + j*2 - 0] = Y[j];
+	Xvec.elements[Npts + j*2 - 1] = X[j];
+	Yvec.elements[Npts + j*2 - 1] = Y[j];
+      }
+      Xvec.elements[Npts+7] = Xvec.elements[Npts];
+      Yvec.elements[Npts+7] = Yvec.elements[Npts];
+      Npts += 8;
+      if (Npts > NPTS - 1) {  /* this is OK because NPTS is made always a multiple of 8 */
+	NPTS += 200;
+	REALLOCATE (Xvec.elements, float, NPTS);
+	REALLOCATE (Yvec.elements, float, NPTS);
+      }
+    }
+  }
+
+  gprint (GP_ERR, "plotting %d catalogs\n", Npts/8);
+  Xvec.Nelements = Xvec.Nelements = Npts;
+  if (Npts > 0) {
+    graphmode.style = 2; /* points */
+    graphmode.ptype = 100; /* connect pairs of points */
+    graphmode.etype = 0;
+    PlotVectorPair (kapa, Npts, Xvec.elements, Yvec.elements, &graphmode);
+  }
+
+  free (Xvec.elements);
+  free (Yvec.elements);
+  free (regions);
+
+  return (TRUE);
+
+}
+
+
+int RD_to_XYpic (double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside) {
+
+  while (r < Rmin) { r += 360.0; }
+  while (r > Rmax) { r -= 360.0; }
+
+  if (*leftside == -1) {
+    *leftside = (r < Rmid);
+  } else {
+    if (  *leftside && (r > Rmid + 90)) { r -= 360.0; }
+    if (! *leftside && (r < Rmid - 90)) { r += 360.0; }
+  }
+
+  RD_to_XY (x, y, r, d, coords);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycoverage.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycoverage.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skycoverage.c	(revision 17422)
@@ -0,0 +1,146 @@
+# include "dvoshell.h"
+
+int skycoverage (int argc, char **argv) {
+
+  int i, N, Nimage, status, TimeSelect, ByName, xs, ys;
+  time_t tzero, tend;
+  double pixscale, dPix, r, d, Xi, Yi, Xs, Ys, x[2], y[2], trange;
+  Image *image;
+  char name[256];
+  float *V;
+  int Nx, Ny;
+  Buffer *buf;
+  Coords coords;
+
+  ByName = FALSE;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (name, argv[N]);
+    remove_argument (N, &argc, argv);
+    ByName = TRUE;
+  }
+
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-trange"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tend)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    trange = tend - tzero;
+    if (trange < 0) {
+      trange = fabs (trange);
+      tzero -= trange;
+    }
+    TimeSelect = TRUE;
+  }
+ 
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: skycoverage (buffer) (pixscale) (dPix) [-time start range] [-name name]\n");
+    gprint (GP_ERR, "       (buffer) saves bitmapped AIT plot\n");
+    gprint (GP_ERR, "       (pixscale) specifies the pixel size in degrees\n");
+    gprint (GP_ERR, "       note: we need 64800 / (pixscale)^2 pixels to represent the sky\n");
+    return (FALSE);
+  }
+  
+  if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  pixscale = atof(argv[2]);
+  dPix     = atof(argv[3]);
+
+  Nx = 360/pixscale;
+  Ny = 180/pixscale;
+
+  gfits_free_matrix (&buf[0].matrix);
+  gfits_free_header (&buf[0].header);
+  CreateBuffer (buf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (buf[0].file, "(empty)");
+
+  coords.crval1 = 180;
+  coords.crval2 = 0;
+  coords.crpix1 = 0.5*Nx;
+  coords.crpix2 = 0.5*Ny;
+  strcpy (coords.ctype, "DEC--AIT");
+  coords.pc1_1 = coords.pc2_2 = 1;
+  coords.pc1_2 = coords.pc2_1 = 0;
+  coords.cdelt1 = coords.cdelt2 = pixscale;
+  coords.Npolyterms = 0;
+
+  PutCoords (&coords, &buf[0].header);
+
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  V = (float *)buf[0].matrix.buffer;
+  bzero (V, Nx*Ny*sizeof(float));
+
+  for (ys = 0; ys < Ny; ys++) {
+    for (xs = 0; xs < Nx; xs++) {
+      status = XY_to_RD (&r, &d, (double)(xs), (double)(ys), &coords);
+      status &= (r > 0);
+      status &= (r < 360);
+      if (status) {
+	V[ys*Nx + xs] = 2;
+      }
+    }
+  }
+
+  for (i = 0; i < Nimage; i++) {
+    if (ByName && strcmp (name, image[i].name)) continue;
+    if (TimeSelect && ((image[i].tzero < tzero) || (image[i].tzero+image[i].trate*image[i].NY > tzero + trange))) continue;
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+
+    /* project this image to screen display coords */
+    /* DIS images represent a field, not a chip */
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) {
+      x[0] = -0.5*image[i].NX; y[0] = -0.5*image[i].NY;
+      x[1] = +0.5*image[i].NX; y[1] = +0.5*image[i].NY;
+    } else {
+      x[0] = 0;                y[0] = 0;
+      x[1] = image[i].NX;      y[1] = image[i].NY;
+    }
+    status = FALSE;
+    
+    for (Yi = y[0] + 0.5*dPix; Yi < y[1]; Yi += dPix) {
+      for (Xi = x[0] + 0.5*dPix; Xi < x[1]; Xi += dPix) {
+	XY_to_RD (&r, &d, Xi, Yi, &image[i].coords);
+	while (r <   0.0) { r += 360.0; }
+	while (r > 360.0) { r -= 360.0; }
+	status = RD_to_XY (&Xs, &Ys, r, d, &coords);
+	if (status) {
+	  xs = (int)Xs;
+	  ys = (int)Ys;
+	  V[ys*Nx + xs] = 1;
+	}
+      }
+    }
+  }
+
+  free (image);
+  return (TRUE);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skydbtile.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skydbtile.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skydbtile.c	(revision 17422)
@@ -0,0 +1,283 @@
+# include "dvo2.h"
+SkyRegion *SkyDivideList (SkyRegion *list, int Nlist);
+
+/* region names:
+
+ depth Ntables  Name 
+ 0     1      	fullsky.cpt
+ 1     16     	n????.cpt, s????.cpt
+ 2     256    	n????/r????.cpt
+ 3     4096   	n????/t????.cpt
+ 4     65536  	n????/r????/r????.cpt
+ 5     1.0+06  	n????/t????/t????.cpt
+ 6     1.7+07  	n????/r????/r????/r????.cpt
+
+ depth Ntables  Name 
+ 0     1      	fullsky
+ 1     16     	d%02
+ 2     256    	d%02/r%03d
+ 3     4096   	d%02/t%04d
+ 4     65536  	d%02/r%03d/r%03d
+ 5     1.0+06  	d%02/t%04d/t%04d
+ 6     1.7+07  	d%02/r%03d/r%03d/r%03d
+ 7     2.7+08  	d%02/t%04d/t%04d/t%04d
+
+*/
+
+/* a valid SkyRegion set must always start with the fullsky as the first entry, 
+   with the sequence of entries ordered primarily by depth, followed by parent 
+*/
+
+int SkyMakeNames (SkyRegion *db, SkyRegion *ref, int depth) {
+
+  int i, j, N, Ns, Ne, ns, ne;
+
+  if (db == NULL) return (FALSE);
+  if (depth == 0) {
+    if (db[0].depth != 0) return (FALSE);
+    strcpy (db[0].name, "fullsky");
+    Ns = db[0].childS;
+    Ne = db[0].childE;
+    for (i = Ns; i < Ne; i++) {
+      sprintf (db[i].name, "d%02d", i - 1);
+      if (!SkyMakeNames (db, &db[i], 2)) return (FALSE);
+      if (!SkyMakeNames (db, &db[i], 3)) return (FALSE);
+    }
+    return (TRUE);
+  }
+
+  if (ref == NULL) return (FALSE);
+  
+  if (depth == 2) {
+    if (!ref[0].child) return (TRUE);
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++, N++) {
+      sprintf (db[i].name, "%s/r%02d", ref[0].name, N);
+      if (!SkyMakeNames (db, &db[i], depth + 2)) return (FALSE);
+    }
+  }
+  
+  if (depth > 2) {
+    Ns = ref[0].childS;
+    Ne = ref[0].childE;
+    N = 0;
+    for (i = Ns; i < Ne; i++) {
+      if (!ref[0].child) continue;
+      ns = db[i].childS;
+      ne = db[i].childE;
+      for (j = ns; j < ne; j++, N++) {
+	if (depth % 2) {
+	  sprintf (db[j].name, "%s/t%03d", ref[0].name, N);
+	} else {				      			    
+	  sprintf (db[j].name, "%s/r%02d", ref[0].name, N);
+	}
+	if (!SkyMakeNames (db, &db[j], depth + 2)) return (FALSE);
+      }
+    }  
+  }
+}
+
+SkyRegion *SkyMakeRegions (int Nlevels, int *nlist) {
+
+  int Nlist, Nnext, Ncurr, i, j;
+  SkyRegion *list, *curr, *next;
+
+  Nlist = 0;
+  Nnext = 1;
+  Ncurr = 1;
+  ALLOCATE (list, SkyRegion, Ncurr);
+  ALLOCATE (curr, SkyRegion, Ncurr);
+
+  curr[0].Rmin =   0;
+  curr[0].Rmax = 360;
+  curr[0].Dmin = -90;
+  curr[0].Dmax =  90;
+  curr[0].depth = 0;
+  curr[0].object = FALSE;
+  curr[0].image = FALSE;
+  curr[0].child = FALSE;
+  curr[0].childS = 0;
+  curr[0].childE = 0;
+
+  for (i = 1; i < Nlevels; i++) {
+    next  = SkyDivideRegions (curr, &Nnext);
+    for (j = 0; j < Nnext; j++) {
+      next[j].depth = i;
+    }   
+    REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+    memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+    for (j = Nlist; j < Nlist + Ncurr; j++) {
+      list[j].childS += Nlist + Ncurr;
+      list[j].childE += Nlist + Ncurr;
+    }
+    Nlist += Ncurr;
+    free (curr);
+    curr = next;
+    Ncurr = Nnext;
+  }
+  REALLOCATE (list, SkyRegion, Nlist + Ncurr);
+  memcpy (&list[Nlist], curr, Ncurr*sizeof(SkyRegion));
+  Nlist += Ncurr;
+  free (curr);
+
+  list[0].object = TRUE;
+  list[0].image = TRUE;
+  *nlist = Nlist;
+  return (list);
+}
+
+SkyRegion *SkyDivideRegions (SkyRegion *parents, int *N) {
+
+  int i, Nchildren, Nparents, Np;
+  SkyRegion *children, *temp;
+  float *child, *index;
+
+  Nchildren = Nparents = *N;
+
+  /* copy input list to a new list, set child to be sequence number */
+  ALLOCATE (children, SkyRegion, Nchildren);
+  memcpy (children, parents, Nchildren*sizeof(SkyRegion));
+  for (i = 0; i < Nchildren; i++) {
+    children[i].childS = i;
+    parents[i].childS = i;
+  }
+
+  /* double the number of children, dividing only the largest in half each pass */
+  for (i = 0; i < 4; i++) {
+    temp = SkyDivideList (children, Nchildren);
+    free (children);
+    children = temp;
+    Nchildren *= 2;
+  }
+
+  /* sort children by childS (currently is parent entry) */
+  ALLOCATE (child, float, Nchildren);
+  ALLOCATE (index, float, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    index[i] = i;
+    child[i] = children[i].childS;
+  }
+  fsortpair (child, index, Nchildren);
+  ALLOCATE (temp, SkyRegion, Nchildren);
+  for (i = 0; i < Nchildren; i++) {
+    temp[i] = children[(int)index[i]];
+  }
+  free (children);
+  free (child);
+  free (index);
+  children = temp;
+  
+  /* look for all matching values of childS, setup parent values of childS, childE */
+  Np = children[0].childS;
+  parents[Np].childS = 0;
+  parents[Np].child = TRUE;
+  for (i = 0; i < Nchildren; i++) {
+    if (children[i].childS != Np) {
+      parents[Np].childE = i;
+      Np = children[i].childS;
+      parents[Np].childS = i;
+      parents[Np].child = TRUE;
+    }
+    children[i].childS = 0;
+    children[i].childE = 0;
+    children[i].child = FALSE;
+  }
+  parents[Np].childE = i;
+  *N = Nchildren;
+  return (children);
+}
+
+/* always break the region with the largest area */
+SkyRegion *SkyDivideBiggest (SkyRegion *list, int *nlist) {
+
+  int i, N, Nlist;
+  float *area, *indx, dec;
+  SkyRegion *out, save;
+
+  Nlist = *nlist;
+  ALLOCATE (area, float, Nlist);
+  ALLOCATE (indx, float, Nlist);
+  for (i = 0; i < Nlist; i++) {
+    dec = 0.5*(list[i].Dmax + list[i].Dmin);
+    area[i] = (list[i].Rmax - list[i].Rmin)*(list[i].Dmax - list[i].Dmin)*cos(RAD_DEG*dec);
+    indx[i] = i;
+  }
+  fsortpair (area, indx, Nlist);
+
+  N = indx[Nlist - 1];
+  out = SkyDivide (&list[N]);
+  
+  REALLOCATE (list, SkyRegion, Nlist + 1);
+  list[N] = out[0];
+  list[Nlist] = out[1];
+
+  *nlist = Nlist + 1;
+  free (out);
+  free (area);
+  free (indx);
+  return (list);
+}
+
+SkyRegion *SkyDivide (SkyRegion *in) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+
+  ALLOCATE (out, SkyRegion, 2);
+
+  out[0] = in[0];
+  out[1] = in[0];
+
+  RA  = 0.5*(in[0].Rmin + in[0].Rmax);
+  DEC = 0.5*(in[0].Dmin + in[0].Dmax);
+  if ((DEC >= 90) || (DEC <= -90)) {
+    gprint (GP_ERR, "error in Dmin");
+    return (NULL);
+  }
+ 
+  D_div = (in[0].Rmax - in[0].Rmin) * cos (RAD_DEG*DEC);
+  R_div = (in[0].Dmax - in[0].Dmin);
+  if (R_div < D_div) {
+    /* divide RA range */
+    out[0].Rmax = RA;
+    out[1].Rmin = RA;
+  } else {
+    out[0].Dmax = DEC;
+    out[1].Dmin = DEC;
+  }
+
+  return (out);
+}
+
+/* return a list with 2x as many entries, each split in two */
+SkyRegion *SkyDivideList (SkyRegion *in, int Nin) {
+
+  SkyRegion *out;
+  double RA, DEC, R_div, D_div;
+  int i;
+
+  ALLOCATE (out, SkyRegion, 2*Nin);
+
+  for (i = 0; i < Nin; i++) {
+    /* inherit properties from parent */
+    out[2*i + 0] = in[i];
+    out[2*i + 1] = in[i];
+
+    RA  = 0.5*(in[i].Rmin + in[i].Rmax);
+    DEC = 0.5*(in[i].Dmin + in[i].Dmax);
+    
+    /* split along longest axis */
+    D_div = (in[i].Rmax - in[i].Rmin) * cos (RAD_DEG*DEC);
+    R_div = (in[i].Dmax - in[i].Dmin);
+    if (R_div < D_div) {
+      out[2*i + 0].Rmax = RA;
+      out[2*i + 1].Rmin = RA;
+    } else {
+      out[2*i + 0].Dmax = DEC;
+      out[2*i + 1].Dmin = DEC;
+    }
+  }
+  return (out);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skyregion.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skyregion.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/skyregion.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "dvoshell.h"
+
+static double RAs = 0.0;
+static double RAe = 0.0;
+static double DECs = 0.0;
+static double DECe = 0.0;
+
+// define the sky region for which extractions are limited
+int skyregion (int argc, char **argv) {
+  
+  if (argc == 1) {
+    gprint (GP_ERR, "current skyregion: %f - %f : %f - %f\n", RAs, RAe, DECs, DECe);
+    gprint (GP_ERR, "USAGE:  skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+    return (FALSE);
+  }
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: skyregion (min RA) (max RA) (min DEC) (max DEC)\n");
+    return (FALSE);
+  }
+
+  set_skyregion (atof(argv[1]), atof(argv[2]), atof(argv[3]), atof(argv[4]));
+
+  return (TRUE);
+}
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De) {
+
+  *Rs = RAs;
+  *Re = RAe;
+  *Ds = DECs;
+  *De = DECe;
+
+  return TRUE;
+}
+
+int set_skyregion (double Rs, double Re, double Ds, double De) {
+
+  RAs  = Rs;
+  RAe  = Re;
+  DECs = Ds;
+  DECe = De;
+
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/subpix.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/subpix.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/subpix.c	(revision 17422)
@@ -0,0 +1,154 @@
+# include "dvoshell.h"
+
+int subpix (int argc, char **argv) {
+  
+  int i, j, I, Nlo, Nhi, Nentry, Nstars, Nimage, Nmeasure;
+  int *index, *entry;
+  int Nmin, Nsub, NSUB, status;
+  int TimeFormat;
+  time_t Timage, TimeReference;
+  double X, Y, Mabs, t;
+  double Ra, Dec, Radius, Radius2, r, Rmin;
+  double *RA, *DEC;
+  
+  SkyTable *sky;
+  SkyList *skylist;
+  Measure *measure;
+  Image *image;
+  Catalog catalog;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: subpix ra dec radius\n");
+    return (FALSE);
+  }
+  if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
+  if (Ra < 0) Ra += 360.0;
+  if (Ra > 360.0) Ra -= 360.0;
+  Radius = atof (argv[3]);
+
+  /* load star nearest position */
+  sky = GetSkyTable ();
+  skylist = SkyListByRadius (sky, -1, Ra, Dec, Radius);
+  if (skylist[0].Nregions > 1) {
+    gprint (GP_ERR, "warning, radius overlaps region boundary, not yet implemented\n");
+  }
+
+  /* lock, load, unlock catalog */
+  catalog.filename = skylist[0].filename[0];
+  catalog.catflags = LOAD_AVES | LOAD_MEAS;
+  catalog.Nsecfilt = 0;
+
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      fprintf (stderr, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+  }
+  dvo_catalog_unlock (&catalog);
+
+  /* quick search of star list for Ra, Dec */
+  Nstars = catalog.Naverage;
+  ALLOCATE (RA, double, Nstars);
+  ALLOCATE (DEC, double, Nstars);
+  ALLOCATE (index, int, Nstars);
+  for (i = 0; i < Nstars; i++) {
+    RA[i] = catalog.average[i].R;
+    DEC[i] = catalog.average[i].D;
+    index[i] = i;
+  }
+  if (Nstars > 1) sort_coords_index (DEC, RA, index, Nstars);
+
+  /* bracket the DEC range of interest */
+  Nlo = bracket (DEC, Nstars, FALSE, Dec - Radius);
+  Nhi = bracket (DEC, Nstars, TRUE,  Dec + Radius);
+  ALLOCATE (entry, int, MAX (Nhi - Nlo, 1));
+  Nentry = 0;
+
+  /* find the list of stars */
+  Radius2 = Radius*Radius;
+  for (i = Nlo; i < Nhi; i++) {
+    r = SQ(Dec - DEC[i]) + SQ(Ra - RA[i]);
+    if (r < Radius2) {
+      entry[Nentry] = i;
+      Nentry ++;
+    }
+  }
+  if (!Nentry) {
+    gprint (GP_ERR, "no stars found\n");
+    free (RA);
+    free (DEC);
+    free (entry);
+    free (index);
+    dvo_catalog_free (&catalog);
+    SkyListFree (skylist);
+    return (TRUE);
+  }
+
+  /* find the closest star */
+  Nmin = 0;
+  Rmin = SQ(Dec - DEC[entry[0]]) + SQ(Ra - RA[entry[0]]);
+  for (i = 1; i < Nentry; i++) {
+    r = SQ(Dec - DEC[entry[i]]) + SQ(Ra - RA[entry[i]]);
+    if (r < Rmin) {
+      Rmin = r;
+      Nmin = i;
+    }
+  }
+  Nentry = index[entry[Nmin]];
+  Ra = RA[entry[Nmin]];
+  Dec = DEC[entry[Nmin]];
+  gprint (GP_ERR, "finding subpix values for star @ %f %f\n", Ra, Dec);
+
+  free (RA);
+  free (DEC);
+  free (entry);
+  free (index);
+
+  /* storage for the image references */
+  Nsub = 0;
+  NSUB = 100;
+  ALLOCATE (index, int, NSUB);
+
+  /* load all images, extract those touching Ra, Dec */
+  if ((image = LoadImages (&Nimage)) == NULL) return (FALSE);
+  BuildChipMatch (image, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    if (!FindMosaicForImage (image, Nimage, i)) continue;
+    status = RD_to_XY (&X, &Y, Ra, Dec, &image[i].coords);
+    if (!status || (X < 0) || (X > image[i].NX) || (Y < 0) || (Y > image[i].NY)) continue;
+    index[Nsub] = i;
+    Nsub ++;
+    if (Nsub == NSUB - 1) {
+      NSUB += 100;
+      REALLOCATE (index, int, NSUB);
+    }
+  }
+
+  /* only print the entries for existing measurements of this star */ 
+  measure = &catalog.measure[catalog.average[Nentry].measureOffset];
+  Nmeasure = catalog.average[Nentry].Nmeasure;
+  for (i = 0; i < Nsub; i++) {
+    I = index[i];
+    if (!FindMosaicForImage (image, Nimage, I)) continue;
+    Timage = image[I].tzero;
+    for (j = 0; j < Nmeasure; j++) {
+      if (measure[j].t == Timage) { 
+	Mabs = PhotCat (&measure[j]);
+	RD_to_XY (&X, &Y, Ra, Dec, &image[I].coords);
+	t = TimeValue (measure[j].t, TimeReference, TimeFormat);
+	gprint (GP_LOG, "%f %6.3f %7.2f %7.2f %5.3f\n", t, Mabs, X, Y, image[I].secz);
+      } 
+    }
+  }
+
+  dvo_catalog_free (&catalog);
+  free (image);
+  free (index);
+  SkyListFree (skylist);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/dvo/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/dvo/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/dvo/version.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "dvoshell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "dvo version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libdvo version: %s\n", (tmp = strip_version (libdvo_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/astro.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/astro.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/astro.h	(revision 17422)
@@ -0,0 +1,34 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "data.h"
+
+# ifndef ASTRO_H
+# define ASTRO_H
+
+void InitAstro ();
+void FreeAstro ();
+
+typedef struct {
+  int    isIdentity;	      // identity transformation 
+  double phi;		      // saved in radians
+  double Xo;		      // saved in radians
+  double xo;		      // saved in degrees
+  double sin_phi_cos_Xo;      // pre-computed values
+  double sin_phi_sin_Xo;      // pre-computed values
+  double cos_phi_cos_Xo;      // pre-computed values
+  double cos_phi_sin_Xo;      // pre-computed values
+  double cos_phi;	      // pre-computed values
+  double sin_phi;	      // pre-computed values
+  double cos_Xo;	      // pre-computed values
+  double sin_Xo;	      // pre-computed values
+} CoordTransform;
+
+typedef enum {COORD_NONE, COORD_CELESTIAL, COORD_GALACTIC, COORD_ECLIPTIC} CoordTransformSystem;
+
+CoordTransform *InitTransform (CoordTransformSystem input, CoordTransformSystem output);
+int ApplyTransform (double *x, double *y, double X, double Y, CoordTransform *transform);
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/basic.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/basic.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/basic.h	(revision 17422)
@@ -0,0 +1,8 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+
+void cleanup ();
+void InitBasic ();
+void FreeBasic ();
+void InitBasic_PantasksClient ();
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/convert.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/convert.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/convert.h	(revision 17422)
@@ -0,0 +1,25 @@
+# include "external.h"
+
+# ifndef CONVERT_H
+# define CONVERT_H
+
+/*** time/coord conversion functions not supplied by libohana ***/
+time_t        TimeRef               PROTO((double time, time_t TimeReference, int TimeFormat));
+double        TimeValue             PROTO((time_t time, time_t TimeReference, int TimeFormat));
+
+int           hh_hms                PROTO((double hh, int *hr, int *mn, double *sc));
+int           dd_dms                PROTO((double dd, int *dg, int *mn, double *sc));
+int           hms_format            PROTO((char *line, double value));
+int           dms_format            PROTO((char *line, double value));
+int           hh_hm                 PROTO((double hh, int *hr, double *mn));
+int           day_to_sec            PROTO((char *string, time_t *second));
+int           hms_to_sec            PROTO((char *string, time_t *second));
+char         *ohana_sec_to_hms      PROTO((time_t second));
+char         *ohana_sec_to_day      PROTO((time_t second));
+
+char         *meade_deg_to_str      PROTO((double deg));
+char         *meade_ra_to_str       PROTO((double deg));
+char         *meade_dec_to_str      PROTO((double deg));
+char         *strptime              PROTO((const char *s, const char *format, struct tm *tm));
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/data.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/data.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/data.h	(revision 17422)
@@ -0,0 +1,160 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+
+# ifndef DATA_H
+# define DATA_H
+
+/*** typedef structs used by math functions ***/
+typedef struct {
+  int   NLINES;
+  int   Nlines;
+  char **lines;
+  char  *name;
+} Queue;
+
+typedef struct {
+  char *name;
+  int NWORDS;
+  int Nwords;
+  char **words;
+  char **value;
+} Page;
+
+typedef struct {
+  char *name;
+  int NPAGES;
+  int Npages;
+  Page **pages;
+  // int *index; (why did I define this?  is it not used?)
+  char **pageIDs;
+} Book;
+
+void InitData ();
+void FreeData ();
+
+/* in book.c */
+void InitBooks ();
+void InitBook (Book *book, char *name);
+void FreeBook (Book *book);
+Book *FindBook (char *name);
+Book *GetBook (int where);
+Book *CreateBook (char *name);
+int DeleteBook (Book *book);
+void ListBooks ();
+
+/* in page.c */
+void InitPage (Page *page, char *name);
+void FreePage (Page *page);
+Page *FindPage (Book *book, char *name);
+Page *GetPage (Book *book, int where);
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue);
+Page *CreatePage (Book *book, char *name);
+int ShufflePages (Book *book);
+int DeletePage (Book *book, Page *page);
+void ListPages (Book *book);
+void ListWords (Page *page);
+int BookSetWord (Page *page, char *word, char *value);
+char *BookGetWord (Page *page, char *word);
+
+/* in queues.c */
+void InitQueues ();
+void ListQueues ();
+Queue *FindQueue (char *name);
+Queue *CreateQueue (char *name);
+void PushQueue (Queue *queue, char *line);
+void PushNamedQueue (char *name, char *line);
+char *PopQueue (Queue *queue);
+char *PopQueueMatch (Queue *queue, char *Key, char *value);
+void PushQueueUnique (Queue *queue, char *line, char *Key);
+void PushQueueReplace (Queue *queue, char *line, char *Key);
+int InitQueue (Queue *queue);
+int DeleteQueue (Queue *queue);
+int PrintQueue (Queue *queue);
+
+/* in fft.c */
+void fft1D (float *dataRe, float *dataIm, int N, int Nbit, int forward);
+int fftND (float *dataRe, float *dataIm, int Ndim, int *Nsize, int forward);
+int IsBinary (int N, int *Nbit);
+
+/* in spline.c */
+void spline_construct (float *x, float *y, int N, float *y2);
+float spline_apply (float *x, float *y, float *y2, int N, float X);
+
+/* in svdcmp.c */
+int svdcmp (float *a, float *w, float *v, int Nx, int Ny);
+
+/* mrqmin.c */
+float mrqcof (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float *, int, float *));
+
+float mrqmin (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE);
+
+float mrqinit (float *x, float *y, float *dy, int Npts, 
+	       float *par, int Npar, 
+	       float (funcs)(float, float *, int, float *), int VERBOSE);
+
+float **mrqcovar (int Npar);
+
+void mrqfree (int Npar);
+
+/* mrq2dmin.c */
+float mrq2dcof (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, float **ta, float **tb, 
+		float (funcs)(float, float, float *, int, float *));
+
+float mrq2dmin (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *), int VERBOSE);
+
+float mrq2dinit (float *x, float *t, float *y, float *dy, int Npts, 
+		 float *par, int Npar, 
+		 float (funcs)(float, float, float *, int, float *), int VERBOSE);
+
+float mrq2dchi (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *));
+
+int mrq2dlimits (float *pmin, float *pmax, int Npar);
+
+float **mrq2dcovar (int Npar);
+
+void mrq2dfree (int Npar);
+
+/* gaussian.c */
+double gaussian (double x, double mean, double sigma);
+void gauss_init (int Nbin);
+double rnd_gauss (double mean, double sigma);
+
+/* starfuncs.c */
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max);
+int set_rough_radii (double Ra, double Ri, double Ro);
+int get_rough_star (float *data, int Nx, int Ny, int x, int y, float *xc, float *yc, float *sx, float *sy, float *sxy, float *zs, float *zp, float *sk);
+
+/* precess.c */
+double BtoJ (double in_epoch);
+double get_epoch (char *in_epoch, char mode);
+
+/* graphtools.c */
+void          SetLimits             PROTO((Vector *xvec, Vector *yvec, Graphdata *graphmode));
+void          SetLimitsRaw          PROTO((float *xvec, float *yvec, int Npts, Graphdata *graphmode));
+void          ApplyLimits           PROTO((int Xgraph, Graphdata *graphmode, int apply));
+int           style_args            PROTO((Graphdata *graphmode, int *argc, char **argv, int *kapa));
+
+int read_table_vectors (int argc, char **argv, char *extname);
+
+void *db_getConnection ();
+
+int bracket (double *list, int Nlist, int mode, double value);
+int ibracket (int *list, int Nlist, int mode, double value);
+
+void FreeKapa ();
+void FreeQueues ();
+void FreeBooks ();
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/dimm.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/dimm.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/dimm.h	(revision 17422)
@@ -0,0 +1,71 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
+# include "sbig.h"
+
+void InitDIMM ();
+
+/* telescope.c */
+double distSky (double r1, double r2, double d1, double d2);
+int getRD (double *r, double *d) ;
+int gotoRD (double r, double d);
+int offset (char *direction, double distance);
+int toffset (char *direction, char *rate, double duration);
+int getXY (double *x, double *y);
+int setRD (double r, double d);
+int setSite (char *sitename, double lon, double lat);
+int setTime (char *lst);
+int getSite (double *lon, double *lat, double *lst);
+int ParkScope();
+int SleepScope();
+int WakeScope();
+
+/* Serial.c */
+int SerialVerbose (int mode);
+int SerialInit (char *port);
+int SerialOpen (char *port);
+int SerialBaudRate (int fdesc, int rate);
+int SerialParity (int fdesc, int parity); 
+int SerialDataBits(int fdesc, int bits); 
+int SerialStopBit(int fdesc, int stpbit); 
+void SerialStop (int fdesc);
+int SerialCommand (char *in, char **out, int wait);
+
+/* camera.c */
+int InitCamera (int port);
+void DumpCameraInfo ();
+void CameraFullSize (int *x, int *y);
+int SetTemperature (double temp);
+double GetTemperature ();
+int DumpCameraStatus ();
+int Exposure (double exptime);
+static int readout_callback (float percent);
+int ReadOut (int x, int y, int dx, int dy, int binning, unsigned short *buffer);
+int OpenShutter ();
+int CloseShutter ();
+
+  /* this image structure stuff is considered for further development */ 
+# if (0)
+/** this Image is incompatible with the DVO Image struct */
+typedef struct {
+
+  /* image data area */
+  int Nx, Ny;
+  char *buffer;
+  int Nbytes;
+
+  /* image metadata */
+  double ccdtemp;
+  double airtemp;
+  double ra, dec, airmass;
+  double exptime;
+  int binning;
+} Image;
+
+/* analysis.c */
+int subtractImage (Image *a, Image *b);
+void statsImage (Image *image, Stats *stats);
+void findStars (Image *image, Stars **stars, int *Nstars, double threshold);
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/display.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/display.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/display.h	(revision 17422)
@@ -0,0 +1,39 @@
+# include "external.h"
+# include "kapa.h"
+
+# ifndef DISPLAY_H
+# define DISPLAY_H
+
+/*** kapa graph functions ***/
+int           PlotVectorPair        PROTO((int kapa, int Npts, float *xValues, float *yValues, Graphdata *graphmode));
+int           PlotVectorTriplet     PROTO((int kapa, int Npts, float *xValues, float *yValues, float *zValues, Graphdata *graphmode));
+int           GetGraphData          PROTO((Graphdata *data, int *kapa, char *name));
+int           GetGraph              PROTO((Graphdata *data, int *kapa, char *name));
+int           SetGraph              PROTO((Graphdata *data));
+
+/*** kapa image functions */
+int           GetImageData          PROTO((KapaImageData *data, int *kapa, char *name));
+int           GetImage              PROTO((KapaImageData *data, int *kapa, char *name));
+int           SetImage              PROTO((KapaImageData *data));
+
+void	      QuitKapa              PROTO(());
+void	      InitKapa		    PROTO(());
+int 	      open_kapa		    PROTO((int entry));
+int 	      close_kapa	    PROTO((char *name));
+int 	      AddKapaDevice	    PROTO((char *name));
+int 	      DelKapaDevice	    PROTO((char *name));
+int 	      FindKapaDevice	    PROTO((char *name));
+char	     *GetKapaName	    PROTO(());
+
+/* calling program need to define a function 'get_variable' which
+ * returns the name of the executable for each of KAPA and KII
+ */
+char         *get_variable          PROTO((char *name));
+
+int SendLabel (char *string, int Xgraph, int mode);
+
+int SendGraphMessage (int device, char *format, ...);
+int SendGraphCommand (int device, int length, char *format, ...);
+int SendGraphCommandV (int device, int length, char *format, va_list argp);
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/dvomath.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/dvomath.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/dvomath.h	(revision 17422)
@@ -0,0 +1,120 @@
+/*** dvomath.h ***/
+
+# ifndef DVOMATH_H
+# define DVOMATH_H
+
+# define NCHARS 256
+
+enum {ANYVECTOR, NEWVECTOR, OLDVECTOR};
+enum {ANYBUFFER, NEWBUFFER, OLDBUFFER};
+
+typedef struct {			/* representation of a variable (0-D) */
+  char     *name;
+  char     *value;
+} Variable;
+
+typedef struct {			/* representation of a vector (1-D) */
+  char name[1024];
+  float *elements;
+  int Nelements;
+} Vector;
+
+typedef struct {			/* representation of buffer (image) */
+  char name[1024];
+  char file[1024];
+  Header header;
+  Matrix matrix;
+  int  bitpix, unsign;
+  double bscale, bzero;
+} Buffer;
+
+typedef struct {			/* math stack structure */
+  char   *name;
+  char    type;
+  float  *ptr;
+  Buffer *buffer;
+  Vector *vector;
+  float   Float;
+} StackVar;
+
+/* math functions */
+char         *dvomath               PROTO((int argc, char **argv, int *size, int maxsize));
+char        **isolate_elements      PROTO((int argc, char **argv, int *nstack));
+StackVar     *convert_to_RPN        PROTO((int argc, char **argv, int *nstack));
+int           check_stack           PROTO((StackVar *stack, int Nstack, int validsize));
+int           evaluate_stack        PROTO((StackVar *stack, int *Nstack));
+void          init_stack            PROTO((StackVar *stack));
+void          copy_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          move_stack	    PROTO((StackVar *stack1, StackVar *stack2));
+void          clean_stack	    PROTO((StackVar *stack, int Nstack));
+void          delete_stack	    PROTO((StackVar *stack, int Nstack));
+void          clear_stack 	    PROTO((StackVar *stack));
+void          assign_stack 	    PROTO((StackVar *stack, char *name, int type));
+
+int           VV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           VM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           MS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SM_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           SS_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           WW_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
+int           S_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           V_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+int           M_unary               PROTO((StackVar *OUT, StackVar *V1, char *op));
+
+/* variable handling */
+char         *get_variable          PROTO((char *name));
+char         *get_variable_ptr      PROTO((char *name));
+char         *get_local_variable_ptr PROTO((char *name));
+double        get_double_variable   PROTO((char *name, int *found));
+int           get_int_variable      PROTO((char *name, int *found));
+int           DeleteNamedScalar     PROTO((char *name));
+int           IsScalar              PROTO((char *name));
+int           set_variable          PROTO((char *name, double dvalue));
+int           set_int_variable      PROTO((char *name, int ivalue));
+int           set_str_variable      PROTO((char *name, char *value));
+int           set_local_variable    PROTO((char *name, char *value));
+void          InitVariables         PROTO((void));
+void          ListVariables         PROTO((void));
+float         get_variable_default  PROTO((char *name, float dvalue));
+int           SelectScalar          PROTO((char *string, double *value));
+
+/* vector handling */
+Vector       *InitVector            PROTO((void));
+void          InitVectors           PROTO((void));
+int           CopyVector            PROTO((Vector *out, Vector *in));
+int           MoveVector            PROTO((Vector *out, Vector *in));
+int           DeleteVector          PROTO((Vector *vec));
+int           CopyNamedVector       PROTO((char *out, char *in));
+int           MoveNamedVector       PROTO((char *out, char *in));
+int           DeleteNamedVector     PROTO((char *name));
+int           IsVector              PROTO((char *name));
+int           IsVectorPtr           PROTO((Vector *vec));
+int           ListVectors           PROTO((void));
+Vector       *SelectVector          PROTO((char *name, int mode, int verbose));
+
+/* buffer handling */
+Buffer       *InitBuffer            PROTO((void));
+void          InitBuffers           PROTO((void));
+int           CopyBuffer            PROTO((Buffer *out, Buffer *in));
+int           MoveBuffer            PROTO((Buffer *out, Buffer *in));
+int           DeleteBuffer          PROTO((Buffer *buf));
+int           CopyNamedBuffer       PROTO((char *out, char *in));
+int           MoveNamedBuffer       PROTO((char *out, char *in));
+int           DeleteNamedBuffer     PROTO((char *name));
+int           IsBuffer              PROTO((char *name));
+int           IsBufferPtr           PROTO((Buffer *buf));
+int           PrintBuffers          PROTO((int Long));
+int           CreateBuffer          PROTO((Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale));
+Buffer       *SelectBuffer          PROTO((char *name, int mode, int verbose));
+void          dump_buffers          PROTO((int n));  /* deprecated? */
+int           SelectOverlay         PROTO((char *name, int *number));
+
+/* why are these in here? */
+int           gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2);
+int           GetTimeFormat         PROTO((time_t *TimeReference, int *TimeFormat));
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/dvoshell.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/dvoshell.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/dvoshell.h	(revision 17422)
@@ -0,0 +1,268 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# ifndef DVOSHELL_H
+# define DVOSHELL_H
+
+/* magnitude types */
+enum {MAG_NONE, 
+      MAG_INST, 
+      MAG_CAT, 
+      MAG_SYS, 
+      MAG_REL, 
+      MAG_CAL, 
+      MAG_AVE, 
+      MAG_REF, 
+      MAG_ERR, 
+      MAG_CHISQ,
+      MAG_NCODE,
+      MAG_NPHOT,
+};
+
+/* measure fields */
+enum {MEAS_ZERO, 
+      MEAS_GLON, 
+      MEAS_GLAT, 
+      MEAS_GLON_AVE, 
+      MEAS_GLAT_AVE,
+      MEAS_ELON, 
+      MEAS_ELAT, 
+      MEAS_ELON_AVE, 
+      MEAS_ELAT_AVE,
+      MEAS_RA, 
+      MEAS_DEC, 
+      MEAS_RA_AVE, 
+      MEAS_DEC_AVE,
+      MEAS_RA_AVE_ERR, 
+      MEAS_DEC_AVE_ERR, 
+      MEAS_U_RA, 
+      MEAS_U_DEC, 
+      MEAS_U_RA_ERR, 
+      MEAS_U_DEC_ERR, 
+      MEAS_PAR, 
+      MEAS_PAR_ERR, 
+      MEAS_RA_OFFSET, 
+      MEAS_DEC_OFFSET, 
+      MEAS_RA_FIT_OFFSET, 
+      MEAS_DEC_FIT_OFFSET, 
+      MEAS_RA_OFFSET_ERR, 
+      MEAS_DEC_OFFSET_ERR, 
+      MEAS_XP, 
+      MEAS_NMEAS, 
+      MEAS_NMISS, 
+      MEAS_OBJFLAGS, 
+      MEAS_MAG, 
+      MEAS_MINST, 
+      MEAS_MCAT, 
+      MEAS_MSYS, 
+      MEAS_MREL, 
+      MEAS_MCAL, 
+      MEAS_EXPTIME, 
+      MEAS_AIRMASS, 
+      MEAS_ALT, 
+      MEAS_AZ, 
+      MEAS_PHOTCODE, 
+      MEAS_TIME, 
+      MEAS_FWHM, 
+      MEAS_FWHM_MAJ, 
+      MEAS_FWHM_MIN, 
+      MEAS_THETA, 
+      MEAS_DOPHOT, 
+      MEAS_DB_FLAGS, 
+      MEAS_PHOT_FLAGS, 
+      MEAS_XCCD, 
+      MEAS_YCCD, 
+      MEAS_XMOSAIC, 
+      MEAS_YMOSAIC, 
+      MEAS_SKY, 
+      MEAS_dSKY, 
+      MEAS_DET_ID, 
+      MEAS_OBJ_ID, 
+      MEAS_IMAGE_ID, 
+      MEAS_PSF_QF, 
+      MEAS_PSF_CHISQ, 
+      MEAS_CR_NSIGMA, 
+      MEAS_EXT_NSIGMA, 
+      MEAS_STARGAL, 
+};
+
+/* average fields */
+enum {AVE_ZERO, 
+      AVE_RA, 
+      AVE_DEC, 
+      AVE_RA_ERR, 
+      AVE_DEC_ERR, 
+      AVE_GLON, 
+      AVE_GLAT, 
+      AVE_ELON, 
+      AVE_ELAT, 
+      AVE_U_RA, 
+      AVE_U_DEC, 
+      AVE_U_RA_ERR, 
+      AVE_U_DEC_ERR, 
+      AVE_PAR, 
+      AVE_PAR_ERR, 
+      AVE_Xp, 
+      AVE_NMEAS, 
+      AVE_NMISS, 
+      AVE_NPHOT, 
+      AVE_NCODE, 
+      AVE_MAG, 
+      AVE_dMAG, 
+      AVE_Xm, 
+      AVE_FLAG, 
+      AVE_TYPE, 
+      AVE_TYPEFRAC,
+      AVE_OBJID
+};
+
+enum {DVO_TABLE_AVERAGE, DVO_TABLE_MEASURE};
+
+// options for selecting the ra,dec limits of the db selections
+typedef struct {
+  char *name;
+  char *list;
+  int useDisplay;
+  int useSkyregion;
+} SkyRegionSelection;
+
+// a single db field 
+typedef struct {
+  char *name;
+  int extract;
+  int table;
+  int ID;
+  int magMode;
+  PhotCode *photcode;
+} dbField;
+
+// db boolean operations
+typedef struct {
+  char   *name;
+  char    type;
+  int     field;
+  float   Float;
+} dbStack;
+
+typedef struct {
+  char name[64];
+  double RA0, RA1, DEC0, DEC1;
+} RegionFile;
+
+typedef struct {
+  double X;
+  double Y;
+  double R;
+  double D;
+  double M, dM;
+  char   dophot;
+  double sky;
+  double fx, fy, df;
+  double Mgal, Map;
+  int found;
+  short int code;
+  e_time t;
+} CMPstars;
+
+/*** dvo prototypes ***/
+int           DetermineTypeCode     PROTO((Average *average, Measure *measure, int code));
+double        DetermineTypefrac     PROTO((Average *average, Measure *measure, PhotCode *code));
+double        ExtractAverages       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int param));
+double       *ExtractByDMag         PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractDMag           PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist));
+double       *ExtractMagnitudes     PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *n));
+double       *ExtractMeasures       PROTO((PhotCode *code, int mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractMeasuresByDMag PROTO((PhotCode **code, int *mode, int use_first, Average *average, SecFilt *secfilt, Measure *measure, int *nlist, int param));
+double       *ExtractMeasuresDMag   PROTO((PhotCode **code, int *mode, Average *average, SecFilt *secfilt, Measure *measure, int *nlist));
+void          FreeImageSelection    PROTO(());
+void          FreeImageSelection    PROTO(());
+int           GetAverageParam       PROTO((char *parname));
+void          GetAverageParamHelp   PROTO(());
+int           GetMagMode            PROTO((char *string));
+double        GetMeasure            PROTO((int param, Average *average, Measure *measure, double mag));
+int           GetMeasureParam       PROTO((char *parname));
+int           GetMeasureTypeCode    PROTO((Measure *measure));
+int           GetPhotcodeInfo       PROTO((char *string, PhotCode **Code, int *Mode));
+int           GetSelectionParam     PROTO(());
+int           GetTimeSelection      PROTO((time_t *tz, time_t *te));
+void          InitDVO               PROTO(());
+void          FreeDVO               PROTO(());
+int           InitPhotcodes         PROTO(());
+Image        *LoadImages            PROTO((int *Nimage));
+Image        *MatchImage            PROTO((unsigned int time, short int source));
+Coords       *MatchMosaic           PROTO((unsigned int time, short int source));
+int           Quality               PROTO((Measure *measure, int IsDophot));
+int           SelectMags            PROTO((int Nphot, int Tphot, int Ns, Average *average, Measure *measure, SecFilt *secfilt, int UL));
+
+SkyList      *SelectRegions         PROTO((SkyRegionSelection *selection));
+SkyList      *SkyListLoadFile       PROTO((char *filename));
+int           SetCATDIR             PROTO((char *path, int verbose));
+char *        GetCATDIR             PROTO(());
+SkyTable     *GetSkyTable           PROTO(());
+SkyList      *SkyListFromFile       PROTO((char *filename));
+SkyRegionSelection *SetRegionSelection    PROTO((int *argc, char **argv));
+
+int           SetImageSelection     PROTO((int mode, SkyRegionSelection *selection));
+int           SetPhotSelections     PROTO((int *argc, char **argv, int Nparams));
+int           SetSelectionParam     PROTO((int param));
+int           TestAverage           PROTO((PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure));
+int           TestPhotSelections    PROTO((PhotCode **code, int *mode, int param));
+void          compare               PROTO((Catalog *catlog1, Catalog *catlog2, Vector *rvec,  Vector *dvec,  Vector *mvec, Vector *drvec, Vector *ddvec, Vector *dmvec, double radius));
+void          cprecess              PROTO((Average *average, int Naverage, double in_epoch, double out_epoch));
+void          image_subset          PROTO((Image *image, int Nimage, int **Subset, int *Nsubset, SkyRegionSelection *selection, unsigned long int tzero, double trange, int TimeSelect));
+int           match_image           PROTO((Image *image, int Nimage, unsigned int T, short int S));
+int           match_image_subset    PROTO((Image *image, int *subset, int Nsubset, unsigned int T, short int S));
+void          print_value           PROTO((double value, short int ival));
+void          sort_image_subset     PROTO((Image *image, int *subset, int N));
+void          sort_images           PROTO((Image *image, int N));
+void          sortave               PROTO((Average *ave, int N));
+CMPstars     *cmpReadFits           PROTO((FILE *f, int *nstars));
+CMPstars     *cmpReadText           PROTO((FILE *f, int *nstars));
+int           RD_to_XYpic           PROTO((double *x, double *y, double r, double d, Coords *coords, double Rmin, double Rmax, double Rmid, int *leftside));
+
+// dvo DB field functions
+dbField     *dbCmdlineFields        PROTO((int argc, char **argv, int table, int *last, int *nfields));
+dbStack     *dbRPN                  PROTO((int argc, char **argv, int *nstack));
+int          dbCheckStack           PROTO((dbStack *stack, int Nstack, int table, dbField **inFields, int *Nfields));
+int          dbBooleanCond          PROTO((dbStack *inStack, int NinStack, float *fields));
+void         dbInitStack            PROTO((dbStack *stack));
+void 	     dbFreeStack            PROTO((dbStack *stack, int Nstack));
+void 	     dbFreeEntry            PROTO((dbStack *stack));
+void         dbFreeTempEntry        PROTO((dbStack *stack));
+
+dbStack     *dbBinary               PROTO((dbStack *V1, dbStack *V2, char *op, float *fields));
+dbStack     *dbUnary                PROTO((dbStack *V1, char *op, float *fields));
+
+int          GetMagMode             PROTO((char *string));
+PhotCode    *ParsePhotcodeField     PROTO((char *field, int *mode, int def));
+int          ParseMeasureField      PROTO((dbField *field, char *fieldName));
+int          ParseAverageField      PROTO((dbField *field, char *fieldName));
+
+double       dbExtractAverages      PROTO((Average *average, SecFilt *secfilt, Measure *measure, dbField *field));
+double       dbExtractMeasures      PROTO((Average *average, SecFilt *secfilt, Measure *measure, dbField *field));
+void         dbExtractMeasuresInit  PROTO(());
+
+void 	     dbInitField            PROTO((dbField *field));
+void 	     dbFreeFields           PROTO((dbField *fields, int Nfields));
+int          dbAstroRegionLimits    PROTO((dbStack **stack, int *nstack, SkyRegionSelection *selection, int table));
+char        *strfloat               PROTO((float value));
+
+int get_skyregion (double *Rs, double *Re, double *Ds, double *De);
+int set_skyregion (double Rs, double Re, double Ds, double De);
+void FreeImageSelection ();
+
+void FreeSkyRegionSelection (SkyRegionSelection *selection);
+int wordhash (char *word);
+
+int dbExtractAverageInitTransform (CoordTransformSystem target);
+int dbExtractAverageInit ();
+
+int dbExtractMeasuresInitTransform (CoordTransformSystem target);
+int dbExtractMeasuresInitAve ();
+int dbExtractMeasuresInitMeas ();
+
+int dbExtractAveragesInitTransform (CoordTransformSystem target);
+int dbExtractAveragesInit ();
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/external.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/external.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/external.h	(revision 17422)
@@ -0,0 +1,30 @@
+# include <signal.h>
+# include <unistd.h>
+# include <sys/uio.h>
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/time.h>
+# include <time.h>
+# include <errno.h>
+# include <pthread.h>
+
+# include <netinet/ip.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+
+# include <ohana.h>
+# include <dvo.h>
+
+/* provide missing external defines */
+# ifdef MISSING_SOCKET_INFO
+#   define F_SETFL         4   
+#   define O_NONBLOCK       0200000  
+#   define AF_UNIX         1          
+#   define SOCK_STREAM     1          
+#   define ENOENT          2 
+# endif
+
+# define strlen(A) ((int)strlen(A))
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/hstgsc.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/hstgsc.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/hstgsc.h	(revision 17422)
@@ -0,0 +1,27 @@
+
+static double BigDecBounds[] = {0.0, 7.5, 15.0, 22.5, 30.0, 37.5, 45.0, 
+				52.5, 60.0, 67.5, 75.0, 82.5, 90.0,
+				0.0, -7.5, -15.0, -22.5, -30.0, -37.5, -45.0, 
+				-52.5, -60.0, -67.5, -75.0, -82.5, -90.0};
+static char *Dec2Sections[] = {"n0000", "n0730", "n1500", "n2230", "n3000", "n3730", "n4500", 
+			       "n5230", "n6000", "n6730", "n7500", "n8230", "weirdness", 
+			       "s0000", "s0730", "s1500", "s2230", "s3000", "s3730", "s4500", 
+			       "s5230", "s6000", "s6730", "s7500", "s8230", "weirdness"};
+
+static int NDecLines[] = {593, 584, 551, 530, 522, 465, 406, 362, 280, 198, 123, 24, 
+			  0, 597, 578, 574, 577, 534, 499, 442, 376, 294, 212, 144, 48};
+
+/** older data layout concepts 
+static char *DecSections[] = {"N0000", "N0730", "N1500", "N2230", "N3000", "N3730", "N4500", 
+			      "N5230", "N6000", "N6730", "N7500", "N8230", "weirdness", 
+			      "S0000", "S0730", "S1500", "S2230", "S3000", "S3730", "S4500", 
+			      "S5230", "S6000", "S6730", "S7500", "S8230", "weirdness"};
+
+static char *disk[] = {"disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", 
+		       "disk 1", "disk 1", "disk 1", "disk 1", "disk 1", "weirdness", 
+		       "disk 1", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", 
+		       "disk 2", "disk 2", "disk 2", "disk 2", "disk 2", "weirdness"};
+
+static int NBigRASections [] = {48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3, 48, 47, 45, 43, 40, 36, 32, 28, 21, 15, 9, 3, 3};
+
+**/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/imfit.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/imfit.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/imfit.h	(revision 17422)
@@ -0,0 +1,20 @@
+# include "astro.h"
+
+int Npar;
+int Nfpar;
+float *par;
+float *fpar;
+float *sky;
+
+float (*fitfunc)(float, float, float *, int, float *);
+void (*imfit_cleanup)();
+
+void fgauss_setup (char *name);
+void pgauss_setup (char *name);
+void sgauss_setup (char *name);
+void qgauss_setup (char *name);
+void qfgauss_setup (char *name);
+void qrgauss_setup (char *name);
+void pgauss_psf_setup (char *name);
+void qgauss_psf_setup (char *name);
+void sgauss_psf_setup (char *name);
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/mana.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/mana.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/mana.h	(revision 17422)
@@ -0,0 +1,11 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# ifndef MANA_H
+
+void InitMana ();
+void FreeMana ();
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks);
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/opihi.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/opihi.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/opihi.h	(revision 17422)
@@ -0,0 +1,5 @@
+# include "external.h"
+# include "shell.h"
+# include "dvomath.h"
+# include "convert.h"
+# include "display.h"
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/pantasks.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/pantasks.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/pantasks.h	(revision 17422)
@@ -0,0 +1,280 @@
+# include "data.h"
+# include "basic.h"
+# include "astro.h"
+
+# include <sys/time.h>
+# include <time.h>
+# include <zlib.h>
+
+# define DEBUG 0
+
+typedef enum {
+  JOB_NONE,
+  JOB_BUSY, 
+  JOB_EXIT, 
+  JOB_HUNG,
+  JOB_CRASH,
+  JOB_PENDING,
+} JobStat;
+
+typedef enum {
+  JOB_LOCAL, 
+  JOB_CONTROLLER, 
+} JobMode;
+
+typedef enum {
+  CONTROLLER_HUNG = -1,
+  CONTROLLER_DOWN = 0,
+  CONTROLLER_GOOD = 1,
+} ControllerStat;
+
+enum {RANGE_ABS, RANGE_DAY, RANGE_WEEK};
+enum {TIMER_ALLJOBS, TIMER_SUCCESS, TIMER_FAILURE};
+
+enum {TASK_NONE, 
+      TASK_EMPTY, 
+      TASK_COMMENT, 
+      TASK_NMAX, 
+      TASK_ACTIVE, 
+      TASK_TRANGE, 
+      TASK_END, 
+      TASK_HOST, 
+      TASK_STDOUT, 
+      TASK_STDERR, 
+      TASK_COMMAND, 
+      TASK_OPTIONS, 
+      TASK_PERIODS, 
+      TASK_NPENDING, 
+      TASK_EXIT, 
+      TASK_EXEC
+} TaskHashResults;
+
+typedef struct {
+  time_t start;
+  time_t stop;
+  char type;
+  char include;
+  int Nmax;
+  int Nrun;
+} TimeRange;
+
+/* a task is a description of the wrapping of a job */
+typedef struct {
+  Macro  *exec;				/* name is 'exec' */
+  Macro  *crash;			/* name is 'crash' */
+  Macro  *timeout;
+  Macro  *defexit;
+
+  int     NEXIT;
+  int     Nexit;
+  Macro **exit;				/* name is exit status */
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  char   *host;
+  int     host_required;
+
+  char   *name;
+
+  char   *stdout_dump;
+  char   *stderr_dump;
+
+  int       Nranges;
+  TimeRange *ranges;
+
+  int     Nmax;  // only construct Ntotal jobs for this task
+  int     Njobs;
+
+  int     Npending;  // number of currently pending jobs
+  int     NpendingMax;  // max number of pending jobs allowed
+
+  float   poll_period;
+  float   exec_period;
+  float   timeout_period;
+
+  struct timeval last;
+
+  int Nsuccess;
+  int Nfailure;
+  int Ntimeout;
+
+  double dtimeAve_alljobs, dtimeMin_alljobs, dtimeMax_alljobs;
+  double dtimeAve_success, dtimeMin_success, dtimeMax_success;
+  double dtimeAve_failure, dtimeMin_failure, dtimeMax_failure;
+
+  int active;
+
+} Task;
+
+// time period include/exclude periods: 
+// date ranges (e_time - e_time) 
+// time ranges (e_time - e_time) (use e_time % 86400)
+// time-of-week ranges  (e_time - e_time
+// -trange Mon Fri (inclusive on days -- end defaults to Fri@23:59:59)
+// -trange 08:00 - 17:00
+// -trange 2005/12/24 2005/12/31
+// be careful of HST!
+// type: day, week, date
+// keep: TRUE: perform action within this time period
+// keep: FALSE: do not perform action within this time period
+  
+typedef struct {
+  int JobID;				/* internal ID for job */
+  int pid;				/* external ID for job */
+
+  struct timeval last;
+  struct timeval start;
+  int state;
+  int exit_status;
+
+  int     argc;
+  char  **argv;
+
+  int     optc;
+  char  **optv;
+
+  Task   *task;
+
+  /* this cries out for another structure... */
+  IOBuffer    stdout_buff;    /* stdout storage buffer */
+  char       *stdout_dump;    // output target file for stdout
+  int         stdout_size;    /* size of pending stdout buffer (controller) */
+  int         stdout_fd;      /* stdout pipe (local only) */
+
+  IOBuffer    stderr_buff;    /* stderr storage buffer */
+  char       *stderr_dump;    // output target file for stderr
+  int         stderr_size;    /* size of pending stderr buffer (controller) */
+  int         stderr_fd;      /* stderr pipe (local only) */
+
+  JobMode     mode;			/* local or controller? */
+  char   *realhost;
+
+  double dtime;
+} Job;
+
+# define CONTROLLER_PROMPT "pcontrol:"
+
+/* scheduler prototypes */
+
+void InitPantasks ();
+void FreePantasks ();
+
+void InitPantasksServer ();
+void FreePantasksServer ();
+
+void InitPantasksClient ();
+void FreePantasksClient ();
+
+void InitTasks ();
+void FreeTasks ();
+
+Task *NextTask ();
+Task *FindTask (char *name);
+void ListTasks (int verbose);
+int ShowTask (char *name);
+int FreeTask (Task *task);
+Task *CreateTask (char *name);
+int ValidateTask (Task *task, int RequireStatic);
+int RegisterNewTask ();
+int DeleteNewTask ();
+Task *GetNewTask ();
+Task *GetActiveTask ();
+void SetTaskTimer (struct timeval *timer);
+double GetTaskTimer (struct timeval start, int verbose);
+void InitTaskTimers ();
+int TaskHash (char *input);
+int RemoveTask (Task *task);
+Task *SetNewTask (Task *task);
+void ListTaskStats (char *regex);
+void ResetTaskStats (char *regex);
+void UpdateTaskTimerStats (Task *task, int mode, double dtime);
+
+int NextJobID ();
+void InitJobIDs ();
+void FreeJobIDs ();
+int FreeJobID (int ID);
+
+void InitJobs ();
+void FreeJobs ();
+
+Job *NextJob ();
+Job *FindJob (int JobID);
+void ListJobs ();
+Job *CreateJob (Task *task);
+int SubmitJob (Job *job);
+int CheckJob (Job *job);
+int DeleteJob (Job *job);
+void FreeJob (Job *job);
+
+float CheckJobs ();
+float CheckTasks ();
+int CheckSystem ();
+int CheckController ();
+int CheckTimeRanges (TimeRange *ranges, int Nranges);
+int BumpTimeRanges (TimeRange *ranges, int Nranges);
+
+int GetJobOutput (char *channel, int pid, IOBuffer *buffer, int Nbytes);
+int CheckControllerJob (Job *job);
+int CheckControllerJobStatus (Job *job);
+int SubmitControllerJob (Job *job);
+int DeleteControllerJob (Job *job);
+Job *FindControllerJob (int JobID);
+int StartController ();
+int ControllerCommand (char *command, char *response, IOBuffer *buffer);
+int SubmitLocalJob (Job *job);
+int CheckLocalJob (Job *job);
+int CheckLocalJobStatus (Job *job);
+void InitTaskTimers ();
+CommandF *FindControllerCommand (char *cmd);
+int QuitController ();
+int StopController ();
+int VerboseMode ();
+int KillLocalJob (Job *job);
+int CheckControllerOutput ();
+int PrintControllerOutput ();
+
+int KillControllerJob (Job *job);
+int CheckControllerStatus ();
+void gotsignal (int signum);
+int client_shell (int argc, char **argv);
+
+void InitClients ();
+void AddNewClient (int client);
+int  DeleteClient (int client);
+void *ListenClients (void *data);
+
+// functions related to the server threads
+void CheckTasksSetState (int state);
+int CheckTasksGetState ();
+void *CheckTasksThread (void *data);
+
+void CheckJobsSetState (int state);
+int CheckJobsGetState ();
+void *CheckJobsThread (void *data);
+
+void CheckControllerSetState (int state);
+int CheckControllerGetState ();
+void *CheckControllerThread (void *data);
+
+void CheckInputsSetState (int state);
+int CheckInputsGetState ();
+void *CheckInputsThread (void *data);
+
+// functions related to the queue of input files
+void InitInputs ();
+void FreeInputs ();
+void AddNewInput (char *input);
+int DeleteInput (char *input);
+void CheckInputs ();
+
+void SerialThreadLock ();
+void SerialThreadUnlock ();
+
+int InitPassword ();
+int CheckPassword (int BindSocket);
+
+int FlushJobs ();
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/pclient.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/pclient.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/pclient.h	(revision 17422)
@@ -0,0 +1,55 @@
+# include "data.h"
+# include "basic.h"
+
+/** pclient global data **/
+
+/** child status values **/
+enum {
+  PCLIENT_NONE,
+  PCLIENT_BUSY,  
+  PCLIENT_EXIT,
+  PCLIENT_CRASH,
+};
+
+int ChildPID;				/** current child PID **/
+int ChildStatus;			/** current status of child **/
+int ChildExitStatus;			/** recent exit status of child **/
+
+/** child may be in the following states:
+    
+PCLIENT_NONE
+PCLIENT_BUSY
+PCLIENT_EXIT
+PCLIENT_CRASH
+
+allowed transitions:
+
+PCLIENT_NONE  ->    PCLIENT_BUSY  (start a job)
+
+PCLIENT_BUSY  ->    PCLIENT_EXIT  (job complete)
+PCLIENT_BUSY  ->    PCLIENT_CRASH (job crashed)
+
+PCLIENT_EXIT  ->    PCLIENT_NONE  (cleanup)
+PCLIENT_CRASH ->    PCLIENT_NONE  (cleanup)
+
+**/
+
+/** file descriptors for child I/O **/
+int child_stdin_fd[2];
+int child_stdout_fd[2];
+int child_stderr_fd[2];
+
+/** buffers for I/O storage **/
+IOBuffer child_stdout;
+IOBuffer child_stderr;
+
+int InitChild ();
+int FreeChild ();
+int CheckChild ();
+void CheckChildStatus ();
+
+void InitPclient ();
+void FreePclient ();
+void gotsignal (int signum);
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/pcontrol.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/pcontrol.h	(revision 17422)
@@ -0,0 +1,229 @@
+# include "data.h"
+# include "basic.h"
+# define THREADED
+
+typedef struct timeval Ptime;
+typedef unsigned long long IDtype;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_PENDING,
+  PCONTROL_JOB_BUSY,  
+  PCONTROL_JOB_HUNG,  
+  PCONTROL_JOB_DONE,  
+  PCONTROL_JOB_KILL,  
+  PCONTROL_JOB_EXIT,
+  PCONTROL_JOB_CRASH,
+} JobStat;
+
+/** job status values **/
+typedef enum {
+  PCONTROL_JOB_ANYHOST,
+  PCONTROL_JOB_WANTHOST,
+  PCONTROL_JOB_NEEDHOST,
+} JobMode;
+
+/** host status values **/
+typedef enum {
+  PCONTROL_HOST_IDLE,
+  PCONTROL_HOST_BUSY,  
+  PCONTROL_HOST_DOWN,
+  PCONTROL_HOST_DONE,
+  PCONTROL_HOST_OFF,
+} HostStat;
+
+typedef enum {
+  PCONTROL_RUN_UNKNOWN,
+  PCONTROL_RUN_NONE,
+  PCONTROL_RUN_HOSTS,
+  PCONTROL_RUN_REAP,
+  PCONTROL_RUN_ALL,
+} RunLevels;
+
+/* stack special positions */
+typedef enum {
+  STACK_TOP = 0,
+  STACK_BOTTOM = -1,
+} StackWhere;
+
+typedef enum {
+  PCLIENT_HUNG = -1,
+  PCLIENT_DOWN = 0,
+  PCLIENT_GOOD = 1,
+} PclientStat;
+
+typedef struct {
+  char *buffer;
+  int   Nalloc;
+  int   Nmaxread;
+  int   Nextra;
+  int   Nlast;
+  int   Nbuffer;
+} Fifo;
+
+/* data to define a job */
+typedef struct {
+  int          argc; 
+  char       **argv;
+  char        *hostname;
+  char        *realhost;
+  int          exit_status;
+  int          Reset;
+  int          stdout_size;
+  int          stderr_size;
+  JobMode      mode;
+  JobStat      state;
+  JobStat      stack;
+  IOBuffer     stdout_buff;
+  IOBuffer     stderr_buff;
+  Ptime        start;
+  Ptime        stop;
+  double       dtime;
+  int          pid;
+  IDtype       JobID;
+  struct Host *host;
+} Job;
+
+/* data to define a host machine */
+typedef struct {
+  char       *hostname;
+  int         stdin_fd;
+  int         stdout_fd;
+  int         stderr_fd;
+  int         markoff;
+  int         pid;
+  HostStat    stack;
+  Ptime       lasttry;
+  Ptime       nexttry;
+  IDtype      HostID;
+  struct Job *job;
+} Host;
+
+typedef struct {
+  void **object;
+  char **name;
+  int   *id;
+  int    Nobject;
+  int    NOBJECT;
+# ifdef THREADED    
+  pthread_mutex_t mutex;
+# endif
+} Stack;
+
+/* XXX if this is hard-wired, we can't change shell name in StartHost */
+# define PCLIENT_PROMPT "pclient:"
+
+// # define FREE(X) if (X != NULL) { free (X); }
+# define CLOSE(FD) { if (FD) close (FD); FD = 0; }
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+# define ZTIME(A) ((A.tv_sec == 0) && (A.tv_usec == 0))
+
+# define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }}
+# define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); abort (); }
+// # define ASSERT(TEST,STRING) { if (!(TEST)) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }}
+// # define ABORT(STRING) { gprint (GP_ERR, "programming error: %s\n", STRING); raise (SIGINT); exit (2); }
+
+void InitPcontrol ();
+void FreePcontrol ();
+
+/*** StackOps.c ***/
+Stack *InitStack ();
+void   FreeStack (Stack *stack);
+int    PushStack (Stack *stack, int where, void *object, int id, char *name);
+void  *PullStackByLocation (Stack *stack, int where);
+void  *PullStackByName (Stack *stack, char *name);
+void  *PullStackByID (Stack *stack, int id);
+int    RemoveStackEntry (Stack *stack, int where);
+void  *RemoveStackByID (Stack *stack, int id);
+void   LockStack (Stack *stack);
+void   UnlockStack (Stack *stack);
+
+// void  *FindStackByID (Stack *stack, int id);
+// void  *FindStackByName (Stack *stack, char *name);
+
+/*** CheckSystem.c ***/
+int   CheckSystem ();
+void *CheckSystem_Threaded (void *data);
+int   CheckBusyJobs (float delay);
+int   CheckDoneJobs (float delay);
+int   CheckKillJobs (float delay);
+int   CheckDoneHosts (float delay);
+int   CheckDownHosts (float delay);
+int   CheckIdleHosts (float delay);
+int   CheckLiveHosts (float delay);
+int   SetRunSystem (int state);
+RunLevels SetRunLevel (RunLevels level);
+
+/*** own files ***/
+int CheckHost (Host *host);
+int StartHost (Host *host);
+int CheckIdleHost (Host *host);
+int CheckDoneHost (Host *host);
+int CheckBusyJob (Job *job, Host *host);
+int CheckDoneJob (Job *job, Host *host);
+int KillJob (Job *job, Host *host);
+int StartJob (Job *job, Host *host);
+int ResetJob (Job *job);
+int GetJobOutput (char *command, Host *host, IOBuffer *buffer, int Nbytes);
+int PclientCommand (Host *host, char *command, char *response, IOBuffer *buffer);
+int rconnect (char *command, char *hostname, char *shell, int *stdio);
+
+/*** misc files ***/
+int    VerboseMode ();  // in verbose.c
+void   gotsignal (int signum); // in pcontrol.c
+
+/*** IDops.c ***/
+void InitIDs ();
+IDtype NextJobID ();
+IDtype NextHostID ();
+void PrintID (gpDest dest, IDtype ID);
+
+/*** CheckPoint.c ***/
+int SetCheckPoint ();
+int ClearCheckPoint ();
+int TestCheckPoint ();
+
+/*** HostOps.c ***/
+void   InitHostStacks ();
+void   FreeHostStacks ();
+Stack *GetHostStack (int StackID);
+char  *GetHostStackName (int StackID);
+Stack *GetHostStackByName (char *name);
+int    PutHost (Host *host, int StackID, int where);
+Host  *PullHostByID (IDtype HostID, int *StackID);
+Host  *PullHostByName (char *name, int *StackID);
+Host  *PullHostFromStackByID (int StackID, IDtype ID);
+Host  *PullHostFromStackByName (int StackID, char *name);
+IDtype AddHost (char *hostname);
+void   DelHost (Host *host);
+
+/*** StopHosts.c ***/
+void   DownHost (Host *host);
+void   OffHost (Host *host);
+int    DownHosts ();
+int    StopHost (Host *host);
+int    HarvestHost (int pid);
+
+/*** JobOps.c ***/
+void   InitJobStacks ();
+void   FreeJobStacks ();
+Stack *GetJobStack (int StackID);
+char  *GetJobStackName (int StackID);
+Stack *GetJobStackByName (char *name);
+int    PutJob (Job *job, int StackID, int where);
+int    PutJobSetState (Job *job, int StackID, int where, int state);
+Job   *PullJobByID (IDtype JobID, int *StackID);
+Job   *PullJobFromStackByID (int StackID, int ID);
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv);
+void   DelJob (Job *job);
+Host  *UnlinkJobAndHost (Job *job);
+void   LinkJobAndHost (Job *job, Host *host);
+
+void pcontrol_exit (int n);
+
+// Job   *FindJobByID (IDtype JobID, int *StackID);
+// Job   *FindJobInStackByID (int StackID, int ID);
+// Host  *FindHostByID (IDtype HostID, int *StackID);
+// Host  *FindHostByName (char *name, int *StackID);
+// Host  *FindHostInStackByID (int StackID, IDtype ID);
+// Host  *FindHostInStackByName (int StackID, char *name);
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/shell.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/shell.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/shell.h	(revision 17422)
@@ -0,0 +1,183 @@
+/*** shell.h ***/
+# include "external.h"
+
+# ifndef SHELL_H
+# define SHELL_H
+
+# define ISVAR(a) (isalnum (a) || (a == ':') || (a == '_'))
+# define ISWORD(a,q) ((q) ? (a != '"') : (isalnum(a) || (a == '/') || (a == '.') || (a == '_') || (a == '-')))
+# define ISNUM(c) (isdigit(c) || (c == '-') || (c == '.'))
+
+# define MACRO_STRING(s) #s
+# define MACRO_NAME(s) MACRO_STRING(s)
+
+/* enums used by gprint functions */
+typedef enum {GP_FILE, GP_BUFF} gpMode;
+typedef enum {GP_LOG, GP_ERR} gpDest;
+
+typedef int CommandF ();
+
+typedef struct sockaddr_in SockAddress;
+
+/*** typedef structs used by shell functions ***/
+typedef struct {			/* basic opihi command structure */
+  char      real;
+  char     *name;
+  CommandF *func;    
+  char     *help;
+} Command;
+
+typedef struct {			/* a macro (collection of commands) */
+  char   *name;
+  char  **line;
+  int     Nlines;
+} Macro;
+
+typedef struct {			/* a list (macro/loop currently being executed) */
+  char **line;
+  int    n;
+  int    Nlines;
+  int    Nalloc;
+} List;
+
+/* structure used to represent the gprint i/o stream */
+typedef struct {
+  FILE *file;
+  char *name;
+  IOBuffer *buffer;
+  gpMode mode;
+  gpDest dest;
+  pthread_t thread;
+} gpStream;
+
+/*** globals used to track the shell language concepts  ***/
+int 	     interrupt;			/* true if C-C has been pressed */
+int 	     auto_break;		/* if true, zero exit status forces macros to escape */
+int 	     loop_next; 		/* set to true when next (or continue) is called */
+int 	     loop_last; 		/* set to true when last (or return) is called */
+int 	     loop_break;		/* set to true when break is called */
+int          is_script;                 /* being run within a shell script */
+
+/*** basic opihi shell functions ***/
+void          general_init            	PROTO((int *argc, char **argv));
+void          program_init            	PROTO((int *argc, char **argv));
+void          startup               	PROTO((int *argc, char **argv));
+int           opihi                     PROTO((int argc, char **argv));
+int           multicommand          	PROTO((char *line));
+int getServer ();
+void          multicommand_InitServer   PROTO((void));
+void multicommand_StopServer ();
+
+int           command               	PROTO((char *line, char **outline, int VERBOSE));
+char         *expand_vars           	PROTO((char *line));
+char         *expand_vectors        	PROTO((char *line));
+char         *parse                 	PROTO((char *line));
+char        **parse_commands        	PROTO((char *, int *));
+void          welcome                   PROTO(());
+
+int           add_listentry             PROTO((int ThisList, char *line));
+int           is_for_loop           	PROTO((char *line));
+int           is_if_block           	PROTO((char *line));
+int           is_list               	PROTO((char *line));
+int           is_loop               	PROTO((char *line));
+int           is_task               	PROTO((char *line));
+int           is_task_exit             	PROTO((char *line));
+int           is_task_exec             	PROTO((char *line));
+int           is_macro_create       	PROTO((char *line));
+void          InitLists                 PROTO(());
+int current_list_depth ();
+int increase_list_depth ();
+int decrease_list_depth ();
+char *get_next_listentry (int ThisList);
+
+void          InitCommands              PROTO(());
+void          AddCommand                PROTO((Command *new));
+int           DeleteCommand             PROTO((Command *command));
+Command      *MatchCommand              PROTO((char *name, int VERBOSE, int EXACT));
+void          sort_commands             PROTO((int *seq));
+
+void          SetCurrentMacroData	PROTO((char *name, int depth));
+Macro        *NewMacro			PROTO((char *name));
+int           DeleteMacro		PROTO((Macro *macro));
+Macro        *MatchMacro		PROTO((char *name, int VERBOSE, int EXACT));
+void          InitMacros                PROTO((void));
+char         *GetMacroName              PROTO((void));
+int           GetMacroDepth             PROTO((void));
+void          ListMacro                 PROTO((Macro *macro));
+void          ListMacros                PROTO(());
+void          FreeMacro                 PROTO((Macro *macro));
+CommandF     *find_macro_command        PROTO((char *name));
+
+int           exec_loop                 PROTO((Macro *loop));
+/* char         *get_next_listentry    	PROTO((int ThisList)); */
+/* char         *remove_listentry      	PROTO((int current)); */
+
+int           ConfigInit            	PROTO((int *argc, char **argv));
+void          ConfigFree            	PROTO((void));
+char         *VarConfig             	PROTO((char *keyword, char *mode, void *ptr));
+char         *VarConfigEntry           	PROTO((char *keyword, char *mode, int entry, void *ptr));
+
+int           init_error                PROTO((void));
+int           push_error                PROTO((char *line));
+int           print_error               PROTO((void));
+void          handle_interrupt      	PROTO((int));
+char        **command_completer     	PROTO((const char *, int, int));
+char         *command_generator     	PROTO((const char *text, int state));
+char        **completion_matches    	PROTO((char *, rl_compentry_func_t *));
+void          print_commands            PROTO((FILE *f));
+void          InitCommands              PROTO((void));
+
+/* command line parsing */
+char         *thisword              	PROTO((char *));
+char         *nextword              	PROTO((char *));
+char         *lastword              	PROTO((char *, char *));
+char         *thisvar               	PROTO((char *));
+char         *aftervar              	PROTO((char *));
+char         *lastvar               	PROTO((char *, char *));
+char         *thiscomm              	PROTO((char *));
+char         *nextcomm              	PROTO((char *));
+char         *opihi_append              PROTO((char *output, int *Noutput, char *start, char *stop));
+void          interpolate_slash         PROTO((char *line));
+
+/* macro functions (mapped to commands) */
+int 	      macro_create 		PROTO((int, char **)); 
+int 	      macro_delete 		PROTO((int, char **)); 
+int 	      macro_edit   		PROTO((int, char **));
+int 	      macro_exec   		PROTO((int, char **));
+int 	      macro_list_f 		PROTO((int, char **));  /* "macro_list" is a readline func */
+int 	      macro_read   		PROTO((int, char **));
+int 	      macro_write   		PROTO((int, char **));
+
+char 	     *memstr        		PROTO((char *m1, char *m2, int n));
+int  	      write_fmt     		PROTO((int fd, char *format, ...));
+char 	     *opihi_version 		PROTO(());
+char 	     *strip_version 		PROTO((char *input));
+
+/* gprint functions */
+void          gprintInit      		PROTO(());
+gpStream     *gprintGetStream 		PROTO((gpDest dest));
+void          gprintSetBuffer 		PROTO((gpDest dest));
+IOBuffer     *gprintGetBuffer 		PROTO((gpDest dest));
+void          gprintSetFileAllThreads   PROTO((gpDest dest, char *filename));
+void          gprintSetFileThisThread   PROTO((gpDest dest, char *filename));
+void          gprintSetFile   		PROTO((gpStream *stream, gpDest dest, char *filename));
+FILE         *gprintGetFile   		PROTO((gpDest dest));
+char         *gprintGetName   		PROTO((gpDest dest));
+int           gprint          		PROTO((gpDest dest, char *format, ...));
+int           gwrite          		PROTO((char *buffer, int size, int N, gpDest dest));
+
+/* socket functions */
+int InitServerSocket (SockAddress *Address);
+int WaitServerSocket (int InitSocket, SockAddress *Address);
+int GetClientSocket (char *hostname);
+int InitServerSocket_Named (char *hostname, SockAddress *Address);
+int DefineValidIP ();
+
+void FreeCommands ();
+void FreeMacros ();
+void FreeBuffers ();
+void FreeVectors ();
+void FreeVariables ();
+void FreeLists ();
+
+# endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/include/user.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/include/user.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/include/user.h	(revision 17422)
@@ -0,0 +1,260 @@
+int abszero         PROTO((int, char **));
+int applyfit        PROTO((int, char **));
+int applyfit2d      PROTO((int, char **));
+int badimages       PROTO((int, char **));
+int box             PROTO((int, char **));
+int calextract      PROTO((int, char **));
+int calmextract     PROTO((int, char **));
+int cals            PROTO((int, char **));
+int catlog          PROTO((int, char **));
+int ccd             PROTO((int, char **));
+int ccdextract      PROTO((int, char **));
+int center          PROTO((int, char **));
+int cgrid           PROTO((int, char **));
+int clear           PROTO((int, char **));
+int cmatch          PROTO((int, char **));
+int cmd             PROTO((int, char **));
+int cmdextract      PROTO((int, char **));
+int cmpread         PROTO((int, char **));
+int concat          PROTO((int, char **));
+int contour         PROTO((int, char **));
+int cplot           PROTO((int, char **));
+int create          PROTO((int, char **));
+int csystem         PROTO((int, char **));
+int ctimes          PROTO((int, char **));
+int cursor          PROTO((int, char **));
+int czplot          PROTO((int, char **));
+int datafile        PROTO((int, char **));
+int date            PROTO((int, char **));
+int ddmagextract    PROTO((int, char **));
+int ddmags          PROTO((int, char **));
+int delete          PROTO((int, char **));
+int device          PROTO((int, char **));
+int dmagaves        PROTO((int, char **));
+int dmagextract     PROTO((int, char **));
+int dmagmeas        PROTO((int, char **));
+int dmags           PROTO((int, char **));
+int dmt             PROTO((int, char **));
+int dumpmags        PROTO((int, char **));
+int elixir          PROTO((int, char **));
+int extract         PROTO((int, char **));
+int file            PROTO((int, char **));
+int fit             PROTO((int, char **));
+int fit2d           PROTO((int, char **));
+int gaussjordan     PROTO((int, char **));
+int gcat            PROTO((int, char **));
+int gimages         PROTO((int, char **));
+int grid            PROTO((int, char **));
+int gstar           PROTO((int, char **));
+int gtypes          PROTO((int, char **));
+int histogram       PROTO((int, char **));
+int images          PROTO((int, char **));
+int imbox           PROTO((int, char **));
+int imdata          PROTO((int, char **));
+int imdense         PROTO((int, char **)); 
+int imextract       PROTO((int, char **));
+int imlist          PROTO((int, char **));
+int imphot          PROTO((int, char **));
+int imrough         PROTO((int, char **));
+int imsearch        PROTO((int, char **));
+int imstats         PROTO((int, char **));
+int interpolate     PROTO((int, char **));
+int jpeg            PROTO((int, char **));
+int labels          PROTO((int, char **));
+int lcat            PROTO((int, char **));
+int lcurve          PROTO((int, char **));
+int limits          PROTO((int, char **));
+int list_buffers    PROTO((int, char **));
+int list_vectors    PROTO((int, char **));
+int mcreate         PROTO((int, char **));
+int mextract        PROTO((int, char **));
+int mget            PROTO((int, char **));
+int mset            PROTO((int, char **));
+int pcat            PROTO((int, char **));
+int photcodes       PROTO((int, char **));
+int photresid       PROTO((int, char **));
+int plot            PROTO((int, char **));
+int pmeasure        PROTO((int, char **));
+int precess         PROTO((int, char **));
+int print           PROTO((int, char **));
+int procks          PROTO((int, char **));
+int ps              PROTO((int, char **));
+int rd              PROTO((int, char **));
+int read_vectors    PROTO((int, char **));
+int region          PROTO((int, char **));
+int resid           PROTO((int, char **));
+int resize          PROTO((int, char **));
+int section         PROTO((int, char **));
+int set             PROTO((int, char **));
+int simage          PROTO((int, char **));
+int sort_vectors    PROTO((int, char **));
+int sprintf_opihi   PROTO((int, char **));
+int stats           PROTO((int, char **));
+int style           PROTO((int, char **));
+int subpix          PROTO((int, char **));
+int subraster       PROTO((int, char **));
+int subset          PROTO((int, char **));
+int textline        PROTO((int, char **));
+int tv              PROTO((int, char **));
+int uniq            PROTO((int, char **));
+int vectobuf        PROTO((int, char **));
+int vstat           PROTO((int, char **));
+int wd              PROTO((int, char **));
+int write_vectors   PROTO((int, char **));
+int zap             PROTO((int, char **));
+int zeropts         PROTO((int, char **));
+int zplot           PROTO((int, char **));
+
+static Command user[] = {  
+  {"abszero", 	   abszero,       "find filter zeropts"},
+  {"applyfit",     applyfit,      "apply fit to new vector"},
+  {"applyfit2d",   applyfit2d,    "apply 2-d fit to new vector"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"badimages",    badimages,     "look for images with anomalous astrometry"},
+  {"box",     	   box,           "draw a box on the plot"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calextract",   calextract,    "extract dmags"},
+  {"calmextract",  calmextract,   "extract dmags"},
+  {"cals",    	   cals,          "plot calibration data"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"catalog", 	   catlog,        "plot catalog stars"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccd",     	   ccd,           "plot color-color diagram"},
+  {"ccdextract",   ccdextract,    "extract star coords from color-color diagram"},
+  {"center",       center,        "center image on coords"},
+  {"center",       center,        "center image on coords"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"cgrid",   	   cgrid,         "plot sky coordinate grid"},
+  {"clear",   	   clear,         "erase plot"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmatch",  	   cmatch,        "match two catalogs"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmd",     	   cmd,           "plot cmd of stars in current region"},
+  {"cmdextract",   cmdextract,    "extract stars based on cmd regions"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"cmpread",      cmpread,       "read data from cmp format files"},
+  {"concat",  	   concat,        "reduce vector dimension"},
+  {"contour", 	   contour,       "create contour from image"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"cplot",   	   cplot,         "plot vectors in sky coordinates"},
+  {"create",  	   create,        "create a new vector"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"csystem", 	   csystem,       "convert between coordinate systems"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"ctimes",  	   ctimes,        "convert between time formats"},
+  {"cursor",  	   cursor,        "get coords from cursor"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"czplot",  	   czplot,        "plot scaled vectors in sky coordinates"},
+  {"datafile",     datafile,      "define file to read vectors"},
+  {"date",    	   date,          "get current date"},
+  {"ddmagextr",    ddmagextract,  "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"ddmags",       ddmags,        "plot magnitude differences"},
+  {"delete",  	   delete,        "delete vectors or matrices"},
+  {"device",  	   device,        "set / get current graphics device"},
+  {"dmagaves",     dmagaves,      "plot differential magnitudes between filters"},
+  {"dmagextract",  dmagextract,   "extract stars based on differential magnitudes between filters"},
+  {"dmagmeas",     dmagmeas,      "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmags",   	   dmags,         "plot differential magnitudes between filters"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dmt",          dmt,           "plot mag scatter"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"dumpmags",     dumpmags,      "custom dB dumping thingy"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"elixir",       elixir,        "get status info from elixir"},
+  {"extract", 	   extract,       "extract vectors from catalogs"},
+  {"file",    	   file,          "test for a file"},
+  {"fit",     	   fit,           "fit polynomial to vector pair"},
+  {"fit2d",        fit2d,         "fit 2-d polynomial to vector triplet"},
+  {"gaussj",  	   gaussjordan,   "solve Ax = B (N-D)"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gcat",         gcat,          "get catalog at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"gimages",      gimages,       "get images at location"},
+  {"grid",    	   grid,          "plot cartesian grid"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gstar",   	   gstar,         "get star statistics"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"gtypes",       gtypes,        "get type fractions"},
+  {"histogram",    histogram,     "generate histogram from vector"},
+  {"images",  	   images,        "plot image boxes"},
+  {"images",  	   images,        "plot image boxes"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imbox",   	   imbox,         "plot expected image box"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdata",  	   imdata,        "extract data for specific images"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imdense", 	   imdense,       "image density plot"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imextract",    imextract,     "extract vectors from catalogs"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imlist",  	   imlist,        "list image info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imphot",  	   imphot,        "image photometry info"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imrough",      imrough,       "get info from imruf database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imsearch",     imsearch,      "get info from imreg database"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"imstats", 	   imstats,       "plot image statistics"},
+  {"interpolate",  interpolate,   "interpolate between vector pairs"},
+  {"jpeg",         jpeg,          "write text line on graph"},
+  {"labels",  	   labels,        "define labels for plot"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcat",    	   lcat,          "list catalogs in region"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"lcurve",  	   lcurve,        "plot lightcurve for a star"},
+  {"limits",  	   limits,        "define plot limits"},
+  {"buffers",      list_buffers,  "list the currently allocated buffers"},
+  {"vectors", 	   list_vectors,  "list vectors"},
+  {"local",  	   local,         "define local variables"},
+  {"mcreate", 	   mcreate,       "create a matrix"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mextract",	   mextract,      "extract vectors from catalogs"},
+  {"mget",    	   mget,          "extract a vector from a matrix"},
+  {"mset",    	   mset,          "insert a vector in a matrix"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"pcat",    	   pcat,          "plot catalog boundaries"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photcodes",    photcodes,     "list photometry codes"},
+  {"photresid",    photresid,     "plot photometry residuals"},
+  {"plot",    	   plot,          "plot a pair of vectors"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"pmeasure",	   pmeasure,      "plot individual measurements"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"precess", 	   precess,       "precess coordinates"},
+  {"print",   	   print,         "write vectors to file"},
+  {"print",   	   print,         "write vectors to file"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"procks",  	   procks,        "plot rocks"},
+  {"ps",      	   ps,            "define labels for plot"},
+  {"rd",           rd,            "load fits image"},
+  {"read",         read_vectors,  "read vectors from datafile"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"region",  	   region,        "define sky region for plot"},
+  {"resid",   	   resid,         "plot residuals"},
+  {"resize",  	   resize,        "set graphics/image window size"},
+  {"section", 	   section,       "define section of graph"},
+  {"set",     	   set,           "vector math"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"simage",  	   simage,        "plot stars in an image"},
+  {"sort",    	   sort_vectors,  "sort list of vectors"},
+  {"sprintf", 	   sprintf_opihi, "formated print to variable"},
+  {"stats",   	   stats,         "give statistics on a portion of a buffer"},
+  {"style",   	   style,         "set the style for graph plots"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subpix",  	   subpix,        "get subpixel positions"},
+  {"subraster",    subraster,     "subraster of fits image"},
+  {"subset",  	   subset,        "expand vector dimension"},
+  {"textline",     textline,      "write text line on graph"},
+  {"tv",      	   tv,            "display an image on the Kii window"},
+  {"uniq",    	   uniq,          "create a uniq vector subset from a vector"},
+  {"vectobuf",     vectobuf,      "convert vector triplet to buffer"},
+  {"vstat",        vstat,         "get info from imreg database"},
+  {"wd",      	   wd,            "write an image to a file"},
+  {"write",   	   write_vectors, "write vectors to datafile"},
+  {"zap",     	   zap,           "delete pixels"},
+  {"zeropts", 	   zeropts,       "show filter zeropts"},
+  {"zplot",   	   zplot,         "plot x y with size scaled by z"},
+}; 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/Makefile	(revision 17422)
@@ -0,0 +1,60 @@
+default: libdata
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.data
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# general numerical functions (libdata) #####################
+srcs = \
+$(SDIR)/book.$(ARCH).o                  \
+$(SDIR)/page.$(ARCH).o                  \
+$(SDIR)/fft.$(ARCH).o			\
+$(SDIR)/svdcmp.$(ARCH).o		\
+$(SDIR)/convert.$(ARCH).o		\
+$(SDIR)/bracket.$(ARCH).o		\
+$(SDIR)/spline.$(ARCH).o		\
+$(SDIR)/mrqmin.$(ARCH).o		\
+$(SDIR)/mrq2dmin.$(ARCH).o		\
+$(SDIR)/precess.$(ARCH).o		\
+$(SDIR)/starfuncs.$(ARCH).o		\
+$(SDIR)/gaussian.$(ARCH).o		\
+$(SDIR)/graphtools.$(ARCH).o            \
+$(SDIR)/queues.$(ARCH).o		\
+$(SDIR)/PlotVectors.$(ARCH).o		\
+$(SDIR)/open_kapa.$(ARCH).o             \
+$(SDIR)/style_args.$(ARCH).o
+
+#$(SDIR)/open_graph.$(ARCH).o            \
+#$(SDIR)/open_image.$(ARCH).o            \
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(srcs): $(incs)
+
+$(LIB)/libdata.$(ARCH).a: $(srcs)
+$(LIB)/libdata.$(ARCH).$(DLLTYPE): $(srcs)
+
+$(DESTLIB)/libdata.a: $(LIB)/libdata.$(ARCH).a
+$(DESTLIB)/libdata.$(DLLTYPE): $(LIB)/libdata.$(ARCH).$(DLLTYPE)
+
+libdata: $(DESTLIB)/libdata.a $(DESTLIB)/libdata.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libdata.a
+	rm -f $(DESTLIB)/libdata.$(DLLTYPE)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/PlotVectors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "display.h"
+
+int PlotVectorPair (int kapa, int Npts, float *xValues, float *yValues, Graphdata *graphmode) {
+
+  KapaPrepPlot (kapa, Npts, graphmode);
+  KapaPlotVector (kapa, Npts, xValues, "x");
+  KapaPlotVector (kapa, Npts, yValues, "y");
+
+  return (TRUE);
+}
+
+int PlotVectorTriplet (int kapa, int Npts, float *xValues, float *yValues, float *zValues, Graphdata *graphmode) {
+
+  KapaPrepPlot   (kapa, Npts, graphmode);
+  KapaPlotVector (kapa, Npts, xValues, "x");
+  KapaPlotVector (kapa, Npts, yValues, "y");
+  KapaPlotVector (kapa, Npts, zValues, "z");
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/book.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/book.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/book.c	(revision 17422)
@@ -0,0 +1,127 @@
+# include "data.h"
+
+Book **books;   /* book to store the list of all books */
+int    Nbooks;   /* number of currently defined books */
+int    NBOOKS;   /* number of currently allocated books */
+
+void InitBooks () {
+  Nbooks = 0;
+  NBOOKS = 16;
+  ALLOCATE (books, Book *, NBOOKS); 
+}
+
+void FreeBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    FreeBook (books[i]);
+  }
+  free (books);
+}
+
+void InitBook (Book *book, char *name) {
+
+    book[0].name = strcreate (name);
+
+    book[0].Npages = 0;
+    book[0].NPAGES = 16;
+    ALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    ALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // ALLOCATE (book[0].index, int, book[0].NPAGES);
+}
+
+void FreeBook (Book *book) {
+
+    int i;
+
+    free (book[0].name);
+    for (i = 0; i < book[0].Npages; i++) {
+	FreePage (book[0].pages[i]);
+	free (book[0].pageIDs[i]);
+    }
+    free (book[0].pages);
+    free (book[0].pageIDs);
+    // free (book[0].index);
+    free (book);
+}
+
+/* return the given book */
+Book *GetBook (int where) {
+
+  if (where < 0) where += Nbooks;
+  if (where < 0) return NULL;
+  if (where >= Nbooks) return NULL;
+  return (books[where]);
+}
+
+/* return the given book */
+Book *FindBook (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    if (!strcmp (books[i][0].name, name)) {
+      return (books[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named book */
+Book *CreateBook (char *name) {
+
+  int N;
+  Book *book;
+
+  book = FindBook (name);
+  if (book != NULL) return (book);
+
+  N = Nbooks;
+  Nbooks ++;
+  CHECK_REALLOCATE (books, Book *, NBOOKS, Nbooks, 16);
+  ALLOCATE (book, Book, 1);
+  InitBook (book, name);
+  books[N] = book;
+  return (book);
+}
+
+/* delete a book */
+int DeleteBook (Book *book) {
+
+  int i, N, NBOOKS_2;
+
+  /* find book in book list */
+  N = -1;
+  for (i = 0; i < Nbooks; i++) {
+    if (books[i] == book) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nbooks - 1; i++) {
+    books[i] = books[i + 1];
+  }
+  Nbooks --;
+  NBOOKS_2 = MAX (16, NBOOKS / 2);
+  if (Nbooks < NBOOKS_2) {
+    NBOOKS = NBOOKS_2;
+    REALLOCATE (books, Book *, NBOOKS);
+  }
+
+  FreeBook (book);
+  return (TRUE);
+}
+
+/* list known books */
+void ListBooks () {
+
+  int i;
+
+  for (i = 0; i < Nbooks; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", books[i][0].name, books[i][0].Npages);
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/bracket.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/bracket.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/bracket.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "data.h"
+
+/* fast operation to find an entry just below (0) or above (1) value */
+/* input list must be sorted */
+int bracket (double *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
+
+int ibracket (int *list, int Nlist, int mode, double value) {
+
+  int Nlo, Nhi, N;
+
+  if (mode == 0) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] < value) {
+	Nlo = N;
+      } else {
+	Nhi = N + 1;
+      }
+    }
+    return (Nlo);
+  }
+  if (mode == 1) {
+    Nlo = 0; Nhi = Nlist;
+    while (Nhi - Nlo > 10) {
+      N = 0.5*(Nlo + Nhi);
+      if (list[N] > value) {
+	Nhi = N;
+      } else {
+	Nlo = N - 1;
+      }
+    }
+    return (Nhi);
+  }
+  return (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/convert.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/convert.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/convert.c	(revision 17422)
@@ -0,0 +1,295 @@
+# include "convert.h"
+# define _XOPEN_SOURCE /* glibc2 (strptime) needs this */
+# include <time.h>
+
+/** additional time / coordinate conversions not supplied by libohana **/
+
+int hh_hms (double hh, int *hr, int *mn, double *sc) {
+
+  int flag;
+
+  flag = SIGN(hh);
+  hh *= flag;
+  hh = 24.0*(hh/24.0 - (int)(hh/24.0));
+  *sc = 60.0*(60.0*hh - (int)(60.0*hh));
+  *mn = 60.0*(hh - (int)hh);
+  *hr = (int) hh;
+  *hr *= flag;
+  return (TRUE);
+}
+ 
+int dd_dms (double dd, int *dg, int *mn, double *sc) {
+
+  int flag;
+
+  flag = SIGN(dd);
+  dd = fabs (dd);
+  *dg = (int) dd;
+  *mn = (int) 60*(dd - *dg);
+  *sc = 3600.0*(dd - *dg - *mn/60.0);
+  if (*sc > 59.99) {
+    *sc = 0;
+    *mn += 1.0;
+  }
+  *dg *= flag;
+  return (TRUE);
+}
+ 
+int hms_format (char *line, double value) {
+
+  int hr, mn;
+  double sc;
+
+  hh_hms (value, &hr, &mn, &sc);
+  hr = (int) value;
+  if (isnan (value))
+    sprintf (line, "xx:xx:xx.xx");
+  else {
+    if (value < 0) {
+      sprintf (line, "-%02d:%02d:%05.2f", abs(hr), mn, sc);
+    } else {
+      sprintf (line, "%02d:%02d:%05.2f", hr, mn, sc);
+    }
+  }      
+  return (TRUE);
+}
+
+int dms_format (char *line, double value) {
+
+  int dg, mn;
+  double sc;
+
+  dd_dms (value, &dg, &mn, &sc);
+  if (value < 0) {
+    sprintf (line, "-%02d:%02d:%05.2f", abs(dg), mn, sc);
+  } else {
+    sprintf (line, "%02d:%02d:%05.2f", dg, mn, sc);
+  }
+  return (TRUE);
+}
+
+/***** convert 00:00:00 or 00:00 to 0 - 86400 ****/
+int hms_to_sec (char *string, time_t *second) {
+  
+  char *p;
+  struct tm time;
+
+  p = strptime (string, "%H:%M:%S", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%H:%M", &time);
+  if (p != NULL) goto valid;
+
+  return (FALSE);
+    
+valid:
+  if (*p) return (FALSE);
+  *second = time.tm_hour*3600 + time.tm_min*60 + time.tm_sec;
+  return (TRUE);
+}
+
+/***** convert Mon[@00:00:00] or 00:00 to 0 - 86400*7 ****/
+int day_to_sec (char *string, time_t *second) {
+  
+  char *p;
+  struct tm time;
+
+  bzero (&time, sizeof(time));
+  p = strptime (string, "%A@%H:%M:%S", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A@%H:%M", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A@%H", &time);
+  if (p != NULL) goto valid;
+
+  p = strptime (string, "%A", &time);
+  if (p != NULL) goto valid;
+
+  return (FALSE);
+
+valid:
+  if (*p) return (FALSE);
+  *second = time.tm_wday*86400 + time.tm_hour*3600 + time.tm_min*60 + time.tm_sec;
+  return (TRUE);
+}
+
+/***** convert seconds to HH:MM:SS ****/
+char *ohana_sec_to_hms (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  sprintf (line, "%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+  return (line);
+}
+
+/***** convert seconds to Day@HH:MM:SS ****/
+char *ohana_sec_to_day (time_t second) {
+  
+  struct tm *gmt;
+  char *line;
+
+  ALLOCATE (line, char, 64);
+  gmt   = gmtime (&second);
+  switch (gmt[0].tm_wday) {
+    case 0:
+      sprintf (line, "Sun@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 1:
+      sprintf (line, "Mon@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 2:
+      sprintf (line, "Tue@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 3:
+      sprintf (line, "Wed@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 4:
+      sprintf (line, "Thu@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 5:
+      sprintf (line, "Fri@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+    case 6:
+      sprintf (line, "Sat@%02d:%02d:%02d", gmt[0].tm_hour, gmt[0].tm_min, gmt[0].tm_sec); 
+      break;
+  }
+  return (line);
+}
+
+int hh_hm (double hh, int *hr, double *mn) {
+
+  int flag;
+
+  flag = SIGN(hh);
+  hh = fabs (hh);
+
+  *mn = 60.0*(hh - (int)hh);
+  *hr = (int) hh;
+  *hr *= flag;
+  return (TRUE);
+}
+
+char *meade_deg_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg, &hr, &mn);
+
+  sprintf (line, "%03d:%04.1f", abs(hr), mn);
+  return (line);
+}
+
+char *meade_ra_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg/15.0, &hr, &mn);
+
+  sprintf (line, "%02d:%04.1f", abs(hr), mn);
+  return (line);
+}
+
+char *meade_dec_to_str (double deg) {
+
+  int hr;
+  double mn;
+  char *line;
+
+  ALLOCATE (line, char, 16);
+
+  hh_hm (deg, &hr, &mn);
+
+  if (deg < 0) {
+    sprintf (line, "-%02d:%04.1f", abs(hr), mn);
+  } else {
+    sprintf (line, "+%02d:%04.1f", hr, mn);
+  }      
+  return (line);
+}
+
+/* convert UNIX time to a value referenced to the TimeReference in the given unit */
+double TimeValue (time_t time, time_t TimeReference, int TimeFormat) {
+
+  double value, dt;
+
+  dt = (time > TimeReference) ? (time - TimeReference) : -1 * (double)(TimeReference - time);
+  switch (TimeFormat) {
+  case TIME_JD:
+    value = time / 86400.0 + 2440587.5;
+    break;
+  case TIME_MJD:
+    value = time / 86400.0 + 40587.0;
+    break;
+  case TIME_DAYS:
+    value = dt / 86400.0;
+    break;
+  case TIME_HOURS:
+    value = dt / 3600.0;
+    break;
+  case TIME_MINUTES:
+    value = dt / 60.0;
+    break;
+  case TIME_SECONDS:
+  default:
+    value = dt;
+    break;
+  }
+  return (value);
+}
+  
+/* convert time value referenced to the TimeReference in the given unit to UNIX time */
+time_t TimeRef (double value, time_t TimeReference, int TimeFormat) {
+
+  int dt;
+  time_t time;
+
+  switch (TimeFormat) {
+  case TIME_JD:
+    time = (value -  2440587.5) * 86400.0;
+    return (time);
+    break;
+  case TIME_MJD:
+    time = (value -  40587.0) * 86400.0;
+    return (time);
+    break;
+  case TIME_DAYS:
+    dt = value * 86400.0;
+    break;
+  case TIME_HOURS:
+    dt = value * 3600.0;
+    break;
+  case TIME_MINUTES:
+    dt = value * 60.0;
+    break;
+  case TIME_SECONDS:
+  default:
+    dt = value;
+    break;
+  }
+
+  time = TimeReference + dt;
+  return (time);
+}
+
+/* times may be in forms as:
+ * 20040200450s (N seconds since 1970.0)
+ * 2440900.232j (julian date)
+ * 99/02/23,03:22:18 (date string)
+ * (separators may be anything except space, +, -)
+ * 99:02:15:12:23:30
+ * 99:02:15:12h23m30s
+ */
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/fft.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/fft.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/fft.c	(revision 17422)
@@ -0,0 +1,171 @@
+# include "data.h"
+
+// fft based on code by Douglas L. Jones (see note at EOF). modified for Ohana C style
+void fft1D (float *x, float *y, int n, int Nbit, int forward) {
+
+  int i,j,k,n1,n2;
+  float c,s,e,a,t1,t2;        
+  double factor;
+         
+  // bit-reverse
+  j = 0; 
+  n2 = n/2;
+  for (i = 1; i < n - 1; i++) {
+    n1 = n2;
+    while ( j >= n1 ) {
+      j -= n1;
+      n1 /= 2;
+    }
+    j += n1;
+               
+    if (i < j) {
+      t1 = x[i];
+      x[i] = x[j];
+      x[j] = t1;
+      t1 = y[i];
+      y[i] = y[j];
+      y[j] = t1;
+    }
+  }
+                                          
+  n1 = 0; /* FFT */
+  n2 = 1;
+                                             
+  if (forward) {
+    factor = +2.0*M_PI;
+  } else {
+    factor = -2.0*M_PI;
+  }
+
+  for (i=0; i < Nbit; i++) {
+    n1 = n2;
+    n2 = n2 + n2;
+    e = factor/n2;
+    a = 0.0;
+                                             
+    for (j=0; j < n1; j++) {
+      c = cos(a);
+      s = sin(a);
+      a = a + e;
+                                            
+      for (k=j; k < n; k=k+n2) {
+	t1 = c*x[k+n1] - s*y[k+n1];
+	t2 = s*x[k+n1] + c*y[k+n1];
+	x[k+n1] = x[k] - t1;
+	y[k+n1] = y[k] - t2;
+	x[k] = x[k] + t1;
+	y[k] = y[k] + t2;
+      }
+    }
+  }
+                                      
+  // re-normalize
+  for (i = 0; i < n; i++) {
+    x[i] /= n;
+    y[i] /= n;
+  }
+
+  return;
+}                          
+
+// This implementation uses the 1-D fft above for each of the vectors in each dimension.
+// This requires 2(Nx*Ny*...) mem copies, but the fft operations are likely to happen in
+// cache.
+int fftND (float *x, float *y, int Ndim, int *Nsize, int forward) {
+
+  int i, nIndex, minor, major, iDim;
+  int step, Nmajor, Nminor, Nmax, Ntotal;
+  int *Nbit;
+  float *tmpX, *tmpY;
+
+  ALLOCATE (Nbit, int, Ndim);
+
+  // find the longest axis and allocate storage for that length
+  Nmax = 0;
+  Ntotal = 1;
+  for (i = 0; i < Ndim; i++) {
+    Nmax = MAX(Nmax, Nsize[i]);
+    Ntotal *= Nsize[i];
+    if (!IsBinary (Nsize[i], &Nbit[i])) {
+      free (Nbit);
+      return (FALSE);
+    }
+  }
+  ALLOCATE (tmpX, float, Nmax);
+  ALLOCATE (tmpY, float, Nmax);
+  
+  step = 1;
+  Nminor = 1;
+  Nmajor = Ntotal;
+  for (iDim = 0; iDim < Ndim; iDim++) {
+    step *= Nsize[iDim];
+    Nmajor /= Nsize[iDim];
+
+    // we perform the FFT along all other dimensions 
+    for (major = 0; major < Nmajor; major++) {
+      for (minor = 0; minor < Nminor; minor++) {
+	// nIndex = minor + i*Nminor + major*step;
+	// extract the data values to the temp vector
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  tmpX[i] = x[nIndex];
+	  tmpY[i] = y[nIndex];
+	  nIndex += Nminor;
+	}
+
+	fft1D (tmpX, tmpY, Nsize[iDim], Nbit[iDim], forward);
+
+	// replace the result vectors
+	nIndex = minor + major*step;
+	for (i = 0; i < Nsize[iDim]; i++) {
+	  x[nIndex] = tmpX[i];
+	  y[nIndex] = tmpY[i];
+	  nIndex += Nminor;
+	}
+      }
+    }
+    Nminor *= Nsize[iDim];
+  }
+  free (tmpX);
+  free (tmpY);
+  return (TRUE);
+}
+
+// check that a number is binary (2^Nbit).  returns int(log_2(N)) in Nbit
+int IsBinary (int N, int *Nbit) {
+
+  int i, Nset;
+
+  if (Nbit != NULL) *Nbit = 0;
+  Nset = 0;
+  for (i = 0; i < 8*sizeof(int); i++) {
+    if (N & 0x01) {
+      Nset ++;
+      if (Nbit != NULL) *Nbit = i;
+    }
+    N >>= 1;
+  }
+  if (Nset > 1) return (FALSE);
+  return (TRUE);  
+}
+
+/**********************************************************/
+/* fft.c                                                  */
+/* (c) Douglas L. Jones                                   */
+/* University of Illinois at Urbana-Champaign             */
+/* January 19, 1992                                       */
+/*                                                        */
+/*   fft: in-place radix-2 DIT DFT of a complex input     */
+/*                                                        */
+/*   input:                                               */
+/* n: length of FFT: must be a power of two               */
+/* m: n = 2**m                                            */
+/*   input/output                                         */
+/* x: float array of length n with real part of data     */
+/* y: float array of length n with imag part of data     */
+/*                                                        */
+/*   Permission to copy and use this program is granted   */
+/*   under a Creative Commons "Attribution" license       */
+/*   http://creativecommons.org/licenses/by/1.0/          */
+/**********************************************************/
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/gaussian.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/gaussian.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/gaussian.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "data.h"
+
+static int Ngaussint = 0;
+static double *gaussint;
+
+extern double drand48();
+
+double gaussian (double x, double mean, double sigma) {
+
+  double f;
+
+  f = exp (-0.5 * SQ(x - mean) / SQ(sigma)) / sqrt(2 * M_PI * SQ(sigma));
+
+  return (f);
+
+}
+
+/* integrate a gaussian from -5 sigma to +5 sigma */
+void gauss_init (int Nbin) {
+ 
+  int i;
+  long A, B;
+  double val, x, dx, dx1, dx2, dx3, df;
+  double mean, sigma;
+ 
+  /* no need to generate this if it already exists */
+  if (Ngaussint == Nbin) return;
+
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  Ngaussint = Nbin;
+  ALLOCATE (gaussint, double, Ngaussint + 1);
+
+  val = 0;
+  dx = 1.0 / Ngaussint;
+  dx1 = dx / 3.0;
+  dx2 = 2.0*dx/3.0;
+  dx3 = dx;
+  mean = 0.0;
+  sigma = 1.0;
+ 
+  for (i = 0, x = -7.0; (i < Ngaussint) && (x < 7.0); x += dx)  {
+    df = (3.0*gaussian(x    , mean, sigma) + 
+          9.0*gaussian(x+dx1, mean, sigma) +
+          9.0*gaussian(x+dx2, mean, sigma) + 
+          3.0*gaussian(x+dx3, mean, sigma)) * (dx1/8.0);
+    val += df;
+    if (val > (i + 0.5) / (double) Ngaussint) {
+      gaussint[i] = x + dx / 2.0;
+      i++;
+    }
+  }
+}
+
+double rnd_gauss (double mean, double sigma) {
+ 
+  int i;
+  double y;
+ 
+  y = drand48();
+  i = Ngaussint*y;
+  y = gaussint[i]*sigma + mean;
+ 
+  return (y);
+ 
+}
+ 
+double int_gauss (int i) {
+  double y;
+  y = gaussint[i];
+  return (y);
+}
+ 
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/graphtools.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/graphtools.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/graphtools.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "data.h"
+
+void SetLimits (Vector *xvec, Vector *yvec, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0].elements[0];
+    for (i = 1; i < xvec[0].Nelements; i++) {
+      if (!finite(xvec[0].elements[i])) continue;
+      maxX = MAX (maxX, xvec[0].elements[i]);
+      minX = MIN (minX, xvec[0].elements[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0].elements[0];
+    for (i = 1; i < yvec[0].Nelements; i++) {
+      if (!finite(yvec[0].elements[i])) continue;
+      maxY = MAX (maxY, yvec[0].elements[i]);
+      minY = MIN (minY, yvec[0].elements[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+}
+
+void SetLimitsRaw (float *xvec, float *yvec, int Nelements, Graphdata *graphmode) {
+
+  double maxX, minX, maxY, minY, range;
+  int i;
+
+  if (xvec != NULL) {
+    maxX = minX = xvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(xvec[i])) continue;
+      maxX = MAX (maxX, xvec[i]);
+      minX = MIN (minX, xvec[i]);
+    }
+    range = maxX - minX;
+    if (range == 0) range = 0.001 * maxX;
+    if (range == 0) range = 0.001;
+    graphmode[0].xmin = minX - 0.05*range;
+    graphmode[0].xmax = maxX + 0.05*range;
+  }
+
+  if (yvec != NULL) {
+    maxY = minY = yvec[0];
+    for (i = 1; i < Nelements; i++) {
+      if (!finite(yvec[i])) continue;
+      maxY = MAX (maxY, yvec[i]);
+      minY = MIN (minY, yvec[i]);
+    }
+    range = maxY - minY;
+    if (range == 0) range = 0.0011 * maxY;
+    if (range == 0) range = 0.0011;
+    graphmode[0].ymin = minY - 0.05*range;
+    graphmode[0].ymax = maxY + 0.05*range;
+  }
+  SetGraph (graphmode);
+
+  set_variable ("XMIN", graphmode[0].xmin);
+  set_variable ("XMAX", graphmode[0].xmax);
+  set_variable ("YMIN", graphmode[0].ymin);
+  set_variable ("YMAX", graphmode[0].ymax);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/hashes.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/hashes.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/hashes.c	(revision 17422)
@@ -0,0 +1,49 @@
+
+/* I need a structure which will be appropriate to carry the psMetadataConfig data.  the main 
+   requirements are:
+
+   - name-based components
+   - string-indexing on columns
+
+   basic elements:
+
+   container->item->element->value
+
+   element: name, value (type?)
+   item->name, Nelement, elements
+   container->items, Nitems
+
+   user interactions
+
+   blob list
+   blob listitems (blob)
+   blob create (blob)
+   blob delete (blob)
+   blob listkeys (blob.item)
+
+   blob getvalue (blob.item.key) -var word
+   blob setvalue (blob.item.key) value
+
+   blob getitem (blob.item)  : list all key/value pairs
+   blob newitem (blob.item)
+   blob delitem (blob.item)
+   blob popitem (blob)
+
+   blob readqueue (queue)    : convert queue in MDC format to blob
+   *** this needs to be able to match by keys against existing items
+   
+   need equivalents to:
+   queuepush -uniq key
+   queuepop -
+   queuesize
+   
+
+   items should be sorted by name so we can lookup an item quickly
+
+
+
+   other related opihi data concepts
+   
+   $a = @function (output of function set to value?)
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrq2dmin.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrq2dmin.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrq2dmin.c	(revision 17422)
@@ -0,0 +1,210 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, t, a, Npar, dy/da) 
+   returns y (x,t) for Npar parameters a along with dy/da at (x,t)
+   dy carries 1/sig^2 
+*/
+
+# define VERY_VERBOSE 0
+
+static float **alpha, **talpha;
+static float **beta, **tbeta;
+static float *partry, *dyda;
+static float ochisq, lambda;
+
+static float *parmin = NULL;
+static float *parmax = NULL;
+
+float mrq2dcof (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float, float *, int, float *)) {
+
+  int k, j, i;
+  float ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+    
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++)
+      ta[k][j] = ta[j][k];
+      
+# if (VERY_VERBOSE)
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) {
+      gprint (GP_ERR, "%9.3e  ", ta[j][k]);
+    }
+    gprint (GP_ERR, "    :   %9.3e  ", tb[j][0]);
+    gprint (GP_ERR, "\n");
+  }
+# endif
+
+  return (chisq);
+
+}
+
+float mrq2dchi (float *x, float *t, float *y, float *dy, int Npts, 
+		float *par, int Npar, 
+		float (funcs)(float, float, float *, int, float *)) {
+
+  int i;
+  float ydiff, chisq;
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+    ydiff = funcs (x[i], t[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+  }
+  return (chisq);
+}
+
+float mrq2dmin (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float, float *, int, float *), int VERBOSE) {
+
+  int j, k;
+  float chisq;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  /* keep this test in here? */
+  if (!fgaussjordan (talpha, tbeta, Npar, 1)) {
+    lambda *= 10.0;
+    return (ochisq);
+  }
+
+  for (j = 0; j < Npar; j++) {
+    partry[j] = par[j] - tbeta[j][0];
+    /*
+    if (parmin != NULL) partry[j] = MAX (parmin[j], partry[j]);
+    if (parmax != NULL) partry[j] = MIN (parmax[j], partry[j]);
+    */
+  }
+
+  chisq = mrq2dcof (x, t, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* if good, save temp values */
+  if (chisq < ochisq) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+int mrq2dlimits (float *pmin, float *pmax, int Npar) {
+
+  int i;
+
+  ALLOCATE (parmin, float, Npar);
+  ALLOCATE (parmax, float, Npar);
+  for (i = 0; i < Npar; i++) {
+    parmin[i] = pmin[i];
+    parmax[i] = pmax[i];
+  }
+  return (TRUE);
+}
+
+float mrq2dinit (float *x, float *t, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float, float *, int, float *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, float, Npar);
+  ALLOCATE (partry, float, Npar);
+  ALLOCATE (alpha, float *, Npar);
+  ALLOCATE (beta, float *, Npar);
+  ALLOCATE (talpha, float *, Npar);
+  ALLOCATE (tbeta, float *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], float, Npar);
+    ALLOCATE (beta[i], float, Npar);
+    ALLOCATE (talpha[i], float, Npar);
+    ALLOCATE (tbeta[i], float, Npar);
+  }
+
+  
+  lambda = 0.001;
+  
+  ochisq = mrq2dcof (x, t, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  return (ochisq);
+
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+float **mrq2dcovar (int Npar) {
+
+  fgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+
+} 
+
+void mrq2dfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+
+  if (parmin != NULL) free (parmin);
+  if (parmax != NULL) free (parmax);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrqmin.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrqmin.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/mrqmin.c	(revision 17422)
@@ -0,0 +1,159 @@
+# include "data.h"  /* only needed for the ALLOCATE def */
+
+/* need to pass in a function of the form:
+   funcs (x, a, Npar, dyda) 
+   returns f (x) for Npar parameters a, also df/da at x 
+   dy carries 1/sig^2 
+*/
+
+static float **alpha, **talpha;
+static float **beta, **tbeta;
+static float *partry, *dyda;
+static float ochisq, lambda;
+
+float mrqcof (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, float **ta, float **tb, 
+	      float (funcs)(float, float *, int, float *)) {
+
+  int k, j, i;
+  float ydiff, wt, chisq;
+
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k <= j; k++) ta[j][k] = 0.0;
+    tb[j][0] = 0.0;
+  }
+
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+
+    ydiff = funcs (x[i], par, Npar, dyda) - y[i];
+    chisq += SQ(ydiff) * dy[i];
+
+    for (j = 0; j < Npar; j++) {
+      wt = dyda[j] * dy[i];
+      for (k = 0; k <= j; k++) ta[j][k] += wt * dyda[k];
+      tb[j][0] += wt * ydiff;
+    }
+  }
+
+  for (j = 1; j < Npar; j++)
+    for (k = 0; k < j; k++) 
+      ta[k][j] = ta[j][k];
+
+  return (chisq);
+
+}
+
+float mrqmin (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE) {
+
+  int j, k;
+  float chisq;
+  float rho, dX, dL;
+
+  /* set up test matrixes for this run */
+  for (j = 0; j < Npar; j++) {
+    for (k = 0; k < Npar; k++) talpha[j][k] = alpha[j][k];
+    talpha[j][j] = alpha[j][j] * (1.0 + lambda);
+    tbeta[j][0] = beta[j][0];
+  }
+
+  fgaussjordan (talpha, tbeta, Npar, 1);
+
+  for (j = 0; j < Npar; j++) partry[j] = par[j] - tbeta[j][0];
+
+  /* get linear model prediction */
+  dL = 0;
+  for (j = 0; j < Npar; j++) {
+      dL += 0.5*lambda*SQ(tbeta[j][0]) + tbeta[j][0]*beta[j][0];
+  }
+
+  chisq = mrqcof (x, y, dy, Npts, partry, Npar, talpha, tbeta, funcs);
+  if (VERBOSE) { 
+    gprint (GP_ERR, "chisq: %f  ", chisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (j = 0; j < Npar; j++) {
+      gprint (GP_ERR, "%f ", partry[j]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+
+  /* compare linear model with actual */
+  dX = ochisq - chisq;
+  rho = dX / dL;
+
+  /* if good, save temp values */
+  if (rho > 0) {
+    lambda *= 0.1;
+    ochisq = chisq;
+    for (j = 0; j < Npar; j++) {
+      for (k = 0; k < Npar; k++) alpha[j][k] = talpha[j][k];
+      beta[j][0] = tbeta[j][0];
+      par[j] = partry[j];
+    }
+  } else {
+    lambda *= 10.0;
+    chisq = ochisq;
+  }
+
+  return (chisq);
+
+}
+
+float mrqinit (float *x, float *y, float *dy, int Npts, 
+	      float *par, int Npar, 
+	      float (funcs)(float, float *, int, float *), int VERBOSE) {
+
+  int i;
+
+  ALLOCATE (dyda, float, Npar);
+  ALLOCATE (partry, float, Npar);
+  ALLOCATE (alpha, float *, Npar);
+  ALLOCATE (beta, float *, Npar);
+  ALLOCATE (talpha, float *, Npar);
+  ALLOCATE (tbeta, float *, Npar);
+  for (i = 0; i < Npar; i++) {
+    ALLOCATE (alpha[i], float, Npar);
+    ALLOCATE (beta[i], float, Npar);
+    ALLOCATE (talpha[i], float, Npar);
+    ALLOCATE (tbeta[i], float, Npar);
+  }
+  
+  lambda = 0.01;
+  
+  ochisq = mrqcof (x, y, dy, Npts, par, Npar, alpha, beta, funcs);
+  if (VERBOSE) {
+    gprint (GP_ERR, "chisq: %f  ", ochisq);
+    gprint (GP_ERR, "lambda: %f  ", lambda);
+    for (i = 0; i < Npar; i++) {
+      gprint (GP_ERR, "%f ", par[i]);
+    }
+    gprint (GP_ERR, "\n");
+  }
+  return (ochisq);
+}
+
+/* don't invoke this in the middle of a run, only near the end */ 
+float **mrqcovar (int Npar) {
+  fgaussjordan (alpha, beta, Npar, 1);
+  return (alpha);
+} 
+
+void mrqfree (int Npar) {
+
+  int i;
+
+  for (i = 0; i < Npar; i++) {
+    free (alpha[i]);
+    free (talpha[i]);
+    free (beta[i]);
+    free (tbeta[i]);
+  }
+  free (alpha);
+  free (talpha);
+  free (beta);
+  free (tbeta);
+  free (partry);
+  free (dyda);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_graph.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_graph.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_graph.c	(revision 17422)
@@ -0,0 +1,176 @@
+# include "display.h"
+# include "shell.h"
+# define DEBUG 0
+
+/* we have space for several kapa windows */
+# define NXGRAPH 5
+
+static int       Active;
+static int       Xgraph[NXGRAPH];  
+static Graphdata graphdata[NXGRAPH];
+
+void QuitGraph () {
+
+  int i;
+  
+  for (i = 0; i < NXGRAPH; i++) {
+    KiiClose (Xgraph[i]);
+  }
+}
+
+void InitGraph () {
+
+  int i;
+
+  Active = 0;
+  for (i = 0; i < NXGRAPH; i++) {
+    Xgraph[i] = -1;
+
+    graphdata[i].xmin = graphdata[i].ymin = 0.0;
+    graphdata[i].xmax = graphdata[i].ymax = 1.0;
+    graphdata[i].style = graphdata[i].ptype = 0;
+    graphdata[i].ltype = graphdata[i].color = 0;
+    graphdata[i].etype = graphdata[i].ebar = 0;
+    graphdata[i].lweight = graphdata[i].size = 1.0;
+    
+    graphdata[i].coords.pc1_1 = graphdata[i].coords.pc2_2 = 1.0;
+    graphdata[i].coords.pc1_2 = graphdata[i].coords.pc2_1 = 0.0;
+    strcpy (graphdata[i].coords.ctype, "RA---LIN");
+    graphdata[i].coords.crval1 = 0.0;
+    graphdata[i].coords.crval2 = 0.0;
+    graphdata[i].coords.crpix1 = 0.0;
+    graphdata[i].coords.crpix2 = 0.0;
+    graphdata[i].coords.cdelt1 = graphdata[i].coords.cdelt2 = 1.0;
+    graphdata[i].flipeast = TRUE;
+    graphdata[i].flipnorth = FALSE;
+    strcpy (graphdata[i].axis, "2222");
+    strcpy (graphdata[i].ticks, "2222");
+    strcpy (graphdata[i].labels, "2222");
+  }
+}
+
+/* set SIGPIPE to this function to close cleanly */ 
+void XGraphDead (int input) {
+  signal (SIGPIPE, XGraphDead);
+  gprint (GP_ERR, "kapa is dead, must restart\n");
+  Xgraph[Active] = -1;
+}
+
+/** start socketed connection */
+int open_graph (int N) {
+
+  int fd;
+  char *kapa_exec, name[16];
+  
+  kapa_exec = get_variable ("KAPA");
+  if (kapa_exec == (char *) NULL) {
+    gprint (GP_ERR, "variable KAPA not found\n");
+    return (FALSE);
+  }
+
+  snprintf (name, 16, "[%d]", N);
+  fd = KiiOpen (kapa_exec, name);
+  free (kapa_exec);
+
+  if (fd < 0) {
+    gprint (GP_ERR, "error starting kapa\n");
+    return (FALSE);
+  } 
+
+  Xgraph[N] = fd;
+  return (TRUE);
+}
+
+int close_graph (int N) {
+
+  if (N <  0) return (FALSE);
+  if (N >= NXGRAPH) return (FALSE);
+
+  KiiClose (Xgraph[N]); 
+  Xgraph[N] = -1;
+  return (TRUE);
+}
+
+/* return pointers for current Xgraph, set if desired, test, open if needed */
+int GetGraph (Graphdata *data, int *socket, int *N) {
+
+  int i, n;
+  char buffer[70];
+
+  SetImageDevice (FALSE);
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXGRAPH) {
+      gprint (GP_ERR, "invalid Xgraph window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      *N = n = Active;
+    } else {
+      Active = n = *N;
+    }
+  }
+  
+  /* test Xgraph[0], flush junk from pipe */
+  signal (SIGPIPE, XGraphDead);
+  fcntl (Xgraph[n], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Xgraph[n], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Xgraph[n], F_SETFL, !O_NONBLOCK); 
+  
+  if (Xgraph[n] < 1) {
+    if (!open_graph(n)) {
+      return (FALSE);
+    }
+  }
+
+  if (data != (Graphdata *) NULL) *data  = graphdata[n];
+  if (socket != (int *) NULL) *socket = Xgraph[n];
+
+  return (TRUE);
+
+}
+
+/* return pointers for given Xgraph, don't set or open */
+int GetGraphData (Graphdata *data, int *sock, int *N) {
+
+  int n;
+
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXGRAPH) {
+      gprint (GP_ERR, "invalid Xgraph window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      n = Active;
+    } else {
+      n = *N;
+    }
+  }
+  
+  if (data != (Graphdata *) NULL) *data  = graphdata[n];
+  if (sock != (int *) NULL) *sock = Xgraph[n];
+
+  return (TRUE);
+
+}
+
+/* assign given values to current Xgraph */
+void SetGraph (Graphdata data) {
+  graphdata[Active] = data;
+}
+
+/** internal tracking of current active device type **/
+
+static int       IsImage = FALSE;
+
+int GetCurrentDevice () {
+  return (IsImage);
+}
+
+void SetImageDevice (int state) {
+  IsImage = state;
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_image.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_image.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_image.c	(revision 17422)
@@ -0,0 +1,133 @@
+# include "display.h"
+# include "shell.h"
+# define DEBUG 0
+
+/* we have space for several kii windows */
+# define NXIMAGE 5
+
+static int       Active;
+static int       Ximage[NXIMAGE];  
+static char      Ximbuffer[NXIMAGE][512];
+static double    Xzero[NXIMAGE];
+static double    Xrange[NXIMAGE];
+
+void QuitImage () {
+
+  int i;
+  
+  for (i = 0; i < NXIMAGE; i++) {
+    KiiClose (Ximage[i]);
+  }
+}
+
+void InitImage () {
+
+  int i;
+
+  Active = 0;
+  for (i = 0; i < NXIMAGE; i++) {
+    Ximage[i] = -1;
+    Xzero[i] = 0;
+    Xrange[i] = 1024;
+    strcpy (Ximbuffer[i], "none");
+  }
+}
+
+/* set SIGPIPE to this function to close cleanly */ 
+void XImageDead (int input) {
+  signal (SIGPIPE, XImageDead);
+  gprint (GP_ERR, "kii is dead, must restart\n");
+  Ximage[Active] = -1;
+}
+
+/** start socketed connection */
+int open_image (int N) {
+
+  int fd;
+  char *kii_exec, name[16];
+
+  kii_exec = get_variable ("KII");
+  if (kii_exec == (char *) NULL) {
+    gprint (GP_ERR, "variable KII not found\n");
+    return (FALSE);
+  }
+
+  snprintf (name, 16, "[%d]", N);
+  fd = KiiOpen (kii_exec, name);
+  free (kii_exec);
+
+  if (fd < 0) {
+    gprint (GP_ERR, "error starting kii\n");
+    return (FALSE);
+  } 
+
+  Ximage[N] = fd;
+  return (TRUE);
+}
+
+int close_image (int N) {
+
+  if (N <  0) return (FALSE);
+  if (N >= NXIMAGE) return (FALSE);
+
+  KiiClose (Ximage[N]); 
+  Ximage[N] = -1;
+  return (TRUE);
+}
+
+/* return pointers for current Ximage, set if desired, test, open if needed */
+int GetImage (int *socket, int *N) {
+
+  int i, n;
+  char buffer[70];
+
+  SetImageDevice (TRUE);
+  if (N == (int *) NULL) {
+    n = Active;
+  } else {
+    if (*N >= NXIMAGE) {
+      gprint (GP_ERR, "invalid Ximage window %d\n", *N); 
+      return (FALSE);
+    }
+    if (*N < 0) {
+      *N = n = Active;
+    } else {
+      Active = n = *N;
+    }
+  }
+  
+  /* test Ximage[0], flush junk from pipe */
+  signal (SIGPIPE, XImageDead);
+  fcntl (Ximage[n], F_SETFL,  O_NONBLOCK); 
+  for (i = 0; (read (Ximage[n], buffer, 64) > 0) && (i < 20); i++);
+  fcntl (Ximage[n], F_SETFL, !O_NONBLOCK); 
+  
+  if (Ximage[n] < 1) {
+    if (!open_image(n)) {
+      return (FALSE);
+    }
+  }
+
+  if (socket != (int *) NULL) *socket = Ximage[n];
+
+  return (TRUE);
+
+}
+
+void SetImageName (char *name) {
+  strcpy (Ximbuffer[Active], name);
+}
+
+char *GetImageName () {
+  return (Ximbuffer[Active]);
+}
+ 
+// leave this information on kapa, request when needed? 
+void SetImageScale (double zero, double range) {
+  Xzero[Active] = zero;
+  Xrange[Active] = range;
+}
+void GetImageScale (double *zero, double *range) {
+  *zero = Xzero[Active];
+  *range = Xrange[Active];
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_kapa.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_kapa.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/open_kapa.c	(revision 17422)
@@ -0,0 +1,292 @@
+# include "display.h"
+# include "shell.h"
+# include <assert.h>
+
+
+/* kapa support for the new version of kapa (v2.0), which has both graph and image
+ * elements merged into a single display device.  The user may now open an arbitrary
+ * number of kapa windows, and the display information is retrieved from kapa across the
+ * socket when it is needed.  Communication is now via an INET socket (not a UNIX socket).
+ */
+
+/* list of available socket connections */
+static int        Active;        // currently active socket entry (index value, not socket value)
+static int       *Socket = NULL; // list of available sockets
+static char     **Device = NULL; // list of device names for each socket
+static int       Ndevice = 0;    // number of available sockets / devices
+
+void InitKapa () {
+
+  Active  = -1;					  // -1 is the INVALID entry
+  Ndevice = 0;					  // number of defined sockets
+  ALLOCATE (Device, char *, 1);			  // for future REALLOCATE calls
+  ALLOCATE (Socket, int, 1);			  // for future REALLOCATE calls
+}
+
+void FreeKapa () {
+  
+  free (Device);
+  free (Socket);
+}
+
+// add new device name if not found
+int AddKapaDevice (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N != -1) return (N);
+  N = Ndevice;
+  Ndevice ++;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+  Device[N] = strcreate (name);
+  Socket[N] = -1;
+  return (N);
+}
+
+// delete device by name, close if not closed
+int DelKapaDevice (char *name) {
+
+  int i, N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) return (FALSE);
+
+  if (Socket[N] != -1) close (Socket[N]);
+  free (Device[N]);
+  for (i = N; i < Ndevice - 1; i++) {
+    Device[i] = Device[i+1];
+    Socket[i] = Socket[i+1];
+  }
+
+  if (N == Active) {
+    Active = -1;
+  }
+
+  Ndevice --;
+  REALLOCATE (Device, char *, Ndevice);
+  REALLOCATE (Socket, int, Ndevice);
+
+  return (TRUE);
+}
+
+// returns the entry of the requested device, or -1 if not found
+int FindKapaDevice (char *name) {
+
+  int i;
+
+  if (name == NULL) return (-1); 
+
+  for (i = 0; i < Ndevice; i++) {
+    if (!strcmp(Device[i], name)) return (i);
+  }
+  return (-1);
+}
+
+// set the active device to the given device, open if needed
+int open_kapa (int entry) {
+
+  int fd;
+  char *kapa_exec, *kapa_name;
+
+  // find the given device number, or create. set this to active
+  assert (entry >= 0);
+  assert (entry <  Ndevice);
+
+  // if the (now) active socket is not open, open it
+  if (Socket[entry] < 0) {
+    kapa_exec = get_variable ("KAPA");
+    if (kapa_exec == (char *) NULL) {
+      gprint (GP_ERR, "variable KAPA not found\n");
+      return (FALSE);
+    }
+
+    // KAPA may be either kapa://host or /path/to/program
+    ALLOCATE (kapa_name, char, strlen(Device[entry]) + 5);
+    snprintf (kapa_name, strlen(Device[entry]) + 5, "[%s]", Device[entry]);
+
+    if (!strncmp (kapa_exec, "unix://", 7)) {
+        fd = KapaOpenNamedSocket (&kapa_exec[7], "psphot");
+    } else {
+	fd = KapaOpen (kapa_exec, kapa_name);
+    }
+
+    free (kapa_exec);
+    free (kapa_name);
+
+    if (fd < 0) {
+      gprint (GP_ERR, "error starting kapa device %s\n", Device[entry]);
+      return (FALSE);
+    } 
+    Socket[entry] = fd;
+  } 
+  Active = entry;
+  return (TRUE);
+}
+
+/**************** graph specific ops *******************/
+
+// return the current device name, if set 
+char *GetKapaName () {
+  if (Active < 0) return NULL;
+  return Device[Active];
+}
+
+/* return pointers for named device or current; open if needed */
+// if fd == NULL, don't return the value
+// if name == NULL, use the currently active device
+int GetGraph (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetGraphData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetGraphData (Graphdata *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetGraphData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetGraph (Graphdata *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetGraphData (Socket[Active], data);
+  return (TRUE);
+}
+
+/************* image ops ***********/
+
+/* return pointers for current Ximage, set if desired, test, open if needed */
+int GetImage (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      entry = AddKapaDevice ("0");
+    } else {
+      entry = Active;
+    }
+  } else {
+    entry = AddKapaDevice (name);
+  }
+  
+  if (!open_kapa (entry)) {
+    return (FALSE);
+  }
+  
+  if (data != NULL) KapaGetImageData (Socket[Active], data);
+  if (fd != NULL) *fd = Socket[Active];
+
+  return (TRUE);
+}
+
+/* return pointers for given kapa, don't set or open */
+int GetImageData (KapaImageData *data, int *fd, char *name) {
+
+  int entry;
+
+  if (name == NULL) {
+    if (Active < 0) {
+      gprint (GP_ERR, "no active kapa window\n"); 
+      return (FALSE);
+    }
+    entry = Active;
+  } else {
+    entry = FindKapaDevice (name);
+    if (entry < 0) {
+      gprint (GP_ERR, "invalid kapa window %s\n", name); 
+      return (FALSE);
+    }
+  }
+
+  if (fd != NULL) *fd = Socket[entry];
+  if (data != NULL) KapaGetImageData (Socket[entry], data);
+  return (TRUE);
+}
+
+/* assign given values to current kapa */
+int SetImage (KapaImageData *data) {
+  if (Active < 0) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  if (Socket[Active] == -1) {
+    gprint (GP_ERR, "no active kapa window\n"); 
+    return (FALSE);
+  }
+  KapaSetImageData (Socket[Active], data);
+  return (TRUE);
+}
+
+int close_kapa (char *name) {
+
+  int N;
+
+  N = FindKapaDevice (name);
+  if (N == -1) {
+    if (Active < 0) return (FALSE);
+    name = Device[Active];
+  }
+  DelKapaDevice (name);
+  return (TRUE);
+}
+
+void QuitKapa () {
+
+  int i;
+  
+  for (i = 0; i < Ndevice; i++) {
+    if (Socket[i] != -1) close (Socket[i]);
+    if (Device[i] != NULL) free (Device[i]);
+  }
+  REALLOCATE (Socket, int, 1);
+  REALLOCATE (Device, char *, 1);
+
+  Ndevice = 0;
+  Active = -1;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/page.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/page.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/page.c	(revision 17422)
@@ -0,0 +1,244 @@
+# include "data.h"
+
+void InitPage (Page *page, char *name) {
+
+    page[0].name = strcreate (name);
+
+    page[0].Nwords = 0;
+    page[0].NWORDS = 16;
+    ALLOCATE (page[0].words, char *, page[0].NWORDS);
+    ALLOCATE (page[0].value, char *, page[0].NWORDS);
+}
+
+void FreePage (Page *page) {
+
+    int i;
+
+    free (page[0].name);
+    for (i = 0; i < page[0].Nwords; i++) {
+      free (page[0].words[i]);
+      free (page[0].value[i]);
+    }
+    free (page[0].words);
+    free (page[0].value);
+    free (page);
+}
+
+/* return the given page */
+Page *GetPage (Book *book, int where) {
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+  return (book[0].pages[where]);
+}
+
+/* return the given page with key restrictions */
+Page *GetPageRestricted (Book *book, int where, char *keyName, char *keyValue) {
+
+  int i;
+  int N, Nout;
+  char *value;
+
+  if (where < 0) where += book[0].Npages;
+  if (where < 0) return NULL;
+  if (where >= book[0].Npages) return NULL;
+
+  Nout = -1;
+  if (where >= 0) {
+    N = -1;
+    for (i = 0; (i < book[0].Npages) && (N < where); i++) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N++;
+	Nout = i;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N++;
+	Nout = i;
+      }
+    }
+  } else {
+    N = 0;
+    for (i = book[0].Npages - 1; (i >= 0) && (N > where); i--) {
+      value = BookGetWord (book[0].pages[i], keyName);
+      if ((value == NULL) && !strcmp (keyValue, "NULL")) {
+	N--;
+      } 
+      if ((value != NULL) && !strcmp (keyValue, value)) {
+	N--;
+      }
+    }
+  }
+
+  if (N != where) return NULL;
+
+  return (book[0].pages[Nout]);
+}
+
+/* return the given page */
+/* XXX use index to find more quickly */
+Page *FindPage (Book *book, char *name) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    if (!strcmp (book[0].pages[i][0].name, name)) {
+      return (book[0].pages[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named page */
+Page *CreatePage (Book *book, char *name) {
+
+  int N;
+  Page *page;
+
+  page = FindPage (book, name);
+  if (page != NULL) return (page);
+
+  N = book[0].Npages;
+  book[0].Npages ++;
+  if (book[0].Npages >= book[0].NPAGES) {
+    book[0].NPAGES += 16;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+  ALLOCATE (page, Page, 1);
+  InitPage (page, name);
+  book[0].pages[N] = page;
+  book[0].pageIDs[N] = strcreate(name);
+  // book[0].index[N] = N;
+  
+  /* at this point, I should sort the index */
+
+  return (page);
+}
+
+void sortpages (int *seq, Page **pages, char **pageIDs, int N) {
+
+# define SWAPFUNC(A,B){ Page *tmpPage; char *tmpID; \
+  tmpPage = pages[A];   pages[A]   = pages[B];   pages[B] = tmpPage; \
+  tmpID   = pageIDs[A]; pageIDs[A] = pageIDs[B]; pageIDs[B] = tmpID; \
+}
+# define COMPARE(A,B)(seq[A] < seq[B])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+/* make a new named page */
+int ShufflePages (Book *book) {
+
+  int i, N, *seq;
+
+  N = book[0].Npages;
+
+  // generate a random index
+  ALLOCATE (seq, int, N);
+  for (i = 0; i < N; i++) {
+    seq[i] = N * drand48();
+  }
+
+  // sort the pages by the random index
+  sortpages (seq, book[0].pages, book[0].pageIDs, N);
+  free (seq);
+
+  return (TRUE);
+}
+
+/* delete a page in a book */
+int DeletePage (Book *book, Page *page) {
+
+  char *pageID;
+  int i, N, NPAGES_2;
+
+  /* find page in page list */
+  N = -1;
+  for (i = 0; i < book[0].Npages; i++) {
+    if (book[0].pages[i] == page) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  pageID = book[0].pageIDs[i];
+
+  for (i = N; i < book[0].Npages - 1; i++) {
+    book[0].pages[i] = book[0].pages[i + 1];
+    book[0].pageIDs[i] = book[0].pageIDs[i + 1];
+    // book[0].index[i] = book[0].index[i + 1];
+  }
+  book[0].Npages --;
+  NPAGES_2 = MAX (16, book[0].NPAGES / 2);
+  if (book[0].Npages < NPAGES_2) {
+    book[0].NPAGES = NPAGES_2;
+    REALLOCATE (book[0].pages, Page *, book[0].NPAGES);
+    REALLOCATE (book[0].pageIDs, char *, book[0].NPAGES);
+    // REALLOCATE (book[0].index, int, book[0].NPAGES);
+  }
+
+  FreePage (page);
+  free (pageID);
+  return (TRUE);
+}
+
+void ListPages (Book *book) {
+
+  int i;
+
+  for (i = 0; i < book[0].Npages; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", book[0].pages[i][0].name, book[0].pages[i][0].Nwords);
+  }
+  return;
+}
+
+void ListWords (Page *page) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    gprint (GP_ERR, "%-15s %15s\n", page[0].words[i], page[0].value[i]);
+  }
+  return;
+}
+
+int BookSetWord (Page *page, char *word, char *value) {
+
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      free (page[0].value[i]);
+      page[0].value[i] = strcreate (value);
+      return TRUE;
+    }
+  }
+
+  page[0].Nwords ++;
+  if (page[0].Nwords >= page[0].NWORDS) {
+    page[0].NWORDS += 16;
+    REALLOCATE (page[0].words, char *, page[0].NWORDS);
+    REALLOCATE (page[0].value, char *, page[0].NWORDS);
+  }      
+  page[0].words[i] = strcreate (word);
+  page[0].value[i] = strcreate (value);
+  return (TRUE);
+}
+
+char *BookGetWord (Page *page, char *word) {
+  int i;
+
+  for (i = 0; i < page[0].Nwords; i++) {
+    if (!strcmp (page[0].words[i], word)) {
+      return (page[0].value[i]);
+    }
+  }
+  return NULL;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/precess.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/precess.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/precess.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "data.h"
+    
+double get_epoch (char *in_epoch, char mode) {
+
+  int done;
+  double epoch;
+
+  epoch = 2000.0;
+  done = FALSE;
+  if (in_epoch[0] == 'B') {
+    epoch = BtoJ(atof(&in_epoch[1]));
+    done = TRUE;
+  }
+
+  if (in_epoch[0] == 'J') {
+    epoch = atof(&in_epoch[1]);
+    done = TRUE;
+  }
+
+  if (!done && (mode == 'B')) {
+    epoch = BtoJ(atof(in_epoch));
+    done = TRUE;
+  }
+    
+  if (!done && (mode == 'J')) {
+    epoch = atof(in_epoch);
+    done = TRUE;
+  }
+
+  if (!done) {
+    gprint (GP_ERR, "error finding epoch %s\n", in_epoch);
+    return FALSE;
+  }
+  
+  return (epoch);
+
+}
+
+  
+double BtoJ (double in_epoch) {
+
+  double JD, out_epoch;
+
+  JD = (in_epoch - 1900.0)*365.242198781 + 2415020.31352;
+  out_epoch = 2000.0 + (JD - 2451545.0)/365.25;
+
+  return (out_epoch);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/queues.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/queues.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/queues.c	(revision 17422)
@@ -0,0 +1,399 @@
+# include "data.h"
+# define DEBUG 0
+
+Queue **queues;   /* queue to store the list of all queues */
+int    Nqueues;   /* number of currently defined queues */
+int    NQUEUES;   /* number of currently allocated queues */
+
+void InitQueues () {
+  Nqueues = 0;
+  NQUEUES = 16;
+  ALLOCATE (queues, Queue *, NQUEUES); 
+}
+
+void FreeQueues () {
+
+  int i, j;
+
+  for (i = 0; i < Nqueues; i++) {
+    for (j = 0; j < queues[i][0].Nlines; j++) {
+      free (queues[i][0].lines[j]);
+    }
+    free (queues[i][0].lines);
+    free (queues[i][0].name);
+    free (queues[i]);
+  }
+  free (queues);
+}
+
+/* list known queues */
+void ListQueues () {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    gprint (GP_LOG, "%-15s %3d\n", queues[i][0].name, queues[i][0].Nlines);
+  }
+  return;
+}
+
+/* return the given queue */
+Queue *FindQueue (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nqueues; i++) {
+    if (!strcmp (queues[i][0].name, name)) {
+      return (&queues[i][0]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named queue */
+int InitQueue (Queue *queue) {
+
+  int i;
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+
+  if (DEBUG) fprintf (stderr, "init: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+  
+  return (TRUE);
+}
+
+/* make a new named queue */
+Queue *CreateQueue (char *name) {
+
+  int N;
+  Queue *queue;
+
+  queue = FindQueue (name);
+  if (queue != NULL) return (queue);
+
+  N = Nqueues;
+  Nqueues ++;
+  CHECK_REALLOCATE (queues, Queue *, NQUEUES, Nqueues, 16);
+  ALLOCATE (queue, Queue, 1);
+  queue[0].Nlines = 0;
+  queue[0].NLINES = 16;
+  queue[0].name = strcreate (name);
+  ALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  queues[N] = queue;
+  return (queue);
+}
+
+/* delete a queue */
+int DeleteQueue (Queue *queue) {
+
+  int i, N, NQUEUES_2;
+
+  /* find queue in queue list */
+  N = -1;
+  for (i = 0; i < Nqueues; i++) {
+    if (queues[i] == queue) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nqueues - 1; i++) {
+    queues[i] = queues[i + 1];
+  }
+  Nqueues --;
+  NQUEUES_2 = MAX (16, NQUEUES / 2);
+  if (Nqueues < NQUEUES_2) {
+    NQUEUES = NQUEUES_2;
+    REALLOCATE (queues, Queue *, NQUEUES);
+  }
+
+  free (queue[0].name);
+  for (i = 0; i < queue[0].Nlines; i++) {
+    free (queue[0].lines[i]);
+  }
+  free (queue[0].lines);
+  free (queue);
+  return (TRUE);
+}
+
+void PushNamedQueue (char *name, char *line) {
+
+  Queue *queue;
+  
+  queue = FindQueue (name);
+  if (queue == NULL) {
+    queue = CreateQueue (name);
+  }
+  PushQueue (queue, line);
+  return;
+}
+
+/* push line onto queue.  return chars create new lines */
+void PushQueue (Queue *queue, char *line) {
+
+  int N;
+  char *p, *q;
+
+  p = line;
+  q = strchr (line, '\n');
+  N = queue[0].Nlines;
+  while (q != NULL) {
+    queue[0].lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    queue[0].lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (queue[0].lines, char *, queue[0].NLINES, N, 16);
+  }
+  queue[0].Nlines = N;
+  return;
+}
+
+// return a newly allocated string containing the requested key value
+char *ChooseSingleKey (char *line, int Key) {
+
+  int i;
+  char *key, *p;
+
+  if (Key == -1) {
+    key = strcreate (line);
+    return (key);
+  }
+
+  key = line;
+  for (i = 0; (i < Key) && (key != NULL); i++) {
+    p = nextword (key);
+    key = p;
+  }
+  key = thisword (key);
+  return (key);
+}
+
+/* construct merged key given keylist of the form K:N:M */ 
+char *ChooseKey (char *line, char *keylist) {
+
+  char *output, *entry, *key, *p, *q;
+  int first, keynum;
+
+  if (line == NULL) return (NULL);
+  if (keylist == NULL) return (line);
+
+  ALLOCATE (output, char, strlen(line) + 1);
+  memset (output, 0, strlen(line) + 1);
+
+  first = TRUE;
+  p = q = keylist;
+  while (q != NULL) {
+    q = strchr (p, ':');
+    if (q == NULL) {
+      entry = strcreate (p);
+    } else {
+      entry = strncreate (p, q - p);
+    }
+    keynum = atoi (entry);
+    free (entry);
+
+    key = ChooseSingleKey (line, keynum);
+
+    if (!first) strcat (output, ":");
+    if (key) {
+	strcat (output, key);
+	free (key);
+    }
+
+    if (q != NULL) p = q + 1;
+    first = FALSE;
+  }
+  return (output);
+}
+
+/* push line onto queue, skipping existing matches (optionally by key) */
+void PushQueueUnique (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+/* push line onto queue, replacing matches (optionally by Key) */
+void PushQueueReplace (Queue *queue, char *line, char *Key) {
+
+  int i, j, N, found;
+  char *p, *q, *key1, *key2;
+  Queue tmp;
+
+  /* init tmp queue */
+  tmp.Nlines = 0;
+  tmp.NLINES = 16;
+  ALLOCATE (tmp.lines, char *, tmp.NLINES);
+
+  /* push entries on tmp queue */
+  p = line;
+  q = strchr (line, '\n');
+  N = tmp.Nlines;
+  while (q != NULL) {
+    tmp.lines[N] = strncreate (p, q - p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+    p = q + 1;
+    q = strchr (p, '\n');
+  }    
+  if (*p) {
+    tmp.lines[N] = strcreate (p);
+    N++;
+    CHECK_REALLOCATE (tmp.lines, char *, tmp.NLINES, N, 16);
+  }
+  tmp.Nlines = N;
+
+  /* add unique entries in tmp to queue */
+  for (i = 0; i < tmp.Nlines; i++) {
+    key1 = ChooseKey (tmp.lines[i], Key);
+    if (key1 == NULL) continue;
+    found = FALSE;
+    for (j = 0; !found && (j < queue[0].Nlines); j++) {
+      key2 = ChooseKey (queue[0].lines[j], Key);
+      if (key2 == NULL) continue;
+      found = !strcmp (key1, key2);
+      if (found) {
+	// XXX do I need to free queue[0].lines[j]??
+	queue[0].lines[j] = strcreate (tmp.lines[i]);
+      }
+      free (key2);
+    }      
+    if (!found) PushQueue (queue, tmp.lines[i]);
+    free (key1);
+  }
+  for (i = 0; i < tmp.Nlines; i++) {
+    free (tmp.lines[i]);
+  } 
+  free (tmp.lines);
+  return;
+}
+
+char *PopQueue (Queue *queue) {
+
+  int i, NLINES_2;
+  char *line;
+
+  if (queue[0].Nlines == 0) return (NULL);
+  line = queue[0].lines[0];
+
+  for (i = 0; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+/* pop the first entry which for which the key matches */
+char *PopQueueMatch (Queue *queue, char *Key, char *value) {
+
+  int i, choice, NLINES_2;
+  char *line, *test;
+
+  if (queue[0].Nlines == 0) return (NULL);
+
+  /* find the matching key */
+  choice = -1;
+  for (i = 0; (i < queue[0].Nlines) && (choice == -1); i++) {
+      test = ChooseKey (queue[0].lines[i], Key);
+      if (test == NULL) continue;
+      if (strcmp (value, test)) { 
+	free (test);
+	continue;
+      }
+      free (test);
+      choice = i;
+  }
+  if (choice == -1) return NULL;
+
+  line = queue[0].lines[choice];
+
+  for (i = choice; i < queue[0].Nlines - 1; i++) {
+    queue[0].lines[i] = queue[0].lines[i+1];
+  }
+  queue[0].Nlines --;
+
+  /* shrink queue allocation if small enough */
+  NLINES_2 = MAX (16, queue[0].NLINES / 2);
+  if (queue[0].Nlines < NLINES_2) {
+    queue[0].NLINES = NLINES_2;
+    REALLOCATE (queue[0].lines, char *, queue[0].NLINES);
+  }    
+  return (line);
+}
+
+int PrintQueue (Queue *queue) {
+
+  int i;
+
+  if (queue[0].Nlines == 0) return (TRUE);
+
+  if (DEBUG) fprintf (stderr, "print: %s (%zx) : %d of %d\n", queue[0].name, (size_t) queue, queue[0].Nlines, queue[0].NLINES);
+
+  for (i = 0; i < queue[0].Nlines; i++) {
+    gprint (GP_LOG, "%s\n", queue[0].lines[i]);
+  }
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/spline.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/spline.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/spline.c	(revision 17422)
@@ -0,0 +1,59 @@
+# include "data.h"
+
+/* construct the natural spline for x, y in y2 */
+void spline_construct (float *x, float *y, int N, float *y2) {
+
+  int i;
+  float dy, dx, *tmp;
+  
+  ALLOCATE (tmp, float, N);
+
+  y2[0] = tmp[0] = 0.0;
+  
+  for (i = 1; i < N-1; i++) {
+    dx = (x[i+0] - x[i-1]) / (x[i+1] - x[i-1]);
+    dy = dx * y2[i-1] + 2.0;
+    y2[i] = (dx - 1.0) / dy;
+    tmp[i] = (y[i+1] - y[i+0]) / (x[i+1] - x[i+0]) - (y[i+0] - y[i-1]) / (x[i+0] - x[i-1]);
+    tmp[i] = (6.0 * tmp[i] / (x[i+1] - x[i-1]) - dx*tmp[i-1]) / dy;
+  }
+  
+  y2[N-1] = 0;
+  for (i = N-2; i >= 1; i--)
+    y2[i] = y2[i]*y2[i+1] + tmp[i];
+
+  free (tmp);
+}
+
+/* evaluate spline for x, y, y2 at X */
+float spline_apply (float *x, float *y, float *y2, int N, float X) {
+
+  int i, lo, hi;
+  float dx, a, b, value;
+  
+  /* find correct element in array (x must be sorted) */
+  lo = 0;
+  hi = N-1;
+  while (hi - lo > 1) {
+    i = 0.5*(hi+lo);
+    if (x[i] > X) {
+      hi = i;
+    } else {
+      lo = i;
+    }
+  }
+
+  /* error condition: duplicate abssisca */
+  dx = x[hi] - x[lo];
+  if (dx == 0.0) {
+    return (HUGE_VAL);
+  }
+
+  /* evaluate spline */
+  a = (x[hi] - X) / dx;
+  b = (X - x[lo]) / dx;
+
+  value = a*y[lo] + b*y[hi] + ((a*a*a - a)*y2[lo] + (b*b*b - b)*y2[hi])*(dx*dx) / 6.0;
+  return (value);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/starfuncs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/starfuncs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/starfuncs.c	(revision 17422)
@@ -0,0 +1,251 @@
+# include "data.h"
+
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max) {
+
+  double *ring;
+  double x, y, x2, y2, xy, I, sky, FWHMx, FWHMy, value, mag, Sxy;
+  int i, j, n, Npix2, Nring, Nmax;
+  double Npts, gain, dsky2, dmag, peak, offset;
+  char *string;
+  
+  string = get_variable ("GAIN");
+  if (string == (char *) NULL) {
+    gprint (GP_ERR, "assuming a value of 1.0\n");
+    gain = 1.0;
+  } else {
+    gain = atof (string);
+  }
+  Nborder = MAX (1, Nborder);
+  Nborder = MIN (1000, Nborder);
+  
+  Npix2 = (int)(0.5*Npix);
+  Npix = 2 * Npix2 + 1;
+  Nring = 4*Nborder*(Nborder + Npix);
+  ALLOCATE (ring, double, Nring);
+  bzero (ring, sizeof(double)*Nring);
+
+  n = 0;  
+  for (j = 0; j < Nborder; j++) {
+    for (i = X - Npix2 - Nborder; i < X + Npix2 + Nborder + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, i, (int)(Y - Npix2 - j));
+      ring[n+1] = gfits_get_matrix_value (matrix, i, (int)(Y + Npix2 + j));
+    }
+    for (i = Y - Npix2; i < Y + Npix2 + 1; i++, n+=2) {
+      ring[n]   = gfits_get_matrix_value (matrix, (int)(X - Npix2 - j), i);
+      ring[n+1] = gfits_get_matrix_value (matrix, (int)(X + Npix2 + j), i);
+    }
+  }
+  dsort (ring, Nring);
+  for (Npts = sky = dsky2 = 0, i = 0.25*Nring; i < 0.75*Nring; i++, Npts += 1.0) {
+    sky += ring[i];
+    dsky2 += ring[i]*ring[i];
+  }
+  sky = sky / Npts;
+  dsky2 = dsky2 / Npts - sky*sky;
+  free (ring);
+
+  peak = 0;
+  Npts = Nmax = 0;
+  x = y = x2 = y2 = xy = I = 0;
+  for (i = X - Npix2; i < X + Npix2 + 1; i++) {
+    for (j = Y - Npix2; j < Y + Npix2 + 1; j++) {
+      value = gfits_get_matrix_value (matrix, i, j);
+      offset = value - sky;
+      x  += i*offset;
+      y  += j*offset;
+      x2 += i*i*offset;
+      y2 += j*j*offset;
+      xy += i*j*offset;
+      I  += offset;
+      Npts ++;
+      if (value > max) {
+	Nmax ++;
+      }
+      if (value > peak) peak = value;
+    }
+  }
+
+  x = x / I;
+  y = y / I;
+  FWHMx = 2.355*sqrt (fabs(x2 / I - x*x));
+  FWHMy = 2.355*sqrt (fabs(y2 / I - y*y));
+  Sxy   = xy / I - x*y;
+  mag = -2.5*log10(I);
+  dmag = sqrt (fabs(1.0 / (gain*I) + Npts*dsky2 / (I*I)));
+  x = x + 1;
+  y = y + 1;
+  
+  set_variable ("Xg", x);
+  set_variable ("Yg", y);
+  set_variable ("SXg", FWHMx);
+  set_variable ("SYg", FWHMy);
+  set_variable ("SXYg", Sxy);
+  set_variable ("Sg", sky);
+  set_variable ("dSg", sqrt (fabs (dsky2)));
+  set_variable ("Zg", mag);
+  set_variable ("dZg", dmag);
+  set_variable ("Zcg", I);
+  set_variable ("Zpk", peak);
+  set_int_variable ("Nsat", Nmax);
+  
+  gprint (GP_LOG, "%f %f %f %f %f %f %f %f\n", x, y, FWHMx, FWHMy, sky, I, mag, dmag);
+
+  return (mag);
+
+}
+
+static double Raper  =  5;
+static double Rinner = 10;
+static double Router = 15;
+static double *sky = NULL;
+
+int set_rough_radii (double Ra, double Ri, double Ro) {
+
+  Raper = Ra;
+  Rinner = Ri;
+  Router = Ro;
+  if (sky == NULL) {
+    ALLOCATE (sky, double, SQ(2*Router + 1));
+  } else {
+    REALLOCATE (sky, double, SQ(2*Router + 1));
+  }
+  return (TRUE);
+}
+
+/* use a circular aperture */
+int get_rough_star (float *data, int Nx, int Ny, int x, int y,
+		    float *xc, float *yc, 
+		    float *sx, float *sy, float *sxy,
+		    float *zs, float *zp, float *sk) {
+
+  double Ra2, Ri2, Ro2, rad2;
+  int i, j, Npts, Nsky;
+  int Xs, Xe, Ys, Ye, off, Xc, Yc;
+  double peak, fsky, value;
+  double Sx, Sy, Sx2, Sy2, Sxy, Sum;
+  
+  /* define circular boundaries */
+  Ra2 = SQ(Raper);
+  Ri2 = SQ(Rinner);
+  Ro2 = SQ(Router);
+
+  /* measure the sky level */
+  /* boundaries for the outer sky region */
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y + Router + 1, Ny);
+
+/* this sample uses a circular aperture */
+# if (0)
+  Nsky = 0;  
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      rad2 = SQ(i - x) + SQ(j - y);
+      if (rad2 > Ro2) continue;
+      if (rad2 < Ri2) continue;
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# else
+
+/* this sample uses a square outer annulus, without loop if tests */
+  Nsky = 0;  
+  Xs = MAX (x - Router, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Rinner, 0);
+  Ye = MIN (y + Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x + Rinner, 0);
+  Xe = MIN (x + Router + 1, Nx);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Xs = MAX (x - Rinner, 0);
+  Xe = MIN (x - Rinner + 1, Nx);
+  Ys = MAX (y - Router, 0);
+  Ye = MIN (y - Rinner + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  Ys = MAX (y + Rinner, 0);
+  Ye = MIN (y + Router + 1, Ny);
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    for (i = Xs; i < Xe; i++) { 
+      sky[Nsky] = data[i+off];
+      Nsky ++;
+    }
+  }
+  dsort (sky, Nsky);
+  for (Npts = fsky = 0, i = 0.25*Nsky; i < 0.75*Nsky; i++, Npts += 1.0) {
+    fsky += sky[i];
+  }
+  fsky = fsky / Npts;
+# endif
+
+  /* boundaries for the star region */
+  Xs = MAX (x - Raper, 0);
+  Xe = MIN (x + Raper + 1, Nx);
+  Ys = MAX (y - Raper, 0);
+  Ye = MIN (y + Raper + 1, Ny);
+
+  /** note that this will fail on negative flux objects */
+  peak = Npts = 0;
+  Sx = Sy = Sx2 = Sy2 = Sxy = Sum = 0;
+  for (j = Ys; j < Ye; j++) {
+    off = j*Nx;
+    Yc = j - y;
+    for (i = Xs; i < Xe; i++) {
+      Xc = i - x;
+      rad2 = SQ(Xc) + SQ(Yc);
+      if (rad2 > Ro2) continue;
+      value = data[i+off] - fsky;
+      Sx  += Xc*value;
+      Sy  += Yc*value;
+      Sx2 += Xc*Xc*value;
+      Sy2 += Yc*Yc*value;
+      Sxy += Xc*Yc*value;
+      Sum += value;
+      Npts ++;
+      if (value > peak) peak = value;
+    }
+  }
+
+  *xc = Sx / Sum;
+  *yc = Sy / Sum;
+  *sx = sqrt (fabs (Sx2 / Sum - SQ(*xc)));
+  *sy = sqrt (fabs (Sy2 / Sum - SQ(*yc)));
+  *sxy = Sxy / Sum;
+  *xc += x;
+  *yc += y;
+  *zs = Sum;
+  *zp = peak;
+  *sk = fsky;
+  /* note sigma is rough: round-off errors can introduce errors */
+  /* using values relative to x,y should minimize this effect */
+
+  return (Npts);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/style_args.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/style_args.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/style_args.c	(revision 17422)
@@ -0,0 +1,70 @@
+# include "data.h"
+# include "display.h"
+
+int style_args (Graphdata *graphmode, int *argc, char **argv, int *kapa) {
+  
+  int N;
+  char *colorName;
+  char *kapaName;
+
+  kapaName = NULL;
+  if ((N = get_argument (*argc, argv, "-n"))) {
+    remove_argument (N, argc, argv);
+    kapaName = strcreate (argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if (!GetGraph (graphmode, kapa, kapaName)) return (FALSE);
+  FREE (kapaName);
+
+  if (*argc == 1) {
+    kapaName = GetKapaName();
+    colorName = KapaColorName (graphmode[0].color);
+    gprint (GP_ERR, "current style (%s): -x %d -c %s -pt %d -lt %d -lw %f -sz %f\n", kapaName,
+	     graphmode[0].style, colorName, graphmode[0].ptype, 
+	     graphmode[0].ltype, graphmode[0].lweight,
+	     graphmode[0].size);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (*argc, argv, "-lt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ltype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-lw"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].lweight = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-pt"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ptype = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "+eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = TRUE;
+  }
+  if ((N = get_argument (*argc, argv, "-eb"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].ebar = FALSE;
+  }
+  if ((N = get_argument (*argc, argv, "-sz"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].size = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-c"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].color = KapaColorByName (argv[N]);
+    if (graphmode[0].color == -1) return (FALSE);
+    remove_argument (N, argc, argv);
+  }
+  if ((N = get_argument (*argc, argv, "-x"))) {
+    remove_argument (N, argc, argv);
+    graphmode[0].style = atof(argv[N]);
+    remove_argument (N, argc, argv);
+  }
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/svdcmp.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/svdcmp.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.data/svdcmp.c	(revision 17422)
@@ -0,0 +1,211 @@
+# include "data.h"
+
+/*
+  static float at,bt,ct;
+  #define PYTHAG(a,b) ((at=fabs(a)) > (bt=fabs(b)) ? \
+  (ct=bt/at,at*sqrt(1.0+ct*ct)) : (bt ? (ct=at/bt,bt*sqrt(1.0+ct*ct)): 0.0))
+  pythag -> hypot (check on roundoff errors) 
+*/
+
+/* use simple max? */
+#define FSIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+/* n == Nx, m == Ny */
+int svdcmp (float *a, float *w, float *v, int Nx, int Ny) {
+
+  int flag, i, its, j, jj, k, l, nm, status;
+  float c, f, h, s, x, y, z;
+  float anorm=0.0, g = 0.0, scale = 0.0;
+  float *rv1;
+
+  if (Ny < Nx) return (0);
+
+  l = nm = 0;
+  ALLOCATE (rv1, float, Nx);
+  for (i = 0; i < Nx; i++) {
+    l = i + 1;
+    rv1[i] = scale*g;
+    g = s = scale = 0.0;
+    if (i < Ny) {
+      for (k = i; k < Ny; k++) scale += fabs(a[k*Nx + i]);
+      if (scale) {
+	for (k = i; k < Ny; k++) {
+	  a[k*Nx + i] /= scale;
+	  s += a[k*Nx + i]*a[k*Nx + i];
+	}
+	f = a[i*Nx + i];
+	g  = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + i] = f-g;
+	if (i != Nx - 1) {
+	  for (j = l; j < Nx; j++) {
+	    for (s = 0.0, k = i; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	    f = s/h;
+	    for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	  }
+	}
+	for (k = i; k < Ny; k++) a[k*Nx + i] *= scale;
+      }
+    }
+    w[i] = scale*g;
+    g = s = scale = 0.0;
+    if ((i < Ny) && (i != (Nx - 1))) {
+      for (k = l; k < Nx; k++) scale += fabs(a[i*Nx + k]);
+      if (scale) {
+	for (k = l; k < Nx; k++) {
+	  a[i*Nx + k] /= scale;
+	  s += a[i*Nx + k]*a[i*Nx + k];
+	}
+	f = a[i*Nx + l];
+	g = -FSIGN(sqrt(s), f);
+	h = f*g - s;
+	a[i*Nx + l] = f-g;
+	for (k = l; k < Nx; k++) rv1[k] = a[i*Nx + k]/h;
+	if (i != Ny - 1) {
+	  for (j = l; j < Ny; j++) {
+	    for (s = 0.0, k = l; k < Nx; k++) s += a[j*Nx + k]*a[i*Nx + k];
+	    for (k = l; k < Nx; k++) a[j*Nx + k] += s*rv1[k];
+	  }
+	}
+	for (k = l; k < Nx; k++) a[i*Nx + k] *= scale;
+      }
+    }
+    anorm = MAX(anorm, (fabs(w[i]) + fabs(rv1[i])));
+  }
+
+  for (i = Nx - 1; i >= 0; i--) {
+    if (i < Nx - 1) {
+      if (g) {
+	/* isn't l == n to start?? */
+	for (j = l; j < Nx; j++) v[j*Nx + i] = (a[i*Nx + j]/a[i*Nx + l])/g;
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Nx; k++) s += a[i*Nx + k]*v[k*Nx + j];
+	  for (k = l; k < Nx; k++) v[k*Nx + j] += s*v[k*Nx + i];
+	}
+      }
+      for (j = l; j < Nx; j++) v[i*Nx + j] = v[j*Nx + i] = 0.0;
+    }
+    v[i*Nx + i] = 1.0;
+    g = rv1[i];
+    l = i;
+  }
+  for (i = Nx - 1; i >= 0; i--) {
+    l = i + 1;
+    g = w[i];
+    if (i < Nx - 1)
+      for (j = l; j < Nx; j++) a[i*Nx + j] = 0.0;
+    if (g) {
+      g = 1.0/g;
+      if (i != Nx - 1) {
+	for (j = l; j < Nx; j++) {
+	  for (s = 0.0, k = l; k < Ny; k++) s += a[k*Nx + i]*a[k*Nx + j];
+	  f = (s/a[i*Nx + i])*g;
+	  for (k = i; k < Ny; k++) a[k*Nx + j] += f*a[k*Nx + i];
+	}
+      }
+      for (j = i; j < Ny; j++) a[j*Nx + i] *= g;
+    } else {
+      for (j = i; j < Ny; j++) a[j*Nx + i] = 0.0;
+    }
+    ++a[i*Nx + i];
+  }
+
+  status = 1;
+  for (k = Nx - 1; k >= 0; k--) {
+    for (its = 0; its < 30; its++) {
+      flag = 1;
+      for (l = k; l >= 0; l--) {
+	nm = l - 1;
+	if (fabs(rv1[l])+anorm == anorm) {
+	  flag = 0;
+	  break;
+	}
+	if (fabs(w[nm])+anorm == anorm) break;
+      }
+      if (flag) {
+	c = 0.0;
+	s = 1.0;
+	for (i = l; i < k; i++) {
+	  f = s*rv1[i];
+	  if (fabs(f)+anorm != anorm) {
+	    g = w[i];
+	    h = hypot (f, g);
+	    w[i] = h;
+	    h = 1.0/h;
+	    c = g*h;
+	    s = (-f*h);
+	    for (j = 0; j < Ny; j++) {
+	      y = a[j*Nx + nm];
+	      z = a[j*Nx + i];
+	      a[j*Nx + nm] = y*c + z*s;
+	      a[j*Nx + i] = z*c - y*s;
+	    }
+	  }
+	}
+      }
+      z = w[k];
+      if (l == k) {
+	if (z < 0.0) {
+	  w[k] = -z;
+	  for (j = 0; j < Nx; j++) v[j*Nx + k] = (-v[j*Nx + k]);
+	}
+	break;
+      }
+      if (its == 29) status = 0;
+      x = w[l];
+      nm = k-1;
+      y = w[nm];
+      g = rv1[nm];
+      h = rv1[k];
+      f = ((y-z)*(y+z) + (g-h)*(g+h))/(2.0*h*y);
+      g = hypot (f, 1.0);
+      f = ((x-z)*(x+z) + h*((y/(f+FSIGN(g, f)))-h))/x;
+      c = s = 1.0;
+      for (j = l; j < nm; j++) {
+	i = j+1;
+	g = rv1[i];
+	y = w[i];
+	h = s*g;
+	g = c*g;
+	z = hypot (f, h);
+	rv1[j] = z;
+	c = f/z;
+	s = h/z;
+	f = x*c+g*s;
+	g = g*c-x*s;
+	h = y*s;
+	y = y*c;
+	for (jj = 0; jj < Nx; jj++) {
+	  x = v[jj*Nx + j];
+	  z = v[jj*Nx + i];
+	  v[jj*Nx + j] = x*c+z*s;
+	  v[jj*Nx + i] = z*c-x*s;
+	}
+	z = hypot(f, h);
+	w[j] = z;
+	if (z) {
+	  z = 1.0/z;
+	  c = f*z;
+	  s = h*z;
+	}
+	f = (c*g) + (s*y);
+	x = (c*y) - (s*g);
+	for (jj = 0; jj < Ny; jj++) {
+	  y = a[jj*Nx + j];
+	  z = a[jj*Nx + i];
+	  a[jj*Nx + j] = y*c+z*s;
+	  a[jj*Nx + i] = z*c-y*s;
+	}
+      }
+      rv1[l] = 0.0;
+      rv1[k] = f;
+      w[k] = x;
+    }
+  }
+  free(rv1);
+  return (1);
+}
+
+#undef FSIGN
+#undef MAX
+#undef PYTHAG
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/BufferOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 17422)
@@ -0,0 +1,259 @@
+# include "opihi.h"
+
+static Buffer **buffers;
+static int     Nbuffers;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitBuffers () {
+  Nbuffers = 0;
+  ALLOCATE (buffers, Buffer *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeBuffers () {
+
+  int i;
+
+  for (i = 0; i < Nbuffers; i++) {
+    gfits_free_header (&buffers[i][0].header);
+    gfits_free_matrix (&buffers[i][0].matrix);
+    free (buffers[i]);
+  }
+  free (buffers);
+}
+
+Buffer *InitBuffer () {
+  Buffer *buf;
+
+  ALLOCATE (buf, Buffer, 1);
+  bzero (buf[0].name, 1024);
+  bzero (buf[0].file, 1024);
+  ALLOCATE (buf[0].matrix.buffer, char, 1);
+  ALLOCATE (buf[0].header.buffer, char, 1);
+  return (buf);
+}
+
+int IsBuffer (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+int IsBufferPtr (Buffer *buf) {
+ 
+  int i;
+
+  if (buf == NULL) return (FALSE);
+
+  for (i = 0; (i < Nbuffers) && (buffers[i] != buf); i++);
+  if (i == Nbuffers) return (FALSE);
+  return (TRUE);
+}
+
+Buffer *SelectBuffer (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsVector(name)) goto error;
+
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  /* is a new buffer */
+  if (i == Nbuffers) { 
+    if (mode == OLDBUFFER) goto error;
+    // lock to protect static array
+    pthread_mutex_lock (&mutex);
+    Nbuffers ++;
+    REALLOCATE (buffers, Buffer *, Nbuffers);
+    buffers[i] = InitBuffer ();
+    strcpy (buffers[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (buffers[i]);
+  } 
+  /* is an old buffer */
+  if (mode == NEWBUFFER) goto error;
+  return (buffers[i]);
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid matrix %s\n", name);
+  return (NULL);
+}
+
+int CreateBuffer (Buffer *buf, int Nx, int Ny, int bitpix, float bzero, float bscale) {
+
+  /* store the default output values */
+  gfits_init_header (&buf[0].header);
+
+  /* assign the necessary internal values */
+  buf[0].header.bitpix   = -32;
+  buf[0].header.Naxes = 2;
+  buf[0].header.Naxis[0] = Nx;
+  buf[0].header.Naxis[1] = Ny;
+
+  buf[0].bitpix = bitpix;
+  buf[0].bzero  = bzero;
+  buf[0].bscale = bscale;
+  
+  /* make some test of the validity of the values */
+
+  /* create the appropriate header and matrix */
+  gfits_create_header (&buf[0].header);
+  gfits_create_matrix (&buf[0].header, &buf[0].matrix);
+
+  return (TRUE);
+}
+  
+/* copy data from in to out - new memory space */
+int CopyNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  CopyBuffer (Out, In);
+  return (TRUE);
+}
+
+int CopyBuffer (Buffer *out, Buffer *in) {
+  free (out[0].matrix.buffer);
+  free (out[0].header.buffer);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  strcpy (out[0].file, in[0].file);
+  gfits_copy_matrix (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  return (TRUE);
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedBuffer (char *out, char *in) {
+  Buffer *In, *Out;
+  if ((In  = SelectBuffer (in,  OLDBUFFER, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectBuffer (out, ANYBUFFER, FALSE)) == NULL) return (FALSE);
+  MoveBuffer (Out, In);
+  return (TRUE);
+}
+int MoveBuffer (Buffer *out, Buffer *in) {
+  int i, j;
+
+  free (out[0].matrix.buffer);
+  free (out[0].header.buffer);
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  out[0].matrix = in[0].matrix;
+  out[0].header = in[0].header;
+  strcpy (out[0].file, in[0].file);
+
+  /* delete buffer entry from buffer list, if it exists */
+  for (i = 0; (i < Nbuffers) && (in != buffers[i]); i++);
+  if (i == Nbuffers) {
+    free (in);
+    return (TRUE);
+  }
+  free (in);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* delete by ptr */
+int DeleteBuffer (Buffer *buf) {
+
+  int i, j;
+
+  if (buf == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (buf != buffers[i]); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedBuffer (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nbuffers) && (strcmp(buffers[i][0].name, name)); i++);
+  if (i == Nbuffers) return (FALSE);
+
+  gfits_free_header (&buffers[i][0].header);
+  gfits_free_matrix (&buffers[i][0].matrix);
+  free (buffers[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nbuffers - 1; j++) buffers[j] = buffers[j + 1];
+  Nbuffers --;
+  REALLOCATE (buffers, Buffer *, MAX (Nbuffers, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+void dump_buffers (int n) {
+
+  int i;
+
+  gprint (GP_ERR, "try %d\n", n);
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_ERR, "%d  %lx\n", i, (long) buffers[i]);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].name, buffers[i][0].name);
+    gprint (GP_ERR, "%d  %lx  %s\n", i, (long) buffers[i][0].file, buffers[i][0].file);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].header, (long) buffers[i][0].header.buffer);
+    gprint (GP_ERR, "%d  %lx  %lx\n", i, (long) &buffers[i][0].matrix, (long) buffers[i][0].matrix.buffer);
+    gprint (GP_ERR, "%d  %d  %d  %f %f\n", i, buffers[i][0].bitpix, buffers[i][0].unsign, buffers[i][0].bscale, buffers[i][0].bzero);
+  }
+}
+
+int PrintBuffers (int Long) {
+
+  int i;
+
+  if (Nbuffers == 0) {
+    gprint (GP_ERR, "No allocated buffers\n");
+    return (TRUE);
+  }
+  
+  if (Long) {
+    gprint (GP_LOG, "    N       name                      file     X     Y    bytes BP  U   bzero     bscale  \n");
+    for (i = 0; i < Nbuffers; i++) {
+      gprint (GP_LOG, "%5d %10s %25s %5d %5d %10d %3d %1d %10.4e %10.4e\n",
+	       i, buffers[i][0].name, buffers[i][0].file, 
+	       buffers[i][0].header.Naxis[0], buffers[i][0].header.Naxis[1],
+	       buffers[i][0].header.size + buffers[i][0].matrix.size, buffers[i][0].bitpix, 
+	       buffers[i][0].unsign, buffers[i][0].bzero, buffers[i][0].bscale);
+    }
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "    N       name                      file     X     Y    bytes\n");
+  for (i = 0; i < Nbuffers; i++) {
+    gprint (GP_LOG, "%5d %10s %25s %5d %5d %10d\n",
+	     i, buffers[i][0].name, buffers[i][0].file, 
+	     buffers[i][0].header.Naxis[0], buffers[i][0].header.Naxis[1],
+	     buffers[i][0].header.size + buffers[i][0].matrix.size);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/CommandOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/CommandOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/CommandOps.c	(revision 17422)
@@ -0,0 +1,176 @@
+# include "opihi.h"
+
+// the command table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+// if the user has installed the libedit version of readline, we need to modify a couple symbols:
+# ifndef RL_READLINE_VERSION
+# define rl_completion_matches(A,B) completion_matches(A,B)
+# endif
+
+static Command  *commands;
+static int      Ncommands;
+static int      NCOMMANDS;
+
+void InitCommands () {
+  NCOMMANDS = 20;
+  Ncommands = 0;
+  ALLOCATE (commands, Command, NCOMMANDS);
+}
+
+void FreeCommands () {
+  int i;
+
+  for (i = 0; i < Ncommands; i++) {
+    if (commands[i].real) continue;
+    free (commands[i].name);
+    free (commands[i].help);
+  }
+  free (commands);
+}
+
+void AddCommand (Command *new) {
+  
+  commands[Ncommands] = *new;
+  Ncommands ++;
+  if (Ncommands == NCOMMANDS) {
+    NCOMMANDS += 20;
+    REALLOCATE (commands, Command, NCOMMANDS);
+  }
+}
+
+int DeleteCommand (Command *command) {
+
+  int i, Nc;
+
+  Nc = -1;
+  for (i = 0; i < Ncommands; i++) {
+    if (command == &commands[i]) {
+      Nc = i;
+      break;
+    }
+  }
+  if (Nc == -1) {
+    gprint (GP_ERR, "programming error: command not found\n");
+    return (FALSE);
+  }
+
+  free (commands[Nc].name);
+  for (i = Nc + 1; i < Ncommands; i++)
+    commands[i - 1] = commands[i];
+  Ncommands --;
+  REALLOCATE (commands, Command, Ncommands);
+  return (TRUE);
+}
+
+/* return command which unambiguously matches name */
+Command *MatchCommand (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ncommands; i++) {
+    if (!strcmp (commands[i].name, name)) {
+      return (&commands[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+
+  /* not found as complete command, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Ncommands); i++) {
+    if (!strncmp (commands[i].name, name, strlen(name))) {  /* found a command */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&commands[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous command: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", commands[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Command not found.\n", name);
+  return (NULL);
+}  
+
+/* generate a command completion list for readline */
+/**** these probably do not interact well with OHANA memory!!! ****/
+char *command_generator (const char *text, int state) {
+
+  /* i must be remembered from call to call */
+  static int i, len;
+
+  /* On first call, state is set to 0: initial the state */
+  if (!state) {
+    i = -1;  
+    len = strlen (text);
+  }
+
+  /* Return the next partial match from the command list */
+  for (i++; i < Ncommands; i++) {
+    if (!len) 
+      return (strcreate(commands[i].name));
+    if (!strncmp (commands[i].name, text, len)) {
+      return (strcreate(commands[i].name));
+    }
+  }
+
+  /* If no names matched, then return NULL. */
+  return ((char *)NULL);
+}
+
+/* tell readline to use out command_generator rather than basic completion */
+char **command_completer (const char *text, int start, int end) {
+  
+  char **matches;
+
+  matches = (char **) NULL;
+  
+  if (start == 0) {
+    matches = rl_completion_matches (text, command_generator);
+  }
+
+  return (matches);
+}
+
+void sort_commands (int *seq) {
+
+  int i;
+
+  for (i = 0; i < Ncommands; i++) seq[i] = i;
+
+# define SWAPFUNC(A,B){ int tmp = seq[A]; seq[A] = seq[B]; seq[B] = tmp; }
+# define COMPARE(A,B)(strcmp (commands[seq[A]].name, commands[seq[B]].name) < 0)
+
+  OHANA_SORT (Ncommands, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+}
+
+void print_commands (FILE *f) {
+
+  int i, *seq;
+
+  ALLOCATE (seq, int, Ncommands);
+  sort_commands (seq);
+  for (i = 0; i < Ncommands; i++) {
+    fprintf (f, "%-25s -- %s\n", commands[seq[i]].name, commands[seq[i]].help);
+  }
+  free (seq);
+
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ConfigInit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ConfigInit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ConfigInit.c	(revision 17422)
@@ -0,0 +1,73 @@
+# include "opihi.h"
+
+static char *GlobalConfig = NULL;
+
+// this function is only called at start, so it is not thread protect
+int ConfigInit (int *argc, char **argv) {
+
+  char *config, *file;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    if (file != (char *) NULL) free (file);
+    return (FALSE);
+  }
+
+  GlobalConfig = config;
+
+  free (file);
+  return (TRUE);
+}
+
+// this function is only called at shutdown, so it is not thread protect
+void ConfigFree () {
+  if (GlobalConfig) free (GlobalConfig);
+  return;
+}
+
+char *VarConfig (char *keyword, char *mode, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, 0, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
+char *VarConfigEntry (char *keyword, char *mode, int entry, void *ptr) {
+
+  char *answer;
+
+  answer = get_variable (keyword);
+  if (answer == (char *) NULL) {
+    answer = ScanConfig (GlobalConfig, keyword, mode, entry, ptr);
+    return (answer);
+  }
+
+  if (!strcmp (mode, "%s"))  strcpy ((char *) ptr, answer);
+  if (!strcmp (mode, "%d"))  *(int *) ptr       = atoi (answer);
+  if (!strcmp (mode, "%u"))  *(unsigned *) ptr  = atoi (answer);
+  if (!strcmp (mode, "%ld")) *(long *) ptr      = atoi (answer);
+  if (!strcmp (mode, "%hd")) *(short *) ptr     = atoi (answer);
+  if (!strcmp (mode, "%f"))  *(float *) ptr     = atof (answer);
+  if (!strcmp (mode, "%lf")) *(double *) ptr    = atof (answer);
+
+  free (answer);
+  return (ptr);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ListOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ListOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/ListOps.c	(revision 17422)
@@ -0,0 +1,270 @@
+# include "opihi.h"
+# include "macro.h"
+
+/*** local static variables used to track the command lists  ***/
+static List *lists;			/* variable to store the list of all lists */
+static int  Nlists;			/* number of currently available lists */
+
+void InitLists () {
+  Nlists = 0;
+  ALLOCATE (lists, List, 1); 
+  return;
+}
+
+void FreeLists () {
+
+  int i, j;
+
+  for (i = 0; i < Nlists; i++) {
+    for (j = 0; j < lists[i].Nlines; j++) {
+      free (lists[i].line[j]);
+    }
+    free (lists[i].line);
+  }
+  free (lists);
+}
+
+int current_list_depth () {
+  return Nlists;
+}
+
+int increase_list_depth () {
+  Nlists ++;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  ALLOCATE (lists[Nlists].line, char *, 16);
+  lists[Nlists].Nalloc = 16;
+  lists[Nlists].Nlines = 0;
+  lists[Nlists].n = 0;
+  return Nlists;
+}
+
+int decrease_list_depth () {
+  
+  int i;
+
+  for (i = 0; i < lists[Nlists].Nlines; i++) {
+    free (lists[Nlists].line[i]);
+  }
+  free (lists[Nlists].line);
+  Nlists --;
+  REALLOCATE (lists, List, MAX (Nlists + 1, 0) + 1);
+  return Nlists;
+}
+
+/* return a new string consisting of the next line in the current list */
+char *get_next_listentry (int ThisList) {
+
+  int Nline;
+  char *output;
+
+  Nline = lists[ThisList].n;
+
+  if (Nline >= lists[ThisList].Nlines) return (NULL);
+
+  output = strcreate (lists[ThisList].line[Nline]);
+  lists[ThisList].n ++;
+  
+  return (output);
+}
+
+# if (0)
+char *remove_listentry (int current) {
+
+  int i;
+  char *output;
+
+  if ((current + 1) >= lists[Nlists].Nlines) 
+    return ((char *) NULL);
+
+  output = lists[Nlists].line[current + 1];
+  
+  for (i = current + 1; i < lists[Nlists].Nlines - 1; i++) {
+    lists[Nlists].line[i] = lists[Nlists].line[i + 1];
+  }
+
+  lists[Nlists].Nlines --;
+  return (output);
+
+}
+# endif 
+
+int add_listentry (int ThisList, char *line) {
+
+  int Nlines;
+
+  Nlines = lists[ThisList].Nlines;
+  lists[ThisList].line[Nlines] = strcreate (line);
+  lists[ThisList].Nlines ++;
+
+  if (lists[ThisList].Nlines == lists[ThisList].Nalloc) {
+    lists[ThisList].Nalloc += 16;
+    REALLOCATE (lists[ThisList].line, char *, lists[ThisList].Nalloc);
+  }
+    
+  return (lists[ThisList].Nlines);
+}
+
+int is_for_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+  
+  status = !strcmp (comm, "for");
+  free (comm);
+  return (status);
+}
+
+int is_macro_create (char *line) {
+
+  int status;
+  char *comm;
+  char *this_macro;
+  CommandF *cmd;
+
+  comm = thisword (line);
+  if (comm == NULL) return (FALSE);
+
+  status = !strcmp (comm, "macro");
+  free (comm);
+  if (!status) return (FALSE);
+  
+  this_macro = thisword (nextword (line));
+  if (this_macro == NULL) return (FALSE);
+
+  cmd = find_macro_command (this_macro);
+  free (this_macro);
+
+  if (cmd == NULL) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+}
+
+int is_if_block (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "if")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) goto escape;
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+// list (word) : nested list
+// list (word) -x : not nested list
+// list (word) -split : not nested list
+int is_list_data (char *line) {
+
+  char *comm, *temp;
+
+  temp = thisword (nextword (nextword (line)));
+  comm = thisword (line);
+
+  if (comm == NULL) goto escape;
+
+  if (strcmp (comm, "list")) goto escape;
+
+  /* if (cond) (command) does not define a complete block */
+  if (temp != NULL) {
+      if (!strcmp (temp, "-x")) goto escape;
+      if (!strcmp (temp, "-split")) goto escape;
+      if (!strcmp (temp, "-add")) goto escape;
+      if (!strcmp (temp, "-del")) goto escape;
+  }
+
+  if (temp != NULL) free (temp);
+  if (comm != NULL) free (comm);
+  return (TRUE);
+
+escape: 
+  if (comm != NULL) free (comm);
+  if (temp != NULL) free (temp);
+  return (FALSE);
+}
+
+int is_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "while");
+  free (comm);
+  return (status);
+}
+
+int is_task (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task");
+  free (comm);
+  return (status);
+}
+
+int is_task_exit (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exit");
+  free (comm);
+  return (status);
+}
+
+int is_task_exec (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+
+  status = !strcmp (comm, "task.exec");
+  free (comm);
+  return (status);
+}
+
+int is_list (char *line) {
+  
+  int status;
+
+  status = is_if_block (line);
+  status |= is_macro_create (line);
+  status |= is_for_loop (line);
+  status |= is_list_data (line);
+  status |= is_loop (line);
+  status |= is_task (line);
+  status |= is_task_exit (line);
+  status |= is_task_exec (line);
+
+  return (status);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/MacroOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 17422)
@@ -0,0 +1,158 @@
+# include "opihi.h"
+
+// the macro table is only modified by macro_create: if thread protection is needed, it
+// should be added there.  it may not be needed if programs only call macro_create in a
+// single thread.  pantasks_server only calls macro_create in the 'input' thread, so it is
+// safe.
+
+static char dot[] = ".";
+
+static Macro *macros;
+static int   Nmacros;
+static int   NMACROS;
+
+static char *MacroName = dot;
+static int   MacroDepth = 0;
+
+void InitMacros () {
+  NMACROS = 20;
+  Nmacros = 0;
+  ALLOCATE (macros, Macro, NMACROS);
+  MacroName = dot;
+  MacroDepth = 0;
+}
+
+void FreeMacros () {
+
+  int i;
+
+  for (i = 0; i < Nmacros; i++) {
+    FreeMacro (&macros[i]);
+  }
+  free (macros);
+}
+
+void SetCurrentMacroData (char *name, int depth) {
+  MacroName = name;
+  MacroDepth = depth;
+}
+
+char *GetMacroName () {
+  return (MacroName);
+}
+
+int GetMacroDepth () {
+  return (MacroDepth);
+}
+
+Macro *NewMacro (char *name) {
+  
+  macros[Nmacros].name = strcreate (name);;
+  macros[Nmacros].Nlines = 0;
+  ALLOCATE (macros[Nmacros].line, char *, 1);
+  Nmacros ++;
+  if (Nmacros == NMACROS) {
+    NMACROS += 20;
+    REALLOCATE (macros, Macro, NMACROS);
+  }
+  return (&macros[Nmacros-1]);
+}
+
+void ListMacro (Macro *macro) {
+
+  int i;
+
+  if ((macro == NULL) || (macro[0].Nlines == 0)) {
+    gprint (GP_ERR, "  macro not defined\n");
+    return;
+  }
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "  %s\n", macro[0].line[i]);
+  }
+  return;
+}
+
+void ListMacros () {
+  int i;
+  for (i = 0; i < Nmacros; i++) {
+    gprint (GP_ERR, "%s\n", macros[i].name);
+  }
+}
+
+void FreeMacro (Macro *macro) {
+  
+  int i;
+
+  if (macro == NULL) return;
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    free (macro[0].line[i]);
+  }
+  free (macro[0].line);
+  free (macro[0].name);
+  return;
+}
+
+int DeleteMacro (Macro *macro) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Nmacros; i++) {
+    if (macro == &macros[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: macro not found\n");
+    return (FALSE);
+  }
+
+  FreeMacro (&macros[Nm]);
+  for (i = Nm + 1; i < Nmacros; i++)
+    macros[i - 1] = macros[i];
+  Nmacros --;
+  return (TRUE);
+}
+
+/* return macro which unambiguously matches name */
+Macro *MatchMacro (char *name, int VERBOSE, int EXACT) {
+
+  int i, match[10], Nmatch;
+
+  /* try for an exact match first */
+  for (i = 0; i < Nmacros; i++) {
+    if (!strcmp (macros[i].name, name)) {
+      return (&macros[i]);
+    }
+  }
+  if (EXACT) {
+    if (VERBOSE) gprint (GP_ERR, "no exact match to %s\n", name);
+    return (NULL);
+  }
+      
+  /* not found as complete macro, try partial */
+  Nmatch = 0;
+  for (i = 0; (Nmatch < 10) && (i < Nmacros); i++) {
+    if (!strncmp (macros[i].name, name, strlen(name))) {  /* found a macro */
+      match[Nmatch] = i;
+      Nmatch ++;
+    }
+  }
+  if (Nmatch == 1) return (&macros[match[0]]);
+
+  if (Nmatch > 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "ambiguous macro: %s ( ", name);
+      for (i = 0; i < Nmatch; i++) {
+	gprint (GP_ERR, "%s ", macros[match[i]].name);
+      }
+      gprint (GP_ERR, ")\n");
+    }
+    return (NULL);
+  }
+  if (VERBOSE) gprint (GP_ERR, "%s: Macro not found.\n", name);
+  return (NULL);
+}  
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/Makefile	(revision 17422)
@@ -0,0 +1,78 @@
+default: libshell
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/lib.shell
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS) -fPIC
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(BASE_LDFLAGS)
+
+# opihi shell functions (libopihi) ########################
+shell = \
+$(SDIR)/BufferOps.$(ARCH).o             \
+$(SDIR)/CommandOps.$(ARCH).o		\
+$(SDIR)/ConfigInit.$(ARCH).o		\
+$(SDIR)/ListOps.$(ARCH).o	   	\
+$(SDIR)/MacroOps.$(ARCH).o		\
+$(SDIR)/SocketOps.$(ARCH).o             \
+$(SDIR)/VariableOps.$(ARCH).o	   	\
+$(SDIR)/VectorOps.$(ARCH).o             \
+$(SDIR)/check_stack.$(ARCH).o           \
+$(SDIR)/command.$(ARCH).o               \
+$(SDIR)/convert_to_RPN.$(ARCH).o	\
+$(SDIR)/dvomath.$(ARCH).o               \
+$(SDIR)/errors.$(ARCH).o		\
+$(SDIR)/evaluate_stack.$(ARCH).o  	\
+$(SDIR)/exec_loop.$(ARCH).o             \
+$(SDIR)/expand_vars.$(ARCH).o           \
+$(SDIR)/expand_vectors.$(ARCH).o        \
+$(SDIR)/gprint.$(ARCH).o		\
+$(SDIR)/interrupt.$(ARCH).o	        \
+$(SDIR)/isolate_elements.$(ARCH).o 	\
+$(SDIR)/macro_create.$(ARCH).o          \
+$(SDIR)/macro_delete.$(ARCH).o          \
+$(SDIR)/macro_edit.$(ARCH).o            \
+$(SDIR)/macro_exec.$(ARCH).o            \
+$(SDIR)/macro_funcs.$(ARCH).o           \
+$(SDIR)/macro_list.$(ARCH).o            \
+$(SDIR)/macro_read.$(ARCH).o            \
+$(SDIR)/macro_write.$(ARCH).o		\
+$(SDIR)/memstr.$(ARCH).o                \
+$(SDIR)/multicommand.$(ARCH).o          \
+$(SDIR)/parse.$(ARCH).o                 \
+$(SDIR)/parse_commands.$(ARCH).o	\
+$(SDIR)/stack_math.$(ARCH).o		\
+$(SDIR)/startup.$(ARCH).o		\
+$(SDIR)/string.$(ARCH).o                \
+$(SDIR)/timeformat.$(ARCH).o            \
+$(SDIR)/version.$(ARCH).o	   	\
+$(SDIR)/opihi.$(ARCH).o
+
+# dependancy rules for include files ########################
+incs = \
+$(INC)/opihi.h \
+$(INC)/external.h \
+$(INC)/shell.h \
+$(INC)/dvomath.h \
+$(INC)/convert.h \
+$(INC)/display.h 
+
+$(shell) : $(incs)
+
+$(LIB)/libshell.$(ARCH).a: $(shell)
+$(LIB)/libshell.$(ARCH).$(DLLTYPE): $(shell)
+
+$(DESTLIB)/libshell.a: $(LIB)/libshell.$(ARCH).a
+$(DESTLIB)/libshell.$(DLLTYPE): $(LIB)/libshell.$(ARCH).$(DLLTYPE)
+
+libshell: $(DESTLIB)/libshell.a $(DESTLIB)/libshell.$(DLLTYPE)
+
+uninstall:
+	rm -f $(DESTLIB)/libshell.a
+	rm -f $(DESTLIB)/libshell.$(DLLTYPE)
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/SocketOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 17422)
@@ -0,0 +1,287 @@
+# include "shell.h"
+
+# define MY_PORT 2000
+# define MY_WAIT 500
+# define DEBUG 0
+
+static int NVALID;
+static int Nvalid;
+static int *VALID;
+
+int InitServerSocket (SockAddress *Address) {
+
+  int status, InitSocket, length;
+
+# if (0)
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+# endif
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
+
+retry_server:
+
+# if (0)  
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (4);
+  }
+# endif
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (5);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+
+# if 0
+    fprintf (stderr, "errno: %d\n", errno);
+    fprintf (stderr, "EACCES: %d\n", EACCES);
+    fprintf (stderr, "EBADF: %d\n", EBADF);
+    fprintf (stderr, "EINVAL: %d\n", EINVAL);
+    fprintf (stderr, "ENOTSOCK: %d\n", ENOTSOCK);
+    fprintf (stderr, "EFAULT: %d\n", EFAULT);
+    fprintf (stderr, "ELOOP: %d\n", ELOOP);
+    fprintf (stderr, "ENAMETOOLONG: %d\n", ENAMETOOLONG);
+    fprintf (stderr, "ENOENT: %d\n", ENOENT);
+    fprintf (stderr, "ENOMEM: %d\n", ENOMEM);
+    fprintf (stderr, "ENOTDIR: %d\n", ENOTDIR);
+    fprintf (stderr, "EROFS: %d\n", EROFS);
+    fprintf (stderr, "EADDRNOTAVAIL: %d\n", EADDRNOTAVAIL);
+    fprintf (stderr, "EADDRINUSE: %d\n", EADDRINUSE);
+    fprintf (stderr, "ENOSR: %d\n", ENOSR);
+# endif
+
+    if (errno == EADDRINUSE) {
+	Address[0].sin_port ++;
+	if (Address[0].sin_port > MY_PORT + 10) {
+	  fprintf (stderr, "failed to find a usable port\n");
+	  exit (6);
+	}
+	goto retry_server;
+    }
+    perror ("bind: ");
+    exit (7);
+  }
+  /* repeated starts of the server are limited by xinetd or something:
+     requires 60sec timeout of the selected socket */
+
+  fprintf (stderr, "bound to port: %d\n", Address[0].sin_port);
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (8);
+  }
+  return (InitSocket);
+}
+
+int WaitServerSocket (int InitSocket, SockAddress *Address) {
+
+  int i, BindSocket;
+  SockAddress Address_in;
+  socklen_t length;
+  u_int32_t addr;
+
+  Address_in = Address[0];
+
+  length = sizeof(Address_in);
+
+  /* this is a blocking wait; use in a separate thread */
+  fcntl (InitSocket, F_SETFL, !O_NONBLOCK); 
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  BindSocket = accept (InitSocket, (struct sockaddr *) &Address_in, &length);
+  if (DEBUG) gprint (GP_ERR, "bind sock: %d\n", BindSocket);
+  if (BindSocket == -1) {
+    perror ("accept: ");
+    exit (9);
+  }
+
+  addr = Address_in.sin_addr.s_addr;
+  if (DEBUG) {
+    gprint (GP_ERR, "incoming connection from: ");
+    gprint (GP_ERR, " %u", (0xff & (addr >>  0)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >>  8)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 16)));
+    gprint (GP_ERR, ".%u", (0xff & (addr >> 24)));
+    gprint (GP_ERR, "\n");
+  }
+
+  if (Nvalid == 0) goto accepted;
+
+  for (i = 0; i < Nvalid; i++) {
+    /* valid IP addresses may be machines (120.90.121.142) or 
+       class C networks (120.90.121.0) */
+       
+    /* for machine, address must match */
+    if ((0xff & (VALID[i] >> 24)) != 0) {
+      if (addr == VALID[i]) goto accepted;
+    }
+
+    /* for network, lower three bytes of address must match */
+    if ((0xff & (VALID[i] >> 24)) == 0) {
+      if ((0x00ffffff & addr) == VALID[i]) goto accepted;
+    }
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connection rejected\n");
+  close (BindSocket);
+  return (-1);
+
+accepted:
+  if (DEBUG) gprint (GP_ERR, "connection accepted\n");
+  fcntl (BindSocket, F_SETFL, O_NONBLOCK); 
+  return (BindSocket);
+}
+
+int GetClientSocket (char *hostname) {
+
+  int i, status, InitSocket, length;
+  SockAddress Address;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+
+  if (DEBUG) {
+    gprint (GP_ERR, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+  }
+
+  Address.sin_family = AF_INET;
+  Address.sin_port   = MY_PORT;
+
+retry_client:
+  status = inet_aton (hostip, &Address.sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (10);
+  }
+
+  length = sizeof(Address);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (11);
+  }
+
+  status = connect (InitSocket, (struct sockaddr *) &Address, length);
+  if (status == -1) {
+    if (errno == ECONNREFUSED) {
+      Address.sin_port ++;
+      if (Address.sin_port > MY_PORT + 10) exit (12);
+      goto retry_client;
+    }
+    perror ("connect: ");
+    exit (13);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "connected on port: %d\n", Address.sin_port);
+  if (DEBUG) gprint (GP_ERR, "connected\n");
+  fcntl (InitSocket, F_SETFL, O_NONBLOCK); 
+  return (InitSocket);
+}
+
+int InitServerSocket_Named (char *hostname, SockAddress *Address) {
+
+  int i, status, InitSocket, length;
+  struct hostent  *host;
+  char tmpline[80], hostip[80];
+
+  host = gethostbyname (hostname);
+  bzero (hostip, 80);
+  for (i = 0; i < host[0].h_length; i++) {
+    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
+    strcat (hostip, tmpline);
+    if (i < host[0].h_length - 1) strcat (hostip, ".");
+  }
+  
+  Address[0].sin_family = AF_INET;
+  Address[0].sin_port   = MY_PORT;
+  status = inet_aton (hostip, &Address[0].sin_addr);
+  if (!status) {
+    gprint (GP_ERR, "invalid address\n");
+    exit (14);
+  }
+
+  length = sizeof(Address[0]);
+
+  InitSocket = socket (PF_INET, SOCK_STREAM, 0);
+  if (InitSocket == -1) {
+    perror ("socket: ");
+    exit (15);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "init sock: %d, len: %d\n", InitSocket, length);
+  status = bind (InitSocket, (struct sockaddr *) Address, length);
+  if (status == -1) {
+    perror ("bind: ");
+    exit (16);
+  }
+
+  status = listen (InitSocket, 10);
+  if (status == -1) {
+    perror ("listen: ");
+    exit (17);
+  }
+
+  if (DEBUG) gprint (GP_ERR, "socket listening on %s (%s:%d)\n", host[0].h_name, hostip, MY_PORT);
+  return (InitSocket);
+}
+
+/* load valid ip list */
+int DefineValidIP () {
+
+  int i, Nvalid, ip1, ip2, ip3, ip4, test, status;
+  char string[80];
+
+  Nvalid = 0;
+  NVALID = 10;
+  ALLOCATE (VALID, int, NVALID);
+  for (i = 0; VarConfigEntry ("VALID_IP", "%s", i, string) != NULL; i++) {
+    status = sscanf (string, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4);
+    test = TRUE;
+    test &= (status == 4);
+    test &= ((ip1 > 0) && (ip1 < 256)); 
+    test &= ((ip2 > 0) && (ip2 < 256)); 
+    test &= ((ip3 > 0) && (ip3 < 256)); 
+    test &= ((ip4 >=0) && (ip4 < 256)); 
+    if (!test) {
+      gprint (GP_ERR, "invalid IP address %s\n", string);
+      exit (18);
+    }
+    VALID[Nvalid] = ip1 | (ip2 << 8) | (ip3 << 16) | (ip4 << 24);
+    Nvalid ++;
+    CHECK_REALLOCATE (VALID, int, NVALID, Nvalid, 10);
+  }
+  NVALID = Nvalid;
+  REALLOCATE (VALID, int, NVALID);
+  if (NVALID == 0) {
+    free (VALID);
+    VALID = NULL;
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VariableOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VariableOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VariableOps.c	(revision 17422)
@@ -0,0 +1,290 @@
+# include "opihi.h"
+
+Variable *variables;   /* variable to store the list of all variables */
+int      Nvariables;   /* number of currently available variables */
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVariables () {
+  Nvariables = 0;
+  ALLOCATE (variables, Variable, 1); 
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVariables () {
+
+  int i; 
+
+  for (i = 0; i < Nvariables; i++) {
+    free (variables[i].name);
+    free (variables[i].value);
+  }
+  free (variables);
+}
+
+int set_local_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  /* a local variable has the form (macroname).(varname) */
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for existing local variable */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = local;
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_str_variable (char *name, char *value) {
+
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* need to use a mutex to prevent two threads from changing variable list simultaneously */
+  pthread_mutex_lock (&mutex);
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (local, variables[i].name)) {
+      free (variables[i].value);
+      free (local);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  free (local);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].value);
+      variables[i].value = strcreate (value);
+      pthread_mutex_unlock (&mutex);
+      return (TRUE);
+    }
+  }
+  /* NEW variable */
+  Nvariables ++;
+  REALLOCATE (variables, Variable, Nvariables);
+  variables[Nvariables - 1].name = strcreate (name);
+  variables[Nvariables - 1].value = strcreate (value);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int set_variable (char *name, double dvalue) {
+
+  char value[1024];
+
+  sprintf (value, "%.12g", dvalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+int set_int_variable (char *name, int ivalue) {
+
+  char value[1024];
+
+  sprintf (value, "%d", ivalue);
+  set_str_variable (name, value);
+  return (TRUE);
+}
+
+static char variable_true[] = "1";
+static char variable_false[] = "0";
+
+char *get_local_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *MacroName;
+
+  MacroName = GetMacroName ();
+
+  /* look for local variable first */
+  ALLOCATE (local, char, strlen(name) + strlen(MacroName) + 2);
+  sprintf (local, "%s.%s", MacroName, name);
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(local, variables[i].name)) {
+      free (local);
+      return (variables[i].value);
+    }
+  }
+  free (local);
+  return (NULL);
+}
+
+char *get_variable_ptr (char *name) {
+  
+  int i;
+  char *local, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  /* check for the existence of the given name (after ?) */
+  /* return a string which should not be freed */
+  if (*name == '?') {
+    value = get_variable_ptr (&name[1]);
+    if (value == NULL) {
+      return variable_false;
+    }
+    return variable_true;
+  }
+
+  // check first for a local variable
+  local = get_local_variable_ptr (name);
+  if (local != NULL) return (local);
+
+  /* look for global variable */
+  for (i = 0; i < Nvariables; i++) { /* find the variable mentioned */
+    if (!strcmp(name, variables[i].name)) {
+      return (variables[i].value);
+    }
+  }
+  return (NULL);
+}
+
+char *get_variable (char *name) {
+  
+  char *ptr, *value;
+
+  if (name == NULL) return (NULL);
+  if (*name == 0) return (NULL);
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    value = strcreate (ptr);
+    return value;
+  }
+  return (NULL);
+}
+
+/* return TRUE / FALSE if name is an existant variable */
+int get_variable_exists (char *name) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) return (TRUE);
+  return (FALSE);
+}
+
+float get_variable_default (char *name, float dvalue) {
+
+  char *value;
+  float fvalue;
+
+  value = get_variable (name);
+  if (value == NULL) {
+    return (dvalue);
+  }
+  fvalue = atof (value);
+  return (fvalue);
+}
+
+double get_double_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+
+  *found = FALSE;
+  return (0.0);
+}
+
+int get_int_variable (char *name, int *found) {
+  
+  char *ptr;
+
+  ptr = get_variable_ptr (name);
+  if (ptr) {
+    *found = TRUE;
+    return (atof (ptr));
+  }
+  *found = FALSE;
+  return (0.0);
+}
+
+int DeleteNamedScalar (char *name) {
+
+  int i, j;
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) {
+      free (variables[i].name);
+      free (variables[i].value);
+      for (j = i; j < Nvariables - 1; j++) {
+	variables[j] = variables[j + 1];
+      }
+      Nvariables --;
+      REALLOCATE (variables, Variable, MAX (Nvariables, 1));
+      return (TRUE);
+    }
+  }
+  return (FALSE);
+}
+
+int IsScalar (char *name) { 
+
+  int i;
+
+  for (i = 0; i < Nvariables; i++) {
+    if (!strcmp (name, variables[i].name)) return (TRUE);
+  }
+  return (FALSE);
+}
+
+void ListVariables () {
+
+  int i;
+
+  if (Nvariables == 0) {
+    gprint (GP_ERR, "No defined variables\n");
+    return;
+  }
+
+  for (i = 0; i < Nvariables; i++) {
+    gprint (GP_ERR, "%s = %s\n", variables[i].name, variables[i].value);
+  }
+  return;
+}
+
+int SelectScalar (char *string, double *value) {
+
+  char *end;
+
+  /* if string is a number, return TRUE */
+  *value = strtod (string, &end);
+  if (end == string + strlen(string)) return (TRUE);
+
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VectorOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/VectorOps.c	(revision 17422)
@@ -0,0 +1,191 @@
+# include "opihi.h"
+
+static Vector **vectors;
+static int     Nvectors;
+  
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void InitVectors () {
+  Nvectors = 0;
+  ALLOCATE (vectors, Vector *, 1);
+}
+
+// this function is NOT thread protected : it is only used in startup and/or shutdown
+void FreeVectors () {
+
+  int i;
+
+  for (i = 0; i < Nvectors; i++) {
+    free (vectors[i][0].elements);
+    free (vectors[i]);
+  }
+  free (vectors);
+}
+
+Vector *InitVector () {
+  Vector *vec;
+
+  ALLOCATE (vec, Vector, 1);
+  ALLOCATE (vec[0].elements, float, 1);
+  bzero (vec[0].name, 1024);
+  vec[0].Nelements = 0;
+  return (vec);
+}
+
+int IsVector (char *name) {
+ 
+  int i;
+
+  if (name == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+int IsVectorPtr (Vector *vec) {
+ 
+  int i;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vectors[i] != vec); i++);
+  if (i == Nvectors) return (FALSE);
+  return (TRUE);
+}
+
+Vector *SelectVector (char *name, int mode, int verbose) {
+
+  int i;
+
+  if (name == NULL) goto error;
+  if (ISNUM(name[0])) goto error;
+  if (IsBuffer(name)) goto error;
+
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  /* is a new vector */
+  if (i == Nvectors) { 
+    if (mode == OLDVECTOR) goto error;
+    pthread_mutex_lock (&mutex);
+    Nvectors ++;
+    REALLOCATE (vectors, Vector *, Nvectors);
+    vectors[i] = InitVector ();
+    strcpy (vectors[i][0].name, name);
+    pthread_mutex_unlock (&mutex);
+    return (vectors[i]);
+  } 
+  /* is an old vector */
+  if (mode == NEWVECTOR) goto error;
+  return (vectors[i]);
+
+ error:
+  if (verbose) gprint (GP_ERR, "invalid vector %s\n", name);
+  return (NULL);
+}
+  
+/* delete by pointer */
+int DeleteVector (Vector *vec) {
+
+  int i, j;
+
+  if (vec == NULL) return (FALSE);
+
+  for (i = 0; (i < Nvectors) && (vec != vectors[i]); i++);
+  if (i == Nvectors) return (FALSE);
+
+  free (vectors[i][0].elements);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+  
+/* delete by name */
+int DeleteNamedVector (char *name) {
+
+  int i, j;
+
+  if (name == NULL) return (FALSE);
+  for (i = 0; (i < Nvectors) && (strcmp(vectors[i][0].name, name)); i++);
+  if (i == Nvectors) return (FALSE);
+
+  free (vectors[i][0].elements);
+  free (vectors[i]);
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+/* copy data from in to out - new memory space */
+int CopyNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  CopyVector (Out, In);
+  return (TRUE);
+}
+int CopyVector (Vector *out, Vector *in) {
+  free (out[0].elements);
+  out[0].Nelements = in[0].Nelements;
+  ALLOCATE (out[0].elements, float, out[0].Nelements);
+  memcpy (out[0].elements, in[0].elements, out[0].Nelements*sizeof(float));
+  return (TRUE);
+}
+
+/* move data from in to out - use old memory space */
+int MoveNamedVector (char *out, char *in) {
+  Vector *In, *Out;
+  if ((In  = SelectVector (in,  OLDVECTOR, FALSE)) == NULL) return (FALSE);
+  if ((Out = SelectVector (out, ANYVECTOR, FALSE)) == NULL) return (FALSE);
+  MoveVector (Out, In);
+  return (TRUE);
+}
+int MoveVector (Vector *out, Vector *in) {
+  int i, j;
+
+  free (out[0].elements);
+  out[0].Nelements = in[0].Nelements;
+  out[0].elements =  in[0].elements;
+
+  /* delete vector entry from vector list, if it exists */
+  for (i = 0; (i < Nvectors) && (in != vectors[i]); i++);
+  if (i == Nvectors) {
+    free (in);
+    return (TRUE);
+  }
+
+  pthread_mutex_lock (&mutex);
+  for (j = i; j < Nvectors - 1; j++) vectors[j] = vectors[j + 1];
+  Nvectors --;
+  REALLOCATE (vectors, Vector *, MAX (Nvectors, 1));
+  free (in);
+  pthread_mutex_unlock (&mutex);
+  return (TRUE);
+}
+
+int ListVectors () {
+
+  int i;
+
+  if (Nvectors == 0) {
+    gprint (GP_ERR, "No defined vectors\n");
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "    N       name      size\n");
+  for (i = 0; i < Nvectors; i++) {
+    gprint (GP_LOG, "%5d %10s %10d\n",
+	     i, vectors[i][0].name, vectors[i][0].Nelements);
+  }
+  return (TRUE);
+}
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/check_stack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/check_stack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/check_stack.c	(revision 17422)
@@ -0,0 +1,84 @@
+# include "opihi.h"
+
+int check_stack (StackVar *stack, int Nstack, int validsize) {
+
+  int i, Nx, Ny, Nv, size;
+  char *c;
+
+  Nv = Nx = Ny = -1;
+
+  for (i = 0; i < Nstack; i++) {
+    if (stack[i].type == 'X') {
+
+      /** if this is a number, put it on the list of scalers and move on **/
+      stack[i].Float = strtod (stack[i].name, &c);
+      if (c == stack[i].name + strlen (stack[i].name)) {
+	stack[i].ptr   = &(stack[i].Float);
+	stack[i].type  = 'S';
+	continue;
+      } 
+
+      /** if this is a matrix, find the dimensions and check with existing values **/
+      if (IsBuffer (stack[i].name)) {
+	stack[i].buffer = SelectBuffer (stack[i].name, OLDBUFFER, TRUE);
+	stack[i].ptr    = (float *) stack[i].buffer[0].matrix.buffer;
+	stack[i].type   = 'M';
+	if (Nx == -1) {
+	  Nx = stack[i].buffer[0].matrix.Naxis[0];
+	  Ny = stack[i].buffer[0].matrix.Naxis[1];
+	} 
+	if ((Nx != stack[i].buffer[0].matrix.Naxis[0]) && (Ny != stack[i].buffer[0].matrix.Naxis[1])) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}	
+	if (Nv != -1) {
+	  if ((Nv != Nx) && (Nv != Ny)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /** if this is a vector, find the dimensions and check with existing values **/
+      if (IsVector (stack[i].name)) {
+	stack[i].vector = SelectVector (stack[i].name, OLDVECTOR, FALSE);
+	stack[i].ptr    = (float *) stack[i].vector[0].elements;
+	stack[i].type   = 'V';
+
+	if (Nv == -1) Nv = stack[i].vector[0].Nelements;
+	if (Nv != stack[i].vector[0].Nelements) {
+	  push_error ("dimensions don't match");
+	  return (-1);
+	}
+	if (Nx != -1) {
+	  if ((Nx != Nv) && (Ny != Nv)) {
+	    push_error ("dimensions don't match");
+	    return (-1);
+	  }
+	}	
+	continue;
+      }
+
+      /* this is not a scalar, vector, or matrix.  must be string */
+      stack[i].type  = 'W';
+    }
+  }
+
+  /* return object dimensions */
+  size = 0;
+  if (Nv != -1) size = 1;
+  if (Nx != -1) size = 2;
+  if (validsize == -1)   return (size);
+  if (validsize != size) return (-1);
+  return (size);
+}
+
+/* check stack identifies the data elements as scalar, vector, matrix, or word.
+   operators have already been identified.  
+   check stack returns the total stack dimensionality (0,1,2)
+   on error, check stack returns -1
+   possible errors:
+     - mismatch with requested dimensionality
+     - mismatch in data dimensions
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/command.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/command.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/command.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "opihi.h"
+# define DEBUG 0
+
+int command (char *line, char **outline, int VERBOSE) {
+
+  int i, status, argc;
+  char **argv, **targv;
+  Command *cmd;
+
+  /* force a space between ! and first word: !ls becomes ! ls */
+  if (line[0] == '!') {
+    REALLOCATE (line, char, strlen(line) + 5);
+    memmove (&line[2], &line[1], strlen(&line[1]) + 1);
+    line[1] = ' ';
+  }
+
+  /* expand anything of the form $fred or $fred$sam, etc */ 
+  line = expand_vars (line);     /* line is freed here, new one allocated */
+  /* expand anything of the form fred[N] */ 
+  line = expand_vectors (line);  /* line is freed here, new one allocated */
+  /* solve math expresions, assign variable, if needed */
+  line = parse (line);        /* line is freed here, new one allocated */
+  /* any entry in line of the form {foo} returns value or tmp vector / buffer */
+
+  /* we may have reallocated line, return new pointer */
+  *outline = line;
+  
+  # if (DEBUG) 
+  fprintf (stderr, "line: %s\n", line);
+  # endif
+
+  argv = parse_commands (line, &argc);
+  if (argc == 0) return (TRUE);  /* empty command or assignment */
+
+  /* save the original values of argv since command may modify the array */
+  ALLOCATE (targv, char *, argc);
+  for (i = 0; i < argc; i++) targv[i] = argv[i];
+
+  cmd = MatchCommand (argv[0], VERBOSE, FALSE);
+  if (cmd == NULL) {
+    status = -1;
+  } else {
+    free (argv[0]);
+    argv[0] = strcreate (cmd[0].name);
+    targv[0] = argv[0];
+    status = (*cmd[0].func) (argc, argv);
+  }
+  for (i = 0; i < argc; i++) free (targv[i]);
+  free (targv);
+  free (argv);
+
+  if (!status) {
+    char *msg;
+    msg = get_variable_ptr ("ERRORMSG");
+    if (msg != (char *) NULL) gprint (GP_ERR, "%s\n", msg);
+  }
+
+  set_int_variable ("STATUS", status);
+
+  # if (DEBUG) 
+  gprint (GP_ERR, "command: %s, status: %d\n", line, status);
+  # endif
+
+  return (status);
+}
+
+/* parse the input line, search for the corresponding command, and execute it
+   if no match is found, return -1; this is used above to distinguish between
+   a command error and an unknown command.  if VERBOSE is true, unknown commands
+   result in an error message.  The input line is freed and the resulting parsed
+   line is returned on 'outline'
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/convert_to_RPN.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 17422)
@@ -0,0 +1,212 @@
+# include "opihi.h"
+# define DUMPSTACK 0
+
+StackVar *convert_to_RPN (int argc, char **argv, int *nstack) {
+  
+  int type, Nx, Ny;
+  int i, j, Nstack, Nop_stack, NSTACK;
+  StackVar *stack, *op_stack;
+
+  /* max total stack size is argc, though should be less, this is safe */
+  NSTACK = argc + 5;
+  ALLOCATE (stack, StackVar, NSTACK);
+  ALLOCATE (op_stack, StackVar, NSTACK);
+  for (i = 0; i < NSTACK; i++) {
+    init_stack (&stack[i]);
+    init_stack (&op_stack[i]);
+  }
+  
+  Nx = Ny = Nstack = Nop_stack = 0;
+  for (i = 0; i < argc; i++) {
+    
+    /* decide on priority of object */
+    type = 0;
+    /* unary operations */
+    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
+
+    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+
+    /* binary operations */
+    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+	
+    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+
+  gotit:
+    /* choose how to deal with object */
+    switch (type) {
+      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+	/* pop previous, higher operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 9: /* unary OPs */
+      case 7: /* binary OPs */
+      case 6:
+      case 5: 
+      case 4: 
+      case 3: 
+	/* pop previous, higher or equal operators from OP stack to stack */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	Nop_stack ++;
+	break;
+      case 2:  
+	/* push operator on OP stack */
+	assign_stack (&op_stack[Nop_stack], argv[i], type);
+	// fprintf (stderr, "push %s\n", op_stack[Nop_stack].name);
+	Nop_stack ++;
+	break;
+      case 1: 
+	/* pop rest of operators from OP stack to stack, looking for '(' */
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != 2)) {
+	  push_error ("syntax error: mismatched parenthesis");
+	  Nstack = 0;
+	  goto cleanup;
+	}
+	// delete the '(' from op_stack:
+	// fprintf (stderr, "pop %s\n", op_stack[j].name);
+	clear_stack (&op_stack[j]);
+	Nop_stack --;
+	break;
+      case 0:
+	/* place the value (number or vector/matrix name) on stack */
+	/* value of 'X' is used as sentinel until we sort out values */
+	assign_stack (&stack[Nstack], argv[i], 'X');
+	Nstack ++;
+	break;
+    }
+  }
+
+  /* dump remaining operators on stack, checking for ')' */
+  for (j = Nop_stack - 1; j >= 0; j--) {
+    if (op_stack[j].type == 2) {
+      push_error ("syntax error: mismatched parenthesis");
+      Nstack = 0;
+      goto cleanup;
+    }
+    move_stack (&stack[Nstack],  &op_stack[j]);
+    Nstack ++;
+  }
+
+cleanup: 
+  /*** free up unused stack space ***/
+
+  // If we parsed everything above, there should not be any un-freed op_stacks at this
+  // point.  However, if we had a syntax error, we will have op_stack entries left behind.
+  delete_stack (op_stack, NSTACK);
+  free (op_stack);
+
+  // XXX there should not be any data on higher stack entries
+  // clean_stack (&stack[Nstack], NSTACK - Nstack);
+  REALLOCATE (stack, StackVar, MAX (Nstack, 1));
+  *nstack = Nstack;
+
+  for (i = 0; i < argc; i++) {
+    free (argv[i]);
+  }
+  free (argv);
+
+# if (DUMPSTACK)
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%s ", stack[i].name);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+  for (i = 0; i < Nstack; i++) {
+    gprint (GP_ERR, "%d ", stack[i].type);
+  }
+  if (Nstack > 0) gprint (GP_ERR, "\n");
+# endif
+
+  return (stack);
+
+}
+
+/* here are the rules for parsing a math AOL line to RPN:
+
+1) if object is a number, push on stack
+2) if object is a third order operand (exp, sin, cos), push on op stack
+3) if object is an open paren, push on op stack,
+4) if object is a second order operand, push on stack
+5) if object is a first order operand, pop all second order operands from stack 
+until paren, push on stack
+6) if object is an end paren, pop all objects from stack until paren, 
+pop next stack, if third order op
+7) if end of line, pop all remaining objects, second order first, etc.
+   
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/dvomath.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/dvomath.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/dvomath.c	(revision 17422)
@@ -0,0 +1,103 @@
+# include "opihi.h"
+
+/* return value on success is temp vector/buffer name or scalar value return value on error is NULL, all
+   internals freed.  errors are sent to error stack.  may be printed by calling function */
+
+/* XXX this function breaks the elements of the line into objects, numbers, and operators.  it
+ * then converts the list to an RPN expresssion.  it then evaluates the expression.  if an
+ * expression is not a valid math expression (string - string), it results in an error.  The
+ * problem is that elements of a valid expression may be invalid as expressions on their own
+ * (eg, string consisting of word-word).  this function probably should be somewhat smarter
+ * about breaking down items into strings and objects.  for the time being, the user must
+ * protect string items with double quotes for safety.
+ */
+   
+char *dvomath (int argc, char **argv, int *size, int validsize) {
+  
+  int  i, Nstack, Ncstack;
+  char   **cstack, *outname;
+  StackVar *stack;
+  Buffer *buf;
+  Vector *vec;
+
+  buf = NULL;
+  vec = NULL;
+  ALLOCATE (outname, char, 256);
+
+  /* take char array with expression, convert to important elements */
+  cstack = isolate_elements (argc, argv, &Ncstack); 
+
+  /* generate RPN stack from cstack arguments */
+  stack = convert_to_RPN (Ncstack, cstack, &Nstack);
+  if (Nstack < 1) goto error;
+
+  /* distinguish scalar, vector, matrix, check dimensions */
+  *size = check_stack (stack, Nstack, validsize);
+  if (*size < 0) goto error;
+
+  switch (*size) {
+    case 0:
+      break;
+    case 1:  /* allocate temp vector */
+      vec = NULL;
+      for (i = 0; (i < 1000) && (vec == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	vec = SelectVector (outname, NEWVECTOR, FALSE);
+      }
+      if (vec == NULL) { 
+	push_error ("too many tmp vectors");
+	goto error;
+      }
+      break;
+    case 2:  /* allocate temp buffer */
+      buf = NULL;
+      for (i = 0; (i < 1000) && (buf == NULL); i++) {
+	sprintf (outname, "tmp%03d", i);
+	buf = SelectBuffer (outname, NEWBUFFER, FALSE);
+      }
+      if (buf == NULL) {
+	push_error ("too many tmp buffers");
+	goto error;
+      }
+      break;
+    default:
+      goto error;
+  }
+
+  /* evaluate operations, free stack on error */
+  Ncstack = Nstack;
+  if (!evaluate_stack (stack, &Nstack)) {
+    if (*size == 1) DeleteVector (vec);
+    if (*size == 2) DeleteBuffer (buf);
+    goto error;
+  }
+
+  switch (*size) {
+    case 0:
+      if (Ncstack == 1) {
+	  /* use exact input work */
+	  sprintf (outname, "%s", stack[0].name);
+      } else {
+	  sprintf (outname, "%.12g", stack[0].Float);
+      }
+      break;
+
+    case 1:
+      MoveVector (vec, stack[0].vector);
+      break;
+  
+    case 2:
+      MoveBuffer (buf, stack[0].buffer);
+      break;
+  }
+
+  clean_stack (stack, Nstack);
+  free (stack);
+  return (outname);
+
+error:  
+  clean_stack (stack, Nstack);
+  free (stack);
+  free (outname);
+  return (NULL);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/errors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/errors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/errors.c	(revision 17422)
@@ -0,0 +1,22 @@
+# include "opihi.h"
+
+static char errorline[1024];
+
+int init_error () {
+
+  bzero (errorline, 1024);
+  return (TRUE);
+}
+
+int push_error (char *line) {
+
+  bzero (errorline, 1024);
+  strncpy (errorline, line, 1023);
+  return (TRUE);
+}
+
+int print_error () {
+
+  gprint (GP_ERR, "%s\n", errorline);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/evaluate_stack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 17422)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+# define VERBOSE 0
+
+# define TWO_OP(A,B,FUNC) \
+  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 
+
+# define ONE_OP(A,FUNC) \
+  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
+    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 
+
+int evaluate_stack (StackVar *stack, int *Nstack) {
+  
+  int i, j, Nvar, Nout, status;
+  char line[512];
+  StackVar tmp_stack;
+  Nout = Nvar = 0;
+
+  status = TRUE;
+  init_stack (&tmp_stack);
+
+  if (*Nstack == 1) {
+    if (stack[0].type == 'S') {
+      clear_stack (&tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'V') {
+      /* need to make a copy so we set output value? */
+      V_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    if (stack[0].type == 'M') {
+      /* need to make a copy so we set output value? */
+      M_unary (&tmp_stack, &stack[0], "=");
+      move_stack (&stack[0], &tmp_stack);
+      return (TRUE);
+    }
+    push_error ("syntax error: not a math expression");
+    clear_stack (&tmp_stack);
+    return (FALSE);
+  }      
+    
+  for (i = 0; i < *Nstack; i++) {
+
+    if (VERBOSE) {
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%s ", stack[j].name);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+      gprint (GP_ERR, "%d: ", i);
+      for (j = 0; j < *Nstack; j++) {
+	gprint (GP_ERR, "%d ", stack[j].type);
+      }
+      if (*Nstack > 0) gprint (GP_ERR, "\n");
+    }
+
+    /***** binary operators *****/
+    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
+
+      if (i < 2) {  /* need two variables to operate on */
+	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      status = FALSE;
+      TWO_OP ("M","M",MM_binary);
+      TWO_OP ("M","V",MV_binary);
+      TWO_OP ("M","S",MS_binary);
+      TWO_OP ("V","M",VM_binary);
+      TWO_OP ("V","V",VV_binary);
+      TWO_OP ("V","S",VS_binary);
+      TWO_OP ("S","M",SM_binary);
+      TWO_OP ("S","V",SV_binary);
+      TWO_OP ("S","S",SS_binary);      
+      TWO_OP ("W","W",WW_binary);      
+      TWO_OP ("W","S",WW_binary);      
+      TWO_OP ("S","W",WW_binary);      
+      
+      if (!status) {
+	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
+	push_error (line);
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+      move_stack (&stack[i-2], &tmp_stack);
+      delete_stack (&stack[i-1], 2);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 2], &stack[j]);
+      }
+      *Nstack -= 2;
+      i -= 2;
+      init_stack (&tmp_stack);
+      continue;
+    }
+
+    /***** unary operators **/
+    if (stack[i].type == 9) {
+
+      if (i < 1) {  /* need one variable to operate on */
+	push_error ("syntax error: unary operator with no operand");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      ONE_OP ("M", M_unary);
+      ONE_OP ("V", V_unary);
+      ONE_OP ("S", S_unary);
+
+      /* there are no valid unary string operators */
+      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
+	push_error ("syntax error: no valid string unary ops");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+      }
+
+      move_stack (&stack[i-1], &tmp_stack);
+      delete_stack (&stack[i], 1);
+      for (j = i + 1; j < *Nstack; j++) {
+	move_stack (&stack[j - 1], &stack[j]);
+      }
+      init_stack (&tmp_stack);
+      *Nstack -= 1;
+      i -= 1;
+      continue;
+    } 
+  }
+  clear_stack (&tmp_stack);
+
+  if (*Nstack > 1) {
+    push_error ("syntax error in evaluation");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+/* copy data to new stack variable */
+void copy_stack (StackVar *stack1, StackVar *stack2) {
+  stack1[0].name   = stack2[0].name  ;
+  stack1[0].type   = stack2[0].type  ;
+  stack1[0].ptr    = stack2[0].ptr   ;
+  stack1[0].buffer = stack2[0].buffer;
+  stack1[0].vector = stack2[0].vector;
+  stack1[0].Float  = stack2[0].Float ;
+  if (!strncasecmp (&stack1[0].type, "S", 1)) {
+    stack1[0].ptr    = &stack1[0].Float;
+  }
+}
+
+/* replace data with new stack variable */
+void move_stack (StackVar *stack1, StackVar *stack2) {
+  clear_stack (stack1);
+  copy_stack (stack1, stack2);
+  stack2[0].name = NULL;
+}
+
+/* delete name and data */
+void clean_stack (StackVar *stack, int Nstack) {
+
+  int i;
+
+  for (i = 0; i < Nstack; i++) {
+    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
+      free (stack[i].buffer[0].header.buffer);
+      free (stack[i].buffer[0].matrix.buffer);
+      free (stack[i].buffer);
+      stack[i].buffer = NULL;
+    }	
+    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
+      if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
+      free (stack[i].vector[0].elements);
+      free (stack[i].vector);
+      stack[i].vector = NULL;
+    }	
+    if (VERBOSE) gprint (GP_ERR, "free %s (name) (%d) (%lx)\n", stack[i].name, i, (long) stack[i].name);
+    clear_stack (&stack[i]);
+  }
+
+}
+
+/* delete name only, not data */
+void delete_stack (StackVar *stack, int Nstack) {
+  int i;
+  for (i = 0; i < Nstack; i++) {
+    clear_stack (&stack[i]);
+  }
+}
+
+void init_stack (StackVar *stack) {
+  stack[0].buffer = NULL;
+  stack[0].vector = NULL;
+  stack[0].name = NULL;
+}
+
+void assign_stack (StackVar *stack, char *name, int type) {
+  stack->name = strcreate (name);
+  stack->type = type;
+}
+
+void clear_stack (StackVar *stack) {
+  if (stack->name == NULL) return;
+  // fprintf (stderr, "free %s\n", stack->name);
+  free (stack->name);
+  stack->name = NULL;
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/exec_loop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/exec_loop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/exec_loop.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "opihi.h"
+
+int exec_loop (Macro *loop) {
+
+  void *Signal;
+  int j, status, ThisList;
+  char *line;
+  
+  /* increase the shell level (Nlists) by one */
+  ThisList = increase_list_depth();
+  if (ThisList == 0) abort();
+
+  /* copy the macro to the current list */
+  for (j = 0; j < loop[0].Nlines; j++) {
+    add_listentry (ThisList, loop[0].line[j]);
+  }
+
+  /* set up interrupts */
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+
+  /* process the list */
+  loop_next = loop_break = loop_last = FALSE;
+  status = TRUE;
+
+  while (!interrupt) {
+    line = get_next_listentry (ThisList);
+    if (line == NULL) break;
+    status = multicommand (line);
+    free (line);
+    if (auto_break && !status) loop_break = TRUE;
+    if (loop_break || loop_last || loop_next) break;
+  }
+  signal (SIGINT, Signal);
+
+  /* free remaining lines on the list, free the list, decrement the shell level */
+  /*** can we free a list which is not the bottom lists? */
+  decrease_list_depth();
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/** note that the list number runs from 1 - Nlists+1 **/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vars.c	(revision 17422)
@@ -0,0 +1,145 @@
+# include "opihi.h"
+
+char *expand_vars (char *line) {
+
+  char *L, *N, *V0, *V1, *Val, *newline, *c, found;
+  int done, MacroDepth, NLINE, Noff;
+
+  if (line == NULL) return (NULL);
+  MacroDepth = GetMacroDepth ();
+
+  found = FALSE;
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);  /* WARNING: this limits the length of the input line */
+
+  V0 = thiscomm (line);
+  if (V0 && !strncmp ("while", V0, strlen(V0))) {
+      // special case: don't expand variables in while () statement
+      strcpy (newline, line);
+      free (line);
+      return (newline);
+  }
+  free (V0);
+
+  /* look for form $a$b..$n and expand only the last ones */
+  for (L = line, N = newline; *L != 0; N++, L++) {  /* loop until end of line */
+    for (done = FALSE; !done;) {
+      if (*L == 0) done = TRUE;
+      if (*L == '$') {
+	V1 = aftervar(L);
+	if ((V1 != NULL) && (*V1 != '$')) done = TRUE;
+	if (V1 == NULL) done = TRUE;
+      }
+      if (!done) { 
+	*N = *L;
+	 L++; 
+	 N++;
+	 if (N - newline >= NLINE - 5) {
+	   Noff = N - newline;
+	   NLINE += 128;
+	   REALLOCATE (newline, char, NLINE);
+	   N = newline + Noff;
+	 }
+      }
+    }
+
+    if (*L == 0) break;
+
+    V1 = aftervar (L);           /* V1 points to the first non-WHITESPACE after the variable */
+    V0 = thisvar (L);            /* V0 points to the name of the var */
+    /* note: V1 points to a fraction of L, it does not need to be freed */
+
+    /* no variable name */
+    if (V0 == NULL) {
+      *N = *L;
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+      continue;
+    }
+
+    /* variable assignment (skip these variables) */
+    if ((L == line) && (V1 != NULL)) {
+      if ((*V1 == '=') || !strcmp (V1, "++") || !strcmp (V1, "--")) {
+	*N = *L;
+	free (V0);
+	if (N - newline >= NLINE - 5) {
+	  Noff = N - newline;
+	  NLINE += 128;
+	  REALLOCATE (newline, char, NLINE);
+	  N = newline + Noff;
+	}
+	continue;
+      }
+    }
+
+    found = TRUE;
+    for (c = V0; isdigit(*c); c++); /* test if this is a macro argument variable (ie, $1.2) */
+    if (*c == 0) {  /* all digit var == macro parameter */
+      if (!MacroDepth) {
+	gprint (GP_ERR, "not in a macro\n");
+	goto error;
+      } else { /* if we are executing a macro, attach the list depth to the front, pass on down the line */
+	// XXX this is limiting!!!
+	ALLOCATE (c, char, 1024);
+	sprintf (c, "%d.%s", MacroDepth, V0);
+	free (V0);
+	V0 = c;
+      }
+    }
+
+    Val = get_variable_ptr (V0);
+    if (Val == NULL) {   /* var was not found! */
+      gprint (GP_ERR, "variable %s not found\n", V0);
+      goto error;
+    }
+    for (; *Val != 0; N++, Val++)  {
+      *N = *Val; /* place the value of the variable in the newline */
+      if (N - newline >= NLINE - 5) {
+	Noff = N - newline;
+	NLINE += 128;
+	REALLOCATE (newline, char, NLINE);
+	N = newline + Noff;
+      }
+    }
+    N--; /* we overshoot on last loop */
+
+    /* skip past the variable in the source line */
+    /* L currently points at $ */
+    L++;
+    if (*L == '?') L++;  /* skip past ? in $?name */
+
+    while (ISVAR(*L)) L++;
+    L--; /* we overshoot */
+
+    if (V0 != NULL) free (V0);
+  }
+
+  *N = 0;
+  free (line);
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  if (found) { /* try again for new variables */
+    newline = expand_vars (newline);
+  }
+  return (newline);
+
+error:
+  free (line);
+  free (V0);
+  free (newline);
+  return (NULL);
+}
+
+
+  /************************
+    we go through the line looking for the dollar signs ($) marking the 
+    variables.  We don't expand a variable if it is the first thing on the line 
+    AND it is followed by an equals sign.  
+    that is, don't expand if:
+    V0 = NULL ($ is last non-blank char on line -- no variable) or
+    V1[0] is = (assignment of variable value), AND L == line (ie, at beginning of line)
+    *************************/
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vectors.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vectors.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/expand_vectors.c	(revision 17422)
@@ -0,0 +1,176 @@
+# include "opihi.h"
+
+char *expand_vectors (char *line) {
+
+  char *newline, *tmpline, strValue[128], *val;
+  char *L, *N, *p, *q, *p2, *w;
+  int n, I, J, size, showLength, NLINE, Noff, isBuffer, inRange;
+  float *ptr;
+  double f1;
+  Vector *vec;
+  Buffer *buf;
+
+  if (line == NULL) return (NULL);
+
+  NLINE = MAX (128, strlen(line));
+  ALLOCATE (newline, char, NLINE);
+
+  /* look for form fred[stuff] */
+  /* skip first word (command) */
+  L = nextword (line);
+  if (L == NULL) {
+    free (newline);
+    return (line);
+  }
+
+  n = L - line;
+  strncpy (newline, line, n);
+  N = newline + n;
+  while (1) {
+    /* find square-bracket pair [..] */
+    p = strchr (L, '[');
+    q = strchr (L, ']');
+    if ((p == NULL) && (q != NULL)) goto dumpline;
+    if ((p != NULL) && (q == NULL)) goto dumpline;
+    if ((p == NULL) && (q == NULL)) goto dumpline;
+    if (p > q) goto dumpline; /* odd state: unmatched pair: ][ */
+
+    /* find vector subscript */
+    n = (int) (q - p - 1);
+    val = NULL;
+    showLength = FALSE;
+    if (n == 0) {
+      showLength = TRUE;
+    } else {
+      tmpline = strncreate (p+1, n);
+      val = dvomath (1, &tmpline, &size, 0);
+      free (tmpline);
+      if (val == NULL) goto dumpline; /* not a valid vector subscript */
+    }
+    I = 0; 
+    if (val != NULL) {
+      I = atoi (val);
+      free (val);
+    }      
+
+    /* if a second [..] immediately follows, we are a buffer, not a vector */
+    isBuffer = FALSE;
+    if (*(q + 1) == '[') {
+      p2 = strchr (q + 1, ']');
+      if (p2 != NULL) {
+	if (showLength) {
+	  gprint (GP_ERR, "unsupported : name[][..]\n"); 
+	  goto asVector;
+	}
+
+	isBuffer = TRUE;
+
+	/* find buffer second subscript */
+	n = (int) (p2 - q - 2);
+	val = NULL;
+	if (n == 0) {
+	  gprint (GP_ERR, "unsupported : name[..][]\n"); 
+	  isBuffer = FALSE;
+	  goto asVector;
+	} 
+	tmpline = strncreate (q+2, n);
+	val = dvomath (1, &tmpline, &size, 0);
+	free (tmpline);
+	if (val == NULL) {
+	  isBuffer = FALSE;
+	  goto asVector; /* not a valid vector subscript */
+	}
+	J = 0; 
+	if (val != NULL) {
+	  J = atoi (val);
+	  free (val);
+	}      
+      }
+      q = p2;
+    }
+
+  asVector:
+    /* find vector/buffer name */
+    for (w = p - 1; (w >= line) && !OHANA_WHITESPACE(*w) && (isalnum(*w) || (*w == ':') || (*w == '_')); w--);
+    w ++;
+    n = (int)(p - w);
+    tmpline = strncreate (w, n);
+
+    if (isBuffer) {
+      if ((buf = SelectBuffer (tmpline, OLDBUFFER, TRUE)) == NULL) goto dumpline;
+
+      /* find buffer element */
+      inRange = TRUE;
+      inRange &= (I <  +1*buf[0].header.Naxis[0]);
+      inRange &= (I >= -1*buf[0].header.Naxis[0]);
+      inRange &= (J <  +1*buf[0].header.Naxis[1]);
+      inRange &= (J >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "buffer subscript out of range\n"); 
+	goto escape;
+      }
+      if (I < 0) I += buf[0].header.Naxis[0];
+      if (J < 0) J += buf[0].header.Naxis[1];
+      ptr = (float *) buf[0].matrix.buffer;
+      f1 = ptr[I + J*buf[0].header.Naxis[0]];
+    } else {
+      if ((vec = SelectVector (tmpline, OLDVECTOR, TRUE)) == NULL) goto dumpline;
+
+      /* find vector element */
+      if (showLength) {
+	f1 = vec[0].Nelements;
+      } else {
+	if ((I >= vec[0].Nelements) || (I < -1*vec[0].Nelements)) {
+	  gprint (GP_ERR, "vector subscript out of range\n"); 
+	  goto escape;
+	}
+	if (I < 0) I += vec[0].Nelements;
+	f1 = vec[0].elements[I];
+      }
+    }
+
+    free (tmpline);
+    if ((int)f1 == f1) 
+      snprintf (strValue, 128, "%.0f", f1);
+    else
+      snprintf (strValue, 128, "%.9g", f1);
+
+    /* interpolate vector element into newline (being accumulated) */
+    size = (N - newline) + (w - L) + strlen(strValue);
+    if (size >= NLINE) {
+      Noff = N - newline;
+      NLINE += 128 + strlen(strValue) + (w - L);
+      REALLOCATE (newline, char, NLINE);
+      N = newline + Noff;
+    }
+
+    n = (int) (w - L);
+    strncpy (N, L, n);
+    N += n;
+    n = strlen (strValue);
+    strncpy (N, strValue, n);
+    N += n;
+    L = q + 1;
+  }
+
+dumpline:
+  size = (N - newline) + strlen(L);
+  if (size >= NLINE) {
+    Noff = N - newline;
+    NLINE += 128 + strlen(L);
+    REALLOCATE (newline, char, NLINE);
+    N = newline + Noff;
+  }
+
+  n = strlen (L);
+  strncpy (N, L, n);
+  N[n] = 0;
+  free (line);
+  return (newline);
+  
+escape:
+  free (line);
+  free (newline);
+  return (NULL);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/gprint.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/gprint.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/gprint.c	(revision 17422)
@@ -0,0 +1,340 @@
+# include "shell.h"
+
+/* we need to control the output destinations a bit carefully
+   in the pantasks_server mode.  The server needs to be able to 
+   send the output to a specific output device (eg, logging file)
+   of to save it within an internal buffer */
+
+/* further notes:
+   gprintf (int destination, char *format, ...);
+
+   the destination may be GP_LOG or GP_ERR.  by default, these go to stdout and
+   stderr.  either one may be redirected to another file or to a buffer.  as a
+   stand-along program, the outfile command redirects the GP_LOG output to an
+   alternate output file.  in the server mode, we redirect LOG to a standard log
+   file and ERR to a standard error file for server messages.  Commands executed
+   by the client have both streams returned to the client in turn, and are in
+   turn sent to stderr or the current stdout destination.
+
+   each thread has an independently set output destination.
+
+   option 1: NULL for invalid element:
+
+     a stream is either set to a FILE or an IOBuffer.  when it is set to a FILE,
+     the IOBuffer is freed and set to NULL.  when it is an IOBuffer, the file
+     pointer is closed and set to NULL.  setting a new FILE results in the 
+     IObuffer being freed and an already opened FILE to be flushed and closed.
+
+
+  gprint & mutex:
+
+     gprintInit : init_stream_mutex
+     gprintGetStream : init_stream_mutex
+     gprintCloseFile : init_stream_mutex
+
+     gprintSetFileAllThreads : set_file_mutex
+     (gprintSetFile -> gprintCloseFile)
+
+     gprintSetFileThisThread : set_file_mutex
+     (gprintGetStream)
+     (gprintSetFile -> gprintCloseFile)
+
+     we are safe from dead locks: init_stream_mutex can be wrapped by set_file_mutex, but
+     not vice-versa
+
+*/
+
+static gpStream **streams = NULL;
+static int Nstreams = 0;
+
+static pthread_mutex_t init_stream_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintInit () {
+
+  int N;
+  pthread_t id;
+
+  /* need to use a mutex to prevent two threads from initing simultaneously */
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // streams is an array of pointers so we can add more streams without changing pointers 
+  if (streams == NULL) {
+    Nstreams = 2;
+    ALLOCATE (streams, gpStream *, Nstreams);
+  } else {
+    Nstreams += 2;
+    REALLOCATE (streams, gpStream *, Nstreams);
+  }
+
+  /* create two output streams for this thread: LOG and ERR */
+  id = pthread_self();
+
+  N = Nstreams - 2;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_LOG;
+  streams[N][0].file = stdout;
+  streams[N][0].name = strcreate ("stdout");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  N = Nstreams - 1;
+  ALLOCATE (streams[N], gpStream, 1);
+  streams[N][0].dest = GP_ERR;
+  streams[N][0].file = stderr;
+  streams[N][0].name = strcreate ("stderr");
+
+  ALLOCATE (streams[N][0].buffer, IOBuffer, 1);
+  InitIOBuffer (streams[N][0].buffer, 64);
+  streams[N][0].mode = GP_FILE;
+  streams[N][0].thread = id;
+
+  pthread_mutex_unlock (&init_stream_mutex);
+}
+
+gpStream *gprintGetStream (gpDest dest) {
+
+  int i;
+  pthread_t id;
+  gpStream *stream;
+
+  // need to wait for initialization to be finished before getting stream or the array
+  // (streams[i]) may move
+  pthread_mutex_lock (&init_stream_mutex);
+
+  id = pthread_self();
+
+  /* find the existing output stream which matches */
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    if (!pthread_equal (streams[i][0].thread, id)) continue;
+    stream = streams[i];
+    pthread_mutex_unlock (&init_stream_mutex);
+    return stream;
+  }
+  pthread_mutex_unlock (&init_stream_mutex);
+  fprintf (stderr, "programming error: gprintInit not called for thread\n");
+  abort ();
+}
+
+// close if necessary, set to file (may be NULL)
+void gprintCloseFile (gpStream *stream, FILE *file) {
+
+  int i, Nmatch;
+
+  // do not close the file if the new file is the same
+  if (stream[0].file == file) return;
+
+  // do not close the file if the old one is NULL
+  if (stream[0].file == NULL) {
+    stream[0].file = file;    
+    return;
+  }
+
+  // check the special cases 
+  if (stream[0].file == stdout) {
+    stream[0].file = file;
+    return;
+  }
+  if (stream[0].file == stderr) {
+    stream[0].file = file;
+    return;
+  }
+
+  // we cannot do the operation below while another thread is initing
+  pthread_mutex_lock (&init_stream_mutex);
+
+  // must we close the existing file? if still being used, then no
+  Nmatch = 0;
+  for (i = 0; i < Nstreams; i++) {
+    if (stream == streams[i]) continue;
+    if (streams[i][0].file == stream[0].file) Nmatch ++;
+  }
+  pthread_mutex_unlock (&init_stream_mutex);
+
+  if (Nmatch == 0) {
+    fflush (stream[0].file);
+    fclose (stream[0].file);
+  }
+
+  stream[0].file = file;
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetBuffer (gpDest dest) {
+
+  gpStream *stream;
+
+  stream = gprintGetStream (dest);
+
+  // close the existing file (if needed), set to NULL
+  gprintCloseFile (stream, NULL);
+
+  assert (stream[0].buffer);
+  FlushIOBuffer (stream[0].buffer);
+  
+  stream[0].mode = GP_BUFF;
+}
+
+// this thread only operates on its own stream
+IOBuffer *gprintGetBuffer (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].buffer);
+}
+
+static pthread_mutex_t set_file_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void gprintSetFileAllThreads (gpDest dest, char *filename) {
+
+  int i;
+
+  // be sure we are not colliding with gprintSetFileThisThread
+  pthread_mutex_lock (&set_file_mutex);
+
+  for (i = 0; i < Nstreams; i++) {
+    if (streams[i][0].dest != dest) continue;
+    gprintSetFile (streams[i], dest, filename);
+  }
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+// this thread only operates on its own stream
+void gprintSetFileThisThread (gpDest dest, char *filename) {
+
+  gpStream *stream;
+
+  // be sure we are not colliding with gprintSetFileAllThreads
+  pthread_mutex_lock (&set_file_mutex);
+
+  stream = gprintGetStream (dest);
+  gprintSetFile (stream, dest, filename);
+
+  pthread_mutex_unlock (&set_file_mutex);
+  return;
+}
+
+void gprintSetFile (gpStream *stream, gpDest dest, char *filename) {
+
+  FILE *file;
+
+  assert (stream[0].buffer);
+
+  /* if we have an open buffer, free it and null it */
+  if (stream[0].buffer[0].Nbuffer) {
+    // XXX we drop what was on the buffer, send it to the old or the new file?
+    FlushIOBuffer (stream[0].buffer);
+  }
+
+  stream[0].mode = GP_FILE;
+
+  // if NULL, reuse exising stream name
+  if (filename != NULL) {
+    free (stream[0].name);
+    stream[0].name = strcreate (filename);
+  }
+
+  // we allow the user to set stdout to ERR and stderr to LOG if they want
+  if (!strcmp (stream[0].name, "stdout")) {
+    stream[0].file = stdout;
+    return;
+  }
+  if (!strcmp (stream[0].name, "stderr")) {
+    stream[0].file = stderr;
+    return;
+  }
+  
+  // open the file. only close old pointer if no one else is using it
+  file = fopen (stream[0].name, "a");
+  if (file == NULL) {
+    fprintf (stderr, "cannot open file %s\n", stream[0].name);
+    free (stream[0].name);
+    stream[0].file = (dest == GP_LOG) ? stdout : stderr;
+    stream[0].name = (dest == GP_LOG) ? strcreate ("stdout") : strcreate("stderr");
+    return;
+  }
+
+  // close the existing file (if needed), set to new file
+  gprintCloseFile (stream, file);
+  return;
+}
+
+// this thread only operates on its own stream
+FILE *gprintGetFile (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].file);
+}
+
+// this thread only operates on its own stream
+char *gprintGetName (gpDest dest) {
+
+  gpStream *stream;
+  stream = gprintGetStream (dest);
+  return (stream[0].name);
+}
+
+int gprint (gpDest dest, char *format, ...) {
+
+  int status;
+  gpStream *stream;
+  va_list argp;  
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  va_start (argp, format);
+
+  if (stream[0].mode == GP_FILE) {
+    status = vfprintf (stream[0].file, format, argp);
+    if (status < 0) {
+      abort();
+    }
+  } else {
+    vPrintIOBuffer (stream[0].buffer, format, argp);
+  }
+  va_end (argp);
+  return (TRUE);
+}
+
+int gwrite (char *buffer, int size, int N, gpDest dest) {
+
+  int Nbyte;
+  IOBuffer *outbuff;
+  gpStream *stream;
+
+  // this thread only writes to its own stream
+  stream = gprintGetStream (dest);
+
+  if (stream[0].mode == GP_FILE) {
+    fwrite (buffer, size, N, stream[0].file);
+  } else {
+    // XXX can we not use exising IOBuffer APIs here?
+    outbuff = stream[0].buffer;
+    Nbyte = size * N;
+    if (outbuff[0].Nbuffer + Nbyte >= outbuff[0].Nalloc) {
+      outbuff[0].Nalloc = outbuff[0].Nbuffer + Nbyte + 64;
+      REALLOCATE (outbuff[0].buffer, char, outbuff[0].Nalloc);
+    }
+    memcpy (&outbuff[0].buffer[outbuff[0].Nbuffer], buffer, Nbyte);
+    outbuff[0].Nbuffer += Nbyte;
+  }
+  return (TRUE);
+}
+
+/* I'm going to need to have different output targets for different threads
+   we can do this with these functions:
+
+   pthread_t pthread_self(void);
+   int pthread_equal(pthread_t thread1, pthread_t thread2);
+   // returns TRUE if equal, FALSE if not
+   
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/interrupt.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/interrupt.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/interrupt.c	(revision 17422)
@@ -0,0 +1,53 @@
+# include "opihi.h"
+
+static int Nint = 0;
+
+/* return FALSE if we are called many times in a row 
+   without a valid answer or any answer */
+
+void handle_interrupt (int input) {
+  
+  char string[64];
+  int Nask;
+
+  signal (SIGINT, SIG_IGN);
+
+  Nask = 0;
+  Nint ++;
+
+  // 3 ctrl-c in a row will interrupt regardless
+  if (Nint > 3) { 
+    interrupt = TRUE;
+    return;
+  }
+  if (Nint > 100) { 
+    exit (3);
+  }
+  
+  while (1) {
+    gprint (GP_ERR, "operation halted, continue? (y/n) ");
+    fscanf (stdin, "%s", string);
+    
+    if ((string[0] == 'y') || (string[0] == 'Y')) {
+      interrupt = FALSE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+
+    if ((string[0] == 'n') || (string[0] == 'N')) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+    Nask ++;
+    if (Nask > 3) {
+      interrupt = TRUE;
+      signal (SIGINT, handle_interrupt);
+      Nint = 0;
+      return;
+    }
+  }  
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/isolate_elements.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/isolate_elements.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/isolate_elements.c	(revision 17422)
@@ -0,0 +1,210 @@
+# include "opihi.h"
+
+/* local private functions */
+char **InsertValue (char **myOutput, int *Nout, int *Nchar, int *NCHAR, char c);
+char **EndOfString (char **myOutput, int *Nout, int *Nchar, int *NOUT, int *NCHAR);
+int IsAnOp (char *c);
+int IsTwoOp (char *c);
+
+char **isolate_elements (int Nin, char **in, int *nout) {
+  
+  /* local private static variables */
+  int NCHAR, Nchar, Nout, NOUT;
+  char **myOutput;
+
+  int i, j, minus, negate, plus, posate, OpStat, SciNotation;
+
+  NOUT = Nin;
+  Nchar = Nout = 0;
+  NCHAR = 256;
+  ALLOCATE (myOutput, char *, NOUT);
+  ALLOCATE (myOutput[Nout], char, NCHAR);
+
+  for (i = 0; i < Nin; i++) {
+    for (j = 0; j < strlen(in[i]); j++) {
+      SciNotation = FALSE;
+      /* identify 'negate' or 'minus' ops */
+      negate = minus = FALSE;
+      if (in[i][j] == '-') { 
+	minus = TRUE;  
+	/* if - is first thing on line, must be a negator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (myOutput[Nout]);
+	  if (myOutput[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (myOutput[Nout-1]);
+	  if (myOutput[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if - follows an operator, must be negator */
+	if (OpStat) {
+	  minus = FALSE;
+	  negate = TRUE;
+	  goto skip1;
+	}
+	/* if - follows 'e' is part of 1e-5 */
+	if (j == 0) goto skip1;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  negate = minus = FALSE;
+	}
+      }
+    skip1:
+      /* idenfity 'posate' or 'plus' ops */
+      posate = plus = FALSE;
+      if (in[i][j] == '+') { 
+	plus = TRUE;  
+	/* if + is first thing on line, must be a posator */
+	if ((Nout == 0) && (Nchar == 0)) {  
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* check previous entry on line */
+	if (Nchar) {
+	  OpStat = IsAnOp (myOutput[Nout]);
+	  if (myOutput[Nout][0] == ')') OpStat = FALSE;
+	} else {
+	  OpStat = IsAnOp (myOutput[Nout-1]);
+	  if (myOutput[Nout-1][0] == ')') OpStat = FALSE;
+	}
+	/* if + follows an operator, must be posator */
+	if (OpStat) {
+	  plus = FALSE;
+	  posate = TRUE;
+	  goto skip2;
+	}
+	/* if + follows 'e' is part of 1e+5 */
+	if (j == 0) goto skip2;
+	if ((in[i][j-1] == 'e') || (in[i][j-1] == 'E')) {
+	  SciNotation = TRUE;
+	  posate = plus = FALSE;
+	}
+      }
+    skip2:
+      /* operators */
+      if (negate || minus || posate || plus || (IsAnOp (&in[i][j]) && !SciNotation)) {
+	if (posate) continue;
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	/* copy operator to myOutput[Nout] */
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	if (negate) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, '-');
+	}
+
+	if (IsTwoOp (&in[i][j])) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j+1]);
+	  j++;
+	} 
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	continue;
+      }
+      /* quoted string */
+      if (in[i][j] == '"') {
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	j++;
+	while ((j < strlen(in[i])) && (in[i][j] != '"')) {
+	  myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	  j++;
+	}
+	if (in[i][j] != '"') continue;
+	/* 
+	  gprint (GP_ERR, "mismatched quotes\n");
+	  return (FALSE);
+	}
+	*/
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+	continue;
+      }
+      /* not an operator, not a quoted string */
+      if (!OHANA_WHITESPACE (in[i][j])) {
+	myOutput = InsertValue (myOutput, &Nout, &Nchar, &NCHAR, in[i][j]);
+      } else {
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+      }
+    }
+	myOutput = EndOfString (myOutput, &Nout, &Nchar, &NOUT, &NCHAR);
+  }
+
+  /* one extra entry is allocated, free here */
+  free (myOutput[Nout]);
+  *nout = Nout;
+  return (myOutput);
+
+}
+
+char **InsertValue (char **myOutput, int *Nout, int *Nchar, int *NCHAR, char c) {
+  myOutput[*Nout][*Nchar] = c;
+  (*Nchar) ++;
+  if ((*Nchar) >= (*NCHAR) - 2) {
+    (*NCHAR) += 256;
+    REALLOCATE (myOutput[*Nout], char, *NCHAR);
+  }
+  myOutput[*Nout][*Nchar] = 0;
+  return (myOutput);
+}
+
+char **EndOfString (char **myOutput, int *Nout, int *Nchar, int *NOUT, int *NCHAR) {
+  if ((*Nchar) > 0) {
+    myOutput[*Nout][*Nchar] = 0;
+    (*Nout) ++;
+    (*Nchar) = 0;
+    
+    if ((*Nout) >= (*NOUT) - 1) {
+      (*NOUT) += 10; 
+      REALLOCATE (myOutput, char *, (*NOUT)); 
+    } 
+    (*NCHAR) = 256;
+    ALLOCATE (myOutput[*Nout], char, (*NCHAR)); 
+  }
+  return (myOutput);
+}
+
+int IsAnOp (char *c) {
+
+  if (!strncmp (c, "*",  1)) return (TRUE);
+  if (!strncmp (c, "/",  1)) return (TRUE);
+  if (!strncmp (c, "+",  1)) return (TRUE);
+  if (!strncmp (c, "-",  1)) return (TRUE);
+  if (!strncmp (c, "^",  1)) return (TRUE);
+  if (!strncmp (c, "@",  1)) return (TRUE);
+  if (!strncmp (c, "(",  1)) return (TRUE);
+  if (!strncmp (c, ")",  1)) return (TRUE);
+  if (!strncmp (c, "&",  1)) return (TRUE);
+  if (!strncmp (c, "|",  1)) return (TRUE);
+  if (!strncmp (c, ">",  1)) return (TRUE);
+  if (!strncmp (c, "<",  1)) return (TRUE);
+  if (!strncmp (c, "<",  1)) return (TRUE);
+  if (!strncmp (c, "<<", 2)) return (TRUE);
+  if (!strncmp (c, ">>", 2)) return (TRUE);
+  if (!strncmp (c, "&&", 2)) return (TRUE);
+  if (!strncmp (c, "||", 2)) return (TRUE);
+  if (!strncmp (c, "==", 2)) return (TRUE);
+  if (!strncmp (c, "!=", 2)) return (TRUE);
+  if (!strncmp (c, "<=", 2)) return (TRUE);
+  if (!strncmp (c, ">=", 2)) return (TRUE);
+
+  return (FALSE);
+
+}
+
+int IsTwoOp (char *c) {
+
+  if (!strncmp (c, "<<", 2)) return (TRUE);
+  if (!strncmp (c, ">>", 2)) return (TRUE);
+  if (!strncmp (c, "&&", 2)) return (TRUE);
+  if (!strncmp (c, "||", 2)) return (TRUE);
+  if (!strncmp (c, "==", 2)) return (TRUE);
+  if (!strncmp (c, "!=", 2)) return (TRUE);
+  if (!strncmp (c, "<=", 2)) return (TRUE);
+  if (!strncmp (c, ">=", 2)) return (TRUE);
+
+  return (FALSE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_create.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_create.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_create.c	(revision 17422)
@@ -0,0 +1,113 @@
+# include "opihi.h"
+# define D_NLINES 100
+# define prompt "> "
+int macro_exec   PROTO((int, char **));
+
+int macro_create (int argc, char **argv) {
+
+  int ThisList, depth, i, done, NLINES, N;
+  char *input, *help;
+  Command *cmd;
+  Macro *macro;
+
+  if ((N = get_argument (argc, argv, "-c"))) {
+    remove_argument (N, &argc, argv);
+    help = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    help = strcreate ("(macro)");
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro <name> [-c \"comment line\"]\n");
+    gprint (GP_ERR, "  (enter commands, end with the word 'END')\n");
+    return (FALSE);
+  }
+
+  /**** Check for existence of this macro ****/
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  
+  if ((macro == NULL) && (cmd != NULL)) {
+    gprint (GP_ERR, "cannot redefine inherent command %s\n", argv[1]);
+    return (FALSE);
+  }
+  if ((macro != NULL) && (cmd == NULL)) {
+    gprint (GP_ERR, "programming error: macro not in command list (%s)\n", argv[1]);
+    return (FALSE);
+  }
+
+  if (macro == NULL) { /**** New Macro ****/
+    ALLOCATE (cmd, Command, 1);
+    cmd[0].real = FALSE;
+    cmd[0].name = strcreate (argv[1]);
+    cmd[0].help = help;
+    cmd[0].func = macro_exec;
+    AddCommand (cmd);
+    free (cmd);
+    macro = NewMacro (argv[1]);
+  } else { /**** Old Macro ****/
+    /* replace existing command help with new value */
+    free (cmd[0].help);
+    cmd[0].help = help;
+  }
+
+  /* reallocate space for macro lines */
+  NLINES = D_NLINES;
+  for (i = 0; i < macro[0].Nlines; i++) free (macro[0].line[i]);
+  REALLOCATE (macro[0].line, char *, NLINES);
+
+  /* read in macro
+     If we are entering at the keyboard (ThisList == 0), use readline.
+     Otherwise, read from the current list
+     End when we hit the final "END" (the true END -- we count macro defines!!) */
+     
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+
+    stripwhite (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", macro is done */
+	free (input);
+	macro[0].Nlines = i;
+	if (macro[0].Nlines == 0) i = 1;
+	REALLOCATE (macro[0].line, char *, i);
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_delete.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_delete.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_delete.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int macro_delete (int argc, char **argv) {
+
+  Command *cmd;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro delete <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[1], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "Macro %s not found\n", argv[1]);
+    return (FALSE);
+  }
+  cmd = MatchCommand (argv[1], FALSE, TRUE);
+  if (cmd == NULL) {
+    gprint (GP_ERR, "programming error: macro exists but not command\n");
+    return (FALSE);
+  }
+
+  DeleteMacro (macro);
+  DeleteCommand (cmd);
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_edit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_edit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_edit.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_edit (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_exec.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_exec.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_exec.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "opihi.h"
+
+int macro_exec (int argc, char **argv) {
+
+  int i, status, MacroDepth;
+  char *PreviousName, **params, tmp[1024];
+  Macro *macro;
+  
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Command not found.\n", argv[0]);
+    return (FALSE);
+  }
+
+  /* increase MacroDepth by one - governs macro args */
+  PreviousName = GetMacroName ();
+  MacroDepth = GetMacroDepth ();
+  MacroDepth ++;
+  SetCurrentMacroData (macro[0].name, MacroDepth);
+
+  ALLOCATE (params, char *, argc);
+  sprintf (tmp, "%d.%d", MacroDepth, 0);
+  params[0] = strcreate (tmp);
+  sprintf (tmp, "%d", argc);
+  set_str_variable (params[0], tmp);
+  for (i = 1; i < argc; i++) {
+    sprintf (tmp, "%d.%d", MacroDepth, i);
+    params[i] = strcreate (tmp);
+    set_str_variable (params[i], argv[i]);
+  }
+
+  /* process this list */
+  status = exec_loop (&macro[0]);
+  loop_last = loop_next = FALSE; 
+  /* 'last' and 'next' should only affect one loop */
+
+  /* clear out the command line variables */
+  for (i = 0; i < argc; i++) {
+    DeleteNamedScalar (params[i]);
+    free (params[i]);
+  }
+  free (params);
+  
+  MacroDepth --;
+  SetCurrentMacroData (PreviousName, MacroDepth);
+  return (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_funcs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_funcs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_funcs.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "basic.h"
+
+static Command macro_command[] = {
+  {1, "create", macro_create, "create a macro *"},
+  {1, "delete", macro_delete, "delete a macro *"},
+  {1, "list",   macro_list_f, "list a macro *"},
+  {1, "edit",   macro_edit,   "edit a macro <not working yet!> *"},
+  {1, "read",   macro_read,   "read a macro from a file <not working yet!> *"},
+  {1, "write",  macro_write,  "write a macro to a file <not working yet!> *"}
+};
+
+CommandF *find_macro_command (char *name) {
+
+  int i, N;
+
+  N = sizeof (macro_command) / sizeof (Command);
+
+  /* find the macro sub-command which matches from the list. */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (macro_command[i].name, name)) {
+      return (macro_command[i].func);
+    }
+  }
+
+  return NULL;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_list.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_list.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_list.c	(revision 17422)
@@ -0,0 +1,23 @@
+# include "opihi.h"
+
+int macro_list_f (int argc, char **argv) {
+
+  int i;
+  Macro *macro;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: macro list <macro>\n");
+    return (FALSE);
+  }
+
+  macro = MatchMacro (argv[0], FALSE, TRUE);
+  if (macro == NULL) {
+    gprint (GP_ERR, "%s: Macro not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  for (i = 0; i < macro[0].Nlines; i++) {
+    gprint (GP_ERR, "%s\n", macro[0].line[i]);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_read.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_read.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_read.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_read (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_write.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_write.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/macro_write.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "opihi.h"
+
+int macro_write (int argc, char **argv) {
+
+  gprint (GP_ERR, "this function is not implemented yet\n");
+  return (FALSE);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/memstr.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/memstr.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/memstr.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "shell.h"
+
+/* memstr returns a view, not an allocated string : don't free */
+/* returns pointer to start of m2 in m1, or NULL if failure */ 
+char *memstr (char *m1, char *m2, int n) {
+
+  int i, N;
+
+  N = strlen (m2);
+  for (i = 0; (i < n - N + 1) && memcmp (m1, m2, N); i++, m1++);
+  if (memcmp (m1, m2, N)) return (NULL);
+  return (m1);
+
+}
+
+/* formatted write statement, with intelligent allocation */
+int write_fmt (int fd, char *format, ...) {
+
+  int Nbyte, status;
+  char tmp, *line;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  va_start (argp, format);
+  ALLOCATE (line, char, Nbyte + 1);
+  vsnprintf (line, Nbyte + 1, format, argp);
+  status = write (fd, line, strlen(line));
+  va_end (argp);
+
+  free (line);
+  return (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/multicommand.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/multicommand.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/multicommand.c	(revision 17422)
@@ -0,0 +1,159 @@
+# include "opihi.h"
+
+static int server = 0;
+static int bufferPending = FALSE;
+
+int getServer () {
+  return (server);
+}
+
+// XXX this is rather pantasks-specific...
+void multicommand_InitServer () {
+
+  char hostname[256], PASSWORD[256];
+
+  if (server != 0) {
+    /* check if down? */
+    fprintf (stderr, "error: server fd already defined\n");
+    exit (30);
+  }
+
+  /* find the defined server hostname */
+  if (VarConfig ("PANTASKS_SERVER", "%s", hostname) == NULL) {
+    gprint (GP_ERR, "pantasks server host undefined\n");
+    exit (31);
+  }
+
+  /* attempt to connect to the server */
+  server = GetClientSocket (hostname);
+
+  /* here we can perform the security handshaking */
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  SendCommand (server, strlen(PASSWORD), PASSWORD);
+  
+  return;
+}
+
+// close connection with remote server
+void multicommand_StopServer () {
+
+  if (!server) return;
+  close (server);
+  server = 0;
+  return;
+}
+
+/* take input line and split into multiple lines
+   at the semi-colons.  send these to 'command' */
+
+int multicommand (char *line) {
+ 
+  int done, status, verbose;
+  char *p, *q, *tmpline, *outline;
+  IOBuffer message;
+
+  /* if no server is defined, use 'verbose' mode for 'command' */ 
+  verbose = (server == 0);
+
+  p = line; 
+  done = FALSE;
+  status = TRUE;
+  while (!done) {
+    q = strchr (p, ';');
+    if (q == NULL) {
+      q = p + strlen(p);
+      done = TRUE;
+    }
+    tmpline = strncreate (p, q - p);
+    stripwhite (tmpline);
+    if (*tmpline) {
+
+      if (bufferPending) {
+	// flush old messages
+	ExpectMessage (server, 0.2, &message);
+	bufferPending = FALSE;
+      }
+
+      status = command (tmpline, &outline, verbose);
+
+      if (status == -1) {
+	if (server) {
+	  // send the command to the server instead
+	  if (!SendMessage (server, outline)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (32);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  }
+
+	  // receive the command exit status
+	  if (ExpectMessage (server, 2.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (33);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    sscanf (message.buffer, "STATUS %d", &status);
+	  }
+
+	  // receive the resulting stderr
+	  if (ExpectMessage (server, 2.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (34);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_ERR);
+	  }
+
+	  // receive the resulting stdout
+	  if (ExpectMessage (server, 2.0, &message)) {
+	    switch (errno) {
+	      case EPIPE:
+		gprint (GP_ERR, "server connection has died\n");
+		exit (35);
+	      default:
+		gprint (GP_ERR, "server is busy...\n");
+		bufferPending = TRUE;
+		goto escape;
+	    }
+	  } else {
+	    gwrite (message.buffer, 1, message.Nbuffer, GP_LOG);
+	  }
+	} else {
+	  // if no server is defined, we treat an unknown command as an error
+	  status = FALSE;
+	}	
+      }
+    escape:
+      if (outline != NULL) free (outline);
+      if (!status && auto_break) done = TRUE;
+    }
+    p = q + 1;
+  }
+  return (status);
+}
+
+/* should skip ; surrounded by "" */
+
+/* commands which are not found are sent to the server socket if 
+   defined. commands executed by the server return their stderr
+   and stdout streams.  server commands should probably not block
+   the server for very long or multiple clients will interfere
+   with each other.  
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/opihi.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/opihi.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/opihi.c	(revision 17422)
@@ -0,0 +1,64 @@
+# include "opihi.h"
+
+/******************/
+int opihi (int argc, char **argv) {
+
+  int Nbad, status;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (20);
+
+    line = readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	signal (SIGPIPE, SIG_IGN);
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (21);
+      }
+      if (!isatty (STDIN_FILENO)) exit (21);
+      gprint (GP_LOG, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+
+    Nbad = 0;
+    ohana_memregister (line);
+
+    stripwhite (line);
+    if (*line) {
+      status = multicommand (line);
+      add_history (line);
+
+// the libedit version of readline does not support an incremental write to history file
+# ifdef RL_READLINE_VERSION
+      if (history != NULL) append_history (1, history);
+# endif
+
+    }
+    free (line);
+    line = (char *) NULL;
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse.c	(revision 17422)
@@ -0,0 +1,308 @@
+# include "opihi.h"
+
+char *parse (char *line) {
+
+  double fval;
+  float *fptr;
+  char *newline, *N, *L, *val, *B, *V, *V0, *V1, *c1, *c2, *p;
+  int Nx, Ny, Nbytes, status, size, NLINE, isBuffer, inRange;
+  FILE *f;
+  Vector *vec;
+  Buffer *buf;
+
+  val = V0 = NULL;
+
+  if (line == (char *) NULL) goto error;
+
+  /* case 1: $var = expression */
+  if (line[0] == '$') {  
+    /* find the target variable name and the rest of the line */
+    interpolate_slash (line);
+    V0 = thisvar (line);
+    if (V0 == NULL) goto error;
+    V1 = aftervar (line);
+    if (V1 == NULL) goto error;
+ 
+    /* increment operator */
+    if (!strcmp (V1, "++")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = 1.0;
+      } else {
+	fval = atof (val) + 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* decrement operator */
+    if (!strcmp (V1, "--")) {
+      val = get_variable (V0);
+      if (val == NULL) {
+	fval = -1.0;
+      } else {
+	fval = atof (val) - 1.0;
+      }
+      set_variable (V0, fval);
+      goto escape;
+    }
+
+    /* not an assignement, syntax error */
+    if (*V1 != '=') goto error;
+
+    /* find first non-WHITESPACE character after = */
+    V1 ++;
+    while (isspace (*V1)) V1++;
+    if (*V1 == 0) goto error;
+
+    /* command replacement.  execute the line, place answer in val. */
+    if (*V1 == '`') {
+
+      /* look for end of line, must be ` */
+      B = V1 + strlen (V1) - 1;
+      if (*B != '`') goto error;
+
+      /* val will hold the result */
+      // XXX this is limiting!!!
+      ALLOCATE (val, char, 1024);
+
+      /* B is the command to be executed */
+      B = strncreate (V1 + 1, strlen(V1) - 2);
+      f = popen (B, "r");
+      Nbytes = fread (val, 1, 1023, f);
+      val[Nbytes] = 0;
+      status = pclose (f);
+      free (B);
+
+      if (status) gprint (GP_ERR, "warning: exit status of command %d\n", status);
+
+      /* convert all but last return to ' '.  drop last return */
+      for (B = val; *B != 0; B++) {
+	if (*B == '\n') {
+	  if (B[1]) {
+	    *B = ' ';
+	  } else {
+	    *B = 0;
+	  }
+	}
+      }
+    } 
+
+    /* simple variable assignment */
+    if (*V1 != '`') {
+      /* dvomath returns a new string, or NULL, with the result of the expression */
+      val = dvomath (1, &V1, &size, 0);
+      if (val == NULL) { 
+	while (OHANA_WHITESPACE (*V1)) V1++;
+	val = strcreate (V1);
+      } 
+    }
+    /* both dvomath and command replacement create (char *) val */
+    set_str_variable (V0, val);
+    goto escape;  /* frees temp variables */
+  }
+
+  /* case 2: vect[N] = value or buff[N][M] = value */
+  V0 = thiscomm (line);
+  if (strchr(V0, '[') != (char *) NULL) { 
+    /* get vector name (left in V0) */
+    N = strchr (V0, '[');
+    if (N == V0) goto error;
+
+    /* get bracket contents (left in V) */
+    L = strchr (V0, ']');
+    if (L == (char *) NULL) goto error;
+
+    *N = 0;
+    V = strncreate (N+1, L - N - 1);
+
+    /* expand value in brackets */
+    val = dvomath (1, &V, &size, 0);
+    if (val == NULL) {
+      print_error ();
+      goto error;
+    }
+    Nx = atoi (val);
+    free (val); val = NULL;
+    free (V);
+
+    isBuffer = FALSE;
+    N = L + 1;
+    if (*N == '[') {
+      isBuffer = TRUE;
+      L = strchr (N, ']');
+
+      V = strncreate (N+1, L - N - 1);
+
+      /* expand value in brackets */
+      val = dvomath (1, &V, &size, 0);
+      if (val == NULL) {
+	print_error ();
+	goto error;
+      }
+      Ny = atoi (val);
+      free (val); val = NULL;
+      free (V);
+    }
+
+    /* find value for assignment */
+    V1 = nextcomm (line);
+    if (V1 == (char *) NULL) goto error;
+    if (V1[0] != '=') goto error;
+    V1 ++;
+
+    /*** assign vector element to value ***/
+    val = dvomath (1, &V1, &size, 0); 
+    if (val == (char *) NULL) {
+      print_error ();
+      goto error;
+    }
+
+    /* find vector */
+    if (isBuffer) {
+      if ((buf = SelectBuffer (V0, OLDBUFFER, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*buf[0].header.Naxis[0]);
+      inRange &= (Nx >= -1*buf[0].header.Naxis[0]);
+      inRange &= (Ny <  +1*buf[0].header.Naxis[1]);
+      inRange &= (Ny >= -1*buf[0].header.Naxis[1]);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d,%d\n", Nx, Ny);
+	goto escape;
+      }
+      if (Nx < 0) Nx += buf[0].header.Naxis[0];
+      if (Ny < 0) Ny += buf[0].header.Naxis[1];
+
+      fptr = (float *) buf[0].matrix.buffer;
+      fptr[Nx + Ny*buf[0].header.Naxis[0]] = atof (val);
+    } else {
+      if ((vec = SelectVector (V0, OLDVECTOR, TRUE)) == NULL) goto error;
+      free (V0); V0 = (char *) NULL;
+
+      inRange = TRUE;
+      inRange &= (Nx <  +1*vec[0].Nelements);
+      inRange &= (Nx >= -1*vec[0].Nelements);
+      if (!inRange) {
+	gprint (GP_ERR, "no element %d\n", Nx);
+	goto escape;
+      }
+      if (Nx < 0) Nx += vec[0].Nelements;
+      vec[0].elements[Nx] = atof (val);
+    }
+
+    goto escape;
+  }
+  free (V0); V0 = (char *) NULL;
+
+  /* case 3: {expression} */
+  NLINE = MAX (128, strlen (line));
+  ALLOCATE (newline, char, NLINE);
+  memset (newline, 0, NLINE);
+  for (L = line; *L != 0; ) { 
+
+    // copy elements from L (line) to newline up to first '{'
+    p = strchr (L, '{');
+    newline = opihi_append (newline, &NLINE, L, p);
+    if (p == NULL) break;
+    L = p + 1;
+
+    // check for \{ at this point, replace \ in newline with {
+    if ((p > line) && (*(p - 1) == 0x5c)) {
+      N = newline + strlen(newline) - 1;
+      *N = '{'; // replace \ with {
+      continue;
+    }
+
+    /* check on the syntax of the line */
+    L = p + 1;
+    c1 = strchr (L, '}');
+    if (c1 == NULL) {
+      gprint (GP_ERR, "no close brackets!\n");
+      goto escape;
+    }
+    c2 = strchr (L, '{');
+    if ((c2 != NULL) && (c2 < c1)) {
+      gprint (GP_ERR, "can't nest brackets!\n");
+      goto escape;
+    }
+    *c1 = 0;
+
+    /* value in brackets must be a math expression of some sort.
+       isolated words are not valid here */
+    val = dvomath (1, &L, &size, -1);
+    if (val == NULL) {
+      print_error ();
+      goto escape;
+    } 
+
+    /* interpolate vector element into newline (being accumulated) */
+    newline = opihi_append (newline, &NLINE, val, NULL);
+
+    /* copy val to outline */
+    L = c1 + 1;
+    free (val); val = (char *) NULL;
+  }
+
+  free (line); line = (char *) NULL;
+
+  /* \ protects next character */
+  interpolate_slash (newline);
+  
+  REALLOCATE (newline, char, strlen (newline) + 1);
+  return (newline); 
+
+error:
+  gprint (GP_ERR, "syntax error\n");
+
+escape:
+  if (line != (char *) NULL) free (line);
+  if (val != (char *) NULL) free (val);
+  if (V0 != (char *) NULL) free (V0);
+  return (NULL);
+}
+
+/* this routine looks for math and/or logic expressions that 
+   need to be evaluated and passes them on to "parenthesis",
+   which evaluates the expression */
+
+/* There are two situations now which define an expression to 
+   be evaluated:
+   1) $var = expression   -- everything after the = must be a math expression or a string
+   2) {expression}        -- everything within the {} must be a math expression
+*/
+
+/* case 1: $var = expression.  test that
+   a) there is a $ as the first character
+   b) the variable defined by that $ is not null
+   c) there is an = sign following the variable
+   d) there is something following the = sign
+*/
+  
+/* case 2: vect[N] = expression. check syntax:
+   a) last char must be ]
+   b) word must contain [
+   c) between [ & ] must be an integer
+   d) vector must exist
+   e) there is an = sign following the first word
+   f) there is something following the = sign
+*/
+     
+
+/* case 3: we hunt for '{', and then the first '}' and pass everything
+   inbetween.  no nested {{}}s are allowed! */
+  
+/* thisword ALLOCATES
+   thisvar  ALLOCATES
+   thiscomm ALLOCATES
+   
+   nextword !ALLOCATE
+   nextcomm !ALLOCATE
+   
+   lastword !ALLOCATE
+   lastvar !ALLOCATE
+   
+   aftervar !ALLOCATE
+*/   
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse_commands.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse_commands.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/parse_commands.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "opihi.h"
+# define D_NARG 10
+
+char **parse_commands (char *line, int *argc) {
+
+  int i, NARG;
+  char *c;
+  char **argv;
+
+  NARG = D_NARG;
+  ALLOCATE (argv, char *, NARG);
+
+  argv[0] = thisword (line);
+  if (argv[0] == (char *) NULL) {
+    free (argv);
+    *argc = 0;
+    return ((char **) NULL);
+  }
+
+  c = nextword (line);
+  for (i = 1; c != (char *) NULL; i++) {
+    argv[i] = thisword (c);
+
+    /* if one of the words does not parse (eg, ""), skip it */
+    if (argv[i] == NULL) i--;
+
+    c = nextword (c);
+    if (i == NARG - 1) {
+      NARG += D_NARG;
+      REALLOCATE (argv, char *, NARG);
+    }
+  }
+  REALLOCATE (argv, char *, i);
+  *argc = i;
+
+  return (argv);
+
+}
+
+  /* parse out the command line into (char **argv).  line looks like:
+     command word word word ... */
+
+  /* argv[0] and argv[1..argc-1] are handled differently because
+     the syntax of a valid command and a valid word are slighly 
+     different (not that this is obviously wanted...) */
+
+
+
+/* old code:
+      for (j = 0; j < i; j++) 
+	free (argv[i]);
+      free (argv);
+      *argc = 0;
+      return (argv);
+    }
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/stack_math.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/stack_math.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/stack_math.c	(revision 17422)
@@ -0,0 +1,1454 @@
+# include "opihi.h"
+
+/* the result of a matrix operation must go into a temp variable,
+   labeled by "m". if one of the input matrices is already a temp variable, we 
+   can continue using it this round 
+   */
+
+int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V1[0].vector[0].Nelements;
+  if (Nx != V2[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  if (V1[0].type == 'v') {  /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == 'v') { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].vector = V2[0].vector;
+      V2[0].type = 'V'; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].vector = InitVector ();
+      CopyVector (OUT[0].vector, V1[0].vector);
+    }
+  }
+  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M1++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+int SV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V2[0].vector[0].Nelements;
+
+  if (V2[0].type == 'v') { /** use V2 as temp buffer, but header of V1 **/
+    OUT[0].vector = V2[0].vector;
+    V2[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V2[0].vector);
+  }
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int VS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V1[0].vector[0].Nelements;
+
+  if (V1[0].type == 'v') { /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V1[0].vector);
+  }
+  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx; i++, out++, M1++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx; i++, out++, M1++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int MV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512];
+ 
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+  if (Ny != V2[0].vector[0].Nelements) {
+    push_error ("dimension mismatch");
+    return (FALSE);
+  }
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {  
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {  
+    /* do buffer.matrix.buffer and buffer.header.buffer get correctly zeroed? */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 + *M2;
+    }
+    break; 
+  case '-': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 - *M2;
+    }
+    break; 
+  case '*': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 * *M2;
+    }
+    break; 
+  case '/': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = *M1 / *M2;
+    }
+    break; 
+  case '%': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++)
+        *out = (int) *M1 % (int) *M2;
+    }
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = pow (*M1, *M2);
+    }
+    break; 
+  case '@': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = DEG_RAD*atan2 (*M1, *M2);
+    }
+    break; 
+  case 'D': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = MIN (*M1, *M2);
+    }
+    break; 
+  case 'U': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = MAX (*M1, *M2);
+    }
+    break; 
+  case '<': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 < *M2) ? 1 : 0;
+    }
+    break; 
+  case '>': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 > *M2) ? 1 : 0;
+    }
+    break; 
+  case '&': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = ((int)*M1 & (int)*M2);
+    }
+    break; 
+  case '|': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = ((int)*M1 | (int)*M2);
+    }
+    break; 
+  case 'E': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 == *M2) ? 1 : 0;
+    }
+    break; 
+  case 'N': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 != *M2) ? 1 : 0;
+    }
+    break; 
+  case 'L': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 <= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'G': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 >= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'A': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 && *M2) ? 1 : 0;
+    }
+    break; 
+  case 'O': 
+    for (i = 0; i < Ny; i++, M2++) {
+      for (j = 0; j < Nx; j++, out++, M1++) 
+        *out = (*M1 || *M2) ? 1 : 0;
+    }
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'v') {
+    free (V2[0].vector[0].elements);
+    free (V2[0].vector);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+
+int VM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+  if (Nx != V1[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  /* if possible, use V2 as temp buffer, otherwise create new one */
+  if (V2[0].type == 'm') {
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 + *M2;
+    }
+    break; 
+  case '-': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 - *M2;
+    }
+    break; 
+  case '*': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 * *M2;
+    }
+    break; 
+  case '/': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = *M1 / *M2;
+    }
+    break; 
+  case '%': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++)
+        *out = (int) *M1 % (int) *M2;
+    }
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = pow (*M1, *M2);
+    }
+    break; 
+  case '@': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = DEG_RAD*atan2 (*M1, *M2);
+    }
+    break; 
+  case 'D': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = MIN (*M1, *M2);
+    }
+    break; 
+  case 'U': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = MAX (*M1, *M2);
+    }
+    break; 
+  case '<': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 < *M2) ? 1 : 0;
+    }
+    break; 
+  case '>': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 > *M2) ? 1 : 0;
+    }
+    break; 
+  case '&': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = ((int)*M1 & (int)*M2);
+    }
+    break; 
+  case '|': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = ((int)*M1 | (int)*M2);
+    }
+    break; 
+  case 'E': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 == *M2) ? 1 : 0;
+    }
+    break; 
+  case 'N': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 != *M2) ? 1 : 0;
+    }
+    break; 
+  case 'L': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 <= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'G': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 >= *M2) ? 1 : 0;
+    }
+    break; 
+  case 'A': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 && *M2) ? 1 : 0;
+    }
+    break; 
+  case 'O': 
+    for (i = 0; i < Ny; i++) {
+      M1 = V1[0].ptr;
+      for (j = 0; j < Nx; j++, out++, M1++, M2++) 
+        *out = (*M1 || *M2) ? 1 : 0;
+    }
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+int MM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {  /** use V1 as temp buffer **/
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == 'm') { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].buffer = V2[0].buffer;
+      V2[0].type = 'M'; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].buffer = InitBuffer ();
+      CopyBuffer (OUT[0].buffer, V1[0].buffer);
+    }
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+  return (TRUE);
+
+}
+
+
+int MS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  /* if possible, use V1 as temp buffer, otherwise create new one */
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M1++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+
+int SM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2;
+  char line[512];
+  
+  Nx = V2[0].buffer[0].matrix.Naxis[0];
+  Ny = V2[0].buffer[0].matrix.Naxis[1];
+
+  if (V2[0].type == 'm') {  /* V2[0] is NOT temporary, we can't use it for storage */
+    OUT[0].buffer = V2[0].buffer;
+    V2[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V2[0].buffer);
+  }
+  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  switch (op[0]) { 
+  case '+': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 + *M2;
+    break; 
+  case '-': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 - *M2;
+    break; 
+  case '*': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 * *M2;
+    break; 
+  case '/': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = *M1 / *M2;
+    break; 
+  case '%': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++)
+      *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    for (i = 0; i < Nx*Ny; i++, out++, M2++) 
+      *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  if (V2[0].type == 'm') {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+
+int SS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  float *M1, *M2, *out;
+  char line[512];
+
+  M1  = V1[0].ptr;
+  M2  = V2[0].ptr;
+  OUT[0].ptr = V1[0].ptr;
+  out = OUT[0].ptr;
+
+
+  switch (op[0]) { 
+  case '+': 
+    *out = *M1 + *M2;
+    break;    
+  case '-': 
+    *out = *M1 - *M2;
+    break;    
+  case '*': 
+    *out = *M1 * *M2;
+    break;    
+  case '/': 
+    *out = *M1 / *M2;
+    break; 
+  case '%': 
+    *out = (int) *M1 % (int) *M2;
+    break; 
+  case 0x5e: 
+    *out = pow (*M1, *M2);
+    break; 
+  case '@': 
+    *out = DEG_RAD*atan2 (*M1, *M2);
+    break; 
+  case 'D': 
+    *out = MIN (*M1, *M2);
+    break; 
+  case 'U': 
+    *out = MAX (*M1, *M2);
+    break; 
+  case '<': 
+    *out = (*M1 < *M2) ? 1 : 0;
+    break; 
+  case '>': 
+    *out = (*M1 > *M2) ? 1 : 0;
+    break; 
+  case '&': 
+    *out = ((int)*M1 & (int)*M2);
+    break; 
+  case '|': 
+    *out = ((int)*M1 | (int)*M2);
+    break; 
+  case 'E': 
+    *out = (*M1 == *M2) ? 1 : 0;
+    break; 
+  case 'N': 
+    *out = (*M1 != *M2) ? 1 : 0;
+    break; 
+  case 'L': 
+    *out = (*M1 <= *M2) ? 1 : 0;
+    break; 
+  case 'G': 
+    *out = (*M1 >= *M2) ? 1 : 0;
+    break; 
+  case 'A': 
+    *out = (*M1 && *M2) ? 1 : 0;
+    break; 
+  case 'O': 
+    *out = (*M1 || *M2) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+  OUT[0].Float = *(OUT[0].ptr);
+  OUT[0].type = 'S';
+
+  clear_stack (V1);
+  clear_stack (V2);
+
+  return (TRUE);
+
+}
+
+int WW_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int value;
+  char line[512];
+
+  /* only 'N' and 'E' are allowed for WW_binary operations. anything else is either a
+   syntax error or is a string which looks like a math expression. */
+
+  if ((op[0] != 'N') && (op[0] != 'E')) {
+    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+  /* evaluate stack will only call WW_binary with one numerical value,
+     and only in the case that the string did not parse to a number 
+     thus: string == number -> false */
+
+  if (!strncasecmp (&V1[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (!strncasecmp (&V2[0].type, "S", 1)) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+
+  switch (op[0]) { 
+  case 'E': 
+    value = strcmp (V1[0].name, V2[0].name) ? 0 : 1;
+    break; 
+  case 'N': 
+    value = strcmp (V1[0].name, V2[0].name) ? 1 : 0;
+    break; 
+  default:
+    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    push_error (line);
+    return (FALSE);
+  }
+
+escape:
+  OUT[0].Float = value;
+  OUT[0].type = 'S';
+  OUT[0].ptr = &OUT[0].Float;
+
+  clear_stack (V1);
+  clear_stack (V2);
+  return (TRUE);
+
+}
+
+
+int S_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  float *out, *M1;
+  
+  out = OUT[0].ptr = V1[0].ptr;
+  M1  = V1[0].ptr;
+
+  if (!strcmp (op, "="))     {    }
+  if (!strcmp (op, "abs"))   {    *out = fabs(*M1);           }
+  if (!strcmp (op, "int"))   {    *out = (float)(int)(*M1);   }
+  if (!strcmp (op, "exp"))   {    *out = exp (*M1);           }
+  if (!strcmp (op, "ten"))   {    *out = pow (10.0,*M1);      }
+  if (!strcmp (op, "log"))   {    *out = log10 (*M1);         }
+  if (!strcmp (op, "ln"))    {    *out = log (*M1);           }
+  if (!strcmp (op, "sqrt"))  {    *out = sqrt (*M1);          }
+  if (!strcmp (op, "erf"))   {    *out = erf (*M1);           }
+
+  if (!strcmp (op, "sinh"))  {    *out = sinh (*M1);          }
+  if (!strcmp (op, "cosh"))  {    *out = cosh (*M1);          }
+  if (!strcmp (op, "asinh")) {    *out = asinh (*M1);         }
+  if (!strcmp (op, "acosh")) {    *out = acosh (*M1);         }
+  if (!strcmp (op, "lgamma")) {   *out = lgamma (*M1);        }
+
+  if (!strcmp (op, "sin"))   {    *out = sin (*M1);           }
+  if (!strcmp (op, "cos"))   {    *out = cos (*M1);           }
+  if (!strcmp (op, "tan"))   {    *out = tan (*M1);           }
+  if (!strcmp (op, "dsin"))  {    *out = sin (*M1*RAD_DEG);   }
+  if (!strcmp (op, "dcos"))  {    *out = cos (*M1*RAD_DEG);   }
+  if (!strcmp (op, "dtan"))  {    *out = tan (*M1*RAD_DEG);   }
+  if (!strcmp (op, "asin"))  {    *out = asin (*M1);          }
+  if (!strcmp (op, "acos"))  {    *out = acos (*M1);          }
+  if (!strcmp (op, "atan"))  {    *out = atan (*M1);          }
+  if (!strcmp (op, "dasin")) {    *out = asin (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "dacos")) {    *out = acos (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "datan")) {    *out = atan (*M1)*DEG_RAD;  }
+  if (!strcmp (op, "rnd"))   {    *out = drand48();           }
+  if (!strcmp (op, "not"))   {    *out = !(*M1);              }
+  if (!strcmp (op, "--"))    {    *out = - (*M1);             }
+  if (!strcmp (op, "isinf")) {    *out = !finite(*M1);        }
+  if (!strcmp (op, "isnan")) {    *out = isnan(*M1);          } 
+
+  OUT[0].Float = *out;
+  OUT[0].type = 'S';
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+int V_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, Nx;
+  float *out, *M1;
+  
+  Nx = V1[0].vector[0].Nelements;
+ 
+  if (V1[0].type == 'v') {  /** use V1 as temp buffer **/
+    OUT[0].vector = V1[0].vector;
+    V1[0].type = 'V'; /* prevent it from being freed below */
+  } else {  /* no spare temp buffer */
+    OUT[0].vector = InitVector ();
+    CopyVector (OUT[0].vector, V1[0].vector);
+  }
+  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+  M1  = V1[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].vector[0].elements;
+
+  if (!strcmp (op, "="))     { } /* already set equal */
+  if (!strcmp (op, "abs"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = fabs(*M1);         }}
+  if (!strcmp (op, "int"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = (float)(int)(*M1); }}
+  if (!strcmp (op, "exp"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = exp(*M1);          }}
+  if (!strcmp (op, "ten"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
+  if (!strcmp (op, "log"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = log10(*M1);        }}
+  if (!strcmp (op, "ln"))    { for (i = 0; i < Nx; i++, out++, M1++) { *out = log(*M1);          }}
+  if (!strcmp (op, "sqrt"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = sqrt(*M1);         }}
+  if (!strcmp (op, "erf"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = erf(*M1);          }}
+
+  if (!strcmp (op, "sinh"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = sinh(*M1);        }}
+  if (!strcmp (op, "cosh"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = cosh(*M1);        }}
+  if (!strcmp (op, "asinh"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = asinh(*M1);       }}
+  if (!strcmp (op, "acosh"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = acosh(*M1);       }}
+  if (!strcmp (op, "lgamma")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = lgamma(*M1);      }}
+
+  if (!strcmp (op, "sin"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = sin(*M1);          }}
+  if (!strcmp (op, "cos"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = cos(*M1);          }}
+  if (!strcmp (op, "tan"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = tan(*M1);          }}
+  if (!strcmp (op, "dsin"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = sin(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dcos"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = cos(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dtan"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = tan(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "asin"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = asin(*M1);         }}
+  if (!strcmp (op, "acos"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = acos(*M1);         }}
+  if (!strcmp (op, "atan"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = atan(*M1);         }}
+  if (!strcmp (op, "dasin")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = asin(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "dacos")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = acos(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "datan")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = atan(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "rnd"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = drand48();         }}
+  if (!strcmp (op, "ramp"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "zero"))  { for (i = 0; i < Nx; i++, out++, M1++) { *out = 0;                 }}
+  if (!strcmp (op, "not"))   { for (i = 0; i < Nx; i++, out++, M1++) { *out = !(*M1);            }}
+  if (!strcmp (op, "--"))    { for (i = 0; i < Nx; i++, out++, M1++) { *out = -(*M1);            }}
+  if (!strcmp (op, "isinf")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = !finite(*M1);      }}
+  if (!strcmp (op, "isnan")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = isnan(*M1);        }}
+
+  /* xramp and yramp only make sense in for matrices. for vectors, xramp = ramp, yramp = zero */
+  if (!strcmp (op, "xramp")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "yramp")) { for (i = 0; i < Nx; i++, out++, M1++) { *out = 0;                 }}
+
+  if (V1[0].type == 'v') {
+    free (V1[0].vector[0].elements);
+    free (V1[0].vector);
+    V1[0].vector = NULL;
+  }  
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+int M_unary (StackVar *OUT, StackVar *V1, char *op) {
+
+  int i, j, Nx, Ny;
+  float *out, *M1;
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == 'm') {
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = 'M'; /* prevent it from being freed below */
+  } else {
+    OUT[0].buffer = InitBuffer ();
+    CopyBuffer (OUT[0].buffer, V1[0].buffer);
+  }
+  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  M1  = V1[0].ptr;
+  out = OUT[0].ptr = (float *)OUT[0].buffer[0].matrix.buffer;
+
+  if (!strcmp (op, "="))     { }
+  if (!strcmp (op, "abs"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = fabs(*M1);         }}
+  if (!strcmp (op, "int"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = (float)(int)(*M1); }}
+  if (!strcmp (op, "exp"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = exp(*M1);          }}
+  if (!strcmp (op, "ten"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
+  if (!strcmp (op, "log"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log10(*M1);        }}
+  if (!strcmp (op, "ln"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = log(*M1);          }}
+  if (!strcmp (op, "sqrt"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sqrt(*M1);         }}
+  if (!strcmp (op, "erf"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = erf(*M1);          }}
+
+  if (!strcmp (op, "sinh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sinh(*M1);         }}
+  if (!strcmp (op, "cosh"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cosh(*M1);         }}
+  if (!strcmp (op, "asinh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asinh(*M1);        }}
+  if (!strcmp (op, "acosh")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acosh(*M1);        }}
+  if (!strcmp (op, "lgamma")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = lgamma(*M1);      }}
+
+  if (!strcmp (op, "sin"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1);          }}
+  if (!strcmp (op, "cos"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1);          }}
+  if (!strcmp (op, "tan"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1);          }}
+  if (!strcmp (op, "dsin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = sin(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dcos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = cos(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "dtan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = tan(*M1*RAD_DEG);  }}
+  if (!strcmp (op, "asin"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1);         }}
+  if (!strcmp (op, "acos"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1);         }}
+  if (!strcmp (op, "atan"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1);         }}
+  if (!strcmp (op, "dasin")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = asin(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "dacos")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = acos(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "datan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = atan(*M1)*DEG_RAD; }}
+  if (!strcmp (op, "not"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !(*M1);            }}
+  if (!strcmp (op, "--"))    { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = -(*M1);            }}
+  if (!strcmp (op, "rnd"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = drand48();         }}
+  if (!strcmp (op, "ramp"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = i;                 }}
+  if (!strcmp (op, "zero"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = 0;                 }}
+  if (!strcmp (op, "isinf")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = !finite(*M1);      }}
+  if (!strcmp (op, "isnan")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = isnan(*M1);        }}
+
+  /* xrm and yrm only make sense in for matrices. see special meaning for vectors */
+  if (!strcmp (op, "xramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+        *out = i;
+      }
+    }
+  }
+  if (!strcmp (op, "yramp")) {
+    for (j = 0; j < Ny; j++) {
+      for (i = 0; i < Nx; i++, out++, M1++) {
+        *out = j;
+      }
+    }
+  }
+  
+  if (V1[0].type == 'm') {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+
+  clear_stack (V1);
+  return (TRUE);
+
+}
+
+/*********************** fits copy header ***********************************/
+int gfits_copy_matrix_info (Matrix *matrix1, Matrix *matrix2) {
+
+  int i;
+
+  /* copy all but the matrix */
+
+  matrix2[0].unsign = matrix1[0].unsign;
+  matrix2[0].bitpix = matrix1[0].bitpix;
+  matrix2[0].size   = matrix1[0].size;
+  matrix2[0].bzero  = matrix1[0].bzero;
+  matrix2[0].bscale = matrix1[0].bscale;
+  matrix2[0].Naxes  = matrix1[0].Naxes;
+  for (i = 0; i < FT_MAX_NAXES; i++) 
+    matrix2[0].Naxis[i] = matrix1[0].Naxis[i];
+
+
+  return (TRUE);
+}       
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/startup.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/startup.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/startup.c	(revision 17422)
@@ -0,0 +1,160 @@
+# include "opihi.h"
+
+/* program-independent initialization
+ * these steps do not depend on the opihi implementation
+ * (ie, the supplied commands or data structures)
+ */
+
+void general_init (int *argc, char **argv) {
+
+  /* init srand for rnd numbers elsewhere */
+  long A, B;
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+ 
+  /* set signals */
+  signal (SIGINT, SIG_IGN);
+
+  /* init for main (or only) thread */
+  gprintInit ();
+
+  init_error ();
+
+  /* load config data (.ptolemyrc) */
+  if (!ConfigInit (argc, argv)) {
+    gprint (GP_ERR, "can't find config file. some functions will be unavailable\n");
+  }
+
+  return;
+}
+
+void startup (int *argc, char **argv) {
+    
+    int i, N, status, ONLY_INPUT, LOAD_RC;
+    char *line, *home, *outline, *varname;
+    char *rcfile, **list;
+    int Nlist, NLIST;
+   
+    /* load in interesting environment variables */
+    ALLOCATE (line, char, 1024);
+    
+    home = getenv ("HOME");
+    if (home == NULL) 
+      set_str_variable ("HOME", ".");
+    else 
+      set_str_variable ("HOME", home);
+    set_str_variable ("KAPA", "kapa");
+    set_variable ("PID", getpid());
+
+    set_int_variable ("UNSIGN", 0);
+    gfits_set_unsign_mode (FALSE);
+    
+  /* check history file permission */
+  {
+    FILE *f;
+    char *opihi_history;
+
+    opihi_history = get_variable ("HISTORY");
+    if (opihi_history && *opihi_history) {
+      f = fopen (opihi_history, "a");
+      if (f == NULL) /* no current history file here */
+	gprint (GP_ERR, "can't save history.\n");
+      else
+	fclose (f);
+      stifle_history (200);
+      read_history (opihi_history);
+    }
+    if (opihi_history) free (opihi_history);
+  }
+
+  if (0) {
+    /* fix the history list to remove the timestamp */
+    char *c;
+    int i;
+    HIST_ENTRY **entry;
+    
+    entry = history_list ();
+    if (entry != (HIST_ENTRY **) NULL) {
+      for (i = 0; entry[i]; i++) {
+	if ((strlen (entry[i][0].line) > 19) &&
+	    (entry[i][0].line[2] == '/') && 
+	    (entry[i][0].line[5] == '/') && 
+	    (entry[i][0].line[11] == ':') && 
+	    (entry[i][0].line[14] == ':') && 
+	    (entry[i][0].line[17] == ':')) {
+	  c = entry[i][0].line + 19;
+	  memmove (entry[i][0].line, c, strlen(c) + 1);
+	}
+      }
+    }
+  }
+
+    LOAD_RC = TRUE;
+    if ((N = get_argument (*argc, argv, "--norc"))) {
+      remove_argument (N, argc, argv);
+      LOAD_RC = FALSE;
+    }
+    
+    ONLY_INPUT = FALSE;
+    if ((N = get_argument (*argc, argv, "--only"))) {
+      remove_argument (N, argc, argv);
+      ONLY_INPUT = TRUE;
+    }
+
+    /* load cmdline files */
+    Nlist = 0;
+    NLIST = 10;
+    ALLOCATE (list, char *, NLIST);
+    while ((N = get_argument (*argc, argv, "--load"))) {
+      remove_argument (N, argc, argv);
+      list[Nlist] = strcreate (argv[N]);
+      remove_argument (N, argc, argv);
+      Nlist++;
+      if (Nlist == NLIST) {
+	NLIST += 10;
+	REALLOCATE (list, char *, NLIST);
+      }
+    }
+
+    is_script = TRUE;
+    if (*argc == 1) is_script = FALSE;
+    set_int_variable ("SCRIPT", is_script);
+
+    if (LOAD_RC && !is_script) {
+      rcfile = get_variable ("RCFILE");
+      sprintf (line, "input %s/%s", home, rcfile);
+      status = command (line, &outline, TRUE);
+      if (outline != (char *) NULL) free (outline);
+      if (rcfile) free (rcfile);
+    }
+      
+    set_int_variable ("argv:n", 0);
+    if (is_script) {
+      /* first argument in input script, rest are argv */
+      list[Nlist] = strcreate (argv[1]);
+      Nlist ++;
+      /* generate list argv:0 - argv:n from arguments */
+      ALLOCATE (varname, char, 256);
+      for (i = 2; i < *argc; i++) {
+	sprintf (varname, "argv:%d", i - 2);
+	set_str_variable (varname, argv[i]);
+      }
+      set_int_variable ("argv:n", i - 2);
+      free (varname);
+    }
+
+    /* execute command-line entries */
+    for (i = 0; i < Nlist; i++) {
+      ALLOCATE (line, char, 1024);
+      sprintf (line, "input %s", list[i]);
+      status = command (line, &outline, TRUE);
+      if (outline != (char *) NULL) free (outline);
+    }
+    free (list);
+
+    /* if this is not an interactive session, exit here */
+    if (ONLY_INPUT) exit (40);
+    if (is_script) exit (41);
+    return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/string.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/string.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/string.c	(revision 17422)
@@ -0,0 +1,289 @@
+# include "opihi.h"
+
+/**********************************************************************/
+/* returns a pointer to an isolated string containing the first word,
+   removing leading WHITESPACE.  A "word" is a contiguous set of 
+   characters from the set: alphanumerics, and any of: / . _ -
+   Any other single, non-WHITESPACE characters are considered to be 
+   complete words in themselves.  Any characters surrounded by quotes 
+   make a single word 
+*/
+
+char *thisword (char *string) {
+
+  int i, j, N;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+  if (string[i] == ';') {
+    word = strncreate (&string[i], 1);
+    return (word);
+  }
+
+  /* a string of characters contained within double quotes is 
+      a single entry.  check that there are pairs of quotes,
+      return only the string between the quotes */
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (string[j] != '"'); j++);
+    if (string[j] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    //    for (; (string[j] != 0) && (!OHANA_WHITESPACE(string[j])); j++);
+    word = strncreate (&string[i], j - i);
+    return (word);
+  } 
+
+  /* a string of characters contained within parentheses is 
+      a single entry.  check that there are matched pairs of
+      parentheses, return string, including exterior parentheses */
+  if (string[i] == '(') { 
+    i++;
+    N = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (j = i; (string[j] != 0) && (N > 0); j++) {
+      if (string[j] == '(') {
+	N++;
+      }
+      if (string[j] == ')') {
+	N--;
+      }
+    }
+    if ((string[j] == 0) && (N != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    word = strncreate (&string[i-1], j - i + 1);
+    return (word);
+  } 
+
+
+  for (j = i; (string[j] != 0) && (string[j] != ';') && !OHANA_WHITESPACE(string[j]); j++);
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* returns a pointer to an isolated string containing the first command,
+   removing leading WHITESPACE.  A command ends with the first non WHITESPACE */
+char *thiscomm (char *string) {
+
+  int i, j;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  for (j = i; ((string[j] != 0) && !OHANA_WHITESPACE (string[j])); j++);
+  if (i == j) return ((char *) NULL);
+
+  word = strncreate (&string[i], j - i);
+  return (word);
+
+}
+
+/* take a pointer to the beginning of a variable (ie $foo)
+and extract only the variable name (eg, foo) */
+
+char *thisvar (char *string) {
+
+  int i, start;
+  char *word;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case $?name : check that name is valid */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  /* the ? is part of the variable */
+  word = strncreate (&string[1], i - 1);
+  return (word);
+
+}
+
+
+/**********************************************************************/
+/* returns a pointer to the next word, or (char *) NULL if there is not a next word */
+char *nextword (char *string) {
+
+  int i, j;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+
+  for (i = 0; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *)NULL);
+
+  if (string[i] == '"') { 
+    i++;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (string[i] != '"'); i++);
+    if (string[i] == 0) {
+      gprint (GP_ERR, "misbalanced quotes\n");
+      return ((char *)NULL);
+    }
+    i++;
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == '(') { 
+    i++; 
+    j = 1;
+    if (string[i] == 0) return ((char *) NULL);
+    for (; (string[i] != 0) && (j > 0); i++) {
+      if (string[i] == '(') {
+	j++;
+      }
+      if (string[i] == ')') {
+	j--;
+      }
+    }
+    if ((string[i] == 0) && (j != 0)) {
+      gprint (GP_ERR, "misbalanced parenthesis\n");
+      return ((char *)NULL);
+    }
+    for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+    if (string[i] == 0) return ((char *) NULL);
+    return (&string[i]);
+  } 
+
+  if (string[i] == ';') i++;
+  for (; (string[i] != 0) && (string[i] != ';') && !OHANA_WHITESPACE(string[i]); i++);
+  for (; (string[i] != 0) && OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the next command, or (char *) NULL 
+   if there is not a next command.  A command is bounded by WHITESPACE */
+char *nextcomm (char *string) {
+
+  int i;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  
+  for (i = 0; (string[i] != 0) && !OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+  
+  for (; OHANA_WHITESPACE (string[i]); i++);
+  if (string[i] == 0) return ((char *) NULL);
+
+  return (&string[i]);
+}
+
+/* returns a pointer to the previous word,
+   or (char *) NULL if there is not a previous word
+*/
+char *lastword (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; OHANA_WHITESPACE(*c) && (c >= string); c--);
+  if (c < string)
+    return ((char *)NULL);
+  for (; !OHANA_WHITESPACE(*c) && (c >= string); c--);
+  c++;
+  return (c);
+}
+
+
+
+/* take a pointer to the beginning of a variable (ie $fred) and return
+   a pointer to the next thing (non WHITESPACE) which is not part of the
+   variable extract only the variable name */
+
+char *aftervar (char *string) {
+
+  int i, j, start;
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (string[0] != '$') return ((char *) NULL);
+
+  /* special case: $?name : test only name */
+  start = 1;
+  if (string[1] == '?') start = 2;
+
+  for (i = start; ISVAR(string[i]); i++);
+  if (i == start) return ((char *) NULL);
+
+  for (j = i; OHANA_WHITESPACE (string[j]); j++);
+  if (string[j] == 0) return ((char *)NULL);
+
+  return (&string[j]);
+
+}
+
+
+/* returns a pointer to the previous var, 
+   or (char *) NULL if there is not a previous word
+*/
+char *lastvar (char *string, char *c) {
+
+  if (string == (char *) NULL) return ((char *) NULL);
+  if (c == (char *) NULL) return ((char *) NULL);
+
+  for (; (c >= string) && OHANA_WHITESPACE(*c); c--);
+  if (c < string) return ((char *)NULL);
+
+  for (; (c >= string) && ISVAR(*c) ; c--);
+  if ((c < string) || (*c != '$')) return ((char *)NULL);
+
+  return (c);
+}
+
+// append string defined by range start - stop to the end of output, which
+// currently has an allocated size of Noutput.  if this operation would overshoot Noutput, 
+// output is reallocated to a sufficiently large size
+char *opihi_append (char *output, int *Noutput, char *start, char *stop) {
+
+  int N1, N2, outlen;
+
+  // a NULL end pointer means 'go to end of line'
+  if (stop == NULL) {
+    stop = start + strlen(start);
+  }
+
+  // enough space?
+  N1 = strlen(output);
+  N2 = stop - start;
+  outlen = N1 + N2;
+  if (outlen >= *Noutput) {
+    *Noutput = outlen + 128;
+    REALLOCATE (output, char, *Noutput);
+    memset (&output[N1], 0, N2 + 128);
+  }
+
+  strncat (output, start, stop - start);
+  return output;
+}
+
+/* replace all instances of \A with A in line */
+void interpolate_slash (char *line) {
+
+  char *in, *out;
+
+  for (in = out = line; *in != 0; in++, out++) {
+    if (*in == 0x5c) in++;
+    *out = *in;
+    if (*in == 0) return;
+  }
+  *out = *in;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/timeformat.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/timeformat.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/timeformat.c	(revision 17422)
@@ -0,0 +1,30 @@
+# include "opihi.h"
+
+int GetTimeFormat (time_t *TimeReference, int *TimeFormat) {
+
+  char *p;
+
+  *TimeReference = 0;
+  if ((p = get_variable ("TIMEREF")) != (char *) NULL) {
+    if (!ohana_str_to_time (p, TimeReference)) {
+      gprint (GP_ERR, "error in TIME_REF format\n");
+      return (FALSE);
+    }
+    free (p);
+  }
+  *TimeFormat = FALSE;
+  if ((p = get_variable ("TIMEFORMAT")) != (char *) NULL) {
+    if (!strcasecmp (p, "JD")) *TimeFormat     = TIME_JD;
+    if (!strcasecmp (p, "MJD")) *TimeFormat    = TIME_MJD;
+    if (!strcasecmp (p, "date")) *TimeFormat   = TIME_DATE;
+    if (!strcasecmp (p, "days")) *TimeFormat   = TIME_DAYS;
+    if (!strcasecmp (p, "hours")) *TimeFormat  = TIME_HOURS;
+    if (!strcasecmp (p, "min")) *TimeFormat    = TIME_MINUTES;
+    if (!strcasecmp (p, "sec")) *TimeFormat    = TIME_SECONDS;
+    if (!*TimeFormat) gprint (GP_ERR, "unknown TIME_FORMAT\n");
+    free (p);
+    return (FALSE);
+  }
+  if (!*TimeFormat) *TimeFormat = TIME_SECONDS;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/lib.shell/version.c	(revision 17422)
@@ -0,0 +1,6 @@
+# include "shell.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *opihi_version () {
+  return (name);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/Makefile	(revision 17422)
@@ -0,0 +1,49 @@
+default: mana
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/mana
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/mana
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# mana user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/mana.$(ARCH).o \
+$(SRC)/findrowpeaks.$(ARCH).o 
+
+cmds = \
+$(SRC)/rawstars.$(ARCH).o \
+$(SRC)/fitcontour.$(ARCH).o \
+$(SRC)/starcontour.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/findpeaks.$(ARCH).o 
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+mana: $(BIN)/mana.$(ARCH)
+$(SRC)/mana.$(ARCH).o : $(libs)
+$(BIN)/mana.$(ARCH)   : $(cmds) $(funcs)
+
+install: $(DESTBIN)/mana help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help
+
+modules: mana.modules
+
+.PHONY: mana
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/adc.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/adc.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/adc.c	(revision 17422)
@@ -0,0 +1,117 @@
+# include "dimm.h"
+
+# define DIGITAL_OUT 0x40
+# define ANALOG_IN_INIT 0x00
+# define ANALOG_IN_HIGH 0xa1
+# define ANALOG_IN_LOW  0x91
+
+static char SerialConnected = FALSE;
+static unsigned char DigitalOutState = 0x00;
+static struct timeval reftime; 
+static char reftimeset = FALSE;
+
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+
+int adc (int argc, char **argv) {
+  
+  struct timeval now;
+  int i, N, mode, hi, lo, value;
+  unsigned char setbit, output[5], *input;
+  double dtime;
+
+  if (N = get_argument (argc, argv, "-reset")) {
+    remove_argument (N, &argc, argv);
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+    return (TRUE);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: adc ai N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc di N (variable)\n");
+    gprint (GP_ERR, "USAGE: adc do N mode\n");
+    return (FALSE);
+  }
+
+  if (!SerialConnected) {
+    if (!SerialInit ('b')) {
+      gprint (GP_ERR, "error opening serial line\n");
+      return (FALSE);
+    }
+    SerialConnected = TRUE;
+  }
+
+  if (!reftimeset) {
+    gettimeofday (&reftime, (struct timeval *) NULL);
+    reftimeset = TRUE;
+  }      
+
+  gettimeofday (&now, (struct timeval *) NULL);
+  dtime = DTIME (now, reftime);
+  set_variable ("TIME", dtime);
+  
+  /* set the digital outputs */
+  if (!strcmp (argv[1], "do")) {
+    N = atof (argv[2]);
+    if ((N < 1) || (N > 6)) {
+      gprint (GP_ERR, "digital output is between 1 and 6\n");
+      return (FALSE);
+    }
+    if (!strcasecmp (argv[3], "on")) {
+      mode = TRUE;
+    } else {
+      mode = FALSE;
+    }
+    setbit = (0x01 << N-1);
+    if (mode) {
+      DigitalOutState |= setbit;
+    } else {
+      DigitalOutState &= ~setbit;
+    }      
+    output[0] = (DIGITAL_OUT | DigitalOutState);
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    gprint (GP_ERR, "%x (%x, %x) -> %x (%d, %d)\n", 
+	     output[0], setbit, DigitalOutState, input[0], 
+	     strlen(input), strlen(output));
+    free (input);
+    return (TRUE);
+  }
+  /* read the analog inputs */
+  if (!strcmp (argv[1], "ai")) {
+    N = atof (argv[2]);
+    if ((N < 2) || (N > 16)) {
+      gprint (GP_ERR, "analog input is between 2 and 16\n");
+      gprint (GP_ERR, " (problem with 1 for the moment...)\n");
+      return (FALSE);
+    }
+    /* init A/D converter */
+    output[0] = (ANALOG_IN_INIT | (N-1));
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    free (input);
+
+    usleep (10000);
+    /* get high byte */
+    output[0] = ANALOG_IN_HIGH;
+    output[1] = 0;
+    do {
+      SerialCommand (output, &input, 50);
+      hi = input[0];
+      free (input);
+    } while (hi & 0x80);
+
+    output[0] = ANALOG_IN_LOW;
+    output[1] = 0;
+    SerialCommand (output, &input, 50);
+    /* gprint (GP_ERR, "%x -> %x\n", output[0], input[0]); */
+    lo = input[0];
+    free (input);
+    value = ((hi & 0x0f) << 8) | lo;
+    if (hi & 0x10) { value = -value; }
+    set_variable (argv[3], 0.0001*value);
+    return (TRUE);
+  }
+
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/demux.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/demux.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/demux.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "dimm.h"
+
+int demux (int argc, char **argv) {
+  
+  int i, j, k, N;
+  float *out, *in, *inptr, **outptr;
+  int nx, ny, Nx, Ny, NX, NY, Nmux, Nbuf;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: demux <buffer> nx ny\n");
+    return (FALSE);
+  }
+
+  if (!SelectBuffer (&Nbuf, argv[1], OLDBUFFER)) return (FALSE);
+
+  nx = atof (argv[2]);
+  ny = atof (argv[3]);
+  Nmux = nx*ny;
+
+  Nx = buffers[Nbuf].matrix.Naxis[0];
+  Ny = buffers[Nbuf].matrix.Naxis[1];
+
+  NX = Nx / nx;
+  NY = Ny / ny;
+
+  inptr = in = (float *) buffers[Nbuf].matrix.buffer;  /* don't lose reference */
+
+  ALLOCATE (out, float, Nx*Ny);
+  ALLOCATE (outptr, float *, Nmux);
+  
+  for (N = i = 0; i < nx; i++) {
+    for (j = 0; j < ny; j++, N++) {
+      outptr[N] = &out[i*NX + j*NX*NY*nx];
+    }
+  }
+
+  for (N = j = 0; j < NY; j++) {
+    for (i = 0; i < NX; i++) {
+      for (k = 0; k < Nmux; k++) {
+	*outptr[k] = *inptr;
+	outptr[k] ++;
+	inptr ++;
+      }
+    }
+    for (k = 0; k < Nmux; k++) outptr[k] += NX;
+  }
+
+  /*
+  for (X = Y = x = y = i = 0; i < NX*NY; i++) {
+    out[X + Y*NX + x*NX*(Y+1) + y*NX*NY*nx] = *in;
+    X++;
+    if (X == NX) {
+      X = 0;
+      Y++;
+    }
+    if (Y == NY) {
+      Y = 0;
+      x++;
+    }
+    if (x == nx) {
+      x = 0;
+      y++;
+    }
+  }
+  */
+
+  free (in);
+  buffers[Nbuf].matrix.buffer = (char *) out;
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/findpeaks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/findpeaks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/findpeaks.c	(revision 17422)
@@ -0,0 +1,106 @@
+# include "mana.h"
+
+int findpeaks (int argc, char **argv) {
+  
+  int i, j, n, N, Nx, Ny;
+  int xo, yo;
+  int Npeak, NPEAK, Npeaks;
+  float *v;
+  int *peaks, *keep, *xp, *yp, *zp;
+  float threshold, vt, vo;
+  Vector *vecx, *vecy, *vecz;
+  Buffer *buf;
+
+  if (argc < 3) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  threshold = atof (argv[2]);
+
+  if ((vecx = SelectVector ("xp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecz = SelectVector ("zp", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+
+  Npeak = 0;
+  NPEAK = Nx;
+  ALLOCATE (xp, int, NPEAK);
+  ALLOCATE (yp, int, NPEAK);
+  ALLOCATE (zp, int, NPEAK);
+
+  /* find peaks for each row */
+  v = (float *) buf[0].matrix.buffer;
+  for (j = 0; j < Ny; j++) {
+    peaks = findrowpeaks (&v[j*Nx], Nx, threshold, &Npeaks);
+
+    if (Npeak + Npeaks >= NPEAK) {
+      NPEAK = Npeak + Npeaks + Nx;
+      REALLOCATE (xp, int, NPEAK);
+      REALLOCATE (yp, int, NPEAK);
+      REALLOCATE (zp, int, NPEAK);
+    }
+    for (i = 0; i < Npeaks; i++) {
+      xp[Npeak + i] = peaks[i];
+      yp[Npeak + i] = j;
+      zp[Npeak + i] = v[peaks[i] + j*Nx];
+    }
+    Npeak += Npeaks;
+    free (peaks);
+  }
+  
+  /* identify non-local peaks */
+  ALLOCATE (keep, int, MAX (Npeak, 1));
+  v = (float *) buf[0].matrix.buffer;
+  for (n = 0; n < Npeak; n++) {
+    xo = xp[n];
+    yo = yp[n];
+    vo = v[xo + yo*Nx];
+    keep[n] = TRUE;
+    for (i = xo - 1; i <= xo + 1; i++) {
+      if (i < 0) continue;
+      if (i >= Nx) continue;
+      for (j = yo - 1; j <= yo + 1; j++) {
+	if ((i == xo) && (j == yo)) continue;
+	if (j < 0) continue;
+	if (j >= Ny) continue;
+	vt = v[i + j*Nx];
+	if (vt > vo) {
+	  keep[n] = FALSE;
+	  goto next_peak;
+	}
+      }
+    }
+  next_peak:
+    continue;
+  }
+
+  REALLOCATE (vecx[0].elements, float, MAX (Npeak, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Npeak, 1));
+  REALLOCATE (vecz[0].elements, float, MAX (Npeak, 1));
+  /* eliminate non-local peaks */
+  for (N = n = 0; n < Npeak; n++) {
+    if (!keep[n]) continue;
+    vecx[0].elements[N] = xp[n];
+    vecy[0].elements[N] = yp[n];
+    vecz[0].elements[N] = zp[n];
+    N ++;
+  }
+  free (xp);
+  free (yp);
+  free (zp);
+  free (keep);
+
+  REALLOCATE (vecx[0].elements, float, MAX (N, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (N, 1));
+  REALLOCATE (vecz[0].elements, float, MAX (N, 1));
+  vecx[0].Nelements = vecy[0].Nelements = vecz[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "findpeaks (buffer) (threshold)\n");
+  return (FALSE);
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/findrowpeaks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/findrowpeaks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/findrowpeaks.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "mana.h"
+
+int *findrowpeaks (float *row, int Nrow, float threshold, int *npeaks) {
+  
+  int i;
+  int Npeaks, NPEAKS;
+  int *peaks;
+
+  Npeaks = 0;
+  NPEAKS = 100;
+  ALLOCATE (peaks, int, NPEAKS);
+
+  if (Nrow < 3) {
+    *npeaks = Npeaks;
+    return (peaks);
+  }
+    
+  /* special case for first pixel in row */
+  if ((row[0] > row[1]) && (row[0] > threshold)) {
+    peaks[Npeaks] = 0;
+    Npeaks ++;
+  }    
+
+  for (i = 1; i < Nrow - 1; i++) {
+    if (row[i] < threshold) continue;
+    if (row[i] < row[i-1]) continue;
+    if (row[i] <= row[i+1]) continue;
+
+    peaks[Npeaks] = i;
+    Npeaks ++;
+    if (Npeaks >= NPEAKS) {
+      NPEAKS += 100;
+      REALLOCATE (peaks, int, NPEAKS);
+    }
+  }      
+
+  /* special case for last pixel in row */
+  if ((row[Nrow-1] >= row[Nrow-2]) && (row[Nrow-1] > threshold)) {
+    peaks[Npeaks] = Nrow-1;
+    Npeaks ++;
+  }    
+
+  *npeaks = Npeaks;
+  return (peaks);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/fitcontour.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/fitcontour.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/fitcontour.c	(revision 17422)
@@ -0,0 +1,139 @@
+# include "mana.h"
+# define NTERM 3
+
+int fitcontour (int argc, char **argv) {
+  
+  int i;
+  double **C, **B;
+  float cs1, sn1, cs, sn, x, y, r, xo, yo;
+  float dR, Rmin, Rmaj, Theta, Rx, Ry, Rxy, R1, R2, R3;
+  Vector *vecx, *vecy;
+
+  /* USAGE fitcontour x y Xo Yo */
+  if (argc < 5) goto usage;
+
+  if ((vecx = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  xo = atof (argv[3]);
+  yo = atof (argv[4]);
+
+  ALLOCATE (B, double *, NTERM);
+  ALLOCATE (C, double *, NTERM);
+  for (i = 0; i < NTERM; i++) {
+    ALLOCATE (C[i], double, NTERM);
+    bzero (C[i], NTERM*sizeof(double));
+    ALLOCATE (B[i], double, 1);
+    bzero (B[i], sizeof(double));
+  }
+
+  /* we are fitting r = ro + rs*sin(2theta) + rc*cos(2theta) */
+  /* sin(2t) = 2cos(t)sin(t)
+     cos(2t) = cos^2(t) - sin^2(t)
+  */
+  for (i = 0; i < vecx[0].Nelements; i++) {
+    x = vecx[0].elements[i] - xo;
+    y = vecy[0].elements[i] - yo;
+    r = hypot (x, y);
+
+    /* calculate sin(2t), cos(2t) using 1/2 angle tri relationship above */
+    sn1 = y / r;
+    cs1 = x / r;
+    sn = 2*sn1*cs1;
+    cs = cs1*cs1 - sn1*sn1;
+
+    C[0][0] += 1.0;
+    C[1][0] += sn;
+    C[1][1] += SQ(sn);
+    C[2][0] += cs; 
+    C[2][1] += cs*sn; 
+    C[2][2] += SQ(cs);
+
+    B[0][0] += r;
+    B[1][0] += r*sn;
+    B[2][0] += r*cs; 
+  }
+  C[0][1] = C[1][0];
+  C[0][2] = C[2][0];
+  C[1][2] = C[2][1];
+    
+  dgaussjordan (C, B, NTERM, 1);
+  
+  /** this is somewhat weak: if the object is too elongated, Rmin can be < 0 **/
+  dR = hypot (B[1][0], B[2][0]);
+  Rmaj = B[0][0] + dR;
+  Rmin = B[0][0] - dR;
+  Theta = DEG_RAD*atan2 (B[1][0], B[2][0]) / 2;
+
+  sn = B[1][0] / dR;
+  cs = B[2][0] / dR;
+
+  R1 = SQ(Rmaj) + SQ(Rmin);
+  R2 = SQ(Rmaj) - SQ(Rmin);
+  R3 = Rmaj*Rmin;
+
+  Rx = R3 / sqrt (R1 - R2*cs);
+  Ry = R3 / sqrt (R1 + R2*cs);
+  Rxy = -sn*R2 / SQ(R3);
+
+  set_variable ("Rx", Rx);
+  set_variable ("Ry", Ry);
+  set_variable ("Rxy", Rxy);
+
+  set_variable ("Rmin", Rmin);
+  set_variable ("Rmaj", Rmaj);
+  set_variable ("Theta", Theta);
+
+  for (i = 0; i < NTERM; i++) {
+    free (B[i]);
+    free (C[i]);
+  }
+  free (B);
+  free (C);
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "fitcontour x y (xo) (yo)\n");
+  return (FALSE);
+}
+
+
+/* this routine fits a single sine and cosine to the value of dr as a function of the angle around the central
+ * point, theta.  this is NOT an exact fit, an is increasingly in error for more eccentric ellipses.  however,
+ * it does a reasonable job of approximating the value of the major and minor axes, and it gets the angle of
+ * orientation right at well.  We then assume the values of major and minor axis and angle are correct to get
+ * the parameters for the elliptical gaussian fit:
+ 
+ z = (x^2) / (2 Rx^2) + (y^2) / (2 Ry^2) + Rxy x y
+
+ the functions above give the correct relationship between these:
+
+   R1 = SQ(Rmaj) + SQ(Rmin);
+   R2 = SQ(Rmaj) - SQ(Rmin);
+   R3 = Rmaj*Rmin;
+
+   Rx = R3 / sqrt (R1 - R2*cs);
+   Ry = R3 / sqrt (R1 + R2*cs);
+   Rxy = -sn*R2 / SQ(R3);
+
+ to derive these relationships, write the equation for an ellipse with major axis in the x-dir and minor in
+ the y-dir:
+
+ z = (x^2) / (2 Rmaj^2) + (y^2) / (2 Rmin^2)
+
+ apply the rotation matrix:
+
+ (x)' = (cos, -sin)(x,y)
+ (y)' = (sin, +cos)(x,y)  (modulo the sign on the sine)
+
+ then group the x^2 terms, the y^2 terms, and the xy terms to find the three coeffs.
+
+ NOTE: I spent a while having trouble deriving this from the polar form of an ellipse:
+
+ x = Rmaj*cos(theta)
+ y = Rmin*sin(theta)
+
+ it turns out that 'theta' above is NOT the angle (0,1)-(0,0)-(x,y). rather, it should be viewed as a
+ paraterization of the ellipse.  
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/focus.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/focus.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/focus.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "dimm.h"
+
+int focus (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "focus on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "pos")) {
+
+    int status, servo, angle;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: focus pos Nservo (angle)\n");
+      return (FALSE);
+    }
+
+    servo = atoi (argv[2]);
+    angle = atoi (argv[3]);
+
+    sprintf (line, "%c%c%c\n", 0xff, servo, angle);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "raw")) {
+
+    int status, value;
+    char *answer = (char *) NULL;
+    char line[64];
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: focus raw value\n");
+      return (FALSE);
+    }
+
+    value = atoi (argv[2]);
+
+    sprintf (line, "%c", value);
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "focus init port\n");
+  gprint (GP_ERR, "focus pos (string)\n");
+  gprint (GP_ERR, "focus raw (value)\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/init.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "opihi.h"
+
+int findpeaks	    PROTO((int, char **));
+int fitcontour	    PROTO((int, char **));
+int starcontour	    PROTO((int, char **));
+int rawstars	    PROTO((int, char **));
+int version	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "findpeaks",   findpeaks,    "find image peaks"},
+  {1, "fitcontour",  fitcontour,   "fit ellipse contour"},
+  {1, "starcontour", starcontour,  "object contour"},
+  {1, "rawstars",    rawstars,     "find raw star stats"},
+  {1, "version",     version,      "show version information"},
+}; 
+
+void InitMana () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreeMana () {
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/mana.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/mana.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/mana.c.in	(revision 17422)
@@ -0,0 +1,69 @@
+# include "mana.h"
+
+# define opihi_name "MANA"
+# define opihi_prompt "mana: "
+# define opihi_description "an image manipulation tool\n"
+# define opihi_history ".mana"
+# define opihi_rcfile ".manarc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitMana ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+  
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreeMana ();
+
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/rawstars.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/rawstars.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/rawstars.c	(revision 17422)
@@ -0,0 +1,95 @@
+# include "mana.h"
+
+int rawstars (int argc, char **argv) {
+  
+  int i, x, y, N, Nx, Ny, Np;
+  float *v;
+  double Raper, Rinner, Router;
+  Vector *xp, *yp;
+  Vector *xc, *yc, *sx, *sy, *sxy, *zs, *zc, *sk;
+  Buffer *buff;
+
+  Raper = 5;
+  if ((N = get_argument (argc, argv, "-Raper"))) {
+    remove_argument (N, &argc, argv);
+    Raper = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Rinner = 10;
+  if ((N = get_argument (argc, argv, "-Rinner"))) {
+    remove_argument (N, &argc, argv);
+    Rinner = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  Router = 15;
+  if ((N = get_argument (argc, argv, "-Router"))) {
+    remove_argument (N, &argc, argv);
+    Router = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 4) goto usage;
+
+  if ((buff = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((xp = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yp = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (xp[0].Nelements != yp[0].Nelements) {
+    gprint (GP_ERR, "vectors are not the same length\n");
+    return (FALSE);
+  }
+
+  set_rough_radii (Raper, Rinner, Router);
+
+  /* output vectors */
+  if ((xc = SelectVector ("xc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yc = SelectVector ("yc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zc = SelectVector ("zc", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((zs = SelectVector ("zs", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sk = SelectVector ("sk", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sx = SelectVector ("sx", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sy = SelectVector ("sy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((sxy = SelectVector ("sxy", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buff[0].matrix.Naxis[0];
+  Ny = buff[0].matrix.Naxis[1];
+  Np = xp[0].Nelements;
+
+  REALLOCATE (xc[0].elements, float, Np);
+  REALLOCATE (yc[0].elements, float, Np);
+  REALLOCATE (sx[0].elements, float, Np);
+  REALLOCATE (sy[0].elements, float, Np);
+  REALLOCATE (sxy[0].elements, float, Np);
+  REALLOCATE (zs[0].elements, float, Np);
+  REALLOCATE (zc[0].elements, float, Np);
+  REALLOCATE (sk[0].elements, float, Np);
+  xc[0].Nelements = yc[0].Nelements = sx[0].Nelements = Np;
+  sy[0].Nelements = zs[0].Nelements = zc[0].Nelements = Np;
+  sxy[0].Nelements = sk[0].Nelements = Np;
+
+  v = (float *) buff[0].matrix.buffer;
+  for (i = 0; i < Np; i++) {
+    x = xp[0].elements[i];
+    y = yp[0].elements[i];
+    if (x < 0) continue;
+    if (x >= Nx) continue;
+    if (y < 0) continue;
+    if (y >= Ny) continue;
+
+    get_rough_star (v, Nx, Ny, x, y, 
+		    &xc[0].elements[i], 
+		    &yc[0].elements[i], 
+		    &sx[0].elements[i], 
+		    &sy[0].elements[i], 
+		    &sxy[0].elements[i], 
+		    &zs[0].elements[i], 
+		    &zc[0].elements[i],
+		    &sk[0].elements[i]);
+  }
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "rawstars (buffer) (xp) (yp)\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/simsignal.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/simsignal.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/simsignal.c	(revision 17422)
@@ -0,0 +1,50 @@
+# include "dimm.h"
+
+int simsignal (int argc, char **argv) {
+  
+  int Nvect, Nbin, Nbit, i, ivalue, jvalue, Nshift, mask, scale;
+  float *buf;
+  double cvalue, dvalue, sigma, SN, period;
+  double rnd_gauss ();
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: simsignal (vector) Nbin Nbits period\n");
+    return (FALSE);
+  }
+
+  if (!SelectVector (&Nvect, argv[1], ANYVECTOR)) return (FALSE);
+  Nbin = atof (argv[2]);
+  Nbit = atof (argv[3]);
+  /* SN = atof (argv[4]); */
+  period = atof (argv[4]);
+
+  vectors[Nvect].Nelements = Nbin;
+  REALLOCATE (vectors[Nvect].elements, float, vectors[Nvect].Nelements);
+
+  scale = (0x1 << Nbit) - 1;
+
+  buf = vectors[Nvect].elements;
+  for (i = 0; i < Nbin; i++, buf++) {
+    ivalue = scale * 0.5 * (sin (i*2*M_PI/period) + 1) + 0.5;
+    /*
+    dvalue = rnd_gauss (cvalue, sigma);
+    cvalue = (dvalue + range) / (2.0*range);
+    dvalue = MAX (0, MIN (0.99999, cvalue));
+    ivalue = scale * dvalue;
+    *buf = (2.0 * range * ((double) ivalue + 0.5)) / scale - range; 
+    */
+    *buf = ivalue;
+  }
+
+  return (TRUE);
+}
+
+/* 
+
+8 bit = 2^8
+
+  gauss_init (2*scale);
+  sigma = 2.0 / SN;
+  range = 1 + 5*sigma;
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/starcontour.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/starcontour.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/starcontour.c	(revision 17422)
@@ -0,0 +1,130 @@
+# include "mana.h"
+
+int starcontour (int argc, char **argv) {
+  
+  int x, y, xs, xp, yp;
+  int Nx, Ny, N, Npts, min, max;
+  float *v;
+  float zt, zo, xmin, xmax;
+  Vector *vecx, *vecy;
+  Buffer *buf;
+
+  if (argc < 5) goto usage;
+
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  xp = atof (argv[2]);
+  yp = atof (argv[3]);
+  zo = atof (argv[4]);
+
+  if ((vecx = SelectVector ("xo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector ("yo", ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  N = 0;
+  Npts = 100;
+  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+
+  Nx = buf[0].matrix.Naxis[0];
+  Ny = buf[0].matrix.Naxis[1];
+  v = (float *)buf[0].matrix.buffer;
+
+  /* find transition below (limit range?) */
+  xmin = xmax = 0;
+  xs = xp;
+  for (y = yp; (y >= 0) && (v[xs + y*Nx] > zo); y--) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmin;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmax;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+
+  /* find transition above (limit range?) */
+  xs = xp;
+  for (y = yp; (y < Ny) && (v[xs + y*Nx] > zo); y++) {
+    /* find transition below (limit range?) */
+    min = max = FALSE;
+    for (x = xs; (x >= 0) && !min; x--) {
+      min = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	min = TRUE;
+	xmin = x + (zo - zt)/(v[x + 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmin;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    /* find transition above (limit range?) */
+    for (x = xs; (x < Nx) && !max; x++) {
+      max = FALSE;
+      zt = v[x + y*Nx];
+      if (zt < zo) {
+	max = TRUE;
+	xmax = x - (zo - zt)/(v[x - 1 + y*Nx] - zt);
+	vecx[0].elements[N] = xmax;
+	vecy[0].elements[N] = y;
+	N ++;
+	if (N >= Npts) {
+	  Npts += 100;
+	  REALLOCATE (vecx[0].elements, float, MAX (Npts, 1));
+	  REALLOCATE (vecy[0].elements, float, MAX (Npts, 1));
+	}
+      }
+      /* ignore edge cases? */
+    }
+    if (min && max) {
+      xs = 0.5*(xmin + xmax);
+    }
+  }
+  vecx[0].Nelements = N;
+  vecy[0].Nelements = N;
+
+  return (TRUE);
+
+ usage:
+  gprint (GP_ERR, "starcontour (buffer) (xpeak) (ypeak) (level)\n");
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/tests.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/tests.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/tests.c	(revision 17422)
@@ -0,0 +1,108 @@
+# include "opihi.h"
+
+run_tests () {
+
+  /* test1 ();
+     test1b ();
+     test2 ();
+     test3 (); */
+  test4 (); 
+  test5 ();
+}
+
+test1 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1\n");
+  sleep (1);
+}
+
+test1b () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 1b...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 1b\n");
+  sleep (1);
+}
+
+test2 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 2...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * 100");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 2\n");
+  sleep (1);
+}
+
+test3 () {
+
+  int i, size;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 3...  ");
+  ALLOCATE (test, char, 256);
+  sprintf (test, "5 * dsin(45)");
+  for (i = 0; i < 1000000; i++) {
+    line = dvomath (1, &test, &size, 0);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 3\n");
+  sleep (1);
+}
+
+test4 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 4...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "$N = 100");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 4\n");
+  sleep (1);
+}
+
+test5 () {
+
+  int i;
+  char *test, *line;
+
+  gprint (GP_ERR, "starting test 5...  ");
+  for (i = 0; i < 1000000; i++) {
+    ALLOCATE (test, char, 256);
+    sprintf (test, "echo {100 * 800}");
+    line = parse (test);
+    if (line != NULL) free (line);
+  }
+  gprint (GP_ERR, "done with test 5\n");
+  sleep (1);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/mana/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/mana/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/mana/version.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "mana.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "mana version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "libohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "libfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/modules/dvo/navigate
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/modules/dvo/navigate	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/modules/dvo/navigate	(revision 17422)
@@ -0,0 +1,623 @@
+# -*- perl -*-
+
+macro navigate
+  style -n 0
+  limits
+  $DRAWSTARS  = -1
+  $DRAWIMAGES =  1
+  $DRAWGRID   = -1
+  $ZOOM = 180 / ($YMAX - $YMIN)
+  #this should be changed to a while loop, except 'while' is broken for some reason
+  $KEY = "none"
+  while ("$KEY" != "q")
+    cursor -g 1
+
+    # help list
+    if ("$KEY" == "h")
+     echo "Arrow Keys - pan in that direction"
+     echo "PgUp,PgDn - zoom in/out a factor of 1.2"
+     echo "Home,End  - zoom in/out a factor of 2"
+     echo "1 - zoom in factor of 2 at the cursor"
+     echo "2 - zoom in factor of 1.2 at the cursor"
+     echo "3 - recenter at cursor"
+     echo "4 - zoom out factor of 1.2 at the cursor"
+     echo "5 - zoom out factor of 2 at the cursor"
+     echo "6 - zoom out factor of 10 at the cursor"
+     echo "z - zoom to radius (requires 2nd keystroke)"
+     echo "f - show full sky"
+     echo ""
+     echo "q - quit"
+     echo "S - toggle auto-plotting of stars"
+     echo "A - toggle auto-plotting of image borders"
+     echo "g - toggle skygrid on/off"
+     echo "c - plot status catalog boundaries"
+     echo "C - list catalog at cursor location"
+     echo "i - list info about images touching cursor location" 
+     echo "I - list info about images, with pixel coords of cursor position"
+     echo "j - adjust mag scale, +0.5"
+     echo "k - adjust mag scale, -0.5"
+     echo "J - adjust dmag scale, /1.25"
+     echo "K - adjust dmag scale, *1.25"
+     echo "r - plot detected asteroids (rocks)"
+     echo "l - plot HST GSC"
+     echo "L - plot Landolt stars"
+     echo "m - list measurements for stars within 1 pixel of cursor"
+     echo "M - list measurements for stars within 1.8 arcsec of cursor"
+     echo "p - ****(don't know what this does)"
+     echo "s - ****(don't know what this does)"
+     echo "t - plot light curve for star within 2 arcsec of cursor position"
+     echo "T - plot 'galaxy' light curve for star within 2 arcsec of cursor position"
+     echo "u - ****(don't know what this does)"
+     echo "x - plot stars scaled by magnitude Chisq"
+     echo "X - plot stars by magnitude scatter"
+     echo "y - ****(don't know what this does)"
+     echo ""
+     echo "@ - execute macro `user_macro`"
+     echo ": - ****input a line and execute (not yet implemented)"
+    end
+
+    # quit from navigate
+    if ("$KEY" == "q") continue
+
+    #pan controls
+    if (("$KEY" == "Left") || ("$KEY" == "Right") || ("$KEY" == "Up") || ("$KEY" == "Down"))
+      $SHIFT = 0.2
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      #assumes standard sky orientation!! (N up, E left)
+      if ("$KEY"=="Left")
+        $R$KEY = $R$KEY + $SHIFT*$XMAX
+      end
+      if ("$KEY"=="Right")
+        $R$KEY = $R$KEY + $SHIFT*$XMIN
+      end
+      if ("$KEY"=="Up")
+        $D$KEY = $D$KEY + $SHIFT*$YMAX
+      end
+      if ("$KEY"=="Down")
+        $D$KEY = $D$KEY + $SHIFT*$YMIN
+      end
+      #pretend like I hit '3' in the place to recenter it
+      nav_zoom 1      
+    end
+
+    # NEW zoom controls
+    if (("$KEY" == "Prior") || ("$KEY" == "Next") || ("$KEY" == "Home") || ("$KEY" == "End") || ("$KEY" == "Button4") || ("$KEY" == "Button5"))    
+      #move where key was hit to center      
+      $R$KEY  = $RMAX-$XMAX  
+      $D$KEY  = $DMAX-$YMAX
+      if ("$KEY" == "Prior")
+        $zfac=1.2
+      end
+      if ("$KEY" == "Next")
+        $zfac={1/1.2}
+      end
+      if ("$KEY" == "Home")
+        $zfac=2
+      end
+      if ("$KEY" == "End")
+        $zfac={1/2.}
+      end
+      if ("$KEY"=="Button4")
+        $zfac=1.6
+      end
+      if ("$KEY"=="Button5")
+        $zfac={1/1.6}
+      end
+      nav_zoom $zfac
+    end
+
+    if ("$KEY"=="Button1")
+      nav_zoom 1
+    end
+    if ("$KEY"=="Button2")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY"=="Button3")
+      nav_zoom 2
+    end
+
+
+
+    # zoom controls
+    if ("$KEY" == "1")
+      nav_zoom 2
+    end
+    if ("$KEY" == "2")
+      nav_zoom 1.2
+    end
+    if ("$KEY" == "3")
+      nav_zoom 1
+    end
+    if ("$KEY" == "4")
+      nav_zoom {1/1.2}
+    end
+    if ("$KEY" == "5")
+      nav_zoom {1/2.}
+    end
+    if ("$KEY" == "6")
+      nav_zoom {1/20.}
+    end
+
+ 
+   # measure distance
+    if ("$KEY" == "d")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'd' again at endpoint"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = 3600*((dcos($d0)*($r0-$r1))^2 + ($d0-$d1)^2)^0.5
+      echo "$dr arcsec"
+    end
+    # show ra, dec
+    if ("$KEY" == "w")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      echo "$tmp $D$KEY" 
+      exec echo $tmp $D$KEY | radec -hh
+    end
+    # zoom to radius
+    if ("$KEY" == "z")
+      $r0 = $R$KEY
+      $d0 = $D$KEY
+      $ok = $KEY
+      echo "type 'z' again at radius"
+      cursor -g 1
+      $r1 = $R$KEY
+      $d1 = $D$KEY
+      $dr = (($r0-$r1)^2 + ($d0-$d1)^2)^0.5
+      $ZOOM = $RAD / $dr
+      nav_recenter
+      nav_redraw
+      $KEY = $ok
+      $R$KEY = $r0
+      $D$KEY = $d0
+    end
+
+    # adjust mag scaling
+    if ("$KEY" == "J")
+      $MAG = $MAG - 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "K")
+      $MAG = $MAG + 0.5
+      nav_redraw
+    end
+    if ("$KEY" == "j")
+      $dMAG = $dMAG * 0.8
+      nav_redraw
+    end
+    if ("$KEY" == "k")
+      $dMAG = $dMAG * 1.25
+      nav_redraw
+    end
+    echo "mag, dmag: $MAG, $dMAG"
+
+
+
+    # plot full sky
+    if ("$KEY" == "f") 
+      echo "full"
+      $ZOOM = 1
+      resize 1150 600		      
+      region 0 0 90 ait
+      $RMIN = 0
+      $RMAX = 360
+      $DMIN = -90
+      $DMAX = +90
+      style -c red; cgrid
+      style -c black
+      images
+    end
+
+    # plot rocks
+    if ("$KEY" == "r") 
+#      plot.rocks
+      style -c blue   -pt 1; procks -speed 0.0041 1
+      style -c red    -pt 1; procks -speed 0.00041 0.0041
+      style -c indigo -pt 1; procks -speed 0 0.00041
+      style -c black -lw 0;
+    end
+    # plot HST-GSC
+    if ("$KEY" == "l") 
+      style -c blue -pt 7; cat -all -g -m 9 16
+      style -c black
+    end
+    # plot Landolt
+    if ("$KEY" == "L") 
+#      style -c red  -lw 2 -pt 3; cat -a 1 2 3 /data/elixir/srcdir/refs/stetson/stetsonBn.txt -m 9 18
+#      style -c blue -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.fix -m 9 18
+      style -c red -lw 2 -pt 7; cat -a 1 2 3 /data/elixir/srcdir/refs/sdss/g_SDSS.dat -m 9 14
+#      style -c red -lw 2 -pt 3; cat -a 25 26 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.hq -m 9 18
+#      style -c red -lw 2 -pt 3; cat -a 22 23 8 /data/elixir/srcdir/refs/landolt/new/Landolt92.unfix -m 9 18
+#      style -c blue -lw 2 -pt 7; cat -a 1 2 4 /data/elixir/srcdir/refs/landolt/extreme/extreme.match -m 0 20
+#      style -x 2 -c red -pt 7 ; cplot RA DEC
+      style -c black -lw 0
+    end
+
+    # list star measurements
+    if ("$KEY" == "m") 
+        $dR = $RAD/$ZOOM/300
+        if ($dR < 0.0005)
+	 $dR = 0.0005
+        end
+	gstar $R$KEY $D$KEY $dR -m
+    end
+
+    # plot mag residuals
+    if ("$KEY" == "R") 
+      echo "filter: "
+      cursor 1
+      clear -n 1 -s; lim 10 22 -0.2 0.2; clear; box
+      dmags $KEY\:rel - $KEY : $KEY -type 0
+      plot -x 2 -pt 0 -sz 0.3 -c red yv xv
+      dmags $KEY\:rel - $KEY : $KEY -type 0 -flag 0 -nphot +3 -chisq 2.0
+      plot -x 2 -pt 2 -sz 0.5 -c black yv xv
+      $KEY = R
+      style -n 0
+    end
+
+    if ("$KEY" == "M") 
+	gstar $R$KEY $D$KEY 0.0005 -m
+    end
+    # list images
+    if ("$KEY" == "i") 
+	gimages $R$KEY $D$KEY
+    end
+    if ("$KEY" == "I") 
+	gimages $R$KEY $D$KEY -pix
+    end
+
+    #toggle images on / off
+    if ("$KEY" == "A")
+      $DRAWIMAGES = $DRAWIMAGES * -1
+      if ($DRAWIMAGES == 1)
+        images
+      end
+    end
+    # toggle stars on / off
+    if ("$KEY" == "S")
+      $DRAWSTARS = $DRAWSTARS * -1
+      if (($ZOOM > 20) && ($DRAWSTARS == 1))
+       style -pt 7
+       pmeasure -all -m $MAG {$MAG + $dMAG}
+      end
+    end
+    # turn grid on / off
+    if ("$KEY" == "g")
+      $DRAWGRID = $DRAWGRID * -1
+      if (($ZOOM > 20) && ($DRAWGRID==1))
+        style -c black; cgrid
+      end
+      if (($ZOOM > 20) && ($DRAWGRID==-1))
+        nav_redraw
+      end
+    end
+
+    # plot light-curve interactive
+    if ("$KEY" == "t")
+      style -n 1 -pt 2 -x 2
+      clear
+      if ($R$KEY < 0) 
+       $R$KEY = $R$KEY + 360
+      end
+      lcurve -l $R$KEY $D$KEY {30/3600} -d -v time mag
+      box
+      lcv
+      style -n 0
+    end
+    # plot light-curve 
+    if ("$KEY" == "T")
+      style -n 1 -pt 1 -c red -x 2
+      lcurve $R$KEY $D$KEY {30/3600} -d
+      style -c black
+      style -n 0 
+    end
+    # plot catalogs
+    if ("$KEY" == "c")
+      style -c blue; pcat; style -c black
+    end
+    # list catalogs
+    if ("$KEY" == "C")
+      gcat $R$KEY $D$KEY
+    end
+
+    # plot image chisqs
+    if ("$KEY" == "x") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME Xm -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c blue
+       czplot ra dec Xm 3 30
+       style -c black -pt 1
+    end
+    # plot meas errors
+    if ("$KEY" == "X") 
+       gcat $R$KEY $D$KEY
+       extract $CATNAME dM -photcode R
+       extract $CATNAME ra
+       extract $CATNAME dec
+       style -x 2 -pt 7 -c red
+       czplot ra dec dM 0 30
+       style -c black -pt 1
+    end
+
+
+    # temp plot for skyprobe
+    if ("$KEY" == "u") 
+      imextract -region time
+      imextract -region mcal
+      imextract -region airmass
+      imextract -region nstar
+      vstat time
+      clear -n 1;
+      section a 0 0.00 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} -0.8 -0.5; box; plot time mcal
+      section b 0 0.33 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3}  0.95 3.0; box; plot time airmass
+      section c 0 0.66 1 0.33
+      lim {$MEDIAN-0.3} {$MEDIAN+0.3} 0 3000; box; plot time nstar
+      style -n 0
+    end
+    if ("$KEY" == "s")
+      $tmp = $R$KEY
+      if ($tmp < 0) 
+        $tmp = $R$KEY + 360.0
+      end
+      $line = `echo $tmp $D$KEY | radec -hh`
+      imextract -region photcode
+      imextract -region time
+     
+      subset t = time if (int(photcode/100) == 1)
+      uniq t T
+      $Bn = t[]
+      $BN = T[]
+      
+      subset t = time if (int(photcode/100) == 2)
+      uniq t T
+      $Vn = t[]
+      $VN = T[]
+      
+      subset t = time if (int(photcode/100) == 3)
+      uniq t T
+      $Rn = t[]
+      $RN = T[]
+      
+      subset t = time if (int(photcode/100) == 4)
+      uniq t T
+      $In = t[]
+      $IN = T[]
+     
+      echo "$line  $Bn $BN  $Vn $VN  $Rn $RN  $In $IN"
+    end
+
+    if ("$KEY" == "p") 
+      echo "P - new coords; p - old coords"
+      cursor -g 1
+      exec echo $Rp $Dp $RP $DP >> fix.coords
+    end
+
+    if ("$KEY" == "y")
+      ccd I - 2MASS_J : 2MASS_J - 2MASS_K
+      lim -n 1 -1 10 -1 3; clear; box; plot -x 2 -pt 2 -sz 0.5 xv yv
+      dev -n 0 -g
+    end
+
+    #  User-defined macro
+    if ("$KEY" == "at")
+      user_macro
+    end
+
+    if ("$KEY" == "colon")
+      #make this work similar to ':' in vi or iraf
+      #does not work correctly now.
+      scan stdin line
+      $line
+    end
+
+  end
+end
+
+#define this so navigate doesn't crash if you try to call it.
+#If you define a user_macro, be sure to do so AFTER this in .dvorc.
+macro user_macro
+  #echo "success!"
+  $do_nothing=0
+end
+    
+
+macro nav_zoom
+  $ZOOM = $ZOOM * $1
+  nav_recenter
+  nav_redraw
+  $Rnum = $R$KEY		      
+  $Dnum = $D$KEY
+  $KEY = num
+end
+
+macro nav_recenter
+  region $R$KEY $D$KEY {$RAD/$ZOOM} sin
+  #assumes standard sky orientation!! (N up, E left)
+  $RMIN = $R$KEY + $XMIN
+  $RMAX = $R$KEY + $XMAX
+  $DMIN = $D$KEY + $YMIN
+  $DMAX = $D$KEY + $YMAX
+end
+
+macro nav_redraw
+  clear
+  if ($ZOOM <= 20) 
+    style -c red; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWGRID==1))
+    style -c black; cgrid
+  end
+  if (($ZOOM > 20) && ($DRAWSTARS == 1))
+    pmeasure -all -m $MAG {$MAG + $dMAG}
+  end    
+  style -c black
+  if ($DRAWIMAGES == 1)
+    images
+  end
+end
+
+
+
+#==================================================
+#=================   END BSNAV   ==================
+#==================================================
+
+
+macro sigclip
+  if ("$0" == "1")
+    echo ""
+    echo "sigclip <clipvector> <N_iterations> <N_sigma> [other vectors ..]"
+    echo ""
+  end
+
+  #required parameters
+  $CLIPVECT = $1
+  $NITERATE = $2
+  $NSIGCLIP = $3
+  
+  for i 0 $NITERATE
+    vstat -q $CLIPVECT
+    #clip boundaries
+    $top = $MEAN + ($NSIGCLIP*$SIGMA)
+    $bot = $MEAN - ($NSIGCLIP*$SIGMA)
+    
+    #clip it good.
+    subset temp = $CLIPVECT if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+    
+    #if you specify other vectors, clip the same elements from them too.
+    #they must all be the same length, of course!!
+    if ($0>4)
+      for j 4 $0
+        subset $$j = $$j if (($CLIPVECT < $top) && ($CLIPVECT > $bot))
+      end
+    end
+    
+    #copy temp back to $CLIPVECT and reiterate!
+    delete $CLIPVECT
+    concat temp $CLIPVECT  
+  end
+end
+
+
+macro binvec
+  if ("$0" == "1")
+    echo ""
+    echo "binvec <vec> <Nbins> [other vectors...]"
+    echo ""
+    echo "Bin the vector 'vec' into Nbin bins.  Listing other vectors will"
+    echo "put the corresponding elements of those into other vectors which"
+    echo "are the subset of the vector in that bin.  (That can probably be"
+    echo "stated better.)  This macro makes lots of new vectors.  Hooray!"
+    echo ""
+    echo "Creates"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $NBINS   = $2
+
+  vstat -q $binvect
+  $step = ($MAX-$MIN)/$NBINS
+  $vmin = $MIN
+  $vmax = $MAX
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>3)
+      for j 3 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
+
+macro binvec.2
+  if ("$0" == "1")
+    echo ""
+    echo "binvec.2 <vec> <min> <max> <binsize> [other vectors...]"
+    echo ""
+    echo ""
+    echo "see also 'binvec'"
+  end
+
+  #REQUIRED PARAMS
+  $binvect = $1
+  $vmin    = $2
+  $vmax    = $3
+  $step    = $4
+
+  $NBINS = ($MAX-$MIN)/$step
+
+  delete -q $binvect\_bins
+  delete -q $binvect\_num
+  for i 1 {$NBINS+1}
+    $top = $vmin + ( $i   *$step)
+    $bot = $vmin + (($i-1)*$step)
+    #      sightly different behavior for last bin    -------v
+    if ($i != $NBINS)
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect< $top))
+    else
+      subset temp = $binvect if (($binvect>=$bot)&&($binvect<=$top))
+    end
+    set $binvect\_bin$i = temp
+    set temp2 = temp
+    delete temp
+    #if you specify other vectors, grab the same elements from them too.
+    #they must all be the same length, of course!!
+
+    if ($0>5)
+      for j 5 $0
+        if ($i != $NBINS)
+          subset temp = $$j if (($binvect>=$bot)&&($binvect< $top))
+        else 
+          subset temp = $$j if (($binvect>=$bot)&&($binvect<=$top))
+        end
+        set $$j\_bin$i = temp
+        delete temp
+      end
+    end
+
+
+    concat {$bot+($step/2)} $binvect\_bins
+    concat temp2[] $binvect\_num
+    #dvo didn't like me saying 'concat $binvect\_bin$i[] $binvect\_num
+    delete temp2
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/modules/mana/plots
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/modules/mana/plots	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/modules/mana/plots	(revision 17422)
@@ -0,0 +1,41 @@
+
+macro cvplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  cplot _tmpr _tmpd
+  style -pt 7
+end
+
+macro vplot 
+  if ($0 != 6)
+    echo "USAGE: cvplot r d ur ud scale"
+    break
+  end
+ 
+  local i
+  delete -q _tmpr _tmpd
+
+  for i 0 $1[]
+   concat $1[$i] _tmpr
+   concat $2[$i] _tmpd
+   concat {$1[$i]+$3[$i]*$5} _tmpr
+   concat {$2[$i]+$4[$i]*$5} _tmpd
+  end
+  style -pt 100
+  plot _tmpr _tmpd
+  style -pt 7
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/.cvsignore	(revision 17422)
@@ -0,0 +1,3 @@
+pantasks.c
+pantasks_client.c
+pantasks_server.c
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckController.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckController.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckController.c	(revision 17422)
@@ -0,0 +1,116 @@
+# include "pantasks.h"
+
+static struct timeval start;
+void TimerMark ();
+float TimerElapsed (int reset);
+
+int CheckController () {
+
+  char *p, *q;
+  int i, Njobs, status, JobID;
+  Job *job;
+  IOBuffer buffer;
+
+  /* get the list of completed jobs (exit / crash), update the job status */
+  if (!CheckControllerStatus()) return (TRUE);
+
+  /*** check EXIT jobs ***/
+  InitIOBuffer (&buffer, 0x100);
+  // TimerMark ();
+  // status = ControllerCommand ("stop", CONTROLLER_PROMPT, &buffer); 
+  // if (VerboseMode()) gprint (GP_ERR, "stop controller %f\n", TimerElapsed(TRUE));
+
+  TimerMark ();
+  FlushIOBuffer (&buffer);
+  status = ControllerCommand ("jobstack exit", CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) gprint (GP_ERR, "check exit stack %f\n", TimerElapsed(TRUE));
+  if (!status) goto escape;
+
+  /** programming error **/
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  if (status != 1) goto escape;
+  if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(TRUE));
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    status = sscanf (p, "%d", &JobID);
+
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in EXIT job list\n", JobID);
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d exit jobs %f\n", i, TimerElapsed(TRUE));
+
+  /*** check CRASH jobs ***/
+  FlushIOBuffer (&buffer);
+  status = ControllerCommand ("jobstack crash", CONTROLLER_PROMPT, &buffer);
+  if (!status) goto escape;
+
+  p = memstr (buffer.buffer, "USAGE: jobstack", buffer.Nbuffer);
+  if (p != NULL) goto escape;
+
+  /** parse job list **/
+  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  if (status != 1) goto escape;
+  if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(TRUE)); 
+
+  p = buffer.buffer;
+  for (i = 0; i < Njobs; i++) {
+    q = strchr (p, '\n');
+    if (q == NULL) {
+      gprint (GP_ERR, "controller message error: incomplete job list\n");
+      break;
+    }
+    p = q + 1;
+    
+    status = sscanf (p, "%d", &JobID);
+    job = FindControllerJob (JobID);
+    if (job == NULL) {
+      gprint (GP_ERR, "misplaced job? %d not in CRASH job list\n", JobID);
+      continue;
+    }
+    /* this checks the individual job status, grabs stdout/stderr */
+    CheckControllerJob (job);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "clear %d crash jobs %f\n", i, TimerElapsed(TRUE)); 
+
+  FlushIOBuffer (&buffer);
+  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+ escape:
+  FlushIOBuffer (&buffer);
+  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer); 
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
+
+void TimerMark () {
+    gettimeofday (&start, (void *) NULL);
+}
+
+float TimerElapsed (int reset) {
+
+  float dtime;
+  struct timeval stop;
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  if (reset) gettimeofday (&start, (void *) NULL);
+  return (dtime);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckJobs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 17422)
@@ -0,0 +1,230 @@
+# include "pantasks.h"
+
+float CheckJobs () {
+
+  FILE *f;
+  Job *job;
+  Task *task;
+  Macro *macro;
+  int i, status;
+  char varname[64];
+  Queue *queue;
+  float time_running, next_timeout;
+
+  // int Ncheck;
+  // Ncheck = 0;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  /** test all jobs: ready to test?  finished? **/
+  while ((job = NextJob ()) != NULL) {
+    // Ncheck ++;
+
+    task = job[0].task;
+    // XXX we need to guarantee that the task exists
+    // if we delete a task, we need to keep a copy until all task jobs are
+    // removed
+
+    /* check poll period (ready to ask for status?) */
+    time_running = GetTaskTimer(job[0].last, FALSE);
+    // fprintf (stderr, "next: %f, poll: %f, run: %f\n", next_timeout, task[0].poll_period, time_running);
+    if (time_running < task[0].poll_period) {
+      next_timeout = MIN (next_timeout, task[0].poll_period - time_running);
+      continue;
+    }
+    next_timeout = 0.0;
+
+    /* check current status */
+    status = CheckJob (job);
+    switch (status) {
+      case JOB_PENDING:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) pending\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_BUSY:
+	/* if (VerboseMode()) gprint (GP_LOG, "job %s (%d) busy\n", task[0].name, job[0].JobID); */
+	break;
+
+      case JOB_CRASH:
+      case JOB_EXIT:
+	/* push output buffer data to the stdout and stderr queues */
+	/* XXX this will break on 0 values in output streams */
+	if (DEBUG) fprintf (stderr, "job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+	PushNamedQueue ("stdout", job[0].stdout_buff.buffer);
+	PushNamedQueue ("stderr", job[0].stderr_buff.buffer);
+
+	/* save the stdout and stderr if desired */
+	if ((job[0].stdout_dump != NULL) && strcasecmp(job[0].stdout_dump, "NULL")) {
+	  f = fopen (job[0].stdout_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stdout dump file %s\n", job[0].stdout_dump);
+	  } else {
+	    fwrite (job[0].stdout_buff.buffer, 1, job[0].stdout_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+	if ((job[0].stderr_dump != NULL) && strcasecmp(job[0].stderr_dump, "NULL")) {
+	  f = fopen (job[0].stderr_dump, "a");
+	  if (f == NULL) {
+	    gprint (GP_ERR, "unable to open stderr dump file %s\n", job[0].stderr_dump);
+	  } else {
+	    fwrite (job[0].stderr_buff.buffer, 1, job[0].stderr_buff.Nbuffer, f);
+	    fclose (f);
+	  }
+	}
+
+	/* set taskarg variables */
+	for (i = 0; i < job[0].argc; i++) {
+	    sprintf (varname, "taskarg:%d", i);
+	    set_str_variable (varname, job[0].argv[i]);
+	}
+	set_int_variable ("taskarg:n", job[0].argc);
+
+	/* set options variables */
+	for (i = 0; i < job[0].optc; i++) {
+	    sprintf (varname, "options:%d", i);
+	    set_str_variable (varname, job[0].optv[i]);
+	}
+	set_int_variable ("options:n", job[0].optc);
+
+	set_variable ("JOB_DTIME", job[0].dtime);
+
+	if (job[0].realhost == NULL) {
+	  set_str_variable ("JOB_HOSTNAME", "localhost");
+	} else {
+	  set_str_variable ("JOB_HOSTNAME", job[0].realhost);
+	}	    
+
+	if (status == JOB_CRASH) {
+	  /* XXX add an Ncrash element? */
+	  task[0].Nfailure ++;
+	  UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+
+	  /* run task[0].crash macro, if it exists */
+	  /* perhaps define PushNamedQueueBuffer */
+
+	  set_str_variable ("JOB_STATUS", "CRASH");
+
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) crash\n", task[0].name, job[0].JobID);
+	  if (task[0].crash != NULL) {
+	    exec_loop (task[0].crash);
+	  }
+	}
+	if (status == JOB_EXIT) {
+	  /* update the exit status counters */
+	  if (job[0].exit_status) {
+	    task[0].Nfailure ++;
+	    UpdateTaskTimerStats (task, TIMER_FAILURE, job[0].dtime);
+	  } else {
+	    task[0].Nsuccess ++;
+	    UpdateTaskTimerStats (task, TIMER_SUCCESS, job[0].dtime);
+	  }
+
+	  set_int_variable ("JOB_STATUS", job[0].exit_status);
+
+	  /* run corresponding task[0].exit macro, if it exists */
+	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) exit\n", task[0].name, job[0].JobID);
+	  macro = task[0].defexit;
+	  for (i = 0; i < task[0].Nexit; i++) {
+	    if (job[0].exit_status == atoi(task[0].exit[i][0].name)) {
+	      macro = task[0].exit[i];
+	      break;
+	    }
+	  }
+	  if (macro != NULL) exec_loop (macro);
+	}
+
+	/* flush the stderr and stdout queues */
+	queue = FindQueue ("stdout");
+	if (queue) InitQueue (queue);
+	queue = FindQueue ("stderr");
+	if (queue) InitQueue (queue);
+
+	UpdateTaskTimerStats (task, TIMER_ALLJOBS, job[0].dtime);
+
+	DeleteJob (job);
+	continue;
+	break;
+
+      default:
+	if (VerboseMode()) gprint (GP_LOG, "unknown exit status: %d\n", status);
+	/** do something more useful here ?? **/
+	break;
+    }
+
+    /* check for timeout - (local jobs only) 
+       we only check timeout after a poll (forces at least one poll)
+     */
+    if (job[0].mode == JOB_LOCAL) {
+      if (GetTaskTimer(job[0].start, FALSE) < task[0].timeout_period) { 
+	/* reset polling clock */
+	SetTaskTimer (&job[0].last);
+	continue;
+      }
+      if (VerboseMode()) gprint (GP_LOG, "timeout on %s\n", task[0].name);
+
+      // XXX harvest STDERR and STDOUT from timeout job (should be available...)
+      // XXX add this to controller as well
+
+      /* update the timeout counter */
+      task[0].Ntimeout ++;
+
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	continue;
+      }
+
+      /* set taskarg variables */
+      for (i = 0; i < job[0].argc; i++) {
+	  sprintf (varname, "taskarg:%d", i);
+	  set_str_variable (varname, job[0].argv[i]);
+      }
+      set_int_variable ("taskarg:n", job[0].argc);
+
+      /* set options variables */
+      for (i = 0; i < job[0].optc; i++) {
+	sprintf (varname, "options:%d", i);
+	set_str_variable (varname, job[0].optv[i]);
+      }
+      set_int_variable ("options:n", job[0].optc);
+
+      /* run task[0].timeout macro, if it exists */
+      if (task[0].timeout != NULL) {
+	exec_loop (task[0].timeout);
+      }
+
+      task[0].Npending --;
+      DeleteJob (job);
+      continue;
+    }
+
+    /* reset polling clock */
+    SetTaskTimer (&job[0].last);
+  }
+  // fprintf (stderr, "check %d jobs\n", Ncheck);
+  return (next_timeout);
+}
+
+/* 
+
+  job / task timeline:
+
+  task:
+  0           exec     
+  start       create
+  task clock  new job
+
+  job:
+  0           1xpoll     2xpoll     3xpoll
+  start       check      check      check 
+  job clock   status     status     status
+
+  .           .          .          timeout
+                                    run
+				    timeout
+
+  must be at least one poll before timeout 
+  (timeout >= poll)
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckPassword.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+static char PASSWORD[256];
+
+int InitPassword () {
+
+  VarConfig ("PASSWORD", "%s", PASSWORD);
+  return (TRUE);
+}
+
+int CheckPassword (int BindSocket) {
+
+  IOBuffer message;
+  int status;
+
+  if (DEBUG) gprint (GP_ERR, "waiting for password %s\n", PASSWORD);
+
+  status = ExpectCommand (BindSocket, strlen(PASSWORD), 0.1, &message);
+  if (status != 0) {
+    if (DEBUG) gprint (GP_ERR, "failed connection\n");
+    FreeIOBuffer (&message);
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (strncmp (message.buffer, PASSWORD, strlen(PASSWORD))) {
+    if (DEBUG) gprint (GP_ERR, "invalid password\n");
+    close (BindSocket);
+    return (FALSE);
+  }
+  if (DEBUG) gprint (GP_ERR, "accepted password (%s)\n", message.buffer);
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTasks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 17422)
@@ -0,0 +1,97 @@
+# include "pantasks.h"
+
+float CheckTasks () {
+
+  Job *job;
+  Task *task;
+  int status;
+  float time_running, next_timeout, fuzz;
+  // struct timeval now;
+
+  // actual maximum delay is controlled in job_threads.c
+  next_timeout = 1.0;
+
+  /** test all tasks: ready to test? ready to run? **/
+  while ((task = NextTask ()) != NULL) {
+
+    /*** test for all reasons we should skip this task ***/
+
+    /* task has been de-activated by the user */
+    if (!task[0].active) {
+      continue;
+    }
+    /* current time is not within valid/invalid periods */
+    if (!CheckTimeRanges (task[0].ranges, task[0].Nranges)) {
+      continue;
+    }
+    /* all allowed tasks have been run in this period */
+    if (task[0].Nmax && (task[0].Njobs >= task[0].Nmax)) {
+      continue;
+    }
+    /* too many outstanding jobs */
+    if (task[0].NpendingMax && (task[0].Npending >= task[0].NpendingMax)) {
+      continue;
+    }
+
+    /* ready to test? : check time since last exec */
+    time_running = GetTaskTimer(task[0].last, FALSE);
+    if (time_running < task[0].exec_period) {
+      // is we are to ready to run, set time to timeout, if shortest of all tasks
+      next_timeout = MIN (next_timeout, task[0].exec_period - time_running);
+      continue;
+    }
+
+    /* ready to try running the task : reset the timer */
+    next_timeout = 0.0;
+    gettimeofday (&task[0].last, (void *) NULL);
+
+    // add random offset between 0 and 5% of exec_period
+    // XXX this should be optional
+    fuzz = 0.05*task[0].exec_period*drand48() + 1e-6*task[0].last.tv_usec; 
+    task[0].last.tv_usec = 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    /* ready to run? : run task.exec macro */
+    if (task[0].exec != NULL) {
+      status = exec_loop (task[0].exec);
+      if (!status) {
+	continue;
+      }
+    }
+
+    // gettimeofday (&now, (void *) NULL);
+    // fprintf (stderr, "t1: %d %6d  - \n", now.tv_sec, now.tv_usec);
+
+    /* check if there are errors with this task */
+    if (!ValidateTask (task, TRUE)) { 
+      continue;
+    }
+    
+    // gettimeofday (&now, (void *) NULL);
+    // fprintf (stderr, "t2: %d %6d  - \n", now.tv_sec, now.tv_usec);
+
+    /* construct job from task */
+    job = CreateJob (task);
+    if (DEBUG) fprintf (stderr, "create job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+
+    // gettimeofday (&now, (void *) NULL);
+    // fprintf (stderr, "t3: %d %6d  - \n", now.tv_sec, now.tv_usec);
+
+    /* execute job - XXX add status test */
+    SubmitJob (job);
+
+    // fprintf (stderr, "nl: %d %6d  - ",
+    // task[0].last.tv_sec, task[0].last.tv_usec);
+
+    /* increment job counters */
+    task[0].Njobs ++;
+    task[0].Npending ++;
+
+    // fprintf (stderr, "%d %6d\n", 
+    // task[0].last.tv_sec, task[0].last.tv_usec);
+
+    /* increment Nrun for inclusive ranges with Nmax */
+    BumpTimeRanges (task[0].ranges, task[0].Nranges);
+  }
+  return (next_timeout);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTimeRanges.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTimeRanges.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/CheckTimeRanges.c	(revision 17422)
@@ -0,0 +1,97 @@
+# include "pantasks.h"
+
+/* the tested time is saved by CheckTimeRanges for a following BumpTimeRanges
+   otherwise we could have an inconsistency between valid ranges and Nrun */
+static time_t daytime, weektime, abstime;
+
+/* test if we meet all time range qualifications */
+int CheckTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime, Ninclude, include, exclude, valid;
+  time_t testtime;
+  struct timeval now;
+  struct tm Now;
+
+  /* get the current time */
+  gettimeofday (&now, NULL);
+  gmtime_r (&now.tv_sec, &Now);
+  daytime  = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600;
+  weektime = Now.tm_sec + Now.tm_min*60 + Now.tm_hour*3600 + Now.tm_wday*86400;
+  abstime  = now.tv_sec;
+
+  Ninclude = 0;
+  include = FALSE;
+  exclude = FALSE;
+  
+  for (i = 0; i < Nranges; i++) {
+    if (ranges[i].include) Ninclude ++;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* check for more than max runs in time range */
+    if (ranges[i].include && intime && ranges[i].Nmax) {
+      if (ranges[i].Nrun >= ranges[i].Nmax) return (FALSE);
+    }
+    /* reset Nrun if we are outside of intime */
+    if (ranges[i].include && !intime && ranges[i].Nmax && ranges[i].Nrun) {
+      ranges[i].Nrun = 0;
+    }
+
+    /* is this a valid time? */
+    if ( ranges[i].include &&  intime) include = TRUE;
+    if (!ranges[i].include && !intime) exclude = TRUE;
+  }
+
+  if (Ninclude == 0) include = TRUE;
+  valid = include && !exclude;
+
+  return (valid);
+}  
+
+/* increment the number of runs for all inclusive time ranges with Nmax > 0
+   (only call when we execute a task -- after CheckTimeRanges) */
+int BumpTimeRanges (TimeRange *ranges, int Nranges) {
+
+  int i, intime;
+  time_t testtime;
+
+  /* only increment the counter for ranges which are valid */
+  for (i = 0; i < Nranges; i++) {
+    if (!ranges[i].Nmax) continue;
+    if (!ranges[i].include) continue;
+
+    switch (ranges[i].type) {
+      /* set the testtime */
+      case RANGE_ABS:
+	testtime = abstime;
+	break;
+      case RANGE_DAY:
+	testtime = daytime;
+	break;
+      case RANGE_WEEK:
+	testtime = weektime;
+	break;
+      default:
+	abort ();
+    }
+    intime = (testtime >= ranges[i].start) && (testtime <= ranges[i].stop);
+
+    /* reset Nrun if we are outside of intime */
+    if (intime) ranges[i].Nrun ++;
+  }
+  return (TRUE);
+}  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 17422)
@@ -0,0 +1,494 @@
+# include "pantasks.h"
+/* adding a new host can delay controller up to a second or so */
+# define CONTROLLER_TIMEOUT 1000
+# define CONNECT_TIMEOUT 300
+
+/* local static variables to hold the connection to the controller */
+static int ControllerStatus = FALSE;
+static int stdin_cntl, stdout_cntl, stderr_cntl;
+static IOBuffer stdout_buffer;
+static IOBuffer stderr_buffer;
+static int ControllerPID = 0;
+
+/* test if the controller is running */
+int CheckControllerStatus () {
+  return (ControllerStatus);
+}
+
+/* check job / get output if done */
+int CheckControllerJob (Job *job) {
+  struct timeval start, stop;
+  float dtime;
+
+  gettimeofday (&start, (void *) NULL);
+  CheckControllerJobStatus (job);
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  /* if (VerboseMode()) gprint (GP_ERR, "check job status %f\n", dtime); */
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stdout", job[0].pid, &job[0].stdout_buff, job[0].stdout_size);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stdout %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    GetJobOutput ("stderr", job[0].pid, &job[0].stderr_buff, job[0].stderr_size);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "get stderr %f\n", dtime); */
+
+    gettimeofday (&start, (void *) NULL);
+    DeleteControllerJob (job);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+    /* if (VerboseMode()) gprint (GP_ERR, "delete job %f\n", dtime); */
+  }  
+  return (TRUE);
+}
+
+int DeleteControllerJob (Job *job) {
+
+  int status;
+  char cmd[128]; 
+  IOBuffer buffer;
+
+  sprintf (cmd, "delete %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+}
+  
+/* ask controller about job status */
+int CheckControllerJobStatus (Job *job) {
+
+  int outstate, status;
+  char cmd[128], status_string[64], string[128];
+  char *p;
+  IOBuffer buffer;
+
+  sprintf (cmd, "check job %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  if (!status) {
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** was this a valid job? **/
+  p = memstr (buffer.buffer, "job not found", buffer.Nbuffer);
+  if (p != NULL) {
+    gprint (GP_ERR, "unknown job %d\n", job[0].pid);
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  /** parse status message **/
+  p = memstr (buffer.buffer, "STATUS",   buffer.Nbuffer);
+  sscanf (p, "%*s %s", status_string);
+  p = memstr (buffer.buffer, "EXITST",   buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer.buffer, "STDOUT",   buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stdout_size);
+  p = memstr (buffer.buffer, "STDERR",   buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stderr_size);
+  p = memstr (buffer.buffer, "DTIME",    buffer.Nbuffer);
+  sscanf (p, "%*s %lf", &job[0].dtime);
+  p = memstr (buffer.buffer, "HOSTNAME", buffer.Nbuffer);
+  sscanf (p, "%*s %s", string);
+  job[0].realhost = strcreate (string);
+  FreeIOBuffer (&buffer);
+
+  /* possible exit status values */
+  outstate = -1;
+  if (!strcmp(status_string, "BUSY"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "DONE"))    outstate = JOB_BUSY;
+  if (!strcmp(status_string, "PENDING")) outstate = JOB_PENDING;
+  if (!strcmp(status_string, "EXIT"))    outstate = JOB_EXIT;
+  if (!strcmp(status_string, "CRASH"))   outstate = JOB_CRASH;
+  if (outstate == -1) {
+    gprint (GP_ERR, "programming error?\n");
+    exit (1);
+  }
+  job[0].state = outstate;
+  return (TRUE);
+}
+
+/* we read Nbytes from the host, then watch for the prompt */ 
+int GetJobOutput (char *cmd, int pid, IOBuffer *buffer, int Nbytes) {
+  
+  int i, status, Nstart;
+  char *line;
+  struct timespec request, remain;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* flush any earlier messages */
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+  Nstart = buffer[0].Nbuffer;
+
+  /* send command to get appropriate channel */
+  status = write_fmt (stdin_cntl, "%s %d\n", cmd, pid);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) return (CONTROLLER_DOWN);
+
+  /* read at least Nbytes, then watch for CONTROLLER_PROMPT */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, stdout_cntl);
+    if ((buffer[0].Nbuffer - Nstart) >= Nbytes) {
+      line = memstr (buffer[0].buffer, CONTROLLER_PROMPT, buffer[0].Nbuffer);
+    }
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) return (CONTROLLER_DOWN);
+  if (status == -1) return (CONTROLLER_HUNG);
+
+  /* if (VerboseMode()) gprint (GP_ERR, "message received (GetJobOutput : %s)\n", cmd);   */
+  /* drop extra bytes from pcontrol (not pclient:job) */
+  buffer[0].Nbuffer = Nstart + Nbytes;
+  if (buffer[0].Nalloc > buffer[0].Nbuffer) {
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  return (CONTROLLER_GOOD);
+}
+
+/* submitting a job to the controller automatically starts controller */
+int SubmitControllerJob (Job *job) {
+
+  int i, Nchar, status;
+  char *cmd, *p, string[64];
+  IOBuffer buffer;
+
+  if (job[0].task[0].host == NULL) return (FALSE); 
+
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  /** construct the line to be sent to the controller **/
+
+  /* determine the total line length */
+  Nchar = 0;
+  for (i = 0; i < job[0].argc; i++) {
+    Nchar += strlen (job[0].argv[i]) + 1;
+  }
+  if (job[0].task[0].host) {
+    Nchar += strlen (job[0].task[0].host) + 1;
+  }
+  Nchar += 10;
+  ALLOCATE (cmd, char, Nchar);
+  bzero (cmd, Nchar);
+
+  /* construct the controller command portion */
+  if (!strcasecmp (job[0].task[0].host, "ANYHOST")) {
+    sprintf (cmd, "job");
+  } else {
+    if (job[0].task[0].host_required) {
+      sprintf (cmd, "job +host %s", job[0].task[0].host);
+    } else {
+      sprintf (cmd, "job -host %s", job[0].task[0].host);
+    }
+  }
+
+  /* add the command arguments */
+  for (i = 0; i < job[0].task[0].argc; i++) {
+    strcat (cmd, " ");
+    strcat (cmd, job[0].task[0].argv[i]);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  free (cmd);
+
+  /* extract the job PID from the controller response */
+  p = memstr (buffer.buffer, "JobID", buffer.Nbuffer);
+  if (p == NULL) {
+    gprint (GP_ERR, "missing PID in pcontrol message : programming error\n");
+    gprint (GP_ERR, "ControllerCommand returns: %d\n", status);
+    gprint (GP_ERR, "ControllerCommand response: %s\n", buffer.buffer);
+    exit (1);
+  }
+  sscanf (p, "%*s %s", string);
+  job[0].pid = atoi (string);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
+
+int StartController () {
+
+  char *p;
+  char **argv, cmd[128];
+  int i, pid, status;
+  int stdin_fd[2], stdout_fd[2], stderr_fd[2];
+  IOBuffer buffer;
+
+  if (ControllerStatus) return (TRUE);
+
+  if (VarConfig ("CONTROLLER", "%s", cmd) == NULL) strcpy (cmd, "pcontrol");
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 2);
+  argv[0] = cmd;
+  argv[1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    gprint (GP_LOG, "starting controller connection\n");
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv); 
+    exit (1);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with controller to verify alive & running */
+  /** this handshake is similar to ControllerCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto pipe_error;
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  /* connection is likely slow; don't bother with nanosleep here */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (10000); // wait for controller to start up
+  }
+  if (status == 0) goto pipe_error;
+  if (status == -1) goto io_error;
+  FreeIOBuffer (&buffer);
+
+  /* set local static vars to pipe connections */
+  stdin_cntl  = stdin_fd[1];
+  stdout_cntl = stdout_fd[0];
+  stderr_cntl = stderr_fd[0];
+
+  InitIOBuffer (&stdout_buffer, 0x100);
+  InitIOBuffer (&stderr_buffer, 0x100);
+
+  ControllerPID = pid;
+  ControllerStatus = TRUE;
+  gprint (GP_LOG, "Connected\n");
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+io_error:
+  gprint (GP_ERR, "timeout while connecting\n");
+  goto close_pipes;
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
+
+int ControllerCommand (char *cmd, char *response, IOBuffer *buffer) {
+
+  int i, j, status;
+  char *line;
+  struct timespec request, remain;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  ReadtoIOBuffer (buffer, stdout_cntl);
+  FlushIOBuffer (buffer);
+
+  /* send command, is pipe still open? */
+  status = write_fmt (stdin_cntl, "%s\n", cmd);
+  if ((status == -1) && (errno == EPIPE)) {
+    StopController ();
+    gprint (GP_ERR, "controller is down\n");
+    return (FALSE);
+  }
+  
+  /* for commands which don't return a prompt, don't look for one */
+  if (response == NULL) {
+      return (TRUE);
+  }
+
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+  for (j = 0; (status == -1) && (j < 10); j++) {
+      for (i = 0; (i < CONTROLLER_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+	  status = ReadtoIOBuffer (buffer, stdout_cntl);
+	  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+	  if (status == -1) nanosleep (&request, &remain);
+      }
+      if (status ==  0) {
+	  StopController ();
+	  gprint (GP_ERR, "controller is down\n");
+	  return (FALSE);
+      }
+      if (status == -1) {
+	  gprint (GP_ERR, "controller is not responding (%d tries)\n", j);
+	  gwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, GP_ERR);
+      }
+  }
+  if (status == -1) {
+      gprint (GP_ERR, "controller still not responding, giving up\n");
+      return (FALSE);
+  }
+
+  /* need to strip off the prompt */
+  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+  if (line != NULL) {
+    buffer[0].Nbuffer = line - buffer[0].buffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
+  /* if (VerboseMode()) gprint (GP_ERR, "message received, %d cycles\n", i); */
+  return (TRUE);
+}
+
+int CheckControllerOutput () {
+
+  int Nread;
+
+  if (!ControllerStatus) return (TRUE);
+
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&stdout_buffer, stdout_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&stderr_buffer, stderr_cntl);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+    case 0:   /* pipe is closed, change child state? **/
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+int PrintControllerOutput () {
+
+  gprint (GP_LOG, "--- stdout ---\n");
+  gwrite (stdout_buffer.buffer, 1, stdout_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "--- stderr ---\n");
+  gwrite (stderr_buffer.buffer, 1, stderr_buffer.Nbuffer, GP_LOG);
+  gprint (GP_LOG, "---  done  ---\n");
+  return (TRUE);
+}
+
+
+int KillControllerJob (Job *job) {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  if (!ControllerStatus) return (TRUE);
+
+  sprintf (cmd, "kill %d", job[0].pid);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
+  FreeIOBuffer (&buffer);
+  return (status);
+
+  /** need to interpret output message & free things **/
+}
+
+int QuitController () {
+
+  int status;
+  char cmd[128];
+  IOBuffer buffer;
+
+  if (!ControllerStatus) return (TRUE);
+
+  sprintf (cmd, "quit");
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (cmd, NULL, &buffer);
+  FreeIOBuffer (&buffer);
+
+  /* the quit command does not return a prompt, 
+     check that the controller exited */
+  StopController ();
+  return (TRUE);
+}
+
+int StopController () {
+
+  int i, waitstatus, result;
+
+  if (!ControllerStatus) return (TRUE);
+
+  ControllerStatus = FALSE;
+  result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for controller to exit
+    result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  }
+  ControllerPID = 0;
+  close (stdin_cntl);
+  close (stdout_cntl);
+  close (stderr_cntl);
+  FreeIOBuffer (&stdout_buffer);
+  FreeIOBuffer (&stderr_buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/InputQueue.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/InputQueue.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/InputQueue.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "pantasks.h"
+
+static int Ninputs = 0;
+static int NINPUTS = 0;
+static char **inputs;
+
+void InitInputs () {
+
+  Ninputs = 0;
+  NINPUTS = 10;
+  ALLOCATE (inputs, char *, NINPUTS);
+}
+
+void FreeInputs () {
+  int i;
+  for (i = 0; i < Ninputs; i++) {
+    free (inputs[i]);
+  }
+  free (inputs);
+}
+
+/* add this input to the inputs table */
+void AddNewInput (char *input) {
+
+  SerialThreadLock ();
+  if (DEBUG) gprint (GP_LOG, "adding a new input (%s)\n", input);
+  inputs[Ninputs] = input;
+  Ninputs ++;
+  if (Ninputs >= NINPUTS - 1) {
+    NINPUTS += 10;
+    REALLOCATE (inputs, char *, NINPUTS);
+  }
+  if (DEBUG) gprint (GP_LOG, "done new input (%s)\n", input);
+  SerialThreadUnlock ();
+}
+
+/* remove this input from the inputs table */
+int DeleteInput (char *input) {
+
+  int i, j;
+
+  if (DEBUG) gprint (GP_LOG, "deleting an input (%s)\n", input);
+  for (i = 0; i < Ninputs; i++) {
+    if (inputs[i] == input) {
+      free (inputs[i]);
+      for (j = i; j < Ninputs - 1; j++) {
+	inputs[j] = inputs[j+1];
+      }
+      Ninputs --;
+      if ((Ninputs > 10) && (Ninputs / 2 < NINPUTS)) {
+	NINPUTS = Ninputs + 10;
+	REALLOCATE (inputs, char *, NINPUTS);
+      }
+      if (DEBUG) gprint (GP_LOG, "deleted an input\n");
+      return TRUE;
+    }
+  }
+  // did not find the input
+  return FALSE;
+}
+
+/* if any inputs are pending, run one */
+void CheckInputs () {
+
+  int Nbytes, status;
+  char *input, *line, *outline, tmp;
+
+  if (Ninputs < 1) return;
+
+  input = inputs[0];
+  if (DEBUG) gprint (GP_LOG, "got an input (%s)\n", input);
+  
+  Nbytes = snprintf (&tmp, 0, "input %s", input);
+  ALLOCATE (line, char, Nbytes + 1);
+  snprintf (line, Nbytes + 1, "input %s", input);
+
+  status = command (line, &outline, TRUE);
+  free (outline);
+  
+  DeleteInput (input);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobIDOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "pantasks.h"
+
+# define MAX_N_JOBS 1000
+static char *JobIDList;
+static int   JobIDPtr;
+
+void InitJobIDs () {
+
+  JobIDPtr = 0;
+  ALLOCATE (JobIDList, char, MAX_N_JOBS);
+  bzero (JobIDList, MAX_N_JOBS*sizeof(char));
+}  
+
+void FreeJobIDs () {
+  free (JobIDList);
+}
+
+/* return next unique ID, recycle every MAX_N_JOBS */
+int NextJobID () {
+
+  int Ntry;
+
+  JobIDPtr ++;
+  if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+
+  Ntry = 0;
+  while (JobIDList[JobIDPtr]) {
+    Ntry ++;
+    JobIDPtr ++;
+    if (JobIDPtr >= MAX_N_JOBS) JobIDPtr = 0;
+    if (Ntry == MAX_N_JOBS) return (-1);
+  }
+  JobIDList[JobIDPtr] = TRUE;
+  return (JobIDPtr);
+}
+
+int FreeJobID (int JobID) {
+
+  if (JobID < 0) return (FALSE);
+  if (JobID >= MAX_N_JOBS) return (FALSE);
+
+  JobIDList[JobID] = FALSE;
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/JobOps.c	(revision 17422)
@@ -0,0 +1,285 @@
+# include "pantasks.h"
+
+static Job **jobs;
+static int   Njobs;
+static int   NJOBS;
+
+/* counter marking job being visited by the run loop */
+static int   ActiveJob;
+
+/* set up the jobs list */
+void InitJobs () {
+  NJOBS = 20;
+  Njobs = 0;
+  ALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+}
+
+void FreeJobs () {
+  int i;
+  for (i = 0; i < Njobs; i++) {
+    FreeJob (jobs[i]);
+  }
+  free (jobs);
+}
+
+/* provide a mechanism to loop over the list of jobs */
+Job *NextJob () {
+  
+  Job *job;
+
+  ActiveJob ++;
+  if (ActiveJob < 0) ActiveJob = 0;
+  if (ActiveJob >= Njobs) {
+    ActiveJob = -1;
+    return (NULL);
+  }
+  job = jobs[ActiveJob];
+  return (job);
+}
+
+/* return job with given ID */
+Job *FindJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].JobID == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* return job with given controller Job ID */
+Job *FindControllerJob (int JobID) {
+
+  int i;
+
+  /* return job with matching JobID */
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) continue;
+    if (jobs[i][0].pid  == JobID) {
+      return (jobs[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* list known jobs */
+void ListJobs () {
+
+  int i;
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  gprint (GP_LOG, "\n");
+  if (Njobs == 0) {
+    gprint (GP_LOG, " no defined jobs\n");
+    return;
+  }
+
+  gprint (GP_LOG, " Jobs in Pantasks Queue\n");
+  for (i = 0; i < Njobs; i++) {
+    gprint (GP_LOG, " %d: %-15s %5d %20s (%lx)\n", Njobs, jobs[i][0].task[0].name, jobs[i][0].JobID, jobs[i][0].argv[0], (long) jobs[i][0].argv);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    return;
+  }
+
+  sprintf (command, "jobstack busy");
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gprint (GP_LOG, " jobs currently running remotely:\n");
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+  return;
+}
+
+/* make a new job from a task */
+Job *CreateJob (Task *task) {
+  
+  int i;
+  Job *job;
+  
+  ALLOCATE (job, Job, 1);
+
+  job[0].JobID = NextJobID ();
+  job[0].pid = 0;
+  job[0].mode = JOB_LOCAL;
+  if (task[0].host != NULL) {
+    job[0].mode = JOB_CONTROLLER;
+  }
+
+  /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
+   *  allocate one extra element, with value 0 to be passed to execvp
+   */
+  job[0].argc = task[0].argc;
+  ALLOCATE (job[0].argv, char *, MAX (task[0].argc + 1, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    job[0].argv[i] = strcreate (task[0].argv[i]);
+  }
+  job[0].argv[i] = 0;
+
+  /* we need our own copy of task[0].optv: optc is the number of valid opts.  */
+  job[0].optc = task[0].optc;
+  ALLOCATE (job[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < job[0].optc; i++) {
+    job[0].optv[i] = strcreate (task[0].optv[i]);
+  }
+
+  /* Other data from the task is needed by the job. We carry a pointer back to the task.  Changes to an
+     executing task are applied to the existing jobs (exit macros, poll_period, timeout) */
+
+  job[0].task = task;
+  job[0].realhost = NULL;
+  
+  /* if we decide we need to be able to dynamically set task qualities (like host, timeouts, etc), the we will
+     need to have matched entries to these quantites in the job structure */
+
+  InitIOBuffer (&job[0].stdout_buff, 0x100);
+  InitIOBuffer (&job[0].stderr_buff, 0x100);
+
+  job[0].stdout_dump = NULL;
+  job[0].stderr_dump = NULL;
+  if (task[0].stdout_dump != NULL) job[0].stdout_dump = strcreate (task[0].stdout_dump);
+  if (task[0].stderr_dump != NULL) job[0].stderr_dump = strcreate (task[0].stderr_dump);
+
+  jobs[Njobs] = job;
+  Njobs ++;
+  if (Njobs == NJOBS) {
+    NJOBS += 20;
+    REALLOCATE (jobs, Job *, NJOBS);
+  }
+  return (jobs[Njobs-1]);
+}
+
+void FreeJob (Job *job) {
+  
+  int i;
+
+  if (job == NULL) return;
+  
+  FreeJobID (job[0].JobID);
+
+  for (i = 0; i < job[0].argc; i++) {
+    free (job[0].argv[i]);
+  }
+  free (job[0].argv);
+
+  for (i = 0; i < job[0].optc; i++) {
+    free (job[0].optv[i]);
+  }
+  free (job[0].optv);
+
+  if (job[0].stdout_dump != NULL) free (job[0].stdout_dump);
+  if (job[0].stderr_dump != NULL) free (job[0].stderr_dump);
+  if (job[0].realhost != NULL) free (job[0].realhost);
+
+  FreeIOBuffer (&job[0].stdout_buff);
+  FreeIOBuffer (&job[0].stderr_buff);
+  free (job);
+  return;
+}
+
+/* delete the job from the job list & adjust ActiveJob counter */
+int DeleteJob (Job *job) {
+
+  int i, Nm;
+
+  Nm = -1;
+  for (i = 0; i < Njobs; i++) {
+    if (job == jobs[i]) {
+      Nm = i;
+      break;
+    }
+  }
+  if (Nm == -1) {
+    gprint (GP_ERR, "programming error: job not found\n");
+    return (FALSE);
+  }
+
+  FreeJob (jobs[Nm]);
+  for (i = Nm; i < Njobs - 1; i++) {
+    jobs[i] = jobs[i + 1];
+  }
+  Njobs --;
+
+  /* adjust active job number */
+  if (ActiveJob >= Nm) {
+    ActiveJob --;
+  }
+
+  job[0].task[0].Npending --;
+  return (TRUE);
+}
+
+int FlushJobs () {
+
+  int i;
+
+  // kill outstanding jobs
+  for (i = 0; i < Njobs; i++) {
+    if (jobs[i][0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (jobs[i])) {
+	jobs[i][0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    } else {
+      if (!KillControllerJob (jobs[i])) {
+	jobs[i][0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", jobs[i][0].pid);
+      }
+    }    
+    jobs[i][0].task[0].Npending = 0;
+    FreeJob (jobs[i]);
+  }
+    
+  Njobs = 0;
+  NJOBS = 20;
+  REALLOCATE (jobs, Job *, NJOBS);
+  ActiveJob = -1;
+  return (TRUE);
+}
+
+int SubmitJob (Job *job) {
+
+  if (job[0].mode == JOB_LOCAL) {
+    if (DEBUG) fprintf (stderr, "submit job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    SubmitLocalJob (job);
+  } else {
+    SubmitControllerJob (job);
+  }
+
+  /* reset clock for start and poll-test */
+  gettimeofday (&job[0].start, (void *) NULL);
+  job[0].last = job[0].start;
+  job[0].state = JOB_PENDING;
+  return (TRUE);
+}
+
+int CheckJob (Job *job) {
+
+  /* add checks for timeouts */
+
+  if (job[0].mode == JOB_LOCAL) {
+    CheckLocalJob (job);
+  } else {
+    /* controller jobs are now checked en masse by CheckController */
+    /* CheckControllerJob (job); */
+  }
+  return (job[0].state);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ListenClients.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ListenClients.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ListenClients.c	(revision 17422)
@@ -0,0 +1,182 @@
+# include "pantasks.h"
+# define DEBUG 0
+
+static int NCLIENTS;
+static int Nclients;
+static int *clients;
+static IOBuffer **buffers;
+
+void InitClients () {
+
+  Nclients = 0;
+  NCLIENTS = 10;
+  ALLOCATE (clients, int, NCLIENTS);
+  ALLOCATE (buffers, IOBuffer *, NCLIENTS);
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+void AddNewClient (int client) {
+
+  if (DEBUG) gprint (GP_LOG, "adding a new client (%d)\n", client);
+  clients[Nclients] = client;
+  ALLOCATE (buffers[Nclients], IOBuffer, 1);
+  InitIOBuffer(buffers[Nclients], 256);
+  Nclients ++;
+  if (Nclients >= NCLIENTS - 1) {
+    NCLIENTS += 10;
+    REALLOCATE (clients, int, NCLIENTS);
+    REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+  }
+}
+
+/* add this client to the client table, create an IOBuffer for it */
+int DeleteClient (int client) {
+
+  int i, j;
+
+  if (DEBUG) gprint (GP_LOG, "deleting a client (%d)\n", client);
+  for (i = 0; i < Nclients; i++) {
+    if (clients[i] == client) {
+      FreeIOBuffer (buffers[i]);
+      free (buffers[i]);
+      close (clients[i]);
+      for (j = i; j < Nclients - 1; j++) {
+	clients[j] = clients[j+1];
+	buffers[j] = buffers[j+1];
+      }
+      Nclients --;
+      if ((Nclients > 10) && (Nclients / 2 < NCLIENTS)) {
+	NCLIENTS = Nclients + 10;
+	REALLOCATE (clients, int, NCLIENTS);
+	REALLOCATE (buffers, IOBuffer *, NCLIENTS);
+      }
+      return TRUE;
+    }
+  }
+  // did not find the client
+  return FALSE;
+}
+
+/* select for messages from the current clients; wait for 0.5s before updating client list */
+void *ListenClients (void *data) {
+  
+  int i, Ncurrent, Nmax, status, Nread;
+  char *line, log_stdout[128], log_stderr[128];
+  fd_set fdSet;
+  struct timeval timeout;
+  IOBuffer *outbuffer;
+
+  InitClients ();
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+    gprintSetFileThisThread (GP_LOG, log_stdout);
+  } else {
+    strcpy (log_stdout, "stdout");
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+    gprintSetFileThisThread (GP_ERR, log_stderr);
+  } else {
+    strcpy (log_stderr, "stderr");
+  }
+
+  while (1) {
+
+    /* Wait up to 0.5 second - need to timeout to update client list */
+    /* timeout gets mucked: need to reset before each select */
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 500000;
+
+    /* place all of the clients in the fdSet */
+    Ncurrent = Nclients;
+    Nmax = 0;
+    FD_ZERO (&fdSet);
+    for (i = 0; i < Ncurrent; i++) {
+      Nmax = MAX (Nmax, clients[i]);
+      FD_SET (clients[i], &fdSet);
+    }    
+    Nmax ++;
+
+    /* block until we have some data on the pipes (or timeout) */
+    if (DEBUG) gprint (GP_ERR, "listening to %d clients\n", Ncurrent);
+    status = select (Nmax, &fdSet, NULL, NULL, &timeout);
+    if (status == -1) {
+      perror("select()");
+      return (FALSE);
+    }
+
+    /* if no data, update client list, wait for another select */
+    if (status <= 0) continue;
+
+    /* loop over the clients with data */
+    for (i = 0; i < Ncurrent; i++) {
+      /* if client has no data, skip it */
+      if (!FD_ISSET(clients[i], &fdSet)) continue;
+
+      /* read until the pipe is empty: 0 is closed, -1 is empty, -2 is error */
+      Nread = 1;
+      while (Nread > 0) {
+	Nread = ReadtoIOBuffer (buffers[i], clients[i]);	
+      }
+      if ((Nread == 0) || (Nread == -2)) {
+	/* error: do something */
+	if (DEBUG && (Nread == 0)) gprint (GP_ERR, "socket is closed\n");
+	if (DEBUG && (Nread == -2)) gprint (GP_ERR, "error reading from socket\n");
+	DeleteClient (clients[i]);
+	continue;
+      }
+
+      if (DEBUG) gprint (GP_ERR, "read %d total bytes\n", buffers[i][0].Nbuffer);
+
+      /* see if we have a complete message waiting; if not, keep waiting for messages */
+      line = CheckForMessage (buffers[i]);
+      if (line == NULL) continue;
+
+      /* we now have a possible command from the client: run it */
+      /* in this thread, we set the print output destination to be an
+	 internal buffer, which we dump at the end of the execution */
+      /* the commands sent to the server should not have ; */
+      stripwhite (line);
+      if (*line) {
+
+	/* set buffers for the output for this client */
+	gprintSetBuffer (GP_LOG);
+	gprintSetBuffer (GP_ERR);
+
+	/* run the command, return the exit status */
+	status = multicommand (line);
+	SendMessage (clients[i], "STATUS %d", status);
+
+	// return the stderr messages first
+	outbuffer = gprintGetBuffer (GP_ERR);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+
+	// return the stdout messages first
+	outbuffer = gprintGetBuffer (GP_LOG);
+	if (outbuffer) {
+	  SendMessageFixed (clients[i], outbuffer[0].Nbuffer, outbuffer[0].buffer);
+	} else {
+	  SendMessageFixed (clients[i], 0, "");
+	}	  
+	
+	/* clear and reset the output buffers to their last output file names */
+	gprintSetFileAllThreads (GP_LOG, NULL);
+	gprintSetFileAllThreads (GP_ERR, NULL);
+      }
+      free (line);
+    }
+    /* if Nread == -2, we probably need to drop the client */
+    /* check if we need to drop / remove any clients */
+    /* check if we need to shut down the thread */
+  }
+}
+
+/* the AddClient commands are issued by the parent thread
+   the value of Nclients may increase after we check it here. 
+   only this thread is allowed to decrease Nclients and remove
+   a client from the table */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/LocalJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/LocalJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/LocalJob.c	(revision 17422)
@@ -0,0 +1,222 @@
+# include "pantasks.h"
+
+/* local jobs are forked in the background 
+   we might need to limit the maximum number of local jobs.
+   should we have a queue/stack of pending local jobs, much
+   like controller? */
+
+/* update current state, drain stdout/stderr buffers */
+int CheckLocalJob (Job *job) {
+
+  int Nread;
+
+  // XXX do something useful with exit status?
+  CheckLocalJobStatus (job);
+
+  if ((job[0].state == JOB_EXIT) || (job[0].state == JOB_CRASH)) {
+    if (DEBUG) fprintf (stderr, "empty buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    EmptyIOBuffer (&job[0].stdout_buff, 10, job[0].stdout_fd);
+    EmptyIOBuffer (&job[0].stderr_buff, 10, job[0].stderr_fd);
+    if (DEBUG) fprintf (stderr, "empty buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    close (job[0].stdout_fd);
+    close (job[0].stderr_fd);
+  } else {
+    /* read stdout buffer */
+    if (DEBUG) fprintf (stderr, "read buffer 0: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+    while ((Nread = ReadtoIOBuffer (&job[0].stdout_buff, job[0].stdout_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	// fprintf (stderr, "read %d bytes (Nblock: %d, Nbuffer: %d)\n", Nread, job[0].stdout.Nblock, job[0].stdout.Nbuffer);
+	break;
+    }
+    if (DEBUG) fprintf (stderr, "read buffer 1: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
+  
+    /* read stderr buffer */
+    while ((Nread = ReadtoIOBuffer (&job[0].stderr_buff, job[0].stderr_fd)) > 0);
+    switch (Nread) {
+      case -2:  /* error in read (programming error?  system level error?) */
+	gprint (GP_ERR, "serious IO error\n");
+	exit (2);
+      case -1:  /* no data in pipe */
+      case 0:   /* pipe is closed, change child state? **/
+      default:  /* data in pipe */
+	break;
+    }
+  }
+  return (TRUE);
+}
+
+int CheckLocalJobStatus (Job *job) {
+
+  int result, waitstatus;
+
+  /* check local job status */
+  result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  job[0].state = JOB_NONE;
+	  job[0].exit_status = 0;
+	  return (FALSE);
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      job[0].state = JOB_BUSY;
+      job[0].exit_status = 0;
+      return (TRUE);
+
+    default:
+      if (result != job[0].pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, job[0].pid);
+	exit (1);
+      }
+      if (WIFEXITED(waitstatus)) {
+	job[0].state = JOB_EXIT;
+	job[0].exit_status = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	job[0].state = JOB_CRASH;
+	job[0].exit_status = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+      job[0].dtime = GetTaskTimer (job[0].start, FALSE);
+      break;
+  }
+  return (FALSE);
+}
+
+/* this could be written a just a one-way pipe */
+int SubmitLocalJob (Job *job) {
+
+  int status, pid;
+  int stdout_fd[2], stderr_fd[2];
+
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  // XXX nothing to be read at this point
+  // other threads are already halted here.
+  fflush (stdout);
+  fflush (stderr);
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting local job\n");
+
+    /* close the other ends of the pipes */
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    // XXX neither of these work to empty the child stdout buffer
+    // fflush (stdout);
+    // fflush (stderr);
+    // close (STDOUT_FILENO);
+    // close (STDERR_FILENO);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    // XXX allow the parent time to read the stdout/stderr buffers
+    usleep (10000);
+
+    status = execvp (job[0].argv[0], job[0].argv); 
+    exit (1);
+  }
+  if (VerboseMode()) gprint (GP_ERR, "local job launched\n");
+
+  /* close the other ends of the pipes */
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  // XXX There seems to always be extra data on the pipe, specifically the 
+  // stdout buffer from the parent.  If I read it here, then it clears out that data.
+  // But, how can I be sure I will not start reading data from the exec-ed process?
+
+  { // test read of the stdout buffer
+    int Nread;
+    char buffer[0x1000];
+
+    Nread = read (stdout_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stdout before exec: %d bytes\n", Nread);
+
+    Nread = read (stderr_fd[0], buffer, 0x1000);
+    if (DEBUG) fprintf (stderr, "read from stderr before exec: %d bytes\n", Nread);
+  }
+
+  job[0].stdout_fd = stdout_fd[0];
+  job[0].stderr_fd = stderr_fd[0];
+  job[0].pid = pid;
+
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
+
+/* should this function close the fd's? */
+int KillLocalJob (Job *job) {
+
+  int i, result, waitstatus;
+
+  if (job[0].state != JOB_BUSY) return (TRUE);
+
+  /* send SIGTERM signal to job */
+  kill (job[0].pid, SIGTERM);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* send SIGKILL signal to job */
+  kill (job[0].pid, SIGKILL);
+  result = 0;
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  // wait for job to exit
+    result = waitpid (job[0].pid, &waitstatus, WNOHANG);
+  }
+  if (result) return (TRUE);
+
+  /* total failure, don't reset */
+  return (FALSE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/Makefile	(revision 17422)
@@ -0,0 +1,127 @@
+default: pantasks pantasks_client pantasks_server
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+SRC     =       $(HOME)/pantasks
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+DATA    =       $(DESTDATA)/pantasks
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1   =       -lbasiccmd -ldatacmd -lastrocmd -lshell -ldata 
+LIBS2   =       -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# sched user commands and support functions ########################
+
+client = \
+$(SRC)/pantasks_client.$(ARCH).o \
+$(SRC)/client_shell.$(ARCH).o \
+$(SRC)/invalid.$(ARCH).o \
+$(SRC)/server_shutdown.$(ARCH).o \
+$(SRC)/server_disconnect.$(ARCH).o \
+$(SRC)/server_connect.$(ARCH).o \
+$(SRC)/init_client.$(ARCH).o
+
+single = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/stop.$(ARCH).o \
+$(SRC)/pantasks.$(ARCH).o \
+$(SRC)/thread_locks.$(ARCH).o \
+$(SRC)/job_threads.$(ARCH).o \
+$(SRC)/task_threads.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o 
+
+server = \
+$(SRC)/pantasks_server.$(ARCH).o \
+$(SRC)/server_run.$(ARCH).o \
+$(SRC)/server_load.$(ARCH).o \
+$(SRC)/InputQueue.$(ARCH).o \
+$(SRC)/ListenClients.$(ARCH).o \
+$(SRC)/server.$(ARCH).o \
+$(SRC)/status_server.$(ARCH).o \
+$(SRC)/init_server.$(ARCH).o \
+$(SRC)/CheckPassword.$(ARCH).o \
+$(SRC)/thread_locks.$(ARCH).o \
+$(SRC)/job_threads.$(ARCH).o \
+$(SRC)/task_threads.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o \
+$(SRC)/input_threads.$(ARCH).o
+
+funcs = \
+$(SRC)/CheckJobs.$(ARCH).o \
+$(SRC)/CheckController.$(ARCH).o \
+$(SRC)/CheckTasks.$(ARCH).o \
+$(SRC)/CheckTimeRanges.$(ARCH).o \
+$(SRC)/ControllerOps.$(ARCH).o \
+$(SRC)/LocalJob.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/JobIDOps.$(ARCH).o \
+$(SRC)/TaskOps.$(ARCH).o
+
+cmds = \
+$(SRC)/pulse.$(ARCH).o \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/flush.$(ARCH).o \
+$(SRC)/showtask.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o \
+$(SRC)/controller.$(ARCH).o \
+$(SRC)/controller_host.$(ARCH).o \
+$(SRC)/controller_exit.$(ARCH).o \
+$(SRC)/controller_check.$(ARCH).o \
+$(SRC)/controller_status.$(ARCH).o \
+$(SRC)/controller_jobstack.$(ARCH).o \
+$(SRC)/controller_run.$(ARCH).o \
+$(SRC)/controller_output.$(ARCH).o \
+$(SRC)/controller_pulse.$(ARCH).o \
+$(SRC)/task.$(ARCH).o \
+$(SRC)/task_host.$(ARCH).o \
+$(SRC)/task_nmax.$(ARCH).o \
+$(SRC)/task_active.$(ARCH).o \
+$(SRC)/task_macros.$(ARCH).o \
+$(SRC)/task_trange.$(ARCH).o \
+$(SRC)/task_stdout.$(ARCH).o \
+$(SRC)/task_periods.$(ARCH).o \
+$(SRC)/task_command.$(ARCH).o \
+$(SRC)/task_options.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/ipptool2book.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libastrocmd.a \
+$(DESTLIB)/libdatacmd.a
+
+pantasks: $(BIN)/pantasks.$(ARCH)
+$(SRC)/pantasks.$(ARCH).o : $(libs)
+$(BIN)/pantasks.$(ARCH) : $(single) $(funcs) $(cmds)
+
+pantasks_client: $(BIN)/pantasks_client.$(ARCH)
+$(SRC)/pantasks_client.$(ARCH).o : $(libs)
+$(BIN)/pantasks_client.$(ARCH) : $(client)
+
+pantasks_server: $(BIN)/pantasks_server.$(ARCH)
+$(SRC)/pantasks_server.$(ARCH).o : $(libs)
+$(BIN)/pantasks_server.$(ARCH) : $(server) $(funcs) $(cmds)
+
+pantasks_client.install: $(DESTBIN)/pantasks_client
+pantasks_server.install: $(DESTBIN)/pantasks_server
+
+$(single) $(client) $(server) $(funcs) $(cmds) : $(INC)/pantasks.h
+
+install: $(DESTBIN)/pantasks $(DESTBIN)/pantasks_client $(DESTBIN)/pantasks_server help modules
+
+help: clean-help cmd.basic.help cmd.data.help cmd.astro.help pantasks.help
+
+modules: pantasks.modules
+
+.PHONY: pantasks pantasks_client pantasks_server
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/TaskOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/TaskOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/TaskOps.c	(revision 17422)
@@ -0,0 +1,675 @@
+# include "pantasks.h"
+# include <regex.h>
+
+static Task **tasks;
+static int    Ntasks;
+static int    NTASKS;
+
+/* counter marking task being visited by the run loop */
+static int   ActiveTask;
+
+/* temporary holder for a new task */
+static Task *NewTask = NULL;
+
+/* set up the task list system */
+void InitTasks () {
+  NTASKS = 20;
+  Ntasks = 0;
+  ALLOCATE (tasks, Task *, NTASKS);
+  ActiveTask = -1;
+}
+
+void FreeTasks () {
+  int i;
+  for (i = 0; i < Ntasks; i++) {
+    FreeTask (tasks[i]);
+  }
+  free (tasks);
+}
+
+/* provide a mechanism to loop over the list of tasks */
+Task *NextTask () {
+  
+  Task *task;
+
+  /* move to the next task and return it */
+  ActiveTask ++;
+  if (ActiveTask < 0) ActiveTask = 0;
+  if (ActiveTask >= Ntasks) {
+    ActiveTask = -1;
+    return (NULL);
+  }
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+/* return task with given name */
+Task *FindTask (char *name) {
+
+  int i;
+
+  /* try for an exact match first */
+  for (i = 0; i < Ntasks; i++) {
+    if (!strcmp (tasks[i][0].name, name)) {
+      return (tasks[i]);
+    }
+  }
+  return (NULL);
+}  
+
+/* list known tasks */
+void ListTasks (int verbose) {
+
+  int i, j, valid, nameLength, cmdLength;
+  char *start, *stop;
+  char format[128];
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  /* find string lengths */
+  nameLength = cmdLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+    if (tasks[i][0].argv == NULL) {
+      cmdLength = MAX (nameLength, strlen("(dynamic)"));
+    } else {
+      cmdLength = MAX (nameLength, strlen(tasks[i][0].argv[0]));
+    }
+  }
+
+  gprint (GP_LOG, " Task Status\n");
+
+  snprintf (format, 128, "  AV %%-%ds %5s  %5s %5s %5s %%-%ds\n", nameLength, "Njobs", "Ngood", "Nfail", "Ntime", cmdLength);
+  gprint (GP_LOG, format, "Name", "Command");
+
+  snprintf (format, 128, "%%-%ds %%5d  %%5d %%5d %%5d %%-%ds\n", nameLength, cmdLength);
+  for (i = 0; i < Ntasks; i++) {
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (verbose) gprint (GP_LOG, "\n");
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, "(dynamic)");
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].Nsuccess, tasks[i][0].Nfailure, tasks[i][0].Ntimeout, tasks[i][0].argv[0]);
+    }
+    if (verbose) {
+      gprint (GP_LOG, "    spawn period: %f, polling period: %f, timeout period: %f\n", 
+	       tasks[i][0].exec_period, tasks[i][0].poll_period, tasks[i][0].timeout_period);
+      for (j = 0; j < tasks[i][0].Nranges; j++) {
+	switch (tasks[i][0].ranges[j].type) {
+	  case RANGE_ABS:
+	    start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_DAY:
+	    start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	    break;
+	  case RANGE_WEEK:
+	    start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	    stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	    break;
+	  default:
+	    abort ();
+	}
+	if (tasks[i][0].ranges[j].include) {
+	  gprint (GP_LOG, "     active : %s - %s", start, stop);
+	  if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+	  gprint (GP_LOG, "\n");
+	} else {
+	  gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+	}
+	free (start);
+	free (stop);
+      }
+      if (tasks[i][0].host == NULL) {
+	gprint (GP_LOG, "    task runs locally\n");
+	continue;
+      }
+      if (!strcasecmp(tasks[i][0].host, "ANYHOST")) {
+	gprint (GP_LOG, "    task host selected by controller\n");
+	continue;
+      }
+      if (tasks[i][0].host_required) {
+	gprint (GP_LOG, "    host %s (required)\n", tasks[i][0].host);
+      } else {
+	gprint (GP_LOG, "    host %s (desired)\n", tasks[i][0].host);
+      }
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ListTaskStats (char *regex) {
+
+  int i, valid, nameLength;
+  char format[128];
+  regex_t preg;
+
+  gprint (GP_LOG, "\n");
+  if (Ntasks == 0) {
+    gprint (GP_LOG, " no defined tasks\n");
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  gprint (GP_LOG, " Task Statistics\n");
+
+  snprintf (format, 128, "     %%-%ds |           alljobs          |           success          |           failure          |\n", nameLength);
+  gprint (GP_LOG, format, "");
+  snprintf (format, 128, "  AV %%-%ds | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax | Njobs   Tmin   Tave   Tmax |\n", nameLength);
+  gprint (GP_LOG, format, "Name");
+
+  snprintf (format, 128, "%%-%ds", nameLength);
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (tasks[i][0].active) {
+      gprint (GP_LOG, "  +");
+    } else {
+      gprint (GP_LOG, "  -");
+    }
+    if (valid) {
+      gprint (GP_LOG, "+ ");
+    } else {
+      gprint (GP_LOG, "- ");
+    }
+    if (tasks[i][0].argv == NULL) {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    } else {
+      gprint (GP_LOG, format, tasks[i][0].name);
+    }
+    if (tasks[i][0].dtimeMin_alljobs < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Njobs,    "NONE",                       tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Njobs,    tasks[i][0].dtimeMin_alljobs, tasks[i][0].dtimeAve_alljobs, tasks[i][0].dtimeMax_alljobs);
+    }      
+    if (tasks[i][0].dtimeMin_success < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f",     tasks[i][0].Nsuccess, "NONE",                       tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f",   tasks[i][0].Nsuccess, tasks[i][0].dtimeMin_success, tasks[i][0].dtimeAve_success, tasks[i][0].dtimeMax_success);
+    }
+    if (tasks[i][0].dtimeMin_failure < 0) {
+      gprint (GP_LOG, " | %5d %6s %6.2f %6.2f |\n",   tasks[i][0].Nfailure, "NONE",                       tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    } else {
+      gprint (GP_LOG, " | %5d %6.2f %6.2f %6.2f |\n", tasks[i][0].Nfailure, tasks[i][0].dtimeMin_failure, tasks[i][0].dtimeAve_failure, tasks[i][0].dtimeMax_failure);
+    }
+  }
+  return;
+}
+
+/* list known tasks */
+void ResetTaskStats (char *regex) {
+
+  int i, nameLength;
+  regex_t preg;
+
+  if (Ntasks == 0) {
+    return;
+  }
+
+  if (regex != NULL) {
+    regcomp (&preg, regex, REG_EXTENDED);
+  }
+
+  /* find string lengths */
+  nameLength = 0;
+  for (i = 0; i < Ntasks; i++) {
+    nameLength = MAX (nameLength, strlen(tasks[i][0].name));
+  }
+
+  for (i = 0; i < Ntasks; i++) {
+      
+    if ((regex != NULL) && regexec (&preg, tasks[i][0].name, 0, NULL, 0)) continue;
+
+    tasks[i][0].Njobs = 0;
+    tasks[i][0].dtimeMin_alljobs = 0;
+    tasks[i][0].dtimeAve_alljobs = 0;
+    tasks[i][0].dtimeMax_alljobs = 0;
+
+    tasks[i][0].Nsuccess = 0;
+    tasks[i][0].dtimeMin_success = 0;
+    tasks[i][0].dtimeAve_success = 0;
+    tasks[i][0].dtimeMax_success = 0;
+
+    tasks[i][0].Nfailure = 0;
+    tasks[i][0].dtimeMin_failure = 0;
+    tasks[i][0].dtimeAve_failure = 0;
+    tasks[i][0].dtimeMax_failure = 0;
+  }
+  return;
+}
+
+/* show details of a task */
+int ShowTask (char *name) {
+
+  int i, j;
+  char *start, *stop;
+  Task *task;
+
+  task = FindTask (name);
+  if (task == NULL) {
+    gprint (GP_LOG, "task %s not found\n", name);
+    return (FALSE);
+  }
+
+  gprint (GP_LOG, "\n macro %s\n", task[0].name);
+
+  gprint (GP_LOG, "\n command: ");
+  for (i = 0; i < task[0].argc; i++) {
+    gprint (GP_LOG, "%s ", task[0].argv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  gprint (GP_LOG, "\n options: ");
+  for (i = 0; i < task[0].optc; i++) {
+    gprint (GP_LOG, "%s ", task[0].optv[i]);
+  }
+  gprint (GP_LOG, "\n\n");
+
+  if (task[0].host == NULL) {
+    gprint (GP_LOG, " task runs locally\n");
+    goto periods;
+  }
+  if (!strcasecmp(task[0].host, "ANYHOST")) {
+    gprint (GP_LOG, " task host selected by controller\n");
+    goto periods;
+  }
+  if (task[0].host_required) {
+    gprint (GP_LOG, " host %s (required)\n", task[0].host);
+  } else {
+    gprint (GP_LOG, " host %s (desired)\n", task[0].host);
+  }
+
+periods:
+  gprint (GP_LOG, " time periods: exec: %f  poll: %f  timeout: %f\n", 
+	  task[0].exec_period, task[0].poll_period, task[0].timeout_period);
+
+  for (j = 0; j < tasks[i][0].Nranges; j++) {
+    switch (tasks[i][0].ranges[j].type) {
+      case RANGE_ABS:
+	start = ohana_sec_to_date (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_date (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_DAY:
+	start = ohana_sec_to_hms (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_hms (tasks[i][0].ranges[j].stop);
+	break;
+      case RANGE_WEEK:
+	start = ohana_sec_to_day (tasks[i][0].ranges[j].start);
+	stop  = ohana_sec_to_day (tasks[i][0].ranges[j].stop);
+	break;
+      default:
+	abort ();
+    }
+    if (tasks[i][0].ranges[j].include) {
+      gprint (GP_LOG, "     active : %s - %s", start, stop);
+      if (tasks[i][0].ranges[j].Nmax) gprint (GP_LOG, " (%d of %d)", tasks[i][0].ranges[j].Nrun, tasks[i][0].ranges[j].Nmax);
+      gprint (GP_LOG, "\n");
+    } else {
+      gprint (GP_LOG, "     avoid  : %s - %s\n", start, stop);
+    }
+    free (start);
+    free (stop);
+  }
+
+  gprint (GP_LOG, "\n pre-execute macro\n");
+  ListMacro (task[0].exec);
+
+  gprint (GP_LOG, "\n timeout macro\n");
+  ListMacro (task[0].timeout);
+
+  gprint (GP_LOG, "\n crash macro\n");
+  ListMacro (task[0].crash);
+
+  gprint (GP_LOG, "\n default exit macro\n");
+  ListMacro (task[0].defexit);
+
+  for (i = 0; i < task[0].Nexit; i++) {
+    gprint (GP_LOG, "\n exit macro (status == %d)\n", atoi(task[0].exit[i][0].name));
+    ListMacro (task[0].exit[i]);
+  }
+
+  return (TRUE);
+}
+
+/* make a new named task */
+int FreeTask (Task *task) {
+  
+  int i;
+
+  if (task == NULL) return (FALSE);
+  
+  if (task[0].name != NULL) free (task[0].name);
+  if (task[0].host != NULL) free (task[0].host);
+  if (task[0].argv != NULL) {
+    for (i = 0; i < task[0].argc; i++) {
+      free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+  if (task[0].optv != NULL) {
+    for (i = 0; i < task[0].optc; i++) {
+      free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+  if (task[0].exec != NULL) {
+    FreeMacro (task[0].exec);
+    free (task[0].exec);
+  }
+  if (task[0].crash != NULL) {
+    FreeMacro (task[0].crash);
+    free (task[0].crash);
+  }
+  if (task[0].timeout != NULL) {
+    FreeMacro (task[0].timeout);
+    free (task[0].timeout);
+  }
+  for (i = 0; i < task[0].Nexit; i++) {
+    if (task[0].exit[i] != NULL) {
+      FreeMacro (task[0].exit[i]);
+    }
+    free (task[0].exit[i]);
+  }
+  free (task[0].exit);
+
+  if (task[0].ranges != NULL) {
+    free (task[0].ranges);
+  }
+  return (TRUE);
+}
+
+/**** new task functions ***/
+
+/* make a new named task */
+Task *CreateTask (char *name) {
+  
+  ALLOCATE (NewTask, Task, 1);
+
+  NewTask[0].name = strcreate (name);;
+
+  NewTask[0].host = NULL;
+  NewTask[0].host_required = FALSE;
+
+  NewTask[0].argc = 0;
+  NewTask[0].argv = NULL;
+
+  NewTask[0].optc = 0;
+  NewTask[0].optv = NULL;
+
+  NewTask[0].stdout_dump = NULL;
+  NewTask[0].stderr_dump = NULL;
+
+  NewTask[0].exec = NULL;
+  NewTask[0].crash = NULL;
+  NewTask[0].timeout = NULL;
+  NewTask[0].defexit = NULL;
+
+  NewTask[0].Nexit = 0;
+  NewTask[0].NEXIT = 10;
+  ALLOCATE (NewTask[0].exit, Macro *, NewTask[0].NEXIT);
+  /* don't free tasks[0].exit, keep at least 1 allocated */
+
+  NewTask[0].exec_period = 1.0;
+  NewTask[0].poll_period = 1.0;
+  NewTask[0].timeout_period = 1.0;
+
+  NewTask[0].Nranges = 0;
+  ALLOCATE (NewTask[0].ranges, TimeRange, 1);
+
+  /* init task timer (is reset by 'run') */  
+  gettimeofday (&NewTask[0].last, (void *) NULL);
+  NewTask[0].Nmax = 0;  /* default value means 'no limit' */
+
+  NewTask[0].NpendingMax = 0;  /* default value means 'no limit' */
+  NewTask[0].Npending = 0;  /* default value means 'no limit' */
+
+  NewTask[0].Njobs = 0;
+  NewTask[0].Nsuccess = 0;
+  NewTask[0].Nfailure = 0;
+  NewTask[0].Ntimeout = 0;
+
+  /* jobs timing statistics */
+  NewTask[0].dtimeAve_alljobs =  0.0;
+  NewTask[0].dtimeMin_alljobs = -1.0;
+  NewTask[0].dtimeMax_alljobs =  0.0;
+
+  NewTask[0].dtimeAve_success =  0.0;
+  NewTask[0].dtimeMin_success = -1.0;
+  NewTask[0].dtimeMax_success =  0.0;
+
+  NewTask[0].dtimeAve_failure =  0.0;
+  NewTask[0].dtimeMin_failure = -1.0;
+  NewTask[0].dtimeMax_failure =  0.0;
+
+  NewTask[0].active = TRUE;
+  return (NewTask);
+}
+
+/* remove the task from the task list */
+int RemoveTask (Task *task) {
+  
+  int i, Nt;
+
+  /* find task in task list */
+  Nt = -1;
+  for (i = 0; i < Ntasks; i++) {
+    if (task == tasks[i]) {
+      Nt = i;
+      break;
+    }
+  }
+  if (Nt == -1) {
+    gprint (GP_ERR, "programming error: task not found\n");
+    return (FALSE);
+  }
+  for (i = Nt; i < Ntasks - 1; i++) {
+    tasks[i] = tasks[i+1];
+  }
+  Ntasks --;
+  return (TRUE);
+}
+
+int ValidateTask (Task *task, int RequireStatic) {
+
+  int i, hash;
+
+  /* is a static command defined? */
+  if (task[0].argc != 0) {
+    if (task[0].argv == NULL) {
+      gprint (GP_ERR, "task command arguments not defined (programming error)\n");
+      return (FALSE);
+    }
+    return (TRUE);
+  }
+  if (RequireStatic) {
+    gprint (GP_ERR, "task command not defined\n");
+    return (FALSE);
+  }
+
+  /* no static command; dynamic command? */
+  if (task[0].exec != NULL) {
+    for (i = 0; i < task[0].exec[0].Nlines; i++) {
+      hash = TaskHash (task[0].exec[0].line[i]);
+      if (hash == TASK_COMMAND) return (TRUE);
+    }
+  }
+  gprint (GP_ERR, "task command not defined\n");
+  return (FALSE);
+}
+
+int RegisterNewTask () {
+  
+  int N;
+
+  N = Ntasks;
+  Ntasks ++;
+  if (Ntasks == NTASKS) {
+    NTASKS += 20;
+    REALLOCATE (tasks, Task *, NTASKS);
+  }
+  tasks[N] = NewTask;
+  NewTask = NULL;
+  return (TRUE);
+}
+
+int DeleteNewTask () {
+  if (NewTask != NULL) {
+    FreeTask (NewTask);
+    free (NewTask);
+    NewTask = NULL;
+  }
+  return (TRUE);
+}
+
+Task *SetNewTask (Task *task) {
+  NewTask = task;
+  return (task);
+}
+
+Task *GetNewTask () {
+  return (NewTask);
+}
+
+Task *GetActiveTask () {
+  Task *task;
+  if (ActiveTask < 0) return (NULL);
+  task = tasks[ActiveTask];
+  return (task);
+}
+
+int TaskHash (char *input) {
+  
+  int hash;
+  char *command;
+
+  hash = TASK_NONE;
+
+  command = thisword (input);
+  if (command == NULL) return (TASK_EMPTY);
+
+  if (command[0] == '#')                  hash = TASK_COMMENT;
+  if (!strcasecmp (command, "END"))       hash = TASK_END;
+  if (!strcasecmp (command, "HOST"))      hash = TASK_HOST;
+  if (!strcasecmp (command, "NMAX"))      hash = TASK_NMAX;
+  if (!strcasecmp (command, "ACTIVE"))    hash = TASK_ACTIVE;
+  if (!strcasecmp (command, "TRANGE"))    hash = TASK_TRANGE;
+  if (!strcasecmp (command, "STDOUT"))    hash = TASK_STDOUT;
+  if (!strcasecmp (command, "STDERR"))    hash = TASK_STDERR;
+  if (!strcasecmp (command, "COMMAND"))   hash = TASK_COMMAND;
+  if (!strcasecmp (command, "OPTIONS"))   hash = TASK_OPTIONS;
+  if (!strcasecmp (command, "PERIODS"))   hash = TASK_PERIODS;
+  if (!strcasecmp (command, "NPENDING"))  hash = TASK_NPENDING;
+  if (!strcasecmp (command, "TASK.EXIT")) hash = TASK_EXIT;
+  if (!strcasecmp (command, "TASK.EXEC")) hash = TASK_EXEC;
+
+  free (command);
+  return (hash);
+}
+
+/*** task timer functions ***/
+
+double GetTaskTimer (struct timeval start, int verbose) {
+
+  double dtime;
+  struct timeval now;
+  
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, start);
+  
+  if (verbose) {
+      fprintf (stderr, "tt: %d %6d  - %d %6d : %f\n", 
+	       (int) now.tv_sec, (int) now.tv_usec, 
+	       (int) start.tv_sec, (int) start.tv_usec, dtime);
+  }
+
+  return (dtime);
+}
+
+void SetTaskTimer (struct timeval *timer) {
+  gettimeofday (timer, (void *) NULL);
+}
+
+/* start the clock for all tasks */
+void InitTaskTimers () {
+
+  Task *task;
+  double isec, fsec;
+
+  while ((task = NextTask ()) != NULL) {
+    gettimeofday (&task[0].last, (void *) NULL);
+    fsec = modf (task[0].exec_period, &isec);
+    task[0].last.tv_sec -= isec;
+    task[0].last.tv_usec -= 1e6*fsec;
+ }
+}
+
+/* must call this after updating the corresponding counter */
+void UpdateTaskTimerStats (Task *task, int mode, double dtime) {
+
+  double total;
+
+  switch (mode) {
+    case TIMER_ALLJOBS:
+      total = task[0].dtimeAve_alljobs * (task[0].Njobs - 1);
+      total += dtime;
+      task[0].dtimeAve_alljobs = total / (float) task[0].Njobs;
+      if (task[0].dtimeMin_alljobs < 0) {
+	task[0].dtimeMin_alljobs = dtime;
+      } else {
+	task[0].dtimeMin_alljobs = MIN (task[0].dtimeMin_alljobs, dtime);
+      }
+      task[0].dtimeMax_alljobs = MAX (task[0].dtimeMax_alljobs, dtime);
+      break;
+    case TIMER_SUCCESS:
+      total = task[0].dtimeAve_success * (task[0].Nsuccess - 1);
+      total += dtime;
+      task[0].dtimeAve_success = total / (float) task[0].Nsuccess;
+      if (task[0].dtimeMin_success < 0) {
+	task[0].dtimeMin_success = dtime;
+      } else {
+	task[0].dtimeMin_success = MIN (task[0].dtimeMin_success, dtime);
+      }
+      task[0].dtimeMax_success = MAX (task[0].dtimeMax_success, dtime);
+      break;
+    case TIMER_FAILURE:
+      total = task[0].dtimeAve_failure * (task[0].Nfailure - 1);
+      total += dtime;
+      task[0].dtimeAve_failure = total / (float) task[0].Nfailure;
+      if (task[0].dtimeMin_failure < 0) {
+	task[0].dtimeMin_failure = dtime;
+      } else {
+	task[0].dtimeMin_failure = MIN (task[0].dtimeMin_failure, dtime);
+      }
+      task[0].dtimeMax_failure = MAX (task[0].dtimeMax_failure, dtime);
+      break;
+    default:
+      abort();
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/client_shell.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/client_shell.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/client_shell.c	(revision 17422)
@@ -0,0 +1,99 @@
+# include "opihi.h"
+
+/******************/
+int client_shell (int argc, char **argv) {
+
+  int Nbad, status;
+  char *line, *prompt, *history;
+  pid_t ppid;
+
+  general_init (&argc, argv);
+  program_init (&argc, argv);
+  startup (&argc, argv);
+  prompt = get_variable("PROMPT");
+  history = get_variable("HISTORY");
+  welcome ();
+
+  /* attempt to connect to the pantasks server (exit on failure) */
+  multicommand_InitServer ();
+
+  Nbad = 0;
+  while (1) {  /** must exit with command "exit" or "quit" */
+    if (Nbad == 10) exit (3);
+
+    line = readline (prompt);
+
+    if (line == NULL) { 
+      
+      ppid = getppid();
+      if (ppid == 1) {
+	gprint (GP_ERR, "caught parent shutdown\n");
+	exit (2);
+      }
+      if (!isatty (STDIN_FILENO)) exit (2);
+      gprint (GP_ERR, "Use \"quit\" to exit\n");
+      Nbad ++;
+      continue;
+    }
+    Nbad = 0;
+    ohana_memregister (line);
+
+    stripwhite (line);
+
+    if (*line) {
+	status = multicommand (line);
+	add_history (line);
+	append_history (1, history);
+    }
+    free (line);
+  }
+}
+
+/* 
+   startup sequence:
+
+   - general_init
+   - program_init
+   - startup (exit if non-interactive)
+   - welcome
+
+*/
+
+/* client issues and questions:
+
+- need to identify the commands to be caught by the client
+- pass each input line through the command parser first, then
+  send to server if not identified as a valid command
+- this raises the question of variables: do we parse variables
+  at the client level?  I would not think so.  what about input?
+  if the input is to be parsed by the client, but executed on the server, 
+  I'll need to re-work the input parsing system.  would be better 
+  for the server to parse the input command.  in this case, the client 
+  user needs to have access to the directories with input scripts.
+  this may be an advantage: it would allow some limitation on who can 
+  install server scripts.
+- variables, vectors and buffers: where are they valid (client or server?)
+  it seems they should all be defined locally on the server.  this makes
+  graphics plotting a server-side action as well.  this is probably easier,
+  but we do need to be careful about multiple clients trying to make plots
+  on the same window at the same time.  Exporting the window can be done 
+  for one client with the $KII variable, but could be tricky for more 
+  complex interactions
+- list of commands which clearly must be parsed by the client:
+  - quit / exit
+  - exec / !
+  - ?
+
+- i may need an alternative version of command, modified to avoid parsing the 
+  variables and vectors.  I also need an alternative version of the Init commands
+  to just list the blocked ones.
+
+- the client now executes all of the basic opihi commands, and only passes
+  through the pantasks commands.  The pantasks server interface should only
+  accept functions which immediately return, sending back the result to be
+  printed by the client.  
+
+  The server should not accept the 'task' commands from the client command-line.
+  To define and run macros or tasks on the server, these need to be added to 
+  a script which is loaded with a varient on the input command.  
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "pantasks.h"
+
+int controller_host    	PROTO((int, char **));
+int controller_exit    	PROTO((int, char **));
+int controller_status  	PROTO((int, char **));
+int controller_jobstack PROTO((int, char **));
+int controller_run     	PROTO((int, char **));
+int controller_stop    	PROTO((int, char **));
+int controller_check   	PROTO((int, char **));
+int controller_output  	PROTO((int, char **));
+int controller_pulse   	PROTO((int, char **));
+
+static Command controller_cmds[] = {
+  {1, "exit",     controller_exit,     "shutdown controller"},
+  {1, "host",     controller_host,     "define host for controller"},
+  {1, "check",    controller_check,    "check controller host/job"},
+  {1, "run",      controller_run,      "start controller operation / set run levels"},
+  {1, "stop",     controller_run,      "stop controller (no disconnect)"},
+  {1, "status",   controller_status,   "check controller status"},
+  {1, "jobstack", controller_jobstack, "check controller status"},
+  {1, "output",   controller_output,   "print controller output"},
+  {1, "pulse",    controller_pulse,    "set controller pulse"},
+};
+
+int controller (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: controller (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (controller_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", controller_cmds[i].name, controller_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindControllerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid controller command\n");
+    return (FALSE);
+  }
+
+  status = (*func)(argc - 1, argv + 1);
+  return (status);
+}
+
+CommandF *FindControllerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (controller_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (controller_cmds[i].name, cmd)) {
+      return (controller_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_check.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_check.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_check.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "pantasks.h"
+
+int controller_check (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 3) goto usage;
+  if (strcasecmp (argv[1], "JOB") && 
+      strcasecmp (argv[1], "HOST")) goto usage;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not running\n");
+    return (TRUE);
+  }
+
+  sprintf (command, "check %s %s", argv[1], argv[2]);
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) {
+    gprint (GP_LOG, "controller command sent\n");  
+    gprint (GP_LOG, "\n Nbytes received: %d\n", buffer.Nbuffer);  
+  }
+  gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: controller check job (jobID)\n");
+  gprint (GP_ERR, "USAGE: controller check host (hostID)\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_exit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_exit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_exit.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "pantasks.h"
+
+int controller_exit (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller exit TRUE\n");
+    return (FALSE);
+  }
+
+  if (strcasecmp (argv[1], "TRUE")) return (FALSE);
+
+  QuitController ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_host.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_host.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_host.c	(revision 17422)
@@ -0,0 +1,37 @@
+# include "pantasks.h"
+
+int controller_host (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: controller host (command) (hostname)\n");
+    return (FALSE);
+  }
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  sprintf (command, "host %s %s", argv[1], argv[2]);
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
+
+/* should I keep an internal host table so I can reload the 
+   hosts if the controller exits?
+
+   alternatively, that could be a user-level choice
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_jobstack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 17422)
@@ -0,0 +1,37 @@
+# include "pantasks.h"
+
+int controller_jobstack (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  // XXX this has an error?  test this out...
+  sprintf (command, "jobstack %s", argv[2]);
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_output.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_output.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_output.c	(revision 17422)
@@ -0,0 +1,13 @@
+# include "pantasks.h"
+
+int controller_output (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller status\n");
+    return (FALSE);
+  }
+
+  CheckControllerOutput ();
+  PrintControllerOutput ();
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_pulse.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_pulse.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_pulse.c	(revision 17422)
@@ -0,0 +1,26 @@
+# include "pantasks.h"
+
+int controller_pulse (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: controller pulse (usec)\n");
+    return (FALSE);
+  }
+
+  /* start controller connection (if needed) */
+  if (!StartController ()) {
+    gprint (GP_ERR, "failure to start pcontrol\n");
+    return (FALSE);
+  }
+
+  sprintf (command, "pulse %d", atoi(argv[1]));
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_run.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_run.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_run.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "pantasks.h"
+
+int controller_run (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help")) 
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: controller run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: controller stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+  if (argc == 1) {
+    strcpy (command, argv[0]);
+  } else {
+    sprintf (command, "run %s", argv[1]);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_status.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_status.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_status.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int controller_status (int argc, char **argv) {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: controller status\n");
+    return (FALSE);
+  }
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    gprint (GP_LOG, "controller is not active\n");
+    return (TRUE);
+  }
+
+
+  sprintf (command, "status");
+  InitIOBuffer (&buffer, 0x100);
+
+  SerialThreadLock ();
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  SerialThreadUnlock ();
+
+  if (status) {
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not communicating\n");
+  }
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_threads.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_threads.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/controller_threads.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "pantasks.h"
+
+/** things related to CheckController **/
+
+static int CheckControllerRun = FALSE;
+
+void CheckControllerSetState (int state) {
+  CheckControllerRun = state;
+}
+int CheckControllerGetState () {
+  return (CheckControllerRun);
+}
+
+void *CheckControllerThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckControllerRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    CheckController ();
+    CheckControllerOutput ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "C");
+    // fprintf (stderr, "**** C ****");
+    usleep (500000); // allow other threads a chance to run
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/delete.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/delete.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/delete.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+int delete_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc != 3) goto usage;
+
+  if (!strcasecmp (argv[1], "job")) {
+    JobID = atoi (argv[2]);
+
+    job = FindJob (JobID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      return (TRUE);
+    }
+
+    if (job[0].mode == JOB_LOCAL) {
+      if (!KillLocalJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	return (FALSE);
+      }
+    } else {
+      if (!KillControllerJob (job)) {
+	job[0].state = JOB_HUNG;
+	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	return (FALSE);
+      }
+    }    
+    DeleteJob (job);
+    gprint (GP_LOG, "job removed\n");
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "task")) {
+    gprint (GP_ERR, "delete task not implemented yet\n");
+    return (FALSE);
+  }
+
+usage:
+    gprint (GP_ERR, "USAGE: delete job (JobID)\n");
+    gprint (GP_ERR, "USAGE: delete task (TaskName)\n");
+    return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/flush.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/flush.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/flush.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "pantasks.h"
+
+int flush_jobs (int argc, char **argv) {
+
+  if (argc != 2) goto usage;
+
+  if (!strcasecmp (argv[1], "jobs")) {
+    FlushJobs ();
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_ERR, "USAGE: flush jobs\n");
+  return (FALSE);
+}
+
+/* find job from jobID, kill and delete */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/controller
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/controller	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/controller	(revision 17422)
@@ -0,0 +1,29 @@
+
+   controller (command) ...
+
+   Interact with the parallel controller 'pcontrol'.  This command
+   groups together several controller sub-commands, listed below.
+
+
+   controller host (hostname)
+
+   Define a new machine available to the parallel controller.  
+
+
+   controller check (host) (HostID)
+   controller check (job) (JobID)
+
+   Check on the status of a specific host or job known to the parallel
+   controller.
+
+
+   controller status
+
+   Query the parallel controller for its current status
+
+
+   controller output
+
+   Dump the contents of the parallel controller message buffers.
+   These are the messages from the controller itself, rather than the
+   messages generated by jobs run by the controller.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task	(revision 17422)
@@ -0,0 +1,27 @@
+
+   task (name)
+
+   Define or modify a task.  A task is defined by issuing a series of
+   task commands, listed below (see their individual help pages).  A
+   task describes the rules for constructing jobs.  At a minimum, a
+   task must include a command, defining the UNIX command to run for
+   each job.  Other task commands define optional concepts for the
+   task.  These include: 
+
+   - a macro to be run before the job is constructed (task.exec).
+   - a set of macros to be run when the job is completed (task.exit)
+   - time ranges in which the task should be run (or not) (trange) 
+   - required / desired parallel controller hosts (host)
+   - job contruction time interval (periods -exec)
+   - job polling time interval (periods -poll)
+   - job timeouts (periods -timeout)
+   - maximum number of jobs to construct
+
+   See also:
+   task.command
+   task.exit
+   task.exec
+   task.trange
+   task.nmax
+   task.host
+   task.periods
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.command
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.command	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.command	(revision 17422)
@@ -0,0 +1,15 @@
+
+   command (arg0) (arg1) ...
+
+   Define the rule for constructing a job command for a task.  The
+   arguments are UNIX command and the collection of command arguments.
+   The arguments may include references to Opihi variables.  The task
+   command may be included at the top level of the task, or it may be
+   defined within the task exec macro.  In the latter case, the
+   command is constructed only when the exec macro is run, every job
+   construction interval.  Thus, the command arguments may depend on
+   the result of the exec macro operation.   
+
+   See also:
+   task
+   task.exec
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.exec
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.exec	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/help/task.exec	(revision 17422)
@@ -0,0 +1,17 @@
+
+   task.exec
+
+   A special macro to be executed before constructing the job command.
+   The macro is defined by commands following the 'task.exec' command,
+   ending with the word 'end'.  If the exit status of the task.exec
+   macro is FALSE, then the job is NOT constructed.  This allows the
+   construction of a job to be conditional on other, external
+   quantites.  (Note: the exit status of the macro is false if 'break'
+   is called).  The task command may be defined within the task.exec
+   macro, in which case the command is constructed only when the exec
+   macro is run, every job construction interval.  Thus, the command
+   arguments may depend on the result of the exec macro operation.
+
+   See also:
+   task
+   task.command
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+int task_stdout     PROTO((int, char **));
+int task_stderr     PROTO((int, char **));
+int run             PROTO((int, char **));
+int stop            PROTO((int, char **));
+int halt            PROTO((int, char **));
+int flush_jobs      PROTO((int, char **));
+int pulse           PROTO((int, char **));
+int showtask        PROTO((int, char **));
+int status_sys      PROTO((int, char **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "halt",       halt,          "halt the scheduler (no job harvesting)"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
+  {1, "options",    task_options,  "define optional variables associated with the job task"},
+  {1, "periods",    task_periods,  "define time scales for a task"},
+  {1, "pulse",      pulse,         "set the scheduler update period"},
+  {1, "run",        run,           "run the scheduler"},
+  {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_sys,    "get system or task status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "stop",       stop,          "stop the scheduler (continue job harvesting)"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+}; 
+
+void InitPantasks () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasks () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_client.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_client.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_client.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+
+int invalid           PROTO((int, char **));
+int server_shutdown   PROTO((int, char **));
+int server_disconnect PROTO((int, char **));
+int server_connect    PROTO((int, char **));
+
+/* we also list here commands which are not valid for the client, but represent valid
+   pantasks commands.  these are caught by the client and sent to the 'invalid'
+   function */
+
+static Command cmds[] = {  
+  {1, "task",       invalid,  	 "define a schedulable task"},
+  {1, "task.exit",  invalid,  	 "define exit macros for a task"},
+  {1, "task.exec",  invalid,  	 "define pre-exec macro for a task"},
+
+  {1, "shutdown",   server_shutdown,   "send exit signal to server"},
+  {1, "disconnect", server_disconnect, "close connection to server"},
+  {1, "connect",    server_connect,    "open connection to server"},
+
+}; 
+
+void InitPantasksClient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
+
+void FreePantasksClient () {
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_server.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_server.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/init_server.c	(revision 17422)
@@ -0,0 +1,81 @@
+# include "pantasks.h"
+
+int controller      PROTO((int, char **));
+int task            PROTO((int, char **));
+int task_host       PROTO((int, char **));
+int task_nmax       PROTO((int, char **));
+int task_npending   PROTO((int, char **));
+int task_active     PROTO((int, char **));
+int task_trange     PROTO((int, char **));
+int task_macros     PROTO((int, char **));
+int task_command    PROTO((int, char **));
+int task_options    PROTO((int, char **));
+int task_periods    PROTO((int, char **));
+int task_stdout     PROTO((int, char **));
+int task_stderr     PROTO((int, char **));
+int pulse           PROTO((int, char **));
+int flush_jobs      PROTO((int, char **));
+int status_server   PROTO((int, char **));
+int showtask        PROTO((int, char **));
+int kill_job        PROTO((int, char **));
+int delete_job      PROTO((int, char **));
+int verbose         PROTO((int, char **));
+int version         PROTO((int, char **));
+int server          PROTO((int, char **));
+int ipptool2book    PROTO((int, char **));
+
+int server_run	    PROTO((int, char **));
+int server_stop	    PROTO((int, char **));
+int server_halt	    PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "active",     task_active,   "set the active state of a task"},
+  {1, "command",    task_command,  "define executed command for a task"},
+  {1, "controller", controller,    "controller commands"},
+  {1, "delete",     delete_job,    "delete job"},
+  {1, "host",       task_host,     "define host machine for a task"},
+  {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
+  {1, "kill",       kill_job,      "kill job"},
+  {1, "nmax",       task_nmax,     "define maximum number of jobs for a task"},
+  {1, "options",    task_options, "define optional variables associated with the job task"},
+  {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
+  {1, "periods",    task_periods,  "define time scales for a task"},
+  {1, "pulse",      pulse,         "set the scheduler update period"},
+  {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
+  {1, "server",     server,        "server-specific commands"},
+  {1, "showtask",   showtask,      "list a task"},
+  {1, "status",     status_server, "get system status"},
+  {1, "stderr",     task_stderr,   "define a file for the job stderr dump"},
+  {1, "stdout",     task_stdout,   "define a file for the job stdout dump"},
+  {1, "task",       task,          "define a schedulable task"},
+  {1, "task.exec",  task_macros,   "define pre-exec macro for a task"},
+  {1, "task.exit",  task_macros,   "define exit macros for a task"},
+  {1, "trange",     task_trange,   "define valid/invalid time periods for a task"},
+  {1, "verbose",    verbose,       "set/toggle verbose mode"},
+  {1, "version",    version,       "show version information"},
+
+  {1, "run",        server_run,    "run scheduler"},
+  {1, "stop",       server_stop,   "stop scheduler"},
+  {1, "halt",       server_halt,   "halt scheduler"},
+}; 
+
+void InitPantasksServer () {
+  
+  int i;
+
+  InitTasks ();
+  InitJobs ();
+  InitJobIDs ();
+  InitInputs ();
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+}
+
+void FreePantasksServer () {
+  FreeTasks ();
+  FreeJobs ();
+  FreeJobIDs ();
+  FreeInputs ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/input_threads.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/input_threads.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/input_threads.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "pantasks.h"
+
+/** things related to CheckInputs **/
+
+static int CheckInputsRun = TRUE;
+
+void CheckInputsSetState (int state) {
+  CheckInputsRun = state;
+}
+int CheckInputsGetState () {
+  return (CheckInputsRun);
+}
+
+void *CheckInputsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckInputsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    CheckInputs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "I");
+    usleep (10000); // allow other threads a chance to run
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/invalid.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/invalid.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/invalid.c	(revision 17422)
@@ -0,0 +1,8 @@
+# include "pantasks.h"
+
+int invalid (int argc, char **argv) {
+
+  gprint (GP_ERR, "%s is not valid for the pantasks client\n", argv[0]);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ipptool2book.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ipptool2book.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/ipptool2book.c	(revision 17422)
@@ -0,0 +1,246 @@
+# include "pantasks.h"
+
+# define FREEKEYS \
+  if (keys) { for (i = 0; i < Nkeys; i++) { free (keys[i]); } free (keys); } \
+  for (i = 0; i < setWordN; i++) { free (setWordList[i]); free (setWordValue[i]); } \
+  free (setWordValue); free (setWordList);
+
+// convert the named queue with ipptool metadata output into book format
+int ipptool2book (int argc, char **argv) {
+
+  int i, N, onPage, found, Unique, Nkeys, NKEYS;
+  char *line, *tmpword, *tmpvalue;
+  char pagename[512], *bookName, **keys, *p, *q;
+  char **setWordList;
+  char **setWordValue;
+  int setWordN, setWordNalloc;
+
+  Page *page = NULL;
+  Book *book = NULL;
+  Queue *queue = NULL;
+
+  /* supply additional constant words */
+  setWordN = 0;
+  setWordNalloc = 10;
+  ALLOCATE (setWordList, char *, setWordNalloc);
+  ALLOCATE (setWordValue, char *, setWordNalloc);
+  while ((N = get_argument (argc, argv, "-setword"))) {
+    remove_argument (N, &argc, argv);
+    setWordList[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordValue[setWordN] = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    setWordN ++;
+    if (setWordN >= setWordNalloc) {
+      setWordNalloc += 10;
+      REALLOCATE (setWordList, char *, setWordNalloc);
+      REALLOCATE (setWordValue, char *, setWordNalloc);
+    }      
+  }
+
+  Unique = FALSE;
+  if ((N = get_argument (argc, argv, "-uniq"))) {
+    remove_argument (N, &argc, argv);
+    Unique = TRUE;
+  }
+
+  Nkeys = 0;
+  keys = NULL;
+  if ((N = get_argument (argc, argv, "-key"))) {
+    remove_argument (N, &argc, argv);
+
+    /* parse the key (word:word:word) into constituents */
+    NKEYS = 10;
+    ALLOCATE (keys, char *, NKEYS);
+    
+    p = q = argv[N];
+    while (q != NULL) {
+      q = strchr (p, ':');
+      keys[Nkeys] = (q == NULL) ? strcreate (p) : strncreate (p, q - p);
+      p = q + 1;
+      Nkeys ++;
+      CHECK_REALLOCATE (keys, char *, NKEYS, Nkeys, 10);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: ipptool2book (queue) (book) [-uniq] [-key key[:key..]]\n");
+    FREEKEYS;
+    return (FALSE);
+  }
+
+  queue = FindQueue (argv[1]);
+  if (queue == NULL) {
+    gprint (GP_ERR, "queue %s not found\n", argv[1]);
+    FREEKEYS;
+    return (FALSE);
+  }
+    
+  book = CreateBook (argv[2]);
+
+  /* the first non-whitespace line of the queue should contain:
+     BookName MULTI
+  */
+
+  /* scan for first non-emtpy line */
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      FREEKEYS;
+      return (TRUE); // no output in queue - not an error?
+    }
+    stripwhite (line);
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    if (*line) break;
+    free (line);
+  }
+  bookName = thisword (line);
+  tmpword = nextword (line);
+
+  if (strcmp(tmpword, "MULTI")) {
+    gprint (GP_ERR, "ERROR: missing metadata output name on first line\n");
+    free (bookName);
+    free (line);
+    FREEKEYS;
+    return FALSE;
+  }
+  free (line);
+  
+  onPage = FALSE;
+  while (TRUE) {
+    line = PopQueue (queue);
+    if (!line) {
+      free (bookName);
+      if (onPage) {
+	gprint (GP_ERR, "ERROR: unterminated metadata\n");
+	FREEKEYS;
+	return (FALSE);
+      }
+      FREEKEYS;
+      return (TRUE);
+    }
+    stripwhite (line);
+    if (!*line) {
+      free (line);
+      continue;
+    }
+    if (line[0] == '#') {
+      free (line);
+      continue;
+    }
+    
+    tmpword = thisword (line);
+    if (tmpword == NULL) abort(); // should not happen if line exists
+
+    if (!onPage) {
+      if (strcmp(tmpword, bookName)) {
+	gprint (GP_ERR, "ERROR: unexpected metadata name %s\n", tmpword);
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return (FALSE);
+      }
+      free (tmpword);
+      tmpword = thisword (nextword (line));
+      if (strcmp(tmpword, "METADATA")) {
+	gprint (GP_ERR, "ERROR: missing METADATA tag\n");
+	free (line);
+	free (tmpword);
+	free (bookName);
+	FREEKEYS;
+	return FALSE;
+      }
+      onPage = TRUE;
+      // we don't know the page name until we load its data?
+      sprintf (pagename, "page.%03d", book[0].Npages);
+      page = CreatePage (book, pagename);
+      free (line);
+      free (tmpword);
+      continue;
+    } 
+
+    /* close out the page */
+    if (!strcmp(tmpword, "END")) {
+      // XXX set the page name based on the contents
+      free (tmpword);
+      free (line);
+
+      if (keys) {
+	memset (pagename, 0, 512);
+	for (i = 0; i < Nkeys; i++) {
+	  tmpword = BookGetWord (page, keys[i]);
+	  if (tmpword == NULL) {
+	    gprint (GP_ERR, "ERROR: missing key %s for ID\n", keys[i]);
+	    FREEKEYS;
+	    return (FALSE);
+	  }
+	  if (i > 0) strcat (pagename, ":");
+	  strcat (pagename, tmpword);
+	}
+	free (page[0].name);
+	page[0].name = strcreate (pagename);
+      }
+
+      // add the fixed words to the page
+      for (i = 0; i < setWordN; i++) {
+	BookSetWord (page, setWordList[i], setWordValue[i]);
+      }
+
+      if (Unique) {
+	/* search for an existing page with this name
+	   delete this one if one is found */
+	/* XXX don't attach this page to the book until this step is passed */	
+
+	found = FALSE;
+	for (i = 0; !found && (i < book[0].Npages); i++) {
+	  if (book[0].pages[i] == page) continue;
+	  if (!strcmp (book[0].pages[i][0].name, page[0].name)) {
+	    found = TRUE;
+	  }
+	}
+	if (found) {
+	  DeletePage (book, page);
+	}
+      }
+
+      onPage = FALSE;
+      continue;
+    }
+
+    tmpvalue = thisword (nextword(nextword(line)));
+    if (tmpvalue == NULL) {
+      gprint (GP_ERR, "ERROR: missing value for %s\n", tmpword);
+      free (tmpword);
+      free (line);
+      free (bookName);
+      FREEKEYS;
+      return (FALSE);
+    }
+    BookSetWord (page, tmpword, tmpvalue);
+    free (tmpword);
+    free (tmpvalue);
+    free (line);
+  }    
+  FREEKEYS;
+  return (FALSE);
+}
+
+/* scan through the queue lines.  these should look like:  
+
+bookName METADATA
+key type value
+key type value
+...
+END
+
+bookName METADATA
+key type value
+key type value
+...
+END
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "pantasks.h"
+
+/* this will require a bit of care: to define a job, we need
+   to specify:
+   - the command 
+     this is defined in the job command: job argv0 argv1 argv2...
+   - timeout (an option to the command)
+   - exit macros?
+     do nothing by default?
+   - stderr / stdout disposal?
+     save in a specific buffer by default?
+     send to a file?
+
+   the command can look just like the controller equivalent one:
+   job [-host host] [-timeout timeout] args...
+
+*/
+
+int job (int argc, char **argv) {
+
+  char *Host, **targv;
+  int i, N, Mode, targc, Timeout;
+  IDtype JobID;
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    if (Mode == PCONTROL_JOB_WANTHOST) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      FREE (Host);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    FREE (Host);
+    return (FALSE);
+  }
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  JobID = AddJob (Host, Mode, Timeout, targc, targv);
+  gprint (GP_LOG, "JobID: %d\n", JobID);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job_threads.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job_threads.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/job_threads.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "pantasks.h"
+
+/** things related to CheckJobs **/
+
+static int CheckJobsRun = FALSE;
+
+void CheckJobsSetState (int state) {
+  CheckJobsRun = state;
+}
+int CheckJobsGetState () {
+  return (CheckJobsRun);
+}
+
+void *CheckJobsThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+  float next_timeout;
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckJobsRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckJobs ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "J");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/kill.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/kill.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/kill.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int kill_job (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  job = FindJob (JobID);
+  if (job == NULL) {
+    gprint (GP_LOG, "job not found\n");
+    return (TRUE);
+  }
+
+  if (job[0].mode == JOB_LOCAL) {
+    if (!KillLocalJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      return (FALSE);
+    }
+  } else {
+    if (!KillControllerJob (job)) {
+      job[0].state = JOB_HUNG;
+      if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      return (FALSE);
+    }
+  }    
+  DeleteJob (job);
+  gprint (GP_LOG, "job removed\n");
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/notes.txt	(revision 17422)
@@ -0,0 +1,36 @@
+
+PanTasks Client / Server design
+
+Client
+
+client uses readline to parse the incoming commands commands are only
+validated by the client before being sent to the server:
+
+client loop:
+
+   line = readline ()
+   validate argv[0]
+   send to server
+   wait for response?
+
+Server
+
+Server does not use readline at all.  server has two threads.  first
+thread listens for commands from the clients.  how many clients?  use
+poll to wait for data?  can we manage enough clients with a single
+thread?
+
+the second thread runs the scheduler loop: this is the same background
+check that is being run by the current model on the timeouts.
+
+server loop:
+
+  check socket for new incoming client connection
+  check known clients for new messages
+    execute new command
+    send response to the client
+  run the scheduler for a single loop
+
+client / server communication is modelled after addstar client /
+server?
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 17422)
@@ -0,0 +1,94 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  pthread_t jobsThread;
+  pthread_t tasksThread;
+  pthread_t controllerThread;
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitPantasks ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  { 
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  /* start up the background threads here */
+  pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
+  pthread_create (&jobsThread,       NULL, &CheckJobsThread, 	   NULL);
+  pthread_create (&controllerThread, NULL, &CheckControllerThread, NULL);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreePantasks ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
+/* pantasks runs three (or four) threads:
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   - the main thread runs readline and accepts user input
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_client.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_client.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_client.c.in	(revision 17422)
@@ -0,0 +1,78 @@
+# include "pantasks.h"
+
+# define opihi_name "pantasks client shell"
+# define opihi_prompt "pantasks: "
+# define opihi_description "parallel task scheduler\n"
+# define opihi_history ".pantasks"
+# define opihi_rcfile ".pantasksrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitAstro ();
+  InitPantasksClient ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+      char *helpdir;
+      char *modules;
+      static char *datadir = "@DATADIR@";
+      ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+      sprintf (helpdir, "%s/help", datadir);
+      set_str_variable ("HELPDIR", helpdir);
+      free (helpdir);
+      ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+      sprintf (modules, "%s/modules", datadir);
+      set_str_variable ("MODULES:0", modules);
+      set_int_variable ("MODULES:n", 1);
+      free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreeData ();
+  FreeAstro ();
+  FreePantasksClient ();
+  return;
+}
+
+/* default signal handling */
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to client_shell: this opihi tool uses a slightly different
+   shell function from the standard tools */
+int main (int argc, char **argv) {
+  int status;
+  status = client_shell (argc, argv);
+  exit (status);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_server.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 17422)
@@ -0,0 +1,110 @@
+# include "pantasks.h"
+
+/* pantasks-server is not a readline program.
+   it runs three (or four) threads:
+   - one thread listens on the socket for incomming connections 
+     and performs client validation
+   - one thread parses commands from the connected clients
+   - one thread runs the scheduler loop
+   - one thread may run the controller loop independently
+   
+   * note: the client shell traps certain commands and performs them
+   locally. at a minimum: help.  perhaps: input, other I/O commands?
+*/
+
+/* program-dependent initialization */
+int main (int argc, char **argv) {
+  
+  char log_stdout[128], log_stderr[128];
+  pthread_t jobsThread;
+  pthread_t tasksThread;
+  pthread_t inputsThread;
+  pthread_t clientsThread;
+  pthread_t controllerThread;
+  int InitSocket, BindSocket;
+  SockAddress Address;
+
+  general_init (&argc, argv);
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  DefineValidIP ();
+
+  /* XXX not sure what is a good setting for this in the server */
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitData ();
+  InitPantasksServer ();
+
+  { 
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  signal (SIGPIPE, gotsignal);
+  // signal (SIGTSTP, gotsignal);
+  // signal (SIGTTIN, gotsignal);
+  signal (SIGINT, SIG_DFL);
+
+  startup (&argc, argv);
+
+  /* start up the background threads here */
+  pthread_create (&clientsThread,    NULL, &ListenClients,    	   NULL);
+  pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
+  pthread_create (&jobsThread,       NULL, &CheckJobsThread,  	   NULL);
+  pthread_create (&controllerThread, NULL, &CheckControllerThread, NULL);
+  pthread_create (&inputsThread,     NULL, &CheckInputsThread,     NULL);
+
+  /* in this loop, we listen for incoming connections, validate, and
+     pass them to the ListenClient thread */
+
+  /* create the listening socket */
+  InitSocket = InitServerSocket (&Address);
+  
+  InitPassword ();
+
+  while (1) {
+
+    /* wait for clients to make connection */
+    BindSocket = WaitServerSocket (InitSocket, &Address);
+    if (BindSocket == -1) continue;
+
+    /* validate : wait for password */
+    if (!CheckPassword (BindSocket)) continue;
+    
+    AddNewClient (BindSocket);
+  }
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  QuitKapa ();
+  QuitController ();
+  ConfigFree ();
+  InitBasic ();
+  InitData ();
+  InitPantasksServer ();
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* these are needed to resolve symbols expected by libshell.so */
+void welcome () { }
+void program_init (int *argc, char **argv) {}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pulse.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pulse.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/pulse.c	(revision 17422)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/run.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/run.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/run.c	(revision 17422)
@@ -0,0 +1,25 @@
+# include "pantasks.h"
+
+// XXX for client/server, we need to simply start or stop the
+// appropriate threads
+// with one thread for each of the major actions, this would
+// make it easy to keep the controller running and stop the 
+// scheduler (don't run CheckTasks, but run everything else 
+// until nothing is left...
+
+int run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  CheckJobsSetState (TRUE);
+
+  // InitTaskTimers ();
+  // rl_event_hook = CheckSystem;
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server.c	(revision 17422)
@@ -0,0 +1,72 @@
+# include "pantasks.h"
+
+/* most of these are defined in cmd.basic */
+int quit            PROTO((int, char **));
+int input      	    PROTO((int, char **));
+int module     	    PROTO((int, char **));
+int server_load	    PROTO((int, char **));
+int server_run	    PROTO((int, char **));
+int server_stop	    PROTO((int, char **));
+int server_halt	    PROTO((int, char **));
+int cd              PROTO((int, char **));
+int pwd        	    PROTO((int, char **));
+int output     	    PROTO((int, char **));
+
+CommandF *FindServerCommand (char *cmd);
+
+static Command server_cmds[] = {
+  {1, "exit",   quit,   "shutdown server"},
+  {1, "quit",   quit,   "shutdown server"},
+  {1, "input",  input,  "load input file on server"},
+  {1, "module", module, "load module file on server"},
+  {1, "load",   server_load, "load input file on server"},
+  {1, "run",    server_run,  "run scheduler"},
+  {1, "stop",   server_stop, "stop scheduler"},
+  {1, "halt",   server_halt, "halt scheduler"},
+  {1, "cd",     cd,     "set local directory"},
+  {1, "pwd",    pwd,    "check local directory"},
+  {1, "output", output, "set server output destinations"},
+};
+
+int server (int argc, char **argv) {
+
+  int i, N, status;
+  CommandF *func;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: server (command) ... \n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "help")) {
+    N = sizeof (server_cmds) / sizeof (Command);
+
+    for (i = 0; i < N; i++) {
+      gprint (GP_LOG, "%-15s %s\n", server_cmds[i].name, server_cmds[i].help);
+    }
+    return (TRUE);
+  }
+
+  func = FindServerCommand (argv[1]);
+  if (func == NULL) {
+    gprint (GP_ERR, "invalid server command\n");
+    return (FALSE);
+  }
+
+  status = (*func)(argc - 1, argv + 1);
+  return (status);
+}
+
+CommandF *FindServerCommand (char *cmd) {
+
+  int i, N;
+
+  N = sizeof (server_cmds) / sizeof (Command);
+
+  for (i = 0; i < N; i++) {
+    if (!strcmp (server_cmds[i].name, cmd)) {
+      return (server_cmds[i].func);
+    }
+  }
+  return (NULL);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_connect.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_connect.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_connect.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_connect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  if (server) return (TRUE);
+
+  // close connection with server
+  // catch errors (don't exit)
+  multicommand_InitServer ();
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_disconnect.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_disconnect.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_disconnect.c	(revision 17422)
@@ -0,0 +1,20 @@
+# include "pantasks.h"
+
+int server_disconnect (int argc, char **argv) {
+
+  int server;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: disconnect\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_load.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_load.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_load.c	(revision 17422)
@@ -0,0 +1,18 @@
+# include "pantasks.h"
+
+int server_load (int argc, char **argv) {
+
+  char *input;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: server load (file)\n");
+    return (FALSE);
+  }
+
+  // the allocated input string is eventually freed 
+  // by the InputQueue system
+  input = strcreate (argv[1]);
+  AddNewInput (input);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_run.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_run.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_run.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "pantasks.h"
+
+int server_run (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server run\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (TRUE);
+  CheckJobsSetState (TRUE);
+  CheckControllerSetState (TRUE);
+  CheckInputsSetState (TRUE);
+  return (TRUE);
+}
+
+int server_stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  // CheckJobsSetState (FALSE);
+  // CheckControllerSetState (FALSE);
+  CheckInputsSetState (FALSE);
+  return (TRUE);
+}
+
+int server_halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: server halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckJobsSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  CheckInputsSetState (FALSE);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_shutdown.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_shutdown.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/server_shutdown.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "pantasks.h"
+
+int server_shutdown (int argc, char **argv) {
+
+  int status, server;
+  IOBuffer message;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  if (strcmp (argv[1], "now")) {
+    gprint (GP_ERR, "USAGE: shutdown now\n");
+    return (FALSE);
+  }
+  
+  server = getServer ();
+  
+  if (!server) return (TRUE);
+
+  // send the command to the server instead
+  status = SendMessage (server, "exit");
+  fprintf (stderr, "exit status of %d\n", status);
+
+  // try to read from the server until we get a status of 0
+  status = ExpectMessage (server, 2.0, &message);
+  fprintf (stderr, "message status of %d\n", status);
+
+  // XXX I should probably loop until we get an exit status of -3 or EPIPE
+
+  // close connection with server
+  multicommand_StopServer ();
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/showtask.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/showtask.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/showtask.c	(revision 17422)
@@ -0,0 +1,12 @@
+# include "pantasks.h"
+
+int showtask (int argc, char **argv) {
+
+  if (argc != 2) {
+    gprint (GP_LOG, "USAGE: showtask (task)\n");
+    return (FALSE);
+  }
+
+  ShowTask (argv[1]);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status.c	(revision 17422)
@@ -0,0 +1,70 @@
+# include "pantasks.h"
+
+int status_sys (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  if ((N = get_argument (argc, argv, "-tasks"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (FALSE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskinfo"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (TRUE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  ListTasks (FALSE);
+  ListJobs ();
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: over system status)\n");
+  gprint (GP_LOG, "       -tasks     : list defined tasks\n");
+  gprint (GP_LOG, "       -taskinfo  : details for tasks\n");
+  gprint (GP_LOG, "       -taskstats : processing statistics for tasks\n");
+  gprint (GP_LOG, "       -taskstatsreset : reset processing statistics for tasks\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status_server.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status_server.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/status_server.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "pantasks.h"
+
+int status_server (int argc, char **argv) {
+
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto help;
+  if (get_argument (argc, argv, "-help")) goto help;
+  if (get_argument (argc, argv, "--help")) goto help;
+
+  if ((N = get_argument (argc, argv, "-tasks"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (FALSE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskinfo"))) {
+    remove_argument (N, &argc, argv);
+    ListTasks (TRUE);
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstats"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ListTaskStats (argv[N]);
+    } else {
+      ListTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc == 2) {
+      ResetTaskStats (argv[N]);
+    } else {
+      ResetTaskStats (NULL);
+    }      
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "\n");
+  if (CheckTasksGetState()) {
+    gprint (GP_LOG, " Scheduler is running\n");
+  } else {
+    if (CheckJobsGetState()) {
+      gprint (GP_LOG, " Scheduler is stopped, harvesting jobs\n");
+    } else {
+      gprint (GP_LOG, " Scheduler is stopped\n");
+    }
+  }
+  if (CheckControllerStatus()) {
+    gprint (GP_LOG, " Controller is running\n");
+  } else {
+    gprint (GP_LOG, " Controller is stopped\n");
+  }
+  ListTasks (FALSE);
+  ListJobs ();
+  return (TRUE);
+
+help:
+  gprint (GP_LOG, "USAGE: status [options]\n");
+  gprint (GP_LOG, "       (without options: over system status)\n");
+  gprint (GP_LOG, "       -tasks     : list defined tasks\n");
+  gprint (GP_LOG, "       -taskinfo  : details for tasks\n");
+  gprint (GP_LOG, "       -taskstats : processing statistics for tasks\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/stop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/stop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/stop.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "pantasks.h"
+
+int stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stop\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  // CheckControllerSetState (FALSE);
+  // CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
+
+int halt (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: halt\n");
+    return (FALSE);
+  }
+
+  CheckTasksSetState (FALSE);
+  CheckControllerSetState (FALSE);
+  CheckJobsSetState (FALSE);
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task.c	(revision 17422)
@@ -0,0 +1,100 @@
+# include "pantasks.h"
+# define prompt "> "
+
+int task (int argc, char **argv) {
+
+  int hash;
+  int ThisList, status;
+  char *input, *outline;
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = FindTask (argv[1]);
+  if (task == NULL) { /**** new task ****/
+    task = CreateTask (argv[1]);
+  } else {
+    RemoveTask (task);
+    SetNewTask (task);
+  }
+  /* While a task is being defined, it is removed from the task list.  The new task is added to the task list
+     when the definition process is complete.  
+     XXX If an outstanding job has a task deleted, it will not be able to complete... */
+
+  /* read in task from appropriate source (keyboard or list) until end */
+
+  /* allowed tokens: command, host, stderr, periods, trange, nmax, task.exit, task.exec, end */
+
+  ThisList = current_list_depth();
+  while (1) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end task with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in task definition\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+    hash = TaskHash (input);
+    switch (hash) {
+
+      case TASK_EMPTY:
+      case TASK_COMMENT:
+	free (input);
+	break;
+
+      case TASK_END:
+	/* I need to add in a test here to see if all task elements 
+	   have been defined.  delete the task if not */
+	free (input);
+	/* validate the new task: all mandatory elements defined? */ 
+	if (!ValidateTask (task, FALSE)) {
+	  DeleteNewTask ();
+	  return (FALSE);
+	}
+	RegisterNewTask ();
+	return (TRUE);
+	break;
+
+      case TASK_TRANGE:
+      case TASK_NMAX:
+      case TASK_HOST:
+      case TASK_EXIT:
+      case TASK_EXEC:
+      case TASK_STDOUT:
+      case TASK_STDERR:
+      case TASK_COMMAND:
+      case TASK_OPTIONS:
+      case TASK_PERIODS:
+      case TASK_NPENDING:
+      case TASK_ACTIVE:
+	status = command (input, &outline, TRUE);
+	if (outline != NULL) free (outline);
+	/* what to do if command is invalid?
+	   if (!status) return (FALSE); */
+	break;
+
+      case TASK_NONE:
+	gprint (GP_ERR, "unknown task command %s\n", input);
+	break;
+    }
+  }
+
+usage:
+  gprint (GP_ERR, "USAGE: task -list\n");
+  gprint (GP_ERR, "USAGE: task -longlist\n");
+  gprint (GP_ERR, "USAGE: task -show (task)\n");
+  gprint (GP_ERR, "USAGE: task <name>\n");
+  gprint (GP_ERR, "  (enter commands & task functions; end with the word 'END')\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_active.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_active.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_active.c	(revision 17422)
@@ -0,0 +1,30 @@
+# include "pantasks.h"
+
+int task_active (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "true")) {
+    task[0].active = TRUE;
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "false")) {
+    task[0].active = FALSE;
+    return (TRUE);
+  }
+
+  gprint (GP_ERR, "ERROR: invalid option: %s\n", argv[1]);
+  return (FALSE);
+
+usage:
+  gprint (GP_ERR, "USAGE: active (true|false)\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_command.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_command.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_command.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "pantasks.h"
+
+int task_command (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: command <command> <arg>. ..\n");
+    gprint (GP_ERR, "  (define command for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by argv */
+  if (task[0].argc != 0) {
+    for (i = 0; i < task[0].argc; i++) {
+      if (task[0].argv[i] != NULL) free (task[0].argv[i]);
+    }
+    free (task[0].argv);
+  }
+
+  /* create new memory for argv */
+  task[0].argc = argc - 1;
+  ALLOCATE (task[0].argv, char *, MAX (task[0].argc, 1));
+  for (i = 0; i < task[0].argc; i++) {
+    task[0].argv[i] = strcreate (argv[i+1]);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_host.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_host.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_host.c	(revision 17422)
@@ -0,0 +1,42 @@
+# include "pantasks.h"
+
+int task_host (int argc, char **argv) {
+
+  int N, RequiredHost;
+  Task *task;
+
+  RequiredHost = FALSE;
+  if ((N = get_argument (argc, argv, "-required"))) {
+    remove_argument (N, &argc, argv);
+    RequiredHost = TRUE;
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: host <name> [-required]\n");
+    gprint (GP_ERR, "  define host machine for this task\n");
+    gprint (GP_ERR, "  -required flags indicates controller must use this host\n");
+    gprint (GP_ERR, "  value of 'local' for host indicates process not using controller\n");
+    gprint (GP_ERR, "  value of 'anyhost' for host indicates controller may assign at will\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  task[0].host_required = RequiredHost;
+
+  if (task[0].host != NULL) free (task[0].host);
+  task[0].host = NULL;
+
+  if (!strcasecmp (argv[1], "LOCAL")) return (TRUE);
+
+  task[0].host = strcreate (argv[1]);
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_macros.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_macros.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_macros.c	(revision 17422)
@@ -0,0 +1,151 @@
+# include "pantasks.h"
+# define D_NLINES 100
+# define prompt "> "
+
+int task_macros (int argc, char **argv) {
+
+  int i, N, NLINES, done, depth, ThisList;
+  char *input;
+  Macro *macro;
+  Task *task;
+
+  if (!strcmp (argv[0], "task.exec") && (argc != 1)) {
+    gprint (GP_ERR, "USAGE: task.exec\n");
+    gprint (GP_ERR, "  (define pre-exec macro for this task)\n");
+    return (FALSE);
+  }
+
+  if (!strcmp (argv[0], "task.exit") && (argc != 2)) {
+    gprint (GP_ERR, "USAGE: task.exit (state)\n");
+    gprint (GP_ERR, "  (define exit state macro for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  macro = NULL;
+
+  /*** identify which Macro in Task this particular macro goes with ***/ 
+  if (!strcmp (argv[0], "task.exec")) {
+    if (task[0].exec != NULL) {
+      FreeMacro (task[0].exec);
+      free (task[0].exec);
+    }
+    ALLOCATE (task[0].exec, Macro, 1);
+    macro = task[0].exec;
+    macro[0].name = strcreate ("exec");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "timeout")) {
+    if (task[0].timeout != NULL) {
+      FreeMacro (task[0].timeout);
+      free (task[0].timeout);
+    }
+    ALLOCATE (task[0].timeout, Macro, 1);
+    macro = task[0].timeout;
+    macro[0].name = strcreate ("timeout");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "crash")) {
+    if (task[0].crash != NULL) {
+      FreeMacro (task[0].crash);
+      free (task[0].crash);
+    }
+    ALLOCATE (task[0].crash, Macro, 1);
+    macro = task[0].crash;
+    macro[0].name = strcreate ("crash");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "default")) {
+    if (task[0].defexit != NULL) {
+      FreeMacro (task[0].defexit);
+      free (task[0].defexit);
+    }
+    ALLOCATE (task[0].defexit, Macro, 1);
+    macro = task[0].defexit;
+    macro[0].name = strcreate ("default");
+    goto found;
+  }
+  if (!strcmp (argv[0], "task.exit")) {	/* generic exit function */
+    for (i = 0; i < task[0].Nexit; i++) { /* find exiting exit function with same name */
+      if (!strcmp (task[0].exit[i][0].name, argv[1])) {	/*** replace this one ***/
+	FreeMacro (task[0].exit[i]);
+	free (task[0].exit[i]);
+	ALLOCATE (task[0].exit[i], Macro, 1);
+	macro = task[0].exit[i];
+	macro[0].name = strcreate (argv[1]);
+	goto found;
+      }
+    }
+    /** create a new exit status macro **/
+    N = task[0].Nexit;
+    ALLOCATE (task[0].exit[N], Macro, 1);
+    task[0].Nexit ++;
+    if (task[0].Nexit == task[0].NEXIT) {
+      task[0].NEXIT += 10;
+      REALLOCATE (task[0].exit, Macro *, task[0].NEXIT);
+    }
+    macro = task[0].exit[N];
+    macro[0].name = strcreate (argv[1]);
+    goto found;
+  }
+
+found:
+
+  macro[0].Nlines = 0;
+  NLINES = D_NLINES;
+  ALLOCATE (macro[0].line, char *, NLINES);
+
+  /* load macro lines from appropriate source (readline / list) */
+
+  depth = 0;
+  done = FALSE;
+  ThisList = current_list_depth();
+  while (!done) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+
+    if ((ThisList == 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "end macro with 'END'\n");
+      continue;
+    }
+    if ((ThisList > 0) && (input == (char *) NULL)) {
+      gprint (GP_ERR, "missing 'END' in macro definition\n");
+      input = strcreate ("end");
+    }
+    if (ThisList == 0) ohana_memregister (input);
+
+    stripwhite (input);
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { /* we hit the last "END", macro is done */
+	free (input);
+	REALLOCATE (macro[0].line, char *, MAX (1, macro[0].Nlines));
+	return (TRUE);
+      }
+    }
+
+    if (*input) { 
+      macro[0].line[macro[0].Nlines] = input;
+      macro[0].Nlines ++;
+      if (macro[0].Nlines == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (macro[0].line, char *, NLINES);
+      }
+    }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_nmax.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_nmax.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_nmax.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+int task_nmax (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  task[0].Nmax = atoi (argv[1]);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: nmax N\n");
+  return (FALSE);
+}
+
+int task_npending (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  task[0].NpendingMax = atoi (argv[1]);
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: npending N\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_options.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_options.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_options.c	(revision 17422)
@@ -0,0 +1,38 @@
+# include "pantasks.h"
+
+int task_options (int argc, char **argv) {
+
+  int i;
+  Task *task;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: options <opt>. ..\n");
+    gprint (GP_ERR, "  (define options for this task)\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  /* free existing memory used by optv */
+  if (task[0].optc != 0) {
+    for (i = 0; i < task[0].optc; i++) {
+      if (task[0].optv[i] != NULL) free (task[0].optv[i]);
+    }
+    free (task[0].optv);
+  }
+
+  /* create new memory for optv */
+  task[0].optc = argc - 1;
+  ALLOCATE (task[0].optv, char *, MAX (task[0].optc, 1));
+  for (i = 0; i < task[0].optc; i++) {
+    task[0].optv[i] = strcreate (argv[i+1]);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_periods.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_periods.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_periods.c	(revision 17422)
@@ -0,0 +1,56 @@
+# include "pantasks.h"
+
+int task_periods (int argc, char **argv) {
+
+  int N, Poll, Exec, Timeout;
+  float PollValue, ExecValue, TimeoutValue;
+  Task *task;
+
+  PollValue = 0;
+  Poll = FALSE;
+  if ((N = get_argument (argc, argv, "-poll"))) {
+    Poll = TRUE;
+    remove_argument (N, &argc, argv);
+    PollValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  ExecValue = 0;
+  Exec = FALSE;
+  if ((N = get_argument (argc, argv, "-exec"))) {
+    Exec = TRUE;
+    remove_argument (N, &argc, argv);
+    ExecValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  TimeoutValue = 0;
+  Timeout = FALSE;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    Timeout = TRUE;
+    remove_argument (N, &argc, argv);
+    TimeoutValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: periods [-poll value] [-exec value] [-timeout value]\n");
+    gprint (GP_ERR, "  define time periods for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+
+  if (Poll) task[0].poll_period = PollValue;
+  if (Exec) task[0].exec_period = ExecValue;
+  if (Timeout) task[0].timeout_period = TimeoutValue;
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_stdout.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_stdout.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_stdout.c	(revision 17422)
@@ -0,0 +1,49 @@
+# include "pantasks.h"
+
+int task_stdout (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout <name>\n");
+    gprint (GP_ERR, "  define dump file for job stdout for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  if (task[0].stdout_dump != NULL) free (task[0].stdout_dump);
+  task[0].stdout_dump = strcreate (argv[1]);
+  return (TRUE);
+}
+
+int task_stderr (int argc, char **argv) {
+
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr <name>\n");
+    gprint (GP_ERR, "  define dump file for job stderr for this task\n");
+    return (FALSE);
+  }
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    task = GetActiveTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+  }
+  if (task[0].stderr_dump != NULL) free (task[0].stderr_dump);
+  task[0].stderr_dump = strcreate (argv[1]);
+  return (TRUE);
+}
+
+/* apparently, local is the default! */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_threads.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_threads.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_threads.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "pantasks.h"
+
+/** things related to CheckTasks **/
+
+static int CheckTasksRun = FALSE;
+
+void CheckTasksSetState (int state) {
+  CheckTasksRun = state;
+}
+int CheckTasksGetState () {
+  return (CheckTasksRun);
+}
+
+void *CheckTasksThread (void *data) {
+
+  float next_timeout;
+  char log_stdout[128], log_stderr[128];
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // check for thread suspend
+    if (!CheckTasksRun) {
+      usleep (100000); // idle if thread action is suspended
+      continue;
+    }
+
+    // one run of the task checker
+    SerialThreadLock ();
+    next_timeout = CheckTasks ();
+    SerialThreadUnlock ();
+    if (VerboseMode() == 2) fprintf (stderr, "T");
+
+    next_timeout = MIN (next_timeout, 0.1);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
+
+// I sleep for a small amount of time here, based on how long until the next expected
+// timeout.  this enforces a certain granularity in the task creation, but prevents the task
+// thread from driving the load up to silly levels.
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_trange.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_trange.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/task_trange.c	(revision 17422)
@@ -0,0 +1,96 @@
+# include "pantasks.h"
+
+int task_trange (int argc, char **argv) {
+
+  int N;
+  Task *task;
+  TimeRange range;
+
+  /* reset the tranges for the current task */
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    if (argc != 1) goto usage;
+
+    task = GetNewTask ();
+    if (task == NULL) {
+      gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
+
+    task[0].Nranges = 0;
+    REALLOCATE (task[0].ranges, TimeRange, 1);
+    return (TRUE);
+  } 
+
+  range.Nmax = 0;
+  range.Nrun = 0;
+  if ((N = get_argument (argc, argv, "-nmax"))) {
+    remove_argument (N, &argc, argv);
+    range.Nmax = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* keep = true means the time range defines a valid time range
+     keep = false means the time range defines an invalid range */
+  range.include = TRUE;
+  if ((N = get_argument (argc, argv, "-exclude"))) {
+    remove_argument (N, &argc, argv);
+    range.include = FALSE;
+  }
+
+  if (argc != 3) goto usage;
+
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    return (FALSE);
+  }
+
+  /* test for Mon[@HH:MM:SS] - both must match */
+  if (day_to_sec (argv[1], &range.start)) {
+    if (!day_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid day/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_WEEK;
+    goto valid;
+  }
+
+  /* allow a syntax which means something like "every hour at MM:SS"
+     this could by something like *:MM:SS ? */
+
+  /* test for HH:MM:SS */
+  if (hms_to_sec (argv[1], &range.start)) {
+    if (!hms_to_sec (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_DAY;
+    goto valid;
+  }
+
+  /* this test does not fail sufficiently robustly for invalid inputs */
+  /* test for YYYY/MM/DD - both must match */
+  if (ohana_str_to_time (argv[1], &range.start)) {
+    if (!ohana_str_to_time (argv[2], &range.stop)) {
+      gprint (GP_ERR, "invalid date/time %s\n", argv[2]);
+      goto usage;
+    }
+    range.type = RANGE_ABS;
+    goto valid;
+  }
+  goto usage;
+
+valid:
+  N = task[0].Nranges;
+  task[0].Nranges ++;
+  REALLOCATE (task[0].ranges, TimeRange, task[0].Nranges);
+  
+  task[0].ranges[N] = range;
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: trange start end [-nmax N]\n");
+  gprint (GP_ERR, "USAGE: trange -reset\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/iotest.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/iotest.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/iotest.sh	(revision 17422)
@@ -0,0 +1,57 @@
+
+$BLOCK = 0
+
+macro testloop
+  queuepush stdout start
+  for i 0 5
+    echo $i
+    queueprint stdout
+  end
+end
+
+# a basic task which just runs 'echo foobar'
+task	       basic
+  command      echo foobar
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    if ($BLOCK) break
+    queuepush stdout start
+    echo "command start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "command stop"
+  end
+
+  # success
+  task.exit    0
+    $BLOCK = 1
+    echo "exit start"
+    queuelist
+    queueprint stdout
+    queueinit stdout
+    queuelist
+    echo "exit stop"
+    $BLOCK = 0
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/joberror.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/joberror.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/joberror.sh	(revision 17422)
@@ -0,0 +1,29 @@
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  command      ls -l /usr/bin /tmp/foobar
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/load.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/load.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/load.sh	(revision 17422)
@@ -0,0 +1,37 @@
+
+# a basic task which just runs 'sleep'
+task	       newtask
+  # command      sleep 1
+  command      echo hi
+  host         local
+
+  periods      -poll 0.01
+  periods      -exec 0.01
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+    # status
+    # halt
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local.sh	(revision 17422)
@@ -0,0 +1,78 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ # echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local2.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local2.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local2.sh	(revision 17422)
@@ -0,0 +1,79 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ echo growth: {$endmem - $startmem}
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($Npass/$dtime < 4.2) 
+   echo "tasks running too slow: {$Npass/$dtime}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local3.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local3.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local3.sh	(revision 17422)
@@ -0,0 +1,88 @@
+
+## a basic test of memory allocation
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local4.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local4.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local4.sh	(revision 17422)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## task.exec test
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  command      echo "a test line"
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local5.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local5.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local5.sh	(revision 17422)
@@ -0,0 +1,93 @@
+
+## a basic test of memory allocation
+## dynamic command
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local6.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local6.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local6.sh	(revision 17422)
@@ -0,0 +1,94 @@
+
+## a basic test of memory allocation
+## test of 'options'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Nrun = 1
+    options "test options line"
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+  exec wc -l tmp.txt
+ end
+
+ $answer = `wc -l tmp.txt`
+ list word -split $answer
+ if ($word:0 != $Npass) 
+   echo "missing lines in tmp.txt: $word:0"
+ end
+ if ($VERBOSE > 3)
+   echo "task speed: {$dtime/$Npass} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Npass > 0.010) 
+   echo "tasks running too slow: {$dtime/$Npass}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local7.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local7.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/local7.sh	(revision 17422)
@@ -0,0 +1,127 @@
+
+## a basic test of memory allocation
+## test of 'exec failure'
+
+exec rm -f tmp.txt
+verbose off
+$Npass = 0
+$Ntry = 0
+if ($?VERBOSE == 0)
+  $VERBOSE = 4
+end
+
+# a basic task which just runs 'echo'
+task	       basic
+  host         local
+
+  periods      -poll 0.005
+  periods      -exec 0.005
+  periods      -timeout 2
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    $Ntry ++
+    if ($Ntry > -1) break
+
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+    echo "this is a line in the task exec code"
+
+    command echo "$Npass : test line"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+macro memcheck.init
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+end
+macro memcheck
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+ if ($VERBOSE > 3)
+   echo growth: {$endmem - $startmem}
+ end
+end
+
+macro done
+ stop
+ date -var dtime -seconds -reftime $start
+ if ($VERBOSE > 3)
+  status
+ end
+
+ # this task generates no output files
+ if ($VERBOSE > 3)
+   echo "task test speed: {$dtime/$Ntry} sec/tasks"
+ end
+ ### this test depends on the value of periods -exec above
+ if ($dtime/$Ntry > 0.010) 
+   echo "tasks tests running too slow: {$dtime/$Ntry}"
+ end
+
+ memcheck
+
+ if ($endmem - $startmem > 30)
+   $PASS = 0
+   echo "failed memcheck"
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/10000}"
+ end
+end
+
+
+memcheck.init
+echo "starting memcheck"
+echo "wait a few seconds and type 'done'"
+
+run
+date -var start -seconds
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep.sh	(revision 17422)
@@ -0,0 +1,34 @@
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 10
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 0.2
+  periods      -timeout 20
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep2.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep2.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep2.sh	(revision 17422)
@@ -0,0 +1,39 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add $hostname
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep3.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep3.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/test/sleep3.sh	(revision 17422)
@@ -0,0 +1,47 @@
+
+$hostname = `hostname`
+
+controller exit true
+controller host add po02
+controller host add po03
+controller host add po04
+controller host add po05
+controller host add po06
+controller host add po07
+controller host add po08
+controller host add po09
+controller host add po10
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      sleep 1
+  host         anyhost
+
+  periods      -poll 0.01
+  periods      -exec 0.02
+  periods      -timeout 20
+  npending 100
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/thread_locks.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/thread_locks.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/thread_locks.c	(revision 17422)
@@ -0,0 +1,14 @@
+# include "pantasks.h"
+
+/* this mutex is used by the serialized threads */
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void SerialThreadLock () {
+    pthread_mutex_lock (&mutex);
+}
+
+void SerialThreadUnlock () {
+    pthread_mutex_unlock (&mutex);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/verbose.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/verbose.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/verbose.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "pantasks.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_LOG, "verbose mode ON\n");
+    } else {
+      gprint (GP_LOG, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "THREADS")) {
+      VERBOSE = 2;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/threads/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pantasks/version.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "pantasks.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pantasks version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/ChildOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/ChildOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/ChildOps.c	(revision 17422)
@@ -0,0 +1,145 @@
+# include "pclient.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+// #include <stropts.h>
+
+static int Nbad = 0;
+static struct timeval last = {0, 0};
+
+int InitChild () {
+
+  ChildPID         = 0;                /** current child PID **/
+  ChildStatus      = PCLIENT_NONE;     /** current status of child **/
+  ChildExitStatus  = 0;                /** recent exit status of child **/
+  
+  child_stdin_fd[0]  = 0;
+  child_stdin_fd[1]  = 0;
+  child_stdout_fd[0] = 0;
+  child_stdout_fd[1] = 0;
+  child_stderr_fd[0] = 0;
+  child_stderr_fd[1] = 0;
+
+  InitIOBuffer (&child_stdout, 1024);
+  InitIOBuffer (&child_stderr, 1024);
+  return (TRUE);
+}
+
+int FreeChild () {
+
+  FreeIOBuffer (&child_stdout);
+  FreeIOBuffer (&child_stderr);
+  return (TRUE);
+}
+
+int CheckChild () {
+
+  int Nread;
+  double dtime;
+  struct timeval now;
+
+  /* runaway test - if pcontrol is killed, pclient starts running away.  this test is a bit
+     dangerous: the choice of dtime probably depends on the processor and the value provided to
+     pclient.c:rl_set_keyboard_input_timeout (1000); note that we cannot use getppid == 1 as a test
+     because the parent of pclient is the ssh process on the pclient host, not pcontrol.  in any
+     case, the opihi shell catches if the ssh dies using getppid
+   */
+  gettimeofday (&now, (void *) NULL);
+  dtime = 1e6*DTIME (now, last);
+  if (dtime < 50) {
+    Nbad ++;
+    if (Nbad > 100) {
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    }
+  }
+  if (dtime > 950) Nbad = 0;
+  last = now;
+
+  CheckChildStatus ();
+  
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&child_stdout, child_stdout_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&child_stderr, child_stderr_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      gprint (GP_ERR, "serious IO error\n");
+      abort ();
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+void CheckChildStatus () {
+
+  int result, waitstatus;
+
+  if (ChildStatus != PCLIENT_BUSY) return;
+
+  /* check current child status */
+  result = waitpid (ChildPID, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  ChildStatus = PCLIENT_NONE;
+	  break;
+	case EINVAL:
+	  gprint (GP_ERR, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  gprint (GP_ERR, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  gprint (GP_ERR, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      ChildStatus = PCLIENT_BUSY;
+      break;
+
+    default:
+      if (result != ChildPID) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, ChildPID);
+	exit (1);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	ChildStatus = PCLIENT_EXIT;
+	ChildExitStatus = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	ChildStatus = PCLIENT_CRASH;
+	ChildExitStatus = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/Makefile	(revision 17422)
@@ -0,0 +1,46 @@
+default: pclient
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/pclient
+DATA    =       $(DESTDATA)/pclient
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lbasiccmd -lshell -ldata 
+LIBS2         = -ldvo -lkapa -lFITS -lohana
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# pclient user commands and support functions ########################
+funcs = \
+$(SDIR)/init.$(ARCH).o \
+$(SDIR)/pclient.$(ARCH).o \
+$(SDIR)/ChildOps.$(ARCH).o \
+
+cmds = \
+$(SDIR)/job.$(ARCH).o \
+$(SDIR)/reset.$(ARCH).o \
+$(SDIR)/check.$(ARCH).o \
+$(SDIR)/status.$(ARCH).o \
+$(SDIR)/stdout.$(ARCH).o  \
+$(SDIR)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pclient: $(BIN)/pclient.$(ARCH)
+$(SRC)/pclient.$(ARCH).o : $(libs)
+$(BIN)/pclient.$(ARCH)   : $(funcs) $(cmds)
+
+install: $(DESTBIN)/pclient help
+
+help: clean-help cmd.basic.help pclient.help
+
+.PHONY: pclient
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/check.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/check.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/check.c	(revision 17422)
@@ -0,0 +1,9 @@
+# include "pclient.h"
+
+int check (int argc, char **argv) {
+
+  /* force a check */
+  CheckChild ();
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/help/job
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/help/job	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/help/job	(revision 17422)
@@ -0,0 +1,1 @@
+0
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/init.c	(revision 17422)
@@ -0,0 +1,33 @@
+# include "pclient.h"
+
+int job	            PROTO((int, char **));
+int reset           PROTO((int, char **));
+int check           PROTO((int, char **));
+int status	    PROTO((int, char **));
+int stdout_pclient  PROTO((int, char **));
+int stderr_pclient  PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "job",       job,      "start job"},
+  {1, "reset",     reset,    "reset job"},
+  {1, "check",     check,    "check job"},
+  {1, "status",    status,   "check job status"},
+  {1, "stdout",    stdout_pclient,   "get stdout buffer"},
+  {1, "stderr",    stderr_pclient,   "get stderr buffer"},
+  {1, "version",   version,      "show version information"},
+}; 
+
+void InitPclient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitChild ();
+}
+
+void FreePclient () {
+  FreeChild ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/job.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/job.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/job.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "pclient.h"
+
+int job (int argc, char **argv) {
+
+  int i, pid, status;
+  char **targv;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job (arg0) (arg1) ... (argN)\n");
+    gprint (GP_LOG, "STATUS %d\n", -2);
+    return (FALSE);
+  }
+  
+  if (ChildStatus != PCLIENT_NONE) {
+    gprint (GP_ERR, "need to clear existing child\n");
+    gprint (GP_LOG, "STATUS %d\n", -3);
+    return (FALSE);
+  }
+
+  if (pipe (child_stdin_fd)  < 0) goto pipe_error;
+  if (pipe (child_stdout_fd) < 0) goto pipe_error;
+  if (pipe (child_stderr_fd) < 0) goto pipe_error;
+
+  /* need to define arg list with NULL termination */
+  ALLOCATE (targv, char *, argc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+  targv[i-1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    /* gprint (GP_ERR, "starting child process %s...\n", targv[0]); */
+
+    /* close the other ends of the pipes */
+    close (child_stdin_fd[1]);
+    close (child_stdout_fd[0]);
+    close (child_stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (child_stdin_fd[0],  STDIN_FILENO);
+    dup2 (child_stdout_fd[1], STDOUT_FILENO);
+    dup2 (child_stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    /* exec job */ 
+    status = execvp (targv[0], targv); 
+    gprint (GP_ERR, "error starting child process: %d\n", status);
+    exit (1);
+  }
+
+  /* free temporary arg list */
+  for (i = 0; i < argc - 1; i++) {
+    free (targv[i]);
+  }
+  free (targv);
+
+  /* close the other ends of the pipes */
+  close (child_stdin_fd[0]);
+  close (child_stdout_fd[1]);
+  close (child_stderr_fd[1]);
+
+  /* make the pipes non-blocking */
+  fcntl (child_stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (child_stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (child_stderr_fd[0], F_SETFL, O_NONBLOCK);
+  
+  ChildStatus = PCLIENT_BUSY;
+  ChildPID = pid;
+
+  gprint (GP_LOG, "STATUS %d\n", ChildPID);
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (child_stdin_fd[0]  != 0) close (child_stdin_fd[0]);
+  if (child_stdin_fd[1]  != 0) close (child_stdin_fd[1]);
+  if (child_stdout_fd[0] != 0) close (child_stdout_fd[0]);
+  if (child_stdout_fd[1] != 0) close (child_stdout_fd[1]);
+  if (child_stderr_fd[0] != 0) close (child_stderr_fd[0]);
+  if (child_stderr_fd[1] != 0) close (child_stderr_fd[1]);
+
+  gprint (GP_LOG, "STATUS %d\n", -1);
+  return (FALSE);
+}
+
+/* possible responses:
+
+STATUS -1 - pipe error
+STATUS -2 - syntax error
+STATUS -3 - existing child
+STATUS >0 - success (PID)
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/notes.txt	(revision 17422)
@@ -0,0 +1,17 @@
+
+pclient special commands:
+
+ job 
+
+ reset
+
+ check
+
+ status
+
+ stdout
+
+ stderr
+
+- communication with pclient should search for the pclient prompt:
+  pclient:
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/outline.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/outline.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/outline.txt	(revision 17422)
@@ -0,0 +1,12 @@
+
+init stage:
+
+  - set child 
+
+readline loop:
+
+  - wait for commands
+
+
+
+  
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/pclient.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/pclient.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/pclient.c.in	(revision 17422)
@@ -0,0 +1,79 @@
+# include "pclient.h"
+
+# define opihi_name "PCLIENT"
+# define opihi_prompt "pclient: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ""
+# define opihi_rcfile ".pcontrolrc"
+
+void dummy_prep_term (int flag) {
+}
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPclient ();
+
+  rl_prep_term_function = dummy_prep_term;
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = CheckChild;
+  rl_set_keyboard_input_timeout (1000); 
+  /* 1 ms seems to be the minimum valid number */
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  ConfigFree ();
+  FreeBasic ();
+  FreePclient ();
+  return;
+}
+
+void gotsignal (int signum) {
+  gprint (GP_ERR, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  exit (status);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/reset.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/reset.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/reset.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "pclient.h"
+
+int reset (int argc, char **argv) {
+
+  int i, result, waitstatus;
+  struct timespec request, remain;
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: reset\n");
+    gprint (GP_LOG, "STATUS -1\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE) {
+    gprint (GP_ERR, "no child process, cannot reset\n");
+    gprint (GP_LOG, "STATUS 2\n");
+    return (TRUE);
+  }
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  if (ChildStatus == PCLIENT_BUSY) {
+    /* job is still running, send SIGTERM signal to job */
+    kill (ChildPID, SIGTERM);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* job did not exit, send SIGKILL signal to job */
+    kill (ChildPID, SIGKILL);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* total failure, don't reset */
+    gprint (GP_ERR, "child process %d is hung, cannot reset\n", ChildPID);
+    gprint (GP_LOG, "STATUS 0\n");
+    return (FALSE);
+  }
+
+reset_job:
+  /* close out ends of the pipes */
+  close (child_stdin_fd[1]);
+  close (child_stdout_fd[0]);
+  close (child_stderr_fd[0]);
+  child_stdin_fd[1] = 0;
+  child_stdout_fd[0] = 0;
+  child_stderr_fd[0] = 0;
+
+  FlushIOBuffer (&child_stdout);
+  FlushIOBuffer (&child_stderr);
+  ChildStatus = PCLIENT_NONE;
+  ChildPID = 0;
+  ChildExitStatus = 0;
+
+  gprint (GP_LOG, "STATUS 1\n");
+  return (TRUE);
+}
+
+/* exit conditions:
+   -1 - usage message / syntax error
+    2 - unknown job
+    0 - process hung
+    1 - successful resetn
+*/
+
+/* the linux kernel timer sticks in a 10ms lag between kill and the harvest */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/status.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/status.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/status.c	(revision 17422)
@@ -0,0 +1,24 @@
+# include "pclient.h"
+
+int status (int argc, char **argv) {
+
+  char status_string[64];
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: status\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE)  strcpy (status_string, "NONE");
+  if (ChildStatus == PCLIENT_BUSY)  strcpy (status_string, "BUSY");
+  if (ChildStatus == PCLIENT_EXIT)  strcpy (status_string, "EXIT");
+  if (ChildStatus == PCLIENT_CRASH) strcpy (status_string, "CRASH");
+
+  gprint (GP_LOG, "STATUS %s\n", status_string);
+  gprint (GP_LOG, "EXITST %d\n", ChildExitStatus);
+  gprint (GP_LOG, "STDOUT %d\n", child_stdout.Nbuffer);
+  gprint (GP_LOG, "STDERR %d\n", child_stderr.Nbuffer);
+
+  set_str_variable ("JOBSTATUS", status_string);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/stdout.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/stdout.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/stdout.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "pclient.h"
+
+int stdout_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stdout\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stdout.buffer, 1, child_stdout.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
+
+int stderr_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stderr\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stderr.buffer, 1, child_stderr.Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/status.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/status.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/status.sh	(revision 17422)
@@ -0,0 +1,64 @@
+
+list tests
+ test1
+ test2
+end
+
+# check status without any jobs
+macro test1
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS NONE")
+   $PASS = 0
+ end
+ if ("$line:1" != "EXITST 0")
+   $PASS = 0
+ end
+ if ("$line:2" != "STDOUT 0")
+   $PASS = 0
+ end
+ if ("$line:3" != "STDERR 0")
+   $PASS = 0
+ end
+end
+
+# check status with a basic job
+macro test2
+ $PASS = 1
+ break -auto off
+
+ output -err /dev/null
+ reset
+ exec rm -f test.txt
+ job ls 
+ usleep 500000
+ output -err stderr
+
+ output test.txt
+ status
+ output stdout
+
+ # this test requires a specific set of output
+ list line -x "cat test.txt"
+ if ($line:n != 4)
+   $PASS = 0
+ end
+ if ("$line:0" != "STATUS EXIT")
+   $PASS = 0
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/version.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/version.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/test/version.sh	(revision 17422)
@@ -0,0 +1,106 @@
+
+list tests
+ test1
+ test2
+ test3
+ test4
+ test5
+ test6
+ test7
+end
+
+## these tests check that the version command reports the versions
+## they do not validate the actual versions themselves
+
+# set up the version output file
+macro test1
+ $PASS = 1
+ break -auto off
+ exec rm test.txt
+ output -err test.txt
+ version
+ output -err stderr
+ file test.txt found
+ if ($found != 1)
+   $PASS = 0
+ end
+end
+
+# test for the correct number of version lines
+macro test2
+ $PASS = 1
+ break -auto off
+ 
+ $line = `wc -l test.txt`
+
+ list word -split $line
+ if ($word:0 != 6)
+   $PASS = 0
+ end
+end
+
+# test for pclient version name
+macro test3
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:0
+
+ if ("$word:0" != "pclient")
+   $PASS = 0
+ end
+end
+
+# test for opihi version name
+macro test4
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:1
+
+ if ("$word:0" != "opihi")
+   $PASS = 0
+ end
+end
+
+# test for ohana version name
+macro test5
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:2
+
+ if ("$word:0" != "ohana")
+   $PASS = 0
+ end
+end
+
+# test for gfits version name
+macro test6
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:3
+
+ if ("$word:0" != "gfits")
+   $PASS = 0
+ end
+end
+
+# test for compilation date/time
+macro test7
+ $PASS = 1
+ break -auto off
+ 
+ list line -x "cat test.txt"
+ list word -split $line:4
+
+ if ("$word:0" != "compiled")
+   $PASS = 0
+ end
+end
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pclient/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pclient/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pclient/version.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "pclient.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pclient version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckBusyJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 17422)
@@ -0,0 +1,104 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckBusyJob (Job *job, Host *host) {
+
+  int      status;
+  int      outstate;
+  char    *p;
+  char     string[64];
+  IOBuffer buffer;
+
+  /* we are checking a job which is currently busy.  it has been pulled from the
+     JOB_BUSY stack, and is linked to a host in the HOST_BUSY stack.  
+     XXX need to check on state of HOST on return */
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  InitIOBuffer (&buffer, 0x100);
+
+  status = PclientCommand (host, "status", PCLIENT_PROMPT, &buffer);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      HarvestHost (host[0].pid);
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      if (job[0].realhost) free (job[0].realhost);
+      job[0].realhost = NULL;
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_HUNG:
+      // don't do anything drastic, just keep trying
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "client is busy, not responding");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (TRUE);
+
+    case PCLIENT_GOOD:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "message received (CheckBusyJob)");
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  /** host is up, need to parse message **/
+  p = memstr (buffer.buffer, "STATUS", buffer.Nbuffer);
+  // this condition means the message is garbage.  toss it and try again
+  if (p == NULL) {
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+    FreeIOBuffer (&buffer);
+    return (FALSE);
+  }
+
+  sscanf (p, "%*s %s", string);
+  ASSERT (strcmp(string, "NONE"), "no current job\n");
+
+  /** no status change, return to BUSY stack **/
+  if (!strcmp(string, "BUSY")) {
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+    FreeIOBuffer (&buffer);
+    return (TRUE);
+  }
+
+  /* exit status better be either EXIT or CRASH */
+  outstate = PCONTROL_JOB_BUSY;
+  if (!strcmp(string, "EXIT")) outstate = PCONTROL_JOB_EXIT;
+  if (!strcmp(string, "CRASH")) outstate = PCONTROL_JOB_CRASH;
+  ASSERT (outstate != PCONTROL_JOB_BUSY, "should not reach here (CheckJob)");
+
+  /* parse the exit status and sizes of output buffers */
+  p = memstr (buffer.buffer, "EXITST", buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].exit_status);
+  p = memstr (buffer.buffer, "STDOUT", buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stdout_size);
+  p = memstr (buffer.buffer, "STDERR", buffer.Nbuffer);
+  sscanf (p, "%*s %d", &job[0].stderr_size);
+
+  // XXX runaway job if output too large?
+  if (job[0].stdout_size > 0x100000) abort();
+  if (job[0].stderr_size > 0x100000) abort();
+
+  // job has exited : move to DONE stack 
+  // the host is still BUSY until job output is gathered (int CheckDoneJob)
+  // don't unlink job and host yet
+  PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+  PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, outstate);
+  gettimeofday (&job[0].stop, (void *) NULL);
+  job[0].dtime = DTIME(job[0].stop, job[0].start);
+  FreeIOBuffer (&buffer);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneHost.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneHost.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneHost.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+int CheckDoneHost (Host *host) {
+  
+  int       status;
+  char     *p;
+  IOBuffer  buffer;
+
+  ASSERT (host, "host not set");
+
+  InitIOBuffer (&buffer, 0x100);
+  
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, &buffer);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      /* DONE host does not have an incomplete job */
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+      // XXX do we need to close the connection?
+
+    case PCLIENT_HUNG:
+      // don't do anything drastic, just try again later
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "message received (CheckDoneHost)\n");  
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  /** successful command, examine result **/
+  p = memstr (buffer.buffer, "STATUS", buffer.Nbuffer);
+  ASSERT (p != NULL, "missing STATUS in pclient message (CheckDoneHost)");
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      ABORT ("reset syntax error");
+      
+    case 0:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "reset failed\n");
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+      
+    case 1:
+    case 2:
+      if (DEBUG || VerboseMode()) gprint (GP_ERR, "successful reset\n");
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    default:
+      ABORT ("should not reach here (CheckDoneHost)");
+  }
+  ABORT ("should not reach here (CheckDoneHost)");
+}
+
+/** probably need to flush the buffer before the command **/
+/** need to add timeout check here **/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 17422)
@@ -0,0 +1,35 @@
+# include "pcontrol.h"
+
+int CheckDoneJob (Job *job, Host *host) {
+  
+  int success;
+
+  ASSERT (job, "job not set");
+  ASSERT (host, "host not set");
+
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job == (Job *) host[0].job, "invalid job");
+
+  success = TRUE;
+  success &= GetJobOutput ("stdout", host, &job[0].stdout_buff, job[0].stdout_size);
+  success &= GetJobOutput ("stderr", host, &job[0].stderr_buff, job[0].stderr_size);
+
+  if (!success) {
+    // XXX some kind of error?
+    // XXX try again later?
+    PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+    PutJob (job, PCONTROL_JOB_DONE, STACK_BOTTOM);
+    return (FALSE);
+  }
+
+  /* job's state is either EXIT or CRASH (verify?) */
+  // unlink host & job
+  job[0].host = NULL;
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+  PutJob (job, job[0].state, STACK_BOTTOM);
+
+  return (TRUE);
+}
+
+/** need to add timeout check here **/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckHost.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckHost.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckHost.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "pcontrol.h"
+
+int CheckHost (Host *host) {
+  
+  int status;
+  IOBuffer buffer;
+
+  ASSERT (host, "host not set");
+
+  if (host[0].job != NULL) return (TRUE);
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host);
+    OffHost (host);
+    return (TRUE);
+  }
+
+  InitIOBuffer (&buffer, 0x100);
+
+  status = PclientCommand (host, "echo OK", PCLIENT_PROMPT, &buffer);
+  switch (status) {
+    case 0:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+      
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      /*** do we mark this in some way (HUNG) ? ***/
+      PutHost (host, host[0].stack, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    default:
+      PutHost (host, host[0].stack, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (TRUE);
+  }
+  ABORT ("should not reach here (Check Host)"); 
+}
+
+// if the host has a job, we skip it (down or crash state will be caught elsewhere)
+// in fact, just touch the IDLE hosts, not the BUSY hosts?
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 17422)
@@ -0,0 +1,102 @@
+# include "pcontrol.h"
+
+/* the supplied host is not on a stack: it cannot be taken by the other thread */
+int CheckIdleHost (Host *host) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+
+  ASSERT (host, "host not set");
+
+  /* if this host has been marked to be turned off, do that and return */
+  if (host[0].markoff) {
+    host[0].markoff = FALSE;
+    StopHost (host);
+    OffHost (host);
+    return (TRUE);
+  }
+    
+  /* search the JOB_PENDING stack for an appropriate job */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+  LockStack (stack);
+  
+  /* look for first NEEDHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_NEEDHOST) continue;
+    ASSERT (job[0].hostname != NULL, "NEEDHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no NEEDHOST entry, look for first WANTHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    ASSERT (job[0].hostname != NULL, "WANTHOST hostname missing");
+    if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no WANTHOST entry, look for first ANYHOST matching this host */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_ANYHOST) continue;
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+
+  /* no ANYHOST entry, look for first WANTHOST with old time */
+  /* XXX perhaps I should add this to the conditions for ANYHOST instead of
+     running a separate loop?  ie, WANTHOST && time > X == ANYHOST */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    // XXX test the job age and skip if too young
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job, host);
+    return (TRUE);
+  }
+  UnlockStack (stack);
+
+  /* no jobs for host, put it back on IDLE stack */
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
+
+/** note : host and job popped off IDLE and PENDING stacks, 
+    unless no job is available **/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckPoint.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckPoint.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckPoint.c	(revision 17422)
@@ -0,0 +1,69 @@
+# include "pcontrol.h"
+
+// one thread (user) interacts with the user and blocks for long periods on input.
+// one thread (client) spins continuously and monitors the hosts and jobs
+// in some cases, the user thread needs to set a check point on the client thread 
+// to ensure the all HOSTs and JOBs are on one of the stacks (nothing 'in flight').
+// these are not symmetric: the client thread should not call Set/Clear, the user 
+// thread should not call Test
+
+# ifdef THREADED
+static pthread_mutex_t client = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t user = PTHREAD_MUTEX_INITIALIZER;
+# endif
+
+// The user thread calls this to stop the client thread from shuffling the Host/Job stacks
+int SetCheckPoint () {
+
+# ifdef THREADED
+  int status;
+  int Nwait;
+
+  // set my lock
+  pthread_mutex_lock (&user);
+
+  // wait until client thread sets its lock
+  Nwait = 0;
+  while (1) {
+    status = pthread_mutex_trylock (&client);
+    if (status == EBUSY) {
+      // client has reached the check-point
+      return (TRUE);
+    }
+    pthread_mutex_unlock (&client);
+    usleep (10000); // wait for client thread to set lock
+    Nwait ++;
+  }
+  // put in a timeout?  (client thread not spinning...)
+  return (FALSE);
+# else
+  return (TRUE);
+# endif
+}
+
+// The user thread calls this to allow the client thread to continue
+int ClearCheckPoint () {
+  // clear my lock
+# ifdef THREADED
+  pthread_mutex_unlock (&user);
+# endif
+  return (TRUE);
+}
+
+// The client thread calls in the thread loop somewhere the stacks are stable
+// (ie, no jobs or hosts currently in flight)
+int TestCheckPoint () {
+
+# ifdef THREADED
+  // set my lock
+  pthread_mutex_lock (&client);
+  
+  // try the user-thread lock
+  pthread_mutex_lock (&user);
+  pthread_mutex_unlock (&user);
+
+  // clear my lock
+  pthread_mutex_unlock (&client);
+# endif
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckSystem.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 17422)
@@ -0,0 +1,390 @@
+# include "pcontrol.h"
+# define DEBUG 0
+
+static struct timeval lastlive = {0, 0};
+static RunLevels RunLevel = PCONTROL_RUN_NONE;
+
+RunLevels SetRunLevel (RunLevels level) {
+  RunLevels oldlevel;
+  oldlevel = RunLevel;
+  RunLevel = level;
+  return oldlevel;
+}
+
+RunLevels GetRunLevel () {
+  return RunLevel;
+}
+
+int CheckSystem () {
+
+  struct timeval now;
+  float dtime;
+
+  /* we want to give each block a maximum allowed time */
+  CheckIdleHosts(0.020); /* submit a new job */
+
+  CheckBusyJobs(0.020);  /* get job status */
+  CheckDoneJobs(0.020);  /* harvest job stdout/stderr */
+  CheckKillJobs(0.020);  /* harvest job stdout/stderr */
+
+  CheckDoneHosts(0.020); /* reset the host */
+  CheckDownHosts(0.100); /* launch the host */
+
+  /* always allow at least one test */
+  /* most tests require about 2ms per host.  
+     CheckDoneJobs must depend on the size of the output buffer */
+
+  gettimeofday (&now, (void *) NULL);
+  dtime = DTIME (now, lastlive);
+  if (dtime > 1.0) {
+    CheckLiveHosts(0.040);
+    lastlive = now;
+  } 
+
+  if (DEBUG) { 
+    Stack *stack;
+    int Nidle, Ndown, Nbusy;
+    stack = GetHostStack (PCONTROL_HOST_IDLE);
+    Nidle = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_DOWN);
+    Ndown = stack[0].Nobject;
+    stack = GetHostStack (PCONTROL_HOST_BUSY);
+    Nbusy = stack[0].Nobject;
+    gprint (GP_ERR, "busy, idle, down: %2d %2d %2d\n", Nbusy, Nidle, Ndown);
+  }
+
+  return (TRUE);
+}
+
+void *CheckSystem_Threaded (void *data) {
+
+  int Njobchecks, Nhostchecks, Nlivechecks, Ndonejobs;
+
+  Nlivechecks = 0;
+
+  gprintInit ();
+
+  while (1) {
+    // stop here if the user-thread requests (no objects in flight) 
+    TestCheckPoint ();
+
+    // don't run the system checks if RunLevel is FALSE
+    // XXX stop should not suspend all checks: we should continue
+    // to harvest completed jobs and migrate idle machines to down
+    if (RunLevel == PCONTROL_RUN_NONE) {
+      usleep (100000); // idle if we are running nothing
+      continue;
+    }
+
+    /* always allow at least one test */
+    /* most tests require about 2ms per host.  
+       CheckDoneJobs must depend on the size of the output buffer */
+    /* the max delay times are fairly arbitrary and do not impact
+       the user interface.
+     */
+
+    Njobchecks = 0;
+    Nhostchecks = 0;
+    Ndonejobs = 0;
+
+    if ((RunLevel == PCONTROL_RUN_ALL) || (RunLevel == PCONTROL_RUN_REAP)) {
+      Njobchecks  += CheckBusyJobs(0.020);  /* get job status (PCLIENT) */
+      TestCheckPoint ();
+      Ndonejobs    = CheckDoneJobs(0.020);  /* harvest job stdout/stderr (!PCLIENT) */
+      Njobchecks  += Ndonejobs;
+      TestCheckPoint ();
+      Njobchecks  += CheckKillJobs(0.020);  /* harvest job stdout/stderr (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel != PCONTROL_RUN_NONE) {
+      Nhostchecks += CheckDoneHosts(0.020); /* reset the host */
+      TestCheckPoint ();
+      Nhostchecks += CheckDownHosts(0.100); /* launch the host */
+      TestCheckPoint ();
+    }
+
+    if (RunLevel == PCONTROL_RUN_ALL) {
+      // we want to give each block a maximum allowed time
+      Nhostchecks += CheckIdleHosts(0.020); /* submit a new job (PCLIENT) */
+      TestCheckPoint ();
+    }
+
+    // there is nothing on the stacks.  test the hosts and wait a bit
+    if (!Njobchecks && !Nhostchecks && (RunLevel != PCONTROL_RUN_NONE)) {
+      CheckLiveHosts(0.040);
+      usleep (100000); // idle if no jobs are waiting
+    } else {
+      // if we only have busy jobs, pause a moment before trying again
+      if (!Ndonejobs) {
+	usleep (100000);
+      }
+    }
+
+    if (DEBUG) { 
+      Stack *stack;
+      int Nidle, Ndown, Nbusy;
+      stack = GetHostStack (PCONTROL_HOST_IDLE);
+      Nidle = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_DOWN);
+      Ndown = stack[0].Nobject;
+      stack = GetHostStack (PCONTROL_HOST_BUSY);
+      Nbusy = stack[0].Nobject;
+      gprint (GP_ERR, "Njobchecks: %d, busy, idle, down: %2d %2d %2d\n", Njobchecks, Nbusy, Nidle, Ndown);
+    }
+  }
+  return (NULL);
+}
+
+int CheckBusyJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once.  Note that it is not important if the
+     stack size is modified by other threads or is changed by any of the actions performed during
+     this loop: the Nobject value is only used to get a rough number for the number of iterations.
+   */
+
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_BUSY);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    // pull both job and host from their stacks
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host != NULL, "host is NULL");
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckBusyJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckDoneJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_DONE);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    CheckDoneJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckKillJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *hoststack;
+  Stack *jobstack;
+  Job   *job;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack = GetJobStack (PCONTROL_JOB_KILL);
+  Nobject = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = (Host *) job[0].host;
+    ASSERT (host, "host is NULL");
+
+    RemoveStackByID (hoststack, host[0].HostID);
+    UnlockStack (hoststack);
+
+    KillJob (job, host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (i);
+}
+
+int CheckDoneHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DONE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckDoneHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckDownHosts (float MaxDelay) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  struct timeval start, stop;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_DOWN);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    if (host[0].markoff) {
+      host[0].markoff = FALSE;
+      OffHost (host);
+      return (TRUE);
+    }
+    dtime = DTIME (host[0].nexttry, start);
+    if (dtime > 0) {
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    } else {
+      StartHost (host);
+    }
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+int CheckIdleHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* check if there are any pending jobs, otherwise skip step */
+  stack = GetJobStack (PCONTROL_JOB_PENDING);
+  if (!stack[0].Nobject) return (0);
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckIdleHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d hosts\n", i);
+  return (i);
+}
+
+/* this is just a heartbeat check (only IDLE hosts) */
+int CheckLiveHosts (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Host  *host;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  Nobject = stack[0].Nobject;
+
+  gettimeofday (&start, (void *) NULL);
+
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
+    CheckHost (host);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (DEBUG) gprint (GP_ERR, "checked %d idle hosts\n", i);
+  return (TRUE);
+}
+
+/*
+
+  gettimeofday (&stop, (void *) NULL);
+  dtime = DTIME (stop, start);
+  if (VerboseMode()) gprint (GP_ERR, "check 4: %f seconds\n", dtime);
+
+  gettimeofday (&start, (void *) NULL);
+*/
+
+/** All of the CheckFooBar entries cycle though their respective queues, popping from the top and
+    pushing to the bottom.  if we stop before the loop is done there is no tendancy for bias because
+    we continue where we left off next round **/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/GetJobOutput.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 500
+
+/* we read Nbytes from the host, then watch for the prompt */ 
+int GetJobOutput (char *cmd, Host *host, IOBuffer *buffer, int Nbytes) {
+  
+  int i, status, Nstart;
+  char *line;
+  struct timespec request, remain;
+
+  ASSERT (cmd, "cmd missing");
+  ASSERT (host, "host missing");
+  ASSERT (buffer, "buffer missing");
+
+  /* flush any earlier messages */
+  ReadtoIOBuffer (buffer, host[0].stdout_fd);
+  FlushIOBuffer (buffer);
+  Nstart = buffer[0].Nbuffer;
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* send cmd (stdout / stderr) */
+  status = write_fmt (host[0].stdin_fd, "%s\n", cmd);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) return (PCLIENT_DOWN);
+
+  /* read at least Nbytes, then watch for PCLIENT_PROMPT */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, host[0].stdout_fd);
+    if ((buffer[0].Nbuffer - Nstart) >= Nbytes) {
+      line = memstr (buffer[0].buffer, PCLIENT_PROMPT, buffer[0].Nbuffer);
+    }
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) return (PCLIENT_DOWN);
+  if (status == -1) return (PCLIENT_HUNG);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      return (FALSE);
+
+    case 0:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      return (FALSE);
+
+    default:
+      if (VerboseMode()) gprint (GP_ERR, "message received (GetJobOutput : %s)\n", cmd);  
+      /* drop extra bytes from pclient (not pclient:job) */
+      buffer[0].Nbuffer = Nstart + Nbytes;
+      if (buffer[0].Nalloc > buffer[0].Nbuffer) {
+	bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+      }
+      return (TRUE);
+  }
+
+  gprint (GP_ERR, "programming error: should not reach here (GetJobOutput)\n");
+  pcontrol_exit (50);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/HostOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/HostOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/HostOps.c	(revision 17422)
@@ -0,0 +1,192 @@
+# include "pcontrol.h"
+
+Stack *HostPool_Idle;
+Stack *HostPool_Busy;
+Stack *HostPool_Done;
+Stack *HostPool_Down;
+Stack *HostPool_Off;
+
+void InitHostStacks () {
+  HostPool_Idle = InitStack ();
+  HostPool_Busy = InitStack ();
+  HostPool_Done = InitStack ();
+  HostPool_Down = InitStack ();
+  HostPool_Off  = InitStack ();
+}
+
+void FreeHostStack (Stack *stack) {
+  Host *host;
+  while ((host = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelHost (host);
+  }
+  FreeStack (stack);
+}
+
+void FreeHostStacks () {
+  FreeHostStack (HostPool_Idle);
+  FreeHostStack (HostPool_Busy);
+  FreeHostStack (HostPool_Done);
+  FreeHostStack (HostPool_Down);
+  FreeHostStack (HostPool_Off );
+}
+
+char *GetHostStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_IDLE: return ("IDLE");
+    case PCONTROL_HOST_DOWN: return ("DOWN");
+    case PCONTROL_HOST_DONE: return ("DONE");
+    case PCONTROL_HOST_BUSY: return ("BUSY");
+    case PCONTROL_HOST_OFF:  return ("OFF");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (51);
+  return (NULL);
+}
+
+Stack *GetHostStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_HOST_IDLE: return (HostPool_Idle);
+    case PCONTROL_HOST_DOWN: return (HostPool_Down);
+    case PCONTROL_HOST_DONE: return (HostPool_Done);
+    case PCONTROL_HOST_BUSY: return (HostPool_Busy);
+    case PCONTROL_HOST_OFF:  return (HostPool_Off);
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (52);
+  return (NULL);
+}
+
+Stack *GetHostStackByName (char *name) {
+  if (!strcasecmp (name, "idle")) return (HostPool_Idle);
+  if (!strcasecmp (name, "down")) return (HostPool_Down);
+  if (!strcasecmp (name, "done")) return (HostPool_Done);
+  if (!strcasecmp (name, "busy")) return (HostPool_Busy);
+  if (!strcasecmp (name, "off"))  return (HostPool_Off);
+  return (NULL);
+}
+
+/* add host to position in stack */
+int PutHost (Host *host, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  host[0].stack = StackID;
+  stat = PushStack (stack, where, host, host[0].HostID, host[0].hostname);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* find the host by ID in the defined host stacks */
+Host *PullHostByID (IDtype HostID, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+/* find the host by ID in the defined host stacks */
+Host *PullHostByName (char *name, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+Host *PullHostFromStackByID (int StackID, IDtype ID) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByID (stack, ID);
+  return (host);
+}
+
+Host *PullHostFromStackByName (int StackID, char *name) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByName (stack, name);
+  return (host);
+}
+
+IDtype AddHost (char *hostname) {
+
+  Host *host;
+
+  ALLOCATE (host, Host, 1);
+
+  host[0].hostname  = strcreate (hostname);
+  host[0].stdin_fd  = 0;
+  host[0].stdout_fd = 0;
+  host[0].stderr_fd = 0;
+  host[0].HostID    = NextHostID();
+
+  host[0].lasttry.tv_sec = 0;
+  host[0].lasttry.tv_usec = 0;
+  host[0].nexttry.tv_sec = 0;
+  host[0].nexttry.tv_usec = 0;
+
+  host[0].markoff  = FALSE;
+  host[0].job      = NULL;
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+  return (host[0].HostID);
+}
+
+void DelHost (Host *host) {
+  FREE (host[0].hostname);
+  FREE (host[0].job);
+  FREE (host);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/IDops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/IDops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/IDops.c	(revision 17422)
@@ -0,0 +1,42 @@
+# include "pcontrol.h"
+
+static IDtype CurrentJobID  = 0;
+static IDtype CurrentHostID = 0;
+
+/* for now, no persistence : we could use the date/time to seed the upper byte(s) if needed */
+void InitIDs () {
+  CurrentJobID = 0;
+  CurrentHostID = 0;
+}
+
+IDtype NextJobID () {
+
+  IDtype ID;
+
+  ID = CurrentJobID;
+  CurrentJobID ++;
+  return (ID);
+}
+
+/* only used by the User thread */
+IDtype NextHostID () {
+
+  IDtype ID;
+
+  ID = CurrentHostID;
+  CurrentHostID ++;
+  return (ID);
+}
+
+void PrintID (gpDest dest, IDtype ID) {
+
+  unsigned short int word0, word1, word2, word3;
+
+  word0 = 0xffff & ID;
+  word1 = 0xffff & (ID >> 16);
+  word2 = 0xffff & (ID >> 32);
+  word3 = 0xffff & (ID >> 48);
+
+  gprint (dest, "%x.%x.%x.%x", word3, word2, word1, word0);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobID.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobID.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobID.c	(revision 17422)
@@ -0,0 +1,41 @@
+# include "pcontrol.h"
+
+static IDtype CurrentJobID  = 0;
+static IDtype CurrentHostID = 0;
+
+/* for now, no persistence : we could use the date/time to seed the upper byte(s) if needed */
+void InitIDs () {
+  CurrentJobID = 0;
+  CurrentHostID = 0;
+}
+
+IDtype NextJobID () {
+
+  IDtype ID;
+
+  ID = CurrentJobID;
+  CurrentJobID ++;
+  return (ID);
+}
+
+IDtype NextHostID () {
+
+  IDtype ID;
+
+  ID = CurrentHostID;
+  CurrentHostID ++;
+  return (ID);
+}
+
+void PrintID (gpDest dest, IDtype ID) {
+
+  unsigned short int word0, word1, word2, word3;
+
+  word0 = 0xffff & ID;
+  word1 = 0xffff & (ID >> 16);
+  word2 = 0xffff & (ID >> 32);
+  word3 = 0xffff & (ID >> 48);
+
+  gprint (dest, "%x.%x.%x.%x", word3, word2, word1, word0);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/JobOps.c	(revision 17422)
@@ -0,0 +1,192 @@
+# include "pcontrol.h"
+
+Stack *JobPool_Pending;
+Stack *JobPool_Busy;
+Stack *JobPool_Done;
+Stack *JobPool_Kill;
+Stack *JobPool_Exit;
+Stack *JobPool_Crash;
+
+void InitJobStacks () {
+  JobPool_Pending = InitStack ();
+  JobPool_Busy    = InitStack ();
+  JobPool_Done    = InitStack ();
+  JobPool_Kill    = InitStack ();
+  JobPool_Exit    = InitStack ();
+  JobPool_Crash   = InitStack ();
+}
+
+void FreeJobStack (Stack *stack) {
+  Job *job;
+  while ((job = PullStackByLocation (stack, stack[0].Nobject - 1)) != NULL) {
+    DelJob (job);
+  }
+  FreeStack (stack);
+}
+
+void FreeJobStacks () {
+  FreeJobStack (JobPool_Pending);
+  FreeJobStack (JobPool_Busy   );
+  FreeJobStack (JobPool_Done   );
+  FreeJobStack (JobPool_Kill   );
+  FreeJobStack (JobPool_Exit   );
+  FreeJobStack (JobPool_Crash  );
+}
+
+char *GetJobStackName (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_PENDING: return ("PENDING");
+    case PCONTROL_JOB_BUSY:    return ("BUSY");
+    case PCONTROL_JOB_DONE:    return ("DONE");
+    case PCONTROL_JOB_KILL:    return ("KILL");
+    case PCONTROL_JOB_EXIT:    return ("EXIT");
+    case PCONTROL_JOB_CRASH:   return ("CRASH");
+  }
+  gprint (GP_ERR, "error: unknown host stack : programming error\n");
+  pcontrol_exit (53);
+  return (NULL);
+}
+
+Stack *GetJobStack (int StackID) {
+  switch (StackID) {
+    case PCONTROL_JOB_PENDING: return (JobPool_Pending);
+    case PCONTROL_JOB_BUSY:    return (JobPool_Busy);
+    case PCONTROL_JOB_DONE:    return (JobPool_Done);
+    case PCONTROL_JOB_KILL:    return (JobPool_Kill);
+    case PCONTROL_JOB_EXIT:    return (JobPool_Exit);
+    case PCONTROL_JOB_CRASH:   return (JobPool_Crash);
+  }
+  gprint (GP_ERR, "error: unknown job stack : programming error\n");
+  pcontrol_exit (54);
+  return (NULL);
+}
+
+Stack *GetJobStackByName (char *name) {
+
+  if (!strcasecmp (name, "pending")) return (JobPool_Pending);
+  if (!strcasecmp (name, "busy"))    return (JobPool_Busy);
+  if (!strcasecmp (name, "done"))    return (JobPool_Done);
+  if (!strcasecmp (name, "exit"))    return (JobPool_Exit);
+  if (!strcasecmp (name, "crash"))   return (JobPool_Crash);
+  return (NULL);
+}
+
+/* add job to position in stack, use StackID as default state */
+int PutJob (Job *job, int StackID, int where) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* by default, these are both the same - to override, use PutJobSetState */
+  job[0].state = StackID;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+/* add job to position in stack.  set state to 'state' */
+int PutJobSetState (Job *job, int StackID, int where, int state) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* alternate state specified by user */
+  job[0].state = state;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+Job *PullJobByID (IDtype JobID, int *StackID) {
+
+  Job *job;
+
+  *StackID = PCONTROL_JOB_PENDING;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_BUSY;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_DONE;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_KILL;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  return (NULL);
+}
+
+/* remove job from position in stack */
+Job *PullJobFromStackByID (int StackID, int ID) {
+
+  Job *job;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  job = PullStackByID (stack, ID);
+  return (job);
+}
+
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv) {
+
+  int JobID;
+  Job *job;
+
+  ALLOCATE (job, Job, 1);
+
+  job[0].argc     = argc;
+  job[0].argv     = argv;
+  job[0].hostname = hostname;
+  job[0].mode     = mode;
+  job[0].host     = NULL;
+  job[0].JobID    = NextJobID();
+  job[0].Reset    = FALSE;
+  job[0].realhost = NULL;
+
+  /* do this step on start? */
+  InitIOBuffer (&job[0].stdout_buff, 0x1000);
+  InitIOBuffer (&job[0].stderr_buff, 0x1000);
+
+  JobID = job[0].JobID;
+  PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+  if (VerboseMode()) gprint (GP_ERR, "added new job\n");
+  return (JobID);
+}
+
+void DelJob (Job *job) {
+
+  int i;
+
+  FREE (job[0].hostname);
+  for (i = 0; i < job[0].argc; i++) {
+    FREE (job[0].argv[i]);
+  }
+  FREE (job[0].argv);
+
+  FreeIOBuffer (&job[0].stdout_buff);
+  FreeIOBuffer (&job[0].stderr_buff);
+
+  FREE (job);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/KillJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/KillJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/KillJob.c	(revision 17422)
@@ -0,0 +1,75 @@
+# include "pcontrol.h"
+
+int KillJob (Job *job, Host *host) {
+  
+  IOBuffer buffer;
+  int status;
+  char *p;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (job != NULL, "job missing");
+
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  InitIOBuffer (&buffer, 0x100);
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, &buffer);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      HarvestHost (host[0].pid);
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_HUNG:
+      // don't do anything drastic, just keep trying
+      // XXX move to which stack??
+      gprint (GP_ERR, "client is busy, not responding (KillJob)");
+      FreeIOBuffer (&buffer);
+      return (TRUE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "message received (KillJob)\n");  
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  /** host is up, need to parse message **/
+  p = memstr (buffer.buffer, "STATUS", buffer.Nbuffer);
+  ASSERT (p != NULL, "missing STATUS in pclient message");
+  if (VerboseMode()) gprint (GP_ERR, "client message: %s\n", buffer.buffer);
+
+  sscanf (p, "%*s %d", &status);
+  FreeIOBuffer (&buffer);
+  gprint (GP_ERR, "client status: %d\n", status);
+
+  switch (status) {
+    case -1:
+      ABORT ("syntax error to pclient");
+    case 0:
+      gprint (GP_ERR, "failure to kill child process\n");
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+      return (FALSE);
+    case 1:
+      gprint (GP_ERR, "killing job %s on %s\n", job[0].argv[0], host[0].hostname);
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_CRASH, STACK_BOTTOM);
+      return (TRUE);
+    case 2:
+      ABORT ("client has no job");
+  }
+  ABORT ("should not reach here (KillJob)");
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/Makefile	(revision 17422)
@@ -0,0 +1,76 @@
+default: pcontrol
+
+include ../../../Makefile.System
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SRC     =       $(HOME)/pcontrol
+DATA    =       $(DESTDATA)/pcontrol
+include ../Makefile.Common
+
+# programs may add their own internal requirements here
+LIBS1         = -lkapa -lFITS -lohana
+LIBS2         = -lbasiccmd -lshell -ldata 
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = $(LIBS1) $(LIBS2) $(BASE_LDFLAGS)
+
+# to build the non-threaded version, remove -lpthread and comment out
+# the THREADED line in include/pcontrol.h
+
+# pcontrol user commands and support functions ########################
+funcs = \
+$(SRC)/init.$(ARCH).o \
+$(SRC)/pcontrol.$(ARCH).o \
+$(SRC)/rconnect.$(ARCH).o \
+$(SRC)/CheckBusyJob.$(ARCH).o \
+$(SRC)/CheckDoneHost.$(ARCH).o \
+$(SRC)/CheckDoneJob.$(ARCH).o \
+$(SRC)/CheckHost.$(ARCH).o \
+$(SRC)/CheckIdleHost.$(ARCH).o \
+$(SRC)/CheckPoint.$(ARCH).o \
+$(SRC)/CheckSystem.$(ARCH).o \
+$(SRC)/GetJobOutput.$(ARCH).o \
+$(SRC)/HostOps.$(ARCH).o \
+$(SRC)/IDops.$(ARCH).o \
+$(SRC)/JobOps.$(ARCH).o \
+$(SRC)/StackOps.$(ARCH).o \
+$(SRC)/PclientCommand.$(ARCH).o \
+$(SRC)/ResetJob.$(ARCH).o \
+$(SRC)/StartHost.$(ARCH).o \
+$(SRC)/StopHosts.$(ARCH).o \
+$(SRC)/KillJob.$(ARCH).o \
+$(SRC)/StartJob.$(ARCH).o
+
+cmds = \
+$(SRC)/check.$(ARCH).o \
+$(SRC)/delete.$(ARCH).o \
+$(SRC)/host.$(ARCH).o \
+$(SRC)/job.$(ARCH).o \
+$(SRC)/jobstack.$(ARCH).o \
+$(SRC)/hoststack.$(ARCH).o \
+$(SRC)/kill.$(ARCH).o \
+$(SRC)/pulse.$(ARCH).o \
+$(SRC)/run.$(ARCH).o \
+$(SRC)/status.$(ARCH).o \
+$(SRC)/stdout.$(ARCH).o \
+$(SRC)/version.$(ARCH).o \
+$(SRC)/verbose.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pcontrol: $(BIN)/pcontrol.$(ARCH)
+$(SRC)/pcontrol.$(ARCH).o : $(libs)
+$(BIN)/pcontrol.$(ARCH)   : $(cmds) $(funcs)
+
+$(cmds) $(funcs) : $(INC)/pcontrol.h
+
+install: $(DESTBIN)/pcontrol help
+
+help: clean-help cmd.basic.help pcontrol.help
+
+.PHONY: pcontrol
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/PclientCommand.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/PclientCommand.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/PclientCommand.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "pcontrol.h"
+# define PCLIENT_TIMEOUT 5000
+
+int PclientCommand (Host *host, char *command, char *response, IOBuffer *buffer) {
+
+  int i;
+  int status;
+  char *line;
+  struct timespec request, remain;
+
+  ASSERT (host != NULL, "host missing");
+  ASSERT (buffer != NULL, "buffer missing");
+  ASSERT (command != NULL, "command missing");
+  ASSERT (response != NULL, "response missing");
+
+  /* avoid blocking on read, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  // flush the stdout and stderr buffers here
+  ReadtoIOBuffer (buffer, host[0].stdout_fd);
+  FlushIOBuffer (buffer);
+  ReadtoIOBuffer (buffer, host[0].stderr_fd);
+  FlushIOBuffer (buffer);
+
+  /* send command to client (adding on \n) */
+  status = write_fmt (host[0].stdin_fd, "%s\n", command);
+
+  /* is pipe still open? */
+  if ((status == -1) && (errno == EPIPE)) {
+    gprint (GP_ERR, "pclient read gives pipe error for %s\n", command);
+    return (PCLIENT_DOWN);
+  }
+  
+  /* watch for response - wait up to 1 second */
+  line = NULL;
+  status = -1;
+  for (i = 0; (i < PCLIENT_TIMEOUT) && (status != 0) && (line == NULL); i++) {
+    status = ReadtoIOBuffer (buffer, host[0].stdout_fd);
+    line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+    if (status == -1) nanosleep (&request, &remain);
+  }
+  if (status ==  0) {
+    gprint (GP_ERR, "pclient read returns 0 for %s\n", command);
+    return (PCLIENT_DOWN);
+  }
+  if (status == -1) return (PCLIENT_HUNG);
+  /* gprint (GP_ERR, "buffer.buffer: %s\n", buffer[0].buffer); */
+  return (PCLIENT_GOOD);
+}
+
+/* memstr returns a view, not an allocated string : don't free */
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/QueueOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/QueueOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/QueueOps.c	(revision 17422)
@@ -0,0 +1,67 @@
+# include "pcontrol.h"
+
+/* get object from point in stack (negative == distance from end) */
+void *GetStack (Stack *stack, int where) {
+
+  int i;
+  void *object;
+  
+  ASSERT (stack != NULL, "stack missing");
+
+  /* STACK_TOP == 0, STACK_BOTTOM == -1 */
+  /* this code correctly handles the negative 'where' and Nobject == 0 */
+  if (where < 0) where += stack[0].Nobject;
+  if (where < 0) return (NULL);
+  if (where >= stack[0].Nobject) return (NULL);
+
+  object = stack[0].object[where];
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+  }
+  return (object);
+}
+
+/* push object on top of stack */
+int PutStack (Stack *stack, int where, void *object) {
+
+  int i;
+
+  ASSERT (stack != NULL, "stack missing");
+
+  /* STACK_TOP == 0, STACK_BOTTOM == -1 */
+  /* this code correctly handles the negative 'where' and Nobject == 0 */
+  if (where < 0) where += stack[0].Nobject + 1;
+  if (where < 0) return (FALSE);
+  if (where > stack[0].Nobject) return (FALSE);
+
+  /* extend stack as needed */
+  if (stack[0].Nobject >= stack[0].NOBJECT) {
+    stack[0].NOBJECT += 100;
+    REALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  }
+
+  for (i = stack[0].Nobject; i > where; i--) {
+    stack[0].object[i] = stack[0].object[i-1];
+  }
+  stack[0].object[where] = object;
+  stack[0].Nobject ++;
+  return (TRUE);
+}
+
+/* allocate stack, setup with default values, allocate data */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack[0].Nobject = 0;
+  stack[0].NOBJECT = 50;
+  ALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  return (stack);
+}
+
+/* these stacks are not super efficient, and should probably be replaced with linked lists, 
+   but I find these easier to get my brain around
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/ResetJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/ResetJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/ResetJob.c	(revision 17422)
@@ -0,0 +1,59 @@
+# include "pcontrol.h"
+
+int ResetJob (Job *job) {
+  
+  int       status;
+  IOBuffer  buffer;
+  Host     *host;
+
+  /** must have a valid host : if not, move to pending? **/
+  ASSERT (job != NULL, "job missing");
+
+  host = (Host *) job[0].host;
+  ASSERT (job != NULL, "host missing");
+
+  InitIOBuffer (&buffer, 0x100);
+  
+  /* we have tried to reset the job; may not get status */
+  job[0].Reset = TRUE;
+
+  status = PclientCommand (host, "reset", PCLIENT_PROMPT, &buffer);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      /*** different behavior for ANYHOST, WANTHOST, NEEDHOST? ***/
+      gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      HarvestHost (host[0].pid);
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_HUNG:
+      /*** should we consider a HUNG host DOWN? ***/
+      gprint (GP_ERR, "host %s is not responding (ResetJob)\n", host[0].hostname);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      gprint (GP_ERR, "message received (ResetJob)\n");  
+      FreeIOBuffer (&buffer);
+      return (TRUE);
+
+    default:
+      gprint (GP_ERR, "unknown status for pclient command: programming error\n");  
+      pcontrol_exit (55);
+  }
+
+  gprint (GP_ERR, "programming error in ResetJob (should not reach here)\n");
+  FreeIOBuffer (&buffer);
+  pcontrol_exit (56);
+  return (FALSE);
+}
+
+/* if machine is down, return FALSE
+   this will place job back in BUSY state,
+   next check of job will catch state and
+   put machine down correctly
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StackOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StackOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StackOps.c	(revision 17422)
@@ -0,0 +1,224 @@
+# include "pcontrol.h"
+
+/* these stacks are not super efficient, and should probably be replaced with linked lists, 
+   but I find these easier to get my brain around.
+*/
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d, NOBJECTS: %d\n", stack[0].Nobject, stack[0].NOBJECT);
+}
+
+/* allocate stack, setup with default values, allocate data */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack[0].Nobject = 0;
+  stack[0].NOBJECT = 50;
+
+  ALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].id,     int,    stack[0].NOBJECT);
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  free (stack[0].object);
+  free (stack[0].name);
+  free (stack[0].id);
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 */
+/* this code correctly handles the negative 'where' and Nobject == 0 */
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject + 1;
+  if (where < 0) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+  if (where > stack[0].Nobject) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+
+  /* extend stack as needed */
+  if (stack[0].Nobject >= stack[0].NOBJECT) {
+    stack[0].NOBJECT += 100;
+    REALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+    REALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+    REALLOCATE (stack[0].id,     int, stack[0].NOBJECT);
+  }
+
+  for (i = stack[0].Nobject; i > where; i--) {
+    stack[0].object[i] = stack[0].object[i-1];
+    stack[0].name[i]   = stack[0].name[i-1];
+    stack[0].id[i]     = stack[0].id[i-1];
+  }
+  stack[0].object[where] = object;
+  stack[0].name[where]   = name;
+  stack[0].id[where]     = id;
+  stack[0].Nobject ++;
+
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject;
+  if (where < 0) { 
+    UnlockStack (stack); 
+    return (NULL); 
+  }
+  if (where >= stack[0].Nobject) {
+    UnlockStack (stack); 
+    return (NULL);
+  }
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartHost.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartHost.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartHost.c	(revision 17422)
@@ -0,0 +1,47 @@
+# include "pcontrol.h"
+# define RETRY_BASE 10.0
+
+int StartHost (Host *host) {
+
+  int pid;
+  int stdio[3];
+  char command[64], shell[64];
+  struct timeval now;
+  float delta;
+
+  /* perhaps change the name of these config variables... */
+  if (VarConfig ("COMMAND", "%s", command) == NULL) strcpy (command, "ssh");
+  if (VarConfig ("SHELL", "%s", shell)     == NULL) strcpy (shell, "pclient");
+
+  if (VerboseMode()) gprint (GP_ERR, "starting host within thread %d\n", pthread_self());
+
+  pid = rconnect (command, host[0].hostname, shell, stdio);
+  if (!pid) {     
+    /** failure to start: extend retry period **/
+    if (VerboseMode()) gprint (GP_ERR, "failure to start %s\n", host[0].hostname);
+    gettimeofday (&now, (void *) NULL);
+    if (ZTIME(host[0].nexttry) || ZTIME(host[0].lasttry)) {
+      /* reset retry period if either is zero */
+      delta = RETRY_BASE;
+    } else {
+      delta = 2*DTIME (host[0].nexttry, host[0].lasttry);
+    }
+    host[0].nexttry.tv_sec  = now.tv_sec  + delta;
+    host[0].nexttry.tv_usec = now.tv_usec;
+    host[0].lasttry.tv_sec  = now.tv_sec;
+    host[0].lasttry.tv_usec = now.tv_usec;
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    return (FALSE);
+  }
+  host[0].nexttry.tv_sec  = 0;
+  host[0].nexttry.tv_usec = 0;
+  host[0].lasttry.tv_sec  = 0;
+  host[0].lasttry.tv_usec = 0;
+
+  host[0].stdin_fd  = stdio[0];
+  host[0].stdout_fd = stdio[1];
+  host[0].stderr_fd = stdio[2];
+  host[0].pid       = pid;
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartJob.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StartJob.c	(revision 17422)
@@ -0,0 +1,102 @@
+# include "pcontrol.h"
+
+int StartJob (Job *job, Host *host) {
+
+  int  i, Nline, status;
+  char *line, *p;
+  IOBuffer buffer;
+
+  InitIOBuffer (&buffer, 0x100);
+
+  /* job must have assigned host */
+  ASSERT (job != NULL, "missing job");
+  ASSERT (host != NULL, "missing host");
+  ASSERT (host == (Host *) job[0].host, "invalid host");
+  ASSERT (job  == (Job *) host[0].job, "invalid job");
+
+  /* construct command line : job arg0 arg1 ... argN\n */
+  Nline = 10 + job[0].argc;
+  for (i = 0; i < job[0].argc; i++) {
+    Nline += strlen (job[0].argv[i]);
+  }
+  ALLOCATE (line, char, Nline);
+  bzero (line, Nline);
+  strcpy (line, "job");
+  for (i = 0; i < job[0].argc; i++) {
+    strcat (line, " ");
+    strcat (line, job[0].argv[i]);
+  }
+
+  status = PclientCommand (host, line, PCLIENT_PROMPT, &buffer);
+  free (line);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
+      goto failure;
+
+    case PCLIENT_HUNG:
+      // we need the job start to return a valid Job ID, 
+      // give up on jobs which don't get started.
+      // XXX we are sensitive here to the time it takes pclient
+      // to fork the job.  if this is slow, the client may appear to hang.
+      gprint (GP_ERR, "host %s is not responding (StartJob)\n", host[0].hostname);
+      if (VerboseMode()) gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+
+      // unlink host & job
+      job[0].host = NULL;
+      host[0].job = NULL;
+      if (job[0].realhost) free (job[0].realhost);
+      job[0].realhost = NULL;
+      PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      return (FALSE);
+
+    case PCLIENT_GOOD:
+      if (VerboseMode()) gprint (GP_ERR, "message received (StartJob)\n");  
+      break;
+
+    default:
+      ABORT ("unknown status for pclient command");  
+  }
+
+  /* check on result of pclient command */
+  p = memstr (buffer.buffer, "STATUS", buffer.Nbuffer);
+  ASSERT (p != NULL, "missing STATUS in pclient message");
+
+  sscanf (p, "%*s %d", &status);
+  switch (status) {
+    case -1:
+      if (VerboseMode()) gprint (GP_ERR, "error in pclient child\n");
+      goto failure;
+
+    case -2:
+      ABORT ("syntax error in pclient command");
+
+    case -3:
+      ABORT ("existing child on pclient");
+
+    default:
+      job[0].realhost = strcreate (host[0].hostname);
+      job[0].pid = status;
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
+      FreeIOBuffer (&buffer);
+      gettimeofday (&job[0].start, (void *) NULL);
+      return (TRUE);
+  }
+  /* we should never reach here */
+  ABORT ("should not reach here (StartJob)");
+
+failure:
+  // unlink host & job
+  job[0].host = NULL;
+  host[0].job = NULL;
+  HarvestHost (host[0].pid);
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+  PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+  FreeIOBuffer (&buffer);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StopHosts.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 17422)
@@ -0,0 +1,157 @@
+# include "pcontrol.h"
+
+void DownHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+}
+
+void OffHost (Host *host) {
+  CLOSE (host[0].stdin_fd);
+  CLOSE (host[0].stdout_fd);
+  CLOSE (host[0].stderr_fd);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_OFF, STACK_BOTTOM);
+}
+
+/* for use by shutdown: force machines which are up to go down
+   wait for a little while for the client thread to take care 
+   of them
+*/
+   
+int DownHosts () {
+
+  int i, Nobject, Nwait;
+  Stack *stack;
+  Host  *host;
+
+  SetCheckPoint (); // ensure we can find the specified host
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+
+  stack = GetHostStack (PCONTROL_HOST_BUSY);
+  ASSERT (stack != NULL, "stack missing");
+  Nobject = stack[0].Nobject;
+  for (i = 0; i < Nobject; i++) {
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) continue;
+    host[0].markoff = TRUE;
+    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
+  }
+  ClearCheckPoint ();
+
+  Nwait = 0;
+  stack = GetHostStack (PCONTROL_HOST_IDLE);
+  ASSERT (stack != NULL, "stack missing");
+
+  gprint (GP_ERR, "waiting for clients to exit");
+  while ((Nwait < 15) && stack[0].Nobject) {
+    gprint (GP_ERR, ".");
+    usleep (100000); // wait for clients to exit
+    Nwait++;
+  }
+  gprint (GP_ERR, "\n");
+  if (stack[0].Nobject) {
+    gprint (GP_ERR, "trouble shutting down all pclient instances: %d still alive\n", stack[0].Nobject);
+  } else {
+    gprint (GP_ERR, "done\n");
+  }
+  return (TRUE);
+}
+
+int StopHost (Host *host) {
+
+  int       status;
+  IOBuffer  buffer;
+
+  InitIOBuffer (&buffer, 0x100);
+  status = PclientCommand (host, "exit", "Goodbye", &buffer);
+  FreeIOBuffer (&buffer);
+
+  /* check on success of pclient command */
+  switch (status) {
+    case PCLIENT_DOWN:
+      break;
+
+    case PCLIENT_HUNG:
+      gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
+      break;
+
+    case PCLIENT_GOOD:
+      break;
+
+    default:
+      gprint (GP_ERR, "unknown status for pclient command: programming error\n");  
+      pcontrol_exit (57);
+  }
+  HarvestHost (host[0].pid);
+  return (TRUE);
+}
+
+/* the host is thought to be down; check for child exit status */
+int HarvestHost (int pid) {
+  
+  int i, result, waitstatus;
+
+  gprint (GP_ERR, "harvesting within thread %d\n", pthread_self());
+  gprint (GP_ERR, "child process %d is down, wait for exit status\n", pid);
+  
+  // Loop a few times waiting for child to exit
+  for (i = 0; i < 50; i++) {
+    result = waitpid (pid, &waitstatus, WNOHANG);
+    if ((result == -1) && (errno == ECHILD)) {
+      usleep (10000); // wait for child to exit
+      continue;
+    } else {
+      break;
+    }
+  }
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  gprint (GP_ERR, "unknown PID, not a child proc\n");
+	  gprint (GP_ERR, "did process already exit?  programming error?\n");
+	  break;
+	case EINTR:
+	case EINVAL:
+	default:
+	  perror ("unexpected error");
+	  ABORT ("(HarvestHost)");
+      }
+      break;
+      
+    case 0:
+      gprint (GP_ERR, "child did not exit??");
+      abort ();
+      /** put back in IDLE state? **/
+      break;
+
+    default:
+      if (result != pid) {
+	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+	pcontrol_exit (58);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child exited with status %d\n", WEXITSTATUS(waitstatus));
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	if (VerboseMode()) gprint (GP_ERR, "child crashed with status %d\n", WTERMSIG(waitstatus));
+      }
+      if (WIFSTOPPED(waitstatus)) {
+        gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+	pcontrol_exit (59);
+      }
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/check.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/check.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/check.c	(revision 17422)
@@ -0,0 +1,58 @@
+# include "pcontrol.h"
+
+int check (int argc, char **argv) {
+
+  Job *job;
+  Host *host;
+  int JobID, HostID, StackID;
+
+  if (argc != 3) {
+    gprint (GP_LOG, "USAGE: check job (JobID)\n");
+    gprint (GP_LOG, "USAGE: check host (HostID)\n");
+    return (FALSE);
+  }
+
+  if (!strcasecmp (argv[1], "JOB")) {
+    JobID = atoi (argv[2]);
+
+    SetCheckPoint ();  // ensure the JOB is on one of the stacks
+    job = PullJobByID (JobID, &StackID);
+    if (job == NULL) {
+      gprint (GP_LOG, "job not found\n");
+      ClearCheckPoint ();
+      return (FALSE);
+    }
+    gprint (GP_LOG, "STATUS %s\n", GetJobStackName(StackID));
+    gprint (GP_LOG, "EXITST %d\n", job[0].exit_status);
+    gprint (GP_LOG, "STDOUT %d\n", job[0].stdout_size);
+    gprint (GP_LOG, "STDERR %d\n", job[0].stderr_size);
+    gprint (GP_LOG, "DTIME %lf\n", job[0].dtime);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "HOSTNAME %s\n", job[0].realhost);
+    } else {
+	gprint (GP_LOG, "HOSTNAME NONE\n");
+    }
+    PutJob (job, StackID, STACK_BOTTOM);
+    ClearCheckPoint ();
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "HOST")) {
+    HostID = atoi (argv[2]);
+
+    SetCheckPoint ();  // ensure the HOST is on one of the stacks
+    host = PullHostByID (HostID, &StackID);
+    if (host == NULL) {
+      gprint (GP_LOG, "host not found\n");
+      ClearCheckPoint ();
+      return (FALSE);
+    }
+    gprint (GP_LOG, "host %s\n", GetHostStackName(StackID));
+    PutHost (host, StackID, STACK_BOTTOM);
+    ClearCheckPoint ();
+    return (TRUE);
+  }
+
+  gprint (GP_LOG, "unknown item to check\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/delete.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/delete.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/delete.c	(revision 17422)
@@ -0,0 +1,45 @@
+# include "pcontrol.h"
+
+int delete (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: delete (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+  /* use a string interp to convert JobIDs to ints ? */
+
+  job = PullJobFromStackByID (PCONTROL_JOB_PENDING, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_CRASH, JobID);
+  if (job != NULL) goto found;
+
+  job = PullJobFromStackByID (PCONTROL_JOB_EXIT, JobID);
+  if (job != NULL) goto found;
+
+  gprint (GP_ERR, "job %s not PENDING, CRASH, EXIT\n", argv[1]);
+  return (FALSE);
+  
+found:
+  {
+    int j;
+    gprint (GP_LOG, "deleting job  %s  %d  ", job[0].hostname, job[0].argc);
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, "\n");
+  }  
+  DelJob (job);
+
+  return (TRUE);
+}
+
+/**** at the moment, this function requires the job to be in the correct state
+      to be deleted.  This should be changed to kill, then delete job if it is 
+      not in the correct state 
+****/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/help/host
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/help/host	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/help/host	(revision 17422)
@@ -0,0 +1,1 @@
+empty
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/host.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/host.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/host.c	(revision 17422)
@@ -0,0 +1,85 @@
+# include "pcontrol.h"
+
+// we use CheckPoints in this function to prevent objects in flight from being missing.
+int host (int argc, char **argv) {
+
+  int StackID;
+  IDtype HostID;
+  Host *host;
+
+  if (argc != 3) goto usage;
+
+  if (!strcasecmp (argv[1], "ADD")) {
+    HostID = AddHost (argv[2]);
+    gprint (GP_LOG, "HostID: %d\n", (int) HostID);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "ON")) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    host[0].markoff = FALSE;
+    DownHost (host);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "RETRY")) {
+    // no need to use a check point [thief: CheckDownHost (DOWN->IDLE)]
+    host = PullHostFromStackByName (PCONTROL_HOST_DOWN, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not DOWN\n", argv[2]);
+      return (FALSE);
+    }
+    /* reset time, place back on DOWN stack */
+    host[0].nexttry.tv_sec  = 0;
+    host[0].nexttry.tv_usec = 0;
+    host[0].lasttry.tv_sec  = 0;
+    host[0].lasttry.tv_usec = 0;
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "CHECK")) {
+    SetCheckPoint ();  // ensure the host is on one of the stacks
+    host = PullHostByName (argv[2], &StackID);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      ClearCheckPoint ();
+      return (FALSE);
+    }
+    PutHost (host, StackID, STACK_BOTTOM);
+    ClearCheckPoint ();
+
+    gprint (GP_LOG, "host %s is %s\n", argv[2], GetHostStackName (StackID));
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "OFF")) {
+    SetCheckPoint (); // ensure we can find the specified host
+    host = PullHostByName (argv[2], &StackID);
+    if (host == NULL) {
+      gprint (GP_LOG, "host %s not found\n", argv[2]);
+      ClearCheckPoint ();
+      return (FALSE);
+    }
+    host[0].markoff = TRUE;
+    PutHost (host, StackID, STACK_BOTTOM);
+    ClearCheckPoint ();
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "DELETE")) {
+    // a check point is not required: no possible thief
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
+      gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
+      return (FALSE);
+    }
+    DelHost (host);
+    return (TRUE);
+  }
+  
+usage:
+  gprint (GP_LOG, "USAGE: host (command) (hostname)\n");
+  gprint (GP_ERR, "  valid commands: add, on, retry, check, off, delete\n");
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/hoststack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/hoststack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/hoststack.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "pcontrol.h"
+
+int hoststack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Host *host;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: hoststack (hoststack)\n");
+    gprint (GP_ERR, "       (hoststack) : idle, busy, done, down, off\n");
+    return (FALSE);
+  }
+
+  /* select hoststack */
+  stack = GetHostStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "hoststack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%lld %s\n", host[0].HostID, host[0].hostname);
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/init.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/init.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/init.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "pcontrol.h"
+
+int check       PROTO((int, char **));
+int delete      PROTO((int, char **));
+int host        PROTO((int, char **));
+int hoststack   PROTO((int, char **));
+int job	        PROTO((int, char **));
+int jobstack    PROTO((int, char **));
+int kill_pc     PROTO((int, char **));
+int status      PROTO((int, char **));
+int run         PROTO((int, char **));
+int stderr_pc   PROTO((int, char **));
+int stdout_pc   PROTO((int, char **));
+int verbose     PROTO((int, char **));
+int version     PROTO((int, char **));
+
+// pulse is only available in the un-threaded version
+int pulse       PROTO((int, char **));
+
+static Command cmds[] = {  
+  {1, "host",      host,      "add / delete / modify host"},
+  {1, "hoststack", hoststack, "list hosts for a single stack"},
+  {1, "status",    status,    "get system status"},
+  {1, "stop",      run,       "stop controller processing"},
+  {1, "run",       run,       "set controller runlevel"},
+  {1, "verbose",   verbose,   "set the verbose mode for job"},
+  {1, "version",   version,   "show version information"},
+  {1, "job",       job,       "add job"},
+  {1, "jobstack",  jobstack,  "list jobs for a single stack"},
+  {1, "check",     check,     "get job or host status"},
+  {1, "delete",    delete,    "delete job"},
+  {1, "kill",      kill_pc,   "kill job"},
+  {1, "stderr",    stderr_pc, "get stderr buffer for job"},
+  {1, "stdout",    stdout_pc, "get stdout buffer for job"},
+# ifndef THREADED
+  {1, "pulse",     pulse,     "set system pulse"},
+# endif
+}; 
+
+void InitPcontrol () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+  InitJobStacks ();
+  InitHostStacks ();
+}
+
+void FreePcontrol () {
+  FreeJobStacks ();
+  FreeHostStacks ();
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/job.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/job.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "pcontrol.h"
+
+int job (int argc, char **argv) {
+
+  char *Host, **targv;
+  int i, N, Mode, targc, Timeout;
+  IDtype JobID;
+
+  if (get_argument (argc, argv, "-host") && get_argument (argc, argv, "+host")) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      return (FALSE);
+  }    
+
+  Host = NULL;
+  Mode = PCONTROL_JOB_ANYHOST;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_WANTHOST;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    remove_argument (N, &argc, argv);
+    Host = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    Mode = PCONTROL_JOB_NEEDHOST;
+  }
+  if (Host == NULL) Host = strcreate ("anyhost");
+ 
+  Timeout = 100;
+  if ((N = get_argument (argc, argv, "-timeout"))) {
+    remove_argument (N, &argc, argv);
+    Timeout = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    FREE (Host);
+    return (FALSE);
+  }
+  
+  targc = argc - 1;
+  ALLOCATE (targv, char *, targc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+
+  JobID = AddJob (Host, Mode, Timeout, targc, targv);
+  gprint (GP_LOG, "JobID: %d\n", (int) JobID);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/jobstack.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/jobstack.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/jobstack.c	(revision 17422)
@@ -0,0 +1,40 @@
+# include "pcontrol.h"
+
+int jobstack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Job *job;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: jobstack (jobstack)\n");
+    gprint (GP_ERR, "       (jobstack) : pending, busy, exit, crash, hung, done\n");
+    return (FALSE);
+  }
+
+  /* select jobstack */
+  stack = GetJobStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "jobstack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = stack[0].object[i];
+    /* PrintID (GP_LOG, job[0].JobID); */
+    gprint (GP_LOG, "%lld ", job[0].JobID);
+    if (job[0].realhost) {
+	gprint (GP_LOG, "%s   %s\n", job[0].argv[0], job[0].realhost);
+    } else {
+	gprint (GP_LOG, "%s  (%s)\n", job[0].argv[0], job[0].hostname);
+    }
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/kill.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/kill.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/kill.c	(revision 17422)
@@ -0,0 +1,24 @@
+# include "pcontrol.h"
+
+int kill_pc (int argc, char **argv) {
+
+  Job *job;
+  int JobID;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: kill (JobID)\n");
+    return (FALSE);
+  }
+  JobID = atoi (argv[1]);
+
+  /* XXX this function should only fail if a process is hung */
+  job = PullJobFromStackByID (PCONTROL_JOB_BUSY, JobID);
+  if (job == NULL) {
+    gprint (GP_ERR, "job %s not BUSY\n", argv[1]);
+    /* make output message more readable by scheduler */
+    return (FALSE);
+  }
+
+  PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pcontrol.c.in
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pcontrol.c.in	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pcontrol.c.in	(revision 17422)
@@ -0,0 +1,96 @@
+# include "pcontrol.h"
+
+# define opihi_name "PCONTROL"
+# define opihi_prompt "pcontrol: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ".pcontrol"
+# define opihi_rcfile ".pcontrolrc"
+
+/* program-dependent initialization */
+void program_init (int *argc, char **argv) {
+  
+# ifdef THREADED  
+  pthread_t clientsThread;
+# endif
+
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPcontrol ();
+
+  /* set global signal masks (these apply to all threads launched below) */
+  signal (SIGPIPE, SIG_IGN); // must ignore SIGPIPE or we get in an infinite loop when pantasks exits
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+# ifdef THREADED
+  SetRunLevel (PCONTROL_RUN_ALL);
+  pthread_create (&clientsThread, NULL, &CheckSystem_Threaded, NULL);
+  rl_event_hook = NULL;
+  rl_set_keyboard_input_timeout (1000); 
+# else
+  rl_event_hook = CheckSystem;
+  rl_set_keyboard_input_timeout (1000); 
+# endif  
+
+  // set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+
+  {
+    char *helpdir;
+    char *modules;
+    static char *datadir = "@DATADIR@";
+    ALLOCATE (helpdir, char, strlen(datadir) + strlen("/help") + 2);
+    sprintf (helpdir, "%s/help", datadir);
+    set_str_variable ("HELPDIR", helpdir);
+    free (helpdir);
+    ALLOCATE (modules, char, strlen(datadir) + strlen("/modules") + 2);
+    sprintf (modules, "%s/modules", datadir);
+    set_str_variable ("MODULES:0", modules);
+    set_int_variable ("MODULES:n", 1);
+    free (modules);
+  }
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  gprint (GP_ERR, "\n");
+  gprint (GP_ERR, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  DownHosts ();
+  ConfigFree ();
+
+  FreeBasic ();
+  FreePcontrol ();
+
+  return;
+}
+
+void gotsignal (int signum) {
+  // this message is lost if we are connected to a pantasks
+  fprintf (stderr, "got signal : %d\n", signum);
+  return;
+}
+
+/* call to opihi shell */
+int main (int argc, char **argv) {
+  int status;
+  status = opihi (argc, argv);
+  abort ();	
+  exit (status);
+}
+
+void pcontrol_exit (int n) {
+  abort ();
+  exit (n);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pulse.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pulse.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/pulse.c	(revision 17422)
@@ -0,0 +1,16 @@
+# include "pantasks.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/rconnect.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/rconnect.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/rconnect.c	(revision 17422)
@@ -0,0 +1,148 @@
+# include "pcontrol.h"
+
+/* connection can take a while, allow up to 2 sec */
+# define CONNECT_TIMEOUT 500
+
+/* connect to host, start the shell: ssh hostname pclient -> command hostname shell
+   stdio is an array of file descriptors (stdio[3]) 
+*/
+
+int rconnect (char *command, char *hostname, char *shell, int *stdio) {
+
+  int i, stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+  int result, waitstatus;
+  pid_t pid;
+  char *p;
+  char **argv;
+  IOBuffer buffer;
+  struct timespec request, remain;
+
+  ASSERT (command != NULL, "command is NULL");
+  ASSERT (hostname != NULL, "hostname is NULL");
+  ASSERT (shell != NULL, "shell is NULL");
+  ASSERT (stdio != NULL, "stdio is NULL");
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 4);
+  argv[0] = command;
+  argv[1] = hostname;
+  argv[2] = shell;
+  argv[3] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting remote connection to %s...", hostname);
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv); 
+    gprint (GP_ERR, "error starting remote shell process\n");
+    pcontrol_exit (60);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with pclient to verify alive & running */
+  /** this handshake is similar to PclientCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto connect_error;
+
+  /* try to get evidence connection is alive - wait upto a few seconds */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (10000); // wait for client to be connected
+  }
+  if (status == 0) goto connect_error;
+  if (status == -1) goto connect_error;
+  if (VerboseMode()) gprint (GP_ERR, "%d cycles to connect\n", i);
+  FreeIOBuffer (&buffer);
+
+  if (VerboseMode()) gprint (GP_ERR, "Connected\n");
+
+  stdio[0] = stdin_fd[1];
+  stdio[1] = stdout_fd[0];
+  stdio[2] = stderr_fd[0];
+
+  return (pid);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+connect_error:
+  if (VerboseMode()) gprint (GP_ERR, "error while connecting\n");
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* harvest the child process: kill & wait (< 100 ms) for exit */
+  kill (pid, SIGKILL);
+  result = waitpid (pid, &waitstatus, WNOHANG);
+  for (i = 0; (i < 50) && (result == 0); i++) {
+    nanosleep (&request, &remain);
+    result = waitpid (pid, &waitstatus, WNOHANG);
+  }
+
+  if ((result == -1) && (errno != ECHILD)) {
+    gprint (GP_ERR, "unexpected error from waitpid (%d): programming error\n", errno);
+    pcontrol_exit (61);
+  }
+  if (result == 0) {
+    if (VerboseMode()) gprint (GP_ERR, "child did not exit (rconnect)??");
+  }
+  if (result > 0) {
+    if (result != pid) {
+      gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+      pcontrol_exit (62);
+    }
+    if (WIFSTOPPED(waitstatus)) {
+      gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+      pcontrol_exit (63);
+    }
+  }
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/run.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/run.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/run.c	(revision 17422)
@@ -0,0 +1,66 @@
+# include "pcontrol.h"
+
+int run (int argc, char **argv) {
+
+  RunLevels level;
+
+  if ((argc > 2) ||
+      get_argument (argc, argv, "help") ||
+      get_argument (argc, argv, "-h") ||
+      get_argument (argc, argv, "--help")) 
+  {
+    if (!strcmp (argv[0], "run")) {
+      gprint (GP_ERR, "USAGE: run [level]\n");
+      gprint (GP_ERR, "  allowed levels:\n");
+      gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+      gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+      gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+      gprint (GP_ERR, "  none          : all stop\n");
+    } else {
+      gprint (GP_ERR, "USAGE: stop (immediate processing halt)\n");
+    }
+    return (FALSE);
+  }
+
+  level = PCONTROL_RUN_UNKNOWN;
+  if (argc == 1) {
+    if (!strcasecmp (argv[0], "run")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[0], "stop")) level = PCONTROL_RUN_NONE;
+  } else {
+    if (!strcasecmp (argv[1], "all")) level = PCONTROL_RUN_ALL;
+    if (!strcasecmp (argv[1], "reap")) level = PCONTROL_RUN_REAP;
+    if (!strcasecmp (argv[1], "host")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "hosts")) level = PCONTROL_RUN_HOSTS;
+    if (!strcasecmp (argv[1], "none")) level = PCONTROL_RUN_NONE;
+  }
+
+  if (level == PCONTROL_RUN_UNKNOWN) {
+    gprint (GP_ERR, "  unknown run level %s\n", argv[1]);
+    gprint (GP_ERR, "  allowed levels:\n");
+    gprint (GP_ERR, "  all (default) : manage machines, spawn jobs, harvest jobs\n");
+    gprint (GP_ERR, "  reap          : manage machines, harvest jobs\n");
+    gprint (GP_ERR, "  hosts         : manage machines, not jobs\n");
+    gprint (GP_ERR, "  none          : all stop\n");
+    return (FALSE);
+  }
+
+# ifdef THREADED
+  SetRunLevel (level);
+# else
+  if (level == PCONTROL_RUN_NONE) {
+    rl_event_hook = NULL;
+  } else {
+    rl_event_hook = CheckSystem;
+  }
+# endif
+
+  return (TRUE);
+}
+
+/* 
+   run levels:
+   all (manage machines, spawn jobs, harvest jobs)
+   reap (manage machines, harvest jobs)
+   hosts (manage machines, not jobs)
+   none (all stop)
+*/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/status.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/status.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "pcontrol.h"
+
+int PrintJobStack (int Nstack);
+int PrintHostStack (int Nstack);
+
+int status (int argc, char **argv) {
+
+  SetCheckPoint ();
+  PrintJobStack (PCONTROL_JOB_PENDING);
+  PrintJobStack (PCONTROL_JOB_BUSY);
+  PrintJobStack (PCONTROL_JOB_DONE);
+  PrintJobStack (PCONTROL_JOB_KILL);
+  PrintJobStack (PCONTROL_JOB_EXIT);
+  PrintJobStack (PCONTROL_JOB_CRASH);
+
+  PrintHostStack (PCONTROL_HOST_OFF);
+  PrintHostStack (PCONTROL_HOST_DOWN);
+  PrintHostStack (PCONTROL_HOST_IDLE);
+  PrintHostStack (PCONTROL_HOST_BUSY);
+  PrintHostStack (PCONTROL_HOST_DONE);
+  ClearCheckPoint ();
+  return (TRUE);
+}
+
+int PrintJobStack (int Nstack) {
+
+  int i, j, Nobject;
+  Stack *stack;
+  Job *job;
+
+  stack = GetJobStack (Nstack);
+  ASSERT (stack != NULL, "programming error");
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "job stack %s:  %d objects\n", GetJobStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    job = stack[0].object[i];
+    ASSERT (job != NULL, "programming error");
+    gprint (GP_LOG, "%d  %s  %d  ", i, job[0].hostname, job[0].argc);
+    for (j = 0; j < job[0].argc; j++) {
+      gprint (GP_LOG, "%s ", job[0].argv[j]);
+    }
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+int PrintHostStack (int Nstack) {
+
+  int i, Nobject;
+  Stack *stack;
+  Host *host;
+
+  stack = GetHostStack (Nstack);
+
+  LockStack (stack);
+  Nobject = stack[0].Nobject;
+  gprint (GP_LOG, "host stack %s:  %d objects\n", GetHostStackName(Nstack), Nobject);
+
+  for (i = 0; i < Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%d  %s  ", i, host[0].hostname);
+    PrintID (GP_LOG, host[0].HostID);
+    gprint (GP_LOG, "\n");
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stdout.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stdout.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stdout.c	(revision 17422)
@@ -0,0 +1,71 @@
+# include "pcontrol.h"
+
+int stdout_pc (int argc, char **argv) {
+
+  int JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stdout (JobID)\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  gprint (GP_LOG, "STATUS %d\n", -2);
+  return (FALSE);
+
+found_stdout:
+  buffer = &job[0].stdout_buff;
+  fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
+
+int stderr_pc (int argc, char **argv) {
+
+  int JobID, StackID;
+  Job *job;
+  IOBuffer *buffer;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: stderr (JobID)\n");
+    gprint (GP_LOG, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  /* find Job of interest (must be EXIT or CRASH) */
+  JobID = atoi (argv[1]);
+
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  gprint (GP_LOG, "STATUS %d\n", -2);
+  return (FALSE);
+
+found_stderr:
+  buffer = &job[0].stderr_buff;
+  fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
+  gprint (GP_LOG, "STATUS %d\n", 0);
+  PutJob (job, StackID, STACK_BOTTOM);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stop.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stop.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/stop.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "pcontrol.h"
+
+int stop (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: stop\n");
+    return (FALSE);
+  }
+
+# ifdef THREADED
+  SetRunSystem (FALSE);
+# else
+  rl_event_hook = NULL;
+# endif
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/verbose.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/verbose.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/verbose.c	(revision 17422)
@@ -0,0 +1,37 @@
+# include "pcontrol.h"
+
+static int VERBOSE = FALSE;
+
+int verbose (int argc, char **argv) {
+
+  if (argc == 1) {
+    if (VERBOSE) {
+      gprint (GP_ERR, "verbose mode ON\n");
+    } else {
+      gprint (GP_ERR, "verbose mode OFF\n");
+    }
+    return (TRUE);
+  }
+
+  if (argc == 2) {
+    if (!strcasecmp (argv[1], "ON")) {
+      VERBOSE = TRUE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "OFF")) {
+      VERBOSE = FALSE;
+      return (TRUE);
+    }
+    if (!strcasecmp (argv[1], "TOGGLE")) {
+      VERBOSE = ~VERBOSE;
+      return (TRUE);
+    }
+  }
+
+  gprint (GP_ERR, "USAGE: verbose (on/off/toggle)\n");
+  return (FALSE);
+}
+
+int VerboseMode () {
+  return (VERBOSE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/version.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/version.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/pcontrol/version.c	(revision 17422)
@@ -0,0 +1,17 @@
+# include "pcontrol.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  char *tmp;
+
+  gprint (GP_LOG, "\n");
+  gprint (GP_LOG, "pcontrol version: %s\n", (tmp = strip_version (name))); free (tmp);
+
+  gprint (GP_LOG, "opihi version: %s\n", (tmp = strip_version (opihi_version()))); free (tmp);
+  gprint (GP_LOG, "ohana version: %s\n", (tmp = strip_version (ohana_version()))); free (tmp);
+  gprint (GP_LOG, "gfits version: %s\n", (tmp = strip_version (gfits_version()))); free (tmp);
+
+  gprint (GP_LOG, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copy.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copy.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copy.pro	(revision 17422)
@@ -0,0 +1,136 @@
+
+# identify the images ready for copy 
+task	       new.images
+  command      new.images
+  host         local
+
+  periods      -poll 1
+  periods      -exec 30
+  periods      -timeout 2
+
+  # success
+  task.exit    0
+    push new.images $stdout
+    $new.image.failure = 0
+  end
+
+  # locked list
+  task.exit    1
+    echo       "new.images: exec failure"
+    $new.image.failure ++
+  end
+
+  # default exit status
+  task.exit    -
+    echo       "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "new.images: timeout"
+    $new.image.failure ++
+  end
+end
+
+# copy specific images from the summit
+task           copy.images
+
+  # these define task properties which are fixed
+
+  period       -exec 2
+  period       -poll 1
+  period       -timeout 50
+
+  # these commands are executed at the start of a new task
+  task.exec
+    local        RemoteName
+    local        FileID
+    local        Host
+
+    queuesize  NewImages -var N
+    if ($N > 0) break
+    if ($network == 0) break
+    if ($filesystem == 1) break
+
+    pop NewImages -var line
+    list tmp -split $line
+    $RemoteName = $tmp:0
+    $FileID     = $tmp:1
+    $Host       = $tmp:2
+    $Ntry       = $tmp:3
+
+    stderr       /data/logfiles/copy/$FileID.log
+    stdout       -queue $FileIDlog
+
+    echo $TaskID
+    echo $TaskName
+    spawn copy.images $RemoteName $FileID $Host -host $Host
+  end
+
+  # success
+  task.exit      0
+    $new.image.failure --
+  end
+
+  # summmit connection failed
+  task.exit      1
+    echo         "copy.images: copy failed $RemoteName $FileID"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # target disk full
+  task.exit      2
+    echo         "copy.images: disk full"
+    push         NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+
+  # file not found
+  task.exit      3
+    echo         "copy.images: missing file"
+    # send a message to OTIS?
+  end
+
+  # task timed out
+  task.exit      timeout
+    echo        "copy.images: timeout"
+    push        NewImages "$RemoteName $FileID $Host 0"
+    $copy.image.failure ++
+  end
+end
+
+# identify the images ready for copy 
+TASK	       new.images
+  COMMAND      new.images
+  HOST         -
+  STDERR       /data/logfiles/new.images.log
+  STDOUT       $stdout
+  EXEC_PERIOD  30
+  POLL_PERIOD  1
+
+  # success
+  EXIT         0
+    STDOUT     @new.images
+    $new.image.failure = 0
+  END
+
+  # locked list
+  EXIT         1
+    MESSAGE    "new.images: exec failure"
+    $new.image.failure ++
+  END
+
+  # default exit status
+  EXIT         -
+    MESSAGE    "new.images: unknown exit status: $EXIT"
+    $new.image.failure ++
+  END
+
+  # operation times out?
+  TIMEOUT      2
+    MESSAGE    "new.images: timeout"
+    $new.image.failure ++
+  END
+END
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.pro	(revision 17422)
@@ -0,0 +1,117 @@
+
+## A test task to demonstrate cycled copying using pending and done pools (directories)
+##   and pending queues
+
+queueinit stdout
+queueinit stderr
+queueinit pending
+
+$indir = dir.01
+$outdir = dir.02
+
+# set the test rate to be 1ms
+pulse 1000
+
+macro init
+  exec rm -r dir.*
+  exec mkdir $indir
+  exec mkdir $outdir
+end
+
+$Nfile = 0
+task pool.create
+  periods -exec 0.02
+  periods -poll 0.02
+
+  task.exec
+    sprintf filename "foo.%04d.dat" $Nfile
+    $Nfile ++
+    host local
+    command copypool.sh -create $indir $filename
+  end
+
+  task.exit  default
+    queueinit stdout
+    queueinit stderr
+  end
+end
+
+task pool.pending
+  command copypool.sh -pending $indir
+  host local
+
+  periods -exec 5
+  periods -poll 1
+
+  # success
+  task.exit    0
+    local i Nstdout
+
+    # keep only new, unique entries (name is key)
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush pending -uniq -key 0 "$line new"
+    end
+    queueprint pending
+  end
+end
+
+task pool.copy
+  host local
+
+  periods -exec 1
+  periods -poll 1
+
+  task.exec
+    local Npending
+    queuesize pending -var Npending
+    if ($Npending == 0) 
+      periods -exec 1
+      periods -poll 1
+      break
+    end
+
+    # if data is available, run fast to clear it out
+    periods -exec 0.002
+    periods -poll 0.002
+
+    # this step grabs and entry by key and updates a field
+    # can we do this in one step?
+    queuepop pending -var line -key 1 new
+    if ("$line" == "NULL") break
+
+    list tmp -split $line
+    $name = $tmp:0
+
+    queuepush pending "$name run"
+
+    host local
+    command copypool.sh -copy $outdir $name
+  end
+
+  # success
+  task.exit 0
+    echo "done with $taskarg:3"
+    queueinit stdout
+    queueinit stderr
+    queuepop pending -key 0 $taskarg:3 -var line
+    # echo "got line: $line"
+
+    if ("$line" == "NULL") 
+      echo "missing entry in pending queue?"
+      break
+    end
+  end
+end
+
+task pool.list
+  host local
+  periods -exec 3
+  command queueprint pending
+
+  task.exit default
+    queueinit stdout
+    queueinit stderr
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/copypool.sh	(revision 17422)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# copypool.sh -pending (indir)
+# copypool.sh -copy (outdir) (path)
+# copypool.sh -create (outdir) (name)
+
+if ($#argv < 2) then
+  echo "USAGE: copypool.sh -pending (indir)"
+  echo "USAGE: copypool.sh -copy (outdir) (path)"
+  echo "USAGE: copypool.sh -create (outdir) (name)"
+  exit 2
+endif
+
+if ("$1" == "-pending") then
+  set n = `ls $2 | wc -l`
+  if ($n == 0) exit 0
+  ls $2/* | cat
+  exit 0
+endif
+
+if ("$1" == "-copy") then
+  set name = `basename $3`
+  rm $3
+  touch $2/$name
+  exit 0
+endif
+
+if ("$1" == "-create") then
+  touch $2/$3
+  exit 0
+endif
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/diff-opihi
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/diff-opihi	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/diff-opihi	(revision 17422)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs diff -r $2 Makefile
+  cvs diff -r $2 Makefile.Common
+  cvs diff -r $2 include
+  cvs diff -r $2 test
+  cvs diff -r $2 lib.data
+  cvs diff -r $2 lib.shell
+  cvs diff -r $2 cmd.astro
+  cvs diff -r $2 cmd.basic
+  cvs diff -r $2 cmd.data
+  cvs diff -r $2 scripts
+  cvs diff -r $2 doc
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # diff the module ($1) with the specified tag ($2)
+  cvs diff -r $2 $1
+  exit 0;
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/jobs.plot
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/jobs.plot	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/jobs.plot	(revision 17422)
@@ -0,0 +1,24 @@
+
+macro jobs.rates
+ clear -s
+
+ section a 0.0 0.0 1.0 0.5
+ data jobs.stop.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+ 
+ section b 0.0 0.5 1.0 0.5
+ data jobs.run.dat
+ read t 6
+ set T = t*(t > 30) + (t+60)*(t<30)
+ create n 0 t[]
+ lim T n; box; plot T n
+ subset tf = T if (T > 70)
+ subset nf = n if (T > 70)
+ fit tf nf 1
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/manytasks.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/manytasks.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/manytasks.pro	(revision 17422)
@@ -0,0 +1,72 @@
+
+controller exit true
+controller host add kiawe
+
+$Ntest = 0
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+macro mktask
+  if ($0 != 2)
+    echo "USAGE: mktask (n)"
+    break
+  end
+
+  $N = $1
+
+  task test.$N
+    command partest
+    # polling period is no longer valid: we check for completed controller tasks
+    # correction: still valid for local tasks
+    periods -poll 0.2
+    periods -exec 0.01
+    periods -timeout 10.0
+    nmax 20
+    host anyhost
+
+    # stdout / stderr lines on named queues
+    task.exit 0
+      # echo "task exit 0"
+      queuedelete stdout
+      queuedelete stderr
+      date date
+      queuepush done "$date"
+      $Ntest ++
+    end
+
+    task.exit 1
+      # echo "task exit 1"
+      queuesize stdout
+      queuesize stderr
+    end
+
+    task.exit crash
+      echo "crashed job"
+    end
+
+    task.exit timeout
+      output timeout.log
+      echo $stdout
+      output stdout
+    end
+  end
+end
+
+macro mktasks
+ for i 0 $1
+  mktask $i
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/psched.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/psched.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/psched.pro	(revision 17422)
@@ -0,0 +1,99 @@
+
+controller exit true
+# controller host add kiawe
+$Ntest = 0
+# controller host add alala
+# verbose on
+pulse 1000
+controller pulse 1000
+
+macro load.machines
+  if ($0 != 2)
+    echo "load.machines (nmach)"
+    break
+  end
+
+  for i 0 $1
+    $n = $i + 1
+    sprintf host "po%02d" $n
+    controller host add $host
+  end
+end
+
+task test
+  command partest
+  # polling period is no longer valid: we check for completed controller tasks
+  # correction: still valid for local tasks
+  periods -poll 0.20
+  periods -exec 0.0001
+  periods -timeout 10.0
+  nmax 1024
+#  nmax 100
+  host anyhost
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+    # echo "task exit 0"
+    queuedelete stdout
+    queuedelete stderr
+    date date
+    queuepush done "$date"
+    $Ntest ++
+#   memory leaks
+#   queuesize stdout -var Nstdout
+#    for i 0 $Nstdout
+#      queuepop stdout -var line
+#      queuepush results "$line"
+#    end
+  end
+
+  task.exit 1
+    # echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
+
+# pulse == 100ms
+# poll/exit = 0.2  : 29 sec / 100 jobs
+# poll/exit = 0.1  : 20 sec / 100 jobs
+# poll/exit = 0.05 : 17 sec / 100 jobs
+# poll/exit = 0.01 : 18 sec / 100 jobs
+
+# pulse == 10ms
+# poll/exit = 0.2  : 20 sec / 100 jobs
+# poll/exit = 0.10 : 12 sec / 100 jobs
+# poll/exit = 0.05 : 12 sec / 100 jobs
+# poll/exit = 0.01 :  9 sec / 100 jobs
+
+# we are limited here by how quickly we can send data to the 
+# controller.  this is limited by the occasional 'CheckSystem'
+# loops, with ~40ms minimum.
+
+# seems to be faster on po01 from kiawe (less interference?)
+
+# pulse == 1ms, controller pulse == 1ms
+# poll/exit = 0.01 :  3 sec / 100 jobs
+# 2 mach, 3 sec
+# 4 mach, 3 sec
+# 8 mach, 3 sec
+
+# 16 machines, 500 jobs, 13 sec: 26ms / job
+# 32 machines, 1024 jobs, 26 sec: 26ms / job
+# job harvesting rate is still the limitation.  Each job harvest requires:
+#  - jobstack exit
+#  - stdout
+#  - stderr
+#  - delete
+#  - jobstack crash
+
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/queuetests
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/queuetests	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/queuetests	(revision 17422)
@@ -0,0 +1,19 @@
+
+macro test.unique
+  list line -x "cat new.list"
+  for i 0 $line:n
+    push t1 "$line:$i"
+  end
+
+  queuelist
+  queuesize t1
+
+  list line -x "cat copy.list"
+  for i 0 $line:n
+    push t1 "$line:$i" -uniq
+  end
+
+  queuelist
+  queuesize t1
+
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/rdiff-elixir
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/rdiff-elixir	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/rdiff-elixir	(revision 17422)
@@ -0,0 +1,31 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the elixir components
+
+if ($#argv != 1) then
+  echo "USAGE: rdiff-elixir (TAG)"
+  exit 2
+endif
+
+cvs rdiff -r $1 opihi/Makefile
+cvs rdiff -r $1 opihi/bin/
+cvs rdiff -r $1 opihi/cmd.astro/
+cvs rdiff -r $1 opihi/cmd.basic/
+cvs rdiff -r $1 opihi/cmd.data/
+cvs rdiff -r $1 opihi/doc/
+cvs rdiff -r $1 opihi/dvo/
+cvs rdiff -r $1 opihi/help/
+cvs rdiff -r $1 opihi/include/
+cvs rdiff -r $1 opihi/lib/
+cvs rdiff -r $1 opihi/lib.data/
+cvs rdiff -r $1 opihi/lib.shell/
+cvs rdiff -r $1 opihi/mana/
+cvs rdiff -r $1 opihi/scripts/
+
+# don't place tag on these implementations
+# cvs tag $1 dimm/
+# cvs tag $1 dvo2/
+# cvs tag $1 old/
+# cvs tag $1 pclient/
+# cvs tag $1 pcontrol/
+# cvs tag $1 sched/
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/run.pclient
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/run.pclient	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/run.pclient	(revision 17422)
@@ -0,0 +1,4 @@
+#!/bin/csh -f
+
+# pclient
+mana
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sample.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sample.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sample.sh	(revision 17422)
@@ -0,0 +1,9 @@
+#!/usr/bin/env dvo
+
+echo "test"
+
+for i 0 $argv:n
+ echo $i $argv:$i
+end
+
+exit 0
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sched.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sched.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/sched.pro	(revision 17422)
@@ -0,0 +1,43 @@
+
+task test
+  command ls
+  periods -poll 1.0
+  periods -exec 2.0
+  periods -timeout 10.0
+  # trange 07:09 07:10
+  # trange -exclude 07:09:30 07:09:45
+  nmax 5
+  host local
+  # host localhost
+  # local is default 
+
+  task.exec
+    # echo "starting job sched.test"
+  end
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+#    echo "task exit 0"
+#    queuesize stdout
+#    queuesize stderr
+    queuedelete stdout
+    queuedelete stderr
+    memory leaks
+  end
+
+  task.exit 1
+    echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/tag-opihi
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/tag-opihi	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/tag-opihi	(revision 17422)
@@ -0,0 +1,38 @@
+#!/bin/csh -f
+
+# this just organizes the tagging of the opihi components
+
+if ($#argv != 2) then
+  echo "USAGE: tag-elixir (MODULE) (TAG)"
+  echo "  MODULE choices: base mana dvo dimm pantasks pcontrol pclient"
+  exit 2
+endif
+
+if ("$1" == "base") then
+  cvs tag $2 Makefile
+  cvs tag $2 Makefile.Common
+  cvs tag $2 include/
+  cvs tag $2 test/
+  cvs tag $2 lib.data/
+  cvs tag $2 lib.shell/
+  cvs tag $2 cmd.astro/
+  cvs tag $2 cmd.basic/
+  cvs tag $2 cmd.data/
+  cvs tag $2 scripts/
+  cvs tag $2 doc/
+  exit 0;
+endif
+
+if ("$1" == "mana") goto valid;
+if ("$1" == "dvo") goto valid;
+if ("$1" == "dimm") goto valid;
+if ("$1" == "pantasks") goto valid;
+if ("$1" == "pclient") goto valid;
+if ("$1" == "pcontrol") goto valid;
+echo "invalid opihi module $1"
+exit 1;
+
+valid:
+  # tag the module ($1) with the specified tag ($2)
+  cvs tag $2 $1
+  exit 0;
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/test.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/test.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/test.pro	(revision 17422)
@@ -0,0 +1,144 @@
+
+macro testgauss
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = $3*exp(-0.5*(X^2/$2^2)) + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1+5
+ $C1 = $2-10
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testpoor
+ create x -20 20 0.1
+ set dy = zero(x) + 0.03
+ set X = x - $1
+ set y = 3 + zero(x) + 0.1*(rnd(X) - 0.5)
+# set y = (x + 20)/10 + $4 + 0.1*(rnd(X) - 0.5)
+# set y = $3*exp(-0.5*(X^2/$2^2)) + $4
+ lim x y; clear; box; plot x y -dy dy -x 2 -pt 2
+
+ $C0 = $1
+ $C1 = $2
+ $C2 = $3
+ $C3 = $4
+
+ vgauss x y dy yf
+end
+
+macro testfit
+ create x 0 1000
+ set y = 3 + 5*x
+ set dy = 10*(rnd(x) - 0.5)
+ set yr = y + dy
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ yr[100] = yr[100] + 50
+ yr[200] = yr[200] - 50
+ yr[300] = yr[300] + 100
+ yr[400] = yr[400] - 500
+ yr[500] = yr[500] + 30
+
+ fit x yr 1
+ echo $Cn, $Cnv
+ echo $dC
+
+ fit x yr 1 -clip 3 3
+ echo $Cn, $Cnv
+ echo $dC
+end
+# expected output:
+# y = 3.013781 x^0 5.000005 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 2.91071543189
+# y = 2.495040 x^0 5.000303 x^1
+#     0.063198     0.000110
+# 1, 1000
+# 16.5928919554
+# y = 3.021974 x^0 4.999991 x^1
+#     0.063419     0.000110
+# 1, 995
+# 2.90820510001
+
+macro testloops
+ for i 0 100
+  if ($i < 20)
+   continue
+  end
+  if ($i > 40)
+   break
+  end
+  echo $i
+ end
+end  
+
+# test math parsing
+macro testvars
+ echo testing variable assignment
+ $a = 1
+ echo $a is 1
+ $b = 5*3 + $a
+ echo $b is 16
+
+ $a = `ls`
+ echo $a
+ exec ls
+
+ echo testing in-line math
+ $a = 1
+ echo {1 + 1} is 2
+ echo {2^3} is 8
+ echo {$a + 2} is 3
+
+ echo testing vector assignment
+ create x 0 10
+ set y = x^2
+ echo x[2] is 2
+ echo y[2] is 4
+ lim x y; clear; box; plot x y
+ label -x "x axis" -y "&s y axis"
+end
+
+macro testspeed
+ exec date
+ create x 0 100000
+ for i 0 5000
+  set y = 3*x + $i
+ end
+ exec date
+end
+
+macro testmemory.buffers
+ mcreate a 2048 2048
+ exec date
+ for i 0 500
+  set b = 3*a + 6
+ end
+ exec date
+end
+
+macro testmemory.macros
+ exec date
+ for i 0 1000
+   for j 0 1000
+    $N = $N + 1
+   end
+ end
+ exec date
+end
+
+macro sample
+ $N = 0
+ for j 0 1000000
+  $N = 100
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testloops.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testloops.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testloops.pro	(revision 17422)
@@ -0,0 +1,90 @@
+
+macro testloop1
+ local i
+ for i 0 10
+  echo "1: $i"
+    end
+end
+
+macro testloop2
+ local i
+ for i 0 5
+   echo "2: $i"
+   testloop1
+end
+end
+
+macro testlocal1
+ local -static foo
+ echo "1 - foo: $foo"
+ $foo = local.1
+ echo "1 - foo: $foo"
+end
+
+macro testlocal2
+ local foo
+ $foo = local.2
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+ testlocal1
+ echo "2 - foo: $foo"
+end
+
+macro test1
+ # echo step $1
+ if ($1 == 1)
+  echo "break here"
+  break
+ end
+ if ($1 == 2)
+  continue
+ end
+end
+
+# continue
+
+macro test2
+ echo "input test"
+ break
+ echo "error, can't get here"
+end
+
+macro testm
+ break -auto off
+ test1 0
+ # echo $STATUS
+ test1 1
+ # echo $STATUS
+ test1 2
+ # echo $STATUS
+ if ($1 == 10) 
+  test1 1
+  if ($STATUS == 0)
+   echo "breaking it"
+   break
+  end
+ end
+ echo done
+end
+
+echo "done loading"
+
+macro test3
+ for i 0 12
+  echo $i
+  testm $i
+  echo "status: $STATUS"
+ end
+end
+
+macro testL
+ for i 0 10
+  for j 0 10
+   for k 0 10
+    echo $i $j $k
+    mcreate a 1024 1024   
+   end
+  end
+ end
+end
Index: /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testscript
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testscript	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/scripts/testscript	(revision 17422)
@@ -0,0 +1,16 @@
+#!/bin/csh -f
+
+date
+
+ls -d *
+# sleep 3
+ls foo
+
+foreach f (*)
+  echo $f
+  ls $f/*
+#  sleep 1
+end
+
+date
+#exit 3
Index: /tags/ipp-2-5-1/Ohana/src/opihi/test/basic.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/test/basic.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/test/basic.sh	(revision 17422)
@@ -0,0 +1,9 @@
+
+list testdir
+  cmd.data/test
+  cmd.basic/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /tags/ipp-2-5-1/Ohana/src/opihi/test/pclient.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/test/pclient.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/test/pclient.sh	(revision 17422)
@@ -0,0 +1,8 @@
+
+list testdir
+  pclient/test
+end
+
+input test/tests.sh
+
+fulltests
Index: /tags/ipp-2-5-1/Ohana/src/opihi/test/tests.sh
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/opihi/test/tests.sh	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/opihi/test/tests.sh	(revision 17422)
@@ -0,0 +1,100 @@
+
+if ($?VERBOSE == 0)
+ $VERBOSE = 0
+end
+
+$failtest:n = 0
+$failfile:n = 0
+$faildirs:n = 0
+$currentdir = .
+
+macro fulltests
+  $Npass = 0
+  $Nfail = 0
+  $Ntest = 0
+  break -auto off
+
+  $failtest:n = 0
+  $failfile:n = 0
+  $faildirs:n = 0
+
+  for Ti 0 $testdir:n
+    if ($VERBOSE > 0)
+       echo "directory $testdir:$Ti"
+    end
+    runtestdir $testdir:$Ti
+  end
+
+  echo "completed $Ntest tests"
+  echo "$Npass tests passed"
+  echo "$Nfail tests failed"
+  echo "examined $testdir:n directories"
+
+  if ($Nfail > 0) 
+    echo " ** failed tests **"
+    for i 0 $Nfail
+      echo $faildirs:$i $failfile:$i $failtest:$i
+    end
+  end
+end
+
+macro runtestdir
+  if ($0 != 2)
+    echo "USAGE: runtestdir (testdir)"
+    break -auto on
+    break
+  end
+  
+  pwd -var startdir
+  cd -q $1
+  list testscripts -x "ls *.sh"
+
+  $currentdir = $1
+  for Tj 0 $testscripts:n
+    if ($VERBOSE > 1)
+      echo " running $testscripts:$Tj"
+    end
+    runtests $testscripts:$Tj      
+  end
+  
+  cd -q $startdir
+end
+
+macro runtests
+  if ($0 != 2)
+    echo "USAGE: runtests (script.sh)"
+    break -auto on
+    break
+  end
+  
+  local Tk
+
+  input $1
+  for Tk 0 $tests:n
+    if ($VERBOSE > 2)
+      echo "   running $tests:$Tk"
+    end
+    $PASS = 1
+    $tests:$Tk
+    if ($PASS == 0)
+      echo "   ** failed $tests:$Tk $1"
+      $Nfail ++
+      $n = $failtest:n
+      $failtest:$n = $tests:$Tk
+      $failfile:$n = $1
+      $faildirs:$n = $currentdir
+      $failtest:n ++
+      $failfile:n ++
+      $faildirs:n ++
+    else
+      $Npass ++
+    end
+    $Ntest ++
+    # echo "finished $tests:$Tk"
+  end
+end
+
+echo "USAGE:"
+echo "runtests (script.sh)"
+echo "runtestdir (testdir)"
+echo "set VERBOSE to 0 through 3"
Index: /tags/ipp-2-5-1/Ohana/src/relastro/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/Makefile	(revision 17422)
@@ -0,0 +1,60 @@
+default: relastro
+help:
+	@echo "make options: relastro (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/relastro
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+relastro: $(BIN)/relastro.$(ARCH)
+install: $(DESTBIN)/relastro
+
+RELASTRO = \
+$(SRC)/ConfigInit.$(ARCH).o	     \
+$(SRC)/FitChip.$(ARCH).o             \
+$(SRC)/FitMosaic.$(ARCH).o           \
+$(SRC)/FitPM.$(ARCH).o               \
+$(SRC)/FitPar.$(ARCH).o              \
+$(SRC)/FitPMandPar.$(ARCH).o         \
+$(SRC)/FitSimple.$(ARCH).o           \
+$(SRC)/ImageOps.$(ARCH).o	     \
+$(SRC)/MosaicOps.$(ARCH).o	     \
+$(SRC)/ParFactor.$(ARCH).o           \
+$(SRC)/SetSignals.$(ARCH).o 	     \
+$(SRC)/Shutdown.$(ARCH).o 	     \
+$(SRC)/UpdateChips.$(ARCH).o         \
+$(SRC)/UpdateMosaic.$(ARCH).o        \
+$(SRC)/UpdateObjects.$(ARCH).o       \
+$(SRC)/UpdateObjectOffsets.$(ARCH).o \
+$(SRC)/UpdateSimple.$(ARCH).o        \
+$(SRC)/UpdateMeasures.$(ARCH).o      \
+$(SRC)/GetAstromError.$(ARCH).o      \
+$(SRC)/args.$(ARCH).o		     \
+$(SRC)/bcatalog.$(ARCH).o	     \
+$(SRC)/dvo_astrom_ops.$(ARCH).o      \
+$(SRC)/fitpoly.$(ARCH).o             \
+$(SRC)/initialize.$(ARCH).o	     \
+$(SRC)/liststats.$(ARCH).o	     \
+$(SRC)/load_catalogs.$(ARCH).o	     \
+$(SRC)/load_images.$(ARCH).o	     \
+$(SRC)/mkpolyterm.$(ARCH).o          \
+$(SRC)/plot_scatter.$(ARCH).o	     \
+$(SRC)/plotstuff.$(ARCH).o	     \
+$(SRC)/select_images.$(ARCH).o	     \
+$(SRC)/relastro.$(ARCH).o	     \
+$(SRC)/relastro_objects.$(ARCH).o    \
+$(SRC)/save_catalogs.$(ARCH).o       \
+$(SRC)/write_coords.$(ARCH).o
+
+$(RELASTRO): $(INC)/relastro.h $(KAPA_INCS)
+$(BIN)/relastro.$(ARCH): $(RELASTRO) $(KAPA_LIBS)
Index: /tags/ipp-2-5-1/Ohana/src/relastro/bin/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/bin/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/bin/.cvsignore	(revision 17422)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/ipp-2-5-1/Ohana/src/relastro/doc/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/doc/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/doc/notes.txt	(revision 17422)
@@ -0,0 +1,124 @@
+
+2008.03.01
+
+  relastro (and relphot for that matter) needs to have a somewhat
+  flexible error model for the detections.  Some data have an accurate
+  astrometric error; some have a reported error, but it is either
+  insufficient (eg, only from poisson error), or inaccurate.  A
+  complete error model would be something like this:
+
+  dR_total^2 =  dR_sys^2 + AS * dR_obs^2 + MS * dM_obs^2
+
+  dR_sys : systematicAstrometryError
+  AS     : astrometryErrorScale
+  MS     : astrometryErrorMagScale
+
+2007.11.12
+
+  relastro is working for the SIMPLE (single-chip) and CHIP
+  (mosaic-chip) modes.  it is not yet working for the MOSAIC mode.
+  The image table contains one mosaic entry and N chip entries for a
+  mosaic with N chips.  The mosaic entry has coord.ctype = DIS, while
+  the corresponding chips have coord.ctype = WRP (and matching
+  time/photcodes).
+
+  (this is fairly weak. we should just define the relationship when we
+  register the chips...)
+
+2007.10.06
+
+  relastro currently loads the set of detections, averages, and image,
+  and sets up arrays of pointers to define the relationships beteen
+  the detections, objects, and images.  This is probably an acceptable
+  scheme.  
+
+  However, the generation of those index arrays is probably very
+  inefficient.  Currently it is written for the old DVO tables in
+  which there was no real link between the detection and image.  We
+  are trying to move to a situation where each detection has a
+  reference to the image (imageID).  If this is unique, then we should
+  be able to make these links directly.
+
+  Outstanding issues:
+
+  * am I getting the correct set of matches for the mosaic images? 
+    ie, do getImageRaw & getImageRef return the correct list when the
+    image is a DIS mosaic image?
+
+  * is the operation in fit_apply_coords doing the correct thing?
+    compare this to psastro code.  This should not be a problem since
+    we are only fitting in the linear frames.  we are just fitting
+    simple polynomials.  We are not changing the reference coordinate
+    for the projection.
+
+  * measure the errors?
+
+  * apply the magnitude errors as weight?
+
+  * improve matching process with the index information available?
+
+  * add the ability to down-weight groups of detections by photcode?
+
+2007.02.11
+
+  relastro major modes:
+
+  - update the astrometry of objects in the images
+    - load objects within a region
+    - foreach object
+      - calculate average R,D
+      - update db tables (dR, dD)
+    - include external trends
+      - parallax factor
+      - atm trends
+
+  - update the astrometric parameters of images
+    - use the average R,D for objects, update image terms
+    * include external refs with adjustable weighting
+
+  - image parameter smoothing
+
+  - simultaneous fit of objects and images
+    - identify parameters to constrain
+    - limited number of internal objects?
+
+
+2006.04.08 : relastro contemplations
+
+relastro will perform the astrometry equivalent to relphot.  For
+region, it loads in the average objects, detections, and image
+parameters.  The goal is to determine improved astrometric positions
+of objects in the images and to determine improved astrometric
+parameters for the images.  Just like relphot, this is an iterative
+process in which the object postions are improved in one step, the
+image parameters are improved in the next, then back to the images.
+
+Relastro needs to deal with outlier objects in a robust way.  There
+are a few reasons why objects should have poor astrometric solutions,
+and these can be flagged up front: saturation and too many bad pixels.
+For detections which come from psphot, we can use the quality
+parameter to exclude objects which have excessively high bad pixel
+counts.  For detections from sextractor or other analysis tools,
+this information is missing.  In this case, we can use the distance
+from the image boundaries as a trigger.
+
+An open question to be addressed is the issue of external calibration
+sources.  Unlike photometry, external astrometric measurements do not
+need to have an unknown filter transformation applied.  They can thus
+be included as part of the solution automatically.  It is not clear,
+however, how to include *proper-motion* data in the analysis.  If I
+introduce, for example, USNO objects with proper motions, the
+information provided consists in practice of a velocity vector and
+errors.  How does this get folded into the fit?  One answer is to
+construct additional artificial datapoints for each entry with the
+proper errors and epochs.  Seems like something of a hack.  
+
+We may want to allow for different errors / weightings for the
+different external sources.
+
+After the initial iterations, we will can solve for proper-motion and
+parallax for (all?) objects
+
+- calculate the parallax normal vector for each image
+- calculate the atmospheric / orbital motion effects for each image
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/doc/parallax.pro
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/doc/parallax.pro	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/doc/parallax.pro	(revision 17422)
@@ -0,0 +1,348 @@
+
+macro sample
+  PMsim 15 30 20 10 $ANGLE
+  set dX = zero(X) + 10
+  set dY = zero(Y) + 10
+
+  style -c black
+  plot -x 2 -pt 2 -sz 2.0 X Y -dx dX -dy dY
+
+  # set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  # set y = (1000.0/$dS)*sin(t*$TP)*dsin($ANGLE) + $vY*t
+  # plot -c grey60 -x 2 -sz 2.0 -pt 7 x y
+
+  create t 0 1.5 0.01
+  set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  set y = (1000.0/$dS)*sin(t*$TP)*dsin($ANGLE) + $vY*t
+  plot -c grey60 -x 0 x y
+
+  # label -x "offset east (milliarcsec)"
+  # label -y "offset north (milliarcsec)"
+end
+
+macro PMsim
+  if ($0 != 6)
+    echo "USAGE: PMsim Do vX vY dP dT"
+    echo "Do : distance in parsec"
+    echo "vX, vY : velocity (milliarcsec/year)"
+    echo "dP : astrometric scatter (arcsec)"
+    echo "dT : ecliptic lattitude (degrees)"
+    break
+  end
+
+  $TP = 2*3.1416
+
+  $dS = $1
+  $vX = $2
+  $vY = $3
+  $dP = $4
+  $dT = $5
+
+  mkbase
+
+  set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+  set y = (1000.0/$dS)*sin(t*$TP)*dsin($dT) + $vY*t
+
+  gaussdev dx x[] 0.0 $dP
+  gaussdev dy y[] 0.0 $dP 
+
+  set X = x + dx
+  set Y = y + dy
+
+  if ($PLOT)
+    lim -200 200 -200 200; clear; box;
+    style -c black
+    plot -x 2 -pt 2 -sz 2.0 X Y
+    plot -x 0 X Y
+  end
+
+  PMfit X Y t $dT
+
+  if ($PLOT) 
+    label -x "offset east (milliarcsec)"
+    label -y "offset north (milliarcsec)"
+    section a 0 0 1 1
+    lim 0 1 0 1
+    sprintf line "input: D: %5.1f (pc), V: %5.1f, %5.1f (mas/yr), dS: %4.1f" $1 $2 $3 $4
+    textline -fn courier 14 0.15 0.95 "$line"
+    sprintf line "fit:    D: %5.1f (pc), V: %5.1f, %5.1f (mas/yr)" $dS $vX $vY
+    textline -fn courier 14 0.15 0.90 "$line"
+    section default
+  end
+end
+
+macro PMfit
+  if ($0 != 5)
+    echo "USAGE: PMfit X Y t (theta)"
+    echo "X,Y are offsets in milliarcsec"
+    echo "t is time in years"
+    break
+  end
+
+  $TP = 2*3.1416
+  set x = $1
+  set y = $2
+  set t = $3
+  set cs = cos(t*$TP)
+  set snx = sin(t*$TP)*dsin($4)
+
+  mcreate A 3 3
+  create B 0 3
+
+  # define A values:
+  set tmp = cs^2 + snx^2
+  vstat -q tmp
+  zap A 0 0 1 1 -v $TOTAL
+
+  set tmp = t*cs
+  vstat -q tmp
+  zap A 1 0 1 1 -v $TOTAL
+  zap A 0 1 1 1 -v $TOTAL
+
+  set tmp = t*snx
+  vstat -q tmp
+  zap A 2 0 1 1 -v $TOTAL
+  zap A 0 2 1 1 -v $TOTAL
+
+  set tmp = t^2
+  vstat -q tmp
+  zap A 1 1 1 1 -v $TOTAL
+  zap A 2 2 1 1 -v $TOTAL
+
+  zap A 2 1 1 1 -v 0
+  zap A 1 2 1 1 -v 0
+
+  # define B values:
+  set tmp = x*cs + y*snx
+  vstat -q tmp
+  B[0] = $TOTAL
+
+  set tmp = x*t
+  vstat -q tmp
+  B[1] = $TOTAL
+
+  set tmp = y*t
+  vstat -q tmp
+  B[2] = $TOTAL
+
+  gaussj A B
+
+  $dS = 1000/B[0]
+  $vX = B[1]
+  $vY = B[2]
+
+  if ($PLOT)
+    echo "Do: $dS"
+    echo "vX: $vX"
+    echo "vY: $vY"
+
+    set x = (1000.0/$dS)*cos(t*$TP) + $vX*t
+    set y = (1000.0/$dS)*sin(t*$TP)*dsin($4) + $vY*t
+    plot -c red -x 2 -sz 2.0 -pt 7 x y
+  end
+end
+
+macro PMstats
+  
+  if ($0 != 7)
+    echo "USAGE: PMsim Do vX vY dP dT (Niter)"
+    echo "Do : distance in parsec"
+    echo "vX, vY : velocity (milliarcsec/year)"
+    echo "dP : astrometric scatter (arcsec)"
+    echo "dT : ecliptic lattitude (degrees)"
+    break
+  end
+
+  delete -q vx vy ds
+  $PLOT = 0
+
+  for i 0 $6
+    PMsim $1 $2 $3 $4 $5
+    concat $dS ds
+    concat $vX vx
+    concat $vY vy
+  end
+
+  vstat ds
+  vstat vx
+  vstat vy
+
+  set gm = 1000/ds
+  vstat gm
+
+  clear -s -n 1
+  label -fn courier 14
+  # section a 0.0 0.0 0.5 1.0
+  lim {$2-30} {$2+30} {$3-30} {$3+30}; box
+  plot -x 2 -pt 2 -sz 0.5 vx vy
+  label -x "p.m. (east, mas/yr)"
+  label -y "p.m. (east, mas/yr)"
+
+  clear -s -n 2
+  # section b 0.5 0.0 0.5 1.0
+  histogram gm Ng {1000/$1 - 30.0} {1000/$1 + 30.0} 1.0
+  create dg {1000/$1 - 30.0} {1000/$1 + 30.0} 1.0
+  lim dg Ng; box
+  plot -x 1 dg Ng
+
+  label -x "parallax (mas)"
+  label -y "# of tests"
+
+end
+
+macro PMtrend
+
+  PMstats 10 5.0 5.0 10 90 100
+  echo 10
+  PMstats 10 5.0 5.0 {750/2.38/25} 90 100
+  echo {750/2.38/25} 
+  PMstats 10 5.0 5.0 {750/2.38/10} 90 100
+  echo {750/2.38/10} 
+  PMstats 10 5.0 5.0 {750/2.38/5} 90 100
+  echo {750/2.38/5} 
+end
+
+list dist
+  5
+  10
+  15
+  20
+  30
+  40
+  50
+  60
+  70
+  80
+end
+
+macro ParDetect
+
+  local i
+  delete -q xp yp dyp dym
+  
+  $ANGLE = 45
+
+  for i 0 $dist:n
+    PMstats $dist:$i 5.0 5.0 10 $ANGLE 100
+    concat $dist:$i xp
+    concat {1000/$MEAN} yp
+    concat {1000/($MEAN - $SIGMA) - 1000/$MEAN} dyp
+    concat {1000/$MEAN - 1000/($MEAN + $SIGMA)} dym
+  end
+
+  section a 0.0 0.0 0.5 1.0
+
+  lim xp -1.0 101; clear; box; 
+  plot -c black -x 2 xp yp -dy dym +dy dyp
+  
+  delete -q xp yp 
+  concat 0 xp
+  concat 100 xp
+  set yp = xp
+  plot -x 0 -c blue xp yp
+
+  set yp = zero(xp) + 62.5
+  plot -x 0 -c red -lt 1 xp yp
+  style -lt 0  
+  label -x "input dist (pc)" -y "output dist" -fn courier 14
+
+  $PLOT = 0
+  section b 0.5 0.0 0.5 1.0
+  lim -120 120 -120 120; box -labels 1001;
+
+  for i 0 5
+   sample
+  end
+
+  label -x "offset east (milliarcsec)"
+  label +y "offset north"
+  ps -name ParallaxDist.ps
+end
+
+macro fields
+
+  $Dx = 0.25*4800*8/3600
+  $Fo = 1.09
+
+  $dD = 2.65/1.09
+
+  $Nf = 0
+  for i {-30+$dD/2} 90 $dD
+    $Np = int(dcos($i)*1.09*360/2.65) + 1
+    $Nf = $Nf + $Np
+    fprintf "%5.1f %5.1f %4d %4d" $i {dcos($i)*1.09*360/2.65} $Np $Nf 
+  end
+end
+
+macro SNsim
+  $w  = 0.76
+  $m1 = 24.8
+  $mu = 20.4
+
+  lim 13.5 26.5 0.0 4.0; clear; box
+
+  $t = 1
+  SN
+
+  $t = 5
+  SN
+
+  $t = 30
+  SN
+
+  $t = 100
+  SN
+
+  set sn = 5 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+
+  set sn = 25 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+
+  set sn = 100 + zero (m)
+  set lsn = log(sn)
+  plot -lt 2 m lsn
+  style -lt 0
+end
+
+macro SN
+  create m 14 26 0.1
+  set sn = ten (-0.2*(2*m - $mu - $m1)) * sqrt($t/$w^2/3.1416)
+  set lsn = log(sn)
+  plot m lsn
+end
+
+# r: 30s @ 5sig = 22.65  (0.6")
+# i: 30s @ 5sig = 22.42  (0.6")
+# i: 30s @ 5sig = 22.27  (for 0.3 mag lower zp)
+
+macro mkbase
+  delete -q t
+  concat 0.0 t
+  concat {10.0/365.0} t
+  concat {20.0/365.0} t
+  concat { 6.0/52.0} t
+
+  concat { 3.0/12.0} t
+  concat { 6.0/12.0} t
+  concat {12.0/12.0} t
+  concat {18.0/12.0} t
+end
+
+
+macro parfactor
+
+  $RA = $1
+  $Dec = $2
+  $e = 23 + 27/60
+  
+  create s 0 360
+  set pR = dcos($e)*d
+
+  set pR =  +(dcos($e)*dsin(s)*dcos($RA) - dcos(s)*dsin($RA))
+  set pD =  -(dcos($e)*dsin(s)*dsin($RA) + dcos(s)*dcos($RA))*dsin($Dec) + dsin($e)*dsin(s)*dcos($Dec)
+
+  lim -1.1 1.1 -1.1 1.1; clear; box; plot pR pD
+end
Index: /tags/ipp-2-5-1/Ohana/src/relastro/include/relastro.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/include/relastro.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/include/relastro.h	(revision 17422)
@@ -0,0 +1,292 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include <signal.h>
+
+typedef enum {
+  MODE_SIMPLE,
+  MODE_CHIP,
+  MODE_MOSAIC,
+} CoordMode;
+
+typedef enum {ERROR_MODE_RA, ERROR_MODE_DEC, ERROR_MODE_POS} ErrorMode;
+
+typedef enum {FIT_NONE, FIT_AVERAGE, FIT_PM_ONLY, FIT_PAR_ONLY, FIT_PM_AND_PAR} FitMode;
+
+typedef enum {TARGET_NONE, TARGET_OBJECTS, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS} FitTarget;
+
+typedef struct {
+  double R, D;  /* Sky Coords    - degrees */
+  double P, Q;  /* Tangent Plane - pixels  */
+  double L, M;  /* Focal Plane   - pixels  */
+  double X, Y;  /* Chip Coords   - pixels  */
+  double Mag;
+  double dMag;
+  double dPos;
+  int mask;
+} StarData;
+
+// structure to hold coordinate fitting terms
+typedef struct {
+    int Npts;
+    int Nterms;
+    int Norder;
+    int Nsums;
+    int Nelems;
+    double **sum;
+    double **xsum;
+    double **ysum;
+    double **xfit;
+    double **yfit;
+} CoordFit;
+
+typedef struct {
+  double Ro, dRo;
+  double Do, dDo;
+  
+  double uR, duR;
+  double uD, duD;
+
+  double p, dp;
+
+  double chisq;
+  int Nfit;
+} PMFit;
+
+typedef struct {
+  unsigned int start;
+  unsigned int stop;
+  float Mcal;
+  float dMcal;
+  short Xm;
+  float secz;
+  char code;
+  Coords coords;
+} Mosaic; 
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+double SIGMA_LIM;
+double MIN_ERROR;
+
+int    VERBOSE;
+
+int    RESET;
+int    UPDATE;
+int    PLOTSTUFF;
+int    SAVEPLOT;
+int    SHOW_PARAMS;
+char   STATMODE[32];
+int    POS_TOOFEW;
+int    PM_TOOFEW;
+double PM_DT_MIN;
+int    PLOTDELAY;
+int    CHIPORDER;
+
+char          *PHOTCODE_KEEP_LIST, *PHOTCODE_SKIP_LIST;
+int           NphotcodesKeep,      NphotcodesSkip;
+PhotCode     **photcodesKeep,     **photcodesSkip;
+
+int AreaSelect;
+double AreaXmin, AreaXmax, AreaYmin, AreaYmax;
+
+int ImagSelect, ImagMin, ImagMax;
+
+double  PlotMmin, PlotMmax, PlotdMmin, PlotdMmax;
+
+int DophotSelect, DophotValue;
+int PhotFlagSelect, PhotFlagPoor, PhotFlagBad;
+
+int TimeSelect;
+time_t TSTART, TSTOP;
+
+FitMode FIT_MODE;
+
+FitTarget FIT_TARGET;
+
+SkyRegion UserPatch;
+
+int DoUpdateObjects;
+int DoUpdateSimple;
+int DoUpdateChips;
+int DoUpdateMosaics;
+
+/*** relphot prototypes ***/
+void          ConfigInit          PROTO((int *argc, char **argv));
+void          GetConfig           PROTO((char *config, char *field, char *format, int N, void *ptr));
+char         *GetPhotnamebyCode   PROTO((PhotCodeData *photcodes, int code));
+void          InterpolateGrid     PROTO((float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords));
+int          *SelectRefMosaic     PROTO((Mosaic **refmosaic, int *Nimage));
+int           args                PROTO((int argc, char **argv));
+int           bcatalog            PROTO((Catalog *subcatalog, Catalog *catalog));
+void          clean_images        PROTO(());
+void          clean_measures      PROTO((Catalog *catalog, int Ncatalog, int final));
+void          clean_mosaics       PROTO(());
+void          clean_stars         PROTO((Catalog *catalog, int Ncatalog));
+int           corner_check        PROTO((double *x1, double *y1, double *x2, double *y2));
+void          dumpGrid            PROTO(());
+void          dump_grid           PROTO(()); 
+int           edge_check          PROTO((double *x1, double *y1, double *x2, double *y2));
+void          findImages          PROTO((Catalog *catalog, int Ncatalog));
+int           findMosaics         PROTO((Catalog *catalog, int Ncatalog));
+Image        *find_images         PROTO((FITS_DB *db, GSCRegion *region, int Nregion, int *Nimage, int **LineNum));
+void set_db (FITS_DB *in);
+int Shutdown (char *format, ...);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+
+GSCRegion    *find_regions        PROTO((Image *image, int Nimage, int *Nregions, GSCRegion *fullregion));
+void          freeGridBins        PROTO((int Ncatalog));
+void          freeImageBins       PROTO((int Ncatalog));
+void          freeMosaicBins      PROTO((int Ncatalog));
+void          free_catalogs       PROTO((Catalog *catalog, int Ncatalog));
+int           gcatalog            PROTO((Catalog *catalog, int FINAL));
+Coords       *getCoords           PROTO((int meas, int cat));
+float         getMcal             PROTO((int meas, int cat));
+float         getMgrid            PROTO((int meas, int cat));
+float         getMmos             PROTO((int meas, int cat));
+float         getMrel             PROTO((Catalog *catalog, int meas, int cat));
+GSCRegion    *get_regions         PROTO((double minRa, double maxRa, double minDec, double maxDec, int *Nregions));
+void          getfullregion       PROTO((Image *image, int Nimage, GSCRegion *fullregion));
+Image        *getimage            PROTO((int N));
+Image        *getimages           PROTO((int *N));
+void          global_stats        PROTO((Catalog *catalog, int Ncatalog));
+void          initGrid            PROTO((int dX, int dY));
+void          initGridBins        PROTO((Catalog *catalog, int Ncatalog));
+void          initImageBins       PROTO((Catalog *catalog, int Ncatalog));
+void          initImages          PROTO((Image *input, int N));
+void          initMosaicBins      PROTO((Catalog *catalog, int Ncatalog));
+void          initMosaicGrid      PROTO((Image *image, int Nimage));
+void          initMosaics         PROTO((Image *image, int Nimage));
+void          initMrel            PROTO((Catalog *catalog, int Ncatalog));
+void          initialize          PROTO((int argc, char **argv));
+void          initstats           PROTO((char *mode));
+int           liststats           PROTO((double *value, double *dvalue, int N, StatType *stats));
+Catalog      *load_catalogs       PROTO((SkyList *skylist, int *Ncatalog, int subselect));
+SkyList      *load_images         PROTO((FITS_DB *db, SkyRegion *region));
+Image        *select_images       PROTO((SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage));
+
+void check_permissions (char *basefile);
+void lock_image_db (FITS_DB *db, char *filename);
+void unlock_image_db (FITS_DB *db);
+void create_image_db (FITS_DB *db);
+void save_catalogs (Catalog *catalog, int Ncatalog);
+
+int           main                PROTO((int argc, char **argv));
+void          mark_images         PROTO((Image *image, int Nimage, Image *timage, int Ntimage));
+void          matchImage          PROTO((Catalog *catalog, int meas, int cat));
+void          matchMosaics        PROTO((Catalog *catalog, int meas, int cat));
+GSCRegion    *name_region         PROTO((char *name, int *Nregions));
+double        opening_angle       PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+void          plot_chisq          PROTO((Catalog *catalog, int Ncatalog));
+void          plot_defaults       PROTO((Graphdata *graphdata));
+void          plot_grid           PROTO((Catalog *catalog));
+void          plot_images         PROTO(());
+void          plot_list           PROTO((Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file));
+void          plot_mosaic_fields  PROTO((Catalog *catalog));
+void          plot_mosaics        PROTO(());
+void          plot_scatter        PROTO((Catalog *catalog, int Ncatalog));
+void          plot_star_coords    PROTO((Catalog *catalog, int Ncatalog));
+void          plot_stars          PROTO((Catalog *catalog, int Ncatalog));
+void          reload_catalogs     PROTO((SkyList *skylist));
+int           setExclusions       PROTO((Catalog *catalog, int Ncatalog));
+void          setMcal             PROTO((Catalog *catalog, int Poor));
+void          setMcalFinal        PROTO(());
+int           setMcalOutput       PROTO((Catalog *catalog, int Ncatalog));
+void          setMgrid            PROTO((Catalog *catalog));
+int           setMmos             PROTO((Catalog *catalog, int Poor));
+int           setMrel             PROTO((Catalog *catalog, int Ncatalog));
+void          setMrelFinal        PROTO((Catalog *catalog));
+int           setMrelOutput       PROTO((Catalog *catalog, int Ncatalog, int mark));
+void          set_ZP              PROTO((double ZERO));
+int           setrefcode          PROTO((Image *image, int Nimage)); 
+void          skip_measurements   PROTO((Catalog *catalog, int pass));
+void          sortA               PROTO((double *X, int N));
+void          sortB               PROTO((double *X, double *Y, int N));
+void          sortC               PROTO((double *X, double *Y, double *F1, double *F2, int N));
+void          sortD               PROTO((double *X, double *Y, double *Z, int N));
+StatType      statsImageM         PROTO((Catalog *catalog));
+StatType      statsImageN         PROTO((Catalog *catalog));
+StatType      statsImageX         PROTO((Catalog *catalog));
+StatType      statsImagedM        PROTO((Catalog *catalog));
+StatType      statsMosaicM        PROTO((Catalog *catalog));
+StatType      statsMosaicN        PROTO((Catalog *catalog));
+StatType      statsMosaicX        PROTO((Catalog *catalog));
+StatType      statsMosaicdM       PROTO((Catalog *catalog));
+StatType      statsStarN          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarS          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarX          PROTO((Catalog *catalog, int Ncatalog));
+void          wcatalog            PROTO((Catalog *catalog));
+void          wimages             PROTO(());
+void          write_coords        PROTO((Header *header, Coords *coords));
+
+double **array_init (int Nx, int Ny);
+void array_free (double **array, int Nx);
+CoordFit *fit_init (int order);
+void fit_free (CoordFit *fit);
+void fit_add (CoordFit *fit, double x1, double y1, double x2, double y2, double wt);
+void fit_eval (CoordFit *fit);
+void fit_apply (CoordFit *fit, double *x2, double *y2, double x1, double y1);
+double **poly2d_dx (double **poly, int Nx, int Ny);
+double **poly2d_dy (double **poly, int Nx, int Ny);
+double **poly2d_copy (double **poly, int Nx, int Ny);
+double poly2d_eval (double **poly, int Nx, int Ny, double x, double y);
+CoordFit *fit_apply_coords (CoordFit *fit, Coords *coords);
+int CoordsGetCenter (CoordFit *fit, double tol, double *xo, double *yo);
+CoordFit *CoordsSetCenter (CoordFit *input, double Xo, double Yo);
+void FitChip (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void FitMosaic (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void FitSimple (StarData *raw, StarData *ref, int Nmatch, Coords *coords);
+void initObjectData (Catalog *catalog, int Ncatalog);
+int UpdateObjects (Catalog *catalog, int Ncatalog);
+int UpdateSimple (Catalog *catalog, int Ncatalog);
+int UpdateChips (Catalog *catalog, int Ncatalog);
+int UpdateMosaic (Catalog *catalog, int Ncatalog);
+int UpdateMeasures (Catalog *catalog, int Ncatalog);
+void fixImageRaw (Catalog *catalog, int Ncatalog, int im);
+
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon);
+int ParFactor (double *pR, double *pD, double R, double D, time_t T);
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts);
+int FitPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *pR, double *pD, int Npts);
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts);
+
+Mosaic *getMosaicForImage (int N);
+
+StarData *getImageRef (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode);
+StarData *getImageRaw (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode);
+
+Mosaic *getmosaics (int *N);
+void initMosaics (Image *image, int Nimage);
+StarData *getMosaicRaw (Catalog *catalog, int Ncatalog, int mos, int *Nstars);
+StarData *getMosaicRef (Catalog *catalog, int Ncatalog, int mos, int *Nstars);
+Mosaic *getMosaicForImage (int im);
+
+double getMeanR (Measure *measure, Average *average, SecFilt *secfilt);
+double getMeanD (Measure *measure, Average *average, SecFilt *secfilt);
+int setMeanR (double ra_fit, Measure *measure, Average *average, SecFilt *secfilt);
+int setMeanD (double dec_fit, Measure *measure, Average *average, SecFilt *secfilt);
+
+float GetAstromError (Measure *measure, int mode);
+int relastro_objects ();
+int UpdateObjectOffsets (SkyList *skylist);
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/ConfigInit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/ConfigInit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/ConfigInit.c	(revision 17422)
@@ -0,0 +1,82 @@
+# include "relastro.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  char  *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+  struct stat filestat;
+  int status;
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "RELASTRO_SIGMA_LIM",     "%lf", 0, &SIGMA_LIM);
+
+  // XXX these are used in relphot to identify poor stars / images -- define
+  // an equivalent concept here?
+  // GetConfig (config, "STAR_SCATTER",           "%lf", 0, &STAR_SCATTER);
+  // GetConfig (config, "IMAGE_SCATTER",          "%lf", 0, &IMAGE_SCATTER);
+  // GetConfig (config, "IMAGE_OFFSET",           "%lf", 0, &IMAGE_OFFSET);
+  // GetConfig (config, "STAR_CHISQ",             "%lf", 0, &STAR_CHISQ);
+
+  GetConfig (config, "PM_DT_MIN",              "%lf", 0, &PM_DT_MIN);
+  GetConfig (config, "PM_TOOFEW",              "%d",  0, &PM_TOOFEW);
+  GetConfig (config, "POS_TOOFEW",             "%d",  0, &POS_TOOFEW);
+
+  GetConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig(config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig(config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig(config, "PHOTCODE_FILE",          "%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  // check for existence of CATDIR
+  status = stat (CATDIR, &filestat);
+  if (status == -1) {
+    fprintf (stderr, "directory %s does not exist, giving up\n", CATDIR);
+    exit (1);
+  } 
+
+  /* update master photcode table if not defined */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (25.0);
+
+  free (config);
+  free (file);
+
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitChip.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitChip.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitChip.c	(revision 17422)
@@ -0,0 +1,142 @@
+# include "relastro.h"
+
+// XXX make these user parameters
+# define FIT_CHIP_MAX_ERROR 0.05
+# define FIT_CHIP_NITER     3
+# define FIT_CHIP_NSIGMA    3.0
+
+// XXX we should test if the fit is sufficiently constrained across the chip, or if the
+// new positions deviate too much from the old positions
+
+// XXX add visualization tools: per chip residual plots
+
+// XXX save the fit[0].Npts value in the image table?
+
+// XXX save measurements of the fit quality (scatter, chisq) in the image table
+
+void FitChip (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i, Nscatter, Niter, skip;
+  CoordFit *fit;
+  double dL, dM, dR, dRmax, *values;
+
+  ALLOCATE (values, double, Nmatch);
+
+  for (Niter = 0; Niter < FIT_CHIP_NITER; Niter ++) {
+
+    // measure the scatter distribution (use only the bright end detections)
+    for (i = Nscatter = 0; i < Nmatch; i++) {
+      if (raw[i].mask) continue;
+      if (raw[i].dMag > FIT_CHIP_MAX_ERROR) continue;
+
+      dL = raw[i].L - ref[i].L;
+      dM = raw[i].M - ref[i].M;
+      dR = hypot (dL, dM);
+    
+      values[Nscatter] = dR;
+      Nscatter++;
+    }
+
+    // for a 2D Gaussian, 40% of the points are within 1 sigma; dRmax is ~ 3 sigma
+    dsort (values, Nscatter);
+    dRmax = FIT_CHIP_NSIGMA*values[(int)(0.40*Nscatter)];
+
+    // fit the requested order polynomial
+    if (CHIPORDER > 0) {
+      coords[0].Npolyterms = CHIPORDER;
+    }
+    fit = fit_init (coords[0].Npolyterms);
+
+    // generate the fit matches
+    for (i = 0; i < Nmatch; i++) {
+      if (raw[i].mask) continue;
+
+      // only keep objects within dRmax
+      dL = raw[i].L - ref[i].L;
+      dM = raw[i].M - ref[i].M;
+      dR = hypot (dL, dM);
+      if (dR > dRmax) continue;
+    
+      fit_add (fit, raw[i].X, raw[i].Y, ref[i].L, ref[i].M, raw[i].dPos);
+    }
+
+    // check if the fit has enough data points for the polynomial order
+    skip = FALSE;
+    switch (coords[0].Npolyterms) {
+      case 0:
+      case 1:
+	skip = (fit[0].Npts < 8);
+	break;
+      case 2:
+	skip = (fit[0].Npts < 11);
+	break;
+      case 3:
+	skip = (fit[0].Npts < 15);
+	break;
+      default:
+	fprintf (stderr, "invalid chip order %d\n", coords[0].Npolyterms);
+	abort ();
+    }
+    if (skip) {
+      fprintf (stderr, "insufficient measurements (%d) for requested order (%d)\n", fit[0].Npts, coords[0].Npolyterms);
+      fit_free (fit);
+      free (values);
+      return;
+    }
+
+    fprintf (stderr, "scatter limit: %f based on %d detections; using %d of %d for fit\n", dRmax, Nscatter, fit[0].Npts, Nmatch);
+
+    // measure the fit, update the coords & object coordinates
+    fit_eval (fit);
+    fit_apply_coords (fit, coords);
+    fit_free (fit);
+
+    for (i = 0; i < Nmatch; i++) {
+      XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, coords);
+    }
+  }
+
+  free (values);
+  return;
+}
+
+/* in the mosaic case, we have four coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   L,M : the focal plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> L,M (polynomial transformation : DIS)
+   L,M -> X,Y (polynomial transformation : WRP)
+
+Some details about this function:
+
+- the initial value of the clipping radius is set based on the distribution of the dR
+  values for the bright sources.
+
+- NITER clipping passes are performed
+
+- the per-star astrometric errors are included in the fit.  The photcode table controls
+  whether only the reported positional errors are used, or if the magnitudes errors are
+  scaled to determine the error, and if there is a systematic floor for all sources.
+
+- input detections are pre-filtered on the basis of the photFlags, etc, in bcatalog
+
+- outlying detections are clipped in the iterative passes, but the clipped detections are
+  not recorded
+
+*/
+
+/* example using fit_apply() :
+
+  f = fopen ("test3.dat", "w");
+
+  // apply new coords to raw (X,Y -> L,M)
+  for (i = 0; i < Nmatch; i++) {
+    fprintf (f, "%f %f  %f %f  ", raw[i].X, raw[i].Y, raw[i].L, raw[i].M);
+    fit_apply (newfit, &L1, &M1, raw[i].X - coords[0].crpix1, raw[i].Y - coords[0].crpix2);
+    fprintf (f, "%f %f\n", L1, M1);
+  }
+  fclose (f);
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitMosaic.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitMosaic.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitMosaic.c	(revision 17422)
@@ -0,0 +1,48 @@
+# include "relastro.h"
+
+void FitMosaic (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i;
+  CoordFit *fit;
+  double dP, dQ, dR;
+
+  fit = fit_init (coords[0].Npolyterms);
+  for (i = 0; i < Nmatch; i++) {
+    if (raw[i].mask) continue;
+
+    // require radius of XXX arcsec
+    dP = raw[i].P - ref[i].P;
+    dQ = raw[i].Q - ref[i].Q;
+    dR = 3600.0 * hypot (dP, dQ);
+
+    // XXX the value needs to be set in a more intelligent way
+    if (dR > 0.15) continue;
+    
+    fit_add (fit, raw[i].L, raw[i].M, ref[i].P, ref[i].Q, 1.0);
+  }
+  if (fit[0].Npts == 0) {
+    fit_free (fit);
+    return;
+  }
+  fit_eval (fit);
+  fit_apply_coords (fit, coords);
+  fit_free (fit);
+
+  // apply new coords to raw (X,Y -> L,M)
+  for (i = 0; i < Nmatch; i++) {
+    XY_to_LM (&raw[i].P, &raw[i].Q, raw[i].L, raw[i].M, coords);
+  }
+}
+
+/* in the mosaic case, we have four coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   L,M : the focal plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> L,M (polynomial transformation : DIS)
+   L,M -> X,Y (polynomial transformation : WRP)
+*/
+
+/* XXX I'm not using the errors at all : this could at least be done with the dMag values */
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPM.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPM.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPM.c	(revision 17422)
@@ -0,0 +1,87 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
+  double chisq, Xf, Yf;
+
+  /* do I need to do this as 2 2x2 matrix equations? */
+  A = array_init (4, 4);
+  B = array_init (4, 1);
+
+  Wx = Wy = Tx = Ty = Tx2 = Ty2 = Xs = Ys = XT = YT = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    Tx += T[i]*wx;
+    Ty += T[i]*wy;
+    
+    Tx2 += SQ(T[i])*wx;
+    Ty2 += SQ(T[i])*wy;
+    
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+
+    XT += X[i]*T[i]*wx;
+    YT += Y[i]*T[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][1] = Tx;
+
+  A[1][0] = Tx;
+  A[1][1] = Tx2;
+
+  A[2][2] = Wy;
+  A[2][3] = Ty;
+
+  A[3][2] = Ty;
+  A[3][3] = Ty2;
+
+  B[0][0] = Xs;
+  B[1][0] = XT;
+  B[2][0] = Ys;
+  B[3][0] = YT;
+
+  dgaussjordan (A, B, 4, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].uR = B[1][0];
+  fit[0].Do = B[2][0];
+  fit[0].uD = B[3][0];
+  fit[0].p  = 0.0;
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].duR = sqrt(A[1][1]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].duD = sqrt(A[3][3]);
+  fit[0].dp  = 0.0;
+  
+  array_free (A, 4);
+  array_free (B, 4);
+
+  // add up the chi square for the fit
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+      Xf = fit[0].Ro + fit[0].uR*T[i];
+      Yf = fit[0].Do + fit[0].uD*T[i];
+      chisq += SQ(X[i] - Xf) / SQ(dX[i]);
+      chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+  }
+  fit[0].Nfit = Npts;
+
+  // the reduced chisq is divided by (Ndof = 2*Npts - 4)
+  fit[0].chisq = chisq / (2.0*Npts - 4.0);
+  return (TRUE);
+}
+
+// XXX this function should (optionally?) iterate and clip outlier detections
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPMandPar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPMandPar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPMandPar.c	(revision 17422)
@@ -0,0 +1,98 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
+  double PR, PD, PRT, PDT, PRX, PDY, PR2, PD2;
+
+  A = array_init (5, 5);
+  B = array_init (5, 1);
+
+  PR = PD = PRT = PDT = PRX = PDY = PR2 = PD2 = 0.0;
+  Wx = Wy = Tx = Ty = Tx2 = Ty2 = Xs = Ys = XT = YT = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    Tx += T[i]*wx;
+    Ty += T[i]*wy;
+    
+    Tx2 += SQ(T[i])*wx;
+    Ty2 += SQ(T[i])*wy;
+    
+    PR += pR[i]*wx;
+    PD += pD[i]*wy;
+    
+    PRT += pR[i]*T[i]*wx;
+    PDT += pD[i]*T[i]*wy;
+    
+    PRX += pR[i]*X[i]*wx;
+    PDY += pD[i]*Y[i]*wy;
+    
+    PR2 += SQ(pR[i])*wx;
+    PD2 += SQ(pD[i])*wy;
+
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+
+    XT += X[i]*T[i]*wx;
+    YT += Y[i]*T[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][1] = Tx;
+  A[0][4] = PR;
+
+  A[1][0] = Tx;
+  A[1][1] = Tx2;
+  A[1][4] = PRT;
+
+  A[2][2] = Wy;
+  A[2][3] = Ty;
+  A[2][4] = PD;
+
+  A[3][2] = Ty;
+  A[3][3] = Ty2;
+  A[3][4] = PDT;
+
+  A[4][0] = PR;
+  A[4][1] = PRT;
+  A[4][2] = PD;
+  A[4][3] = PDT;
+  A[4][4] = PR2 + PD2;
+
+  B[0][0] = Xs;
+  B[1][0] = XT;
+  B[2][0] = Ys;
+  B[3][0] = YT;
+  B[4][0] = PRX + PDY;
+
+  dgaussjordan (A, B, 5, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].uR = B[1][0];
+  fit[0].Do = B[2][0];
+  fit[0].uD = B[3][0];
+  fit[0].p  = B[4][0];
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].duR = sqrt(A[1][1]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].duD = sqrt(A[3][3]);
+  fit[0].dp  = sqrt(A[4][4]);
+  
+  array_free (A, 5);
+  array_free (B, 5);
+
+  /* get the chisq from the matrix values */
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPar.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPar.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitPar.c	(revision 17422)
@@ -0,0 +1,74 @@
+# include "relastro.h"
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *pR, double *pD, int Npts) {
+
+  int i;
+
+  double **A, **B;
+  double wx, wy, Wx, Wy, Xs, Ys;
+  double PR, PD, PRX, PDY, PR2, PD2;
+
+  A = array_init (3, 3);
+  B = array_init (3, 1);
+
+  Wx = Wy = Xs = Ys = 0.0;
+  PR = PD = PRX = PDY = PR2 = PD2 = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    PR += pR[i]*wx;
+    PD += pD[i]*wy;
+    
+    PRX += pR[i]*X[i]*wx;
+    PDY += pD[i]*Y[i]*wy;
+    
+    PR2 += SQ(pR[i])*wx;
+    PD2 += SQ(pD[i])*wy;
+
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][2] = PR;
+
+  A[1][1] = Wy;
+  A[1][2] = PD;
+
+  A[2][0] = PR;
+  A[2][1] = PD;
+  A[2][2] = PR2 + PD2;
+
+  B[0][0] = Xs;
+  B[1][0] = Ys;
+  B[2][0] = PRX + PDY;
+
+  dgaussjordan (A, B, 3, 1);
+
+  fit[0].Ro = B[0][0];
+  fit[0].Do = B[1][0];
+  fit[0].p  = B[2][0];
+
+  fit[0].uR = 0.0;
+  fit[0].uD = 0.0;
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].dp  = sqrt(A[4][4]);
+  
+  fit[0].duR = 0.0;
+  fit[0].duD = 0.0;
+
+  array_free (A, 3);
+  array_free (B, 3);
+
+  /* get the chisq from the matrix values */
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/FitSimple.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/FitSimple.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/FitSimple.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "relastro.h"
+
+void FitSimple (StarData *raw, StarData *ref, int Nmatch, Coords *coords) {
+
+  int i;
+  CoordFit *fit;
+  double dP, dQ, dR;
+
+  fit = fit_init (coords[0].Npolyterms);
+  for (i = 0; i < Nmatch; i++) {
+    if (raw[i].mask) continue;
+
+    // require radius of XXX arcsec
+    dP = raw[i].P - ref[i].P;
+    dQ = raw[i].Q - ref[i].Q;
+    dR = 3600.0 * hypot (dP, dQ);
+
+    // XXX the value needs to be set in a more intelligent way
+    if (dR > 0.15) continue;
+    
+    fit_add (fit, raw[i].X, raw[i].Y, ref[i].P, ref[i].Q, 1.0);
+  }
+  if (fit[0].Npts == 0) {
+    fit_free (fit);
+    return;
+  }
+  fit_eval (fit);
+  fit_apply_coords (fit, coords);
+  fit_free (fit);
+
+  // apply new coords to raw (X,Y -> P,Q)
+  for (i = 0; i < Nmatch; i++) {
+    XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, coords);
+    raw[i].P = raw[i].L;
+    raw[i].Q = raw[i].M;
+  }
+}
+
+/* in the simple case, we only have three coord systems of interest:
+   R,D : the sky
+   P,Q : the tangent plane
+   X,Y : the chip
+
+   R,D -> P,Q (projection)
+   P,Q -> X,Y (polynomial transformation)
+
+   L,M is maintained, but is identical to P,Q
+*/
+
+/* XXX I'm not using the errors at all : this could at least be done with the dMag values */
+
+/* XXX See notes in FitChips.c */
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/GetAstromError.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/GetAstromError.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/GetAstromError.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "relastro.h"
+
+float GetAstromError (Measure *measure, int mode) {
+
+  PhotCode *code;
+  float dPobs, dPsys, dPtotal, dM, AS, MS;
+
+  switch (mode) {
+    case ERROR_MODE_RA:
+      dPobs = measure[0].dXccd;  // need to redefine this as RAerr
+      break;
+    case ERROR_MODE_DEC:
+      dPobs = measure[0].dYccd;  // need to redefine this as RAerr
+      break;
+    case ERROR_MODE_POS:
+      dPobs = hypot (measure[0].dXccd, measure[0].dYccd);  // need to redefine this as RAerr
+      break;
+    default:
+      abort();
+  }
+
+  /* the astrometric errors are not being carried yet (but should be!) */
+  /* we use the photometric mag error as a weighting term */
+
+  code 	= GetPhotcodebyCode (measure[0].photcode);
+  AS   	= code[0].astromErrScale;
+  MS   	= code[0].astromErrMagScale;
+  dPsys = code[0].astromErrSys;
+  dM    = measure[0].dM;
+  
+  dPtotal = sqrt(SQ(dPsys) + AS*SQ(dPobs) + MS*SQ(dM));
+  dPtotal = MAX (dPtotal, MIN_ERROR);
+
+  return (dPtotal);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/ImageOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/ImageOps.c	(revision 17422)
@@ -0,0 +1,382 @@
+# include "relastro.h"
+
+static unsigned int *start;
+static unsigned int *stop;
+static int         **bin;
+
+static int         **clist;
+static int         **mlist;
+static int          *Nlist;
+static int          *NLIST;
+
+static Image        *image;
+static int          Nimage;
+
+Image *getimages (int *N) {
+  *N = Nimage;
+  return (image);
+}
+
+Image *getimage (int N) {
+  return (&image[N]);
+}
+
+void initImages (Image *input, int N) {
+
+  int i;
+
+  image = input;
+  Nimage = N;
+
+  ALLOCATE (start,   unsigned, Nimage);
+  ALLOCATE (stop,    unsigned, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    start[i] = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop[i]  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+  }
+}
+
+void initImageBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  ALLOCATE (Nlist, int, Nimage);
+  ALLOCATE (NLIST, int, Nimage);
+  ALLOCATE (clist, int *, Nimage);
+  ALLOCATE (mlist, int *, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeImageBins (int Ncatalog) {
+
+  int i;
+
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+  for (i = 0; i < Nimage; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (clist);
+  free (mlist);
+}
+
+/* match measurements to images */
+void findImages (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  char *name;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      matchImage (catalog, j, i);
+    }
+  }
+
+  for (i = 0; VERBOSE && (i < Nimage); i++) {
+    name = GetPhotcodeNamebyCode (image[i].photcode);
+    fprintf (stderr, "image %d has %d measures (%s, %s)\n", i, Nlist[i], 
+	     ohana_sec_to_date(image[i].tzero), name);
+  } 
+}
+
+/* modify this function to use the measure->imageID field */
+void matchImage (Catalog *catalog, int meas, int cat) {
+
+  int i;
+  Measure *measure;
+  
+  measure = &catalog[cat].measure[meas];
+
+  /* find the image that supplied this measurement */
+  for (i = 0; i < Nimage; i++) {
+    if (image[0].photcode == -1) continue;
+    if (measure[0].photcode != image[i].photcode) continue;
+    if (measure[0].t < start[i]) continue;
+    if (measure[0].t > stop[i]) continue;
+    
+    // index for (catalog, measure) -> image
+    bin[cat][meas] = i;
+
+    // index for image, Nentry -> catalog
+    clist[i][Nlist[i]] = cat;
+
+    // index for image, Nentry -> measure
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+}
+
+Coords *getCoords (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NULL);
+  return (&image[i].coords);
+}
+
+void plot_images () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  ALLOCATE (xlist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+  ALLOCATE (Mlist, double, Nimage);
+
+  /**** dMcal vs airmass ****/
+  for (i = 0; i < Nimage; i++) {
+    Mlist[i] = image[i].Mcal;
+    dlist[i] = image[i].dMcal;
+    xlist[i] = image[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin; 
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nimage, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, Mlist, dlist, Nimage, "Mcal vs dMcal", NULL);
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nimage; i++) {
+    bin = image[i].dMcal / 0.00025;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D
+void fixImageRaw (Catalog *catalog, int Ncatalog, int im) {
+
+  int i, m, c, n;
+  double X, Y, L, M, P, Q, R, D, dR, dD;
+  
+  Mosaic *mosaic;
+  Coords *moscoords, *imcoords;
+  
+  moscoords = NULL;
+  mosaic = getMosaicForImage (im);
+  if (mosaic != NULL) {
+      moscoords = &mosaic[0].coords;
+  }
+  imcoords = &image[im].coords;
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+
+    X = catalog[c].measure[m].Xccd;
+    Y = catalog[c].measure[m].Yccd;
+    n = catalog[c].measure[m].averef;
+
+    if (moscoords == NULL) {
+      // this is a Simple image (not a mosaic)
+      // note that for a Simple image, L,M = P,Q
+      XY_to_LM (&L, &M, X, Y, imcoords);
+      LM_to_RD (&R, &D, L, M, imcoords);
+    } else {
+      XY_to_LM (&L, &M, X, Y, imcoords);
+      XY_to_LM (&P, &Q, L, M, moscoords);
+      LM_to_RD (&R, &D, P, Q, moscoords);
+    }
+
+    // new dR, dD : test
+    dR = 3600.0*(catalog[c].average[n].R - R);
+    dD = 3600.0*(catalog[c].average[n].D - D);
+    
+    if (fabs(catalog[c].measure[m].dR - dR) > 10.0) {
+      // XXXXX running into this still for last megacam exposure: wrong mosaic?
+      // ???? inconsistently hitting this????
+      fprintf (stderr, "!");
+      // abort ();
+    }
+    if (fabs(catalog[c].measure[m].dD - dD) > 10.0) {
+      fprintf (stderr, "*");
+      // abort ();
+    }
+
+    catalog[c].measure[m].dR = dR;
+    catalog[c].measure[m].dD = dD;
+
+    if (catalog[c].measure[m].dR > +180.0*3600.0) {
+      // average on high end of boundary, move star up
+      R += 360.0;
+      catalog[c].measure[m].dR = 3600.0*(catalog[c].average[n].R - R);
+    }
+    if (catalog[c].measure[m].dR < -180.0*3600.0) {
+      // average on low end of boundary, move star down
+      R -= 360.0;
+      catalog[c].measure[m].dR = 3600.0*(catalog[c].average[n].R - R);
+    }
+  }  
+  return;
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D.  This function is used by the image fitting steps, for
+// which the detections have already been filtered when they were loaded (bcatalog)
+StarData *getImageRaw (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode) {
+
+  int i, m, c, n;
+  
+  Mosaic *mosaic;
+  Coords *moscoords;
+  StarData *raw;
+  
+  ALLOCATE (raw, StarData, Nlist[im]);
+
+  mosaic = NULL;
+  moscoords = NULL;
+  if (mode == MODE_MOSAIC) {
+      mosaic = getMosaicForImage (im);
+      if (mosaic == NULL) {
+	fprintf (stderr, "mosaic not found for image %s\n", image[im].name);
+	exit (1);
+      }
+      moscoords = &mosaic[0].coords;
+  }
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+
+    /* apply the current image transformation or use the current value of R+dR, D+dD? */
+    raw[i].X = catalog[c].measure[m].Xccd;
+    raw[i].Y = catalog[c].measure[m].Yccd;
+    
+    raw[i].Mag  = catalog[c].measure[m].M;
+    raw[i].dMag = catalog[c].measure[m].dM;
+    raw[i].dPos = GetAstromError (&catalog[c].measure[m], ERROR_MODE_POS);
+
+    n = catalog[c].measure[m].averef;
+
+    // an object with only one detection provides no information about the image calibration
+    raw[i].mask = FALSE;
+    if (catalog[c].average[n].Nmeasure < 2) {
+      raw[i].mask = TRUE;
+    }
+
+    switch (mode) {
+      case MODE_SIMPLE:
+	/* note that for a Simple image, L,M = P,Q */
+	XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, &image[im].coords);
+	raw[i].P = raw[i].L;
+	raw[i].Q = raw[i].M;
+	LM_to_RD (&raw[i].R, &raw[i].D, raw[i].P, raw[i].Q, &image[im].coords);
+	break;
+      case MODE_MOSAIC:
+	XY_to_LM (&raw[i].L, &raw[i].M, raw[i].X, raw[i].Y, &image[im].coords);
+	XY_to_LM (&raw[i].P, &raw[i].Q, raw[i].L, raw[i].M, moscoords);
+	LM_to_RD (&raw[i].R, &raw[i].D, raw[i].P, raw[i].Q, moscoords);
+	break;
+    default:
+      fprintf (stderr, "error: invalid mode in getImageRaw");
+      abort ();
+    }
+  }  
+
+  *Nstars = Nlist[im];
+  return (raw);
+}
+
+// return StarData values for averages positions in the specified image, converting coordinates from
+// the sky positions: R,D -> P,Q -> L,M -> X,Y
+
+StarData *getImageRef (Catalog *catalog, int Ncatalog, int im, int *Nstars, CoordMode mode) {
+
+  int i, m, c, n;
+
+  Mosaic *mosaic;
+  Coords *moscoords;
+  StarData *ref;
+  
+  ALLOCATE (ref, StarData, Nlist[im]);
+
+  mosaic = NULL;
+  moscoords = NULL;
+  if (mode == MODE_MOSAIC) {
+    mosaic = getMosaicForImage (im);
+    if (mosaic == NULL) {
+      fprintf (stderr, "mosaic not found for image %s\n", image[im].name);
+      exit (1);
+    }
+    moscoords = &mosaic[0].coords;
+  }
+
+  for (i = 0; i < Nlist[im]; i++) {
+    m = mlist[im][i];
+    c = clist[im][i];
+    n = catalog[c].measure[m].averef;
+
+    /* apply the current image transformation or use the current value of R+dR, D+dD? */
+    ref[i].R = catalog[c].average[n].R;
+    ref[i].D = catalog[c].average[n].D;
+    
+    ref[i].Mag  = catalog[c].measure[m].M;
+    ref[i].dMag = catalog[c].measure[m].dM;
+    ref[i].dPos = GetAstromError (&catalog[c].measure[m], ERROR_MODE_POS);
+
+    ref[i].mask = FALSE;
+
+    /* note that for a Simple image, L,M = P,Q */
+    switch (mode) {
+      case MODE_SIMPLE:
+      RD_to_LM (&ref[i].P, &ref[i].Q, ref[i].R, ref[i].D, &image[im].coords);
+      ref[i].L = ref[i].P;
+      ref[i].M = ref[i].Q;
+      LM_to_XY (&ref[i].X, &ref[i].Y, ref[i].L, ref[i].M, &image[im].coords);
+      break;
+      case MODE_MOSAIC:
+      RD_to_LM (&ref[i].P, &ref[i].Q, ref[i].R, ref[i].D, moscoords);
+      LM_to_XY (&ref[i].L, &ref[i].M, ref[i].P, ref[i].Q, moscoords);
+      LM_to_XY (&ref[i].X, &ref[i].Y, ref[i].L, ref[i].M, &image[im].coords);
+      break;
+      default:
+	fprintf (stderr, "invalid case");
+	abort();
+    }
+  }
+  
+  *Nstars = Nlist[im];
+  return (ref);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/MosaicOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/MosaicOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/MosaicOps.c	(revision 17422)
@@ -0,0 +1,186 @@
+# include "relastro.h"
+
+// array of mosaic definition structures
+static int    Nmosaic;
+static Mosaic *mosaic;
+
+// list of all images associated with a mosaic
+static int   *Nmosaic_own_images; // number of images for this mosaic
+static int   *Amosaic_own_images; // size of allocated array
+static int   **mosaic_own_images; // array of arrays: mosaic -> images
+
+// list of mosaic associated with each image  
+static int    Nmosaic_for_images; // number of images (for internal checks)
+static int    *mosaic_for_images; // array of: image -> mosaic
+
+Mosaic *getmosaics (int *N) {
+  *N = Nmosaic;
+  return (mosaic);
+}
+
+// find mosaic frames (unique time periods & photcode name matches mosaic) 
+void initMosaics (Image *image, int Nimage) {
+
+  int i, j, found, NMOSAIC;
+  unsigned int start, stop;
+
+  Nmosaic = 0;
+  NMOSAIC = 10;
+  ALLOCATE (mosaic, Mosaic, NMOSAIC);
+
+  ALLOCATE (Nmosaic_own_images, int, NMOSAIC);
+  ALLOCATE (Amosaic_own_images, int, NMOSAIC);
+  ALLOCATE (mosaic_own_images, int *, NMOSAIC);
+
+  /* find the mosaic images (coords.ctype = DIS); generate list of unique mosaics */
+  for (i = 0; i < Nimage; i++) {
+    if (strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* a new mosaic, define ranges */
+    mosaic[Nmosaic].start = start;
+    mosaic[Nmosaic].stop  = stop;
+    mosaic[Nmosaic].Mcal  = 0.0;
+    mosaic[Nmosaic].dMcal = 0.0;
+    mosaic[Nmosaic].Xm    = 0.0;
+    mosaic[Nmosaic].code  = image[i].code;
+    mosaic[Nmosaic].secz  = image[i].secz;
+    mosaic[Nmosaic].coords = image[i].coords;
+
+    // init the mosaic_own_images array data
+    Nmosaic_own_images[Nmosaic] = 0;
+    Amosaic_own_images[Nmosaic] = 10;
+    ALLOCATE (mosaic_own_images[Nmosaic], int, Amosaic_own_images[Nmosaic]);
+
+    Nmosaic ++;
+    if (Nmosaic == NMOSAIC) {
+      NMOSAIC += 10;
+      REALLOCATE (mosaic, Mosaic, NMOSAIC);
+      REALLOCATE (mosaic_own_images, int *, NMOSAIC);
+      REALLOCATE (Nmosaic_own_images, int, NMOSAIC);
+      REALLOCATE (Amosaic_own_images, int, NMOSAIC);
+    }
+  }
+
+  // array to store image->mosaic index
+  Nmosaic_for_images = Nimage;
+  ALLOCATE (mosaic_for_images, int, Nmosaic_for_images);
+
+  /* now assign the WRP images to these mosaics */
+  for (i = 0; i < Nimage; i++) {
+    mosaic_for_images[i] = -1; // default value for no mosaic found
+
+    if (strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* find existing mosaic with this time range */
+    found = FALSE;
+    for (j = 0; !found && (j < Nmosaic); j++) { 
+      if (stop  < mosaic[j].start) continue;
+      if (start > mosaic[j].stop)  continue;
+      found = TRUE;
+      break;
+    }
+    /* if no matching mosaic exists, skip this image */
+    if (!found) continue;
+
+    // mosaic corresponding to this image
+    mosaic_for_images[i] = j;
+
+    // add image to mosaic_own_image list 
+    mosaic_own_images[j][Nmosaic_own_images[j]] = i;
+    Nmosaic_own_images[j] ++;
+    if (Nmosaic_own_images[j] == Amosaic_own_images[j]) {
+      Amosaic_own_images[j] += 10;
+      REALLOCATE (mosaic_own_images[j], int, Amosaic_own_images[j]);
+    }
+    assert (Nmosaic_own_images[j] < Amosaic_own_images[j]);
+  }
+
+  return;
+}
+
+// return StarData values for detections in the specified image, converting coordinates from the
+// chip positions: X,Y -> L,M -> P,Q -> R,D
+StarData *getMosaicRaw (Catalog *catalog, int Ncatalog, int mos, int *Nstars) {
+
+  int i, j, im, Nraw, Nnew;
+  StarData *raw, *new;
+
+  Nraw = 0;
+  ALLOCATE (raw, StarData, 1);
+
+  // loop over the images owned by this mosaic
+  for (i = 0; i < Nmosaic_own_images[mos]; i++) {
+
+    im = mosaic_own_images[mos][i];
+    
+    // retrieve stars for this chip, applying chip and mosaic astrometry
+    // this function does the reverse-lookup for the mosaic corresponding to this image
+    new = getImageRaw (catalog, Ncatalog, im, &Nnew, MODE_MOSAIC);
+    
+    // merge new and raw
+    REALLOCATE (raw, StarData, Nraw + Nnew);
+    for (j = 0; j < Nnew; j++) {
+      raw[Nraw+j] = new[j];
+    }
+    Nraw += Nnew;
+
+    free (new);
+  }
+
+  *Nstars = Nraw;
+  return (raw);
+}
+
+// return StarData values for averages positions in the specified image, converting coordinates from
+// the sky positions: R,D -> P,Q -> L,M -> X,Y
+StarData *getMosaicRef (Catalog *catalog, int Ncatalog, int mos, int *Nstars) {
+
+  int i, j, im, Nref, Nnew;
+  StarData *ref, *new;
+  
+  Nref = 0;
+  ALLOCATE (ref, StarData, 1);
+
+  for (i = 0; i < Nmosaic_own_images[mos]; i++) {
+
+    im = mosaic_own_images[mos][i];
+    
+    // retrieve stars for this chip, applying chip and mosaic astrometry
+    // this function does the reverse-lookup for the mosaic corresponding to this image
+    new = getImageRef (catalog, Ncatalog, im, &Nnew, MODE_MOSAIC);
+    
+    // merge new and ref
+    REALLOCATE (ref, StarData, Nref + Nnew);
+    for (j = 0; j < Nnew; j++) {
+      ref[Nref+j] = new[j];
+    }
+    Nref += Nnew;
+
+    free (new);
+  }
+
+  *Nstars = Nref;
+  return (ref);
+}
+
+Mosaic *getMosaicForImage (int im) {
+
+  int mos;
+
+  if (im < 0) abort();
+  if (im >= Nmosaic_for_images) abort();
+
+  // search for the mosaic that 
+  mos = mosaic_for_images[im];
+  if (mos < 0) return NULL;
+
+  return &mosaic[mos];
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/ParFactor.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/ParFactor.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/ParFactor.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "relastro.h"
+
+# if (0)
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+/* ra and dec are returned as decimal hours and decimal degrees. */
+void lpsun (double jd, double *ra, double *dec) {
+
+  double n, L, g, lambda,epsilon,alpha,delta,x,y,z;
+
+  n = jd - J2000;
+  L = 280.460 + 0.9856474 * n;
+  g = (357.528 + 0.9856003 * n)/DEG_IN_RADIAN;
+  lambda = (L + 1.915 * sin(g) + 0.020 * sin(2. * g))/DEG_IN_RADIAN;
+  epsilon = (23.439 - 0.0000004 * n)/DEG_IN_RADIAN;
+
+  // this is the conversion from ecliptic to celestial coords
+  x = cos(lambda);
+  y = cos(epsilon)*sin(lambda);
+  z = sin(epsilon)*sin(lambda);
+
+  *ra = (atan_circ(x,y))*HRS_IN_RADIAN;
+  *dec = (asin(z))*DEG_IN_RADIAN;
+}
+# endif
+
+/* code borrowed from Skycalc : fix this stuff XXX */
+/* Low precision formulae for the sun, from Almanac p. C24 (1990) */
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon) {
+
+  double n, L, g;
+
+# define J2000 2451545.       /* Julian date at standard epoch */
+
+  n = jd - J2000;
+  L = 280.460 + 0.9856474 * n;
+  g = (357.528 + 0.9856003 * n)*RAD_DEG;
+  *lambda = L + 1.915 * sin(g) + 0.020 * sin(2. * g); // longitude in degrees
+  *beta = 0.0;					  // approx latitude
+  *epsilon = (23.439 - 0.0000004 * n);		  // obliquity of ecliptic in degrees
+  return TRUE;
+}
+
+/* given RA, DEC, Time, calculate the parallax factor */
+int ParFactor (double *pR, double *pD, double R, double D, time_t T) {
+
+  double jd;
+  double L, B, E, e, s, r, d;
+
+  /* given a time T in UNIX seconds, determine the solar longitude S */
+
+  jd = ohana_sec_to_jd (T);
+  sun_ecliptic (jd, &L, &B, &E);
+
+  e = E * RAD_DEG;
+  s = L * RAD_DEG;
+  r = R * RAD_DEG;
+  d = D * RAD_DEG;
+  
+  *pR =  +(cos(e)*sin(s)*cos(r) - cos(s)*sin(r));
+  *pD =  -(cos(e)*sin(s)*sin(r) + cos(s)*cos(r))*sin(d) + sin(e)*sin(s)*cos(d);
+  return TRUE;
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/SetSignals.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/SetSignals.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/SetSignals.c	(revision 17422)
@@ -0,0 +1,122 @@
+# include "relastro.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/Shutdown.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/Shutdown.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/Shutdown.c	(revision 17422)
@@ -0,0 +1,30 @@
+# include "relastro.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
+
+/* XXX this is probably not needed anymore : just protect the write statements */
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateChips.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateChips.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateChips.c	(revision 17422)
@@ -0,0 +1,36 @@
+# include "relastro.h"
+
+int UpdateChips (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each non-mosaic chip independently */
+  int i, Nimage, Nraw, Nref;
+  Image *image;
+  StarData *raw, *ref;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip all except WRP images */
+    if (strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+
+    /* convert measure coordinates to raw entries */
+    raw = getImageRaw (catalog, Ncatalog, i, &Nraw, MODE_MOSAIC);
+
+    /* convert average coordinates to ref entries */
+    ref = getImageRef (catalog, Ncatalog, i, &Nref, MODE_MOSAIC);
+
+    // note that Nraw & Nref must be equal: if not, we made a programming error in one of these two functions.
+    assert (Nraw == Nref);
+
+    // FitChip does iterative, clipped fitting
+    fprintf (stderr, "image %d : Nstars: %d\n", i, Nraw);
+    FitChip (raw, ref, Nraw, &image[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMeasures.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMeasures.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMeasures.c	(revision 17422)
@@ -0,0 +1,21 @@
+# include "relastro.h"
+
+int UpdateMeasures (Catalog *catalog, int Ncatalog) {
+
+  int i, Nimage;
+  Image *image;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip DIS images (since they have no associated detections) */
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* convert measure coordinates to raw entries */
+    fixImageRaw (catalog, Ncatalog, i);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMosaic.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMosaic.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateMosaic.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "relastro.h"
+
+int UpdateMosaic (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each mosaic independently */
+  int i, Nmosaic, Nstars;
+  Mosaic *mosaic;
+  StarData *raw, *ref;
+
+  mosaic = getmosaics (&Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+
+    /* convert measure coordinates to raw entries */
+    raw = getMosaicRaw (catalog, Ncatalog, i, &Nstars);
+
+    /* convert average coordinates to ref entries */
+    ref = getMosaicRef (catalog, Ncatalog, i, &Nstars);
+
+    // XXX : I'll need to supply these back to the image[] entry
+    FitMosaic (raw, ref, Nstars, &mosaic[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjectOffsets.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjectOffsets.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjectOffsets.c	(revision 17422)
@@ -0,0 +1,43 @@
+# include "relastro.h"
+
+int UpdateObjectOffsets (SkyList *skylist) {
+
+  int i;
+  Catalog catalog;
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // match measurements with images
+    initImageBins (&catalog, 1);
+    findImages (&catalog, 1);
+
+    // update the detection coordinates using the new image parameters
+    UpdateMeasures (&catalog, 1);
+
+    freeImageBins (1);
+
+    // write the updated detections to disk
+    save_catalogs (&catalog, 1);
+  }
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateObjects.c	(revision 17422)
@@ -0,0 +1,330 @@
+# include "relastro.h"
+
+static int Nmax;
+static double *X, *dX;
+static double *Y, *dY;
+static double *R, *dR;
+static double *D, *dD;
+static double *pX;
+static double *pY;
+static double *T;
+static double *dT;
+
+void initObjectData (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+
+  ALLOCATE (R, double, MAX (1, Nmax));
+  ALLOCATE (D, double, MAX (1, Nmax));
+  ALLOCATE (T, double, MAX (1, Nmax));
+  ALLOCATE (X, double, MAX (1, Nmax));
+  ALLOCATE (Y, double, MAX (1, Nmax));
+
+  ALLOCATE (dR, double, MAX (1, Nmax));
+  ALLOCATE (dD, double, MAX (1, Nmax));
+  ALLOCATE (dT, double, MAX (1, Nmax));
+  ALLOCATE (dX, double, MAX (1, Nmax));
+  ALLOCATE (dY, double, MAX (1, Nmax));
+
+  ALLOCATE (pX, double, MAX (1, Nmax));
+  ALLOCATE (pY, double, MAX (1, Nmax));
+}  
+
+int UpdateObjects (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N, Nsecfilt, found, kp;
+  StatType statsR, statsD;
+  Coords coords;
+  PMFit fit;
+  time_t To;
+  int mode, Nave, Npm, Npar, Nskip;
+  double Tmin, Tmax;
+  float mag;
+  int mask;
+  PhotCode *code;
+
+  initObjectData (catalog, Ncatalog);
+
+  /* project coordinates to a plane centered on the object with units of arcsec */
+  coords.crval1 = 0;
+  coords.crval2 = 0;
+  coords.crpix1 = 0;
+  coords.crpix2 = 0;
+  coords.cdelt1 = coords.cdelt2 = 1.0 / 3600.0;
+  coords.pc1_1  = coords.pc2_2 = 1.0;
+  coords.pc1_2  = coords.pc2_1 = 0.0;
+  coords.Npolyterms = 1;
+  strcpy (coords.ctype, "RA---SIN");
+
+  // use J2000 as a reference time
+  To = ohana_date_to_sec ("2000/01/01");
+  Nave = Npar = Npm = 0;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  for (i = 0; i < Ncatalog; i++) {
+
+    if (VERBOSE) fprintf (stderr, "astrometrize catalog %d : %d ave, %d meas\n", i, catalog[i].Naverage, catalog[i].Nmeasure);
+
+    Nskip = 0;
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      /* calculate the average value of R,D for a single star */
+
+      // skip objects which are known to be problematic
+      // XXX include this code or not?
+      # if (0)
+      if (catalog[i].average[j].code & STAR_BAD) {
+	Nskip ++;
+	continue;  
+      }
+      # endif
+
+      N = 0;
+      m = catalog[i].average[j].measureOffset;
+
+      Tmin = Tmax = (catalog[i].measure[m].t - To) / (86400*365.25);
+      mode = FIT_MODE;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+
+	// exclude measurements by previous outlier detection
+	// XXX include this code or not?
+	# if (0)
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) { 
+	  catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	  continue;
+	}
+	# endif
+
+	/* exclude measurements by quality */
+	if (PhotFlagSelect) {
+	  if (PhotFlagBad) {
+	    mask = PhotFlagBad;
+	  } else {
+	    code = GetPhotcodebyCode (catalog[i].measure[m].photcode);
+	    mask = code[0].astromBadMask;
+	  }
+	  if (mask & catalog[i].measure[m].photFlags) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	}
+	
+	/* exclude measurements by mag limit */
+	if (ImagSelect) {
+	  mag = PhotInst (&catalog[i].measure[m]);
+	  if (mag < ImagMin) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	  if (mag > ImagMax) {
+	    catalog[i].measure[m].dbFlags |= ID_MEAS_SKIP_ASTROM;
+	    continue;
+	  }
+	}
+
+	/* select or exclude measurements by photcode, or equiv photcode, if specified */
+	if (NphotcodesKeep > 0) {
+	  found = FALSE;
+	  for (kp = 0; (kp < NphotcodesKeep) && !found; kp++) {
+	    if (photcodesKeep[kp][0].code == catalog[i].measure[m].photcode) found = TRUE;
+	    if (photcodesKeep[kp][0].code == GetPhotcodeEquivCodebyCode(catalog[i].measure[m].photcode)) found = TRUE;
+	  }
+	  if (!found) continue;
+	}
+	if (NphotcodesSkip > 0) {
+	  found = FALSE;
+	  for (kp = 0; (kp < NphotcodesSkip) && !found; kp++) {
+	    if (photcodesSkip[kp][0].code == catalog[i].measure[m].photcode) found = TRUE;
+	    if (photcodesSkip[kp][0].code == GetPhotcodeEquivCodebyCode(catalog[i].measure[m].photcode)) found = TRUE;
+	  }
+	  if (found) continue;
+	}
+
+	R[N] = getMeanR (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	D[N] = getMeanD (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	T[N] = (catalog[i].measure[m].t - To) / (86400*365.25) ; // time relative to J2000 in years
+
+	Tmin = MIN(Tmin, T[N]);
+	Tmax = MAX(Tmax, T[N]);
+
+	dR[N] = GetAstromError (&catalog[i].measure[m], ERROR_MODE_RA);
+	dD[N] = GetAstromError (&catalog[i].measure[m], ERROR_MODE_DEC);
+	dT[N] = catalog[i].measure[m].dt;
+
+	N++;
+      }
+
+      // if we have too few good detections for the desired fit, or too limited a baseline, use a
+      // fit with fewer parameters.  XXX if we have too few parameters for even the average
+      // position, consider including the lower-quality detections
+
+      catalog[i].average[j].code &= ~ID_STAR_FEW;
+
+      // XXX add the parallax factor range as a criterion as well
+      if ((Tmax - Tmin) < PM_DT_MIN) mode = FIT_AVERAGE;
+      if ((mode == FIT_PM_ONLY) && (N < PM_TOOFEW)) mode = FIT_AVERAGE;
+
+      // too few measurements for average position (require 2 values)
+      if (N < 2) {
+	// XXX need to define PHOTOM and ASTROM object flags
+	// catalog[i].average[j].code |= ID_STAR_FEW;
+	continue;
+      }
+
+      /* we need to do the fit in a locally linear space; choose a ref coordinate */
+      coords.crval1 = R[0];
+      coords.crval2 = D[0];
+      
+      /* project all of the R,D coordinates to a plane centered on this coordinate */
+      for (k = 0; k < N; k++) {
+	RD_to_XY (&X[k], &Y[k], R[k], D[k], &coords);
+	dX[k] =  dR[k];
+	dY[k] =  dD[k];
+	// fprintf (stderr, "%d %f %f %f  %f %f\n", k, T[k], R[k], D[k], X[k], Y[k]);
+      }	  
+
+      /* fit the model components as needed */
+      switch (mode) {
+	case FIT_AVERAGE:
+	  liststats (R, dR, N, &statsR);
+	  liststats (D, dD, N, &statsD);
+
+	  fit.Ro = statsR.mean;
+	  fit.dRo = 3600.0*statsR.sigma;
+
+	  fit.Do = statsD.mean;
+	  fit.dDo = 3600.0*statsD.sigma;
+
+	  fit.chisq = 0.5*(statsR.chisq + statsD.chisq);
+	  fit.Nfit = N;
+
+	  fit.uR = fit.duR = 0.0;
+	  fit.uD = fit.duD = 0.0;
+	  fit.p  = fit.dp  = 0.0;
+
+	  Nave ++;
+	  break;
+
+	case FIT_PM_ONLY:
+	  FitPM (&fit, X, dX, Y, dY, T, N);
+	  // fprintf (stderr, "fitted:  %f - %f : %f %f : %f %f : %f\n", Tmin, Tmax, fit.Ro, fit.Do, fit.uR, fit.uD, fit.p);
+	  // project Ro, Do back to RA,DEC
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+	  // fprintf (stderr, "project: %f %f : %f %f : %f\n", fit.Ro, fit.Do, fit.uR, fit.uD, fit.p);
+	  // continue;
+
+	  fit.p  = fit.dp  = 0.0;
+
+	  Npm ++;
+	  break;
+
+	case FIT_PAR_ONLY:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+
+	  for (k = 0; k < N; k++) {
+	    ParFactor (&pX[k], &pY[k], R[k], D[k], T[k]);
+	  }
+	  FitPar (&fit, X, dX, Y, dY, pX, pY, N);
+
+	  // project Ro, Do back to RA,DEC
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+
+	  fit.uR = fit.duR = 0.0;
+	  fit.uD = fit.duD = 0.0;
+
+	  Npar ++;
+	  break;
+
+	case FIT_PM_AND_PAR:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+
+	  for (k = 0; k < N; k++) {
+	    ParFactor (&pX[k], &pY[k], R[k], D[k], T[k]);
+	  }
+	  FitPMandPar (&fit, X, dX, Y, dY, T, pX, pY, N);
+	  XY_to_RD (&fit.Ro, &fit.Do, fit.Ro, fit.Do, &coords);
+	  Npar ++;
+	  break;
+
+	default:
+	  fprintf (stderr, "programming error at %s, %d", __FILE__, __LINE__);
+	  exit (2);
+      }	  
+
+      if (0 && (j < 100)) {
+	fprintf (stderr, "%f %f -> %f %f (%f,%f)\n",
+		 catalog[i].average[j].R, 
+		 catalog[i].average[j].D, 
+		 fit.Ro, fit.Do, 
+		 3600*(catalog[i].average[j].R - fit.Ro), 
+		 3600*(catalog[i].average[j].D - fit.Do));
+      }
+
+      // the measure fields must be updated before the average fields
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	// XXX why was this here?? if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	setMeanR (fit.Ro, &catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+	setMeanD (fit.Do, &catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]);
+      }      
+
+      catalog[i].average[j].R  	= fit.Ro; // RA in degrees
+      catalog[i].average[j].D  	= fit.Do; // DEC in degrees
+      catalog[i].average[j].dR 	= fit.dRo; // RA scatter in arcsec
+      catalog[i].average[j].dD 	= fit.dDo; // DEC scatter in arcsec
+
+      catalog[i].average[j].uR  = fit.uR; // RA proper motion in arcsec/year
+      catalog[i].average[j].uD  = fit.uD; // DEC proper motion in arcsec/year
+      catalog[i].average[j].duR = fit.duR; // RA proper motion error in arcsec/year
+      catalog[i].average[j].duD = fit.duD; // DEC proper motion error in arcsec/year
+
+      catalog[i].average[j].P   = fit.p; // parallax in arcsec
+      catalog[i].average[j].dP  = fit.dp; // parallax error in arcsec
+
+      catalog[i].average[j].Xp  = (fit.Nfit > 1) ? 100.0*log10(fit.chisq) : NAN_S_SHORT;
+    }
+
+    if (VERBOSE) fprintf (stderr, "catalog %d : %d ave, %d pm, %d par : Nskip % d\n", i, Nave, Npm, Npar, Nskip);
+  }
+
+  if (VERBOSE) fprintf (stderr, "fitted %d objects (%d ave, %d pm, %d par)\n", Nave + Npm + Npar, Nave, Npm, Npar);
+  return (TRUE);
+}
+
+/* fitting proper-motion and parallax:
+
+given a source at position r,d, at a time t, we need to calculate a vector (pr,pd)
+
+let x,y be the coordinate in the linearized frame with y parallel to DEC lines
+
+L,B are the ecliptic longitude and latitude of the object, 
+dL and dB are the offsets in the L and B directions
+
+dL = sin(t - topp)
+dB = cos(t - topp)*sin(B)
+
+these need to be rotated to the R,D frame to yield pR,pD.  Then, the equation of motion
+for the source in the x,y frame is:
+
+x = Ro + uR * (t - to) + p * pR 
+y = Do + uD * (t - to) + p * pD
+
+the unknowns in these equations are Ro, uR, Do, uD, and p
+
+XXX think through the concepts for the pole a bit better.  all objects near the pole 
+move the same way with the same phase.  choose a projection center and define dL,dB relative 
+to that center point coordinate system?
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateSimple.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateSimple.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/UpdateSimple.c	(revision 17422)
@@ -0,0 +1,32 @@
+# include "relastro.h"
+
+int UpdateSimple (Catalog *catalog, int Ncatalog) {
+
+  /* we can measure new image parameters for each non-mosaic chip independently */
+  int i, Nimage, Nstars;
+  Image *image;
+  StarData *raw, *ref;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+
+    /* skip WRP and DIS images */
+    if (!strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+    if (!strcmp(&image[i].coords.ctype[4], "-DIS")) continue;
+
+    /* convert measure coordinates to raw entries */
+    raw = getImageRaw (catalog, Ncatalog, i, &Nstars, MODE_SIMPLE);
+
+    /* convert average coordinates to ref entries */
+    ref = getImageRef (catalog, Ncatalog, i, &Nstars, MODE_SIMPLE);
+
+    FitSimple (raw, ref, Nstars, &image[i].coords);
+
+    free (raw);
+    free (ref);
+  }
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/args.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/args.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/args.c	(revision 17422)
@@ -0,0 +1,270 @@
+# include "relastro.h"
+void usage (void);
+
+int args (int argc, char **argv) {
+
+  int N;
+  double trange;
+
+  /* possible operations */
+  FIT_TARGET = TARGET_NONE;
+  FIT_MODE = FIT_AVERAGE;
+  if ((N = get_argument (argc, argv, "-update-objects"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_OBJECTS;
+
+    // check for object fitting modes (not valid for images)
+    if ((N = get_argument (argc, argv, "-pm"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PM_ONLY;
+    }
+    if ((N = get_argument (argc, argv, "-par"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PAR_ONLY;
+    }
+    if ((N = get_argument (argc, argv, "-pmpar"))) {
+	remove_argument (N, &argc, argv);
+	FIT_MODE = FIT_PM_AND_PAR;
+    }
+  }
+  if ((N = get_argument (argc, argv, "-update-simple"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_SIMPLE;
+  }
+  if ((N = get_argument (argc, argv, "-update-chips"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_CHIPS;
+  }
+  if ((N = get_argument (argc, argv, "-update-mosaics"))) {
+    remove_argument (N, &argc, argv);
+    FIT_TARGET = TARGET_MOSAICS;
+  }
+  if (FIT_TARGET == TARGET_NONE) usage();
+
+  /* specify portion of the sky : allow default of all sky? */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    usage ();
+  }
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+	fprintf (stderr, "ERROR: syntax error\n");
+	return (FALSE);
+      }
+    } else {
+      if (trange < 0) {
+	trange = fabs (trange);
+	TSTOP = TSTART;
+	TSTART -= trange;
+      } else {
+	TSTOP = TSTART + trange;
+      }
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  PHOTCODE_KEEP_LIST = NULL;
+  if ((N = get_argument (argc, argv, "+photcode"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_KEEP_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PHOTCODE_SKIP_LIST = NULL;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_SKIP_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  CHIPORDER = 0;
+  if ((N = get_argument (argc, argv, "-chiporder"))) {
+    remove_argument (N, &argc, argv);
+    CHIPORDER = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  SAVEPLOT = FALSE;
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTDELAY = 500000;
+  if ((N = get_argument (argc, argv, "-plotdelay"))) {
+    remove_argument (N, &argc, argv);
+    PLOTDELAY = 1e6*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // by default, require > 10pts to clip
+  strcpy (STATMODE, "CHI_INNER_80_WTMEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  /* XXX update these for relevant plots */
+  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
+  if ((N = get_argument (argc, argv, "-plrange"))) {
+    remove_argument (N, &argc, argv);
+    PlotMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX update this */
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
+
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+  
+  // for now, make the default to ignore the photflags
+  // XXX make it true by default instead?
+  PhotFlagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "+photflags"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagSelect = TRUE;
+  }
+
+  PhotFlagBad = 0;
+  if ((N = get_argument (argc, argv, "-photflagbad"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagBad = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PhotFlagPoor = 0;
+  if ((N = get_argument (argc, argv, "-photflagpoor"))) {
+    remove_argument (N, &argc, argv);
+    PhotFlagPoor = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX drop this? */
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
+  }
+
+  if (argc != 1) usage ();
+  return TRUE;
+}
+
+void usage () {
+  fprintf (stderr, "ERROR: USAGE: relastro -region RA RA DEC DEC\n");
+  fprintf (stderr, "  working options: \n");
+  fprintf (stderr, "  -update-objects\n");
+  fprintf (stderr, "    -pm\n");
+  fprintf (stderr, "    -par\n");
+  fprintf (stderr, "    -pmpar\n");
+  fprintf (stderr, "  -update-simple\n");
+  fprintf (stderr, "  -update-chips\n");
+  fprintf (stderr, "  -update-mosaics\n");
+  fprintf (stderr, "  -time (start)(stop)\n");
+  fprintf (stderr, "  +photcode (code)[,code,code...]\n");
+  fprintf (stderr, "  -photcode (code)[,code,code...]\n");
+  fprintf (stderr, "  -plot\n");
+  fprintf (stderr, "  -plotdelay (seconds)\n");
+  fprintf (stderr, "  -statmode (mode)\n");
+  fprintf (stderr, "  -reset");
+  fprintf (stderr, "  -update : apply new fit to database\n");
+  fprintf (stderr, "  -params\n");
+  fprintf (stderr, "  -plrange\n");
+  fprintf (stderr, "  -minerror\n");
+  fprintf (stderr, "  -area\n");
+  fprintf (stderr, "  -area Xmin Xmax Ymin Ymax\n");
+  fprintf (stderr, "  -instmag min max\n\n");
+  fprintf (stderr, "  +photflags\n");
+  fprintf (stderr, "  -photflagbad\n");
+  fprintf (stderr, "  -photflagpoor\n");
+  fprintf (stderr, "  -v\n");
+  fprintf (stderr, "  \n");
+  exit (2);
+} 
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/bcatalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/bcatalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/bcatalog.c	(revision 17422)
@@ -0,0 +1,144 @@
+# include "relastro.h"
+
+int bcatalog (Catalog *subcatalog, Catalog *catalog) {
+  
+  int i, j, k, offset, found;
+  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm, Nsecfilt;
+  float mag;
+  int mask;
+  PhotCode *code;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  /* we are moving only the subset of measurements from catalog[0] to subcatalog[0] */
+  NAVERAGE = 50;
+  NMEASURE = 1000;
+  ALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+  ALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+  ALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+  Nmeasure = Naverage = 0;
+
+  /* exclude stars not in range or with too few measurements */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    if (catalog[0].average[i].Nmeasure < 2) continue; 
+
+    /* start with all stars good */
+    subcatalog[0].average[Naverage] = catalog[0].average[i];
+    subcatalog[0].average[Naverage].measureOffset = Nmeasure;
+    for (j = 0; j < Nsecfilt; j++) {
+      subcatalog[0].secfilt[Nsecfilt*Naverage+j] = catalog[0].secfilt[Nsecfilt*i+j];
+    }
+
+    if (RESET) {
+      // XXX reset the ra,dec coords?
+      // XXX define astrometry vs photometry average.dbFlags
+      // XXX put photometry flags in secfilt (ie, per average mag?)
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_FEW;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_POOR;
+    }
+
+    Nm = 0;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+
+      offset = catalog[0].average[i].measureOffset + j;
+
+      /* select measurements by photcode, or equiv photcode, if specified */
+      if (NphotcodesKeep > 0) {
+	found = FALSE;
+	for (k = 0; (k < NphotcodesKeep) && !found; k++) {
+	  if (photcodesKeep[k][0].code == catalog[0].measure[offset].photcode) found = TRUE;
+	  if (photcodesKeep[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
+	}
+	if (!found) continue;
+      }
+      if (NphotcodesSkip > 0) {
+	found = FALSE;
+	for (k = 0; (k < NphotcodesSkip) && !found; k++) {
+	  if (photcodesSkip[k][0].code == catalog[0].measure[offset].photcode) found = TRUE;
+	  if (photcodesSkip[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
+	}
+	if (found) continue;
+      }
+
+      /* select measurements by time */
+      if (TimeSelect) {
+	if (catalog[0].measure[offset].t < TSTART) continue;
+	if (catalog[0].measure[offset].t > TSTOP) continue;
+      }
+
+      /* select measurements by quality */
+      if (DophotSelect && (catalog[0].measure[offset].dophot != DophotValue)) continue;
+
+      /* select measurements by quality */
+      if (PhotFlagSelect) {
+	if (PhotFlagBad) {
+	  mask = PhotFlagBad;
+	} else {
+	  code = GetPhotcodebyCode (catalog[0].measure[offset].photcode);
+	  mask = code[0].astromBadMask;
+	}
+	if (mask & catalog[0].measure[offset].photFlags) continue;
+      }
+
+      /* select measurements by measurement error */
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
+
+      /* select measurements by mag limit */
+      if (ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[offset]);
+	if (mag < ImagMin) continue;
+	if (mag > ImagMax) continue;
+      }
+
+      // re-assess on each run of relastro if a measurement should be used
+
+      // NOCAL : this flag is used to mark measurements which must be ignored for this
+      // analysis.  an example would be time ranges or photcodes which are ignored.  NOCAL
+      // is internal only; it is not written out.
+
+      // SKIP : this flag is used to mark measurements currently ignored, but may be
+      // allowed before the analysis is complete.  for example, a star with measurements
+      // only near the edge of images will have this flag set for most of the analysis,
+      // but before the final average properties are calculated, these measurements may be
+      // allowed.
+
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_SKIP_ASTROM;
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_NOCAL;
+      subcatalog[0].measure[Nmeasure]          = catalog[0].measure[offset];
+      subcatalog[0].measure[Nmeasure].averef   = Naverage;
+      if (RESET) { 
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_POOR_ASTROM;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_AREA;
+      }
+      Nmeasure ++;
+      Nm ++;
+      if (Nmeasure == NMEASURE) {
+	NMEASURE += 1000;
+	REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+      }
+    }
+    subcatalog[0].average[Naverage].Nmeasure = Nm;
+    Naverage ++;
+    if (Naverage == NAVERAGE) {
+      NAVERAGE += 50;
+      REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+      REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*Nsecfilt);
+    }
+  }
+  REALLOCATE (subcatalog[0].average, Average, MAX (Naverage, 1));
+  REALLOCATE (subcatalog[0].measure, Measure, MAX (Nmeasure, 1));
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, Nsecfilt*MAX (Naverage, 1));
+  subcatalog[0].Naverage = Naverage;
+  subcatalog[0].Nmeasure = Nmeasure;
+  subcatalog[0].Nsecfilt = catalog[0].Nsecfilt;
+  subcatalog[0].Nsecf_mem = Naverage * Nsecfilt;
+  assert (Nsecfilt == catalog[0].Nsecfilt);
+
+  if (VERBOSE) {
+    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
+   }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/dvo_astrom_ops.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/dvo_astrom_ops.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/dvo_astrom_ops.c	(revision 17422)
@@ -0,0 +1,91 @@
+# include "relastro.h"
+
+double getMeanR (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  double ra;
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  ra = average[0].R - measure[0].dR / 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (ra);
+}
+
+double getMeanD (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  double dec;
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  dec = average[0].D - measure[0].dD / 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (dec);
+}
+
+int setMeanR (double ra_fit, Measure *measure, Average *average, SecFilt *secfilt) {
+
+  /* math to get from new fitted position to new measure offset
+     ra_obs = average[0].R - measure[0].dR / 3600.0;
+     measure[0].dR = (ra_fit - ra_obs) * 3600.0;
+     measure[0].dR = (ra_fit - average[0].R + measure[0].dR/3600) * 3600.0
+     measure[0].dR = (ra_fit - average[0].R) * 3600.0 + measure[0].dR;
+  */
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  measure[0].dR += (ra_fit - average[0].R) * 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (TRUE);
+}
+
+int setMeanD (double dec_fit, Measure *measure, Average *average, SecFilt *secfilt) {
+
+  /* math to get from new fitted position to new measure offset
+     dec_obs = average[0].D - measure[0].dD / 3600.0;
+     measure[0].dD = (dec_fit - dec_obs) * 3600.0;
+     measure[0].dD = (dec_fit - average[0].D + measure[0].dD/3600) * 3600.0
+     measure[0].dD = (dec_fit - average[0].D) * 3600.0 + measure[0].dD;
+  */
+
+  /* the measure carries the instantaneous mean position at the epoch t */ 
+  measure[0].dD += (dec_fit - average[0].D) * 3600.0;
+
+  /* possible corrections to mean ra:
+
+  - proper-motion and parallax
+  - abberation
+  - precession and nutation, etc
+  - refraction
+  - DCR
+
+  */
+
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/fitpoly.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/fitpoly.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/fitpoly.c	(revision 17422)
@@ -0,0 +1,334 @@
+# include "relastro.h"
+
+/* these functions support simultaneous 2D fits to
+   x2 = \sum a_i,j x1^i y1^j 
+   y2 = \sum b_i,j x1^i y1^j 
+
+   the order of the fit (largest coefficient) is fixed to a single
+   value for both x1,y1 terms and for x2,y2 fits
+
+   the code is currently confusing because we limit to i+j <= order.
+   this could be cleaner if we used masks and allowed i <= order, j <= order
+*/
+
+double **array_init (int Nx, int Ny) {
+
+  int i;
+  double **array;
+
+  ALLOCATE (array, double *, Nx);
+  for (i = 0; i < Nx; i++) {
+    ALLOCATE (array[i], double, Ny);
+    memset (array[i], 0, Ny*sizeof(double));
+  }    
+  return (array);
+}
+
+void array_free (double **array, int Nx) {
+
+  int i;
+
+  for (i = 0; i < Nx; i++) {
+    free (array[i]);
+  }    
+  free (array);
+}
+
+// XXX define a fit structure and drop the file static variables?
+CoordFit *fit_init (int order) {
+
+  CoordFit *fit;
+
+  ALLOCATE (fit, CoordFit, 1);
+
+  fit[0].Npts   = 0;
+  fit[0].Norder = order;
+  fit[0].Nterms = order + 1;
+  fit[0].Nsums  = 2*order + 1;
+  fit[0].Nelems = SQ(order + 1);
+
+  /* summing arrays for fit solution */
+
+  // xsum[i][j] holds \sum (x2 wt x1^i y1^j)
+  // ysum[i][j] holds \sum (y2 wt x1^i y1^j)
+  fit[0].xsum = array_init (fit[0].Nterms, fit[0].Nterms);
+  fit[0].ysum = array_init (fit[0].Nterms, fit[0].Nterms);
+
+  // xfit[i][j] holds x2 coeff for x1^i y1^j
+  // yfit[i][j] holds y2 coeff for x1^i y1^j
+  fit[0].xfit = array_init (fit[0].Nterms, fit[0].Nterms);
+  fit[0].yfit = array_init (fit[0].Nterms, fit[0].Nterms);
+
+  // sum[i][j] holds \sum (wt x1^i y1^j)
+  fit[0].sum = array_init (fit[0].Nsums, fit[0].Nsums);
+
+  return (fit);
+}
+
+void fit_free (CoordFit *fit) {
+
+  array_free (fit[0].xfit, fit[0].Nterms);
+  array_free (fit[0].yfit, fit[0].Nterms);
+
+  array_free (fit[0].sum, fit[0].Nsums);
+  array_free (fit[0].xsum, fit[0].Nterms);
+  array_free (fit[0].ysum, fit[0].Nterms);
+  
+  free (fit);
+}
+  
+// XXX use implicit masks as below or explicit masks (with function to set?)
+// XXX eg, add a global mask to this file and 
+void fit_add (CoordFit *fit, double x1, double y1, double x2, double y2, double wt) {
+
+  int ix, iy;
+  double xterm, yterm, term;
+
+  xterm = 1;
+  for (ix = 0; ix < fit[0].Nsums; ix++) {
+    yterm = 1;
+    for (iy = 0; iy < fit[0].Nsums; iy++) {
+      term = xterm*yterm*wt;
+      fit[0].sum[ix][iy] += term;
+      if ((iy < fit[0].Nterms) && (ix < fit[0].Nterms)) {
+	fit[0].xsum[ix][iy] += x2*term;
+	fit[0].ysum[ix][iy] += y2*term;
+      }
+      yterm *= y1;
+    }
+    xterm *= x1;
+  }
+  fit[0].Npts ++;
+}
+
+/* convert the xsum,ysum,sum terms into vector,matrix and solve */
+void fit_eval (CoordFit *fit) {
+
+  int i, j, ix, iy, jx, jy;
+  double **matrix, **vector;
+
+  if (fit[0].Npts == 0) {
+    fprintf (stderr, "warning: no valid pts\n");
+  }
+
+  // matrix, vector hold the final linear system
+  matrix = array_init (fit[0].Nelems, fit[0].Nelems);
+  vector = array_init (fit[0].Nelems, 2);
+
+  /* remap the xsum,ysum terms into the vector */
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    vector[i][0] = fit[0].xsum[ix][iy];
+    vector[i][1] = fit[0].ysum[ix][iy];
+
+    for (j = 0; j < fit[0].Nelems; j++) {
+      jx = j % fit[0].Nterms;
+      jy = j / fit[0].Nterms;
+      matrix[i][j] = fit[0].sum[ix+jx][iy+jy];
+    }
+
+    // mask the terms not represented by the Coords terms
+    if (ix + iy > fit[0].Norder) {
+      for (j = 0; j < fit[0].Nelems; j++) {
+	matrix[i][j] = 0.0;
+      }
+      vector[i][0] = 0.0;
+      vector[i][1] = 0.0;
+      matrix[i][i] = 1.0;
+    }      
+  }
+
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    // fprintf (stderr, "x2 : x^%dy^%d: %10.4g    y2 : x^%dy^%d: %10.4g \n", 
+    // ix, iy, vector[i][0], ix, iy, vector[i][1]);
+  }	
+
+  dgaussjordan (matrix, vector, fit[0].Nelems, 2); 
+
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    // fprintf (stderr, "x2 : x^%dy^%d: %10.4g    y2 : x^%dy^%d: %10.4g \n", 
+    // ix, iy, vector[i][0], ix, iy, vector[i][1]);
+  }	
+
+  /* remap the vector terms into xfit,yfit */
+  for (i = 0; i < fit[0].Nelems; i++) {
+    ix = i % fit[0].Nterms;
+    iy = i / fit[0].Nterms;
+    fit[0].xfit[ix][iy] = vector[i][0];
+    fit[0].yfit[ix][iy] = vector[i][1];
+  }
+
+  array_free (matrix, fit[0].Nelems);
+  array_free (vector, fit[0].Nelems);
+}
+
+void fit_apply (CoordFit *fit, double *x2, double *y2, double x1, double y1) {
+  
+  int ix, iy;
+  double xterm, yterm, term;
+  double x, y;
+
+  x = 0.0;
+  y = 0.0;
+
+  xterm = 1;
+  for (ix = 0; ix < fit[0].Nterms; ix++) {
+    yterm = 1;
+    for (iy = 0; iy < fit[0].Nterms; iy++) {
+      term = xterm*yterm;
+      x += fit[0].xfit[ix][iy]*term;
+      y += fit[0].yfit[ix][iy]*term;
+      yterm *= y1;
+    }
+    xterm *= x1;
+  }
+  *x2 = x;
+  *y2 = y;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_dx (double **poly, int Nx, int Ny) {
+
+  int i, j, Nxout, Nyout;
+  double **dpoly;
+
+  Nxout = Nx - 1;
+  Nyout = Ny;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  dpoly = array_init (Nxout, Nyout);
+
+  for (i = 0; i < Nxout; i++) {
+    for (j = 0; j < Nyout; j++) {
+      dpoly[i][j] = poly[i+1][j] * (i+1);
+    }
+  }
+  return dpoly;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_dy (double **poly, int Nx, int Ny) {
+
+  int i, j, Nxout, Nyout;
+  double **dpoly;
+
+  Nxout = Nx;
+  Nyout = Ny - 1;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  dpoly = array_init (Nxout, Nyout);
+
+  for (i = 0; i < Nxout; i++) {
+    for (j = 0; j < Nyout; j++) {
+      dpoly[i][j] = poly[i][j+1] * (j+1);
+    }
+  }
+  return dpoly;
+}
+
+// Nx, Ny is the number of terms in x and in y
+double **poly2d_copy (double **poly, int Nx, int Ny) {
+
+  int i, j;
+  double **out;
+
+  // poly[i][j] holds coeff for x1^i y1^j
+  out = array_init (Nx, Ny);
+
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++) {
+      out[i][j] = poly[i][j];
+    }
+  }
+  return out;
+}
+
+double poly2d_eval (double **poly, int Nx, int Ny, double x, double y) {
+
+  int i, j;
+  double xsum, ysum, sum;
+
+  sum = 0;
+  xsum = ysum = 1.0;
+
+  for (i = 0; i < Nx; i++) {
+    ysum = xsum;
+    for (j = 0; j < Ny; j++) {
+      sum += poly[i][j] * ysum;
+      ysum *= y;
+    }
+    xsum *= x;
+  }
+  return (sum);
+}
+
+/* linear portion of fit : NORDER is 1 */
+/* this should only apply to the polynomial, not the projection terms */
+/* compare with psastro supporting code */
+CoordFit *fit_apply_coords (CoordFit *fit, Coords *coords) {
+
+  double Xo, Yo, R1, R2;
+  CoordFit *modfit;
+
+  /* I have L,M = fit(X,Y). set corresponding terms for coords */
+
+  // L = a[0][0] + a[1][0]x^1 y^0 + a[0][1] x^0 y^1 + ...
+  // L = pc1_1*cd1*(x - cp1) + pc1_2*cd2*(y - cp2) + ...
+
+  CoordsGetCenter (fit, 0.001, &Xo, &Yo);
+  coords[0].crpix1 = Xo;
+  coords[0].crpix2 = Yo;
+  
+  // resulting fit should have zero constant terms
+  modfit = CoordsSetCenter (fit, Xo, Yo);
+
+  /* we do not modify crval1,2: these are kept at the default values */
+
+  // set cdelt1, cdelt2
+  coords[0].cdelt1 = hypot (modfit[0].xfit[1][0], modfit[0].xfit[0][1]);
+  coords[0].cdelt2 = hypot (modfit[0].yfit[1][0], modfit[0].yfit[0][1]);
+  R1 = 1 / coords[0].cdelt1;
+  R2 = 1 / coords[0].cdelt2;
+
+  // set pc1_1, pc1_2, pc2_1, pc2_2 (cd1,cd2 = 1.0)
+  coords[0].pc1_1 = modfit[0].xfit[1][0] * R1;
+  coords[0].pc1_2 = modfit[0].xfit[0][1] * R2;
+  coords[0].pc2_1 = modfit[0].yfit[1][0] * R1;
+  coords[0].pc2_2 = modfit[0].yfit[0][1] * R2;
+
+  // set the polyterm elements 
+  if (coords->Npolyterms > 1) {
+    coords[0].polyterms[0][0] = modfit[0].xfit[2][0]*R1*R1;
+    coords[0].polyterms[1][0] = modfit[0].xfit[1][1]*R1*R2;
+    coords[0].polyterms[2][0] = modfit[0].xfit[0][2]*R2*R2;
+
+    coords[0].polyterms[0][1] = modfit[0].yfit[2][0]*R1*R1;
+    coords[0].polyterms[1][1] = modfit[0].yfit[1][1]*R1*R2;
+    coords[0].polyterms[2][1] = modfit[0].yfit[0][2]*R2*R2;
+  }
+
+  // I need to validate Norder
+  if (coords->Npolyterms > 2) {
+    coords[0].polyterms[3][0] = modfit[0].xfit[3][0]*R1*R1*R1;
+    coords[0].polyterms[4][0] = modfit[0].xfit[2][1]*R1*R1*R2;
+    coords[0].polyterms[5][0] = modfit[0].xfit[1][2]*R1*R2*R2;
+    coords[0].polyterms[6][0] = modfit[0].xfit[0][3]*R2*R2*R2;
+
+    coords[0].polyterms[3][1] = modfit[0].yfit[3][0]*R1*R1*R1;
+    coords[0].polyterms[4][1] = modfit[0].yfit[2][1]*R1*R1*R2;
+    coords[0].polyterms[5][1] = modfit[0].yfit[1][2]*R1*R2*R2;
+    coords[0].polyterms[6][1] = modfit[0].yfit[0][3]*R2*R2*R2;
+  }
+
+  fit_free (modfit);
+  /* keep the order and type from initial values */
+  
+  // XXX if desired in the future, return modfit (and free above)
+  return (NULL);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/initialize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/initialize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/initialize.c	(revision 17422)
@@ -0,0 +1,78 @@
+# include "relastro.h"
+
+void initialize (int argc, char **argv) {
+
+  int NPHOTCODES;
+  char *codename, *ptr, *list;
+
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  /* build a list of accepted photcodes. these will be used by bcatalog to accept or
+     reject loaded data */
+
+  NphotcodesKeep = 0;
+  photcodesKeep = NULL;
+  if (PHOTCODE_KEEP_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesKeep, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesKeep */
+    list = PHOTCODE_KEEP_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_LIST: %s\n", PHOTCODE_KEEP_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesKeep[NphotcodesKeep] = GetPhotcodebyName (codename)) == NULL) {
+	fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+	exit (1);
+      }
+      NphotcodesKeep ++;
+      CHECK_REALLOCATE (photcodesKeep, PhotCode *, NPHOTCODES, NphotcodesKeep, 10);
+    }
+  }
+
+  NphotcodesSkip = 0;
+  photcodesSkip = NULL;
+  if (PHOTCODE_SKIP_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesSkip, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesSkip */
+    list = PHOTCODE_SKIP_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_LIST: %s\n", PHOTCODE_SKIP_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesSkip[NphotcodesSkip] = GetPhotcodebyName (codename)) == NULL) {
+	fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+	exit (1);
+      }
+      NphotcodesSkip ++;
+      CHECK_REALLOCATE (photcodesSkip, PhotCode *, NPHOTCODES, NphotcodesSkip, 10);
+    }
+  }
+
+  initstats (STATMODE);
+
+  // IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+  // STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+  // MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_ASTROM | ID_MEAS_SKIP_ASTROM | ID_MEAS_AREA;
+
+  /* XXX drop irrelevant entries */
+  if (SHOW_PARAMS) {
+    fprintf (stderr, "current parameter settings:\n");
+    if (TimeSelect) {
+      fprintf (stderr, "TimeSelect: TRUE (%s - %s)\n", ohana_sec_to_date (TSTART), ohana_sec_to_date (TSTOP));
+    } else {
+      fprintf (stderr, "TimeSelect: FALSE\n");
+    }
+    fprintf (stderr, "VERBOSE: %d, PLOTSTUFF: %d\n", VERBOSE, PLOTSTUFF);
+
+    fprintf (stderr, "IMAGE_CATALOG          %s\n",  ImageCat);
+    fprintf (stderr, "GSCFILE                %s\n",  GSCFILE);
+    fprintf (stderr, "CATDIR                 %s\n",  CATDIR);
+    exit (0);
+  }
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/liststats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/liststats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/liststats.c	(revision 17422)
@@ -0,0 +1,149 @@
+# include "relastro.h"
+
+enum {M_MEDIAN, 
+      M_MEAN, M_WTMEAN, 
+      M_INNER_MEAN, M_INNER_WTMEAN, 
+      M_INNER_80_MEAN, M_INNER_80_WTMEAN, 
+      M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN, 
+      M_CHI_INNER_80_MEAN, M_CHI_INNER_80_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WTMEAN")) statmode = M_WTMEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "INNER_80_MEAN")) statmode = M_INNER_80_MEAN;
+  if (!strcmp (mode, "INNER_80_WTMEAN")) statmode = M_INNER_80_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_80_MEAN")) statmode = M_CHI_INNER_80_MEAN;
+  if (!strcmp (mode, "CHI_INNER_80_WTMEAN")) statmode = M_CHI_INNER_80_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  ke = ks = dMo = 0;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  stats[0].sigma = 0;
+  stats[0].error = 0;
+  stats[0].chisq = 0;
+  if (N < 1) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  if (N % 2) {
+    stats[0].median = value[(int)(0.5*N)];
+  } else {
+    stats[0].median = 0.5*(value[N/2] + value[N/2 - 1]);
+  }
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WTMEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  case M_INNER_80_MEAN:
+  case M_INNER_80_WTMEAN:
+  case M_CHI_INNER_80_MEAN:
+  case M_CHI_INNER_80_WTMEAN:
+    ks = 0.1*N + 0.1;
+    ke = 0.8*N + 0.1;
+    if (N <= 10) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || 
+      (statmode == M_CHI_INNER_WTMEAN) || 
+      (statmode == M_CHI_INNER_80_MEAN) || 
+      (statmode == M_CHI_INNER_80_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WTMEAN) || 
+      (statmode == M_INNER_WTMEAN) || 
+      (statmode == M_INNER_80_WTMEAN) || 
+      (statmode == M_CHI_INNER_WTMEAN) || 
+      (statmode == M_CHI_INNER_80_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/load_catalogs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/load_catalogs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/load_catalogs.c	(revision 17422)
@@ -0,0 +1,68 @@
+# include "relastro.h"
+
+Catalog *load_catalogs (SkyList *skylist, int *Ncatalog, int subselect) {
+
+  int i, j, k, m, Nstar;
+  Catalog *catalog, *pcatalog, tcatalog;
+
+  if (VERBOSE) fprintf (stderr, "loading catalog data\n");
+
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // we only allow output if we do not use a subset.  in this case,
+    // the output parameters are correctly set for catalog[i] via pcatalog
+    pcatalog = subselect ? &tcatalog : &catalog[i];
+
+    // set up the basic catalog info
+    pcatalog[0].filename  = skylist[0].filename[i];
+    pcatalog[0].catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    pcatalog[0].catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    pcatalog[0].catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF; // don't need to load all data at this point
+    pcatalog[0].Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (pcatalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", pcatalog[0].filename);
+      exit (1);
+    }
+    if (VERBOSE && !pcatalog[0].Naves_disk) fprintf (stderr, "no data in %s, skipping\n", pcatalog[0].filename);
+
+    // select only the brighter stars
+    if (subselect) {
+      bcatalog (&catalog[i], &tcatalog);
+      dvo_catalog_unlock (&tcatalog);
+      dvo_catalog_free (&tcatalog);
+    } else {
+      if (RESET) {
+	for (j = 0; j < catalog[i].Naverage; j++) {
+	  catalog[i].average[j].code = 0;
+	  m = catalog[i].average[j].measureOffset;
+	  for (k = 0; k < catalog[i].average[j].Nmeasure; k++) {
+	    catalog[i].measure[m+k].dbFlags = 0;
+	  }
+	}
+      }
+    }
+  }
+
+  // XXX keep this test?
+  Nstar = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    Nstar += catalog[i].Naverage;
+  }
+  if (Nstar < 2) { 
+    fprintf (stderr, "warning: insufficient stars %d\n", Nstar);
+  }
+
+  // XXX consider only returning the populated catalogs
+  *Ncatalog = skylist[0].Nregions;
+  return (catalog);
+}
+
+/* this function loads all relevant catalog files for the first pass.  it currently loads the data
+   read only (SOFT lock) since it assumes the image table has been locked. if we go to the new
+   addstar locking paradigm, in which the images and catalogs are updated independently, then we may
+   need to use an XCLD lock here.  
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/load_images.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/load_images.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/load_images.c	(revision 17422)
@@ -0,0 +1,34 @@
+# include "relastro.h"
+
+SkyList *load_images (FITS_DB *db, SkyRegion *region) {
+
+  Image     *image, *subset;
+  int        Nimage, Nsubset;
+  int       *LineNumber;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area
+  skylist = SkyListByPatch (sky, -1, region);
+
+  // convert database table to internal structure
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  // select the images which overlap the selected sky regions
+  subset = select_images (skylist, image, Nimage, &LineNumber, &Nsubset);
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
+
+  initImages (subset, Nsubset);
+  initMosaics (subset, Nsubset);
+  
+  /* unlock, if we can (else, unlocked below) */
+  if (!UPDATE) dvo_image_unlock (db); 
+
+  return (skylist);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/mkpolyterm.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/mkpolyterm.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/mkpolyterm.c	(revision 17422)
@@ -0,0 +1,166 @@
+# include "relastro.h"
+
+double factorial (int N) {
+
+  int i;
+  double F;
+
+  F = 1;
+  for (i = N; i > 1; i--) {
+    F *= i;
+  }
+  return F;
+}
+
+// XXX use a separate structure for the map (x2,y2 = f,g(x1,y1)) ?
+
+// given a 2D transformation -- L(x,y),M(x,y) -- find the coordinates x,y
+// for which L,M = 0,0. tol is the allowed error on x,y.
+int CoordsGetCenter (CoordFit *fit, double tol, double *xo, double *yo) {
+
+  int i, Nx, Ny;
+  double R, Xo, Yo, dPos, dPosRef;
+  double **XdX, **XdY, **YdX, **YdY, **alpha, **beta;
+  double **xfit, **yfit;
+
+  xfit = fit[0].xfit;
+  yfit = fit[0].yfit;
+
+  // solve for Xo,Yo where L,M(Xo,Yo) = 0,0
+  // start with linear solution for Xo,Yo:
+  R  = (xfit[1][0]*yfit[0][1] - xfit[0][1]*yfit[1][0]);
+  Xo = (yfit[0][0]*xfit[0][1] - xfit[0][0]*yfit[0][1])/R;
+  Yo = (xfit[0][0]*yfit[1][0] - yfit[0][0]*xfit[1][0])/R;
+
+  Nx = fit[0].Nterms;
+  Ny = fit[0].Nterms;
+
+  // iterate to actual solution: requires small non-linear terms
+  if (fit[0].Norder > 1) {
+    XdX = poly2d_dx (xfit, Nx, Ny);
+    XdY = poly2d_dy (xfit, Nx, Ny);
+    YdX = poly2d_dx (yfit, Nx, Ny);
+    YdY = poly2d_dy (yfit, Nx, Ny);
+
+    alpha = array_init (2, 2);
+    beta  = array_init (2, 1);
+
+    /* this loop uses the Newton-Raphson method to solve for Xo,Yo
+     * it needs the high order terms to be small 
+     * Xo,Yo are in pixels;
+     */
+    dPos = tol + 1;
+    dPosRef = dPos;
+    for (i = 0; (dPos > tol) && (i < 20); i++) {
+      // NOTE: order for alpha is: [y][x]
+      alpha[0][0] = poly2d_eval (XdX, Nx-1, Ny,   Xo, Yo);
+      alpha[1][0] = poly2d_eval (XdY, Nx,   Ny-1, Xo, Yo);
+      alpha[0][1] = poly2d_eval (YdX, Nx-1, Ny,   Xo, Yo);
+      alpha[1][1] = poly2d_eval (YdY, Nx,   Ny-1, Xo, Yo);
+
+      beta[0][0] = poly2d_eval (xfit, Nx, Ny, Xo, Yo);
+      beta[1][0] = poly2d_eval (yfit, Nx, Ny, Xo, Yo);
+
+      dgaussjordan (alpha, beta, 2, 1);
+
+      Xo -= beta[0][0];
+      Yo -= beta[1][0];
+      dPos = hypot(beta[0][0], beta[1][0]);
+      if (i == 0) {
+	dPosRef = dPos;
+      }
+    }
+    if (dPos > dPosRef) {
+      fprintf (stderr, "*** warning : non-convergence in model conversion (mkpolyterm.c;73) *** \n");
+    }
+    array_free (alpha, 2);
+    array_free (beta, 2);
+    array_free (XdX, Nx - 1);
+    array_free (XdY, Nx);
+    array_free (YdX, Nx - 1);
+    array_free (YdY, Nx);
+  }
+  *xo = Xo;
+  *yo = Yo;
+  return TRUE;
+}
+
+// convert a transformation L(x,y) to L'(x-xo,y-yo)
+CoordFit *CoordsSetCenter (CoordFit *input, double Xo, double Yo) {
+
+  int i, j, Nx, Ny;
+  double **xPx, **yPx, **xPy, **yPy, **tmp;
+
+  CoordFit *output;
+  output = fit_init (input->Norder);
+
+  /* given two equivalent polynomial representations L(x,y) = \sum_i \sum_j A_{i,j} x^i y^j
+   * we can transform L(x,y) into L'(x-xo,y-yo) by taking the derivatives of both sides and 
+   * noting that the constant term in each is the coefficient in the case of L(x,y) and is the 
+   * value of L'(-xo,-yo) in the second case.
+   */
+
+  Nx = input->Nterms;
+  Ny = input->Nterms;
+
+  xPx = poly2d_copy (input->xfit, Nx, Ny);
+  yPx = poly2d_copy (input->yfit, Nx, Ny);
+
+  for (i = 0; i < input->Nterms; i++) {
+    xPy = poly2d_copy (xPx, Nx, Ny);
+    yPy = poly2d_copy (yPx, Nx, Ny);
+    for (j = 0; j < input->Nterms; j++) {
+      output->xfit[i][j] = poly2d_eval (xPy, Nx, Ny, Xo, Yo) / factorial(i) / factorial(j);
+      output->yfit[i][j] = poly2d_eval (yPy, Nx, Ny, Xo, Yo) / factorial(i) / factorial(j);
+
+      // take the next derivative wrt y, catch output (is NULL on last pass)
+      if (Ny > 0) {
+	tmp = poly2d_dy(xPy, Nx, Ny);
+	array_free (xPy, Nx);
+	xPy = tmp;
+
+	tmp = poly2d_dy(yPy, Nx, Ny);
+	array_free (yPy, Nx);
+	yPy = tmp;
+      } else {
+	array_free (xPy, Nx);
+	array_free (yPy, Nx);
+      }
+
+      Ny --;
+    }
+    Ny = input->Nterms;
+    // take the next derivative wrt x, catch output (is NULL on last pass)
+    if (Nx > 0) {
+      tmp = poly2d_dx(xPx, Nx, Ny);
+      array_free (xPx, Nx);
+      xPx = tmp;
+      tmp = poly2d_dx(yPx, Nx, Ny);
+      array_free (yPx, Nx);
+      yPx = tmp;
+      Nx --;
+    } else {
+      array_free (xPx, Nx);
+      array_free (yPx, Nx);
+    }
+  }
+  return output;
+}
+
+/*
+  Coords uses a rigid sequence for the coefficients:
+
+  x^0 y^0 : crpix1,2 (but note this is applied before higher order terms)
+
+  x^1 y^0 : pc1_1, pc2_1
+  x^0 y^1 : pc1_2, pc2_2
+
+  x^2 y^0 : polyterm[0][0,1]
+  x^1 y^1 : polyterm[1][0,1]
+  x^0 y^2 : polyterm[2][0,1]
+
+  x^3 y^0 : polyterm[3][0,1]
+  x^2 y^1 : polyterm[4][0,1]
+  x^1 y^2 : polyterm[5][0,1]
+  x^0 y^3 : polyterm[6][0,1]
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/plot_scatter.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/plot_scatter.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/plot_scatter.c	(revision 17422)
@@ -0,0 +1,63 @@
+# include "relastro.h"
+   
+void plot_scatter (Catalog *catalog, int Ncatalog) {
+
+# if (0)   
+  int i, j, k, m, N, Ntot, Nsecfilt;
+  float Mrel, Mcal, Mmos, Mgrid;
+  double *xlist, *ylist, *ilist;
+  Graphdata graphdata;
+
+  // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
+  Nsecfilt = GetPhotcodeNsecfilt ();
+  assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Ntot += catalog[i].average[j].Nmeasure;
+    }
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (ylist, double, Ntot);
+  ALLOCATE (ilist, double, Ntot);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].flags & MEAS_BAD) continue;
+
+	Mrel = catalog[i].secfilt[Nsecfilt*j+PhotSec].M;
+	xlist[N] = Mrel;
+	ylist[N] = PhotSys  (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*Nsecfilt]) - Mcal - Mmos - Mgrid - Mrel;
+	ilist[N] = PhotInst (&catalog[i].measure[m]);
+	N++;
+      }
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = PlotMmin;
+  graphdata.xmax = PlotMmax;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, ylist, N, "mag vs dmag", "Mag.png");
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, ilist, ylist, N, "imag vs dmag", "iMag.png");
+  free (xlist);
+  free (ylist);
+  free (ilist);
+
+# endif
+}
+
+/* XXX this should become astrometrically relevant */
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/plotstuff.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/plotstuff.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/plotstuff.c	(revision 17422)
@@ -0,0 +1,148 @@
+# include "relastro.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+enum {black, white, red, orange, yellow, green, blue, indigo, violet};
+
+/*
+static union { unsigned char c[4]; float f; } f_undef = { {0xff, 0xff, 0xff, 0xfe} };
+# define fUNDEF (f_undef.f)
+*/
+
+static union { unsigned char c[8]; float d; } d_undef = { {0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00} };
+# define dUNDEF (d_undef.d)
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+
+  sprintf (name, "gastro [%d]", N);
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void JpegPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaPNG (Xgraph[N], filename);
+  return;
+}
+
+void PSPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KiiPS (Xgraph[N], filename, TRUE, KAPA_PS_NEWPLOT, "default");
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Npts < 1) return;
+
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+
+  KapaClearSections (Xgraph[N]);
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+  return;
+}
+
+void PlotLabel (char *string, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaSendLabel (Xgraph[N], string, 2);
+}
+
+void PlotVector (int Npts, double *vect, int mode, int N, char *type) {
+
+  float *values;
+  int i;
+
+  if (Npts < 1) return;
+
+  ALLOCATE (values, float, Npts);
+  for (i = 0; i < Npts; i++) {
+    values[i] = vect[i];
+  }
+
+  KapaPlotVector (Xgraph[N], Npts, values, type);
+  free (values);
+  return;
+}
+
+void plot_list (Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file) {
+
+  int i;
+  StatType stats;
+  
+  stats.min = stats.max = xlist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, xlist[i]);
+    stats.max = MAX (stats.max, xlist[i]);
+  }
+  if (graphdata[0].xmin == dUNDEF) graphdata[0].xmin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].xmax == dUNDEF) graphdata[0].xmax = 1.05*stats.max - 0.05*stats.min;
+
+  stats.min = stats.max = ylist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, ylist[i]);
+    stats.max = MAX (stats.max, ylist[i]);
+  }
+  if (graphdata[0].ymin == dUNDEF) graphdata[0].ymin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].ymax == dUNDEF) graphdata[0].ymax = 1.05*stats.max - 0.05*stats.min;
+
+  PrepPlotting (N, graphdata, 0);
+  PlotVector (N, xlist, 0, 0, "x");
+  PlotVector (N, ylist, 1, 0, "y");
+  if (label != NULL) PlotLabel (label, 0);
+  DonePlotting (graphdata, 0);
+
+  if ((file != NULL) && SAVEPLOT) JpegPlot (graphdata, 0, file);
+  if (PLOTDELAY > 500000) {
+    fprintf (stdout, "press return\n"); 
+    fscanf (stdin, "%*c");
+  } else {
+    usleep (PLOTDELAY);
+  }
+}
+
+void plot_defaults (Graphdata *graphdata) {
+
+  graphdata[0].style = 2;
+  graphdata[0].ptype = 2;
+  graphdata[0].ltype = 0;
+  graphdata[0].etype = 0;
+  graphdata[0].color = black;
+  graphdata[0].lweight = 0;
+  graphdata[0].size = 0.5;
+
+  graphdata[0].xmin = dUNDEF;
+  graphdata[0].xmax = dUNDEF;
+  graphdata[0].ymin = dUNDEF;
+  graphdata[0].ymax = dUNDEF;
+   
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro.c	(revision 17422)
@@ -0,0 +1,76 @@
+# include "relastro.h"
+
+int main (int argc, char **argv) {
+
+  int status, Ncatalog;
+  Catalog *catalog;
+  FITS_DB db;
+
+  SkyList *skylist = NULL;
+
+  /* get configuration info, args */
+  initialize (argc, argv);
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (FIT_TARGET == TARGET_OBJECTS) {
+    relastro_objects ();
+    exit (0);
+  }
+
+  /* register database handle with shutdown procedure */
+  set_db (&db);
+
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  if (db.dbstate == LCK_EMPTY) Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+
+  /* load regions and images based on specified sky patch (default depth) */
+  skylist = load_images (&db, &UserPatch);
+
+  /* load catalog data from region files : subselect high-quality measurements */
+  catalog = load_catalogs (skylist, &Ncatalog, TRUE);
+
+  /* match measurements with images */
+  initImageBins (catalog, Ncatalog);
+  findImages (catalog, Ncatalog);
+
+  if (PLOTSTUFF) {
+    // plot_star_coords (catalog, Ncatalog);
+    // plot_mosaic_fields (catalog);
+  }
+
+  /* major modes */
+  switch (FIT_TARGET) {
+    case TARGET_SIMPLE:
+      UpdateSimple (catalog, Ncatalog);
+      break;
+
+    case TARGET_CHIPS:
+      UpdateChips (catalog, Ncatalog);
+      break;
+
+    case TARGET_MOSAICS:
+      UpdateMosaic (catalog, Ncatalog);
+      break;
+
+    default:
+      fprintf (stderr, "programming error at %s:%d", __FILE__, __LINE__);
+      exit (2);
+  }
+
+  if (!UPDATE) exit (0);
+
+  // free the image / measurement pointers
+  freeImageBins (1);
+
+  // iterate over catalogs to make detection coordinates consistant
+  UpdateObjectOffsets (skylist);
+
+  // save the updated image parameters
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db); 
+
+  exit (0);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro_objects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro_objects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/relastro_objects.c	(revision 17422)
@@ -0,0 +1,62 @@
+# include "relastro.h"
+
+int relastro_objects () {
+
+  int i, j, k, m;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // XXX consider what gets reset (only ASTROM flags)
+    if (RESET) {
+      for (j = 0; j < catalog.Naverage; j++) {
+	catalog.average[j].code = 0;
+	m = catalog.average[j].measureOffset;
+	for (k = 0; k < catalog.average[j].Nmeasure; k++) {
+	  catalog.measure[m+k].dbFlags = 0;
+	}
+      }
+    }
+
+    UpdateObjects (&catalog, 1);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    save_catalogs (&catalog, 1);
+  }
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/save_catalogs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/save_catalogs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/save_catalogs.c	(revision 17422)
@@ -0,0 +1,15 @@
+# include "relastro.h"
+
+void save_catalogs (Catalog *catalog, int Ncatalog) {
+
+  int i;
+
+  /* load data from each region file */
+  for (i = 0; i < Ncatalog; i++) {
+
+    if (VERBOSE) fprintf (stderr, "saving catalog %s\n", catalog[i].filename);
+    dvo_catalog_save (&catalog[i], VERBOSE); 
+    dvo_catalog_unlock (&catalog[i]);
+    dvo_catalog_free (&catalog[i]);
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/select_images.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/select_images.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/select_images.c	(revision 17422)
@@ -0,0 +1,254 @@
+# include "relastro.h"
+
+/* this function returns a list of all images which overlap the given SkyList (set of
+   SkyRegions).  All images in the image catalog are tested once, so there is no check that an
+   image already has been included.  LineNum stores the locations in the Image database of the
+   list of images */
+
+typedef struct {
+  double Xc[5];
+  double Yc[5];
+  double Rc;
+  double Dc;
+} SkyRegionCoords;
+
+Image *select_images (SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage) {
+  
+  Image *image;
+  int i, j, k, m, found, nimage, NIMAGE;
+  int InRange;
+  double Ri[5], Di[5], Xi[5], Yi[5], dx, dy;
+  int *line_number;
+  Coords tcoords;
+  SkyRegionCoords *skycoords;
+  
+  if (skylist[0].Nregions < 1) {
+    *Nimage = 0;
+    *LineNumber = NULL;
+    if (VERBOSE) fprintf (stderr, "no matching sky regions\n");
+    return NULL;
+  }
+
+  // the comparison is made in the catalog local projection. below we set crval1,2
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---TAN");
+
+  ALLOCATE (skycoords, SkyRegionCoords, skylist[0].Nregions);
+
+  /* compare with each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) { 
+
+    /* we make positional comparisons in the projection of catalog */
+    skycoords[i].Rc = 0.5*(skylist[0].regions[i][0].Rmax + skylist[0].regions[i][0].Rmin);
+    skycoords[i].Dc = 0.5*(skylist[0].regions[i][0].Dmax + skylist[0].regions[i][0].Dmin);
+    tcoords.crval1 = skycoords[i].Rc;
+    tcoords.crval2 = skycoords[i].Dc;
+
+    /* define catalog corners */
+    RD_to_XY (&skycoords[i].Xc[0], &skycoords[i].Yc[0], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[1], &skycoords[i].Yc[1], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[2], &skycoords[i].Yc[2], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmax, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[3], &skycoords[i].Yc[3], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmax, &tcoords);
+    skycoords[i].Xc[4] = skycoords[i].Xc[0];    
+    skycoords[i].Yc[4] = skycoords[i].Yc[0];    
+
+    dx = 0.02*(skycoords[i].Xc[2] - skycoords[i].Xc[0]);
+    dy = 0.02*(skycoords[i].Yc[2] - skycoords[i].Yc[0]);
+    skycoords[i].Xc[0] -= dx; skycoords[i].Yc[0] -= dy;
+    skycoords[i].Xc[1] += dx; skycoords[i].Yc[1] -= dy;
+    skycoords[i].Xc[2] += dx; skycoords[i].Yc[2] += dy;
+    skycoords[i].Xc[3] -= dx; skycoords[i].Yc[3] += dy;
+    skycoords[i].Xc[4] -= dx; skycoords[i].Yc[4] -= dy;
+  }
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+  BuildChipMatch (timage, Ntimage);
+
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (line_number, int, NIMAGE);
+  
+  // go through the complete list of images, selecting ones which overlap any region
+  for (i = 0; i < Ntimage; i++) {
+      
+    /* select images by photcode, or equiv photcode, if specified */
+    if (NphotcodesKeep > 0) {
+      found = FALSE;
+      for (k = 0; (k < NphotcodesKeep) && !found; k++) {
+	if (photcodesKeep[k][0].code == timage[i].photcode) found = TRUE;
+	if (photcodesKeep[k][0].code == GetPhotcodeEquivCodebyCode(timage[i].photcode)) found = TRUE;
+      }
+      if (!found) continue;
+    }
+    if (NphotcodesSkip > 0) {
+      found = FALSE;
+      for (k = 0; (k < NphotcodesSkip) && !found; k++) {
+	if (photcodesSkip[k][0].code == timage[i].photcode) found = TRUE;
+	if (photcodesSkip[k][0].code == GetPhotcodeEquivCodebyCode(timage[i].photcode)) found = TRUE;
+      }
+      if (found) continue;
+    }
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (timage[i].tzero < TSTART) continue;
+      if (timage[i].tzero > TSTOP) continue;
+    }
+    
+    if (!FindMosaicForImage (timage, Ntimage, i)) continue;
+
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+
+    /* transform to ra,dec */
+    for (j = 0; j < 5; j++) {
+      XY_to_RD (&Ri[j], &Di[j], Xi[j], Yi[j], &timage[i].coords);
+    }
+
+    /* compare with each region file */
+    for (m = 0; (m < skylist[0].Nregions) && !found; m++) { 
+
+      /* we make positional comparisons in the projection of catalog */
+      tcoords.crval1 = skycoords[m].Rc;
+      tcoords.crval2 = skycoords[m].Dc;
+
+      /* transform to ra,dec */
+      InRange = TRUE;
+      for (j = 0; (j < 5) && InRange; j++) {
+	InRange = RD_to_XY (&Xi[j], &Yi[j], Ri[j], Di[j], &tcoords);
+      }
+      if (!InRange) continue;
+
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&Xi[j], &Yi[j], &skycoords[m].Xc[0], &skycoords[m].Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&skycoords[m].Xc[j], &skycoords[m].Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found = edge_check (&Xi[j], &Yi[j], &skycoords[m].Xc[k], &skycoords[m].Yc[k]);
+	}
+      }
+      if (!found) continue;
+
+      image[nimage] = timage[i]; 
+      /* always allow 'few' images to succeed, if possible */
+      if (image[nimage].code & ID_IMAGE_FEW) { 
+	image[nimage].code &= ~(ID_IMAGE_FEW | ID_IMAGE_POOR);
+      }
+      if (RESET) {
+	// XXX do we need this in relastro?
+	assignMcal (&image[nimage], (double *) NULL, -1);
+	image[nimage].dMcal = NAN;
+	image[nimage].code &= ~ID_IMAGE_POOR;
+      }
+      line_number[nimage] = i;
+      nimage ++;
+      if (nimage == NIMAGE) {
+	NIMAGE += 100;
+	REALLOCATE (image, Image, NIMAGE);
+	REALLOCATE (line_number, int, NIMAGE);
+      }
+    }
+  }
+      
+  if (VERBOSE) fprintf (stderr, "found %d images\n", nimage);
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  REALLOCATE (line_number, int, MAX (nimage, 1));
+  free (skycoords);
+
+  *Nimage  = nimage;
+  *LineNumber = line_number;
+  return (image);
+}
+
+/* check if line between points 0 and 1 of x1
+   crosses line between points 0 and 1 of x2 */
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (double *x1, double *y1, double *x2, double *y2) {
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relastro/src/write_coords.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relastro/src/write_coords.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relastro/src/write_coords.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "relastro.h"
+
+# define CD_COORDS 1
+
+void write_coords (Header *header, Coords *coords) {
+
+  gfits_modify (header, "CTYPE1",   "%s",  1, "RA---TAN");
+  gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--TAN");
+
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+# if (CD_COORDS)  
+  gfits_modify (header, "CD1_1",    "%le", 1, coords[0].pc1_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD2_1",    "%le", 1, coords[0].pc2_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD1_2",    "%le", 1, coords[0].pc1_2 * coords[0].cdelt2);
+  gfits_modify (header, "CD2_2",    "%le", 1, coords[0].pc2_2 * coords[0].cdelt2);
+# else
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+# endif
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/Makefile
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/Makefile	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/Makefile	(revision 17422)
@@ -0,0 +1,48 @@
+default: relphot
+help:
+	@echo "make options: relphot (default)"
+
+include ../../Makefile.System
+HOME    =       $(ROOT)/src/relphot
+BIN	=	$(HOME)/bin
+LIB	=	$(HOME)/lib
+SRC	=	$(HOME)/src
+MAN	=	$(HOME)/doc
+INC	= 	$(HOME)/include
+include ../../Makefile.Common
+
+# programs may add their own internal requirements here
+FULL_CFLAGS   = $(BASE_CFLAGS)
+FULL_CPPFLAGS = $(BASE_CPPFLAGS)
+FULL_LDFLAGS  = -lkapa -ldvo -lFITS -lohana $(BASE_LDFLAGS)
+
+relphot: $(BIN)/relphot.$(ARCH)
+install: $(DESTBIN)/relphot
+
+RELPHOT = \
+$(SRC)/ConfigInit.$(ARCH).o	 \
+$(SRC)/GridOps.$(ARCH).o	 \
+$(SRC)/ImageOps.$(ARCH).o	 \
+$(SRC)/MosaicOps.$(ARCH).o	 \
+$(SRC)/SetSignals.$(ARCH).o 	 \
+$(SRC)/Shutdown.$(ARCH).o 	 \
+$(SRC)/StarOps.$(ARCH).o	 \
+$(SRC)/args.$(ARCH).o		 \
+$(SRC)/bcatalog.$(ARCH).o	 \
+$(SRC)/global_stats.$(ARCH).o	 \
+$(SRC)/initialize.$(ARCH).o	 \
+$(SRC)/liststats.$(ARCH).o	 \
+$(SRC)/load_catalogs.$(ARCH).o	 \
+$(SRC)/load_images.$(ARCH).o	 \
+$(SRC)/plot_scatter.$(ARCH).o	 \
+$(SRC)/plotstuff.$(ARCH).o	 \
+$(SRC)/reload_catalogs.$(ARCH).o \
+$(SRC)/relphot.$(ARCH).o	 \
+$(SRC)/relphot_objects.$(ARCH).o	 \
+$(SRC)/select_images.$(ARCH).o	 \
+$(SRC)/setExclusions.$(ARCH).o 	 \
+$(SRC)/setMrelFinal.$(ARCH).o 	 \
+$(SRC)/write_coords.$(ARCH).o
+
+$(RELPHOT): $(INC)/relphot.h
+$(BIN)/relphot.$(ARCH): $(RELPHOT)
Index: /tags/ipp-2-5-1/Ohana/src/relphot/bin/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/bin/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/bin/.cvsignore	(revision 17422)
@@ -0,0 +1,4 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
+*.linrh
+*.darwin
+*.darwin_x86
Index: /tags/ipp-2-5-1/Ohana/src/relphot/doc/ChangeLog.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/doc/ChangeLog.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/doc/ChangeLog.txt	(revision 17422)
@@ -0,0 +1,22 @@
+
+- relphot-1-4:
+  * converted to gfits APIs (forces libfits 1.6)
+  * converted to new DVO APIs (forces libdvo 1.3)
+  * allow seletion by RA/DEC limits
+  * removed old, unused code
+  * added time range to time selections
+
+- relphot-1-3:
+  * dropped IMAGE_CATALOG from config
+  * convert to use libkapa
+  * dropped _PS from average.R,D,M,dM
+  * convert to dvo_image_lock,unlock
+
+- relphot-1-2: released 2005.10.20
+  * substantial changes to sync with addstar v1.3
+    (added mode/format support, new dvo load functions, etc).
+  * various cleanups (functions moved to libohana)
+  * changes to use new libohana (v1.5) / libfits (v1.4)
+
+- relphot-1-1
+  * minor cleanup, loneos.h -> dvo.h
Index: /tags/ipp-2-5-1/Ohana/src/relphot/doc/notes.txt
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/doc/notes.txt	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/doc/notes.txt	(revision 17422)
@@ -0,0 +1,135 @@
+
+2006.10.17
+
+  I am working on a partial upgrade of relphot to handle more general
+  problems than I have treated in the past.  In particular, I would
+  like to be able to have it assign the average magnitudes, regardless
+  of whether any or all images and/or objects are well-treated for
+  relative photometry.  In fact, I would like it to be able to
+  determine average magnitudes even for sources which have only
+  externally supplied data, and thus have no matching images.  I would
+  eventually like to have more flexibility about the filtering which
+  is performed on the measurements to determine the average
+  magnitudes.  Some of this work will require a bit of a more careful
+  treatment of the process.  
+
+  At the moment, we can consider the relphot process to consist of the
+  following steps:
+
+  1) load the complete catalog data
+  2) select a subset of the sources:
+     - appropriate photcode (equiv == selected photcode)
+     - in time range, if specified
+     - appropriate dophot type, if specified
+     - bright enough:
+       - mag < MAG_LIM	
+       - dMag > SIGMA_LIM
+       - ImagMin < iMag < ImagMax
+     - other restrictions
+       - within valid image region for chip
+  3) iterate to a solution for Mcal (image offsets)
+     - mark specific images as bad
+     - mark specific sources as bad
+     - mark specific measurements as bad
+     - include desired restrictions on Mcal (eg, all chips of mosaic
+       matched)
+     - only keep the image offsets
+
+  4) if the results are to be kept, recalculate the average magnitudes
+     using the image exclusions and Mcal values determined above 
+
+2006.05.03
+
+  I am upgrading relphot to accept as input an arbitrary region (using
+  the standard ohana SkyRegion concept of a RA & DEC bounded patch on
+  the sky).  This modification has a few implications.  First, the
+  analysis is in fact performed on the populated SkyRegions which
+  overlap the requested area on the sky.
+
+- select different calibration options
+- plot various terms
+
+- rationalize Mmos & Mcal (sum(Mcal) -> Mmos)
+
+- options:
+
+  -update:  update phot database 
+  -mosaic:  apply per-mosaic corrections
+  -grid:    apply X,Y-based offset
+  -chips:   apply per-chip corrections
+  -trange:  select images in time range
+  
+
+find_images: selects images in region, filtered by photcode, time range
+
+bcatalog:    selects stars & measurments, filtered by mag limit, photcode, time range
+
+image:  a single ccd
+mosaic: a collection of associate ccds (mosaiclink[image])
+grid:   x,y magnitude correction grid across mosaic 
+fit:    2-d fit of mag offsets across field
+
+
+
+m = Mrel + Mcal + Mmos + Mxy + Mgrid
+
+Mrel  (average)
+Mcal  (image)
+Mmos  (mosaic)
+Mxy   (Xi, Yi) (Xi,Yi per image ??)
+Mgrid (Xm, Ym)
+
+
+ measure -> average           catalog[i].measure[j].averef 
+ average -> measure           catalog[i].average[j].offset, catalog[i].average[j].Nm
+
+ measure -> image             image:  image[cat, meas]
+ image   -> measure           image:  catlist[image], measlist[image]
+
+ measure -> mosaic            mosaic: mosaic[cat, meas]
+ mosaic  -> measure	      mosaic: catlist[mosaic], measlist[mosaic]
+
+ measure -> gridbin           grid:   bin[cat, meas]
+ gridbin -> measure           grid:   catlist[bin], measlist[bin]
+
+ measure -> Xi, Yi -> Mxy     
+
+
+getMmos:
+
+ for (i = 0; i < Nmosaic; i++) {
+
+   for (j = 0; j < Nmeasure[i]; j++) {
+      cat  = catlist[i][j]
+      meas = measlist[i][j]
+
+      M = catalog[cat].measure[meas].M
+      Mcal  = applyMcal  (cat, meas)
+      Mrel  = applyMrel  (cat, meas)
+      Mxy   = applyMxy   (cat, meas)
+      Mgrid = applyMgrid (cat, meas)
+
+      list[n] = M - Mcal - Mrel - Mxy - Mgrid
+    }
+  }
+
+
+
+excluding data points:
+
+star:   N < 2
+image:  
+
+
+--
+
+MAG_LIM      : keep stars brighter than MAG_LIM (Mcat)
+SIGMA_LIM    : keep stars with error < SIGMA_LIM
+STAR_SCATTER : exclude stars with dM > STAR_SCATTER
+IMAGE_SCATTER, NIMAGE_SCATTER : define image scatter limits
+
+
+
+load_images - exclude images 
+
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/include/relphot.h
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/include/relphot.h	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/include/relphot.h	(revision 17422)
@@ -0,0 +1,216 @@
+# include <ohana.h>
+# include <dvo.h>
+# include <kapa.h>
+# include <signal.h>
+
+/* # define GRID_V1 */
+# define GRID_V2
+# define NO_IMAGE -100
+
+# if (0)
+typedef struct {
+  double xmin, xmax, ymin, ymax;
+  int style, ptype, ltype, etype, color;
+  double lweight, size;
+} Graphdata;
+# endif
+
+typedef struct {
+  unsigned int start;
+  unsigned int stop;
+  float Mcal;
+  float dMcal;
+  short Xm;
+  float secz;
+  char code;
+  Coords coords;
+} Mosaic; 
+
+typedef struct {
+  double median;
+  double mean;
+  double sigma;
+  double error;
+  double chisq;
+  double min;
+  double max;
+  double total;
+  int    Nmeas;
+} StatType;
+
+/* global variables set in parameter file */
+char   ImageCat[256];
+char   ImageTemplate[256];
+char   CatTemplate[256];
+char   GSCFILE[256];
+char   CATDIR[256];
+char   CATMODE[16];    /* raw, mef, split, mysql */
+char   CATFORMAT[16];  /* internal, elixir, loneos, panstarrs */
+char   CameraConfig[256];
+char   SKY_TABLE[256];
+int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+double MAG_LIM;
+double SIGMA_LIM;
+double IMAGE_SCATTER;
+double IMAGE_OFFSET;
+double STAR_SCATTER;
+double STAR_CHISQ;
+double MIN_ERROR;
+
+int    VERBOSE;
+
+int    NLOOP;
+int    RESET;
+int    UPDATE;
+int    PLOTSTUFF;
+int    SAVEPLOT;
+int    SHOW_PARAMS;
+char   MOSAICNAME[256];
+char   STATMODE[32];
+int    STAR_BAD;
+int    MEAS_BAD;
+int    STAR_TOOFEW;
+int    IMAGE_TOOFEW;
+double IMAGE_GOOD_FRACTION;
+int    IMAGE_BAD;
+int    FREEZE_IMAGES;
+int    USE_GRID;
+int    PLOTDELAY;
+int    UpdateAverages;
+
+int    RELPHOT_GRID_X;
+int    RELPHOT_GRID_Y;
+int    RELPHOT_GRID_BINNING;
+
+PhotCode      *photcode;
+int            PhotNsec;
+int            PhotSec;
+
+int AreaSelect;
+double AreaXmin, AreaXmax, AreaYmin, AreaYmax;
+
+int ImagSelect, ImagMin, ImagMax;
+
+int DophotSelect, DophotValue;
+
+double  PlotMmin, PlotMmax, PlotdMmin, PlotdMmax;
+enum {black, white, red, orange, yellow, green, blue, indigo, violet};
+
+int TimeSelect;
+time_t TSTART, TSTOP;
+
+SkyRegion UserPatch;
+int UserPatchSelect;
+
+# ifdef GRID_V1
+int setGridMeasure (int meas, int cat, double X, double Y);
+# endif
+
+# ifdef GRID_V2
+int setGridMeasure (int meas, int cat, double X, double Y, int ccdnum);
+# endif
+
+/*** relphot prototypes ***/
+void          ConfigInit          PROTO((int *argc, char **argv));
+void          GetConfig           PROTO((char *config, char *field, char *format, int N, void *ptr));
+char         *GetPhotnamebyCode   PROTO((PhotCodeData *photcodes, int code));
+void          InterpolateGrid     PROTO((float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords));
+int          *SelectRefMosaic     PROTO((Mosaic **refmosaic, int *Nimage));
+int           args                PROTO((int argc, char **argv));
+int           bcatalog            PROTO((Catalog *subcatalog, Catalog *catalog));
+void          clean_images        PROTO(());
+void          clean_measures      PROTO((Catalog *catalog, int Ncatalog, int final));
+void          clean_mosaics       PROTO(());
+void          clean_stars         PROTO((Catalog *catalog, int Ncatalog));
+int           corner_check        PROTO((double *x1, double *y1, double *x2, double *y2));
+void          dumpGrid            PROTO(());
+void          dump_grid           PROTO(()); 
+int           edge_check          PROTO((double *x1, double *y1, double *x2, double *y2));
+void          findImages          PROTO((Catalog *catalog, int Ncatalog));
+int           findMosaics         PROTO((Catalog *catalog, int Ncatalog));
+
+void set_db (FITS_DB *in);
+int Shutdown (char *format, ...);
+void TrapSignal (int sig);
+void SetProtect (int mode);
+int SetSignals ();
+
+void          freeGridBins        PROTO((int Ncatalog));
+void          freeImageBins       PROTO((int Ncatalog));
+void          freeMosaicBins      PROTO((int Ncatalog));
+void          free_catalogs       PROTO((Catalog *catalog, int Ncatalog));
+int           gcatalog            PROTO((Catalog *catalog, int FINAL));
+Coords       *getCoords           PROTO((int meas, int cat));
+int           getImageEntry       PROTO((int meas, int cat));
+float         getMcal             PROTO((int meas, int cat));
+float         getMgrid            PROTO((int meas, int cat));
+float         getMmos             PROTO((int meas, int cat));
+float         getMrel             PROTO((Catalog *catalog, int meas, int cat));
+Image        *getimage            PROTO((int N));
+Image        *getimages           PROTO((int *N));
+void          global_stats        PROTO((Catalog *catalog, int Ncatalog));
+void          initGrid            PROTO((int dX, int dY));
+void          initGridBins        PROTO((Catalog *catalog, int Ncatalog));
+void          initImageBins       PROTO((Catalog *catalog, int Ncatalog));
+void          initImages          PROTO((Image *input, int N));
+void          initMosaicBins      PROTO((Catalog *catalog, int Ncatalog));
+void          initMosaicGrid      PROTO((Image *image, int Nimage));
+void          initMosaics         PROTO((Image *image, int Nimage));
+void          initMrel            PROTO((Catalog *catalog, int Ncatalog));
+void          initialize          PROTO((int argc, char **argv));
+void          initstats           PROTO((char *mode));
+int           liststats           PROTO((double *value, double *dvalue, int N, StatType *stats));
+Catalog       *load_catalogs      PROTO((SkyList *skylist, int *Ncatalog));
+SkyList      *load_images         PROTO((FITS_DB *db, char *regionName, SkyRegion *region, int RegionSelect));
+Image         *select_images      PROTO((SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage));
+
+int           main                PROTO((int argc, char **argv));
+void          mark_images         PROTO((Image *image, int Nimage, Image *timage, int Ntimage));
+void          matchImage          PROTO((Catalog *catalog, int meas, int cat));
+void          matchMosaics        PROTO((Catalog *catalog, int meas, int cat));
+double        opening_angle       PROTO((double x1, double y1, double x2, double y2, double x3, double y3));
+void          plot_chisq          PROTO((Catalog *catalog, int Ncatalog));
+void          plot_defaults       PROTO((Graphdata *graphdata));
+void          plot_grid           PROTO((Catalog *catalog));
+void          plot_images         PROTO(());
+void          plot_list           PROTO((Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file));
+void          plot_mosaic_fields  PROTO((Catalog *catalog));
+void          plot_mosaics        PROTO(());
+void          plot_scatter        PROTO((Catalog *catalog, int Ncatalog));
+void          plot_star_coords    PROTO((Catalog *catalog, int Ncatalog));
+void          plot_stars          PROTO((Catalog *catalog, int Ncatalog));
+void          reload_catalogs     PROTO((SkyList *skylist));
+int           reload_images       PROTO((FITS_DB *db));
+int           setExclusions       PROTO((Catalog *catalog, int Ncatalog));
+void          setMcal             PROTO((Catalog *catalog, int Poor));
+void          setMcalFinal        PROTO(());
+int           setMcalOutput       PROTO((Catalog *catalog, int Ncatalog));
+void          setMgrid            PROTO((Catalog *catalog));
+int           setMmos             PROTO((Catalog *catalog, int Poor));
+int           setMrel             PROTO((Catalog *catalog, int Ncatalog));
+void          setMrelFinal        PROTO((Catalog *catalog));
+int           setMrelOutput       PROTO((Catalog *catalog, int Ncatalog, int mark));
+int           setMave             PROTO((Catalog *catalog, int Ncatalog));
+void          set_ZP              PROTO((double ZERO));
+int           setrefcode          PROTO((Image *image, int Nimage)); 
+void          skip_measurements   PROTO((Catalog *catalog, int pass));
+void          sortA               PROTO((double *X, int N));
+void          sortB               PROTO((double *X, double *Y, int N));
+void          sortC               PROTO((double *X, double *Y, double *F1, double *F2, int N));
+void          sortD               PROTO((double *X, double *Y, double *Z, int N));
+StatType      statsImageM         PROTO((Catalog *catalog));
+StatType      statsImageN         PROTO((Catalog *catalog));
+StatType      statsImageX         PROTO((Catalog *catalog));
+StatType      statsImagedM        PROTO((Catalog *catalog));
+StatType      statsMosaicM        PROTO((Catalog *catalog));
+StatType      statsMosaicN        PROTO((Catalog *catalog));
+StatType      statsMosaicX        PROTO((Catalog *catalog));
+StatType      statsMosaicdM       PROTO((Catalog *catalog));
+StatType      statsStarN          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarS          PROTO((Catalog *catalog, int Ncatalog));
+StatType      statsStarX          PROTO((Catalog *catalog, int Ncatalog));
+void          wcatalog            PROTO((Catalog *catalog));
+void          wimages             PROTO(());
+void          write_coords        PROTO((Header *header, Coords *coords));
+int relphot_objects ();
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/ConfigInit.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/ConfigInit.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/ConfigInit.c	(revision 17422)
@@ -0,0 +1,79 @@
+# include "relphot.h"
+
+void ConfigInit (int *argc, char **argv) {
+
+  double ZERO_POINT;
+  char  *config, *file;
+  char CatdirPhotcodeFile[256];
+  char MasterPhotcodeFile[256];
+
+  /*** load configuration info ***/
+  file = SelectConfigFile (argc, argv, "ptolemy");
+  config = LoadConfigFile (file);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find configuration file %s\n", file);
+    if (file != (char *) NULL) free (file);
+    exit (0);
+  }
+  if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
+
+  GetConfig (config, "MAG_LIM",                "%lf", 0, &MAG_LIM);
+  GetConfig (config, "SIGMA_LIM",              "%lf", 0, &SIGMA_LIM);
+  GetConfig (config, "STAR_SCATTER",           "%lf", 0, &STAR_SCATTER);
+  GetConfig (config, "IMAGE_SCATTER",          "%lf", 0, &IMAGE_SCATTER);
+  GetConfig (config, "IMAGE_OFFSET",           "%lf", 0, &IMAGE_OFFSET);
+
+  GetConfig (config, "STAR_CHISQ",             "%lf", 0, &STAR_CHISQ);
+  GetConfig (config, "STAR_TOOFEW",            "%d",  0, &STAR_TOOFEW);
+  GetConfig (config, "IMAGE_TOOFEW",           "%d",  0, &IMAGE_TOOFEW);
+  GetConfig (config, "IMAGE_GOOD_FRACTION",    "%lf", 0, &IMAGE_GOOD_FRACTION);
+
+  GetConfig (config, "GSCFILE",                "%s",  0, GSCFILE);
+  GetConfig (config, "CATDIR",                 "%s",  0, CATDIR);
+  ScanConfig (config, "CATMODE",                "%s",  0, CATMODE);
+  ScanConfig (config, "CATFORMAT",              "%s",  0, CATFORMAT);
+  ScanConfig (config, "PHOTCODE_FILE",         	"%s",  0, MasterPhotcodeFile);
+
+  sprintf (ImageCat, "%s/Images.dat", CATDIR);
+
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
+    SKY_DEPTH = 2;
+  }
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
+    SKY_TABLE[0] = 0;
+  }
+
+  GetConfig (config, "ZERO_PT",                "%lf", 0, &ZERO_POINT);
+
+  GetConfig (config, "RELPHOT_GRID_X",         "%d",  0, &RELPHOT_GRID_X);
+  GetConfig (config, "RELPHOT_GRID_Y",         "%d",  0, &RELPHOT_GRID_Y);
+  GetConfig (config, "RELPHOT_GRID_BINNING",   "%d",  0, &RELPHOT_GRID_BINNING);
+  GetConfig (config, "CAMERA_CONFIG",          "%s",  0, CameraConfig);
+
+  if (*CATMODE == 0) strcpy (CATMODE, "RAW");
+  if (*CATFORMAT == 0) strcpy (CATFORMAT, "ELIXIR");
+
+  /* XXX this does not yet write out the master photcode table */
+  sprintf (CatdirPhotcodeFile, "%s/Photcodes.dat", CATDIR);
+  if (!LoadPhotcodes (CatdirPhotcodeFile, MasterPhotcodeFile)) {
+    fprintf (stderr, "error loading photcode table %s or master file %s\n", CatdirPhotcodeFile, MasterPhotcodeFile);
+    exit (1);
+  }
+  SetZeroPoint (ZERO_POINT);
+
+  free (config);
+  free (file);
+
+}
+
+void GetConfig (char *config, char *field, char *format, int N, void *ptr) {
+
+  char *status;
+
+  status = ScanConfig (config, field, format, N, ptr);
+  if (status == NULL) {
+    fprintf (stderr, "error in config, cannot find %s\n", field);
+    exit (1);
+  }
+  return;
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/GridOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/GridOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/GridOps.c	(revision 17422)
@@ -0,0 +1,583 @@
+# include "relphot.h"
+
+static int     Ngrid;
+static float   *gridM;
+static float   *gridS;
+static int     *gridN;
+static int      gridX;
+static int      gridY;
+
+static int    **bin;
+static int    **Xmeas;
+static int    **Ymeas;
+
+static int    **clist;
+static int    **mlist;
+static int     *Nlist;
+static int     *NLIST;
+
+static struct {
+  int Nchip;
+  int Mx, My;  /* mosaic size in chips */
+  int Nx, Ny;  /* chip size in pixels */
+  int *Fx, *Fy;  /* chip flip */
+  int *Ox, *Oy;  /* chip offset */
+  char **ccdname;
+} camera;    
+
+# ifdef GRID_V1
+void initGrid (int dX, int dY) {
+
+  int i;
+
+  /* define mosaic 2d correction grid */
+  gridX = dX / RELPHOT_GRID_BINNING + 1;
+  gridY = dY / RELPHOT_GRID_BINNING + 1;
+  Ngrid = gridX * gridY;
+
+  ALLOCATE (gridM, float, Ngrid);
+  ALLOCATE (gridS, float, Ngrid);
+  ALLOCATE (gridN, int,   Ngrid);
+  bzero (gridM, Ngrid*sizeof(float));
+  bzero (gridS, Ngrid*sizeof(float));
+  bzero (gridN, Ngrid*sizeof(int));
+
+}
+# endif
+
+# ifdef GRID_V2
+void initGrid (int dX, int dY) {
+
+  int i;
+  char field[64], *config, line[256];
+
+  /* load camera config file */
+  config = LoadConfigFile (CameraConfig);
+  if (config == (char *) NULL) {
+    fprintf (stderr, "ERROR: can't find camera config file %s\n", CameraConfig);
+    exit (1);
+  }
+
+  /* load basic mosaic parameters */
+  ScanConfig (config, "NCCD", "%d", 1, &camera.Nchip);
+  ScanConfig (config, "MOSAIC_X", "%d", 1, &camera.Mx);
+  ScanConfig (config, "MOSAIC_Y", "%d", 1, &camera.My);
+  ScanConfig (config, "NAXIS1", "%d", 1, &camera.Nx);
+  ScanConfig (config, "NAXIS2", "%d", 1, &camera.Ny);
+
+  ALLOCATE (camera.Fx, int, camera.Nchip);
+  ALLOCATE (camera.Fy, int, camera.Nchip);
+  ALLOCATE (camera.Ox, int, camera.Nchip);
+  ALLOCATE (camera.Oy, int, camera.Nchip);
+  ALLOCATE (camera.ccdname, char *, camera.Nchip);
+
+  /* load per-chip parameters */
+  for (i = 0; i < camera.Nchip; i++) {
+    ALLOCATE (camera.ccdname[i], char, 256);
+    sprintf (field, "CCD.%d", i);
+    ScanConfig (config, field, "%s", 1, line);
+    sscanf (line, "%s %d %d %d %d", camera.ccdname[i], &camera.Ox[i], &camera.Oy[i], &camera.Fx[i], &camera.Fy[i]);
+  }
+  free (config);
+
+  /* define mosaic 2d correction grid: 
+   * GRID_X is the number of grid pixels per chip in the X direction */
+  gridX = RELPHOT_GRID_X * camera.Mx;
+  gridY = RELPHOT_GRID_Y * camera.My;
+  Ngrid = gridX * gridY;
+
+  ALLOCATE (gridM, float, Ngrid);
+  ALLOCATE (gridS, float, Ngrid);
+  ALLOCATE (gridN, int,   Ngrid);
+  bzero (gridM, Ngrid*sizeof(float));
+  bzero (gridS, Ngrid*sizeof(float));
+  bzero (gridN, Ngrid*sizeof(int));
+
+}
+# endif
+
+void initGridBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  if (!USE_GRID) return;
+
+  /* define cat,meas -> grid pointers */
+  ALLOCATE (bin, int *, Ncatalog);
+  ALLOCATE (Xmeas, int *, Ncatalog);
+  ALLOCATE (Ymeas, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    ALLOCATE (Xmeas[i], int, MAX (catalog[i].Nmeasure, 1));
+    ALLOCATE (Ymeas[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  /* define grid -> cat,meas pointers */
+  ALLOCATE (Nlist, int, Ngrid);
+  ALLOCATE (NLIST, int, Ngrid);
+  ALLOCATE (clist, int *, Ngrid);
+  ALLOCATE (mlist, int *, Ngrid);
+
+  for (i = 0; i < Ngrid; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeGridBins (int Ncatalog) {
+
+  int i;
+
+  if (!USE_GRID) return;
+
+  /* define cat,meas -> grid pointers */
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+    free (Xmeas[i]);
+    free (Ymeas[i]);
+  }
+  free (bin);
+  free (Xmeas);
+  free (Ymeas);
+
+  /* define grid -> cat,meas pointers */
+  for (i = 0; i < Ngrid; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (Nlist);
+  free (NLIST);
+  free (clist);
+  free (mlist);
+}
+
+# ifdef GRID_V1
+int setGridMeasure (int meas, int cat, double X, double Y) {
+
+  int ix, iy, i;
+
+  ix = X / RELPHOT_GRID_BINNING;
+  iy = Y / RELPHOT_GRID_BINNING;
+  if (ix < 0) goto escape;
+  if (iy < 0) goto escape;
+  if (ix >= gridX) goto escape;
+  if (iy >= gridY) goto escape;
+
+  i = ix + iy*gridX;
+
+  bin[cat][meas] = i;
+  Xmeas[cat][meas] = X;
+  Ymeas[cat][meas] = Y;
+  clist[i][Nlist[i]] = cat;
+  mlist[i][Nlist[i]] = meas;
+
+  Nlist[i] ++;
+  if (Nlist[i] == NLIST[i]) {
+    NLIST[i] += 100;
+    REALLOCATE (clist[i], int, NLIST[i]);
+    REALLOCATE (mlist[i], int, NLIST[i]);
+  }	
+  return (TRUE);
+
+escape:
+  fprintf (stderr, "error: star out of grid\n");
+  exit (1);
+}
+# endif
+
+# ifdef GRID_V2
+int setGridMeasure (int meas, int cat, double X, double Y, int ccdnum) {
+
+  int ix, iy, Cx, Cy, i;
+  double x, y;
+
+  /* X, Y are chip coords on chip ccdnum */
+
+  /* normalize X & Y */
+  x = X;
+  if (camera.Fx[ccdnum]) x = camera.Nx - X;
+  y = Y;
+  if (camera.Fy[ccdnum]) y = camera.Ny - Y;
+
+  /* grid coords on the chip */
+  Cx = MIN (MAX ((x / camera.Nx) * RELPHOT_GRID_X, 0), RELPHOT_GRID_X - 1);
+  Cy = MIN (MAX ((y / camera.Ny) * RELPHOT_GRID_Y, 0), RELPHOT_GRID_Y - 1);
+  
+  /* coordinates in the grid */
+  ix = Cx + camera.Ox[ccdnum]*RELPHOT_GRID_X;
+  iy = Cy + camera.Oy[ccdnum]*RELPHOT_GRID_Y;
+
+  i = ix + iy*gridX;
+
+  bin[cat][meas] = i;
+  Xmeas[cat][meas] = x + camera.Ox[ccdnum]*camera.Nx;
+  Ymeas[cat][meas] = Y + camera.Oy[ccdnum]*camera.Ny;
+  clist[i][Nlist[i]] = cat;
+  mlist[i][Nlist[i]] = meas;
+
+  Nlist[i] ++;
+  if (Nlist[i] == NLIST[i]) {
+    NLIST[i] += 100;
+    REALLOCATE (clist[i], int, NLIST[i]);
+    REALLOCATE (mlist[i], int, NLIST[i]);
+  }	
+  return (TRUE);
+
+  fprintf (stderr, "error: star out of grid\n");
+  exit (1);
+}
+# endif
+
+float getMgrid (int meas, int cat) {
+
+  int i;
+  float value;
+
+  if (!USE_GRID) return (0);
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  value = gridM[i];
+  return (value);
+}
+
+/* determine Mgrid values for all grid bins */
+void setMgrid (Catalog *catalog) {
+
+  int i, j, m, c, n, N, Nmax;
+  double *list, *dlist;
+  float Msys, Mrel, Mcal, Mmos;
+  StatType stats;
+  
+  if (!USE_GRID) return;
+
+  Nmax = Nlist[0];
+  for (i = 0; i < Ngrid; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+
+  for (i = 0; i < Ngrid; i++) {
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mcal = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N] = Msys - Mrel - Mcal - Mmos;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      N++;
+    }
+
+    liststats (list, dlist, N, &stats);
+    gridM[i] = stats.mean;
+    gridS[i] = stats.sigma;
+    gridN[i] = N;
+  }
+  free (list);
+  free (dlist);
+}
+
+void plot_grid (Catalog *catalog) {
+
+  int i, j, m, c, n, N, Narea;
+  float Msys, Mrel, Mcal, Mmos;
+  double *xlist, *Mlist, *dlist, *ylist;
+  Graphdata graphdata;
+
+  if (!USE_GRID) return;
+
+  N = 0;
+  for (i = 0; i < Ngrid; i++) 
+    N += Nlist[i];
+
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+  ALLOCATE (Mlist, double, N);
+  ALLOCATE (dlist, double, N);
+
+  Narea = 0;
+  N = 0;
+  for (i = 0; i < Ngrid; i++) {
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) {
+	Narea ++;
+	continue;
+      }
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+
+      xlist[N] = Xmeas[c][m];
+      ylist[N] = Ymeas[c][m];
+      Mlist[N] = Msys - Mrel - Mcal - Mmos;
+      dlist[N] = Msys - Mrel - Mcal - Mmos - gridM[i];
+      N++;
+    }
+  }
+
+  fprintf (stderr, "skipped %d meas for area\n", Narea);
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, N, "X vs dM raw", "XdM.png");
+  plot_list (&graphdata, xlist, dlist, N, "X vs dM corrected", "XdMf.png");
+  plot_list (&graphdata, ylist, dlist, N, "Y vs dM corrected", "YdMf.png");
+
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, xlist, ylist, N, "X vs Y", "XY.png");
+
+  free (ylist);
+  free (xlist);
+  free (Mlist);
+  free (dlist);
+
+}
+
+void dump_grid () { 
+
+  int i, j, Nimage;
+  int *imlist;
+  FILE *f;
+  Header header, theader;
+  Matrix matrix;
+  Mosaic *refmosaic;
+    
+  /* select reference mosaic image */
+  imlist = SelectRefMosaic (&refmosaic, &Nimage);
+
+  /* we are writing to this file */
+  f = fopen ("mosaic.fits", "w");
+  if (f == (FILE *) NULL) { 
+    fprintf (stderr, "cannot open %s for output\n", "mosaic.fits");
+    return;
+  }
+
+  /* create empty phu */
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+  gfits_modify (&header, "NEXTEND", "%d", 1, Nimage + 3);
+  gfits_modify (&header, "FILTER", "%s", 1, photcode[0].name);
+  gfits_modify (&header, "COMMENT", "%S", 1, "Mosaic Photometry Grid Analysis");
+  gfits_fwrite_header (f, &header);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid mag values */
+  gfits_init_header (&theader);
+  theader.Naxes = 2;
+  theader.Naxis[0] = gridX;
+  theader.Naxis[1] = gridY;
+  theader.bitpix   = -32;
+  gfits_create_Theader (&theader, "IMAGE");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "MAG_OFFSET");
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridM[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid Nmeas values */
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "NMEAS");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridN[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+  /* save grid sigma values */
+  gfits_modify (&theader, "EXTNAME", "%s", 1, "SIGMA");
+  gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+  gfits_create_matrix  (&theader, &matrix);
+  for (i = 0; i < gridX; i++) {
+    for (j = 0; j < gridY; j++) {
+      gfits_set_matrix_value (&matrix, i, j, (double) gridS[i + j*gridX]);
+    }
+  }
+  write_coords (&theader, &refmosaic[0].coords);
+  gfits_fwrite_header (f, &theader);
+  gfits_fwrite_matrix (f, &matrix);
+  gfits_free_matrix (&matrix);
+
+# ifdef GRID_V1
+  /* calculate pixel values for each CCD pixel, write out CCD images */
+  /* grid pixels are in RA,DEC coords, transform to image and interpolate */
+  for (i = 0; i < Nimage; i++) {
+    image = getimage (imlist[i]);
+    pname = GetPhotcodeNamebyCode (image[0].photcode);
+
+    /* this is kind of bogus... */
+    /* pname is CAMERA.FILTER.CCD, grab the CCD */
+    p = strrchr (pname, '.');
+    if (p == (char *) NULL) {
+      fprintf (stderr, "error parsing photcode %s\n", pname);
+      exit (2);
+    }
+    p++;
+    sprintf (ccdname, "ccd%s", p);
+
+    gfits_modify (&theader, "EXTNAME", "%s", 1, ccdname);
+    gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+    gfits_modify (&theader, "PHOTCODE", "%s", 1, pname);
+    gfits_modify (&theader, "NX", "%d", 1, image[i].NX);
+    gfits_modify (&theader, "NY", "%d", 1, image[i].NY);
+    write_coords (&theader, &image[0].coords);
+
+    Nx = 2 * image[i].NX / RELPHOT_GRID_BINNING;
+    Ny = 2 * image[i].NY / RELPHOT_GRID_BINNING;
+    theader.Naxis[0] = Nx;
+    theader.Naxis[1] = Ny;
+    gfits_modify (&theader, "NAXIS1", "%d", 1, Nx);
+    gfits_modify (&theader, "NAXIS2", "%d", 1, Ny);
+    gfits_create_matrix  (&theader, &matrix);
+
+    InterpolateGrid ((float *)matrix.buffer, Nx, Ny, &image[0].coords, &refmosaic[0].coords);
+    gfits_fwrite_header (f, &theader);
+    gfits_fwrite_matrix (f, &matrix);
+    gfits_free_matrix (&matrix);
+  }
+# endif
+
+# ifdef GRID_V2
+  /* calculate value for each CCD pixel, write out CCD images */
+  /* grid pixels are tied to detector pixels, but are flipped to match focal plane */
+  for (i = 0; i < camera.Nchip; i++) {
+    int ix, iy, x, y, X, Y, bin;
+
+    gfits_modify (&theader, "EXTNAME", "%s", 1, camera.ccdname[i]);
+    gfits_modify (&theader, "FILTER", "%s", 1, photcode[0].name);
+    gfits_modify (&theader, "NX", "%d", 1, camera.Nx);
+    gfits_modify (&theader, "NY", "%d", 1, camera.Ny);
+      
+    theader.Naxis[0] = RELPHOT_GRID_X;
+    theader.Naxis[1] = RELPHOT_GRID_Y;
+    gfits_modify (&theader, "NAXIS1", "%d", 1, RELPHOT_GRID_X);
+    gfits_modify (&theader, "NAXIS2", "%d", 1, RELPHOT_GRID_Y);
+    gfits_create_matrix  (&theader, &matrix);
+
+    for (Y = 0; Y < RELPHOT_GRID_Y; Y++) {
+      for (X = 0; X < RELPHOT_GRID_X; X++) {
+	      
+	/* normalize X & Y */
+	x = X;
+	if (camera.Fx[i]) x = RELPHOT_GRID_X - X - 1;
+	y = Y;
+	if (camera.Fy[i]) y = RELPHOT_GRID_Y - Y - 1;
+	      
+	/* coordinates in the grid */
+	ix = x + camera.Ox[i]*RELPHOT_GRID_X;
+	iy = y + camera.Oy[i]*RELPHOT_GRID_Y;
+	      
+	bin = ix + iy*gridX;
+	gfits_set_matrix_value (&matrix, X, Y, (double) gridM[bin]);
+      }
+    }
+    gfits_fwrite_header (f, &theader);
+    gfits_fwrite_matrix (f, &matrix);
+    gfits_free_matrix (&matrix);
+  }
+# endif
+
+}
+
+void InterpolateGrid (float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords) {
+
+  int i, j;
+  double x, y, r, d, X, Y, dx, dy;
+  double V00, V01, V10, V11;
+  double wV00, wV01, wV10, wV11;
+  double dV00, dV01, dV10, dV11;
+  double v1, v2, value;
+  int ix, iy, N;
+
+  for (i = 0; i < Nx; i++) {
+    for (j = 0; j < Ny; j++) {
+      x = i * RELPHOT_GRID_BINNING / 2;
+      y = j * RELPHOT_GRID_BINNING / 2;
+      XY_to_RD (&r, &d, x, y, ccd);
+      RD_to_XY (&X, &Y, r, d, gcoords);
+
+      X = X / RELPHOT_GRID_BINNING;
+      Y = Y / RELPHOT_GRID_BINNING;
+
+      ix = (int) X;
+      dx = X - ix;
+      iy = (int) Y;
+      dy = Y - iy;
+
+      if (ix < 0) continue;
+      if (iy < 0) continue;
+      if (ix >= gridX) continue;
+      if (iy >= gridY) continue;
+
+      N = ix + iy*gridX;
+      V00 = gridM[N];
+      V10 = gridM[N + 1];
+      V01 = gridM[N + gridX];
+      V11 = gridM[N + gridX + 1];
+
+      dV00 = gridS[N];
+      dV10 = gridS[N + 1];
+      dV01 = gridS[N + gridX];
+      dV11 = gridS[N + gridX + 1];
+
+      wV00 = (dV00 == 0) ? 0.0 : 1 / SQ(dV00);
+      wV01 = (dV01 == 0) ? 0.0 : 1 / SQ(dV01);
+      wV10 = (dV10 == 0) ? 0.0 : 1 / SQ(dV10);
+      wV11 = (dV11 == 0) ? 0.0 : 1 / SQ(dV11);
+
+      v1 = wV00*V00*(1 + dx*dy - dx - dy) +
+	wV10*V10*(dx - dx*dy) +
+	wV01*V01*(dy - dx*dy) +
+	wV11*V11*(dx*dy);
+
+      v2 = wV00*(1 + dx*dy - dx - dy) +
+	wV10*(dx - dx*dy) +
+	wV01*(dy - dx*dy) +
+	wV11*(dx*dy);
+
+      value = v1 / v2;
+      buffer[j*Nx + i] = value;
+    }
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/ImageOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/ImageOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/ImageOps.c	(revision 17422)
@@ -0,0 +1,480 @@
+# include "relphot.h"
+
+static unsigned int *start;
+static unsigned int *stop;
+static int         **bin;
+
+static int         **clist;
+static int         **mlist;
+static int          *Nlist;
+static int          *NLIST;
+
+static Image        *image;
+static int          Nimage;
+
+void initImages (Image *input, int N) {
+
+  int i;
+
+  image = input;
+  Nimage = N;
+
+  ALLOCATE (start,   unsigned, Nimage);
+  ALLOCATE (stop,    unsigned, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    start[i] = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop[i]  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+  }
+}
+
+void initImageBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  ALLOCATE (Nlist, int, Nimage);
+  ALLOCATE (NLIST, int, Nimage);
+  ALLOCATE (clist, int *, Nimage);
+  ALLOCATE (mlist, int *, Nimage);
+
+  for (i = 0; i < Nimage; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeImageBins (int Ncatalog) {
+
+  int i;
+
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+  for (i = 0; i < Nimage; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (clist);
+  free (mlist);
+}
+
+/* select all image equivalent to the current photcode */
+void findImages (Catalog *catalog, int Ncatalog) {
+
+  int i, j, ecode;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[j].photcode);
+      if (photcode[0].code != ecode) continue;
+      matchImage (catalog, j, i);
+    }
+  }
+}
+
+/* XXX convert this to use the values of measure->Xccd,Yccd */
+/* XXX need to apply the conversion for ELIXIR and LONEOS formats */
+void matchImage (Catalog *catalog, int meas, int cat) {
+
+  int i, ave, ccdnum;
+  double ra, dec, X, Y;
+  char *pname, *filter, *p, base[256];
+  Measure *measure;
+  
+  measure = &catalog[cat].measure[meas];
+  for (i = 0; i < Nimage; i++) {
+    if (image[0].photcode == -1) continue;
+    if (measure[0].photcode != image[i].photcode) continue;
+    if (measure[0].t < start[i]) continue;
+    if (measure[0].t > stop[i]) continue;
+    
+# ifdef GRID_V2 /* this section is added to support GridOps.v2.c */
+    if (USE_GRID) {
+
+      /* identify the ccd on the basis of the photcode name */
+      pname = GetPhotcodeNamebyCode (image[i].photcode);
+      filter = photcode[0].name;
+      sprintf (base, "%s.%s.", MOSAICNAME, filter);
+      if (strncmp (pname, base, strlen (base))) continue;
+      p = pname + strlen(base);
+      if (*p == 0) continue;
+      ccdnum = atoi (p);
+      /* some ambiguity here between seq number and id number */
+
+      /* add this measurement to the grid cell for this chip */
+      ave = measure[0].averef;
+      ra  = catalog[cat].average[ave].R - measure[0].dR / 3600.0;
+      dec = catalog[cat].average[ave].D - measure[0].dD / 3600.0;
+       
+      /* X,Y always positive-definite in range 0,0 - dX, dY */
+      RD_to_XY (&X, &Y, ra, dec, &image[i].coords);
+      setGridMeasure (meas, cat, X, Y, ccdnum);
+    }
+# endif
+
+    bin[cat][meas] = i;
+
+    clist[i][Nlist[i]] = cat;
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+  /*  fprintf (stderr, "can't find source image for this measurement: %d (%d)\n", measure[0].t, measure[0].photcode); */
+}
+
+int getImageEntry (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  return (i);
+}
+
+float getMcal (int meas, int cat) {
+
+  int i;
+  float value;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  if (image[i].code & IMAGE_BAD) return (NAN);  
+  value = image[i].Mcal;
+  return (value);
+}
+
+Coords *getCoords (int meas, int cat) {
+
+  int i;
+
+  i = bin[cat][meas];
+  if (i == -1) return (NULL);
+  return (&image[i].coords);
+}
+
+/* determine Mcal values for all images */
+void setMcal (Catalog *catalog, int PoorImages) {
+
+  int i, j, m, c, n, N, Nmax, mark, bad;
+  float Msys, Mrel, Mmos, Mgrid;
+  double *list, *dlist;
+  StatType stats;
+
+  if (FREEZE_IMAGES) return;
+
+  if (PoorImages) {
+    IMAGE_BAD = STAR_BAD = MEAS_BAD = 0;
+  }
+
+  Nmax = 0;
+  for (i = 0; i < Nimage; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+
+  for (i = 0; i < Nimage; i++) {
+    
+    /* on PoorImages run, skip good images */
+    if (PoorImages) {
+      bad = image[i].code & (ID_IMAGE_FEW | ID_IMAGE_POOR | ID_IMAGE_SKIP);
+      if (!bad) continue;
+    }      
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N] = Msys - Mrel - Mmos - Mgrid;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      N++;
+    }
+    /* Nlist[i] is all measurements, N is good measurements */
+
+    /* too few good measurements or too many bad measurements */
+    if (!PoorImages) {
+      mark = (N < IMAGE_TOOFEW) || (N < IMAGE_GOOD_FRACTION*Nlist[i]);
+      if (mark) {
+	image[i].code |= ID_IMAGE_FEW;
+      } else {
+	image[i].code &= ~ID_IMAGE_FEW;
+      }      
+    }
+    
+    liststats (list, dlist, N, &stats);
+    image[i].Mcal  = stats.mean;
+    image[i].dMcal = stats.sigma;
+    image[i].Xm    = 100.0*log10(stats.chisq);
+  }
+  free (list);
+  free (dlist);
+  if (PoorImages) {
+    IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+    STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+    MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+  }
+  return;
+}
+
+/* mark image if: abs(Mcal) too large, dMcal too large */
+void clean_images () {
+
+  int i, N, mark, Nmark;
+  double *mlist, *slist, *dlist;
+  double MaxOffset, MaxScatter, MedOffset;
+  StatType stats;
+
+  if (FREEZE_IMAGES) return;
+
+  if (VERBOSE) fprintf (stderr, "marking poor images\n");
+
+  ALLOCATE (mlist, double, Nimage);
+  ALLOCATE (slist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  for (i = N = 0; i < Nimage; i++) {
+    if (image[i].code & IMAGE_BAD) continue;
+    mlist[N] = fabs (image[i].Mcal);
+    slist[N] = image[i].dMcal;
+    dlist[N] = 1;
+    N++;
+  }
+  initstats ("MEAN");
+  liststats (mlist, dlist, N, &stats);
+  MaxOffset = MAX (IMAGE_OFFSET, 3*stats.sigma);
+  MedOffset = stats.median;
+  liststats (slist, dlist, N, &stats);
+  MaxScatter = MAX (IMAGE_SCATTER, 2*stats.median);
+  fprintf (stderr, "Mrel: %f, dMrel: %f, Max Scatter: %f, Max Offset: %f\n", MedOffset, stats.median, MaxScatter, MaxOffset);
+  
+  Nmark = 0;
+  for (i = 0; i < Nimage; i++) {
+    mark = FALSE;
+    image[i].code &= ~ID_IMAGE_POOR;
+    mark = (image[i].dMcal > MaxScatter) || (fabs(image[i].Mcal - MedOffset) > MaxOffset);
+    if (mark) { 
+      Nmark ++;
+      image[i].code |= ID_IMAGE_POOR;
+    } else {
+      image[i].code &= ~ID_IMAGE_POOR;
+    }
+  }
+
+  fprintf (stderr, "%d images marked poor\n", Nmark);
+  initstats (STATMODE);
+  free (mlist);
+  free (slist);
+  free (dlist);
+}
+
+void plot_images () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  if (FREEZE_IMAGES) return;
+
+  ALLOCATE (xlist, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+  ALLOCATE (Mlist, double, Nimage);
+
+  /**** dMcal vs airmass ****/
+  for (i = 0; i < Nimage; i++) {
+    Mlist[i] = image[i].Mcal;
+    dlist[i] = image[i].dMcal;
+    xlist[i] = image[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin; 
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nimage, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, Mlist, dlist, Nimage, "Mcal vs dMcal", NULL);
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nimage; i++) {
+    bin = image[i].dMcal / 0.00025;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+StatType statsImageN (Catalog *catalog) {
+
+  int i, j, m, c, n, N;
+  double *list, *dlist;
+  float Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+
+      m = mlist[i][j];
+      c = clist[i][j];
+
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mmos  = getMmos  (m, c);
+      if (isnan(Mmos)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      N++;
+    }
+    list[n] = N;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImageX (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = pow (10.0, 0.01*image[i].Xm);
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImageM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = image[i].Mcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsImagedM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (FREEZE_IMAGES) return (stats);
+
+  ALLOCATE (list, double, Nimage);
+  ALLOCATE (dlist, double, Nimage);
+
+  n = 0;
+  for (i = 0; i < Nimage; i++) {
+
+    if (image[i].code & IMAGE_BAD)  continue;
+
+    list[n] = image[i].dMcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+Image *getimages (int *N) {
+
+  *N = Nimage;
+  return (image);
+}
+
+Image *getimage (int N) {
+  return (&image[N]);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/MosaicOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/MosaicOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/MosaicOps.c	(revision 17422)
@@ -0,0 +1,681 @@
+# include "relphot.h"
+
+Image *getimages (int *N);
+
+static int    Nmosaic;
+static Mosaic *mosaic;
+
+static int    *Nimlist;
+static int   **imlist; /* mosaic -> image[] */
+static int   **bin;    /* catalog, measure -> mosaic */
+
+static int   **clist;  /* mosaic -> catalog[] */
+static int   **mlist;  /* mosiac -> measure[] */
+static int    *Nlist;
+static int    *NLIST;
+
+/* find mosaic frames (unique time periods & photcode name matches mosaic) */
+void initMosaics (Image *image, int Nimage) {
+
+  int i, j, status, found, NMOSAIC, *NIMLIST;
+  unsigned int start, stop;
+  char *pname;
+
+  if (!MOSAICNAME[0]) return;
+
+  Nmosaic = 0;
+  NMOSAIC = 10;
+  ALLOCATE (mosaic, Mosaic, NMOSAIC);
+
+  ALLOCATE (imlist, int *, NMOSAIC);
+  ALLOCATE (Nimlist, int, NMOSAIC);
+  ALLOCATE (NIMLIST, int, NMOSAIC);
+
+  /* generate list of unique mosaics */
+  for (i = 0; i < Nimage; i++) {
+
+    /* select valid mosaic images by photcode */
+    pname = GetPhotcodeNamebyCode (image[i].photcode);
+    status = strncmp (pname, MOSAICNAME, strlen (MOSAICNAME));
+    if (status) continue;
+
+    /* set image time range */
+    start = image[i].tzero - MAX(0.01*image[i].trate*image[i].NY, 1);
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    /* find existing mosaic with this time range */
+    found = FALSE;
+    for (j = 0; !found && (j < Nmosaic); j++) { 
+      if (stop  < mosaic[j].start) continue;
+      if (start > mosaic[j].stop)  continue;
+      found = TRUE;
+
+      /* add image to mosaic image list */
+      imlist[j][Nimlist[j]] = i;
+      Nimlist[j] ++;
+      if (Nimlist[j] == NIMLIST[j]) {
+	NIMLIST[j] += 10;
+	REALLOCATE (imlist[j], int, NIMLIST[j]);
+      }
+
+    }
+    if (found) continue;
+    
+    /* a new mosaic, define ranges */
+    mosaic[Nmosaic].start = start;
+    mosaic[Nmosaic].stop  = stop;
+    mosaic[Nmosaic].Mcal  = 0.0;
+    mosaic[Nmosaic].dMcal = 0.0;
+    mosaic[Nmosaic].Xm    = 0.0;
+    mosaic[Nmosaic].code  = image[i].code;
+    mosaic[Nmosaic].secz  = image[i].secz;
+
+    /* add image to mosaic image list */
+    NIMLIST[Nmosaic] = 10;
+    Nimlist[Nmosaic] = 1;
+    ALLOCATE (imlist[Nmosaic], int, NIMLIST[Nmosaic]);
+    imlist[Nmosaic][0] = i;
+
+    Nmosaic ++;
+    if (Nmosaic == NMOSAIC) {
+      NMOSAIC += 10;
+      REALLOCATE (mosaic, Mosaic, NMOSAIC);
+      REALLOCATE (imlist, int *, NMOSAIC);
+      REALLOCATE (Nimlist, int, NMOSAIC);
+      REALLOCATE (NIMLIST, int, NMOSAIC);
+    }
+  }
+
+  initMosaicGrid (image, Nimage);
+  return;
+}
+
+void initMosaicGrid (Image *image, int Nimage) {
+
+  /* find max dR, dD range for all mosaics */
+  /* define mosaic.coords to cover dR, dD */
+  /* send results to initGridBins */
+
+  int i, j, m, NX, NY;
+  int dXmax, dYmax;
+  double dS, dX, dY;
+  double R, D, Rmin, Rmax, Dmin, Dmax;
+  double Mcal, dMcal, Xm;
+
+  dXmax = dYmax = 0.0;
+  for (i = 0; i < Nmosaic; i++) {
+    Dmin = Rmin =  360.0;
+    Dmax = Rmax = -360.0;
+    dS = 0.0;
+    Mcal = dMcal = Xm = 0;
+    for (j = 0; j < Nimlist[i]; j++) {
+      m = imlist[i][j];
+      NX = image[m].NX;
+      NY = image[m].NY;
+      dS += hypot (image[m].coords.cdelt1*image[m].coords.pc1_1, image[m].coords.cdelt1*image[m].coords.pc2_1);
+      XY_to_RD (&R, &D, 0.0, 0.0, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, (double) NX, 0.0, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, (double) NX, (double) NY, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      XY_to_RD (&R, &D, 0.0, (double) NY, &image[m].coords);
+      Rmin = MIN (Rmin, R);
+      Rmax = MAX (Rmax, R);
+      Dmin = MIN (Dmin, D);
+      Dmax = MAX (Dmax, D);
+      Mcal  += image[m].Mcal;
+      dMcal += image[m].dMcal;
+      Xm    += image[m].Xm;
+      /* we are using mosaic.Mcal, not image.Mcal. reset image.Mcal */
+      image[m].Mcal  = 0.0;
+      image[m].dMcal = NAN;
+      image[m].Xm    = NAN_S_SHORT;
+    }
+    dS /= Nimlist[i];
+    strcpy (mosaic[i].coords.ctype, "RA---TAN");
+    mosaic[i].coords.crval1 = Rmin;
+    mosaic[i].coords.crval2 = Dmin;
+    mosaic[i].coords.crpix1 = 0.0;
+    mosaic[i].coords.crpix2 = 0.0;
+    mosaic[i].coords.cdelt1 = dS;
+    mosaic[i].coords.cdelt2 = dS;
+    mosaic[i].coords.pc1_1  = 1.0;
+    mosaic[i].coords.pc2_2  = 1.0;
+    mosaic[i].coords.pc1_2  = 0.0;
+    mosaic[i].coords.pc2_1  = 0.0;
+    RD_to_XY (&dX, &dY, Rmax, Dmax, &mosaic[i].coords);
+
+    mosaic[i].Mcal  = Mcal / Nimlist[i];
+    mosaic[i].dMcal = dMcal / Nimlist[i];
+    mosaic[i].Xm    = Xm / Nimlist[i];
+  }
+  if (!USE_GRID) return;
+
+  dXmax = MAX (dXmax, dX);
+  dYmax = MAX (dYmax, dY);
+  initGrid (dXmax, dYmax);
+  return;
+}
+
+void setMcalFinal () {
+
+  int i, j, im, Nimage;
+  Image *image;
+
+  if (!MOSAICNAME[0]) return;
+
+  image = getimages (&Nimage);
+
+  for (i = 0; i < Nmosaic; i++) {
+    for (j = 0; j < Nimlist[i]; j++) {
+      im = imlist[i][j];
+      image[im].Mcal = mosaic[i].Mcal;
+      image[im].dMcal = mosaic[i].dMcal;
+      image[im].Xm = mosaic[i].Xm;
+      image[im].code |= (mosaic[i].code & ID_IMAGE_FEW);
+      image[im].code |= (mosaic[i].code & ID_IMAGE_POOR);
+    }
+  }      
+}
+
+void initMosaicBins (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+
+  /* measure -> mosaic */
+  if (!MOSAICNAME[0]) return;
+  ALLOCATE (bin, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (bin[i], int, MAX (catalog[i].Nmeasure, 1));
+    for (j = 0; j < catalog[i].Nmeasure; j++) bin[i][j] = -1;
+  }
+
+  /* mosaic -> measure */
+  ALLOCATE (Nlist, int, Nmosaic);
+  ALLOCATE (NLIST, int, Nmosaic);
+  ALLOCATE (clist, int *, Nmosaic);
+  ALLOCATE (mlist, int *, Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+    Nlist[i] = 0;
+    NLIST[i] = 100;
+    ALLOCATE (clist[i], int, NLIST[i]);
+    ALLOCATE (mlist[i], int, NLIST[i]);
+  }
+}
+
+void freeMosaicBins (int Ncatalog) {
+
+  int i;
+
+  /* measure -> mosaic */
+  if (!MOSAICNAME[0]) return;
+  for (i = 0; i < Ncatalog; i++) {
+    free (bin[i]);
+  }
+  free (bin);
+
+  /* mosaic -> measure */
+  for (i = 0; i < Nmosaic; i++) {
+    free (clist[i]);
+    free (mlist[i]);
+  }
+  free (Nlist);
+  free (NLIST);
+  free (clist);
+  free (mlist);
+}
+
+int findMosaics (Catalog *catalog, int Ncatalog) {
+  
+  int i, j, ecode;
+
+  if (!MOSAICNAME[0]) return (FALSE);
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Nmeasure; j++) {
+      if (TimeSelect) {
+	if (catalog[i].measure[j].t < TSTART) continue;
+	if (catalog[i].measure[j].t > TSTOP) continue;
+      }
+      ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[j].photcode);
+      if (photcode[0].code != ecode) continue;
+      matchMosaics (catalog, j, i);
+    }
+  }
+  return (TRUE);
+}
+
+void matchMosaics (Catalog *catalog, int meas, int cat) {
+
+  int i;
+
+  for (i = 0; i < Nmosaic; i++) {
+    if (catalog[cat].measure[meas].t < mosaic[i].start) continue;
+    if (catalog[cat].measure[meas].t > mosaic[i].stop) continue;
+    
+# ifdef GRID_V1
+    if (USE_GRID) {
+      ave = catalog[cat].measure[meas].averef;
+      ra  = catalog[cat].average[ave].R - catalog[cat].measure[meas].dR / 3600.0;
+      dec = catalog[cat].average[ave].D - catalog[cat].measure[meas].dD / 3600.0;
+
+      /* X,Y always positive-definite in range 0,0 - dX, dY */
+      RD_to_XY (&X, &Y, ra, dec, &mosaic[i].coords);
+      setGridMeasure (meas, cat, X, Y);
+    }
+# endif
+
+    bin[cat][meas] = i;
+
+    clist[i][Nlist[i]] = cat;
+    mlist[i][Nlist[i]] = meas;
+    Nlist[i] ++;
+    
+    if (Nlist[i] == NLIST[i]) {
+      NLIST[i] += 100;
+      REALLOCATE (clist[i], int, NLIST[i]);
+      REALLOCATE (mlist[i], int, NLIST[i]);
+    }	
+    return;
+  }
+  fprintf (stderr, "missed measurement\n");
+  return;
+}
+
+float getMmos (int meas, int cat) {
+
+  int i;
+  float value;
+
+  if (!MOSAICNAME[0]) return (0);
+  i = bin[cat][meas];
+  if (i == -1) return (NAN);
+
+  if (mosaic[i].code & IMAGE_BAD) return (NAN);  
+  value = mosaic[i].Mcal;
+  return (value);
+}
+
+int setMmos (Catalog *catalog, int PoorImages) {
+
+  int i, j, m, c, n, N, Nmax, mark, bad;
+  float Msys, Mrel, Mcal, Mgrid;
+  double *list, *dlist, *Mlist, *dMlist;
+  StatType stats;
+  Image *image;
+
+  if (!MOSAICNAME[0]) return (FALSE);
+
+  image = getimages (&N);
+
+  if (PoorImages) {
+    IMAGE_BAD = 0;
+  }
+
+  Nmax = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    Nmax = MAX (Nmax, Nlist[i]);
+  }
+  ALLOCATE (list, double, Nmax);
+  ALLOCATE (dlist, double, Nmax);
+  ALLOCATE (Mlist, double, Nmax);
+  ALLOCATE (dMlist, double, Nmax);
+
+  for (i = 0; i < Nmosaic; i++) {
+    
+    /* on PoorImages run, skip good images */
+    if (PoorImages) {
+      bad = mosaic[i].code & (ID_IMAGE_FEW | ID_IMAGE_POOR | ID_IMAGE_SKIP);
+      if (!bad) continue;
+    }      
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & MEAS_BAD) continue;
+      Mcal  = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel  = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      
+      n = catalog[c].measure[m].averef;
+      Msys = PhotSys (&catalog[c].measure[m], &catalog[c].average[n], &catalog[c].secfilt[n*PhotNsec]);
+      list[N]  = Msys - Mrel - Mcal - Mgrid;
+      dlist[N] = MAX (catalog[c].measure[m].dM, MIN_ERROR);
+      Mlist[N] = Msys;
+      dMlist[N] = list[N];
+      N++;
+    }
+    /* Nlist[i] is all measurements, N is good measurements */
+
+    /* too few good measurements or too many bad measurements (skip in PoorImages run) */
+
+    if (!PoorImages) {
+      mark = (N < IMAGE_TOOFEW) || (N < IMAGE_GOOD_FRACTION*Nlist[i]);
+      if (mark) {
+	fprintf (stderr, "marked image %s (%d), (%d < %d) || (%d < %f*%d)\n", image[imlist[i][0]].name, i, N, IMAGE_TOOFEW, N, IMAGE_GOOD_FRACTION, Nlist[i]);
+	mosaic[i].code |= ID_IMAGE_FEW;
+      } else {
+	mosaic[i].code &= ~ID_IMAGE_FEW;
+      }
+    }
+    liststats (list, dlist, N, &stats);
+    if (PoorImages) fprintf (stderr, "Mmos: %f %f %d %d\n", stats.mean, stats.sigma, stats.Nmeas, N);
+    mosaic[i].Mcal  = stats.mean;
+    mosaic[i].dMcal = stats.sigma;
+    mosaic[i].Xm    = 100.0*log10(stats.chisq);
+  }
+  free (list);
+  free (dlist);
+  free (Mlist);
+  free (dMlist);
+
+  if (PoorImages) {
+    IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+    STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+    MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+  }
+  return (TRUE);
+}
+  
+StatType statsMosaicM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = mosaic[i].Mcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicdM (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = mosaic[i].dMcal;
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicN (Catalog *catalog) {
+
+  int i, j, m, c, n, N;
+  double *list, *dlist;
+  float Mcal, Mgrid, Mrel;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD)  continue;
+
+    N = 0;
+    for (j = 0; j < Nlist[i]; j++) {
+
+      m = mlist[i][j];
+      c = clist[i][j];
+
+      Mcal = getMcal  (m, c);
+      if (isnan(Mcal)) continue;
+      Mgrid = getMgrid (m, c);
+      if (isnan(Mgrid)) continue;
+      Mrel = getMrel  (catalog, m, c);
+      if (isnan(Mrel)) continue;
+      N++;
+    }
+    list[n] = N;
+    dlist[n] = 1;
+    n++;
+  }
+  fprintf (stderr, "Nmosaic: %d, n: %d\n", Nmosaic, n);
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsMosaicX (Catalog *catalog) {
+
+  int i, n;
+  double *list, *dlist;
+  StatType stats;
+
+  bzero (&stats, sizeof (StatType));
+  if (!MOSAICNAME[0]) return (stats);
+
+  ALLOCATE (list, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  n = 0;
+  for (i = 0; i < Nmosaic; i++) {
+
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    list[n] = pow(10.0, 0.01*mosaic[i].Xm);
+    dlist[n] = 1;
+    n++;
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+/* mark mosaic if: abs(Mcal - <Mcal>) too large, dMcal too large */
+void clean_mosaics () {
+
+  int i, N, mark, Nmark;
+  double *mlist, *slist, *dlist;
+  double MaxOffset, MedOffset, MaxScatter;
+  StatType stats;
+
+  if (!MOSAICNAME[0]) return;
+
+  if (VERBOSE) fprintf (stderr, "marking poor mosaics\n");
+
+  ALLOCATE (mlist, double, Nmosaic);
+  ALLOCATE (slist, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+
+  for (i = N = 0; i < Nmosaic; i++) {
+    if (mosaic[i].code & IMAGE_BAD) continue;
+    mlist[N] = mosaic[i].Mcal;
+    slist[N] = mosaic[i].dMcal;
+    dlist[N] = 1;
+    N++;
+  }
+  initstats ("MEAN");
+  liststats (mlist, dlist, N, &stats);
+  MaxOffset = MAX (IMAGE_OFFSET, 2*stats.sigma);
+  MedOffset = stats.median;
+  liststats (slist, dlist, N, &stats);
+  MaxScatter = MAX (IMAGE_SCATTER, 2*stats.median);
+  fprintf (stderr, "Mrel: %f, dMrel: %f, Max Scatter: %f, Max Offset: %f\n", MedOffset, stats.median, MaxScatter, MaxOffset);
+  
+  Nmark = 0;
+  for (i = 0; i < Nmosaic; i++) {
+    mark = FALSE;
+    mark = (mosaic[i].dMcal > MaxScatter) || (fabs(mosaic[i].Mcal - MedOffset) > MaxOffset);
+    if (mark) { 
+      Nmark ++;
+      mosaic[i].code |= ID_IMAGE_POOR;
+    } else {
+      mosaic[i].code &= ~ID_IMAGE_POOR;
+    }
+  }
+
+  fprintf (stderr, "%d mosaics marked poor\n", Nmark);
+  initstats (STATMODE);
+  free (mlist);
+  free (slist);
+  free (dlist);
+}
+
+void plot_mosaic_fields (Catalog *catalog) {
+
+  int i, j, m, c, N, ave, Nimage;
+  double *xlist, *ylist;
+  double Xmin, Xmax, Ymin, Ymax;
+  char string[64];
+  Image *image;
+  Graphdata graphdata;
+
+  if (!MOSAICNAME[0]) return;
+
+  image = getimages (&Nimage);
+
+  N = 0;
+  for (i = 0; i < Nmosaic; i++) 
+    N = MAX (N, Nlist[i]);
+
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+
+  for (i = 0; i < Nmosaic; i++) {
+    N = 0;
+    Xmin = Ymin = +360.0;
+    Xmax = Ymax = -360.0;
+    for (j = 0; j < Nlist[i]; j++) {
+      
+      m = mlist[i][j];
+      c = clist[i][j];
+      
+      if (catalog[c].measure[m].dbFlags & (ID_MEAS_AREA | ID_MEAS_NOCAL)) continue;
+
+      ave = catalog[c].measure[m].averef;
+      xlist[N] = catalog[c].average[ave].R - catalog[c].measure[m].dR / 3600.0;
+      ylist[N] = catalog[c].average[ave].D - catalog[c].measure[m].dD / 3600.0;
+      N++;
+    }
+  
+    sprintf (string, "Mosaic %d", i);
+    plot_defaults (&graphdata);
+    plot_list (&graphdata, xlist, ylist, N, string, NULL);
+  }
+
+  free (ylist);
+  free (xlist);
+}
+
+void plot_mosaics () {
+
+  int i, bin;
+  double *xlist, *Mlist, *dlist;
+  Graphdata graphdata;
+
+  if (!MOSAICNAME[0]) return;
+
+  ALLOCATE (xlist, double, Nmosaic);
+  ALLOCATE (dlist, double, Nmosaic);
+  ALLOCATE (Mlist, double, Nmosaic);
+
+  for (i = 0; i < Nmosaic; i++) {
+    Mlist[i] = mosaic[i].Mcal;
+    dlist[i] = mosaic[i].dMcal;
+    xlist[i] = mosaic[i].secz;
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = 0.95;
+  graphdata.xmax = 2.50;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, Mlist, Nmosaic, "airmass vs Mcal", "airmass.png");
+  plot_defaults (&graphdata);
+  graphdata.size = 1.5;
+  graphdata.ptype = 7;
+  plot_list (&graphdata, Mlist, dlist, Nmosaic, "Mcal vs dMcal", "MdM.png");
+
+# define NBIN 200
+  REALLOCATE (xlist, double, NBIN);
+  REALLOCATE (Mlist, double, NBIN);
+
+  /**** dMcal histgram ****/
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00005*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Nmosaic; i++) {
+    bin = mosaic[i].dMcal / 0.00005;
+    bin = MAX (0, MIN (NBIN - 1, bin));
+    Mlist[bin] += 1.0;
+  }
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMcal hist", "dMcalhist.png");
+
+  free (dlist);
+  free (xlist);
+  free (Mlist);
+}
+
+int *SelectRefMosaic (Mosaic **refmosaic, int *Nimage) {
+
+  int i, Imax, Nmax;
+
+  Imax = 0;
+  Nmax = Nimlist[0];
+  for (i = 0; i < Nmosaic; i++) {
+    if (Nimlist[i] > Nmax) {
+      Imax = i;
+      Nmax = Nimlist[i];
+    }
+  }
+
+  *refmosaic = &mosaic[Imax];
+  *Nimage = Nmax;
+  return (imlist[Imax]);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/SetSignals.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/SetSignals.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/SetSignals.c	(revision 17422)
@@ -0,0 +1,122 @@
+# include "relphot.h"
+
+static int Protect = FALSE;
+static int Trapped = FALSE;
+
+void TrapSignal (int sig) {
+    fprintf (stderr, "trapped signal %d\n", sig);
+    if (sig == 11) {
+      fprintf (stderr, "seg fault\n");
+      exit (1);
+    }
+    if (Protect) {
+      Trapped = TRUE;
+      fprintf (stderr, "blocking until protected sections are clear\n");
+      return;
+    }
+    Shutdown ("halted by signal (trapped)");
+}    
+
+void SetProtect (int mode) {
+  Protect = mode;
+  if (Trapped && !Protect) Shutdown ("halted by signal (protect)");
+}
+
+int SetSignals () {
+
+  int i;
+
+  /* disable almost all signal interrupts */
+  for (i = 0; i < 36; i++) {
+    switch (i) {
+      /* can't redirect this signals */
+    case SIGKILL:    /* kill -9: cannot be caught or ignored */
+    case SIGSTOP:    /* SIGSTOP: cannot be caught or ignored */
+      /* ignore these signals */
+    case SIGCHLD:    /* child halted: ignore */
+    case SIGCONT:    /* continue - maintain this action */
+    case SIGTSTP:    /* stop signal sent from tty - why ignore? */
+    case SIGURG:     /* socket signal, ignore this */
+# ifdef SIGPWR
+    case SIGPWR:     /* power failure - why ignore this? (Sys V) */
+# endif
+# ifdef SIGWINCH
+    case SIGWINCH:   /* window resized (4.3BSD) */
+# endif
+      break;
+      
+    default:
+      signal (i, TrapSignal);
+    }
+  }
+  return (TRUE);
+}
+/*
+
+       Signal     Value     Action   Comment
+       -------------------------------------------------------------------------
+       SIGHUP        1        A      Hangup detected on controlling terminal
+                                     or death of controlling process
+       SIGINT        2        A      Interrupt from keyboard
+       SIGQUIT       3        A      Quit from keyboard
+       SIGILL        4        A      Illegal Instruction
+       SIGABRT       6        C      Abort signal from abort(3)
+       SIGFPE        8        C      Floating point exception
+       SIGKILL       9       AEF     Kill signal
+       SIGSEGV      11        C      Invalid memory reference
+       SIGPIPE      13        A      Broken pipe: write to pipe with no readers
+       SIGALRM      14        A      Timer signal from alarm(2)
+       SIGTERM      15        A      Termination signal
+       SIGUSR1   30,10,16     A      User-defined signal 1
+       SIGUSR2   31,12,17     A      User-defined signal 2
+       SIGCHLD   20,17,18     B      Child stopped or terminated
+       SIGCONT   19,18,25            Continue if stopped
+       SIGSTOP   17,19,23    DEF     Stop process
+       SIGTSTP   18,20,24     D      Stop typed at tty
+       SIGTTIN   21,21,26     D      tty input for background process
+       SIGTTOU   22,22,27     D      tty output for background process
+
+       Next various other signals.
+
+       Signal       Value     Action   Comment
+       ---------------------------------------------------------------------
+       SIGTRAP        5         CG     Trace/breakpoint trap
+       SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+       SIGEMT       7,-,7       G
+       SIGBUS      10,7,10      AG     Bus error
+       SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+       SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+       SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+       SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+       SIGPOLL                  AG     A synonym for SIGIO (System V)
+       SIGCLD       -,-,18      G      A synonym for SIGCHLD
+       SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+       SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+       SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+       SIGPROF     27,27,29     AG     Profile alarm clock
+       SIGPWR      29,30,19     AG     Power failure (System V)
+       SIGINFO      29,-,-      G      A synonym for SIGPWR
+       SIGLOST      -,-,-       AG     File lock lost
+       SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+       SIGUNUSED    -,31,-      AG     Unused signal
+       (Here - denotes that a signal is absent; there where three values are given, the first one is usually  valid  for  alpha  and
+       sparc,  the  middle  one  for i386 and ppc, the last one for mips. Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a
+       sparc.)
+
+       The letters in the "Action" column have the following meanings:
+
+       A      Default action is to terminate the process.
+
+       B      Default action is to ignore the signal.
+
+       C      Default action is to dump core.
+
+       D      Default action is to stop the process.
+
+       E      Signal cannot be caught.
+
+       F      Signal cannot be ignored.
+
+       G      Not a POSIX.1 conformant signal.
+
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/Shutdown.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/Shutdown.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/Shutdown.c	(revision 17422)
@@ -0,0 +1,28 @@
+# include "relphot.h"
+
+static FITS_DB *db;
+
+void set_db (FITS_DB *in) {
+  db = in;
+}
+
+/* clean up open / locked ImageCat before shutting down */
+int Shutdown (char *format, ...) {  
+  va_list argp;
+  char *formatplus;
+  
+  ALLOCATE (formatplus, char, strlen(format));
+  strcpy (formatplus, format);
+  strcat (formatplus, "\n");
+
+  va_start (argp, format);
+  vfprintf (stderr, formatplus, argp);
+  free (formatplus);
+  va_end (argp);
+
+  SetProtect (TRUE);
+  gfits_db_close (db);
+  fprintf (stderr, "ERROR: addstar halted\n");
+  exit (1);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/StarOps.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/StarOps.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/StarOps.c	(revision 17422)
@@ -0,0 +1,611 @@
+# include "relphot.h"
+
+static int Nmax;
+static double *list;
+static double *dlist;
+
+void initMrel (Catalog *catalog, int Ncatalog) {
+
+  int i, j;
+  
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+}  
+
+float getMrel (Catalog *catalog, int meas, int cat) {
+
+  int ave;
+  float value;
+
+  ave = catalog[cat].measure[meas].averef;
+  if (catalog[cat].average[ave].code & STAR_BAD) return (NAN);  
+ 
+  value = catalog[cat].secfilt[PhotNsec*ave+PhotSec].M;
+  return (value);
+}
+
+int setMrel (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N;
+  float Msys, Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	// XXX allow REF stars (no Image Entry) to be included in the calculation this
+	// should be optionally set, and should allow for REF stars to be downweighted by
+	// more than their reported errors.  how such info is carried is unclear...
+	if (getImageEntry (m, i) < 0) {
+	  Mcal = Mmos = Mgrid = 0;
+	} else {
+	  Mcal  = getMcal  (m, i);
+	  if (isnan(Mcal)) continue;
+	  Mmos  = getMmos  (m, i);
+	  if (isnan(Mmos)) continue;
+	  Mgrid = getMgrid (m, i);
+	  if (isnan(Mgrid)) continue;
+	}
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < STAR_TOOFEW) { /* too few measurements */
+	catalog[i].average[j].code |= ID_STAR_FEW;
+      } else {
+	catalog[i].average[j].code &= ~ID_STAR_FEW;
+      }	
+
+      liststats (list, dlist, N, &stats);
+
+      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = stats.sigma;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+    }
+  }
+  return (TRUE);
+}
+
+int setMrelOutput (Catalog *catalog, int Ncatalog, int mark) {
+
+  int i, j, k, m, N, Nmax;
+  float Msys, Mcal, Mmos, Mgrid;
+  double *list, *dlist;
+  StatType stats;
+
+  /* Nmeasure is now different, need to reallocate */
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* skip stars already calibrated */
+      if (catalog[i].found[j]) continue;  
+
+      N = 0;
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	// XXX allow REF stars (no Image Entry) to be included in the calculation this
+	// should be optionally set, and should allow for REF stars to be downweighted by
+	// more than their reported errors.  how such info is carried is unclear...
+	if (getImageEntry (m, i) < 0) {
+	  Mcal = Mmos = Mgrid = 0;
+	} else {
+	  Mcal  = getMcal  (m, i);
+	  if (isnan(Mcal)) continue;
+	  Mmos  = getMmos  (m, i);
+	  if (isnan(Mmos)) continue;
+	  Mgrid = getMgrid (m, i);
+	  if (isnan(Mgrid)) continue;
+	}
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < 1) continue;
+
+      liststats (list, dlist, N, &stats);
+      if (mark) catalog[i].found[j] = TRUE;
+
+      /* use sigma or error in dM for output? */
+      catalog[i].secfilt[PhotNsec*j+PhotSec].M  = stats.mean;
+      catalog[i].secfilt[PhotNsec*j+PhotSec].dM = MAX (stats.error, stats.sigma);
+      catalog[i].secfilt[PhotNsec*j+PhotSec].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+    }
+  }
+
+  free (list);
+  free (dlist);
+  return (TRUE);
+}
+
+// for each average object, set the average mags based on existing equiv photometry
+int setMave (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, Ns, Nsecfilt, N, Nmax, Nc;
+  float Msys;
+  double *list, *dlist;
+  StatType stats;
+  PhotCode *code;
+
+  // pre-allocate a list for stats purposes
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+
+  Nsecfilt = catalog[0].Nsecfilt;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      for (Ns = 0; Ns < Nsecfilt; Ns++) {
+
+	code = GetPhotcodebyNsec (Ns);
+	Nc = code[0].code;
+	
+	N = 0;
+	m = catalog[i].average[j].measureOffset;
+	for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	  if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	  if (GetPhotcodeEquivCodebyCode (catalog[i].measure[m].photcode) != Nc) continue;
+
+	  Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	  if (isnan(Msys)) continue;
+
+	  list[N] = Msys - catalog[i].measure[m].Mcal;
+	  dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	  N++;
+	}
+	if (N < 1) continue;
+
+	liststats (list, dlist, N, &stats);
+
+	/* use sigma or error in dM for output? */
+	catalog[i].secfilt[Nsecfilt*j+Ns].M  = stats.mean;
+	catalog[i].secfilt[Nsecfilt*j+Ns].dM = MAX (stats.error, stats.sigma);
+	catalog[i].secfilt[Nsecfilt*j+Ns].Xm = (stats.Nmeas > 1) ? 100.0*log10(stats.chisq) : NAN_S_SHORT;
+	catalog[i].secfilt[Nsecfilt*j+Ns].Ncode = N;
+	catalog[i].secfilt[Nsecfilt*j+Ns].Nused = stats.Nmeas;
+      }
+    }
+  }
+
+  free (list);
+  free (dlist);
+  return (TRUE);
+}
+
+/* set measure.Mcal for all measures except ID_MEAS_NOCAL and ID_IMAGE_NOCAL */
+int setMcalOutput (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m;
+  float Mcal, Mmos, Mgrid;
+
+  MEAS_BAD = ID_MEAS_NOCAL;
+  IMAGE_BAD = ID_IMAGE_NOCAL;
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+	catalog[i].measure[m].Mcal = Mcal + Mmos + Mgrid;
+      }
+    }
+  }
+  return (TRUE);
+}
+
+void clean_stars (Catalog *catalog, int Ncatalog) {
+
+  int i, j, Ndel, Nave, Ntot, mark;
+  float dM, Xm;
+  double Chisq, MaxScatter, MaxChisq;
+  double *xlist, *slist, *dlist;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "marking poor stars\n");
+
+  /* find Xm median -> ChiSq lim must be > median */
+  for (i = Ntot = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (slist, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+  for (i = Ntot = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (Xm == -1) continue;
+      Chisq = pow (10.0, 0.01*Xm);
+      xlist[Ntot] = Chisq;
+      slist[Ntot] = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      dlist[Ntot] = 1;
+      Ntot ++;
+    }
+  }
+  
+  initstats ("MEAN");
+  liststats (xlist, dlist, Ntot, &stats);
+  MaxChisq = MAX (STAR_CHISQ, 2*stats.median);
+  liststats (slist, dlist, Ntot, &stats);
+  MaxScatter = MAX (STAR_SCATTER, 2*stats.median);
+  fprintf (stderr, "Max Scatter: %f, Max Chisq: %f\n", MaxScatter, MaxChisq);
+
+  Ndel = Nave = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      Chisq = pow (10.0, 0.01*Xm);
+      mark = (dM > MaxScatter) || (Xm == NAN_S_SHORT) || (Chisq > MaxChisq);
+      if (mark) {
+	catalog[i].average[j].code |= ID_STAR_POOR;
+	Ndel ++;
+      } else {
+	catalog[i].average[j].code &= ~ID_STAR_POOR;
+      }
+      Nave ++;
+    }
+  }
+  fprintf (stderr, "%d stars marked variable, %d total\n", Ndel, Nave);
+  initstats (STATMODE);
+  free (xlist);
+  free (slist);
+  free (dlist);
+}
+
+void clean_measures (Catalog *catalog, int Ncatalog, int final) {
+
+  int i, j, k, m, N, Ndel, Nave, Nmax, image_bad, TOOFEW;
+  int *ilist;
+  double *tlist, *list, *dlist, Ns;
+  float Msys, Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  if (VERBOSE) fprintf (stderr, "marking poor measures\n");
+  /* Nmeasure is now different, need to reallocate */
+  Nmax = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Nmax = MAX (Nmax, catalog[i].average[j].Nmeasure);
+    }
+  }
+  ALLOCATE (list, double, MAX (1, Nmax));
+  ALLOCATE (dlist, double, MAX (1, Nmax));
+  ALLOCATE (ilist, int, Nmax);
+  ALLOCATE (tlist, double, Nmax);
+  
+  /* it makes no sense to mark 3-sigma outliers with <5 measurements */
+  TOOFEW = MAX (5, STAR_TOOFEW);
+
+  Ns = 3;
+  Ndel = Nave = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* skip bad stars to prevent them from becoming good (on inner sample) */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      /* on final processing, skip stars already measured */
+      if (final && catalog[i].found[j]) continue;  
+
+      /* accumulate list of valid measurements */
+      m = catalog[i].average[j].measureOffset;
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	N++;
+      }
+      if (N < TOOFEW) continue;
+
+      /* 3-sigma clip based on stats of inner 50% */
+      initstats ("INNER_MEAN");
+      liststats (list, dlist, N, &stats);
+      stats.sigma = MAX (MIN_ERROR, stats.sigma); /* if measurements agree too well, sigma -> 0.0 */
+      for (k = m = 0; k < N; k++) {
+	if (fabs (list[k] - stats.median) < Ns*stats.sigma) {
+	  list[m] = list[k];
+	  m++;
+	}
+      }
+      initstats ("MEAN");
+      liststats (list, dlist, m, &stats);
+      stats.sigma = MAX (MIN_ERROR, stats.sigma);
+
+      /* apply to list of all relevant measurements, including IMAGE_POOR & IMAGE_FEW */
+      image_bad = IMAGE_BAD;
+      IMAGE_BAD = ID_IMAGE_NOCAL;
+      m = catalog[i].average[j].measureOffset;
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
+	Mcal  = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos  = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
+	list[N] = Msys - Mcal - Mmos - Mgrid;
+	dlist[N] = MAX (catalog[i].measure[m].dM, MIN_ERROR);
+	ilist[N] = m;
+	N++;
+	Nave ++;
+      }
+      if (N < TOOFEW) continue;
+
+      /* mark bad measures */
+      for (k = 0; k < N; k++) {
+	if (fabs (list[k] - stats.median) > Ns*stats.sigma) {
+	  catalog[i].measure[ilist[k]].dbFlags |= ID_MEAS_POOR_PHOTOM;
+	  Ndel ++;
+	}
+      }
+      IMAGE_BAD = image_bad;
+    }
+  }
+  initstats (STATMODE);
+  if (VERBOSE) fprintf (stderr, "%d measures marked poor, %d total\n", Ndel, Nave);
+  free (ilist);
+  free (tlist);
+}
+
+StatType statsStarN (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, n, N, Ntot;
+  double *list, *dlist;
+  float Mcal, Mmos, Mgrid;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      N = 0;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	Mcal = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+	N++;
+      }
+      
+      list[n] = N;
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsStarX (Catalog *catalog, int Ncatalog) {
+
+  int i, j, n, Ntot, Xm;
+  double *list, *dlist;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      Xm = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (Xm == NAN_S_SHORT) continue;
+      list[n] = pow (10.0, 0.01*Xm);
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+StatType statsStarS (Catalog *catalog, int Ncatalog) {
+
+  int i, j, n, Ntot;
+  double *list, *dlist;
+  float dM;
+  StatType stats;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    Ntot += catalog[i].Naverage; 
+  }
+
+  ALLOCATE (list, double, Ntot);
+  ALLOCATE (dlist, double, Ntot);
+
+  n = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+
+      dM = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      list[n] = dM;
+      dlist[n] = 1;
+      n++;
+    }
+  }
+
+  liststats (list, dlist, n, &stats);
+  free (list);
+  free (dlist);
+  return (stats);
+}
+
+void plot_stars (Catalog *catalog, int Ncatalog) {
+
+  int i, j, bin;
+  float dMrel;
+  double *xlist, *Mlist;
+  Graphdata graphdata;
+
+# define NBIN 200
+  ALLOCATE (xlist, double, NBIN);
+  ALLOCATE (Mlist, double, NBIN);
+
+  for (i = 0; i < NBIN; i++) xlist[i] = 0.00025*i;
+  bzero (Mlist, NBIN*sizeof(double));
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      dMrel = catalog[i].secfilt[PhotNsec*j+PhotSec].dM;
+      bin = dMrel / 0.00025;
+      bin = MAX (0, MIN (NBIN-1, bin));
+      Mlist[bin] += 1.0;
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.style = 1;
+  plot_list (&graphdata, xlist, Mlist, NBIN, "dMrel hist", "dMhist.png");
+
+  free (xlist);
+  free (Mlist);
+}
+
+void plot_chisq (Catalog *catalog, int Ncatalog) {
+
+  int i, j, N, Ntotal, value;
+  double *xlist, *ylist;
+  Graphdata graphdata;
+
+  Ntotal = 0;
+  for (i = 0; i < Ncatalog; i++) Ntotal += catalog[i].Naverage;
+
+  ALLOCATE (xlist, double, Ntotal);
+  ALLOCATE (ylist, double, Ntotal);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      if (catalog[i].average[j].code & STAR_BAD) continue;
+      xlist[N] = catalog[i].secfilt[PhotNsec*j+PhotSec].M;
+      value    = catalog[i].secfilt[PhotNsec*j+PhotSec].Xm;
+      if (value == NAN_S_SHORT) continue;
+      ylist[N] = 0.01*value;
+      N++;
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = -3.0;
+  plot_list (&graphdata, xlist, ylist, N, "chisq", NULL);
+  free (xlist);
+  free (ylist);
+}
+
+void plot_star_coords (Catalog *catalog, int Ncatalog) {
+
+  int i, j, N;
+  double *xlist, *ylist;
+  double Xmin, Ymin, Xmax, Ymax;
+  Graphdata graphdata;
+
+  N = 0; 
+  for (i = 0; i < Ncatalog; i++) {
+    N += catalog[i].Naverage;
+  }
+  ALLOCATE (xlist, double, N);
+  ALLOCATE (ylist, double, N);
+
+  N = 0;
+  Xmin = Ymin = +360.0;
+  Xmax = Ymax = -360.0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      xlist[N] = catalog[i].average[j].R;
+      ylist[N] = catalog[i].average[j].D;
+      N++;
+    }
+  }
+  plot_defaults (&graphdata);
+  plot_list (&graphdata, xlist, ylist, N, "coords", NULL);
+
+  free (xlist);
+  free (ylist);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/args.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/args.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/args.c	(revision 17422)
@@ -0,0 +1,217 @@
+# include "relphot.h"
+void usage (void);
+
+int args (int argc, char **argv) {
+
+  int N;
+  double trange;
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+	fprintf (stderr, "ERROR: syntax error\n");
+	return (FALSE);
+      }
+    } else {
+      if (trange < 0) {
+	trange = fabs (trange);
+	TSTOP = TSTART;
+	TSTART -= trange;
+      } else {
+	TSTOP = TSTART + trange;
+      }
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  /* specify portion of the sky */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  UserPatchSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatchSelect = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTDELAY = 500000;
+  if ((N = get_argument (argc, argv, "-plotdelay"))) {
+    remove_argument (N, &argc, argv);
+    PLOTDELAY = 1e6*atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  strcpy (STATMODE, "CHI_INNER_WTMEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  NLOOP = 8;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    NLOOP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
+  if ((N = get_argument (argc, argv, "-plrange"))) {
+    remove_argument (N, &argc, argv);
+    PlotMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* group images by mosaic, find Mmos */
+  MOSAICNAME[0] = 0;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (MOSAICNAME, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  FREEZE_IMAGES = FALSE;
+  if ((N = get_argument (argc, argv, "-imfreeze"))) {
+    remove_argument (N, &argc, argv);
+    FREEZE_IMAGES = TRUE;
+  }
+
+  USE_GRID = FALSE;
+  if ((N = get_argument (argc, argv, "-grid"))) {
+    remove_argument (N, &argc, argv);
+    USE_GRID = TRUE;
+    if (!MOSAICNAME[0]) {
+      fprintf (stderr, "-grid is only valid with -mosaic\n");
+      exit (2);
+    }
+  }
+
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
+
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
+  }
+
+  UpdateAverages = FALSE;
+  if ((N = get_argument (argc, argv, "-averages"))) {
+    remove_argument (N, &argc, argv);
+    UpdateAverages = TRUE;
+  }
+
+  if (UpdateAverages && (argc == 1)) return TRUE;
+  if (UserPatchSelect && (argc == 2)) return TRUE;
+  if (argc != 3) usage ();
+
+  return TRUE;
+}
+
+void usage () {
+  fprintf (stderr, "ERROR: USAGE: relphot (region) (photcode)\n");
+  fprintf (stderr, "       or:    relphot (photcode) -region RA RA DEC DEC\n");
+  fprintf (stderr, "       or:    relphot -averages -region RA RA DEC DEC\n");
+  fprintf (stderr, "  options: \n");
+  fprintf (stderr, "  -time (start) (stop)\n");
+  fprintf (stderr, "  -v\n");
+  fprintf (stderr, "  -plot\n");
+  fprintf (stderr, "  -plotdelay (seconds)\n");
+  fprintf (stderr, "  -statmode (mode)\n");
+  fprintf (stderr, "  -n (nloop)\n");
+  fprintf (stderr, "  -reset\n");
+  fprintf (stderr, "  -update\n");
+  fprintf (stderr, "  -params\n");
+  fprintf (stderr, "  -mosaic (mosaic)\n");
+  fprintf (stderr, "  -imfreeze\n");
+  fprintf (stderr, "  -grid\n");
+  fprintf (stderr, "  -area Xmin Xmax Ymin Ymax\n");
+  fprintf (stderr, "  -instmag min max\n");
+  fprintf (stderr, "  \n");
+  exit (2);
+} 
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/bcatalog.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/bcatalog.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/bcatalog.c	(revision 17422)
@@ -0,0 +1,120 @@
+# include "relphot.h"
+
+int bcatalog (Catalog *subcatalog, Catalog *catalog) {
+  
+  int i, j, offset, ecode;
+  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm;
+  float mag;
+
+  // XXX PhotNsec as a global is a bad idea; either get it from catalog
+  // or get it from:
+  // Nsecfilt = GetPhotcodeNsecfilt ();
+  // assert (catalog[0].Nsecfilt == Nsecfilt);
+
+  /* we are moving only the subset of measurements from catalog[0] to subcatalog[0] */
+  NAVERAGE = 50;
+  NMEASURE = 1000;
+  ALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+  ALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*PhotNsec);
+  ALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+  Nmeasure = Naverage = 0;
+
+  /* exclude stars not in range or with too few measurements */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    // if (catalog[0].average[i].Nmeasure < 2) continue; 
+
+    /* start with all stars good */
+    subcatalog[0].average[Naverage] = catalog[0].average[i];
+    subcatalog[0].average[Naverage].measureOffset = Nmeasure;
+    for (j = 0; j < PhotNsec; j++) {
+      subcatalog[0].secfilt[PhotNsec*Naverage+j] = catalog[0].secfilt[PhotNsec*i+j];
+    }
+
+    if (RESET) {
+      subcatalog[0].secfilt[PhotNsec*Naverage+PhotSec].M  = NAN;
+      subcatalog[0].secfilt[PhotNsec*Naverage+PhotSec].dM = NAN;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_FEW;
+      subcatalog[0].average[Naverage].code &= ~ID_STAR_POOR;
+    }
+
+    Nm = 0;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
+
+      offset = catalog[0].average[i].measureOffset + j;
+
+      /* select measurements by photcode */
+      ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[offset].photcode);
+      if (ecode != photcode[0].code) continue;
+
+      /* select measurements by time */
+      if (TimeSelect) {
+	if (catalog[0].measure[offset].t < TSTART) continue;
+	if (catalog[0].measure[offset].t > TSTOP) continue;
+      }
+
+      /* select measurements by quality */
+      // XXX ignore this criterion for REF measurements?
+      if (DophotSelect && (catalog[0].measure[offset].dophot != DophotValue)) continue;
+
+      /* select measurements by mag limit */
+      mag = PhotCat (&catalog[0].measure[offset]);
+      if (mag > MAG_LIM) continue;
+
+      /* select measurements by measurement error */
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
+
+      /* select measurements by mag limit */
+      if (ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[offset]);
+	if (mag < ImagMin) continue;
+	if (mag > ImagMax) continue;
+      }
+
+      subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+      subcatalog[0].measure[Nmeasure]        = catalog[0].measure[offset];
+      subcatalog[0].measure[Nmeasure].averef = Naverage;
+      if (RESET) { 
+	subcatalog[0].measure[Nmeasure].Mcal = 0;
+	subcatalog[0].measure[Nmeasure].dbFlags &= 0xff00;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_POOR_PHOTOM;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_AREA;
+	subcatalog[0].measure[Nmeasure].dbFlags &= ~ID_MEAS_NOCAL;
+      }
+      Nmeasure ++;
+      Nm ++;
+      if (Nmeasure == NMEASURE) {
+	NMEASURE += 1000;
+	REALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
+      }
+    }
+
+    // XXXX test : what checks do I need to make elsewhere to avoid problems here?
+    # if 0
+    if (Nm < 2) { /* enough measurements in band? */
+      Nmeasure -= Nm;
+      continue; 
+    }
+    # endif
+    subcatalog[0].average[Naverage].Nmeasure = Nm;
+    Naverage ++;
+    if (Naverage == NAVERAGE) {
+      NAVERAGE += 50;
+      REALLOCATE (subcatalog[0].average, Average, NAVERAGE);
+      REALLOCATE (subcatalog[0].secfilt, SecFilt, NAVERAGE*PhotNsec);
+    }
+  }
+  REALLOCATE (subcatalog[0].average, Average, MAX (Naverage, 1));
+  REALLOCATE (subcatalog[0].measure, Measure, MAX (Nmeasure, 1));
+  REALLOCATE (subcatalog[0].secfilt, SecFilt, PhotNsec*MAX (Naverage, 1));
+  subcatalog[0].Naverage = Naverage;
+  subcatalog[0].Nmeasure = Nmeasure;
+  subcatalog[0].Nsecfilt = catalog[0].Nsecfilt;
+  subcatalog[0].Nsecf_mem = Naverage * catalog[0].Nsecfilt;
+  assert (PhotNsec == catalog[0].Nsecfilt);
+
+  if (VERBOSE) {
+    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/global_stats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/global_stats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/global_stats.c	(revision 17422)
@@ -0,0 +1,44 @@
+# include "relphot.h"
+
+void global_stats (Catalog *catalog, int Ncatalog) {
+
+  StatType stN, stX, stS, imN, imX, imM, imD, msM, msX, msN, msD;
+
+  initstats ("MEAN");
+
+  stN = statsStarN (catalog, Ncatalog);
+  stX = statsStarX (catalog, Ncatalog);
+  stS = statsStarS (catalog, Ncatalog);
+
+  imN = statsImageN (catalog);
+  imX = statsImageX (catalog);
+  imM = statsImageM (catalog);
+  imD = statsImagedM (catalog);
+
+  msN = statsMosaicN (catalog);
+  msM = statsMosaicM (catalog);
+  msD = statsMosaicdM (catalog);
+  msX = statsMosaicX (catalog);
+
+  fprintf (stderr, "STATS            median     mean    sigma      min      max   Nmeas\n");
+  fprintf (stderr, "meas / image:   %7.0f  %7.0f  %7.0f  %7.0f  %7.0f  %6d\n",   imN.median, imN.mean, imN.sigma, imN.min, imN.max, imN.Nmeas);
+  fprintf (stderr, "meas / mosaic:  %7.0f  %7.0f  %7.0f  %7.0f  %7.0f  %6d\n",   msN.median, msN.mean, msN.sigma, msN.min, msN.max, msN.Nmeas);
+  fprintf (stderr, "meas / star:    %7.0f  %7.1f  %7.1f  %7.0f  %7.0f  %6d\n",   stN.median, stN.mean, stN.sigma, stN.min, stN.max, stN.Nmeas);
+
+  fprintf (stderr, "chisq image:    %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   imX.median, imX.mean, imX.sigma, imX.min, imX.max, imX.Nmeas);
+  fprintf (stderr, "chisq mosaic:   %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   msX.median, msX.mean, msX.sigma, msX.min, msX.max, msX.Nmeas);
+  fprintf (stderr, "chisq star:     %7.1f  %7.1f  %7.1f  %7.1f  %7.1f  %6d\n",   stX.median, stX.mean, stX.sigma, stX.min, stX.max, stX.Nmeas);
+
+  fprintf (stderr, "Mcal image:     %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   imM.median, imM.mean, imM.sigma, imM.min, imM.max, imM.Nmeas);
+  fprintf (stderr, "dMcal image:    %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   imD.median, imD.mean, imD.sigma, imD.min, imD.max, imD.Nmeas);
+
+  fprintf (stderr, "Mcal mosaic:    %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   msM.median, msM.mean, msM.sigma, msM.min, msM.max, msM.Nmeas);
+  fprintf (stderr, "dMcal mosaic:   %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n",   msD.median, msD.mean, msD.sigma, msD.min, msD.max, msD.Nmeas);
+
+  fprintf (stderr, "dMrel star:     %7.4f  %7.4f  %7.4f  %7.4f  %7.4f  %6d\n\n", stS.median, stS.mean, stS.sigma, stS.min, stS.max, stS.Nmeas);
+
+  initstats (STATMODE);
+
+}
+
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/initialize.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/initialize.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/initialize.c	(revision 17422)
@@ -0,0 +1,57 @@
+# include "relphot.h"
+
+void initialize (int argc, char **argv) {
+
+  int N;
+
+  ConfigInit (&argc, argv);
+  args (argc, argv);
+
+  if (!UpdateAverages) {
+
+    N = UserPatchSelect ? 1 : 2;
+
+    if ((photcode = GetPhotcodebyName (argv[N])) == NULL) {
+      fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      exit (1);
+    }
+    if (photcode[0].type != PHOT_SEC) {
+      fprintf (stderr, "photcode %s is not a primary or secondary filter\n", argv[N]);
+      exit (1);
+    }
+    PhotSec = GetPhotcodeNsec (photcode[0].code);
+  }
+
+  PhotNsec = GetPhotcodeNsecfilt ();
+
+  initstats (STATMODE);
+
+  IMAGE_BAD = ID_IMAGE_POOR | ID_IMAGE_FEW | ID_IMAGE_SKIP;
+  STAR_BAD  = ID_STAR_POOR | ID_STAR_FEW;
+  MEAS_BAD  = ID_MEAS_NOCAL | ID_MEAS_POOR_PHOTOM | ID_MEAS_SKIP_PHOTOM | ID_MEAS_AREA;
+
+  if (SHOW_PARAMS) {
+    fprintf (stderr, "current parameter settings:\n");
+    if (TimeSelect) {
+      fprintf (stderr, "TimeSelect: TRUE (%s - %s)\n", ohana_sec_to_date (TSTART), ohana_sec_to_date (TSTOP));
+    } else {
+      fprintf (stderr, "TimeSelect: FALSE\n");
+    }
+    fprintf (stderr, "VERBOSE: %d, PLOTSTUFF: %d\n", VERBOSE, PLOTSTUFF);
+    fprintf (stderr, "GRID_X: %d, GRID_Y: %d, BINNING: %d == Nmx: %d, Nmy: %d\n", 
+	     RELPHOT_GRID_X, 
+	     RELPHOT_GRID_Y, 
+	     RELPHOT_GRID_BINNING, 
+	     (int)(RELPHOT_GRID_X/RELPHOT_GRID_BINNING), (int)(RELPHOT_GRID_Y/RELPHOT_GRID_BINNING));
+
+    fprintf (stderr, "MAG_LIM                %lf\n", MAG_LIM);
+    fprintf (stderr, "STAR_SCATTER           %lf\n", STAR_SCATTER);
+    fprintf (stderr, "IMAGE_SCATTER          %lf\n", IMAGE_SCATTER);
+    fprintf (stderr, "IMAGE_OFFSET           %lf\n", IMAGE_OFFSET);
+    fprintf (stderr, "IMAGE_CATALOG          %s\n",  ImageCat);
+    fprintf (stderr, "GSCFILE                %s\n",  GSCFILE);
+    fprintf (stderr, "CATDIR                 %s\n",  CATDIR);
+    exit (0);
+  }
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/liststats.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/liststats.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/liststats.c	(revision 17422)
@@ -0,0 +1,119 @@
+# include "relphot.h"
+
+enum {M_MEAN, M_MEDIAN, M_WT_MEAN, M_INNER_MEAN, 
+      M_INNER_WTMEAN, M_CHI_INNER_MEAN, M_CHI_INNER_WTMEAN};
+
+static int statmode;
+
+void initstats (char *mode) {
+
+  statmode = -1;
+  if (!strcmp (mode, "MEAN")) statmode = M_MEAN;
+  if (!strcmp (mode, "MEDIAN")) statmode = M_MEDIAN;
+  if (!strcmp (mode, "WT_MEAN")) statmode = M_WT_MEAN;
+  if (!strcmp (mode, "INNER_MEAN")) statmode = M_INNER_MEAN;
+  if (!strcmp (mode, "INNER_WTMEAN")) statmode = M_INNER_WTMEAN;
+  if (!strcmp (mode, "CHI_INNER_MEAN")) statmode = M_CHI_INNER_MEAN;
+  if (!strcmp (mode, "CHI_INNER_WTMEAN")) statmode = M_CHI_INNER_WTMEAN;
+
+  if (statmode == -1) {
+    fprintf (stderr, "ERROR: invalid stats mode: %s\n", mode);
+    exit (1);
+  }
+}
+
+int liststats (double *value, double *dvalue, int N, StatType *stats) {
+  
+  int i, ks, ke, Nm;
+  double Mo, dMo, M, dM, X2, dS, *chi;
+
+  ke = ks = dMo = 0;
+
+  stats[0].Nmeas = N;
+  stats[0].mean  = 0;
+  stats[0].sigma = 0;
+  stats[0].error = 0;
+  stats[0].chisq = 0;
+  if (N < 1) return (FALSE);
+
+  dsortpair (value, dvalue, N);
+  stats[0].median = value[(int)(0.5*N)];
+  stats[0].min    = value[0];
+  stats[0].max    = value[N-1];
+
+  switch (statmode) {
+  case M_MEDIAN:
+    ks = 0;
+    ke = N;
+    Mo = stats[0].median;
+    Nm = N;
+    goto chisq;
+    break;
+  case M_MEAN:
+  case M_WT_MEAN:
+    ks = 0;
+    ke = N;
+    break;
+  case M_INNER_MEAN:
+  case M_INNER_WTMEAN:
+  case M_CHI_INNER_MEAN:
+  case M_CHI_INNER_WTMEAN:
+    ks = 0.25*N + 0.50;
+    ke = 0.75*N + 0.25;
+    if (N <= 3) {
+      ks = 0;
+      ke = N;
+    }
+    break;
+  }    
+
+  if ((statmode == M_CHI_INNER_MEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    ALLOCATE (chi, double, N);
+    for (i = 0; i < N; i++) {
+      chi[i] = (value[i] - stats[0].median) / dvalue[i];
+    }
+    dsortthree (chi, value, dvalue, N);
+    free (chi);
+  }
+
+  /* calculating the per-star offset based on the weighted average */
+  M = dM = Nm = 0;
+  if ((statmode == M_WT_MEAN) || (statmode == M_INNER_WTMEAN) || (statmode == M_CHI_INNER_WTMEAN)) {
+    for (i = ks; i < ke; i++) {
+      M   += value[i] / SQ (dvalue[i]);
+      dM  += 1.0 / SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / dM;
+    dMo = sqrt (1.0 / dM);
+  } else {
+    for (i = ks; i < ke; i++) {
+      M   += value[i];
+      dM  += SQ (dvalue[i]);
+      Nm  ++;  
+    }	
+    Mo = M / (double) Nm;
+    dMo = sqrt (dM / (double) Nm);
+  }
+
+ chisq:
+  /* find sigma and chisq */
+  X2 = dS = 0;
+  for (i = ks; i < ke; i++) {
+    M  = SQ (value[i] - Mo);
+    dM = SQ (dvalue[i]);
+    X2 += M / dM;
+    dS += M;
+  }
+  X2 = X2 / Nm;
+  dS = sqrt (dS / Nm);
+
+  stats[0].mean  = Mo;
+  stats[0].Nmeas = Nm;
+  stats[0].chisq = X2;
+  stats[0].sigma = dS;
+  stats[0].error = dMo;
+
+  return (TRUE);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/load_catalogs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/load_catalogs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/load_catalogs.c	(revision 17422)
@@ -0,0 +1,52 @@
+# include "relphot.h"
+
+Catalog *load_catalogs (SkyList *skylist, int *Ncatalog) {
+
+  int i, Nstar;
+  Catalog *catalog, tcatalog;
+
+  if (VERBOSE) fprintf (stderr, "loading catalog data\n");
+
+  ALLOCATE (catalog, Catalog, skylist[0].Nregions);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    tcatalog.filename = skylist[0].filename[i];
+    tcatalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    tcatalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    tcatalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF;    // don't need to load all data at this point
+    tcatalog.Nsecfilt  = GetPhotcodeNsecfilt ();               // set the desired number in case we need to create the catalog
+
+    if (!dvo_catalog_open (&tcatalog, skylist[0].regions[i], VERBOSE, "r")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", tcatalog.filename);
+      exit (1);
+    }
+    if (VERBOSE && !tcatalog.Naves_disk) fprintf (stderr, "no data in %s, skipping\n", tcatalog.filename);
+
+    // select only the brighter stars
+    bcatalog (&catalog[i], &tcatalog);
+    dvo_catalog_unlock (&tcatalog);
+    dvo_catalog_free (&tcatalog);
+  }
+
+  // XXX keep this test?
+  Nstar = 0;
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    Nstar += catalog[i].Naverage;
+  }
+  if (Nstar < 2) { 
+    fprintf (stderr, "warning: insufficient stars %d\n", Nstar);
+  }
+
+  // XXX consider only returning the populated catalogs
+  *Ncatalog = skylist[0].Nregions;
+  return (catalog);
+}
+
+/* this function loads all relevant catalog files for the first pass.  it currently loads the data
+   read only (SOFT lock) since it assumes the image table has been locked. if we go to the new
+   addstar locking paradigm, in which the images and catalogs are updated independently, then we may
+   need to use an XCLD lock here.  
+*/
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/load_images.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/load_images.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/load_images.c	(revision 17422)
@@ -0,0 +1,54 @@
+# include "relphot.h"
+
+SkyList *load_images (FITS_DB *db, char *regionName, SkyRegion *region, int RegionSelect) {
+
+  Image     *image, *subset;
+  int        Nimage, Nsubset, Nchar;
+  int       *LineNumber;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area
+  if (RegionSelect) {
+    skylist = SkyListByPatch (sky, -1, region);
+  } else {
+    Nchar = strlen(regionName);
+    if (!strcmp (&regionName[Nchar-4], ".cpt")) regionName[Nchar-4] = 0;
+    skylist = SkyListByName (sky, regionName);
+  }
+
+  // convert database table to internal structure
+  image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+
+  // select the images which overlap the selected sky regions
+  subset = select_images (skylist, image, Nimage, &LineNumber, &Nsubset);
+
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
+
+  initImages (subset, Nsubset);
+  initMosaics (subset, Nsubset);
+  
+  return (skylist);
+}
+
+int reload_images (FITS_DB *db) {
+
+  Image     *image;
+  int        Nimage, Nx, i;
+  VTable    *vtable;
+
+  image = getimages (&Nimage);
+
+  vtable = &db[0].vtable;
+
+  gfits_scan (vtable[0].header, "NAXIS1", "%d", 1, &Nx);
+  for (i = 0; i < Nimage; i++) {
+    memcpy (vtable[0].buffer[i], &image[i], Nx);
+  }
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/plot_scatter.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/plot_scatter.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/plot_scatter.c	(revision 17422)
@@ -0,0 +1,60 @@
+# include "relphot.h"
+   
+void plot_scatter (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, N, Ntot;
+  float Mrel, Mcal, Mmos, Mgrid;
+  double *xlist, *ylist, *ilist;
+  Graphdata graphdata;
+
+  Ntot = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      Ntot += catalog[i].average[j].Nmeasure;
+    }
+  }
+  ALLOCATE (xlist, double, Ntot);
+  ALLOCATE (ylist, double, Ntot);
+  ALLOCATE (ilist, double, Ntot);
+
+  N = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      /* calculate the average value for a single star */
+      if (catalog[i].average[j].code & STAR_BAD) continue;  
+      m = catalog[i].average[j].measureOffset;
+
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+	if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue;
+	Mcal = getMcal  (m, i);
+	if (isnan(Mcal)) continue;
+	Mmos = getMmos  (m, i);
+	if (isnan(Mmos)) continue;
+	Mgrid = getMgrid (m, i);
+	if (isnan(Mgrid)) continue;
+
+	Mrel = catalog[i].secfilt[PhotNsec*j+PhotSec].M;
+	xlist[N] = Mrel;
+	ylist[N] = PhotSys  (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]) - Mcal - Mmos - Mgrid - Mrel;
+	ilist[N] = PhotInst (&catalog[i].measure[m]);
+	N++;
+      }
+    }
+  }
+
+  plot_defaults (&graphdata);
+  graphdata.xmin = PlotMmin;
+  graphdata.xmax = PlotMmax;
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, xlist, ylist, N, "mag vs dmag", "Mag.png");
+
+  plot_defaults (&graphdata);
+  graphdata.ymin = PlotdMmin;
+  graphdata.ymax = PlotdMmax;
+  plot_list (&graphdata, ilist, ylist, N, "imag vs dmag", "iMag.png");
+  free (xlist);
+  free (ylist);
+  free (ilist);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/plotstuff.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/plotstuff.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/plotstuff.c	(revision 17422)
@@ -0,0 +1,149 @@
+# include "relphot.h"
+# include <signal.h>
+
+static int Xgraph[5] = {0,0,0,0,0};
+static int active;
+
+/*
+static union { unsigned char c[4]; float f; } f_undef = { {0xff, 0xff, 0xff, 0xfe} };
+# define fUNDEF (f_undef.f)
+*/
+
+static union { unsigned char c[8]; float d; } d_undef = { {0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00} };
+# define dUNDEF (d_undef.d)
+
+void XDead () {
+  signal (SIGPIPE, XDead);
+  fprintf (stderr, "kapa is dead, must restart\n");
+  Xgraph[active] = -1;
+}
+
+int open_graph (int N) {
+
+  char name[100];
+
+  sprintf (name, "gastro [%d]", N);
+
+  // XXX need to allow for KapaOpenNamed socket instead
+  Xgraph[N] = KapaOpen ("kapa", name);
+
+  if (Xgraph[N] < 0) {
+    fprintf (stderr, "error starting kapa\n");
+    return (FALSE);
+  }
+  return (TRUE);
+}
+
+void DonePlotting (Graphdata *graphmode, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaBox (Xgraph[N], graphmode);
+  return;
+}
+
+void JpegPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaPNG (Xgraph[N], filename);
+  return;
+}
+
+void PSPlot (Graphdata *graphmode, int N, char *filename) {
+
+  if (Xgraph[N] == 0) return;
+
+  KiiPS (Xgraph[N], filename, TRUE, KAPA_PS_NEWPLOT, "default");
+  return;
+}
+
+void PrepPlotting (int Npts, Graphdata *graphmode, int N) {
+
+  if (Npts < 1) return;
+
+  if (Xgraph[N] < 1) if (!open_graph(N)) return;
+
+  KapaClearSections (Xgraph[N]);
+
+  KapaPrepPlot (Xgraph[N], Npts, graphmode);
+  return;
+}
+
+void PlotLabel (char *string, int N) {
+
+  if (Xgraph[N] == 0) return;
+
+  KapaSendLabel (Xgraph[N], string, 2);
+}
+
+void PlotVector (int Npts, double *vect, int mode, int N, char *type) {
+
+  float *values;
+  int i;
+
+  if (Npts < 1) return;
+
+  ALLOCATE (values, float, Npts);
+  for (i = 0; i < Npts; i++) {
+    values[i] = vect[i];
+  }
+
+  KapaPlotVector (Xgraph[N], Npts, values, type);
+  free (values);
+  return;
+}
+
+void plot_list (Graphdata *graphdata, double *xlist, double *ylist, int N, char *label, char *file) {
+
+  int i;
+  StatType stats;
+  
+  stats.min = stats.max = xlist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, xlist[i]);
+    stats.max = MAX (stats.max, xlist[i]);
+  }
+  if (graphdata[0].xmin == dUNDEF) graphdata[0].xmin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].xmax == dUNDEF) graphdata[0].xmax = 1.05*stats.max - 0.05*stats.min;
+
+  stats.min = stats.max = ylist[0];
+  for (i = 0; i < N; i++) {
+    stats.min = MIN (stats.min, ylist[i]);
+    stats.max = MAX (stats.max, ylist[i]);
+  }
+  if (graphdata[0].ymin == dUNDEF) graphdata[0].ymin = 1.05*stats.min - 0.05*stats.max;
+  if (graphdata[0].ymax == dUNDEF) graphdata[0].ymax = 1.05*stats.max - 0.05*stats.min;
+
+  PrepPlotting (N, graphdata, 0);
+  PlotVector (N, xlist, 0, 0, "x");
+  PlotVector (N, ylist, 1, 0, "y");
+  if (label != NULL) PlotLabel (label, 0);
+  DonePlotting (graphdata, 0);
+
+  if ((file != NULL) && SAVEPLOT) JpegPlot (graphdata, 0, file);
+  if (PLOTDELAY > 500000) {
+    fprintf (stdout, "press return\n"); 
+    fscanf (stdin, "%*c");
+  } else {
+    usleep (PLOTDELAY);
+  }
+}
+
+void plot_defaults (Graphdata *graphdata) {
+
+  KapaInitGraph (graphdata);
+  graphdata[0].style = 2;
+  graphdata[0].ptype = 2;
+  graphdata[0].ltype = 0;
+  graphdata[0].etype = 0;
+  graphdata[0].color = black;
+  graphdata[0].lweight = 0;
+  graphdata[0].size = 0.5;
+
+  graphdata[0].xmin = dUNDEF;
+  graphdata[0].xmax = dUNDEF;
+  graphdata[0].ymin = dUNDEF;
+  graphdata[0].ymax = dUNDEF;
+   
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/reload_catalogs.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/reload_catalogs.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/reload_catalogs.c	(revision 17422)
@@ -0,0 +1,55 @@
+# include "relphot.h"
+
+void reload_catalogs (SkyList *skylist) {
+
+  int i;
+  int status;
+  struct stat filestat;
+  Catalog catalog;
+
+  if (VERBOSE) fprintf (stderr, "re-loading catalog data\n");
+
+  /* load data from each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) {
+    catalog.filename = skylist[0].filename[i];
+
+    // only update existing db tables
+    status = stat (catalog.filename, &filestat);
+    if ((status == -1) && (errno == ENOENT)) {
+      if (VERBOSE) fprintf (stderr, "no file %s, skipping\n", catalog.filename);
+      continue;
+    }
+
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();               // set the desired number in case we need to create the catalog
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (VERBOSE && (catalog.Naves_disk == 0)) {
+	fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	continue;
+    }
+
+    initImageBins  (&catalog, 1);
+    initMosaicBins (&catalog, 1);
+    initGridBins   (&catalog, 1);
+
+    findImages (&catalog, 1);
+    findMosaics (&catalog, 1);
+
+    setMrelFinal (&catalog);
+    dvo_catalog_save (&catalog, VERBOSE); 
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+
+    freeImageBins (1);
+    freeMosaicBins (1);
+    freeGridBins (1);
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot.c	(revision 17422)
@@ -0,0 +1,126 @@
+# include "relphot.h"
+
+int main (int argc, char **argv) {
+
+  int i, status, Ncatalog;
+  Catalog *catalog;
+  FITS_DB db;
+
+  SkyList *skylist = NULL;
+
+  /* get configuration info, args */
+  initialize (argc, argv);
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (UpdateAverages) {
+    relphot_objects ();
+    exit (0);
+  }
+
+  /* register database handle with shutdown procedure */
+  set_db (&db);
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status && UPDATE) {
+    fprintf (stderr, "error\n");
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+  // if the file is missing, db.dbstate will have a value of either:
+  // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    // XXX get ZERO_POINT from config
+    dvo_image_create (&db, 25.0);
+    // Shutdown ("ERROR: No images in catalog %s (1)", db.filename);
+  } else {
+    if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+  }
+
+  /* load regions and images based on specified sky patch */
+  // XXX need to mimic old-style load by passing patch name
+  // XXX need to reduce number of global variables in use.
+  // XXX this is fairly lame: argv[1] is photcode if UserPatchSelect is true
+  skylist = load_images (&db, argv[1], &UserPatch, UserPatchSelect);
+
+  /* unlock, if we can (else, unlocked below) */
+  if (!UPDATE) dvo_image_unlock (&db); 
+
+  /* load catalog data from region files */
+  catalog = load_catalogs (skylist, &Ncatalog);
+  
+  /* add in a loop over the catalogs calling dvo_catalog_chipcoords */
+
+  /* match measurements with images, mosaics */
+  initImageBins  (catalog, Ncatalog);
+  initMosaicBins (catalog, Ncatalog);
+  initGridBins   (catalog, Ncatalog);
+  initMrel (catalog, Ncatalog);
+
+  findImages (catalog, Ncatalog);
+  findMosaics (catalog, Ncatalog);  /* also sets Grid values */
+  SAVEPLOT = FALSE;
+
+  setExclusions (catalog, Ncatalog);
+
+  if (PLOTSTUFF) {
+    plot_star_coords (catalog, Ncatalog);
+    plot_mosaic_fields (catalog);
+  }
+
+  /* determine fit values */
+  for (i = 0; i < NLOOP; i++) {
+    setMrel  (catalog, Ncatalog);
+    setMcal  (catalog, FALSE);
+    setMmos  (catalog, FALSE);
+    setMgrid (catalog);
+    
+    if (PLOTSTUFF) {
+      plot_scatter (catalog, Ncatalog); 
+      plot_grid (catalog); 
+      plot_mosaics ();
+      plot_images ();
+      plot_stars (catalog, Ncatalog);
+      plot_chisq (catalog, Ncatalog);
+    }
+    if ((i == 1) || (i == 5) || (i ==  9) || (i == 13)) clean_measures (catalog, Ncatalog, FALSE); 
+    if ((i == 2) || (i == 6) || (i == 10) || (i == 14)) clean_stars (catalog, Ncatalog);
+    if ((i == 4) || (i == 8) || (i == 12) || (i == 16)) clean_mosaics (catalog, Ncatalog);
+    if ((i == 4) || (i == 8) || (i == 12) || (i == 16)) clean_images ();
+    global_stats (catalog, Ncatalog);
+  }
+
+  SAVEPLOT = TRUE;
+  plot_scatter (catalog, Ncatalog); 
+  plot_grid (catalog); 
+  plot_mosaics ();
+  plot_images ();
+  plot_stars (catalog, Ncatalog);
+  plot_chisq (catalog, Ncatalog);
+
+  if (USE_GRID) dump_grid ();
+  if (!UPDATE) exit (0);
+
+  /* set Mcal & Mmos for bad images */
+  setMcal  (catalog, TRUE);
+  setMmos  (catalog, TRUE);
+
+  /* at this point, we have correct cal coeffs in the image/mosaic structures */
+  for (i = 0; i < Ncatalog; i++) dvo_catalog_free (&catalog[i]);
+  freeImageBins (Ncatalog);
+  freeMosaicBins (Ncatalog);
+  freeGridBins (Ncatalog);
+
+  /* load catalog data from region files, update Mrel include all data */
+  reload_catalogs (skylist);
+  setMcalFinal ();
+
+  reload_images (&db);
+  
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db); 
+
+  exit (0);
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot_objects.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot_objects.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/relphot_objects.c	(revision 17422)
@@ -0,0 +1,65 @@
+# include "relphot.h"
+
+int relphot_objects () {
+
+  int i, j, k, m;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // XXX consider what gets reset (only PHOTOM flags)
+    if (RESET) {
+      for (j = 0; j < catalog.Naverage; j++) {
+	catalog.average[j].code = 0;
+	m = catalog.average[j].measureOffset;
+	for (k = 0; k < catalog.average[j].Nmeasure; k++) {
+	  catalog.measure[m+k].dbFlags = 0;
+	}
+      }
+    }
+
+    setMave (&catalog, 1);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    if (VERBOSE) fprintf (stderr, "saving catalog %s\n", catalog.filename);
+    dvo_catalog_save (&catalog, VERBOSE); 
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+  
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/select_images.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/select_images.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/select_images.c	(revision 17422)
@@ -0,0 +1,239 @@
+# include "relphot.h"
+
+/* this function returns a list of all images which overlap the given SkyList (set of
+   SkyRegions).  All images in the image catalog are tested once, so there is no check that an
+   image already has been included.  LineNum stores the locations in the Image database of the
+   list of images */
+
+typedef struct {
+  double Xc[5];
+  double Yc[5];
+  double Rc;
+  double Dc;
+} SkyRegionCoords;
+
+Image *select_images (SkyList *skylist, Image *timage, int Ntimage, int **LineNumber, int *Nimage) {
+  
+  Image *image;
+  int i, j, k, m, found, nimage, NIMAGE;
+  int InRange, ecode;
+  double Ri[5], Di[5], Xi[5], Yi[5], dx, dy;
+  int *line_number;
+  Coords tcoords;
+  SkyRegionCoords *skycoords;
+  
+  if (skylist[0].Nregions < 1) {
+    *Nimage = 0;
+    *LineNumber = NULL;
+    if (VERBOSE) fprintf (stderr, "no matching sky regions\n");
+    return NULL;
+  }
+
+  // the comparison is made in the catalog local projection. below we set crval1,2
+  tcoords.crpix1 = tcoords.crpix2 = 0.0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
+  strcpy (tcoords.ctype, "RA---TAN");
+
+  ALLOCATE (skycoords, SkyRegionCoords, skylist[0].Nregions);
+
+  /* compare with each region file */
+  for (i = 0; i < skylist[0].Nregions; i++) { 
+
+    /* we make positional comparisons in the projection of catalog */
+    skycoords[i].Rc = 0.5*(skylist[0].regions[i][0].Rmax + skylist[0].regions[i][0].Rmin);
+    skycoords[i].Dc = 0.5*(skylist[0].regions[i][0].Dmax + skylist[0].regions[i][0].Dmin);
+    tcoords.crval1 = skycoords[i].Rc;
+    tcoords.crval2 = skycoords[i].Dc;
+
+    /* define catalog corners */
+    RD_to_XY (&skycoords[i].Xc[0], &skycoords[i].Yc[0], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[1], &skycoords[i].Yc[1], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmin, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[2], &skycoords[i].Yc[2], skylist[0].regions[i][0].Rmax, skylist[0].regions[i][0].Dmax, &tcoords);
+    RD_to_XY (&skycoords[i].Xc[3], &skycoords[i].Yc[3], skylist[0].regions[i][0].Rmin, skylist[0].regions[i][0].Dmax, &tcoords);
+    skycoords[i].Xc[4] = skycoords[i].Xc[0];    
+    skycoords[i].Yc[4] = skycoords[i].Yc[0];    
+
+    dx = 0.02*(skycoords[i].Xc[2] - skycoords[i].Xc[0]);
+    dy = 0.02*(skycoords[i].Yc[2] - skycoords[i].Yc[0]);
+    skycoords[i].Xc[0] -= dx; skycoords[i].Yc[0] -= dy;
+    skycoords[i].Xc[1] += dx; skycoords[i].Yc[1] -= dy;
+    skycoords[i].Xc[2] += dx; skycoords[i].Yc[2] += dy;
+    skycoords[i].Xc[3] -= dx; skycoords[i].Yc[3] += dy;
+    skycoords[i].Xc[4] -= dx; skycoords[i].Yc[4] -= dy;
+  }
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+  BuildChipMatch (timage, Ntimage);
+
+  nimage = 0;
+  NIMAGE = 100;
+  ALLOCATE (image, Image, NIMAGE);
+  ALLOCATE (line_number, int, NIMAGE);
+  
+  // go through the complete list of images, selecting ones which overlap any region
+  for (i = 0; i < Ntimage; i++) {
+      
+    /* exclude images by photcode */
+    ecode = GetPhotcodeEquivCodebyCode (timage[i].photcode);
+    if (ecode != photcode[0].code) continue;
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (timage[i].tzero < TSTART) continue;
+      if (timage[i].tzero > TSTOP) continue;
+    }
+    
+    if (!FindMosaicForImage (timage, Ntimage, i)) continue;
+
+    /* define image corners */
+    Xi[0] = 0;            Yi[0] = 0;
+    Xi[1] = timage[i].NX; Yi[1] = 0;
+    Xi[2] = timage[i].NX; Yi[2] = timage[i].NY;
+    Xi[3] = 0;            Yi[3] = timage[i].NY;
+    Xi[4] = 0;            Yi[4] = 0;
+    found = FALSE;
+
+    /* transform to ra,dec */
+    for (j = 0; j < 5; j++) {
+      XY_to_RD (&Ri[j], &Di[j], Xi[j], Yi[j], &timage[i].coords);
+    }
+
+    /* compare with each region file */
+    for (m = 0; (m < skylist[0].Nregions) && !found; m++) { 
+
+      /* we make positional comparisons in the projection of catalog */
+      tcoords.crval1 = skycoords[m].Rc;
+      tcoords.crval2 = skycoords[m].Dc;
+
+      /* transform to ra,dec */
+      InRange = TRUE;
+      for (j = 0; (j < 5) && InRange; j++) {
+	InRange = RD_to_XY (&Xi[j], &Yi[j], Ri[j], Di[j], &tcoords);
+      }
+      if (!InRange) continue;
+
+      /* check if image corner inside catalog */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&Xi[j], &Yi[j], &skycoords[m].Xc[0], &skycoords[m].Yc[0]);
+      }
+      /* check if catalog corner inside image */
+      for (j = 0; (j < 4) && !found; j++) {
+	found = corner_check (&skycoords[m].Xc[j], &skycoords[m].Yc[j], &Xi[0], &Yi[0]);
+      }
+      /* check if edges cross */
+      for (j = 0; (j < 4) && !found; j++) {
+	for (k = 0; (k < 4) && !found; k++) {
+	  found = edge_check (&Xi[j], &Yi[j], &skycoords[m].Xc[k], &skycoords[m].Yc[k]);
+	}
+      }
+      if (!found) continue;
+
+      image[nimage] = timage[i]; 
+      /* always allow 'few' images to succeed, if possible */
+      if (image[nimage].code & ID_IMAGE_FEW) { 
+	image[nimage].code &= ~(ID_IMAGE_FEW | ID_IMAGE_POOR);
+      }
+      if (RESET) {
+	assignMcal (&image[nimage], (double *) NULL, -1);
+	image[nimage].dMcal = NAN;
+	image[nimage].code &= ~ID_IMAGE_POOR;
+      }
+      line_number[nimage] = i;
+      nimage ++;
+      if (nimage == NIMAGE) {
+	NIMAGE += 100;
+	REALLOCATE (image, Image, NIMAGE);
+	REALLOCATE (line_number, int, NIMAGE);
+      }
+    }
+  }
+      
+  if (VERBOSE) fprintf (stderr, "found %d images\n", nimage);
+
+  REALLOCATE (image, Image, MAX (nimage, 1));
+  REALLOCATE (line_number, int, MAX (nimage, 1));
+  free (skycoords);
+
+  *Nimage  = nimage;
+  *LineNumber = line_number;
+  return (image);
+}
+
+/* check if line between points 0 and 1 of x1
+   crosses line between points 0 and 1 of x2 */
+int edge_check (double *x1, double *y1, double *x2, double *y2) {
+
+  double theta1, theta2;
+  double Theta1, Theta2;
+
+  theta1 = opening_angle (x1[0], y1[0], x2[0], y2[0], x1[1], y1[1]); 
+  theta2 = opening_angle (x1[0], y1[0], x2[0], y2[0], x2[1], y2[1]); 
+
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  Theta1 = theta1;
+  Theta2 = theta2;
+  theta1 = opening_angle (x2[0], y2[0], x1[1], y1[1], x2[1], y2[1]); 
+  theta2 = opening_angle (x2[0], y2[0], x1[1], y1[1], x1[0], y1[0]); 
+  
+ 
+  if (theta1*theta2 < 0.0) {
+    return (FALSE);
+  }
+
+  if (fabs(theta1) < fabs(theta2)) {
+    return (FALSE);
+  }
+
+  return (TRUE);
+
+}
+
+/* check if point x1,y1 is in box formed by x2[0-4] */
+int corner_check (double *x1, double *y1, double *x2, double *y2) {
+
+  int i;
+  double theta;
+
+  theta = 0;
+
+  for (i = 0; i < 4; i++) {
+    theta += opening_angle (x2[i], y2[i], x1[0], y1[0], x2[i+1], y2[i+1]); 
+  }
+  if (fabs(theta) > 6) {
+    return (TRUE);
+  } else {
+    return (FALSE);
+  }
+}
+
+/* returns the opening angle between the three points (2 is in middle) 
+   in range -pi to pi */
+
+double opening_angle (double x1, double y1, double x2, double y2, double x3, double y3) {
+
+  double dx1, dy1, dx2, dy2, ct, st, theta;
+
+  dx1 = x1 - x2;
+  dy1 = y1 - y2;
+  
+  dx2 = x3 - x2;
+  dy2 = y3 - y2;
+  
+  ct = (dx1*dx2 + dy1*dy2);
+  st = (dx1*dy2 - dx2*dy1);
+
+  theta = atan2 (st, ct);
+
+  return (theta);
+
+}
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/setExclusions.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/setExclusions.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/setExclusions.c	(revision 17422)
@@ -0,0 +1,55 @@
+# include "relphot.h"
+
+int setExclusions (Catalog *catalog, int Ncatalog) {
+
+  int i, j, k, m, Narea, Nnocal, Ngood, ecode;
+  Coords *coords;
+  double r, d, x, y;
+
+  Ngood = Nnocal = Narea = 0;
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      m = catalog[i].average[j].measureOffset;
+      for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+
+	/* select measurements by photcode */
+	ecode = GetPhotcodeEquivCodebyCode (catalog[i].measure[m].photcode);
+	if (ecode != photcode[0].code) goto mark_nocal;
+	
+	/* select measurements by time */
+	if (TimeSelect) {
+	  if (catalog[i].measure[m].t < TSTART) goto mark_nocal;
+	  if (catalog[i].measure[m].t > TSTOP) goto mark_nocal;
+	}
+
+	/* select measurements by mag limit */
+	if (AreaSelect) {
+	  r = catalog[i].average[j].R + catalog[i].measure[m].dR / 3600.0;
+	  d = catalog[i].average[j].D + catalog[i].measure[m].dD / 3600.0;
+	  if ((coords = getCoords (m, i)) == NULL) goto markbad;
+	  RD_to_XY (&x, &y, r, d, coords);
+	  if (x < AreaXmin) goto markbad;
+	  if (x > AreaXmax) goto markbad;
+	  if (y < AreaYmin) goto markbad;
+	  if (y > AreaYmax) goto markbad;
+	}
+	Ngood ++;
+	continue;
+
+      markbad:
+	catalog[i].measure[m].dbFlags |= ID_MEAS_AREA;
+	Narea ++;
+	continue;
+	
+      mark_nocal:
+	catalog[i].measure[m].dbFlags |= ID_MEAS_NOCAL;
+	Nnocal ++;
+	continue;
+      }
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "%d measurements marked by area\n", Narea);
+  if (VERBOSE) fprintf (stderr, "%d measurements marked nocal\n", Nnocal);
+  if (VERBOSE) fprintf (stderr, "%d measurements kept for analysis\n", Ngood);
+  return (TRUE);
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/setMrelFinal.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/setMrelFinal.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/setMrelFinal.c	(revision 17422)
@@ -0,0 +1,150 @@
+# include "relphot.h"
+
+void setMrelFinal (Catalog *catalog) {
+
+  int i, j, m, ecode;
+
+  /* if we reset the catalog, reset all the current measurements */
+  if (RESET) {
+
+    for (i = 0; i < catalog[0].Naverage; i++) {
+      catalog[0].secfilt[PhotNsec*i+PhotSec].M  = NAN;
+      catalog[0].secfilt[PhotNsec*i+PhotSec].dM = NAN;
+      catalog[0].secfilt[PhotNsec*i+PhotSec].Xm = NAN_S_SHORT;
+
+      m = catalog[0].average[i].measureOffset;
+      for (j = 0; j < catalog[0].average[i].Nmeasure; j++, m++) {
+	
+	/* select measurements by photcode */
+	ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[m].photcode);
+	if (ecode != photcode[0].code) continue;
+	
+	/* select measurements by time */
+	if (TimeSelect) {
+	  if (catalog[0].measure[m].t < TSTART) continue;
+	  if (catalog[0].measure[m].t > TSTOP) continue;
+	}
+	
+	catalog[0].measure[m].Mcal = 0;
+	catalog[0].measure[m].dbFlags &= 0xff00;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_POOR_PHOTOM;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_AREA;
+	catalog[0].measure[m].dbFlags &= ~ID_MEAS_NOCAL;
+      }
+    }
+  }
+
+  setExclusions (catalog, 1);  /* mark by area */
+
+  /* set catalog[0].found[i] = FALSE */
+  ALLOCATE (catalog[0].found, int, MAX (1, catalog[0].Naverage));
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    catalog[0].found[i] = FALSE;
+  }
+
+  for (i = 0; i < 5; i++) {
+    skip_measurements (catalog, i);       /* set ID_MEAS_SKIP for measures to be skipped */
+    setMrelOutput  (catalog, 1, FALSE);   /* set Mrel using all measures not skipped */
+    clean_measures (catalog, 1, TRUE);    /* mark outliers ID_MEAS_POOR_PHOTOM */
+    setMrelOutput  (catalog, 1, TRUE);    /* set Mrel using remaining measures */
+  }
+  setMcalOutput (catalog, 1);
+
+  /* clear ID_STAR_POOR, ID_STAR_FEW, ID_MEAS_NOCAL values before writing ??? */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    catalog[0].average[i].code &= ~ID_STAR_FEW;
+    catalog[0].average[i].code &= ~ID_STAR_POOR;
+    m = catalog[0].average[i].measureOffset;
+    for (j = 0; j < catalog[0].average[i].Nmeasure; j++, m++) {
+      catalog[0].measure[m].dbFlags &= ~ID_MEAS_NOCAL;
+    }
+  }
+}
+
+/* ID_MEAS_SKIP marks measurements which were not used to calculate Mrel */
+void skip_measurements (Catalog *catalog, int pass) {
+
+  int i, k, m, ecode, d1, d2;
+  int Ntot, Ntry, Nkeep, Nskip;
+  float mag;
+
+  Ntot = Ntry = Nskip = Nkeep = 0;
+
+  /* allow measures from images marked POOR and FEW */
+  if (pass >= 3) IMAGE_BAD = ID_IMAGE_NOCAL;
+  
+  /* allow measures marked as outliers (POOR) and off image region (AREA) */
+  if (pass >= 3) MEAS_BAD = ID_MEAS_NOCAL | ID_MEAS_SKIP_PHOTOM;
+
+  /* mark measures which should be ignored on second pass */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    Ntot += catalog[0].average[i].Nmeasure;
+    if (catalog[0].found[i]) continue;
+
+    m = catalog[0].average[i].measureOffset;
+    for (k = 0; k < catalog[0].average[i].Nmeasure; k++, m++) {
+      Ntry++;
+
+      /* clear SKIP for all measures at first */
+      catalog[0].measure[m].dbFlags &= ~ID_MEAS_SKIP_PHOTOM;
+
+      /** never use these measurements (wrong photcode, bad time range) */
+      /* skipped via NOCAL, don't mark as skipped */
+      ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[m].photcode);
+      if (ecode != photcode[0].code) continue;
+
+      /* skip measurements by time range */
+      if (TimeSelect) {
+	if (catalog[0].measure[m].t < TSTART) goto skip;
+	if (catalog[0].measure[m].t > TSTOP) goto skip;
+      }
+
+      /* skip measurements with sat. dophot values */
+      if ((pass < 4) && (catalog[0].measure[m].dophot == 10)) goto skip;
+
+      /* skip measurements from BAD images and mosaics */
+      /* do NOT skip measurements without a matching image */
+      if (isnan(getMcal (m, 0))) goto skip;
+      if (isnan(getMmos (m, 0))) goto skip;
+
+      /* skip measurements by inst mag limit */
+      if ((pass < 4) && ImagSelect) {
+	mag = PhotInst (&catalog[0].measure[m]);
+	if (mag < ImagMin) goto skip;
+	if (mag > ImagMax) goto skip;
+      }
+
+      d1 = (catalog[0].measure[m].dophot == 1);
+      d2 = (catalog[0].measure[m].dophot == 2);
+
+      /* skip measurements by measurement error */
+      if ((pass < 2) && !(d1 || d2)) goto skip;
+
+      /* if ((pass < 1) && (catalog[0].measure[m].dM > SIGMA_LIM)) goto skip; */
+      if ((pass < 1) && !d1) goto skip;
+      Nkeep ++;
+
+      continue;
+
+    skip:
+      catalog[0].measure[m].dbFlags |= ID_MEAS_SKIP_PHOTOM;
+      Nskip ++;
+    }
+  }
+  if (VERBOSE) fprintf (stderr, "pass %d, Ntot: %d, Ntry: %d, Nskip: %d, Nkeep: %d\n",
+			pass, Ntot, Ntry, Nskip, Nkeep);
+}
+
+/* dophot types:
+
+1 - star
+2 - galaxy
+3 - star
+4 - weak
+5 - edge
+7 - weak
+9 - weak
+10 - sat
+*/
+
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/sort.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/sort.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/sort.c	(revision 17422)
@@ -0,0 +1,182 @@
+# include "relphot.h"
+
+void sortA (double *X, int N) {
+
+  int l,j,ir,i;
+  double tX;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+  }
+}
+
+void sortB (double *X, double *Y, int N) {
+
+  int l,j,ir,i;
+  double tX, tY;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+  }
+}
+
+void sortC (double *X, double *Y, double *F1, double *F2, int N) {
+
+  int l,j,ir,i;
+  double tX, tY;
+  double t1, t2;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+      t1 = F1[l];
+      t2 = F2[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      t1 = F1[ir];
+      F1[ir] = F1[0];
+      t2 = F2[ir];
+      F2[ir] = F2[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	F1[0] = t1;
+	F2[0] = t2;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	F1[i] = F1[j];
+	F2[i] = F2[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+    F1[i] = t1;
+    F2[i] = t2;
+  }
+}
+
+void sortD (double *X, double *Y, double *Z, int N) {
+
+  int l,j,ir,i;
+  double tX, tY, tZ;
+  
+  if (N < 2) return;
+
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tX = X[l];
+      tY = Y[l];
+      tZ = Z[l];
+    }
+    else {
+      tX = X[ir];
+      X[ir] = X[0];
+      tY = Y[ir];
+      Y[ir] = Y[0];
+      tZ = Z[ir];
+      Z[ir] = Z[0];
+      if (--ir == 0) {
+	X[0] = tX;
+	Y[0] = tY;
+	Z[0] = tZ;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && X[j] < X[j+1]) j++;
+      if (tX < X[j]) {
+	X[i] = X[j];
+	Y[i] = Y[j];
+	Z[i] = Z[j];
+	j += (i=j) + 1;
+      }
+      else j = ir + 1;
+    }
+    X[i] = tX;
+    Y[i] = tY;
+    Z[i] = tZ;
+  }
+}
Index: /tags/ipp-2-5-1/Ohana/src/relphot/src/write_coords.c
===================================================================
--- /tags/ipp-2-5-1/Ohana/src/relphot/src/write_coords.c	(revision 17422)
+++ /tags/ipp-2-5-1/Ohana/src/relphot/src/write_coords.c	(revision 17422)
@@ -0,0 +1,29 @@
+# include "relphot.h"
+
+# define CD_COORDS 1
+
+void write_coords (Header *header, Coords *coords) {
+
+  gfits_modify (header, "CTYPE1",   "%s",  1, "RA---TAN");
+  gfits_modify (header, "CTYPE2",   "%s",  1, "DEC--TAN");
+
+  gfits_modify (header, "CRVAL1",   "%lf", 1, coords[0].crval1);
+  gfits_modify (header, "CRVAL2",   "%lf", 1, coords[0].crval2);  
+
+  gfits_modify (header, "CRPIX1",   "%lf", 1, coords[0].crpix1);
+  gfits_modify (header, "CRPIX2",   "%lf", 1, coords[0].crpix2);
+
+# if (CD_COORDS)  
+  gfits_modify (header, "CD1_1",    "%le", 1, coords[0].pc1_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD2_1",    "%le", 1, coords[0].pc2_1 * coords[0].cdelt1);
+  gfits_modify (header, "CD1_2",    "%le", 1, coords[0].pc1_2 * coords[0].cdelt2);
+  gfits_modify (header, "CD2_2",    "%le", 1, coords[0].pc2_2 * coords[0].cdelt2);
+# else
+  gfits_modify (header, "CDELT1",   "%le", 1, coords[0].cdelt1); 
+  gfits_modify (header, "CDELT2",   "%le", 1, coords[0].cdelt2);
+  gfits_modify (header, "PC001001", "%le", 1, coords[0].pc1_1);
+  gfits_modify (header, "PC001002", "%le", 1, coords[0].pc1_2);
+  gfits_modify (header, "PC002001", "%le", 1, coords[0].pc2_1);
+  gfits_modify (header, "PC002002", "%le", 1, coords[0].pc2_2);
+# endif
+}
Index: /tags/ipp-2-5-1/psconfig/.cvsignore
===================================================================
--- /tags/ipp-2-5-1/psconfig/.cvsignore	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/.cvsignore	(revision 17422)
@@ -0,0 +1,3 @@
+tarballs
+psconfig.csh
+psconfig.bash
Index: /tags/ipp-2-5-1/psconfig/INSTALL
===================================================================
--- /tags/ipp-2-5-1/psconfig/INSTALL	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/INSTALL	(revision 17422)
@@ -0,0 +1,180 @@
+
+The psconfig system allows the user to build and install the IPP
+software suite into a location which is flexibly defined by the user.
+The tools here also set up the user's environment variables (PATH,
+PERL5LIB, LIBRARY_PATH, etc) to make use of the installed software.
+With the psconfig tools, it is easy to switch between different
+installed versions or to recompile subsets of the IPP tree.
+
+0. Choose a target installation directory.
+
+Choose a location to store the installed software and configuration
+files.  The psconfig system places the installed binary files by
+default in directories below ~/psconfig.  There will be one directory
+for each version of the installation for a given hardware
+architecture.  To use a different location, place the following line
+in ~/.psconfigrc (otherwise, this is not needed):
+
+set PSCONFDIR = INSTALL_PATH
+
+where INSTALL_PATH is the top-level directory for all binary
+installations. 
+
+1. Set up the psconfig system.
+
+If you have not previously installed the IPP suite, and do not the
+psconfig scripts installed, you need to generate the scripts for your
+install directory.  Run the following command in this directory
+(ipp/psconfig):
+
+psbuild -bootstrap INSTALL_PATH
+
+where INSTALL_PATH is the top-level directory for all binary
+installations.  Then, follow the instructions supplied by that
+command:
+
+** if you use csh, tcsh or equivalent as your shell, add the following to your .cshrc
+    if (-e /home/kiawe/eugene/psconfig/psconfig.csh) then
+      alias  psconfig        "source /home/kiawe/eugene/psconfig/psconfig.csh"
+    else
+      alias  psconfig        "echo psconfig not available"
+    endif
+    psconfig default
+
+** if you use sh, bash or equivalent as your shell, add the following to your .bashrc
+    if [ -f /home/kiawe/eugene/psconfig/psconfig.csh ]; then
+      alias psconfig='source /home/kiawe/eugene/psconfig/psconfig.bash'
+    else
+      alias psconfig='echo psconfig not available'
+    fi
+    psconfig default
+
+Though, in your case, "/home/kiawe/eugene/psconfig/" will be replaced
+by the value of INSTALL_PATH.  After you have set up this alias, you
+will need to source the .cshrc / .bashrc, or open a new shell, to have
+these aliases available.
+
+2. Using psconfig to set / examine your install system:
+
+Before running or compiling the IPP, it is necessary to use psconfig
+to set the installation version:
+
+psconfig (version)
+
+This command sets aliases and environment variables for the current
+shell to point at the IPP installation labeled with the given version
+name and hardware architecture.  For example:
+
+psconfig default
+
+will set the PATH to include ~/psconfig/default.linux/bin on a 32-bit
+linux system, and the other paths to point at the corresponding
+installation directories.
+
+3. Dependencies
+
+NOTE: It is possible to use the tools discussed below to manually check on the
+external dependencies.  However, the psbuild system now allows you to
+build the full suite including dependencies in a single pass.  To use
+this method, skip to section 5.
+
+3.1. External C libraries
+
+The program 'pschecklibs' in this directory will check for required
+system libraries and headers.  It examines the system libraries,
+libraries defined by LD_LIBRARY_PATH, and the installation library
+defined by psconfig.  Any missing dependencies will be listed.
+Tarballs for these libraries may be found on the Pan-STARRS web site
+at:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extlibs-2.5.tgz
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl-2.5.tgz
+
+These should be installed so they will be available in the user's
+path.  The psconfig commands can be used to install these libraries in
+the psconfig location:
+
+'psautogen' replaces autogen.sh
+'psconfigure' replaces configure
+
+3.2 External Perl Modules
+
+The program 'pscheckperl' in this directory will check for required
+Perl modules, and can be used to install them in the appropriate user
+location in the psconfig system.  The command defaults to the latest
+perl installation table (eg, tagsets/ipp-2.5.perl).
+
+pscheckperl
+
+will test for the perl modules specified for the latest ipp release.
+if any modules are missing, they can be download from the Pan-STARRS
+web site:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl
+
+The tarballs should be placed in a directory extperl parallel to the
+ipp directory (two levels up from this directory).  If the tarballs
+are in the correct location, they can be built by supplying the -build
+flag to pscheckperl:
+
+pscheckperl -build
+
+4. Building
+
+To build the full IPP tree using the psconfig system, run 'psbuild' in
+this directory:
+
+psbuild
+
+For additional information on using psbuild, see the listing of
+options below (or type psbuild -help)
+
+5. Single-Pass Build with External Dependencies
+
+If you have not already done so, download the tarball with the
+external libraries and perl modules from:
+
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extlibs-2.5.tgz
+http://pan-starrs.ifa.hawaii.edu/project/IPP/software/extperl-2.5.tgz
+
+It should be possible to build the full IPP installation by issuing
+the single command in this directory (ipp/psconfig):
+
+psbuild -extbuild
+
+------
+
+More options for psbuild:
+
+USAGE: psbuild [options] (distribution)
+     : -version (version) : specify alternate psconfig installation version
+     : -verbose           : give additional information
+     : -extlibs (tarball) : specify the location of the extlibs tarball
+     : -extperl (tarball) : specify the location of the extlibs tarball
+     : -extcheck          : check (but do not build) the external dependencies
+     : -extbuild          : check and build (if needed) the external dependencies
+     : -clean             : clean the source directories before building
+     : -rebuild           : run 'configure (and autogen for developer)' (C code)
+     : -optimize          : set flags for optimized code
+     : -only (module)     : only build the specified module
+     : -start (module)    : begin build at specified module
+     : -stop (module)     : stop build after specified module
+
+     : -dev               : build modules not distributed in tarball
+
+     : psbuild -bootstrap : generate the psconfig scripts
+     : psbuild -list      : list the available distributions
+     : psbuild -h         : this help listing
+     : psbuild -help      : this help listing
+     : psbuild --help     : this help listing
+
+Summary of psconfig operations:
+
+psdist -tag        : tag CVS tree
+psdist -dist       : build tarball from tagged tree
+psdist -dist -head : build tarball from head
+psbuild            : build and install software in tree
+pschecklibs        : check for needed external software
+pscheckperl        : check for needed perl modules
+pscheckperl -build : build and install external modules
+
Index: /tags/ipp-2-5-1/psconfig/README
===================================================================
--- /tags/ipp-2-5-1/psconfig/README	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/README	(revision 17422)
@@ -0,0 +1,21 @@
+
+Welcome to the IPP Software Tree.  To build the full IPP suite, you
+must be in the directory ipp/psconfig.  Follow the instructions in
+'ipp/psconfig/INSTALL'.
+
+The directory 'ipp/psconfig' contains tools for building the IPP, for
+testing the build environment, for building external C libraries and
+Perl modules, for setting up the user's environment, and for building
+a tarball to be distributed to the end users.
+
+Developer Build vs User Build
+
+Within the IPP code base, there are a few modules which are not
+distributed as part of the tarball.  These include 'glueforge', which
+is used to generate code for interacting with the database;
+'dbconfig', which defines a database layout; and 'ippdb.src', which is
+the raw auto-generated code.  The tarball is instead packaged with a
+version of ippdb for which autogen has been run, and for which there
+is a valid 'configure' script.  As a developer, it is necessary to
+build these packages.  There is a '-dev' option for psbuild which
+builds these packages.
Index: /tags/ipp-2-5-1/psconfig/TODO
===================================================================
--- /tags/ipp-2-5-1/psconfig/TODO	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/TODO	(revision 17422)
@@ -0,0 +1,40 @@
+
+2007.02.02 : IPP release 1.0 : Known Outstanding Issues
+
+This release of the IPP software is missing a number of major features
+defined in the IPP System Concept Definition.  There are also a number
+of missing minor features, bells and whistles which have been
+identified.
+
+Major Missing Features:
+
+* Phase 4 Tools.  This release does not include the Phase 4 programs
+pswarp, ppStac, and poisub, and the related infrastructure support in
+ippTools and ippScripts.
+
+* Static Sky tools.  This release does not include a number of elements
+related to the static sky definition: the definition of the static sky
+cells and tools to manipulate them; the static sky surface brightness
+modelling tool; and photometry analysis operations for large, extended
+objects.
+
+* relastro.  The global astrometry analysis software is unimplemented.
+
+* flat-field corrections.  The DVO flat-field correction analysis is
+not integrated with the rest of the IPP detrend creation system.
+
+Minor Missing Features:
+
+* full set of columns in output object tables from Phase 2.
+* ippMonitor user interface missing a variety of options
+* output plots from psphot and pspastro are not exported to ippMonitor
+* this list is incomplete.
+* ETC...
+
+In addition, there are 
+
+* incomplete documentation
+
+* incomplete testing on:
+  - Nebulous integration
+  - DVO integration
Index: /tags/ipp-2-5-1/psconfig/psbuild
===================================================================
--- /tags/ipp-2-5-1/psconfig/psbuild	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/psbuild	(revision 17422)
@@ -0,0 +1,431 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+if (!-d $tagsets || !-r $tagsets || !-x $tagsets) { die "missing the directory of distribution tables: $tagsets\n"; }
+
+$version = "";
+$clean = 0;
+$rebuild = 0;
+$optimize = 0;
+$developer = 0;
+$start = "";
+$stop = "";
+$verbose = 0;
+
+$extlibs = "none";
+$extperl = "none";
+
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+	$version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-verbose") {
+	$verbose = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extlibs") {
+	$extlibs = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-extperl") {
+	$extperl = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-clean") {
+	$clean = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extcheck") {
+	$extlibs = "check";
+	$extperl = "check";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-extbuild") {
+	$extlibs = "build";
+	$extperl = "build";
+        shift; next;
+    }
+    if ($ARGV[0] eq "-rebuild") {
+	$rebuild = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-optimize") {
+	$optimize = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-dev") {
+	$developer = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-only") {
+	$start = $ARGV[1];
+	$stop = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-start") {
+	$start = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-stop") {
+	$stop = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-list") {
+	&list_distributions ();
+    }
+    if ($ARGV[0] eq "-bootstrap") {
+	&bootstrap ();
+    }
+    if ($ARGV[0] eq "-env") {
+	&show_environment ();
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if ( @ARGV == 0) { 
+    @list = <$tagsets/*.dist>;
+    $distribution = $list[-1];
+} else {
+    $distribution = "$tagsets/$ARGV[0].dist";
+}
+
+# generate new psconfig.csh if needed
+if (! -e "psconfig.csh" || ! -e "psconfig.bash") {
+    $psconfdir = $ENV{'PSCONFDIR'};
+    if ($psconfdir eq "") { die "PSCONFDIR not found, run psbuild -bootstrap and follow instructions\n"; }
+    vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+    vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+}
+
+&load_distfile ();
+
+&build_distribution ();
+exit 0;
+
+sub show_environment {
+    
+    # use psconfig.csh to set needed build aliases
+
+    # set the psconfig version:
+    if ("$version" eq "") {
+	$version = $ENV{'PSVERSION'};
+    }
+    if ("$version" eq "") {
+	$version = "default";
+    }
+
+    if (! -e psconfig.csh) {
+	$psconfdir = $ENV{'PSCONFDIR'};
+	if ($psconfdir eq "") { die "PSCONFDIR not found, run psbuild -bootstrap and follow instructions\n"; }
+	vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+	vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+    }
+
+    $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    $psautogen   = `csh psconfig.csh --psautogen $version`;   chomp $psautogen;
+    $psperlbuild = `csh psconfig.csh --psperlbuild $version`; chomp $psperlbuild;
+
+    # print "psconfigure: $psconfigure\n";
+    # print "psautogen:   $psautogen\n";
+    # print "psperlbuild: $psperlbuild\n";
+
+    # set build environment variables
+    ps_setenv (1, "PATH", 		  "--path");
+    ps_setenv (1, "CPATH",		  "--cpath");
+    ps_setenv (1, "ARCH", 		  "--arch");
+    ps_setenv (1, "LIBRARY_PATH",    "--library_path");
+    ps_setenv (1, "LD_LIBRARY_PATH", "--ld_library_path");
+    ps_setenv (1, "PKG_CONFIG_PATH", "--pkg_config_path");
+    ps_setenv (1, "ACLOCAL_FLAGS",   "--aclocal_flags");
+    ps_setenv (1, "PERL5LIB",        "--perl5lib");
+
+    exit 0;
+}
+
+sub build_distribution {
+    
+    # use psconfig.csh to set needed build aliases
+
+    if ($extlibs eq "check") { 
+	$status = vsystem ("pschecklibs");
+	if ($status) { die "failed to find external libraries\n"; }
+    }
+    if ($extlibs eq "build") { 
+	$status = vsystem ("pschecklibs -build");
+	if ($status) { die "failed to build external libraries\n"; }
+    }
+
+    if ($extperl eq "check") { 
+	$status = vsystem ("pschecklperl");
+	if ($status) { die "failed to find external perl modules\n"; }
+    }
+    if ($extperl eq "build") { 
+	$status = vsystem ("pscheckperl -build");
+	if ($status) { die "failed to build external perl modules\n"; }
+    }
+
+    # set the psconfig version:
+    if ("$version" eq "") {
+	$version = $ENV{'PSVERSION'};
+    }
+    if ("$version" eq "") {
+	$version = "default";
+    }
+
+    $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    $psautogen   = `csh psconfig.csh --psautogen $version`;   chomp $psautogen;
+    $psperlbuild = `csh psconfig.csh --psperlbuild $version`; chomp $psperlbuild;
+
+    # print "psconfigure: $psconfigure\n";
+    # print "psautogen:   $psautogen\n";
+    # print "psperlbuild: $psperlbuild\n";
+
+    # set build environment variables
+    ps_setenv (0, "PATH", 		  "--path");
+    ps_setenv (0, "CPATH",		  "--cpath");
+    ps_setenv (0, "ARCH", 		  "--arch");
+    ps_setenv (0, "LIBRARY_PATH",    "--library_path");
+    ps_setenv (0, "LD_LIBRARY_PATH", "--ld_library_path");
+    ps_setenv (0, "PKG_CONFIG_PATH", "--pkg_config_path");
+    ps_setenv (0, "ACLOCAL_FLAGS",   "--aclocal_flags");
+    ps_setenv (0, "PERL5LIB",        "--perl5lib");
+
+    # some versions of libtool use this value:
+    $ENV{'D'} = "";
+
+    # make sure the aclocal path exists
+    @word = split (" ", $ENV{'ACLOCAL_FLAGS'});
+    if (@word != 2) { &failure("setup", "ACLOCAL_FLAGS is not set"); }
+    if (! -e $word[1]) { vsystem ("mkdir -p $word[1]"); }
+
+    $psopts = "";
+    if ($optimize) { $psopts = "$psopts --enable-optimize"; }
+
+    $homedir = `pwd`; chomp $homedir;
+
+    $stop_now = 0;
+    for ($i = 0; !$stop_now && ($i < @cvsname); $i++) {
+	if (($stop ne "") && ($stop eq $cvsname[$i])) { $stop_now = 1; }
+	if (($start ne "") && ($start ne $cvsname[$i])) { next; }
+	$start = "";
+
+	($do_tag, $do_build, $do_package, $do_update, $dev_build) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)(\S)|;
+	if ($developer) { $do_build = $dev_build; }
+	if ($do_build eq "N") { next; }
+
+	$workdir = "../$cvsname[$i]";
+
+	# XXX need to grab current value for cleanup
+	print "\n ** psbuild: $cvsname[$i] ** \n";
+	print "\033]0; ** psbuild: $cvsname[$i] ** \007";
+
+	if (!-d $workdir || !-r $workdir || !-x $workdir) { 
+	    print STDERR "WARNING: no directory for component $cvsname[$i], skipping\n"; 
+	    next;
+	}
+	
+	chdir $workdir;
+
+	# how do we build this component?
+	# - autogen.sh : configure : make : make install
+	# - configure : make : make install
+	# - make : make install
+	# - perl Build.PL : ./Build : ./Build install
+ 
+	if (-e "Build.PL") {
+	    vsystem ("$psperlbuild");
+	    if ($?) { &failure($cvsname[$i], "failure in perl Build.PL"); }
+
+	    vsystem ("./Build");
+	    if ($?) { &failure($cvsname[$i], "failure in Build"); }
+
+	    vsystem ("./Build install");
+	    if ($?) { &failure($cvsname[$i], "failure in Build install"); }
+	    
+	    next;
+	}
+
+	if ($rebuild && $clean) {
+	    if (-e "configure") { unlink "Makefile"; }
+	    if ($developer && -e "configure.ac" && -e "autogen.sh") { unlink "configure"; }
+	}
+	$rebuild_this = $rebuild;
+
+	# set a local variable for this loop on rebuild;
+	if (! -e "Makefile") { $rebuild_this = 1; }
+
+	#  run autogen
+	$skip_configure = 0;
+	if ($developer && $rebuild_this && ! -e "configure" && -e "autogen.sh") {
+	    $skip_configure = 1;
+	    vsystem ("$psautogen $psopts");
+	    if ($?) { &failure($cvsname[$i], "failure in psautogen"); }
+	}
+
+	if ($rebuild_this && -e "configure" && !$skip_configure) {
+	    vsystem ("$psconfigure $psopts");
+	    if ($?) { &failure($cvsname[$i], "failure in psconfigure"); }
+	}
+
+	if (! -e "Makefile") { &failure($cvsname[$i], "missing makefile: do you need to run the -dev developer build?"); }
+
+	if ($clean) { 
+	    vsystem ("make clean");
+	    if ($?) { &failure($cvsname[$i], "failure in make clean"); }
+	}
+
+	vsystem ("make");
+	if ($?) { &failure($cvsname[$i], "failure in make"); }
+
+	vsystem ("make install");
+	if ($?) { &failure($cvsname[$i], "failure in make install"); }
+
+	print "*** done with $cvsname[$i] ***\n";
+
+      success:
+	chdir $homedir;
+    }
+    print "\033]0; ** psbuild: finished ** \007";
+    exit 0;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.dist>;
+    foreach $line (@list) {
+	chomp $line;
+	($dist) = $line =~ m|$tagsets/(\S*).dist|;
+	print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub bootstrap {
+
+    if (@ARGV != 2) { die "USAGE: psbuild -bootstrap (install_dir)\n"; }
+    $psconfdir = $ARGV[1];
+
+    # copy psconfig.csh and psconfig.bash to psconfdir
+    vsystem ("cat psconfig.csh.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.csh");
+    vsystem ("cat psconfig.bash.in | sed 's|\@PSCONFDIR@|$psconfdir|' > psconfig.bash");
+
+    vsystem ("mkdir -p $psconfdir");
+
+    vsystem ("cp psconfig.csh $psconfdir/psconfig.csh");
+    vsystem ("cp psconfig.bash $psconfdir/psconfig.bash");
+
+    print STDOUT "\n";
+    
+    print STDOUT "** if you use csh, tcsh or equivalent as your shell, add the following to your .cshrc\n";
+    print STDOUT "    if (-e $psconfdir/psconfig.csh) then\n";
+    print STDOUT "      alias  psconfig        \"source $psconfdir/psconfig.csh\"\n";
+    print STDOUT "    else\n";
+    print STDOUT "      alias  psconfig        \"echo psconfig not available\"\n";
+    print STDOUT "    endif\n";
+    print STDOUT "    psconfig default\n";
+    print STDOUT "\n";
+
+    print STDOUT "** if you use sh, bash or equivalent as your shell, add the following to your .bashrc\n";
+    print STDOUT "    if [ -f $psconfdir/psconfig.csh ]; then\n";
+    print STDOUT "      alias psconfig='source $psconfdir/psconfig.bash'\n";
+    print STDOUT "    else\n";
+    print STDOUT "      alias psconfig='echo psconfig not available'\n";
+    print STDOUT "    fi\n";
+    print STDOUT "    psconfig default\n";
+    print STDOUT "\n";
+
+    exit 0;
+}
+
+sub load_distfile {
+    # open and read the distribution file
+    # results go into @cvsname, @branchtag, @branchver, @mode
+    open (FILE, $distribution) || die "ERROR: can't open distribution file $distribution\n";
+    @list = <FILE>;
+    close (FILE);
+
+    @mode = ();
+    @cvsname = ();
+    @branchtag = ();
+    @branchver = ();
+
+    foreach $line (@list) {
+	chop $line;
+	if ($line =~ m|^\s*$|) { next; }
+	if ($line =~ m|^\s*\#|) { next; }
+
+	($mode, $cvsname, $branchtag, $branchver) = split (" ", $line);
+	
+	if ($cvsname eq "") { die "missing module name\n"; }
+
+	($do_tag, $do_build, $do_package, $do_update) = $mode =~ m|(\S)(\S)(\S)(\S)|;
+	if (($do_tag ne "Y") && ($do_tag ne "N")) { die "invalid tag option $do_tag\n"; }
+	if (($do_build ne "Y") && ($do_build ne "N")) { die "invalid tag option $do_build\n"; }
+	if (($do_package ne "Y") && ($do_package ne "N")) { die "invalid tag option $do_package\n"; }
+	if (($do_update ne "Y") && ($do_update ne "N")) { die "invalid tag option $do_update\n"; }
+	
+	if ($verbose) { print "tag: $do_tag, build: $do_build, package: $do_package, update: $do_update "; }
+	if ($verbose) { print "module: $cvsname, branchtag: $branchtag, branchver: $branchver \n"; }
+
+	push @mode, $mode;
+	push @cvsname, $cvsname;
+	push @branchtag, $branchtag;
+	push @branchver, $branchver;
+    }
+}
+
+sub failure {
+    die "problem building $_[0] : $_[1]\n";
+    print "\033]0; ** psbuild: failure  ** \007";
+}
+
+sub usage {
+    print STDERR "USAGE: psbuild [options] (distribution)\n";
+    print STDERR "     : -version (version) : specify alternate psconfig installation version\n";
+    print STDERR "     : -verbose           : give additional information\n";
+    print STDERR "     : -extlibs (tarball) : specify the location of the extlibs tarball\n";
+    print STDERR "     : -extperl (tarball) : specify the location of the extlibs tarball\n";
+    print STDERR "     : -extcheck          : check (but do not build) the external dependencies\n";
+    print STDERR "     : -extbuild          : check and build (if needed) the external dependencies\n";
+    print STDERR "     : -clean             : clean the source directories before building\n";
+    print STDERR "     : -rebuild           : run 'configure (and autogen for developer)' (C code)\n";
+    print STDERR "     : -optimize          : set flags for optimized code\n";
+    print STDERR "     : -only (module)     : only build the specified module\n";
+    print STDERR "     : -start (module)    : begin build at specified module\n";
+    print STDERR "     : -stop (module)     : stop build after specified module\n\n";
+    print STDERR "     : -dev               : build modules not distributed in tarball\n\n";
+    print STDERR "     : psbuild -bootstrap : generate the psconfig scripts\n";
+    print STDERR "     : psbuild -list      : list the available distributions\n";
+    print STDERR "     : psbuild -h         : this help listing\n";
+    print STDERR "     : psbuild -help      : this help listing\n";
+    print STDERR "     : psbuild --help     : this help listing\n";
+    exit 2;
+}
+
+sub ps_setenv {
+
+    my $verbose = $_[0];
+    my $var = $_[1];
+    my $flag = $_[2];
+
+    my $answer = `csh psconfig.csh $flag $version`;
+    chomp $answer;
+
+    $ENV{$var} = $answer;
+    if ($verbose) {
+	print STDERR "$var = $answer\n";
+    }
+}
+
Index: /tags/ipp-2-5-1/psconfig/pschecklibs
===================================================================
--- /tags/ipp-2-5-1/psconfig/pschecklibs	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/pschecklibs	(revision 17422)
@@ -0,0 +1,460 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+
+# default system library locations
+@binpath = ( );
+@libpath = ( "/lib", "/usr/lib", "/usr/X11R6/lib", "/usr/local/lib" );
+@incpath = ( "/usr/include", "/usr/local/include", "/usr/X11R6/include" );
+
+$version = "";
+$build = 0;
+$force = "";
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+	$version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-build") {
+	$build = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-force") {
+	if (@ARGV < 2) { die "-force must be coupled to a library name\n"; }
+	$force = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "help")   { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+if ( @ARGV > 1) { &usage(); }
+
+if ( @ARGV == 0) { 
+    @list = <$tagsets/*.libs>;
+    $file = $list[-1];
+} else {
+    $file = "$tagsets/$ARGV[0].libs";
+}
+print "examining C libraries based on $file\n\n";
+
+# load the C libraries list
+open (FILE, $file) || die "ERROR: can't open C libraries list: $file\n";
+@list = <FILE>;
+close (FILE);
+
+# set the psconfig version:
+if ("$version" eq "") {
+    $version = $ENV{'PSVERSION'};
+}
+if ("$version" eq "") {
+    $version = "default";
+}
+
+# set the lib and include paths set by psconfig
+$bindir      = `csh psconfig.csh --bin $version`;         chomp $bindir;
+$libdir      = `csh psconfig.csh --libs $version`;        chomp $libdir;
+$mandir      = `csh psconfig.csh --man $version`;         chomp $mandir;
+$incdir      = `csh psconfig.csh --include $version`;     chomp $incdir;
+$prefix      = `csh psconfig.csh --prefix $version`;      chomp $prefix;
+
+$homedir     = `pwd`; chomp $homedir;
+$psconfdir   = `csh psconfig.csh --psconfdir $version`;   chomp $psconfdir;
+$psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+
+print "psconfig version: $version\n";
+print "bindir: $bindir\n";
+print "libdir: $libdir\n";
+print "incdir: $incdir\n";
+print "mandir: $mandir\n";
+print "\n";
+
+# create the directories above if not found:
+if (! -e $libdir) { vsystem ("mkdir -p $libdir"); }
+if (! -e $incdir) { vsystem ("mkdir -p $incdir"); }
+if (! -e $bindir) { vsystem ("mkdir -p $bindir"); }
+if (! -e $mandir) { vsystem ("mkdir -p $mandir"); }
+if (! -e "$mandir/man1") { vsystem ("mkdir -p $mandir/man1"); }
+if (! -e "$mandir/man3") { vsystem ("mkdir -p $mandir/man3"); }
+
+# add the path defined by LIBRARY_PATH
+@tmppath = split (":", $ENV{'LIBRARY_PATH'});
+if (@tmppath) {
+    unshift @libpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @libpath, $libdir;
+
+# add the path defined by PATH
+@tmppath = split (":", $ENV{'PATH'});
+if (@tmppath) {
+    unshift @binpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @binpath, $bindir;
+
+# add the path defined by PATH
+@tmppath = split (":", $ENV{'CPATH'});
+if (@tmppath) {
+    unshift @incpath, @tmppath;
+}
+# search for, and drop, existing libdir entry in libpath?
+unshift @incpath, $incdir;
+
+# add the system paths specified for each architecture
+&checkarch ();
+print "setting architecture to: $arch\n"; 
+print "searching for libraries in: @libpath\n";
+print "searching for programs in: @binpath\n";
+print "\n";
+
+if ($build && ! -d $psconfdir) {
+    mkdir $psconfdir || die "unable to create psconfig dir $psconfdir";
+}
+
+# read the lib distribution file, search / build each entry
+@faillibs = ();
+@failincs = ();
+foreach $line (@list) {
+    chop $line;
+    if ($line =~ m|^\s*$|) { next; }
+    if ($line =~ m|^\s*\#|) { next; }
+
+    ($type, $name, $altnames, $altpaths, $tarball, $tardir, $use_equals, $configure_opts, $make_opts, $install_opts) = split (" ", $line);
+    if (($use_equals ne "Y") && ($use_equals ne "N")) { die "invalid value for use_equals field\n"; }
+
+    if ($force ne "") {
+	if ($force eq $name) {
+	    &buildlib ($name, $tarball, $tardir, $use_equals, $configure_opts, $make_opts, $install_opts);
+	    exit 0;
+	}
+	next;
+    }
+
+    ## check for the C library
+    if ($type eq "lib") { 
+	$found = &checklib ($name, $altnames, $altpaths);
+    } 
+    if ($type eq "bin") { 
+	$found = &checkbin ($name, $altnames, $altpaths);
+    }	
+    if ($type eq "inc") { 
+	$found = &checkinc ($name, $altnames, $altpaths);
+    }	
+
+    if ($found) {
+	print "pass $name ($found)\n";
+	next;
+    } else {
+	print "fail $name\n";
+	push @faillibs, "$name";
+    }	
+
+    if (! $build) { next; }
+    if ($type eq "inc") {
+	print "ERROR: missing header file from library which is supposedly installed\n";
+	next;
+    }
+    &buildlib ($name, $tarball, $tardir, $use_equals, $configure_opts, $make_opts, $install_opts);
+}
+if ($build) { exit 0; }
+print "\n";
+
+if (@faillibs > 0) {
+    print "The following C libraries are missing from your system\n";
+    foreach $name (@faillibs) {
+	print "  $name\n";
+    }
+    print "\n";
+    print "you may install them in your local path by re-running pschecklibs with -build\n";
+    exit 1;
+}
+
+print "no C libraries are missing from your system\n";
+exit 0;
+
+sub buildlib {
+    my ($name, $tarball, $tardir, $use_equals, $configure_opts, $make_opts, $install_opts) = @_;
+
+    if ($tarball eq "NONE") {
+	print "ERROR: require tarball for $name\n";
+	exit 1;
+    }
+
+    $psconfigure = `csh psconfig.csh --psconfigure $version`; chomp $psconfigure;
+    print "psconfigure: $psconfigure";
+
+    ## try to build the module from ../../extlibs/$tarball
+
+    # go to extlibs and unpack the tarball
+    chdir "../../extlibs";
+
+    print "extract $name from $tarball\n";
+    vsystem ("tar xvzf $tarball");
+    
+    print "tardir: $tardir\n";
+
+    # enter the directory and build
+    chdir $tardir;
+
+    # build the library using psconfigure, make, make install
+    if ($configure_opts eq "NONE") { 
+	vsystem ("$psconfigure");
+    } else {
+	$configure_opts = join (' ', split (',', $configure_opts));
+	vsystem ("$psconfigure $configure_opts");
+    }
+    if ($?) { &failure($name, "failure in configure"); }
+    
+    if ($make_opts eq "NONE") { 
+	vsystem ("make");
+    } else {
+	$make_opts = join (' ', split (',', $make_opts));
+	vsystem ("make $make_opts");
+    }
+    if ($?) { &failure($name, "failure in make"); }
+
+    if ($install_opts eq "NONE") {
+	vsystem ("make install");
+    } else {
+	$install_opts = join (' ', split (',', $install_opts));
+	vsystem ("make install $install_opts");
+    }
+    if ($?) { &failure($name, "failure in make install"); }
+
+    chdir $homedir;
+    return 1;
+}
+
+sub checklib {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+    
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+	@altpaths = split (',', $altpaths);
+	push @subdirs, @altpaths;
+    }
+    
+    @trynames = ($name);
+    if ($altnames ne "NONE") {
+	@altnames = split (',', $altnames);
+	push @trynames, @altnames;
+    }
+
+    # try each of the possible library names
+    foreach $tryname (@trynames) { 
+	# try each of the library paths, with the default as well as each altpath
+	foreach $topdir ( @libpath ) {
+	    foreach $subdir ( @subdirs ) {
+		if ($subdir eq ".") {
+		    $path = $topdir;
+		} else {
+		    $path = "$topdir/$subdir";
+		}
+		# print "trying $path\n";
+		if (! -e $path) { next; }
+		$libname = "$path/$tryname.a";
+		if (-e $libname) { return $libname; }
+		# print "no $libname\n";
+		$libname = "$path/$tryname.$dlltype";
+		if (-e $libname) { return $libname; }
+		# print "no $libname\n";
+	    }
+	}
+
+	# if we failed to find the basic named library files, try the
+	# versions libraries if we find only a .so.N without a matching
+	# .so, we link this in the installed libdir
+	foreach $topdir ( @libpath ) {
+	    foreach $subdir ( @subdirs ) {
+		if ($subdir eq ".") {
+		    $path = $topdir;
+		} else {
+		    $path = "$topdir/$subdir";
+		}
+		if (! -e $path) { next; }
+		@libnames = <$path/$tryname.$dlltype*>;
+		if (@libnames > 0) { 
+		    $libname = @libnames[-1];
+		    # print "making link to numbered .$dlltype library $libdir/$f.$dlltype\n";
+		    symlink $libname, "$libdir/$f.$dlltype";
+		    if ($?) { exit 1; }
+		    return $libname; 
+		}
+		# print "no $path/$tryname.$dlltype*\n";
+	    }
+	}
+    }
+    return 0;
+}
+
+sub checkbin {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+    
+    # XXX drop this for bin?
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+	@altpaths = split (',', $altpaths);
+	push @subdirs, @altpaths;
+    }
+    
+    # try each of the library paths, with the default as well as each altpath
+    foreach $topdir ( @binpath ) {
+	foreach $subdir ( @subdirs ) {
+	    if ($subdir eq ".") {
+		$path = $topdir;
+	    } else {
+		$path = "$topdir/$subdir";
+	    }
+	    # print "trying $path\n";
+	    if (! -e $path) { next; }
+	    $binname = "$path/$name";
+	    if (-e $binname) { return $binname; }
+	}
+    }
+    return 0;
+}
+
+sub checkinc {
+    my $name = $_[0];
+    my $altnames = $_[1];
+    my $altpaths = $_[2];
+    
+    @subdirs = ".";
+    if ($altpaths ne "NONE") {
+	@altpaths = split (',', $altpaths);
+	push @subdirs, @altpaths;
+    }
+    
+    # try each of the library paths, with the default as well as each altpath
+    foreach $topdir ( @incpath ) {
+	foreach $subdir ( @subdirs ) {
+	    if ($subdir eq ".") {
+		$path = $topdir;
+	    } else {
+		$path = "$topdir/$subdir";
+	    }
+	    if (! -e $path) { next; }
+	    $incname = "$path/$name";
+	    if (-e $incname) { return $incname; }
+	}
+    }
+
+    return 0;
+}
+
+sub checkarch {
+    # we are going to supplement the global libpath supplied
+    
+    # check the hardware architecture:
+    $sys=`uname -s`; chomp $sys;
+
+    # default values
+    $ranlib = "ranlib";
+    $dlltype = "so";
+
+    if ($sys eq "IRIX64") {
+	$arch = "irix";
+	return;
+    }
+
+    if ($sys eq "SunOS") {
+	$ver=`uname -r | awk '{print substr($1,1,1)}'`;
+	if ($ver == 5) {
+	    $arch = "sol";
+	} else {
+	    $arch="sun4";
+	}
+
+	# sun (at least) seems to need the socket library (linux does not)
+	push @libpath, "/usr/openwin/lib";
+	push @incpath, "/usr/openwin/include";
+
+	# XXX this is a problem
+	print STDERR "need to add system dependent libraries (eg, libsocket, libnsl)\n";
+	exit 1;
+	$needlibs = "$needlibs libsocket libnsl";
+	$ranlib = "touch";
+	return;
+    }
+
+    if ($sys eq "Linux") {
+	$arch = "linux";
+
+	if (-e "/etc/sidious.config") { 
+	    $arch = "sid";
+	    return;
+	}
+
+	$mach = `uname -m`; chomp $mach;
+
+	if ($mach eq "x86_64") {
+	    $arch = "lin64";
+	    unshift @libpath, "/lib64";
+	    unshift @libpath, "/usr/lib64";
+	    unshift @libpath, "/usr/X11R6/lib64";
+	    return;
+	}
+	return;
+    }
+
+    if ($sys eq "Darwin") {
+        $mach = `uname -m`; chomp $mach;
+        if ($mach eq "i386") {
+            $arch="darwin_x86";
+        } else {
+            $arch = "darwin";
+        }
+        $dlltype = "dylib";
+        unshift @libpath, "/sw/lib";
+        unshift @incpath, "/sw/include";
+        unshift @incpath, "/usr/include/sys";
+        return;
+    }
+
+    if ($sys eq "HP-UX") {
+	$arch = "hpux";
+	return;
+    }
+    
+    print "unknown architecture";
+    exit 1;
+}
+
+sub usage {
+    print STDERR "USAGE: pscheckperl [-version] [-build]\n";
+    exit 2;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.perl>;
+    foreach $line (@list) {
+	chomp $line;
+	($dist) = $line =~ m|$tagsets/(\S*).perl|;
+	print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub failure {
+    system ("ls");
+    die "problem building $_[0] : $_[1]\n";
+    print "\033]0; ** pschecklibs: failure  ** \007";
+}
+
Index: /tags/ipp-2-5-1/psconfig/pscheckmods
===================================================================
--- /tags/ipp-2-5-1/psconfig/pscheckmods	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/pscheckmods	(revision 17422)
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+
+if (@ARGV != 2) { die "USAGE: pscheckmods (module) (version)\n"; }
+
+$x = eval "require $ARGV[0]; 1";
+if (! $x) { 
+    exit 1;
+}
+
+$version = eval "\$$ARGV[0]::VERSION";
+print "$ARGV[0]: $version\n";
+
+if ($ARGV[1] > $version) {
+    print "$ARGV[0] is too old: have $version : need $ARGV[1]\n";
+    exit 1;
+}
+
+exit 0;
Index: /tags/ipp-2-5-1/psconfig/pscheckperl
===================================================================
--- /tags/ipp-2-5-1/psconfig/pscheckperl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/pscheckperl	(revision 17422)
@@ -0,0 +1,147 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+
+$version = "";
+$build = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-version") {
+	$version = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-build") {
+	$build = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-h")     { &usage (); }
+    if ($ARGV[0] eq "help")   { &usage (); }
+    if ($ARGV[0] eq "-help")  { &usage (); }
+    if ($ARGV[0] eq "--help") { &usage (); }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+if ( @ARGV > 1) { &usage(); }
+
+if ( @ARGV == 0) { 
+    @list = <$tagsets/*.perl>;
+    $file = $list[-1];
+} else {
+    $file = "$tagsets/$ARGV[0].perl";
+}
+print "examining perl modules based on $file\n";
+
+# load the perl module list
+open (FILE, $file) || die "ERROR: can't open perl module list: $file\n";
+@list = <FILE>;
+close (FILE);
+
+# set the psconfig version:
+if ("$version" eq "") {
+    $version = $ENV{'PSVERSION'};
+}
+if ("$version" eq "") {
+    $version = "default";
+}
+
+
+$prefix  = `csh -f psconfig.csh --prefix $version`; chomp $prefix;
+$perldir = `csh -f psconfig.csh --perldir $version`; chomp $perldir;
+$homedir = `pwd`; chomp $homedir;
+
+print "psconfig version: $version\n";
+print "prefix: $prefix\n";
+print "perldir: $perldir\n";
+print "PERL5LIB: $ENV{'PERL5LIB'}\n";
+$psconfdir = $ENV{'PSCONFDIR'};
+
+if ($build && ! -d $psconfdir) {
+    mkdir $psconfdir || die "unable to create psconfig dir $psconfdir";
+}
+
+$Nmissing = 0;
+@missing = ();
+foreach $line (@list) {
+    chop $line;
+    if ($line =~ m|^\s*$|) { next; }
+    if ($line =~ m|^\s*\#|) { next; }
+
+    ($N, $module, $tarball, $modver, $prompts) = split (" ", $line);
+
+    if ($modver eq "") { $modver = 0; } 
+    system ("pscheckmods $module $modver");
+    if ($? == 0) { 
+	# print "$module: found\n";
+	next; 
+    }
+
+    print "$module: missing\n";
+    if (!$build) { 
+	$Nmissing ++;
+	push @missing, $module;
+	next; 
+    }
+
+    # try to build the module from ../../extperl/Module.*.tar.gz
+    chdir "../../extperl" or die "Unable to find ../../extperl directory.";
+
+    print "extract $module from $tarball\n";
+    vsystem ("tar xvzf $tarball");
+    
+    ($tardir) = $tarball =~ m|(\S*).tar.gz|;
+    print "tardir: $tardir\n";
+
+    chdir $tardir;
+
+    # build the MakeMaker makefile, setting the output directories
+    if ($prompts) {
+	@answers = split (",", $prompts);
+	open (PIPE, "|perl Makefile.PL PREFIX=$prefix LIB=$perldir");
+	foreach $answer (@answers) {
+	    print PIPE "$answer\n";
+	}
+	close (PIPE);
+    } else {
+	vsystem ("perl Makefile.PL PREFIX=$prefix LIB=$perldir");
+    }
+    
+    vsystem ("make < /dev/null");
+    vsystem ("make install");
+
+    chdir $homedir;
+}
+if (!$build) {
+    if ($Nmissing > 0) {
+	print "The following $Nmissing perl modules are missing from your system\n";
+	foreach $name (@missing) {
+	    print "  $name\n";
+	}
+	print "you may install them in your local path by re-running pscheckperl with -build\n";
+    } else {
+	print "no perl modules are missing from your system\n";
+    }
+}
+exit 0;
+
+sub usage {
+    print STDERR "USAGE: pscheckperl [-version] [-build]\n";
+    exit 2;
+}
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.perl>;
+    foreach $line (@list) {
+	chomp $line;
+	($dist) = $line =~ m|$tagsets/(\S*).perl|;
+	print STDERR "$dist\n";
+    }
+    exit 2;
+}
Index: /tags/ipp-2-5-1/psconfig/psconfig.bash.in
===================================================================
--- /tags/ipp-2-5-1/psconfig/psconfig.bash.in	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/psconfig.bash.in	(revision 17422)
@@ -0,0 +1,41 @@
+# this script sets the PS IPP build environment for BASH shell users
+
+if [ -z $PSCONFIG_DIR ]; then
+  # PSCONFIG_DIR=/home/eugene/src/panstarrs/ipp/psconfig
+  PSCONFDIR=@PSCONFDIR@
+fi
+
+if (( $# == 0 )); then
+  /bin/csh $PSCONFDIR/psconfig.csh
+  return 0
+fi
+
+if [[ "$1" == "--help" ]]; then
+  echo "USAGE: psconfig (version) : set configuration to specified version"
+  return 0
+fi
+
+if [[ "$1" == "--list" ]]; then
+  /bin/csh $PSCONFDIR/psconfig.csh --list
+  return 0
+fi
+
+version=$1
+psconfigure=`/bin/csh $PSCONFDIR/psconfig.csh --psconfigure $version`
+psautogen=`/bin/csh $PSCONFDIR/psconfig.csh --psautogen $version`
+psperlbuild=`/bin/csh $PSCONFDIR/psconfig.csh --psperlbuild $version`
+alias psconfigure=$psconfigure
+alias psautogen=$psautogen
+alias psperlbuild=$psperlbuild
+
+# psconfig env variables
+export PSCONFDIR=`/bin/csh $PSCONFDIR/psconfig.csh --psconfdir $version`
+export PSVERSION=`/bin/csh $PSCONFDIR/psconfig.csh --psversion $version`
+
+# environment variables
+export PATH=`/bin/csh $PSCONFDIR/psconfig.csh --path $version`
+export ARCH=`/bin/csh $PSCONFDIR/psconfig.csh --arch $version`
+export LD_LIBRARY_PATH=`/bin/csh $PSCONFDIR/psconfig.csh --ld_library_path $version`
+export PKG_CONFIG_PATH=`/bin/csh $PSCONFDIR/psconfig.csh --pkg_config_path $version`
+export ACLOCAL_FLAGS=`/bin/csh $PSCONFDIR/psconfig.csh --aclocal_flags $version`
+export PERL5LIB=`/bin/csh $PSCONFDIR/psconfig.csh --perl5lib $version`
Index: /tags/ipp-2-5-1/psconfig/psconfig.csh.in
===================================================================
--- /tags/ipp-2-5-1/psconfig/psconfig.csh.in	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/psconfig.csh.in	(revision 17422)
@@ -0,0 +1,555 @@
+#/bin/csh -f
+# this file is sourced using the command 'psconfig'
+
+# list the defined psconfig versions
+set show_prefix = 0
+set show_libs = 0
+set show_bin = 0
+set show_man = 0
+set show_arch = 0
+set show_path = 0
+set show_cpath = 0
+set show_include = 0
+set show_configure = 0
+set show_autogen = 0
+set show_perlbuild = 0
+set show_perldir = 0
+set show_perl5lib = 0
+set show_psversion = 0
+set show_psconfdir = 0
+set show_library_path = 0
+set show_ld_library_path = 0
+set show_pkg_config_path = 0
+set show_aclocal_flags = 0
+set args = ""
+while ($#argv) 
+  switch ($argv[1])
+    case --help:
+      goto help;
+    case --list:
+      echo "PSCONFDIR : $PSCONFDIR"
+      /bin/ls $PSCONFDIR | grep -v "\<man\>" | grep -v "\<share\>" | awk -F. '{printf "  %-10s : ", $NF}{for (i = 1; i < NF-1; i++){printf "%s.", $i}}{printf "%s\n", $(NF-1)}'
+      exit 0
+    case --prefix:
+      set show_prefix = 1
+      breaksw
+    case --libs:
+      set show_libs = 1
+      breaksw
+    case --bin
+      set show_bin = 1
+      breaksw
+    case --man
+      set show_man = 1
+      breaksw
+    case --include
+      set show_include = 1
+      breaksw
+    case --psconfigure:
+      set show_configure = 1
+      breaksw;   
+    case --psautogen:
+      set show_autogen = 1
+      breaksw;
+    case --psperlbuild:
+      set show_perlbuild = 1
+      breaksw;
+    case --perldir:
+      set show_perldir = 1
+      breaksw;
+    case --perl5lib:
+      set show_perl5lib = 1
+      breaksw;
+    case --psversion
+      set show_psversion = 1
+      breaksw;
+    case --psconfdir
+      set show_psconfdir = 1
+      breaksw;
+    case --ld_library_path
+      set show_ld_library_path = 1
+      breaksw;
+    case --library_path
+      set show_library_path = 1
+      breaksw;
+    case --pkg_config_path
+      set show_pkg_config_path = 1
+      breaksw;
+    case --arch
+      set show_arch = 1
+      breaksw;
+    case --path
+      set show_path = 1
+      breaksw;
+    case --cpath
+      set show_cpath = 1
+      breaksw;
+    case --aclocal_flags
+      set show_aclocal_flags = 1
+      breaksw;
+    case -*:
+      echo "unknown option $1"
+      goto help;
+    default:
+      set args=($args $1);
+      breaksw;
+  endsw
+  shift
+end
+if ($#args != 1) goto usage
+if ("$args" == "") goto usage
+
+# make this configurable by the user
+if (! $?PSCONFDIR) then
+  setenv PSCONFDIR @PSCONFDIR@
+endif
+
+if (! $?PSVERSION) then
+  setenv PSVERSION default
+endif
+
+if (-e $HOME/.psconfigrc) then
+  source $HOME/.psconfigrc
+endif
+
+setenv PSVERSION $args[1]
+
+if ($?CPATH == 0) setenv CPATH
+if ($?LIBRARY_PATH == 0) setenv LIBRARY_PATH
+if ($?LD_LIBRARY_PATH == 0) setenv LD_LIBRARY_PATH
+if ($?PKG_CONFIG_PATH == 0) setenv PKG_CONFIG_PATH
+if ($?PERL5LIB == 0) setenv PERL5LIB
+if ($?MANPATH == 0) setenv MANPATH
+
+# identify system architecture
+set sys=`uname -s` 
+if ("$sys" == "SunOS") then
+ set ver = `uname -r | awk '{print substr($1,1,1)}'`;
+ if ($ver > 4) then 
+   set sys = "Solaris"
+ endif
+endif
+if ("$sys" == "Linux") then
+ grep "Sidious" /etc/issue > /dev/null
+ if ($status == 0) then 
+  set sys = Sidious
+ endif
+ grep "Red Hat Enterprise" /etc/issue > /dev/null
+ if ($status == 0) then 
+  set sys = RedHat
+ endif
+endif
+
+# determine architecture-dependent paths & variables for all shells
+switch ($sys)
+ case IRIX64:
+   setenv ARCH irix;
+   breaksw;
+
+ case Solaris:
+   setenv ARCH sol
+   breaksw;
+
+ case SunOs:
+   setenv ARCH sun4
+   breaksw;
+
+ case Linux:
+   setenv ARCH linux;
+   set mach=`uname -m`
+   if ("$mach" == "x86_64") setenv ARCH lin64
+   breaksw;
+
+ case Darwin:
+   setenv ARCH darwin;
+   set mach=`uname -m`
+   if ("$mach" == "i386") setenv ARCH darwin_x86
+   breaksw;
+
+ case Sidious:
+   setenv ARCH sid;
+   breaksw;
+
+ case RedHat:
+   setenv ARCH linrh;
+   set mach=`uname -m`
+   if ("$mach" == "x86_64") setenv ARCH linrh64
+   breaksw;
+
+ case HP-UX:
+    setenv ARCH hp;
+    breaksw;
+
+ default:
+   echo "unknown architecture";
+   setenv ARCH unknown;
+   breaksw;
+endsw
+ 
+setenv PSCONFIG $PSVERSION.$ARCH
+
+set newpath = ""
+set pathlist = `echo $PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+set bindir = {$PSCONFDIR}/{$PSCONFIG}/bin
+if ("$PSCONFIG" == "none") then
+  setenv PATH {$newpath}
+else
+  setenv PATH {$bindir}:{$newpath}
+endif
+
+set mandir  = {$PSCONFDIR}/{$PSCONFIG}/man
+set newpath = ""
+set pathlist = `echo $MANPATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv MANPATH
+  else
+    setenv MANPATH {$newpath}
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv MANPATH {$mandir}:
+  else
+    setenv MANPATH {$mandir}:{$newpath}
+  endif 
+endif 
+
+# set CPATH, used to find include files
+set incdir  = {$PSCONFDIR}/{$PSCONFIG}/include
+set newpath = ""
+set pathlist = `echo $CPATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+
+## XXX mysql (and others?) are not installed in the correct location: they go into
+## prefix/lib/mysql regardless of configure options
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv CPATH
+  else
+    setenv CPATH {$newpath}:
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv CPATH {$incdir}:{$PSCONFDIR}/{$PSCONFIG}/include/mysql:
+  else
+    setenv CPATH {$incdir}:{$PSCONFDIR}/{$PSCONFIG}/include/mysql:{$newpath}:
+  endif 
+endif 
+
+# set LIBRARY_PATH and LD_LIBRARY_PATH, used to find libraries
+set libdir  = {$PSCONFDIR}/{$PSCONFIG}/lib
+set newpath = ""
+set pathlist = `echo $LD_LIBRARY_PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+
+## XXX mysql (and others?) are not installed in the correct location: they go into
+## prefix/lib/mysql regardless of configure options
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv LD_LIBRARY_PATH
+    unsetenv LIBRARY_PATH
+  else
+    setenv LD_LIBRARY_PATH {$newpath}:
+    setenv LIBRARY_PATH {$newpath}:
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv LD_LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:
+    setenv LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:
+  else
+    setenv LD_LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:{$newpath}:
+    setenv LIBRARY_PATH {$libdir}:{$PSCONFDIR}/{$PSCONFIG}/lib/mysql:{$newpath}:
+  endif 
+endif 
+
+# supplement CPATH, LIBRARY_PATH and LD_LIBRARY_PATH with a few extra common locations
+# we probably can drop the system libraries here
+set xtralibs = ( "/lib" "/usr/lib" "/usr/X11R6/lib" "/usr/local/lib" )
+set xtrapath = ( "/usr/include" "/usr/local/include" "/usr/X11R6/include" )
+
+# add architecture-dependent paths
+switch ($ARCH)
+   breaksw;
+
+ case sol:
+ case sun4:
+   set xtralibs = ( $xtralibs "/usr/openwin/lib" )
+   set xtrapath = ( $xtrapath "/usr/openwin/include")
+   breaksw;
+
+ case linux:
+ case linrh:
+   breaksw;
+
+ case lin64:
+ case linrh64:
+   set xtralibs = ( $xtralibs "/lib64" "/usr/lib64" "/usr/X11R6/lib64" )
+   breaksw;
+
+ case darwin:
+ case darwin_x86:
+   set xtralibs = ( $xtralibs "/sw/lib" )
+   set xtrapath = ( $xtrapath "/sw/include" "/usr/include/sys")
+   breaksw;
+
+ case sid:
+ case hp:
+ case irix:
+
+ default:
+   echo "unknown architecture";
+   breaksw;
+endsw
+
+# add to LD_LIBRARY_PATH if not found
+foreach name ($xtralibs)
+  echo $LD_LIBRARY_PATH | grep $name> /dev/null
+  if ($status == 0) continue
+  setenv LD_LIBRARY_PATH {$LD_LIBRARY_PATH}{$name}:
+end
+# add to LIBRARY_PATH if not found
+foreach name ($xtralibs)
+  echo $LIBRARY_PATH | grep $name > /dev/null
+  if ($status == 0) continue
+  setenv LIBRARY_PATH {$LIBRARY_PATH}{$name}:
+end
+# add to CPATH if not found
+foreach name ($xtrapath)
+  echo $CPATH | grep $name > /dev/null
+  if ($status == 0) continue
+  setenv CPATH {$CPATH}{$name}:
+end
+
+# Build wants to put things in prefix/*, MakeMaker wants to put them in prefix/perl5/*
+set plibdir  = {$PSCONFDIR}/{$PSCONFIG}/lib
+# set plib5dir = {$PSCONFDIR}/{$PSCONFIG}/lib/perl5
+set newpath = ""
+set pathlist = `echo $PERL5LIB | tr ':' '\n'`
+# build newpath with contents of PERL5LIB excluding PSCONFDIR entries
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv PERL5LIB
+  else
+    setenv PERL5LIB {$newpath}
+  endif 
+else
+  if ("$newpath" == "") then
+    setenv PERL5LIB {$plibdir}:
+  else
+    setenv PERL5LIB {$plibdir}:{$newpath}
+  endif 
+endif 
+
+set newpath = ""
+set pathlist = `echo $PKG_CONFIG_PATH | tr ':' '\n'`
+foreach name ($pathlist)
+  echo $name | grep $PSCONFDIR > /dev/null
+  if ($status == 0) continue
+  if ($newpath == "") then
+    set newpath = {$name}:
+  else
+    set newpath = {$newpath}{$name}:
+  endif
+end
+if ("$PSCONFIG" == "none") then
+  if ("$newpath" == "") then
+    unsetenv PKG_CONFIG_PATH
+  else
+    setenv PKG_CONFIG_PATH {$newpath}
+  endif
+else
+  if ("$newpath" == "") then
+    setenv PKG_CONFIG_PATH {$PSCONFDIR}/{$PSCONFIG}/lib/pkgconfig:
+  else
+    setenv PKG_CONFIG_PATH {$PSCONFDIR}/{$PSCONFIG}/lib/pkgconfig:{$newpath}
+  endif
+endif
+
+if ("$PSCONFIG" == "none") then
+  alias  psconfigure configure
+  alias  psautogen autogen.sh
+  alias  psperlbuild perl Build.PL
+else
+  alias  psconfigure configure --prefix={$PSCONFDIR}/{$PSCONFIG} --bindir=$bindir --libdir=$libdir --mandir=$mandir --includedir=$incdir --sysconfdir={$PSCONFDIR}/{$PSCONFIG}/etc --datadir={$PSCONFDIR}/{$PSCONFIG}/share
+  alias  psautogen autogen.sh  --prefix={$PSCONFDIR}/{$PSCONFIG} --bindir=$bindir --libdir=$libdir --mandir=$mandir --includedir=$incdir --sysconfdir={$PSCONFDIR}/{$PSCONFIG}/etc --datadir={$PSCONFDIR}/{$PSCONFIG}/share
+  alias  psperlbuild perl Build.PL --prefix {$PSCONFDIR}/{$PSCONFIG} --install_path script=$bindir --install_path arch=$bindir --install_path bin=$bindir --install_path lib=$libdir --install_path bindoc={$PSCONFDIR}/{$PSCONFIG}/man/man1 --install_path libdoc={$PSCONFDIR}/{$PSCONFIG}/man/man3
+  setenv ACLOCAL_FLAGS "-I $PSCONFDIR/$PSCONFIG/share/aclocal"
+endif
+
+# list selected environment variables
+if ($show_prefix) then
+  echo $PSCONFDIR/$PSCONFIG
+  exit 0
+endif
+if ($show_libs) then
+  echo $libdir
+  exit 0
+endif
+if ($show_bin) then
+  echo $bindir
+  exit 0
+endif
+if ($show_man) then
+  echo $mandir
+  exit 0
+endif
+if ($show_include) then
+  echo $incdir
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_configure) then
+  alias psconfigure 
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_autogen) then
+  alias psautogen
+  exit 0
+endif
+
+# list the defined psconfig versions
+if ($show_perlbuild) then
+  alias psperlbuild
+  exit 0
+endif
+
+if ($show_perldir) then
+  echo $plibdir
+  exit 0
+endif
+
+if ($show_perl5lib) then
+  echo $PERL5LIB
+  exit 0
+endif
+
+if ($show_psversion) then
+  echo $PSVERSION
+  exit 0
+endif
+
+if ($show_psconfdir) then
+  echo $PSCONFDIR
+  exit 0
+endif
+
+if ($show_ld_library_path) then
+  echo $LD_LIBRARY_PATH
+  exit 0
+endif
+
+if ($show_library_path) then
+  echo $LIBRARY_PATH
+  exit 0
+endif
+
+if ($show_pkg_config_path) then
+  echo $PKG_CONFIG_PATH
+  exit 0
+endif
+
+if ($show_arch) then
+  echo $ARCH
+  exit 0
+endif
+
+if ($show_path) then
+  echo $PATH
+  exit 0
+endif
+
+if ($show_cpath) then
+  echo $CPATH
+  exit 0
+endif
+
+if ($show_aclocal_flags) then
+  echo $ACLOCAL_FLAGS
+  exit 0
+endif
+
+
+
+exit 0
+
+usage:
+  if (! $?PSCONFDIR) echo "PSCONFDIR is not set : run psconfig with an argument to setup"
+  if (! $?PSVERSION) echo "PSVERSION is not set : run psconfig with an argument to setup"
+  if (! $?ARCH)      echo "ARCH is not set : run psconfig with an argument to setup"
+  echo $PSCONFDIR : $PSVERSION : $ARCH 
+  echo "psconfig --help for additional info"
+  exit 2
+
+help:
+  echo "psconfig: set or show the current pslib configuration information"
+  echo "USAGE: psconfig (version) : set configuration to specified version"
+  echo "       psconfig --bin     : return the current path"
+  echo "       psconfig --libs    : return the current library path"
+  echo "       psconfig --include"
+  echo 
+  echo "       psconfig --prefix  : show prefix directory"
+  echo "       psconfig --psconfigure"
+  echo "       psconfig --psautogen"
+  echo "       psconfig --psperlbuild"
+  echo "       psconfig --perldir"
+  echo "       psconfig --perl5lib"
+  echo "       psconfig --psversion"
+  echo "       psconfig --psconfdir"
+  echo "       psconfig --library_path"
+  echo "       psconfig --ld_library_path"
+  echo "       psconfig --pkg_config_path"
+  echo "       psconfig --arch"
+  echo "       psconfig --path"
+  echo "       psconfig --cpath"
+  echo "       psconfig --aclocal_flags"
+  echo
+  echo "       psconfig --list    : list currently availabe configuration versions"
+  echo "       psconfig --help    : this listing"
+  echo " use ~/.psconfigrc to set PSCONFDIR as desired"
+  exit 1
Index: /tags/ipp-2-5-1/psconfig/psdist
===================================================================
--- /tags/ipp-2-5-1/psconfig/psdist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/psdist	(revision 17422)
@@ -0,0 +1,308 @@
+#!/usr/bin/env perl
+
+$tagsets = "tagsets";
+
+$cvs  = 0;
+$diff = 0;
+$settag = 0;
+$update = 0;
+$mkdist = 0;
+$module = "";
+$settag_mode = "";
+$update_mode = "";
+$mkdist_mode = "";
+$mkdist_head = 0;
+@tARGV = ();
+for (; @ARGV > 0; ) {
+    if ($ARGV[0] eq "-diff") {
+	$diff = 1;
+        shift; next;
+    }
+    if ($ARGV[0] eq "-update") {
+	$update = 1; shift; 
+	if ($ARGV[0] eq "tags") {
+	    $update_mode = "tags";
+	    shift; next
+	}
+	if ($ARGV[0] eq "head") {
+	    $update_mode = "head";
+	    shift; next
+	}
+	&usage();
+    }
+    if ($ARGV[0] eq "-tag") {
+	$settag = 1; shift;
+	if ($ARGV[0] eq "dev") {
+	    $settag_mode = "dev";
+	    shift; next
+	}
+	if ($ARGV[0] eq "rev") {
+	    $settag_mode = "rev";
+	    shift; next
+	}
+	if ($ARGV[0] eq "branch") {
+	    $settag_mode = "branch";
+	    shift; next
+	}
+	&usage();
+    }
+    if ($ARGV[0] eq "-dist") {
+	$mkdist = 1; shift;
+	if ($ARGV[0] eq "cvs") {
+	    $mkdist_mode = "cvs";
+	    shift; next
+	}
+	if ($ARGV[0] eq "tree") {
+	    $mkdist_mode = "tree";
+	    shift; next
+	}
+	if ($ARGV[0] eq "autogen") {
+	    $mkdist_mode = "autogen";
+	    shift; next
+	}
+	if ($ARGV[0] eq "tarball") {
+	    $mkdist_mode = "tarball";
+	    shift; next
+	}
+	&usage();
+    }
+    if ($ARGV[0] eq "-module") {
+	$module = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-head")  { $mkdist_head = 1; shift; next; }
+    if ($ARGV[0] eq "-list")  { &list_distributions(); }
+    if ($ARGV[0] eq "-h")     { &usage(); }
+    if ($ARGV[0] eq "-help")  { &usage(); }
+    if ($ARGV[0] eq "--help") { &usage(); }
+    @tARGV = (@tARGV, $ARGV[0]);
+    shift;
+}
+@ARGV = @tARGV;
+
+if ( @ARGV != 1) { &usage (); }
+
+$distribution = $ARGV[0];
+&load_distfile ();
+
+if ($diff)   { &difflist (); }
+if ($settag) { &settags (); }
+if ($update) { &update_tree (); }
+if ($mkdist) { &make_distribution (); }
+&usage();
+
+sub vsystem {
+    print STDERR "@_\n";
+    $status = system ("@_");
+    $status;
+}
+
+sub difflist {
+
+    for ($i = 0; $i < @cvsname; $i++) {
+	# the base component cannot be rdiffed
+	print STDERR "--- $cvsname[$i] ---\n";
+	if ($cvsname[$i] eq "base") { next; }
+	if ($tag[$i] eq "") { next; }
+	&vsystem ("cvs -q rdiff -s -r $tag[$i] $cvsname[$i]");
+	print STDERR "\n\n";
+    }
+    exit 0;
+}
+
+sub settags {
+    print STDERR "setting tags\n";
+    if ($settag_mode eq "") { die "-tag mode is not set\n"; }
+    for ($i = 0; $i < @cvsname; $i++) {
+	if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+	## XXX make this backwards compatible with pre-ipp-2.4 releases (with four, not five, entries)?
+	($do_tag, $do_build, $do_dist, $do_update, $dev_build) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)(\S)|;
+	if ($do_tag eq "N") { 
+	    push @remind, $cvsname[$i];
+	    next; 
+	}
+	if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+	if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+	$tag = "$branchtag[$i]$branchver[$i]";
+	if ($settag_mode eq "branch") { 
+	    &vsystem ("cvs -q rtag -b $branchtag[$i] $cvsname[$i]");
+	    next;
+	} 
+	if ($settag_mode eq "dev") { 
+	    &vsystem ("cvs -q rtag $tag $cvsname[$i]");
+	    next;
+	}
+	if ($settag_mode eq "rev") { 
+	    &vsystem ("cvs -q rtag -r $branchtag[$i] $tag $cvsname[$i]");
+	    next;
+	}
+	die "programming error";
+    }
+    for ($i = 0; $i < @remind; $i++) {
+	print STDERR "remember to set tag for $remind[$i] if needed\n";
+    }
+    exit 0;
+}
+
+sub update_tree {
+    print STDERR "updating tree\n";
+    if ($update_mode eq "") { die "-update mode is not set\n"; }
+
+    $homedir = `pwd`; chomp $homedir;
+    chdir "..";
+
+    for ($i = 0; $i < @cvsname; $i++) {
+	if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+	($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+	if ($do_update eq "N") { 
+	    push @remind, $cvsname[$i];
+	    next; 
+	}
+	# only update modules with tags
+	if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+	if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+	$tag = "$branchtag[$i]$branchver[$i]";
+	if ($update_mode eq "tags") { 
+	    &vsystem ("cvs -q co -r $tag $cvsname[$i]");
+	    next;
+	}
+	if ($update_mode eq "head") { 
+	    &vsystem ("cvs -q co -A $cvsname[$i]");
+	    next;
+	}
+	die "programming error";
+    }
+    for ($i = 0; $i < @remind; $i++) {
+	print STDERR "remember to update $remind[$i] if needed\n";
+    }
+    chdir $homedir;
+    exit 0;
+}
+
+# XXX probably need to force CVSROOT here
+sub make_distribution {
+    # extract source tree and optionally package the distribution into a tarball
+    print STDERR "making distribution\n";
+    if ($mkdist_mode eq "") { die "-dist mode is not set\n"; }
+    mkdir $distribution;
+    chdir $distribution;
+    for ($i = 0; $i < @cvsname; $i++) {
+	if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+	($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+	if ($do_dist eq "N") { next; }
+
+	if ($branchtag[$i] eq "") { die "branch tag missing for $cvsname[$i]\n"; }
+	if ($branchver[$i] eq "") { die "branch tag version missing\n"; }
+	$tag = "$branchtag[$i]$branchver[$i]";
+
+	if ($mkdist_head) {
+	    &vsystem ("cvs co $cvsname[$i]");
+	    if ($status) { die "error running cvs"; }
+	} else {
+	    &vsystem ("cvs co -r $tag $cvsname[$i]");
+	    if ($status) { die "error running cvs"; }
+	}	
+    }
+    chdir "..";
+    if ($mkdist_mode eq "cvs") { exit 0; }
+
+    # remove the CVS directories
+    &vsystem ("rm -r `find $distribution -name CVS`");
+    if ($mkdist_mode eq "tree") { exit 0; }
+
+    # run autogen.sh, if present, to build a configure script
+    chdir $distribution;
+    for ($i = 0; $i < @cvsname; $i++) {
+	if (($module ne "") && ($module ne $cvsname[$i])) { next; }
+	($do_tag, $do_build, $do_dist, $do_update) = $mode[$i] =~ m|(\S)(\S)(\S)(\S)|;
+	if ($do_dist eq "N") { next; }
+
+	if (! -e "$cvsname[$i]/autogen.sh" || -e "$cvsname[$i]/Build.PL" ) { next; }
+	chdir $cvsname[$i];
+
+	vsystem ("./autogen.sh --no-configure");
+	if ($status) { die "error running autogen.sh"; }
+
+#	vsystem ("make distclean");
+#	if ($status) { die "error running make distclean"; }
+	chdir "..";
+    }
+    chdir "..";
+    if ($mkdist_mode eq "autogen") { exit 0; }
+
+    # build a tarball from the full tree
+    if (! -e tarballs) { mkdir "tarballs"; }
+    &vsystem ("tar cvzf tarballs/$distribution.tgz  $distribution");
+    if ($status) { die "error creating tarball\n"; }
+    exit 0;
+}
+
+sub list_distributions {
+    @list = <$tagsets/*.dist>;
+    foreach $line (@list) {
+	chomp $line;
+	($dist) = $line =~ m|$tagsets/(\S*).dist|;
+	print STDERR "$dist\n";
+    }
+    exit 2;
+}
+
+sub load_distfile {
+    # open and read the distribution file
+    # results go into @cvsname, @branchtag, @branchver, @mode
+    $file = "$tagsets/$ARGV[0].dist";
+    open (FILE, $file) || die "ERROR: can't open distribution file $file\n";
+    @list = <FILE>;
+    close (FILE);
+
+    @mode = ();
+    @cvsname = ();
+    @branchtag = ();
+    @branchver = ();
+
+    foreach $line (@list) {
+	chop $line;
+	if ($line =~ m|^\s*$|) { next; }
+	if ($line =~ m|^\s*\#|) { next; }
+
+	($mode, $cvsname, $branchtag, $branchver) = split (" ", $line);
+	
+	if ($cvsname eq "") { die "missing module name\n"; }
+
+	($do_tag, $do_build, $do_dist, $do_update) = $mode =~ m|(\S)(\S)(\S)(\S)|;
+	if (($do_tag ne "Y") && ($do_tag ne "N")) { die "invalid tag option $do_tag\n"; }
+	if (($do_build ne "Y") && ($do_build ne "N")) { die "invalid tag option $do_build\n"; }
+	if (($do_dist ne "Y") && ($do_dist ne "N")) { die "invalid tag option $do_dist\n"; }
+	if (($do_update ne "Y") && ($do_update ne "N")) { die "invalid tag option $do_update\n"; }
+	
+	# print "module: $cvsname, branchtag: $branchtag, branchver: $branchver "; 
+	# print "tag: $do_tag, build: $do_build, dist: $do_dist, update: $do_update\n";
+
+	push @mode, $mode;
+	push @cvsname, $cvsname;
+	push @branchtag, $branchtag;
+	push @branchver, $branchver;
+    }
+}
+
+sub usage {
+    print STDERR "USAGE: psdist [options] (distribution]\n";
+    print STDERR "     : -tag (dev)      : set tags on cvs HEAD based on distribution table (requires -tag)\n";
+    print STDERR "     : -tag (rev)      : set tags on branch based on distribution table\n";
+    print STDERR "     : -tag (branch)   : create branch tags based on distribution table (requires -tag)\n\n";
+
+    print STDERR "     : -update (tags)  : update this tree from cvs to match distribution tags\n";
+    print STDERR "     : -update (head)  : update this tree from cvs to match distribution tags\n\n";
+
+    print STDERR "     : -dist (cvs)     : check out a distribution and leave CVS directories in place\n";
+    print STDERR "     : -dist (tree)    : check out a distribution and leave a clean tree\n";
+    print STDERR "     : -dist (autogen) : check out a distribution and run autogen\n";
+    print STDERR "     : -dist (tarball) : check out a distribution and make a tarball\n";
+    print STDERR "     : -head           : use the cvs HEAD for distribution checkout (requires -dist)\n\n";
+
+    print STDERR "     : -module         : perform action only on the specified module\n";
+    print STDERR "     : -list           : list valid distributions \n";
+    print STDERR "     : -diff           : show the difference between the distribution and current cvs HEAD\n\n";
+    exit 2;
+}
+
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.dist	(revision 17422)
@@ -0,0 +1,53 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-5         -0
+  NNYY  libohana       	libohana-1-9     -0
+  NNYY  libfits        	libfits-1-7      -0
+  NNYY  libautocode    	libautocode-1-6  -0
+  NNYY  libdvo         	libdvo-1-4       -0
+  NNYY  libkapa        	libkapa-1-3      -0
+  NNYY  addstar        	addstar-1-8      -0
+  NNYY  delstar        	delstar-1-7      -0
+  NNYY  getstar        	getstar-1-3      -0
+  NNYY  kapa           	kapa-1-7         -0
+  NNYY  kii            	kii-1-7          -0
+  NNYY  relphot        	relphot-1-5      -0
+  NNYY  uniphot        	uniphot-1-5      -0
+  NNYY  opihi.base     	opihi-2-9        -0
+  NNYY  mana           	mana-1-7         -0
+  NNYY  dvo            	dvo-1-0          -0
+  NNYY  pantasks       	pantasks-1-0     -0
+  NNYY  pcontrol       	pcontrol-1-0     -0
+  NNYY  pclient        	pclient-1-0      -0
+  NYYY  psLib          	rel-1-0          -0
+  NYYY  psModules      	rel-1-0          -0
+  YYYY  psphot         	rel-0-8          -0
+  YYYY  psastro        	rel-0-8          -0
+  YYYY  ppStats        	rel-1-0          -0
+  YYYY  ppImage        	rel-1-0          -0
+  YYYY  ppNorm         	rel-1-0          -0
+  YYYY  ppMerge        	rel-1-0          -0
+  YNYY  pois           	rel-0-1          -0
+  YNYY  pswarp         	rel-0-1          -0
+  YNYY  ppStac         	rel-0-1          -0
+  YYYY  PS-IPP-Metadata-Config rel-1-0   -0
+  YYYY  PS-IPP-Config  	rel-1-0          -0
+  YYYY  ippScripts     	rel-1-0          -0
+  YYYY  glueforge      	rel-1-0          -0
+  YYYY  dbconfig       	rel-1-0          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-0          -0
+  YYYY  ippTools       	rel-1-0          -0
+  YYYY  ippTasks       	rel-1-0          -0
+  YYYY  simtest        	rel-0-8          -0
+  YNYY  psconfig       	rel-1-0          -0
+  YNYY  ippMonitor     	rel-1-0          -0
+
+# there are externally required perl modules (see README?)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.0.perl	(revision 17422)
@@ -0,0 +1,59 @@
+# NN    Name                           Tarball                                Optional Responses
+  00    Module::Build                  Module-Build-0.2806.tar.gz                
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz            
+  02    Params::Validate               Params-Validate-0.87.tar.gz               
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz             
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz               
+  05    Time::Local                    Time-Local-1.17.tar.gz                    
+  06    DateTime                       DateTime-0.36.tar.gz                      
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                   
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz             
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz            
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz             
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz    
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz                
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                   
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                   
+  15    Digest                         Digest-1.15.tar.gz                        
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz             
+  17    version                        version-0.70.tar.gz
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz               
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz     
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz             
+  21    URI                            URI-1.35.tar.gz                           
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz          
+  23    Test::Exception                Test-Exception-0.24.tar.gz                
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz                 
+  25    Array::Compare                 Array-Compare-1.13.tar.gz                 
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz                   
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                    
+  28    Net::FTP                       libnet-1.19.tar.gz                        
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz                
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz        
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz              
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz                
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz     
+  34    CGI                            CGI.pm-3.25.tar.gz                        
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                      
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz               
+  37    LWP                            libwww-perl-5.805.tar.gz             
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz               
+  39    File::Temp                     File-Temp-0.18.tar.gz                     
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz             
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                     
+  42    YAML                           YAML-0.62.tar.gz                          y
+  43    Module::Load                   Module-Load-0.10.tar.gz                   
+  44    Params::Check                  Params-Check-0.25.tar.gz                  
+  45    Template                       Template-Toolkit-2.16.tar.gz              n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz         
+  47    Storable                       Storable-2.15.tar.gz                      
+  48    IO::String                     IO-String-1.08.tar.gz                     
+  49    Date::Parse                    TimeDate-1.16.tar.gz                      
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz                   
+  51    DB_File                        DB_File-1.814.tar.gz                      
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz                  
+  53    Heap                           Heap-0.71.tar.gz                          
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz       
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                       
+  56    Cache                          Cache-2.04.tar.gz                         
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                       
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.dist	(revision 17422)
@@ -0,0 +1,55 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-6         -1
+  NNYY  libohana       	libohana-1-10    -1
+  NNYY  libfits        	libfits-1-8      -1
+  NNYY  libautocode    	libautocode-1-7  -1
+  NNYY  libdvo         	libdvo-1-5       -1
+  NNYY  libkapa        	libkapa-1-4      -1
+  NNYY  addstar        	addstar-1-9      -1
+  NNYY  delstar        	delstar-1-8      -1
+  NNYY  getstar        	getstar-1-4      -1
+  NNYY  kapa           	kapa-1-8         -1
+  NNYY  kii            	kii-1-8          -1
+  NNYY  relphot        	relphot-1-6      -1
+  NNYY  uniphot        	uniphot-1-6      -1
+  NNYY  opihi.base     	opihi-2-10       -1
+  NNYY  mana           	mana-1-8         -1
+  NNYY  dvo            	dvo-1-1          -1
+  NNYY  pantasks       	pantasks-1-1     -1
+  NNYY  pcontrol       	pcontrol-1-1     -1
+  NNYY  pclient        	pclient-1-1      -1
+  NYYY  psLib          	rel-1-1          -0
+  NYYY  psModules      	rel-1-1          -0
+  YYYY  psphot         	rel-0-9          -0
+  YYYY  psastro        	rel-0-9          -0
+  YYYY  ppStats        	rel-1-1          -0
+  YYYY  ppImage        	rel-1-1          -0
+  YYYY  ppNorm         	rel-1-1          -0
+  YYYY  ppMerge        	rel-1-1          -0
+  YNYY  pedestal       	rel-1-1          -0
+  YNYY  dvoTools       	rel-1-0          -0
+  YNYY  pois           	rel-0-1          -1
+  YNYY  pswarp         	rel-0-1          -1
+  YNYY  ppStac         	rel-0-1          -1
+  YYYY  PS-IPP-Metadata-Config rel-1-1   -0
+  YYYY  PS-IPP-Config  	rel-1-1          -0
+  YYYY  ippScripts     	rel-1-1          -0
+  YYYY  glueforge      	rel-1            _01
+  NYYY  dbconfig       	rel-1_1          _13
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-1          -0
+  NYYY  ippTools       	rel-1_1          _13
+  YYYY  ippTasks       	rel-1-1          -0
+  YYYY  simtest        	rel-0-9          -0
+  YNYY  psconfig       	rel-1-1          -0
+  YNYY  ippMonitor     	rel-1-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.1.perl	(revision 17422)
@@ -0,0 +1,60 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0	    
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.dist	(revision 17422)
@@ -0,0 +1,56 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	base-1-6         -1
+  NNYY  libohana       	libohana-1-10    -1
+  NNYY  libfits        	libfits-1-8      -1
+  NNYY  libautocode    	libautocode-1-7  -1
+  NNYY  libdvo         	libdvo-1-5       -1
+  NNYY  libkapa        	libkapa-1-4      -1
+  NNYY  addstar        	addstar-1-9      -1
+  NNYY  delstar        	delstar-1-8      -1
+  NNYY  getstar        	getstar-1-4      -1
+  NNYY  kapa           	kapa-1-8         -1
+  NNYY  kii            	kii-1-8          -1
+  NNYY  relphot        	relphot-1-6      -1
+  NNYY  uniphot        	uniphot-1-6      -1
+  NNYY  opihi.base     	opihi-2-10       -1
+  NNYY  mana           	mana-1-8         -1
+  NNYY  dvo            	dvo-1-1          -1
+  NNYY  pantasks       	pantasks-1-1     -1
+  NNYY  pcontrol       	pcontrol-1-1     -1
+  NNYY  pclient        	pclient-1-1      -1
+  NYYY  psLib          	rel-1-1          -0
+  NYYY  psModules      	rel-1-1          -0
+  YYYY  psphot         	rel-0-9          -0
+  YYYY  psastro        	rel-0-9          -0
+  YYYY  ppStats        	rel-1-1          -0
+  YYYY  ppConfigDump   	rel-1-1          -0
+  YYYY  ppImage        	rel-1-1          -0
+  YYYY  ppNorm         	rel-1-1          -0
+  YYYY  ppMerge        	rel-1-1          -0
+  YYYY  pedestal       	rel-1-1          -0
+  YYYY  dvoTools       	rel-1-0          -0
+  YYYY  pois           	rel-0-1          -1
+  YYYY  pswarp         	rel-0-1          -1
+  YYYY  ppStac         	rel-0-1          -1
+  YYYY  PS-IPP-Metadata-Config rel-1-1   -0
+  YYYY  PS-IPP-Config  	rel-1-1          -0
+  YYYY  ippScripts     	rel-1-1          -0
+  YYYY  glueforge      	rel-1            _01
+  NYYY  dbconfig       	rel-1_1          _13
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	rel-1-1          -0
+  NYYY  ippTools       	rel-1_1          _13
+  YYYY  ippTasks       	rel-1-1          -0
+  YYYY  simtest        	rel-0-9          -0
+  YNYY  psconfig       	rel-1-1          -0
+  YNYY  ippMonitor     	rel-1-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-1.2.perl	(revision 17422)
@@ -0,0 +1,60 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0	    
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.dist	(revision 17422)
@@ -0,0 +1,64 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-0  	 -0
+  NNYY  libohana       	ipp-2-0  	 -0
+  NNYY  libfits        	ipp-2-0  	 -0
+  NNYY  libautocode    	ipp-2-0  	 -0
+  NNYY  libdvo         	ipp-2-0  	 -0
+  NNYY  libkapa        	ipp-2-0  	 -0
+  NNYY  libtap.ohana   	ipp-2-0  	 -0
+  NNYY  addstar        	ipp-2-0  	 -0
+  NNYY  delstar        	ipp-2-0  	 -0
+  NNYY  getstar        	ipp-2-0  	 -0
+  NNYY  ohana.tools    	ipp-2-0  	 -0
+  NNYY  kapa           	ipp-2-0  	 -0
+  NNYY  kii            	ipp-2-0  	 -0
+  NNYY  relphot        	ipp-2-0  	 -0
+  NNYY  relastro       	ipp-2-0  	 -0
+  NNYY  uniphot        	ipp-2-0  	 -0
+  NNYY  opihi.base     	ipp-2-0  	 -0
+  NNYY  mana           	ipp-2-0  	 -0
+  NNYY  dvo            	ipp-2-0  	 -0
+  NNYY  pantasks       	ipp-2-0  	 -0
+  NNYY  pcontrol       	ipp-2-0  	 -0
+  NNYY  pclient        	ipp-2-0  	 -0      
+
+  YYYN  nebclient       ipp-2-0          -0
+  YYYN  Nebulous        ipp-2-0          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-0   -0
+  YYYY  PS-IPP-Config  	ipp-2-0          -0
+
+  NYYY  psLib          	ipp-2-0          -0
+  NYYY  psModules      	ipp-2-0          -0
+  YYYY  psphot         	ipp-2-0          -0
+  YYYY  psastro        	ipp-2-0          -0
+  YYYY  ppStats        	ipp-2-0          -0
+  YYYY  ppConfigDump   	ipp-2-0          -0
+  YYYY  ppImage        	ipp-2-0          -0
+  YYYY  ppNorm         	ipp-2-0          -0
+  YYYY  ppMerge        	ipp-2-0          -0
+  YYYY  pedestal       	ipp-2-0          -0
+  YYYY  dvoTools       	ipp-2-0          -0
+  YYYY  pois           	ipp-2-0          -0
+  YYYY  pswarp         	ipp-2-0          -0
+  YYYY  ppStac         	ipp-2-0          -0
+  YYYY  ppSim        	ipp-2-0          -0
+  YYYY  ippScripts     	ipp-2-0          -0
+  YYYY  glueforge      	ipp-2-0          -0
+  YYYY  dbconfig       	ipp-2-0          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippconfig      	ipp-2-0          -0
+  YYYY  ippTools       	ipp-2-0          -0
+  YYYY  ippTasks       	ipp-2-0          -0
+  YYYY  simtest        	ipp-2-0          -0
+  YNYY  psconfig       	ipp-2-0          -0
+  YNYY  ippMonitor     	ipp-2-0          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.0.perl	(revision 17422)
@@ -0,0 +1,64 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+  60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.dist	(revision 17422)
@@ -0,0 +1,65 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-1  	 -0
+  NNYY  libohana       	ipp-2-1  	 -0
+  NNYY  libfits        	ipp-2-1  	 -0
+  NNYY  libautocode    	ipp-2-1  	 -0
+  NNYY  libdvo         	ipp-2-1  	 -0
+  NNYY  libkapa        	ipp-2-1  	 -0
+  NNYY  libtap.ohana   	ipp-2-1  	 -0
+  NNYY  addstar        	ipp-2-1  	 -0
+  NNYY  delstar        	ipp-2-1  	 -0
+  NNYY  getstar        	ipp-2-1  	 -0
+  NNYY  ohana.tools    	ipp-2-1  	 -0
+  NNYY  kapa2          	ipp-2-1  	 -0
+  NNYY  relphot        	ipp-2-1  	 -0
+  NNYY  relastro       	ipp-2-1  	 -0
+  NNYY  uniphot        	ipp-2-1  	 -0
+  NNYY  opihi.base     	ipp-2-1  	 -0
+  NNYY  mana           	ipp-2-1  	 -0
+  NNYY  dvo            	ipp-2-1  	 -0
+  NNYY  pantasks       	ipp-2-1  	 -0
+  NNYY  pcontrol       	ipp-2-1  	 -0
+  NNYY  pclient        	ipp-2-1  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-1       -0
+  YYYN  Nebulous        ipp-2-1          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-1   -0
+  YYYY  PS-IPP-Config  	ipp-2-1          -0
+
+  NYYY  psLib          	ipp-2-1          -0
+  NYYY  psModules      	ipp-2-1          -0
+  YYYY  psphot         	ipp-2-1          -0
+  YYYY  psastro        	ipp-2-1          -0
+  YYYY  ppStats        	ipp-2-1          -0
+  YYYY  ppConfigDump   	ipp-2-1          -0
+  YYYY  ppImage        	ipp-2-1          -0
+  YYYY  ppNorm         	ipp-2-1          -0
+  YYYY  ppMerge        	ipp-2-1          -0
+  YYYY  pedestal       	ipp-2-1          -0
+  YYYY  dvoTools       	ipp-2-1          -0
+  YYYY  pswarp         	ipp-2-1          -0
+  YYYY  ppStack        	ipp-2-1          -0
+  YYYY  ppSub          	ipp-2-1          -0
+  YYYY  ppSim        	ipp-2-1          -0
+
+  YYYY  glueforge      	ipp-2-1          -0
+  YYYY  dbconfig       	ipp-2-1          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-1          -0
+  YYYY  ippScripts     	ipp-2-1          -0
+  YYYY  ippTasks       	ipp-2-1          -0
+
+  YYYY  ippconfig      	ipp-2-1          -0
+  YYYY  simtest        	ipp-2-1          -0
+  YNYY  psconfig       	ipp-2-1          -0
+  YNYY  ippMonitor     	ipp-2-1          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.1.perl	(revision 17422)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.dist	(revision 17422)
@@ -0,0 +1,65 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-2  	 -0
+  NNYY  libohana       	ipp-2-2  	 -0
+  NNYY  libfits        	ipp-2-2  	 -0
+  NNYY  libautocode    	ipp-2-2  	 -0
+  NNYY  libdvo         	ipp-2-2  	 -0
+  NNYY  libkapa        	ipp-2-2  	 -0
+  NNYY  libtap.ohana   	ipp-2-2  	 -0
+  NNYY  addstar        	ipp-2-2  	 -0
+  NNYY  delstar        	ipp-2-2  	 -0
+  NNYY  getstar        	ipp-2-2  	 -0
+  NNYY  ohana.tools    	ipp-2-2  	 -0
+  NNYY  kapa2          	ipp-2-2  	 -0
+  NNYY  relphot        	ipp-2-2  	 -0
+  NNYY  relastro       	ipp-2-2  	 -0
+  NNYY  uniphot        	ipp-2-2  	 -0
+  NNYY  opihi.base     	ipp-2-2  	 -0
+  NNYY  mana           	ipp-2-2  	 -0
+  NNYY  dvo            	ipp-2-2  	 -0
+  NNYY  pantasks       	ipp-2-2  	 -0
+  NNYY  pcontrol       	ipp-2-2  	 -0
+  NNYY  pclient        	ipp-2-2  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-2       -0
+  YYYN  Nebulous        ipp-2-2          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-2   -0
+  YYYY  PS-IPP-Config  	ipp-2-2          -0
+
+  NYYY  psLib          	ipp-2-2          -0
+  NYYY  psModules      	ipp-2-2          -0
+  YYYY  psphot         	ipp-2-2          -0
+  YYYY  psastro        	ipp-2-2          -0
+  YYYY  ppStats        	ipp-2-2          -0
+  YYYY  ppConfigDump   	ipp-2-2          -0
+  YYYY  ppImage        	ipp-2-2          -0
+  YYYY  ppNorm         	ipp-2-2          -0
+  YYYY  ppMerge        	ipp-2-2          -0
+  YYYY  pedestal       	ipp-2-2          -0
+  YYYY  dvoTools       	ipp-2-2          -0
+  YYYY  pswarp         	ipp-2-2          -0
+  YYYY  ppStack        	ipp-2-2          -0
+  YYYY  ppSub          	ipp-2-2          -0
+  YYYY  ppSim        	ipp-2-2          -0
+
+  YYYY  glueforge      	ipp-2-2          -0
+  YYYY  dbconfig       	ipp-2-2          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-2          -0
+  YYYY  ippScripts     	ipp-2-2          -0
+  YYYY  ippTasks       	ipp-2-2          -0
+
+  YYYY  ippconfig      	ipp-2-2          -0
+  YYYY  simtest        	ipp-2-2          -0
+  YNYY  psconfig       	ipp-2-2          -0
+  YNYY  ippMonitor     	ipp-2-2          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.libs
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.libs	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.libs	(revision 17422)
@@ -0,0 +1,88 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y NONE NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.2.perl	(revision 17422)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.dist	(revision 17422)
@@ -0,0 +1,66 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update?
+# ||||
+# ||||  CVS module     CVS branch tag   CVS branch version   
+# |||| 
+  NYNN  Ohana
+  NNYY  ohana.base     	ipp-2-3  	 -0
+  NNYY  libohana       	ipp-2-3  	 -0
+  NNYY  libfits        	ipp-2-3  	 -0
+  NNYY  libautocode    	ipp-2-3  	 -0
+  NNYY  libdvo         	ipp-2-3  	 -0
+  NNYY  libkapa        	ipp-2-3  	 -0
+  NNYY  libtap.ohana   	ipp-2-3  	 -0
+  NNYY  addstar        	ipp-2-3  	 -0
+  NNYY  delstar        	ipp-2-3  	 -0
+  NNYY  getstar        	ipp-2-3  	 -0
+  NNYY  ohana.tools    	ipp-2-3  	 -0
+  NNYY  kapa2          	ipp-2-3  	 -0
+  NNYY  relphot        	ipp-2-3  	 -0
+  NNYY  relastro       	ipp-2-3  	 -0
+  NNYY  uniphot        	ipp-2-3  	 -0
+  NNYY  opihi.base     	ipp-2-3  	 -0
+  NNYY  mana           	ipp-2-3  	 -0
+  NNYY  dvo            	ipp-2-3  	 -0
+  NNYY  pantasks       	ipp-2-3  	 -0
+  NNYY  pcontrol       	ipp-2-3  	 -0
+  NNYY  pclient        	ipp-2-3  	 -0      
+
+  NYNN  Nebulous/nebclient ipp-2-3       -0
+  YYYN  Nebulous        ipp-2-3          -0
+  YYYY  PS-IPP-Metadata-Config ipp-2-3   -0
+  YYYY  PS-IPP-Config  	ipp-2-3          -0
+
+  NYYY  psLib          	ipp-2-3          -0
+  NYYY  psModules      	ipp-2-3          -0
+  YYYY  psphot         	ipp-2-3          -0
+  YYYY  psastro        	ipp-2-3          -0
+  YYYY  ppStats        	ipp-2-3          -0
+  YYYY  ppConfigDump   	ipp-2-3          -0
+  YYYY  ppImage        	ipp-2-3          -0
+  YYYY  ppNorm         	ipp-2-3          -0
+  YYYY  ppMerge        	ipp-2-3          -0
+  YYYY  pedestal       	ipp-2-3          -0
+  YYYY  dvoTools       	ipp-2-3          -0
+  YYYY  pswarp         	ipp-2-3          -0
+  YYYY  ppStack        	ipp-2-3          -0
+  YYYY  ppSub          	ipp-2-3          -0
+  YYYY  ppSim        	ipp-2-3          -0
+  YYYY  ppstamp        	ipp-2-3          -0
+
+  YYYY  glueforge      	ipp-2-3          -0
+  YYYY  dbconfig       	ipp-2-3          -0
+  NYNN  ippdb.src      			      
+  YYYY  ippTools       	ipp-2-3          -0
+  YYYY  ippScripts     	ipp-2-3          -0
+  YYYY  ippTasks       	ipp-2-3          -0
+
+  YYYY  ippconfig      	ipp-2-3          -0
+  YYYY  simtest        	ipp-2-3          -0
+  YNYY  psconfig       	ipp-2-3          -0
+  YNYY  ippMonitor     	ipp-2-3          -0
+
+# there are externally required perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.libs
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.libs	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.libs	(revision 17422)
@@ -0,0 +1,88 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y NONE NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.3.perl	(revision 17422)
@@ -0,0 +1,67 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.dist	(revision 17422)
@@ -0,0 +1,69 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  NYNNY  Ohana
+  NNYYN  ohana.base             ipp-2-4          -0
+  NNYYN  libohana               ipp-2-4          -0
+  NNYYN  libfits                ipp-2-4          -0
+  NNYYN  libautocode            ipp-2-4          -0
+  NNYYN  libdvo                 ipp-2-4          -0
+  NNYYN  libkapa                ipp-2-4          -0
+  NNYYN  libtap.ohana           ipp-2-4          -0
+  NNYYN  addstar                ipp-2-4          -0
+  NNYYN  delstar                ipp-2-4          -0
+  NNYYN  getstar                ipp-2-4          -0
+  NNYYN  ohana.tools            ipp-2-4          -0
+  NNYYN  kapa2                  ipp-2-4          -0
+  NNYYN  relphot                ipp-2-4          -0
+  NNYYN  relastro               ipp-2-4          -0
+  NNYYN  uniphot                ipp-2-4          -0
+  NNYYN  opihi.base             ipp-2-4          -0
+  NNYYN  mana                   ipp-2-4          -0
+  NNYYN  dvo                    ipp-2-4          -0
+  NNYYN  pantasks               ipp-2-4          -0
+  NNYYN  pcontrol               ipp-2-4          -0
+  NNYYN  pclient                ipp-2-4          -0      
+          
+  NYNNY  Nebulous/nebclient     ipp-2-4          -0
+  YYYNY  Nebulous               ipp-2-4          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-4          -0
+  YYYYY  PS-IPP-Config          ipp-2-4          -0     
+          
+  YYYYY  psLib                  ipp-2-4          -0
+  YYYYY  psModules              ipp-2-4          -0
+  YYYYY  psphot                 ipp-2-4          -0
+  YYYYY  psastro                ipp-2-4          -0
+  YYYYY  ppStats                ipp-2-4          -0
+  YYYYY  ppConfigDump           ipp-2-4          -0
+  YYYYY  ppImage                ipp-2-4          -0
+  YYYYY  ppNorm                 ipp-2-4          -0
+  YYYYY  ppMerge                ipp-2-4          -0
+  YYYYY  pedestal               ipp-2-4          -0
+  YYYYY  dvoTools               ipp-2-4          -0
+  YYYYY  pswarp                 ipp-2-4          -0
+  YYYYY  ppArith                ipp-2-4          -0
+  YYYYY  ppStack                ipp-2-4          -0
+  YYYYY  ppSub                  ipp-2-4          -0
+  YYYYY  ppSim                  ipp-2-4          -0
+  YNYYN  pstamp                 ipp-2-4          -0
+          
+  YNNYY  glueforge              ipp-2-4          -0
+  YNNYY  dbconfig               ipp-2-4          -0
+  NNNNY  ippdb.src             
+  NYYNN  ippdb                  ipp-2-4          -0
+  YYYYY  ippTools               ipp-2-4          -0
+  YYYYY  ippScripts             ipp-2-4          -0
+  YYYYY  ippTasks               ipp-2-4          -0
+          
+  YYYYY  ippconfig              ipp-2-4          -0
+  YYYYY  simtest                ipp-2-4          -0
+  YNYYN  psconfig               ipp-2-4          -0
+  YNYYN  ippMonitor             ipp-2-4          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.libs
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.libs	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.libs	(revision 17422)
@@ -0,0 +1,91 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+# lib libcfitsio         NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libcfitsio           NONE           NONE   cfitsio3060.tar.gz       cfitsio          Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+# lib libfftw3             NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.4.perl	(revision 17422)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.dist
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.dist	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.dist	(revision 17422)
@@ -0,0 +1,69 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  CVS module             CVS branch tag   CVS branch version   
+# ||||| 
+  NYNNY  Ohana
+  NNYYN  ohana.base             ipp-2-5          -0
+  NNYYN  libohana               ipp-2-5          -0
+  NNYYN  libfits                ipp-2-5          -0
+  NNYYN  libautocode            ipp-2-5          -0
+  NNYYN  libdvo                 ipp-2-5          -1
+  NNYYN  libkapa                ipp-2-5          -0
+  NNYYN  libtap.ohana           ipp-2-5          -0
+  NNYYN  addstar                ipp-2-5          -1
+  NNYYN  delstar                ipp-2-5          -0
+  NNYYN  getstar                ipp-2-5          -0
+  NNYYN  ohana.tools            ipp-2-5          -0
+  NNYYN  kapa2                  ipp-2-5          -1
+  NNYYN  relphot                ipp-2-5          -1
+  NNYYN  relastro               ipp-2-5          -1
+  NNYYN  uniphot                ipp-2-5          -0
+  NNYYN  opihi.base             ipp-2-5          -1
+  NNYYN  mana                   ipp-2-5          -1
+  NNYYN  dvo                    ipp-2-5          -1
+  NNYYN  pantasks               ipp-2-5          -1
+  NNYYN  pcontrol               ipp-2-5          -1
+  NNYYN  pclient                ipp-2-5          -1      
+          
+  YYYYY  nebclient              ipp-2-5          -0
+  YYYNY  Nebulous               ipp-2-5          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-5          -0
+  YYYYY  PS-IPP-Config          ipp-2-5          -0     
+          
+  YYYYY  psLib                  ipp-2-5          -0
+  YYYYY  psModules              ipp-2-5          -0
+  YYYYY  psphot                 ipp-2-5          -0
+  YYYYY  psastro                ipp-2-5          -0
+  YYYYY  ppStats                ipp-2-5          -0
+  YYYYY  ppConfigDump           ipp-2-5          -0
+  YYYYY  ppImage                ipp-2-5          -0
+  YYYYY  ppNorm                 ipp-2-5          -0
+  YYYYY  ppMerge                ipp-2-5          -0
+  YYYYY  pedestal               ipp-2-5          -0
+  YYYYY  dvoTools               ipp-2-5          -0
+  YYYYY  pswarp                 ipp-2-5          -0
+  YYYYY  ppArith                ipp-2-5          -0
+  YYYYY  ppStack                ipp-2-5          -0
+  YYYYY  ppSub                  ipp-2-5          -0
+  YYYYY  ppSim                  ipp-2-5          -0
+          
+  YNNYY  glueforge              ipp-2-5          -0
+  YNNYY  dbconfig               ipp-2-5          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-5          -0
+  YYYYY  pstamp                 ipp-2-5          -0
+  YYYYY  ippTools               ipp-2-5          -0
+  YYYYY  ippScripts             ipp-2-5          -0
+  YYYYY  ippTasks               ipp-2-5          -0
+          
+  YYYYY  ippconfig              ipp-2-5          -0
+  YYYYY  simtest                ipp-2-5          -0
+  YNYYN  psconfig               ipp-2-5          -1
+  YNYYN  ippMonitor             ipp-2-5          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.libs
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.libs	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.libs	(revision 17422)
@@ -0,0 +1,92 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      Y NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2.tar.gz      readline-5.2     Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       Y --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    Y NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          Y --enable-shared NONE install-lib
+# lib libcfitsio         NONE           NONE   cfitsio2510.tar.gz       cfitsio          Y NONE NONE NONE
+lib libcfitsio           NONE           NONE   cfitsio3060.tar.gz       cfitsio          Y NONE NONE NONE
+#lib libmysqlclient       NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     Y NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz      mysql-5.0.51a     Y NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.6.tar.gz           gsl-1.6          Y NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-float,--enable-shared,--disable-fortran NONE NONE
+# lib libfftw3             NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       Y --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  Y NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fitsio.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/readline.h  NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.perl
===================================================================
--- /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.perl	(revision 17422)
+++ /tags/ipp-2-5-1/psconfig/tagsets/ipp-2.5.perl	(revision 17422)
@@ -0,0 +1,79 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806         
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0         
+  02    Params::Validate               Params-Validate-0.87.tar.gz              0.77         
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0         
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0         
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0         
+  06    DateTime                       DateTime-0.36.tar.gz                     0         
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0         
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0         
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0         
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0         
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0         
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0         
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0         
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0         
+  15    Digest                         Digest-1.15.tar.gz                       0         
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz         	0	            
+  17    version                        version-0.70.tar.gz		     	0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz           	0	    
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz 	0	    
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz         	0	    
+  21    URI                            URI-1.35.tar.gz                       	1.30	    
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz      	0	    
+  23    Test::Exception                Test-Exception-0.24.tar.gz            	0	    
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz             	0	    
+  25    Array::Compare                 Array-Compare-1.13.tar.gz             	0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz               	0	    
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                	0	    
+  28    Net::FTP                       libnet-1.19.tar.gz                    	0	    
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz            	0	    
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz    	0	    
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz          	1.94	    
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz            	0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.0403.tar.gz 	0.0402
+  34    CGI                            CGI.pm-3.25.tar.gz                    	3	      
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                  	1.05	    
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz           	1.1.1	    
+  37    LWP                            libwww-perl-5.805.tar.gz              	0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz           	0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                 	0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz         	0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                 	0	    
+  42    YAML                           YAML-0.62.tar.gz                      	0.58	       y
+  43    Module::Load                   Module-Load-0.10.tar.gz               	0	    
+  44    Params::Check                  Params-Check-0.25.tar.gz              	0	    
+  45    Template                       Template-Toolkit-2.16.tar.gz          	0	       n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz     	2.6
+  47    Storable                       Storable-2.15.tar.gz                  	0	    
+  48    IO::String                     IO-String-1.08.tar.gz                 	0	    
+  49    Date::Parse                    TimeDate-1.16.tar.gz                  	0	    
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz               	0	    
+  51    DB_File                        DB_File-1.814.tar.gz                  	0	    
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz              	0	    
+  53    Heap                           Heap-0.71.tar.gz                      	0	    
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz   	0	    
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                   	0	    
+  56    Cache                          Cache-2.04.tar.gz                     	0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                   	0.36	    
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More    		       Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI   		       Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP 		       Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI     		       Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML  	       	       Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr 	       	       File-ExtAttr-1.07.tar.gz                 0
+  73    DBI           	       	       DBI-1.601.tar.gz                         0
+  71    DBD::mysql    	       	       DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize 	       Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                     File-Path-2.04.tar.gz
