Skip to main content

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}