use std::fmt::{Display, Formatter};
use crate::dpt::DPT;
use crate::knxnet::KnxNetIpError;
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct f16(u16);
impl f16{
fn from_f32(f: f32) -> f16{
let x = (f*100f32).to_bits();
let sign = x & 0x8000_0000u32;
let mut exp = ((x & 0x7F80_0000u32) >> 23) as i32 - 127 - 10; let mut man = ((x & 0x007F_FFFFu32) | 0x0080_0000u32) >> 13 ;
if exp == -127 {
return f16(0)
}
while exp < 0 {
man >>= 1;
exp += 1
}
while exp > 15 {
man <<= 1;
exp -= 1
}
if sign > 0 {
man = !man + 1
}
f16(((man & 0x800) << 4) as u16 | ((exp as u16) << 11) | (man & 0x7FF) as u16)
}
fn as_f32(&self) -> f32{
let man = self.0 & 0x7FF;
let exp = (self.0 >> 11) & 0xf;
if (self.0 & 0x8000) > 0 {
-0.01f32 * (((!man + 1)&0x7FF)<<exp) as f32
} else {
0.01f32 * (man<<exp) as f32
}
}
}
impl DPT for f16 {
fn encode(&self, buf: &mut Vec<u8>) {
self.0.encode(buf);
}
fn decode(&mut self, buf: &[u8]) -> Result<(), KnxNetIpError> where Self: Sized {
self.0.decode(buf)
}
fn bit_len(&self) -> u16 {
self.0.bit_len()
}
}
macro_rules! impl_f16_type {
($name: ident, $format: literal, $min: literal, $max: literal) => {
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct $name(f16);
impl $name {
pub fn from_bytes(buf: &[u8]) -> Result<$name, KnxNetIpError> {
let mut res = $name::default();
res.decode(buf)?;
Ok(res)
}
pub fn from_float32(v: f32) -> $name{
return $name(f16::from_f32(v))
}
pub fn as_float32(&self) -> f32{
return self.0.as_f32()
}
}
impl DPT for $name {
fn encode(&self, buf: &mut Vec<u8>) {
self.0.encode(buf);
}
fn decode(&mut self, buf: &[u8]) -> Result<(), KnxNetIpError> where Self: Sized {
self.0.decode(buf)
}
fn bit_len(&self) -> u16 {
self.0.bit_len()
}
}
impl Display for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, $format, self.0.as_f32())
}
}
}
}
impl_f16_type!(DptValueTemp, "{:.2} °C", -273.0, 670_760.0);
impl_f16_type!(DptValueTempd, "{:.2} K", -670_760.0, 670_760.0);
impl_f16_type!(DptValueTempa, "{:.2} K/h", -670_760.0, 670_760.0);
impl_f16_type!(DptValueLux, "{:.2} Lux", 0, 670_760.0);
impl_f16_type!(DptValueWsp, "{:.2} m/s", 0, 670_760.0);
impl_f16_type!(DptValuePres, "{:.2} Pa", 0, 670_760.0);
impl_f16_type!(DptValueHumidity, "{:.2} %", 0, 670_760.0);
impl_f16_type!(DptValueAirQuality, "{:.2} ppm", 0, 670_760.0);
impl_f16_type!(DptValueTime1, "{:.2} s", -670_760.0, 670_760.0);
impl_f16_type!(DptValueTime2, "{:.2} ms", -670_760.0, 670_760.0);
impl_f16_type!(DptValueVolt, "{:.2} mV", -670_760.0, 670_760.0);
impl_f16_type!(DptValueCurr, "{:.2} mA", -670_760.0, 670_760.0);
impl_f16_type!(DptPowerDensity, "{:.2} W/m²", -670_760.0, 670_760.0);
impl_f16_type!(DptKelvinPerPercent, "{:.2} K/%", -670_760.0, 670_760.0);
impl_f16_type!(DptPower, "{:.2} kW", -670_760.0, 670_760.0);
impl_f16_type!(DptValueVolumeFlow, "{:.2} l/h", -670_760.0, 670_760.0);
impl_f16_type!(DptRainAmount, "{:.2} l/m²", -671_088.64, 670_760.96);
impl_f16_type!(DptValueTempF, "{:.2} °F", -459.6, 670_760.96);
impl_f16_type!(DptValueWspKmh, "{:.2} km/h", 0, 670_760.96);
#[cfg(test)]
mod tests {
use crate::dpt::float_16::f16;
#[test]
fn f16_enc_tests() {
assert_eq!(f16::from_f32(0f32), f16(0));
assert_eq!(f16::from_f32(1.0f32), f16(100));
assert_eq!(f16::from_f32(0.01f32), f16(1));
assert_eq!(f16::from_f32(-1f32), f16(0x879c));
assert_eq!(f16::from_f32(20.48f32), f16(0x0C00));
}
#[test]
fn f16_dec_test() {
assert_eq!(f16(0).as_f32(), 0f32);
assert_eq!(f16(100).as_f32(), 1f32);
assert_eq!(f16(1).as_f32(), 0.01f32);
assert_eq!(f16(0x879c).as_f32(), -1f32);
assert_eq!(f16(0xc00).as_f32(), 20.48f32);
}
}