Index: trunk/Ohana/src/opihi/cmd.data/dot.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/dot.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/dot.c	(revision 33963)
@@ -3,9 +3,16 @@
 int dot (int argc, char **argv) {
   
-  int kapa;
+  int kapa, N;
   Graphdata graphmode;
   float x, y;
 
   if (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
+
+  /* FracPositions uses coordinates of 0-1 relative to axis range */
+  int FracPositions = FALSE;
+  if ((N = get_argument (argc, argv, "-frac"))) {
+    remove_argument (N, &argc, argv);
+    FracPositions = TRUE;
+  } 
 
   if (argc != 3) {
@@ -15,4 +22,9 @@
   x = atof(argv[1]);
   y = atof(argv[2]);
+
+  if (FracPositions) {
+    x =  x * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y =  y * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
 
   /* set point style and errorbar mode (these are NOT sticky) */
Index: trunk/Ohana/src/opihi/cmd.data/gridify.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/gridify.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/gridify.c	(revision 33963)
@@ -5,9 +5,11 @@
   int i, Nx, Ny, Xb, Yb, Normalize, N;
   float Xmin, Xmax, dX, Ymin, Ymax, dY, initValue;
-  float *buf, *val;
+  float *buf, *val, *cnt;
   int *Nval;
-  Buffer *bf;
   Vector *vx, *vy, *vz;
   opihi_flt *x, *y, *z;
+
+  Buffer *bf = NULL;
+  Buffer *ct = NULL;
 
   Normalize = TRUE;
@@ -15,4 +17,6 @@
     remove_argument (N, &argc, argv);
     Normalize = FALSE;
+    if ((ct = SelectBuffer (argv[N], ANYBUFFER, TRUE)) == NULL) return (FALSE);
+    remove_argument (N, &argc, argv);
   }
 
@@ -24,6 +28,28 @@
   }
 
-  if (argc != 11) {
-    gprint (GP_ERR, "USAGE: gridify x y z buffer Xmin Xmax dX Ymin Ymax dY\n");
+  Xmin = Xmax = dX = NAN;
+  if ((N = get_argument (argc, argv, "-x"))) {
+    remove_argument (N, &argc, argv);
+    Xmin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Xmax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    dX   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }    
+
+  Ymin = Ymax = dY = NAN;
+  if ((N = get_argument (argc, argv, "-y"))) {
+    remove_argument (N, &argc, argv);
+    Ymin = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    Ymax = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+    dY   = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }    
+
+  if (argc != 5) {
+    gprint (GP_ERR, "USAGE: gridify x y z buffer [-x Xmin Xmax dX] [-y Ymin Ymax dY] [-init-value value] [-raw]\n");
     return (FALSE);
   }
@@ -41,19 +67,25 @@
   REQUIRE_VECTOR_FLT (vz, FALSE); 
 
-  Xmin = atof (argv[5]);
-  Xmax = atof (argv[6]);
-  dX   = atof (argv[7]);
+  if (isnan(dX)) {
+    Xmin = 0;
+    Xmax = bf[0].matrix.Naxis[0];
+    dX = 1;
+  }
 
-  Ymin = atof (argv[8]);
-  Ymax = atof (argv[9]);
-  dY   = atof (argv[10]);
+  if (isnan(dY)) {
+    Ymin = 0;
+    Ymax = bf[0].matrix.Naxis[1];
+    dY = 1;
+  }
 
-  Nx = (Xmax - Xmin) / dX + 1;
-  Ny = (Ymax - Ymin) / dY + 1;
+  Nx = (Xmax - Xmin) / dX;
+  Ny = (Ymax - Ymin) / dY;
   
-  gfits_free_matrix (&bf[0].matrix);
-  gfits_free_header (&bf[0].header);
-  CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
-  strcpy (bf[0].file, "(empty)");
+  if ((Nx != bf[0].matrix.Naxis[0]) || (Ny != bf[0].matrix.Naxis[1])) {
+    gfits_free_matrix (&bf[0].matrix);
+    gfits_free_header (&bf[0].header);
+    CreateBuffer (bf, Nx, Ny, -32, 0.0, 1.0);
+    strcpy (bf[0].file, "(empty)");
+  }
 
   ALLOCATE (val, float, Nx*Ny);
@@ -68,4 +100,6 @@
     Xb = (*x - Xmin) / dX;
     Yb = (*y - Ymin) / dY;
+    if (Xb < 0) continue;
+    if (Yb < 0) continue;
     if (Xb >= Nx) continue;
     if (Yb >= Ny) continue;
@@ -74,15 +108,27 @@
   }
 
+  if (!Normalize) {
+    gfits_free_matrix (&ct[0].matrix);
+    gfits_free_header (&ct[0].header);
+    CreateBuffer (ct, Nx, Ny, -32, 0.0, 1.0);
+    strcpy (ct[0].file, "(empty)");
+
+    buf = (float *) bf[0].matrix.buffer;
+    cnt = (float *) ct[0].matrix.buffer;
+    for (i = 0; i < Nx*Ny; i++) {
+      if (Nval[i] == 0) continue;
+      buf[i] = val[i];
+      cnt[i] = Nval[i];
+    }
+    free (val);
+    free (Nval);
+    return TRUE;
+  }
+
   buf = (float *) bf[0].matrix.buffer;
   for (i = 0; i < Nx*Ny; i++) {
     buf[i] = initValue;
-    if (Normalize) {
-      if (Nval[i] == 0) {
-	continue;
-      }
-      buf[i] = val[i] / Nval[i];
-    } else {
-      buf[i] = val[i];
-    }
+    if (Nval[i] == 0) continue;
+    buf[i] = val[i] / Nval[i];
   }
 
Index: trunk/Ohana/src/opihi/cmd.data/match2d.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/match2d.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/match2d.c	(revision 33963)
@@ -167,4 +167,6 @@
   NMATCH = X1->Nelements;
   ResetVector (index, OPIHI_INT, NMATCH);
+
+  for (i = 0; i < index->Nelements; i++) { index->elements.Int[i] = -1; }
 
   ALLOCATE (N1, off_t, X1->Nelements);
Index: trunk/Ohana/src/opihi/cmd.data/threshold.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 33963)
@@ -69,5 +69,9 @@
     x0 = isFltX ? vecx[0].elements.Flt[Nhi] : vecy[0].elements.Int[Nhi];
     x1 = isFltX ? vecx[0].elements.Flt[Nhi+1]   : vecy[0].elements.Int[Nhi+1];
-    Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }    
   } else {
     // interpolate to value:
@@ -76,5 +80,9 @@
     x0 = isFltX ? vecx[0].elements.Flt[Nhi-1] : vecy[0].elements.Int[Nhi-1];
     x1 = isFltX ? vecx[0].elements.Flt[Nhi]   : vecy[0].elements.Int[Nhi];
-    Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    if (y0 == y1) {
+      Xvalue = 0.5*(x0 + x1);
+    } else {
+      Xvalue = (value - y0) * (x1 - x0) / (y1 - y0) + x0;
+    }
   }
 
Index: trunk/Ohana/src/opihi/cmd.data/vgroup.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/vgroup.c	(revision 33963)
@@ -1,3 +1,5 @@
 # include "data.h"
+
+enum {USE_MEDIAN, USE_COUNT, USE_SUM, USE_MEAN};
 
 int vgroup (int argc, char **argv) {
@@ -23,14 +25,42 @@
   // }
 
+  int mode = USE_MEDIAN;
+  if ((N = get_argument (argc, argv, "-sum"))) {
+    remove_argument (N, &argc, argv);
+    mode = USE_SUM;
+  }
+  if ((N = get_argument (argc, argv, "-mean"))) {
+    remove_argument (N, &argc, argv);
+    mode = USE_MEAN;
+  }
+
+  float binsize = NAN;
+  if ((N = get_argument (argc, argv, "-binsize"))) {
+    remove_argument (N, &argc, argv);
+    binsize = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if (argc != 5) {
-    gprint (GP_ERR, "USAGE: vbin <xin> <yin> <xout> <yout>\n");
+    gprint (GP_ERR, "USAGE: vgroup <xin> <yin> <xout> <yout>\n");
     gprint (GP_ERR, " group x,y values in bins defined by <xout>\n");
+    gprint (GP_ERR, " by default, yout has the median of the associated input values\n");
+    gprint (GP_ERR, " use -sum to add values in the bin\n");
+    gprint (GP_ERR, " use <yin> = histogram count matching values\n");
+    gprint (GP_ERR, " use -binsize to specify a fixed bin width (<xout> will define the bin center)\n");
     return (FALSE);
   }
 
   if ((xin  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
-  if ((yin  = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
   if ((xout = SelectVector (argv[3], OLDVECTOR, TRUE)) == NULL) return (FALSE);
   if ((yout = SelectVector (argv[4], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+  yin = NULL;
+
+  // this should conflict with the -sum option...
+  if (!strcmp(argv[2], "histogram")) {
+    mode = USE_COUNT;
+  } else {
+    if ((yin  = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  }
 
   // re-binning creates a float vector
@@ -41,6 +71,11 @@
 
   for (i = 0; i < xout[0].Nelements - 1; i++) {
-    xmin = xout[0].elements.Flt[i];
-    xmax = xout[0].elements.Flt[i+1];
+    if (isnan(binsize)) {
+      xmin = xout[0].elements.Flt[i];
+      xmax = xout[0].elements.Flt[i+1];
+    } else {
+      xmin = xout[0].elements.Flt[i] - 0.5*binsize;
+      xmax = xout[0].elements.Flt[i] + 0.5*binsize;
+    }
 
     N = 0;
@@ -48,21 +83,40 @@
       if (xin[0].elements.Flt[j] < xmin) continue;
       if (xin[0].elements.Flt[j] > xmax) continue;
-      values[N] = yin[0].elements.Flt[j];
+      if (yin) {
+	values[N] = yin[0].elements.Flt[j];
+      }
       N++;
     }
     
-    dsort (values, N);
-    if (N > 1) {
-      sum = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
-    } else {
-      sum = values[0];
+    sum = NAN;
+    switch (mode) {
+      case USE_MEDIAN:
+	dsort (values, N);
+	if (N > 1) {
+	  sum = (N % 2) ? values[(int)(0.5*N)] : 0.5*(values[N/2] + values[N/2 + 1]);
+	} else {
+	  sum = values[0];
+	}
+	break;
+
+      case USE_SUM:
+	sum = 0.0;
+	for (j = 0; j < N; j++) {
+	  sum += values[j];
+	}
+	break;
+	
+      case USE_COUNT:
+	sum = N;
+	break;
+
+      case USE_MEAN:
+	sum = 0.0;
+	for (j = 0; j < N; j++) {
+	  sum += values[j];
+	}
+	sum /= N;
+	break;
     }
-
-    // measure the stat for this bin
-    // sum = 0.0;
-    // for (j = 0; j < N; j++) {
-    //   sum += values[j];
-    // }
-    // sum /= N;
     yout[0].elements.Flt[i] = sum;
   }
Index: trunk/Ohana/src/opihi/cmd.data/vstats.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/vstats.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/vstats.c	(revision 33963)
@@ -144,5 +144,5 @@
       }      
     }
-    stdev = sqrt (var / N);
+    stdev = sqrt (var / (N - 1));
 
     Nmode = 0;
Index: trunk/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 33963)
@@ -21,7 +21,29 @@
   FITS = NULL;
   if ((N = get_argument (argc, argv, "-fits"))) {
+    if (format) {
+      gprint (GP_ERR, "ERROR: do not mix -fits and -format\n");
+      free (format);
+      return (FALSE);
+    }
     remove_argument (N, &argc, argv);
     FITS = strcreate (argv[N]);
     remove_argument (N, &argc, argv);
+  }
+
+  /* option generate a FITS output table */
+  int CSV = FALSE;
+  if ((N = get_argument (argc, argv, "-csv"))) {
+    if (format) {
+      gprint (GP_ERR, "ERROR: do not mix -csv and -format\n");
+      free (format);
+      return (FALSE);
+    }
+    if (FITS) {
+      gprint (GP_ERR, "ERROR: do not mix -csv and -fits\n");
+      free (FITS);
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    CSV = TRUE;
   }
 
@@ -30,4 +52,10 @@
     remove_argument (N, &argc, argv);
     append = TRUE;
+  }
+
+  int ADD_HEADER = FALSE;
+  if ((N = get_argument (argc, argv, "-header"))) {
+    remove_argument (N, &argc, argv);
+    ADD_HEADER = TRUE;
   }
 
@@ -83,11 +111,32 @@
 
   /* default output format */
+  if (ADD_HEADER) {
+    for (j = 0; j < Nvec; j++) {
+      if (CSV) {
+	fprintf (f, "%s,", vec[j][0].name);
+      } else {
+	if (j == 0) fprintf (f, "# ");
+	fprintf (f, "%s ", vec[j][0].name);
+      }
+    }
+    fprintf (f, "\n");
+  }
+
+  /* default output format */
   if (format == (char *) NULL) {
     for (i = 0; i < vec[0][0].Nelements; i++) {
       for (j = 0; j < Nvec; j++) {
 	if (vec[j][0].type == OPIHI_FLT) {
-	  fprintf (f, "%.12g ", vec[j][0].elements.Flt[i]);
+	  if (CSV) {
+	    fprintf (f, "%.12g,", vec[j][0].elements.Flt[i]);
+	  } else {
+	    fprintf (f, "%.12g ", vec[j][0].elements.Flt[i]);
+	  }
 	} else {
-	  fprintf (f, "%d ", vec[j][0].elements.Int[i]);
+	  if (CSV) {
+	    fprintf (f, "%d,", vec[j][0].elements.Int[i]);
+	  } else {
+	    fprintf (f, "%d ", vec[j][0].elements.Int[i]);
+	  }
 	}
       }
