From 6f67ed44d86b671199a8220bb12f1613d9f22bc6 Mon Sep 17 00:00:00 2001 From: Patrick Willner Date: Tue, 10 Nov 2020 19:52:26 +0100 Subject: [PATCH] Add Optional Encoding for Simulation - enabled if suitable generator matrix is provided - the simulator encodes a random information word and compares the decoded word to the initial codeword --- src/core/ldpc.cpp | 21 ++++++----- src/core/ldpc.h | 1 + src/core/sparse.h | 1 + src/decoding/decoder.h | 3 ++ src/sim/channel.cpp | 79 ++++++++++++++++++++++++------------------ src/sim/channel.h | 70 +++++++++++++++++++++++++++++++++---- src/sim/ldpcsim.cpp | 10 ++++-- src/sim_cpu.cpp | 7 ++-- 8 files changed, 140 insertions(+), 52 deletions(-) diff --git a/src/core/ldpc.cpp b/src/core/ldpc.cpp index 9c00436..f4702fe 100644 --- a/src/core/ldpc.cpp +++ b/src/core/ldpc.cpp @@ -5,7 +5,9 @@ namespace ldpc { ldpc_code::ldpc_code(const std::string &pcFileName) - : mMaxDC(0) + : mMaxDC(0), + mH(), + mG() { try { @@ -21,14 +23,17 @@ namespace ldpc ldpc_code::ldpc_code(const std::string &pcFileName, const std::string &genFileName) : ldpc_code(pcFileName) { - try + if (!genFileName.empty()) { - read_G(genFileName); - } - catch (std::exception &e) - { - std::cout << "Error: ldpc_code(): " << e.what() << std::endl; - exit(EXIT_FAILURE); + try + { + read_G(genFileName); + } + catch (std::exception &e) + { + std::cout << "Error: ldpc_code(): " << e.what() << std::endl; + exit(EXIT_FAILURE); + } } } diff --git a/src/core/ldpc.h b/src/core/ldpc.h index d3e2ba3..83116c3 100644 --- a/src/core/ldpc.h +++ b/src/core/ldpc.h @@ -70,6 +70,7 @@ namespace ldpc const vec_u64 &shorten() const { return mShorten; }; // Maximum check node degree u64 max_dc() const { return mMaxDC; }; + // Index position of transmitted bits const vec_u64 &bit_pos() const { return mBitPos; } // Parity-check matrix const sparse_csr &H() const { return mH; } diff --git a/src/core/sparse.h b/src/core/sparse.h index 65939cd..8f9311b 100644 --- a/src/core/sparse.h +++ b/src/core/sparse.h @@ -52,6 +52,7 @@ namespace ldpc const std::vector> &col_neighbor() const { return colN; } const std::vector> &row_neighbor() const { return rowN; } const std::vector> &nz_entry() const { return nonZeroVals; } + bool empty() const { return ((numCols == 0) && (numRows == 0)); } private: int numCols; // number of columns diff --git a/src/decoding/decoder.h b/src/decoding/decoder.h index 74db5a0..8539f54 100644 --- a/src/decoding/decoder.h +++ b/src/decoding/decoder.h @@ -36,6 +36,9 @@ namespace ldpc const vec_double_t &lc2v() const { return mLc2v; } const vec_double_t &llr_out() const { return mLLROut; } + // The current estimated codeword + const vec_bits_t &estimate() const { return mCO; } + static inline constexpr int sign(const double x); static inline constexpr double jacobian(const double x, const double y); static inline constexpr double minsum(const double x, const double y); diff --git a/src/sim/channel.cpp b/src/sim/channel.cpp index 8fcc633..6437ad0 100644 --- a/src/sim/channel.cpp +++ b/src/sim/channel.cpp @@ -5,12 +5,15 @@ namespace ldpc channel::channel(const std::shared_ptr &code, std::shared_ptr decoder, const u64 seed) : mLdpcCode(code), mLdpcDecoder(decoder), - mRNGSeed(seed), - mRNGEngine(std::mt19937_64(seed)) + mRNG(seed), + mRandInfoWord(std::bind(std::bernoulli_distribution(0.5), std::mt19937_64(seed << 1))), + mInfoWord(vec_bits_t(code->kc(), 0)), + mCodeWord(vec_bits_t(code->nc(), 0)) { } void channel::set_channel_param(const double channelParam) {} + void channel::encode_and_map() {} void channel::simulate() {} void channel::calculate_llrs() {} @@ -20,38 +23,43 @@ namespace ldpc mY(vec_double_t(code->nct())), mSNR(snr), mSigma2(pow(10, -snr / 10)), - mRandNormal(std::normal_distribution(0., sqrt(mSigma2))) + mRandNormal(std::bind(std::normal_distribution(0., sqrt(mSigma2)), mRNG)) { } - /** - * @brief Update the noise variance and reinitialize the RNG. - * - * @param channelParam SNR defined as E_s / \sigma^2, E_s = 1. - */ void channel_awgn::set_channel_param(const double channelParam) { mSNR = channelParam; mSigma2 = pow(10, -mSNR / 10); - mRandNormal = std::normal_distribution(0., sqrt(mSigma2)); + mRandNormal = std::bind(std::normal_distribution(0., sqrt(mSigma2)), mRNG); + } + + void channel_awgn::encode_and_map() + { + for (auto &u : mInfoWord) + { + u = mRandInfoWord(); + } + + mLdpcCode->G().multiply_left(mInfoWord, mCodeWord); + + // only select transmitted codeword bits + for (int i = 0; i < mLdpcCode->nct(); ++i) + { + // 0 --> +1 + // 1 --> -1 + mX[i] = 1 - (2 * mCodeWord[mLdpcCode->bit_pos()[i]].value); + } } - /** - * @brief Simulate the binary-input (biAWGN) channel with noise variance \sigma^2. - * - */ void channel_awgn::simulate() { for (int i = 0; i < mLdpcCode->nct(); ++i) { - mY[i] = mRandNormal(mRNGEngine) + mX[i]; // x \in {-1,+1} + mY[i] = mRandNormal() + mX[i]; // x \in {-1,+1} } } - /** - * @brief Calculate the LLR values for the biAWGN channel. - * - */ void channel_awgn::calculate_llrs() { //puncturing & shortening @@ -82,37 +90,40 @@ namespace ldpc mX(vec_bits_t(code->nct(), 0)), // initialize to all zero cw mY(vec_bits_t(code->nct())), mEpsilon(epsilon), - mRandBernoulli(std::bernoulli_distribution(epsilon)) + mRandBernoulli(std::bind(std::bernoulli_distribution(epsilon), mRNG)) { } - /** - * @brief Update the crossover probability and reinitialize the RNG. - * - * @param channelParam \espilon < 0.5 - */ + void channel_bsc::encode_and_map() + { + for (auto &u : mInfoWord) + { + u = mRandInfoWord(); + } + + mLdpcCode->G().multiply_left(mInfoWord, mCodeWord); + + // only select transmitted codeword bits + for (int i = 0; i < mLdpcCode->nct(); ++i) + { + mX[i] = mCodeWord[mLdpcCode->bit_pos()[i]]; + } + } + void channel_bsc::set_channel_param(const double channelParam) { mEpsilon = channelParam; - mRandBernoulli = std::bernoulli_distribution(mEpsilon); + mRandBernoulli = std::bind(std::bernoulli_distribution(mEpsilon), mRNG); } - /** - * @brief Simulate the Binary Symmetric Channel (BSC) with crossover probability \epsilon. - * - */ void channel_bsc::simulate() { for (int i = 0; i < mLdpcCode->nct(); ++i) { - mY[i] = mRandBernoulli(mRNGEngine); // generate a 1 with probability epsilon + mY[i] = mX[i] + mRandBernoulli(); // flip bit with probability epsilon } } - /** - * @brief Calculate the LLR values for the BSC. - * - */ void channel_bsc::calculate_llrs() { const double delta = log((1 - mEpsilon) / mEpsilon); diff --git a/src/sim/channel.h b/src/sim/channel.h index c824086..30f0dd6 100644 --- a/src/sim/channel.h +++ b/src/sim/channel.h @@ -14,18 +14,32 @@ namespace ldpc // set param and update rng stream virtual void set_channel_param(const double channelParam); + virtual void encode_and_map(); virtual void simulate(); virtual void calculate_llrs(); + + // Current transmitted codeword + const vec_bits_t &codeword() const { return mCodeWord; } + + // Current encoded information word + const vec_bits_t &infoword() const { return mInfoWord; } + protected: // ptr to const ldpc_code for parameters std::shared_ptr mLdpcCode; - // hold the unique decoder for this channel + // Holds the unique decoder for this channel std::shared_ptr mLdpcDecoder; - // rng params - const u64 mRNGSeed; - std::mt19937_64 mRNGEngine; + // RNG engine + std::mt19937_64 mRNG; + + // RNG for encoding information word + std::function mRandInfoWord; + + // Information word + vec_bits_t mInfoWord; + vec_bits_t mCodeWord; }; class channel_awgn : public channel @@ -35,10 +49,32 @@ namespace ldpc channel_awgn(const std::shared_ptr &code, std::shared_ptr decoder, const u64 seed, const double snr); virtual ~channel_awgn() = default; + /** + * @brief Update the noise variance and reinitialize the RNG. + * + * @param channelParam SNR defined as E_s / sigma^2, E_s = 1. + */ void set_channel_param(const double channelParam) override; + /** + * @brief Encodes an randomly chosen information word and maps + * it to the channel input. + * + */ + void encode_and_map() override; + + /** + * @brief Simulate the binary-input (biAWGN) channel with noise variance sigma^2. + * + */ void simulate() override; + + /** + * @brief Calculate the LLR values for the biAWGN channel. + * + */ void calculate_llrs() override; + private: //channel i/o vec_double_t mX; @@ -48,7 +84,7 @@ namespace ldpc double mSNR; double mSigma2; - std::normal_distribution mRandNormal; + std::function mRandNormal; }; class channel_bsc : public channel @@ -58,10 +94,32 @@ namespace ldpc channel_bsc(const std::shared_ptr &code, std::shared_ptr decoder, const u64 seed, const double epsilon); virtual ~channel_bsc() = default; + /** + * @brief Update the crossover probability and reinitialize the RNG. + * + * @param channelParam espilon < 0.5 + */ void set_channel_param(const double channelParam) override; + /** + * @brief Encodes an randomly chosen information word and maps + * it to the channel input. + * + */ + void encode_and_map() override; + + /** + * @brief Simulate the Binary Symmetric Channel (BSC) with crossover probability \epsilon. + * + */ void simulate() override; + + /** + * @brief Calculate the LLR values for the BSC. + * + */ void calculate_llrs() override; + private: //channel i/o vec_bits_t mX; @@ -70,6 +128,6 @@ namespace ldpc // crossover probability double mEpsilon; - std::bernoulli_distribution mRandBernoulli; + std::function mRandBernoulli; }; } // namespace ldpc diff --git a/src/sim/ldpcsim.cpp b/src/sim/ldpcsim.cpp index 20fd0d4..51cc21f 100644 --- a/src/sim/ldpcsim.cpp +++ b/src/sim/ldpcsim.cpp @@ -157,6 +157,11 @@ namespace ldpc do { + if (!mLdpcCode->G().empty()) + { + mChannel[tid]->encode_and_map(); + } + // calculate the channel transitions mChannel[tid]->simulate(); @@ -171,10 +176,11 @@ namespace ldpc #pragma omp atomic update ++frames; + // count the bit errors bec_tmp = 0; - for (int j = 0; j < mLdpcCode->nc(); ++j) + for (auto ci : mLdpcCode->bit_pos()) { - bec_tmp += (mLdpcDecoder[tid]->llr_out()[j] <= 0); + bec_tmp += (mLdpcDecoder[tid]->estimate()[ci] != mChannel[tid]->codeword()[ci]); } if (bec_tmp > 0) diff --git a/src/sim_cpu.cpp b/src/sim_cpu.cpp index 7d3428b..abc4592 100644 --- a/src/sim_cpu.cpp +++ b/src/sim_cpu.cpp @@ -9,6 +9,8 @@ int main(int argc, char *argv[]) parser.add_argument("output-file").help("Results output file."); parser.add_argument("snr-range").help("{MIN} {MAX} {STEP}").nargs(3).action([](const std::string &s) { return std::stod(s); }); + parser.add_argument("-G", "--gen-matrix").help("Generator matrix file, compressed sparse row (CSR) format.").default_value(std::string("")); + parser.add_argument("-i", "--num-iterations").help("Number of iterations for decoding. (Default: 50)").default_value(unsigned(50)).action([](const std::string &s) { return static_cast(std::stoul(s)); }); parser.add_argument("-s", "--seed").help("RNG seed. (Default: 0)").default_value(ldpc::u64(0)).action([](const std::string &s) { return std::stoul(s); }); parser.add_argument("-t", "--num-threads").help("Number of frames to be decoded in parallel. (Default: 1)").default_value(unsigned(1)).action([](const std::string &s) { return static_cast(std::stoul(s)); }); @@ -26,9 +28,10 @@ int main(int argc, char *argv[]) auto snr = parser.get("snr-range"); if (snr[0] > snr[1]) throw std::runtime_error("snr min > snr max"); - std::shared_ptr code(new ldpc::ldpc_code(parser.get("codefile"))); + auto code = std::make_shared(parser.get("codefile"), parser.get("-G")); std::cout << "========================================================================================" << std::endl; - std::cout << "codefile: " << parser.get("codefile") << std::endl; + std::cout << "Parity-Check Matrix: " << parser.get("codefile") << std::endl; + std::cout << "Generator Matrix: " << parser.get("-G") << std::endl; std::cout << *code << std::endl; std::cout << "========================================================================================" << std::endl;