Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/Makefile
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/Makefile	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/Makefile	(revision 35287)
@@ -64,4 +64,6 @@
 $(SRC)/high_speed_objects.$(ARCH).o  \
 $(SRC)/high_speed_utils.$(ARCH).o  \
+$(SRC)/hpm_catalogs.$(ARCH).o  \
+$(SRC)/hpm_objects.$(ARCH).o  \
 $(SRC)/relastro_merge_source.$(ARCH).o  \
 $(SRC)/resort_catalog.$(ARCH).o  \
@@ -106,4 +108,6 @@
 $(SRC)/high_speed_objects.$(ARCH).o  \
 $(SRC)/high_speed_utils.$(ARCH).o  \
+$(SRC)/hpm_catalogs.$(ARCH).o  \
+$(SRC)/hpm_objects.$(ARCH).o  \
 $(SRC)/relastro_merge_source.$(ARCH).o  \
 $(SRC)/resort_catalog.$(ARCH).o  \
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/include/relastro.h
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/include/relastro.h	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/include/relastro.h	(revision 35287)
@@ -20,5 +20,5 @@
 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} FitTarget;
+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 {
@@ -442,2 +442,6 @@
 
 PhotCode **ParsePhotcodeList (char *rawlist, int *nphotcodes, int needAve);
+
+int hpm_catalogs (SkyTable *sky, SkyList *skylist, int hostID, char *hostpath);
+int hpm_catalogs_parallel (SkyList *skylist);
+int hpm_objects (SkyRegion *region, Catalog *catalog);
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/args.c
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/args.c	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/args.c	(revision 35287)
@@ -89,10 +89,20 @@
   if ((N = get_argument (argc, argv, "-high-speed"))) {
     // XXX include a parallax / no-parallax option
+    if (N >= argc - 5) usage();
+    FIT_TARGET = TARGET_HIGH_SPEED;
+    remove_argument (N, &argc, argv);
+    PHOTCODE_A_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    PHOTCODE_B_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    RADIUS = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    HIGH_SPEED_DIR = abspath(argv[N], DVO_MAX_PATH);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-hpm"))) {
     if (N >= argc - 3) usage();
-    FIT_TARGET = TARGET_HIGH_SPEED;
-    remove_argument (N, &argc, argv);
-    PHOTCODE_A_LIST = strcreate(argv[N]);
-    remove_argument (N, &argc, argv);
-    PHOTCODE_B_LIST = strcreate(argv[N]);
+    FIT_TARGET = TARGET_HPM;
     remove_argument (N, &argc, argv);
     RADIUS = atof(argv[N]);
@@ -106,6 +116,6 @@
     remove_argument (N, &argc, argv);
     PARALLEL_OUTPUT = TRUE;
-    if (FIT_TARGET != TARGET_HIGH_SPEED) {
-      fprintf (stderr, "-parallel-output only valid for -high-speed mode\n");
+    if ((FIT_TARGET != TARGET_HIGH_SPEED) && (FIT_TARGET != TARGET_HPM)) {
+      fprintf (stderr, "-parallel-output only valid for -high-speed or -hpm modes\n");
       exit (1);
     }
@@ -456,5 +466,5 @@
   if ((N = get_argument (argc, argv, "-high-speed"))) {
     // XXX include a parallax / no-parallax option
-    if (N >= argc - 3) usage_client();
+    if (N >= argc - 5) usage_client();
     FIT_TARGET = TARGET_HIGH_SPEED;
     remove_argument (N, &argc, argv);
@@ -466,4 +476,14 @@
     remove_argument (N, &argc, argv);
     HIGH_SPEED_DIR = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-hpm"))) {
+    if (N >= argc - 3) usage();
+    FIT_TARGET = TARGET_HPM;
+    remove_argument (N, &argc, argv);
+    RADIUS = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+    HIGH_SPEED_DIR = abspath(argv[N], DVO_MAX_PATH);
     remove_argument (N, &argc, argv);
   }
@@ -668,4 +688,5 @@
   fprintf (stderr, "       OR:    relastro -update-objects [options]\n");
   fprintf (stderr, "       OR:    relastro -high-speed [options]\n");
+  fprintf (stderr, "       OR:    relastro -hpm [options]\n");
   fprintf (stderr, "       OR:    relastro -merge-source [options]\n\n");
 
@@ -679,4 +700,5 @@
   fprintf (stderr, "  -update-mosaics\n");
   fprintf (stderr, "  -high-speed (code[,code,code]) (code[,code,code]) (radius) (output catdir)\n");
+  fprintf (stderr, "  -hpm (radius) (output catdir)\n");
   fprintf (stderr, "  -merge-source (objID) (catID) into (objID) (catID)\n\n");
 
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_catalogs.c
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_catalogs.c	(revision 35287)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_catalogs.c	(revision 35287)
@@ -0,0 +1,203 @@
+# include "relastro.h"
+
+int hpm_catalogs_parallel (SkyList *sky);
+
+int hpm_catalogs (SkyTable *sky, SkyList *skylist, int hostID, char *hostpath) {
+
+  int i;
+  Catalog catalog;
+
+  // tell libdvo the CATDIR
+  dvo_set_catdir(CATDIR);
+
+  // XXX need to decide how to determine PARALLEL mode...
+  if (PARALLEL && !hostID) {
+    hpm_catalogs_parallel (skylist);
+    goto finish;
+  }
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], hostID)) continue;
+
+    // set up the basic catalog info
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", hostpath, skylist[0].regions[i]->name);
+    catalog.filename  = hostID ? hostfile : skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE2, "r")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE2) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    hpm_objects (skylist[0].regions[i], &catalog);
+
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+  
+finish:
+
+  // in relastro (NOT relastr_client), save the Images.dat, Photcodes.dat, SkyTable.fits files
+  if (!hostID) {
+    char photcodeFile[1024];
+    snprintf (photcodeFile, 1024, "%s/Photcodes.dat", HIGH_SPEED_DIR);
+    if (!SavePhotcodesFITS (photcodeFile)) {
+      fprintf (stderr, "error saving photcode table %s\n", photcodeFile);
+      exit (1);
+    }
+    
+    // need to copy across the Images, SkyTable, and Photcode tables:
+    char *skyfile = SkyTableFilename (HIGH_SPEED_DIR);
+    SkyTableSave (sky, skyfile);
+    
+    char line[2048];
+    snprintf (line, 2048, "cp %s/Images.dat %s/Images.dat", CATDIR, HIGH_SPEED_DIR);
+    int status = system (line);
+    if (status) {
+      fprintf (stderr, "copy of Images.dat failed\n");
+      exit (3);
+    }
+  }
+
+  return (TRUE);
+}
+
+// CATDIR is supplied globally
+# define DEBUG 1
+int hpm_catalogs_parallel (SkyList *skylist) {
+
+  // launch the setphot_client jobs to the parallel hosts
+
+  // load the list of hosts
+  HostTable *table = HostTableLoad (CATDIR, skylist->hosts);
+  if (!table) {
+    fprintf (stderr, "ERROR: failure reading Host Table %s for database %s\n", skylist->hosts, CATDIR);
+    exit (1);
+  }    
+
+  // If we want to parallelize the output, the remote client needs to load the HostTable
+  // for the output catdir.  Require the output HostTable to match the input (ie, same
+  // hostIDs must exist).  Preferred, but not required, that they match in the host
+  // location.
+  HostTable *tableOut = NULL;
+  if (PARALLEL_OUTPUT) {
+    // load the list of hosts
+    tableOut = HostTableLoad (HIGH_SPEED_DIR, skylist->hosts);
+    if (!tableOut) {
+      fprintf (stderr, "ERROR: failure reading Host Table %s for output database %s\n", skylist->hosts, HIGH_SPEED_DIR);
+      exit (1);
+    }    
+    if (table->Nhosts != tableOut->Nhosts) {
+      fprintf (stderr, "ERROR: output HostTable must have matching hosts (%d in, %d out)\n", table->Nhosts, tableOut->Nhosts);
+      exit (1);
+    }
+  }
+
+  int i;
+  for (i = 0; i < table->Nhosts; i++) {
+
+    // ensure that the paths are absolute path names
+    char *tmppath = abspath (table->hosts[i].pathname, DVO_MAX_PATH);
+    free (table->hosts[i].pathname);
+    table->hosts[i].pathname = tmppath;
+
+    // use this directory to save the output catalogs (distributed if parallel)
+    char *outputDir = strcreate(HIGH_SPEED_DIR);
+    if (PARALLEL_OUTPUT) {
+      // find the matching host entry in the output HostTable (match by ID, not index)
+      int hostID = table->hosts[i].hostID;
+      int index = table->index[hostID];
+      if (index == -1) {
+	fprintf (stderr, "ERROR: output HostTable must have matching hosts (host ID %d not found)\n", hostID);
+	exit (1);
+      }
+
+      free (outputDir);
+      outputDir = abspath (tableOut->hosts[index].pathname, DVO_MAX_PATH);
+    }
+
+    char command[1024];
+    snprintf (command, 1024, "relastro_client -hpm %f %s -hostID %d -D CATDIR %s -hostdir %s -region %f %f %f %f", 
+	      RADIUS, outputDir, table->hosts[i].hostID, CATDIR, table->hosts[i].pathname, UserPatch.Rmin, UserPatch.Rmax, UserPatch.Dmin, UserPatch.Dmax);
+
+    free (outputDir);
+
+    // options / arguments that can affect relastro_client -high-speed
+    char tmpline[1024];
+    if (FIT_MODE == FIT_PM_ONLY)  	 { snprintf (tmpline, 1024, "%s -pm",    command);           	strcpy (command, tmpline); }
+    if (FIT_MODE == FIT_PAR_ONLY) 	 { snprintf (tmpline, 1024, "%s -par",   command);           	strcpy (command, tmpline); }
+    if (FIT_MODE == FIT_PM_AND_PAR)      { snprintf (tmpline, 1024, "%s -pmpar", command);           	strcpy (command, tmpline); }
+
+    if (VERBOSE)       { snprintf (tmpline, 1024, "%s -v",              command);                    	strcpy (command, tmpline); }
+    if (VERBOSE2)      { snprintf (tmpline, 1024, "%s -vv",             command); 		     	strcpy (command, tmpline); }
+    if (RESET)         { snprintf (tmpline, 1024, "%s -reset",          command); 		     	strcpy (command, tmpline); }
+    if (ImagSelect)    { snprintf (tmpline, 1024, "%s -instmag %f %f",  command, ImagMin, ImagMax);  	strcpy (command, tmpline); }
+    if (MaxDensityUse) { snprintf (tmpline, 1024, "%s -max-density %f", command, MaxDensityValue);   	strcpy (command, tmpline); }
+    
+    if (USE_BASIC_CHECK) { snprintf (tmpline, 1024, "%s -basic-image-search", command);              	strcpy (command, tmpline); }
+
+    if (FlagOutlier)   { snprintf (tmpline, 1024, "%s -clip %d",        command, CLIP_THRESH);       	strcpy (command, tmpline); }
+    
+    if (USE_FIXED_PIXCOORDS) { snprintf (tmpline, 1024, "%s -D USE_FIXED_PIXCOORDS 1", command);     strcpy (command, tmpline); }
+
+    if (PHOTCODE_KEEP_LIST) { snprintf (tmpline, 1024, "%s +photcode %s", command, PHOTCODE_KEEP_LIST); strcpy (command, tmpline); }
+    if (PHOTCODE_SKIP_LIST) { snprintf (tmpline, 1024, "%s -photcode %s", command, PHOTCODE_SKIP_LIST); strcpy (command, tmpline); }
+    if (PhotFlagSelect)    { snprintf (tmpline, 1024, "%s +photflags",   command);                      strcpy (command, tmpline); }
+    if (PhotFlagBad)       { snprintf (tmpline, 1024, "%s +photflagbad %d", command, PhotFlagBad);      strcpy (command, tmpline); }
+    if (PhotFlagPoor)      { snprintf (tmpline, 1024, "%s +photflagpoor %d", command, PhotFlagPoor);    strcpy (command, tmpline); }
+    // XXX note that the above pass in the flag as decimal -- also note that args.c cannot handle 0xHEX values
+
+    if (TimeSelect) { 
+      char *tstart = ohana_sec_to_date (TSTART);
+      char *tstop  = ohana_sec_to_date (TSTOP);
+      snprintf (tmpline, 1024, "%s -time %s %s", command, tstart, tstop); 
+      free (tstart);
+      free (tstop);
+      strcpy (command, tmpline); 
+    }
+
+    fprintf (stderr, "command: %s\n", command);
+
+    if (PARALLEL_MANUAL) continue;
+
+    if (PARALLEL_SERIAL) {
+      int status = system (command);
+      if (status) {
+	fprintf (stderr, "ERROR running relastro_client\n");
+	exit (2);
+      }
+    } else {
+      // launch the job on the remote machine (no handshake)
+      int errorInfo = 0;
+      int pid = rconnect ("ssh", table->hosts[i].hostname, command, table->hosts[i].stdio, &errorInfo, FALSE);
+      if (!pid) {
+	if (DEBUG) fprintf (stderr, "failure to start %s (error %d)\n", table->hosts[i].hostname, errorInfo);
+	exit (1);
+      }
+      table->hosts[i].pid = pid; // save for future reference
+    }
+  }
+
+  if (PARALLEL_MANUAL) {
+    fprintf (stderr, "run the relastro_client commands above.  when these are done, hit return\n");
+    getchar();
+  }
+  if (!PARALLEL_MANUAL && !PARALLEL_SERIAL) {
+    HostTableWaitJobsGetIO (table, __FILE__, __LINE__, VERBOSE);
+  }
+
+  return TRUE;
+}      
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_objects.c
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_objects.c	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/hpm_objects.c	(revision 35287)
@@ -1,3 +1,7 @@
 # include "relastro.h"
+# define MAX_TRANGE 180.0
+# define MIN_PS1_DET 3
+# define MAX_CHISQ_PM 10.0
+# define MIN_NPOS_FRAC 0.8
 
 # define NEXT_I { if (Ngroup < 2) slowMoving[ni] = TRUE; newI = TRUE; i++; continue; }
@@ -6,31 +10,26 @@
 int hpm_objects (SkyRegion *region, Catalog *catalog) {
 
-  off_t i, j, m, J, ni, nj, *N1;
-  off_t Nslow, Ninvalid, NgroupA, NgroupB, NgroupAbad, NgroupBbad;
-  off_t l, i1, NAVERAGE, NMEASURE;
-  int *slowMoving, *groupA, *groupB, status, Nmatch, Nmatchmeas, Nepoch, nv[2], Nmatchmeasobj;
-  int foundA, foundB, XVERB;
+  off_t i, j, J, ni, nj, *N1, k, m;
+  int *slowMoving;
   double *X1, *Y1;
   double dX, dY, dR, RADIUS2;
   Coords tcoords;
-  Catalog catalogOut;
-  Catalog testcat;
+  Catalog catalogOut, testcat;
 
   int Nsecfilt;
   char filename[1024];
 
-  // XXX are we saving these in an hpm dvodb?
+  // we need at least 2 objects if we are going to match anything...
+  if (catalog[0].Naverage < 2) return (TRUE);
+  if (VERBOSE) fprintf (stderr, "checking "OFF_T_FMT" objects\n",  catalog[0].Naverage);
+
+  // we save the best objects in an hpm dvodb
   snprintf (filename, 1024, "%s/%s.cpt", HIGH_SPEED_DIR, region[0].name);
-  fprintf (stderr, "%s\n",filename);
-
-  dvo_catalog_init (&catalogOut, TRUE); /*initialise new catalogue*/
+
+  dvo_catalog_init (&catalogOut, TRUE); /* init new catalog */
   catalogOut.filename = strcreate(filename);
 
-  SIGMA_LIM = 0.0;
-
   Nsecfilt = GetPhotcodeNsecfilt();
-  // off_t Naverage = catalog[0].Naverage;
-  // off_t Nmeasure = catalog[0].Nmeasure;
-  catalogOut.filename = filename; // based on the input name, need to keep everything below the catdir portion
+  catalogOut.filename = filename;
   catalogOut.Nsecfilt = Nsecfilt;
   catalogOut.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF; // load all data
@@ -45,9 +44,11 @@
   }
 
-  NAVERAGE = 1000;
-  NMEASURE = 10000;
+  off_t NAVERAGE = 1000;
+  off_t NMEASURE = 10000;
   REALLOCATE (catalogOut.average, Average, NAVERAGE);
   REALLOCATE (catalogOut.measure, Measure, NMEASURE);
   REALLOCATE (catalogOut.secfilt, SecFilt, NAVERAGE*Nsecfilt);
+  catalogOut.Naverage = 0;
+  catalogOut.Nmeasure = 0;
 
   // testcat is used to determine the fit for a single object group
@@ -55,26 +56,19 @@
   dvo_catalog_init (&testcat, TRUE);
   testcat.Naverage = 1; // this is fixed -- only one obj in testcat
-  NMEASURE_TEST = 1000;
+  off_t NMEASURE_TESTCAT = 1000;
   REALLOCATE (catalogOut.average, Average, 1);
-  REALLOCATE (catalogOut.measure, Measure, NMEASURE_TEST);
+  REALLOCATE (catalogOut.measure, Measure, NMEASURE_TESTCAT);
   REALLOCATE (catalogOut.secfilt, SecFilt, Nsecfilt);
-
-  // we need at least 2 objects if we are going to match anything...
-  if (catalog[0].Naverage < 2) return (TRUE);
 
   // mask with which to mark objects to be ignored
   ALLOCATE (slowMoving, int, catalog[0].Naverage);
   memset (slowMoving, 0, catalog[0].Naverage*sizeof(int));
-
-  if (VERBOSE) fprintf (stderr, "checking "OFF_T_FMT" objects\n",  catalog[0].Naverage);
-  Nslow = 0;
-  Ninvalid = 0;
-  NgroupAbad = 0;
-  NgroupBbad = 0;
-
-  // mark (exclude) objects with both sets of target photcodes
+  off_t Nslow = 0;
+
+  // mark (exclude) objects which do not meet the selection criterion
   for (i = 0; i < catalog[0].Naverage; i++) {
 
-    XVERB  = (catalog[0].average[i].objID == OBJ_ID_SRC) && (catalog[0].average[i].catID == CAT_ID_SRC);
+    int XVERB = FALSE;
+    XVERB |= (catalog[0].average[i].objID == OBJ_ID_SRC) && (catalog[0].average[i].catID == CAT_ID_SRC);
     XVERB |= (catalog[0].average[i].objID == OBJ_ID_DST) && (catalog[0].average[i].catID == CAT_ID_DST);
     if (XVERB) {
@@ -82,6 +76,5 @@
     }
 
-    // selection criteria:
-    // (Nps1 > 2) && (Trange < 180)
+    // selection criteria : (Nps1 > 2) && (Trange < 180)
     if (catalog[0].average[i].Trange > MAX_TRANGE) {
       slowMoving[i] = TRUE;
@@ -129,5 +122,5 @@
   /* build spatial index (RA sort) referencing input array sequence */
   for (i = 0; i < catalog[0].Naverage; i++) {
-    status = RD_to_XY (&X1[i], &Y1[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    int status = RD_to_XY (&X1[i], &Y1[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
     N1[i] = i;
     assert (status);
@@ -135,16 +128,15 @@
   sort_coords_index (X1, Y1, N1, catalog[0].Naverage);
 
-  Nmatch = 0;
-  Nmatchmeas = 0;
   RADIUS2 = SQ(RADIUS);
 
   // group is a list of objects that are within a clump.  this set will be tested
   // via a clipped fit to the measurements after the group is identified
-  Ngroup = 0;
-  NGROUP = 100;
+  int Ngroup = 0;
+  int NGROUP = 100;
+  int *group = NULL;
   ALLOCATE (group, int, NGROUP);
 
   // in the loop below, we need to do a bunch of things when we go to the next main object
-  newI = TRUE;
+  int newI = TRUE;
 
   // mark (skip) objects with both sets of target photcodes
@@ -159,5 +151,4 @@
 
     if (newI) {
-      Nmatch = 0; 
       Ngroup = 1;
       group[0] = ni;
@@ -165,5 +156,6 @@
     }
 
-    XVERB  = (catalog[0].average[ni].objID == OBJ_ID_SRC) && (catalog[0].average[ni].catID == CAT_ID_SRC);
+    int XVERB = FALSE;
+    XVERB |= (catalog[0].average[ni].objID == OBJ_ID_SRC) && (catalog[0].average[ni].catID == CAT_ID_SRC);
     XVERB |= (catalog[0].average[nj].objID == OBJ_ID_DST) && (catalog[0].average[nj].catID == CAT_ID_DST);
     if (XVERB) {
@@ -186,5 +178,5 @@
     if (dX >= +1.02*RADIUS) NEXT_J; // positive dX: j is too small
 
-    // within match range; look for valid matches
+    // within match range; look for valid matches & accumulate the group
     for (J = j; (dX > -1.02*RADIUS) && (J < catalog[0].Naverage); J++) {     
       if (J == i) continue;  // avoid auto-matches
@@ -219,19 +211,19 @@
     // the mean object will start with info from the primary object
     // remember: testcat.Naverage = 1 -- does not change
-    testcat.average[0] = catalog[0].average[group[0]];
+    ni = group[0];
+    int Nmatchmeas = 0;
+    testcat.average[0] = catalog[0].average[ni];
     testcat.average[0].measureOffset = 0;
-    Nmatchmeas = 0;
-    I = group[0];
     for (J = 0; J < Ngroup; J++) {
-      J = group[J];
-      m = catalog[0].average[Nj].measureOffset;
-      for (k = 0; k < catalog[0].average[Nj].Nmeasure; k++) {
+      nj = group[J];
+      m = catalog[0].average[nj].measureOffset;
+      for (k = 0; k < catalog[0].average[nj].Nmeasure; k++) {
 	testcat.measure[Nmatchmeas] = catalog[0].measure[m+k];
 	/* Set offset RA and Dec wrt correct average value*/
-	testcat.measure[Nmatchmeas].dR = catalog[0].measure[m+k].dR + 3600.0*(catalog[0].average[I].R - catalog[0].average[J].R);
-	testcat.measure[Nmatchmeas].dD = catalog[0].measure[m+k].dD + 3600.0*(catalog[0].average[I].D - catalog[0].average[J].D);
+	testcat.measure[Nmatchmeas].dR = catalog[0].measure[m+k].dR + 3600.0*(catalog[0].average[ni].R - catalog[0].average[nj].R);
+	testcat.measure[Nmatchmeas].dD = catalog[0].measure[m+k].dD + 3600.0*(catalog[0].average[ni].D - catalog[0].average[nj].D);
 	testcat.measure[Nmatchmeas].averef = 0;
 	Nmatchmeas++;
-	CHECK_REALLOCATE (testcat.measure, Measure, NMEASURE_TEST, Nmatchmeas, 1000);
+	CHECK_REALLOCATE (testcat.measure, Measure, NMEASURE_TESTCAT, Nmatchmeas, 1000);
       }
     }
@@ -244,41 +236,36 @@
     UpdateObjects (&testcat, 1);
 
-    // logic for keeping the fit:
-    if (testcat.average[0].ChiSqPM < XXX) good = TRUE;
-    if (testcat.average[0].Npos > 0.X * testcat.average[0].Nmeasure) good = TRUE;
-    // what else?
+    // logic for keeping the fit (anything else?)
+    int good = FALSE;
+    good |= (testcat.average[0].ChiSqPM < MAX_CHISQ_PM);
+    good |= (testcat.average[0].Npos > MIN_NPOS_FRAC * testcat.average[0].Nmeasure);
 
     if (good) {
       // save the new object on catalogOut
-      {
-	catalogOut.average[Nout] = testcat.average[0];
-	catalogOut.average[Nout].measureOffset = Nmatchmeas;
-	m = testcat.average[0].measureOffset;
-	for (k = 0; k < testcat.average[0].Nmeasure; k++) {
-	  catalogOut.measure[Noutmeas] = testcat.measure[m+k];
-	  catalogOut.measure[Noutmeas].averef = Nout;
-	}
-	Nout ++;
-	CHECK_REALLOCATE (catalogOut.average, Average, NAVERAGE, Nout, 100);
+      // (note: there is only one object in testcat and thus measureOffset = 0
+      catalogOut.average[catalogOut.Naverage] = testcat.average[0];
+      catalogOut.average[catalogOut.Naverage].measureOffset = catalogOut.Nmeasure;
+      for (k = 0; k < testcat.average[0].Nmeasure; k++) {
+	catalogOut.measure[catalogOut.Nmeasure] = testcat.measure[k];
+	catalogOut.measure[catalogOut.Nmeasure].averef = catalogOut.Naverage;
+	catalogOut.Nmeasure ++;
+	CHECK_REALLOCATE (catalogOut.measure, Measure, NMEASURE, catalogOut.Nmeasure, 100);
       }
+      catalogOut.Naverage ++;
+      CHECK_REALLOCATE (catalogOut.average, Average, NAVERAGE, catalogOut.Naverage, 100);
     }
     NEXT_I;
   }
-
-  catalogOut.Naverage=Nmatch;
-  catalogOut.Nmeasure=Nmatchmeas;
-  catalogOut.Nsecfilt=Nsecfilt;
-  catalogOut.Nsecf_mem=Nmatch*Nsecfilt;
+  catalogOut.Nsecfilt = Nsecfilt;
+  catalogOut.Nsecf_mem = Nsecfilt * catalogOut.Naverage;
 
   populate_tiny_values (&catalogOut, DVO_TV_MEASURE);
   UpdateObjects (&catalogOut, 1);
 
-  fprintf (stderr, "found %d matches\n", Nmatch);
+  fprintf (stderr, "found "OFF_T_FMT" matches with "OFF_T_FMT" detections\n", catalogOut.Naverage, catalogOut.Nmeasure);
   dvo_catalog_save (&catalogOut, VERBOSE2);
   dvo_catalog_unlock (&catalogOut);
   dvo_catalog_free (&catalogOut);
   free (slowMoving);
-  free (groupA);
-  free (groupB);
   free (X1);
   free (Y1);
@@ -287,31 +274,2 @@
   return TRUE;
 }
-
-// return TRUE if measure->photcode match any in the photcodeSet
-// (but, return FALSE if the measurement is bad)
-int MeasMatchesPhotcode(Measure *measure, PhotCode **photcodeSet, int Nset) {
-
-  int found, k;
-
-  if (!Nset) return TRUE;
-  // XXX require a set or not?  assert (Nset > 0);
-
-  if (!finite(measure[0].dR)) return FALSE;
-  if (!finite(measure[0].dD)) return FALSE;
-  if (!finite(measure[0].M))  return FALSE;
-  
-  float dX = GetAstromError (measure, ERROR_MODE_RA);
-  if (isnan(dX)) return FALSE;
-
-  float dY = GetAstromError (measure, ERROR_MODE_DEC);
-  if (isnan(dY)) return FALSE;
-
-  /* select measurements by photcode, or equiv photcode, if specified */
-  found = FALSE;
-  for (k = 0; (k < Nset) && !found; k++) {
-    if (photcodeSet[k][0].code == measure[0].photcode) found = TRUE;
-    if (photcodeSet[k][0].code == GetPhotcodeEquivCodebyCode(measure[0].photcode)) found = TRUE;
-  }
-
-  return found;
-}
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro.c
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro.c	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro.c	(revision 35287)
@@ -19,4 +19,8 @@
       /* 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:
+      hpm_catalogs (sky, skylist, 0, NULL);
       exit (0);
 
Index: /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro_client.c
===================================================================
--- /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro_client.c	(revision 35286)
+++ /branches/eam_branches/ipp-20130307/Ohana/src/relastro/src/relastro_client.c	(revision 35287)
@@ -58,4 +58,10 @@
     }
 
+    case TARGET_HPM: {
+      // USAGE: relastro_client -high-speed
+      hpm_catalogs (sky, skylist, HOST_ID, HOSTDIR);
+      break;
+    }
+
       // XXX loading the images is fairly costly -- see if we can do an image subset
     case TARGET_UPDATE_OFFSETS: {
