Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/.cvsignore
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/.cvsignore	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/.cvsignore	(revision 13183)
@@ -0,0 +1,1 @@
+lib
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/Makefile
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/Makefile	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/Makefile	(revision 13183)
@@ -0,0 +1,102 @@
+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)
+
+default: install
+install: $(DESTLIB)/libdvo.a $(DESTLIB)/libdvo.$(DLLTYPE)
+libdvo: $(LIB)/libdvo.$(ARCH).a $(LIB)/libdvo.$(ARCH).$(DLLTYPE)
+
+INCS = $(DESTINC)/dvo.h $(DESTINC)/autocode.h
+
+OBJS = \
+$(SRC)/version.$(ARCH).o	 \
+$(SRC)/coordops.$(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)/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)
+$(AOBJS): $(ADEF)/autocode.c $(ADEF)/autocode.h $(AINC)/autocode.h
+
+$(AINC)/autocode.h: $(AINCS)
+
+$(DESTINC)/autocode.h: $(AINC)/autocode.h
+	@if [ ! -d $(DESTINC) ]; then mkdir -p $(DESTINC); fi
+	rm -f $@
+	cp $< $@
+
+bar: $(DESTINC)/autocode.h
+
+foo:
+	@echo $(DESTINC)/autocode.h
+	@echo $(AINC)/autocode.h
+	@echo $(AINCS)
+
+$(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)
+
+$(ASRC)/%.$(ARCH).o : $(ADEF)/%.d
+	echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+$(AINC)/%.h : $(ADEF)/%.d
+	echo "make $@ from $^"
+	cd $(AUTO) && make $@
+
+$(AINC)/autocode.h: $(AINCS) $(ADEF)/common.h
+	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/libdvo-1-6-0/Ohana/src/libdvo/doc/ChangeLog.txt
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/ChangeLog.txt	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-catalogs.txt
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-catalogs.txt	(revision 13183)
@@ -0,0 +1,46 @@
+
+// APIs related to the DVO catalogs:
+
+// XXX change name
+// XXX add SORTED test to load
+// XXX add Nsecfilt test? or add an API?
+load_catalog (Catalog *catalog, int VERBOSE);
+// returns
+//   0: failure to lock catalog
+//   1: success: file is locked and opened
+//   2: success: file is locked but empty
+
+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
+
+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); 
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-structures.txt
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/dvo-structures.txt	(revision 13183)
@@ -0,0 +1,167 @@
+
+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/libdvo-1-6-0/Ohana/src/libdvo/doc/fits_db.txt
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/fits_db.txt	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/fits_db.txt	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/doc/notes.txt
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/notes.txt	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/doc/notes.txt	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.h
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.h	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.h	(revision 13183)
@@ -0,0 +1,425 @@
+# 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, 
+      DVO_FORMAT_PANSTARRS_DEV_0
+} 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
+} OhanaProjection;
+
+typedef enum {
+  PROJ_MODE_NONE,
+  PROJ_MODE_CARTESIAN,
+  PROJ_MODE_ZENITHAL,
+  PROJ_MODE_PSEUDOCYL,
+} OhanaProjectionMode;
+
+/* 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;					  // 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 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_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 GetRegisteredMosaic ();
+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);
+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, 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);
+
+Image 		       *ImageElixirToInternal (ImageElixir *in, int Nvalues);
+Image 		       *ImageLoneosToInternal (ImageLoneos *in, int Nvalues);
+Image 		       *ImagePanstarrsToInternal (ImagePanstarrs *in, int Nvalues);
+Image 		       *ImagePanstarrs_DEV_0_ToInternal (ImagePanstarrs_DEV_0 *in, int Nvalues);
+
+ImageElixir 	       *ImageInternalToElixir (Image *in, int Nvalues);
+ImageLoneos 	       *ImageInternalToLoneos (Image *in, int Nvalues);
+ImagePanstarrs         *ImageInternalToPanstarrs (Image *in, int Nvalues);
+ImagePanstarrs_DEV_0   *ImageInternalToPanstarrs_DEV_0 (Image *in, int Nvalues);
+
+Average                *AverageLoneosToInternal (AverageLoneos *in, int Nvalues, SecFilt **primary);
+Average 	       *AverageElixirToInternal (AverageElixir *in, int Nvalues, SecFilt **primary);
+Average 	       *AveragePanstarrsToInternal (AveragePanstarrs *in, int Nvalues);
+Average 	       *AveragePanstarrs_DEV_0_ToInternal (AveragePanstarrs_DEV_0 *in, int Nvalues);
+
+AverageLoneos 	       *AverageInternalToLoneos (Average *in, int Nvalues, SecFilt *primary);
+AverageElixir 	       *AverageInternalToElixir (Average *in, int Nvalues, SecFilt *primary);
+AveragePanstarrs       *AverageInternalToPanstarrs (Average *in, int Nvalues);
+AveragePanstarrs_DEV_0 *AverageInternalToPanstarrs_DEV_0 (Average *in, int Nvalues);
+
+Measure                *MeasureLoneosToInternal (MeasureLoneos *in, int Nvalues);
+Measure 	       *MeasureElixirToInternal (MeasureElixir *in, int Nvalues);
+Measure 	       *MeasurePanstarrsToInternal (MeasurePanstarrs *in, int Nvalues);
+Measure 	       *MeasurePanstarrs_DEV_0_ToInternal (MeasurePanstarrs_DEV_0 *in, int Nvalues);
+
+MeasureLoneos 	       *MeasureInternalToLoneos (Measure *in, int Nvalues);
+MeasureElixir 	       *MeasureInternalToElixir (Measure *in, int Nvalues);
+MeasurePanstarrs       *MeasureInternalToPanstarrs (Measure *in, int Nvalues);
+MeasurePanstarrs_DEV_0 *MeasureInternalToPanstarrs_DEV_0 (Measure *in, int Nvalues);
+
+SecFilt 	       *SecFiltLoneosToInternal (SecFiltLoneos *in, int Nvalues);
+SecFilt 	       *SecFiltElixirToInternal (SecFiltElixir *in, int Nvalues);
+SecFilt 	       *SecFiltPanstarrsToInternal (SecFiltPanstarrs *in, int Nvalues);
+SecFilt 	       *SecFiltPanstarrs_DEV_0_ToInternal (SecFiltPanstarrs_DEV_0 *in, int Nvalues);
+
+SecFiltLoneos 	       *SecFiltInternalToLoneos (SecFilt *in, int Nvalues);
+SecFiltElixir 	       *SecFiltInternalToElixir (SecFilt *in, int Nvalues);
+SecFiltPanstarrs       *SecFiltInternalToPanstarrs (SecFilt *in, int Nvalues);
+SecFiltPanstarrs_DEV_0 *SecFiltInternalToPanstarrs_DEV_0 (SecFilt *in, int Nvalues);
+
+/*** 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 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/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.update.h
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.update.h	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/include/dvo.update.h	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/lib/.cvsignore
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/lib/.cvsignore	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/lib/.cvsignore	(revision 13183)
@@ -0,0 +1,1 @@
+*.linux *.lin64 *.sol *.sun *.sid *.hp *.irix
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodes.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesFITS.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesFITS.c	(revision 13183)
@@ -0,0 +1,110 @@
+# 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;
+
+  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) */
+  photcode = gfits_table_get_PhotCode (&db.ftable, &Ncode, &db.swapped);
+
+  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/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesText.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/LoadPhotcodesText.c	(revision 13183)
@@ -0,0 +1,208 @@
+# include <dvo.h>
+
+/* 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];
+  char name[32], type[32], Zero[32], Airmass[32], Offset[32],
+    C1[32], C2[32], Slope[32], Color[32], Primary[32];
+  int c1, c2;
+
+  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);
+
+  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", 
+		     &code, name, type, Zero, Airmass, Offset, C1, C2, Slope, Color, Primary);
+    if (Nfield != 11) { continue; }
+    
+    c1 = atof (C1);
+    c2 = atof (C2);
+    if (!strcmp (C1, "-")) { c1 = 0; }
+    if (!strcmp (C2, "-")) { c2 = 0; }
+
+    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;
+      photcode[Ncode].C     = 1000*atof (Zero);
+      photcode[Ncode].K     = atof (Airmass);
+      photcode[Ncode].dC    = 1000*atof (Offset);
+      photcode[Ncode].dX    = 1000*atof (Color);
+      photcode[Ncode].c1    = c1;
+      photcode[Ncode].c2    = c2;
+      photcode[Ncode].equiv = atoi (Primary);
+      ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+    }      
+    if (!strncasecmp (type, "sec", 3)) {
+      photcode[Ncode].type  = PHOT_SEC;
+      photcode[Ncode].C     = 1000*atof (Zero);
+      photcode[Ncode].K     = atof (Airmass);
+      photcode[Ncode].dC    = 1000*atof (Offset);
+      photcode[Ncode].dX    = 1000*atof (Color);
+      photcode[Ncode].c1    = c1;
+      photcode[Ncode].c2    = c2;
+      photcode[Ncode].equiv = atoi (Primary);
+      ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+    }      
+    if (!strncasecmp (type, "dep", 3)) {
+      photcode[Ncode].type  = PHOT_DEP;
+      photcode[Ncode].C     = 1000*atof (Zero);    /* zero point in millimags */
+      photcode[Ncode].K     = atof (Airmass);      /* airmass coeff (millimag / millimag) */
+      photcode[Ncode].dC    = 1000*atof (Offset);  /* color ref z.p. (millimag) */
+      photcode[Ncode].dX    = 1000*atof (Color);   /* average color (millimag) */
+      photcode[Ncode].c1    = c1;
+      photcode[Ncode].c2    = c2;
+      photcode[Ncode].equiv = atoi (Primary);
+      ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+    }      
+    if (!strncasecmp (type, "ref", 3)) {
+      photcode[Ncode].type  = PHOT_REF;
+      photcode[Ncode].C     = 0;
+      photcode[Ncode].K     = 0;
+      photcode[Ncode].dC    = 0;
+      photcode[Ncode].dX    = 0;
+      photcode[Ncode].c1    = 0;
+      photcode[Ncode].c2    = 0;
+      photcode[Ncode].equiv = atoi (Primary);
+      photcode[Ncode].X[0]  = 0;
+      photcode[Ncode].Nc    = 0;
+    }
+
+    /* 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 */
+    if (!strncasecmp (type, "alt", 3)) {
+      photcode[Ncode].type  = PHOT_ALT;
+      photcode[Ncode].C     = 1000*atof (Zero);    /* zero point in millimags */
+      photcode[Ncode].K     = atof (Airmass);      /* airmass coeff (millimag / millimag) */
+      photcode[Ncode].dC    = 1000*atof (Offset);  /* color ref z.p. (millimag) */
+      photcode[Ncode].dX    = 1000*atof (Color);   /* average color (millimag) */
+      photcode[Ncode].c1    = c1;
+      photcode[Ncode].c2    = c2;
+      photcode[Ncode].equiv = atoi (Primary);
+      ParseColorTerms (Slope, photcode[Ncode].X, &photcode[Ncode].Nc);
+    }
+    if (!photcode[Ncode].type) {
+      fprintf (stderr, "error in Photfile: unknown type %s\n", type);
+    }
+
+    Ncode++;
+    if (Ncode == NPHOTCODE) {
+      NPHOTCODE += 10;
+      REALLOCATE (photcode, PhotCode, NPHOTCODE);
+    }
+  }
+  fclose (f);
+  
+  /* 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/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesFITS.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 13183)
@@ -0,0 +1,41 @@
+# 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;
+  PhotCode *photcode;
+  FITS_DB db;
+
+  int i, code, Ncode, Nsec;
+
+  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;
+  }
+
+  /* convert FITS format data to internal format (just byteswaps) */
+  gfits_db_create (&db);
+  gfits_table_set_PhotCode (&db.ftable, table[0].code, table[0].Ncode);
+  gfits_db_save (&db);
+  gfits_db_close (&db);
+
+  /* gfits_table_set_PhotCode performs the needed byte swap.  swap back */
+  gfits_convert_PhotCode (table[0].code, sizeof(PhotCode), table[0].Ncode);
+  return TRUE;
+}
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesText.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 13183)
@@ -0,0 +1,96 @@
+# include <dvo.h>
+# define SCALE 0.001
+
+static char *PHOT_PRI_NAME = "pri";
+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;
+  PhotCode *photcode;
+  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;
+  }
+
+  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, " %f,", table[0].code[i].X[j]);
+    }
+    fprintf (f, "%f %f ", table[0].code[i].X[j], table[0].code[i].dX);
+    PrintPhotcodeNamebyCode (f, "%5d ", table[0].code[i].equiv);
+    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/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.c	(revision 13183)
@@ -0,0 +1,713 @@
+# include <dvo.h>
+
+static Coords *mosaic = NULL;
+
+void RegisterMosaic (Coords *coords) {
+  mosaic = coords;
+}
+
+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 (mosaic == NULL) 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_DIS:
+	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:
+	stht = (4 - RAD_DEG*R) / (4 + RAD_DEG*R);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      case PROJ_SIN:
+	ctht = RAD_DEG * R;
+	stht = sqrt (1 - ctht*ctht);
+	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;
+  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 (mosaic == NULL) 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) */
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_DIS:
+	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_SIN:
+	*L = +DEG_RAD * sphi;
+	*M = -DEG_RAD * cphi;
+	return (stht > 0);
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	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], "-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_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_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: PROJ_MODE_NONE;
+  }
+  return PROJ_MODE_NONE;
+}
+
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.update.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.update.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/coordops.update.c	(revision 13183)
@@ -0,0 +1,662 @@
+# include <dvo.h>
+
+static Coords *mosaic = NULL;
+
+void RegisterMosaic (Coords *coords) {
+  mosaic = coords;
+}
+
+int XY_to_RD (double *ra, double *dec, double x, double y, Coords *coords) {
+
+  int Zenith1, Zenith2, Zenithal, Polynomial, Cartesian, PseudoCyl;
+  char *type;
+  double L, M, X, Y, T, Z, Z2;
+  double R, sphi, cphi, stht, ctht;
+  double alpha, delta, salp, calp, sdel, sdp, cdp;
+  
+  *ra  = 0;
+  *dec = 0;
+  stht = ctht = 1;
+  
+  proj = GetProjection (coords[0].ctype);
+  mode = GetProjectionMode (proj);
+  if (proj == PROJ_NONE) return (FALSE);
+  if (proj == PROJ_MODE_NONE) return (FALSE);
+
+  /** 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];
+  }
+
+  /** 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 (mosaic == NULL) return (FALSE);
+      XY_to_RD (ra, dec, L + coords[0].crval1, M + coords[0].crval2, 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:
+	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:
+	stht = (4 - RAD_DEG*R) / (4 + RAD_DEC*R);
+	ctht = sqrt (1 - stht*stht);
+	break;
+      case PROJ_SIN:
+	ctht = RAD_DEG * R;
+	stht = sqrt (1 - ctht*ctht);
+	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;
+    }
+    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_PSEUDOCLY) {
+    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;
+    }
+    *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_XY (double *x, double *y, double ra, double dec, Coords *coords) {
+
+  char *type;
+  int i, status, Polynomial, Zenith1, Zenith2, Zenithal, Cartesian, PseudoCyl;
+  double phi, theta;
+  double determ;
+  double X, Y, L, M, Lo, Mo, dL, dM;
+  double sphi, cphi, stht;
+  double salp, calp, sdel, cdel, sdp, cdp;
+  double P, A, Rc;
+
+  status = TRUE;
+  *x = 0;
+  *y = 0;
+  type = &coords[0].ctype[4];
+  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 (mosaic == NULL) return (FALSE);
+      RD_to_XY (&Lo, &Mo, ra, dec, mosaic);
+      L = (Lo - coords[0].crval1);
+      M = (Mo - coords[0].crval2);
+    } else {
+      L = (ra  - coords[0].crval1);
+      M = (dec - coords[0].crval2);
+    }
+  }
+  
+  /**** 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) */
+    if (stht < 0) status = FALSE;
+
+    switch (proj) {
+      case PROJ_TAN:
+      case PROJ_DIS:
+	L =  DEG_RAD * sphi / stht;
+	M = -DEG_RAD * cphi / stht;
+	break;
+      case PROJ_SIN:
+	L =  DEG_RAD * sphi;
+	M = -DEG_RAD * cphi;
+	break;
+      case PROJ_ZEA:
+      case PROJ_ZPL:
+	Rc = DEG_RAD * M_SQRT2 / sqrt (1 + stht);
+	L =  Rc * sphi;
+	M = -Rc * cphi;
+	status = TRUE;
+	break;
+    }
+  }
+
+  /**** 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) {
+	  A =  DEG_RAD * sqrt (2.0 / P);
+	  L =  2.0 * A * cos (theta) * sin (0.5*phi);
+	  M =  A * sin (theta);
+	} else { 
+	  L =  0.0;
+	  M =  0.0;
+	}	
+	break;
+      case PROJ_GLS:
+	phi = ra - coords[0].crval1;
+	theta = dec - coords[0].crval2;
+	L = phi * cos(RAD_DEG * theta);
+	M = theta;
+	break;
+      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);
+	break;
+    }
+  }
+
+  /* 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 (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 = 3;
+      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 */
+
+int 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], "-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, int 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_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;
+  }
+  return FALSE;
+}  
+
+int GetProjectionMode (int proj) {
+  switch (proj) {
+    case PROJ_ZEA:
+    case PROJ_ZPL:
+    case PROJ_ARC:
+    case PROJ_STG:
+    case PROJ_SIN:
+    case PROJ_TAN:
+    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_PSEUDOCLY;
+  }
+  return PROJ_MODE_NONE;
+}
+
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog.c	(revision 13183)
@@ -0,0 +1,391 @@
+# 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")) return (DVO_FORMAT_PANSTARRS);
+  if (!strcasecmp (catformat, "PMTEST"))    return (DVO_FORMAT_PMTEST);
+  if (!strcasecmp (catformat, "PANSTARRS_DEV_0")) return (DVO_FORMAT_PANSTARRS_DEV_0);
+  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].Nave_disk  = 0;
+  catalog[0].Nmeas_disk = 0;
+  catalog[0].Nmiss_disk = 0;
+  catalog[0].Nmeas_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: 
+   0 - failure (including lock failure)
+   1 - success
+   2 - 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 (2);
+  if (dbstate == LCK_EMPTY)   return (2);
+  if (catalog[0].f == NULL)   return (0);
+
+  fseek (catalog[0].f, 0, SEEK_SET);
+  return (1);
+}
+
+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 0:
+    if (VERBOSE) fprintf (stderr, "can't lock file %s\n", catalog[0].filename);
+    return (FALSE);
+  case 1:
+    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 2:
+    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! */
+
+  // catformat determined in dvo_catalog_load_XXX function
+  catalog[0].catformat = DVO_FORMAT_UNDEF;
+
+  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?
+int dvo_catalog_save (Catalog *catalog, char VERBOSE) {
+
+  // set the 'sorted' header keyword
+  gfits_modify (&catalog[0].header, "SORTED",  "%t", 1, catalog[0].sorted);
+
+  if (catalog[0].Nmeas_off != 0) {
+    if (!dvo_catalog_update (catalog, VERBOSE)) {
+      return (FALSE);
+    } 
+    return (TRUE);
+  }
+
+  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);
+}
+
+int dvo_catalog_update (Catalog *catalog, char VERBOSE) {
+
+  /* update is only valid for catmode SPLIT */
+  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:
+      /* new file needs to use save_catalog_split */
+      if (catalog[0].Nave_disk == 0) {
+	dvo_catalog_save_split (catalog, VERBOSE);
+      } else {
+	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  = NO_MAG;
+	outsec[out].dM = NO_MAG;
+	outsec[out].Xm    = NO_MAG;
+      }
+    }
+    free (catalog[0].secfilt);
+    catalog[0].secfilt = outsec;
+    catalog[0].Nsecfilt = Nsecfilt;
+  }
+  return (TRUE);
+}
+
+void dvo_catalog_free (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].Nsecfilt = 0;
+    catalog[0].secfilt = NULL;
+  }
+  gfits_free_header (&catalog[0].header);
+  // free (catalog[0].filename);
+}
+
+/*
+  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
+*/
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_chipcoords.c	(revision 13183)
@@ -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].offset;
+    for (j = 0; j < average[i].Nm; 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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_create.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_create.c	(revision 13183)
@@ -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) != 2) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].missing_catalog, catalog[0].lockmode) != 2) {
+      fprintf (stderr, "error with file lock\n");
+      exit (2);
+    }
+    if (dvo_catalog_lock (catalog[0].secfilt_catalog, catalog[0].lockmode) != 2) {
+      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 */
+  str_to_time ("now", &now);
+  line = 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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_mef.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 13183)
@@ -0,0 +1,302 @@
+# 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;
+
+  /**  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;
+
+  /* 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, &catalog[0].Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naverage) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naverage);
+    }
+  } else {
+    Nbytes = gfits_matrix_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+  }
+  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].Nmeasure) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeasure);
+    }
+  } else {
+    Nbytes = gfits_matrix_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+  }
+
+  /* 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].Nmissing) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmissing);
+    }
+  } else {
+    Nbytes = gfits_matrix_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+  }
+
+  /* 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].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;
+      free (primary);
+    } 
+
+  } else {
+    /* no real need to skip the data array here... */
+    Nbytes = gfits_matrix_size (&header);
+    fseek (f, Nbytes, SEEK_CUR);
+    if (primary != NULL) free (primary);
+  }
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Nave_disk  = catalog[0].Naverage;
+  catalog[0].Nmeas_disk = catalog[0].Nmeasure;
+  catalog[0].Nmiss_disk = catalog[0].Nmissing;
+
+  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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_raw.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 13183)
@@ -0,0 +1,768 @@
+# 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);
+
+got_format:
+  /* determine datatype sizes */
+  switch (catalog[0].catformat) {
+    case DVO_FORMAT_INTERNAL:
+      AverageSize = sizeof(Average);
+      MeasureSize = sizeof(Measure);
+      SecFiltSize = sizeof(SecFilt);
+      break;
+    case DVO_FORMAT_LONEOS:
+      AverageSize = sizeof(AverageLoneos);
+      MeasureSize = sizeof(MeasureLoneos);
+      SecFiltSize = sizeof(SecFiltLoneos);
+      break;
+    case DVO_FORMAT_ELIXIR:
+      AverageSize = sizeof(AverageElixir);
+      MeasureSize = sizeof(MeasureElixir);
+      SecFiltSize = sizeof(SecFiltElixir);
+      break;
+    case DVO_FORMAT_PANSTARRS_DEV_0:
+      AverageSize = sizeof(AveragePanstarrs_DEV_0);
+      MeasureSize = sizeof(MeasurePanstarrs_DEV_0);
+      SecFiltSize = sizeof(SecFiltPanstarrs_DEV_0);
+      break;
+# if 0
+    case DVO_FORMAT_PANSTARRS:
+      AverageSize = sizeof(AveragePanstarrs);
+      MeasureSize = sizeof(MeasurePanstarrs);
+      SecFiltSize = sizeof(SecFiltPanstarrs);
+      break;
+    case DVO_FORMAT_PMTEST:
+      AverageSize = sizeof(AveragePMtest);
+      MeasureSize = sizeof(MeasurePanstarrs);
+      SecFiltSize = sizeof(SecFiltPanstarrs);
+      break;
+# endif
+    default:
+      fprintf (stderr, "programming error in phot_catalog_raw\n");
+      exit (2);
+  }
+  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;
+      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].Nm; 
+      Nmiss += catalog[0].average[i].Nn; 
+    }
+    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].Nave_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) gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PANSTARRS");
+  // if (catalog[0].catformat == DVO_FORMAT_PMTEST)    gfits_modify (&catalog[0].header, "FORMAT", "%s", 1, "PMTEST");
+
+  /* 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 **/
+
+Average *ReadRawAverage (FILE *f, int Naverage, int format, SecFilt **primary) {
+
+  int nitems;
+  Average *average;
+
+  /* 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;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      AverageElixir *tmpAverage;
+      ALLOCATE (tmpAverage, AverageElixir, MAX (Naverage, 1));
+      nitems = fread (tmpAverage, sizeof(AverageElixir), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_AverageElixir (tmpAverage, sizeof(AverageElixir), Naverage);
+      average = AverageElixirToInternal (tmpAverage, Naverage, primary);
+      free (tmpAverage);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      AverageLoneos *tmpAverage;
+      ALLOCATE (tmpAverage, AverageLoneos, MAX (Naverage, 1));
+      nitems = fread (tmpAverage, sizeof(AverageLoneos), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_AverageLoneos (tmpAverage, sizeof(AverageLoneos), Naverage);
+      average = AverageLoneosToInternal (tmpAverage, Naverage, primary);
+      free (tmpAverage);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      AveragePanstarrs_DEV_0 *tmpAverage;
+      ALLOCATE (tmpAverage, AveragePanstarrs_DEV_0, MAX (Naverage, 1));
+      nitems = fread (tmpAverage, sizeof(AveragePanstarrs_DEV_0), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_AveragePanstarrs_DEV_0 (tmpAverage, sizeof(AveragePanstarrs_DEV_0), Naverage);
+      average = AveragePanstarrs_DEV_0_ToInternal (tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      AveragePanstarrs *tmpAverage;
+      ALLOCATE (tmpAverage, AveragePanstarrs, MAX (Naverage, 1));
+      nitems = fread (tmpAverage, sizeof(AveragePanstarrs), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_AveragePanstarrs (tmpAverage, sizeof(AveragePanstarrs), Naverage);
+      average = AveragePanstarrsToInternal (tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+    case DVO_FORMAT_PMTEST: {
+      AveragePMtest *tmpAverage;
+      ALLOCATE (tmpAverage, AveragePMtest, MAX (Naverage, 1));
+      nitems = fread (tmpAverage, sizeof(AveragePMtest), Naverage, f);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to read averages (%d vs %d)\n", nitems, Naverage);
+	return (NULL);
+      }
+      gfits_convert_AveragePMtest (tmpAverage, sizeof(AveragePMtest), Naverage);
+      average = AveragePMtestToInternal (tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+  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) {
+
+  int nitems;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      AverageElixir *tmpAverage;
+      tmpAverage = AverageInternalToElixir (average, Naverage, primary);
+      gfits_convert_AverageElixir (tmpAverage, sizeof(AverageElixir), Naverage);
+      nitems = fwrite (tmpAverage, sizeof(AverageElixir), Naverage, f);
+      free (tmpAverage);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      AverageLoneos *tmpAverage;
+      tmpAverage = AverageInternalToLoneos (average, Naverage, primary);
+      gfits_convert_AverageLoneos (tmpAverage, sizeof(AverageLoneos), Naverage);
+      nitems = fwrite (tmpAverage, sizeof(AverageLoneos), Naverage, f);
+      free (tmpAverage);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      AveragePanstarrs_DEV_0 *tmpAverage;
+      tmpAverage = AverageInternalToPanstarrs_DEV_0 (average, Naverage);
+      gfits_convert_AveragePanstarrs_DEV_0 (tmpAverage, sizeof(AveragePanstarrs_DEV_0), Naverage);
+      nitems = fwrite (tmpAverage, sizeof(AveragePanstarrs_DEV_0), Naverage, f);
+      free (tmpAverage);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      AveragePanstarrs *tmpAverage;
+      tmpAverage = AverageInternalToPanstarrs (average, Naverage);
+      gfits_convert_AveragePanstarrs (tmpAverage, sizeof(AveragePanstarrs), Naverage);
+      nitems = fwrite (tmpAverage, sizeof(AveragePanstarrs), Naverage, f);
+      free (tmpAverage);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_PMTEST: {
+      AveragePMtest *tmpAverage;
+      tmpAverage = AverageInternalToPMtest (average, Naverage);
+      gfits_convert_AveragePMtest (tmpAverage, sizeof(AveragePMtest), Naverage);
+      nitems = fwrite (tmpAverage, sizeof(AveragePMtest), Naverage, f);
+      free (tmpAverage);
+      if (nitems != Naverage) {
+	fprintf (stderr, "failed to write averages (%d vs %d)\n", nitems, Naverage);
+	return (FALSE);
+      }
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error writing averages\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/** Average / Raw Table conversions **/
+
+Measure *ReadRawMeasure (FILE *f, int Nmeasure, int format) {
+
+  int nitems;
+  Measure *measure;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      MeasureElixir *tmpMeasure;
+      ALLOCATE (tmpMeasure, MeasureElixir, MAX (Nmeasure, 1));
+      nitems = fread (tmpMeasure, sizeof(MeasureElixir), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_MeasureElixir (tmpMeasure, sizeof(MeasureElixir), Nmeasure);
+      measure = MeasureElixirToInternal (tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      MeasureLoneos *tmpMeasure;
+      ALLOCATE (tmpMeasure, MeasureLoneos, MAX (Nmeasure, 1));
+      nitems = fread (tmpMeasure, sizeof(MeasureLoneos), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_MeasureLoneos (tmpMeasure, sizeof(MeasureLoneos), Nmeasure);
+      measure = MeasureLoneosToInternal (tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      MeasurePanstarrs_DEV_0 *tmpMeasure;
+      ALLOCATE (tmpMeasure, MeasurePanstarrs_DEV_0, MAX (Nmeasure, 1));
+      nitems = fread (tmpMeasure, sizeof(MeasurePanstarrs_DEV_0), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_MeasurePanstarrs_DEV_0 (tmpMeasure, sizeof(MeasurePanstarrs_DEV_0), Nmeasure);
+      measure = MeasurePanstarrs_DEV_0_ToInternal (tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      MeasurePanstarrs *tmpMeasure;
+      ALLOCATE (tmpMeasure, MeasurePanstarrs, MAX (Nmeasure, 1));
+      nitems = fread (tmpMeasure, sizeof(MeasurePanstarrs), Nmeasure, f);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to read measures (%d vs %d)\n", nitems, Nmeasure);
+	return (NULL);
+      }
+      gfits_convert_MeasurePanstarrs (tmpMeasure, sizeof(MeasurePanstarrs), Nmeasure);
+      measure = MeasurePanstarrsToInternal (tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+  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) {
+
+  int nitems;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      MeasureElixir *tmpMeasure;
+      tmpMeasure = MeasureInternalToElixir (measure, Nmeasure);
+      gfits_convert_MeasureElixir (tmpMeasure, sizeof(MeasureElixir), Nmeasure);
+      nitems = fwrite (tmpMeasure, sizeof(MeasureElixir), Nmeasure, f);
+      free (tmpMeasure);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      MeasureLoneos *tmpMeasure;
+      tmpMeasure = MeasureInternalToLoneos (measure, Nmeasure);
+      gfits_convert_MeasureLoneos (tmpMeasure, sizeof(MeasureLoneos), Nmeasure);
+      nitems = fwrite (tmpMeasure, sizeof(MeasureLoneos), Nmeasure, f);
+      free (tmpMeasure);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      MeasurePanstarrs_DEV_0 *tmpMeasure;
+      tmpMeasure = MeasureInternalToPanstarrs_DEV_0 (measure, Nmeasure);
+      gfits_convert_MeasurePanstarrs_DEV_0 (tmpMeasure, sizeof(MeasurePanstarrs_DEV_0), Nmeasure);
+      nitems = fwrite (tmpMeasure, sizeof(MeasurePanstarrs_DEV_0), Nmeasure, f);
+      free (tmpMeasure);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      MeasurePanstarrs *tmpMeasure;
+      tmpMeasure = MeasureInternalToPanstarrs (measure, Nmeasure);
+      gfits_convert_MeasurePanstarrs (tmpMeasure, sizeof(MeasurePanstarrs), Nmeasure);
+      nitems = fwrite (tmpMeasure, sizeof(MeasurePanstarrs), Nmeasure, f);
+      free (tmpMeasure);
+      if (nitems != Nmeasure) {
+	fprintf (stderr, "failed to write measures (%d vs %d)\n", nitems, Nmeasure);
+	return (FALSE);
+      }
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error writing measures\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
+/** SecFilt / Raw Table conversions **/
+
+SecFilt *ReadRawSecFilt (FILE *f, int Nsecfilt, int format) {
+
+  int nitems;
+  SecFilt *secfilt;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      SecFiltElixir *tmpSecFilt;
+      ALLOCATE (tmpSecFilt, SecFiltElixir, MAX (Nsecfilt, 1));
+      nitems = fread (tmpSecFilt, sizeof(SecFiltElixir), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFiltElixir (tmpSecFilt, sizeof(SecFiltElixir), Nsecfilt);
+      secfilt = SecFiltElixirToInternal (tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      SecFiltLoneos *tmpSecFilt;
+      ALLOCATE (tmpSecFilt, SecFiltLoneos, MAX (Nsecfilt, 1));
+      nitems = fread (tmpSecFilt, sizeof(SecFiltLoneos), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFiltLoneos (tmpSecFilt, sizeof(SecFiltLoneos), Nsecfilt);
+      secfilt = SecFiltLoneosToInternal (tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      SecFiltPanstarrs_DEV_0 *tmpSecFilt;
+      ALLOCATE (tmpSecFilt, SecFiltPanstarrs_DEV_0, MAX (Nsecfilt, 1));
+      nitems = fread (tmpSecFilt, sizeof(SecFiltPanstarrs_DEV_0), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFiltPanstarrs_DEV_0 (tmpSecFilt, sizeof(SecFiltPanstarrs_DEV_0), Nsecfilt);
+      secfilt = SecFiltPanstarrs_DEV_0_ToInternal (tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      SecFiltPanstarrs *tmpSecFilt;
+      ALLOCATE (tmpSecFilt, SecFiltPanstarrs, MAX (Nsecfilt, 1));
+      nitems = fread (tmpSecFilt, sizeof(SecFiltPanstarrs), Nsecfilt, f);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to read secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (NULL);
+      }
+      gfits_convert_SecFiltPanstarrs (tmpSecFilt, sizeof(SecFiltPanstarrs), Nsecfilt);
+      secfilt = SecFiltPanstarrsToInternal (tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error reading measures\n");
+      return (NULL);
+  }
+  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) {
+
+  int nitems;
+
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      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; }
+    case DVO_FORMAT_ELIXIR: {
+      SecFiltElixir *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToElixir (secfilt, Nsecfilt);
+      gfits_convert_SecFiltElixir (tmpSecFilt, sizeof(SecFiltElixir), Nsecfilt);
+      nitems = fwrite (tmpSecFilt, sizeof(SecFiltElixir), Nsecfilt, f);
+      free (tmpSecFilt);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      SecFiltLoneos *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToLoneos (secfilt, Nsecfilt);
+      gfits_convert_SecFiltLoneos (tmpSecFilt, sizeof(SecFiltLoneos), Nsecfilt);
+      nitems = fwrite (tmpSecFilt, sizeof(SecFiltLoneos), Nsecfilt, f);
+      free (tmpSecFilt);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      SecFiltPanstarrs_DEV_0 *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToPanstarrs_DEV_0 (secfilt, Nsecfilt);
+      gfits_convert_SecFiltPanstarrs_DEV_0 (tmpSecFilt, sizeof(SecFiltPanstarrs_DEV_0), Nsecfilt);
+      nitems = fwrite (tmpSecFilt, sizeof(SecFiltPanstarrs_DEV_0), Nsecfilt, f);
+      free (tmpSecFilt);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      SecFiltPanstarrs *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToPanstarrs (secfilt, Nsecfilt);
+      gfits_convert_SecFiltPanstarrs (tmpSecFilt, sizeof(SecFiltPanstarrs), Nsecfilt);
+      nitems = fwrite (tmpSecFilt, sizeof(SecFiltPanstarrs), Nsecfilt, f);
+      free (tmpSecFilt);
+      if (nitems != Nsecfilt) {
+	fprintf (stderr, "failed to write secfilts (%d vs %d)\n", nitems, Nsecfilt);
+	return (FALSE);
+      }
+      break; }
+# endif
+    default:
+      fprintf (stderr, "error writing secfilts\n");
+      return (FALSE);
+  }
+  return (TRUE);
+}
+
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_split.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 13183)
@@ -0,0 +1,759 @@
+# include <dvo.h>
+
+int dvo_catalog_load_split (Catalog *catalog, int VERBOSE) {
+
+  int Nitems, Nexpect, Naverage, Nmeasure, Nmissing, Nsecfilt, status;
+  char *path, string[80];
+  Header header;
+  Matrix matrix;
+  FTable ftable;
+  SecFilt *primary;
+  Catalog *measure, *missing, *secfilt;
+
+  /* ftable header storage for below */
+  ftable.header = &header;
+
+  /* needed to find the split files below */
+  path = pathname (catalog[0].filename);
+
+  /* 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;
+
+  /**  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 */
+  catalog[0].average = NULL;
+  catalog[0].measure = NULL;
+  catalog[0].missing = NULL;
+  catalog[0].secfilt = NULL;
+
+  /* XXX EAM : validate table mode */
+
+  /*** Average Table ***/
+
+  if (catalog[0].catflags & LOAD_AVES) {
+    /* move pointer past header -- must be already read (load_catalog) */
+    fseek (catalog[0].f, catalog[0].header.size, SEEK_SET);
+    /* matrix should be empty */
+    if (!gfits_fread_matrix (catalog[0].f, &matrix, &catalog[0].header)) {
+      if (VERBOSE) fprintf (stderr, "can't read primary matrix");
+      return (FALSE);
+    }
+    /* 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 */
+    if (!gfits_fread_ftable_data (catalog[0].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, &catalog[0].Naverage, &catalog[0].catformat, &primary);
+    if (Naverage != catalog[0].Naverage) {
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%d vs %d)\n", Naverage, catalog[0].Naverage);
+    }
+    gfits_free_header (&header);
+  } 
+
+  /*** Measure Table ***/
+
+  measure = NULL;
+
+  /* (Full Load) */
+  if (catalog[0].catflags & LOAD_MEAS) {
+    ALLOCATE (measure, Catalog, 1);
+    dvo_catalog_init (measure, TRUE);
+
+    /* get split filename from main header (paths relative to cpt file) */
+    if (!gfits_scan (&catalog[0].header, "MEASURE",  "%s", 1, string)) return (FALSE);
+    ALLOCATE (measure[0].filename, char, strlen(path) + strlen(string) + 2);
+    sprintf (measure[0].filename, "%s/%s", path, string);
+
+    /* lock & open catalog file */
+    if (dvo_catalog_lock (measure, catalog[0].lockmode) != 1) {
+      fprintf (stderr, "cannot access measure file %s\n", measure[0].filename);
+      exit (2);
+    }
+
+    /* read PHU */
+    if (!gfits_load_header (measure[0].f, &measure[0].header)) {
+      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", measure[0].filename);
+      return (FALSE);
+    }
+    /* matrix should be empty */
+    if (!gfits_fread_matrix (measure[0].f, &matrix, &measure[0].header)) {
+      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
+      return (FALSE);
+    }
+    /* read Measure table header */
+    if (!gfits_fread_header (measure[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read measure PHU header\n");
+      return (FALSE);
+    }
+    /* read Measure table data */
+    if (!gfits_fread_ftable_data (measure[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table measure data\n");
+      return (FALSE);
+    }
+    /* convert data format to internal */
+    catalog[0].measure = FtableToMeasure (&ftable, &catalog[0].Nmeasure, &catalog[0].catformat);
+    if (Nmeasure != catalog[0].Nmeasure) {
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%d vs %d)\n", Nmeasure, catalog[0].Nmeasure);
+    }
+    catalog[0].Nmeas_off = 0;
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  }
+
+  /* (Meta Load) */
+  if (catalog[0].catflags & LOAD_MEAS_META) {
+    ALLOCATE (measure, Catalog, 1);
+    dvo_catalog_init (measure, TRUE);
+
+    /* get split filename from main header (paths relative to cpt file) */
+    if (!gfits_scan (&catalog[0].header, "MEASURE",  "%s", 1, string)) return (FALSE);
+    ALLOCATE (measure[0].filename, char, strlen(path) + strlen(string) + 2);
+    sprintf (measure[0].filename, "%s/%s", path, string);
+
+    /* lock & open catalog file */
+    if (dvo_catalog_lock (measure, catalog[0].lockmode) != 1) {
+      fprintf (stderr, "cannot access measure file %s\n", measure[0].filename);
+      exit (2);
+    }
+
+    /* read PHU */
+    if (!gfits_load_header (measure[0].f, &measure[0].header)) {
+      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", measure[0].filename);
+      return (FALSE);
+    }
+
+    /* allocate dummy data for valid realloc */
+    ALLOCATE (catalog[0].measure, Measure, 1);
+
+    /* set up the table size information */
+    catalog[0].Nmeasure  = 0;        /* no rows loaded */
+    catalog[0].Nmeas_off = Nmeasure; /* start of new entries */
+  }
+  catalog[0].measure_catalog = measure;
+
+  /*** Missing Table ***/
+
+  missing = NULL;
+  if (catalog[0].catflags & LOAD_MISS) {
+    ALLOCATE (missing, Catalog, 1);
+    dvo_catalog_init (missing, TRUE);
+
+    /* get split filename from main header (paths relative to cpt file) */
+    if (!gfits_scan (&catalog[0].header, "MISSING",  "%s", 1, string)) return (FALSE);
+    ALLOCATE (missing[0].filename, char, strlen(path) + strlen(string) + 2);
+    sprintf (missing[0].filename, "%s/%s", path, string);
+
+    /* lock & open catalog file */
+    status = dvo_catalog_lock (missing, catalog[0].lockmode);
+    if (!status) {
+      fprintf (stderr, "ERROR: cannot access missing file %s\n", missing[0].filename);
+      exit (2);
+    }
+    if (status == 2) {
+	/* MISSING table is empty (this is not an error) */
+	gfits_create_header (&missing[0].header);
+	goto missing_empty;
+    }
+
+    /* read PHU */
+    if (!gfits_load_header (missing[0].f, &missing[0].header)) {
+      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", missing[0].filename);
+      return (FALSE);
+    }
+    /* matrix should be empty */
+    if (!gfits_fread_matrix (missing[0].f, &matrix, &missing[0].header)) {
+      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
+      return (FALSE);
+    }
+    /* read Missing table header */
+    if (!gfits_fread_header (missing[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing header\n");
+      return (FALSE);
+    }
+    /* read Missing table data */
+    if (!gfits_fread_ftable_data (missing[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table missing data\n");
+      return (FALSE);
+    }
+    /* no conversions currently defined */
+    catalog[0].missing = gfits_table_get_Missing (&ftable, &catalog[0].Nmissing, NULL);
+    if (Nmissing != catalog[0].Nmissing) {
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%d vs %d)\n", Nmissing, catalog[0].Nmissing);
+    }
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  } 
+missing_empty:
+  catalog[0].missing_catalog = missing;
+
+  /*** Secfilt Table ***/
+
+  secfilt = NULL;
+  if (catalog[0].catflags & LOAD_SECF) {
+    ALLOCATE (secfilt, Catalog, 1);
+    dvo_catalog_init (secfilt, TRUE);
+
+    /* get split filename from main header (paths relative to cpt file) */
+    if (!gfits_scan (&catalog[0].header, "SECFILT",  "%s", 1, string)) return (FALSE);
+    ALLOCATE (secfilt[0].filename, char, strlen(path) + strlen(string) + 2);
+    sprintf (secfilt[0].filename, "%s/%s", path, string);
+
+    /* lock & open catalog file */
+    if (dvo_catalog_lock (secfilt, catalog[0].lockmode) != 1) {
+      fprintf (stderr, "cannot access secfilt file %s\n", secfilt[0].filename);
+      exit (2);
+    }
+
+    /* read PHU */
+    if (!gfits_load_header (secfilt[0].f, &secfilt[0].header)) {
+      if (VERBOSE) fprintf (stderr, "catalog file does not exist: %s\n", secfilt[0].filename);
+      return (FALSE);
+    }
+    /* matrix should be empty */
+    if (!gfits_fread_matrix (secfilt[0].f, &matrix, &secfilt[0].header)) {
+      if (VERBOSE) fprintf (stderr, "can't read primary matrix\n");
+      return (FALSE);
+    }
+    /* read secfilt table header */
+    if (!gfits_fread_header (secfilt[0].f, &header)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt header\n");
+      return (FALSE);
+    }
+    /* read secfilt table data */
+    if (!gfits_fread_ftable_data (secfilt[0].f, &ftable)) {
+      if (VERBOSE) fprintf (stderr, "can't read table secfilt data\n");
+      return (FALSE);
+    }
+    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].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;
+      free (primary);
+    } 
+
+
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  } else {
+    if (primary != NULL) free (primary);
+  }
+
+  catalog[0].secfilt_catalog = secfilt;
+
+  /* save the current number so we can do partial updates */
+  catalog[0].Nave_disk  = Naverage;
+  catalog[0].Nmeas_disk = Nmeasure;
+  catalog[0].Nmiss_disk = Nmissing;
+
+  return (TRUE);
+}
+
+/* save_catalog_split writes complete new files from scratch */
+int dvo_catalog_save_split (Catalog *catalog, char VERBOSE) {
+
+  int Nitems;
+  Matrix matrix;
+  Header header;
+  FTable ftable;
+  Catalog *catfile;
+  SecFilt *primary, *secfilt;
+  int i, j, Nsecfilt, Nallfilt, Ntotal;
+
+  ftable.header = &header;
+
+  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
+    if (catalog[0].secfilt == NULL) {       
+      fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c:544)\n");
+      exit (1);
+    }
+    secfilt = catalog[0].secfilt;
+
+    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];
+      }
+    }		
+  } 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);
+
+  /* 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;
+  }
+
+  /* in split mode, we can save only part of the data */ 
+
+  /*** Average Table ***/
+
+  if (catalog[0].average != NULL) {
+    ftruncate (fileno (catalog[0].f), catalog[0].header.size);
+
+    /* 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);
+  }
+
+  /*** Measure Table ***/
+  if (catalog[0].measure != NULL) {
+
+    /* catalog file data is stored in separate structure */
+    catfile = catalog[0].measure_catalog;
+
+    /* XXX EAM : warn about this condition; add code to handle? */
+    if (catalog[0].Nmeas_off != 0) {
+      fprintf (stderr, "WARNING: LOAD_MEAS_META mixed with save??\n");
+      fprintf (stderr, "WARNING: this should not be allowed to happen!\n");
+    }
+
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catfile[0].f, 0, SEEK_SET);
+    ftruncate (fileno (catfile[0].f), 0);
+
+    /* write table PHU header */
+    if (!gfits_fwrite_header  (catfile[0].f, &catfile[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 (&catfile[0].header, &matrix);
+    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
+      fprintf (stderr, "can't write primary matrix");
+      goto failure;
+    }
+    gfits_free_matrix (&matrix);
+
+    /* write out Measure table (convert to FITS table format) */
+    MeasureToFtable (&ftable, catalog[0].measure, catalog[0].Nmeasure, catalog[0].catformat);
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_table (&ftable);
+    gfits_free_header (&header);
+  }
+
+  /*** Missing Table ***/
+  if (catalog[0].missing != NULL) {
+
+    /* catalog data is stored in separate catalog */
+    catfile = catalog[0].missing_catalog;
+
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catfile[0].f, 0, SEEK_SET);
+    ftruncate (fileno (catfile[0].f), 0);
+
+    /* write table PHU header */
+    if (!gfits_fwrite_header  (catfile[0].f, &catfile[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 (&catfile[0].header, &matrix);
+    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
+      fprintf (stderr, "can't write primary matrix");
+      goto failure;
+    }
+    gfits_free_matrix (&matrix);
+
+    /* write out Missing table (convert to FITS table format) */
+    gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_table (&ftable);
+    gfits_free_header (&header);
+  }
+
+  /*** Secfilt Table ***/
+  if (catalog[0].secfilt != NULL) {
+
+    /* catalog file data is stored in a separate catalog structure */
+    catfile = catalog[0].secfilt_catalog;
+
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catfile[0].f, 0, SEEK_SET);
+    ftruncate (fileno (catfile[0].f), 0);
+
+    /* write table PHU header */
+    if (!gfits_fwrite_header  (catfile[0].f, &catfile[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 (&catfile[0].header, &matrix);
+    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
+      fprintf (stderr, "can't write primary matrix");
+      goto failure;
+    }
+    gfits_free_matrix (&matrix);
+
+    /* write out SecFilt table (convert to FITS table format) */
+    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+    SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_table (catfile[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);
+}
+
+/* update_catalog_split only writes new lines to file.  
+ * if file is empty, call save_catalog_split instead.
+ * XXX EAM : save_catalog SHOULD do this
+ */
+
+int dvo_catalog_update_split (Catalog *catalog, char VERBOSE) {
+
+  int i, Nx, Ny, Nlines;
+  int Nitems, Nskip, Nout, Ndisk, Nstart;
+  Matrix matrix;
+  Header header;
+  FTable ftable;
+  VTable vtable;
+  Catalog *catfile;
+  SecFilt *primary, *secfilt;
+  int j, Nsecfilt, Nallfilt, Ntotal;
+
+  ftable.header = &header;
+  vtable.header = &header;
+
+  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
+    if (catalog[0].secfilt == NULL) { 
+      fprintf (stderr, "missing secfilt, cannot build output averages (dvo_catalog_split.c:544)\n");
+      exit (1);
+    }
+    secfilt = catalog[0].secfilt;
+
+    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];
+      }
+    }		
+  } 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 + catalog[0].Nmeas_off);
+  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);
+
+  /* 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;
+  }
+
+  /*** Average Table ***/
+  if (catalog[0].average != NULL) {
+
+    /* skip past matrix (already at end of header) */
+    Nskip = gfits_matrix_size (&catalog[0].header);
+    fseek (catalog[0].f, Nskip, SEEK_CUR);
+
+    /* how many lines to write out? */
+    Nout = catalog[0].Naverage - catalog[0].Nave_disk;
+
+    /* write out Average table (convert to FITS table format) */
+    AverageToFtable (&ftable, catalog[0].average, catalog[0].Naverage, catalog[0].catformat, primary);
+    /* convert only output rows to vtable */
+    gfits_table_to_vtable (&ftable, &vtable, catalog[0].Nave_disk, Nout);
+
+    if (!gfits_fwrite_Theader (catalog[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_vtable (catalog[0].f, &vtable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_vtable (&vtable);
+    gfits_free_table (&ftable);
+    gfits_free_header (&header);
+  }
+
+  /*** Measure Table ***/
+  if (catalog[0].measure != NULL) {
+
+    catfile = catalog[0].measure_catalog;
+
+    /* skip past PHU header and matrix */
+    Nskip = catfile[0].header.size + gfits_matrix_size (&catfile[0].header);
+    fseek (catfile[0].f, Nskip, SEEK_SET);
+
+    Ndisk  = catalog[0].Nmeas_disk;
+    Nstart = catalog[0].Nmeas_disk - catalog[0].Nmeas_off; /* where is first new line? */
+    Nout   = catalog[0].Nmeasure - Nstart;                   /* how many lines to write out? */
+    Nlines = catalog[0].Nmeasure + catalog[0].Nmeas_off;   /* how many lines total in file */
+
+    /* convert to output format FITS table (only rows for output : 0 - Nout) */
+    MeasureToFtable (&ftable, &catalog[0].measure[Nstart], Nout, catalog[0].catformat);
+
+    gfits_scan (&header, "NAXIS1", "%d", 1, &Nx);
+    gfits_scan (&header, "NAXIS2", "%d", 1, &Ny);
+
+    /* convert all output rows to vtable */
+    ALLOCATE (vtable.row, int, MAX (1, Nout));
+    ALLOCATE (vtable.buffer, char *, MAX (1, Nout));
+    for (i = 0; i < Nout; i++) {
+      ALLOCATE (vtable.buffer[i], char, MAX (1, Nx));
+      memcpy (vtable.buffer[i], &ftable.buffer[i*Nx], Nx);
+      vtable.row[i] = i + Ndisk;
+    }
+
+    /* modify vtable to represent full disk table */
+    gfits_modify (&header, "NAXIS2", "%d", 1, Nlines);
+    header.Naxis[1] = Nlines;
+
+    vtable.size = gfits_matrix_size (&header);
+    vtable.Nrow = Nout;
+    vtable.pad = vtable.size - Nx*Ny;
+
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_vtable (catfile[0].f, &vtable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_vtable (&vtable);
+    gfits_free_table (&ftable);
+    gfits_free_header (&header);
+  }
+
+  /*** Missing Table ***/
+  /* missing table CANNOT be written unsorted, thus it is always written 
+     out in full */
+
+  if (catalog[0].missing != NULL) {
+
+    catfile = catalog[0].missing_catalog;
+
+    /* rewind file pointers and truncate (file is still open) */
+    fseek (catfile[0].f, 0, SEEK_SET);
+    ftruncate (fileno (catfile[0].f), 0);
+
+    /* write table PHU header */
+    if (!gfits_fwrite_header  (catfile[0].f, &catfile[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 (&catfile[0].header, &matrix);
+    if (!gfits_fwrite_matrix  (catfile[0].f, &matrix)) {
+      fprintf (stderr, "can't write primary matrix");
+      goto failure;
+    }
+    gfits_free_matrix (&matrix);
+
+    /* write out Missing table (convert to FITS table format) */
+    gfits_table_set_Missing (&ftable, catalog[0].missing, catalog[0].Nmissing);
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_table (catfile[0].f, &ftable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_table (&ftable);
+    gfits_free_header (&header);
+  }
+
+  /*** Secfilt Table ***/
+  if (catalog[0].secfilt != NULL) {
+
+    catfile = catalog[0].secfilt_catalog;
+
+    /* skip past PHU header and matrix */
+    Nskip = catfile[0].header.size + gfits_matrix_size (&catfile[0].header);
+    fseek (catfile[0].f, Nskip, SEEK_SET);
+
+    /* how many lines to write out? */
+    Nout  = catalog[0].Nsecfilt * (catalog[0].Naverage - catalog[0].Nave_disk);
+    Ndisk = catalog[0].Nsecfilt * catalog[0].Nave_disk;
+
+    /* convert to output format FITS table */
+    Nitems = catalog[0].Naverage * catalog[0].Nsecfilt;
+    SecFiltToFtable (&ftable, secfilt, Nitems, catalog[0].catformat);
+    /* convert only output rows to vtable */
+    gfits_table_to_vtable (&ftable, &vtable, Ndisk, Nout);
+
+    if (!gfits_fwrite_Theader (catfile[0].f, &header)) {
+      fprintf (stderr, "can't write table header");
+      goto failure;
+    }
+    if (!gfits_fwrite_vtable (catfile[0].f, &vtable)) {
+      fprintf (stderr, "can't write table data");
+      goto failure;
+    }
+    gfits_free_vtable (&vtable);
+    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);
+}
+
+/* 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 vtable.  we need to define
+   the vtable based on the ftable, but with Ny = Nmeasure */  
+  
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert.c	(revision 13183)
@@ -0,0 +1,546 @@
+# 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);
+  }
+  if (!strcmp (extname, "DVO_AVERAGE")) {
+    average = gfits_table_get_Average (ftable, Naverage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (average);
+  }
+  if (!strcmp (extname, "DVO_AVERAGE_ELIXIR")) {
+    AverageElixir *tmpAverage;
+    tmpAverage = gfits_table_get_AverageElixir (ftable, Naverage, NULL);
+    average = AverageElixirToInternal (tmpAverage, *Naverage, primary);
+    free (tmpAverage);
+    *format = DVO_FORMAT_ELIXIR;
+    return (average);
+  }
+  if (!strcmp (extname, "DVO_AVERAGE_LONEOS")) {
+    AverageLoneos *tmpAverage;
+    tmpAverage = gfits_table_get_AverageLoneos (ftable, Naverage, NULL);
+    average = AverageLoneosToInternal (tmpAverage, *Naverage, primary);
+    free (tmpAverage);
+    *format = DVO_FORMAT_LONEOS;
+    return (average);
+  }
+  if (!strcmp (extname, "DVO_AVERAGE_PANSTARRS_DEV_0")) {
+    AveragePanstarrs_DEV_0 *tmpAverage;
+    tmpAverage = gfits_table_get_AveragePanstarrs_DEV_0 (ftable, Naverage, NULL);
+    average = AveragePanstarrs_DEV_0_ToInternal (tmpAverage, *Naverage);
+    free (tmpAverage);
+    *format = DVO_FORMAT_PANSTARRS_DEV_0;
+    return (average);
+  }
+/* disable the new and minimally-used versions for now.  
+   put them back in after dev work is further along
+   OR replace with other output formats */
+# if 0
+  if (!strcmp (extname, "DVO_AVERAGE_PANSTARRS")) {
+    AveragePanstarrs *tmpAverage;
+    tmpAverage = gfits_table_get_AveragePanstarrs (ftable, Naverage, NULL);
+    average = AveragePanstarrsToInternal (tmpAverage, *Naverage);
+    free (tmpAverage);
+    *format = DVO_FORMAT_PANSTARRS;
+    return (average);
+  }
+  if (!strcmp (extname, "DVO_AVERAGE_PMTEST")) {
+    AveragePMtest *tmpAverage;
+    tmpAverage = gfits_table_get_AveragePMtest (ftable, Naverage, NULL);
+    average = AveragePMtestToInternal (tmpAverage, *Naverage);
+    free (tmpAverage);
+    *format = DVO_FORMAT_PMTEST;
+    return (average);
+  }
+# endif
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Naverage = 0;
+  return (NULL);
+}
+
+int AverageToFtable (FTable *ftable, Average *average, int Naverage, int format, SecFilt *primary) {
+
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Average (ftable, average, Naverage);
+      break; }
+    case DVO_FORMAT_ELIXIR: {
+      AverageElixir *tmpAverage;
+      tmpAverage = AverageInternalToElixir (average, Naverage, primary);
+      gfits_table_set_AverageElixir (ftable, tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      AverageLoneos *tmpAverage ;
+      tmpAverage  = AverageInternalToLoneos (average, Naverage, primary);
+      gfits_table_set_AverageLoneos (ftable, tmpAverage, Naverage);
+      free (tmpAverage );
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      AveragePanstarrs_DEV_0 *tmpAverage;
+      tmpAverage = AverageInternalToPanstarrs_DEV_0 (average, Naverage);
+      gfits_table_set_AveragePanstarrs_DEV_0 (ftable, tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      AveragePanstarrs *tmpAverage;
+      tmpAverage = AverageInternalToPanstarrs (average, Naverage);
+      gfits_table_set_AveragePanstarrs (ftable, tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+    case DVO_FORMAT_PMTEST: {
+      AveragePMtest *tmpAverage;
+      tmpAverage = AverageInternalToPMtest (average, Naverage);
+      gfits_table_set_AveragePMtest (ftable, tmpAverage, Naverage);
+      free (tmpAverage);
+      break; }
+# endif
+    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);
+  }
+  if (!strcmp (extname, "DVO_MEASURE")) {
+    measure = gfits_table_get_Measure (ftable, Nmeasure, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (measure);
+  }
+  if (!strcmp (extname, "DVO_MEASURE_ELIXIR")) {
+    MeasureElixir *tmpMeasure;
+    tmpMeasure = gfits_table_get_MeasureElixir (ftable, Nmeasure, NULL);
+    measure = MeasureElixirToInternal (tmpMeasure, *Nmeasure);
+    free (tmpMeasure);
+    *format = DVO_FORMAT_ELIXIR;
+    return (measure);
+  }
+  if (!strcmp (extname, "DVO_MEASURE_LONEOS")) {
+    MeasureLoneos *tmpMeasure;
+    tmpMeasure = gfits_table_get_MeasureLoneos (ftable, Nmeasure, NULL);
+    measure = MeasureLoneosToInternal (tmpMeasure, *Nmeasure);
+    free (tmpMeasure);
+    *format = DVO_FORMAT_LONEOS;
+    return (measure);
+  }
+  if (!strcmp (extname, "DVO_MEASURE_PANSTARRS_DEV_0")) {
+    MeasurePanstarrs_DEV_0 *tmpMeasure;
+    tmpMeasure = gfits_table_get_MeasurePanstarrs_DEV_0 (ftable, Nmeasure, NULL);
+    measure = MeasurePanstarrs_DEV_0_ToInternal (tmpMeasure, *Nmeasure);
+    free (tmpMeasure);
+    *format = DVO_FORMAT_PANSTARRS_DEV_0;
+    return (measure);
+  }
+# if 0
+  if (!strcmp (extname, "DVO_MEASURE_PANSTARRS") || !strcmp (extname, "DVO_MEASURE_PMTEST")) {
+    MeasurePanstarrs *tmpMeasure;
+    tmpMeasure = gfits_table_get_MeasurePanstarrs (ftable, Nmeasure, NULL);
+    measure = MeasurePanstarrsToInternal (tmpMeasure, *Nmeasure);
+    free (tmpMeasure);
+    *format = DVO_FORMAT_PANSTARRS;
+    return (measure);
+  }
+# endif
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nmeasure = 0;
+  return (NULL);
+}
+
+int MeasureToFtable (FTable *ftable, Measure *measure, int Nmeasure, int format) {
+
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_Measure (ftable, measure, Nmeasure);
+      break; }
+    case DVO_FORMAT_ELIXIR: {
+      MeasureElixir *tmpMeasure;
+      tmpMeasure = MeasureInternalToElixir (measure, Nmeasure);
+      gfits_table_set_MeasureElixir (ftable, tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      MeasureLoneos *tmpMeasure;
+      tmpMeasure = MeasureInternalToLoneos (measure, Nmeasure);
+      gfits_table_set_MeasureLoneos (ftable, tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      MeasurePanstarrs_DEV_0 *tmpMeasure;
+      tmpMeasure = MeasureInternalToPanstarrs_DEV_0 (measure, Nmeasure);
+      gfits_table_set_MeasurePanstarrs_DEV_0 (ftable, tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      MeasurePanstarrs *tmpMeasure;
+      tmpMeasure = MeasureInternalToPanstarrs (measure, Nmeasure);
+      gfits_table_set_MeasurePanstarrs (ftable, tmpMeasure, Nmeasure);
+      free (tmpMeasure);
+      break; }
+# endif
+    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);
+  }
+  if (!strcmp (extname, "DVO_SECFILT")) {
+    secfilt = gfits_table_get_SecFilt (ftable, Nsecfilt, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (secfilt);
+  }
+  if (!strcmp (extname, "DVO_SECFILT_ELIXIR")) {
+    SecFiltElixir *tmpSecFilt;
+    tmpSecFilt = gfits_table_get_SecFiltElixir (ftable, Nsecfilt, NULL);
+    secfilt = SecFiltElixirToInternal (tmpSecFilt, *Nsecfilt);
+    free (tmpSecFilt);
+    *format = DVO_FORMAT_ELIXIR;
+    return (secfilt);
+  }
+  if (!strcmp (extname, "DVO_SECFILT_LONEOS")) {
+    SecFiltLoneos *tmpSecFilt;
+    tmpSecFilt = gfits_table_get_SecFiltLoneos (ftable, Nsecfilt, NULL);
+    secfilt = SecFiltLoneosToInternal (tmpSecFilt, *Nsecfilt);
+    free (tmpSecFilt);
+    *format = DVO_FORMAT_LONEOS;
+    return (secfilt);
+  }
+  if (!strcmp (extname, "DVO_SECFILT_PANSTARRS_DEV_0")) {
+    SecFiltPanstarrs_DEV_0 *tmpSecFilt;
+    tmpSecFilt = gfits_table_get_SecFiltPanstarrs_DEV_0 (ftable, Nsecfilt, NULL);
+    secfilt = SecFiltPanstarrs_DEV_0_ToInternal (tmpSecFilt, *Nsecfilt);
+    free (tmpSecFilt);
+    *format = DVO_FORMAT_PANSTARRS_DEV_0;
+    return (secfilt);
+  }
+# if 0
+  if (!strcmp (extname, "DVO_SECFILT_PANSTARRS") || !strcmp (extname, "DVO_SECFILT_PMTEST")) {
+    SecFiltPanstarrs *tmpSecFilt;
+    tmpSecFilt = gfits_table_get_SecFiltPanstarrs (ftable, Nsecfilt, NULL);
+    secfilt = SecFiltPanstarrsToInternal (tmpSecFilt, *Nsecfilt);
+    free (tmpSecFilt);
+    *format = DVO_FORMAT_PANSTARRS;
+    return (secfilt);
+  }
+# endif
+
+  fprintf (stderr, "table format unknown: %s\n", extname);
+
+  *Nsecfilt = 0;
+  return (NULL);
+}
+
+int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, int Nsecfilt, int format) {
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_table_set_SecFilt (ftable, secfilt, Nsecfilt);
+      break; }
+    case DVO_FORMAT_ELIXIR: {
+      SecFiltElixir *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToElixir (secfilt, Nsecfilt);
+      gfits_table_set_SecFiltElixir (ftable, tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      SecFiltLoneos *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToLoneos (secfilt, Nsecfilt);
+      gfits_table_set_SecFiltLoneos (ftable, tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      SecFiltPanstarrs_DEV_0 *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToPanstarrs_DEV_0 (secfilt, Nsecfilt);
+      gfits_table_set_SecFiltPanstarrs_DEV_0 (ftable, tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      SecFiltPanstarrs *tmpSecFilt;
+      tmpSecFilt = SecFiltInternalToPanstarrs (secfilt, Nsecfilt);
+      gfits_table_set_SecFiltPanstarrs (ftable, tmpSecFilt, Nsecfilt);
+      free (tmpSecFilt);
+      break; }
+# endif
+    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
+    ImageElixir *tmpimage;
+    tmpimage = gfits_table_get_ImageElixir (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) ImageElixirToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    goto set_header;
+  }
+
+  /* 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);
+  }
+  if (!strcmp (extname, "DVO_IMAGE")) {
+    Image *image;
+    image = gfits_table_get_Image (ftable, &Nimage, NULL);
+    *format = DVO_FORMAT_INTERNAL;
+    return (TRUE);
+  }
+  if (!strcmp (extname, "DVO_IMAGE_ELIXIR")) {
+    ImageElixir *tmpimage;
+    *format = DVO_FORMAT_ELIXIR;
+    tmpimage = gfits_table_get_ImageElixir (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) ImageElixirToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    goto set_header;
+  }
+  if (!strcmp (extname, "DVO_IMAGE_LONEOS")) {
+    ImageLoneos *tmpimage;
+    *format = DVO_FORMAT_LONEOS;
+    tmpimage = gfits_table_get_ImageLoneos (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) ImageLoneosToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    goto set_header;
+  }
+  if (!strcmp (extname, "DVO_IMAGE_PANSTARRS_DEV_0")) {
+    ImagePanstarrs_DEV_0 *tmpimage;
+    *format = DVO_FORMAT_PANSTARRS_DEV_0;
+    tmpimage = gfits_table_get_ImagePanstarrs_DEV_0 (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) ImagePanstarrs_DEV_0_ToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    goto set_header;
+  }
+# if 0
+  if (!strcmp (extname, "DVO_IMAGE_PANSTARRS") || !strcmp (extname, "DVO_IMAGE_PMTEST")) {
+    ImagePanstarrs *tmpimage;
+    *format = DVO_FORMAT_PANSTARRS;
+    tmpimage = gfits_table_get_ImagePanstarrs (ftable, &Nimage, NULL);
+    ftable[0].buffer = (char *) ImagePanstarrsToInternal (tmpimage, Nimage);
+    free (tmpimage);
+    goto set_header;
+  }
+# endif
+  fprintf (stderr, "table format unknown: %s\n", extname);
+  return (FALSE);
+
+set_header:
+  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_matrix_size (theader);
+  return (TRUE);
+}
+
+int ImageToFtable (FTable *ftable, Header *theader, int format) {
+
+  int Nimage;
+
+  Nimage = theader[0].Naxis[1];
+
+  /* convert from the internal format */
+  switch (format) {
+    case DVO_FORMAT_INTERNAL: {
+      gfits_convert_Image ((Image *) ftable[0].buffer, sizeof(Image), Nimage);
+      break; }
+    case DVO_FORMAT_ELIXIR: {
+      ImageElixir *tmpImage;
+      tmpImage = ImageInternalToElixir ((Image *) ftable[0].buffer, Nimage);
+      free (ftable[0].buffer);
+      gfits_table_set_ImageElixir (ftable, tmpImage, Nimage);
+      free (tmpImage);
+      break; }
+    case DVO_FORMAT_LONEOS: {
+      ImageLoneos *tmpImage;
+      tmpImage = ImageInternalToLoneos ((Image *) ftable[0].buffer, Nimage);
+      free (ftable[0].buffer);
+      gfits_table_set_ImageLoneos (ftable, tmpImage, Nimage);
+      free (tmpImage);
+      break; }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      ImagePanstarrs_DEV_0 *tmpImage;
+      tmpImage = ImageInternalToPanstarrs_DEV_0 ((Image *) ftable[0].buffer, Nimage);
+      free (ftable[0].buffer);
+      gfits_table_set_ImagePanstarrs_DEV_0 (ftable, tmpImage, Nimage);
+      free (tmpImage);
+      break; }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      ImagePanstarrs *tmpImage;
+      tmpImage = ImageInternalToPanstarrs ((Image *) ftable[0].buffer, Nimage);
+      free (ftable[0].buffer);
+      gfits_table_set_ImagePanstarrs (ftable, tmpImage, Nimage);
+      free (tmpImage);
+      break; }
+# endif
+    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;
+
+  /* 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); }
+    case DVO_FORMAT_ELIXIR: {
+      ImageElixir *tmpImage;
+      /* convert table rows from internal to external format */
+      for (i = 0; i < Nrow; i++) {
+	tmpImage = ImageInternalToElixir ((Image *) vtable[0].buffer[i], 1);
+	gfits_convert_ImageElixir (tmpImage, sizeof(ImageElixir), 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_ImageElixir (theader);
+
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+      theader[0].Naxis[1] = Nimage;
+      vtable[0].size = gfits_matrix_size (theader);
+      return (TRUE); }
+    case DVO_FORMAT_LONEOS: {
+      ImageLoneos *tmpImage;
+      /* convert table rows from internal to external format */
+      for (i = 0; i < Nrow; i++) {
+	tmpImage = ImageInternalToLoneos ((Image *) vtable[0].buffer[i], 1);
+	gfits_convert_ImageLoneos (tmpImage, sizeof(ImageLoneos), 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_ImageLoneos (theader);
+
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+      theader[0].Naxis[1] = Nimage;
+      vtable[0].size = gfits_matrix_size (theader);
+      return (TRUE); }
+    case DVO_FORMAT_PANSTARRS_DEV_0: {
+      ImagePanstarrs_DEV_0 *tmpImage;
+      /* convert table rows from internal to external format */
+      for (i = 0; i < Nrow; i++) {
+	tmpImage = ImageInternalToPanstarrs_DEV_0 ((Image *) vtable[0].buffer[i], 1);
+	gfits_convert_ImagePanstarrs_DEV_0 (tmpImage, sizeof(ImagePanstarrs_DEV_0), 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_ImagePanstarrs_DEV_0 (theader);
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+      theader[0].Naxis[1] = Nimage;
+      vtable[0].size = gfits_matrix_size (theader);
+      return (TRUE); }
+# if 0
+    case DVO_FORMAT_PANSTARRS: {
+      ImagePanstarrs *tmpImage;
+      /* convert table rows from internal to external format */
+      for (i = 0; i < Nrow; i++) {
+	tmpImage = ImageInternalToPanstarrs ((Image *) vtable[0].buffer[i], 1);
+	gfits_convert_ImagePanstarrs (tmpImage, sizeof(ImagePanstarrs), 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_ImagePanstarrs (theader);
+      gfits_modify (theader, "NAXIS2", "%d", 1, Nimage);
+      theader[0].Naxis[1] = Nimage;
+      vtable[0].size = gfits_matrix_size (theader);
+      return (TRUE); }
+# endif
+    default:
+      break;
+  }
+  fprintf (stderr, "table format unknown (image vtable)\n");
+  return (FALSE);
+}
+
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_elixir.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_elixir.c	(revision 13183)
@@ -0,0 +1,290 @@
+# include <dvo.h>
+
+/* convert elixir-format measures to internal measures */
+Measure *MeasureElixirToInternal (MeasureElixir *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR       = in[i].dR * 0.01;
+    out[i].dD       = in[i].dD * 0.01;
+    out[i].M        = in[i].M  * 0.001;
+    out[i].dM       = in[i].dM * 0.001;
+    out[i].dt       = in[i].dt * 0.001;
+    out[i].Mcal     = in[i].Mcal * 0.001;
+    out[i].Mgal     = in[i].Mgal * 0.001;
+    out[i].airmass  = 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].flags    = 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].stargal  = 0;
+    out[i].Sky      = 0;
+    out[i].dSky     = 0;
+    out[i].qPSF     = 0;
+
+    /* XXX add these later */
+    out[i].detID_hi = 0;
+    out[i].detID_lo = 0;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to elixir-format measures */
+MeasureElixir *MeasureInternalToElixir (Measure *in, int Nvalues) {
+
+  int i;
+  MeasureElixir *out;
+
+  ALLOCATE (out, MeasureElixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR      = in[i].dR * 100.0;
+    out[i].dD 	   = in[i].dD * 100.0;
+    out[i].M       = in[i].M  * 1000.0;
+    out[i].dM      = in[i].dM * 1000.0;
+    out[i].dt      = in[i].dt * 1000.0;
+    out[i].Mcal    = in[i].Mcal * 1000.0;
+    out[i].Mgal    = in[i].Mgal * 1000.0;
+    out[i].airmass = in[i].airmass * 1000.0;
+    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].flags;
+  }
+  return (out);
+}
+
+/* convert elixir-format averages to internal averages */
+Average *AverageElixirToInternal (AverageElixir *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].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = 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_hi = 0;
+    out[i].objID_lo = 0;
+
+    primary[0][i].M  = in[i].M  * 0.001;      
+    primary[0][i].dM = in[i].dM * 0.001;      
+    primary[0][i].Xm = in[i].Xm;     
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+
+    // XXX drop this or keep this?
+    // primary[0][i].Xg = in[i].Xg;
+  }
+  return (out);
+}
+
+/* convert internal averages to elixir-format averages */
+AverageElixir *AverageInternalToElixir (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  AverageElixir *out;
+
+  ALLOCATE (out, AverageElixir, 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].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+
+    out[i].M       = primary[i].M  * 1000.0;      
+    out[i].dM      = primary[i].dM * 1000.0;
+    out[i].Xm      = primary[i].Xm;     
+    // out[i].Xg      = in[i].Xg;
+  }
+  return (out);
+}
+
+/* convert elixir-format secfilts to internal secfilts */
+SecFilt *SecFiltElixirToInternal (SecFiltElixir *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M  * 0.001;      
+    out[i].dM    = 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 */
+SecFiltElixir *SecFiltInternalToElixir (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFiltElixir *out;
+
+  ALLOCATE (out, SecFiltElixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M       = in[i].M  * 1000.0;      
+    out[i].dM      = in[i].dM * 1000.0;
+    out[i].Xm      = in[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert elixir-format images to internal images */
+Image *ImageElixirToInternal (ImageElixir *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));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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 * 0.001;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit * 0.001;
+    out[i].dapmifit 	    = in[i].dapmifit * 0.001;
+    out[i].Mcal   	    = in[i].Mcal * 0.001;
+    out[i].dMcal   	    = 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_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal images to elixir-format images */
+ImageElixir *ImageInternalToElixir (Image *in, int Nvalues) {
+
+  int i;
+  ImageElixir *out;
+
+  ALLOCATE (out, ImageElixir, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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 * 1000.0;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit * 1000.0;
+    out[i].dapmifit 	    = in[i].dapmifit * 1000.0;
+    out[i].Mcal	    	    = in[i].Mcal * 1000.0;
+    out[i].dMcal    	    = 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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_loneos.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_loneos.c	(revision 13183)
@@ -0,0 +1,284 @@
+# include <dvo.h>
+
+/* convert loneos-format measures to internal measures */
+Measure *MeasureLoneosToInternal (MeasureLoneos *in, int Nvalues) {
+
+  int i;
+  Measure *out;
+
+  ALLOCATE (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR   = in[i].dR * 0.01;
+    out[i].dD   = in[i].dD * 0.01;
+    out[i].M    = in[i].M  * 0.001;
+    out[i].dM   = in[i].dM * 0.001;
+    out[i].Mcal = 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].flags   = (in[i].averef & 0xff000000) >> 24;
+
+    /* these values don't exist in the Loneos format */
+    out[i].Mgal    = in[i].M;
+    out[i].dt      = 0;
+    out[i].airmass = 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;
+
+    /* 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_hi = 0;
+    out[i].detID_lo = 0;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to loneos-format measures */
+MeasureLoneos *MeasureInternalToLoneos (Measure *in, int Nvalues) {
+
+  int i;
+  MeasureLoneos *out;
+
+  ALLOCATE (out, MeasureLoneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].dR 	  = in[i].dR * 100.0;
+    out[i].dD 	  = in[i].dD * 100.0;
+    out[i].M  	  = in[i].M  * 1000.0;
+    out[i].dM 	  = in[i].dM * 1000.0;
+    out[i].Mcal   = 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].flags << 24);
+  }
+  return (out);
+}
+
+/* convert loneos-format averages to internal averages */
+Average *AverageLoneosToInternal (AverageLoneos *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].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = 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_hi = 0;
+    out[i].objID_lo = 0;
+
+    primary[0][i].M  = in[i].M  * 0.001;      
+    primary[0][i].Xm = in[i].Xm;     
+    primary[0][i].dM = 0xffff;
+    primary[0][i].Ncode = 0;     
+    primary[0][i].Nused = 0;
+
+    // XXX drop this or keep this?
+    // primary[0][i].Xg = in[i].Xg;
+  }
+  return (out);
+}
+
+/* convert internal averages to loneos-format averages */
+AverageLoneos *AverageInternalToLoneos (Average *in, int Nvalues, SecFilt *primary) {
+
+  int i;
+  AverageLoneos *out;
+
+  ALLOCATE (out, AverageLoneos, 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].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+
+    out[i].M       = primary[i].M * 1000.0;      
+    out[i].Xm      = primary[i].Xm;     
+  }
+  return (out);
+}
+
+/* convert loneos-format secfilts to internal secfilts */
+SecFilt *SecFiltLoneosToInternal (SecFiltLoneos *in, int Nvalues) {
+
+  int i;
+  SecFilt *out;
+
+  ALLOCATE (out, SecFilt, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M     = in[i].M*0.001;      
+    out[i].Xm    = in[i].Xm;      
+    out[i].dM    = 0xffff;
+    out[i].Ncode = 0;
+    out[i].Nused = 0;
+  }
+  return (out);
+}
+
+/* convert internal secfilts to loneos-format secfilts */
+SecFiltLoneos *SecFiltInternalToLoneos (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFiltLoneos *out;
+
+  ALLOCATE (out, SecFiltLoneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].M    = in[i].M*1000.0;      
+    out[i].Xm   = in[i].Xm;      
+  }
+  return (out);
+}
+
+/* convert loneos-format images to internal images */
+Image *ImageLoneosToInternal (ImageLoneos *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));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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 * 0.001;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit * 0.001;
+    out[i].dapmifit 	    = in[i].dapmifit * 0.001;
+    out[i].Mcal	    = in[i].Mcal * 0.001;
+    out[i].dMcal    	    = 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_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal images to loneos-format images */
+ImageLoneos *ImageInternalToLoneos (Image *in, int Nvalues) {
+
+  int i;
+  ImageLoneos *out;
+
+  ALLOCATE (out, ImageLoneos, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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 * 1000.0;
+    out[i].NX	    	    = in[i].NX;
+    out[i].NY	    	    = in[i].NY;
+    out[i].apmifit  	    = in[i].apmifit * 1000.0;
+    out[i].dapmifit 	    = in[i].dapmifit * 1000.0;
+    out[i].Mcal	    	    = in[i].Mcal * 1000.0;
+    out[i].dMcal    	    = 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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs.c	(revision 13183)
@@ -0,0 +1,285 @@
+# include <dvo.h>
+
+/*** XXX is this format actually used by anyone?? ***/
+
+/* convert panstarrs-format measures to internal measures */
+Measure *MeasurePanstarrsToInternal (MeasurePanstarrs *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;
+    out[i].Mgal       = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    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].source;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = 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].stargal  = 0;
+    out[i].Sky      = 0;
+    out[i].dSky     = 0;
+    out[i].qPSF     = 0;
+
+    /* XXX add these later */
+    out[i].detID_hi = 0;
+    out[i].detID_lo = 0;
+    out[i].imageID_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+MeasurePanstarrs *MeasureInternalToPanstarrs (Measure *in, int Nvalues) {
+
+  int i;
+  MeasurePanstarrs *out;
+
+  ALLOCATE (out, MeasurePanstarrs, 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].Mgal;
+    out[i].airmass = in[i].airmass;
+    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].source  = in[i].photcode;
+    out[i].t       = in[i].t;
+    out[i].averef  = in[i].averef;
+    out[i].flags   = in[i].flags;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+Average *AveragePanstarrsToInternal (AveragePanstarrs *in, int Nvalues) {
+
+  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].M       = in[i].M;      
+    out[i].dM      = in[i].dM;
+    out[i].Xp      = in[i].Xp;     
+    out[i].Xm      = in[i].Xm;     
+    out[i].Xg      = in[i].Xg;
+    out[i].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+
+    /* these don't exist in Panstarrs */
+    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_hi = 0;
+    out[i].objID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+AveragePanstarrs *AverageInternalToPanstarrs (Average *in, int Nvalues) {
+
+  int i;
+  AveragePanstarrs *out;
+
+  ALLOCATE (out, AveragePanstarrs, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    out[i].R       = in[i].R;      
+    out[i].D       = in[i].D;      
+    out[i].M       = in[i].M;      
+    out[i].dM      = in[i].dM;
+    out[i].Xp      = in[i].Xp;     
+    out[i].Xm      = in[i].Xm;     
+    out[i].Xg      = in[i].Xg;
+    out[i].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFiltPanstarrsToInternal (SecFiltPanstarrs *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 = 0;
+    out[i].Nused = 0;
+ }
+  return (out);
+}
+
+/* convert internal secfilts to panstarrs-format secfilts */
+SecFiltPanstarrs *SecFiltInternalToPanstarrs (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFiltPanstarrs *out;
+
+  ALLOCATE (out, SecFiltPanstarrs, 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;     
+  }
+  return (out);
+}
+
+/* convert panstarrs-format images to internal images */
+Image *ImagePanstarrsToInternal (ImagePanstarrs *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));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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].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_hi = 0;
+    out[i].imageID_lo = 0;
+  }
+  return (out);
+}
+
+/* convert internal images to panstarrs-format images */
+ImagePanstarrs *ImageInternalToPanstarrs (Image *in, int Nvalues) {
+
+  int i;
+  ImagePanstarrs *out;
+
+  ALLOCATE (out, ImagePanstarrs, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    memcpy (&out[i].coords, &in[i].coords, sizeof(Coords));
+    memcpy (out[i].dummy, in[i].dummy, 20);
+    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].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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_panstarrs_DEV_0.c	(revision 13183)
@@ -0,0 +1,295 @@
+# include <dvo.h>
+
+/* convert panstarrs-format measures to internal measures */
+Measure *MeasurePanstarrs_DEV_0_ToInternal (MeasurePanstarrs_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;
+    out[i].Mgal       = 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].flags      = 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;
+    out[i].detID_hi   = in[i].detID_hi;
+    out[i].detID_lo   = in[i].detID_lo;
+    out[i].imageID_hi = in[i].imageID_hi;
+    out[i].imageID_lo = in[i].imageID_lo;
+  }
+  return (out);
+}
+
+/* convert internal measures to panstarrs-format measures */
+MeasurePanstarrs_DEV_0 *MeasureInternalToPanstarrs_DEV_0 (Measure *in, int Nvalues) {
+
+  int i;
+  MeasurePanstarrs_DEV_0 *out;
+
+  ALLOCATE (out, MeasurePanstarrs_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].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].flags      = 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;
+    out[i].detID_hi   = in[i].detID_hi;
+    out[i].detID_lo   = in[i].detID_lo;
+    out[i].imageID_hi = in[i].imageID_hi;
+    out[i].imageID_lo = in[i].imageID_lo;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+Average *AveragePanstarrs_DEV_0_ToInternal (AveragePanstarrs_DEV_0 *in, int Nvalues) {
+
+  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].Nm       = in[i].Nm;     
+    out[i].Nn       = in[i].Nn;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].offset; 
+    out[i].missing  = 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_hi = in[i].objID_hi;
+    out[i].objID_lo = in[i].objID_lo;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+AveragePanstarrs_DEV_0 *AverageInternalToPanstarrs_DEV_0 (Average *in, int Nvalues) {
+
+  int i;
+  AveragePanstarrs_DEV_0 *out;
+
+  ALLOCATE (out, AveragePanstarrs_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].Nm;     
+    out[i].Nn       = in[i].Nn;     
+    out[i].code     = in[i].code;   
+    out[i].offset   = in[i].offset; 
+    out[i].missing  = 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_hi = in[i].objID_hi;
+    out[i].objID_lo = in[i].objID_lo;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format secfilts to internal secfilts */
+SecFilt *SecFiltPanstarrs_DEV_0_ToInternal (SecFiltPanstarrs_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 */
+SecFiltPanstarrs_DEV_0 *SecFiltInternalToPanstarrs_DEV_0 (SecFilt *in, int Nvalues) {
+
+  int i;
+  SecFiltPanstarrs_DEV_0 *out;
+
+  ALLOCATE (out, SecFiltPanstarrs_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 *ImagePanstarrs_DEV_0_ToInternal (ImagePanstarrs_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[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	    = in[i].imageID_hi;
+    out[i].imageID_lo	    = 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 */
+ImagePanstarrs_DEV_0 *ImageInternalToPanstarrs_DEV_0 (Image *in, int Nvalues) {
+
+  int i;
+  ImagePanstarrs_DEV_0 *out;
+
+  ALLOCATE (out, ImagePanstarrs_DEV_0, 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	    = in[i].imageID_hi;
+    out[i].imageID_lo	    = 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);
+}
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_pmtest.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_pmtest.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_convert_pmtest.c	(revision 13183)
@@ -0,0 +1,160 @@
+# include <dvo.h>
+
+/* convert elixir-format measures to internal measures */
+Measure *MeasureElixirToInternal (MeasureElixir *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].dt         = in[i].dt;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    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].source;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = 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;
+					    
+    out[i].detID_hi   = in[i].detID_hi;
+    out[i].detID_lo   = in[i].detID_lo;
+    out[i].imageID_hi = in[i].imageID_hi;
+    out[i].imageID_lo = in[i].imageID_lo;
+  }
+  return (out);
+}
+
+/* convert internal measures to elixir-format measures */
+MeasureElixir *MeasureInternalToElixir (Measure *in, int Nvalues) {
+
+  int i;
+  MeasureElixir *out;
+
+  ALLOCATE (out, MeasureElixir, 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].dt         = in[i].dt;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Mgal       = in[i].Mgal;
+    out[i].airmass    = in[i].airmass;
+    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].source;
+    out[i].t          = in[i].t;
+    out[i].averef     = in[i].averef;
+    out[i].flags      = 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;
+					    
+    out[i].detID_hi   = in[i].detID_hi;
+    out[i].detID_lo   = in[i].detID_lo;
+    out[i].imageID_hi = in[i].imageID_hi;
+    out[i].imageID_lo = in[i].imageID_lo;
+  }
+  return (out);
+}
+
+/* convert panstarrs-format averages to internal averages */
+Average *AveragePMtestToInternal (AveragePMtest *in, int Nvalues) {
+
+  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].M       = in[i].M;      
+    out[i].dM      = in[i].dM;
+    out[i].Xp      = in[i].Xp;     
+    out[i].Xm      = in[i].Xm;     
+    out[i].Xg      = in[i].Xg;
+    out[i].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+  }
+  return (out);
+}
+
+/* convert internal averages to panstarrs-format averages */
+AveragePMtest *AverageInternalToPMtest (Average *in, int Nvalues) {
+
+  int i;
+  AveragePMtest *out;
+
+  ALLOCATE (out, AveragePMtest, 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].M       = in[i].M;      
+    out[i].dM      = in[i].dM;
+    out[i].Xp      = in[i].Xp;     
+    out[i].Xm      = in[i].Xm;     
+    out[i].Xg      = in[i].Xg;
+    out[i].Nm      = in[i].Nm;     
+    out[i].Nn      = in[i].Nn;     
+    out[i].code    = in[i].code;   
+    out[i].offset  = in[i].offset; 
+    out[i].missing = in[i].missing;
+  }
+  return (out);
+}
+
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image.c	(revision 13183)
@@ -0,0 +1,197 @@
+# 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);
+  }
+  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) 	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PANSTARRS");
+  // if (db[0].format == DVO_FORMAT_PMTEST)    	  gfits_modify (&db[0].header, "FORMAT", "%s", 1, "PMTEST");
+  
+  return;
+}
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image_raw.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 13183)
@@ -0,0 +1,153 @@
+# 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(ImageLoneos);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  ImageSize = sizeof(ImageElixir);
+  if (db[0].format == DVO_FORMAT_PANSTARRS) 	  ImageSize = sizeof(ImagePanstarrs);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) ImageSize = sizeof(ImagePanstarrs_DEV_0);
+
+  /* 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_ImageLoneos (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_ELIXIR)    	  gfits_table_mkheader_ImageElixir (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS) 	  gfits_table_mkheader_ImagePanstarrs (&db[0].theader);
+  if (db[0].format == DVO_FORMAT_PANSTARRS_DEV_0) gfits_table_mkheader_ImagePanstarrs_DEV_0 (&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_matrix_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/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_photcode_ops.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 13183)
@@ -0,0 +1,526 @@
+# 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;
+
+void SetZeroPoint (double ZP) {
+  ZERO_POINT = ZP;
+}
+
+# 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 (NO_MAG);
+
+  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 (NO_MAG);
+
+  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 (NO_MAG);
+
+  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 (mc == NO_MAG) 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 (NO_MAG);
+
+  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 (mc == NO_MAG) 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;
+
+  /* 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 (NO_MAG);
+  }
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mrel = thisone[0].M;
+    return (Mrel);
+  }
+  if (code[0].code != photcodes[0].code[Np].equiv) {
+    return (NO_MAG);
+  }
+
+  Mcal = PhotRel (thisone, average, secfilt) + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (mc == NO_MAG) 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 = NO_MAG;
+
+  if (measure == NULL) {
+    Ns1 = photcodes[0].hashNsec[code[0].c1];
+    Ns2 = photcodes[0].hashNsec[code[0].c2];
+  
+    m1 = (Ns1 == -1) ? NO_MAG : secfilt[Ns1].M;
+    m2 = (Ns2 == -1) ? NO_MAG : secfilt[Ns2].M;
+    mc = ((m1 == NO_MAG) || (m2 == NO_MAG)) ? NO_MAG : (m1 - m2);
+    return (mc);
+  }
+
+  /* find magnitude matching first color term */
+  color = GetPhotcodebyCode (code[0].c1);
+  if (color == NULL) return (NO_MAG);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nm) && (m1 == NO_MAG); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m1 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m1 = (Ns == -1) ? NO_MAG : secfilt[Ns].M;
+  }	
+
+  /* find magnitude matching second color term */
+  color = GetPhotcodebyCode (code[0].c2);
+  if (color == NULL) return (NO_MAG);
+  if (color[0].type == PHOT_REF) {
+    for (i = 0; (i < average[0].Nm) && (m2 == NO_MAG); i++) {
+      if (measure[i].photcode == color[0].code) {
+	m2 = measure[i].M;
+      }
+    }
+  } else {
+    Ns = photcodes[0].hashNsec[color[0].code];
+    m2 = (Ns == -1) ? NO_MAG : secfilt[Ns].M;
+  }	
+  mc = ((m1 == NO_MAG) || (m2 == NO_MAG)) ? NO_MAG : (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;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NO_MAG : secfilt[Ns].M;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (mc == NO_MAG) 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;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NO_MAG : secfilt[Ns].M;
+  return (Mave);
+}
+
+float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float dM;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  dM  = (Ns == -1) ? NO_MAG : secfilt[Ns].dM;
+  return (dM);
+}
+
+float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  short Mi;
+  float Xm;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mi = (Ns == -1) ? NO_MAG : secfilt[Ns].Xm;
+  Xm = (Mi == NO_MAG) ? -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].Nm; 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) ? NO_MAG : secfilt[Ns].M;
+  }	
+
+filter1:
+  code = GetPhotcodebyCode (c2);
+  if (code == NULL) return (FALSE);
+  if (code[0].type == PHOT_REF) {
+    for (i = 0; i < average[0].Nm; 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) ? NO_MAG : 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/libdvo-1-6-0/Ohana/src/libdvo/src/fits_db.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/fits_db.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/fits_db.c	(revision 13183)
@@ -0,0 +1,230 @@
+# 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"); 
+    return (FALSE);
+  }
+  if (!gfits_fread_matrix (db[0].f, &db[0].matrix, &db[0].header)) {
+    fprintf (stderr, "can't read primary matrix");
+    return (FALSE);
+  }
+  if (!gfits_fread_header (db[0].f, &db[0].theader)) {
+    fprintf (stderr, "can't read table header");
+    return (FALSE);
+  }
+  if (!gfits_fread_ftable_data (db[0].f, &db[0].ftable)) {
+    fprintf (stderr, "can't read table data");
+    return (FALSE);
+  }
+  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_matrix_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/libdvo-1-6-0/Ohana/src/libdvo/src/imreg_datatypes.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/imreg_datatypes.c	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/src/mosaic_astrom.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/mosaic_astrom.c	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/src/photfits.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/photfits.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/photfits.c	(revision 13183)
@@ -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 (NO_MAG);
+}
+
+/* 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/libdvo-1-6-0/Ohana/src/libdvo/src/skydb.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skydb.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skydb.c	(revision 13183)
@@ -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/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_gsc.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_gsc.c	(revision 13183)
@@ -0,0 +1,565 @@
+# 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;
+  
+  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);
+  }
+  gfits_free_header (&theader);
+
+/* 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);
+    
+    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);
+
+  /* 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);
+  }
+  return (skytable);
+}
+
+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) {
+
+  int i, j, l, ir, N;
+  SkyRegion *regions;
+  SkyRegion tempregion;
+  char *tempfile = NULL;
+  char **filename = NULL;
+  
+  N = table[0].Nregions;
+  regions = table[0].regions;
+  filename = table[0].filename;
+
+  if (N < 2) return;
+  l = N >> 1;
+  ir = N - 1;
+  for (;;) {
+    if (l > 0) {
+      l--;
+      tempregion = regions[l];
+      if (filename) tempfile   = filename[l];
+    } else {
+      tempregion   = regions[ir];
+      if (filename) tempfile     = filename[ir];
+      regions[ir]   = regions[0];
+      if (filename) filename[ir] = filename[0];
+      if (--ir == 0) {
+	regions[0]   = tempregion;
+	if (filename) filename[0] = tempfile;
+	return;
+      }
+    }
+    i = l;
+    j = (l << 1) + 1;
+    while (j <= ir) {
+      if (j < ir && regions[j].Rmin < regions[j+1].Rmin) ++j;
+      if (tempregion.Rmin < regions[j].Rmin) {
+	regions[i]   = regions[j];
+	if (filename) filename[i] = filename[j];
+	j += (i=j) + 1;
+      } 
+      else j = ir + 1;
+    }
+    regions[i]   = tempregion;
+    if (filename) filename[i] = tempfile;
+  }
+}
+
+/* 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.
+ */
+
+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);
+}
+  
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_io.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_io.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_io.c	(revision 13183)
@@ -0,0 +1,169 @@
+# 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/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_ops.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_ops.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/skyregion_ops.c	(revision 13183)
@@ -0,0 +1,522 @@
+# 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;
+
+  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;
+
+  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;
+
+  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, 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++) {
+      list[0].regions[i + Ns] = extra[0].regions[i];
+      list[0].filename[i + Ns] = extra[0].filename[i];
+    }
+    list[0].Nregions += extra[0].Nregions;
+    SkyListFree (extra, FALSE);
+  } 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);
+
+  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, FALSE);
+    } 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 ELEMENTS) {
+
+  int i;
+
+  /* XXX do we need to free the filename array as well? */
+  if (list == NULL) return (TRUE);
+  if (list[0].regions != NULL) {
+    if (ELEMENTS) {
+      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].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);
+}
+
+# if (0)
+
+/* find regions contained within rectangular region  c1 - c2 */
+SkyRegion **SkyFindArea (SkyRegion *db, SkyCoord c1, SkyCoord c2, int *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;
+    list = SkyFindArea (db, c1, c0, &Nlist);
+    c0 = c1; c0.r = 0.0;
+    new  = SkyFindArea (db, c0, c2, &Nnew);
+    REALLOCATE (list, SkyRegion *, MAX (1, Nlist + Nnew));
+    memcpy (&list[Nlist], new, Nnew*sizeof(SkyRegion *));
+    free (new);
+    Nlist += Nnew;
+    *nlist = Nlist;
+    return (list);
+  }    
+
+  Ns = 0;
+  Ne = 1;
+  Nlist = 1;
+  ALLOCATE (list, SkyRegion *, Nlist);
+  list[0] = &region[0];
+  getchild = region[0].child;
+
+  while (getchild) {
+
+    getchild = FALSE;
+    Nnew = 0;
+    NNEW = 100;
+    ALLOCATE (new, SkyRegion *, NNEW);
+
+    for (i = Ns; i < Ne; i++) {
+      children = SkyFindChildren(db, list[i], c1, c2, &Nchildren);
+      if (children == NULL) continue;
+      if (Nnew + Nchildren >= NNEW) {
+	NNEW += 100;
+	REALLOCATE (new, SkyRegion *, NNEW);
+      }
+      for (i = 0; i < Nchildren; i++) {
+	getchild |= children[i][0].child;
+	new[Nnew] = children[i];
+	Nnew++;
+      }
+      free (children);
+    }
+
+    REALLOCATE (list, SkyRegion *, Nlist + Nnew);
+    memcpy (&list[Nlist], new, Nnew*sizeof(SkyRegion *));
+    free (new);
+    Nlist += Nnew;
+  }
+  return (list);
+  *nlist = Nlist;
+}
+
+/* find all children of the given sky region */
+SkyRegion **SkyFindChildren (SkyRegion *db, SkyRegion *parent, SkyCoord c1, SkyCoord c2, int *Nchildren) {
+
+  int i, Ns, Ne, Nregion, NREGIONS;
+  SkyRegion **region;
+  
+  *Nchildren = 0;
+  if (!parent[0].child) return (NULL);
+
+  Ns = parent[0].childS;
+  Ne = parent[0].childE;
+
+  Nregion = 0;
+  NREGIONS = 100;
+  ALLOCATE (region, SkyRegion *, NREGIONS);
+
+  for (i = Ns; i < Ne; i++) {
+    if (region[i].Rmax < c1.r) continue;
+    if (region[i].Rmin > c2.r) continue;
+    if (region[i].Dmax < c1.d) continue;
+    if (region[i].Dmin > c2.d) continue;
+    region[Nregion] = &region[i];
+    Nregion++;
+    if (Nregion == NREGIONS) {
+      NREGIONS += 100;
+      REALLOCATE (region, SkyRegion *, NREGIONS);
+    }      
+  }
+  *Nchildren = Nregion; 
+  return (region);
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyExtend (SkyRegion *db, SkyRegion *region) {
+
+  fprintf (stderr, "not implemented\n");
+
+}
+
+/* region is pointer to entry in db */
+SkyRegion *SkyContract (SkyRegion *db, SkyRegion *region) {
+
+  fprintf (stderr, "not implemented\n");
+
+}
+
+SkyRegion *SkyFindGCircle (SkyRegion *db, SkyCoord c1, SkyCoord c2) {
+
+  fprintf (stderr, "not implemented\n");
+
+}
+# endif 
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/tabletest.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/tabletest.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/tabletest.c	(revision 13183)
@@ -0,0 +1,10 @@
+# include "dvo.h"
+
+int main (int argc, char **argv) {
+
+  SkyTable *table;
+
+  SkyTableFromGSC (argv[1], 2, TRUE);
+
+  exit (0);
+}
Index: /tags/libdvo-1-6-0/Ohana/src/libdvo/src/version.c
===================================================================
--- /tags/libdvo-1-6-0/Ohana/src/libdvo/src/version.c	(revision 13183)
+++ /tags/libdvo-1-6-0/Ohana/src/libdvo/src/version.c	(revision 13183)
@@ -0,0 +1,6 @@
+# include <dvo.h>
+static char *name = "$Name: not supported by cvs2svn $";
+
+char *libdvo_version () {
+  return (name);
+}
