简介:面向5G通信学习和科研的MATLAB非正交多址接入(NOMA)误码率仿真源码包,聚焦功率域多址与SIC干扰消除机制,帮助通信专业学生、算法工程师快速理解NOMA相比传统OFDMA在频谱效率和连接密度上的优势。压缩包含2个m文件,代码规模约2KB,结构精简到可直接运行的主程序与信道函数,便于二次修改参数或扩展为多用户场景。目前已有302人学习下载,常被用作课程设计或5G关键技术的对照实验。仿真覆盖信道建模、QPSK/16QAM调制、用户功率分配、SIC逐层解码及误码统计等关键环节,通过调整信噪比、用户数、功率比例,可直观绘制BER曲线并对比不同策略下的性能差异。源码注释清晰、流程完整,既适合入门者逐行研读NOMA收发链路,也为研究者评估非理想信道条件下的系统表现提供了可复用的基准。
1. 5G-NOMA误码率仿真:在MATLAB里复现叠加信号与SIC解调
“同一个资源块上叠多个用户的信号,接收端再靠功率差一个个拆出来”——这是5G非正交多址NOMA最核心的画面。做链路仿真的工程师拿到这个标题时,第一步通常是在MATLAB里把功率域NOMA的最小闭环跑通:发送端做功率叠加,信道加AWGN或瑞利衰落,接收端用串行干扰消除SIC逐级拆信号,最后统计误码率BER曲线。这个闭环能回答两个实际问题:不同功率分配因子下BER曲线差多少,以及NOMA相对正交接入的可靠性代价有多大。它适合刚接触NOMA物理层仿真的研究生,也适合需要在5G课题里快速产出BER对比数据的工程人员。本文给出一条不依赖额外工具箱、在MATLAB R2021b以上版本可直接运行的完整仿真链路,并附参数扫描和验证技巧。
2. 功率域NOMA与SIC接收:先搞懂叠加信号怎么拆
2.1 从正交到非正交:一个子载波上叠两个人
传统OFDMA把子载波和时隙分配给不同用户,彼此保持正交,接收机用匹配滤波即可分离信号。代价是频谱效率在多用户调度边缘时明显受限。功率域NOMA打破这个正交约束:允许两个用户共享完全相同的时频资源,发射端把两者的调制符号按不同功率加权后直接相加。
发射信号写成:
x = sqrt(P) · (sqrt(α₁)·s₁ + sqrt(α₂)·s₂)
其中P是基站总发射功率,s₁和s₂分别为用户1和用户2的调制符号(QPSK或16QAM),功率分配因子满足α₁+α₂=1。惯例上取α₂ > α₁,即把更多功率分给信道条件较差的用户——通常是小区边缘用户。这样分配的原因很直接:边缘用户本身信噪比低,若功率再小则完全无法解调;而近端用户信道质量好,即使功率占比小,借助SIC干扰消除后仍能恢复出自己的数据。
对比来看,OMA方案里每个用户只占一半时频资源,等效信噪比损失约3dB;NOMA虽然引入了用户间干扰,但每个用户都能使用完整资源块,频谱利用率更高。需要说明的是,NOMA并没有作为5G NR早期版本的必选特性大规模落地,但它在上下行叠加传输、多用户配对、非正交资源复用等场景中的建模思路,一直是链路级仿真和学术研究中的常客,也是6G候选波形研究的参考基线之一。仿真报告里把NOMA和OMA两条BER曲线放到同一张图上,用信噪比差值和频谱利用率差值来解释各自取舍,是标准做法。
2.2 SIC剥离顺序与差错传播:强信号先解,弱信号后解
接收端收到的是叠加信号,两个用户需要采用不同的解调策略。用户2是强功率用户,其信号在叠加中占主导,SINR足够高,因此可以直接对叠加符号做硬判决;而用户1必须先估计出用户2的符号,重构其信号分量并从接收信号中减去,再在残差上解调自己的符号。
用户2的SINR近似为:
SINR₂ = α₂P|h|² / (α₁P|h|² + σ²)
其中h是信道增益,σ²是噪声方差。用户1经过SIC减除后的等效SINR为:
SINR₁ = α₁P|h|² / σ²
两个式子对比能看出NOMA的核心矛盾:用户2的SINR被α₁部分的用户间干扰压低,用户1的SINR则取决于残差消除是否干净。SIC最怕的是差错传播——第一步判决s₂若出错,减掉的信号就是错误的,残差里会残留两倍于s₂功率的干扰,后续s₁的判决基本失败。仿真中这种现象表现为用户1的BER曲线随SNR增加出现“地板效应”,即曲线下降到某个量级后不再下降。
避免差错传播的工程手段有三个方向:一是提高α₂/α₁的比值,从源头上保证第一步判决的低误码率;二是给用户2的信号加信道编码,让SIC反馈的是译码后的比特而非硬判决符号;三是在SIC减法之前先做一次符号可靠性判断,对置信度低的符号不执行减除。最小链路仿真用未编码符号即可,先把前两点对BER的影响跑出来,再考虑编码反馈的增量收益。
2.3 为什么用MATLAB搭这条链路
链路级仿真选MATLAB,核心理由是矩阵语义天然贴合多用户叠加模型。每个用户的比特流和符号序列都是列向量,按功率因子加权后再做矩阵加法即可完成叠加;通信工具箱提供qammod、qamdemod、awgn等现成函数,省去手写基带调制和噪声注入的工作;BER曲线的批量Monte-Carlo循环可以直接写在同一个脚本里,不需要额外的工程封装。
相比C++或Python的scipy实现,MATLAB的调试链路更短:修改一个参数、重跑一段循环、立即看到semilogy绘图结果,非常适合快速迭代参数假设。本文后续代码块在MATLAB R2021b及以上版本可直接运行,不需要安装额外的5G Toolbox,基础通信工具箱足够覆盖本链路全部功能点。如果读者用的是旧版本,需确认qammod支持'InputType','bit'语法,该语法在R2018a之后才稳定可用。
3. 在MATLAB中搭建NOMA误码率仿真主链路
3.1 初始化参数与调制信号生成
仿真采用Monte-Carlo方法:对每个SNR点,生成大量随机比特,经过调制、功率叠加、加噪、判决后统计错误比特比例。先看参数区:
% noma_ber_sim.m 主仿真脚本参数区 clear; clc; M = 4; % 调制阶数:4=QPSK,16=16QAM numBits = 2e5; % 每个SNR点的仿真比特数 snrRange = 0:2:20; % SNR扫描范围(dB) alpha1 = 0.2; % 用户1功率分配因子(近端,功率较小) alpha2 = 1 - alpha1; % 用户2功率分配因子(远端,功率较大) totalPower = 1; % 归一化总发射功率alpha1取0.2、alpha2取0.8是最常见的配置,对应两个用户的功率差约6dB。如果alpha1取0.5,两个叠加分量功率相等,SIC第一步解调s₂时会被s₁的干扰严重恶化,误码率曲线会整体抬高,这也是初学者最容易踩的坑。numBits决定统计精度,2e5个比特在BER=1e-4附近仍有足够的错误样本数;若需要更低的BER下界,建议把numBits提高到1e6,但仿真时间会线性增长。
接着生成两个用户的随机比特流和QPSK符号,并按功率因子完成叠加:
% 生成用户1、用户2的随机比特 bits1 = randi([0 1], numBits, 1); bits2 = randi([0 1], numBits, 1); % QPSK调制,bit输入,符号平均功率归一化为1 sym1 = qammod(bits1, M, 'InputType', 'bit', 'UnitAveragePower', true); sym2 = qammod(bits2, M, 'InputType', 'bit', 'UnitAveragePower', true); % 功率域叠加发射信号 txSig = sqrt(totalPower * alpha1) * sym1 + ... sqrt(totalPower * alpha2) * sym2;qammod的'InputType','bit'参数内部会按格雷映射将比特流转为符号,相邻星座点之间只差1个比特,判决出错时只引入单个比特错误。'UnitAveragePower',true让星座点平均能量归一化为1,功率因子直接乘以符号即可精确控制每个用户的实际发射功率。叠加后的txSig平均功率仍为1,因为α₁+α₂=1且两个符号相互独立,这正是后面awgn函数用'measured'方式注入噪声的前提。
3.2 主循环:信道加噪与SIC接收
完整的主仿真循环如下,把发送、信道、SIC接收和BER统计串成一条链路:
% 预分配BER存储向量 ber1 = zeros(length(snrRange), 1); ber2 = zeros(length(snrRange), 1); for idx = 1:length(snrRange) % 每个SNR点重新生成独立随机比特,避免重复序列 bits1 = randi([0 1], numBits, 1); bits2 = randi([0 1], numBits, 1); sym1 = qammod(bits1, M, 'InputType', 'bit', 'UnitAveragePower', true); sym2 = qammod(bits2, M, 'InputType', 'bit', 'UnitAveragePower', true); % 功率域叠加发射 txSig = sqrt(alpha1) * sym1 + sqrt(alpha2) * sym2; % 过AWGN信道,'measured'表示按信号实际功率计算噪声 rxSig = awgn(txSig, snrRange(idx), 'measured'); % ===== 用户2:直接解调叠加信号 ===== bits2Rx = qamdemod(rxSig, M, 'OutputType', 'bit', 'UnitAveragePower', true); % ===== 用户1:SIC两步接收 ===== sym2Hat = qamdemod(rxSig, M, 'UnitAveragePower', true); % 第一步:硬判决s2 residual = rxSig - sqrt(alpha2) * sym2Hat; % 第二步:重构并相减 bits1Rx = qamdemod(residual, M, 'OutputType', 'bit', 'UnitAveragePower', true); % ===== BER统计 ===== ber1(idx) = mean(bits1Rx ~= bits1); ber2(idx) = mean(bits2Rx ~= bits2); % 避免误码为0导致semilogy无法显示 if ber1(idx) == 0, ber1(idx) = 1e-6; end if ber2(idx) == 0, ber2(idx) = 1e-6; end end三个关键点需要说明。
第一,qamdemod不带'OutputType','bit'时返回符号索引,sym2Hat是符号域的硬判决结果,可以直接乘上功率因子进行重构;如果误用了bit输出,重构信号的长度和幅度都会不匹配,减法后残差中依然残留完整s₂功率,用户1的BER将停留在0.5附近无法下降。
第二,每轮循环都重新生成bits1和bits2,避免不同SNR点使用相同比特序列导致错误样本不独立。若想完全复现某次仿真结果,可以在脚本开头用rng(固定种子)锁定随机数生成器,但参数扫描时一般不锁定,保留统计随机性。
第三,用户2的BER统计用的也是同一份叠加信号,没有额外的干扰消除步骤。它的性能上限受限于α₁带来的多址干扰,因此曲线会位于单用户AWGN理论曲线之下,这个间距可以直接用于评估NOMA相比OMA的覆盖损失。
3.3 瑞利衰落信道下的SIC顺序修正
AWGN信道跑通后,换成瑞利衰落更贴近小区边缘场景。此时每个用户有独立的信道增益h₁和h₂,接收信号变为:
% 瑞利信道:每个用户独立复高斯衰落 h1 = (randn(numBits,1) + 1j*randn(numBits,1)) / sqrt(2); h2 = (randn(numBits,1) + 1j*randn(numBits,1)) / sqrt(2); % 接收信号:两路独立幅度叠加后加噪 rxSig = h1 .* sqrt(alpha1) .* sym1 + ... h2 .* sqrt(alpha2) .* sym2 + ... sqrt(0.5 * 10^(-snrRange(idx)/10)) * ... (randn(numBits,1) + 1j*randn(numBits,1));此时SIC不能直接对叠加信号做判决,需要先做信道均衡。常见做法是先对用户2的信号做最小二乘均衡:
% 用户2:先按自身信道均衡再解调 rxEq2 = rxSig ./ h2; sym2Hat = qamdemod(rxEq2, M, 'UnitAveragePower', true); % 重构用户2的接收幅度并相减 s2Recon = sqrt(alpha2) * sym2Hat .* h2; residual = rxSig - s2Recon; % 用户1:对残差按自身信道均衡后解调 rxEq1 = residual ./ h1; bits1Rx = qamdemod(rxEq1, M, 'OutputType', 'bit', 'UnitAveragePower', true);需要特别注意的是,瑞利信道下SIC的剥离顺序不能只看发射功率,还要看等效接收功率|h|²·α。例如某次信道实现中h₁恰好远好于h₂,用户2的等效接收功率可能反而弱于用户1,固定顺序的SIC就会失效。实际工程中应当先计算每个用户的等效接收功率并排序,功率最大的用户先解调、先剥离,然后再解调剩余的较弱用户。这种动态排序策略在仿真代码里实现起来不过十几行,但性能增益在深衰落信道下非常明显,属于SIC仿真中优先级很高的优化项。
4. 功率分配因子与调制阶数:参数扫描决定曲线走势
4.1 功率分配因子α的物理边界
功率分配因子是NOMA仿真里最敏感的参数。α₂越接近1,用户2的BER曲线越好,但残差信号中用户1的有效功率越小,用户1的BER恶化越快。这是一个不可回避的折中:α₁=0.1时用户1在低信噪比下几乎不可用;α₁=0.4时用户2的多址干扰增强,其曲线向用户1靠拢,两个用户同时丧失NOMA的增益。经验上的合理区间是α₁取0.15到0.3之间,对应功率差约5dB到8dB。把α₁做成扫描变量,画一张二维BER曲面图,是论文里最常见的参数敏感性分析方式。
4.2 扫描脚本的结构设计
在上一章主循环外再包一层α扫描循环,即可对功率分配做批量评估:
alphaList = [0.1 0.2 0.3 0.4]; % 用户1功率占比扫描序列 berMatrix1 = zeros(length(alphaList), length(snrRange)); berMatrix2 = zeros(length(alphaList), length(snrRange)); for ai = 1:length(alphaList) alpha1 = alphaList(ai); alpha2 = 1 - alpha1; for idx = 1:length(snrRange) % 复用第3章中的发送-信道-SIC接收完整流程 % 结果写入 berMatrix1(ai, idx) 和 berMatrix2(ai, idx) end end这个双重循环是标准的参数扫描骨架。numBits较大时整体耗时明显,建议把内层SNR循环替换为parfor并行,前提是每个迭代内部独立生成随机数且不共享内存变量。注意在parfor场景下要让随机数生成器按迭代索引推进,避免并行池各worker因相同初始种子产生完全相同的一组仿真序列,导致统计结果失去独立性。
4.3 典型配置预期对比表
不同配置组合下BER曲线的预期趋势可以直接用于校验仿真结果:
| 配置 | α₁取值 | 调制方式 | 预期BER表现 |
|---|---|---|---|
| 配置A | 0.2 | QPSK | 两用户曲线间距稳定,约4~6dB,无地板 |
| 配置B | 0.3 | QPSK | 用户1曲线变好,用户2曲线略差,间距收窄 |
| 配置C | 0.2 | 16QAM | 两条曲线整体右移6~8dB,斜率变缓,高SNR段接近地板 |
| 配置D | 0.1 | QPSK | 用户2接近单用户AWGN曲线,用户1低SNR段严重劣化 |
表格中的间距指在相同BER下两个用户所需SNR的差值。α₁=0.2时,两用户的SINR理论差为10log10(0.8/0.2)≈6dB,加上SIC残差干扰,实际间距落在5~8dB。若仿真结果间距明显超出这个范围,优先怀疑符号重构是否错误——比如sym2Hat没有乘以sqrt(alpha2),或减法前没有对齐信号长度。配置C中16QAM的相邻星座点欧氏距离变小,叠加后星座状态数从QPSK的16种增长到256种,SIC第一步误判率上升是正常现象,曲线变平是预期行为,不必怀疑代码错误。
4.4 非对称调制:一种常用的性能缓解手段
当两个用户都使用16QAM时,SIC第一步的误符号率会成为用户1的瓶颈。一个工程上很自然的解法是采用非对称调制:用户2用QPSK,用户1用16QAM。这样SIC第一步只需要分辨4个星座点,正确率大幅提高,用户1的残差更干净,整体BER优于对称16QAM配置。
实现时不需要改链路结构,只需要在qammod和qamdemod里分别传入不同的M值:
sym1 = qammod(bits1, 16, 'InputType', 'bit', 'UnitAveragePower', true); sym2 = qammod(bits2, 4, 'InputType', 'bit', 'UnitAveragePower', true);接收端用户1的SIC第一步用M=4解调s₂,第二步用M=16解调s₁。这种非对称结构的代价是用户2的频谱效率降低——它只传了QPSK的比特率。实际系统设计需要在用户1的吞吐增益和用户2的吞吐损失之间做平衡,仿真曲线提供的就是这个量化的取舍依据。
5. 扩展到多用户组与仿真验证技巧
5.1 从2用户到K用户:SIC逐层剥离
将发射端推广到K个用户,叠加信号变为x = sqrt(P) · Σᵢ sqrt(αᵢ)·sᵢ,功率分配满足Σαᵢ=1,按信道质量从差到好分配递减功率。接收端从功率最大的用户开始逐级剥离,每层用符号判决重构后从残余信号中减去,直到最后一个用户直接从最终残差中解调。MATLAB实现只需把SIC的两步写进for循环:
rxK = rxSig; % 初始残余为完整接收信号 for k = 1:K-1 % 从当前残余解调第k个用户的符号 symHat = qamdemod(rxK, M, 'UnitAveragePower', true); % 重构该用户信号并从残余中减去 rxK = rxK - sqrt(alpha(k)) * symHat; end % 最后一个用户从最终残差中解调 bitsK = qamdemod(rxK, M, 'OutputType', 'bit', 'UnitAveragePower', true);K增大时差错传播逐层累积,用户1的曲线会显著上移,因此实际仿真中K取3或4即可,超过4层后最弱用户往往已经无法从残差中解出有效信息。若论文中需要展示K=4以上的结果,建议在中间用户上加入信道编码和软判决反馈,否则仿真结论会明显偏悲观。
5.2 用理论下界校验仿真曲线
验证仿真结果最直接的办法,是把无干扰单用户QPSK的理论BER曲线作为黑虚线叠到仿真图上。AWGN下QPSK的理论误码率可以用erfc函数计算:
snrLin = 10.^(snrRange/10); theoryBer = 0.5 * erfc(sqrt(snrLin)); semilogy(snrRange, theoryBer, 'k--', 'LineWidth', 1.2);用户2的仿真曲线应当位于这条理论黑虚线下方且接近,用户1由于SIC残差干扰会偏离理论值。如果用户2的仿真BER反而优于理论下界,说明awgn注入的噪声功率偏小或'measured'计算方式出了问题。另一种快速验证手段是使用通信工具箱的bertool图形界面,它可以免写代码绘制相同信噪比范围的理论BER曲线,适合在做参数扫描前确认参考基准。
5.3 调试技巧:优先看SIC残差功率
SIC减法是否成功,用一句代码就能诊断:
residualPower = mean(abs(residual).^2);在SNR较高时,残差功率应当趋近于α₁·totalPower,即0.2左右。若残差功率仍接近1,说明第一步符号重构失败,减法没有起到消除干扰的作用,此时不必浪费时间去看BER曲线,直接检查sym2Hat的调制阶数、均衡方式和符号功率归一化是否正确。这个指标比BER更早暴露问题,是我做NOMA仿真时最先查看的诊断量。
本文还有配套的精品资源,点击获取