comnum_mapping/imperfect_csi.rs
1//! Error rates over Rayleigh fading with an imperfect channel estimate (Chang & Su 2002).
2//!
3//! # Model
4//!
5//! The received sample of a subcarrier is $Y = HX + N$, $H$ zero-mean circular Gaussian
6//! of variance $2\sigma_{1}^{2}$, $N$ of variance $2\sigma_{n}^{2}$; the receiver
7//! equalises with an estimate $\hat{H}$ jointly Gaussian with $H$, of variance
8//! $2\sigma_{2}^{2}$ and cross-correlation
9//! $\frac{1}{2}\mathbb{E}[\hat{H} H^{*}] = \mu_{1} + j\mu_{2}$ — every linear pilot-assisted estimate (LS, LMMSE, interpolations) has this
10//! form. With $\rho_{i} = \mu_{i}/(\sigma_{1}\sigma_{2})$, $\rho^{2} = \rho_{1}^{2} +
11//! \rho_{2}^{2}$ and the mean SNR per bit $\bar{\gamma}_{b} = 2\sigma_{1}^{2}
12//! \mathbb{E}|X|^{2}/(2\sigma_{n}^{2} K)$ ($K$ bits per symbol), the bit error
13//! probabilities of M. X. Chang and Y. T. Su (*IEEE Trans. Wireless Commun.* 1(4), 2002)
14//! are, for BPSK (their eq. 31) and Gray QPSK (eq. 32),
15//!
16//! $$
17//! P_{b} = \frac{1}{2}\left[1 - \frac{\rho_{1}}{\sqrt{1 + 1/\bar{\gamma}_{b} -
18//! \rho_{2}^{2}}}\right], \qquad
19//! P_{b} = \frac{1}{2}\left[1 - \frac{1}{2}\frac{(\rho_{1} + \rho_{2})/\sqrt{2}}{\sqrt{1 +
20//! \frac{1}{2\bar{\gamma}_{b}} - \frac{(\rho_{1} - \rho_{2})^{2}}{2}}} - \frac{1}{2}
21//! \frac{(\rho_{1} - \rho_{2})/\sqrt{2}}{\sqrt{1 + \frac{1}{2\bar{\gamma}_{b}} -
22//! \frac{(\rho_{1} + \rho_{2})^{2}}{2}}}\right],
23//! $$
24//!
25//! for Gray 16-QAM their eq. 53, a sum of 24 terms of the coefficients of their Table I,
26//! which reduces with $\hat{H} = H$ to their eq. 69,
27//!
28//! $$
29//! P_{b} = \frac{1}{2} - \frac{3}{8}\frac{1}{\sqrt{1 + \frac{5}{2\bar{\gamma}_{b}}}} -
30//! \frac{1}{4}\frac{1}{\sqrt{1 + \frac{5}{18\bar{\gamma}_{b}}}} + \frac{1}{8}
31//! \frac{1}{\sqrt{1 + \frac{1}{10\bar{\gamma}_{b}}}},
32//! $$
33//!
34//! (whose expansion is $P_{b} \approx 0.4972/\bar{\gamma}_{b}$ at high SNR; the paper prints
35//! $0.4952/\bar{\gamma}_{b}$), and for differential detection over Jakes' fading of normalised Doppler $f_{D}T_{s}$
36//! (eqs. 40, 41)
37//!
38//! $$
39//! P_{b}^{\mathrm{DBPSK}} = \frac{1}{2}\left[1 - \frac{J_{0}(2\pi f_{D}T_{s})}{1 +
40//! 1/\bar{\gamma}_{b}}\right], \qquad
41//! P_{b}^{\mathrm{DQPSK}} = \frac{1}{2}\left[1 - \frac{J_{0}(2\pi f_{D}T_{s})}{\sqrt{(2 +
42//! 1/\bar{\gamma}_{b})(1 + 1/(2\bar{\gamma}_{b})) - J_{0}^{2}(2\pi f_{D}T_{s})}}\right].
43//! $$
44//!
45//! # Examples
46//! ```
47//! use comnum_mapping::imperfect_csi::{ber_bpsk_imperfect_csi, ber_16qam_imperfect_csi};
48//! use comnum_mapping::theory::ber_rayleigh_mrc;
49//! let g = 100.0; // 20 dB
50//! // a perfect estimate: the BER of flat Rayleigh fading
51//! assert!((ber_bpsk_imperfect_csi(g, 1.0, 0.0)? - ber_rayleigh_mrc(g, 1)?).abs() < 1e-15);
52//! // an estimate correlated at 0.999: an error floor
53//! assert!(ber_16qam_imperfect_csi(1e9, 0.999, 0.0, 1.0)? > 1e-3);
54//! // an estimate correlated above 1 is an error, not a NaN
55//! assert!(ber_bpsk_imperfect_csi(g, 1.1, 0.0).is_err());
56//! # Ok::<(), comnum_core::Error>(())
57//! ```
58//!
59//! # References
60//!
61//! 1. M.-X. Chang, Y. T. Su, "Performance analysis of equalized OFDM systems in Rayleigh
62//! fading," *IEEE Trans. Wireless Commun.* 1(4), 721–732, 2002.
63
64use crate::constellation::{Constellation, ConstellationKind};
65use comnum_core::{Error, Result, ensure};
66use comnum_math::bessel_j0;
67use std::f64::consts::{FRAC_1_SQRT_2, PI};
68
69/// The BER of the constellation `c` equalised by an estimate of correlations
70/// $(\rho_{1}, \rho_{2})$ and of standard deviation ratio `sigma_ratio` at the mean SNR
71/// per bit `mean_ebn0_lin` (linear): [`ber_bpsk_imperfect_csi`] for BPSK,
72/// [`ber_qpsk_imperfect_csi`] for Gray QPSK (4-QAM or 4-PSK) and
73/// [`ber_16qam_imperfect_csi`] for Gray 16-QAM (`sigma_ratio` is used by 16-QAM only:
74/// the PSK decisions do not depend on $|\hat{H}|$).
75///
76/// # Errors
77/// [`Error::Config`] for another constellation, and the errors of the three
78/// functions.
79///
80/// # Examples
81/// ```
82/// use comnum_mapping::constellation::Constellation;
83/// use comnum_mapping::imperfect_csi::{ber_16qam_imperfect_csi, ber_imperfect_csi};
84/// let c = Constellation::from_name("16qam")?;
85/// assert_eq!(ber_imperfect_csi(&c, 1e5, 0.999, 0.0, 1.0)?, ber_16qam_imperfect_csi(1e5, 0.999, 0.0, 1.0)?);
86/// # Ok::<(), comnum_core::Error>(())
87/// ```
88pub fn ber_imperfect_csi(c: &Constellation, mean_ebn0_lin: f64, rho1: f64, rho2: f64, sigma_ratio: f64) -> Result<f64> {
89 match (c.kind(), c.order()) {
90 (ConstellationKind::Psk, 2) => ber_bpsk_imperfect_csi(mean_ebn0_lin, rho1, rho2),
91 (ConstellationKind::Qam | ConstellationKind::Psk, 4) => ber_qpsk_imperfect_csi(mean_ebn0_lin, rho1, rho2),
92 (ConstellationKind::Qam, 16) => ber_16qam_imperfect_csi(mean_ebn0_lin, rho1, rho2, sigma_ratio),
93 (kind, m) => Err(Error::Config(format!(
94 "the BER with an imperfect estimate is for BPSK, QPSK and 16-QAM, got {m}-{}",
95 kind.name()
96 ))),
97 }
98}
99
100/// [`comnum_core::Error::Config`] unless the mean SNR is positive and the correlations of
101/// the estimate satisfy $\rho_{1}^{2} + \rho_{2}^{2} \le 1$.
102fn check(mean_ebn0_lin: f64, rho1: f64, rho2: f64) -> Result<()> {
103 ensure_snr(mean_ebn0_lin)?;
104 ensure(rho1.is_finite() && rho2.is_finite() && rho1 * rho1 + rho2 * rho2 <= 1.0, || {
105 format!("the correlations of the estimate need ρ1² + ρ2² ≤ 1, got ({rho1}, {rho2})")
106 })
107}
108
109/// [`comnum_core::Error::Config`] unless the mean SNR per bit is positive.
110fn ensure_snr(mean_ebn0_lin: f64) -> Result<()> {
111 ensure(mean_ebn0_lin > 0.0, || format!("the mean SNR per bit must be positive, got {mean_ebn0_lin}"))
112}
113
114/// The BER of BPSK equalised by an estimate of correlations $(\rho_{1}, \rho_{2})$ at the
115/// mean SNR per bit `mean_ebn0_lin` (linear; Chang & Su eq. 31).
116///
117/// # Errors
118/// [`comnum_core::Error::Config`] for a mean SNR that is not positive or
119/// $\rho_{1}^{2} + \rho_{2}^{2} > 1$.
120pub fn ber_bpsk_imperfect_csi(mean_ebn0_lin: f64, rho1: f64, rho2: f64) -> Result<f64> {
121 check(mean_ebn0_lin, rho1, rho2)?;
122 Ok(0.5 * (1.0 - rho1 / (1.0 + 1.0 / mean_ebn0_lin - rho2 * rho2).sqrt()))
123}
124
125/// The BER of Gray QPSK equalised by an estimate of correlations $(\rho_{1}, \rho_{2})$
126/// (Chang & Su eq. 32).
127///
128/// # Errors
129/// As [`ber_bpsk_imperfect_csi`].
130pub fn ber_qpsk_imperfect_csi(mean_ebn0_lin: f64, rho1: f64, rho2: f64) -> Result<f64> {
131 check(mean_ebn0_lin, rho1, rho2)?;
132 let d = 1.0 + 1.0 / (2.0 * mean_ebn0_lin);
133 let (s, t) = (rho1 + rho2, rho1 - rho2);
134 Ok(0.5
135 * (1.0
136 - 0.5 * s * FRAC_1_SQRT_2 / (d - t * t / 2.0).sqrt()
137 - 0.5 * t * FRAC_1_SQRT_2 / (d - s * s / 2.0).sqrt()))
138}
139
140/// One term of Chang & Su's eq. 53: a row of their Table I.
141#[derive(Clone, Copy)]
142struct TableRow {
143 /// $\mathrm{sgn}_{k}$.
144 sign: f64,
145 /// $a_{k}$, $b_{k}$.
146 a: f64,
147 b: f64,
148 /// $\cos\theta_{k}$, $\sin\theta_{k}$.
149 cos: f64,
150 sin: f64,
151}
152
153/// Three rows $(\mathrm{sgn}_{k}, a_{k}, b_{k})$ of Table I sharing $(\cos\theta_{k},
154/// \sin\theta_{k})$.
155type TableGroup = ([(f64, f64, f64); 3], f64, f64);
156
157/// Table I of Chang & Su: the 24 terms of their eq. 53, in groups of three rows that
158/// share $(\cos\theta_{k}, \sin\theta_{k})$.
159fn table_i() -> [TableRow; 24] {
160 let (r2, r10) = (2f64.sqrt(), 10f64.sqrt());
161 // (sgn, a, b) of the three rows, then (cos θ, sin θ)
162 let groups: [TableGroup; 8] = [
163 ([(1.0, 2.0, -r2), (1.0, 0.0, r2), (1.0, 2.0, r2)], 1.0 / r2, 1.0 / r2),
164 ([(1.0, 2.0, -r2), (1.0, 0.0, r2), (1.0, 2.0, r2)], 1.0 / r2, -1.0 / r2),
165 ([(1.0, 2.0, -r10), (1.0, 0.0, r10), (1.0, 2.0, r10)], 1.0 / r10, 3.0 / r10),
166 ([(1.0, -2.0, r10), (1.0, 0.0, r10), (-1.0, 2.0, r10)], 3.0 / r10, -1.0 / r10),
167 ([(1.0, -2.0, r10), (1.0, 0.0, r10), (-1.0, 2.0, r10)], 3.0 / r10, 1.0 / r10),
168 ([(1.0, 2.0, -r10), (1.0, 0.0, r10), (1.0, 2.0, r10)], 1.0 / r10, -3.0 / r10),
169 ([(1.0, -2.0, 3.0 * r2), (1.0, 0.0, 3.0 * r2), (-1.0, 2.0, 3.0 * r2)], 1.0 / r2, 1.0 / r2),
170 ([(1.0, -2.0, 3.0 * r2), (1.0, 0.0, 3.0 * r2), (-1.0, 2.0, 3.0 * r2)], 1.0 / r2, -1.0 / r2),
171 ];
172 let mut t = [TableRow { sign: 0.0, a: 0.0, b: 0.0, cos: 0.0, sin: 0.0 }; 24];
173 for (g, (rows, cos, sin)) in groups.iter().enumerate() {
174 for (i, &(sign, a, b)) in rows.iter().enumerate() {
175 t[3 * g + i] = TableRow { sign, a, b, cos: *cos, sin: *sin };
176 }
177 }
178 t
179}
180
181/// The BER of Gray 16-QAM equalised by an estimate of correlations $(\rho_{1},
182/// \rho_{2})$ and of standard deviation ratio `sigma_ratio` $= \sigma_{2}/\sigma_{1}$
183/// (Chang & Su eq. 53 and Table I; $\bar{\gamma}_{b} = 5\sigma_{1}^{2}/(2\sigma_{n}^{2})$
184/// for the points $\{\pm 1, \pm 3\}^{2}$).
185///
186/// # Errors
187/// As [`ber_bpsk_imperfect_csi`], and [`comnum_core::Error::Config`] for a ratio that is
188/// not positive.
189pub fn ber_16qam_imperfect_csi(mean_ebn0_lin: f64, rho1: f64, rho2: f64, sigma_ratio: f64) -> Result<f64> {
190 check(mean_ebn0_lin, rho1, rho2)?;
191 ensure(sigma_ratio > 0.0 && sigma_ratio.is_finite(), || {
192 format!("the standard deviation ratio must be positive, got {sigma_ratio}")
193 })?;
194 let rho_sq = rho1 * rho1 + rho2 * rho2;
195 Ok(table_i()
196 .iter()
197 .map(|r| {
198 let u = r.b * (rho1 * r.cos + rho2 * r.sin) + r.a * sigma_ratio;
199 r.sign * (1.0 - u / (r.b * r.b * (1.0 - rho_sq) + u * u + 5.0 / (2.0 * mean_ebn0_lin)).sqrt())
200 })
201 .sum::<f64>()
202 / 32.0)
203}
204
205/// The BER of Gray 16-QAM over flat Rayleigh fading with the perfect channel (Chang &
206/// Su eq. 69).
207///
208/// # Errors
209/// [`comnum_core::Error::Config`] for a mean SNR that is not positive.
210pub fn ber_16qam_rayleigh(mean_ebn0_lin: f64) -> Result<f64> {
211 ensure_snr(mean_ebn0_lin)?;
212 let t = |c: f64| 1.0 / (1.0 + c / mean_ebn0_lin).sqrt();
213 Ok(0.5 - 3.0 / 8.0 * t(5.0 / 2.0) - 0.25 * t(5.0 / 18.0) + 0.125 * t(1.0 / 10.0))
214}
215
216/// The BER of DBPSK over Jakes' Rayleigh fading of normalised Doppler `fd_ts` (Chang &
217/// Su eq. 40).
218///
219/// # Errors
220/// [`comnum_core::Error::Config`] for a mean SNR that is not positive or a Doppler that is
221/// not finite.
222pub fn ber_dbpsk_jakes(mean_ebn0_lin: f64, fd_ts: f64) -> Result<f64> {
223 ensure_snr(mean_ebn0_lin)?;
224 ensure(fd_ts.is_finite(), || format!("the normalised Doppler must be finite, got {fd_ts}"))?;
225 Ok(0.5 * (1.0 - bessel_j0(2.0 * PI * fd_ts) / (1.0 + 1.0 / mean_ebn0_lin)))
226}
227
228/// The BER of Gray DQPSK over Jakes' Rayleigh fading of normalised Doppler `fd_ts`
229/// (Chang & Su eq. 41).
230///
231/// # Errors
232/// As [`ber_dbpsk_jakes`].
233pub fn ber_dqpsk_jakes(mean_ebn0_lin: f64, fd_ts: f64) -> Result<f64> {
234 ensure_snr(mean_ebn0_lin)?;
235 ensure(fd_ts.is_finite(), || format!("the normalised Doppler must be finite, got {fd_ts}"))?;
236 let j = bessel_j0(2.0 * PI * fd_ts);
237 Ok(0.5 * (1.0 - j / ((2.0 + 1.0 / mean_ebn0_lin) * (1.0 + 1.0 / (2.0 * mean_ebn0_lin)) - j * j).sqrt()))
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::theory::ber_rayleigh_mrc;
244
245 #[test]
246 fn the_constellation_selects_the_closed_form() {
247 let (g, r1, r2) = (30.0, 0.98, 0.05);
248 let at = |name: &str| ber_imperfect_csi(&Constellation::from_name(name).unwrap(), g, r1, r2, 0.9);
249 assert_eq!(at("bpsk").unwrap(), ber_bpsk_imperfect_csi(g, r1, r2).unwrap());
250 assert_eq!(at("QPSK").unwrap(), ber_qpsk_imperfect_csi(g, r1, r2).unwrap());
251 assert_eq!(at("4psk").unwrap(), ber_qpsk_imperfect_csi(g, r1, r2).unwrap());
252 assert_eq!(at("16-QAM").unwrap(), ber_16qam_imperfect_csi(g, r1, r2, 0.9).unwrap());
253 assert!(at("8psk").is_err() && at("64qam").is_err());
254 }
255
256 #[test]
257 fn the_perfect_estimate_gives_the_known_results() {
258 for g in [0.5, 3.0, 10.0, 100.0, 1e4] {
259 // eq. 68: BPSK and QPSK have the BER of flat Rayleigh fading
260 let r = ber_rayleigh_mrc(g, 1).unwrap();
261 assert!((ber_bpsk_imperfect_csi(g, 1.0, 0.0).unwrap() - r).abs() < 1e-15);
262 assert!((ber_qpsk_imperfect_csi(g, 1.0, 0.0).unwrap() - r).abs() < 1e-15);
263 // eq. 53 with ρ1 = 1, ρ2 = 0, σ1 = σ2 is eq. 69: the transcription of Table I
264 assert!(
265 (ber_16qam_imperfect_csi(g, 1.0, 0.0, 1.0).unwrap() - ber_16qam_rayleigh(g).unwrap()).abs() < 1e-14,
266 "{g}"
267 );
268 // eqs. 42, 43: slow fading
269 assert!((ber_dbpsk_jakes(g, 0.0).unwrap() - 0.5 / (1.0 + g)).abs() < 1e-15);
270 let dq = 0.5 * (1.0 - 1.0 / (1.0 + 2.0 / g + 1.0 / (2.0 * g * g)).sqrt());
271 assert!((ber_dqpsk_jakes(g, 0.0).unwrap() - dq).abs() < 1e-15);
272 }
273 // the asymptotes of Sec. IV-D3: 1/(4γ) for BPSK; for 16-QAM the expansion of
274 // eq. 69 gives (15/32 + 5/144 - 1/160)/γ = 0.49722/γ, where the paper prints
275 // 0.4952/γ (a misprint, 0.4 % off)
276 let g = 1e6;
277 assert!((ber_bpsk_imperfect_csi(g, 1.0, 0.0).unwrap() * 4.0 * g - 1.0).abs() < 1e-5);
278 assert!((ber_16qam_rayleigh(g).unwrap() * g - (15.0 / 32.0 + 5.0 / 144.0 - 1.0 / 160.0)).abs() < 1e-5);
279 }
280}