1use super::tables::{BG1, BG2, LIFTING_SETS};
53use crate::block::{BlockCode, SoftDecode, container_traits, expect_len};
54use crate::interleaving::Interleaver;
55use crate::ldpc::{LdpcCode, LdpcDecoder};
56use crate::puncture::BitSelection;
57use comnum_core::signal::KNOWN_BIT_LLR;
58use comnum_core::{Result, ensure};
59
60type BaseGraph = [(u8, u8, [u16; 8])];
62
63#[derive(Clone, Debug)]
65pub struct NrLdpc {
66 bg: u8,
67 z: usize,
68 k_info: usize,
69 e: usize,
70 map: BitSelection,
73 code: LdpcCode,
75}
76
77fn base_graph(k_info: usize, e: usize) -> u8 {
79 let r = k_info as f64 / e as f64;
80 if k_info <= 292 || (k_info <= 3824 && r <= 0.67) || r <= 0.25 { 2 } else { 1 }
81}
82
83impl NrLdpc {
84 pub fn new(k_info: usize, e: usize) -> Result<NrLdpc> {
90 NrLdpc::with_options(k_info, e, None, 0, 1)
91 }
92
93 pub fn with_options(k_info: usize, e: usize, base_graph_override: Option<u8>, rv: u8, qm: usize) -> Result<NrLdpc> {
102 ensure(k_info >= 1 && e >= 1, || format!("need K' ≥ 1 and E ≥ 1, got {k_info} and {e}"))?;
103 ensure(rv <= 3 && qm >= 1 && e % qm == 0, || format!("rv ≤ 3 and Qm | E, got rv {rv}, Qm {qm}, E {e}"))?;
104 let bg = base_graph_override.unwrap_or_else(|| base_graph(k_info, e));
105 ensure(bg == 1 || bg == 2, || format!("the base graph is 1 or 2, got {bg}"))?;
106 let k_cb = if bg == 1 { 8448 } else { 3840 };
107 ensure(k_info <= k_cb, || {
108 format!("{k_info} bits exceed one code block of base graph {bg} ({k_cb}); segment the block first")
109 })?;
110 let kb = match (bg, k_info) {
111 (1, _) => 22,
112 (_, b) if b > 640 => 10,
113 (_, b) if b > 560 => 9,
114 (_, b) if b > 192 => 8,
115 _ => 6,
116 };
117 let (z, ils) = LIFTING_SETS
118 .iter()
119 .enumerate()
120 .flat_map(|(i, set)| set.iter().map(move |&z| (z, i)))
121 .filter(|&(z, _)| kb * z >= k_info)
122 .min()
123 .ok_or_else(|| comnum_core::Error::Config(format!("no lifting size for K' = {k_info}")))?;
124 let (table, cols_k, rows_n): (&BaseGraph, usize, usize) = if bg == 1 { (&BG1, 22, 46) } else { (&BG2, 10, 42) };
125 let k = cols_k * z;
126 let n_full = (cols_k + rows_n) * z;
127 let mut rows: Vec<Vec<usize>> = vec![Vec::new(); rows_n * z];
128 for &(i, j, v) in table {
129 let shift = v[ils] as usize % z;
130 for r in 0..z {
131 rows[i as usize * z + r].push(j as usize * z + (r + shift) % z);
132 }
133 }
134 let code = LdpcCode::from_core_rows(n_full, rows, k, 4 * z)?;
135 let ncb = n_full - 2 * z;
137 let num = match (bg, rv) {
138 (_, 0) => 0,
139 (1, 1) => 17,
140 (1, 2) => 33,
141 (1, _) => 56,
142 (_, 1) => 13,
143 (_, 2) => 25,
144 _ => 43,
145 };
146 let den = if bg == 1 { 66 } else { 50 };
147 let k0 = num * ncb / (den * z) * z;
148 let filler = |col: usize| col >= k_info && col < k;
149 let mut sel = Vec::with_capacity(e);
150 let mut j = 0;
151 while sel.len() < e {
152 let col = 2 * z + (k0 + j) % ncb;
153 if !filler(col) {
154 sel.push(col);
155 }
156 j += 1;
157 }
158 let map = BitSelection::new(Interleaver::block(qm, e / qm).interleave(&sel), code.n())?;
161 Ok(NrLdpc { bg, z, k_info, e, map, code })
162 }
163
164 pub fn base_graph(&self) -> u8 {
166 self.bg
167 }
168
169 pub fn lifting_size(&self) -> usize {
171 self.z
172 }
173
174 pub fn bit_map(&self) -> &[usize] {
176 self.map.positions()
177 }
178
179 pub fn code(&self) -> &LdpcCode {
181 &self.code
182 }
183
184 pub fn decoder_mut(&mut self) -> &mut LdpcDecoder {
186 &mut self.code.decoder
187 }
188
189 pub fn combine(&self, llr: &[f64], buffer: &mut [f64]) -> Result<()> {
198 expect_len(buffer.len(), self.code.n(), "buffer LLRs")?;
199 for (b, v) in buffer.iter_mut().zip(self.map.word_llr(llr)?) {
200 *b += v;
201 }
202 Ok(())
203 }
204
205 pub fn decode_buffer(&self, buffer: &[f64]) -> Result<Vec<u8>> {
210 expect_len(buffer.len(), self.code.n(), "buffer LLRs")?;
211 let mut full = buffer.to_vec();
212 for v in full.iter_mut().take(self.code.k()).skip(self.k_info) {
213 *v = KNOWN_BIT_LLR;
214 }
215 let (post, _) = self.code.decode_llr(&full)?;
216 Ok(comnum_core::signal::hard_decisions(&post[..self.k_info]))
217 }
218
219 fn full_llr(&self, llr: &[f64]) -> Result<Vec<f64>> {
222 let mut full = self.map.word_llr(llr)?;
223 for v in full.iter_mut().take(self.code.k()).skip(self.k_info) {
224 *v = KNOWN_BIT_LLR;
225 }
226 Ok(full)
227 }
228}
229
230impl BlockCode for NrLdpc {
231 fn k(&self) -> usize {
232 self.k_info
233 }
234
235 fn n(&self) -> usize {
236 self.e
237 }
238
239 fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
240 expect_len(info.len(), self.k_info, "bits")?;
241 let mut u = info.to_vec();
242 u.resize(self.code.k(), 0);
243 self.map.select(&self.code.encode_block(&u)?)
244 }
245
246 fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
247 expect_len(llr.len(), self.e, "LLRs")?;
248 let (post, _) = self.code.decode_llr(&self.full_llr(llr)?)?;
249 Ok(comnum_core::signal::hard_decisions(&post[..self.k_info]))
250 }
251}
252
253impl SoftDecode for NrLdpc {
254 fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
257 expect_len(llr.len(), self.e, "LLRs")?;
258 expect_len(out.len(), self.k_info, "a posteriori LLRs")?;
259 let mut full = self.full_llr(llr)?;
260 if let Some(a) = apriori {
261 expect_len(a.len(), self.k_info, "a priori LLRs")?;
262 for (v, a) in full.iter_mut().zip(a) {
263 *v += a;
264 }
265 }
266 let (post, it) = self.code.decode_llr(&full)?;
267 out.copy_from_slice(&post[..self.k_info]);
268 Ok(it)
269 }
270}
271
272container_traits!(NrLdpc);
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn soft_combining_of_redundancy_versions_recovers_what_one_cannot() {
280 use comnum_core::rng::Rng;
281 let (k, e) = (480, 528);
282 let codes: Vec<NrLdpc> =
283 [0u8, 2, 3, 1].iter().map(|&rv| NrLdpc::with_options(k, e, Some(2), rv, 2).unwrap()).collect();
284 let mut rng = Rng::new(5);
285 let info: Vec<u8> = (0..k).map(|_| u8::from(rng.uniform() < 0.5)).collect();
286 let sigma2: f64 = 1.6;
288 let mut buffer = vec![0.0; codes[0].code().n()];
289 let mut first = None;
290 for code in &codes {
291 let c = code.encode_block(&info).unwrap();
292 let llr: Vec<f64> = c
293 .iter()
294 .map(|b| {
295 let y = if *b == 0 { 1.0 } else { -1.0 } + rng.gaussian() * sigma2.sqrt();
296 2.0 * y / sigma2
297 })
298 .collect();
299 code.combine(&llr, &mut buffer).unwrap();
300 first.get_or_insert_with(|| code.decode_block(&llr).unwrap() == info);
301 }
302 assert_eq!(first, Some(false));
303 assert_eq!(codes[0].decode_buffer(&buffer).unwrap(), info);
304 let c = codes[0].encode_block(&info).unwrap();
306 let llr: Vec<f64> = c.iter().map(|b| if *b == 0 { 3.0 } else { -3.0 }).collect();
307 let mut one = vec![0.0; codes[0].code().n()];
308 codes[0].combine(&llr, &mut one).unwrap();
309 assert_eq!(codes[0].decode_buffer(&one).unwrap(), codes[0].decode_block(&llr).unwrap());
310 assert!(codes[0].combine(&llr[1..], &mut one).is_err());
311 }
312
313 #[test]
314 fn the_base_graphs_have_their_dimensions() {
315 assert_eq!((BG1.len(), BG2.len()), (316, 197));
316 assert_eq!(BG1.iter().map(|e| e.0).max(), Some(45));
317 assert_eq!(BG1.iter().map(|e| e.1).max(), Some(67));
318 assert_eq!(BG2.iter().map(|e| e.0).max(), Some(41));
319 assert_eq!(BG2.iter().map(|e| e.1).max(), Some(51));
320 for (t, kb) in [(&BG1[..], 22u8), (&BG2[..], 10)] {
322 for i in 4..=t.iter().map(|e| e.0).max().unwrap() {
323 let last = t.iter().filter(|e| e.0 == i).map(|e| e.1).max().unwrap();
324 assert_eq!(last, kb + i);
325 }
326 }
327 }
328
329 #[test]
330 fn every_lifting_size_encodes_codewords() {
331 for set in LIFTING_SETS {
332 for &z in set.iter().filter(|&&z| z <= 64) {
333 for bg in [1u8, 2] {
334 let kb = if bg == 1 { 22 } else { 10 };
335 let k_info = kb * z - 3;
336 let code = NrLdpc::with_options(k_info, 3 * k_info, Some(bg), 0, 1).unwrap();
337 assert_eq!(code.lifting_size(), z.max(code.lifting_size()));
338 let info: Vec<u8> = (0..k_info).map(|i| ((i * 7 + z) % 5 < 2) as u8).collect();
339 let mut u = info.clone();
340 u.resize(code.code().k(), 0);
341 assert!(code.code().is_codeword(&code.code().encode_block(&u).unwrap()));
342 assert_eq!(code.encode_block(&info).unwrap().len(), 3 * k_info);
343 }
344 }
345 }
346 assert!(NrLdpc::new(9000, 10000).is_err());
347 }
348
349 #[test]
352 fn the_selection_gives_the_former_rate_matching() {
353 let mut rng = comnum_core::rng::Rng::new(21);
354 for (k_info, e, rv, qm) in [(100, 300, 0, 1), (500, 1200, 2, 4), (1000, 1500, 3, 6), (60, 1000, 1, 2)] {
355 let code = NrLdpc::with_options(k_info, e, None, rv, qm).unwrap();
356 let k = code.code.k();
357 let z = code.z;
358 let ncb = code.code.n() - 2 * z;
359 let (num, den) = match (code.bg, rv) {
360 (_, 0) => (0, 1),
361 (1, 1) => (17, 66),
362 (1, 2) => (33, 66),
363 (1, _) => (56, 66),
364 (_, 1) => (13, 50),
365 (_, 2) => (25, 50),
366 _ => (43, 50),
367 };
368 let k0 = num * ncb / (den * z) * z;
369 let mut sel = Vec::new();
370 let mut j = 0;
371 while sel.len() < e {
372 let col = 2 * z + (k0 + j) % ncb;
373 if !(col >= k_info && col < k) {
374 sel.push(col);
375 }
376 j += 1;
377 }
378 let map: Vec<usize> = (0..e).map(|t| sel[(t % qm) * (e / qm) + t / qm]).collect();
379 assert_eq!(code.bit_map(), map);
380 let llr: Vec<f64> = (0..e).map(|_| rng.gaussian()).collect();
381 let mut full = vec![0.0; code.code.n()];
382 for (&i, &l) in map.iter().zip(&llr) {
383 full[i] += l;
384 }
385 for v in full.iter_mut().take(k).skip(k_info) {
386 *v = 30.0;
387 }
388 let new = code.full_llr(&llr).unwrap();
389 assert!(new.iter().zip(&full).all(|(a, b)| a.to_bits() == b.to_bits()));
390 let (post, _) = code.code.decode_llr(&full).unwrap();
391 let app = code.app(&llr, None).unwrap();
392 assert!(app.iter().zip(&post[..k_info]).all(|(a, b)| a.to_bits() == b.to_bits()));
393 assert_eq!(code.decode_block(&llr).unwrap(), comnum_core::signal::hard_decisions(&post[..k_info]));
394 }
395 }
396}