]> code.octet-stream.net Git - m17rt/commitdiff
Convolutional encoding
authorThomas Karpiniec <tom.karpiniec@outlook.com>
Wed, 8 Jan 2025 09:35:44 +0000 (20:35 +1100)
committerThomas Karpiniec <tom.karpiniec@outlook.com>
Wed, 8 Jan 2025 09:35:44 +0000 (20:35 +1100)
.gitignore
demod/src/main.rs
m17core/src/decode.rs
m17core/src/encode.rs [new file with mode: 0644]
m17core/src/fec.rs
m17core/src/lib.rs

index eb5a316cbd195d26e3f768c7dd8e1b47299e17f8..54e04cb6bce8975ddf417992f4f13dfdc44dc2d6 100644 (file)
@@ -1 +1,2 @@
 target
 target
+*.kate-swp
index 563b7218cc3bece260818bd23504fa5a5af29ce9..d0e562d86400c96437c6789555fe61d78730c39c 100755 (executable)
@@ -4,9 +4,9 @@ use m17codec2::Codec2Adapter;
 use std::path::PathBuf;
 
 pub fn m17app_test() {
 use std::path::PathBuf;
 
 pub fn m17app_test() {
-    //let path = PathBuf::from("../../Data/test_vk7xt.rrc");
-    //let source = InputRrcFile::new(path);
-    let source = InputSoundcard::new();
+    let path = PathBuf::from("../../Data/test_vk7xt.rrc");
+    let source = InputRrcFile::new(path);
+    //let source = InputSoundcard::new();
     let soundmodem = Soundmodem::new_with_input(source);
     let app = M17App::new(soundmodem);
     app.add_stream_adapter(Codec2Adapter::new());
     let soundmodem = Soundmodem::new_with_input(source);
     let app = M17App::new(soundmodem);
     app.add_stream_adapter(Codec2Adapter::new());
index ba21a27c1522febbd924bca9404afa7d42e8cb13..de131246189bee791e9a0267cd394d2cd07229b0 100755 (executable)
@@ -88,6 +88,7 @@ pub(crate) fn frame_initial_decode(frame: &[f32] /* length 192 */) -> [u8; 46] {
 
 pub(crate) fn parse_lsf(frame: &[f32] /* length 192 */) -> Option<LsfFrame> {
     let deinterleaved = frame_initial_decode(frame);
 
 pub(crate) fn parse_lsf(frame: &[f32] /* length 192 */) -> Option<LsfFrame> {
     let deinterleaved = frame_initial_decode(frame);
+    debug!("deinterleaved: {:?}", deinterleaved);
     let lsf = match fec::decode(&deinterleaved, 240, p_1) {
         Some(lsf) => LsfFrame(lsf),
         None => return None,
     let lsf = match fec::decode(&deinterleaved, 240, p_1) {
         Some(lsf) => LsfFrame(lsf),
         None => return None,
diff --git a/m17core/src/encode.rs b/m17core/src/encode.rs
new file mode 100644 (file)
index 0000000..70fe012
--- /dev/null
@@ -0,0 +1,3 @@
+use crate::protocol::LsfFrame;
+
+pub(crate) fn encode_lsf(frame: &LsfFrame) {}
index 0716be0a38ab4e804cd216001b7d2aab8b25d170..5a49f870d9fc92abc28002ec45dd0fa6d066ff67 100755 (executable)
@@ -269,3 +269,91 @@ pub(crate) fn decode(
         Some(out)
     }
 }
         Some(out)
     }
 }
+
+/// Perform convolutional encoding on payload.
+///
+/// Four flush bits will be appended automatically.
+///
+/// Parameters:
+/// * `type1`: Type 1 bits, maximum length 30 bytes
+/// * `input_len`: Number of type 1 bits, up to 240
+/// * `puncture`: Puncturing scheme - `p_1`, `p_2` or `p_3`
+///
+/// Returns up to 368 type 3 bits. Caller is responsible for knowing the number of
+/// filled bits in the returned array, which is known statically by the type of
+/// payload and puncturing scheme in use.
+pub(crate) fn encode(
+    type1: &[u8],
+    input_len: usize,
+    puncture: fn(usize) -> (bool, bool),
+) -> [u8; 46] {
+    let mut out = [0u8; 46];
+    let bits = Bits::new(type1);
+    let mut out_idx = 0;
+    let mut out_bits = BitsMut::new(&mut out);
+    let mut state: u8 = 0;
+    for (t1_idx, b) in bits
+        .iter()
+        .take(input_len)
+        .chain(core::iter::repeat_n(0, 4))
+        .enumerate()
+    {
+        let (use_g1, use_g2) = puncture(t1_idx);
+        if use_g1 {
+            let g1 = (b + ((state & 0x02) >> 1) + (state & 0x01)) & 0x01;
+            out_bits.set_bit(out_idx, g1);
+            out_idx += 1;
+        }
+        if use_g2 {
+            let g2 = (b + ((state & 0x08) >> 3) + ((state & 0x04) >> 2) + (state & 0x01)) & 0x01;
+            out_bits.set_bit(out_idx, g2);
+            out_idx += 1;
+        }
+        state = (state >> 1) | (b << 3);
+    }
+    out
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn lsf_fec_round_trip() {
+        let lsf = [
+            255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 131, 53,
+        ];
+        let expected_encoded = [
+            222, 73, 36, 146, 73, 37, 182, 219, 109, 76, 0, 0, 0, 5, 191, 47, 25, 186, 30, 214,
+            237, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 153, 208,
+            119,
+        ];
+        let encoded = encode(&lsf, 240, p_1);
+        assert_eq!(encoded, expected_encoded);
+        let decoded = decode(&encoded, 240, p_1);
+        assert_eq!(decoded, Some(lsf));
+    }
+
+    #[test]
+    fn fec_damage() {
+        let lsf = [
+            255, 255, 255, 255, 255, 255, 0, 0, 0, 159, 221, 81, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 131, 53,
+        ];
+        let mut encoded = encode(&lsf, 240, p_1);
+
+        // progressively flip more bits
+        for idx in [50, 90, 51, 200, 15, 7, 100] {
+            let mut bits = BitsMut::new(&mut encoded);
+            let bit = bits.get_bit(idx);
+            bits.set_bit(idx, if bit == 1 { 0 } else { 1 });
+            let decoded = decode(&encoded, 240, p_1);
+            if idx == 100 {
+                assert_eq!(decoded, None); // 7 bits is too much damage
+            } else {
+                assert_eq!(decoded, Some(lsf)); // recovered from errors
+            }
+        }
+    }
+}
index b10427b4550a6af79b7ea29be483dac7847bdd92..eb4a4579607ba9ec9d59b8329b0d8ea154cef089 100755 (executable)
@@ -10,6 +10,7 @@ pub mod tnc;
 
 mod bits;
 mod decode;
 
 mod bits;
 mod decode;
+mod encode;
 mod fec;
 mod interleave;
 mod random;
 mod fec;
 mod interleave;
 mod random;