kopia lustrzana https://github.com/mobilinkd/NucleoTNC
Use same Viterbi code as m17-cxx-demod.
rodzic
c8767e7eb5
commit
5173378320
72
TNC/Util.h
72
TNC/Util.h
|
@ -30,19 +30,47 @@ constexpr std::bitset<sizeof...(Is)> make_bitset(std::index_sequence<Is...>, Tup
|
|||
return result;
|
||||
}
|
||||
|
||||
template<typename FloatType, size_t LLR>
|
||||
constexpr std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, (((1 << (LLR - 1)) - 1) * 6 ) + 1> make_llr_map()
|
||||
/**
|
||||
* This is the max value for the LLR based on size N.
|
||||
*/
|
||||
template <size_t N>
|
||||
constexpr size_t llr_limit()
|
||||
{
|
||||
constexpr size_t size = (((1 << (LLR - 1)) - 1) * 6 ) + 1;
|
||||
return (1 << (N - 1)) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* There are (2^(N-1)-1) elements (E) per segment (e.g. N=4, E=7; N=3, E=3).
|
||||
* These contain the LLR values 1..E. There are 6 segments in the LLR map:
|
||||
* 1. (-Inf,-2]
|
||||
* 2. (-2, -1]
|
||||
* 3. (-1, 0]
|
||||
* 4. (0, 1]
|
||||
* 5. (1, 2]
|
||||
* 6. (2, Inf)
|
||||
*
|
||||
* Note the slight asymmetry. This is OK as we are dealing with floats and
|
||||
* it only matters to an epsilon of the float type.
|
||||
*/
|
||||
template <size_t N>
|
||||
constexpr size_t llr_size()
|
||||
{
|
||||
return llr_limit<N>() * 6 + 1;
|
||||
}
|
||||
|
||||
template<typename FloatType, size_t LLR>
|
||||
constexpr std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, llr_size<LLR>()> make_llr_map()
|
||||
{
|
||||
constexpr size_t size = llr_size<LLR>();
|
||||
std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, size> result;
|
||||
|
||||
constexpr int8_t limit = (1 << (LLR - 1)) - 1;
|
||||
constexpr int8_t limit = llr_limit<LLR>();
|
||||
constexpr FloatType inc = 1.0 / FloatType(limit);
|
||||
int8_t i = limit;
|
||||
int8_t j = limit;
|
||||
|
||||
// Output must be ordered by k, ascending.
|
||||
FloatType k = -3.0;
|
||||
FloatType k = -3.0 + inc;
|
||||
for (size_t index = 0; index != size; ++index)
|
||||
{
|
||||
auto& a = result[index];
|
||||
|
@ -50,19 +78,22 @@ constexpr std::array<std::tuple<FloatType, std::tuple<int8_t, int8_t>>, (((1 <<
|
|||
std::get<0>(std::get<1>(a)) = i;
|
||||
std::get<1>(std::get<1>(a)) = j;
|
||||
|
||||
if (k + 1.0 < inc / -2.0)
|
||||
if (k + 1.0 < 0)
|
||||
{
|
||||
j--;
|
||||
if (j == 0) j = -1;
|
||||
if (j < -limit) j = -limit;
|
||||
}
|
||||
else if (k - 1.0 < inc / -2.0)
|
||||
else if (k - 1.0 < 0)
|
||||
{
|
||||
i--;
|
||||
if (i == 0) i = -1;
|
||||
if (i < -limit) i = -limit;
|
||||
}
|
||||
else
|
||||
{
|
||||
j++;
|
||||
if (j == 0) j = 1;
|
||||
if (j > limit) j = limit;
|
||||
}
|
||||
k += inc;
|
||||
|
@ -96,8 +127,10 @@ template <typename FloatType, size_t LLR>
|
|||
auto llr(FloatType sample)
|
||||
{
|
||||
static constexpr auto symbol_map = detail::make_llr_map<FloatType, LLR>();
|
||||
static constexpr FloatType MAX_VALUE = 3.0;
|
||||
static constexpr FloatType MIN_VALUE = -3.0;
|
||||
|
||||
FloatType s = std::min(3.0, std::max(-3.0, sample));
|
||||
FloatType s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample));
|
||||
|
||||
auto it = std::lower_bound(symbol_map.begin(), symbol_map.end(), s,
|
||||
[](std::tuple<FloatType, std::tuple<int8_t, int8_t>> const& e, FloatType s){
|
||||
|
@ -179,6 +212,7 @@ template <size_t N>
|
|||
constexpr bool get_bit_index(const std::array<uint8_t, N>& input, size_t index)
|
||||
{
|
||||
auto byte_index = index >> 3;
|
||||
assert(byte_index < N);
|
||||
auto bit_index = 7 - (index & 7);
|
||||
|
||||
return (input[byte_index] & (1 << bit_index)) >> bit_index;
|
||||
|
@ -188,6 +222,7 @@ template <size_t N>
|
|||
void set_bit_index(std::array<uint8_t, N>& input, size_t index)
|
||||
{
|
||||
auto byte_index = index >> 3;
|
||||
assert(byte_index < N);
|
||||
auto bit_index = 7 - (index & 7);
|
||||
input[byte_index] |= (1 << bit_index);
|
||||
}
|
||||
|
@ -196,6 +231,7 @@ template <size_t N>
|
|||
void reset_bit_index(std::array<uint8_t, N>& input, size_t index)
|
||||
{
|
||||
auto byte_index = index >> 3;
|
||||
assert(byte_index < N);
|
||||
auto bit_index = 7 - (index & 7);
|
||||
input[byte_index] &= ~(1 << bit_index);
|
||||
}
|
||||
|
@ -259,4 +295,24 @@ constexpr auto to_byte_array(std::array<T, N> in)
|
|||
return out;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr void to_byte_array(std::array<T, N> in, std::array<uint8_t, (N + 7) / 8>& out)
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t b = 0;
|
||||
uint8_t tmp = 0;
|
||||
for (auto c : in)
|
||||
{
|
||||
tmp |= (c << (7 - b));
|
||||
if (++b == 8)
|
||||
{
|
||||
out[i] = tmp;
|
||||
tmp = 0;
|
||||
++i;
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
if (i < out.size()) out[i] = tmp;
|
||||
}
|
||||
|
||||
} // mobilinkd
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
#include "Util.h"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
|
||||
namespace mobilinkd
|
||||
{
|
||||
|
@ -111,7 +114,10 @@ struct Viterbi
|
|||
|
||||
metrics_t prevMetrics, currMetrics;
|
||||
|
||||
std::array<std::bitset<NumStates>, 244> history_storage_;
|
||||
// This is the maximum amount of storage needed for M17. If used for
|
||||
// other modes, this may need to be increased. This will never overflow
|
||||
// because of a static assertion in the decode() function.
|
||||
std::array<std::bitset<NumStates>, 244> history_;
|
||||
|
||||
Viterbi(Trellis_ trellis)
|
||||
: cost_(makeCost<Trellis_, LLR_>(trellis))
|
||||
|
@ -119,7 +125,6 @@ struct Viterbi
|
|||
, prevState_(makePrevState(trellis))
|
||||
{}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void calculate_path_metric(
|
||||
const std::array<int16_t, NumStates / 2>& cost0,
|
||||
const std::array<int16_t, NumStates / 2>& cost1,
|
||||
|
@ -129,8 +134,8 @@ struct Viterbi
|
|||
auto& i0 = nextState_[j][0];
|
||||
auto& i1 = nextState_[j][1];
|
||||
|
||||
int16_t c0 = cost0[j];
|
||||
int16_t c1 = cost1[j];
|
||||
auto& c0 = cost0[j];
|
||||
auto& c1 = cost1[j];
|
||||
|
||||
auto& p0 = prevMetrics[j];
|
||||
auto& p1 = prevMetrics[j + NumStates / 2];
|
||||
|
@ -157,14 +162,15 @@ struct Viterbi
|
|||
template <size_t IN, size_t OUT>
|
||||
size_t decode(std::array<int8_t, IN> const& in, std::array<uint8_t, OUT>& out)
|
||||
{
|
||||
static_assert(sizeof(history_storage_) >= IN / 2);
|
||||
static_assert(sizeof(history_) >= IN / 2);
|
||||
|
||||
constexpr auto MAX_METRIC = std::numeric_limits<typename metrics_t::value_type>::max() / 2;
|
||||
|
||||
prevMetrics.fill(MAX_METRIC);
|
||||
prevMetrics[0] = 0; // Starting point.
|
||||
|
||||
std::span history(history_storage_.begin(), history_storage_.begin() + IN / 2);
|
||||
auto hbegin = history_.begin();
|
||||
auto hend = history_.begin() + IN / 2;
|
||||
|
||||
constexpr size_t BUTTERFLY_SIZE = NumStates / 2;
|
||||
|
||||
|
@ -172,31 +178,39 @@ struct Viterbi
|
|||
std::array<int16_t, BUTTERFLY_SIZE> cost0;
|
||||
std::array<int16_t, BUTTERFLY_SIZE> cost1;
|
||||
|
||||
for (size_t i = 0; i != IN; i += 2)
|
||||
for (size_t i = 0; i != IN; i += 2, hindex += 1)
|
||||
{
|
||||
auto& hist = history[hindex];
|
||||
int16_t s0 = in[i];
|
||||
int16_t s1 = in[i + 1];
|
||||
cost0.fill(0);
|
||||
cost1.fill(0);
|
||||
|
||||
for (size_t j = 0; j != BUTTERFLY_SIZE; ++j)
|
||||
{
|
||||
int16_t c = std::abs(cost_[j][0] - s0) + std::abs(cost_[j][1] - s1);
|
||||
cost0[j] = c;
|
||||
cost1[j] = METRIC - c;
|
||||
if (s0) // is not erased
|
||||
{
|
||||
cost0[j] = std::abs(cost_[j][0] - s0);
|
||||
cost1[j] = std::abs(cost_[j][0] + s0);
|
||||
}
|
||||
if (s1) // is not erased
|
||||
{
|
||||
cost0[j] += std::abs(cost_[j][1] - s1);
|
||||
cost1[j] += std::abs(cost_[j][1] + s1);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j != BUTTERFLY_SIZE; ++j)
|
||||
{
|
||||
calculate_path_metric(cost0, cost1, hist, j);
|
||||
calculate_path_metric(cost0, cost1, history_[hindex], j);
|
||||
}
|
||||
std::swap(currMetrics, prevMetrics);
|
||||
hindex += 1;
|
||||
}
|
||||
|
||||
// Find starting point. Should be 0 for properly flushed CCs.
|
||||
// However, 0 may not be the path with the least errors.
|
||||
// However, 0 may not be the path with the fewest errors.
|
||||
size_t min_element = 0;
|
||||
int32_t min_cost = prevMetrics[0];
|
||||
|
||||
for (size_t i = 0; i != NumStates; ++i)
|
||||
{
|
||||
if (prevMetrics[i] < min_cost)
|
||||
|
@ -206,21 +220,22 @@ struct Viterbi
|
|||
}
|
||||
}
|
||||
|
||||
size_t ber = min_cost / (METRIC >> 1); // Cost is at least equal to # of erasures.
|
||||
size_t cost = std::round(min_cost / float(detail::llr_limit<LLR_>()));
|
||||
|
||||
// Do chainback.
|
||||
auto oit = std::rbegin(out);
|
||||
auto hit = std::rbegin(history);
|
||||
auto hit = std::make_reverse_iterator(hend); // rbegin
|
||||
auto hrend = std::make_reverse_iterator(hbegin); // rend
|
||||
size_t next_element = min_element;
|
||||
size_t index = IN / 2;
|
||||
while (oit != std::rend(out) && hit != std::rend(history))
|
||||
while (oit != std::rend(out) && hit != hrend)
|
||||
{
|
||||
auto v = (*hit++)[next_element];
|
||||
if (index-- <= OUT) *oit++ = next_element & 1;
|
||||
next_element = prevState_[next_element][v];
|
||||
}
|
||||
|
||||
return ber;
|
||||
return cost;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue