summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFOSSEE2015-03-10 17:11:34 +0530
committerFOSSEE2015-03-10 17:11:34 +0530
commitaf6398eb6e3273c005312939fb6d965f8a50baed (patch)
tree7840d8a5a5f4681c49971615cc5b83fe0e70cec7
parent65b50f1ed0b38037dda404a49abda9c809af9bcb (diff)
downloadgnuradio-af6398eb6e3273c005312939fb6d965f8a50baed.tar.gz
gnuradio-af6398eb6e3273c005312939fb6d965f8a50baed.tar.bz2
gnuradio-af6398eb6e3273c005312939fb6d965f8a50baed.zip
Plot-sink added
-rw-r--r--gr-input/grc/CMakeLists.txt6
-rwxr-xr-xgr-input/grc/plot_sink.xml112
-rw-r--r--gr-input/python/CMakeLists.txt16
-rwxr-xr-xgr-input/python/matplotsink.py312
-rwxr-xr-xgr-input/python/plot_sink.py57
5 files changed, 503 insertions, 0 deletions
diff --git a/gr-input/grc/CMakeLists.txt b/gr-input/grc/CMakeLists.txt
index 1b604feb2..61b317f77 100644
--- a/gr-input/grc/CMakeLists.txt
+++ b/gr-input/grc/CMakeLists.txt
@@ -69,3 +69,9 @@ install(FILES
)
+install(FILES
+ plot_sink.xml
+ DESTINATION ${GRC_BLOCKS_DIR}
+ COMPONENT "input_python"
+)
+
diff --git a/gr-input/grc/plot_sink.xml b/gr-input/grc/plot_sink.xml
new file mode 100755
index 000000000..07022ea07
--- /dev/null
+++ b/gr-input/grc/plot_sink.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Variable Sink: Custom blks2 block
+###################################################
+ -->
+<block>
+ <name>Plot Sink</name>
+ <key>plot_sink</key>
+ <category>Single Board Heater System</category>
+ <import>import gnuradio.input.plot_sink</import>
+ <make>#set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self'
+gnuradio.input.plot_sink.plot_sink_$(type.fcn)(
+ $(parent).GetWin(),
+ title=$title,
+ vlen=$vlen,
+ decim=$decim,
+ gsz=$gsz,
+ zoom=$zoom,
+)
+#if not $grid_pos()
+$(parent).Add(self.$(id).win)
+#else
+$(parent).GridAdd(self.$(id).win, $(', '.join(map(str, $grid_pos()))))
+#end if</make>
+ <callback>set_decim($decim)</callback>
+ <param>
+ <name>Type</name>
+ <key>type</key>
+ <type>enum</type>
+ <option>
+ <name>Complex</name>
+ <key>complex</key>
+ <opt>fcn:c</opt>
+ </option>
+ <option>
+ <name>Float</name>
+ <key>float</key>
+ <opt>fcn:f</opt>
+ </option>
+ <option>
+ <name>Int</name>
+ <key>int</key>
+ <opt>fcn:i</opt>
+ </option>
+ <option>
+ <name>Short</name>
+ <key>short</key>
+ <opt>fcn:s</opt>
+ </option>
+ <option>
+ <name>Byte</name>
+ <key>byte</key>
+ <opt>fcn:b</opt>
+ </option>
+ </param>
+ <param>
+ <name>Title</name>
+ <key>title</key>
+ <value>Scope Plot</value>
+ <type>string</type>
+ </param>
+ <param>
+ <name>Decimation</name>
+ <key>decim</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Vec Length</name>
+ <key>vlen</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+
+ <param>
+ <name>Graph size</name>
+ <key>gsz</key>
+ <value>50</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>History Required</name>
+ <key>zoom</key>
+ <value>0</value>
+ <type>int</type>
+ </param>
+
+ <param>
+ <name>Grid Position</name>
+ <key>grid_pos</key>
+ <value></value>
+ <type>grid_pos</type>
+ </param>
+ <param>
+ <name>Notebook</name>
+ <key>notebook</key>
+ <value></value>
+ <type>notebook</type>
+ </param>
+
+ <check>$vlen &gt; 0</check>
+ <sink>
+ <name>in</name>
+ <type>$type</type>
+ <vlen>$vlen</vlen>
+ </sink>
+ <doc>
+Read samples from the input stream and \
+plot one in every decimation samples to the plot sink.
+</doc>
+</block>
diff --git a/gr-input/python/CMakeLists.txt b/gr-input/python/CMakeLists.txt
index 77ac986ac..7c73fc59f 100644
--- a/gr-input/python/CMakeLists.txt
+++ b/gr-input/python/CMakeLists.txt
@@ -92,6 +92,20 @@ GR_PYTHON_INSTALL(
COMPONENT "input_python"
)
+GR_PYTHON_INSTALL(
+ FILES
+ plot_sink.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/input
+ COMPONENT "input_python"
+)
+
+GR_PYTHON_INSTALL(
+ FILES
+ matplotsink.py
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio/input
+ COMPONENT "input_python"
+)
+
########################################################################
# Handle the unit tests
@@ -104,6 +118,8 @@ list(APPEND GR_TEST_PYTHON_DIRS
list(APPEND GR_TEST_TARGET_DEPS gnuradio-input)
include(GrTest)
+
+
file(GLOB py_qa_test_files "qa_*.py")
foreach(py_qa_test_file ${py_qa_test_files})
get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE)
diff --git a/gr-input/python/matplotsink.py b/gr-input/python/matplotsink.py
new file mode 100755
index 000000000..105009b4d
--- /dev/null
+++ b/gr-input/python/matplotsink.py
@@ -0,0 +1,312 @@
+"""
+$$
+
+Modified for working as a GNU Radio block
+Rakesh Peter (rakesh.peter@gmail.com)
+Last modified: 07.05.2010
+
+$$
+
+This demo demonstrates how to draw a dynamic mpl (matplotlib)
+plot in a wxPython application.
+
+It allows "live" plotting as well as manual zooming to specific
+regions.
+
+Both X and Y axes allow "auto" or "manual" settings. For Y, auto
+mode sets the scaling of the graph to see all the data points.
+For X, auto mode makes the graph "follow" the data. Set it X min
+to manual 0 to always see the whole data from the beginning.
+
+Note: press Enter in the 'manual' text box to make a new value
+affect the plot.
+
+Eli Bendersky (eliben@gmail.com)
+License: this code is in the public domain
+Last modified: 31.07.2008
+"""
+
+import os
+import pprint
+import random
+import sys
+import wx
+import gnuradio.grc.gui
+from gnuradio.grc.gui import Actions,ActionHandler
+# The recommended way to use wx with mpl is with the WXAgg
+# backend.
+#
+import matplotlib
+import matplotlib.pyplot as plt
+import matplotlib.animation as animation
+#matplotlib.use('WXAgg')
+from matplotlib.figure import Figure
+from matplotlib.backends.backend_wxagg import \
+ FigureCanvasWxAgg as FigCanvas, \
+ NavigationToolbar2WxAgg as NavigationToolbar
+import numpy as np
+import pylab
+
+
+class DataGen(object):
+ """ A silly class that generates pseudo-random data for
+ display in the plot.
+ """
+ def __init__(self, init=50):
+ self.data = self.init = init
+
+ def next(self):
+ self._recalc_data()
+ #return self.data
+ return [0.0,1.1,2.2,3.3]
+
+ def _recalc_data(self):
+ delta = random.uniform(-0.5, 0.5)
+ r = random.random()
+
+ if r > 0.9:
+ self.data += delta * 15
+ elif r > 0.8:
+ # attraction to the initial value
+ delta += (0.5 if self.init > self.data else -0.5)
+ self.data += delta
+ else:
+ self.data += delta
+
+
+
+class matplotsink(wx.Panel):
+
+ def __init__(self, parent, title, queue,gsz,zoom):
+ wx.Panel.__init__(self, parent, wx.SIMPLE_BORDER)
+
+ self.gsz = gsz
+ self.parent = parent
+ self.title = title
+ self.q = queue
+ self.zoom=zoom
+ self.paused = False
+
+# self.create_menu()
+# self.create_status_bar()
+ self.create_main_panel()
+
+
+ def create_menu(self):
+ self.menubar = wx.MenuBar()
+
+ menu_file = wx.Menu()
+ m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file")
+ self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt)
+ menu_file.AppendSeparator()
+ m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit")
+ self.Bind(wx.EVT_MENU, self.on_exit, m_exit)
+ self.menubar.Append(menu_file, "&File")
+ self.SetMenuBar(self.menubar)
+
+
+ def create_main_panel(self):
+ self.panel = self
+
+ self.init_plot()
+ self.canvas = FigCanvas(self.panel, -1, self.fig)
+ self.scroll_range = 400
+ self.canvas.SetScrollbar(wx.HORIZONTAL,0,5,self.scroll_range)
+ self.canvas.Bind(wx.EVT_SCROLLWIN,self.OnScrollEvt)
+
+
+ self.pause_button = wx.Button(self.panel, -1, "Pause")
+ self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button)
+ self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button)
+
+ self.cb_grid = wx.CheckBox(self.panel, -1,
+ "Show Grid",
+ style=wx.ALIGN_RIGHT)
+ self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid)
+ self.cb_grid.SetValue(True)
+
+ self.cb_xlab = wx.CheckBox(self.panel, -1,
+ "Show X labels",
+ style=wx.ALIGN_RIGHT)
+ self.Bind(wx.EVT_CHECKBOX, self.on_cb_xlab, self.cb_xlab)
+ self.cb_xlab.SetValue(True)
+
+ self.hbox1 = wx.BoxSizer(wx.HORIZONTAL)
+ self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
+ self.hbox1.AddSpacer(20)
+ self.hbox1.Add(self.cb_grid, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
+ self.hbox1.AddSpacer(10)
+ self.hbox1.Add(self.cb_xlab, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
+
+
+ self.vbox = wx.BoxSizer(wx.VERTICAL)
+ self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW)
+ self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP)
+
+ self.panel.SetSizer(self.vbox)
+ self.vbox.Fit(self)
+ self.ani=animation.FuncAnimation(self.fig,self.draw_plot,interval=100)
+
+ def OnScrollEvt(self,event):
+ self.i_start = event.GetPosition()
+ self.i_end = self.i_window + event.GetPosition()
+ self.draw_plot(0)
+
+ def create_status_bar(self):
+ self.statusbar = self.CreateStatusBar()
+
+ def draw_test(self,event):
+ self.xar=np.arange(len(self.q.queue))
+ self.yar=np.array(self.q.queue)
+ self.axes.plot(self.xar,self.yar)
+
+ def init_plot(self):
+ self.dpi = 100
+ self.fig = Figure((3.0, 3.0), dpi=self.dpi)
+ self.fig.set_size_inches(7.0,4.0)
+ self.fig.set_dpi(self.dpi)
+
+ self.axes = self.fig.add_subplot(111)
+ self.axes.set_axis_bgcolor('black')
+ self.axes.set_title(self.title, size=12)
+
+ pylab.setp(self.axes.get_xticklabels(), fontsize=8)
+ pylab.setp(self.axes.get_yticklabels(), fontsize=8)
+ self.i_window = self.gsz
+ self.i_start = 0
+ self.i_end = self.i_start + self.i_window
+ # plot the data as a line series, and save the reference
+ # to the plotted line series
+ #
+ self.plot_data = self.axes.plot(
+ [],
+ linewidth=1,
+ color=(1, 1, 0),
+ )[0]
+
+
+ def draw_plot(self,event):
+ """ Redraws the plot
+ """
+ if len(list(self.q.queue))>1 and not self.paused:
+
+ if self.zoom:
+ xmax = len(list(self.q.queue)) if len(list(self.q.queue)) > 50 else 50
+
+ xmin = xmax - 50
+ # for ymin and ymax, find the minimal and maximal values
+ # in the data set and add a mininal margin.
+ #
+ # note that it's easy to change this scheme to the
+ # minimal/maximal value in the current display, and not
+ # the whole data set.
+ #
+ ymin = round(min(list(self.q.queue)), 0) - 1
+
+ ymax = round(max(list(self.q.queue)), 0) + 1
+
+ self.axes.set_xbound(lower=xmin, upper=xmax)
+ self.axes.set_ybound(lower=ymin, upper=ymax)
+
+ # anecdote: axes.grid assumes b=True if any other flag is
+ # given even if b is set to False.
+ # so just passing the flag into the first statement won't
+ # work.
+ #
+ if self.cb_grid.IsChecked():
+ self.axes.grid(True, color='gray')
+ else:
+ self.axes.grid(False)
+
+ # Using setp here is convenient, because get_xticklabels
+ # returns a list over which one needs to explicitly
+ # iterate, and setp already handles this.
+ #
+ pylab.setp(self.axes.get_xticklabels(),
+ visible=self.cb_xlab.IsChecked())
+
+
+ self.plot_data.set_xdata(np.arange(len(list(self.q.queue))))
+ self.plot_data.set_ydata(np.array(list(self.q.queue)))
+ self.canvas.draw()
+
+ else:
+ if self.cb_grid.IsChecked():
+ self.axes.grid(True, color='gray')
+ else:
+ self.axes.grid(False)
+
+ # Using setp here is convenient, because get_xticklabels
+ # returns a list over which one needs to explicitly
+ # iterate, and setp already handles this.
+
+ pylab.setp(self.axes.get_xticklabels(),
+ visible=self.cb_xlab.IsChecked())
+
+ self.plot_data.set_xdata(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end])
+ self.plot_data.set_ydata(np.array(list(self.q.queue))[self.i_start:self.i_end])
+ self.axes.set_xlim(min(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end]),max(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end]))
+ # if self.zoom:
+ self.axes.set_ylim(min(np.array(list(self.q.queue))),max(np.array(list(self.q.queue))))
+
+ self.canvas.draw()
+
+
+
+ def on_pause_button(self, event):
+ self.paused = not self.paused
+
+ def on_update_pause_button(self, event):
+ label = "Resume" if self.paused else "Pause"
+ self.pause_button.SetLabel(label)
+
+ def on_cb_grid(self, event):
+ self.draw_plot(0)
+
+ def on_cb_xlab(self, event):
+ self.draw_plot(0)
+
+ def on_save_plot(self, event):
+ file_choices = "PNG (*.png)|*.png"
+
+ dlg = wx.FileDialog(
+ self,
+ message="Save plot as...",
+ defaultDir=os.getcwd(),
+ defaultFile="plot.png",
+ wildcard=file_choices,
+ style=wx.SAVE)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ self.canvas.print_figure(path, dpi=self.dpi)
+ self.flash_status_message("Saved to %s" % path)
+
+ def on_redraw_timer(self, event):
+ # if paused do not add data, but still redraw the plot
+ # (to respond to scale modifications, grid change, etc.)
+ #
+ if not self.paused:
+ self.data += self.datagen.next()
+ self.draw_plot(0)
+
+
+ def on_exit(self, event):
+ self.Destroy()
+
+ def flash_status_message(self, msg, flash_len_ms=1500):
+ self.statusbar.SetStatusText(msg)
+ self.timeroff = wx.Timer(self)
+ self.Bind(
+ wx.EVT_TIMER,
+ self.on_flash_status_off,
+ self.timeroff)
+ self.timeroff.Start(flash_len_ms, oneShot=True)
+
+ def on_flash_status_off(self, event):
+ self.statusbar.SetStatusText('')
+
+
+
+
+
diff --git a/gr-input/python/plot_sink.py b/gr-input/python/plot_sink.py
new file mode 100755
index 000000000..1c1def3fc
--- /dev/null
+++ b/gr-input/python/plot_sink.py
@@ -0,0 +1,57 @@
+# Hacked from blks2/variable_sink.py
+# Requires modified Matplotsink code
+
+from gnuradio import gr
+import threading
+import numpy
+import matplotsink
+import Queue
+
+class _plot_sink_base(gr.hier_block2, threading.Thread):
+ """
+ The thread polls the message queue for values and writes to matplotsink callback
+ """
+
+ def __init__(self, parent, title, vlen, decim,gsz,zoom):
+ self._vlen = vlen
+ self._parent = parent
+ self._title = title
+ print "Initing block: %s" % title
+
+ self.plotQueue = Queue.Queue()
+ self.win = matplotsink.matplotsink(parent,title, self.plotQueue,gsz,zoom)
+
+ self._item_size = self._size*self._vlen
+ #init hier block
+ gr.hier_block2.__init__(
+ self, 'plot_sink',
+ gr.io_signature(1, 1, self._item_size),
+ gr.io_signature(0, 0, 0),
+ )
+ #create blocks
+ self._msgq = gr.msg_queue(2)
+ message_sink = gr.message_sink(self._item_size, self._msgq, False)
+ #connect
+ self.connect(self, message_sink)
+ #setup thread
+ threading.Thread.__init__(self)
+ self.setDaemon(True)
+ self.start()
+
+ def set_decim(self, decim): self._decimator.set_n(decim)
+
+ def run(self):
+ while True: #truncate to item size, convert to array, callback
+ msg = self._msgq.delete_head().to_string()[-self._item_size:]
+ arr = map(self._cast, numpy.fromstring(msg, self._numpy))
+ print "Sending value:" , arr
+ self.plotQueue.put(self._vlen > 1 and arr or arr[0])
+
+ def print_callback(self, array):
+ print array
+
+class plot_sink_b(_plot_sink_base): _numpy, _size, _cast = numpy.int8, gr.sizeof_char, int
+class plot_sink_s(_plot_sink_base): _numpy, _size, _cast = numpy.int16, gr.sizeof_short, int
+class plot_sink_i(_plot_sink_base): _numpy, _size, _cast = numpy.int32, gr.sizeof_int, int
+class plot_sink_f(_plot_sink_base): _numpy, _size, _cast = numpy.float32, gr.sizeof_float, float
+class plot_sink_c(_plot_sink_base): _numpy, _size, _cast = numpy.complex64, gr.sizeof_gr_complex, complex