2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
42
43
44
45
46
47
48
49
50
51
52
53
54
55
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85template <
typename T,
typename InputType,
typename OutputType>
88 const T&
derived()
const noexcept {
return static_cast<
const T&>(*
this); }
90 T&
derived()
noexcept {
return static_cast<T&>(*
this); }
94
95
96
97
99 Vector<OutputType> res(in.get_n());
100 for (size_t i = 0; i < in.get_n(); ++i) res.set_component(i, derived()(in[i]));
105
106
107
108
110 const size_t n = in.get_n();
111 Vector<OutputType> res(n);
112 for (size_t i = 0; i < n; ++i) res.set_component(i, derived()(
std::move(in[i])));
117
118
119
120
122 Matrix<OutputType> res(in.get_m(), in.get_n());
123 for (size_t i = 0; i < res.get_m(); ++i) {
124 for (size_t j = 0; j < res.get_n(); ++j) res.set_component(i, j, derived()(in(i, j)));
130
131
132
133
135 if constexpr (std::is_same_v<InputType, OutputType>) {
137 for (size_t i = 0; i < in.get_m(); ++i) {
138 for (size_t j = 0; j < in.get_n(); ++j) in.set_component(i, j, derived()(
std::move(in(i, j))));
140 return std::move(in);
143 Matrix<OutputType> res(in.get_m(), in.get_n());
144 for (size_t i = 0; i < res.get_m(); ++i) {
145 for (size_t j = 0; j < res.get_n(); ++j) res.set_component(i, j, derived()(
std::move(in(i, j))));
152
153
154
155
156
157
158 template <FiniteFieldType U>
159 requires(!std::is_same_v<U, InputType>)
178template <
typename T,
typename ElementType>
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221template <FieldType T>
228
229
230
231
232
233
234
238
239
240
241
245 double get_pe()
const noexcept {
return error_dist.p(); }
248 double get_px()
const noexcept {
return erasure_dist.p(); }
251
252
253
254
259
260
261
262
263
264
269 throw std::
logic_error(
"Bhattacharyya parameter is not defined for channels with erasures");
276 unsigned long long error_trials{0};
277 unsigned long long error_failures_before_hit;
278 std::geometric_distribution<
unsigned long long> erasure_dist;
279 unsigned long long erasure_trials{0};
280 unsigned long long erasure_failures_before_hit;
283template <FieldType T>
285#ifndef CECCO_ERASURE_SUPPORT
286 if (px != 0.0)
throw std::invalid_argument(
"px != 0 requires CECCO_ERASURE_SUPPORT");
288 if (pe < 0.0 || pe > 1.0)
289 throw std::out_of_range(
"SDMEC error probability must be in [0,1], got: " + std::to_string(pe));
290 if (px < 0.0 || px > 1.0 - pe)
291 throw std::out_of_range(
"SDMEC erasure probability must be in [0," + std::to_string(1.0 - pe) +
292 "], got: " + std::to_string(px));
294 error_dist = std::geometric_distribution<
unsigned long long>(pe / (1.0 - px));
295 error_failures_before_hit = error_dist(
gen());
296 erasure_dist = std::geometric_distribution<
unsigned long long>(px);
298 erasure_failures_before_hit = erasure_dist(gen());
302template <FieldType T>
304 if (error_dist.p() == 0.0 && erasure_dist.p() == 0.0)
return in;
306 if (error_trials == error_failures_before_hit) {
307 res.randomize_force_change();
309 error_failures_before_hit = error_dist(
gen());
313#ifdef CECCO_ERASURE_SUPPORT
314 if (erasure_dist.p() == 0.0)
return res;
315 if (erasure_trials == erasure_failures_before_hit) {
318 erasure_failures_before_hit = erasure_dist(gen());
326template <FieldType T>
331 const double q =
static_cast<
double>(
T::
get_size());
345
346
347
348
349
350
351
352
364
365
366
367
368
369
373
374
375
376
377
378
389
390
391
392
393
394
395
396
397
398
399
405
406
407
408
409
410
411
415
416
417
418
420 if (in == Fp<2>(0))
return in;
425 double get_pe()
const noexcept {
return bsc.get_pe(); }
428
429
430
432 const double pe = bsc.get_pe();
434 if (pe == 0.0)
return 1.0;
435 if (pe == 1.0)
return 0.0;
437 return std::log2(1 + (1 - pe) *
std::pow(pe, pe / (1 - pe)));
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
468
469
470
471
472 constexpr NRZMapper(
double a,
double b)
noexcept : a(a), b(b) {}
475
476
479 constexpr double get_Eb()
const noexcept {
return (a * a) + (b * b) / 4.0; }
482 constexpr double get_a()
const noexcept {
return a; }
485 constexpr double get_b()
const noexcept {
return b; }
490
491
492
493
496 return {a - b / 2.0, 0};
498 return {a + b / 2.0, 0};
507
508
509
510
511
512
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
545
546
547
548
549
550 AWGN(
double EbNodB,
double a,
double b)
553 pe(calculate_pe(EbNodB, Eb, b)) {}
556
557
561 const double s = dist.stddev();
569 constexpr double get_pe()
const noexcept {
return pe; }
574
575
576
577
579 std::complex<
double> res(in.real() + dist(gen()), in.imag() + dist(gen()));
584
585
586
587
588
593 std::normal_distribution<
double> dist;
597
598
599
600
601
602
603 static double calculate_pe(
double EbNodB,
double Eb,
double b)
noexcept;
606double AWGN::calculate_pe(
double EbNodB,
double Eb,
double b)
noexcept {
609 const double EbN0_linear =
std::pow(10.0, EbNodB / 10.0);
614 const double constellation_snr = (b * b * EbN0_linear) / (4.0 * Eb);
616 return 0.5 *
std::erfc(
std::sqrt(constellation_snr));
620
621
622
623
624
625
626
627
628
629
630
631
632
633
639
640
641
642
643
647
648
649
650
651 std::complex<
double>
operator()(
const Fp<2>& in) {
return transmission(encoder(in)); }
660
661
662
663
664
668 constexpr double get_pe()
const noexcept {
return transmission.get_pe(); }
671 double get_sigma()
const noexcept {
return transmission.get_standard_deviation(); }
674
675
677 const long double b = encoder
.get_b();
678 const long double sigma = transmission.get_standard_deviation();
679 return std::expl(-(b * b) / (8.0L * sigma * sigma));
690 const double sigma = transmission.get_standard_deviation();
692 if (b == 0.0)
return 0.0;
693 if (sigma == 0.0)
return 1.0;
695 auto f_Y_star = [a, b, sigma](
double x) ->
double {
696 double m = 1.0 / (2.0 * sigma * std::sqrt(2.0 * std::numbers::pi));
697 double s0 =
std::exp(-0.5 *
std::pow((x - (a - b / 2.0)) / sigma, 2.0));
698 double s1 =
std::exp(-0.5 *
std::pow((x - (a + b / 2.0)) / sigma, 2.0));
699 return m * (s0 + s1);
702 auto g = [&f_Y_star](
double x) ->
double {
703 double fy = f_Y_star(x);
704 if (fy <= 0.0)
return 0.0;
705 return fy *
std::log2(fy);
708 const double K = 8.0;
709 const double mu0 = a - b / 2.0;
710 const double mu1 = a + b / 2.0;
711 const double lb =
std::min(mu0, mu1) - K * sigma;
712 const double ub =
std::max(mu0, mu1) + K * sigma;
716 const double h = (ub - lb) / N;
718 double sum = g(lb) + g(ub);
719 for (
int i = 1; i < N; ++i) {
720 double x = lb + i * h;
721 sum += (i % 2 == 0 ? 2.0 : 4.0) * g(x);
724 const double integral = h * sum / 3.0;
726 return std::clamp(-integral - std::log2(sigma * std::sqrt(2 * std::numbers::pi * std::numbers::e)), 0.0, 1.0);
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
751
752
753
757
758
759
763
764
765
766
779
780
781
782
790
791
792
793
794
795
796
797
798
799
800
801
808
809
810
811
816
817
818
825
826
827
828
829 double operator()(
const std::complex<
double>& in)
noexcept {
return b * (a - in.real()) / sigmasq; }
834 const double sigmasq{};
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858template <FiniteFieldType E, FiniteFieldType S>
865
866
867
868
872
873
874
875
879template <FiniteFieldType E, FiniteFieldType S>
898
899
900
901
902
903
904
905
906
907
908
913 constexpr MUX() =
default;
916
917
918
919
923
924
925
926
930template <FiniteFieldType S, FiniteFieldType E>
940
941
942
943
944
945
946
947
948
949
950
951template <
class LHS,
class RHS>
953 requires(RHS&& r, LHS&& x) { std::forward<RHS>(r)(std::forward<LHS>(x)); }
955 return std::forward<RHS>(rhs)(std::forward<LHS>(lhs));
959
960
961
962
963
964
965
966
967
968
969
970template <
class LHS,
class RHS>
975 dst = std::forward<LHS>(lhs);
Additive White Gaussian Noise channel for complex-valued symbols.
double get_standard_deviation() const noexcept
Noise standard deviation σ (per real component).
double get_capacity() const noexcept
Shannon capacity in bits per symbol (real signaling).
constexpr double get_pe() const noexcept
Theoretical hard-decision bit error probability for the configured constellation/SNR.
double get_variance() const noexcept
Noise variance σ² (per real component).
AWGN(double EbNodB, double a, double b)
Construct with SNR and constellation parameters.
std::complex< double > operator()(const std::complex< double > &in)
Add Gaussian noise to a complex symbol.
Binary Asymmetric Channel (Z-channel) — 0 is preserved; 1 flips to 0 with probability p.
double get_capacity() const noexcept
Shannon capacity in bits per symbol.
BAC(double p)
Construct with flip probability p.
Fp< 2 > operator()(const Fp< 2 > &in)
Process one bit.
double get_pe() const noexcept
Flip probability p (probability that 1 → 0).
Binary Erasure Channel — symbols are received correctly or marked erased.
BEC(double px)
Construct with symbol erasure probability px.
Binary-Input AWGN — fused NRZMapper + AWGN block.
double get_capacity() const noexcept
Shannon capacity in bits per symbol.
double get_sigma() const noexcept
Noise standard deviation σ.
const AWGN & get_transmission() const noexcept
Internal AWGN block (for noise statistics).
constexpr double get_pe() const noexcept
Theoretical hard-decision bit error probability for this constellation/SNR.
const NRZMapper & get_encoder() const noexcept
Internal NRZ mapper (for constructing matching demappers/LLR calculators).
long double get_Bhattacharyya_param() const noexcept
Bhattacharyya parameter γ = exp(−b²/(8σ²)).
std::complex< double > operator()(const Fp< 2 > &in)
Map a bit and add noise.
BI_AWGN(double EbN0dB, double a=0.0, double b=2.0)
Construct with SNR and constellation parameters.
BPSK hard-decision demapper — NRZDemapper with threshold 0.
constexpr BPSKDemapper() noexcept
Construct BPSK demapper (threshold 0).
Binary Phase Shift Keying mapper — NRZMapper with a = 0, b = 2.
constexpr BPSKMapper() noexcept
Construct BPSK mapper (a = 0, b = 2).
Field demultiplexer — expand 𝔽_E elements into 𝔽_S coefficient vectors/matrices.
constexpr DEMUX()=default
constexpr Vector< S > operator()(const E &in)
Expand a single element into its subfield-coefficient vector.
Log-Likelihood Ratio calculator for NRZ-over-AWGN soft demodulation.
LLRCalculator(const NRZMapper &nrz, const AWGN &transmission) noexcept
Construct from a matching mapper and channel.
LLRCalculator(const BI_AWGN &bi_awgn) noexcept
Construct from a BI-AWGN channel (convenience).
double operator()(const std::complex< double > &in) noexcept
Compute LLR of a received symbol.
Non-Return-to-Zero (NRZ) hard-decision demapper.
NRZDemapper(const BI_AWGN &bi_awgn) noexcept
Construct from a BI-AWGN channel (convenience).
constexpr NRZDemapper(const NRZMapper &nrz) noexcept
Construct from the corresponding mapper.
constexpr Fp< 2 > operator()(const std::complex< double > &in) noexcept
Hard-decide a received symbol.
Non-Return-to-Zero (NRZ) mapper for binary modulation.
constexpr std::complex< double > operator()(const Fp< 2 > &in) noexcept
Map a bit to its constellation point.
constexpr double get_b() const noexcept
Constellation distance parameter b.
constexpr double get_Eb() const noexcept
Energy per bit Eb = a² + b²/4.
constexpr NRZMapper(double a, double b) noexcept
Construct with constellation parameters.
constexpr double get_a() const noexcept
DC offset parameter a.
Symmetric Discrete Memoryless Erasure Channel (SDMEC) over any finite field 𝔽_q.
double get_pe() const noexcept
Observed symbol error probability pe.
double get_px() const noexcept
Observed symbol erasure probability px.
T operator()(const T &in)
Process one symbol.
SDMEC(double pe, double px=0.0)
Construct with error probability pe and erasure probability px.
long double get_Bhattacharyya_param() const
Bhattacharyya parameter γ = 2·√(pe·(1 − pe)).
double get_capacity() const noexcept
Shannon capacity in bits per symbol.
CRTP base providing element-wise, vector, and matrix operator() overloads.
Vector< OutputType > operator()(Vector< InputType > &&in)
Apply the block element-wise (rvalue input).
Vector< OutputType > operator()(const Vector< InputType > &in)
Apply the block element-wise to each vector entry.
const T & derived() const noexcept
Mixin that deletes the copy operations and defaults the move operations.
Contains implementation details not to be exposed to the user. Functions and classes here may change ...
BlockProcessor< T, std::complex< double >, Fp< 2 > > DecoderProcessor
CRTP base for blocks mapping complex symbols back to bits (hard demod).
BlockProcessor< T, ElementType, ElementType > SameTypeProcessor
CRTP base for blocks where input and output element types coincide.
BlockProcessor< T, Fp< 2 >, std::complex< double > > EncoderProcessor
CRTP base for blocks mapping bits to complex symbols (e.g. NRZ/BPSK modulation).
BlockProcessor< T, std::complex< double >, double > LLRProcessor
CRTP base for blocks producing log-likelihood ratios from complex symbols.
Provides a framework for error correcting codes.
std::mt19937 & gen()
Current thread's random number generator.
decltype(auto) operator>>(LHS &&lhs, RHS &&rhs)
Function-call chaining: x >> f ≡ f(x).
RHS & operator>>(LHS &&lhs, RHS &dst)
Assignment chaining: x >> dst ≡ dst = x; return dst.