aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xpython/dpdce.py63
-rwxr-xr-xpython/gui/api.py9
-rw-r--r--python/gui/static/js/odr-predistortion.js18
-rw-r--r--python/gui/templates/predistortion.html39
4 files changed, 98 insertions, 31 deletions
diff --git a/python/dpdce.py b/python/dpdce.py
index f76b0f6..90cd436 100755
--- a/python/dpdce.py
+++ b/python/dpdce.py
@@ -80,6 +80,7 @@ else:
logging.info("DPDCE starting up");
+import time
import socket
from lib import yamlrpc
import numpy as np
@@ -238,9 +239,9 @@ def engine_worker():
# Extract usable data from measurement
tx, rx, phase_diff, n_per_bin = extStat.extract(txframe_aligned, rxframe_aligned)
- time = datetime.datetime.utcnow()
- plot_file = "stats_{}.png".format(time.strftime("%s"))
- extStat.plot(os.path.join(plot_path, plot_file), time.strftime("%Y-%m-%dT%H%M%S"))
+ utctime = datetime.datetime.utcnow()
+ plot_file = "stats_{}.png".format(utctime.strftime("%s"))
+ extStat.plot(os.path.join(plot_path, plot_file), utctime.strftime("%Y-%m-%dT%H%M%S"))
n_meas = Heuristics.get_n_meas(n_runs)
with lock:
@@ -248,7 +249,6 @@ def engine_worker():
results['stateprogress'] += 5
results['summary'] += ["Extracted Statistics: TX median={} RX median={}".format(tx_median, rx_median),
"Runs: {}/{}".format(extStat.n_meas, n_meas)]
- internal_data['n_runs'] += 1
if extStat.n_meas >= n_meas:
break
@@ -265,11 +265,11 @@ def engine_worker():
model.train(tx, rx, phase_diff, lr=Heuristics.get_learning_rate(n_runs))
- time = datetime.datetime.utcnow()
- model_plot_file = "model_{}.png".format(time.strftime("%s"))
+ utctime = datetime.datetime.utcnow()
+ model_plot_file = "model_{}.png".format(utctime.strftime("%s"))
model.plot(
os.path.join(plot_path, model_plot_file),
- time.strftime("%Y-%m-%dT%H%M%S"))
+ utctime.strftime("%Y-%m-%dT%H%M%S"))
with lock:
results['modelplot'] = "dpd/" + model_plot_file
@@ -293,6 +293,47 @@ def engine_worker():
results['state'] = 'Idle'
results['stateprogress'] = 100
results['summary'] += ["New DPD coefficients calculated"]
+ elif cmd == "adapt":
+ with lock:
+ dpddata = internal_data['dpddata']
+ results['state'] = 'Update Predistorter'
+ results['stateprogress'] = 50
+ results['summary'] = [""]
+ iteration = internal_data['n_runs']
+ internal_data['n_runs'] += 1
+
+ adapt.set_predistorter(dpddata)
+
+ time.sleep(2)
+
+ txframe_aligned, tx_ts, rxframe_aligned, rx_ts, rx_median, tx_median = meas.get_samples()
+
+ # Store all settings for pre-distortion, tx and rx
+ adapt.dump()
+
+ # Collect logging data
+ off = symbol_align.calc_offset(txframe_aligned)
+ tx_mer = mer.calc_mer(txframe_aligned[off:off + c.T_U], debug_name='TX')
+ rx_mer = mer.calc_mer(rxframe_aligned[off:off + c.T_U], debug_name='RX')
+ mse = np.mean(np.abs((txframe_aligned - rxframe_aligned) ** 2))
+ tx_gain = adapt.get_txgain()
+ rx_gain = adapt.get_rxgain()
+ digital_gain = adapt.get_digital_gain()
+ rx_shoulder_tuple = meas_shoulders.average_shoulders(rxframe_aligned)
+ tx_shoulder_tuple = meas_shoulders.average_shoulders(txframe_aligned)
+
+ lr = Heuristics.get_learning_rate(iteration)
+
+ summary = [f"Signal measurements after iteration {iteration} with learning rate {lr}",
+ f"TX MER {tx_mer}, RX MER {rx_mer}",
+ "Shoulders: TX {!r}, RX {!r}".format(tx_shoulder_tuple, rx_shoulder_tuple),
+ f"Mean-square error: {mse}",
+ f"Running with digital gain {digital_gain}, TX gain {tx_gain} and RX gain {rx_gain}"]
+
+ with lock:
+ results['state'] = 'Update Predistorter'
+ results['stateprogress'] = 100
+ results['summary'] = ["Signal measurements after predistortion update"] + summary
finally:
with lock:
results['state'] = 'Terminated'
@@ -318,13 +359,9 @@ try:
logging.error('YAML-RPC unknown error')
break
- if method == 'trigger_run':
- logging.info('YAML-RPC request : {}'.format(method))
- command_queue.put('trigger_run')
- cmd_socket.send_success_response(addr, msg_id, None)
- elif method == 'reset':
+ if any(method == m for m in ['trigger_run', 'reset', 'adapt']):
logging.info('YAML-RPC request : {}'.format(method))
- command_queue.put('reset')
+ command_queue.put(method)
cmd_socket.send_success_response(addr, msg_id, None)
elif method == 'set_setting':
logging.info('YAML-RPC request : {} -> {}'.format(method, params))
diff --git a/python/gui/api.py b/python/gui/api.py
index bff224e..42c89c9 100755
--- a/python/gui/api.py
+++ b/python/gui/api.py
@@ -107,6 +107,15 @@ class API:
@cherrypy.expose
@cherrypy.tools.json_out()
+ def dpd_adapt(self, **kwargs):
+ if cherrypy.request.method == 'POST':
+ return self._wrap_dpd("adapt")
+ else:
+ cherrypy.response.status = 400
+ return send_error("POST only")
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
def dpd_reset(self, **kwargs):
if cherrypy.request.method == 'POST':
return self._wrap_dpd("reset")
diff --git a/python/gui/static/js/odr-predistortion.js b/python/gui/static/js/odr-predistortion.js
index f46219c..b5f29ea 100644
--- a/python/gui/static/js/odr-predistortion.js
+++ b/python/gui/static/js/odr-predistortion.js
@@ -67,17 +67,25 @@ $(function(){
});
});
+ $('#triggerbtn').click(function() {
+ doApiRequestPOST("/api/dpd_trigger_run", {}, function(data) {
+ console.log("run succeeded: " + JSON.stringify(data));
+ });
+ });
+
+ $('#adaptbtn').click(function() {
+ doApiRequestPOST("/api/dpd_adapt", {}, function(data) {
+ console.log("adapt succeeded: " + JSON.stringify(data));
+ });
+ });
+
+
$('#resetbtn').click(function() {
doApiRequestPOST("/api/dpd_reset", {}, function(data) {
console.log("reset succeeded: " + JSON.stringify(data));
});
});
- $('#triggerbtn').click(function() {
- doApiRequestPOST("/api/dpd_trigger_run", {}, function(data) {
- console.log("run succeeded: " + JSON.stringify(data));
- });
- });
});
/*
diff --git a/python/gui/templates/predistortion.html b/python/gui/templates/predistortion.html
index d102101..8682054 100644
--- a/python/gui/templates/predistortion.html
+++ b/python/gui/templates/predistortion.html
@@ -11,28 +11,41 @@
<div class="panel panel-default">
<div class="panel-heading">Status and calibration</div>
<div class="panel-body">
- <div>Current DPDCE status:
- <div>
- <div id="dpdstatus" style="font-weight:bold;">N/A</div>
- <div class="progress">
- <div id="dpdprogress" class="progress-bar" role="progressbar" style="width:0%">
- <span id="dpdprogresstext"></span>
+
+ <div class="container-fluid">
+ <div class="row">
+ <div class="col-sm-8">
+ <h2>Current DPDCE status</h2>
+ <div>
+ <div id="dpdstatus" style="font-weight:bold;">N/A</div>
+ <div class="progress">
+ <div id="dpdprogress" class="progress-bar" role="progressbar" style="width:0%">
+ <span id="dpdprogresstext"></span>
+ </div>
+ </div>
</div>
+ <div class="well well-sm" id="dpdresults">N/A</div>
+ </div>
+ <div class="col-sm-4">
+ <h2>Summary</h2>
+ <p>Calibration needs to be done once before the PA model
+ can be trained. Every time calibration is changed, the predistortion
+ parameters are invalidated!</p>
+ <p>Once calibration succeeded and correct RX gain is set, you
+ can trigger a capture and model the PA. Usually, several capture
+ runs are needed before the model can be trained.</p>
+ <p>The capture and model analysis will calculate a new set of
+ DPD model data, that you can apply using the Update Predistorter button.</p>
</div>
</div>
- <div class="well well-sm" id="dpdresults">N/A</div>
</div>
- <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>
- <div>Once calibration succeeded and correct RX gain is set, you
- can trigger a capture and model the PA. Usually, several capture
- runs are needed before the model can be trained.</div>
<button type="button" class="btn btn-sm btn-warning" id="calibratebtn">
Calibrate</button>
<button type="button" class="btn btn-sm btn-warning" id="triggerbtn">
Trigger Capture and PA Modeling</button>
+ <button type="button" class="btn btn-sm btn-warning" id="adaptbtn">
+ Update Predistorter</button>
<button type="button" class="btn btn-sm btn-info" id="resetbtn">
Reset captured data</button>
</div>