NOMA-OFDMA完整链路MATLAB仿真及BER对比分析
简介这是一套面向无线通信研究者和高年级学生的MATLAB仿真资源聚焦NOMA与OFDMA两种多址接入技术的对比与融合设计可用于学习功率域非正交资源分配、子载波调度以及BER性能评估等核心问题。包体共13个文件以m脚本为主覆盖turbo编解码、OFDM收发、NOMA/OFDMA编码与检测、数据生成及误码率绘图流程同时包含一个mat结果数据文件和zip压缩包整体仅156KB轻量便于部署。已有381人关注学习。资源主干可直接运行能够直观输出不同方案下的误码率曲线配套的turbo编码、NOMA解码、OFDMA解码等模块便于按需拆解研究帮助读者对照理解NOMA在用户密集场景下的频谱效率优势和OFDMA在干扰抑制方面的特点也为进一步探索NOMA-OFDMA联合优化提供可扩展的实验基底。1. NOMA 与 OFDMA 的这场对比缺的是完整链路NOMA 与 OFDMA 看起来是两条相反的路线一个往功率域叠用户一个往频域划子载波。想公平对比这两种多址方式必须有一条完整的编码、调制、收发链路顶着否则 BER 出了问题都说不清是哪一级在拖后腿。NOMA-OFDMA-SIM 就是一套能直接跑通的 MATLAB 仿真工程从数据生成、Turbo 编解码、OFDM 收发到 NOMA/OFDMA 多址编解码和 BER 统计全部串好。代码包里的 data_gen.m、turbo_enc.m、ofdm_tx.m、noma_enc.m、ofdma_enc.m、main_v2_ber.m 等模块覆盖完整链路课程设计、毕业设计可以直接改参数出对比图做预研的也可以拿它验证功率分配策略。下面把主流程、SIC 模块和 OFDMA 子载波映射逐个拆开讲。2. 功率域多址与正交子载波如何共存NOMA-OFDMA 的底层逻辑2.1 频谱效率差异OFDMA 的隔离思想与 NOMA 的叠加思想OFDMA 的设计逻辑是「隔离」把频带切成互相正交的子载波每个用户独占一部分接收端用 FFT 天然分离各路信号不需要做联合检测。这套思路在用户流量均衡时非常干净但它有个隐性成本——当一个子载波上只有一个用户的数据要传时频谱是闲着的当用户数变多而带宽不足时每个用户分到的子载波数量就得压缩随之而来的是调制阶数被迫升高、对信噪比要求变苛刻。NOMA 换了个策略不隔离而是让多路信号在同一个子载波上叠加再靠功率差区分用户。核心依据是接收端可以用 SIC串行干扰消除逐层剥开叠加信号。只要两个用户的信道增益拉开差距功率较强的那路信号就能先解码、重构、再抵消剩余部分留给较弱用户。这两种方式在资源维度和接收端复杂度上有本质差别维度OFDMANOMA资源隔离方式正交子载波独占同一子载波功率域叠加接收端复杂度FFT 后直接抽取子载波需要 SIC 逐级解码频谱效率瓶颈用户增多时缺少额外自由度用户功率差不够大时 SIC 失效典型场景用户分布均匀、信道较好用户密集、信道增益差异大NOMA 被反复提及的价值不是把带宽变大而是把「时频资源」和「功率域」两个自由度同时用起来。代价是接收端复杂度上去了以及功率分配系数必须精确控制。简单说OFDMA 靠的是“划干净”NOMA 靠的是“叠起来再剥开”。2.2 OFDMA 的灵活性边界子载波级调度不是万能的OFDMA 在 4G LTE 和 5G NR 系统里被广泛采用不只是因为它抗多径能力强还因为子载波级别的调度给了系统层很大的灵活性。一个用户可以被分配一段连续子载波也可以被分配分散在频带各处的子载波系统据此做频率选择性调度让信道好的用户用到信道好的资源块。这种灵活性有一个边界它是排他性分配。如果一个用户只有几 KB 的突发数据OFDMA 仍然要给它至少一个资源块来做完整帧如果某子载波上所有用户的信道都很差这个子载波上承载的数据就跟着差系统只能靠链路自适应调整调制编码方案无法在同一个子载波上引入第二份信息。换句话说OFDMA 在用户数量多但总带宽固定的场景里频谱效率是存在天花板的。把 NOMA 和 OFDMA 放一起看就顺理成章了OFDMA 负责把频带切成可控的资源块NOMA 在资源块内部叠加多个用户。仿真工程里 ofdma_enc.m 和 noma_enc.m 同时存在本质就是在模拟“先按 OFDMA 思路划频带再在频带内按 NOMA 思路叠加”的双层接入方式。这也是标题里 NOMA-OFDMA 连字符的实际含义——不是二选一而是嵌套关系。2.3 融合成功与否取决于 SIC 顺序与功率分配系数 α融合链路里最关键的仿真参数不是信道矩阵而是功率分配系数 α 和解码顺序。设近端用户信道增益高被分配 α 的功率占比远端用户信道增益低分到 1-α。发射端发送的是两者在时频资源上的叠加信号即 noma_enc.m 的核心叠加行function y noma_enc(x_near, x_far, alpha) % x_near: 近端用户调制符号 % x_far : 远端用户调制符号 % alpha : 近端用户的功率占比 y sqrt(alpha) * x_near sqrt(1-alpha) * x_far; end这里有个反直觉点远端用户功率大是因为它在 SIC 中是先解的对象。接收端先把功率大的远端信号解出来重构它的干扰然后从叠加信号里减掉剩下的才是近端信号。如果调反顺序近端用户被远端干扰直接淹没BER 曲线会非常难看。参数上常见做法是把 α 设在 0.2 到 0.3 之间近端用户功率占比不高但信道好远端用户靠功率优势保证能被先解调。α 太小两个用户功率拉不开SIC 第一步就出错α 太大近端用户等于在给远端用户让路自己容量被牺牲。这个平衡点需要在一套完整链路里用 BER 曲线去扫描而不是拍脑袋定。另一个容易误用的点是用户配对有人本能认为两用户信道增益接近更好实际恰恰相反NOMA 的增益来自信道增益差大的配对差得越大SIC 解开的成功率越高。3. 把仿真链路跑通从 data_gen.m 到 main_v2_ber.m 的主流程3.1 主脚本的执行顺序与全局参数main_v2_ber.m 是整个工程的入口做的事可以归纳成三步先定义仿真参数信噪比范围、用户数、子载波数、功率分配系数再在循环里跑完整链路最后把两种多址方式的误码率汇给 plot_ber.m 画图。%% 主仿真脚本 main_v2_ber.m clear; clc; % ---- 全局参数 ---- nUsers 2; % 叠加用户数NOMA 按 2 用户设置 nSubcarriers 64; % OFDM 子载波总数 cpLen 16; % 循环前缀长度覆盖 64 点时延扩展 snrRange 0:2:20; % 扫描信噪比单位 dB alpha 0.25; % 近端用户功率分配系数 nFrames 2000; % 每个 SNR 下的帧数用于统计 BER berNoma zeros(size(snrRange)); berOfdma zeros(size(snrRange)); for k 1:length(snrRange) snr snrRange(k); for frame 1:nFrames % 数据生成 Turbo 编码 QPSK 调制 bitSeq data_gen(nUsers, 128); codSeq turbo_enc(bitSeq); modSym qpsk_mod(codSeq); % NOMA 路线OFDM 收发 功率域叠加 nomaSym noma_enc(modSym(:,1), modSym(:,2), alpha); ofdmTx ofdm_tx(nomaSym, nSubcarriers, cpLen); ofdmRx awgn_channel(ofdmTx, snr); nomaDe noma_dec(ofdm_rx(ofdmRx, nSubcarriers, cpLen), alpha); berNoma(k) berNoma(k) count_error(nomaDe, bitSeq); % OFDMA 路线子载波划分 独立解调 ofdmaSym ofdma_enc(modSym, nUsers, nSubcarriers); ofdmTx ofdm_tx(ofdmaSym, nSubcarriers, cpLen); ofdmRx awgn_channel(ofdmTx, snr); ofdmaDe ofdma_dec(ofdm_rx(ofdmRx, nSubcarriers, cpLen), nUsers); berOfdma(k) berOfdma(k) count_error(ofdmaDe, bitSeq); end berNoma(k) berNoma(k) / (nFrames * 128); berOfdma(k) berOfdma(k) / (nFrames * 128); end plot_ber(snrRange, berNoma, berOfdma);主循环的重点是两条路线的对称性。NOMA 路线在 OFDM 基础上多了 noma_enc / noma_dec 一进一出OFDMA 路线则是 ofdma_enc / ofdma_dec。两条路线共享同一套 Turbo 编码与 OFDM 收发最后画出来的 BER 差异就归结到多址方式本身这是这套工程最值得利用的对比设计。两个 BER 数组分别除以总比特数做归一化避免帧数差异导致曲线整体平移。参数说明snrRange 从 0 dB 扫到 20 dB、步进 2 dB覆盖了 Turbo 码纠错增益最明显的区间nFrames2000 是为了让低误码率区间10 的负三次方附近统计稳定如果只看趋势500 帧也能跑但曲线尾部会抖cpLen16 与 nSubcarriers64 的 1/4 比例是 OFDM 系统里的常用值后面避坑章节会讲它不匹配时的症状。文件列表里没有单独的 qpsk_mod.m 和 awgn_channel.m实际代码通常把调制合并进 data_gen.m、把信道合并进 ofdm_rx.m我习惯把它们拆成独立函数便于逐级排查。3.2 data_gen.m 与 Turbo 编解码参数data_gen.m 负责生成发端比特序列。除了随机比特外实际使用中最好让它把帧号也带上便于出错后定位是从哪一帧开始错的。Turbo 编码器 turbo_enc.m 是两路递归卷积码的并联交织器放在第二路之前function out turbo_enc(inBits) % inBits: 1xN 原始信息比特 % 返回: 1x3N依次为系统位、两路校验位 N length(inBits); sys inBits; % 系统位 p1 rsc_encode(inBits); % 第一路校验 interleaved interleave(inBits); % 交织器 p2 rsc_encode(interleaved); % 第二路校验 out [sys, p1, p2]; end关键参数是交织器深度 N 和迭代次数。交织器深度不足时两个成员译码器交换的外部信息相关性变强中期迭代收益消失迭代次数不够时低信噪比区间 BER 降不下来。配套的 turbo_dec.m 一般用 max-log-MAP 做迭代译码迭代 4 到 6 次是个收尾平衡点超过 6 次 BER 改善已经不明显仿真时间却会拉高三四成。data_gen.m 里还要注意编码速率的统计口径。Turbo 编码速率 1/3 意味着每比特多出两路校验noma_dec.m 和 ofdma_dec.m 内部做软信息映射时对数似然比的幅度要按 1/3 速率对应的能量折算。如果漏了这一步两条链路都会出现 BER 在高信噪比下突然抬高的“小平台”。3.3 noma_enc.m 与 noma_dec.m 的功率域编解码实现noma_enc.m 在工程里的实现比原理示意多了一层维度对齐它要保证两个用户的调制符号在同一个子载波位置上对齐然后做叠加。noma_dec.m 处理两步 SICfunction rxBits noma_dec(rxSym, H, alpha) % rxSym: 接收端 OFDM 解调后的叠加符号 % H : 用户信道增益H(1) 近端H(2) 远端 % 第一步解远端用户远端功率大先解 symFarEst rxSym / sqrt(1-alpha) / H(2); bitsFar qpsk_demod(symFarEst); symFarRe qpsk_mod(bitsFar) * sqrt(1-alpha) * H(2); % 第二步消去远端干扰再解近端用户 symNearRem rxSym - symFarRe; bitsNear qpsk_demod(symNearRem / sqrt(alpha) / H(1)); rxBits [bitsNear, bitsFar]; end这个顺序是 NOMA 仿真的核心考点。常见错误是把近端用户当成第一步解码对象结果远端用户的强干扰直接让近端误码率崩掉。另一个细节是干扰重构必须用远端用户的硬判决符号重新映射而不是直接拿接收符号相减否则噪声被搬移两次SIC 增益被吃掉一半。提示SIC 里的干扰重构符号必须来自硬判决再映射直接拿接收符号相减会把噪声带进第二次解调。功率分配上我一般会把 α 设在 0.25 再扫描。α 一旦高于 0.4近端用户和远端用户的功率差只有不到 4 dBSIC 第一步几乎必错α 低于 0.1近端用户信噪比太低即使 SIC 完全正确也没用。所以仿真报告里看到 α 扫描曲线第一眼要盯两边的平衡点落在哪。4. 从子载波分配到 BER 统计OFDM 与 OFDMA 收发链路4.1 ofdm_tx.m 与 ofdm_rx.mIFFT 调制与循环前缀ofdm_tx.m 的核心操作是把频域符号通过 IFFT 转成时域采样再加循环前缀。这里有一个容易被忽视的数据形状问题传入的符号矩阵维度必须跟 nSubcarriers 对齐。常见做法是接收一列频域符号填满 nSub 个子载波function tx ofdm_tx(symFrame, nSub, cpLen) % symFrame: nSub x 1 频域符号列向量 ifftOut ifft(symFrame, nSub, 1); % 沿第一维做 IFFT tx [ifftOut(end-cpLen1:end, :); ifftOut]; % 循环前缀取尾部 tx tx(:); % 展成时域序列 endofdm_rx.m 是逆过程把时域序列先 reshape 回 nSubcpLen 行丢掉开头 cpLen 行再做 FFT然后按发射端的列顺序取符号。接收端最容易漏的一步是 FFT 之前必须先按发射端的展列顺序做 reshape直接用默认的列填充顺序常常把符号顺序对调导致后面的 NOMA 解码拿到错位符号。这种错误表现很隐蔽因为单条 BER 曲线看起来依然在下降只是比正常情况差几个 dB没有耐心的排查很容易误判成信道模型问题。循环前缀长度 cpLen 要覆盖信道最大时延扩展这句在不同教材里写法不一在仿真里更现实的判断标准是信道冲激响应长度超过 cpLen 时前一个 OFDM 符号的尾部渗入后一个符号产生符号间干扰。默认 cpLen16 配上 64 个子载波能容忍 16 个采样点多径展宽如果信道模型的多径抽头超过这个数第一时间把 cpLen 加上去。4.2 ofdma_enc.m 与 ofdma_dec.m子载波按块划分与独立解调OFDMA 在代码里的角色是把两个用户的符号放到不重叠的子载波集合上。以 64 子载波、2 用户为例常见做法是用户 1 占前 32 个子载波用户 2 占后 32 个function txFrame ofdma_enc(modSym, nUsers, nSub) % modSym: nSub/nUsers x nUsers每列是一个用户的调制符号 txFrame zeros(nSub, 1); blockLen nSub / nUsers; for u 1:nUsers idx (u-1) * blockLen (1:blockLen); txFrame(idx) modSym(:, u); end endofdma_dec.m 只需要反向抽取对应子载波的符号。由于子载波间正交接收端拿到频域符号后不需要联合检测复杂度比 NOMA 的 SIC 低一大截。这个反差正好是两种多址方式接收复杂度的直观体现——OFDMA 解码是“按索引取数”NOMA 解码是“解一层、消一层、再解下一层”仿真报告里写对比分析时接收机复杂度这一句可以从这里得到直接支撑。OFDMA 路线的性能瓶颈不在这里而在子载波调度策略本身。固定分块在信道频率选择性强的场景里不是最优分配但作为 BER 对比基线是公平的因为 NOMA 路线也在同样一组子载波上承载用户。如果想让 OFDMA 基线更强可以把固定分块改成按信道增益动态分配不过那属于系统级调度优化跟多址本身无关。4.3 plot_ber.m 与 ber.mat两条曲线的统计口径plot_ber.m 负责把 BER 随 SNR 的变化画成对比图基本职责是在半对数坐标下画两条曲线并标注不同 α 对应的 NOMA 结果。绘图部分的统计分母必须一致NOMA 路线两个用户合起来算误码率OFDMA 路线也是两个用户合起来算否则两条曲线没有可比性。很多第一次上手的人会下意识“分别统计每个用户”然后画四条线结果近端用户曲线和远端用户曲线一高一低跟 OFDMA 的合并曲线没法直接对比。ber.mat 保存的是跑好的结果里面至少应该有 snrRange、berNoma、berOfdma 三个变量。不想重跑整条链路时可以直接load(ber.mat)再调用 plot_ber。加载后先whos看一下变量列表确认变量名跟当前脚本一致避免把主脚本里的同名变量覆盖掉。在报告里怎么用这些数据我习惯输出两句话一是低信噪比区间比如 4 dB 以下NOMA 与 OFDMA 谁先跌下来二是在目标误码率比如 10 的负三次方上两种方案差几个 dB。前一句说明编码增益与 SIC 的交互后一句说明功率分配策略的价值这两句话正好对应 NOMA-OFDMA 融合研究的两个核心问题用户配对怎么选、功率分配怎么调。如果只看曲线形状而不做这两个数值提取仿真结果很难在文档里形成明确结论。5. NOMA-OFDMA 仿真常见问题排查五个容易翻车的点5.1 Turbo 译码不收敛BER 在低信噪比区卡在 0.5 平台现象仿真跑完NOMA 和 OFDMA 的 BER 在某个信噪比以上不再下降呈现近乎水平的直线数值在 0.5 附近。原因Turbo 编码器的移位寄存器状态没有逐帧复位。MATLAB 里如果 turbo_enc.m 的状态变量定义在函数外部或持久变量上一帧的寄存器值会带入下一帧相当于给每个帧头掺了一段随机错误。另一个常见原因是交织器与解交织器索引不一致交织表生成规则在编码端和译码端各写了一份两边没对齐。解决在 turbo_enc.m 每帧入口强制复位移位寄存器交织表只生成一份编码器和译码器引用同一份副本。改完后先跑 OFDMA 单路线验证BER 曲线应当随 SNR 单调下降再回来看 NOMA 曲线。5.2 SIC 顺序搞反近端用户 BER 反而比远端用户差现象α0.25 时近端用户 BER 明显高于远端用户两条用户级曲线出现交叉而理论上近端用户信道好BER 应该更低。原因noma_dec.m 里第一步解的是近端用户。近端用户应是最后被解码的对象因为它的信号混在远端强信号之下先解它等于在没有消掉强干扰时做判决符号错误率自然高。解决把解码顺序完整反过来——第一步解功率占比大的远端用户信道估计 H(2) 对准它重构远端符号后从接收信号里消去第二步才解近端。改完后近端用户 BER 应回落到远端用户下面两条曲线不再交叉。5.3 叠加信号发射功率未归一化星座图外扩削顶现象OFDM 时域信号幅度明显大于单用户场景接收端 BER 在全部信噪比区间都比预期差且 NOMA 路线的曲线形状跟 OFDMA 对不上。原因两个功率归一化的 QPSK 信号叠加后瞬时功率取决于两个符号的相位差峰值因子比单用户高。如果发射端做了硬限幅或削顶SIC 重构用的符号幅度已经失真后面的干扰消除全部建立在错误的重构上。解决发射端不做硬限幅。需要固定平均功率时把叠加后的符号按 1/sqrt(nUsers) 缩放做能量归一化同时把 α 的含义换算清楚——α 是叠加前两个用户之间的功率比不是归一化之后的绝对功率。归一化放在叠加之后功率比例放在叠加之前。5.4 循环前缀长度不够中等信噪比出现共平台现象OFDMA 和 NOMA 两条曲线在 8 到 12 dB 之间同时出现平台超过这个区间又继续下降看起来像两条曲线被“同时绊了一下”。原因cpLen16 小于信道冲激响应抽头数前一个 OFDM 符号的拖尾混入当前符号子载波正交性被破坏。这种错误属于符号间干扰而不是加性噪声它不会随 SNR 提高而消失只会在某个信噪比区间把两条曲线同时压住。解决把信道模型的最大抽头数打印出来设为 nTap让 cpLen 至少大于 nTap。如果是多径抽头数超过 16 的仿真场景直接把 cpLen 加到 32 或 64 重跑平台就会消失。5.5 BER 统计分母不一致两条曲线整体平移现象NOMA 曲线看起来比 OFDMA 好了 3 dB但仔细一算是两套数据的统计基数不同曲线形状其实一样。原因NOMA 解码时把系统位和校验位都算进了误码分母OFDMA 路线只统计了信息位或者 Turbo 编码后某个用户存在打孔两边的有效比特数不一致。这种问题最隐蔽因为曲线形状正常、趋势也正常只有数值整体平移。解决在 main_v2_ber.m 里只用一个变量 nTotalBits 做统计分母两条路线统一用 Turbo 译码后的信息比特做误码判决不要拿编码后的全序列直接比。每次改动统计代码后先在同一信噪比下跑两遍确认数值完全一致再放开全区间扫描。6. 进阶把功率分配系数 α 做成扫描向量前面的流程把 α 固定成单一值但 NOMA-OFDMA 验证里最值得看的就是 α 从 0.1 到 0.4 变化时两条曲线的响应。把主脚本里的 α 从常量改成向量外层加一圈循环alphaList 0.1:0.05:0.4; berAlpha zeros(length(alphaList), length(snrRange)); for a 1:length(alphaList) alpha alphaList(a); % ... 内部复用主循环的 NOMA 链路 ... berAlpha(a, :) berNoma; end plot_ber_alpha(snrRange, berAlpha, alphaList);扫描完会得到一组 NOMA 曲线同一 SNR 下不同 α 的 BER 呈现 U 形α 太小时近端用户信噪比不足α 太大时远端用户 SIC 第一步崩掉中间某个 α 值附近两用户同时达到最低误码率。我习惯再补一张图——在目标误码率 10 的负三次方下把所需信噪比按 α 画成横轴曲线最低点对应的就是当前用户配对下的最佳功率分配。如果两张图的极值点不一样优先看第二张因为工程指标通常以目标 BER 为约束。从那以后我每次拿到一份新的多址仿真代码都会强制走一遍固定顺序先跑单用户 OFDM 链路确认无误码底噪再开双用户 OFDMA 建立基线最后才开 NOMA 的 SIC 与功率分配。链路本身的问题不排除干净多址对比的结果就不可信。希望这套排查顺序能帮到你省下对着曲线猜原因的时间。本文还有配套的精品资源点击获取