Skip to main content

pamoja_radios/
lib.rs

1#![cfg_attr(not(any(test, feature = "std", feature = "sim")), no_std)]
2
3//! LoRa radio drivers for the pamoja SDK.
4//!
5//! `pamoja-lora` works out what a link costs and how far it reaches; this crate puts a
6//! radio on the air to match. Each chip family has a module in two halves. The first is
7//! the chip's commands or registers and its decoders: the bytes that configure it, the
8//! interrupt and status bits it answers with, the signal levels of a received packet,
9//! and the frequency, modulation, packet, and power settings that a
10//! [`LinkSettings`](pamoja_lora::LinkSettings) and a channel plan turn into. That half
11//! needs no bus and no allocation, so a test, a language binding, or a protocol
12//! analyzer can use it without a radio. The second half is an `embedded-hal` driver
13//! that talks to the chip over SPI and transmits and receives frames.
14//!
15//! - [`sx126x`] - Semtech's SX1261, SX1262, and LLCC68, from the SX1261/2 datasheet.
16//! - [`sx127x`] - Semtech's SX1276, SX1277, SX1278, and SX1279, and modules such as the
17//!   RFM95W, from the SX1276/77/78/79 datasheet.
18//! - [`radio`] - one radio of either family behind the same calls: configure from a
19//!   carrier, a link, and an output power, then transmit, receive, and listen.
20//! - [`duty`] - a guard that holds a radio silent for the off time a regional duty-cycle
21//!   limit requires after each transmission.
22//! - `lorawan`, with the `lorawan` feature - a LoRaWAN Class A node: an end device from
23//!   `pamoja-lorawan` driving a radio, with its receive windows opened on time.
24//! - `relay`, with the `lorawan` feature - a LoRaWAN relay driving a radio: it scans for
25//!   wake-on-radio frames, answers them, and forwards the uplinks behind them.
26//! - `linux`, with the `linux` feature - opening a radio on a Linux board over spidev and
27//!   the GPIO character device, with a plain error on every other platform.
28//! - `mesh`, with the `std` feature - a pamoja transport over a radio, carrying topics in
29//!   pamoja-mesh frames that each node relays onward, under the duty-cycle guard.
30//! - `sim`, with the `sim` feature - simulated SX126x and SX127x chips that answer the
31//!   drivers with no radio attached, and report what they were tuned to and what they sent.
32//!
33//! # Examples
34//!
35//! A node on EU868 works out how it may transmit before it touches a radio: the link its
36//! data rate names, how hard its amplifier may drive through its antenna under the plan's
37//! ceiling, and how long it must stay silent after each reading.
38//!
39//! ```
40//! use pamoja_lora::budget::{Decibels, LinkBudget};
41//! use pamoja_lora::region::Region;
42//! use pamoja_radios::duty::DutyCycle;
43//! use pamoja_radios::radio::{RadioConfig, SyncWord};
44//! use pamoja_radios::sx126x::config::{PowerAmplifier, TxPower};
45//!
46//! let eu868 = Region::Eu868.plan();
47//! let carrier = 868_100_000;
48//! let link = eu868.link_settings(3).expect("DR3 is a LoRa data rate");
49//!
50//! // A 2.15 dBi whip on half a decibel of pigtail: the amplifier drives only as hard as
51//! // keeps the radiated power under the plan's ceiling at that carrier.
52//! let whip = LinkBudget {
53//!     transmit_antenna_gain_dbi: Decibels::from_hundredths(215),
54//!     transmit_cable_loss_db: Decibels::from_tenths(5),
55//!     ..LinkBudget::default()
56//! };
57//! let ceiling = Decibels::from_db(eu868.max_eirp_dbm(carrier).into());
58//! let power = TxPower::under_ceiling(PowerAmplifier::HighPower, &whip, ceiling);
59//! assert_eq!(power.setting_dbm, 14);
60//!
61//! // The configuration an SX126x or an SX127x takes, as a LoRaWAN device uses it.
62//! let config = RadioConfig::new(carrier, link, power.setting_dbm).lorawan_device();
63//! assert_eq!(config.link.spreading_factor(), 9);
64//! assert_eq!(config.sync_word, SyncWord::Public);
65//!
66//! // Under the band's one percent duty cycle, a ten-byte reading owes ninety-nine times
67//! // its airtime in silence before the next one may start.
68//! let mut guard = DutyCycle::new(10);
69//! let airtime = guard.transmitted(0, &link, 10);
70//! assert_eq!(guard.wait_us(airtime), 99 * airtime);
71//! ```
72
73pub mod duty;
74#[cfg(feature = "linux")]
75pub mod linux;
76#[cfg(feature = "lorawan")]
77pub mod lorawan;
78#[cfg(feature = "std")]
79pub mod mesh;
80#[cfg(feature = "embedded-hal")]
81pub mod radio;
82#[cfg(feature = "lorawan")]
83pub mod relay;
84#[cfg(feature = "sim")]
85pub mod sim;
86pub mod sx126x;
87pub mod sx127x;
88pub mod sx1302;