From e83e1324a50055a4b972b78e26383df7ee290fee Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 18 Dec 2018 16:26:17 +0100 Subject: GUI: add capture and plot to DPD --- python/dpd/ExtractStatistic.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'python/dpd/ExtractStatistic.py') diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index 639513a..1aa4391 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -41,6 +41,8 @@ class ExtractStatistic: def __init__(self, c): self.c = c + self._plot_data = None + # Number of measurements used to extract the statistic self.n_meas = 0 @@ -58,12 +60,10 @@ class ExtractStatistic: for i in range(c.ES_n_bins): self.tx_values_lists.append([]) - self.plot = c.ES_plot + def plot(self, plot_path, title): + if self._plot_data is not None: + tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists = self._plot_data - def _plot_and_log(self, tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists): - if self.plot and self.c.plot_location is not None: - dt = datetime.datetime.now().isoformat() - fig_path = self.c.plot_location + "/" + dt + "_ExtractStatistic.png" sub_rows = 3 sub_cols = 1 fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) @@ -80,7 +80,7 @@ class ExtractStatistic: np.abs(rx_values_list), s=0.1, color="black") - ax.set_title("Extracted Statistic") + ax.set_title("Extracted Statistic {}".format(title)) ax.set_xlabel("TX Amplitude") ax.set_ylabel("RX Amplitude") ax.set_ylim(0, 0.8) @@ -116,7 +116,7 @@ class ExtractStatistic: ax.set_ylim(0, self.n_per_bin * 1.2) fig.tight_layout() - fig.savefig(fig_path) + fig.savefig(plot_path) plt.close(fig) def _rx_value_per_bin(self): @@ -166,7 +166,7 @@ class ExtractStatistic: phase_diffs_values_lists = self._phase_diff_list_per_bin() phase_diffs_values = _phase_diff_value_per_bin(phase_diffs_values_lists) - self._plot_and_log(tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists) + self._plot_data = (tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists) tx_values_crop = np.array(tx_values, dtype=np.float32)[:idx_end] rx_values_crop = np.array(rx_values, dtype=np.float32)[:idx_end] @@ -176,6 +176,7 @@ class ExtractStatistic: # The MIT License (MIT) # # Copyright (c) 2017 Andreas Steger +# Copyright (c) 2018 Matthias P. Braendli # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal -- cgit v1.2.3 From f4ca82137e850e30d31e7008b34800d8b2699e5d Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 19 Dec 2018 16:11:58 +0100 Subject: DPD: Merge Model_PM and _AM into _Poly --- python/dpd/ExtractStatistic.py | 8 +- python/dpd/GlobalConfig.py | 9 +- python/dpd/Model_AM.py | 119 ------------------------ python/dpd/Model_PM.py | 121 ------------------------- python/dpd/Model_Poly.py | 146 ++++++++++++++++++++++++++---- python/dpd/RX_Agc.py | 18 ++-- python/dpdce.py | 41 ++++++--- python/gui/static/js/odr-predistortion.js | 10 ++ python/gui/templates/predistortion.html | 5 + 9 files changed, 190 insertions(+), 287 deletions(-) delete mode 100644 python/dpd/Model_AM.py delete mode 100644 python/dpd/Model_PM.py (limited to 'python/dpd/ExtractStatistic.py') diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index 1aa4391..a23fa1a 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -38,7 +38,7 @@ class ExtractStatistic: """Calculate a low variance RX value for equally spaced tx values of a predefined range""" - def __init__(self, c): + def __init__(self, c, peak_amplitude): self.c = c self._plot_data = None @@ -47,7 +47,7 @@ class ExtractStatistic: self.n_meas = 0 # Boundaries for the bins - self.tx_boundaries = np.linspace(c.ES_start, c.ES_end, c.ES_n_bins + 1) + self.tx_boundaries = np.linspace(0.0, peak_amplitude, c.ES_n_bins + 1) self.n_per_bin = c.ES_n_per_bin # List of rx values for each bin @@ -60,6 +60,10 @@ class ExtractStatistic: for i in range(c.ES_n_bins): self.tx_values_lists.append([]) + def get_bin_info(self): + return "Binning: {} bins used for amplitudes between {} and {}".format( + len(self.tx_boundaries), np.min(self.tx_boundaries), np.max(self.tx_boundaries)) + def plot(self, plot_path, title): if self._plot_data is not None: tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists = self._plot_data diff --git a/python/dpd/GlobalConfig.py b/python/dpd/GlobalConfig.py index 99280f2..632a63b 100644 --- a/python/dpd/GlobalConfig.py +++ b/python/dpd/GlobalConfig.py @@ -26,6 +26,8 @@ class GlobalConfig: self.T_U = oversample * 2048 # Inverse of carrier spacing self.T_C = oversample * 504 # Duration of cyclic prefix + self.median_to_peak = 12 # Estimated value for a DAB OFDM signal + # Frequency Domain # example: np.delete(fft[3328:4865], 768) self.FFT_delta = 1536 # Number of carrier frequencies @@ -40,10 +42,8 @@ class GlobalConfig: self.phase_offset_per_sample = 1. / self.sample_rate * 2 * np.pi * 1000 # Constants for ExtractStatistic - self.ES_plot = plot - self.ES_start = 0.0 self.ES_end = 1.0 - self.ES_n_bins = 64 # Number of bins between ES_start and ES_end + self.ES_n_bins = 64 self.ES_n_per_bin = 128 # Number of measurements pre bin # Constants for Measure_Shoulder @@ -68,9 +68,6 @@ class GlobalConfig: # Constants for MER self.MER_plot = plot - # Constants for Model - self.MDL_plot = plot - # Constants for Model_PM # Set all phase offsets to zero for TX amplitude < MPM_tx_min self.MPM_tx_min = 0.1 diff --git a/python/dpd/Model_AM.py b/python/dpd/Model_AM.py deleted file mode 100644 index b07a5a5..0000000 --- a/python/dpd/Model_AM.py +++ /dev/null @@ -1,119 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DPD Computation Engine, model implementation for Amplitude and not Phase -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -import datetime -import os -import logging -import numpy as np -import matplotlib.pyplot as plt - - -def is_npfloat32(array): - assert isinstance(array, np.ndarray), type(array) - assert array.dtype == np.float32, array.dtype - assert array.flags.contiguous - assert not any(np.isnan(array)) - - -def check_input_get_next_coefs(tx_dpd, rx_received): - is_npfloat32(tx_dpd) - is_npfloat32(rx_received) - - -def poly(sig): - return np.array([sig ** i for i in range(1, 6)]).T - - -def fit_poly(tx_abs, rx_abs): - return np.linalg.lstsq(poly(rx_abs), tx_abs, rcond=None)[0] - - -def calc_line(coefs, min_amp, max_amp): - rx_range = np.linspace(min_amp, max_amp) - tx_est = np.sum(poly(rx_range) * coefs, axis=1) - return tx_est, rx_range - - -class Model_AM: - """Calculates new coefficients using the measurement and the previous - coefficients""" - - def __init__(self, c, learning_rate_am=1): - self.c = c - self.learning_rate_am = learning_rate_am - self._plot_data = None - - def plot(self, plot_location, title): - if self._plot_data is not None: - tx_dpd, rx_received, coefs_am, coefs_am_new = self._plot_data - - tx_range, rx_est = calc_line(coefs_am, 0, 0.6) - tx_range_new, rx_est_new = calc_line(coefs_am_new, 0, 0.6) - - sub_rows = 1 - sub_cols = 1 - fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) - i_sub = 0 - - i_sub += 1 - ax = plt.subplot(sub_rows, sub_cols, i_sub) - ax.plot(tx_range, rx_est, - label="Estimated TX", - alpha=0.3, - color="gray") - ax.plot(tx_range_new, rx_est_new, - label="New Estimated TX", - color="red") - ax.scatter(tx_dpd, rx_received, - label="Binned Data", - color="blue", - s=1) - ax.set_title("Model_AM {}".format(title)) - ax.set_xlabel("TX Amplitude") - ax.set_ylabel("RX Amplitude") - ax.set_xlim(-0.5, 1.5) - ax.legend(loc=4) - - fig.tight_layout() - fig.savefig(plot_location) - plt.close(fig) - - def get_next_coefs(self, tx_dpd, rx_received, coefs_am): - """Calculate the next AM/AM coefficients using the extracted - statistic of TX and RX amplitude""" - check_input_get_next_coefs(tx_dpd, rx_received) - - coefs_am_new = fit_poly(tx_dpd, rx_received) - coefs_am_new = coefs_am + \ - self.learning_rate_am * (coefs_am_new - coefs_am) - - self._plot_data = (tx_dpd, rx_received, coefs_am, coefs_am_new) - - return coefs_am_new - -# The MIT License (MIT) -# -# Copyright (c) 2017 Andreas Steger -# Copyright (c) 2018 Matthias P. Braendli -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. diff --git a/python/dpd/Model_PM.py b/python/dpd/Model_PM.py deleted file mode 100644 index 40fa1d4..0000000 --- a/python/dpd/Model_PM.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DPD Computation Engine, model implementation for Amplitude and not Phase -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -import datetime -import os -import logging -import numpy as np -import matplotlib.pyplot as plt - - -def is_npfloat32(array): - assert isinstance(array, np.ndarray), type(array) - assert array.dtype == np.float32, array.dtype - assert array.flags.contiguous - assert not any(np.isnan(array)) - - -def check_input_get_next_coefs(tx_dpd, phase_diff): - is_npfloat32(tx_dpd) - is_npfloat32(phase_diff) - - -class Model_PM: - """Calculates new coefficients using the measurement and the previous - coefficients""" - - def __init__(self, c, learning_rate_pm=1): - self.c = c - self.learning_rate_pm = learning_rate_pm - self._plot_data = None - - def plot(self, plot_location, title): - if self._plot_data is not None: - tx_dpd, phase_diff, coefs_pm, coefs_pm_new = self._plot_data - - tx_range, phase_diff_est = self.calc_line(coefs_pm, 0, 0.6) - tx_range_new, phase_diff_est_new = self.calc_line(coefs_pm_new, 0, 0.6) - - sub_rows = 1 - sub_cols = 1 - fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) - i_sub = 0 - - i_sub += 1 - ax = plt.subplot(sub_rows, sub_cols, i_sub) - ax.plot(tx_range, phase_diff_est, - label="Estimated Phase Diff", - alpha=0.3, - color="gray") - ax.plot(tx_range_new, phase_diff_est_new, - label="New Estimated Phase Diff", - color="red") - ax.scatter(tx_dpd, phase_diff, - label="Binned Data", - color="blue", - s=1) - ax.set_title("Model_PM {}".format(title)) - ax.set_xlabel("TX Amplitude") - ax.set_ylabel("Phase DIff") - ax.legend(loc=4) - - fig.tight_layout() - fig.savefig(plot_location) - plt.close(fig) - - def _discard_small_values(self, tx_dpd, phase_diff): - """ Assumes that the phase for small tx amplitudes is zero""" - mask = tx_dpd < self.c.MPM_tx_min - phase_diff[mask] = 0 - return tx_dpd, phase_diff - - def poly(self, sig): - return np.array([sig ** i for i in range(0, 5)]).T - - def fit_poly(self, tx_abs, phase_diff): - return np.linalg.lstsq(self.poly(tx_abs), phase_diff, rcond=None)[0] - - def calc_line(self, coefs, min_amp, max_amp): - tx_range = np.linspace(min_amp, max_amp) - phase_diff = np.sum(self.poly(tx_range) * coefs, axis=1) - return tx_range, phase_diff - - def get_next_coefs(self, tx_dpd, phase_diff, coefs_pm): - """Calculate the next AM/PM coefficients using the extracted - statistic of TX amplitude and phase difference""" - tx_dpd, phase_diff = self._discard_small_values(tx_dpd, phase_diff) - check_input_get_next_coefs(tx_dpd, phase_diff) - - coefs_pm_new = self.fit_poly(tx_dpd, phase_diff) - - coefs_pm_new = coefs_pm + self.learning_rate_pm * (coefs_pm_new - coefs_pm) - self._plot_data = (tx_dpd, phase_diff, coefs_pm, coefs_pm_new) - - return coefs_pm_new - -# The MIT License (MIT) -# -# Copyright (c) 2017 Andreas Steger -# Copyright (c) 2018 Matthias P. Braendli -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. diff --git a/python/dpd/Model_Poly.py b/python/dpd/Model_Poly.py index ca39492..5722531 100644 --- a/python/dpd/Model_Poly.py +++ b/python/dpd/Model_Poly.py @@ -8,15 +8,13 @@ import os import logging import numpy as np +import matplotlib.pyplot as plt -import dpd.Model_AM as Model_AM -import dpd.Model_PM as Model_PM - - -def assert_np_float32(x): - assert isinstance(x, np.ndarray) - assert x.dtype == np.float32 - assert x.flags.contiguous +def assert_np_float32(array): + assert isinstance(array, np.ndarray), type(array) + assert array.dtype == np.float32, array.dtype + assert array.flags.contiguous + assert not any(np.isnan(array)) def _check_input_get_next_coefs(tx_abs, rx_abs, phase_diff): @@ -44,12 +42,73 @@ class Poly: self.reset_coefs() - self.model_am = Model_AM.Model_AM(c) - self.model_pm = Model_PM.Model_PM(c) - def plot(self, am_plot_location, pm_plot_location, title): - self.model_am.plot(am_plot_location, title) - self.model_pm.plot(pm_plot_location, title) + if self._am_plot_data is not None: + tx_dpd, rx_received, coefs_am, coefs_am_new = self._am_plot_data + + tx_range, rx_est = self._am_calc_line(coefs_am, 0, 0.6) + tx_range_new, rx_est_new = self._am_calc_line(coefs_am_new, 0, 0.6) + + sub_rows = 1 + sub_cols = 1 + fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) + i_sub = 0 + + i_sub += 1 + ax = plt.subplot(sub_rows, sub_cols, i_sub) + ax.plot(tx_range, rx_est, + label="Estimated TX", + alpha=0.3, + color="gray") + ax.plot(tx_range_new, rx_est_new, + label="New Estimated TX", + color="red") + ax.scatter(tx_dpd, rx_received, + label="Binned Data", + color="blue", + s=1) + ax.set_title("Model AM {}".format(title)) + ax.set_xlabel("TX Amplitude") + ax.set_ylabel("RX Amplitude") + ax.set_xlim(-0.5, 1.5) + ax.legend(loc=4) + + fig.tight_layout() + fig.savefig(am_plot_location) + plt.close(fig) + + if self._pm_plot_data is not None: + tx_dpd, phase_diff, coefs_pm, coefs_pm_new = self._pm_plot_data + + tx_range, phase_diff_est = self._pm_calc_line(coefs_pm, 0, 0.6) + tx_range_new, phase_diff_est_new = self._pm_calc_line(coefs_pm_new, 0, 0.6) + + sub_rows = 1 + sub_cols = 1 + fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) + i_sub = 0 + + i_sub += 1 + ax = plt.subplot(sub_rows, sub_cols, i_sub) + ax.plot(tx_range, phase_diff_est, + label="Estimated Phase Diff", + alpha=0.3, + color="gray") + ax.plot(tx_range_new, phase_diff_est_new, + label="New Estimated Phase Diff", + color="red") + ax.scatter(tx_dpd, phase_diff, + label="Binned Data", + color="blue", + s=1) + ax.set_title("Model PM {}".format(title)) + ax.set_xlabel("TX Amplitude") + ax.set_ylabel("Phase DIff") + ax.legend(loc=4) + + fig.tight_layout() + fig.savefig(pm_plot_location) + plt.close(fig) def reset_coefs(self): self.coefs_am = np.zeros(5, dtype=np.float32) @@ -65,12 +124,8 @@ class Poly: """ _check_input_get_next_coefs(tx_abs, rx_abs, phase_diff) - if not lr is None: - self.model_am.learning_rate_am = lr - self.model_pm.learning_rate_pm = lr - - coefs_am_new = self.model_am.get_next_coefs(tx_abs, rx_abs, self.coefs_am) - coefs_pm_new = self.model_pm.get_next_coefs(tx_abs, phase_diff, self.coefs_pm) + coefs_am_new = self._am_get_next_coefs(tx_abs, rx_abs, self.coefs_am) + coefs_pm_new = self._pm_get_next_coefs(tx_abs, phase_diff, self.coefs_pm) self.coefs_am = self.coefs_am + (coefs_am_new - self.coefs_am) * self.learning_rate_am self.coefs_pm = self.coefs_pm + (coefs_pm_new - self.coefs_pm) * self.learning_rate_pm @@ -78,9 +133,62 @@ class Poly: def get_dpd_data(self): return "poly", self.coefs_am, self.coefs_pm + def _am_calc_line(self, coefs, min_amp, max_amp): + rx_range = np.linspace(min_amp, max_amp) + tx_est = np.sum(self._am_poly(rx_range) * coefs, axis=1) + return tx_est, rx_range + + def _am_poly(self, sig): + return np.array([sig ** i for i in range(1, 6)]).T + + def _am_fit_poly(self, tx_abs, rx_abs): + return np.linalg.lstsq(self._am_poly(rx_abs), tx_abs, rcond=None)[0] + + def _am_get_next_coefs(self, tx_dpd, rx_received, coefs_am): + """Calculate the next AM/AM coefficients using the extracted + statistic of TX and RX amplitude""" + + coefs_am_new = self._am_fit_poly(tx_dpd, rx_received) + coefs_am_new = coefs_am + \ + self.learning_rate_am * (coefs_am_new - coefs_am) + + self._am_plot_data = (tx_dpd, rx_received, coefs_am, coefs_am_new) + + return coefs_am_new + + def _pm_poly(self, sig): + return np.array([sig ** i for i in range(0, 5)]).T + + def _pm_calc_line(self, coefs, min_amp, max_amp): + tx_range = np.linspace(min_amp, max_amp) + phase_diff = np.sum(self._pm_poly(tx_range) * coefs, axis=1) + return tx_range, phase_diff + + def _discard_small_values(self, tx_dpd, phase_diff): + """ Assumes that the phase for small tx amplitudes is zero""" + mask = tx_dpd < self.c.MPM_tx_min + phase_diff[mask] = 0 + return tx_dpd, phase_diff + + def _pm_fit_poly(self, tx_abs, phase_diff): + return np.linalg.lstsq(self._pm_poly(tx_abs), phase_diff, rcond=None)[0] + + def _pm_get_next_coefs(self, tx_dpd, phase_diff, coefs_pm): + """Calculate the next AM/PM coefficients using the extracted + statistic of TX amplitude and phase difference""" + tx_dpd, phase_diff = self._discard_small_values(tx_dpd, phase_diff) + + coefs_pm_new = self._pm_fit_poly(tx_dpd, phase_diff) + + coefs_pm_new = coefs_pm + self.learning_rate_pm * (coefs_pm_new - coefs_pm) + self._pm_plot_data = (tx_dpd, phase_diff, coefs_pm, coefs_pm_new) + + return coefs_pm_new + # The MIT License (MIT) # # Copyright (c) 2017 Andreas Steger +# Copyright (c) 2018 Matthias P. Brandli # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/python/dpd/RX_Agc.py b/python/dpd/RX_Agc.py index 4700e68..911f3c9 100644 --- a/python/dpd/RX_Agc.py +++ b/python/dpd/RX_Agc.py @@ -19,19 +19,19 @@ import dpd.Adapt as Adapt import dpd.Measure as Measure class Agc: - """The goal of the automatic gain control is to set the - RX gain to a value at which all received amplitudes can - be detected. This means that the maximum possible amplitude + """The goal of the automatic gain control is to set the + RX gain to a value at which all received amplitudes can + be detected. This means that the maximum possible amplitude should be quantized at the highest possible digital value. - A problem we have to face, is that the estimation of the - maximum amplitude by applying the max() function is very - unstable. This is due to the maximum’s rareness. Therefore - we estimate a far more robust value, such as the median, + A problem we have to face, is that the estimation of the + maximum amplitude by applying the max() function is very + unstable. This is due to the maximum’s rareness. Therefore + we estimate a far more robust value, such as the median, and then approximate the maximum amplitude from it. - Given this, we tune the RX gain in such a way, that the - received signal fulfills our desired property, of having + Given this, we tune the RX gain in such a way, that the + received signal fulfills our desired property, of having all amplitudes properly quantized.""" def __init__(self, measure, adapt, c): diff --git a/python/dpdce.py b/python/dpdce.py index e601d9c..18e628b 100755 --- a/python/dpdce.py +++ b/python/dpdce.py @@ -85,6 +85,7 @@ from lib import yamlrpc import numpy as np import traceback import os.path +import glob from threading import Thread, Lock from queue import Queue from dpd.Model import Poly @@ -156,8 +157,19 @@ command_queue = Queue(maxsize=1) # Automatic Gain Control for the RX gain agc = Agc(meas, adapt, c) +def clear_pngs(results): + results['statplot'] = None + results['amplot'] = None + results['pmplot'] = None + pngs = glob.glob(os.path.join(plot_path, "*.png")) + for png in pngs: + try: + os.remove(png) + except: + results['summary'] += ["failed to delete " + png] + def engine_worker(): - extStat = ExtractStatistic(c) + extStat = None try: while True: cmd = command_queue.get() @@ -168,12 +180,13 @@ def engine_worker(): with lock: results['state'] = 'RX Gain Calibration' results['stateprogress'] = 0 + clear_pngs(results) summary = [] N_ITER = 5 for i in range(N_ITER): agc_success, agc_summary = agc.run() - summary += ["calibration run {}:".format(i)] + agc_summary.split("\n") + summary += ["Iteration {}:".format(i)] + agc_summary.split("\n") with lock: results['stateprogress'] = int((i + 1) * 100/N_ITER) @@ -191,14 +204,16 @@ def engine_worker(): results['rx_median'] = float(rx_median) results['state'] = 'Idle' results['stateprogress'] = 100 - results['summary'] = ["Calibration was done:"] + summary + results['summary'] = summary + ["Calibration done"] elif cmd == "reset": with lock: internal_data['n_runs'] = 0 results['state'] = 'Idle' results['stateprogress'] = 0 results['summary'] = ["Reset"] - extStat = ExtractStatistic(c) + clear_pngs(results) + extStat = None + model.reset_coefs() elif cmd == "trigger_run": with lock: results['state'] = 'Capture + Model' @@ -208,12 +223,17 @@ def engine_worker(): while True: # Get Samples and check gain txframe_aligned, tx_ts, rxframe_aligned, rx_ts, rx_median, tx_median = meas.get_samples() - # TODO Check TX median + + if extStat is None: + # At first run, we must decide how to create the bins + peak_estimated = tx_median * c.median_to_peak + extStat = ExtractStatistic(c, peak_estimated) with lock: results['stateprogress'] += 5 results['summary'] = ["Captured {} samples".format(len(txframe_aligned)), - "TX/RX median: {} / {}".format(tx_median, rx_median)] + "TX/RX median: {} / {}".format(tx_median, rx_median), + extStat.get_bin_info()] # Extract usable data from measurement tx, rx, phase_diff, n_per_bin = extStat.extract(txframe_aligned, rxframe_aligned) @@ -240,7 +260,7 @@ def engine_worker(): else: with lock: results['state'] = 'Capture + Model' - results['stateprogress'] = 60 + results['stateprogress'] = 80 results['summary'] += ["Training model"] model.train(tx, rx, phase_diff, lr=Heuristics.get_learning_rate(n_runs)) @@ -257,7 +277,7 @@ def engine_worker(): results['amplot'] = "dpd/" + am_plot_file results['pmplot'] = "dpd/" + pm_plot_file results['state'] = 'Capture + Model' - results['stateprogress'] = 70 + results['stateprogress'] = 85 results['summary'] += ["Getting DPD data"] dpddata = model.get_dpd_data() @@ -266,16 +286,15 @@ def engine_worker(): internal_data['n_runs'] = 0 results['state'] = 'Capture + Model' - results['stateprogress'] = 80 + results['stateprogress'] = 90 results['summary'] += ["Reset statistics"] - extStat = ExtractStatistic(c) + extStat = None with lock: results['state'] = 'Idle' results['stateprogress'] = 100 results['summary'] += ["New DPD coefficients calculated"] - finally: with lock: results['state'] = 'Terminated' diff --git a/python/gui/static/js/odr-predistortion.js b/python/gui/static/js/odr-predistortion.js index ff82142..59dcd82 100644 --- a/python/gui/static/js/odr-predistortion.js +++ b/python/gui/static/js/odr-predistortion.js @@ -39,13 +39,23 @@ function resultrefresh() { if (data['statplot']) { $('#dpdcapturestats').attr('src', data['statplot']); } + else { + $('#dpdcapturestats').attr('src', ""); + } if (data['amplot']) { $('#dpdamplot').attr('src', data['amplot']); } + else { + $('#dpdamplot').attr('src', ""); + } + if (data['pmplot']) { $('#dpdpmplot').attr('src', data['pmplot']); } + else { + $('#dpdpmplot').attr('src', ""); + } }); jqxhr.always(function() { diff --git a/python/gui/templates/predistortion.html b/python/gui/templates/predistortion.html index e21c688..d953dff 100644 --- a/python/gui/templates/predistortion.html +++ b/python/gui/templates/predistortion.html @@ -42,6 +42,11 @@
Capture Statistics
+
+ +
+
AM/AM and AM/PM Model
+
-- cgit v1.2.3 From 49620ea6940543a875e0499a1554b33ffb85fa85 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 19 Dec 2018 16:29:39 +0100 Subject: GUI: Combine model plots into single plot --- .gitignore | 1 + python/dpd/ExtractStatistic.py | 2 +- python/dpd/Model_Poly.py | 26 +++++++++----------------- python/dpdce.py | 22 ++++++++++------------ python/gui/static/js/odr-predistortion.js | 13 ++++--------- python/gui/templates/predistortion.html | 13 +++++++++++-- 6 files changed, 36 insertions(+), 41 deletions(-) (limited to 'python/dpd/ExtractStatistic.py') diff --git a/.gitignore b/.gitignore index 21d7201..3ac59f6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ __pycache__/ python/gui/logs/access.log python/gui/logs/error.log +python/gui/static/dpd/*png diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index a23fa1a..25647a6 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -115,7 +115,7 @@ class ExtractStatistic: i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(num) - ax.set_xlabel("TX Amplitude") + ax.set_xlabel("TX Amplitude bin") ax.set_ylabel("Number of Samples") ax.set_ylim(0, self.n_per_bin * 1.2) diff --git a/python/dpd/Model_Poly.py b/python/dpd/Model_Poly.py index 5722531..7ab6aa1 100644 --- a/python/dpd/Model_Poly.py +++ b/python/dpd/Model_Poly.py @@ -42,18 +42,19 @@ class Poly: self.reset_coefs() - def plot(self, am_plot_location, pm_plot_location, title): - if self._am_plot_data is not None: + def plot(self, plot_location, title): + if self._am_plot_data is not None and self._pm_plot_data is not None: tx_dpd, rx_received, coefs_am, coefs_am_new = self._am_plot_data tx_range, rx_est = self._am_calc_line(coefs_am, 0, 0.6) tx_range_new, rx_est_new = self._am_calc_line(coefs_am_new, 0, 0.6) - sub_rows = 1 + sub_rows = 2 sub_cols = 1 fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) i_sub = 0 + # AM subplot i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_range, rx_est, @@ -67,27 +68,18 @@ class Poly: label="Binned Data", color="blue", s=1) - ax.set_title("Model AM {}".format(title)) + ax.set_title("Model AM and PM {}".format(title)) ax.set_xlabel("TX Amplitude") ax.set_ylabel("RX Amplitude") - ax.set_xlim(-0.5, 1.5) + ax.set_xlim(0, 1.0) ax.legend(loc=4) - fig.tight_layout() - fig.savefig(am_plot_location) - plt.close(fig) - - if self._pm_plot_data is not None: + # PM sub plot tx_dpd, phase_diff, coefs_pm, coefs_pm_new = self._pm_plot_data tx_range, phase_diff_est = self._pm_calc_line(coefs_pm, 0, 0.6) tx_range_new, phase_diff_est_new = self._pm_calc_line(coefs_pm_new, 0, 0.6) - sub_rows = 1 - sub_cols = 1 - fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) - i_sub = 0 - i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_range, phase_diff_est, @@ -101,13 +93,13 @@ class Poly: label="Binned Data", color="blue", s=1) - ax.set_title("Model PM {}".format(title)) ax.set_xlabel("TX Amplitude") ax.set_ylabel("Phase DIff") + ax.set_xlim(0, 1.0) ax.legend(loc=4) fig.tight_layout() - fig.savefig(pm_plot_location) + fig.savefig(plot_location) plt.close(fig) def reset_coefs(self): diff --git a/python/dpdce.py b/python/dpdce.py index 18e628b..f76b0f6 100755 --- a/python/dpdce.py +++ b/python/dpdce.py @@ -143,8 +143,8 @@ internal_data = { } results = { 'statplot': None, - 'amplot': None, - 'pmplot': None, + 'modelplot': None, + 'modeldata': "", 'tx_median': 0, 'rx_median': 0, 'state': 'Idle', @@ -159,8 +159,7 @@ agc = Agc(meas, adapt, c) def clear_pngs(results): results['statplot'] = None - results['amplot'] = None - results['pmplot'] = None + results['modelplot'] = None pngs = glob.glob(os.path.join(plot_path, "*.png")) for png in pngs: try: @@ -206,14 +205,15 @@ def engine_worker(): results['stateprogress'] = 100 results['summary'] = summary + ["Calibration done"] elif cmd == "reset": + model.reset_coefs() with lock: internal_data['n_runs'] = 0 results['state'] = 'Idle' results['stateprogress'] = 0 results['summary'] = ["Reset"] + results['modeldata'] = repr(model.get_dpd_data()) clear_pngs(results) extStat = None - model.reset_coefs() elif cmd == "trigger_run": with lock: results['state'] = 'Capture + Model' @@ -246,7 +246,7 @@ def engine_worker(): with lock: results['statplot'] = "dpd/" + plot_file results['stateprogress'] += 5 - results['summary'] += ["Extracted Statistics".format(tx_median, rx_median), + 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: @@ -266,16 +266,13 @@ def engine_worker(): model.train(tx, rx, phase_diff, lr=Heuristics.get_learning_rate(n_runs)) time = datetime.datetime.utcnow() - am_plot_file = "model_am_{}.png".format(time.strftime("%s")) - pm_plot_file = "model_pm_{}.png".format(time.strftime("%s")) + model_plot_file = "model_{}.png".format(time.strftime("%s")) model.plot( - os.path.join(plot_path, am_plot_file), - os.path.join(plot_path, pm_plot_file), + os.path.join(plot_path, model_plot_file), time.strftime("%Y-%m-%dT%H%M%S")) with lock: - results['amplot'] = "dpd/" + am_plot_file - results['pmplot'] = "dpd/" + pm_plot_file + results['modelplot'] = "dpd/" + model_plot_file results['state'] = 'Capture + Model' results['stateprogress'] = 85 results['summary'] += ["Getting DPD data"] @@ -285,6 +282,7 @@ def engine_worker(): internal_data['dpddata'] = dpddata internal_data['n_runs'] = 0 + results['modeldata'] = repr(dpddata) results['state'] = 'Capture + Model' results['stateprogress'] = 90 results['summary'] += ["Reset statistics"] diff --git a/python/gui/static/js/odr-predistortion.js b/python/gui/static/js/odr-predistortion.js index 59dcd82..f46219c 100644 --- a/python/gui/static/js/odr-predistortion.js +++ b/python/gui/static/js/odr-predistortion.js @@ -43,18 +43,13 @@ function resultrefresh() { $('#dpdcapturestats').attr('src', ""); } - if (data['amplot']) { - $('#dpdamplot').attr('src', data['amplot']); - } - else { - $('#dpdamplot').attr('src', ""); - } + $('#dpdmodeldata').html(data['modeldata']); - if (data['pmplot']) { - $('#dpdpmplot').attr('src', data['pmplot']); + if (data['modelplot']) { + $('#dpdmodelplot').attr('src', data['modelplot']); } else { - $('#dpdpmplot').attr('src', ""); + $('#dpdmodelplot').attr('src', ""); } }); diff --git a/python/gui/templates/predistortion.html b/python/gui/templates/predistortion.html index d953dff..d102101 100644 --- a/python/gui/templates/predistortion.html +++ b/python/gui/templates/predistortion.html @@ -47,8 +47,17 @@
AM/AM and AM/PM Model
- - +
+
+
+

Model data:

+
+
+

+              
+
+
+
-- cgit v1.2.3 From b717160a90279c21c068d39673c6aafad66dfcae Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 9 Jan 2019 15:44:06 +0100 Subject: GUI: Add status to home page --- python/dpd/ExtractStatistic.py | 4 +- python/dpdce.py | 10 +-- python/gui.py | 4 +- python/gui/api.py | 20 ++++-- python/gui/static/css/odr.css | 14 +++++ python/gui/static/js/odr-home.js | 132 +++++++++++++++++++++++++++++++++++++++ python/gui/templates/home.html | 38 ++++++++--- python/lib/yamlrpc.py | 2 +- 8 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 python/gui/static/css/odr.css create mode 100644 python/gui/static/js/odr-home.js (limited to 'python/dpd/ExtractStatistic.py') diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index 25647a6..86580d8 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -76,7 +76,7 @@ class ExtractStatistic: i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_values, rx_values, - label="Estimated Values", + label="Averaged measurements", color="red") for i, tx_value in enumerate(tx_values): rx_values_list = self.rx_values_lists[i] @@ -94,7 +94,7 @@ class ExtractStatistic: i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_values, np.rad2deg(phase_diffs_values), - label="Estimated Values", + label="Averaged measurements", color="red") for i, tx_value in enumerate(tx_values): phase_diff = phase_diffs_values_lists[i] diff --git a/python/dpdce.py b/python/dpdce.py index 27c5253..5b308c4 100755 --- a/python/dpdce.py +++ b/python/dpdce.py @@ -409,7 +409,7 @@ try: try: addr, msg_id, method, params = cmd_socket.receive_request() except ValueError as e: - logging.warning('YAML-RPC request error: {}'.format(e)) + logging.warning('RPC request error: {}'.format(e)) continue except TimeoutError: continue @@ -417,22 +417,22 @@ try: logging.info('Caught KeyboardInterrupt') break except: - logging.error('YAML-RPC unknown error') + logging.error('RPC unknown error') break if any(method == m for m in ['trigger_run', 'reset', 'adapt']): - logging.info('YAML-RPC request : {}'.format(method)) + logging.info('Received RPC request : {}'.format(method)) command_queue.put(method) cmd_socket.send_success_response(addr, msg_id, None) elif method == 'restore_dump': - logging.info('YAML-RPC restore dump {}'.format(params['dump_id'])) + logging.info('Received RPC request : restore_dump({})'.format(params['dump_id'])) command_queue.put(f"restore_dump-{params['dump_id']}") cmd_socket.send_success_response(addr, msg_id, None) elif method == 'get_results': with lock: cmd_socket.send_success_response(addr, msg_id, results) elif method == 'calibrate': - logging.info('YAML-RPC request : {}'.format(method)) + logging.info('Received RPC request : {}'.format(method)) command_queue.put('calibrate') cmd_socket.send_success_response(addr, msg_id, None) else: diff --git a/python/gui.py b/python/gui.py index 0090a3a..a9328ee 100755 --- a/python/gui.py +++ b/python/gui.py @@ -33,6 +33,7 @@ from lib import zmqrc env = Environment(loader=FileSystemLoader('gui/templates')) base_js = ["js/odr.js"] +base_css = ["css/odr.css"] class Root: def __init__(self, dpd_port): @@ -51,7 +52,8 @@ class Root: @cherrypy.expose def home(self): tmpl = env.get_template("home.html") - return tmpl.render(tab='home', js=base_js, is_login=False) + js = base_js + ["js/odr-home.js"] + return tmpl.render(tab='home', js=js, css=base_css, is_login=False) @cherrypy.expose def rcvalues(self): diff --git a/python/gui/api.py b/python/gui/api.py index c0effde..f9e0ad0 100755 --- a/python/gui/api.py +++ b/python/gui/api.py @@ -80,8 +80,18 @@ class API: return send_error(str(e)) return send_ok() else: - cherrypy.response.status = 400 - return send_error("POST only") + if all(p in kwargs for p in ('controllable', 'param')): + try: + return send_ok(self.mod_rc.get_param_value(kwargs['controllable'], kwargs['param'])) + except IOError as e: + cherrypy.response.status = 503 + return send_error(str(e)) + except ValueError as e: + cherrypy.response.status = 503 + return send_error(str(e)) + else: + cherrypy.response.status = 400 + return send_error("missing 'controllable' or 'param' GET parameters") def _wrap_dpd(self, method, data=None): try: @@ -89,12 +99,12 @@ class API: return send_ok(reply) except ValueError as e: cherrypy.response.status = 503 - return send_error("YAML-RPC call error: {}".format(e)) + return send_error("DPDCE remote procedure call error: {}".format(e)) except TimeoutError as e: cherrypy.response.status = 503 - return send_error("YAML-RPC timeout: {}".format(e)) + return send_error("DPDCE remote procedure call timed out") cherrypy.response.status = 500 - return send_error("YAML-RPC unknown error") + return send_error("Unknown DPDCE remote procedure error error") @cherrypy.expose @cherrypy.tools.json_out() diff --git a/python/gui/static/css/odr.css b/python/gui/static/css/odr.css new file mode 100644 index 0000000..1710464 --- /dev/null +++ b/python/gui/static/css/odr.css @@ -0,0 +1,14 @@ +.glyphicon-refresh-animate { + -animation: spin 1.8s infinite linear; + -webkit-animation: spin2 1.8s infinite linear; +} + +@-webkit-keyframes spin2 { + from { -webkit-transform: rotate(0deg);} + to { -webkit-transform: rotate(360deg);} +} + +@keyframes spin { + from { transform: scale(1) rotate(0deg);} + to { transform: scale(1) rotate(360deg);} +} diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js new file mode 100644 index 0000000..11aed8e --- /dev/null +++ b/python/gui/static/js/odr-home.js @@ -0,0 +1,132 @@ +// Copyright (C) 2019 +// Matthias P. Braendli, matthias.braendli@mpb.li +// +// http://www.opendigitalradio.org +// +// This file is part of ODR-DabMod. +// +// ODR-DabMod 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 of the +// License, or (at your option) any later version. +// +// ODR-DabMod 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 ODR-DabMod. If not, see . +// + +function apiRequestChain(uri, get_data, success_callback, fail_callback) { + $.ajax({ + type: "GET", + url: uri, + data: get_data, + contentType: 'application/json', + dataType: 'json', + + error: function(data) { + console.log("GET " + JSON.stringify(get_data) + " error: " + data.responseText); + fail_callback(data.responseText); + }, + success: function(data) { + console.log("GET " + JSON.stringify(get_data) + " success: " + JSON.stringify(data)); + if (data.status == 'ok') { + success_callback(data.data); + } + else { + fail_callback(data.data); + } + }, + }); +} + +function mark_pending(id) { + document.getElementById(id).className = "glyphicon glyphicon-refresh glyphicon-refresh-animate"; +} + +function mark_ok(id, comment) { + document.getElementById(id).className = "glyphicon glyphicon-ok"; + + if (comment) { + document.getElementById(id + "_comment").innerHTML = comment; + } +} + +function mark_fail(id, reason) { + var el = document.getElementById(id); + el.className = "glyphicon glyphicon-remove"; + el.style.color = "#FF3333"; + + document.getElementById(id + "_comment").innerHTML = reason; + + var overall = document.getElementById("overall_state"); + overall.style.color = "#FF8833"; + overall.className = "glyphicon glyphicon-alert"; +} + +function check_rc() { + mark_pending('is_rc_ok'); + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'freq'}, + function(data) { + mark_ok('is_rc_ok'); + check_modulating(0); + }, + function(data) { + mark_fail('is_rc_ok', JSON.parse(data)['reason']); + }); +} + +function check_modulating(last_num_frames) { + mark_pending('is_modulating'); + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'frames'}, + function(data) { + if (data > 0) { + if (last_num_frames == 0) { + setTimeout(function() { check_modulating(data); }, 200); + } + else { + if (data == last_num_frames) { + mark_fail('is_modulating', "Frame counter not incrementing: " + data); + } + else { + mark_ok('is_modulating', "Number of frames modulated: " + data); + check_dpdce_running(); + } + } + } + else { + mark_fail('is_modulating', 'number of frames is 0'); + } + }, + function(data) { + mark_fail('is_modulating', data); + }); +} + +function check_dpdce_running() { + mark_pending('is_dpdce_running'); + apiRequestChain("/api/dpd_results", + {controllable: 'sdr', param: 'frames'}, + function(data) { + mark_ok('is_dpdce_running', "State: " + data['state']); + mark_ok('overall_state'); + }, + function(data) { + mark_fail('is_dpdce_running', JSON.parse(data)['reason']); + }); +} + +$(function(){ + setTimeout(check_rc, 20); +}); + + +// ToolTip init +$(function(){ + $('[data-toggle="tooltip"]').tooltip(); +}); diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index cc8fbf0..bf802d6 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -4,13 +4,37 @@ {% include 'head.html' %} - {% include 'body-nav.html' %} - -
-
-

Opendigitalradio

ODR-DabMod Interface

-
-
+ {% include 'body-nav.html' %} + +
+
+

Opendigitalradio

ODR-DabMod Status Check + +

+
+

ODR-DabMod +

+
    +
  • Answering to RC: + + +
  • +
  • Frame generation: + + +
  • +
+ +

Checking predistortion +

    +
  • DPDCE running: + + +
  • +
+
+
+
diff --git a/python/lib/yamlrpc.py b/python/lib/yamlrpc.py index d4e744a..67c65ff 100644 --- a/python/lib/yamlrpc.py +++ b/python/lib/yamlrpc.py @@ -85,7 +85,7 @@ class Socket: try: data, addr = self.socket.recvfrom(UDP_PACKETSIZE) except socket.timeout as to: - raise TimeoutError("Timeout: " + str(to)) + raise TimeoutError() y = yaml.load(data.decode()) -- cgit v1.2.3 From 0da6202199ceb03d6d83042695c53b7c8b2a7f04 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 23 Jan 2019 10:33:34 +0100 Subject: DPDCE: improve plot axis ranges --- python/dpd/ExtractStatistic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'python/dpd/ExtractStatistic.py') diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index 86580d8..3518dba 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -87,8 +87,8 @@ class ExtractStatistic: ax.set_title("Extracted Statistic {}".format(title)) ax.set_xlabel("TX Amplitude") ax.set_ylabel("RX Amplitude") - ax.set_ylim(0, 0.8) - ax.set_xlim(0, 1.1) + ax.set_ylim(0, np.max(self.tx_boundaries)) # we expect a rougly a 1:1 correspondence between x and y + ax.set_xlim(0, np.max(self.tx_boundaries)) ax.legend(loc=4) i_sub += 1 @@ -105,7 +105,7 @@ class ExtractStatistic: ax.set_xlabel("TX Amplitude") ax.set_ylabel("Phase Difference") ax.set_ylim(-60, 60) - ax.set_xlim(0, 1.1) + ax.set_xlim(0, np.max(self.tx_boundaries)) ax.legend(loc=4) num = [] -- cgit v1.2.3