From 677b064e9756367b62def958f1c7490ea2b2b3a1 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Thu, 7 Mar 2024 22:22:19 -0500 Subject: [PATCH] M17 - Include libM17 Viterbi Decoder and Misc Math; --- include/dsd.h | 8 ++ src/3.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/m17.c | 125 ++++++++++++++++++++++++++- 3 files changed, 362 insertions(+), 3 deletions(-) diff --git a/include/dsd.h b/include/dsd.h index 9ae1c48..d938fb2 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -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, diff --git a/src/3.c b/src/3.c index bb2351a..a4d05fe 100644 --- a/src/3.c +++ b/src/3.c @@ -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= m1) + { + viterbi_history[pos]|=(1<= m3) + { + viterbi_history[pos]|=(1< 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 +//-------------------------------------------------------------------- \ No newline at end of file diff --git a/src/m17.c b/src/m17.c index 0e17ffc..b8d8ac7 100644 --- a/src/m17.c +++ b/src/m17.c @@ -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