document design choices and implement hemming (26, 31) with basic tests
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
mod types;
|
||||
|
||||
use embedded_hal::i2c::{I2c, Operation, SevenBitAddress};
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
//! # Base Value Types
|
||||
//!
|
||||
//! This module implements multiples types used for storing and transmitting/receiving data with
|
||||
//! build in error detection/correction.
|
||||
//!
|
||||
//! For the purposes in the context of this project the following assumptions where made to determine the required data size.
|
||||
//!
|
||||
//! The largest value that could be stored or transmitted would be the running counter for the total amount of charge
|
||||
//! or discharge that has occured since the beginning of recording. The intended use here is the monitoring of single
|
||||
//! 12V LiFePO4 batteries which come in sizes up to 300 Ah. Many of the larger scale ones already come with a more advanced
|
||||
//! BMS, so this is targetting lower tier ones with sizes up to 15 Ah.
|
||||
//!
|
||||
//! Modern LiFePO4 battery can easily withstand past 3000 charge cycles with about 365 charge cycles per year correspoding to
|
||||
//! a charge cycle per day with solar energy.
|
||||
//!
|
||||
//! Targeting a 10 year lifespan would result in an absolute maxium of
|
||||
//! 10,000 mAh * 10 years * 365 days = 53,400,000 mAh of total charge or discharge.
|
||||
//!
|
||||
//! Using a u26 would give us a counter for up to 2**26 = 67,108,863 mAh which is sufficient and allows using up to 6 bits for
|
||||
//! redundancy within the 32bit word width.
|
||||
//!
|
||||
//! ## Use cases:
|
||||
//!
|
||||
//! ### Storage
|
||||
//!
|
||||
//! Storing data on flash for long term history retention might yield errors when flash cells are faulty leading most probably to on
|
||||
//! bit errors. Ussing a hemming code for storing data (giving us 1bit error correction) requires 5 bits which is easily fits in the 6 bit redundancy data. For extra
|
||||
//! redundancy a crc sum could be stored additionally to non recoverable errors.
|
||||
//!
|
||||
//! ### Transmission
|
||||
//!
|
||||
//! During transmission of data via serial interfaces, on bit errors ar less of a concern, here burst errors are more common du to
|
||||
//! possible interfence on the transmission line. Using hemming codes would be waistul here. Instead a crc code is used for error
|
||||
//! detection. If an error is detected the client device is expected to rerequest the data.
|
||||
//!
|
||||
//! ## CRC Selection
|
||||
//!
|
||||
//! Selecting a good crc polynomial requires careful consideration on the use data length and possible room within the code desired
|
||||
//! code word lenght. For a good summary on CRC tradeoffs refer to this [paper](https://users.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf).
|
||||
//! For the purposes of this project the following selection was made.
|
||||
//!
|
||||
//! CRC-6 using the generator polynomial 0x21 is the best choice for a data length of 26 bits and giving us a hemming distance of
|
||||
//! 3 for every valid code word. Meaning it is guaranteed that any error up to 3 bits will always be deteced. Beyond that more
|
||||
//! errors random errors in the transmission have a probabilty of 2**-6 = 0.015625 of matching the original messages CRC code.
|
||||
//! Meaning the chance of errors going undetected is <2%.
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
/// this type is used for storing data on memory devices the concern here is mostly protecting against memory corruption
|
||||
/// du to faulty memory cells. Here one bit errors are the most probabl cause of errors so this types uses a build in
|
||||
/// hemming code for one mit error correction.
|
||||
///
|
||||
/// additionally it is advised to store an extra checks sum to protect against multi bit errors
|
||||
///
|
||||
/// this type is stored in u32 in the following form:
|
||||
/// | 26 bits u26 value | 5 hemming bits | 1 unused extrabit |
|
||||
pub struct Stored_U26(u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
/// this type is used for transmitting data, the concern here is protection against transmission errors which most likely occur
|
||||
/// as burst errors effecting multiple bits, hemming codes would be a waist of space here so instead a 6 bit crc code is used
|
||||
///
|
||||
/// this type is stored in u32 in the following form:
|
||||
/// | 26 bits u26 value | 6 bits crc value |
|
||||
pub struct Transit_U26(u32);
|
||||
|
||||
// only calc hemming code for the first 24 data bits including the 5 hemming bits
|
||||
// pos | | p1 | p2 | p3 | p4 | p5 |
|
||||
// 00001 | p1 | o | | | | |
|
||||
// 00010 | p2 | | o | | | |
|
||||
// 00011 | d1 | x | x | | | |
|
||||
// 00100 | p3 | | | o | | |
|
||||
// 00101 | d2 | x | | x | | |
|
||||
// 00110 | d3 | | x | x | | |
|
||||
// 00111 | d4 | x | x | x | | |
|
||||
// 01000 | p4 | | | | o | |
|
||||
// 01001 | d5 | x | | | x | |
|
||||
// 01010 | d6 | | x | | x | |
|
||||
// 01011 | d7 | x | x | | x | |
|
||||
// 01100 | d8 | | | x | x | |
|
||||
// 01101 | d9 | x | | x | x | |
|
||||
// 01110 | d10 | | x | x | x | |
|
||||
// 01111 | d11 | x | x | x | x | |
|
||||
// 10000 | p5 | | | | | o |
|
||||
// 10001 | d12 | x | | | | x |
|
||||
// 10010 | d13 | | x | | | x |
|
||||
// 10011 | d14 | x | x | | | x |
|
||||
// 10100 | d15 | | | x | | x |
|
||||
// 10101 | d16 | x | | x | | x |
|
||||
// 10110 | d17 | | x | x | | x |
|
||||
// 10111 | d18 | x | x | x | | x |
|
||||
// 11000 | d19 | | | | x | x |
|
||||
// 11001 | d20 | x | | | x | x |
|
||||
// 11010 | d21 | | x | | x | x |
|
||||
// 11011 | d22 | x | x | | x | x |
|
||||
// 11100 | d23 | | | x | x | x |
|
||||
// 11101 | d24 | x | | x | x | x |
|
||||
// 11110 | d25 | | x | x | x | x |
|
||||
// 11111 | d26 | x | x | x | x | x |
|
||||
fn calc_hemming(data_bits: u32) -> u8 {
|
||||
let p1 = ((data_bits)
|
||||
^ (data_bits >> 1)
|
||||
^ (data_bits >> 3)
|
||||
^ (data_bits >> 4)
|
||||
^ (data_bits >> 6)
|
||||
^ (data_bits >> 8)
|
||||
^ (data_bits >> 10)
|
||||
^ (data_bits >> 11)
|
||||
^ (data_bits >> 13)
|
||||
^ (data_bits >> 15)
|
||||
^ (data_bits >> 17)
|
||||
^ (data_bits >> 19)
|
||||
^ (data_bits >> 21)
|
||||
^ (data_bits >> 23)
|
||||
^ (data_bits >> 25))
|
||||
& 0x0001;
|
||||
let p2 = ((data_bits)
|
||||
^ (data_bits >> 2)
|
||||
^ (data_bits >> 3)
|
||||
^ (data_bits >> 5)
|
||||
^ (data_bits >> 6)
|
||||
^ (data_bits >> 9)
|
||||
^ (data_bits >> 10)
|
||||
^ (data_bits >> 12)
|
||||
^ (data_bits >> 13)
|
||||
^ (data_bits >> 16)
|
||||
^ (data_bits >> 17)
|
||||
^ (data_bits >> 20)
|
||||
^ (data_bits >> 21)
|
||||
^ (data_bits >> 24)
|
||||
^ (data_bits >> 25))
|
||||
& 0x0001;
|
||||
let p3 = ((data_bits >> 1)
|
||||
^ (data_bits >> 2)
|
||||
^ (data_bits >> 3)
|
||||
^ (data_bits >> 7)
|
||||
^ (data_bits >> 8)
|
||||
^ (data_bits >> 9)
|
||||
^ (data_bits >> 10)
|
||||
^ (data_bits >> 14)
|
||||
^ (data_bits >> 15)
|
||||
^ (data_bits >> 16)
|
||||
^ (data_bits >> 17)
|
||||
^ (data_bits >> 22)
|
||||
^ (data_bits >> 23)
|
||||
^ (data_bits >> 24)
|
||||
^ (data_bits >> 25))
|
||||
& 0x0001;
|
||||
let p4 = ((data_bits >> 4)
|
||||
^ (data_bits >> 5)
|
||||
^ (data_bits >> 6)
|
||||
^ (data_bits >> 7)
|
||||
^ (data_bits >> 8)
|
||||
^ (data_bits >> 9)
|
||||
^ (data_bits >> 10)
|
||||
^ (data_bits >> 18)
|
||||
^ (data_bits >> 19)
|
||||
^ (data_bits >> 20)
|
||||
^ (data_bits >> 21)
|
||||
^ (data_bits >> 22)
|
||||
^ (data_bits >> 23)
|
||||
^ (data_bits >> 24)
|
||||
^ (data_bits >> 25))
|
||||
& 0x001;
|
||||
let p5 = ((data_bits >> 11)
|
||||
^ (data_bits >> 12)
|
||||
^ (data_bits >> 13)
|
||||
^ (data_bits >> 14)
|
||||
^ (data_bits >> 15)
|
||||
^ (data_bits >> 16)
|
||||
^ (data_bits >> 17)
|
||||
^ (data_bits >> 18)
|
||||
^ (data_bits >> 19)
|
||||
^ (data_bits >> 20)
|
||||
^ (data_bits >> 21)
|
||||
^ (data_bits >> 22)
|
||||
^ (data_bits >> 23)
|
||||
^ (data_bits >> 24)
|
||||
^ (data_bits >> 25))
|
||||
& 0x0001;
|
||||
|
||||
0x00 | (p1 as u8) << 0 | (p2 as u8) << 1 | (p3 as u8) << 2 | (p4 as u8) << 3 | (p5 as u8) << 5
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod base_type_tests {
|
||||
use super::calc_hemming;
|
||||
|
||||
#[test]
|
||||
fn hemming_code_generation() {
|
||||
assert_eq!(calc_hemming(0x000), 0b00000);
|
||||
assert_eq!(calc_hemming(0x001), 0b00011);
|
||||
assert_eq!(calc_hemming(0x002), 0b00101);
|
||||
assert_eq!(calc_hemming(0x003), 0b00110);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user