commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r10570 - in gnuradio/branches/developers/jblum/gui_gut


From: jblum
Subject: [Commit-gnuradio] r10570 - in gnuradio/branches/developers/jblum/gui_guts: gnuradio-core/src/lib/io gr-wxgui/src/python gr-wxgui/src/python/plotter grc/data/platforms/python/blocks
Date: Fri, 6 Mar 2009 15:08:44 -0700 (MST)

Author: jblum
Date: 2009-03-06 15:08:43 -0700 (Fri, 06 Mar 2009)
New Revision: 10570

Modified:
   
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink.i
   
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.cc
   
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.h
   gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/common.py
   
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histo_window.py
   
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histosink_gl.py
   
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/bar_plotter.py
   
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/grid_plotter_base.py
   
gnuradio/branches/developers/jblum/gui_guts/grc/data/platforms/python/blocks/wxgui_histosink2.xml
Log:
WIP Determine histogram minimum and maximum from frame data.
Pass minimum and maximum as arg1,arg2 in the message.
Common message thread also sets values for args 1 and 2 if set.
Mutex locks for histosink set functions and work.



Modified: 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink.i
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink.i
        2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink.i
        2009-03-06 22:08:43 UTC (rev 10570)
@@ -32,12 +32,8 @@
 
   unsigned int get_frame_size(void);
   unsigned int get_num_bins(void);
-  float get_maximum(void);
-  float get_minimum(void);
 
   void set_frame_size(unsigned int frame_size);
   void set_num_bins(unsigned int num_bins);
-  void set_maximum(float maximum);
-  void set_minimum(float minimum);
 
 };

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.cc
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.cc
     2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.cc
     2009-03-06 22:08:43 UTC (rev 10570)
@@ -27,6 +27,22 @@
 #include <gr_histo_sink_f.h>
 #include <gr_io_signature.h>
 
+static float get_clean_num(float num){
+  if (num == 0) return 0;
+  /* extract sign and exponent from num */
+  int sign = (num < 0) ? -1 : 1; num = fabs(num);
+  float exponent = floor(log10(num));
+  /* search for closest number with base 1, 2, 5, 10 */
+  float closest_num = 10*pow(10, exponent);
+  if (fabs(num - 1*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 1*pow(10, exponent);
+  if (fabs(num - 2*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 2*pow(10, exponent);
+  if (fabs(num - 5*pow(10, exponent)) < fabs(num - closest_num))
+    closest_num = 5*pow(10, exponent);
+  return sign*closest_num;
+}
+
 gr_histo_sink_f_sptr
 gr_make_histo_sink_f (gr_msg_queue_sptr msgq)
 {
@@ -35,13 +51,18 @@
 
 gr_histo_sink_f::gr_histo_sink_f (gr_msg_queue_sptr msgq)
   : gr_sync_block ("histo_sink_f", gr_make_io_signature (1, 1, sizeof 
(float)), gr_make_io_signature (0, 0, 0)),
-  d_msgq (msgq), d_frame_size(1000), d_sample_count(0), d_minimum(-1), 
d_maximum(1), d_bins(NULL)
+  d_msgq (msgq), d_num_bins(11), d_frame_size(1000), d_sample_count(0), 
d_bins(NULL), d_samps(NULL)
 {
-  set_num_bins(11); //allocates the bins, calls clear
+  pthread_mutex_init(&d_mutex, 0);
+  //allocate arrays and clear
+  set_num_bins(d_num_bins);
+  set_frame_size(d_frame_size);
 }
 
 gr_histo_sink_f::~gr_histo_sink_f (void)
 {
+  pthread_mutex_destroy(&d_mutex);
+  delete [] d_samps;
   delete [] d_bins;
 }
 
@@ -50,14 +71,10 @@
   gr_vector_const_void_star &input_items,
   gr_vector_void_star &output_items)
 {
-  int index;
   const float *in = (const float *) input_items[0];
-  float scale = ((float)(d_num_bins-1))/(d_maximum - d_minimum);
-  for (unsigned int i = 0; i < noutput_items; i++){
-    /* scale the floating point value to a bin index */
-    index = (int)round(scale*(in[i] - d_minimum));
-    /* increment the bin# */
-    if (index >= 0 && index < (int)d_num_bins) d_bins[index]++;
+  pthread_mutex_lock(&d_mutex);
+  for (unsigned int i = 0; i < (unsigned int)noutput_items; i++){
+    d_samps[d_sample_count] = in[i];
     d_sample_count++;
     /* processed a frame? */
     if (d_sample_count == d_frame_size){
@@ -65,6 +82,7 @@
       clear();
     }
   }
+  pthread_mutex_unlock(&d_mutex);
   return noutput_items;
 }
 
@@ -72,26 +90,33 @@
 gr_histo_sink_f::send_frame(void){
   /* output queue full, drop the data */
   if (d_msgq->full_p()) return;
+  /* find the minimum and maximum */
+  float minimum = d_samps[0];
+  float maximum = d_samps[0];
+  for (unsigned int i = 0; i < d_frame_size; i++){
+    if (d_samps[i] < minimum) minimum = d_samps[i];
+    if (d_samps[i] > maximum) maximum = d_samps[i];
+  }
+  minimum = get_clean_num(minimum);
+  maximum = get_clean_num(maximum);
+  if (minimum == maximum || minimum > maximum) return; //useless data or screw 
up?
+  /* load the bins */
+  int index;
+  float bin_width = (maximum - minimum)/(d_num_bins-1);
+  for (unsigned int i = 0; i < d_sample_count; i++){
+    index = round((d_samps[i] - minimum)/bin_width);
+    /* ensure the index range in case a small floating point error is involed 
*/
+    if (index < 0) index = 0;
+    if (index >= (int)d_num_bins) index = d_num_bins-1;
+    d_bins[index]++;
+  }
   /* Build a message to hold the output records */
-  gr_message_sptr msg = gr_make_message(0, 0, 0, d_num_bins*sizeof(float));
+  gr_message_sptr msg = gr_make_message(0, minimum, maximum, 
d_num_bins*sizeof(float));
   float *out = (float *)msg->msg(); // get pointer to raw message buffer
-  /*
-   * Get a count of all bins. This may not be frame size,
-   * since out of range values are ignored.
-   */
-  unsigned int count = 0;
+  /* normalize the bins and put into message */
   for (unsigned int i = 0; i < d_num_bins; i++){
-    count += d_bins[i];
+    out[i] = ((float)d_bins[i])/d_frame_size;
   }
-  if (count == 0){ //count is zero, send zero bins
-    for (unsigned int i = 0; i < d_num_bins; i++){
-      out[i] = 0;
-    }
-  }else{ //normalize the bins
-    for (unsigned int i = 0; i < d_num_bins; i++){
-      out[i] = ((float)d_bins[i])/count;
-    }
-  }
   /* send the message */
   d_msgq->handle(msg);
 }
@@ -118,42 +143,27 @@
   return d_num_bins;
 }
 
-float
-gr_histo_sink_f::get_maximum(void){
-  return d_maximum;
-}
-
-float
-gr_histo_sink_f::get_minimum(void){
-  return d_minimum;
-}
-
 /**************************************************
  * Setters
  **************************************************/
 void
 gr_histo_sink_f::set_frame_size(unsigned int frame_size){
+  pthread_mutex_lock(&d_mutex);
   d_frame_size = frame_size;
+  /* allocate a new sample array */
+  delete [] d_samps;
+  d_samps = new float[d_frame_size];
   clear();
+  pthread_mutex_unlock(&d_mutex);
 }
 
 void
 gr_histo_sink_f::set_num_bins(unsigned int num_bins){
+  pthread_mutex_lock(&d_mutex);
   d_num_bins = num_bins;
   /* allocate a new bin array */
   delete [] d_bins;
-  d_bins = new float[d_num_bins];
+  d_bins = new unsigned int[d_num_bins];
   clear();
+  pthread_mutex_unlock(&d_mutex);
 }
-
-void
-gr_histo_sink_f::set_maximum(float maximum){
-  d_maximum = maximum;
-  clear();
-}
-
-void
-gr_histo_sink_f::set_minimum(float minimum){
-  d_minimum = minimum;
-  clear();
-}

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.h
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.h
      2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gnuradio-core/src/lib/io/gr_histo_sink_f.h
      2009-03-06 22:08:43 UTC (rev 10570)
@@ -25,6 +25,7 @@
 
 #include <gr_sync_block.h>
 #include <gr_msg_queue.h>
+#include <pthread.h>
 
 class gr_histo_sink_f;
 typedef boost::shared_ptr<gr_histo_sink_f> gr_histo_sink_f_sptr;
@@ -42,9 +43,9 @@
   unsigned int d_num_bins;
   unsigned int d_frame_size;
   unsigned int d_sample_count;
-  float d_minimum;
-  float d_maximum;
-  float *d_bins;
+  unsigned int *d_bins;
+  float *d_samps;
+  pthread_mutex_t d_mutex;
 
   friend gr_histo_sink_f_sptr gr_make_histo_sink_f (gr_msg_queue_sptr msgq);
   gr_histo_sink_f (gr_msg_queue_sptr msgq);
@@ -60,13 +61,9 @@
 
   unsigned int get_frame_size(void);
   unsigned int get_num_bins(void);
-  float get_maximum(void);
-  float get_minimum(void);
 
   void set_frame_size(unsigned int frame_size);
   void set_num_bins(unsigned int num_bins);
-  void set_maximum(float maximum);
-  void set_minimum(float minimum);
 
 };
 

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/common.py
===================================================================
--- gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/common.py   
2009-03-06 03:43:45 UTC (rev 10569)
+++ gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/common.py   
2009-03-06 22:08:43 UTC (rev 10570)
@@ -55,18 +55,23 @@
        Read messages from the message queue.
        Forward messages to the message handler.
        """
-       def __init__ (self, msgq, controller, key):
+       def __init__ (self, msgq, controller, msg_key, arg1_key='', 
arg2_key=''):
                threading.Thread.__init__(self)
                self.setDaemon(1)
                self.msgq = msgq
                self._controller = controller
-               self._key = key
+               self._msg_key = msg_key
+               self._arg1_key = arg1_key
+               self._arg2_key = arg2_key
                self.keep_running = True
                self.start()
 
        def run(self):
                while self.keep_running:
-                       self._controller[self._key] = 
self.msgq.delete_head().to_string()
+                       msg = self.msgq.delete_head()
+                       if self._arg1_key: self._controller[self._arg1_key] = 
msg.arg1()
+                       if self._arg2_key: self._controller[self._arg2_key] = 
msg.arg2()
+                       self._controller[self._msg_key] = msg.to_string()
 
 ##################################################
 # WX Shared Classes

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histo_window.py
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histo_window.py 
    2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histo_window.py 
    2009-03-06 22:08:43 UTC (rev 10570)
@@ -53,39 +53,20 @@
                wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
                control_box = wx.BoxSizer(wx.VERTICAL)
                SIZE = (100, -1)
-               control_box.AddStretchSpacer()
                control_box.Add(common.LabelText(self, 'Options'), 0, 
wx.ALIGN_CENTER)
-               #minimum
                control_box.AddStretchSpacer()
-               minimum_ctrl = common.TextBoxController(self, parent, 
MINIMUM_KEY)
-               control_box.Add(common.LabelBox(self, ' Minimum ', 
minimum_ctrl), 0, wx.EXPAND)
-               #maximum
-               maximum_ctrl = common.TextBoxController(self, parent, 
MAXIMUM_KEY)
-               control_box.Add(common.LabelBox(self, ' Maximum ', 
maximum_ctrl), 0, wx.EXPAND)
                #num bins
-               control_box.AddStretchSpacer()
                def num_bins_cast(num):
                        num = int(num)
                        assert num > 1
                        return num
                num_bins_ctrl = common.TextBoxController(self, parent, 
NUM_BINS_KEY, cast=num_bins_cast)
                control_box.Add(common.LabelBox(self, ' Num Bins ', 
num_bins_ctrl), 0, wx.EXPAND)
+               control_box.AddStretchSpacer()
                #frame size
                frame_size_ctrl = common.TextBoxController(self, parent, 
FRAME_SIZE_KEY, cast=num_bins_cast)
                control_box.Add(common.LabelBox(self, ' Frame Size ', 
frame_size_ctrl), 0, wx.EXPAND)
-               #y axis lvl
                control_box.AddStretchSpacer()
-               y_off_slider = wx.Slider(self, size=SIZE, 
style=wx.SL_HORIZONTAL)
-               y_off_slider.SetRange(0, 1000)
-               def y_off_slider_changed(evt): parent[Y_OFF_KEY] = 
float(y_off_slider.GetValue())/y_off_slider.GetMax()
-               y_off_slider.Bind(wx.EVT_SLIDER, y_off_slider_changed)
-               parent.subscribe(Y_OFF_KEY, lambda x: 
y_off_slider.SetValue(int(round(x*y_off_slider.GetMax()))))
-               control_box.Add(common.LabelBox(self, ' Y Off ', y_off_slider), 
0, wx.EXPAND)
-               #autoscale
-               control_box.AddStretchSpacer()
-               self.autoscale_button = wx.Button(self, label='Autoscale', 
style=wx.BU_EXACTFIT)
-               self.autoscale_button.Bind(wx.EVT_BUTTON, self.parent.autoscale)
-               control_box.Add(self.autoscale_button, 0, wx.EXPAND)
                #run/stop
                self.run_button = common.ToggleButtonController(self, parent, 
RUNNING_KEY, 'Stop', 'Run')
                control_box.Add(self.run_button, 0, wx.EXPAND)
@@ -131,19 +112,16 @@
                main_box.Add(self.control_panel, 0, wx.EXPAND)
                self.SetSizerAndFit(main_box)
                #initialize values
-               self[MAXIMUM_KEY] = self[MAXIMUM_KEY]
-               self[MINIMUM_KEY] = self[MINIMUM_KEY]
+               self[MINIMUM_KEY] = -1
+               self[MAXIMUM_KEY] = 1
                self[NUM_BINS_KEY] = self[NUM_BINS_KEY]
                self[FRAME_SIZE_KEY] = self[FRAME_SIZE_KEY]
                self[Y_OFF_KEY] = 0.25
                self[RUNNING_KEY] = True
                #register events
                self.subscribe(MSG_KEY, self.handle_msg)
-               for key in (
-                       MAXIMUM_KEY, MINIMUM_KEY, NUM_BINS_KEY, Y_OFF_KEY,
-               ): self.subscribe(key, self.update_grid)
                #initial update
-               self.update_grid()
+               #self.update_grid()
 
        def handle_msg(self, msg):
                """
@@ -158,26 +136,17 @@
                        bar_width=0.6,
                        color_spec=(0, 0, 1),
                )
-               #update the plotter
-               self.plotter.update()
-
-       def autoscale(self, *args):
-               if not len(self.samples): return
-               self[Y_OFF_KEY] = numpy.max(self.samples) + 0.01
-
-       def update_grid(self, *args):
-               """
-               Update the plotter grid.
-               Determine the x and y axis grid parameters.
-               """
-               if self[Y_OFF_KEY] <= 0: self[Y_OFF_KEY] = 0.001; return
-               if self[Y_OFF_KEY] > 1: self[Y_OFF_KEY] = 1.0; return
+               #calculate the maximum y value
+               y_off = math.ceil(numpy.max(self.samples)*100)
+               if y_off <= 0: y_off = 1
+               if y_off > 100: y_off = 100
                #update the x grid
                self.plotter.set_x_grid(
                        self[MINIMUM_KEY], self[MAXIMUM_KEY],
                        (self[MAXIMUM_KEY] - self[MINIMUM_KEY])/8,
                )
                self.plotter.set_x_label('Counts')
-               self.plotter.set_y_grid(0, 100.*self[Y_OFF_KEY], 
100.*self[Y_OFF_KEY]/4)
+               #update the y grid
+               self.plotter.set_y_grid(0, y_off, y_off/4.)
                self.plotter.set_y_label('Frequency', '%')
                self.plotter.update()

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histosink_gl.py
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histosink_gl.py 
    2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/histosink_gl.py 
    2009-03-06 22:08:43 UTC (rev 10570)
@@ -41,8 +41,6 @@
                parent,
                size=histo_window.DEFAULT_WIN_SIZE,
                title='',
-               minimum=-1,
-               maximum=1,
                num_bins=11,
                frame_size=1000,
        ):
@@ -56,24 +54,18 @@
                #blocks
                msgq = gr.msg_queue(2)
                histo = gr.histo_sink_f(msgq)
-               histo.set_minimum(minimum)
-               histo.set_maximum(maximum)
                histo.set_num_bins(num_bins)
                histo.set_frame_size(frame_size)
                #connect
                self.connect(self, histo)
                #controller
                self.controller = pubsub()
-               self.controller.subscribe(MAXIMUM_KEY, histo.set_maximum)
-               self.controller.publish(MAXIMUM_KEY, histo.get_maximum)
-               self.controller.subscribe(MINIMUM_KEY, histo.set_minimum)
-               self.controller.publish(MINIMUM_KEY, histo.get_minimum)
                self.controller.subscribe(NUM_BINS_KEY, histo.set_num_bins)
                self.controller.publish(NUM_BINS_KEY, histo.get_num_bins)
                self.controller.subscribe(FRAME_SIZE_KEY, histo.set_frame_size)
                self.controller.publish(FRAME_SIZE_KEY, histo.get_frame_size)
                #start input watcher
-               common.input_watcher(msgq, self.controller, MSG_KEY)
+               common.input_watcher(msgq, self.controller, MSG_KEY, 
arg1_key=MINIMUM_KEY, arg2_key=MAXIMUM_KEY)
                #create window
                self.win = histo_window.histo_window(
                        parent=parent,
@@ -105,7 +97,7 @@
         src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 2e3, 1)
         #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1)
         thr2 = gr.throttle(gr.sizeof_float, input_rate)
-        sink2 = histo_sink_f (panel, title="Data", minimum=-1.2, maximum=1.2, 
num_bins=21, frame_size=1000)
+        sink2 = histo_sink_f (panel, title="Data", num_bins=31, 
frame_size=1000)
         vbox.Add (sink2.win, 1, wx.EXPAND)
 
         self.connect(src2, thr2, sink2)

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/bar_plotter.py
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/bar_plotter.py
      2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/bar_plotter.py
      2009-03-06 22:08:43 UTC (rev 10570)
@@ -112,6 +112,7 @@
                @param y_val the current y value
                @return a string with newlines
                """
+               if len(self._bars) == 0: return ''
                scalar = float(len(self._bars)-1)/(self.x_max - self.x_min)
                #convert x val to bar #
                bar_index = scalar*(x_val - self.x_min)

Modified: 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/grid_plotter_base.py
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/grid_plotter_base.py
        2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/gr-wxgui/src/python/plotter/grid_plotter_base.py
        2009-03-06 22:08:43 UTC (rev 10570)
@@ -275,9 +275,11 @@
                max = float(max)
                step = float(step)
                #check for valid numbers
-               assert step > 0
-               assert max > min
-               assert max - min > step
+               try:
+                       assert step > 0
+                       assert max > min
+                       assert max - min > step
+               except AssertionError: return [-1, 1]
                #determine the start and stop value
                start = int(math.ceil(min/step))
                stop = int(math.floor(max/step))

Modified: 
gnuradio/branches/developers/jblum/gui_guts/grc/data/platforms/python/blocks/wxgui_histosink2.xml
===================================================================
--- 
gnuradio/branches/developers/jblum/gui_guts/grc/data/platforms/python/blocks/wxgui_histosink2.xml
   2009-03-06 03:43:45 UTC (rev 10569)
+++ 
gnuradio/branches/developers/jblum/gui_guts/grc/data/platforms/python/blocks/wxgui_histosink2.xml
   2009-03-06 22:08:43 UTC (rev 10570)
@@ -11,8 +11,6 @@
        <make>histosink_gl.histo_sink_f(
        self.GetWin(),
        title=$title,
-       minimum=$minimum,
-       maximum=$maximum,
        num_bins=$num_bins,
        frame_size=$frame_size,
 )
@@ -22,8 +20,6 @@
 #else
 self.GridAdd(self.$(id).win, $grid_pos[0], $grid_pos[1], $grid_pos[2], 
$grid_pos[3])
 #end if</make>
-       <callback>set_minimum($minimum)</callback>
-       <callback>set_maximum($maximum)</callback>
        <callback>set_num_bins($num_bins)</callback>
        <callback>set_frame_size($frame_size)</callback>
        <param>
@@ -33,18 +29,6 @@
                <type>string</type>
        </param>
        <param>
-               <name>Minimum</name>
-               <key>minimum</key>
-               <value>-1</value>
-               <type>real</type>
-       </param>
-       <param>
-               <name>Maximum</name>
-               <key>maximum</key>
-               <value>1</value>
-               <type>real</type>
-       </param>
-       <param>
                <name>Num Bins</name>
                <key>num_bins</key>
                <value>27</value>





reply via email to

[Prev in Thread] Current Thread [Next in Thread]