diff options
| -rw-r--r-- | dpd/dpd.ini | 8 | ||||
| -rwxr-xr-x | dpd/main.py | 100 | ||||
| -rw-r--r-- | dpd/poly.coef | 18 | ||||
| -rw-r--r-- | dpd/src/Agc.py | 2 | ||||
| -rw-r--r-- | dpd/src/Dab_Util.py | 10 | ||||
| -rw-r--r-- | dpd/src/ExtractStatistic.py | 201 | ||||
| -rw-r--r-- | dpd/src/MER.py | 2 | ||||
| -rw-r--r-- | dpd/src/Measure.py | 4 | ||||
| -rw-r--r-- | dpd/src/Model.py | 389 | ||||
| -rw-r--r-- | dpd/src/Model_AM.py | 115 | ||||
| -rw-r--r-- | dpd/src/Model_PM.py | 118 | ||||
| -rw-r--r-- | dpd/src/Model_Poly.py | 99 | ||||
| -rw-r--r-- | dpd/src/Symbol_align.py | 3 | ||||
| -rw-r--r-- | dpd/src/const.py | 6 | ||||
| -rw-r--r-- | dpd/src/phase_align.py | 2 | ||||
| -rwxr-xr-x | dpd/src/subsample_align.py | 2 | 
16 files changed, 635 insertions, 444 deletions
| diff --git a/dpd/dpd.ini b/dpd/dpd.ini index 9a76393..26a53c5 100644 --- a/dpd/dpd.ini +++ b/dpd/dpd.ini @@ -10,8 +10,8 @@ filelog=1  filename=/tmp/dabmod.log  [input] -transport=file -source=/home/bram/dab/mmbtools-aux/eti/buddard.eti +transport=tcp +source=localhost:9200  [modulator]  gainmode=var @@ -31,7 +31,7 @@ polycoeffile=dpd/poly.coef  [output]  # to prepare a file for the dpd/iq_file_server.py script,  # use output=file -output=file +output=uhd  [fileoutput]  filename=dpd.iq @@ -40,7 +40,7 @@ filename=dpd.iq  device=  master_clock_rate=32768000  type=b200 -txgain=15 +txgain=55  channel=13C  refclk_source=internal  pps_source=none diff --git a/dpd/main.py b/dpd/main.py index de3453e..084ccd5 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -42,6 +42,7 @@ import numpy as np  import traceback  import src.Measure as Measure  import src.Model as Model +import src.ExtractStatistic as ExtractStatistic  import src.Adapt as Adapt  import src.Agc as Agc  import src.TX_Agc as TX_Agc @@ -52,19 +53,19 @@ import argparse  parser = argparse.ArgumentParser(      description="DPD Computation Engine for ODR-DabMod") -parser.add_argument('--port', default='50055', +parser.add_argument('--port', default=50055, type=int,                      help='port of DPD server to connect to (default: 50055)',                      required=False) -parser.add_argument('--rc-port', default='9400', +parser.add_argument('--rc-port', default=9400, type=int,                      help='port of ODR-DabMod ZMQ Remote Control to connect to (default: 9400)',                      required=False) -parser.add_argument('--samplerate', default='8192000', +parser.add_argument('--samplerate', default=8192000, type=int,                      help='Sample rate',                      required=False)  parser.add_argument('--coefs', default='poly.coef',                      help='File with DPD coefficients, which will be read by ODR-DabMod',                      required=False) -parser.add_argument('--txgain', default=71, +parser.add_argument('--txgain', default=73,                      help='TX Gain',                      required=False,                      type=int) @@ -76,10 +77,10 @@ parser.add_argument('--digital_gain', default=1,                      help='Digital Gain',                      required=False,                      type=float) -parser.add_argument('--samps', default='81920', +parser.add_argument('--samps', default='81920', type=int,                      help='Number of samples to request from ODR-DabMod',                      required=False) -parser.add_argument('-i', '--iterations', default='1', +parser.add_argument('-i', '--iterations', default=1, type=int,                      help='Number of iterations to run',                      required=False)  parser.add_argument('-L', '--lut', @@ -88,29 +89,29 @@ parser.add_argument('-L', '--lut',  cli_args = parser.parse_args() -port = int(cli_args.port) -port_rc = int(cli_args.rc_port) +port = cli_args.port +port_rc = cli_args.rc_port  coef_path = cli_args.coefs  digital_gain = cli_args.digital_gain  txgain = cli_args.txgain  rxgain = cli_args.rxgain -num_req = int(cli_args.samps) -samplerate = int(cli_args.samplerate) -num_iter = int(cli_args.iterations) +num_req = cli_args.samps +samplerate = cli_args.samplerate +num_iter = cli_args.iterations  SA = src.Symbol_align.Symbol_align(samplerate)  MER = src.MER.MER(samplerate)  c = src.const.const(samplerate)  meas = Measure.Measure(samplerate, port, num_req) - +extStat = ExtractStatistic.ExtractStatistic(c, plot=True)  adapt = Adapt.Adapt(port_rc, coef_path)  dpddata = adapt.get_predistorter()  if cli_args.lut: -    model = Model.LutModel(c, SA, MER, plot=True) +    model = Model.Lut(c, plot=True)  else: -    model = Model.PolyModel(c, SA, MER, None, None, plot=True) +    model = Model.Poly(c, plot=True)  adapt.set_predistorter(model.get_dpd_data())  adapt.set_digital_gain(digital_gain)  adapt.set_txgain(txgain) @@ -120,7 +121,7 @@ tx_gain = adapt.get_txgain()  rx_gain = adapt.get_rxgain()  digital_gain = adapt.get_digital_gain() -dpddata = adapt.get_coefs() +dpddata = adapt.get_predistorter()  if dpddata[0] == "poly":      coefs_am = dpddata[1]      coefs_pm = dpddata[2] @@ -148,23 +149,62 @@ tx_agc = TX_Agc.TX_Agc(adapt)  agc = Agc.Agc(meas, adapt)  agc.run() -for i in range(num_iter): +state = "measure" +i = 0 +while i < num_iter:      try: -        txframe_aligned, tx_ts, rxframe_aligned, rx_ts, rx_median = meas.get_samples() -        logging.debug("tx_ts {}, rx_ts {}".format(tx_ts, rx_ts)) -        assert tx_ts - rx_ts < 1e-5, "Time stamps do not match." - -        if tx_agc.adapt_if_necessary(txframe_aligned): -            continue - -        model.train(txframe_aligned, rxframe_aligned) -        adapt.set_predistorter(model.get_dpd_data()) +        # Measure +        if state == "measure": +            txframe_aligned, tx_ts, rxframe_aligned, rx_ts, rx_median = meas.get_samples() +            tx, rx, phase_diff, n_per_bin = extStat.extract(txframe_aligned, rxframe_aligned) +            n_use = int(len(n_per_bin) * 0.6) +            tx = tx[:n_use] +            rx = rx[:n_use] +            phase_diff = phase_diff[:n_use] +            if all(c.ES_n_per_bin == np.array(n_per_bin)[0:n_use]): +                state = "model" +            else: +                state = "measure" + +        # Model +        elif state == "model": +            dpddata = model_poly.get_dpd_data(tx, rx, phase_diff) +            del extStat +            extStat = ExtractStatistic.ExtractStatistic(c, plot=True) +            state = "adapt" + +        # Adapt +        elif state == "adapt": +            adapt.set_predistorter(dpddata) +            state = "report" +            i += 1 + +        # Report +        elif state == "report": +            try: +                off = SA.calc_offset(txframe_aligned) +                tx_mer = MER.calc_mer(txframe_aligned[off:off+c.T_U], debug=True) +                rx_mer = MER.calc_mer(rxframe_aligned[off:off+c.T_U], debug=True) +                mse = np.mean(np.abs((txframe_aligned - rxframe_aligned)**2)) + +                if dpddata[0] == "poly": +                    coefs_am = dpddata[1] +                    coefs_pm = dpddata[2] +                    logging.info("It {}: TX_MER {}, RX_MER {}," \ +                                 " MSE {}, coefs_am {}, coefs_pm {}". +                                 format(i, tx_mer, rx_mer, mse, coefs_am, coefs_pm)) +                if dpddata[0] == "lut": +                    scalefactor = dpddata[1] +                    lut = dpddata[2] +                    logging.info("It {}: TX_MER {}, RX_MER {}," \ +                                 " MSE {}, LUT scalefactor {}, LUT {}". +                                 format(i, tx_mer, rx_mer, mse, scalefactor, lut)) +                state = "measure" +            except: +                logging.warning("Iteration {}: Report failed.".format(i)) +                logging.warning(traceback.format_exc()) +                state = "measure" -        off = SA.calc_offset(txframe_aligned) -        tx_mer = MER.calc_mer(txframe_aligned[off:off + c.T_U]) -        rx_mer = MER.calc_mer(rxframe_aligned[off:off + c.T_U], debug=True) -        logging.info("MER with lag in it. {}: TX {}, RX {}". -                     format(i, tx_mer, rx_mer))      except Exception as e:          logging.warning("Iteration {} failed.".format(i))          logging.warning(traceback.format_exc()) diff --git a/dpd/poly.coef b/dpd/poly.coef index 913507a..248d316 100644 --- a/dpd/poly.coef +++ b/dpd/poly.coef @@ -1,12 +1,12 @@  1  5  1.0 -0 -0 -0 -0 -0 -0 -0 -0 -0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 diff --git a/dpd/src/Agc.py b/dpd/src/Agc.py index 978b607..b83c91e 100644 --- a/dpd/src/Agc.py +++ b/dpd/src/Agc.py @@ -139,7 +139,7 @@ class Agc:          fig.tight_layout()          fig.savefig(fig_path) -        fig.clf() +        plt.close(fig)  # The MIT License (MIT) diff --git a/dpd/src/Dab_Util.py b/dpd/src/Dab_Util.py index e3dbfe3..37be5db 100644 --- a/dpd/src/Dab_Util.py +++ b/dpd/src/Dab_Util.py @@ -49,7 +49,7 @@ class Dab_Util:              plt.plot(c, label="corr")              plt.legend()              plt.savefig(corr_path) -            plt.clf() +            plt.close()          return np.argmax(c) - off + 1 @@ -118,7 +118,7 @@ class Dab_Util:              fig.tight_layout()              fig.savefig(fig_path) -            fig.clf() +            plt.close(fig)          off_meas = self.lag_upsampling(sig_rx, sig_tx, n_up=1)          off = int(abs(off_meas)) @@ -161,7 +161,7 @@ class Dab_Util:              fig.tight_layout()              fig.savefig(fig_path) -            fig.clf() +            plt.close(fig)          sig_rx = sa.subsample_align(sig_rx, sig_tx) @@ -185,7 +185,7 @@ class Dab_Util:              fig.tight_layout()              fig.savefig(fig_path) -            fig.clf() +            plt.close(fig)          sig_rx = pa.phase_align(sig_rx, sig_tx) @@ -209,7 +209,7 @@ class Dab_Util:              fig.tight_layout()              fig.savefig(fig_path) -            fig.clf() +            plt.close(fig)          logging.debug("Sig1_cut: %d %s, Sig2_cut: %d %s, off: %d" % (len(sig_tx), sig_tx.dtype, len(sig_rx), sig_rx.dtype, off))          return sig_tx, sig_rx diff --git a/dpd/src/ExtractStatistic.py b/dpd/src/ExtractStatistic.py new file mode 100644 index 0000000..897ec0a --- /dev/null +++ b/dpd/src/ExtractStatistic.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# +# DPD Calculation Engine, +# Extract statistic from data to use in Model +# +# http://www.opendigitalradio.org +# Licence: The MIT License, see notice at the end of this file + +import numpy as np +import pickle +import matplotlib.pyplot as plt + +import datetime +import os +import logging + +logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) + + +def _check_input_extract(tx_dpd, rx_received): +    # Check data type +    assert tx_dpd[0].dtype == np.complex64, \ +        "tx_dpd is not complex64 but {}".format(tx_dpd[0].dtype) +    assert rx_received[0].dtype == np.complex64, \ +        "rx_received is not complex64 but {}".format(rx_received[0].dtype) +    # Check if signals have same normalization +    normalization_error = np.abs(np.median(np.abs(tx_dpd)) - +                                 np.median(np.abs(rx_received))) / ( +                              np.median(np.abs(tx_dpd)) + np.median(np.abs(rx_received))) +    assert normalization_error < 0.01, "Non normalized signals" + + +class ExtractStatistic: +    """Calculate a low variance RX value for equally spaced tx values +    of a predefined range""" + +    def __init__(self, +                 c, +                 plot=False): +        self.c = c + +        self.tx_boundaries = np.linspace(c.ES_start, c.ES_end, c.ES_n_bins + 1) +        self.n_per_bin = c.ES_n_per_bin + +        self.rx_values_lists = [] +        for i in range(c.ES_n_bins): +            self.rx_values_lists.append([]) + +        self.tx_values_lists = [] +        for i in range(c.ES_n_bins): +            self.tx_values_lists.append([]) + +        self.tx_values = self._tx_value_per_bin() + +        self.rx_values = [] +        for i in range(c.ES_n_bins): +            self.rx_values.append(None) + +        self.plot = plot + +    def _plot_and_log(self): +        if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: +            phase_diffs_values_lists = self._phase_diff_list_per_bin() +            phase_diffs_values = self._phase_diff_value_per_bin(phase_diffs_values_lists) + +            dt = datetime.datetime.now().isoformat() +            fig_path = logging_path + "/" + dt + "_ExtractStatistic.png" +            sub_rows = 3 +            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(self.tx_values, self.rx_values, +                    label="Estimated Values", +                    color="red") +            for i, tx_value in enumerate(self.tx_values): +                rx_values = self.rx_values_lists[i] +                ax.scatter(np.ones(len(rx_values)) * tx_value, +                           np.abs(rx_values), +                           s=0.1, +                           color="black") +            ax.set_title("Extracted Statistic") +            ax.set_xlabel("TX Amplitude") +            ax.set_ylabel("RX Amplitude") +            ax.set_ylim(0, 0.8) +            ax.set_xlim(0, 1.1) +            ax.legend(loc=4) + +            i_sub += 1 +            ax = plt.subplot(sub_rows, sub_cols, i_sub) +            ax.plot(self.tx_values, np.rad2deg(phase_diffs_values), +                    label="Estimated Values", +                    color="red") +            for i, tx_value in enumerate(self.tx_values): +                phase_diff = phase_diffs_values_lists[i] +                ax.scatter(np.ones(len(phase_diff)) * tx_value, +                           np.rad2deg(phase_diff), +                           s=0.1, +                           color="black") +            ax.set_xlabel("TX Amplitude") +            ax.set_ylabel("Phase Difference") +            ax.set_ylim(-60,60) +            ax.set_xlim(0, 1.1) +            ax.legend(loc=4) + +            num = [] +            for i, tx_value in enumerate(self.tx_values): +                rx_values = self.rx_values_lists[i] +                num.append(len(rx_values)) +            i_sub += 1 +            ax = plt.subplot(sub_rows, sub_cols, i_sub) +            ax.plot(num) +            ax.set_xlabel("TX Amplitude") +            ax.set_ylabel("Number of Samples") +            ax.set_ylim(0, self.n_per_bin * 1.2) + +            fig.tight_layout() +            fig.savefig(fig_path) +            plt.close(fig) + +            pickle.dump(self.rx_values_lists, open("/tmp/rx_values", "wb")) +            pickle.dump(self.tx_values, open("/tmp/tx_values", "wb")) + +    def _rx_value_per_bin(self): +        rx_values = [] +        for values in self.rx_values_lists: +            rx_values.append(np.mean(np.abs(values))) +        return rx_values + +    def _tx_value_per_bin(self): +        tx_values = [] +        for start, end in zip(self.tx_boundaries, self.tx_boundaries[1:]): +            tx_values.append(np.mean((start, end))) +        return tx_values + +    def _phase_diff_list_per_bin(self): +        phase_values_lists = [] +        for tx_list, rx_list in zip(self.tx_values_lists, self.rx_values_lists): +            phase_diffs = [] +            for tx, rx in zip(tx_list, rx_list): +                phase_diffs.append(np.angle(rx * tx.conjugate())) +            phase_values_lists.append(phase_diffs) +        return phase_values_lists + +    def _phase_diff_value_per_bin(self, phase_diffs_values_lists): +        phase_list = [] +        for values in phase_diffs_values_lists: +            phase_list.append(np.mean(values)) +        return phase_list + +    def extract(self, tx_dpd, rx): +        _check_input_extract(tx_dpd, rx) + +        tx_abs = np.abs(tx_dpd) +        for i, (tx_start, tx_end) in enumerate(zip(self.tx_boundaries, self.tx_boundaries[1:])): +            mask = (tx_abs > tx_start) & (tx_abs < tx_end) +            n_add = max(0, self.n_per_bin - len(self.rx_values_lists[i])) +            self.rx_values_lists[i] += \ +                list(rx[mask][:n_add]) +            self.tx_values_lists[i] += \ +                list(tx_dpd[mask][:n_add]) + +        self.rx_values = self._rx_value_per_bin() +        self.tx_values = self._tx_value_per_bin() + +        self._plot_and_log() + +        n_per_bin = [len(values) for values in self.rx_values_lists] + +        # TODO cleanup +        phase_diffs_values_lists = self._phase_diff_list_per_bin() +        phase_diffs_values = self._phase_diff_value_per_bin(phase_diffs_values_lists) + +        return np.array(self.tx_values,  dtype=np.float32), \ +               np.array(self.rx_values, dtype=np.float32), \ +               np.array(phase_diffs_values, dtype=np.float32), \ +               n_per_bin + +# The MIT License (MIT) +# +# Copyright (c) 2017 Andreas Steger +# +# 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/dpd/src/MER.py b/dpd/src/MER.py index 00fcc23..4f2918e 100644 --- a/dpd/src/MER.py +++ b/dpd/src/MER.py @@ -106,7 +106,7 @@ class MER:              plt.tight_layout()              plt.savefig(fig_path)              plt.show() -            plt.clf() +            plt.close()          MER_res = 20 * np.log10(np.mean([10 ** (MER / 20) for MER in MERs]))          return MER_res diff --git a/dpd/src/Measure.py b/dpd/src/Measure.py index 2450b8a..7a9246c 100644 --- a/dpd/src/Measure.py +++ b/dpd/src/Measure.py @@ -104,10 +104,6 @@ class Measure:          txframe, tx_ts, rxframe, rx_ts = self.receive_tcp() -        if logging.getLogger().getEffectiveLevel() == logging.DEBUG: -            dt = datetime.datetime.now().isoformat() -            txframe.tofile(logging_path + "/txframe_" + dt + ".iq") -          # Normalize received signal with sent signal          rx_median = np.median(np.abs(rxframe))          rxframe = rxframe / rx_median * np.median(np.abs(txframe)) diff --git a/dpd/src/Model.py b/dpd/src/Model.py index a23f0ce..7ce6171 100644 --- a/dpd/src/Model.py +++ b/dpd/src/Model.py @@ -1,388 +1,3 @@  # -*- coding: utf-8 -*- -# -# DPD Calculation Engine, model implementation. -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -import datetime -import os -import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) - -import numpy as np -import matplotlib.pyplot as plt -from sklearn import linear_model - -class PolyModel: -    """Calculates new coefficients using the measurement and the old -    coefficients""" - -    def __init__(self, -                 c, -                 SA, -                 MER, -                 coefs_am, -                 coefs_pm, -                 learning_rate_am=1., -                 learning_rate_pm=1., -                 plot=False): -        logging.debug("Initialising Poly Model") -        self.c = c -        self.SA = SA -        self.MER = MER - -        self.learning_rate_am = learning_rate_am -        self.learning_rate_pm = learning_rate_pm - -        if coefs_am is None: -            self.coefs_am = [1.0, 0, 0, 0, 0] -        else: -            self.coefs_am = coefs_am -        self.coefs_am_history = [coefs_am, ] -        self.mses_am = [] -        self.errs_am = [] - -        self.tx_mers = [] -        self.rx_mers = [] - -        if coefs_pm is None: -            self.coefs_pm = [0, 0, 0, 0, 0] -        else: -            self.coefs_pm = coefs_pm -        self.coefs_pm_history = [coefs_pm, ] -        self.errs_pm = [] - -        self.plot = plot - -    def train(self, tx_dpd, rx_received): -        """Give new training data to the model""" -        # Check data type -        assert tx_dpd[0].dtype == np.complex64, \ -            "tx_dpd is not complex64 but {}".format(tx_dpd[0].dtype) -        assert rx_received[0].dtype == np.complex64, \ -            "rx_received is not complex64 but {}".format(rx_received[0].dtype) -        # Check if signals have same normalization -        normalization_error = np.abs(np.median(np.abs(tx_dpd)) - -                                     np.median(np.abs(rx_received))) / ( -                                  np.median(np.abs(tx_dpd)) + np.median(np.abs(rx_received))) -        assert normalization_error < 0.01, "Non normalized signals" - -        tx_choice, rx_choice = self._sample_uniformly(tx_dpd, rx_received) -        new_coefs_am = self._next_am_coefficent(tx_choice, rx_choice) -        new_coefs_pm, phase_diff_choice = self._next_pm_coefficent(tx_choice, rx_choice) - -        logging.debug('txframe: min {:.2f}, max {:.2f}, ' \ -                      'median {:.2f}; rxframe: min {:.2f}, max {:.2f}, ' \ -                      'median {:.2f}; new coefs_am {};' \ -                      'new_coefs_pm {}'.format( -            np.min(np.abs(tx_dpd)), -            np.max(np.abs(tx_dpd)), -            np.median(np.abs(tx_dpd)), -            np.min(np.abs(rx_choice)), -            np.max(np.abs(rx_choice)), -            np.median(np.abs(rx_choice)), -            new_coefs_am, -            new_coefs_pm)) - -        if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: -            off = self.SA.calc_offset(tx_dpd) -            tx_mer = self.MER.calc_mer(tx_dpd[off:off + self.c.T_U]) -            rx_mer = self.MER.calc_mer(rx_received[off:off + self.c.T_U], debug=True) -            self.tx_mers.append(tx_mer) -            self.rx_mers.append(rx_mer) - -        if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: -            dt = datetime.datetime.now().isoformat() -            fig_path = logging_path + "/" + dt + "_Model.svg" - -            fig = plt.figure(figsize=(2 * 6, 2 * 6)) - -            i_sub = 1 - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            ax.plot(np.abs(tx_dpd[:128]), -                    label="TX sent", -                    linestyle=":") -            ax.plot(np.abs(rx_received[:128]), -                    label="RX received", -                    color="red") -            ax.set_title("Synchronized Signals of Iteration {}" -                         .format(len(self.coefs_am_history))) -            ax.set_xlabel("Samples") -            ax.set_ylabel("Amplitude") -            ax.text(0, 0, "TX (max {:01.3f}, mean {:01.3f}, " \ -                          "median {:01.3f})".format( -                np.max(np.abs(tx_dpd)), -                np.mean(np.abs(tx_dpd)), -                np.median(np.abs(tx_dpd)) -            ), size=8) -            ax.legend(loc=4) - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            ccdf_min, ccdf_max = 0, 1 -            tx_hist, ccdf_edges = np.histogram(np.abs(tx_dpd), -                                               bins=60, -                                               range=(ccdf_min, ccdf_max)) -            tx_hist_normalized = tx_hist.astype(float) / np.sum(tx_hist) -            ccdf = 1.0 - np.cumsum(tx_hist_normalized) -            ax.semilogy(ccdf_edges[:-1], ccdf, label="CCDF") -            ax.semilogy(ccdf_edges[:-1], -                        tx_hist_normalized, -                        label="Histogram", -                        drawstyle='steps') -            ax.legend(loc=4) -            ax.set_ylim(1e-5, 2) -            ax.set_title("Complementary Cumulative Distribution Function") -            ax.set_xlabel("TX Amplitude") -            ax.set_ylabel("Ratio of Samples larger than x") - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            ax.semilogy(np.array(self.mses_am) + 1e-10, label="log(MSE)") -            ax.semilogy(np.array(self.errs_am) + 1e-10, label="log(ERR)") -            ax.legend(loc=4) -            ax.set_title("MSE History") -            ax.set_xlabel("Iterations") -            ax.set_ylabel("MSE") - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            ax.plot(self.tx_mers, label="TX MER") -            ax.plot(self.rx_mers, label="RX MER") -            ax.legend(loc=4) -            ax.set_title("MER History") -            ax.set_xlabel("Iterations") -            ax.set_ylabel("MER") - -            ax = plt.subplot(4, 2, i_sub) -            rx_range = np.linspace(0, 1, num=100, dtype=np.complex64) -            rx_range_dpd = self._dpd_amplitude(rx_range)[0] -            rx_range_dpd_new = self._dpd_amplitude(rx_range, new_coefs_am)[0] -            i_sub += 1 -            ax.scatter( -                np.abs(tx_choice), -                np.abs(rx_choice), -                s=0.1) -            ax.plot(rx_range_dpd / self.coefs_am[0], rx_range, linewidth=0.25, label="current") -            ax.plot(rx_range_dpd_new / self.coefs_am[0], rx_range, linewidth=0.25, label="next") -            ax.set_ylim(0, 1) -            ax.set_xlim(0, 1) -            ax.legend() -            ax.set_title("Amplifier Characteristic") -            ax.set_xlabel("TX Amplitude") -            ax.set_ylabel("RX Amplitude") - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            coefs_am_history = np.array(self.coefs_am_history) -            for idx, coef_hist in enumerate(coefs_am_history.T): -                ax.plot(coef_hist, -                        label="Coef {}".format(idx), -                        linewidth=0.5) -            ax.legend(loc=4) -            ax.set_title("AM/AM Coefficient History") -            ax.set_xlabel("Iterations") -            ax.set_ylabel("Coefficient Value") - -            phase_range = np.linspace(0, 1, num=100, dtype=np.complex64) -            phase_range_dpd = self._dpd_phase(phase_range)[0] -            phase_range_dpd_new = self._dpd_phase(phase_range, -                                                 coefs=new_coefs_pm)[0] -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            ax.scatter( -                np.abs(tx_choice), -                np.rad2deg(phase_diff_choice), -                s=0.1) -            ax.plot( -                np.abs(phase_range), -                np.rad2deg(phase_range_dpd), -                linewidth=0.25, -                label="current") -            ax.plot( -                np.abs(phase_range), -                np.rad2deg(phase_range_dpd_new), -                linewidth=0.25, -                label="next") -            ax.set_ylim(-60, 60) -            ax.set_xlim(0, 1) -            ax.legend() -            ax.set_title("Amplifier Characteristic") -            ax.set_xlabel("TX Amplitude") -            ax.set_ylabel("Phase Difference") - -            ax = plt.subplot(4, 2, i_sub) -            i_sub += 1 -            coefs_pm_history = np.array(self.coefs_pm_history) -            for idx, coef_phase_hist in enumerate(coefs_pm_history.T): -                ax.plot(coef_phase_hist, -                        label="Coef {}".format(idx), -                        linewidth=0.5) -            ax.legend(loc=4) -            ax.set_title("AM/PM Coefficient History") -            ax.set_xlabel("Iterations") -            ax.set_ylabel("Coefficient Value") - -            fig.tight_layout() -            fig.savefig(fig_path) -            fig.clf() - -        self.coefs_am = new_coefs_am -        self.coefs_am_history.append(self.coefs_am) -        self.coefs_pm = new_coefs_pm -        self.coefs_pm_history.append(self.coefs_pm) - -    def get_dpd_data(self): -        return "poly", self.coefs_am, self.coefs_pm - -    def _sample_uniformly(self, tx_dpd, rx_received, n_bins=5): -        """This function returns tx and rx samples in a way -        that the tx amplitudes have an approximate uniform  -        distribution with respect to the tx_dpd amplitudes""" -        mask = np.logical_and((np.abs(tx_dpd) > 0.01), (np.abs(rx_received) > 0.01)) -        tx_dpd = tx_dpd[mask] -        rx_received = rx_received[mask] - -        txframe_aligned_abs = np.abs(tx_dpd) -        ccdf_min = 0 -        ccdf_max = np.max(txframe_aligned_abs) -        tx_hist, ccdf_edges = np.histogram(txframe_aligned_abs, -                                           bins=n_bins, -                                           range=(ccdf_min, ccdf_max)) -        n_choise = np.min(tx_hist) -        tx_choice = np.zeros(n_choise * n_bins, dtype=np.complex64) -        rx_choice = np.zeros(n_choise * n_bins, dtype=np.complex64) - -        for idx, bin in enumerate(tx_hist): -            indices = np.where((txframe_aligned_abs >= ccdf_edges[idx]) & -                               (txframe_aligned_abs <= ccdf_edges[idx + 1]))[0] -            indices_choise = np.random.choice(indices, -                                              n_choise, -                                              replace=False) -            rx_choice[idx * n_choise:(idx + 1) * n_choise] = \ -                rx_received[indices_choise] -            tx_choice[idx * n_choise:(idx + 1) * n_choise] = \ -                tx_dpd[indices_choise] - -        assert isinstance(rx_choice[0], np.complex64), \ -            "rx_choice is not complex64 but {}".format(rx_choice[0].dtype) -        assert isinstance(tx_choice[0], np.complex64), \ -            "tx_choice is not complex64 but {}".format(tx_choice[0].dtype) - -        return tx_choice, rx_choice - -    def _dpd_amplitude(self, sig, coefs=None): -        if coefs is None: -            coefs = self.coefs_am -        assert isinstance(sig[0], np.complex64), "Sig is not complex64 but {}".format(sig[0].dtype) -        sig_abs = np.abs(sig) -        A_sig = np.vstack([np.ones(sig_abs.shape), -                           sig_abs ** 1, -                           sig_abs ** 2, -                           sig_abs ** 3, -                           sig_abs ** 4, -                           ]).T -        sig_dpd = sig * np.sum(A_sig * coefs, axis=1) -        return sig_dpd, A_sig - -    def _dpd_phase(self, sig, coefs=None): -        if coefs is None: -            coefs = self.coefs_pm -        assert isinstance(sig[0], np.complex64), "Sig is not complex64 but {}".format(sig[0].dtype) -        sig_abs = np.abs(sig) -        A_phase = np.vstack([np.ones(sig_abs.shape), -                             sig_abs ** 1, -                             sig_abs ** 2, -                             sig_abs ** 3, -                             sig_abs ** 4, -                             ]).T -        phase_diff_est = np.sum(A_phase * coefs, axis=1) -        return phase_diff_est, A_phase - -    def _next_am_coefficent(self, tx_choice, rx_choice): -        """Calculate new coefficients for AM/AM correction""" -        rx_dpd, rx_A = self._dpd_amplitude(rx_choice) -        rx_dpd = rx_dpd * ( -            np.median(np.abs(tx_choice)) / -            np.median(np.abs(rx_dpd))) -        err = np.abs(rx_dpd) - np.abs(tx_choice) -        mse = np.mean(np.abs((rx_dpd - tx_choice) ** 2)) -        self.mses_am.append(mse) -        self.errs_am.append(np.mean(err**2)) - -        reg = linear_model.Ridge(alpha=0.00001) -        reg.fit(rx_A, err) -        a_delta = reg.coef_ -        new_coefs_am = self.coefs_am - self.learning_rate_am * a_delta -        new_coefs_am = new_coefs_am * (self.coefs_am[0] / new_coefs_am[0]) -        return new_coefs_am - -    def _next_pm_coefficent(self, tx_choice, rx_choice): -        """Calculate new coefficients for AM/PM correction -        Assuming deviations smaller than pi/2""" -        phase_diff_choice = np.angle( -            (rx_choice * tx_choice.conjugate()) / -            (np.abs(rx_choice) * np.abs(tx_choice)) -        ) -        plt.hist(phase_diff_choice) -        plt.savefig('/tmp/hist_' + str(np.random.randint(0,1000)) + '.svg') -        plt.clf() -        phase_diff_est, phase_A = self._dpd_phase(rx_choice) -        err_phase = phase_diff_est - phase_diff_choice -        self.errs_pm.append(np.mean(np.abs(err_phase ** 2))) - -        reg = linear_model.Ridge(alpha=0.00001) -        reg.fit(phase_A, err_phase) -        p_delta = reg.coef_ -        new_coefs_pm = self.coefs_pm - self.learning_rate_pm * p_delta - -        return new_coefs_pm, phase_diff_choice - -class LutModel: -    """Implements a model that calculates lookup table coefficients""" - -    def __init__(self, -                 c, -                 SA, -                 MER, -                 learning_rate=1., -                 plot=False): -        logging.debug("Initialising LUT Model") -        self.c = c -        self.SA = SA -        self.MER = MER -        self.learning_rate = learning_rate -        self.plot = plot - -    def train(self, tx_dpd, rx_received): -        pass - -    def get_dpd_data(self): -        return ("lut", np.ones(32, dtype=np.complex64)) - -# The MIT License (MIT) -# -# Copyright (c) 2017 Andreas Steger -# Copyright (c) 2017 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. +from src.Model_Poly import Poly +from src.Model_Lut import Lut diff --git a/dpd/src/Model_AM.py b/dpd/src/Model_AM.py new file mode 100644 index 0000000..ef6cc6c --- /dev/null +++ b/dpd/src/Model_AM.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# +# DPD Calculation 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 + +logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) + +import numpy as np +import matplotlib.pyplot as plt +from sklearn import linear_model + + +def check_input_get_next_coefs(tx_dpd, rx_received): +    is_float32 = lambda x: (isinstance(x, np.ndarray) and +                        x.dtype == np.float32 and +                        x.flags.contiguous) +    assert is_float32(tx_dpd), \ +           "tx_dpd is not float32 but {}".format(tx_dpd[0].dtype) +    assert is_float32(rx_received), \ +            "rx_received is not float32 but {}".format(tx_dpd[0].dtype) + + +class Model_AM: +    """Calculates new coefficients using the measurement and the previous +    coefficients""" + +    def __init__(self, +                 c, +                 learning_rate_am=0.1, +                 plot=False): +        self.c = c + +        self.learning_rate_am = learning_rate_am +        self.plot = plot + +    def _plot(self, tx_dpd, rx_received, coefs_am, coefs_am_new): +        if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: +            tx_range, rx_est = self.calc_line(coefs_am, 0, 0.6) +            tx_range_new, rx_est_new = self.calc_line(coefs_am_new, 0, 0.6) + +            dt = datetime.datetime.now().isoformat() +            fig_path = logging_path + "/" + dt + "_Model_AM.svg" +            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=0.1) +            ax.set_title("Model_AM") +            ax.set_xlabel("TX Amplitude") +            ax.set_ylabel("RX Amplitude") +            ax.legend(loc=4) + +            fig.tight_layout() +            fig.savefig(fig_path) +            plt.close(fig) + +    def poly(self, sig): +        return np.array([sig ** i for i in range(1, 6)]).T + +    def fit_poly(self, tx_abs, rx_abs): +        return np.linalg.lstsq(self.poly(rx_abs), tx_abs)[0] + +    def calc_line(self, coefs, min_amp, max_amp): +        rx_range = np.linspace(min_amp, max_amp) +        tx_est = np.sum(self.poly(rx_range) * coefs, axis=1) +        return tx_est, rx_range + +    def get_next_coefs(self, tx_dpd, rx_received, coefs_am): +        check_input_get_next_coefs(tx_dpd, rx_received) + +        coefs_am_new = self.fit_poly(tx_dpd, rx_received) +        self._plot(tx_dpd, rx_received, coefs_am, coefs_am_new) + +        return coefs_am_new + +# The MIT License (MIT) +# +# Copyright (c) 2017 Andreas Steger +# +# 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/dpd/src/Model_PM.py b/dpd/src/Model_PM.py new file mode 100644 index 0000000..6639382 --- /dev/null +++ b/dpd/src/Model_PM.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# +# DPD Calculation 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 + +logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) + +import numpy as np +import matplotlib.pyplot as plt +from sklearn import linear_model + + +def check_input_get_next_coefs(tx_dpd, phase_diff): +    is_float32 = lambda x: (isinstance(x, np.ndarray) and +                        x.dtype == np.float32 and +                        x.flags.contiguous) +    assert is_float32(tx_dpd), \ +           "tx_dpd is not float32 but {}".format(tx_dpd[0].dtype) +    assert is_float32(phase_diff), \ +            "phase_diff is not float32 but {}".format(tx_dpd[0].dtype) +    assert tx_dpd.shape == phase_diff.shape, \ +        "tx_dpd.shape {}, phase_diff.shape {}".format( +        tx_dpd.shape, phase_diff.shape) + + +class Model_PM: +    """Calculates new coefficients using the measurement and the previous +    coefficients""" + +    def __init__(self, +                 c, +                 learning_rate_pm=0.1, +                 plot=False): +        self.c = c + +        self.learning_rate_pm = learning_rate_pm +        self.plot = plot + +    def _plot(self, tx_dpd, phase_diff, coefs_pm, coefs_pm_new): +        if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: +            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) + +            dt = datetime.datetime.now().isoformat() +            fig_path = logging_path + "/" + dt + "_Model_PM.svg" +            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=0.1) +            ax.set_title("Model_PM") +            ax.set_xlabel("TX Amplitude") +            ax.set_ylabel("Phase DIff") +            ax.legend(loc=4) + +            fig.tight_layout() +            fig.savefig(fig_path) +            plt.close(fig) + +    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)[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): +        check_input_get_next_coefs(tx_dpd, phase_diff) + +        coefs_pm_new = self.fit_poly(tx_dpd, phase_diff) +        self._plot(tx_dpd, phase_diff, coefs_pm, coefs_pm_new) + +        return coefs_pm_new + +# The MIT License (MIT) +# +# Copyright (c) 2017 Andreas Steger +# +# 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/dpd/src/Model_Poly.py b/dpd/src/Model_Poly.py new file mode 100644 index 0000000..f6c024c --- /dev/null +++ b/dpd/src/Model_Poly.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# +# DPD Calculation Engine, model implementation using polynomial +# +# http://www.opendigitalradio.org +# Licence: The MIT License, see notice at the end of this file + +import datetime +import os +import logging + +logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) + +import numpy as np +import matplotlib.pyplot as plt +from sklearn import linear_model + +import src.Model_AM as Model_AM +import src.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 _check_input_get_next_coefs(tx_abs, rx_abs, phase_diff): +    assert_np_float32(tx_abs) +    assert_np_float32(rx_abs) +    assert_np_float32(phase_diff) + +    assert tx_abs.shape == rx_abs.shape, \ +        "tx_abs.shape {}, rx_abs.shape {}".format( +            tx_abs.shape, rx_abs.shape) +    assert tx_abs.shape == phase_diff.shape, \ +        "tx_abs.shape {}, phase_diff.shape {}".format( +            tx_abs.shape, phase_diff.shape) + + +class Poly: +    """Calculates new coefficients using the measurement and the previous +    coefficients""" + +    def __init__(self, +                 c, +                 learning_rate_am=1.0, +                 learning_rate_pm=1.0, +                 plot=False): +        self.c = c + +        self.learning_rate_am = learning_rate_am +        self.learning_rate_pm = learning_rate_pm + +        self.reset_coefs() + +        self.model_am = Model_AM.Model_AM(c, plot=True) +        self.model_pm = Model_PM.Model_PM(c, plot=True) + +        self.plot = plot + +    def reset_coefs(self): +        self.coefs_am = np.zeros(5, dtype=np.float32) +        self.coefs_am[0] = 1 +        self.coefs_pm = np.zeros(5, dtype=np.float32) +        return self.coefs_am, self.coefs_pm + +    def train(self, tx_abs, rx_abs, phase_diff): +        _check_input_get_next_coefs(tx_abs, rx_abs, phase_diff) + +        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) + +        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 + +    def get_dpd_data(self): +        return "poly", self.coefs_am, self.coefs_pm + +# The MIT License (MIT) +# +# Copyright (c) 2017 Andreas Steger +# +# 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/dpd/src/Symbol_align.py b/dpd/src/Symbol_align.py index 05a9049..6c814a8 100644 --- a/dpd/src/Symbol_align.py +++ b/dpd/src/Symbol_align.py @@ -147,7 +147,8 @@ class Symbol_align:          delta_sample_int = np.round(delta_sample).astype(int)          error = np.abs(delta_sample_int - delta_sample)          if error > 0.1: -            raise RuntimeError("Could not calculate sample offset") +            raise RuntimeError("Could not calculate " \ +                               "sample offset. Error {}".format(error))          return delta_sample_int      def calc_offset(self, tx): diff --git a/dpd/src/const.py b/dpd/src/const.py index 1011c6c..75ff819 100644 --- a/dpd/src/const.py +++ b/dpd/src/const.py @@ -36,3 +36,9 @@ class const:          # frequency per bin = 1kHz          # phase difference per sample offset = delta_t * 2 * pi * delta_freq          self.phase_offset_per_sample = 1. / sample_rate * 2 * np.pi * 1000 + +        # Constants for ExtractStatistic +        self.ES_start = 0.0 +        self.ES_end = 1.0 +        self.ES_n_bins = 64 +        self.ES_n_per_bin = 256 diff --git a/dpd/src/phase_align.py b/dpd/src/phase_align.py index 7f82392..7317d70 100644 --- a/dpd/src/phase_align.py +++ b/dpd/src/phase_align.py @@ -75,7 +75,7 @@ def phase_align(sig, ref_sig, plot=False):          plt.legend(loc=4)          plt.tight_layout()          plt.savefig(fig_path) -        plt.clf() +        plt.close()      return sig diff --git a/dpd/src/subsample_align.py b/dpd/src/subsample_align.py index 6d1cd2a..b0cbe88 100755 --- a/dpd/src/subsample_align.py +++ b/dpd/src/subsample_align.py @@ -78,7 +78,7 @@ def subsample_align(sig, ref_sig, plot=False):              plt.plot(ixs, taus)              plt.title("Subsample correlation, minimum is best: {}".format(best_tau))              plt.savefig(tau_path) -            plt.clf() +            plt.close()          # Prepare rotate_vec = fft_sig with rotated phase          rotate_vec = np.exp(1j * best_tau * omega) | 
