Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/Makefile
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/Makefile	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/Makefile	(revision 37744)
@@ -112,4 +112,6 @@
 $(SRC)/setastrom.$(ARCH).o	    \
 $(SRC)/initialize_setastrom.$(ARCH).o \
+$(SRC)/dcr_correction.$(ARCH).o \
+$(SRC)/kh_correction.$(ARCH).o \
 $(SRC)/astrom_correction.$(ARCH).o \
 $(SRC)/update_dvo_setastrom.$(ARCH).o \
@@ -125,4 +127,6 @@
 $(SRC)/update_dvo_setastrom.$(ARCH).o \
 $(SRC)/update_catalog_setastrom.$(ARCH).o \
+$(SRC)/dcr_correction.$(ARCH).o \
+$(SRC)/kh_correction.$(ARCH).o \
 $(SRC)/astrom_correction.$(ARCH).o \
 $(SRC)/initialize_setastrom.$(ARCH).o
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/include/setastrom.h
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/include/setastrom.h	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/include/setastrom.h	(revision 37744)
@@ -25,4 +25,10 @@
 char        *SINGLE_CPT;
 
+char        *KH_FILE;
+char        *DCR_FILE;
+
+int          KH_RESET;
+int          DCR_RESET;
+
 SkyRegion    UserPatch;
 
@@ -46,11 +52,15 @@
 // int           SetSignals                      PROTO((void));
 
-int           update_dvo_setastrom            PROTO((char *corrfile));
-int           update_dvo_setastrom_parallel   PROTO((SkyTable *sky, char *corrfile));
+int           update_dvo_setastrom            PROTO(());
+int           update_dvo_setastrom_parallel   PROTO((SkyTable *sky));
 
 int           update_catalog_setastrom        PROTO((Catalog *catalog));
 
 // spline correction functions
-int load_astrom_correction (char *filename);
-int get_astrom_correction (int sub, int chip, double *dX, double *dY, float Minst);
 double spline_apply_dbl (double *x, double *y, double *y2, int N, double X);
+
+int load_kh_correction (char *filename);
+int get_kh_correction (int sub, int chip, double *dX, double *dY, float Minst);
+
+int load_dcr_correction (char *filename);
+int get_dcr_correction (int filter, double *dX, double *dY, float Color);
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/astrom_correction.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/astrom_correction.c	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/astrom_correction.c	(revision 37744)
@@ -2,146 +2,6 @@
 
 // astrometry correction functions:
-// load the correction set from a file
-
-// the correction is saved as a set of splines, one per file extension
-// extensions have names of the form XYnn.Dx.Td
-
-// where nn = 00 - 77, the chip number 
-// x is X or Y, the correction direction (dir)
-// d is 0 or 1, pre or post camera correction (sub)
-
-// these are stored in an N-D array of (Spline *) splineset[sub][dir][chip]
-
-# define N_SUB 2
-# define N_DIR 2
-# define N_CHIP 78
-// note that we are allocating pointers for XY00 - XY77, but only the octal elements are set (and not the 00,07,70,77 ones)
-
-Spline ****splineset;
-
-int load_astrom_correction (char *filename) {
-
-  int Ncol;
-  off_t Nrow;
-
-  char type[16];
-  char extname[80];
-
-  Header header;
-  Header theader;
-  FTable ftable;
-
-  /* open file for input */
-  FILE *f = fopen (filename, "r");
-  if (f == (FILE *) NULL) {
-    gprint (GP_ERR, "can't open file for read : %s\n", filename);
-    return FALSE;
-  }
-
-  // read the PHU header
-  if (!gfits_load_header (f, &header)) return (FALSE);
-  
-  int Nbytes = gfits_data_size (&header);
-  fseeko (f, Nbytes, SEEK_CUR);
-  // XXX gfits_free_header (&header);
-
-  ftable.header = &theader;
-
-  int sub, dir, chip;
-
-  // we have a N-D array of splines:
-  // splineset[sub][dir][chip]
-
-  ALLOCATE (splineset, Spline ***, N_SUB);
-  for (sub = 0; sub < N_SUB; sub++) {
-    ALLOCATE (splineset[sub], Spline **, N_DIR);
-    for (dir = 0; dir < N_DIR; dir++) {
-      ALLOCATE (splineset[sub][dir], Spline *, N_CHIP);
-      for (chip = 0; chip < N_CHIP; chip ++) {
-	splineset[sub][dir][chip] = NULL;
-      }
-    }
-  }
-
-  while (TRUE) {
-    // load data for this header : if not found, assume we hit the end of the file
-    if (!gfits_load_header (f, &theader)) break;
-    
-    if (!gfits_fread_ftable_data (f, &ftable, FALSE)) return (FALSE);
-    
-    if (!gfits_scan (&theader, "EXTNAME", "%s", 1, extname)) return (FALSE);
-
-    // figure out the chip ID, diretion, and sequence from the EXTNAME
-
-    // EXTNAME is of the form XY%d%d.D%s.T%s where 
-    char chipname[3];
-    chipname[0] = extname[2];
-    chipname[1] = extname[3];
-    chipname[2] = 0;
-    int chip = atoi(chipname);
-    myAssert (chip >   0, "error in extname %s not(chip >   0)\n", extname);
-    myAssert (chip <  77, "error in extname %s not(chip <  77)\n", extname);
-    myAssert (chip !=  7, "error in extname %s not(chip !=  7)\n", extname);
-    myAssert (chip != 70, "error in extname %s not(chip != 70)\n", extname);
-
-    int dir = -1;
-    if (extname[6] == 'X') {
-      dir = 0;
-    }
-    if (extname[6] == 'Y') {
-      dir = 1;
-    }
-    myAssert (dir != -1, "error in extname %s (dir)\n", extname);
-
-    int sub = -1;
-    if (extname[9] == '0') {
-      sub = 0;
-    }
-    if (extname[9] == '1') {
-      sub = 1;
-    }
-    myAssert (sub != -1, "error in extname %s (sub)\n", extname);
-
-    // need to create and assign to flat-field correction
-    double *xk = gfits_get_bintable_column_data (&theader, &ftable, "X_KNOT", type, &Nrow, &Ncol);
-    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
-    
-    // need to create and assign to flat-field correction
-    double *yk = gfits_get_bintable_column_data (&theader, &ftable, "Y_KNOT", type, &Nrow, &Ncol);
-    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
-    
-    // need to create and assign to flat-field correction
-    double *y2 = gfits_get_bintable_column_data (&theader, &ftable, "DY2_DX", type, &Nrow, &Ncol);
-    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
-    
-    ALLOCATE (splineset[sub][dir][chip], Spline, 1);
-    splineset[sub][dir][chip]->xk = xk;
-    splineset[sub][dir][chip]->yk = yk;
-    splineset[sub][dir][chip]->y2 = y2;
-    splineset[sub][dir][chip]->Nknots = Nrow;
-  }
-  return (TRUE);
-}
-
-int get_astrom_correction (int sub, int chip, double *dX, double *dY, float Minst) {
-
-  Spline *spline = NULL;
-
-  *dX = 0.0;
-  *dY = 0.0;
-
-  // XXX need to include some limits on Minst?
-
-  spline = splineset[sub][0][chip];
-  if (spline) {
-    *dX = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Minst);
-  }
-
-  spline = splineset[sub][1][chip];
-  if (spline) {
-    *dY = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Minst);
-  }
-  return (TRUE);
-}
+// KH correction
+// DCR correction
 
 /* evaluate spline for x, y, y2 at X */
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/dcr_correction.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/dcr_correction.c	(revision 37744)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/dcr_correction.c	(revision 37744)
@@ -0,0 +1,138 @@
+# include "setastrom.h"
+
+// DCR correction functions:
+// load the correction set from a file
+
+// the correction is saved as a set of splines, one per filter and direction
+// extensions have names of the form [grizy].[xy].v0
+
+// [grizy] is the filter
+// [xy] is the correction direction (dir)
+
+// these are stored in an N-D array of (Spline *) splineset[dir][filter]
+
+# define N_DIR 2
+# define N_FILTER 5
+
+static Spline ***splineset;
+
+static float MaxDCR[] = {+0.030, +0.005, +0.005, +0.010, +0.015};
+static float MinDCR[] = {-0.030, -0.005, -0.010, -0.015, -0.010};
+
+int load_dcr_correction (char *filename) {
+
+  int Ncol;
+  off_t Nrow;
+
+  char type[16];
+  char extname[80];
+
+  Header header;
+  Header theader;
+  FTable ftable;
+
+  /* open file for input */
+  FILE *f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for read : %s\n", filename);
+    return FALSE;
+  }
+
+  // read the PHU header
+  if (!gfits_load_header (f, &header)) return (FALSE);
+  
+  // skip the PHU matrix
+  int Nbytes = gfits_data_size (&header);
+  fseeko (f, Nbytes, SEEK_CUR);
+
+  ftable.header = &theader;
+
+  int dir, filter;
+
+  // we have a N-D array of splines:
+  // splineset[dir][filter]
+
+  ALLOCATE (splineset, Spline **, N_DIR);
+  for (dir = 0; dir < N_DIR; dir++) {
+    ALLOCATE (splineset[dir], Spline *, N_FILTER);
+    for (filter = 0; filter < N_FILTER; filter ++) {
+      splineset[dir][filter] = NULL;
+    }
+  }
+
+  while (TRUE) {
+    // load data for this header : if not found, assume we hit the end of the file
+    if (!gfits_load_header (f, &theader)) break;
+    
+    if (!gfits_fread_ftable_data (f, &ftable, FALSE)) return (FALSE);
+    
+    if (!gfits_scan (&theader, "EXTNAME", "%s", 1, extname)) return (FALSE);
+
+    // figure out the filter and direction from the EXTNAME
+
+    // EXTNAME is of the form [grizy].[xy].v0 where 
+    char filtname = extname[0];
+    int filtnum = -1;
+    switch (filtname) {
+      case 'g':	filtnum = 0; break;
+      case 'r':	filtnum = 1; break;
+      case 'i':	filtnum = 2; break;
+      case 'z':	filtnum = 3; break;
+      case 'y':	filtnum = 4; break;
+      default:
+	fprintf (stderr, "invalid filter in ext: %s\n", extname);
+	abort();
+    }
+
+    int dir = -1;
+    if (extname[2] == 'x') {
+      dir = 0;
+    }
+    if (extname[2] == 'y') {
+      dir = 1;
+    }
+    myAssert (dir != -1, "error in extname %s (dir)\n", extname);
+
+    // load the spline elements:
+    double *xk = gfits_get_bintable_column_data (&theader, &ftable, "X_KNOT", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
+    
+    double *yk = gfits_get_bintable_column_data (&theader, &ftable, "Y_KNOT", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading Y_KNOT");
+    
+    double *y2 = gfits_get_bintable_column_data (&theader, &ftable, "DY2_DX", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading DY2_DX");
+    
+    ALLOCATE (splineset[dir][filtnum], Spline, 1);
+    splineset[dir][filtnum]->xk = xk;
+    splineset[dir][filtnum]->yk = yk;
+    splineset[dir][filtnum]->y2 = y2;
+    splineset[dir][filtnum]->Nknots = Nrow;
+  }
+  return (TRUE);
+}
+
+int get_dcr_correction (int filter, double *dX, double *dY, float Color) {
+
+  Spline *spline = NULL;
+
+  *dX = 0.0;
+  *dY = 0.0;
+
+  // XXX need to include some limits on Minst?
+
+  if (0) {
+    spline = splineset[0][filter];
+    if (spline) {
+      *dX = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Color);
+    }
+  }
+
+  spline = splineset[1][filter];
+  if (spline) {
+    *dY = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Color);
+    *dY = MIN(MaxDCR[filter], *dY);
+    *dY = MAX(MinDCR[filter], *dY);
+  }
+  return (TRUE);
+}
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/initialize_setastrom.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/initialize_setastrom.c	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/initialize_setastrom.c	(revision 37744)
@@ -2,6 +2,8 @@
 
 void usage_setastrom () {
-    fprintf (stderr, "USAGE: setastrom (corrections) [options]\n");
+    fprintf (stderr, "USAGE: setastrom [options]\n");
     fprintf (stderr, "  options:\n");
+    fprintf (stderr, "    -DCR (file) : supply DCR correction splines\n");
+    fprintf (stderr, "    -KH (file) : supply Koppenhoefer correction splines\n");
     fprintf (stderr, "    -v : verbose mode\n");
     fprintf (stderr, "    -region Rmin Rmax Dmin Dmax\n");
@@ -49,4 +51,30 @@
   int N;
 
+  KH_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-KH"))) {
+    remove_argument (N, &argc, argv);
+    char *tmpfile = strcreate (argv[N]);
+    KH_FILE = abspath (tmpfile, DVO_MAX_PATH);
+    remove_argument (N, &argc, argv);
+  }
+  KH_RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-KH-reset"))) {
+    remove_argument (N, &argc, argv);
+    KH_RESET = TRUE;
+  }
+
+  DCR_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-DCR"))) {
+    remove_argument (N, &argc, argv);
+    char *tmpfile = strcreate (argv[N]);
+    DCR_FILE = abspath (tmpfile, DVO_MAX_PATH);
+    remove_argument (N, &argc, argv);
+  }
+  DCR_RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-DCR-reset"))) {
+    remove_argument (N, &argc, argv);
+    DCR_RESET = TRUE;
+  }
+
   SINGLE_CPT = NULL;
   if ((N = get_argument (argc, argv, "-cpt"))) {
@@ -118,5 +146,10 @@
   }
 
-  if (argc != 2) usage_setastrom();
+  if (!KH_FILE && !DCR_FILE) {
+    fprintf (stderr, "at least one of -KH and -DCR must be supplied\n");
+    exit (1);
+  }
+
+  if (argc != 1) usage_setastrom();
 
   return (TRUE);
@@ -124,6 +157,8 @@
 
 void usage_setastrom_client () {
-  fprintf (stderr, "USAGE: setastrom_client (correction) -hostID (hostID) -catdir (catdir) -hostdir (hostdir) [options]\n");
+  fprintf (stderr, "USAGE: setastrom_client -hostID (hostID) -catdir (catdir) -hostdir (hostdir) [options]\n");
   fprintf (stderr, "  options:\n");
+  fprintf (stderr, "    -DCR (file) : supply DCR correction splines\n");
+  fprintf (stderr, "    -KH (file) : supply Koppenhoefer correction splines\n");
   fprintf (stderr, "    -region Rmin Rmax Dmin Dmax\n");
   fprintf (stderr, "    -update-catformat (format) : change database schema on output\n");
@@ -163,4 +198,28 @@
 
   int N;
+
+  KH_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-KH"))) {
+    remove_argument (N, &argc, argv);
+    KH_FILE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  KH_RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-KH-reset"))) {
+    remove_argument (N, &argc, argv);
+    KH_RESET = TRUE;
+  }
+
+  DCR_FILE = NULL;
+  if ((N = get_argument (argc, argv, "-DCR"))) {
+    remove_argument (N, &argc, argv);
+    DCR_FILE = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  DCR_RESET = FALSE;
+  if ((N = get_argument (argc, argv, "-DCR-reset"))) {
+    remove_argument (N, &argc, argv);
+    DCR_RESET = TRUE;
+  }
 
   /* specify portion of the sky : allow default of all sky? */
@@ -223,5 +282,10 @@
   if (!CATDIR) usage_setastrom_client();
 
-  if (argc != 2) usage_setastrom_client();
+  if (!KH_FILE && !DCR_FILE) {
+    fprintf (stderr, "at least one of -KH and -DCR must be supplied\n");
+    exit (1);
+  }
+
+  if (argc != 1) usage_setastrom_client();
   return (TRUE);
 }
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/kh_correction.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/kh_correction.c	(revision 37744)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/kh_correction.c	(revision 37744)
@@ -0,0 +1,146 @@
+# include "setastrom.h"
+
+// Koppenhoefer astrometry correction functions:
+// load the correction set from a file
+
+// the correction is saved as a set of splines, one per file extension
+// extensions have names of the form XYnn.Dx.Td
+
+// where nn = 00 - 77, the chip number 
+// x is X or Y, the correction direction (dir)
+// d is 0 or 1, pre or post camera correction (sub)
+
+// these are stored in an N-D array of (Spline *) splineset[sub][dir][chip]
+
+# define N_SUB 2
+# define N_DIR 2
+# define N_CHIP 78
+// note that we are allocating pointers for XY00 - XY77, but only the octal elements are set (and not the 00,07,70,77 ones)
+
+static Spline ****splineset;
+
+int load_kh_correction (char *filename) {
+
+  int Ncol;
+  off_t Nrow;
+
+  char type[16];
+  char extname[80];
+
+  Header header;
+  Header theader;
+  FTable ftable;
+
+  /* open file for input */
+  FILE *f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for read : %s\n", filename);
+    return FALSE;
+  }
+
+  // read the PHU header
+  if (!gfits_load_header (f, &header)) return (FALSE);
+  
+  int Nbytes = gfits_data_size (&header);
+  fseeko (f, Nbytes, SEEK_CUR);
+  // XXX gfits_free_header (&header);
+
+  ftable.header = &theader;
+
+  int sub, dir, chip;
+
+  // we have a N-D array of splines:
+  // splineset[sub][dir][chip]
+
+  ALLOCATE (splineset, Spline ***, N_SUB);
+  for (sub = 0; sub < N_SUB; sub++) {
+    ALLOCATE (splineset[sub], Spline **, N_DIR);
+    for (dir = 0; dir < N_DIR; dir++) {
+      ALLOCATE (splineset[sub][dir], Spline *, N_CHIP);
+      for (chip = 0; chip < N_CHIP; chip ++) {
+	splineset[sub][dir][chip] = NULL;
+      }
+    }
+  }
+
+  while (TRUE) {
+    // load data for this header : if not found, assume we hit the end of the file
+    if (!gfits_load_header (f, &theader)) break;
+    
+    if (!gfits_fread_ftable_data (f, &ftable, FALSE)) return (FALSE);
+    
+    if (!gfits_scan (&theader, "EXTNAME", "%s", 1, extname)) return (FALSE);
+
+    // figure out the chip ID, diretion, and sequence from the EXTNAME
+
+    // EXTNAME is of the form XY%d%d.D%s.T%s where 
+    char chipname[3];
+    chipname[0] = extname[2];
+    chipname[1] = extname[3];
+    chipname[2] = 0;
+    int chip = atoi(chipname);
+    myAssert (chip >   0, "error in extname %s not(chip >   0)\n", extname);
+    myAssert (chip <  77, "error in extname %s not(chip <  77)\n", extname);
+    myAssert (chip !=  7, "error in extname %s not(chip !=  7)\n", extname);
+    myAssert (chip != 70, "error in extname %s not(chip != 70)\n", extname);
+
+    int dir = -1;
+    if (extname[6] == 'X') {
+      dir = 0;
+    }
+    if (extname[6] == 'Y') {
+      dir = 1;
+    }
+    myAssert (dir != -1, "error in extname %s (dir)\n", extname);
+
+    int sub = -1;
+    if (extname[9] == '0') {
+      sub = 0;
+    }
+    if (extname[9] == '1') {
+      sub = 1;
+    }
+    myAssert (sub != -1, "error in extname %s (sub)\n", extname);
+
+    // need to create and assign to flat-field correction
+    double *xk = gfits_get_bintable_column_data (&theader, &ftable, "X_KNOT", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
+    
+    // need to create and assign to flat-field correction
+    double *yk = gfits_get_bintable_column_data (&theader, &ftable, "Y_KNOT", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
+    
+    // need to create and assign to flat-field correction
+    double *y2 = gfits_get_bintable_column_data (&theader, &ftable, "DY2_DX", type, &Nrow, &Ncol);
+    myAssert (!strcmp(type, "double"), "error reading X_KNOT");
+    
+    ALLOCATE (splineset[sub][dir][chip], Spline, 1);
+    splineset[sub][dir][chip]->xk = xk;
+    splineset[sub][dir][chip]->yk = yk;
+    splineset[sub][dir][chip]->y2 = y2;
+    splineset[sub][dir][chip]->Nknots = Nrow;
+  }
+  return (TRUE);
+}
+
+int get_kh_correction (int sub, int chip, double *dX, double *dY, float Minst) {
+
+  Spline *spline = NULL;
+
+  *dX = 0.0;
+  *dY = 0.0;
+
+  // XXX need to include some limits on Minst?
+
+  spline = splineset[sub][0][chip];
+  if (spline) {
+    *dX = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Minst);
+  }
+
+  spline = splineset[sub][1][chip];
+  if (spline) {
+    *dY = spline_apply_dbl (spline->xk, spline->yk, spline->y2, spline->Nknots, Minst);
+  }
+  return (TRUE);
+}
+
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom.c	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom.c	(revision 37744)
@@ -1,3 +1,5 @@
 # include "setastrom.h"
+
+// this code does both DCR and KH corrections, if both are specified.  
 
 // XXX this function is dependent in two ways on the specific numerical values of the photcodes
@@ -7,9 +9,20 @@
 int update_catalog_setastrom (Catalog *catalog) {
 
-  off_t i, found;
+  off_t i;
 
   time_t timeRef = ohana_date_to_sec("2011/05/11,00:00:00");
 
-  found = 0;
+  int Nsecfilt = GetPhotcodeNsecfilt ();
+  PhotCode *code_g = GetPhotcodebyName ("g");
+  PhotCode *code_i = GetPhotcodebyName ("i");
+  PhotCode *code_z = GetPhotcodebyName ("z");
+  PhotCode *code_y = GetPhotcodebyName ("y");
+
+  int Nsec_g = GetPhotcodeNsec(code_g->code);
+  int Nsec_i = GetPhotcodeNsec(code_i->code);
+  int Nsec_z = GetPhotcodeNsec(code_z->code);
+  int Nsec_y = GetPhotcodeNsec(code_y->code);
+
+  off_t found = 0;
 
   for (i = 0; i < catalog[0].Nmeasure; i++) {
@@ -17,9 +30,15 @@
     Measure *measure = &catalog[0].measure[i];
 
-    // XXX only do GPC1 data for now
+    // only do GPC1 data for now (true for both DCR and KH)
     if (measure[0].photcode < 10000) continue;
     if (measure[0].photcode > 10600) continue;
       
-    // XXX hardwire the list of chips to correct
+    int averef = measure->averef;
+    Average *average = &catalog[0].average[averef];
+    SecFilt *secfilt = &catalog[0].secfilt[averef*Nsecfilt];
+
+    int chipID = measure[0].photcode % 100;
+
+    // XXX hardwire the list of chips to KH correct
     // some notes:
     // XY24 surprisingly does not need correction
@@ -28,5 +47,5 @@
     // XY67 has poor astrometry with a weird correction for all mags
 
-    int chipID = measure[0].photcode % 100;
+    int doKH = FALSE;
     switch (chipID) {
       case 4:
@@ -57,55 +76,187 @@
       case 72:
       case 73:
+	doKH = TRUE;
 	break;
       default:
-	measure[0].Xfix = measure[0].Xccd;
-	measure[0].Yfix = measure[0].Yccd;
-	continue;
-    }
-
-    double Xccd = measure[0].Xccd;
-    double Yccd = measure[0].Yccd;
+	break;
+    }
+
     double pltScale = fabs(measure[0].pltscale);
-
-    // this is not needed, is it?
-    // double posAngle = FromShortDegrees(measure[0].posangle);
-
-    // I need to know which chip we have.  use photcode assuming range
-    int chip = measure[0].photcode % 100;
-
-    // correction may be Minst or Minst + 5.0*log(fwhm
-    float fwhm_maj = FromShortPixels(measure[0].FWx);
-    float Minst = PhotInst (measure, MAG_CLASS_PSF);
-    float MinstSB = Minst + 5.0*log10(fwhm_maj);
-
-    // I should validate that the spline I'm using is SB corrected not Minst...
-
-    // XXX this is very ad hoc.  the splines are measured based on an extraction from dvo
-    // the dvo values of fwhm_maj are sigmas (NOT FWHM!) in pixels.  
+    double posAngle = FromShortDegrees(measure[0].posangle);
+
+    /**** KH section ****/
+
+    double dX_KH = 0.0;
+    double dY_KH = 0.0;
+    if (KH_FILE && doKH && (measure[0].t < timeRef)) {
+
+      // correction may be Minst or Minst + 5.0*log(fwhm
+      float fwhm_maj = FromShortPixels(measure[0].FWx);
+      float Minst = PhotInst (measure, MAG_CLASS_PSF);
+      // float MinstSB = Minst + 5.0*log10(fwhm_maj);
+
+      // NOTE: the spline correction was measured on the nightly-science database using measurements
+      // from the period in which fwhm_maj & fwhm_min were a factor of sqrt(2) too small.  I am making 
+      // this correction here (but ideally I would re-measure the KH trend from PV2 data and re-generate 
+      // the splines
+      float MinstSB = Minst + 5.0*log10(M_SQRT2*fwhm_maj);
+
+      // I should validate that the spline I'm using is SB corrected not Minst..
+      // NOTE: it is SB, not raw Minst
+
+      // XXX this is very ad hoc.  the splines are measured based on an extraction from dvo
+      // the dvo values of fwhm_maj are sigmas (NOT FWHM!) in pixels.  
     
-    // saturate fwhm_maj in the range 1.0 < fwhm_maj < 5.0
-    // (images with really odd psf probably have poor astrometry anyway...)
-    fwhm_maj = MIN(fwhm_maj,5.0);
-    fwhm_maj = MAX(fwhm_maj,1.0);
-
-    double dX;
-    double dY;
-
-    // correction is in arcseconds
-    if (measure[0].t < timeRef) {
-      get_astrom_correction (0, chip, &dX, &dY, MinstSB);
-    } else {
-      // XXX do not correct new data
-      // get_astrom_correction (1, chip, &dX, &dY, MinstSB);
-      dX = 0.0;
-      dY = 0.0;
-    }
+      // saturate fwhm_maj in the range 1.0 < fwhm_maj < 5.0
+      // (images with really odd psf probably have poor astrometry anyway...)
+      fwhm_maj = MIN(fwhm_maj,5.0);
+      fwhm_maj = MAX(fwhm_maj,1.0);
+
+      // correction is in arcseconds
+      get_kh_correction (0, chipID, &dX_KH, &dY_KH, MinstSB);
+    }
+
+    /**** DCR section ****/
+
+    double dX_DCR = 0.0;
+    double dY_DCR = 0.0;
+    if (DCR_FILE) {
+
+      // check if the color*airmass term is not-nan (Skip otherwise)
+
+      // XXX hard-wire gpc1 photcodes for now:
+      // I should use photcode ops: PhotCode *code = PhotCodeByCode(measure->photcode)
+      // int equiv = code->equiv
+      // Nsec = GetNsec(equiv)
+
+      float dColor = 0.0;
+      int filtCode = (int)(measure->photcode / 100); // eg, 101 = r
+      switch (filtCode) {
+	case 100:
+	case 101:
+	case 102: {
+	  float gmag = secfilt[Nsec_g].M;
+	  float imag = secfilt[Nsec_i].M;
+	  dColor = average->refColorBlue - (gmag - imag);
+	  break;
+	}
+	case 103:
+	case 104: {
+	  float zmag = secfilt[Nsec_z].M;
+	  float ymag = secfilt[Nsec_y].M;
+	  dColor = average->refColorRed - (zmag - ymag);
+	  break;
+	}
+      }
+      int filtSeq = filtCode % 100;
+      if (!isfinite(dColor)) goto skip_DCR;
+
+      // I need to get the parallactic angle, but I only have alt & az, s:o 
+    
+      // airmass = 1.0 / cos(90 - alt)
+      // cos(90 - alt) = cos(zd) = sin(alt) = 1.0 / airmass
+      // airmass = 1.0 / sin(alt)
+
+      // airmass = 1.0 / cos(zd)
+      // cos(zd) = 1.0 / airmass == sn_alt
+      // sin(zd) = sqrt(1.0 - cos(zd)^2) == cs_alt
+      // tan(zd) = sin(zd) / cos(zd) = cs_alt / sn_alt
+
+      double sn_alt = 1.0 / measure->airmass;
+      double cs_alt = sqrt(1.0 - SQ(sn_alt));
+      double tan_zd = cs_alt / sn_alt;
+
+      double cs_az  = cos(RAD_DEG*measure->az);
+      double sn_az  = sin(RAD_DEG*measure->az);
+
+# define LATITUDE (20.7070999146*RAD_DEG)
+      double sn_lat = sin(LATITUDE);
+      double cs_lat = cos(LATITUDE);
+
+      // double sind = sn_alt * sn_lat + cs_alt * cs_az * cs_lat;
+      // *dec  = DEG_RAD * asin (sind);
+
+      // WARNING: these are NOT sin(ha),cos(ha) but are scaled
+      // double ha = DEG_RAD * atan2 (sinh, cosh);
+      double sinh = -cs_alt * sn_az;
+      double cosh =  sn_alt * cs_lat - cs_alt * cs_az * sn_lat;
+      double r_ha = hypot(sinh, cosh);
+      double sn_ha = sinh / r_ha;
+      double cs_ha = cosh / r_ha;
+
+      sinh = -cs_az * sn_alt * sn_ha * sn_lat + sn_az * sn_alt * cs_ha - sn_ha * cs_alt * cs_lat;
+      cosh = -sn_az          * sn_ha * sn_lat - cs_az          * cs_ha;
+      double r_rot = hypot(sinh, cosh);
+
+      double sn_rot = -sinh / r_rot;
+      double cs_rot = +cosh / r_rot;
+
+      double dPx = 0.0; 
+      double dPy = 0.0;
+
+      float colorOffset = dColor * tan_zd;
+      get_dcr_correction (filtSeq, &dPx, &dPy, colorOffset);
+
+      // if we had x-terms, we would have:
+      // double dPx = dRx *cs_rot + dD_s*sn_rot;
+      // double dPy = dD_s*cs_rot - dRx *sn_rot;
+      // double dR = dPx*cs_rot - dPy*sn_rot;
+      // double dD = dPx*sn_rot + dPy*cs_rot;
+
+      // since dPx = 0.0, instead we have: dPx, dPy as measured in dvo.dcr.sh
+      double dR = -dPy*sn_rot;
+      double dD = +dPy*cs_rot;
+
+      // dR = (measure->R - average->R) * 3600 * dcos(dec) [ie, in the projected plane]
+      // dD = (measure->D - average->D) * 3600
+      // average->D = measure->D - dD/3600
+
+      // now we need to rotate dR,dD into the chip frame and correction Xccd,Yccd
+
+      // are the plate-scale and posangle correctly set in the db?
+
+      // XXX the koppenhoefer code implies pos if angle from y -> D, (ie negative of the
+      // below).  if so, apply sin(pos) -> -sin(pos)
+
+      // if parity = sky:
+      // double dR =  dX*cos(pos) + dY*sin(pos);
+      // double dD = -dX*sin(pos) + dY*cos(pos);
+
+      // XXX signs set here to reflect pos = -pos
+      float posAngRad = posAngle * RAD_DEG;
+      dX_DCR =  dR*cos(posAngRad) + dD*sin(posAngRad);
+      dY_DCR = -dR*sin(posAngRad) + dD*cos(posAngRad);
+    }
+
+  skip_DCR:
 
     // do not modify the original Xccd,Yccd values
-    measure[0].XoffKH = Xccd + dX / pltScale;
-    measure[0].YoffKH = Yccd + dY / pltScale;
-    measure[0].Xfix   = measure[0].XoffKH;
-    measure[0].Yfix   = measure[0].YoffKH;
-
+    if (KH_FILE) {
+      measure[0].XoffKH = dX_KH / pltScale;
+      measure[0].YoffKH = dY_KH / pltScale;
+    }
+    if (KH_RESET) {
+      measure[0].XoffKH = 0.0;
+      measure[0].YoffKH = 0.0;
+    }
+    if (DCR_FILE) {
+      measure[0].XoffDCR = -dX_DCR / pltScale;
+      measure[0].YoffDCR = -dY_DCR / pltScale;
+    }
+    if (DCR_RESET) {
+      measure[0].XoffDCR = -dX_DCR / pltScale;
+      measure[0].YoffDCR = -dY_DCR / pltScale;
+    }
+
+    measure[0].Xfix = measure[0].Xccd;
+    measure[0].Yfix = measure[0].Yccd;
+    if (isfinite(measure[0].XoffKH) && isfinite(measure[0].YoffKH)) {
+      measure[0].Xfix += measure[0].XoffKH;
+      measure[0].Yfix += measure[0].YoffKH;
+    }
+    if (isfinite(measure[0].XoffDCR) && isfinite(measure[0].YoffDCR)) {
+      measure[0].Xfix += measure[0].XoffDCR;
+      measure[0].Yfix += measure[0].YoffDCR;
+    }
     found ++;
   }
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom_dcr.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom_dcr.c	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_catalog_setastrom_dcr.c	(revision 37744)
@@ -1,7 +1,5 @@
 # include "setastrom.h"
 
-// XXX this function is dependent in two ways on the specific numerical values of the photcodes
-// 1) the examined measurements are limited to GPC1 values based on the photcode range 10000 - 10600
-// 2) the chip ID is determined from the numerical value of the photcode (chip = photcode % 100)
+// this DCR code is largely hard-wired for gpc1 data
 
 int update_catalog_setastrom_dcr (Catalog *catalog) {
@@ -24,10 +22,4 @@
     Measure *measure = &catalog[0].measure[i];
 
-    int averef = measure->averef;
-    Average *average = &catalog[0].average[averef];
-    Secfilt *secfilt = &catalog[0].secfilt[averef*Nsecfilt];
-
-    int chipID = measure[0].photcode % 100;
-
     double Xccd = measure[0].Xccd;
     double Yccd = measure[0].Yccd;
@@ -35,10 +27,8 @@
     double posAngle = FromShortDegrees(measure[0].posangle);
 
-    // I need to get the parallactic angle, but I only have alt & az, no 
+    // I need to get the parallactic angle, but I only have alt & az, s:o 
     
     // airmass = 1.0 / cos(90 - alt)
-    // cos(90 - alt) = 1.0 / airmass
-    // cos(90)cos(alt) + sin(90)sin(alt)
-    // 0*cos(alt) + sin(alt)
+    // cos(90 - alt) = cos(zd) = sin(alt) = 1.0 / airmass
     // airmass = 1.0 / sin(alt)
 
@@ -56,4 +46,5 @@
 
 # define LATITUDE (20.7070999146*RAD_DEG)
+
     double sn_ha = -cs_alt * sn_az;
     double cs_ha =  sn_alt * cos(LATITUDE) - cs_alt * cs_az * sin(LATITUDE);
@@ -66,4 +57,8 @@
 
     // XXX hard-wire gpc1 photcodes for now:
+    // I should use photcode ops: PhotCode *code = PhotCodeByCode(measure->photcode)
+    // int equiv = code->equiv
+    // Nsec = GetNsec(equiv)
+
     int filtCode = (int)(measure->photcode / 100); // eg, 101 = r
     switch (filtCode) {
@@ -82,6 +77,8 @@
 	break;
     }
+    int filtSeq = filtCode % 100;
+    float colorOffset = dColor * tan_dz;
 
-    double dPy = dColor * tan_zd * DCRslope;
+    get_dcr_correction (filtSeq, double *dPx, double *dPy, colorOffset);
 
     // if we had x-terms, we would have:
@@ -91,5 +88,5 @@
     // double dD = dPx*sn_rot + dPy*cs_rot;
 
-    // since dPx = 0.0, instead we have:
+    // since dPx = 0.0, instead we have: dPx, dPy as measured in dvo.dcr.sh
     double dR = -dPy*sn_rot;
     double dD = +dPy*cs_rot;
@@ -107,17 +104,26 @@
 
     // if parity = sky:
-    double dD =  dY*cos(pos) - dX*sin(pos);
-    double dR =  dY*sin(pos) + dX*cos(pos);
+    // double dR =  dX*cos(pos) + dY*sin(pos);
+    // double dD = -dX*sin(pos) + dY*cos(pos);
+
     double dX =  dR*cos(pos) - dD*sin(pos);
     double dY =  dR*sin(pos) + dD*cos(pos);
 
     // if parity != sky:
-    double dD =  dY*cos(pos) + dX*sin(pos);
-    double dR =  dY*sin(pos) - dX*cos(pos);
-    double dX = -dR*cos(pos) + dD*sin(pos);
-    double dY =  dR*sin(pos) + dD*cos(pos);
+    // double dD =  dY*cos(pos) + dX*sin(pos);
+    // double dR =  dY*sin(pos) - dX*cos(pos);
+    // double dX = -dR*cos(pos) + dD*sin(pos);
+    // double dY =  dR*sin(pos) + dD*cos(pos);
 
     // do not modify the original Xccd,Yccd values
+
     // why is KH added but DCR subtracted??
+
+    // ANSWER: both KH and DCR trends are measured by extracted dR,dD from the database
+    // (mextract dR dD).  But the KH analysis was done before measure carried R,D when it
+    // instead carried dR,dD.  In the current definition in dbExtractMeasures, dR,dD =
+    // (measure.R,D - average.R,D) * 3600, but in the old format, measure.dR,dD were
+    // defined as (average.R,D - measure.R,D)*3600).
+
     measure[0].XoffDCR = -dX / pltScale;
     measure[0].YoffDCR = -dY / pltScale;
Index: branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_dvo_setastrom.c
===================================================================
--- branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_dvo_setastrom.c	(revision 37743)
+++ branches/eam_branches/ipp-20140904/Ohana/src/uniphot/src/update_dvo_setastrom.c	(revision 37744)
@@ -1,5 +1,5 @@
 # include "setastrom.h"
 
-int update_dvo_setastrom (char *corrfile) {
+int update_dvo_setastrom () {
 
   SkyTable *sky = NULL;
@@ -13,9 +13,10 @@
   
   if (PARALLEL && !HOST_ID) {
-    int status = update_dvo_setastrom_parallel (sky, corrfile);
+    int status = update_dvo_setastrom_parallel (sky);
     return status;
   }
 
-  load_astrom_correction (corrfile);
+  if (KH_FILE)  load_kh_correction (KH_FILE);
+  if (DCR_FILE) load_dcr_correction (DCR_FILE);
 
   // determine the populated SkyRegions overlapping the requested area (default depth)
@@ -78,9 +79,7 @@
 # define DEBUG 1
 
-int update_dvo_setastrom_parallel (SkyTable *sky, char *corrfile) {
+int update_dvo_setastrom_parallel (SkyTable *sky) {
 
   // now launch the setastrom_client jobs to the parallel hosts
-
-  char *abscorrfile = abspath (corrfile, DVO_MAX_PATH);
 
   // load the list of hosts
@@ -96,14 +95,19 @@
 
     char command[1024];
-    snprintf (command, 1024, "setastrom_client %s -hostID %d -catdir %s -hostdir %s -region %f %f %f %f", 
-	      abscorrfile, table->hosts[i].hostID, CATDIR, table->hosts[i].pathname, 
+    snprintf (command, 1024, "setastrom_client -hostID %d -catdir %s -hostdir %s -region %f %f %f %f", 
+	      table->hosts[i].hostID, CATDIR, table->hosts[i].pathname, 
 	      UserPatch.Rmin, UserPatch.Rmax, UserPatch.Dmin, UserPatch.Dmax);
-
-    fprintf (stderr, "command: %s\n", command);
 
     char tmpline[1024];
     if (VERBOSE)       	  { snprintf (tmpline, 1024, "%s -v",                	command); 		    strcpy (command, tmpline); }
     if (UPDATE)        	  { snprintf (tmpline, 1024, "%s -update",           	command); 		    strcpy (command, tmpline); }
+    if (KH_FILE)      	  { snprintf (tmpline, 1024, "%s -KH %s",           	command, KH_FILE);	    strcpy (command, tmpline); }
+    if (DCR_FILE)      	  { snprintf (tmpline, 1024, "%s -DCR %s",           	command, DCR_FILE);	    strcpy (command, tmpline); }
     if (UPDATE_CATFORMAT) { snprintf (tmpline, 1024, "%s -update-catformat %s", command, UPDATE_CATFORMAT); strcpy (command, tmpline); }
+
+    if (KH_RESET)      	  { snprintf (tmpline, 1024, "%s -KH-reset",           	command);	            strcpy (command, tmpline); }
+    if (DCR_RESET)     	  { snprintf (tmpline, 1024, "%s -DCR-reset",           command);	            strcpy (command, tmpline); }
+
+    fprintf (stderr, "command: %s\n", command);
 
     if (PARALLEL_MANUAL) continue;
@@ -126,5 +130,4 @@
     }
   }
-  free (abscorrfile);
 
   // wait for the remote jobs to be completed
