mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-08 17:46:03 -05:00
247 lines
8.3 KiB
C++
247 lines
8.3 KiB
C++
|
/*
|
||
|
LDPC testbench
|
||
|
|
||
|
Copyright 2018 Ahmet Inan <xdsopl@gmail.com>
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <iostream>
|
||
|
#include <iomanip>
|
||
|
#include <random>
|
||
|
#include <cmath>
|
||
|
#include <cassert>
|
||
|
#include <chrono>
|
||
|
#include <cstring>
|
||
|
#include <algorithm>
|
||
|
#include <functional>
|
||
|
#include "testbench.h"
|
||
|
#include "encoder.h"
|
||
|
#include "algorithms.h"
|
||
|
#include "interleaver.h"
|
||
|
#include "modulation.h"
|
||
|
|
||
|
#if 0
|
||
|
#include "flooding_decoder.h"
|
||
|
static const int TRIALS = 50;
|
||
|
#else
|
||
|
#include "layered_decoder.h"
|
||
|
static const int TRIALS = 25;
|
||
|
#endif
|
||
|
|
||
|
namespace ldpctool {
|
||
|
|
||
|
LDPCInterface *create_ldpc(char *standard, char prefix, int number);
|
||
|
Interleaver<code_type> *create_interleaver(char *modulation, char *standard, char prefix, int number);
|
||
|
ModulationInterface<complex_type, code_type> *create_modulation(char *name, int len);
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
if (argc != 6)
|
||
|
return -1;
|
||
|
|
||
|
typedef NormalUpdate<simd_type> update_type;
|
||
|
//typedef SelfCorrectedUpdate<simd_type> update_type;
|
||
|
|
||
|
//typedef MinSumAlgorithm<simd_type, update_type> algorithm_type;
|
||
|
typedef OffsetMinSumAlgorithm<simd_type, update_type, FACTOR> algorithm_type;
|
||
|
//typedef MinSumCAlgorithm<simd_type, update_type, FACTOR> algorithm_type;
|
||
|
//typedef LogDomainSPA<simd_type, update_type> algorithm_type;
|
||
|
//typedef LambdaMinAlgorithm<simd_type, update_type, 3> algorithm_type;
|
||
|
//typedef SumProductAlgorithm<simd_type, update_type> algorithm_type;
|
||
|
|
||
|
LDPCEncoder<code_type> encode;
|
||
|
LDPCDecoder<simd_type, algorithm_type> decode;
|
||
|
|
||
|
LDPCInterface *ldpc = create_ldpc(argv[2], argv[3][0], atoi(argv[3]+1));
|
||
|
if (!ldpc) {
|
||
|
std::cerr << "no such table!" << std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
const int CODE_LEN = ldpc->code_len();
|
||
|
const int DATA_LEN = ldpc->data_len();
|
||
|
std::cerr << "testing LDPC(" << CODE_LEN << ", " << DATA_LEN << ") code." << std::endl;
|
||
|
|
||
|
encode.init(ldpc);
|
||
|
decode.init(ldpc);
|
||
|
|
||
|
ModulationInterface<complex_type, code_type> *mod = create_modulation(argv[4], CODE_LEN);
|
||
|
if (!mod) {
|
||
|
std::cerr << "no such modulation!" << std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
const int MOD_BITS = mod->bits();
|
||
|
assert(CODE_LEN % MOD_BITS == 0);
|
||
|
const int SYMBOLS = CODE_LEN / MOD_BITS;
|
||
|
|
||
|
Interleaver<code_type> *itl = create_interleaver(argv[4], argv[2], argv[3][0], atoi(argv[3]+1));
|
||
|
assert(itl);
|
||
|
|
||
|
value_type SNR = atof(argv[1]);
|
||
|
//value_type mean_signal = 0;
|
||
|
value_type sigma_signal = 1;
|
||
|
value_type mean_noise = 0;
|
||
|
value_type sigma_noise = std::sqrt(sigma_signal * sigma_signal / (2 * std::pow(10, SNR / 10)));
|
||
|
std::cerr << SNR << " Es/N0 => AWGN with standard deviation of " << sigma_noise << " and mean " << mean_noise << std::endl;
|
||
|
|
||
|
value_type code_rate = (value_type)DATA_LEN / (value_type)CODE_LEN;
|
||
|
value_type spectral_efficiency = code_rate * MOD_BITS;
|
||
|
value_type EbN0 = 10 * std::log10(sigma_signal * sigma_signal / (spectral_efficiency * 2 * sigma_noise * sigma_noise));
|
||
|
std::cerr << EbN0 << " Eb/N0, using spectral efficiency of " << spectral_efficiency << " from " << code_rate << " code rate and " << MOD_BITS << " bits per symbol." << std::endl;
|
||
|
|
||
|
std::random_device rd;
|
||
|
std::default_random_engine generator(rd());
|
||
|
typedef std::uniform_int_distribution<int> uniform;
|
||
|
typedef std::normal_distribution<value_type> normal;
|
||
|
auto data = std::bind(uniform(0, 1), generator);
|
||
|
auto awgn = std::bind(normal(mean_noise, sigma_noise), generator);
|
||
|
|
||
|
int BLOCKS = atoi(argv[5]);
|
||
|
if (BLOCKS < 1)
|
||
|
return -1;
|
||
|
void *aligned_buffer = aligned_alloc(sizeof(simd_type), sizeof(simd_type) * CODE_LEN);
|
||
|
simd_type *simd = reinterpret_cast<simd_type *>(aligned_buffer);
|
||
|
code_type *code = new code_type[BLOCKS * CODE_LEN];
|
||
|
code_type *orig = new code_type[BLOCKS * CODE_LEN];
|
||
|
code_type *noisy = new code_type[BLOCKS * CODE_LEN];
|
||
|
complex_type *symb = new complex_type[BLOCKS * SYMBOLS];
|
||
|
|
||
|
for (int j = 0; j < BLOCKS; ++j)
|
||
|
for (int i = 0; i < DATA_LEN; ++i)
|
||
|
code[j * CODE_LEN + i] = 1 - 2 * data();
|
||
|
|
||
|
for (int j = 0; j < BLOCKS; ++j)
|
||
|
encode(code + j * CODE_LEN, code + j * CODE_LEN + DATA_LEN);
|
||
|
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
orig[i] = code[i];
|
||
|
|
||
|
for (int i = 0; i < BLOCKS; ++i)
|
||
|
itl->fwd(code + i * CODE_LEN);
|
||
|
|
||
|
for (int j = 0; j < BLOCKS; ++j)
|
||
|
mod->mapN(symb + j * SYMBOLS, code + j * CODE_LEN);
|
||
|
|
||
|
for (int i = 0; i < BLOCKS * SYMBOLS; ++i)
|
||
|
symb[i] += complex_type(awgn(), awgn());
|
||
|
|
||
|
if (1) {
|
||
|
code_type tmp[MOD_BITS];
|
||
|
value_type sp = 0, np = 0;
|
||
|
for (int i = 0; i < SYMBOLS; ++i) {
|
||
|
mod->hard(tmp, symb[i]);
|
||
|
complex_type s = mod->map(tmp);
|
||
|
complex_type e = symb[i] - s;
|
||
|
sp += std::norm(s);
|
||
|
np += std::norm(e);
|
||
|
}
|
||
|
value_type snr = 10 * std::log10(sp / np);
|
||
|
sigma_signal = std::sqrt(sp / SYMBOLS);
|
||
|
sigma_noise = std::sqrt(np / (2 * sp));
|
||
|
std::cerr << snr << " Es/N0, stddev " << sigma_noise << " of noise and " << sigma_signal << " of signal estimated via hard decision." << std::endl;
|
||
|
}
|
||
|
|
||
|
// $LLR=log(\frac{p(x=+1|y)}{p(x=-1|y)})$
|
||
|
// $p(x|\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}$
|
||
|
value_type precision = FACTOR / (sigma_noise * sigma_noise);
|
||
|
for (int j = 0; j < BLOCKS; ++j)
|
||
|
mod->softN(code + j * CODE_LEN, symb + j * SYMBOLS, precision);
|
||
|
|
||
|
for (int i = 0; i < BLOCKS; ++i)
|
||
|
itl->bwd(code + i * CODE_LEN);
|
||
|
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
noisy[i] = code[i];
|
||
|
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
assert(!std::isnan(code[i]));
|
||
|
|
||
|
int iterations = 0;
|
||
|
int num_decodes = 0;
|
||
|
auto start = std::chrono::system_clock::now();
|
||
|
for (int j = 0; j < BLOCKS; j += SIMD_WIDTH) {
|
||
|
int blocks = j + SIMD_WIDTH > BLOCKS ? BLOCKS - j : SIMD_WIDTH;
|
||
|
for (int n = 0; n < blocks; ++n)
|
||
|
for (int i = 0; i < CODE_LEN; ++i)
|
||
|
reinterpret_cast<code_type *>(simd+i)[n] = code[(j+n)*CODE_LEN+i];
|
||
|
int trials = TRIALS;
|
||
|
int count = decode(simd, simd + DATA_LEN, trials, blocks);
|
||
|
++num_decodes;
|
||
|
for (int n = 0; n < blocks; ++n)
|
||
|
for (int i = 0; i < CODE_LEN; ++i)
|
||
|
code[(j+n)*CODE_LEN+i] = reinterpret_cast<code_type *>(simd+i)[n];
|
||
|
if (count < 0) {
|
||
|
iterations += blocks * trials;
|
||
|
std::cerr << "decoder failed at converging to a code word!" << std::endl;
|
||
|
} else {
|
||
|
iterations += blocks * (trials - count);
|
||
|
std::cerr << trials - count << " iterations were needed." << std::endl;
|
||
|
}
|
||
|
}
|
||
|
auto end = std::chrono::system_clock::now();
|
||
|
auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||
|
int kbs = (BLOCKS * DATA_LEN + msec.count() / 2) / msec.count();
|
||
|
std::cerr << kbs << " kilobit per second." << std::endl;
|
||
|
float avg_iter = (float)iterations / (float)BLOCKS;
|
||
|
std::cerr << avg_iter << " average iterations per block." << std::endl;
|
||
|
float avg_msec = (float)msec.count() / (float)num_decodes;
|
||
|
std::cerr << avg_msec << " average milliseconds per decode." << std::endl;
|
||
|
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
assert(!std::isnan(code[i]));
|
||
|
|
||
|
int awgn_errors = 0;
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
awgn_errors += noisy[i] * orig[i] < 0;
|
||
|
int quantization_erasures = 0;
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
quantization_erasures += !noisy[i];
|
||
|
int uncorrected_errors = 0;
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
uncorrected_errors += code[i] * orig[i] <= 0;
|
||
|
int decoder_errors = 0;
|
||
|
for (int i = 0; i < BLOCKS * CODE_LEN; ++i)
|
||
|
decoder_errors += code[i] * orig[i] <= 0 && orig[i] * noisy[i] > 0;
|
||
|
float bit_error_rate = (float)uncorrected_errors / (float)(BLOCKS * CODE_LEN);
|
||
|
|
||
|
if (1) {
|
||
|
for (int i = 0; i < CODE_LEN; ++i)
|
||
|
code[i] = code[i] < 0 ? -1 : 1;
|
||
|
itl->fwd(code);
|
||
|
value_type sp = 0, np = 0;
|
||
|
for (int i = 0; i < SYMBOLS; ++i) {
|
||
|
complex_type s = mod->map(code + i * MOD_BITS);
|
||
|
complex_type e = symb[i] - s;
|
||
|
sp += std::norm(s);
|
||
|
np += std::norm(e);
|
||
|
}
|
||
|
value_type snr = 10 * std::log10(sp / np);
|
||
|
sigma_signal = std::sqrt(sp / SYMBOLS);
|
||
|
sigma_noise = std::sqrt(np / (2 * sp));
|
||
|
std::cerr << snr << " Es/N0, stddev " << sigma_noise << " of noise and " << sigma_signal << " of signal estimated from corrected symbols." << std::endl;
|
||
|
}
|
||
|
|
||
|
std::cerr << awgn_errors << " errors caused by AWGN." << std::endl;
|
||
|
std::cerr << quantization_erasures << " erasures caused by quantization." << std::endl;
|
||
|
std::cerr << decoder_errors << " errors caused by decoder." << std::endl;
|
||
|
std::cerr << uncorrected_errors << " errors uncorrected." << std::endl;
|
||
|
std::cerr << bit_error_rate << " bit error rate." << std::endl;
|
||
|
|
||
|
if (0) {
|
||
|
std::cout << SNR << " " << bit_error_rate << " " << avg_iter << " " << EbN0 << std::endl;
|
||
|
}
|
||
|
|
||
|
delete ldpc;
|
||
|
delete mod;
|
||
|
delete itl;
|
||
|
|
||
|
free(aligned_buffer);
|
||
|
delete[] code;
|
||
|
delete[] orig;
|
||
|
delete[] noisy;
|
||
|
delete[] symb;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
} // namespace ldpctool
|