pamoja_radios/sx1302/channel.rs
1//! Pointing a concentrator at the channels it listens on.
2//!
3//! A concentrator that has been reset, identified and given firmware still hears nothing. Its
4//! receivers have not been told which intermediate frequencies to listen on, which radio each
5//! takes its samples from, which spreading factors to look for, or that they may run at all.
6//! This is what closes that gap.
7//!
8//! There are ten receivers. Eight take any spreading factor and share a bandwidth fixed in
9//! hardware at 125 kHz. One more is fixed to a single spreading factor and bandwidth, which
10//! is what a network calls its service channel. The last hears frequency shift keying.
11//!
12//! Every frequency here is an offset from the carrier a front end is tuned to, not a carrier
13//! of its own, which is why a plan is described relative to the radio it sits on.
14//!
15//! Nothing here opens a bus. The order is data, so it can be checked against the reference
16//! with no hardware present.
17
18use super::register::{self, Bank, Register};
19use super::tx::{bandwidth_value, Chain};
20
21/// The bandwidth every multi-spreading-factor receiver runs at, in hertz.
22///
23/// It is fixed in hardware. Only the service receiver takes a bandwidth of its own.
24pub const MULTI_BANDWIDTH_HZ: u32 = 125_000;
25
26/// How much spectrum one radio hears in total, in hertz.
27///
28/// A concentrator tunes its radios once and every channel sits inside this window. It is a
29/// property of the radio rather than of the register that carries a channel offset.
30pub const RX_BANDWIDTH_HZ: u32 = 1_600_000;
31
32/// How far from the carrier a channel can sit, in hertz.
33///
34/// Half the radio's window, less half the channel's own width. The register that carries an
35/// offset is thirteen bits and reaches far past this, so a channel beyond it is accepted by
36/// the chip and then hears nothing at all.
37pub const MAX_OFFSET_HZ: i32 = (RX_BANDWIDTH_HZ as i32 - MULTI_BANDWIDTH_HZ as i32) / 2;
38
39/// The sync symbol positions a public network uses.
40const PUBLIC_PEAKS: (u8, u8) = (6, 8);
41
42/// The ones a private network uses, which is also what the chip powers up looking for.
43const PRIVATE_PEAKS: (u8, u8) = (2, 4);
44
45/// How many receivers take any spreading factor.
46pub const MULTI_CHANNELS: usize = 8;
47
48/// The lowest spreading factor a receiver can be asked for.
49pub const MIN_SPREADING_FACTOR: u8 = 5;
50
51/// The highest.
52pub const MAX_SPREADING_FACTOR: u8 = 12;
53
54/// How many preamble symbols a receiver waits for.
55///
56/// Ten covers both cases the chip has to serve at once: twelve at the two lowest spreading
57/// factors and eight above them.
58pub const PREAMBLE_SYMBOLS: u16 = 10;
59
60/// What the channel power reading starts from.
61pub const RSSI_DEFAULT: u8 = 85;
62
63/// How heavily that reading is filtered.
64pub const RSSI_FILTER_ALPHA: u8 = 0x05;
65
66/// Frequency tracking left to the receiver.
67pub const TRACKING_AUTOMATIC: u8 = 0x03;
68
69/// Frequency tracking switched off.
70pub const TRACKING_OFF: u8 = 0x00;
71
72/// Fine timing that follows the drift in a straight line.
73pub const TIMING_LINEAR: u8 = 0x02;
74
75/// Peak selection left to the receiver.
76pub const DFT_PEAK_AUTOMATIC: u8 = 0x03;
77
78/// How many preamble symbols the fixed-rate receiver expects at the usual spreading factors.
79pub const SERVICE_PREAMBLE_SYMBOLS: u16 = 8;
80
81/// How many it expects at the two fastest, which is what end device drivers send there.
82pub const SERVICE_PREAMBLE_SYMBOLS_FAST: u16 = 12;
83
84/// How far above the noise a peak has to be for the fixed-rate receiver to take it.
85pub const SERVICE_PEAK_TO_NOISE: u8 = 52;
86
87/// How many peaks the fixed-rate receiver counts before it believes a preamble.
88pub const SERVICE_PEAK_COUNT: u8 = 7;
89
90/// How many peaks it counts on the second pass.
91pub const SERVICE_SECOND_PEAK_COUNT: u8 = 5;
92
93/// The clock the FSK bit rate is counted against, in hertz.
94pub const FSK_CLOCK_HZ: u32 = 32_000_000;
95
96/// The bit rate a LoRaWAN network runs its frequency shift keying channel at.
97pub const FSK_DEFAULT_BITRATE: u32 = 50_000;
98
99/// The sync word that channel looks for.
100pub const FSK_SYNC_WORD: u64 = 0x00c1_94c1;
101
102/// How many bytes of it there are.
103pub const FSK_SYNC_WORD_LEN: u8 = 3;
104
105/// How wide the FSK receiver listens, which is the setting for a 125 kHz channel.
106pub const FSK_BANDWIDTH_CODE: u8 = 0x03;
107
108/// How many samples the FSK receiver measures a signal level over.
109pub const FSK_RSSI_SAMPLES: u8 = 4;
110
111/// Keeping the bits from settling at one level, which a LoRaWAN FSK channel whitens.
112pub const FSK_WHITENING: u8 = 2;
113
114/// How far the bit timing may drift before a packet is given up on.
115pub const FSK_TOLERANCE: u8 = 10;
116
117/// The longest packet the FSK receiver takes.
118pub const FSK_MAX_PAYLOAD: u8 = 255;
119
120/// How long it waits for one, as the receiver counts it.
121pub const FSK_TIMEOUT: u8 = 128;
122
123/// One of the receivers that takes any spreading factor.
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub struct Listener {
126 /// How far from the carrier it listens, in hertz, which is signed.
127 pub offset_hz: i32,
128 /// Which radio it takes its samples from.
129 pub chain: Chain,
130 /// Whether it runs at all.
131 pub enabled: bool,
132}
133
134impl Listener {
135 /// A receiver on the first radio, listening this far from the carrier.
136 ///
137 /// # Arguments
138 ///
139 /// * `offset_hz` - how far from the carrier, which is signed.
140 ///
141 /// # Returns
142 ///
143 /// The receiver, enabled.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use pamoja_radios::sx1302::channel::Listener;
149 /// use pamoja_radios::sx1302::tx::Chain;
150 ///
151 /// let channel = Listener::at(-400_000);
152 /// assert_eq!(channel.chain, Chain::A);
153 /// assert!(channel.enabled);
154 /// ```
155 #[must_use]
156 pub const fn at(offset_hz: i32) -> Listener {
157 Listener {
158 offset_hz,
159 chain: Chain::A,
160 enabled: true,
161 }
162 }
163
164 /// The same receiver, on the other radio.
165 ///
166 /// # Arguments
167 ///
168 /// * `chain` - the radio it takes its samples from.
169 ///
170 /// # Returns
171 ///
172 /// The receiver.
173 #[must_use]
174 pub const fn on(mut self, chain: Chain) -> Listener {
175 self.chain = chain;
176 self
177 }
178
179 /// The same receiver, switched off.
180 ///
181 /// # Returns
182 ///
183 /// The receiver, which will neither be clocked nor correlated.
184 #[must_use]
185 pub const fn off(mut self) -> Listener {
186 self.enabled = false;
187 self
188 }
189}
190
191impl Default for Listener {
192 fn default() -> Listener {
193 Listener::at(0).off()
194 }
195}
196
197/// What a receiver is told when the packets it hears carry no header of their own.
198///
199/// A header carries the length, the coding rate, and whether a checksum follows. Without one
200/// the receiver has to be told all three up front, and it hears nothing from a sender that
201/// disagrees with any of them.
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub struct Implicit {
204 /// How long every packet is.
205 pub payload_length: u8,
206 /// The coding rate, as the denominator less four, so 4/5 is 1 and 4/8 is 4.
207 pub coding_rate: u8,
208 /// Whether a checksum follows the payload.
209 pub crc: bool,
210}
211
212/// The receiver fixed to one spreading factor, which is what a network calls its service
213/// channel.
214///
215/// The eight receivers beside it take any spreading factor and share a bandwidth fixed in
216/// hardware at 125 kHz. This one takes exactly one spreading factor and a bandwidth of its
217/// own, which is what lets a network carry a faster rate on a channel of its own.
218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
219pub struct Service {
220 /// How far from the carrier it listens, in hertz, which is signed.
221 pub offset_hz: i32,
222 /// Which radio it takes its samples from.
223 pub chain: Chain,
224 /// The one spreading factor it looks for.
225 pub spreading_factor: u8,
226 /// The bandwidth it runs at, in hertz. It is the only receiver that takes its own.
227 pub bandwidth_hz: u32,
228 /// What it assumes when a packet carries no header, or `None` when packets carry one.
229 pub implicit: Option<Implicit>,
230}
231
232impl Service {
233 /// A service channel on the first radio, at 125 kHz, hearing packets that carry a header.
234 ///
235 /// # Arguments
236 ///
237 /// * `offset_hz` - how far from the carrier it listens, which is signed.
238 /// * `spreading_factor` - the one spreading factor it looks for.
239 ///
240 /// # Returns
241 ///
242 /// The service channel.
243 ///
244 /// # Examples
245 ///
246 /// ```
247 /// use pamoja_radios::sx1302::channel::Service;
248 /// use pamoja_radios::sx1302::tx::Chain;
249 ///
250 /// // The channel a European network runs at its fast rate: SF7 on 250 kHz.
251 /// let service = Service::at(300_000, 7).bandwidth(250_000);
252 /// assert_eq!(service.chain, Chain::A);
253 /// assert!(service.implicit.is_none(), "packets carry their own header");
254 /// ```
255 #[must_use]
256 pub const fn at(offset_hz: i32, spreading_factor: u8) -> Service {
257 Service {
258 offset_hz,
259 chain: Chain::A,
260 spreading_factor,
261 bandwidth_hz: MULTI_BANDWIDTH_HZ,
262 implicit: None,
263 }
264 }
265
266 /// The same channel, on the other radio.
267 ///
268 /// # Arguments
269 ///
270 /// * `chain` - the radio it takes its samples from.
271 ///
272 /// # Returns
273 ///
274 /// The service channel.
275 #[must_use]
276 pub const fn on(mut self, chain: Chain) -> Service {
277 self.chain = chain;
278 self
279 }
280
281 /// The same channel, at a bandwidth of its own.
282 ///
283 /// # Arguments
284 ///
285 /// * `bandwidth_hz` - 125000, 250000, or 500000. Anything else leaves a plan a
286 /// concentrator refuses rather than one it quietly narrows.
287 ///
288 /// # Returns
289 ///
290 /// The service channel.
291 #[must_use]
292 pub const fn bandwidth(mut self, bandwidth_hz: u32) -> Service {
293 self.bandwidth_hz = bandwidth_hz;
294 self
295 }
296
297 /// The same channel, hearing packets that carry no header.
298 ///
299 /// # Arguments
300 ///
301 /// * `implicit` - what every packet is, since nothing on the air will say.
302 ///
303 /// # Returns
304 ///
305 /// The service channel.
306 #[must_use]
307 pub const fn without_header(mut self, implicit: Implicit) -> Service {
308 self.implicit = Some(implicit);
309 self
310 }
311
312 /// Whether the concentrator can be set to this channel at all.
313 ///
314 /// # Returns
315 ///
316 /// Whether the spreading factor and the bandwidth are ones the chip takes, and the
317 /// offset is one the radio hears.
318 ///
319 /// # Examples
320 ///
321 /// ```
322 /// use pamoja_radios::sx1302::channel::Service;
323 ///
324 /// assert!(Service::at(300_000, 7).bandwidth(250_000).supported());
325 /// assert!(!Service::at(300_000, 7).bandwidth(200_000).supported(), "not a bandwidth");
326 /// assert!(!Service::at(300_000, 13).supported(), "not a spreading factor");
327 /// assert!(!Service::at(900_000, 7).supported(), "past what the radio hears");
328 /// ```
329 #[must_use]
330 pub const fn supported(&self) -> bool {
331 reachable(self.offset_hz)
332 && bandwidth_value(self.bandwidth_hz).is_some()
333 && self.spreading_factor >= MIN_SPREADING_FACTOR
334 && self.spreading_factor <= MAX_SPREADING_FACTOR
335 }
336}
337
338/// The receiver that hears frequency shift keying rather than LoRa.
339///
340/// A LoRaWAN network carries one such channel, faster than any of its LoRa rates and with
341/// none of their reach. It is the tenth receiver, beside the eight and the fixed-rate one,
342/// and like them it is switched on whatever a plan says.
343#[derive(Clone, Copy, Debug, PartialEq, Eq)]
344pub struct Fsk {
345 /// How far from the carrier it listens, in hertz, which is signed.
346 pub offset_hz: i32,
347 /// Which radio it takes its samples from.
348 pub chain: Chain,
349 /// The bit rate, in bits per second.
350 pub bitrate: u32,
351 /// The sync word it matches, in its low bytes.
352 pub sync_word: u64,
353 /// How many bytes of that sync word there are.
354 pub sync_word_len: u8,
355}
356
357impl Fsk {
358 /// A channel on the first radio, at the rate and sync word a LoRaWAN network uses.
359 ///
360 /// # Arguments
361 ///
362 /// * `offset_hz` - how far from the carrier it listens, which is signed.
363 ///
364 /// # Returns
365 ///
366 /// The channel.
367 ///
368 /// # Examples
369 ///
370 /// ```
371 /// use pamoja_radios::sx1302::channel::{Fsk, FSK_DEFAULT_BITRATE};
372 ///
373 /// let channel = Fsk::at(600_000);
374 /// assert_eq!(channel.bitrate, FSK_DEFAULT_BITRATE);
375 /// assert_eq!(channel.sync_word_len, 3);
376 /// ```
377 #[must_use]
378 pub const fn at(offset_hz: i32) -> Fsk {
379 Fsk {
380 offset_hz,
381 chain: Chain::A,
382 bitrate: FSK_DEFAULT_BITRATE,
383 sync_word: FSK_SYNC_WORD,
384 sync_word_len: FSK_SYNC_WORD_LEN,
385 }
386 }
387
388 /// The same channel, on the other radio.
389 ///
390 /// A European network puts this channel at 868.8 MHz, which is further from the carrier
391 /// than one radio hears, so it usually belongs on the second one.
392 ///
393 /// # Arguments
394 ///
395 /// * `chain` - the radio it takes its samples from.
396 ///
397 /// # Returns
398 ///
399 /// The channel.
400 #[must_use]
401 pub const fn on(mut self, chain: Chain) -> Fsk {
402 self.chain = chain;
403 self
404 }
405
406 /// The same channel, at another bit rate.
407 ///
408 /// # Arguments
409 ///
410 /// * `bitrate` - the rate, in bits per second.
411 ///
412 /// # Returns
413 ///
414 /// The channel.
415 #[must_use]
416 pub const fn bitrate(mut self, bitrate: u32) -> Fsk {
417 self.bitrate = bitrate;
418 self
419 }
420
421 /// The same channel, matching another sync word.
422 ///
423 /// # Arguments
424 ///
425 /// * `sync_word` - the word, in the low bytes.
426 /// * `len` - how many bytes of it, up to eight.
427 ///
428 /// # Returns
429 ///
430 /// The channel.
431 #[must_use]
432 pub const fn matching(mut self, sync_word: u64, len: u8) -> Fsk {
433 self.sync_word = sync_word;
434 self.sync_word_len = len;
435 self
436 }
437
438 /// The pattern the receiver is programmed with.
439 ///
440 /// # Returns
441 ///
442 /// The sync word moved into the top of the pattern, which is where the receiver reads it
443 /// from however few bytes it has.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use pamoja_radios::sx1302::channel::Fsk;
449 ///
450 /// // Three bytes sit at the top, not the bottom.
451 /// assert_eq!(Fsk::at(0).pattern(), 0xc194_c100_0000_0000);
452 /// ```
453 #[must_use]
454 pub const fn pattern(&self) -> u64 {
455 let len = if self.sync_word_len > 8 {
456 8
457 } else {
458 self.sync_word_len
459 };
460 self.sync_word << (8 * (8 - len) as u32)
461 }
462
463 /// Whether the concentrator can be set to this channel at all.
464 ///
465 /// # Returns
466 ///
467 /// Whether the offset is one the radio hears, the sync word is between one and eight
468 /// bytes, and the bit rate is one the counter can hold.
469 ///
470 /// # Examples
471 ///
472 /// ```
473 /// use pamoja_radios::sx1302::channel::Fsk;
474 ///
475 /// assert!(Fsk::at(600_000).supported());
476 /// assert!(!Fsk::at(900_000).supported(), "past what the radio hears");
477 /// assert!(!Fsk::at(0).bitrate(100).supported(), "too slow to count");
478 /// assert!(!Fsk::at(0).matching(0, 9).supported(), "longer than the pattern");
479 /// ```
480 #[must_use]
481 pub const fn supported(&self) -> bool {
482 reachable(self.offset_hz)
483 && self.sync_word_len >= 1
484 && self.sync_word_len <= 8
485 && self.bitrate > 0
486 && FSK_CLOCK_HZ / self.bitrate <= u16::MAX as u32
487 }
488}
489
490/// What a concentrator is to listen for.
491#[derive(Clone, Copy, Debug, PartialEq, Eq)]
492pub struct Plan {
493 /// The carrier the first radio is tuned to, in hertz, which every offset is measured
494 /// from.
495 pub carrier_hz: u32,
496 /// The eight receivers that take any spreading factor.
497 pub channels: [Listener; MULTI_CHANNELS],
498 /// Which spreading factors to look for, one bit each counting from
499 /// [`MIN_SPREADING_FACTOR`].
500 pub spreading_factors: u8,
501 /// Whether the network is a public one, which decides the sync word the receivers look
502 /// for. LoRaWAN networks are public; the chip powers up expecting a private one.
503 pub public: bool,
504 /// The receiver fixed to one spreading factor, or `None` to leave it where reset left it.
505 pub service: Option<Service>,
506 /// The receiver that hears frequency shift keying, or `None` to leave it where reset
507 /// left it.
508 pub fsk: Option<Fsk>,
509}
510
511impl Plan {
512 /// A plan on one carrier, listening on these offsets.
513 ///
514 /// # Arguments
515 ///
516 /// * `carrier_hz` - what the radio is tuned to.
517 /// * `offsets_hz` - how far from it each receiver listens. Anything past the eighth is
518 /// ignored, and any receiver left over is switched off.
519 ///
520 /// # Returns
521 ///
522 /// The plan, looking for every spreading factor.
523 ///
524 /// # Examples
525 ///
526 /// ```
527 /// use pamoja_radios::sx1302::channel::Plan;
528 ///
529 /// // The eight channels a European gateway usually runs, 867.1 MHz through 868.5 MHz.
530 /// // The carrier sits in the middle of them, because a radio only hears so far either
531 /// // side of what it is tuned to.
532 /// let plan = Plan::new(
533 /// 867_800_000,
534 /// &[-700_000, -500_000, -300_000, -100_000, 100_000, 300_000, 500_000, 700_000],
535 /// );
536 /// assert!(plan.channels[0].enabled);
537 /// assert_eq!(plan.channels[4].offset_hz, 100_000);
538 /// ```
539 #[must_use]
540 pub fn new(carrier_hz: u32, offsets_hz: &[i32]) -> Plan {
541 let mut channels = [Listener::default(); MULTI_CHANNELS];
542 let mut at = 0;
543 while at < MULTI_CHANNELS && at < offsets_hz.len() {
544 channels[at] = Listener::at(offsets_hz[at]);
545 at += 1;
546 }
547 Plan {
548 carrier_hz,
549 channels,
550 spreading_factors: every_spreading_factor(),
551 public: true,
552 service: None,
553 fsk: None,
554 }
555 }
556
557 /// The same plan, on a public network or a private one.
558 ///
559 /// # Arguments
560 ///
561 /// * `public` - whether the network is public. Every LoRaWAN network is.
562 ///
563 /// # Returns
564 ///
565 /// The plan.
566 ///
567 /// # Examples
568 ///
569 /// ```
570 /// use pamoja_radios::sx1302::channel::Plan;
571 ///
572 /// // A plan listens to a public network unless it is told otherwise.
573 /// assert!(Plan::new(867_800_000, &[0]).public);
574 /// assert!(!Plan::new(867_800_000, &[0]).network(false).public);
575 /// ```
576 #[must_use]
577 pub const fn network(mut self, public: bool) -> Plan {
578 self.public = public;
579 self
580 }
581
582 /// The same plan, with the fixed-rate receiver pointed at a channel.
583 ///
584 /// That receiver is switched on whatever a plan says, so one never given a channel
585 /// listens wherever reset left it. This is what gives it somewhere to listen.
586 ///
587 /// # Arguments
588 ///
589 /// * `service` - the channel it runs on.
590 ///
591 /// # Returns
592 ///
593 /// The plan.
594 ///
595 /// # Examples
596 ///
597 /// ```
598 /// use pamoja_radios::sx1302::channel::{Plan, Service};
599 ///
600 /// let plan = Plan::new(867_800_000, &[-300_000])
601 /// .serving(Service::at(500_000, 7).bandwidth(250_000));
602 /// assert_eq!(plan.service.map(|service| service.spreading_factor), Some(7));
603 /// ```
604 #[must_use]
605 pub const fn serving(mut self, service: Service) -> Plan {
606 self.service = Some(service);
607 self
608 }
609
610 /// The same plan, with the frequency shift keying receiver pointed at a channel.
611 ///
612 /// That receiver is switched on whatever a plan says, so one never given a channel
613 /// listens wherever reset left it, as the fixed-rate one does.
614 ///
615 /// # Arguments
616 ///
617 /// * `fsk` - the channel it runs on.
618 ///
619 /// # Returns
620 ///
621 /// The plan.
622 ///
623 /// # Examples
624 ///
625 /// ```
626 /// use pamoja_radios::sx1302::channel::{Fsk, Plan};
627 /// use pamoja_radios::sx1302::tx::Chain;
628 ///
629 /// let plan = Plan::new(867_800_000, &[-300_000]).hearing(Fsk::at(600_000).on(Chain::B));
630 /// assert_eq!(plan.fsk.map(|fsk| fsk.bitrate), Some(50_000));
631 /// ```
632 #[must_use]
633 pub const fn hearing(mut self, fsk: Fsk) -> Plan {
634 self.fsk = Some(fsk);
635 self
636 }
637
638 /// The same plan, looking only for these spreading factors.
639 ///
640 /// # Arguments
641 ///
642 /// * `wanted` - the spreading factors to look for. Anything outside 5 to 12 is ignored.
643 ///
644 /// # Returns
645 ///
646 /// The plan.
647 ///
648 /// # Examples
649 ///
650 /// ```
651 /// use pamoja_radios::sx1302::channel::Plan;
652 ///
653 /// // A network that only runs the faster half of the range.
654 /// let plan = Plan::new(867_500_000, &[-400_000]).looking_for(&[7, 8, 9]);
655 /// assert_eq!(plan.spreading_factors, 0b0001_1100);
656 /// ```
657 #[must_use]
658 pub fn looking_for(mut self, wanted: &[u8]) -> Plan {
659 let mut mask = 0u8;
660 let mut at = 0;
661 while at < wanted.len() {
662 let spreading_factor = wanted[at];
663 if (MIN_SPREADING_FACTOR..=MAX_SPREADING_FACTOR).contains(&spreading_factor) {
664 mask |= 1 << (spreading_factor - MIN_SPREADING_FACTOR);
665 }
666 at += 1;
667 }
668 self.spreading_factors = mask;
669 self
670 }
671
672 /// Which radio each receiver takes its samples from, one bit per receiver.
673 ///
674 /// # Returns
675 ///
676 /// The mask the chip takes, with a bit set for every receiver on the second radio.
677 ///
678 /// # Examples
679 ///
680 /// ```
681 /// use pamoja_radios::sx1302::channel::Plan;
682 /// use pamoja_radios::sx1302::tx::Chain;
683 ///
684 /// let mut plan = Plan::new(867_500_000, &[0, 0]);
685 /// plan.channels[1] = plan.channels[1].on(Chain::B);
686 /// assert_eq!(plan.radios(), 0b0000_0010);
687 /// ```
688 #[must_use]
689 pub const fn radios(&self) -> u8 {
690 let mut mask = 0u8;
691 let mut at = 0;
692 while at < MULTI_CHANNELS {
693 if matches!(self.channels[at].chain, Chain::B) {
694 mask |= 1 << at;
695 }
696 at += 1;
697 }
698 mask
699 }
700
701 /// Which receivers run, one bit each.
702 ///
703 /// # Returns
704 ///
705 /// The mask the chip takes.
706 ///
707 /// # Examples
708 ///
709 /// ```
710 /// use pamoja_radios::sx1302::channel::Plan;
711 ///
712 /// // Three offsets given, so three receivers run and five do not.
713 /// let plan = Plan::new(867_500_000, &[-400_000, -200_000, 0]);
714 /// assert_eq!(plan.running(), 0b0000_0111);
715 /// ```
716 #[must_use]
717 pub const fn running(&self) -> u8 {
718 let mut mask = 0u8;
719 let mut at = 0;
720 while at < MULTI_CHANNELS {
721 if self.channels[at].enabled {
722 mask |= 1 << at;
723 }
724 at += 1;
725 }
726 mask
727 }
728}
729
730/// Every spreading factor, as the mask the chip takes.
731///
732/// # Returns
733///
734/// A bit for each of the eight, counting from [`MIN_SPREADING_FACTOR`].
735///
736/// # Examples
737///
738/// ```
739/// use pamoja_radios::sx1302::channel::every_spreading_factor;
740///
741/// assert_eq!(every_spreading_factor(), 0xff);
742/// ```
743#[must_use]
744pub const fn every_spreading_factor() -> u8 {
745 0xff
746}
747
748/// Turns an offset from the carrier into the value a channel register takes.
749///
750/// The chip counts in steps of its own, not in hertz: the register is the offset times 32
751/// over 15625, which divides exactly. A negative offset is carried as the low thirteen bits,
752/// which is what the two registers hold between them.
753///
754/// # Arguments
755///
756/// * `offset_hz` - how far from the carrier, which is signed.
757///
758/// # Returns
759///
760/// The value, of which the high five bits and the low eight are written separately.
761///
762/// # Examples
763///
764/// ```
765/// use pamoja_radios::sx1302::channel::offset_value;
766///
767/// // A channel 400 kHz below the carrier.
768/// assert_eq!(offset_value(-400_000), (-819i16) as u16 & 0x1fff);
769///
770/// // And one on it.
771/// assert_eq!(offset_value(0), 0);
772/// ```
773#[must_use]
774pub const fn offset_value(offset_hz: i32) -> u16 {
775 let steps = (offset_hz as i64 * 32 / 15_625) as i32;
776 (steps as u16) & 0x1fff
777}
778
779/// Splits a channel offset into the two bytes its registers take.
780///
781/// # Arguments
782///
783/// * `offset_hz` - how far from the carrier.
784///
785/// # Returns
786///
787/// The high bits and then the low bits.
788///
789/// # Examples
790///
791/// ```
792/// use pamoja_radios::sx1302::channel::offset_bytes;
793///
794/// let [high, low] = offset_bytes(-400_000);
795/// assert_eq!(high, 0x1c);
796/// assert_eq!(low, 0xcd);
797/// ```
798#[must_use]
799pub const fn offset_bytes(offset_hz: i32) -> [u8; 2] {
800 let value = offset_value(offset_hz);
801 [((value >> 8) & 0x1f) as u8, (value & 0xff) as u8]
802}
803
804/// How fast a frequency error turns into a timing one, as a mantissa and a shift.
805///
806/// The receiver corrects timing from the frequency error it measures, and how much it should
807/// correct depends on where the carrier sits: the same error is a larger fraction of a lower
808/// carrier. The chip takes that as a mantissa of at least 2048 and the shift that got it
809/// there.
810///
811/// # Arguments
812///
813/// * `bandwidth_hz` - the bandwidth the receiver runs at.
814/// * `carrier_hz` - what the radio is tuned to.
815///
816/// # Returns
817///
818/// The mantissa and the shift, or `None` for a carrier of zero.
819///
820/// # Examples
821///
822/// ```
823/// use pamoja_radios::sx1302::channel::drift;
824///
825/// // At 125 kHz on 868.1 MHz the ratio starts at 150 and doubles four times to reach 2400.
826/// assert_eq!(drift(125_000, 868_100_000), Some((2400, 4)));
827/// ```
828#[must_use]
829pub const fn drift(bandwidth_hz: u32, carrier_hz: u32) -> Option<(u16, u8)> {
830 if carrier_hz == 0 {
831 return None;
832 }
833 let mut mantissa = (bandwidth_hz as u64) * (1 << 20) / (carrier_hz as u64);
834 let mut shift = 0u8;
835 while mantissa < 2048 {
836 mantissa <<= 1;
837 shift += 1;
838 }
839 Some((mantissa as u16, shift))
840}
841
842/// A step in pointing a concentrator at its channels.
843///
844/// The order is carried as data, so a driver walks it against a bus and a test walks it
845/// against nothing.
846#[derive(Clone, Copy, Debug, PartialEq, Eq)]
847pub enum Step {
848 /// Write a value into one register.
849 Write(Register, u8),
850}
851
852/// The order a concentrator is pointed at its channels in.
853///
854/// The receivers are given their frequencies and radios, then the correlators are tuned and
855/// told which spreading factors to look for, then the demodulators are set up, and only at
856/// the end is anything switched on. Enabling earlier would start receivers that have not been
857/// told where to listen.
858///
859/// # Arguments
860///
861/// * `plan` - what to listen for.
862///
863/// # Returns
864///
865/// The steps, in the order the chip takes them.
866///
867/// # Examples
868///
869/// ```
870/// use pamoja_radios::sx1302::channel::{steps, Plan, Step};
871/// use pamoja_radios::sx1302::register;
872///
873/// let plan = Plan::new(867_500_000, &[-400_000, -200_000, 0]);
874/// let order: Vec<Step> = steps(&plan).collect();
875///
876/// // Nothing is switched on until the end.
877/// let enable = order
878/// .iter()
879/// .position(|step| matches!(step, Step::Write(register, _) if *register == register::COMMON_GLOBAL_ENABLE))
880/// .expect("the receivers are switched on");
881/// assert_eq!(enable, order.len() - 1);
882/// ```
883pub fn steps(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
884 channelizer(plan)
885 .chain(correlators(plan))
886 .chain(demodulators(plan))
887 .chain(syncword(plan))
888 .chain(service(plan))
889 .chain(fsk(plan))
890 .chain(enables())
891}
892
893/// Telling the receivers which network they are listening to.
894///
895/// A LoRa preamble ends in two symbols whose positions say which network a frame belongs to,
896/// and the chip looks for them where it is told. It powers up looking for a private network,
897/// so receivers that are never given this hear nothing from a public one however well
898/// everything else is configured.
899///
900/// The two lowest spreading factors have no public form and always take the private
901/// positions, which is what the reference does.
902fn syncword(plan: &Plan) -> impl Iterator<Item = Step> {
903 let (first, second) = if plan.public {
904 PUBLIC_PEAKS
905 } else {
906 PRIVATE_PEAKS
907 };
908 let (low_first, low_second) = PRIVATE_PEAKS;
909
910 // The fixed-rate receiver follows the network, except at the two fastest spreading
911 // factors, which have no public form however public the network is.
912 let (service_first, service_second) = match plan.service {
913 Some(service) if matches!(service.spreading_factor, 5 | 6) => PRIVATE_PEAKS,
914 _ => (first, second),
915 };
916
917 [
918 Step::Write(register::RX_SYNC_SF5_PEAK1, low_first),
919 Step::Write(register::RX_SYNC_SF5_PEAK2, low_second),
920 Step::Write(register::RX_SYNC_SF6_PEAK1, low_first),
921 Step::Write(register::RX_SYNC_SF6_PEAK2, low_second),
922 Step::Write(register::RX_SYNC_SF7_TO_SF12_PEAK1, first),
923 Step::Write(register::RX_SYNC_SF7_TO_SF12_PEAK2, second),
924 Step::Write(register::SERVICE_SYNC_PEAK1, service_first),
925 Step::Write(register::SERVICE_SYNC_PEAK2, service_second),
926 ]
927 .into_iter()
928}
929
930/// Whether a rate is slow enough to need the low data rate optimization.
931///
932/// A symbol that lasts long enough drifts against the receiver clock before it ends, and the
933/// two ends have to agree to carry fewer bits in it. Which rates those are depends on the
934/// bandwidth as well as the spreading factor.
935///
936/// # Arguments
937///
938/// * `spreading_factor` - the spreading factor.
939/// * `bandwidth_hz` - the bandwidth, in hertz.
940///
941/// # Returns
942///
943/// Whether the optimization is on for that pair.
944///
945/// # Examples
946///
947/// ```
948/// use pamoja_radios::sx1302::channel::low_rate_optimize;
949///
950/// assert!(low_rate_optimize(11, 125_000));
951/// assert!(low_rate_optimize(12, 125_000));
952/// assert!(!low_rate_optimize(10, 125_000));
953///
954/// // The same spreading factor twice as wide halves the symbol, so it is only on at SF12.
955/// assert!(!low_rate_optimize(11, 250_000));
956/// assert!(low_rate_optimize(12, 250_000));
957/// assert!(!low_rate_optimize(12, 500_000));
958/// ```
959#[must_use]
960pub const fn low_rate_optimize(spreading_factor: u8, bandwidth_hz: u32) -> bool {
961 match bandwidth_hz {
962 125_000 => matches!(spreading_factor, 11 | 12),
963 250_000 => spreading_factor == 12,
964 _ => false,
965 }
966}
967
968/// Whether a channel offset is one the radio can actually hear.
969///
970/// # Arguments
971///
972/// * `offset_hz` - how far from the carrier the channel sits.
973///
974/// # Returns
975///
976/// Whether it falls inside the radio's window.
977///
978/// # Examples
979///
980/// ```
981/// use pamoja_radios::sx1302::channel::{reachable, MAX_OFFSET_HZ};
982///
983/// assert_eq!(MAX_OFFSET_HZ, 737_500);
984/// assert!(reachable(700_000));
985///
986/// // The register carries this happily. The radio does not hear it.
987/// assert!(!reachable(1_000_000));
988/// ```
989#[must_use]
990pub const fn reachable(offset_hz: i32) -> bool {
991 offset_hz >= -MAX_OFFSET_HZ && offset_hz <= MAX_OFFSET_HZ
992}
993
994/// Giving each receiver its frequency, its radio, and the gain it reads power against.
995fn channelizer(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
996 let frequencies = (0..MULTI_CHANNELS).flat_map(move |at| {
997 let [high, low] = offset_bytes(plan.channels[at].offset_hz);
998 let [msb, lsb] = register::channel_frequency(at as u8);
999 [Step::Write(msb, high), Step::Write(lsb, low)]
1000 });
1001
1002 [
1003 Step::Write(register::RX_RADIO_SELECT, plan.radios()),
1004 Step::Write(register::RX_RSSI_FILTER_ALPHA, RSSI_FILTER_ALPHA),
1005 Step::Write(register::RX_RSSI_DEFAULT, RSSI_DEFAULT),
1006 Step::Write(register::RX_GAIN_AUTOMATIC, 0x01),
1007 Step::Write(register::RX_GAIN_THRESHOLD_HIGH, 0xff),
1008 Step::Write(register::RX_GAIN_THRESHOLD_LOW, 0x00),
1009 Step::Write(register::RX_GAIN_MAX_ATTENUATION, 15),
1010 Step::Write(register::RX_GAIN_MIN_ATTENUATION, 0),
1011 ]
1012 .into_iter()
1013 .chain(frequencies)
1014}
1015
1016/// Tuning detection for every spreading factor, then saying which to look for.
1017fn correlators(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
1018 let tuned = (MIN_SPREADING_FACTOR..=MAX_SPREADING_FACTOR).flat_map(|spreading_factor| {
1019 let correlator = register::correlator(spreading_factor);
1020 [
1021 Step::Write(correlator.accumulated_ratio, 52),
1022 Step::Write(correlator.peak_ratio, 24),
1023 Step::Write(correlator.peaks, 7),
1024 Step::Write(correlator.second_peaks, 5),
1025 ]
1026 });
1027
1028 tuned.chain([
1029 Step::Write(register::RX_CORRELATOR_FIRST_EDGE, 0xff),
1030 Step::Write(register::RX_CORRELATOR_CLEAR, 0xff),
1031 Step::Write(register::RX_CORRELATOR_SF_ENABLE, plan.spreading_factors),
1032 Step::Write(register::RX_CORRELATOR_CLOCK_ENABLE, plan.running()),
1033 ])
1034}
1035
1036/// Setting up the demodulators behind the correlators.
1037fn demodulators(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
1038 let sync = (0..MULTI_CHANNELS).map(|at| {
1039 // The four receivers on each radio are staggered so they do not all look at once.
1040 let offset = [1u8, 5, 9, 13][at % 4];
1041 Step::Write(register::channel_sync_offset(at as u8), offset)
1042 });
1043
1044 let tracking = (MIN_SPREADING_FACTOR..=MAX_SPREADING_FACTOR).flat_map(|spreading_factor| {
1045 [
1046 Step::Write(
1047 register::frequency_tracking(Bank::Demodulation, spreading_factor),
1048 TRACKING_AUTOMATIC,
1049 ),
1050 Step::Write(
1051 register::frequency_tracking(Bank::Timestamping, spreading_factor),
1052 TRACKING_OFF,
1053 ),
1054 ]
1055 });
1056
1057 let corrections = (MIN_SPREADING_FACTOR..=MAX_SPREADING_FACTOR).map(|spreading_factor| {
1058 // The two slowest spreading factors drift far enough across a symbol to need it.
1059 let correction = u8::from(spreading_factor >= 11);
1060 Step::Write(register::drift_correction(spreading_factor), correction)
1061 });
1062
1063 let (mantissa, shift) = drift(MULTI_BANDWIDTH_HZ, plan.carrier_hz).unwrap_or((2048, 0));
1064 let preamble = PREAMBLE_SYMBOLS.to_be_bytes();
1065
1066 [
1067 Step::Write(register::RX_DC_NOTCH_ENABLE, 0x00),
1068 Step::Write(register::RX_FORCE_DEFAULT_FILTER, 0x01),
1069 Step::Write(register::RX_GAIN_DROP_COMPENSATION, 0x01),
1070 Step::Write(register::RX_GAIN_TARGET_LEVEL, 0x01),
1071 Step::Write(register::MODEM_ENABLE_FULL, 0xff),
1072 Step::Write(register::MODEM_ENABLE_LIMITED, 0xff),
1073 Step::Write(register::RX_MODEM_SYNC_DELTA_MSB, 0),
1074 Step::Write(register::RX_MODEM_SYNC_DELTA_LSB, 126),
1075 ]
1076 .into_iter()
1077 .chain(sync)
1078 .chain(corrections)
1079 .chain([
1080 Step::Write(register::RX_FINE_TIMING_GAIN_AUTO, 3),
1081 Step::Write(register::RX_FINE_TIMING_GAIN_PAYLOAD, 3),
1082 Step::Write(register::RX_FINE_TIMING_INTEGRATE_SF11, 1),
1083 Step::Write(register::RX_FINE_TIMING_INTEGRATE_SF12, 1),
1084 Step::Write(register::RX_FREQUENCY_SYNC_THRESHOLD, 15),
1085 ])
1086 .chain(tracking)
1087 .chain([
1088 Step::Write(register::RX_TIMING_INTEGRATE_SF11, 0),
1089 Step::Write(register::RX_TIMING_INTEGRATE_SF12, 0),
1090 Step::Write(register::RX_TIMING_ROUNDING, 1),
1091 Step::Write(register::RX_TIMING_MODE, TIMING_LINEAR),
1092 Step::Write(register::RX_TIMING_GAIN_AUTO, 0),
1093 Step::Write(register::RX_TIMING_GAIN_PREAMBLE, 6),
1094 Step::Write(register::RX_TIMING_GAIN_PAYLOAD, 2),
1095 Step::Write(register::RX_TIMING_INTEGRAL_AUTO, 0),
1096 Step::Write(register::RX_TIMING_INTEGRAL_PREAMBLE, 1),
1097 Step::Write(register::RX_TIMING_INTEGRAL_PAYLOAD, 0),
1098 Step::Write(register::RX_PREAMBLE_SYMBOLS_MSB, preamble[0]),
1099 Step::Write(register::RX_PREAMBLE_SYMBOLS_LSB, preamble[1]),
1100 Step::Write(
1101 register::RX_DRIFT_MANTISSA_MSB,
1102 ((mantissa >> 8) & 0x0f) as u8,
1103 ),
1104 Step::Write(register::RX_DRIFT_MANTISSA_LSB, (mantissa & 0xff) as u8),
1105 Step::Write(register::RX_DRIFT_EXPONENT, shift),
1106 Step::Write(register::RX_DRIFT_INVERT_SYMBOL_TIME, 1),
1107 Step::Write(register::RX_DFT_PEAK_MODE, DFT_PEAK_AUTOMATIC),
1108 ])
1109}
1110
1111/// Pointing the fixed-rate receiver at its channel, when a plan gives it one.
1112///
1113/// A plan without one writes nothing here, which leaves that receiver switched on and
1114/// listening wherever reset left it.
1115fn service(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
1116 plan.service.into_iter().flat_map(move |service| {
1117 let spreading_factor = service.spreading_factor;
1118 let [high, low] = offset_bytes(service.offset_hz);
1119
1120 // A bandwidth the chip has no encoding for is refused before a plan reaches a bus,
1121 // so this only decides what a plan that never will looks like.
1122 let bandwidth = bandwidth_value(service.bandwidth_hz).unwrap_or(0);
1123
1124 let fast = matches!(spreading_factor, 5 | 6);
1125 let preamble = if fast {
1126 SERVICE_PREAMBLE_SYMBOLS_FAST
1127 } else {
1128 SERVICE_PREAMBLE_SYMBOLS
1129 }
1130 .to_be_bytes();
1131
1132 // A slower spreading factor holds a symbol longer, so timing is corrected harder
1133 // through the preamble and, at the two slowest, accumulated through the payload.
1134 let preamble_gain = match spreading_factor {
1135 5 | 6 => 4,
1136 7..=10 => 6,
1137 _ => 7,
1138 };
1139 let integral_gain = match (spreading_factor, service.bandwidth_hz) {
1140 (5..=10, _) => 0,
1141 (_, 125_000) => 1,
1142 (_, 250_000) => 2,
1143 (_, 500_000) => 3,
1144 _ => 0,
1145 };
1146
1147 let implicit = service.implicit.unwrap_or(Implicit {
1148 payload_length: 0,
1149 coding_rate: 0,
1150 crc: false,
1151 });
1152 let (mantissa, shift) = drift(service.bandwidth_hz, plan.carrier_hz).unwrap_or((2048, 0));
1153
1154 [
1155 Step::Write(register::SERVICE_FREQUENCY_MSB, high),
1156 Step::Write(register::SERVICE_FREQUENCY_LSB, low),
1157 Step::Write(
1158 register::SERVICE_RADIO_SELECT,
1159 u8::from(matches!(service.chain, Chain::B)),
1160 ),
1161 Step::Write(register::SERVICE_DETECT_PEAK_COUNT, SERVICE_PEAK_COUNT),
1162 Step::Write(
1163 register::SERVICE_DETECT_PEAK_COUNT2,
1164 SERVICE_SECOND_PEAK_COUNT,
1165 ),
1166 Step::Write(register::SERVICE_DETECT_USE_GAIN_SYMBOL, 1),
1167 Step::Write(register::SERVICE_FINE_SYNC, u8::from(fast)),
1168 Step::Write(
1169 register::SERVICE_DETECT_PEAK_TO_NOISE,
1170 SERVICE_PEAK_TO_NOISE,
1171 ),
1172 Step::Write(register::SERVICE_DC_NOTCH_ENABLE, 0x00),
1173 Step::Write(register::SERVICE_FORCE_DEFAULT_FIR, 0x01),
1174 Step::Write(register::SERVICE_DAGC_GAIN_DROP_COMP, 0x01),
1175 Step::Write(register::SERVICE_DAGC_TARGET_LEVEL, 0x01),
1176 Step::Write(register::SERVICE_TIMING_GAIN_AUTOMATIC, 0x03),
1177 Step::Write(register::SERVICE_TIMING_GAIN_PAYLOAD, 0x03),
1178 Step::Write(register::SERVICE_TIMING_GAIN_PREAMBLE, preamble_gain),
1179 Step::Write(register::SERVICE_TIMING_GAIN_INTEGRAL, integral_gain),
1180 Step::Write(
1181 register::SERVICE_IMPLICIT_HEADER,
1182 u8::from(service.implicit.is_some()),
1183 ),
1184 Step::Write(register::SERVICE_CRC_ENABLE, u8::from(implicit.crc)),
1185 Step::Write(register::SERVICE_CODING_RATE, implicit.coding_rate),
1186 Step::Write(register::SERVICE_PAYLOAD_LENGTH, implicit.payload_length),
1187 Step::Write(register::SERVICE_SPREADING_FACTOR, spreading_factor),
1188 Step::Write(register::SERVICE_BANDWIDTH, bandwidth),
1189 Step::Write(
1190 register::SERVICE_PPM_OFFSET,
1191 u8::from(low_rate_optimize(spreading_factor, service.bandwidth_hz)),
1192 ),
1193 Step::Write(register::SERVICE_PREAMBLE_MSB, preamble[0]),
1194 Step::Write(register::SERVICE_PREAMBLE_LSB, preamble[1]),
1195 Step::Write(
1196 register::SERVICE_DRIFT_MANTISSA_MSB,
1197 ((mantissa >> 8) & 0x0f) as u8,
1198 ),
1199 Step::Write(
1200 register::SERVICE_DRIFT_MANTISSA_LSB,
1201 (mantissa & 0xff) as u8,
1202 ),
1203 Step::Write(register::SERVICE_DRIFT_EXPONENT, shift),
1204 Step::Write(register::SERVICE_DRIFT_INVERT_SYMBOL_TIME, 1),
1205 Step::Write(register::SERVICE_DAGC_IN_COMP, 1),
1206 Step::Write(register::SERVICE_MODEM_ENABLE, 1),
1207 Step::Write(register::SERVICE_CAD_RX_TX, 1),
1208 Step::Write(register::SERVICE_MODEM_START, 1),
1209 Step::Write(register::SERVICE_DFT_PEAK_MODE, DFT_PEAK_AUTOMATIC),
1210 ]
1211 .into_iter()
1212 })
1213}
1214
1215/// Pointing the frequency shift keying receiver at its channel, when a plan gives it one.
1216fn fsk(plan: &Plan) -> impl Iterator<Item = Step> + '_ {
1217 plan.fsk.into_iter().flat_map(move |fsk| {
1218 let [high, low] = offset_bytes(fsk.offset_hz);
1219
1220 // The rate goes out as how many clocks make a bit, not as the rate itself.
1221 let clocks = (FSK_CLOCK_HZ / fsk.bitrate.max(1)).min(u32::from(u16::MAX)) as u16;
1222
1223 // The pattern is written least significant byte first, into registers that run
1224 // backwards, so a three byte sync word ends up in the last three of them.
1225 let pattern = fsk.pattern();
1226 let sync = (0..register::FSK_SYNC_WORD_BYTES).map(move |byte| {
1227 Step::Write(
1228 register::fsk_sync_word(byte),
1229 (pattern >> (8 * u32::from(byte))) as u8,
1230 )
1231 });
1232
1233 [
1234 Step::Write(register::FSK_FREQUENCY_MSB, high),
1235 Step::Write(register::FSK_FREQUENCY_LSB, low),
1236 Step::Write(
1237 register::FSK_RADIO_SELECT,
1238 u8::from(matches!(fsk.chain, Chain::B)),
1239 ),
1240 Step::Write(
1241 register::FSK_PATTERN_SIZE,
1242 fsk.sync_word_len.saturating_sub(1),
1243 ),
1244 Step::Write(register::FSK_BITRATE_MSB, (clocks >> 8) as u8),
1245 Step::Write(register::FSK_BITRATE_LSB, clocks as u8),
1246 Step::Write(register::FSK_BANDWIDTH_EXPONENT, FSK_BANDWIDTH_CODE),
1247 Step::Write(register::FSK_INVERT_RX, 0),
1248 Step::Write(register::FSK_INVERT_IQ, 0),
1249 Step::Write(register::FSK_RSSI_LENGTH, FSK_RSSI_SAMPLES),
1250 Step::Write(register::FSK_PACKET_MODE, 1),
1251 Step::Write(register::FSK_CRC_ENABLE, 1),
1252 Step::Write(register::FSK_DCFREE_ENCODING, FSK_WHITENING),
1253 Step::Write(register::FSK_CRC_IBM, 0),
1254 Step::Write(register::FSK_ERROR_TOLERANCE, FSK_TOLERANCE),
1255 Step::Write(register::FSK_PAYLOAD_LENGTH, FSK_MAX_PAYLOAD),
1256 Step::Write(register::FSK_NODE_ADDRESS, 0),
1257 Step::Write(register::FSK_BROADCAST_ADDRESS, 0),
1258 Step::Write(register::FSK_AUTO_AFC, 1),
1259 Step::Write(register::FSK_TIMEOUT_MSB, 0),
1260 Step::Write(register::FSK_TIMEOUT_LSB, FSK_TIMEOUT),
1261 ]
1262 .into_iter()
1263 .chain(sync)
1264 })
1265}
1266
1267/// Switching the receivers on, which is the last thing done.
1268fn enables() -> impl Iterator<Item = Step> {
1269 [
1270 Step::Write(register::COMMON_MULTI_MODEM_ENABLE, 0x01),
1271 Step::Write(register::COMMON_SERVICE_MODEM_ENABLE, 0x01),
1272 Step::Write(register::COMMON_FSK_MODEM_ENABLE, 0x01),
1273 Step::Write(register::COMMON_GLOBAL_ENABLE, 0x01),
1274 ]
1275 .into_iter()
1276}
1277
1278#[cfg(test)]
1279mod tests {
1280 use super::*;
1281
1282 /// The eight channels a European gateway usually runs, around one carrier.
1283 ///
1284 /// The carrier sits in the middle of the band rather than at its low end, because a
1285 /// radio only hears so far either side of what it is tuned to.
1286 fn eu868() -> Plan {
1287 Plan::new(
1288 867_800_000,
1289 &[
1290 -700_000, -500_000, -300_000, -100_000, 100_000, 300_000, 500_000, 700_000,
1291 ],
1292 )
1293 }
1294
1295 #[test]
1296 fn the_receivers_are_told_which_network_they_are_on() {
1297 let public: Vec<Step> = steps(&eu868()).collect();
1298 let private: Vec<Step> = steps(&eu868().network(false)).collect();
1299
1300 let written = |order: &[Step], want: register::Register| {
1301 order
1302 .iter()
1303 .find_map(|Step::Write(named, value)| (*named == want).then_some(*value))
1304 .expect("the sync word is written")
1305 };
1306
1307 // A public network moves both sync symbols out. A private one leaves them where the
1308 // chip powers up, which is why a gateway that never writes these hears nothing from
1309 // a public network however well everything else is configured.
1310 assert_eq!(written(&public, register::RX_SYNC_SF7_TO_SF12_PEAK1), 6);
1311 assert_eq!(written(&public, register::RX_SYNC_SF7_TO_SF12_PEAK2), 8);
1312 assert_eq!(written(&private, register::RX_SYNC_SF7_TO_SF12_PEAK1), 2);
1313 assert_eq!(written(&private, register::RX_SYNC_SF7_TO_SF12_PEAK2), 4);
1314
1315 // The two lowest spreading factors have no public form, on either kind of network.
1316 assert_eq!(written(&public, register::RX_SYNC_SF5_PEAK1), 2);
1317 assert_eq!(written(&public, register::RX_SYNC_SF5_PEAK2), 4);
1318 assert_eq!(written(&public, register::RX_SYNC_SF6_PEAK1), 2);
1319 assert_eq!(written(&public, register::RX_SYNC_SF6_PEAK2), 4);
1320 }
1321
1322 #[test]
1323 fn the_sync_word_is_written_before_anything_is_switched_on() {
1324 let order: Vec<Step> = steps(&eu868()).collect();
1325
1326 let sync = order
1327 .iter()
1328 .position(|step| {
1329 matches!(step, Step::Write(named, _) if *named == register::RX_SYNC_SF7_TO_SF12_PEAK1)
1330 })
1331 .expect("the sync word is written");
1332 let enable = order
1333 .iter()
1334 .position(|step| {
1335 matches!(step, Step::Write(named, _) if *named == register::COMMON_GLOBAL_ENABLE)
1336 })
1337 .expect("the receivers are switched on");
1338
1339 assert!(sync < enable);
1340 }
1341
1342 #[test]
1343 fn a_channel_past_the_radio_window_is_not_reachable() {
1344 // Half the radio's window, less half a channel. The plan this file used to carry
1345 // reached a megahertz, which is past it on both counts.
1346 assert_eq!(MAX_OFFSET_HZ, 737_500);
1347 assert!(reachable(MAX_OFFSET_HZ));
1348 assert!(reachable(-MAX_OFFSET_HZ));
1349 assert!(!reachable(MAX_OFFSET_HZ + 1));
1350 assert!(!reachable(800_000));
1351 assert!(!reachable(-1_000_000));
1352
1353 // And every channel the shipped plan uses is inside it.
1354 for listener in &eu868().channels {
1355 assert!(reachable(listener.offset_hz), "{}", listener.offset_hz);
1356 }
1357 }
1358
1359 #[test]
1360 fn an_offset_is_counted_in_chip_steps() {
1361 // Worked out from the ratio rather than from the function: 400000 * 32 / 15625 is
1362 // 819 exactly, and below the carrier it is carried as a negative in thirteen bits.
1363 assert_eq!(offset_value(400_000), 819);
1364 assert_eq!(offset_value(-400_000), 0x1fff - 819 + 1);
1365 assert_eq!(offset_bytes(0), [0x00, 0x00]);
1366 }
1367
1368 #[test]
1369 fn the_drift_reaches_the_range_the_chip_wants() {
1370 // The mantissa starts below 2048 and is doubled until it is not, and the count of
1371 // doublings is what the chip is told.
1372 let (mantissa, shift) = drift(125_000, 868_100_000).expect("a real carrier");
1373 assert_eq!((mantissa, shift), (2400, 4));
1374 assert!(mantissa >= 2048);
1375
1376 // A higher carrier is a smaller ratio, so it takes one more doubling.
1377 let (_, higher) = drift(125_000, 1_500_000_000).expect("a real carrier");
1378 assert!(higher > shift);
1379
1380 assert_eq!(drift(125_000, 0), None);
1381 }
1382
1383 #[test]
1384 fn the_drift_mantissa_fits_the_field_it_is_written_to() {
1385 // The high half goes into four bits, so a mantissa over twelve bits would be cut.
1386 // Every carrier a concentrator runs on has to stay inside that.
1387 for carrier in [433_000_000, 470_000_000, 868_100_000, 915_000_000] {
1388 let (mantissa, _) = drift(MULTI_BANDWIDTH_HZ, carrier).expect("a real carrier");
1389 assert!(mantissa < 4096, "{carrier} gives {mantissa}");
1390 }
1391 }
1392
1393 #[test]
1394 fn a_plan_switches_off_the_receivers_it_was_not_given() {
1395 let plan = Plan::new(867_500_000, &[-400_000, 0]);
1396 assert_eq!(plan.running(), 0b0000_0011);
1397 assert!(!plan.channels[2].enabled);
1398
1399 // And a plan given more than there are receivers takes the first eight.
1400 let full = Plan::new(867_500_000, &[0; 12]);
1401 assert_eq!(full.running(), 0xff);
1402 }
1403
1404 #[test]
1405 fn the_radios_are_read_off_the_receivers() {
1406 let mut plan = eu868();
1407 for at in 4..MULTI_CHANNELS {
1408 plan.channels[at] = plan.channels[at].on(Chain::B);
1409 }
1410 assert_eq!(plan.radios(), 0b1111_0000);
1411 }
1412
1413 #[test]
1414 fn only_the_spreading_factors_asked_for_are_looked_for() {
1415 let plan = eu868().looking_for(&[7, 12]);
1416 assert_eq!(plan.spreading_factors, 0b1000_0100);
1417
1418 // One outside the range is not a bit anywhere.
1419 let odd = eu868().looking_for(&[4, 13]);
1420 assert_eq!(odd.spreading_factors, 0);
1421 }
1422
1423 #[test]
1424 fn nothing_runs_before_it_has_been_told_where_to_listen() {
1425 let plan = eu868();
1426 let order: Vec<Step> = steps(&plan).collect();
1427
1428 let first_frequency = order
1429 .iter()
1430 .position(|step| {
1431 matches!(step, Step::Write(register, _) if register.address == register::channel_frequency(0)[0].address)
1432 })
1433 .expect("the receivers are given their frequencies");
1434 let enable = order
1435 .iter()
1436 .position(|step| {
1437 matches!(step, Step::Write(register, _) if *register == register::COMMON_GLOBAL_ENABLE)
1438 })
1439 .expect("the receivers are switched on");
1440
1441 assert!(first_frequency < enable, "{order:?}");
1442 assert_eq!(enable, order.len() - 1);
1443 }
1444
1445 #[test]
1446 fn every_receiver_is_given_a_frequency_even_when_it_is_off() {
1447 // A receiver that is off still has its registers set, because leaving them at
1448 // whatever they held is how a channel comes back in a strange place later.
1449 let plan = Plan::new(867_500_000, &[-400_000]);
1450 let order: Vec<Step> = steps(&plan).collect();
1451
1452 for channel in 0..MULTI_CHANNELS {
1453 let [msb, _] = register::channel_frequency(channel as u8);
1454 assert!(
1455 order
1456 .iter()
1457 .any(|step| matches!(step, Step::Write(register, _) if *register == msb)),
1458 "channel {channel} was left as it was"
1459 );
1460 }
1461 }
1462
1463 #[test]
1464 fn the_correlators_are_tuned_for_every_spreading_factor() {
1465 let order: Vec<Step> = steps(&eu868()).collect();
1466
1467 for spreading_factor in MIN_SPREADING_FACTOR..=MAX_SPREADING_FACTOR {
1468 let correlator = register::correlator(spreading_factor);
1469 assert!(
1470 order.iter().any(|step| {
1471 matches!(step, Step::Write(register, value) if *register == correlator.accumulated_ratio && *value == 52)
1472 }),
1473 "SF{spreading_factor} was left untuned"
1474 );
1475 }
1476 }
1477
1478 /// What one register was written, out of a whole plan.
1479 fn written(plan: &Plan, want: register::Register) -> Option<u8> {
1480 steps(plan).find_map(|Step::Write(named, value)| (named == want).then_some(value))
1481 }
1482
1483 /// The European plan, with the fast channel a network runs beside it.
1484 fn with_service() -> Plan {
1485 eu868().serving(Service::at(-200_000, 7).bandwidth(250_000))
1486 }
1487
1488 #[test]
1489 fn a_service_channel_is_given_a_frequency_and_a_radio() {
1490 let plan = with_service();
1491 let [high, low] = offset_bytes(-200_000);
1492
1493 assert_eq!(written(&plan, register::SERVICE_FREQUENCY_MSB), Some(high));
1494 assert_eq!(written(&plan, register::SERVICE_FREQUENCY_LSB), Some(low));
1495 assert_eq!(written(&plan, register::SERVICE_RADIO_SELECT), Some(0));
1496 assert_eq!(written(&plan, register::SERVICE_SPREADING_FACTOR), Some(7));
1497 assert_eq!(written(&plan, register::SERVICE_BANDWIDTH), Some(5));
1498 assert_eq!(written(&plan, register::SERVICE_MODEM_START), Some(1));
1499
1500 let other = eu868().serving(Service::at(0, 7).on(Chain::B));
1501 assert_eq!(written(&other, register::SERVICE_RADIO_SELECT), Some(1));
1502 }
1503
1504 #[test]
1505 fn a_plan_without_a_service_channel_leaves_its_rate_alone() {
1506 let plan = eu868();
1507 assert_eq!(plan.service, None);
1508 assert_eq!(written(&plan, register::SERVICE_FREQUENCY_MSB), None);
1509 assert_eq!(written(&plan, register::SERVICE_SPREADING_FACTOR), None);
1510 assert_eq!(written(&plan, register::SERVICE_MODEM_START), None);
1511 }
1512
1513 #[test]
1514 fn the_two_fastest_spreading_factors_stay_private_on_a_public_network() {
1515 // Everything else on a public network moves its sync symbols out.
1516 let fast = eu868().serving(Service::at(0, 5));
1517 assert_eq!(written(&fast, register::SERVICE_SYNC_PEAK1), Some(2));
1518 assert_eq!(written(&fast, register::SERVICE_SYNC_PEAK2), Some(4));
1519 assert_eq!(written(&fast, register::RX_SYNC_SF7_TO_SF12_PEAK1), Some(6));
1520
1521 let slow = eu868().serving(Service::at(0, 7));
1522 assert_eq!(written(&slow, register::SERVICE_SYNC_PEAK1), Some(6));
1523 assert_eq!(written(&slow, register::SERVICE_SYNC_PEAK2), Some(8));
1524
1525 // A private network is private for every receiver.
1526 let private = eu868().network(false).serving(Service::at(0, 7));
1527 assert_eq!(written(&private, register::SERVICE_SYNC_PEAK1), Some(2));
1528 }
1529
1530 #[test]
1531 fn the_fast_spreading_factors_expect_a_longer_preamble() {
1532 let fast = eu868().serving(Service::at(0, 6));
1533 let usual = eu868().serving(Service::at(0, 9));
1534
1535 assert_eq!(written(&fast, register::SERVICE_PREAMBLE_LSB), Some(12));
1536 assert_eq!(written(&usual, register::SERVICE_PREAMBLE_LSB), Some(8));
1537
1538 // They are also the only ones that synchronize finely.
1539 assert_eq!(written(&fast, register::SERVICE_FINE_SYNC), Some(1));
1540 assert_eq!(written(&usual, register::SERVICE_FINE_SYNC), Some(0));
1541 }
1542
1543 #[test]
1544 fn the_low_rate_optimization_follows_the_bandwidth_as_well_as_the_rate() {
1545 let narrow = eu868().serving(Service::at(0, 11));
1546 let wide = eu868().serving(Service::at(0, 11).bandwidth(250_000));
1547
1548 assert_eq!(written(&narrow, register::SERVICE_PPM_OFFSET), Some(1));
1549 assert_eq!(
1550 written(&wide, register::SERVICE_PPM_OFFSET),
1551 Some(0),
1552 "the same spreading factor twice as wide does not need it"
1553 );
1554 }
1555
1556 #[test]
1557 fn a_packet_without_a_header_carries_what_the_header_would_have() {
1558 let explicit = eu868().serving(Service::at(0, 7));
1559 assert_eq!(
1560 written(&explicit, register::SERVICE_IMPLICIT_HEADER),
1561 Some(0)
1562 );
1563 assert_eq!(
1564 written(&explicit, register::SERVICE_PAYLOAD_LENGTH),
1565 Some(0)
1566 );
1567
1568 let implicit = eu868().serving(Service::at(0, 7).without_header(Implicit {
1569 payload_length: 23,
1570 coding_rate: 1,
1571 crc: true,
1572 }));
1573 assert_eq!(
1574 written(&implicit, register::SERVICE_IMPLICIT_HEADER),
1575 Some(1)
1576 );
1577 assert_eq!(
1578 written(&implicit, register::SERVICE_PAYLOAD_LENGTH),
1579 Some(23)
1580 );
1581 assert_eq!(written(&implicit, register::SERVICE_CODING_RATE), Some(1));
1582 assert_eq!(written(&implicit, register::SERVICE_CRC_ENABLE), Some(1));
1583 }
1584
1585 #[test]
1586 fn a_rate_the_receiver_cannot_run_is_refused_rather_than_narrowed() {
1587 assert!(Service::at(0, 7).supported());
1588 assert!(Service::at(0, 12).bandwidth(500_000).supported());
1589
1590 assert!(!Service::at(0, 4).supported(), "below the slowest");
1591 assert!(!Service::at(0, 13).supported(), "past the fastest");
1592 assert!(
1593 !Service::at(0, 7).bandwidth(62_500).supported(),
1594 "not a width"
1595 );
1596 assert!(!Service::at(MAX_OFFSET_HZ + 1, 7).supported(), "not heard");
1597 }
1598
1599 #[test]
1600 fn a_frequency_shift_keying_channel_is_given_a_frequency_and_a_rate() {
1601 let plan = eu868().hearing(Fsk::at(600_000).on(Chain::B));
1602 let [high, low] = offset_bytes(600_000);
1603
1604 assert_eq!(written(&plan, register::FSK_FREQUENCY_MSB), Some(high));
1605 assert_eq!(written(&plan, register::FSK_FREQUENCY_LSB), Some(low));
1606 assert_eq!(written(&plan, register::FSK_RADIO_SELECT), Some(1));
1607
1608 // Fifty thousand bits a second is six hundred and forty clocks a bit.
1609 assert_eq!(written(&plan, register::FSK_BITRATE_MSB), Some(0x02));
1610 assert_eq!(written(&plan, register::FSK_BITRATE_LSB), Some(0x80));
1611 }
1612
1613 #[test]
1614 fn a_plan_without_one_leaves_that_receiver_alone() {
1615 let plan = eu868();
1616 assert_eq!(plan.fsk, None);
1617 assert_eq!(written(&plan, register::FSK_FREQUENCY_MSB), None);
1618 assert_eq!(written(&plan, register::FSK_BITRATE_LSB), None);
1619 }
1620
1621 #[test]
1622 fn the_sync_word_sits_at_the_top_of_the_pattern() {
1623 let plan = eu868().hearing(Fsk::at(600_000));
1624
1625 // Three bytes, in the three registers the pattern ends at, and nothing in the rest.
1626 assert_eq!(written(&plan, register::fsk_sync_word(7)), Some(0xc1));
1627 assert_eq!(written(&plan, register::fsk_sync_word(6)), Some(0x94));
1628 assert_eq!(written(&plan, register::fsk_sync_word(5)), Some(0xc1));
1629 assert_eq!(written(&plan, register::fsk_sync_word(4)), Some(0x00));
1630 assert_eq!(written(&plan, register::fsk_sync_word(0)), Some(0x00));
1631
1632 // How many of them the receiver matches is written as one less.
1633 assert_eq!(written(&plan, register::FSK_PATTERN_SIZE), Some(2));
1634 }
1635
1636 #[test]
1637 fn a_whole_sync_word_fills_every_register() {
1638 let plan = eu868().hearing(Fsk::at(0).matching(0x0102_0304_0506_0708, 8));
1639
1640 assert_eq!(
1641 Fsk::at(0).matching(0x0102_0304_0506_0708, 8).pattern(),
1642 0x0102_0304_0506_0708
1643 );
1644 assert_eq!(written(&plan, register::fsk_sync_word(7)), Some(0x01));
1645 assert_eq!(written(&plan, register::fsk_sync_word(0)), Some(0x08));
1646 assert_eq!(written(&plan, register::FSK_PATTERN_SIZE), Some(7));
1647 }
1648
1649 #[test]
1650 fn the_channel_is_set_up_the_way_the_reference_does() {
1651 let plan = eu868().hearing(Fsk::at(600_000));
1652
1653 assert_eq!(written(&plan, register::FSK_BANDWIDTH_EXPONENT), Some(0x03));
1654 assert_eq!(written(&plan, register::FSK_PACKET_MODE), Some(1));
1655 assert_eq!(written(&plan, register::FSK_CRC_ENABLE), Some(1));
1656 assert_eq!(written(&plan, register::FSK_CRC_IBM), Some(0));
1657 assert_eq!(written(&plan, register::FSK_DCFREE_ENCODING), Some(2));
1658 assert_eq!(written(&plan, register::FSK_RSSI_LENGTH), Some(4));
1659 assert_eq!(written(&plan, register::FSK_ERROR_TOLERANCE), Some(10));
1660 assert_eq!(written(&plan, register::FSK_PAYLOAD_LENGTH), Some(255));
1661 assert_eq!(written(&plan, register::FSK_AUTO_AFC), Some(1));
1662 assert_eq!(written(&plan, register::FSK_TIMEOUT_MSB), Some(0));
1663 assert_eq!(written(&plan, register::FSK_TIMEOUT_LSB), Some(128));
1664 }
1665
1666 #[test]
1667 fn a_channel_the_receiver_cannot_run_is_refused_rather_than_narrowed() {
1668 assert!(Fsk::at(600_000).supported());
1669 assert!(Fsk::at(-MAX_OFFSET_HZ).supported());
1670
1671 assert!(!Fsk::at(900_000).supported(), "past what the radio hears");
1672 assert!(!Fsk::at(0).bitrate(0).supported(), "not a rate");
1673 assert!(
1674 !Fsk::at(0).bitrate(100).supported(),
1675 "too slow for the counter"
1676 );
1677 assert!(
1678 !Fsk::at(0).matching(0, 0).supported(),
1679 "no sync word at all"
1680 );
1681 assert!(
1682 !Fsk::at(0).matching(0, 9).supported(),
1683 "longer than the pattern"
1684 );
1685 }
1686}