M17 - Include libM17 Viterbi Decoder and Misc Math;

This commit is contained in:
lwvmobile
2024-03-07 22:22:19 -05:00
parent a30512f4e4
commit 677b064e97
3 changed files with 362 additions and 3 deletions

View File

@@ -1062,6 +1062,14 @@ void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
void CNXDNConvolution_init();
//libM17 viterbi decoder
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
void viterbi_reset(void);
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
//keeping these
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,

232
src/3.c
View File

@@ -85,3 +85,235 @@ void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//Ripped from libM17
//prototypes
// uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
// uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
// void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
// uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
// void viterbi_reset(void);
// uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[244];
/**
* @brief Decode unpunctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param len Input length in bits.
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
if(len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
viterbi_reset();
size_t pos = 0;
for(size_t i = 0; i < len; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
viterbi_decode_bit(s0, s1, pos);
pos++;
}
return viterbi_chainback(out, pos, len/2);
}
/**
* @brief Decode punctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param punct Puncturing matrix.
* @param in_len Input data length.
* @param p_len Puncturing matrix length (entries).
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
if(in_len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
uint16_t umsg[244*2]; //unpunctured message
uint8_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
while(i<in_len)
{
if(punct[p])
{
umsg[u]=in[i];
i++;
}
else
{
umsg[u]=0x7FFF;
}
u++;
p++;
p%=p_len;
}
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
/**
* @brief 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 viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
{
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NUM_STATES/2; i++)
{
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
+ q_abs_diff(COST_TABLE_1[i], s1);
uint32_t m0 = prevMetrics[i] + metric;
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
viterbi_history[pos]|=(1<<i0);
currMetrics[i0] = m1;
}
else
{
viterbi_history[pos]&=~(1<<i0);
currMetrics[i0] = m0;
}
if(m2 >= m3)
{
viterbi_history[pos]|=(1<<i1);
currMetrics[i1] = m3;
}
else
{
viterbi_history[pos]&=~(1<<i1);
currMetrics[i1] = m2;
}
}
//swap
uint32_t tmp[NUM_STATES];
for(uint8_t i=0; i<NUM_STATES; i++)
{
tmp[i]=currMetrics[i];
}
for(uint8_t i=0; i<NUM_STATES; i++)
{
currMetrics[i]=prevMetrics[i];
prevMetrics[i]=tmp[i];
}
}
/**
* @brief History chainback to obtain final byte array.
*
* @param out Destination byte array for decoded data.
* @param pos Starting position for the chainback.
* @param len Length of the output in bits.
* @return Minimum Viterbi cost at the end of the decode sequence.
*/
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
{
uint8_t state = 0;
size_t bitPos = len+4;
memset(out, 0, (len-1)/8+1);
while(pos > 0)
{
bitPos--;
pos--;
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
state >>= 1;
if(bit)
{
state |= 0x80;
out[bitPos/8]|=1<<(7-(bitPos%8));
}
}
uint32_t cost = prevMetrics[0];
for(size_t i = 0; i < NUM_STATES; i++)
{
uint32_t m = prevMetrics[i];
if(m < cost) cost = m;
}
return cost;
}
/**
* @brief Reset the decoder state. No args.
*
*/
void viterbi_reset(void)
{
memset((uint8_t*)viterbi_history, 0, 2*244);
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
}
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
//original sources
//--------------------------------------------------------------------
// M17 C library - decode/viterbi.c
//
// This file contains:
// - the Viterbi decoder
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// M17 C library - math/math.c
//
// This file contains:
// - absolute difference value
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
// - soft-valued arrays to integer conversion (and vice-versa)
// - fixed-valued multiplication and division
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------

125
src/m17.c
View File

@@ -812,7 +812,7 @@ void processM17LSF(dsd_opts * opts, dsd_state * state)
} //end processM17LSF
// void processM17LSF_debug(dsd_opts * opts, dsd_state * state, uint8_t * m17_rnd_bits)
//This version is the older method, skipping the depuncturing and just doing deconv
void processM17LSF_debug(dsd_opts * opts, dsd_state * state, uint8_t * m17_depunc)
{
@@ -930,6 +930,124 @@ void processM17LSF_debug(dsd_opts * opts, dsd_state * state, uint8_t * m17_depun
} //end processM17LSF_debug
//This will be the stripped out version to switch over to the libM17 viterbi decoder
void processM17LSF_debug2(dsd_opts * opts, dsd_state * state, uint8_t * m17_rnd_bits)
{
//NOTE: Having run a full debug on all steps, I am convinced that the number of punctures
//renders the current convolutional decoder useless to fix the holes in an LSF frame
//Skipping to the deconvolution step renders the correct frame info and crc value
//We can still send the full LSF as intended over IP frame or RF without this step
//TODO: Switch to M17 Viterbi Decoder for better performance
int i, x;
uint8_t m17_int_bits[368]; //368 bits that are still interleaved
uint8_t m17_bits[368]; //368 bits that have been de-interleaved and de-scramble
uint8_t m17_depunc[488]; //488 bits after depuncturing
memset (m17_int_bits, 0, sizeof(m17_int_bits));
memset (m17_bits, 0, sizeof(m17_bits));
memset (m17_depunc, 0, sizeof(m17_depunc));
//descramble the frame
for (i = 0; i < 368; i++)
m17_int_bits[i] = (m17_rnd_bits[i] ^ m17_scramble[i]) & 1;
//deinterleave the bit array using Quadratic Permutation Polynomial
//function π(x) = (45x + 92x^2 ) mod 368
for (i = 0; i < 368; i++)
{
x = ((45*i)+(92*i*i)) % 368;
m17_bits[i] = m17_int_bits[x];
}
//P1 Depuncture
x = 0;
for (i = 0; i < 488; i++)
{
if (p1[i%61] == 1)
m17_depunc[i] = m17_bits[x++];
else m17_depunc[i] = 0;
}
//setup the convolutional decoder
uint8_t temp[488];
uint8_t s0;
uint8_t s1;
uint8_t m_data[30];
uint8_t trellis_buf[240]; //30*8 = 240
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
for (i = 0; i < 488; i++)
temp[i] = m17_depunc[i] << 1;
CNXDNConvolution_start();
for (i = 0; i < 244; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 240);
//244/8 = 30, last 4 (244-248) are trailing zeroes
for(i = 0; i < 30; i++)
{
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
}
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
memcpy (state->m17_lsf, trellis_buf, 240);
uint8_t lsf_packed[30];
memset (lsf_packed, 0, sizeof(lsf_packed));
//need to pack bytes for the sw5wwp variant of the crc (might as well, may be useful in the future)
for (i = 0; i < 30; i++)
lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8);
uint16_t crc_cmp = crc16m17(lsf_packed, 28);
uint16_t crc_ext = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[224], 16);
int crc_err = 0;
if (crc_cmp != crc_ext) crc_err = 1;
if (crc_err == 0)
M17decodeLSF(state);
if (opts->payload == 1)
{
fprintf (stderr, "\n LSF: ");
for (i = 0; i < 30; i++)
{
if (i == 15) fprintf (stderr, "\n ");
fprintf (stderr, "[%02X]", lsf_packed[i]);
}
}
//debug
// fprintf (stderr, " E-%04X; C-%04X (CRC CHK)", crc_ext, crc_cmp);
if (crc_err == 1) fprintf (stderr, " CRC ERR");
//ending linebreak
// fprintf (stderr, "\n");
} //end processM17LSF_debug2
//debug M17STR for the encoder to pass bits into
void processM17STR_debug(dsd_opts * opts, dsd_state * state, uint8_t * m17_rnd_bits)
{
@@ -1009,7 +1127,7 @@ void encodeM17RF (dsd_opts * opts, dsd_state * state, uint8_t * input, int type)
//Preamble A - 0x7777 (+3, -3, +3, -3, +3, -3, +3, -3)
uint8_t m17_preamble_a[16] = {0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1};
//Preamble B - 0xEEEE (+3, -3, +3, -3, +3, -3, +3, -3)
//Preamble B - 0xEEEE (-3, +3, -3, +3, -3, +3, -3, +3)
uint8_t m17_preamble_b[16] = {1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0};
//EOT Marker - 0x555D
@@ -1697,7 +1815,7 @@ void encodeM17STR(dsd_opts * opts, dsd_state * state)
{
//debug LSF before (see after in decoder for comparison)
// fprintf (stderr, "\n LSF: ");
// fprintf (stderr, "\n lsf: ");
// for (i = 0; i < 30; i++)
// {
// if ((i%15)==0) fprintf (stderr, "\n");
@@ -1707,6 +1825,7 @@ void encodeM17STR(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "\n M17 LSF (ENCODER): ");
if (opts->monitor_input_audio == 0)
processM17LSF_debug(opts, state, m17_lsfc);
// processM17LSF_debug(opts, state, m17_lsfs);
else fprintf (stderr, " To Audio Out Device Type: %d; ", opts->audio_out_type);
//encodeM17RF