你现在是一名无线通信方向的专家,详细介绍一下'TDL-A','TDL-B','TDL-C','TDL...

创建于:2025年11月14日

使用 GPT-5.1 Thinking 回答 Chat01

问题

你现在是一名无线通信方向的专家,详细介绍一下'TDL-A','TDL-B','TDL-C','TDL-D','CDL-A''CDL-B''CDL-C这些场景的区别

思考

Clarifying CDL scenario mapping and profile differences

TR 38.901 doesn't directly specify scenario mapping for CDL, but it can be inferred from earlier docs like TR 38.900. We can say “用于 NLOS 环境,如室内 / UMi” rather than specifying hard mappings. For example, NTIA’s NR PDSCH throughput uses CDL-A/TDL-A for Indoor NLOS and CDL-C/TDL-C for UMi O2I. Regarding the profiles, TDL-A/B each have 23 taps, while TDL-C has 24, and similar distributions apply to CDL profiles.

Crafting a clear answer with key citations

The user likely doesn’t need precise numbers, so I'll focus on clarity. I'll prepare a concise answer, avoiding unnecessary explanations. I'll cite key points, particularly from TR 38.901, including definitions of TDL/CDL models, and the mapping of CDL/TDL profiles like CDL-A/TDL-A for Indoor NLOS and CDL-C/TDL-C for UMi O2I.

Structuring the answer with key citations

Alright, let’s start by acknowledging the speaker’s request—then get into the basic concepts of TDL vs CDL. I’ll mention each profile with clear bullet points:

  • Definitions and differences (delay, power, and information dimensions).
  • TDL for simplified eval/SISO, CDL for spatial modeling/MIMO.
  • Both are defined for 0.5-100 GHz, with profiles A-E for varying scenarios (NLOS/LOS). Citations will be added right after relevant details.

Breaking down TDL and CDL profiles

I’ll start by explaining TDL profiles and their characteristics, such as the environment type (NLOS/LOS) and the Rayleigh or Rician distribution. For TDL-A, I'll mention normalized delay and how the RMS delay spread is scaled in practice, e.g., with “TDL-A30” or “TDL-A300”. I’ll emphasize the concepts, not specific numbers, while keeping it simple for clarity.

Thought for 2m 50s

回答

先把结论说在前面:

  • TDL-A/B/C/D 是 3GPP TR 38.901 里定义的 抽头延时线(Tapped Delay Line, TDL)信道模型,主要是“时延 + 功率 + 多普勒”,不显式建模角度/空间结构,偏向简化仿真。A/B/C 为 NLOS,D(和 E)为 LOS。(ETSI)
  • CDL-A/B/C簇延时线(Clustered Delay Line, CDL)信道模型,在“时延 + 功率”的基础上,把多径按簇划分,并且对每一簇/射线给出 AOD/AOA/ZOD/ZOA 等角度参数,适合 Massive MIMO、波束赋形等空间相关性研究。A/B/C 也都是 NLOS。(ETSI)

下面按层次展开:


1. TDL vs CDL:先把大的区别搞清楚

1.1 模型形式

TDL 模型(抽头延时线)

  • 信道冲激响应写成 h(t,τ)=khk(t)δ(ττk)h(t,\tau)=\sum_k h_k(t)\,\delta(\tau-\tau_k) 每个 tap k 有一个固定的 时延 τk\tau_k 和平均功率,时变的是复高斯系数 hk(t)h_k(t)。(ETSI)
  • 只关心 时延谱(PDP)+ 多普勒谱(Jakes)不显式给出入射角、出射角
  • 非 LOS profile(TDL-A/B/C)所有抽头都是 Rayleigh 分布;LOS profile(TDL-D/E)第一个抽头是 Rician,其 K 因子可调。(ETSI)

CDL 模型(簇延时线)

  • 把多径分成若干 簇(cluster),每个簇又包含若干 射线(rays)
    • 每个簇有自己的相对时延、功率;
    • 每条射线有 AOD、AOA、ZOD、ZOA 以及极化信息(XPR) 等。(ETSI)
  • 同样是 PDP,但附加了 角度域统计特性,可以直接反映空间相关性,适合模拟 大规模天线阵列 / 波束赋形 / 3D MIMO

1.2 profile 集合 & LOS/NLOS

在 TR 38.901 中:

  • TDL:TDL-A, TDL-B, TDL-C(NLOS);TDL-D, TDL-E(LOS)。(ETSI)
  • CDL:CDL-A, CDL-B, CDL-C(NLOS);CDL-D, CDL-E(LOS)。(ETSI)

而且:

  • 所有这些模型都适用于 0.5–100 GHz、带宽 ≤ 2 GHz 的链路级仿真。(ETSI)
  • profile 表里只给“归一化时延”,真正的 RMS Delay Spread(DS)通过一个缩放系数来设定,例如 38.101 里的 TDL-A30、TDL-C300 等。(ETSI)

2. 各 TDL 场景:TDL-A / B / C / D

2.1 TDL-A(NLOS,较短时延扩展)

特点: (ETSI)

  • NLOS profile,所有抽头 Rayleigh。
  • 大约 23 个抽头,大部分功率集中在几个较早的时延上,后面还有一些弱的尾部。
  • 典型用法:代表 室内 / 小型小区的中等多径 NLOS 场景。很多文档把 TDL-A / CDL-A 对应为“Indoor NLOS, DS ≈ 20 ns” 的一类环境。(Institute for Telecommunication Sciences)
  • 频率选择性相对不算强,子载波间相关性比 TDL-C 高一点,适合“中等”多径的 UE 性能测试。

工程直觉:

“房间里有一些反射,但路径不会拖得特别长”,频率选择性适中。


2.2 TDL-B(NLOS,比 A 更“密集”的早期多径)

特点: (ETSI)

  • 同样是 NLOS,23 个抽头,Rayleigh。
  • 相比 TDL-A:
    • 更多功率集中在非常靠近 0 延时的几个抽头;
    • 近零时延附近的抽头数量和功率更“密集”,后面的尾巴略有差别。
  • 常用于代表 多径稍微更丰富一些的 NLOS 环境,可以看作比 TDL-A 更强一点的频率选择性。

工程直觉:

还是 NLOS,但天线周围反射/散射物体更多一点,近身密集反射较多。


2.3 TDL-C(NLOS,长时延扩展)

特点: (ETSI)

  • NLOS profile,24 个抽头(比 A/B 多一个),Rayleigh。
  • 抽头延时分布明显拉长,有比较明显的 远端延时尾巴
  • 通过设定 DS,一般会用在 时延扩展较大的 NLOS 场景,例如:
    • 城市微小区 Outdoor-to-Indoor(UMi O2I)
    • 部分密集城市 NLOS 场景。
  • 在 NTIA 的 NR PDSCH throughput 示例中,TDL-C / CDL-C 被用来表示 UMi O2I NLOS、DS ≈ 300 ns 的场景。(Institute for Telecommunication Sciences)

工程直觉:

“楼外基站打进楼里 / 街道拐角后的非视距”,多径拖得很长,对 OFDM 来说 频率选择性很强,DMRS 设计、循环前缀长度等都更“吃紧”。


2.4 TDL-D(LOS + 丰富散射)

特点: (ETSI)

  • LOS profile:
    • 第一个抽头包含 LOS,Rician 分布(K 因子可调);
    • 后面抽头仍为 Rayleigh,表示散射路径。
  • 抽头时延分布和功率形状与 TDL-C 有点类似,但多了强 LOS tap,整体仍有较长尾巴,可看作“视距 + 丰富 NLOS 反射”的环境。
  • 典型用途:UMa/UMi 带视距的城市链路,或者希望在仿真中显式考察 LOS 对系统性能、波束赋形、码间干扰的影响。

工程直觉:

“主干道上有直视路径,但周围高楼一圈”,LOS 很强,但多径尾巴也很长。


3. 各 CDL 场景:CDL-A / B / C

这三个都是 NLOS 的簇延时线模型,区别主要在:

  1. PDP 形状(时延扩展短/中/长);
  2. 角度扩展(方位/仰角的 ASA/ASD/ZSA/ZSD);
  3. 簇 / 射线的数量和功率分布。(ETSI)

和 TDL 对应关系大致是:

3.1 CDL-A(NLOS,Indoor / 短时延扩展)

特点:

  • NLOS profile,簇数大致与 TDL-A 中抽头数类似(表 7.7.1-1 列出了 23 个簇)。(ETSI)
  • 各簇的延时整体比较集中,DS 较小,空间角度扩展也相对温和,适合:
    • 室内办公/热点 NLOS;
    • 小型小区 NLOS。
  • NTIA 文档里:Indoor NLOS,CDL-A/TDL-A,DS ≈ 20 ns。(Institute for Telecommunication Sciences)

TDL-A vs CDL-A:

  • TDL-A:只看“每一抽头有多少功率,在哪个时延”;
  • CDL-A:同一个 profile 下,还告诉你“这些抽头/簇从哪个方向来、以什么角度离开”,适合做波束赋形、空间相关性、相关矩阵设计等。

3.2 CDL-B(NLOS,中等丰富的城市多径)

特点: (ETSI)

  • NLOS profile,簇的时延比 CDL-A 稍微拉长一些,但还谈不上“很长尾巴”;
  • 角度扩展普遍比 CDL-A 略大,更适合模拟 室外 NLOS 或较复杂室内 环境,比如 UMi NLOS、部分小区边缘 NLOS 场景;
  • 作为 MIMO / Massive MIMO 仿真,经常拿来当“典型城市 NLOS”的空间信道。

对比:

  • 相比 CDL-A:
    • PDP、角度扩展都更“散”,多径更丰富;
    • 更适合作为“中等恶劣”的 NLOS baseline。
  • 相比 CDL-C:
    • 时延尾巴没有 CDL-C 那么长,频率选择性没有那么强。

3.3 CDL-C(NLOS,长时延扩展,UMi O2I 等)

特点: (ETSI)

  • NLOS profile,簇时延明显比 CDL-A/B 更大,有明显的远端延时簇。
  • 很适合描述:
    • 城市微小区 Outdoor-to-Indoor;
    • 建筑物外基站与楼内 UE 之间的多径;
    • 复杂城市峡谷非视距。
  • NTIA 文档中:UMi Outdoor-to-Indoor,CDL-C/TDL-C,DS ≈ 300 ns。(Institute for Telecommunication Sciences)

工程含义:

  • 对 OFDM 子载波间相关性、DMRS 密度、均衡器设计影响最大;
  • 在 Massive MIMO 下,可以研究“大带宽 + 大阵列 + 长时延扩展”组合下的波束赋形、CSI 获取难度等。

4. 这些 profile 之间的“核心差别”可以怎么记?

可以用一个简化对比表(只写相对关系,不写具体数值):

Profile模型类型LOS/NLOS时延扩展(相对)空间信息常见理解/场景示意
TDL-ATDLNLOS无角度室内/小区 NLOS,中等多径
TDL-BTDLNLOS无角度多径更密集的 NLOS
TDL-CTDLNLOS无角度UMi O2I、城市 NLOS,强频率选择性
TDL-DTDLLOS中~长 + 强 LOS无角度视距 + 丰富散射的城市场景
CDL-ACDLNLOS有(3D 角度)Indoor NLOS,小阵列/大阵列都可
CDL-BCDLNLOS城市 NLOS,多径稍丰富
CDL-CCDLNLOSUMi O2I / 城市峡谷 NLOS,大带宽下典型“难场景”

5. 实战里怎么选?

一个很实用的经验(也是 38.101/38.104、NTIA 等文档常用的套路):(Institute for Telecommunication Sciences)

  • 做射频/基带链路基本健壮性
    → 先用 TDL-A/TDL-B:简单、仿真快、能看到频率选择性和多普勒。
  • 想看 UE / gNB 在“很坏的 NLOS、多路径很长”下的表现
    → 用 TDL-C 或 CDL-C
  • 要评估波束赋形、MIMO 空间相关性、角度分布、码本设计
    → 必须用 CDL-A/B/C,因为 TDL 没有角度信息。
  • 要显式考察 LOS 下的 K 因子、波束追踪、LOS/NLOS 检测
    → 用 TDL-D / CDL-D(你只问到 TDL-D,这里顺带点一下 CDL-D)

如果你后面打算做 具体仿真(MATLAB / Sionna / 自写仿真),我可以帮你:

  • 画出几种 profile 的 PDP 对比;
  • 帮你选合适的 DelaySpread、K 因子、UE 速度,并写成完整的配置表 / 代码。

问题

详细介绍一下下面每一行代码的功能:function [precodingValue] = TxAnalogPrecoding(sensing, TxFreqSignal, channel)
txAntNum = sensing.txAntNum;
fftSize = sensing.fftSize;
Nsymbol = sensing.Nsymbol;
[Mg,Ng,M,N,P]=deal(channel.bs.Mg_Ng_M_N_P(1),channel.bs.Mg_Ng_M_N_P(2),channel.bs.Mg_Ng_M_N_P(3),channel.bs.Mg_Ng_M_N_P(4),channel.bs.Mg_Ng_M_N_P(5));
N1=sensing.bs.N1N2(1);
N2=sensing.bs.N1N2(2);
[K,L]=deal(channel.bs.txru_size(1),channel.bs.txru_size(2));
if K*L==1
precodingValue=repmat(TxFreqSignal,1,txAntNum);
precodingValue=reshape(precodingValue,fftSize,Nsymbol,txAntNum);
else

text
theta=channel.targetAngle(2); phi=channel.targetAngle(1); dv=channel.bs.spacing(2); dh=channel.bs.spacing(1); wk=exp(-1j*2*pi*([0:K-1])*dv*cos(theta))/sqrt(K); % according to TR36.897 5.2.2 wl=exp(-1j*2*pi*([0:L-1])*dh*sin(phi))/sqrt(L); W=kron(wl,wk); antId=reshape([1:M*N*Mg*Ng],M*Mg,N*Ng); precodingValue=zeros(size(TxFreqSignal,1),size(TxFreqSignal,2),K*L*size(TxFreqSignal,3)); for p=1:P for n1Id=1:N1 for n2Id=1:N2 portId=N2*(n1Id-1)+n2Id+(p-1)*N1*N2; data=TxFreqSignal(:,:,portId); analogValue=kron(data,W); antId_txru=antId(K*(n2Id-1)+1:n2Id*K,L*(n1Id-1)+1:n1Id*L)+(p-1)*M*N; analogValue=reshape(analogValue,size(TxFreqSignal,1),K*L,size(TxFreqSignal,2)); precodingValue(:,:,antId_txru(:))=permute(analogValue,[1,3,2]); end end end

end

思考

Explaining MATLAB function line-by-line

The user asked for a detailed explanation of each line of a MATLAB function. I'll walk through the function methodically, explaining variables, operations, and relevant concepts, like mmWave MIMO and analog beamforming. I’ll clarify the key parameters, and TR36.897 analog beamforming conventions, and break it down to provide a clear explanation for each line of the code.

Explaining MATLAB logic line-by-line (with formulas)

I'll go line by line to clarify the MATLAB function. The TxFreqSignal likely has the shape (fftSize, Nsymbol, Nport). Based on K*L, if it equals 1, the code replicates TxFreqSignal across antennas. Otherwise, they compute the analog beam steering vector, W, and use various spacing and angle parameters (theta, phi, dv, dh). I'll explain the mapping of antenna positions too. I'll mix Chinese with technical terms in English for clarity.

Explaining the matrix dimensions and kron operation

The frequency sample data is taken for the port, and the operation analogValue = kron(data, W) needs an understanding of matrix dimensions. The data is (fftSize x Nsymbol), while W is a (1 x KL) row vector. I’ll break down the shapes after applying the kron function — it results in (fftSize) x (NsymbolK*L), which is reshaped and permuted to match the final dimensions.

Thought for 28s

回答

我直接按从上到下、逐行给你讲每一句在干嘛,同时尽量说明一下物理/通信含义(混合波束成形、阵列结构之类)。


matlab
function [precodingValue] = TxAnalogPrecoding(sensing, TxFreqSignal, channel)
  • 定义一个函数 TxAnalogPrecoding,输入:
    • sensing:结构体,包含发射端相关配置(天线数、FFT 长度、符号数、端口排列等)。
    • TxFreqSignal:频域发射信号,一般尺寸是 [fftSize, Nsymbol, Nport]
    • channel:结构体,包含阵列结构、目标角度、天线间距等。
  • 输出:
    • precodingValue:加完模拟预编码(analog beamforming)之后、按物理天线展开的频域信号,一般尺寸 [fftSize, Nsymbol, txAntNum]

matlab
txAntNum = sensing.txAntNum; fftSize = sensing.fftSize; Nsymbol = sensing.Nsymbol;
  • sensing 结构体中读取关键配置参数:
    • txAntNum:基站物理发射天线总数。
    • fftSize:OFDM 的 FFT 点数。
    • Nsymbol:本次处理的 OFDM 符号数(时域上有多少个符号)。

matlab
[Mg,Ng,M,N,P]=deal(channel.bs.Mg_Ng_M_N_P(1),channel.bs.Mg_Ng_M_N_P(2),channel.bs.Mg_Ng_M_N_P(3),channel.bs.Mg_Ng_M_N_P(4),channel.bs.Mg_Ng_M_N_P(5));
  • channel.bs.Mg_Ng_M_N_P 是一个长度为 5 的向量,依次表示:
    • Mg垂直方向上 panel(子阵列)的数量(比如有 Mg 行 panel)。
    • Ng水平方向上 panel 的数量(Ng 列)。
    • M:每个 panel 内垂直方向天线数。
    • N:每个 panel 内水平方向天线数。
    • P:一般用来表示极化数或“panel 集合数量”等(这里作为一个额外的维度循环)。
  • deal(...) 的作用是把 5 个值分别“发给” Mg, Ng, M, N, P 五个变量。

简单理解:整个阵列排成 Mg×Ng 个 panel,每个 panel 里是 M×N 个天线,一共 Mg*Ng*M*N 个天线;P 是再额外的一个“层”,下面通过 (p-1)*M*N 偏移把它平铺到索引里。


matlab
N1=sensing.bs.N1N2(1); N2=sensing.bs.N1N2(2);
  • sensing.bs.N1N2 是一个 [N1, N2] 向量。
  • 这里把它拆出来:
    • N1:通常表示天线端口(如 TXRU)在某一方向(比如垂直)的个数。
    • N2:表示在另一方向(水平方向)的个数。
  • 后面的三重 for 循环就是在遍历 p, n1Id, n2Id,组合成 portId,即「第 p 个集合里第 (n1Id,n2Id) 个端口」。

matlab
[K,L]=deal(channel.bs.txru_size(1),channel.bs.txru_size(2));
  • channel.bs.txru_size = [K, L]
    • K:一个 TXRU 在垂直方向控制的天线数。
    • L:一个 TXRU 在水平方向控制的天线数。
  • 所以 一个 TXRU 对应一个 K×L 的天线块,这正是混合波束成形中常见的“子阵列结构”。

matlab
if K*L==1
  • 判断每个 TXRU 包含多少个物理天线:
    • 如果 K*L == 1,说明一个 TXRU 只管 一根天线,也就是非混合、全数字架构(每端口对应一根天线)。
    • 否则 K*L > 1,就是 一个 TXRU 控制多个天线,需要设计 analog precoding(波束赋形)。

1)K*L == 1 的简单情况

matlab
precodingValue=repmat(TxFreqSignal,1,txAntNum);
  • TxFreqSignal 大概是 [fftSize, Nsymbol, Nport]
  • repmat(...,1,txAntNum):在第 2 维保持不变,在第 3 维重复 txAntNum 次。
    • 注意:这里第二个参数是 1,第三个参数是 txAntNum,对 3 维矩阵来说就是 [1, 1, txAntNum] 的复制。
  • 逻辑上是:如果“一个端口=一根天线”,那就直接把所有端口信号复制到足够多的天线上(通常这意味着 Nport = 1,否则这里会有点奇怪,不过从代码风格看是「简化情况:所有天线发同一端口信号」)。
matlab
precodingValue=reshape(precodingValue,fftSize,Nsymbol,txAntNum);
  • 把复制后的矩阵 reshape 成 [fftSize, Nsymbol, txAntNum],也就是:
    • 每个子载波、每个 OFDM 符号,对应 txAntNum 根天线的信号值。
  • 然后 if 分支结束。

2)K*L > 1 的混合波束成形情况

matlab
else
  • 进入 “每个 TXRU 控制 K×L 根物理天线,需要设计模拟波束形成向量” 的路径。

matlab
theta=channel.targetAngle(2); phi=channel.targetAngle(1);
  • channel.targetAngle 一般是 [phi, theta]
    • phi:方位角(azimuth)。
    • theta:仰角/elevation。
  • 这里按 [phi, theta] 顺序存的,所以:
    • phi = channel.targetAngle(1);
    • theta = channel.targetAngle(2);

matlab
dv=channel.bs.spacing(2); dh=channel.bs.spacing(1);
  • channel.bs.spacing 存的是天线间距(单位通常是相对于波长 λ 的归一化间距,比如 0.5 表示 λ/2):
    • dh:水平相邻天线间距。
    • dv:垂直相邻天线间距。

matlab
wk=exp(-1j*2*pi*([0:K-1])*dv*cos(theta))/sqrt(K); % according to TR36.897 5.2.2
  • 生成 垂直方向(K 元素) 的阵列方向向量 wk
    • [0:K-1]:垂直方向第 k 根天线的索引。
    • ([0:K-1])*dv*cos(theta):每根天线相对相位差对应的「归一化距离」。
    • exp(-1j*2*pi*...):构造相位因子 ej2πd/λe^{-j 2 \pi d/\lambda}
    • /sqrt(K):归一化,使得 wk 的 2 范数为 1(功率归一)。
  • 注释里说 “according to TR36.897 5.2.2”,说明这个波束方向向量是按 3GPP TR 36.897 里阵列响应的定义来的。

物理意义:对 K 根竖直方向的天线,构造一个指向角 theta 的波束方向向量。


matlab
wl=exp(-1j*2*pi*([0:L-1])*dh*sin(phi))/sqrt(L);
  • 类似地,生成 水平方向(L 元素) 的阵列方向向量 wl
    • [0:L-1]:水平方向天线索引。
    • dh*sin(phi):与方位角有关的投影。
    • 最终得到一个长度为 L 的复向量,表示水平方向的波束 steering vector。

matlab
W=kron(wl,wk);
  • kron(wl, wk):对 wl(1×L)与 wk(1×K)做 Kronecker 积,得到长度 K*L 的向量 W
  • 结果相当于把二维阵列(K×L)的 steering vector 展平到一维:
    • 先在竖直方向应用 wk,再在水平方向用 wl,形成 K×L 的二维波束,然后按某种顺序展平。

物理意义:这是一个 子阵列(K×L 天线)整体的模拟预编码向量,给这个 TXRU 用来生成指向 [phi, theta] 的波束。


matlab
antId=reshape([1:M*N*Mg*Ng],M*Mg,N*Ng);
  • 创建一个天线编号矩阵 antId
    • [1:M*N*Mg*Ng]:从 1 到总天线数的向量。
    • reshape(..., M*Mg, N*Ng):把它 reshape 成一个 (M*Mg) × (N*Ng) 的矩阵。
  • 这样就构成了一个二维索引表
    • 行方向:垂直 共 M*Mg 个“格子”(Mg 个 panel,每个 panel M 根天线)。
    • 列方向:水平 共 N*Ng 个“格子”。

后面会从这个矩阵中切出 K×L 的块,对应某一个 TXRU 控制的那一小块天线。


matlab
precodingValue=zeros(size(TxFreqSignal,1),size(TxFreqSignal,2),K*L*size(TxFreqSignal,3));
  • precodingValue 预分配空间:
    • 第 1 维:size(TxFreqSignal,1)fftSize
    • 第 2 维:size(TxFreqSignal,2)Nsymbol
    • 第 3 维:K*L*size(TxFreqSignal,3)
  • 如果 TxFreqSignal 第 3 维是 Nport,那么这里第 3 维是 K*L*Nport,也就是“每个端口展开到 K*L 个天线”。

注意:后面通过 antId_txru 索引会把这些 KLNport 个“天线”正确填进实际的天线位置(txAntNum 一般应该等于 KLNport 或再乘以 P 等)。


三重 for 循环:对每个 TXRU 生成 analog precoding 并映射到物理天线

matlab
for p=1:P for n1Id=1:N1 for n2Id=1:N2
  • 外层 for p=1:P:遍历极化 / 面板组 / 端口组等第 3 个维度。
  • 中间 for n1Id=1:N1:TXRU 在某一方向(比如垂直)的位置索引。
  • 内层 for n2Id=1:N2:TXRU 在另一方向(比如水平)的位置索引。
  • 三个循环合起来遍历所有端口 (p, n1Id, n2Id)

matlab
portId=N2*(n1Id-1)+n2Id+(p-1)*N1*N2;
  • 这行是把 (p, n1Id, n2Id) 映射到一个线性端口索引 portId
    • 对固定 pn1Idn2Id 按行优先排成 1...N1*N2
    • +(p-1)*N1*N2:不同 p 的一组端口整体后移一个块,避免冲突。
  • 所以 portId 范围:1P*N1*N2,应该和 TxFreqSignal(:,:,portId) 的第三维对应。

matlab
data=TxFreqSignal(:,:,portId);
  • 取出当前端口的频域信号:
    • data 尺寸为 [fftSize, Nsymbol],即某个端口在所有子载波、符号上的复信号。

matlab
analogValue=kron(data,W);
  • 用 Kronecker 积把端口信号和子阵列方向向量 W 结合:
    • datafftSize × Nsymbol
    • W1 × (K*L)
    • kron(data,W) 的尺寸为 fftSize × (Nsymbol * K*L)
  • 物理意义:
    • 对每一个频点/符号上的信号 data(k, n),把它乘上 W 这个长度为 K*L 的模拟权值向量;
    • 展平写成一个大矩阵,后面再 reshape。

matlab
antId_txru=antId(K*(n2Id-1)+1:n2Id*K,L*(n1Id-1)+1:n1Id*L)+(p-1)*M*N;

这句是整个代码里最关键的索引操作之一,慢慢分解:

  1. antId(K*(n2Id-1)+1:n2Id*K , L*(n1Id-1)+1:n1Id*L)

    • antId 这个 (M*Mg) × (N*Ng) 矩阵中,取出一个大小为 K × L 的子矩阵:
      • 行:K*(n2Id-1)+1 : n2Id*K
      • 列:L*(n1Id-1)+1 : n1Id*L
    • 这个子矩阵,恰好对应 当前 TXRU 所控制的那一片 K×L 的物理天线
  2. +(p-1)*M*N

    • 对不同的 p 做整体偏移:每个 p 的天线索引都加上一个 M*N 的常数。
    • 说明在天线编号上,p 这个维度相当于“再叠了一层 M×N 的天线块”。

最终:

  • antId_txru 是一个 K×L 的矩阵,里面存的是 物理天线编号(考虑了 (n1Id,n2Id) 的 TXRU 位置,以及第 p 层的偏移)。

matlab
analogValue=reshape(analogValue,size(TxFreqSignal,1),K*L,size(TxFreqSignal,2));
  • 之前 analogValuefftSize × (Nsymbol*K*L)
  • 改成:[fftSize, K*L, Nsymbol]
    • 第 1 维:子载波;
    • 第 2 维:当前 TXRU 所控制的 K*L 根天线;
    • 第 3 维:OFDM 符号索引。

matlab
precodingValue(:,:,antId_txru(:))=permute(analogValue,[1,3,2]);
  • antId_txru(:):把 K×L 的天线编号矩阵展平为一维向量,长度 K*L
  • permute(analogValue,[1,3,2])
    • analogValue 尺寸 [fftSize, K*L, Nsymbol][fftSize, Nsymbol, K*L]
    • 这样就对齐了 precodingValue 的维度顺序 [子载波, 符号, 天线]
  • 赋值:
    • precodingValue(:,:,antId_txru(:)) = ...
    • 把当前端口 + 当前 TXRU 对应的 K*L 根天线上的信号,写到 precodingValue 的这些具体天线索引里。

物理意义:
对于当前端口 (p, n1Id, n2Id),我们把它的信号按波束向量 W 拆分到 K*L 根子阵列天线上,然后再根据 antId_txru 把这些天线映射到整个大阵列的对应天线编号上,实现“端口 → TXRU → 子阵列天线”的模拟预编码。


matlab
end end end end
  • 三重 for 循环结束,所有 (p, n1Id, n2Id) 的端口都已经完成 analog precoding,并写入 precodingValue 的相应天线上。
  • if ... else ... end 结束。
  • 函数返回 precodingValue

总结一下这段函数的整体逻辑(方便你在脑子里有一张图)

  1. 读取结构参数

    • 天线阵列结构:Mg, Ng, M, N, P
    • TXRU 尺寸:K, L
    • 端口排布:N1, N2
    • 目标指向角:phi, theta
    • 天线间距:dh, dv
  2. 如果 K*L == 1(“一端口一根天线”):

    • 直接把 TxFreqSignal 复制到每根天线上,不做复杂模拟预编码。
  3. 如果 K*L > 1(混合波束成形):

    • 根据 [phi, theta]dh, dv 生成垂直 wk、水平 wl 的 steering vector;
    • Kronecker 积得到子阵列的 beamforming 向量 W(长度 K*L);
    • 构造一个天线编号矩阵 antId,用来从大阵列里切出每个 TXRU 控制的 K×L 小块;
    • 对每个端口 (p, n1Id, n2Id)
      1. TxFreqSignal(:,:,portId) 取出该端口信号;
      2. kron(data, W) 把端口信号扩展到该 TXRU 下的 K×L 根天线;
      3. antId 取出对应的天线编号 antId_txru,加上 (p-1)*M*N 做偏移;
      4. analogValue 写入 precodingValue(:,:,这些天线索引)
    • 最终 precodingValue 就是“按目标方向做了模拟波束成形、并已经按物理天线展开”的频域信号矩阵。

如果你愿意,我可以下一步帮你画一张阵列/端口/TXRU 的示意图,或者根据你现在仿真的参数(Mg,Ng,M,N,K,L,N1,N2 等)把每个维度的大小和对应关系列成一个表,这样用起来会更清晰。

问题

function timeChannel = timeChannelGen(sensing,channel)

coeff=channel.CDL.coefficientTimeIndependent; % channel coefficient
dopplerf=channel.CDL.coefficientTimeDependent; % dopler parameter
fftSize=sensing.fftSize; % fft size
frameId=sensing.frameId; % frame index
Nsymbol=sensing.Nsymbol; % number of symbol
frameLen=sensing.frameLen; % data frame length, in ms
nonInteger = 1; % Whether to add a decimal delay
clusterNum=size(coeff,1); % number of cluster
rxAntNum=size(coeff,3); % number of receiving antennas
txAntNum=size(coeff,4); % number of transmitting antennas
delayTap = channel.tapDelay_dt; % Integer multiple delay
if sensing.fastchannel==0
dealtaT = [0:Nsymbol-1]frameLen1e-3/Nsymbol +(frameId-1)* frameLen1e-3; % time domain symbol time interval
else
dealtaT=(sensing.tpattern-1)sensing.Tsym + (frameId-1) frameLen
1e-3;
end

timeChannel=zeros(fftSize, Nsymbol, rxAntNum, txAntNum);
for rxAntId=1:rxAntNum
for txAntId=1:txAntNum
for tId=1:length(dealtaT)
channelTemp = zeros(fftSize, 1);
for cId = 1:clusterNum
delayH = zeros(fftSize,1);
delayH(delayTap(cId) + 1,1)=sum(coeff(cId,:,rxAntId,txAntId).exp(1jdopplerf(cId,:,rxAntId,txAntId)dealtaT(tId))); % add doppler affect
if nonInteger == 1
temp = fft(delayH,fftSize) .exp(-1j2
pichannel.deltaDelay(cId)/fftSizecircshift([0:fftSize-1]',fftSize/2)); % add a decimal delay
else
temp = fft(delayH,fftSize);
end
channelTemp = ifft(temp) + channelTemp;
end
timeChannel(:,tId,rxAntId,txAntId)=channelTemp;
end
end

end

思考

Thought for 18s

回答

我还是按从上到下、逐句+物理含义给你拆开讲一下这段信道生成代码。


matlab
function timeChannel = timeChannelGen(sensing,channel)
  • 定义函数 timeChannelGen,输入:
    • sensing:系统/仿真配置(FFT、大框架长度、符号数、tpattern 等)。
    • channel:信道相关参数(CDL 系数、多普勒、时延等)。
  • 输出:
    • timeChannel:一个随时间变化的 离散时域 CIR(channel impulse response),尺寸后面会看到。

matlab
coeff=channel.CDL.coefficientTimeIndependent; % channel coefficient dopplerf=channel.CDL.coefficientTimeDependent; % dopler parameter
  • coeff:CDL 信道的“时间不变系数”,一般是:
    • 维度大概是 [clusterNum, rayPerCluster, rxAntNum, txAntNum]
    • 代表每个簇、每条射线在 t=0 时刻 的复幅度(包括 pathloss、阴影衰落、阵列响应、极化之类)。
  • dopplerf
    • 对应每条射线的 多普勒频移(或与时间相关的参数),维度和 coeff 对应。
    • 等下会看到用 exp(1j*dopplerf * t) 这种形式给不同时间加相位旋转。

直观:coeff 是“起始信道”,dopplerf 告诉你每条径随时间怎么转相位(多普勒)。


matlab
fftSize=sensing.fftSize; % fft size frameId=sensing.frameId; % frame index Nsymbol=sensing.Nsymbol; % number of symbol frameLen=sensing.frameLen; % data frame length, in ms
  • sensing 里读出:
    • fftSize:OFDM 的 FFT 点数,也是离散 CIR 的长度(这里用 fftSize 点去表示 CIR)。
    • frameId:第几个帧(从 1 开始计)。
    • Nsymbol:这个帧/时隙里你要模拟的符号数。
    • frameLen:一帧的长度,单位是 ms(后面×1e-3 变成秒)。

matlab
nonInteger = 1; % Whether to add a decimal delay
  • 标志位:是否考虑非整数采样时延(小数时延)。
    • 1:加上小数时延(fractional delay),用频域相位来实现。
    • 0:只用整数采样点位置的时延。

matlab
clusterNum=size(coeff,1); % number of cluster rxAntNum=size(coeff,3); % number of receiving antennas txAntNum=size(coeff,4); % number of transmitting antennas
  • 根据 coeff 的尺寸提取:
    • clusterNum:簇数(第 1 维)。
    • rxAntNum:接收天线数(第 3 维)。
    • txAntNum:发射天线数(第 4 维)。
  • 第 2 维一般是“每簇的射线数”,后面用 sum(coeff(cId,:,rx,tx).* …) 把一个簇内所有射线相加。

matlab
delayTap = channel.tapDelay_dt; % Integer multiple delay
  • delayTap:每个簇对应的整数采样时延(单位:采样点个数)。
    • 应该是长度 clusterNum 的向量,第 c 个元素是第 c 个 cluster 的整数 tap 索引。
    • 后面会用 delayH(delayTap(cId)+1) 置 1 个非零 tap。

matlab
if sensing.fastchannel==0 dealtaT = [0:Nsymbol-1]*frameLen*1e-3/Nsymbol +(frameId-1)* frameLen*1e-3; % time domain symbol time interval else dealtaT=(sensing.tpattern-1)*sensing.Tsym + (frameId-1)* frameLen*1e-3; end

这里是在生成每个符号对应的实际时间点 dealtaT(应该是 “deltaT” 拼错了)。

  • 情况 1:fastchannel == 0

    matlab
    dealtaT = [0:Nsymbol-1]*frameLen*1e-3/Nsymbol +(frameId-1)* frameLen*1e-3;
    • [0:Nsymbol-1]:第 0、1、…、Nsymbol-1 个符号。
    • frameLen*1e-3/Nsymbol:一帧内符号之间的时间间隔(秒)。
    • [0:Nsymbol-1]*...:从这一帧开始时刻起,往后每个符号中心的时间。
    • +(frameId-1)*frameLen*1e-3:第 frameId 帧的起始时间偏移。
    • 结果:dealtaT 是长度为 Nsymbol 的向量,第 tId 项是当前帧中第 tId 个符号的绝对时间(秒)
  • 情况 2:fastchannel ~= 0

    matlab
    dealtaT=(sensing.tpattern-1)*sensing.Tsym + (frameId-1)* frameLen*1e-3;
    • sensing.tpattern:某种符号时隙模式的索引(比如只在一部分符号上取信道)。
    • Tsym:每个符号时间(秒)。
    • (tpattern-1)*Tsym:把每个符号的时间位置扩展成一个向量。
    • 再加上帧偏移 (frameId-1)*frameLen*1e-3
    • 结果同样是一个包含各个观测时间点的向量,不过符号位置不一定连续(可能是子集)。

总之:dealtaT(tId) 就是第 tId 个 time sample 对应的真实时间,供 Doppler 相位用。


matlab
timeChannel=zeros(fftSize, Nsymbol, rxAntNum, txAntNum);
  • 预分配输出矩阵 timeChannel,尺寸:
    • 第 1 维:fftSize → 表示 CIR 的 tap 索引(0 ~ fftSize-1)。
    • 第 2 维:Nsymbol → 不同时间(符号)位置。
    • 第 3 维:rxAntNum
    • 第 4 维:txAntNum
  • 所以 timeChannel(k, tId, rx, tx):第 tId 个时间点、从 tx 到 rx 的 第 k 个采样点的时域信道系数

四重循环:遍历每对天线 & 每个时间点

matlab
for rxAntId=1:rxAntNum for txAntId=1:txAntNum for tId=1:length(dealtaT) channelTemp = zeros(fftSize, 1);
  • 外三层循环:
    • rxAntId:当前接收天线;
    • txAntId:当前发射天线;
    • tId:当前时间点(符号时刻)。
  • 对每对 (rxAntId, txAntId) 和每个 tId,我们要生成一个长度 fftSize 的 CIR:
    • 初始化 channelTemp 为全 0 列向量,用于累加所有簇的贡献

matlab
for cId = 1:clusterNum delayH = zeros(fftSize,1);
  • 内层循环:遍历所有 clusterNum 个簇。
  • 对每个簇:
    • 先新建 delayH,长度为 fftSize 的全零向量,表示“该簇在当前时间点的 CIR”。

matlab
delayH(delayTap(cId) + 1,1)=sum(coeff(cId,:,rxAntId,txAntId).*exp(1j*dopplerf(cId,:,rxAntId,txAntId)*dealtaT(tId))); % add doppler affect

这一句非常关键,拆开看:

  1. coeff(cId,:,rxAntId,txAntId)

    • 取第 cId 个 cluster、所有射线(第二维 :)、当前 (rxAntId, txAntId) 对应的 复幅度,是一个行向量(射线数)。
  2. dopplerf(cId,:,rxAntId,txAntId)

    • 相同位置的 Doppler 频率,单位一般是 Hz 或 rad/s(这里直接在指数里乘以 t,看上去像 Hz)。
  3. exp(1j*dopplerf(...)*dealtaT(tId))

    • 对每条射线,加上 e^{j 2π f_d t} 类似的时间相关相位(这里只缺个 2π,可能 dopplerf 已经包含 2π)。
  4. coeff .* exp(...)

    • 把每条射线的初始系数乘上对应的时间相位,得到该时间点下每条射线的瞬时系数
  5. sum(...)

    • 把一个簇内部所有射线的贡献相加,变成该簇在当前时间点的 总复系数
  6. delayTap(cId) + 1

    • 找到该簇的整数时延 tap 位置(MATLAB 下标从 1 开始,所以 +1)。

所以整句意思就是:

对 cluster cId:
在当前 (rxAnt, tx, t) 下,把簇内所有射线根据 Doppler 旋转后相加,形成一个复系数,然后放到 delayH 的第 delayTap(cId)+1 个 tap 上,其余位置为 0。


matlab
if nonInteger == 1 temp = fft(delayH,fftSize) .*exp(-1j*2*pi*channel.deltaDelay(cId)/fftSize*circshift([0:fftSize-1]',fftSize/2)); % add a decimal delay else temp = fft(delayH,fftSize); end

这里是在处理 小数时延(fractional delay)

  • 先对 delayH 做 FFT:

    matlab
    fft(delayH, fftSize)
    • 得到频域的信道响应 H(f)(实际上是离散频率点上的值)。
  • 如果 nonInteger == 1,则再乘一个频域相位项:

    matlab
    exp(-1j*2*pi*channel.deltaDelay(cId)/fftSize*circshift([0:fftSize-1]',fftSize/2))

    拆开讲:

    • [0:fftSize-1]':列向量,对应频率索引 k = 0,1,...,fftSize-1
    • circshift(..., fftSize/2):把频率索引平移 fftSize/2,相当于把 DC 移到中间,使频率索引在 [-fftSize/2, ..., fftSize/2-1] 对称。
    • channel.deltaDelay(cId):这个簇的小数时延部分(单位:采样间隔)。
    • 整个指数项相当于: ej2πΔτNk e^{-j 2 \pi \frac{\Delta\tau}{N} k} 对每个频率点 k 乘上一个线性相位斜率,对应时域的小数延迟

数学上:时移 Δτ\Delta \tau 对应频域乘以 ej2πfΔτe^{-j 2\pi f \Delta\tau},这里 f 用归一化频率 k/N 表示,所以用 2π * deltaDelay / fftSize * k 形式。

  • 如果 nonInteger == 0
    • 不加小数延时,直接 temp = fft(delayH, fftSize);

结果 temp考虑了多普勒+(可选)小数时延后的频域响应


matlab
channelTemp = ifft(temp) + channelTemp;
  • temp 做 IFFT:
    • ifft(temp) → 回到时域上的 CIR,但已经包括了:
      • 该簇的整数 tap;
      • 多普勒带来的时间相关相位;
      • 非整数小数时延(若开启)。
  • 然后 + channelTemp
    • 把当前簇的 CIR 累加到所有簇的总 CIR 上。

所以 channelTemp 最终是“对所有簇累加后的、当前 (rx,tx,t) 时刻的整条 CIR”。


matlab
end timeChannel(:,tId,rxAntId,txAntId)=channelTemp;
  • 内层 cId 循环结束后:
    • channelTemp 已经包含了所有簇对当前 (rxAntId,txAntId,tId) 的贡献。
  • 把它写到输出:
    • timeChannel(:, tId, rxAntId, txAntId) = channelTemp;
  • 于是:
    • 第 1 维:tap 索引(0 ~ fftSize-1);
    • 第 2 维:时间索引(符号 tId);
    • 第 3 维:rx 天线;
    • 第 4 维:tx 天线。

matlab
end end end
  • 三层时间/天线循环结束后,timeChannel 填满,函数结束返回。

整体功能用一句话概括

给定 CDL 信道的簇/射线系数、各射线的 Doppler、每簇整数+小数时延,按时间 tId 对每对 (rx, tx) 生成一个长度为 fftSize 的、随时间变化的离散时域信道脉冲响应(CIR),并存入 timeChannel(k, t, rx, tx)


再用一张“维度/物理含义小抄”总结一下关键量

  • coeff(cId, ray, rx, tx)
    第 cId 个簇、第 ray 射线、rx-tx 对,在 t=0 时刻的复系数。

  • dopplerf(cId, ray, rx, tx)
    同一射线的 Doppler 参数,用来算 ejωte^{j \omega t} 相位旋转。

  • delayTap(cId)
    第 cId 个簇的整数采样时延(单位:采样点个数)。

  • channel.deltaDelay(cId)
    第 cId 个簇的小数采样时延(0~1 之类),用于频域线性相位。

  • dealtaT(tId)
    第 tId 个符号对应的真实时间(秒),用于 Doppler 相位。

  • timeChannel(k, tId, rx, tx)
    时间 tId 下,从 tx 到 rx 的 CIR 第 k 个 tap 的值。


如果你愿意,我可以帮你把这段代码“等价”改写成更易懂的伪代码 / 带注释 MATLAB 版本,或者结合一个具体 CDL 配置(比如 CDL-A、2×2 MIMO)画一下:

  • 某个时间点下的 PDP;
  • 不同时间点下某个 tap 的自相关(多普勒效应)。

问题

这个代码里有放我们目标导致的时延和多普勒么

思考

Answering target-related delay and Doppler question in Chinese

The code includes Doppler and delay effects, but whether these relate to the target depends on how the channel's CDL is defined. Specifically, Doppler is applied via dopplerf, and delay through tapDelay_dt and deltaDelay, but no explicit "target" variable exists. To account for target delay, encode it into tapDelay_dt/deltaDelay upstream.

Thought for 8s

回答

有的,但这段代码本身只是在“应用”时延和多普勒,不负责“根据目标距离/速度去算”它们

可以分两块看:


1. 时延是不是放进来了?

① 整数采样时延:delayTap

matlab
delayTap = channel.tapDelay_dt; % Integer multiple delay ... delayH(delayTap(cId) + 1,1)= ...
  • 对第 cId 个簇,它在 CIR 里的主 tap 位置delayTap(cId)(采样点数),在代码里表现为:
    • delayH(delayTap(cId) + 1) 这个 tap 置为非零,其余 tap 为 0。
  • 如果在你构造 channel.tapDelay_dt 的时候,已经把目标距离对应的往返时延换算成采样点数(取整),那这个“目标造成的时延”就已经体现在这里了。

② 小数采样时延:channel.deltaDelay

matlab
if nonInteger == 1 temp = fft(delayH,fftSize) .* ... exp(-1j*2*pi*channel.deltaDelay(cId)/fftSize * circshift([0:fftSize-1]',fftSize/2)); else temp = fft(delayH,fftSize); end
  • channel.deltaDelay(cId) 是第 cId 个簇的小数部分时延(0~1 之类,单位是“采样间隔”)。
  • 在频域乘上一个线性相位: ej2πΔτNk e^{-j 2\pi \frac{\Delta\tau}{N} k} 等价于时域平移 Δτ\Delta\tau 个采样
  • 所以,总时延 = delayTap(cId)(整数) + deltaDelay(cId)(小数)
    你如果在上游根据目标距离算好了「整数 + 小数延时」塞到 tapDelay_dtdeltaDelay 中,那么这段代码就把它完整用上了。

2. 多普勒是不是放进来了?

是的,多普勒就是在下面这句里加的:

matlab
delayH(delayTap(cId) + 1,1) = ... sum( coeff(cId,:,rxAntId,txAntId) .* ... exp( 1j * dopplerf(cId,:,rxAntId,txAntId) * dealtaT(tId) ) );
  • dopplerf(cId,:,rxAntId,txAntId):每条射线对应的 多普勒参数
  • dealtaT(tId):当前符号的绝对时间
  • exp(1j * dopplerf * t):就是给每条射线乘上随时间旋转的相位因子(多普勒效应);
  • 再对同一簇内所有射线 sum(...),得到该簇在当前时间的总系数。

也就是说:

你在 channel.CDL.coefficientTimeIndependent 里面放的是 t=0 时刻的初始系数
channel.CDL.coefficientTimeDependent 里放的是 多普勒频率(一般跟目标径向速度有关)
这段代码在每个时间 t = dealtaT(tId) 时,用 exp(j*dopplerf*t) 把多普勒引起的相位变化加进来。


3. 关键点:这段代码不计算目标的时延/多普勒,只是使用

所以总结一句话:

  • 时延:由 delayTap(cId) + channel.deltaDelay(cId) 决定;
  • 多普勒:由 dopplerf(cId,:,rx,tx) 决定,通过 exp(j*dopplerf*t) 体现;
  • 但是,这些量是不是“目标导致的那一部分”,取决于你在构造 channel 结构体时有没有把目标的距离和速度映射进去

👉 换句话说:

  • 如果你上游在生成 channel.tapDelay_dt / channel.deltaDelaydopplerf 的时候,已经按目标的 R、v算好了,这段 timeChannelGen 的代码就确实在把目标的时延和多普勒注入到信道里
  • 如果上游只是用 3GPP 标准 CDL 的随机参数,那这里加进去的是统计意义上的多径时延和多普勒,而不是你雷达目标的「几米/几百米」和「几 m/s」那种显式目标。

如果你愿意,我可以帮你推一推:比如目标距离 R、速度 v,采样频率 Fs、载频 fc,应该如何算出:

  • delayTapdeltaDelay
  • dopplerf

然后直接对接到这个函数里。

分享这个问答