From 87bd3abaca65b148d2bc2a4e2b2be7d48748867e Mon Sep 17 00:00:00 2001 From: ju6ge Date: Sun, 22 Mar 2026 10:08:57 +0100 Subject: [PATCH] testing hemming error correction logic --- lib-bms-protocol/src/types.rs | 114 +++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 7 deletions(-) diff --git a/lib-bms-protocol/src/types.rs b/lib-bms-protocol/src/types.rs index 17638c6..df944a9 100644 --- a/lib-bms-protocol/src/types.rs +++ b/lib-bms-protocol/src/types.rs @@ -163,7 +163,7 @@ fn calc_hemming(data_bits: u32) -> u8 { ^ (data_bits >> 21) ^ (data_bits >> 23) ^ (data_bits >> 25)) - & 0x0001; + & 0x1; let p2 = ((data_bits) ^ (data_bits >> 2) ^ (data_bits >> 3) @@ -179,7 +179,7 @@ fn calc_hemming(data_bits: u32) -> u8 { ^ (data_bits >> 21) ^ (data_bits >> 24) ^ (data_bits >> 25)) - & 0x0001; + & 0x1; let p3 = ((data_bits >> 1) ^ (data_bits >> 2) ^ (data_bits >> 3) @@ -195,7 +195,7 @@ fn calc_hemming(data_bits: u32) -> u8 { ^ (data_bits >> 23) ^ (data_bits >> 24) ^ (data_bits >> 25)) - & 0x0001; + & 0x1; let p4 = ((data_bits >> 4) ^ (data_bits >> 5) ^ (data_bits >> 6) @@ -211,7 +211,7 @@ fn calc_hemming(data_bits: u32) -> u8 { ^ (data_bits >> 23) ^ (data_bits >> 24) ^ (data_bits >> 25)) - & 0x001; + & 0x1; let p5 = ((data_bits >> 11) ^ (data_bits >> 12) ^ (data_bits >> 13) @@ -227,14 +227,56 @@ fn calc_hemming(data_bits: u32) -> u8 { ^ (data_bits >> 23) ^ (data_bits >> 24) ^ (data_bits >> 25)) - & 0x0001; + & 0x1; - 0x00 | (p1 as u8) << 0 | (p2 as u8) << 1 | (p3 as u8) << 2 | (p4 as u8) << 3 | (p5 as u8) << 5 + 0x00 | (p1 as u8) << 0 | (p2 as u8) << 1 | (p3 as u8) << 2 | (p4 as u8) << 3 | (p5 as u8) << 4 +} + +/// if the hemming code does not equal the expected stored hemming value it calculated +/// hemming code will indicate the position of the invalid bit, this type is used +/// to hold the logic to use this information for correcting the read data in case of +/// an error +pub struct HemmingCorrectionValue(u8); + +impl HemmingCorrectionValue { + pub fn new(invalid_hemming: u8) -> Self { + HemmingCorrectionValue(invalid_hemming) + } + + /// calculate xor bit patterns to apply to the data or the stored hemming code + /// returns tuple -> (xor with data, xor with hemming code) + fn calc_correction_data(&self) -> (u32, u8) { + if [1, 2, 4, 8, 16].contains(&self.0) { + (0x00000000, 0x1 << self.0.ilog2()) + } else { + if self.0 == 3 { + (0x1, 0x00) + } else if self.0 < 8 { + (0x1 << self.0 - 3 - 1, 0x00) + } else if self.0 < 16 { + (0x1 << self.0 - 4 - 1, 0x00) + } else { + (0x1 << self.0 - 5 - 1, 0x00) + } + } + } +} + +/// check if hemming code matches the expected value for the provided data +pub fn check_hemming(data: u32, hemming: u8) -> Result<(), HemmingCorrectionValue> { + let h = calc_hemming(data); + if h == hemming { + Ok(()) + } else { + Err(HemmingCorrectionValue::new(h^hemming)) + } } #[cfg(test)] mod base_type_tests { - use super::calc_hemming; + use std::println; + + use super::{calc_hemming, check_hemming, U26_MAX_VALUE}; #[test] fn hemming_code_generation() { @@ -242,5 +284,63 @@ mod base_type_tests { assert_eq!(calc_hemming(0x001), 0b00011); assert_eq!(calc_hemming(0x002), 0b00101); assert_eq!(calc_hemming(0x003), 0b00110); + assert_eq!(calc_hemming(0x011), 0b01010); + } + + #[test] + fn hemming_failure() { + assert!(check_hemming(0x011, 0b00011).is_err()); + } + + #[test] + fn hemming_correct_invalid_value() { + let invalid_data = 0x011; + let hemming = 0b00011; + let correction = check_hemming(invalid_data, hemming).unwrap_err(); + let (data_correction, hemming_correction) = correction.calc_correction_data(); + assert_eq!(hemming_correction, 0); + assert_eq!(data_correction, 0x10); + let valid_data = invalid_data ^ data_correction; + assert!(check_hemming(valid_data, hemming).is_ok()); + } + + #[test] + fn hemming_correct_invalid_hemming_code() { + let data = 0x001; + let invalid_hemming = 0b00001; + let correction = check_hemming(data, invalid_hemming).unwrap_err(); + let (data_correction, hemming_correction) = correction.calc_correction_data(); + assert_eq!(hemming_correction, 0b00010); + assert_eq!(data_correction, 0x00); + let valid_hemming = invalid_hemming ^ hemming_correction; + assert!(check_hemming(data, valid_hemming).is_ok()); + } + + #[test] + fn full_onebit_value_correction_sweep() { + for val in 1..U26_MAX_VALUE { + let hemming = calc_hemming(val); + for i in 0..26 { + let bit_error = 0x1 << i; + let invalid_value = val ^ bit_error; + //println!("0x{val:x}, 0x{bit_error:x}, 0x{invalid_value:x}, 0b{hemming:b}, 0b{:b}", calc_hemming(invalid_value)); + let correction = check_hemming(invalid_value, hemming).unwrap_err(); + let (dc, _) = correction.calc_correction_data(); + assert_eq!(invalid_value ^ dc, val); + } + } + } + + #[test] + fn full_hemming_code_error_sweep() { + let data = 0x123456; + let hemming = calc_hemming(data); + for i in 0..5 { + let bit_error = 0x1 << i; + let invalid_hemming = hemming ^ bit_error; + let correction = check_hemming(data, invalid_hemming).unwrap_err(); + let (_, hc) = correction.calc_correction_data(); + assert_eq!(invalid_hemming^hc, hemming); + } } }