#![allow(clippy::excessive_precision)]
use crate::core::scalar::ControlScalar;
pub fn crc5(data: u32, bits: u8) -> u8 {
const POLY: u8 = 0x15; let mut crc: u8 = 0x1F;
let n = bits as u32;
for i in (0..n).rev() {
let bit = ((data >> i) & 1) as u8;
let top = (crc >> 4) & 1; crc = (crc << 1) & 0x1F; if top ^ bit != 0 {
crc ^= POLY & 0x1F;
}
}
crc & 0x1F
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EndatResult {
pub position: u32,
pub turn_count: u16,
pub crc_ok: bool,
pub alarm: bool,
pub error1: bool,
pub error2: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct EndatDecoder<S: ControlScalar> {
pub st_bits: u8,
pub mt_bits: u8,
pub scale: S,
_phantom: core::marker::PhantomData<S>,
}
impl<S: ControlScalar> EndatDecoder<S> {
pub fn new(st_bits: u8, mt_bits: u8, scale: S) -> Self {
Self {
st_bits,
mt_bits,
scale,
_phantom: core::marker::PhantomData,
}
}
pub fn decode_frame(&self, bits: u32) -> EndatResult {
let error2 = (bits >> 28) & 1 != 0;
let error1 = (bits >> 27) & 1 != 0;
let alarm = (bits >> 26) & 1 != 0;
let mt_shift = 26 - self.mt_bits as u32;
let mt_mask = (1u32 << self.mt_bits) - 1;
let turn_count = ((bits >> mt_shift) & mt_mask) as u16;
let st_shift = mt_shift - self.st_bits as u32;
let st_mask = (1u32 << self.st_bits) - 1;
let position = (bits >> st_shift) & st_mask;
let crc_received = (bits & 0x1F) as u8;
let data_field = (bits >> 5) & 0x00FF_FFFF;
let data_bits: u8 = 24; let crc_computed = crc5(data_field, data_bits);
EndatResult {
position,
turn_count,
crc_ok: crc_received == crc_computed,
alarm,
error1,
error2,
}
}
pub fn to_angle(&self, raw_pos: u32) -> S {
let full_scale = S::from_f64((1u64 << self.st_bits) as f64);
let raw = S::from_f64(raw_pos as f64);
(raw / full_scale) * self.scale
}
pub fn to_turns(&self, raw_mt: u16) -> S {
S::from_f64(raw_mt as f64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crc5_zero_data() {
let crc = crc5(0, 8);
assert!(crc <= 0x1F, "CRC must fit in 5 bits, got {crc}");
}
#[test]
fn test_to_angle_full_scale() {
let dec = EndatDecoder::<f32>::new(23, 12, 2.0 * core::f32::consts::PI);
let full = (1u32 << 23) - 1;
let angle = dec.to_angle(full);
let two_pi = 2.0 * core::f32::consts::PI;
assert!(angle > 0.0 && angle < two_pi, "angle={angle}");
assert!(
(angle - two_pi).abs() < 0.001,
"angle≈2π expected, got {angle}"
);
}
#[test]
fn test_to_angle_quarter_scale() {
let dec = EndatDecoder::<f32>::new(23, 12, 2.0 * core::f32::consts::PI);
let quarter = 1u32 << 21; let angle = dec.to_angle(quarter);
let expected = core::f32::consts::PI / 2.0;
assert!(
(angle - expected).abs() < 0.001,
"angle={angle}, expected={expected}"
);
}
#[test]
fn test_decode_frame_alarm_bit() {
let frame = 1u32 << 26;
let dec = EndatDecoder::<f32>::new(16, 8, 2.0 * core::f32::consts::PI);
let result = dec.decode_frame(frame);
assert!(result.alarm, "Alarm bit should be set");
assert!(!result.error1);
assert!(!result.error2);
}
#[test]
fn test_decode_frame_no_errors() {
let frame = 0u32;
let dec = EndatDecoder::<f32>::new(16, 8, 2.0 * core::f32::consts::PI);
let result = dec.decode_frame(frame);
assert!(!result.alarm);
assert!(!result.error1);
assert!(!result.error2);
assert_eq!(result.position, 0);
assert_eq!(result.turn_count, 0);
}
#[test]
fn test_to_turns() {
let dec = EndatDecoder::<f32>::new(23, 12, 2.0 * core::f32::consts::PI);
let turns = dec.to_turns(42);
assert!((turns - 42.0_f32).abs() < 1e-6, "turns={turns}");
}
}