/* -*- c++ -*- */ /* * Copyright (C) 2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * * SPDX-License-Identifier: GPL-3.0-or-later * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "interpolating_resampler.h" #include <gnuradio/math.h> #include <boost/make_unique.hpp> #include <deque> #include <stdexcept> namespace gr { namespace digital { interpolating_resampler::interpolating_resampler(enum ir_type type, bool derivative) : d_type(type), d_derivative(derivative), d_phase(0.0f), d_phase_wrapped(0.0f), d_phase_n(0), d_prev_phase(0.0f), d_prev_phase_wrapped(0.0f), d_prev_phase_n(0) { switch (d_type) { case IR_MMSE_8TAP: break; case IR_PFB_NO_MF: break; case IR_PFB_MF: break; case IR_NONE: default: throw std::invalid_argument( "interpolating_resampler: invalid interpolating resampler type."); break; } sync_reset(0.0f); } void interpolating_resampler::next_phase(float increment, float& phase, int& phase_n, float& phase_wrapped) { float n; phase = d_phase_wrapped + increment; n = floorf(phase); phase_wrapped = phase - n; phase_n = static_cast<int>(n); } void interpolating_resampler::advance_phase(float increment) { d_prev_phase = d_phase; d_prev_phase_wrapped = d_phase_wrapped; d_prev_phase_n = d_phase_n; next_phase(increment, d_phase, d_phase_n, d_phase_wrapped); } void interpolating_resampler::revert_phase() { d_phase = d_prev_phase; d_phase_wrapped = d_prev_phase_wrapped; d_phase_n = d_prev_phase_n; } void interpolating_resampler::sync_reset(float phase) { float n; d_phase = phase; n = floorf(d_phase); d_phase_wrapped = d_phase - n; d_phase_n = static_cast<int>(n); d_prev_phase = d_phase; d_prev_phase_wrapped = d_phase_wrapped; d_prev_phase_n = d_phase_n; } /*************************************************************************/ std::unique_ptr<interpolating_resampler_ccf> interpolating_resampler_ccf::make( enum ir_type type, bool derivative, int nfilts, const std::vector<float>& taps) { switch (type) { case IR_MMSE_8TAP: return boost::make_unique<interp_resampler_mmse_8tap_cc>(derivative); case IR_PFB_NO_MF: return boost::make_unique<interp_resampler_pfb_no_mf_cc>(derivative, nfilts); case IR_PFB_MF: return boost::make_unique<interp_resampler_pfb_mf_ccf>(taps, nfilts, derivative); case IR_NONE: return nullptr; } throw std::invalid_argument("interpolating_resampler_ccf: invalid " "interpolating resampler type."); } /*************************************************************************/ std::unique_ptr<interpolating_resampler_fff> interpolating_resampler_fff::make( enum ir_type type, bool derivative, int nfilts, const std::vector<float>& taps) { switch (type) { case IR_MMSE_8TAP: return boost::make_unique<interp_resampler_mmse_8tap_ff>(derivative); case IR_PFB_NO_MF: return boost::make_unique<interp_resampler_pfb_no_mf_ff>(derivative, nfilts); case IR_PFB_MF: return boost::make_unique<interp_resampler_pfb_mf_fff>(taps, nfilts, derivative); case IR_NONE: return nullptr; } throw std::invalid_argument("interpolating_resampler_fff: invalid " "interpolating resampler type."); } /*************************************************************************/ interp_resampler_mmse_8tap_cc::interp_resampler_mmse_8tap_cc(bool derivative) : interpolating_resampler_ccf(IR_MMSE_8TAP, derivative) { if (d_derivative) { d_interp_diff = boost::make_unique<filter::mmse_interp_differentiator_cc>(); } } interp_resampler_mmse_8tap_cc::~interp_resampler_mmse_8tap_cc() {} gr_complex interp_resampler_mmse_8tap_cc::interpolate(const gr_complex input[], float mu) const { return d_interp.interpolate(input, mu); } gr_complex interp_resampler_mmse_8tap_cc::differentiate(const gr_complex input[], float mu) const { return d_interp_diff->differentiate(input, mu); } unsigned int interp_resampler_mmse_8tap_cc::ntaps() const { return d_interp.ntaps(); } /*************************************************************************/ interp_resampler_mmse_8tap_ff::interp_resampler_mmse_8tap_ff(bool derivative) : interpolating_resampler_fff(IR_MMSE_8TAP, derivative) { if (d_derivative) { d_interp_diff = boost::make_unique<filter::mmse_interp_differentiator_ff>(); } } interp_resampler_mmse_8tap_ff::~interp_resampler_mmse_8tap_ff() {} float interp_resampler_mmse_8tap_ff::interpolate(const float input[], float mu) const { return d_interp.interpolate(input, mu); } float interp_resampler_mmse_8tap_ff::differentiate(const float input[], float mu) const { return d_interp_diff->differentiate(input, mu); } unsigned int interp_resampler_mmse_8tap_ff::ntaps() const { return d_interp.ntaps(); } /*************************************************************************/ #include "gnuradio/filter/interp_differentiator_taps.h" #include "gnuradio/filter/interpolator_taps.h" interp_resampler_pfb_no_mf_cc::interp_resampler_pfb_no_mf_cc(bool derivative, int nfilts) : interpolating_resampler_ccf(IR_PFB_NO_MF, derivative) { if (nfilts <= 1) throw std::invalid_argument("interpolating_resampler_pfb_no_mf_cc: " "number of polyphase filter arms " "must be greater than 1"); // Round up the number of filter arms to the current or next power of 2 d_nfilters = 1 << (static_cast<int>(log2f(static_cast<float>(nfilts - 1))) + 1); // N.B. We assume in this class: NSTEPS == DNSTEPS and NTAPS == DNTAPS // Limit to the maximum number of precomputed MMSE tap sets if (d_nfilters <= 0 || d_nfilters > NSTEPS) d_nfilters = NSTEPS; // Create our polyphase filter arms for the steps from 0.0 to 1.0 from // the MMSE interpolating filter and MMSE interpolating differentiator // taps rows. // N.B. We create an extra final row for an offset of 1.0, because it's // easier than dealing with wrap around from 0.99... to 0.0 shifted // by 1 tap. d_filters.reserve(d_nfilters + 1); d_diff_filters.reserve(d_nfilters + 1); std::vector<float> t(NTAPS, 0); int incr = NSTEPS / d_nfilters; for (int src = 0; src <= NSTEPS; src += incr) { t.assign(&taps[src][0], &taps[src][NTAPS]); d_filters.emplace_back(t); if (d_derivative) { t.assign(&Dtaps[src][0], &Dtaps[src][DNTAPS]); d_diff_filters.emplace_back(t); } } } interp_resampler_pfb_no_mf_cc::~interp_resampler_pfb_no_mf_cc() {} gr_complex interp_resampler_pfb_no_mf_cc::interpolate(const gr_complex input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_no_mf_cc: mu is not " "in the range [0.0, 1.0]"); return d_filters[arm].filter(input); } gr_complex interp_resampler_pfb_no_mf_cc::differentiate(const gr_complex input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_no_mf_cc: mu is not " "in the range [0.0, 1.0]"); return d_diff_filters[arm].filter(input); } unsigned int interp_resampler_pfb_no_mf_cc::ntaps() const { return NTAPS; } /*************************************************************************/ interp_resampler_pfb_no_mf_ff::interp_resampler_pfb_no_mf_ff(bool derivative, int nfilts) : interpolating_resampler_fff(IR_PFB_NO_MF, derivative), d_nfilters(0) { if (nfilts <= 1) throw std::invalid_argument("interpolating_resampler_pfb_no_mf_ff: " "number of polyphase filter arms " "must be greater than 1"); // Round up the number of filter arms to the current or next power of 2 d_nfilters = 1 << (static_cast<int>(log2f(static_cast<float>(nfilts - 1))) + 1); // N.B. We assume in this class: NSTEPS == DNSTEPS and NTAPS == DNTAPS // Limit to the maximum number of precomputed MMSE tap sets if (d_nfilters <= 0 || d_nfilters > NSTEPS) d_nfilters = NSTEPS; // Create our polyphase filter arms for the steps from 0.0 to 1.0 from // the MMSE interpolating filter and MMSE interpolating differentiator // taps rows. // N.B. We create an extra final row for an offset of 1.0, because it's // easier than dealing with wrap around from 0.99... to 0.0 shifted // by 1 tap. d_filters.reserve(d_nfilters + 1); d_diff_filters.reserve(d_nfilters + 1); std::vector<float> t(NTAPS, 0); int incr = NSTEPS / d_nfilters; for (int src = 0; src <= NSTEPS; src += incr) { t.assign(&taps[src][0], &taps[src][NTAPS]); d_filters.emplace_back(t); if (d_derivative) { t.assign(&Dtaps[src][0], &Dtaps[src][DNTAPS]); d_diff_filters.emplace_back(t); } } } interp_resampler_pfb_no_mf_ff::~interp_resampler_pfb_no_mf_ff() {} float interp_resampler_pfb_no_mf_ff::interpolate(const float input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_no_mf_ff: mu is not " "in the range [0.0, 1.0]"); return d_filters[arm].filter(input); } float interp_resampler_pfb_no_mf_ff::differentiate(const float input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_no_mf_ff: mu is not " "in the range [0.0, 1.0]"); return d_diff_filters[arm].filter(input); } unsigned int interp_resampler_pfb_no_mf_ff::ntaps() const { return NTAPS; } /*************************************************************************/ interp_resampler_pfb_mf_ccf::interp_resampler_pfb_mf_ccf(const std::vector<float>& taps, int nfilts, bool derivative) : interpolating_resampler_ccf(IR_PFB_MF, derivative), d_nfilters(nfilts), d_taps_per_filter(static_cast<unsigned int>( ceil(static_cast<double>(taps.size()) / static_cast<double>(nfilts)))) { if (d_nfilters <= 1) throw std::invalid_argument("interpolating_resampler_pfb_mf_ccf: " "number of polyphase filter arms " "must be greater than 1"); if (taps.size() < static_cast<unsigned int>(d_nfilters)) throw std::invalid_argument("interpolating_resampler_pfb_mf_ccf: " "length of the prototype filter taps" " must be greater than or equal to " "the number of polyphase filter arms."); // Create a derivative filter from the provided taps // First create a truncated ideal differentiator filter int ideal_diff_filt_len = 3; // Must be odd; rest of init assumes odd. std::vector<float> ideal_diff_taps(ideal_diff_filt_len, 0.0f); int i, n; n = ideal_diff_taps.size() / 2; for (i = -n; i < 0; i++) { ideal_diff_taps[i + n] = (-i & 1) == 1 ? -1.0f / i : 1.0f / i; ideal_diff_taps[n - i] = -ideal_diff_taps[i + n]; } ideal_diff_taps[n] = 0.0f; // Perform linear convolution of prototype filter taps and the truncated // ideal differentiator taps to generate a derivative matched filter. // N.B. the truncated ideal differentiator taps must have an odd length int j, k, l, m; m = ideal_diff_taps.size(); n = taps.size(); l = m + n - 1; // length of convolution std::deque<float> diff_taps(l, 0.0f); for (i = 0; i < l; i++) { for (j = 0; j < m; j++) { k = i + j - (m - 1); if (k < 0 || k >= n) continue; diff_taps[i] += ideal_diff_taps[(m - 1) - j] * taps[k]; } } // Trim the convolution so the prototype derivative filter is the same // length as the passed in prototype filter taps. n = ideal_diff_taps.size() / 2; for (i = 0; i < n; i++) { diff_taps.pop_back(); diff_taps.pop_front(); } // Squash the differentiation noise spikes at the filter ends. diff_taps[0] = 0.0f; diff_taps[diff_taps.size() - 1] = 0.0f; // Normalize the prototype derviative filter gain to the number of // filter arms n = diff_taps.size(); float mag = 0.0f; for (i = 0; i < n; i++) mag += fabsf(diff_taps[i]); for (i = 0; i < n; i++) { diff_taps[i] *= d_nfilters / mag; if (d_derivative && std::isnan(diff_taps[i])) throw std::runtime_error("interpolating_resampler_pfb_mf_ccf: " "NaN error creating derivative filter."); } // Create our polyphase filter arms for the steps from 0.0 to 1.0 from // the prototype matched filter. // N.B. We create an extra final row for an offset of 1.0, because it's // easier than dealing with wrap around from 0.99... to 0.0 shifted // by 1 tap. d_filters.reserve(d_nfilters + 1); d_diff_filters.reserve(d_nfilters + 1); m = taps.size(); n = diff_taps.size(); d_taps.resize(d_nfilters + 1); d_diff_taps.resize(d_nfilters + 1); signed int taps_per_filter = static_cast<signed int>(d_taps_per_filter); for (i = 0; i <= d_nfilters; i++) { d_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f); for (j = 0; j < taps_per_filter; j++) { k = i + j * d_nfilters; if (k < m) d_taps[i][j] = taps[k]; } d_filters.emplace_back(d_taps[i]); if (!d_derivative) continue; d_diff_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f); for (j = 0; j < taps_per_filter; j++) { k = i + j * d_nfilters; if (k < n) d_diff_taps[i][j] = diff_taps[k]; } d_diff_filters.emplace_back(d_diff_taps[i]); } } interp_resampler_pfb_mf_ccf::~interp_resampler_pfb_mf_ccf() {} gr_complex interp_resampler_pfb_mf_ccf::interpolate(const gr_complex input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_mf_ccf: mu is not " "in the range [0.0, 1.0]"); return d_filters[arm].filter(input); } gr_complex interp_resampler_pfb_mf_ccf::differentiate(const gr_complex input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_mf_ccf: mu is not " "in the range [0.0, 1.0]"); return d_diff_filters[arm].filter(input); } unsigned int interp_resampler_pfb_mf_ccf::ntaps() const { return d_taps_per_filter; } /*************************************************************************/ interp_resampler_pfb_mf_fff::interp_resampler_pfb_mf_fff(const std::vector<float>& taps, int nfilts, bool derivative) : interpolating_resampler_fff(IR_PFB_MF, derivative), d_nfilters(nfilts), d_taps_per_filter(static_cast<unsigned int>( ceil(static_cast<double>(taps.size()) / static_cast<double>(nfilts)))) { if (d_nfilters <= 1) throw std::invalid_argument("interpolating_resampler_pfb_mf_fff: " "number of polyphase filter arms " "must be greater than 1"); if (taps.size() < static_cast<unsigned int>(d_nfilters)) throw std::invalid_argument("interpolating_resampler_pfb_mf_fff: " "length of the prototype filter taps" " must be greater than or equal to " "the number of polyphase filter arms."); // Create a derivative filter from the provided taps // First create a truncated ideal differentiator filter int ideal_diff_filt_len = 3; // Must be odd; rest of init assumes odd. std::vector<float> ideal_diff_taps(ideal_diff_filt_len, 0.0f); int i, n; n = ideal_diff_taps.size() / 2; for (i = -n; i < 0; i++) { ideal_diff_taps[i + n] = (-i & 1) == 1 ? -1.0f / i : 1.0f / i; ideal_diff_taps[n - i] = -ideal_diff_taps[i + n]; } ideal_diff_taps[n] = 0.0f; // Perform linear convolution of prototype filter taps and the truncated // ideal differentiator taps to generate a derivative matched filter. // N.B. the truncated ideal differentiator taps must have an odd length int j, k, l, m; m = ideal_diff_taps.size(); n = taps.size(); l = m + n - 1; // length of convolution std::deque<float> diff_taps(l, 0.0f); for (i = 0; i < l; i++) { for (j = 0; j < m; j++) { k = i + j - (m - 1); if (k < 0 || k >= n) continue; diff_taps[i] += ideal_diff_taps[(m - 1) - j] * taps[k]; } } // Trim the convolution so the prototype derivative filter is the same // length as the passed in prototype filter taps. n = ideal_diff_taps.size() / 2; for (i = 0; i < n; i++) { diff_taps.pop_back(); diff_taps.pop_front(); } // Squash the differentiation noise spikes at the filter ends. diff_taps[0] = 0.0f; diff_taps[diff_taps.size() - 1] = 0.0f; // Normalize the prototype derviative filter gain to the number of // filter arms n = diff_taps.size(); float mag = 0.0f; for (i = 0; i < n; i++) mag += fabsf(diff_taps[i]); for (i = 0; i < n; i++) { diff_taps[i] *= d_nfilters / mag; if (d_derivative && std::isnan(diff_taps[i])) throw std::runtime_error("interpolating_resampler_pfb_mf_fff: " "NaN error creating derivative filter."); } // Create our polyphase filter arms for the steps from 0.0 to 1.0 from // the prototype matched filter. // N.B. We create an extra final row for an offset of 1.0, because it's // easier than dealing with wrap around from 0.99... to 0.0 shifted // by 1 tap. d_filters.reserve(d_nfilters + 1); d_diff_filters.reserve(d_nfilters + 1); m = taps.size(); n = diff_taps.size(); d_taps.resize(d_nfilters + 1); d_diff_taps.resize(d_nfilters + 1); signed int taps_per_filter = static_cast<signed int>(d_taps_per_filter); for (i = 0; i <= d_nfilters; i++) { d_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f); for (j = 0; j < taps_per_filter; j++) { k = i + j * d_nfilters; if (k < m) d_taps[i][j] = taps[k]; } d_filters.emplace_back(d_taps[i]); if (!d_derivative) continue; d_diff_taps[i] = std::vector<float>(d_taps_per_filter, 0.0f); for (j = 0; j < taps_per_filter; j++) { k = i + j * d_nfilters; if (k < n) d_diff_taps[i][j] = diff_taps[k]; } d_diff_filters.emplace_back(d_diff_taps[i]); } } interp_resampler_pfb_mf_fff::~interp_resampler_pfb_mf_fff() {} float interp_resampler_pfb_mf_fff::interpolate(const float input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_mf_fff: mu is not " "in the range [0.0, 1.0]"); return d_filters[arm].filter(input); } float interp_resampler_pfb_mf_fff::differentiate(const float input[], float mu) const { int arm = static_cast<int>(rint(mu * d_nfilters)); if (arm < 0 || arm > d_nfilters) throw std::runtime_error("interp_resampler_pfb_mf_fff: mu is not " "in the range [0.0, 1.0]"); return d_diff_filters[arm].filter(input); } unsigned int interp_resampler_pfb_mf_fff::ntaps() const { return d_taps_per_filter; } } /* namespace digital */ } /* namespace gr */