Skip to main content

pamoja_radios/sx1302/
tx.rs

1//! Putting a packet on the air.
2//!
3//! Transmitting is three things in order: the payload goes into a buffer belonging to one of
4//! the two chains, the chip is told how long to wait before it starts, and then a trigger is
5//! armed. Which trigger decides when: now, at a counter value, or on the pulse from a GPS.
6//!
7//! The waiting is the part worth care. A gateway answers a device inside a receive window a
8//! second wide, and the chip does not begin radiating the instant it is triggered: it has a
9//! front end to settle, a filter to fill, and a modulator to start. So a timed send is
10//! programmed early by exactly those delays, and [`start_delay`] works out by how much. Get it
11//! wrong and nothing fails anywhere: the packet goes out, the device is no longer listening,
12//! and the join silently never completes.
13//!
14//! Nothing here opens a bus. The buffer address, the delay, and the trigger word are values,
15//! so they can be checked against the reference implementation with no hardware present.
16
17use pamoja_lora::LinkSettings;
18
19use super::register::{Register, TX_TOP_A_BASE, TX_TOP_B_BASE};
20
21/// Where the payload for the first chain is written.
22pub const TX_BUFFER_A: u16 = 0x5300;
23
24/// Where the payload for the second chain is written.
25pub const TX_BUFFER_B: u16 = 0x5500;
26
27/// The delay the chip starts from, in its own ticks, before the parts below are taken off.
28///
29/// The reference keeps this as microseconds and multiplies by the thirty-two ticks each one
30/// takes, which is why it is not a round number here.
31pub const START_DELAY_BASE: u16 = 1500 * 32;
32
33/// How many ticks of the concentrator counter go by in a microsecond.
34pub const TICKS_PER_US: u32 = 32;
35
36/// The value the status register reads when a chain is idle.
37pub const STATUS_FREE: u8 = 0x80;
38
39/// Which of the two transmit chains a packet goes out on.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Chain {
42    /// The first chain.
43    A,
44    /// The second chain.
45    B,
46}
47
48impl Chain {
49    /// Where this chain keeps the payload it is about to send.
50    ///
51    /// # Returns
52    ///
53    /// The address the first payload byte is written to.
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use pamoja_radios::sx1302::tx::Chain;
59    ///
60    /// assert_eq!(Chain::A.buffer(), 0x5300);
61    /// assert_eq!(Chain::B.buffer(), 0x5500);
62    /// ```
63    #[must_use]
64    pub const fn buffer(&self) -> u16 {
65        match self {
66            Chain::A => TX_BUFFER_A,
67            Chain::B => TX_BUFFER_B,
68        }
69    }
70
71    /// The block of registers that belong to this chain.
72    ///
73    /// # Returns
74    ///
75    /// The base address the chain's registers are offsets from.
76    #[must_use]
77    pub const fn base(&self) -> u16 {
78        match self {
79            Chain::A => TX_TOP_A_BASE,
80            Chain::B => TX_TOP_B_BASE,
81        }
82    }
83
84    /// The control that hands this chain's buffer to the host to write.
85    ///
86    /// # Returns
87    ///
88    /// The register, set before the payload is written and cleared after it.
89    #[must_use]
90    pub const fn write_buffer(&self) -> Register {
91        Register::new(self.base() + 7, 0, 1, false)
92    }
93
94    /// Where the chain reports what it is doing.
95    ///
96    /// # Returns
97    ///
98    /// The register [`TxStatus::of`] reads.
99    #[must_use]
100    pub const fn status(&self) -> Register {
101        Register::new(self.base() + 17, 0, 8, true)
102    }
103
104    /// The bit that starts a send of this kind.
105    ///
106    /// # Arguments
107    ///
108    /// * `trigger` - what the send waits for.
109    ///
110    /// # Returns
111    ///
112    /// The register, which is cleared and then set to arm it.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use pamoja_radios::sx1302::tx::{Chain, Trigger};
118    ///
119    /// // All three share a byte and differ only in which bit they are.
120    /// let immediate = Chain::A.trigger(Trigger::Immediate);
121    /// let gps = Chain::A.trigger(Trigger::OnGps);
122    /// assert_eq!(immediate.address, gps.address);
123    /// assert_eq!((immediate.offset, gps.offset), (0, 2));
124    /// ```
125    #[must_use]
126    pub const fn trigger(&self, trigger: Trigger) -> Register {
127        let offset = match trigger {
128            Trigger::Immediate => 0,
129            Trigger::At(_) => 1,
130            Trigger::OnGps => 2,
131        };
132        Register::new(self.base(), offset, 1, false)
133    }
134
135    /// One of the four bytes a timed send is programmed across.
136    ///
137    /// The bytes run backward: the least significant sits at the highest address. So index
138    /// zero is the first byte [`trigger_bytes`] hands back, and each one after it is written
139    /// an address lower.
140    ///
141    /// # Arguments
142    ///
143    /// * `index` - which byte, from zero to three. Anything higher is clamped to three.
144    ///
145    /// # Returns
146    ///
147    /// The register that byte is written to.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use pamoja_radios::sx1302::tx::{trigger_bytes, Chain};
153    ///
154    /// let bytes = trigger_bytes(0x1234_5678);
155    /// // The least significant byte goes to the highest of the four addresses.
156    /// assert_eq!(bytes[0], 0x78);
157    /// assert!(Chain::A.timer_byte(0).address > Chain::A.timer_byte(3).address);
158    /// ```
159    #[must_use]
160    pub const fn timer_byte(&self, index: u8) -> Register {
161        let index = if index > 3 { 3 } else { index };
162        Register::new(self.base() + 4 - index as u16, 0, 8, false)
163    }
164
165    /// The high byte of the delay the chain starts early by.
166    ///
167    /// # Returns
168    ///
169    /// The register the first of [`start_delay_bytes`] is written to.
170    #[must_use]
171    pub const fn start_delay_msb(&self) -> Register {
172        Register::new(self.base() + 5, 0, 8, false)
173    }
174
175    /// The low byte of that delay.
176    ///
177    /// # Returns
178    ///
179    /// The register the second of [`start_delay_bytes`] is written to.
180    #[must_use]
181    pub const fn start_delay_lsb(&self) -> Register {
182        Register::new(self.base() + 6, 0, 8, false)
183    }
184
185    /// Which modulator the chain runs.
186    ///
187    /// # Returns
188    ///
189    /// The register that takes [`MODULATION_LORA`] or [`MODULATION_FSK`].
190    #[must_use]
191    pub const fn modulation_type(&self) -> Register {
192        Register::new(self.base() + 9, 0, 1, false)
193    }
194
195    /// Where the modulator takes its samples from.
196    ///
197    /// # Returns
198    ///
199    /// The register that takes [`IF_SOURCE_LORA`] or [`IF_SOURCE_FSK`]. This is what parts
200    /// LoRa from an unmodulated carrier, since both name the same modulator.
201    #[must_use]
202    pub const fn if_source(&self) -> Register {
203        Register::new(self.base() + 32, 0, 2, false)
204    }
205
206    /// The bandwidth the gain control works the transmission out against.
207    ///
208    /// # Returns
209    ///
210    /// The register that takes what [`bandwidth_value`] gives.
211    #[must_use]
212    pub const fn agc_bandwidth(&self) -> Register {
213        Register::new(self.base() + 12, 0, 8, false)
214    }
215
216    /// The power the chain transmits at.
217    ///
218    /// # Returns
219    ///
220    /// The register that takes what [`Gain::power_value`] gives, which is the amplifier and
221    /// the power index together rather than a number of decibels.
222    #[must_use]
223    pub const fn power(&self) -> Register {
224        Register::new(self.base() + 13, 0, 8, false)
225    }
226
227    /// The digital gain applied before the front end.
228    ///
229    /// # Returns
230    ///
231    /// The register.
232    #[must_use]
233    pub const fn digital_gain(&self) -> Register {
234        Register::new(self.base() + 34, 0, 2, false)
235    }
236
237    /// The calibrated offset of the in-phase channel.
238    ///
239    /// # Returns
240    ///
241    /// The register.
242    #[must_use]
243    pub const fn offset_i(&self) -> Register {
244        Register::new(self.base() + 35, 0, 8, false)
245    }
246
247    /// The calibrated offset of the quadrature channel.
248    ///
249    /// # Returns
250    ///
251    /// The register.
252    #[must_use]
253    pub const fn offset_q(&self) -> Register {
254        Register::new(self.base() + 36, 0, 8, false)
255    }
256
257    /// One byte of the carrier the chain transmits on, most significant first.
258    ///
259    /// # Arguments
260    ///
261    /// * `index` - which byte, 0 to 2. Anything higher is taken as 2.
262    ///
263    /// # Returns
264    ///
265    /// The register that byte of [`frequency_value`] is written to.
266    #[must_use]
267    pub const fn frequency_byte(&self, index: u8) -> Register {
268        let index = if index > 2 { 2 } else { index };
269        Register::new(self.base() + 37 + index as u16, 0, 8, false)
270    }
271
272    /// The high bits of the frequency deviation, which is four bits rather than eight.
273    ///
274    /// # Returns
275    ///
276    /// The register.
277    #[must_use]
278    pub const fn deviation_msb(&self) -> Register {
279        Register::new(self.base() + 40, 0, 4, false)
280    }
281
282    /// The low bits of it.
283    ///
284    /// # Returns
285    ///
286    /// The register.
287    #[must_use]
288    pub const fn deviation_lsb(&self) -> Register {
289        Register::new(self.base() + 41, 0, 8, false)
290    }
291
292    /// The bandwidth the modulator runs at.
293    ///
294    /// # Returns
295    ///
296    /// The register, which shares a byte with the spreading factor.
297    #[must_use]
298    pub const fn bandwidth(&self) -> Register {
299        Register::new(self.base() + 96, 4, 4, false)
300    }
301
302    /// The spreading factor it runs at.
303    ///
304    /// # Returns
305    ///
306    /// The register, which is the factor itself rather than an index.
307    #[must_use]
308    pub const fn spreading_factor(&self) -> Register {
309        Register::new(self.base() + 96, 0, 4, false)
310    }
311
312    /// Whether the header carries the low data rate setting.
313    ///
314    /// # Returns
315    ///
316    /// The register, which the reference leaves at zero.
317    #[must_use]
318    pub const fn header_control(&self) -> Register {
319        Register::new(self.base() + 97, 6, 2, false)
320    }
321
322    /// Whether low data rate optimization is on.
323    ///
324    /// # Returns
325    ///
326    /// The register. A symbol longer than sixteen milliseconds needs it, and the airtime
327    /// assumes it, so the two have to agree.
328    #[must_use]
329    pub const fn ppm_offset(&self) -> Register {
330        Register::new(self.base() + 97, 4, 2, false)
331    }
332
333    /// The coding rate it runs at.
334    ///
335    /// # Returns
336    ///
337    /// The register, which takes the denominator less four.
338    #[must_use]
339    pub const fn coding_rate(&self) -> Register {
340        Register::new(self.base() + 97, 0, 3, false)
341    }
342
343    /// Whether the fine synchronization the two lowest spreading factors need is on.
344    ///
345    /// # Returns
346    ///
347    /// The register.
348    #[must_use]
349    pub const fn fine_sync(&self) -> Register {
350        Register::new(self.base() + 98, 7, 1, false)
351    }
352
353    /// Whether the modulator runs at all.
354    ///
355    /// # Returns
356    ///
357    /// The register.
358    #[must_use]
359    pub const fn modem_enable(&self) -> Register {
360        Register::new(self.base() + 98, 6, 1, false)
361    }
362
363    /// Which of listening, transmitting and channel activity detection the modem does.
364    ///
365    /// # Returns
366    ///
367    /// The register, which takes [`CAD_RX_TX_TRANSMIT`] for a send.
368    #[must_use]
369    pub const fn cad_rx_tx(&self) -> Register {
370        Register::new(self.base() + 98, 4, 2, false)
371    }
372
373    /// Whether the packet goes out without a header.
374    ///
375    /// # Returns
376    ///
377    /// The register.
378    #[must_use]
379    pub const fn implicit_header(&self) -> Register {
380        Register::new(self.base() + 98, 1, 1, false)
381    }
382
383    /// Whether the packet carries a physical layer checksum.
384    ///
385    /// # Returns
386    ///
387    /// The register. A LoRaWAN downlink goes out without one.
388    #[must_use]
389    pub const fn crc_enable(&self) -> Register {
390        Register::new(self.base() + 98, 0, 1, false)
391    }
392
393    /// How many bytes the packet carries.
394    ///
395    /// # Returns
396    ///
397    /// The register.
398    #[must_use]
399    pub const fn payload_length(&self) -> Register {
400        Register::new(self.base() + 99, 0, 8, false)
401    }
402
403    /// The bit that starts the modulator once it is configured.
404    ///
405    /// # Returns
406    ///
407    /// The register.
408    #[must_use]
409    pub const fn modem_start(&self) -> Register {
410        Register::new(self.base() + 101, 7, 1, false)
411    }
412
413    /// The low bits of the preamble length, in symbols.
414    ///
415    /// # Returns
416    ///
417    /// The register.
418    #[must_use]
419    pub const fn preamble_lsb(&self) -> Register {
420        Register::new(self.base() + 102, 0, 8, false)
421    }
422
423    /// The high bits of it.
424    ///
425    /// # Returns
426    ///
427    /// The register.
428    #[must_use]
429    pub const fn preamble_msb(&self) -> Register {
430        Register::new(self.base() + 103, 0, 8, false)
431    }
432
433    /// How hard the chirp is filtered, which the start delay is worked from.
434    ///
435    /// # Returns
436    ///
437    /// The register.
438    #[must_use]
439    pub const fn chirp_lowpass(&self) -> Register {
440        Register::new(self.base() + 105, 4, 3, false)
441    }
442
443    /// Whether the chirp runs continuously.
444    ///
445    /// # Returns
446    ///
447    /// The register.
448    #[must_use]
449    pub const fn continuous_chirp(&self) -> Register {
450        Register::new(self.base() + 105, 2, 1, false)
451    }
452
453    /// Whether the chirp sweeps downward, which a LoRaWAN downlink does.
454    ///
455    /// # Returns
456    ///
457    /// The register.
458    #[must_use]
459    pub const fn chirp_invert(&self) -> Register {
460        Register::new(self.base() + 105, 1, 1, false)
461    }
462
463    /// Whether the chain transmits continuously rather than one packet.
464    ///
465    /// # Returns
466    ///
467    /// The register.
468    #[must_use]
469    pub const fn continuous(&self) -> Register {
470        Register::new(self.base() + 105, 0, 1, false)
471    }
472
473    /// Where the first sync symbol goes, which says which network the packet belongs to.
474    ///
475    /// # Returns
476    ///
477    /// The register. It powers up holding the private position, as the receive side does.
478    #[must_use]
479    pub const fn sync_peak1(&self) -> Register {
480        Register::new(self.base() + 109, 0, 5, false)
481    }
482
483    /// Where the second one goes.
484    ///
485    /// # Returns
486    ///
487    /// The register.
488    #[must_use]
489    pub const fn sync_peak2(&self) -> Register {
490        Register::new(self.base() + 110, 0, 5, false)
491    }
492}
493
494/// Which front end is wired to a chain, since each settles at its own pace.
495#[derive(Clone, Copy, Debug, PartialEq, Eq)]
496pub enum FrontEnd {
497    /// The SX1250, the front end a current gateway pairs with this chip.
498    Sx1250,
499    /// The SX1255 or SX1257, which the earlier boards used.
500    Sx125x,
501}
502
503/// What the transmit chain is doing.
504#[derive(Clone, Copy, Debug, PartialEq, Eq)]
505pub enum TxStatus {
506    /// Idle, and ready to be given a packet.
507    Free,
508    /// Holding a packet, waiting for its trigger.
509    Scheduled,
510    /// On the air now.
511    Emitting,
512    /// A value the chip does not document, carried so a caller can report it.
513    Unknown(u8),
514}
515
516impl TxStatus {
517    /// Reads the transmit status register.
518    ///
519    /// # Arguments
520    ///
521    /// * `value` - what the status register answered with.
522    ///
523    /// # Returns
524    ///
525    /// What the chain is doing.
526    ///
527    /// # Examples
528    ///
529    /// ```
530    /// use pamoja_radios::sx1302::tx::TxStatus;
531    ///
532    /// assert_eq!(TxStatus::of(0x80), TxStatus::Free);
533    /// assert_eq!(TxStatus::of(0x91), TxStatus::Scheduled);
534    /// assert_eq!(TxStatus::of(0x60), TxStatus::Emitting);
535    /// ```
536    #[must_use]
537    pub const fn of(value: u8) -> TxStatus {
538        match value {
539            STATUS_FREE => TxStatus::Free,
540            0x91 | 0x92 => TxStatus::Scheduled,
541            0x30 | 0x50 | 0x60 | 0x70 => TxStatus::Emitting,
542            other => TxStatus::Unknown(other),
543        }
544    }
545
546    /// Whether a chain in this state will take another packet.
547    ///
548    /// # Returns
549    ///
550    /// Whether it is idle. A chain that is scheduled or emitting keeps what it holds, so
551    /// writing to it now would lose one of the two packets.
552    #[must_use]
553    pub const fn is_free(&self) -> bool {
554        matches!(self, TxStatus::Free)
555    }
556}
557
558/// When a packet goes out.
559#[derive(Clone, Copy, Debug, PartialEq, Eq)]
560pub enum Trigger {
561    /// As soon as the chip is told.
562    Immediate,
563    /// When the concentrator counter reaches a value, in microseconds.
564    At(u32),
565    /// On the next pulse from a GPS receiver.
566    OnGps,
567}
568
569/// A step in sending a packet.
570///
571/// The order is carried as data, so a driver walks it against a bus and a test walks it
572/// against nothing.
573#[derive(Clone, Copy, Debug, PartialEq, Eq)]
574pub enum Send {
575    /// Open the transmit buffer for writing.
576    OpenBuffer(Register),
577    /// Write the payload, starting here. For frequency shift keying the length goes first.
578    WritePayload(u16),
579    /// Close the buffer again.
580    CloseBuffer(Register),
581    /// Set the delay the chip starts early by, as a pair of bytes.
582    SetStartDelay(u16),
583    /// Program the counter value a timed send goes out at.
584    SetTimerTrigger(u32),
585    /// Reset a trigger, which the chip needs before it will take a new one.
586    ResetTrigger(Register),
587    /// Arm it.
588    ArmTrigger(Register),
589}
590
591/// How far ahead of its window a chain has to be started.
592///
593/// The chip does not radiate the moment it is triggered. The front end settles, the filter
594/// fills, and the modulator starts, so a send meant for a given moment is programmed by that
595/// much earlier.
596///
597/// # Arguments
598///
599/// * `front_end` - which front end is wired to the chain.
600/// * `bandwidth_hz` - the bandwidth the packet goes out at.
601/// * `chirp_lowpass` - the filter setting the chain is configured with.
602///
603/// # Returns
604///
605/// The delay in the chip's own ticks, or `None` for a bandwidth no front end supports.
606/// Anything that is not LoRa needs no delay at all, so a caller sends those with zero.
607///
608/// # Examples
609///
610/// ```
611/// use pamoja_radios::sx1302::tx::{start_delay, FrontEnd};
612///
613/// // The usual case: an SX1250 at 125 kHz.
614/// let delay = start_delay(FrontEnd::Sx1250, 125_000, 6).expect("a supported bandwidth");
615/// assert!(delay < 1500 * 32, "the chip starts early, never late");
616///
617/// // A bandwidth no front end covers has no answer.
618/// assert_eq!(start_delay(FrontEnd::Sx1250, 62_500, 6), None);
619/// ```
620#[must_use]
621pub fn start_delay(front_end: FrontEnd, bandwidth_hz: u32, chirp_lowpass: u8) -> Option<u16> {
622    let radio = match (front_end, bandwidth_hz) {
623        (FrontEnd::Sx1250, 125_000) => 19,
624        (FrontEnd::Sx1250, 250_000) => 24,
625        (FrontEnd::Sx1250, 500_000) => 21,
626        // The earlier front ends take a fixed time, and only 250 kHz adds to it.
627        (FrontEnd::Sx125x, 125_000 | 500_000) => 3 * 32 + 4,
628        (FrontEnd::Sx125x, 250_000) => 3 * 32 + 4 + 6,
629        _ => return None,
630    };
631
632    let filter = ((1u32 << chirp_lowpass) - 1) * 1_000_000 / bandwidth_hz;
633    let modem = 8 * (32_000_000 / (32 * bandwidth_hz));
634
635    Some(START_DELAY_BASE.wrapping_sub((radio + filter + modem) as u16))
636}
637
638/// The counter value a timed send is programmed at.
639///
640/// The counter runs in the chip's own ticks rather than microseconds, and the send is moved
641/// earlier by the start delay so the packet is on the air at the moment asked for.
642///
643/// # Arguments
644///
645/// * `at_us` - when the packet should be heard, in microseconds of the counter.
646/// * `start_delay` - what [`start_delay`] worked out.
647///
648/// # Returns
649///
650/// The value to write across the four trigger bytes.
651///
652/// # Examples
653///
654/// ```
655/// use pamoja_radios::sx1302::tx::trigger_value;
656///
657/// // A send a whole second out, brought forward by the delay.
658/// assert_eq!(trigger_value(1_000_000, 48_000), 1_000_000 * 32 - 48_000);
659/// ```
660#[must_use]
661pub const fn trigger_value(at_us: u32, start_delay: u16) -> u32 {
662    at_us
663        .wrapping_mul(TICKS_PER_US)
664        .wrapping_sub(start_delay as u32)
665}
666
667/// Splits a trigger value into the four bytes the chip takes.
668///
669/// # Arguments
670///
671/// * `value` - what [`trigger_value`] worked out.
672///
673/// # Returns
674///
675/// The bytes, least significant first, which is the order the four registers are written in.
676///
677/// # Examples
678///
679/// ```
680/// use pamoja_radios::sx1302::tx::trigger_bytes;
681///
682/// assert_eq!(trigger_bytes(0x1234_5678), [0x78, 0x56, 0x34, 0x12]);
683/// ```
684#[must_use]
685pub const fn trigger_bytes(value: u32) -> [u8; 4] {
686    value.to_le_bytes()
687}
688
689/// Splits a start delay into the two bytes the chip takes.
690///
691/// # Arguments
692///
693/// * `delay` - what [`start_delay`] worked out.
694///
695/// # Returns
696///
697/// The bytes, most significant first, which is the order this one register takes them in.
698/// It is the opposite of the trigger, which is easy to get backwards.
699///
700/// # Examples
701///
702/// ```
703/// use pamoja_radios::sx1302::tx::start_delay_bytes;
704///
705/// assert_eq!(start_delay_bytes(0x1234), [0x12, 0x34]);
706/// ```
707#[must_use]
708pub const fn start_delay_bytes(delay: u16) -> [u8; 2] {
709    delay.to_be_bytes()
710}
711
712/// The order a packet is loaded and sent in.
713///
714/// A timed send carries one step the other two do not, since only it programs the counter
715/// value to go out at.
716///
717/// # Arguments
718///
719/// * `chain` - which transmit chain is sending.
720/// * `trigger` - what the send waits for.
721/// * `start_delay` - what [`start_delay`] worked out for this packet.
722///
723/// # Returns
724///
725/// The steps, in the order the chip takes them.
726///
727/// # Examples
728///
729/// ```
730/// use pamoja_radios::sx1302::tx::{steps, Chain, Send, Trigger};
731///
732/// // A send that goes out now: delay, buffer, trigger.
733/// assert_eq!(steps(Chain::A, Trigger::Immediate, 1_000).count(), 6);
734///
735/// // A timed one programs the counter as well.
736/// assert_eq!(steps(Chain::A, Trigger::At(1_000_000), 1_000).count(), 7);
737///
738/// let mut order = steps(Chain::A, Trigger::Immediate, 1_000);
739/// assert!(matches!(order.next(), Some(Send::SetStartDelay(1_000))));
740/// assert!(matches!(order.next(), Some(Send::OpenBuffer(_))));
741/// assert!(matches!(order.next(), Some(Send::WritePayload(0x5300))));
742/// ```
743pub fn steps(chain: Chain, trigger: Trigger, start_delay: u16) -> impl Iterator<Item = Send> {
744    let arm = chain.trigger(trigger);
745    let timed = match trigger {
746        Trigger::At(at_us) => Some(Send::SetTimerTrigger(trigger_value(at_us, start_delay))),
747        Trigger::Immediate | Trigger::OnGps => None,
748    };
749    [
750        Some(Send::SetStartDelay(start_delay)),
751        Some(Send::OpenBuffer(chain.write_buffer())),
752        Some(Send::WritePayload(chain.buffer())),
753        Some(Send::CloseBuffer(chain.write_buffer())),
754        timed,
755        Some(Send::ResetTrigger(arm)),
756        Some(Send::ArmTrigger(arm)),
757    ]
758    .into_iter()
759    .flatten()
760}
761
762/// What the modulation register takes for LoRa, which is also what it takes for an
763/// unmodulated carrier. [`Chain::if_source`] is what parts the two.
764pub const MODULATION_LORA: u8 = 0x00;
765
766/// What it takes for frequency shift keying.
767pub const MODULATION_FSK: u8 = 0x01;
768
769/// What the sample source takes for LoRa.
770pub const IF_SOURCE_LORA: u8 = 0x01;
771
772/// What it takes for frequency shift keying.
773pub const IF_SOURCE_FSK: u8 = 0x02;
774
775/// What the modem mode takes to transmit.
776pub const CAD_RX_TX_TRANSMIT: u8 = 0x02;
777
778/// The chirp filtering a spreading factor below ten is sent with.
779pub const CHIRP_LOWPASS_FAST: u8 = 6;
780
781/// And what the slower ones are sent with.
782pub const CHIRP_LOWPASS_SLOW: u8 = 7;
783
784/// The sync symbol positions a public network transmits with.
785pub const PUBLIC_PEAKS: (u8, u8) = (6, 8);
786
787/// The ones a private network uses, which is what the chain powers up holding.
788pub const PRIVATE_PEAKS: (u8, u8) = (2, 4);
789
790/// Turns a carrier into the value the transmit chain is tuned with.
791///
792/// This is the concentrator's own scaling and it is not the one a front end takes: the
793/// divisor is the same 32 MHz but the shift is eighteen rather than twenty five, so using
794/// [`sx1250::frequency_value`](super::sx1250::frequency_value) here would be wrong by a
795/// factor of 128.
796///
797/// # Arguments
798///
799/// * `hertz` - the carrier.
800///
801/// # Returns
802///
803/// The value, of which three bytes are written most significant first.
804///
805/// # Examples
806///
807/// ```
808/// use pamoja_radios::sx1302::tx::frequency_value;
809///
810/// // Worked from the ratio rather than from the function: 868100000 * 2^18 / 32000000.
811/// assert_eq!(frequency_value(868_100_000), 7_111_475);
812/// ```
813#[must_use]
814pub const fn frequency_value(hertz: u32) -> u32 {
815    ((hertz as u64 * (1 << 18)) / 32_000_000) as u32
816}
817
818/// Turns a bandwidth into the value the modulator takes.
819///
820/// The values do not start at zero and they are not hertz, so a plausible guess is wrong by
821/// four.
822///
823/// # Arguments
824///
825/// * `bandwidth_hz` - the bandwidth.
826///
827/// # Returns
828///
829/// The value, or `None` for a bandwidth this chip does not transmit at.
830///
831/// # Examples
832///
833/// ```
834/// use pamoja_radios::sx1302::tx::bandwidth_value;
835///
836/// assert_eq!(bandwidth_value(125_000), Some(4));
837/// assert_eq!(bandwidth_value(250_000), Some(5));
838/// assert_eq!(bandwidth_value(500_000), Some(6));
839/// assert_eq!(bandwidth_value(62_500), None);
840/// ```
841#[must_use]
842pub const fn bandwidth_value(bandwidth_hz: u32) -> Option<u8> {
843    match bandwidth_hz {
844        125_000 => Some(4),
845        250_000 => Some(5),
846        500_000 => Some(6),
847        _ => None,
848    }
849}
850
851/// One entry of the table that turns a wanted power into what the chain is set to.
852///
853/// A concentrator is not told a number of decibels. It is told an amplifier setting and an
854/// index into the front end's own power steps, and which pair gives which radiated power is a
855/// property of the board: its amplifier, its filters, and its losses. So the table belongs to
856/// whoever built the gateway, and the one here is the reference design's.
857#[derive(Clone, Copy, Debug, PartialEq, Eq)]
858pub struct Gain {
859    /// What this entry radiates, in dBm.
860    pub radiated_dbm: i8,
861    /// The external amplifier setting. Anything above zero switches it on.
862    pub amplifier: u8,
863    /// The index into the front end's power steps.
864    pub power_index: u8,
865    /// The digital gain applied ahead of the front end.
866    pub digital_gain: u8,
867    /// The calibrated offset of the in-phase channel.
868    pub offset_i: i8,
869    /// The calibrated offset of the quadrature channel.
870    pub offset_q: i8,
871}
872
873impl Gain {
874    /// What the power register takes for this entry.
875    ///
876    /// # Returns
877    ///
878    /// The amplifier bit above the power index, which is how an SX1250 board is set.
879    ///
880    /// # Examples
881    ///
882    /// ```
883    /// use pamoja_radios::sx1302::tx::DEFAULT_GAINS;
884    ///
885    /// // The lowest entry runs the amplifier off, so only the index is carried.
886    /// assert_eq!(DEFAULT_GAINS[0].power_value(), 15);
887    ///
888    /// // The highest runs it on, which is the bit above.
889    /// assert_eq!(DEFAULT_GAINS[15].power_value(), 0x40 | 14);
890    /// ```
891    #[must_use]
892    pub const fn power_value(&self) -> u8 {
893        let amplifier = if self.amplifier > 0 { 1 } else { 0 };
894        (amplifier << 6) | (self.power_index & 0x3f)
895    }
896}
897
898/// The table the reference design carries for an SX1250 board on the European band.
899///
900/// A gateway with different hardware has a different table, which is why a configuration can
901/// name its own. Nothing here is a law of the chip.
902pub const DEFAULT_GAINS: [Gain; 16] = [
903    gain(12, 0, 15),
904    gain(13, 0, 16),
905    gain(14, 0, 17),
906    gain(15, 0, 19),
907    gain(16, 0, 20),
908    gain(17, 0, 22),
909    gain(18, 1, 1),
910    gain(19, 1, 2),
911    gain(20, 1, 3),
912    gain(21, 1, 4),
913    gain(22, 1, 5),
914    gain(23, 1, 6),
915    gain(24, 1, 7),
916    gain(25, 1, 9),
917    gain(26, 1, 11),
918    gain(27, 1, 14),
919];
920
921/// One entry of the reference table, whose digital gain and offsets are all zero.
922const fn gain(radiated_dbm: i8, amplifier: u8, power_index: u8) -> Gain {
923    Gain {
924        radiated_dbm,
925        amplifier,
926        power_index,
927        digital_gain: 0,
928        offset_i: 0,
929        offset_q: 0,
930    }
931}
932
933/// Picks the entry to transmit a wanted power at.
934///
935/// The strongest entry that does not exceed what was asked for, so a gateway told to
936/// transmit at more than its table reaches transmits at the most it has rather than refusing
937/// or exceeding it. A request below the whole table takes the weakest entry.
938///
939/// # Arguments
940///
941/// * `table` - the board's table, strongest last.
942/// * `wanted_dbm` - the power asked for.
943///
944/// # Returns
945///
946/// The entry, or `None` for an empty table.
947///
948/// # Examples
949///
950/// ```
951/// use pamoja_radios::sx1302::tx::{gain_for, DEFAULT_GAINS};
952///
953/// // Exactly on an entry, and between two.
954/// assert_eq!(gain_for(&DEFAULT_GAINS, 20).map(|g| g.radiated_dbm), Some(20));
955/// assert_eq!(gain_for(&DEFAULT_GAINS, 16).map(|g| g.radiated_dbm), Some(16));
956///
957/// // Past the top of the table, and below the bottom of it.
958/// assert_eq!(gain_for(&DEFAULT_GAINS, 30).map(|g| g.radiated_dbm), Some(27));
959/// assert_eq!(gain_for(&DEFAULT_GAINS, 2).map(|g| g.radiated_dbm), Some(12));
960/// ```
961#[must_use]
962pub fn gain_for(table: &[Gain], wanted_dbm: i8) -> Option<Gain> {
963    table
964        .iter()
965        .rev()
966        .find(|entry| entry.radiated_dbm <= wanted_dbm)
967        .or_else(|| table.first())
968        .copied()
969}
970
971/// Why a packet cannot be described to the chain.
972#[derive(Clone, Copy, Debug, PartialEq, Eq)]
973pub enum TransmitError {
974    /// A bandwidth the transmit chain does not run at.
975    Bandwidth {
976        /// The bandwidth asked for, in hertz.
977        hertz: u32,
978    },
979    /// A packet longer than the length register can describe.
980    PayloadTooLong {
981        /// How many bytes were offered.
982        bytes: usize,
983    },
984}
985
986impl core::fmt::Display for TransmitError {
987    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
988        match self {
989            TransmitError::Bandwidth { hertz } => {
990                write!(f, "a chain does not transmit at {hertz} Hz")
991            }
992            TransmitError::PayloadTooLong { bytes } => write!(
993                f,
994                "a packet of {bytes} bytes is longer than the length register describes"
995            ),
996        }
997    }
998}
999
1000impl core::error::Error for TransmitError {}
1001
1002/// A packet to put on the air, and how.
1003///
1004/// Everything the chain has to be told before a payload means anything. Built from the link
1005/// settings the rest of pamoja works in, so the airtime, the duty cycle and the transmission
1006/// itself all come from one description rather than three.
1007#[derive(Clone, Copy, Debug)]
1008pub struct Transmit<'a> {
1009    /// The carrier to transmit on.
1010    pub frequency_hz: u32,
1011    /// The spreading factor, bandwidth, coding rate, preamble, header and checksum.
1012    pub link: LinkSettings,
1013    /// What the chain is set to, from the board's table.
1014    pub gain: Gain,
1015    /// Whether the chirp sweeps the other way, which every LoRaWAN downlink does.
1016    pub invert_polarity: bool,
1017    /// Whether the network is a public one, which picks the sync word.
1018    pub public: bool,
1019    /// The packet.
1020    pub payload: &'a [u8],
1021}
1022
1023/// Everything a chain is told before a packet is loaded.
1024///
1025/// The order is the reference order. A modulator is configured and then started, and the
1026/// payload and the trigger that [`steps`] carries come after all of it.
1027///
1028/// # Arguments
1029///
1030/// * `chain` - the chain that sends it.
1031/// * `transmit` - what to send and how.
1032///
1033/// # Returns
1034///
1035/// The writes, in order.
1036///
1037/// # Errors
1038///
1039/// Returns [`TransmitError::Bandwidth`] for a bandwidth the chain does not run at, and
1040/// [`TransmitError::PayloadTooLong`] for a packet the length register cannot describe.
1041pub fn settings(
1042    chain: Chain,
1043    transmit: &Transmit<'_>,
1044) -> Result<[(Register, u8); 31], TransmitError> {
1045    let link = &transmit.link;
1046    let bandwidth = bandwidth_value(link.bandwidth_hz()).ok_or(TransmitError::Bandwidth {
1047        hertz: link.bandwidth_hz(),
1048    })?;
1049    let length =
1050        u8::try_from(transmit.payload.len()).map_err(|_| TransmitError::PayloadTooLong {
1051            bytes: transmit.payload.len(),
1052        })?;
1053    let carrier = frequency_value(transmit.frequency_hz);
1054
1055    // The deviation is half the bandwidth in the chip's own frequency units, and its high
1056    // half is four bits wide, which every supported bandwidth fits inside.
1057    let deviation = frequency_value(link.bandwidth_hz() / 2);
1058    let preamble = link.preamble_symbols();
1059    let spreading_factor = link.spreading_factor();
1060
1061    let (peak1, peak2) = if transmit.public {
1062        PUBLIC_PEAKS
1063    } else {
1064        PRIVATE_PEAKS
1065    };
1066
1067    // The two lowest spreading factors have no public form and need the fine synchronization
1068    // the others do not.
1069    let low = spreading_factor <= 6;
1070    let (peak1, peak2) = if low { PRIVATE_PEAKS } else { (peak1, peak2) };
1071
1072    let lowpass = if spreading_factor < 10 {
1073        CHIRP_LOWPASS_FAST
1074    } else {
1075        CHIRP_LOWPASS_SLOW
1076    };
1077
1078    Ok([
1079        (chain.modulation_type(), MODULATION_LORA),
1080        (chain.if_source(), IF_SOURCE_LORA),
1081        (chain.offset_i(), transmit.gain.offset_i as u8),
1082        (chain.offset_q(), transmit.gain.offset_q as u8),
1083        (chain.power(), transmit.gain.power_value()),
1084        (chain.digital_gain(), transmit.gain.digital_gain),
1085        (chain.frequency_byte(0), ((carrier >> 16) & 0xff) as u8),
1086        (chain.frequency_byte(1), ((carrier >> 8) & 0xff) as u8),
1087        (chain.frequency_byte(2), (carrier & 0xff) as u8),
1088        (chain.agc_bandwidth(), bandwidth),
1089        (chain.deviation_msb(), ((deviation >> 8) & 0xff) as u8),
1090        (chain.deviation_lsb(), (deviation & 0xff) as u8),
1091        (chain.bandwidth(), bandwidth),
1092        (chain.preamble_msb(), ((preamble >> 8) & 0xff) as u8),
1093        (chain.preamble_lsb(), (preamble & 0xff) as u8),
1094        (chain.spreading_factor(), spreading_factor),
1095        (chain.chirp_lowpass(), lowpass),
1096        (
1097            chain.coding_rate(),
1098            link.coding_rate_denominator().saturating_sub(4),
1099        ),
1100        (chain.modem_enable(), 1),
1101        (chain.cad_rx_tx(), CAD_RX_TX_TRANSMIT),
1102        (chain.modem_start(), 1),
1103        (chain.continuous(), 0),
1104        (chain.chirp_invert(), u8::from(transmit.invert_polarity)),
1105        (chain.implicit_header(), u8::from(!link.explicit_header())),
1106        (chain.crc_enable(), u8::from(link.crc())),
1107        (chain.sync_peak1(), peak1),
1108        (chain.sync_peak2(), peak2),
1109        (chain.fine_sync(), u8::from(low)),
1110        (chain.payload_length(), length),
1111        (chain.header_control(), 0),
1112        (
1113            chain.ppm_offset(),
1114            u8::from(link.low_data_rate_optimization()),
1115        ),
1116    ])
1117}
1118
1119#[cfg(test)]
1120mod tests {
1121    use super::*;
1122
1123    /// What a chain is told for one packet, so a test can look a register up by name.
1124    fn settings_for(link: LinkSettings, public: bool, payload: &[u8]) -> [(Register, u8); 31] {
1125        let request = Transmit {
1126            frequency_hz: 868_100_000,
1127            link,
1128            gain: DEFAULT_GAINS[8],
1129            invert_polarity: true,
1130            public,
1131            payload,
1132        };
1133        settings(Chain::A, &request).expect("a packet the chain can describe")
1134    }
1135
1136    fn value_of(written: &[(Register, u8)], want: Register) -> u8 {
1137        written
1138            .iter()
1139            .find_map(|(named, value)| (*named == want).then_some(*value))
1140            .expect("the register is written")
1141    }
1142
1143    #[test]
1144    fn a_packet_is_described_in_the_units_the_chip_counts_in() {
1145        let link = LinkSettings::new(7, 125_000)
1146            .with_coding_rate(5)
1147            .with_preamble(8);
1148        let written = settings_for(link, true, &[0; 12]);
1149        let chain = Chain::A;
1150
1151        // None of these are the numbers a person would write. The bandwidth is 4 rather than
1152        // 125000 or an index from zero, the coding rate is the denominator less four, and the
1153        // spreading factor is the only one that is itself.
1154        assert_eq!(value_of(&written, chain.bandwidth()), 4);
1155        assert_eq!(value_of(&written, chain.agc_bandwidth()), 4);
1156        assert_eq!(value_of(&written, chain.coding_rate()), 1);
1157        assert_eq!(value_of(&written, chain.spreading_factor()), 7);
1158
1159        // The carrier is three bytes of the chip's own scaling, most significant first.
1160        assert_eq!(value_of(&written, chain.frequency_byte(0)), 0x6c);
1161        assert_eq!(value_of(&written, chain.frequency_byte(1)), 0x83);
1162        assert_eq!(value_of(&written, chain.frequency_byte(2)), 0x33);
1163
1164        // The deviation is half the bandwidth in those same units, and its high half is four
1165        // bits wide, which 512 fits inside.
1166        assert_eq!(value_of(&written, chain.deviation_msb()), 2);
1167        assert_eq!(value_of(&written, chain.deviation_lsb()), 0);
1168
1169        assert_eq!(value_of(&written, chain.payload_length()), 12);
1170        assert_eq!(value_of(&written, chain.preamble_lsb()), 8);
1171        assert_eq!(value_of(&written, chain.preamble_msb()), 0);
1172        assert_eq!(value_of(&written, chain.chirp_invert()), 1);
1173    }
1174
1175    #[test]
1176    fn the_modulator_is_named_by_its_sample_source_rather_than_its_type() {
1177        let link = LinkSettings::new(9, 125_000);
1178        let written = settings_for(link, true, &[0; 4]);
1179
1180        // Writing one into the modulation type would select frequency shift keying. LoRa and
1181        // an unmodulated carrier share it, and the sample source is what parts them.
1182        assert_eq!(
1183            value_of(&written, Chain::A.modulation_type()),
1184            MODULATION_LORA
1185        );
1186        assert_eq!(value_of(&written, Chain::A.if_source()), IF_SOURCE_LORA);
1187        assert_ne!(MODULATION_LORA, MODULATION_FSK);
1188    }
1189
1190    #[test]
1191    fn a_transmission_says_which_network_it_belongs_to() {
1192        let link = LinkSettings::new(7, 125_000);
1193        let public = settings_for(link, true, &[0; 4]);
1194        let private = settings_for(link, false, &[0; 4]);
1195
1196        assert_eq!(value_of(&public, Chain::A.sync_peak1()), 6);
1197        assert_eq!(value_of(&public, Chain::A.sync_peak2()), 8);
1198        assert_eq!(value_of(&private, Chain::A.sync_peak1()), 2);
1199        assert_eq!(value_of(&private, Chain::A.sync_peak2()), 4);
1200
1201        // The two lowest spreading factors have no public form, and take the fine
1202        // synchronization the others do not.
1203        let low = settings_for(LinkSettings::new(5, 125_000), true, &[0; 4]);
1204        assert_eq!(value_of(&low, Chain::A.sync_peak1()), 2);
1205        assert_eq!(value_of(&low, Chain::A.sync_peak2()), 4);
1206        assert_eq!(value_of(&low, Chain::A.fine_sync()), 1);
1207        assert_eq!(value_of(&public, Chain::A.fine_sync()), 0);
1208    }
1209
1210    #[test]
1211    fn the_slow_spreading_factors_are_sent_the_way_their_airtime_assumes() {
1212        let fast = settings_for(LinkSettings::new(7, 125_000), true, &[0; 4]);
1213        let slow = settings_for(LinkSettings::new(11, 125_000), true, &[0; 4]);
1214
1215        // Low data rate optimization is on exactly when a symbol runs past sixteen
1216        // milliseconds, which is what the airtime already assumes.
1217        assert_eq!(value_of(&fast, Chain::A.ppm_offset()), 0);
1218        assert_eq!(value_of(&slow, Chain::A.ppm_offset()), 1);
1219
1220        // And the chirp is filtered harder from SF10 upward.
1221        assert_eq!(
1222            value_of(&fast, Chain::A.chirp_lowpass()),
1223            CHIRP_LOWPASS_FAST
1224        );
1225        assert_eq!(
1226            value_of(&slow, Chain::A.chirp_lowpass()),
1227            CHIRP_LOWPASS_SLOW
1228        );
1229    }
1230
1231    #[test]
1232    fn a_packet_the_chain_cannot_describe_is_refused() {
1233        let narrow = Transmit {
1234            frequency_hz: 868_100_000,
1235            link: LinkSettings::new(7, 62_500),
1236            gain: DEFAULT_GAINS[0],
1237            invert_polarity: false,
1238            public: true,
1239            payload: &[0; 4],
1240        };
1241        assert_eq!(
1242            settings(Chain::A, &narrow),
1243            Err(TransmitError::Bandwidth { hertz: 62_500 })
1244        );
1245
1246        // A packet longer than the length register describes is refused rather than sent
1247        // with a length that is not its own.
1248        let long = Transmit {
1249            frequency_hz: 868_100_000,
1250            link: LinkSettings::new(7, 125_000),
1251            gain: DEFAULT_GAINS[0],
1252            invert_polarity: false,
1253            public: true,
1254            payload: &[0; 300],
1255        };
1256        assert_eq!(
1257            settings(Chain::A, &long),
1258            Err(TransmitError::PayloadTooLong { bytes: 300 })
1259        );
1260    }
1261
1262    #[test]
1263    fn each_chain_has_its_own_buffer_and_registers() {
1264        assert_eq!(Chain::A.buffer(), 0x5300);
1265        assert_eq!(Chain::B.buffer(), 0x5500);
1266        assert_ne!(Chain::A.buffer(), Chain::B.buffer());
1267
1268        // The buffers sit inside the blocks that belong to each chain.
1269        assert!(Chain::A.buffer() > Chain::A.base());
1270        assert!(Chain::B.buffer() > Chain::B.base());
1271        assert!(Chain::A.buffer() < Chain::B.base());
1272    }
1273
1274    #[test]
1275    fn the_status_values_are_the_ones_the_chip_reports() {
1276        assert_eq!(TxStatus::of(0x80), TxStatus::Free);
1277        assert!(TxStatus::of(0x80).is_free());
1278
1279        for scheduled in [0x91, 0x92] {
1280            assert_eq!(TxStatus::of(scheduled), TxStatus::Scheduled);
1281            assert!(!TxStatus::of(scheduled).is_free());
1282        }
1283        for emitting in [0x30, 0x50, 0x60, 0x70] {
1284            assert_eq!(TxStatus::of(emitting), TxStatus::Emitting);
1285            assert!(!TxStatus::of(emitting).is_free());
1286        }
1287
1288        // Anything else is carried rather than mistaken for idle.
1289        assert_eq!(TxStatus::of(0x00), TxStatus::Unknown(0x00));
1290        assert!(!TxStatus::of(0x00).is_free());
1291    }
1292
1293    #[test]
1294    fn the_chip_always_starts_early_and_never_late() {
1295        // Whatever the settings, the delay comes off the base rather than adding to it.
1296        for bandwidth in [125_000, 250_000, 500_000] {
1297            for lowpass in 0..=8 {
1298                for front_end in [FrontEnd::Sx1250, FrontEnd::Sx125x] {
1299                    let delay = start_delay(front_end, bandwidth, lowpass)
1300                        .expect("these are supported bandwidths");
1301                    assert!(
1302                        delay < START_DELAY_BASE,
1303                        "{front_end:?} {bandwidth} {lowpass}"
1304                    );
1305                }
1306            }
1307        }
1308    }
1309
1310    #[test]
1311    fn the_front_ends_settle_at_their_own_pace() {
1312        // The two differ, which is the whole reason the front end is a parameter.
1313        let modern = start_delay(FrontEnd::Sx1250, 125_000, 6).expect("supported");
1314        let earlier = start_delay(FrontEnd::Sx125x, 125_000, 6).expect("supported");
1315        assert_ne!(modern, earlier);
1316
1317        // The older part takes longer to settle, so it starts earlier still.
1318        assert!(earlier < modern);
1319    }
1320
1321    #[test]
1322    fn a_bandwidth_no_front_end_covers_has_no_answer() {
1323        for bandwidth in [7_800, 62_500, 1_000_000] {
1324            assert_eq!(start_delay(FrontEnd::Sx1250, bandwidth, 6), None);
1325            assert_eq!(start_delay(FrontEnd::Sx125x, bandwidth, 6), None);
1326        }
1327    }
1328
1329    #[test]
1330    fn a_wider_filter_takes_longer_to_fill() {
1331        // The filter term grows with the setting, so the chip has to start earlier.
1332        let narrow = start_delay(FrontEnd::Sx1250, 125_000, 2).expect("supported");
1333        let wide = start_delay(FrontEnd::Sx1250, 125_000, 8).expect("supported");
1334        assert!(wide < narrow, "a wider filter means an earlier start");
1335    }
1336
1337    #[test]
1338    fn a_timed_send_is_programmed_earlier_than_the_moment_asked_for() {
1339        // The window is counted in microseconds and the chip in ticks.
1340        let delay = 48_000u16;
1341        let value = trigger_value(1_000_000, delay);
1342        assert_eq!(value, 1_000_000 * TICKS_PER_US - u32::from(delay));
1343
1344        // Which is earlier than the plain conversion would be.
1345        assert!(value < 1_000_000 * TICKS_PER_US);
1346    }
1347
1348    #[test]
1349    fn the_two_words_go_out_in_opposite_orders() {
1350        // The trigger is least significant first and the delay most significant first, which
1351        // is the kind of thing that reads correctly and transmits at the wrong moment.
1352        assert_eq!(trigger_bytes(0x1234_5678), [0x78, 0x56, 0x34, 0x12]);
1353        assert_eq!(start_delay_bytes(0x1234), [0x12, 0x34]);
1354
1355        // Round trips, so neither is quietly reversed.
1356        let value = 0xdead_beefu32;
1357        assert_eq!(u32::from_le_bytes(trigger_bytes(value)), value);
1358        let delay = 0xbeefu16;
1359        assert_eq!(u16::from_be_bytes(start_delay_bytes(delay)), delay);
1360    }
1361
1362    #[test]
1363    fn a_trigger_that_wraps_the_counter_still_reads_back() {
1364        // The counter is thirty-two bits and wraps about every thirty-six minutes, so a
1365        // send near the wrap must not panic or saturate.
1366        let near_wrap = trigger_value(u32::MAX / TICKS_PER_US, 48_000);
1367        assert_eq!(
1368            near_wrap,
1369            (u32::MAX / TICKS_PER_US)
1370                .wrapping_mul(TICKS_PER_US)
1371                .wrapping_sub(48_000)
1372        );
1373
1374        // And a moment so early that the delay takes it below zero wraps rather than panics.
1375        let underflow = trigger_value(0, 48_000);
1376        assert_eq!(underflow, 0u32.wrapping_sub(48_000));
1377    }
1378}