From bf53849fba5796d1b98254a7eeab575a50ae7996 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 06:42:45 +0100 Subject: DPD: Remove mention of conda from README --- dpd/README.md | 40 ++++++++-------- dpd/environment.yml | 134 ---------------------------------------------------- 2 files changed, 20 insertions(+), 154 deletions(-) delete mode 100644 dpd/environment.yml diff --git a/dpd/README.md b/dpd/README.md index 24611d9..1b481c0 100644 --- a/dpd/README.md +++ b/dpd/README.md @@ -81,32 +81,30 @@ essential that there is no nonlinearity in the RX path! Software Setup -------------- -We assume that you already installed *ODR-DabMux* and *ODR-DabMod*. In order to -satisfy dependencies for the predistortion, you can install all required python -modules using *conda*. To obtain the *conda* command line tool, install -[miniconda](https://conda.io/docs/user-guide/install/linux.html) and do the -beginners tutorial. It helps you keep the global python environment clean and -install the exact same package versions as we used for development. - -``` -conda env create -f dpd/environment.yml -source activate dab -``` - -Alternatively you can also install the dependencies from your distribution. -You will need at least scipy, matplotlib and python-zeromq, and maybe more. - +We assume that you already installed *ODR-DabMux* and *ODR-DabMod*. +You should install the required python dependencies for the DPDCE using +distribution packages. You will need at least scipy, matplotlib and +python-zeromq. Use the predistortion ---------------------- -Run the multiplexer and the modulator: +Make sure you have a ODR-DabMux running with a TCP output on port 9200. + +Then run the modulator, with the example dpd configuration file. ``` -ODR-DabMux/src/odr-dabmux ../simple.mux -ODR-DabMod/odr-dabmod dpd/dpd.ini +./odr-dabmod dpd/dpd.ini ``` +This configuration file is different from usual defaults in several respects: + + * logging to /tmp/dabmod.log + * 4x oversampling: 8192000 sample rate + * a very small digital gain, which will be overridden by the DPDCE + * predistorter enabled + * UHD output with a rather low TX gain, which will be overridden by DPDCE + The DPDCE uses automatic gain control for both TX and RX gain to get both a high quantisation quality for the most frequent amplitude regions and a high enough back-off so the peaks are also quantised correctly. This means that the @@ -125,6 +123,7 @@ change. cd dpd python main.py --plot ``` + The DPDCE now does 10 iterations, and tries to improve the predistortion effectiveness. In each step the learning rate is decreased. The learning rate is the factor with which new coefficients are weighted in a weighted mean with the old @@ -137,7 +136,8 @@ time stamp and its label. Following plots are generated chronologically: - ExtractStatistic: Extracted information from one or multiple measurements. - Model\_AM: Fitted function for the amplitudes of the power amplifier against the TX amplitude. - Model\_PM: Fitted function for the phase difference of the power amplifier against the TX amplitude. - - adapt.pkl: Contains the settings for the predistortion. To load them again without further measurements, you can use `apply_adapt_dumps.py`. + - adapt.pkl: Contains all settings for the predistortion. + You can load them again without doing measurements with the `apply_adapt_dumps.py` script. - MER: Constellation diagram used to calculate the modulation error rate. After the run you should be able to observe that the peak-to-shoulder @@ -198,7 +198,7 @@ TODO same. - At the moment we assume that the USRP RX gain has to be larger than 30dB and the received signal should have a median absolute value of 0.05 in order to - have a hight quality quantization. Do measurements to support or improve + have a high quality quantization. Do measurements to support or improve this heuristic. - Check if we need to measure MER differently (average over more symbols?) - Is -45dBm the best RX feedback power level? diff --git a/dpd/environment.yml b/dpd/environment.yml deleted file mode 100644 index 8abedb2..0000000 --- a/dpd/environment.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: dab -channels: -- jochym -- defaults -dependencies: -- _nb_ext_conf=0.4.0=py27_1 -- anaconda-client=1.6.3=py27_0 -- backports=1.0=py27_0 -- backports_abc=0.5=py27_0 -- bleach=1.5.0=py27_0 -- cairo=1.12.18=6 -- clyent=1.2.2=py27_0 -- configparser=3.5.0=py27_0 -- cycler=0.10.0=py27_0 -- dbus=1.10.10=0 -- decorator=4.0.11=py27_0 -- entrypoints=0.2.3=py27_0 -- enum34=1.1.6=py27_0 -- expat=2.1.0=0 -- fontconfig=2.11.1=6 -- freetype=2.5.5=2 -- functools32=3.2.3.2=py27_0 -- get_terminal_size=1.0.0=py27_0 -- glib=2.50.2=1 -- gst-plugins-base=1.8.0=0 -- gstreamer=1.8.0=0 -- harfbuzz=0.9.39=1 -- html5lib=0.9999999=py27_0 -- icu=54.1=0 -- ipykernel=4.6.1=py27_0 -- ipython=5.3.0=py27_0 -- ipython_genutils=0.2.0=py27_0 -- ipywidgets=6.0.0=py27_0 -- jinja2=2.9.6=py27_0 -- jpeg=8d=2 -- jsonschema=2.6.0=py27_0 -- jupyter=1.0.0=py27_3 -- jupyter_client=5.1.0=py27_0 -- jupyter_console=5.1.0=py27_0 -- jupyter_core=4.3.0=py27_0 -- libffi=3.2.1=1 -- libgcc=5.2.0=0 -- libgfortran=3.0.0=1 -- libiconv=1.14=0 -- libpng=1.6.27=0 -- libsodium=1.0.10=0 -- libxcb=1.12=1 -- libxml2=2.9.4=0 -- markupsafe=0.23=py27_2 -- matplotlib=2.0.2=np112py27_0 -- mistune=0.7.4=py27_0 -- mkl=2017.0.1=0 -- natsort=5.0.3=py27_0 -- nb_anacondacloud=1.4.0=py27_0 -- nb_conda=2.2.0=py27_0 -- nb_conda_kernels=2.1.0=py27_0 -- nbconvert=5.2.1=py27_0 -- nbformat=4.3.0=py27_0 -- nbpresent=3.0.2=py27_0 -- notebook=5.0.0=py27_0 -- numpy=1.12.1=py27_0 -- openssl=1.0.2k=2 -- pandas=0.20.1=np112py27_0 -- pandocfilters=1.4.1=py27_0 -- pango=1.39.0=0 -- path.py=10.3.1=py27_0 -- pathlib2=2.3.0=py27_0 -- pcre=8.39=1 -- pexpect=4.2.1=py27_0 -- pickleshare=0.7.4=py27_0 -- pip=9.0.1=py27_1 -- pixman=0.32.6=0 -- plotly=2.0.9=py27_0 -- prompt_toolkit=1.0.14=py27_0 -- ptyprocess=0.5.2=py27_0 -- py2cairo=1.10.0=py27_2 -- pycairo=1.10.0=py27_0 -- pygments=2.2.0=py27_0 -- pyparsing=2.1.4=py27_0 -- pyqt=5.6.0=py27_2 -- python=2.7.13=0 -- python-dateutil=2.6.0=py27_0 -- pytz=2017.2=py27_0 -- pyyaml=3.12=py27_0 -- pyzmq=16.0.2=py27_0 -- qt=5.6.2=0 -- qtconsole=4.3.0=py27_0 -- readline=6.2=2 -- requests=2.14.2=py27_0 -- scandir=1.5=py27_0 -- scikit-learn=0.18.1=np112py27_1 -- scipy=0.19.0=np112py27_0 -- setuptools=27.2.0=py27_0 -- simplegeneric=0.8.1=py27_1 -- singledispatch=3.4.0.3=py27_0 -- sip=4.18=py27_0 -- six=1.10.0=py27_0 -- sqlite=3.13.0=0 -- ssl_match_hostname=3.4.0.2=py27_1 -- subprocess32=3.2.7=py27_0 -- terminado=0.6=py27_0 -- testpath=0.3.1=py27_0 -- tk=8.5.18=0 -- tornado=4.5.1=py27_0 -- tqdm=4.11.2=py27_0 -- traitlets=4.3.2=py27_0 -- wcwidth=0.1.7=py27_0 -- wheel=0.29.0=py27_0 -- widgetsnbextension=2.0.0=py27_0 -- yaml=0.1.6=0 -- zeromq=4.1.5=0 -- zlib=1.2.8=3 -- atk=2.8.0=0 -- gdk-pixbuf=2.28.2=0 -- gtk2=2.24.28=0 -- pygobject2=2.28.6=0 -- pygtk2=2.24.0=0 -- pip: - - backports-abc==0.5 - - backports.shutil-get-terminal-size==1.0.0 - - backports.ssl-match-hostname==3.4.0.2 - - ipython-genutils==0.2.0 - - jupyter-client==5.1.0 - - jupyter-console==5.1.0 - - jupyter-core==4.3.0 - - multiprocessing==2.6.2.1 - - nb-anacondacloud==1.4.0 - - nb-conda==2.2.0 - - nb-conda-kernels==2.1.0 - - prompt-toolkit==1.0.14 - - pynverse==0.1.4.4 - - zmq==0.0.0 -prefix: /home/andreas/miniconda2/envs/dab - -- cgit v1.2.3 From a62e05c9564045044df734fd3723b3f665ad91ae Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 06:43:49 +0100 Subject: DPD: Make logging to file optional in all modules, simplify apply_adapt_dumps --- dpd/apply_adapt_dumps.py | 132 ++----------------------------------------- dpd/dpd.ini | 2 +- dpd/main.py | 2 +- dpd/src/Adapt.py | 10 +++- dpd/src/Const.py | 6 +- dpd/src/Dab_Util.py | 15 +++-- dpd/src/ExtractStatistic.py | 8 ++- dpd/src/MER.py | 8 ++- dpd/src/Measure.py | 2 - dpd/src/Measure_Shoulders.py | 8 ++- dpd/src/Model.py | 6 ++ dpd/src/Model_AM.py | 8 ++- dpd/src/Model_Lut.py | 6 +- dpd/src/Model_PM.py | 8 ++- dpd/src/Model_Poly.py | 6 +- dpd/src/RX_Agc.py | 8 ++- dpd/src/Symbol_align.py | 4 +- dpd/src/TX_Agc.py | 5 +- dpd/src/phase_align.py | 7 ++- dpd/src/subsample_align.py | 8 ++- 20 files changed, 92 insertions(+), 167 deletions(-) diff --git a/dpd/apply_adapt_dumps.py b/dpd/apply_adapt_dumps.py index 6b15aff..f1f3359 100755 --- a/dpd/apply_adapt_dumps.py +++ b/dpd/apply_adapt_dumps.py @@ -6,38 +6,16 @@ # http://www.opendigitalradio.org # Licence: The MIT License, see notice at the end of this file -"""This Python script is the main file for ODR-DabMod's DPD Computation Engine. -This engine calculates and updates the parameter of the digital -predistortion module of ODR-DabMod.""" - import datetime import os -import matplotlib import glob -import natsort -matplotlib.use('GTKAgg') - import logging dt = datetime.datetime.now().isoformat() -logging_path = "/tmp/dpd_{}".format(dt).replace(".", "_").replace(":", "-") -os.makedirs(logging_path) logging.basicConfig(format='%(asctime)s - %(module)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S', - filename='{}/dpd.log'.format(logging_path), - filemode='w', level=logging.DEBUG) -# also log up to INFO to console -console = logging.StreamHandler() -console.setLevel(logging.INFO) -# set a format which is simpler for console use -formatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s') -# tell the handler to use this format -console.setFormatter(formatter) -# add the handler to the root logger -logging.getLogger('').addHandler(console) - import src.Measure as Measure import src.Model as Model import src.ExtractStatistic as ExtractStatistic @@ -59,120 +37,22 @@ parser.add_argument('--port', default=50055, type=int, 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, type=int, - help='Sample rate', - required=False) -parser.add_argument('--coefs', default='/tmp/poly.coef', +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=75, - help='TX Gain', - required=False, - type=int) -parser.add_argument('--rxgain', default=30, - help='TX Gain', - required=False, - type=int) -parser.add_argument('--digital_gain', default=1, - help='Digital Gain', - required=False, - type=float) -parser.add_argument('--samps', default='81920', type=int, - help='Number of samples to request from ODR-DabMod', - required=False) -parser.add_argument('--target_median', default=0.1, - help='target_median', - required=False, - type=float) -parser.add_argument('--searchpath', default='./stored', type=str, - help='Path to search .pkl files with stored configuration' - 'for adapt', - required=False) -parser.add_argument('-L', '--lut', - help='Use lookup table instead of polynomial predistorter', - action="store_true") +parser.add_argument('file', help='File to read the DPD settings from') cli_args = parser.parse_args() 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 = cli_args.samps -samplerate = cli_args.samplerate -searchpath = cli_args.searchpath -target_median = cli_args.target_median - -c = src.Const.Const(samplerate, target_median, False) -SA = src.Symbol_align.Symbol_align(c) -MER = src.MER.MER(c) -MS = src.Measure_Shoulders.Measure_Shoulders(c) - -meas = Measure.Measure(samplerate, port, num_req) -extStat = ExtractStatistic.ExtractStatistic(c) -adapt = Adapt.Adapt(port_rc, coef_path) -dpddata = adapt.get_predistorter - -if cli_args.lut: - model = Model.Lut(c) -else: - model = Model.Poly(c) -adapt.set_predistorter(model.get_dpd_data()) -adapt.set_digital_gain(digital_gain) -adapt.set_txgain(txgain) -adapt.set_rxgain(rxgain) - -tx_gain = adapt.get_txgain() -rx_gain = adapt.get_rxgain() -digital_gain = adapt.get_digital_gain() - -dpddata = adapt.get_predistorter() -if dpddata[0] == "poly": - coefs_am = dpddata[1] - coefs_pm = dpddata[2] - logging.info( - "TX gain {}, RX gain {}, dpd_coefs_am {}," - " dpd_coefs_pm {}, digital_gain {}".format( - tx_gain, rx_gain, coefs_am, coefs_pm, digital_gain - ) - ) -elif dpddata[0] == "lut": - scalefactor = dpddata[1] - lut = dpddata[2] - logging.info( - "TX gain {}, RX gain {}, LUT scalefactor {}," - " LUT {}, digital_gain {}".format( - tx_gain, rx_gain, scalefactor, lut, digital_gain - ) - ) -else: - logging.error("Unknown dpd data format {}".format(dpddata[0])) - -tx_agc = TX_Agc.TX_Agc(adapt, c) - -# Automatic Gain Control -agc = Agc.Agc(meas, adapt, c) -agc.run() - -paths = natsort.natsorted(glob.glob(searchpath + "/*.pkl")) -print(paths) - -for i, path in enumerate(paths): - print(i, path) - adapt.load(path) - dpddata_after = adapt.get_predistorter() - - coefs_am, coefs_pm = model.reset_coefs() - adapt.set_predistorter(("poly", coefs_am, coefs_pm)) - print("Loaded configuration without pre-distortion") +filename = cli_args.file - raw_input("Key for pre-distortion ") - adapt.set_predistorter(dpddata_after) - print("Pre-distortion done") +adapt = Adapt(port_rc, coef_path) - raw_input("Key for next ") +print("Loading and applying DPD settings from {}".format(filename)) +adapt.load(filename) # The MIT License (MIT) # diff --git a/dpd/dpd.ini b/dpd/dpd.ini index 7e4bd5f..af5c2fb 100644 --- a/dpd/dpd.ini +++ b/dpd/dpd.ini @@ -18,7 +18,7 @@ gainmode=var rate=8192000 # keep in mind that the DPDCE will set the digital gain through the RC! -digital_gain=0.6 +digital_gain=0.01 [firfilter] enabled=1 diff --git a/dpd/main.py b/dpd/main.py index 49c0d1d..e15c6b5 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -45,7 +45,7 @@ parser.add_argument('--digital_gain', default=0.6, required=False, type=float) parser.add_argument('--target_median', default=0.05, - help='target_median', + help='The target median for the RX and TX AGC', required=False, type=float) parser.add_argument('--samps', default='81920', type=int, diff --git a/dpd/src/Adapt.py b/dpd/src/Adapt.py index 7e19a2c..c78c920 100644 --- a/dpd/src/Adapt.py +++ b/dpd/src/Adapt.py @@ -16,7 +16,10 @@ import os import datetime import pickle -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None LUT_LEN = 32 FORMAT_POLY = 1 @@ -226,7 +229,10 @@ class Adapt: """Backup current settings to a file""" dt = datetime.datetime.now().isoformat() if path is None: - path = logging_path + "/" + dt + "_adapt.pkl" + if logging_path is not None: + path = logging_path + "/" + dt + "_adapt.pkl" + else: + raise Exception("Cannot dump Adapt without either logging_path or path set") d = { "txgain": self.get_txgain(), "rxgain": self.get_rxgain(), diff --git a/dpd/src/Const.py b/dpd/src/Const.py index 6c9bafa..d80cfac 100644 --- a/dpd/src/Const.py +++ b/dpd/src/Const.py @@ -73,14 +73,14 @@ class Const: self.MPM_tx_min = 0.1 # Constants for TX_Agc - self.TAGC_max_txgain = 89 # USRP specific + self.TAGC_max_txgain = 89 # USRP B200 specific self.TAGC_tx_median_target = target_median self.TAGC_tx_median_max = self.TAGC_tx_median_target * 1.4 self.TAGC_tx_median_min = self.TAGC_tx_median_target / 1.4 # Constants for RX_AGC - self.RAGC_min_rxgain = 25 # USRP specific - self.RAGC_rx_median_target = self.TAGC_tx_median_target + self.RAGC_min_rxgain = 25 # USRP B200 specific + self.RAGC_rx_median_target = target_median # The MIT License (MIT) # diff --git a/dpd/src/Dab_Util.py b/dpd/src/Dab_Util.py index 2021f38..56c9503 100644 --- a/dpd/src/Dab_Util.py +++ b/dpd/src/Dab_Util.py @@ -9,7 +9,10 @@ import datetime import os import logging -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib @@ -53,7 +56,7 @@ class Dab_Util: off = sig_rec.shape[0] c = np.abs(signal.correlate(sig_orig, sig_rec)) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() corr_path = (logging_path + "/" + dt + "_tx_rx_corr.svg") plt.plot(c, label="corr") @@ -107,7 +110,7 @@ class Dab_Util: Returns an aligned version of sig_tx and sig_rx by cropping and subsample alignment """ - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_sync_raw.svg" @@ -151,7 +154,7 @@ class Dab_Util: sig_tx = sig_tx[:-1] sig_rx = sig_rx[:-1] - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_sync_sample_aligned.svg" @@ -175,7 +178,7 @@ class Dab_Util: sig_rx = sa.subsample_align(sig_rx, sig_tx) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_sync_subsample_aligned.svg" @@ -199,7 +202,7 @@ class Dab_Util: sig_rx = pa.phase_align(sig_rx, sig_tx) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_sync_phase_aligned.svg" diff --git a/dpd/src/ExtractStatistic.py b/dpd/src/ExtractStatistic.py index d27cd77..e917909 100644 --- a/dpd/src/ExtractStatistic.py +++ b/dpd/src/ExtractStatistic.py @@ -12,8 +12,10 @@ import matplotlib.pyplot as plt import datetime import os import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None def _check_input_extract(tx_dpd, rx_received): @@ -64,7 +66,7 @@ class ExtractStatistic: self.plot = c.ES_plot def _plot_and_log(self, tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists): - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_ExtractStatistic.png" diff --git a/dpd/src/MER.py b/dpd/src/MER.py index f186261..a4c3591 100644 --- a/dpd/src/MER.py +++ b/dpd/src/MER.py @@ -11,7 +11,7 @@ import logging try: logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) except: - logging_path = "/tmp/" + logging_path = None import numpy as np import matplotlib @@ -76,9 +76,11 @@ class MER: spectrum = self._calc_spectrum(tx) - if self.plot: + if self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_MER" + debug_name + ".svg" + else: + fig_path = None MERs = [] for i, (x, y) in enumerate(self._split_in_carrier( @@ -103,7 +105,7 @@ class MER: ylim = ax.get_ylim() ax.set_ylim(ylim[0] - (ylim[1] - ylim[0]) * 0.1, ylim[1]) - if self.plot: + if fig_path is not None: plt.tight_layout() plt.savefig(fig_path) plt.show() diff --git a/dpd/src/Measure.py b/dpd/src/Measure.py index d4b1d9e..e4333d9 100644 --- a/dpd/src/Measure.py +++ b/dpd/src/Measure.py @@ -11,9 +11,7 @@ import struct import numpy as np import src.Dab_Util as DU import os - import logging -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) class Measure: """Collect Measurement from DabMod""" diff --git a/dpd/src/Measure_Shoulders.py b/dpd/src/Measure_Shoulders.py index c733dfd..2249ac6 100644 --- a/dpd/src/Measure_Shoulders.py +++ b/dpd/src/Measure_Shoulders.py @@ -10,7 +10,10 @@ import os import logging import multiprocessing -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib.pyplot as plt @@ -79,6 +82,9 @@ class Measure_Shoulders: self.plot = c.MS_plot def _plot(self, signal): + if logging_path is None: + return + dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_sync_subsample_aligned.svg" diff --git a/dpd/src/Model.py b/dpd/src/Model.py index 67feeb6..b2c303f 100644 --- a/dpd/src/Model.py +++ b/dpd/src/Model.py @@ -2,6 +2,12 @@ from src.Model_Poly import Poly from src.Model_Lut import Lut +def select_model_from_dpddata(dpddata): + if dpddata[0] == 'lut': + return Lut + elif dpddata[0] == 'poly': + return Poly + # The MIT License (MIT) # # Copyright (c) 2017 Andreas Steger diff --git a/dpd/src/Model_AM.py b/dpd/src/Model_AM.py index d7e880c..596ca4a 100644 --- a/dpd/src/Model_AM.py +++ b/dpd/src/Model_AM.py @@ -8,8 +8,10 @@ import datetime import os import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib.pyplot as plt @@ -55,7 +57,7 @@ class Model_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: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: 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) diff --git a/dpd/src/Model_Lut.py b/dpd/src/Model_Lut.py index 6d4db52..b349433 100644 --- a/dpd/src/Model_Lut.py +++ b/dpd/src/Model_Lut.py @@ -7,8 +7,10 @@ import os import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np diff --git a/dpd/src/Model_PM.py b/dpd/src/Model_PM.py index d4f8c00..e721d1a 100644 --- a/dpd/src/Model_PM.py +++ b/dpd/src/Model_PM.py @@ -8,8 +8,10 @@ import datetime import os import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib.pyplot as plt @@ -41,7 +43,7 @@ class Model_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: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: 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) diff --git a/dpd/src/Model_Poly.py b/dpd/src/Model_Poly.py index ff15941..1cf8ecd 100644 --- a/dpd/src/Model_Poly.py +++ b/dpd/src/Model_Poly.py @@ -7,8 +7,10 @@ import os import logging - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np diff --git a/dpd/src/RX_Agc.py b/dpd/src/RX_Agc.py index 670fbbb..7b5e0b6 100644 --- a/dpd/src/RX_Agc.py +++ b/dpd/src/RX_Agc.py @@ -9,7 +9,10 @@ import datetime import os import logging import time -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib @@ -70,6 +73,9 @@ class Agc: def plot_estimates(self): """Plots the estimate of for Max, Median, Mean for different number of samples.""" + if logging_path is None: + return + self.adapt.set_rxgain(self.min_rxgain) time.sleep(1) diff --git a/dpd/src/Symbol_align.py b/dpd/src/Symbol_align.py index d921f25..f2802ee 100644 --- a/dpd/src/Symbol_align.py +++ b/dpd/src/Symbol_align.py @@ -12,7 +12,7 @@ import logging try: logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) except: - logging_path = "/tmp/" + logging_path = None import numpy as np import scipy @@ -75,7 +75,7 @@ class Symbol_align: offset = peaks[np.argmin([tx_product_avg[peak] for peak in peaks])] - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_Symbol_align.svg" diff --git a/dpd/src/TX_Agc.py b/dpd/src/TX_Agc.py index 3c804fa..7555450 100644 --- a/dpd/src/TX_Agc.py +++ b/dpd/src/TX_Agc.py @@ -10,7 +10,10 @@ import os import logging import time -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib diff --git a/dpd/src/phase_align.py b/dpd/src/phase_align.py index 68c216d..21a210c 100644 --- a/dpd/src/phase_align.py +++ b/dpd/src/phase_align.py @@ -7,7 +7,10 @@ import datetime import os import logging -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np import matplotlib.pyplot as plt @@ -24,7 +27,7 @@ def phase_align(sig, ref_sig, plot=False): real_diffs = np.cos(angle_diff) imag_diffs = np.sin(angle_diff) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and plot: + if logging.getLogger().getEffectiveLevel() == logging.DEBUG and plot and logging_path is not None: dt = datetime.datetime.now().isoformat() fig_path = logging_path + "/" + dt + "_phase_align.svg" diff --git a/dpd/src/subsample_align.py b/dpd/src/subsample_align.py index 68f3591..a5e9f8c 100755 --- a/dpd/src/subsample_align.py +++ b/dpd/src/subsample_align.py @@ -7,8 +7,10 @@ import datetime import logging import os - -logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +try: + logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) +except AttributeError: + logging_path = None import numpy as np from scipy import optimize @@ -72,7 +74,7 @@ def subsample_align(sig, ref_sig, plot=False): if optim_result.success: best_tau = optim_result.x - if plot: + if plot and logging_path is not None: corr = np.vectorize(correlate_for_delay) ixs = np.linspace(-1, 1, 100) taus = corr(ixs) -- cgit v1.2.3 From 02c6e85a988149a2ee5d3f54e5120b664c5d6716 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 07:00:19 +0100 Subject: Shorten TII name() printout --- src/TII.cpp | 12 +++--------- src/TII.h | 4 ---- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/TII.cpp b/src/TII.cpp index 446d9c6..89cd6d0 100644 --- a/src/TII.cpp +++ b/src/TII.cpp @@ -160,20 +160,14 @@ TII::TII(unsigned int dabmode, const tii_config_t& tii_config, unsigned phase) : prepare_pattern(); } - -TII::~TII() -{ - PDEBUG("TII::~TII() @ %p\n", this); -} - const char* TII::name() { // Calculate name on demand because comb and pattern are // modifiable through RC std::stringstream ss; - ss << "TII(comb:" << m_conf.comb << - ", pattern:" << m_conf.pattern << - ", variant:" << (m_conf.old_variant ? "old" : "new") << ")"; + ss << "TII(c:" << m_conf.comb << + " p:" << m_conf.pattern << + " vrnt:" << (m_conf.old_variant ? "old" : "new") << ")"; m_name = ss.str(); return m_name.c_str(); diff --git a/src/TII.h b/src/TII.h index 4c5c605..b0ffdb3 100644 --- a/src/TII.h +++ b/src/TII.h @@ -84,9 +84,6 @@ class TII : public ModCodec, public RemoteControllable { public: TII(unsigned int dabmode, const tii_config_t& tii_config, unsigned phase); - virtual ~TII(); - TII(const TII&) = delete; - TII& operator=(const TII&) = delete; int process(Buffer* dataIn, Buffer* dataOut); const char* name(); @@ -128,4 +125,3 @@ class TII : public ModCodec, public RemoteControllable std::vector m_enabled_carriers; }; - -- cgit v1.2.3 From 740d4815fda8737cc7f021999d30aeacd851b032 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 07:40:20 +0100 Subject: DPDCE: fix Adapt init and pickle usage --- dpd/apply_adapt_dumps.py | 2 +- dpd/src/Adapt.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dpd/apply_adapt_dumps.py b/dpd/apply_adapt_dumps.py index f1f3359..0eb65f3 100755 --- a/dpd/apply_adapt_dumps.py +++ b/dpd/apply_adapt_dumps.py @@ -49,7 +49,7 @@ port_rc = cli_args.rc_port coef_path = cli_args.coefs filename = cli_args.file -adapt = Adapt(port_rc, coef_path) +adapt = Adapt.Adapt(port_rc, coef_path) print("Loading and applying DPD settings from {}".format(filename)) adapt.load(filename) diff --git a/dpd/src/Adapt.py b/dpd/src/Adapt.py index c78c920..939e50e 100644 --- a/dpd/src/Adapt.py +++ b/dpd/src/Adapt.py @@ -239,20 +239,20 @@ class Adapt: "digital_gain": self.get_digital_gain(), "predistorter": self.get_predistorter() } - with open(path, "w") as f: + with open(path, "wb") as f: pickle.dump(d, f) return path def load(self, path): """Restore settings from a file""" - with open(path, "r") as f: + with open(path, "rb") as f: d = pickle.load(f) - self.set_txgain(d["txgain"]) - self.set_digital_gain(d["digital_gain"]) - self.set_rxgain(d["rxgain"]) - self.set_predistorter(d["predistorter"]) + self.set_txgain(d["txgain"]) + self.set_digital_gain(d["digital_gain"]) + self.set_rxgain(d["rxgain"]) + self.set_predistorter(d["predistorter"]) # The MIT License (MIT) # -- cgit v1.2.3 From bc25ed90fb34398f9fd3066b7237d1306628b16f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 08:32:58 +0100 Subject: Transform Const into a global config structure, avoid depending on logging for plot location --- dpd/apply_adapt_dumps.py | 21 ++++----- dpd/main.py | 10 ++-- dpd/src/Adapt.py | 14 ++---- dpd/src/Const.py | 106 ------------------------------------------ dpd/src/Dab_Util.py | 29 +++++------- dpd/src/ExtractStatistic.py | 10 +--- dpd/src/GlobalConfig.py | 107 +++++++++++++++++++++++++++++++++++++++++++ dpd/src/MER.py | 9 +--- dpd/src/Measure.py | 5 +- dpd/src/Measure_Shoulders.py | 10 +--- dpd/src/Model_AM.py | 9 +--- dpd/src/Model_Lut.py | 6 --- dpd/src/Model_PM.py | 9 +--- dpd/src/Model_Poly.py | 7 --- dpd/src/RX_Agc.py | 9 +--- dpd/src/Symbol_align.py | 10 +--- dpd/src/TX_Agc.py | 6 --- dpd/src/phase_align.py | 9 +--- dpd/src/subsample_align.py | 12 ++--- 19 files changed, 160 insertions(+), 238 deletions(-) delete mode 100644 dpd/src/Const.py create mode 100644 dpd/src/GlobalConfig.py diff --git a/dpd/apply_adapt_dumps.py b/dpd/apply_adapt_dumps.py index 0eb65f3..20bc013 100755 --- a/dpd/apply_adapt_dumps.py +++ b/dpd/apply_adapt_dumps.py @@ -16,21 +16,11 @@ logging.basicConfig(format='%(asctime)s - %(module)s - %(levelname)s - %(message datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG) -import src.Measure as Measure -import src.Model as Model -import src.ExtractStatistic as ExtractStatistic import src.Adapt as Adapt -import src.RX_Agc as Agc -import src.TX_Agc as TX_Agc import argparse -import src.Const -import src.Symbol_align -import src.Measure_Shoulders -import src.MER - parser = argparse.ArgumentParser( - description="DPD Computation Engine for ODR-DabMod") + description="Load pkl dumps DPD settings into ODR-DabMod") parser.add_argument('--port', default=50055, type=int, help='port of DPD server to connect to (default: 50055)', required=False) @@ -49,7 +39,14 @@ port_rc = cli_args.rc_port coef_path = cli_args.coefs filename = cli_args.file -adapt = Adapt.Adapt(port_rc, coef_path) +# No need to initialise a GlobalConfig since adapt only needs this one field +class DummyConfig: + def __init__(self): + self.plot_location = None + +c = DummyConfig() + +adapt = Adapt.Adapt(c, port_rc, coef_path) print("Loading and applying DPD settings from {}".format(filename)) adapt.load(filename) diff --git a/dpd/main.py b/dpd/main.py index e15c6b5..24daa5a 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -58,7 +58,7 @@ parser.add_argument('-L', '--lut', help='Use lookup table instead of polynomial predistorter', action="store_true") parser.add_argument('--plot', - help='Enable all plots, to be more selective choose plots in Const.py', + help='Enable all plots, to be more selective choose plots in GlobalConfig.py', action="store_true") parser.add_argument('--name', default="", type=str, help='Name of the logging directory') @@ -113,17 +113,17 @@ from src.Adapt import Adapt from src.RX_Agc import Agc from src.TX_Agc import TX_Agc from src.Symbol_align import Symbol_align -from src.Const import Const +from src.GlobalConfig import GlobalConfig from src.MER import MER from src.Measure_Shoulders import Measure_Shoulders -c = Const(samplerate, target_median, plot) +c = GlobalConfig(cli_args, logging_path) SA = Symbol_align(c) MER = MER(c) MS = Measure_Shoulders(c) -meas = Measure(samplerate, port, num_req) +meas = Measure(c, samplerate, port, num_req) extStat = ExtractStatistic(c) -adapt = Adapt(port_rc, coef_path) +adapt = Adapt(c, port_rc, coef_path) if cli_args.lut: model = Lut(c) diff --git a/dpd/src/Adapt.py b/dpd/src/Adapt.py index 939e50e..153442b 100644 --- a/dpd/src/Adapt.py +++ b/dpd/src/Adapt.py @@ -16,11 +16,6 @@ import os import datetime import pickle -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - LUT_LEN = 32 FORMAT_POLY = 1 FORMAT_LUT = 2 @@ -58,8 +53,9 @@ class Adapt: ZMQ remote control. """ - def __init__(self, port, coef_path): + def __init__(self, config, port, coef_path): logging.debug("Instantiate Adapt object") + self.c = config self.port = port self.coef_path = coef_path self.host = "localhost" @@ -229,10 +225,10 @@ class Adapt: """Backup current settings to a file""" dt = datetime.datetime.now().isoformat() if path is None: - if logging_path is not None: - path = logging_path + "/" + dt + "_adapt.pkl" + if self.c.plot_location is not None: + path = self.c.plot_location + "/" + dt + "_adapt.pkl" else: - raise Exception("Cannot dump Adapt without either logging_path or path set") + raise Exception("Cannot dump Adapt without either plot_location or path set") d = { "txgain": self.get_txgain(), "rxgain": self.get_rxgain(), diff --git a/dpd/src/Const.py b/dpd/src/Const.py deleted file mode 100644 index d80cfac..0000000 --- a/dpd/src/Const.py +++ /dev/null @@ -1,106 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DPD Computation Engine, constants. -# -# Source for DAB standard: etsi_EN_300_401_v010401p p145 -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -import numpy as np - - -class Const: - def __init__(self, sample_rate, target_median, plot): - assert sample_rate == 8192000 # By now only constants for 8192000 - self.sample_rate = sample_rate - - # DAB frame - # Time domain - self.T_F = sample_rate / 2048000 * 196608 # Transmission frame duration - self.T_NULL = sample_rate / 2048000 * 2656 # Null symbol duration - self.T_S = sample_rate / 2048000 * 2552 # Duration of OFDM symbols of indices l = 1, 2, 3,... L; - self.T_U = sample_rate / 2048000 * 2048 # Inverse of carrier spacing - self.T_C = sample_rate / 2048000 * 504 # Duration of cyclic prefix - - # Frequency Domain - # example: np.delete(fft[3328:4865], 768) - self.FFT_delta = 1536 # Number of carrier frequencies - self.FFT_delete = 768 - self.FFT_start = 3328 - self.FFT_end = 4865 - - # Calculate sample offset from phase rotation - # time per sample = 1 / sample_rate - # 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_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_per_bin = 128 # Number of measurements pre bin - - # Constants for Measure_Shoulder - self.MS_enable = False - self.MS_plot = plot - - meas_offset = 976 # Offset from center frequency to measure shoulder [kHz] - meas_width = 100 # Size of frequency delta to measure shoulder [kHz] - shoulder_offset_edge = np.abs(meas_offset - self.FFT_delta) - self.MS_shoulder_left_start = self.FFT_start - shoulder_offset_edge - meas_width / 2 - self.MS_shoulder_left_end = self.FFT_start - shoulder_offset_edge + meas_width / 2 - self.MS_shoulder_right_start = self.FFT_end + shoulder_offset_edge - meas_width / 2 - self.MS_shoulder_right_end = self.FFT_end + shoulder_offset_edge + meas_width / 2 - self.MS_peak_start = self.FFT_start + 100 # Ignore region near edges - self.MS_peak_end = self.FFT_end - 100 - - self.MS_FFT_size = 8192 - self.MS_averaging_size = 4 * self.MS_FFT_size - self.MS_n_averaging = 40 - self.MS_n_proc = 4 - - # Constants for MER - self.MER_plot = plot - - # Constants for Model - self.MDL_plot = True or plot # Override default - - # Constants for Model_PM - # Set all phase offsets to zero for TX amplitude < MPM_tx_min - self.MPM_tx_min = 0.1 - - # Constants for TX_Agc - self.TAGC_max_txgain = 89 # USRP B200 specific - self.TAGC_tx_median_target = target_median - self.TAGC_tx_median_max = self.TAGC_tx_median_target * 1.4 - self.TAGC_tx_median_min = self.TAGC_tx_median_target / 1.4 - - # Constants for RX_AGC - self.RAGC_min_rxgain = 25 # USRP B200 specific - self.RAGC_rx_median_target = target_median - -# 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. diff --git a/dpd/src/Dab_Util.py b/dpd/src/Dab_Util.py index 56c9503..bc89a39 100644 --- a/dpd/src/Dab_Util.py +++ b/dpd/src/Dab_Util.py @@ -8,12 +8,6 @@ import datetime import os import logging - -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib @@ -36,10 +30,11 @@ class Dab_Util: complex IQ samples of a DAB signal """ - def __init__(self, sample_rate, plot=False): + def __init__(self, config, sample_rate, plot=False): """ :param sample_rate: sample rate [sample/sec] to use for calculations """ + self.c = config self.sample_rate = sample_rate self.dab_bandwidth = 1536000 # Bandwidth of a dab signal self.frame_ms = 96 # Duration of a Dab frame @@ -56,9 +51,9 @@ class Dab_Util: off = sig_rec.shape[0] c = np.abs(signal.correlate(sig_orig, sig_rec)) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - corr_path = (logging_path + "/" + dt + "_tx_rx_corr.svg") + corr_path = self.c.plot_location + "/" + dt + "_tx_rx_corr.png" plt.plot(c, label="corr") plt.legend() plt.savefig(corr_path) @@ -110,9 +105,9 @@ class Dab_Util: Returns an aligned version of sig_tx and sig_rx by cropping and subsample alignment """ - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_sync_raw.svg" + fig_path = self.c.plot_location + "/" + dt + "_sync_raw.png" fig, axs = plt.subplots(2) axs[0].plot(np.abs(sig_tx[:128]), label="TX Frame") @@ -154,9 +149,9 @@ class Dab_Util: sig_tx = sig_tx[:-1] sig_rx = sig_rx[:-1] - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_sync_sample_aligned.svg" + fig_path = self.c.plot_location + "/" + dt + "_sync_sample_aligned.png" fig, axs = plt.subplots(2) axs[0].plot(np.abs(sig_tx[:128]), label="TX Frame") @@ -178,9 +173,9 @@ class Dab_Util: sig_rx = sa.subsample_align(sig_rx, sig_tx) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_sync_subsample_aligned.svg" + fig_path = self.c.plot_location + "/" + dt + "_sync_subsample_aligned.png" fig, axs = plt.subplots(2) axs[0].plot(np.abs(sig_tx[:128]), label="TX Frame") @@ -202,9 +197,9 @@ class Dab_Util: sig_rx = pa.phase_align(sig_rx, sig_tx) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_sync_phase_aligned.svg" + fig_path = self.c.plot_location + "/" + dt + "_sync_phase_aligned.png" fig, axs = plt.subplots(2) axs[0].plot(np.abs(sig_tx[:128]), label="TX Frame") diff --git a/dpd/src/ExtractStatistic.py b/dpd/src/ExtractStatistic.py index e917909..639513a 100644 --- a/dpd/src/ExtractStatistic.py +++ b/dpd/src/ExtractStatistic.py @@ -8,14 +8,9 @@ import numpy as np import matplotlib.pyplot as plt - import datetime import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None def _check_input_extract(tx_dpd, rx_received): @@ -66,10 +61,9 @@ class ExtractStatistic: self.plot = c.ES_plot def _plot_and_log(self, tx_values, rx_values, phase_diffs_values, phase_diffs_values_lists): - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: - + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_ExtractStatistic.png" + 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)) diff --git a/dpd/src/GlobalConfig.py b/dpd/src/GlobalConfig.py new file mode 100644 index 0000000..684a881 --- /dev/null +++ b/dpd/src/GlobalConfig.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# DPD Computation Engine, constants and global configuration +# +# Source for DAB standard: etsi_EN_300_401_v010401p p145 +# +# http://www.opendigitalradio.org +# Licence: The MIT License, see notice at the end of this file + +import numpy as np + +class GlobalConfig: + def __init__(self, cli_args, plot_location): + self.sample_rate = cli_args.sample_rate + assert self.sample_rate == 8192000 # By now only constants for 8192000 + + self.plot_location = plot_location + + # DAB frame + # Time domain + self.T_F = self.sample_rate / 2048000 * 196608 # Transmission frame duration + self.T_NULL = self.sample_rate / 2048000 * 2656 # Null symbol duration + self.T_S = self.sample_rate / 2048000 * 2552 # Duration of OFDM symbols of indices l = 1, 2, 3,... L; + self.T_U = self.sample_rate / 2048000 * 2048 # Inverse of carrier spacing + self.T_C = self.sample_rate / 2048000 * 504 # Duration of cyclic prefix + + # Frequency Domain + # example: np.delete(fft[3328:4865], 768) + self.FFT_delta = 1536 # Number of carrier frequencies + self.FFT_delete = 768 + self.FFT_start = 3328 + self.FFT_end = 4865 + + # Calculate sample offset from phase rotation + # time per sample = 1 / sample_rate + # frequency per bin = 1kHz + # phase difference per sample offset = delta_t * 2 * pi * delta_freq + self.phase_offset_per_sample = 1. / self.sample_rate * 2 * np.pi * 1000 + + # Constants for ExtractStatistic + self.ES_plot = cli_args.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_per_bin = 128 # Number of measurements pre bin + + # Constants for Measure_Shoulder + self.MS_enable = False + self.MS_plot = cli_args.plot + + meas_offset = 976 # Offset from center frequency to measure shoulder [kHz] + meas_width = 100 # Size of frequency delta to measure shoulder [kHz] + shoulder_offset_edge = np.abs(meas_offset - self.FFT_delta) + self.MS_shoulder_left_start = self.FFT_start - shoulder_offset_edge - meas_width / 2 + self.MS_shoulder_left_end = self.FFT_start - shoulder_offset_edge + meas_width / 2 + self.MS_shoulder_right_start = self.FFT_end + shoulder_offset_edge - meas_width / 2 + self.MS_shoulder_right_end = self.FFT_end + shoulder_offset_edge + meas_width / 2 + self.MS_peak_start = self.FFT_start + 100 # Ignore region near edges + self.MS_peak_end = self.FFT_end - 100 + + self.MS_FFT_size = 8192 + self.MS_averaging_size = 4 * self.MS_FFT_size + self.MS_n_averaging = 40 + self.MS_n_proc = 4 + + # Constants for MER + self.MER_plot = cli_args.plot + + # Constants for Model + self.MDL_plot = cli_args.plot + + # Constants for Model_PM + # Set all phase offsets to zero for TX amplitude < MPM_tx_min + self.MPM_tx_min = 0.1 + + # Constants for TX_Agc + self.TAGC_max_txgain = 89 # USRP B200 specific + self.TAGC_tx_median_target = cli_args.target_median + self.TAGC_tx_median_max = self.TAGC_tx_median_target * 1.4 + self.TAGC_tx_median_min = self.TAGC_tx_median_target / 1.4 + + # Constants for RX_AGC + self.RAGC_min_rxgain = 25 # USRP B200 specific + self.RAGC_rx_median_target = cli_args.target_median + +# 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. diff --git a/dpd/src/MER.py b/dpd/src/MER.py index a4c3591..693058d 100644 --- a/dpd/src/MER.py +++ b/dpd/src/MER.py @@ -8,11 +8,6 @@ import datetime import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except: - logging_path = None - import numpy as np import matplotlib matplotlib.use('agg') @@ -76,9 +71,9 @@ class MER: spectrum = self._calc_spectrum(tx) - if self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_MER" + debug_name + ".svg" + fig_path = self.c.plot_location + "/" + dt + "_MER" + debug_name + ".png" else: fig_path = None diff --git a/dpd/src/Measure.py b/dpd/src/Measure.py index e4333d9..b7423c6 100644 --- a/dpd/src/Measure.py +++ b/dpd/src/Measure.py @@ -15,8 +15,9 @@ import logging class Measure: """Collect Measurement from DabMod""" - def __init__(self, samplerate, port, num_samples_to_request): + def __init__(self, config, samplerate, port, num_samples_to_request): logging.info("Instantiate Measure object") + self.c = config self.samplerate = samplerate self.sizeof_sample = 8 # complex floats self.port = port @@ -104,7 +105,7 @@ class Measure: rx_median = np.median(np.abs(rxframe)) rxframe = rxframe / rx_median * np.median(np.abs(txframe)) - du = DU.Dab_Util(self.samplerate) + du = DU.Dab_Util(self.c, self.samplerate) txframe_aligned, rxframe_aligned = du.subsample_align(txframe, rxframe) logging.info( diff --git a/dpd/src/Measure_Shoulders.py b/dpd/src/Measure_Shoulders.py index 2249ac6..fd90050 100644 --- a/dpd/src/Measure_Shoulders.py +++ b/dpd/src/Measure_Shoulders.py @@ -9,12 +9,6 @@ import datetime import os import logging import multiprocessing - -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib.pyplot as plt @@ -82,11 +76,11 @@ class Measure_Shoulders: self.plot = c.MS_plot def _plot(self, signal): - if logging_path is None: + if self.c.plot_location is None: return dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_sync_subsample_aligned.svg" + fig_path = self.c.plot_location + "/" + dt + "_sync_subsample_aligned.png" fft = calc_fft_db(signal, 100, self.c) peak, idxs_peak = _calc_peak(fft, self.c) diff --git a/dpd/src/Model_AM.py b/dpd/src/Model_AM.py index 596ca4a..9800d83 100644 --- a/dpd/src/Model_AM.py +++ b/dpd/src/Model_AM.py @@ -8,11 +8,6 @@ import datetime import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib.pyplot as plt @@ -57,12 +52,12 @@ class Model_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 and logging_path is not None: + if self.plot and self.c.plot_location is not None: 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) dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_Model_AM.svg" + fig_path = self.c.plot_location + "/" + dt + "_Model_AM.png" sub_rows = 1 sub_cols = 1 fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) diff --git a/dpd/src/Model_Lut.py b/dpd/src/Model_Lut.py index b349433..e70fdb0 100644 --- a/dpd/src/Model_Lut.py +++ b/dpd/src/Model_Lut.py @@ -7,14 +7,8 @@ import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np - class Lut: """Implements a model that calculates lookup table coefficients""" diff --git a/dpd/src/Model_PM.py b/dpd/src/Model_PM.py index e721d1a..3aafea0 100644 --- a/dpd/src/Model_PM.py +++ b/dpd/src/Model_PM.py @@ -8,11 +8,6 @@ import datetime import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib.pyplot as plt @@ -43,12 +38,12 @@ class Model_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 and logging_path is not None: + if self.plot and self.c.plot_location is not None: 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" + fig_path = self.c.plot_location + "/" + dt + "_Model_PM.png" sub_rows = 1 sub_cols = 1 fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6)) diff --git a/dpd/src/Model_Poly.py b/dpd/src/Model_Poly.py index 1cf8ecd..3ec717b 100644 --- a/dpd/src/Model_Poly.py +++ b/dpd/src/Model_Poly.py @@ -7,11 +7,6 @@ import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import src.Model_AM as Model_AM @@ -56,8 +51,6 @@ class Poly: self.model_am = Model_AM.Model_AM(c, plot=self.plot) self.model_pm = Model_PM.Model_PM(c, plot=self.plot) - self.plot = c.MDL_plot - def reset_coefs(self): self.coefs_am = np.zeros(5, dtype=np.float32) self.coefs_am[0] = 1 diff --git a/dpd/src/RX_Agc.py b/dpd/src/RX_Agc.py index 7b5e0b6..f778dee 100644 --- a/dpd/src/RX_Agc.py +++ b/dpd/src/RX_Agc.py @@ -9,11 +9,6 @@ import datetime import os import logging import time -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib matplotlib.use('agg') @@ -73,14 +68,14 @@ class Agc: def plot_estimates(self): """Plots the estimate of for Max, Median, Mean for different number of samples.""" - if logging_path is None: + if self.c.plot_location is None: return self.adapt.set_rxgain(self.min_rxgain) time.sleep(1) dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_agc.svg" + fig_path = self.c.plot_location + "/" + dt + "_agc.png" fig, axs = plt.subplots(2, 2, figsize=(3*6,1*6)) axs = axs.ravel() diff --git a/dpd/src/Symbol_align.py b/dpd/src/Symbol_align.py index f2802ee..2a17a65 100644 --- a/dpd/src/Symbol_align.py +++ b/dpd/src/Symbol_align.py @@ -8,12 +8,6 @@ import datetime import os import logging - -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except: - logging_path = None - import numpy as np import scipy import matplotlib @@ -75,9 +69,9 @@ class Symbol_align: offset = peaks[np.argmin([tx_product_avg[peak] for peak in peaks])] - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot and logging_path is not None: + if self.plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_Symbol_align.svg" + fig_path = self.c.plot_location + "/" + dt + "_Symbol_align.png" fig = plt.figure(figsize=(9, 9)) diff --git a/dpd/src/TX_Agc.py b/dpd/src/TX_Agc.py index 7555450..309193d 100644 --- a/dpd/src/TX_Agc.py +++ b/dpd/src/TX_Agc.py @@ -9,12 +9,6 @@ import datetime import os import logging import time - -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib diff --git a/dpd/src/phase_align.py b/dpd/src/phase_align.py index 21a210c..8654333 100644 --- a/dpd/src/phase_align.py +++ b/dpd/src/phase_align.py @@ -7,11 +7,6 @@ import datetime import os import logging -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np import matplotlib.pyplot as plt @@ -27,9 +22,9 @@ def phase_align(sig, ref_sig, plot=False): real_diffs = np.cos(angle_diff) imag_diffs = np.sin(angle_diff) - if logging.getLogger().getEffectiveLevel() == logging.DEBUG and plot and logging_path is not None: + if plot and self.c.plot_location is not None: dt = datetime.datetime.now().isoformat() - fig_path = logging_path + "/" + dt + "_phase_align.svg" + fig_path = self.c.plot_location + "/" + dt + "_phase_align.png" plt.subplot(511) plt.hist(angle_diff, bins=60, label="Angle Diff") diff --git a/dpd/src/subsample_align.py b/dpd/src/subsample_align.py index a5e9f8c..20ae56b 100755 --- a/dpd/src/subsample_align.py +++ b/dpd/src/subsample_align.py @@ -7,16 +7,10 @@ import datetime import logging import os -try: - logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except AttributeError: - logging_path = None - import numpy as np from scipy import optimize import matplotlib.pyplot as plt - def gen_omega(length): if (length % 2) == 1: raise ValueError("Needs an even length array.") @@ -34,7 +28,7 @@ def gen_omega(length): return omega -def subsample_align(sig, ref_sig, plot=False): +def subsample_align(sig, ref_sig, plot_location=None): """Do subsample alignment for sig relative to the reference signal ref_sig. The delay between the two must be less than sample @@ -74,13 +68,13 @@ def subsample_align(sig, ref_sig, plot=False): if optim_result.success: best_tau = optim_result.x - if plot and logging_path is not None: + if plot_location is not None: corr = np.vectorize(correlate_for_delay) ixs = np.linspace(-1, 1, 100) taus = corr(ixs) dt = datetime.datetime.now().isoformat() - tau_path = (logging_path + "/" + dt + "_tau.svg") + tau_path = (plot_location + "/" + dt + "_tau.png") plt.plot(ixs, taus) plt.title("Subsample correlation, minimum is best: {}".format(best_tau)) plt.savefig(tau_path) -- cgit v1.2.3 From 1a3ae6085967be4cd76cabb2a630a04464f38748 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 08:42:10 +0100 Subject: DPDCE: Add reset option --- dpd/main.py | 11 ++++++++++- dpd/src/GlobalConfig.py | 2 +- dpd/src/Model_Poly.py | 1 - 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dpd/main.py b/dpd/main.py index 24daa5a..3ca0d35 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -11,6 +11,7 @@ This engine calculates and updates the parameter of the digital predistortion module of ODR-DabMod.""" +import sys import datetime import os import argparse @@ -62,6 +63,8 @@ parser.add_argument('--plot', action="store_true") parser.add_argument('--name', default="", type=str, help='Name of the logging directory') +parser.add_argument('-r', '--reset', action="store_true", + help='Reset the DPD settings to the defaults.') cli_args = parser.parse_args() @@ -101,7 +104,7 @@ console.setFormatter(formatter) # add the handler to the root logger logging.getLogger('').addHandler(console) -logging.info(cli_args) +logging.info("DPDCE starting up with options: {}".format(cli_args)) import numpy as np import traceback @@ -129,6 +132,8 @@ if cli_args.lut: model = Lut(c) else: model = Poly(c) + +# Models have the default settings on startup adapt.set_predistorter(model.get_dpd_data()) adapt.set_digital_gain(digital_gain) @@ -170,6 +175,10 @@ elif dpddata[0] == "lut": else: logging.error("Unknown dpd data format {}".format(dpddata[0])) +if cli_args.reset: + logging.info("DPD Settings were reset to default values.") + sys.exit(0) + tx_agc = TX_Agc(adapt, c) # Automatic Gain Control diff --git a/dpd/src/GlobalConfig.py b/dpd/src/GlobalConfig.py index 684a881..b84b9d7 100644 --- a/dpd/src/GlobalConfig.py +++ b/dpd/src/GlobalConfig.py @@ -11,7 +11,7 @@ import numpy as np class GlobalConfig: def __init__(self, cli_args, plot_location): - self.sample_rate = cli_args.sample_rate + self.sample_rate = cli_args.samplerate assert self.sample_rate == 8192000 # By now only constants for 8192000 self.plot_location = plot_location diff --git a/dpd/src/Model_Poly.py b/dpd/src/Model_Poly.py index 3ec717b..cdfd319 100644 --- a/dpd/src/Model_Poly.py +++ b/dpd/src/Model_Poly.py @@ -55,7 +55,6 @@ class Poly: 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, lr=None): """ -- cgit v1.2.3 From a2df42ad09b27685d51d974ab86a306276f43a72 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 09:04:49 +0100 Subject: DPDCE: Add status option --- dpd/main.py | 83 ++++++++++++++++++++++++++++++-------------------------- dpd/src/Adapt.py | 13 +++++++++ 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/dpd/main.py b/dpd/main.py index 3ca0d35..1a2d696 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -65,6 +65,8 @@ parser.add_argument('--name', default="", type=str, help='Name of the logging directory') parser.add_argument('-r', '--reset', action="store_true", help='Reset the DPD settings to the defaults.') +parser.add_argument('-s', '--status', action="store_true", + help='Display the currently running DPD settings.') cli_args = parser.parse_args() @@ -84,25 +86,34 @@ plot = cli_args.plot # Logging import logging -dt = datetime.datetime.now().isoformat() -logging_path = '/tmp/dpd_{}'.format(dt).replace('.', '_').replace(':', '-') -if name: - logging_path += '_' + name -os.makedirs(logging_path) -logging.basicConfig(format='%(asctime)s - %(module)s - %(levelname)s - %(message)s', - datefmt='%Y-%m-%d %H:%M:%S', - filename='{}/dpd.log'.format(logging_path), - filemode='w', - level=logging.DEBUG) -# also log up to INFO to console -console = logging.StreamHandler() -console.setLevel(logging.INFO) -# set a format which is simpler for console use -formatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s') -# tell the handler to use this format -console.setFormatter(formatter) -# add the handler to the root logger -logging.getLogger('').addHandler(console) +# Simple usage scenarios don't need to clutter /tmp +if not (cli_args.status or cli_args.reset): + dt = datetime.datetime.now().isoformat() + logging_path = '/tmp/dpd_{}'.format(dt).replace('.', '_').replace(':', '-') + if name: + logging_path += '_' + name + print("Logs and plots written to {}".format(logging_path)) + os.makedirs(logging_path) + logging.basicConfig(format='%(asctime)s - %(module)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + filename='{}/dpd.log'.format(logging_path), + filemode='w', + level=logging.DEBUG) + # also log up to INFO to console + console = logging.StreamHandler() + console.setLevel(logging.INFO) + # set a format which is simpler for console use + formatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s') + # tell the handler to use this format + console.setFormatter(formatter) + # add the handler to the root logger + logging.getLogger('').addHandler(console) +else: + dt = datetime.datetime.now().isoformat() + logging.basicConfig(format='%(asctime)s - %(module)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + level=logging.DEBUG) + logging_path = None logging.info("DPDCE starting up with options: {}".format(cli_args)) @@ -128,6 +139,17 @@ meas = Measure(c, samplerate, port, num_req) extStat = ExtractStatistic(c) adapt = Adapt(c, port_rc, coef_path) +if cli_args.status: + status = { + "txgain": adapt.get_txgain(), + "rxgain": adapt.get_rxgain(), + "digital_gain": adapt.get_digital_gain(), + "predistorter": adapt.get_predistorter() + } + + logging.info("ODR-DabMod currently running with {}".format(status)) + sys.exit(0) + if cli_args.lut: model = Lut(c) else: @@ -154,26 +176,9 @@ rx_gain = adapt.get_rxgain() digital_gain = adapt.get_digital_gain() dpddata = adapt.get_predistorter() -if dpddata[0] == "poly": - coefs_am = dpddata[1] - coefs_pm = dpddata[2] - logging.info( - "TX gain {}, RX gain {}, dpd_coefs_am {}," - " dpd_coefs_pm {}, digital_gain {}".format( - tx_gain, rx_gain, coefs_am, coefs_pm, digital_gain - ) - ) -elif dpddata[0] == "lut": - scalefactor = dpddata[1] - lut = dpddata[2] - logging.info( - "TX gain {}, RX gain {}, LUT scalefactor {}," - " LUT {}, digital_gain {}".format( - tx_gain, rx_gain, scalefactor, lut, digital_gain - ) - ) -else: - logging.error("Unknown dpd data format {}".format(dpddata[0])) + +logging.info("TX gain {}, RX gain {}, digital_gain {}, {!s}".format( + tx_gain, rx_gain, digital_gain, Adapt.dpddata_to_str(dpddata))) if cli_args.reset: logging.info("DPD Settings were reset to default values.") diff --git a/dpd/src/Adapt.py b/dpd/src/Adapt.py index 153442b..53ef037 100644 --- a/dpd/src/Adapt.py +++ b/dpd/src/Adapt.py @@ -42,6 +42,19 @@ def _write_lut_file(scalefactor, lut, path): f.write("{}\n{}\n".format(coef.real, coef.imag)) f.close() +def dpddata_to_str(dpddata): + if dpddata[0] == "poly": + coefs_am = dpddata[1] + coefs_pm = dpddata[2] + return "dpd_coefs_am {}, dpd_coefs_pm {}".format( + self.coefs_am, self.coefs_pm) + elif dpddata[0] == "lut": + scalefactor = dpddata[1] + lut = dpddata[2] + return "LUT scalefactor {}, LUT {}".format( + scalefactor, lut) + else: + raise ValueError("Unknown dpddata type {}".format(dpddata[0])) class Adapt: """Uses the ZMQ remote control to change parameters of the DabMod -- cgit v1.2.3 From 9e553773d3e24a9b3e1044fa1ef6b09532992451 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 09:10:09 +0100 Subject: DPDCE improve status --- dpd/main.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/dpd/main.py b/dpd/main.py index 1a2d696..093f227 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -123,7 +123,7 @@ from src.Model import Lut, Poly import src.Heuristics as Heuristics from src.Measure import Measure from src.ExtractStatistic import ExtractStatistic -from src.Adapt import Adapt +from src.Adapt import Adapt, dpddata_to_str from src.RX_Agc import Agc from src.TX_Agc import TX_Agc from src.Symbol_align import Symbol_align @@ -140,14 +140,13 @@ extStat = ExtractStatistic(c) adapt = Adapt(c, port_rc, coef_path) if cli_args.status: - status = { - "txgain": adapt.get_txgain(), - "rxgain": adapt.get_rxgain(), - "digital_gain": adapt.get_digital_gain(), - "predistorter": adapt.get_predistorter() - } - - logging.info("ODR-DabMod currently running with {}".format(status)) + txgain = adapt.get_txgain() + rxgain = adapt.get_rxgain() + digital_gain = adapt.get_digital_gain() + dpddata = dpddata_to_str(adapt.get_predistorter()) + + logging.info("ODR-DabMod currently running with TXGain {}, RXGain {}, digital gain {} and {}".format( + rxgain, rxgain, digital_gain, dpddata)) sys.exit(0) if cli_args.lut: @@ -178,7 +177,7 @@ digital_gain = adapt.get_digital_gain() dpddata = adapt.get_predistorter() logging.info("TX gain {}, RX gain {}, digital_gain {}, {!s}".format( - tx_gain, rx_gain, digital_gain, Adapt.dpddata_to_str(dpddata))) + tx_gain, rx_gain, digital_gain, dpddata_to_str(dpddata))) if cli_args.reset: logging.info("DPD Settings were reset to default values.") -- cgit v1.2.3 From 0386346f9bad5214b7515b33a1c8fac353de4b6b Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 09:11:12 +0100 Subject: DPDCE fix dpddata_to_str() --- dpd/src/Adapt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpd/src/Adapt.py b/dpd/src/Adapt.py index 53ef037..329ee20 100644 --- a/dpd/src/Adapt.py +++ b/dpd/src/Adapt.py @@ -47,7 +47,7 @@ def dpddata_to_str(dpddata): coefs_am = dpddata[1] coefs_pm = dpddata[2] return "dpd_coefs_am {}, dpd_coefs_pm {}".format( - self.coefs_am, self.coefs_pm) + coefs_am, coefs_pm) elif dpddata[0] == "lut": scalefactor = dpddata[1] lut = dpddata[2] -- cgit v1.2.3 From 727e8c1e139cec691d07a45abab21836f46c3d33 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 09:24:06 +0100 Subject: Update DPD readme --- dpd/README.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/dpd/README.md b/dpd/README.md index 1b481c0..fc84bad 100644 --- a/dpd/README.md +++ b/dpd/README.md @@ -109,14 +109,31 @@ The DPDCE uses automatic gain control for both TX and RX gain to get both a high quantisation quality for the most frequent amplitude regions and a high enough back-off so the peaks are also quantised correctly. This means that the output power will stay at the same level, but the DPDCE may change TX gain to -trade it with digital gain and also change RX gain. - -As a first test you should run the DPDCE with the *--plot* parameter. It -preserves the output power and generates all available visualisation plots in -the newly created logging directory `/tmp/dpd_`. As the predistortion -should increase the peak to shoulder ratio, you should select a *txgain* in the -ODR-DabMod configuration file such that the initial peak-to-soulder ratio -visible on your spectrum analyser. This way, you will be able to see a the +trade it with digital gain and also change RX gain. This also implies that you +should *not modify txgain, rxgain, digital gain or coefficient settings manually!* +When the DPDCE is used, it controls these settings, and there are command line +options for you to define initial values. + +To verify that the communication between the DPDCE and ODR-DabMod is ok, +you can use the status and reset options: + +``` +cd dpd +python main.py --status +python main.py --reset +``` + +The reset option sets all DPD-related settings to the defaults (as shown in the +`--help` usage screen) and stops. + +When neither `--status` nor `--reset` is given, the DPDCE will run the predistortion +algorithm. As a first test you should run the DPDCE with the `--plot` +parameter. It preserves the output power and generates all available +visualisation plots in the newly created logging directory +`/tmp/dpd_`. As the predistortion should increase the peak to +shoulder ratio, you should select a *txgain* in the ODR-DabMod configuration +file such that the initial peak-to-soulder ratio visible on your spectrum +analyser. This way, you will be able to see a the change. ``` -- cgit v1.2.3 From 8e3338479c180418a05ab030c60ba01c2a8615ca Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 29 Dec 2017 09:25:31 +0100 Subject: Fix complexf file output format --- src/DabMod.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DabMod.cpp b/src/DabMod.cpp index b6f2dee..25a93bf 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -182,7 +182,7 @@ static shared_ptr prepare_output( if (s.fileOutputFormat == "complexf") { output = make_shared(s.outputName); } - if (s.fileOutputFormat == "complexf_normalised") { + else if (s.fileOutputFormat == "complexf_normalised") { if (s.gainMode == GainMode::GAIN_FIX) s.normalise = 1.0f / normalise_factor_file_fix; else if (s.gainMode == GainMode::GAIN_MAX) -- cgit v1.2.3