简介资源包围绕5G非正交多址接入NOMA的功率分配问题整理了完整的实现与学习资料适合通信工程专业学生及5G物理层算法研究人员。包内含MATLAB脚本、Simulink仿真模型、相关论文PDF及图文说明覆盖从NOMA基本原理到功率分配算法仿真验证的完整链条。全部文件共9个压缩包体积仅2.08MB其中两个.m文件提供可直接运行的功率分配与调制代码.slx模型支持交互式仿真PDF文档给出理论支撑。资源目前已有436人浏览学习。通过分析包内模型和代码读者可深入理解基于信噪比差异的功率分配策略掌握恒定功率与动态功率分配的实现方式并学习结合优化算法提升系统吞吐量与公平性的方法为5G网络部署提供参考。1. 5G NOMA 功率分配同样是给用户分功率为什么你的仿真总比别人差一截做 5G 物理层仿真时NOMA非正交多址是绕不开的候选方案。它的核心收益不靠频域或时域划分而靠功率域拉开用户差异再通过 SIC串行干扰消除在接收端把混叠信号一层层剥出来。也就是说功率分配直接决定用户能否解调、系统吞吐量能到多少、弱用户会不会被饿死。很多人在这个环节翻车要么把总功率均分给用户导致 SIC 完全失效要么只盯着和速率优化结果边缘用户速率惨不忍睹。这篇笔记从系统模型、信道生成讲起把分数阶功率分配FTPA、注水算法WF两种主流方案的核心代码和参数边界铺开再给出一套可复现的蒙特卡洛仿真框架。适合正在做 NOMA 物理层仿真的同学、需要对比功率分配算法的工程师以及想验证 SIC 解码链路是否正确的入门者。你会看到每一步的代码、参数怎么设、瓶颈在哪以及我踩过的一些坑。2. NOMA 功率分配仿真先搞清楚系统模型再做算法选型2.1 单小区多用户下行 NOMA 的基本链路关系NOMA 下行链路里基站把多个用户的信号在功率域叠加后从同一资源块发出去。接收端各用户拿到的是混合信号必须依赖信道差异来区分彼此。最常用的场景是单小区单天线、单载波、K 个用户基站总发射功率为 P_total用户 k 的分配功率为 P_k且满足所有用户功率之和不超过 P_total。信道模型一般写成复数信道系数 h_k包含大尺度衰落和小尺度衰落。大尺度用路径损耗指数和距离表示小尺度用瑞利衰落或莱斯衰落生成。排序规则是核心用户按信道增益 |h_k|^2 降序排列增益最强的用户排在最后做 SIC最弱的用户最先解码同时把强用户的信号当作干扰直接解调自己的信号。SIC 解码顺序和功率分配顺序必须一致否则从源头就错了。信道系数生成是整套仿真的地基。常见做法是先给每个用户一个基准距离 d_k然后叠加路径损耗和瑞利衰落分量。路径损耗公式通常写成 PL(d) 128.1 37.6 * log10(d)这是城市宏站场景下比较通用的参数组合。瑞利衰落部分生成标准复高斯随机变量就行。这样生成的 |h_k|^2 就同时包含距离远近和快衰落的随机性排序结果每次仿真都不同更贴近真实调度。2.2 频谱效率、用户速率与公平性指标怎么算NOMA 性能评估离不开两组指标。第一组是吞吐量和频谱效率第二组是公平性常用 Jain 公平性指数或最弱用户速率来表征。吞吐量计算前要先确定每个用户的 SINR而 SINR 的计算顺序完全依赖 SIC 的解码次序。用户 k 在 SIC 解码后自身信号受到的干扰来自哪些用户取决于它排在哪个位置。信道增益最差的用户通常第一个被解码会把所有其他用户的信号都当作干扰SINR 表达式的分母里包含所有强用户的功率加噪声。信道增益最强的用户做 SIC 时已经把所有弱用户的信号消掉了分母只剩噪声和尚未消除的同层残留干扰。SINR 算出来后用户速率用香农公式 R_k B * log2(1 SINR_k) 计算系统总速率是所有用户速率之和。公平性指数则用 K * (sum R_k)^2 / sum (R_k^2) 来算这个值越接近 1 说明用户间速率越均衡。实际仿真中经常出现系统总速率很高但 Jain 指数很低的场景说明某几个强用户把资源吃光了。2.3 关键公式的参数含义与量纲核对功率分配算法里的每个参数都有明确的物理意义量纲没对齐是仿真对不上论文数值的第一大原因。功率相关变量统一用瓦特W或毫瓦mW都行但噪声功率、信道增益、路径损耗这三者的量纲必须配套。信道增益 |h|^2 是无量纲的复数模平方路径损耗要转成线性值再乘进去。噪声功率 N_0 等于热噪声功率谱密度乘以带宽常温下热噪声谱密度约为 -174 dBm/Hz。带宽 B 若用 MHz那噪声功率就是 -174 10*log10(B_Hz) dBm。换算到线性功率后SINR 的计算公式才能保证量纲正确。很多新手直接把 SNR 配成固定值这就丢掉了 NOMA 最核心的距离差异和功率竞争关系。功率分配比例的常用设置也别拍脑袋。分数阶功率分配里衰减因子 α 一般在 0 到 1 之间取值论文里常见 0.2、0.4、0.6 这几档。注水算法里的注水电平 λ 要满足总功率约束是通过二分搜索或牛顿迭代解出来的。这两类算法的参数看着简单实际跑起来对结果的影响非常敏感下一章展开对比。3. 分数阶功率分配与注水算法两个主流方案的代码级对比3.1 固定功率分配FTPA的公式与 MATLAB 最小实现固定功率分配也叫分数阶功率分配核心思想是让信道增益差的用户分到更多功率用衰减因子 α 控制功率分配的倾斜程度。用户 k 的功率按以下公式计算P_k P_total * (|h_k|^2)^(-α) / sum((|h_j|^2)^(-α))j 遍历所有用户。α 0 时退化为平均功率分配α 越大弱用户分到的功率比例越高。这个公式实现起来非常短但必须注意排序在前、计算在后的顺序。以下代码是在 MATLAB 里实现 FTPA 的最小片段输入是信道系数向量 h 和总功率 P_total% 输入: h 是 1xK 的复信道系数向量, P_total 是总发射功率 % 输出: P 是每个用户的分配功率, 满足 sum(P) P_total function P ftpa_power_allocation(h, P_total, alpha) % 取信道增益并做归一化 h_gain abs(h).^2; % 按信道增益降序排序, idx(1) 是信道最强用户 [h_sorted, idx] sort(h_gain, descend); % 计算每个用户的功率权重: |h_k|^(-alpha) % 注意这里用排序后的信道增益, 保证权重和排序一致 weights h_sorted.^(-alpha); % 归一化权重, 使总功率等于 P_total P_sorted P_total * weights / sum(weights); % 映射回原始用户顺序 P zeros(size(h)); P(idx) P_sorted; end这段代码里有两个关键点。第一个是sort的descend参数排序后idx(1)是信道最强用户后续 SIC 解码也从最强用户开始。第二个是所有权重都按排序后的顺序计算后再映射回去这一步漏掉的话功率分配和 SIC 顺序会错位。α 的值直接影响分配结果建议先跑 α 0.4 的基准再往两边扫。3.2 注水算法的迭代求解与功率约束处理注水算法的目标是在总功率约束下最大化所有用户的可达速率和解出来的功率分配满足 KKT 条件。最优解是每个用户的功率 P_k (λ - 1 / |h_k|^2)^其中(x)^表示 max(0, x)λ 是注水电平需要满足所有用户的功率之和等于 P_total。由于 NOMA 的用户间干扰使问题不完全是经典的注水形式实际仿真里常用迭代注水或基于 SIC 的逐次注水把前一个用户的解码顺序当作已知条件。实现注水算法的核心是二分搜索 λ。先设 λ 的一个上界和下界区间然后计算 P_k 判断总和与 P_total 的关系反复缩小区间。下面这段代码是带 SIC 顺序的迭代注水实现% 输入: h_gain 是排序后的信道增益(降序), 和 h 同长度 % 输出: P 是注水后的功率分配向量 function P iterative_waterfilling(h_gain, P_total, noise_power, max_iter) K length(h_gain); P zeros(1, K); % 剩余功率分配给尚未满足条件的用户 remaining P_total; active_users 1:K; % 初始假设所有用户都参与注水 for iter 1:max_iter % 二分搜索注水电平 lambda lambda_low 0; lambda_high remaining max(1 ./ h_gain(active_users)); for bisect 1:50 lambda_mid (lambda_low lambda_high) / 2; % 当前候选功率 P_candidate max(0, lambda_mid - 1 ./ h_gain(active_users)); if sum(P_candidate) remaining lambda_high lambda_mid; else lambda_low lambda_mid; end end P(active_users) max(0, lambda_mid - 1 ./ h_gain(active_users)); % 若某些用户功率为 0, 则剔除后重新搜索 zero_users find(P(active_users) 0); if isempty(zero_users) break; end active_users(zero_users) []; end end这段代码的二分次数设了 50 次基本能收敛到足够精度。信道增益用排序后的h_gain因此注水自然会把更多功率分给弱用户。注意lambda_high的初始值我一般用剩余功率加最大1/h_gain来保证上界够大。若某些用户计算出负功率说明信道太弱不值得分配功率直接剔除并重新搜索。3.3 两种算法的边界条件与选择依据FTPA 和注水算法的适用场景差异很明显。FTPA 的优势是计算量极低单轮分配的复杂度是 O(K)适合大规模用户快速仿真和调参扫描。它的弱点是只按信道增益的负指数加权没有显式考虑噪声和用户间干扰在用户数超过 6 个时性能会明显偏离最优值。注水的性能更接近最优但计算复杂度高每个时隙都要迭代求解 λ在蒙特卡洛仿真里跑几百次时总耗时显著增加。选择依据主要看仿真目的如果是为了画吞吐量和公平性的趋势对比图FTPA 足够如果是为了验证 SIC 链路解码的边界或逼近容量上界注水更合适。还有一种混合做法我在实际仿真中常用先用 FTPA 算出一个初始功率分配然后只对信道增益前十的强用户组做一次迭代注水微调。这样复杂度增加不多但能让强用户间的干扰抑制更充分弱用户的速率也不会被压得太低。4. 把完整仿真跑起来信道生成、SIC 解码与蒙特卡洛主循环4.1 用户位置生成与信道系数计算的可复现代码一套能复现的仿真必须从随机种子管理开始。MATLAB 里用rng(2024)固定随机数生成器保证每次跑出来的信道序列完全一致。用户位置按照均匀分布在小区半径内取样距离直接影响路径损耗因此也影响功率分配的结果。信道生成代码里路径损耗用 dB 表示瑞利衰落用复数高斯随机变量。最终的信道系数要转成线性幅度。以下是一段完整的信道生成代码% 参数设置 rng(2024); num_users 4; % 用户数 cell_radius 500; % 小区半径, 单位米 path_loss_exp 37.6; % 路径损耗指数 shadowing_std 8; % 阴影衰落标准差, dB noise_power_dBm -104; % 噪声功率, dBm, 对应 10MHz 带宽 P_total_dBm 43; % 基站总功率, dBm, 约 20W % 生成用户距离 (均匀分布在小区内) distances cell_radius * sqrt(rand(1, num_users)); % 路径损耗 dB PL_dB 128.1 path_loss_exp * log10(distances); % 阴影衰落 shadowing_dB shadowing_std * randn(1, num_users); % 瑞利衰落信道系数 (复数) rayleigh (randn(1, num_users) 1j * randn(1, num_users)) / sqrt(2); % 合成信道系数: 线性幅度 h rayleigh .* 10.^(-(PL_dB shadowing_dB) / 20); h_gain abs(h).^2;这段代码里小区半径设了 500 米用户距离用sqrt(rand)而不是rand是因为均匀分布在圆形区域内时距离的平方是均匀的用sqrt才能保证空间分布均匀。噪声功率 -104 dBm 对应约 10 MHz 带宽如果你用 20 MHz记得改成 -101 dBm。瑞利衰落除以sqrt(2)是为了让实部和虚部的方差各为 0.5模平方的均值等于 1。信道系数计算好后紧接着做信道增益排序。NOMA 的 SIC 解码顺序完全基于这个排序排序方式决定了后面所有 SINR 的计算顺序。排序结果每轮蒙特卡洛都不一样这正是需要蒙特卡洛仿真的原因——单次信道实现只能反映一个随机时刻的链路质量。4.2 SIC 解码顺序与 SINR 计算的完整链路SIC 解码的过程是逐级消除干扰必须严格按信道增益降序来。信道最弱的用户直接解自己的信号其他所有用户都是干扰信道次弱的用户解码时强度最弱的那个用户信号已经被消除了干扰集合少一个以此类推。SINR 计算的数学表达要写清楚。设用户索引按信道增益降序为 u_1最强, u_2, ..., u_K最弱。用户 u_m 的 SINR 分母中包含所有 m1 到 K 用户的功率加上一个残留干扰项。理想 SIC 假设残留干扰为 0但实际仿真里残留因子 β 常取 0.01 到 0.1 之间对应 -20 dB 到 -10 dB 的消除深度。以下代码计算理想 SIC 下各个用户的 SINR并输出速率和公平性指标% 输入: h_gain 已排序降序, P 是排序后的功率分配 % 计算每个用户的 SINR K length(h_gain); SINR zeros(1, K); noise_linear 10^(noise_power_dBm / 10) / 1000; % dBm 转 W for m 1:K % 分子: 自己的信号功率乘信道增益 signal_power P(m) * h_gain(m); % 分母: 更弱用户的功率之和(它们还没被消除) 噪声 interference 0; for n m1:K interference interference P(n) * h_gain(n); end SINR(m) signal_power / (interference noise_linear); end % 速率计算 (bps/Hz) rate log2(1 SINR); sum_rate sum(rate); % Jain 公平性指数 jain_index (sum_rate)^2 / (K * sum(rate.^2));这段代码的循环里有两点容易写错。第一个是干扰项只累加m1到K的用户因为 SIC 解码顺序从弱到强信道强的用户后解码此时弱用户信号已经被消除不再构成干扰。第二个是噪声功率必须先从 dBm 转成瓦特再参与线性计算直接拿 dBm 值带入公式会出数量级错误。信号功率的物理意义是基站给该用户分配的功率乘以该用户自身的信道增益干扰功率同理。因为每个用户看到的是一个叠加信号功率分配结果的差异直接体现在 SINR 上。4.3 蒙特卡洛主循环批处理与结果汇总蒙特卡洛仿真的目的是统计平均性能消除随机信道带来的波动。主循环里每次迭代生成一组新距离和信道系数然后按同一种功率分配算法计算吞吐量和公平性最后对所有迭代结果取平均。循环次数一般 1000 到 5000 次城市宏站场景下 1000 次就能得到稳定的均值。我一般会在循环开始前用tic/toc计时如果单次信道计算加功率分配超过 5 毫秒就考虑优化代码。注水算法的二分搜索在大规模迭代时是主要的耗时来源此时可以把二分次数从 50 降到 20性能损失很小但速度提升近一倍。循环结束后把每次迭代的sum_rate和jain_index分别存数组最后取均值。画图时横轴用 SNR 或用户数纵轴用吞吐量或公平性指数。多方案对比时把 FTPA 的三种 α 值、注水算法和平均功率分配放在同一张图里用不同线型区分。以下是完整的主循环框架代码num_iter 2000; sum_rate_mean zeros(1, 4); % 四种方案 jain_mean zeros(1, 4); % 方案列表: 平均分配, FTPA alpha0.2, FTPA alpha0.6, 注水 for iter 1:num_iter % 重新生成用户距离和信道 (见 4.1 节代码) % 距离每次重新生成, 保证统计独立性 % 按信道增益降序排序 [h_gain_sorted, idx] sort(h_gain, descend); % 方案1: 平均功率分配 P_avg (P_total_linear / num_users) * ones(1, num_users); % 方案2: FTPA alpha0.2 P_ftp02 ftpa_power_allocation(h, P_total_linear, 0.2); % 方案3: FTPA alpha0.6 P_ftp06 ftpa_power_allocation(h, P_total_linear, 0.6); % 方案4: 注水 P_wf iterative_waterfilling(h_gain_sorted, P_total_linear, noise_linear, 30); % 注意: 所有方案都要按排序后的 h_gain_sorted 计算 SINR % 计算四个方案的 sum_rate 和 jain_index, 累加到均值数组 end sum_rate_mean sum_rate_mean / num_iter; jain_mean jain_mean / num_iter;方案间比较时必须保证信道路径完全一致所以每种算法消耗的都是同一个h只是分配功率不同。排序操作在循环里只做一次四个方案共用排序结果。P_ftp02返回的功率顺序是原始用户顺序计算 SINR 前要重排到降序否则 SIC 顺序会乱掉。5. NOMA 功率分配仿真避坑五条真实踩坑记录5.1 SIC 解码顺序与功率分配顺序不一致导致速率异常低某次我跑出的系统总速率比理论值低了近一半单用户速率全部异常。逐个检查发现功率分配函数返回的功率顺序是按照原始用户编号排的而 SINR 计算用的信道增益是降序排列两个序列的顺序没有对齐。SIC 顺序跟着信道增益走功率分配自然也按同一个顺序二者一错位所有 SINR 公式里的干扰项就全错了。解决方法是功率分配完成后显式做一次重排映射。代码里用P(idx) P_sorted把功率放回原始位置或者干脆把排序后的h_gain_sorted和对应的P_sorted传入 SINR 计算函数保持同一个顺序从头到尾。5.2 均分功率导致弱用户 SINR 为负SIC 链路直接崩溃平均功率分配看似简单在用户数大于 3 时弱用户的分母里堆积了所有强用户的信号功率SINR 经常算出来是负数log2 后速率变成 NaN。这不是代码 bug而是 NOMA 的天然约束功率差异不够大时 SIC 解码条件不满足。解决方法是放弃均分作为对比基线改用 FTPA 的 α 0 退化为均分的近似值或者只把均分放在用户数等于 2 的小场景里做验证。用户数超过 4 时至少用 FTPA α 0.4 起步避免整个仿真链路被 NaN 污染。5.3 注水算法不收敛二分区间上界设太小注水的二分搜索里我把lambda_high设成了用户中最大的1 / h_gain结果迭代 50 次后分配的功率之和明显小于总功率。原因是注水电平 λ 可能大于所有1/h_gain此时所有用户的功率都是正的λ 的上界不足导致永远搜不到正确值。正确的上界应该是remaining max(1 ./ h_gain)把剩余总功率加上最大的倒数值。这条改完二分搜索每次都能收敛功率总和误差低于 1e-6。如果你用牛顿迭代法求 λ记得对目标函数逐步检查迭代超 100 次仍未收敛就重置初值。5.4 噪声功率量纲换算错误导致 SNR 偏移 30 dB有一次仿真的 SNR 整体比论文参考值高了约 30 dB排查半天发现问题出在10^(noise_power_dBm / 10) / 1000这行。原本写的是10^(noise_power_dBm / 10)没有除以 1000把 dBm 直接当成 dBW 用了。dBm 转瓦特必须除以 1000因为 0 dBm 对应 1 mW而 1 W 是 1000 mW。量纲错误在功率分配类仿真里影响是全局性的不只是 SNR 偏移SIC 解码顺序、功率分配权重、公平性指数全都会跟着变。建议在做完信道生成后先跑一个单次仿真脚本打印出sum(P)、noise_linear和SINR三个变量确认量纲对了再跑蒙特卡洛。5.5 功率分配结果没有归一化导致总功率越界FTPA 实现里如果sum(weights)忘记归一化分配出的总功率会超过 P_total 或不足 P_total。表现在结果上是系统总速率忽高忽低没有稳定的增长趋势。FTPA 的表达式里分母是所有权重之和必须有这一步漏掉等于没做功率约束。加一层保护功率分配函数里计算完P后检查abs(sum(P) - P_total) 1e-10超了就报错。这个断言在调试阶段能快速暴露问题跑大规模仿真时再注释掉避免每次迭代都做浮点比较浪费计算。6. 验证你的仿真结果把正确性证明给别人看的三个方法仿真跑完不等于结果可信至少要做三类验证。第一类是退化场景验证把 α 设为 0FTPA 退化为均分此时单用户速率应该和 OMA正交多址的结果接近。第二类是对比验证固定信道不重生成手动口算一组功率分配和 SINR和代码输出逐位核对。第三类是趋势验证随着 SNR 增加系统总速率应该单调递增公平性指数应该稳定在某个区间。我习惯在脚本末尾加一段自动检查逻辑若isnan(sum_rate)或jain_index 1直接输出错误信息中止运行。跑完 2000 次迭代后打印出均值、标准差和 95% 置信区间这样论文汇报数值时能交代清楚波动范围。若均值附近抖动超过 5%就把迭代次数翻倍多数情况下 2000 次足够。进阶验证是对比已有文献曲线。把信道路径损耗的指数从 37.6 改成 30.1对应城区室外热点观察 FTPA 在弱信道衰减下是否比强信道衰减下更接近注水性能。这个趋势符合理论预期信道衰减变小后用户的增益差异缩小FTPA 固定指数权重的优势减弱注水的动态调节收益更明显。搭配一个复杂度对比实验更完整记录每种方案单次蒙特卡洛迭代的运行时间FTPA 应该在毫秒级注水通常在 2 到 3 倍耗时。这两条曲线放在一起读者就能直观判断性能提升值不值得额外计算开销。最后一条习惯所有功率分配函数必须显式接收P_total而不是从全局变量读值。我吃过全局变量的亏改参数跑对比实验时上一次的赋值残留让某组仿真全程用错了总功率浪费了整整半天。把参数全通过函数入参传递每次调用都是独立上下文排查问题时不需要回忆全局状态。这个小习惯希望帮到你。本文还有配套的精品资源点击获取