别再死磕OFDMA了!用Python+PyTorch手把手复现NOMA的SIC接收机(附代码)
用Python+PyTorch实战NOMA的SIC接收机:从理论到代码实现
在5G和后5G时代,非正交多址接入(NOMA)技术因其卓越的频谱效率而备受关注。与传统的正交多址(OFDMA)不同,NOMA允许用户在相同时频资源上叠加传输,通过功率域复用和先进的接收机设计实现多用户检测。本文将带您用Python和PyTorch一步步构建NOMA系统中的关键组件——串行干扰消除(SIC)接收机,通过可运行的代码揭示其核心原理。
1. NOMA系统基础搭建
要实现SIC接收机,首先需要构建一个简化的NOMA下行链路仿真环境。我们考虑两个用户的场景:用户1(近端用户)和用户2(远端用户),基站将两个用户的信号按不同功率叠加后发送。
import torch import numpy as np import matplotlib.pyplot as plt # 系统参数设置 num_users = 2 # 用户数量 num_symbols = 1000 # 每个用户传输的符号数 snr_db = 20 # 信噪比(dB) # 生成用户数据 (BPSK调制) user_data = torch.randint(0, 2, (num_users, num_symbols)) * 2 - 1 # 转换为±1 # 功率分配系数 alpha = torch.tensor([0.8, 0.2]) # 用户1和用户2的功率分配比 # 信道模型 (瑞利衰落) h = (torch.randn(num_users) + 1j*torch.randn(num_users)) / np.sqrt(2)在这个基础设置中,我们使用BPSK调制生成用户数据,并为两个用户分配不同的功率系数。近端用户(用户1)获得更多功率(0.8),而远端用户(用户2)获得较少功率(0.2),这符合NOMA的功率分配原则。
2. 信号叠加与信道传输
NOMA的核心思想是在发送端进行信号叠加编码。基站将两个用户的信号按分配的功率叠加后发送:
# 信号叠加 (叠加编码) superposed_signal = torch.sqrt(alpha[0]) * user_data[0] + torch.sqrt(alpha[1]) * user_data[1] # 添加高斯白噪声 snr_linear = 10 ** (snr_db / 10) noise_power = 1 / snr_linear noise = torch.randn(num_symbols) * np.sqrt(noise_power / 2) + 1j * torch.randn(num_symbols) * np.sqrt(noise_power / 2) # 通过信道传输 received_signal = superposed_signal * h[0] + noise # 假设两个用户经历相同的信道这里需要注意几个关键点:
- 功率分配系数的平方根用于幅度调整
- 噪声功率根据SNR计算
- 为了简化,我们假设两个用户经历相同的瑞利衰落信道
提示:实际系统中,不同用户通常会经历不同的信道条件,这是SIC接收机需要处理的重要问题。
3. SIC接收机实现
SIC接收机的工作流程可以分为三个主要步骤:信号排序、最强用户检测与解码、干扰消除。我们将分别实现这些步骤。
3.1 信号排序与初始检测
首先需要确定解码顺序。在NOMA中,通常按照接收信号功率或信干噪比(SINR)排序:
# 计算各用户的等效接收功率 received_power = alpha * torch.abs(h)**2 # 确定解码顺序 (从高功率到低功率) decode_order = torch.argsort(received_power, descending=True) print(f"解码顺序: 用户{decode_order[0]+1} -> 用户{decode_order[1]+1}")在我们的设置中,由于α₁=0.8 > α₂=0.2,用户1将首先被解码。
3.2 线性检测与解码
对于首先解码的用户(用户1),我们可以使用线性检测方法。这里我们实现两种常见的检测方案:迫零(ZF)和最小均方误差(MMSE)。
def zf_detection(received, channel): return received / channel def mmse_detection(received, channel, noise_power): return (torch.conj(channel) * received) / (torch.abs(channel)**2 + noise_power) # 对用户1进行ZF检测 user1_est = zf_detection(received_signal, h[0]) # 硬判决解码 user1_data_est = torch.sign(torch.real(user1_est / torch.sqrt(alpha[0])))检测后的信号需要除以分配的功率系数以恢复原始数据。硬判决通过简单的符号函数实现BPSK解调。
3.3 干扰消除与迭代处理
解码出用户1的数据后,我们可以重构其信号并从接收信号中消除:
# 重构用户1的信号 user1_reconstructed = torch.sqrt(alpha[0]) * user1_data_est # 消除用户1的干扰 residual_signal = received_signal - user1_reconstructed * h[0] # 解码用户2 user2_est = zf_detection(residual_signal, h[0]) user2_data_est = torch.sign(torch.real(user2_est / torch.sqrt(alpha[1])))这样就完成了完整的SIC过程。为了评估性能,我们可以计算误码率(BER):
# 计算误码率 ber_user1 = torch.sum(user1_data_est != user_data[0]).item() / num_symbols ber_user2 = torch.sum(user2_data_est != user_data[1]).item() / num_symbols print(f"用户1误码率: {ber_user1:.4f}") print(f"用户2误码率: {ber_user2:.4f}")4. 性能分析与可视化
为了全面评估SIC接收机性能,我们需要在不同SNR条件下测试并可视化结果。下面实现一个完整的性能测试流程:
def simulate_noma_sic(snr_db, alpha=[0.8, 0.2], num_symbols=10000): # 生成数据 user_data = torch.randint(0, 2, (2, num_symbols)) * 2 - 1 # 信道和噪声 h = (torch.randn(2) + 1j*torch.randn(2)) / np.sqrt(2) snr_linear = 10 ** (snr_db / 10) noise_power = 1 / snr_linear noise = torch.randn(num_symbols) * np.sqrt(noise_power/2) + 1j*torch.randn(num_symbols) * np.sqrt(noise_power/2) # 发送端处理 superposed_signal = torch.sqrt(alpha[0])*user_data[0] + torch.sqrt(alpha[1])*user_data[1] received_signal = superposed_signal * h[0] + noise # SIC接收机 # 解码用户1 user1_est = received_signal / h[0] user1_data_est = torch.sign(torch.real(user1_est / torch.sqrt(alpha[0]))) # 消除用户1干扰 user1_reconstructed = torch.sqrt(alpha[0]) * user1_data_est residual_signal = received_signal - user1_reconstructed * h[0] # 解码用户2 user2_est = residual_signal / h[0] user2_data_est = torch.sign(torch.real(user2_est / torch.sqrt(alpha[1]))) # 计算BER ber1 = torch.sum(user1_data_est != user_data[0]).item() / num_symbols ber2 = torch.sum(user2_data_est != user_data[1]).item() / num_symbols return ber1, ber2 # 测试不同SNR下的性能 snr_range = np.arange(0, 31, 5) ber_user1 = [] ber_user2 = [] for snr in snr_range: b1, b2 = simulate_noma_sic(snr) ber_user1.append(b1) ber_user2.append(b2) # 绘制性能曲线 plt.figure(figsize=(10, 6)) plt.semilogy(snr_range, ber_user1, 'o-', label='用户1 (高功率)') plt.semilogy(snr_range, ber_user2, 's-', label='用户2 (低功率)') plt.xlabel('SNR (dB)') plt.ylabel('误码率 (BER)') plt.title('NOMA系统SIC接收机性能') plt.grid(True, which="both", ls="--") plt.legend() plt.show()这个仿真将展示两个用户在不同SNR条件下的误码率曲线。通常可以观察到:
- 高功率用户(用户1)性能较好,因为其信号首先被解码
- 低功率用户(用户2)性能较差,因为它需要承受用户1的残留干扰
- 随着SNR提高,两者的性能差距会减小
5. 高级话题与优化方向
基础SIC实现后,我们可以考虑几个优化方向来提升系统性能:
5.1 MMSE-SIC接收机
用MMSE检测代替ZF检测可以提升性能,特别是在低SNR区域:
def mmse_sic(received, h, alpha, noise_power): # 第一层解码 w_mmse = torch.conj(h[0]) / (torch.abs(h[0])**2 + noise_power/alpha[0]) user1_est = received * w_mmse user1_data_est = torch.sign(torch.real(user1_est)) # 干扰消除 user1_reconstructed = torch.sqrt(alpha[0]) * user1_data_est residual = received - user1_reconstructed * h[0] # 第二层解码 w_mmse = torch.conj(h[0]) / (torch.abs(h[0])**2 + noise_power/alpha[1]) user2_est = residual * w_mmse user2_data_est = torch.sign(torch.real(user2_est)) return user1_data_est, user2_data_est5.2 导频辅助的信道估计
实际系统中信道是未知的,需要导频来估计:
def channel_estimation(pilot_symbols, received_pilots): # 最小二乘信道估计 return received_pilots.mean() / pilot_symbols.mean() # 生成导频符号 pilot_len = 20 pilot_symbols = torch.randint(0, 2, (pilot_len,)) * 2 - 1 # 发送导频 received_pilots = torch.sqrt(alpha[0]) * pilot_symbols * h[0] + torch.randn(pilot_len) * np.sqrt(noise_power/2) # 信道估计 h_est = channel_estimation(pilot_symbols, received_pilots)5.3 免调度NOMA实现
免调度是NOMA的重要特性,可以通过随机接入实现:
def grant_free_noma(num_users, active_prob=0.3): # 随机确定活跃用户 active_users = torch.rand(num_users) < active_prob # 为活跃用户随机分配功率 power_weights = torch.rand(num_users) power_weights = power_weights / power_weights.sum() return active_users, power_weights这个简化示例展示了如何随机选择活跃用户并分配功率,而不需要中心化的调度过程。
