25年9月通信基础知识补充1:NTN-TDL信道建模matlab代码(satellite-communications toolbox学习)

发布于:2025-09-09 ⋅ 阅读:(18) ⋅ 点赞:(0)

看文献过程中不断发现有太多不懂的基础知识,故长期更新这类blog不断补充在这过程中学到的知识。由于这些内容与我的研究方向并不一定强相关,故记录不会很深入请见谅。

【通信基础知识补充10】25年9月通信基础知识补充1:NTN-TDL信道建模matlab代码(satellite-communications toolbox学习)

matlab satellite-communications toolbox 代码地址

一、设置NTN信道参数 (Set NTN Channel Common Parameters)

   设置用于建模非地面网络(NTN)窄带平坦衰落信道和 NTN 时域延迟(TDL)信道所需的通用参数。此示例对一颗在 600 千米高度运行的低地球轨道(LEO)卫星进行建模,其工作频段为 S 波段。假设移动设备或用户设备(UE)的速度为 3 千米/小时。这些默认参数来自 3GPP TR 38.821 表 6.1.2-4。
在这里插入图片描述

%% 1. Set NTN Channel Common Parameters
% ------------ 1.1 设置常规参数 ------------
commonParams = struct;
commonParams.CarrierFrequency = 2e9;                % 载波频率,单位 Hz(2 GHz)
commonParams.ElevationAngle = 50;                   % 卫星仰角,单位度
commonParams.SatelliteAltitude = 600000;            % 卫星高度,单位米(600 km)
commonParams.MobileAltitude = 0;                    % 移动设备高度,单位米(地面水平)
commonParams.MobileSpeed = 3*1000/3600;             % 移动设备速度,单位 m/s(3 km/h 转换为 m/s)
commonParams.SampleRate = 7680000;                  % 采样率,单位 Hz(8 MHz)
commonParams.RandomStream = "mt19937ar with seed";  % 随机流设置,用于保证结果的可复现性
                                                    % 选择项:
                                                    % "mt19937ar with seed":Mersenne Twister 算法(设定种子保证可复现性)
                                                    % "Global stream":使用全局随机流(适用于不需要控制随机性时)

commonParams.Seed = 73;                             % 随机种子,用于初始化随机数生成器
commonParams.NumSinusoids = 48;                     % 用于多普勒扩展建模的正弦波数量

% ------------ 1.2 计算卫星多普勒频移 ------------
% 使用卫星和用户的相对运动来计算卫星的多普勒频移
satelliteDopplerShift = dopplerShiftCircularOrbit( ...
    commonParams.ElevationAngle, ...
    commonParams.SatelliteAltitude, ...
    commonParams.MobileAltitude, ...
    commonParams.CarrierFrequency);

% 计算由于移动设备的运动产生的最大多普勒频移
c = physconst('lightspeed');  % 光速
mobileMaxDoppler = commonParams.MobileSpeed * commonParams.CarrierFrequency / c;

卫星多普勒频移的计算函数如下:

function shift = dopplerShiftCircularOrbit(el,hs,hg,freq,time)
%dopplerShiftCircularOrbit Doppler shift due to satellite movement in circular orbit
%   Copyright 2023-2024 The MathWorks, Inc.
%#codegen
    % 参数验证块(确保输入参数合法)
    arguments
        % 仰角(必须为实数/有限值/非空)
        el (1,:) {mustBeA(el,{'double','single'}), mustBeReal, mustBeFinite, mustBeNonempty}
        % 卫星高度(必须为正实数)
        hs (1,1) {mustBeA(hs,{'double','single'}), mustBeReal, mustBeFinite, mustBeNonempty, mustBePositive}
        % 地面站高度(必须非负且小于卫星高度)
        hg (1,1) {mustBeA(hg,{'double','single'}), mustBeReal, mustBeFinite, mustBeNonempty, mustBeNonnegative, mustBeLessThan(hg,hs)}
        % 载波频率(必须为实数)
        freq (1,1) {mustBeA(freq,{'double','single'}), mustBeReal, mustBeFinite, mustBeNonempty}  
        % 时间序列(可选,默认0)
        time (1,:) {mustBeA(time,{'double','single'}), mustBeReal, mustBeFinite, mustBeNonempty} = 0
    end
    % 获取圆形轨道的多普勒统计信息
    % 内部调用 circularOrbitStats 计算轨道动力学参数
    info = satcom.internal.circularOrbitStats(el, hs, hg, time, freq);
    % 提取多普勒频移结果
    shift = info.DopplerShift;
end

二、NTN 窄带信道 (NTN Narrowband Channel)

   如 3GPP TR 38.811 第 6.7.1 节所定义,该代码支持 NTN 平坦衰落窄带信道所有可用的陆地移动卫星 (LMS) 场景。S波段定义的 LMS 场景包括:城市 (Urban)、郊区 (Suburban)、乡村林地 (RuralWooded) 、住宅区 (Residential)。Ka 波段定义的 LMS 场景包括:郊区 (Suburban)、乡村林地 (RuralWooded)。下面是各场景的典型特征:

场景 (Scenario) 环境描述与特点 对信道的影响
城市 (Urban) 密集的高层建筑、街道峡谷。 最恶劣的场景。直射路径(LOS)被严重遮挡甚至完全阻挡(NLOS为主),信号经历严重的阴影衰落丰富的多径反射,导致深衰落和信号波动剧烈。
郊区 (Suburban) 低层建筑、住宅区、更开阔的道路,有一些树木。 环境介于城市和乡村之间。存在LOS的概率比城市高,但也会经历一定程度的遮挡和多径效应。信道条件优于城市,但劣于乡村
乡村林地 (RuralWooded) 开阔地、农田,但存在成片的树林或小森林。 主要遮挡物是树木。树林会部分遮挡或散射信号,导致信号强度发生中等到显著的变化。LOS条件比郊区更常见,但可能因树叶密度而季节性地变化。
住宅区 (Residential) 独立的房屋、低密度住宅区,有花园和零星的树木。 与郊区类似,但建筑密度更低,环境更开阔。直射路径通常较好,多径分量相对较弱且主要来自房屋和地面的反射。信道条件通常较好。
S波段 vs Ka波段 S波段(2-4 GHz)频率较低,Ka波段(26.5-40 GHz)频率非常高。 Ka波段信号波长短更容易被小雨滴、树叶等小物体吸收和散射,因此雨衰和植被衰减比S波段严重得多。这意味着即使在相同名称的场景(如Suburban)下,Ka波段的信道条件也会比S波段更差

   总结:选择不同的LMS场景,实质上是在选择不同环境遮挡程度和载波频率的组合。Urban代表最严重的遮挡和衰落,Residential代表较好的条件。而相同名称的场景在Ka波段下会比在S波段下更具挑战性。在仿真中,这些差异会通过不同的统计模型参数来实现,从而影响最终的链路性能和系统设计选择(如所需的链路余量)。
   以下代码为 NTN 窄带信道配置了一个城市 (Urban) LMS 场景,步骤如下:
   1.设置特定于 NTN 平坦衰落窄带信道的信道参数。
   2.生成 NTN 平坦衰落窄带信道。
   3.可视化衰落或滤波后信号的频谱。

%% 2. NTN Narrowband Channel
% ------------ 初始化 NTN 平坦衰落窄带信道 ------------
% 使用 p681LMSChannel 初始化 NTN 信道模型
ntnNarrowbandChan = p681LMSChannel;
ntnNarrowbandChan.SampleRate = commonParams.SampleRate;
ntnNarrowbandChan.CarrierFrequency = commonParams.CarrierFrequency;
ntnNarrowbandChan.ElevationAngle = commonParams.ElevationAngle;
ntnNarrowbandChan.MobileSpeed = commonParams.MobileSpeed;
ntnNarrowbandChan.SatelliteDopplerShift = satelliteDopplerShift;
ntnNarrowbandChan.RandomStream = commonParams.RandomStream;  % 设置随机数流为 "mt19937ar with seed"
ntnNarrowbandChan.Seed = commonParams.Seed;
ntnNarrowbandChan.Environment = "Urban";  % 环境模型选择:"Urban", "Suburban", "Rural", "Custom"
                                                    % 可选:
                                                    % "Urban":城市环境模型,建筑物的反射、多径衰落影响
                                                    % "Suburban":郊区环境模型,适用于中等密度地区
                                                    % "Rural":乡村环境,远离城市的自然环境
                                                    % "Custom":自定义环境设置,可手动定义路径损失、多径等
 
ntnNarrowbandChan.AzimuthOrientation = 0;           % 方位角朝向为0度,表示移动终端的运动方向与卫星方位完全一致
ntnNarrowbandChan.FadingTechnique = "Sum of sinusoids"; % 衰落模型选择: "Sum of sinusoids" 或 "Filtered Gaussian noise"
                                                    % "Sum of sinusoids":正弦波叠加模型,模拟平坦衰落
                                                    % "Filtered Gaussian noise":滤波高斯噪声模型,适用于复杂衰落

ntnNarrowbandChan.NumSinusoids = commonParams.NumSinusoids;

% 如果选择"Custom"则需要自定义环境,则需要进一步设置环境参数
if strcmpi(ntnNarrowbandChan.Environment,'Custom')
    % 定义自定义环境中的路径损失、多径功率系数等
    ntnNarrowbandChan.StateDistribution = [3.0639 2.9108; 1.6980 1.2602];
    ntnNarrowbandChan.MinStateDuration = [10 6];
    ntnNarrowbandChan.DirectPathDistribution = [-1.8225 -15.4844; 1.1317 3.3245];
    ntnNarrowbandChan.MultipathPowerCoefficients = [-0.0481 0.9434; -14.7450 -1.7555];
    ntnNarrowbandChan.StandardDeviationCoefficients = [-0.4643 -0.0798; 0.3334 2.8101];
    ntnNarrowbandChan.DirectPathCorrelationDistance = [1.7910 1.7910];
    ntnNarrowbandChan.TransitionLengthCoefficients = [0.0744; 2.1423];
    ntnNarrowbandChan.StateProbabilityRange = [0.05 0.1; 0.95 0.9];
end

% 获取 p681 LMS 基带信道模型的信息,并检查信道滤波延迟为0(由于平坦衰落)
p681ChannelInfo = info(ntnNarrowbandChan);
% ------------ 生成 NTN 平坦衰落窄带信道 ------------
% 生成一个随机输入信号
rng(commonParams.Seed);
in = randn(commonParams.SampleRate, 1, 'like', 1i); % 生成复数高斯信号
% 通过 NTN 信道对输入信号进行滤波,得到路径增益和输出
[narrowbandOut, narrowbandPathGains, narrowbandSampleTimes] = ntnNarrowbandChan(in);

% 可视化接收信号的频谱
% ntnNarrowbandAnalyzer = spectrumAnalyzer('SampleRate', ntnNarrowbandChan.SampleRate);
% ntnNarrowbandAnalyzer.Title = "Received Signal Spectrum " + "NTN Narrowband with " + string(ntnNarrowbandChan.Environment) + " environment";
% ntnNarrowbandAnalyzer.ShowLegend = true;
% ntnNarrowbandAnalyzer.ChannelNames = "Rx Antenna 1";
% ntnNarrowbandAnalyzer(narrowbandOut);

三、NTN TDL 信道

   该代码支持 3GPP TR 38.811 第 6.9.2 节中定义的所有四种 NTN TDL 信道配置文件。这四种信道配置文件分别为:NTN-TDL-A、NTN-TDL-B、NTN-TDL-C、NTN-TDL-D,具体如下表所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
   关键特性说明:
   1.NTN-TDL-A 和 NTN-TDL-B 信道配置文件定义为非视距(NLOS) 条件。
   2.NTN-TDL-C 和 NTN-TDL-D 信道配置文件定义为视距(LOS) 条件。
   3.所有四种信道配置文件均在50度仰角下定义(可以认为是比较理想的情况)。

   补充:NTN TDL 信道与地面 TDL 信道的主要区别:
   1.多普勒频移补偿:NTN TDL 信道除了考虑移动终端或用户设备(UE)运动引起的多普勒频移外,还考虑了卫星运动引起的多普勒频移。
   2.延迟特性差异:由于大传播延迟(单程传播时延为ms级)和不同的散射环境(由于卫星离地很远,这些反射路径与直射路径的夹角非常小),NTN TDL 信道的延迟配置文件与地面 TDL 信道配置文件不同。

   按照 3GPP TR 38.811 第 6.9.2 节 [1] 的规定,对 NTN 频率选择性衰落 TDL 信道进行建模步骤如下:
   1.设置特定于 NTN 频率选择性衰落 TDL 信道的参数
   2.生成 NTN 频率选择性衰落 TDL 信道
   3.可视化衰落或滤波后信号的频谱

%% 3. NTN TDL Channel
% ------------ 初始化 NTN TDL 信道 ------------
% 使用 nrTDLChannel 设置 NTN-TDL 信道(选择 DelayProfile)
ntnTDLChan = nrTDLChannel;
ntnTDLChan.DelayProfile = "NTN-TDL-D";              % 选择 NTN-TDL-C 配置
                                                    % 可选:
                                                    % "NTN-TDL-A":适用于视距(LOS)环境的延迟配置
                                                    % "NTN-TDL-B":适用于视距(LOS)环境的不同配置
                                                    % "NTN-TDL-C":适用于视距(LOS)环境的另一种延迟配置(常用于卫星通信)
                                                    % "NTN-TDL-D":适用于非视距(NLOS)环境的配置
                                                    % "Custom":自定义延迟配置,手动设置多径和延迟

ntnTDLChan.DelaySpread = 30e-9;                     % 设置延迟扩展(30 ns)
ntnTDLChan.TransmissionDirection = "Downlink";      % 下行链路
ntnTDLChan.MIMOCorrelation = "Low";                 % MIMO 相关性设置为低
                                                    % 可选:
                                                    % "Low":低相关性,天线间信号独立性强
                                                    % "Medium":中等相关性,适合密集环境
                                                    % "High":高相关性,适合非常密集的环境
                                                    % "Custom":自定义MIMO相关性矩阵

ntnTDLChan.Polarization = "Co-Polar";               % 极化方式
                                                    % 可选:
                                                    % "Co-Polar":同极化,发送和接收天线使用相同的极化方式
                                                    % "Cross-Polar":交叉极化,发送和接收天线使用相对垂直的极化方式
                                                    % "Custom":自定义极化设置

ntnTDLChan.SampleRate = commonParams.SampleRate;
ntnTDLChan.MaximumDopplerShift = mobileMaxDoppler;  % 设置最大多普勒频移
ntnTDLChan.SatelliteDopplerShift = satelliteDopplerShift;  % 卫星的多普勒频移
ntnTDLChan.RandomStream = commonParams.RandomStream;
ntnTDLChan.Seed = commonParams.Seed;

% ------------ 设置天线配置 ------------
if ~strcmpi(ntnTDLChan.MIMOCorrelation,'Custom')
    ntnTDLChan.NumTransmitAntennas = 1;  % 发送天线数
    ntnTDLChan.NumReceiveAntennas = 2;   % 接收天线数
else
    % 自定义 MIMO 相关性配置
    if any(strcmpi(ntnTDLChan.Polarization,{'Co-Polar','Cross-Polar'}))
        ntnTDLChan.TransmitCorrelationMatrix = 1;
        ntnTDLChan.ReceiveCorrelationMatrix = [1 0; 0 1];
    end
    % 自定义交叉极化设置
    if strcmpi(ntnTDLChan.Polarization,'Cross-Polar')
        ntnTDLChan.TransmitPolarizationAngles = [45 -45];    % 发送极化角度
        ntnTDLChan.ReceivePolarizationAngles = [90 0];       % 接收极化角度
        ntnTDLChan.XPR = 10;                                 % 极化交叉比,单位 dB
    end
    if strcmpi(ntnTDLChan.Polarization,'Custom')
        ntnTDLChan.SpatialCorrelationMatrix = [1 0; 0 1];    % 空间相关矩阵
    end
end


% ------------ 生成 NTN TDL 信道的随机输入信号 ------------
% 生成一个随机输入信号
rng(commonParams.Seed);
in = randn(commonParams.SampleRate, ntnTDLChan.NumTransmitAntennas, 'like', 1i);

% 通过 NTN-TDL 信道生成衰落波形
[tdlOut, tdlPathGains, tdlSampleTimes] = ntnTDLChan(in);

% ------------ 可视化 NTN TDL 信道的接收信号频谱 ------------
% 可视化 NTN TDL 信道的频谱
ntnTDLAnalyzer = spectrumAnalyzer('SampleRate', ntnTDLChan.SampleRate);
ntnTDLAnalyzer.Title = "Received Signal Spectrum " + "NTN TDL with " + string(ntnTDLChan.DelayProfile) + " delay profile";
ntnTDLAnalyzer.ShowLegend = true;

for nRx = 1:size(tdlOut,2)
    ntnTDLAnalyzer.ChannelNames{nRx} = "Rx Antenna " + nRx;
end
ntnTDLAnalyzer(tdlOut);  % 显示接收信号的频谱

   上述代码都是封装好的,修改起来没那么丝滑,若想进一步修改函数定义,同时学习TDL信道是如何设定的,则需要找到函数nrTDLChannel.m进一步学习。例如,以下为根据指定的延迟配置文件(自定义或预定义)生成TDL信道的路径延迟和增益的代码,特别处理了LOS路径的拆分与合并,最终输出合并后的路径延迟、增益以及第一抽头的K因子。
函数使用范例如下所示:

%   示例 1:
%   配置TDL信道,对输入信号进行滤波,并绘制接收波形频谱。
%   使用TDL-C延迟配置文件,300 ns时延扩展和UE速度30 km/h。
%   
%   v = 30.0;                    % UE速度,单位km/h
%   fc = 4e9;                    % 载波频率,单位Hz
%   c = physconst('lightspeed'); % 光速,单位m/s
%   fd = (v*1000/3600)/c*fc;     % UE最大多普勒频移,单位Hz
%
%   tdl = nrTDLChannel;
%   tdl.DelayProfile = 'TDL-C';
%   tdl.DelaySpread = 300e-9;
%   tdl.MaximumDopplerShift = fd;
%
%   % 创建1个子帧时长的随机波形(1天线),通过信道并绘制接收波形频谱
%
%   SR = 30.72e6;
%   T = SR * 1e-3;
%   tdl.SampleRate = SR;
%   tdlinfo = info(tdl);
%   Nt = tdlinfo.NumTransmitAntennas;
%
%   txWaveform = complex(randn(T,Nt),randn(T,Nt));
%
%   rxWaveform = tdl(txWaveform);
%
%   analyzer = spectrumAnalyzer('SampleRate',tdl.SampleRate);
%   analyzer.Title = ['接收信号频谱 ' tdl.DelayProfile];
%   analyzer(rxWaveform);
%
%   % 示例 2:
%   % 绘制SISO情况下TDL-E延迟配置文件的路径增益,多普勒频移为70 Hz。
%    
%   tdl = nrTDLChannel;
%   tdl.SampleRate = 500e3;
%   tdl.NumTransmitAntennas = 1;
%   tdl.NumReceiveAntennas = 1;
%   tdl.MaximumDopplerShift = 70;
%   tdl.DelayProfile = 'TDL-E';
%    
%   % 虚拟输入信号,其长度决定生成的路径增益样本数
%   in = zeros(1000,tdl.NumTransmitAntennas);
%    
%   % 生成路径增益
%   [~, pathGains] = tdl(in);
%   mesh(10*log10(abs(pathGains)));
%   view(26,17); xlabel('信道路径');
%   ylabel('样本(时间)'); zlabel('幅度(dB)');
%
%   % 示例 3:
%   % 配置交叉极化天线的信道并滤波输入信号。
%   % 使用TDL-D延迟配置文件,10 ns时延扩展和期望总体K因子7.0 dB。
%   % 根据TS 36.101附录B.2.3A.3 4x2高相关配置交叉极化天线。
%
%   tdl = nrTDLChannel;
%   tdl.NumTransmitAntennas = 4;
%   tdl.DelayProfile = 'TDL-D';
%   tdl.DelaySpread = 10e-9;
%   tdl.KFactorScaling = true;
%   tdl.KFactor = 7.0; % 期望模型K因子(K_desired),单位dB
%   tdl.MIMOCorrelation = 'High';
%   tdl.Polarization = 'Cross-Polar';
%
%   % 创建4天线1个子帧时长的随机波形,通过信道
%
%   SR = 1.92e6;
%   T = SR * 1e-3;
%   tdl.SampleRate = SR;
%   tdlinfo = info(tdl);
%   Nt = tdlinfo.NumTransmitAntennas;
%
%   txWaveform = complex(randn(T,Nt),randn(T,Nt));
%
%   rxWaveform = tdl(txWaveform);
%
%   % 示例 4:
%   % 配置自定义延迟配置文件的信道并滤波输入信号。
%   % 设置两个信道抽头如下:
%   %   抽头1:莱斯,平均功率0 dB,K因子10 dB,延迟零
%   %   抽头2:瑞利,平均功率-5 dB,延迟45 ns
%
%   tdl = nrTDLChannel;
%   tdl.NumTransmitAntennas = 1;
%   tdl.DelayProfile = 'Custom';
%   tdl.FadingDistribution = 'Rician';
%   tdl.KFactorFirstTap = 10.0; % 第一抽头K因子(K_1),单位dB
%   tdl.PathDelays = [0.0 45e-9];
%   tdl.AveragePathGains = [0.0 -5.0];
%
%   % 创建1天线1个子帧时长的随机波形,通过信道
%
%   SR = 30.72e6;
%   T = SR * 1e-3;
%   tdl.SampleRate = SR;
%   tdlinfo = info(tdl);
%   Nt = tdlinfo.NumTransmitAntennas;
%
%   txWaveform = complex(randn(T,Nt),randn(T,Nt));
%
%   rxWaveform = tdl(txWaveform);
%
%   % 示例 5:
%   % 为高度600 km、速度7562.2 m/s、与UE仰角50度的卫星配置NTN信道。
%   % 使用NTN-TDL-A配置文件,100 ns时延扩展,UE速度3 km/h,载波频率2 GHz。
%
%   % 计算卫星运动引起的多普勒频移和UE周围散射环境引起的最大多普勒频移
%   r = physconst('earthradius');              % 地球半径,单位m
%   c = physconst('lightspeed');               % 光速,单位m/s
%   fc = 2e9;                                  % 载波频率,单位Hz
%   theta = 50;                                % 仰角,单位度
%   h = 600e3;                                 % 卫星高度,单位m
%   vSat = 7562.2;                             % 卫星速度,单位m/s
%   vUE = 3 * 1000/3600;                         % UE速度,单位m/s
%   fdMaxUE = (vUE*fc)/c;                      % UE最大多普勒频移,单位Hz
%   fdSat = (vSat*fc/c)*(r*cosd(theta)/(r+h)); % 卫星多普勒频移,单位Hz
%
%   % 配置TDL信道
%   ntnChan = nrTDLChannel;
%   ntnChan.DelayProfile = 'NTN-TDL-A';
%   ntnChan.DelaySpread = 100e-9;
%   ntnChan.SatelliteDopplerShift = fdSat;
%   ntnChan.MaximumDopplerShift = fdMaxUE;
%
%   % 使用配置的天线数创建1个子帧时长的随机波形
%   SR = 30.72e6;
%   T = SR*1e-3;
%   ntnChan.SampleRate = SR;
%   ntnChanInfo = info(ntnChan);
%   Nt = ntnChanInfo.NumTransmitAntennas;
%   txWaveform = randn(T,Nt,'like',1i);
%
%   % 使波形通过信道
%   rxWaveform = ntnChan(txWaveform);
%
%   另见 nrCDLChannel, nrHSTChannel, comm.MIMOChannel,
%   nrPerfectTimingEstimate, nrPerfectChannelEstimate.

%   版权所有 2016-2024 The MathWorks, Inc.
    
%#codegen

% =========================================================================
%   公共接口

该函数的代码如下:

function [pathDelaysOut, pathGainsOut, K_1dB] = getDelayProfile(obj)
    % getDelayProfile - 获取TDL信道的延迟分布特性
    % 该函数根据指定的延迟配置文件生成路径延迟和路径增益
    %
    % 输入参数:
    %   obj - nrTDLChannel对象实例
    %
    % 输出参数:
    %   pathDelaysOut - 输出的路径延迟向量(单位:秒)
    %   pathGainsOut - 输出的路径增益向量(单位:dB)
    %   K_1dB - 第一抽头的莱斯K因子(单位:dB),若无LOS路径则为-Inf
    
    % 声明外部函数(这些函数在代码生成时需要特殊处理)
    coder.extrinsic('nr5g.internal.nrTDLChannel.getTDLProfile');
    coder.extrinsic('wireless.internal.channelmodels.scaleDelaysAndKFactor');
    
    %% 处理自定义延迟配置文件
    if strcmpi(obj.DelayProfile, 'Custom')
        % 使用用户自定义的路径延迟和增益
        pathDelaysIn = obj.PathDelays;
        pathGainsIn = obj.AveragePathGains;
        
        % 检查是否存在LOS路径
        if nrTDLChannel.hasLOSPath(obj)
            % 存在LOS路径时,将第一抽头拆分为LOS部分和Rayleigh部分
            % 根据K_1因子进行拆分
            
            % 获取第一抽头的K因子(dB)并转换为线性值
            K_1dB = obj.KFactorFirstTap;
            K_1 = 10^(K_1dB/10);
            
            % 获取第一抽头的总功率(dB)并转换为线性值
            P_1dB = pathGainsIn(1);
            P_1 = 10^(P_1dB/10);
            
            % 拆分第一抽头:
            % LOS部分功率 = P_1 * K_1 / (1 + K_1)
            % Rayleigh部分功率 = P_1 / (1 + K_1)
            losPower = P_1 * K_1 / (1 + K_1);
            rayleighPower = P_1 / (1 + K_1);
            
            % 创建新的延迟向量:
            % 将原第一抽头的延迟拆分为两个相同的延迟值
            pathDelays = [pathDelaysIn(1), pathDelaysIn(1), pathDelaysIn(2:end)];
            
            % 创建新的增益向量:
            % 将原第一抽头的增益拆分为LOS和Rayleigh两部分
            pathGains = [10*log10(losPower), ...    % LOS部分增益(dB)
                        10*log10(rayleighPower), ... % Rayleigh部分增益(dB)
                        pathGainsIn(2:end)];        % 其余抽头增益
            
        else
            % 无LOS路径时,直接使用自定义参数
            pathDelays = pathDelaysIn;
            pathGains = pathGainsIn;
        end
        
    %% 处理预定义延迟配置文件
    else
        % 初始化期望的K因子
        desiredKFactor = NaN;
        
        % 检查是否存在LOS路径
        if nrTDLChannel.hasLOSPath(obj)
            % 如果启用了K因子缩放,则使用指定的K因子
            if obj.KFactorScaling
                desiredKFactor = obj.KFactor;
            end
        end
        
        % 获取预定义的延迟分布(PDP)
        pdp = coder.const(double(nr5g.internal.nrTDLChannel.getTDLProfile(obj.DelayProfile)));
        
        % 对非简化延迟配置文件执行延迟和K因子缩放
        % 简化配置文件列表(不进行缩放)
        simplifiedProfiles = {'TDLA30','TDLB100','TDLC300','TDLC60','TDLD30',...
                             'TDLA10','TDLD10','NTN-TDLA100','NTN-TDLC5'};
        
        if ~any(strcmp(obj.DelayProfile, simplifiedProfiles))
            % 对延迟和K因子进行缩放
            pdp = coder.const(double(wireless.internal.channelmodels.scaleDelaysAndKFactor(...
                pdp, desiredKFactor, obj.DelaySpread)));
        end
        
        % 从PDP中提取路径延迟(第1列)和路径增益(第2列)
        pathDelays = pdp(:,1).'; 
        pathGains = pdp(:,2).';  
    end
    
    %% 合并LOS和Rayleigh部分(如果存在)
    % 无论使用自定义还是预定义配置,如果存在LOS路径,
    % 此时第一抽头已被拆分为LOS和Rayleigh两部分
    
    if nrTDLChannel.hasLOSPath(obj)
        % 计算K因子: LOS增益 - Rayleigh增益
        K_1dB = pathGains(1) - pathGains(2);
        
        % 合并LOS和Rayleigh部分的功率:
        % 先将两部分功率转换为线性值,相加后再转换回dB
        totalPower = 10^(pathGains(1)/10) + 10^(pathGains(2)/10);
        pathGainsOut = [10*log10(totalPower), pathGains(3:end)];
        
        % 更新延迟向量: 移除Rayleigh部分对应的延迟
        pathDelaysOut = pathDelays([1, 3:end]);
        
    else
        % 无LOS路径时,K因子设为-Inf
        K_1dB = -Inf;
        
        % 直接使用原始增益和延迟
        pathGainsOut = pathGains;
        pathDelaysOut = pathDelays;
    end
end

函数doppler.m则为关于多普勒频移的模拟:

function s = doppler(specType, varargin)
%DOPPLER 构造多普勒频谱结构体,用于衰落信道系统对象
%   该函数创建特定类型的多普勒频谱结构体,用于衰落信道建模
%
%   s = doppler(SPECTYPE) 构造指定SPECTYPE类型的多普勒频谱结构体
%   返回的结构体s包含SpectrumType字段,部分类型还包含其他依赖字段
%
%   支持的频谱类型及其依赖字段和默认值:
%   -----------------------------------------------------------------------
%     频谱类型             |  依赖字段                  |  默认值
%   -----------------------|----------------------------|-----------------
%     'Jakes'             |  无                       |  无
%     'Flat'              |  无                       |  无
%     'Rounded'           |  Polynomial(多项式系数)   |  [1 -1.72 0.785]
%     'Bell'              |  Coefficient(系数)        |  9
%     'Asymmetric Jakes'  |  NormalizedFrequencyInterval(归一化频率区间) | [0 1]
%     'Restricted Jakes'  |  NormalizedFrequencyInterval(归一化频率区间) | [0 1]
%     'Gaussian'          |  NormalizedStandardDeviation(归一化标准差) | 1/sqrt(2)
%     'BiGaussian'        |  NormalizedStandardDeviations(归一化标准差) | 1/sqrt(2)*[1 1]
%                         |  NormalizedCenterFrequencies(归一化中心频率) | [0 0]
%                         |  PowerGains(功率增益)      |  [0.5 0.5]
%   -----------------------------------------------------------------------
%
%   函数也支持通过输入参数指定依赖字段的值。以下是各频谱类型的详细说明和解析表达式,
%   其中fd表示关联衰落信道系统对象的MaximumDopplerShift(最大多普勒频移)
%
%   s = doppler('Jakes') 构造Jakes多普勒频谱结构体
%   理论Jakes多普勒频谱解析表达式:
%       S(f) = 1/(pi*fd*sqrt(1-(f/fd)^2))         -fd <= f <= fd
%
%   s = doppler('Flat') 构造平坦多普勒频谱结构体
%   理论平坦多普勒频谱解析表达式:
%       S(f) = 1/(2*fd)                          -fd <= f <= fd
%
%   s = doppler('Rounded', POLY) 构造圆润多普勒频谱结构体
%   参数POLY设置Polynomial字段,理论表达式为4阶多项式(仅含偶次项):
%       S(f) = a0 + a2*(f/fd)^2 + a4*(f/fd)^4    -fd <= f <= fd
%   系数a0, a2, a4通过POLY = [a0 a2 a4]指定
%
%   s = doppler('Bell', COEFF) 构造钟形多普勒频谱结构体
%   参数COEFF设置Coefficient字段,理论表达式:
%       S(f) = 1/(1 + c*(f/fd)^2)                -fd <= f <= fd
%   其中c = COEFF为非负实数标量
%
%   s = doppler('Asymmetric Jakes', FREQINTERVAL) 构造非对称Jakes频谱
%   参数FREQINTERVAL设置NormalizedFrequencyInterval字段,理论表达式:
%       S(f) = 1/(pi*fd*sqrt(1-(f/fd)^2))        fmin <= f <= fmax
%   其中fmin和fmax通过FREQINTERVAL = [fmin/fd fmax/fd]指定(归一化值)
%
%   s = doppler('Restricted Jakes', FREQINTERVAL) 构造受限Jakes频谱
%   参数FREQINTERVAL设置NormalizedFrequencyInterval字段,理论表达式:
%       S(f) = 1/(pi*fd*sqrt(1-(f/fd)^2))        fmin <= |f| <= fmax
%   其中fmin和fmax通过FREQINTERVAL = [fmin/fd fmax/fd]指定(归一化值)
%
%   s = doppler('Gaussian', STDDEVIATION) 构造高斯多普勒频谱
%   参数STDDEVIATION设置NormalizedStandardDeviation字段,理论表达式:
%       S(f) = 1/sqrt(2*pi*stdDev^2) * exp(-f^2/(2*stdDev^2))  
%   其中stdDev通过STDDEVIATION = stdDev/fd指定(归一化标准差)
%
%   s = doppler('BiGaussian', 'NormalizedStandardDeviations', STDDEVIATION,
%               'NormalizedCenterFrequencies', CENTERFREQ, 
%               'PowerGains', GAIN) 构造双高斯多普勒频谱
%   参数说明:
%     STDDEVIATION = [stdDev1/fd stdDev2/fd]  (归一化标准差)
%     CENTERFREQ = [fc1/fd fc2/fd]            (归一化中心频率)
%     GAIN = [gain1 gain2]                    (功率增益分量)
%   理论表达式:
%       S(f) = gain1/sqrt(2*pi*stdDev1^2)*exp(-(f-fc1)^2/(2*stdDev1^2))
%            + gain2/sqrt(2*pi*stdDev2^2)*exp(-(f-fc2)^2/(2*stdDev2^2))
%
%   示例:
%     % 构造Jakes多普勒频谱
%     doppler('Jakes')
%     
%     % 构造自定义多项式系数的圆润多普勒频谱
%     doppler('Rounded', [1 2 1])
%     
%     % 构造系数为3的钟形多普勒频谱
%     doppler('Bell', 3)
%     
%     % 构造受限Jakes频谱(归一化频率区间[0, 0.5])
%     doppler('Restricted Jakes', [0 0.5])
%     
%     % 构造双高斯频谱(自定义中心频率)
%     doppler('BiGaussian', 'NormalizedCenterFrequencies', [0.1 0.8])
%
%   另见:comm.RayleighChannel, comm.RicianChannel, comm.MIMOChannel

% 版权所有 2012-2018 The MathWorks, Inc.

% 确保specType为字符类型
specType = convertStringsToChars(specType);
coder.internal.errorIf(~ischar(specType), 'comm:doppler:InvalidSpecType');

%% 根据频谱类型处理不同情况
switch specType
  case {'Jakes', 'Flat'} % Jakes和平坦频谱
    narginchk(1, 1); % 验证输入参数数量
    s = struct('SpectrumType', specType); % 创建基础结构体
  
  case 'Bell' % 钟形频谱
    narginchk(1, 2); % 验证输入参数数量
    if nargin == 2
        % 验证系数参数有效性
        validateattributes(varargin{1}, {'numeric'}, ...
            {'real','scalar','nonnegative','finite'}, ...
            'doppler(''Bell'', Coefficient)', 'Coefficient');
        coeff = varargin{1};
    else
        coeff = 9; % 默认系数
    end
    % 创建包含系数的结构体
    s = struct('SpectrumType', specType, ...
               'Coefficient',  coeff);
  
  case 'Rounded' % 圆润频谱
    narginchk(1, 2); % 验证输入参数数量
    if nargin == 2
        % 验证多项式参数有效性
        validateattributes(varargin{1}, {'numeric'}, ...
            {'real','row','ncols',3,'finite'}, ...
            'doppler(''Rounded'', Polynomial)', 'Polynomial');
        poly = varargin{1};
    else
        poly = [1 -1.7200 0.7850]; % 默认多项式系数
    end
    % 创建包含多项式的结构体
    s = struct('SpectrumType', specType, ...
               'Polynomial',   poly);
  
  case {'Asymmetric Jakes', 'Restricted Jakes'} % 非对称/受限Jakes频谱
    narginchk(1, 2); % 验证输入参数数量
    if nargin == 2
        % 确定频率区间的最小边界
        minBound = strcmp(specType, 'Restricted Jakes') - 1;
        % 验证频率区间参数有效性
        validateattributes(varargin{1}, {'numeric'}, ...
            {'real','row','ncols',2,'<=',1,'>=', minBound}, ...
            ['doppler(''', specType, ''', NormalizedFrequencyInterval)'], ...
            'NormalizedFrequencyInterval');
        % 检查频率区间是否递增
        coder.internal.errorIf(diff(varargin{1}) < 0, 'comm:doppler:InvalidFreqInterval');
        freqInterval = varargin{1};
    else
        freqInterval = [0 1]; % 默认频率区间
    end
    % 创建包含频率区间的结构体
    s = struct('SpectrumType',                specType, ...
               'NormalizedFrequencyInterval', freqInterval);
  
  case 'Gaussian' % 高斯频谱
    narginchk(1, 2); % 验证输入参数数量
    if nargin == 2
        % 验证标准差参数有效性
        validateattributes(varargin{1}, {'numeric'}, ...
            {'real','scalar','positive','finite'}, ...
            'doppler(''Gaussian'', NormalizedStandardDeviation)', ...
            'NormalizedStandardDeviation');
        stdDev = varargin{1};
    else
        stdDev = 1/sqrt(2); % 默认标准差
    end
    % 创建包含标准差的结构体
    s = struct('SpectrumType',                specType, ...
               'NormalizedStandardDeviation', stdDev);
  
  case 'BiGaussian' % 双高斯频谱
    narginchk(1, 7); % 验证输入参数数量
    % 检查是否为参数-值对形式
    coder.internal.errorIf((mod(nargin - 1,  2) ~= 0), ...
        'comm:doppler:InputNotPVParis');
    % 创建默认双高斯结构体
    s = struct('SpectrumType',                 specType, ...
               'NormalizedStandardDeviations', [1/sqrt(2), 1/sqrt(2)], ...
               'NormalizedCenterFrequencies',  [0, 0], ...
               'PowerGains',                   [0.5, 0.5]);
    % 处理参数-值对输入
    for i = 1 : 2: nargin-2
        % 验证参数名称有效性
        coder.internal.errorIf(~comm.internal.utilities.isCharOrStringScalar(varargin{i}), ...
            'comm:doppler:InvalidBiGaussianField');
        % 根据参数名处理不同字段
        switch varargin{i}
          case 'NormalizedStandardDeviations' % 归一化标准差
            validateattributes(varargin{i+1}, {'numeric'}, ...
                {'real','row','ncols',2,'positive','finite'}, ...
                'doppler(''BiGaussian'', ''NormalizedStandardDeviations'', value)', ...
                'NormalizedStandardDeviations');
            s.NormalizedStandardDeviations = varargin{i+1};
          case 'NormalizedCenterFrequencies' % 归一化中心频率
            validateattributes(varargin{i+1}, {'numeric'}, ...
                {'real','row','ncols',2,'<=',1,'>=',-1}, ...
                'doppler(''BiGaussian'', ''NormalizedCenterFrequencies'', value)', ...
                'NormalizedCenterFrequencies');
            s.NormalizedCenterFrequencies = varargin{i+1};
          case 'PowerGains' % 功率增益
            validateattributes(varargin{i+1}, {'numeric'}, ...
                {'real','row','ncols',2,'nonnegative','finite'}, ...
                'doppler(''BiGaussian'', ''PowerGains'', value)', 'PowerGains');
            % 检查增益是否全为零
            coder.internal.errorIf(isequal(varargin{i+1}, zeros(1, 2)), ...
                'comm:doppler:PowerGainsBothZeros');
            s.PowerGains = varargin{i+1};
          otherwise % 无效参数名
            coder.internal.errorIf(true, 'comm:doppler:InvalidBiGaussianField');                    
        end
    end
  otherwise % 无效频谱类型
    coder.internal.errorIf(true, 'comm:doppler:InvalidSpecType');
end

end

网站公告

今日签到

点亮在社区的每一天
去签到