简介这份压缩包聚焦非正交多址接入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/21: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.5modOrder4 (QPSK)调制阶数先 QPSK 验证逻辑再上 16QAMnumIter1000每个 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 点的数值。先看日志再看图因为日志里的具体数值能帮你判断交叉点在哪。如果日志长这样SNR0dB NOMA2.1 bps/Hz OFDMA1.8 bps/Hz SNR6dB NOMA3.9 bps/Hz OFDMA3.6 bps/Hz SNR12dB NOMA5.8 bps/Hz OFDMA5.9 bps/Hz可以直观看到低 SNR 时 NOMA 领先约 0.3~0.4 bps/Hz12dB 附近被 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 结论就跑偏。希望这一套从解压、理论到参数和踩坑的流程能帮到你把这个方向的对比仿真做成一组可信、能写进文档的基线数据。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?