aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2018-11-28 15:04:41 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2018-11-28 15:04:41 +0100
commit8b42d3115db2ecec9031c5d1421463b0191e055c (patch)
tree66c1a9d5864c478ca5026a681963e81db6142e0f
parentcfa9461f269e616d6d54658d583b37d215f35a7b (diff)
downloaddabmod-8b42d3115db2ecec9031c5d1421463b0191e055c.tar.gz
dabmod-8b42d3115db2ecec9031c5d1421463b0191e055c.tar.bz2
dabmod-8b42d3115db2ecec9031c5d1421463b0191e055c.zip
Use multiprocessing for DPD functionality
-rwxr-xr-xgui/api/__init__.py55
-rw-r--r--gui/dpd/Capture.py2
-rw-r--r--gui/dpd/__init__.py5
-rwxr-xr-xgui/run.py92
-rw-r--r--gui/static/js/odr-predistortion.js2
5 files changed, 87 insertions, 69 deletions
diff --git a/gui/api/__init__.py b/gui/api/__init__.py
index 56062c3..c1d73a0 100755
--- a/gui/api/__init__.py
+++ b/gui/api/__init__.py
@@ -21,7 +21,6 @@
# along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
import cherrypy
-from cherrypy.process import wspbus, plugins
from cherrypy.lib.httputil import parse_query_string
import urllib
@@ -29,6 +28,7 @@ import os
import io
import datetime
+import threading
def send_ok(data=None):
if data is not None:
@@ -42,26 +42,36 @@ def send_error(reason=""):
else:
return {'status' : 'error'}
-class API(plugins.SimplePlugin):
- def __init__(self, mod_rc, bus):
- plugins.SimplePlugin.__init__(self, bus)
+class RXThread(threading.Thread):
+ def __init__(self, api):
+ super(RXThread, self).__init__()
+ self.api = api
+ self.running = False
+ self.daemon = True
+
+ def cancel(self):
+ self.running = False
+
+ def run(self):
+ self.running = True
+ while self.running:
+ if self.api.dpd_pipe.poll(1):
+ rx = self.api.dpd_pipe.recv()
+ if rx['cmd'] == "quit":
+ break
+ elif rx['cmd'] == "dpd-state":
+ self.api.dpd_state = rx['data']
+ elif rx['cmd'] == "dpd-calibration-result":
+ self.api.calibration_result = rx['data']
+
+class API:
+ def __init__(self, mod_rc, dpd_pipe):
self.mod_rc = mod_rc
+ self.dpd_pipe = dpd_pipe
self.dpd_state = None
self.calibration_result = None
-
- def start(self):
- self.bus.subscribe("dpd-state", self.dpd_state)
- self.bus.subscribe("dpd-calibration-result", self.calibration_result)
-
- def stop(self):
- self.bus.unsubscribe("dpd-state", self.dpd_state)
- self.bus.unsubscribe("dpd-calibration-result", self.calibration_result)
-
- def calibration_result(self, new_result):
- self.calibration_result = new_result
-
- def dpd_state(self, new_state):
- self.dpd_state = new_state
+ self.receive_thread = RXThread(self)
+ self.receive_thread.start()
@cherrypy.expose
def index(self):
@@ -93,7 +103,7 @@ class API(plugins.SimplePlugin):
@cherrypy.tools.json_out()
def trigger_capture(self, **kwargs):
if cherrypy.request.method == 'POST':
- cherrypy.engine.publish('dpd-capture', None)
+ self.dpd_pipe.send({'cmd': "dpd-capture"})
return send_ok()
else:
cherrypy.response.status = 400
@@ -109,12 +119,13 @@ class API(plugins.SimplePlugin):
@cherrypy.expose
@cherrypy.tools.json_out()
- def dpd_calibrate(self, **kwargs):
+ def calibrate(self, **kwargs):
if cherrypy.request.method == 'POST':
- cherrypy.engine.publish('dpd-calibrate', None)
+ self.dpd_pipe.send({'cmd': "dpd-calibrate"})
return send_ok()
else:
- if self.dpd_state is not None:
+ if self.calibration_result is not None:
+ print("cal result", repr(self.calibration_result))
return send_ok(self.calibration_result)
else:
return send_error("DPD calibration result unknown")
diff --git a/gui/dpd/Capture.py b/gui/dpd/Capture.py
index 4c0e99c..3b2988b 100644
--- a/gui/dpd/Capture.py
+++ b/gui/dpd/Capture.py
@@ -172,7 +172,7 @@ class Capture:
rxframe = rxframe * self.rx_normalisation
txframe_aligned, rxframe_aligned, coarse_offset = align_samples(txframe, rxframe)
- return tx_ts, tx_median, rx_ts, rx_median, coarse_offset, correlation_coefficient(txframe_aligned, rxframe_aligned)
+ return tx_ts, tx_median, rx_ts, rx_median, np.abs(coarse_offset), correlation_coefficient(txframe_aligned, rxframe_aligned)
def get_samples(self):
"""Connect to ODR-DabMod, retrieve TX and RX samples, load
diff --git a/gui/dpd/__init__.py b/gui/dpd/__init__.py
index 716b8c2..85abe86 100644
--- a/gui/dpd/__init__.py
+++ b/gui/dpd/__init__.py
@@ -57,12 +57,13 @@ class DPD:
def capture_calibration(self):
tx_ts, tx_median, rx_ts, rx_median, coarse_offset, correlation_coefficient = self.capture.calibrate()
result = {'status': "ok"}
- result['length'] = len(txframe_aligned)
result['tx_median'] = "{:.2}dB".format(20*np.log10(tx_median))
result['rx_median'] = "{:.2}dB".format(20*np.log10(rx_median))
result['tx_ts'] = tx_ts
result['rx_ts'] = rx_ts
- result['correlation'] = correlation_coefficient
+ result['coarse_offset'] = int(coarse_offset)
+ result['correlation'] = float(correlation_coefficient)
+ return result
def capture_samples(self):
"""Captures samples and store them in the accumulated samples,
diff --git a/gui/run.py b/gui/run.py
index dea3222..7162d70 100755
--- a/gui/run.py
+++ b/gui/run.py
@@ -22,9 +22,9 @@
# along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
import configuration
+from multiprocessing import Process, Pipe
import os.path
import cherrypy
-from cherrypy.process import wspbus, plugins
import argparse
from jinja2 import Environment, FileSystemLoader
from api import API
@@ -36,12 +36,11 @@ env = Environment(loader=FileSystemLoader('templates'))
base_js = ["js/odr.js"]
class Root:
- def __init__(self, config_file):
+ def __init__(self, config_file, dpd_pipe):
self.config_file = config_file
self.conf = configuration.Configuration(self.config_file)
self.mod_rc = zmqrc.ModRemoteControl("localhost")
- self.api = API(self.mod_rc, cherrypy.engine)
- self.api.subscribe()
+ self.api = API(self.mod_rc, dpd_pipe)
@cherrypy.expose
def index(self):
@@ -75,24 +74,32 @@ class Root:
js = base_js + ["js/odr-predistortion.js"]
return tmpl.render(tab='predistortion', js=js, is_login=False)
-class DPDPlugin(plugins.SimplePlugin):
- def __init__(self, bus):
- plugins.SimplePlugin.__init__(self, bus)
+class DPDRunner:
+ def __init__(self):
+ self.web_end, self.dpd_end = Pipe()
self.dpd = dpd.DPD()
- def start(self):
- self.bus.subscribe("dpd-capture", self.trigger_capture)
- self.bus.subscribe("dpd-calibrate", self.trigger_calibrate)
+ def __enter__(self):
+ self.p = Process(target=self._handle_messages)
+ self.p.start()
+ return self.web_end
- def stop(self):
- self.bus.unsubscribe("dpd-capture", self.trigger_capture)
- self.bus.unsubscribe("dpd-calibrate", self.trigger_calibrate)
+ def _handle_messages(self):
+ while True:
+ rx = self.dpd_end.recv()
+ if rx['cmd'] == "quit":
+ break
+ elif rx['cmd'] == "dpd-capture":
+ self.dpd.capture_samples()
+ elif rx['cmd'] == "dpd-calibrate":
+ self.dpd_end.send({'cmd': "dpd-calibration-result",
+ 'data': self.dpd.capture_calibration()})
- def trigger_capture(self, param):
- print("trigger_capture({})".format(param))
- def trigger_calibrate(self, param):
- cherrypy.engine.publish('dpd-calibration-result', self.dpd.capture_calibration())
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.web_end.send({'cmd': "quit"})
+ self.p.join()
+ return False
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='ODR-DabMod Web GUI')
@@ -127,30 +134,29 @@ if __name__ == '__main__':
staticdir = os.path.realpath(config.config['global']['static_directory'])
- DPDPlugin(cherrypy.engine).subscribe()
-
- cherrypy.tree.mount(
- Root(cli_args.config), config={
- '/': { },
- '/css': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': os.path.join(staticdir, u"css/")
- },
- '/js': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': os.path.join(staticdir, u"js/")
- },
- '/fonts': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': os.path.join(staticdir, u"fonts/")
- },
- '/favicon.ico': {
- 'tools.staticfile.on': True,
- 'tools.staticfile.filename': os.path.join(staticdir, u"fonts/favicon.ico")
- },
- }
- )
-
- cherrypy.engine.start()
- cherrypy.engine.block()
+ with DPDRunner() as dpd_pipe:
+ cherrypy.tree.mount(
+ Root(cli_args.config, dpd_pipe), config={
+ '/': { },
+ '/css': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': os.path.join(staticdir, u"css/")
+ },
+ '/js': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': os.path.join(staticdir, u"js/")
+ },
+ '/fonts': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': os.path.join(staticdir, u"fonts/")
+ },
+ '/favicon.ico': {
+ 'tools.staticfile.on': True,
+ 'tools.staticfile.filename': os.path.join(staticdir, u"fonts/favicon.ico")
+ },
+ }
+ )
+
+ cherrypy.engine.start()
+ cherrypy.engine.block()
diff --git a/gui/static/js/odr-predistortion.js b/gui/static/js/odr-predistortion.js
index 01992d7..e13fb88 100644
--- a/gui/static/js/odr-predistortion.js
+++ b/gui/static/js/odr-predistortion.js
@@ -19,7 +19,7 @@
// along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
$(function(){
- $('#calibrate').click(function() {
+ $('#calibratebtn').click(function() {
doApiRequestPOST("/api/calibrate", {}, function(data) {
console.log("calibrate succeeded: " + JSON.stringify(data));