ssdv_fec/
ssdv.rs

1use core::borrow::Borrow;
2use generic_array::{ArrayLength, GenericArray, typenum::Unsigned};
3
4/// SSDV packet.
5///
6/// This trait represents an abstract SSDV packet. Implementors of this trait
7/// correspond to the different SSDV packet formats that exist (for instance,
8/// the standard no-FEC format, or the custom format used for Lonjiang-2).
9pub trait SSDVPacket: Default + Copy {
10    /// Length of the data field of an SSDV packet.
11    ///
12    /// The length is specified in bytes as an unsigned integer type from
13    /// [`generic_array::typenum`].
14    ///
15    /// The data field includes the MCU offset, MCU index and payload fields of
16    /// the SSDV packet.
17    type DataLen: ArrayLength;
18
19    /// Length of the data taken into account for CRC-32 calculation.
20    ///
21    /// The length is specified in bytes as an unsigned integer type from
22    /// [`generic_array::typenum`].
23    type CrcDataLen: ArrayLength;
24
25    /// Length of the callsign field.
26    ///
27    /// The length is specified in bytes as an unsigned integer type from
28    /// [`generic_array::typenum`].
29    type CallsignLen: ArrayLength;
30
31    /// Sets the fields of the packet that have fixed values.
32    ///
33    /// This function writes into this packet the fields that have a fixed
34    /// value. For instance, for the standard SSDV format, this is the sync byte
35    /// and the packet type field.
36    fn set_fixed_fields(&mut self);
37
38    /// Returns a reference to an array containing the callsign field.
39    fn callsign(&self) -> &GenericArray<u8, Self::CallsignLen>;
40
41    /// Returns a mutable reference to an array containing the callsign field.
42    fn callsign_as_mut(&mut self) -> &mut GenericArray<u8, Self::CallsignLen>;
43
44    /// Returns the value of the image ID field.
45    fn image_id(&self) -> u8;
46
47    /// Sets the value of the image ID field.
48    fn set_image_id(&mut self, image_id: u8);
49
50    /// Returns the value of the packet ID field.
51    fn packet_id(&self) -> u16;
52
53    /// Sets the value of the packet ID field.
54    fn set_packet_id(&mut self, packet_id: u16);
55
56    /// Returns the value of the width field.
57    ///
58    /// The width field is only present in systematic packets. If this function
59    /// is called on a FEC packet it returns `None`.
60    fn width(&self) -> Option<u8>;
61
62    /// Sets the value of the width field.
63    ///
64    /// The width field is only present in systematic packets. This function
65    /// should only be called for systematic packets.
66    fn set_width(&mut self, width: u8);
67
68    /// Returns the value of the height field.
69    ///
70    /// The height field is only present in systematic packets. If this function
71    /// is called on a FEC packet it returns `None`.
72    fn height(&self) -> Option<u8>;
73
74    /// Sets the value of the height field.
75    ///
76    /// The height field is only present in systematic packets. This function
77    /// should only be called for systematic packets.
78    fn set_height(&mut self, height: u8);
79
80    /// Returns the value of the number of systematic packets field.
81    ///
82    /// This field is only present in FEC packets. If this function is called on
83    /// a systematic packet it returns `None`.
84    fn number_systematic_packets(&self) -> Option<u16>;
85
86    /// Sets the value of the number of systematic packets field.
87    ///
88    /// This field is only present in FEC packets. This function
89    /// should only be called for FEC packets.
90    fn set_number_systematic_packets(&mut self, number_systematic_packets: u16);
91
92    /// Returns the value of the flags field.
93    fn flags(&self) -> u8;
94
95    /// Sets the value of the flags field.
96    fn set_flags(&mut self, flags: u8);
97
98    /// Returns true if the packet has the EOI flag set.
99    fn is_eoi(&self) -> bool {
100        self.flags() & 0x4 != 0
101    }
102
103    /// Sets the value of the EOI flag.
104    fn set_eoi(&mut self, eoi: bool) {
105        self.set_flags((self.flags() & !0x4) | (u8::from(eoi) << 2));
106    }
107
108    /// Returns true if the packet has the FEC packet flag set.
109    fn is_fec_packet(&self) -> bool {
110        self.flags() & 0x40 != 0
111    }
112
113    /// Sets the value of the FEC packet flag.
114    fn set_fec_packet(&mut self, fec_packet: bool) {
115        self.set_flags((self.flags() & !0x40) | (u8::from(fec_packet) << 6));
116    }
117
118    /// Returns a reference to an array containing the packet data field.
119    ///
120    /// The data field includes the MCU offset, MCU index and payload fields of
121    /// the SSDV packet.
122    fn data(&self) -> &GenericArray<u8, Self::DataLen>;
123
124    /// Returns a mutable reference to an array containing the packet data field.
125    ///
126    /// The data field includes the MCU offset, MCU index and payload fields of
127    /// the SSDV packet.
128    fn data_as_mut(&mut self) -> &mut GenericArray<u8, Self::DataLen>;
129
130    /// Returns a reference to an array containing the part of the packet
131    /// covered by the CRC-32 calculation.
132    fn crc32_data(&self) -> &GenericArray<u8, Self::CrcDataLen>;
133
134    /// Computes the CRC-32 of some data with the CRC-32 algorithm used by this
135    /// SSDV packet format.
136    ///
137    /// The default implementation corresponds to the standard CRC-32 algorithm.
138    fn compute_crc32<I, T>(data: I) -> u32
139    where
140        I: Iterator<Item = T>,
141        T: Borrow<u8>,
142    {
143        crate::crc::crc32(data)
144    }
145
146    /// Calculates the CRC-32 of the data in the packet.
147    ///
148    /// This function returns the CRC-32 of the array returned by
149    /// [`Self::crc32_data`].
150    fn computed_crc32(&self) -> u32 {
151        Self::compute_crc32(self.crc32_data().iter())
152    }
153
154    /// Returns the value of the CRC-32 field of the packet.
155    fn crc32(&self) -> u32;
156
157    /// Sets the value of the CRC-32 field of the packet.
158    fn set_crc32(&mut self, crc32: u32);
159
160    /// Returns `true` if the CRC-32 of the packet is correct.
161    fn crc32_is_valid(&self) -> bool {
162        self.computed_crc32() == self.crc32()
163    }
164
165    /// Sets the CRC-32 field of the packet to the CRC computed from the data.
166    ///
167    /// This function modifies the CRC-32 field of the packet by setting it to
168    /// the CRC-32 returned by [`Self::crc32`].
169    fn update_crc32(&mut self) {
170        self.set_crc32(self.computed_crc32());
171    }
172}
173
174/// SSDV format parameters.
175///
176/// This trait describes an SSDV packet format in a minimalistic way, and allows
177/// the usage of the [`SSDVPacketArray`] struct to store SSDV packets of this
178/// format in a [`GenericArray`]. Implementing this trait is the usual way of
179/// adding support for an SSDV packet format.
180///
181/// Implementing an SSDV packet format using this trait has the following
182/// flexibility:
183///
184/// - The image ID, packet ID, width, height, flags, and data (which includes
185///   MCU offset, MCU index, and payload) fields must be adjacent and have the
186///   same lengths as defined in the
187///   [standard SSDV packet format](https://ukhas.org.uk/doku.php?id=guides:ssdv#packet_format).
188///   The offset of the image ID field within the packet can be arbitrary (allowing
189///   for any preceding fields in the format), and is defined with
190///   [`SSDVParameters::IMAGE_ID_OFFSET`].
191///
192/// - The callsign field is optional and can have an arbitrary length ad
193///   position within the packet. These are defined with
194///   [`SSDVParameters::CALLSIGN_OFFSET`] and [`SSDVParameters::CallsignLen`].
195///
196/// - The part of the packet that is covered by the CRC-32 calculation can be
197///   defined arbitrarily using [`SSDVParameters::CRC_DATA_OFFSET`] and
198///   [`SSDVParameters::CrcDataLen`], but it needs to be a contiguous segment.
199///   The CRC-32 is placed at the end of the packet. A custom CRC-32 algorithm
200///   can be defined by overriding [`SSDVParameters::compute_crc32`].
201///
202/// - Arbitrary packet fields with fixed values are supported with
203///   [`SSDVParameters::set_fixed_fields`].
204pub trait SSDVParameters {
205    /// Length of the SSDV packet.
206    ///
207    /// The length is specified in bytes as an unsigned integer type from
208    /// [`generic_array::typenum`].
209    type PacketLen: ArrayLength<ArrayType<u8> = Self::ArrayType>;
210
211    /// Array type.
212    ///
213    /// This type exists only for technical reasons. It is needed so that the
214    /// Rust compiler can infer that [`GenericArray<u8, Self::PacketLen>`]
215    /// implements [`Copy`]. Typically, this type should be set to
216    /// `<Self::PacketLen as ArrayLength>::ArrayType<u8>`.
217    type ArrayType: Copy;
218
219    /// Length of the data field of an SSDV packet.
220    ///
221    /// The length is specified in bytes as an unsigned integer type from
222    /// [`generic_array::typenum`].
223    type DataLen: ArrayLength;
224
225    /// Length of the data that is considered in the CRC-32 calculation.
226    ///
227    /// The length is specified in bytes as an unsigned integer type from
228    /// [`generic_array::typenum`].
229    type CrcDataLen: ArrayLength;
230
231    /// Length of the callsign field.
232    ///
233    /// The length is specified in bytes as an unsigned integer type from
234    /// [`generic_array::typenum`]. The length should be
235    /// [`generic_array::typenum::U0`] for formats which do not contain a
236    /// callsign field.
237    type CallsignLen: ArrayLength;
238
239    /// Offset of the data that is considered in the CRC-32 calculation.
240    ///
241    /// This gives the offset in bytes, with respect to the beginning of the
242    /// packet, of the data that is considered in the CRC-32 calculation.
243    const CRC_DATA_OFFSET: usize;
244
245    /// Offset of the callsign field.
246    ///
247    /// This gives the offset in bytes, with respect to the beginning of the
248    /// packet, of the callsign field. Formats which do not contain a callsign
249    /// field should set this constant to `0`.
250    const CALLSIGN_OFFSET: usize;
251
252    /// Offset of the image ID field.
253    ///
254    /// This gives the offset in bytes, with respect to the beginning of the
255    /// packet, of the image ID field.
256    const IMAGE_ID_OFFSET: usize;
257
258    /// Computes the CRC-32 of some data.
259    ///
260    /// This function returns the CRC-32 corresponding to `data`, using the
261    /// CRC-32 algorithm specified by this packet format.
262    ///
263    /// The default implementation uses a standard CRC-32 algorithm.
264    fn compute_crc32<I, T>(data: I) -> u32
265    where
266        I: Iterator<Item = T>,
267        T: Borrow<u8>,
268    {
269        crate::crc::crc32(data)
270    }
271
272    /// Sets the fields of the packet that have fixed values.
273    ///
274    /// This function writes into the [`GenericArray`] containing this packet
275    /// the fields that have a fixed value. For instance, for the standard SSDV
276    /// format, this is the sync byte and the packet type field.
277    ///
278    /// The default implementation does nothing.
279    fn set_fixed_fields(_packet: &mut GenericArray<u8, Self::PacketLen>) {}
280}
281
282/// SSDV packet stored in a [`GenericArray`].
283///
284/// This struct stores an SSDV packet in a [`GenericArray`] transparently (so
285/// the packet has the same ABI as an array `[u8; N]`). The struct implements
286/// the [`SSDVPacket`] trait by using the parameters defined in the
287/// [`SSDVParameters`] implementation of the type parameter `P`.
288#[repr(transparent)]
289pub struct SSDVPacketArray<P: SSDVParameters>(pub GenericArray<u8, P::PacketLen>);
290
291impl<P: SSDVParameters> SSDVPacketArray<P> {
292    /// Creates a new SSDV packet from a slice.
293    ///
294    /// This function creates a new SSDV packet by copying the data in
295    /// `slice`. If the length of `slice` is different from the SSDV packet
296    /// length specified by `P::PacketLen`, an error is returned.
297    pub fn new_from_slice(slice: &[u8]) -> Result<Self, generic_array::LengthError> {
298        let array: &GenericArray<u8, P::PacketLen> = slice.try_into()?;
299        Ok(Self(*array))
300    }
301}
302
303impl<P: SSDVParameters> core::fmt::Debug for SSDVPacketArray<P> {
304    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305        f.debug_struct("SSDVPacketArray")
306            .field("0", &self.0)
307            .finish()
308    }
309}
310
311impl<P: SSDVParameters> Clone for SSDVPacketArray<P> {
312    fn clone(&self) -> Self {
313        *self
314    }
315}
316
317impl<P: SSDVParameters> Copy for SSDVPacketArray<P> {}
318
319impl<P: SSDVParameters> PartialEq for SSDVPacketArray<P> {
320    fn eq(&self, other: &Self) -> bool {
321        self.0 == other.0
322    }
323}
324
325impl<P: SSDVParameters> Eq for SSDVPacketArray<P> {}
326
327impl<P: SSDVParameters> core::hash::Hash for SSDVPacketArray<P> {
328    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
329        self.0.hash(state);
330    }
331}
332
333impl<P: SSDVParameters> Default for SSDVPacketArray<P> {
334    fn default() -> Self {
335        Self(Default::default())
336    }
337}
338
339impl<P: SSDVParameters> From<GenericArray<u8, P::PacketLen>> for SSDVPacketArray<P> {
340    fn from(value: GenericArray<u8, P::PacketLen>) -> Self {
341        Self(value)
342    }
343}
344
345impl<P: SSDVParameters> From<SSDVPacketArray<P>> for GenericArray<u8, P::PacketLen> {
346    fn from(value: SSDVPacketArray<P>) -> Self {
347        value.0
348    }
349}
350
351impl<P: SSDVParameters> SSDVPacket for SSDVPacketArray<P>
352where
353    <P::PacketLen as ArrayLength>::ArrayType<u8>: Copy,
354{
355    type DataLen = P::DataLen;
356
357    type CrcDataLen = P::CrcDataLen;
358
359    type CallsignLen = P::CallsignLen;
360
361    fn set_fixed_fields(&mut self) {
362        P::set_fixed_fields(&mut self.0);
363    }
364
365    fn callsign(&self) -> &GenericArray<u8, Self::CallsignLen> {
366        self.0[P::CALLSIGN_OFFSET..P::CALLSIGN_OFFSET + Self::CallsignLen::USIZE]
367            .try_into()
368            .unwrap()
369    }
370
371    fn callsign_as_mut(&mut self) -> &mut GenericArray<u8, Self::CallsignLen> {
372        (&mut self.0[P::CALLSIGN_OFFSET..P::CALLSIGN_OFFSET + Self::CallsignLen::USIZE])
373            .try_into()
374            .unwrap()
375    }
376
377    fn image_id(&self) -> u8 {
378        self.0[P::IMAGE_ID_OFFSET]
379    }
380
381    fn set_image_id(&mut self, image_id: u8) {
382        self.0[P::IMAGE_ID_OFFSET] = image_id;
383    }
384
385    fn packet_id(&self) -> u16 {
386        u16::from_be_bytes(
387            self.0[P::IMAGE_ID_OFFSET + 1..P::IMAGE_ID_OFFSET + 3]
388                .try_into()
389                .unwrap(),
390        )
391    }
392
393    fn set_packet_id(&mut self, packet_id: u16) {
394        self.0[P::IMAGE_ID_OFFSET + 1] = (packet_id >> 8) as u8;
395        self.0[P::IMAGE_ID_OFFSET + 2] = (packet_id & 0xff) as u8;
396    }
397
398    fn width(&self) -> Option<u8> {
399        if self.is_fec_packet() {
400            None
401        } else {
402            Some(self.0[P::IMAGE_ID_OFFSET + 3])
403        }
404    }
405
406    fn set_width(&mut self, width: u8) {
407        self.0[P::IMAGE_ID_OFFSET + 3] = width;
408    }
409
410    fn height(&self) -> Option<u8> {
411        if self.is_fec_packet() {
412            None
413        } else {
414            Some(self.0[P::IMAGE_ID_OFFSET + 4])
415        }
416    }
417
418    fn set_height(&mut self, height: u8) {
419        self.0[P::IMAGE_ID_OFFSET + 4] = height;
420    }
421
422    fn number_systematic_packets(&self) -> Option<u16> {
423        if self.is_fec_packet() {
424            Some(u16::from_be_bytes(
425                self.0[P::IMAGE_ID_OFFSET + 3..P::IMAGE_ID_OFFSET + 5]
426                    .try_into()
427                    .unwrap(),
428            ))
429        } else {
430            None
431        }
432    }
433
434    fn set_number_systematic_packets(&mut self, number_systematic_packets: u16) {
435        self.0[P::IMAGE_ID_OFFSET + 3] = (number_systematic_packets >> 8) as u8;
436        self.0[P::IMAGE_ID_OFFSET + 4] = (number_systematic_packets & 0xff) as u8;
437    }
438
439    fn flags(&self) -> u8 {
440        self.0[P::IMAGE_ID_OFFSET + 5]
441    }
442
443    fn set_flags(&mut self, flags: u8) {
444        self.0[P::IMAGE_ID_OFFSET + 5] = flags;
445    }
446
447    fn data(&self) -> &GenericArray<u8, Self::DataLen> {
448        self.0[P::IMAGE_ID_OFFSET + 6..P::IMAGE_ID_OFFSET + 6 + Self::DataLen::USIZE]
449            .try_into()
450            .unwrap()
451    }
452
453    fn data_as_mut(&mut self) -> &mut GenericArray<u8, Self::DataLen> {
454        (&mut self.0[P::IMAGE_ID_OFFSET + 6..P::IMAGE_ID_OFFSET + 6 + Self::DataLen::USIZE])
455            .try_into()
456            .unwrap()
457    }
458
459    fn crc32_data(&self) -> &GenericArray<u8, Self::CrcDataLen> {
460        self.0[P::CRC_DATA_OFFSET..P::CRC_DATA_OFFSET + P::CrcDataLen::USIZE]
461            .try_into()
462            .unwrap()
463    }
464
465    fn compute_crc32<I, T>(data: I) -> u32
466    where
467        I: Iterator<Item = T>,
468        T: Borrow<u8>,
469    {
470        P::compute_crc32(data)
471    }
472
473    fn crc32(&self) -> u32 {
474        u32::from_be_bytes(self.0[P::PacketLen::USIZE - 4..].try_into().unwrap())
475    }
476
477    fn set_crc32(&mut self, crc32: u32) {
478        self.0[P::PacketLen::USIZE - 4..].copy_from_slice(&crc32.to_be_bytes());
479    }
480}