-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP Channel Estimation #8
Changes from 11 commits
8235f53
a513161
9ef1196
5e91c93
fe70e99
a99c463
109e26c
ce6949a
c64bdfd
00a8bd6
63cc74e
3853c6b
4a7b1e9
ad2eade
c87e0d8
0760983
a8ec51c
ebf0998
cb239fa
38e9308
31946c5
feba058
5c9bed1
2057a1b
722a1fa
53830b8
643fd8b
83f80cc
2d34a8e
ec9e2ef
10f9181
80a9b32
e85e832
633aa81
1389d55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright (C) 2022 Transit Live Mapping Solutions | ||
* All rights reserved. | ||
* | ||
* Authors: | ||
* Marenz Schmidl | ||
* Tassilo Tanneberger | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <memory> | ||
#include <optional> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include <l2/lower_mac.hpp> | ||
|
||
/** | ||
* Tetra downlink decoder for PI/4-DQPSK modulation | ||
* | ||
* Following downlink burst types for Phase Modulation are supported: (See TSI | ||
* EN 300 392-2 V3.8.1 (2016-08) Table 9.2) | ||
* | ||
* normal continuous downlink burst (NDB) | ||
* | ||
* synchronization continuous downlink burst (SB) | ||
* | ||
* Follawing downlink burst types for Phase Modulation are not supported: | ||
* discontinuous downlink burst (NDB) | ||
* synchronization discontinuous downlink burst (SB) | ||
* | ||
* Uplink burst types are not supported: | ||
* control uplink burst (CB) | ||
* normal uplink burst (NUB) | ||
*/ | ||
class BitStreamDecoder { | ||
public: | ||
BitStreamDecoder(std::shared_ptr<LowerMac> lower_mac, bool is_uplink) | ||
: lower_mac_(lower_mac) | ||
, is_uplink_(is_uplink){}; | ||
~BitStreamDecoder() = default; | ||
|
||
/** | ||
* @brief Process a received symbol. | ||
* | ||
* This function is called by "physical layer" when a bit is ready | ||
* to be processed. | ||
* | ||
* Note that "frame" is actually called "burst" in Tetra doc | ||
* | ||
* @return true if frame (burst) found, false otherwise | ||
* | ||
*/ | ||
void process_bit(uint8_t symbol) noexcept; | ||
|
||
private: | ||
std::shared_ptr<LowerMac> lower_mac_{}; | ||
|
||
bool is_synchronized_ = false; | ||
bool is_uplink_{}; | ||
std::size_t sync_bit_counter_ = 0; | ||
|
||
const std::size_t kFRAME_LEN = 510; | ||
|
||
std::vector<uint8_t> frame_{}; | ||
|
||
// 9.4.4.3.2 Normal training sequence | ||
const std::vector<uint8_t> kNORMAL_TRAINING_SEQ_1 = {1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, | ||
0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0}; // n1..n22 | ||
const std::vector<uint8_t> kNORMAL_TRAINING_SEQ_2 = {0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, | ||
0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0}; // p1..p22 | ||
const std::vector<uint8_t> kNORMAL_TRAINING_SEQ_3_BEGIN = {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1}; // q11..q22 | ||
const std::vector<uint8_t> kNORMAL_TRAINING_SEQ_3_END = {1, 0, 1, 1, 0, 1, 1, 1, 0, 0}; // q1..q10 | ||
|
||
// 9.4.4.3.3 Extended training sequence | ||
const std::vector<uint8_t> kEXTENDED_TRAINING_SEQ = {1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, | ||
0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1}; // x1..x30 | ||
|
||
// 9.4.4.3.4 Synchronisation training sequence | ||
const std::vector<uint8_t> kSYNC_TRAINING_SEQ = {1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, | ||
1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1}; // y1..y38 | ||
|
||
/** | ||
* @brief Reset the synchronizer | ||
* | ||
* Burst was matched, we can reset the synchronizer to allow 50 missing frames | ||
* (expressed in burst units = 50 * 510 bits) | ||
* | ||
*/ | ||
void reset_synchronizer() noexcept; | ||
|
||
/** | ||
* @brief Process frame to decide which type of burst it is then service lower | ||
* MAC | ||
* | ||
*/ | ||
void process_downlink_frame() noexcept; | ||
|
||
/** | ||
* @brief Return pattern/data comparison errors count at position in data | ||
* vector | ||
* | ||
* @param data Vector to look in from pattern | ||
* @param pattern Pattern to search | ||
* @param position Position in vector to start search | ||
* | ||
* @return Score based on similarity with pattern (differences count between | ||
* vector and pattern) | ||
* | ||
*/ | ||
static auto pattern_at_position_score(const std::vector<uint8_t>& data, const std::vector<uint8_t>& pattern, | ||
std::size_t position) noexcept -> std::size_t; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#include <deque> | ||
#include <iostream> | ||
#include <queue> | ||
|
||
template <typename T, int MaxLen, typename Container = std::deque<T>> | ||
class FixedQueue : public std::queue<T, Container> { | ||
public: | ||
FixedQueue() { | ||
for (auto i = 0; i < MaxLen; i++) { | ||
T default_value{}; | ||
std::queue<T, Container>::push(default_value); | ||
} | ||
} | ||
|
||
void push(const T& value) { | ||
if (this->size() == MaxLen) { | ||
this->c.pop_front(); | ||
} | ||
std::queue<T, Container>::push(value); | ||
} | ||
|
||
void pop(const T& value) { std::logic_error("Function not implemented"); } | ||
|
||
typename Container::const_iterator cbegin() { return this->c.cbegin(); } | ||
|
||
typename Container::const_reverse_iterator crbegin() { return this->c.crbegin(); } | ||
|
||
typename Container::const_iterator cend() { return this->c.cend(); } | ||
|
||
typename Container::const_reverse_iterator crend() { return this->c.crend(); } | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright (C) 2022 Transit Live Mapping Solutions | ||
* All rights reserved. | ||
* | ||
* Authors: | ||
* Marenz Schmidl | ||
* Tassilo Tanneberger | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <complex> | ||
#include <memory> | ||
#include <thread> | ||
|
||
#include <bit_stream_decoder.hpp> | ||
#include <fixed_queue.hpp> | ||
#include <l2/lower_mac.hpp> | ||
#include <streaming_ordered_output_thread_pool_executor.hpp> | ||
|
||
/** | ||
* Tetra downlink decoder for PI/4-DQPSK modulation | ||
* | ||
* Implements Channel Estimation | ||
*/ | ||
class IQStreamDecoder { | ||
public: | ||
IQStreamDecoder(std::shared_ptr<LowerMac> lower_mac, std::shared_ptr<BitStreamDecoder> bit_stream_decoder, | ||
bool is_uplink); | ||
~IQStreamDecoder() = default; | ||
|
||
void process_complex(std::complex<float> symbol) noexcept; | ||
|
||
private: | ||
void upperMacWorker(); | ||
|
||
std::complex<float> hard_decision(std::complex<float> symbol); | ||
std::vector<uint8_t> symbols_to_bitstream(std::vector<std::complex<float>> const& stream); | ||
|
||
std::vector<std::complex<float>> convolve_valid(std::vector<std::complex<float>> const& a, | ||
std::vector<std::complex<float>> const& b); | ||
|
||
std::vector<std::complex<float>> channel_estimation(std::vector<std::complex<float>> const& stream, | ||
std::vector<std::complex<float>> const& pilots); | ||
|
||
FixedQueue<std::complex<float>, 300> symbol_buffer_; | ||
FixedQueue<std::complex<float>, 300> symbol_buffer_hard_decision_; | ||
|
||
// 9.4.4.3.2 Normal training sequence | ||
const std::vector<std::complex<float>> 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<std::complex<float>> training_seq_n_reversed_conj_{}; | ||
const std::vector<std::complex<float>> 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<std::complex<float>> training_seq_p_reversed_conj_{}; | ||
// 9.4.4.3.3 Extended training sequence | ||
const std::vector<std::complex<float>> 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<std::complex<float>> training_seq_x_reversed_conj_{}; | ||
|
||
const float SEQUENCE_DETECTION_THRESHOLD = 1.5; | ||
|
||
std::shared_ptr<LowerMac> lower_mac_{}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this shared_ptr ? |
||
std::shared_ptr<BitStreamDecoder> bit_stream_decoder_{}; | ||
|
||
bool is_uplink_{}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe init the value |
||
|
||
std::shared_ptr<StreamingOrderedOutputThreadPoolExecutor<std::vector<std::function<void()>>>> threadPool_; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the fucking name :D |
||
|
||
std::thread upperMacWorkerThread_; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trailing return types.