1use super::{Block, Coding, Extension, Frame};
28use comnum_core::rng::Rng;
29use comnum_core::{Error, Result, ensure};
30use comnum_math::Jacobian;
31use comnum_systems::dvbs2::{Dvbs2, FrameSize, Modcod, PLHEADER_LEN, bbframe, bbframe_data, decode_plheader};
32
33pub(super) fn blocks() -> Vec<Extension> {
35 vec![
36 builtin!(
37 start "dvbs2_tx",
38 "DVB-S2 transmitter: BBFRAME, BCH and LDPC, bit interleaving, mapping, PL framing.",
39 Dvbs2Spec,
40 needs [],
41 gives [Samples, Payload],
42 |q, _, _| Ok(Box::new(Dvbs2Tx(Dvbs2Link::new(&q)?))),
43 ),
44 builtin!(
45 "dvbs2_rx",
46 "DVB-S2 receiver of frame-aligned PLFRAMEs: descrambling, demapping, decoding.",
47 Dvbs2Spec,
48 needs [Samples, Noise],
49 gives [Decoded],
50 |q, label, _| Ok(Box::new(Dvbs2Rx { link: Dvbs2Link::new(&q)?, label })),
51 ),
52 ]
53}
54
55block_params! {
56 struct Dvbs2Spec {
57 modulation: String ["QPSK"] one_of ["QPSK", "8PSK", "16APSK", "32APSK"], "modulation";
58 rate: String ["1/2"]
59 one_of ["1/4", "1/3", "2/5", "1/2", "3/5", "2/3", "3/4", "4/5", "5/6", "8/9", "9/10"],
60 "code rate (Table 12)";
61 frame: String ["normal"] one_of ["normal", "short", "64800", "16200"],
62 "FECFRAME of 64 800 bits (normal) or 16 200 bits (short)";
63 pilots: bool [true], "pilot blocks of 36 symbols every 16 slots";
64 scrambling_code: usize [0] <= 262_141, "number n of the Gold scrambling sequence";
65 frames: usize [1] >= 1, "PLFRAMEs per trial (dvbs2_tx; the receiver reads it from the frame)";
66 symbol_rate: f64 [1.0] "Bd" > 0, "symbol rate";
67 iterations: usize [50] >= 1, "LDPC iterations (sum-product, layered)";
68 max_log: bool [false], "max-log demapping instead of the exact LLRs";
69 }
70}
71
72struct Dvbs2Link {
73 link: Dvbs2,
74 frames: usize,
75 symbol_rate: f64,
76 max_log: bool,
77}
78
79impl Dvbs2Link {
80 fn new(p: &Dvbs2Spec) -> Result<Dvbs2Link> {
81 let frame: FrameSize = p.frame.parse().map_err(|e: Error| e.context("dvbs2"))?;
82 let mut link = Dvbs2::new(Modcod::from_name(&p.modulation, &p.rate)?, frame, p.pilots, p.scrambling_code)?;
83 link.fec_mut().ldpc_mut().decoder.max_iterations = p.iterations;
84 Ok(Dvbs2Link { link, frames: p.frames, symbol_rate: p.symbol_rate, max_log: p.max_log })
85 }
86}
87
88struct Dvbs2Tx(Dvbs2Link);
89
90impl Block for Dvbs2Tx {
91 fn apply(&self, s: &mut Frame, rng: &mut Rng) -> Result<()> {
92 let l = &self.0;
93 let k = l.link.k();
94 let data: Vec<u8> = (0..(k - 80) * l.frames).map(|_| rng.bit()).collect();
95 let mut samples = Vec::with_capacity(l.link.plframe_len() * l.frames);
96 for chunk in data.chunks(k - 80) {
97 samples.extend(l.link.modulate(&bbframe(chunk, k, 0)?)?);
98 }
99 let bits_per_symbol = l.link.modcod().modulation().bits_per_symbol();
100 *s = Frame {
101 trial: s.trial,
102 coding: Coding {
103 code_rate: Some(data.len() as f64 / (samples.len() * bits_per_symbol) as f64),
104 payload: data,
105 n_blocks: l.frames,
106 ..Coding::default()
107 },
108 sps: 1.0,
109 symbol_rate: l.symbol_rate,
110 ..Frame::default()
111 };
112 s.samples = samples;
113 Ok(())
114 }
115}
116
117struct Dvbs2Rx {
118 link: Dvbs2Link,
119 label: String,
120}
121
122impl Block for Dvbs2Rx {
123 fn apply(&self, s: &mut Frame, _: &mut Rng) -> Result<()> {
124 let l = &self.link;
125 let per_symbol = &s.receiver.symbol_noise;
128 let var = s
129 .receiver
130 .noise_var
131 .or(s.channel.noise_var)
132 .or((!per_symbol.is_empty()).then(|| per_symbol.iter().sum::<f64>() / per_symbol.len() as f64))
133 .ok_or_else(|| {
134 Error::Config("dvbs2_rx needs the noise variance: add a noise block (awgn) before it".into())
135 })?;
136 let len = l.link.plframe_len();
137 let frames = s.coding.n_blocks.max(1);
138 ensure(s.samples.len() >= len * frames, || {
139 format!("dvbs2_rx: {} samples for {frames} PLFRAMEs of {len} symbols", s.samples.len())
140 })?;
141 let number = l.link.modcod().number();
142 let (mut decoded, mut header_errors, mut crc_failures) = (Vec::new(), 0usize, 0usize);
143 for f in 0..frames {
144 let rx = &s.samples[f * len..(f + 1) * len];
145 if decode_plheader(&rx[..PLHEADER_LEN])?.modcod != number {
146 header_errors += 1;
147 }
148 let bb = l.link.fec().decode(&l.link.llr(rx, var, Jacobian::from_max_log(l.max_log))?)?;
149 let (data, crc_ok) = bbframe_data(&bb)?;
150 crc_failures += usize::from(!crc_ok);
151 decoded.extend(data);
152 }
153 s.record(&self.label, "plheader_errors", header_errors as f64);
154 s.record(&self.label, "crc_failures", crc_failures as f64);
155 s.coding.decoded = decoded;
156 Ok(())
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::blocks::build;
164 use serde_json::json;
165
166 #[test]
167 fn a_clean_channel_decodes_every_frame() {
168 let ctx = crate::blocks::BuildContext::default();
169 let p = json!({"modulation": "16APSK", "rate": "3/4", "frame": "short", "frames": 2});
170 let mut q = p.clone();
171 q["type"] = json!("dvbs2_tx");
172 let tx = build(&q, &ctx).unwrap();
173 q["type"] = json!("dvbs2_rx");
174 let rx = build(&q, &ctx).unwrap();
175 let mut s = Frame::default();
176 let mut rng = Rng::new(1);
177 tx.apply(&mut s, &mut rng).unwrap();
178 s.channel.noise_var = Some(1e-3);
179 rx.apply(&mut s, &mut rng).unwrap();
180 assert_eq!(s.coding.decoded, s.coding.payload);
181 assert_eq!(s.coding.payload.len(), 2 * (11712 - 80));
182 }
183}