Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/GetFileMode.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/GetFileMode.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/GetFileMode.c	(revision 36680)
@@ -12,5 +12,5 @@
   gfits_scan_alt (header, "SIMPLE", "%t", 1, &simple);
   int haveNaxis = gfits_scan (header, "NAXIS",  "%d", 1, &Naxis);
-  int haveCTYPE = gfits_scan (header, "CTYPE1", "%s", 1, ctype);
+  int haveCTYPE = gfits_scan (header, "CTYPE2", "%s", 1, ctype);
 
   gfits_scan_alt (header, "EXTEND", "%t", 1, &extend);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/LoadDataPMM.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/LoadDataPMM.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/LoadDataPMM.c	(revision 36680)
@@ -193,5 +193,5 @@
 
     // XXX for now, we define a totally fake coordinate system centered on the plate center
-    strcpy (image[0].coords.ctype, "RA---TAN");
+    strcpy (image[0].coords.ctype, "DEC--TAN");
     
     image[0].coords.crval1  = pmm_get_ra (RA);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/ReadStarsSDSS.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/ReadStarsSDSS.c	(revision 36680)
@@ -201,5 +201,5 @@
     
     // XXX for now, we define a totally fake coordinate system centered on the first listed star
-    strcpy (images[N].coords.ctype, "RA---TAN");
+    strcpy (images[N].coords.ctype, "DEC--TAN");
     
     images[N].coords.crval1 = stars[0].average.R;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/fakeimage.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/fakeimage.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/fakeimage.c	(revision 36680)
@@ -34,5 +34,5 @@
 
   /* create a mosaic distortion structure */
-  strcpy (MOSAIC.ctype, "RA---DIS");
+  strcpy (MOSAIC.ctype, "DEC--DIS");
   MOSAIC.crval1 = FAKE_RA;
   MOSAIC.crval2 = FAKE_DEC;
@@ -86,5 +86,5 @@
     strcpy (image[i+1].name, name);
 
-    strcpy (image[i+1].coords.ctype, "RA---WRP");
+    strcpy (image[i+1].coords.ctype, "DEC--WRP");
     
     image[i+1].coords.crval1 = dX*pixscale;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches.c	(revision 36680)
@@ -65,5 +65,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   /* build spatial index (RA sort) referencing input array sequence */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest.c	(revision 36680)
@@ -64,5 +64,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   /* build spatial index (RA sort) referencing input array sequence */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest_refstars.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest_refstars.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_closest_refstars.c	(revision 36680)
@@ -59,5 +59,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 0;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
   
   /* build spatial index (RA sort) referencing input array sequence */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_refstars.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_refstars.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/find_matches_refstars.c	(revision 36680)
@@ -57,5 +57,5 @@
   tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
-  strcpy (tcoords.ctype, "RA---ZEA");
+  strcpy (tcoords.ctype, "DEC--ZEA");
   tcoords.Npolyterms = 0;
   
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/findskycell.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/findskycell.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/findskycell.c	(revision 36680)
@@ -12,8 +12,10 @@
 // in an even more specific case, RA[i,zone] = RA_origin[zone] + RA_offset[zone]
 
-enum {TREE_NONE, TREE_MAKE, TREE_USE};
+enum {TREE_NONE, TREE_MAKE, TREE_LOCAL, TREE_USE};
+enum {REGION_NONE, REGION_USER, REGION_MIN, REGION_MAX};
 
 void usage (void) {
   fprintf (stderr, "USAGE: findcell -mktree (tree) (catdir) [-nx Nx] [-ny Ny]\n");
+  fprintf (stderr, "USAGE: findcell -mklocal (file) (catdir) [-nx Nx] [-ny Ny]\n");
   fprintf (stderr, "USAGE: findcell -tree (tree) (datafile)\n");
   fprintf (stderr, "   (datafile) should contain a list of RA,DEC pairs\n");
@@ -22,4 +24,5 @@
 
 int mktree (char *treefile, char *catdir);
+int mklocal (char *treefile, char *catdir);
 int apply_tree (char *treefile, char *datafile);
 
@@ -28,4 +31,16 @@
 int NY_SUB = 1;
 
+double R_MIN = 0.0;
+double R_MAX = 0.0;
+double D_MIN = 0.0;
+double D_MAX = 0.0;
+int REGION_OPTION = REGION_NONE;
+
+char *BASENAME = NULL;
+int projectIDoff = -1;
+int skycellIDoff = -1;
+
+int APPEND = FALSE;
+
 int main (int argc, char **argv) {
 
@@ -42,5 +57,4 @@
   if (get_argument (argc, argv, "-h")) usage ();
 
-  NX_SUB = NY_SUB = 1;
   if ((N = get_argument (argc, argv, "-nx"))) {
     remove_argument (N, &argc, argv);
@@ -55,8 +69,55 @@
 
   /* pixel scale (arcsec/pixel) */
-  SCALE = 1.0;
   if ((N = get_argument (argc, argv, "-scale"))) {
     remove_argument (N, &argc, argv);
     SCALE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* pixel scale (arcsec/pixel) */
+  if ((N = get_argument (argc, argv, "-append"))) {
+    remove_argument (N, &argc, argv);
+    APPEND = TRUE;
+  }
+
+  // user-specified region
+  R_MIN = NAN;
+  R_MAX = NAN;
+  D_MIN = NAN;
+  D_MAX = NAN;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    if (N > argc - 4) {
+      fprintf (stderr, "USAGE: -region requires 4 arguments (Rmin Rmax Dmin Dmax)\n");
+      exit (1);
+    }
+    R_MIN = atof (argv[N]); remove_argument (N, &argc, argv);
+    R_MAX = atof (argv[N]); remove_argument (N, &argc, argv);
+    D_MIN = atof (argv[N]); remove_argument (N, &argc, argv);
+    D_MAX = atof (argv[N]); remove_argument (N, &argc, argv);
+    REGION_OPTION = REGION_USER;
+  }
+
+  if ((N = get_argument (argc, argv, "-region-min"))) {
+    remove_argument (N, &argc, argv);
+    REGION_OPTION = REGION_MIN;
+  }
+
+  if ((N = get_argument (argc, argv, "-region-max"))) {
+    remove_argument (N, &argc, argv);
+    REGION_OPTION = REGION_MAX;
+  }
+
+  if ((N = get_argument (argc, argv, "-basename"))) {
+    remove_argument (N, &argc, argv);
+    if (N > argc - 3) {
+      fprintf (stderr, "USAGE: -basename (name) (proj_offset) (skycell_offset)\n");
+      exit (1);
+    }
+    BASENAME = strcreate  (argv[N]);
+    remove_argument (N, &argc, argv);
+    projectIDoff = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+    skycellIDoff = atoi(argv[N]);
     remove_argument (N, &argc, argv);
   }
@@ -70,4 +131,10 @@
     remove_argument (N, &argc, argv);
   }
+  if ((N = get_argument (argc, argv, "-mklocal"))) {
+    MODE = TREE_LOCAL;
+    remove_argument (N, &argc, argv);
+    treefile = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
   if ((N = get_argument (argc, argv, "-tree"))) {
     MODE = TREE_USE;
@@ -83,4 +150,9 @@
   if (MODE == TREE_MAKE) {
     mktree (treefile, argv[1]);
+    exit (0);
+  }
+
+  if (MODE == TREE_LOCAL) {
+    mklocal (treefile, argv[1]);
     exit (0);
   }
@@ -348,5 +420,29 @@
   MARKTIME("-- test %d pts: %f sec\n", Npts, dtime);
 
-  BoundaryTreeSave (treefile, &tree);
+  if (APPEND) {
+    int Ntess = 0;
+    TessellationTable *tess = TessellationTableLoad (treefile, &Ntess);
+    REALLOCATE (tess, TessellationTable, Ntess + 1);
+    TessellationTableInit (&tess[Ntess], 1);
+    tess[Ntess].tree = &tree;
+    tess[Ntess].type = TESS_RINGS;
+    tess[Ntess].Rmin =   0;
+    tess[Ntess].Rmax = 360;
+    tess[Ntess].Dmin = -90;
+    tess[Ntess].Dmax = +90;
+
+    if (BASENAME) {
+      tess[Ntess].Nbasename = strlen(BASENAME);
+      tess[Ntess].basename = strcreate(BASENAME);
+      tess[Ntess].projectIDoff = projectIDoff;
+      tess[Ntess].skycellIDoff = skycellIDoff;
+    }
+      
+    // add basename an related here...
+    Ntess ++;
+    TessellationTableSave (treefile, tess, Ntess);
+  } else {
+    BoundaryTreeSave (treefile, &tree);
+  }
 
   return TRUE;
@@ -354,4 +450,36 @@
 
 int apply_tree (char *treefile, char *datafile) {
+
+  int Ntess = 0;
+  TessellationTable *tess = TessellationTableLoad (treefile, &Ntess);
+  if (!tess) {
+    fprintf (stderr, "error loading tessellation table file %s\n", treefile);
+    exit (2);
+  }
+
+  FILE *f = fopen (datafile, "r");
+  if (!f) {
+    fprintf (stderr, "error opening data file %s\n", datafile);
+    exit (3);
+  }
+
+  double ra, dec;
+  int Nvalue = 0;
+  while ((Nvalue = fscanf (f, "%lf %lf", &ra, &dec)) != EOF) {
+
+    int tessID, projID, skycellID;
+
+    if (!TessellationPrimaryCellIDs (tess, Ntess, &tessID, &projID, &skycellID, ra, dec)) {
+      fprintf (stderr, "error finding cell for %f,%f\n", ra, dec);
+      continue;
+    }
+
+    fprintf (stdout, "%10.6f %10.6f : %2d  %04d %03d : %s\n", ra, dec, tessID, projID, skycellID, tess[tessID].basename);
+  }
+
+  exit (0);
+}
+
+int apply_tree_old (char *treefile, char *datafile) {
 
   BoundaryTree *tree = BoundaryTreeLoad (treefile);
@@ -405,2 +533,128 @@
 }
 
+// a given tess is defined by a catdir (more than one per catdir?)
+// this function takes a catdir and generates an extension for a tess file for a local tess.
+
+/*
+
+  LOCAL projections are defined by single projection cells.  should I be supplying the info on
+  the cmd line or figure out the bounds from the catdir?  
+
+ */
+
+int mklocal (char *treefile, char *catdir) {
+
+  int i, j, status;
+  FITS_DB db;
+  Image *image;
+  off_t Nimage;
+  double x, y, ra, dec;
+
+  if (REGION_OPTION == REGION_NONE) {
+    fprintf (stderr, "ERROR: need to define bounding region (-region Rmin Rmax Dmin Dmax | -region-min | -region-max)\n");
+    exit (2);
+  }
+
+  if (!BASENAME) {
+    fprintf (stderr, "ERROR: need to define BASENAME -basename (name) (proj_offset) (skycell_offset)\n");
+    exit (2);
+  }
+
+  char imagefile[DVO_MAX_PATH];
+  snprintf (imagefile, DVO_MAX_PATH, "%s/Images.dat", catdir);
+
+  status = dvo_image_lock (&db, imagefile, 2.0, LCK_XCLD);
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  /* load or create the image table */
+  if (db.dbstate == LCK_EMPTY) Shutdown ("can't read image catalog %s", db.filename);
+
+  if (!dvo_image_load (&db, TRUE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+
+  // convert database table to internal structure (binary to Image)
+  // 'image' points to the same memory as db->ftable->buffer
+  image = gfits_table_get_Image (&db.ftable, &Nimage, &db.swapped);
+  if (!image) {
+    fprintf (stderr, "ERROR: failed to read images\n");
+    exit (2);
+  }
+  
+  // generate an empty BoundaryTree
+  TessellationTable *tess = NULL;
+
+  int NtessDisk = 0;
+  if (APPEND) {
+    tess = TessellationTableLoad (treefile, &NtessDisk);
+    REALLOCATE (tess, TessellationTable, Nimage + NtessDisk);
+  } else {
+    ALLOCATE (tess, TessellationTable, Nimage + NtessDisk);
+  }
+
+  TessellationTableInit (&tess[NtessDisk], Nimage);
+
+  // find the RA,DEC of the image centers & assign to cells
+  int Ntess = NtessDisk;
+  for (i = 0; i < Nimage; i++) {
+    // user supplied values, do not try to derive from Image.dat
+    tess[Ntess].NX_SUB = NX_SUB;
+    tess[Ntess].NY_SUB = NY_SUB;
+    tess[Ntess].dPix = SCALE/3600.0;
+
+    x = 0.5*image[i].NX;
+    y = 0.5*image[i].NY;
+    XY_to_RD (&ra, &dec, x, y, &image[i].coords);
+
+    tess[Ntess].Ro  = ra;
+    tess[Ntess].Do  = dec;
+    tess[Ntess].Xo  = x;
+    tess[Ntess].Yo  = y;
+    tess[Ntess].dX  = image[i].NX / NX_SUB;
+    tess[Ntess].dY  = image[i].NY / NY_SUB;
+
+    // find the minimum or maximum containing region
+    if ((REGION_OPTION == REGION_MIN) || (REGION_OPTION == REGION_MAX)) {
+      // XXX short cut for now : if the projection cell bounds the equator or 0,360 boundary, this will fail:
+      double R[4], D[4];
+      XY_to_RD (&R[0], &D[0],           0,           0, &image[i].coords);
+      XY_to_RD (&R[1], &D[1], image[i].NX,           0, &image[i].coords);
+      XY_to_RD (&R[2], &D[2],           0, image[i].NY, &image[i].coords);
+      XY_to_RD (&R[3], &D[3], image[i].NX, image[i].NY, &image[i].coords);
+
+      if (REGION_OPTION == REGION_MIN) { 
+	for (j = 0; j < 4; j++) {
+	  R_MIN = (R[j] < ra)  ? (isfinite(R_MIN) ? MAX(R_MIN, R[j]) : R[j]) : R_MIN;
+	  R_MAX = (R[j] > ra)  ? (isfinite(R_MAX) ? MIN(R_MAX, R[j]) : R[j]) : R_MAX;
+	  D_MIN = (D[j] < dec) ? (isfinite(D_MIN) ? MAX(D_MIN, D[j]) : D[j]) : D_MIN;
+	  D_MAX = (D[j] > dec) ? (isfinite(D_MAX) ? MIN(D_MAX, D[j]) : D[j]) : D_MAX;
+	}
+      } else {
+	for (j = 0; j < 4; j++) {
+	  R_MIN = (R[j] < ra)  ? (isfinite(R_MIN) ? MIN(R_MIN, R[j]) : R[j]) : R_MIN;
+	  R_MAX = (R[j] > ra)  ? (isfinite(R_MAX) ? MAX(R_MAX, R[j]) : R[j]) : R_MAX;
+	  D_MIN = (D[j] < dec) ? (isfinite(D_MIN) ? MIN(D_MIN, D[j]) : D[j]) : D_MIN;
+	  D_MAX = (D[j] > dec) ? (isfinite(D_MAX) ? MAX(D_MAX, D[j]) : D[j]) : D_MAX;
+	}
+      }
+    }
+    tess[Ntess].Rmin = R_MIN;
+    tess[Ntess].Rmax = R_MAX;
+    tess[Ntess].Dmin = D_MIN;
+    tess[Ntess].Dmax = D_MAX;
+
+    tess[Ntess].type = TESS_LOCAL;
+
+    // XXX I don't really want to do the work of discovering the rule...
+    tess[Ntess].Nbasename = strlen(BASENAME);
+    tess[Ntess].basename = strcreate(BASENAME);
+
+    tess[Ntess].projectIDoff = projectIDoff;
+    tess[Ntess].skycellIDoff = skycellIDoff;
+
+    Ntess ++;
+  }
+
+  TessellationTableSave (treefile, tess, Ntess);
+
+  return TRUE;
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/loadsupercos_plates.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/loadsupercos_plates.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/loadsupercos_plates.c	(revision 36680)
@@ -118,5 +118,5 @@
 
     // for now, we define a fake coordinate system based on the boresite center
-    strcpy (image[Nimage].coords.ctype, "RA---TAN");
+    strcpy (image[Nimage].coords.ctype, "DEC--TAN");
     
     image[Nimage].coords.crval1 = RAo;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/sky_tessalation.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/addstar/src/sky_tessalation.c	(revision 36680)
@@ -1106,5 +1106,5 @@
   refcoords[0].Npolyterms = 0;
   memset (refcoords[0].polyterms, 0, 14*sizeof(float));
-  strcpy (refcoords[0].ctype, "RA---TAN");
+  strcpy (refcoords[0].ctype, "DEC--TAN");
   return (TRUE);
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c	(revision 36680)
@@ -167,14 +167,22 @@
   off_t i, oldID, newID;
 
+  if (!IDmap->old) {
+    fprintf (stderr, "input database has image IDs, but no Image table\n");
+    return FALSE;
+  }
+
+  off_t lastID = IDmap->old[IDmap->Nmap-1];
+
   for (i = 0; i < catalog[0].Nmeasure; i++) {
     oldID = catalog[0].measure[i].imageID;
     if (oldID == 0) continue;
 
-    if (!IDmap->old) {
-      fprintf (stderr, "input database has image IDs, but no Image table\n");
-    }
-
     newID = dvo_map_image_ID (IDmap, oldID);
     if (newID == 0) {
+      if (oldID > lastID) {
+	fprintf (stderr, "problem with image IDs : input out of range\n");
+	fprintf (stderr, "old ID: "OFF_T_FMT", last ID: "OFF_T_FMT"\n", oldID, lastID);
+	exit (2);
+      }
       if (!IDmap->notFound[oldID]) {
 	fprintf (stderr, "cannot find image ID "OFF_T_FMT"\n",  oldID);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate.c	(revision 36680)
@@ -111,5 +111,5 @@
   SetPhotcodeTable(NULL);
 
-  dvomergeUpdate_catalogs (input, output, outsky, inlist, NsecfiltInput, NsecfiltOutput, &IDmap, secfiltMap);
+  int status = dvomergeUpdate_catalogs (input, output, outsky, inlist, NsecfiltInput, NsecfiltOutput, &IDmap, secfiltMap);
 
   // save the output sky table copy
@@ -123,6 +123,11 @@
   gettimeofday (&stop, NULL);
   dtime = DTIME (stop, start);
+
+  if (!status) {
+    fprintf (stderr, "ERROR: elapsed time %9.4f sec\n", dtime);
+    exit (3);
+  }
+
   fprintf (stderr, "SUCCESS: elapsed time %9.4f sec\n", dtime);
-
   exit (0);
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate_catalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate_catalogs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvomergeUpdate_catalogs.c	(revision 36680)
@@ -140,4 +140,6 @@
     }
 
+    dvo_update_image_IDs (IDmap, &incatalog);
+
     // merge input into the appropriate output tables
     for (j = 0; j < outlist[0].Nregions; j++) {
@@ -156,10 +158,9 @@
       LoadCatalog (&outcatalog, outlist[0].regions[j], outcatalog.filename, "w", NsecfiltOutput);
 
-      dvo_update_image_IDs (IDmap, &incatalog);
       merge_catalogs_old (outlist[0].regions[j], &outcatalog, &incatalog, RADIUS, secfiltMap);
 
       outcatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
 
-      if (outstat[i].missed) {
+      if (outstat[j].missed) {
 	dmhObjectAdd (outstat[j].history, &outcatalog.header, inStats);
       }
@@ -248,4 +249,9 @@
     if (FORCE_MERGE)         { snprintf (tmpline, DVO_MAX_PATH, "%s -force-merge",     command); strcpy (command, tmpline); }
 
+    // add some config variables:
+    snprintf (tmpline, DVO_MAX_PATH, "%s -D CATMODE %s",    command, CATMODE);   strcpy (command, tmpline);
+    snprintf (tmpline, DVO_MAX_PATH, "%s -D CATFORMAT %s",  command, CATFORMAT); strcpy (command, tmpline);
+    snprintf (tmpline, DVO_MAX_PATH, "%s -D SKY_DEPTH %d",  command, SKY_DEPTH); strcpy (command, tmpline);
+
     fprintf (stderr, "command: %s\n", command);
 
@@ -275,5 +281,9 @@
   }
   if (!PARALLEL_MANUAL && !PARALLEL_SERIAL) {
-    HostTableWaitJobsGetIO (table, __FILE__, __LINE__, VERBOSE);
+    int status = HostTableWaitJobsGetIO (table, __FILE__, __LINE__, VERBOSE);
+    if (!status) {
+      fprintf (stderr, "error running one of the remote clients\n");
+      return status;
+    }
   }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_args.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_args.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_args.c	(revision 36680)
@@ -56,4 +56,8 @@
   }
 
+  /*** dvoverify -parallel (dvoverify_client, actually) ignores this argument
+       dvoverify should save a subset table of just the IDs, and dvoverify_client should load it
+       dvoverify -cpt should have CHECK_IMAGE_ID = F as default ***/
+       
   CHECK_IMAGE_ID = TRUE;
   if ((N = get_argument (*argc, argv, "-skip-image-ids"))) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_catalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_catalogs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_catalogs.c	(revision 36680)
@@ -143,5 +143,10 @@
       continue;
     }
-    fscanf (results, "%*s %d %*s %d", &NbadHost, &NNotSortedHost);
+    int Nresults = fscanf (results, "%*s %d %*s %d", &NbadHost, &NNotSortedHost);
+    if (Nresults != 2) {
+      fprintf (stderr, "warning: failure scanning results\n");
+      fclose (results);
+      continue;
+    }
     *Nbad += NbadHost;
     NNotSorted += NNotSortedHost;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_utils.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_utils.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/dvoverify_utils.c	(revision 36680)
@@ -302,4 +302,5 @@
     for (j = 0; j < catalog[0].average[i].Nmeasure; j++) {
       id = catalog[0].measure[m+j].imageID;
+      if (id == 0) continue; // detections from ref photcodes can (should) have unset image IDs
       if (id > maxID) {
 	Nfail ++;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/merge_catalogs_old.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/merge_catalogs_old.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvomerge/src/merge_catalogs_old.c	(revision 36680)
@@ -76,5 +76,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   if (VERBOSE) fprintf (stderr, "merging %s into %s\n", input[0].filename, output[0].filename);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/include/dvopsps.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/include/dvopsps.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/include/dvopsps.h	(revision 36680)
@@ -3,4 +3,5 @@
 # include <signal.h>
 # include "mysql.h"
+# include "limits.h"
 
 # define DVO_MAX_PATH 1024
@@ -38,4 +39,10 @@
 int          PARALLEL_MANUAL;
 int          PARALLEL_SERIAL;
+
+char        *TIME_START;
+char        *TIME_END;
+
+int          PHOTCODE_START;
+int          PHOTCODE_END;
 
 char        *SINGLE_CPT;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/initialize_dvopsps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/initialize_dvopsps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/initialize_dvopsps.c	(revision 36680)
@@ -2,5 +2,5 @@
 
 void usage_dvopsps () {
-  fprintf (stderr, "USAGE: dvopsps (mode) [dbinfo] (mode) [options]\n");
+  fprintf (stderr, "USAGE: dvopsps (mode) [dbinfo] [options]\n");
   fprintf (stderr, "    mysql database info is supplied with these:\n");
   fprintf (stderr, "    -dbhost : database host machine\n");
@@ -11,9 +11,22 @@
   fprintf (stderr, "    -v : verbose mode\n");
   fprintf (stderr, "    -region Rmin Rmax Dmin Dmax\n");
+  fprintf (stderr, "    -cpt n0000/0000.00 : limit to the named cpt file\n");
   fprintf (stderr, "    -parallel : run in parallel mode\n");
+  fprintf (stderr, "    -insert-remote : in parallel mode, the client sends the data to the dbhost\n");
+  fprintf (stderr, "    -time-start YYYY/MM/DD,hh:mm:ss : limit detections to >= this time\n");
+  fprintf (stderr, "    -time-end   YYYY/MM/DD,hh:mm:ss : limit detections to <  this time\n");
+
+  fprintf (stderr, "    -photcode-start NN : limit detections to >= this photcode number\n");
+  fprintf (stderr, "    -photcode-end   NN : limit detections to <  this photcode number\n");
+  fprintf (stderr, "\n");
+
+  fprintf (stderr, " (mode) is one of detections, objects, skytable\n");
+
+  fprintf (stderr, "\n");
   fprintf (stderr, "    -h     : this help list\n");
   fprintf (stderr, "    -help  : this help list\n");
   fprintf (stderr, "    --h    : this help list\n");
   fprintf (stderr, "    --help : this help list\n");
+
   fprintf (stderr, "    Note that the dvo db can be specified by -D CATDIR (directory)\n");
   exit (2);
@@ -110,4 +123,42 @@
   if ((N = get_argument (argc, argv, "-insert-remote"))) {
     SAVE_REMOTE = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+
+  TIME_START = NULL;
+  if ((N = get_argument (argc, argv, "-time-start"))) {
+    remove_argument (N, &argc, argv);
+    TIME_START = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    time_t TIME_START_SEC;
+    if (!ohana_str_to_time (TIME_START, &TIME_START_SEC)) {
+      fprintf (stderr, "error with starting time given by -time-start: %s\n", TIME_START);
+      exit (2);
+    }
+  }
+
+  TIME_END = NULL;
+  if ((N = get_argument (argc, argv, "-time-end"))) {
+    remove_argument (N, &argc, argv);
+    TIME_END = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    time_t TIME_END_SEC;
+    if (!ohana_str_to_time (TIME_END, &TIME_END_SEC)) {
+      fprintf (stderr, "error with starting time given by -time-end: %s\n", TIME_END);
+      exit (2);
+    }
+  }
+
+  PHOTCODE_START = 0;
+  if ((N = get_argument (argc, argv, "-photcode-start"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_START = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PHOTCODE_END = INT_MAX;
+  if ((N = get_argument (argc, argv, "-photcode-end"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_END = atoi(argv[N]);
     remove_argument (N, &argc, argv);
   }
@@ -260,4 +311,42 @@
   } 
 
+  TIME_START = NULL;
+  if ((N = get_argument (argc, argv, "-time-start"))) {
+    remove_argument (N, &argc, argv);
+    TIME_START = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    time_t TIME_START_SEC;
+    if (!ohana_str_to_time (TIME_START, &TIME_START_SEC)) {
+      fprintf (stderr, "error with starting time given by -time-start: %s\n", TIME_START);
+      exit (2);
+    }
+  }
+
+  TIME_END = NULL;
+  if ((N = get_argument (argc, argv, "-time-end"))) {
+    remove_argument (N, &argc, argv);
+    TIME_END = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    time_t TIME_END_SEC;
+    if (!ohana_str_to_time (TIME_END, &TIME_END_SEC)) {
+      fprintf (stderr, "error with starting time given by -time-end: %s\n", TIME_END);
+      exit (2);
+    }
+  }
+
+  PHOTCODE_START = 0;
+  if ((N = get_argument (argc, argv, "-photcode-start"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_START = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  PHOTCODE_END = INT_MAX;
+  if ((N = get_argument (argc, argv, "-photcode-end"))) {
+    remove_argument (N, &argc, argv);
+    PHOTCODE_END = atoi(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   VERBOSE = FALSE;
   if ((N = get_argument (argc, argv, "-v"))) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps.c	(revision 36680)
@@ -152,4 +152,11 @@
     if (VERBOSE)     { snprintf (tmpline, 1024, "%s -v",       command);                          strcpy (command, tmpline); }
     if (SAVE_REMOTE) { snprintf (tmpline, 1024, "%s -save %s", command, table->hosts[i].results); strcpy (command, tmpline); }
+
+    // some filters -- these are the detections we skip
+    if (TIME_START) { snprintf (tmpline, 1024, "%s -time-start %s", command, TIME_START); strcpy (command, tmpline); }
+    if (TIME_END)   { snprintf (tmpline, 1024, "%s -time-end %s",   command, TIME_END);   strcpy (command, tmpline); }
+    
+    if (PHOTCODE_START > 0)      { snprintf (tmpline, 1024, "%s -photcode-start %d", command, PHOTCODE_START); strcpy (command, tmpline); }
+    if (PHOTCODE_END <= INT_MAX) { snprintf (tmpline, 1024, "%s -photcode-end %d",   command, PHOTCODE_END);   strcpy (command, tmpline); }
 
     fprintf (stderr, "command: %s\n", command);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c	(revision 36680)
@@ -29,4 +29,9 @@
   }
 
+  time_t TIME_START_SEC = 0;
+  time_t TIME_END_SEC   = 0;
+  if (TIME_START) ohana_str_to_time (TIME_START, &TIME_START_SEC); // we validate this in the args.c
+  if (TIME_END)   ohana_str_to_time (TIME_END,   &TIME_END_SEC);   // we validate this in the args.c
+
   for (i = 0; i < catalog[0].Naverage; i++) {
 
@@ -40,4 +45,12 @@
       Average *average = &catalog->average[i];
       Measure *measure = &catalog->measure[m + j];
+
+      // some filters -- these are the detections we skip
+      if (TIME_START && (measure->t < TIME_START_SEC)) continue;
+      if (TIME_END   && (measure->t >=  TIME_END_SEC)) continue;
+
+      if (measure->photcode < PHOTCODE_START) continue;
+      if (measure->photcode >=  PHOTCODE_END) continue;
+
       PhotCode *code = GetPhotcodebyCode(measure->photcode);
 
@@ -87,4 +100,7 @@
 }
 
+time_t TIME_START_SEC = 0;
+time_t TIME_END_SEC   = INT_MAX;
+
 int insert_detections_dvopsps_catalog (Catalog *catalog, MYSQL *mysql) {
 
@@ -108,4 +124,7 @@
   int status = TRUE;
 
+  if (TIME_START) ohana_str_to_time (TIME_START, &TIME_START_SEC); // we validate this in the args.c
+  if (TIME_END)   ohana_str_to_time (TIME_END,   &TIME_END_SEC);   // we validate this in the args.c
+
   // NOTE for testing, just do a few objects
   if (!mysql) {
@@ -121,6 +140,15 @@
     for (j = 0; j < average[i].Nmeasure; j++) {
 
+      off_t Nmeas = m + j;
+
+      // some filters -- these are the detections we skip (not an error, just skipped)
+      if (TIME_START && (measure[Nmeas].t < TIME_START_SEC)) continue;
+      if (TIME_END   && (measure[Nmeas].t >=  TIME_END_SEC)) continue;
+  
+      if (measure[Nmeas].photcode < PHOTCODE_START) continue;
+      if (measure[Nmeas].photcode >=  PHOTCODE_END) continue;
+
       // XXX check return status
-      if (!insert_detections_mysql_value (&buffer, &average[i], &measure[m+j])) {
+      if (!insert_detections_mysql_value (&buffer, &average[i], &measure[Nmeas])) {
 	fprintf (stderr, "failure to insert detections in mysql\n");
 	status = FALSE;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/fixcat/src/gcatstats.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/fixcat/src/gcatstats.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/fixcat/src/gcatstats.c	(revision 36680)
@@ -52,5 +52,5 @@
   catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
   catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
-  strcpy (catstats[0].coords.ctype, "RA---TAN");
+  strcpy (catstats[0].coords.ctype, "DEC--TAN");
 
   X1 = catstats[0].X;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro/src/gstars.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro/src/gstars.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro/src/gstars.c	(revision 36680)
@@ -79,5 +79,5 @@
   if (!strcasecmp (ROUGH_ASTROMETRY, "config")) {
     /* default values for coords */
-    strcpy (coords[0].ctype, "RA---TAN");
+    strcpy (coords[0].ctype, "DEC--TAN");
     coords[0].pc1_1 = CCD_PC1_1; coords[0].pc1_2 = CCD_PC1_2;
     coords[0].pc2_1 = CCD_PC2_1; coords[0].pc2_2 = CCD_PC2_2;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/coordtest.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/coordtest.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/coordtest.c	(revision 36680)
@@ -85,5 +85,5 @@
   fit_eval ();
 
-  strcpy (coords.ctype, "RA---PLY");
+  strcpy (coords.ctype, "DEC--PLY");
   coords.crval1 = 0.0;
   coords.crval2 = 0.0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/gstars2.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/gstars2.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/gastro2/src/gstars2.c	(revision 36680)
@@ -28,5 +28,5 @@
 
   /* default values for coords */
-  strcpy (Target[0].coords.ctype, "RA---TAN");
+  strcpy (Target[0].coords.ctype, "DEC--TAN");
   Target[0].coords.pc1_1 = CCD_PC1_1; Target[0].coords.pc1_2 = CCD_PC1_2;
   Target[0].coords.pc2_1 = CCD_PC2_1; Target[0].coords.pc2_2 = CCD_PC2_2;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/GetFileMode.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/GetFileMode.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/GetFileMode.c	(revision 36680)
@@ -10,5 +10,5 @@
   gfits_scan_alt (header, "SIMPLE", "%t", 1, &simple);
   haveNaxis = gfits_scan (header, "NAXIS",  "%d", 1, &Naxis);
-  haveCTYPE = gfits_scan (header, "CTYPE1", "%s", 1, ctype);
+  haveCTYPE = gfits_scan (header, "CTYPE2", "%s", 1, ctype);
 
   gfits_scan_alt (header, "EXTEND", "%t", 1, &extend);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/MatchImages.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/MatchImages.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/MatchImages.c	(revision 36680)
@@ -36,5 +36,5 @@
     tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
     tcoords.Npolyterms = 1;
-    strcpy (tcoords.ctype, "RA---TAN");
+    strcpy (tcoords.ctype, "DEC--TAN");
     /* register so image->sky conversions below have correct mosaic */
   } else {
@@ -44,5 +44,5 @@
     tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
     tcoords.Npolyterms = 1;
-    strcpy (tcoords.ctype, "RA---TAN");
+    strcpy (tcoords.ctype, "DEC--TAN");
   }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/dvoImagesAtCoords.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/dvoImagesAtCoords.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/getstar/src/dvoImagesAtCoords.c	(revision 36680)
@@ -9,4 +9,5 @@
 static int makePoint(double ra, double dec, Point **pointsOut);
 static int ListImagesAtCoords (Image *dbImages, Point *points, int Npoints);
+static int readPointFromFile(FILE *f, Point *pt);
 
 int main (int argc, char **argv) {
@@ -15,5 +16,4 @@
   int status;
   Image *dbImages;
-  int Npoints;
   FITS_DB db;
 
@@ -22,11 +22,17 @@
   args_coords (argc, argv);
   
-  Point *points;
+  Point *points = NULL;
+  int readStdin = 0;
+  int Npoints = 0;
   if (coordsFile) {
-      Npoints = readPoints(coordsFile, &points);
+      if (strcmp(coordsFile, "-") == 0) {
+        readStdin = 1;
+      } else {
+        Npoints = readPoints(coordsFile, &points);
+      }
   } else {
       Npoints = makePoint(cmd_line_ra, cmd_line_dec, &points);
   }
-  if (!Npoints) {
+  if (!Npoints && !readStdin) {
     exit(1);
   }
@@ -61,10 +67,52 @@
   }
   
-  if (MatchCoords (dbImages, NdbImages, points, Npoints)) {
-    ListImagesAtCoords(dbImages, points, Npoints);
-    exit(0);
+  if (readStdin) {
+    Point pt;
+    memset(&pt, 0, sizeof(pt));
+    pt.Nmatches = 0;
+    ALLOCATE(pt.matches, Match, 20);
+    pt.arrayLength = 20;
+
+    int status;
+    while ((status = readPointFromFile(stdin, &pt))) {
+      if (status < 0) {
+        fprintf(stdout, "ERROR malformed input line\n");
+        exit(1);
+      }
+      if (MatchCoords (dbImages, NdbImages, &pt, 1)) {
+        ListImagesAtCoords(dbImages, &pt, 1);
+      }
+      fprintf(stdout, "DONE\n");
+      fflush(stdout);
+      pt.Nmatches = 0;
+    }
   } else {
-    exit(PSTAMP_NO_OVERLAP);
-  }
+    if (MatchCoords (dbImages, NdbImages, points, Npoints)) {
+      ListImagesAtCoords(dbImages, points, Npoints);
+    } else {
+      exit(PSTAMP_NO_OVERLAP);
+    }
+  }
+  exit(0);
+}
+
+static int readPointFromFile(FILE *f, Point *pt) {
+    char buf[80];
+    char *good = fgets(buf, 80, f);
+    if (!good) {
+        return 0;
+    }
+    // fprintf(stderr, "READ: %s\n", buf);
+    int Nread = sscanf(buf, "%d %lf %lf\n", &pt->id, &pt->ra, &pt->dec);
+    if (Nread == 3) {
+        // got one
+        return 1;
+    } else if (Nread == -1) {
+        // all done time to go
+        return 0;
+    } else {
+        // fprintf(stderr, "malformed input line: %d\n", Nread);
+        return -1;
+    }
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/include/constants.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/include/constants.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/include/constants.h	(revision 36680)
@@ -15,5 +15,5 @@
 // XXX for the moment, this is set to match the values in SetColorScale3D_CC
 # define NPIXELS_STATIC 128
-// # define NPIXELS_STATIC 1000
+// # define NPIXELS_STATIC 4600 -- if we want fullcolor to work, need this
 
 # define PAD1  3
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Graphs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Graphs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Graphs.c	(revision 36680)
@@ -64,5 +64,5 @@
   graph[0].data.coords.crpix2 = 0.0;
   graph[0].data.coords.cdelt1 = graph[0].data.coords.cdelt2 = 1.0;
-  strcpy (graph[0].data.coords.ctype, "RA---LIN");
+  strcpy (graph[0].data.coords.ctype, "DEC--LIN");
   strcpy (graph[0].data.axis, "2222");
   strcpy (graph[0].data.ticks, "2222");
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Image.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Image.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/Image.c	(revision 36680)
@@ -18,5 +18,5 @@
   channel->coords.crpix2 = 0.0;
   channel->coords.cdelt1 = channel->coords.cdelt2 = 1.0;
-  strcpy (channel->coords.ctype, "RA---LIN");
+  strcpy (channel->coords.ctype, "DEC--LIN");
   channel->coords.Npolyterms = 0;
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/bDrawObjects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/bDrawObjects.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/kapa2/src/bDrawObjects.c	(revision 36680)
@@ -315,4 +315,5 @@
 	D = scaleSize ? dz*z[i] : ds;
 	FillRectangle (buffer, sx, sy, 2*D, 2*D);
+	// plot range saturated by bDrawRectFill
       }
     }
@@ -335,4 +336,5 @@
 	D = scaleSize ? dz*z[i] : ds;
 	DrawRectangle (buffer, sx, sy, 2*D, 2*D);
+	// plot range saturated by bDrawRectOpen
       }
     }
@@ -356,4 +358,5 @@
 	DrawLine (buffer, sx - D, sy, sx + D, sy);
 	DrawLine (buffer, sx, sy - D, sx, sy + D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -377,4 +380,5 @@
 	DrawLine (buffer, sx + D, sy - D, sx - D, sy + D);
 	DrawLine (buffer, sx - D, sy - D, sx + D, sy + D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -398,4 +402,5 @@
 	// FillTriangle (buffer, sx - D, sy - 0.58*D, sx + D, sy - 0.58*D, sx, sy + 1.15*D);
 	FillTriangle (buffer, sx, sy + 0.58*D, D, -1.73*D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -418,4 +423,5 @@
 	D = scaleSize ? dz*z[i] : ds;
 	OpenTriangle (buffer, sx - D, sy + 0.58*D, sx + D, sy + 0.58*D, sx, sy - 1.15*D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -440,4 +446,5 @@
 	DrawLine (buffer, sx, sy, sx + D, sy - 0.58*D);
 	DrawLine (buffer, sx, sy, sx,     sy + 1.15*D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -460,4 +467,5 @@
 	D = scaleSize ? dz*z[i] : ds;
 	DrawCircle (buffer, sx, sy, D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -484,4 +492,5 @@
 	DrawLine (buffer, sx - 0.58*D, sy + 0.81*D, sx - 0.95*D, sy - 0.31*D);
 	DrawLine (buffer, sx - 0.95*D, sy - 0.31*D, sx + 0.00*D, sy - 1.00*D);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -509,4 +518,5 @@
 	DrawLine (buffer, sx + 0.50*D, sy - 0.87*D, sx - 0.50*D, sy - 0.87*D);
 	DrawLine (buffer, sx - 0.50*D, sy - 0.87*D, sx -      D, sy);
+	// out-of-range points skipped by bDrawPoint
       }
     }
@@ -529,4 +539,5 @@
 	D = scaleSize ? dz*z[i] : ds;
 	FillCircle (buffer, sx, sy, D);
+	// out-of-range points skipped by bDrawLineHorizontal
       }
     }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/Makefile	(revision 36680)
@@ -103,10 +103,11 @@
 $(SRC)/ImageMetadata.$(ARCH).o \
 $(SRC)/ImageOps.$(ARCH).o		\
+$(SRC)/RegionHostTable.$(ARCH).o		\
 $(SRC)/match_image.$(ARCH).o		\
 $(SRC)/db_utils.$(ARCH).o		\
 $(SRC)/convert.$(ARCH).o                \
 $(SRC)/HostTable.$(ARCH).o              \
-$(SRC)/BoundaryTree.$(ARCH).o
-
+$(SRC)/BoundaryTree.$(ARCH).o           \
+$(SRC)/TessellationTable.$(ARCH).o
 
 # $(SRC)/dvo_convert_panstarrs.$(ARCH).o 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvo.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvo.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvo.h	(revision 36680)
@@ -285,4 +285,46 @@
 } HostTable;
 
+// A RegionHost processes data for some region in parallel with other regions
+typedef struct RegionHostInfo {
+  double Rmin;	      // (Rmin,Rmax),(Dmin,Dmax) arehard RA,DEC boundaries of the 
+  double Rmax;	      // region for which each host is responsible.  A given host
+  double Dmin;	      // calibrates the images for which the fiducial point (center) 
+  double Dmax;	      // lands in the region, and all objects in the region
+
+  double RminCat;      // (RminCat,RmaxCat),(DminCat,DmaxCat) are the region for which 
+  double RmaxCat;      // the catalogs need to be loaded : this is the outer bounds
+  double DminCat;      // of the region containing all images completely
+  double DmaxCat;
+
+  char *hostname;
+
+  int hostID;		      // remove machine ID in SkyTable
+  int stdio[3]; 	      // fd's for communication with the remote host
+  int pid;		      // remote process ID
+  int status;
+  IOBuffer stdout;
+  IOBuffer stderr;
+
+  off_t Nimage;
+  off_t NIMAGE;
+  Image *image;
+  off_t *imseq;
+
+  int *neighbors;	      // list of neighbor index values
+  int Nneighbors;	      // number of neighbors
+  char isNeighbor;	      // TRUE if I am a neighbor to the current region host
+} RegionHostInfo;
+
+typedef struct {
+  double Rmin;
+  double Rmax;
+  double Dmin;
+  double Dmax;
+
+  int Nhosts;
+  RegionHostInfo *hosts;
+  short *index;
+} RegionHostTable;
+
 // special-case function:
 CMF_PS1_V2 *gfits_table_get_CMF_PS1_V1_Alt (FTable *ftable, off_t *Ndata, char *swapped);
@@ -308,4 +350,6 @@
 # define BOUNDARY_TREE_NAME_LENGTH 128
 
+// BoundaryTree is a structure to describe the 3pi RINGS skycell boundaries in terms of lines of constant (RA,DEC)
+// the structure is flexible for a variety of RINGS-like tessellations, but is not appropriate for the LOCAL style tess
 typedef struct {
   int FixedGridDEC;	      // is the DEC sequence linear?
@@ -342,13 +386,36 @@
 } BoundaryTree;
 
-// XXX DROP? // a reduced-subset structure for relastro
-// XXX DROP? typedef struct {
-// XXX DROP?   double         R;
-// XXX DROP?   double         D;
-// XXX DROP?   unsigned short Nmeasure;
-// XXX DROP?   int            measureOffset;
-// XXX DROP?   uint32_t       flags;
-// XXX DROP?   int            catID;
-// XXX DROP? } AverageTinyAstro;
+typedef enum { TESS_NONE, TESS_LOCAL, TESS_RINGS } TessType;
+
+// TessellationTable is a structure to describe the parameters of a set of "tessellations"
+// (these are not strictly tessellations but projection sets as only the non-local
+// versions can cover the full sky).  For LOCAL projection cells, the structure describes
+// the boundaries of a SINGLE projection cell with Nx * Ny skycells and includes some
+// basic parameters (not used by the fullsky, eg RINGS, tessellations)
+typedef struct {
+  double Rmin; // this tessellation is valid only for RA >= Rmin
+  double Rmax; // this tessellation is valid only for RA <  Rmax
+  double Dmin; // this tessellation is valid only for DEC >= Dmin
+  double Dmax; // this tessellation is valid only for DEC <  Dmax
+
+  double Xo;
+  double Yo;
+  double Ro;
+  double Do;
+  double dPix;
+  int dX;
+  int dY;
+
+  int NX_SUB;
+  int NY_SUB;
+
+  char *basename;
+  int Nbasename;
+  int projectIDoff;
+  int skycellIDoff;
+
+  TessType type; // 
+  BoundaryTree *tree;
+} TessellationTable;
 
 // a reduced-subset structure for relphot
@@ -360,4 +427,6 @@
   uint32_t       flags;
   int            catID;
+  int            objID;
+  int            nOwn;
 } AverageTiny;
 
@@ -385,5 +454,98 @@
   short          dYccd;
   short          dRsys;
+  char           myDet;
 } MeasureTiny;
+
+/** STRUCT DEFINITION **/
+typedef struct {
+  double           R;                    // RA (decimal degrees )
+  double           D;                    // DEC (decimal degrees )
+  float            dR;                   // RA error (arcsec)
+  float            dD;                   // DEC error (arcsec)
+  float            uR;                   // RA*cos(D) proper-motion (arcsec/year)
+  float            uD;                   // DEC proper-motion (arcsec/year)
+  float            duR;                  // RA*cos(D) p-m error (arcsec/year)
+  float            duD;                  // DEC p-m error (arcsec/year)
+  float            P;                    // parallax (arcsec)
+  float            dP;                   // parallax error (arcsec)
+  float            ChiSqAve;             // astrometry analysis chisq
+  float            ChiSqPM;              // astrometry analysis chisq
+  float            ChiSqPar;             // astrometry analysis chisq
+  int              Tmean;                // mean epoch (PM,PAR ref) (unix time seconds)
+  int              Trange;               // mean epoch (PM,PAR ref) (unix time seconds)
+  float            Xp;                   // unused
+  unsigned short   Npos;                 // number of detections used for astrometry
+  unsigned short   Nmeasure;             // number of psf measurements
+  unsigned short   Nmissing;             // number of missings
+  unsigned short   Nextend;              // number of extended measurements
+  uint32_t         measureOffset;        // offset to first psf measurement
+  uint32_t         missingOffset;        // offset to first missing obs
+  uint32_t         extendOffset;         // offset to first extended measurement
+  uint32_t         flags;                // average object flags (star; ghost; etc)
+  uint32_t         photFlagsUpper;       // upper bit of 2 bit summary of per-measure photflags
+  uint32_t         photFlagsLower;       // lower bit of 2 bit summary of per-measure photflags
+  unsigned int     objID;                // unique ID for object in table
+  unsigned int     catID;                // unique ID for table in which object was first realized
+  uint64_t         extID;                // external ID for object (eg PSPS objID)
+} Average_PS1_V4alt;
+
+Average_PS1_V4alt *gfits_table_get_Average_PS1_V4alt (FTable *table, off_t *Ndata, char *swapped);
+Average *Average_PS1_V4alt_ToInternal (Average_PS1_V4alt *in, off_t Nvalues);
+
+/** STRUCT DEFINITION **/
+typedef struct {
+  float            dR;                   // RA offset (arcsec)
+  float            dD;                   // DEC offset (arcsec)
+  float            M;                    // catalog mag (mag)
+  float            Mcal;                 // image cal mag (mag)
+  float            Map;                  // aperture mag (mag)
+  float            Mkron;                // kron magnitude (mag)
+  float            dMkron;               // kron magnitude error (mag)
+  float            dM;                   // mag error (mag)
+  float            dMcal;                // systematic calibration error (mag)
+  float            dt;                   // exposure time (2.5*log(exptime))
+  float            FluxPSF;              // flux from psf fit (counts/sec?)
+  float            dFluxPSF;             // error on psf flux (counts/sec?)
+  float            FluxKron;             // flux from kron ap (counts/sec?)
+  float            dFluxKron;            // error on kron flux (counts/sec?)
+  float            airmass;              // (airmass - 1) (airmass)
+  float            az;                   // telescope azimuth
+  float            Xccd;                 // X coord on chip (raw value) (pixels)
+  float            Yccd;                 // Y coord on chip (raw value) (pixels)
+  float            Sky;                  // local estimate of sky flux (counts/sec)
+  float            dSky;                 // local estimate of sky flux (counts/sec)
+  int              t;                    // time in seconds (UNIX)
+  unsigned int     averef;               // reference to average entry      
+  unsigned int     detID;                // detection ID
+  unsigned int     imageID;              // reference to DVO image ID
+  unsigned int     objID;                // unique ID for object in table
+  unsigned int     catID;                // unique ID for table in which object was first realized
+  uint64_t         extID;                // external ID (eg PSPS detID)
+  float            psfQF;                // psf coverage/quality factor
+  float            psfQFperf;            // psf coverage / quality factor (all mask bits)
+  float            psfChisq;             // psf fit chisq
+  int              psfNdof;              // psf degrees of freedom
+  int              psfNpix;              // psf number of pixels
+  float            crNsigma;             // Nsigma deviation towards CR
+  float            extNsigma;            // Nsigma deviation towards EXT
+  short            FWx;                  // object fwhm major axis (1/100 of pixels)
+  short            FWy;                  // object fwhm minor axis (1/100 of pixels )
+  short            theta;                // angle wrt ccd X dir ((0xffff/360) deg)
+  short            Mxx;                  // second moments in pixel coords (1/100 of pixels)
+  short            Mxy;                  // second moments in pixel coords (1/100 of pixels)
+  short            Myy;                  // second moments in pixel coords (1/100 of pixels)
+  unsigned short   t_msec;               // time fraction of second (milliseconds)
+  unsigned short   photcode;             // photcode
+  short            dXccd;                // X coord error on chip (1/100 of pixels)
+  short            dYccd;                // Y coord error on chip (1/100 of pixels)
+  short            dRsys;                // systematic error from astrom (1/100 of pixels)
+  short            posangle;             // position angle sky to chip ((0xffff/360) deg)
+  float            pltscale;             // plate scale (arcsec/pixel)
+  unsigned int     dbFlags;              // flags supplied by analysis in database
+  unsigned int     photFlags;            // flags supplied by photometry program
+} Measure_PS1_V4alt;
+
+Measure_PS1_V4alt *gfits_table_get_Measure_PS1_V4alt (FTable *table, off_t *Ndata, char *swapped);
+Measure *Measure_PS1_V4alt_ToInternal (Measure_PS1_V4alt *in, off_t Nvalues);
 
 /* a catalog contains this data */
@@ -438,4 +600,5 @@
   float *X;
   float *Y;
+  int   *nOwn; // relastro uses this to count owned detections per object
 
 } Catalog;
@@ -499,6 +662,4 @@
 float PhotInst (Measure *measure);
 float PhotCat (Measure *measure);
-float PhotAper (Measure *measure);
-float PhotKron (Measure *measure);
 float PhotSys (Measure *measure, Average *average, SecFilt *secfilt);
 float PhotRel (Measure *measure, Average *average, SecFilt *secfilt);
@@ -506,11 +667,24 @@
 float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt);
 float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotAveErr (PhotCode *code, Average *average, SecFilt *secfilt);
+
+float PhotAperInst (Measure *measure);
+float PhotAperCat (Measure *measure);
+float PhotAperSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotAperRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotAperCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotAperAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotAperRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+
+float PhotKronInst (Measure *measure);
+float PhotKronCat (Measure *measure);
+float PhotKronSys (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotKronRel (Measure *measure, Average *average, SecFilt *secfilt);
+float PhotKronCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code);
+float PhotKronAve (PhotCode *code, Average *average, SecFilt *secfilt);
+float PhotKronRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure);
+float PhotKronAveErr (PhotCode *code, Average *average, SecFilt *secfilt);
+
 float PhotXm (PhotCode *code, Average *average, SecFilt *secfilt);
-float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt);
-
-float PhotAperInst (Measure *measure);
-float PhotKronInst (Measure *measure);
-float PhotKronAve (PhotCode *code, Average *average, SecFilt *secfilt);
-float PhotKronAveErr (PhotCode *code, Average *average, SecFilt *secfilt);
 
 float PhotZeroPoint (Measure *measure, Average *average, SecFilt *secfilt);
@@ -696,4 +870,5 @@
 void sort_coords_index (double *X, double *Y, off_t *S, off_t N);
 void sort_coords_indexonly (double *X, double *Y, off_t *S, off_t N);
+void sort_IDs_indexonly (opihi_int *X, off_t *S, off_t N);
 void sort_regions (SkyRegion *region, off_t N);
 
@@ -716,8 +891,17 @@
 int free_tiny_values (Catalog *catalog);
 
+BoundaryTree *BoundaryTreeLoad(char *filename);
+BoundaryTree *BoundaryTreeRead(Header *headerPHU, Header *headerZone, FILE *f);
+
+int BoundaryTreeSave(char *filename, BoundaryTree *tree);
+int BoundaryTreeWrite(FILE *f, BoundaryTree *tree);
+
 int BoundaryTreeCellCoords (BoundaryTree *tree, int *zone, int *band, double ra, double dec);
-int BoundaryTreeSave(char *filename, BoundaryTree *tree);
-BoundaryTree *BoundaryTreeLoad(char *filename);
 int BoundaryTreeProjection (double *x, double *y, double r, double d, BoundaryTree *tree, int zone, int band);
+
+TessellationTable *TessellationTableLoad(char *filename, int *Ntess);
+int TessellationTableSave(char *filename, TessellationTable *tess, int Ntess);
+int TessellationPrimaryCellIDs (TessellationTable *tess, int Ntess, int *tessID, int *projID, int *skycellID, double ra, double dec);
+void TessellationTableInit (TessellationTable *tess, int Ntess);
 
 void dvo_average_init (Average *average);
@@ -727,3 +911,11 @@
 void dvo_measureT_init (MeasureTiny *measure);
 
+void InitRegionHosts (RegionHostInfo *hosts, int Nhosts, int NHOSTS);
+void FreeRegionHosts (RegionHostInfo *hosts, int Nhosts);
+void FreeRegionHostTable (RegionHostTable *table);
+RegionHostTable *RegionHostTableLoad (char *catdir, char *rootname);
+int RegionHostTableWaitJobs (RegionHostTable *regionHosts, char *file, int lineno);
+int RegionHostTableWaitJobsGetIO (RegionHostTable *regionHosts, char *file, int lineno, int VERBOSE);
+int RegionHostFindNeighbors (RegionHostTable *table, int Nhost);
+
 # endif // DVO_H
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvodb.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvodb.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/include/dvodb.h	(revision 36680)
@@ -23,31 +23,60 @@
 /* magnitude types */
 enum {MAG_NONE, 
-      MAG_INST, 
-      MAG_CAT, 
-      MAG_APER, 
-      MAG_APER_INST, 
-      MAG_KRON, 
-      MAG_KRON_INST, 
-      MAG_KRON_ERR, 
-      MAG_SYS, 
-      MAG_REL, 
-      MAG_CAL, 
+
+      // these magnitude types are from the measurement table (or derived therein)
+      // the following imply PSF magnitudes:
+      MAG_INST, // -2.5*log(DN) [ie, not DN/sec]
+      MAG_CAT,  // MAG_INST + 2.5*log(exptime) + C_lambda + K_lambda*(airmass - 1)
+      MAG_SYS,  // MAG_CAT + \sum_i A_i * (color_i - color_o) [color correction for measure photcode]
+      MAG_REL,  // MAG_SYS - Mcal [specific zero point for image]
+      MAG_CAL,  // MAG_REL + \sum_i A_i * (color_i - color_o) [color correction for average photcode]
+      MAG_ERR,  // error on PSF MAG
+
+      // the following imply APERTURE magnitudes:
+      MAG_APER_INST, // -2.5*log(DN) [ie, not DN/sec]
+      MAG_APER_CAT,  // MAG_APER_INST + 2.5*log(exptime) + C_lambda + K_lambda*(airmass - 1)
+      MAG_APER_SYS,  // MAG_APER_CAT + \sum_i A_i * (color_i - color_o) [color correction for measure photcode]
+      MAG_APER_REL,  // MAG_APER_SYS - Mcal [specific zero point for image]
+      MAG_APER_CAL,  // MAG_APER_REL + \sum_i A_i * (color_i - color_o) [color correction for average photcode]
+      MAG_APER_ERR,  // error on APER MAG (if known, else psf error)
+      MAG_APER,      // in mextract -> MAG_APER_REL, in avextract -> MAG_APER_AVE
+
+      // the following imply KRON magnitudes:
+      MAG_KRON_INST, // -2.5*log(DN) [ie, not DN/sec]
+      MAG_KRON_CAT,  // MAG_KRON_INST + 2.5*log(exptime) + C_lambda + K_lambda*(airmass - 1)
+      MAG_KRON_SYS,  // MAG_KRON_CAT + \sum_i A_i * (color_i - color_o) [color correction for measure photcode]
+      MAG_KRON_REL,  // MAG_KRON_SYS - Mcal [specific zero point for image]
+      MAG_KRON_CAL,  // MAG_KRON_REL + \sum_i A_i * (color_i - color_o) [color correction for average photcode]
+      MAG_KRON_ERR,  // error on KRON MAG
+      MAG_KRON,      // in mextract -> MAG_KRON_REL, in avextract -> MAG_KRON_AVE
+
+      // these magnitude types are from the average (secfilt) table:
+      // the following imply PSF magnitudes (or fluxes)
       MAG_AVE, 
       MAG_REF, 
-      MAG_ERR, 
       MAG_STDEV, 
       MAG_AVE_ERR, 
+      MAG_CHISQ,
+      MAG_20, 
+      MAG_80, 
+      MAG_FLUX_PSF,
+      MAG_FLUX_PSF_ERR,
+
+      // the following imply APERTURE magnitudes:
+      MAG_APER_AVE,
+      MAG_APER_REF,
+
+      // the following imply KRON magnitudes or fluxes:
+      MAG_KRON_AVE,
+      MAG_KRON_REF,
+      MAG_FLUX_KRON,
+      MAG_FLUX_KRON_ERR,
+
+      // other secfilt fields of interest
       MAG_PHOT_FLAGS, 
-      MAG_CHISQ,
       MAG_NCODE,
       MAG_NPHOT,
-      MAG_20, 
-      MAG_80, 
       MAG_UC_DIST, 
       MAG_STACK_DET_ID, 
-      MAG_FLUX_PSF,
-      MAG_FLUX_PSF_ERR,
-      MAG_FLUX_KRON,
-      MAG_FLUX_KRON_ERR,
 };
 
@@ -174,4 +203,7 @@
       AVE_TMEAN,
       AVE_TRANGE, 
+      AVE_PSF_QF,
+      AVE_PSF_QF_PERF,
+      AVE_STARGAL,
       AVE_Xp, 
       AVE_NMEAS, 
@@ -372,5 +404,5 @@
 
 // Some values used by code moved to libdvo from opihi.
-enum {OPIHI_FLT, OPIHI_INT};
+enum {OPIHI_NOTYPE, OPIHI_FLT, OPIHI_INT};
 #define opihi_flt double
 #define opihi_int int
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/BoundaryTree.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/BoundaryTree.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/BoundaryTree.c	(revision 36680)
@@ -1,10 +1,10 @@
 # include "dvo.h"
 
-# define GET_COLUMN_NEW(OUT,NAME,TYPE)					\
-  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+# define GET_COLUMN_NEW(HEADER,FTABLE,OUT,NAME,TYPE)				\
+  TYPE *OUT = gfits_get_bintable_column_data (HEADER, FTABLE, NAME, type, &Nrow, &Ncol); \
   myAssert (!strcmp(type, #TYPE), "wrong column type");
 
-# define GET_COLUMN_RAW(OUT,NAME,TYPE)					\
-  OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+# define GET_COLUMN_RAW(HEADER,FTABLE,OUT,NAME,TYPE)			\
+  OUT = gfits_get_bintable_column_data (HEADER, FTABLE, NAME, type, &Nrow, &Ncol); \
   myAssert (!strcmp(type, #TYPE), "wrong column type");
 
@@ -12,159 +12,182 @@
 
 BoundaryTree *BoundaryTreeLoad(char *filename) {
+
+  Header header;
+  Header theader;
+  Matrix matrix;
+
+  header.buffer = NULL;
+  matrix.buffer = NULL;
+  theader.buffer = NULL;
+  BoundaryTree *tree = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open boundary tree file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (DEBUG) fprintf (stderr, "can't read boundary tree header\n");
+    goto escape;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (DEBUG) fprintf (stderr, "can't read boundary tree matrix\n");
+    goto escape;
+  }
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    if (DEBUG) fprintf (stderr, "can't read boundary tree zone table header\n");
+    goto escape;
+  }
+
+  tree = BoundaryTreeRead (&header, &theader, f);
+
+escape:
+
+  gfits_free_header (&theader);
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  fclose (f);
+
+  return tree;
+}
+
+// assume we are pointing at the relevant table portion
+BoundaryTree *BoundaryTreeRead(Header *headerPHU, Header *headerZone, FILE *f) {
 
   int i, j, nz, nb, Ncol;
   off_t Nrow;
   char type[16];
-  Header header;
-  Header theader;
-  Matrix matrix;
-  FTable ftable;
-
-  header.buffer = NULL;
-  matrix.buffer = NULL;
-  ftable.buffer = NULL;
-  theader.buffer = NULL;
+
+  FTable ftableZone;
+
+  Header headerCell;
+  FTable ftableCell;
+
+  ftableZone.buffer = NULL;
+  ftableCell.buffer = NULL;
+  headerCell.buffer = NULL;
+
+  // we must have already read in the Zone table header section
+  ftableZone.header =  headerZone;
+  ftableCell.header = &headerCell;
+
   BoundaryTree *tree = NULL;
 
-  FILE *f = fopen (filename, "r");
-  if (!f) {
-    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
-    return NULL;
-  }
-
-  /* load in PHU segment (ignore) */
-  if (!gfits_fread_header (f, &header)) {
-    if (DEBUG) fprintf (stderr, "can't read image subset header\n");
-    goto escape;
-  }
-  if (!gfits_fread_matrix (f, &matrix, &header)) {
-    if (DEBUG) fprintf (stderr, "can't read image subset matrix\n");
-    goto escape;
-  }
-
   ALLOCATE (tree, BoundaryTree, 1);
 
-  gfits_scan (&header, "DEC_ORI", "%lf", 1, &tree->DEC_origin);
-  gfits_scan (&header, "DEC_OFF", "%lf", 1, &tree->DEC_offset);
-
-  gfits_scan (&header, "NX_SUB", "%d", 1, &tree->NX_SUB);
-  gfits_scan (&header, "NY_SUB", "%d", 1, &tree->NY_SUB);
-  gfits_scan (&header, "PIXSCALE", "%lf", 1, &tree->dPix);
-
-  ftable.header = &theader;
+  // we need to read the boundary tree parameters from the correct header
+  // put them in the PHU header in any case?
+  gfits_scan (headerPHU, "DEC_ORI",  "%lf", 1, &tree->DEC_origin);
+  gfits_scan (headerPHU, "DEC_OFF",  "%lf", 1, &tree->DEC_offset);
+  gfits_scan (headerPHU, "NX_SUB",   "%d",  1, &tree->NX_SUB);
+  gfits_scan (headerPHU, "NY_SUB",   "%d",  1, &tree->NY_SUB);
+  gfits_scan (headerPHU, "PIXSCALE", "%lf", 1, &tree->dPix);
 
   /*** zone information table ***/
-  { 
-    // load data for this header 
-    if (!gfits_load_header (f, &theader)) goto escape;
-
-    // read the fits table bytes
-    if (!gfits_fread_ftable_data (f, &ftable, FALSE)) goto escape;
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftableZone, FALSE)) goto escape;
  
-    // need to create and assign to flat-field correction
-    GET_COLUMN_RAW(tree->Nband,       "NBAND",  	 int);
-    GET_COLUMN_RAW(tree->RA_origin,   "RA_ORIGIN",   double);
-    GET_COLUMN_RAW(tree->RA_offset,   "RA_OFFSET",   double);
-    GET_COLUMN_RAW(tree->DEC_min  ,   "DEC_MIN",     double);
-    GET_COLUMN_RAW(tree->DEC_max  ,   "DEC_MAX",     double);
-    GET_COLUMN_RAW(tree->DEC_min_raw, "DEC_MIN_RAW", double);
-    GET_COLUMN_RAW(tree->DEC_max_raw, "DEC_MAX_RAW", double);
-    gfits_free_header (&theader);
-    gfits_free_table  (&ftable);
-
-    fprintf (stderr, "loaded data for %lld zones\n", (long long) Nrow);
-    tree->Nzone = Nrow;
-
-    // allocate the storage arrays
-    ALLOCATE (tree->ra,   double *, tree->Nzone);
-    ALLOCATE (tree->dec,  double *, tree->Nzone);
-    ALLOCATE (tree->Xo,   double *, tree->Nzone);
-    ALLOCATE (tree->Yo,   double *, tree->Nzone);
-    ALLOCATE (tree->dX,      int *, tree->Nzone);
-    ALLOCATE (tree->dY,      int *, tree->Nzone);
-    ALLOCATE (tree->cell,    int *, tree->Nzone);
-    ALLOCATE (tree->projID,  int *, tree->Nzone);
-    ALLOCATE (tree->name,  char **, tree->Nzone);
-    for (i = 0; i < tree->Nzone; i++) {
-      ALLOCATE (tree->ra[i],   double, tree->Nband[i]);
-      ALLOCATE (tree->dec[i],  double, tree->Nband[i]);
-      ALLOCATE (tree->Xo[i],   double, tree->Nband[i]);
-      ALLOCATE (tree->Yo[i],   double, tree->Nband[i]);
-      ALLOCATE (tree->dX[i],      int, tree->Nband[i]);
-      ALLOCATE (tree->dY[i],      int, tree->Nband[i]);
-      ALLOCATE (tree->cell[i],    int, tree->Nband[i]);
-      ALLOCATE (tree->projID[i],  int, tree->Nband[i]);
-      ALLOCATE (tree->name[i], char *, tree->Nband[i]);
-      for (j = 0; j < tree->Nband[i]; j++) {
-	ALLOCATE (tree->name[i][j], char, BOUNDARY_TREE_NAME_LENGTH);
-      }
+  // need to create and assign to flat-field correction
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->Nband,       "NBAND",  	    int);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->RA_origin,   "RA_ORIGIN",   double);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->RA_offset,   "RA_OFFSET",   double);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->DEC_min  ,   "DEC_MIN",     double);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->DEC_max  ,   "DEC_MAX",     double);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->DEC_min_raw, "DEC_MIN_RAW", double);
+  GET_COLUMN_RAW(headerZone, &ftableZone, tree->DEC_max_raw, "DEC_MAX_RAW", double);
+  gfits_free_table  (&ftableZone);
+
+  fprintf (stderr, "loaded data for %lld zones\n", (long long) Nrow);
+  tree->Nzone = Nrow;
+
+  // allocate the storage arrays
+  ALLOCATE (tree->ra,   double *, tree->Nzone);
+  ALLOCATE (tree->dec,  double *, tree->Nzone);
+  ALLOCATE (tree->Xo,   double *, tree->Nzone);
+  ALLOCATE (tree->Yo,   double *, tree->Nzone);
+  ALLOCATE (tree->dX,      int *, tree->Nzone);
+  ALLOCATE (tree->dY,      int *, tree->Nzone);
+  ALLOCATE (tree->cell,    int *, tree->Nzone);
+  ALLOCATE (tree->projID,  int *, tree->Nzone);
+  ALLOCATE (tree->name,  char **, tree->Nzone);
+  for (i = 0; i < tree->Nzone; i++) {
+    ALLOCATE (tree->ra[i],   double, tree->Nband[i]);
+    ALLOCATE (tree->dec[i],  double, tree->Nband[i]);
+    ALLOCATE (tree->Xo[i],   double, tree->Nband[i]);
+    ALLOCATE (tree->Yo[i],   double, tree->Nband[i]);
+    ALLOCATE (tree->dX[i],      int, tree->Nband[i]);
+    ALLOCATE (tree->dY[i],      int, tree->Nband[i]);
+    ALLOCATE (tree->cell[i],    int, tree->Nband[i]);
+    ALLOCATE (tree->projID[i],  int, tree->Nband[i]);
+    ALLOCATE (tree->name[i], char *, tree->Nband[i]);
+    for (j = 0; j < tree->Nband[i]; j++) {
+      ALLOCATE (tree->name[i][j], char, BOUNDARY_TREE_NAME_LENGTH);
     }
   }
 
   /*** cell information table ***/
-  { 
-    // load data for this header 
-    if (!gfits_load_header (f, &theader)) goto escape;
-
-    // read the fits table bytes
-    if (!gfits_fread_ftable_data (f, &ftable, FALSE)) goto escape;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &headerCell)) goto escape;
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftableCell, FALSE)) goto escape;
  
-    // need to create and assign to flat-field correction
-    GET_COLUMN_NEW(R,     "RA",   	 double);
-    GET_COLUMN_NEW(D,     "DEC",  	 double);
-    GET_COLUMN_NEW(zone,  "ZONE",        int);
-    GET_COLUMN_NEW(band,  "BAND",        int);
-    GET_COLUMN_NEW(index, "INDEX",       int);
-    GET_COLUMN_NEW(Xo,    "X_CENT",      double);
-    GET_COLUMN_NEW(Yo,    "Y_CENT",      double);
-    GET_COLUMN_NEW(dX,    "X_GRID",      int);
-    GET_COLUMN_NEW(dY,    "Y_GRID",      int);
-    GET_COLUMN_NEW(name,  "NAME",        char); // XXX how is this done?
-    gfits_free_header (&theader);
-    gfits_free_table  (&ftable);
-    fprintf (stderr, "loaded data for %lld cells\n", (long long) Nrow);
-
-    // assign the storage arrays
-    for (i = 0; i < Nrow; i++) {
-      nz = zone[i];
-      nb = band[i];
-      tree->ra[nz][nb] = R[i];
-      tree->dec[nz][nb] = D[i];
-      tree->Xo[nz][nb] = Xo[i];
-      tree->Yo[nz][nb] = Yo[i];
-      tree->dX[nz][nb] = dX[i];
-      tree->dY[nz][nb] = dY[i];
-      tree->cell[nz][nb] = i; // XXX ?
-      memcpy(tree->name[nz][nb], &name[i*BOUNDARY_TREE_NAME_LENGTH], BOUNDARY_TREE_NAME_LENGTH);
-      // XXX parse out the ID from the name (skycell.NNNN)
-      tree->projID[nz][nb] = atoi(&tree->name[nz][nb][8]);
-    }
-
-    free (R     );
-    free (D     );
-    free (zone  );
-    free (band  );
-    free (Xo    );
-    free (Yo    );
-    free (dX    );
-    free (dY    );
-    free (index );
-    free (name  );
-  }
-
-  gfits_free_header (&header);
-  gfits_free_matrix (&matrix);
-  fclose (f);
+  // need to create and assign to flat-field correction
+  GET_COLUMN_NEW(&headerCell, &ftableCell, R,     "RA",   	 double);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, D,     "DEC",  	 double);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, zone,  "ZONE",        int);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, band,  "BAND",        int);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, index, "INDEX",       int);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, Xo,    "X_CENT",      double);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, Yo,    "Y_CENT",      double);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, dX,    "X_GRID",      int);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, dY,    "Y_GRID",      int);
+  GET_COLUMN_NEW(&headerCell, &ftableCell, name,  "NAME",        char); // XXX how is this done?
+  gfits_free_header (&headerCell);
+  gfits_free_table  (&ftableCell);
+
+  fprintf (stderr, "loaded data for %lld cells\n", (long long) Nrow);
+
+  // assign the storage arrays
+  for (i = 0; i < Nrow; i++) {
+    nz = zone[i];
+    nb = band[i];
+    tree->ra[nz][nb] = R[i];
+    tree->dec[nz][nb] = D[i];
+    tree->Xo[nz][nb] = Xo[i];
+    tree->Yo[nz][nb] = Yo[i];
+    tree->dX[nz][nb] = dX[i];
+    tree->dY[nz][nb] = dY[i];
+    tree->cell[nz][nb] = i; // XXX ?
+    memcpy(tree->name[nz][nb], &name[i*BOUNDARY_TREE_NAME_LENGTH], BOUNDARY_TREE_NAME_LENGTH);
+    // XXX parse out the ID from the name (skycell.NNNN)
+    tree->projID[nz][nb] = atoi(&tree->name[nz][nb][8]);
+  }
+
+  free (R     );
+  free (D     );
+  free (zone  );
+  free (band  );
+  free (Xo    );
+  free (Yo    );
+  free (dX    );
+  free (dY    );
+  free (index );
+  free (name  );
 
   return tree;
 
 escape:
-  gfits_free_header (&header);
-  gfits_free_matrix (&matrix);
-  gfits_free_header (&theader);
-  gfits_free_table  (&ftable);
+  gfits_free_header (&headerCell);
+  gfits_free_table  (&ftableCell);
+  gfits_free_table  (&ftableZone);
   if (tree) free (tree);
 
-  fclose (f);
   return NULL;
 }
@@ -173,9 +196,6 @@
 int BoundaryTreeSave(char *filename, BoundaryTree *tree) {
 
-  int i, nz, nb;
   Header header;
-  Header theader;
   Matrix matrix;
-  FTable ftable;
 
   gfits_init_header (&header);
@@ -202,4 +222,16 @@
   gfits_free_header (&header);
   gfits_free_matrix (&matrix);
+
+  BoundaryTreeWrite (f, tree);
+  fclose (f);
+
+  return TRUE;
+}
+
+int BoundaryTreeWrite(FILE *f, BoundaryTree *tree) {
+
+  int i, nz, nb;
+  Header theader;
+  FTable ftable;
 
   /*** zone information table ***/
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageMetadataSelection.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageMetadataSelection.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageMetadataSelection.c	(revision 36680)
@@ -18,5 +18,5 @@
   field.pc1_2  = field.pc2_1  = 0.0;
   field.Npolyterms = 0;
-  strcpy (field.ctype, "RA---SIN");
+  strcpy (field.ctype, "DEC--SIN");
 
   /* mosaic defines a frame with 0,0 at the mosaic center, and 1 arcsec / pixel */
@@ -26,5 +26,5 @@
   mosaic.pc1_2  = mosaic.pc2_1  = 0.0;
   mosaic.Npolyterms = 0;
-  strcpy (mosaic.ctype, "RA---SIN");
+  strcpy (mosaic.ctype, "DEC--SIN");
 
   if ((image = ImageMetadataLoad (filename, &Nimage)) == NULL) return (FALSE);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageSelection.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageSelection.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/ImageSelection.c	(revision 36680)
@@ -28,5 +28,5 @@
       mosaic.pc1_2  = mosaic.pc2_1  = 0.0;
       mosaic.Npolyterms = 0;
-      strcpy (mosaic.ctype, "RA---SIN");
+      strcpy (mosaic.ctype, "DEC--SIN");
   }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/RegionHostTable.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/RegionHostTable.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/RegionHostTable.c	(revision 36680)
@@ -0,0 +1,497 @@
+# include "dvo.h"
+# include <sys/types.h>
+# include <sys/wait.h>
+
+void InitRegionHosts (RegionHostInfo *hosts, int Nhosts, int NHOSTS) {
+
+  int i;
+  for (i = Nhosts; i < NHOSTS; i++) {
+    hosts[i].Rmin = NAN;
+    hosts[i].Rmax = NAN;
+    hosts[i].Dmin = NAN;
+    hosts[i].Dmax = NAN;
+
+    hosts[i].RminCat = NAN;
+    hosts[i].RmaxCat = NAN;
+    hosts[i].DminCat = NAN;
+    hosts[i].DmaxCat = NAN;
+
+    hosts[i].hostname = NULL;
+    hosts[i].stdio[HOST_STDIN] = -1;
+    hosts[i].stdio[HOST_STDOUT] = -1;
+    hosts[i].stdio[HOST_STDERR] = -1;
+    hosts[i].pid = 0;
+
+    hosts[i].image = NULL;
+    hosts[i].imseq = NULL;
+    hosts[i].Nimage = 0;
+    hosts[i].NIMAGE = 0;
+
+    hosts[i].neighbors = NULL;
+    hosts[i].Nneighbors = 0;
+    hosts[i].isNeighbor = FALSE;
+  }
+  return;
+}
+
+void FreeRegionHosts (RegionHostInfo *hosts, int Nhosts) {
+
+  int i;
+  for (i = 0; i < Nhosts; i++) {
+    free (hosts[i].hostname);
+  }
+  free (hosts);
+  return;
+}
+
+void FreeRegionHostTable (RegionHostTable *table) {
+
+  if (!table) return;
+  if (table->hosts) {
+    FreeRegionHosts (table->hosts, table->Nhosts);
+  }
+  if (table->index) free (table->index);
+  free (table);
+  return;
+}
+
+RegionHostTable *RegionHostTableLoad (char *catdir, char *rootname) {
+
+  int i, Nline;
+
+  char *filename = NULL;
+
+  ALLOCATE (filename, char, strlen(catdir) + strlen(rootname) + 2); // one slash and one EOL
+  sprintf (filename, "%s/%s", catdir, rootname);
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "failed to open host table %s\n", filename);
+    free (filename);
+    return NULL;
+  }
+
+  // simple format: ID hostname pathname
+  
+  int NHOSTS = 16;
+  int Nhosts = 0;
+  RegionHostInfo *hosts = NULL;
+  ALLOCATE (hosts, RegionHostInfo, NHOSTS);
+  InitRegionHosts (hosts, Nhosts, NHOSTS);
+
+  int maxID = 0;
+
+  RegionHostTable *table = NULL;
+  ALLOCATE (table, RegionHostTable, 1);
+  table->Rmin = 360;
+  table->Rmax =   0;
+  table->Dmin = +90;
+  table->Dmax = -90;
+
+  for (Nline = 0; TRUE; Nline ++) {
+    int ID;
+    char tmphost[1024];
+    char line[1024];
+
+    // XXXX use this for safety: int status = scan_line_maxlen (f, line, 1024);
+    int status = scan_line (f, line);
+    if (status == EOF) break;
+
+    // find first non-whitespace char & skip commented lines
+    for (i = 0; OHANA_WHITESPACE (line[i]); i++);
+    if (line[i] == '#') continue;
+    if (line[i] == 0) continue;
+
+    double Rmin, Rmax, Dmin, Dmax;
+    status = sscanf (line, "%d %1023s %lf %lf %lf %lf", &ID, tmphost, &Rmin, &Rmax, &Dmin, &Dmax);
+    if (status != 6) {
+      fprintf (stderr, "error reading line %d of region host table %s\n", Nline, filename);
+      FreeRegionHosts (hosts, Nhosts);
+      free (filename);
+      fclose (f);
+      return NULL;
+    }
+
+    // check the validity of ID (0 < ID < MAX_SHORT)
+
+    if (ID < 1) {
+      fprintf (stderr, "invalid host ID %d\n", ID);
+      exit (1);
+    }
+    if (ID > 255) {
+      fprintf (stderr, "invalid host ID %d\n", ID);
+      exit (1);
+    }
+    maxID = MAX(maxID, ID);
+
+    hosts[Nhosts].hostID = ID;
+    hosts[Nhosts].hostname = strcreate(tmphost);
+
+    InitIOBuffer (&hosts[Nhosts].stdout, 1000);
+    InitIOBuffer (&hosts[Nhosts].stderr, 1000);
+
+    hosts[Nhosts].Rmin = Rmin;
+    hosts[Nhosts].Rmax = Rmax;
+    hosts[Nhosts].Dmin = Dmin;
+    hosts[Nhosts].Dmax = Dmax;
+    
+    hosts[Nhosts].RminCat = Rmin;
+    hosts[Nhosts].RmaxCat = Rmax;
+    hosts[Nhosts].DminCat = Dmin;
+    hosts[Nhosts].DmaxCat = Dmax;
+    
+    table->Rmin = MIN(Rmin, table->Rmin);
+    table->Rmax = MAX(Rmax, table->Rmax);
+    table->Dmin = MIN(Dmin, table->Dmin);
+    table->Dmax = MAX(Dmax, table->Dmax);
+
+    // InitIOBuffer (&hosts[Nhosts].stdout, 1000);
+    // InitIOBuffer (&hosts[Nhosts].stderr, 1000);
+
+    Nhosts ++;
+    if (Nhosts >= NHOSTS) {
+      NHOSTS += 16;
+      REALLOCATE (hosts, RegionHostInfo, NHOSTS);
+      InitRegionHosts (hosts, Nhosts, NHOSTS);
+    }
+  }    
+
+  table->Nhosts = Nhosts;
+  table->hosts = hosts;
+
+  ALLOCATE (table->index, short, maxID + 1);
+  for (i = 0; i <= maxID; i++) table->index[i] = -1;
+
+  for (i = 0; i < table->Nhosts; i++) {
+    if (table->index[table->hosts[i].hostID] != -1) {
+      fprintf (stderr, "error: duplicate hostID %d\n", table->hosts[i].hostID);
+      exit (1);
+    }
+    table->index[table->hosts[i].hostID] = i;
+  }
+
+  free (filename);
+  fclose (f);
+  return table;
+}
+
+int RegionHostFindNeighbors (RegionHostTable *table, int Nhost) {
+
+  int i;
+
+  // given a specific host (by table sequence), find all of its neighbors
+  // (eg, (host->Rmin == althost->Rmax && (host->Dmin <= althost->Dmax) && (host->Dmax >= althost->Dmin)
+
+  RegionHostInfo *myhost = &table->hosts[Nhost];
+  myAssert (!myhost->neighbors, "myhost neighbors already allocated");
+  myAssert (!myhost->Nneighbors, "myhost Nneighbors not zero?");
+  ALLOCATE (myhost->neighbors, int, 1); // always allocate 1 extra
+
+  for (i = 0; i < table->Nhosts; i++) {
+
+    if (i == Nhost) continue;
+    RegionHostInfo *altHost = &table->hosts[i];
+    
+    int onBorder;
+
+    onBorder = 
+      (myhost->Rmin == altHost->Rmax) && 
+      (myhost->Dmin <= altHost->Dmax) &&
+      (myhost->Dmax >= altHost->Dmin);
+    if (onBorder) goto add_neighbor;
+
+    onBorder = 
+      (myhost->Rmax == altHost->Rmin) && 
+      (myhost->Dmin <= altHost->Dmax) &&
+      (myhost->Dmax >= altHost->Dmin);
+    if (onBorder) goto add_neighbor;
+
+    // handle the 0,360 boundary (Rmin,Rmax are in the range 0,360, but 0 == 360)
+    onBorder = 
+      (myhost->Rmin == 0.0) && 
+      (altHost->Rmax == 360.0) && 
+      (myhost->Dmin <= altHost->Dmax) &&
+      (myhost->Dmax >= altHost->Dmin);
+    if (onBorder) goto add_neighbor;
+
+    onBorder = 
+      (myhost->Rmax == 360.0) && 
+      (altHost->Rmin == 0.0) && 
+      (myhost->Dmin <= altHost->Dmax) &&
+      (myhost->Dmax >= altHost->Dmin);
+    if (onBorder) goto add_neighbor;
+
+    onBorder = 
+      (myhost->Dmin == altHost->Dmax) && 
+      (myhost->Rmin <= altHost->Rmax) &&
+      (myhost->Rmax >= altHost->Rmin);
+    if (onBorder) goto add_neighbor;
+
+    onBorder = 
+      (myhost->Dmax == altHost->Dmin) && 
+      (myhost->Rmin <= altHost->Rmax) &&
+      (myhost->Rmax >= altHost->Rmin);
+    if (onBorder) goto add_neighbor;
+    
+    continue;
+
+  add_neighbor:
+      myhost->neighbors[myhost->Nneighbors] = i;
+      myhost->Nneighbors ++;
+      REALLOCATE (myhost->neighbors, int, myhost->Nneighbors + 1);
+      altHost->isNeighbor = TRUE;
+  }
+  return TRUE;
+}
+
+// wait for all children to complete, report output to stdout
+int RegionHostTableWaitJobs (RegionHostTable *table, char *file, int lineno) {
+
+  int i;
+
+  // we have launched table->Nhosts jobs; wait for all of them to complete...
+  // if one (N) failed to launch, we will get an ECHILD error from the last (N) calls
+  int done = FALSE;
+  for (i = 0; !done  && (i < table->Nhosts); i++) {
+    int status = 0;
+
+    // XXX we'll need to pass in WNOHANG and keep retrying if we want to have a timeout...
+    int pid = waitpid (-1, &status, 0);
+    if (!pid) {
+      // this should only occur if we called waitpid with the WNOHANG option
+      fprintf (stderr, "programming error (1)? %s %d", file, lineno);
+      exit (2);
+    }
+    if (pid == -1) {
+      switch (errno) {
+	case ECHILD:
+	  done = TRUE;
+	  break;
+	default:
+	  fprintf (stderr, "programming error (2)? %s %d", file, lineno);
+	  exit (2);
+      }
+    }
+
+    // when the host has finished, close the open sockets
+
+    // find the host which has finished
+    int Nout, j;
+    int found = FALSE;
+    for (j = 0; j < table->Nhosts; j++) {
+      if (table->hosts[j].pid != pid) continue;
+      found = TRUE;
+      // check on the status of this and report any output?
+      fprintf (stderr, "job finished for %s (%d)\n", table->hosts[j].hostname, pid);
+      // read the stderr and stdout
+      IOBuffer buffer;
+      InitIOBuffer (&buffer, 100);
+      EmptyIOBuffer (&buffer, 100, table->hosts[j].stdio[HOST_STDOUT]);
+      fprintf (stderr, "--- stdout from %s ---\n", table->hosts[j].hostname);
+      Nout = write (STDOUT_FILENO, buffer.buffer, buffer.Nbuffer);
+      if (Nout != buffer.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+      fprintf (stderr, "\n");
+	  
+      InitIOBuffer (&buffer, 100);
+      EmptyIOBuffer (&buffer, 100, table->hosts[j].stdio[HOST_STDERR]);
+      fprintf (stderr, "--- stderr from %s ---\n", table->hosts[j].hostname);
+      Nout = write (STDOUT_FILENO, buffer.buffer, buffer.Nbuffer);
+      if (Nout != buffer.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+      fprintf (stderr, "\n");
+      if (WIFEXITED(status)) {
+	fprintf (stderr, "normal completion, exit status is %d\n", WEXITSTATUS(status));
+	table->hosts[j].status = WEXITSTATUS(status);
+	if (table->hosts[j].status) {
+	  fprintf (stderr, "job failed on %s\n", table->hosts[j].hostname);
+	  continue;
+	}
+      } else {
+	table->hosts[j].status = -1;
+	fprintf (stderr, "job exited abnormally on %s\n", table->hosts[j].hostname);
+	continue;
+      }
+    }
+    if (!found) {
+      fprintf (stderr, "Programming error: failed to matched finished job to known host!\n");
+      exit (2);
+    }
+  }
+  return TRUE;
+}
+
+
+// wait for all children to complete, report output to stdout
+int RegionHostTableWaitJobsGetIO (RegionHostTable *table, char *file, int lineno, int VERBOSE) {
+
+  // we have launched table->Nhosts jobs; wait for all of them to complete...
+  // if one (N) failed to launch, we will get an ECHILD error from the last (N) calls
+
+  // we need to read any data waiting on stderr or stdout from these jobs, or the overfull
+  // buffers can cause a problem.  we alternate between 'select' and 'waitpid' calls with
+  // timeouts for both
+
+  // add all hosts' sockets to the fd_sets
+  fd_set rdSet, wtSet;
+  FD_ZERO (&rdSet);
+  FD_ZERO (&wtSet);
+
+  // XXX can I set the fd_sets once, since I am not actually closing the fd's?
+
+  int globalStatus = TRUE;
+
+  int i;
+  int Nmax = 0;
+  for (i = 0; i < table->Nhosts; i++) {
+    if (!table->hosts[i].pid) continue; // any unconnected hosts should be skipped
+    FD_SET (table->hosts[i].stdio[HOST_STDIN], &wtSet);
+    Nmax = MAX (Nmax, table->hosts[i].stdio[HOST_STDIN]);
+    FD_SET (table->hosts[i].stdio[HOST_STDOUT], &rdSet);
+    Nmax = MAX (Nmax, table->hosts[i].stdio[HOST_STDOUT]);
+    FD_SET (table->hosts[i].stdio[HOST_STDERR], &rdSet);
+    Nmax = MAX (Nmax, table->hosts[i].stdio[HOST_STDERR]);
+  }    
+  Nmax ++;
+
+  // need the list of connected hosts for exit test below
+  int Nrunning = 0;
+  for (i = 0; i < table->Nhosts; i++) {
+    if (!table->hosts[i].pid) continue; // any unconnected hosts should be skipped
+    Nrunning ++;
+  }
+
+  int Nfound = 0;
+
+  // this loop has 2 chunks: (a) check for I/O + (b) check for jobs done
+  while (1) {
+
+    // Wait up to 0.5 second for host to provide I/O
+    // timeout gets mucked: need to reset before each select
+    struct timeval timeout;
+    timeout.tv_sec = 10;
+    timeout.tv_usec = 500000;
+
+    int status = select (Nmax, NULL, &wtSet, NULL, &timeout);
+    if (status == -1) {
+      perror("select()");
+      exit (2);
+    }
+
+    // we have some sockets to check, check sockets for all hosts
+    for (i = 0; (status > 0) && (i < table->Nhosts); i++) {
+      if (!table->hosts[i].pid) continue; // any unconnected hosts should be skipped
+
+      if (FALSE && FD_ISSET (table->hosts[i].stdio[HOST_STDIN], &wtSet)) {
+	// this host is waiting for input : this is an error, so exit
+	fprintf (stderr, "host %s is waiting for input\n", table->hosts[i].hostname);
+	abort();
+      }
+      
+      if ((table->hosts[i].stdio[HOST_STDOUT] > 0) && FD_ISSET (table->hosts[i].stdio[HOST_STDOUT], &rdSet)) {
+	// this host has waiting output : read to buffer, and dump if necessary
+	ReadtoIOBuffer (&table->hosts[i].stdout, table->hosts[i].stdio[HOST_STDOUT]);
+	// if (table->hosts[i].stdout.Nbuffer > 0x10000) {
+	if (table->hosts[i].stdout.Nbuffer > 0x1000) {
+	  int printHead = VERBOSE || (table->hosts[i].stdout.Nbuffer > 0);
+	  if (printHead) fprintf (stdout, "--- stdout from %s --- (%d bytes, v1)\n", table->hosts[i].hostname, table->hosts[i].stdout.Nbuffer);
+	  int Nout = write (STDOUT_FILENO, table->hosts[i].stdout.buffer, table->hosts[i].stdout.Nbuffer);
+	  if (Nout != table->hosts[i].stdout.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+	  FlushIOBuffer (&table->hosts[i].stdout);
+	  if (printHead) fprintf (stdout, "\n");
+	}
+      }
+
+      if ((table->hosts[i].stdio[HOST_STDERR] > 0) && FD_ISSET (table->hosts[i].stdio[HOST_STDERR], &rdSet)) {
+	// this host has waiting output : read to buffer, and dump if necessary
+	ReadtoIOBuffer (&table->hosts[i].stderr, table->hosts[i].stdio[HOST_STDERR]);
+	// if (table->hosts[i].stderr.Nbuffer > 0x10000) {
+	if (table->hosts[i].stderr.Nbuffer > 0x1000) {
+	  int printHead = VERBOSE || (table->hosts[i].stderr.Nbuffer > 0);
+	  if (printHead) fprintf (stdout, "--- stderr from %s --- (%d bytes, v1)\n", table->hosts[i].hostname, table->hosts[i].stderr.Nbuffer);
+	  int Nout = write (STDOUT_FILENO, table->hosts[i].stderr.buffer, table->hosts[i].stderr.Nbuffer);
+	  if (Nout != table->hosts[i].stderr.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+	  FlushIOBuffer (&table->hosts[i].stderr);
+	  if (printHead) fprintf (stdout, "\n");
+	}
+      }
+    }
+
+    // now check if any children have finished...
+    while (TRUE) {
+      int status = 0;
+      int pid = waitpid (-1, &status, WNOHANG);
+      if (!pid) {
+	// fprintf (stderr, "no hosts to harvest\n");
+	usleep (500000);
+	break; // no outstanding jobs have finished 
+      }
+      if ((pid == -1) && (errno == ECHILD)) goto escape; // no more jobs on which to wait
+      if ((pid == -1) && (errno != ECHILD)) {
+	fprintf (stderr, "programming error (2)? %s %d", file, lineno);
+	exit (2);
+      }
+
+      // find the host which has finished
+      int found = FALSE;
+      for (i = 0; (i < table->Nhosts) && !found; i++) {
+	if (table->hosts[i].pid != pid) continue;
+	found = TRUE;
+
+	RegionHostInfo *host = &table->hosts[i];
+
+	// check on the status of this and report any output?
+	if (VERBOSE) fprintf (stdout, "job finished for %s (%d)\n", host->hostname, pid);
+
+	// read stdout
+	int printHead;
+	printHead = VERBOSE || (host->stdout.Nbuffer > 0);
+	EmptyIOBuffer (&host->stdout, 100, host->stdio[HOST_STDOUT]);
+	if (printHead) fprintf (stdout, "--- stdout from %s --- (%d bytes, v2)\n", host->hostname, host->stdout.Nbuffer);
+	int Nout = write (STDOUT_FILENO, host->stdout.buffer, host->stdout.Nbuffer);
+	if (Nout != host->stdout.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+	FlushIOBuffer (&host->stdout);
+	if (printHead) fprintf (stdout, "\n");
+	    
+	// read stderr
+	printHead = VERBOSE || (host->stderr.Nbuffer > 0);
+	EmptyIOBuffer (&host->stderr, 100, host->stdio[HOST_STDERR]);
+	if (printHead) fprintf (stdout, "--- stderr from %s --- (%d bytes, v2)\n", host->hostname, host->stderr.Nbuffer);
+	Nout = write (STDOUT_FILENO, host->stderr.buffer, host->stderr.Nbuffer);
+	if (Nout != host->stderr.Nbuffer) { fprintf (stderr, "(error writing log?)\n"); }
+	FlushIOBuffer (&host->stderr);
+	if (printHead) fprintf (stdout, "\n");
+
+	if (WIFEXITED(status)) {
+	  if (VERBOSE) fprintf (stdout, "normal completion, exit status is %d\n", WEXITSTATUS(status));
+	  host->status = WEXITSTATUS(status);
+	  if (host->status) {
+	    fprintf (stdout, "job failed on %s\n", host->hostname);
+	    globalStatus = FALSE;
+	  }
+	} else {
+	  host->status = -1;
+	  fprintf (stdout, "job exited abnormally on %s\n", host->hostname);
+	  globalStatus = FALSE;
+	  continue;
+	}
+      }
+      if (!found) {
+	fprintf (stderr, "Programming error: failed to matched finished job to known host!\n");
+	exit (2);
+      }
+      Nfound ++;
+      if (Nfound == Nrunning) goto escape; // we've harvested all jobs
+    }
+  }
+
+escape:
+
+  // close all opened connections
+  for (i = 0; i < table->Nhosts; i++) {
+    if (!table->hosts[i].pid) continue; // any unconnected hosts should be skipped
+    close (table->hosts[i].stdio[HOST_STDIN]);
+    close (table->hosts[i].stdio[HOST_STDOUT]);
+    close (table->hosts[i].stdio[HOST_STDERR]);
+  }
+
+  return globalStatus;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/TessellationTable.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/TessellationTable.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/TessellationTable.c	(revision 36680)
@@ -0,0 +1,573 @@
+# include "dvo.h"
+
+# define GET_COLUMN_NEW(OUT,NAME,TYPE)					\
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+# define GET_COLUMN_RAW(OUT,NAME,TYPE)					\
+  OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+# define DEBUG 0
+
+void TessellationTableInit (TessellationTable *tess, int Ntess) {
+
+  int i;
+
+  for (i = 0; i < Ntess; i++) {
+    tess[i].Rmin = NAN;
+    tess[i].Rmax = NAN;
+    tess[i].Dmin = NAN;
+    tess[i].Dmax = NAN;
+
+    tess[i].Xo = NAN;
+    tess[i].Yo = NAN;
+    tess[i].Ro = NAN;
+    tess[i].Do = NAN;
+
+    tess[i].dPix = NAN;
+
+    tess[i].dX = 0;
+    tess[i].dY = 0;
+
+    tess[i].NX_SUB = 0;
+    tess[i].NY_SUB = 0;
+
+    tess[i].basename = NULL;
+    tess[i].Nbasename = 0;
+
+    tess[i].projectIDoff = 0;
+    tess[i].skycellIDoff = 0;
+
+    tess[i].type = TESS_NONE;
+    tess[i].tree = NULL;
+  }
+  return;
+}
+
+// backwards compatible load : if the first table is a BoundaryTree, not a TessellationTable, just load that.
+TessellationTable *TessellationTableLoad(char *filename, int *Ntess) {
+
+  int i, Ncol;
+  off_t Nrow;
+  char type[16];
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  header.buffer = NULL;
+  matrix.buffer = NULL;
+  ftable.buffer = NULL;
+  theader.buffer = NULL;
+  TessellationTable *tess = NULL;
+  *Ntess = 0;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (DEBUG) fprintf (stderr, "can't read image subset header\n");
+    goto escape;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (DEBUG) fprintf (stderr, "can't read image subset matrix\n");
+    goto escape;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) goto escape;
+
+  // what kind of table have I just read (if any!)
+  char extname[64];
+  gfits_scan (&theader, "EXTNAME", "%s", 1, extname);
+
+  // is this a BoundaryTree?  if so, it must be stand-alone: load just that and generate a
+  // containing TessellationTable.
+  if (!strcmp(extname, "ZONE_DATA")) {
+    BoundaryTree *tree = BoundaryTreeRead (&header, &theader, f);
+    gfits_free_header (&theader);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    fclose (f);
+    
+    ALLOCATE (tess, TessellationTable, 1);
+    TessellationTableInit (tess, 1);
+    tess[0].tree = tree;
+    tess[0].type = TESS_RINGS;
+    tess[0].Rmin =   0;
+    tess[0].Rmax = 360;
+    tess[0].Dmin = -90;
+    tess[0].Dmax = +90;
+    *Ntess = 1;
+    return tess;
+  }
+  
+  if (strcmp(extname, "TESS_DATA")) {
+    fprintf (stderr, "header is neither ZONE_DATA nor TESS_DATA, problem with file\n");
+    goto escape;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) goto escape;
+ 
+  // need to create and assign to flat-field correction
+  GET_COLUMN_NEW(Ro,     "RA",     double);
+  GET_COLUMN_NEW(Do,     "DEC",    double);
+  GET_COLUMN_NEW(Xo,     "X_CENT", double);
+  GET_COLUMN_NEW(Yo,     "Y_CENT", double);
+  GET_COLUMN_NEW(dX,     "X_GRID", int);
+  GET_COLUMN_NEW(dY,     "Y_GRID", int);
+  GET_COLUMN_NEW(Rmin,   "R_MIN",  double);
+  GET_COLUMN_NEW(Rmax,   "R_MAX",  double);
+  GET_COLUMN_NEW(Dmin,   "D_MIN",  double);
+  GET_COLUMN_NEW(Dmax,   "D_MAX",  double);
+  GET_COLUMN_NEW(dPix,   "SCALE",  double);
+  GET_COLUMN_NEW(NX_SUB, "NX_SUB", int);
+  GET_COLUMN_NEW(NY_SUB, "NY_SUB", int);
+  GET_COLUMN_NEW(TYPE,   "TYPE",   int);
+
+  GET_COLUMN_NEW(PROJECT_ID_OFF, "PROJECT_ID_OFF", int);
+  GET_COLUMN_NEW(SKYCELL_ID_OFF, "SKYCELL_ID_OFF", int);
+
+  GET_COLUMN_NEW(BASENAME, "BASENAME", char);
+
+  *Ntess = Nrow;
+  ALLOCATE (tess, TessellationTable, *Ntess);
+  for (i = 0; i < *Ntess; i++) {
+    tess[i].Ro     = Ro[i]    ;
+    tess[i].Do     = Do[i]    ;
+    tess[i].Xo     = Xo[i]    ;
+    tess[i].Yo     = Yo[i]    ;
+    tess[i].dX     = dX[i]    ;
+    tess[i].dY     = dY[i]    ;
+    tess[i].Rmin   = Rmin[i]  ;
+    tess[i].Rmax   = Rmax[i]  ;
+    tess[i].Dmin   = Dmin[i]  ;
+    tess[i].Dmax   = Dmax[i]  ;
+    tess[i].dPix   = dPix[i]  ;
+    tess[i].NX_SUB = NX_SUB[i];
+    tess[i].NY_SUB = NY_SUB[i];
+    tess[i].type   = TYPE[i];
+
+    tess[i].projectIDoff = PROJECT_ID_OFF[i];
+    tess[i].skycellIDoff = SKYCELL_ID_OFF[i];
+
+    ALLOCATE (tess[i].basename, char, BOUNDARY_TREE_NAME_LENGTH);
+    memcpy (tess[i].basename, &BASENAME[i*BOUNDARY_TREE_NAME_LENGTH], BOUNDARY_TREE_NAME_LENGTH);
+    tess[i].Nbasename = strlen (tess[i].basename);
+
+    tess[i].tree   = NULL;
+  }
+
+  free(Ro    );
+  free(Do    );
+  free(Xo    );
+  free(Yo    );
+  free(dX    );
+  free(dY    );
+  free(Rmin  );
+  free(Rmax  );
+  free(Dmin  );
+  free(Dmax  );
+  free(dPix  );
+  free(NX_SUB);
+  free(NY_SUB);
+  free(TYPE);
+
+  free(PROJECT_ID_OFF);
+  free(SKYCELL_ID_OFF);
+
+  gfits_free_header (&theader);
+  gfits_free_table  (&ftable);
+
+  // fprintf (stderr, "loaded data for %lld projection cell\n", (long long) *Ntess);
+
+  /*** check for a RINGS entry ***/
+
+  // find the RINGS tessellation (if present)
+  TessellationTable *rings = NULL;
+  for (i = 0; i < *Ntess; i++) {
+    if (tess[i].type == TESS_LOCAL) continue;
+    if (tess[i].type != TESS_RINGS) continue;
+    rings = &tess[i];
+  }
+
+  if (!rings) {
+    fclose (f);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    return (tess);
+  }
+
+  // load data for this header, or exit
+  if (!gfits_load_header (f, &theader))  {
+    fprintf (stderr, "warning : RINGS tessellation listed in table but not present\n");
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    return tess;
+  }
+
+  // what kind of table have I just read (if any!)
+  gfits_scan (&theader, "EXTNAME", "%s", 1, extname);
+
+  if (strcmp(extname, "ZONE_DATA")) {
+    fprintf (stderr, "warning : RINGS tessellation listed in table but not present\n");
+    gfits_free_header (&theader);
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+    return tess;
+  }
+
+  // is this a BoundaryTree?  if so, it must be stand-alone: load just that and generate a
+  // containing TessellationTable.
+  BoundaryTree *tree = BoundaryTreeRead (&header, &theader, f);
+  gfits_free_header (&theader);
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  fclose (f);
+    
+  rings->tree = tree;
+  return tess;
+
+escape:
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table  (&ftable);
+  if (tess) free (tess);
+
+  fclose (f);
+  return NULL;
+}
+
+// we are passed a TessellationTable structure, write it to a FITS table.
+// if one of the tess entries is a RINGS tessellation, write it as a boundary tree
+int TessellationTableSave(char *filename, TessellationTable *tess, int Ntess) {
+
+  int i;
+  Header header;
+  Matrix matrix;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open boundary tess file for output %s\n", filename);
+    return FALSE;
+  }
+
+  // find the RINGS tessellation (if present)
+  BoundaryTree *rings = NULL;
+  for (i = 0; i < Ntess; i++) {
+    if (tess[i].type == TESS_LOCAL) continue;
+    if (tess[i].type != TESS_RINGS) continue;
+    rings = tess[i].tree;
+  }
+
+  // if we have a RINGS entry, we need to save some information in the header to define the layout
+  if (rings) {
+    gfits_modify (&header, "DEC_ORI", "%lf", 1, rings->DEC_origin);
+    gfits_modify (&header, "DEC_OFF", "%lf", 1, rings->DEC_offset);
+
+    gfits_modify (&header, "NX_SUB", "%d", 1, rings->NX_SUB);
+    gfits_modify (&header, "NY_SUB", "%d", 1, rings->NY_SUB);
+    gfits_modify (&header, "PIXSCALE", "%lf", 1, rings->dPix);
+  }
+
+  gfits_fwrite_header  (f, &header);
+  gfits_fwrite_matrix  (f, &matrix);
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+
+  /** LOCAL tessellation table info **/
+  Header theader;
+  FTable ftable;
+
+  gfits_create_table_header (&theader, "BINTABLE", "TESS_DATA");
+
+  gfits_define_bintable_column (&theader, "D", "RA",     "ra (J2000) of cell center", "degree", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "DEC",    "dec (J2000) of cell center", "degree", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "X_CENT", "projection cell center pixel", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "Y_CENT", "projection cell center pixel", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "X_GRID", "skycell grid spacing", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "Y_GRID", "skycell grid spacing", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "R_MIN",  "RA limit (lower)", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "R_MAX",  "RA limit (upper)", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "D_MIN",  "DEC limit (lower)", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "D_MAX",  "DEC limit (upper)", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "SCALE",  "pixel scale for projection cell", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "NX_SUB", "skycell subdivision in x", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "NY_SUB", "skycell subdivision in y", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "TYPE",   "type of tessellation", "none", 1.0, 0.0);
+
+  gfits_define_bintable_column (&theader, "J", "PROJECT_ID_OFF", "offset in image name to projection cell ID", "none", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "SKYCELL_ID_OFF", "offset in image name to skycell ID", "none", 1.0, 0.0);
+
+  char fmt[16];
+  snprintf (fmt, 16, "%dA", BOUNDARY_TREE_NAME_LENGTH);
+  gfits_define_bintable_column (&theader, fmt, "BASENAME", "image base name", "none", 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+  
+  // int Nout = (rings == NULL) ? Ntess : Ntess - 1;
+  int Nout = Ntess;
+
+  // create intermediate storage arrays
+  // NOTE: we have to unroll the 2D arrays in tess into 1D arrays
+  double *Ro            ; ALLOCATE (Ro    ,    double, Nout);
+  double *Do            ; ALLOCATE (Do    ,    double, Nout);
+  double *Xo            ; ALLOCATE (Xo    ,    double, Nout);
+  double *Yo            ; ALLOCATE (Yo    ,    double, Nout);
+  int    *dX            ; ALLOCATE (dX    ,    int,    Nout);
+  int    *dY            ; ALLOCATE (dY    ,    int,    Nout);
+  double *Rmin          ; ALLOCATE (Rmin  ,    double, Nout);
+  double *Rmax          ; ALLOCATE (Rmax  ,    double, Nout);
+  double *Dmin          ; ALLOCATE (Dmin  ,    double, Nout);
+  double *Dmax          ; ALLOCATE (Dmax  ,    double, Nout);
+  double *dPix          ; ALLOCATE (dPix  ,    double, Nout);
+  int    *NX_SUB        ; ALLOCATE (NX_SUB,    int,    Nout);
+  int    *NY_SUB        ; ALLOCATE (NY_SUB,    int,    Nout);
+  int    *TYPE          ; ALLOCATE (TYPE,      int,    Nout);
+
+  int    *PROJECT_ID_OFF; ALLOCATE (PROJECT_ID_OFF, int, Nout);
+  int    *SKYCELL_ID_OFF; ALLOCATE (SKYCELL_ID_OFF, int, Nout);
+
+  char   *BASENAME      ; ALLOCATE (BASENAME,  char,   Nout*BOUNDARY_TREE_NAME_LENGTH);
+  memset (BASENAME, 0, Nout*BOUNDARY_TREE_NAME_LENGTH);
+
+  // assign the storage arrays
+  // XXX I was not writing out the RINGS tess entry, but that probably is not useful (drop
+  // N vs i in future?)
+  int N = 0;
+  for (i = 0; i < Ntess; i++) {
+    // if (tess->type != TESS_LOCAL) continue;
+    Ro[N]     = tess[i].Ro;
+    Do[N]     = tess[i].Do;
+    Xo[N]     = tess[i].Xo;
+    Yo[N]     = tess[i].Yo;
+    dX[N]     = tess[i].dX;
+    dY[N]     = tess[i].dY;
+    Rmin[N]   = tess[i].Rmin;
+    Rmax[N]   = tess[i].Rmax;
+    Dmin[N]   = tess[i].Dmin;
+    Dmax[N]   = tess[i].Dmax;
+    dPix[N]   = tess[i].dPix;
+    NX_SUB[N] = tess[i].NX_SUB;
+    NY_SUB[N] = tess[i].NY_SUB;
+    TYPE[N]   = tess[i].type;
+
+    PROJECT_ID_OFF[N] = tess[i].projectIDoff;
+    SKYCELL_ID_OFF[N] = tess[i].skycellIDoff;
+
+    if (tess[i].basename) {
+      memcpy(&BASENAME[N*BOUNDARY_TREE_NAME_LENGTH], tess[i].basename, BOUNDARY_TREE_NAME_LENGTH);
+    }
+
+    N++; 
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "RA",     Ro,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "DEC",    Do,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "X_CENT", Xo,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "Y_CENT", Yo,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "X_GRID", dX,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "Y_GRID", dY,     Nout);
+  gfits_set_bintable_column (&theader, &ftable, "R_MIN",  Rmin,   Nout);
+  gfits_set_bintable_column (&theader, &ftable, "R_MAX",  Rmax,   Nout);
+  gfits_set_bintable_column (&theader, &ftable, "D_MIN",  Dmin,   Nout);
+  gfits_set_bintable_column (&theader, &ftable, "D_MAX",  Dmax,   Nout);
+  gfits_set_bintable_column (&theader, &ftable, "SCALE",  dPix,   Nout);
+  gfits_set_bintable_column (&theader, &ftable, "NX_SUB", NX_SUB, Nout);
+  gfits_set_bintable_column (&theader, &ftable, "NY_SUB", NY_SUB, Nout);
+  gfits_set_bintable_column (&theader, &ftable, "TYPE",   TYPE,   Nout);
+
+  gfits_set_bintable_column (&theader, &ftable, "PROJECT_ID_OFF", PROJECT_ID_OFF, Nout);
+  gfits_set_bintable_column (&theader, &ftable, "SKYCELL_ID_OFF", SKYCELL_ID_OFF, Nout);
+  gfits_set_bintable_column (&theader, &ftable, "BASENAME", BASENAME, Nout);
+
+  free (Ro    );
+  free (Do    );
+  free (Xo    );
+  free (Yo    );
+  free (dX    );
+  free (dY    );
+  free (Rmin  );
+  free (Rmax  );
+  free (Dmin  );
+  free (Dmax  );
+  free (dPix  );
+  free (NX_SUB);
+  free (NY_SUB);
+  free (TYPE);
+
+  free (PROJECT_ID_OFF);
+  free (SKYCELL_ID_OFF);
+  free (BASENAME);
+
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  if (rings) {
+    BoundaryTreeWrite(f, rings);
+  }
+
+  fclose (f);
+
+  return TRUE;
+}
+
+int TessellationLocalProjection (double *x, double *y, double r, double d, TessellationTable *tess) {
+
+    double Xo = tess->Xo;
+    double Yo = tess->Yo;
+    double Ro = tess->Ro;
+    double Do = tess->Do;
+    double dPix = tess->dPix;
+
+    // this block only depends on Ro, Do
+
+    double sdp  = sin(RAD_DEG*Do);
+    double cdp  = cos(RAD_DEG*Do);
+    double salp = sin(RAD_DEG*(r - Ro));
+    double calp = cos(RAD_DEG*(r - Ro));
+    double sdel = sin(RAD_DEG*d);
+    double cdel = cos(RAD_DEG*d);
+    
+    double stht = sdel*sdp + cdel*cdp*calp;    /* sin(theta) */
+    double sphi = cdel*salp;                   /* = cos(theta)*sin(phi) */
+    double cphi = cdel*sdp*calp - sdel*cdp;    /* = cos(theta)*cos(phi) */
+
+    // defines the TAN projection (one of zenithal projections available, libdvo/src/coordops.c
+    // R = cot (theta) = cos(theta) / sin(theta)
+    double L, M;
+    if (stht == 0) {
+	double Rc = hypot(sphi, cphi);
+	L = 180.0 * sphi / Rc;
+	M = 180.0 * cphi / Rc;
+    } else {
+	L = +DEG_RAD * sphi / stht;
+	M = -DEG_RAD * cphi / stht;
+    }
+
+    // scale, rotation, parity:
+    // rotation == 0.0 (pc1_1 == pc2_2 == 1.0, pc1_2 = pc2_1 = 0.0)
+
+    // if there were rotation or parity:
+    // Ro = (coords[0].pc1_1*coords[0].pc2_2 - coords[0].pc1_2*coords[0].pc2_1);
+    // Xo = (coords[0].pc2_2*L - coords[0].pc1_2*M) / Ro;
+    // Yo = (coords[0].pc1_1*M - coords[0].pc2_1*L) / Ro;
+
+    double X = L;
+    double Y = M;
+
+    // scale is dPix
+
+    *x = Xo - X / dPix;
+    *y = Yo + Y / dPix;
+    
+    return TRUE;
+}
+
+static int Nfail = 0;
+
+// for the given ra,dec : find the valid tessellation and the containing projection / skycell IDs
+int TessellationPrimaryCellIDs (TessellationTable *tess, int Ntess, int *tessID, int *projID, int *skycellID, double ra, double dec) {
+
+  int i, zone, band;
+
+  *tessID = -1;
+  *projID = -1;
+  *skycellID = -1;
+
+  if (!tess) return FALSE;
+
+  // find the tessellation which includes this location
+  int myTess = -1;
+  for (i = 0; i < Ntess; i++) {
+    if (ra  <  tess[i].Rmin) continue;
+    if (ra  >= tess[i].Rmax) continue;
+    if (dec <  tess[i].Dmin) continue;
+    if (dec >= tess[i].Dmax) continue;
+    myTess = i;
+    break;
+  }
+  if (myTess < 0) {
+    if (Nfail < 100) {
+      fprintf (stderr, "no matching tessellation @ %f,%f\n", ra, dec);
+    }
+    Nfail ++;
+    return FALSE;
+  }
+
+  if (tess[myTess].type == TESS_NONE) {
+    if (Nfail < 100) {
+      fprintf (stderr, "tess has invalid type\n");
+      Nfail ++;
+    }
+    return FALSE;
+  }
+
+  if (tess[myTess].type == TESS_LOCAL) {
+    // I have ra, dec, and the primary projection cell.  In order to choose the primary skycell,
+    // I just need to project to ra,dec to X,Y based on the center of the cell and then get the subdivision right.
+    
+    double x = 0.0;
+    double y = 0.0;
+    TessellationLocalProjection (&x, &y, ra, dec, &tess[myTess]);
+  
+    int xi = x / tess[myTess].dX;
+    int yi = y / tess[myTess].dY;
+    int N = xi + tess[myTess].NX_SUB * yi;
+  
+    *tessID = myTess;
+    *projID = 0;
+    *skycellID = N;
+
+    // *projID = tess[myTess].projID :: NOTE : The projection cell may be non-zero like eg
+    // STS.  However, I'm not certain we are going to implement it like this.
+
+    return TRUE;
+  }
+
+  if (tess[myTess].type == TESS_RINGS) {
+    if (!BoundaryTreeCellCoords (tess[myTess].tree, &zone, &band, ra, dec)) {
+      fprintf (stderr, "mismatch!\n");
+      return FALSE;
+    }
+  
+    // I have ra, dec, and the primary projection cell.  In order to choose the primary skycell,
+    // I just need to project to ra,dec to X,Y based on the center of the cell and then get the subdivision right.
+    
+    double x = 0.0;
+    double y = 0.0;
+    BoundaryTreeProjection (&x, &y, ra, dec, tess[myTess].tree, zone, band);
+  
+    int xi = x / tess[myTess].tree->dX[zone][band];
+    int yi = y / tess[myTess].tree->dY[zone][band];
+    int N = xi + tess[myTess].tree->NX_SUB * yi;
+  
+    *tessID = myTess;
+    *projID = tess[myTess].tree->projID[zone][band];
+    *skycellID = N;
+
+    return TRUE;
+  }
+
+  fprintf (stderr, "invalid tessellation type!\n");
+  return FALSE;
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/coordops.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/coordops.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/coordops.c	(revision 36680)
@@ -1,3 +1,5 @@
 # include <dvo.h>
+
+/* note that Coords.ctype carries the DEC (ctype2) value */
 
 static Coords mosaic;
@@ -621,4 +623,5 @@
 
   /* modifications to the ctype? */
+  /* note that Coords.ctype carries the DEC (ctype2) value */
   OldAIPS = FALSE;
   gfits_modify (header, "CTYPE2",   "%s",  1, coords[0].ctype);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbCheckStack.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbCheckStack.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbCheckStack.c	(revision 36680)
@@ -7,6 +7,4 @@
 // these would be better done using bit values to test for field? temp? float?
 //
-
-#define OPIHI_INT 1
 
 int dbCheckStack (dbStack *stack, int Nstack, int table, dbField **inFields, int *inNfields) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractAverages.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractAverages.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractAverages.c	(revision 36680)
@@ -151,4 +151,14 @@
     case AVE_TRANGE:
       value.Flt = TimeValue (average[0].Trange, 0, TimeFormat);
+      break;
+
+    case AVE_PSF_QF:
+      value.Flt = average[0].psfQF;
+      break;
+    case AVE_PSF_QF_PERF:
+      value.Flt = average[0].psfQFperf;
+      break;
+    case AVE_STARGAL:
+      value.Flt = average[0].stargal;
       break;
 
@@ -201,5 +211,5 @@
 	    }
 	  } else {
-	    value.Flt = PhotdM (field->photcode, average, secfilt);
+	    value.Flt = PhotAveErr (field->photcode, average, secfilt);
 	  }
 	  break;
@@ -233,6 +243,20 @@
 	  break;
 
+	case MAG_APER:
+	case MAG_APER_AVE:
+	  value.Flt = PhotAperAve (field->photcode, average, secfilt);
+	  break;
+
+	case MAG_APER_REF:
+	  value.Flt = PhotAperRef (field->photcode, average, secfilt, measure);
+	  break;
+
 	case MAG_KRON:
+	case MAG_KRON_AVE:
 	  value.Flt = PhotKronAve (field->photcode, average, secfilt);
+	  break;
+
+	case MAG_KRON_REF:
+	  value.Flt = PhotKronRef (field->photcode, average, secfilt, measure);
 	  break;
 
@@ -269,5 +293,5 @@
       break;
     case AVE_dMAG:
-      value.Flt = PhotdM (field->photcode, average, secfilt);
+      value.Flt = PhotAveErr (field->photcode, average, secfilt);
       break;
     case AVE_Xm:
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractMeasures.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 36680)
@@ -123,10 +123,20 @@
 	  case MAG_REL:
 	  case MAG_CAL:
-	  case MAG_APER:
+	  case MAG_ERR:
+
 	  case MAG_APER_INST:
-	  case MAG_KRON:
+	  case MAG_APER_CAT:
+	  case MAG_APER_SYS:
+	  case MAG_APER_REL:
+	  case MAG_APER_CAL:
+	  case MAG_APER_ERR:
+
 	  case MAG_KRON_INST:
+	  case MAG_KRON_CAT:
+	  case MAG_KRON_SYS:
+	  case MAG_KRON_REL:
+	  case MAG_KRON_CAL:
 	  case MAG_KRON_ERR:
-	  case MAG_ERR:
+
 	  case MAG_PHOT_FLAGS:
 	    equiv = myEquiv;
@@ -137,12 +147,23 @@
 	  case MAG_AVE:
 	  case MAG_REF:
+	  case MAG_STDEV:
+	  case MAG_AVE_ERR:
 	  case MAG_CHISQ:
-	  case MAG_AVE_ERR:
+	  case MAG_FLUX_PSF:
+	  case MAG_FLUX_PSF_ERR:
+
+	  case MAG_APER_AVE:
+
+	  case MAG_KRON_AVE:
+	  case MAG_FLUX_KRON:
+	  case MAG_FLUX_KRON_ERR:
+
+	  case MAG_20:
+	  case MAG_80:
+	  case MAG_UC_DIST:
+	  case MAG_STACK_DET_ID:
+
 	  case MAG_NCODE:
 	  case MAG_NPHOT:
-	  case MAG_FLUX_PSF:
-	  case MAG_FLUX_PSF_ERR:
-	  case MAG_FLUX_KRON:
-	  case MAG_FLUX_KRON_ERR:
 	    equiv = field->photcode;
 	    goto valid_photcode;
@@ -178,40 +199,71 @@
 	  value.Flt = PhotRef  (equiv, average, secfilt, measure); 
 	  break;
-	case MAG_APER:
-	  value.Flt = PhotAper (measure); 
-	  break;
+	case MAG_ERR:
+	  value.Flt = measure[0].dM;
+	  break;
+	case MAG_AVE_ERR:
+	  value.Flt = PhotAveErr  (equiv, average, secfilt); 
+	  break;
+
 	case MAG_APER_INST:
 	  value.Flt = PhotAperInst (measure); 
 	  break;
-	case MAG_KRON:
-	  value.Flt = PhotKron (measure); 
-	  break;
+	case MAG_APER_CAT:
+	  value.Flt = PhotAperCat (measure); 
+	  break;
+	case MAG_APER_SYS:
+	  value.Flt = PhotAperSys (measure, average, secfilt); 
+	  break;
+	case MAG_APER:
+	case MAG_APER_REL:
+	  value.Flt = PhotAperRel (measure, average, secfilt); 
+	  break;
+	case MAG_APER_CAL:
+	  value.Flt = PhotAperCal (measure, average, secfilt, measure, equiv); 
+	  break;
+	case MAG_APER_AVE:
+	  value.Flt = PhotAperAve (equiv, average, secfilt);
+	  break;
+	case MAG_APER_REF:
+	  value.Flt = PhotAperRef (equiv, average, secfilt, measure); 
+	  break;
+	case MAG_APER_ERR:
+	  value.Flt = measure[0].dM;
+	  break;
+
 	case MAG_KRON_INST:
 	  value.Flt = PhotKronInst (measure); 
 	  break;
+	case MAG_KRON_CAT:
+	  value.Flt = PhotKronCat (measure); 
+	  break;
+	case MAG_KRON_SYS:
+	  value.Flt = PhotKronSys (measure, average, secfilt); 
+	  break;
+	case MAG_KRON:
+	case MAG_KRON_REL:
+	  value.Flt = PhotKronRel (measure, average, secfilt); 
+	  break;
+	case MAG_KRON_CAL:
+	  value.Flt = PhotKronCal (measure, average, secfilt, measure, equiv); 
+	  break;
+	case MAG_KRON_AVE:
+	  value.Flt = PhotKronAve (equiv, average, secfilt);
+	  break;
+	case MAG_KRON_REF:
+	  value.Flt = PhotKronRef (equiv, average, secfilt, measure); 
+	  break;
 	case MAG_KRON_ERR:
-	  value.Flt = measure[0].dMkron; 
-	  break;
-	case MAG_ERR:
-	  value.Flt = measure[0].dM;
-	  break;
-	case MAG_AVE_ERR:
-	  value.Flt = PhotdM  (equiv, average, secfilt); 
-	  break;
+	  value.Flt = measure[0].dMkron;
+	  break;
+
 	case MAG_PHOT_FLAGS:
 	  if ((field->photcode->type == PHOT_REF) || (field->photcode->type == PHOT_DEP)) {
 	    value.Int = measure[0].photFlags;
-	  } else {
-	    value.Int = 0;
 	  }
 	  break;
-	case MAG_CHISQ:
-	  value.Flt = PhotXm  (equiv, average, secfilt); 
-	  break;
+
 	case MAG_NCODE:
-	  if (equiv == NULL) {
-	    value.Int = 0;
-	    break;
-	  }
+	  if (equiv == NULL) break;
 	  Nsec = GetPhotcodeNsec (equiv->code);
 	  if (Nsec == -1) break;
@@ -219,12 +271,26 @@
 	  break;
 	case MAG_NPHOT:
-	  if (equiv == NULL) {
-	    value.Int = 0;
-	    break;
-	  }
+	  if (equiv == NULL) break;
 	  Nsec = GetPhotcodeNsec (equiv->code);
 	  if (Nsec == -1) break;
 	  value.Int = secfilt[Nsec].Nused;
 	  break;
+
+	case MAG_CHISQ:
+	  value.Flt = PhotXm (equiv, average, secfilt); 
+	  break;
+	case MAG_STDEV:
+	  value.Flt = PhotMstdev (equiv, average, secfilt);
+	  break;
+	case MAG_20:
+	  value.Flt = PhotM20 (equiv, average, secfilt);
+	  break;
+	case MAG_80:
+	  value.Flt = PhotM80 (equiv, average, secfilt);
+	  break;
+	case MAG_UC_DIST:
+	  value.Flt = PhotUCdist (equiv, average, secfilt); 
+	  break;
+
 	case MAG_FLUX_PSF:
 	  value.Flt = PhotAveFluxPSF (field->photcode, average, secfilt);
@@ -531,7 +597,8 @@
 	  fieldc = MatchFieldMetadata (measure[0].imageID);
 	} else {
-	  fprintf (stderr, "non-parallel Xmos broken\n");
-	  abort();
+	  // fprintf (stderr, "non-parallel Xmos broken\n");
+	  // abort();
 	  // fieldc = MatchField (measure[0].t, measure[0].photcode);
+	  fieldc = MatchFieldMetadata (measure[0].imageID);
 	}
 	if (fieldc == NULL) break;
@@ -547,7 +614,8 @@
 	  fieldc = MatchFieldMetadata (measure[0].imageID);
 	} else {
-	  fprintf (stderr, "non-parallel Xmos broken\n");
-	  abort();
+	  // fprintf (stderr, "non-parallel Xmos broken\n");
+	  // abort();
 	  // fieldc = MatchField (measure[0].t, measure[0].photcode);
+	  fieldc = MatchFieldMetadata (measure[0].imageID);
 	}
 	if (fieldc == NULL) break;
@@ -564,7 +632,8 @@
 	  mosaic = MatchMosaicMetadata (measure[0].imageID);
 	} else {
-	  fprintf (stderr, "non-parallel Xmos broken\n");
-	  abort();
-	  mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+	  // fprintf (stderr, "non-parallel Xmos broken\n");
+	  // abort();
+	  // mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+	  mosaic = MatchMosaicMetadata (measure[0].imageID);
 	}
 	if (mosaic == NULL) break;
@@ -580,7 +649,8 @@
 	  mosaic = MatchMosaicMetadata (measure[0].imageID);
 	} else {
-	  fprintf (stderr, "non-parallel Xmos broken\n");
-	  abort();
-	  mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+	  // fprintf (stderr, "non-parallel Xmos broken\n");
+	  // abort();
+	  // mosaic = MatchMosaic (measure[0].t, measure[0].photcode);
+	  mosaic = MatchMosaicMetadata (measure[0].imageID);
 	}
 	if (mosaic == NULL) break;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbFields.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbFields.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dbFields.c	(revision 36680)
@@ -30,4 +30,5 @@
 int GetMagMode (char *string) {
 
+  // these all imply PSF mags:
   if (!strcasecmp (string, "inst"))        return (MAG_INST);
   if (!strcasecmp (string, "cat"))         return (MAG_CAT);
@@ -37,14 +38,45 @@
   if (!strcasecmp (string, "ave"))         return (MAG_AVE);
   if (!strcasecmp (string, "ref"))         return (MAG_REF);
+  if (!strcasecmp (string, "err"))         return (MAG_ERR);
+  if (!strcasecmp (string, "aveerr"))      return (MAG_AVE_ERR);
+
+  // these are explicit PSF mags:
+  if (!strcasecmp (string, "psf_inst"))    return (MAG_INST);
+  if (!strcasecmp (string, "psf_cat"))     return (MAG_CAT);
+  if (!strcasecmp (string, "psf_sys"))     return (MAG_SYS);
+  if (!strcasecmp (string, "psf_rel"))     return (MAG_REL);
+  if (!strcasecmp (string, "psf_cal"))     return (MAG_CAL);
+  if (!strcasecmp (string, "psf_ave"))     return (MAG_AVE);
+  if (!strcasecmp (string, "psf_ref"))     return (MAG_REF);
+  if (!strcasecmp (string, "psf_err"))     return (MAG_ERR);
+  if (!strcasecmp (string, "psf_aveerr"))  return (MAG_AVE_ERR);
+
+  // these are aper mags:
   if (!strcasecmp (string, "ap"))          return (MAG_APER);
   if (!strcasecmp (string, "aper"))        return (MAG_APER);
   if (!strcasecmp (string, "aperinst"))    return (MAG_APER_INST);
   if (!strcasecmp (string, "aper_inst"))   return (MAG_APER_INST);
+  if (!strcasecmp (string, "aper_cat"))    return (MAG_APER_CAT);
+  if (!strcasecmp (string, "aper_sys"))    return (MAG_APER_SYS);
+  if (!strcasecmp (string, "aper_rel"))    return (MAG_APER_REL);
+  if (!strcasecmp (string, "aper_cal"))    return (MAG_APER_CAL);
+  if (!strcasecmp (string, "aper_ave"))    return (MAG_APER_AVE);
+  if (!strcasecmp (string, "aper_ref"))    return (MAG_APER_REF);
+  if (!strcasecmp (string, "aper_err"))    return (MAG_APER_ERR);
+
+  // these are kron mags:
   if (!strcasecmp (string, "kron"))        return (MAG_KRON);
+  if (!strcasecmp (string, "kron_inst"))   return (MAG_KRON_INST);
+  if (!strcasecmp (string, "kron_cat"))    return (MAG_KRON_CAT);
+  if (!strcasecmp (string, "kron_sys"))    return (MAG_KRON_SYS);
+  if (!strcasecmp (string, "kron_rel"))    return (MAG_KRON_REL);
+  if (!strcasecmp (string, "kron_cal"))    return (MAG_KRON_CAL);
+  if (!strcasecmp (string, "kron_ave"))    return (MAG_KRON_AVE);
+  if (!strcasecmp (string, "kron_ref"))    return (MAG_KRON_REF);
+  if (!strcasecmp (string, "kron_err"))    return (MAG_KRON_ERR);
   if (!strcasecmp (string, "kroninst"))    return (MAG_KRON_INST);
-  if (!strcasecmp (string, "kron_inst"))   return (MAG_KRON_INST);
   if (!strcasecmp (string, "kronerr"))     return (MAG_KRON_ERR);
-  if (!strcasecmp (string, "err"))         return (MAG_ERR);
-  if (!strcasecmp (string, "aveerr"))      return (MAG_AVE_ERR);
+
+  // other fields:
   if (!strcasecmp (string, "photflags"))   return (MAG_PHOT_FLAGS);
   if (!strcasecmp (string, "flags"))       return (MAG_PHOT_FLAGS);
@@ -314,34 +346,39 @@
   }
 
-  if (!strcasecmp (fieldName, "RA"))        ESCAPE (AVE_RA,        MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "DEC"))       ESCAPE (AVE_DEC,       MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "GLON"))      ESCAPE (AVE_GLON,      MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "GLAT"))      ESCAPE (AVE_GLAT,      MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "ELON"))      ESCAPE (AVE_ELON,      MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "ELAT"))      ESCAPE (AVE_ELAT,      MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "dRA"))       ESCAPE (AVE_RA_ERR,    MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "dDEC"))      ESCAPE (AVE_DEC_ERR,   MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "uRA"))       ESCAPE (AVE_U_RA,      MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "uDEC"))      ESCAPE (AVE_U_DEC,     MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "duRA"))      ESCAPE (AVE_U_RA_ERR,  MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "duDEC"))     ESCAPE (AVE_U_DEC_ERR, MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "PAR"))       ESCAPE (AVE_PAR,       MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "dPAR"))      ESCAPE (AVE_PAR_ERR,   MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "ChiSqPos"))  ESCAPE (AVE_CHISQ_POS, MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "ChiSqPM"))   ESCAPE (AVE_CHISQ_PM,  MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "ChiSqPar"))  ESCAPE (AVE_CHISQ_PAR, MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "Tmean"))     ESCAPE (AVE_TMEAN,     MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "Trange"))    ESCAPE (AVE_TRANGE,    MAG_NONE, OPIHI_FLT);
-  if (!strcasecmp (fieldName, "NMEAS"))     ESCAPE (AVE_NMEAS,     MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "NMISS"))     ESCAPE (AVE_NMISS,     MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "NPOS"))      ESCAPE (AVE_NPOS,      MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "NASTROM"))   ESCAPE (AVE_NPOS,      MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "FLAGS"))     ESCAPE (AVE_OBJ_FLAGS, MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "OBJ_FLAGS")) ESCAPE (AVE_OBJ_FLAGS, MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "OBJFLAGS"))  ESCAPE (AVE_OBJ_FLAGS, MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "OBJID"))     ESCAPE (AVE_OBJID,     MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "CATID"))     ESCAPE (AVE_CATID,     MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "EXTID_HI"))  ESCAPE (AVE_EXTID_HI,  MAG_NONE, OPIHI_INT);
-  if (!strcasecmp (fieldName, "EXTID_LO"))  ESCAPE (AVE_EXTID_LO,  MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "RA"))          ESCAPE (AVE_RA,          MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "DEC"))         ESCAPE (AVE_DEC,         MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "GLON"))        ESCAPE (AVE_GLON,        MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "GLAT"))        ESCAPE (AVE_GLAT,        MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "ELON"))        ESCAPE (AVE_ELON,        MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "ELAT"))        ESCAPE (AVE_ELAT,        MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "dRA"))         ESCAPE (AVE_RA_ERR,      MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "dDEC"))        ESCAPE (AVE_DEC_ERR,     MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "uRA"))         ESCAPE (AVE_U_RA,        MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "uDEC"))        ESCAPE (AVE_U_DEC,       MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "duRA"))        ESCAPE (AVE_U_RA_ERR,    MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "duDEC"))       ESCAPE (AVE_U_DEC_ERR,   MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "PAR"))         ESCAPE (AVE_PAR,         MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "dPAR"))        ESCAPE (AVE_PAR_ERR,     MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "ChiSqPos"))    ESCAPE (AVE_CHISQ_POS,   MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "ChiSqPM"))     ESCAPE (AVE_CHISQ_PM,    MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "ChiSqPar"))    ESCAPE (AVE_CHISQ_PAR,   MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "Tmean"))       ESCAPE (AVE_TMEAN,       MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "Trange"))      ESCAPE (AVE_TRANGE,      MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "psfqf"))       ESCAPE (AVE_PSF_QF,      MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "psfqfperf"))   ESCAPE (AVE_PSF_QF_PERF, MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "psf_qf"))      ESCAPE (AVE_PSF_QF,      MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "psf_qf_perf")) ESCAPE (AVE_PSF_QF_PERF, MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "stargal"))     ESCAPE (AVE_STARGAL,     MAG_NONE, OPIHI_FLT);
+  if (!strcasecmp (fieldName, "NMEAS"))       ESCAPE (AVE_NMEAS,       MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "NMISS"))       ESCAPE (AVE_NMISS,       MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "NPOS"))        ESCAPE (AVE_NPOS,        MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "NASTROM"))     ESCAPE (AVE_NPOS,        MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "FLAGS"))       ESCAPE (AVE_OBJ_FLAGS,   MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "OBJ_FLAGS"))   ESCAPE (AVE_OBJ_FLAGS,   MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "OBJFLAGS"))    ESCAPE (AVE_OBJ_FLAGS,   MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "OBJID"))       ESCAPE (AVE_OBJID,       MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "CATID"))       ESCAPE (AVE_CATID,       MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "EXTID_HI"))    ESCAPE (AVE_EXTID_HI,    MAG_NONE, OPIHI_INT);
+  if (!strcasecmp (fieldName, "EXTID_LO"))    ESCAPE (AVE_EXTID_LO,    MAG_NONE, OPIHI_INT);
 
   // check for code:mode in photcode name 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_catalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_catalog.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_catalog.c	(revision 36680)
@@ -119,4 +119,5 @@
   average->measureOffset   = -1;
   average->catID     	   = 0;
+  average->nOwn     	   = 0;
 }
 
@@ -242,4 +243,5 @@
  measure->dYccd     = 0;
  measure->dRsys     = 0;
+ measure->myDet     = FALSE;
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert.c	(revision 36680)
@@ -65,4 +65,18 @@
   }
 
+  // block to convert broken tables (PS1_V4 made before the Xfix addition)
+  if (!strcmp (extname, "DVO_AVERAGE_PS1_V4") && (ftable[0].header[0].Naxis[0] == 120)) {
+    Average_PS1_V4alt *tmpAverage;
+    tmpAverage = gfits_table_get_Average_PS1_V4alt (ftable, Naverage, NULL);
+    if (!tmpAverage) {
+      fprintf (stderr, "ERROR: failed to read averages\n");
+      exit (2);
+    }
+    average = Average_PS1_V4alt_ToInternal (tmpAverage, *Naverage);
+    free (tmpAverage);
+    *format = DVO_FORMAT_PS1_V4;
+    return (average); 
+  }
+
 # define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
   if (!strcmp (extname, NAME)) { \
@@ -154,4 +168,19 @@
     fprintf (stderr, "EXTNAME missing for measure table\n");
     return (FALSE);
+  }
+
+  // block to convert broken tables (PS1_V4 made before the Xfix addition)
+  if (!strcmp (extname, "DVO_MEASURE_PS1_V4") && (ftable[0].header[0].Naxis[0] == 176)) {
+    fprintf (stderr, "reading alt PS1_V4 format\n");
+    Measure_PS1_V4alt *tmpMeasure;
+    tmpMeasure = gfits_table_get_Measure_PS1_V4alt (ftable, Nmeasure, NULL);
+    if (!tmpMeasure) {
+      fprintf (stderr, "ERROR: failed to read measures\n");
+      exit (2);
+    }
+    measure = Measure_PS1_V4alt_ToInternal (tmpMeasure, *Nmeasure);
+    free (tmpMeasure);
+    *format = DVO_FORMAT_PS1_V4;
+    return (measure); 
   }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert_PS1_V4.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert_PS1_V4.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_convert_PS1_V4.c	(revision 36680)
@@ -481,2 +481,292 @@
   return (out);
 }
+
+/*** there are some mini dvodbs with the wrong PS1_V4 format (missing Xoff,Yoff / Xfix,Yfix) ************/
+
+Measure *Measure_PS1_V4alt_ToInternal (Measure_PS1_V4alt *in, off_t Nvalues) {
+
+  off_t i;
+  Measure *out;
+
+  ALLOCATE_ZERO (out, Measure, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    dvo_measure_init (&out[i]);
+
+    out[i].dR         = in[i].dR;
+    out[i].dD         = in[i].dD;
+    out[i].M          = in[i].M;
+    out[i].Mcal       = in[i].Mcal;
+    out[i].Map        = in[i].Map;
+    out[i].Mkron      = in[i].Mkron;
+    out[i].dMkron     = in[i].dMkron;
+    out[i].dM         = in[i].dM;
+    out[i].dMcal      = in[i].dMcal;
+    out[i].dt         = in[i].dt;
+    out[i].FluxPSF    = in[i].FluxPSF;
+    out[i].dFluxPSF   = in[i].dFluxPSF;
+    out[i].FluxKron   = in[i].FluxKron;
+    out[i].dFluxKron  = in[i].dFluxKron;
+    out[i].airmass    = in[i].airmass;
+    out[i].az         = in[i].az;
+    out[i].Xccd       = in[i].Xccd;
+    out[i].Yccd       = in[i].Yccd;
+    out[i].Xfix       = in[i].Xccd;
+    out[i].Yfix       = in[i].Yccd;
+    out[i].Sky        = in[i].Sky;
+    out[i].dSky       = in[i].dSky;
+    out[i].t          = in[i].t;
+    out[i].t_msec     = in[i].t_msec;
+    out[i].averef     = in[i].averef;
+    out[i].detID      = in[i].detID;
+    out[i].imageID    = in[i].imageID;
+    out[i].objID      = in[i].objID;
+    out[i].catID      = in[i].catID;
+    out[i].extID      = in[i].extID;
+    out[i].psfQF      = in[i].psfQF;
+    out[i].psfQFperf  = in[i].psfQFperf;
+    out[i].psfChisq   = in[i].psfChisq;
+    out[i].psfNdof    = in[i].psfNdof;
+    out[i].psfNpix    = in[i].psfNpix;
+    out[i].crNsigma   = in[i].crNsigma;
+    out[i].extNsigma  = in[i].extNsigma;
+    out[i].FWx 	      = in[i].FWx;
+    out[i].FWy 	      = in[i].FWy;
+    out[i].theta      = in[i].theta;
+    out[i].Mxx 	      = in[i].Mxx;
+    out[i].Mxy 	      = in[i].Mxy;
+    out[i].Myy 	      = in[i].Myy;
+    out[i].dXccd      = in[i].dXccd;
+    out[i].dYccd      = in[i].dYccd;
+    out[i].dRsys      = in[i].dRsys;
+    out[i].posangle   = in[i].posangle;
+    out[i].pltscale   = in[i].pltscale;
+    out[i].photcode   = in[i].photcode;
+    out[i].dbFlags    = in[i].dbFlags;
+    out[i].photFlags  = in[i].photFlags;
+  }
+  return (out);
+}
+
+// XXX note that there are 2 bad versions of PS1_V4 : the other one does have have PSF_QF_PERFECT, but has PAD instead
+int gfits_convert_Measure_PS1_V4alt (Measure_PS1_V4alt *data, off_t size, off_t nitems) {
+
+  off_t i;
+  unsigned char *byte, tmp;
+
+  if (size != 176) { 
+    fprintf (stderr, "WARNING: mismatch in data types Measure_PS1_V4alt: "OFF_T_FMT" vs %d\n",  size,  176);
+    return (FALSE);
+  }
+
+  /* provide initial values to avoid compiler warnings for non-BYTE_SWAP arch */
+  i = tmp = 0;
+  byte = NULL;
+
+# ifdef BYTE_SWAP
+  byte = (unsigned char *) data;
+  for (i = 0; i < nitems; i++, byte += 176) {
+    /** BYTE SWAP **/
+    SWAP_WORD (0); // D_RA
+    SWAP_WORD (4); // D_DEC
+    SWAP_WORD (8); // MAG
+    SWAP_WORD (12); // M_CAL
+    SWAP_WORD (16); // M_APER
+    SWAP_WORD (20); // M_KRON
+    SWAP_WORD (24); // M_KRON_ERR
+    SWAP_WORD (28); // MAG_ERR
+    SWAP_WORD (32); // MAG_CAL_ERR
+    SWAP_WORD (36); // M_TIME
+    SWAP_WORD (40); // FLUX_PSF
+    SWAP_WORD (44); // FLUX_PSF_ERR
+    SWAP_WORD (48); // FLUX_KRON
+    SWAP_WORD (52); // FLUX_KRON_ERR
+    SWAP_WORD (56); // AIRMASS
+    SWAP_WORD (60); // AZ
+    SWAP_WORD (64); // X_CCD
+    SWAP_WORD (68); // Y_CCD
+    SWAP_WORD (72); // SKY_FLUX	   
+    SWAP_WORD (76); // SKY_FLUX_ERR
+    SWAP_WORD (80); // TIME
+    SWAP_WORD (84); // AVE_REF
+    SWAP_WORD (88); // DET_ID
+    SWAP_WORD (92); // IMAGE_ID
+    SWAP_WORD (96); // OBJ_ID
+    SWAP_WORD (100); // CAT_ID
+    SWAP_DBLE (104); // EXT_ID
+    SWAP_WORD (112); // PSF_QF	      
+    SWAP_WORD (116); // PSF_QF_PERFECT 
+    SWAP_WORD (120); // PSF_CHISQ     
+    SWAP_WORD (124); // PSF_NDOF      
+    SWAP_WORD (128); // PSF_NPIX      
+    SWAP_WORD (132); // CR_NSIGMA     
+    SWAP_WORD (136); // EXT_NSIGMA    
+    SWAP_BYTE (140); // FWHM_MAJOR 
+    SWAP_BYTE (142); // FWHM_MINOR 
+    SWAP_BYTE (144); // PSF_THETA  
+    SWAP_BYTE (146); // MXX	   
+    SWAP_BYTE (148); // MXY	   
+    SWAP_BYTE (150); // MYY	   
+    SWAP_BYTE (152); // TIME_MSEC  
+    SWAP_BYTE (154); // PHOTCODE   
+    SWAP_BYTE (156); // X_CCD_ERR  
+    SWAP_BYTE (158); // Y_CCD_ERR  
+    SWAP_BYTE (160); // POS_SYS_ERR
+    SWAP_BYTE (162); // POSANGLE   
+    SWAP_WORD (164); // PLTSCALE  
+    SWAP_WORD (168); // DB_FLAGS  
+    SWAP_WORD (172); // PHOT_FLAGS
+  }
+# endif  
+
+  return (TRUE);
+} 
+
+/*** add test of EXTNAME and header-defined columns? ***/
+/* return internal structure representation */
+Measure_PS1_V4alt *gfits_table_get_Measure_PS1_V4alt (FTable *ftable, off_t *Ndata, char *swapped) {
+
+  int Ncols;
+  Measure_PS1_V4alt *data;
+
+  Ncols = ftable[0].header[0].Naxis[0];
+  if (Ncols != 176) {
+    fprintf (stderr, "ERROR: mis-match in table size: width is %d but should be %d bytes\n", Ncols, 176);
+    return NULL;
+  }
+
+  *Ndata = ftable[0].header[0].Naxis[1];
+  data = (Measure_PS1_V4alt *) ftable[0].buffer;
+  if ((swapped == NULL) || (*swapped == FALSE)) {
+    if (!gfits_convert_Measure_PS1_V4alt (data, sizeof (Measure_PS1_V4alt), *Ndata)) {
+      return NULL;
+    }
+    gfits_table_scale_data (ftable);
+    if (swapped != NULL) *swapped = TRUE;
+  }
+  return (data);
+}
+
+// 'primary' is needed to conform with the API for Loneos and Elixir, but is not used
+Average *Average_PS1_V4alt_ToInternal (Average_PS1_V4alt *in, off_t Nvalues) {
+
+  off_t i;
+  Average *out;
+
+  ALLOCATE_ZERO (out, Average, Nvalues);
+
+  for (i = 0; i < Nvalues; i++) {
+    dvo_average_init (&out[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].ChiSqAve    	 = in[i].ChiSqAve;     
+    out[i].ChiSqPM    	 = in[i].ChiSqPM;     
+    out[i].ChiSqPar    	 = in[i].ChiSqPar;     
+    out[i].Tmean    	 = in[i].Tmean;     
+    out[i].Trange   	 = in[i].Trange;     
+    out[i].Npos       	 = in[i].Npos;     
+    out[i].Nmeasure      = in[i].Nmeasure;     
+    out[i].Nmissing      = in[i].Nmissing;     
+    out[i].Nextend       = in[i].Nextend;     
+    out[i].measureOffset = in[i].measureOffset; 
+    out[i].missingOffset = in[i].missingOffset;
+    out[i].extendOffset  = in[i].extendOffset;
+    out[i].flags     	 = in[i].flags;   
+    out[i].photFlagsUpper = in[i].photFlagsUpper;   
+    out[i].photFlagsLower = in[i].photFlagsLower;   
+    out[i].objID 	 = in[i].objID;
+    out[i].catID 	 = in[i].catID;
+    out[i].extID 	 = in[i].extID;
+  }
+  return (out);
+}
+
+/******/
+
+int gfits_convert_Average_PS1_V4alt (Average_PS1_V4alt *data, off_t size, off_t nitems) {
+
+  off_t i;
+  unsigned char *byte, tmp;
+
+  if (size != 120) { 
+    fprintf (stderr, "WARNING: mismatch in data types Average_PS1_V4alt: "OFF_T_FMT" vs %d\n",  size,  120);
+    return (FALSE);
+  }
+
+  /* provide initial values to avoid compiler warnings for non-BYTE_SWAP arch */
+  i = tmp = 0;
+  byte = NULL;
+
+# ifdef BYTE_SWAP
+  byte = (unsigned char *) data;
+  for (i = 0; i < nitems; i++, byte += 120) {
+    /** BYTE SWAP **/
+    SWAP_DBLE (0); // RA
+    SWAP_DBLE (8); // DEC
+    SWAP_WORD (16); // RA_ERR
+    SWAP_WORD (20); // DEC_ERR
+    SWAP_WORD (24); // U_RA
+    SWAP_WORD (28); // U_DEC
+    SWAP_WORD (32); // V_RA_ERR
+    SWAP_WORD (36); // V_DEC_ERR
+    SWAP_WORD (40); // PAR
+    SWAP_WORD (44); // PAR_ERR
+    SWAP_WORD (48); // CHISQ_POS
+    SWAP_WORD (52); // CHISQ_PM
+    SWAP_WORD (56); // CHISQ_PAP
+    SWAP_WORD (60); // MEAN_EPOCH
+    SWAP_WORD (64); // TIME_RANGE
+    SWAP_WORD (68); // SIGMA
+    SWAP_BYTE (72); // NUMBER_POS
+    SWAP_BYTE (74); // NMEASURE
+    SWAP_BYTE (76); // NMISSING
+    SWAP_BYTE (78); // NEXTEND
+    SWAP_WORD (80); // OFF_MEASURE
+    SWAP_WORD (84); // OFF_MISSING
+    SWAP_WORD (88); // OFF_EXTEND
+    SWAP_WORD (92); // FLAGS
+    SWAP_WORD (96); // PHOTFLAGS_U
+    SWAP_WORD (100); // PHOTFLAGS_L
+    SWAP_WORD (104); // OBJ_ID
+    SWAP_WORD (108); // CAT_ID
+    SWAP_DBLE (112); // EXT_ID
+  }
+# endif  
+
+  return (TRUE);
+} 
+
+/*** add test of EXTNAME and header-defined columns? ***/
+/* return internal structure representation */
+Average_PS1_V4alt *gfits_table_get_Average_PS1_V4alt (FTable *ftable, off_t *Ndata, char *swapped) {
+
+  int Ncols;
+  Average_PS1_V4alt *data;
+
+  Ncols = ftable[0].header[0].Naxis[0];
+  if (Ncols != 120) {
+    fprintf (stderr, "ERROR: mis-match in table size: width is %d but should be %d bytes\n", Ncols, 120);
+    return NULL;
+  }
+
+  *Ndata = ftable[0].header[0].Naxis[1];
+  data = (Average_PS1_V4alt *) ftable[0].buffer;
+  if ((swapped == NULL) || (*swapped == FALSE)) {
+    if (!gfits_convert_Average_PS1_V4alt (data, sizeof (Average_PS1_V4alt), *Ndata)) {
+      return NULL;
+    }
+    gfits_table_scale_data (ftable);
+    if (swapped != NULL) *swapped = TRUE;
+  }
+  return (data);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_photcode_ops.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_photcode_ops.c	(revision 36680)
@@ -169,4 +169,5 @@
 }
 
+// returns Nsec if code is PRI/SEC, else -1
 int GetPhotcodeNsec (int code) {
   
@@ -259,76 +260,4 @@
 }
 
-float PhotAper (Measure *measure) {
-
-  int Np;
-  float Mcat;
-  PhotCode *code;
-
-  Np = photcodes[0].hashcode[measure[0].photcode];
-  if (Np == -1) return (NAN);
-
-  if (photcodes[0].code[Np].type == PHOT_REF) {
-    Mcat = measure[0].Map;
-    return (Mcat);
-  }
-  code = &photcodes[0].code[Np];
-  Mcat = measure[0].Map - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
-  
-  return (Mcat);
-}
-
-float PhotAperInst (Measure *measure) {
-
-  int Np;
-  float Minst;
-
-  Np = photcodes[0].hashcode[measure[0].photcode];
-  if (Np == -1) return (NAN);
-
-  if (photcodes[0].code[Np].type == PHOT_REF) {
-    Minst = measure[0].Map;
-    return (Minst);
-  }
-  Minst = measure[0].Map - measure[0].dt - ZERO_POINT;
-  
-  return (Minst);
-}
-
-float PhotKron (Measure *measure) {
-
-  int Np;
-  float Mcat;
-  PhotCode *code;
-
-  Np = photcodes[0].hashcode[measure[0].photcode];
-  if (Np == -1) return (NAN);
-
-  if (photcodes[0].code[Np].type == PHOT_REF) {
-    Mcat = measure[0].Mkron;
-    return (Mcat);
-  }
-  code = &photcodes[0].code[Np];
-  Mcat = measure[0].Mkron - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
-  
-  return (Mcat);
-}
-
-float PhotKronInst (Measure *measure) {
-
-  int Np;
-  float Minst;
-
-  Np = photcodes[0].hashcode[measure[0].photcode];
-  if (Np == -1) return (NAN);
-
-  if (photcodes[0].code[Np].type == PHOT_REF) {
-    Minst = measure[0].Mkron;
-    return (Minst);
-  }
-  Minst = measure[0].Mkron - measure[0].dt - ZERO_POINT;
-  
-  return (Minst);
-}
-
 float PhotSys (Measure *measure, Average *average, SecFilt *secfilt) {
 
@@ -362,23 +291,4 @@
 }
 
-float PhotZeroPoint (Measure *measure, Average *average, SecFilt *secfilt) {
-
-  int Np;
-  float ZP;
-  PhotCode *code;
-
-  Np = photcodes[0].hashcode[measure[0].photcode];
-  if (Np == -1) return (NAN);
-
-  if (photcodes[0].code[Np].type == PHOT_REF) {
-    ZP = 0.0;
-    return (ZP);
-  }
-  code = &photcodes[0].code[Np];
-  ZP = code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
-
-  return (ZP);
-}
-
 float PhotRel (Measure *measure, Average *average, SecFilt *secfilt) {
 
@@ -444,4 +354,435 @@
   Mcal += Mcol;
   return (Mcal);
+}
+
+/***/
+float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float Mave;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  return (Mave);
+}
+
+/* return calibrated magnitude from average/secfilt for given photcode */
+float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Ns;
+  float Mave, Mref, Mcol, mc;
+  double Mc;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mref);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mref += Mcol;
+  return (Mref);
+}
+
+float PhotAveErr (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float dM;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  dM  = (Ns == -1) ? NAN : secfilt[Ns].dM;
+  return (dM);
+}
+
+/************************************** APERTURE-related Magnitudes ***********************************************/
+
+float PhotAperInst (Measure *measure) {
+
+  int Np;
+  float Minst;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Minst = measure[0].Map;
+    return (Minst);
+  }
+  Minst = measure[0].Map - measure[0].dt - ZERO_POINT;
+  
+  return (Minst);
+}
+
+float PhotAperCat (Measure *measure) {
+
+  int Np;
+  float Mcat;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].Map;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].Map - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+  
+  return (Mcat);
+}
+
+float PhotAperSys (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Msys, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].Map;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].Map - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+  
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mcat);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Msys = Mcat + Mcol;
+  return (Msys);
+}
+
+float PhotAperRel (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Mrel, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].Map;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mrel = measure[0].Map - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C - measure[0].Mcal;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mrel);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mrel += Mcol;
+  return (Mrel);
+}
+
+/* return calibrated magnitude from measure for given photcode */
+float PhotAperCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Np; 
+  float Mcal, Mrel, Mcol, mc, Mc;
+
+  if (code == NULL) return NAN;
+
+  /* code must be the matching PRI/SEC code for this measurement or an equivalent ALT */
+  Np = photcodes[0].hashcode[thisone[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mrel = thisone[0].Map;
+    return (Mrel);
+  }
+  if (code[0].code != photcodes[0].code[Np].equiv) return (NAN);
+
+  Mcal = PhotAperRel (thisone, average, secfilt) + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mcal);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Mcal += Mcol;
+  return (Mcal);
+}
+
+float PhotAperAve (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float Maper;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Maper = (Ns == -1) ? NAN : secfilt[Ns].Map;
+  return (Maper);
+}
+
+/* return calibrated magnitude from average/secfilt for given photcode */
+float PhotAperRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Ns;
+  float Mave, Mref, Mcol, mc;
+  double Mc;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].Map;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mref);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mref += Mcol;
+  return (Mref);
+}
+
+/************************************** KRON-related Magnitudes ***********************************************/
+
+float PhotKronInst (Measure *measure) {
+
+  int Np;
+  float Minst;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Minst = measure[0].Mkron;
+    return (Minst);
+  }
+  Minst = measure[0].Mkron - measure[0].dt - ZERO_POINT;
+  
+  return (Minst);
+}
+
+float PhotKronCat (Measure *measure) {
+
+  int Np;
+  float Mcat;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].Mkron;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].Mkron - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+  
+  return (Mcat);
+}
+
+float PhotKronSys (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Msys, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Msys = measure[0].Mkron;
+    return (Msys);
+  }
+  code = &photcodes[0].code[Np];
+  Mcat = measure[0].Mkron - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mcat);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Msys = Mcat + Mcol;
+  return (Msys);
+}
+
+float PhotKronRel (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int i, Np;
+  float Mcat, Mcol, Mrel, mc, Mc;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mcat = measure[0].Mkron;
+    return (Mcat);
+  }
+  code = &photcodes[0].code[Np];
+  Mrel = measure[0].Mkron - ZERO_POINT + code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C - measure[0].Mcal;
+
+  /* for DEP, color must be made of PRI/SEC */
+  mc = PhotColorForCode (average, secfilt, NULL, code);
+  if (isnan(mc)) return (Mrel);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mrel += Mcol;
+  return (Mrel);
+}
+
+/* return calibrated magnitude from measure for given photcode */
+float PhotKronCal (Measure *thisone, Average *average, SecFilt *secfilt, Measure *measure, PhotCode *code) {
+
+  int i, Np; 
+  float Mcal, Mrel, Mcol, mc, Mc;
+
+  if (code == NULL) return NAN;
+
+  /* code must be the matching PRI/SEC code for this measurement or an equivalent ALT */
+  Np = photcodes[0].hashcode[thisone[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    Mrel = thisone[0].Mkron;
+    return (Mrel);
+  }
+  if (code[0].code != photcodes[0].code[Np].equiv) return (NAN);
+
+  Mcal = PhotKronRel (thisone, average, secfilt) + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mcal);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;
+  }
+  Mcal += Mcol;
+  return (Mcal);
+}
+
+float PhotKronAve (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float Mkron;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mkron = (Ns == -1) ? NAN : secfilt[Ns].Mkron;
+  return (Mkron);
+}
+
+/* return calibrated magnitude from average/secfilt for given photcode */
+float PhotKronRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
+
+  int i, Ns;
+  float Mave, Mref, Mcol, mc;
+  double Mc;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  Mave = (Ns == -1) ? NAN : secfilt[Ns].Mkron;
+  Mref = Mave + SCALE*code[0].C;
+
+  mc = PhotColorForCode (average, secfilt, measure, code);
+  if (isnan(mc)) return (Mref);
+  mc = mc - SCALE*code[0].dX;
+
+  Mc = mc;
+  Mcol = 0;
+  for (i = 0; i < code[0].Nc; i++) {
+    Mcol += code[0].X[i]*Mc;
+    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
+  }
+  Mref += Mcol;
+  return (Mref);
+}
+
+float PhotKronAveErr (PhotCode *code, Average *average, SecFilt *secfilt) {
+
+  int Ns;
+  float dMkron;
+
+  if (code == NULL) return NAN;
+
+  Ns = photcodes[0].hashNsec[code[0].code];
+  dMkron = (Ns == -1) ? NAN : secfilt[Ns].dMkron;
+  return (dMkron);
+}
+
+/********************* other support functions ********************************/
+
+float PhotZeroPoint (Measure *measure, Average *average, SecFilt *secfilt) {
+
+  int Np;
+  float ZP;
+  PhotCode *code;
+
+  Np = photcodes[0].hashcode[measure[0].photcode];
+  if (Np == -1) return (NAN);
+
+  if (photcodes[0].code[Np].type == PHOT_REF) {
+    ZP = 0.0;
+    return (ZP);
+  }
+  code = &photcodes[0].code[Np];
+  ZP = code[0].K*(measure[0].airmass - 1.000) + SCALE*code[0].C;
+
+  return (ZP);
 }
 
@@ -496,56 +837,4 @@
 }
 
-/* return calibrated magnitude from average/secfilt for given photcode */
-float PhotRef (PhotCode *code, Average *average, SecFilt *secfilt, Measure *measure) {
-
-  int i, Ns;
-  float Mave, Mref, Mcol, mc;
-  double Mc;
-
-  if (code == NULL) return NAN;
-
-  Ns = photcodes[0].hashNsec[code[0].code];
-  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
-  Mref = Mave + SCALE*code[0].C;
-
-  mc = PhotColorForCode (average, secfilt, measure, code);
-  if (isnan(mc)) return (Mref);
-  mc = mc - SCALE*code[0].dX;
-
-  Mc = mc;
-  Mcol = 0;
-  for (i = 0; i < code[0].Nc; i++) {
-    Mcol += code[0].X[i]*Mc;
-    Mc *= mc;    /* the 0.001 is needed for higher order terms to keep the units mag = mag^n */
-  }
-  Mref += Mcol;
-  return (Mref);
-}
-
-/***/
-float PhotAve (PhotCode *code, Average *average, SecFilt *secfilt) {
-
-  int Ns;
-  float Mave;
-
-  if (code == NULL) return NAN;
-
-  Ns = photcodes[0].hashNsec[code[0].code];
-  Mave = (Ns == -1) ? NAN : secfilt[Ns].M;
-  return (Mave);
-}
-
-float PhotdM (PhotCode *code, Average *average, SecFilt *secfilt) {
-
-  int Ns;
-  float dM;
-
-  if (code == NULL) return NAN;
-
-  Ns = photcodes[0].hashNsec[code[0].code];
-  dM  = (Ns == -1) ? NAN : secfilt[Ns].dM;
-  return (dM);
-}
-
 float PhotMstdev (PhotCode *code, Average *average, SecFilt *secfilt) {
 
@@ -558,28 +847,4 @@
   Mstdev  = (Ns == -1) ? NAN : 0.001*secfilt[Ns].Mstdev;
   return (Mstdev);
-}
-
-float PhotKronAve (PhotCode *code, Average *average, SecFilt *secfilt) {
-
-  int Ns;
-  float Mkron;
-
-  if (code == NULL) return NAN;
-
-  Ns = photcodes[0].hashNsec[code[0].code];
-  Mkron = (Ns == -1) ? NAN : secfilt[Ns].Mkron;
-  return (Mkron);
-}
-
-float PhotKronAveErr (PhotCode *code, Average *average, SecFilt *secfilt) {
-
-  int Ns;
-  float dMkron;
-
-  if (code == NULL) return NAN;
-
-  Ns = photcodes[0].hashNsec[code[0].code];
-  dMkron = (Ns == -1) ? NAN : secfilt[Ns].dMkron;
-  return (dMkron);
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_tiny_values.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_tiny_values.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvo_tiny_values.c	(revision 36680)
@@ -8,4 +8,6 @@
   averageT[0].measureOffset = average[0].measureOffset;
   averageT[0].catID         = average[0].catID;
+  averageT[0].objID         = average[0].objID;
+  averageT[0].nOwn          = -1;
   return;
 }
@@ -33,4 +35,5 @@
   measureT[0].dYccd      = measure[0].dYccd;
   measureT[0].dRsys      = measure[0].dRsys;
+  measureT[0].myDet      = 0;
   return ;
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvosorts.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvosorts.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libdvo/src/dvosorts.c	(revision 36680)
@@ -60,4 +60,19 @@
 
 /* sort a coordinate pair (X,Y) and the associated index (S) */
+void sort_IDs_indexonly (opihi_int *X, off_t *S, off_t N) {
+  
+# define SWAPFUNC(A,B){ off_t itmp; \
+  itmp = S[A]; S[A] = S[B]; S[B] = itmp; \
+}
+# define COMPARE(A,B)(X[S[A]] < X[S[B]])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort a coordinate pair (X,Y) and the associated index (S) */
 void sort_coords_indexonly (double *X, double *Y, off_t *S, off_t N) {
   
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libfits/table/F_get_column.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libfits/table/F_get_column.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libfits/table/F_get_column.c	(revision 36680)
@@ -174,118 +174,13 @@
 int gfits_get_bintable_column (Header *header, FTable *table, char *label, void **data) {
 
-  off_t Nx, Ny;
-  int i, N, Nfields, Nval, Nbytes, Nstart, Nv, Nb;
-  char tlabel[80], field[80], format[80], type[16], tmpline[16];
-  char *Pin, *Pout, *array;
-  double Bscale, Bzero;
-
-  if (label == (char *) NULL) return (FALSE);
-  if (label[0] == 0) return (FALSE);
-
-  /* find label in header */
-  tlabel[0] = 0;
-  gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
-  for (i = 1; strcasecmp (label, tlabel) && (i < Nfields + 1); i++) {
-    sprintf (field, "TTYPE%d", i);
-    gfits_scan (header, field, "%s", 1, tlabel);
-  }
-  if (strcasecmp (label, tlabel)) return (FALSE);
-  N = i - 1;
-
-  Bscale = 1; 
-  Bzero  = 0;
-
-  /* interpret format */
-  sprintf (field, "TSCAL%d", N);
-  gfits_scan (header, field, "%lf", 1, &Bscale);
-  sprintf (field, "TZERO%d", N);
-  gfits_scan (header, field, "%lf", 1, &Bzero);
-  sprintf (field, "TFORM%d", N);
-  gfits_scan (header, field, "%s", 1, format);
-
-  if (!gfits_bintable_format (format, type, &Nval, &Nbytes)) return (FALSE);
-  
-  /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
-  gfits_scan (header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
-
-  /* scan columns to find insert point */
-  Nstart = 0;
-  for (i = 1; i < N; i++) {
-    sprintf (field, "TFORM%d", i);
-    gfits_scan (header, field, "%s", 1, format);
-    gfits_bintable_format (format, tmpline, &Nv, &Nb);
-    Nstart += Nv*Nb;
-  }
-
-  /* extract bytes from table into temporary array */
-  ALLOCATE (array, char, Nbytes*Nval*Ny);
-  Pin  = table[0].buffer + Nstart;
-  Pout = array;
-  for (i = 0; i < Ny; i++, Pin += Nx, Pout += Nval*Nbytes) {
-    memcpy (Pout, Pin, Nval*Nbytes);
-  }
-
-  /* convert data in-situ with correct type, byte swap and Bzero/Bscale */
-  Pin  = array;
-  Pout = array;
-  if (!strcmp (type, "char")) {
-    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-      *(char *)Pout = *(char *)Pin*Bscale + Bzero;
-    }
-  }
-  if (!strcmp (type, "short")) {
-    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-      SWAP_BYTE;
-# endif
-      *(short *)Pout = *(short *)Pin*Bscale + Bzero;
-    }  
-  }
-  if (!strcmp (type, "int")) {
-    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-      SWAP_WORD;
-# endif
-      *(int *)Pout = *(int *)Pin*Bscale + Bzero;
-    }
-  }
-  if (!strcmp (type, "int64_t")) {
-    // XXX 64 bit int operations with Bzero & Bscale are inaccurate even with doubles
-    if ((Bzero == 0.0) && (Bscale == 1.0)) {
-      for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-	SWAP_DBLE;
-# endif
-	*(int64_t *)Pout = *(int64_t *)Pin;
-      }
-    } else {
-      for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-	SWAP_DBLE;
-# endif
-	*(int64_t *)Pout = *(int64_t *)Pin*Bscale + Bzero;
-      }
-    }
-  }
-  if (!strcmp (type, "float")) {
-    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-      SWAP_WORD;
-# endif
-      *(float *)Pout = *(float *)Pin*Bscale + Bzero;
-    }
-  }
-  if (!strcmp (type, "double")) {
-    for (i = 0; i < Nval*Ny; i++, Pin+=Nbytes, Pout+=Nbytes) {
-# ifdef BYTE_SWAP
-      SWAP_DBLE;
-# endif
-      *(double *)Pout = *(double *)Pin*Bscale + Bzero;
-    }
-  }
+  char type[16];
+  off_t Nrow;
+  int Ncol;
+
+  char *array = gfits_get_bintable_column_data (header, table, label, type, &Nrow, &Ncol);
+  if (array == NULL) return (FALSE);
 
   *data = array;
-  return (TRUE);
+  return TRUE;
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/IOfuncs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/IOfuncs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/IOfuncs.c	(revision 36680)
@@ -54,5 +54,8 @@
   va_end (argp);
 
-  if (!Nbyte) return (FALSE);
+  if (!Nbyte) {
+    KiiSendCommand (device, 16, "LEN: %11d", 0);
+    return (FALSE);
+  }
 
   /* the message may contain up to 99,999,999,999 bytes (100MB) */
@@ -84,6 +87,11 @@
   /* find the message length, allocate space */
   sscanf (buffer, "%*s %d", &Nbytes);
+  if (Nbytes == 0) {
+    return TRUE;
+  }
+
   ALLOCATE (message, char, Nbytes + 1);
   memset (message, 0, Nbytes + 1);
+
 
   /* read Nbytes from the device */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/KapaWindow.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/KapaWindow.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/KapaWindow.c	(revision 36680)
@@ -100,5 +100,5 @@
   graphdata[0].coords.pc1_1 = graphdata[0].coords.pc2_2 = 1.0;
   graphdata[0].coords.pc1_2 = graphdata[0].coords.pc2_1 = 0.0;
-  strcpy (graphdata[0].coords.ctype, "RA---LIN");
+  strcpy (graphdata[0].coords.ctype, "DEC--LIN");
   graphdata[0].coords.crval1 = 0.0;
   graphdata[0].coords.crval2 = 0.0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/bDrawFuncs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/bDrawFuncs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libkapa/src/bDrawFuncs.c	(revision 36680)
@@ -247,5 +247,10 @@
   int i;
 
+  if (Y <  0) return;
+  if (Y >= buffer[0].Ny) return;
+
   for (i = X1; i < X2; i++) {
+    if (i <  0) continue;
+    if (i >= buffer[0].Nx) continue;
     if (buffer[0].Nbyte == 1) {
       buffer[0].pixels[Y][i] = buffer->bColor;
@@ -263,5 +268,10 @@
   int i;
 
+  if (X <  0) return;
+  if (X >= buffer[0].Nx) return;
+
   for (i = Y1; i < Y2; i++) {
+    if (i <  0) continue;
+    if (i >= buffer[0].Ny) continue;
     if (buffer[0].Nbyte == 1) {
       buffer[0].pixels[i][X] = buffer->bColor;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/libohana/src/gaussj.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/libohana/src/gaussj.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/libohana/src/gaussj.c	(revision 36680)
@@ -1,5 +1,5 @@
 # include <ohana.h>
 # define GROWTHTEST 0
-# define MAX_RANGE 1.0e7
+# define MAX_RANGE 1.0e9
 
 // Gauss-Jordan elimination using full pivots based on Press et al's description.  Substantially
@@ -91,4 +91,5 @@
     /* rescale by pivot reciprocal */
     double tmpval = 1.0 / A[maxcol][maxcol];
+    // XXX why is this here (don't I double count this element?) A[maxcol][maxcol] = 1.0;
     A[maxcol][maxcol] = 1.0;
     for (col = 0; col < N; col++) A[maxcol][col] *= tmpval;
@@ -117,4 +118,15 @@
     }
   }
+
+# if (GROWTHTEST)
+    fprintf (stderr, "final A matrix:\n");
+    for (row = 0; row < N; row++) {
+	for (col = 0; col < N; col++) {
+	    fprintf (stderr, "%10.3e ", A[row][col]);
+	}
+	fprintf (stderr, "\n");
+    }
+    fprintf (stderr, "\n");
+# endif
 
   // swap back the inverse matrix based on the row swaps above
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/markrock/src/gcatstats.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/markrock/src/gcatstats.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/markrock/src/gcatstats.c	(revision 36680)
@@ -40,5 +40,5 @@
   catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
   catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
-  strcpy (catstats[0].coords.ctype, "RA---TAN");
+  strcpy (catstats[0].coords.ctype, "DEC--TAN");
 
   X1 = catstats[0].X;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/markstar/src/gcatstats.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/markstar/src/gcatstats.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/markstar/src/gcatstats.c	(revision 36680)
@@ -50,5 +50,5 @@
   catstats[0].coords.pc1_1 = catstats[0].coords.pc2_2 = 1.0;
   catstats[0].coords.pc1_2 = catstats[0].coords.pc2_1 = 0.0;
-  strcpy (catstats[0].coords.ctype, "RA---TAN");
+  strcpy (catstats[0].coords.ctype, "DEC--TAN");
 
   X1 = catstats[0].X;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/mosastro/src/warptest.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/mosastro/src/warptest.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/mosastro/src/warptest.c	(revision 36680)
@@ -15,5 +15,5 @@
 
   /* bore site center guess */
-  sprintf (coords.ctype, "RA---%s", argv[1]);
+  sprintf (coords.ctype, "DEC--%s", argv[1]);
   coords.crval1 = 0.0;
   coords.crval2 = 0.0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/Makefile	(revision 36680)
@@ -28,7 +28,9 @@
 $(SRC)/cval.$(ARCH).o		   \
 $(SRC)/czplot.$(ARCH).o	   \
+$(SRC)/cdensify.$(ARCH).o	   \
 $(SRC)/drizzle.$(ARCH).o	   \
 $(SRC)/flux.$(ARCH).o		   \
 $(SRC)/fitplx.$(ARCH).o	   \
+$(SRC)/fitpm.$(ARCH).o	   \
 $(SRC)/fixwrap.$(ARCH).o	   \
 $(SRC)/fixcols.$(ARCH).o	   \
@@ -60,4 +62,5 @@
 $(SRC)/scale.$(ARCH).o		   \
 $(SRC)/sexigesimal.$(ARCH).o	   \
+$(SRC)/sersic.$(ARCH).o	   \
 $(SRC)/spec.$(ARCH).o		   \
 $(SRC)/specpairfit.$(ARCH).o	   \
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/cdensify.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/cdensify.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/cdensify.c	(revision 36680)
@@ -0,0 +1,192 @@
+# include "data.h"
+
+# define CHECKVAL(ARG) if (!isfinite(ARG)) { gprint (GP_ERR, "illegal value for %s: %f\n", #ARG, ARG); return (FALSE); }
+enum {IS_DOT, IS_SQUARE, IS_CIRCLE, IS_GAUSS};
+
+int cdensify (int argc, char **argv) {
+
+  int i, Nx, Ny, Xb, Yb, N, Xpix, Ypix;
+  double Xmin, Xmax, dX, Ymin, Ymax, dY, ix, iy;
+  float *val;
+  Buffer *bf;
+  Vector *vr, *vd;
+  opihi_flt *r, *d, x, y;
+  int kapa;
+  Graphdata graphmode;
+
+  int Normalize = TRUE;
+  if ((N = get_argument (argc, argv, "-raw"))) {
+    remove_argument (N, &argc, argv);
+    Normalize = FALSE;
+  }
+
+  if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+  double Rmin = graphmode.coords.crval1 - 182.0;
+  double Rmax = graphmode.coords.crval1 + 182.0;
+
+  float scale = 0.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    scale = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int PSFTYPE = IS_DOT;
+  if ((N = get_argument (argc, argv, "-psf"))) {
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp(argv[N], "dot"))    PSFTYPE = IS_DOT;
+    if (!strcasecmp(argv[N], "square")) PSFTYPE = IS_SQUARE;
+    if (!strcasecmp(argv[N], "circle")) PSFTYPE = IS_CIRCLE;
+    if (!strcasecmp(argv[N], "gauss"))  PSFTYPE = IS_GAUSS;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: cdensify buffer R D\n");
+    gprint (GP_ERR, " option: -psf [dot] (circle) (square) (gauss)\n");
+    return (FALSE);
+  }
+  
+  if ((bf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vr = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vd = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (vr[0].Nelements != vd[0].Nelements) return (FALSE);
+
+  REQUIRE_VECTOR_FLT (vr, FALSE); 
+  REQUIRE_VECTOR_FLT (vd, FALSE); 
+
+  KapaGetImageRange (kapa, &Xmin, &Xmax, &Ymax, &Ymin, &Xpix, &Ypix);
+  Xmax = graphmode.xmax;
+  Xmin = graphmode.xmin;
+  Ymax = graphmode.ymax;
+  Ymin = graphmode.ymin;
+  dX = (Xmax - Xmin) / (Xpix - 1);
+  dY = (Ymax - Ymin) / (Ypix - 1);
+
+  CHECKVAL(Xmin);
+  CHECKVAL(Xmax);
+  CHECKVAL(dX);
+
+  CHECKVAL(Ymin);
+  CHECKVAL(Ymax);
+  CHECKVAL(dY);
+
+  Nx = (Xmax - Xmin) / dX + 1;
+  Ny = (Ymax - Ymin) / dY + 1;
+  
+  Coords newcoords = graphmode.coords;
+  newcoords.cdelt1 *= dX;
+  newcoords.cdelt2 *= dY;
+  newcoords.crpix1 = (newcoords.crpix1 - Xmin) / dX;
+  newcoords.crpix2 = (newcoords.crpix2 - Ymin) / dY;
+
+  gfits_free_matrix (&bf[0].matrix);
+  gfits_free_header (&bf[0].header);
+  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+  strcpy (bf[0].file, "(empty)");
+  PutCoords (&newcoords, &bf[0].header);
+  
+  float scalescale = scale*scale;
+  float scale2 = (scale + 1.0) * (scale + 1.0);
+  float fSquare = 1.0 / scale2;
+  float fCircle = 1.0 / (3.141592 * scale2);
+  float fSigma  = 0.5 / scale2;
+  float fGauss  = 1.0 / (2.0 * 3.141592 * scale2);
+
+  // generate the PSF in a local tangent plane
+  Coords coords;
+  coords.crpix1 = coords.crpix2 = 0.0;
+  coords.crval1 = coords.crval2 = 0.0;
+  coords.cdelt1 = coords.cdelt2 = 1.0;
+  coords.pc1_1  = coords.pc2_2  = 1.0;
+  coords.pc1_2  = coords.pc2_1  = 0.0;
+  coords.Npolyterms = 0;
+  strcpy (coords.ctype, "DEC--TAN");
+
+  r = vr[0].elements.Flt;
+  d = vd[0].elements.Flt;
+  val = (float *)bf[0].matrix.buffer;
+  for (i = 0; i < vr[0].Nelements; i++, r++, d++) {
+    double rn = ohana_normalize_angle (*r);
+    while (rn < Rmin) rn += 360.0;
+    while (rn > Rmax) rn -= 360.0;
+    coords.crval1 = rn;
+    coords.crval2 = *d;
+
+    switch (PSFTYPE) {
+      case IS_DOT:
+	RD_to_XY (&x, &y, rn, *d, &graphmode.coords);
+	Xb = (x - Xmin) / dX;
+	Yb = (y - Ymin) / dY;
+
+	RD_to_XY (&x, &y, rn, *d, &newcoords);
+	Xb = (int) x;
+	Yb = (int) y;
+	if (Xb >= Nx) continue;
+	if (Yb >= Ny) continue;
+	if (Xb < 0) continue;
+	if (Yb < 0) continue;
+	val[Xb + Yb*Nx] ++;
+	break;
+      case IS_SQUARE:
+	for (ix = -scale; ix <= scale; ix += dX) {
+	  for (iy = -scale; iy <= scale; iy += dY) {
+	    double rp, dp;
+	    XY_to_RD (&rp, &dp, ix, iy, &coords);
+	    while (rp < Rmin) rp += 360.0;
+	    while (rp > Rmax) rp -= 360.0;
+	    RD_to_XY (&x, &y, rp, dp, &graphmode.coords);
+	    Xb = (x - Xmin) / dX;
+	    Yb = (y - Ymin) / dY;
+	    if (Xb >= Nx) continue;
+	    if (Yb >= Ny) continue;
+	    if (Xb < 0) continue;
+	    if (Yb < 0) continue;
+	    val[Xb + Yb*Nx] += Normalize ? fSquare : 1.0;
+	  }
+	}
+	break;
+      case IS_CIRCLE:
+	for (ix = -scale; ix <= scale; ix += dX) {
+	  for (iy = -scale; iy <= scale; iy += dY) {
+	    float r2 = ix*ix + iy*iy;
+	    double rp, dp;
+	    if (r2 > scalescale) continue;
+	    XY_to_RD (&rp, &dp, ix, iy, &coords);
+	    while (rp < Rmin) rp += 360.0;
+	    while (rp > Rmax) rp -= 360.0;
+	    RD_to_XY (&x, &y, rp, dp, &graphmode.coords);
+	    Xb = (x - Xmin) / dX;
+	    Yb = (y - Ymin) / dY;
+	    if (Xb >= Nx) continue;
+	    if (Yb >= Ny) continue;
+	    if (Xb < 0) continue;
+	    if (Yb < 0) continue;
+	    val[Xb + Yb*Nx] += Normalize ? fCircle : 1.0;
+	  }
+	}
+	break;
+      case IS_GAUSS:
+	for (ix = -3.0*scale; ix <= 3.0*scale; ix += dX) {
+	  for (iy = -3.0*scale; iy <= 3.0*scale; iy += dY) {
+	    float r2 = ix*ix + iy*iy;
+	    double rp, dp;
+	    XY_to_RD (&rp, &dp, ix, iy, &coords);
+	    while (rp < Rmin) rp += 360.0;
+	    while (rp > Rmax) rp -= 360.0;
+	    RD_to_XY (&x, &y, rp, dp, &graphmode.coords);
+	    Xb = (x - Xmin) / dX;
+	    Yb = (y - Ymin) / dY;
+	    if (Xb >= Nx) continue;
+	    if (Yb >= Ny) continue;
+	    if (Xb < 0) continue;
+	    if (Yb < 0) continue;
+	    val[Xb + Yb*Nx] += fGauss*exp(-fSigma*r2);
+	  }
+	}
+	break;
+    }
+  }
+  return (TRUE);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitplx.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitplx.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitplx.c	(revision 36680)
@@ -41,4 +41,8 @@
     remove_argument (N, &argc, argv);
     VERBOSE = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = 2;
   }
 
@@ -108,5 +112,5 @@
   coords.pc1_2  = coords.pc2_1 = 0.0;
   coords.Npolyterms = 1;
-  strcpy (coords.ctype, "RA---SIN");
+  strcpy (coords.ctype, "DEC--SIN");
 
   double *X, *Y, *t, *pX, *pY, *dX, *dY;
@@ -145,5 +149,7 @@
 
   PlxFit fit;
-  FitPMandPar (&fit, X, dX, Y, dY, t, pX, pY, n, VERBOSE);
+  if (!FitPMandPar (&fit, X, dX, Y, dY, t, pX, pY, n, VERBOSE)) {
+    return FALSE;
+  }
 
   // fprintf (stderr, "Roff, Doff: %f, %f; dRo, dDo: %f, %f\n", fit.Ro, fit.Do, fit.dRo, fit.dDo);
@@ -204,9 +210,9 @@
   for (i = 0; i < Npts; i++) {
 
-    if (VERBOSE) fprintf (stderr, "%f %f : %f %f : %f : %f %f\n", X[i], dX[i], Y[i], dY[i], T[i], pR[i], pD[i]);
+    if (VERBOSE == 2) fprintf (stderr, "%f %f : %f %f : %f : %f %f\n", X[i], dX[i], Y[i], dY[i], T[i], pR[i], pD[i]);
 
     /* handle case where dX or dY = 0.0 */
-    wx = 1.0 / SQ(dX[i]);
-    wy = 1.0 / SQ(dY[i]);
+    wx = (fabs(dX[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dX[i]);
+    wy = (fabs(dY[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dY[i]);
 
     Wx += wx;
@@ -266,5 +272,17 @@
   B[4][0] = PRX + PDY;
 
-  dgaussjordan ((double **)A, (double **)B, 5, 1);
+  if (!dgaussjordan ((double **)A, (double **)B, 5, 1)) {
+    if (VERBOSE) fprintf (stderr, "error in fit\n");
+    if (VERBOSE == 2) {
+      int j;
+      for (i = 0; i < 5; i++) {
+	for (j = 0; j < 5; j++) {
+	  fprintf (stderr, "%e ", A[i][j]);
+	}
+	fprintf (stderr, " : %e\n", A[i][0]);
+      }
+    }
+    return FALSE;
+  }
 
   fit[0].Ro = B[0][0];
@@ -285,7 +303,9 @@
     Xf = fit[0].Ro + fit[0].uR*T[i] + fit[0].p*pR[i];
     Yf = fit[0].Do + fit[0].uD*T[i] + fit[0].p*pD[i];
-    chisq += SQ(X[i] - Xf) / SQ(dX[i]);
-    chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
-    if (VERBOSE) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
+    wx = (fabs(dX[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dX[i]);
+    wy = (fabs(dY[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dY[i]);
+    chisq += SQ(X[i] - Xf) * wx;
+    chisq += SQ(Y[i] - Yf) * wy;
+    // if (VERBOSE) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
   }
   fit[0].Nfit = Npts;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitpm.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitpm.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/fitpm.c	(revision 36680)
@@ -0,0 +1,262 @@
+# include "astro.h"
+# define J2000 51544.5       /* Modified Julian date at standard epoch J2000 */
+
+# define ESCAPE(MSG,...) {			\
+    gprint (GP_ERR, MSG, __VA_ARGS__);		\
+    return FALSE; }
+
+typedef struct {
+  double Ro, dRo;
+  double Do, dDo;
+
+  double uR, duR;
+  double uD, duD;
+
+  double chisq;
+  int Nfit;
+} PMFit;
+
+int FitPMonly (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts, int VERBOSE);
+
+int fitpm (int argc, char **argv) {
+  
+  int i, N;
+
+  Vector *rvec, *dvec, *tvec, *dRvec, *dDvec;
+
+  Vector *mvec = NULL; // mask vector
+  if ((N = get_argument (argc, argv, "-mask"))) {
+    remove_argument (N, &argc, argv);
+    if ((mvec = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
+    CastVector (mvec, OPIHI_INT);
+  }
+
+  int VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = 2;
+  }
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: fitplx (ra) (dR) (dec) (dD) (mjd) [-mask mask]\n");
+    // what about the errors?
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  // if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  // Nx = buf[0].header.Naxis[0];
+  // Ny = buf[0].header.Naxis[1];
+  
+  if ((rvec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) ESCAPE ("missing vector %s\n", argv[1]);
+  if ((dvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) ESCAPE ("missing vector %s\n", argv[3]);
+  if ((tvec = SelectVector (argv[5], ANYVECTOR, TRUE)) == NULL) ESCAPE ("missing vector %s\n", argv[5]);
+
+  if ((dRvec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) ESCAPE ("missing vector %s\n", argv[2]);
+  if ((dDvec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) ESCAPE ("missing vector %s\n", argv[4]);
+
+  double *R = rvec->elements.Flt;
+  double *D = dvec->elements.Flt;
+  double *T = tvec->elements.Flt;
+  
+  double *dR = dRvec->elements.Flt;
+  double *dD = dDvec->elements.Flt;
+
+  int *mask = NULL;
+  if (mvec) {
+    mask = mvec->elements.Int;
+  }
+
+  N = tvec->Nelements; // XXX check other lengths
+
+  // find mean values to remove
+  double Npts = 0;
+  double Tmean = 0;
+  double Rmean = 0;
+  double Dmean = 0;
+  double Tmin = +1000000;
+  double Tmax = -1000000;
+  for (i = 0; i < N; i++) {
+    if (mask && !mask[i]) continue;
+    Rmean += R[i];
+    Dmean += D[i];
+    Tmean += T[i];
+    Tmin = MIN(Tmin, T[i]);
+    Tmax = MAX(Tmax, T[i]);
+    Npts += 1.0;
+  }
+  Rmean /= Npts;
+  Dmean /= Npts;
+  Tmean /= Npts;
+
+  float Trange = Tmax - Tmin;
+  // fprintf (stderr, "R,D : %f,%f, T: %f, Trange: %f, Tmin: %f, Tmax: %f\n", Rmean, Dmean, Tmean, Trange, Tmin, Tmax);
+
+  /* project coordinates to a plane centered on the object with units of arcsec */
+  Coords coords;
+  coords.crval1 = Rmean;
+  coords.crval2 = Dmean;
+  coords.crpix1 = 0;
+  coords.crpix2 = 0;
+  coords.cdelt1 = coords.cdelt2 = 1.0 / 3600.0;
+  coords.pc1_1  = coords.pc2_2 = 1.0;
+  coords.pc1_2  = coords.pc2_1 = 0.0;
+  coords.Npolyterms = 1;
+  strcpy (coords.ctype, "DEC--SIN");
+
+  double *X, *Y, *t, *dX, *dY;
+  ALLOCATE (X, double, N);
+  ALLOCATE (Y, double, N);
+  ALLOCATE (dX, double, N);
+  ALLOCATE (dY, double, N);
+  ALLOCATE (t, double, N);
+
+  int n = 0;
+  for (i = 0; i < N; i++) {
+    if (mask && !mask[i]) continue;
+    RD_to_XY (&X[n], &Y[n], R[i], D[i], &coords);
+    dX[n] = dR[i];
+    dY[n] = dD[i];
+    t[n] = (T[i] - Tmean) / 365.25;
+    n++;
+  }
+
+  PMFit fit;
+  if (!FitPMonly (&fit, X, dX, Y, dY, t, n, VERBOSE)) {
+    return FALSE;
+  }
+
+  // fprintf (stderr, "Roff, Doff: %f, %f; dRo, dDo: %f, %f\n", fit.Ro, fit.Do, fit.dRo, fit.dDo);
+  
+  XY_to_RD (&Rmean, &Dmean, fit.Ro, fit.Do, &coords);
+  if (VERBOSE) {
+    fprintf (stderr, "Ro, Do: %f, %f +/- %f, %f\n", Rmean, Dmean, fit.dRo, fit.dDo);
+    fprintf (stderr, "uR, uD: %f, %f; duR, duD: %f, %f\n", fit.uR, fit.uD, fit.duR, fit.duD);
+    fprintf (stderr, "chisq: %f Nfit %d\n", fit.chisq, fit.Nfit);
+  }
+
+  set_variable ("RA",   Rmean);
+  set_variable ("DEC",  Dmean);
+  set_variable ("dR",   fit.dRo);
+  set_variable ("dD",   fit.dDo);
+  set_variable ("uR",   fit.uR);
+  set_variable ("uD",   fit.uD);
+  set_variable ("duR",   fit.duR);
+  set_variable ("duD",   fit.duD);
+  set_variable ("plx",  0.0);
+  set_variable ("dplx", 0.0);
+  
+  set_variable ("Tmean",  Tmean);
+  set_variable ("Trange", Trange);
+  set_variable ("Prange", 0.0);
+
+  set_variable ("chisq", fit.chisq);
+  set_variable ("Nfit",  fit.Nfit);
+
+  return (TRUE);
+}
+
+/* do we want an init function which does the alloc and a clear function to free? */
+int FitPMonly (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts, int VERBOSE) {
+
+  int i;
+
+  static double **A, **B;
+  double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
+  double chisq, Xf, Yf;
+
+  /* do I need to do this as 2 2x2 matrix equations? */
+  if (A == NULL) {
+    ALLOCATE (A, double *, 4);
+    ALLOCATE (B, double *, 4);
+    for (i = 0; i < 4; i++) {
+      ALLOCATE (A[i], double, 4);
+      ALLOCATE (B[i], double, 1);
+      memset (A[i], 0, 4*sizeof(double));
+      memset (B[i], 0, 1*sizeof(double));
+    }
+  }
+
+  Wx = Wy = Tx = Ty = Tx2 = Ty2 = Xs = Ys = XT = YT = 0.0;
+  for (i = 0; i < Npts; i++) {
+    /* handle case where dX or dY = 0.0 */
+    wx = 1.0 / SQ(dX[i]);
+    wy = 1.0 / SQ(dY[i]);
+
+    Wx += wx;
+    Wy += wy;
+
+    Tx += T[i]*wx;
+    Ty += T[i]*wy;
+    
+    Tx2 += SQ(T[i])*wx;
+    Ty2 += SQ(T[i])*wy;
+    
+    Xs += X[i]*wx;
+    Ys += Y[i]*wy;
+
+    XT += X[i]*T[i]*wx;
+    YT += Y[i]*T[i]*wy;
+  }
+
+  A[0][0] = Wx;
+  A[0][1] = Tx;
+
+  A[1][0] = Tx;
+  A[1][1] = Tx2;
+
+  A[2][2] = Wy;
+  A[2][3] = Ty;
+
+  A[3][2] = Ty;
+  A[3][3] = Ty2;
+
+  B[0][0] = Xs;
+  B[1][0] = XT;
+  B[2][0] = Ys;
+  B[3][0] = YT;
+
+  if (!dgaussjordan ((double **)A, (double **)B, 4, 1)) {
+    if (VERBOSE) fprintf (stderr, "error in fit\n");
+    if (VERBOSE == 2) {
+      int j;
+      for (i = 0; i < 4; i++) {
+	for (j = 0; j < 4; j++) {
+	  fprintf (stderr, "%e ", A[i][j]);
+	}
+	fprintf (stderr, " : %e\n", A[i][0]);
+      }
+    }
+    return FALSE;
+  }
+
+  fit[0].Ro = B[0][0];
+  fit[0].uR = B[1][0];
+  fit[0].Do = B[2][0];
+  fit[0].uD = B[3][0];
+  
+  fit[0].dRo = sqrt(A[0][0]);
+  fit[0].duR = sqrt(A[1][1]);
+  fit[0].dDo = sqrt(A[2][2]);
+  fit[0].duD = sqrt(A[3][3]);
+  
+  // add up the chi square for the fit
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+    Xf = fit[0].Ro + fit[0].uR*T[i];
+    Yf = fit[0].Do + fit[0].uD*T[i];
+    chisq += SQ(X[i] - Xf) / SQ(dX[i]);
+    chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+    // if (VERBOSE) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
+  }
+  fit[0].Nfit = Npts;
+
+  // the reduced chisq is divided by (Ndof = 2*Npts - 4)
+  fit[0].chisq = chisq / (2.0*Npts - 4.0);
+  return (TRUE);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/gauss.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/gauss.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/gauss.c	(revision 36680)
@@ -10,4 +10,15 @@
   Buffer *buf;
   KapaImageData data;
+  int VERBOSE;
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    VERBOSE = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    VERBOSE = FALSE;
+    remove_argument (N, &argc, argv);
+  }
 
   name = NULL;
@@ -58,5 +69,5 @@
     KiiCursorRead (kapa, &X, &Y, &ZP, &RA, &DEC, key);
     if (!strcasecmp (key, "Q")) break;
-    get_aperture_stats (&buf[0].matrix, (int)(X+0.5), (int)(Y+0.5), Npix, Nborder, max);
+    get_aperture_stats (&buf[0].matrix, (int)(X+0.5), (int)(Y+0.5), Npix, Nborder, max, VERBOSE);
   }
   KiiCursorOff (kapa);
@@ -64,2 +75,3 @@
 }
 
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/init.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/init.c	(revision 36680)
@@ -13,7 +13,9 @@
 int czplot                  PROTO((int, char **));
 int czcplot                 PROTO((int, char **));
+int cdensify                PROTO((int, char **));
 int drizzle                 PROTO((int, char **));
 int flux                    PROTO((int, char **));
 int fitplx                  PROTO((int, char **));
+int fitpm                   PROTO((int, char **));
 int fixwrap                 PROTO((int, char **));
 int fiximage                PROTO((int, char **));
@@ -49,4 +51,5 @@
 int scale                   PROTO((int, char **));
 int sexigesimal             PROTO((int, char **));
+int sersic                  PROTO((int, char **));
 int spec                    PROTO((int, char **));
 int specpairfit             PROTO((int, char **));
@@ -72,7 +75,9 @@
   {1, "czplot",      czplot,       "plot scaled vectors in sky coordinates"},
   {1, "czcplot",     czcplot,      "plot color-scaled vectors in sky coordinates"},
+  {1, "cdensify",    cdensify,      "vectors to density history on projection"},
   {1, "drizzle",     drizzle,      "transform image to image"},
   {1, "flux",        flux,         "flux in a convex contour"},
   {1, "fitplx",      fitplx,       "fit proper motion and parallax"},
+  {1, "fitpm",       fitpm,        "fit proper motion only"},
   {1, "fixwrap",     fixwrap,      "fix megacam over-wrapped pixels"},
   {1, "fiximage",    fiximage,     "fix pixels in an image by interpolation"},
@@ -107,4 +112,5 @@
   {1, "scale",       scale,        "get / set real bzero / bscale values"},
   {1, "sexigesimal", sexigesimal,  "convert to/from sexigesimal/decimal"},
+  {1, "sersic",      sersic,       "generate sub-pixel resolved sersic model"},
   {1, "spec",        spec,         "extract a spectrum"},
   {1, "specpairfit", specpairfit,  "fit spectrum to another spectrum"},
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/region.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/region.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/region.c	(revision 36680)
@@ -80,22 +80,22 @@
   if (!ohana_str_to_radec (&Ra, &Dec, argv[1], argv[2])) return (FALSE);
   Radius = atof (argv[3]);
-  strcpy (graphmode.coords.ctype, "RA---TAN");
+  strcpy (graphmode.coords.ctype, "DEC--TAN");
   if (argc == 5) {
     if (!strcasecmp (argv[4], "TAN")) 
-      strcpy (graphmode.coords.ctype, "RA---TAN");
+      strcpy (graphmode.coords.ctype, "DEC--TAN");
     if (!strcasecmp (argv[4], "SIN")) 
-      strcpy (graphmode.coords.ctype, "RA---SIN");
+      strcpy (graphmode.coords.ctype, "DEC--SIN");
     if (!strcasecmp (argv[4], "ARC")) 
-      strcpy (graphmode.coords.ctype, "RA---ARC");
+      strcpy (graphmode.coords.ctype, "DEC--ARC");
     if (!strcasecmp (argv[4], "STG")) 
-      strcpy (graphmode.coords.ctype, "RA---STG");
+      strcpy (graphmode.coords.ctype, "DEC--STG");
     if (!strcasecmp (argv[4], "ZEA"))
-      strcpy (graphmode.coords.ctype, "RA---ZEA");
+      strcpy (graphmode.coords.ctype, "DEC--ZEA");
     if (!strcasecmp (argv[4], "AIT")) 
-      strcpy (graphmode.coords.ctype, "RA---AIT");
+      strcpy (graphmode.coords.ctype, "DEC--AIT");
     if (!strcasecmp (argv[4], "GLS")) 
-      strcpy (graphmode.coords.ctype, "RA---GLS");
+      strcpy (graphmode.coords.ctype, "DEC--GLS");
     if (!strcasecmp (argv[4], "PAR")) 
-      strcpy (graphmode.coords.ctype, "RA---PAR");
+      strcpy (graphmode.coords.ctype, "DEC--PAR");
   }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/sersic.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/sersic.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/sersic.c	(revision 36680)
@@ -0,0 +1,115 @@
+# include "astro.h"
+
+float sersic_norm (float index);
+
+// the sersic model has an integral of 2 \pi Rmin Rmax Io norm * Nsubpix^2
+
+int sersic (int argc, char **argv) {
+  
+  int i, j;
+  Buffer *buf;
+
+  // generate a sersic profile in the given buffer using N
+
+  if (argc != 10) {
+    gprint (GP_ERR, "USAGE: sersic (buffer) (Io) (Xo) (Yo) (Rmajor) (Rminor) (theta) (index) (Nsubpix)\n");
+    gprint (GP_ERR, "  generate a sersic profile for the given parameters\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  int Nx = buf[0].header.Naxis[0];
+  int Ny = buf[0].header.Naxis[1];
+  
+  // Io is the central pixel normalization
+  float Io = atof(argv[2]);
+
+  // Xo, Yo are the central pixel coordinates in real pixels
+  float Xo = atof(argv[3]);
+  float Yo = atof(argv[4]);
+
+  float Rmaj = atof(argv[5]);
+  float Rmin = atof(argv[6]);
+
+  float theta = atof(argv[7]);
+  float index = atof(argv[8]);
+  float rindex = 0.5 / index;
+
+  float Nsubpix = atof(argv[9]);
+
+  float f1 = 1.0 / SQ(Rmin) + 1.0 / SQ(Rmaj);
+  float f2 = 1.0 / SQ(Rmin) - 1.0 / SQ(Rmaj);
+  
+  float sxr = 0.5*f1 - 0.5*f2*cos(2.0*theta*RAD_DEG);
+  float syr = 0.5*f1 + 0.5*f2*cos(2.0*theta*RAD_DEG);
+  
+  // sxr, syr cannot be < 0 (f1 >= f2)
+  
+  float Rxx  = +1.0 / sqrt(sxr);
+  float Ryy  = +1.0 / sqrt(syr);
+  float Rxy = -f2*sin(2.0*theta*RAD_DEG);
+
+  float kappa = -0.275552 + 1.972625*index + 0.003487 * SQ(index);
+
+  // dereference pointer
+  float *in = (float *) buf[0].matrix.buffer;
+
+  for (j = 0; j < Ny; j++) {
+    for (i = 0; i < Nx; i++) {
+
+      float x = ((i / Nsubpix) - Xo);
+      float y = ((j / Nsubpix) - Yo);
+
+      // z = r^2
+      float z = SQ(x/Rxx) + SQ(y/Ryy) + Rxy*x*y;
+	  
+      float q = pow (z, rindex);
+      float f = Io * exp(-kappa*q);
+
+      in[i + j*Nx] = f;
+    }
+  }
+
+  float norm = sersic_norm(index);
+  set_variable ("sersic_norm", norm);
+  set_variable ("sersic_kappa", kappa);
+  
+  return (TRUE);
+}
+
+float sersic_norm (float index) {
+
+  float lnorm, norm;
+
+  if ((index >= 0.0) && (index < 1.0)) {
+    lnorm = 0.201545  - 0.950965 * index - 0.315248 * SQ(index);
+    norm = exp(lnorm);
+    return norm;
+  }
+
+  if ((index >= 1.0) && (index < 2.0)) {
+    lnorm = 0.402084  - 1.357775 * index - 0.105102 * SQ(index);
+    norm = exp(lnorm);
+    return norm;
+  }
+
+  if ((index >= 2.0) && (index < 3.0)) {
+    lnorm = 0.619093 - 1.591674 * index - 0.041576 * SQ(index);
+    norm = exp(lnorm);
+    return norm;
+  }
+
+  if ((index >= 3.0) && (index < 4.0)) {
+    lnorm = 0.770263 - 1.696421 * index - 0.023363 * SQ(index);
+    norm = exp(lnorm);
+    return norm;
+  }
+
+  if ((index >= 4.0) && (index < 5.5))  {
+    lnorm = 0.885891 - 1.755684 * index - 0.015753 * SQ(index);
+    norm = exp(lnorm);
+    return norm;
+  }
+  return NAN;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/star.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/star.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.astro/star.c	(revision 36680)
@@ -6,4 +6,15 @@
   double max;
   Buffer *buf;
+  int VERBOSE;
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    VERBOSE = FALSE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-quiet"))) {
+    VERBOSE = FALSE;
+    remove_argument (N, &argc, argv);
+  }
 
   Nborder = 3;
@@ -36,5 +47,5 @@
   }
 
-  get_aperture_stats (&buf[0].matrix, x, y, dx, Nborder, max);
+  get_aperture_stats (&buf[0].matrix, x, y, dx, Nborder, max, VERBOSE);
   
   return (TRUE);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.basic/list.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.basic/list.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.basic/list.c	(revision 36680)
@@ -2,4 +2,6 @@
 # define D_NLINES 100
 static char prompt[] = ">> ";
+
+static int set_list_varname (char *line, char *base, int N, int excelStyle);
 
 int list (int argc, char **argv) {
@@ -21,4 +23,14 @@
   }
 
+  int EXCEL_STYLE = FALSE;
+  if ((N = get_argument (argc, argv, "-excel-style"))) {
+    remove_argument (N, &argc, argv);
+    EXCEL_STYLE = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-excel"))) {
+    remove_argument (N, &argc, argv);
+    EXCEL_STYLE = TRUE;
+  }
+
   if ((N = get_argument (argc, argv, "-vectors"))) {
     remove_argument (N, &argc, argv);
@@ -49,5 +61,5 @@
     
     for (i = 0; i < argc - 3; i++) {
-      sprintf (line, "%s:%d", argv[1], i);
+      set_list_varname (line, argv[1], i, EXCEL_STYLE);
       set_str_variable (line, argv[i+3]);
     }
@@ -83,5 +95,7 @@
 	if (!word) break;
 	
-	sprintf (line, "%s:%d", argv[1], nWords);
+	// sprintf (line, "%s:%d", argv[1], nWords);
+	set_list_varname (line, argv[1], nWords, EXCEL_STYLE);
+
 	set_str_variable (line, word);
 	FREE (word);
@@ -120,5 +134,6 @@
       sprintf (line, "%s:%d", argv[3], i);
       value = get_variable (line);
-      sprintf (line, "%s:%d", argv[1], i);
+      // sprintf (line, "%s:%d", argv[1], i);
+      set_list_varname (line, argv[1], i, EXCEL_STYLE);
       set_str_variable (line, value);
     }
@@ -136,5 +151,6 @@
     N = get_int_variable (line, &found);
     for (i = 0; i < argc - 3; i++) {
-      sprintf (line, "%s:%d", argv[1], N + i);
+      // sprintf (line, "%s:%d", argv[1], N + i);
+      set_list_varname (line, argv[1], N + i, EXCEL_STYLE);
       set_str_variable (line, argv[i+3]);
     }
@@ -159,5 +175,6 @@
     N = get_int_variable (line, &found);
     for (i = 0; i < N; i++) {
-      sprintf (line, "%s:%d", argv[1], i);
+      // sprintf (line, "%s:%d", argv[1], i);
+      set_list_varname (line, argv[1], i, EXCEL_STYLE);
       value = get_variable (line);
       if (value == NULL) continue;
@@ -165,5 +182,6 @@
 	free (value);
 	for (j = i + 1; j < N; j++) {
-	  sprintf (line2, "%s:%d", argv[1], j);
+	  // sprintf (line2, "%s:%d", argv[1], j);
+	  set_list_varname (line2, argv[1], j, EXCEL_STYLE);
 	  next_value = get_variable (line2);
 	  set_str_variable (line, next_value);
@@ -235,5 +253,6 @@
       if (B != (char *) NULL) { *B = 0; }
       if (*A != 0) {
-	sprintf (line, "%s:%d", argv[1], i);
+	// sprintf (line, "%s:%d", argv[1], i);
+	set_list_varname (line, argv[1], i, EXCEL_STYLE);
 	set_str_variable (line, A);
 	A = B + 1;
@@ -283,10 +302,41 @@
 
     if (*input) { 
-      sprintf (line, "%s:%d", argv[1], i);
+      // sprintf (line, "%s:%d", argv[1], i);
+      set_list_varname (line, argv[1], i, EXCEL_STYLE);
       set_str_variable (line, input);
       free (input);
       i++;
-   }
+    }
   }
   return (TRUE);
 }
+
+static int set_list_varname (char *line, char *base, int N, int excelStyle) {
+
+  int i;
+    
+  // A-Z correspond to 0 - 25
+
+  if (excelStyle) {
+    float f = log(26.0);
+    float g = (N == 0) ? 0.0 : log(1.0*N);
+    int Ndigit = (int) (g / f) + 1;
+    if (Ndigit > 10) {
+      sprintf (line, "%s:ZZZZZZZZZZ", base);
+      return FALSE;
+    }
+    char name[12];
+    memset (name, 0, 12);
+    for (i = 0; i < Ndigit; i++) {
+      float Npow = Ndigit - i - 1;
+      float g = pow(26.0, Npow);
+      int V = (int) (N / g);
+      name[i] = (Npow == 0.0) ? 'A' + V : 'A' + V - 1;
+      N -= V * g;
+    }
+    sprintf (line, "%s:%s", base, name);
+  } else {
+    sprintf (line, "%s:%d", base, N);
+  }
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/Makefile	(revision 36680)
@@ -25,4 +25,5 @@
 $(SRC)/book.$(ARCH).o		\
 $(SRC)/book_commands.$(ARCH).o	\
+$(SRC)/cast.$(ARCH).o		\
 $(SRC)/center.$(ARCH).o	\
 $(SRC)/clear.$(ARCH).o		\
@@ -63,7 +64,11 @@
 $(SRC)/imcut.$(ARCH).o	 	\
 $(SRC)/imhist.$(ARCH).o	\
+$(SRC)/impeaks.$(ARCH).o	\
 $(SRC)/imsmooth.$(ARCH).o	\
+$(SRC)/imsmooth.generic.$(ARCH).o	\
+$(SRC)/imsmooth.2d.$(ARCH).o	\
 $(SRC)/integrate.$(ARCH).o	\
 $(SRC)/interpolate.$(ARCH).o	\
+$(SRC)/join.$(ARCH).o		\
 $(SRC)/jpeg.$(ARCH).o		\
 $(SRC)/kern.$(ARCH).o		\
@@ -84,4 +89,6 @@
 $(SRC)/mget.$(ARCH).o		\
 $(SRC)/minterpolate.$(ARCH).o	\
+$(SRC)/medimage.$(ARCH).o	\
+$(SRC)/medimage_commands.$(ARCH).o \
 $(SRC)/mset.$(ARCH).o		\
 $(SRC)/peak.$(ARCH).o		\
@@ -143,4 +150,5 @@
 $(SRC)/vmaxwell.$(ARCH).o          \
 $(SRC)/vgrid.$(ARCH).o		   \
+$(SRC)/vlist.$(ARCH).o		   \
 $(SRC)/vload.$(ARCH).o		   \
 $(SRC)/vzload.$(ARCH).o		   \
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/cast.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/cast.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/cast.c	(revision 36680)
@@ -0,0 +1,71 @@
+# include "data.h"
+
+int cast (int argc, char **argv) {
+  
+  int  i, valid;
+  Vector *ivec, *ovec;
+
+  ivec = ovec = NULL;
+
+  if (argc < 6) goto usage;
+
+  valid = TRUE;
+  valid &= !strcmp(argv[2], "=");
+  valid &= !strcmp(argv[4], "as");
+  if (!valid) goto usage;
+
+  // out type 
+  char outType = OPIHI_NOTYPE;
+  if (!strcasecmp(argv[5], "int")) outType = OPIHI_INT;
+  if (!strcasecmp(argv[5], "float")) outType = OPIHI_FLT;
+  if (outType == OPIHI_NOTYPE) goto usage;
+
+  if ((ovec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) goto error;
+  if ((ivec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) goto error;
+
+  // ovec matches ivec in type
+  ResetVector (ovec, outType, ivec[0].Nelements);
+
+  // we have four cases: (ivec == flt or int) and (tvec == flt or int)
+  if ((ivec->type == OPIHI_FLT) && (ovec->type == OPIHI_FLT)) {
+    opihi_flt *vi = ivec[0].elements.Flt;
+    opihi_flt *vo = ovec[0].elements.Flt;
+    for (i = 0; i < ovec[0].Nelements; i++, vi++, vo++) {
+      *vo = *vi;
+    }
+    return (TRUE);
+  }
+  if ((ivec->type == OPIHI_FLT) && (ovec->type == OPIHI_INT)) {
+    opihi_flt *vi = ivec[0].elements.Flt;
+    opihi_int *vo = ovec[0].elements.Int;
+    for (i = 0; i < ovec[0].Nelements; i++, vi++, vo++) {
+      *vo = *vi;
+    }
+    return (TRUE);
+  }
+  if ((ivec->type == OPIHI_INT) && (ovec->type == OPIHI_FLT)) {
+    opihi_int *vi = ivec[0].elements.Int;
+    opihi_flt *vo = ovec[0].elements.Flt;
+    for (i = 0; i < ovec[0].Nelements; i++, vi++, vo++) {
+      *vo = *vi;
+    }
+    return (TRUE);
+  }
+  if ((ivec->type == OPIHI_INT) && (ovec->type == OPIHI_INT)) {
+    opihi_int *vi = ivec[0].elements.Int;
+    opihi_int *vo = ovec[0].elements.Int;
+    for (i = 0; i < ovec[0].Nelements; i++, vi++, vo++) {
+      *vo = *vi;
+    }
+    return (TRUE);
+  }
+
+error:
+  DeleteVector (ovec);
+  return (FALSE);
+
+usage:
+  gprint (GP_ERR, "SYNTAX: cast vec = vec as (int/float)\n");
+  return (FALSE);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/densify.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/densify.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/densify.c	(revision 36680)
@@ -2,8 +2,9 @@
 
 # define CHECKVAL(ARG) if (!isfinite(ARG)) { gprint (GP_ERR, "illegal value for %s: %f\n", #ARG, ARG); return (FALSE); }
+enum {IS_DOT, IS_SQUARE, IS_CIRCLE, IS_GAUSS};
 
 int densify (int argc, char **argv) {
 
-  int i, Nx, Ny, Xb, Yb, N, Xpix, Ypix, good, UseGraph;
+  int i, Nx, Ny, Xb, Yb, ix, iy, N, Xpix, Ypix, good, UseGraph;
   double Xmin, Xmax, dX, Ymin, Ymax, dY;
   float *val;
@@ -24,8 +25,26 @@
   }
 
+  float scale = 0.0;
+  if ((N = get_argument (argc, argv, "-scale"))) {
+    remove_argument (N, &argc, argv);
+    scale = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int PSFTYPE = IS_DOT;
+  if ((N = get_argument (argc, argv, "-psf"))) {
+    remove_argument (N, &argc, argv);
+    if (!strcasecmp(argv[N], "dot"))    PSFTYPE = IS_DOT;
+    if (!strcasecmp(argv[N], "square")) PSFTYPE = IS_SQUARE;
+    if (!strcasecmp(argv[N], "circle")) PSFTYPE = IS_CIRCLE;
+    if (!strcasecmp(argv[N], "gauss"))  PSFTYPE = IS_GAUSS;
+    remove_argument (N, &argc, argv);
+  }
+
   good = UseGraph ? (argc == 4) : (argc == 10);
   if (!good) {
     gprint (GP_ERR, "USAGE: densify buffer x y Xmin Xmax dX Ymin Ymax dY\n");
     gprint (GP_ERR, "   OR: densify buffer x y -graph\n");
+    gprint (GP_ERR, " option: -psf [dot] (circle) (square) (gauss)\n");
     return (FALSE);
   }
@@ -69,4 +88,7 @@
   CHECKVAL(dY);
 
+  float scaleX = (scale > 0.0) ? scale / dX : 3.0;
+  float scaleY = (scale > 0.0) ? scale / dY : 3.0;
+
   Nx = (Xmax - Xmin) / dX + 1;
   Ny = (Ymax - Ymin) / dY + 1;
@@ -76,4 +98,10 @@
   CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
   strcpy (bf[0].file, "(empty)");
+  
+  float scale2 = (scaleX + 1.0) * (scaleY + 1.0);
+  float fSquare = 1.0 / scale2;
+  float fCircle = 1.0 / (3.141592 * scale2);
+  float fSigma  = 0.5 / scale2;
+  float fGauss  = 1.0 / (2.0 * 3.141592 * scale2);
 
   x = vx[0].elements.Flt;
@@ -83,9 +111,53 @@
     Xb = (*x - Xmin) / dX;
     Yb = (*y - Ymin) / dY;
-    if (Xb >= Nx) continue;
-    if (Yb >= Ny) continue;
-    if (Xb < 0) continue;
-    if (Yb < 0) continue;
-    val[Xb + Yb*Nx] ++;
+    switch (PSFTYPE) {
+      case IS_DOT:
+	if (Xb >= Nx) continue;
+	if (Yb >= Ny) continue;
+	if (Xb < 0) continue;
+	if (Yb < 0) continue;
+	val[Xb + Yb*Nx] ++;
+	break;
+      case IS_SQUARE:
+	for (ix = Xb - scaleX; ix <= Xb + scaleX; ix++) {
+	  for (iy = Yb - scaleY; iy <= Yb + scaleY; iy++) {
+	    if (ix >= Nx) continue;
+	    if (iy >= Ny) continue;
+	    if (ix < 0) continue;
+	    if (iy < 0) continue;
+	    val[ix + iy*Nx] += fSquare;
+	  }
+	}
+	break;
+      case IS_CIRCLE:
+	for (ix = Xb - scaleX; ix <= Xb + scaleX; ix++) {
+	  float dX = ix - Xb;
+	  for (iy = Yb - scaleY; iy <= Yb + scaleY; iy++) {
+	    float dY = iy - Yb;
+	    float r2 = dX*dX + dY*dY;
+	    if (r2 > 9) continue;
+	    if (ix >= Nx) continue;
+	    if (iy >= Ny) continue;
+	    if (ix < 0) continue;
+	    if (iy < 0) continue;
+	    val[ix + iy*Nx] += fCircle;
+	  }
+	}
+	break;
+      case IS_GAUSS:
+	for (ix = Xb - scaleX; ix <= Xb + scaleX; ix++) {
+	  float dX = ix - Xb;
+	  for (iy = Yb - scaleY; iy <= Yb + scaleY; iy++) {
+	    float dY = iy - Yb;
+	    float r2 = dX*dX + dY*dY;
+	    if (ix >= Nx) continue;
+	    if (iy >= Ny) continue;
+	    if (ix < 0) continue;
+	    if (iy < 0) continue;
+	    val[ix + iy*Nx] += fGauss*exp(-fSigma*r2);
+	  }
+	}
+	break;
+    }
   }
   return (TRUE);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/impeaks.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/impeaks.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/impeaks.c	(revision 36680)
@@ -0,0 +1,113 @@
+# include "data.h"
+
+float *findPeaksInRow (Buffer *buff, int y, float threshold, float *peaks, int *npeaks);
+
+int impeaks (int argc, char **argv) {
+  
+  int iy, n;
+  Vector *vecx, *vecy, *vecf;
+  Buffer *buff;
+
+  if (argc != 6) {
+    gprint (GP_ERR, "USAGE: impeaks (buffer) <xvec> <yvec> <fvec> <threshold>\n");
+    return (FALSE);
+  }
+
+  if ((buff = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vecx = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecf = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  float threshold = atof (argv[5]);
+
+  // we find the peaks in the image by looking for peaks in each row, then checking their neighboring pixels
+
+  float *image = (float *) buff[0].matrix.buffer;
+  int Nx = buff[0].matrix.Naxis[0];
+  int Ny = buff[0].matrix.Naxis[1];
+
+  // we will use this vector for each row (allocated first pass only)
+  float *rowPeaks = NULL;
+
+  int Npeak = 0;
+  int NPEAK = 1000;
+  opihi_flt *peakX = NULL;
+  opihi_flt *peakY = NULL;
+  opihi_flt *peakF = NULL;
+  ALLOCATE (peakX, opihi_flt, NPEAK);
+  ALLOCATE (peakY, opihi_flt, NPEAK);
+  ALLOCATE (peakF, opihi_flt, NPEAK);
+
+  // exclude peaks on the outer edge
+  for (iy = 1; iy < Ny; iy++) {
+    
+    int iyn = iy - 1;
+    int iyp = iy + 1;
+
+    int nRowPeaks;
+    rowPeaks = findPeaksInRow (buff, iy, threshold, rowPeaks, &nRowPeaks);
+
+    // each each of the row peaks to see if it is an image peak
+
+    for (n = 0; n < nRowPeaks; n++) {
+      int ix = rowPeaks[n];
+      int ixn = ix - 1;
+      int ixp = ix + 1;
+      
+      float value = image[ix + iy*Nx];
+
+      // I know ix,iy is a peak in the row, is it above the neighbors in the previous and next rows?
+      if (value <= image[ix  + iyn*Nx]) continue;
+      if (value <  image[ix  + iyp*Nx]) continue;
+      if (value <  image[ixn + iyn*Nx]) continue;
+      if (value <= image[ixn + iyp*Nx]) continue;
+      if (value <  image[ixp + iyn*Nx]) continue;
+      if (value <= image[ixp + iyp*Nx]) continue;
+
+      peakX[Npeak] = ix;
+      peakY[Npeak] = iy;
+      peakF[Npeak] = value;
+      Npeak ++;
+      if (Npeak == NPEAK) {
+	NPEAK += 100;
+	REALLOCATE (peakX, opihi_flt, NPEAK);
+	REALLOCATE (peakY, opihi_flt, NPEAK);
+	REALLOCATE (peakF, opihi_flt, NPEAK);
+      }
+    }
+
+  }
+
+  free (vecx->elements.Flt); vecx->elements.Flt = peakX; vecx->Nelements = Npeak;
+  free (vecy->elements.Flt); vecy->elements.Flt = peakY; vecy->Nelements = Npeak;
+  free (vecf->elements.Flt); vecf->elements.Flt = peakF; vecf->Nelements = Npeak;
+
+  return (TRUE);
+}
+
+// skip the first and last pixel
+float *findPeaksInRow (Buffer *buff, int y, float threshold, float *peaks, int *npeaks) {
+
+  int ix;
+
+  int Nx = buff[0].matrix.Naxis[0];
+
+  float *row = (float *) buff[0].matrix.buffer + Nx*y;
+
+  if (peaks == NULL) {
+    ALLOCATE (peaks, float, Nx);
+  }
+  int Npeaks = 0;
+  for (ix = 1; ix < Nx - 1; ix++) {
+    if (row[ix] <  threshold) continue;   // only accept pixels above threshold
+    if (row[ix] <  row[ix - 1]) continue; // peak pixel must be at least preceeding pixel
+    if (row[ix] <= row[ix + 1]) continue; // we accept the last pixel of a series of equal values
+    
+    peaks[Npeaks] = ix;
+    Npeaks ++;
+  }
+
+  *npeaks = Npeaks;
+  return peaks;
+}
+    
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.2d.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.2d.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.2d.c	(revision 36680)
@@ -0,0 +1,214 @@
+# include "data.h"
+
+# define NRAD_MAX 44
+static float radii2[NRAD_MAX] = {  0.0,  1.0,  2.0,  4.0,  5.0, 
+				   8.0,  9.0, 10.0, 13.0, 16.0, 
+				   17.0, 18.0, 20.0, 25.0, 26.0, 
+				   29.0, 32.0, 34.0, 36.0, 37.0, 
+				   40.0, 41.0, 45.0, 49.0, 50.0, 
+				   52.0, 53.0, 58.0, 61.0, 64.0,
+				   65.0, 68.0, 72.0, 73.0, 74.0, 
+				   80.0, 81.0, 82.0, 85.0, 89.0,
+				   90.0, 97.0, 98.0, 100.0
+};
+
+static int   radiiN[NRAD_MAX] = {    1,  4, 4,  4,  8,
+				     4,  4, 8,  8,  4,
+				     8,  4, 8, 12,  8, 
+				     8,  4, 8,  4,  8,
+				     8,  8, 8,  4, 12,
+				     8,  8, 8,  8,  4, 
+				     16, 8, 4,  8,  8, 
+				      8, 4, 8, 16,  8, 
+				      8, 8, 4, 12
+};
+
+/*
+ 0,  0 :  0 : 0  :   0
+ 0,  1 :  1 : 1  :   1
+ 1,  1 :  2 : 1  :   2
+ 0,  2 :  3 : 2  :   4
+ 1,  2 :  4 : 2  :   5
+ 2,  2 :  5 : 2  :   8
+ 0,  3 :  6 : 3  :   9
+ 1,  3 :  7 : 3  :  10
+ 2,  3 :  8 : 3  :  13
+ 0,  4 :  9 : 4  :  16
+ 1,  4 : 10 : 4  :  17
+ 3,  3 : 11 : 4  :  18
+ 2,  4 : 12 : 4  :  20
+ 0,  5 : 13 : 5  :  25
+ 3,  4 : 13 : 5  :  25
+ 1,  5 : 14 : 5  :  26
+ 2,  5 : 15 : 5  :  29
+ 4,  4 : 16 : 5  :  32
+ 3,  5 : 17 : 5  :  34
+ 0,  6 : 18 : 6  :  36
+ 1,  6 : 19 : 6  :  37
+ 2,  6 : 20 : 6  :  40
+ 4,  5 : 21 : 6  :  41
+ 3,  6 : 22 : 6  :  45
+ 0,  7 : 23 : 7  :  49
+ 1,  7 : 24 : 7  :  50 *
+ 5,  5 : 24 : 7  :  50 *
+ 4,  6 : 25 : 7  :  52
+ 2,  7 : 26 : 7  :  53
+ 3,  7 : 27 : 7  :  58
+ 5,  6 : 28 : 7  :  61
+ 0,  8 : 29 : 8  :  64
+ 1,  8 : 30 : 8  :  65 *
+ 4,  7 : 30 : 8  :  65 *
+ 2,  8 : 31 : 8  :  68
+ 6,  6 : 32 : 8  :  72
+ 3,  8 : 33 : 8  :  73
+ 5,  7 : 34 : 8  :  74
+ 4,  8 : 35 : 8  :  80
+ 0,  9 : 36 : 9  :  81
+ 1,  9 : 37 : 9  :  82
+ 2,  9 : 38 : 9  :  85 *
+ 6,  7 : 38 : 9  :  85 *
+ 5,  8 : 39 : 9  :  89
+ 3,  9 : 40 : 9  :  90
+ 4,  9 : 41 : 9  :  97
+ 7,  7 : 42 : 9  :  98
+ 0, 10 : 43 : 10 : 100 *
+ 6,  8 : 43 : 10 : 100 *
+ */
+
+# define ADD_AXIS(RAD,DD) s += radflux[RAD]*(vi[p - DD] + vi[p + DD] + vi[p - DD*Nx] + vi[p + DD*Nx]);
+# define ADD_DIAG(RAD,DD) s += radflux[RAD]*(vi[p - DD - DD*Nx] + vi[p + DD - DD*Nx] + vi[p - DD + DD*Nx] + vi[p + DD + DD*Nx]);
+# define ADD_RAND(RAD,DX,DY) s += radflux[RAD]*(vi[p - DX - DY*Nx] + vi[p + DX - DY*Nx] + vi[p - DX + DY*Nx] + vi[p + DX + DY*Nx] + \
+						vi[p - DY - DX*Nx] + vi[p + DY - DX*Nx] + vi[p - DY + DX*Nx] + vi[p + DY + DX*Nx]);
+
+int imsmooth_2d (int argc, char **argv) {
+  
+  int i, j;
+  float *vi, *vo;
+  Buffer *in;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) sigma Nsigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  float  sigma = atof(argv[2]);
+  float Nsigma = atof(argv[3]);
+
+  int Ns = (int)(Nsigma * sigma);
+  Ns = MAX (3, MIN (Ns, 10));
+  int Ns2 = Ns * Ns;
+
+  // we are going to use a hard-wired radial profile
+  float *radflux = NULL;
+  ALLOCATE (radflux, float, NRAD_MAX);
+  float sum = 0.0;
+  for (i = 0; i < NRAD_MAX; i++) {
+    float z = radii2[i] / SQ(sigma);
+    radflux[i] = 1.0 / (1.0 + z + pow(z,1.666));
+    if (radii2[i] > Ns2) continue;
+    sum += radiiN[i] * radflux[i];
+  }
+  for (i = 0; i < NRAD_MAX; i++) {
+    radflux[i] = radflux[i] / sum;
+  }
+
+  int Nx = in[0].matrix.Naxis[0];
+  int Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (vo, float, Nx*Ny);
+  memset (vo, 0, Nx*Ny*sizeof(float));
+
+  /* smooth in X direction */
+  vi = (float *) in[0].matrix.buffer;
+  for (j = Ns; j < Ny - Ns; j++) {
+    for (i = Ns; i < Nx - Ns; i++) {
+
+      int p = i + j*Nx; // current pixel of interest
+      float s = radflux[0]*(vi[p]);
+
+      // r <= 1.0
+      ADD_AXIS (1,  1);      // r^2 = 1
+
+      // r <= 2.0
+      ADD_DIAG (2,  1);      // r^2 = 2
+      ADD_AXIS (3,  2);      // r^2 = 4
+
+      // r <= 3.0
+      ADD_RAND (4,  1,  2);  // r^2 = 5
+      ADD_DIAG (5,  2);      // r^2 = 8
+      ADD_AXIS (6,  3);      // r^2 = 9
+      if (Ns <= 3) goto finish;
+
+      // r <= 4.0
+      ADD_RAND (7,  1,  3);  // r^2 = 10
+      ADD_RAND (8,  2,  3);  // r^2 = 13
+      ADD_AXIS (9,  4);      // r^2 = 16
+      if (Ns <= 4) goto finish;
+
+      // r <= 5.0
+      ADD_RAND (10,  1,  4); // r^2 = 17
+      ADD_DIAG (11,  3);     // r^2 = 18
+      ADD_RAND (12,  2,  4); // r^2 = 20
+      ADD_RAND (13,  3,  4); // r^2 = 25
+      ADD_AXIS (13,  5);     // r^2 = 25
+      if (Ns <= 5) goto finish;
+
+      // r <= 6.0
+      ADD_RAND (14,  1,  5); // r^2 = 26
+      ADD_RAND (15,  2,  5); // r^2 = 29
+      ADD_DIAG (16,  4);     // r^2 = 32
+      ADD_RAND (17,  3,  5); // r^2 = 34
+      ADD_AXIS (18,  6);     // r^2 = 36
+      if (Ns <= 6) goto finish;
+
+      // r <= 7.0
+      ADD_RAND (19,  1,  6); // r^2 = 37
+      ADD_RAND (20,  2,  6); // r^2 = 40
+      ADD_RAND (21,  4,  5); // r^2 = 41
+      ADD_RAND (22,  3,  6); // r^2 = 45
+      ADD_AXIS (23,  7);     // r^2 = 49
+      if (Ns <= 7) goto finish;
+
+      // r <= 8.0
+      ADD_RAND (24,  1,  7); // r^2 = 50
+      ADD_DIAG (24,  5);     // r^2 = 50
+      ADD_RAND (25,  4,  6); // r^2 =   52
+      ADD_RAND (26,  2,  7); // r^2 =   53
+      ADD_RAND (27,  3,  7); // r^2 =   58
+      ADD_RAND (28,  5,  6); // r^2 =   61
+      ADD_AXIS (29,  8);     // r^2 =   64
+      if (Ns <= 8) goto finish;
+
+      ADD_RAND (30,  1,  8); // r^2 =   65 *
+      ADD_RAND (30,  4,  7); // r^2 =   65 *
+      ADD_RAND (31,  2,  8); // r^2 =   68
+      ADD_DIAG (32,  6);     // r^2 =   72
+      ADD_RAND (33,  3,  8); // r^2 =   73
+      ADD_RAND (34,  5,  7); // r^2 =   74
+      ADD_RAND (35,  4,  8); // r^2 =   80
+      ADD_AXIS (36,  9);     // r^2 =   81
+      if (Ns <= 9) goto finish;
+
+      ADD_RAND (37,  1,  9); // r^2 =   82
+      ADD_RAND (38,  2,  9); // r^2 =   85 *
+      ADD_RAND (38,  6,  7); // r^2 =   85 *
+      ADD_RAND (39,  5,  8); // r^2 =   89
+      ADD_RAND (40,  3,  9); // r^2 =   90
+      ADD_RAND (41,  4,  9); // r^2 =   97
+      ADD_DIAG (42,  7);     // r^2 =   98
+      ADD_RAND (43,  6,  8); // r^2 =  100 *
+      ADD_AXIS (43, 10);     // r^2 =  100 *
+      if (Ns <= 10) goto finish;
+
+    finish:
+      vo[p] = s;
+    }
+  }
+
+  free (radflux);
+  free (in[0].matrix.buffer);
+  in[0].matrix.buffer = (char *) vo;
+
+  return (TRUE);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.generic.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.generic.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/imsmooth.generic.c	(revision 36680)
@@ -0,0 +1,68 @@
+# include "data.h"
+
+int imsmooth_generic (int argc, char **argv) {
+  
+  int i, j, n;
+  float *vi, *vo;
+  float g, s;
+  Buffer *in;
+  Vector *vec;
+  float *temp;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) vector\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (vec[0].Nelements % 2 == 0) {
+    gprint (GP_ERR, "vector must have an odd number of elements\n");
+    return FALSE;
+  }
+
+  int Nx = in[0].matrix.Naxis[0];
+  int Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (temp, float, Nx*Ny);
+
+  /* build a 1D gaussian */
+  int Ns = 0.5*(vec[0].Nelements - 1);
+  opihi_flt *smvec = &vec[0].elements.Flt[Ns];
+
+  /* smooth in X direction */
+  for (j = 0; j < Ny; j++) {
+    vi = (float *) in[0].matrix.buffer + j*Nx;
+    vo = &temp[j*Nx];
+    for (i = 0; i < Nx; i++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (i+n < 0) continue;
+	if (i+n >= Nx) continue;
+	s += smvec[n]*vi[i+n];
+	g += smvec[n];
+      }
+      vo[i] = s / g;
+    }
+  }
+
+  /* smooth in Y direction */
+  for (i = 0; i < Nx; i++) {
+    vi = &temp[i];
+    vo = (float *)in[0].matrix.buffer + i;
+    for (j = 0; j < Ny; j++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (j+n < 0) continue;
+	if (j+n >= Ny) continue;
+	s += smvec[n]*vi[(n+j)*Nx];
+	g += smvec[n];
+      }
+      vo[j*Nx] = s / g;
+    }
+  }
+
+  free (temp);
+  return (TRUE);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/init.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/init.c	(revision 36680)
@@ -10,4 +10,5 @@
 int center           PROTO((int, char **));
 int parity           PROTO((int, char **));
+int cast             PROTO((int, char **));
 int circstats        PROTO((int, char **));
 int clear            PROTO((int, char **));
@@ -52,7 +53,11 @@
 int imcut            PROTO((int, char **));
 int imhist           PROTO((int, char **));
+int impeaks          PROTO((int, char **));
 int imsmooth         PROTO((int, char **));
+int imsmooth_generic PROTO((int, char **));
+int imsmooth_2d      PROTO((int, char **));
 int integrate        PROTO((int, char **));
 int interpolate      PROTO((int, char **));
+int join             PROTO((int, char **));
 int jpeg             PROTO((int, char **));
 int kern             PROTO((int, char **));
@@ -74,4 +79,5 @@
 int mget             PROTO((int, char **));
 int minterp          PROTO((int, char **));
+int medimage_command PROTO((int, char **));
 int mset             PROTO((int, char **));
 int peak             PROTO((int, char **));
@@ -133,4 +139,5 @@
 int vmaxwell         PROTO((int, char **));
 int vload            PROTO((int, char **));
+int vlist            PROTO((int, char **));
 int vzload           PROTO((int, char **));
 int vstats           PROTO((int, char **));
@@ -160,4 +167,5 @@
   {1, "buffers",      list_buffers,     "list the currently allocated buffers (images)"},
   {1, "center",       center,           "center image on coords"},
+  {1, "cast",         cast,             "cast input vector to specified type"},
   {1, "circstats",    circstats,        "circular statistics"},
   {1, "clear",        clear,            "erase plot"},
@@ -205,8 +213,12 @@
   {1, "imcut",        imcut,            "linear image cut between arbitrary coords"},
   {1, "imhistogram",  imhist,           "histogram of an image region"},
+  {1, "impeaks",      impeaks,          "find peaks in an image (return vectors)"},
   {1, "imsmooth",     imsmooth,         "circular gaussian smoothing"},
+  {1, "imsmooth.generic", imsmooth_generic, "circular non-gaussian smoothing"},
+  {1, "imsmooth.2d",  imsmooth_2d,      "circular non-gaussian smoothing"},
   {1, "imstats",      imstats,          "statistics on a portion of an image"},
   {1, "integrate",    integrate,        "integrate a vector"},
   {1, "interpolate",  interpolate,      "interpolate between vector pairs"},
+  {1, "join",         join,             "find the join of two ID vectors"},
   {1, "jpeg",         jpeg,             "convert display image to JPEG"},
   {1, "kern",         kern,             "convolve with 3x3 kernel"},
@@ -224,4 +236,5 @@
   {1, "minterp",      minterp,          "interpolate image pixels"},
   {1, "iminterp",     minterp,          "interpolate image pixels"},
+  {1, "medimage",     medimage_command, "median image manipulation"},
   {1, "matrix",       matrix,           "matrix math operations"},
   {1, "match2d",      match2d,          "match 2 pairs of X,Y vectors and return matched indexes"},
@@ -290,4 +303,5 @@
   {1, "vgrid",        vgrid,            "generate an image from a triplet of vectors"},
   {1, "vhistogram",   histogram,        "generate histogram from vector"},
+  {1, "vlist",        vlist,            "append values to a vector from command line"},
   {1, "vload",        vload,            "load vectors as overlay on image display"},
   {1, "vmaxwell",     vmaxwell,         "fit a Maxwellian to a vector"},
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/join.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/join.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/join.c	(revision 36680)
@@ -0,0 +1,195 @@
+# include "data.h"
+
+int join_IDs_inner (Vector *ID1, Vector *ID2, Vector *index1, Vector *index2);
+int join_IDs_outer (Vector *ID1, Vector *ID2, Vector *index);
+
+// join (ID1) (ID2) [-index1 (index1)] [-index2 (index2)]
+// generate indexes for ID1 and ID2 to identify the matches of ID1 and ID2 (must be int?)
+int join (int argc, char **argv) {
+  
+  int N, OUTER;
+  Vector *ID1vec, *ID2vec;
+  Vector *index1, *index2;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto usage;
+  if ((N = get_argument (argc, argv, "--help"))) goto usage;
+
+  OUTER = FALSE;
+  if ((N = get_argument (argc, argv, "-outer"))) {
+    remove_argument (N, &argc, argv);
+    OUTER = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-index1"))) {
+    remove_argument (N, &argc, argv);
+    if ((index1 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index1 = SelectVector ("index1", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if ((N = get_argument (argc, argv, "-index2"))) {
+    remove_argument (N, &argc, argv);
+    if ((index2 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index2 = SelectVector ("index2", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: join ID1 ID2 [-index1 (index1)] [-index2 (index2)] [-outer]\n");
+    gprint (GP_ERR, "  use -h or --help for more detail\n");
+    return FALSE;
+  }
+
+  if ((ID1vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((ID2vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  REQUIRE_VECTOR_INT (ID1vec, FALSE); 
+  REQUIRE_VECTOR_INT (ID2vec, FALSE); 
+
+  if (OUTER) {
+      join_IDs_outer (ID1vec, ID2vec, index1);
+      join_IDs_outer (ID2vec, ID1vec, index2);
+  } else {
+      join_IDs_inner (ID1vec, ID2vec, index1, index2);
+  }
+
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "we have two modes of operation:\n\n");
+
+  gprint (GP_ERR, "without -outer, we are finding all matched ID pairs.  in this\n");
+  gprint (GP_ERR, "case, the two index vectors have the same length, one entry per matched pair.\n");
+  gprint (GP_ERR, "ID1[index1] matches to ID2[index2].\n\n");
+
+  gprint (GP_ERR, "with -outer selected, we are finding the matched element of set 1 for each of set 2\n");
+  gprint (GP_ERR, "and vice versa.  in this case, index1 is always the same length as ID1, while index2\n");
+  gprint (GP_ERR, "is the same length as ID2.  ID2[index1] matches ID1 while x1[ID2] matches ID1\n");
+
+  return FALSE;
+}
+
+// find the entries in ID1 which match ID2 (duplicates allows)
+int join_IDs_inner (Vector *ID1, Vector *ID2, Vector *index1, Vector *index2) {
+  
+  off_t i, j, first_j, I, J, *N1, *N2, Nmatch, NMATCH, DMATCH;
+  opihi_int dID;
+
+  NMATCH = MAX(MAX(0.01*ID1->Nelements, 0.01*ID2->Nelements), 100);
+  DMATCH = NMATCH;
+
+  ResetVector (index1, OPIHI_INT, NMATCH);
+  ResetVector (index2, OPIHI_INT, NMATCH);
+
+  ALLOCATE (N1, off_t, ID1->Nelements);
+  ALLOCATE (N2, off_t, ID2->Nelements);
+
+  for (i = 0; i < ID1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < ID2->Nelements; i++) { N2[i] = i; }
+
+  sort_IDs_indexonly (ID1->elements.Int, N1, ID1->Nelements);
+  sort_IDs_indexonly (ID2->elements.Int, N2, ID2->Nelements);
+
+  Nmatch = 0;
+  for (i = j = 0; (i < ID1->Nelements) && (j < ID2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+
+    if (dID < 0) { i++; continue; }
+    if (dID > 0) { j++; continue; }
+
+    // look for all matches of list2() to list1(i)
+    // this allows for multiple values of ID1 or ID2
+    first_j = j;
+    for (j = first_j; (dID == 0) && (j < ID2->Nelements); j++) {
+      J = N2[j];
+      dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+      if (dID == 0) {
+	index1->elements.Int[Nmatch] = I;
+	index2->elements.Int[Nmatch] = J;
+
+	Nmatch ++;
+	if (Nmatch >= NMATCH) {
+	  NMATCH += DMATCH;
+	  REALLOCATE (index1->elements.Int, opihi_int, NMATCH);
+	  REALLOCATE (index2->elements.Int, opihi_int, NMATCH);
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  index1->Nelements = Nmatch;
+  index2->Nelements = Nmatch;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
+
+// find the elements of ID1 which match ID2 (-1 if no match)
+int join_IDs_outer (Vector *ID1, Vector *ID2, Vector *index) {
+  
+  off_t i, j, Jfirst, Ji, I, J, *N1, *N2, NMATCH;
+  opihi_int dID;
+
+  NMATCH = ID1->Nelements;
+  ResetVector (index, OPIHI_INT, NMATCH);
+
+  for (i = 0; i < index->Nelements; i++) { index->elements.Int[i] = -1; }
+
+  ALLOCATE (N1, off_t, ID1->Nelements);
+  ALLOCATE (N2, off_t, ID2->Nelements);
+
+  for (i = 0; i < ID1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < ID2->Nelements; i++) { N2[i] = i; }
+
+  sort_IDs_indexonly (ID1->elements.Int, N1, ID1->Nelements);
+  sort_IDs_indexonly (ID2->elements.Int, N2, ID2->Nelements);
+
+  // find the closest entry in list 2 to the current entry in list 1:
+  for (i = j = 0; (i < ID1->Nelements) && (j < ID2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+
+    if (dID < 0) { 
+      // no match in list 2 to this entry
+      index->elements.Int[I] = -1;
+      i++; 
+      continue; 
+    }
+    if (dID > 0) { j++; continue; }
+
+    // look for closest matches of list2() to list1(i)
+    Jfirst = -1;
+    for (Ji = j; (Jfirst == -1) && (dID == 0) && (Ji < ID2->Nelements); Ji++) {
+      J = N2[Ji];
+      dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+      if (dID == 0) {
+	Jfirst = J;
+      }
+    }
+
+    // no match in list 2 to this entry
+    if (Jfirst == -1) {
+      index->elements.Int[I] = -1;
+      i++;
+      continue;
+    }
+    index->elements.Int[I] = Jfirst;
+    i++;
+  }
+  index->Nelements = NMATCH;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/match2d.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/match2d.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/match2d.c	(revision 36680)
@@ -14,4 +14,7 @@
   Vector *index1, *index2;
 
+  if ((N = get_argument (argc, argv, "-h"))) goto usage;
+  if ((N = get_argument (argc, argv, "--help"))) goto usage;
+
   CLOSEST = FALSE;
   if ((N = get_argument (argc, argv, "-closest"))) {
@@ -38,6 +41,5 @@
   if (argc != 6) {
     gprint (GP_ERR, "USAGE: match2d X1 Y1 X2 Y2 Radius [-index1 (index1)] [-index2 (index2)] [-closest]\n");
-    gprint (GP_ERR, "  if -closest is provided, index1 & index2 will have the same length as X1 and X2 (respectively)\n");
-    gprint (GP_ERR, "    with either the index of the match or a value of -1 for non-matches\n");
+    gprint (GP_ERR, "  use -h or --help for more detail\n");
     return (FALSE);
   }
@@ -90,4 +92,21 @@
 
   return (TRUE);
+
+usage:
+  gprint (GP_ERR, "we have two modes of operation:\n\n");
+
+  gprint (GP_ERR, "without -closest, we are finding all matched pairs within the match radius.  in this\n");
+  gprint (GP_ERR, "case, the two index vectors have the same length, one entry per matched pair.\n");
+  gprint (GP_ERR, "x1[index1],y1[index1] matches to x2[index2],y2[index2].\n\n");
+
+  gprint (GP_ERR, "with -closest selected, we are finding the closest element of set 1 to each of set 2\n");
+  gprint (GP_ERR, "and vice versa.  in this case, index1 is always the same length as x1,y1, while index2\n");
+  gprint (GP_ERR, "is the same lengths as x2,y2.  x2[index1],y2[index1] matches x1,y1 while\n");
+  gprint (GP_ERR, "x1[index2],y1[index2] matches x2,y2\n\n");
+
+  gprint (GP_ERR, "if -index1 or -index2 is not supplied, the vectors are created with names index1 or index2\n");
+  gprint (GP_ERR, "use 'reindex' to generate new vectors based on these index vectors\n");
+
+  return FALSE;
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage.c	(revision 36680)
@@ -0,0 +1,53 @@
+# include "data.h"
+
+int medimage_help (int argc, char **argv);
+int medimage_list (int argc, char **argv);
+int medimage_add (int argc, char **argv);
+int medimage_calc (int argc, char **argv);
+int medimage_delete (int argc, char **argv);
+int medimage_rename (int argc, char **argv);
+
+static Command medimage_commands[] = {
+  {1, "help",       medimage_help,       "list medimage help info"},
+  {1, "list",       medimage_list,       "list medimages"},
+  {1, "add",        medimage_add,        "add an image to the given medimage (creates new one if needed)"},
+  {1, "calc",       medimage_calc,       "measure the median image and save to a buffer"},
+  {1, "delete",     medimage_delete,     "delete a medimage"},
+  {1, "rename",     medimage_rename,     "rename a medimage"},
+};
+
+int medimage_help (int argc, char **argv) {
+
+  gprint (GP_ERR, "USAGE: medimage (command)\n");
+  gprint (GP_ERR, "    medimage help                       : this listing\n");
+  gprint (GP_ERR, "    medimage list                       : list medimages\n");
+  gprint (GP_ERR, "    medimage add    (medimage) (image)  : add the given image to a medimage\n");
+  gprint (GP_ERR, "    medimage calc   (medimage) (output) : calculate the median image\n");
+  gprint (GP_ERR, "    medimage delete (medimage)          : delete named medimage\n");
+  gprint (GP_ERR, "    medimage rename (medimage) (new)    : change medimage name to new name\n");
+
+  return FALSE;
+}
+
+int medimage_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    medimage_help(0,NULL);
+    return (FALSE);
+  }
+
+  N = sizeof (medimage_commands) / sizeof (Command);
+
+  /* find the medimage sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (medimage_commands[i].name, argv[1])) {
+      status = (*medimage_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown medimage command %s\n", argv[1]);
+  return (FALSE);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage_commands.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage_commands.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/medimage_commands.c	(revision 36680)
@@ -0,0 +1,186 @@
+# include "data.h"
+
+int medimage_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: medimage list\n");
+    return FALSE;
+  }
+
+  ListMedImages();
+  return TRUE;
+}
+
+int medimage_add (int argc, char **argv) {
+
+  Buffer *image;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: medimage add (name) (image)\n");
+    gprint (GP_ERR, "       add the given image to the set of images to be medianed\n");
+    return FALSE;
+  }
+
+  if ((image = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+
+  MedImageType *median = FindMedImage (argv[1]);
+  if (!median) {
+    // create a new median with this name
+    median = CreateMedImage (argv[1], image->matrix.Naxis[0], image->matrix.Naxis[1]);
+    if (!median) {
+      gprint (GP_ERR, "failed to generate a new median image\n");
+      return FALSE;
+    }
+  }
+
+  if (median->Nx != image->matrix.Naxis[0]) {
+    gprint (GP_ERR, "image does not match medimage dimensions\n");
+    return FALSE;
+  }
+  if (median->Ny != image->matrix.Naxis[1]) {
+    gprint (GP_ERR, "image does not match medimage dimensions\n");
+    return FALSE;
+  }
+
+  // new image should match existing medimage dimensions
+
+  int Ninput = median->Ninput;
+  median->Ninput ++;
+  REALLOCATE (median->buffers, float *, median->Ninput);
+
+  ALLOCATE (median->buffers[Ninput], float, median->Nx*median->Ny);
+  memcpy (median->buffers[Ninput], image->matrix.buffer, sizeof(float)*median->Nx*median->Ny);
+
+  return TRUE;
+}
+
+int medimage_calc (int argc, char **argv) {
+
+  int ix, iy, n;
+  Buffer *output;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: medimage calc (name) (output)\n");
+    gprint (GP_ERR, "       calculate the median image for the median image set\n");
+    return FALSE;
+  }
+
+  MedImageType *median = FindMedImage (argv[1]);
+  if (!median) {
+    gprint (GP_ERR, "median image %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  if ((output = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  int Ninput = median->Ninput;
+  int Nx = median->Nx;
+  int Ny = median->Ny;
+
+  float *value = NULL;
+  ALLOCATE (value, float, Ninput);
+
+  gfits_free_matrix (&output->matrix);
+  gfits_free_header (&output->header);
+  CreateBuffer (output, Nx, Ny, -32, 0.0, 1.0);
+
+  float *outvalue = (float *) output->matrix.buffer;
+
+  for (iy = 0; iy < Ny; iy++) {
+    for (ix = 0; ix < Nx; ix++) {
+
+      int N = 0;
+      int Npix = ix + Nx*iy;
+      for (n = 0; n < Ninput; n++) {
+	float v = median->buffers[n][Npix];
+	if (!isfinite(v)) continue;
+	value[N] = v;
+	N++;
+      }
+      if (N == 0) continue;
+
+      fsort (value, N);
+      outvalue[Npix] = value[(int)(0.5*N)];
+    }
+  }
+
+  return TRUE;
+}
+
+/* 
+int medimage_save (int argc, char **argv) {
+
+  int N;
+
+  int APPEND = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    APPEND = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: medimage save (name) (filename) [-append]\n");
+    return FALSE;
+  }
+
+  if (!SaveMedImage(argv[2], argv[1], APPEND)) {
+    gprint (GP_ERR, "failed to save medimage %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+
+int medimage_load (int argc, char **argv) {
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: medimage load (name) (filename)\n");
+    return FALSE;
+  }
+
+  if (!LoadMedImage(argv[2], argv[1])) {
+    gprint (GP_ERR, "failed to load medimage %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+*/
+
+int medimage_delete (int argc, char **argv) {
+
+  int status;
+  MedImageType *medimage;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: medimage delete (medimage)\n");
+    return FALSE;
+  }
+
+  medimage = FindMedImage (argv[1]);
+  if (medimage == NULL) {
+    gprint (GP_ERR, "medimage %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteMedImage (medimage);
+  if (!status) abort ();
+  return TRUE;
+}
+
+int medimage_rename (int argc, char **argv) {
+
+  MedImageType *medimage;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: medimage rename (medimage) (newname)\n");
+    return FALSE;
+  }
+
+  medimage = FindMedImage (argv[1]);
+  if (medimage == NULL) {
+    gprint (GP_ERR, "medimage %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  free (medimage->name);
+  medimage->name = strcreate (argv[2]);
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36680)
@@ -23,4 +23,5 @@
 // vector types
 enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
+static int FITS_TRANSPOSE;
 
 int read_vectors (int argc, char **argv) {
@@ -34,4 +35,10 @@
 
   char *buffer = NULL;
+
+  FITS_TRANSPOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-transpose"))) {
+    remove_argument (Narg, &argc, argv);
+    FITS_TRANSPOSE = TRUE;
+  }
 
   /* auto-sense table type */
@@ -410,60 +417,122 @@
     }
 	
-    // define the multifield vector names
-    ALLOCATE (vec, Vector *, Nval);
-    for (j = 0; j < Nval; j++) {
-      if (Nval == 1) 
-	sprintf (name, "%s", argv[i]);
-      else
-	sprintf (name, "%s:%d", argv[i], j);
-      if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
-      ResetVector (vec[j], vecType, Ny);
-    }
-
-    if (!strcmp (type, "char")) {
-      char *Ptr = data;
+    if (!FITS_TRANSPOSE) {
+      // define the multifield vector names (Nval vectors x Ny elements)
+      ALLOCATE (vec, Vector *, Nval);
+      for (j = 0; j < Nval; j++) {
+	if (Nval == 1) 
+	  sprintf (name, "%s", argv[i]);
+	else
+	  sprintf (name, "%s:%d", argv[i], j);
+	if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+	ResetVector (vec[j], vecType, Ny);
+      }
+
+      if (!strcmp (type, "char")) {
+	char *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "short")) {
+	short *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int")) {
+	int *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int64_t")) {
+	int64_t *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "float")) {
+	float *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Flt[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "double")) {
+	double *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Flt[j] = *Ptr;
+	  }
+	}
+      }
+    } else {
+      // define the multifield vector names (Ny vectors x Nval elements)
+      ALLOCATE (vec, Vector *, Ny);
       for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "short")) {
-      short *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "int")) {
-      int *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "int64_t")) {
-      int64_t *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "float")) {
-      float *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Flt[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "double")) {
-      double *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Flt[j] = *Ptr;
+	if (Ny == 1) 
+	  sprintf (name, "%s", argv[i]);
+	else
+	  sprintf (name, "%s:%d", argv[i], j);
+	if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+	ResetVector (vec[j], vecType, Nval);
+      }
+
+      if (!strcmp (type, "char")) {
+	char *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "short")) {
+	short *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int")) {
+	int *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int64_t")) {
+	int64_t *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "float")) {
+	float *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Flt[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "double")) {
+	double *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Flt[k] = *Ptr;
+	  }
 	}
       }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/rebin.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/rebin.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/rebin.c	(revision 36680)
@@ -133,4 +133,5 @@
 	      *Vout += *Vin;
 	      if (Normalize) {(*Vn) ++;}
+	      // if ((i == 1) && (j == 1)) fprintf (stderr, "%d,%d : %d,%d : %f : %f : %d\n", i, j, x, y, *Vin, *Vout, *Vn);
 	    }
 	    if (Normalize) {Vn ++;}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/join.sh
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/join.sh	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/join.sh	(revision 36680)
@@ -0,0 +1,110 @@
+
+list tests
+ test1
+ memtest1
+end
+
+# Test if imhist works
+macro test1
+
+ $PASS = 1
+
+ local i
+
+ delete ID1 ID2
+
+ vlist -int ID1 1   2   3   4   5   6
+ vlist val1     0.1 4.3 2.1 5.5 2.1 4.2
+
+ vlist -int ID2 5   3   1   6
+ vlist val      0.3 2.3 1.1 2.5
+
+ join ID1 ID2 
+
+mana: for i 0 index1[]
+>> echo index1[$i] index2[$i]
+>> end
+0 2
+2 1
+4 0
+5 3
+mana: join ID1 ID2 -outer
+mana: vectors 
+    N       name      size
+    0        ID1          6 (INT)
+    1       val1          6 (FLT)
+    2        ID2          4 (INT)
+    3        val          4 (FLT)
+    4     index1          6 (INT)
+    5     index2          4 (INT)
+mana: for i 0 index1[]
+>> echo index1[$i]
+>> end
+2
+-1
+1
+-1
+0
+3
+mana: echo ID1[2]
+3
+mana: echo ID1[0] ID2[2]
+1 1
+mana: for i 0 index1[]
+>> if (index1[$i] == -1) continue
+>> $N = index1[$i]
+>> echo $i index1[$i] ID1[$i] ID2[$N]
+>> end
+0 2 1 1
+2 1 3 3
+4 0 5 5
+5 3 6 6
+mana: for i 0 index2[]
+>> if (index2[$i] == -1) continue
+>> $N = index2[$i]
+>> echo $i index2[$i] ID2[$i] ID1[$N]
+>> end
+0 4 5 5
+1 2 3 3
+2 0 1 1
+3 5 6 6
+mana: 
+
+ # if ((xvec[1024] != 17) || (yvec[1024] != 100))
+ #  $PASS = 0
+ #  echo "Value mismatch: xvec[1024] yvec[1024] (should be 17,100)"
+ # end
+ # 
+ # imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+ # 
+ # if ((xvec[1024] != 10) || (yvec[1024] != 100))
+ #  $PASS = 0
+ #  echo "Value mismatch: xvec[1024] yvec[1024] (should be 10,100)"
+ # end
+
+end
+
+
+# Memory test
+macro memtest1
+
+ local i
+
+ list word -x "ps -p $PID -o rss"
+ $startmem = $word:1
+
+ for i 0 1000
+  imhist -q buff xvec yvec -region 40 0 25 10 -range 0 10
+ end
+
+ list word -x "ps -p $PID -o rss"
+ $endmem = $word:1
+
+ $PASS = 1
+
+ if ($endmem - $startmem > 10)
+   $PASS = 0
+   echo "growth: {$endmem-$startmem}"
+   echo "kB/loop: {($endmem-$startmem)/1000}"
+ end
+end
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/medimage.sh
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/medimage.sh	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/test/medimage.sh	(revision 36680)
@@ -0,0 +1,11 @@
+
+macro go
+ mcreate a 30 30
+ for i 0 40
+  set a$i = zero(a) + $i
+ end
+
+ for i 0 40
+  medimage add t1 a$i
+ end
+end
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vellipse.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vellipse.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vellipse.c	(revision 36680)
@@ -228,4 +228,5 @@
  */
 
+// XXX NOTE that PHI is defined with the wrong sign, should fix this...
 opihi_flt fellipseOD (opihi_flt alpha, opihi_flt *par, int Npar, opihi_flt *dpar) {
   
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vgauss.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vgauss.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vgauss.c	(revision 36680)
@@ -48,4 +48,7 @@
   if ((ovec = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
 
+  CastVector (xvec, OPIHI_FLT);
+  CastVector (yvec, OPIHI_FLT);
+
   int Nsvec = strlen(argv[3]);
 
@@ -69,6 +72,4 @@
   }
 
-  CastVector (xvec, OPIHI_FLT);
-  CastVector (yvec, OPIHI_FLT);
   CastVector (svec, OPIHI_FLT);
   // XXX Cast is failing.
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vlist.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vlist.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/cmd.data/vlist.c	(revision 36680)
@@ -0,0 +1,63 @@
+# include "data.h"
+
+int vlist (int argc, char **argv) {
+  
+  int i, N;
+  Vector *vec;
+  
+  int APPEND = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    APPEND = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  int INT = FALSE;
+  if ((N = get_argument (argc, argv, "-int"))) {
+    if (APPEND) {
+      gprint (GP_ERR, " ERROR : cannot mix -append and -int\n");
+      return (FALSE);
+    }
+    INT = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc < 3) {
+    gprint (GP_ERR, "USAGE: vlist [-append] [-int] vector value value ...\n");
+    gprint (GP_ERR, "    -int : resulting vector is integer type (values are truncated to integer)\n");
+    gprint (GP_ERR, " -append : append value to end of vector (otherwise deleted)\n");
+    return (FALSE);
+  }
+
+  if ((vec = SelectVector (argv[1], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // for append, type comes from input vector
+  if (APPEND) {
+    INT = (vec[0].type == OPIHI_INT);
+  }
+
+  int Nitem = argc - 2;
+  int Noffset = APPEND ? vec[0].Nelements : 0;
+  vec[0].Nelements = APPEND ? vec[0].Nelements + Nitem : Nitem;
+
+  if (INT) {
+    vec[0].type = OPIHI_INT;
+    REALLOCATE (vec[0].elements.Int, opihi_int, vec[0].Nelements);
+    for (i = 0; i < Nitem; i++) {
+      vec[0].elements.Int[i + Noffset] = atoi (argv[i+2]);
+    }
+  } else {
+    vec[0].type = OPIHI_FLT;
+    REALLOCATE (vec[0].elements.Flt, opihi_flt, vec[0].Nelements);
+    for (i = 0; i < Nitem; i++) {
+      vec[0].elements.Flt[i + Noffset] = atof (argv[i+2]);
+    }
+  }
+
+  return (TRUE);
+}
+
+// vlist name 0 1 2 3
+// argc = 6
+// Nitem = 4
+// Noffset = 0
+// Int[0,1,2,3] = argv[2,3,4,5]
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/Makefile	(revision 36680)
@@ -40,4 +40,5 @@
 $(SRC)/cmpload.$(ARCH).o	  	\
 $(SRC)/cmpread.$(ARCH).o	  	\
+$(SRC)/coordimage.$(ARCH).o	  	\
 $(SRC)/ddmags.$(ARCH).o	  	\
 $(SRC)/detrend.$(ARCH).o	  	\
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/avmatch.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/avmatch.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/avmatch.c	(revision 36680)
@@ -70,40 +70,6 @@
   dbExtractAveragesInit (); 
 
-  // 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) {
-    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;
-    }
-
-    char *targv1 = argv[1];
-    char *targv2 = argv[2];
-    argv[1] = strcreate ("-coords");
-    argv[2] = strcreate (CoordsFile);
-    free (CoordsFile);
-
-    // I need to pass the RA & DEC vectors to the remote clients...
-    int status = HostTableParallelOps (skylist, argc, argv, RESULT_FILE, TRUE, RAvec->Nelements, VERBOSE);
-    if (vec) free (vec);
-    
-    free (argv[1]);
-    free (argv[2]);
-    argv[1] = targv1;
-    argv[2] = targv2;
-
-    return status;
-  }
-
   // get vectors corresponding to coordinates of interest
   if (CoordsFile) {
@@ -120,4 +86,55 @@
     remove_argument (1, &argc, argv);
   }
+
+  /* load regions which contain all supplied RA,DEC coordinates */
+  if ((skylist = SelectRegionsByCoordVectors (RAvec, DECvec)) == NULL) goto escape;
+
+  // this does all the work of re-packaging the command, calling it on the remote machines, then loading in the results
+  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) : avmatch (RADIUS) field, ... [we removed RA & DEC above]
+    // if ( CoordsFile) : avmatch (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++) {
+      targv[targc] = strcreate (argv[i]);
+      targc ++;
+    }
+
+    // if not specified, create the coords.fits input file
+    // NOTE: RAvec, DECvec were set above
+    if (!CoordsFile) {
+      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;
+    
+    // I need to pass the RA & DEC vectors to the remote clients...
+    int status = HostTableParallelOps (skylist, targc, targv, RESULT_FILE, TRUE, RAvec->Nelements, VERBOSE);
+    if (vec) free (vec);
+    
+    // free up targv
+    for (i = 0; i < targc; i++) {
+      free (targv[i]);
+    }
+    free (targv);
+
+    return status;
+  }
+
   RADIUS = atof (argv[1]);
   remove_argument (1, &argc, argv);
@@ -140,7 +157,4 @@
     if (fields[i].photcode[0].type == PHOT_DEP) needMeasures = TRUE;
   }
-
-  /* load regions which contain all supplied RA,DEC coordinates */
-  if ((skylist = SelectRegionsByCoordVectors (RAvec, DECvec)) == NULL) goto escape;
 
   /* create output storage vectors */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/coordimage.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/coordimage.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/coordimage.c	(revision 36680)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+
+int coordimage (int argc, char **argv) {
+
+  int N;
+  off_t i, j, Nimage, *subset, Nsubset;
+  time_t tzero;
+  double trange;
+  Buffer *bufX, *bufY;
+  SkyRegionSelection *selection;
+  Image *image;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  int TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "plotting in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  }
+
+  PhotCode *Photcode = NULL;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    Photcode = GetPhotcodebyName (argv[N]);
+    if (Photcode == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-Nphotcode"))) {
+    remove_argument (N, &argc, argv);
+    Photcode = GetPhotcodebyCode (atoi(argv[N]));
+    if (Photcode == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  char *Name = NULL;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    Name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int chipID = -1;
+  if ((N = get_argument (argc, argv, "-chip"))) {
+    remove_argument (N, &argc, argv);
+    chipID = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if (argc != 5) goto syntax;
+
+  if ((bufX = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) goto escape;
+  if ((bufY = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) goto escape;
+  int Nx = atoi (argv[3]);
+  int Ny = atoi (argv[4]);
+
+  gfits_free_matrix (&bufX[0].matrix);
+  gfits_free_header (&bufX[0].header);
+  CreateBuffer (bufX, Nx, Ny, -32, 0.0, 1.0); // initialized to 0.0 here
+  strcpy (bufX[0].file, "(empty)");
+
+  gfits_free_matrix (&bufY[0].matrix);
+  gfits_free_header (&bufY[0].header);
+  CreateBuffer (bufY, Nx, Ny, -32, 0.0, 1.0); // initialized to 0.0 here
+  strcpy (bufY[0].file, "(empty)");
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+
+  float Nsum = 0.0;
+  float *vX = (float *) bufX[0].matrix.buffer;
+  float *vY = (float *) bufY[0].matrix.buffer;
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (Name && (strstr (image[i].name, Name) == (char *) NULL)) continue;
+    if (Photcode) {
+      if (Photcode[0].type == PHOT_DEP) {
+	if (Photcode[0].code != image[i].photcode) continue;
+      } else {
+	if (Photcode[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+    if (chipID > -1) {
+      if (image[i].ccdnum != chipID) continue;
+    }
+
+    float dX = image[i].NX / (float) Nx;
+    float dY = image[i].NY / (float) Ny;
+
+    Coords coords = image[i].coords;
+    coords.Npolyterms = 0;
+
+    int ix, iy;
+    for (iy = 0; iy < Ny; iy++) {
+      double fy = iy * dY; // coordinate in full image
+      for (ix = 0; ix < Nx; ix++) {
+	double fx = ix * dX; // coordinate in full image
+
+	double Lo, Lm, dL;
+	double Mo, Mm, dM;
+
+	XY_to_LM (&Lo, &Mo, fx, fy, &coords);
+	XY_to_LM (&Lm, &Mm, fx, fy, &image[i].coords);
+	dL = Lo - Lm;
+	dM = Mo - Mm;
+
+	vX[ix + iy*Nx] += dL;
+	vY[ix + iy*Nx] += dM;
+      }
+    }
+    Nsum ++;
+  }
+  fprintf (stderr, "Nsum: %f\n", Nsum);
+  return TRUE;
+
+ syntax:
+  gprint (GP_ERR, "USAGE: coordimage buffX buffY Nx Ny\n");
+ escape:
+  return (FALSE);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/dvo_client.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/dvo_client.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/dvo_client.c	(revision 36680)
@@ -90,4 +90,10 @@
   }
 
+  // these are set in 'startup.c' for readline-based programs
+  set_variable ("PID", getpid());
+  set_str_variable ("KAPA", "kapa");
+  set_int_variable ("UNSIGN", 0);
+  gfits_set_unsign_mode (FALSE);
+
   return;
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/find_matches.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/find_matches.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/find_matches.c	(revision 36680)
@@ -50,5 +50,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   // this region includes a boundary layer of size RADIUS
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/gimages.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/gimages.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/gimages.c	(revision 36680)
@@ -136,5 +136,5 @@
       local.pc2_1 = local.pc1_2 = 0.0;
       local.Npolyterms = 1;
-      strcpy (local.ctype, "RA---TAN");
+      strcpy (local.ctype, "DEC--TAN");
 
       if (typehash == DistortImage) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/hosts.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/hosts.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/hosts.c	(revision 36680)
@@ -12,5 +12,5 @@
     gprint (GP_ERR, "  commands:\n");
     gprint (GP_ERR, "    purge-temp : delete all tempfiles for this shell\n");
-    gprint (GP_ERR, "               : [-old-pid] [-all-pid] [-v] [-verbose] [-commit] [-type type]\n");
+    gprint (GP_ERR, "               : [-old-pid] [-all-pid] [-v] [-verbose] [-commit] [-type type] [-age hours]\n");
     gprint (GP_ERR, "    get-results : determine name of RESULTS file\n");
     return FALSE;
@@ -33,4 +33,15 @@
     }
     
+    struct timeval now;
+    gettimeofday (&now, NULL);
+
+    float AGE = 0;
+    int NOW = now.tv_sec;
+    if ((N = get_argument (argc, argv, "-age"))) {
+      remove_argument (N, &argc, argv);
+      AGE = atof(argv[N]);
+      remove_argument (N, &argc, argv);
+    }
+
     int VERBOSE = FALSE;
     if ((N = get_argument (argc, argv, "-v"))) {
@@ -121,5 +132,14 @@
       glob (name, 0, NULL, &pglob);
       int j;
+      struct stat filestats;
       for (j = 0; j < pglob.gl_pathc; j++) {
+	if (AGE > 0) {
+	  if (stat(pglob.gl_pathv[j], &filestats)) {
+	    gprint (GP_ERR, "failed to get stats for %s\n", pglob.gl_pathv[j]);
+	    continue;
+	  }
+	  float myAge = (NOW - filestats.st_mtime) / 3600.0;
+	  if (myAge < AGE) continue;
+	}
 	if (VERBOSE) gprint (GP_ERR, "unlink %s\n", pglob.gl_pathv[j]);
 	if (!DRYRUN) unlink (pglob.gl_pathv[j]);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/init.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/init.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/init.c	(revision 36680)
@@ -13,4 +13,5 @@
 int cmpload         PROTO((int, char **));
 int cmpread         PROTO((int, char **));
+int coordimage      PROTO((int, char **));
 int ddmags          PROTO((int, char **));
 int detrend         PROTO((int, char **));
@@ -71,4 +72,5 @@
   {1, "cmpload",     cmpload,      "load cmp file into ?"},
   {1, "cmpread",     cmpread,      "read data from cmp format files"},
+  {1, "coordimage",  coordimage,   "generate a map of the transformation residuals"},
   {1, "ddmags",      ddmags,       "plot magnitude differences"},
   {1, "detrend",     detrend,      "extract from detrend database?"},
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/photometry.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/photometry.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/photometry.c	(revision 36680)
@@ -662,5 +662,5 @@
       break;
     case AVE_dMAG:
-      value = PhotdM (code, average, secfilt);
+      value = PhotAveErr (code, average, secfilt);
       break;
     case AVE_Xm:
@@ -865,5 +865,5 @@
   /* for ErrSelect, check average errors */
   if (ErrSelect) {
-    dM = PhotdM (code, average, secfilt);
+    dM = PhotAveErr (code, average, secfilt);
     if (dM > ErrValue) return (FALSE);
   }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/showtile.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/showtile.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/showtile.c	(revision 36680)
@@ -34,5 +34,5 @@
   coords.pc1_2  = coords.pc2_1  = 0.0;
   coords.Npolyterms = 0;
-  strcpy (coords.ctype, "RA---TAN");
+  strcpy (coords.ctype, "DEC--TAN");
   
   /* fill in top-left region */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/simage.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/simage.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/dvo/simage.c	(revision 36680)
@@ -43,6 +43,6 @@
   /* get astrometry information */
   strcpy (coords.ctype, "NONE");
-  gfits_scan (&header, "CTYPE1",   "%s",  1, coords.ctype);
-  if (strcmp (coords.ctype, "RA---PLY")) {
+  gfits_scan (&header, "CTYPE2",   "%s",  1, coords.ctype);
+  if (strcmp (coords.ctype, "DEC--PLY")) {
     gprint (GP_ERR, "ERROR: wrong astrometric info in header\n");
     return (FALSE);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/data.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/data.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/data.h	(revision 36680)
@@ -33,4 +33,21 @@
   char **pageIDs;
 } Book;
+
+// the interpolating spline has valu
+typedef struct {
+  int Nknots;
+  opihi_flt *xk;
+  opihi_flt *yk;
+  opihi_flt *y2;
+  char *name;
+} Spline;
+
+typedef struct {
+  char *name;
+  int Ninput;
+  int Nx;
+  int Ny;
+  float **buffers;
+} MedImageType;
 
 void InitData (void);
@@ -144,5 +161,5 @@
 
 /* starfuncs.c */
-double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max);
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max, int VERBOSE);
 int set_rough_radii (double Ra, double Ri, double Ro);
 int get_rough_star (float *data, int Nx, int Ny, int x, int y, opihi_flt *xc, opihi_flt *yc, opihi_flt *sx, opihi_flt *sy, opihi_flt *sxy, opihi_flt *zs, opihi_flt *zp, opihi_flt *sk);
@@ -170,13 +187,4 @@
 void FreeQueues (void);
 void FreeBooks (void);
-
-// the interpolating spline has valu
-typedef struct {
-  int Nknots;
-  opihi_flt *xk;
-  opihi_flt *yk;
-  opihi_flt *y2;
-  char *name;
-} Spline;
 
 /* in SplineOps.c */
@@ -207,3 +215,12 @@
 double hermitian_10(double x);
 
+/* in MedImageOps.c */
+void InitMedImages ();
+void FreeMedImages ();
+void FreeMedImage (MedImageType *medimage);
+MedImageType *FindMedImage (char *name);
+MedImageType *CreateMedImage (char *name, int Nx, int Ny);
+int DeleteMedImage (MedImageType *medimage);
+void ListMedImages ();
+
 # endif
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/dvomath.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/dvomath.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/dvomath.h	(revision 36680)
@@ -55,7 +55,35 @@
 } Buffer;
 
+typedef enum {
+  ST_NONE,
+  ST_LEFT,
+  ST_RIGHT,
+  ST_COMMA,
+  ST_TRINARY,
+  ST_OR,
+  ST_AND,
+  ST_LOGIC,
+  ST_BITWISE,
+  ST_ADD,
+  ST_TIMES,
+  ST_POWER,
+  ST_UNARY,
+  ST_BINARY,
+
+  ST_VALUE,
+  ST_SCALAR_INT,
+  ST_SCALAR_FLT,
+  ST_VECTOR,
+  ST_VECTOR_TMP,
+  ST_MATRIX,
+  ST_MATRIX_TMP,
+
+  ST_STRING,
+  ST_STRING_TMP,
+} StackVarType;
+
 typedef struct {			/* math stack structure */
   char   *name;
-  char    type;
+  StackVarType type;
   Buffer *buffer;
   Vector *vector;
@@ -77,5 +105,9 @@
 void          delete_stack	    PROTO((StackVar *stack, int Nstack));
 void          clear_stack 	    PROTO((StackVar *stack));
-void          assign_stack 	    PROTO((StackVar *stack, char *name, int type));
+void          assign_stack 	    PROTO((StackVar *stack, char *name, StackVarType type));
+
+int           SSS_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
+int           VVV_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
+int           MMM_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
 
 int           VV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pantasks.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pantasks.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pantasks.h	(revision 36680)
@@ -116,5 +116,5 @@
 
   int active;
-  int priority;
+  int nicelevel;
 
 } Task;
@@ -161,5 +161,5 @@
 
   JobMode     mode;			/* local or controller? */
-  int     priority;
+  int     nicelevel;
   char   *realhost;
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pcontrol.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/include/pcontrol.h	(revision 36680)
@@ -8,4 +8,5 @@
 /** job status values **/
 typedef enum {
+  PCONTROL_JOB_NONE, // XXX OK?
   PCONTROL_JOB_ALLJOBS,
   PCONTROL_JOB_PENDING,
@@ -119,5 +120,5 @@
   int          exit_status;
   int          Reset;
-  int          priority;
+  int          nicelevel;
   JobMode      mode;
   JobStat      state;
@@ -309,9 +310,10 @@
 char  *GetJobStackName (int StackID);
 Stack *GetJobStackByName (char *name);
+int GetJobStackIDbyName (char *name);
 int    PutJob (Job *job, int StackID, int where);
 int    PutJobSetState (Job *job, int StackID, int where, int state);
 Job   *PullJobByID (IDtype JobID, int *StackID);
 Job   *PullJobFromStackByID (int StackID, int ID);
-IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts);
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int nicelevel, int argc, char **argv, int Nxhosts, char **xhosts);
 void   DelJob (Job *job);
 Host  *UnlinkJobAndHost (Job *job);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/Makefile	(revision 36680)
@@ -24,4 +24,5 @@
 $(SDIR)/spline.$(ARCH).o		\
 $(SDIR)/SplineOps.$(ARCH).o		\
+$(SDIR)/MedImageOps.$(ARCH).o		\
 $(SDIR)/mrqmin.$(ARCH).o		\
 $(SDIR)/mrq2dmin.$(ARCH).o		\
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/MedImageOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/MedImageOps.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/MedImageOps.c	(revision 36680)
@@ -0,0 +1,125 @@
+# include "data.h"
+
+/* this file contains functions to manage the median image data
+ */
+
+MedImageType **medimages = NULL; /* book to store the list of all splines */
+int     Nmedimages;   /* number of currently defined medimages */
+int     NMEDIMAGES;   /* number of currently allocated medimages */
+
+void InitMedImages () {
+  Nmedimages = 0;
+  NMEDIMAGES = 16;
+  ALLOCATE (medimages, MedImageType *, NMEDIMAGES); 
+}
+
+void FreeMedImages () {
+
+  int i;
+
+  for (i = 0; i < Nmedimages; i++) {
+    FreeMedImage (medimages[i]);
+  }
+  free (medimages);
+}
+
+void FreeMedImage (MedImageType *medimage) {
+
+  int i;
+
+  if (!medimage) return;
+
+  free (medimage[0].name);
+  for (i = 0; i < medimage[0].Ninput; i++) {
+    free (medimage[0].buffers[i]);
+  }
+  free (medimage[0].buffers);
+  free (medimage);
+}
+
+/* return the given medimage */
+MedImageType *FindMedImage (char *name) {
+
+  int i;
+
+  if (!medimages) return NULL;
+
+  for (i = 0; i < Nmedimages; i++) {
+    if (!strcmp (medimages[i][0].name, name)) {
+      return (medimages[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named medimage */
+MedImageType *CreateMedImage (char *name, int Nx, int Ny) {
+
+  int N;
+  MedImageType *medimage;
+
+  // do not create an image if one exists
+  medimage = FindMedImage (name);
+  if (medimage != NULL) {
+    return NULL;
+  }
+
+  if (!medimages) InitMedImages ();
+
+  N = Nmedimages;
+  Nmedimages ++;
+  CHECK_REALLOCATE (medimages, MedImageType *, NMEDIMAGES, Nmedimages, 16);
+  ALLOCATE (medimage, MedImageType, 1);
+  medimage->name = strcreate (name);
+  medimage->Ninput = 0;
+  medimage->Nx = Nx;
+  medimage->Ny = Ny;
+  ALLOCATE (medimage->buffers, float *, 1);
+
+  medimages[N] = medimage;
+  return (medimage);
+}
+
+/* delete a medimage */
+int DeleteMedImage (MedImageType *medimage) {
+
+  int i, N, NMEDIMAGES_2;
+
+  if (!medimages) return FALSE;
+
+  /* find medimage in medimage list */
+  N = -1;
+  for (i = 0; i < Nmedimages; i++) {
+    if (medimages[i] == medimage) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nmedimages - 1; i++) {
+    medimages[i] = medimages[i + 1];
+  }
+  Nmedimages --;
+  NMEDIMAGES_2 = MAX (16, NMEDIMAGES / 2);
+  if (Nmedimages < NMEDIMAGES_2) {
+    NMEDIMAGES = NMEDIMAGES_2;
+    REALLOCATE (medimages, MedImageType *, NMEDIMAGES);
+  }
+
+  FreeMedImage (medimage);
+  return (TRUE);
+}
+
+/* list known medimages */
+void ListMedImages () {
+
+  int i;
+
+  if (!medimages) return;
+
+  for (i = 0; i < Nmedimages; i++) {
+    gprint (GP_ERR, "%-15s %4d x %4d : %3d inputs\n", medimages[i][0].name, medimages[i][0].Nx, medimages[i][0].Ny, medimages[i][0].Ninput);
+  }
+  return;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/starfuncs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/starfuncs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.data/starfuncs.c	(revision 36680)
@@ -1,5 +1,5 @@
 # include "data.h"
 
-double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max) {
+double get_aperture_stats (Matrix *matrix, int X, int Y, int Npix, int Nborder, double max, int VERBOSE) {
 
   double *ring;
@@ -95,5 +95,5 @@
   set_int_variable ("Npts", Npts);
   
-  gprint (GP_LOG, "%f %f %f %f %f %f %f %f\n", x, y, FWHMx, FWHMy, sky, I, mag, dmag);
+  if (VERBOSE) gprint (GP_LOG, "%f %f %f %f %f %f %f %f\n", x, y, FWHMx, FWHMy, sky, I, mag, dmag);
 
   return (mag);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/ListOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/ListOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/ListOps.c	(revision 36680)
@@ -189,12 +189,16 @@
 int is_list_data (char *line) {
 
-  char *comm, *temp;
-
-  temp = thisword (nextword (nextword (line)));
-  comm = thisword (line);
-
+  char *comm = NULL;
+  char *temp = NULL;
+  char *ptr  = NULL;
+
+  comm = thisword (line);
   if (comm == NULL) goto escape;
-
   if (strcmp (comm, "list")) goto escape;
+
+  ptr = nextword (line);
+  if (!strncmp("-excel", ptr, strlen("-excel"))) ptr = nextword (ptr);
+  if (!strncmp("-excel-style", ptr, strlen("-excel-style"))) ptr = nextword (ptr);
+  temp = thisword (ptr);
 
   /* if (cond) (command) does not define a complete block */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/check_stack.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/check_stack.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/check_stack.c	(revision 36680)
@@ -12,5 +12,5 @@
 
   for (i = 0; i < Nstack; i++) {
-    if (stack[i].type == 'X') {
+    if (stack[i].type == ST_VALUE) {
 
       /** if this is a number, put it on the list of scalars and move on.  assume value is
@@ -27,13 +27,13 @@
       stack[i].IntValue = strtol (stack[i].name, &c2, 0);
       if ((fabs(stack[i].FltValue) > MAX_INT) && (c1 == stack[i].name + strlen (stack[i].name))) {
-	stack[i].type  = 'S'; // 'S' == (float)
+	stack[i].type  = ST_SCALAR_FLT; // (float)
 	continue;
       } 
       if (c2 == stack[i].name + strlen (stack[i].name)) {
-	stack[i].type  = 's'; // 's' == (int)
+	stack[i].type  = ST_SCALAR_INT; // (int)
 	continue;
       } 
       if (c1 == stack[i].name + strlen (stack[i].name)) {
-	stack[i].type  = 'S'; // 'S' == (float)
+	stack[i].type  = ST_SCALAR_FLT; // (float)
 	continue;
       } 
@@ -42,5 +42,5 @@
       if (IsBuffer (stack[i].name)) {
 	stack[i].buffer = SelectBuffer (stack[i].name, OLDBUFFER, TRUE);
-	stack[i].type   = 'M';
+	stack[i].type   = ST_MATRIX;
 	if (Nx == -1) {
 	  Nx = stack[i].buffer[0].matrix.Naxis[0];
@@ -63,5 +63,5 @@
       if (IsVector (stack[i].name)) {
 	stack[i].vector = SelectVector (stack[i].name, OLDVECTOR, FALSE);
-	stack[i].type   = 'V';
+	stack[i].type   = ST_VECTOR;
 
 	if (Nv == -1) Nv = stack[i].vector[0].Nelements;
@@ -80,5 +80,5 @@
 
       /* this is not a scalar, vector, or matrix.  must be string */
-      stack[i].type  = 'W';
+      stack[i].type  = ST_STRING;
     }
   }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/convert_to_RPN.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 36680)
@@ -4,5 +4,5 @@
 StackVar *convert_to_RPN (int argc, char **argv, int *nstack) {
   
-  int type;
+ StackVarType type;
   int i, j, Nstack, Nop_stack, NSTACK;
   StackVar *stack, *op_stack;
@@ -21,78 +21,88 @@
     
     /* decide on priority of object */
-    type = 0;
+    type = ST_NONE;
+
+    /* trinary operations */
+    if (!strcmp (argv[i], "?"))      { type = ST_TRINARY; goto gotit; }
+    if (!strcmp (argv[i], ":"))      { type = ST_COMMA; goto gotit; }
+
     /* unary operations */
-    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "abs"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "floor"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "round"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ceil"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "rint"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sinh"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sin"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "lgamma")) { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "rnd"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = ST_UNARY; goto gotit; }
 
     /* binary operations */
-    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
-
-    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
-
-    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
-    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "^"))      { type = ST_POWER; goto gotit; }
+
+    if (!strcmp (argv[i], "max"))    { type = ST_BINARY; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "min"))    { type = ST_BINARY; strcpy (argv[i], "D"); goto gotit; }
+    if (!strcmp (argv[i], "atan2"))  { type = ST_BINARY; goto gotit; }
+    if (!strcmp (argv[i], ","))      { type = ST_COMMA; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = ST_TIMES; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = ST_ADD; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = ST_ADD; goto gotit; }
 	
-    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
-    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
-
-    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
-    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
-    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
-    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
-    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
-    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
-    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
-    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
-
-    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
-    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
-
-    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
-    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+    if (!strcmp (argv[i], "&"))      { type = ST_BITWISE; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = ST_BITWISE; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = ST_LOGIC; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = ST_LOGIC; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = ST_LOGIC; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = ST_LOGIC; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = ST_LOGIC; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = ST_LOGIC; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = ST_LOGIC; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = ST_LOGIC; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = ST_AND; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = ST_OR ; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = ST_LEFT;  goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = ST_RIGHT; goto gotit; }
 
   gotit:
     /* choose how to deal with object */
     switch (type) {
-      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+      case ST_POWER:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
 	/* pop previous, higher operators from OP stack to stack */
 	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
@@ -105,10 +115,13 @@
 	Nop_stack ++;
 	break;
-      case 9: /* unary OPs */
-      case 7: /* binary OPs */
-      case 6:
-      case 5: 
-      case 4: 
-      case 3: 
+      case ST_UNARY: 
+      case ST_BINARY: 
+      case ST_TRINARY:
+      case ST_TIMES:
+      case ST_ADD:
+      case ST_BITWISE: 
+      case ST_LOGIC: 
+      case ST_AND: 
+      case ST_OR: 
 	/* pop previous, higher or equal operators from OP stack to stack */
 	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
@@ -121,5 +134,5 @@
 	Nop_stack ++;
 	break;
-      case 2:  
+      case ST_LEFT:  
 	/* push operator on OP stack */
 	assign_stack (&op_stack[Nop_stack], argv[i], type);
@@ -127,12 +140,12 @@
 	Nop_stack ++;
 	break;
-      case 1: 
+      case ST_RIGHT: 
 	/* pop rest of operators from OP stack to stack, looking for '(' */
-	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
-	  move_stack (&stack[Nstack], &op_stack[j]);
-	  Nstack ++;
-	  Nop_stack --;
-	}
-	if ((j == -1) || (op_stack[j].type != 2)) {
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != ST_LEFT); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != ST_LEFT)) {
 	  push_error ("syntax error: mismatched parenthesis");
 	  Nstack = 0;
@@ -144,16 +157,29 @@
 	Nop_stack --;
 	break;
-      case 0:
+      case ST_COMMA: 
+	/* pop rest of operators from OP stack to stack, looking for '(' (but do not pop the '(')*/
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != ST_LEFT) && (op_stack[j].type != ST_TRINARY); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	break;
+      case ST_NONE:
 	/* place the value (number or vector/matrix name) on stack */
 	/* value of 'X' is used as sentinel until we sort out values */
-	assign_stack (&stack[Nstack], argv[i], 'X');
+	assign_stack (&stack[Nstack], argv[i], ST_VALUE);
 	Nstack ++;
 	break;
+
+      default:
+	push_error ("invalid stack typ");
+	Nstack = 0;
+	goto cleanup;
     }
   }
 
-  /* dump remaining operators on stack, checking for ')' */
+  /* dump remaining operators on stack, checking for '(' */
   for (j = Nop_stack - 1; j >= 0; j--) {
-    if (op_stack[j].type == 2) {
+    if (op_stack[j].type == ST_LEFT) {
       push_error ("syntax error: mismatched parenthesis");
       Nstack = 0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/dvomath.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/dvomath.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/dvomath.c	(revision 36680)
@@ -80,5 +80,5 @@
 	sprintf (outname, "%s", stack[0].name);
       } else {
-	if (stack[0].type == 's') {
+	if (stack[0].type == ST_SCALAR_INT) {
 	  sprintf (outname, "%d", stack[0].IntValue);
 	} else {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/evaluate_stack.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 36680)
@@ -2,11 +2,51 @@
 # define VERBOSE 0
 
-# define TWO_OP(A,B,FUNC) \
-  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
-    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 
-
-# define ONE_OP(A,FUNC) \
-  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
-    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 
+// all three operands must have the same type
+# define THREE_OP(A,FUNC) 						\
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+
+// A & B value types all have 2 possible values
+# define TWO_OP(A,B,FUNC) {						\
+    if ((stack[i - 2].type == A) && (stack[i - 1].type == B)) {		\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A+1) && (stack[i - 1].type == B)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A) && (stack[i - 1].type == B+1)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A+1) && (stack[i - 1].type == B+1)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; } \
+  }
+
+# define ONE_OP(A,FUNC)						\
+  if (stack[i - 1].type == A) {					\
+    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name);   \
+    goto got_one_op; }
 
 int evaluate_stack (StackVar *stack, int *Nstack) {
@@ -20,9 +60,9 @@
 
   if (*Nstack == 1) {
-    if ((stack[0].type == 'S') || (stack[0].type == 's')) {
+    if ((stack[0].type == ST_SCALAR_INT) || (stack[0].type == ST_SCALAR_FLT)) {
       clear_stack (&tmp_stack);
       return (TRUE);
     }
-    if (stack[0].type == 'V') {
+    if (stack[0].type == ST_VECTOR) {
       /* need to make a copy so we set output value? */
       V_unary (&tmp_stack, &stack[0], "=");
@@ -30,5 +70,5 @@
       return (TRUE);
     }
-    if (stack[0].type == 'M') {
+    if (stack[0].type == ST_MATRIX) {
       /* need to make a copy so we set output value? */
       M_unary (&tmp_stack, &stack[0], "=");
@@ -56,75 +96,148 @@
     }
 
-    /***** binary operators *****/
-    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
-
-      if (i < 2) {  /* need two variables to operate on */
-	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+    /***** trinary operators *****/
+    switch (stack[i].type) {
+      case ST_TRINARY:
+
+	if (i < 3) {  /* need two variables to operate on */
+	  snprintf (line, 512, "syntax error: trinary operator without three operands: %s\n", stack[i].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	status = FALSE;
+	THREE_OP (ST_MATRIX,MMM_trinary);
+	THREE_OP (ST_VECTOR,VVV_trinary);
+
+	THREE_OP (ST_SCALAR_FLT,SSS_trinary);
+	THREE_OP (ST_SCALAR_INT,SSS_trinary);
+
+	/* there are no valid unary string operators */
+	push_error ("invalid operands for trinary operator (mismatch types?)");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+
+      got_three_op:
+	if (!status) {
+	  snprintf (line, 512, "syntax error: invalid operand for binary operation: %s or %s or %s\n", stack[i-1].name, stack[i-2].name, stack[i-3].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+	move_stack (&stack[i-3], &tmp_stack);
+	delete_stack (&stack[i-2], 3);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j-3], &stack[j]);
+	}
+	*Nstack -= 3;
+	i -= 3;
+	init_stack (&tmp_stack);
+	continue;
+
+	/***** binary operators *****/
+      case ST_OR:
+      case ST_AND:
+      case ST_LOGIC:
+      case ST_BITWISE:
+      case ST_ADD:
+      case ST_TIMES:
+      case ST_POWER:
+      case ST_BINARY:
+
+	if (i < 2) {  /* need two variables to operate on */
+	  snprintf (line, 512, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	status = FALSE;
+	TWO_OP (ST_MATRIX,ST_MATRIX,MM_binary);
+	TWO_OP (ST_MATRIX,ST_VECTOR,MV_binary);
+	TWO_OP (ST_MATRIX,ST_SCALAR_INT,MS_binary);
+
+	TWO_OP (ST_VECTOR,ST_MATRIX,VM_binary);
+	TWO_OP (ST_VECTOR,ST_VECTOR,VV_binary);
+	TWO_OP (ST_VECTOR,ST_SCALAR_INT,VS_binary);
+
+	TWO_OP (ST_SCALAR_INT,ST_MATRIX,SM_binary);
+	TWO_OP (ST_SCALAR_INT,ST_VECTOR,SV_binary);
+	TWO_OP (ST_SCALAR_INT,ST_SCALAR_INT,SS_binary);      
+
+	TWO_OP (ST_SCALAR_FLT,ST_MATRIX,SM_binary);
+	TWO_OP (ST_SCALAR_FLT,ST_VECTOR,SV_binary);
+	TWO_OP (ST_SCALAR_FLT,ST_SCALAR_INT,SS_binary);      
+
+	TWO_OP (ST_STRING,ST_STRING,WW_binary);      
+	TWO_OP (ST_STRING,ST_SCALAR_INT,WW_binary);      
+	TWO_OP (ST_SCALAR_INT,ST_STRING,WW_binary);      
+      
+      got_two_op:
+	if (!status) {
+	  snprintf (line, 512, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+	move_stack (&stack[i-2], &tmp_stack);
+	delete_stack (&stack[i-1], 2);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j - 2], &stack[j]);
+	}
+	*Nstack -= 2;
+	i -= 2;
+	init_stack (&tmp_stack);
+	continue;
+
+	/***** unary operators **/
+      case ST_UNARY:
+
+	if (i < 1) {  /* need one variable to operate on */
+	  push_error ("syntax error: unary operator with no operand");
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	ONE_OP (ST_MATRIX, M_unary);
+	ONE_OP (ST_MATRIX_TMP, M_unary);
+
+	ONE_OP (ST_VECTOR, V_unary);
+	ONE_OP (ST_VECTOR_TMP, V_unary);
+
+	ONE_OP (ST_SCALAR_INT, S_unary);
+	ONE_OP (ST_SCALAR_FLT, S_unary);
+
+	/* there are no valid unary string operators */
+	push_error ("syntax error: no valid string unary ops");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+
+      got_one_op:
+	move_stack (&stack[i-1], &tmp_stack);
+	delete_stack (&stack[i], 1);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j - 1], &stack[j]);
+	}
+	init_stack (&tmp_stack);
+	*Nstack -= 1;
+	i -= 1;
+	continue;
+
+      case ST_SCALAR_INT:
+      case ST_SCALAR_FLT:
+      case ST_VECTOR:
+      case ST_VECTOR_TMP:
+      case ST_MATRIX:
+      case ST_MATRIX_TMP:
+      case ST_STRING:
+	continue;
+
+      default:
+	snprintf (line, 512, "syntax error: unexpected operator type %s", stack[i].name);
 	push_error (line);
 	clear_stack (&tmp_stack);
 	return (FALSE);
-      }
-
-      status = FALSE;
-      TWO_OP ("M","M",MM_binary);
-      TWO_OP ("M","V",MV_binary);
-      TWO_OP ("M","S",MS_binary);
-      TWO_OP ("V","M",VM_binary);
-      TWO_OP ("V","V",VV_binary);
-      TWO_OP ("V","S",VS_binary);
-      TWO_OP ("S","M",SM_binary);
-      TWO_OP ("S","V",SV_binary);
-      TWO_OP ("S","S",SS_binary);      
-      TWO_OP ("W","W",WW_binary);      
-      TWO_OP ("W","S",WW_binary);      
-      TWO_OP ("S","W",WW_binary);      
-      
-      if (!status) {
-	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
-	push_error (line);
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-      move_stack (&stack[i-2], &tmp_stack);
-      delete_stack (&stack[i-1], 2);
-      for (j = i + 1; j < *Nstack; j++) {
-	move_stack (&stack[j - 2], &stack[j]);
-      }
-      *Nstack -= 2;
-      i -= 2;
-      init_stack (&tmp_stack);
-      continue;
-    }
-
-    /***** unary operators **/
-    if (stack[i].type == 9) {
-
-      if (i < 1) {  /* need one variable to operate on */
-	push_error ("syntax error: unary operator with no operand");
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-
-      ONE_OP ("M", M_unary);
-      ONE_OP ("V", V_unary);
-      ONE_OP ("S", S_unary);
-
-      /* there are no valid unary string operators */
-      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
-	push_error ("syntax error: no valid string unary ops");
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-
-      move_stack (&stack[i-1], &tmp_stack);
-      delete_stack (&stack[i], 1);
-      for (j = i + 1; j < *Nstack; j++) {
-	move_stack (&stack[j - 1], &stack[j]);
-      }
-      init_stack (&tmp_stack);
-      *Nstack -= 1;
-      i -= 1;
-      continue;
-    } 
+    }
   }
   clear_stack (&tmp_stack);
@@ -160,5 +273,5 @@
 
   for (i = 0; i < Nstack; i++) {
-    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
+    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == ST_MATRIX_TMP)) {
       if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
       free (stack[i].buffer[0].header.buffer);
@@ -167,5 +280,5 @@
       stack[i].buffer = NULL;
     }	
-    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
+    if (IsVectorPtr (stack[i].vector) && (stack[i].type == ST_VECTOR_TMP)) {
       if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
       free (stack[i].vector[0].elements.Ptr);
@@ -193,5 +306,5 @@
 }
 
-void assign_stack (StackVar *stack, char *name, int type) {
+void assign_stack (StackVar *stack, char *name, StackVarType type) {
   stack->name = strcreate (name);
   stack->type = type;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/stack_math.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/stack_math.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/lib.shell/stack_math.c	(revision 36680)
@@ -6,9 +6,46 @@
 */
 
-// XXX we temporarily drop the concept of using one of the temporary input vectors for 
-// the output vector (thus saving an ALLOC): we have to juggle the size of the input vectors 
-// as well as their temporary state
-
-int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+int SSS_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
+
+  char line[512]; // this is only used to report an error 
+  
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define SSS_FUNC(OP) {						\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_FLT) && (V3->type == ST_SCALAR_FLT)) { \
+      opihi_flt M1  =  V1[0].FltValue;			\
+      opihi_flt M2  =  V2[0].FltValue;			\
+      opihi_flt M3  =  V3[0].FltValue;			\
+      OUT[0].type = ST_SCALAR_FLT;			\
+      OUT[0].FltValue = OP;							\
+      break;								\
+    }									\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT) && (V3->type == ST_SCALAR_INT)) { \
+      opihi_int M1  =  V1[0].IntValue;			\
+      opihi_int M2  =  V2[0].IntValue;			\
+      opihi_int M3  =  V3[0].IntValue;			\
+      OUT[0].type = ST_SCALAR_INT;			\
+      OUT[0].IntValue = OP;							\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) {
+    case '?': SSS_FUNC(M1 ? M2: M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef SSS_FUNC
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+int VVV_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
 
   int i, Nx;
@@ -19,4 +56,7 @@
     return (FALSE);
   }
+  if (V1[0].vector[0].Nelements != V3[0].vector[0].Nelements) {
+    return (FALSE);
+  }
 
   Nx = V1[0].vector[0].Nelements;
@@ -24,5 +64,154 @@
   // create the output vector guaranteed to be temporary until the very end
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';
+  OUT[0].type = ST_VECTOR_TMP;
+
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define VVV_FUNC(OP) {						\
+    if ((V1->vector->type == OPIHI_FLT) && (V2->vector->type == OPIHI_FLT) && (V3->vector->type == OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *M3  =  V3[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++, M3++) {		\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->vector->type == OPIHI_INT) && (V2->vector->type == OPIHI_INT) && (V3->vector->type == OPIHI_INT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_int *M3  =  V3[0].vector[0].elements.Int;			\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++, M3++) {		\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) {
+    case '?': VVV_FUNC(*M1 ? *M2: *M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef VVV_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == ST_VECTOR_TMP) {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == ST_VECTOR_TMP) {
+    free (V2[0].vector[0].elements.Ptr);
+    free (V2[0].vector);
+  }
+  if (V3[0].type == ST_VECTOR_TMP) {
+    free (V3[0].vector[0].elements.Ptr);
+    free (V3[0].vector);
+  }
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+int MMM_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2, *M3;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == ST_MATRIX_TMP) { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].buffer = V2[0].buffer;
+      V2[0].type = ST_MATRIX; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].buffer = InitBuffer ();
+      CopyBuffer (OUT[0].buffer, V1[0].buffer);
+    }
+  }
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = (float *)V1[0].buffer[0].matrix.buffer;
+  M2  = (float *)V2[0].buffer[0].matrix.buffer;
+  M3  = (float *)V3[0].buffer[0].matrix.buffer;
+  out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define MMM_FUNC(OP)					\
+  for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++, M3++) {	\
+    *out = OP;						\
+  }							\
+  break; 
+
+  switch (op[0]) {
+    case '?': MMM_FUNC(*M1 ? *M2: *M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef MMM_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == ST_MATRIX_TMP) {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == ST_MATRIX_TMP) {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+  if (V3[0].type == ST_MATRIX_TMP) {
+    free (V3[0].buffer[0].header.buffer);
+    free (V3[0].buffer[0].matrix.buffer);
+    free (V3[0].buffer);
+  }
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+// XXX we temporarily drop the concept of using one of the temporary input vectors for 
+// the output vector (thus saving an ALLOC): we have to juggle the size of the input vectors 
+// as well as their temporary state
+
+int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  char line[512]; // this is only used to report an error 
+  
+  // the vectors have to match in length
+  if (V1[0].vector[0].Nelements != V2[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  Nx = V1[0].vector[0].Nelements;
+
+  // create the output vector guaranteed to be temporary until the very end
+  OUT[0].vector = InitVector ();
+  OUT[0].type = ST_VECTOR_TMP;
 
   // set up the possible operations : int OP int -> int, all else yield float
@@ -59,5 +248,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -82,25 +271,26 @@
 
   switch (op[0]) {
-    case '+': VV_FUNC('s', *M1 + *M2);
-    case '-': VV_FUNC('s', *M1 - *M2);
-    case '*': VV_FUNC('s', *M1 * *M2);
-    case '/': VV_FUNC('S', *M1 / (opihi_flt) *M2);
-    case '%': VV_FUNC('s', (long long)*M1 % (long long)*M2);
-    case '^': VV_FUNC('S', pow (*M1, *M2));
-    case '@': VV_FUNC('S', DEG_RAD*atan2 (*M1, *M2));
-    case 'D': VV_FUNC('s', MIN (*M1, *M2));
-    case 'U': VV_FUNC('s', MAX (*M1, *M2));
-    case '<': VV_FUNC('s', (*M1 < *M2) ? 1 : 0);
-    case '>': VV_FUNC('s', (*M1 > *M2) ? 1 : 0);
-    case '&': VV_FUNC('s', ((long long)*M1 & (long long)*M2));
-    case '|': VV_FUNC('s', ((long long)*M1 | (long long)*M2));
-    case 'E': VV_FUNC('s', (*M1 == *M2) ? 1 : 0);
-    case 'N': VV_FUNC('s', (*M1 != *M2) ? 1 : 0);
-    case 'L': VV_FUNC('s', (*M1 <= *M2) ? 1 : 0);
-    case 'G': VV_FUNC('s', (*M1 >= *M2) ? 1 : 0);
-    case 'A': VV_FUNC('s', (*M1 && *M2) ? 1 : 0);
-    case 'O': VV_FUNC('s', (*M1 || *M2) ? 1 : 0);
+    case '+': VV_FUNC(ST_SCALAR_INT, *M1 + *M2);
+    case '-': VV_FUNC(ST_SCALAR_INT, *M1 - *M2);
+    case '*': VV_FUNC(ST_SCALAR_INT, *M1 * *M2);
+    case '/': VV_FUNC(ST_SCALAR_FLT, *M1 / (opihi_flt) *M2);
+    case '%': VV_FUNC(ST_SCALAR_INT, (long long)*M1 % (long long)*M2);
+    case '^': VV_FUNC(ST_SCALAR_FLT, pow (*M1, *M2));
+    case '@': VV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, *M2));
+    case 'a': VV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, *M2));
+    case 'D': VV_FUNC(ST_SCALAR_INT, MIN (*M1, *M2));
+    case 'U': VV_FUNC(ST_SCALAR_INT, MAX (*M1, *M2));
+    case '<': VV_FUNC(ST_SCALAR_INT, (*M1 < *M2) ? 1 : 0);
+    case '>': VV_FUNC(ST_SCALAR_INT, (*M1 > *M2) ? 1 : 0);
+    case '&': VV_FUNC(ST_SCALAR_INT, ((long long)*M1 & (long long)*M2));
+    case '|': VV_FUNC(ST_SCALAR_INT, ((long long)*M1 | (long long)*M2));
+    case 'E': VV_FUNC(ST_SCALAR_INT, (*M1 == *M2) ? 1 : 0);
+    case 'N': VV_FUNC(ST_SCALAR_INT, (*M1 != *M2) ? 1 : 0);
+    case 'L': VV_FUNC(ST_SCALAR_INT, (*M1 <= *M2) ? 1 : 0);
+    case 'G': VV_FUNC(ST_SCALAR_INT, (*M1 >= *M2) ? 1 : 0);
+    case 'A': VV_FUNC(ST_SCALAR_INT, (*M1 && *M2) ? 1 : 0);
+    case 'O': VV_FUNC(ST_SCALAR_INT, (*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -110,9 +300,9 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
   }
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -134,10 +324,10 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP;   /*** <<--- says this is a temporary matrix ***/
 
   // set up the possible operations : int OP int -> int, all else yield float
   // OP is the operation performed on *M1 and *M2
 # define SV_FUNC(FTYPE,OP) {						\
-    if ((V1->type == 'S') && (V2->vector->type == OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_flt  M1  =  V1[0].FltValue;					\
@@ -149,5 +339,5 @@
       break;								\
     }									\
-    if ((V1->type == 'S') && (V2->vector->type != OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->vector->type != OPIHI_FLT)) {		\
       MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
       opihi_flt  M1  =  V1[0].FltValue;					\
@@ -159,5 +349,5 @@
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->vector->type == OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_INT) && (V2->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -169,5 +359,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->type == 's') && (V2->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT) && (V2->vector->type != OPIHI_FLT)) { \
       MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -179,5 +369,5 @@
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->vector->type != OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_INT) && (V2->vector->type != OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -192,25 +382,26 @@
 
   switch (op[0]) { 
-    case '+': SV_FUNC('s', M1 + *M2);
-    case '-': SV_FUNC('s', M1 - *M2);
-    case '*': SV_FUNC('s', M1 * *M2);
-    case '/': SV_FUNC('S', M1 / (opihi_flt) *M2);
-    case '%': SV_FUNC('s', (long long) M1 % (long long) *M2);
-    case '^': SV_FUNC('S', pow (M1, *M2));
-    case '@': SV_FUNC('S', DEG_RAD*atan2 (M1, *M2));
-    case 'D': SV_FUNC('s', MIN (M1, *M2));
-    case 'U': SV_FUNC('s', MAX (M1, *M2));
-    case '<': SV_FUNC('s', (M1 < *M2) ? 1 : 0);
-    case '>': SV_FUNC('s', (M1 > *M2) ? 1 : 0);
-    case '&': SV_FUNC('s', ((long long)M1 & (long long)*M2));
-    case '|': SV_FUNC('s', ((long long)M1 | (long long)*M2));
-    case 'E': SV_FUNC('s', (M1 == *M2) ? 1 : 0);
-    case 'N': SV_FUNC('s', (M1 != *M2) ? 1 : 0);
-    case 'L': SV_FUNC('s', (M1 <= *M2) ? 1 : 0);
-    case 'G': SV_FUNC('s', (M1 >= *M2) ? 1 : 0);
-    case 'A': SV_FUNC('s', (M1 && *M2) ? 1 : 0);
-    case 'O': SV_FUNC('s', (M1 || *M2) ? 1 : 0);
+    case '+': SV_FUNC(ST_SCALAR_INT, M1 + *M2);
+    case '-': SV_FUNC(ST_SCALAR_INT, M1 - *M2);
+    case '*': SV_FUNC(ST_SCALAR_INT, M1 * *M2);
+    case '/': SV_FUNC(ST_SCALAR_FLT, M1 / (opihi_flt) *M2);
+    case '%': SV_FUNC(ST_SCALAR_INT, (long long) M1 % (long long) *M2);
+    case '^': SV_FUNC(ST_SCALAR_FLT, pow (M1, *M2));
+    case '@': SV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, *M2));
+    case 'a': SV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, *M2));
+    case 'D': SV_FUNC(ST_SCALAR_INT, MIN (M1, *M2));
+    case 'U': SV_FUNC(ST_SCALAR_INT, MAX (M1, *M2));
+    case '<': SV_FUNC(ST_SCALAR_INT, (M1 < *M2) ? 1 : 0);
+    case '>': SV_FUNC(ST_SCALAR_INT, (M1 > *M2) ? 1 : 0);
+    case '&': SV_FUNC(ST_SCALAR_INT, ((long long)M1 & (long long)*M2));
+    case '|': SV_FUNC(ST_SCALAR_INT, ((long long)M1 | (long long)*M2));
+    case 'E': SV_FUNC(ST_SCALAR_INT, (M1 == *M2) ? 1 : 0);
+    case 'N': SV_FUNC(ST_SCALAR_INT, (M1 != *M2) ? 1 : 0);
+    case 'L': SV_FUNC(ST_SCALAR_INT, (M1 <= *M2) ? 1 : 0);
+    case 'G': SV_FUNC(ST_SCALAR_INT, (M1 >= *M2) ? 1 : 0);
+    case 'A': SV_FUNC(ST_SCALAR_INT, (M1 && *M2) ? 1 : 0);
+    case 'O': SV_FUNC(ST_SCALAR_INT, (M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -219,5 +410,5 @@
 
   /** free up any temporary buffers: **/
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -240,10 +431,10 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP;   /*** <<--- says this is a temporary matrix ***/
 
   // set up the possible operations : int OP int -> int, all else yield float
   // OP is the operation performed on *M1 and *M2
 # define VS_FUNC(FTYPE,OP) {						\
-    if ((V2->type == 'S') && (V1->vector->type == OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_FLT) && (V1->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
@@ -255,5 +446,5 @@
       break;								\
     }									\
-    if ((V2->type == 'S') && (V1->vector->type != OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_FLT) && (V1->vector->type != OPIHI_FLT)) {		\
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -265,5 +456,5 @@
       break;								\
     }									\
-    if ((V2->type == 's') && (V1->vector->type == OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_INT) && (V1->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
@@ -275,5 +466,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V2->type == 's') && (V1->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_INT) && (V1->vector->type != OPIHI_FLT)) { \
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -285,5 +476,5 @@
       break;								\
     }									\
-    if ((V2->type == 's') && (V1->vector->type != OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_INT) && (V1->vector->type != OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -298,25 +489,26 @@
 
   switch (op[0]) { 
-    case '+': VS_FUNC('s', *M1 + M2);
-    case '-': VS_FUNC('s', *M1 - M2);
-    case '*': VS_FUNC('s', *M1 * M2);
-    case '/': VS_FUNC('S', *M1 / (opihi_flt) M2);
-    case '%': VS_FUNC('s', (long long) *M1 % (long long) M2);
-    case '^': VS_FUNC('S', pow (*M1, M2));
-    case '@': VS_FUNC('S', DEG_RAD*atan2 (*M1, M2));
-    case 'D': VS_FUNC('s', MIN (*M1, M2));
-    case 'U': VS_FUNC('s', MAX (*M1, M2));
-    case '<': VS_FUNC('s', (*M1 < M2) ? 1 : 0);
-    case '>': VS_FUNC('s', (*M1 > M2) ? 1 : 0);
-    case '&': VS_FUNC('s', ((long long)*M1 & (long long)M2));
-    case '|': VS_FUNC('s', ((long long)*M1 | (long long)M2));
-    case 'E': VS_FUNC('s', (*M1 == M2) ? 1 : 0);
-    case 'N': VS_FUNC('s', (*M1 != M2) ? 1 : 0);
-    case 'L': VS_FUNC('s', (*M1 <= M2) ? 1 : 0);
-    case 'G': VS_FUNC('s', (*M1 >= M2) ? 1 : 0);
-    case 'A': VS_FUNC('s', (*M1 && M2) ? 1 : 0);
-    case 'O': VS_FUNC('s', (*M1 || M2) ? 1 : 0);
+    case '+': VS_FUNC(ST_SCALAR_INT, *M1 + M2);
+    case '-': VS_FUNC(ST_SCALAR_INT, *M1 - M2);
+    case '*': VS_FUNC(ST_SCALAR_INT, *M1 * M2);
+    case '/': VS_FUNC(ST_SCALAR_FLT, *M1 / (opihi_flt) M2);
+    case '%': VS_FUNC(ST_SCALAR_INT, (long long) *M1 % (long long) M2);
+    case '^': VS_FUNC(ST_SCALAR_FLT, pow (*M1, M2));
+    case '@': VS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, M2));
+    case 'a': VS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, M2));
+    case 'D': VS_FUNC(ST_SCALAR_INT, MIN (*M1, M2));
+    case 'U': VS_FUNC(ST_SCALAR_INT, MAX (*M1, M2));
+    case '<': VS_FUNC(ST_SCALAR_INT, (*M1 < M2) ? 1 : 0);
+    case '>': VS_FUNC(ST_SCALAR_INT, (*M1 > M2) ? 1 : 0);
+    case '&': VS_FUNC(ST_SCALAR_INT, ((long long)*M1 & (long long)M2));
+    case '|': VS_FUNC(ST_SCALAR_INT, ((long long)*M1 | (long long)M2));
+    case 'E': VS_FUNC(ST_SCALAR_INT, (*M1 == M2) ? 1 : 0);
+    case 'N': VS_FUNC(ST_SCALAR_INT, (*M1 != M2) ? 1 : 0);
+    case 'L': VS_FUNC(ST_SCALAR_INT, (*M1 <= M2) ? 1 : 0);
+    case 'G': VS_FUNC(ST_SCALAR_INT, (*M1 >= M2) ? 1 : 0);
+    case 'A': VS_FUNC(ST_SCALAR_INT, (*M1 && M2) ? 1 : 0);
+    case 'O': VS_FUNC(ST_SCALAR_INT, (*M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -326,5 +518,5 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
@@ -353,7 +545,7 @@
 
   /* if possible, use V1 as temp buffer, otherwise create new one */
-  if (V1[0].type == 'm') {  
+  if (V1[0].type == ST_MATRIX_TMP) {  
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {  
     /* do buffer.matrix.buffer and buffer.header.buffer get correctly zeroed? */
@@ -361,5 +553,5 @@
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float     *M1  = (float *) V1[0].buffer[0].matrix.buffer;
@@ -395,4 +587,5 @@
     case '^': MV_FUNC(pow (*M1, *M2));
     case '@': MV_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': MV_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': MV_FUNC(MIN (*M1, *M2));
     case 'U': MV_FUNC(MAX (*M1, *M2));
@@ -408,5 +601,5 @@
     case 'O': MV_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -416,10 +609,10 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
     free (V1[0].buffer);
   }
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -446,12 +639,12 @@
 
   /* if possible, use V2 as temp buffer, otherwise create new one */
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V2[0].buffer;
-    V2[0].type = 'M'; /* prevent it from being freed below */
+    V2[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {  /* no spare temp buffer */
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V2[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float     *M2  = (float *) V2[0].buffer[0].matrix.buffer;
@@ -487,4 +680,5 @@
     case '^': VM_FUNC(pow (*M1, *M2));
     case '@': VM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': VM_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': VM_FUNC(MIN (*M1, *M2));
     case 'U': VM_FUNC(MAX (*M1, *M2));
@@ -500,5 +694,5 @@
     case 'O': VM_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -508,9 +702,9 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
   }
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -535,11 +729,11 @@
   Ny = V1[0].buffer[0].matrix.Naxis[1];
 
-  if (V1[0].type == 'm') {  /** use V1 as temp buffer **/
+  if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
-    if (V2[0].type == 'm') { /** use V2 as temp buffer, but header of V1 **/
+    if (V2[0].type == ST_MATRIX_TMP) { /** use V2 as temp buffer, but header of V1 **/
       OUT[0].buffer = V2[0].buffer;
-      V2[0].type = 'M'; /* prevent it from being freed below */
+      V2[0].type = ST_MATRIX; /* prevent it from being freed below */
     } else {  /* no spare temp buffer */
       OUT[0].buffer = InitBuffer ();
@@ -547,5 +741,5 @@
     }
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   M1  = (float *)V1[0].buffer[0].matrix.buffer;
@@ -567,4 +761,5 @@
     case '^': MM_FUNC(pow (*M1, *M2));
     case '@': MM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': MM_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': MM_FUNC(MIN (*M1, *M2));
     case 'U': MM_FUNC(MAX (*M1, *M2));
@@ -580,5 +775,5 @@
     case 'O': MM_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -588,10 +783,10 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
     free (V1[0].buffer);
   }
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -615,12 +810,12 @@
 
   /* if possible, use V1 as temp buffer, otherwise create new one */
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP;      /*** <<--- says this is a temporary matrix ***/
 
   float *M1    = (float *)V1[0].buffer[0].matrix.buffer;
@@ -628,5 +823,5 @@
 
 # define MS_FUNC(OP) {					\
-    if (V2->type == 'S')  {				\
+    if (V2->type == ST_SCALAR_FLT)  {				\
       opihi_flt M2 = V2[0].FltValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
@@ -635,5 +830,5 @@
       break;						\
     }							\
-    if (V2->type == 's')  {				\
+    if (V2->type == ST_SCALAR_INT)  {				\
       opihi_int M2 = V2[0].IntValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
@@ -652,4 +847,5 @@
     case '^': MS_FUNC(pow (*M1, M2));
     case '@': MS_FUNC(DEG_RAD*atan2 (*M1, M2));
+    case 'a': MS_FUNC(DEG_RAD*atan2 (*M1, M2));
     case 'D': MS_FUNC(MIN (*M1, M2));
     case 'U': MS_FUNC(MAX (*M1, M2));
@@ -665,5 +861,5 @@
     case 'O': MS_FUNC((*M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -671,5 +867,5 @@
 # undef MS_FUNC
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
@@ -691,12 +887,12 @@
   Ny = V2[0].buffer[0].matrix.Naxis[1];
 
-  if (V2[0].type == 'm') {  /* V2[0] is NOT temporary, we can't use it for storage */
+  if (V2[0].type == ST_MATRIX_TMP) {  /* V2[0] is NOT temporary, we can't use it for storage */
     OUT[0].buffer = V2[0].buffer;
-    V2[0].type = 'M'; /* prevent it from being freed below */
+    V2[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V2[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float *M2    = (float *)V2[0].buffer[0].matrix.buffer;
@@ -704,5 +900,5 @@
 
 # define SM_FUNC(OP) {					\
-    if (V1->type == 'S')  {				\
+    if (V1->type == ST_SCALAR_FLT)  {				\
       opihi_flt M1 = V1[0].FltValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
@@ -711,5 +907,5 @@
       break;						\
     }							\
-    if (V1->type == 's')  {				\
+    if (V1->type == ST_SCALAR_INT)  {				\
       opihi_int M1 = V1[0].IntValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
@@ -728,4 +924,5 @@
     case '^': SM_FUNC(pow (M1, *M2));
     case '@': SM_FUNC(DEG_RAD*atan2 (M1, *M2));
+    case 'a': SM_FUNC(DEG_RAD*atan2 (M1, *M2));
     case 'D': SM_FUNC(MIN (M1, *M2));
     case 'U': SM_FUNC(MAX (M1, *M2));
@@ -741,5 +938,5 @@
     case 'O': SM_FUNC((M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -747,5 +944,5 @@
 # undef SM_FUNC
 
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -764,36 +961,36 @@
 
 # define SS_FUNC(FTYPE,OP) {						\
-    if ((V1->type == 'S') && (V2->type == 'S')) {			\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_FLT)) {			\
       opihi_flt M1 = V1[0].FltValue;					\
       opihi_flt M2 = V2[0].FltValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 'S') && (V2->type == 's')) {			\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_INT)) {			\
       opihi_flt M1 = V1[0].FltValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->type == 'S')) {			\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_FLT)) {			\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_flt M2 = V2[0].FltValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->type == 's') && (V2->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT)) {	\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->type == 's')) {			\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT)) {			\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 's';						\
+      OUT[0].type = ST_SCALAR_INT;						\
       OUT[0].IntValue = OP;						\
       break;								\
@@ -802,25 +999,26 @@
 
   switch (op[0]) { 
-    case '+': SS_FUNC('s', M1 + M2);
-    case '-': SS_FUNC('s', M1 - M2);
-    case '*': SS_FUNC('s', M1 * M2);
-    case '/': SS_FUNC('S', M1 / (opihi_flt) M2);
-    case '%': SS_FUNC('s', (long long) M1 % (long long) M2);
-    case '^': SS_FUNC('S', pow (M1, M2));
-    case '@': SS_FUNC('S', DEG_RAD*atan2 (M1, M2));
-    case 'D': SS_FUNC('s', MIN (M1, M2));
-    case 'U': SS_FUNC('s', MAX (M1, M2));
-    case '<': SS_FUNC('s', (M1 < M2) ? 1 : 0);
-    case '>': SS_FUNC('s', (M1 > M2) ? 1 : 0);
-    case '&': SS_FUNC('s', ((long long)M1 & (long long)M2));
-    case '|': SS_FUNC('s', ((long long)M1 | (long long)M2));
-    case 'E': SS_FUNC('s', (M1 == M2) ? 1 : 0);
-    case 'N': SS_FUNC('s', (M1 != M2) ? 1 : 0);
-    case 'L': SS_FUNC('s', (M1 <= M2) ? 1 : 0);
-    case 'G': SS_FUNC('s', (M1 >= M2) ? 1 : 0);
-    case 'A': SS_FUNC('s', (M1 && M2) ? 1 : 0);
-    case 'O': SS_FUNC('s', (M1 || M2) ? 1 : 0);
+    case '+': SS_FUNC(ST_SCALAR_INT, M1 + M2);
+    case '-': SS_FUNC(ST_SCALAR_INT, M1 - M2);
+    case '*': SS_FUNC(ST_SCALAR_INT, M1 * M2);
+    case '/': SS_FUNC(ST_SCALAR_FLT, M1 / (opihi_flt) M2);
+    case '%': SS_FUNC(ST_SCALAR_INT, (long long) M1 % (long long) M2);
+    case '^': SS_FUNC(ST_SCALAR_FLT, pow (M1, M2));
+    case '@': SS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, M2));
+    case 'a': SS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, M2));
+    case 'D': SS_FUNC(ST_SCALAR_INT, MIN (M1, M2));
+    case 'U': SS_FUNC(ST_SCALAR_INT, MAX (M1, M2));
+    case '<': SS_FUNC(ST_SCALAR_INT, (M1 < M2) ? 1 : 0);
+    case '>': SS_FUNC(ST_SCALAR_INT, (M1 > M2) ? 1 : 0);
+    case '&': SS_FUNC(ST_SCALAR_INT, ((long long)M1 & (long long)M2));
+    case '|': SS_FUNC(ST_SCALAR_INT, ((long long)M1 | (long long)M2));
+    case 'E': SS_FUNC(ST_SCALAR_INT, (M1 == M2) ? 1 : 0);
+    case 'N': SS_FUNC(ST_SCALAR_INT, (M1 != M2) ? 1 : 0);
+    case 'L': SS_FUNC(ST_SCALAR_INT, (M1 <= M2) ? 1 : 0);
+    case 'G': SS_FUNC(ST_SCALAR_INT, (M1 >= M2) ? 1 : 0);
+    case 'A': SS_FUNC(ST_SCALAR_INT, (M1 && M2) ? 1 : 0);
+    case 'O': SS_FUNC(ST_SCALAR_INT, (M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -844,5 +1042,5 @@
 
   if ((op[0] != 'N') && (op[0] != 'E')) {
-    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    snprintf (line, 512, "error: op %c not defined for string operations!", op[0]);
     push_error (line);
     return (FALSE);
@@ -853,9 +1051,17 @@
      thus: string == number -> false */
 
-  if (!strncasecmp (&V1[0].type, "S", 1)) {
+  if (V1[0].type == ST_SCALAR_INT) {
     value = (op[0] == 'N');
     goto escape;
   }
-  if (!strncasecmp (&V2[0].type, "S", 1)) {
+  if (V1[0].type == ST_SCALAR_FLT) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (V2[0].type == ST_SCALAR_INT) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (V2[0].type == ST_SCALAR_FLT) {
     value = (op[0] == 'N');
     goto escape;
@@ -870,5 +1076,5 @@
     break; 
     default:
-      sprintf (line, "error: op %c not defined for string operations!", op[0]);
+      snprintf (line, 512, "error: op %c not defined for string operations!", op[0]);
       push_error (line);
       return (FALSE);
@@ -877,5 +1083,5 @@
 escape:
   OUT[0].FltValue = value;
-  OUT[0].type = 'S';
+  OUT[0].type = ST_SCALAR_FLT;
 
   clear_stack (V1);
@@ -891,21 +1097,21 @@
   
 # define S_FUNC(OP,FTYPE) {			\
-    if (V1->type == 'S') {			\
+    if (V1->type == ST_SCALAR_FLT) {			\
       opihi_flt M1  = V1[0].FltValue;		\
-      OUT[0].type = 'S';			\
+      OUT[0].type = ST_SCALAR_FLT;			\
       OUT[0].FltValue = OP;			\
       clear_stack (V1);				\
       return (TRUE);				\
     }						\
-    if ((FTYPE == 'S') && (V1->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT)) {	\
       opihi_int M1  = V1[0].IntValue;		\
-      OUT[0].type = 'S';			\
+      OUT[0].type = ST_SCALAR_FLT;			\
       OUT[0].FltValue = OP;			\
       clear_stack (V1);				\
       return (TRUE);				\
     }						\
-    if ((FTYPE == 's') && (V1->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_INT) && (V1->type == ST_SCALAR_INT)) {	\
       opihi_int M1  = V1[0].IntValue;		\
-      OUT[0].type = 's';			\
+      OUT[0].type = ST_SCALAR_INT;			\
       OUT[0].IntValue = OP;			\
       clear_stack (V1);				\
@@ -914,40 +1120,43 @@
   }
 
-  if (!strcmp (op, "="))      S_FUNC(M1, 's');
-  if (!strcmp (op, "abs"))    S_FUNC(fabs(M1), 's');
-  if (!strcmp (op, "int"))    S_FUNC((long long)(M1), 's');
-  if (!strcmp (op, "exp"))    S_FUNC(exp (M1), 'S');
-  if (!strcmp (op, "ten"))    S_FUNC(pow (10.0,M1), 'S');
-  if (!strcmp (op, "log"))    S_FUNC(log10 (M1), 'S');
-  if (!strcmp (op, "ln"))     S_FUNC(log (M1), 'S');
-  if (!strcmp (op, "sqrt"))   S_FUNC(sqrt (M1), 'S');
-  if (!strcmp (op, "erf"))    S_FUNC(erf (M1), 'S');
-  if (!strcmp (op, "sinh"))   S_FUNC(sinh (M1), 'S');
-  if (!strcmp (op, "cosh"))   S_FUNC(cosh (M1), 'S');
-  if (!strcmp (op, "asinh"))  S_FUNC(asinh (M1), 'S');
-  if (!strcmp (op, "acosh"))  S_FUNC(acosh (M1), 'S');
-  if (!strcmp (op, "lgamma")) S_FUNC(lgamma (M1), 'S');
-  if (!strcmp (op, "sin"))    S_FUNC(sin (M1), 'S');
-  if (!strcmp (op, "cos"))    S_FUNC(cos (M1), 'S');
-  if (!strcmp (op, "tan"))    S_FUNC(tan (M1), 'S');
-  if (!strcmp (op, "dsin"))   S_FUNC(sin (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dcos"))   S_FUNC(cos (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dtan"))   S_FUNC(tan (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "asin"))   S_FUNC(asin (M1), 'S');
-  if (!strcmp (op, "acos"))   S_FUNC(acos (M1), 'S');
-  if (!strcmp (op, "atan"))   S_FUNC(atan (M1), 'S');
-  if (!strcmp (op, "dasin"))  S_FUNC(asin (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "dacos"))  S_FUNC(acos (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "datan"))  S_FUNC(atan (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "rnd"))    S_FUNC(M1*0.0 + drand48(), 'S');
-  if (!strcmp (op, "not"))    S_FUNC(!(M1), 's');
-  if (!strcmp (op, "--"))     S_FUNC(-1*M1, 's'); // NOTE: opihi_int is signed, 
-  if (!strcmp (op, "isinf"))  S_FUNC(!finite(M1), 'S'); // XXX modify in future 
-  if (!strcmp (op, "isnan"))  S_FUNC(isnan(M1), 'S'); // XXX modify in future   
+  if (!strcmp (op, "="))      S_FUNC(M1, ST_SCALAR_INT);
+  if (!strcmp (op, "abs"))    S_FUNC(fabs(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "int"))    S_FUNC((long long)(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "floor"))  S_FUNC(floor (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ceil"))   S_FUNC(ceil (M1), ST_SCALAR_FLT);
+  // if (!strcmp (op, "rint"))   S_FUNC(nearbyint (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "exp"))    S_FUNC(exp (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ten"))    S_FUNC(pow (10.0,M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "log"))    S_FUNC(log10 (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ln"))     S_FUNC(log (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sqrt"))   S_FUNC(sqrt (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "erf"))    S_FUNC(erf (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sinh"))   S_FUNC(sinh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cosh"))   S_FUNC(cosh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "asinh"))  S_FUNC(asinh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acosh"))  S_FUNC(acosh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "lgamma")) S_FUNC(lgamma (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sin"))    S_FUNC(sin (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cos"))    S_FUNC(cos (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "tan"))    S_FUNC(tan (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dsin"))   S_FUNC(sin (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dcos"))   S_FUNC(cos (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dtan"))   S_FUNC(tan (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "asin"))   S_FUNC(asin (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acos"))   S_FUNC(acos (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "atan"))   S_FUNC(atan (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dasin"))  S_FUNC(asin (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "dacos"))  S_FUNC(acos (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "datan"))  S_FUNC(atan (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "rnd"))    S_FUNC(M1*0.0 + drand48(), ST_SCALAR_FLT);
+  if (!strcmp (op, "not"))    S_FUNC(!(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "--"))     S_FUNC(-1*M1, ST_SCALAR_INT); // NOTE: opihi_int is signed, 
+  if (!strcmp (op, "isinf"))  S_FUNC(!finite(M1), ST_SCALAR_FLT); // XXX modify in future 
+  if (!strcmp (op, "isnan"))  S_FUNC(isnan(M1), ST_SCALAR_FLT); // XXX modify in future   
 
 # undef S_FUNC
 
   clear_stack (V1);
-  sprintf (line, "error: op %s not defined!", op);
+  snprintf (line, 512, "error: op %s not defined!", op);
   push_error (line);
   return (FALSE);
@@ -962,5 +1171,5 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP; /*** <<--- says this is a temporary matrix ***/
 
 # define V_FUNC(OP,FTYPE) {					\
@@ -974,5 +1183,5 @@
       goto escape;						\
     }								\
-    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 'S')) {	\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == ST_SCALAR_FLT)) {	\
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);	\
       opihi_int *M1  = V1[0].vector[0].elements.Int;		\
@@ -983,5 +1192,5 @@
       goto escape;						\
     }								\
-    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 's')) {	\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == ST_SCALAR_INT)) {	\
       CopyVector (OUT[0].vector, V1[0].vector);			\
       opihi_int *M1  = V1[0].vector[0].elements.Int;		\
@@ -993,39 +1202,42 @@
     } }							
 
-  if (!strcmp (op, "="))      V_FUNC(*M1, 's');
-  if (!strcmp (op, "abs"))    V_FUNC(fabs(*M1), 's');
-  if (!strcmp (op, "int"))    V_FUNC((long long)(*M1), 's');
-  if (!strcmp (op, "exp"))    V_FUNC(exp(*M1), 'S');
-  if (!strcmp (op, "ten"))    V_FUNC(pow(10.0,*M1), 'S');
-  if (!strcmp (op, "log"))    V_FUNC(log10(*M1), 'S');
-  if (!strcmp (op, "ln"))     V_FUNC(log(*M1), 'S');
-  if (!strcmp (op, "sqrt"))   V_FUNC(sqrt(*M1), 'S');
-  if (!strcmp (op, "erf"))    V_FUNC(erf(*M1), 'S');
-  if (!strcmp (op, "sinh"))   V_FUNC(sinh(*M1), 'S');
-  if (!strcmp (op, "cosh"))   V_FUNC(cosh(*M1), 'S');
-  if (!strcmp (op, "asinh"))  V_FUNC(asinh(*M1), 'S');
-  if (!strcmp (op, "acosh"))  V_FUNC(acosh(*M1), 'S');
-  if (!strcmp (op, "lgamma")) V_FUNC(lgamma(*M1), 'S');
-  if (!strcmp (op, "sin"))    V_FUNC(sin(*M1), 'S');
-  if (!strcmp (op, "cos"))    V_FUNC(cos(*M1), 'S');
-  if (!strcmp (op, "tan"))    V_FUNC(tan(*M1), 'S');
-  if (!strcmp (op, "dsin"))   V_FUNC(sin(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dcos"))   V_FUNC(cos(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dtan"))   V_FUNC(tan(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "asin"))   V_FUNC(asin(*M1), 'S');
-  if (!strcmp (op, "acos"))   V_FUNC(acos(*M1), 'S');
-  if (!strcmp (op, "atan"))   V_FUNC(atan(*M1), 'S');
-  if (!strcmp (op, "dasin"))  V_FUNC(asin(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "dacos"))  V_FUNC(acos(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "datan"))  V_FUNC(atan(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "rnd"))    V_FUNC(drand48(), 'S');
-  if (!strcmp (op, "ramp"))   V_FUNC(i, 's');
-  if (!strcmp (op, "zero"))   V_FUNC(0, 's');
-  if (!strcmp (op, "not"))    V_FUNC(!(*M1), 's');
-  if (!strcmp (op, "--"))     V_FUNC(-1*(*M1), 's'); // NOTE: opihi_int is signed
-  if (!strcmp (op, "isinf"))  V_FUNC(!finite(*M1), 'S');
-  if (!strcmp (op, "isnan"))  V_FUNC(isnan(*M1), 'S');
-  if (!strcmp (op, "xramp"))  V_FUNC(i, 's');
-  if (!strcmp (op, "yramp"))  V_FUNC(0, 's');
+  if (!strcmp (op, "="))      V_FUNC(*M1, ST_SCALAR_INT);
+  if (!strcmp (op, "abs"))    V_FUNC(fabs(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "int"))    V_FUNC((long long)(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "floor"))  V_FUNC(floor (*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ceil"))   V_FUNC(ceil (*M1), ST_SCALAR_FLT);
+  // if (!strcmp (op, "rint"))   V_FUNC(nearbyint (*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "exp"))    V_FUNC(exp(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ten"))    V_FUNC(pow(10.0,*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "log"))    V_FUNC(log10(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ln"))     V_FUNC(log(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sqrt"))   V_FUNC(sqrt(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "erf"))    V_FUNC(erf(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sinh"))   V_FUNC(sinh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cosh"))   V_FUNC(cosh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "asinh"))  V_FUNC(asinh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acosh"))  V_FUNC(acosh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "lgamma")) V_FUNC(lgamma(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sin"))    V_FUNC(sin(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cos"))    V_FUNC(cos(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "tan"))    V_FUNC(tan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dsin"))   V_FUNC(sin(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dcos"))   V_FUNC(cos(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dtan"))   V_FUNC(tan(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "asin"))   V_FUNC(asin(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acos"))   V_FUNC(acos(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "atan"))   V_FUNC(atan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dasin"))  V_FUNC(asin(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "dacos"))  V_FUNC(acos(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "datan"))  V_FUNC(atan(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "rnd"))    V_FUNC(drand48(), ST_SCALAR_FLT);
+  if (!strcmp (op, "ramp"))   V_FUNC(i, ST_SCALAR_INT);
+  if (!strcmp (op, "zero"))   V_FUNC(0, ST_SCALAR_INT);
+  if (!strcmp (op, "not"))    V_FUNC(!(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "--"))     V_FUNC(-1*(*M1), ST_SCALAR_INT); // NOTE: opihi_int is signed
+  if (!strcmp (op, "isinf"))  V_FUNC(!finite(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "isnan"))  V_FUNC(isnan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "xramp"))  V_FUNC(i, ST_SCALAR_INT);
+  if (!strcmp (op, "yramp"))  V_FUNC(0, ST_SCALAR_INT);
   /* xramp and yramp above only make sense for matrices. for vectors, xramp = ramp, yramp = zero */
 
@@ -1034,5 +1246,5 @@
 escape:
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
@@ -1053,12 +1265,12 @@
   Ny = V1[0].buffer[0].matrix.Naxis[1];
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP;      /*** <<--- says this is a temporary matrix ***/
   M1  = (float *) V1[0].buffer[0].matrix.buffer;
   out = (float *)OUT[0].buffer[0].matrix.buffer;
@@ -1067,4 +1279,9 @@
   if (!strcmp (op, "abs"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = fabs(*M1);         }}
   if (!strcmp (op, "int"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = (opihi_flt)(long long)(*M1); }}
+
+  if (!strcmp (op, "floor")) { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = floor (*M1); }}
+  if (!strcmp (op, "ceil"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = ceil (*M1); }}
+  // if (!strcmp (op, "rint"))  { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = nearbyint (*M1); }}
+
   if (!strcmp (op, "exp"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = exp(*M1);          }}
   if (!strcmp (op, "ten"))   { for (i = 0; i < Nx*Ny; i++, out++, M1++) { *out = pow(10.0,*M1);     }}
@@ -1116,5 +1333,5 @@
   }
   
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/CheckController.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/CheckController.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/CheckController.c	(revision 36680)
@@ -29,10 +29,23 @@
   if (p != NULL) goto escape;
 
+  /** skip past any leading garbage? ***/
+
+  /** find "Njobs: **/
+  p = memstr (buffer.buffer, "BEGIN BLOCK Njobs:", MIN(buffer.Nbuffer, 256));
+  p += strlen("BEGIN BLOCK Njobs:");
+
   /** parse job list **/
-  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  status = sscanf (p, "%d", &Njobs);
   if (status != 1) goto escape;
   if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(&start, TRUE));
 
-  p = buffer.buffer;
+  /* output looks like:
+     BEGIN BLOCK Njobs: NN\n
+     ID name machine\n
+     ID name machine\n
+  */
+
+  // p is currently pointing at "BEGIN BLOCK Njobs"
+
   for (i = 0; i < Njobs; i++) {
     q = strchr (p, '\n');
@@ -67,10 +80,23 @@
   if (p != NULL) goto escape;
 
+  /** skip past any leading garbage? ***/
+
+  /** find "Njobs: **/
+  p = memstr (buffer.buffer, "BEGIN BLOCK Njobs:", MIN(buffer.Nbuffer, 256));
+  p += strlen("BEGIN BLOCK Njobs:");
+
   /** parse job list **/
-  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  status = sscanf (p, "%d", &Njobs);
   if (status != 1) goto escape;
   if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(&start, TRUE)); 
 
-  p = buffer.buffer;
+  /* output looks like:
+     BEGIN BLOCK Njobs: NN\n
+     ID name machine\n
+     ID name machine\n
+  */
+
+  // p is currently pointing at "BEGIN BLOCK Njobs"
+
   for (i = 0; i < Njobs; i++) {
     q = strchr (p, '\n');
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 36680)
@@ -259,7 +259,7 @@
   }
   
-  if (job[0].priority) {
+  if (job[0].nicelevel) {
     char tmp[64];
-    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    snprintf (tmp, 64, " -nice %d", job[0].nicelevel);
     strcat (cmd, tmp);
   }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/JobOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/JobOps.c	(revision 36680)
@@ -107,5 +107,5 @@
     job[0].mode = JOB_CONTROLLER;
   }
-  job[0].priority = task[0].priority;
+  job[0].nicelevel = task[0].nicelevel;
 
   /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/LocalJob.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/LocalJob.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/LocalJob.c	(revision 36680)
@@ -162,8 +162,8 @@
 
   /* set nice level for the child process -- maybe I should not exit here... */
-  if (job[0].priority) {
-      status = setpriority (PRIO_PROCESS, pid, job[0].priority);
+  if (job[0].nicelevel) {
+      status = setpriority (PRIO_PROCESS, pid, job[0].nicelevel);
       if (status == -1) {
-	  gprint (GP_ERR, "error setting priority\n");
+	  gprint (GP_ERR, "error setting nice level\n");
 	  perror ("setpriority: ");
 	  exit (2);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/TaskOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/TaskOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/TaskOps.c	(revision 36680)
@@ -487,5 +487,5 @@
 
   NewTask[0].active = TRUE;
-  NewTask[0].priority = 0;
+  NewTask[0].nicelevel = 0;
   return (NewTask);
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init.c	(revision 36680)
@@ -34,5 +34,5 @@
   {1, "halt",       halt,          "halt the scheduler (no job harvesting)"},
   {1, "host",       task_host,     "define host machine for a task"},
-  {1, "nice",       task_nice,     "set nice priority level for a task"},
+  {1, "nice",       task_nice,     "set nice level for a task"},
   {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
   {1, "kill",       kill_job,      "kill job"},
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init_server.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init_server.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/init_server.c	(revision 36680)
@@ -35,5 +35,5 @@
   {1, "delete",     delete_job,    "delete job"},
   {1, "host",       task_host,     "define host machine for a task"},
-  {1, "nice",       task_nice,     "set nice priority level for a task"},
+  {1, "nice",       task_nice,     "set nice level for a task"},
   {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
   {1, "kill",       kill_job,      "kill job"},
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/task_nice.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/task_nice.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pantasks/task_nice.c	(revision 36680)
@@ -4,9 +4,9 @@
 
   char *endptr = NULL;
-  int priority;
+  int nicelevel;
   Task *task;
 
   if (argc != 2) {
-    gprint (GP_ERR, "USAGE: nice (priority)\n");
+    gprint (GP_ERR, "USAGE: nice (nicelevel)\n");
     return (FALSE);
   }
@@ -20,15 +20,15 @@
   }
 
-  priority = strtol (argv[1], &endptr, 10);
+  nicelevel = strtol (argv[1], &endptr, 10);
   if (*endptr) goto fail;
-  if (priority < 0) goto fail;
-  if (priority > 20) goto fail;
+  if (nicelevel < 0) goto fail;
+  if (nicelevel > 20) goto fail;
 
-  task[0].priority = priority;
+  task[0].nicelevel = nicelevel;
   JobTaskUnlock();
   return (TRUE);
 
 fail:
-    gprint (GP_ERR, "ERROR: nice (priority) -- priority must be an integer 0 to 20\n");
+    gprint (GP_ERR, "ERROR: nice (nicelevel) -- nicelevel must be an integer 0 to 20\n");
     return (FALSE);
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pclient/job.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pclient/job.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pclient/job.c	(revision 36680)
@@ -3,11 +3,11 @@
 int job (int argc, char **argv) {
 
-  int i, N, pid, status, priority;
+  int i, N, pid, status, nicelevel;
   char **targv;
 
-  priority = 0;
+  nicelevel = 0;
   if ((N = get_argument (argc, argv, "-nice"))) {
     remove_argument (N, &argc, argv);
-    priority = atoi (argv[N]);
+    nicelevel = atoi (argv[N]);
     remove_argument (N, &argc, argv);
   }
@@ -62,8 +62,8 @@
 
   /* set nice level for the child process */
-  if (priority) {
-      status = setpriority (PRIO_PROCESS, pid, priority);
+  if (nicelevel) {
+      status = setpriority (PRIO_PROCESS, pid, nicelevel);
       if (status == -1) {
-	  gprint (GP_ERR, "error setting priority\n");
+	  gprint (GP_ERR, "error setting nicelevel\n");
 	  perror ("setpriority: ");
 	  exit (2);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/CheckBusyJob.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 36680)
@@ -99,6 +99,6 @@
 
   // XXX runaway job if output too large?
-  if (job[0].stdout_buf.size > 0x1000000) abort();
-  if (job[0].stderr_buf.size > 0x1000000) abort();
+  if (job[0].stdout_buf.size > 0x10000000) abort();
+  if (job[0].stderr_buf.size > 0x10000000) abort();
 
   // job has exited : move to DONE stack 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/JobOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/JobOps.c	(revision 36680)
@@ -61,4 +61,16 @@
 }
 
+int GetJobStackIDbyName (char *name) {
+  if (!strcasecmp (name, "ALLJOBS"))  return PCONTROL_JOB_ALLJOBS;
+  if (!strcasecmp (name, "PENDING"))  return PCONTROL_JOB_PENDING;
+  if (!strcasecmp (name, "BUSY"))     return PCONTROL_JOB_BUSY   ;
+  if (!strcasecmp (name, "RESP"))     return PCONTROL_JOB_RESP   ;
+  if (!strcasecmp (name, "DONE"))     return PCONTROL_JOB_DONE   ;
+  if (!strcasecmp (name, "KILL"))     return PCONTROL_JOB_KILL   ;
+  if (!strcasecmp (name, "EXIT"))     return PCONTROL_JOB_EXIT   ;
+  if (!strcasecmp (name, "CRASH"))    return PCONTROL_JOB_CRASH  ;
+  return (PCONTROL_JOB_NONE);
+}
+
 Stack *GetJobStack (int StackID) {
   switch (StackID) {
@@ -195,5 +207,5 @@
 }
 
-IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts) {
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int nicelevel, int argc, char **argv, int Nxhosts, char **xhosts) {
 
   int JobID;
@@ -220,5 +232,5 @@
 
   job[0].mode     = mode;
-  job[0].priority = priority;
+  job[0].nicelevel = nicelevel;
 
   job[0].state = 0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/StartJob.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/StartJob.c	(revision 36680)
@@ -25,7 +25,7 @@
   bzero (line, Nline);
   strcpy (line, "job");
-  if (job[0].priority) {
+  if (job[0].nicelevel) {
     char tmp[64];
-    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    snprintf (tmp, 64, " -nice %d", job[0].nicelevel);
     strcat (line, tmp);
   }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/job.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/job.c	(revision 36680)
@@ -5,5 +5,5 @@
   char *Host = NULL;
   char **targv = NULL;
-  int i, N, Mode, targc, Timeout, priority;
+  int i, N, Mode, targc, Timeout, nicelevel;
   IDtype JobID;
   char **xhosts = NULL;
@@ -43,8 +43,8 @@
   }
 
-  priority = 0;
+  nicelevel = 0;
   if ((N = get_argument (argc, argv, "-nice"))) {
     remove_argument (N, &argc, argv);
-    priority = atoi (argv[N]);
+    nicelevel = atoi (argv[N]);
     remove_argument (N, &argc, argv);
   }
@@ -76,5 +76,5 @@
 
   // a JobID < 0 mean the job was not accepted
-  JobID = AddJob (Host, Mode, Timeout, priority, targc, targv, Nxhosts, xhosts);
+  JobID = AddJob (Host, Mode, Timeout, nicelevel, targc, targv, Nxhosts, xhosts);
   gprint (GP_LOG, "JobID: %d\n", (int) JobID);
   return (TRUE);
@@ -82,5 +82,5 @@
  usage:
     gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
-    gprint (GP_ERR, "  options: -host, +host, -timeout, -xhost (host)\n");
+    gprint (GP_ERR, "  options: -host, +host, -timeout, -xhost (host) -nice (level)\n");
     gprint (GP_ERR, "  arguments of the form @MAX_THREADS@ will be replaced when the job is launched\n");
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/jobstack.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/jobstack.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/jobstack.c	(revision 36680)
@@ -22,5 +22,5 @@
   /* print list */
   LockStack (stack);
-  gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
+  gprint (GP_LOG, "BEGIN BLOCK Njobs: %d\n", stack[0].Nobject);
   for (i = 0; i < stack[0].Nobject; i++) {
     job = stack[0].object[i];
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/status.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/opihi/pcontrol/status.c	(revision 36680)
@@ -1,16 +1,93 @@
 # include "pcontrol.h"
 
-int PrintJobStack (int Nstack);
+int PrintJobStack (int Nstack, char *command, char *hostname, int state, float age);
 int PrintHostStack (int Nstack);
 
 int status (int argc, char **argv) {
 
-  PrintJobStack (PCONTROL_JOB_ALLJOBS);
-  PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto usage;
+  if (get_argument (argc, argv, "-help")) goto usage;
+  if (get_argument (argc, argv, "--help")) goto usage;
+
+  /* I would like to add the following options:
+   * strsub on argv[0]
+   * strsub on hostname, realhost
+   * list hostname and realhost
+   * filter by state
+   * filter by dtime
+  */
+
+  // -cmd (cmd)
+  // -host (hostname)
+  // -state (busy, pending, done, kill, exit, crash, resp, hung
+  // -age (seconds?) (minutes?)
+  // -nohost
+
+  char *COMMAND = NULL;
+  if ((N = get_argument (argc, argv, "-cmd"))) {
+    remove_argument (N, &argc, argv);
+    COMMAND = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  char *HOSTNAME = NULL;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    HOSTNAME = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int STATE = PCONTROL_JOB_ALLJOBS;
+  if ((N = get_argument (argc, argv, "-state"))) {
+    remove_argument (N, &argc, argv);
+    STATE = GetJobStackIDbyName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (STATE == PCONTROL_JOB_NONE) goto usage;
+  }
+
+  float AGE = 0.0;
+  if ((N = get_argument (argc, argv, "-age"))) {
+    remove_argument (N, &argc, argv);
+    AGE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int SHOWHOST = TRUE;
+  if ((N = get_argument (argc, argv, "-nohost"))) {
+    remove_argument (N, &argc, argv);
+    SHOWHOST = FALSE;
+  }
+  if ((N = get_argument (argc, argv, "+jobs"))) {
+    remove_argument (N, &argc, argv);
+    SHOWHOST = FALSE;
+  }
+
+  int SHOWJOBS = TRUE;
+  if ((N = get_argument (argc, argv, "-nojobs"))) {
+    remove_argument (N, &argc, argv);
+    SHOWJOBS = FALSE;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    remove_argument (N, &argc, argv);
+    SHOWJOBS = FALSE;
+  }
+
+  if (SHOWJOBS) {
+    PrintJobStack (PCONTROL_JOB_ALLJOBS, COMMAND, HOSTNAME, STATE, AGE);
+  }
+  if (SHOWHOST) {
+    PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+  }
 
   return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: status [-cmd command] [-host hostname] [-state state] [-age seconds] [+jobs,-nohost] [+host,-nojobs]\n");
+  return FALSE;
 }
 
-int PrintJobStack (int Nstack) {
+int PrintJobStack (int Nstack, char *command, char *hostname, int state, float age) {
 
   int i, j, Nobject;
@@ -30,10 +107,6 @@
     job = stack[0].object[i];
     ASSERT (job != NULL, "programming error");
-    if (job[0].realhost == NULL) {
-	gprint (GP_LOG, "%3d %9s ", i, job[0].hostname);
-    } else {
-	gprint (GP_LOG, "%3d %9s ", i, job[0].realhost);
-    }
-    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+
+    char *thishost = (job[0].realhost == NULL) ? job[0].hostname : job[0].realhost;
 
     switch (job[0].state) {
@@ -45,5 +118,4 @@
 	gettimeofday (&now, (void *) NULL);
 	dtime = DTIME (now, job[0].start);
-	gprint (GP_LOG, "%8.2f ", dtime);
 	break;
 
@@ -55,10 +127,32 @@
       default:
 	dtime = DTIME (job[0].stop, job[0].start);
-	gprint (GP_LOG, "%8.2f ", dtime);
 	break;
     }
 
+    // check on the filters
+    if (command) {
+      if (!strstr (job[0].argv[0], command)) continue;
+    }
+    if (hostname) {
+      if (!strstr (thishost, hostname)) continue;
+    }
+    if (age > 0.0) {
+      if (dtime < age) continue;
+    }
+    if (state != PCONTROL_JOB_ALLJOBS) {
+      // allow PCONTROL_JOB_RESP == BUSY
+      int validState = FALSE;
+      validState |= (state == PCONTROL_JOB_RESP) && (job[0].state == PCONTROL_JOB_BUSY);
+      validState |= (state == PCONTROL_JOB_BUSY) && (job[0].state == PCONTROL_JOB_RESP);
+      validState |= (state == job[0].state);
+      if (!validState) continue;
+    }
+
+    gprint (GP_LOG, "%3d %9s ", i, thishost);
+    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+    gprint (GP_LOG, "%8.2f ", dtime);
+
     PrintID (GP_LOG, job[0].JobID);
-    gprint (GP_LOG, " %2d ", job[0].priority);
+    gprint (GP_LOG, " %2d ", job[0].nicelevel);
     for (j = 0; j < job[0].argc; j++) {
       gprint (GP_LOG, "%s ", job[0].argv[j]);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/find_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/find_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/find_images.c	(revision 36680)
@@ -25,5 +25,5 @@
   tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
   tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
-  strcpy (tcoords.ctype, "RA---TAN");
+  strcpy (tcoords.ctype, "DEC--TAN");
 
   timage = gfits_table_get_Image (&db[0].ftable, &Ntimage, &db[0].swapped);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/join_stars.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/join_stars.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/photdbc/src/join_stars.c	(revision 36680)
@@ -57,5 +57,5 @@
   tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 0;
-  strcpy (tcoords.ctype, "RA---TAN");
+  strcpy (tcoords.ctype, "DEC--TAN");
   
   /* project & sort coordinates in local linear frame */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/Makefile	(revision 36680)
@@ -69,4 +69,16 @@
 $(SRC)/resort_catalog.$(ARCH).o  \
 $(SRC)/BrightCatalog.$(ARCH).o  \
+$(SRC)/assign_images.$(ARCH).o \
+$(SRC)/launch_region_hosts.$(ARCH).o \
+$(SRC)/relastro_parallel_images.$(ARCH).o \
+$(SRC)/relastro_parallel_regions.$(ARCH).o \
+$(SRC)/MeanPosIO.$(ARCH).o \
+$(SRC)/share_mean_pos.$(ARCH).o \
+$(SRC)/share_images_pos.$(ARCH).o \
+$(SRC)/ImagePosIO.$(ARCH).o \
+$(SRC)/ImageTable.$(ARCH).o \
+$(SRC)/markObjects.$(ARCH).o \
+$(SRC)/indexCatalogs.$(ARCH).o \
+$(SRC)/syncfile.$(ARCH).o \
 $(SRC)/relastroVisual.$(ARCH).o
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/include/relastro.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/include/relastro.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/include/relastro.h	(revision 36680)
@@ -6,4 +6,13 @@
 # include <pthread.h>
 
+# define MARKTIME(MSG,...) {			\
+    gettimeofday (&stopTimer, (void *) NULL);	\
+    float dtime = DTIME (stopTimer, startTimer);	\
+    fprintf (stderr, MSG, __VA_ARGS__); }
+
+# define INITTIME \
+  struct timeval startTimer, stopTimer; \
+  gettimeofday (&startTimer, (void *) NULL);
+
 // choose off_t or int depending on full-scale relphot analysis resources
 // # define IDX_T off_t
@@ -20,5 +29,7 @@
 typedef enum {FIT_NONE, FIT_AVERAGE, FIT_PM_ONLY, FIT_PAR_ONLY, FIT_PM_AND_PAR} FitMode;
 
-typedef enum {TARGET_NONE, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS, TARGET_HIGH_SPEED, TARGET_MERGE_SOURCE, TARGET_UPDATE_OBJECTS, TARGET_UPDATE_OFFSETS, TARGET_LOAD_OBJECTS, TARGET_HPM} FitTarget;
+typedef enum {OP_NONE, OP_IMAGES, OP_HIGH_SPEED, OP_MERGE_SOURCE, OP_UPDATE_OBJECTS, OP_UPDATE_OFFSETS, OP_LOAD_OBJECTS, OP_HPM, OP_PARALLEL_REGIONS, OP_PARALLEL_IMAGES} RelastroOp;
+
+typedef enum {TARGET_NONE, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS} FitTarget;
 
 typedef enum {
@@ -30,4 +41,20 @@
   MARK_BIG_OFFSET    = 0x0010,
 } MeasurementMask;
+
+typedef struct {
+  float R;
+  float D;
+  unsigned int objID;
+  unsigned int catID;
+} MeanPos;
+
+typedef struct {
+  Coords coords;
+  float dXpixSys;
+  float dYpixSys;
+  unsigned int imageID;
+  int nFitAstrom;
+  int flags;
+} ImagePos;
 
 typedef struct {
@@ -93,4 +120,5 @@
   unsigned int start;
   unsigned int stop;
+  off_t myImage;
   float Mcal;
   float dMcal;
@@ -113,10 +141,4 @@
 } StatType;
 
-# define MARKTIME(MSG,...) { \
-  float dtime; \
-  gettimeofday (&stop, (void *) NULL); \
-  dtime = DTIME (stop, start); \
-  fprintf (stderr, MSG, __VA_ARGS__); }
-
 /* global variables set in parameter file */
 # define DVO_MAX_PATH 1024
@@ -129,4 +151,10 @@
 char   SKY_TABLE[DVO_MAX_PATH];
 int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+// globals for parallel region operations
+char  *REGION_FILE;
+char  *IMAGE_TABLE;
+int    REGION_HOST_ID;
+int    PARALLEL_REGIONS_MANUAL;
 
 int          HOST_ID;
@@ -208,4 +236,5 @@
 FitMode FIT_MODE;
 
+RelastroOp RELASTRO_OP;
 FitTarget FIT_TARGET;
 
@@ -450,2 +479,44 @@
 int hpm_catalogs_parallel (SkyList *skylist);
 int hpm_objects (SkyRegion *region, Catalog *catalog);
+
+int strextend (char *input, char *format,...);
+int launch_region_hosts (RegionHostTable *regionHosts);
+
+int assign_images (FITS_DB *db, RegionHostTable *regionHosts);
+int select_images_hostregion (RegionHostTable *regionHosts, Image *image, off_t Nimage);
+int calculate_image_bounds (Image *image, double *rmin, double *rmax, double *dmin, double *dmax, double Rmid);
+int calculate_host_image_bounds (RegionHostTable *regionHosts);
+int find_host_for_coords (RegionHostTable *regionHosts, double Rc, double Dc);
+
+int relastro_parallel_regions ();
+int relastro_parallel_images ();
+
+char *make_filename (char *dirname, char *hostname, int hostID, char *tailname);
+int check_sync_file (char *filename, int nloop);
+int clear_sync_file (char *filename);
+int update_sync_file (char *filename, int nloop);
+
+int share_mean_pos (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop);
+int slurp_mean_pos (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop);
+int set_mean_pos (MeanPos *meanpos, Average *average);
+MeanPos *merge_mean_pos (MeanPos *target, int *ntarget, MeanPos *source, int Nsource);
+
+int MeanPosSave(char *filename, MeanPos *meanpos, off_t Nmeanpos);
+MeanPos *MeanPosLoad(char *filename, off_t *nmeanpos);
+
+int indexCatalogs (Catalog *catalog, int Ncatalog);
+int catID_and_objID_to_seq (int catID, int objID, int *catSeq, off_t *objSeq);
+
+int markObjects (Catalog *catalog, int Ncatalog);
+
+int ImagePosSave(char *filename, ImagePos *image_pos, off_t Nimage_pos);
+ImagePos *ImagePosLoad(char *filename, off_t *nimage_pos);
+
+int share_image_pos (RegionHostTable *regionHosts, int nloop);
+int slurp_image_pos (RegionHostTable *regionHosts, int nloop);
+ImagePos *merge_image_pos (ImagePos *target, int *ntarget, ImagePos *source, int Nsource);
+int set_image_pos (ImagePos *image_pos, Image *image);
+
+Image *ImageTableLoad(char *filename, off_t *nimage);
+int ImageTableSave (char *filename, Image *images, off_t Nimages);
+int select_mosaics_hostregion (RegionHostTable *regionHosts, Image *image, off_t Nimage);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ConfigInit.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ConfigInit.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ConfigInit.c	(revision 36680)
@@ -18,6 +18,7 @@
   if (VERBOSE) fprintf (stderr, "loaded config file: %s\n", file);
 
-  GetConfig (config, "RELASTRO_SIGMA_LIM",         "%lf", 0, &SIGMA_LIM); // exclude measurements on this basis
-  GetConfig (config, "RELASTRO_SRC_MEAS_TOOFEW",   "%d",  0, &SRC_MEAS_TOOFEW);
+  // set defaults for all of these if they are not used by parallel / remote clients
+  if (!ScanConfig (config, "RELASTRO_SIGMA_LIM",         "%lf", 0, &SIGMA_LIM))       SIGMA_LIM = 0.01; 
+  if (!ScanConfig (config, "RELASTRO_SRC_MEAS_TOOFEW",   "%d",  0, &SRC_MEAS_TOOFEW)) SRC_MEAS_TOOFEW = 3;
 
   if (!ScanConfig (config, "RELASTRO_IMFIT_CLIP_NITER",    "%d",  0, &IMFIT_CLIP_NITER))    IMFIT_CLIP_NITER  = 3;
@@ -25,18 +26,14 @@
   if (!ScanConfig (config, "RELASTRO_IMFIT_SYS_SIGMA_LIM", "%lf", 0, &IMFIT_SYS_SIGMA_LIM)) IMFIT_SYS_SIGMA_LIM = 0.01;
 
-  GetConfig (config, "PM_DT_MIN",              "%lf", 0, &PM_DT_MIN);
-  GetConfig (config, "PM_TOOFEW",              "%d",  0, &PM_TOOFEW);
-  GetConfig (config, "POS_TOOFEW",             "%d",  0, &POS_TOOFEW);
+  if (!ScanConfig (config, "PM_DT_MIN",              "%lf", 0, &PM_DT_MIN))        PM_DT_MIN = 0.25;   
+  if (!ScanConfig (config, "PM_TOOFEW",              "%d",  0, &PM_TOOFEW))	   PM_TOOFEW = 4;   
+  if (!ScanConfig (config, "POS_TOOFEW",             "%d",  0, &POS_TOOFEW))	   POS_TOOFEW = 1;  
+  if (!ScanConfig (config, "PAR_FACTOR_MIN",         "%lf", 0, &PAR_FACTOR_MIN))   PAR_FACTOR_MIN = 0.2;
+  if (!ScanConfig (config, "RELASTRO_MAP_NX",        "%d",  0, &NX_MAP))	   NX_MAP = 5;
+  if (!ScanConfig (config, "RELASTRO_MAP_NY",        "%d",  0, &NY_MAP))	   NY_MAP = 5;
+  if (!ScanConfig (config, "RELASTRO_DPOS_MAX",      "%lf", 0, &DPOS_MAX))	   DPOS_MAX = 6.0;    
+  if (!ScanConfig (config, "ADDSTAR_RADIUS",         "%lf", 0, &ADDSTAR_RADIUS))   ADDSTAR_RADIUS = 1.0;
 
-  GetConfig (config, "PAR_FACTOR_MIN",         "%lf", 0, &PAR_FACTOR_MIN);
-
-  GetConfig (config, "RELASTRO_MAP_NX",        "%d",  0, &NX_MAP);
-  GetConfig (config, "RELASTRO_MAP_NY",        "%d",  0, &NY_MAP);
-  GetConfig (config, "RELASTRO_DPOS_MAX",      "%lf", 0, &DPOS_MAX);
-  GetConfig (config, "ADDSTAR_RADIUS",         "%lf", 0, &ADDSTAR_RADIUS);
-
-  if (!ScanConfig (config, "USE_FIXED_PIXCOORDS", "%d", 0, &USE_FIXED_PIXCOORDS)) {
-    USE_FIXED_PIXCOORDS = FALSE;
-  }
+  if (!ScanConfig (config, "USE_FIXED_PIXCOORDS", "%d", 0, &USE_FIXED_PIXCOORDS))  USE_FIXED_PIXCOORDS = FALSE;
 
   // force CATDIR to be absolute (so parallel mode will work)
@@ -56,10 +53,6 @@
   sprintf (ImageCat, "%s/Images.dat", CATDIR);
 
-  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) {
-    SKY_DEPTH = 2;
-  }
-  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) {
-    SKY_TABLE[0] = 0;
-  }
+  if (!ScanConfig (config, "SKY_DEPTH",         "%d",  0, &SKY_DEPTH)) SKY_DEPTH = 2;
+  if (!ScanConfig (config, "SKY_TABLE",         "%s",  0, SKY_TABLE)) SKY_TABLE[0] = 0;
 
   if (*CATMODE == 0) strcpy (CATMODE, "RAW");
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageOps.c	(revision 36680)
@@ -226,6 +226,6 @@
     Measure *measureBig = &catalog[cat].measure[meas];
     int TESTPT = FALSE;
-    TESTPT |= (measureBig->imageID == CAT_ID_SRC) && (measureBig->detID == OBJ_ID_SRC);
-    TESTPT |= (measureBig->imageID == CAT_ID_DST) && (measureBig->detID == OBJ_ID_DST);
+    TESTPT |= CAT_ID_SRC && OBJ_ID_SRC && (measureBig->imageID == CAT_ID_SRC) && (measureBig->detID == OBJ_ID_SRC);
+    TESTPT |= CAT_ID_DST && OBJ_ID_DST && (measureBig->imageID == CAT_ID_DST) && (measureBig->detID == OBJ_ID_DST);
     if (TESTPT) {
       fprintf (stderr, "got test det\n");
@@ -462,6 +462,6 @@
     int TESTPT = FALSE;
 
-    TESTPT |= (measure->imageID == CAT_ID_SRC) && (measure->detID == OBJ_ID_SRC);
-    TESTPT |= (measure->imageID == CAT_ID_DST) && (measure->detID == OBJ_ID_DST);
+    TESTPT |= CAT_ID_SRC && OBJ_ID_SRC && (measure->imageID == CAT_ID_SRC) && (measure->detID == OBJ_ID_SRC);
+    TESTPT |= CAT_ID_DST && OBJ_ID_DST && (measure->imageID == CAT_ID_DST) && (measure->detID == OBJ_ID_DST);
     if (TESTPT) {
       fprintf (stderr, "got test det\n");
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImagePosIO.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImagePosIO.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImagePosIO.c	(revision 36680)
@@ -0,0 +1,315 @@
+# include "relastro.h"
+
+# define GET_COLUMN(OUT,NAME,TYPE) \
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+// this is nearly identical to the one in 'uniphot/src' used by setphot / setphot_client.
+// Here, we use a handful of different columns (if not, we could move to libdvo)
+ImagePos *ImagePosLoad(char *filename, off_t *nimage_pos) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  *nimage_pos = 0;
+  ImagePos *image_pos = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return NULL;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) {
+    fclose (f);
+    return (NULL);
+  }
+  fclose (f);
+
+  // we read the entire block of data, then extract the columns, then set the image structure values.
+  // I free the FITS table data after extracting the colums to avoid having 3 copies in memory.
+
+  char type[16];
+
+  GET_COLUMN (crval1,           "CRVAL1",            double);
+  GET_COLUMN (crval2,           "CRVAL2",            double);
+  GET_COLUMN (crpix1,           "CRPIX1",            float);
+  GET_COLUMN (crpix2,           "CRPIX2",            float);
+  GET_COLUMN (cdelt1,           "CDELT1",            float);
+  GET_COLUMN (cdelt2,           "CDELT2",            float);
+  GET_COLUMN (pc1_1,            "PC1_1",             float);
+  GET_COLUMN (pc1_2,            "PC1_2",             float);
+  GET_COLUMN (pc2_1,            "PC2_1",             float);
+  GET_COLUMN (pc2_2,            "PC2_2",             float);
+  GET_COLUMN (polyterms,        "POLYTERMS",         float); // verify that we got 14 columns?
+  GET_COLUMN (ctype,            "CTYPE",             char);  // verify that we got 15 columns?
+  GET_COLUMN (Npolyterms,       "NPOLYTERMS",        char);
+  GET_COLUMN (dXpixSys,         "XPIX_SYS_ERR",      float);
+  GET_COLUMN (dYpixSys,         "YPIX_SYS_ERR",      float);
+  GET_COLUMN (imageID,          "ID",                int);
+  GET_COLUMN (nFitAstrom,       "NFIT",              int);
+  GET_COLUMN (flags,            "FLAGS",             int);
+
+  // free the memory associated with the FITS files
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table  (&ftable);
+
+  ALLOCATE (image_pos, ImagePos, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    image_pos[i].coords.crval1             = crval1    [i];
+    image_pos[i].coords.crval2             = crval2    [i];
+    image_pos[i].coords.crpix1             = crpix1    [i];
+    image_pos[i].coords.crpix2             = crpix2    [i];
+    image_pos[i].coords.cdelt1             = cdelt1    [i];
+    image_pos[i].coords.cdelt2             = cdelt2    [i];
+    image_pos[i].coords.pc1_1              = pc1_1     [i];
+    image_pos[i].coords.pc1_2              = pc1_2     [i];
+    image_pos[i].coords.pc2_1              = pc2_1     [i];
+    image_pos[i].coords.pc2_2              = pc2_2     [i];
+    image_pos[i].coords.Npolyterms         = Npolyterms[i];
+    image_pos[i].dXpixSys                  = dXpixSys  [i];
+    image_pos[i].dYpixSys                  = dYpixSys  [i];
+    image_pos[i].imageID                   = imageID   [i];
+    image_pos[i].nFitAstrom                = nFitAstrom[i];
+    image_pos[i].flags                     = flags     [i];
+
+    // polyterms and ctype are a bit different
+    memcpy (&image_pos[i].coords.polyterms, &polyterms[i*14], 14*sizeof(float));
+    memcpy (&image_pos[i].coords.ctype    , &ctype    [i*15], 15*sizeof(char));
+  }
+  fprintf (stderr, "loaded data for %lld images\n", (long long) Nrow);
+
+  free (crval1    );
+  free (crval2    );
+  free (crpix1    );
+  free (crpix2    );
+  free (cdelt1    );
+  free (cdelt2    );
+  free (pc1_1     );
+  free (pc1_2     );
+  free (pc2_1     );
+  free (pc2_2     );
+  free (polyterms );
+  free (ctype     );
+  free (Npolyterms);
+  free (dXpixSys  );
+  free (dYpixSys  );
+  free (imageID   );
+  free (nFitAstrom);
+  free (flags     );
+
+  *nimage_pos = Nrow;
+  return image_pos;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)                                   \
+  if (!(STATUS)) {                                                      \
+    fprintf (stderr, MSG, __VA_ARGS__);                                 \
+    return FALSE;                                                       \
+  }
+
+int ImagePosSave(char *filename, ImagePos *image_pos, off_t Nimage_pos) {
+
+  int i;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_create_table_header (&theader, "BINTABLE", "IMAGE_POS");
+
+  gfits_define_bintable_column (&theader, "D",   "CRVAL1",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D",   "CRVAL2",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "CRPIX1",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "CRPIX2",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "CDELT1",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "CDELT2",       "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "PC1_1",        "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "PC1_2",        "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "PC2_1",        "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "PC2_2",        "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "14E", "POLYTERMS",    "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "15A", "CTYPE",        "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "L",   "NPOLYTERMS",   "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "XPIX_SYS_ERR", "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E",   "YPIX_SYS_ERR", "word", "unit", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J",   "ID",           "image ID",               "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J",   "NFIT",         "number of fitted stars", "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J",   "FLAGS",        "analysis flags",         "unitless", 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  double *crval1    ;
+  double *crval2    ;
+  float  *crpix1    ;
+  float  *crpix2    ;
+  float  *cdelt1    ;
+  float  *cdelt2    ;
+  float  *pc1_1     ;
+  float  *pc1_2     ;
+  float  *pc2_1     ;
+  float  *pc2_2     ;
+  float  *polyterms ;
+  char   *ctype     ;
+  char   *Npolyterms;
+  float  *dXpixSys  ;
+  float  *dYpixSys  ;
+  int    *imageID   ;
+  int    *nFitAstrom;
+  int    *flags     ;
+
+  // create intermediate storage arrays
+  ALLOCATE (crval1    ,         double,          Nimage_pos);
+  ALLOCATE (crval2    ,         double,          Nimage_pos);
+  ALLOCATE (crpix1    ,         float ,          Nimage_pos);
+  ALLOCATE (crpix2    ,         float ,          Nimage_pos);
+  ALLOCATE (cdelt1    ,         float ,          Nimage_pos);
+  ALLOCATE (cdelt2    ,         float ,          Nimage_pos);
+  ALLOCATE (pc1_1     ,         float ,          Nimage_pos);
+  ALLOCATE (pc1_2     ,         float ,          Nimage_pos);
+  ALLOCATE (pc2_1     ,         float ,          Nimage_pos);
+  ALLOCATE (pc2_2     ,         float ,          Nimage_pos);
+  ALLOCATE (polyterms ,         float ,          14*Nimage_pos);
+  ALLOCATE (ctype     ,         char  ,          15*Nimage_pos);
+  ALLOCATE (Npolyterms,         char  ,          Nimage_pos);
+  ALLOCATE (dXpixSys  ,         float ,          Nimage_pos);
+  ALLOCATE (dYpixSys  ,         float ,          Nimage_pos);
+  ALLOCATE (imageID   ,         int   ,          Nimage_pos);
+  ALLOCATE (nFitAstrom,         int   ,          Nimage_pos);
+  ALLOCATE (flags     ,         int   ,          Nimage_pos);
+
+  // assign the storage arrays
+  for (i = 0; i < Nimage_pos; i++) {
+    crval1    [i] = image_pos[i].coords.crval1    ;
+    crval2    [i] = image_pos[i].coords.crval2    ;
+    crpix1    [i] = image_pos[i].coords.crpix1    ;
+    crpix2    [i] = image_pos[i].coords.crpix2    ;
+    cdelt1    [i] = image_pos[i].coords.cdelt1    ;
+    cdelt2    [i] = image_pos[i].coords.cdelt2    ;
+    pc1_1     [i] = image_pos[i].coords.pc1_1     ;
+    pc1_2     [i] = image_pos[i].coords.pc1_2     ;
+    pc2_1     [i] = image_pos[i].coords.pc2_1     ;
+    pc2_2     [i] = image_pos[i].coords.pc2_2     ;
+    Npolyterms[i] = image_pos[i].coords.Npolyterms;
+    dXpixSys  [i] = image_pos[i].dXpixSys         ;
+    dYpixSys  [i] = image_pos[i].dYpixSys         ;
+    imageID   [i] = image_pos[i].imageID          ;
+    nFitAstrom[i] = image_pos[i].nFitAstrom       ;
+    flags     [i] = image_pos[i].flags            ;
+
+    // polyterms and ctype are a bit different
+    memcpy (&polyterms[i*14], &image_pos[i].coords.polyterms, 14*sizeof(float));
+    memcpy (&ctype    [i*15], &image_pos[i].coords.ctype    , 15*sizeof(char));
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "CRVAL1",      crval1    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CRVAL2",      crval2    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CRPIX1",      crpix1    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CRPIX2",      crpix2    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CDELT1",      cdelt1    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CDELT2",      cdelt2    ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "PC1_1",       pc1_1     ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "PC1_2",       pc1_2     ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "PC2_1",       pc2_1     ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "PC2_2",       pc2_2     ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "POLYTERMS",   polyterms ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "CTYPE",       ctype     ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "NPOLYTERMS",  Npolyterms,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "XPIX_SYS_ERR",dXpixSys  ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "YPIX_SYS_ERR",dYpixSys  ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "ID",          imageID   ,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "NFIT",        nFitAstrom,         Nimage_pos);
+  gfits_set_bintable_column (&theader, &ftable, "FLAGS",       flags     ,         Nimage_pos);
+
+  free (crval1    );
+  free (crval2    );
+  free (crpix1    );
+  free (crpix2    );
+  free (cdelt1    );
+  free (cdelt2    );
+  free (pc1_1     );
+  free (pc1_2     );
+  free (pc2_1     );
+  free (pc2_2     );
+  free (polyterms );
+  free (ctype     );
+  free (Npolyterms);
+  free (dXpixSys  );
+  free (dYpixSys  );
+  free (imageID   );
+  free (nFitAstrom);
+  free (flags     );
+  
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image_pos file for output %s\n", filename);
+    return FALSE;
+  }
+
+  int status;
+  status = gfits_fwrite_header  (f, &header);
+  CHECK_STATUS (status, "ERROR: cannot write header for image_pos %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for image_pos %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for image_pos %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for image_pos %s\n", filename);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  int fd = fileno (f);
+
+  status = fflush (f);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image_pos %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image_pos %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing image_pos file %s\n", filename);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageTable.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageTable.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/ImageTable.c	(revision 36680)
@@ -0,0 +1,62 @@
+# include "relastro.h"
+
+Image *ImageTableLoad(char *filename, off_t *nimage) {
+
+  int status;
+  off_t Nimage;
+  FITS_DB db;
+
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, filename, 60.0, LCK_SOFT);
+  if (!status) {
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+
+  // if the file is missing, db.dbstate will have a value of either:
+  // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    Shutdown ("ERROR: database %s contains no image data", CATDIR);
+  }
+
+  // read data from Image.dat file
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+
+  // convert database table to internal structure (binary to Image)
+  // 'image' points to the same memory as db->ftable->buffer
+  Image *image = gfits_table_get_Image (&db.ftable, &Nimage, &db.swapped);
+  if (!image) {
+    Shutdown ("ERROR: failed to read images");
+  }
+
+  *nimage = Nimage;
+  return image;
+}
+
+int ImageTableSave (char *filename, Image *images, off_t Nimages) {
+
+  int status;
+  FITS_DB db;
+
+  // setup image table format and lock 
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, filename, 60.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  // load or create the image table 
+  if (db.dbstate != LCK_EMPTY) {
+    Shutdown ("image catalog already exists %s", db.filename);
+  }
+
+  dvo_image_create (&db, GetZeroPoint());
+
+  /* add the new images and save */
+  dvo_image_addrows (&db, images, Nimages);
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MeanPosIO.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MeanPosIO.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MeanPosIO.c	(revision 36680)
@@ -0,0 +1,183 @@
+# include "relastro.h"
+
+# define GET_COLUMN(OUT,NAME,TYPE) \
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+// this is nearly identical to the one in 'uniphot/src' used by setphot / setphot_client.
+// Here, we use a handful of different columns (if not, we could move to libdvo)
+MeanPos *MeanPosLoad(char *filename, off_t *nmeanpos) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  *nmeanpos = 0;
+  MeanPos *meanpos = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return NULL;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) {
+    fclose (f);
+    return (NULL);
+  }
+  fclose (f);
+
+  // a bit annoying : we read the entire block of data, then extract the columns, then set the image structure values.
+  // this means I need 3 copies in memory at some point.  ugh.
+
+  char type[16];
+
+  GET_COLUMN (R,    	 "RA",           double);
+  GET_COLUMN (D,   	 "DEC",          double);
+  GET_COLUMN (objID, 	 "OBJ_ID",       int);
+  GET_COLUMN (catID, 	 "CAT_ID",       int);
+
+  ALLOCATE (meanpos, MeanPos, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    meanpos[i].R              = R    [i];
+    meanpos[i].D              = D    [i];
+    meanpos[i].objID          = objID[i];
+    meanpos[i].catID          = catID[i];
+  }
+  fprintf (stderr, "loaded data for %lld objects (* filters)\n", (long long) Nrow);
+
+  free (R    );
+  free (D    );
+  free (objID);
+  free (catID);
+
+  // free FITS table pieces...
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table  (&ftable);
+
+  *nmeanpos = Nrow;
+  return meanpos;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)					\
+  if (!(STATUS)) {							\
+    fprintf (stderr, MSG, __VA_ARGS__);					\
+    return FALSE;							\
+  }
+
+int MeanPosSave(char *filename, MeanPos *meanpos, off_t Nmeanpos) {
+
+  int i;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_create_table_header (&theader, "BINTABLE", "MEANPOS");
+
+  gfits_define_bintable_column (&theader, "D", "RA",        "mean position, ra",  "degrees", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "DEC",       "mean position, dec", "degrees", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "OBJ_ID",    "object ID",          NULL,      1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "J", "CAT_ID",    "catalog ID",         NULL,      1.0, 1.0*0x8000);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  double *R, *D;
+  unsigned int *objID, *catID;
+
+  // create intermediate storage arrays
+  ALLOCATE (R,         double, 	       Nmeanpos);
+  ALLOCATE (D,         double, 	       Nmeanpos);
+  ALLOCATE (objID,     unsigned int,   Nmeanpos);
+  ALLOCATE (catID,     unsigned int,   Nmeanpos);
+
+  // assign the storage arrays
+  for (i = 0; i < Nmeanpos; i++) {
+    R    [i]   = meanpos[i].R    ;
+    D    [i]   = meanpos[i].D    ;
+    objID[i]   = meanpos[i].objID;
+    catID[i]   = meanpos[i].catID;
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "RA",        R,         Nmeanpos);
+  gfits_set_bintable_column (&theader, &ftable, "DEC",       D,         Nmeanpos);
+  gfits_set_bintable_column (&theader, &ftable, "OBJ_ID",    objID,     Nmeanpos);
+  gfits_set_bintable_column (&theader, &ftable, "CAT_ID",    catID,     Nmeanpos);
+
+  free (R    );
+  free (D    );
+  free (objID);
+  free (catID);
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open meanmag file for output %s\n", filename);
+    return FALSE;
+  }
+
+  int status;
+  status = gfits_fwrite_header  (f, &header);
+  CHECK_STATUS (status, "ERROR: cannot write header for meanpos %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for meanpos %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for meanpos %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for meanpos %s\n", filename);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  int fd = fileno (f);
+
+  status = fflush (f);
+  CHECK_STATUS (!status, "ERROR: cannot flush file meanpos %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot flush file meanpos %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing meanpos file %s\n", filename);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MosaicOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MosaicOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/MosaicOps.c	(revision 36680)
@@ -11,5 +11,5 @@
 
 // list of mosaic associated with each image  
-static off_t    Nmosaic_for_images; // number of images (for off_ternal checks)
+static off_t    Nmosaic_for_images; // number of images (for internal checks)
 static off_t    *mosaic_for_images; // array of: image -> mosaic
 
@@ -105,4 +105,5 @@
     mosaic[Nmosaic].secz  = image[i].secz;
     mosaic[Nmosaic].coords = image[i].coords;
+    mosaic[Nmosaic].myImage = i;
 
     // init the mosaic_own_images array data
@@ -135,4 +136,8 @@
   ALLOCATE (mosaic_for_images, off_t, Nmosaic_for_images);
 
+  // emit an error if we miss mosaics, but stop if we miss too many
+  int NmissMosaic = 0;
+  int NtestMosaic = 0;
+
   /* now assign the WRP images to these mosaics */
   for (i = 0; i < Nimage; i++) {
@@ -140,4 +145,5 @@
 
     if (strcmp(&image[i].coords.ctype[4], "-WRP")) continue;
+    NtestMosaic ++;
 
     /* set image time range */
@@ -147,5 +153,8 @@
     Nmos = getMosaicByTimes (start, stop, startMos, stopMos, indexMos);
     if (Nmos == -1) {
-      fprintf (stderr, "cannot match mosaic for %s\n", image[i].name);
+      if (NmissMosaic < 1000) {
+	fprintf (stderr, "cannot match mosaic for %s\n", image[i].name);
+      }
+      NmissMosaic ++;
       continue;
     }
@@ -164,4 +173,10 @@
   }
 
+  fprintf (stderr, "mosaic matching : %d of possible %d failed to match\n", NmissMosaic, NtestMosaic);
+  if (NmissMosaic > 0.5*NtestMosaic) {
+    fprintf (stderr, "serious problem with mosaic matching\n");
+    exit (5);
+  }
+
   free (startMos);
   free (stopMos);
@@ -250,5 +265,5 @@
   if (im >= Nmosaic_for_images) abort();
 
-  // search for the mosaic that 
+  // search for the mosaic that matches this image
   mos = mosaic_for_images[im];
   if (mos < 0) return NULL;
@@ -256,2 +271,60 @@
   return &mosaic[mos];
 }
+
+// extend each host image table to include the mosaic 'images' needed by the host
+int select_mosaics_hostregion (RegionHostTable *regionHosts, Image *image, off_t Nimage) {
+
+  int i;
+  off_t j;
+  char *mosaicUsed;
+
+  ALLOCATE (mosaicUsed, char, Nmosaic);
+
+  // we need to add the mosaics to each of the region hosts lists of images
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    int Nadd = 0;
+    int NADD = 100;
+    off_t *addMosaic = NULL;
+    ALLOCATE (addMosaic, off_t, NADD);
+
+    // reset the mosaicUsed flags (valid only for this host)
+    memset (mosaicUsed, 0, Nmosaic * sizeof(char));
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // find the mosaics associated with a given 
+    for (j = 0; j < host->Nimage; j++) {
+
+      int im = host->imseq[j];
+      
+      if (im < 0) abort();
+      if (im >= Nmosaic_for_images) abort();
+
+      // search for the mosaic that matches this image (skip unmatched images)
+      off_t mos = mosaic_for_images[im];
+      if (mos < 0) continue; 
+
+      if (mosaicUsed[mos]) continue;
+
+      mosaicUsed[mos] = TRUE;
+      addMosaic[Nadd] = mos;
+      Nadd ++;
+      
+      CHECK_REALLOCATE (addMosaic, off_t, NADD, Nadd, 100);
+    }
+
+    REALLOCATE (host->image, Image, host->Nimage + Nadd);
+
+    for (j = 0; j < Nadd; j++) {
+      off_t mos = addMosaic[j];
+      off_t mos_im = mosaic[mos].myImage;
+
+      host->image[host->Nimage + j] = image[mos_im];
+    }
+    
+    host->Nimage += Nadd;
+  }
+  return TRUE;
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/StarMaps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/StarMaps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/StarMaps.c	(revision 36680)
@@ -59,7 +59,6 @@
   off_t i, N, Nimages;
   int xbin, ybin;
-  struct timeval start, stop;
 
-  gettimeofday (&start, (void *) NULL);
+  INITTIME;
 
   // Images *images = getimages(&Nimages, NULL); return value ignored
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/UpdateObjects.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/UpdateObjects.c	(revision 36680)
@@ -1,5 +1,5 @@
 # include "relastro.h"
 
-static off_t   Nmax;
+static off_t Nmax;
 static double *X, *dX;
 static double *Y, *dY;
@@ -36,4 +36,22 @@
   ALLOCATE (pX, double, MAX (1, Nmax));
   ALLOCATE (pY, double, MAX (1, Nmax));
+}  
+
+void freeObjectData () {
+
+  free (R);
+  free (D);
+  free (T);
+  free (X);
+  free (Y);
+
+  free (dR);
+  free (dD);
+  free (dT);
+  free (dX);
+  free (dY);
+
+  free (pX);
+  free (pY);
 }  
 
@@ -67,5 +85,5 @@
   coords.pc1_2  = coords.pc2_1 = 0.0;
   coords.Npolyterms = 1;
-  strcpy (coords.ctype, "RA---SIN");
+  strcpy (coords.ctype, "DEC--SIN");
 
   XVERB = FALSE;
@@ -205,5 +223,5 @@
       if (((mode == FIT_PM_ONLY) || (mode == FIT_PM_AND_PAR)) && (N <= PM_TOOFEW)) mode = FIT_AVERAGE;
 
-      if (FIT_TARGET == TARGET_HIGH_SPEED) {
+      if (RELASTRO_OP == OP_HIGH_SPEED) {
 	  Tmean = 0.5*(Tmax - Tmin);
       } else {
@@ -412,4 +430,6 @@
   }
 
+  freeObjectData ();
+
   if (VERBOSE) fprintf (stderr, "fitted "OFF_T_FMT" objects ("OFF_T_FMT" ave, "OFF_T_FMT" pm, "OFF_T_FMT" par), skipped "OFF_T_FMT", "OFF_T_FMT" have too large an offset\n",  (NaveSum + NpmSum + NparSum),  NaveSum,  NpmSum,  NparSum,  NskipSum, NoffSum);
   return (TRUE);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/args.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/args.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/args.c	(revision 36680)
@@ -13,4 +13,5 @@
   /* possible operations */
   FIT_TARGET = TARGET_NONE;
+  RELASTRO_OP = OP_NONE;
   FIT_MODE = FIT_AVERAGE;
 
@@ -21,5 +22,5 @@
     if (N > argc - 6) usage_merge_source();
     if (strcmp(argv[N+3], "into")) usage_merge_source();
-    FIT_TARGET = TARGET_MERGE_SOURCE;
+    RELASTRO_OP = OP_MERGE_SOURCE;
     remove_argument (N, &argc, argv);
     OBJ_ID_SRC = strtol(argv[N], &endptr, 0);
@@ -43,10 +44,51 @@
   if ((N = get_argument (argc, argv, "-update-objects"))) {
     remove_argument (N, &argc, argv);
-    FIT_TARGET = TARGET_UPDATE_OBJECTS;
+    RELASTRO_OP = OP_UPDATE_OBJECTS;
   }
 
   if ((N = get_argument (argc, argv, "-update-offsets"))) {
     remove_argument (N, &argc, argv);
-    FIT_TARGET = TARGET_UPDATE_OFFSETS;
+    RELASTRO_OP = OP_UPDATE_OFFSETS;
+  }
+
+  // elements needed for parallel regions / parallel images
+  REGION_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-region-hosts"))) {
+    remove_argument (N, &argc, argv);
+    REGION_FILE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  REGION_HOST_ID = 0;
+  if ((N = get_argument (argc, argv, "-region-hostID"))) {
+    remove_argument (N, &argc, argv);
+    REGION_HOST_ID = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  IMAGE_TABLE = NULL;
+  if ((N = get_argument (argc, argv, "-parallel-images"))) {
+    remove_argument (N, &argc, argv);
+    RELASTRO_OP = OP_PARALLEL_IMAGES;
+    if (N >= argc) usage();
+    IMAGE_TABLE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!REGION_FILE) usage();
+  }
+
+  if ((N = get_argument (argc, argv, "-images"))) {
+    remove_argument (N, &argc, argv);
+    RELASTRO_OP = OP_IMAGES;
+  }
+
+  PARALLEL_REGIONS_MANUAL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel-regions"))) {
+    remove_argument (N, &argc, argv);
+    RELASTRO_OP = OP_PARALLEL_REGIONS;
+    if (!REGION_FILE) usage();
+    if ((N = get_argument (argc, argv, "-parallel-regions-manual"))) {
+      remove_argument (N, &argc, argv);
+      PARALLEL_REGIONS_MANUAL = TRUE;
+    }
   }
 
@@ -90,5 +132,5 @@
     // XXX include a parallax / no-parallax option
     if (N >= argc - 4) usage();
-    FIT_TARGET = TARGET_HIGH_SPEED;
+    RELASTRO_OP = OP_HIGH_SPEED;
     remove_argument (N, &argc, argv);
     PHOTCODE_A_LIST = strcreate(argv[N]);
@@ -104,5 +146,5 @@
   if ((N = get_argument (argc, argv, "-hpm"))) {
     if (N >= argc - 2) usage();
-    FIT_TARGET = TARGET_HPM;
+    RELASTRO_OP = OP_HPM;
     remove_argument (N, &argc, argv);
     RADIUS = atof(argv[N]);
@@ -116,5 +158,5 @@
     remove_argument (N, &argc, argv);
     PARALLEL_OUTPUT = TRUE;
-    if ((FIT_TARGET != TARGET_HIGH_SPEED) && (FIT_TARGET != TARGET_HPM)) {
+    if ((RELASTRO_OP != OP_HIGH_SPEED) && (RELASTRO_OP != OP_HPM)) {
       fprintf (stderr, "-parallel-output only valid for -high-speed or -hpm modes\n");
       exit (1);
@@ -143,5 +185,7 @@
   }
 
-  if (FIT_TARGET == TARGET_NONE) usage();
+  if (RELASTRO_OP == OP_NONE) usage();
+
+  if (((RELASTRO_OP == OP_IMAGES) || (RELASTRO_OP == OP_PARALLEL_REGIONS) || (RELASTRO_OP == OP_PARALLEL_IMAGES)) && (FIT_TARGET == TARGET_NONE)) usage();
 
   /* specify portion of the sky : allow default of all sky? */
@@ -427,5 +471,5 @@
 
   /* possible operations */
-  FIT_TARGET = TARGET_NONE;
+  RELASTRO_OP = TARGET_NONE;
   FIT_MODE = FIT_AVERAGE;
 
@@ -437,4 +481,9 @@
   BCATALOG = NULL;
 
+  REGION_FILE = NULL;
+  REGION_HOST_ID = 0;
+  IMAGE_TABLE = NULL;
+  PARALLEL_REGIONS_MANUAL = FALSE;
+
   HOST_ID = 0;
   if ((N = get_argument (argc, argv, "-hostID"))) {
@@ -457,15 +506,15 @@
     BCATALOG = strcreate(argv[N]);
     remove_argument (N, &argc, argv);
-    FIT_TARGET = TARGET_LOAD_OBJECTS;
+    RELASTRO_OP = OP_LOAD_OBJECTS;
   }
 
   if ((N = get_argument (argc, argv, "-update-objects"))) {
     remove_argument (N, &argc, argv);
-    FIT_TARGET = TARGET_UPDATE_OBJECTS;
+    RELASTRO_OP = OP_UPDATE_OBJECTS;
   }
 
   if ((N = get_argument (argc, argv, "-update-offsets"))) {
     remove_argument (N, &argc, argv);
-    FIT_TARGET = TARGET_UPDATE_OFFSETS;
+    RELASTRO_OP = OP_UPDATE_OFFSETS;
   }
 
@@ -487,5 +536,5 @@
     // XXX include a parallax / no-parallax option
     if (N >= argc - 5) usage_client();
-    FIT_TARGET = TARGET_HIGH_SPEED;
+    RELASTRO_OP = OP_HIGH_SPEED;
     remove_argument (N, &argc, argv);
     PHOTCODE_A_LIST = strcreate(argv[N]);
@@ -501,5 +550,5 @@
   if ((N = get_argument (argc, argv, "-hpm"))) {
     if (N >= argc - 3) usage();
-    FIT_TARGET = TARGET_HPM;
+    RELASTRO_OP = OP_HPM;
     remove_argument (N, &argc, argv);
     RADIUS = atof(argv[N]);
@@ -539,5 +588,5 @@
   }
 
-  if (FIT_TARGET == TARGET_NONE) usage_client();
+  if (RELASTRO_OP == OP_NONE) usage_client();
 
   /* specify portion of the sky : allow default of all sky? */
@@ -718,7 +767,13 @@
 
 void usage () {
-  fprintf (stderr, "ERROR: USAGE: relastro -update-simple [options]\n");
-  fprintf (stderr, "       OR:    relastro -update-chips [options]\n");
-  fprintf (stderr, "       OR:    relastro -update-mosaic [options]\n");
+  fprintf (stderr, "ERROR: USAGE: relastro -images -update-simple [options]\n");
+  fprintf (stderr, "       OR:    relastro -images -update-chips [options]\n");
+  fprintf (stderr, "       OR:    relastro -images -update-mosaic [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-regions -update-simple [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-regions -update-chips [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-regions -update-mosaic [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-images -update-simple [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-images -update-chips [options]\n");
+  fprintf (stderr, "       OR:    relastro -parallel-images -update-mosaic [options]\n");
   fprintf (stderr, "       OR:    relastro -update-objects [options]\n");
   fprintf (stderr, "       OR:    relastro -high-speed [options]\n");
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/assign_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/assign_images.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/assign_images.c	(revision 36680)
@@ -0,0 +1,280 @@
+# include "relastro.h"
+
+// This function generates a subset of the images based on selections.  Input db has already
+// been loaded with the raw fits table data
+int assign_images (FITS_DB *db, RegionHostTable *regionHosts) {
+
+  off_t Nimage;
+
+  INITTIME;
+
+  // convert database table to internal structure (binary to Image)
+  // 'image' points to the same memory as db->ftable->buffer
+  Image *image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+  if (!image) {
+      fprintf (stderr, "ERROR: failed to read images\n");
+      exit (2);
+  }
+  MARKTIME("convert image table to internal structure: %f sec\n", dtime);
+
+  // *** NOTE : for the moment, regions must be in the range 0 - 360, -90 - +90
+
+  // generate the chip match here so we can define the mosaic centers (if needed)
+  BuildChipMatch (image, Nimage);
+  MARKTIME("build chip match for %d images: %f sec\n", (int) Nimage, dtime);
+
+  initMosaics (image, Nimage);
+  MARKTIME("set mosaic coordinates and Mcal values: %f sec\n", dtime);
+
+  // register the image array with ImageOps.c for later getimageByID calls
+  initImages (image, NULL, Nimage);
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+
+  // for each regionHost, select images which are contained by the region
+  // even faster would be to use a tree to get to the real regions...
+  select_images_hostregion (regionHosts, image, Nimage);
+
+  // supply the mosaics to the image table for the regionHosts : we already have the image
+  // <-> mosaic relationship, we just need to select these mosaics (once per regionHost)
+  select_mosaics_hostregion (regionHosts, image, Nimage);
+
+  return TRUE;
+}
+
+# define D_NIMAGE 1000
+
+// assign images to the region hosts; at the end, each host will have its list of images
+int select_images_hostregion (RegionHostTable *regionHosts, Image *image, off_t Nimage) {
+
+  off_t i, j;
+
+  // INITTIME;
+  
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    regionHosts->hosts[i].Nimage = 0;
+    regionHosts->hosts[i].NIMAGE = D_NIMAGE;
+    ALLOCATE (regionHosts->hosts[i].image, Image, regionHosts->hosts[i].NIMAGE);
+    ALLOCATE (regionHosts->hosts[i].imseq, off_t, regionHosts->hosts[i].NIMAGE);
+  }
+
+  for (j = 0; j < Nimage; j++) {
+    
+    /* select images by photcode, or equiv photcode, if specified */
+    if (NphotcodesKeep > 0) {
+      int found = FALSE;
+      // XXX this bit of code excludes DIS mosaics and should be fixed
+      for (i = 0; (i < NphotcodesKeep) && !found; i++) {
+	if (photcodesKeep[i][0].code == image[j].photcode) found = TRUE;
+	if (photcodesKeep[i][0].code == GetPhotcodeEquivCodebyCode(image[j].photcode)) found = TRUE;
+      }
+      if (!found) continue;
+    }
+    if (NphotcodesSkip > 0) {
+      int found = FALSE;
+      for (i = 0; (i < NphotcodesSkip) && !found; i++) {
+	if (photcodesSkip[i][0].code == image[j].photcode) found = TRUE;
+	if (photcodesSkip[i][0].code == GetPhotcodeEquivCodebyCode(image[j].photcode)) found = TRUE;
+      }
+      if (found) continue;
+    }
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (image[j].tzero < TSTART) continue;
+      if (image[j].tzero > TSTOP) continue;
+    }
+    
+    // do not include DIS (PHU-level mosaics) in the output list
+    if (!strcmp(&image[j].coords.ctype[4], "-DIS")) continue;
+
+    // match the image to its corresponding ASTROMETRIC mosaic (not the same as the Mosaic above)
+    if (!FindMosaicForImage (image, Nimage, j)) {
+      if (VERBOSE2) fprintf (stderr, "cannot find mosaic for "OFF_T_FMT"\n", i);
+      continue;
+    }
+    
+    // Exclude images with crazy astrometry
+    // XXX NOTE : this is gpc1-specific
+    { 
+      double dP1 = hypot(image[j].coords.pc1_1, image[j].coords.pc1_2);
+      double dP2 = hypot(image[j].coords.pc2_1, image[j].coords.pc2_2);
+      if (fabs(dP1 - 1.0) > 0.02) continue;
+      if (fabs(dP2 - 1.0) > 0.02) continue;
+
+      double X00, Y00, X10, Y10, X01, Y01;
+      XY_to_LM (&X00, &Y00, 0.0, 0.0, &image[j].coords);
+      XY_to_LM (&X10, &Y10, image[j].NX, 0.0, &image[j].coords);
+      XY_to_LM (&X01, &Y01, 0.0, image[j].NY, &image[j].coords);
+      double dS0 = hypot ((X00 - X10), (Y00 - Y10));
+      double dS1 = hypot ((X00 - X01), (Y00 - Y01));
+      if (dS0 > 6000) continue;
+      if (dS1 > 6500) continue;
+    }	
+
+    // use a reference coordinate for each image to assign to hosts
+    // define image center - note the DIS images (mosaic phu) are special
+    double Xc, Yc;
+    double Rc, Dc;
+    Xc = 0.5*image[j].NX; 
+    Yc = 0.5*image[j].NY;
+    
+    XY_to_RD (&Rc, &Dc, Xc, Yc, &image[j].coords);
+    Rc = ohana_normalize_angle_to_midpoint (Rc, 180.0);
+    image[j].RAo  = Rc;
+    image[j].DECo = Dc;
+
+    i = find_host_for_coords (regionHosts, Rc, Dc);
+
+    if (i == -1) continue;
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // image bounds are defined for a range centered on the image center thus, an image
+    // with center 0.5 will have chips bounds ranging from ~ -1.5 to +2.5 or so, while an
+    // image with center 359.5 will have chips bounds ranging from ~ 357.5 to 361.5 or so
+    double Rmin, Rmax, Dmin, Dmax;
+    calculate_image_bounds (&image[j], &Rmin, &Rmax, &Dmin, &Dmax, Rc);
+
+    host->RminCat = MIN(Rmin, host->RminCat);
+    host->RmaxCat = MAX(Rmax, host->RmaxCat);
+    host->DminCat = MIN(Dmin, host->DminCat);
+    host->DmaxCat = MAX(Dmax, host->DmaxCat);
+
+    // regionHosts needs to have the full outer boundary
+    // (so reload_catalogs covers the correct region)
+    regionHosts->Rmin = MIN(Rmin, regionHosts->Rmin);
+    regionHosts->Rmax = MAX(Rmax, regionHosts->Rmax);
+    regionHosts->Dmin = MIN(Dmin, regionHosts->Dmin);
+    regionHosts->Dmax = MAX(Dmax, regionHosts->Dmax);
+
+    // this is a bit memory expensive : I am making a complete copy of the image table here
+    off_t Nsubset = regionHosts->hosts[i].Nimage;
+    regionHosts->hosts[i].image[Nsubset] = image[j];
+    regionHosts->hosts[i].imseq[Nsubset] = j;
+
+    regionHosts->hosts[i].Nimage ++;
+    if (regionHosts->hosts[i].Nimage == regionHosts->hosts[i].NIMAGE) {
+      regionHosts->hosts[i].NIMAGE += D_NIMAGE;
+      REALLOCATE (regionHosts->hosts[i].image, Image, regionHosts->hosts[i].NIMAGE);
+      REALLOCATE (regionHosts->hosts[i].imseq, off_t, regionHosts->hosts[i].NIMAGE);
+    }
+  }
+
+  return TRUE;
+}
+
+double Xf[] = {0.0, 1.0, 0.0, 1.0};
+double Yf[] = {0.0, 0.0, 1.0, 1.0};
+
+int calculate_image_bounds (Image *image, double *rmin, double *rmax, double *dmin, double *dmax, double Rmid) {
+
+  int n;
+
+  double Rmin = 360.0;
+  double Rmax =   0.0;
+  double Dmin = +90.0;
+  double Dmax = -90.0;
+
+  // define image corners
+  for (n = 0; n < 4; n++) {
+    double Xc, Yc, Rc, Dc;
+    Xc = Xf[n]*image->NX; 
+    Yc = Yf[n]*image->NY;
+    XY_to_RD (&Rc, &Dc, Xc, Yc, &image->coords);
+    Rc = ohana_normalize_angle_to_midpoint (Rc, Rmid);
+      
+    Rmin = MIN (Rmin, Rc);
+    Rmax = MAX (Rmax, Rc);
+    Dmin = MIN (Dmin, Dc);
+    Dmax = MAX (Dmax, Dc);
+  }
+
+  *rmin = Rmin;
+  *rmax = Rmax;
+  *dmin = Dmin;
+  *dmax = Dmax;
+
+  return TRUE;
+}
+
+int calculate_host_image_bounds (RegionHostTable *regionHosts) {
+
+  int i, n;
+  off_t j;
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // XXX clear the chip match?
+
+    BuildChipMatch (host->image, host->Nimage);
+
+    double Rmin = 360.0;
+    double Rmax =   0.0;
+    double Dmin = +90.0;
+    double Dmax = -90.0;
+
+    for (j = 0; j < host->Nimage; j++) {
+
+      Image *image = &host->image[j];
+
+      if (!FindMosaicForImage (host->image, host->Nimage, j)) {
+	fprintf (stderr, "missing astrometry? programming error?\n");
+	abort ();
+      }
+      
+      // define image corners
+      for (n = 0; n < 4; n++) {
+	double Xc, Yc, Rc, Dc;
+	Xc = Xf[n]*image->NX; 
+	Yc = Yf[n]*image->NY;
+	XY_to_RD (&Rc, &Dc, Xc, Yc, &image->coords);
+	Rc = ohana_normalize_angle_to_midpoint (Rc, 180.0);
+      
+	Rmin = MIN (Rmin, Rc);
+	Dmin = MIN (Dmin, Rc);
+	
+	Rmax = MAX (Rmax, Rc);
+	Dmax = MAX (Dmax, Rc);
+      }
+
+    }
+
+    host->RminCat = Rmin;
+    host->DminCat = Dmin;
+    host->RmaxCat = Rmax;
+    host->DmaxCat = Dmax;
+
+    // regionHosts needs to have the full outer boundary
+    // (so reload_catalogs covers the correct region)
+    regionHosts->Rmin = MIN(Rmin, regionHosts->Rmin);
+    regionHosts->Rmax = MAX(Rmax, regionHosts->Rmax);
+    regionHosts->Dmin = MIN(Dmin, regionHosts->Dmin);
+    regionHosts->Dmax = MAX(Dmax, regionHosts->Dmax);
+  }
+  return TRUE;
+}
+
+
+// XXX add a search tree to speed this up?
+int find_host_for_coords (RegionHostTable *regionHosts, double Rc, double Dc) {
+
+  int i;
+
+  if (isnan(Rc)) return -1;
+  if (isnan(Dc)) return -1;
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+    if (Rc <  host->Rmin) continue;
+    if (Rc >= host->Rmax) continue;
+    if (Dc <  host->Dmin) continue;
+    if (Dc >= host->Dmax) continue;
+
+    return i;
+  }
+  return -1;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/high_speed_objects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/high_speed_objects.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/high_speed_objects.c	(revision 36680)
@@ -161,5 +161,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   /* build spatial index (RA sort) referencing input array sequence */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/hpm_objects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/hpm_objects.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/hpm_objects.c	(revision 36680)
@@ -138,5 +138,5 @@
   tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
   tcoords.Npolyterms = 1;
-  strcpy (tcoords.ctype, "RA---ARC");
+  strcpy (tcoords.ctype, "DEC--ARC");
 
   /* build spatial index (RA sort) referencing input array sequence */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/indexCatalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/indexCatalogs.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/indexCatalogs.c	(revision 36680)
@@ -0,0 +1,66 @@
+# include "relastro.h"
+
+static int   catIDmax = 0;
+static int  *catIDseq = NULL;
+static int  *objIDmax = NULL;
+static int **objIDseq = NULL;
+
+int indexCatalogs (Catalog *catalog, int Ncatalog) {
+
+  int i;
+  off_t j;
+
+  if (!Ncatalog) return TRUE;
+
+  // find the max value of catID
+  for (i = 0; i < Ncatalog; i++) {
+    catIDmax = MAX (catalog[i].catID, catIDmax);
+  }
+
+  ALLOCATE (catIDseq, int, catIDmax + 1);
+  for (i = 0; i < catIDmax + 1; i++) {
+    catIDseq[i] = -1;
+  }
+
+  for (i = 0; i < Ncatalog; i++) {
+    int catID = catalog[i].catID;
+    catIDseq[catID] = i;
+  }
+  
+  ALLOCATE (objIDmax, int,   Ncatalog);
+  ALLOCATE (objIDseq, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    objIDmax[i] = 0;
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      objIDmax[i] = MAX (catalog[i].average[j].objID, objIDmax[i]);
+    }
+
+    ALLOCATE (objIDseq[i], int, objIDmax[i] + 1);
+    for (j = 0; j < objIDmax[i] + 1; j++) {
+      objIDseq[i][j] = -1;
+    }
+
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      int objID = catalog[i].average[j].objID;
+      objIDseq[i][objID] = j;
+    }
+  }
+  return TRUE;
+}
+
+int catID_and_objID_to_seq (int catID, int objID, int *catSeq, off_t *objSeq) {
+
+  if (catID > catIDmax) return FALSE;
+
+  int cat = catIDseq[catID];
+  if (cat < 1) return FALSE;
+
+  if (objID > objIDmax[cat]) return FALSE;
+
+  int obj = objIDseq[cat][objID];
+  if (obj < 1) return FALSE;
+
+  *catSeq = cat;
+  *objSeq = obj;
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/initialize.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/initialize.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/initialize.c	(revision 36680)
@@ -6,5 +6,5 @@
   args (argc, argv);
 
-  if (FIT_TARGET == TARGET_MERGE_SOURCE) return;
+  if (RELASTRO_OP == OP_MERGE_SOURCE) return;
 
   fprintf (stderr, "PHOTCODE_KEEP_LIST: %s\n", PHOTCODE_KEEP_LIST);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/launch_region_hosts.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/launch_region_hosts.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/launch_region_hosts.c	(revision 36680)
@@ -0,0 +1,144 @@
+# include "relastro.h"
+# define DEBUG 0
+
+int strextend (char *input, char *format,...) {
+
+  char tmpextra[1024], tmpline[1024];
+  va_list argp;
+
+  va_start (argp, format);
+  vsnprintf (tmpextra, 1024, format, argp);
+  snprintf (tmpline, 1024, "%s %s", input, tmpextra);
+  strcpy (input, tmpline);
+
+  return TRUE;
+}
+
+int launch_region_hosts (RegionHostTable *regionHosts) {
+
+  int i;
+
+  // clear the I/O files
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.sync");
+    truncate (syncfile, 0);
+    free (syncfile);
+
+    char *fitsfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.fits");
+    truncate (fitsfile, 0);
+    free (fitsfile);
+
+    char *imsyncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.sync");
+    truncate (imsyncfile, 0);
+    free (imsyncfile);
+
+    char *imfitsfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.fits");
+    truncate (imfitsfile, 0);
+    free (imfitsfile);
+
+    char *loopsyncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "loop.sync");
+    truncate (loopsyncfile, 0);
+    free (loopsyncfile);
+  }
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // communication files:
+    // subset images per host : CATDIR/Image.HOSTNAME.fits
+    char filename[1024];
+    snprintf (filename, 1024, "%s/Image.%d.fits", CATDIR, host->hostID);
+
+    // write the image subset for this host
+    ImageTableSave (filename, host->image, host->Nimage);
+
+    char command[1024];
+    snprintf (command, 1024, "relastro -parallel-images %s", filename);
+    strextend (command, "-region-hosts %s", REGION_FILE);
+    strextend (command, "-region-hostID %d", host->hostID);
+    strextend (command, "-D CATDIR %s", CATDIR);
+    strextend (command, "-region %f %f %f %f", host->RminCat, host->RmaxCat, host->DminCat, host->DmaxCat);
+    strextend (command, "-statmode %s", STATMODE);
+    strextend (command, "-minerror %f", MIN_ERROR);
+    strextend (command, "-nloop %d", NLOOP);
+    strextend (command, "-threads %d", NTHREADS);
+
+    switch (FIT_TARGET) {
+      case TARGET_SIMPLE:
+	strextend (command, "-update-simple");
+	break;
+      case TARGET_CHIPS:
+	strextend (command, "-update-chips");
+	break;
+      case TARGET_MOSAICS:
+	strextend (command, "-update-mosaics");
+	break;
+      case TARGET_NONE:
+	abort();
+    }
+
+    if (VERBOSE)       	    strextend (command, "-v");
+    if (VERBOSE2)      	    strextend (command, "-vv");
+    if (RESET)         	    strextend (command, "-reset");
+    if (UPDATE)        	    strextend (command, "-update");
+    if (PARALLEL)      	    strextend (command, "-parallel");
+    if (PARALLEL_MANUAL)    strextend (command, "-parallel-manual");
+    if (PARALLEL_SERIAL)    strextend (command, "-parallel-serial");
+    if (PHOTCODE_KEEP_LIST) strextend (command, "+photcode %s", PHOTCODE_KEEP_LIST); 
+    if (PHOTCODE_SKIP_LIST) strextend (command, "-photcode %s", PHOTCODE_SKIP_LIST);
+
+    if (MaxDensityUse) 	    strextend (command, "-max-density %f", MaxDensityValue);
+    if (ImagSelect)         strextend (command, "-instmag %f %f", ImagMin, ImagMax);
+    if (PhotFlagSelect)     strextend (command, "+photflags"); 
+    if (PhotFlagBad)        strextend (command, "+photflagbad %d", PhotFlagBad); 
+    if (PhotFlagPoor)       strextend (command, "+photflagpoor %d", PhotFlagPoor); 
+
+    if (MinBadQF > 0.0)        strextend (command, "-min-bad-psfqf %f", MinBadQF);
+    if (MaxMeanOffset != 10.0) strextend (command, "-max-mean-offset  %f", MaxMeanOffset);
+
+    if (TimeSelect) { 
+      char *tstart = ohana_sec_to_date (TSTART);
+      char *tstop  = ohana_sec_to_date (TSTOP);
+      strextend (command, "-time %s %s", tstart, tstop); 
+      free (tstart);
+      free (tstop);
+    }
+
+    fprintf (stderr, "command: %s\n", command);
+    
+    if (PARALLEL_REGIONS_MANUAL) continue;
+
+    // launch the job, then wait for it to be done loading catalogs.  force the remote
+    // client to generate the file
+    char *syncfile = make_filename (CATDIR, host->hostname, host->hostID, "loadcat.sync");
+    clear_sync_file (syncfile);
+
+    // launch the job on the remote machine (no handshake)
+    int errorInfo = 0;
+    int pid = rconnect ("ssh", host->hostname, command, host->stdio, &errorInfo, FALSE);
+    if (!pid) {
+      if (DEBUG) fprintf (stderr, "failure to start %s (error %d)\n", host->hostname, errorInfo);
+      exit (1);
+    }
+    host->pid = pid; // save for future reference
+
+    // remove client is done, go ahead with next client
+    check_sync_file (syncfile, 1);
+    free (syncfile);
+  }
+
+  if (PARALLEL_REGIONS_MANUAL) {
+    fprintf (stderr, "run the relastro_client commands above.  when these are done, hit return\n");
+    getchar();
+  } else {
+    RegionHostTableWaitJobsGetIO (regionHosts, __FILE__, __LINE__, VERBOSE);
+  }
+ 
+  int status = TRUE;
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    status = status && (regionHosts->hosts[i].status == 0);
+  }
+
+  return status;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_catalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_catalogs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_catalogs.c	(revision 36680)
@@ -115,4 +115,9 @@
 Catalog *load_catalogs_parallel (SkyList *sky, int *Ncatalog) {
 
+  char uniquer[12];
+  int TIME = time(NULL);
+  int PID = getpid();
+  snprintf (uniquer, 12, "%05d.%05d", PID, TIME % 100000);
+
   // load the list of hosts
   HostTable *table = HostTableLoad (CATDIR, sky->hosts);
@@ -131,5 +136,5 @@
 
     ALLOCATE (table->hosts[i].results, char, 1024);
-    snprintf (table->hosts[i].results, 1024, "%s/relastro.catalog.subset.dat", table->hosts[i].pathname);
+    snprintf (table->hosts[i].results, 1024, "%s/relastro.catalog.%s.dat", table->hosts[i].pathname, uniquer);
 
     // options / arguments that can affect relastro_client -load:
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/load_images.c	(revision 36680)
@@ -1,9 +1,3 @@
 # include "relastro.h"
-
-# define MARKTIME(MSG,...) { \
-  float dtime; \
-  gettimeofday (&stop, (void *) NULL); \
-  dtime = DTIME (stop, start); \
-  fprintf (stderr, MSG, __VA_ARGS__); }
 
 int load_images (FITS_DB *db, SkyList *skylist, int UseFullOverlap) {
@@ -12,7 +6,6 @@
   off_t      Nimage, Nsubset;
   off_t     *LineNumber;
-  struct timeval start, stop;
 
-  gettimeofday (&start, (void *) NULL);
+  INITTIME;
 
   // convert database table to internal structure
@@ -35,5 +28,5 @@
   
   /* unlock, if we can (else, unlocked below) */
-  int unlockImages = !UPDATE || (FIT_TARGET == TARGET_UPDATE_OFFSETS);
+  int unlockImages = !UPDATE || (RELASTRO_OP == OP_UPDATE_OFFSETS);
   if (unlockImages) dvo_image_unlock (db); 
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/markObjects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/markObjects.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/markObjects.c	(revision 36680)
@@ -0,0 +1,24 @@
+# include "relastro.h"
+
+int markObjects (Catalog *catalog, int Ncatalog) {
+
+  int i, n;
+  off_t j;
+
+  // How strongly do I own this object?
+  for (i = 0; i < Ncatalog; i++) {
+    ALLOCATE (catalog[i].nOwn, int, catalog[i].Naverage);
+    memset (catalog[i].nOwn, 0, catalog[i].Naverage*sizeof(int));
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      int nOwn = 0;
+      int m = catalog[i].average[j].measureOffset;
+      for (n = 0; n < catalog[i].average[j].Nmeasure; n++) {
+	if (!catalog[i].measureT[m+n].myDet) continue;
+	nOwn ++;
+      }
+      catalog[i].nOwn[j] = nOwn;
+    }
+  }
+  return TRUE;
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro.c	(revision 36680)
@@ -10,33 +10,41 @@
   SkyList *skylist = SkyListByPatch (sky, -1, &UserPatch);
 
-  switch (FIT_TARGET) {
-    case TARGET_UPDATE_OBJECTS:
+  switch (RELASTRO_OP) {
+    case OP_UPDATE_OBJECTS:
       /* the object analysis is a separate process iterating over catalogs */
       relastro_objects (skylist, 0, NULL);
       exit (0);
 
-    case TARGET_HIGH_SPEED:
+    case OP_HIGH_SPEED:
       /* high-speed is a 2pt cross-correlation process for linking moving objects (high PM) */
       high_speed_catalogs (sky, skylist, 0, NULL);
       exit (0);
 
-    case TARGET_HPM:
+    case OP_HPM:
       hpm_catalogs (sky, skylist, 0, NULL);
       exit (0);
 
-    case TARGET_MERGE_SOURCE:
+    case OP_MERGE_SOURCE:
       /* a special method to manually merge unlinked detections of sources togther (not parallel) */
       relastro_merge_source (sky);
       exit (0);
 
-    case TARGET_SIMPLE:
-    case TARGET_CHIPS:
-    case TARGET_MOSAICS:
+    case OP_IMAGES:
       relastro_images (skylist);
       exit (0);
 
-    case TARGET_UPDATE_OFFSETS:
+    case OP_UPDATE_OFFSETS:
       // iterate over catalogs to make detection coordinates consistant
       UpdateObjectOffsets (skylist, 0, NULL);
+      exit (0);
+
+    case OP_PARALLEL_REGIONS:
+      // run image updates in parallel across multiple remote machines
+      relastro_parallel_regions ();
+      exit (0);
+
+    case OP_PARALLEL_IMAGES:
+      // operation on the remote machines in the PARALLEL_REGION mode
+      relastro_parallel_images ();
       exit (0);
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_client.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_client.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_client.c	(revision 36680)
@@ -28,23 +28,23 @@
   SkyList *skylist = SkyListByPatch (sky, -1, &UserPatch);
 
-  switch (FIT_TARGET) {
+  switch (RELASTRO_OP) {
 
-    case TARGET_LOAD_OBJECTS: {
+    case OP_LOAD_OBJECTS: {
       // USAGE: relastro_client -load-objects
       int Ncatalog;
       Catalog *catalog = load_catalogs (skylist, &Ncatalog, TRUE, HOST_ID, HOSTDIR);
       if (!catalog) {
-	  fprintf (stderr, "ERROR loading catalogs from %s\n", CATDIR);
-	  exit (2);
+	fprintf (stderr, "ERROR loading catalogs from %s\n", CATDIR);
+	exit (2);
       }
       BrightCatalog *bcatalog = BrightCatalogMerge (catalog, Ncatalog);
       if (!BrightCatalogSave (BCATALOG, bcatalog)) {
-	  fprintf (stderr, "ERROR saving bright catalog from %s\n", CATDIR);
-	  exit (2);
+	fprintf (stderr, "ERROR saving bright catalog from %s\n", CATDIR);
+	exit (2);
       }
       break;
     }
       
-    case TARGET_UPDATE_OBJECTS: {
+    case OP_UPDATE_OBJECTS: {
       // USAGE: relastro_client -update-objects
       relastro_objects (skylist, HOST_ID, HOSTDIR);
@@ -52,5 +52,5 @@
     }
 
-    case TARGET_HIGH_SPEED: {
+    case OP_HIGH_SPEED: {
       // USAGE: relastro_client -high-speed
       high_speed_catalogs (sky, skylist, HOST_ID, HOSTDIR);
@@ -58,5 +58,5 @@
     }
 
-    case TARGET_HPM: {
+    case OP_HPM: {
       // USAGE: relastro_client -high-speed
       hpm_catalogs (sky, skylist, HOST_ID, HOSTDIR);
@@ -64,6 +64,6 @@
     }
 
-      // XXX loading the images is fairly costly -- see if we can do an image subset
-    case TARGET_UPDATE_OFFSETS: {
+      // XXX loading the images is fairly costly -- see if we can do an image subset?
+    case OP_UPDATE_OFFSETS: {
       FITS_DB db;
       
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_images.c	(revision 36680)
@@ -6,7 +6,6 @@
   Catalog *catalog;
   FITS_DB db;
-  struct timeval start, stop;
 
-  gettimeofday (&start, (void *) NULL);
+  INITTIME;
 
   /* register database handle with shutdown procedure */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_objects.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_objects.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_objects.c	(revision 36680)
@@ -144,5 +144,4 @@
     // PM_TOOFEW
     // SRC_MEAS_TOOFEW
-    // FIT_TARGET
 
     char command[1024];
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_images.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_images.c	(revision 36680)
@@ -0,0 +1,131 @@
+# include "relastro.h"
+
+/* This function is essentially identical to relastro_images, except:
+
+ * load the subset images saved by the master node
+ * distinguish detections we own (touch our images) and those we don't
+ * distinguish objects we own (in region) and those we don't
+ * update the unowned detections for owned objects from neighbor regions
+ * update the unowned objects for owned detections 
+ */
+
+int relastro_parallel_images () {
+
+  int i, Ncatalog;
+  Catalog *catalog = NULL;
+
+  INITTIME;
+
+  // load the RegionTable (UserRegion should not be used at this level)
+  RegionHostTable *regionHosts = RegionHostTableLoad (CATDIR, REGION_FILE);
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  RegionHostFindNeighbors (regionHosts, myHost);
+
+  // load the subset images belonging to this host
+  off_t Nimage;
+  Image *image = ImageTableLoad (IMAGE_TABLE, &Nimage);
+  if (!image) {
+    fprintf (stderr, "ERROR loading image %s\n", IMAGE_TABLE);
+    exit (2);
+  }
+
+  // once we have read this table, we should remove it for repeat runs
+  // unlink (IMAGE_TABLE); // XXX a bit risky, add some protection?
+
+  // XXX need to deal with mosaic vs image...
+  initMosaics (image, Nimage);
+
+  initImages (image, NULL, Nimage);
+
+  SkyTable *sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  SkyList *skylist = SkyListByBounds (sky, -1, UserPatch.Rmin, UserPatch.Rmax, UserPatch.Dmin, UserPatch.Dmax);
+
+  initCoords();
+
+  /* load catalog data from region files (hostID is 0 since we are not a client */
+  catalog = load_catalogs (skylist, &Ncatalog, TRUE, 0, NULL);
+  MARKTIME("-- load catalog data: %f sec\n", dtime);
+  
+  // NOTE: if I let all hosts load blindly, I saturate the data clients with too many
+  // relastro_client requests.  I need to have the master mediate this.  the master
+  // will not launch the next remote job until this one says it is done
+  char *syncfile = make_filename (CATDIR, regionHosts->hosts[myHost].hostname, REGION_HOST_ID, "loadcat.sync");
+  update_sync_file (syncfile, 1);
+  
+  // generate tables go from catID,objID -> catSeq,objSeq
+  indexCatalogs (catalog, Ncatalog);
+
+  /* match measurements with images, mosaics */
+  initImageBins  (catalog, Ncatalog, TRUE);
+  MARKTIME("-- make image bins: %f sec\n", dtime);
+
+  findImages (catalog, Ncatalog, TRUE);
+  MARKTIME("-- set up image indexes: %f sec\n", dtime);
+
+  // set test points based on the starmap
+  createStarMap (catalog, Ncatalog);
+
+  markObjects (catalog, Ncatalog);
+
+  SAVEPLOT = FALSE;
+
+  /* major modes */
+  switch (FIT_TARGET) {
+    case TARGET_SIMPLE:
+      for (i = 0; i < NLOOP; i++) {
+	UpdateObjects (catalog, Ncatalog);
+	share_mean_pos (catalog, Ncatalog, regionHosts, i);
+	slurp_mean_pos (catalog, Ncatalog, regionHosts, i);
+	UpdateSimple (catalog, Ncatalog);
+	share_image_pos (regionHosts, i);
+	slurp_image_pos (regionHosts, i);
+      }
+      break;
+
+    case TARGET_CHIPS:
+      for (i = 0; i < NLOOP; i++) {
+	UpdateObjects (catalog, Ncatalog);
+	share_mean_pos (catalog, Ncatalog, regionHosts, i);
+	slurp_mean_pos (catalog, Ncatalog, regionHosts, i);
+	UpdateChips (catalog, Ncatalog);
+	share_image_pos (regionHosts, i);
+	slurp_image_pos (regionHosts, i);
+	MARKTIME("update chips: %f sec\n", dtime);
+      }
+      // create summary plots of the process
+      // relastroVisualSummaryChips();
+      break;
+
+    case TARGET_MOSAICS:
+      for (i = 0; i < NLOOP; i++) {
+	UpdateObjects (catalog, Ncatalog);
+	share_mean_pos (catalog, Ncatalog, regionHosts, i);
+	slurp_mean_pos (catalog, Ncatalog, regionHosts, i);
+	UpdateMosaic (catalog, Ncatalog);
+	share_image_pos (regionHosts, i);
+	slurp_image_pos (regionHosts, i);
+      }
+      break;
+
+    default:
+      fprintf (stderr, "programming error at %s:%d", __FILE__, __LINE__);
+      exit (2);
+  }
+
+  // this is a checkpoint to make sure all hosts have finished the loop above
+  char *loopsyncfile = make_filename (CATDIR, regionHosts->hosts[myHost].hostname, REGION_HOST_ID, "loop.sync");
+  update_sync_file (loopsyncfile, 0);
+  free (loopsyncfile);
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    char *loopsync = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "loop.sync");
+    check_sync_file (loopsync, 0);
+    free (loopsync);
+  }    
+
+  share_image_pos (regionHosts, -1);
+
+  exit (0);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_regions.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_regions.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/relastro_parallel_regions.c	(revision 36680)
@@ -0,0 +1,79 @@
+# include "relastro.h"
+
+int relastro_parallel_regions () {
+
+  int status;
+  FITS_DB db;
+
+  INITTIME;
+
+  // load the RegionTable (UserRegion should not be used at this level)
+  RegionHostTable *regionHosts = RegionHostTableLoad (CATDIR, REGION_FILE);
+  if (!regionHosts) {
+    fprintf (stderr, "ERROR: problem with region host table\n");
+    exit (2);
+  }
+
+  // register database handle with shutdown procedure 
+  set_db (&db);
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+
+  // lock and load the image db table
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status && UPDATE) {
+    fprintf (stderr, "error\n");
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    // if the Image.dat file is missing, db.dbstate will have a value of either:
+    // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+    Shutdown ("ERROR: database %s contains no image data", CATDIR);
+  }
+
+  // read data from Image.dat file
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+  MARKTIME("-- load image data: %f sec\n", dtime);
+
+  /* assign the images to the different region hosts */
+  if (!assign_images (&db, regionHosts)) Shutdown ("error assigning images to region hosts");
+  MARKTIME("-- assign images: %f sec\n", dtime);
+
+  /* launch processing on the parallel region hosts */
+  if (!launch_region_hosts (regionHosts)) Shutdown ("error launching region hosts");
+
+  // retrieve updated image parameters from the remote hosts (also set Image.mcal)
+  if (!slurp_image_pos (regionHosts, -1)) Shutdown ("error loading image updates");
+
+  if (!UPDATE) { 
+    dvo_image_unlock (&db); 
+    MARKTIME ("finished relastro -parallel-regions: %f sec total\n", dtime);
+    fprintf (stderr, "NOTE: UPDATE is OFF (results are not saved)\n");
+    exit (0);
+  }
+
+  SkyTable *sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  SkyList *skylist = SkyListByBounds (sky, -1, regionHosts->Rmin, regionHosts->Rmax, regionHosts->Dmin, regionHosts->Dmax);
+
+  if (PARALLEL) {
+    // save the updated image parameters
+    dvo_image_save (&db, VERBOSE);
+    dvo_image_unlock (&db); 
+  }
+
+  // iterate over catalogs to make detection coordinates consistant
+  UpdateObjectOffsets (skylist, 0, NULL);
+
+  if (!PARALLEL) {
+    // save the changes to the image parameters
+    dvo_image_save (&db, VERBOSE);
+    dvo_image_unlock (&db); 
+  }
+
+  MARKTIME ("finished relastro -parallel-regions: %f sec total\n", dtime);
+
+  exit (0);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/resort_catalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/resort_catalog.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/resort_catalog.c	(revision 36680)
@@ -30,6 +30,5 @@
   if (catalog[0].sorted == TRUE) return;
 
-  // struct timeval start, stop;
-  // gettimeofday (&start, NULL);
+  // INITTIME;
 
   /* internal counters */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/select_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/select_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/select_images.c	(revision 36680)
@@ -15,10 +15,4 @@
 void dsortindex (double *X, off_t *Y, int N);
 off_t getRegionStartByRA (double R, double *Rref, off_t Nregions);
-
-# define MARKTIME(MSG,...) { \
-  float dtime; \
-  gettimeofday (&stop, (void *) NULL); \
-  dtime = DTIME (stop, start); \
-  fprintf (stderr, MSG, __VA_ARGS__); }
 
 Image *select_images (SkyList *skylist, Image *timage, off_t Ntimage, off_t **LineNumber, off_t *Nimage, int UseFullOverlap) {
@@ -31,5 +25,4 @@
   Coords tcoords;
   SkyRegionCoords *skycoords;
-  struct timeval start, stop;
   
   double RmaxSkyRegion, RminSkyRegion, RmidSkyRegion, DminSkyRegion, DmaxSkyRegion;
@@ -50,5 +43,5 @@
   }
 
-  gettimeofday (&start, (void *) NULL);
+  INITTIME;
 
   // the comparison is made in the catalog local projection. below we set crval1,2
@@ -57,5 +50,5 @@
   tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
   tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
-  strcpy (tcoords.ctype, "RA---TAN");
+  strcpy (tcoords.ctype, "DEC--TAN");
 
   ALLOCATE (skycoords, SkyRegionCoords, skylist[0].Nregions);
@@ -132,5 +125,6 @@
     if (NphotcodesKeep > 0) {
       found = FALSE;
-      // XXX this bit of code excludes DIS mosaics and should be fixed
+      // we have to keep DIS mosaics explicitly (photcode = 0)
+      if (!strcmp(&timage[i].coords.ctype[4], "-DIS")) found = TRUE;
       for (k = 0; (k < NphotcodesKeep) && !found; k++) {
 	if (photcodesKeep[k][0].code == timage[i].photcode) found = TRUE;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_images_pos.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_images_pos.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_images_pos.c	(revision 36680)
@@ -0,0 +1,117 @@
+# include "relastro.h"
+
+// we are sharing image astrometry calibrations for all images which (a) I own and (b) which have unowned detections
+
+# define D_NIMAGEPOS 1000
+int share_image_pos (RegionHostTable *regionHosts, int nloop) {
+
+  off_t i, Nimages;
+  Image *images = getimages (&Nimages, NULL);
+
+  off_t Nimage_pos = 0;
+  off_t NIMAGE_POS = D_NIMAGEPOS;
+  
+  ImagePos *image_pos = NULL;
+  ALLOCATE (image_pos, ImagePos, NIMAGE_POS);
+
+  for (i = 0; i < Nimages; i++) {
+    // XXX does this image have missing detections (does someone else need it?)
+    // XXX : NOTE NEED TO FIX THIS: if (imageExtra[i].Nmiss == 0) continue;
+    
+    set_image_pos (&image_pos[Nimage_pos], &images[i]);
+    Nimage_pos ++;
+
+    CHECK_REALLOCATE (image_pos, ImagePos, NIMAGE_POS, Nimage_pos, D_NIMAGEPOS);
+  }
+
+  // write out the image_mag fits table AND write state in some file
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  char *hostname = regionHosts->hosts[myHost].hostname;
+
+  char *iposfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "imagepos.fits");
+  ImagePosSave (iposfile, image_pos, Nimage_pos);
+  free (image_pos);
+  free (iposfile);
+
+  char *syncfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "imagepos.sync");
+  update_sync_file (syncfile, nloop);
+  free (syncfile);
+
+  return TRUE;
+}
+
+int slurp_image_pos (RegionHostTable *regionHosts, int nloop) {
+
+  off_t Nimage, i;
+  Image *images = getimages (&Nimage, NULL);
+
+  int Nimage_pos = 0;
+  ImagePos *image_pos = NULL;
+  ALLOCATE (image_pos, ImagePos, 1);
+
+  fprintf (stderr, "grabbing image mags from other hosts...\n");
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    if (REGION_HOST_ID && !regionHosts->hosts[i].isNeighbor) continue;
+
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagepos.sync");
+    check_sync_file (syncfile, nloop);
+    free (syncfile);
+    
+    off_t Nsubset;
+    char *iposfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagepos.fits");
+    ImagePos *image_pos_subset = ImagePosLoad (iposfile, &Nsubset);
+    free (iposfile);
+
+    image_pos = merge_image_pos (image_pos, &Nimage_pos, image_pos_subset, Nsubset);
+  }
+
+  for (i = 0; i < Nimage_pos; i++) {
+    off_t seq = getImageByID (image_pos[i].imageID);
+    if (seq < 0) {
+      // XXX is this a problem? (no, other hosts don't know which images I own)
+      continue;
+    }
+    images[seq].coords     = image_pos[i].coords    ;
+    images[seq].dXpixSys   = image_pos[i].dXpixSys  ;
+    images[seq].dYpixSys   = image_pos[i].dYpixSys  ;
+    images[seq].imageID    = image_pos[i].imageID   ;
+    images[seq].nFitAstrom = image_pos[i].nFitAstrom;
+    images[seq].flags      = image_pos[i].flags     ;
+  }
+  free (image_pos);
+
+  fprintf (stderr, "DONE grabbing image mags from other hosts\n");
+
+  return TRUE;
+}
+
+int set_image_pos (ImagePos *image_pos, Image *image) {
+
+  image_pos->coords       = image->coords;
+  image_pos->dXpixSys     = image->dXpixSys;
+  image_pos->dYpixSys     = image->dYpixSys;
+  image_pos->imageID      = image->imageID;
+  image_pos->nFitAstrom   = image->nFitAstrom;
+  image_pos->flags        = image->flags;
+
+  return TRUE;
+}
+
+ImagePos *merge_image_pos (ImagePos *target, int *ntarget, ImagePos *source, int Nsource) {
+
+  off_t i;
+
+  REALLOCATE (target, ImagePos, *ntarget + Nsource);
+  for (i = 0; i < Nsource; i++) {
+    off_t n = i + *ntarget;
+    target[n] = source[i];
+  }
+  
+  free (source);
+
+  *ntarget += Nsource;
+  return (target);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_mean_pos.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_mean_pos.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/share_mean_pos.c	(revision 36680)
@@ -0,0 +1,132 @@
+# include "relastro.h"
+// we are sharing mean positions for all objects which (a) I own and (b) which have unowned detections
+
+# define D_NMEANPOS 10000
+int share_mean_pos (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop) {
+
+  int i;
+  off_t j;
+
+  off_t Nmeanpos = 0;
+  off_t NMEANPOS = D_NMEANPOS;
+
+  MeanPos *meanpos = NULL;
+  ALLOCATE (meanpos, MeanPos, NMEANPOS);
+
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  double Rmin = regionHosts->hosts[myHost].Rmin;
+  double Rmax = regionHosts->hosts[myHost].Rmax;
+  double Dmin = regionHosts->hosts[myHost].Dmin;
+  double Dmax = regionHosts->hosts[myHost].Dmax;
+
+  // XXX skip some catalogs based on UserPatch?
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      // do I own this object? (in region range?) --- CAREFUL HERE!!
+      if (catalog[i].average[j].R <  Rmin) continue;
+      if (catalog[i].average[j].R >= Rmax) continue;
+      if (catalog[i].average[j].D <  Dmin) continue;
+      if (catalog[i].average[j].D >= Dmax) continue;
+
+      // does this object have missing detections (does someone else need it?)
+      // XXX : sky objects without missing detections
+      // XXX watch out for detections which are not associated with an image (REF)
+      if (catalog[i].nOwn[j] == catalog[i].average[j].Nmeasure) continue;
+
+      set_mean_pos (&meanpos[Nmeanpos], &catalog[i].average[j]);
+      Nmeanpos ++;
+      CHECK_REALLOCATE (meanpos, MeanPos, NMEANPOS, Nmeanpos, D_NMEANPOS);
+    }
+  }
+
+  // write out the meanmag fits table AND write state in some file
+  char *hostname = regionHosts->hosts[myHost].hostname;
+
+  char *posfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "meanpos.fits");
+  MeanPosSave (posfile, meanpos, Nmeanpos);
+  free (meanpos);
+  free (posfile);
+
+  char *syncfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "meanpos.sync");
+  update_sync_file (syncfile, nloop);
+  free (syncfile);
+
+  return TRUE;
+}
+
+int slurp_mean_pos (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop) {
+
+  off_t i;
+
+  int Nmeanpos = 0;
+  MeanPos *meanpos = NULL;
+  ALLOCATE (meanpos, MeanPos, 1);
+
+  fprintf (stderr, "grabbing mean object pos from other hosts...\n");
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    if (REGION_HOST_ID && !regionHosts->hosts[i].isNeighbor) continue;
+
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanpos.sync");
+    check_sync_file (syncfile, nloop);
+    free (syncfile);
+    
+    off_t Nsubset = 0;
+    char *posfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanpos.fits");
+    MeanPos *meanposSubset = MeanPosLoad (posfile, &Nsubset);
+    free (posfile);
+
+    // merge_mean_pos reallocs meanpos and frees the input meanposSubset
+    meanpos = merge_mean_pos (meanpos, &Nmeanpos, meanposSubset, Nsubset);
+  }
+
+  for (i = 0; i < Nmeanpos; i++) {
+    int objID = meanpos[i].objID;
+    int catID = meanpos[i].catID;
+
+    // set the mean mag
+    int catSeq;
+    off_t objSeq;
+    if (!catID_and_objID_to_seq (catID, objID, &catSeq, &objSeq)) {
+	// XXX what should I do if this does not match?
+	continue;
+    }
+
+    catalog[catSeq].average[objSeq].R = meanpos[i].R;
+    catalog[catSeq].average[objSeq].D = meanpos[i].D;
+  }
+  free (meanpos);
+
+  fprintf (stderr, "DONE grabbing mean object pos from other hosts...\n");
+
+  return TRUE;
+}
+
+int set_mean_pos (MeanPos *meanpos, Average *average) {
+
+  meanpos->R     = average->R;
+  meanpos->D     = average->D;
+  meanpos->objID = average->objID;
+  meanpos->catID = average->catID;
+
+  return TRUE;
+}
+
+MeanPos *merge_mean_pos (MeanPos *target, int *ntarget, MeanPos *source, int Nsource) {
+
+  off_t i;
+
+  REALLOCATE (target, MeanPos, *ntarget + Nsource);
+  for (i = 0; i < Nsource; i++) {
+    off_t n = i + *ntarget;
+    target[n] = source[i];
+  }
+  
+  free (source);
+
+  *ntarget += Nsource;
+  return (target);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/syncfile.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/syncfile.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/syncfile.c	(revision 36680)
@@ -0,0 +1,77 @@
+# include "relastro.h"
+# define MSG_LENGTH 12
+
+char *make_filename (char *dirname, char *hostname, int hostID, char *tailname) {
+
+  char tmp[10], *line; 
+  int Nchar = snprintf (tmp, 0, "%s/%s.%03d.%s", dirname, hostname, hostID, tailname);
+  
+  ALLOCATE (line, char, Nchar + 1);
+  snprintf (line, Nchar + 1, "%s/%s.%03d.%s", dirname, hostname, hostID, tailname);
+
+  return line;
+}
+
+int check_sync_file (char *filename, int nloop) {
+
+  char message[MSG_LENGTH];
+
+  FILE *f = NULL; 
+
+  while (TRUE) {
+
+    f = fopen (filename, "r");
+    if (!f) {
+      usleep (2000000);
+      continue;
+    }
+
+    // XXX MSG_LENGTH : 0 EOL byte?
+    int Nread = fread (message, 1, MSG_LENGTH, f);
+    if (Nread < MSG_LENGTH) {
+      fclose (f);
+      usleep (2000000);
+      continue;
+    }
+    fclose (f);
+
+    // message is of the form: NLOOP: %03d
+    int loop;
+    sscanf (message, "%*s %d", &loop);
+    if (loop != nloop) {
+      usleep (2000000);
+      continue;
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+int clear_sync_file (char *filename) {
+  // delete file contents
+  truncate (filename, 0);
+
+  return TRUE;
+}
+
+int update_sync_file (char *filename, int nloop) {
+
+  char message[MSG_LENGTH];
+
+  FILE *f = fopen (filename, "w");
+  if (!f) { 
+    fprintf (stderr, "failure to open sync file for write\n");
+    exit (4);
+  }
+
+  snprintf (message, MSG_LENGTH, "NLOOP: %03d\n", nloop);
+  
+  int Nwrite = fwrite (message, 1, MSG_LENGTH, f);
+  if (Nwrite != MSG_LENGTH) {
+    fprintf (stderr, "failure to write sync message\n");
+    exit (3);
+  }
+
+  fclose (f);
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/testparallax.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/testparallax.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relastro/src/testparallax.c	(revision 36680)
@@ -57,5 +57,5 @@
   coords.pc1_2  = coords.pc2_1 = 0.0;
   coords.Npolyterms = 1;
-  strcpy (coords.ctype, "RA---SIN");
+  strcpy (coords.ctype, "DEC--SIN");
 
   // use one point as a local reference
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/Makefile
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/Makefile	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/Makefile	(revision 36680)
@@ -47,5 +47,16 @@
 $(SRC)/relphot_images.$(ARCH).o	 \
 $(SRC)/relphot_objects.$(ARCH).o	 \
+$(SRC)/relphot_parallel_regions.$(ARCH).o \
+$(SRC)/relphot_parallel_images.$(ARCH).o \
 $(SRC)/select_images.$(ARCH).o	 \
+$(SRC)/assign_images.$(ARCH).o	 \
+$(SRC)/launch_region_hosts.$(ARCH).o	 \
+$(SRC)/ImageTable.$(ARCH).o	 \
+$(SRC)/ImageMagIO.$(ARCH).o	 \
+$(SRC)/syncfile.$(ARCH).o	 \
+$(SRC)/share_image_mags.$(ARCH).o	 \
+$(SRC)/share_mean_mags.$(ARCH).o	 \
+$(SRC)/MeanMagIO.$(ARCH).o	 \
+$(SRC)/indexCatalog.$(ARCH).o	 \
 $(SRC)/client_logger.$(ARCH).o	 \
 $(SRC)/setExclusions.$(ARCH).o 	 \
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/doc/parallel.txt
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/doc/parallel.txt	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/doc/parallel.txt	(revision 36680)
@@ -1,2 +1,71 @@
+
+2014.02.15
+
+Nearly done with the relphot mods.  some outstanding questions;
+
+ * image vs mosaic?
+ * how do decide which images need to be shared?
+
+2014.02.14
+
+ more relphot notes:
+
+ * I load the catalogs, with a  
+
+2014.02.12
+
+I am making progess on the relphot -parallel-regions implementation.  some things I need to deal with:
+
+ * assign_images: select mosaics for all images saved for a given host (need to supply them as well)
+   ** I need to decide on the Image / Mosaic split.  If I am calibrating by image, then the image center defines ownership
+   ** if I am calibrating by exposure, then the mosaic center must define ownership
+ o determine SkyList covering the images for region host
+ o tag detections and objects which I own or do not own
+   o ImageOps.c / matchImage matches detections to images and needs to handle mine / not mine cases
+   o ditto for matchMosaics
+ o load_catalogs : I need to make my requested catalogs unique (just add uniquer to load_catalogs.c:130
+ o figure out what fields I'm saving in meanmags and how to construct it
+   -- mag, objID, catID, photcode, (dmag or other stats?)
+ * from which hosts do I slurp mean mags?
+ o function to merge a new meanmag array into the existing one
+ o how to go from (objID,catID) to a given catalog[i].average[j]
+
+2014.02.06
+
+** a single region-level host owns images for which the center lands
+   in its region.
+
+** it owns detections which come from images which it owns
+   
+** it owns objects which land in its region
+
+Extending parallel relphot processing to split the sky (and images)
+into regions, each of which runs in parallel at the same time:
+
+Top Level (relphot -parallel-images):
+ * define regions of the sky -> hosts
+ * load images, assign to hosts
+ * launch region-level jobs on remote hosts
+
+ Region Level (relphot -parallel-images-region)
+  * load my image subset table
+  * request objects and detections for my skyregion
+  * match images & objects, etc
+
+  * update my image parameters
+  * update my detections
+  * write out detections 
+  * load detections from my border hosts
+  * match to my objects
+  * update my objects
+  * write out objects
+  * load objects from my border hosts
+  * update images
+    (iterate N times)
+  * write out image parameters
+
+Top Level
+  * read image parameters, update
+  * update objects
 
 2012.02.13
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/include/relphot.h
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/include/relphot.h	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/include/relphot.h	(revision 36680)
@@ -23,4 +23,13 @@
 // # define IDX_T off_t
 # define IDX_T int 
+
+typedef enum {
+  MODE_ERROR = 0,
+  UPDATE_IMAGES,
+  UPDATE_AVERAGES,
+  PARALLEL_REGIONS,
+  PARALLEL_IMAGES,
+  APPLY_OFFSETS,
+} RelphotMode;
 
 typedef enum {
@@ -86,10 +95,15 @@
   // others are only used on the final output steps.
 
-  double *list;		      // list of measure.mag values for a given star
-  double *dlist;	      // mag errors for a star
-  double *wlist;	      // weights to use for mean mags
-  double *aplist;	      // ap mags for a star
-  double *kronlist;	      // kron mags for a star
+  double *Mpsflist;           // list of measure.mag values for a given star
+  double *dpsflist;	      // mag errors for a star
+  double *wpsflist;	      // weights to use for mean mags
+
+  double *Maplist;	      // ap mags for a star
+  double *daplist;	      // ap mags for a star
+  double *waplist;	      // ap mags for a star
+
+  double *Mkronlist;	      // kron mags for a star
   double *dkronlist;	      // kron mag errors 
+  double *wkronlist;	      // kron mag errors 
 
   double *psfqf_list;	      // psfqf for all filters
@@ -97,8 +111,29 @@
   double *stargal_list;	      // stargal for all filters
 
-  StatType stats;
+  StatType psfstats;
   StatType apstats;
   StatType kronstats;
 } SetMrelInfo;
+
+typedef struct {
+  float M;
+  float dM;
+  float Xm;
+  int Nsec;
+  unsigned int objID;
+  unsigned int catID;
+  int photcode;
+} MeanMag;
+
+typedef struct {
+  float Mcal;
+  float dMcal;
+  float dMagSys;
+  float Xm;
+  int nFitPhotom;
+  int flags;
+  unsigned int imageID;
+  short ubercalDist;
+} ImageMag;
 
 typedef struct {
@@ -116,4 +151,5 @@
   unsigned int photom_map_id;
   unsigned int flags;
+  int tessID;
   int projID;
   int skycellID;
@@ -149,4 +185,9 @@
 int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
 
+// globals for parallel region operations
+char  *REGION_FILE;
+char  *IMAGE_TABLE;
+int    REGION_HOST_ID;
+
 int          HOST_ID;
 char        *HOSTDIR;
@@ -173,4 +214,6 @@
 int    PARALLEL_MANUAL;
 int    PARALLEL_SERIAL;
+
+int    PARALLEL_REGIONS_MANUAL;
 
 int    NTHREADS;
@@ -260,5 +303,5 @@
 void          InterpolateGrid     PROTO((float *buffer, int Nx, int Ny, Coords *ccd, Coords *gcoords));
 off_t        *SelectRefMosaic     PROTO((Mosaic **refmosaic, off_t *Nimage));
-int           args                PROTO((int argc, char **argv));
+RelphotMode   args                PROTO((int argc, char **argv));
 int           args_client         PROTO((int argc, char **argv));
 int           bcatalog            PROTO((Catalog *subcatalog, Catalog *catalog, int Ncat));
@@ -273,4 +316,8 @@
 void          findImages          PROTO((Catalog *catalog, int Ncatalog, int doImageList));
 int           findMosaics         PROTO((Catalog *catalog, int Ncatalog, int doMosaicList));
+
+void makeMosaics (Image *image, off_t Nimage);
+Mosaic *getMosaicForImage (off_t im);
+void setMosaicCenters (Image *image, off_t Nimage);
 
 void set_db (FITS_DB *in);
@@ -309,5 +356,5 @@
 void          initMosaics         PROTO((Image *subset, off_t Nsubset, Image *image, char *inSubset, off_t Nimage));
 void          initMrel            PROTO((Catalog *catalog, int Ncatalog));
-void          initialize          PROTO((int argc, char **argv));
+RelphotMode   initialize          PROTO((int argc, char **argv));
 void          initialize_client   PROTO((int argc, char **argv));
 void          liststats_setmode   PROTO((StatType *stats, char *strmode));
@@ -397,13 +444,16 @@
 ImageSubset *ImageSubsetLoad(char *filename, off_t *nimage);
 
-int client_logger_init ();
+int client_logger_init (char *dirname);
 int client_logger_message (char *format,...);
 
 int MatchImageName (off_t meas, int cat, char *name);
-int MatchImageSkycellID (off_t meas, int cat, int myProjectionID, int mySkycellID);
-
-int load_tree (char *treefile);
-int BoundaryTreePrimaryCell (char *primaryCellName, double ra, double dec);
-int BoundaryTreePrimaryCellIDs (int *projID, int *skycellID, double ra, double dec);
+int MatchImageSkycellID (off_t meas, int cat, int myTessID, int myProjectionID, int mySkycellID);
+
+int load_tess (char *treefile);
+int get_tess_ids (int *tessID, int *projID, int *skycellID, double ra, double dec);
+int TessellationIDsByImageName (int *tessID, int *projID, int *skycellID, char *name);
+
+// int BoundaryTreePrimaryCell (char *primaryCellName, double ra, double dec);
+// int BoundaryTreePrimaryCellIDs (int *projID, int *skycellID, double ra, double dec);
 
 int print_measure_set_alt (Average *average, SecFilt *secfilt, Measure *measure);
@@ -417,2 +467,44 @@
 int init_synthetic_mags ();
 int add_synthetic_mags (AverageTiny *average, SecFilt *secfilt, MeasureTiny *measure, off_t *Nmeasure, off_t *Nm);
+
+int relphot_parallel_regions ();
+
+int assign_images (FITS_DB *db, RegionHostTable *regionHosts);
+int select_images_hostregion (RegionHostTable *hosts, Image *image, off_t Nimage);
+int find_host_for_coords (RegionHostTable *regionHosts, double R, double d);
+int calculate_image_bounds (Image *image, double *rmin, double *rmax, double *dmin, double *dmax, double Rmid);
+
+int launch_region_hosts (RegionHostTable *regionHosts);
+int strextend (char *input, char *format,...);
+
+Image *ImageTableLoad(char *filename, off_t *nimage);
+int ImageTableSave (char *filename, Image *images, off_t Nimages);
+
+int indexCatalogs (Catalog *catalog, int Ncatalog);
+int catID_and_objID_to_seq (int catID, int objID, int *catSeq, off_t *objSeq);
+
+int check_sync_file (char *filename, int nloop);
+int clear_sync_file (char *filename);
+int update_sync_file (char *filename, int nloop);
+char *make_filename (char *dirname, char *hostname, int hostID, char *tailname);
+
+int share_mean_mags (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop);
+int slurp_mean_mags (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop);
+int set_mean_mags (MeanMag *meanmags, AverageTiny *average, SecFilt *secfilt, int Nsec);
+MeanMag *merge_mean_mags (MeanMag *target, int *ntarget, MeanMag *source, int Nsource);
+
+MeanMag *MeanMagLoad(char *filename, off_t *nmeanmags);
+int MeanMagSave(char *filename, MeanMag *meanmags, off_t Nmeanmags);
+
+int share_image_mags (RegionHostTable *regionHosts, int nloop);
+int slurp_image_mags (RegionHostTable *regionHosts, int nloop);
+int set_image_mags (ImageMag *image_mags, Image *image);
+ImageMag *merge_image_mags (ImageMag *target, int *ntarget, ImageMag *source, int Nsource);
+
+ImageMag *ImageMagLoad(char *filename, off_t *nimage_mags);
+int ImageMagSave(char *filename, ImageMag *image_mags, off_t Nimage_mags);
+
+int markObjects (Catalog *catalog, int Ncatalog);
+
+int relphot_parallel_images ();
+int relphot_parallel_regions ();
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BoundaryTreeOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BoundaryTreeOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BoundaryTreeOps.c	(revision 36680)
@@ -1,88 +1,42 @@
 # include "relphot.h"
 
-// XXX for the moment, only load one boundary tree at a time
-// XXX in fact, only allow RINGS.V3...
+static int Ntess = 0;
+static TessellationTable *tess = NULL;
 
-static BoundaryTree *tree = NULL;
+int TessellationIDsByImageName (int *tessID, int *projID, int *skycellID, char *name) {
 
-int BoundaryTreePrimaryCell (char *primaryCellName, double ra, double dec) {
+  int i;
 
-  int zone, band;
-
-  if (!primaryCellName) return FALSE;
-
-  primaryCellName[0] = 0;
-
-  if (!tree) return FALSE;
-
-  if (!BoundaryTreeCellCoords (tree, &zone, &band, ra, dec)) {
-    fprintf (stderr, "mismatch!\n");
-    return FALSE;
-  }
-  
-# define USE_PROJECTION_CELL 0
-# if (USE_PROJECTION_CELL)
-  snprintf (primaryCellName, DVO_MAX_PATH, "RINGS.V3.%s", tree->name[zone][band]);
-# else
-
-  // I have ra, dec, and the primary projection cell.  In order to choose the primary skycell,
-  // I just need to project to ra,dec to X,Y based on the center of the cell and then get the subdivision right.
-    
-  double x = 0.0;
-  double y = 0.0;
-  BoundaryTreeProjection (&x, &y, ra, dec, tree, zone, band);
-  
-  int xi = x / tree->dX[zone][band];
-  int yi = y / tree->dY[zone][band];
-  int N = xi + tree->NX_SUB * yi;
-  
-  // XXX short-circuit this for now (we should use this code if we make tree have more variable NX,NY values
-  // char format[24], skycellname[128];
-  // int Ndigit = (int)(log10(tree->NX_SUB*tree->NY_SUB)) + 1 ;
-  // snprintf (format, 24, "%s.%%0%dd", tree->name[zone][band], Ndigit);
-  // snprintf (skycellname, 128, format, N);
-
-  snprintf (primaryCellName, DVO_MAX_PATH, "RINGS.V3.%s.%03d", tree->name[zone][band], N);
-# endif
-
-  return TRUE;
-}
-
-int BoundaryTreePrimaryCellIDs (int *projID, int *skycellID, double ra, double dec) {
-
-  int zone, band;
-
+  *tessID = -1;
   *projID = -1;
   *skycellID = -1;
 
-  if (!tree) return FALSE;
+  if (!tess) return FALSE;
 
-  if (!BoundaryTreeCellCoords (tree, &zone, &band, ra, dec)) {
-    fprintf (stderr, "mismatch!\n");
-    return FALSE;
+  for (i = 0; i < Ntess; i++) {
+    // do this with a strhash of some kind?
+    if (!strncmp (name, tess[i].basename, tess[i].Nbasename)) {
+      *tessID    = i;
+      if (tess[i].projectIDoff >= 0) {
+	*projID    = atoi(&name[tess[i].projectIDoff]);
+      } else {
+	*projID    = 0;
+      }
+      if (tess[i].skycellIDoff >= 0) {
+	*skycellID = atoi(&name[tess[i].skycellIDoff]);
+      } else {
+	*skycellID = 0;
+      }
+      return TRUE;
+    }
   }
-  
-  // I have ra, dec, and the primary projection cell.  In order to choose the primary skycell,
-  // I just need to project to ra,dec to X,Y based on the center of the cell and then get the subdivision right.
-    
-  double x = 0.0;
-  double y = 0.0;
-  BoundaryTreeProjection (&x, &y, ra, dec, tree, zone, band);
-  
-  int xi = x / tree->dX[zone][band];
-  int yi = y / tree->dY[zone][band];
-  int N = xi + tree->NX_SUB * yi;
-  
-  *projID = tree->projID[zone][band];
-  *skycellID = N;
-
-  return TRUE;
+  return FALSE;
 }
 
-int load_tree (char *treefile) {
+int load_tess (char *tessfile) {
 
-  tree = BoundaryTreeLoad (treefile);
-  if (!tree) {
-    fprintf (stderr, "failed to load boundary tree %s\n", treefile);
+  tess = TessellationTableLoad (tessfile, &Ntess);
+  if (!tess) {
+    fprintf (stderr, "failed to load tessellation boundary file %s\n", tessfile);
     exit (2);
   }
@@ -90,2 +44,12 @@
   return TRUE;
 }
+
+int get_tess_ids (int *tessID, int *projID, int *skycellID, double ra, double dec) {
+
+  int status;
+
+  if (!tess) return FALSE;
+
+  status = TessellationPrimaryCellIDs(tess, Ntess, tessID, projID, skycellID, ra, dec);
+  return status;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BrightCatalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BrightCatalog.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/BrightCatalog.c	(revision 36680)
@@ -101,4 +101,5 @@
       measure[i].catID     = catID[i];
       measure[i].photcode  = photcode[i];
+      measure[i].myDet     = FALSE;
     }
     fprintf (stderr, "loaded data for %lld measure\n", (long long) Nrow);
@@ -141,4 +142,5 @@
     GET_COLUMN(flags,         "FLAGS",       int);
     GET_COLUMN(catID,         "CAT_ID",      int);
+    GET_COLUMN(objID,         "OBJ_ID",      int);
     gfits_free_header (&theader);
     gfits_free_table  (&ftable);
@@ -153,4 +155,6 @@
       average[i].flags          = flags[i];
       average[i].catID          = catID[i];
+      average[i].objID          = objID[i];
+      average[i].nOwn           = 0;
     }
     fprintf (stderr, "loaded data for %lld average\n", (long long) Nrow);
@@ -162,4 +166,5 @@
     free (flags         );
     free (catID         );
+    free (objID         );
 
     catalog->average = average;
@@ -370,4 +375,5 @@
     gfits_define_bintable_column (&theader, "J", "FLAGS",       "flags",                  NULL,    1.0, 0.0);
     gfits_define_bintable_column (&theader, "J", "CAT_ID",      "catalog ref",            NULL,    1.0, 0.0);
+    gfits_define_bintable_column (&theader, "J", "OBJ_ID",      "object ref",             NULL,    1.0, 0.0);
 
     // generate the output array that carries the data
@@ -381,4 +387,5 @@
     int   *flags          ; ALLOCATE (flags,         int,    catalog->Naverage);
     int   *catID          ; ALLOCATE (catID,         int,    catalog->Naverage);
+    int   *objID          ; ALLOCATE (objID,         int,    catalog->Naverage);
 
     // assign the storage arrays
@@ -391,4 +398,5 @@
       flags[i]          = average[i].flags;
       catID[i]          = average[i].catID;
+      objID[i]          = average[i].objID;
     }
 
@@ -400,4 +408,5 @@
     gfits_set_bintable_column (&theader, &ftable, "FLAGS",       flags,         catalog->Naverage);
     gfits_set_bintable_column (&theader, &ftable, "CAT_ID",      catID,         catalog->Naverage);
+    gfits_set_bintable_column (&theader, &ftable, "OBJ_ID",      objID,         catalog->Naverage);
 
     free (R             );
@@ -407,4 +416,5 @@
     free (flags         );
     free (catID         );
+    free (objID         );
 
     gfits_fwrite_Theader (f, &theader);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageMagIO.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageMagIO.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageMagIO.c	(revision 36680)
@@ -0,0 +1,221 @@
+# include "relphot.h"
+
+# define GET_COLUMN(OUT,NAME,TYPE) \
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+// this is nearly identical to the one in 'uniphot/src' used by setphot / setphot_client.
+// Here, we use a handful of different columns (if not, we could move to libdvo)
+ImageMag *ImageMagLoad(char *filename, off_t *nimage_mags) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  *nimage_mags = 0;
+  ImageMag *image_mags = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return NULL;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) {
+    fclose (f);
+    return (NULL);
+  }
+  fclose (f);
+
+  // we read the entire block of data, then extract the columns, then set the image structure values.
+  // I free the FITS table data after extracting the colums to avoid having 3 copies in memory.
+
+  char type[16];
+
+  GET_COLUMN (Mcal,              "MCAL",           float);
+  GET_COLUMN (dMcal,             "MCAL_ERR",       float);
+  GET_COLUMN (dMagSys,           "MCAL_SYSERR",    float);
+  GET_COLUMN (Xm,                "CHISQ",          float);
+  GET_COLUMN (nFitPhotom,        "NFIT",             int);
+  GET_COLUMN (flags,             "FLAGS",            int);
+  GET_COLUMN (ubercalDist,       "UDIST",            int);
+  GET_COLUMN (imageID,           "ID",               int);
+
+  // free the memory associated with the FITS files
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  ALLOCATE (image_mags, ImageMag, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    image_mags[i].Mcal                      = Mcal       [i];
+    image_mags[i].dMcal                     = dMcal      [i];
+    image_mags[i].dMagSys                   = dMagSys    [i];
+    image_mags[i].Xm                        = Xm         [i];
+    image_mags[i].nFitPhotom                = nFitPhotom [i];
+    image_mags[i].flags                     = flags      [i];
+    image_mags[i].ubercalDist               = ubercalDist[i];
+    image_mags[i].imageID                   = imageID    [i];
+  }
+  fprintf (stderr, "loaded data for %lld images\n", (long long) Nrow);
+
+  free (Mcal       );
+  free (dMcal      );
+  free (dMagSys    );
+  free (Xm         );
+  free (nFitPhotom );
+  free (flags      );
+  free (ubercalDist);
+  free (imageID    );
+
+  *nimage_mags = Nrow;
+  return image_mags;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)                                   \
+  if (!(STATUS)) {                                                      \
+    fprintf (stderr, MSG, __VA_ARGS__);                                 \
+    return FALSE;                                                       \
+  }
+
+int ImageMagSave(char *filename, ImageMag *image_mags, off_t Nimage_mags) {
+
+  int i;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_create_table_header (&theader, "BINTABLE", "IMAGE_MAGS");
+
+  gfits_define_bintable_column (&theader, "E", "MCAL",           "cal offset", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "MCAL_ERR",       "cal error", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "MCAL_SYSERR",    "systematic error", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "CHISQ",          "cal chisq", "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "NFIT",           "number of fitted stars", "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "FLAGS",          "analysis flags", "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "UDIST",          "distance to ubercal images", "images", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "ID",             "image ID", "unitless", 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  float *Mcal        ;
+  float *dMcal       ;
+  float *dMagSys     ;
+  float *Xm          ;
+  int   *nFitPhotom  ;
+  int   *flags       ;
+  int   *ubercalDist ;
+  int   *imageID     ;
+
+  // create intermediate storage arrays
+  ALLOCATE (Mcal        ,         float,          Nimage_mags);
+  ALLOCATE (dMcal       ,         float,          Nimage_mags);
+  ALLOCATE (dMagSys     ,         float,          Nimage_mags);
+  ALLOCATE (Xm          ,         float,          Nimage_mags);
+  ALLOCATE (nFitPhotom  ,           int,          Nimage_mags);
+  ALLOCATE (flags       ,           int,          Nimage_mags);
+  ALLOCATE (ubercalDist ,           int,          Nimage_mags);
+  ALLOCATE (imageID     ,           int,          Nimage_mags);
+
+  // assign the storage arrays
+  for (i = 0; i < Nimage_mags; i++) {
+    Mcal       [i]   = image_mags[i].Mcal       ;
+    dMcal      [i]   = image_mags[i].dMcal      ;
+    dMagSys    [i]   = image_mags[i].dMagSys    ;
+    Xm         [i]   = image_mags[i].Xm         ;
+    nFitPhotom [i]   = image_mags[i].nFitPhotom ;
+    flags      [i]   = image_mags[i].flags      ;
+    ubercalDist[i]   = image_mags[i].ubercalDist;
+    imageID    [i]   = image_mags[i].imageID    ;
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "MCAL",           Mcal       ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "MCAL_ERR",       dMcal      ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "MCAL_SYSERR",    dMagSys    ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "CHISQ",          Xm         ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "NFIT",           nFitPhotom ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "FLAGS",          flags      ,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "UDIST",          ubercalDist,         Nimage_mags);
+  gfits_set_bintable_column (&theader, &ftable, "ID",             imageID    ,         Nimage_mags);
+
+  free (Mcal       );
+  free (dMcal      );
+  free (dMagSys    );
+  free (Xm         );
+  free (nFitPhotom );
+  free (flags      );
+  free (ubercalDist);
+  free (imageID    );
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image_mags file for output %s\n", filename);
+    return FALSE;
+  }
+
+  int status;
+  status = gfits_fwrite_header  (f, &header);
+  CHECK_STATUS (status, "ERROR: cannot write header for image_mags %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for image_mags %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for image_mags %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for image_mags %s\n", filename);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  int fd = fileno (f);
+
+  status = fflush (f);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image_mags %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot flush file image_mags %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing image_mags file %s\n", filename);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageOps.c	(revision 36680)
@@ -48,4 +48,5 @@
 // stack image.  for now, we generate these ID arrays based on the image names when we load in the image table (initImages).
 // When we pass data to the remote clients via the ImageSubset, the projID/skycellID values are carried directly in the table.
+int *tessID    = NULL;
 int *projectID = NULL;
 int *skycellID = NULL;
@@ -81,4 +82,5 @@
 
   // for stack images, assign projection cell ID and skycell ID based on filenames
+  ALLOCATE (tessID,      int, Nimage);
   ALLOCATE (projectID,   int, Nimage);
   ALLOCATE (skycellID,   int, Nimage);
@@ -87,13 +89,14 @@
     imageIdx[i] = i;
     imageIDs[i] = image[i].imageID;
+
+    tessID[i]    = -1;
     projectID[i] = -1;
     skycellID[i] = -1;
-    if (!strncmp (image[i].name, "RINGS.V3.skycell", strlen("RINGS.V3.skycell"))) {
-      projectID[i] = atoi(&image[i].name[17]);
-      skycellID[i] = atoi(&image[i].name[22]);
-    }
+
+    TessellationIDsByImageName (&tessID[i], &projectID[i], &skycellID[i], image[i].name);
   }
 
   // sort the image index by the IDs
+  // XXX does this break the imageID <-> projectID, etc match?
   llsortpair (imageIDs, imageIdx, Nimage);
 }
@@ -106,4 +109,6 @@
   // create full a Image array and save the needed values
   ALLOCATE (image, Image, N);
+
+  ALLOCATE (tessID,    int, N);
   ALLOCATE (projectID, int, N);
   ALLOCATE (skycellID, int, N);
@@ -118,4 +123,5 @@
     image[i].trate         = input[i].trate        ;
     image[i].ubercalDist   = input[i].ubercalDist  ;
+    tessID[i]              = input[i].tessID       ;
     projectID[i]           = input[i].projID       ;
     skycellID[i]           = input[i].skycellID    ;
@@ -153,4 +159,5 @@
     subset[i].trate         = image[i].trate        ;
     subset[i].ubercalDist   = image[i].ubercalDist  ;
+    subset[i].tessID        = tessID[i];
     subset[i].projID        = projectID[i];
     subset[i].skycellID     = skycellID[i];
@@ -237,7 +244,11 @@
   
   int Nmatch = 0;
- for (i = 0; i < Ncatalog; i++) {
+  for (i = 0; i < Ncatalog; i++) {
     for (j = 0; j < catalog[i].Nmeasure; j++) {
+      catalog[i].measureT[j].myDet = FALSE; // a detetion is not mine until proven otherwise
       ecode = GetPhotcodeEquivCodebyCode (catalog[i].measureT[j].photcode);
+
+      // skip measurements which do not match one of the requested photcodes (
+      // (do we not already exclude in bcatalog -- maybe needed for reload_objects?
       found = FALSE;
       for (Ns = 0; !found && (Ns < Nphotcodes); Ns++) {
@@ -245,4 +256,6 @@
       }
       if (!found) continue; 
+
+      // if we match one of our images, myDet gets set to TRUE
       matchImage (catalog, j, i, doImageList);
       Nmatch ++;
@@ -315,4 +328,5 @@
     return;
   }
+  catalog[cat].measureT[meas].myDet = TRUE;
 
   if (USE_GRID) {
@@ -461,5 +475,5 @@
 
 // returns image.Mcal - ff(x,y)
-int MatchImageSkycellID (off_t meas, int cat, int myProjectionID, int mySkycellID) {
+int MatchImageSkycellID (off_t meas, int cat, int myTessID, int myProjectionID, int mySkycellID) {
 
   off_t i;
@@ -470,7 +484,9 @@
   if (i == -1) return FALSE;
 
+  if (tessID[i]    == -1) return FALSE;
   if (projectID[i] == -1) return FALSE;
   if (skycellID[i] == -1) return FALSE;
 
+  if (tessID[i]    != myTessID) return FALSE;
   if (projectID[i] != myProjectionID) return FALSE;
   if (skycellID[i] != mySkycellID) return FALSE;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageSubset.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageSubset.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageSubset.c	(revision 36680)
@@ -63,4 +63,5 @@
   GET_COLUMN (map,     	 "PHOTOM_MAP",   int);
   GET_COLUMN (flags,   	 "FLAGS",        int);
+  GET_COLUMN (tessID,  	 "TESS_ID",      int);
   GET_COLUMN (projID,  	 "PROJ_ID",      int);
   GET_COLUMN (skycellID, "SKYCELL_ID",   int);
@@ -78,4 +79,5 @@
     image[i].photom_map_id = map[i];
     image[i].flags         = flags[i];
+    image[i].tessID        = tessID[i];
     image[i].projID        = projID[i];
     image[i].skycellID     = skycellID[i];
@@ -91,4 +93,5 @@
   free (map);
   free (flags);
+  free (tessID);
   free (projID);
   free (skycellID);
@@ -130,4 +133,5 @@
   gfits_define_bintable_column (&theader, "J", "FLAGS", "flags", NULL, 1.0, 1.0*0x8000);
 
+  gfits_define_bintable_column (&theader, "J", "TESS_ID", "ID", NULL, 1.0, 0.0);
   gfits_define_bintable_column (&theader, "J", "PROJ_ID", "ID", NULL, 1.0, 0.0);
   gfits_define_bintable_column (&theader, "J", "SKYCELL_ID", "ID", NULL, 1.0, 0.0);
@@ -143,5 +147,5 @@
   float *Mcal, *dMcal;
   unsigned int *imageID, *map, *flags, *tzero;
-  int *projID, *skycellID;
+  int *tessID, *projID, *skycellID;
   unsigned short *trate;
   short *ucdist;
@@ -153,4 +157,5 @@
   ALLOCATE (map,       unsigned int,   Nimage);
   ALLOCATE (flags,     unsigned int,   Nimage);
+  ALLOCATE (tessID,    int,            Nimage);
   ALLOCATE (projID,    int,            Nimage);
   ALLOCATE (skycellID, int,            Nimage);
@@ -169,4 +174,5 @@
     trate[i]     = image[i].trate;
     ucdist[i]    = image[i].ubercalDist;
+    tessID[i]    = image[i].tessID;
     projID[i]    = image[i].projID;
     skycellID[i] = image[i].skycellID;
@@ -179,4 +185,5 @@
   gfits_set_bintable_column (&theader, &ftable, "PHOTOM_MAP",   map,       Nimage);
   gfits_set_bintable_column (&theader, &ftable, "FLAGS",        flags,     Nimage);
+  gfits_set_bintable_column (&theader, &ftable, "TESS_ID",      tessID,    Nimage);
   gfits_set_bintable_column (&theader, &ftable, "PROJ_ID",      projID,    Nimage);
   gfits_set_bintable_column (&theader, &ftable, "SKYCELL_ID",   skycellID, Nimage);
@@ -190,4 +197,5 @@
   free (map);
   free (flags);
+  free (tessID);
   free (projID);
   free (skycellID);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageTable.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageTable.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/ImageTable.c	(revision 36680)
@@ -0,0 +1,62 @@
+# include "relphot.h"
+
+Image *ImageTableLoad(char *filename, off_t *nimage) {
+
+  int status;
+  off_t Nimage;
+  FITS_DB db;
+
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  
+  /* lock and load the image db table */
+  status = dvo_image_lock (&db, filename, 60.0, LCK_SOFT);
+  if (!status) {
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+
+  // if the file is missing, db.dbstate will have a value of either:
+  // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    Shutdown ("ERROR: database %s contains no image data", CATDIR);
+  }
+
+  // read data from Image.dat file
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+
+  // convert database table to internal structure (binary to Image)
+  // 'image' points to the same memory as db->ftable->buffer
+  Image *image = gfits_table_get_Image (&db.ftable, &Nimage, &db.swapped);
+  if (!image) {
+    Shutdown ("ERROR: failed to read images");
+  }
+
+  *nimage = Nimage;
+  return image;
+}
+
+int ImageTableSave (char *filename, Image *images, off_t Nimages) {
+
+  int status;
+  FITS_DB db;
+
+  // setup image table format and lock 
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+  status    = dvo_image_lock (&db, filename, 60.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+
+  // load or create the image table 
+  if (db.dbstate != LCK_EMPTY) {
+    Shutdown ("image catalog already exists %s", db.filename);
+  }
+
+  dvo_image_create (&db, GetZeroPoint());
+
+  /* add the new images and save */
+  dvo_image_addrows (&db, images, Nimages);
+  dvo_image_update (&db, VERBOSE);
+  dvo_image_unlock (&db);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MeanMagIO.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MeanMagIO.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MeanMagIO.c	(revision 36680)
@@ -0,0 +1,200 @@
+# include "relphot.h"
+
+# define GET_COLUMN(OUT,NAME,TYPE) \
+  TYPE *OUT = gfits_get_bintable_column_data (&theader, &ftable, NAME, type, &Nrow, &Ncol); \
+  myAssert (!strcmp(type, #TYPE), "wrong column type");
+
+// this is nearly identical to the one in 'uniphot/src' used by setphot / setphot_client.
+// Here, we use a handful of different columns (if not, we could move to libdvo)
+MeanMag *MeanMagLoad(char *filename, off_t *nmeanmags) {
+
+  int i, Ncol;
+  off_t Nrow;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  *nmeanmags = 0;
+  MeanMag *meanmags = NULL;
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open image subset file %s\n", filename);
+    return NULL;
+  }
+
+  /* load in PHU segment (ignore) */
+  if (!gfits_fread_header (f, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset header\n");
+    fclose (f);
+    return NULL;
+  }
+  if (!gfits_fread_matrix (f, &matrix, &header)) {
+    if (VERBOSE) fprintf (stderr, "can't read image subset matrix\n");
+    gfits_free_header (&header);
+    fclose (f);
+    return NULL;
+  }
+
+  ftable.header = &theader;
+
+  // load data for this header 
+  if (!gfits_load_header (f, &theader)) {
+    fclose (f);
+    return NULL;
+  }
+
+  // read the fits table bytes
+  if (!gfits_fread_ftable_data (f, &ftable, FALSE)) {
+    fclose (f);
+    return (NULL);
+  }
+  fclose (f);
+
+  // a bit annoying : we read the entire block of data, then extract the columns, then set the image structure values.
+  // this means I need 3 copies in memory at some point.  ugh.
+
+  char type[16];
+
+  GET_COLUMN (M,    	 "MAG",          float);
+  GET_COLUMN (dM,   	 "MAG_ERR",      float);
+  GET_COLUMN (Xm,   	 "MAG_CHISQ",    float);
+  GET_COLUMN (Nsec, 	 "NSEC",         int);
+  GET_COLUMN (objID, 	 "OBJ_ID",       int);
+  GET_COLUMN (catID, 	 "CAT_ID",       int);
+
+  // free the memory associated with the FITS files
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  ALLOCATE (meanmags, MeanMag, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    meanmags[i].M              = M    [i];
+    meanmags[i].dM             = dM   [i];
+    meanmags[i].Xm             = Xm   [i];
+    meanmags[i].Nsec           = Nsec [i];
+    meanmags[i].objID          = objID[i];
+    meanmags[i].catID          = catID[i];
+  }
+  fprintf (stderr, "loaded data for %lld objects (* filters)\n", (long long) Nrow);
+
+  free (M    );
+  free (dM   );
+  free (Xm   );
+  free (Nsec );
+  free (objID);
+  free (catID);
+
+  *nmeanmags = Nrow;
+  return meanmags;
+}
+
+// STATUS is value expected for success
+# define CHECK_STATUS(STATUS,MSG,...)					\
+  if (!(STATUS)) {							\
+    fprintf (stderr, MSG, __VA_ARGS__);					\
+    return FALSE;							\
+  }
+
+int MeanMagSave(char *filename, MeanMag *meanmags, off_t Nmeanmags) {
+
+  int i;
+  Header header;
+  Header theader;
+  Matrix matrix;
+  FTable ftable;
+
+  gfits_init_header (&header);
+  header.extend = TRUE;
+  gfits_create_header (&header);
+  gfits_create_matrix (&header, &matrix);
+
+  gfits_create_table_header (&theader, "BINTABLE", "MEANMAGS");
+
+  gfits_define_bintable_column (&theader, "E", "MAG",       "mean magnitude", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "MAG_ERR",   "mean magnitude error", "magnitudes", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "E", "MAG_CHISQ", "mean magnitude chisq", "unitless", 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "J", "NSEC",      "secfilt sequence", NULL, 1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "J", "OBJ_ID",    "object ID",        NULL, 1.0, 1.0*0x8000);
+  gfits_define_bintable_column (&theader, "J", "CAT_ID",    "catalog ID",       NULL, 1.0, 1.0*0x8000);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  float *M, *dM, *Xm;
+  int *Nsec;
+  unsigned int *objID, *catID;
+
+  // create intermediate storage arrays
+  ALLOCATE (M,         float, 	       Nmeanmags);
+  ALLOCATE (dM,        float, 	       Nmeanmags);
+  ALLOCATE (Xm,        float, 	       Nmeanmags);
+  ALLOCATE (Nsec,      int,            Nmeanmags);
+  ALLOCATE (objID,     unsigned int,   Nmeanmags);
+  ALLOCATE (catID,     unsigned int,   Nmeanmags);
+
+  // assign the storage arrays
+  for (i = 0; i < Nmeanmags; i++) {
+    M    [i]   = meanmags[i].M    ;
+    dM   [i]   = meanmags[i].dM   ;
+    Xm   [i]   = meanmags[i].Xm   ;
+    Nsec [i]   = meanmags[i].Nsec ;
+    objID[i]   = meanmags[i].objID;
+    catID[i]   = meanmags[i].catID;
+  }
+
+  // add the columns to the output array
+  gfits_set_bintable_column (&theader, &ftable, "MAG",       M,         Nmeanmags);
+  gfits_set_bintable_column (&theader, &ftable, "MAG_ERR",   dM,        Nmeanmags);
+  gfits_set_bintable_column (&theader, &ftable, "MAG_CHISQ", Xm,        Nmeanmags);
+  gfits_set_bintable_column (&theader, &ftable, "NSEC",      Nsec,      Nmeanmags);
+  gfits_set_bintable_column (&theader, &ftable, "OBJ_ID",    objID,     Nmeanmags);
+  gfits_set_bintable_column (&theader, &ftable, "CAT_ID",    catID,     Nmeanmags);
+
+  free (M    );
+  free (dM   );
+  free (Xm   );
+  free (Nsec );
+  free (objID);
+  free (catID);
+
+  FILE *f = fopen (filename, "w");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open meanmag file for output %s\n", filename);
+    return FALSE;
+  }
+
+  int status;
+  status = gfits_fwrite_header  (f, &header);
+  CHECK_STATUS (status, "ERROR: cannot write header for meanmags %s\n", filename);
+
+  status = gfits_fwrite_matrix  (f, &matrix);
+  CHECK_STATUS (status, "ERROR: cannot write matrix for meanmags %s\n", filename);
+
+  status = gfits_fwrite_Theader (f, &theader);
+  CHECK_STATUS (status, "ERROR: cannot write table header for meanmags %s\n", filename);
+
+  status = gfits_fwrite_table  (f, &ftable);
+  CHECK_STATUS (status, "ERROR: cannot write table data for meanmags %s\n", filename);
+
+  gfits_free_header (&header);
+  gfits_free_matrix (&matrix);
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  int fd = fileno (f);
+
+  status = fflush (f);
+  CHECK_STATUS (!status, "ERROR: cannot flush file meanmags %s\n", filename);
+
+  status = fsync (fd);
+  CHECK_STATUS (!status, "ERROR: cannot flush file meanmags %s\n", filename);
+
+  status = fclose (f);
+  CHECK_STATUS (!status, "ERROR: problem closing meanmags file %s\n", filename);
+
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MosaicOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MosaicOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/MosaicOps.c	(revision 36680)
@@ -320,4 +320,157 @@
 }
 
+/* find mosaic frames (unique time periods) (NOTE : we do NOT require matching photcodes but we 
+   match images by MOSAICNAME.  this last point is weak: it forces a single camera at a time.
+   we can extend the logic to multiple cameras if we make list of MOSAICNAMES (better to assign a camera ID)
+ */
+void makeMosaics (Image *image, off_t Nimage) {
+
+  off_t i, j, status, found, NMOSAIC, *MosaicN_IMAGE;
+  unsigned int start, stop, *startTimes, *startTimesMosaic;
+  char *pname;
+
+  if (!MOSAIC_ZEROPT) return;
+
+  INITTIME;
+
+  /* a 'mosaic' in relphot is (unlike relastro) a virtual concept: there is no 
+   * entry in the image table that represents this mosaic.  Instead, it is an
+   * internal construct that defines a group of related images 
+   */
+
+  // generate a list of all image start times
+  ALLOCATE (startTimes, unsigned int, Nimage);
+  for (i = 0; i < Nimage; i++) {
+    startTimes[i] = image[i].tzero;
+  }
+  sort_times (startTimes, Nimage);
+  MARKTIME("create array of all image obstimes: %f sec\n", dtime);
+  
+  Nmosaic = 0;
+  NMOSAIC = 1000;
+  ALLOCATE (startTimesMosaic, unsigned int, NMOSAIC);
+  startTimesMosaic[0] = startTimes[0];
+
+  // generate a list of the unique start times (these define the mosaics)
+  for (i = 0; i < Nimage; i++) {
+    if (startTimes[i] < startTimesMosaic[Nmosaic]) {
+      fprintf (stderr, "error?\n");
+      abort();
+    }
+    if (startTimes[i] == startTimesMosaic[Nmosaic]) continue;
+    Nmosaic ++;
+    if (Nmosaic >= NMOSAIC) {
+      NMOSAIC += 1000;
+      REALLOCATE (startTimesMosaic, unsigned int, NMOSAIC);
+    }
+    startTimesMosaic[Nmosaic] = startTimes[i];
+  }
+  Nmosaic ++;
+  MARKTIME("create array of mosaic obstimes: %f sec\n", dtime);
+
+  // now I have a list of uniq start times, and they are in order
+  // create the mosaic arrays for these times
+  ALLOCATE (mosaic, Mosaic, Nmosaic);
+
+  ALLOCATE (MosaicToImage, off_t *, Nmosaic);
+  ALLOCATE (MosaicN_Image, off_t,   Nmosaic);
+  ALLOCATE (MosaicN_IMAGE, off_t,   Nmosaic);
+
+  // init the mosaic array values
+  for (i = 0; i < Nmosaic; i++) {
+    mosaic[i].start = startTimesMosaic[i];
+    mosaic[i].stop  = 0;
+    mosaic[i].Mcal  = 0.0;
+    mosaic[i].dMcal = 0.0;
+    mosaic[i].dMsys = 0.0;
+    mosaic[i].Xm    = 0.0;
+    mosaic[i].flags = 0;
+    mosaic[i].secz  = NAN;
+    mosaic[i].photcode = 0;
+    mosaic[i].skipCal = FALSE;
+    
+    memset (&mosaic[i].coords, 0, sizeof(Coords));
+
+    MosaicN_IMAGE[i] = 10;
+    MosaicN_Image[i] = 0;
+    ALLOCATE (MosaicToImage[i], off_t, MosaicN_IMAGE[i]);
+    MosaicToImage[i][0] = -1;
+  }
+
+  ALLOCATE (ImageToMosaic, off_t, Nimage); // mosaic to which image belongs
+
+  // assign each image to a mosaic
+  for (i = 0; i < Nimage; i++) {
+    ImageToMosaic[i] = -1;
+
+    /* select valid mosaic images by photcode */
+    pname = GetPhotcodeNamebyCode (image[i].photcode);
+    if (!pname) continue;
+
+    status = strncmp (pname, MOSAICNAME, strlen (MOSAICNAME));
+    if (status) continue;
+
+    start = image[i].tzero;
+    stop  = image[i].tzero + MAX(1.01*image[i].trate*image[i].NY, 1);
+
+    j = findMosaic(startTimesMosaic, Nmosaic, start);
+    if (j == -1) {
+      fprintf (stderr, "programming error? all image images should belong to a mosaic\n");
+      abort();
+    }
+
+    // add reference from image to mosaic
+    ImageToMosaic[i] = j;
+
+    // have we already found this mosaic?
+    found = (MosaicN_Image[j] > 0);
+
+    /* add image to mosaic image list */
+    MosaicToImage[j][MosaicN_Image[j]] = i;
+    MosaicN_Image[j] ++;
+    if (MosaicN_Image[j] == MosaicN_IMAGE[j]) {
+      MosaicN_IMAGE[j] += 10;
+      REALLOCATE (MosaicToImage[j], off_t, MosaicN_IMAGE[j]);
+    }
+    if (found) continue;
+    
+    /* a new mosaic, define ranges */
+    if (mosaic[j].start != start) { 
+      fprintf (stderr, "error?\n");
+      abort();
+    }
+    mosaic[j].stop  = stop;
+    mosaic[j].Mcal  = 0.0;
+    mosaic[j].dMcal = 0.0;
+    mosaic[j].Xm    = 0.0;
+    mosaic[j].dMsys = image[i].flags;
+    mosaic[j].flags = image[i].flags;
+    mosaic[j].secz  = image[i].secz;
+    mosaic[j].photcode = GetPhotcodeEquivCodebyCode (image[i].photcode);
+  }
+  MARKTIME("assign images to mosaic: %f sec\n", dtime);
+
+  // free this or not?
+  free (MosaicN_IMAGE);
+  free (startTimes);
+  free (startTimesMosaic);
+
+  fprintf (stderr, "matched %d images to %d mosaics\n", (int) Nimage, (int) Nmosaic);
+
+  return;
+}
+
+Mosaic *getMosaicForImage (off_t im) {
+
+  if (im < 0) return NULL;
+  if (!ImageToMosaic) return NULL;
+
+  off_t m = ImageToMosaic[im];
+  if (m < 0) return NULL;
+  if (m >= Nmosaic) return NULL;
+
+  return (&mosaic[m]);
+}
+
 // use bisection to find the overlapping mosaic (returns exact match)
 // startTimes is a sorted, unique list of times
@@ -350,4 +503,123 @@
   }
   return (-1);
+}
+
+void setMosaicCenters (Image *image, off_t Nimage) {
+
+  /* find max dR, dD range for all mosaics */
+  /* define mosaic.coords to cover dR, dD */
+  /* send results to initGridBins */
+
+  off_t i, j, m, NX, NY, NC, Nc;
+  double R, D, Rmid, Dmid;
+  double Mcal, dMcal, Xm;
+  double *Rc, *Dc;
+
+  NC = 100;
+  ALLOCATE (Rc, double, NC);
+  ALLOCATE (Dc, double, NC);
+
+  for (i = 0; i < Nmosaic; i++) {
+    Nc = 0;
+    Rmid = Dmid = NAN;
+    Mcal = dMcal = Xm = 0;
+    for (j = 0; j < MosaicN_Image[i]; j++) {
+      m = MosaicToImage[i][j];
+
+      if (!FindMosaicForImage (image, Nimage, m)) {
+	if (VERBOSE2) fprintf (stderr, "cannot find mosaic for "OFF_T_FMT"\n", i);
+	continue;
+      }
+
+      NX = image[m].NX;
+      NY = image[m].NY;
+      XY_to_RD (&R, &D, 0.5*NX, 0.5*NY, &image[m].coords);
+      R = ohana_normalize_angle_to_midpoint (R, 180.0);
+
+      // Exclude images with crazy astrometry
+      // XXX NOTE : this is gpc1-specific
+      { 
+	double dP1 = hypot(image[m].coords.pc1_1, image[m].coords.pc1_2);
+	double dP2 = hypot(image[m].coords.pc2_1, image[m].coords.pc2_2);
+	if (fabs(dP1 - 1.0) > 0.02) continue;
+	if (fabs(dP2 - 1.0) > 0.02) continue;
+
+	double X00, Y00, X10, Y10, X01, Y01;
+	XY_to_LM (&X00, &Y00, 0.0, 0.0, &image[m].coords);
+	XY_to_LM (&X10, &Y10, image[m].NX, 0.0, &image[m].coords);
+	XY_to_LM (&X01, &Y01, 0.0, image[m].NY, &image[m].coords);
+	double dS0 = hypot ((X00 - X10), (Y00 - Y10));
+	double dS1 = hypot ((X00 - X01), (Y00 - Y01));
+	if (dS0 > 6000) continue;
+	if (dS1 > 6500) continue;
+      }	
+
+      Rc[Nc] = R;
+      Dc[Nc] = D;
+      Nc ++;
+      if (Nc >= NC) {
+	NC += 100;
+	REALLOCATE (Rc, double, NC);
+	REALLOCATE (Dc, double, NC);
+      }
+
+      Mcal  += image[m].Mcal;
+      dMcal += image[m].dMcal;
+      Xm    += image[m].Xm;
+
+      // for ubercal images, we (elsewhere) keep Mcal frozen
+
+      /* we are using mosaic.Mcal, not image.Mcal. reset image.Mcal */
+      image[m].Mcal  = 0.0;
+      image[m].dMcal = NAN;
+      image[m].Xm    = NAN_S_SHORT;
+    }
+
+    if (Nc > 0) {
+      dsort (Rc, Nc);
+      if (Rc[Nc-1] - Rc[0] > 180.0) {
+	// in our list, Rc is in the range 0.0 to 360.0.  
+	// any mosaic which is close to the 0.0, 360.0 boundary may have some on
+	// one side or the other.  count how many have values more than Rc[0] + 180.
+	// if more than half are at the large end, re-normalize to that range
+	int Nbig = 0;
+	for (j = 1; j < Nc; j++) {
+	  if (Rc[j] - Rc[0] > 180.0) Nbig ++;
+	}
+	if (Nbig  > 0.5*Nc) {
+	  for (j = 0; j < Nc; j++) {
+	    Rc[j] = ohana_normalize_angle_to_midpoint (Rc[j], 360.0);
+	  }
+	  dsort (Rc, Nc);
+	} else if (Nbig > 0) {
+	  for (j = 0; j < Nc; j++) {
+	    Rc[j] = ohana_normalize_angle_to_midpoint (Rc[j], 0.0);
+	  }
+	  dsort (Rc, Nc);
+	}
+      }
+      dsort (Dc, Nc);
+
+      Rmid = Rc[(int)(0.5*Nc)];
+      Dmid = Dc[(int)(0.5*Nc)];
+    }
+
+    strcpy (mosaic[i].coords.ctype, "DEC--TAN");
+    mosaic[i].coords.crval1 = Rmid;
+    mosaic[i].coords.crval2 = Dmid;
+    mosaic[i].coords.crpix1 = 0.0;
+    mosaic[i].coords.crpix2 = 0.0;
+    mosaic[i].coords.cdelt1 = 1.0 / 3600.0;
+    mosaic[i].coords.cdelt2 = 1.0 / 3600.0;
+    mosaic[i].coords.pc1_1  = 1.0;
+    mosaic[i].coords.pc2_2  = 1.0;
+    mosaic[i].coords.pc1_2  = 0.0;
+    mosaic[i].coords.pc2_1  = 0.0;
+
+    mosaic[i].Mcal  = Mcal / MosaicN_Image[i];
+    mosaic[i].dMcal = dMcal / MosaicN_Image[i];
+    mosaic[i].Xm    = Xm / MosaicN_Image[i];
+  }
+  return;
 }
 
@@ -412,5 +684,5 @@
     }
     dS /= MosaicN_Image[i];
-    strcpy (mosaic[i].coords.ctype, "RA---TAN");
+    strcpy (mosaic[i].coords.ctype, "DEC--TAN");
     mosaic[i].coords.crval1 = Rmin;
     mosaic[i].coords.crval2 = Dmin;
@@ -527,8 +799,11 @@
 
   if (!MOSAIC_ZEROPT) return (FALSE);
+  // if we are calibrating by mosaic, redefine myDet == on one of my mosaics
 
   int Nmatch = 0;
   for (i = 0; i < Ncatalog; i++) {
     for (j = 0; j < catalog[i].Nmeasure; j++) {
+      catalog[i].measureT[j].myDet = FALSE; // a detetion is not mine until proven otherwise
+
       if (TimeSelect) {
 	if (catalog[i].measureT[j].t < TSTART) continue;
@@ -582,4 +857,6 @@
   }
 
+  // this measurement is on one of my mosaics, mark it as mine.
+  catalog[cat].measureT[meas].myDet = TRUE;
   MeasureToMosaic[cat][meas] = mosID;
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/StarOps.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/StarOps.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/StarOps.c	(revision 36680)
@@ -63,19 +63,19 @@
   results->Ngrid = 0;
   if (allocLists) {
-    ALLOCATE (results->list,  double, Nmax);
-    ALLOCATE (results->dlist, double, Nmax);
-    ALLOCATE (results->wlist, double, Nmax);
+    ALLOCATE (results->Mpsflist, double, Nmax);
+    ALLOCATE (results->dpsflist, double, Nmax);
+    ALLOCATE (results->wpsflist, double, Nmax);
   }
 }
 
 void SetMrelInfoFree (SetMrelInfo *results) {
-  free (results->list);
-  free (results->dlist);
-  free (results->wlist);
+  free (results->Mpsflist);
+  free (results->dpsflist);
+  free (results->wpsflist);
 }
 
 void SetMrelInfoAccum (SetMrelInfo *summary, SetMrelInfo *results) {
   summary->Nfew  += results->Nfew ;
-  summary->Ncode  += results->Ncode ;
+  summary->Ncode += results->Ncode ;
   summary->Nsys  += results->Nsys ;
   summary->Nbad  += results->Nbad ;
@@ -158,7 +158,12 @@
   SetMrelInfoInit (&summary, FALSE);
   SetMrelInfoInit (&results, TRUE); // allocates results->list,dlist,wlist
-  ALLOCATE (results.aplist, double, Nmax);
-  ALLOCATE (results.kronlist, double, Nmax);
+
+  ALLOCATE (results.Maplist,  double, Nmax);
+  ALLOCATE (results.daplist,  double, Nmax);
+  ALLOCATE (results.waplist,  double, Nmax);
+
+  ALLOCATE (results.Mkronlist, double, Nmax);
   ALLOCATE (results.dkronlist, double, Nmax);
+  ALLOCATE (results.wkronlist, double, Nmax);
 
   ALLOCATE (results.psfqf_list, double, Nmax);
@@ -183,7 +188,11 @@
 
   SetMrelInfoFree (&results);
-  free (results.aplist);
-  free (results.kronlist);
+  free (results.Maplist);
+  free (results.daplist);
+  free (results.waplist);
+
+  free (results.Mkronlist);
   free (results.dkronlist);
+  free (results.wkronlist);
 
   free (results.psfqf_list);
@@ -328,4 +337,9 @@
 int setMrel_catalog (Catalog *catalog, int Nc, int pass, FlatCorrectionTable *flatcorr, SetMrelInfo *results, int Nsecfilt) {
 
+  fprintf (stderr, "??? should you still be using the old version of setMrel_catalog??\n");
+  exit (3);
+
+# if (0)
+
   off_t j, k, m, ID;
   int N;
@@ -333,5 +347,5 @@
 
   StatType stats, apstats, kronstats;
-  liststats_setmode (&stats, STATMODE);
+  liststats_setmode (&psfstats, STATMODE);
   liststats_setmode (&apstats, STATMODE);
   liststats_setmode (&kronstats, STATMODE);
@@ -366,5 +380,6 @@
     if (isSetMrelFinal) {
       // set the name of the primary skycell (this is used in a strcmp to match the skycells in stack detections)
-      BoundaryTreePrimaryCell(primaryCell, catalog[Nc].average[j].R, catalog[Nc].average[j].D);
+      // XXX : this whole function is deprecated
+      // BoundaryTreePrimaryCell(primaryCell, catalog[Nc].average[j].R, catalog[Nc].average[j].D);
     }
 
@@ -833,4 +848,5 @@
   if (primaryCell) free (primaryCell);
   return (TRUE);
+# endif
 }
 
@@ -890,4 +906,43 @@
   }
   return (TRUE);
+}
+
+int markObjects (Catalog *catalog, int Ncatalog) {
+
+  int i, n;
+  off_t j;
+
+  // How strongly do I own this object?
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      int nOwn = 0;
+      int m = catalog[i].averageT[j].measureOffset;
+      for (n = 0; n < catalog[i].averageT[j].Nmeasure; n++) {
+	if (!catalog[i].measureT[m+n].myDet) continue;
+	nOwn ++;
+      }
+      catalog[i].averageT[j].nOwn = nOwn;
+    }
+  }
+  return TRUE;
+}
+
+int dumpObjects (char *filename, Catalog *catalog, int Ncatalog) {
+
+  int i, n;
+  off_t j;
+
+  FILE *ftest = fopen (filename, "w");
+
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      int m = catalog[i].averageT[j].measureOffset;
+      for (n = 0; n < catalog[i].averageT[j].Nmeasure; n++) {
+	fprintf (ftest, "%08x %08x %10.6f %10.6f  %3d %1d\n", catalog[i].averageT[j].catID, catalog[i].averageT[j].objID, catalog[i].averageT[j].R, catalog[i].averageT[j].D, catalog[i].averageT[j].Nmeasure, catalog[i].measureT[m+n].myDet); 
+      }
+    }
+  }
+  fclose (ftest);
+  return TRUE;
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/args.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/args.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/args.c	(revision 36680)
@@ -1,8 +1,538 @@
 # include "relphot.h"
 
-int args (int argc, char **argv) {
+RelphotMode args (int argc, char **argv) {
 
   int N;
   double trange;
+
+  /* define time */
+  TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &TSTART)) { 
+      fprintf (stderr, "ERROR: syntax error\n");
+      return (MODE_ERROR);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
+	fprintf (stderr, "ERROR: syntax error\n");
+	return (MODE_ERROR);
+      }
+    } else {
+      if (trange < 0) {
+	trange = fabs (trange);
+	TSTOP = TSTART;
+	TSTART -= trange;
+      } else {
+	TSTOP = TSTART + trange;
+      }
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+  }
+
+  /* specify portion of the sky */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* specify region file by name (eg n0000/0000.00) */
+  UserCatalog = NULL;
+  if ((N = get_argument (argc, argv, "-catalog"))) {
+    remove_argument (N, &argc, argv);
+    UserCatalog = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  USE_BASIC_CHECK = FALSE;
+  if ((N = get_argument (argc, argv, "-basic-image-search"))) {
+    remove_argument (N, &argc, argv);
+    USE_BASIC_CHECK = TRUE;
+  }
+
+  USE_FULL_OVERLAP = TRUE;
+  if ((N = get_argument (argc, argv, "-sloppy-image-overlap"))) {
+    remove_argument (N, &argc, argv);
+    USE_FULL_OVERLAP = FALSE;
+  }
+
+  SET_MREL_VERSION = 1;
+  if ((N = get_argument (argc, argv, "-set-mrel-version"))) {
+    remove_argument (N, &argc, argv);
+    SET_MREL_VERSION = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = VERBOSE2 = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    VERBOSE2 = VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  NTHREADS = 0;
+  if ((N = get_argument (argc, argv, "-threads"))) {
+    remove_argument (N, &argc, argv);
+    NTHREADS = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // XXX for the moment, make this selection manual.  it needs to be automatic 
+  // based on the state of the SkyTable
+  HOST_ID = 0;
+  PARALLEL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel"))) {
+    PARALLEL = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  // this is a test mode : rather than launching the remote jobs and waiting for completion,
+  // relphot will simply list the remote command and wait for the user to signal completion
+  PARALLEL_MANUAL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel-manual"))) {
+    PARALLEL = TRUE; // -parallel-manual implies -parallel
+    PARALLEL_MANUAL = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  // this is a test mode : rather than launching the relphot_client jobs remotely, they are 
+  // run in serial via 'system'
+  PARALLEL_SERIAL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel-serial"))) {
+    if (PARALLEL_MANUAL) {
+      fprintf (stderr, "ERROR: cannot mix -parallel-manual and -parallel-serial\n");
+      exit (1);
+    }
+    PARALLEL = TRUE; // -parallel-serial implies -parallel
+    PARALLEL_SERIAL = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTSTUFF = FALSE;
+  if ((N = get_argument (argc, argv, "-plot"))) {
+    PLOTSTUFF = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  PLOTDELAY = 500000;
+  if ((N = get_argument (argc, argv, "-plotdelay"))) {
+    remove_argument (N, &argc, argv);
+    PLOTDELAY = 1e6*atof(argv[N]);
+    PLOTSTUFF = TRUE; // always turn on plotting if i request a plot delay
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-outroot"))) {
+    remove_argument (N, &argc, argv);
+    OUTROOT = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  } else {
+    OUTROOT = strcreate ("relphot");
+  }      
+
+  strcpy (STATMODE, "WT_MEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  NLOOP = 8;
+  if ((N = get_argument (argc, argv, "-n"))) {
+    remove_argument (N, &argc, argv);
+    NLOOP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-nloop"))) {
+    remove_argument (N, &argc, argv);
+    NLOOP = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  NGRID = 8;
+  if ((N = get_argument (argc, argv, "-ngrid"))) {
+    remove_argument (N, &argc, argv);
+    NGRID = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  RESET_ZEROPTS = FALSE;
+  if ((N = get_argument (argc, argv, "-reset-zpts"))) {
+    remove_argument (N, &argc, argv);
+    RESET_ZEROPTS = TRUE;
+  }
+
+  UPDATE = FALSE;
+  if ((N = get_argument (argc, argv, "-update"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE = TRUE;
+  }
+
+  UPDATE_CATFORMAT = NULL;
+  if ((N = get_argument (argc, argv, "-update-catformat"))) {
+    remove_argument (N, &argc, argv);
+    UPDATE_CATFORMAT = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  SAVE_IMAGE_UPDATES = TRUE;
+  if ((N = get_argument (argc, argv, "-skip-image-updates"))) {
+    remove_argument (N, &argc, argv);
+    SAVE_IMAGE_UPDATES = FALSE;
+  }
+
+  MaxDensityUse = FALSE;
+  if ((N = get_argument (argc, argv, "-max-density"))) {
+    remove_argument (N, &argc, argv);
+    MaxDensityValue = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    MaxDensityUse = TRUE;
+  }
+
+  CLOUD_TOLERANCE = 0.02;
+  if ((N = get_argument (argc, argv, "-cloud-limit"))) {
+    remove_argument (N, &argc, argv);
+    CLOUD_TOLERANCE = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  // XXX should we load a tree from CATDIR by default?
+  // NOTE: a given catdir needs an appropriate boundary tree
+  BOUNDARY_TREE = NULL;
+  if ((N = get_argument (argc, argv, "-boundary-tree"))) {
+    remove_argument (N, &argc, argv);
+    BOUNDARY_TREE = strcreate(argv[N]);
+    load_tess (BOUNDARY_TREE);
+    remove_argument (N, &argc, argv);
+  }
+
+  SHOW_PARAMS = FALSE;
+  if ((N = get_argument (argc, argv, "-params"))) {
+    remove_argument (N, &argc, argv);
+    SHOW_PARAMS = TRUE;
+  }
+
+  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
+  if ((N = get_argument (argc, argv, "-plrange"))) {
+    remove_argument (N, &argc, argv);
+    PlotMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    PlotdMmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* XXX this argument used to do two things: specify the camera name and tell the analysis to
+     calculate a common zero point for a single mosaic.  I've moved the MOSAICNAME concept into the
+     config system, but need to use this argument to specify that the mosaic zeropoints should be
+     calculated. */
+  MOSAIC_ZEROPT = FALSE;
+  if ((N = get_argument (argc, argv, "-mosaic"))) {
+    remove_argument (N, &argc, argv);
+    MOSAIC_ZEROPT = TRUE;
+    if (!strcasecmp (MOSAICNAME, "none")) {
+      fprintf (stderr, "mosaic astrometry selected by MOSAICNAME not defined\n");
+      exit (2);
+    }
+  }
+
+  FREEZE_IMAGES = FALSE;
+  if ((N = get_argument (argc, argv, "-imfreeze"))) {
+    remove_argument (N, &argc, argv);
+    FREEZE_IMAGES = TRUE;
+  }
+
+  FREEZE_MOSAICS = FALSE;
+  if ((N = get_argument (argc, argv, "-mosfreeze"))) {
+    remove_argument (N, &argc, argv);
+    FREEZE_MOSAICS = TRUE;
+  }
+
+  // USE_GRID is not valid for all cases, probably should be its own mode...
+  USE_GRID = FALSE;
+  if ((N = get_argument (argc, argv, "-grid"))) {
+    remove_argument (N, &argc, argv);
+    USE_GRID = TRUE;
+  }
+
+  KEEP_UBERCAL = TRUE;
+  if ((N = get_argument (argc, argv, "-reset-ubercal"))) {
+    remove_argument (N, &argc, argv);
+    KEEP_UBERCAL = FALSE;
+  }
+
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
+
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
+  }
+
+  SyntheticPhotometry = FALSE;
+  if ((N = get_argument (argc, argv, "-synthphot"))) {
+    remove_argument (N, &argc, argv);
+    SyntheticPhotometry = TRUE;
+    init_synthetic_mags();
+  }
+
+  refPhotcode = NULL;
+  if ((N = get_argument (argc, argv, "-refcode"))) {
+    remove_argument (N, &argc, argv);
+    refPhotcode = GetPhotcodebyName (argv[N]);
+    if (!refPhotcode) {
+      fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", argv[N]);
+      exit (1);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  REGION_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-region-hosts"))) {
+    remove_argument (N, &argc, argv);
+    REGION_FILE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  REGION_HOST_ID = 0;
+  if ((N = get_argument (argc, argv, "-region-hostID"))) {
+    remove_argument (N, &argc, argv);
+    REGION_HOST_ID = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  RelphotMode mode = MODE_ERROR;
+  if ((N = get_argument (argc, argv, "-images"))) {
+    remove_argument (N, &argc, argv);
+    mode = UPDATE_IMAGES;
+  }
+  if ((N = get_argument (argc, argv, "-averages"))) {
+    remove_argument (N, &argc, argv);
+    mode = UPDATE_AVERAGES;
+  }
+  if ((N = get_argument (argc, argv, "-apply-offsets"))) {
+    remove_argument (N, &argc, argv);
+    mode = APPLY_OFFSETS;
+  }
+  IMAGE_TABLE = NULL;
+  if ((N = get_argument (argc, argv, "-parallel-images"))) {
+    remove_argument (N, &argc, argv);
+    mode = PARALLEL_IMAGES;
+    if (N >= argc) relphot_usage();
+    IMAGE_TABLE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (!REGION_FILE) relphot_usage();
+  }
+
+  PARALLEL_REGIONS_MANUAL = FALSE;
+  if ((N = get_argument (argc, argv, "-parallel-regions"))) {
+    remove_argument (N, &argc, argv);
+    mode = PARALLEL_REGIONS;
+    if (!REGION_FILE) relphot_usage();
+    if ((N = get_argument (argc, argv, "-parallel-regions-manual"))) {
+      remove_argument (N, &argc, argv);
+      PARALLEL_REGIONS_MANUAL = TRUE;
+    }
+  }
+
+  switch (mode) {
+    case UPDATE_AVERAGES:
+      if (argc != 1) relphot_usage();
+      break;
+      
+    case UPDATE_IMAGES:
+    case PARALLEL_IMAGES:
+    case PARALLEL_REGIONS:
+      PhotcodeList = strcreate (argv[1]);
+      photcodes = ParsePhotcodeList (PhotcodeList, &Nphotcodes, TRUE); // require SEC photcodes
+      remove_argument (1, &argc, argv);
+      break;
+
+    default:
+      fprintf (stderr, "no valid mode selected\n");
+      relphot_usage();
+      break;
+  }
+  if (argc != 1) relphot_usage ();
+
+  return mode;
+}
+
+int args_client (int argc, char **argv) {
+
+  int N;
+  double trange;
+
+  // by definition, the client is not parallel 
+  PARALLEL = FALSE;
+  PARALLEL_MANUAL = FALSE;
+  PARALLEL_SERIAL = FALSE;
+
+  HOST_ID = 0;
+  if ((N = get_argument (argc, argv, "-hostID"))) {
+    remove_argument (N, &argc, argv);
+    HOST_ID = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!HOST_ID) relphot_client_usage();
+
+  HOSTDIR = NULL;
+  if ((N = get_argument (argc, argv, "-hostdir"))) {
+    remove_argument (N, &argc, argv);
+    HOSTDIR = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (!HOSTDIR) relphot_client_usage();
+
+  IMAGES = NULL; // used in -update mode
+  BCATALOG = NULL; // used in -load mode
+  MODE = MODE_NONE;
+  if ((N = get_argument (argc, argv, "-load"))) {
+    MODE = MODE_LOAD;
+    remove_argument (N, &argc, argv);
+    BCATALOG = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-update-catalogs"))) {
+    if (MODE) {
+      fprintf (stderr, "ERROR: cannot mix modes (-load, -update-catalogs, -update-objects)\n");
+      relphot_client_usage();
+    }
+    MODE = MODE_UPDATE;
+    remove_argument (N, &argc, argv);
+    IMAGES = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-update-objects"))) {
+    if (MODE) {
+      fprintf (stderr, "ERROR: cannot mix modes (-load, -update-catalogs, -update-objects)\n");
+      relphot_client_usage();
+    }
+    MODE = MODE_UPDATE_OBJECTS;
+    remove_argument (N, &argc, argv);
+  }
+  if (!MODE) relphot_client_usage();
+
+  strcpy (STATMODE, "WT_MEAN");
+  if ((N = get_argument (argc, argv, "-statmode"))) {
+    remove_argument (N, &argc, argv);
+    strcpy (STATMODE, argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  BOUNDARY_TREE = NULL;
+  if ((N = get_argument (argc, argv, "-boundary-tree"))) {
+    remove_argument (N, &argc, argv);
+    BOUNDARY_TREE = strcreate(argv[N]);
+    load_tess (BOUNDARY_TREE);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* specify portion of the sky */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax = 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+  if ((N = get_argument (argc, argv, "-region"))) {
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Rmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    UserPatch.Dmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  SET_MREL_VERSION = 1;
+  if ((N = get_argument (argc, argv, "-set-mrel-version"))) {
+    remove_argument (N, &argc, argv);
+    SET_MREL_VERSION = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  VERBOSE = VERBOSE2 = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    VERBOSE2 = VERBOSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-reset"))) {
+    remove_argument (N, &argc, argv);
+    RESET = TRUE;
+  }
+
+  RESET_ZEROPTS = FALSE;
+  if ((N = get_argument (argc, argv, "-reset-zpts"))) {
+    remove_argument (N, &argc, argv);
+    RESET_ZEROPTS = TRUE;
+  }
+
+  KEEP_UBERCAL = TRUE;
+  if ((N = get_argument (argc, argv, "-reset-ubercal"))) {
+    remove_argument (N, &argc, argv);
+    KEEP_UBERCAL = FALSE;
+  }
 
   /* define time */
@@ -33,153 +563,12 @@
   }
 
-  /* specify portion of the sky */
-  UserPatch.Rmin = 0;
-  UserPatch.Rmax = 360;
-  UserPatch.Dmin = -90;
-  UserPatch.Dmax = +90;
-  if ((N = get_argument (argc, argv, "-region"))) {
-    remove_argument (N, &argc, argv);
-    UserPatch.Rmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Rmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Dmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Dmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  /* specify region file by name (eg n0000/0000.00) */
-  UserCatalog = NULL;
-  if ((N = get_argument (argc, argv, "-catalog"))) {
-    remove_argument (N, &argc, argv);
-    UserCatalog = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  USE_BASIC_CHECK = FALSE;
-  if ((N = get_argument (argc, argv, "-basic-image-search"))) {
-    remove_argument (N, &argc, argv);
-    USE_BASIC_CHECK = TRUE;
-  }
-
-  USE_FULL_OVERLAP = TRUE;
-  if ((N = get_argument (argc, argv, "-sloppy-image-overlap"))) {
-    remove_argument (N, &argc, argv);
-    USE_FULL_OVERLAP = FALSE;
-  }
-
-  SET_MREL_VERSION = 1;
-  if ((N = get_argument (argc, argv, "-set-mrel-version"))) {
-    remove_argument (N, &argc, argv);
-    SET_MREL_VERSION = atof(argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  VERBOSE = VERBOSE2 = FALSE;
-  if ((N = get_argument (argc, argv, "-v"))) {
-    VERBOSE = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-  if ((N = get_argument (argc, argv, "-vv"))) {
-    VERBOSE2 = VERBOSE = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-
-  NTHREADS = 0;
-  if ((N = get_argument (argc, argv, "-threads"))) {
-    remove_argument (N, &argc, argv);
-    NTHREADS = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  // XXX for the moment, make this selection manual.  it needs to be automatic 
-  // based on the state of the SkyTable
-  HOST_ID = 0;
-  PARALLEL = FALSE;
-  if ((N = get_argument (argc, argv, "-parallel"))) {
-    PARALLEL = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-  // this is a test mode : rather than launching the remote jobs and waiting for completion,
-  // relphot will simply list the remote command and wait for the user to signal completion
-  PARALLEL_MANUAL = FALSE;
-  if ((N = get_argument (argc, argv, "-parallel-manual"))) {
-    PARALLEL = TRUE; // -parallel-manual implies -parallel
-    PARALLEL_MANUAL = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-  // this is a test mode : rather than launching the relphot_client jobs remotely, they are 
-  // run in serial via 'system'
-  PARALLEL_SERIAL = FALSE;
-  if ((N = get_argument (argc, argv, "-parallel-serial"))) {
-    if (PARALLEL_MANUAL) {
-      fprintf (stderr, "ERROR: cannot mix -parallel-manual and -parallel-serial\n");
-      exit (1);
-    }
-    PARALLEL = TRUE; // -parallel-serial implies -parallel
-    PARALLEL_SERIAL = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-
-  PLOTSTUFF = FALSE;
-  if ((N = get_argument (argc, argv, "-plot"))) {
-    PLOTSTUFF = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-
-  PLOTDELAY = 500000;
-  if ((N = get_argument (argc, argv, "-plotdelay"))) {
-    remove_argument (N, &argc, argv);
-    PLOTDELAY = 1e6*atof(argv[N]);
-    PLOTSTUFF = TRUE; // always turn on plotting if i request a plot delay
-    remove_argument (N, &argc, argv);
-  }
-
-  if ((N = get_argument (argc, argv, "-outroot"))) {
-    remove_argument (N, &argc, argv);
-    OUTROOT = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  } else {
-    OUTROOT = strcreate ("relphot");
-  }      
-
-  strcpy (STATMODE, "WT_MEAN");
-  if ((N = get_argument (argc, argv, "-statmode"))) {
-    remove_argument (N, &argc, argv);
-    strcpy (STATMODE, argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  NLOOP = 8;
-  if ((N = get_argument (argc, argv, "-n"))) {
-    remove_argument (N, &argc, argv);
-    NLOOP = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-  if ((N = get_argument (argc, argv, "-nloop"))) {
-    remove_argument (N, &argc, argv);
-    NLOOP = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  NGRID = 8;
-  if ((N = get_argument (argc, argv, "-ngrid"))) {
-    remove_argument (N, &argc, argv);
-    NGRID = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  RESET = FALSE;
-  if ((N = get_argument (argc, argv, "-reset"))) {
-    remove_argument (N, &argc, argv);
-    RESET = TRUE;
-  }
-
-  RESET_ZEROPTS = FALSE;
-  if ((N = get_argument (argc, argv, "-reset-zpts"))) {
-    remove_argument (N, &argc, argv);
-    RESET_ZEROPTS = TRUE;
-  }
+
+  MIN_ERROR = 0.001;
+  if ((N = get_argument (argc, argv, "-minerror"))) {
+    remove_argument (N, &argc, argv);
+    MIN_ERROR = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    /* require MIN_ERROR > 0 */
+  }  
 
   UPDATE = FALSE;
@@ -196,8 +585,41 @@
   }
 
-  SAVE_IMAGE_UPDATES = TRUE;
-  if ((N = get_argument (argc, argv, "-skip-image-updates"))) {
-    remove_argument (N, &argc, argv);
-    SAVE_IMAGE_UPDATES = FALSE;
+  AreaSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-area"))) {
+    remove_argument (N, &argc, argv);
+    AreaXmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaXmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaYmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    AreaSelect = TRUE;
+  }
+
+  SyntheticPhotometry = FALSE;
+  if ((N = get_argument (argc, argv, "-synthphot"))) {
+    remove_argument (N, &argc, argv);
+    SyntheticPhotometry = FALSE;
+    init_synthetic_mags();
+  }
+
+  ImagSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-instmag"))) {
+    remove_argument (N, &argc, argv);
+    ImagMin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagMax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    ImagSelect = TRUE;
+  }
+
+  DophotSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-dophot"))) {
+    remove_argument (N, &argc, argv);
+    DophotValue = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    DophotSelect = TRUE;
   }
 
@@ -210,374 +632,8 @@
   }
 
-  CLOUD_TOLERANCE = 0.02;
-  if ((N = get_argument (argc, argv, "-cloud-limit"))) {
-    remove_argument (N, &argc, argv);
-    CLOUD_TOLERANCE = atof(argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  BOUNDARY_TREE = NULL;
-  if ((N = get_argument (argc, argv, "-boundary-tree"))) {
-    remove_argument (N, &argc, argv);
-    BOUNDARY_TREE = strcreate(argv[N]);
-    load_tree (BOUNDARY_TREE);
-    remove_argument (N, &argc, argv);
-  }
-
-  SHOW_PARAMS = FALSE;
-  if ((N = get_argument (argc, argv, "-params"))) {
-    remove_argument (N, &argc, argv);
-    SHOW_PARAMS = TRUE;
-  }
-
-  PlotMmin = 10.0; PlotMmax = 20.0; PlotdMmin = -1.0; PlotdMmax = 1.0;
-  if ((N = get_argument (argc, argv, "-plrange"))) {
-    remove_argument (N, &argc, argv);
-    PlotMmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    PlotMmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    PlotdMmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    PlotdMmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  /* XXX this argument used to do two things: specify the camera name and tell the analysis to
-     calculate a common zero point for a single mosaic.  I've moved the MOSAICNAME concept into the
-     config system, but need to use this argument to specify that the mosaic zeropoints should be
-     calculated. */
-  MOSAIC_ZEROPT = FALSE;
-  if ((N = get_argument (argc, argv, "-mosaic"))) {
-    remove_argument (N, &argc, argv);
-    MOSAIC_ZEROPT = TRUE;
-    if (!strcasecmp (MOSAICNAME, "none")) {
-      fprintf (stderr, "mosaic astrometry selected by MOSAICNAME not defined\n");
-      exit (2);
-    }
-  }
-
-  FREEZE_IMAGES = FALSE;
-  if ((N = get_argument (argc, argv, "-imfreeze"))) {
-    remove_argument (N, &argc, argv);
-    FREEZE_IMAGES = TRUE;
-  }
-
-  FREEZE_MOSAICS = FALSE;
-  if ((N = get_argument (argc, argv, "-mosfreeze"))) {
-    remove_argument (N, &argc, argv);
-    FREEZE_MOSAICS = TRUE;
-  }
-
-  USE_GRID = FALSE;
-  if ((N = get_argument (argc, argv, "-grid"))) {
-    remove_argument (N, &argc, argv);
-    USE_GRID = TRUE;
-  }
-
-  KEEP_UBERCAL = TRUE;
-  if ((N = get_argument (argc, argv, "-reset-ubercal"))) {
-    remove_argument (N, &argc, argv);
-    KEEP_UBERCAL = FALSE;
-  }
-
-  MIN_ERROR = 0.001;
-  if ((N = get_argument (argc, argv, "-minerror"))) {
-    remove_argument (N, &argc, argv);
-    MIN_ERROR = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    /* require MIN_ERROR > 0 */
-  }  
-
-  AreaSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-area"))) {
-    remove_argument (N, &argc, argv);
-    AreaXmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaXmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaYmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaYmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaSelect = TRUE;
-  }
-
-  ImagSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-instmag"))) {
-    remove_argument (N, &argc, argv);
-    ImagMin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    ImagMax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    ImagSelect = TRUE;
-  }
-
-  DophotSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-dophot"))) {
-    remove_argument (N, &argc, argv);
-    DophotValue = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    DophotSelect = TRUE;
-  }
-
-  SyntheticPhotometry = FALSE;
-  if ((N = get_argument (argc, argv, "-synthphot"))) {
-    remove_argument (N, &argc, argv);
-    SyntheticPhotometry = TRUE;
-    init_synthetic_mags();
-  }
-
-  refPhotcode = NULL;
-  if ((N = get_argument (argc, argv, "-refcode"))) {
-    remove_argument (N, &argc, argv);
-    refPhotcode = GetPhotcodebyName (argv[N]);
-    if (!refPhotcode) {
-      fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", argv[N]);
-      exit (1);
-    }
-    remove_argument (N, &argc, argv);
-  }
-
-  UpdateAverages = FALSE;
-  if ((N = get_argument (argc, argv, "-averages"))) {
-    remove_argument (N, &argc, argv);
-    UpdateAverages = TRUE;
-  }
-
-  ApplyOffsets = FALSE;
-  if ((N = get_argument (argc, argv, "-apply-offsets"))) {
-    remove_argument (N, &argc, argv);
-    ApplyOffsets = TRUE;
-  }
-
-  if (UpdateAverages && (argc == 1)) return TRUE;
-  if (argc != 2) relphot_usage ();
+  if ((MODE == MODE_UPDATE_OBJECTS)  && (argc == 1)) return TRUE;
+  if (argc != 2) relphot_client_usage ();
 
   return TRUE;
 }
 
-int args_client (int argc, char **argv) {
-
-  int N;
-  double trange;
-
-  // by definition, the client is not parallel 
-  PARALLEL = FALSE;
-  PARALLEL_MANUAL = FALSE;
-  PARALLEL_SERIAL = FALSE;
-
-  HOST_ID = 0;
-  if ((N = get_argument (argc, argv, "-hostID"))) {
-    remove_argument (N, &argc, argv);
-    HOST_ID = atoi (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-  if (!HOST_ID) relphot_client_usage();
-
-  HOSTDIR = NULL;
-  if ((N = get_argument (argc, argv, "-hostdir"))) {
-    remove_argument (N, &argc, argv);
-    HOSTDIR = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-  if (!HOSTDIR) relphot_client_usage();
-
-  IMAGES = NULL; // used in -update mode
-  BCATALOG = NULL; // used in -load mode
-  MODE = MODE_NONE;
-  if ((N = get_argument (argc, argv, "-load"))) {
-    MODE = MODE_LOAD;
-    remove_argument (N, &argc, argv);
-    BCATALOG = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-  if ((N = get_argument (argc, argv, "-update-catalogs"))) {
-    if (MODE) {
-      fprintf (stderr, "ERROR: cannot mix modes (-load, -update-catalogs, -update-objects)\n");
-      relphot_client_usage();
-    }
-    MODE = MODE_UPDATE;
-    remove_argument (N, &argc, argv);
-    IMAGES = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-  if ((N = get_argument (argc, argv, "-update-objects"))) {
-    if (MODE) {
-      fprintf (stderr, "ERROR: cannot mix modes (-load, -update-catalogs, -update-objects)\n");
-      relphot_client_usage();
-    }
-    MODE = MODE_UPDATE_OBJECTS;
-    remove_argument (N, &argc, argv);
-  }
-  if (!MODE) relphot_client_usage();
-
-  strcpy (STATMODE, "WT_MEAN");
-  if ((N = get_argument (argc, argv, "-statmode"))) {
-    remove_argument (N, &argc, argv);
-    strcpy (STATMODE, argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  BOUNDARY_TREE = NULL;
-  if ((N = get_argument (argc, argv, "-boundary-tree"))) {
-    remove_argument (N, &argc, argv);
-    BOUNDARY_TREE = strcreate(argv[N]);
-    load_tree (BOUNDARY_TREE);
-    remove_argument (N, &argc, argv);
-  }
-
-  /* specify portion of the sky */
-  UserPatch.Rmin = 0;
-  UserPatch.Rmax = 360;
-  UserPatch.Dmin = -90;
-  UserPatch.Dmax = +90;
-  if ((N = get_argument (argc, argv, "-region"))) {
-    remove_argument (N, &argc, argv);
-    UserPatch.Rmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Rmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Dmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    UserPatch.Dmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  SET_MREL_VERSION = 1;
-  if ((N = get_argument (argc, argv, "-set-mrel-version"))) {
-    remove_argument (N, &argc, argv);
-    SET_MREL_VERSION = atof(argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  VERBOSE = VERBOSE2 = FALSE;
-  if ((N = get_argument (argc, argv, "-v"))) {
-    VERBOSE = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-  if ((N = get_argument (argc, argv, "-vv"))) {
-    VERBOSE2 = VERBOSE = TRUE;
-    remove_argument (N, &argc, argv);
-  }
-
-  RESET = FALSE;
-  if ((N = get_argument (argc, argv, "-reset"))) {
-    remove_argument (N, &argc, argv);
-    RESET = TRUE;
-  }
-
-  RESET_ZEROPTS = FALSE;
-  if ((N = get_argument (argc, argv, "-reset-zpts"))) {
-    remove_argument (N, &argc, argv);
-    RESET_ZEROPTS = TRUE;
-  }
-
-  KEEP_UBERCAL = TRUE;
-  if ((N = get_argument (argc, argv, "-reset-ubercal"))) {
-    remove_argument (N, &argc, argv);
-    KEEP_UBERCAL = FALSE;
-  }
-
-  /* define time */
-  TimeSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-time"))) {
-    remove_argument (N, &argc, argv);
-    if (!ohana_str_to_time (argv[N], &TSTART)) { 
-      fprintf (stderr, "ERROR: syntax error\n");
-      return (FALSE);
-    }
-    remove_argument (N, &argc, argv);
-    if (!ohana_str_to_dtime (argv[N], &trange)) { 
-      if (!ohana_str_to_time (argv[N], &TSTOP)) { 
-	fprintf (stderr, "ERROR: syntax error\n");
-	return (FALSE);
-      }
-    } else {
-      if (trange < 0) {
-	trange = fabs (trange);
-	TSTOP = TSTART;
-	TSTART -= trange;
-      } else {
-	TSTOP = TSTART + trange;
-      }
-    }
-    remove_argument (N, &argc, argv);
-    TimeSelect = TRUE;
-  }
-
-
-  MIN_ERROR = 0.001;
-  if ((N = get_argument (argc, argv, "-minerror"))) {
-    remove_argument (N, &argc, argv);
-    MIN_ERROR = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    /* require MIN_ERROR > 0 */
-  }  
-
-  UPDATE = FALSE;
-  if ((N = get_argument (argc, argv, "-update"))) {
-    remove_argument (N, &argc, argv);
-    UPDATE = TRUE;
-  }
-
-  UPDATE_CATFORMAT = NULL;
-  if ((N = get_argument (argc, argv, "-update-catformat"))) {
-    remove_argument (N, &argc, argv);
-    UPDATE_CATFORMAT = strcreate (argv[N]);
-    remove_argument (N, &argc, argv);
-  }
-
-  AreaSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-area"))) {
-    remove_argument (N, &argc, argv);
-    AreaXmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaXmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaYmin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaYmax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    AreaSelect = TRUE;
-  }
-
-  SyntheticPhotometry = FALSE;
-  if ((N = get_argument (argc, argv, "-synthphot"))) {
-    remove_argument (N, &argc, argv);
-    SyntheticPhotometry = FALSE;
-    init_synthetic_mags();
-  }
-
-  ImagSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-instmag"))) {
-    remove_argument (N, &argc, argv);
-    ImagMin = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    ImagMax = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    ImagSelect = TRUE;
-  }
-
-  DophotSelect = FALSE;
-  if ((N = get_argument (argc, argv, "-dophot"))) {
-    remove_argument (N, &argc, argv);
-    DophotValue = atof (argv[N]);
-    remove_argument (N, &argc, argv);
-    DophotSelect = TRUE;
-  }
-
-  MaxDensityUse = FALSE;
-  if ((N = get_argument (argc, argv, "-max-density"))) {
-    remove_argument (N, &argc, argv);
-    MaxDensityValue = atof(argv[N]);
-    remove_argument (N, &argc, argv);
-    MaxDensityUse = TRUE;
-  }
-
-  if ((MODE == MODE_UPDATE_OBJECTS)  && (argc == 1)) return TRUE;
-  if (argc != 2) relphot_client_usage ();
-
-  return TRUE;
-}
-
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/assign_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/assign_images.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/assign_images.c	(revision 36680)
@@ -0,0 +1,289 @@
+# include "relphot.h"
+
+// This function generates a subset of the images based on selections.  Input db has already
+// been loaded with the raw fits table data
+int assign_images (FITS_DB *db, RegionHostTable *regionHosts) {
+
+  off_t Nimage;
+
+  INITTIME;
+
+  // convert database table to internal structure (binary to Image)
+  // 'image' points to the same memory as db->ftable->buffer
+  Image *image = gfits_table_get_Image (&db[0].ftable, &Nimage, &db[0].swapped);
+  if (!image) {
+      fprintf (stderr, "ERROR: failed to read images\n");
+      exit (2);
+  }
+  MARKTIME("convert image table to internal structure: %f sec\n", dtime);
+
+  // *** NOTE : for the moment, regions must be in the range 0 - 360, -90 - +90
+
+  // generate the chip match here so we can define the mosaic centers (if needed)
+  BuildChipMatch (image, Nimage);
+  MARKTIME("build chip match for %d images: %f sec\n", (int) Nimage, dtime);
+
+  if (MOSAIC_ZEROPT) {
+    makeMosaics (image, Nimage);
+
+    // center coords and Mcal, dMcal, Xm for the mosaics
+    setMosaicCenters (image, Nimage);
+    MARKTIME("set mosaic coordinates and Mcal values: %f sec\n", dtime);
+  }
+
+  // register the image array with ImageOps.c for later getimageByID calls
+  initImages (image, NULL, Nimage);
+
+  if (VERBOSE) fprintf (stderr, "finding images\n");
+
+  // for each regionHost, select images which are contained by the region
+  // even faster would be to use a tree to get to the real regions...
+  select_images_hostregion (regionHosts, image, Nimage);
+
+  return TRUE;
+}
+
+# define D_NIMAGE 1000
+
+// assign images to the region hosts; at the end, each host will have its list of images
+int select_images_hostregion (RegionHostTable *regionHosts, Image *image, off_t Nimage) {
+
+  int ecode, found;
+  off_t i, j;
+
+  // INITTIME;
+  
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    regionHosts->hosts[i].Nimage = 0;
+    regionHosts->hosts[i].NIMAGE = D_NIMAGE;
+    ALLOCATE (regionHosts->hosts[i].image, Image, regionHosts->hosts[i].NIMAGE);
+  }
+
+  for (j = 0; j < Nimage; j++) {
+    
+    /* exclude images by photcode */
+    ecode = GetPhotcodeEquivCodebyCode (image[j].photcode);
+    found = FALSE;
+    int Ns;
+    for (Ns = 0; !found && (Ns < Nphotcodes); Ns++) {
+      if (ecode == photcodes[Ns][0].code) found = TRUE;
+    }
+    if (!found) continue;
+
+    /* exclude images by time */
+    if (TimeSelect) {
+      if (image[j].tzero < TSTART) continue;
+      if (image[j].tzero > TSTOP) continue;
+    }
+    
+    // do not include DIS (PHU-level mosaics) in the output list
+    if (!strcmp(&image[j].coords.ctype[4], "-DIS")) continue;
+
+    // match the image to its corresponding ASTROMETRIC mosaic (not the same as the Mosaic above)
+    if (!FindMosaicForImage (image, Nimage, j)) {
+      if (VERBOSE2) fprintf (stderr, "cannot find mosaic for "OFF_T_FMT"\n", i);
+      continue;
+    }
+    
+    // Exclude images with crazy astrometry
+    // XXX NOTE : this is gpc1-specific
+    { 
+      double dP1 = hypot(image[j].coords.pc1_1, image[j].coords.pc1_2);
+      double dP2 = hypot(image[j].coords.pc2_1, image[j].coords.pc2_2);
+      if (fabs(dP1 - 1.0) > 0.02) continue;
+      if (fabs(dP2 - 1.0) > 0.02) continue;
+
+      double X00, Y00, X10, Y10, X01, Y01;
+      XY_to_LM (&X00, &Y00, 0.0, 0.0, &image[j].coords);
+      XY_to_LM (&X10, &Y10, image[j].NX, 0.0, &image[j].coords);
+      XY_to_LM (&X01, &Y01, 0.0, image[j].NY, &image[j].coords);
+      double dS0 = hypot ((X00 - X10), (Y00 - Y10));
+      double dS1 = hypot ((X00 - X01), (Y00 - Y01));
+      if (dS0 > 6000) continue;
+      if (dS1 > 6500) continue;
+    }	
+
+    // use a reference coordinate for each image to assign to hosts
+    // define image center - note the DIS images (mosaic phu) are special, but we have 
+    // already excluded them above.  we also save the image centers for reference
+    double Xc, Yc;
+    double Rc, Dc;
+
+    Xc = 0.5*image[j].NX; 
+    Yc = 0.5*image[j].NY;
+
+    XY_to_RD (&Rc, &Dc, Xc, Yc, &image[j].coords);
+    Rc = ohana_normalize_angle_to_midpoint (Rc, 180.0);
+    image[j].RAo  = Rc;
+    image[j].DECo = Dc;
+
+    // NOTE: if we are NOT using mosaic centers, we don't need to keep chips with centers
+    // on opposite sides of 0,360 together.  in which case, it is OK for the range of Rc
+    // to be 0.0 to 360.0.  also NOTE: RAo,DECo are only used for debugging reference.
+
+    if (MOSAIC_ZEROPT) {
+      // use the coords of the associated mosaic to select
+      Mosaic *mosaic = getMosaicForImage (j); 
+      Rc = mosaic->coords.crval1;
+      Dc = mosaic->coords.crval2;
+      // NOTE : have defined mosaic Rc,Dc to choose the side of 0,360 on which most of the
+      // chips are located.  but, for host assignment, we rationalize to 0.0 - 360.0
+      Rc = ohana_normalize_angle_to_midpoint (Rc, 180.0);
+    }
+
+    i = find_host_for_coords (regionHosts, Rc, Dc);
+
+    if (i == -1) continue;
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // image bounds are defined for a range centered on the mosaic center thus, a mosaic
+    // with center 0.5 will have chips bounds ranging from ~ -1.5 to +2.5 or so, while a mosaic
+    // with center 359.5 will have chips bounds ranging from ~ 357.5 to 361.5 or so
+    double Rmin, Rmax, Dmin, Dmax;
+    calculate_image_bounds (&image[j], &Rmin, &Rmax, &Dmin, &Dmax, Rc);
+
+    host->RminCat = MIN(Rmin, host->RminCat);
+    host->RmaxCat = MAX(Rmax, host->RmaxCat);
+    host->DminCat = MIN(Dmin, host->DminCat);
+    host->DmaxCat = MAX(Dmax, host->DmaxCat);
+
+    // regionHosts needs to have the full outer boundary
+    // (so reload_catalogs covers the correct region)
+    regionHosts->Rmin = MIN(Rmin, regionHosts->Rmin);
+    regionHosts->Rmax = MAX(Rmax, regionHosts->Rmax);
+    regionHosts->Dmin = MIN(Dmin, regionHosts->Dmin);
+    regionHosts->Dmax = MAX(Dmax, regionHosts->Dmax);
+
+    // this is a bit memory expensive : I am making a complete copy of the image table here
+    off_t Nsubset = regionHosts->hosts[i].Nimage;
+    regionHosts->hosts[i].image[Nsubset] = image[j];
+    // regionHosts->hosts[i].line_number[Nsubset] = j;
+
+    regionHosts->hosts[i].Nimage ++;
+    if (regionHosts->hosts[i].Nimage == regionHosts->hosts[i].NIMAGE) {
+      regionHosts->hosts[i].NIMAGE += D_NIMAGE;
+      REALLOCATE (regionHosts->hosts[i].image, Image, regionHosts->hosts[i].NIMAGE);
+      // REALLOCATE (regionHosts->hosts[i].line_number, off_t, regionHosts->hosts[i].NIMAGE);
+    }
+  }
+
+  return TRUE;
+}
+
+double Xf[] = {0.0, 1.0, 0.0, 1.0};
+double Yf[] = {0.0, 0.0, 1.0, 1.0};
+
+int calculate_image_bounds (Image *image, double *rmin, double *rmax, double *dmin, double *dmax, double Rmid) {
+
+  int n;
+
+  double Rmin = 360.0;
+  double Rmax =   0.0;
+  double Dmin = +90.0;
+  double Dmax = -90.0;
+
+  // define image corners
+  for (n = 0; n < 4; n++) {
+    double Xc, Yc, Rc, Dc;
+    Xc = Xf[n]*image->NX; 
+    Yc = Yf[n]*image->NY;
+    XY_to_RD (&Rc, &Dc, Xc, Yc, &image->coords);
+    Rc = ohana_normalize_angle_to_midpoint (Rc, Rmid);
+      
+    Rmin = MIN (Rmin, Rc);
+    Rmax = MAX (Rmax, Rc);
+    Dmin = MIN (Dmin, Dc);
+    Dmax = MAX (Dmax, Dc);
+  }
+
+  *rmin = Rmin;
+  *rmax = Rmax;
+  *dmin = Dmin;
+  *dmax = Dmax;
+
+  return TRUE;
+}
+
+int calculate_host_image_bounds (RegionHostTable *regionHosts, double Rmid) {
+
+  int i, n;
+  off_t j;
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // XXX clear the chip match?
+
+    BuildChipMatch (host->image, host->Nimage);
+
+    double Rmin =  720.0;
+    double Rmax = -360.0;
+    double Dmin =  +90.0;
+    double Dmax = -90.0;
+
+    for (j = 0; j < host->Nimage; j++) {
+
+      Image *image = &host->image[j];
+
+      if (!FindMosaicForImage (host->image, host->Nimage, j)) {
+	fprintf (stderr, "missing astrometry? programming error?\n");
+	abort ();
+      }
+      
+      // define image corners
+      for (n = 0; n < 4; n++) {
+	double Xc, Yc, Rc, Dc;
+	Xc = Xf[n]*image->NX; 
+	Yc = Yf[n]*image->NY;
+	XY_to_RD (&Rc, &Dc, Xc, Yc, &image->coords);
+	Rc = ohana_normalize_angle_to_midpoint (Rc, Rmid);
+      
+	Rmin = MIN (Rmin, Rc);
+	Dmin = MIN (Dmin, Rc);
+	
+	Rmax = MAX (Rmax, Rc);
+	Dmax = MAX (Dmax, Rc);
+      }
+
+    }
+
+    // RminCat, RmaxCat may extended beyond 0.0 - 360.0
+    host->RminCat = Rmin;
+    host->DminCat = Dmin;
+    host->RmaxCat = Rmax;
+    host->DmaxCat = Dmax;
+
+    // regionHosts needs to have the full outer boundary
+    // (so reload_catalogs covers the correct region)
+    regionHosts->Rmin = MIN(Rmin, regionHosts->Rmin);
+    regionHosts->Rmax = MAX(Rmax, regionHosts->Rmax);
+    regionHosts->Dmin = MIN(Dmin, regionHosts->Dmin);
+    regionHosts->Dmax = MAX(Dmax, regionHosts->Dmax);
+  }
+  return TRUE;
+}
+
+
+// Rc is in range 0.0 - 360.0, hosts Rmin,Rmax in range 0.0 - 360.0
+int find_host_for_coords (RegionHostTable *regionHosts, double Rc, double Dc) {
+
+  int i;
+
+  if (isnan(Rc)) return -1;
+  if (isnan(Dc)) return -1;
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+    if (Rc <  host->Rmin) continue;
+    if (Rc >= host->Rmax) continue;
+    if (Dc <  host->Dmin) continue;
+    if (Dc >= host->Dmax) continue;
+
+    return i;
+  }
+  return -1;
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/client_logger.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/client_logger.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/client_logger.c	(revision 36680)
@@ -5,13 +5,13 @@
 
 static FILE *logfile = NULL;
-int client_logger_init () {
+int client_logger_init (char *dirname) {
 
   char filename[DVO_MAX_PATH];
 
-  snprintf (filename, DVO_MAX_PATH, "%s/log.rlpc.XXXXXX", HOSTDIR);
+  snprintf (filename, DVO_MAX_PATH, "%s/log.rlpc.XXXXXX", dirname);
     
   int fd = mkstemp (filename);
   if (fd == -1) {
-    fprintf (stderr, "failed to open client logger, exiting\n");
+    fprintf (stderr, "failed to open client logger %s, exiting\n", filename);
     exit (50);
   }
@@ -32,4 +32,6 @@
   vfprintf (logfile, format, argp);
   va_end (argp);
+
+  fflush (logfile);
   return TRUE;
 }
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/help.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/help.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/help.c	(revision 36680)
@@ -2,7 +2,11 @@
 
 void relphot_usage (void) {
-  fprintf (stderr, "ERROR: USAGE: relphot (photcodes) -region RA RA DEC DEC\n");
-  fprintf (stderr, "       or:    relphot (photcodes) -catalog (name)\n");
-  fprintf (stderr, "       or:    relphot -averages -region RA RA DEC DEC\n");
+  fprintf (stderr, "ERROR: USAGE: relphot (photcodes) -images\n");
+  fprintf (stderr, "       or:    relphot -averages\n");
+  fprintf (stderr, "       or:    relphot -apply-offsets\n");
+  fprintf (stderr, "       or:    relphot (photcodes) -parallel-regions -region-hosts (RegionFile)\n");
+  fprintf (stderr, "       or:    relphot (photcodes) -parallel-images (ImageTable) -region-hosts (RegionFile)\n\n");
+  fprintf (stderr, "  regions:    -region RA RA DEC DEC)\n");
+  fprintf (stderr, "       or:    -catalog (name)\n");
   fprintf (stderr, "  use -h for more usage information\n");
   exit (2);
@@ -18,7 +22,11 @@
 
 show_help:
-  fprintf (stderr, "ERROR: USAGE: relphot (photcode) -region RA RA DEC DEC\n");
-  fprintf (stderr, "       or:    relphot (photcode) -catalog (name)\n");
-  fprintf (stderr, "       or:    relphot -averages -region RA RA DEC DEC\n");
+  fprintf (stderr, "ERROR: USAGE: relphot (photcodes) -images\n");
+  fprintf (stderr, "       or:    relphot -averages\n");
+  fprintf (stderr, "       or:    relphot -apply-offsets\n");
+  fprintf (stderr, "       or:    relphot (photcodes) -parallel-regions -region-hosts (RegionFile)\n");
+  fprintf (stderr, "       or:    relphot (photcodes) -parallel-images (ImageTable) -region-hosts (RegionFile)\n\n");
+  fprintf (stderr, "  regions:    -region RA RA DEC DEC)\n");
+  fprintf (stderr, "       or:    -catalog (name)\n");
   fprintf (stderr, "  options: \n");
   fprintf (stderr, "  -time (start) (stop)\n");
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/indexCatalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/indexCatalog.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/indexCatalog.c	(revision 36680)
@@ -0,0 +1,64 @@
+# include "relphot.h"
+
+static int   catIDmax = 0;
+static int  *catIDseq = NULL;
+static int  *objIDmax = NULL;
+static int **objIDseq = NULL;
+
+int indexCatalogs (Catalog *catalog, int Ncatalog) {
+
+  int i;
+  off_t j;
+
+  // find the max value of catID
+  for (i = 0; i < Ncatalog; i++) {
+    catIDmax = MAX (catalog[i].catID, catIDmax);
+  }
+
+  ALLOCATE (catIDseq, int, catIDmax + 1);
+  for (i = 0; i < catIDmax + 1; i++) {
+    catIDseq[i] = -1;
+  }
+
+  for (i = 0; i < Ncatalog; i++) {
+    int catID = catalog[i].catID;
+    catIDseq[catID] = i;
+  }
+  
+  ALLOCATE (objIDmax, int,   Ncatalog);
+  ALLOCATE (objIDseq, int *, Ncatalog);
+  for (i = 0; i < Ncatalog; i++) {
+    objIDmax[i] = 0;
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      objIDmax[i] = MAX (catalog[i].averageT[j].objID, objIDmax[i]);
+    }
+
+    ALLOCATE (objIDseq[i], int, objIDmax[i] + 1);
+    for (j = 0; j < objIDmax[i] + 1; j++) {
+      objIDseq[i][j] = -1;
+    }
+
+    for (j = 0; j < catalog[i].Naverage; j++) {
+      int objID = catalog[i].averageT[j].objID;
+      objIDseq[i][objID] = j;
+    }
+  }
+  return TRUE;
+}
+
+int catID_and_objID_to_seq (int catID, int objID, int *catSeq, off_t *objSeq) {
+
+  if (catID > catIDmax) return FALSE;
+
+  int cat = catIDseq[catID];
+  if (cat < 1) return FALSE;
+
+  if (objID > objIDmax[cat]) return FALSE;
+
+  int obj = objIDseq[cat][objID];
+  if (obj < 1) return FALSE;
+
+  *catSeq = cat;
+  *objSeq = obj;
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/initialize.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/initialize.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/initialize.c	(revision 36680)
@@ -1,16 +1,13 @@
 # include "relphot.h"
 
-void initialize (int argc, char **argv) {
+RelphotMode initialize (int argc, char **argv) {
 
   relphot_help (argc, argv);
   ConfigInit (&argc, argv);
-  args (argc, argv);
+  RelphotMode mode = args (argc, argv);
+  if (!mode) exit (2);
 
-  if (!UpdateAverages) {
-    // load the list of photcodes into the globals (photcodes, Nphotcodes)
-    // only a single remaining argument in this mode (the list of photcodes, eg g,r,i)
-    PhotcodeList = strcreate (argv[1]);
-    photcodes = ParsePhotcodeList (PhotcodeList, &Nphotcodes, TRUE); // require SEC photcodes
-  } else {
+  // UPDATE_AVERAGES always operates on all photcodes? 
+  if (mode == UPDATE_AVERAGES) {
     char tmpline1[256];
     int Ns;
@@ -67,4 +64,6 @@
   for (B = 0; A == time(NULL); B++);
   srand48(B);
+
+  return mode;
 }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/launch_region_hosts.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/launch_region_hosts.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/launch_region_hosts.c	(revision 36680)
@@ -0,0 +1,117 @@
+# include "relphot.h"
+# define DEBUG 0
+
+int strextend (char *input, char *format,...) {
+
+  char tmpextra[1024], tmpline[1024];
+  va_list argp;
+
+  va_start (argp, format);
+  vsnprintf (tmpextra, 1024, format, argp);
+  snprintf (tmpline, 1024, "%s %s", input, tmpextra);
+  strcpy (input, tmpline);
+
+  return TRUE;
+}
+
+int launch_region_hosts (RegionHostTable *regionHosts) {
+
+  int i;
+
+  // clear the I/O files
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.sync");
+    truncate (syncfile, 0);
+    free (syncfile);
+
+    char *fitsfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.fits");
+    truncate (fitsfile, 0);
+    free (fitsfile);
+
+    char *imsyncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.sync");
+    truncate (imsyncfile, 0);
+    free (imsyncfile);
+
+    char *imfitsfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.fits");
+    truncate (imfitsfile, 0);
+    free (imfitsfile);
+
+    char *loopsyncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "loop.sync");
+    truncate (loopsyncfile, 0);
+    free (loopsyncfile);
+  }
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+
+    RegionHostInfo *host = &regionHosts->hosts[i];
+
+    // communication files:
+    // subset images per host : CATDIR/Image.HOSTNAME.fits
+    char filename[1024];
+    snprintf (filename, 1024, "%s/Image.%d.fits", CATDIR, host->hostID);
+
+    // write the image subset for this host
+    ImageTableSave (filename, host->image, host->Nimage);
+
+    char command[1024];
+    snprintf (command, 1024, "relphot %s -parallel-images %s -region-hosts %s -region-hostID %d -D CATDIR %s -region %f %f %f %f -statmode %s -D CAMERA %s -D STAR_TOOFEW %d -minerror %f", 
+	      PhotcodeList, filename, REGION_FILE, host->hostID, CATDIR, host->RminCat, host->RmaxCat, host->DminCat, host->DmaxCat, STATMODE, CAMERA, STAR_TOOFEW, MIN_ERROR);
+
+    if (VERBOSE)       	     strextend (command, "-v");
+    if (VERBOSE2)      	     strextend (command, "-vv");
+    if (RESET)         	     strextend (command, "-reset");
+    if (RESET_ZEROPTS) 	     strextend (command, "-reset-zpts");
+    if (!KEEP_UBERCAL) 	     strextend (command, "-reset-ubercal");
+    if (DophotSelect)  	     strextend (command, "-dophot %d", DophotValue);
+    if (ImagSelect)    	     strextend (command, "-instmag %f %f", ImagMin, ImagMax);
+    if (MaxDensityUse) 	     strextend (command, "-max-density %f", MaxDensityValue);
+    if (SyntheticPhotometry) strextend (command, "-synthphot");
+
+    if (UPDATE)        	     strextend (command, "-update");
+    if (MOSAIC_ZEROPT) 	     strextend (command, "-mosaic");
+    if (FREEZE_IMAGES) 	     strextend (command, "-imfreeze");
+    if (FREEZE_MOSAICS)	     strextend (command, "-mosfreeze");
+    if (PARALLEL)      	     strextend (command, "-parallel");
+    if (PARALLEL_MANUAL)     strextend (command, "-parallel-manual");
+    if (PARALLEL_SERIAL)     strextend (command, "-parallel-serial");
+
+    // XXX deprecate this if we are happy with the new version
+    if (SET_MREL_VERSION != 1) strextend (command, "-set-mrel-version %d", SET_MREL_VERSION);
+
+    fprintf (stderr, "command: %s\n", command);
+    
+    if (PARALLEL_REGIONS_MANUAL) continue;
+
+    // launch the job, then wait for it to be done loading catalogs.  force the remote
+    // client to generate the file
+    char *syncfile = make_filename (CATDIR, host->hostname, host->hostID, "loadcat.sync");
+    clear_sync_file (syncfile);
+
+    // launch the job on the remote machine (no handshake)
+    int errorInfo = 0;
+    int pid = rconnect ("ssh", host->hostname, command, host->stdio, &errorInfo, FALSE);
+    if (!pid) {
+      if (DEBUG) fprintf (stderr, "failure to start %s (error %d)\n", host->hostname, errorInfo);
+      exit (1);
+    }
+    host->pid = pid; // save for future reference
+
+    // remote client is done, go ahead with next client
+    check_sync_file (syncfile, 1);
+    free (syncfile);
+  }
+
+  if (PARALLEL_REGIONS_MANUAL) {
+    fprintf (stderr, "run the relphot_client commands above.  when these are done, hit return\n");
+    getchar();
+  } else {
+    RegionHostTableWaitJobsGetIO (regionHosts, __FILE__, __LINE__, VERBOSE);
+  }
+ 
+  int status = TRUE;
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    status = status && (regionHosts->hosts[i].status == 0);
+  }
+
+  return status;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/load_catalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/load_catalogs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/load_catalogs.c	(revision 36680)
@@ -89,7 +89,6 @@
 
   fprintf (stderr, "using "OFF_T_FMT" of "OFF_T_FMT" stars ("OFF_T_FMT" of "OFF_T_FMT" measurements)\n", Nstar, Nstar_total, Nmeas, Nmeas_total);
-  if (!hostID && (Nstar < 1)) Shutdown ("%s", "ERROR: no stars match the minimum requirements; exiting \n");
+  if (!hostID && !REGION_HOST_ID && (Nstar < 1)) Shutdown ("%s", "ERROR: no stars match the minimum requirements; exiting \n");
   // in regular relphot, we shutdown here; in relphot_client, we generate and return an empty table (for consistency)
-   
 
   // XXX consider only returning the populated catalogs
@@ -107,4 +106,9 @@
 # define DEBUG 1
 Catalog *load_catalogs_parallel (SkyList *sky, int *Ncatalog) {
+
+  char uniquer[12];
+  int TIME = time(NULL);
+  int PID = getpid();
+  snprintf (uniquer, 12, "%05d.%05d", PID, TIME % 100000);
 
   int Nsecfilt  = GetPhotcodeNsecfilt ();               // set the desired number in case we need to create the catalog
@@ -128,5 +132,5 @@
 
     ALLOCATE (table->hosts[i].results, char, 1024);
-    snprintf (table->hosts[i].results, 1024, "%s/relphot.catalog.subset.dat", table->hosts[i].pathname);
+    snprintf (table->hosts[i].results, 1024, "%s/relphot.catalog.%s.dat", table->hosts[i].pathname, uniquer);
 
     // options / arguments that can affect relphot_client -load:
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/reload_catalogs.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/reload_catalogs.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/reload_catalogs.c	(revision 36680)
@@ -156,4 +156,8 @@
   // load the list of hosts
   HostTable *table = HostTableLoad (CATDIR, sky->hosts);
+  if (!table) {
+    fprintf (stderr, "ERROR: problem with parallel host table\n");
+    exit (2);
+  }
 
   if (BOUNDARY_TREE) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot.c	(revision 36680)
@@ -3,26 +3,42 @@
 int main (int argc, char **argv) {
 
-  /* get configuration info, args */
-  initialize (argc, argv);
+  // get configuration info, args
+  RelphotMode mode = initialize (argc, argv);
+  if (!mode) exit (2);
 
-  /* the object analysis is a separate process iterating over catalogs */
-  if (UpdateAverages) {
-    relphot_objects (0, NULL);
-    exit (0);
+  switch (mode) {
+    case UPDATE_IMAGES:
+      relphot_images ();
+      exit (0);
+
+    case UPDATE_AVERAGES:
+      // take the current set of detections and set the mean magnitudes
+      relphot_objects (0, NULL);
+      exit (0);
+
+    case PARALLEL_REGIONS:
+      // run image updates in parallel across multiple remote machines
+      relphot_parallel_regions ();
+      exit (0);
+
+    case PARALLEL_IMAGES:
+      // operation on the remote machines in the PARALLEL_REGION mode
+      relphot_parallel_images ();
+      exit (0);
+
+    case APPLY_OFFSETS:
+      // re-run this step from a previous attempt (assumes an existing Images.subset.dat file)
+      if (!PARALLEL) {
+	fprintf (stderr, "-apply-offsets only makes sense in an parallel context\n");
+	exit (2);
+      }
+      reload_catalogs (NULL, NULL, 0, NULL);
+      exit (0);
+
+    default:
+      fprintf (stderr, "ERROR: no valid relphot mode chosen\n");
+      exit (2);
   }
-
-  if (ApplyOffsets) {
-    // re-run this step from a previous attempt (assumes an existing Images.subset.dat file)
-    if (!PARALLEL) {
-      fprintf (stderr, "-apply-offsets only makes sense in an parallel context\n");
-      exit (2);
-    }
-    reload_catalogs (NULL, NULL, 0, NULL);
-    exit (0);
-  }
-
-  relphot_images ();
-
-  exit (0);
+  fprintf (stderr, "IMPOSSIBLE: skipped out of switch?\n");
+  exit (1);
 }
-
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_client.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_client.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_client.c	(revision 36680)
@@ -19,5 +19,5 @@
   // get configuration info, args, lockfile (set CATDIR, HOST_ID, HOSTDIR, etc) 
   initialize_client (argc, argv);
-  client_logger_init ();
+  client_logger_init (HOSTDIR);
 
   // load the current sky table (layout of all SkyRegions) 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_images.c	(revision 36680)
@@ -244,5 +244,7 @@
   if (!UPDATE) exit (0);
   
-  /* load catalog data from region files, update Mrel include all data */
+  /* Load catalog data from region files, update Mrel include all data.  In a parallel
+     context, this function writes the image parameters as a subset table for the remote
+     clients */
   reload_catalogs (skylist, flatcorr, 0, NULL);
   MARKTIME("-- updated all catalogs: %f sec\n", dtime);
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_images.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_images.c	(revision 36680)
@@ -0,0 +1,165 @@
+# include "relphot.h"
+
+/* This function is essentially identical to relphot_images, except:
+
+ * load the subset images saved by the master node
+ * distinguish detections we own (touch our images) and those we don't
+ * distinguish objects we own (in region) and those we don't
+ * update the unowned detections for owned objects from neighbor regions
+ * update the unowned objects for owned detections 
+ */
+
+int dumpObjects (char *filename, Catalog *catalog, int Ncatalog);
+
+int relphot_parallel_images () {
+
+  int i, Ncatalog;
+  Catalog *catalog = NULL;
+
+  INITTIME;
+
+  client_logger_init (CATDIR);
+
+  // load the RegionTable (UserRegion should not be used at this level)
+  RegionHostTable *regionHosts = RegionHostTableLoad (CATDIR, REGION_FILE);
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  RegionHostFindNeighbors (regionHosts, myHost);
+  client_logger_message ("started parallel images on %s\n", regionHosts->hosts[myHost].hostname);
+
+  // load the subset images belonging to this host
+  off_t Nimage;
+  Image *image = ImageTableLoad (IMAGE_TABLE, &Nimage);
+  if (!image) {
+    fprintf (stderr, "ERROR loading image %s\n", IMAGE_TABLE);
+    exit (2);
+  }
+
+  // once we have read this table, we should remove it for repeat runs
+  // unlink (IMAGE_TABLE); // XXX a bit risky, add some protection?
+
+  // XXX need to deal with mosaic vs image...
+  makeMosaics (image, Nimage);
+
+  initImages (image, NULL, Nimage);
+
+  // load the flat correction table (if defined)
+  char flatcorrfile[256];
+  sprintf (flatcorrfile, "%s/flatcorr.fits", CATDIR);
+  FlatCorrectionTable *flatcorr = FlatCorrectionLoad (flatcorrfile, VERBOSE);
+
+  SkyTable *sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+
+  // UserPatch.Rmin,Rmax may have range from a few degrees < 0.0 to few degrees > 360.0.
+  // the following function correctly chooses the sky regions on the 0,360 boundary
+  SkyList *skylist = SkyListByBounds (sky, -1, UserPatch.Rmin, UserPatch.Rmax, UserPatch.Dmin, UserPatch.Dmax);
+
+  /* load catalog data from region files (hostID is 0 since we are not a client */
+  catalog = load_catalogs (skylist, &Ncatalog, 0, NULL);
+  MARKTIME("-- load catalog data: %f sec\n", dtime);
+  client_logger_message ("loaded catalog data\n");
+
+  // NOTE: if I let all hosts load blindly, I saturate the data clients with too many
+  // relphot_client requests.  I need to have the master mediate this.  the master
+  // will not launch the next remote job until this one says it is done
+  char *syncfile = make_filename (CATDIR, regionHosts->hosts[myHost].hostname, REGION_HOST_ID, "loadcat.sync");
+  update_sync_file (syncfile, 1);
+  
+  // generate tables go from catID,objID -> catSeq,objSeq
+  indexCatalogs (catalog, Ncatalog);
+  client_logger_message ("indexed Catalogs\n");
+
+  /* match measurements with images, mosaics */
+  initImageBins  (catalog, Ncatalog, TRUE);
+  MARKTIME("-- make image bins: %f sec\n", dtime);
+
+  initMosaicBins (catalog, Ncatalog, TRUE);
+  initMrel (catalog, Ncatalog);
+
+  findImages (catalog, Ncatalog, TRUE);
+  MARKTIME("-- set up image indexes: %f sec\n", dtime);
+
+  findMosaics (catalog, Ncatalog, TRUE);  /* also sets Grid values */
+  MARKTIME("-- set up mosaic indexes: %f sec\n", dtime);
+
+  client_logger_message ("done setting up indexes\n");
+
+  // dumpObjects ("test.obj.dat", catalog, Ncatalog);
+  markObjects (catalog, Ncatalog);
+
+  SAVEPLOT = FALSE;
+
+  setExclusions (catalog, Ncatalog, TRUE);
+
+  global_stats (catalog, Ncatalog, flatcorr);
+
+  if (PLOTSTUFF) {
+    plot_star_coords (catalog, Ncatalog);
+    // plot_mosaic_fields (catalog);
+  }
+
+  // XXX : add this in the loop at various points
+  // if (PLOTSTUFF) plot_scatter (catalog, Ncatalog, flatcorr); 
+
+  /* determine fit values */
+  client_logger_message ("starting the loops : %d \n", NLOOP);
+  for (i = 0; i < NLOOP; i++) {
+
+    // set the mean stellar mags given the measurements and the image calibrations
+    setMrel  (catalog, Ncatalog, flatcorr);
+
+    // share mean mags for objects at the boundary (number of unowned meas > 0)
+    share_mean_mags (catalog, Ncatalog, regionHosts, i);
+    client_logger_message ("shared mean mag data : loop %d \n", i);
+
+    // load mean mags from other region hosts
+    slurp_mean_mags (catalog, Ncatalog, regionHosts, i);
+    client_logger_message ("slurped mean mag data : loop %d \n", i);
+
+    // set the image (Mcal) and mosaic (Mmos) zero point offsets given the mean mags and measurements
+    setMcal  (catalog, FALSE, flatcorr);
+    setMmos  (catalog, FALSE, flatcorr);
+    MARKTIME("-- set Mrel, Mcal, Mmos, Mgrid : %f sec\n", dtime);
+    
+    // share image mags for images with non-zero unowned detections
+    share_image_mags (regionHosts, i);
+    client_logger_message ("shared image data : loop %d \n", i);
+
+    slurp_image_mags (regionHosts, i);
+    client_logger_message ("slurped image data : loop %d \n", i);
+
+    if ((i > 8) && (i % 8 == 2)) clean_measures (catalog, Ncatalog, FALSE, flatcorr); 
+    if ((i > 8) && (i % 8 == 3)) clean_stars (catalog, Ncatalog);
+    if ((i > 8) && (i % 8 == 5)) clean_mosaics ();
+    if ((i > 8) && (i % 8 == 5)) clean_images ();
+
+    if (i % 3 == 2) global_stats (catalog, Ncatalog, flatcorr);
+    MARKTIME("-- finished loop %d: %f sec\n", i, dtime);
+  }
+  client_logger_message ("done with loops\n");
+
+  // this is a checkpoint to make sure all hosts have finished the loop above
+  char *loopsyncfile = make_filename (CATDIR, regionHosts->hosts[myHost].hostname, REGION_HOST_ID, "loop.sync");
+  update_sync_file (loopsyncfile, 0);
+  free (loopsyncfile);
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    char *loopsync = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "loop.sync");
+    check_sync_file (loopsync, 0);
+    free (loopsync);
+  }    
+  client_logger_message ("all hosts are done the loops\n");
+
+  /* set Mcal & Mmos for bad images */
+  setMcal  (catalog, TRUE, flatcorr);
+  setMmos  (catalog, TRUE, flatcorr);
+  MARKTIME("-- finalize Mcal values: %f sec\n", dtime);
+
+  setMcalFinal (); // copy per-mosaic calibrations to the images
+
+  share_image_mags (regionHosts, -1);
+  client_logger_message ("done with parallel images\n");
+
+  exit (0);
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_regions.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_regions.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/relphot_parallel_regions.c	(revision 36680)
@@ -0,0 +1,75 @@
+# include "relphot.h"
+
+int relphot_parallel_regions () {
+
+  int status;
+  FITS_DB db;
+
+  INITTIME;
+
+  // load the RegionTable (UserRegion should not be used at this level)
+  RegionHostTable *regionHosts = RegionHostTableLoad (CATDIR, REGION_FILE);
+  if (!regionHosts) {
+    fprintf (stderr, "ERROR: problem with region host table\n");
+    exit (2);
+  }
+
+  // load the flat correction table (if defined)
+  char flatcorrfile[256];
+  sprintf (flatcorrfile, "%s/flatcorr.fits", CATDIR);
+  FlatCorrectionTable *flatcorr = FlatCorrectionLoad (flatcorrfile, VERBOSE);
+
+  // register database handle with shutdown procedure
+  set_db (&db);
+  db.mode   = dvo_catalog_catmode (CATMODE);
+  db.format = dvo_catalog_catformat (CATFORMAT);
+
+  // lock and load the image db table
+  status = dvo_image_lock (&db, ImageCat, 60.0, (UPDATE ? LCK_XCLD : LCK_SOFT));
+  if (!status && UPDATE) {
+    fprintf (stderr, "error\n");
+    Shutdown ("ERROR: failure to lock image catalog %s", db.filename);
+  }
+
+  if ((db.dbstate == LCK_EMPTY) || (db.dbstate == LCK_MISSING)) {
+    // if the Image.dat file is missing, db.dbstate will have a value of either:
+    // LCK_EMPTY (if UPDATE) or LCK_MISSING (if !UPDATE)
+    Shutdown ("ERROR: database %s contains no image data", CATDIR);
+  }
+
+  // read data from Image.dat file
+  if (!dvo_image_load (&db, VERBOSE, FALSE)) Shutdown ("can't read image catalog %s", db.filename);
+  MARKTIME("-- load image data: %f sec\n", dtime);
+
+  /* assign the images to the different region hosts */
+  if (!assign_images (&db, regionHosts)) Shutdown ("error assigning images to region hosts");
+  MARKTIME("-- assign images: %f sec\n", dtime);
+
+  /* launch processing on the parallel region hosts */
+  if (!launch_region_hosts (regionHosts)) Shutdown ("error launching region hosts");
+
+  // retrieve updated image parameters from the remote hosts (also set Image.mcal)
+  if (!slurp_image_mags (regionHosts, -1)) Shutdown ("error loading image updates");
+
+  if (!UPDATE) { 
+    dvo_image_unlock (&db); 
+    MARKTIME ("finished relphot -parallel-regions: %f sec total\n", dtime);
+    fprintf (stderr, "NOTE: UPDATE is OFF (results are not saved)\n");
+    exit (0);
+  }
+
+  SkyTable *sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  SkyList *skylist = SkyListByBounds (sky, -1, regionHosts->Rmin, regionHosts->Rmax, regionHosts->Dmin, regionHosts->Dmax);
+
+  /* update catalogs (in parallel) */
+  reload_catalogs (skylist, flatcorr, 0, NULL);
+
+  // save the changes to the image parameters
+  dvo_image_save (&db, VERBOSE);
+  dvo_image_unlock (&db); 
+  MARKTIME ("finished relphot -parallel-regions: %f sec total\n", dtime);
+
+  exit (0);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/select_images.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/select_images.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/select_images.c	(revision 36680)
@@ -40,5 +40,5 @@
   tcoords.pc1_1  = tcoords.pc2_2 = 1.0;
   tcoords.pc1_2  = tcoords.pc2_1 = 0.0;
-  strcpy (tcoords.ctype, "RA---TAN");
+  strcpy (tcoords.ctype, "DEC--TAN");
 
   double RminSkyRegion = region[0].Rmin;
@@ -412,5 +412,5 @@
   tcoords.Npolyterms = 0;
   memset (tcoords.polyterms, 0, 14*sizeof(float));
-  strcpy (tcoords.ctype, "RA---TAN");
+  strcpy (tcoords.ctype, "DEC--TAN");
 
   /* compare with each region file */
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/setMrelCatalog.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/setMrelCatalog.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/setMrelCatalog.c	(revision 36680)
@@ -34,5 +34,5 @@
   off_t j;
 
-  liststats_setmode (&results->stats, STATMODE);
+  liststats_setmode (&results->psfstats, STATMODE);
   liststats_setmode (&results->apstats, STATMODE);
   liststats_setmode (&results->kronstats, STATMODE);
@@ -67,16 +67,23 @@
 
   off_t k;
-  int N;
   float Msys = 0, Mcal= 0, Mmos = 0, Mgrid = 0;
 
-  double *list      = results->list;
-  double *dlist     = results->dlist;
-  double *wlist     = results->wlist;
-  double *aplist    = results->aplist;
-  double *kronlist  = results->kronlist;
+  // we are measuring means for 3 types of mags: psf, ap, kron.  I am using the psf mag
+  // error for the ap mags, but krons have their own errors.  it is an open question if I
+  // should be doing weighted or unweighted fits (this is a user option)
+  double *Mpsflist  = results->Mpsflist;
+  double *dpsflist  = results->dpsflist;
+  double *wpsflist  = results->wpsflist;
+
+  double *Maplist   = results->Maplist;
+  double *daplist   = results->daplist;
+  double *waplist   = results->waplist;
+
+  double *Mkronlist = results->Mkronlist;
   double *dkronlist = results->dkronlist;
-
-  StatType *stats = &results->stats;
-  StatType *apstats = &results->apstats;
+  double *wkronlist = results->wkronlist;
+
+  StatType *psfstats  = &results->psfstats;
+  StatType *apstats   = &results->apstats;
   StatType *kronstats = &results->kronstats;
 
@@ -125,5 +132,7 @@
     off_t meas = measureOffset;
 
-    N = 0;
+    int Nap = 0;
+    int Npsf = 0;
+    int Nkron = 0;
     for (k = 0; k < averageT[0].Nmeasure; k++, meas++) {
 
@@ -135,5 +144,5 @@
 
       // are we a PS1 exposure photcode?
-      if ((measure[k].photcode >= 10000) && (measure[k].photcode <= 10500)) {
+      if ((measureT[k].photcode >= 10000) && (measureT[k].photcode <= 10500)) {
 	NexpPS1 ++;
       }
@@ -164,4 +173,9 @@
       int myUbercalDist = getUbercalDist(meas, cat);
       minUbercalDist = MIN(minUbercalDist, myUbercalDist);
+
+      int isUbercal = (measureT[k].dbFlags & ID_MEAS_PHOTOM_UBERCAL);
+      float dMpsf = MAX (hypot(measureT[k].dM, code->photomErrSys), MIN_ERROR);
+
+      if (isUbercal) haveUbercal = TRUE; // haveUbercal is set per secfilt, isUbercal is per measure
 
       if (isSetMrelFinal) {
@@ -231,5 +245,5 @@
 	  if (measureT[k].M < MaxMagForceSynth) {
 	    forceSynth = TRUE;
-	    forceSynthEntry = N;
+	    forceSynthEntry = Npsf;
 	  } else {
 	    if (pass < 4) {
@@ -240,12 +254,21 @@
 	}
 
-	// Map (aplist) and Mkron (kronlist,dkronlist) are used to calculate mean mags per filter
-	float Map = PhotAper (&measure[k]);
-	aplist[N] = Map - Mcal - Mmos - Mgrid;
-
-	float Mkron = PhotKron (&measure[k]);
-	kronlist[N] = Mkron - Mcal - Mmos - Mgrid;
-	dkronlist[N] = measure[k].dMkron;
-      }
+	// Map (Maplist) and Mkron (Mkronlist,dkronlist) are used to calculate mean mags per filter
+	float Map = PhotAperCat (&measure[k]);
+	if (!isnan(Map)) {
+	    Maplist[Nap] = Map - Mcal - Mmos - Mgrid;
+	    daplist[Nap] = dMpsf;
+	    waplist[Nap] = isUbercal ? 10.0 : 1.0;
+	    Nap ++;
+	}
+
+	float Mkron = PhotKronCat (&measure[k]);
+	if (!isnan(Mkron)) {
+	  Mkronlist[Nkron] = Mkron - Mcal - Mmos - Mgrid;
+	  dkronlist[Nkron] = measure[k].dMkron;
+	  wkronlist[Nkron] = isUbercal ? 10.0 : 1.0;
+	  Nkron ++;
+	}
+      } // if (isSetMrelFinal)
 
       // dlist gives the error per measurement, wlist gives the weight
@@ -255,23 +278,23 @@
       // 3) UBERCAL measurements can have their weight increased by a big factor to help tie down the averages
       // 4) some reference photcode of some kind can be specified as fixed and have a high weight
-      dlist[N] = MAX (hypot(measureT[k].dM, code->photomErrSys), MIN_ERROR);
-      wlist[N] = 1.0;
-      list[N]  = Msys - Mcal - Mmos - Mgrid;
-
-      // up-weight the ubercal values (or convergence can take a long time...)
-      if (measureT[k].dbFlags & ID_MEAS_PHOTOM_UBERCAL) {
-	haveUbercal = TRUE;
-	wlist[N] = 10.0;
-      }
+      Mpsflist[Npsf] = Msys - Mcal - Mmos - Mgrid;
+      dpsflist[Npsf] = dMpsf;
+      wpsflist[Npsf] = isUbercal ? 10.0 : 1.0;
+
+      // NOTE: 
+      // Msys is measure[i].M + zp corrections
+      // Mcal is image[j].Mcal
+      // Mmos and Mgrid are offsets for mosaic and grid
 
       // tie down reference photometry if the -refcode (code) option is selected
       // eg, -refcode g_SDSS
       // this probably makes no sense in the context of multifilter analysis
+      // XXX probably need to use the photocde table to assign reference mag weights.
       if (refPhotcode) {
 	if (code->code == refPhotcode->code) {
-	  wlist[N] = 100.0;
-	}
-      }
-      N++;
+	  wpsflist[Npsf] = 100.0;
+	}
+      }
+      Npsf ++;
     }
 
@@ -288,6 +311,6 @@
 
     // when performing the grid analysis, STAR_TOOFEW should be set to 1;
-    if (N < Nminmeas) { /* too few measurements */
-      // fprintf (f, "%10.6f %10.6f %d %d %d\n", averageT[0].R, averageT[0].D, measureT[0].imageID, N, STAR_TOOFEW); 
+    if (Npsf < Nminmeas) { /* too few measurements */
+      // fprintf (f, "%10.6f %10.6f %d %d %d\n", averageT[0].R, averageT[0].D, measureT[0].imageID, Npsf, STAR_TOOFEW); 
       secfilt[Nsec].flags |= ID_STAR_FEW;
       if (Ncode == 0) {
@@ -303,15 +326,15 @@
     if (forceSynth) {
       // use the single SYNTH value instead of the other mags here
-      myAssert ((forceSynthEntry < N) && (forceSynthEntry >= 0), "programming error");
-      list[0]  = list[forceSynthEntry];
-      dlist[0] = dlist[forceSynthEntry];
-      wlist[0] = wlist[forceSynthEntry];
-      N = 1;
-    }
-    liststats (list, dlist, wlist, N, stats);
-
-    secfilt[Nsec].M  = stats->mean;
-    secfilt[Nsec].dM = stats->error;
-    secfilt[Nsec].Xm = (stats->Nmeas > 1) ? 100.0*log10(stats->chisq + 1e-4) : NAN_S_SHORT;
+      myAssert ((forceSynthEntry < Npsf) && (forceSynthEntry >= 0), "programming error");
+      Mpsflist[0] = Mpsflist[forceSynthEntry];
+      dpsflist[0] = dpsflist[forceSynthEntry];
+      wpsflist[0] = wpsflist[forceSynthEntry];
+      Npsf = 1;
+    }
+    liststats (Mpsflist, dpsflist, wpsflist, Npsf, psfstats);
+
+    secfilt[Nsec].M  = psfstats->mean;
+    secfilt[Nsec].dM = psfstats->error;
+    secfilt[Nsec].Xm = (psfstats->Nmeas > 1) ? 100.0*log10(psfstats->chisq + 1e-4) : NAN_S_SHORT;
 
     // when running -averages, we have no information about the images, so we cannot set this
@@ -323,21 +346,21 @@
       found[Nsec] = TRUE;
 
-      secfilt[Nsec].Mstdev = 1000.0*stats->sigma; // Mstdev is in millimags (not enough space for more precision)
+      secfilt[Nsec].Mstdev = 1000.0*psfstats->sigma; // Mstdev is in millimags (not enough space for more precision)
       // secfilt[Nsec].Ncode = Ncode;
-      secfilt[Nsec].Nused = stats->Nmeas;
-
-      secfilt[Nsec].M_80 = 1000 * stats->Upper80;
-      secfilt[Nsec].M_20 = 1000 * stats->Lower20;
+      secfilt[Nsec].Nused = psfstats->Nmeas;
+
+      secfilt[Nsec].M_80 = 1000 * psfstats->Upper80;
+      secfilt[Nsec].M_20 = 1000 * psfstats->Lower20;
 
       // NOTE : use the modified weight for apmags as well as psf mags
-      liststats (aplist, dlist, wlist, N, apstats);
-      secfilt[Nsec].Map  = apstats->mean; 
-
-      liststats (kronlist, dkronlist, wlist, N, kronstats);
-      secfilt[Nsec].Mkron  = kronstats->mean; 
-      secfilt[Nsec].dMkron = kronstats->error; 
+      liststats (Maplist, daplist, waplist, Nap, apstats);
+      secfilt[Nsec].Map  = Nap > 0 ? apstats->mean : NAN; 
+
+      liststats (Mkronlist, dkronlist, wkronlist, Nkron, kronstats);
+      secfilt[Nsec].Mkron  = Nkron > 0 ? kronstats->mean  : NAN; 
+      secfilt[Nsec].dMkron = Nkron > 0 ? kronstats->error : NAN; 
 
       // NOTE: for 2MASS measurements, Next should be 1, as should N
-      if ((Next > 0) && (Next > 0.5*N)) {
+      if ((Next > 0) && (Next > 0.5*Npsf)) {
 	secfilt[Nsec].flags |= ID_SECF_OBJ_EXT;
       }
@@ -405,9 +428,9 @@
   float Msys = 0, Mcal= 0, Mmos = 0, Mgrid = 0;
 
-  // set the name of the primary skycell (this is used in a strcmp to match the skycells in stack detections)
-  // XXX drop BoundaryTreePrimaryCell(primaryCell, average[0].R, average[0].D);
+  // set the primary projection cell and skycell for this coordinate
+  int tessID    = -1;
   int projectID = -1;
   int skycellID = -1;
-  BoundaryTreePrimaryCellIDs(&projectID, &skycellID, average[0].R, average[0].D);
+  get_tess_ids(&tessID, &projectID, &skycellID, average[0].R, average[0].D);
 
   int NstackGood = 0;
@@ -477,5 +500,5 @@
       // which stack image should we use for the mean value?
       // if we request the primary (USE_TREE_FOR_PRIMARY), then find the min distances for data from the primary cell
-      if (MatchImageSkycellID (meas, cat, projectID, skycellID)) {
+      if (MatchImageSkycellID (meas, cat, tessID, projectID, skycellID)) {
 	float stackPrimaryOffset = getCenterOffset (meas, cat, &measure[k], &stackImageID);
 	if (stackPrimaryOffset < stackPrimaryOffsetMin) {
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_image_mags.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_image_mags.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_image_mags.c	(revision 36680)
@@ -0,0 +1,120 @@
+# include "relphot.h"
+
+// we are sharing image calibrations mags for all images which (a) I own and (b) which have unowned detections
+
+# define D_NIMAGEMAGS 1000
+int share_image_mags (RegionHostTable *regionHosts, int nloop) {
+
+  off_t i, Nimages;
+  Image *images = getimages (&Nimages, NULL);
+
+  off_t Nimage_mags = 0;
+  off_t NIMAGE_MAGS = D_NIMAGEMAGS;
+  
+  ImageMag *image_mags = NULL;
+  ALLOCATE (image_mags, ImageMag, NIMAGE_MAGS);
+
+  for (i = 0; i < Nimages; i++) {
+    // XXX does this image have missing detections (does someone else need it?)
+    // XXX : NOTE NEED TO FIX THIS: if (imageExtra[i].Nmiss == 0) continue;
+    
+    set_image_mags (&image_mags[Nimage_mags], &images[i]);
+    Nimage_mags ++;
+
+    CHECK_REALLOCATE (image_mags, ImageMag, NIMAGE_MAGS, Nimage_mags, D_NIMAGEMAGS);
+  }
+
+  // write out the image_mag fits table AND write state in some file
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  char *hostname = regionHosts->hosts[myHost].hostname;
+
+  char *imagfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "imagemags.fits");
+  ImageMagSave (imagfile, image_mags, Nimage_mags);
+  free (image_mags);
+  free (imagfile);
+
+  char *syncfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "imagemags.sync");
+  update_sync_file (syncfile, nloop);
+  free (syncfile);
+
+  return TRUE;
+}
+
+int slurp_image_mags (RegionHostTable *regionHosts, int nloop) {
+
+  off_t Nimage, i;
+  Image *images = getimages (&Nimage, NULL);
+
+  int Nimage_mags = 0;
+  ImageMag *image_mags = NULL;
+  ALLOCATE (image_mags, ImageMag, 1);
+
+  fprintf (stderr, "grabbing image mags from other hosts...\n");
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    if (REGION_HOST_ID && !regionHosts->hosts[i].isNeighbor) continue;
+
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.sync");
+    check_sync_file (syncfile, nloop);
+    free (syncfile);
+    
+    off_t Nsubset;
+    char *imagfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "imagemags.fits");
+    ImageMag *image_mags_subset = ImageMagLoad (imagfile, &Nsubset);
+    free (imagfile);
+
+    image_mags = merge_image_mags (image_mags, &Nimage_mags, image_mags_subset, Nsubset);
+  }
+
+  for (i = 0; i < Nimage_mags; i++) {
+    off_t seq = getImageByID (image_mags[i].imageID);
+    if (seq < 0) {
+      // XXX is this a problem? (no, other hosts don't know which images I own)
+      continue;
+    }
+    images[seq].Mcal  	    = image_mags[i].Mcal;
+    images[seq].dMcal  	    = image_mags[i].dMcal;
+    images[seq].dMagSys	    = image_mags[i].dMagSys;
+    images[seq].Xm  	    = image_mags[i].Xm;
+    images[seq].nFitPhotom  = image_mags[i].nFitPhotom;
+    images[seq].flags 	    = image_mags[i].flags;
+    images[seq].ubercalDist = image_mags[i].ubercalDist;
+  }
+  free (image_mags);
+
+  fprintf (stderr, "DONE grabbing image mags from other hosts\n");
+
+  return TRUE;
+}
+
+int set_image_mags (ImageMag *image_mags, Image *image) {
+
+  image_mags->Mcal  	  = image->Mcal;
+  image_mags->dMcal  	  = image->dMcal;
+  image_mags->dMagSys	  = image->dMagSys;
+  image_mags->Xm  	  = image->Xm;
+  image_mags->nFitPhotom  = image->nFitPhotom;
+  image_mags->flags 	  = image->flags;
+  image_mags->ubercalDist = image->ubercalDist;
+  image_mags->imageID     = image->imageID;
+
+  return TRUE;
+}
+
+ImageMag *merge_image_mags (ImageMag *target, int *ntarget, ImageMag *source, int Nsource) {
+
+  off_t i;
+
+  REALLOCATE (target, ImageMag, *ntarget + Nsource);
+  for (i = 0; i < Nsource; i++) {
+    off_t n = i + *ntarget;
+    target[n] = source[i];
+  }
+  
+  free (source);
+
+  *ntarget += Nsource;
+  return (target);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_mean_mags.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_mean_mags.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/share_mean_mags.c	(revision 36680)
@@ -0,0 +1,149 @@
+# include "relphot.h"
+// we are sharing mean mags for all objects which (a) I own and (b) which have unowned detections
+
+# define D_NMEANMAGS 10000
+int share_mean_mags (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop) {
+
+  int i;
+  off_t j;
+
+  off_t Nmeanmags = 0;
+  off_t NMEANMAGS = D_NMEANMAGS;
+
+  MeanMag *meanmags = NULL;
+  ALLOCATE (meanmags, MeanMag, NMEANMAGS);
+
+  int Ns;
+  int Nsecfilt = GetPhotcodeNsecfilt();
+
+  int myHost = regionHosts->index[REGION_HOST_ID];
+  double Rmin = regionHosts->hosts[myHost].Rmin;
+  double Rmax = regionHosts->hosts[myHost].Rmax;
+  double Dmin = regionHosts->hosts[myHost].Dmin;
+  double Dmax = regionHosts->hosts[myHost].Dmax;
+
+  // XXX skip some catalogs based on UserPatch?
+  for (i = 0; i < Ncatalog; i++) {
+    for (j = 0; j < catalog[i].Naverage; j++) {
+
+      // do I own this object? (in region range?)
+      if (catalog[i].averageT[j].R <  Rmin) continue;
+      if (catalog[i].averageT[j].R >= Rmax) continue;
+      if (catalog[i].averageT[j].D <  Dmin) continue;
+      if (catalog[i].averageT[j].D >= Dmax) continue;
+
+      // does this object have missing detections (does someone else need it?)
+      // XXX : sky objects without missing detections
+      // XXX watch out for detections which are not associated with an image (REF)
+      if (catalog[i].averageT[j].nOwn == catalog[i].averageT[j].Nmeasure) continue;
+
+      for (Ns = 0; Ns < Nphotcodes; Ns++) {
+	int thisCode = photcodes[Ns][0].code;
+	int Nsec = GetPhotcodeNsec(thisCode);
+	set_mean_mags (&meanmags[Nmeanmags], &catalog[i].averageT[j], &catalog[i].secfilt[Nsecfilt*j + Nsec], Nsec);
+	Nmeanmags ++;
+	CHECK_REALLOCATE (meanmags, MeanMag, NMEANMAGS, Nmeanmags, D_NMEANMAGS);
+      }
+    }
+  }
+
+  // write out the meanmag fits table AND write state in some file
+  char *hostname = regionHosts->hosts[myHost].hostname;
+
+  char *magsfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "meanmags.fits");
+  MeanMagSave (magsfile, meanmags, Nmeanmags);
+  free (meanmags);
+  free (magsfile);
+
+  char *syncfile = make_filename (CATDIR, hostname, REGION_HOST_ID, "meanmags.sync");
+  update_sync_file (syncfile, nloop);
+  free (syncfile);
+
+  return TRUE;
+}
+
+int slurp_mean_mags (Catalog *catalog, int Ncatalog, RegionHostTable *regionHosts, int nloop) {
+
+  off_t i;
+
+  int Nmeanmags = 0;
+  MeanMag *meanmags = NULL;
+  ALLOCATE (meanmags, MeanMag, 1);
+
+  fprintf (stderr, "grabbing mean object mags from other hosts...\n");
+
+  int Nsecfilt = GetPhotcodeNsecfilt();
+
+  for (i = 0; i < regionHosts->Nhosts; i++) {
+    // if (not_neighbor(host[i])) continue;
+    if (regionHosts->hosts[i].hostID == REGION_HOST_ID) continue;
+    if (REGION_HOST_ID && !regionHosts->hosts[i].isNeighbor) continue;
+
+    char *syncfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.sync");
+    check_sync_file (syncfile, nloop);
+    free (syncfile);
+    
+    off_t Nsubset = 0;
+    char *magsfile = make_filename (CATDIR, regionHosts->hosts[i].hostname, regionHosts->hosts[i].hostID, "meanmags.fits");
+    MeanMag *meanmagsSubset = MeanMagLoad (magsfile, &Nsubset);
+    free (magsfile);
+
+    // merge_mean_mags reallocs meanmags and frees the input meanmagsSubset
+    meanmags = merge_mean_mags (meanmags, &Nmeanmags, meanmagsSubset, Nsubset);
+  }
+
+  for (i = 0; i < Nmeanmags; i++) {
+    int objID = meanmags[i].objID;
+    int catID = meanmags[i].catID;
+
+    // set the mean mag
+    int catSeq;
+    off_t objSeq;
+    if (!catID_and_objID_to_seq (catID, objID, &catSeq, &objSeq)) {
+	// XXX what should I do if this does not match?
+	continue;
+    }
+
+    int ecode = GetPhotcodeEquivCodebyCode (meanmags[i].photcode);
+    if (ecode < 0) continue;
+    int Nsec = GetPhotcodeNsec (ecode);
+    if (Nsec < 0) continue;
+
+    catalog[catSeq].secfilt[objSeq*Nsecfilt + Nsec].M = meanmags[i].M;
+  }
+  free (meanmags);
+
+  fprintf (stderr, "DONE grabbing mean object mags from other hosts...\n");
+
+  return TRUE;
+}
+
+int set_mean_mags (MeanMag *meanmags, AverageTiny *average, SecFilt *secfilt, int Nsec) {
+
+  meanmags->M  = secfilt->M;
+  meanmags->dM = secfilt->dM;
+  meanmags->Xm = secfilt->Xm;
+  meanmags->Nsec = Nsec; // key to secfilt entry
+
+  meanmags->objID = average->objID;
+  meanmags->catID = average->catID;
+
+  return TRUE;
+}
+
+MeanMag *merge_mean_mags (MeanMag *target, int *ntarget, MeanMag *source, int Nsource) {
+
+  off_t i;
+
+  REALLOCATE (target, MeanMag, *ntarget + Nsource);
+  for (i = 0; i < Nsource; i++) {
+    off_t n = i + *ntarget;
+    target[n] = source[i];
+  }
+  
+  free (source);
+
+  *ntarget += Nsource;
+  return (target);
+}
+
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/syncfile.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/syncfile.c	(revision 36680)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/relphot/src/syncfile.c	(revision 36680)
@@ -0,0 +1,77 @@
+# include "relphot.h"
+# define MSG_LENGTH 12
+
+char *make_filename (char *dirname, char *hostname, int hostID, char *tailname) {
+
+  char tmp[10], *line; 
+  int Nchar = snprintf (tmp, 0, "%s/%s.%03d.%s", dirname, hostname, hostID, tailname);
+  
+  ALLOCATE (line, char, Nchar + 1);
+  snprintf (line, Nchar + 1, "%s/%s.%03d.%s", dirname, hostname, hostID, tailname);
+
+  return line;
+}
+
+int check_sync_file (char *filename, int nloop) {
+
+  char message[MSG_LENGTH];
+
+  FILE *f = NULL; 
+
+  while (TRUE) {
+
+    f = fopen (filename, "r");
+    if (!f) {
+      usleep (2000000);
+      continue;
+    }
+
+    // XXX MSG_LENGTH : 0 EOL byte?
+    int Nread = fread (message, 1, MSG_LENGTH, f);
+    if (Nread < MSG_LENGTH) {
+      fclose (f);
+      usleep (2000000);
+      continue;
+    }
+    fclose (f);
+
+    // message is of the form: NLOOP: %03d
+    int loop;
+    sscanf (message, "%*s %d", &loop);
+    if (loop != nloop) {
+      usleep (2000000);
+      continue;
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+int clear_sync_file (char *filename) {
+  // delete file contents
+  truncate (filename, 0);
+
+  return TRUE;
+}
+
+int update_sync_file (char *filename, int nloop) {
+
+  char message[MSG_LENGTH];
+
+  FILE *f = fopen (filename, "w");
+  if (!f) { 
+    fprintf (stderr, "failure to open sync file for write\n");
+    exit (4);
+  }
+
+  snprintf (message, MSG_LENGTH, "NLOOP: %03d\n", nloop);
+  
+  int Nwrite = fwrite (message, 1, MSG_LENGTH, f);
+  if (Nwrite != MSG_LENGTH) {
+    fprintf (stderr, "failure to write sync message\n");
+    exit (3);
+  }
+
+  fclose (f);
+  return TRUE;
+}
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/tools/src/medianfilter.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/tools/src/medianfilter.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/tools/src/medianfilter.c	(revision 36680)
@@ -96,14 +96,26 @@
     for (j = 0; j < Npixrd; j++, O++) {
       Nvalid = 0;
+      int debug = FALSE && (n == 5) && (j >= 1776779) && (j <= 1776789);
       for (k = 0; k < Nfiles; k++) {
 	v = (float *)tmpmatr[k].buffer;
+	if (debug) {
+	    fprintf (stderr, "%8.2f ", v[j]);
+	}
 	if (v[j] < MinValid) continue;
-	if (isnan(v[j])) continue;
-	if (isinf(v[j])) continue;
-	list[k] = v[j];
+	if (isnan(v[j])) {
+	    continue;
+	}
+	if (isinf(v[j])) {
+	    continue;
+	}
+	list[Nvalid] = v[j];
 	Nvalid ++;
       }
+      if (debug) fprintf (stderr, "\n");
       if (Nvalid == 0) {
-	*O = 0.0;
+	*O = NAN;
+	if (debug) {
+	    fprintf (stderr, "Nvalid is 0\n");
+	}
 	continue;
       }
@@ -111,9 +123,17 @@
       sum = 0;
       Nval = 0;
+      if (debug) fprintf (stderr, "list : ");
       for (k = fmin*Nvalid; k < fmax*Nvalid; k++) {
+	if (debug) {
+	    fprintf (stderr, "%8.2f ", list[k]);
+	}
 	sum += list[k];
 	Nval ++;
       }
       *O = (sum / Nval);
+      if (debug) {
+	  fprintf (stderr, " = %f / %f = %f\n", sum, Nval, *O);
+	  fprintf (stderr, "\n");
+      }
     }
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/find_image_sgroups.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/find_image_sgroups.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/find_image_sgroups.c	(revision 36680)
@@ -22,5 +22,5 @@
   coords.pc1_1 = coords.pc2_2 = 1.0;
   coords.pc1_2 = coords.pc2_1 = 0.0;
-  strcpy (coords.ctype, "RA---TAN");
+  strcpy (coords.ctype, "DEC--TAN");
   
   Ngroup = 0;
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_setphot.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_setphot.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_setphot.c	(revision 36680)
@@ -2,4 +2,5 @@
 int update_dvo_setphot_parallel (SkyTable *sky, Image *image, off_t Nimage);
 
+// XXX convert this to use the ImageSubset to save space
 int update_dvo_setphot (Image *image, off_t Nimage, FlatCorrectionTable *flatcorr) {
 
Index: branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_uniphot.c
===================================================================
--- branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_uniphot.c	(revision 35859)
+++ branches/eam_branches/ps2-tc3-20130727/Ohana/src/uniphot/src/update_dvo_uniphot.c	(revision 36680)
@@ -61,5 +61,5 @@
   coords.pc1_1 = coords.pc2_2 = 1.0;
   coords.pc1_2 = coords.pc2_1 = 0.0;
-  strcpy (coords.ctype, "RA---TAN");
+  strcpy (coords.ctype, "DEC--TAN");
   
   for (i = 0; i < pglob.gl_pathc; i++) {
