residual doppler correction using adaptive filter taps

This commit is contained in:
cmayer 2019-03-29 20:36:04 +01:00
parent 127449e592
commit 97d63273d9
8 changed files with 257 additions and 26 deletions

View File

@ -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)

View File

@ -503,6 +503,53 @@
<value>1</value>
</param>
</block>
<block>
<key>blocks_null_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>bus_conns</key>
<value>[[0,],]</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(714, 261)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_null_sink_0</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>num_inputs</key>
<value>1</value>
</param>
<param>
<key>vlen</key>
<value>(1+(nB+nF)*sps)</value>
</param>
</block>
<block>
<key>blocks_tag_debug</key>
<param>
@ -2103,4 +2150,10 @@
<source_key>soft_dec</source_key>
<sink_key>in</sink_key>
</connection>
<connection>
<source_block_id>digitalhf_physical_layer_driver_0</source_block_id>
<sink_block_id>blocks_null_sink_0</sink_block_id>
<source_key>2</source_key>
<sink_key>0</sink_key>
</connection>
</flow_graph>

View File

@ -164,7 +164,7 @@
</param>
<param>
<key>value</key>
<value>0.0035</value>
<value>0.004</value>
</param>
<param>
<key>_enabled</key>
@ -297,7 +297,7 @@
</param>
<param>
<key>value</key>
<value>4</value>
<value>5</value>
</param>
</block>
<block>
@ -562,6 +562,57 @@
<value>1</value>
</param>
</block>
<block>
<key>blocks_file_sink</key>
<param>
<key>append</key>
<value>False</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>1</value>
</param>
<param>
<key>file</key>
<value>/Users/chm/Software/gr-digitalhf/examples/taps.bin</value>
</param>
<param>
<key>_coordinate</key>
<value>(576, 245)</value>
</param>
<param>
<key>_rotation</key>
<value>180</value>
</param>
<param>
<key>id</key>
<value>blocks_file_sink_0_1</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>unbuffered</key>
<value>True</value>
</param>
<param>
<key>vlen</key>
<value>(1+(nB+nF)*sps)</value>
</param>
</block>
<block>
<key>blocks_float_to_complex</key>
<param>
@ -782,7 +833,7 @@
</param>
<param>
<key>file</key>
<value>/Users/chm/Software/gr-kiwisdr/examples/20190326T120049Z_2670000_GB3WE_iq.wav</value>
<value>/Users/chm/Software/gr-kiwisdr/examples/20190327T095527Z_2503000_GB3WE_iq.wav</value>
</param>
<param>
<key>_coordinate</key>
@ -861,7 +912,7 @@
</param>
<param>
<key>alpha</key>
<value>0.005</value>
<value>0.2</value>
</param>
<param>
<key>mode</key>
@ -1583,7 +1634,7 @@
</param>
<param>
<key>update_time</key>
<value>.1</value>
<value>.01</value>
</param>
<param>
<key>ylabel</key>
@ -2270,4 +2321,10 @@
<source_key>soft_dec</source_key>
<sink_key>pdus</sink_key>
</connection>
<connection>
<source_block_id>digitalhf_physical_layer_driver_0</source_block_id>
<sink_block_id>blocks_file_sink_0_1</sink_block_id>
<source_key>2</source_key>
<sink_key>0</sink_key>
</connection>
</flow_graph>

View File

@ -818,6 +818,53 @@
<value>1</value>
</param>
</block>
<block>
<key>blocks_null_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>bus_conns</key>
<value>[[0,],]</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(586, 314)</value>
</param>
<param>
<key>_rotation</key>
<value>180</value>
</param>
<param>
<key>id</key>
<value>blocks_null_sink_0</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>num_inputs</key>
<value>1</value>
</param>
<param>
<key>vlen</key>
<value>(1+(nB+nF)*sps)</value>
</param>
</block>
<block>
<key>blocks_pdu_to_tagged_stream</key>
<param>
@ -2634,6 +2681,12 @@
<source_key>soft_dec</source_key>
<sink_key>pdus</sink_key>
</connection>
<connection>
<source_block_id>digitalhf_physical_layer_driver_0</source_block_id>
<sink_block_id>blocks_null_sink_0</sink_block_id>
<source_key>2</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>fec_decode_ccsds_27_fb_0</source_block_id>
<sink_block_id>blocks_file_sink_0</sink_block_id>

View File

@ -73,4 +73,11 @@
<type>message</type>
<optional>1</optional>
</source>
<source>
<name>taps</name>
<type>complex</type>
<vlen>(1+$sps*($nB+$nF))</vlen>
<optional>1</optional>
</source>
</block>

View File

@ -24,6 +24,7 @@
#include <boost/format.hpp>
#include <gnuradio/math.h>
#include <gnuradio/expj.h>
#include <gnuradio/io_signature.h>
#include <gnuradio/logger.h>
@ -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<ninput && nout<noutput_items; i+=_sps, ninput_processed+=_sps) {
// rotate samples
if (i == history()-1) {
_rotator.rotateN(&_rotated_samples[0] + i - _nB,
in + i - _nB,
_nB+_nF+1);
} else {
_rotator.rotateN(&_rotated_samples[0] + i + _nF+1 - _sps,
in + i + _nF+1 - _sps,
_sps);
}
if (_symbol_counter == _symbols.size()) {
publish_frame_info();
publish_soft_dec();
@ -187,7 +206,10 @@ adaptive_dfe_impl::general_work(int noutput_items,
}
// std::cout << "FILTER_CHECK: " << i << " " << i-1-_nB << " " << i+_nF << " " << in[i] << std::endl;
assert(i+_nF < nin && i-1-_nB >= 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()

View File

@ -21,6 +21,8 @@
#ifndef INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H
#define INCLUDED_DIGITALHF_ADAPTIVE_DFE_IMPL_H
#include <gnuradio/blocks/control_loop.h>
#include <gnuradio/blocks/rotator.h>
#include <gnuradio/digital/constellation.h>
#include <digitalhf/adaptive_dfe.h>
@ -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<std::string, pmt::pmt_t> _msg_ports;
pmt::pmt_t _msg_metadata;
int _num_samples_since_filter_update;
std::vector<gr_complex> _rotated_samples;
blocks::rotator _rotator;
gr::blocks::control_loop _control_loop;
enum state {
WAIT_FOR_PREAMBLE,
WAIT_FOR_FRAME_INFO,

View File

@ -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'))