SemanticCommunication/code/envs/channel_model.py

198 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
无线资源分配信道模型 / Channel model for OFDMA wireless resource allocation.
该模块实现了 3GPP 风格的路径损耗模型和多用户 OFDMA 下行链路系统的复信道增益生成。
所有公式遵循论文中的公式 (5)-(8)。
This module implements the 3GPP-style path loss model and complex channel gain generation
for a multi-user OFDMA downlink system. All formulas follow the paper's equations (5)(8).
作者/Author: Sisyphus-Junior
日期/Date: 2026-02-28
论文引用/Paper Reference: Co-MADDPG based Resource Allocation for Semantic Communication
依赖/Dependencies: numpy
"""
import numpy as np
class ChannelModel:
"""
多用户 OFDMA 系统的频率选择性信道模型。
Frequency-selective channel model for multi-user OFDMA systems.
生成包含距离相关路径损耗和瑞利衰落的每子载波复信道增益,并计算每子载波的信噪比 (SNR)。
Generates per-subcarrier complex channel gains incorporating distance-dependent
path loss with Rayleigh fading, and computes per-subcarrier SNR values.
Parameters
----------
config : dict
完整的配置字典(必须包含 "env" 部分,且具有 carrier_freq, noise_psd 和 subcarrier_spacing 键)。
Full configuration dictionary (must contain an "env" section with keys
"carrier_freq", "noise_psd", and "subcarrier_spacing").
"""
def __init__(self, config: dict) -> None:
# 初始化环境配置 / Initialize environment configurations
self.config = config
env = config["env"]
# 载波频率 (GHz) / Carrier frequency in GHz
self._carrier_freq_ghz: float = env["carrier_freq"]
# 噪声功率谱密度 (dBm/Hz) / Noise power spectral density in dBm/Hz
self._noise_psd_dbm: float = env["noise_psd"]
# 子载波间隔 (Hz) / Subcarrier spacing in Hz
self._subcarrier_spacing: float = env["subcarrier_spacing"]
# ------------------------------------------------------------------
# 路径损耗 / Path loss
# ------------------------------------------------------------------
def path_loss(self, distance: float) -> float:
"""
计算与距离相关的路径损耗 (dB)。
Compute distance-dependent path loss in dB.
使用 3GPP Urban Micro (UMi) NLOS 模型 (公式 5):
Uses the 3GPP Urban Micro (UMi) NLOS model (Eq. 5):
PL(d) = 36.7 * log10(d) + 22.7 + 26 * log10(fc)
其中 d 的单位为米fc 的单位为 GHz。
where *d* is in metres and *fc* is in GHz.
Parameters
----------
distance : float or np.ndarray
收发机之间的距离,单位为米。
Transmitterreceiver distance(s) in metres.
Returns
-------
float or np.ndarray
路径损耗值,单位为 dB。
Path loss value(s) in dB.
"""
fc = self._carrier_freq_ghz
# 应用 3GPP UMi NLOS 公式 / Apply 3GPP UMi NLOS formula - Eq.(5)
return 36.7 * np.log10(distance) + 22.7 + 26.0 * np.log10(fc)
# ------------------------------------------------------------------
# 信道生成 / Channel generation
# ------------------------------------------------------------------
def generate_channel(
self, distances: np.ndarray, num_subcarriers: int
) -> np.ndarray:
"""
生成所有用户和子载波的复信道增益。
Generate complex channel gains for all users and subcarriers.
每个元素 h_{k,n} 服从复高斯分布 CN(0, 10^{-PL/10}) (公式 6)。
即:独立循环对称复高斯分布,其方差等于线性尺度的逆路径损耗。
Each element h_{k,n} is drawn from CN(0, 10^{-PL/10}) (Eq. 6), i.e.
independent circularly-symmetric complex Gaussian with variance
equal to the linear-scale inverse path loss.
Parameters
----------
distances : array_like, shape (K,)
每个用户距离基站的距离(米)。
Distance of each user from the base station (metres).
num_subcarriers : int
OFDM 子载波数量 N。
Number of OFDM subcarriers *N*.
Returns
-------
np.ndarray, shape (K, N)
复信道增益矩阵。
Complex channel gain matrix.
"""
distances = np.asarray(distances, dtype=np.float64)
K = len(distances)
N = num_subcarriers
# 每用户路径损耗 -> 线性尺度信道方差 / Per-user path loss -> linear-scale channel variance
pl_db = self.path_loss(distances) # (K,)
# 方差 = 10^(-PL/10) / Variance = 10^(-PL/10) - Eq.(6)
variance = 10.0 ** (-pl_db / 10.0) # (K,)
variance = variance.reshape(K, 1) # (K, 1) 用于广播 / for broadcasting
# 复高斯:每个分量服从 N(0, var/2) / Complex Gaussian: each component ~ N(0, var/2)
std = np.sqrt(variance / 2.0)
# 生成实部和虚部 / Generate real and imaginary parts
real_part = np.random.randn(K, N) * std
imag_part = np.random.randn(K, N) * std
# 返回复增益 / Return complex gains
return real_part + 1j * imag_part
# ------------------------------------------------------------------
# SNR 计算 / SNR computation
# ------------------------------------------------------------------
def compute_snr(
self,
channel_gains: np.ndarray,
power_alloc: np.ndarray,
noise_power: float,
) -> np.ndarray:
"""
计算每个用户的每子载波信噪比 (SNR)。
Compute per-subcarrier SNR for every user.
γ_{k,n} = p_{k,n} * |h_{k,n}|² / σ² (公式 8)
γ_{k,n} = p_{k,n} · |h_{k,n}|² / σ² (Eq. 8)
Parameters
----------
channel_gains : np.ndarray, shape (K, N)
复信道增益矩阵。
Complex channel gain matrix.
power_alloc : np.ndarray, shape (K, N)
每个用户在每个子载波上分配的功率(瓦特)。
Power allocated by each user on each subcarrier (Watts).
noise_power : float
每子载波的噪声功率 σ²(瓦特)。
Noise power σ² per subcarrier (Watts).
Returns
-------
np.ndarray, shape (K, N)
SNR 值(线性尺度)。
SNR values (linear scale).
"""
# 计算 SNR = 功率 * 增益平方 / 噪声 / Compute SNR = Power * Gain^2 / Noise - Eq.(8)
return power_alloc * (np.abs(channel_gains) ** 2) / noise_power
# ------------------------------------------------------------------
# 噪声功率属性 / Noise power property
# ------------------------------------------------------------------
@property
def noise_power(self) -> float:
"""
每子载波的热噪声功率 (瓦特)。
Thermal noise power per subcarrier (Watts).
σ² = N₀ * Δf (公式 7)
σ² = N₀ · Δf (Eq. 7)
其中 N₀ 是从 dBm/Hz 转换为线性 (W/Hz) 的噪声功率谱密度:
where N₀ is the noise PSD converted from dBm/Hz to linear (W/Hz):
N₀_linear = 10^((N₀_dBm - 30) / 10)
Returns
-------
float
噪声功率(瓦特)。
Noise power in Watts.
"""
n0_dbm = self._noise_psd_dbm
delta_f = self._subcarrier_spacing
# 转换为线性功率谱密度 / Convert to linear PSD - Eq.(7)
n0_linear = 10.0 ** ((n0_dbm - 30.0) / 10.0)
# 计算总噪声功率 / Compute total noise power
return n0_linear * delta_f