简介:面向5G通信系统设计与优化人员及通信专业学生,一套研究通信距离对信道容量影响的仿真源码提供了可直接运行的m文件实现。压缩包共8个m文件,大小仅9KB,覆盖多输入多输出多路复用、混合预编码、天线导向矢量、非视距路径损耗、氧气吸收等关键计算模块,便于拆解和复用。目前已有144人学习下载。仿真以香农容量公式为基础,结合自由空间衰减、多径衰落与毫米波传播损耗模型,量化了距离增加对信噪比及信道容量的影响,同时对比不同天线配置和功率控制策略下的容量变化。读者可以借助源码观察容量随距离衰减的曲线,理解5G网络覆盖与速率的权衡,并扩展自定义场景,为实际部署和参数优化提供参考。
1. 5G通信系统里信道容量随距离衰减:这套仿真源码拆出了什么
做5G链路预算或毫米波覆盖仿真的人,大概率卡过同一个问题:用户离基站10米和300米,容量到底掉多少?手算香农公式容易,但路径损耗、氧气吸收、MIMO预编码增益、多径信道全压在一起,手算不现实。这套仿真源码就是把“通信距离增加→SNR下降→信道容量变化”这条链路拆成8个可执行的m文件。
源码里既有MIMO_Multiplexing、MIMO_HybridPrecoding这样的预编码实现,也有getPathLossNLOS、Oxygen_Absorption损耗模型,还有CDL-A信道下的平均容量统计。跑出来的结果就是一张MIMO信道容量图像:横轴是距离,纵轴是容量。照着这篇笔记的操作,你能复现这张图,并解释曲线形态。
适合三类人:做5G网络规划与物理层算法的工程师,拿通信仿真做课程设计的研究生,以及想快速拿到一条可信距离-容量曲线的从业者。这条线没走通之前,后面所有优化讨论都是空中楼阁。
2. 从香农公式到距离-容量曲线:8个m文件在链路里分别干什么
2.1 距离是怎么一口一口吃掉容量的
先回到最基础的香农公式:C = B·log2(1 + SNR)。容量对SNR是对数关系,这意味着高SNR区间里再增加SNR,容量收益很小;而低SNR区间里SNR一旦下降,容量会快速崩塌。所以距离-容量曲线天然不是一条直线,而是“先平缓、后陡峭”的形态,仿真要复现的就是这个拐点。
距离的影响主要通过接收功率进入SNR。自由空间损耗是20log10(d),到了5G毫米波的非视距场景,TR 38.901 UMi模型的路径损耗斜率变成36.7log10(d)左右,也就是说同样翻一倍距离,损耗多掉的dB数远大于低频段。再加上5G NR里大规模MIMO带来的是阵列增益和空间复用增益,远距离时信道矩阵秩下降、条件数变差,本来能并传4个流,最后只能传1到2个流,这比单纯SNR下降还致命。
源码把损耗拆成多个独立文件是有道理的。路径损耗是一个距离相关的主项,氧气吸收是另一个频率相关的附加项,两者随距离的衰减机制不同。如果把全部损耗揉进一个函数里,你很难判断曲线拐点到底是路径损耗主导,还是MIMO复用能力退化主导。拆开之后,每条曲线、每个中间量都能单独画出来验证,这才是仿真的价值。
2.2 文件职责与调用关系
先看这套源码里8个m文件各自的位置,我拆包之后按职责整理成下面这张表。
| 文件名 | 职责 | 在链路中的位置 |
|---|---|---|
| SphericalUnitVector.m | 把到达角、离开角转成三维单位方向矢量 | 所有阵列运算的最前端 |
| getSteeringVector.m | 根据方向矢量生成阵列导向矢量 | 波束指向和预编码权重的基础 |
| Coupling.m | 计算天线阵元间互耦并修正幅相 | 大阵列、小间距时修正导向矢量 |
| getPathLossNLOS.m | 计算非视距路径损耗 | 距离进入SNR的主项 |
| Oxygen_Absorption.m | 计算毫米波频段氧气分子吸收损耗 | 距离进入SNR的附加项 |
| MIMO_Multiplexing.m | 空间复用容量计算 | 理论复用上限 |
| MIMO_HybridPrecoding.m | 混合预编码容量计算 | 接近实际基站架构的实现 |
| AverageCapacityCDL_A.m | CDL-A信道模型下统计平均容量 | 最终输出距离-容量统计值 |
调用顺序上,常见做法是先由SphericalUnitVector生成方向矢量,交给getSteeringVector得到BS和UE两侧的导向矢量,再用Coupling对阵列互耦做修正。与此同时,getPathLossNLOS和Oxygen_Absorption根据当前距离算出总损耗,折算成等效SNR。之后分两条线走:一条进MIMO_Multiplexing算空间复用上限,一条进MIMO_HybridPrecoding算混合预编码下的实际容量,最后都汇总到AverageCapacityCDL_A里做多次快照平均。
2.3 先按骨架把链路跑通
不要急着改参数,先把这条链路用最小骨架跑通。我一般会新建一个空脚本,按下面的顺序调一遍,确认每个文件能正确输入输出:
% 最小链路骨架:验证8个m文件能串起来 fc = 28e9; % 载波频率 28GHz,毫米波典型频段 d = 100; % 通信距离 100米 snrBase = 20; % 参考SNR 20dB % 1) 球面单位矢量:确定信号到达方向 [az, el] = deal(deg2rad(30), deg2rad(10)); % 方位角30度,俯仰角10度 u = SphericalUnitVector(az, el); % 以源码内签名为准 % 2) 生成BS侧导向矢量,8行8列UPA阵列 aBS = getSteeringVector(8, 8, u, fc); % 常见做法:尺寸+方向+频率 % 3) 互耦修正,0.5倍波长间距时建议开启 aBS_corr = Coupling(aBS, 8, 8, 0.5); % 0.5为阵元间距比值 % 4) 距离损耗折算成等效SNR pl = getPathLossNLOS(fc, d, false); % false表示NLOS o2 = Oxygen_Absorption(fc, d); % 氧气吸收dB值 snrEff = snrBase - (pl + o2); % 等效SNR,单位dB % 5) 打印关键量,确认链路通 fprintf('路径损耗 %.2f dB,氧气吸收 %.4f dB,等效SNR %.2f dB\n', ... pl, o2, snrEff);这骨架的意义是把链路里最容易被忽略的“方向矢量→导向矢量→损耗→SNR”的顺序固定下来。参数说明:fc用28GHz是NR n257/n258频段,d取100米是室外微蜂窝典型覆盖点,snrBase给20dB是为后续距离扫描留出足够的衰退空间。跑通后你应该看到路径损耗几十dB、氧气吸收非常小、等效SNR明显下降,这代表距离已经真正进入了容量计算的链路。
3. 跑通第一张图:MIMO_Multiplexing与混合预编码的单点容量
3.1 参数配置头:载波频率、天线阵列、带宽怎么定
仿真最怕参数随手填。距离-容量仿真里,载波频率、阵列规模、带宽这三个参数直接决定曲线形态是否合理。我拆这套源码时的习惯是先把配置头单独列出来,放在脚本最前面,后面所有函数都引用这两个变量,避免改一处漏一处:
% 统一参数配置头 fc = 28e9; % 载波频率 28GHz,对应5G NR毫米波频段 bw = 100e6; % 信道带宽 100MHz,NR标准参数集 nrBS = [8, 8]; % BS侧天线阵列:8行8列,共64阵元 nrUE = [2, 2]; % UE侧天线阵列:2行2列,共4阵元 dList = 10:10:500; % 距离扫描范围:10米到500米 nReal = 200; % 信道实现次数,平均容量用 snrRef = 20; % 参考SNR,单位dB参数说明:BS侧选64阵元是5G大规模MIMO的典型配置,低于这个规模空间复用优势不明显,高于这个规模仿真耗时明显增加;UE侧4天线对应手机终端常见配置。距离从10米起步是因为毫米波覆盖半径通常就在几百米量级,500米之后NLOS场景下SNR往往已经低到容量接近零。nReal取200是保证平均容量稳定性与耗时的折中,想要曲线平滑可以加到500。
3.2 导向矢量与球面单位矢量:波束方向是怎么进信道的
MIMO容量计算不是简单给信道矩阵加个SNR就完事。BS侧和UE侧的导向矢量决定了波束指向,而波束指向又决定了信道矩阵里每个元素的相位关系。这套源码里getSteeringVector.m和SphericalUnitVector.m承担的就是这个职责。
% 生成BS侧和UE侧的导向矢量 azBs = 30 * pi/180; elBs = 10 * pi/180; % BS侧角度 azUe = -20 * pi/180; elUe = 5 * pi/180; % UE侧角度 uBs = SphericalUnitVector(azBs, elBs); uUe = SphericalUnitVector(azUe, elUe); aBs = getSteeringVector(nrBS(1), nrBS(2), uBs, fc); aUe = getSteeringVector(nrUE(1), nrUE(2), uUe, fc); % 互耦修正:阵元间距0.5倍波长时耦合最强 aBs = Coupling(aBs, nrBS(1), nrBS(2), 0.5);导向矢量的物理含义是:在某个来波方向上,各个阵元因为空间位置不同,接收到信号的相位差不同,这些相位差构成的向量就是导向矢量。getSteeringVector本质上在算每个阵元相对参考点的时延差。互耦修正则是告诉你不修正的结果:大阵列小间距时,相邻阵元会互相耦合,实际幅相比理论值偏出去好几个dB,在高SNR区间不明显,在远距离低SNR区间会被放大。
3.3 距离循环与等效SNR计算
单点容量验证通过后,把距离写进主循环。这里我建议不要改动MIMO_Multiplexing.m内部,而是把距离损耗换算成等效SNR再传入,这样既保住了源码原始逻辑,又让距离变量只在一个地方控制:
% 距离循环:计算不同距离下的MIMO空间复用容量 capMimo = zeros(size(dList)); for k = 1:length(dList) d = dList(k); pl = getPathLossNLOS(fc, d, false); % NLOS路径损耗,单位dB o2 = Oxygen_Absorption(fc, d); % 氧气吸收,单位dB snrEffLin = 10^((snrRef - pl - o2) / 10); % 等效线性SNR capMimo(k) = MIMO_Multiplexing(nrBS, nrUE, bw, snrEffLin, nReal); end % 绘图:距离-容量曲线 figure; plot(dList, capMimo / 1e6, 'b-o', 'LineWidth', 1.5); xlabel('通信距离 (m)'); ylabel('信道容量 (Mbps)'); title('MIMO空间复用容量随距离变化'); grid on;这段代码的核心逻辑是“损耗进SNR,SNR进容量”。每个距离点上先算路径损耗和氧气吸收,叠加后从参考SNR里扣掉,得到等效线性SNR,再交给MIMO_Multiplexing做容量计算。MIMO_Multiplexing内部常见做法是生成一组独立同分布的瑞利信道,对信道矩阵做SVD后按奇异值分配功率,最后统计平均容量。你不需要读懂它每一行,但要知道输出单位是bps,所以绘图时除以1e6转成Mbps。
3.4 混合预编码跑出来和复用上限差多少
MIMO_Multiplexing给的是理论复用上限,实际基站不可能做到每根天线一个独立射频链,成本控制不住。真正落地的是MIMO_HybridPrecoding.m,它把预编码拆成模拟域和数字域两级:模拟部分用移相器只调相位不调幅度,数字部分在基带维度做精细处理。代价是模拟预编码码本有限,波束指向精度受限。
% 对比运行:同一距离下复用上限 vs 混合预编码 capHybrid = zeros(size(dList)); for k = 1:length(dList) d = dList(k); pl = getPathLossNLOS(fc, d, false); o2 = Oxygen_Absorption(fc, d); snrEffLin = 10^((snrRef - pl - o2) / 10); capHybrid(k) = MIMO_HybridPrecoding(nrBS, nrUE, bw, snrEffLin, nReal); end hold on; plot(dList, capHybrid / 1e6, 'r-s', 'LineWidth', 1.5); legend('空间复用上限', '混合预编码');两条曲线放一起看是有信息量的:近距离处两者差距可能在20%到30%,因为模拟预编码的量化相位误差吃掉了一部分阵列增益;远距离处两者差距会缩小,因为此时容量受限于SNR而非预编码精度,波束方向准不准已经不是瓶颈。如果你发现远距离处混合预编码反而超过复用上限,基本可以断定代码里某个环节把天线增益算了两次,这个我在第5章会展开。
4. 把距离写进信道:路径损耗、氧气吸收与CDL-A平均容量
4.1 NLOS路径损耗模型:从自由空间到TR 38.901
第2章提到自由空间损耗只有20log10(d),但5G毫米波实际部署几乎都是非视距,所以源码里专门有getPathLossNLOS.m。这套模型按TR 38.901 UMi场景的常见写法是:
PL = 26·log10(fc_GHz) + 36.7·log10(d_m) + 22.7
其中fc以GHz为单位,d以米为单位。和自由空间公式对比,距离项系数从20涨到36.7,说明同样的距离翻倍,NLOS路径损耗多衰减约11dB。这就是毫米波覆盖半径天然短的根本原因,不是频率高穿透差这么一句能概括的,而是数学上损耗斜率本身变陡了。
% 手动计算并对比两种模型 d = 50:50:500; % 距离向量 fcGHz = 28; % 单位GHz plFs = 20*log10(d) + 20*log10(fcGHz) + 32.4; % 自由空间参考 plNlos = 26*log10(fcGHz) + 36.7*log10(d) + 22.7; % TR38.901 UMi NLOS figure; plot(d, plFs, '--', d, plNlos, '-'); xlabel('距离 (m)'); ylabel('路径损耗 (dB)'); legend('自由空间', 'UMi NLOS');参数说明:fcGHz必须先转成GHz单位再进log10,这是最容易错的地方。你会发现500米处两种模型差了将近40dB,这40dB落在SNR上就是容量从几百Mbps直接掉到个位数Mbps。如果你拿到的getPathLossNLOS.m里fc用的是Hz,那调用前就要做单位换算,否则曲线整体平移,看起来有点像样,实际上物理含义全错。
4.2 氧气吸收:哪些频段必须开,哪些频段可以关
氧气吸收是毫米波特有的损耗项,来源是氧分子对电磁波能量的共振吸收。需要强调的是,它的频率选择性极强。我在拆Oxygen_Absorption.m之后,对照TR 38.901的典型数值整理了一份参考表:
| 频段 | 氧气吸收典型量级 | 仿真建议 |
|---|---|---|
| 28GHz | 约0.01 dB/km量级 | 几百米内影响极小,可关 |
| 39GHz | 约0.02 dB/km量级 | 影响可忽略,可关 |
| 60GHz | 约15 dB/km量级 | 必须开启,强吸收峰 |
| 73GHz | 约0.5 dB/km量级 | 建议开启,量级可见 |
注意这张表的量级是每公里,如果你的距离扫描范围只有几百米,28GHz下氧气吸收累计起来不到千分之一dB,画在容量曲线上完全看不出变化。这不是代码bug,而是物理机制在这个频段本来就不显著。
% 对比28GHz和60GHz的氧气吸收 dTest = 0:50:1000; o2_28 = Oxygen_Absorption(28e9, dTest); o2_60 = Oxygen_Absorption(60e9, dTest); figure; plot(dTest, o2_28, '-', dTest, o2_60, '--'); xlabel('距离 (m)'); ylabel('氧气吸收 (dB)'); legend('28GHz', '60GHz');如果你跑的是60GHz的仿真,Oxygen_Absorption这个文件就是决定曲线形态的关键角色之一。近距离时氧气吸收小,远距离时它叠加在路径损耗上,让容量曲线出现更早的下跌拐点。如果你跑的是28GHz,直接把这个函数的结果打出来看一眼,确认它在合理范围,然后放心忽略它的影响。
4.3 CDL-A信道下的平均容量:SVD加等功率分配
路径损耗和氧气吸收解决的是“平均功率掉多少”,但信道容量还取决于信道矩阵的结构。5G标准测试场景里,CDL-A是城区微蜂窝非视距的代表性信道,包含35个散射簇,每个簇有独立的时延、角度和功率。AverageCapacityCDL_A.m的作用就是在这个信道模型下做多次快照统计:
% CDL-A信道配置与平均容量计算 cdl = nrCDLChannel; % 5G Toolbox标准对象 cdl.DelayProfile = 'CDL-A'; % 标准非视距场景 cdl.DelaySpread = 100e-9; % 100ns时延扩展 cdl.CarrierFrequency = fc; % 和前面配置头保持一致 cdl.SampleRate = bw; % 采样率不低于带宽 cdl.TransmitAntennaArray.Size = [nrBS(1) nrBS(2) 1 1 1]; cdl.ReceiveAntennaArray.Size = [nrUE(1) nrUE(2) 1 1 1]; cdl.Seed = 7; % 固定种子便于复现 avgCap = 0; for r = 1:nReal [H, ~] = cdl(); % 生成一次信道实现 H = H / norm(H, 'fro') * sqrt(prod(nrBS) * prod(nrUE)); % 归一化 [~, S, ~] = svd(H); % 奇异值分解 lambda = diag(S).^2; % 特征值即各流功率增益 c = sum(log2(1 + snrEffLin / length(lambda) * lambda)); avgCap = avgCap + c; end avgCap = avgCap / nReal * bw; % 平均容量,单位bps这段代码有两个关键点。一是归一化:CDL-A信道对象输出的信道矩阵幅度是随机的,如果不做归一化,每次运行容量都会波动很大,距离的影响会被随机性掩盖。二是等功率分配:这里没做注水,而是把SNR平均分到每个空间流上,这是5G系统级仿真的常见假设,因为实际调度器不知道瞬时信道特征值。如果你想让结果更乐观,可以改成注水算法,但那需要额外的功率分配迭代。
4.4 全链路脚本:距离扫描到容量曲线一步到位
把前面三节串起来,就是一个完整的距离-容量仿真主程序。我把参数配置、损耗计算、信道生成、容量统计全部放进同一个循环,这样每次改距离步长或者天线配置,只需要改配置头:
% 全链路距离-容量扫描脚本 fc = 28e9; bw = 100e6; nrBS = [8 8]; nrUE = [2 2]; dList = 10:10:500; nReal = 200; snrRef = 20; capCdl = zeros(size(dList)); for k = 1:length(dList) d = dList(k); pl = getPathLossNLOS(fc, d, false); o2 = Oxygen_Absorption(fc, d); snrEffLin = 10^((snrRef - pl - o2) / 10); cdl = nrCDLChannel; cdl.DelayProfile = 'CDL-A'; cdl.DelaySpread = 100e-9; cdl.CarrierFrequency = fc; cdl.SampleRate = bw; cdl.TransmitAntennaArray.Size = [8 8 1 1 1]; cdl.ReceiveAntennaArray.Size = [2 2 1 1 1]; cdl.Seed = k; % 每个距离点换种子 avg = 0; for r = 1:nReal [H, ~] = cdl(); H = H / norm(H, 'fro') * sqrt(64 * 4); [~, S, ~] = svd(H); lambda = diag(S).^2; avg = avg + sum(log2(1 + snrEffLin / length(lambda) * lambda)); end capCdl(k) = avg / nReal * bw; end plot(dList, capCdl / 1e6, 'g-^', 'LineWidth', 1.5); xlabel('通信距离 (m)'); ylabel('平均容量 (Mbps)'); title('CDL-A信道下5G系统距离-容量曲线'); grid on;跑完这张图,你就得到了一个真正带信道统计特性的距离-容量曲线。和前面MIMO_Multiplexing的曲线对比,CDL-A版本会更低更抖,因为多径衰落让信道矩阵的条件数变差了。这也是这套源码最有价值的地方:它让你同时看到理论理想边界和标准信道模型下的实际预期,两者之间的差距就是信道特征带来的损失。
5. 避坑记录:跑距离-容量仿真时必须绕开的五个问题
5.1 距离单位混用,曲线前半段直接失形
现象:跑出来的容量曲线在近距离段异常陡峭,10米到50米掉得离谱,后面反而平缓,跟理论上“先平缓后陡峭”的形态完全相反。
原因:getPathLossNLOS.m里的公式可能是按米写的,但调用时传入了千米,或者反过来。TR 38.901的公式里距离项是log10(d),单位错了不会报错,但会让整条路径损耗曲线平移,只是由于对数关系,平移量固定,导致近距离段相对变化被放大。我排查过一次,问题就出在dList写成10:10:500但代码里偷偷乘了0.001,把米变成千米。
解决:在配置头下面加一行断言,强制检查距离量级。
assert(all(dList > 1), 'dList单位应该是米,检查是否混入了km数值');我现在的习惯是第一次跑曲线之前,先单独打印一个距离点的路径损耗值,比如100米处应该落在100到130dB之间,如果看到才30dB,那一定是单位错了。
5.2 CDL-A采样率不够,容量被系统性低估
现象:CDL-A跑出来的平均容量比MIMO_Multiplexing低得离谱,低到全距离段都接近零,或者曲线形态正确但整体偏低20%以上。
原因:nrCDLChannel的SampleRate设置太低,比如默认1.92MHz或者只按1MHz仿真带宽配。CDL-A信道包含35个簇,每个簇有时延,采样率不够时簇的时延分辨率不足,信道抽头数量减少,频率选择性丢失,多径分集增益出不来。这属于典型配置错误,不是源码问题。
解决:把SampleRate设置成信道带宽的两倍以上,100MHz带宽至少要配200MHz采样率。如果跑的是带宽聚合场景,SampleRate要按总带宽覆盖的最大频率范围来定。
cdl.SampleRate = max(bw * 2, 100e6); % 至少两倍带宽判断标准很简单:把信道对象生成一次,看输出H的列数是否接近带宽对应的时间抽头数,如果少得离谱,采样率一定低了。
5.3 信道矩阵没归一化,距离变化被SNR掩盖
现象:每个距离点的容量都有几十Mbps的随机抖动,滤波都滤不干净;或者不同距离点的容量平均值几乎一样,距离的影响完全看不出来。
原因:从nrCDLChannel取出的H没有做功率归一化,导致每次信道实现的增益随机浮动。更隐蔽的情况是代码里已经做了归一化,但归一化因子写错,比如用了sqrt(nrBS)而不是sqrt(nrBS * nrUE),结果64发4收的场景里信道增益被系统性放大,SNR虚高,距离损耗全被掩盖。
解决:用Frobenius范数归一化,这是最常见也最稳的做法。
H = H / norm(H, 'fro') * sqrt(prod(nrBS) * prod(nrUE));这条做完,你才真正让距离损耗单独决定容量变化。如果归一化前后两条曲线差异巨大,说明问题就在这一行。
5.4 氧气吸收在28GHz跑不出效果,别急着怀疑代码
现象:开了Oxygen_Absorption和不开,容量曲线完全重合,很多人会以为函数写得不对。
原因:28GHz频段氧气吸收本来就只有每公里0.01dB量级,仿真距离只有几百米时,累积损耗不到0.001dB,对SNR的影响完全可以忽略。这是物理事实,不是代码缺陷。
解决:先判断你的频段是否需要这个损耗项。60GHz必须开,28GHz和39GHz可以跳过,73GHz建议开。我在拆包时会把Oxygen_Absorption单独拎出来画一条距离-损耗曲线,确认它在目标频段的量级合理,再用进主循环。
o2Test = Oxygen_Absorption(60e9, 0:50:1000); plot(0:50:1000, o2Test); % 60GHz下应能看到明显上升曲线如果在60GHz下这条曲线还是平的,那才需要回头检查函数实现,大概率是吸收系数查表范围写窄了。
5.5 混合预编码远距离出现容量断崖,不一定是bug
现象:MIMO_HybridPrecoding在近距离和MIMO_Multiplexing接近,到某个距离点突然掉到几乎为零,曲线不光滑。
原因:混合预编码的模拟部分用的是码本波束,波束增益是量化后的离散值。随着距离增加SNR下降,波束增益不足以补偿损耗,等效SNR直接跌破解调门限,容量进入“悬崖区”。这是混合预编码架构的真实特性,尤其在码本波束数少的时候更明显。
解决:先确认不是代码数值问题,可以把模拟预编码改成理想无量化波束赋形跑一次对比。如果理想波束没有断崖,说明断崖来源于码本量化,这是物理限制,不是bug。
% 对比理想波束与码本波束的等效SNR snrIdeal = snrRef - pl - o2 + 20 * log10(sqrt(64)); % 理想阵列增益 snrCodebook = snrRef - pl - o2 + 10 * log10(max(real(aBs))); % 码本增益那之后我看到混合预编码曲线上的断崖,第一反应不是调代码,而是先看码本里波束的数量和间距。波束越少,断崖越早,这是天线架构决定的,和算法实现无关。
6. 进阶:把单点容量改造成参数扫描,用条件数判断复用能力的边界
6.1 三种配置的距离-容量叠加对比
单条曲线跑通之后,最有价值的一步是把理论复用上限、混合预编码、CDL-A统计容量三条曲线叠在一张图里。这三条曲线的间距就是你可以向别人解释的系统损失:第一条到第二条的差距是预编码架构损耗,第二条到第三条的差距是标准信道模型下多径和簇结构带来的额外损失。
figure; plot(dList, capMimo / 1e6, 'b-o', 'LineWidth', 1.5); hold on; plot(dList, capHybrid / 1e6, 'r-s', 'LineWidth', 1.5); plot(dList, capCdl / 1e6, 'g-^', 'LineWidth', 1.5); xlabel('通信距离 (m)'); ylabel('平均容量 (Mbps)'); legend('空间复用上限', '混合预编码', 'CDL-A统计'); grid on;观察重点放在两条曲线的交叉点。如果混合预编码在远距离接近甚至超过CDL-A统计值,说明CDL-A信道下的秩损失比预编码量化损失更严重;如果反过来,说明当前天线配置下信道秩还够,是预编码精度先触底。这个交叉点就是你做网络规划时决定“这个距离要不要换更小码本或更多射频链”的参考位置。
6.2 用条件数判断空间复用能力
容量是宏观结果,条件数是微观解释。同一段距离下,信道矩阵的条件数越接近1,说明各空间流之间越独立,复用能力越强;条件数大于10的6次方量级,基本可以判定复用已经失效,只能走单流波束赋形。在AverageCapacityCDL_A的循环里顺手把条件数存下来,比单独跑完再看曲线更省时间。
% 在CDL-A循环内部记录条件数 condHAll = zeros(1, nReal); for r = 1:nReal [H, ~] = cdl(); H = H / norm(H, 'fro') * sqrt(prod(nrBS) * prod(nrUE)); condHAll(r) = cond(H); end fprintf('距离 %d 米,平均条件数 %.1f\n', d, mean(condHAll));条件数超过阈值时的结论是:这段距离不适合做高阶空间复用,应该转向单流波束赋形或者降低调制阶数。那以后我每次跑这类仿真,都会把单位、采样率、信道归一化三个检查项写在脚本最前面,先跑单点验证再扫描距离,看到曲线异常先去查这三个地方,而不是直接改仿真模型。这套源码的价值不在某个函数有多高级,而是让你把从距离到容量这条链路上的每个环节都拆开看清楚。希望帮到你。
本文还有配套的精品资源,点击获取