Index: /trunk/Ohana/src/opihi/cmd.astro/cgrid.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.astro/cgrid.c	(revision 34584)
@@ -279,5 +279,5 @@
   graphmode.ptype = 100; /* connect a pair */
   graphmode.etype = 0;
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
 
   free (Xvec.elements.Ptr);
Index: /trunk/Ohana/src/opihi/cmd.astro/cplot.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/cplot.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.astro/cplot.c	(revision 34584)
@@ -3,4 +3,5 @@
 int cplot (int argc, char **argv) {
   
+  double ra_prev;
   int i, kapa, Npts, status, leftside;
   opihi_flt *x, *y, *r, *d, Rmin, Rmax, Rmid;
@@ -41,15 +42,19 @@
   Npts = 0;
   for (i = 0; i < Xvec.Nelements; i++, r++, d++) {
-    *r = ohana_normalize_angle (*r);
-    while (*r < Rmin) *r += 360.0;
-    while (*r > Rmax) *r -= 360.0;
+    double ra = ohana_normalize_angle (*r);
+    while (ra < Rmin) ra += 360.0;
+    while (ra > Rmax) ra -= 360.0;
 
     // for pair-by-pair connections, check on second point if we straddle the back midline
-    if ((graphmode.ptype == 100) && (i % 2)) {
-      leftside = (r[-1] < Rmid); // if first of the pair is left, second must be as well
-      if ( leftside && (r[0] > Rmid + 90)) { r[0] -= 360.0; }
-      if (!leftside && (r[0] < Rmid - 90)) { r[0] += 360.0; }
+    if (graphmode.ptype == 100) {
+      if (i % 2) {
+	leftside = (ra_prev < Rmid); // if first of the pair is left, second must be as well
+	if ( leftside && (ra > Rmid + 90)) { ra -= 360.0; }
+	if (!leftside && (ra < Rmid - 90)) { ra += 360.0; }
+      } else {
+	ra_prev = ra;
+      }
     }
-    status = RD_to_XY (x, y, *r, *d, &graphmode.coords);
+    status = RD_to_XY (x, y, ra, *d, &graphmode.coords);
 
     // if we fail on one of the points, drop the corresponding pair
@@ -78,5 +83,5 @@
 
   graphmode.etype = 0;
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   
   free (Xvec.elements.Ptr);
Index: /trunk/Ohana/src/opihi/cmd.astro/czplot.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.astro/czplot.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.astro/czplot.c	(revision 34584)
@@ -76,5 +76,5 @@
   graphmode.size = -1; /* point size determined by Zvec */
   graphmode.etype = 0;
-  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, &graphmode);
+  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, NULL, &graphmode);
 
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 34584)
@@ -114,6 +114,8 @@
 $(SRC)/shift.$(ARCH).o		\
 $(SRC)/sort.$(ARCH).o		\
-$(SRC)/spline_apply.$(ARCH).o	\
-$(SRC)/spline_construct.$(ARCH).o \
+$(SRC)/spline.$(ARCH).o		\
+$(SRC)/spline_commands.$(ARCH).o \
+$(SRC)/imspline_apply.$(ARCH).o	\
+$(SRC)/imspline_construct.$(ARCH).o \
 $(SRC)/imstats.$(ARCH).o	   \
 $(SRC)/style.$(ARCH).o		   \
Index: /trunk/Ohana/src/opihi/cmd.data/dbconnect.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/dbconnect.c	(revision 34584)
@@ -33,4 +33,6 @@
   password[i] = 0;
 # endif
+
+  // XXX do I need to call mysql_library_init()?
 
   mysql_init (&mysql);
Index: /trunk/Ohana/src/opihi/cmd.data/dbselect.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/dbselect.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/dbselect.c	(revision 34584)
@@ -61,4 +61,5 @@
   ALLOCATE (vec, Vector *, Ncols);
   for (i = 0; i < Ncols; i++) {
+    char *name = fields[i].name ? fields[i].name : fields[i].org_name;
     if ((vec[i] = SelectVector (fields[i].name, ANYVECTOR, TRUE)) == NULL) {
       gprint (GP_ERR, "trouble creating vector named %s\n", fields[i].name);
Index: /trunk/Ohana/src/opihi/cmd.data/grid.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/grid.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/grid.c	(revision 34584)
@@ -178,5 +178,5 @@
   graphmode.ptype = 100; /* connect a pair */
   graphmode.etype = 0;
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
 
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/cmd.data/imspline_apply.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/imspline_apply.c	(revision 34584)
+++ /trunk/Ohana/src/opihi/cmd.data/imspline_apply.c	(revision 34584)
@@ -0,0 +1,98 @@
+# include "data.h"
+
+// need to rename this as an image function
+int imspline_apply (int argc, char **argv) {
+  
+  int i, j, I, J;
+  int nx, ny, Nx, Ny;
+  float rx, ry, x, y;
+  float *Tx1, *Tx2, *Txc, *Ty1, *Ty2, *Tyc, *V, *V1, *V2;
+  Buffer *out, *y1, *y2;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: imspline_apply <Y> <Y2> <out> (x/y) Nx Ny\n");
+    return (FALSE);
+  }
+
+  if ((y1  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((y2  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  // xdir = FALSE;
+  // if (!strcmp (argv[4], "x")) xdir = TRUE; 
+
+  nx = atoi (argv[5]);
+  ny = atoi (argv[6]);
+
+  Nx = y1[0].matrix.Naxis[0];
+  Ny = y1[0].matrix.Naxis[1];
+
+  rx = Nx / (float) nx;
+  ry = Ny / (float) ny;
+
+  /* create an output matrix buffer with desired nx, ny */
+  gfits_free_matrix (&out[0].matrix);
+  gfits_free_header (&out[0].header);
+
+  out[0].bitpix = y1[0].bitpix;
+  out[0].unsign = y1[0].unsign;
+  out[0].bscale = y1[0].bscale;
+  out[0].bzero  = y1[0].bzero;
+  gfits_copy_header (&y1[0].header, &out[0].header);
+  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
+  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
+
+  out[0].header.Naxis[0] = nx;
+  out[0].header.Naxis[1] = ny;
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+  if ((y1[0].file[0] != '*') && (y1[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", y1[0].file);
+  } else {
+    sprintf (out[0].file, "%s", y1[0].file);
+  }
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty1, float, Ny);
+  ALLOCATE (Tyc, float, Ny);
+  for (i = 0; i < Ny; i++) { Tyc[i] = i; }
+
+  ALLOCATE (Tx1, float, Nx);
+  ALLOCATE (Tx2, float, Nx);
+  ALLOCATE (Txc, float, Nx);
+  for (i = 0; i < Nx; i++) { Txc[i] = i; }
+
+  V = (float *)(out[0].matrix.buffer);
+
+  for (J = 0; J < ny; J++) {
+    y = J * ry;
+
+    /* construct spline for each element in this row */
+    for (i = 0; i < Nx; i++) {
+      V1 = (float *)(y1[0].matrix.buffer) + i;
+      V2 = (float *)(y2[0].matrix.buffer) + i;
+      for (j = 0; j < Ny; j++, V1+=Nx, V2+=Nx) {
+	Ty1[j] = *V1;
+	Ty2[j] = *V2;
+      }
+      Tx1[i] = spline_apply_flt (Tyc, Ty1, Ty2, Ny, y);
+    }
+    spline_construct_flt (Txc, Tx1, Nx, Tx2);
+
+    /* apply x-dir spline to new image */
+    for (I = 0; I < nx; I++, V++) {
+      x = I * rx;
+      *V = spline_apply_flt (Txc, Tx1, Tx2, Nx, x);
+    }
+  }
+
+  free (Ty1);
+  free (Ty2);
+  free (Tyc);
+  
+  free (Tx1);
+  free (Tx2);
+  free (Txc);
+  
+  return (TRUE);
+
+}
Index: /trunk/Ohana/src/opihi/cmd.data/imspline_construct.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/imspline_construct.c	(revision 34584)
+++ /trunk/Ohana/src/opihi/cmd.data/imspline_construct.c	(revision 34584)
@@ -0,0 +1,72 @@
+# include "data.h"
+
+// need to rename this as an image function
+int imspline_construct (int argc, char **argv) {
+  
+  int i, j, Nx, Ny;
+  float *Tx, *Ty, *Ty2, *V;
+  Buffer *in, *out;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imspline_construct <in> <out> (x/y)\n");
+    return (FALSE);
+  }
+
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+
+  // XXX move this to gfits_create_matrix
+  free (out[0].matrix.buffer);
+  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
+    sprintf (out[0].file, "*%s", in[0].file);
+  } else {
+    sprintf (out[0].file, "%s", in[0].file);
+  }
+  out[0].bitpix = in[0].bitpix;
+  out[0].unsign = in[0].unsign;
+  out[0].bscale = in[0].bscale;
+  out[0].bzero  = in[0].bzero;
+  gfits_copy_matrix_info (&in[0].matrix, &out[0].matrix);
+  gfits_copy_header (&in[0].header, &out[0].header);
+  gfits_create_matrix (&out[0].header, &out[0].matrix);
+
+  // int xdir = FALSE;
+  // if (!strcmp (argv[3], "x")) xdir = TRUE; 
+  /* ideally, the resulting image should carry this info (in header?) */
+
+  Nx = in[0].matrix.Naxis[0];
+  Ny = in[0].matrix.Naxis[1];
+
+  ALLOCATE (Ty2, float, Ny);
+  ALLOCATE (Ty, float, Ny);
+  ALLOCATE (Tx, float, Ny);
+
+  /** for now only perform the operation for the ydir splines */
+
+  /* construct coordinate vector */
+  for (j = 0; j < Ny; j++) { Tx[j] = j; }
+  
+  for (i = 0; i < Nx; i++) {
+    
+    /* construct temp vector with values to spline */
+    V = (float *)(in[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      Ty[j] = *V;
+    }
+  
+    spline_construct_flt (Tx, Ty, Ny, Ty2);
+  
+    /* copy derivatives to output buffer */
+    V = (float *)(out[0].matrix.buffer) + i;
+    for (j = 0; j < Ny; j++, V+=Nx) {
+      *V = Ty2[j];
+    }
+  }
+
+  free (Tx);
+  free (Ty);
+  free (Ty2);
+  
+  return (TRUE);
+
+}
Index: /trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/init.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/init.c	(revision 34584)
@@ -102,6 +102,7 @@
 int shift            PROTO((int, char **));
 int sort_vectors     PROTO((int, char **));
-int spline_apply_cmd PROTO((int, char **));
-int spline_construct_cmd PROTO((int, char **));
+int spline_command   PROTO((int, char **));
+int imspline_apply   PROTO((int, char **));
+int imspline_construct PROTO((int, char **));
 int stats            PROTO((int, char **));
 int imstats          PROTO((int, char **));
@@ -254,6 +255,7 @@
   {1, "shift",        shift,            "shift data in an image"},
   {1, "sort",         sort_vectors,     "sort list of vectors"},
-  {1, "spline.apply", spline_apply_cmd, "apply spline fit to generate an image"},
-  {1, "spline.const", spline_construct_cmd, "create spline 2nd deriv. terms"},
+  {1, "spline",       spline_command,   "shift data in an image"},
+  {1, "imspline.apply", imspline_apply, "apply spline fit to generate an image"},
+  {1, "imspline.const", imspline_construct, "create spline 2nd deriv. terms"},
   {1, "stats",        imstats,          "statistics on a portion of an image"},
   {1, "style",        style,            "set the style for graph plots"},
Index: /trunk/Ohana/src/opihi/cmd.data/plot.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/plot.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/plot.c	(revision 34584)
@@ -3,5 +3,6 @@
 int plot (int argc, char **argv) {
   
-  int kapa, N, Npts;
+  char *out;
+  int kapa, N, Npts, valid, size, i;
   Graphdata graphmode;
   Vector *xvec, *yvec, *dxmvec, *dxpvec, *dymvec, *dypvec;
@@ -32,7 +33,27 @@
   }
 
-  if (argc != 3) {
+  valid  = (argc == 3);
+  valid |= (argc > 4) && !strcmp (argv[3], "where");
+  if (!valid) {
     gprint (GP_ERR, "USAGE: plot <x> <y> [style]\n");
+    gprint (GP_ERR, "   OR: plot <x> <y> [style] where (condition)\n");
     return (FALSE);
+  }
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 4) {
+    out = dvomath (argc - 4, &argv[4], &size, 1);
+    if (out == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (out, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (out);
+      free (out);
+      return (FALSE);
+    }
   }
 
@@ -53,4 +74,8 @@
     return (FALSE);
   }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
+    return (FALSE);
+  }
   if (dypvec && (dypvec->Nelements != xvec->Nelements)) goto mismatch;
   if (dymvec && (dymvec->Nelements != xvec->Nelements)) goto mismatch;
@@ -59,17 +84,39 @@
 
   Npts = xvec[0].Nelements;
-  if (Npts == 0) return (TRUE);
+  if (Npts == 0) {
+    if (tvec) DeleteVector (tvec);
+    return (TRUE);
+  }
+
+  if (tvec) {
+    Npts = 0;
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+      if (!mask[i]) Npts ++;
+    }
+    if (Npts == 0) {
+      DeleteVector (tvec);
+      free (mask);
+      return TRUE;
+    }
+  }
 
   if (!KapaPrepPlot (kapa, Npts, &graphmode)) return (FALSE);
   
-  PlotVectorSingle (kapa, xvec, "x");
-  PlotVectorSingle (kapa, yvec, "y");
+  PlotVectorSingle (kapa, xvec, mask, "x");
+  PlotVectorSingle (kapa, yvec, mask, "y");
   if (graphmode.etype & 0x01) {
-    PlotVectorSingle (kapa, dymvec, "dym");
-    PlotVectorSingle (kapa, dypvec, "dyp");
+    PlotVectorSingle (kapa, dymvec, mask, "dym");
+    PlotVectorSingle (kapa, dypvec, mask, "dyp");
   }
   if (graphmode.etype & 0x02) {
-    PlotVectorSingle (kapa, dxmvec, "dxm");
-    PlotVectorSingle (kapa, dxpvec, "dxp");
+    PlotVectorSingle (kapa, dxmvec, mask, "dxm");
+    PlotVectorSingle (kapa, dxpvec, mask, "dxp");
+  }
+
+  if (tvec) {
+    free (mask);
+    DeleteVector (tvec);
   }
   return (TRUE);
Index: /trunk/Ohana/src/opihi/cmd.data/spline.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/spline.c	(revision 34584)
+++ /trunk/Ohana/src/opihi/cmd.data/spline.c	(revision 34584)
@@ -0,0 +1,54 @@
+# include "data.h"
+
+int spline_list (int argc, char **argv);
+int spline_create (int argc, char **argv);
+int spline_apply (int argc, char **argv);
+int spline_load (int argc, char **argv);
+int spline_save (int argc, char **argv);
+int spline_delete (int argc, char **argv);
+int spline_getspline (int argc, char **argv);
+
+static Command spline_commands[] = {
+  {1, "list",       spline_list,       "list splines"},
+  {1, "create",     spline_create,     "create a spline"},
+  {1, "apply",      spline_apply,      "apply a spline"},
+  {1, "load",       spline_load,       "write a spline to a FITS file"},
+  {1, "save",       spline_save,       "read a spline from a FITS file"},
+  {1, "delete",     spline_delete,     "delete a spline"},
+};
+
+int spline_command (int argc, char **argv) {
+
+  int i, N, status;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: spline (command)\n");
+    gprint (GP_ERR, "    spline list                                    : list splines\n");
+    gprint (GP_ERR, "    spline create   (spline)                       : create a spline\n");
+    gprint (GP_ERR, "    spline delete   (spline)                       : delete a spline\n");
+    return (FALSE);
+  }
+
+  N = sizeof (spline_commands) / sizeof (Command);
+
+  /* find the spline sub-command which matches */
+  for (i = 0; i < N; i++) {
+    if (!strcmp (spline_commands[i].name, argv[1])) {
+      status = (*spline_commands[i].func) (argc - 1, argv + 1);
+      return (status);
+    }
+  }
+
+  gprint (GP_ERR, "unknown spline command %s\n", argv[1]);
+  return (FALSE);
+}
+
+/* spline is called with the command "spline".  
+   the command line word "spline" is meant to be followed the one of several 
+   possible options:
+   
+   spline create
+   spline delete
+   spline list
+
+*/
Index: unk/Ohana/src/opihi/cmd.data/spline_apply.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/spline_apply.c	(revision 34583)
+++ 	(revision )
@@ -1,98 +1,0 @@
-# include "data.h"
-
-// need to rename this as an image function
-int spline_apply_cmd (int argc, char **argv) {
-  
-  int i, j, I, J;
-  int nx, ny, Nx, Ny;
-  float rx, ry, x, y;
-  float *Tx1, *Tx2, *Txc, *Ty1, *Ty2, *Tyc, *V, *V1, *V2;
-  Buffer *out, *y1, *y2;
-
-  if (argc != 7) {
-    gprint (GP_ERR, "USAGE: spline_apply <Y> <Y2> <out> (x/y) Nx Ny\n");
-    return (FALSE);
-  }
-
-  if ((y1  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
-  if ((y2  = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) return (FALSE);
-  if ((out = SelectBuffer (argv[3], ANYBUFFER, TRUE)) == NULL) return (FALSE);
-
-  // xdir = FALSE;
-  // if (!strcmp (argv[4], "x")) xdir = TRUE; 
-
-  nx = atoi (argv[5]);
-  ny = atoi (argv[6]);
-
-  Nx = y1[0].matrix.Naxis[0];
-  Ny = y1[0].matrix.Naxis[1];
-
-  rx = Nx / (float) nx;
-  ry = Ny / (float) ny;
-
-  /* create an output matrix buffer with desired nx, ny */
-  gfits_free_matrix (&out[0].matrix);
-  gfits_free_header (&out[0].header);
-
-  out[0].bitpix = y1[0].bitpix;
-  out[0].unsign = y1[0].unsign;
-  out[0].bscale = y1[0].bscale;
-  out[0].bzero  = y1[0].bzero;
-  gfits_copy_header (&y1[0].header, &out[0].header);
-  gfits_modify (&out[0].header, "NAXIS1", "%d", 1, nx);
-  gfits_modify (&out[0].header, "NAXIS2", "%d", 1, ny);
-
-  out[0].header.Naxis[0] = nx;
-  out[0].header.Naxis[1] = ny;
-  gfits_create_matrix (&out[0].header, &out[0].matrix);
-  if ((y1[0].file[0] != '*') && (y1[0].file[0] != '(')) {
-    sprintf (out[0].file, "*%s", y1[0].file);
-  } else {
-    sprintf (out[0].file, "%s", y1[0].file);
-  }
-
-  ALLOCATE (Ty2, float, Ny);
-  ALLOCATE (Ty1, float, Ny);
-  ALLOCATE (Tyc, float, Ny);
-  for (i = 0; i < Ny; i++) { Tyc[i] = i; }
-
-  ALLOCATE (Tx1, float, Nx);
-  ALLOCATE (Tx2, float, Nx);
-  ALLOCATE (Txc, float, Nx);
-  for (i = 0; i < Nx; i++) { Txc[i] = i; }
-
-  V = (float *)(out[0].matrix.buffer);
-
-  for (J = 0; J < ny; J++) {
-    y = J * ry;
-
-    /* construct spline for each element in this row */
-    for (i = 0; i < Nx; i++) {
-      V1 = (float *)(y1[0].matrix.buffer) + i;
-      V2 = (float *)(y2[0].matrix.buffer) + i;
-      for (j = 0; j < Ny; j++, V1+=Nx, V2+=Nx) {
-	Ty1[j] = *V1;
-	Ty2[j] = *V2;
-      }
-      Tx1[i] = spline_apply (Tyc, Ty1, Ty2, Ny, y);
-    }
-    spline_construct (Txc, Tx1, Nx, Tx2);
-
-    /* apply x-dir spline to new image */
-    for (I = 0; I < nx; I++, V++) {
-      x = I * rx;
-      *V = spline_apply (Txc, Tx1, Tx2, Nx, x);
-    }
-  }
-
-  free (Ty1);
-  free (Ty2);
-  free (Tyc);
-  
-  free (Tx1);
-  free (Tx2);
-  free (Txc);
-  
-  return (TRUE);
-
-}
Index: /trunk/Ohana/src/opihi/cmd.data/spline_commands.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/spline_commands.c	(revision 34584)
+++ /trunk/Ohana/src/opihi/cmd.data/spline_commands.c	(revision 34584)
@@ -0,0 +1,148 @@
+# include "data.h"
+
+int spline_list (int argc, char **argv) {
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: spline list\n");
+    return FALSE;
+  }
+
+  ListSplines();
+  return TRUE;
+}
+
+int spline_create (int argc, char **argv) {
+
+  int i;
+  Vector *xvec, *yvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline create (name) (Xknots) (Yknots)\n");
+    return FALSE;
+  }
+
+  if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (xvec->Nelements != yvec->Nelements) {
+    gprint (GP_ERR, "x and y vectors must be the same length\n");
+    return FALSE;
+  }
+
+  Spline *myspline = CreateSpline (argv[1], xvec->Nelements);
+
+  for (i = 0; i < xvec->Nelements; i++) {
+    myspline->xk[i] = xvec->elements.Flt[i];
+    myspline->yk[i] = yvec->elements.Flt[i];
+  }    
+
+  spline_construct_dbl (myspline->xk, myspline->yk, myspline->Nknots, myspline->y2);
+  return TRUE;
+}
+
+int spline_apply (int argc, char **argv) {
+
+  int i;
+  Vector *xvec, *yvec;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: spline apply (name) (Xpoints) (Ypoints)\n");
+    return FALSE;
+  }
+
+  Spline *myspline = FindSpline (argv[1]);
+  if (!myspline) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return (FALSE);
+  }
+
+  if ((xvec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((yvec = SelectVector (argv[3], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  ResetVector (yvec, OPIHI_FLT, xvec->Nelements);
+
+  for (i = 0; i < xvec->Nelements; i++) {
+    opihi_flt value = spline_apply_dbl (myspline->xk, myspline->yk, myspline->y2, myspline->Nknots, xvec->elements.Flt[i]);
+    yvec->elements.Flt[i] = value;
+  }    
+
+  return TRUE;
+}
+
+int spline_save (int argc, char **argv) {
+
+  int N;
+
+  int APPEND = FALSE;
+  if ((N = get_argument (argc, argv, "-append"))) {
+    APPEND = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: spline save (name) (filename) [-append]\n");
+    return FALSE;
+  }
+
+  if (!SaveSpline(argv[2], argv[1], APPEND)) {
+    gprint (GP_ERR, "failed to save spline %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+
+int spline_load (int argc, char **argv) {
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: spline load (name) (filename)\n");
+    return FALSE;
+  }
+
+  if (!LoadSpline(argv[2], argv[1])) {
+    gprint (GP_ERR, "failed to load spline %s\n", argv[1]);
+    return (FALSE);
+  }
+  return TRUE;
+}
+
+int spline_delete (int argc, char **argv) {
+
+  int status;
+  Spline *spline;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: spline delete (spline)\n");
+    return FALSE;
+  }
+
+  spline = FindSpline (argv[1]);
+  if (spline == NULL) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  status = DeleteSpline (spline);
+  if (!status) abort ();
+  return TRUE;
+}
+
+/* 
+int spline_listspline (int argc, char **argv) {
+
+  Spline *spline;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: spline listspline (spline)\n");
+    return FALSE;
+  }
+
+  spline = FindSpline (argv[1]);
+  if (spline == NULL) {
+    gprint (GP_ERR, "spline %s not found\n", argv[1]);
+    return FALSE;
+  }
+
+  ListPages (spline);
+  return TRUE;
+}
+*/
+
Index: unk/Ohana/src/opihi/cmd.data/spline_construct.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/spline_construct.c	(revision 34583)
+++ 	(revision )
@@ -1,72 +1,0 @@
-# include "data.h"
-
-// need to rename this as an image function
-int spline_construct_cmd (int argc, char **argv) {
-  
-  int i, j, Nx, Ny;
-  float *Tx, *Ty, *Ty2, *V;
-  Buffer *in, *out;
-
-  if (argc != 4) {
-    gprint (GP_ERR, "USAGE: spline_construct <in> <out> (x/y)\n");
-    return (FALSE);
-  }
-
-  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
-  if ((out = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) return (FALSE);
-
-  // XXX move this to gfits_create_matrix
-  free (out[0].matrix.buffer);
-  if ((in[0].file[0] != '*') && (in[0].file[0] != '(')) {
-    sprintf (out[0].file, "*%s", in[0].file);
-  } else {
-    sprintf (out[0].file, "%s", in[0].file);
-  }
-  out[0].bitpix = in[0].bitpix;
-  out[0].unsign = in[0].unsign;
-  out[0].bscale = in[0].bscale;
-  out[0].bzero  = in[0].bzero;
-  gfits_copy_matrix_info (&in[0].matrix, &out[0].matrix);
-  gfits_copy_header (&in[0].header, &out[0].header);
-  gfits_create_matrix (&out[0].header, &out[0].matrix);
-
-  // int xdir = FALSE;
-  // if (!strcmp (argv[3], "x")) xdir = TRUE; 
-  /* ideally, the resulting image should carry this info (in header?) */
-
-  Nx = in[0].matrix.Naxis[0];
-  Ny = in[0].matrix.Naxis[1];
-
-  ALLOCATE (Ty2, float, Ny);
-  ALLOCATE (Ty, float, Ny);
-  ALLOCATE (Tx, float, Ny);
-
-  /** for now only perform the operation for the ydir splines */
-
-  /* construct coordinate vector */
-  for (j = 0; j < Ny; j++) { Tx[j] = j; }
-  
-  for (i = 0; i < Nx; i++) {
-    
-    /* construct temp vector with values to spline */
-    V = (float *)(in[0].matrix.buffer) + i;
-    for (j = 0; j < Ny; j++, V+=Nx) {
-      Ty[j] = *V;
-    }
-  
-    spline_construct (Tx, Ty, Ny, Ty2);
-  
-    /* copy derivatives to output buffer */
-    V = (float *)(out[0].matrix.buffer) + i;
-    for (j = 0; j < Ny; j++, V+=Nx) {
-      *V = Ty2[j];
-    }
-  }
-
-  free (Tx);
-  free (Ty);
-  free (Ty2);
-  
-  return (TRUE);
-
-}
Index: /trunk/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 34584)
@@ -99,5 +99,5 @@
   }
 
-  /* open file for outuput */
+  /* open file for output */
   if (append) {
     f = fopen (argv[1], "a");
Index: /trunk/Ohana/src/opihi/cmd.data/zplot.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/zplot.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/cmd.data/zplot.c	(revision 34584)
@@ -3,5 +3,6 @@
 int zplot (int argc, char **argv) {
   
-  int i, kapa;
+  char *outname;
+  int i, kapa, valid, size;
   opihi_flt *out;
   double min, range;
@@ -11,6 +12,9 @@
   if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
 
-  if (argc != 6) {
+  valid  = (argc == 6);
+  valid |= (argc > 7) && !strcmp (argv[6], "where");
+  if (!valid) {
     gprint (GP_ERR, "USAGE: zplot <x> <y> <z> min max\n");
+    gprint (GP_ERR, "   OR: zplot <x> <y> <z> min max where (condition)\n");
     return (FALSE);
   }
@@ -18,4 +22,21 @@
   min = atof(argv[4]);
   range = atof(argv[5]) - min;
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 7) {
+    outname = dvomath (argc - 7, &argv[7], &size, 1);
+    if (outname == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (outname, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (outname);
+      free (outname);
+      return (FALSE);
+    }
+  }
 
   /* find vectors */
@@ -29,4 +50,8 @@
   if (xvec[0].Nelements != zvec[0].Nelements) {
     gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
     return (FALSE);
   }
@@ -46,11 +71,20 @@
   }
 
+  if (tvec) {
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+    }
+  }
+
   /* point size determined by Zvec */
   graphmode.style = 2; /* plot points */
   graphmode.size = -1; /* point size determined by Zvec */
   graphmode.etype = 0; /* no errorbars */
-  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, &graphmode);
+  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, mask, &graphmode);
 
   free (Zvec.elements.Ptr);
+  if (mask) free (mask);
+  DeleteNamedVector (outname);
 
   return (TRUE);
@@ -60,5 +94,6 @@
 int zcplot (int argc, char **argv) {
   
-  int i, kapa;
+  char *outname;
+  int i, kapa, valid, size;
   opihi_flt *out;
   double min, range;
@@ -68,6 +103,9 @@
   if (!style_args (&graphmode, &argc, argv, &kapa)) return (FALSE);
 
-  if (argc != 6) {
-    gprint (GP_ERR, "USAGE: zplot <x> <y> <z> min max\n");
+  valid  = (argc == 6);
+  valid |= (argc > 7) && !strcmp (argv[6], "where");
+  if (!valid) {
+    gprint (GP_ERR, "USAGE: zcplot <x> <y> <z> min max\n");
+    gprint (GP_ERR, "   OR: zcplot <x> <y> <z> min max where (condition)\n");
     return (FALSE);
   }
@@ -75,4 +113,21 @@
   min = atof(argv[4]);
   range = atof(argv[5]) - min;
+
+  // tvec is used for logical test (truth vector)
+  Vector *tvec = NULL;
+  char *mask = NULL;
+  if (argc > 7) {
+    outname = dvomath (argc - 7, &argv[7], &size, 1);
+    if (outname == NULL) {
+      print_error ();
+      return FALSE;
+    }
+    if ((tvec = SelectVector (outname, OLDVECTOR, TRUE)) == NULL) {
+      gprint (GP_ERR, " invalid logic result\n");
+      DeleteNamedVector (outname);
+      free (outname);
+      return (FALSE);
+    }
+  }
 
   /* find vectors */
@@ -86,4 +141,8 @@
   if (xvec[0].Nelements != zvec[0].Nelements) {
     gprint (GP_ERR, "vectors %s and %s not the same length\n", argv[1], argv[3]);
+    return (FALSE);
+  }
+  if (tvec && tvec[0].Nelements != yvec[0].Nelements) {
+    gprint (GP_ERR, "logic test vector not the same length as data vectors\n");
     return (FALSE);
   }
@@ -103,11 +162,20 @@
   }
 
+  if (tvec) {
+    ALLOCATE (mask, char, tvec->Nelements);
+    for (i = 0; i < tvec->Nelements; i++) {
+      mask[i] = (tvec->type == OPIHI_FLT) ? (tvec->elements.Flt[i] == 0.0) : (tvec->elements.Int[i] == 0.0);
+    }
+  }
+
   /* point size determined by Zvec */
   graphmode.style = 2; /* plot points */
   graphmode.color = -1; /* point color determined by Zvec */
   graphmode.etype = 0; /* no errorbars */
-  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, &graphmode);
+  PlotVectorTriplet (kapa, xvec, yvec, &Zvec, mask, &graphmode);
 
   free (Zvec.elements.Ptr);
+  if (mask) free (mask);
+  DeleteNamedVector (outname);
 
   return (TRUE);
Index: /trunk/Ohana/src/opihi/doc/pantasks.controller.txt
===================================================================
--- /trunk/Ohana/src/opihi/doc/pantasks.controller.txt	(revision 34584)
+++ /trunk/Ohana/src/opihi/doc/pantasks.controller.txt	(revision 34584)
@@ -0,0 +1,273 @@
+
+Pantasks interacts with a parallel job manager.  The current version
+is 'pcontrol', but we would like to allow interactions with condor as
+well.  This file documents the pantasks / controller interactions and
+the requirements for implementing a condor (or other) interface.
+
+Some terminology:
+
+* controller : the abstract concept of the software or system which
+  manages parallel jobs for pantasks.
+
+* pcontrol : the default (Ohana-native) controller implementation
+
+Pantasks Controller Commands.  
+
+The following commands are available within the pantasks shell to send
+commands to the controller.  Within the pantasks shell, these are
+invoked with "controller (command) [options]".  When running the
+pcontrol shell on its own, these commands are called directly.
+
+* check job (jobID)
+* check host (hostID)
+
+check on the status of a single job or host.  the jobID or hostID is
+an integer value [1].  The return is a block of information giving the
+status and some other infomation.  
+
+For 'host', the return is of the form:
+
+host (state)
+STATUS 1
+
+where (state) is one of IDLE, BUSY, RESP, DONE, DOWN, OFF.
+
+For 'job', the return is of the form:
+
+STATUS (status)
+EXITST (Nexit)
+STDOUT (Nbytes)
+STDERR (Nbytes)
+DTIME  (elapsed time)
+
+where:
+
+(status) is one of PENDING, BUSY, DONE, EXIT, CRASH
+(Nexit) is the exit status of the command (ie, as if it were run on
+	the UNIX command line).
+(Nbytes) is the size of the standard out and standard error buffers
+	 from the job
+(elapsed time) is the number of seconds it took to run the command;
+	 this is only set on exit.
+
+* exit
+
+Tell the controller to exit.  In pantasks, it is necessary to give this command in
+the form 'controller exit TRUE'.
+
+* host [options] : manipulate hosts managed by the controller.  The
+following commands may be given:
+
+  * host add (hostname) [-threads N] : add a new connection to the host
+    (hostname).  The optional -threads N argument specifies the default
+    value for this machine for the @MAX_THREADS@ directive.  A job sent
+    to this machine with @MAX_THREADS@ in the command line will have
+    that value replaced by "-threads N" for this machine, were N is the
+    value specified in this host add command.
+  
+  * host on (hostname) : tell the controller to activate the specified
+    host
+  
+  * host off (hostname) : tell the contoller to de-activate the
+    specified host
+  
+  * host check (hostname) : check the status of the given host by
+    hostname (returns 'host (hostname) is (status)' where (status) is
+    one of the list given above for check host
+  
+  * host retry (hostname) : tell the controller to re-attempt a
+    connection to the specified machine NOW (if the connection failed,
+    pcontrol attempts to connect with an increasingly long timeout.  If
+    the timeout is long, but the user knows the machine is now alive,
+    they may desire to force a connection attempt sooner rather than
+    waiting for the timeout to complete).
+  
+  * host delete (hostname) : remove the named host from the list of
+    managed hosts.
+
+Note that the controller manages connections to host machines by
+name. Multiple connections are not generally tied together -- as far
+as pantasks normally is aware, they are not related.  Thus, if a given
+session has N connections to a given machine (host add was called N
+times), then manipulation of the state of the machine may require N
+calls of the same function.  (Note that commands which change a
+machine state, such as 'on', 'off', 'delete' only affect machines in
+the appropriate state.  eg, 'host on ipp050' is invalid if ipp050 is not
+currently off.
+
+* hoststack (stack) : list the hosts in the given 'stack'.  A
+  collection of hosts in a given state is called a 'stack' of hosts.
+  This command lists all of the hosts in one of the stacks.  Stack
+  names are case-insensitive and may be one of the following:
+
+  * IDLE : machines which are currently unoccupied with processing
+  * BUSY : machines which are currently active
+  * RESP : machines which are currently active and responding to
+           another command
+  * DONE : machines which have completed a job, but are not yet ready
+    	   to accept a new job.
+  * DOWN : machines which are currently unresponsive (pcontrol will
+    	   try to reconnect after an interval)
+  * OFF  : machines which are currently off (pcontrol will not try to reconnect)
+
+The response to this command is a list of the machines (a series of
+lines each with ID NAME on a single line).
+
+* jobstack (stack) : list the jobs in the given 'stack'.  Like the
+  hosts, a collection of jobs in a given state is called a 'stack' of
+  jobs.  This command lists all of the jobs in one of the stacks.
+  Stack names are case-insensitive and may be one of the following
+
+  * PENDING : job is waiting for a host
+  * BUSY    : job is running on a host
+  * RESP    : job is running on a host, and responding to another command
+  * DONE    : job has finished, but its completion state has not yet
+    	      been assessed
+  * EXIT    : job finished with a valid exit status (ie, no abort or segfault)
+  * CRASH   : job aborted or segfaulted
+  * KILL    : kill has been requested for the job
+
+* machines : list the status of the hosts by unique machine name.
+  This command is one of the few which work with the multiple
+  connections to a single named machine as the same connection.  The
+  command lists the number of connections to the give machine, the
+  number of jobs currently running on that host, and the number of
+  jobs running on another host which requested the given host. 
+
+* parameters : set several pcontrol internal parameters.  this
+  function lets the user interact with the pcontrol shell and set some
+  internal state variables. The 3 currently allowed options are:
+
+  * parameters connect_time (time) : set the maximum time a pclient is
+    kept alive before pcontrol attempts to reset the connection (time
+    in seconds?)
+  * parameters wanthost_wait (time) : set the amount of time pcontrol
+    will wait before sending a job to a host other than the desired
+    host (time in seconds)
+  * parameters unwanted_host_jobs (Njobs) : set the number of jobs
+    allowed to run which desire a specific host.  this parameters
+    prevents pcontrol from overloading some specific machine due to
+    I/O operations when the process operation is elsewhere.
+
+ * output : print the bufers which carry the controller output.  when
+   pcontrol starts up, the output can be redirected to a file.  if it
+   is not redirected, it is stored by pantasks.  the buffer is not
+   normally dumped in a regular fashion, and can fill the pantasks
+   memory usage.  this command dumps the output to pantasks, and can
+   also flush the buffer (if the "flush" option is given).
+
+ * run : set the run level for the controller.  the command is of the
+   form "run (level)".  pcontrol may be in one of 4 run levels: 
+   * all : all normal pcontrol ops (this is aslo set with no optional
+     	   argument to the run command).
+   * reap : keep the machines running (maintain comms and turn on/off
+     	    as needed) and harvest results from jobs, but do not spawn
+   	    new jobs.
+   * hosts : manage the machines, but do not manage jobs (spawn or
+     	     harvest).
+   * none  : stop all pcontrol processing
+
+ * status : report the current status of the controller: list all
+   known jobs and all known hosts, giving their status.
+
+ * stop : stop all pcontrol processing (equivalent to "run none")
+
+ * verbose : turn on verbose mode for pcontrol (output to pcontrol.log
+   or use "controller output").
+
+ * version : print version info for pcontrol
+
+ * pulse : in non-threaded pcontrol mode (deprecated), set the
+   readline timeout.
+  
+[1] : note that the command 'controller status' includes the job and
+host ID values in the form X.X.X.X where X is a hexidecimal number.
+Unfortunately, pcontrol does not understand this format for the IDs
+for the 'check' command.   
+
+--
+
+Pantasks / Controller Interactions
+
+Pantasks Threads & Controller Interactions
+
+Pantasks has a server/client mode and a stand-alone mode.  These share
+the bulk of code, but there are some minor difference.  In stand-alone
+mode, there are 3 active threads.  The main thread interprets the
+commands (accepted by readline); a second thread manages the tasks and
+jobs known to pantasks; the third thread manages interactions with the
+parallel controller.   In server/client mode, a fourth thread manages
+the communcations with the remote clients.  
+
+The task/job thread is responsible for monitoring the task rules and
+constructing the commands which corresponds to jobs when appropriate.
+Those jobs which are defined to be local are executed on the local
+machine, while remote jobs are sent to the thread which interacts with
+the parallel controller.
+
+The primary job of the controller thread is to monitor the status of
+jobs submitted to the controller and to harvest jobs which have
+finished.  A secondary job is to flush the stdout and stderr buffers
+of the pantasks / pcontrol connection.  
+
+Jobs are submitted to the controller directly by the Task/Job thread.
+Other operations, check as manual checks of the job status are
+performed by the main thread.
+
+Commands send to pcontrol:
+
+In addition to the user-level commands discussed above, the following
+messages are sent to the controller:
+
+* jobstack exit : the controller thread checks for the set of completed
+  jobs (which did not crash) by sending this command.  The response is
+  a list of jobs ready for harvest.
+
+* jobstack crash : the controller thread checks for the set of
+  complete jobs which crashed by sending this command.  The response
+  is a list of jobs ready for harvest.
+
+* delete : jobs which have completed and for which the stderr/stdout
+  have been received can be deleted from the controller.  For
+  'pcontrol', this is necessary to free up resources managing the
+  specific job.
+
+* check job : once the controller gets a list of jobs which have
+  exited or crashed, pantasks loops over those jobs, harvesting their
+  results.  this command is used to get the needed stats (size of
+  stderr buffer, size of stdout buffer, exit status, etc).  
+
+* job : Submit a job to the controller.  this command is used by the
+  function SubmitControllerJob to send a new job to the controller.
+  the function appends options such as +host, -nice as needed.  the
+  function expects to receive a job ID from the controller for future
+  interactions.
+
+* quit : shutdown the controller
+
+---
+
+pantasks & condor
+
+There are three classes of pcontrol operations which pantasks
+currently perform:
+
+* submit jobs to pcontrol
+* detect completion and harvest job output
+* manage the hosts used by pcontrol
+
+The communication between pantasks and pcontrol uses a pipe; pcontrol
+is run as a forked child of pantasks.  The communication is done via
+text blocks with fairly minimal hand-shaking.  
+
+If pantasks is integrated with condor, it is critical that it be able
+to perform the first two of these functions.  It is not required that
+pantasks manage the condor hosts.
+
+Condor client / server interaction could be performed via forked
+commands or we could use the SOAP interface.  I suspect that SOAP will
+be more effective for the high-rate job harvesting process.
+
+However jobs are submitted to condor, it is critical that condor be
+able to respect the limit on the number of mistargeted jobs currently
+active.
Index: /trunk/Ohana/src/opihi/dvo/fitcolors.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/fitcolors.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/fitcolors.c	(revision 34584)
@@ -311,5 +311,5 @@
 	KapaBox (kapa, &graphdata);
 
-	PlotVectorPair (kapa, xvec, yvec, &graphdata);
+	PlotVectorPair (kapa, xvec, yvec, NULL, &graphdata);
 
 	for (i = 0; i < 11; i++) {
Index: /trunk/Ohana/src/opihi/dvo/images.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/images.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/images.c	(revision 34584)
@@ -350,5 +350,5 @@
     graphmode.ptype = 100; /* connect pairs of points */
     graphmode.etype = 0;
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/imbox.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/imbox.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/imbox.c	(revision 34584)
@@ -123,5 +123,5 @@
     graphmode.ptype = 100; /* connect pairs of points */
     graphmode.etype = 0;
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/imdense.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/imdense.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/imdense.c	(revision 34584)
@@ -65,5 +65,5 @@
     graphmode.style = 2; /* points */
     graphmode.etype = 0;
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/imstats.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/imstats.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/imstats.c	(revision 34584)
@@ -54,5 +54,5 @@
   graphmode.style = 2;
   graphmode.etype = 0;
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/dvo/lcurve.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/lcurve.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/lcurve.c	(revision 34584)
@@ -160,5 +160,5 @@
     PlotVectorPairErrors (kapa, &Xvec, &Yvec, &dYvec, &graphmode);
   } else {
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/procks.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/procks.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/procks.c	(revision 34584)
@@ -113,5 +113,5 @@
   graphmode.style = 2; /* set style to points */
   graphmode.etype = 0; /* no errorbars */
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
 
   /* now plot vectors between two extrema */
@@ -146,5 +146,5 @@
   graphmode.etype = 0; /* no errorbars */
 
-  PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+  PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
 
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/dvo/showtile.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/showtile.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/showtile.c	(revision 34584)
@@ -86,5 +86,5 @@
     graphmode.ptype = 100; /* connect pairs of points */
     graphmode.etype = 0;
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/dvo/simage.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/simage.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/simage.c	(revision 34584)
@@ -151,5 +151,5 @@
   graphmode.etype = 0;
 
-  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, &graphmode);
+  PlotVectorTriplet (kapa, &Xvec, &Yvec, &Zvec, NULL, &graphmode);
 
   free (Xvec.elements.Flt);
Index: /trunk/Ohana/src/opihi/dvo/skycat.c
===================================================================
--- /trunk/Ohana/src/opihi/dvo/skycat.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/dvo/skycat.c	(revision 34584)
@@ -100,5 +100,5 @@
     graphmode.ptype = 100; /* connect pairs of points */
     graphmode.etype = 0;
-    PlotVectorPair (kapa, &Xvec, &Yvec, &graphmode);
+    PlotVectorPair (kapa, &Xvec, &Yvec, NULL, &graphmode);
   }
 
Index: /trunk/Ohana/src/opihi/include/data.h
===================================================================
--- /trunk/Ohana/src/opihi/include/data.h	(revision 34583)
+++ /trunk/Ohana/src/opihi/include/data.h	(revision 34584)
@@ -84,6 +84,8 @@
 
 /* in spline.c */
-void spline_construct (float *x, float *y, int N, float *y2);
-float spline_apply (float *x, float *y, float *y2, int N, float X);
+void spline_construct_flt (float *x, float *y, int N, float *y2);
+float spline_apply_flt (float *x, float *y, float *y2, int N, float X);
+void spline_construct_dbl (opihi_flt *x, opihi_flt *y, int N, opihi_flt *y2);
+opihi_flt spline_apply_dbl (opihi_flt *x, opihi_flt *y, opihi_flt *y2, int N, opihi_flt X);
 
 /* in svdcmp.c */
@@ -168,4 +170,26 @@
 void FreeQueues (void);
 void FreeBooks (void);
+
+// the interpolating spline has valu
+typedef struct {
+  int Nknots;
+  opihi_flt *xk;
+  opihi_flt *yk;
+  opihi_flt *y2;
+  char *name;
+} Spline;
+
+/* in SplineOps.c */
+void InitSplines ();
+void FreeSplines ();
+void InitSpline (Spline *spline, char *name, int Nknots);
+void FreeSpline (Spline *spline);
+Spline *GetSpline (int where);
+Spline *FindSpline (char *name);
+Spline *CreateSpline (char *name, int Nknots);
+int DeleteSpline (Spline *spline);
+void ListSplines ();
+int SaveSpline (char *filename, char *name, int append);
+int LoadSpline (char *filename, char *name);
 
 /* hermitian functions */
Index: /trunk/Ohana/src/opihi/include/display.h
===================================================================
--- /trunk/Ohana/src/opihi/include/display.h	(revision 34583)
+++ /trunk/Ohana/src/opihi/include/display.h	(revision 34584)
@@ -7,8 +7,9 @@
 
 /*** kapa graph functions ***/
-int           PlotVectorSingle      PROTO((int kapa, Vector *vec, char *mode));
-int           PlotVectorPair        PROTO((int kapa, Vector *xVec, Vector *yVec, Graphdata *graphmode));
+int           PlotVectorSingle      PROTO((int kapa, Vector *vec, char *mask, char *mode));
+int           PlotVectorPair        PROTO((int kapa, Vector *xVec, Vector *yVec, char *mask, Graphdata *graphmode));
 int           PlotVectorPairErrors  PROTO((int kapa, Vector *xVec, Vector *yVec, Vector *dyValues, Graphdata *graphmode));
-int           PlotVectorTriplet     PROTO((int kapa, Vector *xVec, Vector *yVec, Vector *zValues, Graphdata *graphmode));
+
+int           PlotVectorTriplet     PROTO((int kapa, Vector *xVec, Vector *yVec, Vector *zValues, char *mask, Graphdata *graphmode));
 // int           GetGraphData          PROTO((Graphdata *data, int *kapa, char *name));
 int           GetGraph              PROTO((Graphdata *data, int *kapa, char *name));
Index: /trunk/Ohana/src/opihi/lib.data/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/Makefile	(revision 34583)
+++ /trunk/Ohana/src/opihi/lib.data/Makefile	(revision 34584)
@@ -23,4 +23,5 @@
 $(SDIR)/bracket.$(ARCH).o		\
 $(SDIR)/spline.$(ARCH).o		\
+$(SDIR)/SplineOps.$(ARCH).o		\
 $(SDIR)/mrqmin.$(ARCH).o		\
 $(SDIR)/mrq2dmin.$(ARCH).o		\
Index: /trunk/Ohana/src/opihi/lib.data/PlotVectors.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/lib.data/PlotVectors.c	(revision 34584)
@@ -1,7 +1,7 @@
 # include "display.h"
 
-int PlotVectorSingle (int kapa, Vector *vec, char *mode) {
-
-  int i, Npts;
+int PlotVectorSingle (int kapa, Vector *vec, char *mask, char *mode) {
+
+  int i, Npts, Nout;
   float *temp;
 
@@ -9,25 +9,30 @@
   ALLOCATE (temp, float, Npts);
 
+  Nout = 0;
   if (vec->type == OPIHI_FLT) {
     opihi_flt *value = vec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = vec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, mode);
-
-  free (temp);
-
-  return (TRUE);
-}
-
-int PlotVectorPair (int kapa, Vector *xVec, Vector *yVec, Graphdata *graphmode) {
-
-  int i, Npts;
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, mode);
+
+  free (temp);
+
+  return (TRUE);
+}
+
+int PlotVectorPair (int kapa, Vector *xVec, Vector *yVec, char *mask, Graphdata *graphmode) {
+
+  int i, Npts, Nout;
   float *temp;
 
@@ -37,40 +42,49 @@
   ALLOCATE (temp, float, Npts);
 
-  KapaPrepPlot (kapa, Npts, graphmode);
-
+  Nout = 0;
   if (xVec->type == OPIHI_FLT) {
     opihi_flt *value = xVec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = xVec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, "x");
-
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPrepPlot (kapa, Nout, graphmode);
+  KapaPlotVector (kapa, Nout, temp, "x");
+
+  Nout = 0;
   if (yVec->type == OPIHI_FLT) {
     opihi_flt *value = yVec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = yVec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, "y");
-
-  free (temp);
-
-  return (TRUE);
-}
-
-int PlotVectorTriplet (int kapa, Vector *xVec, Vector *yVec, Vector *zVec, Graphdata *graphmode) {
-
-  int i, Npts;
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "y");
+
+  free (temp);
+
+  return (TRUE);
+}
+
+int PlotVectorTriplet (int kapa, Vector *xVec, Vector *yVec, Vector *zVec, char *mask, Graphdata *graphmode) {
+
+  int i, Npts, Nout;
   float *temp;
 
@@ -81,44 +95,58 @@
   ALLOCATE (temp, float, Npts);
 
-  KapaPrepPlot (kapa, Npts, graphmode);
-
+  Nout = 0;
   if (xVec->type == OPIHI_FLT) {
     opihi_flt *value = xVec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = xVec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, "x");
-
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPrepPlot (kapa, Nout, graphmode);
+  KapaPlotVector (kapa, Nout, temp, "x");
+
+  Nout = 0;
   if (yVec->type == OPIHI_FLT) {
     opihi_flt *value = yVec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = yVec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, "y");
-
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "y");
+
+  Nout = 0;
   if (zVec->type == OPIHI_FLT) {
     opihi_flt *value = zVec->elements.Flt;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
     }
   } else {
     opihi_int *value = zVec->elements.Int;
     for (i = 0; i < Npts; i++) {
-      temp[i] = value[i];
-    }
-  }
-  KapaPlotVector (kapa, Npts, temp, "z");
+      if (mask && mask[i]) continue;
+      temp[Nout] = value[i];
+      Nout ++;
+    }
+  }
+  KapaPlotVector (kapa, Nout, temp, "z");
 
   free (temp);
Index: /trunk/Ohana/src/opihi/lib.data/SplineOps.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/SplineOps.c	(revision 34584)
+++ /trunk/Ohana/src/opihi/lib.data/SplineOps.c	(revision 34584)
@@ -0,0 +1,263 @@
+# include "data.h"
+
+/* this file contains functions to manage the collection of Splines.
+   A spline in opihi is a native data structure which defines a 1D spline.  
+ */
+
+Spline **splines;   /* book to store the list of all splines */
+int     Nsplines;   /* number of currently defined splines */
+int     NSPLINES;   /* number of currently allocated splines */
+
+void InitSplines () {
+  Nsplines = 0;
+  NSPLINES = 16;
+  ALLOCATE (splines, Spline *, NSPLINES); 
+}
+
+void FreeSplines () {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    FreeSpline (splines[i]);
+  }
+  free (splines);
+}
+
+void InitSpline (Spline *spline, char *name, int Nknots) {
+
+  if (!spline[0].name) {
+    spline[0].name = strcreate (name);
+  }
+  spline[0].Nknots = Nknots;
+  
+  if (spline[0].xk) {
+    REALLOCATE (spline[0].xk, opihi_flt, spline[0].Nknots);
+    REALLOCATE (spline[0].yk, opihi_flt, spline[0].Nknots);
+    REALLOCATE (spline[0].y2, opihi_flt, spline[0].Nknots);
+  } else {
+    ALLOCATE (spline[0].xk, opihi_flt, spline[0].Nknots);
+    ALLOCATE (spline[0].yk, opihi_flt, spline[0].Nknots);
+    ALLOCATE (spline[0].y2, opihi_flt, spline[0].Nknots);
+  }
+  memset (spline[0].xk, 0, spline[0].Nknots * sizeof(opihi_flt));
+  memset (spline[0].yk, 0, spline[0].Nknots * sizeof(opihi_flt));
+  memset (spline[0].y2, 0, spline[0].Nknots * sizeof(opihi_flt));
+}
+
+void FreeSpline (Spline *spline) {
+
+    free (spline[0].name);
+    free (spline[0].xk);
+    free (spline[0].yk);
+    free (spline[0].y2);
+    free (spline);
+}
+
+/* return the given spline */
+Spline *GetSpline (int where) {
+
+  if (where < 0) where += Nsplines;
+  if (where < 0) return NULL;
+  if (where >= Nsplines) return NULL;
+  return (splines[where]);
+}
+
+/* return the given spline */
+Spline *FindSpline (char *name) {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    if (!strcmp (splines[i][0].name, name)) {
+      return (splines[i]);
+    }
+  }
+  return (NULL);
+}
+
+/* make a new named spline */
+Spline *CreateSpline (char *name, int Nknots) {
+
+  int N;
+  Spline *spline;
+
+  spline = FindSpline (name);
+  if (spline != NULL) {
+    InitSpline (spline, name, Nknots);
+    return (spline);
+  }
+
+  N = Nsplines;
+  Nsplines ++;
+  CHECK_REALLOCATE (splines, Spline *, NSPLINES, Nsplines, 16);
+  ALLOCATE (spline, Spline, 1);
+  spline->name = NULL;
+  spline->xk = NULL;
+  spline->yk = NULL;
+  spline->y2 = NULL;
+  InitSpline (spline, name, Nknots);
+  splines[N] = spline;
+  return (spline);
+}
+
+/* delete a spline */
+int DeleteSpline (Spline *spline) {
+
+  int i, N, NSPLINES_2;
+
+  /* find spline in spline list */
+  N = -1;
+  for (i = 0; i < Nsplines; i++) {
+    if (splines[i] == spline) {
+      N = i;
+      break;
+    }
+  }
+  if (N == -1) return (FALSE);
+
+  for (i = N; i < Nsplines - 1; i++) {
+    splines[i] = splines[i + 1];
+  }
+  Nsplines --;
+  NSPLINES_2 = MAX (16, NSPLINES / 2);
+  if (Nsplines < NSPLINES_2) {
+    NSPLINES = NSPLINES_2;
+    REALLOCATE (splines, Spline *, NSPLINES);
+  }
+
+  FreeSpline (spline);
+  return (TRUE);
+}
+
+/* list known books */
+void ListSplines () {
+
+  int i;
+
+  for (i = 0; i < Nsplines; i++) {
+    gprint (GP_ERR, "%-15s %3d\n", splines[i][0].name, splines[i][0].Nknots);
+  }
+  return;
+}
+
+// write the spline data to a FITS file.
+int SaveSpline (char *filename, char *name, int append) {
+  
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+
+  Spline *myspline = FindSpline (name);
+  if (!myspline) {
+    gprint (GP_ERR, "can't find spline for write : %s\n", name);
+    return (FALSE);
+  }
+
+  FILE *f = NULL;
+
+  /* open file for output */
+  if (append) {
+    f = fopen (filename, "a");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write : %s\n", filename);
+    return (FALSE);
+  }
+
+  gfits_create_table_header (&theader, "BINTABLE", name);
+
+  gfits_define_bintable_column (&theader, "D", "X_KNOT", NULL, NULL, 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "Y_KNOT", NULL, NULL, 1.0, 0.0);
+  gfits_define_bintable_column (&theader, "D", "DY2_DX", NULL, NULL, 1.0, 0.0);
+
+  // generate the output array that carries the data
+  gfits_create_table (&theader, &ftable);
+
+  gfits_set_bintable_column_reformat (&theader, &ftable, "X_KNOT", "double", myspline->xk, myspline->Nknots);
+  gfits_set_bintable_column_reformat (&theader, &ftable, "Y_KNOT", "double", myspline->yk, myspline->Nknots);
+  gfits_set_bintable_column_reformat (&theader, &ftable, "DY2_DX", "double", myspline->y2, myspline->Nknots);
+
+  if (!append) {
+    gfits_init_header (&header);
+    header.extend = TRUE;
+
+    gfits_create_header (&header);
+    gfits_create_matrix (&header, &matrix);
+
+    gfits_fwrite_header  (f, &header);
+    gfits_fwrite_matrix  (f, &matrix);
+
+    gfits_free_header (&header);
+    gfits_free_matrix (&matrix);
+  }
+  gfits_fwrite_Theader (f, &theader);
+  gfits_fwrite_table  (f, &ftable);
+
+  gfits_free_header (&theader);
+  gfits_free_table (&ftable);
+
+  fclose (f);
+  fflush (f);
+  return (TRUE);
+}
+
+// read the spline data from a FITS file.
+// these functions (LoadSpline, SaveSpline) should probably take spline pointers not names
+int LoadSpline (char *filename, char *name) {
+  
+  int i, Ncol;
+  off_t Nrow;
+  char type[16];
+
+  Header theader;
+  FTable ftable;
+
+  FILE *f = NULL;
+
+  /* open file for input */
+  f = fopen (filename, "r");
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for read : %s\n", filename);
+    return FALSE;
+  }
+
+  ftable.header = &theader;
+
+  // read the full table data into a buffer
+  if (!gfits_fread_ftable (f, &ftable, name)) {
+    fclose (f);
+    gprint (GP_ERR, "can't read FITS file : %s\n", filename);
+    return FALSE;
+  }
+
+  // XXX: need to handle case of spline data not existing...
+
+  // need to create and assign to flat-field correction
+  double *xk = gfits_get_bintable_column_data (&theader, &ftable, "X_KNOT", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+  
+  // need to create and assign to flat-field correction
+  double *yk = gfits_get_bintable_column_data (&theader, &ftable, "Y_KNOT", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+
+  // need to create and assign to flat-field correction
+  double *y2 = gfits_get_bintable_column_data (&theader, &ftable, "DY2_DX", type, &Nrow, &Ncol);
+  assert (!strcmp(type, "double"));
+
+  Spline *myspline = CreateSpline (name, Nrow);
+  for (i = 0; i < Nrow; i++) {
+    myspline->xk[i] = xk[i];
+    myspline->yk[i] = yk[i];
+    myspline->y2[i] = y2[i];
+  }
+  free (xk);
+  free (yk);
+  free (y2);
+
+  fclose (f);
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/lib.data/spline.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/spline.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/lib.data/spline.c	(revision 34584)
@@ -2,5 +2,5 @@
 
 /* construct the natural spline for x, y in y2 */
-void spline_construct (float *x, float *y, int N, float *y2) {
+void spline_construct_flt (float *x, float *y, int N, float *y2) {
 
   int i;
@@ -27,5 +27,5 @@
 
 /* evaluate spline for x, y, y2 at X */
-float spline_apply (float *x, float *y, float *y2, int N, float X) {
+float spline_apply_flt (float *x, float *y, float *y2, int N, float X) {
 
   int i, lo, hi;
@@ -58,2 +58,60 @@
 
 }
+
+/* construct the natural spline for x, y in y2 */
+void spline_construct_dbl (opihi_flt *x, opihi_flt *y, int N, opihi_flt *y2) {
+
+  int i;
+  opihi_flt dy, dx, *tmp;
+  
+  ALLOCATE (tmp, opihi_flt, N);
+
+  y2[0] = tmp[0] = 0.0;
+  
+  for (i = 1; i < N-1; i++) {
+    dx = (x[i+0] - x[i-1]) / (x[i+1] - x[i-1]);
+    dy = dx * y2[i-1] + 2.0;
+    y2[i] = (dx - 1.0) / dy;
+    tmp[i] = (y[i+1] - y[i+0]) / (x[i+1] - x[i+0]) - (y[i+0] - y[i-1]) / (x[i+0] - x[i-1]);
+    tmp[i] = (6.0 * tmp[i] / (x[i+1] - x[i-1]) - dx*tmp[i-1]) / dy;
+  }
+  
+  y2[N-1] = 0;
+  for (i = N-2; i >= 1; i--)
+    y2[i] = y2[i]*y2[i+1] + tmp[i];
+
+  free (tmp);
+}
+
+/* evaluate spline for x, y, y2 at X */
+opihi_flt spline_apply_dbl (opihi_flt *x, opihi_flt *y, opihi_flt *y2, int N, opihi_flt X) {
+
+  int i, lo, hi;
+  opihi_flt dx, a, b, value;
+  
+  /* find correct element in array (x must be sorted) */
+  lo = 0;
+  hi = N-1;
+  while (hi - lo > 1) {
+    i = 0.5*(hi+lo);
+    if (x[i] > X) {
+      hi = i;
+    } else {
+      lo = i;
+    }
+  }
+
+  /* error condition: duplicate abssisca */
+  dx = x[hi] - x[lo];
+  if (dx == 0.0) {
+    return (HUGE_VAL);
+  }
+
+  /* evaluate spline */
+  a = (x[hi] - X) / dx;
+  b = (X - x[lo]) / dx;
+
+  value = a*y[lo] + b*y[hi] + ((a*a*a - a)*y2[lo] + (b*b*b - b)*y2[hi])*(dx*dx) / 6.0;
+  return (value);
+
+}
Index: /trunk/Ohana/src/opihi/lib.shell/VectorIO.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 34583)
+++ /trunk/Ohana/src/opihi/lib.shell/VectorIO.c	(revision 34584)
@@ -2,34 +2,11 @@
   
 // write a set of vectors to a FITS file (vectors names become fits column names)
-int WriteVectorTableFITS (char *filename, char *extname, Vector **vec, int Nvec, int append, char *format) {
-  
+int WriteVectorTable (Header *theader, FTable *ftable, char *extname, Vector **vec, int Nvec, char *format) {
+  
+  int j;
+
   char *tformat = NULL;
-  Header header;
-  Matrix matrix;
-  Header theader;
-  FTable ftable;
-
-  int j;
-  FILE *f = NULL;
-
-  /* open file for outuput */
-  if (append) {
-    f = fopen (filename, "a");
-  } else {
-    f = fopen (filename, "w");
-  }
-  if (f == (FILE *) NULL) {
-    gprint (GP_ERR, "can't open file for write : %s\n", filename);
-    return (FALSE);
-  }
-
-  if (!append) {
-    gfits_init_header (&header);
-    header.extend = TRUE;
-    gfits_create_header (&header);
-    gfits_create_matrix (&header, &matrix);
-  }
-
-  gfits_create_table_header (&theader, "BINTABLE", extname);
+
+  gfits_create_table_header (theader, "BINTABLE", extname);
 
   ALLOCATE (tformat, char, 2*Nvec);
@@ -71,21 +48,54 @@
   // this somehow
   for (j = 0; j < Nvec; j++) {
-    gfits_define_bintable_column (&theader, &tformat[2*j], vec[j][0].name, NULL, NULL, 1.0, 0.0);
+    gfits_define_bintable_column (theader, &tformat[2*j], vec[j][0].name, NULL, NULL, 1.0, 0.0);
   }
   free (tformat);
 
   // generate the output array that carries the data
-  gfits_create_table (&theader, &ftable);
+  gfits_create_table (theader, ftable);
 
   // add the vectors to the output array
   for (j = 0; j < Nvec; j++) {
     if (vec[j][0].type == OPIHI_FLT) {
-      gfits_set_bintable_column_reformat (&theader, &ftable, vec[j][0].name, "double", vec[j][0].elements.Flt, vec[j][0].Nelements);
+      gfits_set_bintable_column_reformat (theader, ftable, vec[j][0].name, "double", vec[j][0].elements.Flt, vec[j][0].Nelements);
     } else {
-      gfits_set_bintable_column_reformat (&theader, &ftable, vec[j][0].name, "int", vec[j][0].elements.Int, vec[j][0].Nelements);
-    }
-  }
+      gfits_set_bintable_column_reformat (theader, ftable, vec[j][0].name, "int", vec[j][0].elements.Int, vec[j][0].Nelements);
+    }
+  }
+  return (TRUE);
+
+ escape:
+  if (tformat) free (tformat);
+  return (FALSE);
+}
+  
+// write a set of vectors to a FITS file (vectors names become fits column names)
+int WriteVectorTableFITS (char *filename, char *extname, Vector **vec, int Nvec, int append, char *format) {
+  
+  Header header;
+  Matrix matrix;
+  Header theader;
+  FTable ftable;
+
+  FILE *f = NULL;
+
+  /* open file for outuput */
+  if (append) {
+    f = fopen (filename, "a");
+  } else {
+    f = fopen (filename, "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write : %s\n", filename);
+    return (FALSE);
+  }
+
+  if (!WriteVectorTable (&theader, &ftable, extname, vec, Nvec, format)) goto escape;
 
   if (!append) {
+    gfits_init_header (&header);
+    header.extend = TRUE;
+    gfits_create_header (&header);
+    gfits_create_matrix (&header, &matrix);
     gfits_fwrite_header  (f, &header);
     gfits_fwrite_matrix  (f, &matrix);
@@ -111,5 +121,4 @@
   gfits_free_table (&ftable);
 
-  if (tformat) free (tformat);
   fclose (f);
   fflush (f);
