summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore14
-rwxr-xr-xdpd/main.py11
-rw-r--r--dpd/src/Measure_Shoulders.py131
-rw-r--r--dpd/src/const.py2
4 files changed, 155 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index d797fb1..e9eac8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -138,3 +138,17 @@ Session.vim
tags
# End of https://www.gitignore.io/api/vim,python
+*.ipynb
+
+.directory
+.idea/
+.ycm_extra_conf.py
+coef_83
+doc/.directory
+dpd.zip
+dpd/.idea/
+dpd/bin/
+dpd/run.sh
+dpd/test.csv
+dpd/test_data/
+dpd/dpdpoly.coef
diff --git a/dpd/main.py b/dpd/main.py
index b4355c3..ee75146 100755
--- a/dpd/main.py
+++ b/dpd/main.py
@@ -49,6 +49,7 @@ import src.TX_Agc as TX_Agc
import src.Symbol_align
import src.const
import src.MER
+import src.Measure_Shoulders
import argparse
parser = argparse.ArgumentParser(
@@ -65,7 +66,7 @@ parser.add_argument('--samplerate', default=8192000, type=int,
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=73,
+parser.add_argument('--txgain', default=70,
help='TX Gain',
required=False,
type=int)
@@ -99,9 +100,10 @@ num_req = cli_args.samps
samplerate = cli_args.samplerate
num_iter = cli_args.iterations
+c = src.const.const(samplerate)
SA = src.Symbol_align.Symbol_align(samplerate)
MER = src.MER.MER(samplerate)
-c = src.const.const(samplerate)
+MS = src.Measure_Shoulders.Measure_Shoulder(c, plot=True)
meas = Measure.Measure(samplerate, port, num_req)
extStat = ExtractStatistic.ExtractStatistic(c, plot=True)
@@ -195,9 +197,12 @@ while i < num_iter:
rx_gain = adapt.get_rxgain()
digital_gain = adapt.get_digital_gain()
tx_median = np.median(np.abs(txframe_aligned))
+ rx_shoulders = MS.average_shoulders(rxframe_aligned)
+ tx_shoulders = MS.average_shoulders(txframe_aligned)
logging.info(list((name, eval(name)) for name in
- ['i', 'tx_mer', 'rx_mer', 'mse', 'tx_gain',
+ ['i', 'tx_mer', 'tx_shoulders', 'rx_mer',
+ 'rx_shoulders', 'mse', 'tx_gain',
'digital_gain', 'rx_gain', 'rx_median',
'tx_median']))
if dpddata[0] == "poly":
diff --git a/dpd/src/Measure_Shoulders.py b/dpd/src/Measure_Shoulders.py
new file mode 100644
index 0000000..d2b4b5b
--- /dev/null
+++ b/dpd/src/Measure_Shoulders.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+#
+# DPD Calculation Engine, calculate peak to shoulder difference
+#
+# 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
+
+def plt_next_axis(sub_rows, sub_cols, i_sub):
+ i_sub += 1
+ ax = plt.subplot(sub_rows, sub_cols, i_sub)
+ return i_sub, ax
+
+def plt_annotate(ax, x,y,title=None,legend_loc=None):
+ ax.set_xlabel(x)
+ ax.set_ylabel(y)
+ if title is not None: ax.set_title(title)
+ if legend_loc is not None: ax.legend(loc=legend_loc)
+
+
+class Measure_Shoulder:
+ """Calculate difference between the DAB signal and the shoulder hight in the
+ power spectrum"""
+
+ def __init__(self,
+ c,
+ plot=False):
+ self.c = c
+ self.plot = plot
+
+ def calc_fft_db(self, signal, offset=0):
+ fft = np.fft.fftshift(np.fft.fft(signal[offset:offset + self.c.MS_FFT_size]))
+ fft_db = 20 * np.log10(np.abs(fft))
+ return fft_db
+
+ def _calc_peak(self, fft):
+ assert fft.shape == (self.c.MS_FFT_size,), fft.shape
+ idxs = (self.c.MS_peak_start, self.c.MS_peak_end)
+ peak = np.mean(fft[idxs[0]:idxs[1]])
+ return peak, idxs
+
+ def _calc_shoulder_hight(self, fft_db):
+ assert fft_db.shape == (self.c.MS_FFT_size,), fft_db.shape
+ idxs_left = (self.c.MS_shoulder_left_start, self.c.MS_shoulder_left_end)
+ idxs_right = (self.c.MS_shoulder_right_start, self.c.MS_shoulder_right_end)
+
+ shoulder_left = np.mean(fft_db[idxs_left[0]:idxs_left[1]])
+ shoulder_right = np.mean(fft_db[idxs_right[0]:idxs_right[1]])
+
+ shoulder = np.mean((shoulder_left, shoulder_right))
+ return shoulder, (idxs_left, idxs_right)
+
+ def calc_shoulder(self, fft):
+ peak = self._calc_peak(fft)[0]
+ shoulder = self._calc_shoulder_hight(fft)[0]
+ assert (peak >= shoulder), (peak, shoulder)
+ return peak - shoulder
+
+ def _plot(self, signal):
+ fft = self.calc_fft_db(signal, 100)
+ peak, idxs_peak = self._calc_peak(fft)
+ shoulder, idxs_sh = self._calc_shoulder_hight(fft)
+
+ sub_rows = 1
+ sub_cols = 1
+ fig = plt.figure(figsize=(sub_cols * 6, sub_rows / 2. * 6))
+ i_sub = 0
+
+ i_sub, ax = plt_next_axis(sub_rows, sub_cols, i_sub)
+ ax.scatter(np.arange(fft.shape[0]), fft, s=0.1,
+ label="FFT",
+ color="red")
+ ax.plot(idxs_peak, (peak, peak))
+ ax.plot(idxs_sh[0], (shoulder, shoulder), color='blue')
+ ax.plot(idxs_sh[1], (shoulder, shoulder), color='blue')
+ plt_annotate(ax, "Frequency", "Magnitude [dB]", None, 4)
+
+ ax.text(100, -17, str(self.calc_shoulder(fft)))
+
+ ax.set_ylim(-20, 30)
+ fig.tight_layout()
+
+ def average_shoulders(self, signal, n_avg=None):
+ assert signal.shape[0] > 4 * self.c.MS_FFT_size
+ if n_avg is None: n_avg = self.c.MS_averaging_size
+
+ off_min = 0
+ off_max = signal.shape[0] - self.c.MS_FFT_size
+ offsets = np.linspace(off_min, off_max, num=n_avg, dtype=int)
+
+ shoulders = []
+ for offset in offsets:
+ fft_db = self.calc_fft_db(signal, offset)
+ shoulders.append(self.calc_shoulder(fft_db))
+ shoulder = np.mean(shoulders)
+
+ if logging.getLogger().getEffectiveLevel() == logging.DEBUG and self.plot:
+ self._plot(signal)
+
+ return shoulder
+
+
+# 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/const.py b/dpd/src/const.py
index daaac5d..275557e 100644
--- a/dpd/src/const.py
+++ b/dpd/src/const.py
@@ -59,3 +59,5 @@ class const:
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