别再死磕OFDMA了!用Python+PyTorch手把手复现NOMA的SIC接收机(附代码)
用PythonPyTorch实战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, descendingTrue) 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_symbols10000): # 生成数据 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, whichboth, 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_prob0.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这个简化示例展示了如何随机选择活跃用户并分配功率而不需要中心化的调度过程。