diff --git a/openrtx/include/protocols/M17/M17Golay.h b/openrtx/include/protocols/M17/M17Golay.h index cc50f866..ea1134d0 100644 --- a/openrtx/include/protocols/M17/M17Golay.h +++ b/openrtx/include/protocols/M17/M17Golay.h @@ -1,10 +1,10 @@ /*************************************************************************** * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * + * Wojciech Kaczmarski SP5WWP * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * * * - * Adapted from original code written by Rob Riggs, Mobilinkd LLC * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -29,9 +29,115 @@ #include +namespace Golay24 +{ + /** - * Compute the Golay(24,12) codeword of a given block of data using the M17 - * Golay polynomial. + * Data encoding matrix for Golay(24,12) code with generator polynomial 0xC75; + */ +static const uint16_t encode_matrix[12] = +{ + 0x8eb, 0x93e, 0xa97, 0xdc6, 0x367, 0x6cd, + 0xd99, 0x3da, 0x7b4, 0xf68, 0x63b, 0xc75 +}; + + +/** + * Data decoding matrix for Golay(24,12) code with generator polynomial 0xC75; + */ +static const uint16_t decode_matrix[12] = +{ + 0xc75, 0x49f, 0x93e, 0x6e3, 0xdc6, 0xf13, + 0xab9, 0x1ed, 0x3da, 0x7b4, 0xf68, 0xa4f +}; + + +/** + * Function computing the Golay(24,12) checksum of a given 12-bit data block. + * + * @param value: input data. + * @return Golay(24,12) checksum. + */ +static uint16_t calcChecksum(const uint16_t& value) +{ + uint16_t checksum = 0; + + for(uint8_t i = 0; i < 12; i++) + { + if(value & (1 << i)) + { + checksum ^= encode_matrix[i]; + } + } + + return checksum; +} + + +/** + * Detect and correct errors in a Golay(24,12) codeword. + * + * @param codeword: input codeword. + * @return bitmask corresponding to detected bit errors in the codeword, or + * 0xFFFFFFFF if bit errors are unrecoverable. + */ +static uint32_t detectErrors(const uint32_t& codeword) +{ + uint16_t data = codeword >> 12; + uint16_t parity = codeword & 0xFFF; + + uint16_t syndrome = parity ^ calcChecksum(data); + + if(__builtin_popcount(syndrome) <= 3) + { + return syndrome; + } + + for(uint8_t i = 0; i<12; i++) + { + uint16_t e = 1 << i; + uint16_t coded_error = encode_matrix[i]; + + if(__builtin_popcount(syndrome^coded_error) <= 2) + { + return (e << 12) | (syndrome ^ coded_error); + } + } + + + uint16_t inv_syndrome = 0; + for(uint8_t i = 0; i < 12; i++) + { + if(syndrome & (1 << i)) + { + inv_syndrome ^= decode_matrix[i]; + } + } + + if(__builtin_popcount(inv_syndrome) <= 3) + { + return inv_syndrome << 12; + } + + for(uint8_t i = 0; i < 12; i++ ) + { + uint16_t e = 1 << i; + uint16_t coding_error = decode_matrix[i]; + if(__builtin_popcount(inv_syndrome ^ coding_error) <= 2 ) + { + return ((inv_syndrome ^ coding_error) << 12) | e; + } + } + + return 0xFFFFFFFF; +} + +} // namespace Golay24 + + +/** + * Compute the Golay(24,12) codeword of a given block of data using the + * generator polynomial 0xC75. * Result is composed as follows: * * +--------+----------+--------+ @@ -43,21 +149,25 @@ * \param data: input data, upper four bits are discarded. * \return resulting 24 bit codeword. */ -static uint32_t golay_encode24(const uint16_t& data) +static inline uint32_t golay24_encode(const uint16_t& data) { - // Compute [23,12] Golay codeword - uint32_t codeword = data & 0x0FFFF; - for(size_t i = 0; i < 12; i++) - { - if(codeword & 0x01) codeword ^= 0x0C75; - codeword >>= 1; - } + return (data << 12) | Golay24::calcChecksum(data); +} - // Append data to codeword, result is checkbits(11) | data(12) - codeword |= (data << 11); - // Compute parity and append it to codeword - return (codeword << 1) | (__builtin_popcount(codeword) & 0x01); +/** + * Decode a Golay(24,12) codeword, correcting eventual bit errors. In case the + * bit errors are not correctable, the function returns 0xFFFF, a value outside + * the range allowed for the 12-bit input data required by Golay coding. + * + * \param codeword: input Golay(24,12) codeword. + * \return original data block or 0xFFFF in case of unrecoverable errors. + */ +static inline uint16_t golay24_decode(const uint32_t& codeword) +{ + uint32_t errors = Golay24::detectErrors(codeword); + if(errors == 0xFFFFFFFF) return 0xFFFF; + return ((codeword ^ errors) >> 12) & 0x0FFF; } #endif /* M17_GOLAY_H */ diff --git a/openrtx/src/protocols/M17/M17LinkSetupFrame.cpp b/openrtx/src/protocols/M17/M17LinkSetupFrame.cpp index 1b6e84c9..7230f60b 100644 --- a/openrtx/src/protocols/M17/M17LinkSetupFrame.cpp +++ b/openrtx/src/protocols/M17/M17LinkSetupFrame.cpp @@ -111,7 +111,7 @@ lich_t M17LinkSetupFrame::generateLichSegment(const uint8_t segmentNum) lich_t result; for(size_t i = 0; i < blocks.size(); i++) { - uint32_t encoded = golay_encode24(blocks[i]); + uint32_t encoded = golay24_encode(blocks[i]); encoded = __builtin_bswap32(encoded << 8); memcpy(&result[3*i], &encoded, 3); } diff --git a/tests/unit/.keep b/tests/unit/.keep deleted file mode 100644 index 8d1c8b69..00000000 --- a/tests/unit/.keep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/unit/M17_golay.cpp b/tests/unit/M17_golay.cpp new file mode 100644 index 00000000..b487291e --- /dev/null +++ b/tests/unit/M17_golay.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include "M17/M17Golay.h" + +using namespace std; + +default_random_engine rng; + +/** + * Generate a mask with a random number of bit errors in random positions. + */ +uint32_t generateErrorMask() +{ + uint32_t errorMask = 0; + uniform_int_distribution< uint8_t > numErrs(0, 5); + uniform_int_distribution< uint8_t > errPos(0, 23); + + for(uint8_t i = 0; i < numErrs(rng); i++) + { + errorMask |= 1 << errPos(rng); + } + + return errorMask; +} + +int main() +{ + uniform_int_distribution< uint16_t > rndValue(0, 2047); + + for(uint32_t i = 0; i < 10000; i++) + { + uint16_t value = rndValue(rng); + uint32_t cword = golay24_encode(value); + uint32_t emask = generateErrorMask(); + + // Check for correct encoding/decoding in absence of errors + bool decoding_ok = (golay24_decode(cword) == value); + + // Check for correct encoding/decoding in presence of errors + uint16_t decoded = golay24_decode(cword ^ emask); + bool correcting_ok = false; + + // For four or more bit errors, decode should return 0xFFFF (uncorrectable error) + if((__builtin_popcount(emask) >= 4) && (decoded == 0xFFFF)) + { + correcting_ok = true; + } + + // Less than four errors should be corrected. + if(decoded == value) correcting_ok = true; + + printf("Value %04x, emask %08x errs %d -> d %s, c %s\n", value, emask, + __builtin_popcount(emask), + decoding_ok ? "OK" : "FAIL", + correcting_ok ? "OK" : "FAIL"); + } + + return 0; +}