1use super::tables::{POLAR_INTERLEAVER, POLAR_SEQUENCE, SUBBLOCK_PATTERN};
59use crate::block::{BlockCode, container_traits, expect_len};
60use crate::crc::Crc;
61use crate::polar::PolarCode;
62use crate::puncture::BitSelection;
63use comnum_core::signal::KNOWN_BIT_LLR;
64use comnum_core::{Error, Result, ensure};
65
66#[derive(Clone, Debug)]
68pub struct NrPolar {
69 a: usize,
70 e: usize,
71 crc: Crc,
72 il: Vec<usize>,
75 map: BitSelection,
77 shortened: Vec<usize>,
79 code: PolarCode,
81}
82
83struct Direction {
86 n_pc: usize,
89 n_pc_wm: usize,
90 i_il: bool,
93 i_bil: bool,
94 n_max: u32,
96}
97
98fn ceil_log2(x: usize) -> u32 {
100 x.next_power_of_two().trailing_zeros()
101}
102
103fn mother_length(k: usize, e: usize, n_max: u32) -> usize {
105 let ce = ceil_log2(e);
106 let n1 = if ce >= 1 && 8 * e <= 9 * (1 << (ce - 1)) && 16 * k < 9 * e { ce - 1 } else { ce };
107 let n2 = ceil_log2(8 * k);
108 1usize << n1.min(n2).min(n_max).max(5)
109}
110
111fn rate_matching_frozen(j: &[usize], k: usize, e: usize) -> (bool, Vec<bool>, Vec<usize>) {
114 let n = j.len();
115 let mut frozen = vec![false; n];
116 let puncturing = e < n && 16 * k <= 7 * e;
117 let mut shortened = Vec::new();
118 if e < n {
119 if puncturing {
120 for &p in &j[..n - e] {
121 frozen[p] = true;
122 }
123 let t = if 4 * e >= 3 * n {
124 (6 * n - 4 * e).div_ceil(8)
126 } else {
127 (9 * n - 4 * e).div_ceil(16)
129 };
130 for f in frozen.iter_mut().take(t) {
131 *f = true;
132 }
133 } else {
134 for &p in &j[e..] {
135 frozen[p] = true;
136 shortened.push(p);
137 }
138 }
139 }
140 (puncturing, frozen, shortened)
141}
142
143fn info_and_pc(frozen: &[bool], k: usize, d: &Direction) -> Result<(Vec<usize>, Vec<usize>)> {
148 let n = frozen.len();
149 let reliable: Vec<usize> = POLAR_SEQUENCE.iter().map(|&q| q as usize).filter(|&q| q < n && !frozen[q]).collect();
150 let used: Vec<usize> = reliable[reliable.len() - (k + d.n_pc)..].to_vec();
151 let mut pc = Vec::new();
152 if d.n_pc > 0 {
153 pc.extend_from_slice(&used[..d.n_pc - d.n_pc_wm]);
155 if d.n_pc_wm > 0 {
156 let best = used[d.n_pc..]
159 .iter()
160 .enumerate()
161 .min_by_key(|&(r, &p)| (p.count_ones(), std::cmp::Reverse(r)))
162 .map(|(_, &p)| p)
163 .ok_or_else(|| comnum_core::Error::Config("no position for the parity check".into()))?;
164 pc.push(best);
165 }
166 }
167 let info: Vec<usize> = used.iter().copied().filter(|p| !pc.contains(p)).collect();
168 Ok((info, pc))
169}
170
171fn triangular<T: Copy>(bits: &[T]) -> Vec<T> {
174 let e = bits.len();
175 let mut t = 0;
176 while t * (t + 1) / 2 < e {
177 t += 1;
178 }
179 let mut cell = vec![vec![None; t]; t];
181 let mut idx = 0;
182 for (i, row) in cell.iter_mut().enumerate() {
183 for c in row.iter_mut().take(t - i) {
184 if idx < e {
185 *c = Some(bits[idx]);
186 }
187 idx += 1;
188 }
189 }
190 let mut out = Vec::with_capacity(e);
191 for col in 0..t {
192 for row in cell.iter().take(t - col) {
193 if let Some(v) = row[col] {
194 out.push(v);
195 }
196 }
197 }
198 out
199}
200
201impl NrPolar {
202 pub fn from_name(link: &str, a: usize, e: usize) -> Result<NrPolar> {
208 match link.to_ascii_lowercase().as_str() {
209 "uplink" => NrPolar::uplink(a, e),
210 "downlink" => NrPolar::downlink(a, e),
211 _ => Err(Error::Config(format!("the link is uplink or downlink, got {link:?}"))),
212 }
213 }
214
215 pub fn uplink(a: usize, e: usize) -> Result<NrPolar> {
221 ensure(a >= 12, || format!("the uplink polar code takes A ≥ 12 bits, got {a}"))?;
222 ensure(a < 1013 && !(a >= 360 && e >= 1088), || {
223 format!("A = {a}, E = {e} needs two code blocks (Sec. 6.3.1.2.1), not supported")
224 })?;
225 let (crc, n_pc) = if a <= 19 { (Crc::crc6(), 3) } else { (Crc::crc11(), 0) };
226 let k = a + crc.width() as usize;
227 let n_pc_wm = usize::from(n_pc > 0 && e + n_pc > k + 192);
228 NrPolar::build(a, e, crc, &Direction { n_pc, n_pc_wm, i_il: false, i_bil: true, n_max: 10 })
229 }
230
231 pub fn downlink(a: usize, e: usize) -> Result<NrPolar> {
238 ensure(a >= 1 && a + 24 <= 164, || format!("the downlink polar code takes 1 ≤ A ≤ 140 bits, got {a}"))?;
239 NrPolar::build(a, e, Crc::crc24c(), &Direction { n_pc: 0, n_pc_wm: 0, i_il: true, i_bil: false, n_max: 9 })
240 }
241
242 fn build(a: usize, e: usize, crc: Crc, d: &Direction) -> Result<NrPolar> {
243 let k = a + crc.width() as usize;
244 ensure(e >= 1, || "E must be positive".to_string())?;
245 let n = mother_length(k, e, d.n_max);
246 ensure(k + d.n_pc <= e && k + d.n_pc <= n, || format!("K + n_PC = {} exceeds E = {e} or N = {n}", k + d.n_pc))?;
247 let b = n / 32;
249 let j: Vec<usize> = (0..n).map(|m| SUBBLOCK_PATTERN[32 * m / n] as usize * b + m % b).collect();
250 let (puncturing, frozen, shortened) = rate_matching_frozen(&j, k, e);
251 let (info, pc) = info_and_pc(&frozen, k, d)?;
252 let code = PolarCode::with_positions(n, &info, &pc)?;
253 let il: Vec<usize> = if d.i_il {
255 POLAR_INTERLEAVER.iter().map(|&m| m as usize).filter(|&m| m >= 164 - k).map(|m| m - (164 - k)).collect()
256 } else {
257 (0..k).collect()
258 };
259 let sel: Vec<usize> = (0..e)
261 .map(|t| {
262 if e >= n {
263 t % n
264 } else if puncturing {
265 t + n - e
266 } else {
267 t
268 }
269 })
270 .map(|y| j[y])
271 .collect();
272 let map = BitSelection::new(if d.i_bil { triangular(&sel) } else { sel }, n)?;
273 Ok(NrPolar { a, e, crc, il, map, shortened, code })
274 }
275
276 pub fn with_list(mut self, list: usize) -> Result<NrPolar> {
281 self.code = self.code.with_list(list)?;
282 Ok(self)
283 }
284
285 pub fn list(&self) -> usize {
287 self.code.list()
288 }
289
290 pub fn mother_length(&self) -> usize {
292 self.code.n()
293 }
294
295 pub fn code(&self) -> &PolarCode {
297 &self.code
298 }
299
300 pub fn bit_map(&self) -> &[usize] {
302 self.map.positions()
303 }
304}
305
306impl BlockCode for NrPolar {
307 fn k(&self) -> usize {
308 self.a
309 }
310
311 fn n(&self) -> usize {
312 self.e
313 }
314
315 fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
316 expect_len(info.len(), self.a, "bits")?;
317 let c = self.crc.attach(info);
318 let interleaved: Vec<u8> = self.il.iter().map(|&p| c[p]).collect();
319 self.map.select(&self.code.encode_block(&interleaved)?)
320 }
321
322 fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
323 expect_len(llr.len(), self.e, "LLRs")?;
324 let mut d = self.map.word_llr(llr)?;
325 for &p in &self.shortened {
326 d[p] = KNOWN_BIT_LLR;
327 }
328 let k = self.il.len();
329 let mut first = None;
330 for cand in self.code.decode_list(&d)? {
331 let mut c = vec![0u8; k];
332 for (&p, &b) in self.il.iter().zip(&cand) {
333 c[p] = b;
334 }
335 if self.crc.check(&c) {
336 return Ok(c[..self.a].to_vec());
337 }
338 first.get_or_insert(c);
339 }
340 Ok(first.map_or_else(|| vec![0; self.a], |c| c[..self.a].to_vec()))
341 }
342}
343
344container_traits!(NrPolar);
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn the_links_are_read_by_name() {
352 let same = |a: NrPolar, b: NrPolar| a.k() == b.k() && a.n() == b.n() && a.bit_map() == b.bit_map();
353 assert!(same(NrPolar::from_name("Uplink", 30, 100).unwrap(), NrPolar::uplink(30, 100).unwrap()));
354 assert!(same(NrPolar::from_name("downlink", 30, 100).unwrap(), NrPolar::downlink(30, 100).unwrap()));
355 assert!(!same(NrPolar::uplink(30, 100).unwrap(), NrPolar::downlink(30, 100).unwrap()));
356 assert!(NrPolar::from_name("sidelink", 30, 100).is_err());
357 }
358
359 #[test]
360 fn the_tables_are_permutations() {
361 let mut q: Vec<u16> = POLAR_SEQUENCE.to_vec();
362 q.sort_unstable();
363 assert!(q.iter().enumerate().all(|(i, &v)| v as usize == i));
364 let mut il: Vec<u8> = POLAR_INTERLEAVER.to_vec();
365 il.sort_unstable();
366 assert!(il.iter().enumerate().all(|(i, &v)| v as usize == i));
367 let mut p: Vec<u8> = SUBBLOCK_PATTERN.to_vec();
368 p.sort_unstable();
369 assert!(p.iter().enumerate().all(|(i, &v)| v as usize == i));
370 }
371
372 #[test]
373 fn puncturing_shortening_repetition_and_parity_checks_round_trip() {
374 for (a, e, down) in
377 [(30, 45, false), (30, 200, false), (40, 600, false), (14, 100, false), (14, 300, false), (39, 108, true)]
378 {
379 let code =
380 if down { NrPolar::downlink(a, e) } else { NrPolar::uplink(a, e) }.unwrap().with_list(8).unwrap();
381 let info: Vec<u8> = (0..a).map(|i| ((i * 5 + a) % 3 == 0) as u8).collect();
382 let c = code.encode_block(&info).unwrap();
383 assert_eq!(c.len(), e);
384 let llr: Vec<f64> = c.iter().map(|b| if *b == 0 { 2.0 } else { -2.0 }).collect();
385 assert_eq!(code.decode_block(&llr).unwrap(), info, "A {a}, E {e}");
386 }
387 assert!(NrPolar::uplink(11, 100).is_err() && NrPolar::uplink(400, 2000).is_err());
388 assert!(NrPolar::uplink(30, 100).unwrap().with_list(0).is_err());
389 }
390
391 #[test]
394 fn the_triangular_interleaver_reads_the_columns() {
395 let e: Vec<usize> = (0..10).collect();
396 assert_eq!(triangular(&e), [0, 4, 7, 9, 1, 5, 8, 2, 6, 3]);
397 assert_eq!(triangular(&e[..8]), [0, 4, 7, 1, 5, 2, 6, 3]);
399 }
400}