Index: branches/eam_branches/ipp-20120905/Ohana/src/addstar/src/ReadStarsFITS.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/addstar/src/ReadStarsFITS.c	(revision 34468)
@@ -606,5 +606,5 @@
     stars[i].measure.psfChisq   = ps1data[i].psfChisq;
     stars[i].measure.psfQF      = ps1data[i].psfQF;
-    stars[i].measure.psfQFperf  = ps1data[i].psfQFpef;
+    stars[i].measure.psfQFperf  = ps1data[i].psfQFperf;
     stars[i].measure.psfNdof    = ps1data[i].psfNdof;
     stars[i].measure.psfNpix    = ps1data[i].psfNpix;
@@ -687,5 +687,6 @@
     stars[i].measure.psfChisq   = ps1data[i].psfChisq;
     stars[i].measure.psfQF      = ps1data[i].psfQF;
-    stars[i].measure.psfQFperf  = ps1data[i].psfQFpef;
+    stars[i].measure.psfQFperf  = ps1data[i].psfQFperf;
+
     stars[i].measure.psfNdof    = ps1data[i].psfNdof;
     stars[i].measure.psfNpix    = ps1data[i].psfNpix;
@@ -773,5 +774,6 @@
     stars[i].measure.psfChisq   = ps1data[i].psfChisq;
     stars[i].measure.psfQF      = ps1data[i].psfQF;
-    stars[i].measure.psfQFperf  = ps1data[i].psfQFpef;
+    stars[i].measure.psfQFperf  = ps1data[i].psfQFperf;
+
     stars[i].measure.psfNdof    = ps1data[i].psfNdof;
     stars[i].measure.psfNpix    = ps1data[i].psfNpix;
@@ -930,5 +932,6 @@
     stars[i].measure.psfChisq   = ps1data[i].psfChisq;
     stars[i].measure.psfQF      = ps1data[i].psfQF;
-    stars[i].measure.psfQFperf  = ps1data[i].psfQFpef;
+    stars[i].measure.psfQFperf  = ps1data[i].psfQFperf;
+
     stars[i].measure.psfNdof    = ps1data[i].psfNdof;
     stars[i].measure.psfNpix    = ps1data[i].psfNpix;
Index: branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/MatchImages.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/MatchImages.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/MatchImages.c	(revision 34468)
@@ -43,4 +43,5 @@
     tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
     tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+    tcoords.Npolyterms = 1;
     strcpy (tcoords.ctype, "RA---TAN");
   }
Index: branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/args.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/args.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/args.c	(revision 34468)
@@ -40,4 +40,5 @@
 
   MagLimitUse = FALSE;
+  MagLimitValue = 100;
   if ((N = get_argument (argc, argv, "-maglim"))) {
     MagLimitUse = TRUE;
@@ -60,4 +61,5 @@
 
   MinMagUse = FALSE;
+  MinMagValue = -100;
   if ((N = get_argument (argc, argv, "-minmag"))) {
     MinMagUse = TRUE;
Index: branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/select_by_region.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/select_by_region.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/getstar/src/select_by_region.c	(revision 34468)
@@ -82,4 +82,6 @@
       }
       if (isnan(mag)) continue;
+      if (mag < MinMagValue) continue;
+      if (mag > MagLimitValue) continue;
 
       bin = (mag - MagMin) / dMag;
@@ -118,5 +120,7 @@
     if (D > region[0].Dmax) continue;
 
-    if (MagLimitUse) {
+    // If either magnitude limit was specfied it a apply both.
+    // If one of the limits is not used it is initialized to an unphysical value so test will succeed
+    if (MagLimitUse || MinMagUse) {
       mag = NAN;
       if (needMeas) {
@@ -132,4 +136,5 @@
       }
       if (isnan(mag) || (mag > MagLimitValue)) continue;
+      if (isnan(mag) || (mag < MinMagValue)) continue;
     }
 
Index: branches/eam_branches/ipp-20120905/Ohana/src/libohana/include/ohana.h
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/libohana/include/ohana.h	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/libohana/include/ohana.h	(revision 34468)
@@ -256,4 +256,6 @@
 int     iparse                 PROTO((int *X, int NX, char *line));
 int     iparse_csv             PROTO((int *X, int NX, char *line));
+int     charparse_csv          PROTO((char *X, int NX, char *line));
+int     charparse              PROTO((char *X, int NX, char *line));
 int     tparse                 PROTO((time_t *X, int NX, char *line));
 int     tparse_csv             PROTO((time_t *X, int NX, char *line));
Index: branches/eam_branches/ipp-20120905/Ohana/src/libohana/src/string.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/libohana/src/string.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/libohana/src/string.c	(revision 34468)
@@ -267,4 +267,36 @@
 }
 
+int charparse (char *X, int NX, char *line) {
+
+  int i;
+  char *word;
+
+  word = line;
+  for (i = 0; i < NX - 1; i++)
+    word = _parse_nextword (word);
+
+  *X = word[0];
+  return (1);
+}
+
+int charparse_csv (char *X, int NX, char *line) {
+
+  int i;
+  char *word;
+
+  word = line;
+  for (i = 0; i < NX - 1; i++)
+    word = _parse_nextword_csv (word);
+  
+  if (word[0] == '"') word[0] = word[1];
+  if (word[0] == ',') {
+      *X = 0;
+      return 1;
+  }
+
+  *X = word[0];
+  return (1);
+}
+
 int tparse (time_t *X, int NX, char *line) {
 
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 34468)
@@ -22,5 +22,5 @@
 
 // vector types
-enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME};
+enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
 
 int read_vectors (int argc, char **argv) {
@@ -101,4 +101,5 @@
       if (!strcasecmp(ptr, "float")) { coltype[i] = COLTYPE_FLT; }
       if (!strcasecmp(ptr, "int"))   { coltype[i] = COLTYPE_INT; }
+      if (!strcasecmp(ptr, "char"))  { coltype[i] = COLTYPE_CHAR; }
       if (!strcasecmp(ptr, "time"))  { coltype[i] = COLTYPE_TIME; }
       if (!coltype[i]) goto bad_colname;
@@ -143,5 +144,5 @@
   NELEM = 1000;
   for (i = 0; i < Nvec; i++) {
-    if (coltype[i] == COLTYPE_INT) {
+    if ((coltype[i] == COLTYPE_INT) || (coltype[i] == COLTYPE_CHAR)) {
       ResetVector (vec[i], OPIHI_INT, NELEM);
     } else {
@@ -195,4 +196,5 @@
       for (i = 0; i < Nvec; i++) {
 	int ivalue;
+	char cvalue;
 	double dvalue;
 	time_t tvalue;
@@ -203,4 +205,8 @@
 	    readStatus = IsCSV ? iparse_csv (&ivalue, col[i], c0) : iparse (&ivalue, col[i], c0);
 	    vec[i][0].elements.Int[Nelem] = readStatus ? ivalue : 0;
+	    break;
+	  case COLTYPE_CHAR:
+	    readStatus = IsCSV ? charparse_csv (&cvalue, col[i], c0) : charparse (&cvalue, col[i], c0);
+	    vec[i][0].elements.Int[Nelem] = readStatus ? cvalue : 0;
 	    break;
 	  case COLTYPE_FLT:
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/reindex.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/reindex.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/reindex.c	(revision 34468)
@@ -5,9 +5,15 @@
 int reindex (int argc, char **argv) {
   
-  int  i, Npts, Nmax;
+  int  i, Nmax, N;
   Vector *ivec, *ovec, *xvec;
 
   ivec = ovec = xvec = NULL;
-  Npts = 0;
+  int Npts = 0;
+
+  int KEEP_UNMATCH = FALSE;
+  if ((N = get_argument (argc, argv, "-keep-unmatched"))) {
+    remove_argument (N, &argc, argv);
+    KEEP_UNMATCH = TRUE;
+  }
 
   if (argc != 6) goto usage;
@@ -22,4 +28,5 @@
 
   // ovec matches ivec in type and xvec in size (xvec need not have all ivec elements, and may have duplicates
+  int NPTS = xvec[0].Nelements;
   ResetVector (ovec, ivec->type, xvec[0].Nelements);
 
@@ -31,6 +38,15 @@
     opihi_int *vx = xvec[0].elements.Int;
     for (Npts = i = 0; i < xvec[0].Nelements; i++, vx++) {
-      if (*vx == -1) continue;
-      if (*vx < 0) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	REALLOCATE (ovec[0].elements.Flt, opihi_flt, NPTS);
+      }
+      if (*vx < 0) {
+	if (KEEP_UNMATCH) {
+	  ovec[0].elements.Flt[Npts] = NAN;
+	  Npts++;
+	} 
+	continue;
+      }
       if (*vx > Nmax) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
       ovec[0].elements.Flt[Npts] = vi[*vx];
@@ -42,6 +58,15 @@
     opihi_int *vx = xvec[0].elements.Int;
     for (Npts = i = 0; i < xvec[0].Nelements; i++, vx++) {
-      if (*vx == -1) continue;
-      if (*vx < 0) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
+      if (Npts >= NPTS) {
+	NPTS += 2000;
+	REALLOCATE (ovec[0].elements.Int, opihi_int, NPTS);
+      }
+      if (*vx < 0) {
+	if (KEEP_UNMATCH) {
+	  ovec[0].elements.Flt[Npts] = -1;
+	  Npts++;
+	} 
+	continue;
+      }
       if (*vx > Nmax) ESCAPE("unexpected value in index: %d (%d)\n", *vx, i);
       ovec[0].elements.Int[Npts] = vi[*vx];
@@ -60,5 +85,10 @@
 usage:
     gprint (GP_ERR, "USAGE: reindex (out) = (in) using (index)\n");
-    gprint (GP_ERR, "  creates a new vectors (out) from (in) based on sequence in (index)\n");
+    gprint (GP_ERR, "  Creates a new vector (out) from (in) based on sequence in (index)\n");
+    gprint (GP_ERR, "  output[i] = input[index[i]]\n");
+    gprint (GP_ERR, "  If -keep-unmatched is provided, elements of index with negative values will be set to NaN / -1,\n");
+    gprint (GP_ERR, "    otherwise they will be skipped in the output.\n");
+    gprint (GP_ERR, "  The output vector has the type of the input vector and the length of the index (if -keep-unmatched).\n");
+    gprint (GP_ERR, "  The index vector may have duplicates\n");
     return (FALSE);
 }
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/subset.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/subset.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/cmd.data/subset.c	(revision 34468)
@@ -13,6 +13,10 @@
   Npts = 0;
 
+  if (argc < 6) {
+    gprint (GP_ERR, "SYNTAX: subset vec = vec [if/where] (logic expression)\n");
+    return (FALSE);
+  }
+
   valid = TRUE;
-  valid &= (argc >= 6);
   valid &= !strcmp(argv[2], "=");
   valid &= !strcmp(argv[4], "if") || !strcmp (argv[4], "where");
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/Makefile
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/Makefile	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/Makefile	(revision 34468)
@@ -67,4 +67,5 @@
 $(SRC)/lightcurve.$(ARCH).o	  	\
 $(SRC)/mextract.$(ARCH).o	  	\
+$(SRC)/mmatch.$(ARCH).o	  	\
 $(SRC)/mmextract.$(ARCH).o	  	\
 $(SRC)/objectcoverage.$(ARCH).o	  	\
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/avmatch.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/avmatch.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/avmatch.c	(revision 34468)
@@ -203,6 +203,6 @@
       Npts = j;
 
-      if (Ncat == -1) continue;
-      if (Ncat == -2) continue;
+      if (Ncat == -1) continue; // this point is not in this catalog file
+      if (Ncat == -2) continue; // no matches to this point
 
       m = catalog.average[Ncat].measureOffset;
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/init.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/init.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/init.c	(revision 34468)
@@ -42,4 +42,5 @@
 int lightcurve      PROTO((int, char **));
 int mextract        PROTO((int, char **));
+int mmatch          PROTO((int, char **));
 int mmextract       PROTO((int, char **));
 int objectcoverage  PROTO((int, char **));
@@ -97,4 +98,5 @@
   {1, "lightcurve",  lightcurve,   "extract lightcurve for a star"},
   {1, "mextract",    mextract,     "extract measure data values"},
+  {1, "mmatch",      mmatch,       "extract measure data values matched to RA,DEC points"},
   {1, "mmextract",   mmextract,    "extract joined measurements"},
   {1, "objectcoverage", objectcoverage, "plot catalog boundaries"},
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/mmatch.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/mmatch.c	(revision 34468)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/mmatch.c	(revision 34468)
@@ -0,0 +1,342 @@
+# include "dvoshell.h"
+
+/* This function uses the 'find_match' algorithm to select the objects of interest.
+   All measurements for matched objects result in an element in the output vectors.
+   if -index (index) is supplied, the index of the input vectors are stored to match
+   the output vectors.
+ */
+
+int mmatch (int argc, char **argv) {
+  
+  off_t i, j, k, n, m, *index;
+  int N, Ncat, Npts, NPTS, last, Nfields, Nsecfilt, Ninvec;
+  int VERBOSE;
+  char name[1024];
+  void *Signal;
+  float RADIUS;
+
+  Catalog catalog;
+
+  Vector **vec, **invec, *RAvec, *DECvec, *IDXvec;
+  dbField *fields;
+  dbValue *values;
+  SkyList *skylist;
+
+  /* defaults */
+  vec = NULL;
+  invec = NULL;
+  fields = NULL;
+  values = NULL;
+  skylist = NULL;
+  Ninvec = 0;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto help;
+  if ((N = get_argument (argc, argv, "--help"))) goto help;
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+
+  IDXvec = NULL;
+  if ((N = get_argument (argc, argv, "-index"))) {
+    remove_argument (N, &argc, argv);
+    IDXvec = SelectVector (argv[N], ANYVECTOR, TRUE);
+    if (IDXvec == NULL) goto help;
+    remove_argument (N, &argc, argv);
+  }
+
+  int PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    remove_argument (N, &argc, argv);
+    PARALLEL = TRUE;
+  }
+
+  // read RA,DEC coords from a fits file (esp for parallel dvo)
+  char *CoordsFile = NULL;
+  if ((N = get_argument (argc, argv, "-coords"))) {
+    remove_argument (N, &argc, argv);
+    CoordsFile = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (!CoordsFile && (argc < 5)) goto help;
+  if ( CoordsFile && (argc < 3)) goto help;
+
+  dvo_catalog_init (&catalog, TRUE);
+
+  /* load photcode information */
+  if (!InitPhotcodes ()) goto escape;
+  Nsecfilt = GetPhotcodeNsecfilt ();
+
+  // init locally static variables (time refs)
+  dbExtractMeasuresInit();
+
+  // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
+  RAvec  = NULL;
+  DECvec = NULL;
+  if (PARALLEL && !HOST_ID) {
+
+    // We need to copy the args to a temp array and modify them so that we send the
+    // correct set to the remote client.  The args list looks like this:
+    // if (!CoordsFile) : mmatch (RA) (DEC) (RADIUS) field, ...
+    // if ( CoordsFile) : mmatch (RADIUS) field, ... [because we stripped off the -coords filename elements]
+
+    // allocate the temp array and copy all but (RA) (DEC)
+    int targc = 0;
+    char **targv = NULL;
+    ALLOCATE (targv, char *, argc + 2);
+    for (i = 0; i < argc; i++) {
+      if (!CoordsFile && (i == 1)) continue;
+      if (!CoordsFile && (i == 2)) continue;
+      targv[targc] = strcreate (argv[i]);
+      targc ++;
+    }
+
+    // if not specified, create the coords.fits input file
+    if (!CoordsFile) {
+      // get vectors corresponding to coordinates of interest
+      if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+      if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+      
+      ALLOCATE (vec, Vector *, 2);
+      vec[0] = RAvec;
+      vec[1] = DECvec;
+
+      CoordsFile = abspath("coords.fits", 1024);
+      int status = WriteVectorTableFITS (CoordsFile, "COORDS", vec, 2, FALSE, NULL);
+      if (!status) goto escape;
+    }
+
+    // add the coords file to the args list
+    targv[targc+0] = strcreate ("-coords");
+    targv[targc+1] = CoordsFile; // this gets freed with targv
+    targc += 2;
+    
+    // if needed, add the index vector to the args list
+    if (IDXvec) {
+      REALLOCATE (targv, char *, targc + 2);
+      targv[targc+0] = strcreate ("-index");
+      targv[targc+1] = strcreate (IDXvec[0].name);
+      targc += 2;
+    }      
+
+    // call the remote client
+    int status = HostTableParallelOps (targc, targv, RESULT_FILE, 0, VERBOSE);
+    if (vec) free (vec);
+    
+    // free up targv
+    for (i = 0; i < targc; i++) {
+      free (targv[i]);
+    }
+    free (targv);
+
+    return status;
+  }
+
+  // get vectors corresponding to coordinates of interest
+  if (CoordsFile) {
+    // read RAvec, DECvec from coords file (1st 2 fields?)
+    Ninvec = 0;
+    invec = ReadVectorTableFITS (CoordsFile, "COORDS", &Ninvec);
+    RAvec = invec[0];
+    DECvec = invec[1];
+  } else {
+    if ((RAvec  = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto help;
+    if ((DECvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) goto help;
+    // strip off RA & DEC
+    remove_argument (1, &argc, argv);
+    remove_argument (1, &argc, argv);
+  }
+  RADIUS = atof (argv[1]);
+  remove_argument (1, &argc, argv);
+
+  // parse the fields to be extracted and returned
+  fields = dbCmdlineFields (argc, argv, DVO_TABLE_MEASURE, &last, &Nfields);
+  if (fields == NULL) goto help;
+  if ((Nfields == 0) || (last != argc)) {
+    dbFreeFields (fields, Nfields);
+    dvo_catalog_free (&catalog);
+    goto help;
+  }
+
+  // load image data if needed (for fields listed below)
+  int loadImages = FALSE;
+  int mosaicMode = FALSE;
+  for (i = 0; !loadImages && (i < Nfields); i++) {
+    if (!MEASURE_HAS_XCCD) {
+      // I'm keeping this code because it gives a way of handling dvo dbs that don't have
+      // measure.xccd if we need it
+      if (fields[i].ID == MEAS_XCCD) loadImages = TRUE;
+      if (fields[i].ID == MEAS_YCCD) loadImages = TRUE;
+    }
+    if (fields[i].ID == MEAS_XMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_YMOSAIC) 	    loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_EXTERN_ID)     loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_FLAT)          loadImages = mosaicMode = TRUE;
+    if (fields[i].ID == MEAS_CENTER_OFFSET) loadImages = mosaicMode = TRUE;
+  }
+
+  // use the whole sky (since we select random points around the sky)
+  SkyRegionSelection selection;
+  selection.useDisplay = FALSE;
+  selection.useSkyregion = FALSE;
+  if (loadImages && !SetImageSelection (mosaicMode, &selection)) goto escape;
+
+  /* load regions which contain all supplied RA,DEC coordinates */
+  if ((skylist = SelectRegionsByCoordVectors (RAvec, DECvec)) == NULL) goto escape;
+
+  /* create output storage vectors */
+  Npts = 0;
+  NPTS = 1000;
+  ALLOCATE (values, dbValue, Nfields);
+  ALLOCATE (vec, Vector *, Nfields);
+  for (i = 0; i < Nfields; i++) {
+    if (ISNUM(fields[i].name[0])) {
+      sprintf (name, "v_%s", fields[i].name);
+    } else {
+      sprintf (name, "%s", fields[i].name);
+    }
+    if ((vec[i] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) goto escape;
+    ResetVector (vec[i], fields[i].type, NPTS);
+  }
+  if (IDXvec) {
+    ResetVector (IDXvec, OPIHI_INT, NPTS);
+  }
+
+  // vectors to track recovered values
+  int Nelem = RAvec->Nelements;
+  ALLOCATE (index, off_t, Nelem);
+
+  // grab data from all selected sky regions
+  Signal = signal (SIGINT, handle_interrupt);
+  interrupt = FALSE;
+  for (i = 0; (i < skylist[0].Nregions) && !interrupt; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], HOST_ID)) continue;
+
+    /* lock, load, unlock catalog */
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", HOSTDIR, skylist[0].regions[i]->name);
+    catalog.filename = HOST_ID ? hostfile : skylist[0].filename[i];
+    catalog.catflags = LOAD_AVES | LOAD_SECF | LOAD_MEAS;
+    catalog.Nsecfilt = Nsecfilt;
+
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
+      
+    // an error exit status here is a significant error
+    if (!dvo_catalog_open (&catalog, NULL, FALSE, "r")) {
+      gprint (GP_ERR, "ERROR: failure to open catalog file %s\n", catalog.filename);
+      exit (2);
+    }
+    dvo_catalog_unlock (&catalog);  /// we can unlock here since this is read-only (do not block other access)
+    if (catalog.Naverage == 0) {
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    // returns the matches to AVERAGE objects; what do we do for each MEASURE?
+    find_matches_by_vectors (skylist[0].regions[i], &catalog, RAvec, DECvec, RADIUS, index);
+
+    for (j = 0; (j < Nelem) && !interrupt; j++) {
+      Ncat = index[j];
+
+      if (Ncat == -1) continue;
+      if (Ncat == -2) continue;
+
+      m = catalog.average[Ncat].measureOffset;
+
+      dbExtractMeasuresInitAve (); // reset counters for saved fields (costs very little)
+
+      for (k = 0; (k < catalog.average[Ncat].Nmeasure); k++, m++) {
+	if (catalog.measure[m].averef != Ncat) {
+	  gprint (GP_ERR, "ERROR: inconsistent measure->average link.  Unsorted database?\n");
+	  goto escape;
+	}
+
+	// extract the relevant values for this measurement
+	dbExtractMeasuresInitMeas (); // reset counters for saved fields  (costs very little
+	for (n = 0; n < Nfields; n++) {
+	  values[n] = dbExtractMeasures (&catalog.average[Ncat], &catalog.secfilt[Ncat*Nsecfilt], &catalog.measure[m], &fields[n]);
+	}
+
+	// XXX if we are allowed to return more rows than the supplied RA,DEC we will need to create an output RA,DEC
+	for (n = 0; n < Nfields; n++) {
+	  if (vec[n][0].type == OPIHI_FLT) {
+	    vec[n][0].elements.Flt[Npts] = values[n].Flt;
+	  } else {
+	    vec[n][0].elements.Int[Npts] = values[n].Int;
+	  }
+	}
+	if (IDXvec) {
+	  IDXvec[0].elements.Int[Npts] = j;
+	}
+
+	// extend length of output vectors
+	Npts++;
+	if (Npts >= NPTS) {
+	  NPTS += 2000;
+	  for (n = 0; n < Nfields; n++) {
+	    REALLOCATE (vec[n][0].elements.Flt, opihi_flt, NPTS);
+	  }
+	  if (IDXvec) {
+	    REALLOCATE (IDXvec[0].elements.Int, opihi_int, NPTS);
+	  }
+	}
+      }
+    }
+
+    dvo_catalog_free (&catalog);
+  }
+  signal (SIGINT, Signal);
+  interrupt = FALSE;
+
+  for (n = 0; n < Nfields; n++) {
+    ResetVector (vec[n], fields[n].type, Npts);
+  }
+  if (IDXvec) {
+    ResetVector (IDXvec, IDXvec->type, Npts);
+  }
+
+  // write vectors to a table (this is used by parallel dvo operations, but can be used elsewhere)
+  // only write the fields which were in a valid catalog
+  if (RESULT_FILE) {
+    if (IDXvec) {
+      // extend the array by one to hold index array
+      Nfields ++;
+      REALLOCATE (vec, Vector *, Nfields);
+      vec[Nfields-1] = IDXvec;
+    }
+    int status = WriteVectorTableFITS (RESULT_FILE, "RESULT", vec, Nfields, FALSE, NULL);
+    if (!status) goto escape;
+  }
+
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (TRUE);
+
+ escape:
+  if (vec) free (vec);
+  if (values) free (values);
+  if (invec) FreeVectorArray (invec, Ninvec);
+  dbFreeFields (fields, Nfields);
+  SkyListFree (skylist);
+  return (FALSE);
+
+ help:
+  gprint (GP_ERR, "USAGE: mmatch (RA) (DEC) (RADIUS) field[,field,field...] [-index index]\n");
+  gprint (GP_ERR, "   OR: mmatch -coords (filename.fits) (RADIUS) field[,field,field...] [-index index]\n");
+
+  if ((argc > N + 1) && !strcasecmp (argv[N+1], "fields")) {
+    gprint (GP_ERR, "  RA : right ascension (J2000) [degrees]\n");
+    gprint (GP_ERR, "  DEC : declination [degrees]\n");
+    return (FALSE);
+  }
+  gprint (GP_ERR, " mmatch --help fields : for a complete listing of allowed fields\n");
+  return (FALSE);
+}
Index: branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/test/mmatch.sh
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/test/mmatch.sh	(revision 34468)
+++ branches/eam_branches/ipp-20120905/Ohana/src/opihi/dvo/test/mmatch.sh	(revision 34468)
@@ -0,0 +1,36 @@
+
+macro test
+
+  #$Ro = 185.382364
+  #$Do =  34.697608
+  #catdir /data/pikake.1/eugene/dvodist.20120514/dvodist/catdir.syn.test/
+  $Gname = g_SYNTH
+  $Rname = r_SYNTH
+
+  $Ro = 2.59
+  $Do = 1.23
+  catdir /data/pikake.0/eugene/src/ipp-dev/Ohana/src/dvomerge/test/catdir.merge
+  $Gname = g
+  $Rname = r
+
+  skyregion {$Ro - 0.3} {$Ro + 0.3} {$Do - 0.3} {$Do + 0.3}
+  avextract ra dec $Gname $Rname
+  subset R = ra  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset D = dec if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset g_ave = $Gname  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  subset r_ave = $Rname  if (ra > $Ro - 0.1) && (ra > $Ro + 0.1) && (dec > $Do - 0.1) && (dec < $Do + 0.1)
+  vectors 
+  mmatch -v -parallel R D 1.0 RA DEC MAG PHOTCODE -index index
+
+  reindex g_ave_match = g_ave using index
+  reindex r_ave_match = r_ave using index
+
+  set dg = g_ave_match - MAG
+  set dr = r_ave_match - MAG
+  
+  subset  g_match = MAG if (PHOTCODE == 3001)
+  subset dg_match = dg if (PHOTCODE == 3001)
+
+  subset dr_match = dr if (PHOTCODE == 3002)
+  subset  r_match = MAG if (PHOTCODE == 3002)
+end
Index: branches/eam_branches/ipp-20120905/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/relastro/src/UpdateObjects.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/relastro/src/UpdateObjects.c	(revision 34468)
@@ -113,5 +113,5 @@
 	}
 
-	//outlier rejection
+	// outlier rejection
 	if (FlagOutlier && (measure[k].dbFlags & ID_MEAS_POOR_ASTROM)) {
 	  measure[k].dbFlags &= ~ID_MEAS_USED_OBJ;
Index: branches/eam_branches/ipp-20120905/Ohana/src/relphot/src/StarOps.c
===================================================================
--- branches/eam_branches/ipp-20120905/Ohana/src/relphot/src/StarOps.c	(revision 34467)
+++ branches/eam_branches/ipp-20120905/Ohana/src/relphot/src/StarOps.c	(revision 34468)
@@ -651,5 +651,5 @@
 
 	  // get the zero point for the selected image
-	  float zp = Mcal + Mmos + Mgrid + PhotZeroPoint (&catalog[Nc].measure[m], &catalog[Nc].average[j], &catalog[Nc].secfilt[j*Nsecfilt]);
+	  float zp = PhotZeroPoint (&catalog[Nc].measure[m], &catalog[Nc].average[j], &catalog[Nc].secfilt[j*Nsecfilt]) - (Mcal + Mmos + Mgrid);
 
 	  // flux_cgs : erg sec^1 cm^-2 Hz^-1
