You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/tests/edac/Hamming_Tests.cpp

659 lines
20 KiB

// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Test Suite
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
*
*/
#include "common/Log.h"
#include "common/Utils.h"
#include <catch2/catch_test_macros.hpp>
#include <cstring>
#include "common/edac/Hamming.h"
using namespace edac;
// ---------------------------------------------------------------------------
// Hamming (15,11,3) Type 1 Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming15113_1 encode preserves zero data", "[edac][hamming]") {
bool data[15] = {false};
Hamming::encode15113_1(data);
// All parity bits should be zero for all-zero data
REQUIRE(data[11] == false);
REQUIRE(data[12] == false);
REQUIRE(data[13] == false);
REQUIRE(data[14] == false);
}
TEST_CASE("Hamming15113_1 encode/decode round trip", "[edac][hamming]") {
bool original[15] = {true, false, true, false, true, false, true, false, true, false, true, false, false, false, false};
bool data[15];
::memcpy(data, original, sizeof(data));
// Encode
Hamming::encode15113_1(data);
// Should not have errors
bool hasErrors = Hamming::decode15113_1(data);
REQUIRE_FALSE(hasErrors);
// Data bits should match original
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming15113_1 corrects single data bit errors", "[edac][hamming]") {
bool original[15] = {true, true, false, false, true, true, false, false, true, true, false, false, false, false, false};
bool data[15];
// Test single bit errors in all 11 data bit positions
for (int bit = 0; bit < 11; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode15113_1(data);
// Introduce error
data[bit] = !data[bit];
// Decode should correct it
bool hasErrors = Hamming::decode15113_1(data);
REQUIRE(hasErrors);
// Data should match original
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming15113_1 corrects single parity bit errors", "[edac][hamming]") {
bool original[15] = {false, true, false, true, false, true, false, true, false, true, false, false, false, false, false};
bool data[15];
// Test single bit errors in all 4 parity bit positions
for (int bit = 11; bit < 15; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode15113_1(data);
// Save correct parity
bool savedParity[4];
for (int i = 0; i < 4; i++) {
savedParity[i] = data[11 + i];
}
// Introduce error in parity bit
data[bit] = !data[bit];
// Decode should correct it
bool hasErrors = Hamming::decode15113_1(data);
REQUIRE(hasErrors);
// Parity should be corrected
for (int i = 0; i < 4; i++) {
REQUIRE(data[11 + i] == savedParity[i]);
}
}
}
TEST_CASE("Hamming15113_1 detects no errors in valid codeword", "[edac][hamming]") {
bool data[15] = {true, false, true, true, false, true, false, true, true, false, true, false, false, false, false};
Hamming::encode15113_1(data);
bool hasErrors = Hamming::decode15113_1(data);
REQUIRE_FALSE(hasErrors);
}
// ---------------------------------------------------------------------------
// Hamming (15,11,3) Type 2 Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming15113_2 encode preserves zero data", "[edac][hamming]") {
bool data[15] = {false};
Hamming::encode15113_2(data);
// All parity bits should be zero for all-zero data
REQUIRE(data[11] == false);
REQUIRE(data[12] == false);
REQUIRE(data[13] == false);
REQUIRE(data[14] == false);
}
TEST_CASE("Hamming15113_2 encode/decode round trip", "[edac][hamming]") {
bool original[15] = {false, true, true, false, true, false, false, true, true, false, true, false, false, false, false};
bool data[15];
::memcpy(data, original, sizeof(data));
Hamming::encode15113_2(data);
bool hasErrors = Hamming::decode15113_2(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming15113_2 corrects single data bit errors", "[edac][hamming]") {
bool original[15] = {true, false, true, false, true, true, true, false, false, true, true, false, false, false, false};
bool data[15];
for (int bit = 0; bit < 11; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode15113_2(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode15113_2(data);
REQUIRE(hasErrors);
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming15113_2 corrects single parity bit errors", "[edac][hamming]") {
bool original[15] = {true, true, false, false, true, false, true, true, false, false, true, false, false, false, false};
bool data[15];
for (int bit = 11; bit < 15; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode15113_2(data);
bool savedParity[4];
for (int i = 0; i < 4; i++) {
savedParity[i] = data[11 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode15113_2(data);
REQUIRE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[11 + i] == savedParity[i]);
}
}
}
// ---------------------------------------------------------------------------
// Hamming (13,9,3) Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming1393 encode preserves zero data", "[edac][hamming]") {
bool data[13] = {false};
Hamming::encode1393(data);
// All parity bits should be zero for all-zero data
REQUIRE(data[9] == false);
REQUIRE(data[10] == false);
REQUIRE(data[11] == false);
REQUIRE(data[12] == false);
}
TEST_CASE("Hamming1393 encode/decode round trip", "[edac][hamming]") {
bool original[13] = {true, false, true, false, true, false, true, false, true, false, false, false, false};
bool data[13];
::memcpy(data, original, sizeof(data));
Hamming::encode1393(data);
bool hasErrors = Hamming::decode1393(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 9; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming1393 corrects single data bit errors", "[edac][hamming]") {
bool original[13] = {false, true, false, true, false, true, false, true, false, false, false, false, false};
bool data[13];
for (int bit = 0; bit < 9; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode1393(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode1393(data);
REQUIRE(hasErrors);
for (int i = 0; i < 9; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming1393 corrects single parity bit errors", "[edac][hamming]") {
bool original[13] = {true, true, true, false, false, false, true, true, true, false, false, false, false};
bool data[13];
for (int bit = 9; bit < 13; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode1393(data);
bool savedParity[4];
for (int i = 0; i < 4; i++) {
savedParity[i] = data[9 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode1393(data);
REQUIRE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[9 + i] == savedParity[i]);
}
}
}
// ---------------------------------------------------------------------------
// Hamming (10,6,3) Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming1063 encode preserves zero data", "[edac][hamming]") {
bool data[10] = {false};
Hamming::encode1063(data);
// All parity bits should be zero for all-zero data
REQUIRE(data[6] == false);
REQUIRE(data[7] == false);
REQUIRE(data[8] == false);
REQUIRE(data[9] == false);
}
TEST_CASE("Hamming1063 encode/decode round trip", "[edac][hamming]") {
bool original[10] = {true, false, true, false, true, false, false, false, false, false};
bool data[10];
::memcpy(data, original, sizeof(data));
Hamming::encode1063(data);
bool hasErrors = Hamming::decode1063(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 6; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming1063 corrects single data bit errors", "[edac][hamming]") {
bool original[10] = {false, true, true, false, true, true, false, false, false, false};
bool data[10];
for (int bit = 0; bit < 6; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode1063(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode1063(data);
REQUIRE(hasErrors);
for (int i = 0; i < 6; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming1063 corrects single parity bit errors", "[edac][hamming]") {
bool original[10] = {true, false, false, true, true, false, false, false, false, false};
bool data[10];
for (int bit = 6; bit < 10; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode1063(data);
bool savedParity[4];
for (int i = 0; i < 4; i++) {
savedParity[i] = data[6 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode1063(data);
REQUIRE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[6 + i] == savedParity[i]);
}
}
}
// ---------------------------------------------------------------------------
// Hamming (16,11,4) Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming16114 encode preserves zero data", "[edac][hamming]") {
bool data[16] = {false};
Hamming::encode16114(data);
// All parity bits should be zero for all-zero data
for (int i = 11; i < 16; i++) {
REQUIRE(data[i] == false);
}
}
TEST_CASE("Hamming16114 encode/decode round trip", "[edac][hamming]") {
bool original[16] = {true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false};
bool data[16];
::memcpy(data, original, sizeof(data));
Hamming::encode16114(data);
bool hasErrors = Hamming::decode16114(data);
REQUIRE(hasErrors); // Returns true even when no errors (see code)
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming16114 corrects single data bit errors", "[edac][hamming]") {
bool original[16] = {false, true, true, false, true, false, false, true, true, false, true, false, false, false, false, false};
bool data[16];
for (int bit = 0; bit < 11; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode16114(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode16114(data);
REQUIRE(hasErrors);
for (int i = 0; i < 11; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming16114 corrects single parity bit errors", "[edac][hamming]") {
bool original[16] = {true, true, false, false, true, true, false, false, true, true, false, false, false, false, false, false};
bool data[16];
for (int bit = 11; bit < 16; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode16114(data);
bool savedParity[5];
for (int i = 0; i < 5; i++) {
savedParity[i] = data[11 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode16114(data);
REQUIRE(hasErrors);
for (int i = 0; i < 5; i++) {
REQUIRE(data[11 + i] == savedParity[i]);
}
}
}
TEST_CASE("Hamming16114 detects double-bit errors", "[edac][hamming]") {
bool original[16] = {true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false};
bool data[16];
::memcpy(data, original, sizeof(data));
Hamming::encode16114(data);
// Introduce two bit errors
data[0] = !data[0];
data[5] = !data[5];
// Should detect but may not correct properly
bool result = Hamming::decode16114(data);
// With (16,11,4), we can detect 2-bit errors but correction may fail
// Result will be false for unrecoverable errors
if (!result) {
REQUIRE_FALSE(result); // Properly detected as uncorrectable
}
}
// ---------------------------------------------------------------------------
// Hamming (17,12,3) Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming17123 encode preserves zero data", "[edac][hamming]") {
bool data[17] = {false};
Hamming::encode17123(data);
// All parity bits should be zero for all-zero data
for (int i = 12; i < 17; i++) {
REQUIRE(data[i] == false);
}
}
TEST_CASE("Hamming17123 encode/decode round trip", "[edac][hamming]") {
bool original[17] = {true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false, false};
bool data[17];
::memcpy(data, original, sizeof(data));
Hamming::encode17123(data);
bool hasErrors = Hamming::decode17123(data);
REQUIRE(hasErrors); // Returns true even when no errors (see code)
for (int i = 0; i < 12; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming17123 corrects single data bit errors", "[edac][hamming]") {
bool original[17] = {false, true, true, false, true, false, false, true, true, false, true, false, false, false, false, false, false};
bool data[17];
for (int bit = 0; bit < 12; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode17123(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode17123(data);
REQUIRE(hasErrors);
for (int i = 0; i < 12; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming17123 corrects single parity bit errors", "[edac][hamming]") {
bool original[17] = {true, true, false, false, true, true, false, false, true, true, false, false, false, false, false, false, false};
bool data[17];
for (int bit = 12; bit < 17; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode17123(data);
bool savedParity[5];
for (int i = 0; i < 5; i++) {
savedParity[i] = data[12 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode17123(data);
REQUIRE(hasErrors);
for (int i = 0; i < 5; i++) {
REQUIRE(data[12 + i] == savedParity[i]);
}
}
}
TEST_CASE("Hamming17123 detects uncorrectable errors", "[edac][hamming]") {
bool original[17] = {true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false, false};
bool data[17];
::memcpy(data, original, sizeof(data));
Hamming::encode17123(data);
// Introduce multiple bit errors beyond correction capability
data[0] = !data[0];
data[3] = !data[3];
data[7] = !data[7];
bool result = Hamming::decode17123(data);
// Should return false for unrecoverable errors
if (!result) {
REQUIRE_FALSE(result);
}
}
// ---------------------------------------------------------------------------
// Hamming (8,4,4) Tests
// ---------------------------------------------------------------------------
TEST_CASE("Hamming844 encode preserves zero data", "[edac][hamming]") {
bool data[8] = {false};
Hamming::encode844(data);
// All parity bits should be zero for all-zero data
for (int i = 4; i < 8; i++) {
REQUIRE(data[i] == false);
}
}
TEST_CASE("Hamming844 encode/decode round trip", "[edac][hamming]") {
bool original[8] = {true, false, true, false, false, false, false, false};
bool data[8];
::memcpy(data, original, sizeof(data));
Hamming::encode844(data);
bool hasErrors = Hamming::decode844(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming844 corrects single data bit errors", "[edac][hamming]") {
bool original[8] = {false, true, true, false, false, false, false, false};
bool data[8];
for (int bit = 0; bit < 4; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode844(data);
data[bit] = !data[bit];
bool hasErrors = Hamming::decode844(data);
REQUIRE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[i] == original[i]);
}
}
}
TEST_CASE("Hamming844 corrects single parity bit errors", "[edac][hamming]") {
bool original[8] = {true, true, false, false, false, false, false, false};
bool data[8];
for (int bit = 4; bit < 8; bit++) {
::memcpy(data, original, sizeof(data));
Hamming::encode844(data);
bool savedParity[4];
for (int i = 0; i < 4; i++) {
savedParity[i] = data[4 + i];
}
data[bit] = !data[bit];
bool hasErrors = Hamming::decode844(data);
REQUIRE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[4 + i] == savedParity[i]);
}
}
}
TEST_CASE("Hamming844 detects double-bit errors", "[edac][hamming]") {
bool original[8] = {true, false, true, false, false, false, false, false};
bool data[8];
::memcpy(data, original, sizeof(data));
Hamming::encode844(data);
// Introduce two bit errors
data[0] = !data[0];
data[2] = !data[2];
// Hamming (8,4,4) can detect double-bit errors but not correct them
bool result = Hamming::decode844(data);
// Should return false for uncorrectable double-bit error
REQUIRE_FALSE(result);
}
TEST_CASE("Hamming844 handles all-ones data", "[edac][hamming]") {
bool original[8] = {true, true, true, true, false, false, false, false};
bool data[8];
::memcpy(data, original, sizeof(data));
Hamming::encode844(data);
bool hasErrors = Hamming::decode844(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[i] == original[i]);
}
}
TEST_CASE("Hamming844 various data patterns", "[edac][hamming]") {
const bool patterns[][4] = {
{false, false, false, false},
{true, true, true, true},
{true, false, true, false},
{false, true, false, true},
{true, true, false, false},
{false, false, true, true},
{true, false, false, true},
{false, true, true, false}
};
for (auto& pattern : patterns) {
bool data[8];
for (int i = 0; i < 4; i++) {
data[i] = pattern[i];
}
Hamming::encode844(data);
bool hasErrors = Hamming::decode844(data);
REQUIRE_FALSE(hasErrors);
for (int i = 0; i < 4; i++) {
REQUIRE(data[i] == pattern[i]);
}
}
}

Powered by TurnKey Linux.