diff options
| -rw-r--r-- | AlignSample.cpp | 124 | ||||
| -rw-r--r-- | AlignSample.hpp | 17 | ||||
| -rwxr-xr-x | align/GenerateExampleTxRxIQ.py | 47 | ||||
| -rw-r--r-- | main.cpp | 7 | 
4 files changed, 151 insertions, 44 deletions
diff --git a/AlignSample.cpp b/AlignSample.cpp index 4f5a8eb..dd29f64 100644 --- a/AlignSample.cpp +++ b/AlignSample.cpp @@ -1,5 +1,5 @@  /* -   Copyright (C) 2015 +   Copyright (C) 2017     Matthias P. Braendli, matthias.braendli@mpb.li      http://opendigitalradio.org @@ -23,6 +23,10 @@  #include "AlignSample.hpp"  #include <cstring> +#include <exception> +#include <algorithm> +#include <numeric> +#include <boost/math/tools/minima.hpp>  namespace fftw {      class plan { @@ -119,12 +123,12 @@ CorrelationResult AlignSample::crosscorrelate(size_t len)       */      const size_t N = 2 * len; -    std::vector<complexf> rx_fft_in(N); -    std::vector<complexf> rx_fft_out(N); -    std::vector<complexf> tx_fft_in(N); -    std::vector<complexf> tx_fft_out(N); -    std::vector<complexf> ifft_in(N); -    std::vector<complexf> ifft_out(N); +    vec_cf rx_fft_in(N); +    vec_cf rx_fft_out(N); +    vec_cf tx_fft_in(N); +    vec_cf tx_fft_out(N); +    vec_cf ifft_in(N); +    vec_cf ifft_out(N);      fftw::plan rx_fft_plan(N,              rx_fft_in, @@ -198,21 +202,113 @@ CorrelationResult AlignSample::crosscorrelate(size_t len)      return result;  } -std::pair<std::vector<complexf>, std::vector<complexf> > AlignSample::get_samples( -                size_t len, size_t rx_delay) +void AlignSample::delay_rx_samples(size_t delay)  { -    std::pair<std::vector<complexf>, std::vector<complexf> > rval; +    std::lock_guard<std::mutex> lock(m_mutex); +    if (align() and delay > 0 and m_rxsamples.size() > delay) { +        m_rxsamples.erase( +                m_rxsamples.begin(), +                m_rxsamples.begin() + delay - 1); +    } +} + +static std::vector<double> gen_omega(size_t length) +{ +    if (not length % 2 == 0) { +        throw std::runtime_error("Needs an even length array."); +    } +    size_t halflength = length/2; + +    double factor = 2.0 * M_PI / length; + +    std::vector<double> omega(length); +    for (size_t i = 0; i < halflength; i++) { +        omega[i] = factor * i; +    } +    for (size_t i = halflength+1; i < length; i++) { +        omega[i] = factor * ((ssize_t)i - length); +    } + +    return omega; +} + +/* Find subsample delay in signal versus the reference signal ref and + * correct it + */ +static vec_cf align_subsample(vec_cf& signal, vec_cf& ref) +{ +    size_t N = signal.size(); +    vec_cf fft_sig(N); +    vec_cf rotate_vec(N); +    vec_cf corr_sig(N); + +    fftw::plan plan(N, signal, fft_sig, FFTW_FORWARD); +    fftw::plan plan_ifft(N, rotate_vec, corr_sig, FFTW_BACKWARD); +    plan.execute(); + +    const auto omega = gen_omega(N); +    auto correlate_point = [&](float tau) { +        for (size_t i = 0; i < N; i++) { +            const complexf angle(0, tau * i); +            rotate_vec[i] = std::exp(angle); +        } +        rotate_vec[N/2] = cos(M_PI * tau); + +        for (size_t i = 0; i < N; i++) { +            rotate_vec[i] *= fft_sig[i]; +        } + +        plan_ifft.execute(); + +        std::vector<float> corr_real(N); +        std::transform(corr_sig.begin(), corr_sig.end(), corr_real.begin(), +                [&](complexf f){ return f.real(); }); + +        for (size_t i = 0; i < N; i++) { +            complexf f = corr_real[i] * ref[i]; +            corr_real[i] = f.real(); +        } +        return std::accumulate(corr_real.begin(), corr_real.end(), 0.0f); +    }; + +    const float start_arg = -1; +    const float end_arg = 1; + +    auto tau = +        boost::math::tools::brent_find_minima( +                correlate_point, start_arg, end_arg, 1000).first; + +    fprintf(stderr, "Fractional delay is %f\n", tau); + +    for (size_t i = 0; i < N; i++) { +        const complexf angle(0, tau * i); +        rotate_vec[i] = std::exp(angle); +    } +    rotate_vec[N/2] = cos(M_PI * tau); +    for (size_t i = 0; i < N; i++) { +        rotate_vec[i] *= fft_sig[i]; +    } + +    plan_ifft.execute(); + +    return corr_sig; +} + +std::pair<vec_cf, vec_cf> AlignSample::get_samples( +                size_t len) +{ +    std::pair<vec_cf, vec_cf> rval;      std::lock_guard<std::mutex> lock(m_mutex);      if (align() and -            m_rxsamples.size() > len + rx_delay -            and m_txsamples.size() > len) { +            m_rxsamples.size() > len and +            m_txsamples.size() > len) {          rval.first.reserve(len);          rval.second.reserve(len); -        std::copy(m_rxsamples.begin() + rx_delay, -                m_rxsamples.begin() + rx_delay + len, +        std::copy(m_rxsamples.begin(), +                m_rxsamples.begin() + len,                  std::back_inserter(rval.first));          std::copy(m_txsamples.begin(), diff --git a/AlignSample.hpp b/AlignSample.hpp index d027823..9529792 100644 --- a/AlignSample.hpp +++ b/AlignSample.hpp @@ -1,5 +1,5 @@  /* -   Copyright (C) 2015 +   Copyright (C) 2017     Matthias P. Braendli, matthias.braendli@mpb.li      http://opendigitalradio.org @@ -33,7 +33,8 @@  #define FFT_TYPE fftwf_complex -typedef std::complex<float> complexf; +using complexf = std::complex<float>; +using vec_cf = std::vector<complexf>;  struct CorrelationResult {      CorrelationResult(size_t len) : @@ -43,7 +44,7 @@ struct CorrelationResult {          rx_timestamp(0),          tx_timestamp(0) {} -    std::vector<complexf> correlation; +    vec_cf correlation;      float rx_power;      float tx_power; @@ -85,8 +86,14 @@ class AlignSample {          CorrelationResult crosscorrelate(size_t len); -        std::pair<std::vector<complexf>, std::vector<complexf> > get_samples( -                size_t len, size_t rx_delay); +        /* Drop delay samples from the rx_samples */ +        void delay_rx_samples(size_t delay); + +        /* Calculate and compensate for subsample delay, assuming that the +         * RX and TX samples are already aligned +         */ +        std::pair<vec_cf, vec_cf> get_samples( +                size_t len);          void consume(size_t samples); diff --git a/align/GenerateExampleTxRxIQ.py b/align/GenerateExampleTxRxIQ.py index fc105fd..03c79b6 100755 --- a/align/GenerateExampleTxRxIQ.py +++ b/align/GenerateExampleTxRxIQ.py @@ -30,19 +30,6 @@ import scipy  import scipy.signal  import numpy as np -## Configuration -# whether to correct for delays larger than one sample -# Not necessary unless you have delay larger than oversample/2 -do_integer_compensation = 0 - -# by how much to oversample the signal before applying the delay -oversample = 8 - -# Add a delay of delay/oversample samples to the input signal -#delay = 7 - -do_plot = False -  def gen_omega(length):      if not length % 2 == 0:          raise ValueError('Needs an even length array.') @@ -53,8 +40,9 @@ def gen_omega(length):      omega[halflength+1:] = 2*np.pi*( np.arange(halflength+1, length)-length )      return omega / length -def gen_signals(delay): -    ## Generate signal +def gen_signals(oversample, delay): +    """Generate a signal that is delayed and a bit noisy. Returns a tuple +    (original signal, shifted signal)"""      iq_file = "/home/bram/dab/aux/odr-dab-cir/phasereference.2048000.fc64.iq" @@ -137,13 +125,28 @@ def delay_signal(sig, delay):      return np.fft.ifft(sig_fft * rotate_vec) -for d in [2, 5, 9, 15, 34, 120]: -    a, b = gen_signals(d) -    delay = arg_max_corr(a,b) -    print("{} {}".format(d / oversample, delay)) +def fftplot(sig): +    plt.plot(np.abs(np.fft.fftshift(np.fft.fft(sig)))) + +if __name__ == '__main__': +    # Add a delay of d/oversample samples to the input signal + +    # by how much to oversample the signal before applying the delay +    oversample = 8 + +    do_plot = False + +    for d in [2, 7]: +        a, b = gen_signals(d, oversample) +        delay = arg_max_corr(a,b) +        print("{} {}".format(d / oversample, delay)) -    print("{} {}".format(d / oversample, arg_max_corr(a, delay_signal(b, delay)))) +        print("{} {}".format(d / oversample, arg_max_corr(a, delay_signal(b, delay)))) +        if do_plot: +            plt.figure() +            fftplot(a) +            fftplot(a-b) -if do_plot: -    plt.show() +    if do_plot: +        plt.show() @@ -1,5 +1,5 @@  /* -   Copyright (C) 2015 +   Copyright (C) 2017     Matthias P. Braendli, matthias.braendli@mpb.li      http://opendigitalradio.org @@ -62,7 +62,7 @@ AlignSample aligner;  PointCloud cloud(10000); -size_t do_receive(OutputUHD* output_uhd) +size_t do_receive(OutputUHD *output_uhd)  {      std::vector<complexf> samps(samps_per_buffer);      double first_sample_time = 0; @@ -99,7 +99,8 @@ long user_delay = 0;  void push_to_point_cloud(size_t rx_delay)  { -    auto points = aligner.get_samples(correlation_length, rx_delay + user_delay); +    aligner.delay_rx_samples(rx_delay + user_delay); +    auto points = aligner.get_samples(correlation_length);      if (points.first.size() > 0) {          cloud.push_samples(points);  | 
