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}