Skip to main content
Code Review

Return to Question

Commonmark migration
Source Link

###rdstypes.hh #ifndef RDSTYPES_HH #define RDSTYPES_HH

rdstypes.hh

#ifndef RDSTYPES_HH
#define RDSTYPES_HH
#include <cstdint>
namespace rds
{
 struct block {
 std::uint16_t value;
 std::uint8_t err_count = 0xff;
 bool is_valid = false;
 operator std::uint16_t() const { return value; }
 };
 struct group {
 block a, b, c, d;
 };
}
#endif /* RDSTYPES_HH */

###crc.hh #ifndef CRC_HH #define CRC_HH

crc.hh

#ifndef CRC_HH
#define CRC_HH
#include <cstdint>
namespace rds
{
 // Figure B.1, page 61
 static constexpr std::uint32_t encode(std::uint16_t x)
 {
 return ((x >> 15) & 1) * 0b10000000000000000001110111
 ^ ((x >> 14) & 1) * 0b01000000000000001011100111
 ^ ((x >> 13) & 1) * 0b00100000000000001110101111
 ^ ((x >> 12) & 1) * 0b00010000000000001100001011
 ^ ((x >> 11) & 1) * 0b00001000000000001101011001
 ^ ((x >> 10) & 1) * 0b00000100000000001101110000
 ^ ((x >> 9) & 1) * 0b00000010000000000110111000
 ^ ((x >> 8) & 1) * 0b00000001000000000011011100
 ^ ((x >> 7) & 1) * 0b00000000100000000001101110
 ^ ((x >> 6) & 1) * 0b00000000010000000000110111
 ^ ((x >> 5) & 1) * 0b00000000001000001011000111
 ^ ((x >> 4) & 1) * 0b00000000000100001110111111
 ^ ((x >> 3) & 1) * 0b00000000000010001100000011
 ^ ((x >> 2) & 1) * 0b00000000000001001101011101
 ^ ((x >> 1) & 1) * 0b00000000000000101101110010
 ^ ((x >> 0) & 1) * 0b00000000000000010110111001;
 }
 // Figure B.3, page 63
 constexpr std::uint16_t syndrome(std::uint32_t y)
 {
 return ((y >> 25) & 1) * 0b1000000000
 ^ ((y >> 24) & 1) * 0b0100000000
 ^ ((y >> 23) & 1) * 0b0010000000
 ^ ((y >> 22) & 1) * 0b0001000000
 ^ ((y >> 21) & 1) * 0b0000100000
 ^ ((y >> 20) & 1) * 0b0000010000
 ^ ((y >> 19) & 1) * 0b0000001000
 ^ ((y >> 18) & 1) * 0b0000000100
 ^ ((y >> 17) & 1) * 0b0000000010
 ^ ((y >> 16) & 1) * 0b0000000001
 ^ ((y >> 15) & 1) * 0b1011011100
 ^ ((y >> 14) & 1) * 0b0101101110
 ^ ((y >> 13) & 1) * 0b0010110111
 ^ ((y >> 12) & 1) * 0b1010000111
 ^ ((y >> 11) & 1) * 0b1110011111
 ^ ((y >> 10) & 1) * 0b1100010011
 ^ ((y >> 9) & 1) * 0b1101010101
 ^ ((y >> 8) & 1) * 0b1101110110
 ^ ((y >> 7) & 1) * 0b0110111011
 ^ ((y >> 6) & 1) * 0b1000000001
 ^ ((y >> 5) & 1) * 0b1111011100
 ^ ((y >> 4) & 1) * 0b0111101110
 ^ ((y >> 3) & 1) * 0b0011110111
 ^ ((y >> 2) & 1) * 0b1010100111
 ^ ((y >> 1) & 1) * 0b1110001111
 ^ ((y >> 0) & 1) * 0b1100011011;
 }
}
#endif /* CRC_HH */

###corrections.cc // This program is run as part of the build, to generate the table of // corrections as C++ source, to be compiled into the program.

corrections.cc

// This program is run as part of the build, to generate the table of
// corrections as C++ source, to be compiled into the program.
#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = false;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = true;
 else if (c != correction)
 correctable = false; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0] = {0, 0, true};
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}

###synchronizer.hh #ifndef SYNCHRONIZER_HH #define SYNCHRONIZER_HH

synchronizer.hh

#ifndef SYNCHRONIZER_HH
#define SYNCHRONIZER_HH
#include "rdstypes.hh"
#include <cstdint>
namespace rds {
class Synchronizer
{
 // No valid programme identifier has country code 0, so we can use
 // zero to indicate initial condition.
 std::uint32_t prog_id = 0;
 // Initial state needs to not look like 0BCD0
 static constexpr unsigned __int128 initial_state = 0xffff;
 // 128 bits can store a whole 104-bit group, plus 24 bits of
 // adjacent context.
 unsigned __int128 state = initial_state;
 static auto constexpr max_loss_blocks = 10;
 unsigned int bits_until_a = 0;
 unsigned int blocks_since_lock = 0;
 std::uint32_t last_good_b = 0;
public:
 static constexpr std::uint32_t CHECKWORD_A = 0x0fc;
 static constexpr std::uint32_t CHECKWORD_B = 0x198;
 static constexpr std::uint32_t CHECKWORD_C = 0x168;
 static constexpr std::uint32_t CHECKWORD_K = 0x350;
 static constexpr std::uint32_t CHECKWORD_D = 0x1b4;
 virtual ~Synchronizer() = default;
 std::uint16_t pid() const {
 return prog_id >> 10;
 }
 virtual void reset() {
 state = initial_state;
 prog_id = 0;
 perBitAction = &Synchronizer::search_for_lock;
 // other members will be set before we enter the locked state
 }
 void push(bool bit)
 {
 state <<= 1;
 state += bit;
 (this->*perBitAction)();
 }
protected:
 // @return error weight
 static rds::block correct(std::uint16_t checkword, std::uint32_t input);
 void (Synchronizer::*perBitAction)() = &Synchronizer::search_for_lock;
 void search_for_lock();
 void confirm_lock();
 void lock_found(std::uint16_t pid, unsigned int bits_to_go);
 void lock_lost();
 virtual void process_group(const rds::group& group);
};
}
#endif /* SYNCHRONIZER_HH */

###synchronizer.cc #include "synchronizer.hh"

synchronizer.cc

#include "synchronizer.hh"
#include "crc.hh"
#include <algorithm>
#include <array>
#include <cstdint>
using rds::Synchronizer;
namespace {
 struct Correction
 {
 std::uint16_t correction;
 unsigned char distance;
 bool correctable;
 };
 const std::array<Correction, 1024> corrections{{
#include "corrections.table"
 }};
 template<unsigned char size, typename... T>
 static constexpr int hamming_distance(T... v)
 {
 auto constexpr mask = (std::common_type_t<T...>(1) << size) - 1;
 return __builtin_popcount((mask & (... ^ v)));
 }
}
// @return error weight
rds::block Synchronizer::correct(std::uint16_t checkword, std::uint32_t input)
{
 input ^= checkword;
 auto s = syndrome(input);
 rds::block b = {static_cast<std::uint16_t>(input >> 10), 0, true};
 if (!s) {
 // perfect
 return b;
 }
 auto const& entry = corrections[s];
 b.value ^= entry.correction;
 b.err_count = entry.distance;
 b.is_valid = entry.correctable;
 return b;
}
void Synchronizer::search_for_lock()
{
 constexpr int MAX_ERR = 8;
 // Check self-similarity first, with 0.5% false-positive rate, before examining CRCs
 if (hamming_distance<26>(CHECKWORD_A, CHECKWORD_K, state, state >> 52) <= 6) {
 rds::block a,b,k,d;
 int err;
 if ((err = (k = correct(CHECKWORD_K, state)).err_count) <= MAX_ERR
 && (err += (a = correct(CHECKWORD_A, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (b = correct(CHECKWORD_B, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched A.K
 lock_found(a.value, 52);
 return;
 }
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (k = correct(CHECKWORD_K, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched K.A
 lock_found(a.value, 0);
 return;
 }
 }
 // Have we got a match with A...A? Again, we require >99.5% certainty before checking CRCs.
 // Note that we only have 24 bits remaining of the previous A, so fill in from the new one.
 if (hamming_distance<24>(state, state >> 104) <= 5) {
 rds::block a,b,c,d,e;
 int err;
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (e = correct(CHECKWORD_A, ((state >> 104) & 0xffffff) | (state & 0x3000000))).err_count) <= MAX_ERR
 && e.value == a.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR
 && (err += (c = correct(CHECKWORD_C, state >> 52)).err_count) <= MAX_ERR
 && (err += (b = correct(CHECKWORD_B, state >> 78)).err_count) <= MAX_ERR)
 {
 // we matched A...A
 lock_found(a.value, 0);
 return;
 }
 }
}
void Synchronizer::confirm_lock()
{
 if (--bits_until_a)
 return;
 bits_until_a = 104;
 rds::block a = correct(CHECKWORD_A, state);
 rds::block b = correct(CHECKWORD_B, state >> 78);
 rds::block c = correct(CHECKWORD_C, state >> 52);
 rds::block k = correct(CHECKWORD_K, state >> 52);
 rds::block d = correct(CHECKWORD_D, state >> 26);
 unsigned ck = std::min(k.err_count, c.err_count);
 if ((a.err_count >= 4 || a.value != prog_id>>10) && b.err_count * ck * d.err_count >= 16) {
 if (hamming_distance<25>(std::uint32_t(state), prog_id>>1) <= 5) {
 // bit slip - need one more bit
 bits_until_a = 1;
 } else if (hamming_distance<26>(std::uint32_t(state)>>1, prog_id) <= 5) {
 // bit slip - miss one bit
 bits_until_a = 103;
 } else {
 lock_lost();
 }
 return;
 }
 if (a.err_count == 0 && a.value != prog_id>>10) {
 // prog_id has changed
 prog_id = CHECKWORD_A ^ encode(a);
 }
 // reset counter
 blocks_since_lock = 0;
 if (b.err_count > 8) {
 // attempt to recover block 2 using saved PTY and inferred A/B
 // AAAABTPPPPPiiiii..........
 static constexpr auto pty_mask = 0b00000011111000000000000000;
 static constexpr auto ab_mask = 0b00001000000000000000000000;
 std::uint32_t block2 =
 ((~pty_mask & ~ab_mask) & (state >> 78))
 | (pty_mask & last_good_b)
 | (ab_mask & (c.err_count == k.err_count ? last_good_b : -1 * (c.err_count >= k.err_count)));
 auto b2 = correct(CHECKWORD_B, block2);
 if (b2.err_count >= b.err_count) {
 // couldn't decode the B block, so the rest is useless
 return;
 }
 } else {
 last_good_b = state >> 78;
 }
 process_group({a, b, c, d});
}
void Synchronizer::lock_found(std::uint16_t pid, unsigned int bits_to_go) {
 perBitAction = &Synchronizer::confirm_lock;
 prog_id = CHECKWORD_A ^ encode(pid);
 blocks_since_lock = 0;
 bits_until_a = 1 + bits_to_go;
 confirm_lock();
}
void Synchronizer::lock_lost() {
 if (++blocks_since_lock >= max_loss_blocks)
 perBitAction = &Synchronizer::search_for_lock;
}
void Synchronizer::process_group(const rds::group&)
{
 // to be overridden
}

###decode-file.cc #include "synchronizer.hh"

decode-file.cc

#include "synchronizer.hh"
#include <fstream>
#include <iostream>
struct TestSynchronizer : rds::Synchronizer
{
 // keep track of some statistics
 unsigned long bits = 0;
 unsigned long good_blocks = 0;
 unsigned long corrected_blocks = 0;
 unsigned long bad_blocks = 0;
 void push(bool bit) {
 ++bits;
 rds::Synchronizer::push(bit);
 }
 void process_group(const rds::group& group) override
 {
 for (auto const& block: {group.a, group.b, group.c, group.d}) {
 auto& count
 = !block.is_valid ? bad_blocks
 : block.err_count ? corrected_blocks
 : good_blocks;
 ++count;
 }
 }
 void read_file(const std::string& filename)
 {
 reset();
 bits = good_blocks = corrected_blocks = bad_blocks = 0;
 std::ifstream in(filename, std::ifstream::binary);
 int c;
 while ((c = in.get()) != EOF) {
 for (int m = 0x80; m; m >>= 1)
 push(c & m);
 }
 // Summary statistics
 std::clog << filename << ": "
 << good_blocks << " good, "
 << corrected_blocks << " corrected, "
 << bad_blocks << " uncorrectable blocks; "
 << bits - 26 * (good_blocks + corrected_blocks + bad_blocks)
 << " undecoded bits"
 << std::endl;
 }
};
int main(int, char **argv)
{
 TestSynchronizer s;
 while (*++argv) {
 s.read_file(*argv);
 }
}

###Makefile

Makefile

Single-source version

#Single-source version ThisThis may be useful if you want to experiment with the code (in the case of any discrepancies, the multi-file version is authoritative). Compile using g++ -std=c++17 -Wall -Wextra -Werror.

###rdstypes.hh #ifndef RDSTYPES_HH #define RDSTYPES_HH

#include <cstdint>
namespace rds
{
 struct block {
 std::uint16_t value;
 std::uint8_t err_count = 0xff;
 bool is_valid = false;
 operator std::uint16_t() const { return value; }
 };
 struct group {
 block a, b, c, d;
 };
}
#endif /* RDSTYPES_HH */

###crc.hh #ifndef CRC_HH #define CRC_HH

#include <cstdint>
namespace rds
{
 // Figure B.1, page 61
 static constexpr std::uint32_t encode(std::uint16_t x)
 {
 return ((x >> 15) & 1) * 0b10000000000000000001110111
 ^ ((x >> 14) & 1) * 0b01000000000000001011100111
 ^ ((x >> 13) & 1) * 0b00100000000000001110101111
 ^ ((x >> 12) & 1) * 0b00010000000000001100001011
 ^ ((x >> 11) & 1) * 0b00001000000000001101011001
 ^ ((x >> 10) & 1) * 0b00000100000000001101110000
 ^ ((x >> 9) & 1) * 0b00000010000000000110111000
 ^ ((x >> 8) & 1) * 0b00000001000000000011011100
 ^ ((x >> 7) & 1) * 0b00000000100000000001101110
 ^ ((x >> 6) & 1) * 0b00000000010000000000110111
 ^ ((x >> 5) & 1) * 0b00000000001000001011000111
 ^ ((x >> 4) & 1) * 0b00000000000100001110111111
 ^ ((x >> 3) & 1) * 0b00000000000010001100000011
 ^ ((x >> 2) & 1) * 0b00000000000001001101011101
 ^ ((x >> 1) & 1) * 0b00000000000000101101110010
 ^ ((x >> 0) & 1) * 0b00000000000000010110111001;
 }
 // Figure B.3, page 63
 constexpr std::uint16_t syndrome(std::uint32_t y)
 {
 return ((y >> 25) & 1) * 0b1000000000
 ^ ((y >> 24) & 1) * 0b0100000000
 ^ ((y >> 23) & 1) * 0b0010000000
 ^ ((y >> 22) & 1) * 0b0001000000
 ^ ((y >> 21) & 1) * 0b0000100000
 ^ ((y >> 20) & 1) * 0b0000010000
 ^ ((y >> 19) & 1) * 0b0000001000
 ^ ((y >> 18) & 1) * 0b0000000100
 ^ ((y >> 17) & 1) * 0b0000000010
 ^ ((y >> 16) & 1) * 0b0000000001
 ^ ((y >> 15) & 1) * 0b1011011100
 ^ ((y >> 14) & 1) * 0b0101101110
 ^ ((y >> 13) & 1) * 0b0010110111
 ^ ((y >> 12) & 1) * 0b1010000111
 ^ ((y >> 11) & 1) * 0b1110011111
 ^ ((y >> 10) & 1) * 0b1100010011
 ^ ((y >> 9) & 1) * 0b1101010101
 ^ ((y >> 8) & 1) * 0b1101110110
 ^ ((y >> 7) & 1) * 0b0110111011
 ^ ((y >> 6) & 1) * 0b1000000001
 ^ ((y >> 5) & 1) * 0b1111011100
 ^ ((y >> 4) & 1) * 0b0111101110
 ^ ((y >> 3) & 1) * 0b0011110111
 ^ ((y >> 2) & 1) * 0b1010100111
 ^ ((y >> 1) & 1) * 0b1110001111
 ^ ((y >> 0) & 1) * 0b1100011011;
 }
}
#endif /* CRC_HH */

###corrections.cc // This program is run as part of the build, to generate the table of // corrections as C++ source, to be compiled into the program.

#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = false;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = true;
 else if (c != correction)
 correctable = false; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0] = {0, 0, true};
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}

###synchronizer.hh #ifndef SYNCHRONIZER_HH #define SYNCHRONIZER_HH

#include "rdstypes.hh"
#include <cstdint>
namespace rds {
class Synchronizer
{
 // No valid programme identifier has country code 0, so we can use
 // zero to indicate initial condition.
 std::uint32_t prog_id = 0;
 // Initial state needs to not look like 0BCD0
 static constexpr unsigned __int128 initial_state = 0xffff;
 // 128 bits can store a whole 104-bit group, plus 24 bits of
 // adjacent context.
 unsigned __int128 state = initial_state;
 static auto constexpr max_loss_blocks = 10;
 unsigned int bits_until_a = 0;
 unsigned int blocks_since_lock = 0;
 std::uint32_t last_good_b = 0;
public:
 static constexpr std::uint32_t CHECKWORD_A = 0x0fc;
 static constexpr std::uint32_t CHECKWORD_B = 0x198;
 static constexpr std::uint32_t CHECKWORD_C = 0x168;
 static constexpr std::uint32_t CHECKWORD_K = 0x350;
 static constexpr std::uint32_t CHECKWORD_D = 0x1b4;
 virtual ~Synchronizer() = default;
 std::uint16_t pid() const {
 return prog_id >> 10;
 }
 virtual void reset() {
 state = initial_state;
 prog_id = 0;
 perBitAction = &Synchronizer::search_for_lock;
 // other members will be set before we enter the locked state
 }
 void push(bool bit)
 {
 state <<= 1;
 state += bit;
 (this->*perBitAction)();
 }
protected:
 // @return error weight
 static rds::block correct(std::uint16_t checkword, std::uint32_t input);
 void (Synchronizer::*perBitAction)() = &Synchronizer::search_for_lock;
 void search_for_lock();
 void confirm_lock();
 void lock_found(std::uint16_t pid, unsigned int bits_to_go);
 void lock_lost();
 virtual void process_group(const rds::group& group);
};
}
#endif /* SYNCHRONIZER_HH */

###synchronizer.cc #include "synchronizer.hh"

#include "crc.hh"
#include <algorithm>
#include <array>
#include <cstdint>
using rds::Synchronizer;
namespace {
 struct Correction
 {
 std::uint16_t correction;
 unsigned char distance;
 bool correctable;
 };
 const std::array<Correction, 1024> corrections{{
#include "corrections.table"
 }};
 template<unsigned char size, typename... T>
 static constexpr int hamming_distance(T... v)
 {
 auto constexpr mask = (std::common_type_t<T...>(1) << size) - 1;
 return __builtin_popcount((mask & (... ^ v)));
 }
}
// @return error weight
rds::block Synchronizer::correct(std::uint16_t checkword, std::uint32_t input)
{
 input ^= checkword;
 auto s = syndrome(input);
 rds::block b = {static_cast<std::uint16_t>(input >> 10), 0, true};
 if (!s) {
 // perfect
 return b;
 }
 auto const& entry = corrections[s];
 b.value ^= entry.correction;
 b.err_count = entry.distance;
 b.is_valid = entry.correctable;
 return b;
}
void Synchronizer::search_for_lock()
{
 constexpr int MAX_ERR = 8;
 // Check self-similarity first, with 0.5% false-positive rate, before examining CRCs
 if (hamming_distance<26>(CHECKWORD_A, CHECKWORD_K, state, state >> 52) <= 6) {
 rds::block a,b,k,d;
 int err;
 if ((err = (k = correct(CHECKWORD_K, state)).err_count) <= MAX_ERR
 && (err += (a = correct(CHECKWORD_A, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (b = correct(CHECKWORD_B, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched A.K
 lock_found(a.value, 52);
 return;
 }
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (k = correct(CHECKWORD_K, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched K.A
 lock_found(a.value, 0);
 return;
 }
 }
 // Have we got a match with A...A? Again, we require >99.5% certainty before checking CRCs.
 // Note that we only have 24 bits remaining of the previous A, so fill in from the new one.
 if (hamming_distance<24>(state, state >> 104) <= 5) {
 rds::block a,b,c,d,e;
 int err;
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (e = correct(CHECKWORD_A, ((state >> 104) & 0xffffff) | (state & 0x3000000))).err_count) <= MAX_ERR
 && e.value == a.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR
 && (err += (c = correct(CHECKWORD_C, state >> 52)).err_count) <= MAX_ERR
 && (err += (b = correct(CHECKWORD_B, state >> 78)).err_count) <= MAX_ERR)
 {
 // we matched A...A
 lock_found(a.value, 0);
 return;
 }
 }
}
void Synchronizer::confirm_lock()
{
 if (--bits_until_a)
 return;
 bits_until_a = 104;
 rds::block a = correct(CHECKWORD_A, state);
 rds::block b = correct(CHECKWORD_B, state >> 78);
 rds::block c = correct(CHECKWORD_C, state >> 52);
 rds::block k = correct(CHECKWORD_K, state >> 52);
 rds::block d = correct(CHECKWORD_D, state >> 26);
 unsigned ck = std::min(k.err_count, c.err_count);
 if ((a.err_count >= 4 || a.value != prog_id>>10) && b.err_count * ck * d.err_count >= 16) {
 if (hamming_distance<25>(std::uint32_t(state), prog_id>>1) <= 5) {
 // bit slip - need one more bit
 bits_until_a = 1;
 } else if (hamming_distance<26>(std::uint32_t(state)>>1, prog_id) <= 5) {
 // bit slip - miss one bit
 bits_until_a = 103;
 } else {
 lock_lost();
 }
 return;
 }
 if (a.err_count == 0 && a.value != prog_id>>10) {
 // prog_id has changed
 prog_id = CHECKWORD_A ^ encode(a);
 }
 // reset counter
 blocks_since_lock = 0;
 if (b.err_count > 8) {
 // attempt to recover block 2 using saved PTY and inferred A/B
 // AAAABTPPPPPiiiii..........
 static constexpr auto pty_mask = 0b00000011111000000000000000;
 static constexpr auto ab_mask = 0b00001000000000000000000000;
 std::uint32_t block2 =
 ((~pty_mask & ~ab_mask) & (state >> 78))
 | (pty_mask & last_good_b)
 | (ab_mask & (c.err_count == k.err_count ? last_good_b : -1 * (c.err_count >= k.err_count)));
 auto b2 = correct(CHECKWORD_B, block2);
 if (b2.err_count >= b.err_count) {
 // couldn't decode the B block, so the rest is useless
 return;
 }
 } else {
 last_good_b = state >> 78;
 }
 process_group({a, b, c, d});
}
void Synchronizer::lock_found(std::uint16_t pid, unsigned int bits_to_go) {
 perBitAction = &Synchronizer::confirm_lock;
 prog_id = CHECKWORD_A ^ encode(pid);
 blocks_since_lock = 0;
 bits_until_a = 1 + bits_to_go;
 confirm_lock();
}
void Synchronizer::lock_lost() {
 if (++blocks_since_lock >= max_loss_blocks)
 perBitAction = &Synchronizer::search_for_lock;
}
void Synchronizer::process_group(const rds::group&)
{
 // to be overridden
}

###decode-file.cc #include "synchronizer.hh"

#include <fstream>
#include <iostream>
struct TestSynchronizer : rds::Synchronizer
{
 // keep track of some statistics
 unsigned long bits = 0;
 unsigned long good_blocks = 0;
 unsigned long corrected_blocks = 0;
 unsigned long bad_blocks = 0;
 void push(bool bit) {
 ++bits;
 rds::Synchronizer::push(bit);
 }
 void process_group(const rds::group& group) override
 {
 for (auto const& block: {group.a, group.b, group.c, group.d}) {
 auto& count
 = !block.is_valid ? bad_blocks
 : block.err_count ? corrected_blocks
 : good_blocks;
 ++count;
 }
 }
 void read_file(const std::string& filename)
 {
 reset();
 bits = good_blocks = corrected_blocks = bad_blocks = 0;
 std::ifstream in(filename, std::ifstream::binary);
 int c;
 while ((c = in.get()) != EOF) {
 for (int m = 0x80; m; m >>= 1)
 push(c & m);
 }
 // Summary statistics
 std::clog << filename << ": "
 << good_blocks << " good, "
 << corrected_blocks << " corrected, "
 << bad_blocks << " uncorrectable blocks; "
 << bits - 26 * (good_blocks + corrected_blocks + bad_blocks)
 << " undecoded bits"
 << std::endl;
 }
};
int main(int, char **argv)
{
 TestSynchronizer s;
 while (*++argv) {
 s.read_file(*argv);
 }
}

###Makefile

#Single-source version This may be useful if you want to experiment with the code (in the case of any discrepancies, the multi-file version is authoritative). Compile using g++ -std=c++17 -Wall -Wextra -Werror.

rdstypes.hh

#ifndef RDSTYPES_HH
#define RDSTYPES_HH
#include <cstdint>
namespace rds
{
 struct block {
 std::uint16_t value;
 std::uint8_t err_count = 0xff;
 bool is_valid = false;
 operator std::uint16_t() const { return value; }
 };
 struct group {
 block a, b, c, d;
 };
}
#endif /* RDSTYPES_HH */

crc.hh

#ifndef CRC_HH
#define CRC_HH
#include <cstdint>
namespace rds
{
 // Figure B.1, page 61
 static constexpr std::uint32_t encode(std::uint16_t x)
 {
 return ((x >> 15) & 1) * 0b10000000000000000001110111
 ^ ((x >> 14) & 1) * 0b01000000000000001011100111
 ^ ((x >> 13) & 1) * 0b00100000000000001110101111
 ^ ((x >> 12) & 1) * 0b00010000000000001100001011
 ^ ((x >> 11) & 1) * 0b00001000000000001101011001
 ^ ((x >> 10) & 1) * 0b00000100000000001101110000
 ^ ((x >> 9) & 1) * 0b00000010000000000110111000
 ^ ((x >> 8) & 1) * 0b00000001000000000011011100
 ^ ((x >> 7) & 1) * 0b00000000100000000001101110
 ^ ((x >> 6) & 1) * 0b00000000010000000000110111
 ^ ((x >> 5) & 1) * 0b00000000001000001011000111
 ^ ((x >> 4) & 1) * 0b00000000000100001110111111
 ^ ((x >> 3) & 1) * 0b00000000000010001100000011
 ^ ((x >> 2) & 1) * 0b00000000000001001101011101
 ^ ((x >> 1) & 1) * 0b00000000000000101101110010
 ^ ((x >> 0) & 1) * 0b00000000000000010110111001;
 }
 // Figure B.3, page 63
 constexpr std::uint16_t syndrome(std::uint32_t y)
 {
 return ((y >> 25) & 1) * 0b1000000000
 ^ ((y >> 24) & 1) * 0b0100000000
 ^ ((y >> 23) & 1) * 0b0010000000
 ^ ((y >> 22) & 1) * 0b0001000000
 ^ ((y >> 21) & 1) * 0b0000100000
 ^ ((y >> 20) & 1) * 0b0000010000
 ^ ((y >> 19) & 1) * 0b0000001000
 ^ ((y >> 18) & 1) * 0b0000000100
 ^ ((y >> 17) & 1) * 0b0000000010
 ^ ((y >> 16) & 1) * 0b0000000001
 ^ ((y >> 15) & 1) * 0b1011011100
 ^ ((y >> 14) & 1) * 0b0101101110
 ^ ((y >> 13) & 1) * 0b0010110111
 ^ ((y >> 12) & 1) * 0b1010000111
 ^ ((y >> 11) & 1) * 0b1110011111
 ^ ((y >> 10) & 1) * 0b1100010011
 ^ ((y >> 9) & 1) * 0b1101010101
 ^ ((y >> 8) & 1) * 0b1101110110
 ^ ((y >> 7) & 1) * 0b0110111011
 ^ ((y >> 6) & 1) * 0b1000000001
 ^ ((y >> 5) & 1) * 0b1111011100
 ^ ((y >> 4) & 1) * 0b0111101110
 ^ ((y >> 3) & 1) * 0b0011110111
 ^ ((y >> 2) & 1) * 0b1010100111
 ^ ((y >> 1) & 1) * 0b1110001111
 ^ ((y >> 0) & 1) * 0b1100011011;
 }
}
#endif /* CRC_HH */

corrections.cc

// This program is run as part of the build, to generate the table of
// corrections as C++ source, to be compiled into the program.
#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = false;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = true;
 else if (c != correction)
 correctable = false; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0] = {0, 0, true};
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}

synchronizer.hh

#ifndef SYNCHRONIZER_HH
#define SYNCHRONIZER_HH
#include "rdstypes.hh"
#include <cstdint>
namespace rds {
class Synchronizer
{
 // No valid programme identifier has country code 0, so we can use
 // zero to indicate initial condition.
 std::uint32_t prog_id = 0;
 // Initial state needs to not look like 0BCD0
 static constexpr unsigned __int128 initial_state = 0xffff;
 // 128 bits can store a whole 104-bit group, plus 24 bits of
 // adjacent context.
 unsigned __int128 state = initial_state;
 static auto constexpr max_loss_blocks = 10;
 unsigned int bits_until_a = 0;
 unsigned int blocks_since_lock = 0;
 std::uint32_t last_good_b = 0;
public:
 static constexpr std::uint32_t CHECKWORD_A = 0x0fc;
 static constexpr std::uint32_t CHECKWORD_B = 0x198;
 static constexpr std::uint32_t CHECKWORD_C = 0x168;
 static constexpr std::uint32_t CHECKWORD_K = 0x350;
 static constexpr std::uint32_t CHECKWORD_D = 0x1b4;
 virtual ~Synchronizer() = default;
 std::uint16_t pid() const {
 return prog_id >> 10;
 }
 virtual void reset() {
 state = initial_state;
 prog_id = 0;
 perBitAction = &Synchronizer::search_for_lock;
 // other members will be set before we enter the locked state
 }
 void push(bool bit)
 {
 state <<= 1;
 state += bit;
 (this->*perBitAction)();
 }
protected:
 // @return error weight
 static rds::block correct(std::uint16_t checkword, std::uint32_t input);
 void (Synchronizer::*perBitAction)() = &Synchronizer::search_for_lock;
 void search_for_lock();
 void confirm_lock();
 void lock_found(std::uint16_t pid, unsigned int bits_to_go);
 void lock_lost();
 virtual void process_group(const rds::group& group);
};
}
#endif /* SYNCHRONIZER_HH */

synchronizer.cc

#include "synchronizer.hh"
#include "crc.hh"
#include <algorithm>
#include <array>
#include <cstdint>
using rds::Synchronizer;
namespace {
 struct Correction
 {
 std::uint16_t correction;
 unsigned char distance;
 bool correctable;
 };
 const std::array<Correction, 1024> corrections{{
#include "corrections.table"
 }};
 template<unsigned char size, typename... T>
 static constexpr int hamming_distance(T... v)
 {
 auto constexpr mask = (std::common_type_t<T...>(1) << size) - 1;
 return __builtin_popcount((mask & (... ^ v)));
 }
}
// @return error weight
rds::block Synchronizer::correct(std::uint16_t checkword, std::uint32_t input)
{
 input ^= checkword;
 auto s = syndrome(input);
 rds::block b = {static_cast<std::uint16_t>(input >> 10), 0, true};
 if (!s) {
 // perfect
 return b;
 }
 auto const& entry = corrections[s];
 b.value ^= entry.correction;
 b.err_count = entry.distance;
 b.is_valid = entry.correctable;
 return b;
}
void Synchronizer::search_for_lock()
{
 constexpr int MAX_ERR = 8;
 // Check self-similarity first, with 0.5% false-positive rate, before examining CRCs
 if (hamming_distance<26>(CHECKWORD_A, CHECKWORD_K, state, state >> 52) <= 6) {
 rds::block a,b,k,d;
 int err;
 if ((err = (k = correct(CHECKWORD_K, state)).err_count) <= MAX_ERR
 && (err += (a = correct(CHECKWORD_A, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (b = correct(CHECKWORD_B, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched A.K
 lock_found(a.value, 52);
 return;
 }
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (k = correct(CHECKWORD_K, state >> 52)).err_count) <= MAX_ERR
 && a.value == k.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR)
 {
 // we matched K.A
 lock_found(a.value, 0);
 return;
 }
 }
 // Have we got a match with A...A? Again, we require >99.5% certainty before checking CRCs.
 // Note that we only have 24 bits remaining of the previous A, so fill in from the new one.
 if (hamming_distance<24>(state, state >> 104) <= 5) {
 rds::block a,b,c,d,e;
 int err;
 if ((err = (a = correct(CHECKWORD_A, state)).err_count) <= MAX_ERR
 && (err += (e = correct(CHECKWORD_A, ((state >> 104) & 0xffffff) | (state & 0x3000000))).err_count) <= MAX_ERR
 && e.value == a.value
 && (err += (d = correct(CHECKWORD_D, state >> 26)).err_count) <= MAX_ERR
 && (err += (c = correct(CHECKWORD_C, state >> 52)).err_count) <= MAX_ERR
 && (err += (b = correct(CHECKWORD_B, state >> 78)).err_count) <= MAX_ERR)
 {
 // we matched A...A
 lock_found(a.value, 0);
 return;
 }
 }
}
void Synchronizer::confirm_lock()
{
 if (--bits_until_a)
 return;
 bits_until_a = 104;
 rds::block a = correct(CHECKWORD_A, state);
 rds::block b = correct(CHECKWORD_B, state >> 78);
 rds::block c = correct(CHECKWORD_C, state >> 52);
 rds::block k = correct(CHECKWORD_K, state >> 52);
 rds::block d = correct(CHECKWORD_D, state >> 26);
 unsigned ck = std::min(k.err_count, c.err_count);
 if ((a.err_count >= 4 || a.value != prog_id>>10) && b.err_count * ck * d.err_count >= 16) {
 if (hamming_distance<25>(std::uint32_t(state), prog_id>>1) <= 5) {
 // bit slip - need one more bit
 bits_until_a = 1;
 } else if (hamming_distance<26>(std::uint32_t(state)>>1, prog_id) <= 5) {
 // bit slip - miss one bit
 bits_until_a = 103;
 } else {
 lock_lost();
 }
 return;
 }
 if (a.err_count == 0 && a.value != prog_id>>10) {
 // prog_id has changed
 prog_id = CHECKWORD_A ^ encode(a);
 }
 // reset counter
 blocks_since_lock = 0;
 if (b.err_count > 8) {
 // attempt to recover block 2 using saved PTY and inferred A/B
 // AAAABTPPPPPiiiii..........
 static constexpr auto pty_mask = 0b00000011111000000000000000;
 static constexpr auto ab_mask = 0b00001000000000000000000000;
 std::uint32_t block2 =
 ((~pty_mask & ~ab_mask) & (state >> 78))
 | (pty_mask & last_good_b)
 | (ab_mask & (c.err_count == k.err_count ? last_good_b : -1 * (c.err_count >= k.err_count)));
 auto b2 = correct(CHECKWORD_B, block2);
 if (b2.err_count >= b.err_count) {
 // couldn't decode the B block, so the rest is useless
 return;
 }
 } else {
 last_good_b = state >> 78;
 }
 process_group({a, b, c, d});
}
void Synchronizer::lock_found(std::uint16_t pid, unsigned int bits_to_go) {
 perBitAction = &Synchronizer::confirm_lock;
 prog_id = CHECKWORD_A ^ encode(pid);
 blocks_since_lock = 0;
 bits_until_a = 1 + bits_to_go;
 confirm_lock();
}
void Synchronizer::lock_lost() {
 if (++blocks_since_lock >= max_loss_blocks)
 perBitAction = &Synchronizer::search_for_lock;
}
void Synchronizer::process_group(const rds::group&)
{
 // to be overridden
}

decode-file.cc

#include "synchronizer.hh"
#include <fstream>
#include <iostream>
struct TestSynchronizer : rds::Synchronizer
{
 // keep track of some statistics
 unsigned long bits = 0;
 unsigned long good_blocks = 0;
 unsigned long corrected_blocks = 0;
 unsigned long bad_blocks = 0;
 void push(bool bit) {
 ++bits;
 rds::Synchronizer::push(bit);
 }
 void process_group(const rds::group& group) override
 {
 for (auto const& block: {group.a, group.b, group.c, group.d}) {
 auto& count
 = !block.is_valid ? bad_blocks
 : block.err_count ? corrected_blocks
 : good_blocks;
 ++count;
 }
 }
 void read_file(const std::string& filename)
 {
 reset();
 bits = good_blocks = corrected_blocks = bad_blocks = 0;
 std::ifstream in(filename, std::ifstream::binary);
 int c;
 while ((c = in.get()) != EOF) {
 for (int m = 0x80; m; m >>= 1)
 push(c & m);
 }
 // Summary statistics
 std::clog << filename << ": "
 << good_blocks << " good, "
 << corrected_blocks << " corrected, "
 << bad_blocks << " uncorrectable blocks; "
 << bits - 26 * (good_blocks + corrected_blocks + bad_blocks)
 << " undecoded bits"
 << std::endl;
 }
};
int main(int, char **argv)
{
 TestSynchronizer s;
 while (*++argv) {
 s.read_file(*argv);
 }
}

Makefile

Single-source version

This may be useful if you want to experiment with the code (in the case of any discrepancies, the multi-file version is authoritative). Compile using g++ -std=c++17 -Wall -Wextra -Werror.

Tweeted twitter.com/StackCodeReview/status/1100410335071096833
Improve corrections.cc according to comments (and make it clear that it runs at build time).
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325
#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = true;false;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = 1;true;
 else if (c != correction)
 correctable = 0;false; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0].distance = 0;{0, 0, true};
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}
#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = true;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = 1;
 else if (c != correction)
 correctable = 0; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0].distance = 0;
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}
#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = false;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = true;
 else if (c != correction)
 correctable = false; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0] = {0, 0, true};
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}
Improve corrections.cc according to comments (and make it clear that it runs at build time).
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325

###corrections.cc #include "crc// This program is run as part of the build, to generate the table of // corrections as C++ source, to be compiled into the program.hh"

#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = 0xf;~0;
 bool correctable = true;
 // Correction()
 // : correction(0), distance(0xf), correctable(0)
 // {}
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = 1;
 else if (c != correction)
 correctable = 0; // detect only
 }
};
int main()
{
 static auto const t =
 []{
  std::array<Correction, 1024> t;
 // Recieved exactly
 t[0].distance = 0;
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 return t;
 }();
 std::cout << std::boolalpha"// <<Generated std::showbase;
by corrections.cc - edits intmay ibe =overwritten 0;without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << std::hex << std::internal << std::setfill('0') << std::setw(6)
  << item.correction << ','
 << std::setfill(' ') << std::dec << 0u + item.distance << ','
 << std::setw(5) << item.correctable << "},";
 if (++i == 16) {
 std::cout << '\n';
 i = 0;
 }
 }
}

###corrections.cc #include "crc.hh"

#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = 0xf;
 bool correctable = true;
 // Correction()
 // : correction(0), distance(0xf), correctable(0)
 // {}
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = 1;
 else if (c != correction)
 correctable = 0; // detect only
 }
};
int main()
{
 static auto const t =
 []{
  std::array<Correction, 1024> t;
 // Recieved exactly
 t[0].distance = 0;
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 return t;
 }();
 std::cout << std::boolalpha << std::showbase;
 int i = 0;
 for (auto const& item: t) {
 std::cout << "{"
 << std::hex << std::internal << std::setfill('0') << std::setw(6)
  << item.correction << ','
 << std::setfill(' ') << std::dec << 0u + item.distance << ','
 << std::setw(5) << item.correctable << "},";
 if (++i == 16) {
 std::cout << '\n';
 i = 0;
 }
 }
}

###corrections.cc // This program is run as part of the build, to generate the table of // corrections as C++ source, to be compiled into the program.

#include "crc.hh"
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
struct Correction
{
 std::uint16_t correction = 0;
 unsigned char distance = ~0;
 bool correctable = true;
 void update(unsigned int c, unsigned int d)
 {
 if (d > distance)
 return; // already have a better correction
 else if (d < distance)
 distance = d, correction = c, correctable = 1;
 else if (c != correction)
 correctable = 0; // detect only
 }
};
int main()
{
 std::array<Correction, 1024> t;
 // Recieved exactly
 t[0].distance = 0;
 // 1-9 bit burst errors
 for (unsigned int dist = 1; dist < 10; ++dist) {
 const std::uint32_t first = (1u << (dist-1)) | 1;
 const std::uint32_t last = 1u << dist;
 for (std::uint32_t i = first; i < last; i+=2) {
 for (std::uint32_t j = i; !(j & 1u<<26); j <<=1)
 t[rds::syndrome(j)].update(j>>10, dist);
 }
 }
 // two independent 1-2 bit errors
 for (unsigned int dist = 9; dist < 24; ++dist) {
 for (std::uint32_t j: { ((1<<dist)+1), ((3<<dist)+1), ((1<<dist)+3), ((3<<dist)+3) }) {
 for (unsigned shift = 0; dist+shift < 26; ++shift) {
 t[rds::syndrome(j<<shift)].update(j<<shift>>10, 1 + __builtin_popcount(j));
 }
 }
 }
 std::cout << "// Generated by corrections.cc - edits may be overwritten without notice\n";
 for (auto const& item: t) {
 std::cout << "{"
 << item.correction << ','
 << 0u + item.distance << ','
 << item.correctable << "},";
 }
}
Make the single-file version generate its own corrections, to make it easier to play with the generator
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325
Loading
Word choice and typo fix
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325
Loading
Removed obsolete stuff from makefile
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325
Loading
Source Link
Toby Speight
  • 87.7k
  • 14
  • 104
  • 325
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /