diff options
| author | Author Name <[email protected]> | 2023-07-07 12:20:59 +0930 |
|---|---|---|
| committer | David Rowe <[email protected]> | 2023-07-07 12:29:06 +0930 |
| commit | ac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (patch) | |
| tree | a2d0ace57a9c0e2e5b611c4987f6fed1b38b81e7 /src/interldpc.c | |
shallow zip-file copy from codec2 e9d726bf20
Diffstat (limited to 'src/interldpc.c')
| -rw-r--r-- | src/interldpc.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/interldpc.c b/src/interldpc.c new file mode 100644 index 0000000..b17e9b7 --- /dev/null +++ b/src/interldpc.c @@ -0,0 +1,317 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: interldpc.c + AUTHOR......: David Rowe + DATE CREATED: April 2018 + + Helper functions for LDPC-based waveforms. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2018 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. This program is + distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +#include "interldpc.h" +#include "ofdm_internal.h" +#include "mpdecode_core.h" +#include "gp_interleaver.h" + +void freedv_pack(unsigned char *bytes, unsigned char *bits, int nbits); +void freedv_unpack(unsigned char *bits, unsigned char *bytes, int nbits); +unsigned short freedv_crc16_unpacked(unsigned char *bits, int nbits); + +void set_up_ldpc_constants(struct LDPC *ldpc, int code_length, int parity_bits) { + /* following provided for convenience and to match Octave variable names */ + + /* these remain fixed */ + ldpc->ldpc_data_bits_per_frame = code_length - parity_bits; + ldpc->ldpc_coded_bits_per_frame = code_length; + + /* in the case there are some unused data bits, these may be + modified to be less that ldpc->ldpc_xxx versions above. We + place known bits in the unused data bit positions, which make + the code stronger, and allow us to mess with different speech + codec bit allocations without designing new LDPC codes. */ + + ldpc->data_bits_per_frame = ldpc->ldpc_data_bits_per_frame; + ldpc->coded_bits_per_frame = ldpc->ldpc_coded_bits_per_frame; + ldpc->protection_mode = LDPC_PROT_2020; +} + +void set_data_bits_per_frame(struct LDPC *ldpc, int new_data_bits_per_frame) { + ldpc->data_bits_per_frame = new_data_bits_per_frame; + ldpc->coded_bits_per_frame = ldpc->data_bits_per_frame + ldpc->NumberParityBits; +} + +/* 1' stuffing (code rate reduction) - tweak LDPC code setup for selected modes */ +void ldpc_mode_specific_setup(struct OFDM *ofdm, struct LDPC *ldpc) { + /* mode specific set up */ + if (!strcmp(ofdm->mode,"2020")) set_data_bits_per_frame(ldpc, 312); + if (!strcmp(ofdm->mode,"2020B")) { + set_data_bits_per_frame(ldpc, 156); + ldpc->protection_mode = LDPC_PROT_2020B; + } + if (!strcmp(ofdm->mode,"2020C")) set_data_bits_per_frame(ldpc, 156); + if (!strcmp(ofdm->mode,"datac4")) set_data_bits_per_frame(ldpc, 448); + if (!strcmp(ofdm->mode,"datac13")) set_data_bits_per_frame(ldpc, 128); +} + +/* LDPC encode frame - generate parity bits and a codeword, applying the selected + FEC protection scheme */ +void ldpc_encode_frame(struct LDPC *ldpc, int codeword[], unsigned char tx_bits_char[]) { + unsigned char pbits[ldpc->NumberParityBits]; + int codec_frame; + int i, j; + + unsigned char tx_bits_char_padded[ldpc->ldpc_data_bits_per_frame]; + + switch (ldpc->protection_mode) { + case LDPC_PROT_EQUAL: + assert(ldpc->data_bits_per_frame == ldpc->ldpc_data_bits_per_frame); + /* we have enough data bits to fill the codeword */ + encode(ldpc, tx_bits_char, pbits); + break; + + case LDPC_PROT_2020: + /* not all data bits in codeword used, so set them to known values */ + memcpy(tx_bits_char_padded, tx_bits_char, ldpc->data_bits_per_frame); + for (i = ldpc->data_bits_per_frame; i < ldpc->ldpc_data_bits_per_frame; i++) + tx_bits_char_padded[i] = 1; + encode(ldpc, tx_bits_char_padded, pbits); + break; + + case LDPC_PROT_2020B: + /* We only want to protect the stage 1 VQ data bits, 0..10 in + each 52 bit codec frame. There are 3 codec frames 3x52=156 + bits, and 56 parity bits. We only use 11*3 = 33 bits of + the LDPC codeword data bits, the rest are set to known + values. + */ + for(j=0,codec_frame=0; codec_frame<3; codec_frame++) + for(i=0; i<11; i++,j++) + tx_bits_char_padded[j] = tx_bits_char[codec_frame*52+i]; + assert(j == 33); + for (i = 33; i < ldpc->ldpc_data_bits_per_frame; i++) + tx_bits_char_padded[i] = 1; + encode(ldpc, tx_bits_char_padded, pbits); + + break; + + default: + assert(0); + } + + /* output codeword is concatenation of (used) data bits and parity + bits, we don't bother sending unused (known) data bits */ + for (i = 0; i < ldpc->data_bits_per_frame; i++) codeword[i] = tx_bits_char[i]; + for (j = 0; j < ldpc->NumberParityBits; i++, j++) codeword[i] = pbits[j]; +} + +void qpsk_modulate_frame(COMP tx_symbols[], int codeword[], int n) { + int s, i; + int dibit[2]; + complex float qpsk_symb; + + for (s = 0, i = 0; i < n; s += 2, i++) { + dibit[0] = codeword[s + 1] & 0x1; + dibit[1] = codeword[s] & 0x1; + qpsk_symb = qpsk_mod(dibit); + tx_symbols[i].real = crealf(qpsk_symb); + tx_symbols[i].imag = cimagf(qpsk_symb); + } +} + +/* run LDPC decoder, taking into account the FEC protection scheme */ +void ldpc_decode_frame(struct LDPC *ldpc, int *parityCheckCount, int *iter, uint8_t out_char[], float llr[]) { + float llr_full_codeword[ldpc->ldpc_coded_bits_per_frame]; + int unused_data_bits = ldpc->ldpc_data_bits_per_frame - ldpc->data_bits_per_frame; + uint8_t out_char_ldpc[ldpc->coded_bits_per_frame]; + int i,j; + int codec_frame; + + switch (ldpc->protection_mode) { + case LDPC_PROT_EQUAL: + /* Equal protection all data bits in codeword + (e.g. 700D/700E), works well with rate 0.5 codes */ + assert(ldpc->data_bits_per_frame == ldpc->ldpc_data_bits_per_frame); + *iter = run_ldpc_decoder(ldpc, out_char, llr, parityCheckCount); + break; + case LDPC_PROT_2020: + /* some data bits in codeword unused, effectively + decreasing code rate and making FEC more powerful + (without having to design a new code) */ + for (i = 0; i < ldpc->data_bits_per_frame; i++) + llr_full_codeword[i] = llr[i]; + // known bits ... so really likely + for (i = ldpc->data_bits_per_frame; i < ldpc->ldpc_data_bits_per_frame; i++) + llr_full_codeword[i] = -100.0f; + // parity bits at end + for (i = ldpc->ldpc_data_bits_per_frame; i < ldpc->ldpc_coded_bits_per_frame; i++) + llr_full_codeword[i] = llr[i - unused_data_bits]; + *iter = run_ldpc_decoder(ldpc, out_char, llr_full_codeword, parityCheckCount); + break; + case LDPC_PROT_2020B: + /* 2020B waveform, with unequal error protection. Only the + stage1 VQ index of each LPCNet vocoder frames is + protected. In this case the FEC codeword is much smaller + than the payload data. */ + + // set up LDPC codeword + for(j=0,codec_frame=0; codec_frame<3; codec_frame++) + for(i=0; i<11; i++,j++) + llr_full_codeword[j] = llr[codec_frame*52+i]; + // set known LDPC codeword data bits + for (i = 33; i < ldpc->ldpc_data_bits_per_frame; i++) + llr_full_codeword[i] = -100; + // parity bits at end + for (i=0; i<ldpc->NumberParityBits; i++) + llr_full_codeword[ldpc->ldpc_data_bits_per_frame+i] = llr[ldpc->data_bits_per_frame+i]; + *iter = run_ldpc_decoder(ldpc, out_char_ldpc, llr_full_codeword, parityCheckCount); + + // pass through received data bits, replacing only decoded bits + for (i = 0; i < ldpc->data_bits_per_frame; i++) { + out_char[i] = llr[i] < 0; + } + for(j=0,codec_frame=0; codec_frame<3; codec_frame++) + for(i=0; i<11; i++,j++) + out_char[codec_frame*52+i] = out_char_ldpc[j]; + + break; + default: + assert(0); + } +} + + +/* Count uncoded (raw) bit errors over frame, note we don't include UW + of txt bits as this is done after we dissassemmble the frame */ + +int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, COMP codeword_symbols_de[], int crc16) { + int i, Nerrs; + + int coded_syms_per_frame = ldpc->coded_bits_per_frame/config->bps; + int coded_bits_per_frame = ldpc->coded_bits_per_frame; + int data_bits_per_frame = ldpc->data_bits_per_frame; + int rx_bits_raw[coded_bits_per_frame]; + + /* generate test codeword from known payload data bits */ + + int test_codeword[coded_bits_per_frame]; + uint16_t r[data_bits_per_frame]; + uint8_t tx_bits[data_bits_per_frame]; + + ofdm_rand(r, data_bits_per_frame); + + for (i = 0; i < data_bits_per_frame; i++) { + tx_bits[i] = r[i] > 16384; + } + if (crc16) { + uint16_t tx_crc16 = freedv_crc16_unpacked(tx_bits, data_bits_per_frame - 16); + uint8_t tx_crc16_bytes[] = { tx_crc16 >> 8, tx_crc16 & 0xff }; + freedv_unpack(tx_bits + data_bits_per_frame - 16, tx_crc16_bytes, 16); + } + ldpc_encode_frame(ldpc, test_codeword, tx_bits); + + for (i = 0; i < coded_syms_per_frame; i++) { + int bits[2]; + complex float s = codeword_symbols_de[i].real + I * codeword_symbols_de[i].imag; + qpsk_demod(s, bits); + rx_bits_raw[config->bps * i] = bits[1]; + rx_bits_raw[config->bps * i + 1] = bits[0]; + } + + Nerrs = 0; + + for (i = 0; i < coded_bits_per_frame; i++) { + if (test_codeword[i] != rx_bits_raw[i]) Nerrs++; + } + + return Nerrs; +} + +int count_errors(uint8_t tx_bits[], uint8_t rx_bits[], int n) { + int i; + int Nerrs = 0; + + for (i = 0; i < n; i++) + if (tx_bits[i] != rx_bits[i]) Nerrs++; + + return Nerrs; +} + + +/* for unequal protection modes, count coded errors only in those bits that have been protected */ +void count_errors_protection_mode(int protection_mode, int *pNerrs, int *pNcoded, uint8_t tx_bits[], + uint8_t rx_bits[], int n) { + int i; + int Nerrs = 0; + int Ncoded = 0; + + switch (protection_mode) { + case LDPC_PROT_EQUAL: + case LDPC_PROT_2020: + for (i = 0; i < n; i++) { + if (tx_bits[i] != rx_bits[i]) Nerrs++; + Ncoded++; + } + break; + case LDPC_PROT_2020B: + /* We only protect bits 0..10 in each 52 bit LPCNet codec + frame. There are 3 codec frames 3x52=156 data bits, of + which only 11*3 = 33 bits are protected. + */ + for(int codec_frame=0; codec_frame<3; codec_frame++) { + for(i=0; i<11; i++) { + if (tx_bits[codec_frame*52+i] != rx_bits[codec_frame*52+i]) Nerrs++; + Ncoded++; + } + } + break; + default: + assert(0); + } + + *pNerrs = Nerrs; + *pNcoded = Ncoded; +} + +/* + Given an array of tx_bits, LDPC encodes, interleaves, and OFDM modulates + */ + +void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, complex float tx_sams[], uint8_t tx_bits[], uint8_t txt_bits[]) { + int Npayloadsymsperpacket = ldpc->coded_bits_per_frame/ofdm->bps; + int Npayloadbitsperpacket = ldpc->coded_bits_per_frame; + int Nbitsperpacket = ofdm_get_bits_per_packet(ofdm); + int codeword[Npayloadbitsperpacket]; + COMP payload_symbols[Npayloadsymsperpacket]; + COMP payload_symbols_inter[Npayloadsymsperpacket]; + complex float tx_symbols[Nbitsperpacket/ ofdm->bps]; + + ldpc_encode_frame(ldpc, codeword, tx_bits); + qpsk_modulate_frame(payload_symbols, codeword, Npayloadsymsperpacket); + gp_interleave_comp(payload_symbols_inter, payload_symbols, Npayloadsymsperpacket); + ofdm_assemble_qpsk_modem_packet_symbols(ofdm, tx_symbols, payload_symbols_inter, txt_bits); + ofdm_txframe(ofdm, tx_sams, tx_symbols); +} |
