diff options
| author | andreas128 <Andreas> | 2017-09-29 18:50:41 +0200 | 
|---|---|---|
| committer | andreas128 <Andreas> | 2017-09-29 18:50:41 +0200 | 
| commit | 5e3ca125bfc56d31b9c9ef819eab9171b73b7333 (patch) | |
| tree | 297d400f754742faf6bfc4fc7eff9858163c845c | |
| parent | 64e0193824262e93e7cd67bb8c3af68d278f990c (diff) | |
| download | dabmod-5e3ca125bfc56d31b9c9ef819eab9171b73b7333.tar.gz dabmod-5e3ca125bfc56d31b9c9ef819eab9171b73b7333.tar.bz2 dabmod-5e3ca125bfc56d31b9c9ef819eab9171b73b7333.zip | |
Cleanup
| -rwxr-xr-x | dpd/apply_adapt_dumps.py | 4 | ||||
| -rwxr-xr-x | dpd/main.py | 6 | ||||
| -rw-r--r-- | dpd/src/Const.py | 2 | ||||
| -rw-r--r-- | dpd/src/Heuristics.py | 13 | ||||
| -rw-r--r-- | dpd/src/MER.py | 7 | ||||
| -rw-r--r-- | dpd/src/Measure.py | 21 | ||||
| -rw-r--r-- | dpd/src/Measure_Shoulders.py | 37 | ||||
| -rw-r--r-- | dpd/src/Model_AM.py | 2 | ||||
| -rw-r--r-- | dpd/src/Model_PM.py | 21 | ||||
| -rw-r--r-- | dpd/src/Symbol_align.py | 67 | ||||
| -rw-r--r-- | dpd/src/TX_Agc.py | 2 | ||||
| -rw-r--r-- | dpd/src/Test_data.py | 136 | ||||
| -rwxr-xr-x | dpd/src/subsample_align.py | 19 | ||||
| -rw-r--r-- | dpd/src/test_dab_Util.py | 92 | ||||
| -rw-r--r-- | dpd/src/test_measure.py | 62 | 
15 files changed, 112 insertions, 379 deletions
| diff --git a/dpd/apply_adapt_dumps.py b/dpd/apply_adapt_dumps.py index 4ecdc08..ee75e25 100755 --- a/dpd/apply_adapt_dumps.py +++ b/dpd/apply_adapt_dumps.py @@ -128,7 +128,7 @@ tx_gain = adapt.get_txgain()  rx_gain = adapt.get_rxgain()  digital_gain = adapt.get_digital_gain() -dpddata = adapt.get_predistorter +dpddata = adapt.get_predistorter()  if dpddata[0] == "poly":      coefs_am = dpddata[1]      coefs_pm = dpddata[2] @@ -162,7 +162,7 @@ print(paths)  for i, path in enumerate(paths):      print(i, path)      adapt.load(path) -    dpddata_after = adapt.get_predistorter +    dpddata_after = adapt.get_predistorter()      coefs_am, coefs_pm = model.reset_coefs()      adapt.set_predistorter(("poly", coefs_am, coefs_pm)) diff --git a/dpd/main.py b/dpd/main.py index 4e9647a..b2f930a 100755 --- a/dpd/main.py +++ b/dpd/main.py @@ -176,8 +176,10 @@ tx_agc = TX_Agc(adapt, c)  agc = Agc(meas, adapt, c)  agc.run() -state = "measure" +state = 'report'  i = 0 +lr = None +n_meas = None  while i < num_iter:      try:          # Measure @@ -218,6 +220,8 @@ while i < num_iter:          # Report          elif state == 'report':              try: +                txframe_aligned, tx_ts, rxframe_aligned, rx_ts, rx_median = meas.get_samples() +                  # Store all settings for pre-distortion, tx and rx                  adapt.dump() diff --git a/dpd/src/Const.py b/dpd/src/Const.py index 390dfbb..ac48b59 100644 --- a/dpd/src/Const.py +++ b/dpd/src/Const.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # -# DPD Calculation Engine, constants +# DPD Calculation Engine, constants.  #  # Source for DAB standard: etsi_EN_300_401_v010401p p145  # diff --git a/dpd/src/Heuristics.py b/dpd/src/Heuristics.py index a32ccff..f98490d 100644 --- a/dpd/src/Heuristics.py +++ b/dpd/src/Heuristics.py @@ -1,26 +1,33 @@  # -*- coding: utf-8 -*-  # -# DPD Calculation Engine, heuristics we use to tune the parameters +# DPD Calculation Engine, heuristics we use to tune the parameters.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file  import numpy as np +  def get_learning_rate(idx_run): +    """Gradually reduce learning rate from lr_max to lr_min within +     idx_max steps, then keep the learning rate at lr_min"""      idx_max = 10.0      lr_min = 0.05      lr_max = 0.4      lr_delta = lr_max - lr_min      idx_run = min(idx_run, idx_max) -    learning_rate = lr_max - lr_delta * idx_run/idx_max +    learning_rate = lr_max - lr_delta * idx_run / idx_max      return learning_rate +  def get_n_meas(idx_run): +    """Gradually increase number of measurements used to extract +    a statistic from n_meas_min to n_meas_max within idx_max steps, +    then keep number of measurements at n_meas_max"""      idx_max = 10.0      n_meas_min = 10      n_meas_max = 20      n_meas_delta = n_meas_max - n_meas_min      idx_run = min(idx_run, idx_max) -    learning_rate = n_meas_delta * idx_run/idx_max + n_meas_min +    learning_rate = n_meas_delta * idx_run / idx_max + n_meas_min      return int(np.round(learning_rate)) diff --git a/dpd/src/MER.py b/dpd/src/MER.py index 69c94f9..0f169a7 100644 --- a/dpd/src/MER.py +++ b/dpd/src/MER.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # -# Modulation Error Rate +# DPD Calculation Engine, Modulation Error Rate.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file @@ -13,7 +13,6 @@ try:  except:      logging_path = "/tmp/" -import src.Const  import numpy as np  import matplotlib  matplotlib.use('agg') @@ -72,8 +71,8 @@ class MER:          return x_mean, y_mean, U_RMS, U_ERR, MER      def calc_mer(self, tx, debug_name=""): -        assert tx.shape[0] == self.c.T_U,\ -                "Wrong input length" +        """Calculate MER for input signal from a symbol aligned signal.""" +        assert tx.shape[0] == self.c.T_U, "Wrong input length"          spectrum = self._calc_spectrum(tx) diff --git a/dpd/src/Measure.py b/dpd/src/Measure.py index 7a9246c..d485a86 100644 --- a/dpd/src/Measure.py +++ b/dpd/src/Measure.py @@ -6,14 +6,11 @@  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file -import sys  import socket  import struct  import numpy as np -import logging  import src.Dab_Util as DU  import os -import datetime  import logging  logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) @@ -78,15 +75,15 @@ class Measure:              rxframe = np.array([], dtype=np.complex64)          if logging.getLogger().getEffectiveLevel() == logging.DEBUG: -            logging.debug("txframe: min %f, max %f, median %f" % -                          (np.min(np.abs(txframe)), -                           np.max(np.abs(txframe)), -                           np.median(np.abs(txframe)))) - -            logging.debug("rxframe: min %f, max %f, median %f" % -                          (np.min(np.abs(rxframe)), -                           np.max(np.abs(rxframe)), -                           np.median(np.abs(rxframe)))) +            logging.debug('txframe: min {}, max {}, median {}'.format( +                          np.min(np.abs(txframe)), +                          np.max(np.abs(txframe)), +                          np.median(np.abs(txframe)))) + +            logging.debug('rxframe: min {}, max {}, median {}'.format( +                          np.min(np.abs(rxframe)), +                          np.max(np.abs(rxframe)), +                          np.median(np.abs(rxframe))))          logging.debug("Disconnecting")          s.close() diff --git a/dpd/src/Measure_Shoulders.py b/dpd/src/Measure_Shoulders.py index 7d48a2b..4cf7d0d 100644 --- a/dpd/src/Measure_Shoulders.py +++ b/dpd/src/Measure_Shoulders.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # -# DPD Calculation Engine, calculate peak to shoulder difference +# DPD Calculation Engine, calculate peak to shoulder difference.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file @@ -15,28 +15,35 @@ logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFil  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): + +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) +    if title is not None: +        ax.set_title(title) +    if legend_loc is not None: +        ax.legend(loc=legend_loc) +  def calc_fft_db(signal, offset, c):      fft = np.fft.fftshift(np.fft.fft(signal[offset:offset + c.MS_FFT_size]))      fft_db = 20 * np.log10(np.abs(fft))      return fft_db +  def _calc_peak(fft, c):      assert fft.shape == (c.MS_FFT_size,), fft.shape      idxs = (c.MS_peak_start, c.MS_peak_end)      peak = np.mean(fft[idxs[0]:idxs[1]])      return peak, idxs +  def _calc_shoulder_hight(fft_db, c):      assert fft_db.shape == (c.MS_FFT_size,), fft_db.shape      idxs_left = (c.MS_shoulder_left_start, c.MS_shoulder_left_end) @@ -48,26 +55,26 @@ def _calc_shoulder_hight(fft_db, c):      shoulder = np.mean((shoulder_left, shoulder_right))      return shoulder, (idxs_left, idxs_right) +  def calc_shoulder(fft, c):      peak = _calc_peak(fft, c)[0]      shoulder = _calc_shoulder_hight(fft, c)[0]      assert (peak >= shoulder), (peak, shoulder)      return peak, shoulder +  def shoulder_from_sig_offset(arg):      signal, offset, c = arg      fft_db = calc_fft_db(signal, offset, c)      peak, shoulder = calc_shoulder(fft_db, c) -    return peak-shoulder, peak, shoulder +    return peak - shoulder, peak, shoulder  class Measure_Shoulders:      """Calculate difference between the DAB signal and the shoulder hight in the      power spectrum""" -    def __init__(self, -                 c, -                 plot=False): +    def __init__(self, c):          self.c = c          self.plot = c.MS_plot @@ -75,9 +82,9 @@ class Measure_Shoulders:          dt = datetime.datetime.now().isoformat()          fig_path = logging_path + "/" + dt + "_sync_subsample_aligned.svg" -        fft = calc_fft_db(signal, 100, 10) -        peak, idxs_peak = self._calc_peak(fft) -        shoulder, idxs_sh = self._calc_shoulder_hight(fft, self.c) +        fft = calc_fft_db(signal, 100, self.c) +        peak, idxs_peak = _calc_peak(fft, self.c) +        shoulder, idxs_sh = _calc_shoulder_hight(fft, self.c)          sub_rows = 1          sub_cols = 1 @@ -93,7 +100,7 @@ class Measure_Shoulders:          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, self.c))) +        ax.text(100, -17, str(calc_shoulder(fft, self.c)))          ax.set_ylim(-20, 30)          fig.tight_layout() @@ -106,14 +113,13 @@ class Measure_Shoulders:              return None          assert signal.shape[0] > 4 * self.c.MS_FFT_size -        if n_avg is None: n_avg = self.c.MS_averaging_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 = [] -          args = zip(              [signal, ] * offsets.shape[0],              offsets, @@ -129,7 +135,6 @@ class Measure_Shoulders:          return np.mean(shoulders_diff), np.mean(shoulders), np.mean(peaks) -  # The MIT License (MIT)  #  # Copyright (c) 2017 Andreas Steger diff --git a/dpd/src/Model_AM.py b/dpd/src/Model_AM.py index cdc3de1..a40f421 100644 --- a/dpd/src/Model_AM.py +++ b/dpd/src/Model_AM.py @@ -90,6 +90,8 @@ class Model_AM:              plt.close(fig)      def get_next_coefs(self, tx_dpd, rx_received, coefs_am): +        """Calculate the next AM/AM coefficients using the extracted +        statistic of TX and RX amplitude"""          check_input_get_next_coefs(tx_dpd, rx_received)          coefs_am_new = fit_poly(tx_dpd, rx_received) diff --git a/dpd/src/Model_PM.py b/dpd/src/Model_PM.py index e0fcb55..b0c7ae2 100644 --- a/dpd/src/Model_PM.py +++ b/dpd/src/Model_PM.py @@ -15,17 +15,16 @@ import numpy as np  import matplotlib.pyplot as plt +def is_npfloat32(array): +    assert isinstance(array, np.ndarray), type(array) +    assert array.dtype == np.float32, array.dtype +    assert array.flags.contiguous +    assert not any(np.isnan(array)) + +  def check_input_get_next_coefs(tx_dpd, phase_diff): -    is_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) +    is_npfloat32(tx_dpd) +    is_npfloat32(phase_diff)  class Model_PM: @@ -93,6 +92,8 @@ class Model_PM:          return tx_range, phase_diff      def get_next_coefs(self, tx_dpd, phase_diff, coefs_pm): +        """Calculate the next AM/PM coefficients using the extracted +        statistic of TX amplitude and phase difference"""          tx_dpd, phase_diff = self._discard_small_values(tx_dpd, phase_diff)          check_input_get_next_coefs(tx_dpd, phase_diff) diff --git a/dpd/src/Symbol_align.py b/dpd/src/Symbol_align.py index e21e793..4ea79c5 100644 --- a/dpd/src/Symbol_align.py +++ b/dpd/src/Symbol_align.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # -# Modulation Error Rate +# DPD Calculation Engine, Modulation Error Rate.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file @@ -8,24 +8,50 @@  import datetime  import os  import logging -import time +  try:      logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename)  except:      logging_path = "/tmp/"  import numpy as np -import src.Const  import scipy  import matplotlib +  matplotlib.use('agg')  import matplotlib.pyplot as plt + +def _remove_outliers(x, stds=5): +    deviation_from_mean = np.abs(x - np.mean(x)) +    inlier_idxs = deviation_from_mean < stds * np.std(x) +    x = x[inlier_idxs] +    return x + + +def _calc_delta_angle(fft): +    # Introduce invariance against carrier +    angles = np.angle(fft) % (np.pi / 2.) + +    # Calculate Angle difference and compensate jumps +    deltas_angle = np.diff(angles) +    deltas_angle[deltas_angle > np.pi / 4.] = \ +        deltas_angle[deltas_angle > np.pi / 4.] - np.pi / 2. +    deltas_angle[-deltas_angle > np.pi / 4.] = \ +        deltas_angle[-deltas_angle > np.pi / 4.] + np.pi / 2. +    deltas_angle = _remove_outliers(deltas_angle) + +    delta_angle = np.mean(deltas_angle) + +    return delta_angle + +  class Symbol_align:      """      Find the phase offset to the start of the DAB symbols in an      unaligned dab signal.      """ +      def __init__(self, c, plot=False):          self.c = c          self.plot = plot @@ -95,8 +121,8 @@ class Symbol_align:              ax.set_title("Min Filter")              ax = fig.add_subplot(4, 1, 4) -            tx_product_crop = tx_product[peaks[0]-50:peaks[0]+50] -            x = range(tx_product.shape[0])[peaks[0]-50:peaks[0]+50] +            tx_product_crop = tx_product[peaks[0] - 50:peaks[0] + 50] +            x = range(tx_product.shape[0])[peaks[0] - 50:peaks[0] + 50]              ax.plot(x, tx_product_crop)              ylim = ax.get_ylim()              ax.plot((peaks[0], peaks[0]), (ylim[0], ylim[1])) @@ -113,38 +139,16 @@ class Symbol_align:          # of the shift to find the offset of the symbol start.          return (offset + self.c.T_C) % self.c.T_S -    def _remove_outliers(self, x, stds=5): -        deviation_from_mean = np.abs(x - np.mean(x)) -        inlier_idxs = deviation_from_mean < stds * np.std(x) -        x = x[inlier_idxs] -        return x - -    def _calc_delta_angle(self, fft): -        # Introduce invariance against carrier -        angles = np.angle(fft) % (np.pi / 2.) - -        # Calculate Angle difference and compensate jumps -        deltas_angle = np.diff(angles) -        deltas_angle[deltas_angle > np.pi/4.] =\ -            deltas_angle[deltas_angle > np.pi/4.] - np.pi/2. -        deltas_angle[-deltas_angle > np.pi/4.] = \ -            deltas_angle[-deltas_angle > np.pi/4.] + np.pi/2. -        deltas_angle = self._remove_outliers(deltas_angle) - -        delta_angle = np.mean(deltas_angle) - -        return delta_angle -      def _delta_angle_to_samples(self, angle):          return - angle / self.c.phase_offset_per_sample -    def _calc_sample_offset(self, sig, debug=False): -        assert sig.shape[0] == self.c.T_U,\ +    def _calc_sample_offset(self, sig): +        assert sig.shape[0] == self.c.T_U, \              "Input length is not a Symbol without cyclic prefix"          fft = np.fft.fftshift(np.fft.fft(sig))          fft_crop = np.delete(fft[self.c.FFT_start:self.c.FFT_end], self.c.FFT_delete) -        delta_angle = self._calc_delta_angle(fft_crop) +        delta_angle = _calc_delta_angle(fft_crop)          delta_sample = self._delta_angle_to_samples(delta_angle)          delta_sample_int = np.round(delta_sample).astype(int)          error = np.abs(delta_sample_int - delta_sample) @@ -154,6 +158,7 @@ class Symbol_align:          return delta_sample_int      def calc_offset(self, tx): +        """Calculate the offset the first symbol"""          off_sym = self._calc_offset_to_first_symbol_without_prefix(              tx)          off_sam = self._calc_sample_offset( @@ -168,7 +173,7 @@ class Symbol_align:          off = self.calc_offset(tx)          return tx[                 off: -               off+self.c.T_U +               off + self.c.T_U                 ]  # The MIT License (MIT) diff --git a/dpd/src/TX_Agc.py b/dpd/src/TX_Agc.py index 2602ea6..857ae06 100644 --- a/dpd/src/TX_Agc.py +++ b/dpd/src/TX_Agc.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # -# Automatic Gain Control +# DPD Calculation Engine,  Automatic Gain Control.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file diff --git a/dpd/src/Test_data.py b/dpd/src/Test_data.py deleted file mode 100644 index bbef282..0000000 --- a/dpd/src/Test_data.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Modulation Error Rate -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -import datetime -import os -import logging -import time -try: -    logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename) -except: -    logging_path = "/tmp/" - -import src.Const -import src.Dab_Util -import numpy as np -import matplotlib -matplotlib.use('agg') -import matplotlib.pyplot as plt - - -class Test_data: -    def __init__(self, sample_rate, type): -        """ -        Standardized access to complex64 test data files. -         -        :param sample_rate:  -        :param type:  -         -        Testing: -        TD = src.Test_data.Test_data(8192000, 'file') -        tx_orig = TD.get_symbol(0,0) -        fig = plt.figure(figsize=(9,6)) -        ax = fig.add_subplot(2,1,1) -        ax.plot(tx_orig) -        ax = fig.add_subplot(2,1,2) -        plt.plot(np.angle(np.fft.fftshift(np.fft.fft(tx_orig))), 'p') -        """ - -        self.c = src.Const.Const(sample_rate,, False -        self.du = src.Dab_Util.Dab_Util(sample_rate) - -        self.file_paths = { -            (2048000, 'file'): -                ("./test_data/odr-dabmod_to_file_2048_NoFir_noDPD.iq", -                 ( -                    self.c.T_F +    # Pipelineing -                    self.c.T_NULL + # NULL Symbol -                    self.c.T_S +    # Synchronization Symbol -                    self.c.T_C      # Cyclic Prefix -                 )), -            (8192000, 'file'): -                ("./test_data/odr-dabmod_to_file_8192_NoFir_noDPD.iq", -                 ( -                     self.c.T_F +    # Pipelining -                     self.c.T_U +    # Pipelining Resampler TODO(?) -                     self.c.T_NULL + # NULL Symbol -                     self.c.T_S +    # Synchronization Symbol -                     self.c.T_C      # Cyclic Prefix -                 )), -            (8192000, 'rec_noFir'): -                ("./test_data/odr-dabmod_reconding_8192_NoFir_DPD_2104.iq", -                 ( 64 )), -            (8192000, 'rec_fir'): -                ("./test_data/odr-dabmod_reconding_8192_Fir_DPD_2104.iq", -                 ( 232 )), -        } - -        config = (sample_rate, type) -        if not config in self.file_paths.keys(): -            raise RuntimeError("Configuration not found, possible are:\n {}". -                               format(self.file_paths)) - -        self.path, self.file_offset = self.file_paths[(sample_rate, type)] - -    def _load_from_file(self, offset, length): -        print(offset, length, self.file_offset) -        return self.du.fromfile( -            self.path, -            length=length, -            offset=offset + self.file_offset) - -    def get_symbol_without_prefix(self, -                                  frame_idx=0, -                                  symbol_idx=0, -                                  off=0): -        return self._load_from_file( -            frame_idx*self.c.T_F + -            symbol_idx*self.c.T_S + -            off, -            self.c.T_U) - -    def get_symbol_with_prefix(self, -                               frame_idx=0, -                               symbol_idx=0, -                               n_symbols=1, -                               off=0): -        offset = ( -            frame_idx*self.c.T_F + -            symbol_idx*self.c.T_S - -            self.c.T_C + -            off) -        return self._load_from_file( offset, self.c.T_S * n_symbols) - -    def get_file_length_in_symbols(self): -        symbol_size = float( -                        64/8 * # complex 64 -                        self.c.T_S # Symbol Size -                    ) -        return os.path.getsize(self.path) / symbol_size - - -# 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/subsample_align.py b/dpd/src/subsample_align.py index b0cbe88..0c98e50 100755 --- a/dpd/src/subsample_align.py +++ b/dpd/src/subsample_align.py @@ -1,23 +1,25 @@  # -*- coding: utf-8 -*-  # -# DPD Calculation Engine, utility to do subsample alignment +# DPD Calculation Engine, utility to do subsample alignment.  #  # http://www.opendigitalradio.org  # Licence: The MIT License, see notice at the end of this file  import datetime -import os  import logging +import os +  logging_path = os.path.dirname(logging.getLoggerClass().root.handlers[0].baseFilename)  import numpy as np -from scipy import signal, optimize +from scipy import optimize  import matplotlib.pyplot as plt +  def gen_omega(length):      if (length % 2) == 1:          raise ValueError("Needs an even length array.") -    halflength = int(length/2) +    halflength = int(length / 2)      factor = 2.0 * np.pi / length      omega = np.zeros(length, dtype=np.float) @@ -29,6 +31,7 @@ def gen_omega(length):      return omega +  def subsample_align(sig, ref_sig, plot=False):      """Do subsample alignment for sig relative to the reference signal      ref_sig. The delay between the two must be less than sample @@ -38,7 +41,7 @@ def subsample_align(sig, ref_sig, plot=False):      n = len(sig)      if (n % 2) == 1:          raise ValueError("Needs an even length signal.") -    halflen = int(n/2) +    halflen = int(n / 2)      fft_sig = np.fft.fft(sig) @@ -63,7 +66,8 @@ def subsample_align(sig, ref_sig, plot=False):          return -np.abs(np.sum(np.conj(corr_sig) * ref_sig)) -    optim_result = optimize.minimize_scalar(correlate_for_delay, bounds=(-1,1), method='bounded', options={'disp': True}) +    optim_result = optimize.minimize_scalar(correlate_for_delay, bounds=(-1, 1), method='bounded', +                                            options={'disp': True})      if optim_result.success:          best_tau = optim_result.x @@ -85,10 +89,9 @@ def subsample_align(sig, ref_sig, plot=False):          rotate_vec[halflen] = np.cos(np.pi * best_tau)          return np.fft.ifft(rotate_vec * fft_sig).astype(np.complex64)      else: -        #print("Could not optimize: " + optim_result.message) +        # print("Could not optimize: " + optim_result.message)          return np.zeros(0, dtype=np.complex64) -  # The MIT License (MIT)  #  # Copyright (c) 2017 Andreas Steger diff --git a/dpd/src/test_dab_Util.py b/dpd/src/test_dab_Util.py deleted file mode 100644 index 86ee2d8..0000000 --- a/dpd/src/test_dab_Util.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Test code for DAB util -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file - -from unittest import TestCase - -import numpy as np -import pandas as pd -import src.Dab_Util as DU - -class TestDab_Util(TestCase): - -    def test_subsample_align(self, sample_orig=r'../test_data/orig_rough_aligned.dat', -                             sample_rec =r'../test_data/recored_rough_aligned.dat', -                             length = 10240, max_size = 1000000): -        du = DU -        res1 = [] -        res2 = [] -        for i in range(10): -            start = np.random.randint(50, max_size) -            r = np.random.randint(-50, 50) - -            s1 = du.fromfile(sample_orig, offset=start+r, length=length) -            s2 = du.fromfile(sample_rec, offset=start, length=length) - -            res1.append(du.lag_upsampling(s2, s1, 32)) - -            s1_aligned, s2_aligned = du.subsample_align(s1, s2) - -            res2.append(du.lag_upsampling(s2_aligned, s1_aligned, 32)) - -        error_rate = np.mean(np.array(res2) != 0) -        self.assertEqual(error_rate, 0.0, "The error rate for aligning was %.2f%%" -                         % error_rate * 100) - -#def test_using_aligned_pair(sample_orig=r'../data/orig_rough_aligned.dat', sample_rec =r'../data/recored_rough_aligned.dat', length = 10240, max_size = 1000000): -#    res = [] -#    for i in tqdm(range(100)): -#        start = np.random.randint(50, max_size) -#        r = np.random.randint(-50, 50) -# -#        s1 = du.fromfile(sample_orig, offset=start+r, length=length) -#        s2 = du.fromfile(sample_rec, offset=start, length=length) -# -#        res.append({'offset':r, -#                    '1':r - du.lag_upsampling(s2, s1, n_up=1), -#                    '2':r - du.lag_upsampling(s2, s1, n_up=2), -#                    '3':r - du.lag_upsampling(s2, s1, n_up=3), -#                    '4':r - du.lag_upsampling(s2, s1, n_up=4), -#                    '8':r - du.lag_upsampling(s2, s1, n_up=8), -#                    '16':r - du.lag_upsampling(s2, s1, n_up=16), -#                    '32':r - du.lag_upsampling(s2, s1, n_up=32), -#                    }) -#    df = pd.DataFrame(res) -#    df = df.reindex_axis(sorted(df.columns), axis=1) -#    print(df.describe()) -# -# -#print("Align using upsampling") -#for n_up in [1, 2, 3, 4, 7, 8, 16]: -#   correct_ratio = test_phase_offset(lambda x,y: du.lag_upsampling(x,y,n_up), tol=1./n_up) -#   print("%.1f%% of the tested offsets were measured within tolerance %.4f for n_up = %d" % (correct_ratio * 100, 1./n_up, n_up)) -#test_using_aligned_pair() -# -#print("Phase alignment") -#test_subsample_alignment() - - -# 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/test_measure.py b/dpd/src/test_measure.py deleted file mode 100644 index ee3cfdb..0000000 --- a/dpd/src/test_measure.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DPD Calculation Engine, test case for measure -# -# http://www.opendigitalradio.org -# Licence: The MIT License, see notice at the end of this file -from unittest import TestCase -from Measure import Measure -import socket - - -class TestMeasure(TestCase): - -    def _open_socks(self): -        sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -        sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -        sock_server.bind(('localhost', 1234)) -        sock_server.listen(1) - -        sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -        sock_client.connect(('localhost', 1234)) - -        conn_server, addr_server = sock_server.accept() -        return conn_server, sock_client - -    def test__recv_exact(self): -        m = Measure(1234, 1) -        payload = b"test payload" - -        conn_server, sock_client = self._open_socks() -        conn_server.send(payload) -        rec = m._recv_exact(sock_client, len(payload)) - -        self.assertEqual(rec, payload, -                "Did not receive the same message as sended. (%s, %s)" % -                (rec, payload)) - -    def test_get_samples(self): -        self.fail() - - -# 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. | 
