mirror of
https://github.com/hb9fxq/gr-digitalhf
synced 2024-12-22 15:10:00 +00:00
initial doppler estimate before starting the adaptive DFE
This commit is contained in:
parent
56ae39c0ed
commit
4c94787579
|
@ -242,7 +242,7 @@
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>label0</key>
|
<key>label0</key>
|
||||||
<value>PSK</value>
|
<value>BPSK</value>
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>label1</key>
|
<key>label1</key>
|
||||||
|
@ -282,7 +282,7 @@
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>option2</key>
|
<key>option2</key>
|
||||||
<value>'1'</value>
|
<value>'2'</value>
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>option3</key>
|
<key>option3</key>
|
||||||
|
@ -802,7 +802,7 @@
|
||||||
</param>
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<key>repeat</key>
|
<key>repeat</key>
|
||||||
<value>False</value>
|
<value>True</value>
|
||||||
</param>
|
</param>
|
||||||
</block>
|
</block>
|
||||||
<block>
|
<block>
|
||||||
|
|
|
@ -44,7 +44,17 @@ public:
|
||||||
PyGILState_Release(_state);
|
PyGILState_Release(_state);
|
||||||
}
|
}
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
boost::python::numpy::ndarray
|
||||||
|
complex_vector_to_ndarray(std::vector<gr_complex> const& v) {
|
||||||
|
return boost::python::numpy::from_data
|
||||||
|
(&v.front(),
|
||||||
|
boost::python::numpy::dtype::get_builtin<gr_complex>(),
|
||||||
|
boost::python::make_tuple(v.size()),
|
||||||
|
boost::python::make_tuple(sizeof(gr_complex)),
|
||||||
|
boost::python::object());
|
||||||
}
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
adaptive_dfe::sptr
|
adaptive_dfe::sptr
|
||||||
adaptive_dfe::make(int sps, // samples per symbol
|
adaptive_dfe::make(int sps, // samples per symbol
|
||||||
|
@ -97,6 +107,7 @@ adaptive_dfe_impl::adaptive_dfe_impl(int sps, // samples per symbol
|
||||||
, _scramble()
|
, _scramble()
|
||||||
, _descrambled_symbols()
|
, _descrambled_symbols()
|
||||||
, _symbol_counter(0)
|
, _symbol_counter(0)
|
||||||
|
, _need_samples(false)
|
||||||
, _df(0)
|
, _df(0)
|
||||||
, _phase(0)
|
, _phase(0)
|
||||||
, _b{0.338187046465954, -0.288839024460507}
|
, _b{0.338187046465954, -0.288839024460507}
|
||||||
|
@ -137,13 +148,12 @@ adaptive_dfe_impl::general_work(int noutput_items,
|
||||||
for (; i<ninput_items[0] && nout < noutput_items; ++i) {
|
for (; i<ninput_items[0] && nout < noutput_items; ++i) {
|
||||||
assert(nout < noutput_items);
|
assert(nout < noutput_items);
|
||||||
|
|
||||||
insert_sample(in[i]);
|
|
||||||
|
|
||||||
if (_state == WAIT_FOR_PREAMBLE) {
|
if (_state == WAIT_FOR_PREAMBLE) {
|
||||||
|
insert_sample(in[i]);
|
||||||
uint64_t offset = 0;
|
uint64_t offset = 0;
|
||||||
float phase_est = 0;
|
float phase_est = 0;
|
||||||
if (get_correlation_tag(i, offset, phase_est)) {
|
if (get_correlation_tag(i, offset, phase_est)) {
|
||||||
_state = DO_FILTER;
|
_state = INITIAL_DOPPLER_ESTIMATE;
|
||||||
_sample_counter = 0;
|
_sample_counter = 0;
|
||||||
_symbol_counter = 0;
|
_symbol_counter = 0;
|
||||||
// _symbols.clear();
|
// _symbols.clear();
|
||||||
|
@ -165,113 +175,91 @@ adaptive_dfe_impl::general_work(int noutput_items,
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // WAIT_FOR_PREAMBLE
|
||||||
|
|
||||||
|
if (_state == INITIAL_DOPPLER_ESTIMATE) {
|
||||||
|
// buffer samples and replay them later once the initial doppler estimate is there
|
||||||
|
if (_samples.size() == _sps * _symbols.size()) {
|
||||||
|
GILLock gil_lock;
|
||||||
|
try {
|
||||||
|
std::vector<gr_complex> const empty_vec;
|
||||||
|
// initial doppler estimate
|
||||||
|
if (!update_doppler_information(_physicalLayer.attr("get_doppler")
|
||||||
|
(complex_vector_to_ndarray(empty_vec),
|
||||||
|
complex_vector_to_ndarray(_samples)))) {
|
||||||
|
_state = WAIT_FOR_PREAMBLE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (boost::python::error_already_set const&) {
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
// (1) correct all samples in the circular buffer with the inital doppler estimate
|
||||||
|
for (int j=_nB+1; j<_nB+_nF+1; ++j) {
|
||||||
|
assert(_hist_sample_index+j < 2*(_nB+_nF+1));
|
||||||
|
_hist_samples[_hist_sample_index+j] *= gr_expj(-_phase);
|
||||||
|
update_local_oscillator();
|
||||||
|
}
|
||||||
|
// (2) insert all buffered samples and run the adaptive filter for them
|
||||||
|
// instead of pop_front() we first reverse _samples and then insert back() + pop_back()
|
||||||
|
// O(N) instead of O(N^2)
|
||||||
|
std::reverse(_samples.begin(), _samples.end());
|
||||||
|
while (!_samples.empty() && nout < noutput_items) {
|
||||||
|
insert_sample(_samples.back());
|
||||||
|
_sample_counter += 1;
|
||||||
|
_samples.pop_back();
|
||||||
|
if ((_sample_counter%_sps) == 0)
|
||||||
|
out[nout++] = filter();
|
||||||
|
}
|
||||||
|
if (_samples.empty()) {
|
||||||
|
_state = DO_FILTER;
|
||||||
|
} else {
|
||||||
|
_state = INITIAL_DOPPLER_ESTIMATE_CONTINUE;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_samples.push_back(in[i]);
|
||||||
|
} // INITIAL_DOPPLER_ESTIMATE_CONTINUE
|
||||||
|
|
||||||
|
if (_state == INITIAL_DOPPLER_ESTIMATE_CONTINUE) {
|
||||||
|
std::cout << "INITIAL_DOPPLER_ESTIMATE_CONTINUE\n";
|
||||||
|
while (!_samples.empty() && nout < noutput_items) {
|
||||||
|
insert_sample(_samples.back());
|
||||||
|
_sample_counter += 1;
|
||||||
|
_samples.pop_back();
|
||||||
|
if ((_sample_counter%_sps) == 0)
|
||||||
|
out[nout++] = filter();
|
||||||
|
}
|
||||||
|
if (_samples.empty()) {
|
||||||
|
_state = DO_FILTER;
|
||||||
|
} else {
|
||||||
|
_state = INITIAL_DOPPLER_ESTIMATE_CONTINUE;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} // INITIAL_DOPPLER_ESTIMATE_CONTINUE
|
||||||
|
|
||||||
if (_state == DO_FILTER) {
|
if (_state == DO_FILTER) {
|
||||||
gr_complex dot_samples = 0;
|
|
||||||
// volk_32fc_x2_dot_prod_32fc(&dot_samples,
|
|
||||||
// _hist_samples+_hist_sample_index,
|
|
||||||
// _taps_samples,
|
|
||||||
// _nB+_nF+1);
|
|
||||||
// if (_sample_counter < 80*5)
|
|
||||||
// std::cout << "SAMPLE " << _sample_counter << " " << dot_samples << std::endl;
|
|
||||||
gr_complex filter_output = dot_samples;
|
|
||||||
_samples.push_back(_hist_samples[_hist_sample_index+_nB+1]);
|
|
||||||
if ((_sample_counter%_sps) == 0) {
|
if ((_sample_counter%_sps) == 0) {
|
||||||
if (_symbol_counter == _symbols.size()) {
|
if (_symbol_counter == _symbols.size()) {
|
||||||
_symbol_counter = 0;
|
_symbol_counter = 0;
|
||||||
GILLock gil_lock;
|
GILLock gil_lock;
|
||||||
try {
|
try {
|
||||||
boost::python::numpy::ndarray sy =
|
update_doppler_information(_physicalLayer.attr("get_doppler")
|
||||||
boost::python::numpy::from_data(&_descrambled_symbols.front(),
|
(complex_vector_to_ndarray(_descrambled_symbols),
|
||||||
boost::python::numpy::dtype::get_builtin<gr_complex>(),
|
complex_vector_to_ndarray(_samples)));
|
||||||
boost::python::make_tuple(_descrambled_symbols.size()),
|
|
||||||
boost::python::make_tuple(sizeof(gr_complex)),
|
|
||||||
boost::python::object());
|
|
||||||
boost::python::numpy::ndarray sa =
|
|
||||||
boost::python::numpy::from_data(&_samples.front(),
|
|
||||||
boost::python::numpy::dtype::get_builtin<gr_complex>(),
|
|
||||||
boost::python::make_tuple(_samples.size()),
|
|
||||||
boost::python::make_tuple(sizeof(gr_complex)),
|
|
||||||
boost::python::object());
|
|
||||||
_samples.clear();
|
_samples.clear();
|
||||||
update_doppler_information(_physicalLayer.attr("get_doppler")(sy, sa));
|
|
||||||
update_frame_information(_physicalLayer.attr("get_frame")());
|
update_frame_information(_physicalLayer.attr("get_frame")());
|
||||||
} catch (boost::python::error_already_set const&) {
|
} catch (boost::python::error_already_set const&) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gr_complex known_symbol = _symbols[_symbol_counter];
|
out[nout++] = filter();
|
||||||
bool is_known = true;
|
|
||||||
filter_output = 0;
|
|
||||||
#if 1
|
|
||||||
volk_32fc_x2_dot_prod_32fc(&filter_output,
|
|
||||||
_hist_samples+_hist_sample_index,
|
|
||||||
_taps_samples,
|
|
||||||
_nB+_nF+1);
|
|
||||||
#else
|
|
||||||
for (int l=0; l<_nB+_nF+1; ++l) {
|
|
||||||
assert(_hist_sample_index+l < 2*(_nB+_nF+1));
|
|
||||||
filter_output += _hist_samples[_hist_sample_index+l]*_taps_samples[l];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
gr_complex dot_symbols=0;
|
|
||||||
for (int l=0; l<_nW; ++l) {
|
|
||||||
assert(_hist_symbol_index+l < 2*_nW);
|
|
||||||
dot_symbols += _hist_symbols[_hist_symbol_index+l]*_taps_symbols[l];
|
|
||||||
}
|
|
||||||
filter_output += dot_symbols;
|
|
||||||
if (std::abs(known_symbol) < 1e-5) { // not known
|
|
||||||
is_known = false;
|
|
||||||
gr_complex descrambled_filter_output = std::conj(_scramble[_symbol_counter]) * filter_output;
|
|
||||||
gr::digital::constellation_sptr constell = _constellations[_constellation_index];
|
|
||||||
unsigned int jc = constell->decision_maker(&descrambled_filter_output);
|
|
||||||
gr_complex descrambled_symbol = 0;
|
|
||||||
constell->map_to_points(jc, &descrambled_symbol);
|
|
||||||
|
|
||||||
// make soft decisions
|
|
||||||
float const err = std::abs(descrambled_filter_output - descrambled_symbol);
|
|
||||||
_npwr_counter[_constellation_index] += (_npwr_counter[_constellation_index] < _npwr_max_time_constant);
|
|
||||||
float const alpha = 1.0f/_npwr_counter[_constellation_index];
|
|
||||||
_npwr[_constellation_index] = (1-alpha)*_npwr[_constellation_index] + alpha*err;
|
|
||||||
std::vector<float> soft_dec = constell->calc_soft_dec(descrambled_filter_output, _npwr[_constellation_index]);
|
|
||||||
// std::cout << "soft_dec " << _npwr[_constellation_index] << " : ";
|
|
||||||
// for (int k=0; k<soft_dec.size(); ++k) {
|
|
||||||
// std::cout << soft_dec[k] << " ";
|
|
||||||
// }
|
|
||||||
// std::cout << "\n";
|
|
||||||
|
|
||||||
known_symbol = _scramble[_symbol_counter] * descrambled_symbol;
|
|
||||||
}
|
|
||||||
gr_complex err = filter_output - known_symbol;
|
|
||||||
int jMax=0;
|
|
||||||
float tMax=0;
|
|
||||||
for (int j=0; j<_nB+_nF+1; ++j) {
|
|
||||||
assert(_hist_sample_index+j < 2*(_nB+_nF+1));
|
|
||||||
_taps_samples[j] -= _mu*err*std::conj(_hist_samples[_hist_sample_index+j]);
|
|
||||||
// if (std::abs(_taps_samples[j]) > tMax) {
|
|
||||||
// tMax = std::abs(_taps_samples[j]);
|
|
||||||
// jMax = j;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
// std::cout << "taps_max: " << jMax << " " << tMax << std::endl;
|
|
||||||
for (int j=0; j<_nW; ++j) {
|
|
||||||
assert(_hist_symbol_index+j < 2*_nW);
|
|
||||||
_taps_symbols[j] -= _mu*err*std::conj(_hist_symbols[_hist_symbol_index+j]) + _alpha*_taps_symbols[j];
|
|
||||||
}
|
|
||||||
// if (_sample_counter < 80*5)
|
|
||||||
// std::cout << "filter: " << _symbol_counter << " " << _sample_counter << " " << filter_output << " " << known_symbol << " " << std::abs(err) << std::endl;
|
|
||||||
if (is_known || true) {
|
|
||||||
_hist_symbols[_hist_symbol_index] = _hist_symbols[_hist_symbol_index + _nW] = known_symbol;
|
|
||||||
if (++_hist_symbol_index == _nW)
|
|
||||||
_hist_symbol_index = 0;
|
|
||||||
}
|
|
||||||
_descrambled_symbols[_symbol_counter] = filter_output*std::conj(_scramble[_symbol_counter]);
|
|
||||||
out[nout++] = filter_output*std::conj(_scramble[_symbol_counter]);
|
|
||||||
++_symbol_counter;
|
|
||||||
}
|
}
|
||||||
|
insert_sample(in[i]);
|
||||||
|
if (_need_samples)
|
||||||
|
_samples.push_back(_hist_samples[_hist_sample_index+_nB+1]);
|
||||||
_sample_counter += 1;
|
_sample_counter += 1;
|
||||||
}
|
} // DO_FILTER
|
||||||
}
|
} // next input sample
|
||||||
|
|
||||||
consume(0, i);
|
consume(0, i);
|
||||||
|
|
||||||
|
@ -311,7 +299,7 @@ bool adaptive_dfe_impl::start()
|
||||||
try {
|
try {
|
||||||
boost::python::object module = boost::python::import(boost::python::str("digitalhf.physical_layer." + _py_module_name));
|
boost::python::object module = boost::python::import(boost::python::str("digitalhf.physical_layer." + _py_module_name));
|
||||||
boost::python::object PhysicalLayer = module.attr("PhysicalLayer");
|
boost::python::object PhysicalLayer = module.attr("PhysicalLayer");
|
||||||
_physicalLayer = PhysicalLayer();
|
_physicalLayer = PhysicalLayer(_sps);
|
||||||
update_constellations(_physicalLayer.attr("get_constellations")());
|
update_constellations(_physicalLayer.attr("get_constellations")());
|
||||||
} catch (boost::python::error_already_set const&) {
|
} catch (boost::python::error_already_set const&) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
|
@ -331,10 +319,71 @@ bool adaptive_dfe_impl::stop()
|
||||||
VOLK_SAFE_DELETE(_hist_symbols);
|
VOLK_SAFE_DELETE(_hist_symbols);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
gr_complex adaptive_dfe_impl::filter() {
|
||||||
|
gr_complex filter_output = 0;
|
||||||
|
volk_32fc_x2_dot_prod_32fc(&filter_output,
|
||||||
|
_hist_samples+_hist_sample_index,
|
||||||
|
_taps_samples,
|
||||||
|
_nB+_nF+1);
|
||||||
|
gr_complex dot_symbols=0;
|
||||||
|
for (int l=0; l<_nW; ++l) {
|
||||||
|
assert(_hist_symbol_index+l < 2*_nW);
|
||||||
|
dot_symbols += _hist_symbols[_hist_symbol_index+l]*_taps_symbols[l];
|
||||||
|
}
|
||||||
|
filter_output += dot_symbols;
|
||||||
|
gr_complex known_symbol = _symbols[_symbol_counter];
|
||||||
|
bool const is_known = std::abs(known_symbol) > 1e-5;
|
||||||
|
if (not is_known) { // not known
|
||||||
|
gr_complex const descrambled_filter_output = std::conj(_scramble[_symbol_counter]) * filter_output;
|
||||||
|
gr::digital::constellation_sptr constell = _constellations[_constellation_index];
|
||||||
|
unsigned int jc = constell->decision_maker(&descrambled_filter_output);
|
||||||
|
gr_complex descrambled_symbol = 0;
|
||||||
|
constell->map_to_points(jc, &descrambled_symbol);
|
||||||
|
|
||||||
|
// make soft decisions
|
||||||
|
float const err = std::abs(descrambled_filter_output - descrambled_symbol);
|
||||||
|
_npwr_counter[_constellation_index] += (_npwr_counter[_constellation_index] < _npwr_max_time_constant);
|
||||||
|
float const alpha = 1.0f/_npwr_counter[_constellation_index];
|
||||||
|
_npwr[_constellation_index] = (1-alpha)*_npwr[_constellation_index] + alpha*err;
|
||||||
|
std::vector<float> soft_dec = constell->calc_soft_dec(descrambled_filter_output, _npwr[_constellation_index]);
|
||||||
|
// std::cout << "soft_dec " << _npwr[_constellation_index] << " : ";
|
||||||
|
// for (int k=0; k<soft_dec.size(); ++k) {
|
||||||
|
// std::cout << soft_dec[k] << " ";
|
||||||
|
// }
|
||||||
|
// std::cout << "\n";
|
||||||
|
|
||||||
|
known_symbol = _scramble[_symbol_counter] * descrambled_symbol;
|
||||||
|
}
|
||||||
|
gr_complex err = filter_output - known_symbol;
|
||||||
|
int jMax=0;
|
||||||
|
float tMax=0;
|
||||||
|
for (int j=0; j<_nB+_nF+1; ++j) {
|
||||||
|
assert(_hist_sample_index+j < 2*(_nB+_nF+1));
|
||||||
|
_taps_samples[j] -= _mu*err*std::conj(_hist_samples[_hist_sample_index+j]);
|
||||||
|
// if (std::abs(_taps_samples[j]) > tMax) {
|
||||||
|
// tMax = std::abs(_taps_samples[j]);
|
||||||
|
// jMax = j;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// std::cout << "taps_max: " << jMax << " " << tMax << std::endl;
|
||||||
|
for (int j=0; j<_nW; ++j) {
|
||||||
|
assert(_hist_symbol_index+j < 2*_nW);
|
||||||
|
_taps_symbols[j] -= _mu*err*std::conj(_hist_symbols[_hist_symbol_index+j]) + _alpha*_taps_symbols[j];
|
||||||
|
}
|
||||||
|
// if (_sample_counter < 80*5)
|
||||||
|
// std::cout << "filter: " << _symbol_counter << " " << _sample_counter << " " << filter_output << " " << known_symbol << " " << std::abs(err) << std::endl;
|
||||||
|
if (is_known || true) {
|
||||||
|
_hist_symbols[_hist_symbol_index] = _hist_symbols[_hist_symbol_index + _nW] = known_symbol;
|
||||||
|
if (++_hist_symbol_index == _nW)
|
||||||
|
_hist_symbol_index = 0;
|
||||||
|
}
|
||||||
|
_descrambled_symbols[_symbol_counter] = filter_output*std::conj(_scramble[_symbol_counter]);
|
||||||
|
return filter_output*std::conj(_scramble[_symbol_counter++]);
|
||||||
|
}
|
||||||
|
|
||||||
void adaptive_dfe_impl::set_mode(std::string mode) {
|
void adaptive_dfe_impl::set_mode(std::string mode) {
|
||||||
gr::thread::scoped_lock lock(d_setlock);
|
gr::thread::scoped_lock lock(d_setlock);
|
||||||
std::cout << "adaptive_dfe_impl::stop()" << std::endl;
|
std::cout << "adaptive_dfe_impl::set_mode " << mode << std::endl;
|
||||||
GILLock gil_lock;
|
GILLock gil_lock;
|
||||||
try {
|
try {
|
||||||
_physicalLayer.attr("set_mode")(mode);
|
_physicalLayer.attr("set_mode")(mode);
|
||||||
|
@ -342,7 +391,6 @@ void adaptive_dfe_impl::set_mode(std::string mode) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
update_constellations(_physicalLayer.attr("get_constellations")());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void adaptive_dfe_impl::update_constellations(boost::python::object obj)
|
void adaptive_dfe_impl::update_constellations(boost::python::object obj)
|
||||||
|
@ -368,10 +416,10 @@ void adaptive_dfe_impl::update_constellations(boost::python::object obj)
|
||||||
_npwr_counter[i] = 0;
|
_npwr_counter[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void adaptive_dfe_impl::update_frame_information(boost::python::object obj)
|
bool adaptive_dfe_impl::update_frame_information(boost::python::object obj)
|
||||||
{
|
{
|
||||||
int const n = boost::python::extract<int>(obj.attr("__len__")());
|
int const n = boost::python::extract<int>(obj.attr("__len__")());
|
||||||
assert(n==2);
|
assert(n==3);
|
||||||
boost::python::numpy::ndarray array = boost::python::numpy::array(obj[0]);
|
boost::python::numpy::ndarray array = boost::python::numpy::array(obj[0]);
|
||||||
char const* data = array.get_data();
|
char const* data = array.get_data();
|
||||||
int const m = array.shape(0);
|
int const m = array.shape(0);
|
||||||
|
@ -384,9 +432,11 @@ void adaptive_dfe_impl::update_frame_information(boost::python::object obj)
|
||||||
std::memcpy(&_scramble[i], data+16*i+8, sizeof(gr_complex));
|
std::memcpy(&_scramble[i], data+16*i+8, sizeof(gr_complex));
|
||||||
// std::cout << "get_frame " << i << " " << _symbols[i] << " " << _scramble[i] << std::endl;
|
// std::cout << "get_frame " << i << " " << _symbols[i] << " " << _scramble[i] << std::endl;
|
||||||
}
|
}
|
||||||
_constellation_index = boost::python::extract<int>(obj[1]);
|
_constellation_index = boost::python::extract<int> (obj[1]);
|
||||||
|
_need_samples = boost::python::extract<bool>(obj[2]);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void adaptive_dfe_impl::update_doppler_information(boost::python::object obj)
|
bool adaptive_dfe_impl::update_doppler_information(boost::python::object obj)
|
||||||
{
|
{
|
||||||
int const n = boost::python::extract<int>(obj.attr("__len__")());
|
int const n = boost::python::extract<int>(obj.attr("__len__")());
|
||||||
assert(n==2);
|
assert(n==2);
|
||||||
|
@ -398,10 +448,11 @@ void adaptive_dfe_impl::update_doppler_information(boost::python::object obj)
|
||||||
std::fill_n(_hist_samples, 2*(_nB+_nF+1), gr_complex(0));
|
std::fill_n(_hist_samples, 2*(_nB+_nF+1), gr_complex(0));
|
||||||
_hist_sample_index = 0;
|
_hist_sample_index = 0;
|
||||||
_sample_counter = 0;
|
_sample_counter = 0;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
float const doppler = boost::python::extract<float>(obj[1]);
|
float const doppler = boost::python::extract<float>(obj[1]);
|
||||||
update_pll(doppler);
|
update_pll(doppler);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void adaptive_dfe_impl::update_pll(float doppler) {
|
void adaptive_dfe_impl::update_pll(float doppler) {
|
||||||
|
@ -422,8 +473,9 @@ void adaptive_dfe_impl::insert_sample(gr_complex z) {
|
||||||
_hist_samples[_hist_sample_index] = _hist_samples[_hist_sample_index+_nB+_nF+1] = z * gr_expj(-_phase);
|
_hist_samples[_hist_sample_index] = _hist_samples[_hist_sample_index+_nB+_nF+1] = z * gr_expj(-_phase);
|
||||||
if (++_hist_sample_index == _nB+_nF+1)
|
if (++_hist_sample_index == _nB+_nF+1)
|
||||||
_hist_sample_index = 0;
|
_hist_sample_index = 0;
|
||||||
|
update_local_oscillator();
|
||||||
// local oscillator update
|
}
|
||||||
|
void adaptive_dfe_impl:: update_local_oscillator() {
|
||||||
_phase += _df;
|
_phase += _df;
|
||||||
if (_phase > M_PI)
|
if (_phase > M_PI)
|
||||||
_phase -= 2*M_PI;
|
_phase -= 2*M_PI;
|
||||||
|
|
|
@ -63,6 +63,8 @@ private:
|
||||||
std::vector<gr_complex> _descrambled_symbols;
|
std::vector<gr_complex> _descrambled_symbols;
|
||||||
int _symbol_counter;
|
int _symbol_counter;
|
||||||
|
|
||||||
|
bool _need_samples;
|
||||||
|
|
||||||
// PLL for doppler tracking
|
// PLL for doppler tracking
|
||||||
float _df; // frequency offset in radians per sample
|
float _df; // frequency offset in radians per sample
|
||||||
float _phase; // accumulated phase for frequency correction
|
float _phase; // accumulated phase for frequency correction
|
||||||
|
@ -71,12 +73,17 @@ private:
|
||||||
|
|
||||||
enum state {
|
enum state {
|
||||||
WAIT_FOR_PREAMBLE,
|
WAIT_FOR_PREAMBLE,
|
||||||
|
INITIAL_DOPPLER_ESTIMATE,
|
||||||
|
INITIAL_DOPPLER_ESTIMATE_CONTINUE,
|
||||||
DO_FILTER
|
DO_FILTER
|
||||||
} _state;
|
} _state;
|
||||||
|
|
||||||
void update_constellations(boost::python::object obj);
|
void update_constellations(boost::python::object obj);
|
||||||
void update_frame_information(boost::python::object obj);
|
bool update_frame_information(boost::python::object obj);
|
||||||
void update_doppler_information(boost::python::object obj);
|
bool update_doppler_information(boost::python::object obj);
|
||||||
|
|
||||||
|
void update_local_oscillator();
|
||||||
|
gr_complex filter();
|
||||||
|
|
||||||
void insert_sample(gr_complex z);
|
void insert_sample(gr_complex z);
|
||||||
void update_pll(float doppler);
|
void update_pll(float doppler);
|
||||||
|
|
|
@ -6,56 +6,72 @@ from gnuradio import digital
|
||||||
class PhysicalLayer(object):
|
class PhysicalLayer(object):
|
||||||
"""Physical layer description for STANAG 4285"""
|
"""Physical layer description for STANAG 4285"""
|
||||||
|
|
||||||
def __init__(self, mode=0):
|
MODE_BPSK=0
|
||||||
"""For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK"""
|
MODE_QPSK=1
|
||||||
self._constellations = [PhysicalLayer.make_psk(2, [0,1]),
|
MODE_8PSK=2
|
||||||
PhysicalLayer.make_psk(4, [0,1,3,2]),
|
|
||||||
PhysicalLayer.make_psk(8, [1,0,2,3,6,7,5,4])]
|
def __init__(self, sps):
|
||||||
self._preamble = [PhysicalLayer.get_preamble(), 0] ## BPSK
|
"""intialization"""
|
||||||
self._data = [PhysicalLayer.get_data(), mode] ## according to the mode
|
self._sps = sps
|
||||||
self._counter = 0
|
self._mode = self.MODE_BPSK
|
||||||
self._is_first_frame = True
|
self._frame_counter = 0
|
||||||
|
self._is_first_frame = True
|
||||||
|
self._constellations = [self.make_psk(2, [0,1]),
|
||||||
|
self.make_psk(4, [0,1,3,2]),
|
||||||
|
self.make_psk(8, [1,0,2,3,6,7,5,4])]
|
||||||
|
self._preamble = self.get_preamble()
|
||||||
|
self._data = self.get_data()
|
||||||
|
|
||||||
def set_mode(self, mode):
|
def set_mode(self, mode):
|
||||||
"""For STANAG 4258 the mode has to be set manually: mode=0 -> BPSK, mode=1 -> QPSK, mode=2 -> 8PSK"""
|
"""set phase modultation type"""
|
||||||
print('set_mode', mode)
|
print('set_mode', mode)
|
||||||
self._data[1] = int(mode)
|
self._mode = int(mode)
|
||||||
|
|
||||||
def get_constellations(self):
|
def get_constellations(self):
|
||||||
return self._constellations
|
return self._constellations
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
"""returns the known+unknown symbols and scrambling"""
|
"""returns a tuple describing the frame:
|
||||||
print('-------------------- get_frame --------------------',self._counter)
|
[0] ... known+unknown symbols and scrambling
|
||||||
return self._preamble if self._counter == 0 else self._data
|
[1] ... modulation type after descrambling
|
||||||
|
[2] ... a boolean indicating whethere or not raw IQ samples needed"""
|
||||||
|
print('-------------------- get_frame --------------------', self._frame_counter)
|
||||||
|
return [self._preamble,self.MODE_BPSK,True] if self.is_preamble() else [self._data,self._mode,False]
|
||||||
|
|
||||||
def get_doppler(self, sy, sa):
|
def get_doppler(self, symbols, iq_samples):
|
||||||
"""used for doppler shift update, for determining which frame to provide next,
|
"""returns a tuple
|
||||||
and for stopping at end of data/when the signal quality is too low
|
[0] ... quality flag
|
||||||
sy ... equalized symbols; sa ... samples"""
|
[1] ... doppler estimate (rad/symbol) if available"""
|
||||||
print('-------------------- get_doppler --------------------',self._counter,len(sy),len(sa))
|
print('-------------------- get_doppler --------------------', self._frame_counter,len(symbols),len(iq_samples))
|
||||||
success,doppler = self.quality_preamble(sy,sa) if self._counter == 0 else self.quality_data(sy)
|
success,doppler = self.quality_preamble(symbols,iq_samples) if self.is_preamble() else self.quality_data(symbols)
|
||||||
self._counter = (self._counter+1)&1 if success else 0
|
if len(symbols) != 0:
|
||||||
self._is_first_frame = not success
|
self._frame_counter = (self._frame_counter+1)&1 if success else 0
|
||||||
|
self._is_first_frame = not success
|
||||||
return success,doppler
|
return success,doppler
|
||||||
|
|
||||||
def quality_preamble(self, sy, sa):
|
def is_preamble(self):
|
||||||
sps = 5
|
return self._frame_counter == 0
|
||||||
zp = [x for x in self._preamble[0]['symb'][9:40] for i in range(sps)]
|
|
||||||
cc = np.array([np.sum(sa[ i*5:(31+i)*5]*zp) for i in range(49)])
|
def quality_preamble(self, symbols, iq_samples):
|
||||||
imax = np.argmax(np.abs(cc[0:18]))
|
"""quality check and doppler estimation for preamble"""
|
||||||
pks = cc[(imax,imax+15,imax+16,imax+31),]
|
success = True
|
||||||
apks = np.abs(pks)
|
doppler = 0
|
||||||
test = np.mean(apks[(0,3),]) > 2*np.mean(apks[(1,2),])
|
if len(iq_samples) != 0:
|
||||||
doppler = np.diff(np.unwrap(np.angle(pks[(0,3),])))[0]/31 if test else 0
|
zp = [x for x in self._preamble['symb'][9:40] for i in range(self._sps)]
|
||||||
idx = range(80)
|
cc = np.array([np.sum(iq_samples[ i*5:(31+i)*5]*zp) for i in range(49)])
|
||||||
if self._is_first_frame:
|
imax = np.argmax(np.abs(cc[0:18]))
|
||||||
idx = range(30,80)
|
pks = cc[(imax,imax+15,imax+16,imax+31),]
|
||||||
z = sy[idx]*np.conj(self._preamble[0]['symb'][idx])
|
apks = np.abs(pks)
|
||||||
success = np.sum(np.real(z)<0) < 30
|
success = np.mean(apks[(0,3),]) > 2*np.mean(apks[(1,2),])
|
||||||
|
doppler = np.diff(np.unwrap(np.angle(pks[(0,3),])))[0]/31 if success else 0
|
||||||
|
if len(symbols) != 0:
|
||||||
|
idx = range(30,80) if self._is_first_frame else range(80)
|
||||||
|
z = symbols[idx]*np.conj(self._preamble['symb'][idx])
|
||||||
|
success = np.sum(np.real(z)<0) < 30
|
||||||
return success,doppler
|
return success,doppler
|
||||||
|
|
||||||
def quality_data(self, s):
|
def quality_data(self, s):
|
||||||
|
"""quality check for the data frame"""
|
||||||
known_symbols = np.mod(range(176),48)>=32
|
known_symbols = np.mod(range(176),48)>=32
|
||||||
success = np.sum(np.real(s[known_symbols])<0) < 20
|
success = np.sum(np.real(s[known_symbols])<0) < 20
|
||||||
return success,0 ## no doppler estimate for data frames
|
return success,0 ## no doppler estimate for data frames
|
||||||
|
@ -87,7 +103,7 @@ class PhysicalLayer(object):
|
||||||
for j in range(3):
|
for j in range(3):
|
||||||
state = np.concatenate(([np.sum(state&taps)&1], state[0:-1]))
|
state = np.concatenate(([np.sum(state&taps)&1], state[0:-1]))
|
||||||
a=np.zeros(176, dtype=[('symb',np.complex64), ('scramble', np.complex64)])
|
a=np.zeros(176, dtype=[('symb',np.complex64), ('scramble', np.complex64)])
|
||||||
## PSK-8 modulation
|
## 8PSK modulation
|
||||||
constellation = PhysicalLayer.make_psk(8,range(8))['points']
|
constellation = PhysicalLayer.make_psk(8,range(8))['points']
|
||||||
a['scramble'] = constellation[p,]
|
a['scramble'] = constellation[p,]
|
||||||
known_symbols = np.mod(range(176),48)>=32
|
known_symbols = np.mod(range(176),48)>=32
|
||||||
|
@ -96,11 +112,13 @@ class PhysicalLayer(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_psk(n, gray_code):
|
def make_psk(n, gray_code):
|
||||||
|
"""generates n-PSK constellation data"""
|
||||||
c = np.zeros(n, dtype=[('points', np.complex64), ('symbols', np.uint8)])
|
c = np.zeros(n, dtype=[('points', np.complex64), ('symbols', np.uint8)])
|
||||||
c['points'] = np.exp(2*np.pi*1j*np.array(range(n))/n)
|
c['points'] = np.exp(2*np.pi*1j*np.array(range(n))/n)
|
||||||
c['symbols'] = gray_code
|
c['symbols'] = gray_code
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
## for now not used (doppler estimation after adaptive filtering does not work)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def data_aided_frequency_estimation(x,c):
|
def data_aided_frequency_estimation(x,c):
|
||||||
"""Data-Aided Frequency Estimation for Burst Digital Transmission,
|
"""Data-Aided Frequency Estimation for Burst Digital Transmission,
|
||||||
|
|
Loading…
Reference in a new issue