简介:面向无线通信方向的学生与初入D2D研究的学习者,这份MATLAB仿真实例围绕设备直连通信的信道建模与频谱资源分配展开,可用于课程实验、毕业设计选题验证与算法对比练习。压缩包共22个文件,以20个.m脚本为主体,另有2个.asv自动备份文件,体积约16KB,虽小但结构紧凑:既有主流程入口与D2D用户、常规用户的生成脚本,也包含路径损耗与平均干扰距离计算、干扰项求解等信道环节,还给出随机分配、启发式搜索与最优方案三类资源分配实现,便于横向比较吞吐量与干扰表现。目前已有1214人学习下载。代码按功能拆分为独立函数,读者可替换信道参数、用户密度与功率配置后直接复跑,观察不同算法在覆盖率与频谱效率上的差异,也可作为理解D2D干扰协调与功率控制思路的起点,并在此基础上扩展多小区场景或加入新的启发式策略。
1. 同一条 SINR 曲线,换个种子就翻车:D2D 仿真的不确定性到底来自哪里
有人在 D2D 信道仿真的 MATLAB 工程里把随机种子从 1 改成 2,同一套资源分配算法跑出的平均频谱效率就从 3.4 bps/Hz 掉到 1.7 bps/Hz,改回 1 又原样复现。第一反应是信道模型写错了,于是把路径损耗和瑞利衰落的代码翻了个底朝天,却什么都没找到。问题不在公式:D2D 仿真的输出本身就是强随机量,它由撒点位置、配对距离、阴影实现和对端干扰共同决定,换一组样本,就等于换了一张完全不同的干扰图。分清哪些随机性应该被平均掉、哪些必须显式控制,比抄一份更精确的信道公式重要得多;这也直接决定了后面每一个资源分配结论能不能站得住脚。
2. 从链路预算到干扰矩阵:D2D 仿真真正在算的是什么
2.1 大尺度、阴影、小尺度:三层衰落为什么不能随便交换顺序
一条 D2D 链路的接收功率可以拆成四个因子相乘:发射功率、路径损耗、阴影衰落、小尺度衰落。数学上它们可交换、可合并,工程上却不能混着用,因为每一层对应的时间尺度差了三个数量级。路径损耗随距离单调变化,用户在几十秒内走不出几米,基本是常量;阴影衰落是几百毫秒到几秒量级的慢变对数正态;小尺度衰落是波长量级的快变,2 GHz 下载波波长 15 厘米,用户走半步就完全去相关。
这三个尺度对应的是两类完全不同的仿真。系统级仿真关心的是“网络能承载多少用户、资源分配算法能提升多少吞吐”,它的决策周期是几十毫秒的调度周期,小尺度衰落早已去相关,所以分配算法只能看到大尺度加阴影,小尺度留到最后算性能时才叠加。链路级仿真关心的是误块率、调制编码阶数选择,它只在乎小尺度,大尺度可以直接折算成一个 SNR 偏移量。
把两者搅在一起是新手最常见的翻车点。我见过一个模拟项目,资源分配模块直接读取瞬时信道增益做决策,仿真曲线漂亮得不像话,比固定功率方案高出 60% 的吞吐。看上去是算法强,实际上是算法偷看了未来:真实系统里,这一份瞬时 CSI 需要测量、量化、反馈,等回到发射端早就过期了,过期 CSI 下的性能往往还不如老老实实看大尺度。判断标准很简单——如果你的算法用到了小尺度衰落,就得先回答“这份信息从哪来、延迟多少、量化几比特”,答不上来就说明建模越界了。
阴影衰落还有个容易被忽略的细节:空域相关性。同一个基站到两个邻近用户的阴影分量是相关的,因为它们的传播路径共享了同一批建筑物遮挡。主流做法是假设独立,这让仿真简单很多,但会低估小区边缘的用户相关性,进而高估干扰的随机性。做干扰协调类算法时必须留意这个偏差,做点对点的链路评估则可以忽略。
2.2 复用模式下的 SINR:四个干扰项里哪一个才是主导
D2D 最典型的场景是复用蜂窝用户的上行资源。之所以偏向复用上行而不是下行,是因为上行资源在基站侧可以统一协调,而且 D2D 发射端对基站的干扰是单点的、可测量的;下行复用则要对所有可能被干扰的邻近用户负责,协调成本高得多。
设蜂窝用户 c 以功率 P_c 发往基站,D2D 对 d 的发射端以功率 P_d 发往接收端,服务链路与干扰链路分别记为 g。那么基站侧收到的蜂窝用户 SINR 是:
γ_c = P_c·g(c,B) / ( Σ_{c'≠c} P_c'·g(c',B) + P_d·g(d,B) + N )
而 D2D 接收端的 SINR 是:
γ_d = P_d·g(d) / ( P_c·g(c,rx_d) + Σ_{d'≠d} P_d'·g(d',rx_d) + N )
公式本身没什么可讲的,值得讲的是量级。取一组典型参数:载波 2 GHz,小区半径 500 m,蜂窝用户距基站 200 m,D2D 对间距 30 m,D2D 发射端距基站 100 m,两者发射功率都是 23 dBm,噪声底约 -95 dBm。按 128.1 + 37.6·log10(d_km) 的宏蜂窝路损算,蜂窝用户到基站的接收功率约 -79 dBm;D2D 有用信号按 38.46 + 20·log10(d_m) 的视距路损算,约 -45 dBm;蜂窝用户到 D2D 接收端(假设 150 m)的干扰约 -74 dBm。
算完会发现一件很反直觉的事:D2D 接收端的 SINR 高达 29 dB 左右,性能好得离谱;真正被卡死的是基站侧——D2D 发射端对基站的干扰是 -67.5 dBm,比蜂窝用户的有用信号还高 11 dB。这就是所谓的近端-远端问题:D2D 用户彼此很近,但它们对基站的干扰取决于 D2D 发射端离基站多近,而不是彼此多近。
这个量级直觉决定了仿真的重点。如果你的仿真里 D2D 的 SINR 分布漂亮但整体吞吐上不去,八成是基站侧的干扰建模漏了项,或者漏掉了 D2D 发射端到基站这条链路。很多公开代码只算 D2D 对之间的 SINR,把蜂窝链路当成不受影响的背景,结论自然一片大好。
2.3 快照式蒙特卡洛:为什么 D2D 仿真不做连续时间演化
D2D 系统级仿真几乎都是快照式(snapshot / drop-based)的:每个 drop 里重新撒一次用户位置、重新生成一套阴影实现,小尺度要么在 drop 内生成若干次独立实现,要么直接用遍历容量公式算期望,不做时间上的连续演化。
原因还是时间尺度。3 km/h 步行速度、2 GHz 载波,多普勒频移约 5.6 Hz,相干时间约 90 ms。如果要模拟 10 秒的真实演化,就得更新上百次小尺度信道;而评估中断概率、平均吞吐这类统计量,需要的是足够多的独立样本,不是时间上的连续性。用 1000 个独立 drop 换来的统计置信度,远高于用 1 个 drop 演化 1000 步。
这里有个隐含假设值得说清楚:独立 drop 意味着不同快照之间没有相关性,用户位置、阴影全部重抽。这个假设对统计平均是合理的,但会抹掉一类现象——某个用户长期处于深衰落、被反复调度失败。评估公平性或者用户满意度时,就得改成“用户位置固定、只重抽阴影和小尺度”的半静态模型,让每个用户在自己的位置上累积多次调度结果。
还有个工程细节:撒点必须在圆内均匀,不能是正方形均匀。正方形撒点会让四个角落的用户密度偏高,而角落到基站的距离最远,等于人为加重了边缘负载,中断概率会被系统性高估。正确做法是用半径的平方根变换,下一章给代码。
3. 信道模型的 MATLAB 落地:从路径损耗公式到 N×M 增益矩阵
3.1 路径损耗与场景参数:一行公式背后的取值依据
先把参数固定下来,后面的所有讨论都基于这张表。数值本身不神圣,重要的是每个数为什么取这个量级——这决定了你改参数时知道自己在改什么。
| 参数 | 取值 | 说明 |
|---|---|---|
| 载波频率 f_c | 2 GHz | 决定自由空间基准路损与多普勒 |
| 系统带宽 BW | 10 MHz | 噪声底按带宽换算 |
| 噪声系数 NF | 9 dB | 典型基站/终端接收机量级 |
| 小区半径 | 500 m | 决定撒点范围与最远路损 |
| 蜂窝用户功率 | 23 dBm | 上行发射功率量级 |
| D2D 发射功率 | 23 dBm | 与蜂窝用户对齐,便于比较 |
| 阴影标准差(蜂窝) | 8 dB | 宏站覆盖的慢衰落实测范围 |
| 阴影标准差(D2D) | 4 dB | 直连链路高度低、遮挡少 |
噪声底一行就能算出整个仿真的基准:-174 dBm/Hz 是热噪声功率谱密度,乘 10 MHz 得 -104 dBm,加 9 dB 噪声系数得 -95 dBm。后面判断“干扰是否可以忽略”全靠这个数。
function pl_db = pathloss(d_m, scenario) %PATHLOSS 分场景路损,输入米制距离,输出 dB % scenario = 'cellular':基站与用户之间 % scenario = 'd2d' :两个终端之间的直连链路 switch scenario case 'cellular' d_km = max(d_m, 10) / 1000; % 10 m 截断,防止 log 发散 pl_db = 128.1 + 37.6 * log10(d_km); % 2 GHz 宏蜂窝典型拟合式 case 'd2d' d_m = max(d_m, 1); % 直连链路最小 1 m pl_db = 38.46 + 20 * log10(d_m); % 视距为主,衰减指数接近 2 end end这段代码有两个刻意为之的地方。第一是距离截断:路损公式在 d 趋近 0 时会发散,而仿真里总会出现两个用户距离不到 1 米的情况,不截断就会得到负无穷大的路损,进而算出天文数字的增益,把整个 SINR 分布带偏。第二是 D2D 用视距模型、蜂窝用非视距模型:直连链路高度通常 1.5 m 左右,30 m 内大概率有直射径,而基站到用户 200 m 的距离上几乎必然被建筑遮挡。如果两者都用同一个指数,D2D 的增益会被严重低估,资源分配算法就会做出“D2D 不值得复用”的错误判断。
实际工程里更精细的做法是按概率混合视距与非视距两支,视距概率随距离指数下降。这会引入额外的随机性,但在密集城区场景下对结果影响不小,属于“值得加”的那一类复杂度。
3.2 瑞利衰落的生成:复高斯、Jakes 与那些不该省的自由度
小尺度衰落最省事的生成方式是一行复高斯:
% 每一条链路一个独立复高斯系数,E|h|^2 = 1 h = (randn(size(D)) + 1j * randn(size(D))) / sqrt(2);除以 sqrt(2) 这一步是必须的。实部和虚部各是方差 1/2 的高斯,模平方的期望才是 1;漏掉这个归一化,信道平均功率会高出 3 dB,所有 SINR 整体偏移 3 dB,而且因为是非线性的,偏移量在不同信噪比区间还不一样,你会以为是算法本身的问题。
复高斯生成的是“快照独立”的瑞利信道,它丢掉了时间相关性。这个丢法在系统级仿真里完全可以接受,因为前面说过,决策周期远大于相干时间,相关性不影响统计平均。但如果你的项目要评估信道预测、移动性管理、或者任何依赖时间序列的模块,就必须上 Jakes 模型或者滤波法生成具有指定多普勒谱的信道。
Jakes 的经典实现是一组正弦叠加,工程上更常用的是“滤波白噪声”法:生成白高斯序列,通过一个频域响应为多普勒谱平方根的形状滤波器,再逆变换回时域。参数只有两个:多普勒频移 f_d = v·f_c/c,以及采样率。唯一要小心的是滤波器阶数不足会让输出序列的包络统计偏离瑞利分布,检验方法是画包络的累积分布,和理论瑞利曲线对比,偏差超过 1 dB 就说明滤波器不够长。
再往上一层是莱斯信道,直连链路有强直射径时用它更合适。参数是 K 因子,即直射功率与散射功率之比,K→0 退化为瑞利,K→∞ 退化为纯视距。D2D 近距离场景 K 取 4 到 10 dB 都算合理,这个参数对中断概率的低端影响很大,值得做一次敏感性扫描。
3.3 矩阵化:把逐对循环压成一次 N×M 矩阵运算
新手写仿真最容易写成双层 for 循环,每对链路单独算距离、单独算路损。40 个蜂窝用户加 40 个 D2D 对,一次快照就是 1600 次链路计算,1000 个 drop 就是 160 万次,MATLAB 的解释器开销会让整个仿真跑上几十分钟。实际上这些计算可以全部压成矩阵运算。
function G = gain_matrix(rx, tx, sigma_db, pl_fun) %GAIN_MATRIX 一次性算出所有接收-发射对的线性功率增益 % rx、tx:K×2 的坐标矩阵 % G(i,j):第 j 个发射端到第 i 个接收端的增益(线性,非 dB) D = sqrt((rx(:,1) - tx(:,1)').^2 + (rx(:,2) - tx(:,2)').^2); % K×K 距离矩阵 shadow = sigma_db * randn(size(D)); % 每条链路独立阴影 pl = pl_fun(D); % 逐元素路损,单位 dB G = 10.^(-(pl + shadow) / 10); % 统一在线性域表达 end核心是第三行那个(rx(:,1) - tx(:,1)'):rx 的列向量是 K×1,tx 的转置是 1×K,相减自动扩展成 K×K。这是 MATLAB 自 R2016b 起支持的隐式扩展,等价于旧版本的bsxfun。如果目标环境版本较老,改成bsxfun(@minus, rx(:,1), tx(:,1)')即可,性能几乎一致。
pl_fun传入一个函数句柄,里面用向量化的条件判断处理视距/非视距分支,而不是 if-else 逐元素判断。这样整个链路的增益矩阵一次算完,1000 个 drop 也就几秒钟。
内存也要留意:K 取 200 时,一个 K×K 的 double 矩阵是 320 KB,1000 个 drop 不要同时保存,每个 drop 算完立刻统计所需的标量指标然后丢弃。我见过把每个 drop 的完整增益矩阵存进 cell 数组的写法,跑到第 300 个 drop 就内存溢出,而实际需要的只是每个 drop 的一个平均吞吐。
4. 资源分配的三层决策:模式选择、信道分配与功率控制怎么排
4.1 干扰图的构建:阈值取多少才不是拍脑袋
资源分配的第一步是把连续的信道关系离散成一张干扰图:谁和谁不能共用同一个资源块。图的节点是链路(蜂窝用户和 D2D 对各占一个节点),边表示两条链路之间的干扰超过某个门限。
门限怎么取?最自然的依据就是噪声底。如果一条干扰链路的接收功率比噪声底低 10 dB 以上,它对 SINR 的影响不到 0.4 dB,可以安全忽略。本场景噪声底是 -95 dBm,那么干扰门限取 -105 dBm 就是有依据的,对应的路损是 23 + 105 = 128 dB。反过来,如果门限取 -85 dBm,图会变得极稠密,几乎每条链路都和其他所有链路冲突,着色数接近节点总数,等于宣布“资源不够用”——这不是算法结论,是门限选错了。
% 用大尺度增益建图,而不是瞬时增益 G_long = 10.^(-(pl + shadow) / 10); % 不含小尺度 adj = double(G_long > 10^(I_th_db / 10)); % I_th_db = -105 dBm 折算的线性门限 adj = double(adj | adj'); % 对称化,干扰是双向的 adj(1:size(adj,1)+1:end) = 0; % 去掉对角项必须用大尺度增益建图,这一点常被写错。用瞬时增益建图,每个 drop 的图都不同,而且图上会随机出现一些“瞬时不干扰”的边被删掉,导致分配结果在小尺度上过度乐观;更糟的是真机无法复现,因为瞬时 CSI 拿不到。用大尺度加阴影建图,相当于用统计意义上的强干扰关系做规划,小尺度留给链路自适应去吸收,这才是实际系统的工作方式。
同一个 drop 里,图一旦建好就不该再变。如果算法在迭代中反复重算邻接关系,复杂度会从 O(K²) 涨到 O(K²·迭代次数),而且结果依赖迭代顺序,可复现性也没了。
4.2 信道分配:贪心、图着色与二分匹配的适用边界
图上色是最直观的信道分配:每种颜色代表一个资源块,相邻节点不能同色,色数就是需要的资源块数。贪心着色按度数降序处理节点,每次挑一个邻居没用过的最小颜色。
function [ncol, col] = greedy_color(adj) %GREEDY_COLOR 按度数降序的贪心图着色 n = size(adj, 1); col = zeros(n, 1); [~, order] = sort(sum(adj, 2), 'descend'); % 度数大的先分配,减少后续冲突 for k = order(:)' used = col(adj(k, :) == 1); % 取出所有邻居的颜色 used = used(used > 0); c = 1; while any(used == c) % 找最小的未占用颜色 c = c + 1; end col(k) = c; end ncol = max(col); end复杂度是 O(K·C),C 为色数,K 为节点数,对几千个节点也是毫秒级。贪心着色的理论近似比是 O(log K),实际在干扰图上往往比最优解多用 10% 到 20% 的资源块,对仿真来说完全够用——真要最优解得靠整数规划,那个开销在蒙特卡洛里根本撑不住。
二分匹配(匈牙利算法)是另一条路:把 D2D 对和蜂窝用户的资源块当成两个集合,边权是复用后的总吞吐增益,求最大权匹配。MATLAB 里可以用matchpairs(较新版本)或者自己写 Munkres。但要想清楚它的适用边界——匹配是严格一对一的,一个 D2D 对只能配一个资源块,一个资源块也只能给一个 D2D 对;而真实的复用场景允许多个 D2D 对共享同一资源块,只要它们彼此干扰够小。所以匹配算法给出的是保守解,通常作为性能下界或者对比基线,而不是最终方案。
实践中更常见的做法是分层:先用图着色把干扰关系解耦,把节点分成若干互不冲突的组,组内再按某种准则(最大化最小 SINR、最大化总吞吐)分配资源块。这样既保留了图着色的低复杂度,又比纯着色多了自由度。
4.3 功率控制:为什么固定功率常常是更诚实的基线
功率控制最容易写,也最容易写出假结果。开环分数功率控制的公式很短:
alpha = 0.8; % 路损补偿系数,1 为全补偿,0 为不补偿 P0 = -80; % 目标接收功率,dBm P_tx = min(P_max_dbm, P0 + alpha * pl_db); % 逐链路计算α 取 1 时,发射功率随路损等量增加,接收端功率恒定,远近用户公平但总功耗高;α 取 0 时所有链路同一功率,靠近基站的 D2D 对会给基站造成巨大干扰。α 在 0.6 到 0.8 之间是常见折中,它让远端用户多分一点功率,又不至于让近端用户把基站打爆。
需要警惕的是“水注功率分配”这类看起来很高级的方案。注水定理的前提是已知全局 CSI、且各子信道独立,在 D2D 复用场景里这两个前提都不成立:全局 CSI 需要大量反馈,而复用带来的干扰耦合让子信道根本不独立。强行套用会得到一个数学上最优、物理上不可实现的解,仿真曲线好看但没有任何参考价值。
所以固定功率是必须保留的基线。它的价值不在于性能好,而在于把所有变量隔离:当你想验证“信道模型改对了吗”“干扰图建对了吗”,固定功率下结果应该和解析估算吻合。基线不对,上面叠的任何优化都是空中楼阁。我一般会要求任何功率控制方案至少在两件事上胜过固定功率——小区边缘用户的 5% 分位吞吐、以及总功耗——只赢一项就说明它是在做功率搬运而不是真的变好。
5. 排查现场:五类“能跑通但结论反了”的仿真事故
5.1 单位与数值类事故
现象:所有 D2D 用户的 SINR 都在 40 dB 以上,频谱效率曲线贴到香农界顶部,看起来算法完美。
原因:dB 与线性域混用。典型写法是sinr = P_d - interference - noise(三个 dBm 相减),然后在速率公式里直接log2(1 + sinr)。dBm 减 dBm 得到的是 dB 比值,比如 23 dB 可能代表 200 倍功率比,代入 log2(1+23) 等于把 200 倍当成 23 倍,结果偏低;但更常见的错法是在 dB 域里做加减干扰功率,那会得到完全无意义的值,最终可能碰巧给出偏高的 SINR。
解决:定一条铁律——所有功率、增益、干扰的累加和相除全部在线性域(瓦特)完成,只在打印和画图时转 dB。转换函数集中写两个db2lin/lin2db,不许在业务代码里手写10.^(x/10)。
现象:中断概率随用户数增加反而下降。
原因:统计口径变了。用户数增加时,分母用的是“总用户数”,分子用的是“成功用户数”,但成功判定阈值是固定的 SINR 门限,用户多了之后新增的都是近距离用户,拉高了成功率。这不是物理规律,是样本组成变了。
解决:中断概率必须固定单链路统计口径,分母是该类链路的全部样本数,不随网络规模变化;跨规模比较时同时报告每类的样本数。
5.2 模型与拓扑类事故
现象:小区中心用户速率异常高,边缘用户速率长期为 0,5% 分位吞吐恒等于 0。
原因:撒点不均匀。用x = R*rand、y = R*rand在正方形里撒点,等于把用户堆在四个角,而角落到基站的距离是半径的 1.4 倍,路损多出近 5 dB。边缘负载被人为放大,中断概率被系统性高估。
解决:圆内均匀撒点必须用半径平方根变换:r = R*sqrt(rand(n,1)); theta = 2*pi*rand(n,1);。验证方法是把撒点结果画出来,或者干脆统计半径的分布,应该和 r² 成正比。
现象:D2D 用户的接收信号强度比理论值低 10 dB 以上,但路径损耗公式逐行核对没错。
原因:距离算错了坐标系。常见的是发射端和接收端用了不同的单位(一个用米一个用千米),或者 D2D 对生成时把接收端放在了距离发射端d_max处而不是[d_min, d_max]区间内,导致所有配对距离都取最大值。还有一种更隐蔽的,基站坐标写在原点但撒点用的是[0, R]区间而非[-R, R],所有用户被迫挤在第一象限,平均距离变小一半。
解决:每次改完拓扑生成,先打印三个统计量——平均配对距离、平均用户到基站距离、最远用户距离——和理论值对照。理论平均距离约为 2R/3,对不上就是坐标错了。
5.3 统计口径与收敛类事故
现象:蒙特卡洛曲线抖动很大,把 drop 数从 200 加到 500,抖动几乎没改善。
原因:样本不独立。随机种子在循环外只设了一次,但每次 drop 里用的是同一个种子重新rng,或者用了固定种子加固定偏移,生成的其实是同一套拓扑的微扰。另一种情况是并行了多个 worker,每个 worker 的随机流没有独立设置,结果高度相关。
解决:种子只在外层设一次,之后让随机流自然推进;用并行时给每个 worker 显式设定不同的子流(rng(seed + worker_id)),或者干脆在串行域预先生成所有随机数再分发。收敛判据不要看“跑了多少次”,要看指标的 95% 置信区间宽度是否小于均值的 1%。
现象:同一份代码,两个人跑出来的平均吞吐差了 20%。
原因:MATLAB 版本或者浮点运算顺序不同导致着色结果不同。图着色是贪心算法,节点处理顺序稍有差别,色数就可能差一两个,进而在资源受限的场景里放大成吞吐差异。
解决:在排序时加一个确定性的二次排序键(度数相同的按节点编号排),保证任何环境下处理顺序唯一。这类确定性问题不解决,后面所有“算法改进”的比较都不可信——你分不清 20% 的提升是算法带来的还是排序带来的。
6. 精度与复杂度的取舍:什么该建模,什么该抽象掉
仿真做到最后,拼的不是谁的模型更精细,而是谁清楚每一项复杂度换来了什么。我在第 2 章算过的那组量级数字,其实已经把答案写在里面了:D2D 的瓶颈在基站侧干扰,那么 D2D 链路上小尺度衰落建模得再精细,对整体结论的影响也有限;反过来,D2D 发射端到基站这条链路的阴影相关性如果按独立处理,基站侧的干扰分布就会被低估或高估,结论直接翻转。判断标准只有一条——这一项是否落在系统的瓶颈路径上。
三种资源分配策略的取舍可以列成一张表,选哪种取决于你要回答什么问题:
| 策略 | 复杂度 | 典型性能 | 适用场景 |
|---|---|---|---|
| 随机分配 | O(1) | 下界 | 基线对照,验证其他模块是否正确 |
| 贪心图着色 | O(K·C) | 接近最优 10% 到 20% 以内 | 蒙特卡洛大样本、系统级吞吐评估 |
| 二分匹配 | O(K³) | 一对一约束下的最优 | 性能上界、算法对比 |
| 整数规划 | 指数级 | 最优 | 小规模场景,只跑几十个 drop |
验证仿真器最有效的手段不是加更多功能,而是找一个能解析计算的特例去套。最简单的特例是关掉 D2D,只留单小区上行:此时每个用户的 SINR 分布可以和高斯近似、或者对数正态近似的结果对照,误差在 1 dB 以内就说明路损、阴影、噪声底这一整条链路是对的。第二个特例是把所有 D2D 对放到离基站足够远的位置,让基站侧干扰可忽略,此时 D2D 的 SINR 应该独立于蜂窝用户数量,如果随着用户数增加而变化,说明干扰矩阵里混入了本不该有的项。
这套“先对特例、再放全场景”的流程,是我早年吃过亏之后养成的习惯:那时候一上来就跑全场景,曲线出来不对,只能从头逐个模块排查,两天时间全耗在二分定位上。后来改成每加一个模块就先过一遍特例校准,虽然前期慢半天,但后面再也没出现过“整条曲线重跑一遍才知道哪里错了”的局面。从那以后我每次动信道模型或者干扰矩阵,都强制走一遍无 D2D 的解析对照,哪怕只改了一行距离截断。
这套仿真骨架的价值也在这里:它把“信道建模、干扰图构建、资源分配、统计口径”拆成四段可独立校准的部件,每一段都能用特例验证,合起来才敢对资源分配的优化幅度下结论。如果你手上正好缺一份能把这几段串起来、参数和公式都能对得上的 MATLAB 实现,这份工程值得拿下来跑一遍——先跑通它的基线,再改你想改的那一段,比从零搭一个黑匣子省太多事。希望帮到你。
本文还有配套的精品资源,点击获取