你现在是无线通信方向的专家。正在写一个OTFS和AFDM的ISAC感知的代码。现在我们已经写好了OT...

作成日: 2026年1月9日

使用モデル GPT-5.2 Thinking by Chat01

質問

你现在是无线通信方向的专家。正在写一个OTFS和AFDM的ISAC感知的代码。现在我们已经写好了OTFS sensing的程序,相关需要调用的参数都已经给到了。现在需要你在这个基础上,我们增加一个可以仿真AFDM的代码。现在这个OTFS也是在OFDM的框架上来写的。我们现在一步一步来,先来理解一下下面的代码:function [Range,Velocity,Angle]=offsSensing(Hest,sensing)
Angle=0;
targetNum=sensing.targetNum;
fc=sensing.fc;
SCS=sensing.SCS;
slotT=sensing.frameLen*1e-3/sensing.sensingSymbolNum;
sensingSymbolNum=sensing.sensingSymbolNum;
fdmax=1/slotT;
Nc=sensing.Nc;
Nzc=sensing.Nzc;
TxSignalDD=sensing.TxSignalDD;
overSamplNumf=sensing.overSamplNumf;
overSamplNumt=sensing.overSamplNumt;
overSamplFlag=sensing.overSamplFlag;
freqSpace=sensing.freqSpace;
ZcSeq=sensing.ZcSeq;

if sensing.otfsSensingFlag==1
% SFFT
RxSignalTF=Hest;
SensingNsymbol=size(Hest,2);
RxSignalDD=ifft(RxSignalTF).'.*sqrt(Nc/SensingNsymbol);
RxRsSignal=RxSignalDD(1:Nzc,:);

text
% Receive reference signal oersampling RxRsSignalTF=ifft(fft(RxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); RxOverSamplSignalDD=ifft(fft(RxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol); % Transmit reference signal oversampling TxRsSignal=repmat(ZcSeq,1,SensingNsymbol); TxRsSignalTF=ifft(fft(TxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); TxOverSamplSignalDD=ifft(fft(TxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol); % reconstruct local oversampling signal OverSampZcSeq=ifft(fft(ZcSeq,overSamplNumf)); OverSampZcSeq=repmat(OverSampZcSeq,1,overSamplNumt); % reconstruct receive oversampling signal OverSampTF=ifft(fft(TxSignalDD(1:Nzc,:)).'.'); OverSampDD=fft(ifft(OverSampTF,overSamplNumf).',overSamplNumt).'; % channel estimate if overSamplFlag == 0 EstHDD=ifft(conj(fft(TxRsSignal)).*fft(RxRsSignal))/(sqrt(SensingNsymbol)*Nzc); else EstHDD=ifft(conj(fft(TxOverSamplSignalDD)).*fft(RxOverSamplSignalDD))/(sqrt(SensingNsymbol)*Nzc); end peakValue=max(EstHDD(:)); EstHDD=EstHDD/peakValue; if overSampFlag == 0 deltaD = 3e8/(SCS*Nzc); len=sensingNsymbol; deltafd = fdmax/len; else deltaD = 3e8/(SCS*overSampNumf); len=overSamplNumt; deltafd = fdmax/len; end initH=fftshift(abs(EstHDD),2); [lenf,lent]=size(initH); % mesh(-lent/2:lent/2-1]*deltafd,[0:lenf-1]*deltaD,(initH)); % xlabel('Doppler [Hz]'); % ylabel('Range [m]') % zlabel('Normalized Power') % ylim([0,2000]) % xlim([-15000,15000]) % set(gcf,'Color','white'); % colorbar residualH=initH; distanceEst=zeros(targetNum,1); dopplerEst=zeros(targetNum,1); velocityEst=zeros(targetNum,1); for targetInd=1:targetNum [~,peakInd]=max(residualH(:)); [row,col]=ind2sub(size(residualH),peakInd); range=(row-1)*deltaD; doppler=(col-1-lenf/2)*deltafd; rowSet=[row]; colSet=[col]; rowSet(rowSet==0)=[]; rowSet(rowSet>size(initH,1))=[]; colSet(colSet==0)=[]; colSet(colSet>size(initH,2))=[]; for row=rowSet for col=colSet if overSamplFlag==0 reconSignal=circshift(TxSignalDD(1:Nzc,:),row-1,1); reconSignal=circshift(reconSignal,col-1,2); reconH=ifft(conj(fft(TxRsSignal)).*fft(reconSignal))/(sqrt(SensingNsymbol)*Nzc); else reconSignal=circshift(OverSampDD,row-1,1); reconSignal=circshift(reconSignal,col-1,2); reconH=ifft(conj(fft(OverSampZcSeq)).*fft(reconSignal))/(sqrt(SensingNsymbol)*Nzc); end reconH=abs(reconH/max(reconH(:))); factor=initH(row,col); residualH=residualH-factor*reconH; residualH(residualH<0)=0; end end distanceEst(targetInd,1)=range; dopplerEst(targetInd,1)=doppler; velocityEst(targetInd,1)=dopplerEst(targetInd,1)*3e8/fc/2; Range=distanceEst; Velocity=velocityEst; end

else
channelHEst=Hest;
if overSamplFlag == 0
[lenf,lent]=size(channelHEst);
else
lenf = overSamplNumf;
lent = overSamplNumt;
end
deltaD = 3e8/(SCSfreqSpacelenf);
deltafd = fdmax/lent;

text
EstHTF=abs(fft(ifft(channelHEst,lenf).',lent).')*sqrt(lenf/lent); peakValue=max(EstHTF(:)); EstHTF=EstHTF/peakValue; initH=fftshift(abs(EstHTF),2); residualH=initH; distanceEst=zeros(targetNum,1); dopplerEst=zeros(targetNum,1); velocityEst=zeros(targetNum,1); [lenf,lent]=size(initH); % mesh(-lent/2:lent/2-1]*deltafd,[0:lenf-1]*deltaD,(initH)); % xlabel('Doppler [Hz]'); % ylabel('Range [m]') % zlabel('Normalized amplitude') % ylim([0,800]) % xlim([-10000,10000]) % set(gcf,'Color','white'); % colorbar for targetInd=1:targetNum [~,peakInd]=max(residualH(:)); [row,col]=ind2sub(size(residualH),peakInd); range=(row-1)*deltaD; doppler=(col-1-lent/2)*deltafd; c=3e8; fR=exp(-1j*2*pi*[0:Nzc-1]*SCS*range/c); fV=exp(1j*2*pi*[0:sensingSymbolNum-1]*(col-1)*deltafd*slotT); recon=fR.'*fV; reconH=fft(ifft(recon,lenf).',lent).'*sqrt(lenf/lent); reconH=abs(reconH/max(reconH(:))); factor=initH(row,col); residualH=residualH-factor*reconH; distanceEst(targetInd,1)=range; dopplerEst(targetInd,1)=doppler; velocityEst(targetInd,1)=dopplerEst(targetInd,1)*3e8/fc/2; Range=distanceEst; Velocity=velocityEst; end

end
end

思考

Explaining MATLAB function with an eye on bugs and details

User asks for a step-by-step explanation of the MATLAB function offsSensing(Hest, sensing). The function involves OTFS sensing versus OFDM, dealing with oversampling and some variable inconsistencies like overSampFlag vs overSamplFlag—potential bug noted. I’ll explain the inputs, outputs, variables, modes, and processing steps, like the IFFT/FFT transformation process.

Explaining oversampling process and transformations

They begin by extracting the first Nzc rows of the reference signal and then perform oversampling by transforming into the TF domain. This includes an FFT over frequency and IFFT over time to improve Doppler and delay resolution. Afterward, a similar process is done on the transmit signal and for reconstructing the oversampled signal with a few transformations.

Channel estimation process and correlation

They're estimating the channel by applying circular correlation in the delay dimension for each Doppler bin, depending on whether oversampling is enabled. This involves doing element-wise multiplication of frequency-domain signals and applying IFFT for delay estimation. Afterward, the delay-Doppler channel is constructed, and resolutions for both range and Doppler are computed using known formulas.

Explaining iterative peak detection and channel reconstruction

The process shifts the Doppler axis and performs iterative peak detection to find targets, updating the channel estimate by removing sidelobe patterns. Once the maximum Doppler and range are found, a reconstruction for cancellation is performed using shifted signals. This resembles techniques like CLEAN or matching pursuit. In the non-OTFS case, the channel estimate is handled differently, applying transforms to obtain a range-Doppler representation.

Explaining channel estimation and correction

The process involves normalizing, FFT shifting, and performing peak detection for range and Doppler. The template reconstruction uses exponential vectors fR and fV. A bug is identified regarding Doppler bin indexing, and there are inconsistencies in key variable names. Oversampling is used to improve resolution, and the results update during target iteration. I'll explain inputs, oversampling, and the approach, proposing the next steps. Since the user prefers Chinese, I'll ensure the explanation is clear in that language.

Thought for 38s

回答

下面我按“这段代码到底在做什么—每一步为什么这么做—输出怎么来的”的顺序,把 offsSensing(Hest,sensing) 讲清楚。整体上它是在做 Range–Doppler(距离–多普勒)图 的生成 + 多目标峰值检测/消除(类似 CLEAN / Matching Pursuit),最后把 Doppler 换算成速度。Angle 目前恒置 0,相当于“角度维未实现”。


1) 输入/输出与总体流程

输入

  • Hest:你们前端已经估计出来的“接收二维信号/信道”的矩阵
    • sensing.otfsSensingFlag==1:代码把它当作 OTFS sensing 的 Rx TF-domain 矩阵(变量名 RxSignalTF=Hest
    • 否则:把它当作 OFDM 框架下的 TF 信道估计(变量名 channelHEst=Hest
  • sensing:结构体,包含载频、子载波间隔、帧结构、ZC 序列、过采样参数等

输出

  • Range:长度 targetNum 的距离估计(单位 m)
  • Velocity:长度 targetNum 的速度估计(单位 m/s)
  • Angle:恒为 0(占位)

总体做法(两条分支都类似)

  1. 生成/得到 Range–Doppler 图 initH(二维幅度图)
  2. 循环 targetNum 次:
    • 找当前最大峰 (row,col) → 转成 range,doppler
    • 构造一个“该目标的理论/重构响应” reconH
    • residualH = residualH - factor * reconH 做消除,继续找下一个目标

2) 关键参数的物理意义(代码开头那堆拆包)

  • fc:载频,用于 Doppler→速度: v=fdc2fcv = \frac{f_d c}{2 f_c}
  • SCS:子载波间隔(Hz)
  • slotT = frameLen*1e-3/sensingSymbolNum:一个 sensing symbol 对应的“符号时长”(这里你们把一帧毫秒长度平均到 sensingSymbolNum)
  • fdmax = 1/slotT:可分辨/可观测的 Doppler 周期范围相关(近似最大无模糊 Doppler 间隔的倒数尺度)
  • Nc:系统子载波数(或 OTFS 网格的频域点数)
  • Nzc:用于 sensing 的参考序列长度(ZC 序列长度,对应参与距离处理的有效频点数)
  • overSamplNumf / overSamplNumt:频向/时向过采样长度(本质是 零填充 FFT 长度,提高距离/多普勒栅格分辨率)
  • freqSpace:在非 OTFS 分支里用于距离分辨率的“频域抽取间隔”(例如导频每隔多少子载波放一个)
  • ZcSeq:发射参考序列

3) 分支 A:otfsSensingFlag==1(OTFS sensing 路径)

A1. “SFFT”相关:把 Rx 从 TF 变到 DD(或至少变成可做 DD 相关的形态)

matlab
RxSignalTF=Hest; SensingNsymbol=size(Hest,2); RxSignalDD=ifft(RxSignalTF).'.*sqrt(Nc/SensingNsymbol); RxRsSignal=RxSignalDD(1:Nzc,:);
  • SensingNsymbol 是 sensing 使用的符号数(列数)
  • ifft(RxSignalTF):沿第 1 维做 IFFT(MATLAB 默认对每列做),然后转置。你们这里的“DD”更像是把频维做 IFFT得到“delay-like”维度,再把时间维放到第二维。
  • RxRsSignal=... (1:Nzc,:):只取前 Nzc 个 delay bin(对应 ZC 长度)参与后续相关,等于“只用参考资源块”。

A2. 对 Rx/Tx 参考信号做二维过采样(提高栅格分辨率)

这一段的形式很像:先在某一维做 FFT 转域,再用指定 FFT 长度 overSamplNumf/overSamplNumt 做零填充,从而得到更细的 delay/Doppler 网格。

matlab
RxRsSignalTF=ifft(fft(RxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); RxOverSamplSignalDD=ifft(fft(RxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol); ... TxRsSignal=repmat(ZcSeq,1,SensingNsymbol); TxRsSignalTF=ifft(fft(TxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); TxOverSamplSignalDD=ifft(fft(TxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol);
  • TxRsSignal = repmat(ZcSeq,1,SensingNsymbol):每个 symbol 发相同 ZC
  • ...overSamplNumf..., overSamplNumt...:分别把两个维度的 FFT 长度拉长 → 过采样后的 DD 信号
  • 这些 sqrt(...) 是能量归一化,避免长度变化导致幅度缩放。

A3. 构造本地过采样序列/信号(用于后面“重构消除”)

matlab
OverSampZcSeq=ifft(fft(ZcSeq,overSamplNumf)); OverSampZcSeq=repmat(OverSampZcSeq,1,overSamplNumt); OverSampTF=ifft(fft(TxSignalDD(1:Nzc,:)).'.'); OverSampDD=fft(ifft(OverSampTF,overSamplNumf).',overSamplNumt).';
  • OverSampZcSeq:过采样版的 ZC(频向零填充)
  • OverSampDD:过采样版的发射 DD(你们用 TxSignalDD 的前 Nzc 行)

这两者用于后面 CLEAN:当检测到某个 (delay,doppler) 峰值时,构造该峰对应的“模板响应”,从 residualH 里减掉。

A4. 估计 DD 域“匹配滤波/相关输出”得到 Range–Doppler 图

matlab
if overSamplFlag == 0 EstHDD=ifft(conj(fft(TxRsSignal)).*fft(RxRsSignal))/(sqrt(SensingNsymbol)*Nzc); else EstHDD=ifft(conj(fft(TxOverSamplSignalDD)).*fft(RxOverSamplSignalDD))/(sqrt(SensingNsymbol)*Nzc); end peakValue=max(EstHDD(:)); EstHDD=EstHDD/peakValue; initH=fftshift(abs(EstHDD),2);
  • ifft(conj(FFT(tx)).*FFT(rx)):经典 FFT 快速实现的 循环相关/匹配滤波
  • 这里等效于:对每个 Doppler bin(每列)做 delay 相关,从而得到二维“delay–(something)”图,再对第 2 维 fftshift 把 Doppler 零频移到中间。

A5. 计算距离/多普勒分辨率(栅格间隔)

matlab
if overSampFlag == 0 deltaD = 3e8/(SCS*Nzc); len=sensingNsymbol; deltafd = fdmax/len; else deltaD = 3e8/(SCS*overSampNumf); len=overSamplNumt; deltafd = fdmax/len; end
  • 距离栅格:ΔRcΔfN\Delta R \approx \frac{c}{\Delta f \cdot N},这里 Δf = SCSNNzc 或过采样长度
  • Doppler 栅格:用 fdmax / (#doppler_bins) 这样的离散化(你们用 len = sensingNsymboloverSamplNumt

A6. 多目标检测 + CLEAN 消除

核心循环:

  1. 找最大峰
  2. row→rangecol→doppler
  3. 构造 reconH 并消除
matlab
[~,peakInd]=max(residualH(:)); [row,col]=ind2sub(size(residualH),peakInd); range=(row-1)*deltaD; doppler=(col-1-lenf/2)*deltafd; % (这里有个明显的维度问题,下面我会指出) reconSignal=circshift(TxSignalDD(1:Nzc,:),row-1,1); reconSignal=circshift(reconSignal,col-1,2); reconH=ifft(conj(fft(TxRsSignal)).*fft(reconSignal))/(sqrt(SensingNsymbol)*Nzc); ... residualH=residualH-factor*reconH;
  • circshift:把发射信号在 delay 维、Doppler 维做循环移位,模拟一个目标带来的时延和多普勒
  • reconH:对该移位信号再做一次匹配滤波,得到这个目标在 RD 图上的“点扩散/旁瓣形状”
  • residualH -= factor * reconH:把该目标贡献减掉,从而更容易找到下一个目标(典型 CLEAN)

4) 分支 B:otfsSensingFlag!=1(更“OFDM/TF 信道估计→RD 图”的路径)

B1. 决定 RD 图尺寸与分辨率

matlab
if overSamplFlag == 0 [lenf,lent]=size(channelHEst); else lenf = overSamplNumf; lent = overSamplNumt; end deltaD = 3e8/(SCS*freqSpace*lenf); deltafd = fdmax/lent;
  • 距离分辨率里多了 freqSpace:表示你在频域不是每个子载波都用(例如导频每隔 freqSpace 放一个),有效带宽缩小 → 距离分辨率变差

B2. 2D 变换得到 RD 幅度图

matlab
EstHTF=abs(fft(ifft(channelHEst,lenf).',lent).')*sqrt(lenf/lent); EstHTF=EstHTF/max(EstHTF(:)); initH=fftshift(abs(EstHTF),2);
  • ifft(...,lenf):频域 → delay(距离维)
  • fft(...,lent):慢时间 → Doppler
  • fftshift(...,2):把 Doppler 轴搬到中心

B3. 多目标检测 + 用解析模型重构并消除

matlab
range=(row-1)*deltaD; doppler=(col-1-lent/2)*deltafd; fR=exp(-1j*2*pi*[0:Nzc-1]*SCS*range/c); fV=exp(1j*2*pi*[0:sensingSymbolNum-1]*(col-1)*deltafd*slotT); recon=fR.'*fV; reconH=fft(ifft(recon,lenf).',lent).'*sqrt(lenf/lent); residualH=residualH-factor*abs(reconH/max(reconH(:)));
  • fR:距离引起的频域相位斜率(delay → 频域线性相位)
  • fV:Doppler 引起的慢时间相位旋转
  • recon = fR.' * fV:生成 TF 域的“单目标响应”
  • 再做同样的 TF→RD 变换得到 reconH,然后 CLEAN

5) 这段代码里有几个很关键的“潜在 bug/不一致”,建议你们先修掉再扩展 AFDM

(1) overSamplFlag vs overSampFlag 拼写不一致

你开头定义的是:

matlab
overSamplFlag=sensing.overSamplFlag;

但后面多处写成了 overSampFlag(少了 l)以及 overSampNumf(少了 l)。
MATLAB 这会直接报未定义变量,或者你们工作区恰好有同名变量导致“隐形错误”。

✅ 建议:全局统一为 overSamplFlag / overSamplNumf / overSamplNumt

(2) OTFS 分支里 Doppler 轴中心化用错维度

这里:

matlab
[lenf,lent]=size(initH); doppler=(col-1-lenf/2)*deltafd;
  • col 是第 2 维索引(Doppler 维),应该用 lent/2,不是 lenf/2

✅ 应为:

matlab
doppler = (col-1 - lent/2) * deltafd;

(3) OTFS 分支里的 rowSet/colSet 现在是“空壳”

matlab
rowSet=[row]; colSet=[col]; ... for row=rowSet for col=colSet ... end end

这其实等效于只跑一次。看起来你们原本想把邻域一起消(比如 3x3 或主瓣范围),但现在没实现。

✅ 若你们想增强消除效果,可以把 rowSet/colSet 扩成邻域(注意边界裁剪你们已经写了)。

(4) 非 OTFS 分支里 fRNzc 可能与 lenf 不一致

你们 RD 图的距离维长度是 lenf,但 fR 的频点用了 0:Nzc-1。如果 lenf ≠ Nzc(例如 overSampling 或 freqSpace 情况),模板长度可能不匹配或物理含义不一致。

✅ 更稳妥:fR 的索引长度与 lenf 或“实际参与的频点数”对齐。


6) 你可以把这段函数理解成一句话

  • OTFS 分支:用发/收参考序列在 DD 域做匹配滤波 → 得到 RD 图 → 用 CLEAN 找多个目标并消旁瓣
  • 非 OTFS 分支:对 TF 信道估计做 2D FFT(频→距、时→多普勒)→ 得到 RD 图 → 同样 CLEAN

如果你同意,下一步我们就可以在这个“RD 图 + CLEAN 检测框架”上加 AFDM 的发射、接收与等效 RD 映射:关键是把 AFDM 的“chirp/DAFT 结构”接到你们现在的 OFDM 框架里(Tx/Rx 变换、参考信号插入方式、以及 Hest 的定义域保持一致)。你把你们 AFDM 目前计划的参数(比如 chirp rate、DAFT/IDFT 尺寸、循环前缀/guard、以及你们希望 Hest 处于哪个域)发我,我就按你们现有 OTFS 的接口给出一份可直接跑的 AFDM sensing 分支代码。

質問

我们现在认为这个框架没问题。现在在这个框架上我们想要改成AFDM,应该需要怎么调整?已知现在这个otfs,输入进来的量就是OFDM的。我可以直接用这个修改么?

思考

Discussing OTFS to AFDM adaptation

I need to explain the difference between OTFS and AFDM. AFDM uses DAFT, meaning we'll modify the current OFDM-based structure. Let's adapt the framework to the affine-frequency domain. Transmitter mapping should switch to IDAFT, and receiver must use DAFT. Channel estimation needs to be performed in the AFDM domain, possibly after DAFT. For ISAC sensing, we’ll use a ZC sequence with AFDM for delay-Doppler estimation, applying DAFT and FFT.

Clarifying adaptations from OTFS to AFDM

We can't directly use the same code unless Hest or RxSignalTF matches the AFDM domain, where delay and Doppler shifts map to 2D circular shifts. In AFDM, the channel becomes sparse on delay-Doppler for doubly selective channels with an affine structure. I'll propose step-by-step changes, replacing transforms for RD mapping and using DAFT for affine frequency mapping. This modification focuses on AFDM’s chirp parameters and phase compensation.

Adapting OFDM for AFDM sensing

If AFDM wasn’t transmitted, then treating received OFDM as AFDM won't work. But the user hints they want to use OFDM-like input to simulate AFDM. To achieve this, they’d need to generate the Tx AFDM waveform, pass it through the channel, and tweak the sensing algorithms. They’ll need to update the pilot construction and transformation to delay-Doppler (DD) space. For delay estimation, correlation can be done in the time domain.

Approach for AFDM channel estimation

If the channel is in the AFDM domain (H_A), the received signal (y_A) relates to the transmitted signal (x_A). Under integer delay or Doppler shifts, the channel becomes a 2D cyclic shift with an additional phase. This involves using 2D circular correlation, similar to OTFS but with added phase. To adapt, adjust the function to replace the RxSignalDD generation—rather than using ifft(RxSignalTF), apply AFDM-specific transforms to move to the AFDM "DD-like" domain.

Adapting functions for AFDM domain

If Hest is still in the TF (OFDM) domain, you can convert each column to the delay domain with IFFT, but for AFDM's matched filter, it should be in the AFDM domain with chirp compensation. You'll define C1 and C2 matrices and apply DAFT for transformation. If Hest is in frequency domain, approximate with chirp on the subcarrier. You can't just modify the sensing function to accept OFDM input. The AFDM domain estimate must be ensured in the front end, so consider creating an afdmSensingFlag branch.

ISAC modulation in AFDM domain

In ISAC, you'll embed ZC in the AFDM domain and use AFDM modulation with chirp pre/post-processing and IFFT for transmission. In the receiver, apply AFDM demodulation to obtain y_A and estimate H_A. For the RD map, compute a 2D correlation/ambiguity function. Range/Doppler mapping follows earlier principles. You can also implement AFDM as OFDM with chirp multiplication. You'll need to modify the sensing structure, adding new parameters like chirp rates and ensuring the delay/Doppler mapping.

Adjusting sensing for AFDM domain

For AFDM, instead of using the usual ifft(RxSignalTF), you should apply DAFT to convert from the OFDM domain to the AFDM domain. If you're working with frequency-domain symbols, it's better to switch to time-domain after CP removal, apply FFT, and then apply chirps. In the user's case, you can reuse the structure but need AFDM-specific transformations for accurate sensing.

Explaining AFDM sensing and RD map

To evaluate AFDM waveform sensing, regenerate Tx/Rx and apply chirp modulation. For modulation, use matrices C1 and C2 along with the inverse FFT. For demodulation, apply the known formulas with FFT. To estimate the channel in the AFDM domain, compute the element-wise ratio to the pilot. For RD map building, transforming the AFDM channel matrix into delay-Doppler via 2D FFT is an option.

Explaining RD calculation and adjustments

To compute the RD map, we need to do 2D correlation between Tx/Rx sequences in the AFDM domain with chirp compensation. The formula involves adjusting the AFDM demod output with precompensation to handle the delay and Doppler shifts. This requires careful handling of chirp parameters and ensuring the right synchronization across symbols.

Guidance on RD map generation with AFDM and modifications

To generate the RD map, we should apply a matched filter in the AFDM domain, potentially using phase dechirping to remove quadratic phase before performing IFFT/FFT for range and Doppler. If input remains OFDM Hest, convert to AFDM domain or emulate AFDM by adding chirp beforehand.

Thought for 1m 21s

回答

可以“复用这个框架”,但不能只在这一个 offsSensing() 里把 OTFS 分支改几行就算 AFDM——关键取决于你现在喂进来的 Hest 到底处在哪个“域”。

你现在这段 sensing 的本质是:

  1. 把输入矩阵(随频/随慢时间变化)变成一个 Range–Doppler 图 initH
  2. targetNum 次峰值检测 +(可选)CLEAN 消除
  3. 由 Doppler 换算速度

所以要改成 AFDM,框架(RD 图 + peak/CLEAN)基本不动,要动的是“把接收观测变成 RD 图”的那一段,以及“重构模板 reconH”的那一段。


一句话回答你问的:我能直接用 OFDM 的输入来改吗?

  • 如果你输入的 Hest 还是 OFDM 的 TF 信道估计(子载波×符号)
    你在 offsSensing() 里不管怎么改,最后做出来的 RD 图本质仍然是“OFDM sensing”(2D FFT/相关意义下),不是 AFDM waveform 的 sensing

  • 要做“AFDM 的 sensing 仿真”:你必须让输入进 sensing 的观测是“AFDM 解调域(DAFT 域 / affine-frequency 域)”下的观测或等效信道,也就是:** 发的是 AFDM,收端按 AFDM 解调,再把得到的矩阵送进 sensing**。这样你才能说你仿真的确实是 AFDM。


AFDM 相比 OFDM/OTFS,你需要调整的模块在哪里?

1) 发射端:AFDM = “OFDM IFFT 前后加 chirp”

AFDM 常见实现(不同论文符号略有差别,但结构一样)是:

  • 定义两个对角 chirp 矩阵(或等效逐点相乘向量)
    C1[n]=ejπc1n2/NC_1[n]=e^{j\pi c_1 n^2/N}C2[n]=ejπc2n2/NC_2[n]=e^{j\pi c_2 n^2/N}

  • **调制(每个符号)**可以写成(示意):

x=C2HIFFT ⁣(C1Hd)x = C_2^{H}\,\text{IFFT}\!\left(C_1^{H} d\right)

这里的 IFFT 就是你 OFDM 的 IFFT,所以你们“OFDM 框架”可以直接复用:只是在 IFFT 前后各乘一次 chirp

✅ 你要新增 sensing.c1, sensing.c2(或 alpha/beta)这类 AFDM 参数。


2) 接收端:AFDM 解调(FFT 前后也加 chirp)

去 CP 后,对每个符号(示意):

d^=C1FFT ⁣(C2r)\hat d = C_1\,\text{FFT}\!\left(C_2\, r\right)

得到的 d^\hat d(按符号堆起来形成 N×M 矩阵)才是你后面做 AFDM sensing 最合适的输入。


3) Hest 的定义要换:从 “OFDM TF 信道”换成 “AFDM 解调域等效观测/等效信道”

你现在 offsSensing() 里默认 Hest 是某种“TF 估计/观测”。
做 AFDM 时,建议你把 Hest 定义为:

  • Y_A:AFDM 解调后的导频观测矩阵(N×M),或
  • H_A:用导频估出来的 AFDM 域等效信道(N×M)

最简单的导频估计(如果导频逐点非零):

HA(:,m)=YA(:,m)Dpilot(:,m)H_A(:,m) = \frac{Y_A(:,m)}{D_\text{pilot}(:,m)}

然后把这个 H_A 当作你 sensing 的输入。


offsSensing() 里具体该怎么改(保持你现有框架最大复用)

我建议你们加一个分支,比如 sensing.afdmSensingFlag==1,并把它放到“非 OTFS 分支”的同一层,因为 AFDM 也更像“每个符号一维 N 点 + 跨符号慢时间”的结构。

A) RD 图生成:仍然是 “距离维 IFFT、慢时间 FFT”,但输入换成 AFDM 域观测

如果你把输入改成 H_A(N×M):

  • 距离维(delay/range)仍可用 IFFT(等价于用有效带宽形成距离分辨率)
  • Doppler 维仍用慢时间 FFT

也就是你现在非 OTFS 分支的这一句逻辑基本能复用:

matlab
EstRD = abs( fft( ifft(H_A, lenf).', lent ).' ) * sqrt(lenf/lent); initH = fftshift(EstRD, 2);

注意:这时你做出来的是“AFDM 解调域等效信道的 RD 图”。只有当你的发射/接收确实按 AFDM chirp 做过,上述 RD 图才对应 AFDM 体制下的性能。


B) CLEAN 重构模板 reconH:不能再只用“纯 circshift”

你 OTFS 分支里用的是:

  • 延迟/多普勒 → 在 DD 网格上近似 二维循环移位(circshift)

而 AFDM 下,延迟/多普勒映射到解调域通常是“移位 + 额外相位项(chirp 相关的二次相位)”。所以:

  • 如果你先想快速跑通
    先别做 CLEAN 重构(或只做峰值检测),验证 RD 图是否正确出峰;
  • 如果你要保留 CLEAN
    reconSignal = circshift(...) 这一行要改成:
    • circshift 之后再乘一个与 (range,doppler) 相关的相位补偿向量(由你选的 c1,c2 推出来)
    • 或者更稳妥:直接用“发射导频通过 AFDM 调制→施加假设延迟/多普勒→AFDM 解调”的方式生成 reconH(模板生成更通用,但算量大)

✅ 实操建议:第一版 AFDM sensing 先不做 CLEAN,只做 initH 出峰 + targetNum 个最大峰抓取,等 RD 图逻辑验证正确后,再把 CLEAN 模板精确化。


你问“能不能直接修改”:给你一个最省事的落地路径

最小改动路径(推荐)

  1. 保持 offsSensing() 的输出/接口不变
  2. 在你们的 Tx/Rx 物理链路里加入 AFDM chirp(围绕 IFFT/FFT)
  3. 用 AFDM 解调后的导频估计得到 H_A(N×M)
  4. H_A 作为 Hest 喂给 sensing(新增 afdmSensingFlag 分支)
  5. RD 图生成先复用你“非 OTFS 分支”的 2D 变换
  6. 先做峰值检测,CLEAN 后置

这样你基本不用推翻现有框架,也不会在“输入还是 OFDM,却说自己在仿 AFDM”这件事上踩坑。


你接下来给我这 4 个量,我就能把 AFDM 分支的代码按你们现有风格写出来(不改外层框架)

不用你再解释很多,只要明确:

  1. AFDM 采用的参数:c1,c2(或论文里的 alpha,beta)以及 N(=Nc?)
  2. 你们希望 offsSensing() 的输入 Hest 在 AFDM 情况下是:Y_A 还是 H_A
  3. 导频结构:ZC 是放在 d(AFDM 域)里整段占满,还是只占一部分子载波?(影响 deltaD 和模板)
  4. 是否必须保留 CLEAN(多目标消除)?还是先只要峰值检测版本

你给出这几点后,我可以直接给你一份 afdmSensingFlag 的 MATLAB 代码:

  • 第一版:峰值检测可用、与现有参数一致
  • 第二版:补齐 AFDM 相位项的 CLEAN 模板(更严谨)

質問

那我们不用这个算法也行。因为我们的AFDM实际上解调用的也是2DDFT对吧?下面这个是我们OFDM在解调时候用的程序,看一下在这个程序里面,想实现AFDM,有没有需要调整的地方?function [Range,Velocity,Angle]=dft_2d(Hest,sensing)
targetNum=sensing.targetNum;
fc=sensing.fc;
Deltaf=sensing.SCS;
slotT=sensing.frameLen*1e-3/sensing.sensingSymbolNum;
c=3e8;
UEdV=sensing.UEdV;
UEdH=sensing.UEdH;
freqSpace=sensing.freqSpace;
pseudoPeakFlag=sensing.pseudoPeakFlag;
Range=zeros(targetNum,1);
Velocity=zeros(targetNum,1);
Angle=zeros(targetNum,1);
ang=0;

if sensing.overSamplFlag==1
lenf=sensing.overSamplNumf;
lent=sensing.overSamplNumt;
else
[lenf,lent]=size(Hest(:,:,1));
end
rxAntNum=sensing.rxAntNum;
initH=zeros(lenf,lent);
dataSet=cell(1,rxAntNum);
for antId=1:rxAntNum
Htemp=Hest(:,:,antId);
Hdft=fft(ifft(Htemp,lenf).',lent).';
dataSet{antId}=fftshift(Hdft,2);
initH=initH+fftshift(abs(Hdft),2);
end
residualH=initH;

for Idx=1:targetNum
loop=1;
flag=0;
while loop
if Idx>1 || flag==1
[Nc,Nt,Np]=size(Hest);

text
for antId=1:rxAntNum for delayIdx=delayTapSet-lent/2-1 for dolpIdx=dolpTapSet-lent/2-1 distance=delayIdx*deltaR; phiA = exp(-1j*2*pi*[0:Np-1]'*0.5*cos(ang*pi/180)); phiR = exp(-1j*2*pi*Deltaf*freqSpace*distance*[0:Nc-1]/c); phiV=exp(1j*2*pi*dolpIdx*2*deltafd*slotT*[0:Nt-1]); phi=kron(phiR,phiV); phiphi=kron(phiA',phi); phi=phiphi; reSignal=abs(fftshift(fft(ifft(phi,lenf).',lent).',2)); reSignal=reSignal/max(reSignal(:)); factor=residualH(delayIdx+1,dolpIdx+1+lent/2+1); DDRxH = factor*reSignal; residualH=residualH-abs(DDRxH); residualH(residualH<0)=0; end end end end end % [Nc,Nt]=size(residualH); % Vset= c*[-Nt/2:Nt/2-1]./(fc*slotT*Nt); % Rset=[0:Nc-1]./(Deltaf*Nc)*c; deltaV=c/(fc*slotT*lent*2); deltafd=1/(lenf*slotT*2); deltaR=c/(freqSpace*Deltaf*lenf); % residualH=residualH(1:floor(lenf/2),:); residualH=residualH(1:floor(lenf/1),:); [~,index]=max(residualH(:)); [maxRow,maxCol]=ind2sub(size(residualH),index); surf(residualH); figure plot(residualH(1:maxCol)); distance=(maxRow-1)*deltaR; rangeReal=sensing.targetRange; realRow = rangeReal/deltaR+1; speed=(maxCol-lent/2-1)*deltaV; if pseudoPeakFlag==1 dolpTapSet=[maxCol-1,maxCol,maxCol+1]; dolpTapSet(dolpTapSet>lent)=[]; dolpTapSet(dolpTapSet<=0)=[]; delayTapSet=[maxRow-1,maxRow,maxRow+1]; delayTapSet(delayTapSet<=0)=[]; else dolpTapSet=maxCol; delayTapSet=maxRow; end % INN=120; % mesh(deltafd*[-lent/2:lent/2-1],deltaR*[0:Nc-1],residualH(1:INN,:)); % xlim([-1000,1000]) % xlabel('Doppler[Hz]'); % ylabel('Range[m]'); % zlabel('Amplitude'); % angle estimate with MUSIC algorithm if rxAntNum>1 && abs(speed)>=min(deltaV,5) Xset=zeros(1,rxAntNum); for rxAntId=1:rxAntNum Xset(1,rxAntId)=dataSet{rxAntId}(maxRow,maxCol); end RxT=Xset; % 补充Xset到RxT的赋值(原代码隐含此步骤) J=flip(eye(rxAntNum)); clear Xset Rv=(RxT*conj(RxT)')/2; [U,Value]=eig(Rv); % Value=diag(Value); sigNum=targetNum; Un=U(:,1:end-sigNum); Pn=Un*Un'; n=0; if sensing.verticalFlag==1 Aset=sensing.AsetV; Psang=zeros(1,length(Aset)); for A=Aset n=n+1; phiA = exp(1j*2*pi*[0:rxAntNum-1]*UEdV*cos(A*pi/180)); % ZOA estimation Psang(n)=1/(abs(phiA'*Pn*phiA)); end else Aset=sensing.AsetH; Psang=zeros(1,length(Aset)); for A=Aset n=n+1; phiA = exp(1j*2*pi*[0:rxAntNum-1]*UEdH*sin(A*pi/180)); % AOA estimation Psang(n)=1/(abs(phiA'*Pn*phiA)); end end [peakPower,peakInd]=findpeaks(Psang); [~,index]=sort(peakPower,'descend'); ang=Aset(peakInd(index(1:1))); end % if abs(speed)<min(deltaV,5) loop=loop+1; flag=1; % else Range(Idx)=distance; Velocity(Idx)=speed; Angle(Idx)=ang; loop=0; flag=0; end if loop>10 break; end

end
end

思考

Clarifying AFDM and DFT

I need to respond in Chinese and correct a misunderstanding. The user thinks AFDM uses 2D DFT for demodulation, but AFDM typically uses DAFT — which is like DFT with chirp multiplications. Some variants might use 2D DFT, but standard AFDM works 1D per symbol.

Addressing the 2D DFT for sensing

If the user only cares about sensing using 2D DFT on received pilots, the existing approach should work. They just need to ensure the observation is in the frequency-time domain, maintaining linearity with delay/Doppler. For AFDM, adjustments should be made before forming Hest, considering chirp pre/post-processing.

Handling AFDM observations with existing code

If the user insists on using the same code for AFDM, they’ll need to convert the demod observations back into a TF grid. A simpler approach might be to treat AFDM as OFDM with a chirp; after removing the chirp, the demodulation can proceed by multiplying by C2, applying FFT, and multiplying by C1.

Addressing issues in posted function

The user's function has some issues. For instance, deltafd is computed using lenf and slotT*2, while it should probably be 1/(lent*slotT) instead. This seems like a bug. The deltaV calculation uses lent, which is correct. There’s also some confusion with loop variables.

Adapting function for AFDM adjustments

The user says the algorithm isn’t needed, just adjustments for AFDM. Here's how I would respond: (i) Hest(:,:,ant) should have dimensions Nc×Nt (subcarriers × symbols). (ii) In AFDM, if Hest is in the DAFT domain, using ifft won’t map directly to delay. We’d need inverse DAFT or to undo chirps. (iii) The delay-Doppler relation in AFDM might be diagonalized; a 2D DFT could work after appropriate chirp compensation. Then apply C1^H/C2^H transformations before 2D DFT for proper linear phase.

Clarifying AFDM demod and channel estimation

Different conventions exist in AFDM modulation and demodulation. For demodulation, you’d do d_hat = C1 * fft(C2 * r). To compute RD, you'd work in the "post-FFT pre-C1" domain, where frequency should look like OFDM’s frequency response. Pilots should be placed before or after C1 based on how you intend to estimate the channel. AFDM schemes often use DAFT, but for sensing, you can still apply a 2D DFT after dechirping.

Addressing AFDM adjustments and recommendations

The 2D DFT process remains but with a few adjustments:

  1. Define AFDM chirp compensation at the receiver to match OFDM-like channel estimate.
  2. Update deltaR formula with freqSpace, Deltaf and lenf.
  3. Adjust lenf/lent if the AFDM grid mapping changes.
  4. reorder and apply phase correction if nonuniform subcarriers mapping is due to chirp. Multi-antenna angle estimation remains the same with the MUSIC algorithm.

Also, deltafd bug needs fixing as it should be based on lent*slotT not just lenf.

Clarifying two AFDM options

I think there are two options to consider:

  • Option 1: Keep AFDM demodulation using the TF domain, then perform 2D DFT. You'll output Hest after C2 multiplication/FFT, but before applying C1 (or after compensation). Pilots need to be inserted accordingly.
  • Option 2: Perform 2D DFT directly on the AFDM demodulated data and replace ifft(Htemp,lenf) with inverse DAFT along subcarrier dimension, applying chirp factors and IFFT.

Clarifying AFDM demodulation adjustments

To convert an AFDM-domain channel to time/frequency, undo the chirps accordingly. In the case of the 2D DFT-based algorithm, if your Hest is in the AFDM domain (DAFT domain), apply the inverse DAFT along the frequency dimension. Then perform the 2D DFT as usual. Adjust the demod function by multiplying by conjugate chirps before channel estimation to make the pilots align as in OFDM.

Clarifying code inspection related to AFDM

The function is intended for sensing using 2D DFT, not for demodulation, even though it's referred to as being for "demodulation." A nested while loop in the code seems messy due to issues with variable initialization and loop conditions, causing potential mismatched execution. The part for computing Hdft=fft(ifft(Htemp,lenf).',lent) matches the 2D DFT, but for AFDM with chirps, you'd introduce a chirp compensation step before or after this.

Thought for 1m 17s

回答

你这段 dft_2d() 本质上不是“解调”,而是把 输入矩阵 Hest(子载波×慢时间×天线)

  • 频域 → 延迟:ifft(·, lenf)
  • 慢时间 → 多普勒:fft(·, lent)

从而得到 Range–Doppler 图(你叫 Hdft / dataSet / initH)。

先纠正一句:AFDM 不是“纯 2DDFT 解调”

典型 AFDM(DAFT)在每个符号内是“chirp × FFT/IFFT × chirp”的结构(离散仿射傅里叶变换),它确实用到了 DFT,但多了两侧的 chirp(二次相位)
你做 Doppler(跨符号)那一维用 DFT 是合理的,但“频→延迟”的那一维如果输入在 AFDM 域,就不能直接当 OFDM 的子载波频响来 ifft


结论:在这个 dft_2d() 里要不要改,取决于你喂进来的 Hest 是什么域

情况 A(最省事、也最推荐):Hest 仍然是 “OFDM-like 的 TF 频响/信道估计”

也就是:即便你发的是 AFDM,你在接收端把 chirp 去掉/补偿,再做 FFT 得到“等效子载波频响”,然后估 Hest

✅ 那么 dft_2d()几乎不用改
Hdft=fft(ifft(Htemp,lenf).',lent).'; 这条 2D 变换继续成立,你得到的 RD 图就是对的。

你需要改的是 AFDM 的调制/解调与信道估计模块,保证输出的 Hest 还是“每个子载波的频响随慢时间变化”。


情况 B:你喂进来的是 AFDM/DAFT 域的观测或等效信道(AFDM 解调输出)

也就是 Hest 的第一维不是“子载波频率采样”,而是 DAFT 的系数。

✅ 那么 dft_2d()必须加一步“从 AFDM 域转回 OFDM-like 频域”,否则 ifft 的“距离意义”不成立。

最常见做法:在做 ifft(·) 之前,先对每个慢时间列做一次 inverse-DAFT(或等效去 chirp),把 AFDM 域矩阵变成等效频域矩阵,再按你原来的 2D DFT 做 RD。


在你这份代码里,AFDM 需要动的最小位置(核心就是动 Htemp)

你现在每根天线做:

matlab
Htemp=Hest(:,:,antId); Hdft=fft(ifft(Htemp,lenf).',lent).';

如果你是情况 B(输入在 AFDM 域),你把它改成:

matlab
HtempA = Hest(:,:,antId); % AFDM/DAFT 域 HtempF = idaft_cols(HtempA, sensing); % 先变回“等效频域”(每列做一次 inverse-DAFT) Hdft = fft(ifft(HtempF,lenf).',lent).';

下面给你一个列处理版本的模板(你只需要把 chirp 参数按你们 AFDM 定义填对;共轭方向可能与你们论文符号相反,但结构就是这样):

matlab
function Xf = idaft_cols(Xa, sensing) % Xa: N x M (AFDM/DAFT域), 每列一个symbol % Xf: N x M (等效“频域/子载波域”) N = size(Xa,1); M = size(Xa,2); n = (0:N-1).'; alpha = sensing.afdmAlpha; % 你们AFDM参数名自定 beta = sensing.afdmBeta; Da = exp( 1j*pi*alpha*(n.^2)/N ); % 可能需要共轭,取决于你们DAFT定义 Db = exp( 1j*pi*beta *(n.^2)/N ); Xf = zeros(N,M); for m = 1:M % 一种常见inverse-DAFT形态: x = Da .* ifft( conj(Db).*X ) Xf(:,m) = Da .* ifft( conj(Db).*Xa(:,m), N ); end end

你们到底用 Da/Db 还是它们的共轭,取决于你们 AFDM 调制公式是 “DbFDa” 还是 “Db^HFDa^H”。但无论如何:在进入 ifft(·) 做距离之前,要先把 AFDM 域恢复成等效“频域采样”,这是硬要求。


你代码里还有 2 个“跟 AFDM无关但会影响结果”的明显点(建议顺手修)

  1. 你这里 Doppler 分辨率写错维度了:
matlab
deltafd=1/(lenf*slotT*2); % 这里用lenf不对

Doppler 轴长度是 lent,一般应跟 lent 相关(是否要 *2 取决于你把速度/单程双程怎么定义):

matlab
deltafd = 1/(lent*slotT); % 常见写法(再根据双程/符号定义调整)
  1. deltaV 你写成:
matlab
deltaV=c/(fc*slotT*lent*2);

这相当于默认双程雷达(有 /2)。如果你 ISAC 里确实按双程换算就留着;否则会差一倍。


CLEAN/重构那一大段(while 里)如果你做 AFDM也要注意

你里面用 phiR/phiV 生成模板,再走同样的 2DDFT:

matlab
reSignal=abs(fftshift(fft(ifft(phi,lenf).',lent).',2));

如果你的 residualH 来自 AFDM 域输入(情况B),那模板也必须经过同样的“AFDM域↔等效频域”的处理,否则模板和观测不在同一个域,CLEAN 会乱减。

所以:

  • 情况A(你输出的 Hest 是等效频域):模板这段可以继续用(最多改改参数/bug)
  • 情况B(你输入的是 AFDM 域):模板要么也在 AFDM 域生成,要么先在等效频域生成再 DAFT 映射过去

给你一个实际建议(能最快跑通且不踩坑)

如果你想“AFDM 仿真 + 继续用这套 2DDFT sensing”:

  • 优先走情况A:在接收端先做 AFDM 的去 chirp/解调,但把输出整理成 等效 OFDM 的 TF 网格(子载波×符号),再估 Hest
  • 这样 dft_2d() 基本不用动(最多修 deltafd 那个 bug),你就能公平对比 OFDM/OTFS/AFDM 的 sensing 表现

如果你告诉我一句话就行:你们现在的 Hest(:,:,ant) 在 AFDM 情况下到底是“DAFT域”还是“等效子载波频域”(也就是你们 AFDM 解调后输出在哪一层),我就能把上面 idaft_cols() 里的共轭方向和参数位置按你们的 AFDM 定义一次性对齐,并把 dft_2d() 给你改成可直接跑的版本。

質問

我们在调制解调里面现在是按下面写的:
function [TxTimeSignal,sensing]=ofdmModulate(TxFreqSignal,sensing,frameId)
Nsybmol=sensing.Nsymbol;
txAntNum=sensing.txAntNum;
fftSize=sensing.fftSize;
if sensing.blockCP==1
CplenSet=zeros(1,Nsybmol);
CplenSet(1:14:end)=fftSize;
sensing.CplenSet=CplenSet;
else
CplenSet=sensing.CplenSet;
end

removeCpInd=zeros(1,Nsybmol); % CP position index
TxTimeSignal=zeros(txAntNum,Nsybmol*fftSize+sum(CplenSet(1:Nsybmol)));

for antId=1:txAntNum
TxTimeSignalTemp=[];
% changed:choose AFD
% sensing.useAFDM
if sensing.useAFDM
TxSignalT=idaft(TxFreqSignal(:,:,antId),sensing.afdm.c1,sensing.afdm.c2); % idaft
else
TxSignalT=ifft(TxFreqSignal(:,:,antId),fftSize)*sqrt(fftSize); % ifft
end
%%%
for symInd=1:Nsybmol
removeCpInd(symInd)=length(TxTimeSignalTemp);
Cpdata=TxSignalT(end-CplenSet(symInd)+1:end,symInd);
TxSignalCp=[Cpdata.',TxSignalT(:,symInd).']; % add CP
TxTimeSignalTemp=cat(2,TxTimeSignalTemp,TxSignalCp);
end
TxTimeSignal(antId,:)=TxTimeSignalTemp;
end
removeCpInd(removeCpInd+1)=length(TxTimeSignalTemp);
sensing.removeCpInd=removeCpInd;
sensing.frameId=frameId;
end

function [RxFreqSignal]=ofdmDemodulate(channelOut,sensing)
if sensing.cfr==0
Nsybmol=sensing.Nsymbol;
fftSize=sensing.fftSize;
CplenSet=sensing.CplenSet;
removeCpInd=sensing.removeCpInd;
rxAntNum=sensing.rxAntNum;

text
% remove CP and FFT RxSignal=zeros(fftSize,Nsybmol,rxAntNum); RxFreqSignal=zeros(fftSize,Nsybmol,rxAntNum); for antId=1:rxAntNum for symInd=1:Nsybmol RxSignal(:,symInd,antId)=channelOut(antId, removeCpInd(symInd)+CplenSet(symInd)+1 : removeCpInd(symInd)+CplenSet(symInd)+fftSize); %%% AFD added if sensing.useAFDM RxFreqSignal(:,symInd,antId)=daft(RxSignal(:,symInd,antId),sensing.afdm.c1,sensing.afdm.c2); % daft else RxFreqSignal(:,symInd,antId)=fft(RxSignal(:,symInd,antId))/sqrt(fftSize); % fft end end end

else
RxFreqSignal=channelOut; % if pass frequency domain channel,skip this step
end
end
然后参数定义上,c1=1/2N;c2=1/128N。这种配置可以么?

思考

Guidance on adjusting prefix and computing Doppler for AFDM

The Doppler shift is normalized as ν_i = N f_i = α_i + a_i. To compute αmax from the physical max Doppler ν_max = f_D,max / Δf, we approximate αmax ≈ ceil(νmax). This leads to c1 = (2αmax+1)/(2N). For the user's AFDM implementation, they should adjust the prefix, possibly using CPP, but their choice of c1 ensures CP = CPP if N is even.

Explaining c1 and c2 choices for AFDM

To ensure benefits, the choice of c1 should depend on νmax and delay spread to prevent overlap, as shown in formulas (28)/(29). For non-zero νmax, increase c1. For instance, with νmax = 0.62, set c1 to 3/(2N). Choose c2 as irrational or small rational, which is fine. It also impacts DAFT separability and performance in ISAC sensing.

Clarifying adjustments for modulation/demodulation

To make adjustments in your program, focus on normalization and handling the prefix. Check these steps:

  1. Confirm the modulation and demodulation structure aligns with AFDM. Use IDAFT/DAFT around IFFT/FFT. Refer to equations like S = Λ_c2 F Λ_c1 s.
  2. Regarding c1 and c2, in MATLAB, 1/2N should be interpreted as 1/(2N), not 1/2N.
  3. For compatibility with OFDM and sensing, transform RxFreqSignal if necessary.

Clarifying parameter choices and prefix handling

For AFDM, use a CP equivalent if 2Nc1 is an integer and N is even. With c1=1/(2N), the equation 2Nc1=1 holds, so CP can be preserved. If N is odd, implement CPP by adjusting prefix samples with exp(-j2πc1(N^2 + 2Nn)).

For c1, to avoid overlap and achieve full delay-Doppler representation, use c1 = (2αmax + 1) / (2N). Consider c2 to be irrational or sufficiently smaller than 1/(2N) for diversity.

Thought for 1m 57s

回答

你们现在这套改法(把 OFDM 的 ifft/fft 换成 idaft/daft,CP 结构不变)总体思路是对的:AFDM 本质上就是在每个 OFDM 符号的 IFFT/FFT 两侧加“chirp(二次相位)”的 DAFT/IDAFT,论文里的框图就是 IDAFT → 加前缀 → 信道 → 去前缀 → DAFT。 (arXiv)

但要注意两点:前缀类型c1/c2 的取值条件。下面按你问的“这个程序里要不要调整、c1/c2 这样配行不行”来回答。


1) 你这段调制/解调代码里,AFDM 需要调整的地方有哪些?

(A) 前缀:AFDM严格来说用的是 CPP,不是普通 CP

AFDM 推导里用的是 chirp-periodic prefix(CPP),并给了 CPP 的具体形式;只有在某些条件下 CPP 才会退化成普通 CP。 (arXiv)

论文明确写到:2Nc12Nc_1 是整数且 NN 为偶数时,CPP 就“等同于”普通 CP。 (arXiv)
所以:

  • 如果你坚持像现在这样“直接复制尾巴做 CP”,那就请把 c1 选成满足 2*N*c1 为整数(并确保 fftSize=N 是偶数,通常都是偶数)。
  • 一旦你以后把 c1 选成不满足这个条件,你的 CP 就不再等价于 CPP,严格模型下会出现额外失配(尤其在推导“循环卷积/简化输入输出关系”时)。

✅ 你现在给的 c1=1/(2N)
2*N*c1 = 1(整数),只要 N 偶数,则 你现在的 CP 写法是 OK 的(等价 CPP)。


(B) 归一化/能量尺度:确认 idaft/daft 是否是“单位酉(unitary)”

AFDM/DAFT 在文中是用带 1/√N 的 DFT 矩阵定义的,并且提到相应变换是 unitary(噪声协方差不变)。 (arXiv)

你 OFDM 分支用的是 unitary 的实现方式:

  • ifft()*sqrt(N)fft()/sqrt(N)

而 AFDM 分支你直接:

  • idaft(...) / daft(...)(没额外乘除)

所以关键是:你们的 idaft/daft 函数内部是不是也按 unitary 来写的(比如 DFT 矩阵用 /sqrt(N),或在 fft/ifft 外做了对应尺度补偿)。

✅ 建议你做个快速一致性检查(不改算法也能发现问题):

  • 比较 mean(abs(ifft(X)*sqrt(N)).^2)mean(abs(idaft(X,c1,c2)).^2) 是否同量级
  • 或者看 daft(idaft(X)) 是否≈X(数值误差内)

如果能量不一致,你后面所有 SNR/检测阈值/峰值高度都会偏。


(C) sensing.cfr==1 这个旁路要小心

你解调里:

matlab
else RxFreqSignal=channelOut; % if pass frequency domain channel,skip this step end

如果你在 AFDM 模式下走这个分支,那么 channelOut 必须已经是 DAFT 域输出(等价你做过 daft 了),否则域不对。


2) 你给的参数:c1=1/(2N)c2=1/(128N) 这样配可以吗?

(A) c2=1/(128N):一般是“安全的/推荐方向”的

经典 AFDM 结论之一是:为了保证某些满秩/全分集性质,c2 往往选 无理数,或者选 远小于 1/(2N)1/(2N) 的有理数。 (arXiv)

你这个 c2=1/(128N) 明显满足 “1/(2N)\ll 1/(2N)”(小了 64 倍),所以从这个角度看是合理的。 (arXiv)
另外也有文献提到 c2 具有周期性、有效范围在 [0,1]。 (arXiv)

✅ 结论:c2=1/(128N) 作为仿真起点没问题。


(B) c1=1/(2N):能用,但它隐含“多普勒不大/不追求最强分离”的假设

在 AFDM 参数设计里,c1 的作用是让不同 delay/Doppler 的路径在 DAFT 域不重叠。论文给了避免重叠的约束,并在“最坏情况下(delay 不稀疏,相邻 delay 差 1)”给出一个直接的选法:

c1=2αmax+12Nc_1 = \frac{2\alpha_{\max}+1}{2N}

其中 αmax\alpha_{\max} 是“归一化多普勒(相对 SCS)”的整数部分上界。 (arXiv)

同时论文也指出:OCDM 取 c1=c2=1/(2N),它比 OFDM 好但不保证在一般 LTV 信道下达到 AFDM 的“完全分离/全分集”效果。 (arXiv)

而你现在 c1=1/(2N) 等价于把上面那个公式里的 αmax=0\alpha_{\max}=0
如果你系统里最大归一化多普勒 νmax=fD,max/Δf\nu_{\max}=f_{D,\max}/\Delta f 明显不为 0(高速/高载频 ISAC 很常见),那 c1 选太小就可能导致 DAFT 域路径“挤在一起”。

论文对归一化多普勒的定义也写得很清楚:νi=Nfi=αi+ai\nu_i=Nf_i=\alpha_i+a_iνi\nu_i 是相对 SCS 的归一化多普勒,αi\alpha_i 是整数部分。 (arXiv)

✅ 结论:

  • 能跑:可以,用来先把链路/接口跑通、对齐 OTFS/OFDM 框架。
  • 想发挥 AFDM 的“路径分离/抗多普勒”优势c1 应该按你们的 νmax\nu_{\max}(或 αmax\alpha_{\max})去调,常用就是把 c1 提到 (2αmax+1)/(2N)(2\alpha_{\max}+1)/(2N) 这个量级。 (arXiv)

额外好消息:如果你把 c1 选成 (2αmax+1)/(2N)(2\alpha_{\max}+1)/(2N),那么 2Nc1=2\alpha_{\max}+1 仍然是整数;只要 N 偶数,你仍可继续用“普通 CP”,不必改成 CPP(满足“CP=CPP”的条件)。 (arXiv)


3) 你现在这段实现整体上“还缺什么”?

就调制/解调这块来说,你们缺的不是大改,而是三件事的“确认/约束”:

  1. 确认 idaft/daft 的归一化(是否 unitary),否则 SNR 和峰值会不公平。 (arXiv)
  2. 保持 2Nc1 为整数且 N 偶数,你才能一直用普通 CP,不用实现 CPP。 (arXiv)
  3. c1 要跟你们的最大归一化多普勒匹配,否则 AFDM 的“分离”效果出不来。 (arXiv)

如果你愿意,我下一步可以直接给你一个“按你们 fc/SCS/frameLen 计算 νmax\nu_{\max}、进而给出推荐 c1”的计算方式(并说明双程 ISAC 的 Doppler 归一化该怎么取),这样你们可以把 c1 自动化配置起来,不靠拍脑袋。

質問

image.pngimage.png
image.pngimage.png

现在我们已经确认了 idaft/daft 的归一化,但是现在距离误差很大,速度误差和OFDM是一样的。我们现在需要分析一下原因并修改。附件里的图是我们的daft的细节

質問

现在我们已经确认了 idaft/daft 的归一化,但是现在距离误差很大,速度误差和OFDM是一样的。我们现在需要分析一下原因并一起来找一找问题在哪。现在速度检测是没问题的,但是距离检测误差较大

質問

现在我们已经确认了 idaft/daft 的归一化,但是现在距离误差很大,速度误差和OFDM是一样的。我们现在需要分析一下原因并一起来找一找问题在哪。现在速度检测是没问题的,但是距离检测误差较大。我们现在收发都是用的DAFT来做的,但是在做匹配滤波的时候我们做了如下处理。所以速度检测问题不大。但是delay感觉是不是得补偿:
function Hest=channelEst(TxFreqSignal,RxFreqSignal,sensing)
timeSpace = sensing.timeSpace;
freqSpace = sensing.freqSpace;
Nzc=sensing.Nzc;
Nc=sensing.Nc;
fpattern=sensing.fpattern;
tpattern=sensing.tpattern;
sensingSymbolNum=sensing.sensingSymbolNum;
if sensing.fastchannel==1
tpattern=[1:sensingSymbolNum];
end

rxAntNum=sensing.rxAntNum;

%% AFD
if sensing.useAFDM == 1
txTime = idaft(TxFreqSignal,sensing.afdm.c1,sensing.afdm.c2);
rxTime = zeros(size(RxFreqSignal));
for rx=1:rxAntNum
rxTime(:,:,rx) = idaft(RxFreqSignal(:,:,rx),sensing.afdm.c1,sensing.afdm.c2);
end
TxFreqSignal = fft(txTime,Nc,1)/sqrt(Nc);
RxFreqSignal = fft(rxTime,Nc,1)/sqrt(Nc);
end

TxFreqSignal=circshift(TxFreqSignal,Nc/2,1);
TxSignalTF=TxFreqSignal(fpattern,tpattern);
Hest=zeros(length(sensingSymbolNum),sensingSymbolNum,rxAntNum);

RxFreqSignal=circshift(RxFreqSignal,Nc/2,1); % Frequency domain data passing through the time domain channel

for RxIdx=1:rxAntNum
RxSignalTF=RxFreqSignal(fpattern,tpattern,RxIdx);
Hest(:,:,RxIdx)=RxSignalTF ./ TxSignalTF;
end

if sensing.otfsSensingFlag==1
Hest=RxFreqSignal(1:Nc,tpattern);
end
end

思考

Confirming idaft/chirp1 and chirp2 steps

The idaft steps in the displayed screenshot seem correct. It involves chirping and applying inverse DAFT. The additional step in their channel estimation uses idaft on both Tx and Rx signals. RxFreqSignal comes from demodulation, and applying idaft should return the original time-domain signal (RxSignal).

Considering AFDM vs OFDM & pilot selection

They seem to be recovering the original RxSignal by undoing DAFT and then using FFT, which effectively makes their channelEst like standard OFDM. So, they could directly use the time-domain signals instead of going through DAFT. The possible issue might be with pilot selection and any indexing misalignment due to shifts.

Considering AFDM and delay error sources

Shifting both the Tx and Rx signals cancels out, unless the fpattern references physical subcarriers and might introduce issues. The distance error could come from the deltaR formula or the shift causing negative delay indices. For AFDM, they might use all bins, unlike OFDM's pilots. The delay issue could be about the time-domain signals needing compensation.

Considering Doppler effect and ICI in AFDM vs OFDM

If the channel includes Doppler (time-varying), the frequency-domain relationship becomes more complex than simple multiplication. In OFDM, Doppler causes ICI, but their channel estimation assumes no ICI. For AFDM, ICI may be mitigated in the DAFT domain, but reapplying FFT brings it back. This could affect Hest more in AFDM than OFDM. However, the speed remains the same, which suggests Doppler isn’t a major factor or the estimation uses symbol-wide data rather than within symbols.

Analyzing AFDM's range and Doppler estimation

In their speed estimation, they use the maximum column along the Doppler axis from the 2D DFT of Hest. If Hest is corrupted, the Doppler peak remains accurate while the delay peak shifts. With AFDM, the frequency response varies with time and ICI, complicating this relationship. Chirp spreading in AFDM handles Doppler, but requires "dechirping" for accurate range estimation, as delay and Doppler are coupled by the chirp.

Addressing AFDM delay and Doppler estimation bias

The delay estimate in the frequency domain might be skewed unless the chirp is removed. While they process in the FFT domain, not the AFDM domain during sensing, the effect of chirps likely appears in X(k) rather than H. ICI may still impact the estimation, causing range issues in AFDM due to spectra not being flat. Actionable steps include checking the CP length and adjusting if needed.

Adjusting AFDM for delay and Doppler estimation

To improve AFDM’s delay and Doppler estimation, I shouldn't apply idaft and FFT in channelEst. Instead, I can use RxFreqSignal directly in the DAFT domain and perform 2D matched filtering for range and Doppler mapping. For delay, cross-correlation in the time domain should help, avoiding small X[k] issues. Adding delay compensation before IFFT is another potential adjustment.

Analyzing DAFT domain processing and channel estimation

In the DAFT domain, a chirp in time corresponds to a convolution in frequency, affecting subcarrier mapping. If range estimation uses IFFT of Hest, its quadratic phase could be influenced by preprocessing. However, in channelEst, RxFreqSignal undergoes transformations, and with correct handling of CP or CPP, everything seems to align.

Analyzing channel estimation and Doppler effects

The issue could be Doppler causing a non-circular convolution, which prevents FFT from diagonalizing and results in range errors. AFDM is worse than OFDM because its pilot symbols vary, increasing noise and interference in range estimation. To fix this, channel estimation needs a method like LS over the full band.

Investigating distance error in AFDM

The large distance error could be due to deltaR being computed with freqSpace and lenf, assuming equally spaced frequencies, but AFDM may use a non-contiguous fpattern for subcarriers. If fpattern is sparse, deltaR needs to account for total bandwidth and actual spacing. Incorrect dimensions for Hest in channelEst may also be contributing.

Tracking the source of range error

The large range error may be due to the mismatch between the expected frequency index and the actual positioning after fftshift/circshift operations. The assumption of a centered subcarrier after shifts could be affecting the accuracy in AFDM. In addition, using a non-contiguous subcarrier pattern makes the bandwidth and range resolution coarser, contributing to the issue.

Considering fixes for range error in AFDM

The range error in AFDM could stem from a mismatch in frequency spacing, particularly if the pilot pattern isn’t evenly spaced. While Doppler estimation stays stable, range precision suffers. Using regularization during channel estimation, avoiding small/weak bins, or switching to a full-length ZC sequence could help improve accuracy.

Evaluating range error sources in AFDM

The range error may be due to channel estimation being done in the wrong domain (FFT vs DAFT), or inaccuracies caused by non-sparse fpattern and incorrect assumptions about pilot positions. In AFDM, data and pilot contributions combine, making X[k] uncertain unless full data is known. Resolving this might require more careful pilot handling.

Examining potential issues in AFDM channel estimation

The potential cause of range errors could lie in the double application of the inverse DAFT in the AFDM function. Also, the c1 setting for Doppler compensation and phase calculations may not be optimal for larger velocities. Further investigation should focus on verifying channel estimate consistency, addressing dynamic range issues, and testing adaptations for multipath and Doppler effects. Alternative methods like least squares or time-domain correlation might help refine estimates.

Investigating channel estimation with AFDM and DAFT

It seems that the method used in channel estimation may be reintroducing unwanted chirp effects. When using AFDM, undoing the DAFT (IDAFT) in the estimation step can negate benefits, causing range errors. To improve the accuracy of delay estimation, I suggest staying in the DAFT domain or performing dechirping before channel estimation. This would help reduce Doppler and delay coupling, yielding better results.

Proposing AFDM sensing adjustments for better delay estimation

To improve AFDM sensing, I suggest avoiding the idaft step and instead working directly in the DAFT domain. For delay estimation, consider using the DAFT-domain signals and focusing on dechirped FFT outputs for better channel frequency response estimation. This would preserve dechirp benefits, yielding more accurate delay estimation and preventing mismatches due to chirp effects.

Refining AFDM range estimation strategy

It seems like there’s an issue with noise amplification in AFDM due to non-flat spectrum after frequency division. A better approach would be to avoid this division and instead use full-band estimation across all subcarriers, or switch to a time-domain matched filter. This change could help yield more reliable delay and range estimations by maintaining a constant modulus.

Troubleshooting AFDM range errors

It looks like the range error stems from spectral shaping issues caused by AFDM's non-flat spectrum. Dividing at weak frequency bins can lead to ill-conditioning. A more robust solution would involve a time-domain matched filter or using full-band least-squares estimation. We could suggest modifications to avoid dividing at weak bins, ensuring smoother results. I'll propose updates to their existing code to focus on better handling of AFDM's structure, and explain why range error occurs with their current method.

Thought for 8m 21s

回答

你们这套 daft/idaft 写法(截图里那种“chirp→FFT/ifft→chirp” + 1/sqrt(N))本身没问题;现在速度(Doppler)正常但距离(delay/range)误差大,从你给的 channelEst() 逻辑来看,最常见的根因基本都落在 “距离维用到的频域样本/权重不对或病态” 上,而 Doppler 维(跨符号 FFT)天然更稳,所以会出现“速度像 OFDM 一样,距离却崩”的现象。

下面我按“最可能原因 → 怎么验证 → 怎么改”给你一个可落地的排查/修改路径。


1) 最可能的原因(按概率从高到低)

原因 A:AFDM 的符号经 idaft 后,其 FFT 频谱幅度不平坦甚至有深陷,你们在 Hest = Y/X 时在某些子载波上被 小 |X| 放大噪声/ICI,导致 delay 峰位跑偏

你在 AFDM 分支里做了:

matlab
txTime = idaft(TxFreqSignal,c1,c2); rxTime = idaft(RxFreqSignal,c1,c2); % 其实就是把DAFT解调输出变回时域 TxFreqSignal = fft(txTime,Nc,1)/sqrt(Nc); RxFreqSignal = fft(rxTime,Nc,1)/sqrt(Nc); ... Hest = RxSignalTF ./ TxSignalTF;

这一步 数学上在“严格循环卷积且无 ICI”时是成立的(任意波形都可做 Y/X 得 CFR),但在 ISAC 的 LTV(存在 Doppler)场景里,Y[k] 不是单纯 H[k]X[k],而是带 ICI 的混合;此时 |X[k]| 很小的频点会把 ICI/噪声的影响放大很多,delay(靠跨频相位斜率/一致性)会最先坏,而 Doppler(靠跨符号相位变化)看起来还行。

OFDM pilot 常常是“选出来的那些频点幅度恒定”,所以不会触发这个病态;AFDM 经过 chirp 后的 FFT 幅度一般不再恒定,这是差异来源。


原因 B:你们只在 fpattern 这些稀疏频点上估计 Hest,对 AFDM 来说等效带宽/有效频点更“不可控”

OFDM 时 fpattern 上就是你真正发的 pilot,能量集中、SNR 高;
AFDM 时你虽然也能算出这些频点的 X[k],但它们可能刚好落在谱陷/低能量区域,导致距离误差大。


原因 C(次要但要排):CP 长度不足 或者 fpattern/freqSpacedeltaR 的标定不一致

这个一般会让 OFDM 也不对;你说 OFDM 的距离没问题,所以概率较低,但还是建议顺手确认一下:maxDelayTap <= min(CplenSet)


2) 两个“立刻就能定位”的验证动作(强烈建议先做)

验证 1:看 AFDM 发射符号的 FFT 幅度在你们 fpattern 上是不是很糟

channelEst() 的 AFDM 分支里加几行(随便取一个符号):

matlab
X = fft(txTime(:,1),Nc)/sqrt(Nc); fprintf("Xmag min/max = %.3e / %.3e\n", min(abs(X)), max(abs(X))); fprintf("Xmag@fpattern min/max = %.3e / %.3e\n", ... min(abs(X(fpattern))), max(abs(X(fpattern))));
  • 如果 min(abs(X(fpattern))) 很小(比如比 max 小 30~60 dB),那基本就坐实“距离坏是因为 Y/X 病态”。

验证 2:对单目标场景,看你估出来的 Hest(:,m) 相位是否接近线性(delay 应该对应线性相位)

取一个 symbol m,画 unwrap(angle(Hest(:,m))),应该基本是直线(加少量噪声)。
如果不是线性而是乱飘/二次弯曲,delay 峰必然会漂。


3) 修改方案(从“最小改动”到“最稳妥”)

方案 1:最小改动——在 AFDM 下把 Hest = Y/X 改成“正则化 LS”,并避免用低能量频点

把这一句:

matlab
Hest(:,:,RxIdx)=RxSignalTF ./ TxSignalTF;

改成(核心是加 eps,并且推荐只选 |X| 足够大的频点):

matlab
eps0 = 1e-8; Hest(:,:,RxIdx) = (RxSignalTF .* conj(TxSignalTF)) ./ (abs(TxSignalTF).^2 + eps0);

再进一步(很有效):AFDM 时别用原来的 fpattern,改成:

  • 直接全带fpattern = 1:Nc; freqSpace = 1;(用于 sensing 仿真对比最公平)
  • 或者 自适应挑频点:只保留 abs(TxFreqSignal(:,m)) 大于某阈值的频点(比如 > -15 dB of max)

这样通常能显著改善距离误差,且不改你后面的 dft_2d()


方案 2:推荐(做 sensing 更对路)——不要先估 Hest,直接用时域做匹配滤波得到 delay,再做 Doppler FFT

既然你在 AFDM 分支里已经得到了 txTimerxTime(其实就是每个 symbol 的时域波形),那距离最稳的方法是标准雷达脉压:

对每个 symbol:

c[,m]=IFFT{X[k,m]Y[k,m]}c[\ell,m] = \text{IFFT}\{ \overline{X[k,m]}Y[k,m]\}

也就是“时域循环相关”。这样你不会被 1/X 放大,也不依赖频点选择。

MATLAB 形态就是:

matlab
Xf = fft(txTime, Nc, 1); Yf = fft(rxTime(:,:,RxIdx), Nc, 1); corrDelay = ifft(conj(Xf).*Yf, Nc, 1); % Nc × Ns (delay × slow-time) RD = fftshift(fft(corrDelay, lent, 2), 2); % delay × doppler

然后你沿用你现在的 peak picking(maxRow/maxCol→range/velocity)即可。
这套方法一般会出现你描述的现象:速度继续正确,距离从“漂”变成“稳”

这其实也解释了你现在的现象:你们“速度检测没问题”是因为 Doppler FFT 本来就对这些病态不敏感;距离靠相位一致性/相关峰,最容易被 Y/X 的病态搞坏。


方案 3:如果你们坚持“用 AFDM 的优势”,就不要在 channelEst() 里把 DAFT 解调结果 idaft 回去再 FFT

你现在 AFDM 分支把 RxFreqSignal(DAFT 域)又 idaft 回时域再 FFT,本质上是在用 OFDM 的 CFR 视角做 sensing;这会让 AFDM 的优势(在 DAFT 域下对 delay/Doppler 的结构化表征)发挥不出来。
如果你们想做“真正 AFDM 域 sensing”,应该直接在 DAFT 域构造 RD(这需要把你们后面的 range 轴变换从 ifft 换成“与 DAFT 定义匹配的逆变换/模板”,工作量更大,但更“正宗”)。


4) 你这段 channelEst() 里一个我建议立刻改的小点

你现在 AFDM 分支里估计 Hest 用的是 RxSignalTF./TxSignalTF,如果 TxSignalTF 有很小的点,会直接炸。

所以至少先做方案 1 的正则化 LS,这是低成本高收益的第一步。


5) 我建议你接下来把这两个数贴出来,我就能直接锁死问题属于哪类

不用发全工程,只要你运行一次把下面打印出来:

  1. min(abs(X(fpattern))) / max(abs(X(fpattern)))(AFDM 下)
  2. 同样的比值(OFDM 下)

如果 AFDM 这个比值小很多(深陷明显),那距离误差几乎肯定就是“Y/X 病态 + ICI/噪声放大”导致的;我们就按上面方案 1/2 改,基本就能收敛。

你要是愿意,我也可以按你们当前接口把“方案 2(时域匹配滤波 + Doppler FFT)”封装成一个 afdmSensing(),输出 Range/Velocity 与 dft_2d() 一致,这样你们主流程改动最小。

質問

我们现在想参考下面这个afdm是对的的demo,看看基于这个思路,对于我们的channelEst以及DFT应该怎么改?或者我们可以不用原来的DFT2d,而是重新写一个代码:
clc; clear; close all;rng(2025);

%% ===================== 系统参数 =====================
N = 1024; % AFDM 子载波数
Nsym = 64; % 慢时脉冲数
M = 16; % QAM 阶数
SNRdB = 15; % AWGN

fc = 24e9; % 载频 [Hz]
DeltaF = 22.729e3; % 子载波间隔 [Hz]
Fs = N*DeltaF; % 采样率 [Hz]
Ts = 1/Fs; % 采样间隔 [s]

alphamax = 2; kv = 4;
c1 = (2*(alphamax+kv)+1)/(2N);
c2 = 1/(128
N);
Ncp = 128; % CPP(需 > 最大时延采样)
TAFDM = (N+Ncp)/Fs; % 单符号时长(含CPP)

c_light = 3e8;

%% ===================== 发射端:QAM & AFDM 调制 =====================
symIdx = randi([0 M-1], N*Nsym, 1);
xTx = qammod(symIdx, M, 'UnitAveragePower', true);
X = reshape(xTx, [N, Nsym]); % DAFT 域符号

s_blocks = afdm_mod(X, c1, c2); % N x Nsym(时域)
s_tx = cpp_add(s_blocks, Ncp); % (N+Ncp)*Nsym x 1

%% ===================== 目标真值(请确保 max(li)<Ncp) =====================
R_true = [400, 20, 180]; % m
v_true = 2*[ 31, 31, -10]; % m/s
P = numel(R_true);

tau = 2R_true / c_light; % s
li = round(tau / Ts); % 样点
fd = 2
v_true*fc / c_light; % Hz
fi = fd * Ts; % 归一化(乘以 n 的相位增量)

h_i = (randn(1,P)+1jrandn(1,P))/sqrt(2P);

%% ===================== 接收:叠加回波 + 噪声 =====================
r_rx = apply_radar_channel(s_tx, N, Ncp, Nsym, li, fi, h_i);
r_rx = awgn(r_rx, SNRdB, 'measured');

%% ===================== 通信侧接收:DAFT & 简易等化 =====================
Y_blocks = cpp_remove(r_rx, N, Ncp, Nsym); % N x Nsym(时域)
Y_daft = daft_colwise(Y_blocks, c1, c2); % N x Nsym(DAFT)

Hhat = Y_daft ./ (X + 1e-12); % 粗糙 LS,默认通信信息完全已知
Xhat = Y_daft ./ (Hhat + 1e-12);
plot_constellation(X(:), Xhat(:), M);

%% ===================== 公共坐标换算(物理轴) =====================
% 距离分辨率:ΔR = c/(2B) = c/(2NΔf) = c/(2Fs)
range_bin_m = c_light/(2
Fs);

% Doppler 轴:K 点慢时 FFT,Δfd = 1/(KTAFDM),范围 ±1/(2TAFDM)
fd_axis = (-floor(Nsym/2):ceil(Nsym/2)-1) * (1/(NsymTAFDM)); % Hz
vel_axis = fd_axis * (c_light/(2
fc)); % m/s

%% ===================== 感知 :时域 FCCR =====================
RD_fccr = abs( rdm_fccr(Y_blocks, s_blocks) ); % N x Nsym
RD_show = fftshift(RD_fccr(1:Ncp, :), 2); % 仅对 Doppler 维中心化
ranges_plot = (0:Ncp-1) * range_bin_m;

% 2D RDM
figure('Name','RDM (Time-Domain FCCR)','Color','w');
imagesc(vel_axis, ranges_plot, mag2db_norm(RD_show)); axis xy;
colormap turbo; colorbar; xlabel('Velocity (m/s)'); ylabel('Range (m)');
title('Time-Domain FCCR (Range–Doppler)'); hold on;
hScat = scatter(v_true, R_true, 80, 'r', 'filled', 'MarkerEdgeColor','k', 'LineWidth',1.2);
legend(hScat, {'True targets'}, 'Location','northoutside'); % 仅真值图例
grid on; box on;

% 3D RDM
figure('Name','RDM 3D (FCCR)','Color','w');
[VV,RR] = meshgrid(vel_axis, ranges_plot);
Zfccr = mag2db_norm(RD_show);
surf(VV, RR, Zfccr, 'EdgeColor','none'); view(40,30);
xlabel('Velocity (m/s)'); ylabel('Range (m)'); zlabel('Magnitude (dB)');
title('3D Range–Doppler (FCCR)'); colormap turbo; colorbar; grid on; box on;
% 3D 叠加真值(Z 取最近网格值)
[~,kidxT] = arrayfun(@(v) min(abs(vel_axis-v)), v_true);
ridxT = min(max(round(R_true/range_bin_m)+1,1), Ncp);
zT = arrayfun(@(ii) Zfccr(ridxT(ii),kidxT(ii)), 1:P);
hold on; scatter3(v_true, R_true, zT, 60, 'r', 'filled', 'MarkerEdgeColor','k', 'LineWidth',1.2);

disp('仿真完成。');

%% ======================================================================
%% ============================ 本地函数区 ===============================
%% ======================================================================

function s_blocks = afdm_mod(X, c1, c2)
% 逐列 IDAFT(论文式(1) 的快速实现)
[N, K] = size(X);
s_blocks = zeros(N, K);
for k = 1:K
s_blocks(:,k) = idaft_col(X(:,k), c1, c2);
end
end

function s = idaft_col(x, c1, c2)
% 单列 IDAFT
[N, K] = size(x); assert(K==1, 'idaft_col 需要 N×1 列向量');
n = (0:N-1).'; m = (0:N-1).';
E1 = exp(1j2pic1(n.^2)); % 时间域 chirp
E2 = exp(1j2pic2(m.^2)); % DAFT 域 chirp
s = E1 .* (ifft(E2 .* x, [], 1) * sqrt(N));
end

function Y = daft_colwise(S, c1, c2)
% 逐列 DAFT(IDAFT 的逆)
[N, K] = size(S);
n = (0:N-1).'; m = (0:N-1).';
E1 = exp(-1j2pic1(n.^2));
E2 = exp(-1j2pic2(m.^2));
Y = (fft(E1 .* S, [], 1) / sqrt(N)) .* E2;
end

function s_tx = cpp_add(s_blocks, Ncp)
% CPP 添加:每块头部拼接 Ncp 个尾样点
[N, K] = size(s_blocks);
out = zeros(N+Ncp, K);
for k = 1:K
blk = s_blocks(:,k);
out(:,k) = [blk(end-Ncp+1:end); blk];
end
s_tx = out(:);
end

function S_blocks = cpp_remove(rx, N, Ncp, K)
% 去 CPP:按块切分并去掉前 Ncp
rx_mat = reshape(rx, N+Ncp, K);
S_blocks = rx_mat(Ncp+1:end, :);
end

function r = apply_radar_channel(s_tx, N, Ncp, K, li, fi, h_i)
% 论文式(3):r[n] = Σ_i h_i · s[n-li] · e^{j2π f_i n}
P = numel(li);
L = (N+Ncp)K;
n = (0:L-1).';
r = zeros(L,1);
s_mat = reshape(s_tx, N+Ncp, K);
for p = 1:P
s_shift = zeros(size(s_mat));
for k = 1:K
s_shift(:,k) = circshift(s_mat(:,k), li(p));
end
r = r + h_i(p) * ( s_shift(:) .
exp(1j2pi*fi(p)*n) );
end
end

function RD = rdm_fccr(R, S)
% 时域 FCCR:fast-time FFT → 乘积 → IFFT → slow-time FFT
Rf = fft(R, [], 1);
Sf = fft(S, [], 1);
Z = ifft(Rf .* conj(Sf), [], 1);
RD = fft(Z, [], 2);
end

function ydb = mag2db_norm(Y)
% 归一幅度 → dB(避免 -Inf)
Yn = Y ./ (max(Y(:)) + eps);
ydb = 20*log10(Yn + eps);
end

function plot_constellation(X, Xhat, M)
% 星座图(仅演示)
Nplot = min(4000, numel(X));
idx = randperm(numel(X), Nplot);
figure('Name','Constellation','Color','w');
subplot(1,2,1);
plot(real(X(idx)), imag(X(idx)), 'o'); axis equal; grid on;
title(sprintf('TX %d-QAM', M)); xlabel('I'); ylabel('Q');
subplot(1,2,2);
plot(real(Xhat(idx)), imag(Xhat(idx)), 'o'); axis equal; grid on;
title('RX Equalized'); xlabel('I'); ylabel('Q');
end

思考

Outlining AFDM adjustments and code proposals

For AFDM, we may replace channelEst by returning Rx and Tx blocks for sensing or computing RD within it. We could integrate it into dft_2d with a time-domain FCCR routine, calling it afdm_fccr_sensing. We’ll also propose changes to ofdmDemodulate and ofdmModulate to ensure proper handling of time blocks and return them as needed.

AFDM changes and sensor adjustments

We can update the function to include TxSignalT for each antenna by adding it to sensing.TxBlockTime. Then, adjust the physical axis by setting Fs = Nc * SCS and ensuring uniform symbol periods for Doppler processing. For range bins, we’ll set the value accordingly, considering delay and cropping to the first Ncp as in the demo. Lastly, I’ll make sure to avoid any spectral notch issues!

Incorporating channel estimation for AFDM

For the channelEst patterns, I’ll keep fpattern as is, but modify for the AFDM system. Since TxFreqSignal passes through idaft, it should be treated in the AFDM domain. For sensing, use the time-domain waveform reference TxSignalT. If they plan to equalize, they should keep DAFT demod and compute Hhat in DAFT domain separately.

Proposing separation for comm and sensing functions

I suggest separating the processes:

  • For communication, either keep the existing channelEst or update it to compute DAFT domain estimates directly, without idaft/fft conversion.
  • For sensing, compute RD using FCCR in the time domain. The existing code converts to the OFDM frequency domain, but it might not affect communication. For the DAFT domain, they can compute the channel estimate directly without idaft to avoid domain mismatches.

Clarifying domain handling for sensing and delay compensation

For LS, the channel estimate should stay in the same domain as the transmission. If you transmit in the DAFT domain, you should calculate the channel estimate in the DAFT domain as well. Converting to the OFDM frequency domain introduces errors, especially relating to delay. For sensing, the best approach is time-domain correlation, avoiding issues caused by domain changes.

Modifying channel estimation and sensing for AFDM

For AFDM, keep the channel estimation in the DAFT domain without converting to frequency. For sensing, use time-domain blocks and remove CP to build the range and delay correctly. Avoid using 2D DFT unless necessary, as it might misalign the delays, which could cause range errors in AFDM.

Channel estimation and sensing with DAFT for AFDM

In AFDM, avoid the circshift for Tx and Rx signals—shifting introduces a basis change in DAFT domain. The correct domain is DAFT for channel estimation to avoid delay bias. For sensing using FCCR, don't shift and ensure fpattern refers to the full frequency range.

Proposing changes for channel estimation and sensing

For sensing, I suggest using min(CplenSet) or a constant Ncp. In the calculation, Fs = N*SCS, and range is range_bin = c/(2*Fs). To improve accuracy, avoid domain conversion and circshift for channel estimation. Instead, adapt the FCCR method to directly use time domain blocks.

Thought for 3m 17s

回答

你这个现象(速度跟 OFDM 一样正常,但距离误差很大)和你现在 channelEst() 的 AFDM 处理方式高度一致:你把 DAFT 域的收发符号又 idaft→(时域)→fft→(OFDM 频域),然后再做 Hest = Y./X,这会让距离维(delay)特别容易崩,而速度(慢时 FFT)看起来还能对。

下面我按你给的 demo 思路,把“问题在哪 + 怎么改”说清楚,并给你一套可以直接替换的实现方案(你们也可以完全不用原来的 dft_2d)。


1) 为什么“速度对、距离偏”很常见?

核心原因:你在 AFDM 模式下用 Y./X 做“频域信道估计”,但在雷达/ISAC 的 LTV(含 Doppler)情况下

对 OFDM 来说,Y[k] ≈ H[k]X[k] 只有在多普勒很小/ICI 可忽略时才近似成立;
对 AFDM 来说,你又额外做了基变换(DAFT↔时域↔FFT),等效会让

  • Y[k] 里 ICI/混叠更明显;
  • 某些频点 |X[k]| 可能很小(或谱形不平坦),Y./X 会把误差放大;
  • delay 估计依赖跨频相位斜率/相关峰,对上述失配极其敏感 → 距离漂移;
  • Doppler 估计主要靠慢时相位变化/慢时 FFT,反而更“钝”,所以你会看到速度跟 OFDM 差不多。

你自己也观察到:你现在“匹配滤波”那套处理对速度影响不大,但 delay 需要补偿——是的,本质就是“你现在 delay 维的处理域不对/估计方式不稳”。


2) 按 demo 的正确做法:AFDM 感知不要先做 Hest = Y./X,直接用时域 FCCR 做 Range–Doppler

demo 的 sensing 用的是 Time-domain FCCR

  1. 去 CPP/CP 得到每个符号的时域块 Y_blocks (N×K)
  2. 已知发射的无前缀时域块 s_blocks (N×K)
  3. 快时做匹配滤波(频域乘积再 IFFT)得到 delay 相关输出
  4. 慢时 FFT 得到 Doppler
    这一步完全避免 1/X,delay 会稳很多。

3) 你们现有代码怎么改:两条线分开(通信估计 & 感知)

A) 通信侧 channelEst(如果你还需要):AFDM 时直接在 DAFT 域估计,不要 idaft→fft→再除

你的 ofdmDemodulate()useAFDM 时输出的 RxFreqSignal 实际上就是 DAFT 域的 YTxFreqSignal 也是你发的 DAFT 域 X。那通信侧最一致的 LS 就是:

  • 删掉你现在这段(AFDM 分支里):
matlab
txTime = idaft(TxFreqSignal,...); rxTime = idaft(RxFreqSignal,...); TxFreqSignal = fft(txTime,...); RxFreqSignal = fft(rxTime,...);
  • 改成 DAFT 域正则化 LS(强烈建议,避免小幅值点放大):
matlab
if sensing.useAFDM % TxFreqSignal: X (DAFT域), RxFreqSignal: Y (DAFT域) eps0 = 1e-8; TxSignalTF = TxFreqSignal(fpattern,tpattern); % 如果你坚持 pattern for RxIdx = 1:rxAntNum RxSignalTF = RxFreqSignal(fpattern,tpattern,RxIdx); Hest(:,:,RxIdx) = (RxSignalTF .* conj(TxSignalTF)) ./ (abs(TxSignalTF).^2 + eps0); end return; end

重要:AFDM 下 不要做 circshift(Nc/2) 再除 —— 那是 OFDM 子载波重排的习惯操作,但你现在已经不在 OFDM 子载波域了,会引入额外相位/索引错配,最先影响的就是 delay。


B) 感知侧(你们现在距离误差大的根源):建议新写一个 afdmSensing_fccr() 替换 dft_2d()

你不需要 Hest,直接用 去 CP 后的时域块做 RDM:

  • 发射端:你在 ofdmModulate() 里已经算出了 TxSignalT(每符号 N 点、无 CP 的时域块),AFDM 时它就是 demo 里的 s_blocks
  • 接收端:在 ofdmDemodulate() 里你已经有 RxSignal(:,sym,ant)(去 CP 后的时域块),这就是 demo 里的 Y_blocks

建议把 ofdmDemodulate 改成同时输出时域块(最小侵入)

matlab
function [RxFreqSignal, RxTimeBlocks] = ofdmDemodulate(channelOut,sensing) ... RxTimeBlocks = RxSignal; % N×Nsymbol×rxAnt end

ofdmModulate() 里把 TxSignalT 存到 sensing 里返回:

matlab
if sensing.useAFDM TxSignalT = idaft(...); else TxSignalT = ifft(...); end sensing.TxTimeBlocks = TxSignalT; % N×Nsymbol(或按天线存)

4) 可直接用的 AFDM FCCR 感知函数(支持多天线、输出 Range/Velocity/Angle)

下面这版先实现 R/V(Angle 你们可以沿用现在 MUSIC:在检测到的 RD bin 上取各天线复值快照即可)。

matlab
function [Range,Velocity,Angle, RDsum, RDant] = afdmSensing_fccr(RxTimeBlocks, TxTimeBlocks, sensing) % RxTimeBlocks: N×K×Nr (去CP后的时域块) % TxTimeBlocks: N×K (发射无CP时域块,AFDM: idaft输出;OFDM: ifft输出) % RDsum: 距离×多普勒幅度图(用于debug) % RDant: 距离×多普勒×天线 复值(用于角度估计) c = 3e8; fc = sensing.fc; N = size(RxTimeBlocks,1); K = size(RxTimeBlocks,2); Nr = size(RxTimeBlocks,3); targetNum = sensing.targetNum; % ---------- 采样率/符号时长(建议用真实 N*Δf) ---------- Deltaf = sensing.SCS; Fs = N * Deltaf; % 你们CP若每符号不同,严格说慢时FFT会受影响;建议AFDM用固定CP/CPP if isfield(sensing,'CplenSet') Ncp = min(sensing.CplenSet(1:K)); % 保守取最短CP对应的无歧义距离 else Ncp = sensing.afdm.Ncp; % 或你们自己存 end Tsym = (N + Ncp)/Fs; % 慢时采样周期(含前缀) % ---------- 物理坐标:注意你们仿真里 delay 是 R/c 还是 2R/c ---------- twoWay = true; % <<< 如果你们信道里用 tau=2R/c,就设 true;若 tau=R/c,就设 false if twoWay range_bin = c/(2*Fs); else range_bin = c/(Fs); end fd_bin = 1/(K*Tsym); % 多普勒分辨率(Hz) vel_bin = fd_bin * c/(2*fc); % 速度分辨率(m/s,双程) % ---------- FCCR:快时相关 + 慢时FFT ---------- Sf = fft(TxTimeBlocks, [], 1); % N×K RDsum = zeros(N, K); RDant = zeros(N, K, Nr); for ant = 1:Nr Rf = fft(RxTimeBlocks(:,:,ant), [], 1); % N×K Z = ifft(Rf .* conj(Sf), [], 1); % N×K (delay相关输出) RD = fft(Z, [], 2); % N×K (doppler) RD = fftshift(RD, 2); RDant(:,:,ant) = RD; RDsum = RDsum + abs(RD); end % 只取无歧义距离(<= CP)范围 RDcut = RDsum(1:Ncp, :); % ---------- 峰值检测(先做最简单版本:逐次取最大点并做小窗口抑制) ---------- Range = zeros(targetNum,1); Velocity = zeros(targetNum,1); Angle = zeros(targetNum,1); res = RDcut; guardR = 2; guardV = 2; % 抑制窗口(可按需要改) for p = 1:targetNum [~,idx] = max(res(:)); [rIdx, vIdx] = ind2sub(size(res), idx); % 距离与速度 Range(p) = (rIdx-1) * range_bin; Velocity(p) = (vIdx - (K/2) - 1) * vel_bin; % 窗口抑制(避免重复挑到同一个主瓣) r1 = max(1, rIdx-guardR); r2 = min(size(res,1), rIdx+guardR); v1 = max(1, vIdx-guardV); v2 = min(size(res,2), vIdx+guardV); res(r1:r2, v1:v2) = 0; % 角度:你们如果要MUSIC,就在这里取复值快照 % snapshot = squeeze(RDant(rIdx, vIdx, :)); % Nr×1 % Angle(p) = musicAoA(snapshot, sensing); % 你们现有的那套MUSIC可复用 end end

5) 你们当前系统里“最容易一改就好”的点(优先级最高)

  1. 感知别用 Hest = Y./X 走距离维,先用上面 FCCR(基本能立刻把距离拉回正常)
  2. AFDM 通信估计要在 DAFT 域做,别 idaft→fft 再除,且别 circshift(N/2)
  3. 确认 delay→range 的 2 倍关系:你们信道如果是 tau=2R/c,range_bin 必须有 /2;如果你们是 tau=R/c,就不要 /2。这个一旦搞反,距离误差会“很大而且呈比例”。

6) 我建议你立刻做的两个小自检(不用改很多代码就能定位)

  1. 单目标、无噪声、无多普勒(fd=0):
    • 用 FCCR 的 RDcut 看峰是不是在 li 对应的 bin
  2. 同一组目标,用 twoWay=true/false 切一下:
    • 如果发现距离刚好差一个 2 倍,就说明你们信道里的 delay 定义跟坐标换算不一致

如果你把你们信道里“目标时延”到底是 tau=R/c 还是 tau=2R/c(以及 AFDM 时 CP 长度是否固定、CplenSet 是否每符号不同)告诉我一句,我就能把上面函数里的 Tsym/range_bin 精确对齐到你们工程的定义,并把 Angle 的 MUSIC 部分也按你们现有 dft_2d 的写法直接接上去。

質問

Nc = 3276 目标双程最大距离1000 速度最大0,SCS = 30000 CP长度是288

質問

Nc = 3276 目标双程最大距离1000 速度最大0,SCS = 30000 CP长度是288。我也觉得我们可以直接写一个demo,而不是在channelest中进行修改。我们就像下面的这个OTFSsensing一样,单独写一个AFDM的函数吧。原来的channelEst我们就不调整了。现在OFDM的调制解调都用的DAFT,其他没有做处理。请写一个函数,用上面发你的参考代码的思路,对照OTFS和OFDM的框架,写一个AFDM,用类似2DDFT的·方式进行感知:function [Range,Velocity,Angle]=dft_2d(Hest,sensing)
targetNum=sensing.targetNum;
fc=sensing.fc;
Deltaf=sensing.SCS;
slotT=sensing.frameLen*1e-3/sensing.sensingSymbolNum;
c=3e8;
UEdV=sensing.UEdV;
UEdH=sensing.UEdH;
freqSpace=sensing.freqSpace;
pseudoPeakFlag=sensing.pseudoPeakFlag;
Range=zeros(targetNum,1);
Velocity=zeros(targetNum,1);
Angle=zeros(targetNum,1);
ang=0;

if sensing.overSamplFlag==1
lenf=sensing.overSamplNumf;
lent=sensing.overSamplNumt;
else
[lenf,lent]=size(Hest(:,:,1));
end
rxAntNum=sensing.rxAntNum;
initH=zeros(lenf,lent);
dataSet=cell(1,rxAntNum);
for antId=1:rxAntNum
Htemp=Hest(:,:,antId);
Hdft=fft(ifft(Htemp,lenf).',lent).';
dataSet{antId}=fftshift(Hdft,2);
initH=initH+fftshift(abs(Hdft),2);
end
residualH=initH;

for Idx=1:targetNum
loop=1;
flag=0;
while loop
if Idx>1 || flag==1
[Nc,Nt,Np]=size(Hest);

text
for antId=1:rxAntNum for delayIdx=delayTapSet-lent/2-1 for dolpIdx=dolpTapSet-lent/2-1 distance=delayIdx*deltaR; phiA = exp(-1j*2*pi*[0:Np-1]'*0.5*cos(ang*pi/180)); phiR = exp(-1j*2*pi*Deltaf*freqSpace*distance*[0:Nc-1]/c); phiV=exp(1j*2*pi*dolpIdx*2*deltafd*slotT*[0:Nt-1]); phi=kron(phiR,phiV); phiphi=kron(phiA',phi); phi=phiphi; reSignal=abs(fftshift(fft(ifft(phi,lenf).',lent).',2)); reSignal=reSignal/max(reSignal(:)); factor=residualH(delayIdx+1,dolpIdx+1+lent/2+1); DDRxH = factor*reSignal; residualH=residualH-abs(DDRxH); residualH(residualH<0)=0; end end end end end % [Nc,Nt]=size(residualH); % Vset= c*[-Nt/2:Nt/2-1]./(fc*slotT*Nt); % Rset=[0:Nc-1]./(Deltaf*Nc)*c; deltaV=c/(fc*slotT*lent*2); deltafd=1/(lenf*slotT*2); deltaR=c/(freqSpace*Deltaf*lenf); % residualH=residualH(1:floor(lenf/2),:); residualH=residualH(1:floor(lenf/1),:); [~,index]=max(residualH(:)); [maxRow,maxCol]=ind2sub(size(residualH),index); surf(residualH); figure plot(residualH(1:maxCol)); distance=(maxRow-1)*deltaR; rangeReal=sensing.targetRange; realRow = rangeReal/deltaR+1; speed=(maxCol-lent/2-1)*deltaV; if pseudoPeakFlag==1 dolpTapSet=[maxCol-1,maxCol,maxCol+1]; dolpTapSet(dolpTapSet>lent)=[]; dolpTapSet(dolpTapSet<=0)=[]; delayTapSet=[maxRow-1,maxRow,maxRow+1]; delayTapSet(delayTapSet<=0)=[]; else dolpTapSet=maxCol; delayTapSet=maxRow; end % INN=120; % mesh(deltafd*[-lent/2:lent/2-1],deltaR*[0:Nc-1],residualH(1:INN,:)); % xlim([-1000,1000]) % xlabel('Doppler[Hz]'); % ylabel('Range[m]'); % zlabel('Amplitude'); % angle estimate with MUSIC algorithm if rxAntNum>1 && abs(speed)>=min(deltaV,5) Xset=zeros(1,rxAntNum); for rxAntId=1:rxAntNum Xset(1,rxAntId)=dataSet{rxAntId}(maxRow,maxCol); end RxT=Xset; % 补充Xset到RxT的赋值(原代码隐含此步骤) J=flip(eye(rxAntNum)); clear Xset Rv=(RxT*conj(RxT)')/2; [U,Value]=eig(Rv); % Value=diag(Value); sigNum=targetNum; Un=U(:,1:end-sigNum); Pn=Un*Un'; n=0; if sensing.verticalFlag==1 Aset=sensing.AsetV; Psang=zeros(1,length(Aset)); for A=Aset n=n+1; phiA = exp(1j*2*pi*[0:rxAntNum-1]*UEdV*cos(A*pi/180)); % ZOA estimation Psang(n)=1/(abs(phiA'*Pn*phiA)); end else Aset=sensing.AsetH; Psang=zeros(1,length(Aset)); for A=Aset n=n+1; phiA = exp(1j*2*pi*[0:rxAntNum-1]*UEdH*sin(A*pi/180)); % AOA estimation Psang(n)=1/(abs(phiA'*Pn*phiA)); end end [peakPower,peakInd]=findpeaks(Psang); [~,index]=sort(peakPower,'descend'); ang=Aset(peakInd(index(1:1))); end % if abs(speed)<min(deltaV,5) loop=loop+1; flag=1; % else Range(Idx)=distance; Velocity(Idx)=speed; Angle(Idx)=ang; loop=0; flag=0; end if loop>10 break; end

end
end
function [Range,Velocity,Angle]=offsSensing(Hest,sensing)
Angle=0;
targetNum=sensing.targetNum;
fc=sensing.fc;
SCS=sensing.SCS;
slotT=sensing.frameLen*1e-3/sensing.sensingSymbolNum;
sensingSymbolNum=sensing.sensingSymbolNum;
fdmax=1/slotT;
Nc=sensing.Nc;
Nzc=sensing.Nzc;
TxSignalDD=sensing.TxSignalDD;
overSamplNumf=sensing.overSamplNumf;
overSamplNumt=sensing.overSamplNumt;
overSamplFlag=sensing.overSamplFlag;
freqSpace=sensing.freqSpace;
ZcSeq=sensing.ZcSeq;

if sensing.otfsSensingFlag==1
% SFFT
RxSignalTF=Hest;
SensingNsymbol=size(Hest,2);
RxSignalDD=ifft(RxSignalTF).'.*sqrt(Nc/SensingNsymbol);
RxRsSignal=RxSignalDD(1:Nzc,:);

text
% Receive reference signal oersampling RxRsSignalTF=ifft(fft(RxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); RxOverSamplSignalDD=ifft(fft(RxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol); % Transmit reference signal oversampling TxRsSignal=repmat(ZcSeq,1,SensingNsymbol); TxRsSignalTF=ifft(fft(TxRsSignal).'.*sqrt(SensingNsymbol/Nzc)); TxOverSamplSignalDD=ifft(fft(TxRsSignalTF,overSamplNumf).',overSamplNumt).*sqrt(Nzc/SensingNsymbol); % reconstruct local oversampling signal OverSampZcSeq=ifft(fft(ZcSeq,overSamplNumf)); OverSampZcSeq=repmat(OverSampZcSeq,1,overSamplNumt); % reconstruct receive oversampling signal OverSampTF=ifft(fft(TxSignalDD(1:Nzc,:)).'.'); OverSampDD=fft(ifft(OverSampTF,overSamplNumf).',overSamplNumt).'; % channel estimate if overSamplFlag == 0 EstHDD=ifft(conj(fft(TxRsSignal)).*fft(RxRsSignal))/(sqrt(SensingNsymbol)*Nzc); else EstHDD=ifft(conj(fft(TxOverSamplSignalDD)).*fft(RxOverSamplSignalDD))/(sqrt(SensingNsymbol)*Nzc); end peakValue=max(EstHDD(:)); EstHDD=EstHDD/peakValue; if overSampFlag == 0 deltaD = 3e8/(SCS*Nzc); len=sensingNsymbol; deltafd = fdmax/len; else deltaD = 3e8/(SCS*overSampNumf); len=overSamplNumt; deltafd = fdmax/len; end initH=fftshift(abs(EstHDD),2); [lenf,lent]=size(initH); % mesh(-lent/2:lent/2-1]*deltafd,[0:lenf-1]*deltaD,(initH)); % xlabel('Doppler [Hz]'); % ylabel('Range [m]') % zlabel('Normalized Power') % ylim([0,2000]) % xlim([-15000,15000]) % set(gcf,'Color','white'); % colorbar residualH=initH; distanceEst=zeros(targetNum,1); dopplerEst=zeros(targetNum,1); velocityEst=zeros(targetNum,1); for targetInd=1:targetNum [~,peakInd]=max(residualH(:)); [row,col]=ind2sub(size(residualH),peakInd); range=(row-1)*deltaD; doppler=(col-1-lenf/2)*deltafd; rowSet=[row]; colSet=[col]; rowSet(rowSet==0)=[]; rowSet(rowSet>size(initH,1))=[]; colSet(colSet==0)=[]; colSet(colSet>size(initH,2))=[]; for row=rowSet for col=colSet if overSamplFlag==0 reconSignal=circshift(TxSignalDD(1:Nzc,:),row-1,1); reconSignal=circshift(reconSignal,col-1,2); reconH=ifft(conj(fft(TxRsSignal)).*fft(reconSignal))/(sqrt(SensingNsymbol)*Nzc); else reconSignal=circshift(OverSampDD,row-1,1); reconSignal=circshift(reconSignal,col-1,2); reconH=ifft(conj(fft(OverSampZcSeq)).*fft(reconSignal))/(sqrt(SensingNsymbol)*Nzc); end reconH=abs(reconH/max(reconH(:))); factor=initH(row,col); residualH=residualH-factor*reconH; residualH(residualH<0)=0; end end distanceEst(targetInd,1)=range; dopplerEst(targetInd,1)=doppler; velocityEst(targetInd,1)=dopplerEst(targetInd,1)*3e8/fc/2; Range=distanceEst; Velocity=velocityEst; end

else
channelHEst=Hest;
if overSamplFlag == 0
[lenf,lent]=size(channelHEst);
else
lenf = overSamplNumf;
lent = overSamplNumt;
end
deltaD = 3e8/(SCSfreqSpacelenf);
deltafd = fdmax/lent;

text
EstHTF=abs(fft(ifft(channelHEst,lenf).',lent).')*sqrt(lenf/lent); peakValue=max(EstHTF(:)); EstHTF=EstHTF/peakValue; initH=fftshift(abs(EstHTF),2); residualH=initH; distanceEst=zeros(targetNum,1); dopplerEst=zeros(targetNum,1); velocityEst=zeros(targetNum,1); [lenf,lent]=size(initH); % mesh(-lent/2:lent/2-1]*deltafd,[0:lenf-1]*deltaD,(initH)); % xlabel('Doppler [Hz]'); % ylabel('Range [m]') % zlabel('Normalized amplitude') % ylim([0,800]) % xlim([-10000,10000]) % set(gcf,'Color','white'); % colorbar for targetInd=1:targetNum [~,peakInd]=max(residualH(:)); [row,col]=ind2sub(size(residualH),peakInd); range=(row-1)*deltaD; doppler=(col-1-lent/2)*deltafd; c=3e8; fR=exp(-1j*2*pi*[0:Nzc-1]*SCS*range/c); fV=exp(1j*2*pi*[0:sensingSymbolNum-1]*(col-1)*deltafd*slotT); recon=fR.'*fV; reconH=fft(ifft(recon,lenf).',lent).'*sqrt(lenf/lent); reconH=abs(reconH/max(reconH(:))); factor=initH(row,col); residualH=residualH-factor*reconH; distanceEst(targetInd,1)=range; dopplerEst(targetInd,1)=doppler; velocityEst(targetInd,1)=dopplerEst(targetInd,1)*3e8/fc/2; Range=distanceEst; Velocity=velocityEst; end

end
end

思考

Designing MATLAB function for AFDM sensing

I need to write a standalone MATLAB function for AFDM sensing based on a demo that uses FCCR time-domain. Modulation and demodulation use DAFT, and they want minimal processing elsewhere. The function might use inputs like Hest, but they prefer separating it, like OTFS. I'll need to account for Rx and Tx time blocks and follow demo structure.

Integrating demodulator for AFDM sensing

I'll modify the demodulator to output time blocks directly, or use idaft to reconstruct them from RxFreqSignal and TxFreqSignal. I'll ensure to respect the CP length, and consider physical axes like Nc=3276, SCS=30k, and max range. I'll also allow for optional oversampling and calculate the Doppler axis based on symbol duration.

Implementing 2DFFT, MUSIC, and fixing covariance issues

I'll implement the 2D FFT as described—FFT along slow time and IFFT along fast time to calculate RD. For the multi-antenna and angle estimation, we’ll apply MUSIC after getting the complex RD per antenna. I'll fix the flaw in their MUSIC covariance calculation while staying minimal for compatibility.

Simplifying range/velocity estimation approach

I'll avoid using complex loops and CLEAN for range/velocity estimation. Instead, I'll follow the dft_2d approach — compute RD magnitude sum across antennas, then find the top targetNum peaks with optional pseudoPeakFlag. Since the max unambiguous range is below 1000m, I’ll focus on range/velocity.

Addressing CP length and max range mismatch

The user’s system has CP=288, but for a 1000m range, this is too short, causing wrap-around errors. To fix this, I’ll suggest increasing CP to at least 656 or using cyclic prefix extension. I’ll implement a warning and allow configurable range window lengths to handle these issues.

Implementing sensing function with demo approach

The user requested a function that mimics the demo's approach for range/velocity estimation. I'll compute RxTimeBlocks and TxTimeBlocks from DAFT signals. For range, I'll calculate bins based on CP length and perform peak search for Doppler. For velocity, I'll calculate using specific formulas. I'll implement phase difference estimator for angle.

Implementing AFDM sensing and optional oversampling

I’ll implement the AFDM sensing function to estimate range, velocity, and angle using 2D FFT. We’ll create a function where users input RxFreqSignal, TxFreqSignal, and sensing. I'll fix the covariance and use rank-1 for angle estimation. Optional zero-padding with overSamplFlag will improve resolution, but at the cost of performance.

Clarifying CP limitations and Doppler bin steps

To address CP limitations, I'll compute the maximum range (Rmax) as a function of CP and range_bin, warning the user if CP is too short. I'll clarify that a CP of 288 gives a max range of 439m, needing at least 656 for a 1000m range. I'll adjust formulas based on their system’s specifications.

Thought for 1m 10s

回答

下面给你一个独立的 AFDM 感知函数(不改你们现有 channelEst),思路严格对齐你贴的 demo:** 去 CP 后的时域块做 FCCR(快时匹配滤波)+ 慢时 FFT(多普勒)**,本质就是“类似 2DDFT”的实现方式:

  • 快时:FFT(R) * conj(FFT(S)) → IFFT 得到 delay 相关(脉压)
  • 慢时:对每个 delay bin 做 FFT 得到 Doppler
  • 多天线:对各天线幅度累加找峰;角度可选用你们已有的阵列参数做简单波束形成(单快照更稳)

同时我先把你们当前参数里一个决定性问题讲清楚(这就是“距离误差很大”的最常见根因):

关键检查:CP=288 不足以支撑双程 1000 m

你给的:Nc=3276, SCS=30000
采样率 Fs=NΔf=3276×30000=98.28 MHzF_s = N\Delta f = 3276\times 30000 = 98.28\text{ MHz}

双程距离的每个 delay bin 距离分辨率:

ΔR=c2Fs3×1082×98.28×1061.526 m\Delta R = \frac{c}{2F_s} \approx \frac{3\times 10^8}{2\times 98.28\times 10^6} \approx 1.526\text{ m}

CP=288 的无歧义最大双程距离(近似):

Rmax(CP1)ΔR287×1.526438 mR_{\max} \approx (CP-1)\Delta R \approx 287\times 1.526 \approx 438\text{ m}

而你们目标双程最大 1000 m,需要的 CP 至少:

CP10001.526+1656CP \ge \left\lceil \frac{1000}{1.526}\right\rceil + 1 \approx 656

所以如果你仿真里真的放了 1000 m 的目标,delay 一定会循环混叠(wrap/alias),距离就会“很大误差/跳变”,而速度(你这里 vmax=0)当然看起来没问题。


AFDM 感知函数:afdmSensing_fccr.m

输入建议直接用你们解调输出 RxFreqSignal(AFDM 模式下是 DAFT 域的 Y)以及发射的 TxFreqSignal(DAFT 域的 X)。函数内部会 idaft 回到去 CP 后的时域块,然后按 demo 做 FCCR + Doppler FFT。

matlab
function [Range,Velocity,Angle, RDsum] = afdmSensing_fccr(RxFreqSignal, TxFreqSignal, sensing) % AFDM sensing via time-domain FCCR + slow-time FFT (2D-DFT style) % % RxFreqSignal: Nc x Nsym x Nr (AFDM mode: DAFT-domain output of daft()) % TxFreqSignal: Nc x Nsym (or Nc x Nsym x Nt) (AFDM mode: DAFT-domain symbols) % sensing: struct with fields: % Nc, SCS, fc, CplenSet or CP length, targetNum, rxAntNum % afdm.c1, afdm.c2 % pseudoPeakFlag (optional), overSamplFlag/overSamplNumf/overSamplNumt (optional) % AsetH/AsetV, UEdH/UEdV, verticalFlag (optional for Angle) % % Outputs: % Range [m], Velocity [m/s], Angle [deg], RDsum magnitude map (for debug) c = 3e8; Nc = sensing.Nc; Deltaf = sensing.SCS; fc = sensing.fc; Nr = sensing.rxAntNum; targetNum = sensing.targetNum; % --------- symbol count ---------- if ndims(RxFreqSignal) == 2 K = size(RxFreqSignal,2); Nr = 1; else K = size(RxFreqSignal,2); end % --------- CP length ---------- if isfield(sensing,'CplenSet') && ~isempty(sensing.CplenSet) Ncp = min(sensing.CplenSet(1:K)); % conservative elseif isfield(sensing,'CP') && ~isempty(sensing.CP) Ncp = sensing.CP; else % fallback Ncp = 0; end % --------- sampling / slow-time period ---------- Fs = Nc * Deltaf; Tsym = (Nc + Ncp) / Fs; % include CP/CPP for Doppler grid % --------- bins (two-way radar) ---------- range_bin = c/(2*Fs); % meters per delay sample (two-way) fd_bin = 1/(K*Tsym); % Hz per Doppler bin vel_bin = fd_bin * c/(2*fc); % m/s per Doppler bin (two-way) % warn if max range exceeds CP unambiguous Rmax_unamb = (max(Ncp,1)-1) * range_bin; if isfield(sensing,'targetRange') && ~isempty(sensing.targetRange) if max(sensing.targetRange) > Rmax_unamb && Ncp > 0 warning('AFDM sensing: CP=%d gives Rmax≈%.1f m (two-way). Targets beyond this will alias. Need CP>=~%d for 1000m at Nc=%d,SCS=%.0f.', ... Ncp, Rmax_unamb, ceil(1000/range_bin)+1, Nc, Deltaf); end end % --------- optional oversampling (zero-padding) ---------- if isfield(sensing,'overSamplFlag') && sensing.overSamplFlag == 1 Nfft = sensing.overSamplNumf; Kfft = sensing.overSamplNumt; else Nfft = Nc; Kfft = K; end % --------- pick Tx (assume single TX; if multiple, use the first) ---------- if ndims(TxFreqSignal) == 3 TxX = TxFreqSignal(:,:,1); else TxX = TxFreqSignal; end % --------- reconstruct no-CP time blocks via IDAFT ---------- % Tx blocks: Nc x K TxTimeBlocks = idaft(TxX, sensing.afdm.c1, sensing.afdm.c2); % Rx blocks per antenna: Nc x K x Nr RxTimeBlocks = zeros(Nc, K, Nr); if Nr == 1 RxTimeBlocks(:,:,1) = idaft(RxFreqSignal, sensing.afdm.c1, sensing.afdm.c2); else for ant = 1:Nr RxTimeBlocks(:,:,ant) = idaft(RxFreqSignal(:,:,ant), sensing.afdm.c1, sensing.afdm.c2); end end % --------- FCCR + Doppler FFT ---------- Sf = fft(TxTimeBlocks, Nfft, 1); % Nfft x K RDsum = zeros(Nfft, Kfft); RDant = zeros(Nfft, Kfft, Nr); % complex map for AoA for ant = 1:Nr Rf = fft(RxTimeBlocks(:,:,ant), Nfft, 1); % Nfft x K Z = ifft(Rf .* conj(Sf), Nfft, 1); % delay correlation RD = fft(Z, Kfft, 2); % Doppler FFT RD = fftshift(RD, 2); RDant(:,:,ant) = RD; RDsum = RDsum + abs(RD); end % use only unambiguous delay window (<= CP). If Ncp==0, use full if Ncp > 0 Nwin = min(Ncp, Nfft); else Nwin = Nfft; end RDwin = RDsum(1:Nwin, :); % --------- peak picking (simple + optional pseudo peak neighborhood) ---------- Range = zeros(targetNum,1); Velocity = zeros(targetNum,1); Angle = zeros(targetNum,1); pseudoPeakFlag = isfield(sensing,'pseudoPeakFlag') && sensing.pseudoPeakFlag == 1; res = RDwin; guardR = 2; guardV = 2; % suppression window to avoid picking same mainlobe for p = 1:targetNum [~,idx] = max(res(:)); [rIdx, vIdx] = ind2sub(size(res), idx); Range(p) = (rIdx-1) * range_bin; Velocity(p) = (vIdx - floor(Kfft/2) - 1) * vel_bin; % Angle estimate (single-snapshot beam scan; more stable than "MUSIC with 1 snapshot") ang = 0; if Nr > 1 && isfield(sensing,'AsetH') && isfield(sensing,'AsetV') x = squeeze(RDant(rIdx, vIdx, :)); % Nr x 1 complex snapshot if isfield(sensing,'verticalFlag') && sensing.verticalFlag == 1 Aset = sensing.AsetV; d = sensing.UEdV; score = zeros(size(Aset)); for ii=1:numel(Aset) A = Aset(ii); a = exp(1j*2*pi*(0:Nr-1).'*d*cos(A*pi/180)); score(ii) = abs(a' * x).^2; end else Aset = sensing.AsetH; d = sensing.UEdH; score = zeros(size(Aset)); for ii=1:numel(Aset) A = Aset(ii); a = exp(1j*2*pi*(0:Nr-1).'*d*sin(A*pi/180)); score(ii) = abs(a' * x).^2; end end [~,im] = max(score); ang = Aset(im); end Angle(p) = ang; % suppression / pseudo neighborhood if pseudoPeakFlag r1 = max(1, rIdx-1); r2 = min(size(res,1), rIdx+1); v1 = max(1, vIdx-1); v2 = min(size(res,2), vIdx+1); else r1 = max(1, rIdx-guardR); r2 = min(size(res,1), rIdx+guardR); v1 = max(1, vIdx-guardV); v2 = min(size(res,2), vIdx+guardV); end res(r1:r2, v1:v2) = 0; end end

怎么在你们工程里用(最少改动)

你们现在 AFDM 模式下:

  • ofdmModulate() 的输入 TxFreqSignal 是 DAFT 域 X
  • ofdmDemodulate() 的输出 RxFreqSignal 是 DAFT 域 Y

所以直接在接收端(得到 RxFreqSignal 后)调用:

matlab
[Range,Velocity,Angle,RDsum] = afdmSensing_fccr(RxFreqSignal, TxFreqSignal, sensing);

不需要走 channelEst(),也不需要用 dft_2d(Hest)


你给的场景(vmax=0、Rmax=1000m)我建议你先这样做两步验证

  1. 把目标距离先限制在 ≤ 430 m(比如 100 m、200 m、300 m),你会看到距离立刻变正常
  2. 如果必须做 1000 m:把 CP/CPP 拉到 ≥ 656(你现在 288 明显不够),否则任何基于“循环相关/脉压”的方法都会混叠

如果你愿意,我也可以把这个函数按你们现有 dft_2d() 的输出风格补齐:

  • mesh/imagesc 的 RD 图可视化(带物理轴)
  • slotT 的定义替换成你们框架的一致版本(如果你们符号时长不是 (Nc+CP)/Fs 而是由 frameLen/sensingSymbolNum 决定)

但即使不改,上面这版对你给的 Nc/SCS/CP/fc 是自洽的,并且会直接暴露“CP 不够导致距离混叠”的问题。

質問

image.pngimage.png

把 slotT 的定义替换成你们框架的一致版本,同时我们需要过这个channelEst和图片中国的函数,然后再处理。请更新一下AFDM的处理代码。我们只需要关注距离和速度的感知,angle不需要

Python

python
Nc=3276; SCS=30000; c=3e8 Fs=Nc*SCS range_bin=c/(2*Fs) Rmax=(288-1)*range_bin Fs, range_bin, Rmax (98280000, 1.5262515262515262, 438.034188034188)

思考

Crafting response and edge case handling

I’ll craft the response in Chinese, explaining how to set sensing.afdmTxTF and the limitations of range with CP 288. Even with vmax=0, the 2D DFT will show the peak at zero Doppler. I’ll also include clutter cancellation and implement iterative peak selection with optional pseudo peak flag. No plotting.

Thought for 2m 30s

回答

下面我给你一份独立 AFDM sensing 函数,满足你提出的几点:

  • slotT 按你们框架slotT = sensing.frameLen*1e-3/sensing.sensingSymbolNum;
  • 输入就是 channelEst 的输出 Hest(不改 channelEst)
  • 先过图里那个 clutterCancel 函数(我在函数内部调用)
  • 只输出 Range / Velocity(Angle 不做)
  • 感知算法参考你给的 AFDM demo:核心是“快时匹配滤波/相关 + 慢时 FFT”的 2D 处理;但因为你只给了 Hest(而不是 Y_blocks / s_blocks),我用一个等价的“加权相关”版本来避免 Y./X 带来的距离偏差(AFDM 最常见问题点)。

先提醒一个“必炸点”:CP=288 无法覆盖双程 1000 m(会循环混叠)

你给的:Nc=3276, SCS=30000, CP=288

采样率 Fs=NΔf=98.28 MHzF_s=N\Delta f=98.28\text{ MHz},双程距离 bin:

ΔR=c2Fs1.526 m\Delta R = \frac{c}{2F_s}\approx 1.526\text{ m}

CP=288 的无歧义最大双程距离:

Rmax(2881)ΔR438 mR_{\max}\approx(288-1)\Delta R\approx 438\text{ m}

所以只要你真的放了 >438 m 的目标(比如 1000 m),不管 OFDM/AFDM/OTFS 你怎么做,距离都会 wrap/alias,表现就是“距离误差很大、而速度可能还正常”。
如果你要双程 1000 m,CP 至少要到大约 656(按 full-rate 采样算)。

你现在 vmax=0,所以速度“看着没问题”非常正常;距离这边如果超过 CP 覆盖范围,必错。


AFDM 感知函数:afdmSensing_2d.m

特点:

  • 输入 Hest(你现有 channelEst 输出)
  • 先做 clutterCancel(Hest,sensing)
  • 用“加权相关”的方式做 delay(Range)维:
    • 如果你能提供发端在同一 TF 网格上的参考 TxTF,就做:ifft( |TxTF|^2 .* Hest )
    • 这等价于 demo 里的 ifft( Y .* conj(X) )(避免 1/X 放大导致距离漂)
  • 再做慢时 FFT 得 Doppler/Velocity

你需要在 sensing 里提供一个 TxTF(与 Hest 同维度的 Tx reference 网格,通常就是 channelEst 里用的 TxSignalTF)。不改 channelEst 的话,你在主程序里自己算一次并塞进 sensing.afdmTxTF 就行;如果不给,我会退化成 TxTF=ones(效果可能仍然差)。

matlab
function [Range,Velocity] = afdmSensing_2d(Hest, sensing) % AFDM sensing (2D style): weighted fast-time correlation + slow-time FFT % Input: % Hest: Nf x Nt x Nr (output of channelEst) % sensing: includes fc, SCS, frameLen, sensingSymbolNum, freqSpace, targetNum, % overSamplFlag/overSamplNumf/overSamplNumt (optional), % clutterCancelFlag (optional), % afdmTxTF (recommended): Nf x Nt complex reference grid aligned with Hest % % Output: % Range [m], Velocity [m/s] c = 3e8; targetNum = sensing.targetNum; fc = sensing.fc; Deltaf = sensing.SCS; freqSpace = sensing.freqSpace; % slotT aligned with your framework slotT = sensing.frameLen*1e-3 / sensing.sensingSymbolNum; % --- clutter cancel (as you required) --- % assumes you have: function Hest=clutterCancel(Hest,sensing) if isfield(sensing,'clutterCancelFlag') && sensing.clutterCancelFlag==1 Hest = clutterCancel(Hest, sensing); end % --- sizes / oversampling --- if isfield(sensing,'overSamplFlag') && sensing.overSamplFlag==1 lenf = sensing.overSamplNumf; lent = sensing.overSamplNumt; else lenf = size(Hest,1); lent = size(Hest,2); end rxAntNum = size(Hest,3); % --- Range/Velocity bin spacing --- % Range: use two-way radar scaling (recommended). If you want to keep legacy behavior, % remove the "/2". deltaR = c / (2 * freqSpace * Deltaf * lenf); deltaV = c / (2 * fc * slotT * lent); % two-way % --- get Tx reference aligned with Hest --- % strongly recommended: sensing.afdmTxTF is same size as Hest(:,:,1) if isfield(sensing,'afdmTxTF') && ~isempty(sensing.afdmTxTF) TxTF = sensing.afdmTxTF; % allow TxTF to be Nf x Nt; if oversampled you can zero-pad via fft-size below else % fallback: no weighting (will behave closer to plain IFFT(Hest)) TxTF = ones(size(Hest,1), size(Hest,2)); warning('afdmSensing_2d: sensing.afdmTxTF not provided. Falling back to unweighted processing; range may be inaccurate for AFDM.'); end % use |Tx|^2 weighting to emulate matched filtering: conj(X).*Y = |X|^2 .* (Y./X) = |X|^2 .* Hest W = abs(TxTF).^2; % --- build Range-Doppler map --- initH = zeros(lenf, lent); for antId = 1:rxAntNum Htemp = Hest(:,:,antId); % weighted fast-time correlation (delay compression) % (zero-padding via ifft length = lenf) Zdelay = ifft(W .* Htemp, lenf, 1); % lenf x Nt % slow-time FFT (Doppler) Zrd = fft(Zdelay.', lent, 1).'; % lenf x lent initH = initH + fftshift(abs(Zrd), 2); end % --- peak picking (simple iterative, no angle, no CLEAN) --- Range = zeros(targetNum,1); Velocity = zeros(targetNum,1); residualH = initH; % small suppression window to avoid picking same mainlobe repeatedly guardR = 2; guardV = 2; for k = 1:targetNum [~,idx] = max(residualH(:)); [rIdx, vIdx] = ind2sub(size(residualH), idx); Range(k) = (rIdx-1) * deltaR; Velocity(k) = (vIdx - floor(lent/2) - 1) * deltaV; r1 = max(1, rIdx-guardR); r2 = min(size(residualH,1), rIdx+guardR); v1 = max(1, vIdx-guardV); v2 = min(size(residualH,2), vIdx+guardV); residualH(r1:r2, v1:v2) = 0; end end

你需要在主程序里补的唯一一件事:给 sensing.afdmTxTF

因为你坚持“不改 channelEst”,但 AFDM 下距离想稳,必须知道 Tx 在同一网格上的幅度权重(至少 |X|^2)。
最简单做法:你在调用 channelEst(TxFreqSignal,RxFreqSignal,...) 之前,按 channelEst 里 AFDM 分支同样的方式算一遍 TxTF,然后存进 sensing。

如果你现在传进 channelEst 的 TxFreqSignalDAFT 域X(Nc×Nt),那 TxTF 可以这样算(与你们 channelEst 的 AFDM 分支保持一致):

matlab
% X_daft = TxFreqSignal(:,:,txAntId) or TxFreqSignal (Nc x Nt) txTime = idaft(X_daft, sensing.afdm.c1, sensing.afdm.c2); TxF_ofdm = fft(txTime, sensing.Nc, 1)/sqrt(sensing.Nc); TxF_ofdm = circshift(TxF_ofdm, sensing.Nc/2, 1); % 若你们channelEst里做了这个,就保持一致 TxTF = TxF_ofdm(sensing.fpattern, sensing.tpattern); sensing.afdmTxTF = TxTF; % 维度应与 Hest(:,:,1) 一致

如果你们 Hest 只在 fpattern/tpattern 上估计,那 afdmTxTF 也必须是同样的 pattern,否则维度对不上。


为啥这个能解决你们“距离漂”的问题(但速度仍与 OFDM 类似)

你们现有 Hest 在 AFDM 模式下本质上是 Hest = Y./X。当 |X| 有起伏/存在很小点时,Y./X 会把噪声和 ICI 放大,最先破坏 delay 的“相位一致性/相关峰”
我这里用 |X|^2 .* Hest 等价于 conj(X).*Y(匹配滤波),避免 1/X 放大,这就是 demo 的核心思路迁移到你“必须经过 channelEst”的限制下的折中实现。


下一步建议(你们现在这个参数下必须做)

  1. 先把目标距离限制到 ≤ 438 m 跑通验证(否则必 alias)
  2. 若确实需要 1000 m:把 CP/CPP 拉到 ≥ 656(基于 full-rate 采样)
  3. sensing.afdmTxTF 后再看距离误差——通常会明显改善

你要是把你们 fpattern 的长度(等效 Nf)和 tpattern 的长度(Nt)发一句,我也可以顺手帮你把 deltaR/deltaV 的标定再核一遍,确保跟你们现有 OFDM/OTFS 输出完全一致(尤其是是否需要 /2 这一点)。

このQ&Aを共有