Skip to main content

Relay

Struct Relay 

Source
pub struct Relay<'p> { /* private fields */ }
Expand description

A LoRaWAN relay: an end device that scans for wake-on-radio frames, acknowledges and forwards the uplinks behind them, and sends the network’s answers on in each end device’s RXR window.

Like EndDevice, it owns no radio and no clock. One forwarded uplink runs like this:

  1. next_scan says when and where to detect a preamble.
  2. A WOR frame received there goes to heard_wor, which says whether to acknowledge it and when to listen for the uplink.
  3. That uplink goes to heard_uplink, and forward sends it to the network in the relay’s own uplink on port LA_FPORT_RELAY.
  4. What the relay’s receive windows hear goes to heard_in, which reads the relay commands among its MAC commands and turns a downlink for the end device into an RxrDownlink.

§Examples

A relay forwards a sensor’s reading and sends the network’s answer back:

use pamoja_lora::region::Region;
use pamoja_lorawan::device::{EndDevice, ReceiveWindow, Settings};
use pamoja_lorawan::mac::MacCommand;
use pamoja_lorawan::relay::{
    root_wor_s_key, wor_uplink, CadToRx, Carrier, ForwardedUplink, Relay, RelayConfig,
    RelayHeard, RelaySettings, Wake, WorKeys, XtalAccuracy, LA_FPORT_RELAY,
};
use pamoja_lorawan::{Downlink, Session, Uplink};

let plan = Region::Eu868.plan();
let relay_session = Session::new(0x2601_0001, [0x11; 16], [0x22; 16]);
let device = EndDevice::personalized(plan, relay_session, Settings::new(2, 14))?;
let settings = RelaySettings::new(XtalAccuracy::Ppm10, CadToRx::Symbols2);
let mut relay = Relay::new(device, settings);
relay.start(RelayConfig::region_default(plan).expect("EU868 has relay channels"))?;

// The network trusts a sensor to it, as an UpdateUplinkListReq would.
let sensor = Session::new(0x2601_1BDA, [0x2B; 16], [0x99; 16]);
let root = root_wor_s_key(&[0x2B; 16]);
relay.trust(0, sensor.dev_addr(), &root, 0, 63, 0);

// The first scan starts now, on 865.1 MHz.
let scan = relay.next_scan(0).expect("running");
assert_eq!(scan.carrier, Carrier::new(865_100_000, 3));

// The sensor's WOR frame says its uplink follows on 868.1 MHz at DR5.
let uplink = Carrier::new(868_100_000, 5);
let wor = wor_uplink(&WorKeys::derive(&root, sensor.dev_addr()), sensor.dev_addr(), 0, uplink, scan.carrier)?;
let Wake::Uplink { acknowledgment, listen, .. } = relay.heard_wor(&scan, &wor, -90, 4, 900_000)?
else {
    panic!("a trusted device's uplink follows");
};
assert_eq!(acknowledgment.expect("room to acknowledge").carrier, Carrier::new(865_300_000, 3));
let listen = listen.expect("forwarding is available");

// The uplink arrives, and goes out in the relay's own uplink on port 226.
let frame = sensor.encode_uplink(&Uplink::new(0, 1, b"21.5"))?;
let due = relay.heard_uplink(frame.as_bytes(), -88, 6, listen.start_us + 60_000)?;
let transmission = relay.forward(due)?;

// The network reads it back, and answers the sensor through the relay.
let sent = relay_session.decode(transmission.frame.as_bytes(), 0)?;
assert_eq!(sent.fport(), Some(LA_FPORT_RELAY));
assert_eq!(ForwardedUplink::parse(sent.payload())?.phy_payload, frame.as_bytes());
let answer = sensor.encode_downlink(&Downlink::new(0, 1, b"ok"))?;
let reply = relay_session.encode_downlink(&Downlink::new(0, LA_FPORT_RELAY, answer.as_bytes()))?;

let RelayHeard::Downlink { downlink, .. } = relay.heard_in(ReceiveWindow::Rx1, reply.as_bytes(), 7)?
else {
    panic!("a downlink for the sensor");
};
assert_eq!(downlink.frame(), answer.as_bytes());
assert_eq!(downlink.carrier.frequency_hz, 865_100_000, "on the WOR frequency");
assert_eq!(downlink.start_us, listen.start_us + 60_000 + 18_000_000);

Implementations§

Source§

impl<'p> Relay<'p>

Source

pub const fn new(device: EndDevice<'p>, settings: RelaySettings) -> Relay<'p>

Makes a relay of an end device.

It does not scan until start or its network’s RelayConfReq gives it channels, and it forwards every join request and trusts no one until its network says otherwise.

§Arguments
  • device - the relay’s own device, joined or to join.
  • settings - what its radio can do.
§Returns

The relay.

Source

pub const fn device(&self) -> &EndDevice<'p>

Returns the relay’s own device.

§Returns

The device.

Source

pub fn device_mut(&mut self) -> &mut EndDevice<'p>

Returns the relay’s own device, to join, send its own uplinks, and hear what comes back for them.

Read downlinks through heard_in instead, so the relay commands among their MAC commands reach the relay.

§Returns

The device.

Source

pub fn into_device(self) -> EndDevice<'p>

Gives the relay’s device back.

§Returns

The device.

Source

pub const fn settings(&self) -> RelaySettings

Returns what the relay’s radio can do.

§Returns

The settings.

Source

pub const fn config(&self) -> Option<RelayConfig>

Returns the channels the relay scans.

§Returns

The configuration, or None while the relay is stopped.

Source

pub fn region_channel(&self, index: u8) -> Option<RelayChannel>

Returns one of the region’s wake-on-radio channels, TS011-1.0.1 section 3.2.2.

§Arguments
  • index - which, 0 or 1.
§Returns

The channel, or None where the region defines none.

Source

pub fn start(&mut self, config: RelayConfig) -> Result<(), RelayError>

Starts scanning, or changes what a running relay scans from its next scan on.

§Arguments
  • config - the channels and how often.
§Errors

Returns RelayError::Configuration for a channel that is not LoRa or not in the radio’s range, or scans so frequent that detecting a preamble and starting to receive on one channel does not fit before the next.

Source

pub fn stop(&mut self)

Stops scanning. A forwarded uplink already waiting still goes out.

Source

pub const fn join_filter(&self) -> &JoinFilter

Returns the join request filter.

§Returns

The filter.

Source

pub fn join_filter_mut(&mut self) -> &mut JoinFilter

Returns the join request filter to change.

§Returns

The filter.

Source

pub const fn trusted(&self) -> &TrustedDevices

Returns the end devices the relay trusts.

§Returns

The list.

Source

pub fn trusted_mut(&mut self) -> &mut TrustedDevices

Returns the end devices the relay trusts, to change.

§Returns

The list.

Source

pub fn trust( &mut self, index: u8, dev_addr: u32, root_wor_s_key: &[u8; 16], next_wfcnt: u32, reload_rate: u8, bucket_size: u8, ) -> bool

Trusts an end device, as an UpdateUplinkListReq with the same fields does.

§Arguments
  • index - the entry, 0 to 15.
  • dev_addr - the device’s address.
  • root_wor_s_key - its root relay session key.
  • next_wfcnt - the WOR frame counter to expect from it next.
  • reload_rate - its uplinks forwarded an hour, 63 for no limit.
  • bucket_size - the coded bucket size multiplier, TS011-1.0.1 table 55.
§Returns

false for an index past 15.

Source

pub const fn limits(&self) -> &ForwardLimits

Returns the relay-wide forwarding limits.

§Returns

The limits.

Source

pub fn limits_mut(&mut self) -> &mut ForwardLimits

Returns the relay-wide forwarding limits, to change.

§Returns

The limits.

Source

pub fn next_scan(&mut self, now_us: u64) -> Option<Scan>

Says when and where to scan next.

The first call after the relay starts begins its scan at now_us, with every forwarding limit at one hour’s reload, as TS011-1.0.1 section 8.8 has. Scans fall on whole periods from then, and a second channel’s half a period between, so a relay that was busy picks the schedule up at the next of them, section 3.2.1.

§Arguments
  • now_us - the time, in microseconds.
§Returns

The scan, or None while the relay is stopped.

Source

pub fn heard_wor( &mut self, scan: &Scan, frame: &[u8], rssi_dbm: i16, snr_db: i8, ended_us: u64, ) -> Result<Wake, RelayError>

Reads a wake-on-radio frame a scan heard, TS011-1.0.1 sections 3.2.4 and 3.3.

A join request’s frame has the relay listen for the request, if the join request limit allows. A trusted device’s frame is verified against the counter the relay expects from it, and answered with a WOR ACK saying whether the relay forwards the uplink, which it listens for only if it does. A frame from a device the relay does not trust queues a notification of it for the network, once while one is waiting, if the notification limit allows.

§Arguments
  • scan - the scan that heard it.
  • frame - the frame.
  • rssi_dbm - its signal strength.
  • snr_db - its signal-to-noise ratio.
  • ended_us - when it finished arriving, in microseconds.
§Returns

What to do next.

§Errors

Returns RelayError::Stopped while stopped, RelayError::Busy while a forwarded uplink waits to go out, RelayError::Frame for a frame that does not decode or, from a trusted device’s address, does not verify, RelayError::Carrier for an uplink on a carrier the relay cannot hear, and RelayError::Limited when a limit keeps it from listening or notifying.

Reads the uplink a wake-on-radio frame announced, and holds it to forward, TS011-1.0.1 sections 3.5, 8.1 and 8.6.

A join request must be one, and pass the join request filter. A data uplink must come from the device whose WOR frame announced it. Either spends a token from every limit it counts against.

§Arguments
  • frame - the uplink.
  • rssi_dbm - its signal strength, which the forwarded metadata carries.
  • snr_db - its signal-to-noise ratio, likewise.
  • ended_us - when it finished arriving, in microseconds.
§Returns

When to forward it: RELAY_FWD_DELAY_US after it ended.

§Errors

Returns RelayError::NotListening with no uplink announced, RelayError::Frame for a frame longer than the relay forwards or not of the kind announced, RelayError::Foreign for another device’s uplink, RelayError::Filtered for a join request the filter drops, and RelayError::Limited when a limit is reached.

Clears the uplink a wake-on-radio frame announced, once listening for it heard nothing.

Source

pub fn forward_due(&self) -> Option<u64>

Returns when the forwarded uplink waiting to go out is due.

§Returns

The time, or None with nothing waiting.

Source

pub fn forward(&mut self, now_us: u64) -> Result<Transmission, RelayError>

Sends the uplink heard_uplink holds, in the relay’s own uplink on port LA_FPORT_RELAY, TS011-1.0.1 section 9.1.

It stays held when the relay’s device cannot send yet, and when the answers the device owes crowd it out of this frame, which Transmission::carries_payload reports; call again once the device is free.

§Arguments
  • now_us - the time, in microseconds.
§Returns

What the relay’s device transmits, with its receive windows.

§Errors

Returns RelayError::NothingHeld with nothing held, and RelayError::Device with DeviceError::Wait before it is due or whatever else keeps the device from sending.

Source

pub fn heard_in( &mut self, window: ReceiveWindow, frame: &[u8], snr_db: i8, ) -> Result<RelayHeard, RelayError>

Reads a frame heard in a given receive window of the relay’s last transmission.

Relay commands among its MAC commands reconfigure the relay, answered in order with the rest. A downlink on port LA_FPORT_RELAY answering a forwarded uplink becomes an RxrDownlink for the end device, TS011-1.0.1 section 9.2.

§Arguments
  • window - the window the radio heard the frame in.
  • frame - the bytes the radio received.
  • snr_db - the frame’s signal-to-noise ratio.
§Returns

What the frame was.

§Errors

Returns RelayError::Device for whatever the relay’s device refuses, as EndDevice::heard_in does.

Source

pub fn heard( &mut self, frame: &[u8], snr_db: i8, ) -> Result<RelayHeard, RelayError>

Reads a frame heard in one of the receive windows of the relay’s last transmission, without saying which, as heard_in otherwise does.

§Arguments
  • frame - the bytes the radio received.
  • snr_db - the frame’s signal-to-noise ratio.
§Returns

What the frame was.

§Errors

As heard_in.

Source

pub fn nothing_heard(&mut self, now_us: u64) -> Result<Next, RelayError>

Says what comes next once the relay’s receive windows closed with nothing for it, as EndDevice::nothing_heard does. A forwarded uplink not repeated no longer waits on an answer.

§Arguments
  • now_us - the time the second window closed, in microseconds.
§Returns

Whether to repeat the frame, join again, or move on.

§Errors

Returns RelayError::Device with DeviceError::NothingPending with no transmission waiting on its windows.

Auto Trait Implementations§

§

impl<'p> Freeze for Relay<'p>

§

impl<'p> RefUnwindSafe for Relay<'p>

§

impl<'p> Send for Relay<'p>

§

impl<'p> Sync for Relay<'p>

§

impl<'p> Unpin for Relay<'p>

§

impl<'p> UnsafeUnpin for Relay<'p>

§

impl<'p> UnwindSafe for Relay<'p>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.