ssdv_fec/
fec.rs

1use crate::{GF64K, SSDVPacket};
2use generic_array::GenericArray;
3#[cfg(feature = "std")]
4use thiserror::Error;
5
6/// SSDV FEC encoder.
7///
8/// This struct is used to encode an arbitrary number of packets for an SSDV
9/// image in a fountain-code-like manner. The encoder is initialized with
10/// [`Encoder::new`] by giving it the SSDV image packets. Afterwards, the
11/// [`Encoder::encode`] function can be called to generate a packet with an
12/// arbitrary `packet_id`.
13///
14/// The struct contains a mutable reference to a slice containing the SSDV
15/// packets of the image. The lifetime of this slice is given by the lifetime
16/// parameter `'a`.
17#[derive(Debug)]
18pub struct Encoder<'a, S> {
19    buffer: &'a mut [S],
20}
21
22/// Error produced by the SSDV FEC encoder.
23///
24/// This enum lists the errors that can be produced by [`Encoder`].
25#[allow(clippy::enum_variant_names)] // this is triggered because all the variants end in Input
26#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
27#[cfg_attr(feature = "std", derive(Error))]
28pub enum EncoderError {
29    /// The encoder input is empty.
30    #[cfg_attr(feature = "std", error("encoder input is empty"))]
31    EmptyInput,
32    /// The encoder input is too long.
33    #[cfg_attr(feature = "std", error("encoder input is too long"))]
34    TooLongInput,
35    /// There is a non-systematic packet in the encoder input.
36    #[cfg_attr(feature = "std", error("non-systematic packet in encoder input"))]
37    NonSystematicInput,
38}
39
40// Computes
41// w_j^{-1} = \prod_{m \neq j} (x_j - x_m).
42fn wj_inv(j: u16, k: u16) -> GF64K {
43    let xj = GF64K::from(j);
44    let mut ret = GF64K::from(1);
45    for m in 0..k {
46        if m != j {
47            let xm = GF64K::from(m);
48            ret *= xj - xm;
49        }
50    }
51    ret
52}
53
54impl<S: SSDVPacket> Encoder<'_, S> {
55    /// Creates a new FEC encoder for an SSDV image.
56    ///
57    /// The systematic packets for the image are given in the slice
58    /// `systematic_packets`. They must be in order and without repetitions. The
59    /// encoder works in-place in this slice, modifying its contents.
60    ///
61    /// If there is a problem with the input contents, this function returns an
62    /// error. Otherwise, an [`Encoder`] struct on which
63    /// [`encode`](`Encoder::encode`) can be called is returned.
64    pub fn new(systematic_packets: &mut [S]) -> Result<Encoder<S>, EncoderError> {
65        if systematic_packets.is_empty() {
66            return Err(EncoderError::EmptyInput);
67        }
68        if systematic_packets.len() > usize::from(u16::MAX) {
69            return Err(EncoderError::TooLongInput);
70        }
71        // only check the first packet for efficiency
72        if systematic_packets[0].is_fec_packet() {
73            return Err(EncoderError::NonSystematicInput);
74        }
75        let mut encoder = Encoder {
76            buffer: systematic_packets,
77        };
78        encoder.values_to_lagrange();
79        Ok(encoder)
80    }
81
82    fn values_to_lagrange(&mut self) {
83        // The Lagrange polynomial L(x) that interpolates
84        // L(x_j) = y_j
85        // can be computed as
86        // L(x) = l(x) \sum_{j=0}^{k-1} w_j y_j / (x - x_j),
87        // where
88        // l(x) = \prod_{j=0}^{k-1} (x - x_j),
89        // and
90        // w_j = \prod_{m \neq j} (x_j - x_m)^{-1}.
91        //
92        // This function replaces in-place in self.buffer the values y_j by the
93        // terms w_j y_j. This speeds up evaluation of the L(x) for encoding
94        // each FEC packet.
95        let k = self.num_systematic();
96        for j in 0..k {
97            // Compute w_j
98            let wj = GF64K::from(1) / wj_inv(j, k);
99            // Multiply each y_j by w_j
100            let data = self.buffer[usize::from(j)].data_as_mut();
101            for word in data.chunks_exact_mut(2) {
102                let word: &mut [u8; 2] = word.try_into().unwrap();
103                let yj = GF64K::from(u16::from_be_bytes(*word));
104                let yj_wj = yj * wj;
105                *word = u16::from(yj_wj).to_be_bytes();
106            }
107        }
108    }
109
110    /// Generate the packet with a corresponding `packet_id`.
111    ///
112    /// If the `packet_id` is smaller than the number of systematic packets in
113    /// the image, the corresponding systematic packet give to [`Encoder::new`]
114    /// is generated. Otherwise, a FEC packet is generated. The packet is
115    /// written to `output`.
116    pub fn encode(&self, packet_id: u16, output: &mut S) {
117        self.encode_header(packet_id, output);
118        if output.is_fec_packet() {
119            self.encode_fec_data(packet_id, output.data_as_mut());
120        } else {
121            self.encode_systematic_data(packet_id, output.data_as_mut());
122        }
123        output.update_crc32();
124    }
125
126    fn encode_header(&self, packet_id: u16, output: &mut S) {
127        output.set_fixed_fields();
128        output.callsign_as_mut().copy_from_slice(self.callsign());
129        output.set_image_id(self.image_id());
130        output.set_packet_id(packet_id);
131        let is_fec = packet_id >= self.num_systematic();
132        if is_fec {
133            output.set_number_systematic_packets(self.num_systematic());
134        } else {
135            output.set_width(self.image_width());
136            output.set_height(self.image_height());
137        }
138        output.set_flags(self.flags());
139        output.set_eoi(packet_id == self.num_systematic() - 1);
140        output.set_fec_packet(is_fec);
141    }
142
143    fn encode_fec_data(&self, packet_id: u16, data: &mut GenericArray<u8, S::DataLen>) {
144        // See values_to_lagrange for the formulas
145        let x = GF64K::from(packet_id);
146        let k = self.num_systematic();
147        // Compute l(x)
148        let mut lx = GF64K::from(1);
149        for j in 0..k {
150            let xj = GF64K::from(j);
151            lx *= x - xj;
152        }
153
154        // Compute \sum_{j=0}^{k-1} w_j y_j / (x - x_j) for each word in the
155        // output data
156        for (r, word) in data.chunks_exact_mut(2).enumerate() {
157            let mut sum = GF64K::from(0);
158            for (j, wj_yj_s) in self.buffer.iter().map(|packet| packet.data()).enumerate() {
159                let wj_yj = GF64K::from(u16::from_be_bytes(
160                    wj_yj_s[2 * r..2 * r + 2].try_into().unwrap(),
161                ));
162                let xj = GF64K::from(j as u16);
163                sum += wj_yj / (x - xj);
164            }
165            let word: &mut [u8; 2] = word.try_into().unwrap();
166            let result = lx * sum;
167            *word = u16::from(result).to_be_bytes();
168        }
169    }
170
171    fn encode_systematic_data(&self, packet_id: u16, data: &mut GenericArray<u8, S::DataLen>) {
172        // The algorithm in encode_fec_data is not valid for systematic packets,
173        // because both l(x) and one of the terms 1 / (x - x_j) vanish. In the
174        // systematic case we compute w_j again and divide, undoing what we did
175        // in values_to_lagrange.
176        let wjinv = wj_inv(packet_id, self.num_systematic());
177        for (word_in, word_out) in self.buffer[usize::from(packet_id)]
178            .data()
179            .chunks_exact(2)
180            .zip(data.chunks_exact_mut(2))
181        {
182            let wj_yj = GF64K::from(u16::from_be_bytes(word_in.try_into().unwrap()));
183            let yj = wj_yj * wjinv;
184            let word_out: &mut [u8; 2] = word_out.try_into().unwrap();
185            *word_out = u16::from(yj).to_be_bytes();
186        }
187    }
188
189    fn callsign(&self) -> &GenericArray<u8, S::CallsignLen> {
190        self.buffer[0].callsign()
191    }
192
193    fn num_systematic(&self) -> u16 {
194        self.buffer.len() as u16
195    }
196
197    fn image_id(&self) -> u8 {
198        self.buffer[0].image_id()
199    }
200
201    fn image_width(&self) -> u8 {
202        self.buffer[0].width().unwrap()
203    }
204
205    fn image_height(&self) -> u8 {
206        self.buffer[0].height().unwrap()
207    }
208
209    fn flags(&self) -> u8 {
210        self.buffer[0].flags()
211    }
212}
213
214/// SSDV FEC decoder.
215///
216/// This struct represents the FEC decoder. The way to use the FEC decoder is
217/// through the [`Decoder::decode`] associated function. The struct only exists
218/// for namespacing this function.
219#[derive(Debug)]
220pub struct Decoder {}
221
222#[derive(Debug)]
223struct DecoderHelper<'a, 'b, S: SSDVPacket> {
224    input: &'a mut [S],
225    output: &'b mut [S],
226    callsign: GenericArray<u8, S::CallsignLen>,
227    num_systematic: u16,
228    image_id: u8,
229    image_width: u8,
230    image_height: u8,
231    flags: u8,
232}
233
234/// Error produced by the SSDV FEC decoder.
235///
236/// This enum lists the errors that can be produced by [`Decoder`].
237#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
238#[cfg_attr(feature = "std", derive(Error))]
239pub enum DecoderError {
240    /// The EOI flag is set on a FEC packet.
241    #[cfg_attr(feature = "std", error("EOI set on FEC packet"))]
242    EoiOnFecPacket,
243    /// The EOI flag is set on several different systematic packets.
244    #[cfg_attr(feature = "std", error("EOI set on several different packets"))]
245    DuplicatedEoi,
246    /// There are different FEC packets containing a different value in the
247    /// number of systematic packets field.
248    #[cfg_attr(
249        feature = "std",
250        error("mismatched number of systematic packets on different FEC packets")
251    )]
252    NumSystematicMismatch,
253    /// The number of systematic packets in the image could not be determined.
254    ///
255    /// This happens if the last systematic packet (carrying the EOI flag) is
256    /// missing and there are no FEC packets.
257    #[cfg_attr(
258        feature = "std",
259        error("could not determine number of systematic packets")
260    )]
261    UnknownNumSystematic,
262    /// There is a mismatch between the packet ID of the packet carrying the EOI
263    /// flag and the number of systematic packets field in the FEC packets.
264    #[cfg_attr(
265        feature = "std",
266        error("mismatch between EOI and number of systematic packets")
267    )]
268    EoiFecMismatch,
269    /// There are not enough input packets for decoding.
270    ///
271    /// The decoder needs as least as many distinct input packets as systematic
272    /// packets are there in the image.
273    #[cfg_attr(feature = "std", error("not enough input packets"))]
274    NotEnoughInput,
275    /// The output buffer is too short.
276    ///
277    /// The length of the output buffer must be greater or equal than the number
278    /// of systematic packets in the image.
279    #[cfg_attr(feature = "std", error("output buffer is too short"))]
280    OutputTooShort,
281    /// A systematic packet has a wrong packet ID.
282    #[cfg_attr(feature = "std", error("wrong packet ID on systematic packet"))]
283    WrongSystematicId,
284    /// There are multiple image IDs in the packets.
285    #[cfg_attr(feature = "std", error("multiple image IDs"))]
286    MultipleImageIds,
287    /// There are different packets with inconsistent values of the flags field.
288    #[cfg_attr(feature = "std", error("inconsistent flags on different packets"))]
289    InconsistentFlags,
290    /// There are systematic packets with different values of the image width or
291    /// height.
292    #[cfg_attr(
293        feature = "std",
294        error("mismatched width or height on different systematic packets")
295    )]
296    DimensionsMismatch,
297    /// There are no systematic packets.
298    ///
299    /// At least one systematic packet is required to obtain the image width and
300    /// height.
301    #[cfg_attr(feature = "std", error("no systematic packets"))]
302    NoSystematic,
303}
304
305impl Decoder {
306    /// Decodes a list of SSDV packets to obtain the original SSDV image.
307    ///
308    /// This function receives a slice `input` containing SSDV packets from a
309    /// single image, and, if possible, obtains the original SSDV image and
310    /// writes the results to the beginning of the `output` slice, returning the
311    /// subslice of `output` that contains the image packets. If decoding is not
312    /// possible, the function returns an error.
313    ///
314    /// The packets in `input` can be in any order and can have duplicates. The
315    /// function works in-place in the `input` slice, modifying its contents.
316    pub fn decode<'a, S: SSDVPacket>(
317        input: &mut [S],
318        output: &'a mut [S],
319    ) -> Result<&'a mut [S], DecoderError> {
320        let mut decoder = DecoderHelper::new(input, output)?;
321        decoder.init_output();
322        decoder.copy_systematic();
323        if !decoder.all_systematic_obtained() {
324            decoder.values_to_lagrange();
325            decoder.interpolate_missing();
326        }
327        Ok(&mut decoder.output[..usize::from(decoder.num_systematic)])
328    }
329}
330
331impl<'a, 'b, S: SSDVPacket> DecoderHelper<'a, 'b, S> {
332    fn new(input: &'a mut [S], output: &'b mut [S]) -> Result<Self, DecoderError> {
333        let input = Self::remove_duplicates_and_wrong_crcs(input);
334        let num_systematic = Self::find_num_systematic(input)?;
335        // Check here that !input.is_empty(), since we will access to input[0]
336        // below. In principle, num_systematic should be > 0, but if the packets
337        // are maliciously formed, perhaps it might be computed as 0 by the
338        // decoder.
339        if input.is_empty() || input.len() < usize::from(num_systematic) {
340            return Err(DecoderError::NotEnoughInput);
341        }
342        if output.len() < usize::from(num_systematic) {
343            return Err(DecoderError::OutputTooShort);
344        }
345        let callsign = input[0].callsign().clone();
346        Self::check_systematic_ids(input, num_systematic)?;
347        let (image_id, flags) = Self::find_image_id_flags(input)?;
348        let (image_width, image_height) = Self::find_image_dimensions(input)?;
349        Ok(DecoderHelper {
350            input,
351            output,
352            callsign,
353            num_systematic,
354            image_id,
355            image_width,
356            image_height,
357            flags,
358        })
359    }
360
361    fn remove_duplicates_and_wrong_crcs(input: &mut [S]) -> &mut [S] {
362        let mut len = input.len();
363        let mut j = 0;
364        while j < len {
365            if !input[j].crc32_is_valid() {
366                // remove wrong CRC
367                input.copy_within(j + 1..len, j);
368                len -= 1;
369                continue;
370            }
371            let id = input[j].packet_id();
372            let mut k = j + 1;
373            while k < len {
374                if input[k].packet_id() == id {
375                    // remove duplicate
376                    input.copy_within(k + 1..len, k);
377                    len -= 1;
378                } else {
379                    k += 1;
380                }
381            }
382            j += 1;
383        }
384        &mut input[..len]
385    }
386
387    fn find_num_systematic(input: &[S]) -> Result<u16, DecoderError> {
388        let mut id_eoi = None;
389        let mut from_fec_packets = None;
390        for packet in input {
391            if packet.is_eoi() {
392                if packet.is_fec_packet() {
393                    return Err(DecoderError::EoiOnFecPacket);
394                }
395                if id_eoi.is_some() {
396                    return Err(DecoderError::DuplicatedEoi);
397                }
398                id_eoi = Some(packet.packet_id());
399            }
400            if let Some(k) = packet.number_systematic_packets() {
401                if let Some(k2) = from_fec_packets {
402                    if k != k2 {
403                        return Err(DecoderError::NumSystematicMismatch);
404                    }
405                } else {
406                    from_fec_packets = Some(k);
407                }
408            }
409        }
410        match (id_eoi, from_fec_packets) {
411            (None, None) => Err(DecoderError::UnknownNumSystematic),
412            (Some(k), None) => Ok(k + 1),
413            (None, Some(k)) => Ok(k),
414            (Some(k), Some(k2)) => {
415                if k + 1 == k2 {
416                    Ok(k2)
417                } else {
418                    Err(DecoderError::EoiFecMismatch)
419                }
420            }
421        }
422    }
423
424    fn check_systematic_ids(input: &[S], num_systematic: u16) -> Result<(), DecoderError> {
425        for packet in input {
426            if !packet.is_fec_packet() && packet.packet_id() >= num_systematic {
427                return Err(DecoderError::WrongSystematicId);
428            }
429        }
430        Ok(())
431    }
432
433    fn find_image_id_flags(input: &[S]) -> Result<(u8, u8), DecoderError> {
434        let image_id = input[0].image_id();
435
436        fn clean_flags(flags: u8) -> u8 {
437            // remove EOI and FEC packet flags
438            flags & !0x44
439        }
440
441        let flags = clean_flags(input[0].flags());
442
443        for packet in input {
444            if packet.image_id() != image_id {
445                return Err(DecoderError::MultipleImageIds);
446            }
447            if clean_flags(packet.flags()) != flags {
448                return Err(DecoderError::InconsistentFlags);
449            }
450        }
451        Ok((image_id, flags))
452    }
453
454    fn find_image_dimensions(input: &[S]) -> Result<(u8, u8), DecoderError> {
455        let mut dimensions = None;
456        for packet in input {
457            if let Some(width) = packet.width() {
458                // if width is present, then height is also present
459                let height = packet.height().unwrap();
460                if let Some((w, h)) = dimensions {
461                    if w != width || h != height {
462                        return Err(DecoderError::DimensionsMismatch);
463                    }
464                } else {
465                    dimensions = Some((width, height))
466                }
467            }
468        }
469        dimensions.ok_or(DecoderError::NoSystematic)
470    }
471
472    fn init_output(&mut self) {
473        for packet in self.output.iter_mut() {
474            // this lets us know that the packet has not been recovered yet
475            packet.set_packet_id(Self::INVALID_PACKET_ID);
476        }
477    }
478
479    const INVALID_PACKET_ID: u16 = 0xffff;
480
481    fn copy_systematic(&mut self) {
482        for packet in self.input.iter() {
483            if !packet.is_fec_packet() {
484                let id = packet.packet_id();
485                self.output[usize::from(id)].clone_from(packet);
486            }
487        }
488    }
489
490    fn all_systematic_obtained(&self) -> bool {
491        !self.output[..usize::from(self.num_systematic)]
492            .iter()
493            .any(|&packet| packet.packet_id() == Self::INVALID_PACKET_ID)
494    }
495
496    // Computes
497    // w_j^{-1} = \prod_{m \neq j} (x_j - x_m).
498    //
499    // This is different from the wj_inv function used in Encoder because the
500    // packet_id's of the first k packets in the input buffer are not
501    // sequential.
502    fn wj_inv(&self, j: usize) -> GF64K {
503        let xj = GF64K::from(self.input[j].packet_id());
504        let mut ret = GF64K::from(1);
505        for (m, p) in self.input[0..usize::from(self.num_systematic)]
506            .iter()
507            .enumerate()
508        {
509            if m != j {
510                let xm = GF64K::from(p.packet_id());
511                ret *= xj - xm;
512            }
513        }
514        ret
515    }
516
517    fn values_to_lagrange(&mut self) {
518        // See Encoder::values_to_lagrange
519        for j in 0..usize::from(self.num_systematic) {
520            let wj = GF64K::from(1) / self.wj_inv(j);
521            let data = self.input[j].data_as_mut();
522            for word in data.chunks_exact_mut(2) {
523                let word: &mut [u8; 2] = word.try_into().unwrap();
524                let yj = GF64K::from(u16::from_be_bytes(*word));
525                let yj_wj = yj * wj;
526                *word = u16::from(yj_wj).to_be_bytes();
527            }
528        }
529    }
530
531    fn interpolate_missing(&mut self) {
532        // See Encoder::encode_fec_data
533        let k = usize::from(self.num_systematic);
534        for (j, packet) in self.output[..k]
535            .iter_mut()
536            .enumerate()
537            .filter(|(_, packet)| packet.packet_id() == Self::INVALID_PACKET_ID)
538        {
539            // Compute l(x)
540            let x = GF64K::from(j as u16);
541            let mut lx = GF64K::from(1);
542            for p in &self.input[..k] {
543                let xj = GF64K::from(p.packet_id());
544                lx *= x - xj;
545            }
546
547            // Compute \sum_{j=0}^{k-1} w_j y_j / (x - x_j) for each word in the
548            // output data
549            let data = packet.data_as_mut();
550            for (r, word) in data.chunks_exact_mut(2).enumerate() {
551                let mut sum = GF64K::from(0);
552                for p in &self.input[..k] {
553                    let wj_yj_s = p.data();
554                    let wj_yj = GF64K::from(u16::from_be_bytes(
555                        wj_yj_s[2 * r..2 * r + 2].try_into().unwrap(),
556                    ));
557                    let xj = GF64K::from(p.packet_id());
558                    sum += wj_yj / (x - xj);
559                }
560                let word: &mut [u8; 2] = word.try_into().unwrap();
561                let result = lx * sum;
562                *word = u16::from(result).to_be_bytes();
563            }
564
565            // Fill header
566            packet.set_fixed_fields();
567            packet.callsign_as_mut().copy_from_slice(&self.callsign);
568            packet.set_image_id(self.image_id);
569            packet.set_packet_id(j as u16);
570            packet.set_width(self.image_width);
571            packet.set_height(self.image_height);
572            packet.set_flags(self.flags);
573            packet.set_eoi(j == k - 1);
574            packet.set_fec_packet(false);
575
576            // Fill CRC32
577            packet.update_crc32();
578        }
579    }
580}
581
582#[cfg(test)]
583mod test {
584    use super::*;
585    use crate::{
586        SSDVParameters,
587        packet_formats::longjiang2::{Parameters, SSDVPacket},
588        test_data::IMG_230_SSDV,
589    };
590    use generic_array::typenum::Unsigned;
591
592    const PACKET_LEN: usize = <Parameters as SSDVParameters>::PacketLen::USIZE;
593
594    #[test]
595    fn encode_img_230_systematic() {
596        let mut ssdv = IMG_230_SSDV
597            .chunks_exact(PACKET_LEN)
598            .map(|chunk| SSDVPacket::new_from_slice(chunk).unwrap())
599            .collect::<Vec<SSDVPacket>>();
600        let encoder = Encoder::new(&mut ssdv).unwrap();
601
602        let mut encoded_packet = SSDVPacket::default();
603        for (j, packet) in IMG_230_SSDV.chunks_exact(PACKET_LEN).enumerate() {
604            let original_packet = SSDVPacket::new_from_slice(packet).unwrap();
605            encoder.encode(u16::try_from(j).unwrap(), &mut encoded_packet);
606            assert_eq!(&encoded_packet, &original_packet);
607        }
608    }
609
610    #[test]
611    fn encode_decode_img_230_one_every_n() {
612        let ssdv = IMG_230_SSDV
613            .chunks_exact(PACKET_LEN)
614            .map(|chunk| SSDVPacket::new_from_slice(chunk).unwrap())
615            .collect::<Vec<SSDVPacket>>();
616        let k = ssdv.len();
617        // Do a copy to keep ssdv as a reference (since the encoder destroys the input)
618        let mut ssdv_copy = ssdv.clone();
619        let encoder = Encoder::new(&mut ssdv_copy).unwrap();
620
621        for one_in_every in 1..=10 {
622            let mut encoded_packets = (0..one_in_every * k)
623                .step_by(one_in_every)
624                .map(|j| {
625                    let mut encoded_packet = SSDVPacket::default();
626                    encoder.encode(u16::try_from(j).unwrap(), &mut encoded_packet);
627                    encoded_packet
628                })
629                .collect::<Vec<SSDVPacket>>();
630
631            let mut output = vec![SSDVPacket::default(); k];
632            Decoder::decode(&mut encoded_packets[..], &mut output[..]).unwrap();
633            for (j, (s, o)) in ssdv.iter().zip(output.iter()).enumerate() {
634                assert_eq!(
635                    &s, &o,
636                    "SSDV packet {j} is different \
637                     (decoding with one in every {one_in_every} packet received)"
638                );
639            }
640        }
641    }
642}