1use super::{
6 channel::{AwgnChannel, Channel},
7 factory::Ber,
8 interleaving::Interleaver,
9 modulation::{Demodulator, Modulation, Modulator},
10 puncturing::Puncturer,
11};
12use crate::{
13 decoder::{
14 LdpcDecoder,
15 factory::{DecoderFactory, DecoderImplementation},
16 },
17 encoder::{Encoder, Error},
18 gf2::GF2,
19 sparse::SparseMatrix,
20};
21use ndarray::Array1;
22use num_traits::{One, Zero};
23use rand::{Rng, distr::StandardUniform};
24use std::{
25 sync::mpsc::{self, Receiver, Sender, SyncSender, TryRecvError},
26 time::{Duration, Instant},
27};
28
29#[derive(Debug)]
33pub struct BerTest<Mod: Modulation, Dec = DecoderImplementation> {
34 decoder_implementation: Dec,
35 h: SparseMatrix,
36 num_workers: usize,
37 k: usize,
38 n: usize,
39 n_cw: usize,
40 rate: f64,
41 encoder: Encoder,
42 puncturer: Option<Puncturer>,
43 interleaver: Option<Interleaver>,
44 modulator: Mod::Modulator,
45 ebn0s_db: Vec<f32>,
46 statistics: Vec<Statistics>,
47 bch_max_errors: u64,
48 max_iterations: usize,
49 max_frame_errors: u64,
50 min_run_time: Duration,
51 max_run_time: Duration,
52 reporter: Option<Reporter>,
53 last_reported: Instant,
54}
55
56#[derive(Debug)]
60pub struct BerTestParameters<'a, Dec = DecoderImplementation> {
61 pub h: SparseMatrix,
63 pub decoder_implementation: Dec,
65 pub puncturing_pattern: Option<&'a [bool]>,
67 pub interleaving_columns: Option<isize>,
71 pub max_frame_errors: u64,
73 pub min_run_time: Option<Duration>,
78 pub max_run_time: Option<Duration>,
83 pub max_iterations: usize,
85 pub ebn0s_db: &'a [f32],
87 pub reporter: Option<Reporter>,
90 pub bch_max_errors: u64,
94 pub num_workers: usize,
96}
97
98#[derive(Debug)]
99struct Worker<Mod: Modulation> {
100 terminate_rx: Receiver<()>,
101 results_tx: Sender<WorkerResult>,
102 k: usize,
103 encoder: Encoder,
104 puncturer: Option<Puncturer>,
105 interleaver: Option<Interleaver>,
106 modulator: Mod::Modulator,
107 channel: AwgnChannel,
108 demodulator: Mod::Demodulator,
109 decoder: Box<dyn LdpcDecoder>,
110 max_iterations: usize,
111}
112
113#[derive(Debug, Clone)]
114struct WorkerResultOk {
115 bit_errors: u64,
116 frame_error: bool,
117 false_decode: bool,
118 iterations: u64,
119}
120
121type WorkerResult = Result<WorkerResultOk, ()>;
122
123#[derive(Debug, Clone, PartialEq)]
124struct CurrentStatistics {
125 num_frames: u64,
126 false_decodes: u64,
127 total_iterations: u64,
128 start: Instant,
129 ldpc: CurrentCodeStatistics,
130 bch: Option<CurrentCodeStatistics>,
131}
132
133#[derive(Debug, Clone, PartialEq)]
134struct CurrentCodeStatistics {
135 bit_errors: u64,
136 frame_errors: u64,
137 correct_iterations: u64,
138}
139
140#[derive(Debug, Clone, PartialEq)]
145pub struct Statistics {
146 pub ebn0_db: f32,
148 pub num_frames: u64,
150 pub total_iterations: u64,
152 pub false_decodes: u64,
157 pub average_iterations: f64,
159 pub elapsed: Duration,
161 pub throughput_mbps: f64,
163 pub ldpc: CodeStatistics,
165 pub bch: Option<CodeStatistics>,
167}
168
169#[derive(Debug, Clone, PartialEq)]
176pub struct CodeStatistics {
177 pub bit_errors: u64,
179 pub frame_errors: u64,
181 pub correct_iterations: u64,
183 pub ber: f64,
185 pub fer: f64,
187 pub average_iterations_correct: f64,
189}
190
191#[derive(Debug, Clone)]
196pub struct Reporter {
197 pub tx: Sender<Report>,
199 pub interval: Duration,
201}
202
203#[derive(Debug, Clone, PartialEq)]
208pub enum Report {
209 Statistics(Statistics),
213 Finished,
217}
218
219macro_rules! report {
220 ($self:expr, $current_statistics:expr, $ebn0_db:expr, $final:expr) => {
221 if let Some(reporter) = $self.reporter.as_ref() {
222 let now = Instant::now();
223 if $final || $self.last_reported + reporter.interval < now {
224 reporter
225 .tx
226 .send(Report::Statistics(Statistics::from_current(
227 &$current_statistics,
228 $ebn0_db,
229 $self.k,
230 )))
231 .unwrap();
232 $self.last_reported = now;
233 }
234 }
235 };
236}
237
238impl<Mod: Modulation, Dec: DecoderFactory> BerTest<Mod, Dec> {
239 pub fn new(parameters: BerTestParameters<'_, Dec>) -> Result<BerTest<Mod, Dec>, Error> {
247 let k = parameters.h.num_cols() - parameters.h.num_rows();
248 let n_cw = parameters.h.num_cols();
249 let puncturer = parameters.puncturing_pattern.map(Puncturer::new);
250 let interleaver = parameters
251 .interleaving_columns
252 .map(|n| Interleaver::new(n.unsigned_abs(), n < 0));
253 let puncturer_rate = if let Some(p) = puncturer.as_ref() {
254 p.rate()
255 } else {
256 1.0
257 };
258 let n = (n_cw as f64 / puncturer_rate).round() as usize;
259 let rate = k as f64 / n as f64;
260 Ok(BerTest {
261 decoder_implementation: parameters.decoder_implementation,
262 num_workers: parameters.num_workers,
263 k,
264 n,
265 n_cw,
266 rate,
267 encoder: Encoder::from_h(¶meters.h)?,
268 h: parameters.h,
269 puncturer,
270 interleaver,
271 modulator: Mod::Modulator::default(),
272 ebn0s_db: parameters.ebn0s_db.to_owned(),
273 statistics: Vec::with_capacity(parameters.ebn0s_db.len()),
274 bch_max_errors: parameters.bch_max_errors,
275 max_iterations: parameters.max_iterations,
276 max_frame_errors: parameters.max_frame_errors,
277 min_run_time: parameters.min_run_time.unwrap_or(Duration::ZERO),
278 max_run_time: parameters.max_run_time.unwrap_or(Duration::MAX),
279 reporter: parameters.reporter,
280 last_reported: Instant::now(),
281 })
282 }
283
284 pub fn run(mut self) -> Result<Vec<Statistics>, Box<dyn std::error::Error>> {
289 let ret = self.do_run();
290 if let Some(reporter) = self.reporter.as_ref() {
291 reporter.tx.send(Report::Finished).unwrap();
292 }
293 ret?;
294 Ok(self.statistics)
295 }
296
297 fn do_run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
298 self.last_reported = Instant::now();
299 for &ebn0_db in &self.ebn0s_db {
300 let ebn0 = 10.0_f64.powf(0.1 * f64::from(ebn0_db));
301 let esn0 = self.rate * Mod::BITS_PER_SYMBOL * ebn0;
302 let noise_sigma = (0.5 / esn0).sqrt();
303 let (results_tx, results_rx) = mpsc::channel();
304 let workers = std::iter::repeat_with(|| {
305 let (mut worker, terminate_tx) = self.make_worker(noise_sigma, results_tx.clone());
306 let handle = std::thread::spawn(move || worker.work());
307 (handle, terminate_tx)
308 })
309 .take(self.num_workers)
310 .collect::<Vec<_>>();
311
312 let mut current_statistics = CurrentStatistics::new(self.bch_max_errors > 0);
313 while !current_statistics.run_finished(
314 self.max_frame_errors,
315 self.min_run_time,
316 self.max_run_time,
317 ) {
318 match results_rx.recv().unwrap() {
319 Ok(result) => {
320 current_statistics.ldpc.bit_errors += result.bit_errors;
321 current_statistics.ldpc.frame_errors += u64::from(result.frame_error);
322 current_statistics.false_decodes += u64::from(result.false_decode);
323 current_statistics.total_iterations += result.iterations;
324 if !result.frame_error {
325 current_statistics.ldpc.correct_iterations += result.iterations;
326 }
327 current_statistics.num_frames += 1;
328 if let Some(bch) = &mut current_statistics.bch {
329 if result.bit_errors > self.bch_max_errors {
330 bch.bit_errors += result.bit_errors;
332 bch.frame_errors += 1;
333 } else {
334 bch.correct_iterations += result.iterations;
336 }
337 }
338 }
339 Err(()) => break,
340 }
341 report!(self, current_statistics, ebn0_db, false);
342 }
343 report!(self, current_statistics, ebn0_db, true);
344
345 for (_, terminate_tx) in workers.iter() {
346 let _ = terminate_tx.send(());
349 }
350
351 let mut join_error = None;
352 for (handle, _) in workers.into_iter() {
353 if let Err(e) = handle.join().unwrap() {
354 join_error = Some(e);
355 }
356 }
357 if let Some(e) = join_error {
358 return Err(e);
359 }
360
361 self.statistics.push(Statistics::from_current(
362 ¤t_statistics,
363 ebn0_db,
364 self.k,
365 ));
366 }
367 Ok(())
368 }
369
370 fn make_worker(
371 &self,
372 noise_sigma: f64,
373 results_tx: Sender<WorkerResult>,
374 ) -> (Worker<Mod>, SyncSender<()>) {
375 let (terminate_tx, terminate_rx) = mpsc::sync_channel(1);
376 (
377 Worker {
378 terminate_rx,
379 results_tx,
380 k: self.k,
381 encoder: self.encoder.clone(),
382 puncturer: self.puncturer.clone(),
383 interleaver: self.interleaver.clone(),
384 modulator: self.modulator.clone(),
385 channel: AwgnChannel::new(noise_sigma),
386 demodulator: Mod::Demodulator::from_noise_sigma(noise_sigma),
387 decoder: self.decoder_implementation.build_decoder(self.h.clone()),
388 max_iterations: self.max_iterations,
389 },
390 terminate_tx,
391 )
392 }
393}
394
395impl<Mod: Modulation, Dec: DecoderFactory> Ber for BerTest<Mod, Dec> {
396 fn run(self: Box<Self>) -> Result<Vec<Statistics>, Box<dyn std::error::Error>> {
397 BerTest::run(*self)
398 }
399
400 fn n(&self) -> usize {
401 self.n
402 }
403
404 fn n_cw(&self) -> usize {
405 self.n_cw
406 }
407
408 fn k(&self) -> usize {
409 self.k
410 }
411
412 fn rate(&self) -> f64 {
413 self.rate
414 }
415}
416
417impl<Mod: Modulation> Worker<Mod> {
418 fn work(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
419 let mut rng = rand::rng();
420 loop {
421 match self.terminate_rx.try_recv() {
422 Ok(()) => return Ok(()),
423 Err(TryRecvError::Disconnected) => panic!(),
424 Err(TryRecvError::Empty) => (),
425 };
426 let result = self.simulate(&mut rng);
427 let to_send = match result.as_ref() {
428 Ok(r) => Ok(r.clone()),
429 Err(_) => Err(()),
430 };
431 self.results_tx.send(to_send).unwrap();
432 result?;
433 }
434 }
435
436 fn simulate<R: Rng>(
437 &mut self,
438 rng: &mut R,
439 ) -> Result<WorkerResultOk, Box<dyn std::error::Error + Send + Sync + 'static>> {
440 let message = Self::random_message(rng, self.k);
441 let codeword = self.encoder.encode(&Self::gf2_array(&message));
442 let transmitted = match self.puncturer.as_ref() {
443 Some(p) => p.puncture(&codeword)?,
444 None => codeword,
445 };
446 let transmitted = match self.interleaver.as_ref() {
447 Some(i) => i.interleave(&transmitted),
448 None => transmitted,
449 };
450 let mut symbols = self.modulator.modulate(&transmitted);
451 self.channel.add_noise(rng, &mut symbols);
452 let llrs_demod = self.demodulator.demodulate(&symbols);
453 let llrs_decoder = match self.interleaver.as_ref() {
454 Some(i) => i.deinterleave(&llrs_demod),
455 None => llrs_demod,
456 };
457 let llrs_decoder = match self.puncturer.as_ref() {
458 Some(p) => p.depuncture(&llrs_decoder)?,
459 None => llrs_decoder,
460 };
461
462 let (decoded, iterations, success) =
463 match self.decoder.decode(&llrs_decoder, self.max_iterations) {
464 Ok(output) => (output.codeword, output.iterations, true),
465 Err(output) => (output.codeword, output.iterations, false),
466 };
467 let bit_errors = message
469 .iter()
470 .zip(decoded.iter())
471 .filter(|(a, b)| a != b)
472 .count() as u64;
473 let frame_error = bit_errors > 0;
474 let false_decode = frame_error && success;
475 Ok(WorkerResultOk {
476 bit_errors,
477 frame_error,
478 false_decode,
479 iterations: iterations as u64,
480 })
481 }
482
483 fn random_message<R: Rng>(rng: &mut R, size: usize) -> Vec<u8> {
484 rng.sample_iter(StandardUniform)
485 .map(<u8 as From<bool>>::from)
486 .take(size)
487 .collect()
488 }
489
490 fn gf2_array(bits: &[u8]) -> Array1<GF2> {
491 Array1::from_iter(
492 bits.iter()
493 .map(|&b| if b == 1 { GF2::one() } else { GF2::zero() }),
494 )
495 }
496}
497
498impl CurrentStatistics {
499 fn new(has_bch: bool) -> CurrentStatistics {
500 CurrentStatistics {
501 num_frames: 0,
502 false_decodes: 0,
503 total_iterations: 0,
504 start: Instant::now(),
505 ldpc: CurrentCodeStatistics::new(),
506 bch: if has_bch {
507 Some(CurrentCodeStatistics::new())
508 } else {
509 None
510 },
511 }
512 }
513
514 fn errors_for_termination(&self) -> u64 {
515 if let Some(bch) = &self.bch {
516 bch.frame_errors
517 } else {
518 self.ldpc.frame_errors
519 }
520 }
521
522 fn run_finished(
523 &self,
524 max_frame_errors: u64,
525 min_run_time: Duration,
526 max_run_time: Duration,
527 ) -> bool {
528 let elapsed = self.start.elapsed();
529 (self.errors_for_termination() >= max_frame_errors && elapsed >= min_run_time)
530 || elapsed >= max_run_time
531 }
532}
533
534impl CurrentCodeStatistics {
535 fn new() -> CurrentCodeStatistics {
536 CurrentCodeStatistics {
537 bit_errors: 0,
538 frame_errors: 0,
539 correct_iterations: 0,
540 }
541 }
542}
543
544impl Default for CurrentCodeStatistics {
545 fn default() -> CurrentCodeStatistics {
546 CurrentCodeStatistics::new()
547 }
548}
549
550impl CodeStatistics {
551 fn from_current(stats: &CurrentCodeStatistics, num_frames: u64, k: usize) -> CodeStatistics {
552 CodeStatistics {
553 bit_errors: stats.bit_errors,
554 frame_errors: stats.frame_errors,
555 correct_iterations: stats.correct_iterations,
556 ber: stats.bit_errors as f64 / (k as f64 * num_frames as f64),
557 fer: stats.frame_errors as f64 / num_frames as f64,
558 average_iterations_correct: stats.correct_iterations as f64
559 / (num_frames - stats.frame_errors) as f64,
560 }
561 }
562}
563
564impl Statistics {
565 fn from_current(stats: &CurrentStatistics, ebn0_db: f32, k: usize) -> Statistics {
566 let elapsed = Instant::now() - stats.start;
567 Statistics {
568 ebn0_db,
569 num_frames: stats.num_frames,
570 false_decodes: stats.false_decodes,
571 total_iterations: stats.total_iterations,
572 average_iterations: stats.total_iterations as f64 / stats.num_frames as f64,
573 elapsed,
574 throughput_mbps: 1e-6 * (k as f64 * stats.num_frames as f64) / elapsed.as_secs_f64(),
575 ldpc: CodeStatistics::from_current(&stats.ldpc, stats.num_frames, k),
576 bch: stats
577 .bch
578 .as_ref()
579 .map(|b| CodeStatistics::from_current(b, stats.num_frames, k)),
580 }
581 }
582}