mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -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
 |