pamoja_lorawan/adr.rs
1//! Keeping a device on the air when the network stops answering.
2//!
3//! A network that runs adaptive data rate moves a device to the fastest rate and lowest power
4//! that still reach it, which costs the device less battery and the network less air time.
5//! The risk is the other direction: a device moved up and then left there, in a spot the
6//! network can no longer hear, would go on transmitting into nothing forever.
7//!
8//! So a device counts. Every new uplink raises a counter, any Class A downlink at all resets
9//! it, and once the counter passes a limit the device starts asking the network to say
10//! something. Past that it gives back what the network had tuned away, a step at a time,
11//! until either the network answers or the device is back where it started.
12//!
13//! The two revisions of the specification step differently, and [`Backoff`] follows the one
14//! it is given.
15//!
16//! - **LoRaWAN 1.0.3**, section 4.3.1.1: after the limit plus a delay the device lowers its
17//! data rate, and again every delay after that. The request bit is not set at the slowest
18//! rate, since nothing could be done about the answer.
19//! - **TS001-1.0.4**, section 4.3.1.1 and its table 9: the first step restores the default
20//! transmit power, each step after that lowers the data rate, and the step after reaching
21//! the default data rate re-enables the default channels and sets the repetition count
22//! back to one. The request bit is set throughout, and stops once that last step is done.
23//!
24//! This is the device half, and it is the half the specification settles. Which rate to move
25//! a device to in the first place is a policy the specification deliberately leaves open,
26//! and it is not here.
27//!
28//! The limit and the delay belong to the regional parameters rather than the link layer,
29//! which names them without values. RP002-1.0.5 section 3.3 recommends 64 and 32 for every
30//! region, and [`Backoff::recommended`] starts with those; [`Backoff::new`] takes others, for
31//! a device commissioned with different ones.
32
33use crate::defaults::{ADR_ACK_DELAY, ADR_ACK_LIMIT};
34use crate::Version;
35
36/// Where a device stands against its defaults when it sends an uplink.
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
38pub struct Standing {
39 /// Whether it is already at its default data rate, the slowest it uses, so there is no
40 /// lower rate to step to.
41 pub default_data_rate: bool,
42}
43
44/// What the back-off says to do with one uplink.
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub struct Step {
47 /// Whether to set the ADR acknowledgment request bit, asking the network to answer.
48 pub request_ack: bool,
49 /// Whether to go back to the default transmit power before sending this uplink.
50 ///
51 /// Only TS001-1.0.4 takes this step, the first after the limit plus a delay.
52 pub restore_power: bool,
53 /// Whether to step the data rate down, by the region's back-off table, before sending.
54 pub lower_data_rate: bool,
55 /// Whether to re-enable the default channels and set the repetition count back to one
56 /// before sending.
57 ///
58 /// Only TS001-1.0.4 takes this step, once the default data rate has been reached. A
59 /// dynamic channel plan re-enables its default channels, and a fixed plan all of them.
60 pub restore_channels: bool,
61}
62
63/// A device's count of how long the network has been silent.
64///
65/// # Examples
66///
67/// A 1.0.4 device that stops hearing its network, starting two rates above its default:
68///
69/// ```
70/// use pamoja_lorawan::adr::{Backoff, Standing};
71/// use pamoja_lorawan::Version;
72///
73/// let mut backoff = Backoff::recommended(Version::V1_0_4);
74/// let mut data_rate = 2;
75/// let mut sent = 0;
76/// let mut steps = Vec::new();
77///
78/// while sent < 200 {
79/// let step = backoff.uplink(Standing { default_data_rate: data_rate == 0 });
80/// sent += 1;
81/// if step.restore_power {
82/// steps.push((sent, "power"));
83/// }
84/// if step.lower_data_rate {
85/// data_rate -= 1;
86/// steps.push((sent, "rate"));
87/// }
88/// if step.restore_channels {
89/// steps.push((sent, "channels"));
90/// }
91/// }
92///
93/// // Power a delay after the limit, then a rate every delay, then the channels.
94/// assert_eq!(steps, [(96, "power"), (128, "rate"), (160, "rate"), (192, "channels")]);
95/// assert!(!backoff.uplink(Standing { default_data_rate: true }).request_ack);
96/// ```
97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub struct Backoff {
99 version: Version,
100 counter: u32,
101 limit: u32,
102 delay: u32,
103 restored: bool,
104}
105
106impl Backoff {
107 /// A count from zero, with the limit and delay RP002-1.0.5 section 3.3 recommends for
108 /// every region.
109 ///
110 /// # Arguments
111 ///
112 /// * `version` - the revision whose steps to follow.
113 ///
114 /// # Returns
115 ///
116 /// The count, asking after [`ADR_ACK_LIMIT`] unanswered uplinks and stepping every
117 /// [`ADR_ACK_DELAY`] after that.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// use pamoja_lorawan::adr::Backoff;
123 /// use pamoja_lorawan::Version;
124 ///
125 /// assert_eq!(Backoff::recommended(Version::V1_0_4), Backoff::new(Version::V1_0_4, 64, 32));
126 /// ```
127 #[must_use]
128 pub const fn recommended(version: Version) -> Backoff {
129 Backoff::new(version, ADR_ACK_LIMIT, ADR_ACK_DELAY)
130 }
131
132 /// A count from zero, with a limit and delay a device was commissioned with.
133 ///
134 /// # Arguments
135 ///
136 /// * `version` - the revision whose steps to follow.
137 /// * `limit` - how many unanswered uplinks before it starts asking.
138 /// * `delay` - how many more before its first step, and between each step after that.
139 /// Treated as one if given as zero, since a step every zero uplinks is not a thing a
140 /// device can do.
141 ///
142 /// # Returns
143 ///
144 /// The count.
145 #[must_use]
146 pub const fn new(version: Version, limit: u32, delay: u32) -> Backoff {
147 Backoff {
148 version,
149 counter: 0,
150 limit,
151 delay: if delay == 0 { 1 } else { delay },
152 restored: false,
153 }
154 }
155
156 /// Counts one new uplink, and says what to do before sending it.
157 ///
158 /// Call this once per uplink the frame counter moves for. A repeated transmission of the
159 /// same uplink is not a new one and does not count, which is the same rule the frame
160 /// counter itself follows.
161 ///
162 /// # Arguments
163 ///
164 /// * `standing` - where the device stands against its defaults right now.
165 ///
166 /// # Returns
167 ///
168 /// Whether to ask the network for an answer, and which step, if any, this uplink takes.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use pamoja_lorawan::adr::{Backoff, Standing};
174 /// use pamoja_lorawan::Version;
175 ///
176 /// let mut backoff = Backoff::recommended(Version::V1_0_3);
177 /// let above = Standing { default_data_rate: false };
178 /// for _ in 0..63 {
179 /// assert!(!backoff.uplink(above).request_ack);
180 /// }
181 ///
182 /// // The sixty-fourth unanswered uplink is the one that starts asking.
183 /// assert!(backoff.uplink(above).request_ack);
184 /// ```
185 pub fn uplink(&mut self, standing: Standing) -> Step {
186 self.counter = self.counter.saturating_add(1);
187 if self.counter < self.limit {
188 return Step::default();
189 }
190
191 let since = self.counter - self.limit;
192 let stepping = since >= self.delay && since.is_multiple_of(self.delay);
193 match self.version {
194 Version::V1_0_3 => Step {
195 request_ack: !standing.default_data_rate,
196 lower_data_rate: stepping && !standing.default_data_rate,
197 ..Step::default()
198 },
199 Version::V1_0_4 => {
200 if self.restored {
201 return Step::default();
202 }
203 let mut step = Step {
204 request_ack: true,
205 ..Step::default()
206 };
207 if stepping {
208 if since == self.delay {
209 step.restore_power = true;
210 } else if !standing.default_data_rate {
211 step.lower_data_rate = true;
212 } else {
213 step.restore_channels = true;
214 self.restored = true;
215 }
216 }
217 step
218 }
219 }
220 }
221
222 /// Counts a Class A downlink, which proves the network still hears the device.
223 ///
224 /// Any such downlink resets the count, whatever it carried; it does not need to
225 /// acknowledge anything.
226 pub fn downlink(&mut self) {
227 self.counter = 0;
228 self.restored = false;
229 }
230
231 /// How many uplinks have gone unanswered.
232 ///
233 /// # Returns
234 ///
235 /// The counter.
236 #[must_use]
237 pub const fn counter(&self) -> u32 {
238 self.counter
239 }
240
241 /// Whether the last step re-enabled the channels, for a device saving its state.
242 pub(crate) const fn has_restored(&self) -> bool {
243 self.restored
244 }
245
246 /// The same count carried on from a saved counter.
247 pub(crate) const fn resumed(mut self, counter: u32, restored: bool) -> Backoff {
248 self.counter = counter;
249 self.restored = restored;
250 self
251 }
252
253 /// The revision whose steps this count follows.
254 ///
255 /// # Returns
256 ///
257 /// The revision.
258 #[must_use]
259 pub const fn version(&self) -> Version {
260 self.version
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 const ABOVE: Standing = Standing {
269 default_data_rate: false,
270 };
271 const AT_DEFAULT: Standing = Standing {
272 default_data_rate: true,
273 };
274
275 // Sends `count` uplinks from the same standing and hands back the last step.
276 fn after(backoff: &mut Backoff, count: u32, standing: Standing) -> Step {
277 let mut step = backoff.uplink(standing);
278 for _ in 1..count {
279 step = backoff.uplink(standing);
280 }
281 step
282 }
283
284 #[test]
285 fn nothing_is_asked_for_until_the_limit_is_reached() {
286 for version in [Version::V1_0_3, Version::V1_0_4] {
287 let mut backoff = Backoff::recommended(version);
288 assert!(!after(&mut backoff, ADR_ACK_LIMIT - 1, ABOVE).request_ack);
289 assert!(
290 backoff.uplink(ABOVE).request_ack,
291 "{version:?}: the uplink that reaches the limit is the one that asks"
292 );
293 }
294 }
295
296 #[test]
297 fn any_downlink_resets_the_count() {
298 for version in [Version::V1_0_3, Version::V1_0_4] {
299 let mut backoff = Backoff::recommended(version);
300 assert!(after(&mut backoff, ADR_ACK_LIMIT, ABOVE).request_ack);
301
302 backoff.downlink();
303 assert_eq!(backoff.counter(), 0);
304 assert!(!backoff.uplink(ABOVE).request_ack);
305 }
306 }
307
308 #[test]
309 fn version_1_0_4_follows_table_9() {
310 // TS001-1.0.4 table 9, ADR_ACK_LIMIT 64 and ADR_ACK_DELAY 32, starting at DR1 with
311 // reduced power: counts 0 to 63 normal; 64 to 95 asking; 96 to 127 default power;
312 // 128 DR0, the default; 160 channels and NbTrans restored.
313 let mut backoff = Backoff::recommended(Version::V1_0_4);
314
315 let step = after(&mut backoff, 63, ABOVE);
316 assert_eq!(step, Step::default(), "the first 63 are normal operation");
317
318 for count in 64..=95 {
319 let step = backoff.uplink(ABOVE);
320 assert!(step.request_ack, "uplink {count} asks");
321 assert!(!step.restore_power && !step.lower_data_rate && !step.restore_channels);
322 }
323
324 let step = backoff.uplink(ABOVE);
325 assert_eq!(
326 step,
327 Step {
328 request_ack: true,
329 restore_power: true,
330 ..Step::default()
331 },
332 "uplink 96 restores the power and leaves the rate"
333 );
334
335 let step = after(&mut backoff, 32, ABOVE);
336 assert_eq!(
337 step,
338 Step {
339 request_ack: true,
340 lower_data_rate: true,
341 ..Step::default()
342 },
343 "uplink 128 steps DR1 down to DR0"
344 );
345
346 let step = after(&mut backoff, 32, AT_DEFAULT);
347 assert_eq!(
348 step,
349 Step {
350 request_ack: true,
351 restore_channels: true,
352 ..Step::default()
353 },
354 "uplink 160, at the default rate, restores the channels and NbTrans"
355 );
356
357 assert_eq!(
358 after(&mut backoff, 100, AT_DEFAULT),
359 Step::default(),
360 "and with nothing left to give back, it stops asking"
361 );
362 }
363
364 #[test]
365 fn version_1_0_3_steps_the_rate_a_delay_after_the_limit_and_every_delay_after() {
366 // LoRaWAN 1.0.3 section 4.3.1.1.
367 let mut backoff = Backoff::recommended(Version::V1_0_3);
368
369 let step = after(&mut backoff, ADR_ACK_LIMIT + ADR_ACK_DELAY - 1, ABOVE);
370 assert!(step.request_ack);
371 assert!(!step.lower_data_rate);
372
373 let step = backoff.uplink(ABOVE);
374 assert!(step.lower_data_rate, "the first step comes at 96");
375 assert!(!step.restore_power, "and 1.0.3 has no power step");
376
377 let step = after(&mut backoff, ADR_ACK_DELAY, ABOVE);
378 assert!(step.lower_data_rate, "and another at 128");
379 assert!(!step.restore_channels, "and no channel step either");
380 }
381
382 #[test]
383 fn version_1_0_3_does_not_ask_at_the_slowest_rate() {
384 // "The ADRACKReq SHALL not be set if the device uses its lowest available data rate."
385 let mut backoff = Backoff::recommended(Version::V1_0_3);
386 let step = after(&mut backoff, ADR_ACK_LIMIT * 4, AT_DEFAULT);
387 assert!(!step.request_ack);
388 assert!(!step.lower_data_rate);
389 }
390
391 #[test]
392 fn a_device_already_at_its_default_rate_still_restores_power_then_channels() {
393 let mut backoff = Backoff::recommended(Version::V1_0_4);
394 let step = after(&mut backoff, ADR_ACK_LIMIT + ADR_ACK_DELAY, AT_DEFAULT);
395 assert!(step.restore_power);
396 let step = after(&mut backoff, ADR_ACK_DELAY, AT_DEFAULT);
397 assert!(step.restore_channels);
398 assert!(!step.lower_data_rate);
399 }
400
401 #[test]
402 fn a_downlink_after_restoring_starts_the_whole_sequence_again() {
403 let mut backoff = Backoff::new(Version::V1_0_4, 2, 1);
404 let steps: Vec<Step> = (0..4).map(|_| backoff.uplink(AT_DEFAULT)).collect();
405 assert!(steps[2].restore_power);
406 assert!(steps[3].restore_channels);
407 assert_eq!(backoff.uplink(AT_DEFAULT), Step::default());
408
409 backoff.downlink();
410 let steps: Vec<Step> = (0..4).map(|_| backoff.uplink(AT_DEFAULT)).collect();
411 assert!(steps[1].request_ack);
412 assert!(steps[3].restore_channels, "restoring can happen again");
413 }
414
415 #[test]
416 fn a_delay_of_zero_is_taken_as_one_rather_than_dividing_by_it() {
417 let mut backoff = Backoff::new(Version::V1_0_3, 2, 0);
418
419 assert!(!backoff.uplink(ABOVE).lower_data_rate);
420 assert!(backoff.uplink(ABOVE).request_ack);
421 assert!(
422 backoff.uplink(ABOVE).lower_data_rate,
423 "a step every uplink after the limit"
424 );
425 }
426}