Added implementation of soft-decision Viterbi decoder for M17 protocol, updated version number in meson.build

pull/131/head
Silvano Seva 2022-11-11 22:04:34 +01:00
rodzic ae8bba88b6
commit 49c6cbd7c0
4 zmienionych plików z 230 dodań i 10 usunięć

Wyświetl plik

@ -2,7 +2,7 @@
## OpenRTX - Modular Open Source Radio Firmware
##
project('OpenRTX', ['c', 'cpp'],
version : '0.3.3',
version : '0.3.5',
default_options : ['warning_level=3', 'b_staticpic=false'])
##

Wyświetl plik

@ -140,7 +140,7 @@ private:
M17LinkSetupFrame lsf; ///< Latest LSF received.
M17LinkSetupFrame lsfFromLich; ///< LSF assembled from LICH segments.
M17StreamFrame streamFrame; ///< Latest stream dat frame received.
M17Viterbi viterbi; ///< Viterbi decoder.
M17HardViterbi viterbi; ///< Viterbi decoder.
///< Maximum allowed hamming distance when determining the frame type.
static constexpr uint8_t MAX_SYNC_HAMM_DISTANCE = 4;

Wyświetl plik

@ -3,6 +3,7 @@
* Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *
* Wojciech Kaczmarski SP5WWP *
* *
* Adapted from original code written by Phil Karn KA9Q *
* *
@ -43,20 +44,20 @@ namespace M17
* G2 = 0x17.
*/
class M17Viterbi
class M17HardViterbi
{
public:
/**
* Constructor.
*/
M17Viterbi() : prevMetrics(&prevMetricsData), currMetrics(&currMetricsData)
M17HardViterbi() : prevMetrics(&prevMetricsData), currMetrics(&currMetricsData)
{ }
/**
* Destructor.
*/
~M17Viterbi() { }
~M17HardViterbi() { }
/**
* Decode unpunctured convolutionally encoded data.
@ -237,6 +238,225 @@ private:
std::array< std::bitset< NumStates >, 244 > history;
};
/**
* Soft decision Viterbi decoder tailored on M17 protocol specifications,
* that is for decoding of data encoded with a convolutional encoder with a
* coder rate R = 1/2, a constraint length K = 5 and polynomials G1 = 0x19 and
* G2 = 0x17.
*/
class M17SoftViterbi
{
public:
/**
* Constructor.
*/
M17SoftViterbi() : prevMetrics(&prevMetricsData), currMetrics(&currMetricsData)
{ }
/**
* Destructor.
*/
~M17SoftViterbi() { }
/**
* Decode unpunctured convolutionally encoded data.
*
* @param in: input data.
* @param out: destination array where decoded data are written.
* @return number of bit errors corrected.
*/
template < size_t IN, size_t OUT >
uint32_t decode(const std::array< uint16_t, IN >& in,
std::array< uint8_t, OUT >& out)
{
static_assert(IN < 244*2, "Input size exceeds max history");
currMetricsData.fill(0);
prevMetricsData.fill(0);
size_t pos = 0;
for (size_t i = 0; i < IN; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
decodeBit(s0, s1, pos);
pos++;
}
return chainback(out, pos);
}
/**
* Decode punctured convolutionally encoded data.
*
* @param in: input data.
* @param out: destination array where decoded data are written.
* @param punctureMatrix: puncturing matrix.
* @return number of bit errors corrected.
*/
template < size_t IN, size_t OUT, size_t P >
uint16_t decodePunctured(const std::array< uint16_t, IN >& in,
std::array< uint8_t, OUT >& out,
const std::array< uint8_t, P >& punctureMatrix)
{
static_assert(IN < 244*2, "Input size exceeds max history");
currMetricsData.fill(0);
prevMetricsData.fill(0);
size_t histPos = 0;
size_t punctIndex = 0;
size_t bitPos = 0;
uint16_t punctBitCnt = 0;
while(bitPos < IN)
{
uint16_t sym[2] = {0xFFFF, 0xFFFF};
for(uint8_t i = 0; i < 2; i++)
{
if(punctureMatrix[punctIndex++])
{
sym[i] = in[bitPos];
bitPos++;
}
else
{
sym[i] = 0x7FFF; //half range for punctured out bit
punctBitCnt++;
}
if(punctIndex >= P) punctIndex = 0;
}
decodeBit(sym[0], sym[1], histPos);
histPos++;
}
return chainback(out, histPos) - punctBitCnt;
}
private:
/**
* Decode one bit and update trellis.
*
* @param s0: cost of the first symbol.
* @param s1: cost of the second symbol.
* @param pos: bit position in history.
*/
void decodeBit(uint16_t s0, uint16_t s1, size_t pos)
{
static constexpr uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF};
static constexpr uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0,
0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NumStates/2; i++)
{
uint32_t metric = q_AbsDiff(COST_TABLE_0[i], s0)
+ q_AbsDiff(COST_TABLE_1[i], s1);
uint32_t m0 = (*prevMetrics)[i] + metric;
uint32_t m1 = (*prevMetrics)[i + NumStates/2] + (0x1FFFE - metric);
uint32_t m2 = (*prevMetrics)[i] + (0x1FFFE - metric);
uint32_t m3 = (*prevMetrics)[i + NumStates/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
history[pos].set(i0, true);
(*currMetrics)[i0] = m1;
}
else
{
history[pos].set(i0, false);
(*currMetrics)[i0] = m0;
}
if(m2 >= m3)
{
history[pos].set(i1, true);
(*currMetrics)[i1] = m3;
}
else
{
history[pos].set(i1, false);
(*currMetrics)[i1] = m2;
}
}
std::swap(currMetrics, prevMetrics);
}
/**
* History chainback to obtain final byte array.
*
* @param out: destination byte array for decoded data.
* @param pos: starting position for the chainback.
* @return minimum Viterbi cost at the end of the decode sequence.
*/
template < size_t OUT >
uint32_t chainback(std::array< uint8_t, OUT >& out, size_t pos)
{
uint8_t state = 0;
size_t bitPos = OUT*8;
while(bitPos > 0)
{
bitPos--;
pos--;
bool bit = history[pos].test(state >> 4);
state >>= 1;
if(bit) state |= 0x80;
setBit(out, bitPos, bit);
}
uint32_t cost = (*prevMetrics)[0];
for(size_t i = 0; i < NumStates; i++)
{
uint32_t m = (*prevMetrics)[i];
if(m < cost) cost = m;
}
return cost;
}
/**
* Utility function to compute the absolute value of a difference between
* two fixed-point values.
*
* @param v1: first value
* @param v2: second value
* @return abs(v1-v2)
*/
inline uint16_t q_AbsDiff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
static constexpr size_t K = 5;
static constexpr size_t NumStates = (1 << (K - 1));
std::array< uint32_t, NumStates > *prevMetrics;
std::array< uint32_t, NumStates > *currMetrics;
std::array< uint32_t, NumStates > prevMetricsData;
std::array< uint32_t, NumStates > currMetricsData;
std::array< std::bitset< NumStates >, 244 > history;
};
} // namespace M17
#endif // M17_VITERBI_H

Wyświetl plik

@ -23,10 +23,10 @@
#include <cstdint>
#include <random>
#include <array>
#include "M17/M17ConvolutionalEncoder.h"
#include "M17/M17CodePuncturing.h"
#include "M17/M17Viterbi.h"
#include "M17/M17Utils.h"
#include "M17/M17ConvolutionalEncoder.hpp"
#include "M17/M17CodePuncturing.hpp"
#include "M17/M17Viterbi.hpp"
#include "M17/M17Utils.hpp"
using namespace std;
@ -72,7 +72,7 @@ int main()
generateErrors(punctured);
array< uint8_t, 18 > result;
M17Viterbi decoder;
M17HardViterbi decoder;
decoder.decodePunctured(punctured, result, DATA_PUNCTURE);
for(size_t i = 0; i < result.size(); i++)