ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

NOMA vs OFDMA:5G/6G物理层仿真对比与MATLAB实现解析

NOMA vs OFDMA:5G/6G物理层仿真对比与MATLAB实现解析

简介:这份压缩包聚焦非正交多址接入(NOMA)与正交频分多址(OFDMA)的对比仿真,面向无线通信、5G技术方向的研究者与学生,用于理解两种多址接入方案的原理与性能差异。包内共18个文件,包含11个MATLAB脚本、3个fig图形、3个png图片及1个mat数据文件。脚本覆盖数据生成、编码解码、OFDM发射接收、误码率绘制等核心流程,fig与png文件直观展示BPSK、4PSK、8PSK调制下NOMA与OFDMA的性能对比。压缩包整体仅147KB,轻量易用。目前已有266人学习下载。通过运行main_v2_ber.m等程序,可复现误码率仿真结果,观察调制方式与多址技术对系统可靠性的影响,为选择NOMA或OFDMA提供数据参考。

1. 为什么仿真一上来就要拿 NOMA 和 OFDMA 对比?

做 5G/6G 物理层仿真,绕不开一个基线问题:NOMA 相比 OFDMA 到底赢在哪、赢多少、在什么条件下会输。这个“compare NOMA OFDMA”的仿真包,就是把两套多址方案放在同一套信道模型下,跑和速率曲线和 BER 曲线,让你用数据回答这三个问题。适合正在做毕业设计复现论文、或者准备预研报告的一线工程师。一个反直觉的结论先说在前面:NOMA 并不是所有 SNR 区间都比 OFDMA 好,在用户信道差异不够大、信噪比偏高的时候,OFDMA 反而能追回来。想看清这个交叉点,手里必须有一个能改参数、能逐行解释的代码工程。

2. 先立住理论:NOMA 与 OFDMA 的容量边界和数学差异

2.1 从正交到非正交:OFDMA 的频谱利用率瓶颈在哪

OFDMA 的核心思想是把可用频带切成一组正交子载波,每个用户独占其中一部分。下行链路里,基站对不同用户分配互不重叠的子载波组,接收端拿 FFT 解调,正交性让用户间干扰趋近于零。这个设计在工程上非常干净,但也埋了一个天然瓶颈:用户的频谱资源是被物理隔离的,强弱用户之间没法“互相帮忙”。

假设两个用户各占一半带宽。近用户信道好,能跑 10 bps/Hz;远用户信道差,只能跑 1 bps/Hz。系统总频谱效率就是平均值 5.5 bps/Hz。如果换成功率域 NOMA,近用户可以让出一部分功率给远用户,同时保留全部带宽,整体和速率往往高于 OFDMA。OFDMA 的容量公式可以写成:

% 计算 OFDMA 两用户下行和速率,等带宽分配 function sum_rate = ofdma_sum_rate(snr_user1, snr_user2, bandwidth_ratio) % snr_user1: 用户1接收信噪比(线性值,不是 dB) % bandwidth_ratio: 用户1分到的带宽占比,用户2自动拿剩余带宽 rate1 = bandwidth_ratio * log2(1 + snr_user1); rate2 = (1 - bandwidth_ratio) * log2(1 + snr_user2); sum_rate = rate1 + rate2; end

这个函数的逻辑很直白:先按带宽比例把频谱切成两份,各自用香农公式算容量,再相加。参数bandwidth_ratio通常取 0.5,代表不考究的等带宽基线。如果要做更公平的对比,可以对 OFDMA 用注水功率分配,给信道好的用户多分带宽,但代价是弱用户速率进一步下降,公平性变差。很多对比仿真包里的 OFDMA 基线只做等带宽分配,这是一个保守下限,NOMA 对上它才显得增益明显。

2.2 NOMA 的功率域复用:叠加编码与串行干扰消除的数学模型

NOMA 的做法和 OFDMA 完全相反:两个用户占用完全相同的时频资源,用功率把他们区分开。基站发送的符号是两个用户的叠加:

% NOMA 下行发送端:叠加编码 function x = noma_tx(s1, s2, alpha) % alpha: 近用户的功率占比,小于0.5 % 远用户自动获得 (1-alpha) 的功率 x = sqrt(alpha) * s1 + sqrt(1-alpha) * s2; end

关键参数alpha是近用户的功率占比。注意这里有一个反直觉设定:信道好的近用户只拿小功率,信道差的远用户拿大功率。原因是远用户信道衰减大,需要更多功率才能维持基本速率;近用户信道好,小功率也够用。发送端的sqrt(alpha)和sqrt(1-alpha)是幅度加权,功率加起来正好是 1,这是仿真里最容易出错的地方。

接收端才是 NOMA 的核心。近用户接收信号里同时包含两个用户的符号,而且都经过了近用户的信道h1:

% 近用户接收端:SIC 先解远用户,再解自己 function [s1_hat, s2_hat] = sic_decode_near(y1, h1, alpha, M) % y1 = h1 * (sqrt(alpha)*s1 + sqrt(1-alpha)*s2) + n1 % M: 调制阶数,4 表示 QPSK % 步骤1:均衡信道,先解调远用户 s2 z = y1 / h1; s2_hat = qamdemod(z / sqrt(1-alpha), M, 'UnitAveragePower', true); % 步骤2:重建远用户信号,从 y1 中减掉 y_remain = y1 - sqrt(1-alpha) * h1 * s2_hat; % 步骤3:在残余信号里解调近用户 s1 s1_hat = qamdemod(y_remain / (sqrt(alpha) * h1), M, 'UnitAveragePower', true); end

这段代码对应的数学过程是:近用户先把远用户当作目标解调,因为远用户功率大、SINR 高,容易解对;拿到s2_hat后重建出远用户信号sqrt(1-alpha)*h1*s2_hat,从原始接收信号里减掉;剩下的信号就只有近用户自己的符号加噪声,解调几乎没有同道干扰。远用户接收端则不做 SIC,直接把近用户信号当噪声,解调自己的符号。

这里有一个常见的原理性误解:SIC 的“先解谁”不是按用户的信道好坏排序,而是按接收端的信号功率排序。下行 NOMA 中远用户功率大,所以先解的是远用户。近用户的容量在理想 SIC 后是log2(1 + alpha*P*|h1|^2 / N0),远用户直接解码的容量是log2(1 + (1-alpha)*P*|h2|^2 / (alpha*P*|h2|^2 + N0))。这时alpha就是一对权衡:alpha越小,近用户给远用户的干扰越小,但近用户自己的速率也越低。

2.3 对比仿真的量化指标:和速率、公平性和用户配对

判断 NOMA 和 OFDMA 谁好,光看一条曲线不靠谱,至少要看三个指标。

和速率是第一位,也是大多数论文里那张主图。两用户下行场景,NOMA 的和速率 = 近用户 SIC 后的速率 + 远用户直接解码的速率。OFDMA 的和速率 = 两个用户各自带宽切片的速率之和。和速率曲线的交叉点位置,就是两个方案的分水岭。

公平性排在第二位。NOMA 对远用户友好,但可能把近用户牺牲得太狠。工程上常用 Jain 公平性指数:

% Jain 公平性指数 function jf = jain_fairness(rates) % rates: 各用户速率向量 jf = sum(rates)^2 / (length(rates) * sum(rates.^2)); end

这个指数越接近 1 代表用户间越公平。NOMA 通过调alpha可以逼近 OFDMA 达不到的公平-和速率边界,这也是做对比时要强调的价值点。

用户配对是第三个维度。两用户 NOMA 如果两个用户信道增益相近,叠加后的功率差不足,SIC 第一步就容易出错。配对越“极端”(一个强一个弱),NOMA 增益越明显,但过度的极端配对并不符合真实用户分布。仿真包里通常会提供固定配对和随机配对两种模式,固定配对用于复现论文理想场景,随机配对用于评估平均增益。

3. 拆解 compare 仿真包:代码结构与核心模块

3.1 入口脚本与目录结构:从 zip 解压开始找 main

拿到compare_NOMA_OFDMA.zip,第一件事是解压到纯英文路径。我见过太多次因为把 zip 解压在“下载”目录导致 MATLAB 乱码的事故,别让路径问题卡住第一步。

unzip compare_NOMA_OFDMA.zip -d noma_ofdma_compare cd noma_ofdma_compare ls -la

这个包通常包含一个主脚本main.m、几个功能函数(信道生成、NOMA 收发、OFDMA 收发)和一个画图脚本。入口是main.m,它负责设置仿真参数、循环 SNR、调用链路函数,最后画图。打开main.m,先看它的循环结构:

% main.m 骨架:主循环遍历 SNR 点 snr_db = 0:2:20; for idx = 1:length(snr_db) snr_linear = 10^(snr_db(idx)/10); % 生成同一组信道和符号,保证两个方案公平对比 [h1, h2] = generate_channel(numUsers, numIter); s1 = randi([0 M-1], numIter, 1); s2 = randi([0 M-1], numIter, 1); % OFDMA 链路 rate_ofdma(idx) = ofdma_link(s1, s2, h1, h2, snr_linear); % NOMA 链路 rate_noma(idx) = noma_link(s1, s2, h1, h2, snr_linear, alpha); end plot(snr_db, rate_ofdma, snr_db, rate_noma);

这个骨架最关键的一点是:OFDMA 和 NOMA 共用同一组s1/s2/h1/h2。如果它们各自生成随机信道,相当于两次独立实验,曲线上的差异会混入随机性,结论没法自洽。所有正经的对比仿真都会把“随机数共用”写进主循环。

3.2 信道生成模块:瑞利衰落、路径损耗和噪声基底怎么配

信道模型直接决定仿真结论的适用范围。默认配置是瑞利衰落,适合市区多径场景;如果要加基站距离差异,给远用户叠加一个固定的路径损耗偏置。

% 瑞利衰落信道生成,返回复信道增益 function h = rayleigh_channel(numUsers, numIter) % numUsers: 用户数 % numIter: 蒙特卡洛迭代次数 % 每个信道系数独立同分布,服从 CN(0,1) h = (randn(numUsers, numIter) + 1j*randn(numUsers, numIter)) / sqrt(2); end

除以 sqrt(2)很重要。如果不做这个归一化,信道幅度的均值会偏大,等效 SNR 整体抬升,NOMA 和 OFDMA 的差距会被夸大。加上路径损耗偏置的写法是:

% 给远用户叠加 6dB 路径损耗偏置 function h = channel_with_pathloss(h, pathlossDb) loss = 10^(-pathlossDb/20); h(2,:) = h(2,:) * loss; end

噪声基底由 SNR 推导:当前 SNR 为snr_linear时,噪声方差按P / snr_linear设置,发射功率 P 归一化为 1。这样每个 SNR 点都能保证发射功率一致,只改变噪声强度。信道模块是这些公式落地的统一入口,配置得不一致,后面的容量和 BER 全是错的。

3.3 发送端与接收端:OFDMA 资源分配和 NOMA SIC 解码的实现差异

OFDMA 的发送端让两个用户各占一半子载波,互不重叠:

% OFDMA 发送端频域资源分配 function tx = ofdma_tx(sym1, sym2, numCarriers) % sym1, sym2: 两个用户各 numCarriers/2 个符号 tx = zeros(1, numCarriers); tx(1:numCarriers/2) = sym1; tx(numCarriers/2+1:end) = sym2; end

接收端则只提取自己那份子载波解调。这个链路里没有任何干扰消除,复杂度低,性能也直观。NOMA 链路则要在发送端做叠加、接收端做 SIC,代码量多出一截,但原理已经在前面章节写了。实际包里的 NOMA 接收函数可能同时支持近用户和远用户两种分支:

% 根据用户类型选择解码方式 function s_hat = noma_rx(y, h, alpha, M, isNearUser) if isNearUser % 近用户:SIC,先消除远用户符号 [s_hat, ~] = sic_decode_near(y, h, alpha, M); else % 远用户:直接把对方信号当噪声,解调自己 z = y / h; s_hat = qamdemod(z / sqrt(1-alpha), M, 'UnitAveragePower', true); end end

写代码时最容易忽略的是isNearUser和alpha的关联:近用户拿sqrt(alpha),远用户拿sqrt(1-alpha)。一旦把两个用户的幅度加权写反,整个 SIC 解码顺序都会颠倒,BER 曲线直接变成一条 0.5 的横线。

4. 跑通最小复现:MATLAB 下的参数配置与结果读取

4.1 最小可运行配置:解压、加路径、运行 main 脚本

跑通这个包只需要三句 MATLAB 命令。第一步确保解压路径纯英文,第二步把当前目录和所有子目录加进 MATLAB 搜索路径,第三步运行主脚本。

cd('C:\workspace\noma_ofdma_compare'); addpath(genpath(pwd)); % 递归添加所有子目录,防止函数找不到 main; % 运行主脚本

addpath(genpath(pwd))是这里最关键的一句。很多仿真包把函数放在utils/、lib/这种子目录里,不加路径,MATLAB 会直接报Undefined function 'generate_channel'。运行后如果弹出图窗,说明链路跑通了;如果只打印几行日志就报错,先看错误信息里是不是提到了某个函数名,然后检查main.m顶部是不是还有单独的configure或init语句需要先执行。

4.2 必调参数表:用户数、SNR 范围、功率分配因子和调制阶数

仿真参数集中在主脚本开头,改参数前先理解每个变量的含义。

参数常见取值作用
numUsers2用户数,先从两用户开始,多用户复杂度高
snr_range0:2:20 dB决定曲线形状,区间太窄看不到交叉点
alpha0.2 ~ 0.4近用户功率占比,必须小于 0.5
modOrder4 (QPSK)调制阶数,先 QPSK 验证逻辑,再上 16QAM
numIter1000每个 SNR 点的蒙特卡洛次数,太小曲线抖动大
channelSeed固定值或随机控制随机信道种子,影响复现性
enableBERtrue是否同时统计 BER,验证 SIC 实现正确性

alpha是最需要小心的参数。它定义的是近用户的功率占比,取值小于 0.5,远用户自动拿1-alpha。如果把alpha改成 0.8,近用户拿了大功率,远用户信道差还拿小功率,SIC 第一步解码对象就错了。我一般先把alpha的合法性检查写进配置:

% config.m assert(alpha > 0 && alpha < 0.5, 'alpha must be less than 0.5 for near user'); assert(abs(alpha + (1 - alpha) - 1) < 1e-12, 'power split not normalized');

4.3 输出结果怎么看:和速率曲线、BER 曲线和日志关键行

跑完main.m后,图窗里通常有两条和速率曲线,命令行会打印每个 SNR 点的数值。先看日志再看图,因为日志里的具体数值能帮你判断交叉点在哪。

如果日志长这样:

SNR=0dB NOMA=2.1 bps/Hz OFDMA=1.8 bps/Hz SNR=6dB NOMA=3.9 bps/Hz OFDMA=3.6 bps/Hz SNR=12dB NOMA=5.8 bps/Hz OFDMA=5.9 bps/Hz

可以直观看到:低 SNR 时 NOMA 领先约 0.3~0.4 bps/Hz,12dB 附近被 OFDMA 反超。这是非常典型的曲线形态。如果 NOMA 在所有 SNR 点都压着 OFDMA 打,优先怀疑 OFDMA 链路写得太弱,比如带宽切片忘乘 0.5,而不是 NOMA 真的这么强。

采样点数据要留存的话,在main.m末尾加一句:

save('compare_result.mat', 'snr_db', 'rate_noma', 'rate_ofdma', 'ber_noma', 'ber_ofdma');

这样写报告时不需要重新跑仿真,直接load这个 mat 文件去画图。导出论文图用exportgraphics(gcf, 'noma_vs_ofdma.png', 'Resolution', 300),比截图清晰得多。

5. 避坑指南:NOMA 与 OFDMA 仿真里最容易翻车的 5 个细节

5.1 现象:SIC 解码顺序错乱导致 BER 暴高

现象:BER 曲线在低 SNR 段直接贴到 0.5,怎么调 SNR 都不下降。

原因:SIC 的解码顺序是按“接收信号功率”排序,不是按用户的远近或 alpha 大小排序。如果代码里不分青红皂白先解调近用户,叠加信号里功率大的远用户反而成了残留干扰,两步解码全部失效。

解决:在接收端动态计算每个用户信号的接收功率排名,再决定先解谁。经验法是写一个排序函数:

% 按接收功率排序,得到解码顺序 power_rank = sort(abs(h).^2 .* [alpha, 1-alpha], 'descend'); % power_rank 第一位对应的用户先被 SIC 解码

注意这里必须同时乘上信道增益模平方和功率占比,只排 alpha 或只排信道都会出错。

5.2 现象:上下行 NOMA 模型混用,信道卷积对象出错

现象:和速率曲线看着有增益,但 BER 怎么都降不到预期,某个用户的星座图有异常。

原因:下行 NOMA 里,基站发一个叠加信号,近用户接收时两个符号都经过近用户自己的信道h1;而上行 NOMA 里,两个用户的符号分别经过h1和h2到达基站,基站收到的信号是y = h1*sqrt(alpha)*s1 + h2*sqrt(1-alpha)*s2 + n。代码里把这两个场景的接收表达式混在一起,SIC 消除出来的信号就是错的。

解决:先在文件注释里写清楚这个模块是上行还是下行。下行模型的近用户接收表达式就是y1 = h1*(sqrt(alpha)*s1 + sqrt(1-alpha)*s2) + n1,任何让 s2 乘h2的写法都属于上行模型,不要混用。

5.3 现象:功率分配因子未归一化,和速率虚高

现象:NOMA 和速率在高 SNR 时比理论香农容量还高,明显不物理。

原因:发送端sqrt(alpha)和sqrt(1-alpha)写成了两个独立的魔法数,比如sqrt(0.2) + sqrt(0.9),叠加后的信号平均功率超过 1,等于偷偷给系统加了发射功率。和速率当然虚高。

解决:只定义alpha,远用户功率显式计算为1-alpha,并且在发送端加一个功率校验:

% 发送功率归一化校验 assert(abs(mean(abs(x).^2) - 1) < 0.05, 'Tx power not normalized');

如果校验失败,加一个缩放系数让x的平均功率回到 1。这样才能保证 NOMA 和 OFDMA 在同等发射功率下对比。

5.4 现象:用户配对不随机,对比结果带偏

现象:NOMA 稳定有增益,但把随机种子换一个,增益消失甚至变负。

原因:仿真包可能把近远用户的信道固定成同一个seed,导致每次迭代的近用户信道增益都正好是远用户的几倍,人为制造了最有利 NOMA 的配对。这种“最优配对”在单次仿真里能复现论文曲线,但不能说明平均增益。

解决:每轮迭代重新生成两个用户的信道,改成随机种子:

rng(shuffle); % 每轮使用不同随机种子 h = rayleigh_channel(2, numIter);

然后把整个 SNR 扫描跑 5 次取平均,看增益是否还有。如果平均增益缩水不少,说明原来那个漂亮曲线确实靠配对挑出来的。

5.5 现象:解压后路径含中文或空格,脚本批量报错

现象:main.m运行时报错找不到文件,或者load数据时路径拼接异常。

原因:zip 包解压到“下载”或“桌面”目录后,路径里包含中文字符,MATLAB 的脚本在遍历目录时编码不对,函数文件路径识别失败。

解决:把 zip 包解压到纯英文路径再运行。这是个 10 秒钟能解决的操作,却让很多人卡了一晚上,也算血泪经验。遇到已经解压到中文目录的,直接copyfile('D:\下载\noma_ofdma_compare', 'C:\work\noma_ofdma_compare')搬过去,别在原目录里折腾。

6. 把一次对比仿真做扎实:曲线解读、SIC 边界验证和下一步扩展

6.1 曲线解读:NOMA 的增益是出现在低 SNR 还是高 SNR

经验规律是:NOMA 的增益集中在中低 SNR 区间,高 SNR 时会被 OFDMA 追平甚至反超。原因是高 SNR 下香农容量的对数曲线已经接近饱和,NOMA 通过功率复用获得的自由度变小,而 SIC 的实际误差传播始终在拖后腿。跑完曲线后先定位交叉点,交叉点越靠右,说明这个场景里 NOMA 的优势区间越大。如果全区间没有交叉,不要急着庆祝,回头检查 OFDMA 是不是用了等带宽且没做任何优化的最弱基线。一个可信的结论应该是:NOMA 在低 SNR 有正增益,高 SNR 持平或略亏,工程取舍才有依据。

6.2 用理想 SIC 和实际 SIC 做算法边界验证

如果代码里同时保留了理想 SIC 和实际 SIC 两种模式,你就能画出算法的上下边界。

% 理想 SIC:假设远用户信号被完美消除 if strcmp(sicMode, 'ideal') rate_near = log2(1 + alpha * snr_user1); % 远用户仍受近用户干扰 rate_far = log2(1 + (1-alpha) * snr_user2 / (1 + alpha * snr_user2)); else % 实际 SIC:把残余干扰误差也计入 SINR rate_near = log2(1 + alpha * snr_user1 / (1 + residual_noise)); end

理想 SIC 的和速率曲线是理论上限,实际 SIC 的曲线是落地下限,两条曲线的间距就是 SIC 误差传播的代价。这个间距越小,说明你实现的 SIC 越接近理想算法,代码的可信度越高。

6.3 从两用户对比转向多用户配对:下一步可以这么走

两用户跑通之后,自然的方向是把用户分成多个簇,每个簇内两个用户做 NOMA,簇间用 OFDMA 隔离。代码上只需要在现有的noma_tx和sic_decode_near外面套一层簇循环,再把不同簇分配到不同子载波组。先固定配对策略(比如“近+远”的固定配对),记录一组和速率;再改成按信道增益差动态配对,对比增益变化。这样能快速定位提升是来自 SIC 算法本身还是来自配对策略。

我做这类对比仿真最后一步永远是固定随机种子的全量复跑,确认三组曲线交叉点的位置完全一致后,才敢把数据放进报告。随机种子这东西有时候挺玄学,同一套代码换个 seed 结论就跑偏。希望这一套从解压、理论到参数和踩坑的流程能帮到你,把这个方向的对比仿真做成一组可信、能写进文档的基线数据。

本文还有配套的精品资源,点击获取

返回列表