mfsk_core/uvpacket/message.rs
1// SPDX-License-Identifier: GPL-3.0-or-later
2//! `UvPacketRawMessage` — trait-required-only message codec.
3//!
4//! The new uvpacket bypasses [`MessageCodec`] entirely; encoding /
5//! decoding happens at the *frame* level in [`crate::uvpacket::tx`]
6//! and [`crate::uvpacket::rx`]. The [`Protocol`] trait nevertheless
7//! requires a [`MessageCodec`] associated type to keep generic
8//! pipeline / registry code honest.
9//!
10//! `UvPacketRawMessage` satisfies that requirement with a trivial
11//! passthrough that:
12//! - declares `PAYLOAD_BITS = 101` (= the K of `Ldpc240_101`)
13//! - returns `None` from `pack` (uvpacket TX does not go through
14//! this codec)
15//! - returns the raw 101 info bits as a 13-byte `Vec<u8>` from
16//! `unpack` (with the trailing 3 bits zero-padded to a byte
17//! boundary)
18//! - accepts unconditionally in `verify_info` — frame-level CRC-16
19//! is what catches corruption, not a per-LDPC-block CRC
20//!
21//! This codec is **not** intended to be invoked directly by user
22//! code. The public uvpacket API is byte-pipe — see
23//! [`crate::uvpacket::tx::encode`] and
24//! [`crate::uvpacket::rx::decode`].
25//!
26//! [`MessageCodec`]: crate::core::MessageCodec
27//! [`Protocol`]: crate::core::Protocol
28
29use crate::core::{DecodeContext, MessageCodec, MessageFields};
30
31/// Trivial passthrough [`MessageCodec`] for the uvpacket family.
32/// See module-level docs.
33#[derive(Copy, Clone, Debug, Default)]
34pub struct UvPacketRawMessage;
35
36impl MessageCodec for UvPacketRawMessage {
37 type Unpacked = Vec<u8>;
38
39 /// Equal to `<Ldpc240_101 as FecCodec>::K`. The FEC produces 101
40 /// info bits per LDPC block; this codec exposes those bits as
41 /// raw bytes with the trailing 3 bits zero-padded to a byte
42 /// boundary.
43 const PAYLOAD_BITS: u32 = 101;
44
45 /// No per-block CRC — the frame-level CRC-16 (in
46 /// [`crate::uvpacket::framing`]) carries the integrity field.
47 const CRC_BITS: u32 = 0;
48
49 fn pack(&self, _fields: &MessageFields) -> Option<Vec<u8>> {
50 // Bypass: callers should encode at the frame level, not the
51 // message level. Returning `None` makes accidental misuse
52 // visible at the call site.
53 None
54 }
55
56 fn unpack(&self, info: &[u8], _ctx: &DecodeContext) -> Option<Self::Unpacked> {
57 if info.len() != Self::PAYLOAD_BITS as usize {
58 return None;
59 }
60 let n_bytes = info.len().div_ceil(8);
61 let mut out = vec![0u8; n_bytes];
62 for (i, &bit) in info.iter().enumerate() {
63 if bit != 0 {
64 out[i / 8] |= 1 << (7 - (i % 8));
65 }
66 }
67 Some(out)
68 }
69
70 fn verify_info(_info: &[u8]) -> bool {
71 // Per-block integrity is delegated to frame-level CRC-16.
72 true
73 }
74}