Skip to main content

pamoja_lorawan/relay/
mod.rs

1//! A LoRaWAN relay, TS011-1.0.1: what an end device and a relay say to each other.
2//!
3//! A relay is an end device that listens on behalf of others. It sleeps, waking every
4//! [`CadPeriodicity`] to scan a channel for radio activity. An end device out of a gateway's
5//! reach first sends a wake-on-radio (WOR) frame with a preamble long enough to span that
6//! sleep, which says where and how fast its LoRaWAN uplink will follow. The relay may answer
7//! with a WOR ACK describing its own timing, so the next WOR frame can be short, then
8//! listens for the uplink and forwards it to the network on port [`LA_FPORT_RELAY`] over its
9//! own session. A downlink for the end device comes back the same way and goes out in the
10//! device's RXR window.
11//!
12//! This module holds the frames and the arithmetic, sans IO like [`device`](crate::device):
13//!
14//! - [`WorKeys`]: the integrity and encryption keys an end device and a relay share, from
15//!   the root key [`root_wor_s_key`] derives out of the network session key, section 4.
16//! - [`Wor`]: the two WOR frames, [`wor_join_request`] and [`wor_uplink`], section 5.3.
17//! - [`wor_ack`] and [`open_wor_ack`] with [`StateSync`]: the acknowledgment, section 6.2.
18//! - [`ForwardedUplink`]: an uplink a relay forwards and its metadata, section 9.1.
19//! - [`Synchronization`], [`unsynchronized_preamble_symbols`] and [`t_offset_ms`]: how long a
20//!   WOR preamble must be and when it goes out, section 5.2 and appendix 1.
21//! - [`Relay`]: the relay itself, an end device that scans for those frames, answers them,
22//!   forwards the uplinks behind them and sends the network's answers on, chapters 3, 7 and
23//!   8. It keeps the tables its network configures, a [`JoinFilter`], its
24//!   [`TrustedDevices`] and its [`ForwardLimits`], each usable on its own.
25//!
26//! The relay commands a network configures both sides with are
27//! [`MacCommand`](crate::mac::MacCommand) variants, and each region's default WOR channels
28//! are [`ChannelPlan::relay_channels`](pamoja_lora::region::ChannelPlan::relay_channels).
29//!
30//! # Examples
31//!
32//! An end device wakes a relay, which checks the frame and acknowledges it:
33//!
34//! ```
35//! use pamoja_lorawan::relay::{
36//!     open_wor_ack, wor_ack, wor_uplink, CadPeriodicity, CadToRx, Carrier, Forward, StateSync,
37//!     Wor, XtalAccuracy,
38//! };
39//! use pamoja_lorawan::Session;
40//!
41//! let session = Session::new(0x2601_1BDA, [0x2B; 16], [0x99; 16]);
42//! let keys = session.wor_keys();
43//! let wor = Carrier::new(865_100_000, 3);
44//! let ack = Carrier::new(865_300_000, 3);
45//! let uplink = Carrier::new(868_100_000, 5);
46//!
47//! // The end device says its next uplink goes out on 868.1 MHz at DR5.
48//! let frame = wor_uplink(&keys, session.dev_addr(), 1, uplink, wor)?;
49//!
50//! // The relay, holding the same keys, reads it back.
51//! let Wor::Uplink(sealed) = Wor::parse(&frame)? else { unreachable!() };
52//! assert_eq!(sealed.open(&keys, 1, wor)?, uplink);
53//!
54//! // And tells the device how it scans.
55//! let state = StateSync {
56//!     cad_to_rx: CadToRx::Symbols4,
57//!     forward: Forward::Available,
58//!     relay_data_rate: 5,
59//!     xtal_accuracy: XtalAccuracy::Ppm30,
60//!     cad_periodicity: CadPeriodicity::Ms500,
61//!     t_offset_ms: 892,
62//! };
63//! let answer = wor_ack(&keys, session.dev_addr(), 1, ack, uplink, state)?;
64//! assert_eq!(open_wor_ack(&answer, &keys, session.dev_addr(), 1, ack, uplink)?, state);
65//! # Ok::<(), pamoja_lorawan::LorawanError>(())
66//! ```
67
68mod ack;
69mod filter;
70mod forward;
71mod keys;
72mod limits;
73mod mode;
74mod node;
75mod state;
76mod timing;
77mod trusted;
78mod wor;
79
80#[cfg(test)]
81mod node_tests;
82#[cfg(test)]
83mod tests;
84
85pub use ack::{open_wor_ack, wor_ack, CadPeriodicity, CadToRx, Forward, StateSync, XtalAccuracy};
86pub use filter::{FilterAction, FilterRule, JoinFilter, FILTER_RULES};
87pub use forward::{ForwardedUplink, UplinkMetadata, WorChannel, FORWARD_OVERHEAD};
88pub use keys::{root_wor_s_key, WorKeys};
89pub use limits::{
90    CounterReset, ForwardLimits, TokenBucket, RELOAD_PERIOD_US, UNLIMITED_DEVICE_RATE,
91    UNLIMITED_RELAY_RATE,
92};
93pub use mode::{smart_enable_uplinks, RelayActivation, RelaySync};
94pub use node::{Acknowledgment, Listen, Relay, RelayError, RelayHeard, RxrDownlink, Scan, Wake};
95pub(crate) use state::RelayState;
96pub use state::{RelayConfig, RelaySettings};
97pub use timing::{
98    t_offset_ms, unsynchronized_preamble_symbols, Synchronization, WorSlot,
99    MIN_WOR_PREAMBLE_SYMBOLS,
100};
101pub use trusted::{TrustedDevice, TrustedDevices};
102pub use wor::{wor_join_request, wor_uplink, Carrier, SealedWor, Wor};
103
104/// The port every message between a relay and its network uses, TS011-1.0.1 section 9.
105pub const LA_FPORT_RELAY: u8 = 226;
106
107/// How many end devices a relay verifies wake-on-radio frames for, RP002-1.0.5 section
108/// 5.4.5.
109pub const TRUSTED_ED_NUMBER: usize = 16;
110
111/// How many WOR frames an end device sends without an acknowledgment before its uplink goes
112/// out anyway, by default, RP002-1.0.5 section 5.4.5.
113pub const WOR_ATTEMPTS_WO_ACK: u8 = 8;
114
115/// The gap between a WOR frame, or its acknowledgment, and the LoRaWAN frame after it, in
116/// microseconds.
117pub const WOR_DATA_DELAY_US: u32 = 50_000;
118
119/// The gap between a WOR frame and its acknowledgment, in microseconds.
120pub const WOR_ACK_DELAY_US: u32 = 50_000;
121
122/// The gap between a relay hearing an uplink and forwarding it, in microseconds.
123pub const RELAY_FWD_DELAY_US: u32 = 50_000;
124
125/// How long after an uplink an end device's RXR window opens at the latest, in
126/// microseconds.
127pub const RXR_DELAY_US: u32 = 18_000_000;
128
129/// The length of a WOR frame ahead of a join request: its header and five bytes.
130pub const WOR_JOIN_REQUEST_LEN: usize = 5;
131
132/// The length of a WOR frame ahead of a Class A uplink.
133pub const WOR_UPLINK_LEN: usize = 15;
134
135/// The length of a WOR ACK.
136pub const WOR_ACK_LEN: usize = 7;