Index: trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 40622)
+++ trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 40623)
@@ -178,4 +178,5 @@
 $(SRC)/vroll.$(ARCH).o		   \
 $(SRC)/vshift.$(ARCH).o		   \
+$(SRC)/vmedfilt.$(ARCH).o	   \
 $(SRC)/vsmooth.$(ARCH).o	   \
 $(SRC)/vstats.$(ARCH).o		   \
Index: trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/init.c	(revision 40622)
+++ trunk/Ohana/src/opihi/cmd.data/init.c	(revision 40623)
@@ -160,4 +160,5 @@
 int vload            PROTO((int, char **));
 int vlist            PROTO((int, char **));
+int vmedfilt         PROTO((int, char **));
 int vzload           PROTO((int, char **));
 int vstats           PROTO((int, char **));
@@ -349,4 +350,5 @@
   {1, "vhistogram",   histogram,        "generate histogram from vector"},
   {1, "vlist",        vlist,            "append values to a vector from command line"},
+  {1, "vmedfilt",     vmedfilt,         "median filter for a vector"},
   {1, "vload",        vload,            "load vectors as overlay on image display"},
   {1, "vmaxwell",     vmaxwell,         "fit a Maxwellian to a vector"},
Index: trunk/Ohana/src/opihi/cmd.data/medimage_commands.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/medimage_commands.c	(revision 40622)
+++ trunk/Ohana/src/opihi/cmd.data/medimage_commands.c	(revision 40623)
@@ -149,6 +149,12 @@
 int medimage_delete (int argc, char **argv) {
 
-  int status;
+  int N, status;
   MedImageType *medimage;
+
+  int QUIET = FALSE;
+  if ((N = get_argument (argc, argv, "-q"))) {
+    QUIET = TRUE;
+    remove_argument (N, &argc, argv);
+  }
 
   if (argc != 2) {
@@ -159,4 +165,5 @@
   medimage = FindMedImage (argv[1]);
   if (medimage == NULL) {
+    if (QUIET) return TRUE;
     gprint (GP_ERR, "medimage %s not found\n", argv[1]);
     return FALSE;
Index: trunk/Ohana/src/opihi/cmd.data/threshold.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 40622)
+++ trunk/Ohana/src/opihi/cmd.data/threshold.c	(revision 40623)
@@ -3,9 +3,8 @@
 int threshold (int argc, char **argv) {
   
-  int N, QUIET;
-  double value;
+  int N;
   Vector *vecx, *vecy;
 
-  QUIET = FALSE;
+  int QUIET = FALSE;
   if ((N = get_argument (argc, argv, "-q"))) {
     QUIET = TRUE;
@@ -13,8 +12,26 @@
   }
 
+  int REVERSE = FALSE;
+  if ((N = get_argument (argc, argv, "-r"))) {
+    REVERSE = TRUE;
+    remove_argument (N, &argc, argv);
+  }
+
+  int BinMin = -1;
+  int BinMax = -1;
+  if ((N = get_argument (argc, argv, "-range"))) {
+    remove_argument (N, &argc, argv);
+    BinMin = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+    BinMax = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if (argc != 4) {
-    gprint (GP_ERR, "USAGE: threshold <x> <y> (value)\n");
+    gprint (GP_ERR, "USAGE: threshold <x> <y> (value) [-q] [-r] [-range BinMin BinMax] \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");
+    gprint (GP_ERR, "  -q : quiet mode\n");
+    gprint (GP_ERR, "  -r : reverse (find downward transition)\n");
     return (FALSE);
   }
@@ -22,40 +39,70 @@
   if ((vecx = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
   if ((vecy = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if (vecx[0].Nelements != vecy[0].Nelements) return FALSE;
+  int Nelements = vecx[0].Nelements;
   
-  value = atof (argv[3]);
+  double 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);
 
+  // BinMin = -1 -> range not provide
+  if (BinMin == -1) {
+    BinMin = 0;
+    BinMax = Nelements - 1;
+  } else {
+    if ((BinMin >= vecx[0].Nelements) || (BinMin < -1*vecx[0].Nelements)) {
+      gprint (GP_ERR, "vector subscript out of range\n"); 
+      return FALSE;
+    }
+    if (BinMin < 0) BinMin += vecx[0].Nelements;
+    if ((BinMax >= vecx[0].Nelements) || (BinMax < -1*vecx[0].Nelements)) {
+      gprint (GP_ERR, "vector subscript out of range\n"); 
+      return FALSE;
+    }
+    if (BinMax < 0) BinMax += vecx[0].Nelements;
+  }
+
   // 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);
+  int Nlo = BinMin;
+  int Nhi = BinMax;
+
+  if (!REVERSE) {
+    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[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;
+      }
+    }
+  } else {
+    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;
+      }
     }
   }
Index: trunk/Ohana/src/opihi/cmd.data/vmedfilt.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/vmedfilt.c	(revision 40623)
+++ trunk/Ohana/src/opihi/cmd.data/vmedfilt.c	(revision 40623)
@@ -0,0 +1,64 @@
+# include "data.h"
+
+int vmedfilt (int argc, char **argv) {
+  
+  Vector *in, *out;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: vmedfilt (input) (output) Npts\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+  if ((out = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  // smoothing window is 2Ns + 1 wide (require an odd number)
+  int Ns = atoi (argv[3]);
+  int Np = 2*Ns + 1;
+
+  int Nx = in[0].Nelements;
+
+  ResetVector (out, OPIHI_FLT, Nx);
+
+  // storage for the current sample
+  ALLOCATE_PTR (sample, opihi_flt, Np);
+
+  int isFloat = (in[0].type == OPIHI_FLT);
+  opihi_flt *vf = in[0].elements.Flt;
+  opihi_int *vi = in[0].elements.Int;
+
+  opihi_flt *tf = out[0].elements.Flt;
+
+  for (int i = 0; i < Nx; i++) {
+
+    int s = 0;
+    for (int n = -Ns; n <= Ns; n++) {
+      if (i+n < 0) continue;
+      if (i+n >= Nx) continue;
+      opihi_flt value = isFloat ? vf[i+n] : vi[i+n];
+      if (isnan(value)) continue;
+
+      sample[s] = value;
+      s ++;
+    }
+    dsort (sample, s);
+
+    if (s == 0) { tf[i] = NAN; continue; }
+
+    int s2 = s / 2;
+    if (s % 2) {
+      // s == 1, s2 = 0, s == 3, s2 = 1; s == 5, s2 = 2, ..
+      tf[i] = sample[s2]; 
+    } else {
+      // s == 2, s2 = 1, s2-1 = 0; s == 4, s2 = 2, s2-1 = 1; s == 6, s2 = 3, s2-1 = 2..
+      tf[i] = 0.5*(sample[s2] + sample[s2-1]); 
+    }
+  }
+
+  free (sample);
+  return (TRUE);
+}
+
+// we are running the sort for every sample vector; we could probably do this more efficiently, see:
+// https://nomis80.org/ctmf.pdf
+// https://arxiv.org/pdf/1406.1717.pdf
