mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 20:40:20 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			261 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|   FILE........: interldpc.c
 | |
|   AUTHOR......: David Rowe
 | |
|   DATE CREATED: April 2018
 | |
| 
 | |
|   Helper functions for interleaved LDPC 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 "codec2_ofdm.h"
 | |
| #include "mpdecode_core.h"
 | |
| #include "gp_interleaver.h"
 | |
| #include "HRA_112_112.h"
 | |
| 
 | |
| namespace FreeDV
 | |
| {
 | |
| 
 | |
| /* CRC type function, used to compare QPSK vectors when debugging */
 | |
| 
 | |
| COMP test_acc(COMP v[], int n) {
 | |
|     COMP acc = {0.0f, 0.0f};
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n; i++) {
 | |
|         acc.real += roundf(v[i].real);
 | |
|         acc.imag += roundf(v[i].imag);
 | |
|     }
 | |
| 
 | |
|     return acc;
 | |
| }
 | |
| 
 | |
| void printf_n(COMP v[], int n) {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n; i++) {
 | |
|         fprintf(stderr, "%d %10f %10f\n", i, round(v[i].real), round(v[i].imag));
 | |
|     }
 | |
| }
 | |
| 
 | |
| void set_up_hra_112_112(struct LDPC *ldpc, struct OFDM_CONFIG *config) {
 | |
|     ldpc->max_iter = HRA_112_112_MAX_ITER;
 | |
|     ldpc->dec_type = 0;
 | |
|     ldpc->q_scale_factor = 1;
 | |
|     ldpc->r_scale_factor = 1;
 | |
|     ldpc->CodeLength = HRA_112_112_CODELENGTH;
 | |
|     ldpc->NumberParityBits = HRA_112_112_NUMBERPARITYBITS;
 | |
|     ldpc->NumberRowsHcols = HRA_112_112_NUMBERROWSHCOLS;
 | |
|     ldpc->max_row_weight = HRA_112_112_MAX_ROW_WEIGHT;
 | |
|     ldpc->max_col_weight = HRA_112_112_MAX_COL_WEIGHT;
 | |
|     ldpc->H_rows = (uint16_t *) HRA_112_112_H_rows;
 | |
|     ldpc->H_cols = (uint16_t *) HRA_112_112_H_cols;
 | |
| 
 | |
|     /* provided for convenience and to match Octave vaiable names */
 | |
| 
 | |
|     ldpc->data_bits_per_frame = HRA_112_112_CODELENGTH - HRA_112_112_NUMBERPARITYBITS;
 | |
|     ldpc->coded_bits_per_frame = HRA_112_112_CODELENGTH;
 | |
|     ldpc->coded_syms_per_frame = ldpc->coded_bits_per_frame / config->bps;
 | |
| }
 | |
| 
 | |
| void ldpc_encode_frame(struct LDPC *ldpc, int codeword[], unsigned char tx_bits_char[]) {
 | |
|     unsigned char *pbits = new unsigned char[ldpc->NumberParityBits];
 | |
|     int i, j;
 | |
| 
 | |
|     encode(ldpc, tx_bits_char, pbits);
 | |
| 
 | |
|     for (i = 0; i < ldpc->data_bits_per_frame; i++) {
 | |
|         codeword[i] = tx_bits_char[i];
 | |
|     }
 | |
| 
 | |
|     for (j = 0; i < ldpc->coded_bits_per_frame; i++, j++) {
 | |
|         codeword[i] = pbits[j];
 | |
|     }
 | |
| 
 | |
|     delete[] pbits;
 | |
| }
 | |
| 
 | |
| void qpsk_modulate_frame(COMP tx_symbols[], int codeword[], int n) {
 | |
|     int s, i;
 | |
|     int dibit[2];
 | |
|     std::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 = std::real(qpsk_symb);
 | |
|         tx_symbols[i].imag = std::imag(qpsk_symb);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void interleaver_sync_state_machine(struct OFDM *ofdm,
 | |
|         struct LDPC *ldpc,
 | |
|         struct OFDM_CONFIG *config,
 | |
|         COMP codeword_symbols_de[],
 | |
|         float codeword_amps_de[],
 | |
|         float EsNo, int interleave_frames,
 | |
|         int *iter, int *parityCheckCount, int *Nerrs_coded)
 | |
| {
 | |
|     (void) config;
 | |
|     int coded_syms_per_frame = ldpc->coded_syms_per_frame;
 | |
|     int coded_bits_per_frame = ldpc->coded_bits_per_frame;
 | |
|     int data_bits_per_frame = ldpc->data_bits_per_frame;
 | |
|     float *llr = new float[coded_bits_per_frame];
 | |
|     uint8_t *out_char = new uint8_t[coded_bits_per_frame];
 | |
|     State next_sync_state_interleaver;
 | |
| 
 | |
|     next_sync_state_interleaver = ofdm->sync_state_interleaver;
 | |
| 
 | |
|     if ((ofdm->sync_state_interleaver == search) && (ofdm->frame_count >= (interleave_frames - 1))) {
 | |
|         symbols_to_llrs(llr, codeword_symbols_de, codeword_amps_de, EsNo, ofdm->mean_amp, coded_syms_per_frame);
 | |
|         iter[0] = run_ldpc_decoder(ldpc, out_char, llr, parityCheckCount);
 | |
|         Nerrs_coded[0] = data_bits_per_frame - parityCheckCount[0];
 | |
| 
 | |
|         if ((Nerrs_coded[0] == 0) || (interleave_frames == 1)) {
 | |
|             /* sucessful decode! */
 | |
|             next_sync_state_interleaver = synced;
 | |
|             ofdm->frame_count_interleaver = interleave_frames;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     ofdm->sync_state_interleaver = next_sync_state_interleaver;
 | |
|     delete[] out_char;
 | |
|     delete[] llr;
 | |
| }
 | |
| 
 | |
| /* measure uncoded (raw) bit errors over interleaver frame, note we
 | |
|    don't include txt bits as this is done after we dissassemmble the
 | |
|    frame */
 | |
| 
 | |
| int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, int Nerrs_raw[], int interleave_frames, COMP codeword_symbols_de[]) {
 | |
|     int i, j, Nerrs, Terrs;
 | |
| 
 | |
|     int coded_syms_per_frame = ldpc->coded_syms_per_frame;
 | |
|     int coded_bits_per_frame = ldpc->coded_bits_per_frame;
 | |
|     int data_bits_per_frame = ldpc->data_bits_per_frame;
 | |
|     int *rx_bits_raw = new int[coded_bits_per_frame];
 | |
| 
 | |
|     /* generate test codeword from known payload data bits */
 | |
| 
 | |
|     int *test_codeword = new int[coded_bits_per_frame];
 | |
|     uint16_t *r = new uint16_t[data_bits_per_frame];
 | |
|     uint8_t *tx_bits = new uint8_t[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;
 | |
|     }
 | |
| 
 | |
|     ldpc_encode_frame(ldpc, test_codeword, tx_bits);
 | |
| 
 | |
|     Terrs = 0;
 | |
|     for (j = 0; j < interleave_frames; j++) {
 | |
|         for (i = 0; i < coded_syms_per_frame; i++) {
 | |
|             int bits[2];
 | |
|             std::complex<float> s = std::complex<float>{codeword_symbols_de[j * coded_syms_per_frame + i].real, codeword_symbols_de[j * coded_syms_per_frame + 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++;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         Nerrs_raw[j] = Nerrs;
 | |
|         Terrs += Nerrs;
 | |
|     }
 | |
| 
 | |
|     delete[] tx_bits;
 | |
|     delete[] r;
 | |
|     delete[] test_codeword;
 | |
|     delete[] rx_bits_raw;
 | |
| 
 | |
|     return Terrs;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|    Given an array of tx_bits, LDPC encodes, interleaves, and OFDM
 | |
|    modulates.
 | |
| 
 | |
|    Note this could be refactored to save memory, e.g. for embedded
 | |
|    applications we could call ofdm_txframe on a frame by frame
 | |
|    basis
 | |
|  */
 | |
| 
 | |
| void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, std::complex<float> tx_sams[], uint8_t tx_bits[], uint8_t txt_bits[], int interleave_frames, struct OFDM_CONFIG *config) {
 | |
|     int coded_syms_per_frame = ldpc->coded_syms_per_frame;
 | |
|     int coded_bits_per_frame = ldpc->coded_bits_per_frame;
 | |
|     int data_bits_per_frame = ldpc->data_bits_per_frame;
 | |
|     int ofdm_bitsperframe = ofdm_get_bits_per_frame();
 | |
|     int *codeword = new int[coded_bits_per_frame];
 | |
|     COMP *coded_symbols = new COMP[interleave_frames * coded_syms_per_frame];
 | |
|     COMP *coded_symbols_inter = new COMP[interleave_frames * coded_syms_per_frame];
 | |
|     int Nsamperframe = ofdm_get_samples_per_frame();
 | |
|     std::complex<float> *tx_symbols = new std::complex<float>[ofdm_bitsperframe / config->bps];
 | |
|     int j;
 | |
| 
 | |
|     for (j = 0; j < interleave_frames; j++) {
 | |
|         ldpc_encode_frame(ldpc, codeword, &tx_bits[j * data_bits_per_frame]);
 | |
|         qpsk_modulate_frame(&coded_symbols[j * coded_syms_per_frame], codeword, coded_syms_per_frame);
 | |
|     }
 | |
| 
 | |
|     gp_interleave_comp(coded_symbols_inter, coded_symbols, interleave_frames * coded_syms_per_frame);
 | |
| 
 | |
|     for (j = 0; j < interleave_frames; j++) {
 | |
|         ofdm_assemble_modem_frame_symbols(tx_symbols, &coded_symbols_inter[j * coded_syms_per_frame], &txt_bits[config->txtbits * j]);
 | |
|         ofdm_txframe(ofdm, &tx_sams[j * Nsamperframe], tx_symbols);
 | |
|     }
 | |
| 
 | |
|     delete[] tx_symbols;
 | |
|     delete[] coded_symbols_inter;
 | |
|     delete[] coded_symbols;
 | |
|     delete[] codeword;
 | |
| }
 | |
| 
 | |
| } // FreeDV
 |