From fe70e99133f2b6d238dfb11671de30bb8c9a1d39 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 4 Jul 2023 00:15:45 +0200 Subject: [PATCH] implement first version of synchronization of bursts. very unperformant, but a start --- include/fixed_queue.hpp | 8 +- include/iq_stream_decoder.hpp | 14 +++- src/decoder.cpp | 3 +- src/iq_stream_decoder.cpp | 146 ++++++++++++++++++++++++---------- 4 files changed, 122 insertions(+), 49 deletions(-) diff --git a/include/fixed_queue.hpp b/include/fixed_queue.hpp index 3f30ce2..9557f15 100644 --- a/include/fixed_queue.hpp +++ b/include/fixed_queue.hpp @@ -21,11 +21,11 @@ class FixedQueue : public std::queue { void pop(const T& value) { std::logic_error("Function not implemented"); } - typename Container::iterator begin() { return this->c.begin(); } + typename Container::const_iterator cbegin() { return this->c.cbegin(); } - typename Container::reverse_iterator rbegin() { return this->c.rbegin(); } + typename Container::const_reverse_iterator crbegin() { return this->c.crbegin(); } - typename Container::iterator end() { return this->c.end(); } + typename Container::const_iterator cend() { return this->c.cend(); } - typename Container::reverse_iterator rend() { return this->c.rend(); } + typename Container::const_reverse_iterator crend() { return this->c.crend(); } }; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index fc8ce57..b018cca 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -13,6 +13,7 @@ #include #include +#include /** * Tetra downlink decoder for PI/4-DQPSK modulation @@ -21,35 +22,42 @@ */ class IQStreamDecoder { public: - IQStreamDecoder(std::shared_ptr bit_stream_decoder, bool is_uplink) - : bit_stream_decoder_(bit_stream_decoder) - , is_uplink_(is_uplink){}; + IQStreamDecoder(std::shared_ptr lower_mac, std::shared_ptr bit_stream_decoder, + bool is_uplink); ~IQStreamDecoder() = default; void process_complex(std::complex symbol) noexcept; private: std::complex hard_decision(std::complex symbol); + std::vector symbols_to_bitstream(std::vector> const& stream); std::vector> convolve_valid(std::vector> const& a, std::vector> const& b); + std::vector> channel_estimation(std::vector> const& stream, + std::vector> const& pilots); + FixedQueue, 300> symbol_buffer_; FixedQueue, 300> symbol_buffer_hard_decision_; // 9.4.4.3.2 Normal training sequence const std::vector> training_seq_n_ = {{-1, -1}, {-1, 1}, {1, 1}, {1, 1}, {-1, -1}, {1, -1}, {1, -1}, {-1, 1}, {-1, -1}, {-1, 1}, {1, 1}}; + std::vector> training_seq_n_reversed_conj_{}; const std::vector> training_seq_p_ = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, {1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}}; + std::vector> training_seq_p_reversed_conj_{}; // 9.4.4.3.3 Extended training sequence const std::vector> training_seq_x_ = { {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}}; + std::vector> training_seq_x_reversed_conj_{}; const float SEQUENCE_DETECTION_THRESHOLD = 1.5; + std::shared_ptr lower_mac_{}; std::shared_ptr bit_stream_decoder_{}; bool is_uplink_{}; diff --git a/src/decoder.cpp b/src/decoder.cpp index 34feff7..fd64699 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -33,7 +33,8 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op lower_mac_ = std::make_shared(reporter_); bit_stream_decoder_ = std::make_shared(lower_mac_, uplink_scrambling_code_.has_value()); - iq_stream_decoder_ = std::make_unique(bit_stream_decoder_, uplink_scrambling_code_.has_value()); + iq_stream_decoder_ = + std::make_unique(lower_mac_, bit_stream_decoder_, uplink_scrambling_code_.has_value()); if (uplink_scrambling_code_.has_value()) { // set scrambling_code for uplink diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 6d8d2f5..b2ad306 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -7,8 +7,23 @@ * Tassilo Tanneberger */ +#include + #include +IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, + std::shared_ptr bit_stream_decoder, bool is_uplink) + : lower_mac_(lower_mac) + , bit_stream_decoder_(bit_stream_decoder) + , is_uplink_(is_uplink) { + std::transform(training_seq_n_.crbegin(), training_seq_n_.crend(), + std::back_inserter(training_seq_n_reversed_conj_), [](auto v) { return std::conj(v); }); + std::transform(training_seq_p_.crbegin(), training_seq_p_.crend(), + std::back_inserter(training_seq_p_reversed_conj_), [](auto v) { return std::conj(v); }); + std::transform(training_seq_x_.crbegin(), training_seq_x_.crend(), + std::back_inserter(training_seq_x_reversed_conj_), [](auto v) { return std::conj(v); }); +} + std::complex IQStreamDecoder::hard_decision(std::complex symbol) { if (symbol.real() > 0) { if (symbol.imag() > 0) { @@ -25,6 +40,40 @@ std::complex IQStreamDecoder::hard_decision(std::complex symbol) { } } +std::vector IQStreamDecoder::symbols_to_bitstream(std::vector> const& stream) { + std::vector bits; + + for (auto it = stream.begin(); it != stream.end(); it++) { + + auto real = it->real(); + auto imag = it->imag(); + + if (real > 0.0) { + if (imag > 0.0) { + // I + bits.push_back(0); + bits.push_back(0); + } else { + // IV + bits.push_back(1); + bits.push_back(0); + } + } else { + if (imag > 0.0) { + // II + bits.push_back(0); + bits.push_back(1); + } else { + // III + bits.push_back(1); + bits.push_back(1); + } + } + } + + return bits; +} + std::vector> IQStreamDecoder::convolve_valid(std::vector> const& a, std::vector> const& b) { std::vector> res; @@ -48,6 +97,12 @@ std::vector> IQStreamDecoder::convolve_valid(std::vector> IQStreamDecoder::channel_estimation(std::vector> const& stream, + std::vector> const& pilots) { + // TODO: implement channel estimation + return stream; +} + void IQStreamDecoder::process_complex(std::complex symbol) noexcept { if (is_uplink_) { // Control Uplink Burst or Normal Uplink Burst @@ -56,55 +111,64 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { // convolve hard_decision with flipped conjugate of n, p and x and check if abs > SEQUENCE_DETECTION_THRESHOLD // to find potential correlation peaks - auto find_n = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_n_); - auto find_p = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_p_); - auto find_x = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_x_); - + // + // find NUB + // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 + auto start_n = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_n, 109); + auto end_n = start_n; + std::advance(end_n, 11); + auto find_n = convolve_valid({start_n, end_n}, training_seq_n_reversed_conj_)[0]; + // find NUB_Split + // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 + auto start_p = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_p, 109); + auto end_p = start_p; + std::advance(end_p, 11); + auto find_p = convolve_valid({start_p, end_p}, training_seq_p_reversed_conj_)[0]; // find CUB // 2 tail + 42 coded symbols + middle of 15 training sequence (8) = 52 - if (std::abs(find_x[51]) > SEQUENCE_DETECTION_THRESHOLD) { - // potentially found CUB - std::cout << "Potential CUB found" << std::endl; + auto start_x = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_x, 44); + auto end_x = start_x; + std::advance(end_x, 15); + auto find_x = convolve_valid({start_x, end_x}, training_seq_x_reversed_conj_)[0]; + + auto start = symbol_buffer_.cbegin(); + + if (std::abs(find_x) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential CUB found" << std::endl; + + auto end = start; + std::advance(end, 103); + + auto corrected = channel_estimation({start, end}, training_seq_x_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::ControlUplinkBurst); } - // find NUB - // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 - if (std::abs(find_p[115]) > SEQUENCE_DETECTION_THRESHOLD) { - std::cout << "Potential NUB_Split found" << std::endl; + if (std::abs(find_p) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential NUB_Split found" << std::endl; + + auto end = start; + std::advance(end, 231); + + auto corrected = channel_estimation({start, end}, training_seq_p_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurstSplit); } - if (std::abs(find_n[115]) > SEQUENCE_DETECTION_THRESHOLD && - std::abs(find_x[115]) <= SEQUENCE_DETECTION_THRESHOLD) { - std::cout << "Potential NUB found" << std::endl; + if (std::abs(find_n) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential NUB found" << std::endl; + + auto end = start; + std::advance(end, 231); + + auto corrected = channel_estimation({start, end}, training_seq_n_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurst); } } else { - // Simple hard decission symbol mapper for now... - auto real = symbol.real(); - auto imag = symbol.imag(); - - if (real > 0.0) { - if (imag > 0.0) { - // I - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(0); - } else { - // IV - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(0); - } - } else { - if (imag > 0.0) { - // II - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(1); - } else { - // III - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(1); - } + auto bits = symbols_to_bitstream({symbol}); + for (auto it = bits.begin(); it != bits.end(); ++it) { + bit_stream_decoder_->process_bit(*it); } } }