lorawan_device/region/fixed_channel_plans/
join_channels.rs1use super::*;
2use core::cmp::Ordering;
3
4#[derive(Clone, Default)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub(crate) struct JoinChannels {
7 max_retries: usize,
9 pub num_retries: usize,
11 preferred_subband: Option<Subband>,
13 pub(crate) available_channels: AvailableChannels,
15 pub(crate) previous_channel: u8,
17}
18
19impl JoinChannels {
20 pub(crate) fn has_bias_and_not_exhausted(&self) -> bool {
21 self.preferred_subband.is_some()
23 && self.num_retries < self.max_retries
24 && self.num_retries != 0
25 }
26
27 pub(crate) fn first_data_channel(&mut self, rng: &mut impl RngCore) -> Option<u8> {
30 if self.preferred_subband.is_some() && self.num_retries != 0 {
31 self.clear_join_bias();
32 let sb = if self.previous_channel < 64 {
34 self.previous_channel / 8
35 } else {
36 self.previous_channel % 8
37 };
38 Some((rng.next_u32() & 0b111) as u8 + (sb * 8))
40 } else {
41 None
42 }
43 }
44
45 pub(crate) fn set_join_bias(&mut self, subband: Subband, max_retries: usize) {
46 self.preferred_subband = Some(subband);
47 self.max_retries = max_retries;
48 }
49
50 pub(crate) fn clear_join_bias(&mut self) {
51 self.preferred_subband = None;
52 self.max_retries = 0;
53 }
54
55 pub(crate) fn reset(&mut self) {
57 self.num_retries = 0;
58 self.available_channels = AvailableChannels::default();
59 }
60
61 pub(crate) fn get_next_channel(&mut self, rng: &mut impl RngCore) -> u8 {
62 match (self.preferred_subband, self.num_retries.cmp(&self.max_retries)) {
63 (Some(sb), Ordering::Less) => {
64 self.num_retries += 1;
65 let channel = (rng.next_u32() % 8) as u8 + ((sb as usize - 1) as u8 * 8);
68 if self.num_retries == self.max_retries {
69 self.available_channels.previous = Some(channel);
73 self.available_channels.data.set_channel(channel.into(), false);
74 }
75 self.previous_channel = channel;
76 channel
77 }
78 _ => {
79 self.num_retries += 1;
80 self.available_channels.get_next(rng)
81 }
82 }
83 }
84}
85
86#[derive(Clone, Default)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88pub(crate) struct AvailableChannels {
89 data: ChannelMask<9>,
90 previous: Option<u8>,
91}
92
93impl AvailableChannels {
94 fn is_exhausted(&self) -> bool {
95 for byte in self.data.as_ref() {
97 if *byte != 0 {
98 return false;
99 }
100 }
101 true
102 }
103
104 fn get_next(&mut self, rng: &mut impl RngCore) -> u8 {
105 if self.is_exhausted() {
107 self.reset();
108 }
109
110 let channel = self.get_next_channel_inner(rng);
111 self.data.set_channel(channel.into(), false);
113 self.previous = Some(channel);
114 channel
115 }
116
117 fn get_next_channel_inner(&mut self, rng: &mut impl RngCore) -> u8 {
118 if let Some(previous) = self.previous {
119 let next = (previous + 8) % 72;
121 if self.data.is_enabled(next.into()).unwrap() {
123 next
124 } else {
125 let bank = next / 8;
131 let mut entropy = rng.next_u32();
132 let mut channel = (entropy & 0b111) as u8 + bank * 8;
133 let mut entropy_used = 1;
134 loop {
135 if self.data.is_enabled(channel.into()).unwrap() {
136 return channel;
137 } else {
138 if entropy_used == 10 {
140 entropy = rng.next_u32();
141 entropy_used = 0;
142 }
143 entropy >>= 3;
144 entropy_used += 1;
145 channel = (entropy & 0b111) as u8 + bank * 8;
146 }
147 }
148 }
149 } else {
150 (rng.next_u32() as u8) & 0b111111
153 }
154 }
155
156 fn reset(&mut self) {
157 self.data = ChannelMask::default();
158 self.previous = None;
159 }
160}
161
162macro_rules! impl_join_bias {
166 ($region:ident) => {
167 impl $region {
168 pub fn new() -> Self {
170 Self::default()
171 }
172
173 pub fn set_join_bias(&mut self, subband: Subband) {
177 self.0.join_channels.set_join_bias(subband, 1)
178 }
179
180 pub fn set_join_bias_and_noncompliant_retries(
194 &mut self,
195 subband: Subband,
196 max_retries: usize,
197 ) {
198 self.0.join_channels.set_join_bias(subband, max_retries)
199 }
200
201 pub fn clear_join_bias(&mut self) {
202 self.0.join_channels.clear_join_bias()
203 }
204 }
205 };
206}
207
208#[cfg(feature = "region-au915")]
209impl_join_bias!(AU915);
210#[cfg(feature = "region-us915")]
211impl_join_bias!(US915);
212
213#[cfg(test)]
214mod test {
215 use super::*;
216 use crate::mac::Response;
217 use crate::{
218 mac::{Mac, SendData},
219 test_util::{get_key, handle_join_request, Uplink},
220 AppEui, AppKey, DevEui, NetworkCredentials,
221 };
222 use heapless::Vec;
223 use lorawan::default_crypto::DefaultFactory;
224
225 #[test]
226 fn test_join_channels_standard() {
227 let mut rng = rand_core::OsRng;
228 for _ in 0..100 {
230 let mut join_channels = JoinChannels::default();
231 let first_channel = join_channels.get_next_channel(&mut rng);
232 assert!(first_channel < 64);
234 let next_channel = join_channels.get_next_channel(&mut rng);
235 assert_eq!(next_channel, first_channel + 8);
238 for _ in 0..7 {
240 let c = join_channels.get_next_channel(&mut rng);
241 assert!(c < 72);
242 }
243 let ninth_channel = join_channels.get_next_channel(&mut rng);
245 assert_eq!(ninth_channel / 8, first_channel / 8);
246 assert_ne!(ninth_channel, first_channel);
247 }
248 }
249
250 #[test]
251 fn test_join_channels_standard_exhausted() {
252 let mut rng = rand_core::OsRng;
253
254 let mut join_channels = JoinChannels::default();
255 let first_channel = join_channels.get_next_channel(&mut rng);
256 assert!(first_channel < 64);
258 let next_channel = join_channels.get_next_channel(&mut rng);
259 assert_eq!(next_channel, first_channel + 8);
262 for _ in 0..6000 {
264 let c = join_channels.get_next_channel(&mut rng);
265 assert!(c < 72);
266 }
267 }
268
269 #[test]
270 fn test_join_channels_biased() {
271 let mut rng = rand_core::OsRng;
272 for _ in 0..100 {
274 let mut join_channels = JoinChannels::default();
275 join_channels.set_join_bias(Subband::_2, 1);
276 let first_channel = join_channels.get_next_channel(&mut rng);
277 assert!(first_channel > 7);
279 assert!(first_channel < 16);
280 let next_channel = join_channels.get_next_channel(&mut rng);
281 assert_eq!(next_channel, first_channel + 8);
284 for _ in 0..7 {
286 let c = join_channels.get_next_channel(&mut rng);
287 assert!(c < 72);
288 }
289 let ninth_channel = join_channels.get_next_channel(&mut rng);
291 assert_eq!(ninth_channel / 8, first_channel / 8);
292 assert_ne!(ninth_channel, first_channel);
293 }
294 }
295
296 #[test]
297 fn test_full_mac_compliant_bias() {
298 let mut us915 = US915::new();
299 us915.set_join_bias(Subband::_2);
300 let mut mac = Mac::new(us915.into(), 21, 2);
301
302 let mut buf: RadioBuffer<255> = RadioBuffer::new();
303 let (tx_config, _len) = mac.join_otaa::<DefaultFactory, _, 255>(
304 &mut rand::rngs::OsRng,
305 NetworkCredentials::new(
306 AppEui::from([0x0; 8]),
307 DevEui::from([0x0; 8]),
308 AppKey::from(get_key()),
309 ),
310 &mut buf,
311 );
312 assert!(
314 tx_config.rf.frequency >= 903_900_000,
315 "Unexpected frequency: {} is below 903.9 MHz!",
316 tx_config.rf.frequency
317 );
318 assert!(
319 tx_config.rf.frequency <= 905_300_000,
320 "Unexpected frequency: {} is above 905.3 MHz!",
321 tx_config.rf.frequency
322 );
323 let mut downlinks: Vec<_, 3> = Vec::new();
324 let mut data = std::vec::Vec::new();
325 data.extend_from_slice(buf.as_ref_for_read());
326 let uplink = Uplink::new(buf.as_ref_for_read(), tx_config).unwrap();
327
328 let mut rx_buf = [0; 255];
329 let len = handle_join_request::<0>(Some(uplink), tx_config.rf, &mut rx_buf);
330 buf.clear();
331 buf.extend_from_slice(&rx_buf[..len]).unwrap();
332 let response = mac.handle_rx::<DefaultFactory, 255, 3>(&mut buf, &mut downlinks);
333 if let Response::JoinSuccess = response {
334 } else {
335 panic!("Did not receive join success");
336 }
337 let (tx_config, _len) = mac
338 .send::<DefaultFactory, _, 255>(
339 &mut rand::rngs::OsRng,
340 &mut buf,
341 &SendData { fport: 1, data: &[0x0; 1], confirmed: false },
342 )
343 .unwrap();
344 assert!(
346 tx_config.rf.frequency >= 903_900_000,
347 "Unexpected frequency: {} is below 903.9 MHz!",
348 tx_config.rf.frequency
349 );
350 assert!(
351 tx_config.rf.frequency <= 905_300_000,
352 "Unexpected frequency: {} is above 905.3 MHz!",
353 tx_config.rf.frequency
354 );
355 }
356
357 #[test]
358 fn test_full_mac_non_compliant_bias() {
359 let mut us915 = US915::new();
360 us915.set_join_bias_and_noncompliant_retries(Subband::_2, 8);
361 let mut mac = Mac::new(us915.into(), 21, 2);
362
363 let mut buf: RadioBuffer<255> = RadioBuffer::new();
364 let (tx_config, _len) = mac.join_otaa::<DefaultFactory, _, 255>(
365 &mut rand::rngs::OsRng,
366 NetworkCredentials::new(
367 AppEui::from([0x0; 8]),
368 DevEui::from([0x0; 8]),
369 AppKey::from(get_key()),
370 ),
371 &mut buf,
372 );
373 assert!(
375 tx_config.rf.frequency >= 903_900_000,
376 "Unexpected frequency: {} is below 903.9 MHz!",
377 tx_config.rf.frequency
378 );
379 assert!(
380 tx_config.rf.frequency <= 905_300_000,
381 "Unexpected frequency: {} is above 905.3 MHz!",
382 tx_config.rf.frequency
383 );
384 let mut downlinks: Vec<_, 3> = Vec::new();
385 let mut data = std::vec::Vec::new();
386 data.extend_from_slice(buf.as_ref_for_read());
387 let uplink = Uplink::new(buf.as_ref_for_read(), tx_config).unwrap();
388
389 let mut rx_buf = [0; 255];
390 let len = handle_join_request::<0>(Some(uplink), tx_config.rf, &mut rx_buf);
391 buf.clear();
392 buf.extend_from_slice(&rx_buf[..len]).unwrap();
393 let response = mac.handle_rx::<DefaultFactory, 255, 3>(&mut buf, &mut downlinks);
394 if let Response::JoinSuccess = response {
395 } else {
396 panic!("Did not receive JoinSuccess")
397 }
398 for _ in 0..8 {
399 let (tx_config, _len) = mac
400 .send::<DefaultFactory, _, 255>(
401 &mut rand::rngs::OsRng,
402 &mut buf,
403 &SendData { fport: 1, data: &[0x0; 1], confirmed: false },
404 )
405 .unwrap();
406 assert!(
408 tx_config.rf.frequency >= 903_900_000,
409 "Unexpected frequency: {} is below 903.9 MHz!",
410 tx_config.rf.frequency
411 );
412 assert!(
413 tx_config.rf.frequency <= 905_300_000,
414 "Unexpected frequency: {} is above 905.3 MHz!",
415 tx_config.rf.frequency
416 );
417 mac.rx2_complete();
418 }
419 }
420}