From 97d63273d9ba8c3ba712f6525c9b5b33957a529b Mon Sep 17 00:00:00 2001 From: cmayer Date: Fri, 29 Mar 2019 20:36:04 +0100 Subject: [PATCH] residual doppler correction using adaptive filter taps --- CMakeLists.txt | 2 +- examples/test_188-110A.grc | 53 ++++++++++++++++ examples/test_188-110C.grc | 67 ++++++++++++++++++-- examples/test_s4285.grc | 53 ++++++++++++++++ grc/digitalhf_physical_layer_driver.xml | 7 +++ lib/adaptive_dfe_impl.cc | 84 +++++++++++++++++++------ lib/adaptive_dfe_impl.h | 8 +++ python/physical_layer_driver.py | 9 ++- 8 files changed, 257 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6fe6d7..dce854e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,7 @@ find_package(Doxygen) # components required to the list of GR_REQUIRED_COMPONENTS (in all # caps such as FILTER or FFT) and change the version to the minimum # API compatible version required. -set(GR_REQUIRED_COMPONENTS RUNTIME DIGITAL VOLK) +set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS DIGITAL VOLK) find_package(Gnuradio "3.7.2" REQUIRED) list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) include(GrVersion) diff --git a/examples/test_188-110A.grc b/examples/test_188-110A.grc index c0a333b..b37ab02 100644 --- a/examples/test_188-110A.grc +++ b/examples/test_188-110A.grc @@ -503,6 +503,53 @@ 1 + + blocks_null_sink + + alias + + + + bus_conns + [[0,],] + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (714, 261) + + + _rotation + 0 + + + id + blocks_null_sink_0 + + + type + complex + + + num_inputs + 1 + + + vlen + (1+(nB+nF)*sps) + + blocks_tag_debug @@ -2103,4 +2150,10 @@ soft_dec in + + digitalhf_physical_layer_driver_0 + blocks_null_sink_0 + 2 + 0 + diff --git a/examples/test_188-110C.grc b/examples/test_188-110C.grc index 978c298..bec8389 100644 --- a/examples/test_188-110C.grc +++ b/examples/test_188-110C.grc @@ -164,7 +164,7 @@ value - 0.0035 + 0.004 _enabled @@ -297,7 +297,7 @@ value - 4 + 5 @@ -562,6 +562,57 @@ 1 + + blocks_file_sink + + append + False + + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + file + /Users/chm/Software/gr-digitalhf/examples/taps.bin + + + _coordinate + (576, 245) + + + _rotation + 180 + + + id + blocks_file_sink_0_1 + + + type + complex + + + unbuffered + True + + + vlen + (1+(nB+nF)*sps) + + blocks_float_to_complex @@ -782,7 +833,7 @@ file - /Users/chm/Software/gr-kiwisdr/examples/20190326T120049Z_2670000_GB3WE_iq.wav + /Users/chm/Software/gr-kiwisdr/examples/20190327T095527Z_2503000_GB3WE_iq.wav _coordinate @@ -861,7 +912,7 @@ alpha - 0.005 + 0.2 mode @@ -1583,7 +1634,7 @@ update_time - .1 + .01 ylabel @@ -2270,4 +2321,10 @@ soft_dec pdus + + digitalhf_physical_layer_driver_0 + blocks_file_sink_0_1 + 2 + 0 + diff --git a/examples/test_s4285.grc b/examples/test_s4285.grc index 91dfe36..2c69eeb 100644 --- a/examples/test_s4285.grc +++ b/examples/test_s4285.grc @@ -818,6 +818,53 @@ 1 + + blocks_null_sink + + alias + + + + bus_conns + [[0,],] + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (586, 314) + + + _rotation + 180 + + + id + blocks_null_sink_0 + + + type + complex + + + num_inputs + 1 + + + vlen + (1+(nB+nF)*sps) + + blocks_pdu_to_tagged_stream @@ -2634,6 +2681,12 @@ soft_dec pdus + + digitalhf_physical_layer_driver_0 + blocks_null_sink_0 + 2 + 0 + fec_decode_ccsds_27_fb_0 blocks_file_sink_0 diff --git a/grc/digitalhf_physical_layer_driver.xml b/grc/digitalhf_physical_layer_driver.xml index e4bcce5..904cbbd 100644 --- a/grc/digitalhf_physical_layer_driver.xml +++ b/grc/digitalhf_physical_layer_driver.xml @@ -73,4 +73,11 @@ message 1 + + + taps + complex + (1+$sps*($nB+$nF)) + 1 + diff --git a/lib/adaptive_dfe_impl.cc b/lib/adaptive_dfe_impl.cc index 90ccf24..782f261 100644 --- a/lib/adaptive_dfe_impl.cc +++ b/lib/adaptive_dfe_impl.cc @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -59,7 +60,9 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol float alpha) : gr::block("adaptive_dfe", gr::io_signature::make(1, 1, sizeof(gr_complex)), - gr::io_signature::make(1, 1, sizeof(gr_complex))) + gr::io_signature::make2(2, 2, + sizeof(gr_complex), + sizeof(gr_complex)*(sps*(nF+nB)+1))) , _sps(sps) , _nB(nB*sps) , _nF(nF*sps) @@ -88,6 +91,10 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol {"frame_info", pmt::intern("frame_info")}} , _msg_metadata(pmt::make_dict()) , _state(WAIT_FOR_PREAMBLE) + , _num_samples_since_filter_update(0) + , _rotated_samples() + , _rotator() + , _control_loop(2*M_PI/100, 5e-3, -5e-3) { GR_LOG_DECLARE_LOGPTR(d_logger); GR_LOG_ASSIGN_LOGPTR(d_logger, "adaptive_dfe"); @@ -130,7 +137,8 @@ adaptive_dfe_impl::general_work(int noutput_items, gr::thread::scoped_lock lock(d_setlock); //GR_LOG_DEBUG(d_logger, str(boost::format("work: %d") % noutput_items)); gr_complex const* in = (gr_complex const *)input_items[0]; - gr_complex *out = (gr_complex *)output_items[0]; + gr_complex *out_symb = (gr_complex *)output_items[0]; + gr_complex *out_taps = (gr_complex *)output_items[1]; const int nin = ninput_items[0]; @@ -171,8 +179,19 @@ adaptive_dfe_impl::general_work(int noutput_items, } // WAIT_FOR_FRAME_INFO case DO_FILTER: { // std::cout << "========= offset (DO_FILTER) nitems_read(0)= " << nitems_read(0) << " ==========" << std::endl; + _rotated_samples.resize(ninput+_nF+1); int ninput_processed = 0; for (int i=history()-1; i= 0); - out[nout++] = filter(in + i - _nB, in + i + _nF+1); + out_symb[nout] = filter(&_rotated_samples[0] + i - _nB, + &_rotated_samples[0] + i + _nF+1); + std::memcpy(&out_taps[(_nB+_nF+1)*nout], _taps_samples, (_nB+_nF+1)*sizeof(gr_complex)); + ++nout; } // next sample consume(0, ninput_processed); break; @@ -199,10 +221,10 @@ adaptive_dfe_impl::general_work(int noutput_items, bool adaptive_dfe_impl::start() { gr::thread::scoped_lock lock(d_setlock); - _taps_samples = (gr_complex*)(volk_malloc((_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); - _taps_symbols = (gr_complex*)(volk_malloc( _nW*sizeof(gr_complex), volk_get_alignment())); - _hist_symbols = (gr_complex*)(volk_malloc( 2*_nW*sizeof(gr_complex), volk_get_alignment())); - + _taps_samples = (gr_complex*)(volk_malloc((_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); + _last_taps_samples = (gr_complex*)(volk_malloc((_nB+_nF+1)*sizeof(gr_complex), volk_get_alignment())); + _taps_symbols = (gr_complex*)(volk_malloc( _nW*sizeof(gr_complex), volk_get_alignment())); + _hist_symbols = (gr_complex*)(volk_malloc( 2*_nW*sizeof(gr_complex), volk_get_alignment())); reset_filter(); GR_LOG_DEBUG(d_logger,str(boost::format("adaptive_dfe_impl::start() nB=%d nF=%d mu=%f alpha=%f") % _nB % _nF % _mu % _alpha)); @@ -213,6 +235,7 @@ bool adaptive_dfe_impl::stop() gr::thread::scoped_lock lock(d_setlock); GR_LOG_DEBUG(d_logger, "adaptive_dfe_impl::stop()"); VOLK_SAFE_DELETE(_taps_samples); + VOLK_SAFE_DELETE(_last_taps_samples); VOLK_SAFE_DELETE(_taps_symbols); VOLK_SAFE_DELETE(_hist_symbols); return true; @@ -221,14 +244,19 @@ bool adaptive_dfe_impl::stop() gr_complex adaptive_dfe_impl::filter(gr_complex const* start, gr_complex const* end) { assert(end-start == _nB + _nF + 1); - gr_complex filter_output = 0; + _num_samples_since_filter_update += _sps; + + // (1) run the filter filter + gr_complex filter_output(0); + // (1a) taps_samples volk_32fc_x2_dot_prod_32fc(&filter_output, start, _taps_samples, _nB+_nF+1); - gr_complex dot_symbols=0; + // (1b) taps_symbols + gr_complex dot_symbols(0); gr::digital::constellation_sptr constell = _constellations[_constellation_index]; - bool const update_taps = true;//constell->bits_per_symbol() <= 3; + bool const update_taps = true; //constell->bits_per_symbol() <= 3; if (constell->bits_per_symbol() > 3) _use_symbol_taps = false; if (_use_symbol_taps) { @@ -239,8 +267,10 @@ gr_complex adaptive_dfe_impl::filter(gr_complex const* start, gr_complex const* filter_output += dot_symbols; } assert(_symbol_counter < _symbols.size()); + gr_complex known_symbol = _symbols[_symbol_counter]; bool const is_known = std::abs(known_symbol) > 1e-5; + // (2) unknown symbols (=data): compute soft decisions if (not is_known) { gr_complex const descrambled_filter_output = std::conj(_scramble[_symbol_counter]) * filter_output; unsigned int const jc = constell->decision_maker(&descrambled_filter_output); @@ -255,11 +285,27 @@ gr_complex adaptive_dfe_impl::filter(gr_complex const* start, gr_complex const* known_symbol = _scramble[_symbol_counter] * descrambled_symbol; } // std::cout << "FILTER: " << filter_output <<" " << known_symbol << " " << start[_nB+1] << std::endl; + // (3) filter update if (is_known || update_taps) { - gr_complex const err = filter_output - known_symbol; - for (int j=0; j<_nB+_nF+1; ++j) - _taps_samples[j] -= _mu*err*std::conj(start[j]); + // (3a) control loop update for doppler correction using the adaptibve filter taps + gr_complex acc(0); + for (int j=_nB+1-2*_sps; j<_nB+1+2*_sps+1; ++j) + acc += std::conj(_last_taps_samples[j]) * _taps_samples[j]; + float const frequency_err = gr::fast_atan2f(acc)/_num_samples_since_filter_update; // frequency error (rad/sample) + _control_loop.advance_loop(frequency_err); + _control_loop.phase_wrap(); + _control_loop.frequency_limit(); + _rotator.set_phase_incr(gr_expj(_control_loop.get_frequency())); + // (3b) update of adaptive filter taps + gr_complex const err = filter_output - known_symbol; + // taps_samples + for (int j=0; j<_nB+_nF+1; ++j) { + _last_taps_samples[j] = _taps_samples[j]; + _taps_samples[j] -= _mu*err*std::conj(start[j]); + _num_samples_since_filter_update = 0; + } + // taps_symbols if (_use_symbol_taps) { for (int j=0; j<_nW; ++j) { assert(_hist_symbol_index+j < 2*_nW); @@ -270,8 +316,9 @@ gr_complex adaptive_dfe_impl::filter(gr_complex const* start, gr_complex const* _hist_symbol_index = 0; } } + // (4) save the descrambled symbol (-> frame_info) _descrambled_symbols[_symbol_counter] = filter_output*std::conj(_scramble[_symbol_counter]); - return filter_output*std::conj(_scramble[_symbol_counter++]); + return _descrambled_symbols[_symbol_counter++]; } int @@ -305,12 +352,13 @@ adaptive_dfe_impl::recenter_filter_taps() { void adaptive_dfe_impl::reset_filter() { - std::fill_n(_taps_samples, _nB+_nF+1, gr_complex(0)); - std::fill_n(_taps_symbols, _nW, gr_complex(0)); - std::fill_n(_hist_symbols, 2*_nW, gr_complex(0)); - _taps_samples[_nB+1] = 0.01; + std::fill_n(_taps_samples, _nB+_nF+1, gr_complex(0)); + std::fill_n(_last_taps_samples, _nB+_nF+1, gr_complex(0)); + std::fill_n(_taps_symbols, _nW, gr_complex(0)); + std::fill_n(_hist_symbols, 2*_nW, gr_complex(0)); _taps_symbols[0] = 1; _hist_symbol_index = 0; + _num_samples_since_filter_update = 0; } void adaptive_dfe_impl::publish_frame_info() diff --git a/lib/adaptive_dfe_impl.h b/lib/adaptive_dfe_impl.h index d7b2464..07eb90b 100644 --- a/lib/adaptive_dfe_impl.h +++ b/lib/adaptive_dfe_impl.h @@ -21,6 +21,8 @@ #ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H #define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H +#include +#include #include #include @@ -69,6 +71,7 @@ private: gr_complex* _taps_samples; gr_complex* _taps_symbols; + gr_complex* _last_taps_samples; gr_complex* _hist_symbols; int _hist_symbol_index; @@ -87,6 +90,11 @@ private: std::map _msg_ports; pmt::pmt_t _msg_metadata; + int _num_samples_since_filter_update; + std::vector _rotated_samples; + blocks::rotator _rotator; + gr::blocks::control_loop _control_loop; + enum state { WAIT_FOR_PREAMBLE, WAIT_FOR_FRAME_INFO, diff --git a/python/physical_layer_driver.py b/python/physical_layer_driver.py index ed667fd..7060933 100644 --- a/python/physical_layer_driver.py +++ b/python/physical_layer_driver.py @@ -36,7 +36,10 @@ class physical_layer_driver(gr.hier_block2): gr.hier_block2.__init__(self, "physical_layer_driver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(2, 2, gr.sizeof_gr_complex)) # Output signature + gr.io_signature3(3, 3, + gr.sizeof_gr_complex, + gr.sizeof_gr_complex, + gr.sizeof_gr_complex*(1+sps*(nB+nF)))) # Output signature self._sps = sps self._alpha = alpha @@ -68,8 +71,10 @@ class physical_layer_driver(gr.hier_block2): (self._doppler_correction, 0), (self._adaptive_filter, 0), (self, 0)) - self.connect((self._corr_est, 1), ## correlation + self.connect((self._corr_est, 1), ## correlation (self, 1)) + self.connect((self._adaptive_filter, 1), ## taps + (self, 2)) self.msg_connect((self._doppler_correction, 'doppler'), (self._msg_proxy, 'doppler')) self.msg_connect((self._msg_proxy, 'doppler'), (self._doppler_correction, 'doppler'))