aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2018-11-28 11:11:22 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2018-11-28 11:11:22 +0100
commitcfa9461f269e616d6d54658d583b37d215f35a7b (patch)
treebc56977f6479c297521dff9564ba6ecbffe00a52
parentee435c029eac59e0399dc3ae765cc74d66b9442e (diff)
downloaddabmod-cfa9461f269e616d6d54658d583b37d215f35a7b.tar.gz
dabmod-cfa9461f269e616d6d54658d583b37d215f35a7b.tar.bz2
dabmod-cfa9461f269e616d6d54658d583b37d215f35a7b.zip
GUI: Add part of calibration routine
-rwxr-xr-xgui/api/__init__.py19
-rw-r--r--gui/dpd/Capture.py51
-rw-r--r--gui/dpd/__init__.py14
-rwxr-xr-xgui/run.py5
-rw-r--r--gui/static/js/odr-predistortion.js12
-rw-r--r--gui/templates/predistortion.html20
6 files changed, 96 insertions, 25 deletions
diff --git a/gui/api/__init__.py b/gui/api/__init__.py
index a535eb3..56062c3 100755
--- a/gui/api/__init__.py
+++ b/gui/api/__init__.py
@@ -47,15 +47,20 @@ class API(plugins.SimplePlugin):
plugins.SimplePlugin.__init__(self, bus)
self.mod_rc = mod_rc
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):
- print("API got new dpd-state {}".format(new_state))
self.dpd_state = new_state
@cherrypy.expose
@@ -102,3 +107,15 @@ class API(plugins.SimplePlugin):
else:
return send_error("DPD state unknown")
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ def dpd_calibrate(self, **kwargs):
+ if cherrypy.request.method == 'POST':
+ cherrypy.engine.publish('dpd-calibrate', None)
+ return send_ok()
+ else:
+ if self.dpd_state is not None:
+ 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 e2ac63d..4c0e99c 100644
--- a/gui/dpd/Capture.py
+++ b/gui/dpd/Capture.py
@@ -36,6 +36,9 @@ import io
from . import Align as sa
+def correlation_coefficient(sig_tx, sig_rx):
+ return np.corrcoef(sig_tx, sig_rx)[0, 1]
+
def align_samples(sig_tx, sig_rx):
"""
Returns an aligned version of sig_tx and sig_rx by cropping, subsample alignment and
@@ -61,7 +64,7 @@ def align_samples(sig_tx, sig_rx):
# Fine subsample alignment and phase offset
sig_rx = sa.subsample_align(sig_rx, sig_tx)
sig_rx = sa.phase_align(sig_rx, sig_tx)
- return sig_tx, sig_rx
+ return sig_tx, sig_rx, abs(off_meas)
class Capture:
"""Capture samples from ODR-DabMod"""
@@ -76,14 +79,16 @@ class Capture:
# samples to avoid that the polynomial gets overfitted in the low-amplitude
# part, which is less interesting than the high-amplitude part, where
# non-linearities become apparent.
- self.binning_start = 0.0
- self.binning_end = 1.0
self.binning_n_bins = 64 # Number of bins between binning_start and binning_end
self.binning_n_per_bin = 128 # Number of measurements pre bin
- self.target_median = 0.05
- self.median_max = self.target_median * 1.4
- self.median_min = self.target_median / 1.4
+ self.rx_normalisation = 1.0
+
+ self.clear_accumulated()
+
+ def clear_accumulated(self):
+ self.binning_start = 0.0
+ self.binning_end = 1.0
# axis 0: bins
# axis 1: 0=tx, 1=rx
@@ -156,30 +161,32 @@ class Capture:
return txframe, tx_ts, rxframe, rx_ts
- def get_samples(self):
- """Connect to ODR-DabMod, retrieve TX and RX samples, load
- into numpy arrays, and return a tuple
- (txframe_aligned, tx_ts, tx_median, rxframe_aligned, rx_ts, rx_median)
- """
-
+ def calibrate(self):
txframe, tx_ts, rxframe, rx_ts = self.receive_tcp()
# Normalize received signal with sent signal
tx_median = np.median(np.abs(txframe))
+ rx_median = np.median(np.abs(rxframe))
+ self.rx_normalisation = tx_median / rx_median
- if self.median_max < tx_median:
- raise ValueError("TX median {} too high, decrease digital_gain!".format(tx_median))
- elif tx_median < self.median_min:
- raise ValueError("TX median {} too low, increase digital_gain!".format(tx_median))
- else:
- rx_median = np.median(np.abs(rxframe))
- rxframe = rxframe / rx_median * tx_median
+ rxframe = rxframe * self.rx_normalisation
+ txframe_aligned, rxframe_aligned, coarse_offset = align_samples(txframe, rxframe)
- txframe_aligned, rxframe_aligned = align_samples(txframe, rxframe)
+ return tx_ts, tx_median, rx_ts, rx_median, coarse_offset, correlation_coefficient(txframe_aligned, rxframe_aligned)
+
+ def get_samples(self):
+ """Connect to ODR-DabMod, retrieve TX and RX samples, load
+ into numpy arrays, and return a tuple
+ (txframe_aligned, tx_ts, tx_median, rxframe_aligned, rx_ts, rx_median)
+ """
- self._bin_and_accumulate(txframe_aligned, rxframe_aligned)
+ txframe, tx_ts, rxframe, rx_ts = self.receive_tcp()
- return txframe_aligned, tx_ts, tx_median, rxframe_aligned, rx_ts, rx_median
+ # Normalize received signal with calibrated normalisation
+ rxframe = rxframe * self.rx_normalisation
+ txframe_aligned, rxframe_aligned, coarse_offset = align_samples(txframe, rxframe)
+ self._bin_and_accumulate(txframe_aligned, rxframe_aligned)
+ return txframe_aligned, tx_ts, tx_median, rxframe_aligned, rx_ts, rx_median
def bin_histogram(self):
return [b.shape[0] for b in self.accumulated_bins]
diff --git a/gui/dpd/__init__.py b/gui/dpd/__init__.py
index 06d180d..716b8c2 100644
--- a/gui/dpd/__init__.py
+++ b/gui/dpd/__init__.py
@@ -23,6 +23,7 @@
# along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
from . import Capture
+import numpy as np
class DPD:
def __init__(self, samplerate=8192000):
@@ -50,6 +51,19 @@ class DPD:
def pointcloud_png(self):
return self.capture.pointcloud_png()
+ def clear_accumulated(self):
+ return self.capture.clear_accumulated()
+
+ 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
+
def capture_samples(self):
"""Captures samples and store them in the accumulated samples,
returns a dict with some info"""
diff --git a/gui/run.py b/gui/run.py
index b4af742..dea3222 100755
--- a/gui/run.py
+++ b/gui/run.py
@@ -82,13 +82,18 @@ class DPDPlugin(plugins.SimplePlugin):
def start(self):
self.bus.subscribe("dpd-capture", self.trigger_capture)
+ self.bus.subscribe("dpd-calibrate", self.trigger_calibrate)
def stop(self):
self.bus.unsubscribe("dpd-capture", self.trigger_capture)
+ self.bus.unsubscribe("dpd-calibrate", self.trigger_calibrate)
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())
+
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='ODR-DabMod Web GUI')
parser.add_argument('-c', '--config',
diff --git a/gui/static/js/odr-predistortion.js b/gui/static/js/odr-predistortion.js
index 4e86f1c..01992d7 100644
--- a/gui/static/js/odr-predistortion.js
+++ b/gui/static/js/odr-predistortion.js
@@ -19,6 +19,18 @@
// along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
$(function(){
+ $('#calibrate').click(function() {
+ doApiRequestPOST("/api/calibrate", {}, function(data) {
+ console.log("calibrate succeeded: " + JSON.stringify(data));
+
+ setTimeout(function() {
+ doApiRequestGET("/api/calibrate", function(data) {
+ $('#calibrationresults').text(data);
+ });
+ }, 2000);
+ });
+ });
+
$('#capturebutton').click(function() {
doApiRequestPOST("/api/trigger_capture", {}, function(data) {
console.log("trigger_capture succeeded: " + JSON.stringify(data));
diff --git a/gui/templates/predistortion.html b/gui/templates/predistortion.html
index ed224d8..139d982 100644
--- a/gui/templates/predistortion.html
+++ b/gui/templates/predistortion.html
@@ -29,16 +29,31 @@ along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
<div class="container-fluid">
<div class="panel-group">
<div class="panel panel-default">
+ <div class="panel-heading">Calibration</div>
+ <div class="panel-body">
+ <div>Calibration needs to be done once before the PA model
+ can be trained. Every time calibration is changed, the predistortion
+ parameters are invalidated!</div>
+ <button type="button" class="btn btn-sm btn-info" id="calibratebtn">
+ Calibrate</button>
+ <div>Calibration results:<span id="calibrationresults">N/A<span></div>
+ </div>
+ </div>
+
+ <!--
+ <div class="panel panel-default">
+ <div class="panel-heading">Capture</div>
<div class="panel-body">
- <h3>Capture</h3>
<div>On pressing this button,
the DPDCE will trigger a capture and a quick data
analysis, without updating any DPD models.</div>
<button type="button" class="btn btn-sm btn-info" id="capturebutton">
Capture</button>
</div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading">Status</div>
<div class="panel-body">
- <h3>Status</h3>
<button type="button" class="btn btn-sm btn-info" id="dpdstatusbutton">
Update</button>
<div>Histogram: <span id="histogram">N/A</span></div>
@@ -51,6 +66,7 @@ along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
<div>Point cloud: <img id="dpd_pointcloud" /></div>
</div>
</div>
+ -->
</div>
</div>
</body>