├── .gitignore ├── README.md ├── src ├── binarymatrix.h ├── binaryprint.h ├── bitmaskcombination.h ├── bitpacker.h ├── blockcode.h ├── convolutional-decoder.h ├── convolutional-encoder.h ├── crc.h ├── fecmagic-global.h ├── golay.h ├── hamming.h └── sequence.h └── tests ├── helper.h ├── test_binarymatrix.cpp ├── test_binaryprint.cpp ├── test_bitmaskcombination.cpp ├── test_bitpacker.cpp ├── test_convolutional_decoder.cpp ├── test_convolutional_encoder.cpp ├── test_crc.cpp ├── test_golay.cpp └── test_hamming.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | *.ko 7 | *.elf 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.so.* 16 | *.dylib 17 | *.dll 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Compiled executables 26 | *.exe 27 | *.out 28 | *.app 29 | *.hex 30 | 31 | # Qt-specific files 32 | /.qmake.cache 33 | /.qmake.stash 34 | *.pro.user 35 | *.pro.user.* 36 | *.qbs.user 37 | *.qbs.user.* 38 | *.moc 39 | moc_*.cpp 40 | qrc_*.cpp 41 | ui_*.h 42 | Makefile* 43 | *build-* 44 | 45 | # Qt Creator files 46 | *.autosave 47 | 48 | # QtCtreator Qml 49 | *.qmlproject.user 50 | *.qmlproject.user.* 51 | 52 | # QtCtreator CMake 53 | CMakeLists.txt.user 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Welcome to fecmagic 3 | ========== 4 | 5 | fecmagic is a header-only C++ library which implements some FEC (forward error correction) 6 | codes and some utility functions that help developing and testing such codes. 7 | 8 | What's in the box? 9 | ---------- 10 | 11 | Currently, we've got the following: 12 | 13 | * A generic block code algorithm that works with binary matrix operations, and has 14 | a generic method of syndrome-based decoding. 15 | * A generic convolutional codec that is implemented using variadic 16 | templates and can be configured to work with any convolutional code. 17 | The encoder implements the classical "shift register" algorithm, and 18 | the decoder implements a hard-decision Viterbi algorithm. 19 | 20 | And we have specialized codecs for: 21 | 22 | * Hamming(7, 4) 23 | * Golay(24, 12) 24 | 25 | And for your convenience, we've also got: 26 | 27 | * A way to work with binary matrices efficiently 28 | * A way to print a number in binary 29 | * A way to iterate through bit mask combinations 30 | 31 | 32 | 33 | How to use 34 | ---------- 35 | 36 | All the algorithms are implemented in modern C++ and should compile on a C++11 compiler. 37 | The tests use the `0b` notation for binary numbers, so you need C++14 to compile the tests. 38 | 39 | Since this is a header-only library, you can simply copy the necessary header files, include 40 | them and you're good to go. 41 | 42 | Contributions 43 | ---------- 44 | 45 | ### Testing and bugreports 46 | 47 | This code is primarily tested using GCC on Linux x86. If you find bugs or issues on other compilers 48 | or platforms, feel free to open a bug report. 49 | 50 | Each piece of code has a small test application. Some of these (Hamming and Golay) cover all possible 51 | inputs, and some just contain basic tests to see if stuff works. These tests will be further expanded 52 | as needed. 53 | 54 | ### Code 55 | 56 | If you think you can improve on our code or add other features, your contribution is welcome. 57 | Feel free to send us a pull request. Thanks in advance! :) 58 | 59 | ### License 60 | 61 | The code is licensed to you under the terms of the MIT license. 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/binarymatrix.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef BINARYMATRIX_H 25 | #define BINARYMATRIX_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "binaryprint.h" 40 | #include "fecmagic-global.h" 41 | 42 | namespace fecmagic { 43 | 44 | /** 45 | * @brief Represents a matrix that consists of zeroes and ones, with the given number of rows and columns. 46 | * 47 | * The BinaryMatrix class is an efficient implementation of matrices in GF(2) 48 | * that can only consist of zeroes and ones. It is intended for coding applications 49 | * such as block coders and interleavers. 50 | * It has the ability to multiply the matrix with a GF(2) vector and an efficient 51 | * transposition operation. 52 | * 53 | * Number of rows and columns are configurable but both must be multiples of 8 because 54 | * of internal implementation details. 55 | * 56 | * Template parameters: 57 | * - Rows: the number of rows, must be a multiple of 8 58 | * - Cols: the number of columns, must be a multiple of 8 59 | * 60 | */ 61 | template 62 | class BinaryMatrix final { 63 | private: 64 | // Assert correct row and column numbers 65 | static_assert(Cols % 8 == 0, "BinaryMatrix: template parameter Cols must be a multiple of 8."); 66 | static_assert(Rows % 8 == 0, "BinaryMatrix: template parameter Rows must be a multiple of 8."); 67 | 68 | // Friend: stream operator for debug printing purposes 69 | template 70 | friend std::ostream &::operator<<(std::ostream &os, const BinaryMatrix &matrix); 71 | 72 | // Friend: transposed BinaryMatrix type 73 | friend class BinaryMatrix; 74 | 75 | // Number of bytes required to hold the matrix 76 | static const constexpr std::size_t byteCount = (Cols / 8) * Rows; 77 | 78 | // Bytes that hold the matrix 79 | std::uint8_t bytes[byteCount]; 80 | 81 | // Gets the upper 32 bits of the result of a 32-bit multiplication operation 82 | // NOTE: the result of 32-bit multiplication results in a 64-bit number, and this function returns the upper 32 bits of that 83 | inline static uint32_t getMultiplyUpperPart(const uint32_t &i1, const uint32_t &i2) { 84 | return static_cast((static_cast(i1) * static_cast(i2)) >> 32); 85 | } 86 | 87 | public: 88 | /** 89 | * Creates an empty BinaryMatrix instance, filled with zeroes. 90 | */ 91 | constexpr inline explicit BinaryMatrix() { 92 | std::memset(bytes, 0, byteCount); 93 | } 94 | 95 | /** 96 | * Creates a BinaryMatrix instance by copying a byte array. 97 | */ 98 | constexpr inline explicit BinaryMatrix(std::uint8_t *b) { 99 | std::memcpy(bytes, b, byteCount); 100 | } 101 | 102 | /** 103 | * Creates a BinaryMatrix instance from an initializer list of bytes that are copied. 104 | */ 105 | constexpr inline BinaryMatrix(const std::initializer_list &init) { 106 | size_t i = 0; 107 | for (auto it = init.begin(); it != init.end(); it++) { 108 | bytes[i++] = *it; 109 | } 110 | } 111 | 112 | /** 113 | * Gets the internal buffer that stores the bytes that make up the matrix. 114 | */ 115 | constexpr inline const uint8_t *getBytes() const { 116 | return bytes; 117 | } 118 | 119 | /** 120 | * Gets the number of bytes the matrix needs to store its data. 121 | */ 122 | constexpr inline static std::size_t getByteCount() { 123 | return byteCount; 124 | } 125 | 126 | /** 127 | * Sets a specific bit in the matrix. 128 | */ 129 | inline void setBit(uint32_t row, uint32_t col, uint8_t bit) { 130 | // A bit is either 0 or 1. 131 | assert(bit == 0 || bit == 1); 132 | 133 | // Position within the byte array 134 | uint32_t bytecol = col / 8; 135 | uint32_t bitcol = col % 8; 136 | uint32_t targetAddr = row * (Cols / 8) + bytecol; 137 | 138 | // Mask 139 | uint32_t mask = ~(1 << (7 - bitcol)); 140 | 141 | // Clear the bit 142 | this->bytes[targetAddr] &= mask; 143 | 144 | // Set the bit 145 | this->bytes[targetAddr] |= (bit << (7 - bitcol)); 146 | } 147 | 148 | /** 149 | * Gets a specific bit from the matrix. 150 | */ 151 | inline uint8_t getBit(uint32_t row, uint32_t col) const { 152 | // Position within the byte array 153 | uint32_t bytecol = col / 8; 154 | uint32_t bitcol = col % 8; 155 | uint32_t targetAddr = row * (Cols / 8) + bytecol; 156 | 157 | // Get the bit 158 | uint8_t result = (this->bytes[targetAddr] >> (7 - bitcol)) & 1; 159 | // std::cout << "getBit(" << row << "," << col << "): addr=" << targetAddr << " byte=" << BinaryPrint(this->bytes[targetAddr]) << ", bit=" << (unsigned)result << std::endl; 160 | return result; 161 | } 162 | 163 | /** 164 | * Gets a pointer to the memory that stores a specified row. 165 | */ 166 | inline const uint8_t *getRow(uint32_t rowNumber) const { 167 | return (this->bytes + (rowNumber * (Cols / 8))); 168 | } 169 | 170 | /** 171 | * @brief Calculates the product of the matrix and a GF(2) vector. 172 | * 173 | * This function multiplies the current GF(2) matrix with a GF(2) vector. 174 | * 175 | * Template parameters: 176 | * - Tin: the type that holds the GF(2) vector with which you wish to multiply 177 | * - Tout: the type that holds the GF(2) vector that is the result of the multiplication 178 | */ 179 | template 180 | inline Tout calculateProduct(const Tin &vec) const { 181 | // TODO: accelerate using SSE on Intel processors 182 | // TODO: accelerate using NEON on Cortex-A processors 183 | 184 | static_assert((sizeof(Tin) * 8) == Cols, "The input vector must have as many rows as the columns of the matrix."); 185 | static_assert((sizeof(Tout) * 8) >= Rows, "The output vector must have at least as many rows as the rows of the matrix."); 186 | 187 | Tout result = 0; 188 | const uint8_t *v = reinterpret_cast(&vec); 189 | 190 | // Go through each row 191 | for (uint8_t i = 0; i < Rows; i++) { 192 | // Shift the result 193 | result <<= 1; 194 | 195 | // Go through each column (in one-byte leaps) and compute the total parity of the row 196 | // TODO: speed this up by using more bits at the same time 197 | uint8_t parity = 0; 198 | for (uint8_t j = 0; j < sizeof(Tin); j++) { 199 | // Get byte from matrix 200 | uint8_t b1 = bytes[(i * Cols / 8) + j]; 201 | // Get byte from vector (compensate for little endianness) 202 | uint8_t b2 = v[sizeof(Tin) - j - 1]; 203 | // Compute the parity 204 | parity ^= computeParity(b1 & b2); 205 | // std::cout << "bytes[...]=" << BinaryPrint(bytes[(i * Cols / 8) + j]) << ", v[...]=" << BinaryPrint(v[sizeof(Tin) - j - 1]) << std::endl; 206 | } 207 | // Set the LSB of the result 208 | result |= parity; 209 | 210 | // std::cout << "(row " << (unsigned)i << ": " << (unsigned)parity << ", result=" << BinaryPrint(result) << ")" << std::endl; 211 | } 212 | 213 | return result; 214 | } 215 | 216 | /** 217 | * Calculates the product of the current matrix and another. 218 | */ 219 | template 220 | BinaryMatrix calculateProduct(const BinaryMatrix &other) const { 221 | // Result matrix 222 | BinaryMatrix result; 223 | 224 | // Columns become rows in the transposed matrix, so we can work with them easily 225 | auto otherTransposed = other.transpose(); 226 | 227 | // Go through each row in the current matrix 228 | for (uint32_t i = 0; i < Rows; i++) { 229 | auto *r1 = this->getRow(i); 230 | 231 | // Go through each row in the transposed other matrix 232 | // (which correspond to columns in the original other matrix) 233 | for (uint32_t j = 0; j < X; j++) { 234 | auto *r2 = otherTransposed.getRow(j); 235 | 236 | // TODO: speed this up by using more bits at the same time 237 | uint8_t parity = 0; 238 | for (uint8_t x = 0; x < (Cols / 8); x++) { 239 | parity ^= computeParity(r1[x] & r2[x]); 240 | } 241 | 242 | result.setBit(i, j, parity); 243 | } 244 | } 245 | 246 | return result; 247 | } 248 | 249 | /** 250 | * @brief Transposes the matrix. 251 | * 252 | * This method uses SSE2 instructions on processors where they are available. 253 | * Otherwise it falls back to a generic 32-bit implementation. 254 | */ 255 | BinaryMatrix transpose() const { 256 | // TODO: add support for NEON on Cortex-A processors 257 | 258 | BinaryMatrix result; 259 | 260 | unsigned row, col; 261 | int i; 262 | 263 | if (SSE2_SUPPORTED) { 264 | #ifndef COMPILE_SSE2_CODE 265 | //#pragma message "Not compiling SSE2 code" 266 | #else 267 | //#pragma message "Compiling SSE2 code" 268 | 269 | // Variable that holds a 16x8 submatrix in a 128-bit register 270 | union { 271 | __m128i x; 272 | uint8_t b[16]; 273 | } m16x8; 274 | 275 | // Process what we can in 16x8 blocks 276 | for (row = 0; row + 16 <= Rows; row += 16) { 277 | for (col = 0; col < Cols; col += 8) { 278 | // Fill the 16x8 submatrix, by putting all the bytes into a 128-bit register 279 | // (Input is 16 pieces of uint8_t) 280 | // NOTE: the resulting uint16_t must be put into its place taking into accunt endianness, 281 | // so here we play a small trick with the order of how things are handled. 282 | for (i = 0; i < 8; i++) { 283 | // Figure out where we're taking the byte from 284 | unsigned sourceAddr = ((row + i) * (Cols / 8)) + (col / 8); 285 | // Put the byte into the 128-bit register (in reverse order) 286 | m16x8.b[7 - i] = this->bytes[sourceAddr]; 287 | } 288 | for (i = 8; i < 16; i++) { 289 | // Figure out where we're taking the byte from 290 | unsigned sourceAddr = ((row + i) * (Cols / 8)) + (col / 8); 291 | // Put the byte into the 128-bit register (in reverse order) 292 | m16x8.b[15 - i + 8] = this->bytes[sourceAddr]; 293 | } 294 | // std::cout << BinaryPrint<__m128i>(m16x8.x, "|") << std::endl; 295 | 296 | // Transpose the submatrix with clever SSE intrinsics 297 | // (Output is 8 pieces of uint16_t) 298 | for (i = 0; i < 8; i++) { 299 | // Figure out where the current 16-bit row should go 300 | unsigned targetAddr = ((col + i) * (Rows / 8) + (row) / 8); 301 | // std::cout << (unsigned)targetAddr << std::endl; 302 | 303 | // Create a transposed 16-bit row by taking the MSBs of the 8-bit bytes in the 128-bit register 304 | uint16_t transposedRow = _mm_movemask_epi8(m16x8.x); 305 | // std::cout << BinaryPrint(transposedRow, ":") << std::endl; 306 | 307 | // Put the 16-bit row into its destination 308 | *reinterpret_cast(result.bytes + targetAddr) = transposedRow; 309 | // Shift the 128-bit register left by 1 bit 310 | m16x8.x = _mm_slli_epi64(m16x8.x, 1); 311 | } 312 | } 313 | } 314 | 315 | if (row != Rows) { 316 | // There are 8 rows remaining. 317 | 318 | // Set the 128-bit register to zero 319 | for (i = 0; i < 16; i++) { 320 | m16x8.b[i] = 0; 321 | } 322 | 323 | // Process the remaining rows in 8x8 units 324 | for (col = 0; col < Cols; col += 8) { 325 | // Fill half of the 128-bit register with the 8x8 submatrix 326 | for (i = 0; i < 8; i++) { 327 | unsigned sourceAddr = (row + i) * (Cols / 8) + col / 8; 328 | m16x8.b[7 - i] = this->bytes[sourceAddr]; 329 | } 330 | 331 | // Transpose the 8x8 submatrix 332 | for (i = 0; i < 8; i++) { 333 | // Create a transposed 8-bit row by taking the MSBs of the 8-bit bytes in the 128-bit register 334 | uint8_t transposedRow = _mm_movemask_epi8(m16x8.x); 335 | // Figure out where the current 16-bit row should go 336 | unsigned targetAddr = ((col + i) * (Rows / 8) + (row) / 8); 337 | // Put the 8-bit row into its destination 338 | *(result.bytes + targetAddr) = transposedRow; 339 | // Shift the 128-bit register left by 1 bit 340 | m16x8.x = _mm_slli_epi64(m16x8.x, 1); 341 | } 342 | } 343 | } 344 | #endif 345 | } 346 | else { 347 | // The following code works on any 32-bit architecture. 348 | // We do the same trick as above but using a 32-bit variable, 349 | // which means we can work with the matrix in dual 4×8 blocks 350 | 351 | // Variable that holds two 4×8 submatrices 352 | union { 353 | uint32_t x[2]; 354 | uint8_t b[8]; 355 | } m4x8d; 356 | 357 | for (row = 0; row < Rows; row += 8) { 358 | for (col = 0; col < Cols; col += 8) { 359 | // Fill the two 4×8 submatrices 360 | // (Input is 8 pieces of uint8_t) 361 | for (i = 0; i < 8; i++) { 362 | uint32_t sourceAddr = (row + i) * (Cols / 8) + col / 8; 363 | m4x8d.b[7 - i] = this->bytes[sourceAddr]; 364 | 365 | 366 | // std::cout << "input" << i << ": " << BinaryPrint(this->bytes[sourceAddr]) << std::endl; 367 | } 368 | 369 | 370 | // std::cout << "packed input: " << BinaryPrint(m4x8d.x[0]) << std::endl; 371 | // std::cout << "packed input: " << BinaryPrint(m4x8d.x[1]) << std::endl; 372 | 373 | uint32_t targetAddr; 374 | 375 | for (i = 0; i < 7; i++) { 376 | targetAddr = ((col + i) * (Rows / 8) + (row) / 8); 377 | result.bytes[targetAddr] = static_cast(getMultiplyUpperPart(m4x8d.x[1] & (0x80808080 >> i), (0x02040810 << i)) & 0x0f) << 4; 378 | result.bytes[targetAddr] |= static_cast(getMultiplyUpperPart(m4x8d.x[0] & (0x80808080 >> i), (0x02040810 << i)) & 0x0f) << 0; 379 | 380 | // std::cout << "output" << i << ": " << BinaryPrint(result.bytes[targetAddr]) << std::endl; 381 | } 382 | 383 | targetAddr = ((col + 7) * (Rows / 8) + (row) / 8); 384 | result.bytes[targetAddr] = static_cast(getMultiplyUpperPart((m4x8d.x[1] << 7) & (0x80808080 >> 0), (0x02040810 << 0)) & 0x0f) << 4; 385 | result.bytes[targetAddr] |= static_cast(getMultiplyUpperPart((m4x8d.x[0] << 7) & (0x80808080 >> 0), (0x02040810 << 0)) & 0x0f) << 0; 386 | 387 | // std::cout << "output7: " << BinaryPrint(result.bytes[targetAddr]) << std::endl; 388 | } 389 | } 390 | 391 | } 392 | 393 | return result; 394 | } 395 | 396 | inline bool isZero() const { 397 | // Check all bytes for zero 398 | for (size_t i = 0; i < BinaryMatrix::byteCount; i++) { 399 | // If any byte is non-zero, return false 400 | if (this->bytes[i] != 0) { 401 | return false; 402 | } 403 | } 404 | 405 | // If all bytes are zero, return true 406 | return true; 407 | } 408 | 409 | inline bool operator==(const BinaryMatrix &other) const { 410 | return memcmp(other.bytes, this->bytes, BinaryMatrix::byteCount) == 0; 411 | } 412 | 413 | inline bool operator!=(const BinaryMatrix &other) const { 414 | return memcmp(other.bytes, this->bytes, BinaryMatrix::byteCount) != 0; 415 | } 416 | 417 | /** Returns the number of rows in the matrix. */ 418 | inline std::uint32_t rows() const { 419 | return Rows; 420 | } 421 | 422 | /** Returns the number of columns in the matrix. */ 423 | inline std::uint32_t cols() const { 424 | return Cols; 425 | } 426 | 427 | }; 428 | 429 | } 430 | 431 | /** Prints a binary matrix, for debugging purposes */ 432 | template 433 | ::std::ostream &operator<<(::std::ostream &os, const ::fecmagic::BinaryMatrix &matrix) { 434 | for (uint8_t i = 0; i < Rows; i++) { 435 | for (uint8_t j = 0; j < Cols / 8; j++) { 436 | os << ::fecmagic::BinaryPrint(matrix.bytes[i * Cols / 8 + j]); 437 | } 438 | os << std::endl; 439 | } 440 | 441 | return os; 442 | } 443 | 444 | #endif // BINARYMATRIX_H 445 | 446 | -------------------------------------------------------------------------------- /src/binaryprint.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef BINARYPRINT_H 25 | #define BINARYPRINT_H 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace fecmagic { 32 | 33 | // Forward-declaration of the BinaryPrint class 34 | template 35 | class BinaryPrint; 36 | 37 | } 38 | 39 | // Forward-declaration of the stream operators for BinaryPrint 40 | template 41 | ::std::ostream &operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint &bp); 42 | #if defined(COMPILE_SSE2_CODE) 43 | ::std::ostream &operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint<__m128i> &bp); 44 | #endif // defined(COMPILE_SSE2_CODE) 45 | 46 | namespace fecmagic { 47 | 48 | /** 49 | * Convenience class for printing integers in binary form. 50 | */ 51 | template 52 | class BinaryPrint { 53 | private: 54 | // Friend: generic stream operator 55 | template 56 | friend ::std::ostream &::operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint &bp); 57 | 58 | #if defined(COMPILE_SSE2_CODE) 59 | // Friend: specialized stream operator 60 | friend ::std::ostream &::operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint<__m128i> &bp); 61 | #endif // defined(COMPILE_SSE2_CODE) 62 | 63 | // Wrapped value 64 | T val; 65 | 66 | // Separator 67 | ::std::string separator; 68 | 69 | public: 70 | /** 71 | * Constructs an instance of the BinaryPrint class. 72 | */ 73 | inline explicit BinaryPrint(const T &v, std::string sep = std::string()) 74 | : val(v), separator(sep) { } 75 | 76 | }; 77 | 78 | } 79 | 80 | /** 81 | * Prints a BinaryPrint instance into a standard stream. 82 | */ 83 | template 84 | inline ::std::ostream &operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint &bp) { 85 | // Current bit index 86 | uint32_t x = 0; 87 | // Go through all bits with a mask 88 | for (T i = (1 << (sizeof(T) * 8 - 1)); i; i >>= 1) { 89 | // If appropriate, print a separator 90 | if ((x % 8 == 0) && (x != 0)) { 91 | os << bp.separator; 92 | } 93 | 94 | // Print current bit 95 | os << ((bp.val & i) ? "1" : "0"); 96 | // Increase current bit index 97 | x++; 98 | } 99 | 100 | return os; 101 | } 102 | 103 | #if defined(COMPILE_SSE2_CODE) 104 | /** 105 | * Prints a specialized SSE BinaryPrint instance into a standard stream. 106 | */ 107 | inline ::std::ostream &operator<<(::std::ostream &os, const ::fecmagic::BinaryPrint<__m128i> &bp) { 108 | // TODO: ifdef this function 109 | 110 | // Treat the 128-bit register as 16 pieces of 8-bit values 111 | union { __m128i x; uint8_t b[16]; } u; 112 | u.x = bp.val; 113 | 114 | // Print the 8-bit values individually 115 | for (uint8_t i = 0; i < 16; i++) { 116 | os << ::fecmagic::BinaryPrint(u.b[i]) << bp.separator; 117 | // TODO: don't print last separator 118 | } 119 | 120 | return os; 121 | } 122 | #endif 123 | 124 | #endif // BINARYPRINT_H 125 | 126 | -------------------------------------------------------------------------------- /src/bitmaskcombination.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef BITMASKCOMBINATION_H 25 | #define BITMASKCOMBINATION_H 26 | 27 | #include 28 | #include 29 | 30 | namespace fecmagic { 31 | 32 | /** 33 | * @brief Produces all possible combinations of a bitmask with the given number of bits set. 34 | * 35 | * This class can be used to produce bitmask combinations, which is useful for 36 | * testing forward error correction codes. It can be used, for example, to produce 37 | * all possible combinations of bit errors. 38 | * 39 | * Template parameters: 40 | * - T: the integral type in which to produce the combinations 41 | * - MaxN: the maximum number of allowed set bits (used for sizing an array) 42 | */ 43 | template 44 | class BitmaskCombination final { 45 | private: 46 | // Assert T to be an integral type 47 | static_assert(std::is_integral::value, "T must be an integral type."); 48 | 49 | // Number of set bits 50 | unsigned n_; 51 | 52 | // Position of each set bit 53 | unsigned x[MaxN]; 54 | 55 | // Whether we are done iterating or not 56 | bool done; 57 | 58 | // Produces the current bit mask from the positions of each set bit 59 | inline T currentMask() const { 60 | // Initialize empty result 61 | T result = 0; 62 | 63 | // Go through each position and set the bits accordingly 64 | for (unsigned i = 0; i < n_; i++) { 65 | result |= (1 << (Length - x[i] - 1)); 66 | } 67 | 68 | return result; 69 | } 70 | 71 | public: 72 | /** 73 | * Constructs an instance of the BitmaskCombination class. 74 | * Parameters: 75 | * - n: the number of set bits 76 | */ 77 | inline explicit BitmaskCombination(unsigned n = MaxN) 78 | : n_(n), done(false) { 79 | 80 | // Number of combinations must not exceed maximum number of combinations 81 | assert(n_ <= MaxN); 82 | 83 | // Initialize the array in ascending order 84 | for (unsigned i = 0; i < n_; i++) { 85 | x[i] = i; 86 | } 87 | 88 | // If zero combinations are allowed, we are already done 89 | if (n_ == 0) { 90 | done = true; 91 | } 92 | } 93 | 94 | /** 95 | * Returns the next bit mask combination. 96 | */ 97 | T next() { 98 | // Check if we are done with all the possible combinations 99 | if (done) { 100 | return 0; 101 | } 102 | 103 | // Get the current combination (this is the current result) 104 | T result = currentMask(); 105 | 106 | // Advance the combination: 107 | // Go through the array backwards 108 | for (int i = n_ - 1; i >= 0; i--) { 109 | // See if the current element is at the max value 110 | if (x[i] == (Length - (n_ - i))) { 111 | // Check if this is the last possible combination 112 | if (i == 0) { 113 | done = true; 114 | break; 115 | } 116 | else { 117 | // Set position of current element 118 | x[i] = x[i - 1] + 2; 119 | 120 | // Fix positions of following elements 121 | for (unsigned j = i + 1; j < n_; j++) { 122 | x[j] = x[j - 1] + 1; 123 | } 124 | } 125 | } 126 | else { 127 | // Increase current element 128 | x[i] ++; 129 | 130 | // No need to loop further 131 | break; 132 | } 133 | } 134 | 135 | return result; 136 | } 137 | 138 | }; 139 | 140 | } 141 | 142 | #endif // BITMASKCOMBINATION_H 143 | 144 | -------------------------------------------------------------------------------- /src/bitpacker.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef BITPACKER_H_ 25 | #define BITPACKER_H_ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | //#define BITPACKER_DEBUG 33 | #ifdef BITPACKER_DEBUG 34 | # include 35 | # include "binaryprint.h" 36 | # define DEBUG_PRINT(x) (::std::cout << "[BitPacker] " << x << std::endl); 37 | #else 38 | # define DEBUG_PRINT(x) 39 | #endif 40 | 41 | namespace fecmagic { 42 | 43 | template 44 | class BitPacker final { 45 | 46 | private: 47 | 48 | uint_fast8_t shift_ = 0; 49 | size_t pos_ = 0; 50 | uint8_t *output_; 51 | 52 | public: 53 | 54 | BitPacker(uint8_t *output) 55 | : output_(output) { } 56 | 57 | void pack(TBlock block) { 58 | DEBUG_PRINT("pos=" << pos_ << ", shift=" << (uint32_t)shift_); 59 | uint8_t x[affectedByteCount]; 60 | 61 | constexpr uint32_t l = affectedByteCount - 1; 62 | uint32_t sh; 63 | for (size_t i = 0; i < l; i++) { 64 | sh = blockSizeInBits - (i * 8) - shift_; 65 | x[i] = (block >> (sh & 0xff)); 66 | DEBUG_PRINT("x[" << i << "] = " << BinaryPrint(x[i]) << " sh=" << sh); 67 | } 68 | sh = (l * 8) - blockSizeInBits + shift_; 69 | x[l] = (block << (sh) & 0xff); 70 | DEBUG_PRINT("x[" << l << "] = " << BinaryPrint(x[l]) << " sh=" << sh); 71 | 72 | if (shift_ != 0) { 73 | output_[pos_] |= x[0]; 74 | DEBUG_PRINT("out[" << pos_ << "] = " << BinaryPrint(output_[pos_])); 75 | pos_++; 76 | } 77 | 78 | shift_++; 79 | shift_ %= 8; 80 | 81 | for (size_t i = 1; i < affectedByteCount; i++) { 82 | output_[pos_] = x[i]; 83 | DEBUG_PRINT("out[" << pos_ << "] = " << BinaryPrint(output_[pos_])); 84 | if (i != (affectedByteCount - 1)) { 85 | pos_++; 86 | } 87 | } 88 | } 89 | 90 | }; 91 | 92 | template 93 | class BitUnpacker final { 94 | 95 | private: 96 | 97 | uint_fast8_t shift_ = 0; 98 | size_t pos_ = 0; 99 | const uint8_t *input_; 100 | 101 | public: 102 | 103 | BitUnpacker(const uint8_t *input) 104 | : input_(input) { } 105 | 106 | TBlock unpack() { 107 | constexpr TBlock blockMask = ::std::numeric_limits::max() >> ((sizeof(TBlock) * 8) - blockSizeInBits); 108 | DEBUG_PRINT("pos = " << pos_); 109 | 110 | uint8_t x[affectedByteCount]; 111 | if (shift_ == 0) { 112 | x[0] = 0; 113 | memcpy(x + 1, input_ + pos_, affectedByteCount - 1); 114 | } 115 | else { 116 | memcpy(x, input_ + pos_, affectedByteCount); 117 | } 118 | 119 | TBlock block = 0; 120 | 121 | constexpr uint32_t l = affectedByteCount - 1; 122 | uint32_t sh; 123 | for (size_t i = 0; i < l; i++) { 124 | sh = blockSizeInBits - shift_ - (i * 8); 125 | block |= (((TBlock)x[i]) << sh); 126 | DEBUG_PRINT("x[" << i << "] = " << BinaryPrint(x[i]) << " sh=" << sh); 127 | } 128 | sh = (l * 8) - blockSizeInBits + shift_; 129 | block |= (((TBlock)x[l]) >> sh); 130 | DEBUG_PRINT("x[" << l << "] = " << BinaryPrint(x[l]) << " sh=" << sh); 131 | 132 | if (shift_ == 0) { 133 | pos_ += (affectedByteCount - 2); 134 | } 135 | else { 136 | pos_ += (affectedByteCount - 1); 137 | } 138 | 139 | shift_++; 140 | shift_ %= 8; 141 | 142 | return block & blockMask; 143 | } 144 | 145 | }; 146 | 147 | } 148 | 149 | #endif // BITPACKER_H_ 150 | 151 | -------------------------------------------------------------------------------- /src/blockcode.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef BLOCKCODE_H 25 | #define BLOCKCODE_H 26 | 27 | #include "binaryprint.h" 28 | #include "binarymatrix.h" 29 | #include "bitmaskcombination.h" 30 | #define ROUND_8(x) ((x) + (((x) % 8) > 0 ? (8 - ((x) % 8)) : 0)) 31 | 32 | namespace fecmagic { 33 | 34 | /** 35 | * @brief Base class for block codes. 36 | * 37 | * The BlockCode class implements basic functionality that is useful 38 | * for all block codes. It only needs the necessary matrices to work. 39 | * However, this class only contains a very wasteful algorithm for 40 | * decoding, so it is suggested to create derived classes for specific 41 | * block codes that override the decode method. 42 | * 43 | * Template parameters: 44 | * - Maximum number errors that can be corrected by the code 45 | * - Type used for storing codewords 46 | * - Type used for storing a source block 47 | * - Type used for storing syndromes 48 | */ 49 | template 50 | class BlockCode { 51 | private: 52 | // Static assertions for some general things 53 | static_assert(sizeof(TCodeword) >= sizeof(TSourceBlock), "Codeword size must be greater than or equal to the source block size."); 54 | static_assert((sizeof(TCodeword) - sizeof(TSourceBlock)) <= sizeof(TSyndrome), "Syndrome must fit into the syndrome type."); 55 | 56 | // Useful constants 57 | static constexpr unsigned CodewordEffectiveLength = sizeof(TCodeword) * 8; 58 | static constexpr unsigned SourceBlockEffectiveLength = sizeof(TSourceBlock) * 8; 59 | static constexpr unsigned SyndromeEffectiveLength = sizeof(TSyndrome) * 8; 60 | 61 | // Matrices 62 | BinaryMatrix generator_; 63 | BinaryMatrix parityCheck_; 64 | BinaryMatrix decoder_; 65 | 66 | protected: 67 | /** 68 | * Calculates a syndrome of a codeword. 69 | */ 70 | inline TSyndrome calculateSyndrome(const TCodeword &codeword) const { 71 | return parityCheck_.template calculateProduct(codeword); 72 | } 73 | 74 | /** 75 | * Tries every possible combination of corrections 76 | * until either the possibilities run out or the codeword 77 | * is corrected. 78 | * 79 | * This algorithm should work with any block code, but it is 80 | * suggested to use more sophisticated ways for specific 81 | * codes for which better algorithms exist. 82 | */ 83 | inline TCodeword fixCodeword(const TCodeword &codeword, TSyndrome syndrome, bool &success) const { 84 | // std::cout << "Syndrome is: " << BinaryPrint(syndrome) << std::endl; 85 | 86 | // Try every possible way to flip bits, to a maximum of MaxCorrectedErrors 87 | for (unsigned attempts = 1; attempts <= MaxCorrectedErrors; attempts++) { 88 | // Create helper for constructing bitmask combinations 89 | BitmaskCombination b(attempts); 90 | 91 | // Go through all possible combinations 92 | for (TCodeword mask = b.next(); mask != 0; mask = b.next()) { 93 | TSyndrome s = calculateSyndrome(mask); 94 | 95 | if (s == syndrome) { 96 | TCodeword result = codeword ^ mask; 97 | syndrome = calculateSyndrome(result); 98 | 99 | if (syndrome == 0) { 100 | // std::cout << "correct mask is: " << BinaryPrint(mask) << std::endl; 101 | // std::cout << "codeword fixed to: " << BinaryPrint(result) << std::endl; 102 | success = true; 103 | return result; 104 | } 105 | else { 106 | std::cout << "unfixable error detected." << std::endl; 107 | success = false; 108 | return 0; 109 | } 110 | 111 | } 112 | } 113 | } 114 | 115 | // If we didn't manage to fix it, we assume we detected an unfixable error 116 | success = false; 117 | return 0; 118 | } 119 | 120 | public: 121 | /** 122 | * @brief Initializes the BlockCode class. 123 | * 124 | * Constructor parameters: 125 | * - Generator matrix 126 | * - Parity check matrix 127 | * - Decoder matrix 128 | */ 129 | inline explicit BlockCode(const decltype(generator_) &generator, const decltype(parityCheck_) &parityCheck, const decltype(decoder_) &decoder) 130 | : generator_(generator), parityCheck_(parityCheck), decoder_(decoder) { } 131 | 132 | /** 133 | * Encodes a block into a codeword. 134 | */ 135 | inline TCodeword encode(const TSourceBlock &input) const { 136 | return generator_.template calculateProduct(input); 137 | } 138 | 139 | /** 140 | * Decodes a codeword into a block, or tells if an unfixable error is detected. 141 | */ 142 | inline virtual TSourceBlock decode(const TCodeword &input, bool &success) const { 143 | // Initial value of success is true 144 | success = true; 145 | // Check if codeword has any issues 146 | TCodeword codeword = input; 147 | TSyndrome syndrome = calculateSyndrome(input); 148 | if (syndrome != 0) { 149 | // See if the codeword has issues and if so, try to fix it 150 | codeword = fixCodeword(input, syndrome, success); 151 | } 152 | 153 | // If couldn't fix it, return 0 154 | if (!success) { 155 | return 0; 156 | } 157 | 158 | // If the codeword is okay, decode it 159 | return decoder_.template calculateProduct(codeword); 160 | } 161 | }; 162 | 163 | } 164 | 165 | #endif // BLOCKCODE_H 166 | 167 | -------------------------------------------------------------------------------- /src/convolutional-decoder.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef CONVOLUTIONAL_DECODER 25 | #define CONVOLUTIONAL_DECODER 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "fecmagic-global.h" 34 | 35 | //#define CONVOLUTIONAL_DECODER_DEBUG 36 | #ifdef CONVOLUTIONAL_DECODER_DEBUG 37 | # include 38 | # include "binaryprint.h" 39 | # define DEBUG_PRINT(x) (::std::cout << "[ConvolutionalDecoder] " << x << std::endl); 40 | #else 41 | # define DEBUG_PRINT(x) 42 | #endif 43 | 44 | namespace fecmagic { 45 | 46 | /** 47 | * @brief Decoder that can decode convolutional code. 48 | * 49 | * This is a customizable decoder that can process varying convolutional codes. 50 | * It implements the Viterbi algorithm, and can be customized for your own 51 | * choice of convolutional code. 52 | * 53 | * Template parameters: 54 | * - Depth: the size of the state space (in other words, the number of possible 55 | * steps that the decoder will keep in memory at once. 56 | * - ConstraintLength: the constraint length of the code 57 | * - TShiftReg: unsigned integral type that can hold the shift register 58 | * - Polynomials: the polynomials used for this convolutional code. Each polynomial 59 | * corresponds to an output but, so in other words the code rate is the reciproc 60 | * of the number of polynomials. 61 | */ 62 | template 63 | class PuncturedConvolutionalDecoder final { 64 | 65 | // Check template parameters using static asserts 66 | static_assert((sizeof(TShiftReg) * 8) >= ConstraintLength, "The shift register must be able to hold the constraint length of the code."); 67 | static_assert(Depth >= 2, "The Depth template parameter must be at least two."); 68 | static_assert(ConstraintLength >= 2, "The ConstraintLength template parameter must be at least two."); 69 | static_assert(sizeof...(Polynomials) >= 2, "There must be at least two polynomials."); 70 | 71 | private: 72 | 73 | // Maximum error metric, represents infinity. 74 | constexpr static TShiftReg maxErrorMetric = ::std::numeric_limits::max(); 75 | 76 | // Number of outputs of the convolutional code (each polynomial corresponds to an output) 77 | constexpr static uint32_t outputCount_ = sizeof...(Polynomials); 78 | 79 | // Unpack variadic template argument, to allow access to each polynomial 80 | constexpr static TShiftReg polynomials_[sizeof...(Polynomials)] = { Polynomials... }; 81 | 82 | // Puncturing matrix 83 | TPuncturingMatrix puncturingMatrix; 84 | 85 | // Represents a single state that the encoder can be in, 86 | // used in the decoder for tracing the most likely encoder input. 87 | class State final { 88 | 89 | friend class PuncturedConvolutionalDecoder; 90 | 91 | #ifdef CONVOLUTIONAL_DECODER_DEBUG 92 | // It is wasteful to store the identifier of the state here, 93 | // because it is obvious from the loop that works with the states. 94 | // However it is still useful to track it when debugging. 95 | TShiftReg state = 0; 96 | #endif 97 | 98 | // The presumed input bit that leads to this state 99 | TShiftReg presumedInputBit; 100 | 101 | // The accumulated error metric along the path that leads to this state 102 | TShiftReg accumulatedErrorMetric; 103 | 104 | // The previous state that lead to this state 105 | const State *previous; 106 | 107 | inline explicit State() { 108 | this->reset(); 109 | } 110 | 111 | inline void reset() { 112 | presumedInputBit = 0; 113 | accumulatedErrorMetric = maxErrorMetric; 114 | previous = nullptr; 115 | } 116 | 117 | }; 118 | 119 | // Represents a step in the decoding procedure 120 | class Step final { 121 | 122 | friend class PuncturedConvolutionalDecoder; 123 | 124 | // Maximum possible number of states when the current decoder is used. 125 | constexpr static TShiftReg possibleStateCount = (1 << (ConstraintLength - 1)); 126 | 127 | // All the possible states in the current decoder step. 128 | State states[possibleStateCount]; 129 | 130 | // The lowest error metric found so far in this step. 131 | TShiftReg lowestErrorMetric = maxErrorMetric; 132 | 133 | // The state with the lowest error metric found so far in this step. 134 | State *lowestErrorState = nullptr; 135 | 136 | inline explicit Step() { 137 | this->reset(); 138 | } 139 | 140 | inline void reset() { 141 | lowestErrorMetric = maxErrorMetric; 142 | 143 | for (TShiftReg i = 0; i < possibleStateCount; i++) { 144 | this->states[i].reset(); 145 | } 146 | } 147 | }; 148 | 149 | // Gets the output of the convolutional encoder for the given state. 150 | static inline TShiftReg getEncoderOutput(TShiftReg shiftReg) { 151 | TShiftReg output = 0; 152 | for (uint32_t o = 0; o < outputCount_; o++) { 153 | output <<= 1; 154 | output |= ::fecmagic::computeParity(shiftReg & polynomials_[o]); 155 | } 156 | return output; 157 | } 158 | 159 | // Calculates error metric for the given current state assuming the specified presumed input bit. 160 | static inline void calculateErrorMetricForInput(const State ¤tState, Step &nextStep, TShiftReg currentSt, TShiftReg receivedBits, TShiftReg knownBits, uint8_t presumedInputBit) { 161 | assert(presumedInputBit == (presumedInputBit & 1)); 162 | 163 | // Next shift register value for the presumed input bit 164 | TShiftReg nextSr = currentSt | (presumedInputBit << (ConstraintLength - 1)); 165 | TShiftReg nextSt = nextSr >> 1; 166 | 167 | // Output of the encoder for the next shift register value 168 | TShiftReg nextSrOut = getEncoderOutput(nextSr); 169 | 170 | // Hamming distance between the would-be encoder output at this state and the received bits 171 | uint8_t hammingDistance = computeHammingDistance(nextSrOut & knownBits, receivedBits); 172 | 173 | // Calculate error metric 174 | TShiftReg oldMetric = currentState.accumulatedErrorMetric; 175 | TShiftReg metric = oldMetric + hammingDistance; 176 | 177 | if (metric < oldMetric) { 178 | // Ooops, overflow, this is bad 179 | metric = maxErrorMetric; 180 | } 181 | 182 | // Next encoder state for the given presumed input bit 183 | State &nextState = nextStep.states[nextSt]; 184 | 185 | DEBUG_PRINT("currstate=" << BinaryPrint(currentSt) << " nextsr=" << BinaryPrint(nextSr) << " nextstate=" << BinaryPrint(nextSt) << " pib=" << (uint32_t)presumedInputBit << " aem=" << (uint32_t)metric << " rbits=" << BinaryPrint(receivedBits) << " eout=" << BinaryPrint(nextSrOut) << " hd=" << (uint32_t)hammingDistance); 186 | if (nextState.accumulatedErrorMetric >= metric) { 187 | // Overwrite old error metric of this next state with the newly computed one 188 | nextState.accumulatedErrorMetric = metric; 189 | nextState.presumedInputBit = presumedInputBit; 190 | nextState.previous = ¤tState; 191 | 192 | #ifdef CONVOLUTIONAL_DECODER_DEBUG 193 | nextState.state = nextSt; 194 | #endif 195 | 196 | if (metric < nextStep.lowestErrorMetric) { 197 | nextStep.lowestErrorMetric = metric; 198 | nextStep.lowestErrorState = &nextState; 199 | } 200 | } 201 | } 202 | 203 | // Current output 204 | uint8_t *output; 205 | 206 | // All the state that we need to keep in memory at once. 207 | Step window[Depth]; 208 | 209 | // Current position within the window, indicates which 210 | // data structure is used to store the current state. 211 | uint32_t windowPos = 0; 212 | 213 | // Number of steps already taken. 214 | uint32_t currentStepCount; 215 | 216 | // Output position 217 | size_t outAddr; 218 | uint32_t outBitPos; 219 | 220 | public: 221 | 222 | /** 223 | * @brief Returns the reciproc of the code rate. 224 | */ 225 | constexpr static inline uint32_t reciprocCodeRate() { 226 | return outputCount_; 227 | } 228 | 229 | /** 230 | * @brief Default constructor. 231 | */ 232 | explicit PuncturedConvolutionalDecoder(void *output = nullptr) { 233 | this->reset(output); 234 | } 235 | 236 | /** 237 | * @brief Copy constructor. Intentionally disabled for this class. 238 | */ 239 | PuncturedConvolutionalDecoder(const PuncturedConvolutionalDecoder &other) = delete; 240 | 241 | /** 242 | * @brief Move constructor. 243 | */ 244 | PuncturedConvolutionalDecoder(const PuncturedConvolutionalDecoder &&other) { 245 | this->operator=(other); 246 | } 247 | 248 | /** 249 | * @brief Copy assignment operator. Intentionally disabled for this class. 250 | */ 251 | PuncturedConvolutionalDecoder &operator=(const PuncturedConvolutionalDecoder &other) = delete; 252 | 253 | /** 254 | * @brief Move assignment operator. 255 | */ 256 | PuncturedConvolutionalDecoder &operator=(const PuncturedConvolutionalDecoder &&other) { 257 | this->output = std::move(other.output); 258 | this->window = std::move(other.window); 259 | this->windowPos = std::move(other.windowPos); 260 | this->currentStepCount = std::move(other.currentStepCount); 261 | this->outAddr = std::move(other.outAddr); 262 | this->outBitPos = std::move(other.outBitPos); 263 | }; 264 | 265 | /** 266 | * @bried Resets the convolutional decoder and sets the given output. 267 | */ 268 | void reset(void *output) { 269 | // Set output 270 | this->output = reinterpret_cast(output); 271 | 272 | // Reset the state space. 273 | // Only the first step needs to be reset here, the 274 | // rest of them will be overwritten anyway in decode(). 275 | window[0].reset(); 276 | 277 | // The encoder always starts at the 0 state, so the 278 | // error metric corresponding to that state is 0 279 | window[0].states[0].accumulatedErrorMetric = 0; 280 | window[0].lowestErrorMetric = 0; 281 | window[0].lowestErrorState = &(window[windowPos].states[0]); 282 | 283 | // Start from the beginning 284 | windowPos = 0; 285 | currentStepCount = 0; 286 | outAddr = 0; 287 | outBitPos = 7; 288 | 289 | // Reset the puncturing matrix 290 | puncturingMatrix.reset(); 291 | } 292 | 293 | /** 294 | * @brief Returns the size of the output you need to allocate for a given input. 295 | * 296 | * Output size: space for the decoded output, and additional bytes 297 | * are needed because the encoder is flushed and the decoder needs 298 | * space to fill the decoded result of the flushed bits too. These 299 | * are likely just going to be zeroes. 300 | */ 301 | static inline size_t calculateOutputSize(uint32_t inputSize) { 302 | 303 | size_t puncturedBits = inputSize * 8; 304 | size_t t = puncturedBits * TPuncturingMatrix::count; 305 | size_t nonPuncturedBits = t / TPuncturingMatrix::nonZeroes(); 306 | if (0 != (t % TPuncturingMatrix::nonZeroes())) { 307 | nonPuncturedBits += 1; 308 | } 309 | 310 | size_t outputBits = nonPuncturedBits / outputCount_; 311 | if (0 != (nonPuncturedBits % outputCount_)) { 312 | outputBits += 1; 313 | } 314 | 315 | size_t outputSize = outputBits / 8; 316 | if (0 != (outputBits % 8)) { 317 | outputSize += 1; 318 | } 319 | 320 | return outputSize; 321 | } 322 | 323 | /** 324 | * @brief Decodes a given block. 325 | * 326 | * Decodes the block of supplied input bytes using the current convolutional code. 327 | * The caller of this method is responsible for making sure that enough memory is 328 | * allocated to fit the output. 329 | * 330 | * This method is suitable for streaming. 331 | */ 332 | void decode(const void *input, size_t inputSize) { 333 | // Check parameters 334 | if (inputSize == 0) { 335 | return; 336 | } 337 | 338 | const uint8_t *inputBytes = reinterpret_cast(input); 339 | assert(inputBytes != nullptr); 340 | assert(output != nullptr); 341 | 342 | DEBUG_PRINT("depth=" << (uint32_t)Depth << ", possibleStateCount=" << (uint32_t)Step::possibleStateCount); 343 | 344 | // Input position 345 | size_t inAddr = 0; 346 | uint32_t inBitPos = 7; 347 | 348 | while (inAddr < inputSize) { 349 | uint32_t nextWindowPos; 350 | uint32_t afterNextWindowPos; 351 | 352 | // The actual received input bits 353 | TShiftReg receivedBits = 0; 354 | // Bit mask, 0=punctured, 1=known bit 355 | TShiftReg knownBits = 0; 356 | 357 | // Get necessary number of input bits, one by one. 358 | // NOTE: We need to check inAddr vs. inputSize here again, 359 | // because if the output count is odd, inAddr might go 360 | // out of range. 361 | for (uint32_t o = 0; o < outputCount_ && inAddr < inputSize; o++) { 362 | // Read current input bit 363 | receivedBits <<= 1; 364 | // Shift the known bits 365 | knownBits <<= 1; 366 | 367 | if (0 == puncturingMatrix.next()) { 368 | continue; 369 | } 370 | 371 | knownBits |= 1; 372 | receivedBits |= ((inputBytes[inAddr] >> inBitPos) & 1); 373 | 374 | // Advance input bit position 375 | if (inBitPos == 0) { 376 | inAddr++; 377 | inBitPos = 7; 378 | } 379 | else { 380 | inBitPos--; 381 | } 382 | } 383 | 384 | // Calculate next position in the window 385 | if (windowPos == (Depth - 1)) { 386 | nextWindowPos = 0; 387 | } 388 | else { 389 | nextWindowPos = windowPos + 1; 390 | } 391 | 392 | // Go through all possible states at current step 393 | for (TShiftReg i = 0; i < Step::possibleStateCount; i++) { 394 | // Current state 395 | State ¤tState = window[windowPos].states[i]; 396 | 397 | // If accumulated metric is infinity, we don't bother with this state 398 | if (currentState.accumulatedErrorMetric == maxErrorMetric) { 399 | continue; 400 | } 401 | #ifdef CONVOLUTIONAL_DECODER_DEBUG 402 | currentState.state = i; 403 | #endif 404 | 405 | // Calculate appropriate error metric for possible input bits 406 | calculateErrorMetricForInput(currentState, window[nextWindowPos], i, receivedBits, knownBits, 0); 407 | calculateErrorMetricForInput(currentState, window[nextWindowPos], i, receivedBits, knownBits, 1); 408 | } 409 | 410 | 411 | if (currentStepCount > (Depth - 2)) { 412 | // Get output bits, if any, by tracing back 413 | const State *stateWithOutput = window[nextWindowPos].lowestErrorState; 414 | for (uint32_t i = 0; i < (Depth - 1); i++) { 415 | assert(stateWithOutput->previous != nullptr); 416 | stateWithOutput = stateWithOutput->previous; 417 | } 418 | 419 | // Get output bit and put it to its correct place 420 | uint8_t pib = stateWithOutput->presumedInputBit; 421 | assert((pib & 1) == pib); 422 | 423 | if (outBitPos == 7) { 424 | // Set current output byte to zero, so that Valgrind and other 425 | // memory check tools don't complain about it. 426 | output[outAddr] = 0; 427 | } 428 | DEBUG_PRINT("<--- OUTADDR=" << outAddr << " pib_out=" << ((uint32_t)pib) << " state=" << BinaryPrint(stateWithOutput->state)); 429 | output[outAddr] |= (pib << outBitPos); 430 | 431 | // Advance output bit position 432 | if (outBitPos == 0) { 433 | outAddr++; 434 | outBitPos = 7; 435 | } 436 | else { 437 | outBitPos--; 438 | } 439 | } 440 | 441 | // Get the window position after the next one 442 | if (nextWindowPos == (Depth - 1)) { 443 | afterNextWindowPos = 0; 444 | } 445 | else { 446 | afterNextWindowPos = nextWindowPos + 1; 447 | } 448 | 449 | // Reset the step after the next one, so that it can start fresh 450 | window[afterNextWindowPos].reset(); 451 | 452 | // Advance window position 453 | windowPos = nextWindowPos; 454 | 455 | // Increment step counter 456 | currentStepCount ++; 457 | } 458 | } 459 | 460 | void flush() { 461 | // We ran out of inputs. 462 | // Let's get the remaining output bits, if any, by tracing back 463 | uint8_t remainingOutputBits[Depth - 1]; 464 | const State *stateWithOutput = window[windowPos].lowestErrorState; 465 | 466 | // Go through the state with lowest error metric backwards, this will go through them in reversed order 467 | uint32_t tracebackDepth; 468 | if (currentStepCount > (Depth - 1)) { 469 | tracebackDepth = Depth - 1; 470 | } 471 | else { 472 | tracebackDepth = currentStepCount; 473 | } 474 | 475 | uint32_t trackbackIndex = tracebackDepth; 476 | for (; trackbackIndex > 0; trackbackIndex--) { 477 | DEBUG_PRINT("at the end, got out bit: " << (uint32_t)stateWithOutput->presumedInputBit << " state=" << (uint32_t)stateWithOutput->state); 478 | 479 | // Useful for debugging when decoding without bit errors 480 | //assert(stateWithOutput->accumulatedErrorMetric == 0); 481 | 482 | assert(stateWithOutput->previous != nullptr); 483 | remainingOutputBits[trackbackIndex - 1] = stateWithOutput->presumedInputBit; 484 | stateWithOutput = stateWithOutput->previous; 485 | } 486 | 487 | // Put the remaining bits to the output in the correct order 488 | for (; trackbackIndex < tracebackDepth; trackbackIndex++) { 489 | uint8_t pib = remainingOutputBits[trackbackIndex]; 490 | assert((pib & 1) == pib); 491 | 492 | if (outBitPos == 7) { 493 | // Set current output byte to zero, so that Valgrind and other 494 | // memory check tools don't complain about it. 495 | output[outAddr] = 0; 496 | } 497 | 498 | DEBUG_PRINT("<--- pib_out=" << ((uint32_t)pib)); 499 | output[outAddr] |= (pib << outBitPos); 500 | 501 | // Advance output bit position 502 | if (outBitPos == 0) { 503 | outAddr++; 504 | outBitPos = 7; 505 | } 506 | else { 507 | outBitPos--; 508 | } 509 | } 510 | } 511 | 512 | 513 | }; 514 | 515 | // Definition for the static member PuncturedConvolutionalDecoder::polynomials_ 516 | template 517 | constexpr TShiftReg PuncturedConvolutionalDecoder::polynomials_[sizeof...(Polynomials)]; 518 | 519 | template 520 | using ConvolutionalDecoder = PuncturedConvolutionalDecoder, Depth, ConstraintLength, TShiftReg, Polynomials...>; 521 | 522 | } 523 | 524 | #ifdef DEBUG_PRINT 525 | # undef DEBUG_PRINT 526 | #endif 527 | 528 | #endif // CONVOLUTIONAL_DECODER 529 | 530 | -------------------------------------------------------------------------------- /src/convolutional-encoder.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef CONVOLUTIONAL_ENCODER 25 | #define CONVOLUTIONAL_ENCODER 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "fecmagic-global.h" 34 | #include "sequence.h" 35 | 36 | //#define CONVOLUTIONAL_ENCODER_DEBUG 37 | #ifdef CONVOLUTIONAL_ENCODER_DEBUG 38 | # include 39 | # include "binaryprint.h" 40 | # define DEBUG_PRINT(x) (::std::cout << "[ConvolutionalEncoder] " << x << std::endl); 41 | #else 42 | # define DEBUG_PRINT(x) 43 | #endif 44 | 45 | namespace fecmagic { 46 | 47 | /** 48 | * @brief Encoder that can produce convolutional code. 49 | * 50 | * This is a customizable encoder that produces varying convolutional codes. 51 | * 52 | * Template parameters: 53 | * - ConstraintLength: the constraint length of the code 54 | * - TShiftReg: unsigned integral type that can hold the shift register 55 | * - Polynomials: the polynomials used for this convolutional code. Each polynomial 56 | * corresponds to an output but, so in other words the code rate is the reciproc 57 | * of the number of polynomials. 58 | * 59 | */ 60 | template 61 | class PuncturedConvolutionalEncoder final { 62 | // Check template parameters using static asserts 63 | static_assert((sizeof(TShiftReg) * 8) >= ConstraintLength, "The shift register must be able to hold the constraint length of the code."); 64 | static_assert(ConstraintLength >= 2, "The ConstraintLength template parameter must be at least two."); 65 | static_assert(sizeof...(Polynomials) >= 2, "There must be at least two polynomials."); 66 | 67 | private: 68 | 69 | // Number of outputs of the convolutional code (each polynomial corresponds to an output) 70 | constexpr static uint32_t outputCount_ = sizeof...(Polynomials); 71 | 72 | // Unpack variadic template argument, to allow access to each polynomial 73 | constexpr static TShiftReg polynomials_[sizeof...(Polynomials)] = { Polynomials... }; 74 | 75 | // Instantiate puncturing matrix 76 | TPuncturingMatrix puncturingMatrix; 77 | 78 | // Output 79 | uint8_t *output; 80 | 81 | // Output position 82 | size_t outAddr; 83 | uint32_t outBitPos; 84 | 85 | // The shift register 86 | TShiftReg shiftReg = 0; 87 | 88 | inline void produceOutput() { 89 | // Compute outputs from the current state of the encoder 90 | for (uint32_t o = 0; o < outputCount_; o++) { 91 | // Don't output when the next item in the puncturing matrix is zero 92 | if (0 == puncturingMatrix.next()) { 93 | DEBUG_PRINT("punctured bit"); 94 | continue; 95 | } 96 | 97 | if (outBitPos == 7) { 98 | DEBUG_PRINT("set " << outAddr << " byte to 0"); 99 | output[outAddr] = 0; 100 | } 101 | 102 | DEBUG_PRINT(outAddr << "/" << outBitPos << " shiftReg=" << BinaryPrint(shiftReg)); 103 | // Compute output and put it to its correct place 104 | output[outAddr] |= (::fecmagic::computeParity(polynomials_[o] & shiftReg) << outBitPos); 105 | 106 | // Advance output bit position 107 | if (outBitPos == 0) { 108 | outAddr++; 109 | outBitPos = 7; 110 | } 111 | else { 112 | outBitPos--; 113 | } 114 | } 115 | } 116 | 117 | public: 118 | 119 | /** 120 | * @brief Creates a convolutional encoder 121 | */ 122 | explicit PuncturedConvolutionalEncoder(void *output = nullptr) { 123 | this->reset(output); 124 | } 125 | 126 | /** 127 | * @brief Copy constructor. Intentionally disabled for this class. 128 | */ 129 | PuncturedConvolutionalEncoder(const PuncturedConvolutionalEncoder &other) = delete; 130 | 131 | /** 132 | * @brief Move constructor. 133 | */ 134 | PuncturedConvolutionalEncoder(const PuncturedConvolutionalEncoder &&other) { 135 | this->operator=(other); 136 | } 137 | 138 | /** 139 | * @brief Copy assignment operator. Intentionally disabled for this class. 140 | */ 141 | PuncturedConvolutionalEncoder &operator=(const PuncturedConvolutionalEncoder &other) = delete; 142 | 143 | /** 144 | * @brief Move assignment operator. 145 | */ 146 | PuncturedConvolutionalEncoder &operator=(const PuncturedConvolutionalEncoder &&other) { 147 | this->output = std::move(other.output); 148 | this->shiftReg = std::move(other.shiftReg); 149 | this->outAddr = std::move(other.outAddr); 150 | this->outBitPos = std::move(other.outBitPos); 151 | }; 152 | 153 | /** 154 | * @brief Resets the convolutional encoder and sets the given output. 155 | */ 156 | void reset(void *output) { 157 | DEBUG_PRINT(::std::endl << "resetting encoder"); 158 | this->puncturingMatrix.reset(); 159 | this->output = reinterpret_cast(output); 160 | this->shiftReg = 0; 161 | this->outAddr = 0; 162 | this->outBitPos = 7; 163 | } 164 | 165 | /** 166 | * @brief Returns the size of the output you need to allocate for a given input. 167 | * 168 | * Output size: give space to encoded bits and 169 | * after that, allow the encoder to be flushed. 170 | */ 171 | static inline size_t calculateOutputSize(size_t inputSize) { 172 | // Calculate non-punctured output bits 173 | size_t outputBits = ((inputSize * 8) + ConstraintLength) * outputCount_; 174 | DEBUG_PRINT("inputsize=" << inputSize << ", constraintlength=" << ConstraintLength << ", non-punctured output bit count: " << outputBits); 175 | 176 | // Take puncturing into account 177 | size_t t = outputBits * TPuncturingMatrix::nonZeroes(); 178 | size_t puncturedOutputBits = t / TPuncturingMatrix::count; 179 | if (0 != (t % TPuncturingMatrix::count)) { 180 | puncturedOutputBits += 1; 181 | } 182 | DEBUG_PRINT("punctured output bit count: " << puncturedOutputBits); 183 | 184 | // Calculate byte count 185 | size_t outputSize = puncturedOutputBits / 8; 186 | if (0 != puncturedOutputBits % 8) { 187 | outputSize += 1; 188 | } 189 | DEBUG_PRINT("output byte count: " << outputSize); 190 | 191 | return outputSize; 192 | } 193 | 194 | /** 195 | * @brief Encodes the given block. 196 | * 197 | * Encodes the block of supplied input bytes using the current convolutional code. 198 | * The caller of this method is responsible for making sure that enough memory is 199 | * allocated to fit the output. 200 | * 201 | * This method is suitable for streaming. 202 | */ 203 | void encode(const void *input, size_t inputSize) { 204 | DEBUG_PRINT("encode()ing"); 205 | 206 | if (inputSize == 0) { 207 | return; 208 | } 209 | 210 | const uint8_t *inputBytes = reinterpret_cast(input); 211 | assert(inputBytes != nullptr); 212 | assert(output != nullptr); 213 | 214 | // Input position 215 | size_t inAddr = 0; 216 | uint32_t inBitPos = 7; 217 | 218 | // Go through each input byte 219 | while (inAddr < inputSize) { 220 | // Shift the register right 221 | shiftReg >>= 1; 222 | 223 | // If the input still has bytes, use them, otherwise just flush the register 224 | if (inAddr < inputSize) { 225 | // Shift the next input bit into the register 226 | shiftReg |= (((inputBytes[inAddr] >> inBitPos) & 1) << (ConstraintLength - 1)); 227 | } 228 | 229 | // Produce output 230 | produceOutput(); 231 | 232 | // Advance input bit position 233 | if (inBitPos == 0) { 234 | inAddr++; 235 | inBitPos = 7; 236 | } 237 | else { 238 | inBitPos--; 239 | } 240 | } 241 | } 242 | 243 | /** 244 | * @brief Flushes the encoder, outputting the remaining bits until the shift register is empty. 245 | */ 246 | void flush() { 247 | DEBUG_PRINT("flush()ing"); 248 | 249 | for (uint32_t i = 0; i < ConstraintLength; i++) { 250 | // Shift the register right 251 | shiftReg >>= 1; 252 | 253 | // Produce output 254 | produceOutput(); 255 | } 256 | } 257 | 258 | }; 259 | 260 | // Definition for the static member PuncturedConvolutionalEncoder::polynomials_ 261 | template 262 | constexpr TShiftReg PuncturedConvolutionalEncoder::polynomials_[sizeof...(Polynomials)]; 263 | 264 | template 265 | using ConvolutionalEncoder = PuncturedConvolutionalEncoder, ConstraintLength, TShiftReg, Polynomials...>; 266 | 267 | } 268 | 269 | #ifdef DEBUG_PRINT 270 | # undef DEBUG_PRINT 271 | #endif 272 | 273 | #endif // CONVOLUTIONAL_ENCODER 274 | -------------------------------------------------------------------------------- /src/crc.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef FECMAGIC_CRC_H 25 | #define FECMAGIC_CRC_H 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "fecmagic-global.h" 32 | 33 | //#define CRC_DEBUG 34 | #ifdef CRC_DEBUG 35 | # include 36 | # define DEBUG_PRINT(x) (::std::cout << "[CRC] " << x << ::std::endl << ::std::flush); 37 | #else 38 | # define DEBUG_PRINT(x) 39 | #endif 40 | 41 | namespace fecmagic { 42 | 43 | template 44 | T crc_calc(const uint8_t *input_bytes, size_t input_size) 45 | { 46 | if (!input_size) { 47 | return 0; 48 | } 49 | 50 | assert(input_bytes); 51 | T out = INIT; 52 | 53 | for (size_t byte_pos = 0; byte_pos < (input_size); ++byte_pos) { 54 | uint8_t in_byte = input_bytes[byte_pos]; 55 | out ^= (REF_IN ? bitreverse_8(in_byte) : in_byte) << (sizeof(T) * 8 - 8); 56 | 57 | for (size_t bit_cnt = 0; bit_cnt < 8; ++bit_cnt) { 58 | uint32_t mask = -(out >> (sizeof(T) * 8 - 1)); 59 | out <<= 1; 60 | out ^= (POLY & mask); 61 | } 62 | } 63 | 64 | if (REF_OUT) { 65 | static_assert(sizeof(T) <= sizeof(uint32_t), "Size of T must be less than or equal to 32-bit"); 66 | out = bitreverse_32(out) >> (sizeof(uint32_t) * 8 - sizeof(T) * 8); 67 | } 68 | 69 | // Flip output bits 70 | out ^= XOROUT; 71 | 72 | return out; 73 | } 74 | 75 | uint16_t crc16_buypass(const uint8_t *input_bytes, size_t input_size) 76 | { 77 | return crc_calc(input_bytes, input_size); 78 | } 79 | 80 | uint16_t crc16_arc(const uint8_t *input_bytes, size_t input_size) 81 | { 82 | return crc_calc(input_bytes, input_size); 83 | } 84 | 85 | uint16_t crc16_usb(const uint8_t *input_bytes, size_t input_size) 86 | { 87 | return crc_calc(input_bytes, input_size); 88 | } 89 | 90 | uint32_t crc32_iso(const uint8_t *input_bytes, size_t input_size) 91 | { 92 | return crc_calc(input_bytes, input_size); 93 | } 94 | 95 | uint32_t crc32_posix(const uint8_t *input_bytes, size_t input_size) 96 | { 97 | return crc_calc(input_bytes, input_size); 98 | } 99 | 100 | uint32_t crc32_32c(const uint8_t *input_bytes, size_t input_size) 101 | { 102 | return crc_calc(input_bytes, input_size); 103 | } 104 | 105 | } 106 | 107 | #endif // FECMAGIC_CRC_H 108 | 109 | -------------------------------------------------------------------------------- /src/fecmagic-global.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef FECMAGIC_GLOBAL_H 25 | #define FECMAGIC_GLOBAL_H 26 | 27 | // Pre-defined macros for architectures: 28 | // http://sourceforge.net/p/predef/wiki/Architectures/ 29 | #if defined(COMPILE_SSE2_CODE) 30 | # include 31 | # if defined(__GNUC__) 32 | # define SSE2_SUPPORTED __builtin_cpu_supports("sse2") 33 | # elif defined(_MSC_VER) 34 | # define SSE2_SUPPORTED IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) 35 | # else 36 | # define SSE2_SUPPORTED false 37 | # endif 38 | #endif 39 | 40 | #ifndef COMPILE_SSE2_CODE 41 | # define SSE2_SUPPORTED false 42 | #endif 43 | 44 | namespace fecmagic { 45 | 46 | /** 47 | * Computes the parity of the given number: 48 | * result is 1 when there are an odd number of ones, 49 | * and and 0 when there are an even number of ones. 50 | */ 51 | inline uint8_t computeParity(unsigned x) { 52 | #if defined(__GNUC__) 53 | return __builtin_parity(x); 54 | #elif defined(_MSC_VER) 55 | return __popcnt(x) % 2; 56 | #else 57 | # error "Unknown compiler, write parity check function" 58 | #endif 59 | } 60 | 61 | /** 62 | * Returns the number of 1 bits in the given number. 63 | */ 64 | inline uint8_t computePopcount(unsigned x) { 65 | #if defined(__GNUC__) 66 | return __builtin_popcount(x); 67 | #elif defined(_MSC_VER) 68 | return __popcnt(x); 69 | #else 70 | # error "Unknown compiler, write popcount function" 71 | #endif 72 | } 73 | 74 | /** 75 | * Returns the hamming distance between two numbers. 76 | */ 77 | inline uint8_t computeHammingDistance(unsigned x, unsigned y) { 78 | return computePopcount(x ^ y); 79 | } 80 | 81 | /** 82 | * Reverses (or reflects) the bits in a byte. 83 | * 84 | * Source: "Bit Twiddling Hacks" http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits 85 | */ 86 | inline uint8_t bitreverse_8(uint8_t b) 87 | { 88 | return (uint8_t) (((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); 89 | } 90 | 91 | /** 92 | * Reverses (or reflects) the bits in a 32-bit number. 93 | * 94 | * Source: "The Aggregate Magic Algorithms" http://aggregate.org/MAGIC/#Bit%20Reversal 95 | */ 96 | inline uint32_t bitreverse_32(register unsigned int x) 97 | { 98 | x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); 99 | x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); 100 | x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); 101 | x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); 102 | return ((x >> 16) | (x << 16)); 103 | } 104 | 105 | } 106 | 107 | #endif // FECMAGIC_GLOBAL_H 108 | -------------------------------------------------------------------------------- /src/golay.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef GOLAY_H 25 | #define GOLAY_H 26 | 27 | #include "blockcode.h" 28 | 29 | namespace fecmagic { 30 | 31 | class GolayCode final : public BlockCode<3, uint32_t, uint16_t, uint16_t> { 32 | public: 33 | inline explicit GolayCode() 34 | : BlockCode({ 35 | // 24x12 generator matrix (represented in 32x16) 36 | 0, 0, 37 | 0, 0, 38 | 0, 0, 39 | 0, 0, 40 | 0, 0, 41 | 0, 0, 42 | 0, 0, 43 | 0, 0, 44 | 0b00001000, 0b00000000, 45 | 0b00000100, 0b00000000, 46 | 0b00000010, 0b00000000, 47 | 0b00000001, 0b00000000, 48 | 0b00000000, 0b10000000, 49 | 0b00000000, 0b01000000, 50 | 0b00000000, 0b00100000, 51 | 0b00000000, 0b00010000, 52 | 0b00000000, 0b00001000, 53 | 0b00000000, 0b00000100, 54 | 0b00000000, 0b00000010, 55 | 0b00000000, 0b00000001, 56 | 0b00001001, 0b11110001, 57 | 0b00000100, 0b11111010, 58 | 0b00000010, 0b01111101, 59 | 0b00001001, 0b00111110, 60 | 0b00001100, 0b10011101, 61 | 0b00001110, 0b01001110, 62 | 0b00001111, 0b00100101, 63 | 0b00001111, 0b10010010, 64 | 0b00000111, 0b11001001, 65 | 0b00000011, 0b11100110, 66 | 0b00000101, 0b01010111, 67 | 0b00001010, 0b10101011, 68 | }, { 69 | // 12x24 parity check matrix (represented as 16x32) 70 | 0, 0, 0, 71 | 0, 0, 0, 72 | 0, 0, 0, 73 | 0, 0, 0, 74 | 0, 0b10011111, 0b00011000, 0b00000000, 75 | 0, 0b01001111, 0b10100100, 0b00000000, 76 | 0, 0b00100111, 0b11010010, 0b00000000, 77 | 0, 0b10010011, 0b11100001, 0b00000000, 78 | 0, 0b11001001, 0b11010000, 0b10000000, 79 | 0, 0b11100100, 0b11100000, 0b01000000, 80 | 0, 0b11110010, 0b01010000, 0b00100000, 81 | 0, 0b11111001, 0b00100000, 0b00010000, 82 | 0, 0b01111100, 0b10010000, 0b00001000, 83 | 0, 0b00111110, 0b01100000, 0b00000100, 84 | 0, 0b01010101, 0b01110000, 0b00000010, 85 | 0, 0b10101010, 0b10110000, 0b00000001, 86 | }, { 87 | // 12x24 decode matrix (represented as 16x32) 88 | 0, 0, 0, 0, 89 | 0, 0, 0, 0, 90 | 0, 0, 0, 0, 91 | 0, 0, 0, 0, 92 | 0, 0b10000000, 0b00000000, 0, 93 | 0, 0b01000000, 0b00000000, 0, 94 | 0, 0b00100000, 0b00000000, 0, 95 | 0, 0b00010000, 0b00000000, 0, 96 | 0, 0b00001000, 0b00000000, 0, 97 | 0, 0b00000100, 0b00000000, 0, 98 | 0, 0b00000010, 0b00000000, 0, 99 | 0, 0b00000001, 0b00000000, 0, 100 | 0, 0b00000000, 0b10000000, 0, 101 | 0, 0b00000000, 0b01000000, 0, 102 | 0, 0b00000000, 0b00100000, 0, 103 | 0, 0b00000000, 0b00010000, 0, 104 | }) { } 105 | }; 106 | 107 | } 108 | 109 | #endif // GOLAY_H 110 | 111 | -------------------------------------------------------------------------------- /src/hamming.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef HAMMING_H 25 | #define HAMMING_H 26 | 27 | #include "blockcode.h" 28 | 29 | namespace fecmagic { 30 | 31 | class HammingCode final : public BlockCode<1, uint8_t, uint8_t, uint8_t> { 32 | public: 33 | // TODO: add the possibility of choosing different Hamming codes 34 | inline explicit HammingCode() 35 | : BlockCode({ 36 | 0, 37 | 0b00001101, 38 | 0b00001011, 39 | 0b00001000, 40 | 0b00000111, 41 | 0b00000100, 42 | 0b00000010, 43 | 0b00000001, 44 | }, { 45 | 0, 46 | 0, 47 | 0, 48 | 0, 49 | 0, 50 | 0b01010101, 51 | 0b00110011, 52 | 0b00001111, 53 | }, { 54 | 0, 55 | 0, 56 | 0, 57 | 0, 58 | 0b00010000, 59 | 0b00000100, 60 | 0b00000010, 61 | 0b00000001, 62 | }) { } 63 | }; 64 | 65 | } 66 | 67 | #endif // HAMMING_H 68 | 69 | -------------------------------------------------------------------------------- /src/sequence.h: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef FECMAGIC_SEQUENCE_H 25 | #define FECMAGIC_SEQUENCE_H 26 | 27 | #include 28 | #include 29 | 30 | namespace fecmagic { 31 | 32 | template 33 | class Sequence final { 34 | 35 | private: 36 | 37 | size_t index; 38 | 39 | public: 40 | 41 | constexpr static T count = sizeof...(Numbers); 42 | 43 | constexpr static T numbers[sizeof...(Numbers)] = { Numbers... }; 44 | 45 | explicit inline Sequence() { 46 | reset(); 47 | } 48 | 49 | // TODO: convert this into a C++11 constexpr function 50 | static inline size_t zeroes() { 51 | size_t n = 0; 52 | for (size_t i = 0; i < count; i++) { 53 | if (numbers[i] == 0) { 54 | n++; 55 | } 56 | } 57 | return n; 58 | } 59 | 60 | static inline size_t nonZeroes() { 61 | return count - zeroes(); 62 | } 63 | 64 | inline T current() const { 65 | return numbers[index]; 66 | } 67 | 68 | inline T next() { 69 | index ++; 70 | 71 | if (index == count) { 72 | index = 0; 73 | } 74 | 75 | return current(); 76 | } 77 | 78 | void reset() { 79 | index = count - 1; 80 | } 81 | 82 | 83 | }; 84 | 85 | template 86 | constexpr T Sequence::numbers[sizeof...(Numbers)]; 87 | 88 | } 89 | 90 | #endif // FECMAGIC_SEQUENCE_H 91 | 92 | -------------------------------------------------------------------------------- /tests/helper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CODEMAGIC_TESTS_HELPER_H 3 | #define CODEMAGIC_TESTS_HELPER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template 13 | constexpr T reverseBits(T v) { 14 | // Check template parameter 15 | static_assert(std::is_integral::value == true, "You must use this function with integral types."); 16 | 17 | // Thanks to Sean Eron Anderson for making this algorithm, 18 | // see http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious 19 | T r = v; 20 | uint32_t s = sizeof(v) * 8 - 1; 21 | 22 | for (v >>= 1; v; v >>= 1) { 23 | r <<= 1; 24 | r |= v & 1; 25 | s--; 26 | } 27 | r <<= s; 28 | 29 | return r; 30 | } 31 | 32 | // Creates an array that contains zeros and ones from a byte array 33 | void bytearray2zeroone(unsigned bytelength, const unsigned char in[], unsigned char out[]) { 34 | unsigned i, j; 35 | unsigned char cmp; 36 | for (i = 0; i < bytelength; i++) { 37 | cmp = 128; 38 | for (j = 0; j < 8; j++) { 39 | out[i * 8 + j] = ((cmp & in[i]) != 0); 40 | cmp /= 2; 41 | } 42 | } 43 | } 44 | 45 | // Creates a byte array from an array that contains zeros and ones 46 | void zeroone2bytearray(unsigned bytelength, const unsigned char in[], unsigned char out[]) { 47 | unsigned i, j; 48 | unsigned char cmp; 49 | for (i = 0; i < bytelength; i++) { 50 | out[i] = 0; 51 | cmp = 128; 52 | for (j = 0; j < 8; j++) { 53 | out[i] += in[i * 8 + j] * cmp; 54 | cmp /= 2; 55 | } 56 | } 57 | } 58 | 59 | #endif // CODEMAGIC_TESTS_HELPER_H 60 | 61 | -------------------------------------------------------------------------------- /tests/test_binarymatrix.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include 26 | #include 27 | #include "../src/binarymatrix.h" 28 | #include "../src/binaryprint.h" 29 | #include "../src/bitmaskcombination.h" 30 | 31 | using namespace std; 32 | using namespace fecmagic; 33 | 34 | // Creates a sequence of random bytes with the given size 35 | uint8_t *createRandomBytes(size_t size) { 36 | uint8_t *bytes = new uint8_t[size]; 37 | for (size_t i = 0; i < size; i++) { 38 | bytes[i] = (uint8_t)(rand() % 255); 39 | } 40 | return bytes; 41 | } 42 | 43 | // Transposes a binary matrix in a simple and naive way. 44 | // This method is used for verifying the more advanced implementations 45 | // in the BinaryMatrix class. 46 | template 47 | BinaryMatrix naiveTranspose(const BinaryMatrix &input) { 48 | BinaryMatrix output; 49 | for (unsigned i = 0; i < input.rows(); i++) { 50 | for (unsigned j = 0; j < input.cols(); j++) { 51 | auto bit = input.getBit(i, j); 52 | output.setBit(j, i, bit); 53 | } 54 | } 55 | 56 | return output; 57 | } 58 | 59 | // Tests the transpose operation of a given matrix. 60 | template 61 | void testTranspose(const BinaryMatrix &m) { 62 | auto t1 = m.transpose(); 63 | auto t2 = naiveTranspose(m); 64 | if (t1 != t2) { 65 | cout << "FAIL! " << R << "×" << C << endl; 66 | cout << "m:" << endl << m << endl; 67 | cout << "t1:" << endl << t1 << endl; 68 | cout << "t2:" << endl << t2 << endl; 69 | } 70 | assert(t1 == t2); 71 | } 72 | 73 | // Tests the transpose operation of a randomized matrix of the given size. 74 | template 75 | void testTransposeRandom(unsigned n = 50) { 76 | size_t byteCount = BinaryMatrix::getByteCount(); 77 | uint8_t *bytes = new uint8_t[byteCount]; 78 | 79 | for (unsigned nn = 0; nn < n; nn++) { 80 | //cout << R << "×" << C << " #" << nn << endl; 81 | 82 | for (size_t i = 0; i < byteCount; i++) { 83 | bytes[i] = (uint8_t)(rand() % 255); 84 | } 85 | 86 | BinaryMatrix m(bytes); 87 | //cout << m; 88 | testTranspose(m); 89 | } 90 | 91 | delete bytes; 92 | } 93 | 94 | // Test transpose operation using pre-defined matrices 95 | void transposeTests1() { 96 | testTranspose<8, 8>({ 97 | 0b10000000, 98 | 0b00000001, 99 | 0b00010000, 100 | 0b00001000, 101 | 0b01000000, 102 | 0b00100000, 103 | 0b00000100, 104 | 0b00000010, 105 | }); 106 | testTranspose<16, 8>({ 107 | 0b10000000, 108 | 0b00000001, 109 | 0b00010000, 110 | 0b00001000, 111 | 0b01000000, 112 | 0b00100000, 113 | 0b00000100, 114 | 0b00000010, 115 | 0b10000000, 116 | 0b00000001, 117 | 0b00010000, 118 | 0b00001000, 119 | 0b01000000, 120 | 0b00100000, 121 | 0b00000100, 122 | 0b00000010, 123 | }); 124 | testTranspose<16, 8>({ 125 | 0b00110000, 126 | 0b00110001, 127 | 0b11110111, 128 | 0b10101000, 129 | 0b01001110, 130 | 0b01001110, 131 | 0b01010001, 132 | 0b01111011, 133 | 0b11010000, 134 | 0b00100110, 135 | 0b01111011, 136 | 0b10110010, 137 | 0b11011111, 138 | 0b00110101, 139 | 0b11010010, 140 | 0b01010100, 141 | }); 142 | testTranspose<24, 8>({ 143 | 0b10000000, 144 | 0b00000001, 145 | 0b00010000, 146 | 0b00001000, 147 | 0b01000000, 148 | 0b00100000, 149 | 0b00000100, 150 | 0b00000010, 151 | 0b10000000, 152 | 0b00000001, 153 | 0b00010000, 154 | 0b00001000, 155 | 0b01000000, 156 | 0b00100000, 157 | 0b00000100, 158 | 0b00000010, 159 | 0b10000000, 160 | 0b00000001, 161 | 0b00010000, 162 | 0b00001000, 163 | 0b01000000, 164 | 0b00100000, 165 | 0b00000100, 166 | 0b00000010, 167 | }); 168 | testTranspose<24, 16>({ 169 | 0b10000000, 0b10000000, 170 | 0b00000001, 0b00000001, 171 | 0b00010000, 0b00010000, 172 | 0b00001000, 0b00001000, 173 | 0b01000000, 0b01000000, 174 | 0b00100000, 0b00100000, 175 | 0b00000100, 0b00000100, 176 | 0b00000010, 0b00000010, 177 | 0b10000000, 0b10000000, 178 | 0b00000001, 0b00000001, 179 | 0b00010000, 0b00010000, 180 | 0b00001000, 0b00001000, 181 | 0b01000000, 0b01000000, 182 | 0b00100000, 0b00100000, 183 | 0b00000100, 0b00000100, 184 | 0b00000010, 0b00000010, 185 | 0b10000000, 0b10000000, 186 | 0b00000001, 0b00000001, 187 | 0b00010000, 0b00010000, 188 | 0b00001000, 0b00001000, 189 | 0b01000000, 0b01000000, 190 | 0b00100000, 0b00100000, 191 | 0b00000100, 0b00000100, 192 | 0b00000010, 0b00000010, 193 | }); 194 | } 195 | 196 | // Tests transpose operation using random generated matrices. 197 | void transposeTests2() { 198 | testTransposeRandom<8, 8>(); 199 | testTransposeRandom<16, 8>(); 200 | testTransposeRandom<8, 16>(); 201 | testTransposeRandom<24, 8>(); 202 | testTransposeRandom<8, 24>(); 203 | testTransposeRandom<24, 24>(); 204 | testTransposeRandom<32, 24>(); 205 | testTransposeRandom<80, 80>(); 206 | testTransposeRandom<80, 8>(); 207 | testTransposeRandom<8, 80>(); 208 | testTransposeRandom<800, 800>(); 209 | } 210 | 211 | template 212 | BinaryMatrix naiveCalculateProduct(const BinaryMatrix &m1, const BinaryMatrix &m2) { 213 | BinaryMatrix result; 214 | 215 | for (size_t i = 0; i < result.rows(); i++) { 216 | for (size_t j = 0; j < result.cols(); j++) { 217 | uint8_t bit = 0; 218 | for (size_t x = 0; x < C; x++) { 219 | bit ^= (m1.getBit(i, x) & m2.getBit(x, j)); 220 | } 221 | result.setBit(i, j, bit); 222 | } 223 | } 224 | 225 | return result; 226 | } 227 | 228 | template 229 | void matrixProductTest(const BinaryMatrix &m1, const BinaryMatrix &m2) { 230 | auto product1 = m1.calculateProduct(m2); 231 | auto product2 = naiveCalculateProduct(m1, m2); 232 | assert(product1 == product2); 233 | } 234 | 235 | template 236 | void randomMatrixProductTest(unsigned n = 50) { 237 | for (unsigned i = 0; i < n; i++) { 238 | uint8_t *randomBytes1 = createRandomBytes(R * C / 8); 239 | uint8_t *randomBytes2 = createRandomBytes(C * X / 8); 240 | 241 | BinaryMatrix m1(randomBytes1); 242 | BinaryMatrix m2(randomBytes2); 243 | matrixProductTest(m1, m2); 244 | 245 | delete [] randomBytes1; 246 | delete [] randomBytes2; 247 | } 248 | } 249 | 250 | void matrixProductTests1() { 251 | matrixProductTest<16, 16, 16>({ 252 | 0b10000000, 0b10000000, 253 | 0b00000001, 0b00000001, 254 | 0b00010000, 0b00010000, 255 | 0b00001000, 0b00001000, 256 | 0b01000000, 0b01000000, 257 | 0b00100000, 0b00100000, 258 | 0b00000100, 0b00000100, 259 | 0b00000010, 0b00000010, 260 | 0b10000000, 0b10000000, 261 | 0b00000001, 0b00000001, 262 | 0b00010000, 0b00010000, 263 | 0b00001000, 0b00001000, 264 | 0b01000000, 0b01000000, 265 | 0b00100000, 0b00100000, 266 | 0b00000100, 0b00000100, 267 | 0b00000010, 0b00000010, 268 | }, { 269 | 0b10000000, 0b10000000, 270 | 0b00000001, 0b00000001, 271 | 0b00010000, 0b00010000, 272 | 0b00001000, 0b00001000, 273 | 0b01000000, 0b01000000, 274 | 0b00100000, 0b00100000, 275 | 0b00000100, 0b00000100, 276 | 0b00000010, 0b00000010, 277 | 0b10000000, 0b10000000, 278 | 0b00000001, 0b00000001, 279 | 0b00010000, 0b00010000, 280 | 0b00001000, 0b00001000, 281 | 0b01000000, 0b01000000, 282 | 0b00100000, 0b00100000, 283 | 0b00000100, 0b00000100, 284 | 0b00000010, 0b00000010, 285 | }); 286 | } 287 | 288 | void matrixProductTests2() { 289 | randomMatrixProductTest<16, 8, 24>(); 290 | randomMatrixProductTest<24, 32, 16>(); 291 | randomMatrixProductTest<8, 8, 8>(); 292 | } 293 | 294 | int main() { 295 | transposeTests1(); 296 | transposeTests2(); 297 | matrixProductTests1(); 298 | matrixProductTests2(); 299 | // TODO: test vector multiplication too 300 | 301 | return 0; 302 | } 303 | 304 | -------------------------------------------------------------------------------- /tests/test_binaryprint.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "../src/binaryprint.h" 29 | 30 | using namespace std; 31 | using namespace fecmagic; 32 | 33 | int main() { 34 | ostringstream ss; 35 | 36 | ss << BinaryPrint(0b10101010); 37 | assert(ss.str() == "10101010"); 38 | ss.str(string()); 39 | 40 | ss << BinaryPrint(0b00000000); 41 | assert(ss.str() == "00000000"); 42 | ss.str(string()); 43 | 44 | ss << BinaryPrint(0b1111111111111111); 45 | assert(ss.str() == "1111111111111111"); 46 | ss.str(string()); 47 | 48 | ss << BinaryPrint(0b1111111111111111, "|"); 49 | assert(ss.str() == "11111111|11111111"); 50 | ss.str(string()); 51 | 52 | ss << BinaryPrint(0b01010101010101010101010101010101); 53 | assert(ss.str() == "01010101010101010101010101010101"); 54 | ss.str(string()); 55 | 56 | ss << BinaryPrint(0b01010101010101010101010101010101, " "); 57 | assert(ss.str() == "01010101 01010101 01010101 01010101"); 58 | ss.str(string()); 59 | 60 | cout << "BinaryPrint test successful" << endl; 61 | 62 | return 0; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /tests/test_bitmaskcombination.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include 26 | #include "../src/bitmaskcombination.h" 27 | #include "../src/binaryprint.h" 28 | 29 | using namespace std; 30 | using namespace fecmagic; 31 | 32 | int main() { 33 | BitmaskCombination c1(1); 34 | for (uint8_t x = c1.next(), i = 1; x; x = c1.next(), i++) { 35 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 36 | } 37 | cout << endl; 38 | 39 | BitmaskCombination c2(7); 40 | for (uint8_t x = c2.next(), i = 1; x; x = c2.next(), i++) { 41 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 42 | } 43 | cout << endl; 44 | 45 | BitmaskCombination c3(2); 46 | for (uint8_t x = c3.next(), i = 1; x; x = c3.next(), i++) { 47 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 48 | } 49 | cout << endl; 50 | 51 | BitmaskCombination c4(6); 52 | for (uint8_t x = c4.next(), i = 1; x; x = c4.next(), i++) { 53 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 54 | } 55 | cout << endl; 56 | 57 | BitmaskCombination c5(3); 58 | for (uint8_t x = c5.next(), i = 1; x; x = c5.next(), i++) { 59 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 60 | } 61 | cout << endl; 62 | 63 | BitmaskCombination c6(5); 64 | for (uint8_t x = c6.next(), i = 1; x; x = c6.next(), i++) { 65 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 66 | } 67 | cout << endl; 68 | 69 | BitmaskCombination c7(4); 70 | for (uint8_t x = c7.next(), i = 1; x; x = c7.next(), i++) { 71 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 72 | } 73 | cout << endl; 74 | 75 | BitmaskCombination c8(0); 76 | for (uint8_t x = c8.next(), i = 1; x; x = c8.next(), i++) { 77 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 78 | } 79 | cout << endl; 80 | 81 | BitmaskCombination c9(8); 82 | for (uint8_t x = c9.next(), i = 1; x; x = c9.next(), i++) { 83 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 84 | } 85 | cout << endl; 86 | 87 | // Test with less than the full width of the variable 88 | BitmaskCombination aaa(1); 89 | for (uint8_t x = aaa.next(), i = 1; x; x = aaa.next(), i++) { 90 | cout << BinaryPrint(x) << " <- " << (unsigned)i << endl; 91 | } 92 | cout << endl; 93 | 94 | return 0; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /tests/test_bitpacker.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../src/bitpacker.h" 3 | #include "../src/binaryprint.h" 4 | #include "helper.h" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace fecmagic; 10 | using namespace std; 11 | 12 | int main() { 13 | srand(time(nullptr)); 14 | 15 | // Create input ========================================================= 16 | 17 | constexpr size_t inputBlocks = 22; 18 | uint32_t input[inputBlocks] = { 0 }; 19 | uint8_t input_test[inputBlocks * 23] = { 0 }; 20 | 21 | for (size_t i = 0; i < inputBlocks; i++) { 22 | uint32_t block = rand(); 23 | block >>= (32 - 23); 24 | 25 | input[i] = block; 26 | uint8_t block_test[4] = { 27 | static_cast((block >> 24) & 0xff), 28 | static_cast((block >> 16) & 0xff), 29 | static_cast((block >> 8) & 0xff), 30 | static_cast((block & 0xff)) 31 | }; 32 | uint8_t block_test_zeroone[32]; 33 | bytearray2zeroone(4, block_test, block_test_zeroone); 34 | memcpy(input_test + (i * 23), block_test_zeroone + 32 - 23, 23); 35 | 36 | cout << "block #" << i << ":\t"; 37 | for (size_t j = 0; j < 23; j++) { 38 | cout << (uint32_t) *(block_test_zeroone + 32 - 23 + j); 39 | } 40 | cout << endl; 41 | } 42 | 43 | for (size_t i = 0; i < inputBlocks * 23; i++) { 44 | if (i % 23 == 0) 45 | cout << endl << "block #?:\t"; 46 | 47 | cout << (uint32_t) input_test[i]; 48 | } 49 | 50 | cout << endl; 51 | 52 | // Pack ================================================================= 53 | 54 | cout << endl << "pack ======================" << endl; 55 | 56 | uint8_t packed[64] = { 0 }; 57 | uint8_t packed_test[64 * 8] = { 0 }; 58 | 59 | BitPacker packer(packed); 60 | 61 | for (size_t i = 0; i < inputBlocks; i++) { 62 | packer.pack(input[i]); 63 | } 64 | 65 | 66 | // for (size_t i = 0; i < 64; i++) { 67 | // if (i % 4 == 0) { 68 | // cout << endl; 69 | // } 70 | // else { 71 | // cout << " "; 72 | // } 73 | // cout << BinaryPrint(packed[i]); 74 | // } 75 | // 76 | // cout << endl; 77 | 78 | bytearray2zeroone(64, packed, packed_test); 79 | 80 | cout << endl << "comparison ====================" << endl; 81 | 82 | if (0 == memcmp(packed_test, input_test, inputBlocks * 23)) { 83 | cout << "Packed bits match the input." << endl; 84 | } 85 | else { 86 | cout << "Packed bits DON'T match the input." << endl; 87 | return 1; 88 | } 89 | 90 | cout << endl << "unpack =====================" << endl; 91 | 92 | // Unpack ================================================================== 93 | 94 | 95 | uint32_t unpacked[inputBlocks] = { 0 }; 96 | uint8_t unpacked_test[inputBlocks * 23] = { 0 }; 97 | BitUnpacker unpacker(packed); 98 | 99 | for (size_t i = 0; i < inputBlocks; i++) { 100 | uint32_t block = unpacker.unpack(); 101 | unpacked[i] = block; 102 | 103 | uint8_t block_test[4] = { 104 | static_cast((block >> 24) & 0xff), 105 | static_cast((block >> 16) & 0xff), 106 | static_cast((block >> 8) & 0xff), 107 | static_cast((block & 0xff)) 108 | }; 109 | uint8_t block_test_zeroone[32]; 110 | bytearray2zeroone(4, block_test, block_test_zeroone); 111 | memcpy(unpacked_test + (i * 23), block_test_zeroone + 32 - 23, 23); 112 | } 113 | 114 | for (size_t i = 0; i < inputBlocks * 23; i++) { 115 | if (i % 23 == 0) 116 | cout << endl << "block #?:\t"; 117 | 118 | cout << (uint32_t) unpacked_test[i]; 119 | if (unpacked_test[i] != input_test[i]) { 120 | cout << " <- mismatch at " << i << endl; 121 | return 1; 122 | } 123 | } 124 | 125 | cout << endl; 126 | 127 | // Check match 128 | if (0 == memcmp(input, unpacked, inputBlocks)) { 129 | cout << "unpacked blocks match the input" << endl; 130 | } 131 | else { 132 | cout << "unpacked blocks DON'T match the input :-(" << endl; 133 | } 134 | 135 | 136 | return 0; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /tests/test_convolutional_decoder.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "../src/binaryprint.h" 29 | #include "../src/convolutional-encoder.h" 30 | #include "../src/convolutional-decoder.h" 31 | 32 | //#define TEST_CONVOLUTIONAL_DECODER_DEBUG 33 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 34 | # define DEBUG_PRINT(x) (::std::cout << "[TestConvolutionalDecoder] " << x << std::endl); 35 | #else 36 | # define DEBUG_PRINT(x) 37 | #endif 38 | 39 | using namespace std; 40 | using namespace fecmagic; 41 | 42 | constexpr uint8_t poly1 = 0x5b; 43 | constexpr uint8_t poly2 = 0x79; 44 | 45 | template 46 | bool testEncodeAndDecode(TEnc &enc, TDec &dec, const char *data) { 47 | size_t dataSize = strlen(data) + 1; 48 | size_t encodedSize = TEnc::calculateOutputSize(dataSize); 49 | size_t decodedSize = TDec::calculateOutputSize(encodedSize); 50 | uint8_t *encInput = new uint8_t[dataSize]; 51 | uint8_t *encOutput = new uint8_t[encodedSize]; 52 | uint8_t *decOutput = new uint8_t[decodedSize]; 53 | memcpy(encInput, data, dataSize); 54 | 55 | cout << "datasize=" << dataSize << " encodedSize=" << encodedSize << " decodedSize=" << decodedSize << endl; 56 | 57 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 58 | for (uint32_t i = 0; i < dataSize; i++) { 59 | cout << BinaryPrint(encInput[i]) << " "; 60 | } 61 | cout << endl; 62 | #endif 63 | 64 | enc.reset(encOutput); 65 | enc.encode(encInput, dataSize); 66 | enc.flush(); 67 | 68 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 69 | for (uint32_t i = 0; i < encodedSize; i++) { 70 | cout << i << " " << encOutput[i] << endl; // BinaryPrint(encOutput[i]) << " " << endl; 71 | } 72 | cout << endl; 73 | #endif 74 | 75 | dec.reset(decOutput); 76 | dec.decode(encOutput, encodedSize); 77 | dec.flush(); 78 | 79 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 80 | for (uint32_t i = 0; i < decodedSize; i++) { 81 | cout << BinaryPrint(decOutput[i]) << " "; 82 | } 83 | cout << endl; 84 | #endif 85 | 86 | bool success = (0 == memcmp(decOutput, encInput, dataSize)); 87 | 88 | delete [] encInput; 89 | delete [] encOutput; 90 | delete [] decOutput; 91 | 92 | return success; 93 | } 94 | 95 | template 96 | bool testEncodeAndDecodeWithBitErrors(TEnc &enc, TDec &dec, const char *data, uint32_t errorCount) { 97 | size_t dataSize = strlen(data) + 1; 98 | size_t encodedSize = TEnc::calculateOutputSize(dataSize); 99 | size_t decodedSize = TDec::calculateOutputSize(encodedSize); 100 | uint8_t *encInput = new uint8_t[dataSize]; 101 | uint8_t *encOutput = new uint8_t[encodedSize]; 102 | uint8_t *decOutput = new uint8_t[decodedSize]; 103 | memcpy(encInput, data, dataSize); 104 | 105 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 106 | for (uint32_t i = 0; i < dataSize; i++) { 107 | cout << BinaryPrint(encInput[i]) << " "; 108 | } 109 | cout << endl; 110 | #endif 111 | 112 | enc.reset(encOutput); 113 | enc.encode(encInput, dataSize); 114 | enc.flush(); 115 | 116 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 117 | for (uint32_t i = 0; i < encodedSize; i++) { 118 | cout << BinaryPrint(encOutput[i]) << " "; 119 | } 120 | cout << endl; 121 | #endif 122 | 123 | // Initialize random engine 124 | std::random_device rd; 125 | std::mt19937 mt(rd()); 126 | std::uniform_real_distribution dist(1.0, 10.0); 127 | 128 | // Add bit errors 129 | for (uint32_t i = 0; i < errorCount; i++) { 130 | uint32_t randomNumber = dist(mt); 131 | uint32_t byte = randomNumber / 8; 132 | uint32_t bit = randomNumber - (byte * 8); 133 | 134 | encOutput[byte] ^= (1 << bit); 135 | } 136 | 137 | // Decode 138 | dec.reset(decOutput); 139 | dec.decode(encOutput, encodedSize); 140 | dec.flush(); 141 | 142 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 143 | for (uint32_t i = 0; i < decodedSize; i++) { 144 | cout << BinaryPrint(decOutput[i]) << " "; 145 | } 146 | cout << endl; 147 | #endif 148 | 149 | bool success = (0 == memcmp(decOutput, encInput, dataSize)); 150 | 151 | delete [] encInput; 152 | delete [] encOutput; 153 | delete [] decOutput; 154 | 155 | return success; 156 | } 157 | 158 | bool testPuncturingSimple(const void *input, size_t inputSize) { 159 | 160 | // Here, we assume that the punctured encoder works correctly, 161 | // because it has its own test in test_convolutional_encoder.cpp, 162 | // so we only verify that the decoder can decode the punctured 163 | // output of the encoder. 164 | 165 | // Instantiate encoder and decoder 166 | PuncturedConvolutionalEncoder, 7, uint8_t, poly1, poly2> encoder; 167 | PuncturedConvolutionalDecoder, 35, 7, uint8_t, poly1, poly2> decoder; 168 | 169 | // Allocate output 170 | const uint8_t *inputBytes = reinterpret_cast(input); 171 | size_t encoderOutputSize = decltype(encoder)::calculateOutputSize(inputSize); 172 | size_t decoderOutputSize = decltype(decoder)::calculateOutputSize(encoderOutputSize); 173 | uint8_t *encoderOutput = new uint8_t[encoderOutputSize]; 174 | uint8_t *decoderOutput = new uint8_t[decoderOutputSize]; 175 | 176 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 177 | cout << "inputSize=" << inputSize << endl; 178 | cout << "encoderOutputSize=" << encoderOutputSize << endl; 179 | cout << "decoderOutputSize=" << decoderOutputSize << endl; 180 | #endif 181 | 182 | // Encode 183 | encoder.reset(encoderOutput); 184 | encoder.encode(inputBytes, inputSize); 185 | encoder.flush(); 186 | 187 | // Decode 188 | decoder.reset(decoderOutput); 189 | decoder.decode(encoderOutput, encoderOutputSize); 190 | decoder.flush(); 191 | 192 | #ifdef TEST_CONVOLUTIONAL_DECODER_DEBUG 193 | cout << "input:" << endl; 194 | for (size_t i = 0; i < inputSize; i++) { 195 | cout << BinaryPrint(inputBytes[i]) << " "; 196 | } 197 | cout << endl; 198 | cout << "output:" << endl; 199 | for (size_t i = 0; i < decoderOutputSize; i++) { 200 | cout << BinaryPrint(decoderOutput[i]) << " "; 201 | } 202 | cout << endl; 203 | #endif 204 | 205 | return 0 == memcmp(input, decoderOutput, inputSize); 206 | 207 | } 208 | 209 | int main() { 210 | cout << "Testing basic functionality (k=3, rate=1/3)" << endl; 211 | ConvolutionalEncoder<3, uint8_t, 7, 3, 5> enc2; 212 | ConvolutionalDecoder<15, 3, uint8_t, 7, 3, 5> dec2; 213 | ConvolutionalDecoder<2, 3, uint8_t, 7, 3, 5> dec2_2; 214 | assert(testEncodeAndDecode(enc2, dec2, "Hello!")); 215 | 216 | assert(testEncodeAndDecode(enc2, dec2_2, "Hello!")); 217 | cout << "OK" << endl; 218 | 219 | cout << "Testing basic functionality (k=3, rate=1/2)" << endl; 220 | ConvolutionalEncoder<3, uint8_t, 7, 5> enc1; 221 | ConvolutionalDecoder<10, 3, uint8_t, 7, 5> dec1; 222 | ConvolutionalDecoder<50, 3, uint8_t, 7, 5> dec1_2; 223 | ConvolutionalDecoder<5, 3, uint8_t, 7, 5> dec1_3; 224 | 225 | assert(testEncodeAndDecode(enc1, dec1, "Hello!")); 226 | assert(testEncodeAndDecode(enc1, dec1_2, "Hello!")); 227 | assert(testEncodeAndDecode(enc1, dec1_3, "Hello!")); 228 | cout << "OK" << endl; 229 | 230 | cout << "Testing basic functionality (k=7, rate=1/2)" << endl; 231 | ConvolutionalEncoder<7, uint8_t, poly1, poly2> encoder; 232 | ConvolutionalDecoder<100, 7, uint8_t, poly1, poly2> decoder; 233 | 234 | assert(testEncodeAndDecode(encoder, decoder, "Hello!")); 235 | assert(testEncodeAndDecode(encoder, decoder, "Good morning, Captain! Are we awesome yet?")); 236 | cout << "OK" << endl; 237 | 238 | cout << "Testing with 1-bit errors" << endl; 239 | for (uint32_t i = 0; i < 100; i++) { 240 | assert(testEncodeAndDecodeWithBitErrors(encoder, decoder, "Hello!", 1)); 241 | } 242 | cout << "OK" << endl; 243 | 244 | cout << "Testing with 2-bit errors" << endl; 245 | for (uint32_t i = 0; i < 100; i++) { 246 | assert(testEncodeAndDecodeWithBitErrors(encoder, decoder, "Hello world! Are we awesome yet?", 2)); 247 | } 248 | cout << "OK" << endl; 249 | 250 | cout << "Testing with 3-bit errors" << endl; 251 | for (uint32_t i = 0; i < 100; i++) { 252 | assert(testEncodeAndDecodeWithBitErrors(encoder, decoder, "Hello world! Are we awesome yet?", 3)); 253 | } 254 | cout << "OK" << endl; 255 | 256 | cout << "Puncturing / simple" << endl; 257 | cout << testPuncturingSimple("Hello, world!", sizeof("Hello, world!")) << endl; 258 | 259 | return 0; 260 | } 261 | 262 | -------------------------------------------------------------------------------- /tests/test_convolutional_encoder.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | // 25 | // README 26 | // ====== 27 | // 28 | // This file is a simple program to test the convolutional encoder of fecmagic. The 29 | // encoder is tested against a known-to-work old implementation that we used in an 30 | // old project. The results produced by the new algorithm are simply compared to the 31 | // results produced by the new one. 32 | 33 | #include "helper.h" 34 | #include "../src/binaryprint.h" 35 | #include "../src/convolutional-encoder.h" 36 | #include "../src/convolutional-decoder.h" 37 | 38 | #include 39 | 40 | using namespace std; 41 | using namespace fecmagic; 42 | 43 | constexpr uint8_t poly1 = 0x6d; 44 | constexpr uint8_t poly2 = 0x4f; 45 | 46 | void old_encode(unsigned char in[], unsigned char out[], size_t inputsize, size_t encodedlength) { 47 | size_t i; 48 | unsigned char shft = 0; 49 | 50 | memset(out, 0, encodedlength); 51 | 52 | for(i = 0; i < inputsize + 4; i++) { 53 | shft = (shft << 1) | ((i >= inputsize) ? 0 : in[i]); 54 | out[2 * i + 0] = computeParity(shft & poly1); 55 | out[2 * i + 1] = computeParity(shft & poly2); 56 | } 57 | } 58 | 59 | void encodeWithOldAlgorithm(uint8_t *input, size_t inputSize, uint8_t *output) { 60 | size_t inputbitsize = inputSize * 8; 61 | size_t encodedlength = inputbitsize * 2 + 8; 62 | 63 | unsigned char *input_bits = new unsigned char[inputbitsize]; 64 | memset(input_bits, 0, inputbitsize); 65 | bytearray2zeroone(inputSize, input, input_bits); 66 | 67 | unsigned char *encoded_bits = new unsigned char[encodedlength]; 68 | memset(encoded_bits, 0, encodedlength); 69 | 70 | old_encode(input_bits, encoded_bits, inputSize * 8, encodedlength); 71 | 72 | memset(output, 0, encodedlength / 8); 73 | zeroone2bytearray(encodedlength / 8, encoded_bits, output); 74 | 75 | delete [] input_bits; 76 | delete [] encoded_bits; 77 | } 78 | 79 | 80 | 81 | bool testConvolutionalCode(size_t inputSize, const uint8_t *inputData) { 82 | // NOTE: the ConvolutionalEncoder class shifts in bits from the other direction, 83 | // so we need to reverse the polynomials as well, to get the same output. 84 | ConvolutionalEncoder<7, uint8_t, (reverseBits(poly1) >> 1), (reverseBits(poly2) >> 1)> encoder; 85 | 86 | // Create input 87 | uint8_t *input = new uint8_t[inputSize]; 88 | memset(input, 0, inputSize); 89 | memcpy(input, inputData, inputSize); 90 | 91 | // Create output 92 | size_t outputSize = encoder.calculateOutputSize(inputSize); 93 | uint8_t *output1 = new uint8_t[outputSize]; 94 | 95 | // Encode output using the new algorithm 96 | encoder.reset(output1); 97 | encoder.encode(input, inputSize); 98 | encoder.flush(); 99 | 100 | // Encode output using old (known to work) algorithm 101 | uint8_t *output2 = new uint8_t[outputSize]; 102 | memset(output2, 0, outputSize); 103 | encodeWithOldAlgorithm(input, inputSize, output2); 104 | 105 | // Check if both algorithms create the same result 106 | bool success = (0 == memcmp(output1, output2, outputSize)); 107 | 108 | // Free used memory 109 | delete [] input; 110 | delete [] output1; 111 | delete [] output2; 112 | 113 | return success; 114 | } 115 | 116 | bool testStreamingSimple() { 117 | ConvolutionalEncoder<7, uint8_t, (reverseBits(poly1) >> 1), (reverseBits(poly2) >> 1)> enc1; 118 | 119 | const char *input1 = "Hello world, are we cool yet?"; 120 | uint32_t inputSize = strlen(input1); 121 | uint32_t outputSize = enc1.calculateOutputSize(inputSize); 122 | 123 | uint8_t *output1 = new uint8_t[outputSize]; 124 | 125 | enc1.reset(output1); 126 | enc1.encode(input1, inputSize); 127 | enc1.flush(); 128 | 129 | const char *input2_1 = "Hello "; 130 | const char *input2_2 = "world, are"; 131 | const char *input2_3 = " we cool yet?"; 132 | uint8_t *output2 = new uint8_t[outputSize]; 133 | 134 | ConvolutionalEncoder<7, uint8_t, (reverseBits(poly1) >> 1), (reverseBits(poly2) >> 1)> enc2; 135 | enc2.reset(output2); 136 | enc2.encode(input2_1, strlen(input2_1)); 137 | enc2.encode(input2_2, strlen(input2_2)); 138 | enc2.encode(input2_3, strlen(input2_3)); 139 | enc2.flush(); 140 | 141 | bool success = (0 == memcmp(output1, output2, outputSize)); 142 | 143 | delete [] output1; 144 | delete [] output2; 145 | 146 | return success; 147 | } 148 | 149 | bool testStreaming() { 150 | ConvolutionalEncoder<7, uint8_t, (reverseBits(poly1) >> 1), (reverseBits(poly2) >> 1)> enc1; 151 | 152 | constexpr uint32_t inputSize = 40; 153 | uint32_t outputSize = enc1.calculateOutputSize(inputSize); 154 | 155 | // Create input1 156 | uint8_t *input = new uint8_t[inputSize]; 157 | for (uint32_t i = 0; i < inputSize; i++) { 158 | input[i] = rand() % 256; 159 | } 160 | 161 | // cout << "input1:" << endl; 162 | // for (uint32_t i = 0; i < inputSize; i++) { 163 | // cout << BinaryPrint(input[i]) << " "; 164 | // } 165 | // cout << endl; 166 | 167 | // Create input2 168 | constexpr uint32_t sd = 15; 169 | uint8_t *input2_1 = new uint8_t[inputSize - sd]; 170 | uint8_t *input2_2 = new uint8_t[sd]; 171 | memcpy(input2_1, input, inputSize - sd); 172 | memcpy(input2_2, input + inputSize - sd, sd); 173 | 174 | // cout << "input2:" << endl; 175 | // for (uint32_t i = 0; i < (inputSize - sd); i++) { 176 | // cout << BinaryPrint(input2_1[i]) << " "; 177 | // } 178 | // for (uint32_t i = 0; i < (sd); i++) { 179 | // cout << BinaryPrint(input2_2[i]) << " "; 180 | // } 181 | // cout << endl; 182 | 183 | // Create output1 184 | uint8_t *output = new uint8_t[outputSize]; 185 | 186 | enc1.reset(output); 187 | enc1.encode(input, inputSize); 188 | enc1.flush(); 189 | 190 | // cout << "output1:" << endl; 191 | // for (uint32_t i = 0; i < outputSize; i++) { 192 | // cout << BinaryPrint(output[i]) << " "; 193 | // } 194 | // cout << endl; 195 | 196 | // Create output2 197 | uint8_t *output2 = new uint8_t[outputSize]; 198 | 199 | ConvolutionalEncoder<7, uint8_t, (reverseBits(poly1) >> 1), (reverseBits(poly2) >> 1)> enc2; 200 | enc2.reset(output2); 201 | enc2.encode(input2_1, inputSize - sd); 202 | enc2.encode(input2_2, sd); 203 | enc2.flush(); 204 | 205 | 206 | // cout << "output2:" << endl; 207 | // for (uint32_t i = 0; i < outputSize; i++) { 208 | // cout << BinaryPrint(output2[i]) << " "; 209 | // } 210 | // cout << endl; 211 | 212 | uint8_t *output3 = new uint8_t[outputSize]; 213 | enc1.reset(output3); 214 | enc1.encode(input2_1, inputSize - sd); 215 | enc1.encode(input2_2, sd); 216 | enc1.flush(); 217 | 218 | bool success = (0 == memcmp(output, output2, outputSize)); 219 | success = success && (0 == memcmp(output, output3, outputSize)); 220 | 221 | delete [] input; 222 | delete [] input2_1; 223 | delete [] input2_2; 224 | delete [] output; 225 | delete [] output2; 226 | delete [] output3; 227 | 228 | return success; 229 | } 230 | 231 | bool testSequenceSimple() { 232 | Sequence seq; 233 | 234 | assert(seq.next() == 1); 235 | assert(seq.next() == 1); 236 | assert(seq.next() == 0); 237 | assert(seq.next() == 1); 238 | 239 | assert(seq.next() == 1); 240 | assert(seq.next() == 1); 241 | assert(seq.next() == 0); 242 | assert(seq.next() == 1); 243 | 244 | assert(seq.next() == 1); 245 | assert(seq.next() == 1); 246 | assert(seq.next() == 0); 247 | assert(seq.next() == 1); 248 | 249 | assert(seq.next() == 1); 250 | assert(seq.next() == 1); 251 | seq.reset(); 252 | 253 | assert(seq.next() == 1); 254 | assert(seq.next() == 1); 255 | assert(seq.next() == 0); 256 | assert(seq.next() == 1); 257 | 258 | return true; 259 | } 260 | 261 | bool testPuncturedSimple(const char *input) { 262 | // Test if the Sequence class works correctly 263 | testSequenceSimple(); 264 | 265 | // Non-punctured 266 | ConvolutionalEncoder<3, uint8_t, 7, 5> nonPuncturedEncoder; 267 | PuncturedConvolutionalEncoder, 3, uint8_t, 7, 5> puncturedEncoder; 268 | 269 | // Input size 270 | size_t inputSize = strlen(input); 271 | 272 | // Output 273 | size_t outputSize = decltype(nonPuncturedEncoder)::calculateOutputSize(inputSize); 274 | size_t puncturedOutputSize = decltype(puncturedEncoder)::calculateOutputSize(inputSize); 275 | uint8_t *output = new uint8_t[outputSize]; 276 | uint8_t *puncturedOutput = new uint8_t[puncturedOutputSize]; 277 | 278 | // Encode input 279 | nonPuncturedEncoder.reset(output); 280 | nonPuncturedEncoder.encode(input, inputSize); 281 | nonPuncturedEncoder.flush(); 282 | 283 | puncturedEncoder.reset(puncturedOutput); 284 | puncturedEncoder.encode(input, inputSize); 285 | puncturedEncoder.flush(); 286 | 287 | // Examine each output bit 288 | uint8_t *outputBits = new uint8_t[outputSize * 8]; 289 | uint8_t *puncturedOutputBits = new uint8_t[puncturedOutputSize * 8]; 290 | 291 | bytearray2zeroone(outputSize, output, outputBits); 292 | bytearray2zeroone(puncturedOutputSize, puncturedOutput, puncturedOutputBits); 293 | 294 | // cout << "inputSize=" << inputSize << endl; 295 | // cout << "outputSize=" << outputSize << endl; 296 | // cout << "puncturedOutputSize=" << puncturedOutputSize << endl; 297 | 298 | Sequence seq; 299 | for (size_t i = 0, j = 0; i < (outputSize * 8) && j < (puncturedOutputSize * 8); i++) { 300 | if (0 == seq.next()) { 301 | continue; 302 | } 303 | 304 | assert(outputBits[i] == puncturedOutputBits[j]); 305 | 306 | j++; 307 | } 308 | 309 | // // Print 310 | // cout << endl << "Non-punctured:" << endl; 311 | // for (size_t i = 0; i < outputSize; i++) { 312 | // cout << BinaryPrint(output[i]) << " "; 313 | // } 314 | // cout << endl << "Punctured:" << endl; 315 | // for (size_t i = 0; i < puncturedOutputSize; i++) { 316 | // cout << BinaryPrint(puncturedOutput[i]) << " "; 317 | // } 318 | // cout << endl; 319 | 320 | delete [] output; 321 | delete [] puncturedOutput; 322 | delete [] outputBits; 323 | delete [] puncturedOutputBits; 324 | 325 | return true; 326 | } 327 | 328 | int main() { 329 | srand(time(0)); 330 | 331 | cout << "poly1 = " << BinaryPrint(poly1) << endl; 332 | cout << "poly2 = " << BinaryPrint(poly2) << endl; 333 | 334 | // Test the encoder with the K=7, rate=1/2 (Voyager) code 335 | const char *testInput; 336 | testInput = "Hello!"; 337 | cout << "test with '" << testInput << "': " << testConvolutionalCode(strlen(testInput) + 1, reinterpret_cast(testInput)) << endl; 338 | testInput = "Hello world!"; 339 | cout << "test with '" << testInput << "': " << testConvolutionalCode(strlen(testInput) + 1, reinterpret_cast(testInput)) << endl; 340 | testInput = "Good morning, Captain! Are we awesome yet?"; 341 | cout << "test with '" << testInput << "': " << testConvolutionalCode(strlen(testInput) + 1, reinterpret_cast(testInput)) << endl; 342 | 343 | // Test the encoder for the code that is described here: 344 | // http://home.netcom.com/~chip.f/viterbi/algrthms.html 345 | ConvolutionalEncoder<3, uint8_t, 7, 5> enc3; 346 | uint8_t input[] = { 0b01011100, 0b10100010 }; 347 | uint8_t output[] = { 0, 0, 0, 0, 0 }; 348 | 349 | enc3.reset(output); 350 | enc3.encode(input, 2); 351 | enc3.flush(); 352 | uint8_t expectedOutput[] = { 0b00111000, 0b01100111, 0b11100010, 0b11001110, 0b11000000 }; 353 | 354 | if (0 == memcmp(output, expectedOutput, 5)) { 355 | cout << "yup, k=3, rate=1/2 works!" << endl; 356 | } 357 | else { 358 | cout << "nope, k=3, rate=1/2 doesn't work!" << endl; 359 | } 360 | 361 | uint8_t decoded[] = { 0, 0, 0 }; 362 | ConvolutionalDecoder<15, 3, uint8_t, 7, 5> dec3(decoded); 363 | 364 | dec3.decode(expectedOutput, 5); 365 | dec3.flush(); 366 | 367 | if (0 == memcmp(decoded, input, 2)) { 368 | cout << "yup, k=3, rate=1/2 decode works!" << endl; 369 | } 370 | else { 371 | cout << "nope, k=3, rate=1/2 decode doesn't work!" << endl; 372 | } 373 | 374 | cout << "Simple streaming: " << testStreamingSimple() << endl; 375 | 376 | for (uint32_t i = 0; i < 100; i++) { 377 | assert(testStreaming()); 378 | } 379 | cout << "Random streaming: ok" << endl; 380 | 381 | cout << "Simple punctured encoding: " << testPuncturedSimple("Hello, world!") << endl; 382 | 383 | return 0; 384 | } 385 | -------------------------------------------------------------------------------- /tests/test_crc.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../src/crc.h" 3 | #include "../src/binaryprint.h" 4 | #include "helper.h" 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace fecmagic; 10 | 11 | // Old crc generator algorithm: 12 | // This is a know-to-work algorithm for the same CRC, 13 | // used in an old project in some form at some point. 14 | void old_crc_gen(unsigned char in[], uint32_t inSize, unsigned char crc_b[]) { 15 | constexpr uint32_t th = 17; 16 | uint32_t i, j; 17 | uint32_t k = inSize; 18 | unsigned char gen[th] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }; 19 | 20 | for (i = 0; i < k; i++) { 21 | if (in[i]) { 22 | for (j = 0; j < th; j++) { 23 | in[i + j] ^= gen[j]; 24 | } 25 | } 26 | } 27 | 28 | for (i = 0; i < (th - 1); i++) { 29 | crc_b[i] = in[i + k]; 30 | } 31 | } 32 | 33 | bool testCrc(const uint8_t *input, size_t inputSize) { 34 | size_t testbitsize = inputSize * 8 + 2 * 8; 35 | uint8_t *testbits = new uint8_t[testbitsize]; 36 | uint8_t *resultbits = new uint8_t[16]; 37 | uint8_t *resultArr = new uint8_t[2]; 38 | memset(testbits, 0, testbitsize); 39 | memset(resultbits, 0, 16); 40 | 41 | // Old algorithm 42 | bytearray2zeroone(inputSize, input, testbits); 43 | old_crc_gen(testbits, inputSize * 8, resultbits); 44 | zeroone2bytearray(2, resultbits, resultArr); 45 | uint16_t result1 = ((uint16_t)resultArr[0] << 8) | resultArr[1]; 46 | 47 | // New algorithm 48 | uint16_t result2 = crc16_buypass(input, inputSize); 49 | 50 | // cout << BinaryPrint(result1) << endl; 51 | // cout << BinaryPrint(result2) << endl; 52 | 53 | delete [] testbits; 54 | delete [] resultbits; 55 | delete [] resultArr; 56 | 57 | return result1 == result2; 58 | } 59 | 60 | int main(int argc, char **argv) { 61 | if (argc == 2) { 62 | const char *test_str = argv[1]; 63 | size_t test_len = strlen(test_str); 64 | 65 | cout << "CRC-16/ARC: " << hex << crc16_arc(reinterpret_cast(test_str), test_len) << dec << endl; 66 | cout << "CRC-16/BUYPASS: " << hex << crc16_buypass(reinterpret_cast(test_str), test_len) << dec << endl; 67 | cout << "CRC-16/USB: " << hex << crc16_usb(reinterpret_cast(test_str), test_len) << dec << endl; 68 | 69 | cout << "CRC-32/ISO-HDLC: " << hex << crc32_iso(reinterpret_cast(test_str), test_len) << dec << endl; 70 | cout << "CRC-32/POSIX: " << hex << crc32_posix(reinterpret_cast(test_str), test_len) << dec << endl; 71 | cout << "CRC-32/32C: " << hex << crc32_32c(reinterpret_cast(test_str), test_len) << dec << endl; 72 | 73 | return 0; 74 | } 75 | 76 | uint8_t test1[] = { 3, 42, 16 }; 77 | cout << (testCrc(test1, 3) ? "yes" : "no") << endl; 78 | 79 | for (uint32_t i = 0; i < 20; i++) { 80 | uint8_t test[3]; 81 | for (uint32_t j = 0; j < 3; j++) { 82 | test[j] = rand() % 256; 83 | } 84 | //cout << "test: " << BinaryPrint(test[0]) << " " << BinaryPrint(test[1]) << " " << BinaryPrint(test[2]) << endl; 85 | 86 | bool r = testCrc(test, 3); 87 | assert(r); 88 | } 89 | 90 | for (uint32_t i = 0; i < 20; i++) { 91 | uint8_t test[4]; 92 | for (uint32_t j = 0; j < 4; j++) { 93 | test[j] = rand() % 256; 94 | } 95 | //cout << "test: " << BinaryPrint(test[0]) << " " << BinaryPrint(test[1]) << " " << BinaryPrint(test[2]) << " " << BinaryPrint(test[3]) << endl; 96 | 97 | bool r = testCrc(test, 4); 98 | assert(r); 99 | } 100 | 101 | for (uint32_t i = 0; i < 20; i++) { 102 | uint8_t test[10]; 103 | for (uint32_t j = 0; j < 10; j++) { 104 | test[j] = rand() % 256; 105 | } 106 | //cout << "test: " << BinaryPrint(test[0]) << " " << BinaryPrint(test[1]) << " " << BinaryPrint(test[2]) << endl; 107 | 108 | bool r = testCrc(test, 10); 109 | assert(r); 110 | } 111 | 112 | for (uint32_t i = 0; i < 20; i++) { 113 | uint8_t test[50]; 114 | for (uint32_t j = 0; j < 50; j++) { 115 | test[j] = rand() % 256; 116 | } 117 | //cout << "test: " << BinaryPrint(test[0]) << " " << BinaryPrint(test[1]) << " " << BinaryPrint(test[2]) << endl; 118 | 119 | bool r = testCrc(test, 50); 120 | assert(r); 121 | } 122 | 123 | uint8_t p[29] = { 0x02, 0x77, 0x37, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 124 | cout << testCrc(p, 29) << endl; 125 | 126 | cout << "all tests successful" << endl; 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /tests/test_golay.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include "../src/golay.h" 26 | 27 | using namespace std; 28 | using namespace fecmagic; 29 | 30 | int main() { 31 | GolayCode c; 32 | 33 | // One-bit errors 34 | cout << "Testing one-bit errors" << endl; 35 | for (uint16_t i = 0; i <= 0xfff; i++) { 36 | uint32_t coded = c.encode(i); 37 | 38 | // Test single-bit errors 39 | BitmaskCombination bmc1; 40 | for (uint32_t mask = bmc1.next(); mask; mask = bmc1.next()) { 41 | bool decodeSuccess; 42 | uint32_t corrupted = coded ^ mask; 43 | uint32_t decoded = c.decode(corrupted, decodeSuccess); 44 | 45 | if (!decodeSuccess || decoded != i) { 46 | cout << "FAIL! Could not decode one-bit error." << endl 47 | << " the input=\t" << BinaryPrint(i) << endl 48 | << " encoded=\t" << BinaryPrint(coded) << endl 49 | << " the mask=\t" << BinaryPrint(mask) << endl 50 | << " corrupted=\t" << BinaryPrint(corrupted) << endl 51 | << " output=\t" << BinaryPrint(decoded) << endl; 52 | } 53 | else { 54 | cout << "\rSo far so good: " << i; 55 | } 56 | assert(decoded == i); 57 | } 58 | } 59 | cout << endl; 60 | 61 | // Two-bit errors 62 | cout << "Testing two-bit errors" << endl; 63 | for (uint16_t i = 0; i <= 0xfff; i++) { 64 | uint32_t coded = c.encode(i); 65 | 66 | // Test single-bit errors 67 | BitmaskCombination bmc1; 68 | for (uint32_t mask = bmc1.next(); mask; mask = bmc1.next()) { 69 | bool decodeSuccess; 70 | uint32_t corrupted = coded ^ mask; 71 | uint32_t decoded = c.decode(corrupted, decodeSuccess); 72 | 73 | if (!decodeSuccess || decoded != i) { 74 | cout << "FAIL! Could not decode two-bit error." << endl 75 | << " the input=\t" << BinaryPrint(i) << endl 76 | << " encoded=\t" << BinaryPrint(coded) << endl 77 | << " the mask=\t" << BinaryPrint(mask) << endl 78 | << " corrupted=\t" << BinaryPrint(corrupted) << endl 79 | << " output=\t" << BinaryPrint(decoded) << endl; 80 | } 81 | else { 82 | cout << "\rSo far so good: " << i; 83 | } 84 | assert(decoded == i); 85 | } 86 | } 87 | cout << endl; 88 | 89 | // Three-bit errors 90 | cout << "Testing three-bit errors" << endl; 91 | for (uint16_t i = 0; i <= 0xfff; i++) { 92 | uint32_t coded = c.encode(i); 93 | 94 | // Test single-bit errors 95 | BitmaskCombination bmc1; 96 | for (uint32_t mask = bmc1.next(); mask; mask = bmc1.next()) { 97 | bool decodeSuccess; 98 | uint32_t corrupted = coded ^ mask; 99 | uint32_t decoded = c.decode(corrupted, decodeSuccess); 100 | 101 | if (!decodeSuccess || decoded != i) { 102 | cout << "FAIL! Could not decode three-bit error." << endl 103 | << " the input=\t" << BinaryPrint(i) << endl 104 | << " encoded=\t" << BinaryPrint(coded) << endl 105 | << " the mask=\t" << BinaryPrint(mask) << endl 106 | << " corrupted=\t" << BinaryPrint(corrupted) << endl 107 | << " output=\t" << BinaryPrint(decoded) << endl; 108 | } 109 | else { 110 | cout << "\rSo far so good: " << i; 111 | } 112 | assert(decoded == i); 113 | } 114 | } 115 | cout << endl; 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /tests/test_hamming.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of fecmagic, the forward error correction library. 3 | // Copyright (c) 2016 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include 25 | #include "../src/hamming.h" 26 | 27 | using namespace std; 28 | using namespace fecmagic; 29 | 30 | int main() { 31 | HammingCode c; 32 | 33 | for (uint8_t i = 0; i <= 0b00001111; i++) { 34 | uint8_t coded = c.encode(i); 35 | 36 | // Test single-bit errors 37 | BitmaskCombination bmc1; 38 | for (uint8_t mask = bmc1.next(); mask; mask = bmc1.next()) { 39 | bool decodeSuccess; 40 | uint8_t corrupted = coded ^ mask; 41 | uint8_t decoded = c.decode(corrupted, decodeSuccess); 42 | 43 | if (!decodeSuccess || decoded != i) { 44 | cout << "FAIL! Could not decode single-bit error." << endl 45 | << " the input=\t" << BinaryPrint(i) << endl 46 | << " encoded=\t" << BinaryPrint(coded) << endl 47 | << " the mask=\t" << BinaryPrint(mask) << endl 48 | << " corrupted=\t" << BinaryPrint(corrupted) << endl 49 | << " output=\t" << BinaryPrint(decoded) << endl; 50 | } 51 | else { 52 | //cout << "SUCCESS! Could decode single-bit error." << endl; 53 | } 54 | assert(decoded == i); 55 | } 56 | 57 | } 58 | 59 | return 0; 60 | } 61 | --------------------------------------------------------------------------------