Index: trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 33662)
@@ -123,4 +123,5 @@
 $(SRC)/swapbytes.$(ARCH).o	   \
 $(SRC)/textline.$(ARCH).o	   \
+$(SRC)/threshold.$(ARCH).o		\
 $(SRC)/tv.$(ARCH).o		   \
 $(SRC)/tvchannel.$(ARCH).o	   \
Index: trunk/Ohana/src/opihi/cmd.data/cumulative.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/cumulative.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/cumulative.c	(revision 33662)
@@ -23,4 +23,6 @@
     opihi_flt *Vi = ivec[0].elements.Flt;
     *Vo = *Vi;
+    Vi++; 
+    Vo++;
     for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
       *Vo = Vo[-1] + *Vi;
@@ -29,4 +31,6 @@
     opihi_int *Vi = ivec[0].elements.Int;
     *Vo = *Vi;
+    Vi++;
+    Vo++;
     for (i = 1; i < ivec[0].Nelements; i++, Vi++, Vo++) {
       *Vo = Vo[-1] + *Vi;
Index: trunk/Ohana/src/opihi/cmd.data/cursor.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/cursor.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/cursor.c	(revision 33662)
@@ -4,5 +4,5 @@
 
   char string[20], key[20], *name;
-  int i, N, kapa;
+  int i, N, kapa, VERBOSE;
   double X, Y, R, D, Z;
   void *oldsignal;
@@ -13,4 +13,9 @@
   // if ((N = get_argument (argc, argv, "-g"))) {
   // if ((N = get_argument (argc, argv, "-i"))) {
+
+  VERBOSE = TRUE;
+  if ((N = get_argument (argc, argv, "-a"))) {
+    VERBOSE = FALSE;
+  }
 
   name = NULL;
@@ -53,5 +58,5 @@
     set_str_variable ("KEY", key);
     
-    gprint (GP_LOG, "%s %f %f %f %f %f\n", key, X, Y, Z, R, D);
+    if (VERBOSE) gprint (GP_LOG, "%s %f %f %f %f %f\n", key, X, Y, Z, R, D);
 
     if (!strcasecmp (key, "Q")) break;
Index: trunk/Ohana/src/opihi/cmd.data/cut.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/cut.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/cut.c	(revision 33662)
@@ -48,6 +48,6 @@
   case 'X':
     /* create output vectors */
-    ResetVector (xvec, OPIHI_FLT, MAX (nx, 1));
-    ResetVector (yvec, OPIHI_FLT, MAX (nx, 1));
+    ResetVector (xvec, OPIHI_FLT, nx);
+    ResetVector (yvec, OPIHI_FLT, nx);
     bzero (yvec[0].elements.Flt, nx*sizeof(opihi_flt));
     for (i = 0; i < nx; i++) {
@@ -80,6 +80,6 @@
   case 'y':
   case 'Y':
-    ResetVector (xvec, OPIHI_FLT, MAX (ny, 1));
-    ResetVector (yvec, OPIHI_FLT, MAX (ny, 1));
+    ResetVector (xvec, OPIHI_FLT, ny);
+    ResetVector (yvec, OPIHI_FLT, ny);
     bzero (yvec[0].elements.Flt, ny*sizeof(opihi_flt));
     for (i = 0; i < ny; i++) {
Index: trunk/Ohana/src/opihi/cmd.data/imcut.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/imcut.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/imcut.c	(revision 33662)
@@ -37,6 +37,6 @@
   dY = dY / L;
 
-  ResetVector (xvec, OPIHI_FLT, MAX (L, 1));
-  ResetVector (yvec, OPIHI_FLT, MAX (L, 1));
+  ResetVector (xvec, OPIHI_FLT, L);
+  ResetVector (yvec, OPIHI_FLT, L);
 
   V = (float *)buf[0].matrix.buffer;
Index: trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/init.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/init.c	(revision 33662)
@@ -61,4 +61,5 @@
 int header           PROTO((int, char **));
 int list_vectors     PROTO((int, char **));
+int vtype            PROTO((int, char **));
 int load             PROTO((int, char **));
 int lookup           PROTO((int, char **));
@@ -111,4 +112,5 @@
 int swapbytes        PROTO((int, char **));
 int textline         PROTO((int, char **));
+int threshold        PROTO((int, char **));
 int tv               PROTO((int, char **));
 int tvchannel        PROTO((int, char **));
@@ -138,4 +140,8 @@
 int zplot            PROTO((int, char **));
 int zcplot            PROTO((int, char **));
+
+// ???
+// int mtype            PROTO((int, char **));
+//  {1, "mtype",        mtype,            "return the type of the defined buffer"},
 
 static Command cmds[] = {  
@@ -257,4 +263,5 @@
   {1, "swapbytes",    swapbytes,        "byte swap thing"},
   {1, "textline",     textline,         "write text line on graph"},
+  {1, "threshold",    threshold,        "find (interpolate) location of transition"},
   {1, "tv",           tv,               "display an image on the Kii window"},
   {1, "tvchannel",    tvchannel,        "set the current tv channel"},
@@ -269,4 +276,5 @@
   {1, "vclip",        vclip,            "clip values in a vector to be within a range"},
   {1, "vectors",      list_vectors,     "list vectors"},
+  {1, "vtype",        vtype,            "return the vector type (FLT or INT)"},
   {1, "vgauss",       vgauss,           "fit a Gaussian to a vector"},
   {1, "vellipse",     vellipse,         "fit a Ellipse to a vector pair"},
Index: trunk/Ohana/src/opihi/cmd.data/line.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/line.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/line.c	(revision 33662)
@@ -3,7 +3,14 @@
 int line (int argc, char **argv) {
   
-  int kapa;
+  int kapa, N;
   Graphdata graphmode;
   float x[2], y[2];
+
+  /* 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 (!style_args (&graphmode, &argc, argv, &kapa)) return FALSE;
@@ -18,4 +25,11 @@
   y[1] = atof(argv[5]);
 
+  if (FracPositions) {
+    x[0] =  x[0] * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y[0] =  y[0] * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+    x[1] =  x[1] * (graphmode.xmax - graphmode.xmin) + graphmode.xmin;
+    y[1] =  y[1] * (graphmode.ymax - graphmode.ymin) + graphmode.ymin;
+  }    
+
   /* set point style and errorbar mode (these are NOT sticky) */
   graphmode.style = 0;
Index: trunk/Ohana/src/opihi/cmd.data/list_buffers.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/list_buffers.c	(revision 33662)
@@ -9,2 +9,43 @@
 
 }
+
+# if (0) 
+int mtype (int argc, char **argv) {
+
+  Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: mtype (buffer) [-var out]\n");
+    return (FALSE);
+  }
+
+  Buffer *buf = SelectBuffer (argv[1], OLDBUFFER, FALSE);
+  if (!buf) {
+    gprint (GP_ERR, "unknown buffer %s\n", argv[1]);
+    free (Variable);
+    return FALSE;
+  }
+
+  if (buf->type == OPIHI_FLT) {
+    if (Variable) {
+      set_str_variable (Variable, "FLT");
+    } else {
+      gprint (GP_LOG, "%s : FLT\n", argv[1]);
+    }
+  } else {
+    if (Variable) {
+      set_str_variable (Variable, "INT");
+    } else {
+      gprint (GP_LOG, "%s : INT\n", argv[1]);
+    }
+  }
+  if (Variable) free (Variable);
+
+  return (TRUE);
+}
+# endif
Index: trunk/Ohana/src/opihi/cmd.data/list_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/list_vectors.c	(revision 33662)
@@ -7,2 +7,43 @@
 }
 
+int vtype (int argc, char **argv) {
+
+  int N;
+
+  char *Variable = (char *) NULL;
+  if ((N = get_argument (argc, argv, "-var"))) {
+    remove_argument (N, &argc, argv);
+    Variable = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: vtype (vector) [-var out]\n");
+    return (FALSE);
+  }
+
+  Vector *vec = SelectVector (argv[1], OLDVECTOR, FALSE);
+  if (!vec) {
+    gprint (GP_ERR, "unknown vector %s\n", argv[1]);
+    free (Variable);
+    return FALSE;
+  }
+
+  if (vec->type == OPIHI_FLT) {
+    if (Variable) {
+      set_str_variable (Variable, "FLT");
+    } else {
+      gprint (GP_LOG, "%s : FLT\n", argv[1]);
+    }
+  } else {
+    if (Variable) {
+      set_str_variable (Variable, "INT");
+    } else {
+      gprint (GP_LOG, "%s : INT\n", argv[1]);
+    }
+  }
+  if (Variable) free (Variable);
+
+  return (TRUE);
+}
+
Index: trunk/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 33662)
@@ -21,19 +21,23 @@
 }
 
+// vector types
+enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME};
+
 int read_vectors (int argc, char **argv) {
   
-    int i, j, Nskip, Nvec, *col, done, status, IsCSV, VERBOSE;
-  int Nbytes, nbytes, Nstart, NELEM, N, nread;
+  int TimeFormat;
+  time_t TimeReference;
+  int i, j, Nskip, Narg, Nvec, *col, IsCSV, VERBOSE;
+  int Nbytes, Nstart, NELEM, Nelem, nread, *coltype;
   char *colstr, *c0, *c1, *buffer, *extname;
-  double value;
   Vector **vec;
 
   /* auto-sense table type */
-  if ((N = get_argument (argc, argv, "-fits"))) {
-    remove_argument (N, &argc, argv);
-    extname = strcreate (argv[N]);
+  if ((Narg = get_argument (argc, argv, "-fits"))) {
+    remove_argument (Narg, &argc, argv);
+    extname = strcreate (argv[Narg]);
     if (extname == (char *) NULL) return (FALSE);
-    remove_argument (N, &argc, argv);
-    status = read_table_vectors (argc, argv, extname);
+    remove_argument (Narg, &argc, argv);
+    int status = read_table_vectors (argc, argv, extname);
     free (extname);
     return (status);
@@ -41,19 +45,19 @@
 
   Nskip = 0;
-  if ((N = get_argument (argc, argv, "-skip"))) {
-    remove_argument (N, &argc, argv);
-    Nskip = atof (argv[N]);
-    remove_argument (N, &argc, argv);
+  if ((Narg = get_argument (argc, argv, "-skip"))) {
+    remove_argument (Narg, &argc, argv);
+    Nskip = atof (argv[Narg]);
+    remove_argument (Narg, &argc, argv);
   }
 
   IsCSV = FALSE;
-  if ((N = get_argument (argc, argv, "-csv"))) {
-    remove_argument (N, &argc, argv);
+  if ((Narg = get_argument (argc, argv, "-csv"))) {
+    remove_argument (Narg, &argc, argv);
     IsCSV = TRUE;
   }
 
   VERBOSE = FALSE;
-  if ((N = get_argument (argc, argv, "-v"))) {
-    remove_argument (N, &argc, argv);
+  if ((Narg = get_argument (argc, argv, "-v"))) {
+    remove_argument (Narg, &argc, argv);
     VERBOSE = TRUE;
   }
@@ -65,4 +69,7 @@
   /* read name N name N  */
 
+  // do this only optionally?
+  GetTimeFormat (&TimeReference, &TimeFormat);
+
   if (f == (FILE *) NULL) {
     gprint (GP_ERR, "no open file for read\n");
@@ -71,14 +78,31 @@
   fseeko (f, 0LL, SEEK_SET);
 
-//  if (IsCSV) {
-//    status = read_vectors_csv (argc, argv, Nskip, f);
-//    return status;
-//  }
-
   Nvec = (argc - 1) / 2;
   ALLOCATE (vec, Vector *, Nvec);
   ALLOCATE (col, int, Nvec);
+  ALLOCATE (coltype, int, Nvec);
 
   for (i = 0; i < Nvec; i++) {
+
+    // interpret the column names including type flags
+    // XXX review the grammar before releasing this : is foo:type best, or is something else needed?
+    // Note the conflict wrt list entries
+    // the name may be of the form foo:type, where type may be one of : int, float, time
+    
+    coltype[i] = COLTYPE_FLT;
+    char *colname = argv[2*i + 1];
+    char *ptr = strchr (colname, ':');
+    if (ptr) {
+      // split out colname and type
+      *ptr = 0;
+      ptr ++;
+      if (!ptr) goto bad_colname;
+      coltype[i] = COLTYPE_NONE;
+      if (!strcasecmp(ptr, "float")) { coltype[i] = COLTYPE_FLT; }
+      if (!strcasecmp(ptr, "int"))   { coltype[i] = COLTYPE_INT; }
+      if (!strcasecmp(ptr, "time"))  { coltype[i] = COLTYPE_TIME; }
+      if (!coltype[i]) goto bad_colname;
+    }
+
     if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
       gprint (GP_ERR, "USAGE: read name N name N ...\n");
@@ -87,5 +111,5 @@
       return (FALSE);    
     }
-    // XXX we could allow flags (eg, N.i) to specify INT vs FLT types vectors...
+
     colstr = argv[2*i+2];
     for (j = 0; j < strlen (colstr); j++) {
@@ -119,5 +143,10 @@
   NELEM = 1000;
   for (i = 0; i < Nvec; i++) {
-    ResetVector (vec[i], OPIHI_FLT, NELEM);
+    if (coltype[i] == COLTYPE_INT) {
+      ResetVector (vec[i], OPIHI_INT, NELEM);
+    } else {
+      // note that COLTYPE_TIME is a type of float
+      ResetVector (vec[i], OPIHI_FLT, NELEM);
+    }
   }
   
@@ -128,8 +157,11 @@
   }
 
-  Nstart = 0;
-  N = 0;
-  done = FALSE;
-  while (!done) {
+  // we have a working buffer read from the file. we parse the lines in the working buffer
+  // until we reach the last chunk without an EOL char.  at that point, we shift the start
+  // of the last (partial) line to the start of the buffer and re-fill.
+
+  Nstart = 0; // location of the last valid byte in the buffer (start filling here)
+  Nelem = 0; // number of valid rows read (vector elements)
+  while (TRUE) {
     Nbytes = 0x10000 - Nstart;
     bzero (&buffer[Nstart], Nbytes);
@@ -139,61 +171,83 @@
       break;
     }
-    if (nread == 0) break;
-    nbytes = nread + Nstart;
+    if (nread == 0) break; // end of the file
+    // nbytes = nread + Nstart;
     
-    status = TRUE;
-    c0 = buffer; 
-    while (status) {
-      c1 = strchr (c0, '\n');
+    int bufferStatus = TRUE; 
+    c0 = buffer; // c0 always marks the start of a line
+    while (bufferStatus) {
+      c1 = strchr (c0, '\n'); // find the end of this current line
       if (c1 == (char *) NULL) {
 	Nstart = strlen (c0);
 	memmove (buffer, c0, Nstart);
-	status = FALSE;
-      } else {
-	*c1 = 0;
-      }      
-      if ((*c0 != '#') && (*c0 != '!')) {
-	for (i = 0; (i < Nvec) && status; i++) {
+	bufferStatus = FALSE;
+	continue;
+      }
+      *c1 = 0; // mark the end of the line 
+
+      if (*c0 == '#') {	c0 = c1 + 1; continue; }
+      if (*c0 == '!') {	c0 = c1 + 1; continue; }
+
+      // parse the vectors in this line.  this code is a bit inefficient: each column
+      // requires a separate pass through the line.
+
+      int lineStatus = TRUE;
+      for (i = 0; i < Nvec; i++) {
+	int ivalue;
+	double dvalue;
+	time_t tvalue;
+	int readStatus = FALSE;
+	// need to make the if cases for coltype[i]
+	switch (coltype[i]) {
+	  case COLTYPE_INT:
+	    readStatus = IsCSV ? iparse_csv (&ivalue, col[i], c0) : iparse (&ivalue, col[i], c0);
+	    vec[i][0].elements.Int[Nelem] = readStatus ? ivalue : 0;
+	    break;
+	  case COLTYPE_FLT:
+	    readStatus = IsCSV ? dparse_csv (&dvalue, col[i], c0) : dparse (&dvalue, col[i], c0);
+	    vec[i][0].elements.Flt[Nelem] = readStatus ? dvalue : NAN;
+	    break;
+	  case COLTYPE_TIME:
+	    readStatus = IsCSV ? tparse_csv (&tvalue, col[i], c0) : tparse (&tvalue, col[i], c0);
+	    dvalue = TimeValue (tvalue, TimeReference, TimeFormat);
+	    vec[i][0].elements.Flt[Nelem] = readStatus ? dvalue : NAN;
+	    break;
+	}
+	if (!readStatus && VERBOSE) {
 	  if (IsCSV) {
-	    status = dparse_csv (&value, col[i], c0);
+	    gprint (GP_ERR, "suspect field: %d (%s) in %s\n", col[i], argv[2*i+2], c0);
 	  } else {
-	    status = dparse (&value, col[i], c0);
+	    gprint (GP_ERR, "suspect field: %d in %s\n", col[i], c0);
 	  }
-	  if (status) {
-	      vec[i][0].elements.Flt[N] = value;
-	  } else {
-	      vec[i][0].elements.Flt[N] = NAN;
-	      if (VERBOSE) {
-		  if (IsCSV) {
-		      gprint (GP_ERR, "suspect field: %d (%s) in %s\n", col[i], argv[2*i+2], c0);
-		  } else {
-		      gprint (GP_ERR, "suspect field: %d in %s\n", col[i], c0);
-		  }
-	      }
-	  }
-	}
-	if (status) {
-	    N++;
-	} else {
-	    if (VERBOSE && FALSE) {
-		char temp[32];
-		strncpy (temp, c0, 32);
-		temp[31] = 0;
-		gprint (GP_ERR, "skip line %s\n\n", temp);
-	    }
-	}
-      }
-      c0 = c1 + 1;
-      if (N == NELEM) {
+	}
+	lineStatus &= readStatus;
+      }
+      if (!lineStatus && VERBOSE) {
+	char temp[32];
+	strncpy (temp, c0, 32);
+	temp[31] = 0;
+	gprint (GP_ERR, "skip line %s\n\n", temp);
+      }
+      Nelem ++;
+      if (Nelem == NELEM) {
 	NELEM += 1000;
 	for (i = 0; i < Nvec; i++) {
-	  REALLOCATE (vec[i][0].elements.Flt, opihi_flt, NELEM);
-	}
-      }
+	  if (coltype[i] == COLTYPE_INT) {
+	    REALLOCATE (vec[i][0].elements.Int, opihi_int, NELEM);
+	  } else {
+	    REALLOCATE (vec[i][0].elements.Flt, opihi_flt, NELEM);
+	  }
+	}
+      }
+      c0 = c1 + 1;
     }
   }
   for (i = 0; i < Nvec; i++) {
-    REALLOCATE (vec[i][0].elements.Flt, opihi_flt, MAX (N,1));
-    vec[i][0].Nelements = N;
+    if (coltype[i] == COLTYPE_INT) {
+      REALLOCATE (vec[i][0].elements.Int, opihi_int, MAX (Nelem,1));
+    } else {
+      REALLOCATE (vec[i][0].elements.Flt, opihi_flt, MAX (Nelem,1));
+    }
+    vec[i][0].Nelements = Nelem;
   }
   
@@ -242,4 +296,12 @@
     Nextend = atoi (extname);
   }
+
+  // XXX ReadAll needs: deal with Extnum vs Extname, save vectors, etc
+  // ReadAll = FALSE;
+  // if ((N = get_argument (argc, argv, "-all"))) {
+  //   remove_argument (N, &argc, argv);
+  //   ReadAll = atoi (extname);
+  //   if (argc != 1) ESCAPE ("-all option cannot be mixed with selected field");
+  // }
 
   if (argc < 2) ESCAPE ("USAGE: read -fits extension [-extnum] [-keyword key] name name ...");
@@ -314,9 +376,4 @@
   for (i = 1; i < argc; i++) {
     void   *data;
-    char   *Pc;
-    short  *Ps;
-    int    *Pi;
-    float  *Pf;
-    double *Pd;
     int Nval;
     char name[80];
@@ -345,44 +402,52 @@
 	sprintf (name, "%s:%d", argv[i], j);
       if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
-      ResetVector (vec[j], vecType, MAX (Ny,1));
-    }
-
+      ResetVector (vec[j], vecType, Ny);
+    }
+
+    if (!strcmp (type, "char")) {
+      char *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "short")) {
+      short *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "int")) {
+      int *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "int64_t")) {
+      int64_t *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Int[j] = *Ptr;
+	}
+      }
+    }
+    if (!strcmp (type, "float")) {
+      float *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Flt[j] = *Ptr;
+	}
+      }
+    }
     if (!strcmp (type, "double")) {
-      Pd = (double *) data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Pd++) {
-	  vec[k][0].elements.Flt[j] = *Pd;
-	}
-      }
-    }
-    if (!strcmp (type, "float")) {
-      Pf = (float *) data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Pf++) {
-	  vec[k][0].elements.Flt[j] = *Pf;
-	}
-      }
-    }
-    if (!strcmp (type, "int")) {
-      Pi = (int *) data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Pi++) {
-	  vec[k][0].elements.Int[j] = *Pi;
-	}
-      }
-    }
-    if (!strcmp (type, "short")) {
-      Ps = (short *) data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ps++) {
-	  vec[k][0].elements.Int[j] = *Ps;
-	}
-      }
-    }
-    if (!strcmp (type, "char")) {
-      Pc = (char *) data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Pc++) {
-	  vec[k][0].elements.Int[j] = *Pc;
+      double *Ptr = data;
+      for (j = 0; j < Ny; j++) {
+	for (k = 0; k < Nval; k++, Ptr++) {
+	  vec[k][0].elements.Flt[j] = *Ptr;
 	}
       }
@@ -396,104 +461,2 @@
   return (TRUE);
 }
-
-# if (0)
-int read_vectors_csv (int argc, char **argv, int Nskip, FILE *f) {
-  
-  int i, j, Nvec, *col, done, status;
-  int Nbytes, nbytes, Nstart, NELEM, N, nread;
-  char *colstr, *c0, *c1, *buffer, *extname;
-  double value;
-  Vector **vec;
-
-  Nvec = (argc - 1) / 2;
-  ALLOCATE (vec, Vector *, Nvec);
-  ALLOCATE (col, int, Nvec);
-
-  for (i = 0; i < Nvec; i++) {
-    if ((vec[i] = SelectVector (argv[2*i + 1], ANYVECTOR, TRUE)) == NULL) {
-      gprint (GP_ERR, "USAGE: read name N name N ...\n");
-      free (vec);
-      free (col);
-      return (FALSE);    
-    }
-    // XXX we could allow flags (eg, N.i) to specify INT vs FLT types vectors...
-    colstr = argv[2*i+2];
-    for (j = 0; j < strlen (colstr); j++) {
-      if (!isdigit(colstr[j])) {
-	gprint (GP_ERR, "USAGE: read name N name N ...\n");
-	free (vec);
-	free (col);
-	return (FALSE);    
-      }
-    }
-    col[i] = atof (colstr);
-  }
-
-  // currently, all read vectors are forced to be type FLT
-  NELEM = 1000;
-  for (i = 0; i < Nvec; i++) {
-    ResetVector (vec[i], OPIHI_FLT, NELEM);
-  }
-  
-  ALLOCATE (buffer, char, 0x10001);
-  bzero (buffer, 0x10001);
-  for (i = 0; i < Nskip; i++) {
-    scan_line (f, buffer);
-  }
-
-  Nstart = 0;
-  N = 0;
-  done = FALSE;
-  while (!done) {
-    Nbytes = 0x10000 - Nstart;
-    bzero (&buffer[Nstart], Nbytes);
-    nread = fread (&buffer[Nstart], 1, Nbytes, f);
-    if (ferror (f)) {
-      perror ("error reading data file");
-      break;
-    }
-    if (nread == 0) break;
-    nbytes = nread + Nstart;
-    
-    status = TRUE;
-    c0 = buffer; 
-    while (status) {
-      c1 = strchr (c0, '\n');
-      if (c1 == (char *) NULL) {
-	Nstart = strlen (c0);
-	memmove (buffer, c0, Nstart);
-	status = FALSE;
-      } else {
-	*c1 = 0;
-      }      
-      if ((*c0 != '#') && (*c0 != '!')) {
-	for (i = 0; (i < Nvec) && status; i++) {
-	  status = dparse_csv (&value, col[i], c0);
-	  vec[i][0].elements.Flt[N] = value;
-	  if (!status) vec[i][0].elements.Flt[N] = NAN;
-	}
-	if (status) N++;
-      }
-      c0 = c1 + 1;
-      if (N == NELEM) {
-	NELEM += 1000;
-	for (i = 0; i < Nvec; i++) {
-	  REALLOCATE (vec[i][0].elements.Flt, opihi_flt, NELEM);
-	}
-      }
-	
-    }
-  }
-  for (i = 0; i < Nvec; i++) {
-    REALLOCATE (vec[i][0].elements.Flt, opihi_flt, MAX (N,1));
-    vec[i][0].Nelements = N;
-  }
-  
-  free (vec);
-  free (col);
-  free (buffer);
-  return (TRUE);
-
-}
-
-# endif
Index: trunk/Ohana/src/opihi/cmd.data/reindex.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/reindex.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/reindex.c	(revision 33662)
@@ -22,5 +22,5 @@
 
   // ovec matches ivec in type and xvec in size (xvec need not have all ivec elements, and may have duplicates
-  ResetVector (ovec, ivec->type, MAX (xvec[0].Nelements, 1));
+  ResetVector (ovec, ivec->type, xvec[0].Nelements);
 
   Nmax = ivec[0].Nelements - 1;
@@ -51,5 +51,5 @@
 
   // free up unused memory
-  ResetVector (ovec, ivec->type, MAX (Npts, 1));
+  ResetVector (ovec, ivec->type, Npts);
   return (TRUE);
 
Index: trunk/Ohana/src/opihi/cmd.data/select.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/select.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/select.c	(revision 33662)
@@ -28,7 +28,7 @@
 
   if ((in1->type == OPIHI_INT) && (in2->type == OPIHI_INT)) {
-    ResetVector (ovec, OPIHI_INT, MAX (tvec[0].Nelements, 1));
+    ResetVector (ovec, OPIHI_INT, tvec[0].Nelements);
   } else {
-    ResetVector (ovec, OPIHI_FLT, MAX (tvec[0].Nelements, 1));
+    ResetVector (ovec, OPIHI_FLT, tvec[0].Nelements);
   }
 
Index: trunk/Ohana/src/opihi/cmd.data/test/read.sh
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/test/read.sh	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/test/read.sh	(revision 33662)
@@ -0,0 +1,145 @@
+
+list tests
+ #test1
+ #test2
+ #test3
+ #test4
+ test5
+ test6
+end
+
+# basic read
+macro test1
+ 
+ $PASS = 1
+
+ data read.t1.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with int types
+macro test2
+ 
+ $PASS = 1
+
+ data read.t1.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with comments
+macro test3
+ 
+ $PASS = 1
+
+ data read.t2.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# basic read with comments & int types
+macro test4
+ 
+ $PASS = 1
+
+ data read.t2.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 3) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 7) set PASS = 0
+end
+
+# read with invalid entries
+macro test5
+ 
+ $PASS = 1
+
+ data read.t3.dat
+ read t1 1 t2 2 t3 3
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (not(isnan(t2[1]))) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (not(isnan(t3[2]))) set PASS = 0
+end
+
+# read with invalid entries & int types
+macro test6
+ 
+ $PASS = 1
+
+ data read.t3.dat
+ read t1:int 1 t2:int 2 t3:int 3
+
+ # note: no way to verify the mode of a vector
+ vectors
+
+ if (t1[0] != 1) set PASS = 0
+ if (t1[1] != 2) set PASS = 0
+ if (t1[2] != 4) set PASS = 0
+
+ if (t2[0] != 5) set PASS = 0
+ if (t2[1] != 0) set PASS = 0
+ if (t2[2] != 5) set PASS = 0
+
+ if (t3[0] != 8) set PASS = 0
+ if (t3[1] != 9) set PASS = 0
+ if (t3[2] != 0) set PASS = 0
+end
+
Index: trunk/Ohana/src/opihi/cmd.data/test/read.t1.dat
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/test/read.t1.dat	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/test/read.t1.dat	(revision 33662)
@@ -0,0 +1,3 @@
+1 5 8
+2 3 9
+4 5 7
Index: trunk/Ohana/src/opihi/cmd.data/test/read.t2.dat
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/test/read.t2.dat	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/test/read.t2.dat	(revision 33662)
@@ -0,0 +1,5 @@
+# comment line
+1 5 8
+2 3 9
+! comment line 2
+4 5 7
Index: trunk/Ohana/src/opihi/cmd.data/test/read.t3.dat
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/test/read.t3.dat	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/test/read.t3.dat	(revision 33662)
@@ -0,0 +1,3 @@
+1 5 8
+2 X 9
+4 5 Y
Index: trunk/Ohana/src/opihi/cmd.data/test/read.t4.dat
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/test/read.t4.dat	(revision 33662)
+++ trunk/Ohana/src/opihi/cmd.data/test/read.t4.dat	(revision 33662)
@@ -0,0 +1,7 @@
+1 5 8 2000/01/01,00:00:00
+2 3 9 2000/01/01,01:00:00
+4 5 7 2000/01/01,02:00:00
+4 5 7 now
+4 5 7 today
+4 5 7 2451544.750000j
+4 5 7 51544.500000J
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 33662)
@@ -0,0 +1,88 @@
+# include "data.h"
+
+int threshold (int argc, char **argv) {
+  
+  int N, QUIET;
+  double value;
+  Vector *vecx, *vecy;
+
+  QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: threshold <x> <y> (value)\n");
+    gprint (GP_ERR, "  find the x coordinate at which we pass the specified value\n");
+    gprint (GP_ERR, "  by default, y must be monotonically increasing\n");
+    return (FALSE);
+  }
+  
+  if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  
+  value = atof (argv[3]);
+  
+  // if (argc == 6) {
+  //   start = atof (argv[4]);
+  //   end   = atof (argv[5]);
+  // } else {
+  //   start = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[0] : vecx[0].elements.Int[0];
+  //   end   = (vecx[0].type == OPIHI_FLT) ? vecx[0].elements.Flt[vecx[0].Nelements - 1] : vecx[0].elements.Int[vecx[0].Nelements - 1];
+  // }
+
+  int isFltX = (vecx[0].type == OPIHI_FLT);
+  int isFltY = (vecy[0].type == OPIHI_FLT);
+
+  // use bisection to find the value
+  int Nlo, Nhi;
+  int Nelements = vecx[0].Nelements;
+
+  // find the last entry before start
+  Nlo = 0;
+  Nhi = Nelements - 1;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    double testval = isFltY ? vecy[0].elements.Flt[N] : vecy[0].elements.Int[N];
+    if (testval < value) {
+      Nlo = MAX(N, 0);
+    } else {
+      Nhi = MIN(N, Nelements - 1);
+    }
+  }
+  // v[Nlo] < value <= v[Nhi]
+  for (N = Nlo; N <= Nhi; N++) {
+    double testval = isFltY ? vecy[0].elements.Flt[N] : vecy[0].elements.Int[N];
+    if (testval > value) {
+      Nhi = N;
+      break;
+    }
+  }
+  // v[Nhi] is transition bin
+  
+  double x0, x1, y0, y1, Xvalue;
+  if (Nhi == 0) {
+    // interpolate to value:
+    y0 = isFltY ? vecy[0].elements.Flt[Nhi] : vecy[0].elements.Int[Nhi];
+    y1 = isFltY ? vecy[0].elements.Flt[Nhi+1]   : vecy[0].elements.Int[Nhi+1];
+    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;
+  } else {
+    // interpolate to value:
+    y0 = isFltY ? vecy[0].elements.Flt[Nhi-1] : vecy[0].elements.Int[Nhi-1];
+    y1 = isFltY ? vecy[0].elements.Flt[Nhi]   : vecy[0].elements.Int[Nhi];
+    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;
+  }
+
+  set_variable ("threshval", y1);
+  set_int_variable ("threshbin", Nhi);
+  set_variable ("threshold", Xvalue);
+
+  if (!QUIET) gprint (GP_LOG, "theshold %f (bin %d is %f)\n", Xvalue, Nhi, y1);
+
+  return (TRUE);
+}
Index: trunk/Ohana/src/opihi/cmd.data/write_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 32632)
+++ trunk/Ohana/src/opihi/cmd.data/write_vectors.c	(revision 33662)
@@ -7,5 +7,5 @@
   FILE *f;
   char **fmtlist, *fmttype;
-  char *p0, *p1, *p2, *format;
+  char *p0, *p1, *p2, *format, *FITS;
   Vector **vec;
 
@@ -15,4 +15,12 @@
     remove_argument (N, &argc, argv);
     format = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  /* option generate a FITS output table */
+  FITS = NULL;
+  if ((N = get_argument (argc, argv, "-fits"))) {
+    remove_argument (N, &argc, argv);
+    FITS = strcreate (argv[N]);
     remove_argument (N, &argc, argv);
   }
@@ -29,22 +37,9 @@
   }
 
-  /* open file for outuput */
-  if (append) {
-      f = fopen (argv[1], "a");
-  } else {
-      f = fopen (argv[1], "w");
-  }
-  if (f == (FILE *) NULL) {
-    gprint (GP_ERR, "can't open file for write\n");
-    return (FALSE);
-  }
-
   /* find number of output vectors */
   Nvec = (argc - 2);
   if (Nvec < 1) {
-      gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
-      fclose (f);
-  fflush (f);
-      return (FALSE);
+    gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
+    return (FALSE);
   }
   ALLOCATE (vec, Vector *, Nvec);
@@ -56,6 +51,4 @@
       gprint (GP_ERR, "USAGE: write (file) vector vector ...\n");
       free (vec);
-      fclose (f);
-  fflush (f);
       return (FALSE);    
     }
@@ -68,8 +61,23 @@
       gprint (GP_ERR, "error: vectors must all be the same size\n");
       free (vec);
-      fclose (f);
-  fflush (f);
       return (FALSE);    
     }
+  }
+
+  if (FITS) {
+    int status = WriteVectorTableFITS (argv[1], FITS, vec, Nvec, append, format);
+    free (vec);
+    return status;
+  }
+
+  /* open file for outuput */
+  if (append) {
+    f = fopen (argv[1], "a");
+  } else {
+    f = fopen (argv[1], "w");
+  }
+  if (f == (FILE *) NULL) {
+    gprint (GP_ERR, "can't open file for write\n");
+    return (FALSE);
   }
 
@@ -88,5 +96,5 @@
     fclose (f);
     free (vec);
-  fflush (f);
+    fflush (f);
     return (TRUE);
   }
@@ -111,5 +119,5 @@
       free (format);
       fclose (f);
-  fflush (f);
+      fflush (f);
       return (FALSE);
     }
