Skip to main content

pamoja_radios/sx127x/
driver.rs

1//! The SX127x driven over embedded-hal in LoRa mode: reset, calibrate, configure, transmit,
2//! and receive.
3//!
4//! [`Sx127x`] owns the SPI device, the NRESET line, and a delay. It reads and writes the
5//! registers of [`register`](super::register), reads RegIrqFlags to learn when a frame has
6//! gone out or come in, and follows the transmit and receive sequences of the SX1276/77/78/79
7//! datasheet (Rev 7), with the errata applied as Semtech's LoRaMac-node applies them.
8
9use embedded_hal::delay::DelayNs;
10use embedded_hal::digital::{self, OutputPin};
11use embedded_hal::spi::{Operation, SpiDevice};
12use pamoja_lora::budget::Decibels;
13use pamoja_lora::LinkSettings;
14
15use super::config::{
16    self, automatic_if, frequency_bytes, high_bw_optimize, image_cal_start, invert_iq, invert_iq_2,
17    preamble_bytes, spurious_reception, LoraModulation, ModulationError, PaOutput, SyncWord,
18    TxPower,
19};
20use super::irq::IrqFlags;
21use super::register::{self, fsk_op_mode, lora_op_mode, read_address, write_address, Mode};
22use super::status::{rssi_dbm, ModemStatus, PacketStatus, Port};
23
24/// How long NRESET is held low, in microseconds. The datasheet's Manual Reset section asks for
25/// a hundred microseconds; this is the 1 ms Semtech's LoRaMac-node holds it for.
26pub const RESET_HOLD_US: u32 = 1_000;
27
28/// How long to wait after releasing NRESET, in microseconds. The datasheet asks for 5 ms;
29/// this is the 6 ms LoRaMac-node waits.
30pub const RESET_SETTLE_US: u32 = 6_000;
31
32/// How often the interrupt flags are read while a frame goes out or comes in, in
33/// microseconds.
34pub const IRQ_POLL_US: u32 = 1_000;
35
36/// How much longer than a frame's airtime a transmission may take before the driver gives
37/// up on it, in microseconds.
38pub const TIMEOUT_MARGIN_US: u64 = 1_000_000;
39
40/// How often RegImageCal is read while a calibration runs, in microseconds.
41pub const CALIBRATION_POLL_US: u32 = 1_000;
42
43/// How long a calibration may run before the driver gives up, in microseconds. The datasheet
44/// says it takes about 10 ms.
45pub const CALIBRATION_LIMIT_US: u32 = 100_000;
46
47/// How a module wires its SX127x: the amplifier output on its antenna and its clock.
48///
49/// The SPI interface cannot see which output a module uses, so it comes from the module's
50/// schematic.
51///
52/// # Examples
53///
54/// ```
55/// use pamoja_radios::sx127x::config::PaOutput;
56/// use pamoja_radios::sx127x::Board;
57///
58/// // An RFM95W: the antenna on PA_BOOST and a crystal.
59/// let board = Board::new(PaOutput::PaBoost);
60/// assert!(!board.tcxo);
61/// ```
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub struct Board {
64    /// The amplifier output the antenna is on.
65    pub output: PaOutput,
66    /// Whether a TCXO drives the XTA pin in place of a crystal.
67    pub tcxo: bool,
68}
69
70impl Board {
71    /// A module with a crystal.
72    ///
73    /// # Arguments
74    ///
75    /// * `output` - the amplifier output the antenna is on.
76    ///
77    /// # Returns
78    ///
79    /// The board.
80    pub const fn new(output: PaOutput) -> Board {
81        Board {
82            output,
83            tcxo: false,
84        }
85    }
86
87    /// Returns the board clocked by a TCXO on XTA.
88    ///
89    /// # Returns
90    ///
91    /// The board.
92    pub const fn with_tcxo(mut self) -> Board {
93        self.tcxo = true;
94        self
95    }
96}
97
98/// What a radio sends and listens with: the carrier, the LoRa link, the power, and the sync
99/// word and IQ polarity that keep one network apart from another.
100///
101/// # Examples
102///
103/// ```
104/// use pamoja_lora::LinkSettings;
105/// use pamoja_radios::sx127x::config::{PaOutput, SyncWord, TxPower};
106/// use pamoja_radios::sx127x::RadioConfig;
107///
108/// // A LoRaWAN device on 868.1 MHz at SF9 with 14 dBm on PA_BOOST.
109/// let config = RadioConfig::new(
110///     868_100_000,
111///     LinkSettings::new(9, 125_000),
112///     TxPower::for_output(PaOutput::PaBoost, 14),
113/// )
114/// .lorawan_device();
115/// assert_eq!(config.sync_word, SyncWord::Public);
116/// assert!(config.invert_iq_receive && !config.invert_iq_transmit);
117/// ```
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub struct RadioConfig {
120    /// The carrier frequency in hertz.
121    pub frequency_hz: u32,
122    /// The LoRa link settings frames are sent and received with.
123    pub link: LinkSettings,
124    /// The amplifier settings.
125    pub power: TxPower,
126    /// The sync word.
127    pub sync_word: SyncWord,
128    /// Whether transmitted frames have inverted IQ.
129    pub invert_iq_transmit: bool,
130    /// Whether received frames are expected with inverted IQ.
131    pub invert_iq_receive: bool,
132}
133
134impl RadioConfig {
135    /// A configuration with the private sync word and standard IQ both ways.
136    ///
137    /// # Arguments
138    ///
139    /// * `frequency_hz` - the carrier frequency in hertz.
140    /// * `link` - the LoRa link settings.
141    /// * `power` - the amplifier settings.
142    ///
143    /// # Returns
144    ///
145    /// The configuration.
146    pub const fn new(frequency_hz: u32, link: LinkSettings, power: TxPower) -> RadioConfig {
147        RadioConfig {
148            frequency_hz,
149            link,
150            power,
151            sync_word: SyncWord::Private,
152            invert_iq_transmit: false,
153            invert_iq_receive: false,
154        }
155    }
156
157    /// Returns the configuration with another sync word.
158    ///
159    /// # Arguments
160    ///
161    /// * `sync_word` - the sync word.
162    ///
163    /// # Returns
164    ///
165    /// The configuration.
166    pub const fn with_sync_word(mut self, sync_word: SyncWord) -> RadioConfig {
167        self.sync_word = sync_word;
168        self
169    }
170
171    /// Returns the configuration with the IQ polarity of each direction set.
172    ///
173    /// # Arguments
174    ///
175    /// * `transmit` - whether transmitted frames have inverted IQ.
176    /// * `receive` - whether received frames have inverted IQ.
177    ///
178    /// # Returns
179    ///
180    /// The configuration.
181    pub const fn with_inverted_iq(mut self, transmit: bool, receive: bool) -> RadioConfig {
182        self.invert_iq_transmit = transmit;
183        self.invert_iq_receive = receive;
184        self
185    }
186
187    /// Returns the configuration for a LoRaWAN end device: the public sync word, standard IQ
188    /// on uplinks, and inverted IQ on downlinks.
189    ///
190    /// # Returns
191    ///
192    /// The configuration.
193    pub const fn lorawan_device(self) -> RadioConfig {
194        self.with_sync_word(SyncWord::Public)
195            .with_inverted_iq(false, true)
196    }
197}
198
199/// How a reception ended.
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
201pub enum Reception {
202    /// A frame whose CRC checked, or that carried none.
203    Frame {
204        /// The payload length; the payload is at the start of the buffer.
205        len: usize,
206        /// The signal levels the frame arrived with.
207        status: PacketStatus,
208    },
209    /// No preamble arrived before the timeout.
210    Timeout,
211    /// A frame arrived whose payload CRC failed, and was dropped.
212    Corrupt,
213}
214
215/// What can go wrong driving an SX127x.
216#[derive(Clone, Copy, Debug, PartialEq, Eq)]
217pub enum RadioError<E> {
218    /// The SPI device failed.
219    Spi(E),
220    /// The NRESET line could not be driven.
221    Pin(digital::ErrorKind),
222    /// RegVersion held this instead of 0x12, so no SX1276, SX1277, SX1278, or SX1279, or a
223    /// miswired one, is on the bus.
224    Absent(u8),
225    /// The link settings are not ones the SX127x can use at the carrier.
226    Modulation(ModulationError),
227    /// The power settings are for the other amplifier output than the board wires.
228    Output,
229    /// A payload the chip cannot send: longer than 255 bytes, or empty, with its length.
230    PayloadLength(usize),
231    /// A received payload of this many bytes does not fit the buffer given.
232    BufferTooSmall(usize),
233    /// A transmission or a reception was asked for before [`Sx127x::configure`].
234    NotConfigured,
235    /// The image calibration was still running after [`CALIBRATION_LIMIT_US`].
236    Calibration,
237    /// The expected interrupt did not arrive in the time the driver allows.
238    NoInterrupt,
239}
240
241impl<E: core::fmt::Debug> core::fmt::Display for RadioError<E> {
242    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243        match self {
244            RadioError::Spi(error) => write!(f, "SPI error: {error:?}"),
245            RadioError::Pin(kind) => write!(f, "NRESET line error: {kind:?}"),
246            RadioError::Absent(version) => {
247                write!(f, "no SX127x answered: RegVersion read {version:#04x}")
248            }
249            RadioError::Modulation(ModulationError::Bandwidth(hz)) => {
250                write!(
251                    f,
252                    "the SX127x has no {hz} Hz LoRa bandwidth at this carrier"
253                )
254            }
255            RadioError::Modulation(ModulationError::SpreadingFactor(sf)) => {
256                write!(f, "the SX127x has no SF{sf}")
257            }
258            RadioError::Modulation(ModulationError::ExplicitHeaderAtSf6) => {
259                write!(f, "SF6 needs an implicit header")
260            }
261            RadioError::Output => {
262                write!(f, "the power settings are for the other amplifier output")
263            }
264            RadioError::PayloadLength(len) => {
265                write!(f, "a LoRa frame carries 1 to 255 bytes, not {len}")
266            }
267            RadioError::BufferTooSmall(len) => {
268                write!(f, "a {len} byte payload does not fit the buffer")
269            }
270            RadioError::NotConfigured => write!(f, "the radio has not been configured"),
271            RadioError::Calibration => write!(f, "the image calibration did not finish"),
272            RadioError::NoInterrupt => write!(f, "the radio raised no interrupt in time"),
273        }
274    }
275}
276
277impl<E: core::fmt::Debug> core::error::Error for RadioError<E> {}
278
279fn pin<E, P: digital::Error>(error: P) -> RadioError<E> {
280    RadioError::Pin(error.kind())
281}
282
283/// A Semtech SX1276, SX1277, SX1278, or SX1279 on an SPI bus, with its NRESET line.
284///
285/// [`init`](Sx127x::init) resets the chip and puts it in LoRa mode,
286/// [`configure`](Sx127x::configure) calibrates its receiver and tunes it to a
287/// [`RadioConfig`], and [`transmit`](Sx127x::transmit) and [`receive`](Sx127x::receive)
288/// send and wait for one frame each. For anything those do not cover, the register and data
289/// buffer methods reach the chip directly.
290///
291/// # Examples
292///
293/// The chip's side of initialization, scripted: an RFM95W that answers RegVersion with 0x12.
294///
295/// ```
296/// use pamoja_hal::digital::PinState;
297/// use pamoja_hal::script::{DelayLog, PinScript, SpiScript, SpiStep};
298/// use pamoja_radios::sx127x::config::PaOutput;
299/// use pamoja_radios::sx127x::{Board, Sx127x};
300///
301/// let spi = SpiScript::new([
302///     SpiStep::write([0x42]),
303///     SpiStep::read([0x12]),
304///     SpiStep::write([0x81]),
305///     SpiStep::write([0x08]),
306///     SpiStep::write([0x81]),
307///     SpiStep::write([0x88]),
308///     SpiStep::write([0x81]),
309///     SpiStep::write([0x89]),
310///     SpiStep::write([0x8C]),
311///     SpiStep::write([0x23]),
312/// ]);
313/// let board = Board::new(PaOutput::PaBoost);
314///
315/// let mut radio = Sx127x::new(spi, PinScript::new([]), DelayLog::new(), board);
316/// radio.init().expect("the scripted RFM95W answers");
317///
318/// let (spi, reset, _) = radio.release();
319/// assert!(spi.done());
320/// assert_eq!(reset.driven(), [PinState::Low, PinState::High]);
321/// ```
322pub struct Sx127x<SPI, RESET, D> {
323    spi: SPI,
324    reset: RESET,
325    delay: D,
326    board: Board,
327    config: Option<RadioConfig>,
328    tuned_hz: Option<u32>,
329    calibrated_high: bool,
330    calibrated_low: bool,
331}
332
333impl<SPI, RESET, D> Sx127x<SPI, RESET, D> {
334    /// Wraps a chip's SPI device and NRESET line. Nothing is sent until
335    /// [`init`](Sx127x::init).
336    ///
337    /// # Arguments
338    ///
339    /// * `spi` - the SPI device, with NSS as its chip select.
340    /// * `reset` - the NRESET line, as an output.
341    /// * `delay` - a delay for the reset pulse and the polling.
342    /// * `board` - how the module wires the chip.
343    ///
344    /// # Returns
345    ///
346    /// The driver.
347    pub fn new(spi: SPI, reset: RESET, delay: D, board: Board) -> Self {
348        Sx127x {
349            spi,
350            reset,
351            delay,
352            board,
353            config: None,
354            tuned_hz: None,
355            calibrated_high: false,
356            calibrated_low: false,
357        }
358    }
359
360    /// Returns how the module wires the chip.
361    ///
362    /// # Returns
363    ///
364    /// The board.
365    pub fn board(&self) -> Board {
366        self.board
367    }
368
369    /// Returns the configuration the chip was last tuned to.
370    ///
371    /// # Returns
372    ///
373    /// The configuration, or `None` before [`configure`](Sx127x::configure) or after a
374    /// reset.
375    pub fn config(&self) -> Option<&RadioConfig> {
376        self.config.as_ref()
377    }
378
379    /// Returns the power settings for an output power on this board's amplifier output.
380    ///
381    /// # Arguments
382    ///
383    /// * `output_dbm` - the output power wanted at the antenna port.
384    ///
385    /// # Returns
386    ///
387    /// The amplifier settings.
388    pub fn tx_power(&self, output_dbm: i8) -> TxPower {
389        TxPower::for_output(self.board.output, output_dbm)
390    }
391
392    /// Gives back the SPI device, the line, and the delay.
393    ///
394    /// # Returns
395    ///
396    /// The SPI device, NRESET, and the delay.
397    pub fn release(self) -> (SPI, RESET, D) {
398        (self.spi, self.reset, self.delay)
399    }
400}
401
402impl<SPI, RESET, D> Sx127x<SPI, RESET, D>
403where
404    SPI: SpiDevice,
405    RESET: OutputPin,
406    D: DelayNs,
407{
408    /// Resets the chip and puts it in LoRa mode.
409    ///
410    /// NRESET is pulsed low and RegVersion read. The chip, which comes out of reset as an
411    /// FSK radio in standby, is put to sleep, clocked from a TCXO if the board has one, and
412    /// switched to LoRa, which it only allows in sleep. It then goes to standby with the LNA
413    /// at LoRaMac-node's setting.
414    ///
415    /// # Errors
416    ///
417    /// Returns [`RadioError::Absent`] if RegVersion is not 0x12, and [`RadioError::Spi`] or
418    /// [`RadioError::Pin`] if the bus or the line fails.
419    pub fn init(&mut self) -> Result<(), RadioError<SPI::Error>> {
420        self.config = None;
421        self.tuned_hz = None;
422        self.calibrated_high = false;
423        self.calibrated_low = false;
424        self.reset.set_low().map_err(pin)?;
425        self.delay.delay_us(RESET_HOLD_US);
426        self.reset.set_high().map_err(pin)?;
427        self.delay.delay_us(RESET_SETTLE_US);
428
429        let version = self.version()?;
430        if version != register::VERSION_SX1276 {
431            return Err(RadioError::Absent(version));
432        }
433        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
434        if self.board.tcxo {
435            self.write_register(register::TCXO, config::TCXO_INPUT_ON)?;
436        }
437        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
438        self.write_register(register::OP_MODE, lora_op_mode(Mode::Standby))?;
439        self.write_register(register::LNA, config::LNA_BOOSTED)
440    }
441
442    /// Tunes the chip to a configuration.
443    ///
444    /// The first time a carrier on each RF port is configured, the receiver's image and RSSI
445    /// calibration runs there, as the datasheet's Image and RSSI Calibration section advises,
446    /// since the calibration at reset only covers the low frequency port at 434 MHz. The chip
447    /// then takes the frequency, the amplifier, the modem settings, the preamble, the SF6
448    /// detection settings, the 500 kHz erratum, the longest payload, and the sync word.
449    ///
450    /// # Arguments
451    ///
452    /// * `config` - the configuration.
453    ///
454    /// # Errors
455    ///
456    /// Returns [`RadioError::Modulation`] if the link does not fit the chip at the carrier,
457    /// [`RadioError::Output`] if the power settings are for the other output,
458    /// [`RadioError::Calibration`] if the calibration does not finish, and the bus errors of
459    /// [`write_register`](Sx127x::write_register).
460    pub fn configure(&mut self, config: RadioConfig) -> Result<(), RadioError<SPI::Error>> {
461        let modulation = LoraModulation::from_link(&config.link).map_err(RadioError::Modulation)?;
462        if !modulation.bandwidth.in_band(config.frequency_hz) {
463            return Err(RadioError::Modulation(ModulationError::Bandwidth(
464                config.link.bandwidth_hz(),
465            )));
466        }
467        if config.power.output() != self.board.output {
468            return Err(RadioError::Output);
469        }
470        let calibrated = match Port::for_frequency(config.frequency_hz) {
471            Port::High => self.calibrated_high,
472            Port::Low => self.calibrated_low,
473        };
474        if calibrated {
475            self.standby()?;
476        } else {
477            self.calibrate(config.frequency_hz)?;
478        }
479        self.tune(config.frequency_hz)?;
480        self.write_register(register::PA_CONFIG, config.power.pa_config)?;
481        self.write_register(register::PA_DAC, config.power.pa_dac)?;
482        self.write_register(register::OCP, config.power.ocp)?;
483        self.write_register(register::MODEM_CONFIG_1, modulation.modem_config_1())?;
484        self.write_register(register::MODEM_CONFIG_2, modulation.modem_config_2(0))?;
485        self.write_register(register::MODEM_CONFIG_3, modulation.modem_config_3())?;
486        self.write_registers(register::PREAMBLE_MSB, &preamble_bytes(&config.link))?;
487        self.update_register(register::DETECT_OPTIMIZE, |value| {
488            modulation.detect_optimize(value)
489        })?;
490        self.write_register(
491            register::DETECTION_THRESHOLD,
492            modulation.detection_threshold(),
493        )?;
494        let (optimize_1, optimize_2) = high_bw_optimize(modulation.bandwidth, config.frequency_hz);
495        self.write_register(register::HIGH_BW_OPTIMIZE_1, optimize_1)?;
496        if let Some(optimize_2) = optimize_2 {
497            self.write_register(register::HIGH_BW_OPTIMIZE_2, optimize_2)?;
498        }
499        self.write_register(register::MAX_PAYLOAD_LENGTH, 0xFF)?;
500        self.write_register(register::SYNC_WORD, config.sync_word.to_byte())?;
501        self.config = Some(config);
502        Ok(())
503    }
504
505    /// Sends one frame and waits for it to leave.
506    ///
507    /// This is [`start_transmit`](Sx127x::start_transmit), a wait for the frame's airtime,
508    /// and [`finish_transmit`](Sx127x::finish_transmit) read every [`IRQ_POLL_US`] until the
509    /// chip reports the frame sent.
510    ///
511    /// # Arguments
512    ///
513    /// * `payload` - the frame's payload, 1 to 255 bytes.
514    ///
515    /// # Returns
516    ///
517    /// The frame's airtime in microseconds, for a [`DutyCycle`](crate::duty::DutyCycle) to
518    /// count.
519    ///
520    /// # Errors
521    ///
522    /// Returns the errors of [`start_transmit`](Sx127x::start_transmit), and
523    /// [`RadioError::NoInterrupt`] if TxDone does not arrive within the airtime and
524    /// [`TIMEOUT_MARGIN_US`] twice over.
525    pub fn transmit(&mut self, payload: &[u8]) -> Result<u64, RadioError<SPI::Error>> {
526        let airtime_us = self.start_transmit(payload)?;
527        let limit_us = airtime_us.saturating_add(2 * TIMEOUT_MARGIN_US);
528        let mut waited_us = airtime_us;
529        self.pause_us(airtime_us);
530        while !self.finish_transmit()? {
531            if waited_us >= limit_us {
532                return Err(RadioError::NoInterrupt);
533            }
534            self.delay.delay_us(IRQ_POLL_US);
535            waited_us = waited_us.saturating_add(u64::from(IRQ_POLL_US));
536        }
537        Ok(airtime_us)
538    }
539
540    /// Starts sending one frame and returns once the chip is transmitting.
541    ///
542    /// The steps are the datasheet's transmit sequence: standby, the IQ polarity, the payload
543    /// length, the data buffer pointer at the transmit base, the payload into RegFifo, TxDone
544    /// on DIO0, the interrupt flags cleared, and TX mode, after which the chip returns to
545    /// standby by itself.
546    ///
547    /// # Arguments
548    ///
549    /// * `payload` - the frame's payload, 1 to 255 bytes.
550    ///
551    /// # Returns
552    ///
553    /// The frame's airtime in microseconds.
554    ///
555    /// # Errors
556    ///
557    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
558    /// [`RadioError::PayloadLength`] for an empty payload or one past 255 bytes, and the bus
559    /// errors of [`write_register`](Sx127x::write_register).
560    pub fn start_transmit(&mut self, payload: &[u8]) -> Result<u64, RadioError<SPI::Error>> {
561        let settings = self.config.ok_or(RadioError::NotConfigured)?;
562        let len = u8::try_from(payload.len())
563            .ok()
564            .filter(|len| *len > 0)
565            .ok_or(RadioError::PayloadLength(payload.len()))?;
566        let invert = settings.invert_iq_transmit;
567
568        self.standby()?;
569        self.write_register(register::INVERT_IQ, invert_iq(false, invert))?;
570        self.write_register(register::INVERT_IQ_2, invert_iq_2(invert))?;
571        self.tune(settings.frequency_hz)?;
572        self.write_register(register::PAYLOAD_LENGTH, len)?;
573        self.write_register(register::FIFO_TX_BASE_ADDR, 0)?;
574        self.write_register(register::FIFO_ADDR_PTR, 0)?;
575        self.write_registers(register::FIFO, payload)?;
576        self.write_register(register::DIO_MAPPING_1, config::DIO0_TX_DONE)?;
577        self.write_register(register::IRQ_FLAGS, IrqFlags::ALL.bits())?;
578        self.write_register(register::OP_MODE, lora_op_mode(Mode::Tx))?;
579        Ok(settings.link.airtime_us(payload.len()))
580    }
581
582    /// Reports whether the frame [`start_transmit`](Sx127x::start_transmit) began has left.
583    ///
584    /// RegIrqFlags is read once, and on TxDone that flag is cleared.
585    ///
586    /// # Returns
587    ///
588    /// `true` once the frame has been sent, `false` while it is still going out.
589    ///
590    /// # Errors
591    ///
592    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
593    pub fn finish_transmit(&mut self) -> Result<bool, RadioError<SPI::Error>> {
594        let flags = self.irq_flags()?;
595        if !flags.contains(IrqFlags::TX_DONE) {
596            return Ok(false);
597        }
598        self.write_register(register::IRQ_FLAGS, IrqFlags::TX_DONE.bits())?;
599        Ok(true)
600    }
601
602    /// Listens for one frame in RXSINGLE mode.
603    ///
604    /// The receiver is prepared as for [`listen`](Sx127x::listen), the symbol timeout is set
605    /// from `timeout_us`, and RegIrqFlags is read until RxDone or RxTimeout. The chip returns
606    /// to standby by itself either way.
607    ///
608    /// # Arguments
609    ///
610    /// * `buffer` - where the payload goes.
611    /// * `timeout_us` - how long to listen for a preamble, in microseconds, which the chip
612    ///   counts in symbols from 4 to 1023; a longer timeout ends at 1023 symbols.
613    ///
614    /// # Returns
615    ///
616    /// The frame's length and levels, or that the timeout passed or the frame was corrupt.
617    ///
618    /// # Errors
619    ///
620    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
621    /// [`RadioError::BufferTooSmall`] if the payload does not fit, [`RadioError::NoInterrupt`]
622    /// if the chip never answers, and the bus errors of
623    /// [`write_register`](Sx127x::write_register).
624    pub fn receive(
625        &mut self,
626        buffer: &mut [u8],
627        timeout_us: u64,
628    ) -> Result<Reception, RadioError<SPI::Error>> {
629        let settings = self.config.ok_or(RadioError::NotConfigured)?;
630        let modulation =
631            LoraModulation::from_link(&settings.link).map_err(RadioError::Modulation)?;
632        self.prepare_reception(&settings, &modulation)?;
633        let symbols = config::symbol_timeout(&settings.link, timeout_us);
634        self.write_register(register::MODEM_CONFIG_2, modulation.modem_config_2(symbols))?;
635        self.write_register(register::SYMB_TIMEOUT_LSB, symbols as u8)?;
636        self.write_register(register::OP_MODE, lora_op_mode(Mode::RxSingle))?;
637
638        let window_us = u64::from(symbols) * settings.link.symbol_time_us();
639        let limit_us = window_us
640            .saturating_add(settings.link.airtime_us(usize::from(u8::MAX)))
641            .saturating_add(TIMEOUT_MARGIN_US);
642        let flags = self.wait_for(IrqFlags::RX_DONE | IrqFlags::RX_TIMEOUT, limit_us)?;
643        self.write_register(register::IRQ_FLAGS, flags.bits())?;
644        if !flags.contains(IrqFlags::RX_DONE) {
645            return Ok(Reception::Timeout);
646        }
647        if flags.contains(IrqFlags::PAYLOAD_CRC_ERROR) {
648            return Ok(Reception::Corrupt);
649        }
650        self.read_frame(buffer, &settings)
651    }
652
653    /// Starts listening in RXCONTINUOUS mode, so the chip receives frame after frame until
654    /// another mode is set.
655    ///
656    /// The steps are the datasheet's receive sequence with erratum 2.3 applied: standby, the
657    /// IQ polarity, the IF and the carrier offset of the erratum, the data buffer pointer at
658    /// the receive base, RxDone on DIO0, the interrupt flags cleared, and RXCONTINUOUS. Each
659    /// frame is read with [`take_frame`](Sx127x::take_frame).
660    ///
661    /// # Errors
662    ///
663    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure), and the
664    /// bus errors of [`write_register`](Sx127x::write_register).
665    pub fn listen(&mut self) -> Result<(), RadioError<SPI::Error>> {
666        let settings = self.config.ok_or(RadioError::NotConfigured)?;
667        let modulation =
668            LoraModulation::from_link(&settings.link).map_err(RadioError::Modulation)?;
669        self.prepare_reception(&settings, &modulation)?;
670        self.write_register(register::OP_MODE, lora_op_mode(Mode::RxContinuous))
671    }
672
673    /// Takes the frame a [`listen`](Sx127x::listen) has received, if one has arrived.
674    ///
675    /// RegIrqFlags is read once. On RxDone the reception flags are cleared, and a frame whose
676    /// CRC checked is copied out of the data buffer from its start address with its signal
677    /// levels while the chip goes on listening.
678    ///
679    /// # Arguments
680    ///
681    /// * `buffer` - where the payload goes.
682    ///
683    /// # Returns
684    ///
685    /// The frame, a corrupt frame, or `None` when nothing has arrived.
686    ///
687    /// # Errors
688    ///
689    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure),
690    /// [`RadioError::BufferTooSmall`] if the payload does not fit, and the bus errors of
691    /// [`write_register`](Sx127x::write_register).
692    pub fn take_frame(
693        &mut self,
694        buffer: &mut [u8],
695    ) -> Result<Option<Reception>, RadioError<SPI::Error>> {
696        let settings = self.config.ok_or(RadioError::NotConfigured)?;
697        let flags = self.irq_flags()?;
698        if !flags.contains(IrqFlags::RX_DONE) {
699            return Ok(None);
700        }
701        let received = IrqFlags::RX_DONE | IrqFlags::PAYLOAD_CRC_ERROR | IrqFlags::VALID_HEADER;
702        self.write_register(register::IRQ_FLAGS, (flags & received).bits())?;
703        if flags.contains(IrqFlags::PAYLOAD_CRC_ERROR) {
704            return Ok(Some(Reception::Corrupt));
705        }
706        self.read_frame(buffer, &settings).map(Some)
707    }
708
709    /// Puts the chip in standby, which stops a transmission or a reception.
710    ///
711    /// # Errors
712    ///
713    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
714    pub fn standby(&mut self) -> Result<(), RadioError<SPI::Error>> {
715        self.write_register(register::OP_MODE, lora_op_mode(Mode::Standby))
716    }
717
718    /// Puts the chip to sleep, where it keeps its registers but loses the data buffer.
719    ///
720    /// The configuration survives sleep, so the next transmission or reception wakes the chip
721    /// by setting its mode.
722    ///
723    /// # Errors
724    ///
725    /// Returns the bus errors of [`write_register`](Sx127x::write_register).
726    pub fn sleep(&mut self) -> Result<(), RadioError<SPI::Error>> {
727        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))
728    }
729
730    /// Reads RegVersion.
731    ///
732    /// # Returns
733    ///
734    /// The silicon revision, 0x12 for the SX1276 family.
735    ///
736    /// # Errors
737    ///
738    /// Returns [`RadioError::Spi`] if the SPI device fails.
739    pub fn version(&mut self) -> Result<u8, RadioError<SPI::Error>> {
740        self.read_register(register::VERSION)
741    }
742
743    /// Reads the raised interrupts.
744    ///
745    /// # Returns
746    ///
747    /// RegIrqFlags.
748    ///
749    /// # Errors
750    ///
751    /// Returns [`RadioError::Spi`] if the SPI device fails.
752    pub fn irq_flags(&mut self) -> Result<IrqFlags, RadioError<SPI::Error>> {
753        self.read_register(register::IRQ_FLAGS)
754            .map(IrqFlags::from_bits)
755    }
756
757    /// Reads the live state of the LoRa modem.
758    ///
759    /// # Returns
760    ///
761    /// RegModemStat, decoded.
762    ///
763    /// # Errors
764    ///
765    /// Returns [`RadioError::Spi`] if the SPI device fails.
766    pub fn modem_status(&mut self) -> Result<ModemStatus, RadioError<SPI::Error>> {
767        self.read_register(register::MODEM_STAT)
768            .map(ModemStatus::from_byte)
769    }
770
771    /// Reads the signal power the receiver hears right now, while it listens.
772    ///
773    /// # Returns
774    ///
775    /// The RSSI in dBm, with the offset of the port the configured carrier uses.
776    ///
777    /// # Errors
778    ///
779    /// Returns [`RadioError::NotConfigured`] before [`configure`](Sx127x::configure), and
780    /// [`RadioError::Spi`] if the SPI device fails.
781    pub fn rssi(&mut self) -> Result<Decibels, RadioError<SPI::Error>> {
782        let settings = self.config.ok_or(RadioError::NotConfigured)?;
783        let byte = self.read_register(register::RSSI_VALUE)?;
784        Ok(rssi_dbm(byte, Port::for_frequency(settings.frequency_hz)))
785    }
786
787    /// Reads one register.
788    ///
789    /// # Arguments
790    ///
791    /// * `address` - the register address.
792    ///
793    /// # Returns
794    ///
795    /// The register value.
796    ///
797    /// # Errors
798    ///
799    /// Returns [`RadioError::Spi`] if the SPI device fails.
800    pub fn read_register(&mut self, address: u8) -> Result<u8, RadioError<SPI::Error>> {
801        let mut value = [0u8; 1];
802        self.read_registers(address, &mut value)?;
803        Ok(value[0])
804    }
805
806    /// Writes one register.
807    ///
808    /// # Arguments
809    ///
810    /// * `address` - the register address.
811    /// * `value` - the value.
812    ///
813    /// # Errors
814    ///
815    /// Returns [`RadioError::Spi`] if the SPI device fails.
816    pub fn write_register(&mut self, address: u8, value: u8) -> Result<(), RadioError<SPI::Error>> {
817        self.write_registers(address, &[value])
818    }
819
820    /// Reads consecutive registers in one transaction, or bytes out of the data buffer when
821    /// `address` is [`register::FIFO`].
822    ///
823    /// # Arguments
824    ///
825    /// * `address` - the first register's address.
826    /// * `values` - where the values go.
827    ///
828    /// # Errors
829    ///
830    /// Returns [`RadioError::Spi`] if the SPI device fails.
831    pub fn read_registers(
832        &mut self,
833        address: u8,
834        values: &mut [u8],
835    ) -> Result<(), RadioError<SPI::Error>> {
836        self.spi
837            .transaction(&mut [
838                Operation::Write(&[read_address(address)]),
839                Operation::Read(values),
840            ])
841            .map_err(RadioError::Spi)
842    }
843
844    /// Writes consecutive registers in one transaction, or bytes into the data buffer when
845    /// `address` is [`register::FIFO`].
846    ///
847    /// # Arguments
848    ///
849    /// * `address` - the first register's address.
850    /// * `values` - the values.
851    ///
852    /// # Errors
853    ///
854    /// Returns [`RadioError::Spi`] if the SPI device fails.
855    pub fn write_registers(
856        &mut self,
857        address: u8,
858        values: &[u8],
859    ) -> Result<(), RadioError<SPI::Error>> {
860        self.spi
861            .transaction(&mut [
862                Operation::Write(&[write_address(address)]),
863                Operation::Write(values),
864            ])
865            .map_err(RadioError::Spi)
866    }
867
868    fn calibrate(&mut self, frequency_hz: u32) -> Result<(), RadioError<SPI::Error>> {
869        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
870        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
871        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Standby))?;
872        self.write_register(register::PA_CONFIG, 0x00)?;
873        self.write_registers(register::FRF_MSB, &frequency_bytes(frequency_hz))?;
874        self.tuned_hz = Some(frequency_hz);
875        self.update_register(register::IMAGE_CAL, image_cal_start)?;
876        let mut waited_us = 0u32;
877        while self.read_register(register::IMAGE_CAL)? & config::IMAGE_CAL_RUNNING != 0 {
878            if waited_us >= CALIBRATION_LIMIT_US {
879                return Err(RadioError::Calibration);
880            }
881            self.delay.delay_us(CALIBRATION_POLL_US);
882            waited_us = waited_us.saturating_add(CALIBRATION_POLL_US);
883        }
884        self.write_register(register::OP_MODE, fsk_op_mode(Mode::Sleep))?;
885        self.write_register(register::OP_MODE, lora_op_mode(Mode::Sleep))?;
886        self.standby()?;
887        match Port::for_frequency(frequency_hz) {
888            Port::High => self.calibrated_high = true,
889            Port::Low => self.calibrated_low = true,
890        }
891        Ok(())
892    }
893
894    fn tune(&mut self, frequency_hz: u32) -> Result<(), RadioError<SPI::Error>> {
895        if self.tuned_hz != Some(frequency_hz) {
896            self.write_registers(register::FRF_MSB, &frequency_bytes(frequency_hz))?;
897            self.tuned_hz = Some(frequency_hz);
898        }
899        Ok(())
900    }
901
902    fn prepare_reception(
903        &mut self,
904        settings: &RadioConfig,
905        modulation: &LoraModulation,
906    ) -> Result<(), RadioError<SPI::Error>> {
907        let invert = settings.invert_iq_receive;
908        self.standby()?;
909        self.write_register(register::INVERT_IQ, invert_iq(invert, false))?;
910        self.write_register(register::INVERT_IQ_2, invert_iq_2(invert))?;
911        let erratum = spurious_reception(modulation.bandwidth);
912        self.update_register(register::DETECT_OPTIMIZE, |value| {
913            automatic_if(value, erratum.automatic_if)
914        })?;
915        if let Some(if_freq_2) = erratum.if_freq_2 {
916            self.write_register(register::IF_FREQ_1, 0x00)?;
917            self.write_register(register::IF_FREQ_2, if_freq_2)?;
918        }
919        self.tune(settings.frequency_hz.saturating_add(erratum.offset_hz))?;
920        self.write_register(register::FIFO_RX_BASE_ADDR, 0)?;
921        self.write_register(register::FIFO_ADDR_PTR, 0)?;
922        self.write_register(register::DIO_MAPPING_1, config::DIO0_RX_DONE)?;
923        self.write_register(register::IRQ_FLAGS, IrqFlags::ALL.bits())
924    }
925
926    fn read_frame(
927        &mut self,
928        buffer: &mut [u8],
929        settings: &RadioConfig,
930    ) -> Result<Reception, RadioError<SPI::Error>> {
931        let len = usize::from(self.read_register(register::RX_NB_BYTES)?);
932        let frame = buffer
933            .get_mut(..len)
934            .ok_or(RadioError::BufferTooSmall(len))?;
935        let start = self.read_register(register::FIFO_RX_CURRENT_ADDR)?;
936        self.write_register(register::FIFO_ADDR_PTR, start)?;
937        if len > 0 {
938            self.read_registers(register::FIFO, frame)?;
939        }
940        let mut levels = [0u8; 2];
941        self.read_registers(register::PKT_SNR_VALUE, &mut levels)?;
942        Ok(Reception::Frame {
943            len,
944            status: PacketStatus::from_bytes(levels, Port::for_frequency(settings.frequency_hz)),
945        })
946    }
947
948    fn update_register(
949        &mut self,
950        address: u8,
951        change: impl FnOnce(u8) -> u8,
952    ) -> Result<(), RadioError<SPI::Error>> {
953        let value = self.read_register(address)?;
954        self.write_register(address, change(value))
955    }
956
957    fn wait_for(
958        &mut self,
959        events: IrqFlags,
960        limit_us: u64,
961    ) -> Result<IrqFlags, RadioError<SPI::Error>> {
962        let mut waited_us = 0u64;
963        loop {
964            let flags = self.irq_flags()?;
965            if flags.intersects(events) {
966                return Ok(flags);
967            }
968            if waited_us >= limit_us {
969                return Err(RadioError::NoInterrupt);
970            }
971            self.delay.delay_us(IRQ_POLL_US);
972            waited_us = waited_us.saturating_add(u64::from(IRQ_POLL_US));
973        }
974    }
975
976    fn pause_us(&mut self, micros: u64) {
977        let mut left = micros;
978        while left > 0 {
979            let step = u32::try_from(left).unwrap_or(u32::MAX);
980            self.delay.delay_us(step);
981            left -= u64::from(step);
982        }
983    }
984}
985
986#[cfg(test)]
987mod tests {
988    use super::*;
989    use pamoja_hal::digital::PinState;
990    use pamoja_hal::script::{DelayLog, PinScript, SpiScript, SpiStep};
991
992    type Radio = Sx127x<SpiScript, PinScript, DelayLog>;
993
994    fn radio(steps: Vec<SpiStep>) -> Radio {
995        Sx127x::new(
996            SpiScript::new(steps),
997            PinScript::new([]),
998            DelayLog::new(),
999            Board::new(PaOutput::PaBoost),
1000        )
1001    }
1002
1003    fn wrote(address: u8, values: &[u8]) -> [SpiStep; 2] {
1004        [
1005            SpiStep::write([address | 0x80]),
1006            SpiStep::write(values.to_vec()),
1007        ]
1008    }
1009
1010    fn read(address: u8, values: &[u8]) -> [SpiStep; 2] {
1011        [SpiStep::write([address]), SpiStep::read(values.to_vec())]
1012    }
1013
1014    fn eu868() -> RadioConfig {
1015        RadioConfig::new(
1016            868_100_000,
1017            LinkSettings::new(7, 125_000),
1018            TxPower::for_output(PaOutput::PaBoost, 14),
1019        )
1020        .with_sync_word(SyncWord::Public)
1021    }
1022
1023    fn calibration(frequency: [u8; 3]) -> Vec<SpiStep> {
1024        let mut steps = Vec::new();
1025        steps.extend(wrote(0x01, &[0x88]));
1026        steps.extend(wrote(0x01, &[0x08]));
1027        steps.extend(wrote(0x01, &[0x09]));
1028        steps.extend(wrote(0x09, &[0x00]));
1029        steps.extend(wrote(0x06, &frequency));
1030        steps.extend(read(0x3B, &[0x82]));
1031        steps.extend(wrote(0x3B, &[0x42]));
1032        steps.extend(read(0x3B, &[0x22]));
1033        steps.extend(read(0x3B, &[0x02]));
1034        steps.extend(wrote(0x01, &[0x08]));
1035        steps.extend(wrote(0x01, &[0x88]));
1036        steps.extend(wrote(0x01, &[0x89]));
1037        steps
1038    }
1039
1040    fn configuration() -> Vec<SpiStep> {
1041        let mut steps = calibration([0xD9, 0x06, 0x66]);
1042        steps.extend(wrote(0x09, &[0xFC]));
1043        steps.extend(wrote(0x4D, &[0x84]));
1044        steps.extend(wrote(0x0B, &[0x2B]));
1045        steps.extend(wrote(0x1D, &[0x72]));
1046        steps.extend(wrote(0x1E, &[0x74]));
1047        steps.extend(wrote(0x26, &[0x04]));
1048        steps.extend(wrote(0x20, &[0x00, 0x08]));
1049        steps.extend(read(0x31, &[0xC3]));
1050        steps.extend(wrote(0x31, &[0xC3]));
1051        steps.extend(wrote(0x37, &[0x0A]));
1052        steps.extend(wrote(0x36, &[0x03]));
1053        steps.extend(wrote(0x23, &[0xFF]));
1054        steps.extend(wrote(0x39, &[0x34]));
1055        steps
1056    }
1057
1058    fn configured(more: Vec<SpiStep>) -> Radio {
1059        let mut steps = configuration();
1060        steps.extend(more);
1061        let mut radio = radio(steps);
1062        radio.configure(eu868()).expect("configures");
1063        radio
1064    }
1065
1066    fn reception_setup() -> Vec<SpiStep> {
1067        let mut steps = Vec::new();
1068        steps.extend(wrote(0x01, &[0x89]));
1069        steps.extend(wrote(0x33, &[0x27]));
1070        steps.extend(wrote(0x3B, &[0x1D]));
1071        steps.extend(read(0x31, &[0xC3]));
1072        steps.extend(wrote(0x31, &[0x43]));
1073        steps.extend(wrote(0x30, &[0x00]));
1074        steps.extend(wrote(0x2F, &[0x40]));
1075        steps.extend(wrote(0x0F, &[0x00]));
1076        steps.extend(wrote(0x0D, &[0x00]));
1077        steps.extend(wrote(0x40, &[0x00]));
1078        steps.extend(wrote(0x12, &[0xFF]));
1079        steps
1080    }
1081
1082    #[test]
1083    fn init_resets_checks_the_version_and_enters_lora_mode_with_a_tcxo() {
1084        let mut steps = Vec::new();
1085        steps.extend(read(0x42, &[0x12]));
1086        steps.extend(wrote(0x01, &[0x08]));
1087        steps.extend(wrote(0x4B, &[0x19]));
1088        steps.extend(wrote(0x01, &[0x88]));
1089        steps.extend(wrote(0x01, &[0x89]));
1090        steps.extend(wrote(0x0C, &[0x23]));
1091        let board = Board::new(PaOutput::PaBoost).with_tcxo();
1092        let mut radio = Sx127x::new(
1093            SpiScript::new(steps),
1094            PinScript::new([]),
1095            DelayLog::new(),
1096            board,
1097        );
1098
1099        radio.init().expect("initializes");
1100
1101        let (spi, reset, delay) = radio.release();
1102        assert!(spi.done(), "{} steps left", spi.remaining());
1103        assert_eq!(reset.driven(), [PinState::Low, PinState::High]);
1104        assert_eq!(delay.waits_ns(), [1_000_000, 6_000_000]);
1105    }
1106
1107    #[test]
1108    fn init_refuses_a_chip_that_is_not_an_sx1276() {
1109        let mut radio = radio(read(0x42, &[0x22]).to_vec());
1110        assert_eq!(radio.init(), Err(RadioError::Absent(0x22)));
1111    }
1112
1113    #[test]
1114    fn configure_calibrates_the_port_once_then_writes_the_modem() {
1115        let mut steps = configuration();
1116        steps.extend(wrote(0x01, &[0x89]));
1117        steps.extend(wrote(0x06, &[0xD9, 0x13, 0x33]));
1118        steps.extend(wrote(0x09, &[0xFC]));
1119        steps.extend(wrote(0x4D, &[0x84]));
1120        steps.extend(wrote(0x0B, &[0x2B]));
1121        steps.extend(wrote(0x1D, &[0x72]));
1122        steps.extend(wrote(0x1E, &[0x74]));
1123        steps.extend(wrote(0x26, &[0x04]));
1124        steps.extend(wrote(0x20, &[0x00, 0x08]));
1125        steps.extend(read(0x31, &[0xC3]));
1126        steps.extend(wrote(0x31, &[0xC3]));
1127        steps.extend(wrote(0x37, &[0x0A]));
1128        steps.extend(wrote(0x36, &[0x03]));
1129        steps.extend(wrote(0x23, &[0xFF]));
1130        steps.extend(wrote(0x39, &[0x34]));
1131        let mut radio = radio(steps);
1132
1133        radio.configure(eu868()).expect("configures on 868.1 MHz");
1134        let mut next = eu868();
1135        next.frequency_hz = 868_300_000;
1136        radio.configure(next).expect("configures on 868.3 MHz");
1137
1138        let (spi, _, delay) = radio.release();
1139        assert!(spi.done(), "{} steps left", spi.remaining());
1140        assert_eq!(delay.waits_ns(), [1_000_000], "one poll while calibrating");
1141    }
1142
1143    #[test]
1144    fn configure_refuses_settings_the_chip_cannot_use() {
1145        let mut radio = radio(Vec::new());
1146        let sf5 = RadioConfig::new(
1147            868_100_000,
1148            LinkSettings::new(5, 125_000),
1149            TxPower::for_output(PaOutput::PaBoost, 14),
1150        );
1151        assert_eq!(
1152            radio.configure(sf5),
1153            Err(RadioError::Modulation(ModulationError::SpreadingFactor(5)))
1154        );
1155        let wide_at_169 = RadioConfig::new(
1156            169_400_000,
1157            LinkSettings::new(7, 500_000),
1158            TxPower::for_output(PaOutput::PaBoost, 14),
1159        );
1160        assert_eq!(
1161            radio.configure(wide_at_169),
1162            Err(RadioError::Modulation(ModulationError::Bandwidth(500_000)))
1163        );
1164        let rfo = RadioConfig::new(
1165            868_100_000,
1166            LinkSettings::new(7, 125_000),
1167            TxPower::for_output(PaOutput::Rfo, 14),
1168        );
1169        assert_eq!(radio.configure(rfo), Err(RadioError::Output));
1170        let (spi, _, _) = radio.release();
1171        assert_eq!(spi.consumed(), 0, "nothing reaches the bus");
1172    }
1173
1174    #[test]
1175    fn a_frame_goes_out_through_the_fifo_and_tx_mode() {
1176        let mut steps = Vec::new();
1177        steps.extend(wrote(0x01, &[0x89]));
1178        steps.extend(wrote(0x33, &[0x27]));
1179        steps.extend(wrote(0x3B, &[0x1D]));
1180        steps.extend(wrote(0x22, &[0x05]));
1181        steps.extend(wrote(0x0E, &[0x00]));
1182        steps.extend(wrote(0x0D, &[0x00]));
1183        steps.extend(wrote(0x00, b"level"));
1184        steps.extend(wrote(0x40, &[0x40]));
1185        steps.extend(wrote(0x12, &[0xFF]));
1186        steps.extend(wrote(0x01, &[0x8B]));
1187        steps.extend(read(0x12, &[0x00]));
1188        steps.extend(read(0x12, &[0x08]));
1189        steps.extend(wrote(0x12, &[0x08]));
1190        let mut radio = configured(steps);
1191
1192        let airtime = radio.transmit(b"level").expect("sends");
1193
1194        assert_eq!(airtime, LinkSettings::new(7, 125_000).airtime_us(5));
1195        let (spi, _, _) = radio.release();
1196        assert!(spi.done(), "{} steps left", spi.remaining());
1197    }
1198
1199    #[test]
1200    fn a_transmission_needs_a_configuration_and_a_payload_that_fits() {
1201        let mut unconfigured = radio(Vec::new());
1202        assert_eq!(
1203            unconfigured.start_transmit(b"x"),
1204            Err(RadioError::NotConfigured)
1205        );
1206        let mut radio = configured(Vec::new());
1207        assert_eq!(radio.start_transmit(&[]), Err(RadioError::PayloadLength(0)));
1208        assert_eq!(
1209            radio.start_transmit(&[0; 256]),
1210            Err(RadioError::PayloadLength(256))
1211        );
1212    }
1213
1214    #[test]
1215    fn listening_applies_the_spurious_reception_erratum_and_takes_frames() {
1216        let mut steps = reception_setup();
1217        steps.extend(wrote(0x01, &[0x8D]));
1218        steps.extend(read(0x12, &[0x00]));
1219        steps.extend(read(0x12, &[0x50]));
1220        steps.extend(wrote(0x12, &[0x50]));
1221        steps.extend(read(0x13, &[0x02]));
1222        steps.extend(read(0x10, &[0x00]));
1223        steps.extend(wrote(0x0D, &[0x00]));
1224        steps.extend(read(0x00, b"hi"));
1225        steps.extend(read(0x19, &[0xF6, 0x30]));
1226        steps.extend(read(0x12, &[0x60]));
1227        steps.extend(wrote(0x12, &[0x60]));
1228        let mut radio = configured(steps);
1229        let mut buffer = [0u8; 255];
1230
1231        radio.listen().expect("listens");
1232        assert_eq!(radio.take_frame(&mut buffer), Ok(None));
1233        let frame = radio.take_frame(&mut buffer).expect("reads the frame");
1234        assert_eq!(
1235            frame,
1236            Some(Reception::Frame {
1237                len: 2,
1238                status: PacketStatus::from_bytes([0xF6, 0x30], Port::High),
1239            })
1240        );
1241        assert_eq!(&buffer[..2], b"hi");
1242        assert_eq!(radio.take_frame(&mut buffer), Ok(Some(Reception::Corrupt)));
1243
1244        let (spi, _, _) = radio.release();
1245        assert!(spi.done(), "{} steps left", spi.remaining());
1246    }
1247
1248    #[test]
1249    fn a_narrow_bandwidth_listens_one_bandwidth_above_the_carrier() {
1250        let narrow = RadioConfig::new(
1251            433_175_000,
1252            LinkSettings::new(9, 20_833),
1253            TxPower::for_output(PaOutput::PaBoost, 10),
1254        );
1255        let mut steps = calibration([0x6C, 0x4B, 0x33]);
1256        steps.extend(wrote(0x09, &[0xF8]));
1257        steps.extend(wrote(0x4D, &[0x84]));
1258        steps.extend(wrote(0x0B, &[0x2B]));
1259        steps.extend(wrote(0x1D, &[0x32]));
1260        steps.extend(wrote(0x1E, &[0x94]));
1261        steps.extend(wrote(0x26, &[0x0C]));
1262        steps.extend(wrote(0x20, &[0x00, 0x08]));
1263        steps.extend(read(0x31, &[0xC3]));
1264        steps.extend(wrote(0x31, &[0xC3]));
1265        steps.extend(wrote(0x37, &[0x0A]));
1266        steps.extend(wrote(0x36, &[0x03]));
1267        steps.extend(wrote(0x23, &[0xFF]));
1268        steps.extend(wrote(0x39, &[0x12]));
1269        steps.extend(wrote(0x01, &[0x89]));
1270        steps.extend(wrote(0x33, &[0x27]));
1271        steps.extend(wrote(0x3B, &[0x1D]));
1272        steps.extend(read(0x31, &[0xC3]));
1273        steps.extend(wrote(0x31, &[0x43]));
1274        steps.extend(wrote(0x30, &[0x00]));
1275        steps.extend(wrote(0x2F, &[0x44]));
1276        steps.extend(wrote(0x06, &frequency_bytes(433_175_000 + 20_830)));
1277        steps.extend(wrote(0x0F, &[0x00]));
1278        steps.extend(wrote(0x0D, &[0x00]));
1279        steps.extend(wrote(0x40, &[0x00]));
1280        steps.extend(wrote(0x12, &[0xFF]));
1281        steps.extend(wrote(0x01, &[0x8D]));
1282        let mut radio = radio(steps);
1283
1284        radio.configure(narrow).expect("configures");
1285        radio.listen().expect("listens");
1286
1287        let (spi, _, _) = radio.release();
1288        assert!(spi.done(), "{} steps left", spi.remaining());
1289    }
1290
1291    #[test]
1292    fn a_single_reception_counts_its_timeout_in_symbols_and_reports_it() {
1293        let mut steps = reception_setup();
1294        steps.extend(wrote(0x1E, &[0x74]));
1295        steps.extend(wrote(0x1F, &[0x62]));
1296        steps.extend(wrote(0x01, &[0x8E]));
1297        steps.extend(read(0x12, &[0x80]));
1298        steps.extend(wrote(0x12, &[0x80]));
1299        let mut radio = configured(steps);
1300        let mut buffer = [0u8; 64];
1301
1302        let outcome = radio.receive(&mut buffer, 100_000).expect("listens");
1303
1304        assert_eq!(outcome, Reception::Timeout);
1305        let (spi, _, _) = radio.release();
1306        assert!(spi.done(), "{} steps left", spi.remaining());
1307    }
1308
1309    #[test]
1310    fn a_frame_longer_than_the_buffer_is_refused() {
1311        let mut steps = reception_setup();
1312        steps.extend(wrote(0x01, &[0x8D]));
1313        steps.extend(read(0x12, &[0x40]));
1314        steps.extend(wrote(0x12, &[0x40]));
1315        steps.extend(read(0x13, &[0x10]));
1316        let mut radio = configured(steps);
1317        let mut buffer = [0u8; 8];
1318
1319        radio.listen().expect("listens");
1320        assert_eq!(
1321            radio.take_frame(&mut buffer),
1322            Err(RadioError::BufferTooSmall(16))
1323        );
1324    }
1325
1326    #[test]
1327    fn a_calibration_that_never_finishes_is_an_error() {
1328        let mut steps = Vec::new();
1329        steps.extend(wrote(0x01, &[0x88]));
1330        steps.extend(wrote(0x01, &[0x08]));
1331        steps.extend(wrote(0x01, &[0x09]));
1332        steps.extend(wrote(0x09, &[0x00]));
1333        steps.extend(wrote(0x06, &[0xD9, 0x06, 0x66]));
1334        steps.extend(read(0x3B, &[0x82]));
1335        steps.extend(wrote(0x3B, &[0x42]));
1336        for _ in 0..=CALIBRATION_LIMIT_US / CALIBRATION_POLL_US {
1337            steps.extend(read(0x3B, &[0x22]));
1338        }
1339        let mut radio = radio(steps);
1340
1341        assert_eq!(radio.configure(eu868()), Err(RadioError::Calibration));
1342    }
1343
1344    #[test]
1345    fn the_rssi_uses_the_offset_of_the_configured_port() {
1346        let mut radio = configured(read(0x1B, &[0x30]).to_vec());
1347        assert_eq!(radio.rssi(), Ok(Decibels::from_db(-109)));
1348    }
1349}