diff --git a/bose_chaudhuri_hocquenghem_encoder.hh b/bose_chaudhuri_hocquenghem_encoder.hh index fba9422..282f02a 100644 --- a/bose_chaudhuri_hocquenghem_encoder.hh +++ b/bose_chaudhuri_hocquenghem_encoder.hh @@ -11,16 +11,78 @@ Copyright 2018 Ahmet Inan namespace CODE { -template +template class BoseChaudhuriHocquenghemEncoder { +public: + static const int N = LEN, K = MSG, NP = N - K; + static const int G = ((NP+1)+7)/8; + uint8_t generator[G]; + BoseChaudhuriHocquenghemEncoder(std::initializer_list minimal_polynomials) + { + // $generator(x) = \prod_i(minpoly_i(x))$ + int generator_degree = 1; + generator[0] = 1; + for (int i = 1; i < G; ++i) + generator[i] = 0; + for (auto m: minimal_polynomials) { + int m_degree = 0; + while (m>>m_degree) + ++m_degree; + --m_degree; + assert(generator_degree + m_degree <= NP + 1); + for (int i = generator_degree; i >= 0; --i) { + if (!((generator[i/8]>>(i%8))&1)) + continue; + generator[i/8] &= ~(1<<(i%8)); + generator[i/8] |= (m&1)<<(i%8); + for (int j = 1; j <= m_degree; ++j) + generator[(i+j)/8] ^= ((m>>j)&1)<<((i+j)%8); + } + generator_degree += m_degree; + } + assert(generator_degree == NP + 1); + if (0) { + std::cerr << "generator ="; + for (int i = 0; i <= NP; ++i) + std::cerr << " " << int((generator[i/8] >> (i%8))&1); + std::cerr << std::endl; + } + } + void operator()(uint8_t *code) + { + // $code = data * x^{NP} + (data * x^{NP}) \mod{generator}$ + for (int i = 0; i < NP; ++i) + code[(K+i)/8] &= ~(128>>((K+i)%8)); + for (int i = 0; i < K; ++i) { + if (((code[i/8]>>(7-i%8))&1) != ((code[K/8]>>(7-K%8))&1)) { + for (int j = 1; j < NP; ++j) { + code[(K+j-1)/8] &= ~(128>>((K+j-1)%8)); + code[(K+j-1)/8] |= (((generator[(NP-j)/8]>>((NP-j)%8))^(code[(K+j)/8]>>(7-(K+j)%8)))&1)<<(7-(K+j-1)%8); + } + code[(N-1)/8] &= ~(128>>((N-1)%8)); + code[(N-1)/8] |= (generator[0]&1)<<(7-(N-1)%8); + } else { + for (int j = 1; j < NP; ++j) { + code[(K+j-1)/8] &= ~(128>>((K+j-1)%8)); + code[(K+j-1)/8] |= ((code[(K+j)/8]>>(7-(K+j)%8))&1)<<(7-(K+j-1)%8); + } + code[(N-1)/8] &= ~(128>>((N-1)%8)); + } + } + } +}; + +template +class BoseChaudhuriHocquenghemEncoderReference +{ public: typedef typename GF::value_type value_type; typedef typename GF::ValueType ValueType; typedef typename GF::IndexType IndexType; static const int N = GF::N, K = MSG, NP = N - K; ValueType generator[NP+1]; - BoseChaudhuriHocquenghemEncoder(std::initializer_list minimal_polynomials) + BoseChaudhuriHocquenghemEncoderReference(std::initializer_list minimal_polynomials) { // $generator(x) = \prod_i(minpoly_i(x))$ int generator_degree = 1; diff --git a/tests/bch_encoder_test.cc b/tests/bch_encoder_test.cc index e3bd83c..a6ea9ce 100644 --- a/tests/bch_encoder_test.cc +++ b/tests/bch_encoder_test.cc @@ -12,9 +12,43 @@ Copyright 2018 Ahmet Inan int main() { if (1) { + // NASA INTRO BCH(15, 5) T=3 + typedef CODE::BoseChaudhuriHocquenghemEncoder<15, 5> BCH; + const int L = (BCH::N + 7) / 8; + BCH encode({0b10011, 0b11111, 0b00111}); + uint8_t code[L] = { 0b11001000 }; + uint8_t target[L] = { 0b11001000, 0b11110100 }; + encode(code); + for (int i = 0; i < L; ++i) + assert(code[i] == target[i]); + } + if (1) { + // DVB-S2 FULL BCH(65535, 65343) T=12 + typedef CODE::BoseChaudhuriHocquenghemEncoder<65535, 65343> BCH; + const int L = (BCH::N + 7) / 8; + BCH *encode = new BCH({0b10000000000101101, 0b10000000101110011, 0b10000111110111101, 0b10101101001010101, 0b10001111100101111, 0b11111011110110101, 0b11010111101100101, 0b10111001101100111, 0b10000111010100001, 0b10111010110100111, 0b10011101000101101, 0b10001101011100011}); + uint8_t *target = new uint8_t[L]; + for (int i = 0; i < L; ++i) + target[i] = 0; + for (int i = 0, s = 0; i < BCH::K; ++i, s=(s*(s*s*51767+71287)+35149)&0xffffff) + target[i/8] |= ((s^=s>>7)&1) << (7-i%8); + uint8_t *code = new uint8_t[L]; + for (int i = 0; i < L; ++i) + code[i] = target[i]; + bool parity[BCH::NP] = { 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0 }; + for (int i = 0; i < BCH::NP; ++i) + target[(BCH::K+i)/8] |= parity[i] << (7-(BCH::K+i)%8); + (*encode)(code); + for (int i = 0; i < L; ++i) + assert(code[i] == target[i]); + delete[] target; + delete[] code; + delete encode; + } + if (0) { // NASA INTRO BCH(15, 5) T=3 typedef CODE::GaloisField<4, 0b10011, uint8_t> GF; - typedef CODE::BoseChaudhuriHocquenghemEncoder<6, 1, 5, GF> BCH; + typedef CODE::BoseChaudhuriHocquenghemEncoderReference<6, 1, 5, GF> BCH; GF instance; BCH encode({0b10011, 0b11111, 0b00111}); uint8_t code[BCH::N] = { 1, 1, 0, 0, 1 }; @@ -23,10 +57,10 @@ int main() for (int i = 0; i < BCH::N; ++i) assert(code[i] == target[i]); } - if (1) { + if (0) { // DVB-S2 FULL BCH(65535, 65343) T=12 typedef CODE::GaloisField<16, 0b10000000000101101, uint16_t> GF; - typedef CODE::BoseChaudhuriHocquenghemEncoder<24, 1, 65343, GF> BCH; + typedef CODE::BoseChaudhuriHocquenghemEncoderReference<24, 1, 65343, GF> BCH; GF *instance = new GF(); BCH *encode = new BCH({0b10000000000101101, 0b10000000101110011, 0b10000111110111101, 0b10101101001010101, 0b10001111100101111, 0b11111011110110101, 0b11010111101100101, 0b10111001101100111, 0b10000111010100001, 0b10111010110100111, 0b10011101000101101, 0b10001101011100011}); uint16_t *code = new uint16_t[BCH::N];