Last active
February 16, 2023 07:05
-
-
Save bd1es/89de3fa26cc0595a80c1f1644c2e90f9 to your computer and use it in GitHub Desktop.
Generate [15,11] BCH code for AVR MCUs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This file is part of the QTR Clock AVR, one of my project displaying time. | |
* | |
* Copyright (C) 2010-2022 BD1ES. | |
* | |
* This program is free software; you can redistribute it and/or modify it under | |
* the terms of the GNU General Public License as published by the Free Software | |
* Foundation; either version 3 of the License, or (at your option) any later | |
* version. | |
* | |
* 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 General Public License along with | |
* this program; if not, write to the Free Software Foundation, Inc., 51 Franklin | |
* Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
*/ | |
/* | |
* Author: BD1ES | |
* Date modified: 2022 | |
* | |
* This program generates constants and code snippets for QTR clock projects. | |
*/ | |
#include <string> | |
#include <vector> | |
#include <iostream> | |
#include <sstream> | |
#include <chrono> | |
#include <stdexcept> | |
#include <cstdarg> | |
#include <cstdint> | |
#include <cmath> | |
#include <climits> | |
// ------------------------------------------------------------------------------------------------ | |
static std::string vsformat(const char* fmt, ...) | |
{ | |
va_list vl; | |
va_start(vl, fmt); | |
auto size = vsnprintf(nullptr, 0, fmt, vl) + 1; // The size contains the terminator '\0'. | |
va_end(vl); | |
std::vector <char> buf(size + 1); | |
va_start(vl, fmt); | |
size = vsnprintf(&buf[0], size_t(size), fmt, vl); | |
va_end(vl); | |
return std::string(&buf[0], size); | |
} | |
// ------------------------------------------------------------------------------------------------ | |
/* This type is from const_def.c initially written for AVR QTR clocks and put here as a comment. | |
* The dot_duration_table is not used in the current time2tty because it only involves CW. | |
*/ | |
struct const_def_t { | |
void print_comments() | |
{ | |
std::cout << R"rawliteral( | |
// These macros are defined for sharing AVR GCC code with PCs. | |
#if defined(__GNUC__) && defined(__AVR_ARCH__) | |
#include <avr/pgmspace.h> | |
#define PGM_ROM_SPACE const PROGMEM | |
#else | |
#define PGM_ROM_SPACE const | |
#define pgm_read_byte * | |
#define pgm_read_word * | |
#define pgm_read_dword * | |
#endif | |
)rawliteral" << std::endl; | |
} | |
// WPM table | |
void print_wpm_table(double timer_freq, uint8_t wpm_low = 1, uint8_t wpm_high = 64) | |
{ | |
auto gen_table = [timer_freq, wpm_low, wpm_high] () | |
{ | |
static const double PARIS_FACTOR = 50.0; | |
std::vector<int> table; | |
for(uint8_t i = wpm_low; i <= wpm_high; i++){ | |
table.push_back(int(0.5 + 60.0*timer_freq / i / PARIS_FACTOR)); | |
} | |
return table; | |
}; | |
const std::vector<int> wpm_table = gen_table(); | |
const int maxd = 1 + log10(wpm_table[0]); | |
const int maxc = (80-4) / (1+maxd); | |
const std::string d = "%" + vsformat("%d", maxd) + "d"; | |
std::stringstream ss; | |
ss << vsformat("// Dot durations in %d-%d WPM for %.2f Hz timer interval.\n", | |
wpm_low, wpm_high, timer_freq); | |
ss << vsformat("static const uint8_t DOT_DURATION_WPM_LOW = %d;\n", wpm_low); | |
ss << vsformat("static const uint8_t DOT_DURATION_WPM_HIGH = %d;\n", wpm_high); | |
ss << vsformat("static PGM_ROM_SPACE int dot_duration_table[%d] = {", | |
wpm_high-wpm_low+1); | |
for(size_t i = 0; i < wpm_table.size(); i++){ | |
if((i%maxc) == 0){ | |
ss << "\n "; | |
} | |
ss << vsformat(d.c_str(), wpm_table[i]); | |
if(i < (wpm_table.size()-1)) ss << ","; | |
} | |
ss << "\n};\n"; | |
print(ss); | |
} | |
// Hamming encoding table | |
void print_hamming_enc_table() | |
{ | |
auto gen_table = [] () { | |
/* s3 = d3^d2^d1; | |
* s2 = d2^d1^(~d0); | |
* s1 = d3^d1^(~d0); | |
* s0 = d3^d2^(~d0); | |
* d = i3,s3,i2,s2,i1,s1,i0,s0; | |
*/ | |
std::vector<int> table; | |
for(int i = 0; i < 16; i++){ | |
int d, d0, d1, d2, d3, s, s0, s1, s2, s3; | |
d3 = (i>>3) & 1; | |
d2 = (i>>2) & 1; | |
d1 = (i>>1) & 1; | |
d0 = i & 1; | |
d = (d3<<7) | (d2<<5) | (d1<<3) | (d0<<1); | |
s3 = d3 ^ d2 ^ d1; | |
s2 = d2 ^ d1 ^ (~d0&1); | |
s1 = d3 ^ d1 ^ (~d0&1); | |
s0 = d3 ^ d2 ^ (~d0&1); | |
s = (s3<<6) | (s2<<4) | (s1<<2) | s0; | |
d |= s; | |
table.push_back(d); | |
} | |
return table; | |
}; | |
std::vector<int> enc_table = gen_table(); | |
std::stringstream ss; | |
ss << "// [8,4] extended Hamming encoding table:\n"; | |
ss << "static PGM_ROM_SPACE uint8_t hamming_enc_table[16] = {"; | |
for(size_t i = 0; i < enc_table.size(); i++){ | |
if((i%8) == 0){ | |
ss << "\n "; | |
} | |
ss << vsformat(" 0x%02x", enc_table[i]); | |
if(i < (enc_table.size()-1)) ss << ","; | |
} | |
ss << "\n};\n"; | |
print(ss); | |
} | |
// Hamming decoding table | |
void print_hamming_dec_table() | |
{ | |
auto gen_table = [] () { | |
/* s3 = d7^d5^d1^d0; | |
* s2 = d7^d3^d2^d1; | |
* s1 = d5^d4^d3^d1; | |
* s0 = d7^d6^d5^d4^d3^d2^d1^d0; | |
*/ | |
std::vector<int>table; | |
for(int i = 0; i < 256; i++){ | |
int d, d0, d1, d2, d3, d4, d5, d6, d7, s, s0, s1, s2, s3; | |
d7 = (i>>7) & 1; | |
d6 = (i>>6) & 1; | |
d5 = (i>>5) & 1; | |
d4 = (i>>4) & 1; | |
d3 = (i>>3) & 1; | |
d2 = (i>>2) & 1; | |
d1 = (i>>1) & 1; | |
d0 = i & 1; | |
s3 = d7 ^ d5 ^ d1 ^ d0; | |
s2 = d7 ^ d3 ^ d2 ^ d1; | |
s1 = d5 ^ d4 ^ d3 ^ d1; | |
s0 = d7 ^ d6 ^ d5 ^ d4 ^ d3 ^ d2 ^ d1 ^ d0; | |
s = (s3<<3) | (s2<<2) | (s1<<1) | s0; | |
switch(s){ | |
case 0x0f: | |
case 0x0e: | |
case 0x0c: | |
case 0x0a: | |
case 0x06: | |
d = (d7<<3) | (d5<<2) | (d3<<1) | d1; | |
break; | |
case 0x02: | |
d = ((~d7&0x01)<<3) | (d5<<2) | (d3<<1) | d1; | |
break; | |
case 0x04: | |
d = (d7<<3) | ((~d5&0x01)<<2) | (d3<<1) | d1; | |
break; | |
case 0x08: | |
d = (d7<<3) | (d5<<2) | ((~d3&0x01)<<1) | d1; | |
break; | |
case 0x00: | |
d = (d7<<3) | (d5<<2) | (d3<<1) | (~d1&0x01); | |
break; | |
default: | |
d = 0xff; | |
break; | |
} | |
table.push_back(d); | |
} | |
return table; | |
}; | |
std::vector <int> dec_table = gen_table(); | |
std::stringstream ss; | |
ss << "// [8,4] extended Hamming decoding table:\n"; | |
ss << "static PGM_ROM_SPACE uint8_t hamming_dec_table[256] = {"; | |
for(size_t i = 0; i < dec_table.size(); i++){ | |
if((i%12) == 0){ | |
ss << "\n "; | |
} | |
ss << vsformat(" 0x%02x", dec_table[i]); | |
if(i < (dec_table.size()-1)) ss << ","; | |
} | |
ss << "\n};\n"; | |
print(ss); | |
} | |
/* http://users.ece.cmu.edu/~koopman/crc/index.html | |
* http://users.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf | |
*/ | |
static const uint16_t CRC16_CCITT = 0x1021; // x^16+x^12+x^5+1 | |
static const uint16_t CRC16_KOOPMAN = 0x5935; // x^16+x^14+x^12+x^11+x^8+x^5+x^4+x^2+1 | |
// CRC table | |
void print_crc_table(uint16_t poly) | |
{ | |
auto gen_table = [poly] () { | |
std::vector<int>table; | |
for(int i = 0; i < 256; i++){ | |
int d = i << 8; | |
for(int j = 0; j < 8; j++){ | |
int s = d & 0x8000; | |
d <<= 1; | |
if(s) d ^= poly; | |
} | |
table.push_back(d & 0xffff); | |
} | |
return table; | |
}; | |
std::vector<int> crc_table = gen_table(); | |
std::stringstream ss; | |
ss << vsformat("// CRC-16 table for the polynomial 0x%04x:\n", poly); | |
ss << "static PGM_ROM_SPACE uint16_t crc_table[256] = {"; | |
for(size_t i = 0; i < crc_table.size(); i++){ | |
if((i%9) == 0){ | |
ss << "\n "; | |
} | |
ss << vsformat(" 0x%04x", crc_table[i]); | |
if(i < (crc_table.size()-1)) ss << ","; | |
} | |
ss << "\n};\n"; | |
print(ss); | |
} | |
private: | |
void print(std::stringstream &ss) | |
{ | |
while(!ss.eof()){ | |
std::string s; | |
std::getline(ss, s); | |
std::cout << s << std::endl; | |
} | |
} | |
}; | |
// ------------------------------------------------------------------------------------------------ | |
// Simple BCH(15,11,1). This type is used for generating coding tables and test vectors. | |
struct bch1511_base_t { | |
bch1511_base_t() | |
{ | |
uint8_t mreg4, mreg3; | |
uint8_t tmp; | |
// Calculate encoder mregs with 3-bit and 4-bit remainders. Put them together in the table. | |
for(uint8_t mreg = 0; mreg < 16; mreg++){ | |
for(uint8_t d = 0; d < 16; d++){ | |
mreg4 = mreg; | |
tmp = d; | |
for(uint8_t i = 0; i < 4; i++){ | |
encoder_shift(mreg4, tmp); | |
tmp >>= 1; | |
} | |
if(d < 8){ | |
mreg3 = mreg; | |
tmp = d; | |
for(uint8_t i = 0; i < 3; i++){ | |
encoder_shift(mreg3, tmp); | |
tmp >>= 1; | |
} | |
}else{ | |
// 4-bit data is not required, make them nils. | |
mreg3 = 0; | |
} | |
encoder_mreg[mreg][d] = mreg3*16 + mreg4; | |
} | |
} | |
// Calculate decoder mregs with 3-bit and 4-bit remainders. Put them together in the table. | |
for(uint8_t mreg = 0; mreg < 16; mreg++){ | |
for(uint8_t d = 0; d < 16; d++){ | |
mreg4 = mreg; | |
tmp = d; | |
for(uint8_t i = 0; i < 4; i++){ | |
decoder_shift(mreg4, tmp); | |
tmp >>= 1; | |
} | |
if(d < 8){ | |
mreg3 = mreg; | |
tmp = d; | |
for(uint8_t i = 0; i < 3; i++){ | |
decoder_shift(mreg3, tmp); | |
tmp >>= 1; | |
} | |
}else{ | |
mreg3 = 0; | |
} | |
decoder_mreg[mreg][d] = mreg3*16 + mreg4; | |
} | |
} | |
// Calculate error positions and the correction patterns based on the codeword '0'. | |
correction_pattern[0] = error_position[0] = 0; | |
for(uint8_t i = 0; i < 15; i++){ | |
// Invert the specific bit of the codeword to simulate code with a corruption. | |
uint16_t x = uint16_t(1 << i); | |
// Make divisions using the prepared table. | |
uint8_t mreg = 0; | |
for(uint8_t j = 0; j < 4; j++){ | |
if(j < 3){ | |
mreg = decoder_mreg[mreg][x&15] & 15; | |
}else{ | |
mreg = decoder_mreg[mreg][x&15] >> 4; | |
} | |
x >>= 4; | |
} | |
// Use mreg as a remainder to index a related correction pattern. | |
correction_pattern[mreg] = uint16_t(1 << i); | |
error_position[mreg] = i + 1; | |
} | |
} | |
uint16_t encode(uint16_t d) | |
{ | |
d &= 2047; | |
uint16_t tmp_d = d; | |
uint8_t mreg = 0; | |
for(uint8_t i = 0; i < 3; i++){ | |
if(i < 2){ | |
mreg = encoder_mreg[mreg][d&15] & 15; | |
}else{ | |
mreg = encoder_mreg[mreg][d&15] >> 4; | |
} | |
d >>= 4; | |
} | |
return mreg*2048 + tmp_d; | |
} | |
uint16_t decode(uint16_t c, uint8_t &e) | |
{ | |
c &= 32767; | |
uint16_t tmp_c = c; | |
uint8_t mreg = 0; | |
for(uint8_t i = 0; i < 4; i++){ | |
if(i < 3){ | |
mreg = decoder_mreg[mreg][c&15] & 15; | |
}else{ | |
mreg = decoder_mreg[mreg][c&15] >> 4; | |
} | |
c >>= 4; | |
} | |
// The error position will be placed in e. | |
e = error_position[mreg]; | |
return tmp_c ^ correction_pattern[mreg]; | |
} | |
protected: | |
uint8_t encoder_mreg[16][16]; | |
uint8_t decoder_mreg[16][16]; | |
uint8_t error_position[16]; | |
uint16_t correction_pattern[16]; | |
// The encoder polynomial division, 1+X+X^4 | |
void encoder_shift(uint8_t &mreg, uint8_t d) | |
{ | |
// bits of uint8_t mreg | |
// 7 6 5 4 3 2 1 0 -> x x x x 0 1 2 3 | |
uint8_t bit = (mreg ^ d) & 1; | |
bit <<= 3; | |
mreg ^= bit; | |
mreg = (mreg >> 1) | bit; | |
} | |
// The decoder polynomial division, X^4+X+1 | |
void decoder_shift(uint8_t &mreg, uint8_t d) | |
{ | |
// bits of uint8_t mreg | |
// 7 6 5 4 3 2 1 0 -> x x x x 3 2 1 0 | |
uint8_t bit = (mreg >> 3) & 1; | |
mreg ^= bit; | |
bit ^= d & 1; | |
mreg = ((mreg << 1) | bit) & 15; | |
} | |
}; | |
// ------------------------------------------------------------------------------------------------ | |
// This type is used for generating the BCH code for AVR MCUs. | |
struct bch1511_avrgen_t : public bch1511_base_t { | |
void print_code(const char *indent, const char *class_name) { | |
print_vector(indent, gen_class(class_name)); | |
} | |
protected: | |
std::vector <std::string> gen_class(const std::string &class_name) | |
{ | |
std::stringstream ss; | |
std::vector <std::string> v; | |
ss << "// BCH[15,11,1] base type for AVR MCUs.\n"; | |
ss << "struct " << class_name << " {"; | |
v = gen_encoder(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
v = gen_decoder(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
v = gen_interleave(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
v = gen_deinterleave(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
v = gen_bitrev(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
v = gen_parity(); | |
for(auto s : v) if(s.empty()) ss << "\n"; else ss << "\n " << s; | |
ss << "};\n"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_encoder() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
static uint16_t encode(uint16_t d) | |
{ | |
// Encoder mreg values for 1+X+X^4. | |
static PGM_ROM_SPACE uint8_t enc_mreg[16][16] = { | |
)rawliteral"; | |
// Print contents of the encoding table. | |
for(uint8_t mreg = 0; mreg < 16; mreg++){ | |
ss << " {"; | |
for(uint8_t d = 0; d < 16; d++){ | |
ss << vsformat("%3d", encoder_mreg[mreg][d]); | |
if(d < 15) ss << ','; | |
} | |
ss << "}"; | |
if(mreg < 15){ | |
ss << ",\n"; | |
} | |
} | |
ss << "\n };\n"; | |
ss << R"rawliteral( | |
d &= 2047; | |
uint16_t tmp_d = d; | |
uint8_t mreg = 0; | |
for(uint8_t i = 0; i < 3; i++){ | |
if(i < 2){ | |
mreg = pgm_read_byte(&enc_mreg[mreg][tmp_d&15]) & 15; | |
}else{ | |
mreg = pgm_read_byte(&enc_mreg[mreg][tmp_d&15]) >> 4; | |
} | |
tmp_d >>= 4; | |
} | |
return mreg*2048 + d; | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_decoder() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
static uint16_t decode(uint16_t c, uint8_t &e) | |
{ | |
// Decoder mreg values for X^4+X+1. | |
static PGM_ROM_SPACE uint8_t dec_mreg[16][16] = { | |
)rawliteral"; | |
// Print contents in the decoding table. | |
for(uint8_t mreg = 0; mreg < 16; mreg++){ | |
ss << " {"; | |
for(uint8_t d = 0; d < 16; d++){ | |
ss << vsformat("%3d", decoder_mreg[mreg][d]); | |
if(d < 15) ss << ','; | |
} | |
ss << "}"; | |
if(mreg < 15){ | |
ss << ",\n"; | |
} | |
} | |
ss << "\n };\n"; | |
ss << R"rawliteral( | |
// Correction patterns. | |
static PGM_ROM_SPACE uint16_t corr_pattern[16] = { | |
)rawliteral"; | |
// Print contents in the correction pattern table. | |
for(uint8_t i = 0; i < 16; i++){ | |
if((i%8) == 0) ss << " "; | |
ss << vsformat(" 0x%04x", correction_pattern[i]); | |
if((i%8) < 7){ | |
ss << ","; | |
}else{ | |
if(i < 15){ | |
ss << ",\n"; | |
} | |
} | |
} | |
ss << "\n };\n"; | |
ss << R"rawliteral( | |
// Error positions. | |
static PGM_ROM_SPACE uint8_t err_position[16] = { | |
)rawliteral"; | |
// Print contents in the error position table. | |
for(uint8_t i = 0; i < 16; i++){ | |
if((i%8) == 0) ss << " "; | |
ss << vsformat(" 0x%02x", error_position[i]); | |
if((i%8) < 7){ | |
ss << ","; | |
}else{ | |
if(i < 15){ | |
ss << ",\n"; | |
} | |
} | |
} | |
ss << "\n };\n"; | |
ss << R"rawliteral( | |
c &= 32767; | |
uint16_t tmp_c = c; | |
uint8_t mreg = 0; | |
for(uint8_t i = 0; i < 4; i++){ | |
if(i < 3){ | |
mreg = pgm_read_byte(&dec_mreg[mreg][tmp_c&15]) & 15; | |
}else{ | |
mreg = pgm_read_byte(&dec_mreg[mreg][tmp_c&15]) >> 4; | |
} | |
tmp_c >>= 4; | |
} | |
// The error position will be placed in e. | |
e = pgm_read_byte(&err_position[mreg]); | |
return c ^ pgm_read_word(&corr_pattern[mreg]); | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_interleave() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
// Some Bit Twiddlings - graphics.stanford.edu/~seander/bithacks.html | |
static void interleave(uint16_t x, uint16_t y, uint32_t &z) | |
{ | |
uint32_t xt = x; | |
xt = (xt | (xt << 8)) & 0x00FF00FF; | |
xt = (xt | (xt << 4)) & 0x0F0F0F0F; | |
xt = (xt | (xt << 2)) & 0x33333333; | |
xt = (xt | (xt << 1)) & 0x55555555; | |
uint32_t yt = y; | |
yt = (yt | (yt << 8)) & 0x00FF00FF; | |
yt = (yt | (yt << 4)) & 0x0F0F0F0F; | |
yt = (yt | (yt << 2)) & 0x33333333; | |
yt = (yt | (yt << 1)) & 0x55555555; | |
z = xt | (yt << 1); | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_deinterleave() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
static void deinterleave(uint32_t z, uint16_t &x, uint16_t &y) | |
{ | |
uint32_t xt = z & 0x55555555; | |
xt = (xt | (xt >> 1)) & 0x33333333; | |
xt = (xt | (xt >> 2)) & 0x0F0F0F0F; | |
xt = (xt | (xt >> 4)) & 0x00FF00FF; | |
xt = (xt | (xt >> 8)) & 0x0000FFFF; | |
x = uint16_t(xt); | |
uint32_t yt = (z>>1) & 0x55555555; | |
yt = (yt | (yt >> 1)) & 0x33333333; | |
yt = (yt | (yt >> 2)) & 0x0F0F0F0F; | |
yt = (yt | (yt >> 4)) & 0x00FF00FF; | |
yt = (yt | (yt >> 8)) & 0x0000FFFF; | |
y = uint16_t(yt); | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_bitrev() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
static uint16_t bitrev(uint16_t v) | |
{ | |
v = ((v >> 1) & 0x5555) | ((v & 0x5555) << 1); | |
v = ((v >> 2) & 0x3333) | ((v & 0x3333) << 2); | |
v = ((v >> 4) & 0x0F0F) | ((v & 0x0F0F) << 4); | |
v = ((v >> 8) & 0x00FF) | ((v & 0x00FF) << 8); | |
return v; | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
std::vector <std::string> gen_parity() | |
{ | |
std::stringstream ss; | |
ss << R"rawliteral( | |
static uint16_t parity(uint16_t v) | |
{ | |
v ^= v >> 8; | |
v ^= v >> 4; | |
return (0x6996 >> (v&0xf)) & 1; | |
} | |
)rawliteral"; | |
return get_vector(ss); | |
} | |
private: | |
std::vector <std::string> get_vector(std::stringstream &ss) { | |
using namespace std; | |
vector <string> v; | |
while(!ss.eof()){ | |
string s; | |
getline(ss, s); | |
v.push_back(s); | |
} | |
return v; | |
} | |
void print_vector(const char *indents, const std::vector <std::string> &v) { | |
using namespace std; | |
for(size_t i = 0; i < v.size(); i++){ | |
cout << indents << v[i] << endl; | |
} | |
} | |
}; | |
// ------------------------------------------------------------------------------------------------ | |
// Convert a given integer to a bit string. | |
template <typename T> | |
static std::string bitstr(T n, uint8_t num_bits = 0) | |
{ | |
std::string s = "0b"; | |
if((num_bits==0) || num_bits>CHAR_BIT*sizeof(n)){ | |
num_bits = CHAR_BIT*sizeof(n); | |
} | |
for(int8_t i = num_bits-1; i >= 0; i--){ | |
s.push_back((n & (1 << i)) ? '1' : '0'); | |
} | |
return s; | |
} | |
struct bch_codec_test_t { | |
void run() { | |
using namespace std; | |
cerr << "Testing the BCH codec in the space of [0..2047] "; | |
static const uint32_t NUM_ITTERS = 20000000; | |
auto start = chrono::high_resolution_clock::now(); | |
for(uint32_t i = 0; i < NUM_ITTERS; i++){ | |
if((i%(NUM_ITTERS/30)) == 0) cerr << '.'; | |
single_test(i); | |
} | |
chrono::duration <double> elapsed = chrono::high_resolution_clock::now() - start; | |
cerr << endl; | |
cerr << vsformat(" %d codes are tested in %f seconds, and", NUM_ITTERS, elapsed.count()) | |
<< endl; | |
cerr << vsformat(" each single test took %f nanoseconds.", | |
elapsed.count() * 1e9 / NUM_ITTERS / 17) << endl; | |
cerr << "Finished." << endl; | |
} | |
private: | |
bch1511_base_t b1511; | |
inline void single_test(uint16_t n) { | |
using namespace std; | |
uint16_t x, y; | |
uint8_t res; | |
// Make 'x' the encoding result. | |
n &= 2047; | |
x = b1511.encode(n); | |
// Test the decoding result with the input code has no error. | |
y = b1511.decode(x, res); | |
if(res != 0){ | |
throw runtime_error( | |
vsformat("%s: n = %d, b1511.decode() returns a non-zero value %d while decoding %s.\n", | |
__func__, n, res, bitstr(x, 15).c_str()) | |
); | |
} | |
if((y&2047) != n){ | |
throw runtime_error( | |
vsformat("%s: n = %d, b1511.decode() produces an incorrect result %s while decoding %s.\n", | |
__func__, n, bitstr(y, 15).c_str(), bitstr(x, 15).c_str()) | |
); | |
} | |
// Test error corrections by inverting bits of x. | |
for(uint8_t i = 0; i < 15; i++){ | |
uint16_t tmp = x ^ (1<<i); | |
y = b1511.decode(tmp, res); | |
if(res != (i+1)){ | |
throw runtime_error( | |
vsformat("%s: n = %d, b1511.decode() returns an invalid value %d while decoding %s.\n", | |
__func__, n, res, bitstr(tmp, 15).c_str()) | |
); | |
} | |
if((y&2047) != n){ | |
throw runtime_error( | |
vsformat("%s: n = %d, b1511.decode() produces an incorrect result %s while decoding %s.\n", | |
__func__, n, bitstr(y, 15).c_str(), bitstr(tmp, 15).c_str()) | |
); | |
} | |
} | |
} | |
}; | |
// ------------------------------------------------------------------------------------------------ | |
int main() | |
{ | |
bch_codec_test_t bch_codec_test; | |
bch_codec_test.run(); | |
std::cout << std::endl; | |
const_def_t const_def; | |
const_def.print_comments(); | |
//const_def.print_wpm_table(4166.67); | |
const_def.print_wpm_table(9600); | |
const_def.print_hamming_enc_table(); | |
const_def.print_hamming_dec_table(); | |
const_def.print_crc_table(const_def.CRC16_CCITT); | |
const_def.print_crc_table(const_def.CRC16_KOOPMAN); | |
std::cout << std::endl; | |
bch1511_avrgen_t bch1511_def; | |
bch1511_def.print_code("", "bch1511_avr_t"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment