ph_veml7700_als/
threshold.rs1use crate::config::{MeasurementConfig, Persistence};
4use crate::measurement::AlsCounts;
5use crate::power::PowerSavingConfig;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub struct Thresholds {
17 low: AlsCounts,
18 high: AlsCounts,
19}
20
21impl Thresholds {
22 pub const fn new(low: AlsCounts, high: AlsCounts) -> Option<Self> {
24 if low.counts() <= high.counts() {
25 Some(Self { low, high })
26 } else {
27 None
28 }
29 }
30
31 pub const fn low(self) -> AlsCounts {
33 self.low
34 }
35
36 pub const fn high(self) -> AlsCounts {
38 self.high
39 }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub struct ThresholdStatus {
55 pub low: bool,
57 pub high: bool,
59}
60
61impl ThresholdStatus {
62 pub(crate) const fn decode(word: u16) -> Result<Self, ThresholdStatusDecodeError> {
63 let reserved = word & 0x3FFF;
64 if reserved != 0 {
65 return Err(ThresholdStatusDecodeError::ReservedBits { observed: reserved });
66 }
67 Ok(Self {
68 low: word & (1 << 15) != 0,
69 high: word & (1 << 14) != 0,
70 })
71 }
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77#[non_exhaustive]
78pub enum ThresholdStatusDecodeError {
79 ReservedBits {
81 observed: u16,
83 },
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub struct ThresholdMonitorConfig {
90 pub measurement: MeasurementConfig,
92 pub thresholds: Thresholds,
94 pub persistence: Persistence,
97 pub power_saving: PowerSavingConfig,
103}
104
105impl ThresholdMonitorConfig {
106 pub const fn new(
112 measurement: MeasurementConfig,
113 thresholds: Thresholds,
114 persistence: Persistence,
115 power_saving: PowerSavingConfig,
116 ) -> Self {
117 Self {
118 measurement,
119 thresholds,
120 persistence,
121 power_saving,
122 }
123 }
124}
125
126impl core::fmt::Display for ThresholdStatusDecodeError {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 match self {
129 Self::ReservedBits { observed } => {
130 write!(
131 f,
132 "reserved threshold-status bits were set: {observed:#06x}"
133 )
134 }
135 }
136 }
137}
138
139impl core::error::Error for ThresholdStatusDecodeError {}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn every_reserved_status_bit_is_rejected() {
147 for bit in 0_u32..14 {
148 let observed = 1_u16 << bit;
149 assert_eq!(
150 ThresholdStatus::decode(observed),
151 Err(ThresholdStatusDecodeError::ReservedBits { observed })
152 );
153 }
154 }
155
156 #[test]
157 fn all_documented_status_flag_combinations_decode() {
158 for (word, low, high) in [
159 (0x0000, false, false),
160 (0x4000, false, true),
161 (0x8000, true, false),
162 (0xC000, true, true),
163 ] {
164 assert_eq!(
165 ThresholdStatus::decode(word),
166 Ok(ThresholdStatus { low, high })
167 );
168 }
169 }
170
171 #[test]
172 fn thresholds_accept_equal_endpoints_and_reject_reversal() {
173 let equal = AlsCounts::from_counts(42);
174 let ordered = Thresholds::new(equal, equal).expect("equal endpoints are ordered");
175 assert_eq!(ordered.low(), equal);
176 assert_eq!(ordered.high(), equal);
177
178 let ascending =
179 Thresholds::new(AlsCounts::from_counts(42), AlsCounts::from_counts(43)).unwrap();
180 assert_eq!(ascending.low().counts(), 42);
181 assert_eq!(ascending.high().counts(), 43);
182
183 assert_eq!(
184 Thresholds::new(AlsCounts::from_counts(43), AlsCounts::from_counts(42)),
185 None
186 );
187 }
188}