Skip to main content

twine_codec/radio/
caps.rs

1// Copyright (c) 2025 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8bitflags::bitflags! {
9    /// Radio capabilities
10    #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
11    pub struct RadioCapabilities: u8 {
12        /// Radio supports no capability
13        const NONE = 0b0000_0000;
14
15        /// Radio supports AckTime event
16        const ACK_TIMEOUT = 0b0000_0001;
17
18        /// Radio supports Energy Scans
19        const ENERGY_SCAN = 0b0000_0010;
20
21        /// Radio supports TX retry logic with collision avoidance (CSMA)
22        const TRANSMIT_RETRIES = 0b0000_0100;
23
24        /// Radio supports CSMA backoff for frame transmission (but no retry)
25        const CSMA_BACKOFF = 0b0000_1000;
26
27        /// Radio supports TX security
28        const SLEEP_TO_TX = 0b0001_0000;
29
30        /// Radio supports TX security
31        const TRANSMIT_SEC = 0b0010_0000;
32
33        /// Radio supports TX at specific time
34        const TRANSMIT_TIMING = 0b0100_0000;
35
36        /// Radio supports RX at specific time
37        const RECEIVE_TIMING = 0b1000_0000;
38    }
39}
40
41impl RadioCapabilities {
42    /// Check if radio supports no capability
43    pub fn none(&self) -> bool {
44        self.is_empty()
45    }
46
47    /// Check if radio supports AckTime event
48    pub fn ack_timeout(&self) -> bool {
49        self.contains(Self::ACK_TIMEOUT)
50    }
51
52    /// Check if radio supports Energy Scans
53    pub fn energy_scan(&self) -> bool {
54        self.contains(Self::ENERGY_SCAN)
55    }
56
57    /// Check if radio supports transmit retries
58    pub fn transmit_retries(&self) -> bool {
59        self.contains(Self::TRANSMIT_RETRIES)
60    }
61
62    /// Check if radio supports CSMA backoff
63    pub fn csma_backoff(&self) -> bool {
64        self.contains(Self::CSMA_BACKOFF)
65    }
66
67    /// Check if radio supports sleep to TX
68    pub fn sleep_to_tx(&self) -> bool {
69        self.contains(Self::SLEEP_TO_TX)
70    }
71
72    /// Check if radio supports transmit security
73    pub fn transmit_security(&self) -> bool {
74        self.contains(Self::TRANSMIT_SEC)
75    }
76
77    /// Check if radio supports transmit timing
78    pub fn transmit_timing(&self) -> bool {
79        self.contains(Self::TRANSMIT_TIMING)
80    }
81
82    /// Check if radio supports receive timing
83    pub fn receive_timing(&self) -> bool {
84        self.contains(Self::RECEIVE_TIMING)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use std::sync::LazyLock;
92
93    static ALL_CAPS: LazyLock<RadioCapabilities> = LazyLock::new(|| {
94        RadioCapabilities::ACK_TIMEOUT
95            | RadioCapabilities::ENERGY_SCAN
96            | RadioCapabilities::TRANSMIT_RETRIES
97            | RadioCapabilities::CSMA_BACKOFF
98            | RadioCapabilities::SLEEP_TO_TX
99            | RadioCapabilities::TRANSMIT_SEC
100            | RadioCapabilities::TRANSMIT_TIMING
101            | RadioCapabilities::RECEIVE_TIMING
102    });
103
104    #[test]
105    fn contains_none() {
106        let caps = RadioCapabilities::NONE;
107        assert!(caps.none());
108
109        assert!(!ALL_CAPS.none());
110    }
111
112    #[test]
113    fn contains_ack_timeout() {
114        let caps = RadioCapabilities::ACK_TIMEOUT;
115        assert!(!caps.none());
116        assert!(caps.ack_timeout());
117        assert!(!caps.energy_scan());
118        assert!(!caps.transmit_retries());
119        assert!(!caps.csma_backoff());
120        assert!(!caps.sleep_to_tx());
121        assert!(!caps.transmit_security());
122        assert!(!caps.transmit_timing());
123        assert!(!caps.receive_timing());
124
125        assert!(ALL_CAPS.ack_timeout());
126    }
127
128    #[test]
129    fn contains_energy_scan() {
130        let caps = RadioCapabilities::ENERGY_SCAN;
131        assert!(!caps.none());
132        assert!(!caps.ack_timeout());
133        assert!(caps.energy_scan());
134        assert!(!caps.transmit_retries());
135        assert!(!caps.csma_backoff());
136        assert!(!caps.sleep_to_tx());
137        assert!(!caps.transmit_security());
138        assert!(!caps.transmit_timing());
139        assert!(!caps.receive_timing());
140
141        assert!(ALL_CAPS.energy_scan());
142    }
143
144    #[test]
145    fn contains_transmit_retries() {
146        let caps = RadioCapabilities::TRANSMIT_RETRIES;
147        assert!(!caps.none());
148        assert!(!caps.ack_timeout());
149        assert!(!caps.energy_scan());
150        assert!(caps.transmit_retries());
151        assert!(!caps.csma_backoff());
152        assert!(!caps.sleep_to_tx());
153        assert!(!caps.transmit_security());
154        assert!(!caps.transmit_timing());
155        assert!(!caps.receive_timing());
156
157        assert!(ALL_CAPS.transmit_retries());
158    }
159
160    #[test]
161    fn contains_csma_backoff() {
162        let caps = RadioCapabilities::CSMA_BACKOFF;
163        assert!(!caps.none());
164        assert!(!caps.ack_timeout());
165        assert!(!caps.energy_scan());
166        assert!(!caps.transmit_retries());
167        assert!(caps.csma_backoff());
168        assert!(!caps.sleep_to_tx());
169        assert!(!caps.transmit_security());
170        assert!(!caps.transmit_timing());
171        assert!(!caps.receive_timing());
172
173        assert!(ALL_CAPS.csma_backoff());
174    }
175
176    #[test]
177    fn contains_sleep_to_tx() {
178        let caps = RadioCapabilities::SLEEP_TO_TX;
179        assert!(!caps.none());
180        assert!(!caps.ack_timeout());
181        assert!(!caps.energy_scan());
182        assert!(!caps.transmit_retries());
183        assert!(!caps.csma_backoff());
184        assert!(caps.sleep_to_tx());
185        assert!(!caps.transmit_security());
186        assert!(!caps.transmit_timing());
187        assert!(!caps.receive_timing());
188
189        assert!(ALL_CAPS.sleep_to_tx());
190    }
191
192    #[test]
193    fn contains_transmit_security() {
194        let caps = RadioCapabilities::TRANSMIT_SEC;
195        assert!(!caps.none());
196        assert!(!caps.ack_timeout());
197        assert!(!caps.energy_scan());
198        assert!(!caps.transmit_retries());
199        assert!(!caps.csma_backoff());
200        assert!(!caps.sleep_to_tx());
201        assert!(caps.transmit_security());
202        assert!(!caps.transmit_timing());
203        assert!(!caps.receive_timing());
204
205        assert!(ALL_CAPS.transmit_security());
206    }
207
208    #[test]
209    fn contains_transmit_timing() {
210        let caps = RadioCapabilities::TRANSMIT_TIMING;
211        assert!(!caps.none());
212        assert!(!caps.ack_timeout());
213        assert!(!caps.energy_scan());
214        assert!(!caps.transmit_retries());
215        assert!(!caps.csma_backoff());
216        assert!(!caps.sleep_to_tx());
217        assert!(!caps.transmit_security());
218        assert!(caps.transmit_timing());
219        assert!(!caps.receive_timing());
220
221        assert!(ALL_CAPS.transmit_timing());
222    }
223
224    #[test]
225    fn contains_receive_timing() {
226        let caps = RadioCapabilities::RECEIVE_TIMING;
227        assert!(!caps.none());
228        assert!(!caps.ack_timeout());
229        assert!(!caps.energy_scan());
230        assert!(!caps.transmit_retries());
231        assert!(!caps.csma_backoff());
232        assert!(!caps.sleep_to_tx());
233        assert!(!caps.transmit_security());
234        assert!(!caps.transmit_timing());
235        assert!(caps.receive_timing());
236
237        assert!(ALL_CAPS.receive_timing());
238    }
239}