Generating Fading Channels
Why Channel Generation Is a Core Simulation Skill
Every wireless link-level simulation begins with generating channel realizations. A single-tap Rayleigh coefficient models flat fading; a Ricean coefficient adds a line-of-sight component; a MIMO matrix with Kronecker correlation models realistic antenna arrays. Time-varying channels require Jakes' model or 3GPP spatial channel models.
Getting the channel statistics wrong invalidates everything downstream: BER curves, capacity results, and beamforming gains. This section teaches you to generate all these channels correctly in NumPy.
Definition: Rayleigh Fading Channel
Rayleigh Fading Channel
A Rayleigh fading channel coefficient is:
The envelope follows a Rayleigh distribution with parameter , and the power follows an exponential distribution with mean .
rng = np.random.default_rng(42)
N = 100000
h = (rng.standard_normal(N) + 1j * rng.standard_normal(N)) \
* np.sqrt(sigma2 / 2)
The Rayleigh model applies when there is no dominant line-of-sight path and many scattered multipath components contribute to the signal.
Definition: Ricean Fading Channel
Ricean Fading Channel
A Ricean fading channel with -factor is:
where is the deterministic LOS component and .
The envelope follows a Rice distribution. When , Ricean reduces to Rayleigh; as , the channel becomes AWGN.
def rice_channel(K, N, rng):
h_los = np.exp(1j * rng.uniform(0, 2*np.pi))
h_nlos = (rng.standard_normal(N)
+ 1j * rng.standard_normal(N)) / np.sqrt(2)
return np.sqrt(K/(K+1)) * h_los \
+ np.sqrt(1/(K+1)) * h_nlos
The -factor is typically 0-20 dB. Indoor environments with a clear LOS path have - dB; dense urban NLOS has (Rayleigh).
Definition: Nakagami- Fading
Nakagami- Fading
The Nakagami- distribution generalizes Rayleigh () and approximates Rice. The envelope PDF is:
where and .
from scipy.stats import nakagami
m = 2.0
samples = nakagami.rvs(m, scale=np.sqrt(omega), size=N)
The power follows a Gamma distribution: .
Definition: Kronecker MIMO Channel Model
Kronecker MIMO Channel Model
For an MIMO system, the Kronecker model generates:
where has i.i.d. entries, is the receive correlation, and is the transmit correlation.
def kronecker_mimo(Rr, Rt, rng):
Nr, Nt = Rr.shape[0], Rt.shape[0]
Lr = np.linalg.cholesky(Rr)
Lt = np.linalg.cholesky(Rt)
Hw = (rng.standard_normal((Nr, Nt))
+ 1j * rng.standard_normal((Nr, Nt))) / np.sqrt(2)
return Lr @ Hw @ Lt.T
The Kronecker model assumes separable transmit and receive correlations. This is a good approximation for moderate antenna spacing but breaks down for closely spaced or very large arrays.
Definition: Jakes' Model for Time-Varying Channels
Jakes' Model for Time-Varying Channels
For a mobile terminal moving at velocity , the maximum Doppler frequency is and the channel autocorrelation is:
where is the zeroth-order Bessel function. To generate time-correlated samples at sampling rate :
- Build the autocorrelation matrix
- Apply Cholesky:
Alternatively, filter white noise through the Doppler spectrum:
from scipy.special import j0
R = j0(2 * np.pi * fd * np.abs(
np.arange(N)[:, None] - np.arange(N)[None, :]) / fs)
Definition: 3GPP Spatial Channel Model (Overview)
3GPP Spatial Channel Model (Overview)
The 3GPP defines standardized channel models (TR 38.901) with:
- Delay profiles: TDL-A through TDL-E (tapped delay line models)
- Cluster-based: each cluster has a delay, angle of arrival/departure, and power
- Large-scale parameters: path loss, shadow fading, -factor, delay spread, angular spread
For Python simulation, the key steps are:
- Select a TDL model (e.g., TDL-A for NLOS)
- Scale delays by desired delay spread
- Generate per-tap Rayleigh or Ricean coefficients
- Apply Doppler filtering per tap
# TDL-A model (simplified)
tdl_a_delays = np.array([0, 30, 70, 90, 110, 190, 410]) * 1e-9
tdl_a_powers_db = np.array([0, -1, -2, -3, -8, -17.2, -20.8])
tdl_a_powers = 10 ** (tdl_a_powers_db / 10)
tdl_a_powers /= tdl_a_powers.sum() # normalize
Theorem: Envelope and Power of Complex Gaussian
If , then:
-
Envelope: with
-
Power: with
-
Phase: , independent of
A complex Gaussian in polar coordinates separates into independent Rayleigh amplitude and uniform phase β the "circular symmetry" that gives CSCG its name.
Theorem: Kronecker Model Covariance Structure
For , the vectorized channel has covariance:
The channel's spatial correlation is the Kronecker product of the transmit and receive correlation matrices.
The Kronecker structure means that antenna correlations at the transmitter and receiver are separable β knowing and fully specifies the MIMO spatial correlation.
Vectorize
using the identity .
Compute covariance
.
Theorem: Clarke-Jakes Autocorrelation
For a 2D isotropic scattering environment with maximum Doppler frequency , the channel autocorrelation function is:
The coherence time, defined as the time lag where , is approximately:
starts at 1 and oscillates with decreasing amplitude. Faster movement (larger ) means faster decorrelation β the channel changes more rapidly.
Example: Generating and Validating Rayleigh and Rice Channels
Generate 100000 Rayleigh and Ricean channel samples, verify their envelope distributions, and compare the theoretical and empirical outage probabilities.
Generate channels
import numpy as np
from scipy.stats import rayleigh, rice
rng = np.random.default_rng(42)
N = 100000
# Rayleigh
h_ray = (rng.standard_normal(N)
+ 1j * rng.standard_normal(N)) / np.sqrt(2)
# Ricean K=5 dB
K_db = 5.0
K = 10 ** (K_db / 10)
h_ric = np.sqrt(K/(K+1)) * np.ones(N) \
+ np.sqrt(1/(K+1)) * (rng.standard_normal(N)
+ 1j * rng.standard_normal(N)) / np.sqrt(2)
Verify outage probability
threshold_db = -10 # dB
threshold = 10 ** (threshold_db / 10)
p_out_ray = np.mean(np.abs(h_ray)**2 < threshold)
p_out_theory = 1 - np.exp(-threshold) # Rayleigh
print(f"Rayleigh outage: sim={p_out_ray:.4f}, "
f"theory={p_out_theory:.4f}")
Example: Generating Correlated 4x4 MIMO Channels
Generate 1000 realizations of a MIMO channel with exponential correlation at both transmit and receive arrays.
Setup and generate
import numpy as np
rng = np.random.default_rng(42)
Nr, Nt = 4, 4
rho_r, rho_t = 0.7, 0.5
Rr = np.array([[rho_r**abs(i-j)
for j in range(Nr)] for i in range(Nr)])
Rt = np.array([[rho_t**abs(i-j)
for j in range(Nt)] for i in range(Nt)])
Lr = np.linalg.cholesky(Rr)
Lt = np.linalg.cholesky(Rt)
N_real = 1000
H_all = np.zeros((N_real, Nr, Nt), dtype=complex)
for n in range(N_real):
Hw = (rng.standard_normal((Nr, Nt))
+ 1j * rng.standard_normal((Nr, Nt))) / np.sqrt(2)
H_all[n] = Lr @ Hw @ Lt.T
Verify correlation
# Check receive correlation of first column
h_col0 = H_all[:, :, 0] # Nr x N_real
R_hat = np.mean(
h_col0[:, :, None] * h_col0[:, None, :].conj(),
axis=0)
print("Target Rr:")
print(Rr)
print("Estimated Rr (from col 0):")
print(np.abs(R_hat).round(2))
Example: Generating a Time-Varying Rayleigh Channel (Jakes)
Generate a time-varying Rayleigh channel for a mobile at 60 km/h at 2 GHz carrier, sampled at 1 kHz, and verify the autocorrelation.
Parameters and generation
import numpy as np
from scipy.special import j0
v = 60 / 3.6 # m/s
fc = 2e9 # Hz
c = 3e8
fd = v * fc / c # max Doppler frequency
fs = 1000 # sampling rate
N = 2000 # samples
rng = np.random.default_rng(42)
# Build autocorrelation matrix
lags = np.arange(N)
R = j0(2 * np.pi * fd * np.abs(
lags[:, None] - lags[None, :]) / fs)
R += 1e-8 * np.eye(N) # regularize
L = np.linalg.cholesky(R)
z = rng.standard_normal(N)
h_real = L @ z
z = rng.standard_normal(N)
h_imag = L @ z
h = (h_real + 1j * h_imag) / np.sqrt(2)
Verify autocorrelation
max_lag = 100
acf = np.correlate(h, h, mode='full')
acf = acf[N-1:N-1+max_lag] / acf[N-1]
acf_theory = j0(2*np.pi*fd*np.arange(max_lag)/fs)
print(f"Max ACF error: {np.max(np.abs(acf - acf_theory)):.4f}")
Fading Channel Generator
Generate Rayleigh, Ricean, and Nakagami fading channels and visualize the envelope distribution, power distribution, and time series.
Parameters
Fading Channel Generation
# Code from: ch09/python/fading_channels.py
# Load from backend supplements endpointQuick Check
What distribution does follow when ?
Rayleigh
Exponential with mean 1
Chi-squared with 2 degrees of freedom
Gaussian
The squared envelope of a unit-variance complex Gaussian is exponentially distributed with mean equal to the variance.
Common Mistake: Wrong Transpose in Kronecker Model
Mistake:
Writing H = Lr @ Hw @ Lt instead of H = Lr @ Hw @ Lt.T.
This applies the wrong correlation structure and produces incorrect
channel statistics.
Correction:
The Kronecker model is .
For complex channels with Hermitian correlation, use Lt.conj().T.
Key Takeaway
Always validate your channel generator against known statistics before using it in a simulation. Check: (1) the envelope distribution matches theory, (2) the mean power is correct, (3) the spatial correlation matches the target , and (4) the temporal autocorrelation matches for Jakes.
Why This Matters: Channel Models Drive System Design
The channel model determines the performance limits of any wireless system. Rayleigh fading sets the baseline diversity order (error probability decays as ). Ricean fading with high approaches AWGN. Correlated MIMO channels reduce the multiplexing gain below the i.i.d. case. Understanding these models is essential before studying MIMO detection and beamforming.
Historical Note: William C. Jakes and the Mobile Radio Channel
1974William C. Jakes of Bell Labs published "Microwave Mobile Communications" in 1974, establishing the mathematical framework for mobile radio channels. His model of isotropic scattering leading to the autocorrelation and the classical Doppler spectrum remains the foundation of all mobile channel simulators, including 3GPP models.
Rayleigh Fading
A fading model where the channel coefficient is , applicable when there is no line-of-sight path.
Related: Ricean Fading
Ricean Fading
A fading model with a deterministic LOS component plus scattered components, parameterized by the -factor.
Related: Rayleigh Fading
K-factor
Ratio of LOS power to scattered power in Ricean fading: .
Doppler Spread
Maximum Doppler frequency caused by relative motion between transmitter and receiver.
Kronecker Channel Model
A MIMO channel model where the spatial correlation separates as , generated via .