From ddb4c1d744b417e79cf2d263d6cd8db37c4dbcb2 Mon Sep 17 00:00:00 2001
From: Johnathan Corgan <johnathan@corganlabs.com>
Date: Tue, 6 May 2014 13:40:15 -0700
Subject: zeromq: cleanup docs and examples

---
 gr-zeromq/examples/python/README         |  71 +++++++++++
 gr-zeromq/examples/python/client.py      | 116 ++++++++++++++++++
 gr-zeromq/examples/python/fixui4py.sh    |   2 +
 gr-zeromq/examples/python/gui.py         | 164 +++++++++++++++++++++++++
 gr-zeromq/examples/python/main_window.ui | 199 +++++++++++++++++++++++++++++++
 gr-zeromq/examples/python/run_app.sh     |   4 +
 gr-zeromq/examples/python/server.py      | 125 +++++++++++++++++++
 7 files changed, 681 insertions(+)
 create mode 100644 gr-zeromq/examples/python/README
 create mode 100755 gr-zeromq/examples/python/client.py
 create mode 100755 gr-zeromq/examples/python/fixui4py.sh
 create mode 100755 gr-zeromq/examples/python/gui.py
 create mode 100644 gr-zeromq/examples/python/main_window.ui
 create mode 100755 gr-zeromq/examples/python/run_app.sh
 create mode 100755 gr-zeromq/examples/python/server.py

(limited to 'gr-zeromq/examples/python')

diff --git a/gr-zeromq/examples/python/README b/gr-zeromq/examples/python/README
new file mode 100644
index 0000000000..2bc2dc8a52
--- /dev/null
+++ b/gr-zeromq/examples/python/README
@@ -0,0 +1,71 @@
+THIS IS EXPERIMENTAL SOFTWARE AND API IS SUBJECT TO CHANGE
+
+How to run the example
+----------------------
+
+Assuming that the module has been compiled but not installed, in the
+gr-zeromq folder do
+
+    cd examples
+    ./run_app.sh server
+
+on another terminal or machine
+
+    ./run_app.sh client -s hostname
+
+You can also run a (remote) GUI on any of the two or a third machine for monitoring and control.
+
+    ./run_app.sh gui.py -s servername -c hostname
+
+in doing so the order of starting the scripts is arbitrary. When installing the
+module, the run\_app.sh script is of course not needed.
+
+
+How to use the API
+------------------
+
+### PROBE API
+Connect a zmq pubsub sink to the block you want to monitor
+
+        self.zmq_probe = zeromq.sink_pubsub(gr.sizeof_float, "tcp://*:5556")
+
+add a probe manager to your Python GUI
+
+        # ZeroMQ
+        probe_manager = zeromq.probe_manager()
+        probe_manager.add_socket("tcp://localhost:5556",
+                                 'float32', self.plot_data)
+
+        def plot_data(self,samples):
+            [...]
+
+basically creates a watcher thread that calls the call back functions and
+unpacks sample data. Now you can use a timer to update the plot, e.g. in PyQt
+
+        update_timer = Qt.QTimer()
+        self.connect(update_timer,
+                     QtCore.SIGNAL("timeout()"),
+                     probe_manager.watcher)
+        update_timer.start(30)
+
+### RPC API
+Add an rpc manager to your Python app to receive RPCs
+
+        rpc_manager = zeromq.rpc_manager()
+        rpc_manager.set_reply_socket("tcp://*:6666")
+        rpc_manager.add_interface("start_fg",self.start)
+        rpc_manager.start_watcher()
+
+to be able to send requests also add one on the other side
+
+        rpc_manager = zeromq.rpc_manager()
+        rpc_manager.set_request_socket("tcp://localhost:6666")
+
+send a request
+
+        rpc_mganager.request("start_fg")
+        rpc_mgr_server.request("set_k",gain)
+
+RPCs use GNU Radio pmt's to serialize arguments, the watcher thread will
+regularly poll for incoming RPC requests, deserializes arguments and call the
+interface callback function accordingly.
diff --git a/gr-zeromq/examples/python/client.py b/gr-zeromq/examples/python/client.py
new file mode 100755
index 0000000000..3d55cc9a98
--- /dev/null
+++ b/gr-zeromq/examples/python/client.py
@@ -0,0 +1,116 @@
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio.
+#
+# This is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+###############################################################################
+# Imports
+###############################################################################
+from gnuradio import zeromq
+#import zeromq
+from gnuradio import gr
+from gnuradio import blocks
+from gnuradio import analog
+from gnuradio import eng_notation
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+import numpy
+import sys
+from threading import Thread
+import time
+
+###############################################################################
+# GNU Radio top_block
+###############################################################################
+class top_block(gr.top_block):
+    def __init__(self, options):
+        gr.top_block.__init__(self)
+
+        self.options = options
+
+        # socket addresses
+        rpc_adr = "tcp://*:6667"
+        probe_adr = "tcp://*:5557"
+        source_adr = "tcp://"+self.options.servername+":5555"
+
+        # blocks
+        self.zmq_source = zeromq.req_source(gr.sizeof_float, 1, source_adr)
+        #self.zmq_source = zeromq.pull_source(gr.sizeof_float, 1, source_adr)
+        #self.zmq_probe = zeromq.push_sink(gr.sizeof_float,probe_adr)
+        self.zmq_probe = zeromq.pub_sink(gr.sizeof_float,probe_adr)
+
+        # connects
+        self.connect(self.zmq_source, self.zmq_probe)
+
+        # ZeroMQ
+        self.rpc_manager = zeromq.rpc_manager()
+        self.rpc_manager.set_reply_socket(rpc_adr)
+        self.rpc_manager.add_interface("start_fg",self.start_fg)
+        self.rpc_manager.add_interface("stop_fg",self.stop_fg)
+        self.rpc_manager.start_watcher()
+
+    def start_fg(self):
+        print "Start Flowgraph"
+        try:
+            self.start()
+        except RuntimeError:
+            print "Can't start, flowgraph already running!"
+
+    def stop_fg(self):
+        print "Stop Flowgraph"
+        self.stop()
+        self.wait()
+
+###############################################################################
+# Options Parser
+###############################################################################
+def parse_options():
+    """ Options parser. """
+    parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
+    parser.add_option("-s", "--servername", type="string", default="localhost",
+                      help="Server hostname")
+    (options, args) = parser.parse_args()
+    return options
+
+###############################################################################
+# Waiter Thread
+###############################################################################
+class waiter(Thread):
+    """ To keep the program alive when flowgraph is stopped. """
+    def run(self):
+        while keep_running:
+            time.sleep(1)
+
+###############################################################################
+# Main
+###############################################################################
+if __name__ == "__main__":
+    options = parse_options()
+    tb = top_block(options)
+    try:
+        # keep the program running when flowgraph is stopped
+        while True:
+            time.sleep(1)
+    except KeyboardInterrupt:
+        pass
+    print "Shutting down flowgraph."
+    tb.rpc_manager.stop_watcher()
+    tb.stop()
+    tb.wait()
+    tb = None
diff --git a/gr-zeromq/examples/python/fixui4py.sh b/gr-zeromq/examples/python/fixui4py.sh
new file mode 100755
index 0000000000..d2978000bf
--- /dev/null
+++ b/gr-zeromq/examples/python/fixui4py.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+sed -i 's/qwt_plot.h/PyQt4.Qwt5.Qwt/' $1
diff --git a/gr-zeromq/examples/python/gui.py b/gr-zeromq/examples/python/gui.py
new file mode 100755
index 0000000000..91223dd734
--- /dev/null
+++ b/gr-zeromq/examples/python/gui.py
@@ -0,0 +1,164 @@
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio.
+#
+# This is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+###############################################################################
+# Imports
+###############################################################################
+from optparse import OptionParser
+from gnuradio.eng_option import eng_option
+import gui
+import sys
+import os
+from PyQt4 import Qt, QtGui, QtCore, uic
+import PyQt4.Qwt5 as Qwt
+from gnuradio import zeromq
+import signal
+
+class gui(QtGui.QMainWindow):
+    def __init__(self, window_name, options, parent=None):
+        QtGui.QMainWindow.__init__(self, parent)
+
+        # give Ctrl+C back to system
+        signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+        self.gui = uic.loadUi(os.path.join(os.path.dirname(__file__),'main_window.ui'), self)
+
+        self.update_timer = Qt.QTimer()
+
+        # socket addresses
+        rpc_adr_server = "tcp://"+options.servername+":6666"
+        rpc_adr_client = "tcp://"+options.clientname+":6667"
+        probe_adr_server = "tcp://"+options.servername+":5556"
+        probe_adr_client = "tcp://"+options.clientname+":5557"
+
+        # ZeroMQ
+        self.probe_manager = zeromq.probe_manager()
+        self.probe_manager.add_socket(probe_adr_server, 'float32', self.plot_data_server)
+        self.probe_manager.add_socket(probe_adr_client, 'float32', self.plot_data_client)
+
+        self.rpc_mgr_server = zeromq.rpc_manager()
+        self.rpc_mgr_server.set_request_socket(rpc_adr_server)
+        self.rpc_mgr_client = zeromq.rpc_manager()
+        self.rpc_mgr_client.set_request_socket(rpc_adr_client)
+
+        self.gui.setWindowTitle(window_name)
+        self.gui.qwtPlotServer.setTitle("Signal Scope")
+        self.gui.qwtPlotServer.setAxisTitle(Qwt.QwtPlot.xBottom, "Samples")
+        self.gui.qwtPlotServer.setAxisTitle(Qwt.QwtPlot.yLeft, "Amplitude")
+        self.gui.qwtPlotServer.setAxisScale(Qwt.QwtPlot.xBottom, 0, 100)
+        self.gui.qwtPlotServer.setAxisScale(Qwt.QwtPlot.yLeft, -2, 2)
+        self.gui.qwtPlotClient.setTitle("Signal Scope")
+        self.gui.qwtPlotClient.setAxisTitle(Qwt.QwtPlot.xBottom, "Samples")
+        self.gui.qwtPlotClient.setAxisTitle(Qwt.QwtPlot.yLeft, "Amplitude")
+        self.gui.qwtPlotClient.setAxisScale(Qwt.QwtPlot.xBottom, 0, 100)
+        self.gui.qwtPlotClient.setAxisScale(Qwt.QwtPlot.yLeft, -2, 2)
+
+        # Grid
+        pen = Qt.QPen(Qt.Qt.DotLine)
+        pen.setColor(Qt.Qt.black)
+        pen.setWidth(0)
+        grid_server = Qwt.QwtPlotGrid()
+        grid_client = Qwt.QwtPlotGrid()
+        grid_server.setPen(pen)
+        grid_client.setPen(pen)
+        grid_server.attach(self.gui.qwtPlotServer)
+        grid_client.attach(self.gui.qwtPlotClient)
+
+        #Signals
+        self.connect(self.update_timer, QtCore.SIGNAL("timeout()"), self.probe_manager.watcher)
+        self.connect(self.gui.pushButtonRunServer, QtCore.SIGNAL("clicked()"), self.start_fg_server)
+        self.connect(self.gui.pushButtonStopServer, QtCore.SIGNAL("clicked()"), self.stop_fg_server)
+        self.connect(self.gui.pushButtonRunClient, QtCore.SIGNAL("clicked()"), self.start_fg_client)
+        self.connect(self.gui.pushButtonStopClient, QtCore.SIGNAL("clicked()"), self.stop_fg_client)
+        self.connect(self.gui.comboBox, QtCore.SIGNAL("currentIndexChanged(QString)"), self.set_waveform)
+        self.connect(self.gui.spinBox, QtCore.SIGNAL("valueChanged(int)"), self.set_gain)
+        self.shortcut_start = QtGui.QShortcut(Qt.QKeySequence("Ctrl+S"), self.gui)
+        self.shortcut_stop = QtGui.QShortcut(Qt.QKeySequence("Ctrl+C"), self.gui)
+        self.shortcut_exit = QtGui.QShortcut(Qt.QKeySequence("Ctrl+D"), self.gui)
+        self.connect(self.shortcut_exit, QtCore.SIGNAL("activated()"), self.gui.close)
+
+        # start update timer
+        self.update_timer.start(30)
+
+    def start_fg_server(self):
+        self.rpc_mgr_server.request("start_fg")
+
+    def stop_fg_server(self):
+        self.rpc_mgr_server.request("stop_fg")
+
+    def start_fg_client(self):
+        self.rpc_mgr_client.request("start_fg")
+
+    def stop_fg_client(self):
+        self.rpc_mgr_client.request("stop_fg")
+
+    # plot the data from the queues
+    def plot_data(self, plot, samples):
+        self.x = range(0,len(samples),1)
+        self.y = samples
+        # clear the previous points from the plot
+        plot.clear()
+        # draw curve with new points and plot
+        curve = Qwt.QwtPlotCurve()
+        curve.setPen(Qt.QPen(Qt.Qt.blue, 2))
+        curve.attach(plot)
+        curve.setData(self.x, self.y)
+        plot.replot()
+
+    def plot_data_server(self, samples):
+        self.plot_data(self.gui.qwtPlotServer, samples)
+
+    def plot_data_client(self, samples):
+        self.plot_data(self.gui.qwtPlotClient, samples)
+
+    def set_waveform(self, waveform_str):
+        self.rpc_mgr_server.request("set_waveform",str(waveform_str))
+
+    def set_gain(self, gain):
+        self.rpc_set_gain(gain)
+
+    def rpc_set_gain(self, gain):
+        self.rpc_mgr_server.request("set_k",gain)
+
+###############################################################################
+# Options Parser
+###############################################################################
+def parse_options():
+    """ Options parser. """
+    parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
+    parser.add_option("-s", "--servername", type="string", default="localhost",
+                      help="Server hostname")
+    parser.add_option("-c", "--clientname", type="string", default="localhost",
+                      help="Server hostname")
+    (options, args) = parser.parse_args()
+    return options
+
+
+###############################################################################
+# Main
+###############################################################################
+if __name__ == "__main__":
+    options = parse_options()
+    qapp = Qt.QApplication(sys.argv)
+    qapp.main_window = gui("Remote GNU Radio GUI",options)
+    qapp.main_window.show()
+    qapp.exec_()
+
diff --git a/gr-zeromq/examples/python/main_window.ui b/gr-zeromq/examples/python/main_window.ui
new file mode 100644
index 0000000000..14f810e67d
--- /dev/null
+++ b/gr-zeromq/examples/python/main_window.ui
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ControlPort_Example</class>
+ <widget class="QMainWindow" name="ControlPort_Example">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1000</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>ControlPort Example</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="2">
+     <widget class="QGroupBox" name="groupBox_2">
+      <property name="title">
+       <string>Client</string>
+      </property>
+      <layout class="QGridLayout" name="gridLayout_3">
+       <item row="1" column="0">
+        <widget class="QPushButton" name="pushButtonRunClient">
+         <property name="text">
+          <string>Run</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0" colspan="2">
+        <widget class="QwtPlot" name="qwtPlotClient">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QPushButton" name="pushButtonStopClient">
+         <property name="text">
+          <string>Stop</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="0" column="1" rowspan="4">
+     <widget class="Line" name="line">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="0">
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Waveform:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="comboBox">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="currentIndex">
+         <number>1</number>
+        </property>
+        <item>
+         <property name="text">
+          <string>Constant</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Sine</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Cosine</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Square</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Triangle</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Saw Tooth</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Gain:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="spinBox">
+        <property name="value">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="0">
+     <widget class="QGroupBox" name="groupBox">
+      <property name="title">
+       <string>Server</string>
+      </property>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="1" column="0">
+        <widget class="QPushButton" name="pushButtonRunServer">
+         <property name="text">
+          <string>Run</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QPushButton" name="pushButtonStopServer">
+         <property name="text">
+          <string>Stop</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0" colspan="2">
+        <widget class="QwtPlot" name="qwtPlotServer">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="1" column="0">
+     <widget class="Line" name="line_2">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="2">
+     <widget class="Line" name="line_3">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1000</width>
+     <height>25</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QwtPlot</class>
+   <extends>QFrame</extends>
+   <header>PyQt4.Qwt5.Qwt</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gr-zeromq/examples/python/run_app.sh b/gr-zeromq/examples/python/run_app.sh
new file mode 100755
index 0000000000..4af936ca82
--- /dev/null
+++ b/gr-zeromq/examples/python/run_app.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+export LD_LIBRARY_PATH="$PWD/../../build/gr-zeromq/lib"
+export PYTHONPATH="$PWD/../../build/gr-zeromq/swig:$PWD/../../python"
+/usr/bin/python $1 $2 $3 $4 $5 $6 $7 $8 $9
diff --git a/gr-zeromq/examples/python/server.py b/gr-zeromq/examples/python/server.py
new file mode 100755
index 0000000000..28b732ed3e
--- /dev/null
+++ b/gr-zeromq/examples/python/server.py
@@ -0,0 +1,125 @@
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio.
+#
+# This is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+###############################################################################
+# Imports
+###############################################################################
+from gnuradio import zeromq
+from gnuradio import gr
+from gnuradio import blocks
+from gnuradio import analog
+from gnuradio import eng_notation
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+import numpy
+import sys
+from threading import Thread
+import time
+
+
+###############################################################################
+# GNU Radio top_block
+###############################################################################
+class top_block(gr.top_block):
+    def __init__(self, options):
+        gr.top_block.__init__(self)
+
+        self.options = options
+
+        # socket addresses
+        rpc_adr = "tcp://*:6666"
+        probe_adr = "tcp://*:5556"
+        sink_adr = "tcp://*:5555"
+
+        # the strange sampling rate gives a nice movement in the plot :P
+        self.samp_rate = samp_rate = 48200
+
+        # blocks
+        self.gr_sig_source = analog.sig_source_f(samp_rate, analog.GR_SIN_WAVE , 1000, 1, 0)
+        self.throttle = blocks.throttle(gr.sizeof_float, samp_rate)
+        self.mult = blocks.multiply_const_ff(1)
+        self.zmq_sink = zeromq.rep_sink(gr.sizeof_float, 1, sink_adr)
+        #self.zmq_sink = zeromq.push_sink(gr.sizeof_float, 1, sink_adr)
+        #self.zmq_probe = zeromq.push_sink(gr.sizeof_float, probe_adr)
+        self.zmq_probe = zeromq.pub_sink(gr.sizeof_float, probe_adr)
+        #self.null_sink = blocks.null_sink(gr.sizeof_float)
+
+        # connects
+        self.connect(self.gr_sig_source, self.mult, self.throttle, self.zmq_sink)
+        self.connect(self.throttle, self.zmq_probe)
+
+        # ZeroMQ
+        self.rpc_manager = zeromq.rpc_manager()
+        self.rpc_manager.set_reply_socket(rpc_adr)
+        self.rpc_manager.add_interface("start_fg",self.start_fg)
+        self.rpc_manager.add_interface("stop_fg",self.stop_fg)
+        self.rpc_manager.add_interface("set_waveform",self.set_waveform)
+        self.rpc_manager.add_interface("set_k",self.mult.set_k)
+        self.rpc_manager.add_interface("get_sample_rate",self.throttle.sample_rate)
+        self.rpc_manager.start_watcher()
+
+    def start_fg(self):
+        print "Start Flowgraph"
+        try:
+            self.start()
+        except RuntimeError:
+            print "Can't start, flowgraph already running!"
+
+    def stop_fg(self):
+        print "Stop Flowgraph"
+        self.stop()
+        self.wait()
+
+    def set_waveform(self, waveform_str):
+        waveform = {'Constant' : analog.GR_CONST_WAVE,
+                    'Sine' : analog.GR_SIN_WAVE,
+                    'Cosine' : analog.GR_COS_WAVE,
+                    'Square' : analog.GR_SQR_WAVE,
+                    'Triangle' : analog.GR_TRI_WAVE,
+                    'Saw Tooth' : analog.GR_SAW_WAVE}[waveform_str]
+        self.gr_sig_source.set_waveform(waveform)
+
+###############################################################################
+# Options Parser
+###############################################################################
+def parse_options():
+    """ Options parser. """
+    parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
+    (options, args) = parser.parse_args()
+    return options
+
+###############################################################################
+# Main
+###############################################################################
+if __name__ == "__main__":
+    options = parse_options()
+    tb = top_block(options)
+    try:
+        # keep the program running when flowgraph is stopped
+        while True:
+            time.sleep(1)
+    except KeyboardInterrupt:
+        pass
+    print "Shutting down flowgraph."
+    tb.rpc_manager.stop_watcher()
+    tb.stop()
+    tb.wait()
+    tb = None
-- 
cgit v1.2.3