knx_rust/dpt/
float_16.rs

1use std::fmt::{Display, Formatter};
2use crate::dpt::DPT;
3use crate::knxnet::KnxNetIpError;
4// Datapoint types "2-Octed Float Value" (See 3/7/2 3.10)
5
6#[derive(Debug, Copy, Clone, PartialEq, Default)]
7pub struct f16(u16);
8
9impl f16{
10    fn from_f32(f: f32) -> f16{
11        let x = (f*100f32).to_bits();
12        let sign = x & 0x8000_0000u32;
13        let mut exp = ((x & 0x7F80_0000u32) >> 23) as i32 - 127 - 10; // 127 from IEEE standard and -10 because comma sits at the end in KNX f16
14        let mut man = ((x & 0x007F_FFFFu32) | 0x0080_0000u32) >> 13 ; // only 11 of 24 bits fit into our f16 type
15
16        //special handling for 0
17        if exp == -127 {
18            return f16(0)
19        }
20
21        while exp < 0 {
22            man >>= 1;
23            exp += 1
24        }
25
26        while exp > 15 {
27            man <<= 1;
28            exp -= 1
29        }
30
31        if sign > 0 {
32            man = !man + 1
33        }
34
35        f16(((man & 0x800) << 4) as u16 | ((exp as u16)  << 11) | (man & 0x7FF)  as u16)
36    }
37
38    fn as_f32(&self) -> f32{
39        let man =  self.0 & 0x7FF;
40        let exp = (self.0 >> 11) & 0xf;
41
42        if (self.0 & 0x8000) > 0 {
43            -0.01f32 * (((!man + 1)&0x7FF)<<exp) as f32
44        } else {
45            0.01f32 * (man<<exp) as f32
46        }
47    }
48}
49
50impl DPT for f16 {
51    fn encode(&self, buf: &mut Vec<u8>) {
52        self.0.encode(buf);
53    }
54
55    fn decode(&mut self, buf: &[u8]) -> Result<(), KnxNetIpError> where Self: Sized {
56        self.0.decode(buf)
57    }
58
59    fn bit_len(&self) -> u16 {
60        self.0.bit_len()
61    }
62}
63
64macro_rules! impl_f16_type {
65    ($name: ident, $format: literal, $min: literal, $max: literal) => {
66        #[derive(Debug, Copy, Clone, PartialEq, Default)]
67        pub struct $name(f16);
68
69        impl $name {
70            pub fn from_bytes(buf: &[u8]) -> Result<$name, KnxNetIpError> {
71                let mut res = $name::default();
72                res.decode(buf)?;
73                Ok(res)
74            }
75            pub fn from_float32(v: f32) -> $name{
76                return $name(f16::from_f32(v))
77            }
78            pub fn as_float32(&self) -> f32{
79                return self.0.as_f32()
80            }
81        }
82
83        impl DPT for $name {
84            fn encode(&self, buf: &mut Vec<u8>) {
85                self.0.encode(buf);
86            }
87
88            fn decode(&mut self, buf: &[u8]) -> Result<(), KnxNetIpError> where Self: Sized {
89                self.0.decode(buf)
90            }
91
92            fn bit_len(&self) -> u16 {
93                self.0.bit_len()
94            }
95        }
96
97        impl Display for $name {
98            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99                write!(f, $format, self.0.as_f32())
100            }
101        }
102    }
103}
104
105//9.001
106impl_f16_type!(DptValueTemp, "{:.2} °C", -273.0, 670_760.0);
107//9.002
108impl_f16_type!(DptValueTempd, "{:.2} K", -670_760.0, 670_760.0);
109//9.003
110impl_f16_type!(DptValueTempa, "{:.2} K/h", -670_760.0, 670_760.0);
111//9.004
112impl_f16_type!(DptValueLux, "{:.2} Lux", 0, 670_760.0);
113//9.005
114impl_f16_type!(DptValueWsp, "{:.2} m/s", 0, 670_760.0);
115//9.006
116impl_f16_type!(DptValuePres, "{:.2} Pa", 0, 670_760.0);
117//9.007
118impl_f16_type!(DptValueHumidity, "{:.2} %", 0, 670_760.0);
119//9.008
120impl_f16_type!(DptValueAirQuality, "{:.2} ppm", 0, 670_760.0);
121//9.010
122impl_f16_type!(DptValueTime1, "{:.2} s", -670_760.0, 670_760.0);
123//9.011
124impl_f16_type!(DptValueTime2, "{:.2} ms", -670_760.0, 670_760.0);
125//9.020
126impl_f16_type!(DptValueVolt, "{:.2} mV", -670_760.0, 670_760.0);
127//9.021
128impl_f16_type!(DptValueCurr, "{:.2} mA", -670_760.0, 670_760.0);
129//9.022
130impl_f16_type!(DptPowerDensity, "{:.2} W/m²", -670_760.0, 670_760.0);
131//9.023
132impl_f16_type!(DptKelvinPerPercent, "{:.2} K/%", -670_760.0, 670_760.0);
133//9.024
134impl_f16_type!(DptPower, "{:.2} kW", -670_760.0, 670_760.0);
135//9.025
136impl_f16_type!(DptValueVolumeFlow, "{:.2} l/h", -670_760.0, 670_760.0);
137//9.026
138impl_f16_type!(DptRainAmount, "{:.2} l/m²", -671_088.64, 670_760.96);
139//9.027
140impl_f16_type!(DptValueTempF, "{:.2} °F", -459.6, 670_760.96);
141//9.028
142impl_f16_type!(DptValueWspKmh, "{:.2} km/h", 0, 670_760.96);
143
144
145#[cfg(test)]
146mod tests {
147    use crate::dpt::float_16::f16;
148
149    #[test]
150    fn f16_enc_tests() {
151        assert_eq!(f16::from_f32(0f32), f16(0));
152        assert_eq!(f16::from_f32(1.0f32), f16(100));
153        assert_eq!(f16::from_f32(0.01f32), f16(1));
154        assert_eq!(f16::from_f32(-1f32), f16(0x879c));
155        assert_eq!(f16::from_f32(20.48f32), f16(0x0C00));
156    }
157
158    #[test]
159    fn f16_dec_test() {
160        assert_eq!(f16(0).as_f32(), 0f32);
161        assert_eq!(f16(100).as_f32(), 1f32);
162        assert_eq!(f16(1).as_f32(), 0.01f32);
163        assert_eq!(f16(0x879c).as_f32(), -1f32);
164        assert_eq!(f16(0xc00).as_f32(), 20.48f32);
165    }
166}