5G-NOMA功率分配MATLAB仿真:从SIC解码到算法实现全解析 简介提供一套完整的MATLAB 5G-NOMA系统功率分配仿真环境适配MATLAB 2022a版本面向通信专业学生与工程师用于快速复现并评估单小区多用户非正交多址系统的功率域资源分配性能。资源包共8个文件、约8.87MB包含带逐行中文注释的MATLAB主程序、演示完整操作流程的AVI操作录像、辅助验证结果的仿真图片以及txt/py等配置文件。核心计算逻辑基于标准香农公式实现用户容量Cf、Cnf、Cn及累积容量Ca等指标可清晰看出功率分配策略对用户速率与容量的影响并输出SINR、用户可达速率对比及功率系数影响分析。操作录像还专门强调了工作目录的正确设置避免路径错误导致变量未定义或数据加载失败适合直接运行并开展二次开发。压缩包内文件类型覆盖主程序、操作录屏、结果图片与说明文档结构清楚便于按需取用目前已有156人学习下载。1. 5G-NOMA功率分配仿真一个MATLAB资源包能帮你省下什么有段时间我在做5G-NOMA的链路级仿真项目X卡在功率分配模块上整整两周。当时手头有一堆参考论文但论文里那几行优化公式落到MATLAB代码里各种对不上号——信道增益排序方向错了、SIC解调顺序反了、功率归一化漏了跑出来的误码率曲线简直没法看。后来拿到一份带中文注释和操作录像的MATLAB仿真资源包半天时间就把系统级平台跑通了并且功率分配算法部分可以直接改参数做对照实验。这份资源的核心价值是它把NOMA系统建模、用户配对、SIC解码和功率分配算法的完整链路打包成可运行的MATLAB工程每行关键代码都有中文注释还附带操作录像帮你对步骤。适合正在做5G通信方向毕业设计、课程设计或者想快速搭一个NOMA仿真平台验证算法效果的人——你不用从零啃通信原理和MATLAB语法直接改参数、跑结果、看曲线就行。2. 功率域NOMA的工作机制叠加编码、SIC解码与功率分配的角色2.1 为什么NOMA能提升频谱效率从正交到非正交的演进逻辑传统正交多址OMA系统里不同用户占用不同的时频资源块互相不干扰但频谱利用率天花板明显——5G的eMBB场景要支撑海量连接和高吞吐正交资源划分太奢侈了。NOMA的思路是让多个用户共享同一时频资源块在功率域或码域上做区分接收端用干扰消除算法把信号一层层解出来。功率域NOMA是最主流、也最容易用MATLAB仿真验证的一种。它不需要复杂的码本设计核心就两件事发送端对不同用户信号分配不同功率并叠加发射接收端按信道增益从强到弱依次做串行干扰消除SIC。由于信道好的用户先解码并消除其他用户干扰信道差的用户反而因为分配功率高而更容易保底系统总吞吐量能比OMA提升30%~50%具体数值取决于信道条件和功率分配策略。在MATLAB里建模这个链路不算难但有两个前提必须理清第一功率分配算法决定每个用户的发射功率直接影响SIC的解码门限第二用户配对方式决定哪些用户共享同一资源块——通常是一个信道好的近用户配一个信道差的远用户这样功率差大、SIC增益明显。这两点在仿真代码里对应两个独立的子函数后面第4章会具体展开。2.2 发送端功率叠加与接收端SIC解码功率域NOMA的发送端假设同一子信道上两个用户u1和u2总发射功率为P功率分配系数分别为α1和α2α1α21那么发送信号为s sqrt(α1·P)·s1 sqrt(α2·P)·s2这里有个关键约定信道增益低的用户分配更大的功率系数。原因是信道差的用户信号弱如果功率也低SIC解码时它的信号会被信道好的用户信号完全淹没。所以一般约定|h1|² |h2|² 时α1 α2。也就是说信道好的用户拿小功率信道差的用户拿大功率。接收端SIC解码顺序信道好的用户u1先解码把u2的信号当作干扰直接解出自己的信号因为自己的信号功率虽小但信道条件好等效信噪比能扛得住然后u1从接收信号中减去自己的信号分量剩下的就是u2的信号由于u2功率大u2在u1解码完后的残余信号里信噪比也很高能顺利解出。MATLAB实现这个过程的伪代码逻辑是% SIC解码顺序信道增益降序排列 [~, idx] sort(channel_gain, descend); % idx(1)是信道最好的用户先解码 for k 1:num_users % 计算当前用户信号在接收信号中的功率占比 % 解码当前用户信号 % 减去已解码信号分量更新残余接收信号 end逻辑说明这段代码的核心是按信道增益排序后依次解码。每个用户解码时把尚未解码的用户信号视为噪声所以排序方向错了整个SIC链路就全乱了。参数说明里channel_gain是各用户的信道增益向量num_users是共享同一资源块的用户数一般取2或3超过3个用户时SIC的误差传播会急剧放大仿真结果很难看。2.3 功率分配在NOMA中的核心地位功率分配策略直接决定NOMA系统的性能上限。固定功率分配FTPA实现简单但公平性差——信道好的用户速率极高、信道差的用户可能连最低速率都保不住。分数阶功率分配FTPA引入一个公平性因子在吞吐量和公平性之间折中。注水算法则是在总功率约束下最大化系统总速率但需要知道所有用户的信道状态信息计算复杂度高不适合实时场景。在MATLAB仿真里这三种策略对应三条不同的功率分配曲线直接影响后面误码率、吞吐量、用户速率均衡性三个指标。很多初学者在跑仿真时为了省事直接用等功率分配α1α20.5结果SIC解码出来的误码率跟OMA差不多然后得出“NOMA也没什么优势”的错误结论。实际上等功率分配在NOMA里是最差的选择——功率差没拉开SIC增益几乎为零。所以功率分配这块既不能为了追求吞吐量把功率全给信道差的用户也不能为了公平性把功率平均分。我一般会在仿真参数表里同时定义总功率P、功率分配策略类型、以及策略对应的关键参数比如FTPA的衰减因子这样跑一个对照实验只需要改一行配置。3. MATLAB仿真框架搭建从系统参数到信道模型的分层设计3.1 顶层系统参数配置载波频率、带宽、用户数与调制阶数拿到这份资源包后第一步不是看代码而是先改仿真参数。这套MATLAB工程的顶层参数集中在一个初始化脚本里包含载波频率、系统带宽、子载波数、用户数、用户配对方式、调制方式、信道类型、目标误码率等。参数直接决定仿真运行时间——我之前有次把子载波数设成2048、蒙特卡洛次数设成10000结果脚本跑了一整晚还没结束。% 仿真系统参数配置 fc 2e9; % 载波频率 2GHz5G FR1典型频段 bw 20e6; % 系统带宽 20MHz num_subcarriers 128; % 子载波数资源块粒度 num_users_per_sub 2; % 每个子信道复用的用户数NOMA配对 total_power 46; % 基站总发射功率 dBm约40W noise_power -174 10*log10(bw) noise_figure; % 底噪功率 dBm mod_order 4; % QPSK调制高阶调试验时可改16QAM num_mc_trials 1000; % 蒙特卡洛仿真次数调试时先设100逻辑说明这段参数配置是整个仿真平台的“总闸门”。总功率和噪声功率都用了dBm单位计算信噪比时直接相减即可不用来回转换线性功率。注意noise_figure是接收机噪声系数资源包里默认设置为5dB实际仿真时可以按场景调整。参数说明num_subcarriers决定频域粒度资源包默认128个子载波对应一个20MHz带宽下的基本资源块配置num_users_per_sub设成2是最经典的NOMA场景改成3或4时需要同步修改SIC解码部分的循环逻辑资源包代码已经支持但复杂度会明显上升。3.2 信道模型与用户配对频率选择性信道与归一化增益NOMA仿真里信道模型的选择比OMA更敏感。因为SIC解码顺序完全依赖信道增益排序如果信道模型太理想比如纯AWGN所有用户的信道增益接近相等功率分配拉开差距的效果就体现不出来仿真结果跟OMA几乎没区别。资源包默认使用瑞利衰落信道模型并考虑路径损耗——近用户和远用户的信道增益差异通过路径损耗体现。% 瑞利衰落信道生成 路径损耗 distance_near 100; % 近用户距离基站 100m distance_far 500; % 远用户距离基站 500m path_loss_exp 3.5; % 城区典型路径损耗指数 % 生成信道系数h 小尺度衰落 * sqrt(路径损耗) h_near (randn(1,num_subcarriers) 1j*randn(1,num_subcarriers)) / sqrt(2); h_far (randn(1,num_subcarriers) 1j*randn(1,num_subcarriers)) / sqrt(2); pl_near 10^(-28/10) * distance_near^(-path_loss_exp); pl_far 10^(-28/10) * distance_far^(-path_loss_exp); h_near h_near .* sqrt(pl_near); h_far h_far .* sqrt(pl_far); % 计算归一化信道增益用于用户配对和SIC排序 gain_near abs(h_near).^2; gain_far abs(h_far).^2;逻辑说明信道系数由单位方差复高斯随机变量乘上路径损耗的平方根得到这是OFDM系统仿真的标准做法。randn生成实部和虚部都服从标准正态分布除以sqrt(2)保证幅度期望为1。路径损耗用对数距离模型L(dB) 28 10·n·log10(d)这里的28dB是1米参考距离处的路径损耗n是路径损耗指数。参数说明distance_near和distance_far决定了两个用户之间的信道增益差这个差值直接影响NOMA功率分配的收益——差值太小比如100m和150mSIC增益有限差值太大比如100m和1000m远用户即使分到最多功率也达不到最低速率门限。实际仿真中我会把远近用户距离比控制在3~5倍效果最典型。3.3 代码结构设计主函数、算法子函数、统计输出三层划分这份资源包的工程结构分三层跟我个人做仿真项目的习惯很一致。第一层是主脚本负责参数初始化、循环调度、汇总结果第二层是核心算法子函数包括用户配对、功率分配、SIC解码、误码率统计第三层是结果可视化脚本负责画星座图、误码率曲线、吞吐量柱状图。这样分层的最大好处是换功率分配算法时只改第二层对应的子函数其他代码完全不动。% 主脚本调用结构 % 1. 初始化参数 config.m % 2. 生成信道并配对用户 [user_pairs, channel_gain] pairing(channel_params) % 3. 功率分配 alpha power_allocation(strategy, channel_gain, power_params) % 4. 发送端叠加编码 tx_signal superpose_signals(signal1, signal2, alpha) % 5. 经过信道 rx_signal channel_transmit(tx_signal, h) % 6. 接收端SIC解码 [rx_bit1, rx_bit2] sic_decode(rx_signal, h, alpha) % 7. 统计误码率和吞吐量 ber bit_error_rate(tx_bits, rx_bits)逻辑说明这种调用链把NOMA收发流程拆成了7个步骤每步对应一个独立子函数。如果只想看功率分配的作用可以直接在步骤3打断点检查alpha功率分配系数在不同策略下的变化。参数说明strategy参数支持equal等功率、ftp固定功率分配、frac分数阶功率分配、water注水算法在config.m里修改这个字符串变量就能切换算法不需要改其他代码。这个设计对于做对照实验特别友好——同一套信道、同一组比特只换功率分配策略跑出来的性能差就是算法的真实差距。4. 功率分配算法实战固定分配、分数阶分配与注水算法的代码实现4.1 等功率分配与固定功率分配基准线和最简单的分配方式等功率分配作为NOMA的“反面教材”基准线在对照实验中必不可少。它的代码最简单但跑出来的曲线能让你直观看到功率差对SIC的重要性。固定功率分配FTPA在此基础上引入一个固定比例——信道好的用户分小功率、信道差的用户分大功率比例预先设定不随信道变化。function alpha power_allocation_equal(P, num_users) % 等功率分配每个用户分到相同功率 alpha ones(1, num_users) / num_users; end function alpha power_allocation_fixed(P, channel_gain, ratio) % 固定功率分配按预设比例分配信道差用户拿更多功率 num_users length(channel_gain); alpha zeros(1, num_users); % 按信道增益降序排序增益从小到大对应功率从大到小 [~, idx] sort(channel_gain, ascend); % 给信道最差的用户分配 ratio 比例的功率 alpha(idx(1)) ratio; alpha(idx(2)) 1 - ratio; end逻辑说明等功率分配里alpha全是0.5此时SIC解码时用户间干扰完全无法消除仿真出来的速率曲线和OMA几乎重合。固定功率分配里ratio是信道差用户的功率占比一般设0.7~0.9ratio越大信道差用户的保护越强但信道好用户的速率会明显下降。参数说明注意sort用ascend排序目的是让idx(1)对应信道增益最小的用户即信道最差的用户。很多初学者在这里会用错排序方向导致功率分配给了信道最好的人仿真结果直接反直觉。4.2 分数阶功率分配FTPA公平性因子的作用与代码实现分数阶功率分配是目前论文和课题里最常用的策略它用一个公平性因子α_ftp控制功率分配的不均匀程度。当α_ftp0时退化为等功率分配α_ftp1时功率与信道增益成反比极端不公平。通常取0.2~0.6之间兼顾系统吞吐量和用户公平性。function alpha power_allocation_frac(P, channel_gain, alpha_ftp) % 分数阶功率分配alpha_i P * (1/gain_i)^alpha_ftp / sum(...) num_users length(channel_gain); numerator (1 ./ channel_gain).^alpha_ftp; denominator sum(numerator); alpha P * numerator / denominator; % 归一化保证功率系数之和为1 alpha alpha / sum(alpha); end逻辑说明核心公式是功率系数与信道增益的-α_ftp次幂成正比。信道增益小信道差的用户其倒数的α_ftp次幂大分到的功率就多。α_ftp越大功率分配越倾斜。参数说明alpha_ftp这个参数需要手动调节在不同信道条件下最优值不同。我一般会写一个外层循环扫alpha_ftp从0.1到0.8画出吞吐量和公平性随alpha_ftp变化的曲线然后选取拐点处的值。这是资源包操作录像里重点演示的操作——因为alpha_ftp不是越大越好超过0.6后信道好用户的速率会急剧恶化系统总吞吐量反而下降。4.3 迭代注水算法总功率约束下最大化系统吞吐量注水算法是功率分配的性能上界它的思想是把更多的功率分配给信道条件好的子信道因为那里“性价比”最高。但在NOMA场景下注水算法要同时考虑SIC解码的功率差约束不能完全按OMA的思路来做。function alpha power_allocation_water(P, channel_gain, noise, max_iter) % 迭代注水算法最大化系统总速率 % 初始化功率分配 num_users length(channel_gain); alpha P / num_users * ones(1, num_users); tolerance 1e-3; for iter 1:max_iter % 计算每个用户的等效信噪比 sinr alpha .* P .* channel_gain / noise; % 注水水位1/lambda mean(1./sinr) water_level mean(1 ./ sinr); % 更新功率分配alpha_new max(0, water_level - 1./channel_gain*...) alpha_new max(0, water_level - noise ./ (P .* channel_gain)); % 归一化 alpha_new P * alpha_new / sum(alpha_new); % 判断收敛 if norm(alpha_new - alpha) tolerance break; end alpha alpha_new; end alpha alpha / sum(alpha); end逻辑说明这是一个简化版的迭代注水实现。每次迭代先算当前分配下的SINR再根据注水公式更新功率分配。max(0, ...)确保功率不为负归一化保证总功率恒定。收敛判据是前后两次迭代的功率分配向量差值的范数小于容差。参数说明max_iter一般设50就到头了实际仿真中10~20次迭代就能收敛。tolerance设1e-3够用设太小1e-6会显著增加迭代次数对结果精度几乎没帮助。另一个坑是噪声归一化——noise必须是线性功率值不能直接用dBm数值参与计算否则注水公式完全失效。值得注意的一点注水算法虽然总速率最高但它的功率分配结果可能不满足SIC解码的门限约束——尤其是信道好的用户可能分配到极小的功率导致它第一层解码就失败。所以在工程实用中注水算法的结果通常只作为性能上界参考实际系统还是用FTPA多一些。这个资源包的对照实验里注水算法的总速率通常比FTPA高10%~15%但信道好用户的误码率也可能比FTPA高一个量级。5. 调参与避坑指南初学者最常见的五个翻车现场5.1 翻车现场一SIC解码顺序用错方向信号全解错现象仿真出来的误码率接近0.5跟随机猜差不多而且两个用户的误码率都很高。原因SIC解码顺序应该按信道增益从高到低排列——信道好的先解码。但很多初学者在排序时用了ascend导致信道差的用户先去解码它的信号功率大但信道增益差解出来全是错的错误信号被减掉后反而把信道好用户的信号也破坏了。解决在排序前加一行显示当前排序结果确认是降序排列。代码里统一用sort(channel_gain, descend)并且排序索引要同步传递给功率分配函数和SIC解码函数保证两边用的是同一套顺序。我曾经在调试时发现功率分配用的是降序索引、SIC解码用的是升序索引两个函数各排各的结果恶劣到完全不能看。从那以后我每次都会在配对和SIC两个函数入口各加一个assert检查排序方向一致。5.2 翻车现场二功率归一化漏做SIC解码门限全乱现象仿真曲线整体偏移吞吐量看起来比理论值高出一大截——高得离谱明显不合理。原因功率分配函数返回的α系数没有除以总功率和导致叠加信号的总功率超过设定值P。在接收端SIC解码门限是基于归一化功率计算的实际功率偏大时等效信噪比被抬高误码率虚低、吞吐量虚高。解决在功率分配函数末尾统一加alpha alpha / sum(alpha)确保所有用户的功率系数之和严格等于1。资源包代码里每个功率分配函数都有这一行但如果你自己新写了一个功率分配算法很容易忘记加。我一般会在主脚本里加一行assert(abs(sum(alpha) - 1) 1e-6)程序一运行就自动检查省得事后对着错误曲线瞎猜。5.3 翻车现场三注水算法不收敛迭代次数耗尽现象运行注水算法时10次仿真里有几次结果明显异常——某次吞吐量特别低功率分配系数出现负值或者大于1的情况。原因注水算法对初始值敏感尤其是信道增益动态范围很大时初始等功率分配可能让某些用户的SINR特别低导致注水水位计算偏离功率更新后出现震荡不收敛。另外噪声参数如果用了dBm值而不是线性值整个公式就错了。解决首先确认noise参数是线性功率瓦特不是dBm。然后给注水算法加一个“稳定化”处理——每次迭代的步长乘以一个0.8的衰减因子防止功率更新在临界点附近震荡。最后把收敛容差适当放大到1e-2因为注水算法对精度要求不高——功率系数差0.01对最终速率的贡献可以忽略。如果还是不收敛检查初值把等功率分配改成按信道增益反比分配能加速收敛。5.4 翻车现场四随机信道不固定种子蒙特卡洛对比失真现象两次运行同一个仿真脚本结果差异巨大或者用两套功率分配算法做对比一个比另一个高20%换一次运行时差距变成5%结论完全不稳定。原因MATLAB随机数生成器没固定种子。每次运行生成不同的信道系数蒙特卡洛统计结果没有可比性。更隐蔽的是不同功率分配算法内部可能也调用了随机数函数比如迭代算法里的随机初始化导致算法间的对比混入了随机因素。解决在主脚本最前面加rng(2024)固定随机种子确保每次运行的信道完全一致。如果要比较不同功率分配算法必须在同一批信道下逐个运行算法然后对比结果。资源包的脚本设计已经保证了这一点——信道生成在功率分配之前所以固定种子后所有算法跑的是同一批信道。我个人的习惯是固定三组不同种子分别跑看结论是否在三种信道条件下都成立这样论文里的对比曲线更有说服力。5.5 翻车现场五误以为NOMA总优于OMA不设公平性约束现象仿真发现NOMA总速率比OMA高但其中一个用户的速率还不如OMA甚至低了一半以上。如果只看总速率指标这个结果看似不错但实际系统里这个用户可能直接掉线。原因NOMA牺牲信道好用户的速率来换取信道差用户的接入和总吞吐量提升。如果不加任何公平性约束功率分配算法会把大部分功率给信道差的用户信道好的用户速率被压得很低。在总速率最大化的目标下极端情况是信道差的用户占用几乎全部功率信道好的用户速率接近零——这在工程上不可接受。解决在功率分配算法中加一个最低速率约束或最低功率占比约束。比如在FTPA中把α_ftp最大值限制在0.5以内确保信道好用户至少保留30%的功率。另外我也养成了一个习惯每次跑完仿真不止看总吞吐量曲线还要单独画两个用户的速率对比柱状图用肉眼看公平性。如果发现信道好用户的速率低于OMA方案就说明功率分配倾斜过度了需要回调α_ftp或增加公平性约束项。6. 仿真验证与进阶蒙特卡洛统计、收敛性判别与可视化输出技巧完成基础仿真后下一步是验证算法的可信度。单个信道的误码率曲线意义有限因为瑞利信道是随机的一次信道的仿真结果可能偏差很大。我一般会跑蒙特卡洛统计固定信道参数配置随机生成1000次信道实现每次记录系统吞吐量和各用户速率最后统计均值和方差。这样得到的结果在论文或者课程设计里才有说服力。资源包的操作录像里演示了怎么用parfor并行计算加速——把for循环改成parfor配合Parallel Computing Toolbox1000次仿真从半小时压缩到几分钟前提是每次循环调用独立、不共享内存变量。收敛性判别方面注水算法和任何迭代类算法都要检查收敛行为。我习惯在算法里记录每次迭代后的目标函数值总速率画成一条收敛曲线。正常情况是一条快速下降然后平稳的曲线如果出现锯齿状震荡说明步长太大或容差太紧。判断收敛的标准不是迭代次数达到上限而是目标函数值的变化量连续几次小于容差。有一次我发现注水算法的收敛曲线在平稳后又突然跳变追查后发现是某次迭代里信道增益向量被意外修改了——这个排查过程让我养成了一个习惯迭代算法里所有不变的输入都加constant标志或复制存档防止被意外覆盖。可视化输出这块除了常规的误码率曲线和吞吐量柱状图我最推荐的是“用户-功率-速率”三维关系图X轴是信道好用户的功率分配系数0到1Y轴是信道差用户的速率Z轴是系统总吞吐量。这张图能直观显示功率分配策略的最优工作点——曲面的最高点就是最优功率分配但往往不是总吞吐量的最大值而是在公平性约束下的合理折中。用MATLAB的surf函数画这个曲面每个点对应一组功率分配系数做一次网格扫描就能覆盖整个参数空间。这个技巧是我在某次课题评审时被问“你怎么确定你的功率分配是最优的”之后才补上的——从那以后我每次做功率分配仿真都强制自己画一张三维功率-速率曲面图既验证了算法的正确性也方便看出参数边界在哪里。希望这个验证路径能帮到你。本文还有配套的精品资源点击获取