1use std::collections::{BTreeMap, VecDeque};
52use std::convert::Infallible;
53use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
54use std::vec::Vec;
55
56use embedded_hal::delay::DelayNs;
57use embedded_hal::digital::{self, InputPin, OutputPin};
58use embedded_hal::spi::{self, Operation, SpiDevice};
59use pamoja_lora::budget::{self, Decibels, RADIO_NOISE_FIGURE_DB};
60use pamoja_lora::LinkSettings;
61
62use crate::radio::{Family, Radio, SyncWord};
63use crate::sx126x::command::opcode;
64use crate::sx126x::config::register as sx126x_register;
65use crate::sx126x::irq::Irq;
66use crate::sx127x::irq::IrqFlags;
67use crate::sx127x::register as sx127x_register;
68use crate::sx127x::status::Port;
69use crate::{sx126x, sx127x};
70
71pub type SimRadio = Radio<ChipSpi, ChipPin, ChipPin, ChipDelay>;
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub struct Tuning {
77 pub frequency_hz: u32,
80 pub link: LinkSettings,
82 pub output_dbm: i8,
84 pub sync_word: SyncWord,
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
90pub struct Sent {
91 pub tuning: Tuning,
93 pub payload: Vec<u8>,
95}
96
97#[derive(Clone, Debug)]
101pub struct Chip {
102 state: Arc<Mutex<State>>,
103}
104
105impl Chip {
106 pub fn sx126x(board: sx126x::Board) -> Chip {
117 Chip::with(Model::Sx126x(Sx126xModel::new(board)))
118 }
119
120 pub fn sx127x(board: sx127x::Board) -> Chip {
131 Chip::with(Model::Sx127x(Sx127xModel::new(board)))
132 }
133
134 fn with(model: Model) -> Chip {
135 Chip {
136 state: Arc::new(Mutex::new(State {
137 model,
138 air: VecDeque::new(),
139 sent: Vec::new(),
140 random: RANDOM_SEED,
141 })),
142 }
143 }
144
145 pub fn family(&self) -> Family {
151 match self.lock().model {
152 Model::Sx126x(_) => Family::Sx126x,
153 Model::Sx127x(_) => Family::Sx127x,
154 }
155 }
156
157 pub fn radio(&self) -> SimRadio {
163 let spi = ChipSpi {
164 state: Arc::clone(&self.state),
165 };
166 let busy = ChipPin {
167 state: Arc::clone(&self.state),
168 };
169 let reset = ChipPin {
170 state: Arc::clone(&self.state),
171 };
172 match &self.lock().model {
173 Model::Sx126x(model) => Radio::from(sx126x::Sx126x::new(
174 spi,
175 busy,
176 reset,
177 ChipDelay,
178 model.board,
179 )),
180 Model::Sx127x(model) => {
181 Radio::from(sx127x::Sx127x::new(spi, reset, ChipDelay, model.board))
182 }
183 }
184 }
185
186 pub fn hear(&self, payload: &[u8], rssi_dbm: Decibels, snr_db: Decibels) {
195 let payload = payload[..payload.len().min(usize::from(u8::MAX))].to_vec();
196 self.lock().air.push_back(Arrival {
197 payload,
198 rssi_dbm,
199 snr_db,
200 intact: true,
201 });
202 }
203
204 pub fn hear_corrupt(&self, rssi_dbm: Decibels, snr_db: Decibels) {
211 self.lock().air.push_back(Arrival {
212 payload: Vec::new(),
213 rssi_dbm,
214 snr_db,
215 intact: false,
216 });
217 }
218
219 pub fn waiting(&self) -> usize {
225 self.lock().air.len()
226 }
227
228 pub fn sent(&self) -> Vec<Sent> {
234 self.lock().sent.clone()
235 }
236
237 pub fn tuning(&self) -> Tuning {
246 self.lock().model.tuning()
247 }
248
249 fn lock(&self) -> MutexGuard<'_, State> {
250 self.state.lock().unwrap_or_else(PoisonError::into_inner)
251 }
252}
253
254#[derive(Debug)]
256pub struct ChipSpi {
257 state: Arc<Mutex<State>>,
258}
259
260impl spi::ErrorType for ChipSpi {
261 type Error = Infallible;
262}
263
264impl SpiDevice for ChipSpi {
265 fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Infallible> {
266 let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
267 state.transaction(operations);
268 Ok(())
269 }
270}
271
272#[derive(Debug)]
275pub struct ChipPin {
276 state: Arc<Mutex<State>>,
277}
278
279impl digital::ErrorType for ChipPin {
280 type Error = Infallible;
281}
282
283impl InputPin for ChipPin {
284 fn is_high(&mut self) -> Result<bool, Infallible> {
285 Ok(false)
286 }
287
288 fn is_low(&mut self) -> Result<bool, Infallible> {
289 Ok(true)
290 }
291}
292
293impl OutputPin for ChipPin {
294 fn set_low(&mut self) -> Result<(), Infallible> {
295 let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
296 state.model.hold_in_reset();
297 Ok(())
298 }
299
300 fn set_high(&mut self) -> Result<(), Infallible> {
301 Ok(())
302 }
303}
304
305#[derive(Clone, Copy, Debug, Default)]
307pub struct ChipDelay;
308
309impl DelayNs for ChipDelay {
310 fn delay_ns(&mut self, _ns: u32) {}
311}
312
313const RANDOM_SEED: u32 = 0x2545_F491;
314
315#[derive(Clone, Debug)]
316struct Arrival {
317 payload: Vec<u8>,
318 rssi_dbm: Decibels,
319 snr_db: Decibels,
320 intact: bool,
321}
322
323#[derive(Debug)]
324struct State {
325 model: Model,
326 air: VecDeque<Arrival>,
327 sent: Vec<Sent>,
328 random: u32,
329}
330
331impl State {
332 fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) {
333 let mut written = Vec::new();
334 let mut answered = false;
335 for operation in operations.iter_mut() {
336 match operation {
337 Operation::Write(bytes) => written.extend_from_slice(bytes),
338 Operation::Read(answer) => {
339 self.answer(&written, answer);
340 answered = true;
341 }
342 Operation::Transfer(answer, bytes) => {
343 written.extend_from_slice(bytes);
344 self.answer(&written, answer);
345 answered = true;
346 }
347 Operation::TransferInPlace(bytes) => {
348 written.extend_from_slice(bytes);
349 self.answer(&written, bytes);
350 answered = true;
351 }
352 Operation::DelayNs(_) => {}
353 }
354 }
355 if !answered {
356 self.write(&written);
357 }
358 }
359
360 fn answer(&mut self, written: &[u8], answer: &mut [u8]) {
361 let State {
362 model,
363 air,
364 sent: _,
365 random,
366 } = self;
367 match model {
368 Model::Sx126x(chip) => chip.answer(written, answer, air, random),
369 Model::Sx127x(chip) => chip.read(written, answer, air, random),
370 }
371 }
372
373 fn write(&mut self, written: &[u8]) {
374 let State { model, sent, .. } = self;
375 match model {
376 Model::Sx126x(chip) => chip.command(written, sent),
377 Model::Sx127x(chip) => chip.write(written, sent),
378 }
379 }
380}
381
382#[derive(Debug)]
383enum Model {
384 Sx126x(Sx126xModel),
385 Sx127x(Sx127xModel),
386}
387
388impl Model {
389 fn tuning(&self) -> Tuning {
390 match self {
391 Model::Sx126x(chip) => chip.tuning(),
392 Model::Sx127x(chip) => chip.tuning(),
393 }
394 }
395
396 fn hold_in_reset(&mut self) {
397 match self {
398 Model::Sx126x(chip) => *chip = Sx126xModel::new(chip.board),
399 Model::Sx127x(chip) => *chip = Sx127xModel::new(chip.board),
400 }
401 }
402}
403
404fn noise_byte(random: &mut u32) -> u8 {
406 let mut x = *random;
407 x ^= x << 13;
408 x ^= x >> 17;
409 x ^= x << 5;
410 *random = x;
411 x.to_le_bytes()[0]
412}
413
414fn noise_floor(bandwidth_hz: u32) -> Decibels {
417 budget::noise_floor_dbm(bandwidth_hz) + RADIO_NOISE_FIGURE_DB
418}
419
420fn half_decibels_below(level: Decibels) -> u8 {
422 let halves = (-level.hundredths() + 25).div_euclid(50);
423 halves.clamp(0, i32::from(u8::MAX)) as u8
424}
425
426fn quarter_decibels(ratio: Decibels) -> u8 {
428 let quarters = (ratio.hundredths() + 12).div_euclid(25);
429 quarters.clamp(i32::from(i8::MIN), i32::from(i8::MAX)) as i8 as u8
430}
431
432fn signal_level(arrival: &Arrival) -> Decibels {
434 if arrival.snr_db < Decibels::ZERO {
435 arrival.rssi_dbm + arrival.snr_db
436 } else {
437 arrival.rssi_dbm
438 }
439}
440
441#[derive(Clone, Copy, Debug, PartialEq, Eq)]
442enum Sx126xMode {
443 Sleep,
444 StandbyRc,
445 StandbyXosc,
446 Fs,
447 Rx { continuous: bool, timeout: bool },
448 Cad,
449}
450
451const SX126X_STATUS_DATA_AVAILABLE: u8 = 0x2;
452const SX126X_STATUS_TIMEOUT: u8 = 0x3;
453const SX126X_STATUS_TX_DONE: u8 = 0x6;
454const SX126X_RX_CONTINUOUS: u32 = 0x00FF_FFFF;
455
456#[derive(Debug)]
457struct Sx126xModel {
458 board: sx126x::Board,
459 mode: Sx126xMode,
460 command_status: u8,
461 packet_type: u8,
462 frequency_word: u32,
463 modulation: [u8; 4],
464 packet: [u8; 6],
465 output_dbm: i8,
466 irq_mask: u16,
467 irq: u16,
468 tx_base: u8,
469 rx_base: u8,
470 buffer: [u8; 256],
471 rx_buffer: [u8; 2],
472 packet_status: [u8; 3],
473 registers: BTreeMap<u16, u8>,
474}
475
476impl Sx126xModel {
477 fn new(board: sx126x::Board) -> Sx126xModel {
478 let mut registers = BTreeMap::new();
479 let [high, low] = sx126x::config::SyncWord::Private.to_bytes();
480 registers.insert(sx126x_register::LORA_SYNC_WORD, high);
481 registers.insert(sx126x_register::LORA_SYNC_WORD + 1, low);
482 Sx126xModel {
483 board,
484 mode: Sx126xMode::StandbyRc,
485 command_status: 0,
486 packet_type: sx126x::config::PacketType::Lora.code(),
487 frequency_word: 0,
488 modulation: [7, sx126x::config::LoraBandwidth::Khz125.code(), 0x01, 0],
489 packet: [0, 8, 0, u8::MAX, 1, 0],
490 output_dbm: 0,
491 irq_mask: 0,
492 irq: 0,
493 tx_base: 0,
494 rx_base: 0,
495 buffer: [0; 256],
496 rx_buffer: [0; 2],
497 packet_status: [0; 3],
498 registers,
499 }
500 }
501
502 fn tuning(&self) -> Tuning {
503 let [spreading_factor, bandwidth, coding_rate, _] = self.modulation;
504 let bandwidth_hz = sx126x::config::LoraBandwidth::from_code(bandwidth)
505 .map_or(0, sx126x::config::LoraBandwidth::hz);
506 let denominator = match coding_rate {
507 2 | 6 => 6,
508 3 => 7,
509 4 | 7 => 8,
510 _ => 5,
511 };
512 let [preamble_high, preamble_low, header, _, crc, _] = self.packet;
513 let mut link = LinkSettings::new(spreading_factor, bandwidth_hz)
514 .with_coding_rate(denominator)
515 .with_preamble(u16::from_be_bytes([preamble_high, preamble_low]));
516 if header != 0 {
517 link = link.implicit_header();
518 }
519 if crc == 0 {
520 link = link.without_crc();
521 }
522 let high = self.register(sx126x_register::LORA_SYNC_WORD);
523 let low = self.register(sx126x_register::LORA_SYNC_WORD + 1);
524 Tuning {
525 frequency_hz: sx126x::config::frequency_from_word(self.frequency_word),
526 link,
527 output_dbm: self.output_dbm,
528 sync_word: SyncWord::from_byte((high & 0xF0) | (low >> 4)),
529 }
530 }
531
532 fn register(&self, address: u16) -> u8 {
533 self.registers.get(&address).copied().unwrap_or(0)
534 }
535
536 fn status(&self) -> u8 {
537 let mode = match self.mode {
538 Sx126xMode::Sleep | Sx126xMode::StandbyRc => 0x2,
539 Sx126xMode::StandbyXosc => 0x3,
540 Sx126xMode::Fs => 0x4,
541 Sx126xMode::Rx { .. } | Sx126xMode::Cad => 0x5,
542 };
543 (mode << 4) | (self.command_status << 1)
544 }
545
546 fn raise(&mut self, events: Irq) {
547 self.irq |= events.bits() & self.irq_mask;
548 }
549
550 fn command(&mut self, written: &[u8], sent: &mut Vec<Sent>) {
551 if self.mode == Sx126xMode::Sleep {
552 self.mode = Sx126xMode::StandbyRc;
553 }
554 let Some((&code, params)) = written.split_first() else {
555 return;
556 };
557 let word = |at: usize| -> u32 {
558 params.get(at..at + 3).map_or(0, |bytes| {
559 u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]])
560 })
561 };
562 let pair = |at: usize| -> u16 {
563 params
564 .get(at..at + 2)
565 .map_or(0, |bytes| u16::from_be_bytes([bytes[0], bytes[1]]))
566 };
567 match code {
568 opcode::SET_SLEEP => {
569 let warm = params.first().is_some_and(|config| config & 0x04 != 0);
570 if !warm {
571 *self = Sx126xModel::new(self.board);
572 }
573 self.mode = Sx126xMode::Sleep;
574 }
575 opcode::SET_STANDBY => {
576 self.mode = if params.first() == Some(&1) {
577 Sx126xMode::StandbyXosc
578 } else {
579 Sx126xMode::StandbyRc
580 };
581 }
582 opcode::SET_FS => self.mode = Sx126xMode::Fs,
583 opcode::SET_TX => self.transmit(sent),
584 opcode::SET_RX => {
585 let timeout = word(0);
586 self.mode = Sx126xMode::Rx {
587 continuous: timeout == SX126X_RX_CONTINUOUS,
588 timeout: timeout != 0 && timeout != SX126X_RX_CONTINUOUS,
589 };
590 }
591 opcode::SET_CAD => self.mode = Sx126xMode::Cad,
592 opcode::SET_PACKET_TYPE => {
593 if let Some(&packet_type) = params.first() {
594 self.packet_type = packet_type;
595 }
596 }
597 opcode::SET_RF_FREQUENCY => {
598 if let Some(bytes) = params.get(..4) {
599 self.frequency_word =
600 u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
601 }
602 }
603 opcode::SET_MODULATION_PARAMS => {
604 if let Some(bytes) = params.get(..4) {
605 self.modulation.copy_from_slice(bytes);
606 }
607 }
608 opcode::SET_PACKET_PARAMS => {
609 if let Some(bytes) = params.get(..6) {
610 self.packet.copy_from_slice(bytes);
611 }
612 }
613 opcode::SET_TX_PARAMS => {
614 if let Some(&power) = params.first() {
615 self.output_dbm = power as i8;
616 }
617 }
618 opcode::SET_DIO_IRQ_PARAMS => self.irq_mask = pair(0),
619 opcode::CLEAR_IRQ_STATUS => self.irq &= !pair(0),
620 opcode::SET_BUFFER_BASE_ADDRESS => {
621 if let [tx_base, rx_base, ..] = params {
622 self.tx_base = *tx_base;
623 self.rx_base = *rx_base;
624 }
625 }
626 opcode::WRITE_REGISTER => {
627 let address = pair(0);
628 for (offset, &value) in params.iter().skip(2).enumerate() {
629 let at = address.wrapping_add(offset as u16);
630 self.registers.insert(at, value);
631 }
632 }
633 opcode::WRITE_BUFFER => {
634 if let Some((&offset, bytes)) = params.split_first() {
635 for (index, &value) in bytes.iter().enumerate() {
636 let at = offset.wrapping_add(index as u8);
637 self.buffer[usize::from(at)] = value;
638 }
639 }
640 }
641 _ => {}
642 }
643 }
644
645 fn answer(
646 &mut self,
647 written: &[u8],
648 answer: &mut [u8],
649 air: &mut VecDeque<Arrival>,
650 random: &mut u32,
651 ) {
652 if self.mode == Sx126xMode::Sleep {
653 self.mode = Sx126xMode::StandbyRc;
654 }
655 answer.fill(0);
656 let Some(&code) = written.first() else {
657 return;
658 };
659 match code {
660 opcode::GET_STATUS => {
661 if let Some(first) = answer.first_mut() {
662 *first = self.status();
663 }
664 }
665 opcode::GET_IRQ_STATUS => {
666 self.settle(air);
667 fill(answer, &self.irq.to_be_bytes());
668 }
669 opcode::GET_RX_BUFFER_STATUS => fill(answer, &self.rx_buffer),
670 opcode::GET_PACKET_STATUS => fill(answer, &self.packet_status),
671 opcode::GET_PACKET_TYPE => fill(answer, &[self.packet_type]),
672 opcode::GET_RSSI_INST => {
673 let bandwidth_hz = self.tuning().link.bandwidth_hz();
674 fill(answer, &[half_decibels_below(noise_floor(bandwidth_hz))]);
675 }
676 opcode::READ_REGISTER => {
677 let address = written
678 .get(1..3)
679 .map_or(0, |bytes| u16::from_be_bytes([bytes[0], bytes[1]]));
680 for (offset, value) in answer.iter_mut().enumerate() {
681 let at = address.wrapping_add(offset as u16);
682 let random_number = sx126x_register::RANDOM_NUMBER;
683 *value = if (random_number..random_number + 4).contains(&at) {
684 noise_byte(random)
685 } else {
686 self.register(at)
687 };
688 }
689 }
690 opcode::READ_BUFFER => {
691 let offset = written.get(1).copied().unwrap_or(0);
692 for (index, value) in answer.iter_mut().enumerate() {
693 *value = self.buffer[usize::from(offset.wrapping_add(index as u8))];
694 }
695 }
696 _ => {}
697 }
698 }
699
700 fn settle(&mut self, air: &mut VecDeque<Arrival>) {
701 match self.mode {
702 Sx126xMode::Rx {
703 continuous,
704 timeout,
705 } => {
706 if let Some(arrival) = air.pop_front() {
707 self.receive(&arrival);
708 if !continuous {
709 self.mode = Sx126xMode::StandbyRc;
710 }
711 } else if timeout {
712 self.raise(Irq::TIMEOUT);
713 self.command_status = SX126X_STATUS_TIMEOUT;
714 self.mode = Sx126xMode::StandbyRc;
715 }
716 }
717 Sx126xMode::Cad => {
718 self.raise(Irq::CAD_DONE);
719 if !air.is_empty() {
720 self.raise(Irq::CAD_DETECTED);
721 }
722 self.mode = Sx126xMode::StandbyRc;
723 }
724 _ => {}
725 }
726 }
727
728 fn receive(&mut self, arrival: &Arrival) {
729 for (index, &value) in arrival.payload.iter().enumerate() {
730 let at = self.rx_base.wrapping_add(index as u8);
731 self.buffer[usize::from(at)] = value;
732 }
733 self.rx_buffer = [arrival.payload.len() as u8, self.rx_base];
734 self.packet_status = [
735 half_decibels_below(arrival.rssi_dbm),
736 quarter_decibels(arrival.snr_db),
737 half_decibels_below(signal_level(arrival)),
738 ];
739 self.raise(Irq::PREAMBLE_DETECTED | Irq::HEADER_VALID | Irq::RX_DONE);
740 if !arrival.intact {
741 self.raise(Irq::CRC_ERROR);
742 }
743 self.command_status = SX126X_STATUS_DATA_AVAILABLE;
744 }
745
746 fn transmit(&mut self, sent: &mut Vec<Sent>) {
747 let len = self.packet[3];
748 let payload = (0..len)
749 .map(|index| self.buffer[usize::from(self.tx_base.wrapping_add(index))])
750 .collect();
751 sent.push(Sent {
752 tuning: self.tuning(),
753 payload,
754 });
755 self.raise(Irq::TX_DONE);
756 self.command_status = SX126X_STATUS_TX_DONE;
757 self.mode = Sx126xMode::StandbyRc;
758 }
759}
760
761const SX127X_MODE_MASK: u8 = 0x07;
762const SX127X_MODE_STANDBY: u8 = 0x01;
763const SX127X_MODE_TX: u8 = 0x03;
764const SX127X_MODE_RX_CONTINUOUS: u8 = 0x05;
765const SX127X_MODE_RX_SINGLE: u8 = 0x06;
766const SX127X_MODE_CAD: u8 = 0x07;
767const SX127X_PAGED_FIRST: usize = 0x0D;
768const SX127X_PAGED_LAST: usize = 0x3F;
769const SX127X_IMAGE_CAL_START: u8 = 0x40;
770const SX127X_IMAGE_CAL_RUNNING: u8 = 0x20;
771
772#[derive(Debug)]
773struct Sx127xModel {
774 board: sx127x::Board,
775 lora: [u8; 128],
776 fsk: [u8; 128],
777 fifo: [u8; 256],
778}
779
780impl Sx127xModel {
781 fn new(board: sx127x::Board) -> Sx127xModel {
782 let mut lora = [0u8; 128];
783 let mut fsk = [0u8; 128];
784 for (address, value) in [
785 (sx127x_register::OP_MODE, 0x09),
786 (sx127x_register::FRF_MSB, 0x6C),
787 (sx127x_register::FRF_MID, 0x80),
788 (sx127x_register::FRF_LSB, 0x00),
789 (sx127x_register::PA_CONFIG, 0x4F),
790 (sx127x_register::PA_RAMP, 0x09),
791 (sx127x_register::OCP, 0x2B),
792 (sx127x_register::LNA, 0x20),
793 (sx127x_register::VERSION, sx127x_register::VERSION_SX1276),
794 (sx127x_register::TCXO, 0x09),
795 (sx127x_register::PA_DAC, sx127x::config::PA_DAC_DEFAULT),
796 ] {
797 lora[usize::from(address)] = value;
798 }
799 for (address, value) in [
800 (sx127x_register::FIFO_TX_BASE_ADDR, 0x80),
801 (sx127x_register::MODEM_CONFIG_1, 0x72),
802 (sx127x_register::MODEM_CONFIG_2, 0x70),
803 (sx127x_register::SYMB_TIMEOUT_LSB, 0x64),
804 (sx127x_register::PREAMBLE_LSB, 0x08),
805 (sx127x_register::PAYLOAD_LENGTH, 0x01),
806 (sx127x_register::MAX_PAYLOAD_LENGTH, 0xFF),
807 (sx127x_register::DETECT_OPTIMIZE, 0xC3),
808 (sx127x_register::INVERT_IQ, 0x27),
809 (sx127x_register::DETECTION_THRESHOLD, 0x0A),
810 (sx127x_register::SYNC_WORD, SyncWord::Private.to_byte()),
811 (sx127x_register::INVERT_IQ_2, 0x1D),
812 ] {
813 lora[usize::from(address)] = value;
814 }
815 fsk[usize::from(sx127x_register::IMAGE_CAL)] = 0x82;
816 Sx127xModel {
817 board,
818 lora,
819 fsk,
820 fifo: [0; 256],
821 }
822 }
823
824 fn lora_mode(&self) -> bool {
825 self.lora[usize::from(sx127x_register::OP_MODE)] & sx127x_register::LONG_RANGE_MODE != 0
826 }
827
828 fn slot(&mut self, address: u8) -> &mut u8 {
829 let address = usize::from(address & 0x7F);
830 if (SX127X_PAGED_FIRST..=SX127X_PAGED_LAST).contains(&address) && !self.lora_mode() {
831 &mut self.fsk[address]
832 } else {
833 &mut self.lora[address]
834 }
835 }
836
837 fn lora_register(&self, address: u8) -> u8 {
838 self.lora[usize::from(address & 0x7F)]
839 }
840
841 fn mode(&self) -> u8 {
842 self.lora_register(sx127x_register::OP_MODE) & SX127X_MODE_MASK
843 }
844
845 fn enter(&mut self, mode: u8) {
846 let op_mode = usize::from(sx127x_register::OP_MODE);
847 self.lora[op_mode] = (self.lora[op_mode] & !SX127X_MODE_MASK) | mode;
848 }
849
850 fn tuning(&self) -> Tuning {
851 let frf = u32::from_be_bytes([
852 0,
853 self.lora_register(sx127x_register::FRF_MSB),
854 self.lora_register(sx127x_register::FRF_MID),
855 self.lora_register(sx127x_register::FRF_LSB),
856 ]);
857 let modem_1 = self.lora_register(sx127x_register::MODEM_CONFIG_1);
858 let modem_2 = self.lora_register(sx127x_register::MODEM_CONFIG_2);
859 let bandwidth_hz = sx127x::config::LoraBandwidth::from_code(modem_1 >> 4)
860 .map_or(0, sx127x::config::LoraBandwidth::hz);
861 let preamble = u16::from_be_bytes([
862 self.lora_register(sx127x_register::PREAMBLE_MSB),
863 self.lora_register(sx127x_register::PREAMBLE_LSB),
864 ]);
865 let mut link = LinkSettings::new(modem_2 >> 4, bandwidth_hz)
866 .with_coding_rate(((modem_1 >> 1) & 0x07) + 4)
867 .with_preamble(preamble);
868 if modem_1 & 0x01 != 0 {
869 link = link.implicit_header();
870 }
871 if modem_2 & 0x04 == 0 {
872 link = link.without_crc();
873 }
874 let pa_config = self.lora_register(sx127x_register::PA_CONFIG);
875 let output_power = (pa_config & 0x0F) as i8;
876 let output_dbm = if pa_config & 0x80 != 0 {
877 let high_power = self.lora_register(sx127x_register::PA_DAC) & 0x07 == 0x07;
878 output_power + if high_power { 5 } else { 2 }
879 } else if (pa_config >> 4) & 0x07 == 0x07 {
880 output_power
881 } else {
882 output_power - 4
883 };
884 Tuning {
885 frequency_hz: sx127x::config::frequency_from_word(frf),
886 link,
887 output_dbm,
888 sync_word: SyncWord::from_byte(self.lora_register(sx127x_register::SYNC_WORD)),
889 }
890 }
891
892 fn raise(&mut self, flags: IrqFlags) {
893 let mask = self.lora_register(sx127x_register::IRQ_FLAGS_MASK);
894 self.lora[usize::from(sx127x_register::IRQ_FLAGS)] |= flags.bits() & !mask;
895 }
896
897 fn write(&mut self, written: &[u8], sent: &mut Vec<Sent>) {
898 let Some((&address, values)) = written.split_first() else {
899 return;
900 };
901 let address = address & !sx127x_register::WRITE;
902 for (offset, &value) in values.iter().enumerate() {
903 let at = if address == sx127x_register::FIFO {
904 address
905 } else {
906 address.wrapping_add(offset as u8) & 0x7F
907 };
908 self.write_register(at, value, sent);
909 }
910 }
911
912 fn write_register(&mut self, address: u8, value: u8, sent: &mut Vec<Sent>) {
913 match address {
914 sx127x_register::FIFO => {
915 let pointer = self.lora_register(sx127x_register::FIFO_ADDR_PTR);
916 self.fifo[usize::from(pointer)] = value;
917 self.lora[usize::from(sx127x_register::FIFO_ADDR_PTR)] = pointer.wrapping_add(1);
918 }
919 sx127x_register::OP_MODE => {
920 self.lora[usize::from(address)] = value;
921 if self.lora_mode() && value & SX127X_MODE_MASK == SX127X_MODE_TX {
922 self.transmit(sent);
923 }
924 }
925 sx127x_register::IRQ_FLAGS if self.lora_mode() => {
926 self.lora[usize::from(address)] &= !value;
927 }
928 sx127x_register::IMAGE_CAL if !self.lora_mode() => {
929 self.fsk[usize::from(address)] =
930 value & !(SX127X_IMAGE_CAL_START | SX127X_IMAGE_CAL_RUNNING);
931 }
932 sx127x_register::VERSION => {}
933 _ => *self.slot(address) = value,
934 }
935 }
936
937 fn read(
938 &mut self,
939 written: &[u8],
940 answer: &mut [u8],
941 air: &mut VecDeque<Arrival>,
942 random: &mut u32,
943 ) {
944 let address = written.first().copied().unwrap_or(0) & !sx127x_register::WRITE;
945 for (offset, value) in answer.iter_mut().enumerate() {
946 let at = if address == sx127x_register::FIFO {
947 address
948 } else {
949 address.wrapping_add(offset as u8) & 0x7F
950 };
951 *value = self.read_register(at, air, random);
952 }
953 }
954
955 fn read_register(&mut self, address: u8, air: &mut VecDeque<Arrival>, random: &mut u32) -> u8 {
956 if !self.lora_mode() {
957 return *self.slot(address);
958 }
959 match address {
960 sx127x_register::FIFO => {
961 let pointer = self.lora_register(sx127x_register::FIFO_ADDR_PTR);
962 self.lora[usize::from(sx127x_register::FIFO_ADDR_PTR)] = pointer.wrapping_add(1);
963 self.fifo[usize::from(pointer)]
964 }
965 sx127x_register::IRQ_FLAGS => {
966 self.settle(air);
967 self.lora_register(address)
968 }
969 sx127x_register::RSSI_WIDEBAND => noise_byte(random),
970 sx127x_register::RSSI_VALUE => {
971 let tuning = self.tuning();
972 let port = Port::for_frequency(tuning.frequency_hz);
973 let floor = noise_floor(tuning.link.bandwidth_hz());
974 let above = (floor.hundredths() + 50).div_euclid(100) - port.rssi_offset_dbm();
975 above.clamp(0, i32::from(u8::MAX)) as u8
976 }
977 _ => *self.slot(address),
978 }
979 }
980
981 fn settle(&mut self, air: &mut VecDeque<Arrival>) {
982 match self.mode() {
983 SX127X_MODE_RX_CONTINUOUS => {
984 if let Some(arrival) = air.pop_front() {
985 self.receive(&arrival);
986 }
987 }
988 SX127X_MODE_RX_SINGLE => {
989 if let Some(arrival) = air.pop_front() {
990 self.receive(&arrival);
991 } else {
992 self.raise(IrqFlags::RX_TIMEOUT);
993 }
994 self.enter(SX127X_MODE_STANDBY);
995 }
996 SX127X_MODE_CAD => {
997 self.raise(IrqFlags::CAD_DONE);
998 if !air.is_empty() {
999 self.raise(IrqFlags::CAD_DETECTED);
1000 }
1001 self.enter(SX127X_MODE_STANDBY);
1002 }
1003 _ => {}
1004 }
1005 }
1006
1007 fn receive(&mut self, arrival: &Arrival) {
1008 let base = self.lora_register(sx127x_register::FIFO_RX_BASE_ADDR);
1009 for (index, &value) in arrival.payload.iter().enumerate() {
1010 self.fifo[usize::from(base.wrapping_add(index as u8))] = value;
1011 }
1012 let port = Port::for_frequency(self.tuning().frequency_hz);
1013 let rssi = (arrival.rssi_dbm.hundredths() + 50).div_euclid(100) - port.rssi_offset_dbm();
1014 self.lora[usize::from(sx127x_register::FIFO_RX_CURRENT_ADDR)] = base;
1015 self.lora[usize::from(sx127x_register::RX_NB_BYTES)] = arrival.payload.len() as u8;
1016 self.lora[usize::from(sx127x_register::PKT_SNR_VALUE)] = quarter_decibels(arrival.snr_db);
1017 self.lora[usize::from(sx127x_register::PKT_RSSI_VALUE)] =
1018 rssi.clamp(0, i32::from(u8::MAX)) as u8;
1019 self.raise(IrqFlags::VALID_HEADER | IrqFlags::RX_DONE);
1020 if !arrival.intact {
1021 self.raise(IrqFlags::PAYLOAD_CRC_ERROR);
1022 }
1023 }
1024
1025 fn transmit(&mut self, sent: &mut Vec<Sent>) {
1026 let base = self.lora_register(sx127x_register::FIFO_TX_BASE_ADDR);
1027 let len = self.lora_register(sx127x_register::PAYLOAD_LENGTH);
1028 let payload = (0..len)
1029 .map(|index| self.fifo[usize::from(base.wrapping_add(index))])
1030 .collect();
1031 sent.push(Sent {
1032 tuning: self.tuning(),
1033 payload,
1034 });
1035 self.raise(IrqFlags::TX_DONE);
1036 self.enter(SX127X_MODE_STANDBY);
1037 }
1038}
1039
1040fn fill(answer: &mut [u8], bytes: &[u8]) {
1042 for (slot, &byte) in answer.iter_mut().zip(bytes) {
1043 *slot = byte;
1044 }
1045}
1046
1047#[cfg(test)]
1048mod tests {
1049 use super::*;
1050 use crate::radio::{RadioConfig, Reception};
1051 use crate::sx126x::config::PowerAmplifier;
1052 use crate::sx127x::config::PaOutput;
1053 use pamoja_lora::region::Region;
1054
1055 fn dr3() -> LinkSettings {
1056 Region::Eu868
1057 .plan()
1058 .link_settings(3)
1059 .expect("DR3 is a LoRa data rate")
1060 }
1061
1062 fn carrier(hz: u32, asked: u32) -> bool {
1063 hz.abs_diff(asked) <= 61
1064 }
1065
1066 fn chips() -> [Chip; 2] {
1067 [
1068 Chip::sx126x(sx126x::Board::new(PowerAmplifier::HighPower)),
1069 Chip::sx127x(sx127x::Board::new(PaOutput::PaBoost)),
1070 ]
1071 }
1072
1073 #[test]
1074 fn the_driver_tunes_each_chip_to_what_it_was_asked() {
1075 for chip in chips() {
1076 let mut radio = chip.radio();
1077 radio.init().expect("the chip answers");
1078 let config = RadioConfig::new(868_100_000, dr3(), 14).lorawan_device();
1079 radio.configure(config).expect("a link the chip carries");
1080 let tuning = chip.tuning();
1081 assert!(
1082 carrier(tuning.frequency_hz, 868_100_000),
1083 "{:?}",
1084 chip.family()
1085 );
1086 assert_eq!(tuning.link.spreading_factor(), 9);
1087 assert_eq!(tuning.link.bandwidth_hz(), 125_000);
1088 assert_eq!(tuning.link.coding_rate_denominator(), 5);
1089 assert_eq!(tuning.output_dbm, 14);
1090 assert_eq!(tuning.sync_word, SyncWord::Public);
1091 }
1092 }
1093
1094 #[test]
1095 fn a_transmission_puts_the_payload_on_the_air_as_tuned() {
1096 for chip in chips() {
1097 let mut radio = chip.radio();
1098 radio.init().expect("the chip answers");
1099 radio
1100 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1101 .expect("a link the chip carries");
1102 let airtime = radio.transmit(b"21.5").expect("the frame leaves");
1103 assert_eq!(airtime, dr3().airtime_us(4));
1104 let sent = chip.sent();
1105 assert_eq!(sent.len(), 1);
1106 assert_eq!(sent[0].payload, b"21.5");
1107 assert!(carrier(sent[0].tuning.frequency_hz, 868_100_000));
1108 assert_eq!(sent[0].tuning.link.preamble_symbols(), 8);
1109 }
1110 }
1111
1112 #[test]
1113 fn a_frame_on_the_air_arrives_with_the_levels_it_was_heard_at() {
1114 for chip in chips() {
1115 let mut radio = chip.radio();
1116 radio.init().expect("the chip answers");
1117 radio
1118 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1119 .expect("a link the chip carries");
1120 chip.hear(b"ack", Decibels::from_db(-109), Decibels::from_tenths(-25));
1121 let mut buffer = [0u8; 255];
1122 let reception = radio
1123 .receive(&mut buffer, 1_000_000)
1124 .expect("the chip answers");
1125 let Reception::Frame { len, levels } = reception else {
1126 panic!("a frame was waiting: {reception:?}");
1127 };
1128 assert_eq!(&buffer[..len], b"ack");
1129 assert_eq!(levels.rssi_dbm, Decibels::from_db(-109));
1130 assert_eq!(levels.snr_db, Decibels::from_tenths(-25));
1131 assert_eq!(levels.signal_rssi_dbm, Decibels::from_hundredths(-11_150));
1132 assert_eq!(chip.waiting(), 0);
1133 }
1134 }
1135
1136 #[test]
1137 fn nothing_on_the_air_times_a_reception_out_and_a_bad_crc_is_corrupt() {
1138 for chip in chips() {
1139 let mut radio = chip.radio();
1140 radio.init().expect("the chip answers");
1141 radio
1142 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1143 .expect("a link the chip carries");
1144 let mut buffer = [0u8; 255];
1145 assert_eq!(
1146 radio.receive(&mut buffer, 1_000_000),
1147 Ok(Reception::Timeout)
1148 );
1149 chip.hear_corrupt(Decibels::from_db(-120), Decibels::from_db(-10));
1150 assert_eq!(
1151 radio.receive(&mut buffer, 1_000_000),
1152 Ok(Reception::Corrupt)
1153 );
1154 }
1155 }
1156
1157 #[test]
1158 fn listening_takes_frame_after_frame_until_the_air_is_quiet() {
1159 for chip in chips() {
1160 let mut radio = chip.radio();
1161 radio.init().expect("the chip answers");
1162 radio
1163 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1164 .expect("a link the chip carries");
1165 radio.listen().expect("the chip listens");
1166 let mut buffer = [0u8; 255];
1167 assert_eq!(radio.take_frame(&mut buffer), Ok(None));
1168 chip.hear(b"one", Decibels::from_db(-100), Decibels::from_db(5));
1169 chip.hear(b"two", Decibels::from_db(-101), Decibels::from_db(4));
1170 for expected in [b"one", b"two"] {
1171 let Ok(Some(Reception::Frame { len, .. })) = radio.take_frame(&mut buffer) else {
1172 panic!("a frame was waiting");
1173 };
1174 assert_eq!(&buffer[..len], expected);
1175 }
1176 assert_eq!(radio.take_frame(&mut buffer), Ok(None));
1177 }
1178 }
1179
1180 #[test]
1181 fn activity_is_detected_only_while_a_frame_is_on_the_air() {
1182 for chip in chips() {
1183 let mut radio = chip.radio();
1184 radio.init().expect("the chip answers");
1185 radio
1186 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1187 .expect("a link the chip carries");
1188 assert_eq!(radio.detect(4), Ok(false));
1189 chip.hear(b"wake", Decibels::from_db(-110), Decibels::from_db(-5));
1190 assert_eq!(radio.detect(4), Ok(true));
1191 assert_eq!(
1192 chip.waiting(),
1193 1,
1194 "detection leaves the frame for the receiver"
1195 );
1196 }
1197 }
1198
1199 #[test]
1200 fn the_noise_gives_a_random_number_and_leaves_the_air_alone() {
1201 for chip in chips() {
1202 let mut radio = chip.radio();
1203 radio.init().expect("the chip answers");
1204 radio
1205 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1206 .expect("a link the chip carries");
1207 chip.hear(b"kept", Decibels::from_db(-100), Decibels::from_db(5));
1208 let first = radio.random().expect("noise");
1209 let second = radio.random().expect("noise");
1210 assert_ne!(first, second);
1211 assert_eq!(chip.waiting(), 1);
1212 }
1213 }
1214
1215 #[test]
1216 fn a_reset_forgets_the_tuning_and_keeps_what_was_sent() {
1217 for chip in chips() {
1218 let mut radio = chip.radio();
1219 radio.init().expect("the chip answers");
1220 radio
1221 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1222 .expect("a link the chip carries");
1223 radio.transmit(b"once").expect("the frame leaves");
1224 radio.init().expect("the chip answers again");
1225 assert_ne!(chip.tuning().frequency_hz, 868_100_000);
1226 assert_eq!(chip.sent().len(), 1);
1227 }
1228 }
1229
1230 #[test]
1231 fn a_warm_sleep_keeps_the_tuning_and_the_next_command_wakes_the_chip() {
1232 for chip in chips() {
1233 let mut radio = chip.radio();
1234 radio.init().expect("the chip answers");
1235 radio
1236 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1237 .expect("a link the chip carries");
1238 radio.sleep().expect("the chip sleeps");
1239 assert!(carrier(chip.tuning().frequency_hz, 868_100_000));
1240 radio
1241 .configure(RadioConfig::new(868_300_000, dr3(), 14))
1242 .expect("the chip wakes");
1243 assert!(carrier(chip.tuning().frequency_hz, 868_300_000));
1244 }
1245 }
1246
1247 #[test]
1248 fn a_cold_sleep_forgets_the_tuning() {
1249 let chip = Chip::sx126x(sx126x::Board::new(PowerAmplifier::HighPower));
1250 let Radio::Sx126x(mut radio) = chip.radio() else {
1251 panic!("an SX126x");
1252 };
1253 radio.init().expect("the chip answers");
1254 let power = radio.tx_power(14);
1255 let config = sx126x::RadioConfig::new(868_100_000, dr3(), power);
1256 radio.configure(config).expect("a link the chip carries");
1257 radio.sleep(false).expect("the chip sleeps");
1258 assert_eq!(chip.tuning().frequency_hz, 0);
1259 }
1260
1261 #[test]
1262 fn the_rfo_output_and_the_high_power_setting_report_their_own_power() {
1263 let rfo = Chip::sx127x(sx127x::Board::new(PaOutput::Rfo));
1264 let mut radio = rfo.radio();
1265 radio.init().expect("the chip answers");
1266 radio
1267 .configure(RadioConfig::new(
1268 433_175_000,
1269 LinkSettings::new(9, 125_000),
1270 10,
1271 ))
1272 .expect("a link the chip carries");
1273 assert_eq!(rfo.tuning().output_dbm, 10);
1274 assert!(carrier(rfo.tuning().frequency_hz, 433_175_000));
1275
1276 let boost = Chip::sx127x(sx127x::Board::new(PaOutput::PaBoost));
1277 let mut radio = boost.radio();
1278 radio.init().expect("the chip answers");
1279 radio
1280 .configure(RadioConfig::new(
1281 915_000_000,
1282 LinkSettings::new(7, 500_000),
1283 20,
1284 ))
1285 .expect("a link the chip carries");
1286 assert_eq!(boost.tuning().output_dbm, 20);
1287 assert_eq!(boost.tuning().link.bandwidth_hz(), 500_000);
1288 }
1289
1290 #[test]
1291 fn a_payload_longer_than_a_frame_is_cut_to_one() {
1292 let chip = Chip::sx126x(sx126x::Board::new(PowerAmplifier::HighPower));
1293 chip.hear(&[7; 300], Decibels::from_db(-100), Decibels::from_db(5));
1294 let mut radio = chip.radio();
1295 radio.init().expect("the chip answers");
1296 radio
1297 .configure(RadioConfig::new(868_100_000, dr3(), 14))
1298 .expect("a link the chip carries");
1299 let mut buffer = [0u8; 255];
1300 let Ok(Reception::Frame { len, .. }) = radio.receive(&mut buffer, 1_000_000) else {
1301 panic!("a frame was waiting");
1302 };
1303 assert_eq!(len, 255);
1304 }
1305}