Index: /branches/eam_branches/ipp-20150112/Ohana/src/kapa2/src/InterpretKeys.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/kapa2/src/InterpretKeys.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/kapa2/src/InterpretKeys.c	(revision 37869)
@@ -98,7 +98,6 @@
       Screen_to_Image (&X, &Y, (double)(event[0].x + 0.5), (double)(event[0].y + 0.5), &image[0].picture); \
       UpdateStatusBox (graphic, image, X, Y, 0.0, 1); \
+      Remap (graphic, image); \
       break;
-
-//    Remap (graphic, image); 
 
     // the number of entries here must match the value of NCHANNELS in contants.h
Index: /branches/eam_branches/ipp-20150112/Ohana/src/libfits/include/gfitsio.h
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/libfits/include/gfitsio.h	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/libfits/include/gfitsio.h	(revision 37869)
@@ -147,4 +147,5 @@
 
 int     gfits_init_matrix              PROTO((Matrix *matrix));
+off_t   gfits_npix_matrix              PROTO((Matrix *matrix));
 void   	gfits_add_matrix_value         PROTO((Matrix *matrix, off_t x, off_t y, double value)); 
 int    	gfits_convert_format           PROTO((Header *header, Matrix *matrix, int outBitpix, double outScale, double outZero, int inBlank, int outUnsign));
Index: /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_convert_format.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_convert_format.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_convert_format.c	(revision 37869)
@@ -90,7 +90,8 @@
 
 /*********************** fits convert format ***********************************/
+/* this function is safe in the number of axes (can even be 0) */
 int gfits_convert_format (Header *header, Matrix *matrix, int outBitpix, double outScale, double outZero, int inBlank, int outUnsign) {
 
-  unsigned long i, nbytes, Npixels;
+  off_t i, nbytes;
   int    inBitpix, inUnsign;
   double inScale, inZero;
@@ -116,6 +117,6 @@
   gfits_modify_alt (header, "UNSIGN", "%t", 1, outUnsign);
 
-  Npixels          = header[0].Naxis[0]*header[0].Naxis[1];
-  nbytes           = Npixels * (abs(outBitpix) / 8);
+  off_t Npixels = gfits_npix_matrix (matrix);
+  nbytes                = Npixels * (abs(outBitpix) / 8);
 
   A = inScale / outScale;
Index: /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_create_M.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_create_M.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/libfits/matrix/F_create_M.c	(revision 37869)
@@ -43,3 +43,15 @@
 }
 
-// XXX free buffer if non-null: need to double check for existing frees
+/*********************** return number of valid pixels *******************************/
+off_t gfits_npix_matrix (Matrix *matrix) {
+
+  if (!matrix->Naxes) return 0;
+
+  int i;
+  int Npix = 1;
+  for (i = 0; i < matrix->Naxes; i++) {
+    if (matrix->Naxis[i] == 0) break;
+    Npix *= matrix->Naxis[i];
+  }
+  return Npix;
+}
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/mcreate.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/mcreate.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/mcreate.c	(revision 37869)
@@ -3,20 +3,32 @@
 int mcreate (int argc, char **argv) {
   
-  int Nx, Ny;
+  int N;
   Buffer *buf;
 
+  int Nz = 0;
+  if ((N = get_argument (argc, argv, "-nz"))) {
+    remove_argument (N, &argc, argv);
+    Nz = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if (argc != 4) {
-    gprint (GP_ERR, "USAGE: mcreate <buffer> Nx Ny\n");
+    gprint (GP_ERR, "USAGE: mcreate <buffer> Nx Ny [-nz Nz]\n");
     return (FALSE);
   }
 
   if ((buf = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) return (FALSE);
-  Nx = atof (argv[2]);
-  Ny = atof (argv[3]);
+  int Nx = atof (argv[2]);
+  int Ny = atof (argv[3]);
 
   /* I should encapsulate this in a create_default_buffer */
   gfits_free_matrix (&buf[0].matrix);
   gfits_free_header (&buf[0].header);
-  CreateBuffer (buf, Nx, Ny, -32, 1.0, 0.0);
+
+  if (Nz) {
+    CreateBuffer3D (buf, Nx, Ny, Nz, -32, 1.0, 0.0);
+  } else {
+    CreateBuffer (buf, Nx, Ny, -32, 1.0, 0.0);
+  }
   return (TRUE);
 }
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/rd.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/rd.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/rd.c	(revision 37869)
@@ -4,11 +4,10 @@
 int rd (int argc, char **argv) {
   
-  int i, N, status, plane, Nplane, extend, Nextend, Nskip, JustHead, blank;
-  int ccdsel, done, Nword, IsCompressed;
-  char region[512], *ccdid, *filename;
-  FILE *f;
+  int i, N, Nskip, blank;
+  int done, Nword;
+  char region[512];
   Buffer *buf;
 
-  JustHead = FALSE;
+  int JustHead = FALSE;
   if ((N = get_argument (argc, argv, "-head"))) {
     remove_argument (N, &argc, argv);
@@ -16,5 +15,5 @@
   }
 
-  plane = 1;
+  int plane = -1;
   if ((N = get_argument (argc, argv, "-plane"))) {
     remove_argument (N, &argc, argv);
@@ -23,6 +22,6 @@
   }
 
-  extend = FALSE;
-  Nextend = -1;
+  int extend = FALSE;
+  int Nextend = -1;
   if ((N = get_argument (argc, argv, "-x"))) {
     remove_argument (N, &argc, argv);
@@ -32,6 +31,6 @@
   }
 
-  ccdsel = FALSE;
-  ccdid = (char *) NULL;
+  int ccdsel = FALSE;
+  char *ccdid = NULL;
   if ((N = get_argument (argc, argv, "-n"))) {
     remove_argument (N, &argc, argv);
@@ -52,5 +51,5 @@
 
   /* test if file exists */
-  f = fopen (argv[2], "r");
+  FILE *f = fopen (argv[2], "r");
   if (f == (FILE *) NULL) {
     gprint (GP_ERR, "file %s not found\n", argv[2]);
@@ -67,10 +66,10 @@
 
   /* save file name */
-  filename = filebasename (argv[2]);
+  char *filename = filebasename (argv[2]);
   strcpy (buf[0].file, filename);
   free (filename);
 
-  status = FALSE;
-  IsCompressed = FALSE;
+  int status = FALSE;
+  int IsCompressed = FALSE;
 
   /*** advance to the correct FITS extension ***/
@@ -168,15 +167,24 @@
 
   /* check for valid plane */
-  Nplane = buf[0].header.Naxis[2];
-  if (Nplane == 0) Nplane = 1;
-  if (plane > Nplane) {
-    gprint (GP_ERR, "-plane is too large: %d total planes\n", Nplane);
-    DeleteBuffer (buf);
-    fclose (f);
-    return (FALSE);
+  int Nz = buf[0].header.Naxis[2];
+  if (plane >= 0) {
+    // we are requesting a specific plane (-1 : all data)
+    int tooFar = Nz ? (plane >= Nz) : (plane > Nz);
+    if (tooFar) {
+      gprint (GP_ERR, "-plane is too large: %d total planes\n", Nz);
+      DeleteBuffer (buf);
+      fclose (f);
+      return (FALSE);
+    }
   }
 
   /* load matrix data */
   if (IsCompressed) {
+    if (plane > -1) {
+      gprint (GP_ERR, "-plane incompatible with compressed image\n");
+      DeleteBuffer (buf);
+      fclose (f);
+      return (FALSE);
+    }
     FTable ftable;
     Header theader;
@@ -191,6 +199,15 @@
     // XXX this currently does not work for a cube (we get a cube back, not a specific plane)
   } else {
-    sprintf (region, "-1 -1 -1 -1 %d %d", (plane - 1), plane);
-    status = gfits_fread_matrix_segment (f, &buf[0].matrix, &buf[0].header, region);
+    if (plane > -1) {
+      // read a single plane into a 2D image
+      sprintf (region, "-1 -1 -1 -1 %d %d", (plane - 1), plane);
+      status = gfits_fread_matrix_segment (f, &buf[0].matrix, &buf[0].header, region);
+      buf[0].header.Naxis[2] = 0;
+      buf[0].header.Naxes = 2;
+      gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
+      gfits_modify (&buf[0].header, "NAXIS3", "%d", 1, 0);
+    } else {
+      status = gfits_fread_matrix (f, &buf[0].matrix, &buf[0].header);
+    }
   }
   fclose (f);
@@ -200,12 +217,4 @@
     DeleteBuffer (buf);
     return (FALSE);
-  }
-
-  /* adjust buffer to represent 2D data */
-  if (Nplane > 1) {
-    buf[0].header.Naxis[2] = 0;
-    buf[0].header.Naxes = 2;
-    gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
-    gfits_delete (&buf[0].header, "NAXIS3", 1);
   }
 
@@ -215,4 +224,6 @@
     buf[0].header.Naxis[1] = 1;
     buf[0].matrix.Naxis[1] = 1;
+    gfits_modify (&buf[0].header, "NAXIS", "%d", 1, 2);
+    gfits_modify (&buf[0].header, "NAXIS2", "%d", 1, 1);
   }    
 
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/tv.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/tv.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/tv.c	(revision 37869)
@@ -28,4 +28,15 @@
   }
 
+  int plane = 0;
+  if ((N = get_argument (argc, argv, "-plane"))) {
+    remove_argument (N, &argc, argv);
+    plane = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  if (plane < 0) {
+    gprint (GP_ERR, " ERROR: -plane (plane) : cannot be negative\n");
+    return (FALSE);
+  }
+
   /* shell exits on pipe close, FIX */
   if ((N = get_argument (argc, argv, "-kill"))) {
@@ -55,7 +66,16 @@
   GetCoords (&coords, &buf[0].header);
   
-  image.data1d = (float *) buf[0].matrix.buffer;
   image.Nx = buf[0].matrix.Naxis[0];
   image.Ny = buf[0].matrix.Naxis[1];
+
+  int tooBig = buf[0].matrix.Naxis[2] ? (plane >= buf[0].matrix.Naxis[2]) : plane > 0;
+  if (tooBig) {
+    gprint (GP_ERR, " ERROR: -plane (plane) : out of bounds (%d vs %d)\n", plane, buf[0].matrix.Naxis[2]);
+    return (FALSE);
+  }
+  int Npix2D = image.Nx * image.Ny;
+
+  float *imdata = (float *) buf[0].matrix.buffer;
+  image.data1d = &imdata[plane*Npix2D];
 
   // send only the root of the file, not the full path
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/wd.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/wd.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/cmd.data/wd.c	(revision 37869)
@@ -81,14 +81,9 @@
   memcpy (temp_header.buffer, buf[0].header.buffer, temp_header.datasize);
 
-  if (temp_header.Naxes) {
-    // the inBlank value probably does not matter: temp_matrix is float, so nan is used
-    gfits_convert_format (&temp_header, &temp_matrix, outBitpix, outScale, outZero, 0xffff, outUnsign);
-  } else {
-    gfits_modify (&temp_header, "BITPIX", "%d", 1, outBitpix);
-    gfits_modify (&temp_header, "BSCALE", "%lf", 1, outScale);
-    gfits_modify (&temp_header, "BZERO",  "%lf", 1, outZero);
-    gfits_modify_alt (&temp_header, "UNSIGN", "%t", 1, outUnsign);
-  }
+  gfits_convert_format (&temp_header, &temp_matrix, outBitpix, outScale, outZero, 0xffff, outUnsign);
 
+  // Extend puts the output matrix in the first available non-PHU slot (ie, the last one)
+  // it updates NEXTEND and set EXTEND to TRUE, and modifies the PHU header (neither should happen)
+  // if those keywords do not exist...
   if (Extend) {
     Header Xhead;
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/BufferOps.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/BufferOps.c	(revision 37869)
@@ -120,4 +120,6 @@
 }
   
+// buffer is 1D array referenced in the order:
+// buffer[x + Nx*y + Nx*Ny*z + ...]
 int CreateBuffer3D (Buffer *buf, int Nx, int Ny, int Nz, int bitpix, float bzero, float bscale) {
 
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/convert_to_RPN.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 37869)
@@ -60,4 +60,5 @@
     if (!strcmp (argv[i], "xramp"))  { type = ST_UNARY; goto gotit; }
     if (!strcmp (argv[i], "yramp"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "zramp"))  { type = ST_UNARY; goto gotit; }
     if (!strcmp (argv[i], "ramp"))   { type = ST_UNARY; goto gotit; }
     if (!strcmp (argv[i], "zero"))   { type = ST_UNARY; goto gotit; }
Index: /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/stack_math.c
===================================================================
--- /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/stack_math.c	(revision 37868)
+++ /branches/eam_branches/ipp-20150112/Ohana/src/opihi/lib.shell/stack_math.c	(revision 37869)
@@ -127,13 +127,9 @@
 int MMM_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
 
-  int i, Npix;
+  int i;
   float *out, *M1, *M2, *M3;
   char line[512]; // this is only used to report an error 
   
-  Npix = 1;
-  for (i = 0; i < V1[0].buffer[0].matrix.Naxes; i++) {
-    if (V1[0].buffer[0].matrix.Naxis[i] == 0) break;
-    Npix *= V1[0].buffer[0].matrix.Naxis[i];
-  }
+  int Npix = gfits_npix_matrix (&V1[0].buffer[0].matrix);
   
   if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
@@ -725,13 +721,9 @@
 int MM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
 
-  int i, Npix;
+  int i;
   float *out, *M1, *M2;
   char line[512]; // this is only used to report an error 
   
-  Npix = 1;
-  for (i = 0; i < V1[0].buffer[0].matrix.Naxes; i++) {
-    if (V1[0].buffer[0].matrix.Naxis[i] == 0) break;
-    Npix *= V1[0].buffer[0].matrix.Naxis[i];
-  }
+  int Npix = gfits_npix_matrix (&V1[0].buffer[0].matrix);
   
   if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
@@ -809,12 +801,8 @@
 int MS_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
 
-  int i, Npix;
+  int i;
   char line[512]; // this is only used to report an error 
   
-  Npix = 1;
-  for (i = 0; i < V1[0].buffer[0].matrix.Naxes; i++) {
-    if (V1[0].buffer[0].matrix.Naxis[i] == 0) break;
-    Npix *= V1[0].buffer[0].matrix.Naxis[i];
-  }
+  int Npix = gfits_npix_matrix (&V1[0].buffer[0].matrix);
 
   /* if possible, use V1 as temp buffer, otherwise create new one */
@@ -890,12 +878,8 @@
 int SM_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
 
-  int i, Npix;
+  int i;
   char line[512]; // this is only used to report an error 
   
-  Npix = 1;
-  for (i = 0; i < V2[0].buffer[0].matrix.Naxes; i++) {
-    if (V2[0].buffer[0].matrix.Naxis[i] == 0) break;
-    Npix *= V2[0].buffer[0].matrix.Naxis[i];
-  }
+  int Npix = gfits_npix_matrix (&V2[0].buffer[0].matrix);
   
   if (V2[0].type == ST_MATRIX_TMP) {  /* V2[0] is NOT temporary, we can't use it for storage */
@@ -1252,5 +1236,6 @@
   if (!strcmp (op, "xramp"))  V_FUNC(i, ST_SCALAR_INT);
   if (!strcmp (op, "yramp"))  V_FUNC(0, ST_SCALAR_INT);
-  /* xramp and yramp above only make sense for matrices. for vectors, xramp = ramp, yramp = zero */
+  if (!strcmp (op, "zramp"))  V_FUNC(0, ST_SCALAR_INT);
+  /* xramp, yramp, zramp above only make sense for matrices. for vectors, xramp = ramp, yramp = zero */
 
 # undef V_FUNC
@@ -1271,12 +1256,8 @@
 int M_unary (StackVar *OUT, StackVar *V1, char *op) {
 
-  int i, j, Npix;
+  int i, j, k;
   float *out, *M1;
   
-  Npix = 1;
-  for (i = 0; i < V1[0].buffer[0].matrix.Naxes; i++) {
-    if (V1[0].buffer[0].matrix.Naxis[i] == 0) break;
-    Npix *= V1[0].buffer[0].matrix.Naxis[i];
-  }
+  int Npix = gfits_npix_matrix (&V1[0].buffer[0].matrix);
   
   if (V1[0].type == ST_MATRIX_TMP) {
@@ -1336,7 +1317,10 @@
     int Nx = V1[0].buffer[0].matrix.Naxis[0];
     int Ny = V1[0].buffer[0].matrix.Naxis[1];
-    for (j = 0; j < Ny; j++) {
-      for (i = 0; i < Nx; i++, out++, M1++) {
-	*out = i;
+    int Nz = MAX (1, V1[0].buffer[0].matrix.Naxis[2]);
+    for (k = 0; k < Nz; k++) {
+      for (j = 0; j < Ny; j++) {
+	for (i = 0; i < Nx; i++, out++, M1++) {
+	  *out = i;
+	}
       }
     }
@@ -1345,7 +1329,22 @@
     int Nx = V1[0].buffer[0].matrix.Naxis[0];
     int Ny = V1[0].buffer[0].matrix.Naxis[1];
-    for (j = 0; j < Ny; j++) {
-      for (i = 0; i < Nx; i++, out++, M1++) {
-	*out = j;
+    int Nz = MAX (1, V1[0].buffer[0].matrix.Naxis[2]);
+    for (k = 0; k < Nz; k++) {
+      for (j = 0; j < Ny; j++) {
+	for (i = 0; i < Nx; i++, out++, M1++) {
+	  *out = j;
+	}
+      }
+    }
+  }
+  if (!strcmp (op, "zramp")) {
+    int Nx = V1[0].buffer[0].matrix.Naxis[0];
+    int Ny = V1[0].buffer[0].matrix.Naxis[1];
+    int Nz = MAX (1, V1[0].buffer[0].matrix.Naxis[2]);
+    for (k = 0; k < Nz; k++) {
+      for (j = 0; j < Ny; j++) {
+	for (i = 0; i < Nx; i++, out++, M1++) {
+	  *out = k;
+	}
       }
     }
