pamoja_lorawan/frame.rs
1//! The LoRaWAN PHYPayload and the small shared pieces of its header.
2
3use crate::error::LorawanError;
4
5/// The largest PHYPayload, in bytes, this crate builds or accepts.
6///
7/// Comfortably above the largest regional LoRaWAN maximum, so a frame always fits.
8pub const MAX_FRAME: usize = 256;
9
10/// The largest application payload, in bytes, a single frame can carry (with no frame
11/// options present).
12pub const MAX_PAYLOAD: usize = MAX_FRAME - 13;
13
14// MType values, in the top three bits of the MHDR.
15pub(crate) const MTYPE_JOIN_REQUEST: u8 = 0x00;
16pub(crate) const MTYPE_JOIN_ACCEPT: u8 = 0x20;
17pub(crate) const MTYPE_UNCONFIRMED_UP: u8 = 0x40;
18pub(crate) const MTYPE_UNCONFIRMED_DOWN: u8 = 0x60;
19pub(crate) const MTYPE_CONFIRMED_UP: u8 = 0x80;
20pub(crate) const MTYPE_CONFIRMED_DOWN: u8 = 0xA0;
21// The mask selecting the MType bits of the MHDR.
22pub(crate) const MTYPE_MASK: u8 = 0xE0;
23
24/// The direction a frame travels, which the MIC and the payload encryption both fold in.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum Direction {
27 /// From an end device up to the network.
28 Uplink,
29 /// From the network down to an end device.
30 Downlink,
31}
32
33impl Direction {
34 // The direction bit used in the MIC and encryption blocks: 0 up, 1 down.
35 pub(crate) fn bit(self) -> u8 {
36 match self {
37 Direction::Uplink => 0,
38 Direction::Downlink => 1,
39 }
40 }
41}
42
43/// An encoded LoRaWAN frame, the bytes that go on the air.
44///
45/// Built by a [`Session`](crate::Session) or a join exchange, and held in a fixed buffer
46/// so encoding never allocates. [`as_bytes`](PhyPayload::as_bytes) hands the radio exactly
47/// what to transmit.
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct PhyPayload {
50 bytes: [u8; MAX_FRAME],
51 len: usize,
52}
53
54impl PhyPayload {
55 // Copies an assembled frame into a fixed buffer.
56 pub(crate) fn new(bytes: &[u8]) -> Result<Self, LorawanError> {
57 if bytes.len() > MAX_FRAME {
58 return Err(LorawanError::PayloadTooLong);
59 }
60 let mut buf = [0u8; MAX_FRAME];
61 buf[..bytes.len()].copy_from_slice(bytes);
62 Ok(PhyPayload {
63 bytes: buf,
64 len: bytes.len(),
65 })
66 }
67
68 /// Returns the frame as bytes, ready to transmit.
69 ///
70 /// # Returns
71 ///
72 /// The whole PHYPayload, MIC included.
73 pub fn as_bytes(&self) -> &[u8] {
74 &self.bytes[..self.len]
75 }
76}