简介:面向5G通信方向学习者与科研人员,这份MATLAB仿真资源聚焦NOMA非正交多址和OMA正交多址在5G-MIMO系统中的性能对比,适合课程设计、毕业设计或预研验证。基于MATLAB 2022a环境,代码包含MIMO-NOMA与MIMO-OMA可达速率的完整实现,并配有中文注释,便于逐行理解功率分配、信道增益与速率公式推导。资源包共5个文件:1个M脚本为核心,附3张JPG结果图对比不同信噪比下的和速率曲线,1段AVI操作录像展示运行流程与注意事项,整体约574KB。脚本给出R1n、R12n、R2n等关键变量,对应NOMA下用户可达速率;OMA则以0.5系数体现半速率特性,对比逻辑清晰。目前已有959人学习使用,适合掌握NOMA/OMA建模要点、参考代码注释和操作演示的初学者。通过该资源可省去从零搭建仿真的时间,直接理解多址接入技术在MIMO信道下的性能差异。
1. 5G-MIMO 下 NOMA 和 OMA 性能对比仿真到底在比什么
一个经常被忽略的结论:NOMA 非正交多址并不在任何信噪比下都优于 OMA。低信噪比、用户间信道差异不大,或者接收端 SIC 解调出现误差传播时,NOMA 的增益会被吃光,反而打不过老老实实做正交分配的 OMA。下面要解决的问题是:在 5G-MIMO 系统里,怎样用 MATLAB 把 NOMA 与 OMA 的性能对比仿真完整搭起来。
从叠加编码、SIC 接收机,到功率分配系数与信道设置,每个环节的参数怎么设、代码怎么写、最后两条误码率曲线之间的差距说明什么,都会按可复现的方式给出。适合正在做课程设计、毕业论文,或者想从物理层评估多址方案选型的工程师直接照着跑。
2. NOMA 非正交多址原理与 SIC 解调:先把增益来源算清楚
2.1 功率域 NOMA 的叠加发送模型与 α 的物理含义
NOMA 非正交多址的核心是把多个用户的信号放在同一个时频资源上叠加发射,靠功率轴把用户区分开。下行两用户场景里,远端用户拿到大功率,近端用户拿小功率,发射信号写成 s = sqrt(α·P)·x1 + sqrt((1−α)·P)·x2,其中 α 是近用户功率占比,仿真里一般从 0.1 扫到 0.3。物理直觉很直接:远端用户信道差,必须用高功率跨过解调门限;近端用户信道好,即使功率小,也能在接收端用 SIC 把远用户信号消掉再解自己。OMA 走的是完全相反的路线,把时频资源切成互不重叠的块,每个用户独占一块,接收机不做干扰消除,代价是用户一多,每个人分到的资源就变少,频谱效率的上限被锁死。
α 的取值是全仿真第一个要调的参数。α 太小,近用户自己的符号贴近噪声地板,SIC 第一步解远用户时出错率上升,判决错误会通过相消步骤传播到近用户自己的解调结果;α 太大,远用户的信干噪比被近用户功率压住,误码率出现干扰受限的“水池底”。我一般会把 α 从 0.05 扫到 0.5,看两条 BER 曲线的交点落在哪,而不是凭感觉拍脑袋定一个值。
| 对比项 | NOMA | OMA |
|---|---|---|
| 资源占用 | 同一时频资源,功率区分用户 | 正交时隙/子载波,互不重叠 |
| 接收机 | SIC 逐级干扰消除 | 单用户匹配滤波/MMSE |
| 频谱效率 | 高,支持用户过载 | 随用户数线性下降 |
| 实现复杂度 | 接收机复杂,需要信道排序 | 简单,成熟度高 |
| 典型落点 | mMTC、上行免调度、多流配对 | eMBB 传统调度 |
2.2 SIC 解调顺序与两用户 SINR 推导
SIC 的规则一句话就能说清:先解功率大的信号。近用户收到混合信号后,第一步把远用户当成目标解出来并做硬判决,第二步把重构出的远用户分量从接收信号里减掉,第三步才是解自己的符号;远用户不做 SIC,直接把近用户的信号当作干扰。用等效信道增益 g = ||H·w|| 和噪声方差 σ² 表示,两个用户的关键 SINR 是:远用户 γ_far = ((1−α)·g2²) / (α·g2² + σ²);近用户在理想 SIC 之后 γ_near = α·g1² / σ²。近用户在第一步解远用户时看到的 SINR 是 ((1−α)·g1²) / (α·g1² + σ²),和远用户公式只差在 g1 换成 g2,这个中间值决定了误差传播的概率,α 越小,第一步的 SINR 越高,相消越干净。
理想 SIC 假设相消后残留为 0,工程上做不到。常见做法是引入残留系数 β,把近用户等效 SINR 写成 α·g1² / (β·(1−α)·g1² + σ²),β 取 0.01 到 0.1。下面的代码把这三个 SINR 一次性算出来,方便先看理论趋势再跑蒙特卡洛:
% sic_snr.m —— 由等效信道增益直接算理论 SINR alpha = 0.2; noiseVar = 10^(-10/10); % 10 dB 噪声方差, 信号功率假设为 1 gammaFar = (1-alpha)*g2^2 / (alpha*g2^2 + noiseVar); gammaNear = alpha*g1^2 / noiseVar; % 理想 SIC gammaStep1 = (1-alpha)*g1^2 / (alpha*g1^2 + noiseVar); beta = 0.05; gammaNearBad = alpha*g1^2 / (beta*(1-alpha)*g1^2 + noiseVar);这里 g1、g2 是生成信道后算出的等效增益,先代入再算 SINR;gammaStep1 专用于观察 SIC 第一步的质量,gammaNearBad 和 gammaNear 的差距就是误差传播的代价。
2.3 MIMO 在 NOMA 仿真里的两个作用
MIMO 在 NOMA 仿真里承担两个角色。第一个是分集与合并增益:Nr 根接收天线做 MRC 合并后,等效增益 g = ||H·w||,随机波束下 g² 的均值约等于 Nr;要让发射端也出增益,w 要取 H 的最大右奇异向量,这时 g² 的均值接近 Nt×Nr。第二个是空间复用:真实 5G 系统里每个波束或空间层可以承载一个独立的 NOMA 簇,用户总数等于簇数乘每簇用户数,这也是 NOMA 支持用户过载的根因。
课程仿真通常只做一个簇、一条空间流,但信道生成必须保留完整的 Nt×Nr 矩阵,再用波束赋形和合并降成标量,不能图省事直接抽一个复高斯数当信道,否则结果和单天线 SISO 完全一样,5G-MIMO 这个前提就名不副实。OMA 的对比基准也要在同一套 g1、g2 和总功率下搭,后一章的实现里两者共用同一批信道,这是公平对比的前提。
3. MATLAB 仿真参数设置与最小可运行代码:从信道生成到叠加发射
3.1 仿真参数表与 5G 场景设定
仿真的系统参数按下表设定。这套取值是课程仿真和论文复现里最常见的起点,代码只用 MATLAB 基础函数,R2019b 及以上都能直接跑,不需要 5G Toolbox 或 Communication Toolbox。
| 参数 | 取值 | 说明 |
|---|---|---|
| 载波频率 | 3.5 GHz | 5G 中频段典型值,路径衰落在归一化信道里不单独建模 |
| 带宽与子载波间隔 | 10 MHz / 15 kHz | 相干带宽远大于子载波间隔,每个子载波按平坦衰落处理 |
| 天线配置 Nt×Nr | 2×2 | 想验证分集阶数时改 4×4,代码不用动 |
| 调制方式 | QPSK | 每个符号 2 bit,BER 统计效率高 |
| 用户数 | 2 | 一个近用户一个远用户,对应单簇 NOMA |
| 功率分配系数 α | 0.2 | 近用户功率占比,另做 0.05~0.5 扫描 |
| SNR 扫描 | 0~20 dB,步进 2 dB | 覆盖干扰受限到噪声受限的全区间 |
| 信道实现次数 trials | 100 | 低于 20 时高 SNR 段曲线毛刺明显 |
关于参数有个容易忽略的点:子载波间隔 15 kHz 对应的符号周期约 66.7 μs,远大于城市环境的时延扩展,因此每个子载波内可以安全地假设平坦衰落。若把带宽改成 100 MHz 配 30 kHz 子载波间隔,结论不变,只是频选性更强,需要按子载波逐个生成信道矩阵。
3.2 信道生成:每子载波的瑞利衰落与 MRC 合并
发射端把 2 个用户配对成一个 NOMA 簇,占一条空间流,波束向量 w 把这条流映射到 Nt 根天线;接收端每用户用 MRC 合并成标量。生成代码如下:
% channel_combine.m —— MIMO 信道生成与等效标量信道 Nt = 2; Nr = 2; % 改 4×4 时只改这两个数 H = (randn(Nr,Nt) + 1i*randn(Nr,Nt))/sqrt(2); % 每子载波独立瑞利 w = (randn(Nt,1) + 1i*randn(Nt,1))/sqrt(Nt); % 波束向量功率归一 g = norm(H*w); % 等效信道增益, 含 MRC 合并 u = (H*w)/g; % MRC 合并向量, ||u|| 恒为 1每行都要说清楚:H 除以 sqrt(2) 是为了让每个元素实部虚部各占 1/2 方差,这样 E[|h|²]=1,接收 SNR 才和理论公式对得上;w 除以 sqrt(Nt) 保证发射总功率为 1,否则 g 会带上一个 √2 的静默增益,BER 曲线右移约 3 dB;g1、g2 是两个用户各自 H 生成的标量,SIC 排序、功率分配都作用在这两个数上。忘记归一化是这类仿真最常见的结果漂移来源。
3.3 发射端:QPSK 映射与功率域叠加
用户比特流先映射成 QPSK 符号,再按 α 加权叠加:
% transmitter_noma.m —— 两用户 QPSK 与功率域叠加 numBits = 1e4; b1 = randi([0 1], numBits, 2); % 每行2个bit对应一个QPSK符号 b2 = randi([0 1], numBits, 2); x1 = (b1(:,1)*2-1 + 1i*(b1(:,2)*2-1))/sqrt(2); % 实部虚部各1bit x2 = (b2(:,1)*2-1 + 1i*(b2(:,2)*2-1))/sqrt(2); alpha = 0.2; s = sqrt(alpha)*x1 + sqrt(1-alpha)*x2; % 叠加信号, 总功率恒为1QPSK 映射用 2b−1 把 bit 0 变 −1、bit 1 变 +1,再除 sqrt(2) 让符号平均能量为 1;叠加信号 s 的总功率保持 1,这一点决定了后面 OMA 方案也必须用总功率 1 的发射信号,两边才公平。alpha 在这一行体现为近用户只拿 0.2 功率,远用户拿 0.8。
3.4 OMA 基准方案:正交资源怎么分才公平
OMA 用分时方案实现:两个用户各占一半时隙,在自己的时隙里用满功率发射一整个 QPSK 符号:
% transmitter_oma.m —— 分时正交发送, 帧级功耗与 NOMA 一致 txOma = [x1, x2]; % 第1列占时隙1, 第2列占时隙2 z1o = g1*txOma(:,1) + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1)); z2o = g2*txOma(:,2) + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1));公平性在这里有个关键约定:NOMA 每个符号时刻都用功率 1 发送叠加信号,OMA 用户虽然只在一半时隙发送,但发送时也用功率 1,因此一个帧内的平均功耗一致。如果把 OMA 的发射功率按半分,OMA 的 BER 会整体比 NOMA 差一截,那比的是功放设置而不是多址方案。频率域的等价做法是把子载波对半劈开,和分时方案共享同一个速率折算系数。
4. 误码率与和速率仿真结果对比:NOMA 相对 OMA 的增益从哪来
4.1 蒙特卡洛主循环与 BER 统计代码
把前三章拼成主循环,NOMA 和 OMA 共用同一批信道和比特流。判读前必须先做幅度归一:接收信号 z = g·s + n 的星座被 g 做了缩放,直接 sign(real(z)) 判出来的全是错码,必须先除 g√P 再硬判,这是初跑 BER 大于 0.5 的头号原因。
% main_noma_oma.m —— 对比仿真主循环 clear; clc; rng(2024); Nt=2; Nr=2; numBits=1e4; alpha=0.2; snrList = 0:2:20; trials = 100; berN = zeros(2,numel(snrList)); berO = berN; for si = 1:numel(snrList) noiseVar = 10^(-snrList(si)/10); % 信号功率为1, 反推噪声方差 errN = zeros(2,1); errO = zeros(2,1); for t = 1:trials % 信道与等效增益(保证用户1是近用户) H1=(randn(Nr,Nt)+1i*randn(Nr,Nt))/sqrt(2); H2=(randn(Nr,Nt)+1i*randn(Nr,Nt))/sqrt(2); w =(randn(Nt,1)+1i*randn(Nt,1))/sqrt(Nt); g1=norm(H1*w); g2=norm(H2*w); if g1 < g2, tmp=g1; g1=g2; g2=tmp; end % 比特与QPSK符号 b1=randi([0 1],numBits,2); b2=randi([0 1],numBits,2); x1=(b1(:,1)*2-1+1i*(b1(:,2)*2-1))/sqrt(2); x2=(b2(:,1)*2-1+1i*(b2(:,2)*2-1))/sqrt(2); % NOMA 发射/加噪/SIC sN = sqrt(alpha)*x1 + sqrt(1-alpha)*x2; z1 = g1*sN + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1)); z2 = g2*sN + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1)); x2_est = z1/(sqrt(1-alpha)*g1); % 近用户解远用户 x2_hard = (sign(real(x2_est))+1i*sign(imag(x2_est)))/sqrt(2); z1_clean = z1 - sqrt(1-alpha)*g1*x2_hard; % 相消 x1_est = z1_clean/(sqrt(alpha)*g1); x1_hard = (sign(real(x1_est))+1i*sign(imag(x1_est)))/sqrt(2); x2_far = z2/(sqrt(1-alpha)*g2); % 远用户直接解 x2_hard2= (sign(real(x2_far))+1i*sign(imag(x2_far)))/sqrt(2); % OMA 分时发送 z1o = g1*x1 + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1)); z2o = g2*x2 + sqrt(noiseVar/2)*(randn(numBits,1)+1i*randn(numBits,1)); x1o = (sign(real(z1o/g1))+1i*sign(imag(z1o/g1)))/sqrt(2); x2o = (sign(real(z2o/g2))+1i*sign(imag(z2o/g2)))/sqrt(2); % 比特级误码统计: 实/虚部符号各对应1个bit errN(1)=errN(1)+sum(sum(b1~=[real(x1_hard)>0,imag(x1_hard)>0])); errN(2)=errN(2)+sum(sum(b2~=[real(x2_hard2)>0,imag(x2_hard2)>0])); errO(1)=errO(1)+sum(sum(b1~=[real(x1o)>0,imag(x1o)>0])); errO(2)=errO(2)+sum(sum(b2~=[real(x2o)>0,imag(x2o)>0])); end berN(:,si)=errN/(numBits*2*trials); berO(:,si)=errO/(numBits*2*trials); end semilogy(snrList,berN(1,:),'-o',snrList,berN(2,:),'-s', ... snrList,berO(1,:),'--o',snrList,berO(2,:),'--s'); grid on; legend('NOMA近用户','NOMA远用户','OMA用户1','OMA用户2');循环结构里值得注意的有三处。trials 是信道实现次数,每个 SNR 点总共统计 numBits×2×trials 个比特,100 次实验在高 SNR 段仍可能有零星 0 误差,这时把 numBits 提到 1e5 即可,不要只加 trials,后者的随机性重复度太高。SIC 部分用硬判决反馈,x2_hard 错了会直接污染 z1_clean,这是和解析公式最大的差异来源。OMA 分支没有 SIC,判决前除 g 是唯一的归一化动作。
4.2 和速率对比与 α 扫描代码
BER 只反映单用户可靠性,多址方案的收益还要看和速率。按同一信道实现同时给两种方案打分:
% rate_compare.m —— 同信道下的理论可达速率 gammaFar = (1-alpha)*g2^2/(alpha*g2^2 + noiseVar); gammaNear = alpha*g1^2/noiseVar; % 理想SIC rateN = log2(1+gammaFar) + log2(1+gammaNear); % NOMA 独占全部资源 rateO = 0.5*log2(1+g1^2/noiseVar) + 0.5*log2(1+g2^2/noiseVar);rateO 前面的 0.5 是 OMA 正交切分时频资源的折算系数,NOMA 不需要乘。把这段放进一个 α 从 0.05 扫到 0.5 的外层循环,就能画出“和速率 vs α”的曲线,曲线峰值对应的 α 就是在当前 SNR 和信道下最优的功率分配,这一步可以直接替换主循环里的固定 alpha 值再跑一轮,验证 BER 结论是否随 α 移动。
4.3 结果解读:三个 SNR 区间的表现
低 SNR(约 0~6 dB)区间,NOMA 的 BER 曲线和 OMA 基本缠在一起,甚至略差。原因是噪声主导,SIC 第一步硬判决的错判率高,误差传播抵消了叠加带来的频谱效率收益。中 SNR 区间,远用户 NOMA 的 BER 明显低于 OMA,因为 0.2 与 0.8 之间是约 6 dB 的功率差,远用户先解时的 SINR 起步就高。高 SNR 区间,近用户 BER 逼近单用户下界,和速率优势稳定出现,叠加和相消进入良性工作区。
提示:如果跑出来的曲线在任意 SNR 处 NOMA 全面优于 OMA,先检查 OMA 的发射功率是不是被写成了 0.5 而不是 1;再检查 g1、g2 是否在两种方案里共用。这两处是最容易制造假增益的位置。
期望结果可对照下表判断:
| SNR 区间 | NOMA 近用户 | NOMA 远用户 | 判断依据 |
|---|---|---|---|
| 0~6 dB | 与 OMA 相当或更差 | 优势不明显 | SIC 错判率过高 |
| 8~14 dB | 略优于 OMA | 明显优于 OMA | 功率占比红利显现 |
| 16~20 dB | 逼近单用户下界 | 干扰受限见底 | 理想 SIC 接近成立 |
5. 验证与排错:把 NOMA/OMA 对比仿真的结论做扎实
5.1 不完美 SIC 的建模方法
理想 SIC 是理论天花板,工程评估要把它放开。符号级的实现是相消时只减掉一部分重构信号:
% imperfect_sic.m —— 部分相消, beta 越大残留越多 beta = 0.1; % 残差比例, 0.01~0.1 是常用范围 z1_clean = z1 - (1-beta)*sqrt(1-alpha)*g1*x2_hard; % 残留功率约 (beta*sqrt(1-alpha)*g1)^2, 直接抬高近用户误码地板这个实现比在公式里改 γ_near 更真实,因为残留项和相消时刻的噪声、硬判结果耦合在一起。跑完对照理想版曲线,两条线的间距就是 SIC 误差传播的代价,这也是答辩时最容易被追问的点。
5.2 可信度验证与仿真发散排查
结果可信度按四个步骤检查。第一,高 SNR 段 NOMA 近用户 BER 在对数坐标下的斜率应接近 −Nr,2×2 配置下约每 10 dB 掉两个数量级;第二,远用户 BER 在干扰受限区出现平底,把 α 调大再跑,平底高度应上升;第三,BER 大于 0.5 基本是判决极性写反了,NaN 则是增益除零,给 g 加 eps 保护即可;第四,曲线毛刺大优先加 numBits 而不是堆 trials。这四步都过了,结论才敢写进报告。
仿真发散还有一个隐蔽来源:信道生成的位置放错。H 必须在 trials 循环内生成,如果挪到循环外,等效于所有 SNR 点共用同一信道,曲线会异常平滑且完全没有分集特征,看斜率那一步立刻就会暴露。
5.3 把仿真过程直接导出成操作录像
要交操作录像时,不用屏幕录制软件,直接在仿真循环里抓帧最省事:
% record_result.m —— 逐轮绘制并写视频 v = VideoWriter('compare.avi'); v.FrameRate=2; open(v); for si = 1:numel(snrList) semilogy(snrList(1:si),berN(1,1:si),'-o'); grid on; drawnow; writeVideo(v, getframe(gcf)); % 每轮画图后抓帧写视频 end close(v);帧率设 2 是为了让每一帧的曲线增量肉眼可见,配合脚本里 fprintf 打印的当前 SNR 与 BER 数值,视频每一帧都能对应到具体参数。录像交付后评审对着任何一帧提问,都能回推到主循环的对应迭代,这类细节比单纯录屏更经得起追问。
本文还有配套的精品资源,点击获取