Index: trunk/Ohana/src/addstar/Makefile
===================================================================
--- trunk/Ohana/src/addstar/Makefile	(revision 41562)
+++ trunk/Ohana/src/addstar/Makefile	(revision 41563)
@@ -24,7 +24,9 @@
 sedstar      : $(BIN)/sedstar.$(ARCH)
 load2mass    : $(BIN)/load2mass.$(ARCH)
+loadatlas    : $(BIN)/loadatlas.$(ARCH)
 loadgalphot  : $(BIN)/loadgalphot.$(ARCH)
 loadgaia     : $(BIN)/loadgaia.$(ARCH)
 loadgaia_dr2 : $(BIN)/loadgaia_dr2.$(ARCH)
+loadgaia_edr3 : $(BIN)/loadgaia_edr3.$(ARCH)
 loadukirt_uhs : $(BIN)/loadukirt_uhs.$(ARCH)
 loadstarpar  : $(BIN)/loadstarpar.$(ARCH)
@@ -45,5 +47,5 @@
 # programs in 'SERVER' use the client-server concept and are out of date
 
-INSTALL = addstar addstar_client sedstar loadgalphot loadgaia loadgaia_dr2 loadukirt_uhs loadstarpar loadstarpar_client setobjflags setobjflags_client loadICRF loadICRF_client skycells mkcmf dumpskycells findskycell load2mass loadwise loadtycho loadbsc loadsupercos 
+INSTALL = addstar addstar_client sedstar loadgalphot loadgaia loadgaia_dr2 loadgaia_edr3 loadukirt_uhs loadstarpar loadstarpar_client setobjflags setobjflags_client loadICRF loadICRF_client skycells mkcmf dumpskycells findskycell load2mass loadwise loadtycho loadbsc loadsupercos loadatlas
 SERVER  = addstarc addstard addstart
 
@@ -306,4 +308,40 @@
 $(SRC)/psps_ids.$(ARCH).o
 
+LOAD-GAIA_EDR3 = \
+$(SRC)/loadgaia_edr3.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/args_loadgaia_edr3.$(ARCH).o \
+$(SRC)/find_matches_gaia_edr3.$(ARCH).o \
+$(SRC)/loadgaia_edr3_catalog.$(ARCH).o \
+$(SRC)/loadgaia_edr3_make_subset.$(ARCH).o \
+$(SRC)/loadgaia_edr3_readstars.$(ARCH).o \
+$(SRC)/loadgaia_edr3_table.$(ARCH).o \
+$(SRC)/parse_csv.$(ARCH).o \
+$(SRC)/resort_catalog.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/strhash.$(ARCH).o \
+$(SRC)/sortIDs.$(ARCH).o \
+$(SRC)/psps_ids.$(ARCH).o
+
+LOAD-ATLAS = \
+$(SRC)/loadatlas.$(ARCH).o \
+$(SRC)/ConfigInit.$(ARCH).o \
+$(SRC)/SetSignals.$(ARCH).o \
+$(SRC)/Shutdown.$(ARCH).o \
+$(SRC)/args_loadatlas.$(ARCH).o \
+$(SRC)/find_matches_atlas.$(ARCH).o \
+$(SRC)/loadatlas_catalog.$(ARCH).o \
+$(SRC)/loadatlas_make_subset.$(ARCH).o \
+$(SRC)/loadatlas_readstars.$(ARCH).o \
+$(SRC)/loadatlas_table.$(ARCH).o \
+$(SRC)/parse_csv.$(ARCH).o \
+$(SRC)/resort_catalog.$(ARCH).o \
+$(SRC)/build_links.$(ARCH).o \
+$(SRC)/strhash.$(ARCH).o \
+$(SRC)/sortIDs.$(ARCH).o \
+$(SRC)/psps_ids.$(ARCH).o
+
 LOAD-UKIRT_UHS = \
 $(SRC)/loadukirt_uhs.$(ARCH).o \
@@ -548,7 +586,9 @@
 $(FINDSKYCELL)	       : $(INC)/addstar.h
 $(LOAD-2MASS)  	       : $(INC)/addstar.h $(INC)/2mass.h
+$(LOAD-ATLAS)          : $(INC)/addstar.h $(INC)/atlas.h
 $(LOAD-GALPHOT)	       : $(INC)/addstar.h $(INC)/loadgalphot.h
 $(LOAD-GAIA)	       : $(INC)/addstar.h $(INC)/gaia.h
 $(LOAD-GAIA_DR2)       : $(INC)/addstar.h $(INC)/gaia_dr2.h
+$(LOAD-GAIA_EDR3)      : $(INC)/addstar.h $(INC)/gaia_edr3.h
 $(LOAD-UKIRT_UHS)      : $(INC)/addstar.h $(INC)/ukirt_uhs.h
 $(LOAD-STARPAR)	       : $(INC)/addstar.h $(INC)/loadstarpar.h
@@ -569,4 +609,5 @@
 $(BIN)/sedstar.$(ARCH)        : $(SEDSTAR)
 $(BIN)/load2mass.$(ARCH)      : $(LOAD-2MASS)
+$(BIN)/loadatlas.$(ARCH)      : $(LOAD-ATLAS)
 $(BIN)/loadwise.$(ARCH)       : $(LOAD-WISE)
 $(BIN)/loadtycho.$(ARCH)      : $(LOAD-TYCHO)
@@ -575,4 +616,5 @@
 $(BIN)/loadgaia.$(ARCH)       : $(LOAD-GAIA)
 $(BIN)/loadgaia_dr2.$(ARCH)   : $(LOAD-GAIA_DR2)
+$(BIN)/loadgaia_edr3.$(ARCH)  : $(LOAD-GAIA_EDR3)
 $(BIN)/loadukirt_uhs.$(ARCH)  : $(LOAD-UKIRT_UHS)
 $(BIN)/loadstarpar.$(ARCH)    : $(LOAD-STARPAR)
Index: trunk/Ohana/src/addstar/include/atlas.h
===================================================================
--- trunk/Ohana/src/addstar/include/atlas.h	(revision 41563)
+++ trunk/Ohana/src/addstar/include/atlas.h	(revision 41563)
@@ -0,0 +1,50 @@
+
+// measure[0] = g, measure[1] = r, measure[2] = i,  measure[3] = z, measure[4] = y, measure[5] = J, measure[6] = H, measure[7] = K
+
+typedef struct {
+  Average average;
+  Measure measure[8];
+  int flag; // in a subset?
+  int found; // assigned to an object?
+} Atlas_Stars;
+
+int   HOST_ID;
+char *HOSTDIR;
+char *CPT_FILE;
+char *INPUT;
+
+AddstarClientOptions args_loadatlas (int *argc, char **argv, AddstarClientOptions options);
+// AddstarClientOptions args_loadatlas_client (int *argc, char **argv, AddstarClientOptions options);
+
+int loadatlas_table (int Nstart, int Nend, SkyList *skylistInput, HostTable *hosts, char **filename, AddstarClientOptions *options);
+
+Atlas_Stars *loadatlas_make_subset (Atlas_Stars *stars, int Nstars, int start, SkyRegion *region, int *nsubset);
+
+// int loadatlas_save_remote (Atlas_Stars *stars, int Nstars, HostTable *hosts, SkyRegion *region, char *fullname, AddstarClientOptions *options);
+// int save_remote_host (HostInfo *host);
+
+// int init_remote_hosts (void);
+// void free_remote_hosts (void);
+// int find_empty_slot (void);
+// int harvest_all (void);
+// int harvest_host (void);
+
+int loadatlas_catalog (Atlas_Stars *stars, int Nstars, SkyRegion *region, char *filename, AddstarClientOptions *options);
+
+// int galactic_to_celestial (double *R, double *D, double l, double b);
+
+int find_matches_atlas (SkyRegion *region, Atlas_Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions *options);
+
+int loadatlas_save_stars (char *filename, Atlas_Stars *stars, int Nstars);
+// Atlas_Stars *loadatlas_load_stars (char *filename, int *nstars);
+
+Atlas_Stars *loadatlas_readstars (char *filename, Atlas_Stars *stars, int *nstars, AddstarClientOptions *options);
+
+int loadatlas_sortStars (Atlas_Stars *stars, int Nstars);
+
+// int loadatlas_tmpdir (void);
+
+//  roll these into a function with a structure to carry the Nlast, etc?
+char *dparse_csv_rpt (double *X, int Nwant, int Nlast, char *line, int *status);
+char *iparse_csv_rpt (int *X,    int Nwant, int Nlast, char *line, int *status);
+char *jparse_csv_rpt (uint64_t *X, int Nwant, int Nlast, char *line, int *status);
Index: trunk/Ohana/src/addstar/include/gaia_edr3.h
===================================================================
--- trunk/Ohana/src/addstar/include/gaia_edr3.h	(revision 41563)
+++ trunk/Ohana/src/addstar/include/gaia_edr3.h	(revision 41563)
@@ -0,0 +1,50 @@
+
+// measure[0] = g, measure[1] = b, measure[2] = r, 
+
+typedef struct {
+  Average average;
+  Measure measure[3];
+  int flag; // in a subset?
+  int found; // assigned to an object?
+} Gaia_EDR3_Stars;
+
+int   HOST_ID;
+char *HOSTDIR;
+char *CPT_FILE;
+char *INPUT;
+
+AddstarClientOptions args_loadgaia_edr3 (int *argc, char **argv, AddstarClientOptions options);
+// AddstarClientOptions args_loadgaia_edr3_client (int *argc, char **argv, AddstarClientOptions options);
+
+int loadgaia_edr3_table (int Nstart, int Nend, SkyList *skylistInput, HostTable *hosts, char **filename, AddstarClientOptions *options);
+
+Gaia_EDR3_Stars *loadgaia_edr3_make_subset (Gaia_EDR3_Stars *stars, int Nstars, int start, SkyRegion *region, int *nsubset);
+
+// int loadgaia_edr3_save_remote (Gaia_EDR3_Stars *stars, int Nstars, HostTable *hosts, SkyRegion *region, char *fullname, AddstarClientOptions *options);
+// int save_remote_host (HostInfo *host);
+
+// int init_remote_hosts (void);
+// void free_remote_hosts (void);
+// int find_empty_slot (void);
+// int harvest_all (void);
+// int harvest_host (void);
+
+int loadgaia_edr3_catalog (Gaia_EDR3_Stars *stars, int Nstars, SkyRegion *region, char *filename, AddstarClientOptions *options);
+
+// int galactic_to_celestial (double *R, double *D, double l, double b);
+
+int find_matches_gaia_edr3 (SkyRegion *region, Gaia_EDR3_Stars *stars, int Nstars, Catalog *catalog, AddstarClientOptions *options);
+
+int loadgaia_edr3_save_stars (char *filename, Gaia_EDR3_Stars *stars, int Nstars);
+// Gaia_EDR3_Stars *loadgaia_edr3_load_stars (char *filename, int *nstars);
+
+Gaia_EDR3_Stars *loadgaia_edr3_readstars (char *filename, Gaia_EDR3_Stars *stars, int *nstars, AddstarClientOptions *options);
+
+int loadgaia_edr3_sortStars (Gaia_EDR3_Stars *stars, int Nstars);
+
+// int loadgaia_edr3_tmpdir (void);
+
+//  roll these into a function with a structure to carry the Nlast, etc?
+char *dparse_csv_rpt (double *X, int Nwant, int Nlast, char *line, int *status);
+char *iparse_csv_rpt (int *X,    int Nwant, int Nlast, char *line, int *status);
+char *jparse_csv_rpt (uint64_t *X, int Nwant, int Nlast, char *line, int *status);
Index: trunk/Ohana/src/addstar/src/args_loadatlas.c
===================================================================
--- trunk/Ohana/src/addstar/src/args_loadatlas.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/args_loadatlas.c	(revision 41563)
@@ -0,0 +1,137 @@
+# include "addstar.h"
+# include "atlas.h"
+
+static void help (void);
+
+AddstarClientOptions args_loadatlas (int *argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    help ();
+  }
+
+  /* basic mode: image, list, refcat */
+  options.mode = ADDSTAR_MODE_REFCAT;
+
+  /* we do not allow a subset to be extracted -- all or nothing, babe */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+
+  // 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);
+  }
+
+  /* only add to existing regions */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (*argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (*argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (*argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+
+  /* accept proper-motion (& parallax) data from reference */
+  ACCEPT_MOTION = FALSE;
+  if ((N = get_argument (*argc, argv, "-accept-motion"))) {
+    ACCEPT_MOTION = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* provide a time for dataset */
+  options.timeref = 0; 
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    options.timeref = tmp;
+    remove_argument (N, argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* other addstar options which cannot be used in loadatlas */
+  // options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (*argc < 2) {
+    fprintf (stderr, "USAGE: loadatlas [options] (fitsfile) [..more files]\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE: loadatlas [options] (file) [..more files]\n");
+  fprintf (stderr, "  add data from stellar parameter file to DVO\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: trunk/Ohana/src/addstar/src/args_loadgaia_edr3.c
===================================================================
--- trunk/Ohana/src/addstar/src/args_loadgaia_edr3.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/args_loadgaia_edr3.c	(revision 41563)
@@ -0,0 +1,137 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+static void help (void);
+
+AddstarClientOptions args_loadgaia_edr3 (int *argc, char **argv, AddstarClientOptions options) {
+  
+  int N;
+
+  /* check for help request */
+  if (get_argument (*argc, argv, "-help") ||
+      get_argument (*argc, argv, "-h")) {
+    help ();
+  }
+
+  /* basic mode: image, list, refcat */
+  options.mode = ADDSTAR_MODE_REFCAT;
+
+  /* we do not allow a subset to be extracted -- all or nothing, babe */
+  UserPatch.Rmin = 0;
+  UserPatch.Rmax= 360;
+  UserPatch.Dmin = -90;
+  UserPatch.Dmax = +90;
+
+  // 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);
+  }
+
+  /* only add to existing regions */
+  options.existing_regions = FALSE;
+  if ((N = get_argument (*argc, argv, "-existing-regions"))) {
+    options.existing_regions = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* only add to existing objects */
+  options.only_match = FALSE;
+  if ((N = get_argument (*argc, argv, "-only-match"))) {
+    options.only_match = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* replace measurement, don't duplicate (ref/cat only) */
+  options.replace = FALSE;
+  if ((N = get_argument (*argc, argv, "-replace"))) {
+    options.replace = TRUE;
+    remove_argument (N, argc, argv);
+  }
+  /* override any header PHOTCODE values */
+  options.photcode = 0;
+
+  /* accept proper-motion (& parallax) data from reference */
+  ACCEPT_MOTION = FALSE;
+  if ((N = get_argument (*argc, argv, "-accept-motion"))) {
+    ACCEPT_MOTION = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* provide a time for dataset */
+  options.timeref = 0; 
+  if ((N = get_argument (*argc, argv, "-time"))) {
+    time_t tmp;
+    remove_argument (N, argc, argv);
+    if (!ohana_str_to_time (argv[N], &tmp)) { 
+      fprintf (stderr, "syntax error in time\n");
+      exit (1);
+    }
+    options.timeref = tmp;
+    remove_argument (N, argc, argv);
+  }
+
+  /* extra error messages */
+  VERBOSE = FALSE;
+  if ((N = get_argument (*argc, argv, "-v"))) {
+    VERBOSE = TRUE;
+    remove_argument (N, argc, argv);
+  }
+
+  /* other addstar options which cannot be used in loadgaia_edr3 */
+  // options.timeref = 0; 
+  options.mosaic = FALSE;
+  options.skip_missed = FALSE;
+  options.closest = FALSE;
+  options.nosort = FALSE;
+  options.update = FALSE;
+  options.only_images = FALSE;
+  options.calibrate = FALSE;
+  options.quality_airmass = FALSE;
+  ACCEPT_ASTROM = FALSE;
+  FORCE_READ = FALSE;
+  TEXTMODE = FALSE;
+  SUBPIX = FALSE;
+  DUMP = NULL;
+
+  if (*argc < 2) {
+    fprintf (stderr, "USAGE: loadgaia_edr3 [options] (fitsfile) [..more files]\n");
+    exit (2);
+  }
+  return (options);
+}
+
+static void help () {
+
+  fprintf (stderr, "USAGE: loadgaia_edr3 [options] (file) [..more files]\n");
+  fprintf (stderr, "  add data from stellar parameter file to DVO\n\n");
+
+  fprintf (stderr, "  optional flags:\n");
+  fprintf (stderr, "  -only-match           	  : only add measurements to existing objects\n");
+  fprintf (stderr, "  -replace              	  : replace time/photcode measurements (no duplication)\n");
+  fprintf (stderr, "  -v                    	  : verbose mode\n");
+  fprintf (stderr, "  -help                 	  : this list\n");
+  fprintf (stderr, "  -h                    	  : this list\n\n");
+  exit (2);
+}
Index: trunk/Ohana/src/addstar/src/find_matches_atlas.c
===================================================================
--- trunk/Ohana/src/addstar/src/find_matches_atlas.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/find_matches_atlas.c	(revision 41563)
@@ -0,0 +1,346 @@
+# include "addstar.h"
+# include "atlas.h"
+
+int find_matches_atlas (SkyRegion *region, Atlas_Stars *stars, int NstarsIn, Catalog *tgtcat, AddstarClientOptions *options) {
+
+  int Nsecfilt = GetPhotcodeNsecfilt ();
+  int Nsec     = GetPhotcodeNsec (options->photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE_PTR (X1, double, NstarsIn);
+  ALLOCATE_PTR (Y1, double, NstarsIn);
+  ALLOCATE_PTR (N1, off_t,  NstarsIn);
+
+  /** allocate local arrays (tgtcat) **/
+  off_t NAVE = tgtcat[0].Naverage;
+  off_t Nave = tgtcat[0].Naverage;
+  ALLOCATE_PTR (X2, double, NAVE);
+  ALLOCATE_PTR (Y2, double, NAVE);
+  ALLOCATE_PTR (N2, off_t,  NAVE);
+
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  off_t Nmatch = 0;
+  off_t NMEAS = tgtcat[0].Nmeasure;
+  off_t Nmeas = tgtcat[0].Nmeasure;
+
+  off_t *next_meas = NULL;
+
+  // current max obj ID for this tgtcat
+  unsigned int objID = tgtcat[0].objID;
+  unsigned int catID = tgtcat[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ARC projection has
+   * the advantage that every point in R,D has a mapping to a unique X,Y.  However, note
+   * that not all possible X,Y points map back to R,D and the local plate scale changes
+   * substantially far from the projection pole. We use the center of the region (tgtcat)
+   * for crval1,2.
+   */
+
+  Coords tcoords;
+  InitCoords (&tcoords, "DEC--ARC");
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if ((region[0].Dmax < 90) && (region[0].Dmin > -90)) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = (region[0].Dmax >= 90) ? 90.0 : -90.0;
+  }
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  off_t Nstars = 0;
+  for (off_t i = 0; i < NstarsIn; i++) {
+    int status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].average.R, stars[i].average.D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", tgtcat[0].filename);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return 0;
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+
+  /* build spatial index (RA sort) */
+  for (off_t i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], tgtcat[0].average[i].R, tgtcat[0].average[i].D, &tcoords);
+    N2[i] = i;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure */
+  if (tgtcat[0].sorted && (tgtcat[0].Nmeasure == tgtcat[0].Nmeasure_disk)) {
+    // this version is only valid if we have done a full tgtcat load, and if the tgtcat
+    // is sorted while processed
+    next_meas = init_measure_links (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas);
+  }    
+
+  /* choose a radius for matches */
+  double RADIUS = (options->radius == 0) ? 2.0 : options->radius; /* provided by config */
+  double RADIUS2 = RADIUS*RADIUS;
+
+  /****************** find matched stars ********************/
+
+  for (off_t i = 0, j = 0; (i < Nstars) && (j < Nave); ) {
+    if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+    
+    /* negative dX: j is too large */
+    double dX = X1[i] - X2[j];
+    if (dX <= -1.02*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 1.02*RADIUS) {
+      j++;
+      continue;
+    }
+
+    // skip this star if already assigned to an object
+    if (stars[N1[i]].found) {
+        i++;
+        continue;
+    }
+
+    /* this block will match a given detection to the closest object within range of that detection.
+       XXX note that this matches ALL detections within range of the single object to that same object 
+       this is bad, but I cannot just go in linear order (ie, mark off each object as they are
+       used).  I should make a list of all Nobj * Ndet pairs in range and choose the matches
+       based on their separations.  UGH
+     */
+    
+    /* within match range; look for matches */
+    off_t Jmin = -1;
+    double Rmin = RADIUS2;
+    for (off_t J = j; (dX > -1.02*RADIUS) && (J < Nave); J++) {
+      /* find closest match for this detection */
+      dX = X1[i] - X2[J];
+      double dY = Y1[i] - Y2[J];
+      double dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+      if (dR < Rmin) {
+	Rmin = dR;
+	Jmin  = J;
+      }
+    }
+
+    /* no match, try next detection */ 
+    if (Jmin == -1) {
+      i++;
+      continue;
+    }
+
+    /*** a match is found, add to average, measure ***/
+    Nmatch ++;
+    off_t n = N2[Jmin];
+    off_t N = N1[i];
+
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS - stars[N].average.Nmeasure) {
+      NMEAS = Nmeas + 1000 + stars[N].average.Nmeasure;
+      REALLOCATE (next_meas, off_t, NMEAS);
+      REALLOCATE (tgtcat[0].measure, Measure, NMEAS);
+    }
+
+    /** add measurements for this star **/
+    for (int mSrc = 0; mSrc < stars[N].average.Nmeasure; mSrc++) {
+
+      /* add to end of measurement list */
+      add_meas_link (&tgtcat[0].average[n], next_meas, Nmeas, NMEAS);
+
+      /** add measurements for this star **/
+
+      // set the new measurements
+      tgtcat[0].measure[Nmeas]          = stars[N].measure[mSrc];
+
+      // measure now carries R,D (not dR,dD) 
+      tgtcat[0].measure[Nmeas].dbFlags  = 0;
+      tgtcat[0].measure[Nmeas].averef   = n;
+      tgtcat[0].measure[Nmeas].objID    = tgtcat[0].average[n].objID;
+      tgtcat[0].measure[Nmeas].catID    = tgtcat[0].catID;
+      
+      float dRoff = dvoOffsetR(&tgtcat[0].measure[Nmeas], &tgtcat[0].average[n]);
+
+      // rationalize dR
+      if (dRoff > +180.0*3600.0) {
+	// average on high end of boundary, move star up
+	tgtcat[0].measure[Nmeas].R += 360.0;
+	dRoff -= 360.0*3600.0;
+      }
+      if (dRoff < -180.0*3600.0) {
+	// average on low end of boundary, move star down
+	tgtcat[0].measure[Nmeas].R -= 360.0;
+	dRoff += 360.0*3600.0;
+      }
+      if (fabs(dRoff) > 10*RADIUS) {
+	// take declination into account and check again.
+	double cosD = cos(RAD_DEG*tgtcat[0].average[n].D);
+	if (fabs(dRoff*cosD) > 10*RADIUS) {
+	  fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+		   tgtcat[0].average[n].R, tgtcat[0].average[n].D, 
+		   stars[N].average.R, stars[N].average.D,
+		   X1[i], X2[Jmin], 
+		   Y1[i], Y2[Jmin]);
+	  // XXX abort on this? -- this is a bad failure...
+	}
+      }
+      
+      /* Nm is updated, but not written out in -update mode (for existing entries)
+	 Nm is recalculated in build_meas_links if loaded table is not sorted */
+      tgtcat[0].average[n].Nmeasure ++;
+      Nmeas ++;
+      
+      /* if we choose to flag close encounters, see find_matches.c */
+      /* if we choose to calculate RA,DEC averages, see update_coords.c */
+    }
+    
+    if (ACCEPT_MOTION) {
+      tgtcat[0].average[n].uR    = stars[N].average.uR;
+      tgtcat[0].average[n].uD    = stars[N].average.uD;
+      tgtcat[0].average[n].duR   = stars[N].average.duR;
+      tgtcat[0].average[n].duD   = stars[N].average.duD;
+      tgtcat[0].average[n].P     = stars[N].average.P;
+      tgtcat[0].average[n].dP    = stars[N].average.dP;
+      tgtcat[0].average[n].Tmean = stars[N].average.Tmean;
+    }
+	
+    stars[N].found = TRUE;
+    i++;
+  }
+
+  /*************** add unmatched stars *************************/
+
+  /** incorporate unmatched image stars, if this star is in field of this tgtcat **/
+  /* these new entries are all written out in UPDATE mode */ 
+  for (off_t i = 0; (i < Nstars) && !options->only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS - stars[i].average.Nmeasure) {
+      NMEAS = Nmeas + 1000 + stars[i].average.Nmeasure;
+      REALLOCATE (next_meas, off_t, NMEAS);
+      REALLOCATE (tgtcat[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (tgtcat[0].average, Average, NAVE);
+      if (tgtcat[0].secfilt) {
+	// we only update the secfilt table if it has been allocated for output
+	REALLOCATE (tgtcat[0].secfilt, SecFilt, NAVE*tgtcat[0].Nsecfilt);
+      }
+    }
+
+    if (stars[i].found) continue;
+    if (!IN_REGION (stars[i].average.R, stars[i].average.D)) continue;
+
+    dvo_average_init (&tgtcat[0].average[Nave]);
+    tgtcat[0].average[Nave].R         	   = stars[i].average.R;
+    tgtcat[0].average[Nave].D         	   = stars[i].average.D;
+    tgtcat[0].average[Nave].dR         	   = stars[i].average.dR;
+    tgtcat[0].average[Nave].dD         	   = stars[i].average.dD;
+
+    tgtcat[0].average[Nave].measureOffset  = Nmeas;
+    tgtcat[0].average[Nave].objID     	   = objID;
+    tgtcat[0].average[Nave].catID     	   = catID;
+
+    if (PSPS_ID) {
+      tgtcat[0].average[Nave].extID = CreatePSPSObjectID(tgtcat[0].average[Nave].R, tgtcat[0].average[Nave].D);
+    }
+
+    if (ACCEPT_MOTION) {
+      tgtcat[0].average[Nave].uR    = stars[i].average.uR;
+      tgtcat[0].average[Nave].uD    = stars[i].average.uD;
+      tgtcat[0].average[Nave].duR   = stars[i].average.duR;
+      tgtcat[0].average[Nave].duD   = stars[i].average.duD;
+      tgtcat[0].average[Nave].P     = stars[i].average.P;
+      tgtcat[0].average[Nave].dP    = stars[i].average.dP;
+      tgtcat[0].average[Nave].Tmean = stars[i].average.Tmean;
+    }
+	
+    objID ++;
+
+    // we only update the secfilt table if it has been allocated for output
+    for (int j = 0; tgtcat[0].secfilt && (j < Nsecfilt); j++) {
+      dvo_secfilt_init (&tgtcat[0].secfilt[Nave*Nsecfilt+j], SECFILT_RESET_ALL);
+    }
+
+    // supply the measurements from this detection
+    for (int mSrc = 0; mSrc < stars[i].average.Nmeasure; mSrc++) {
+      dvo_measure_init (&tgtcat[0].measure[Nmeas]);
+      tgtcat[0].measure[Nmeas] = stars[i].measure[mSrc];
+
+      // the following measure elements cannot be set until here:
+      tgtcat[0].measure[Nmeas].dbFlags  = 0;
+      tgtcat[0].measure[Nmeas].averef   = Nave;
+      tgtcat[0].measure[Nmeas].objID    = tgtcat[0].average[Nave].objID;
+      tgtcat[0].measure[Nmeas].catID    = tgtcat[0].catID;
+      
+      /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+      /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+      if (Nsec > -1) { 
+	tgtcat[0].secfilt[Nave*Nsecfilt+Nsec].MpsfChp = PhotCat (&tgtcat[0].measure[Nmeas], MAG_CLASS_PSF);
+      }
+
+      tgtcat[0].average[Nave].Nmeasure ++;
+      Nmeas ++;
+
+      // update the next_meas pointer for this entry (last one for this star is -1)
+      next_meas[Nmeas-1] = (mSrc < stars[i].average.Nmeasure - 1) ? Nmeas : -1;
+    }
+    stars[i].found = TRUE;
+    Nave ++;
+  }
+
+  REALLOCATE (tgtcat[0].average, Average, Nave);
+  REALLOCATE (tgtcat[0].measure, Measure, Nmeas);
+ 
+  if (options->nosort) {
+    tgtcat[0].sorted = FALSE;
+  } else {
+    tgtcat[0].sorted = TRUE;
+    tgtcat[0].measure = sort_measure (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas, next_meas);
+  }
+
+  /* check if the tgtcat has changed?  if no change, no need to write */
+  tgtcat[0].objID    = objID; // new max value, save on tgtcat close
+  tgtcat[0].Naverage = Nave;
+  tgtcat[0].Nmeasure = Nmeas;
+  tgtcat[0].Nsecfilt_mem = tgtcat[0].secfilt ? Nave*Nsecfilt : 0;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas, Nmatch);
+
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  free (next_meas);
+
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a tgtcat star is in an image or an image star is in the tgtcat:
+   
+   tgtcats have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   tgtcat[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: trunk/Ohana/src/addstar/src/find_matches_gaia_edr3.c
===================================================================
--- trunk/Ohana/src/addstar/src/find_matches_gaia_edr3.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/find_matches_gaia_edr3.c	(revision 41563)
@@ -0,0 +1,346 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+int find_matches_gaia_edr3 (SkyRegion *region, Gaia_EDR3_Stars *stars, int NstarsIn, Catalog *tgtcat, AddstarClientOptions *options) {
+
+  int Nsecfilt = GetPhotcodeNsecfilt ();
+  int Nsec     = GetPhotcodeNsec (options->photcode);
+
+  /** allocate local arrays (stars) **/
+  ALLOCATE_PTR (X1, double, NstarsIn);
+  ALLOCATE_PTR (Y1, double, NstarsIn);
+  ALLOCATE_PTR (N1, off_t,  NstarsIn);
+
+  /** allocate local arrays (tgtcat) **/
+  off_t NAVE = tgtcat[0].Naverage;
+  off_t Nave = tgtcat[0].Naverage;
+  ALLOCATE_PTR (X2, double, NAVE);
+  ALLOCATE_PTR (Y2, double, NAVE);
+  ALLOCATE_PTR (N2, off_t,  NAVE);
+
+  /* for secfilt j and star i, secfilt[i*Nsecfilt+j] */
+
+  /* internal counters */
+  off_t Nmatch = 0;
+  off_t NMEAS = tgtcat[0].Nmeasure;
+  off_t Nmeas = tgtcat[0].Nmeasure;
+
+  off_t *next_meas = NULL;
+
+  // current max obj ID for this tgtcat
+  unsigned int objID = tgtcat[0].objID;
+  unsigned int catID = tgtcat[0].catID;
+
+  /* project onto rectilinear grid with 1 arcsec pixels. the choice of ARC projection has
+   * the advantage that every point in R,D has a mapping to a unique X,Y.  However, note
+   * that not all possible X,Y points map back to R,D and the local plate scale changes
+   * substantially far from the projection pole. We use the center of the region (tgtcat)
+   * for crval1,2.
+   */
+
+  Coords tcoords;
+  InitCoords (&tcoords, "DEC--ARC");
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if ((region[0].Dmax < 90) && (region[0].Dmin > -90)) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = (region[0].Dmax >= 90) ? 90.0 : -90.0;
+  }
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  off_t Nstars = 0;
+  for (off_t i = 0; i < NstarsIn; i++) {
+    int status = RD_to_XY (&X1[Nstars], &Y1[Nstars], stars[i].average.R, stars[i].average.D, &tcoords);
+    if (!status) continue;
+    N1[Nstars] = i;
+    Nstars ++;
+  }
+  if (Nstars < 1) {
+    if (VERBOSE) fprintf (stderr, "skipping %s, no overlapping stars\n", tgtcat[0].filename);
+    free (X1);
+    free (Y1);
+    free (N1);
+    free (X2);
+    free (Y2);
+    free (N2);
+    return 0;
+  }
+  if (Nstars > 1) sort_coords_index (X1, Y1, N1, Nstars);
+
+  /* build spatial index (RA sort) */
+  for (off_t i = 0; i < Nave; i++) {
+    RD_to_XY (&X2[i], &Y2[i], tgtcat[0].average[i].R, tgtcat[0].average[i].D, &tcoords);
+    N2[i] = i;
+  }
+  if (Nave > 1) sort_coords_index (X2, Y2, N2, Nave);
+
+  /* set up pointers for linked list of measure */
+  if (tgtcat[0].sorted && (tgtcat[0].Nmeasure == tgtcat[0].Nmeasure_disk)) {
+    // this version is only valid if we have done a full tgtcat load, and if the tgtcat
+    // is sorted while processed
+    next_meas = init_measure_links (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas);
+  } else {
+    next_meas = build_measure_links (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas);
+  }    
+
+  /* choose a radius for matches */
+  double RADIUS = (options->radius == 0) ? 2.0 : options->radius; /* provided by config */
+  double RADIUS2 = RADIUS*RADIUS;
+
+  /****************** find matched stars ********************/
+
+  for (off_t i = 0, j = 0; (i < Nstars) && (j < Nave); ) {
+    if (!finite(X1[i]) || !finite(Y1[i])) { 
+      i++; 
+      continue;
+    }
+    if (!finite(X2[j]) || !finite(Y2[j])) { 
+      j++; 
+      continue;
+    }
+    
+    /* negative dX: j is too large */
+    double dX = X1[i] - X2[j];
+    if (dX <= -1.02*RADIUS) {
+      i++;
+      continue;
+    }
+    /* positive dX, i is too large */
+    if (dX >= 1.02*RADIUS) {
+      j++;
+      continue;
+    }
+
+    // skip this star if already assigned to an object
+    if (stars[N1[i]].found) {
+        i++;
+        continue;
+    }
+
+    /* this block will match a given detection to the closest object within range of that detection.
+       XXX note that this matches ALL detections within range of the single object to that same object 
+       this is bad, but I cannot just go in linear order (ie, mark off each object as they are
+       used).  I should make a list of all Nobj * Ndet pairs in range and choose the matches
+       based on their separations.  UGH
+     */
+    
+    /* within match range; look for matches */
+    off_t Jmin = -1;
+    double Rmin = RADIUS2;
+    for (off_t J = j; (dX > -1.02*RADIUS) && (J < Nave); J++) {
+      /* find closest match for this detection */
+      dX = X1[i] - X2[J];
+      double dY = Y1[i] - Y2[J];
+      double dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+      if (dR < Rmin) {
+	Rmin = dR;
+	Jmin  = J;
+      }
+    }
+
+    /* no match, try next detection */ 
+    if (Jmin == -1) {
+      i++;
+      continue;
+    }
+
+    /*** a match is found, add to average, measure ***/
+    Nmatch ++;
+    off_t n = N2[Jmin];
+    off_t N = N1[i];
+
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS - stars[N].average.Nmeasure) {
+      NMEAS = Nmeas + 1000 + stars[N].average.Nmeasure;
+      REALLOCATE (next_meas, off_t, NMEAS);
+      REALLOCATE (tgtcat[0].measure, Measure, NMEAS);
+    }
+
+    /** add measurements for this star **/
+    for (int mSrc = 0; mSrc < stars[N].average.Nmeasure; mSrc++) {
+
+      /* add to end of measurement list */
+      add_meas_link (&tgtcat[0].average[n], next_meas, Nmeas, NMEAS);
+
+      /** add measurements for this star **/
+
+      // set the new measurements
+      tgtcat[0].measure[Nmeas]          = stars[N].measure[mSrc];
+
+      // measure now carries R,D (not dR,dD) 
+      tgtcat[0].measure[Nmeas].dbFlags  = 0;
+      tgtcat[0].measure[Nmeas].averef   = n;
+      tgtcat[0].measure[Nmeas].objID    = tgtcat[0].average[n].objID;
+      tgtcat[0].measure[Nmeas].catID    = tgtcat[0].catID;
+      
+      float dRoff = dvoOffsetR(&tgtcat[0].measure[Nmeas], &tgtcat[0].average[n]);
+
+      // rationalize dR
+      if (dRoff > +180.0*3600.0) {
+	// average on high end of boundary, move star up
+	tgtcat[0].measure[Nmeas].R += 360.0;
+	dRoff -= 360.0*3600.0;
+      }
+      if (dRoff < -180.0*3600.0) {
+	// average on low end of boundary, move star down
+	tgtcat[0].measure[Nmeas].R -= 360.0;
+	dRoff += 360.0*3600.0;
+      }
+      if (fabs(dRoff) > 10*RADIUS) {
+	// take declination into account and check again.
+	double cosD = cos(RAD_DEG*tgtcat[0].average[n].D);
+	if (fabs(dRoff*cosD) > 10*RADIUS) {
+	  fprintf (stderr, "error: %10.6f,%10.6f vs %10.6f,%10.6f (%f,%f vs %f,%f)\n", 
+		   tgtcat[0].average[n].R, tgtcat[0].average[n].D, 
+		   stars[N].average.R, stars[N].average.D,
+		   X1[i], X2[Jmin], 
+		   Y1[i], Y2[Jmin]);
+	  // XXX abort on this? -- this is a bad failure...
+	}
+      }
+      
+      /* Nm is updated, but not written out in -update mode (for existing entries)
+	 Nm is recalculated in build_meas_links if loaded table is not sorted */
+      tgtcat[0].average[n].Nmeasure ++;
+      Nmeas ++;
+      
+      /* if we choose to flag close encounters, see find_matches.c */
+      /* if we choose to calculate RA,DEC averages, see update_coords.c */
+    }
+    
+    if (ACCEPT_MOTION) {
+      tgtcat[0].average[n].uR    = stars[N].average.uR;
+      tgtcat[0].average[n].uD    = stars[N].average.uD;
+      tgtcat[0].average[n].duR   = stars[N].average.duR;
+      tgtcat[0].average[n].duD   = stars[N].average.duD;
+      tgtcat[0].average[n].P     = stars[N].average.P;
+      tgtcat[0].average[n].dP    = stars[N].average.dP;
+      tgtcat[0].average[n].Tmean = stars[N].average.Tmean;
+    }
+	
+    stars[N].found = TRUE;
+    i++;
+  }
+
+  /*************** add unmatched stars *************************/
+
+  /** incorporate unmatched image stars, if this star is in field of this tgtcat **/
+  /* these new entries are all written out in UPDATE mode */ 
+  for (off_t i = 0; (i < Nstars) && !options->only_match; i++) {
+    /* make sure there is space for next entry */
+    if (Nmeas >= NMEAS - stars[i].average.Nmeasure) {
+      NMEAS = Nmeas + 1000 + stars[i].average.Nmeasure;
+      REALLOCATE (next_meas, off_t, NMEAS);
+      REALLOCATE (tgtcat[0].measure, Measure, NMEAS);
+    }
+    if (Nave >= NAVE) {
+      NAVE = Nave + 1000;
+      REALLOCATE (tgtcat[0].average, Average, NAVE);
+      if (tgtcat[0].secfilt) {
+	// we only update the secfilt table if it has been allocated for output
+	REALLOCATE (tgtcat[0].secfilt, SecFilt, NAVE*tgtcat[0].Nsecfilt);
+      }
+    }
+
+    if (stars[i].found) continue;
+    if (!IN_REGION (stars[i].average.R, stars[i].average.D)) continue;
+
+    dvo_average_init (&tgtcat[0].average[Nave]);
+    tgtcat[0].average[Nave].R         	   = stars[i].average.R;
+    tgtcat[0].average[Nave].D         	   = stars[i].average.D;
+    tgtcat[0].average[Nave].dR         	   = stars[i].average.dR;
+    tgtcat[0].average[Nave].dD         	   = stars[i].average.dD;
+
+    tgtcat[0].average[Nave].measureOffset  = Nmeas;
+    tgtcat[0].average[Nave].objID     	   = objID;
+    tgtcat[0].average[Nave].catID     	   = catID;
+
+    if (PSPS_ID) {
+      tgtcat[0].average[Nave].extID = CreatePSPSObjectID(tgtcat[0].average[Nave].R, tgtcat[0].average[Nave].D);
+    }
+
+    if (ACCEPT_MOTION) {
+      tgtcat[0].average[Nave].uR    = stars[i].average.uR;
+      tgtcat[0].average[Nave].uD    = stars[i].average.uD;
+      tgtcat[0].average[Nave].duR   = stars[i].average.duR;
+      tgtcat[0].average[Nave].duD   = stars[i].average.duD;
+      tgtcat[0].average[Nave].P     = stars[i].average.P;
+      tgtcat[0].average[Nave].dP    = stars[i].average.dP;
+      tgtcat[0].average[Nave].Tmean = stars[i].average.Tmean;
+    }
+	
+    objID ++;
+
+    // we only update the secfilt table if it has been allocated for output
+    for (int j = 0; tgtcat[0].secfilt && (j < Nsecfilt); j++) {
+      dvo_secfilt_init (&tgtcat[0].secfilt[Nave*Nsecfilt+j], SECFILT_RESET_ALL);
+    }
+
+    // supply the measurements from this detection
+    for (int mSrc = 0; mSrc < stars[i].average.Nmeasure; mSrc++) {
+      dvo_measure_init (&tgtcat[0].measure[Nmeas]);
+      tgtcat[0].measure[Nmeas] = stars[i].measure[mSrc];
+
+      // the following measure elements cannot be set until here:
+      tgtcat[0].measure[Nmeas].dbFlags  = 0;
+      tgtcat[0].measure[Nmeas].averef   = Nave;
+      tgtcat[0].measure[Nmeas].objID    = tgtcat[0].average[Nave].objID;
+      tgtcat[0].measure[Nmeas].catID    = tgtcat[0].catID;
+      
+      /* set the average magnitude if not already set and the photcode.equiv is not 0 */
+      /* in UPDATE mode, this value is not saved; use relphot to recalculate */
+      if (Nsec > -1) { 
+	tgtcat[0].secfilt[Nave*Nsecfilt+Nsec].MpsfChp = PhotCat (&tgtcat[0].measure[Nmeas], MAG_CLASS_PSF);
+      }
+
+      tgtcat[0].average[Nave].Nmeasure ++;
+      Nmeas ++;
+
+      // update the next_meas pointer for this entry (last one for this star is -1)
+      next_meas[Nmeas-1] = (mSrc < stars[i].average.Nmeasure - 1) ? Nmeas : -1;
+    }
+    stars[i].found = TRUE;
+    Nave ++;
+  }
+
+  REALLOCATE (tgtcat[0].average, Average, Nave);
+  REALLOCATE (tgtcat[0].measure, Measure, Nmeas);
+ 
+  if (options->nosort) {
+    tgtcat[0].sorted = FALSE;
+  } else {
+    tgtcat[0].sorted = TRUE;
+    tgtcat[0].measure = sort_measure (tgtcat[0].average, Nave, tgtcat[0].measure, Nmeas, next_meas);
+  }
+
+  /* check if the tgtcat has changed?  if no change, no need to write */
+  tgtcat[0].objID    = objID; // new max value, save on tgtcat close
+  tgtcat[0].Naverage = Nave;
+  tgtcat[0].Nmeasure = Nmeas;
+  tgtcat[0].Nsecfilt_mem = tgtcat[0].secfilt ? Nave*Nsecfilt : 0;
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas, Nmatch);
+
+  free (X1);
+  free (Y1);
+  free (N1);
+  free (X2);
+  free (Y2);
+  free (N2);
+  free (next_meas);
+
+  return (Nmatch);
+}
+
+/* 
+   notes:
+   
+   for finding if a tgtcat star is in an image or an image star is in the tgtcat:
+   
+   tgtcats have boundaries defined by RA and DEC, but they may curve in projection
+   images have boundaries which are lines in pixels coords, but curve in RA and DEC
+   
+   tgtcat[0].found[Ncat] but stars[Nstar].found
+   
+*/
Index: trunk/Ohana/src/addstar/src/loadatlas.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadatlas.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadatlas.c	(revision 41563)
@@ -0,0 +1,43 @@
+# include "addstar.h"
+# include "atlas.h"
+
+/* This is the DVO program to upload atlas detections from gaia cvs files into a DVO database.
+   It is modeled on the loadgaia program but the source files are CSV tables.
+
+   USAGE: loadatlas -D CATDIR (catdir) (atlasfile) [...more files]
+*/
+
+int main (int argc, char **argv) {
+
+  SkyTable *sky;
+  SkyList *skylist = NULL;
+  AddstarClientOptions options;
+
+  // need to construct these options with args_loadtycho...
+  SetSignals ();
+  options = ConfigInit (&argc, argv);
+  options = args_loadatlas (&argc, argv, options);
+
+  // load the full sky description table:
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // generate the subset matching the user-selected region
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  int Nstart = 1;
+  int Nend = argc;
+  while (Nstart < Nend) {
+    Nstart = loadatlas_table (Nstart, Nend, skylist, NULL, argv, &options);
+  }
+
+  SkyTableFree (sky);
+  SkyListFree(skylist);
+  FreePhotcodeTable();
+  free (CATDIR);
+
+  ohana_memcheck (VERBOSE);
+  ohana_memdump (VERBOSE);
+  exit (0);
+}  
+
Index: trunk/Ohana/src/addstar/src/loadatlas_catalog.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadatlas_catalog.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadatlas_catalog.c	(revision 41563)
@@ -0,0 +1,32 @@
+# include "addstar.h"
+# include "atlas.h"
+
+int loadatlas_catalog (Atlas_Stars *stars, int Nstars, SkyRegion *region, char *filename, AddstarClientOptions *options) {
+
+  Catalog catalog;
+
+  // now we have all of the loaded stars in this catalog
+  dvo_catalog_init (&catalog, TRUE);
+  catalog.filename  = filename;
+  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  = DVO_LOAD_AVERAGE | DVO_LOAD_SECFILT | DVO_LOAD_MEASURE;
+  catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, region, VERBOSE, "w")) {
+    fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+    exit (2);
+  }
+
+  find_matches_atlas (region, stars, Nstars, &catalog, options);
+    
+  SetProtect (TRUE);
+  if (!dvo_catalog_save (&catalog, VERBOSE)) { fprintf (stderr, "ERROR: failed to save %s\n", catalog.filename); exit (1); }
+  if (!dvo_catalog_unlock (&catalog)) { fprintf (stderr, "ERROR: failed to unlock %s\n", catalog.filename); exit (1); }
+  SetProtect (FALSE);
+
+  dvo_catalog_free (&catalog);
+
+  return (TRUE);
+}
Index: trunk/Ohana/src/addstar/src/loadatlas_make_subset.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadatlas_make_subset.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadatlas_make_subset.c	(revision 41563)
@@ -0,0 +1,44 @@
+# include "addstar.h"
+# include "atlas.h"
+
+// assign stars in the given region to the subset (NOTE: stars are sorted by RA, start is
+// first entry in stars array in this region)
+
+Atlas_Stars *loadatlas_make_subset (Atlas_Stars *stars, int Nstars, int start, SkyRegion *region, int *nsubset) {
+
+  int i;
+
+  Atlas_Stars *subset = NULL;
+
+  // collect array of (Stars *) stars in a new output catalog
+  int Nsubset = 0;
+  int NSUBSET = 3000;
+  ALLOCATE (subset, Atlas_Stars, NSUBSET);
+
+  // find the rest of the stars in this output region
+  for (i = start; i < Nstars; i++) {
+    if (stars[i].flag) continue;
+
+    // check if in skyregion
+    if (stars[i].average.R <  region[0].Rmin) continue;
+    if (stars[i].average.R >= region[0].Rmax) break;
+    if (stars[i].average.D <  region[0].Dmin) continue;
+    if (stars[i].average.D >= region[0].Dmax) continue;
+	  
+    // check if in UserPatch (a GLOBAL)
+    if (stars[i].average.R < UserPatch.Rmin) continue;
+    if (stars[i].average.R > UserPatch.Rmax) break;
+    if (stars[i].average.D < UserPatch.Dmin) continue;
+    if (stars[i].average.D > UserPatch.Dmax) continue;
+	  
+    subset[Nsubset] = stars[i];
+    Nsubset ++;
+
+    stars[i].flag = TRUE;
+
+    CHECK_REALLOCATE (subset, Atlas_Stars, NSUBSET, Nsubset, 10000);
+  }
+
+  *nsubset = Nsubset;
+  return subset;
+}
Index: trunk/Ohana/src/addstar/src/loadatlas_readstars.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadatlas_readstars.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadatlas_readstars.c	(revision 41563)
@@ -0,0 +1,449 @@
+# include "addstar.h"
+# include "atlas.h"
+
+/* this function reads the values of interest from the Gaia CSV files:
+
+  data model description:
+  http://gea.esac.esa.int/archive/documentation/GEDR3/Gaia_archive/chap_datamodel/sec_dm_main_tables/ssec_dm_gaia_source.html
+  Name   |   Data Type   |   Unit   |   Description
+-------------------------------------------------
+  01 objid      bigint          none       Object ID
+  02 RA 	   float  	   degrees    Right ascension from Gaia DR2, J2000, epoch 2015.5
+  03 Dec 	   float  	   degrees    Declination from Gaia DR2, J2000, epoch 2015.5
+  04 plx 	   real   	   mas	      Parallax from Gaia DR2
+  05 dplx 	   real   	   mas	      Parallax uncertainty from Gaia DR2
+  06 pmra 	   real   	   mas/yr     Proper motion in right ascension from Gaia DR2
+  07 dpmra      real   	   mas/yr     Proper motion uncertainty in right ascension
+  08 pmdec      real   	   mas/yr     Proper motion in declination from Gaia DR2
+  09 dpmdec     real   	   mas/yr     Proper motion uncertainty in declination
+  10 Gaia 	   real   	   mag	      Gaia G magnitude
+  11 dGaia      real   	   mag	      Gaia G magnitude uncertainty
+  12 BP 	   real   	   mag	      Gaia G_bp magnitude
+  13 dBP 	   real   	   mag	      Gaia G_bp magnitude uncertainty
+  14 RP 	   real   	   mag	      Gaia G_rp magnitude
+  15 dRP 	   real   	   mag	      Gaia G_rp magnitude uncertainty
+  16 Teff 	   int    	   K	      Gaia stellar effective temperature
+  17 AGaia      real   	   mag	      Gaia estimate of G-band extinction for this star
+  18 dupvar     int    	   none	      Gaia variability and duplicate flags, 0/1/2 for "CONSTANT"/"VARIABLE"/"NOT AVAILABLE" + 4*DUPLICATE
+  19 Ag 	   real   	   mag	      SFD estimate of total g-band extinction
+  20 rp1 	   real   	   arcsec     Radius where cummulative G flux exceeds 0.1 x this star
+  21 r1 	   real   	   arcsec     Radius where cummulative G flux exceeds 1.0 x this star
+  22 r10 	   real   	   arcsec     Radius where cummulative G flux exceeds 10.0 x this star
+  23 g	   real   	   mag	      PanSTARRS g magnitude
+  24 dg 	   real   	   mag	      PanSTARRS g magnitude uncertainty
+  25 gchi 	   real   	   none	      chi^2 / DOF for contributors
+  26 gcontrib   int    	   none	      Bitmap of conributing catalogs to g
+  27 r 	   real   	   mag	      PanSTARRS r magnitude
+  28 dr 	   real   	   mag	      PanSTARRS r magnitude uncertainty
+  29 rchi 	   real   	   none	      chi^2 / DOF for contributors
+  30 rcontrib   int    	   none	      Bitmap of conributing catalogs to r
+  31 i 	   real   	   mag	      PanSTARRS i magnitude
+  32 di 	   real   	   mag	      PanSTARRS i magnitude uncertainty
+  33 ichi 	   real   	   none	      chi^2 / DOF for contributors
+  34 icontrib   int    	   none	      Bitmap of conributing catalogs to i
+  35 z 	   real   	   mag	      PanSTARRS z magnitude
+  36 dz 	   real   	   mag	      PanSTARRS z magnitude uncertainty
+  37 zchi 	   real   	   none	      chi^2 / DOF for contributors
+  38 zcontrib   int    	   none	      Bitmap of conributing catalogs to z
+  39 nstat      int    	   none	      Count of griz outliers rejected
+  40 J 	   real   	   mag	      2MASS J magnitude
+  41 dJ 	   real   	   mag	      2MASS J magnitude uncertainty
+  42 H 	   real   	   mag	      2MASS H magnitude
+  43 dH 	   real   	   mag	      2MASS H magnitude uncertainty
+  44 K 	   real   	   mag	      2MASS K magnitude
+  45 dK 	   real   	   mag	      2MASS K magnitude uncertainty
+
+//  
+
+ measure.psfChisq  : gchi/rchi/ichi/zchi
+ photFlags : 
+  name          hexadecimal  value   description
+  Gaia DR2      0x00000000   0       Gaia DR2 contributed to griz magnitude
+  GMP           0x00000001   1       GMP contributed to griz magnitude.
+  Pan-STARRS    0x00000002   2       Pan-STARRS contributed to griz magnitude.
+  SkyMapper     0x00000004   4       SkyMapper contributed to griz magnitude.
+  Pathfinder    0x00000008   8       Pathfinder contributed to griz magnitude.
+  APASS         0x00000010   16      APASS contributed to griz magnitude.
+  APASS DR9     0x00000020   32      APASS DR9 contributed to griz magnitude.
+  Tycho-2/BSC   0x00000040   64      Tycho-2/BSC contributed to griz magnitude.
+
+*/
+
+Atlas_Stars *loadatlas_readstars (char *filename, Atlas_Stars *stars, int *nstars, AddstarClientOptions *options) {
+
+  int codeG = GetPhotcodeCodebyName ("g_PS"); if (!codeG) Shutdown ("missing photocde g_PS");
+  int codeR = GetPhotcodeCodebyName ("r_PS"); if (!codeR) Shutdown ("missing photocde r_PS");
+  int codeI = GetPhotcodeCodebyName ("i_PS"); if (!codeI) Shutdown ("missing photocde i_PS");
+  int codeZ = GetPhotcodeCodebyName ("z_PS"); if (!codeZ) Shutdown ("missing photocde z_PS");
+  int codeJ = GetPhotcodeCodebyName ("2MASS_J"); if (!codeJ) Shutdown ("missing photocde 2MASS_J");
+  int codeH = GetPhotcodeCodebyName ("2MASS_H"); if (!codeH) Shutdown ("missing photocde 2MASS_H");
+  int codeK = GetPhotcodeCodebyName ("2MASS_K"); if (!codeK) Shutdown ("missing photocde 2MASS_K");
+
+  // Atlas Epoch is 2016.0 == 2016/01/01,00:00:00 (? this is for GAIA edr3)
+  time_t ATLAS_EPOCH = ohana_date_to_sec ("2016/01/01,00:00:00");
+
+  // read in the full FITS files
+  FILE *f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read atlas file: %s", filename);
+
+  int Nskip = 0; // ATLAS CSV files have no header row (and now special character to mark)
+
+  int Nelem = 0;      // number of valid rows read (vector elements)
+  int NELEM = 5000000;  // currently-allocated number of output rows
+
+  // for initial testing, a minimal subset:
+  ALLOCATE_PTR (srcID, uint64_t, NELEM);
+  ALLOCATE_PTR (Rg, double, NELEM);
+  ALLOCATE_PTR (Dg, double, NELEM);
+  ALLOCATE_PTR (plx, double, NELEM);
+  ALLOCATE_PTR (dplx, double, NELEM);
+  ALLOCATE_PTR (uR, double, NELEM);
+  ALLOCATE_PTR (duR, double, NELEM);
+  ALLOCATE_PTR (uD, double, NELEM);
+  ALLOCATE_PTR (duD, double, NELEM);
+
+  ALLOCATE_PTR (gg, double, NELEM);
+  ALLOCATE_PTR (rr, double, NELEM);
+  ALLOCATE_PTR (ii, double, NELEM);
+  ALLOCATE_PTR (zz, double, NELEM);
+  ALLOCATE_PTR (jj, double, NELEM);
+  ALLOCATE_PTR (hh, double, NELEM);
+  ALLOCATE_PTR (kk, double, NELEM);
+
+  ALLOCATE_PTR (dgg, double, NELEM);
+  ALLOCATE_PTR (drr, double, NELEM);
+  ALLOCATE_PTR (dii, double, NELEM);
+  ALLOCATE_PTR (dzz, double, NELEM);
+  ALLOCATE_PTR (djj, double, NELEM);
+  ALLOCATE_PTR (dhh, double, NELEM);
+  ALLOCATE_PTR (dkk, double, NELEM);
+
+  ALLOCATE_PTR (cgg, double, NELEM);
+  ALLOCATE_PTR (crr, double, NELEM);
+  ALLOCATE_PTR (cii, double, NELEM);
+  ALLOCATE_PTR (czz, double, NELEM);
+
+  ALLOCATE_PTR (fgg, int, NELEM);
+  ALLOCATE_PTR (frr, int, NELEM);
+  ALLOCATE_PTR (fii, int, NELEM);
+  ALLOCATE_PTR (fzz, int, NELEM);
+
+ //ALLOCATE_PTR (procMode, int, NELEM);
+  // we allocate one extra byte into which we never read so there will always be a NULL terminating the string
+  ALLOCATE_PTR (buffer, char, 0x10001);
+  bzero (buffer, 0x10001);
+
+  int Nline_read = 0; // track number of lines read so far (use to skip lines as well)
+
+  // we have a working buffer read from the file. we parse the lines in the working buffer
+  // until we reach the last chunk without an EOL char.  at that point, we shift the start
+  // of the last (partial) line to the start of the buffer and re-fill.
+
+  // we treat \n\r pair as a single EOL char to handle mac files:
+
+  int Nstart = 0; // location of the last valid byte in the buffer (start filling here)
+  int EndOfFile = FALSE;
+  while (!EndOfFile) {
+    int Nbytes = 0x10000 - Nstart;
+    // we have allocated one extra byte into which we never read so there will always be a NULL terminating the string
+    bzero (&buffer[Nstart], Nbytes + 1);
+    int Nread = fread (&buffer[Nstart], 1, Nbytes, f);
+    if (ferror (f)) {
+      perror ("error reading data file");
+      break;
+    }
+    // we still need to parse the rest of the buffer, but there might not be an EOL on the last line
+    if (Nread == 0) {
+      EndOfFile = TRUE;
+    }
+    
+    int bufferStatus = TRUE; 
+    char *c0 = buffer; // c0 always marks the start of a line
+    char *cA = NULL; // cA will carry the curr point within the line
+    while (bufferStatus) {
+
+      // find the end of this current line (\n or \r).  if we hit the end of the buffer (NULL),
+      // attempt to read more data.  finish up when we hit the end of the file
+      char *c1 = strchr (c0, '\n'); // find the end of this current line (also valid for a Mac: \r\n)
+      if (!c1) {
+	c1 = strchr (c0, '\r'); // try \r for Windows files
+      }
+      if (!c1) {
+	Nstart = strlen (c0);
+	if (EndOfFile) {
+	  // if we have reached EOF, we need to do one last pass in case there is a line without a return
+	  c1 = c0 + Nstart;
+	  bufferStatus = FALSE;
+	  if (Nstart == 0) continue; // if we have reached EOF and c0 points at the last valid character, we are done
+	} else {
+	  // if we have not reached EOF, we need to shift the buffer to the start of this line and read more data
+	  memmove (buffer, c0, Nstart);
+	  bufferStatus = FALSE;
+	  continue;
+	}
+      }
+      *c1 = 0; // mark the end of the line 
+      Nline_read ++;
+
+      // skip to the next line (but if EOF, do not overrun buffer)
+      if (Nline_read <= Nskip) { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+
+      // these are not needed: Gaia CSV files do not have any commented-out lines
+      if (*c0 == '#')          { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+      if (*c0 == '!')          { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+
+      // XXX we know which columns we want in advance
+
+      int lineStatus = TRUE;
+      int readStatus;
+      double dvalue;
+      int ivalue;
+      uint64_t jvalue;
+
+      // cA will follow the currently extracted field, c0 points to the start of the line
+      cA = c0;
+
+      // Tref == 2016.0 for EDR3
+
+      // the start of the line is the 1st element (fields are 1-counting)
+      cA = jparse_csv_rpt (&jvalue,  1,  1, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  1, "source_id", c0); }  	       lineStatus &= readStatus; srcID[Nelem]     = jvalue;
+      cA = dparse_csv_rpt (&dvalue,  2,  1, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  2, "Ra", c0); }         	       lineStatus &= readStatus; Rg[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  3,  2, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  3, "Dec", c0); }        	       lineStatus &= readStatus; Dg[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  4,  3, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  4, "plx", c0); }        	       lineStatus &= readStatus; plx[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  5,  4, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  5, "dplx", c0); }       	       lineStatus &= readStatus; dplx[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  6,  5, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  6, "uR", c0); }         	       lineStatus &= readStatus; uR[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  7,  6, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  7, "duR", c0); }        	       lineStatus &= readStatus; duR[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  8,  7, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  8, "uD", c0); }         	       lineStatus &= readStatus; uD[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  9,  8, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  9, "duD", c0); }        	       lineStatus &= readStatus; duD[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 23,  9, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 23, "phot_g", c0); }             lineStatus &= readStatus;  gg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 24, 23, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 24, "dphot_g", c0); }            lineStatus &= readStatus;  dgg[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 25, 24, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 25, "cphot_g", c0); }            lineStatus &= readStatus;  cgg[Nelem]      = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 26, 25, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 26, "fphot_g", c0); }            lineStatus &= readStatus;  fgg[Nelem]      = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 27, 26, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 27, "phot_r", c0); }             lineStatus &= readStatus;  rr[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 28, 27, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 28, "dphot_r", c0); }            lineStatus &= readStatus;  drr[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 29, 28, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 29, "cphot_r", c0); }            lineStatus &= readStatus;  crr[Nelem]      = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 30, 29, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 30, "fphot_r", c0); }            lineStatus &= readStatus;  frr[Nelem]      = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 31, 30, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 31, "phot_i", c0); }             lineStatus &= readStatus;  ii[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 32, 31, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 32, "dphot_i", c0); }            lineStatus &= readStatus;  dii[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 33, 32, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 33, "cphot_i", c0); }            lineStatus &= readStatus;  cii[Nelem]      = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 34, 33, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 34, "fphot_i", c0); }            lineStatus &= readStatus;  fii[Nelem]      = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 35, 34, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 35, "phot_z", c0); }             lineStatus &= readStatus;  zz[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 36, 35, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 36, "dphot_z", c0); }            lineStatus &= readStatus;  dzz[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 37, 36, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 37, "cphot_z", c0); }            lineStatus &= readStatus;  czz[Nelem]      = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 38, 37, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 38, "fphot_z", c0); }            lineStatus &= readStatus;  fzz[Nelem]      = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 40, 38, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 40, "phot_j", c0); }             lineStatus &= readStatus;  jj[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 41, 40, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 41, "dphot_j", c0); }            lineStatus &= readStatus;  djj[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 42, 41, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 42, "phot_h", c0); }             lineStatus &= readStatus;  hh[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 43, 42, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 43, "dphot_h", c0); }            lineStatus &= readStatus;  dhh[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 44, 43, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 44, "phot_k", c0); }             lineStatus &= readStatus;  kk[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 45, 44, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 45, "dphot_k", c0); }            lineStatus &= readStatus;  dkk[Nelem]      = dvalue;
+
+      if (!lineStatus && VERBOSE) {
+	// why do I need to copy temp here, does gprint modify the value of temp?
+	char temp[32];
+	strncpy (temp, c0, 32);
+	temp[31] = 0;
+	gprint (GP_ERR, "skip line %s\n\n", temp);
+      }
+
+      Nelem ++;
+      if (Nelem == NELEM) {
+	NELEM += 1000;
+        REALLOCATE (srcID, uint64_t, NELEM);
+        REALLOCATE (Rg, double, NELEM);
+        REALLOCATE (Dg, double, NELEM);
+        REALLOCATE (plx, double, NELEM);
+        REALLOCATE (dplx, double, NELEM);
+        REALLOCATE (uR, double, NELEM);
+        REALLOCATE (duR, double, NELEM);
+        REALLOCATE (uD, double, NELEM);
+        REALLOCATE (duD, double, NELEM);
+        REALLOCATE (gg, double, NELEM);
+        REALLOCATE (rr, double, NELEM);
+        REALLOCATE (ii, double, NELEM);
+        REALLOCATE (zz, double, NELEM);
+        REALLOCATE (jj, double, NELEM);
+        REALLOCATE (hh, double, NELEM);
+        REALLOCATE (kk, double, NELEM);
+        REALLOCATE (dgg, double, NELEM);
+        REALLOCATE (drr, double, NELEM);
+        REALLOCATE (dii, double, NELEM);
+        REALLOCATE (dzz, double, NELEM);
+        REALLOCATE (djj, double, NELEM);
+        REALLOCATE (dhh, double, NELEM);
+        REALLOCATE (dkk, double, NELEM);
+        REALLOCATE (cgg, double, NELEM);
+        REALLOCATE (crr, double, NELEM);
+        REALLOCATE (cii, double, NELEM);
+        REALLOCATE (czz, double, NELEM);
+        REALLOCATE (fgg, int, NELEM);
+        REALLOCATE (frr, int, NELEM);
+        REALLOCATE (fii, int, NELEM);
+        REALLOCATE (fzz, int, NELEM);
+      }
+      if (!EndOfFile) {
+	c0 = c1 + 1;
+      }
+    }
+  }
+
+  // Nelem is now the number of items (objects,stars) read from the Gaia CSV file
+  int NstarsIn = Nelem;
+
+  double Rmin = +360.0;
+  double Rmax = -360.0;
+  double Dmin = +360.0;
+  double Dmax = -360.0;
+
+  // start off where we finished on a previous read
+  int Nstars = *nstars;
+  int NSTARS = Nstars + 0.1*NstarsIn;
+
+  if (!stars) {
+    ALLOCATE (stars, Atlas_Stars, NSTARS);
+  } else {
+    REALLOCATE (stars, Atlas_Stars, NSTARS);
+  }
+
+  for (int i = 0; i < NstarsIn; i++) {
+
+    Rmin = MIN (Rmin, Rg[i]);
+    Rmax = MAX (Rmax, Rg[i]);
+    Dmin = MIN (Dmin, Dg[i]);
+    Dmax = MAX (Dmax, Dg[i]);
+
+    // each atlas source corresponds to 7 measurements: g, r, i, z, j, h, k:
+    dvo_average_init (&stars[Nstars].average);
+    for (int j = 0; j < 7; j++) {
+      dvo_measure_init (&stars[Nstars].measure[j]);
+    }
+
+    stars[Nstars].average.R = Rg[i];
+    stars[Nstars].average.D = Dg[i];
+
+    if (ACCEPT_MOTION) {
+      // atlas values are reported in milliarcseconds, but DVO wants arcseconds
+      stars[Nstars].average.uR    =   uR[i] * 0.001;
+      stars[Nstars].average.uD    =   uD[i] * 0.001;
+      stars[Nstars].average.duR   =  duR[i] * 0.001;
+      stars[Nstars].average.duD   =  duD[i] * 0.001;
+      stars[Nstars].average.P     =  plx[i] * 0.001;
+      stars[Nstars].average.dP    = dplx[i] * 0.001;
+      stars[Nstars].average.Tmean = ATLAS_EPOCH;
+    }
+
+    stars[Nstars].flag  = FALSE;
+    stars[Nstars].found = FALSE;
+
+    for (int j = 0; j < 7; j++) {
+      stars[Nstars].measure[j].R = Rg[i];
+      stars[Nstars].measure[j].D = Dg[i];
+    }
+
+    // measure->M         = NAN;
+    // measure->dM        = NAN;
+    // measure->Map       = NAN;
+    // measure->dMap      = NAN;
+    // measure->Mkron     = NAN;
+    // measure->dMkron    = NAN;
+    // measure->McalPSF   = NAN;
+    // measure->McalAPER  = NAN;
+    // measure->dMcal     = NAN;
+    // measure->dt        = NAN;
+    stars[Nstars].measure[0].M         = gg[i];
+    stars[Nstars].measure[0].dM        = dgg[i];
+    stars[Nstars].measure[0].psfChisq  = cgg[i]; // not sure how to extract this value from avextract 
+    stars[Nstars].measure[0].photFlags = fgg[i];
+    stars[Nstars].measure[0].Mkron     = cgg[i]; // for chisq that can be loaded from avextract photcode:kron
+
+    stars[Nstars].measure[1].M         = rr[i];
+    stars[Nstars].measure[1].dM        = drr[i];
+    stars[Nstars].measure[1].psfChisq  = crr[i];
+    stars[Nstars].measure[1].photFlags = frr[i];
+    stars[Nstars].measure[1].Mkron     = crr[i]; // for chisq that can be loaded from avextract photcode:kron
+
+    stars[Nstars].measure[2].M         = ii[i];
+    stars[Nstars].measure[2].dM        = dii[i];
+    stars[Nstars].measure[2].psfChisq  = cii[i];
+    stars[Nstars].measure[2].photFlags = fii[i];
+    stars[Nstars].measure[2].Mkron     = cii[i]; // for chisq that can be loaded from avextract photcode:kron
+
+    stars[Nstars].measure[3].M         = zz[i];
+    stars[Nstars].measure[3].dM        = dzz[i];
+    stars[Nstars].measure[3].psfChisq  = czz[i];
+    stars[Nstars].measure[3].photFlags = fzz[i];
+    stars[Nstars].measure[3].Mkron     = czz[i]; // for chisq that can be loaded from avextract photcode:kron
+
+    stars[Nstars].measure[4].M         = jj[i];  // WARNING: there are many duplicate values between J and H
+    stars[Nstars].measure[4].dM        = djj[i];
+    stars[Nstars].measure[5].M         = hh[i];
+    stars[Nstars].measure[5].dM        = dhh[i];
+    stars[Nstars].measure[6].M         = kk[i];
+    stars[Nstars].measure[6].dM        = dkk[i];
+
+    stars[Nstars].measure[0].photcode = codeG;
+    stars[Nstars].measure[1].photcode = codeR;
+    stars[Nstars].measure[2].photcode = codeI;
+    stars[Nstars].measure[3].photcode = codeZ;
+    stars[Nstars].measure[4].photcode = codeJ;
+    stars[Nstars].measure[5].photcode = codeH;
+    stars[Nstars].measure[6].photcode = codeK;
+
+    stars[Nstars].average.Nmeasure = 7;
+
+    //printf ("%18ld %10.5f %10.5f %7.3f %6.2f %6.2f %3d %7.3f %7.3f %7.3f %7.3f\n", srcID[i], Rg[i], Dg[i], gg[i], dgg[i], cgg[i], fgg[i], rr[i], ii[i], zz[i], jj[i]);
+    Nstars ++;
+
+    CHECK_REALLOCATE (stars, Atlas_Stars, NSTARS, Nstars, 10000);
+  }
+
+  free (srcID);
+  free (Rg);
+  free (Dg);
+  free (plx);
+  free (dplx);
+  free (uR);
+  free (duR);
+  free (uD);
+  free (duD);
+  free (gg);
+  free (rr);
+  free (ii);
+  free (zz);
+  free (jj);
+  free (hh);
+  free (kk);
+  free (dgg);
+  free (drr);
+  free (dii);
+  free (dzz);
+  free (djj);
+  free (dhh);
+  free (dkk);
+  free (cgg);
+  free (crr);
+  free (cii);
+  free (czz);
+  free (fgg);
+  free (frr);
+  free (fii);
+  free (fzz);
+  free (buffer);
+
+  *nstars = Nstars;
+  return (stars);
+}
+
+int loadatlas_sortStars (Atlas_Stars *stars, int Nstars) {
+
+# define SWAPFUNC(A,B){ Atlas_Stars temp = stars[A]; stars[A] = stars[B]; stars[B] = temp; }
+# define COMPARE(A,B)(stars[A].average.R < stars[B].average.R)
+
+  OHANA_SORT (Nstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+  return TRUE;
+}
+
Index: trunk/Ohana/src/addstar/src/loadatlas_table.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadatlas_table.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadatlas_table.c	(revision 41563)
@@ -0,0 +1,64 @@
+# include "addstar.h"
+# include "atlas.h"
+# define NSTARS_MAX 5000000
+
+int loadatlas_table (int Nstart, int Nend, SkyList *skylistInput, HostTable *hosts, char **filename, AddstarClientOptions *options) {
+  OHANA_UNUSED_PARAM(hosts);
+  
+  int i;
+
+  int Nstars = 0;
+  Atlas_Stars *stars = NULL;
+  for (i = Nstart; (Nstars < NSTARS_MAX) && (i < Nend); i++) {
+    // read the next file and append to the current set of stars
+    fprintf (stderr, "loading %s\n", filename[i]);
+    stars = loadatlas_readstars (filename[i], stars, &Nstars, options);
+  }
+  Nstart = i; // we pass back the entry for the next file to be read
+  if (!stars) return Nstart;
+
+  fprintf (stderr, "writing %d stars to dvo\n", Nstars);
+
+  // sort the stars by RA
+  loadatlas_sortStars (stars, Nstars);
+
+  // scan through the stars, loading the containing catalogs
+  // skip through table for unsaved stars
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].flag) continue;
+
+    // scan forward until we read the UserPatch
+    if (stars[i].average.R < UserPatch.Rmin) continue;
+    if (stars[i].average.R > UserPatch.Rmax) break;
+    if (stars[i].average.D < UserPatch.Dmin) continue;
+    if (stars[i].average.D > UserPatch.Dmax) continue;
+
+    // identify the relevant catalog
+    SkyList *skylist = SkyRegionByPoint_List (skylistInput, -1, stars[i].average.R, stars[i].average.D);
+    if (skylist[0].Nregions == 0) {
+      SkyListFree (skylist);
+      continue;
+    }
+    SkyRegion *region = skylist[0].regions[0];
+
+    // select stars matching this region
+    int Nsubset;
+    Atlas_Stars *subset = loadatlas_make_subset (stars, Nstars, i, region, &Nsubset);
+
+    // In parallel mode, write out the subset to a disk file.  Block until a remote host
+    // is available.  In serial mode, just match against the appropriate region and save
+    // NOTE: disable parallel mode for now: 
+    // loadatlas_save_remote (subset, Nsubset, hosts, region, skylist[0].filename[0], options);
+    loadatlas_catalog (subset, Nsubset, region, skylist[0].filename[0], options);
+    free (subset);
+    SkyListFree (skylist);
+  }
+
+  // wait for last remote clients to finish
+  // NOTE: disable parallel mode for now: 
+  // harvest_all ();
+
+  free (stars);
+  return Nstart;
+}
+
Index: trunk/Ohana/src/addstar/src/loadgaia_edr3.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadgaia_edr3.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadgaia_edr3.c	(revision 41563)
@@ -0,0 +1,43 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+/* This is the DVO program to upload gaia_edr3 detections from gaia cvs files into a DVO database.
+   It is modeled on the loadgaia program but the source files are CSV tables.
+
+   USAGE: loadgaia_edr3 -D CATDIR (catdir) (gaia_edr3file) [...more files]
+*/
+
+int main (int argc, char **argv) {
+
+  SkyTable *sky;
+  SkyList *skylist = NULL;
+  AddstarClientOptions options;
+
+  // need to construct these options with args_loadtycho...
+  SetSignals ();
+  options = ConfigInit (&argc, argv);
+  options = args_loadgaia_edr3 (&argc, argv, options);
+
+  // load the full sky description table:
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // generate the subset matching the user-selected region
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  int Nstart = 1;
+  int Nend = argc;
+  while (Nstart < Nend) {
+    Nstart = loadgaia_edr3_table (Nstart, Nend, skylist, NULL, argv, &options);
+  }
+
+  SkyTableFree (sky);
+  SkyListFree(skylist);
+  FreePhotcodeTable();
+  free (CATDIR);
+
+  ohana_memcheck (VERBOSE);
+  ohana_memdump (VERBOSE);
+  exit (0);
+}  
+
Index: trunk/Ohana/src/addstar/src/loadgaia_edr3_catalog.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadgaia_edr3_catalog.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadgaia_edr3_catalog.c	(revision 41563)
@@ -0,0 +1,32 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+int loadgaia_edr3_catalog (Gaia_EDR3_Stars *stars, int Nstars, SkyRegion *region, char *filename, AddstarClientOptions *options) {
+
+  Catalog catalog;
+
+  // now we have all of the loaded stars in this catalog
+  dvo_catalog_init (&catalog, TRUE);
+  catalog.filename  = filename;
+  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  = DVO_LOAD_AVERAGE | DVO_LOAD_SECFILT | DVO_LOAD_MEASURE;
+  catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+    
+  // an error exit status here is a significant error
+  if (!dvo_catalog_open (&catalog, region, VERBOSE, "w")) {
+    fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+    exit (2);
+  }
+
+  find_matches_gaia_edr3 (region, stars, Nstars, &catalog, options);
+    
+  SetProtect (TRUE);
+  if (!dvo_catalog_save (&catalog, VERBOSE)) { fprintf (stderr, "ERROR: failed to save %s\n", catalog.filename); exit (1); }
+  if (!dvo_catalog_unlock (&catalog)) { fprintf (stderr, "ERROR: failed to unlock %s\n", catalog.filename); exit (1); }
+  SetProtect (FALSE);
+
+  dvo_catalog_free (&catalog);
+
+  return (TRUE);
+}
Index: trunk/Ohana/src/addstar/src/loadgaia_edr3_make_subset.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadgaia_edr3_make_subset.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadgaia_edr3_make_subset.c	(revision 41563)
@@ -0,0 +1,44 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+// assign stars in the given region to the subset (NOTE: stars are sorted by RA, start is
+// first entry in stars array in this region)
+
+Gaia_EDR3_Stars *loadgaia_edr3_make_subset (Gaia_EDR3_Stars *stars, int Nstars, int start, SkyRegion *region, int *nsubset) {
+
+  int i;
+
+  Gaia_EDR3_Stars *subset = NULL;
+
+  // collect array of (Stars *) stars in a new output catalog
+  int Nsubset = 0;
+  int NSUBSET = 3000;
+  ALLOCATE (subset, Gaia_EDR3_Stars, NSUBSET);
+
+  // find the rest of the stars in this output region
+  for (i = start; i < Nstars; i++) {
+    if (stars[i].flag) continue;
+
+    // check if in skyregion
+    if (stars[i].average.R <  region[0].Rmin) continue;
+    if (stars[i].average.R >= region[0].Rmax) break;
+    if (stars[i].average.D <  region[0].Dmin) continue;
+    if (stars[i].average.D >= region[0].Dmax) continue;
+	  
+    // check if in UserPatch (a GLOBAL)
+    if (stars[i].average.R < UserPatch.Rmin) continue;
+    if (stars[i].average.R > UserPatch.Rmax) break;
+    if (stars[i].average.D < UserPatch.Dmin) continue;
+    if (stars[i].average.D > UserPatch.Dmax) continue;
+	  
+    subset[Nsubset] = stars[i];
+    Nsubset ++;
+
+    stars[i].flag = TRUE;
+
+    CHECK_REALLOCATE (subset, Gaia_EDR3_Stars, NSUBSET, Nsubset, 10000);
+  }
+
+  *nsubset = Nsubset;
+  return subset;
+}
Index: trunk/Ohana/src/addstar/src/loadgaia_edr3_readstars.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadgaia_edr3_readstars.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadgaia_edr3_readstars.c	(revision 41563)
@@ -0,0 +1,466 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+
+/* this function reads the values of interest from the Gaia CSV files:
+
+  data model description:
+  http://gea.esac.esa.int/archive/documentation/GEDR3/Gaia_archive/chap_datamodel/sec_dm_main_tables/ssec_dm_gaia_source.html
+# proper motion
+  3 source_id (map to extID)
+  5 ref_epoch
+  6 ra
+  7 ra_error
+  8 dec
+  9 dec_error
+ 10 parallax
+ 11 parallax_error
+ 12 parallax_over_error
+ 13 pm
+ 14 pmra
+ 15 pmra_error
+ 16 pmdec
+ 17 pmdec_error
+# astrometric
+ 28 astrometric_n_obs_al
+ 30 astrometric_n_good_obs_al
+ 32 astrometric_gof_al
+ 33 astrometric_chi2_al
+ 34 astrometric_excess_noise
+ 35 astrometric_excess_noise_sig
+ 47 visibility_periods_used
+ 65 duplicated_source
+# phot info
+ 66 phot_g_n_obs
+ 67 phot_g_mean_flux
+ 68 phot_g_mean_flux_error
+ 69 phot_g_mean_flux_over_error
+ 70 phot_g_mean_mag
+ 71 phot_bp_n_obs
+ 72 phot_bp_mean_flux
+ 73 phot_bp_mean_flux_error
+ 74 phot_bp_mean_flux_over_error
+ 75 phot_bp_mean_mag
+ 76 phot_rp_n_obs
+ 77 phot_rp_mean_flux
+ 78 phot_rp_mean_flux_error
+ 79 phot_rp_mean_flux_over_error
+ 80 phot_rp_mean_mag
+
+ 85 phot_proc_mode : use for a flag
+
+//     - sqrt(astrometric_chi2_al / (astrometric_n_good_obs_al - 5)) < 1.2 * max(1.0, exp(-0.2 * (phot_g_mean_mag - 19.5)))
+
+ we have 32 x 2 bits of photFlags potentially available for use. Gaia does have have a single flag
+ value, so I will generate bits to represent some data quality cuts proposed in the Gaia docs.
+
+ I'm also going to overload a couple of values as needed:
+
+ measure.psfNpix   : astrometric_n_obs_al (nObs)
+ measure.psfNdof   : astrometric_n_good_obs_al (nGood)
+ measure.extNsigma : astrometric_gof_al (goFit)
+ measure.psfChisq  : astrometric_chi2_al (chiSq)
+ measure.psfQF     : astrometric_excess_noise (exNoise)
+ measure.psfQFPerf : astrometric_excess_noise_sig (exNoiseSig)
+ measure.FWx       : visibility_periods_used (nPeriods)
+
+ photFlags : 
+ 0x0000.0001 : visibility_periods_used > 6 (nPeriods > 6)
+ 0x0000.0002 : visibility_periods_used > 8 (nPeriods > 8)
+ 0x0000.0004 : astrometric_excess_noise < 1.0 (exNoise < 1)
+ 0x0000.0008 : ChiSq < limit (
+
+ 0x0000.0010 : duplicated_source
+ 0x0000.0020 : phot_proc_mode == 0
+ 0x0000.0040 : phot_proc_mode == 1
+ 0x0000.0080 : phot_proc_mode == 2
+
+*/
+
+Gaia_EDR3_Stars *loadgaia_edr3_readstars (char *filename, Gaia_EDR3_Stars *stars, int *nstars, AddstarClientOptions *options) {
+
+  int codeG = GetPhotcodeCodebyName ("GAIA_G_EDR3"); if (!codeG) Shutdown ("missing photocde GAIA_G_EDR3");
+  int codeB = GetPhotcodeCodebyName ("GAIA_B_EDR3"); if (!codeB) Shutdown ("missing photocde GAIA_B_EDR3");
+  int codeR = GetPhotcodeCodebyName ("GAIA_R_EDR3"); if (!codeR) Shutdown ("missing photocde GAIA_R_EDR3");
+
+  // GAIA EDR3 Epoch is 2016.0 == 2016/01/01,00:00:00
+  time_t GAIA_EDR3_EPOCH = ohana_date_to_sec ("2016/01/01,00:00:00");
+
+  // read in the full FITS files
+  FILE *f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read gaia_edr3 file: %s", filename);
+
+  int Nskip = 1; // Gaia CSV files have a single header row (and now special character to mark)
+
+  int Nelem = 0;      // number of valid rows read (vector elements)
+  int NELEM = 1000000;  // currently-allocated number of output rows
+
+  // for initial testing, a minimal subset:
+  ALLOCATE_PTR (srcID, uint64_t, NELEM);
+
+  ALLOCATE_PTR (Rg, double, NELEM);
+  ALLOCATE_PTR (dRg, double, NELEM);
+  ALLOCATE_PTR (Dg, double, NELEM);
+  ALLOCATE_PTR (dDg, double, NELEM);
+
+  ALLOCATE_PTR (plx, double, NELEM);
+  ALLOCATE_PTR (dplx, double, NELEM);
+
+  ALLOCATE_PTR (uR, double, NELEM);
+  ALLOCATE_PTR (duR, double, NELEM);
+
+  ALLOCATE_PTR (uD, double, NELEM);
+  ALLOCATE_PTR (duD, double, NELEM);
+
+  ALLOCATE_PTR (nObs, int, NELEM);
+  ALLOCATE_PTR (nGood, int, NELEM);
+
+  ALLOCATE_PTR (goFit, double, NELEM);
+  ALLOCATE_PTR (chiSq, double, NELEM);
+
+  ALLOCATE_PTR (exNoise, double, NELEM);
+  ALLOCATE_PTR (exNoiseSig, double, NELEM);
+
+  ALLOCATE_PTR (nPeriods, int, NELEM);
+  ALLOCATE_PTR (dupSource, int, NELEM);
+
+  ALLOCATE_PTR (gg, double, NELEM);
+  ALLOCATE_PTR (dgg, double, NELEM);
+  ALLOCATE_PTR (ngg, int, NELEM);
+
+  ALLOCATE_PTR (bg, double, NELEM);
+  ALLOCATE_PTR (dbg, double, NELEM);
+  ALLOCATE_PTR (nbg, int, NELEM);
+
+  ALLOCATE_PTR (rg, double, NELEM);
+  ALLOCATE_PTR (drg, double, NELEM);
+  ALLOCATE_PTR (nrg, int, NELEM);
+
+  ALLOCATE_PTR (procMode, int, NELEM);
+
+  // we allocate one extra byte into which we never read so there will always be a NULL terminating the string
+  ALLOCATE_PTR (buffer, char, 0x10001);
+  bzero (buffer, 0x10001);
+
+  int Nline_read = 0; // track number of lines read so far (use to skip lines as well)
+
+  // we have a working buffer read from the file. we parse the lines in the working buffer
+  // until we reach the last chunk without an EOL char.  at that point, we shift the start
+  // of the last (partial) line to the start of the buffer and re-fill.
+
+  // we treat \n\r pair as a single EOL char to handle mac files:
+
+  int Nstart = 0; // location of the last valid byte in the buffer (start filling here)
+  int EndOfFile = FALSE;
+  while (!EndOfFile) {
+    int Nbytes = 0x10000 - Nstart;
+    // we have allocated one extra byte into which we never read so there will always be a NULL terminating the string
+    bzero (&buffer[Nstart], Nbytes + 1);
+    int Nread = fread (&buffer[Nstart], 1, Nbytes, f);
+    if (ferror (f)) {
+      perror ("error reading data file");
+      break;
+    }
+    // we still need to parse the rest of the buffer, but there might not be an EOL on the last line
+    if (Nread == 0) {
+      EndOfFile = TRUE;
+    }
+    
+    int bufferStatus = TRUE; 
+    char *c0 = buffer; // c0 always marks the start of a line
+    char *cA = NULL; // cA will carry the curr point within the line
+    while (bufferStatus) {
+
+      // find the end of this current line (\n or \r).  if we hit the end of the buffer (NULL),
+      // attempt to read more data.  finish up when we hit the end of the file
+      char *c1 = strchr (c0, '\n'); // find the end of this current line (also valid for a Mac: \r\n)
+      if (!c1) {
+	c1 = strchr (c0, '\r'); // try \r for Windows files
+      }
+      if (!c1) {
+	Nstart = strlen (c0);
+	if (EndOfFile) {
+	  // if we have reached EOF, we need to do one last pass in case there is a line without a return
+	  c1 = c0 + Nstart;
+	  bufferStatus = FALSE;
+	  if (Nstart == 0) continue; // if we have reached EOF and c0 points at the last valid character, we are done
+	} else {
+	  // if we have not reached EOF, we need to shift the buffer to the start of this line and read more data
+	  memmove (buffer, c0, Nstart);
+	  bufferStatus = FALSE;
+	  continue;
+	}
+      }
+      *c1 = 0; // mark the end of the line 
+      Nline_read ++;
+
+      // skip to the next line (but if EOF, do not overrun buffer)
+      if (Nline_read <= Nskip) { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+
+      // these are not needed: Gaia CSV files do not have any commented-out lines
+      if (*c0 == '#')          { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+      if (*c0 == '!')          { if (!EndOfFile) { c0 = c1 + 1; } continue; }
+
+      // XXX for Gaia, we know which columns we want in advance
+
+      int lineStatus = TRUE;
+      int readStatus;
+      double dvalue;
+      int ivalue;
+      uint64_t jvalue;
+
+      // cA will follow the currently extracted field, c0 points to the start of the line
+      cA = c0;
+
+      // Tref == 2016.0 for EDR3
+
+      // the start of the line is the 1st element (fields are 1-counting)
+      cA = jparse_csv_rpt (&jvalue,  3,  1, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  3, "source_id", c0); }  	       lineStatus &= readStatus; srcID[Nelem]     = jvalue;
+      cA = dparse_csv_rpt (&dvalue,  6,  3, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  6, "Ra", c0); }         	       lineStatus &= readStatus; Rg[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  7,  6, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  7, "dRa", c0); }        	       lineStatus &= readStatus; dRg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  8,  7, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  8, "Dec", c0); }        	       lineStatus &= readStatus; Dg[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue,  9,  8, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n",  9, "dDec", c0); }       	       lineStatus &= readStatus; dDg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 10,  9, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 10, "plx", c0); }        	       lineStatus &= readStatus; plx[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 11, 10, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 11, "dplx", c0); }       	       lineStatus &= readStatus; dplx[Nelem]      = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 14, 11, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 14, "uR", c0); }         	       lineStatus &= readStatus; uR[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 15, 14, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 15, "duR", c0); }        	       lineStatus &= readStatus; duR[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 16, 15, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 16, "uD", c0); }         	       lineStatus &= readStatus; uD[Nelem]        = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 17, 16, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 17, "duD", c0); }        	       lineStatus &= readStatus; duD[Nelem]       = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 28, 17, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 28, "n_obs", c0); }      	       lineStatus &= readStatus; nObs[Nelem]      = ivalue;
+      cA = iparse_csv_rpt (&ivalue, 30, 28, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 30, "n_good", c0); }     	       lineStatus &= readStatus; nGood[Nelem]     = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 32, 30, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 32, "GOF", c0); }        	       lineStatus &= readStatus; goFit[Nelem]     = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 33, 32, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 33, "chiSq", c0); }      	       lineStatus &= readStatus; chiSq[Nelem]     = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 34, 33, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 34, "exNoise", c0); }    	       lineStatus &= readStatus; exNoise[Nelem]   = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 35, 34, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 35, "exNoiseSig", c0); } 	       lineStatus &= readStatus; exNoiseSig[Nelem]= dvalue;
+      cA = iparse_csv_rpt (&ivalue, 47, 35, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 47, "visibility_periods", c0); } lineStatus &= readStatus; nPeriods[Nelem]  = ivalue;
+					
+      // the duplicated_source field has the value false (or true)
+      for (int i = 47; i < 65; i++) cA = parse_nextword_csv (cA);
+      dupSource[Nelem] = strncmp (cA, "false", 5) ? 1 : 0; 
+//    cA = dparse_csv_rpt (&dvalue, 65, 47, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 65, "phot_r", c0); } lineStatus &= readStatus;  dupSource[Nelem] = dvalue;
+
+      cA = iparse_csv_rpt (&ivalue, 66, 65, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 66, "nphot_g", c0); }            lineStatus &= readStatus;  ngg[Nelem]       = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 69, 66, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 69, "dphot_g", c0); }            lineStatus &= readStatus;  dgg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 70, 69, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 70, "phot_g", c0); }             lineStatus &= readStatus;  gg[Nelem]        = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 71, 70, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 71, "nphot_b", c0); }            lineStatus &= readStatus;  nbg[Nelem]       = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 74, 71, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 74, "dphot_b", c0); }            lineStatus &= readStatus;  dbg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 75, 74, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 75, "phot_b", c0); }             lineStatus &= readStatus;  bg[Nelem]        = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 76, 75, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 76, "nphot_r", c0); }            lineStatus &= readStatus;  nrg[Nelem]       = ivalue;
+      cA = dparse_csv_rpt (&dvalue, 79, 76, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 79, "dphot_r", c0); }            lineStatus &= readStatus;  drg[Nelem]       = dvalue;
+      cA = dparse_csv_rpt (&dvalue, 80, 79, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 80, "phot_r", c0); }             lineStatus &= readStatus;  rg[Nelem]        = dvalue;
+      cA = iparse_csv_rpt (&ivalue, 85, 80, cA, &readStatus); if (!readStatus && VERBOSE) { gprint (GP_ERR, "suspect field: %d (%s) in %s\n", 85, "phot_mode", c0); }          lineStatus &= readStatus;  procMode[Nelem]  = ivalue;
+
+      if (!lineStatus && VERBOSE) {
+	// why do I need to copy temp here, does gprint modify the value of temp?
+	char temp[32];
+	strncpy (temp, c0, 32);
+	temp[31] = 0;
+	gprint (GP_ERR, "skip line %s\n\n", temp);
+      }
+
+      Nelem ++;
+      if (Nelem == NELEM) {
+	NELEM += 1000;
+        REALLOCATE (srcID, uint64_t, NELEM);
+        REALLOCATE (Rg, double, NELEM);
+        REALLOCATE (dRg, double, NELEM);
+        REALLOCATE (Dg, double, NELEM);
+        REALLOCATE (dDg, double, NELEM);
+        REALLOCATE (plx, double, NELEM);
+        REALLOCATE (dplx, double, NELEM);
+        REALLOCATE (uR, double, NELEM);
+        REALLOCATE (duR, double, NELEM);
+        REALLOCATE (uD, double, NELEM);
+        REALLOCATE (duD, double, NELEM);
+        REALLOCATE (nObs, int, NELEM);
+        REALLOCATE (nGood, int, NELEM);
+        REALLOCATE (goFit, double, NELEM);
+        REALLOCATE (chiSq, double, NELEM);
+        REALLOCATE (exNoise, double, NELEM);
+        REALLOCATE (exNoiseSig, double, NELEM);
+        REALLOCATE (nPeriods, int, NELEM);
+        REALLOCATE (dupSource, int, NELEM);
+        REALLOCATE (gg, double, NELEM);
+        REALLOCATE (dgg, double, NELEM);
+        REALLOCATE (ngg, int, NELEM);
+        REALLOCATE (bg, double, NELEM);
+        REALLOCATE (dbg, double, NELEM);
+        REALLOCATE (nbg, int, NELEM);
+        REALLOCATE (rg, double, NELEM);
+        REALLOCATE (drg, double, NELEM);
+        REALLOCATE (nrg, int, NELEM);
+        REALLOCATE (procMode, int, NELEM);
+      }
+      if (!EndOfFile) {
+	c0 = c1 + 1;
+      }
+    }
+  }
+
+  // Nelem is now the number of items (objects,stars) read from the Gaia CSV file
+  int NstarsIn = Nelem;
+
+  double Rmin = +360.0;
+  double Rmax = -360.0;
+  double Dmin = +360.0;
+  double Dmax = -360.0;
+
+  // start off where we finished on a previous read
+  int Nstars = *nstars;
+  int NSTARS = Nstars + 0.1*NstarsIn;
+
+  if (!stars) {
+    ALLOCATE (stars, Gaia_EDR3_Stars, NSTARS);
+  } else {
+    REALLOCATE (stars, Gaia_EDR3_Stars, NSTARS);
+  }
+
+  for (int i = 0; i < NstarsIn; i++) {
+
+    Rmin = MIN (Rmin, Rg[i]);
+    Rmax = MAX (Rmax, Rg[i]);
+    Dmin = MIN (Dmin, Dg[i]);
+    Dmax = MAX (Dmax, Dg[i]);
+
+    // each Gaia EDR3 source corresponds to 3 measurements: Gg, Gr, Gb:
+
+    dvo_average_init (&stars[Nstars].average);
+    for (int j = 0; j < 3; j++) {
+      dvo_measure_init (&stars[Nstars].measure[j]);
+    }
+
+    stars[Nstars].average.R = Rg[i];
+    stars[Nstars].average.D = Dg[i];
+    stars[Nstars].average.dR = 0.001*dRg[i];
+    stars[Nstars].average.dD = 0.001*dDg[i];
+
+    if (ACCEPT_MOTION) {
+      // GAIA values are reported in milliarcseconds, but DVO wants arcseconds
+      stars[Nstars].average.uR    =   uR[i] * 0.001;
+      stars[Nstars].average.uD    =   uD[i] * 0.001;
+      stars[Nstars].average.duR   =  duR[i] * 0.001;
+      stars[Nstars].average.duD   =  duD[i] * 0.001;
+      stars[Nstars].average.P     =  plx[i] * 0.001;
+      stars[Nstars].average.dP    = dplx[i] * 0.001;
+      stars[Nstars].average.Tmean = GAIA_EDR3_EPOCH;
+    }
+
+    stars[Nstars].flag  = FALSE;
+    stars[Nstars].found = FALSE;
+
+    // for the following cut, see https://arxiv.org/pdf/1804.09378.pdf (Gaia Collaboration et al 2018)
+    double ChiSqLimit = (nGood[i] > 5) ? sqrt(chiSq[i] / (nGood[i] - 5)) : NAN;
+    double ChiCompare = 1.2 * MAX (1.0, exp(-0.2*(gg[i] - 19.5)));
+
+    int NVis6   = (nPeriods[i] >  6)        ? 0x00000001 : 0x0;
+    int NVis8   = (nPeriods[i] >  8)        ? 0x00000002 : 0x0;
+    int ExNoise = (exNoise[i] < 1.0)        ? 0x00000004 : 0x0;
+    int ChiGood = (ChiSqLimit < ChiCompare) ? 0x00000008 : 0x0;
+
+    int isDup = dupSource[i]                ? 0x00000010 : 0x0;
+    int Mode0 = (procMode[i] == 0)          ? 0x00000020 : 0x0;
+    int Mode1 = (procMode[i] == 1)          ? 0x00000040 : 0x0;
+    int Mode2 = (procMode[i] == 2)          ? 0x00000080 : 0x0;
+
+    int photFlags = NVis6 | NVis8 | ExNoise | ChiGood | isDup | Mode0 | Mode1 | Mode2;
+
+    for (int j = 0; j < 3; j++) {
+      stars[Nstars].measure[j].R = Rg[i];
+      stars[Nstars].measure[j].D = Dg[i];
+
+      //stars[Nstars].measure[j].psfNpix = nObs[i];
+      //stars[Nstars].measure[j].psfNdof = nGood[i];
+      stars[Nstars].measure[j].psfNpix = ngg[i] + nbg[i] + nrg[i];
+      stars[Nstars].measure[j].extNsigma = goFit[i];
+      stars[Nstars].measure[j].psfChisq = chiSq[i];
+      stars[Nstars].measure[j].psfQF = exNoise[i];
+      stars[Nstars].measure[j].psfQFperf = exNoiseSig[i];
+      stars[Nstars].measure[j].FWx = nPeriods[i]; // this one is not great
+      stars[Nstars].measure[j].photFlags = photFlags;
+      stars[Nstars].measure[j].t = GAIA_EDR3_EPOCH;
+    }
+
+    // the flux reported by Gaia is in electrons / second; we just want to use the mags:
+    // dgg, dbg, drg store (flux / error), i.e., S/N.  mag error is 2.5/ln(10) ~ 1.0857 / (S/N)
+    stars[Nstars].measure[0].M  = gg[i];
+    stars[Nstars].measure[0].dM = 1.08573620476 / dgg[i];
+    stars[Nstars].measure[0].psfNdof = ngg[i];
+
+    stars[Nstars].measure[1].M = bg[i];
+    stars[Nstars].measure[1].dM = 1.08573620476 / dbg[i];
+    stars[Nstars].measure[1].psfNdof = nbg[i];
+
+    stars[Nstars].measure[2].M = rg[i];
+    stars[Nstars].measure[2].dM = 1.08573620476 / drg[i];
+    stars[Nstars].measure[2].psfNdof = nrg[i];
+
+    stars[Nstars].measure[0].photcode = codeG;
+    stars[Nstars].measure[1].photcode = codeB;
+    stars[Nstars].measure[2].photcode = codeR;
+
+    // printf ("%10.5f %10.5f %10.5f\n", gg[i], bg[i], rg[i]);
+    // stars[Nstars].measure.dM = dmgFlux[i] / flux;
+    // stars[Nstars].measure.FluxPSF = flux;
+    // stars[Nstars].measure.dFluxPSF = dgFlux[i];
+    // stars[Nstars].measure.t = options->timeref;
+
+    stars[Nstars].average.Nmeasure = 3;
+    // Not sure how to make these work
+    stars[Nstars].average.Npos     = nGood[i];
+    stars[Nstars].average.Nmissing = nObs[i] - nGood[i];
+    //stars[Nstars].average.Nmissing = 1;
+
+    printf ("%5d %5d %5d %5d %5d %5d\n", nObs[i], nGood[i], ngg[i], nbg[i], nrg[i], nPeriods[i]);
+    Nstars ++;
+
+    CHECK_REALLOCATE (stars, Gaia_EDR3_Stars, NSTARS, Nstars, 10000);
+  }
+
+  free (srcID);
+  free (Rg);
+  free (dRg);
+  free (Dg);
+  free (dDg);
+  free (plx);
+  free (dplx);
+  free (uR);
+  free (duR);
+  free (uD);
+  free (duD);
+  free (nObs);
+  free (nGood);
+  free (goFit);
+  free (chiSq);
+  free (exNoise);
+  free (exNoiseSig);
+  free (nPeriods);
+  free (dupSource);
+  free (gg);
+  free (dgg);
+  free (ngg);
+  free (bg);
+  free (dbg);
+  free (nbg);
+  free (rg);
+  free (drg);
+  free (nrg);
+ // free (procMode);
+
+  free (buffer);
+
+  *nstars = Nstars;
+  return (stars);
+}
+
+int loadgaia_edr3_sortStars (Gaia_EDR3_Stars *stars, int Nstars) {
+
+# define SWAPFUNC(A,B){ Gaia_EDR3_Stars temp = stars[A]; stars[A] = stars[B]; stars[B] = temp; }
+# define COMPARE(A,B)(stars[A].average.R < stars[B].average.R)
+
+  OHANA_SORT (Nstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+  return TRUE;
+}
+
Index: trunk/Ohana/src/addstar/src/loadgaia_edr3_table.c
===================================================================
--- trunk/Ohana/src/addstar/src/loadgaia_edr3_table.c	(revision 41563)
+++ trunk/Ohana/src/addstar/src/loadgaia_edr3_table.c	(revision 41563)
@@ -0,0 +1,64 @@
+# include "addstar.h"
+# include "gaia_edr3.h"
+# define NSTARS_MAX 1000000
+
+int loadgaia_edr3_table (int Nstart, int Nend, SkyList *skylistInput, HostTable *hosts, char **filename, AddstarClientOptions *options) {
+  OHANA_UNUSED_PARAM(hosts);
+  
+  int i;
+
+  int Nstars = 0;
+  Gaia_EDR3_Stars *stars = NULL;
+  for (i = Nstart; (Nstars < NSTARS_MAX) && (i < Nend); i++) {
+    // read the next file and append to the current set of stars
+    fprintf (stderr, "loading %s\n", filename[i]);
+    stars = loadgaia_edr3_readstars (filename[i], stars, &Nstars, options);
+  }
+  Nstart = i; // we pass back the entry for the next file to be read
+  if (!stars) return Nstart;
+
+  fprintf (stderr, "writing %d stars to dvo\n", Nstars);
+
+  // sort the stars by RA
+  loadgaia_edr3_sortStars (stars, Nstars);
+
+  // scan through the stars, loading the containing catalogs
+  // skip through table for unsaved stars
+  for (i = 0; i < Nstars; i++) {
+    if (stars[i].flag) continue;
+
+    // scan forward until we read the UserPatch
+    if (stars[i].average.R < UserPatch.Rmin) continue;
+    if (stars[i].average.R > UserPatch.Rmax) break;
+    if (stars[i].average.D < UserPatch.Dmin) continue;
+    if (stars[i].average.D > UserPatch.Dmax) continue;
+
+    // identify the relevant catalog
+    SkyList *skylist = SkyRegionByPoint_List (skylistInput, -1, stars[i].average.R, stars[i].average.D);
+    if (skylist[0].Nregions == 0) {
+      SkyListFree (skylist);
+      continue;
+    }
+    SkyRegion *region = skylist[0].regions[0];
+
+    // select stars matching this region
+    int Nsubset;
+    Gaia_EDR3_Stars *subset = loadgaia_edr3_make_subset (stars, Nstars, i, region, &Nsubset);
+
+    // In parallel mode, write out the subset to a disk file.  Block until a remote host
+    // is available.  In serial mode, just match against the appropriate region and save
+    // NOTE: disable parallel mode for now: 
+    // loadgaia_edr3_save_remote (subset, Nsubset, hosts, region, skylist[0].filename[0], options);
+    loadgaia_edr3_catalog (subset, Nsubset, region, skylist[0].filename[0], options);
+    free (subset);
+    SkyListFree (skylist);
+  }
+
+  // wait for last remote clients to finish
+  // NOTE: disable parallel mode for now: 
+  // harvest_all ();
+
+  free (stars);
+  return Nstart;
+}
+
Index: trunk/ippconfig/dvo.photcodes
===================================================================
--- trunk/ippconfig/dvo.photcodes	(revision 41562)
+++ trunk/ippconfig/dvo.photcodes	(revision 41563)
@@ -27,6 +27,6 @@
 # external references: 1000 - 2999
 
-#                                           airmass      color                         astrometry  mag    photom  astrom mask    photom mask
-# code  name                type    zero  slope offset c1    c2   slope   zero  equiv  sys scale   scale  sys     poor   bad     poor   bad
+#                                           airmass      color                         astrometry  mag    photom  astrom mask    photom mask  
+# code  name                type    zero  slope offset c1    c2   slope   zero  equiv  sys scale   scale  sys     poor   bad     poor   bad  
   1     g                    sec   0.000  0.000 0.000     1     3 0.0000     0  1051   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2     r                    sec   0.000  0.000 0.000     2     3 0.0000     0  1052   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -38,5 +38,5 @@
   8     K                    sec   0.000  0.000 0.000     -     - 0.0000     0  2013   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   9     w                    sec   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# L92 UBVRI 
   1010  U_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1011  B_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -44,5 +44,5 @@
   1013  R_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1014  I_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# STET UBVIR 
   1020  U_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1021  B_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -50,5 +50,5 @@
   1023  R_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1024  I_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# SDSS ugriz,UGRIZ 
   1050  u_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1051  g_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -93,5 +93,5 @@
   1088  SDSS.g.55            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1089  SDSS.g.56            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# USNO BLUE,RED,J,F,N 
   1100  GSC                  ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1000  USNO_BLUE            ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -136,5 +136,5 @@
   1143  USNO.IVN.RG9         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1144  USNO.IVN.WR88A       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# SCOS 
   1150  SCOS.103a.E          dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.100 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
   1151  SCOS.4414.OG590      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.100 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -147,28 +147,34 @@
   1158  SCOS.IVN.RG715       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.100 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
   1159  SCOS.IVN.RG9         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.100 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# TYCHO BV 
   2020  TYCHO_B              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2021  TYCHO_V              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2022  TYCHO_B_2MASS        ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2023  TYCHO_V_2MASS        ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# GAIA G,BR 
   1030  GAIA_G_DR1           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1031  GAIA_G_DR2           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1032  GAIA_B_DR2           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   1033  GAIA_R_DR2           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
-  2025  BSC_U        	     ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-  2026  BSC_B        	     ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1034  GAIA_G_EDR3          ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1035  GAIA_B_EDR3          ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1036  GAIA_R_EDR3          ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1037  GAIA_G_DR3           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1038  GAIA_B_DR3           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1039  GAIA_R_DR3           ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# BSC UBV 
+  2025  BSC_U                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  2026  BSC_B                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2027  BSC_V                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# 2MASS JHK 
   2011  2MASS_J              ref   0.000  0.000 0.000     -     - 0.0000     0     6   0.080 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
   2012  2MASS_H              ref   0.000  0.000 0.000     -     - 0.0000     0     7   0.080 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
   2013  2MASS_K              ref   0.000  0.000 0.000     -     - 0.0000     0     8   0.080 0.000 2.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# WISE W1234 
   2014  WISE_W1              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
   2015  WISE_W2              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
   2016  WISE_W3              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
   2017  WISE_W4              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# UKIRT JYJHK 
   2031  UKIRT.Z.01           dep  22.840  0.050 0.000     -     - 0.0000     0     4   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
   2032  UKIRT.Z.02           dep  22.840  0.050 0.000     -     - 0.0000     0     4   0.080 0.000 5.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -197,5 +203,5 @@
   2083  UKIRT_H              ref   0.000  0.000 0.000     -     - 0.0000     0     7   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2084  UKIRT_K              ref   0.000  0.000 0.000     -     - 0.0000     0     8   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# ICRF 
   # ICRF QSOs have astrometric error stored in dX,dY as 1/100th mas 
   2090  ICRF_S               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.001 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -204,5 +210,5 @@
   2093  ICRF_U               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.001 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   2094  ICRF_K               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.001 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# SYNTH grizyw 
   3001  SYNTH.g              ref   0.000  0.000 0.000     -     - 0.0000     0     1     nan 0.000 2.000  0.600   0x0000 0x0000  0x0000 0x0000
   3002  SYNTH.r              ref   0.000  0.000 0.000     -     - 0.0000     0     2     nan 0.000 2.000  0.600   0x0000 0x0000  0x0000 0x0000
@@ -211,5 +217,12 @@
   3005  SYNTH.y              ref   0.000  0.000 0.000     -     - 0.0000     0     5     nan 0.000 2.000  0.600   0x0000 0x0000  0x0000 0x0000
   3006  SYNTH.w              ref   0.000  0.000 0.000     -     - 0.0000     0     9     nan 0.000 2.000  0.600   0x0000 0x0000  0x0000 0x0000
-
+# REF grizyw  
+  4001  g_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     1     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  4002  r_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     2     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  4003  i_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     3     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  4004  z_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     4     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  4005  y_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     5     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  4006  w_PS                 ref   0.000  0.000 0.000     -     - 0.0000     0     9     nan 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# MEGACAM ugriz 
   100   MEGACAM.u.00         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
   101   MEGACAM.u.01         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
@@ -412,4 +425,5 @@
   538   MEGACAM.z.38         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
   539   MEGACAM.z.39         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
+# CFHT BVRIZetc 
   140   CFH12K.B.00          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
   141   CFH12K.B.01          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
@@ -544,4 +558,5 @@
   692   CFH12K.B2F.10        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   693   CFH12K.B2F.11        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# ISP grizy 
   3200  ISP-Apogee-01.g      dep  19.250  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   3300  ISP-Apogee-01.r      dep  19.150  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -549,7 +564,9 @@
   3500  ISP-Apogee-01.z      dep  17.250  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   3600  ISP-Apogee-01.y      dep  14.150  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# VYSO ri 
   5002  VYSOS5.r             dep  17.200  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   5022  VYSOS20.r            dep  21.400  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   5023  VYSOS20.i            dep  20.500  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# SIMTEST grizy 
   4100  SIMTEST.g.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   4200  SIMTEST.r.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -567,4 +584,5 @@
   15400 SIMTEST.z.ForcedWarp dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   15500 SIMTEST.y.ForcedWarp dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# SIMMOSAIC grizy 
   4101  SIMMOSAIC.g.00       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   4102  SIMMOSAIC.g.01       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -587,4 +605,6 @@
   4503  SIMMOSAIC.y.02       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   4504  SIMMOSAIC.y.03       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# GPC1 grizyw 
+ # g 
   10001 GPC1.g.XY01          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10002 GPC1.g.XY02          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -647,4 +667,5 @@
   10075 GPC1.g.XY75          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10076 GPC1.g.XY76          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # r 
   10101 GPC1.r.XY01          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10102 GPC1.r.XY02          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -707,4 +728,5 @@
   10175 GPC1.r.XY75          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10176 GPC1.r.XY76          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # i 
   10201 GPC1.i.XY01          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10202 GPC1.i.XY02          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -767,4 +789,5 @@
   10275 GPC1.i.XY75          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10276 GPC1.i.XY76          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # z 
   10301 GPC1.z.XY01          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10302 GPC1.z.XY02          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -827,4 +850,5 @@
   10375 GPC1.z.XY75          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10376 GPC1.z.XY76          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # y 
   10401 GPC1.y.XY01          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10402 GPC1.y.XY02          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -887,4 +911,5 @@
   10475 GPC1.y.XY75          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10476 GPC1.y.XY76          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # w 
   10501 GPC1.w.XY01          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10502 GPC1.w.XY02          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -947,4 +972,5 @@
   10575 GPC1.w.XY75          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   10576 GPC1.w.XY76          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # SkyChip  
   11000 GPC1.g.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   11100 GPC1.r.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -953,4 +979,5 @@
   11400 GPC1.y.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   11500 GPC1.w.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     9   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+ # ForcedWarp  
   12000 GPC1.g.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   12100 GPC1.r.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -959,4 +986,5 @@
   12400 GPC1.y.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   12500 GPC1.w.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     9   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+# MOSAIC BVRI 
   160   MOSAIC2.B.00         dep  25.107  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
   161   MOSAIC2.B.01         dep  25.120  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
@@ -999,5 +1027,5 @@
   566   MOSAIC2.I.06         dep  24.887  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
   567   MOSAIC2.I.07         dep  24.912  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000
-
+# HSC grizyNB 
   20000 HSC.g.00             dep  26.783 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   20001 HSC.g.01             dep  26.783 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -1784,5 +1812,6 @@
   26110 HSC.NB816.110            dep  25.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   26111 HSC.NB816.111            dep  25.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
+# GPC2 grizyw 
+ # g 
   30000 GPC2.g.XY00          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30001 GPC2.g.XY01          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -1849,4 +1878,5 @@
   30076 GPC2.g.XY76          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30077 GPC2.g.XY77          dep  24.563 -0.147 0.000     -     - 0.0000     0     1   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # r 
   30100 GPC2.r.XY00          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30101 GPC2.r.XY01          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -1913,4 +1943,5 @@
   30176 GPC2.r.XY76          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30177 GPC2.r.XY77          dep  24.750 -0.085 0.000     -     - 0.0000     0     2   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # i 
   30200 GPC2.i.XY00          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30201 GPC2.i.XY01          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -1977,4 +2008,5 @@
   30276 GPC2.i.XY76          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30277 GPC2.i.XY77          dep  24.611 -0.044 0.000     -     - 0.0000     0     3   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # z 
   30300 GPC2.z.XY00          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30301 GPC2.z.XY01          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -2041,4 +2073,5 @@
   30376 GPC2.z.XY76          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30377 GPC2.z.XY77          dep  24.250 -0.033 0.000     -     - 0.0000     0     4   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # y 
   30400 GPC2.y.XY00          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30401 GPC2.y.XY01          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -2105,4 +2138,5 @@
   30476 GPC2.y.XY76          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30477 GPC2.y.XY77          dep  23.320 -0.073 0.000     -     - 0.0000     0     5   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # w 
   30500 GPC2.w.XY00          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30501 GPC2.w.XY01          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
@@ -2169,4 +2203,5 @@
   30576 GPC2.w.XY76          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
   30577 GPC2.w.XY77          dep  26.060  0.000 0.000     -     - 0.0000     0     9   0.015 0.000 1.000  0.000   0x0000 0x0000  0xe0440130 0x1003bc88
+ # SkyChip 
   31000 GPC2.g.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   31100 GPC2.r.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -2175,4 +2210,5 @@
   31400 GPC2.y.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   31500 GPC2.w.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     9   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+ # ForcedWarp 
   32000 GPC2.g.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   32100 GPC2.r.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
@@ -2181,3 +2217,2 @@
   32400 GPC2.y.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
   32500 GPC2.w.ForcedWarp    dep  25.000  0.000 0.000     -     - 0.0000     0     9   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
-
