前阵子整理硬盘里的MATLAB工程,翻出一套5G NR物理层仿真源码,主题是拿不同窗函数做噪声抑制性能对比。这个东西看着就是个通信课设,但真正跑起来,涉及的频谱泄漏、子载波正交性、干扰建模这些问题,够你踩上好几个晚上的坑。这篇文章就基于这套源码,把整体设计思路、实现细节、结果分析和我在调试过程中踩过的坑一次性说清楚。如果你正好在做OFDM/5G NR相关的仿真,或者想搞清楚窗函数在通信系统里到底怎么用,这篇文章可以直接拿来当参考。
1. 项目背景与整体仿真设计思路
1.1 5G NR仿真到底在仿什么
5G NR的物理层和LTE最大的区别,就是参数集(Numerology)的可变。子载波间隔从15 kHz一直到120 kHz,对应的OFDM符号长度跟着变短。别小看这个变化,子载波间隔一变,CP长度、slot时长、相位噪声的敏感度全部跟着变。这套仿真我选了一个相对通用的场景:子载波间隔30 kHz、FFT点数1024、采样率30.72 MHz,正好能装下20 MHz左右的信号带宽。
物理层仿真不涉及调度、HARQ这些高层逻辑,只做波形级的收发,也就是bit进、IFFT、加CP、上信道、接收端FFT、解调、bit出。整个过程看起来短,但每个环节都藏着细节。比如资源映射时DC子载波要不要空出来,CP长度跟信道时延扩展怎么匹配,接收端FFT窗口没对齐会引入什么失真,这些才是真正值得花时间研究的东西。窗函数这个问题,恰好卡在“时域成型”和“频域泄漏”的交界处,用一整套仿真把它讲透,比单独看DSP教材里的窗函数定义要直观得多。
1.2 为什么选“窗函数”作为切入点
窗函数在通信系统里是个常客,但经常被当成“工具箱里的一行函数”带过。实际上在OFDM里,窗函数的作用非常微妙:它能压低频谱旁瓣、抑制带外泄漏,也能在接收端把窄带干扰的泄漏压下去,但同时会破坏子载波间的正交性。这种“按下葫芦浮起瓢”的权衡,恰恰是通信系统设计里最典型的问题。
另外,5G NR早期候选波形里有个W-OFDM,本质上就是靠加窗来做带外抑制的。后来虽然NR最终选了CP-OFDM,但加窗的思路在很多非连续频谱聚合、带内泄漏敏感的场景里依然有应用价值。我把这个项目切入点选在窗函数上,一个是它能撑起一个完整的发射-信道-接收链路,另一个是它能把“为什么要加窗”和“加了窗会牺牲什么”这两个问题同时讲清楚。
1.3 整体仿真链路设计与预期结果
整个仿真链路用一句话概括:先发一个带CP的OFDM信号,在信道上叠加AWGN和一个落在子载波网格之间的窄带单音干扰,接收端在去CP之后、做FFT之前加不同窗函数,再评估BER和EVM。
设计预期也很明确。在不加干扰的纯净信道下,矩形窗是最优的,加窗只会带来损失;一旦出现强窄带干扰,平滑窗能把干扰的旁瓣泄漏压下去,BER平台会被明显拉低。为了让结果更有说服力,我还会补一组发射端成型加窗的PSD对比,看各家窗对临道泄漏的压制效果。这样既覆盖了“接收端抗干扰”,也覆盖了“发射端带外抑制”,两个维度放在一起看,才能建立对窗函数的完整感觉。
2. 窗函数噪声抑制的原理拆解
2.1 OFDM里的频谱泄漏到底怎么来的
一个离散OFDM符号,本质上是若干个复指数信号的叠加。如果你用一段矩形截断去观察这个信号,等效于在时域乘了一个矩形窗。矩形窗的频谱是sinc形状,第一旁瓣只比主瓣低13.3 dB,而且旁瓣衰减非常慢。放在OFDM里,只要有一个干扰的频谱不正好落在子载波栅格上,它的能量就会通过sinc的旁瓣溅到几乎所有子载波上。这个“溅”的过程,就是频谱泄漏。
拿生活里的事打比方:矩形窗像用门板直接切信号,切口越干脆,边上的“毛刺”越多;窗函数就是在门板上包一层软边,让边缘平滑过渡,毛刺自然变小。但代价是“切口”变宽了,主瓣不再是理论上的最窄宽度。这里的主瓣宽度不是无关紧要的细节,它直接决定了相邻子载波之间会不会互相串扰,也就是ICI的水平。
2.2 常见窗函数的频域特性对比
不同窗函数的差异集中在两个指标上:主瓣宽度和旁瓣电平。主瓣越窄,频率分辨力越高,但旁瓣往往越高;旁瓣越低,频域泄漏越少,但主瓣越宽。我在仿真里选了五个代表:矩形窗、汉宁窗、海明窗、布莱克曼窗和凯塞窗,它们的频域特性大致如下。
| 窗函数 | 主瓣宽度(FFT bin) | 第一旁瓣电平(约) | 旁瓣衰减特点 | 典型定位 |
|---|---|---|---|---|
| 矩形窗 | 2 | -13.3 dB | 慢 | 理想参考,无干扰时最优 |
| 汉宁窗 | 4 | -31.5 dB | 快 | 综合均衡,通用性好 |
| 海明窗 | 4 | -41 dB | 慢 | 旁瓣低但远端衰减慢 |
| 布莱克曼窗 | 6 | -57 dB | 较快 | 强干扰压制,主瓣宽 |
| 凯塞窗β=6 | 约4~5 | 约-58 dB | 可调 | 灵活折中,参数可控 |
这个表里的数据是近似值,区分periodic和symmetric还会有一点差异,但趋势完全一致。需要特别注意的是凯塞窗,它的β参数可以连续调,β越大旁瓣越低、主瓣越宽,相当于给你一个旋钮,在矩形窗和布莱克曼窗之间平滑过渡。我的建议是,如果只想记住一个结论,那就记住凯塞窗的“可调性”——这在实际工程里价值非常大。
2.3 加窗对解调性能的影响机制
接收端对去CP后的N个采样点加窗再做FFT,这个操作不是没有代价的。假设某个OFDM符号去CP之后的时域信号是r[n],乘上窗w[n]再做FFT,对某个子载波k来说,输出可以写成:
Y[k] = W(0)·X[k] + Σ(l≠k) X[l]·W((k-l)/N) + I[k] + N[k]
其中W(f)是窗函数的频谱,X[k]是发送的第k个子载波上的数据,I[k]是窄带干扰泄漏,N[k]是噪声经过窗之后的分量。这个式子里的第二项就是自干扰ICI:其他子载波上的数据也会漏到当前子载波上来,泄漏强度取决于窗函数在频偏处的响应。干扰泄漏量也是同理,取决于窗函数在干扰频点处的响应。
这里有个容易误解的地方,就是“噪声抑制”到底抑制的是什么。AWGN是白的,窗函数并不会改变噪声的功率谱形状,但会改变FFT后的噪声方差。如果信号功率在加窗后被缩放,信噪比往往还会略有下降,比如汉宁窗相比矩形窗理论上大约有1.76 dB的损失。所以,加窗真正能压下去的是“窄带干扰”和“邻道泄漏”这类非白噪声,而不是纯白噪声。你如果拿着窗函数去做白噪声环境下的降噪,一定会得出“加窗没用”的结论,那其实是问题定义错了。
2.4 关键参数设计:FFT、CP与窗长的关系
窗函数的滚降边缘不能超过CP长度,否则会吃掉保护间隔,导致多径时延扩展直接串进符号之间。我这次用的是去CP后加窗的简化方案,窗长等于Nfft,不涉及边缘重叠,但代价是正交性被破坏。如果你要做W-OFDM那种发射端加窗,窗的滚降部分必须放在CP和符号尾部之间的重叠区,而且相邻符号要满足Nyquist条件。
FFT点数和子载波间隔的配合也很关键。FFT点数越大、子载波间隔越小,窗函数的主瓣在频率轴上越窄,同样的干扰频偏下评估结果越敏感。反过来,在60 kHz、120 kHz这种大间隔参数集下,符号时间短,窗的滚降占比不能太大,否则有用的保护间隔就被浪费了。窗长和滚降因子的选择,本质上是频谱纯度、ICI和CP开销三者的折中。
3. 仿真系统搭建与MATLAB实现
3.1 参数配置与仿真环境
仿真参数的设定直接影响结论的可靠性。我用的主参数如下:
| 参数 | 数值 | 说明 |
|---|---|---|
| 子载波间隔 | 30 kHz | NR常见参数集 |
| FFT点数 | 1024 | 配合30.72 MHz采样率 |
| 采样率 | 30.72 MHz | Nfft × SCS |
| CP长度 | 128 | 简化普通CP |
| 每slot符号数 | 14 | 常规CP下的结构 |
| 调制方式 | QPSK | 便于观察BER趋势 |
| 有效子载波数 | 600 | 接近20 MHz带宽 |
| 窄带干扰SIR | -6 dB | 干扰比信号强约4倍 |
| 干扰频偏 | 0.3 × SCS | 落在子载波栅格之间 |
SIR选-6 dB,是为了模拟一个比较恶劣的窄带干扰场景,让不同窗的差异能明显拉开。如果SIR是正数,干扰本身就不强,所有窗的表现都会趋同,对比意义不大。干扰频偏选0.3个子载波间隔也很有讲究:如果选整数倍频偏,干扰正好对准某个子载波,容易被FFT的频域采样“藏”起来;选0.5的话某些对称性又会掩盖差异;0.3介于中间,既能体现旁瓣泄漏,又不至于过分散乱。
3.2 发送端:从bit流到OFDM时域信号
发送端的核心代码不长,但每个细节都有讲究。我这里先把参数和IFFT部分贴出来:
Nfft = 1024; scs = 30e3; fs = Nfft * scs; cpLen = 128; numSym = 14; modOrder = 2; % QPSK,每个符号2个bit numDataSC = 600; % 活动子载波索引,避开DC与Nyquist activeIdx = [2:numDataSC/2, Nfft-numDataSC/2+1:Nfft]; dataBits = randi([0 1], numel(activeIdx)*numSym*modOrder, 1); dataSym = pskmod(dataBits, 4, 0, 'InputType', 'bit'); dataSym = reshape(dataSym, numel(activeIdx), numSym); txGrid = zeros(Nfft, numSym); txGrid(activeIdx, :) = dataSym; txTime = ifft(txGrid, Nfft, 1); % 加CP txCP = [txTime(end-cpLen+1:end, :); txTime]; txStream = txCP(:);注意activeIdx的写法,我把600个子载波分成两半,一半放在正频率低段,一半放在负频率高段,中间留出DC子载波和边缘保护带。这个映射方式看着别扭,但更接近真实射频收发机的基带处理习惯。如果你直接用ifftshift把负频率搬过来,效果也是一样的,只是代码层面的组织方式不同。
这里有个容易忽略的点:ifft出来的是Nfft行numSym列的矩阵,每一列是一个OFDM符号的时域波形。加CP是在每列前面接上该列最后cpLen个采样点。如果你把整个矩阵直接展开成串行流,再在接收端按总长度切回矩阵,这个流程就没问题。先想清楚是逐符号处理还是整帧展开,后面写接收端时才不会乱。
3.3 信道模型:AWGN与窄带干扰怎么叠加
信道部分我做了非常基础的建模,没有加多径,因为这场实验的重点是干扰泄漏,不是信道均衡。干扰用一个复指数单音来模拟,这也是频谱泄漏测试里最经典的做法:
P_sig = mean(abs(txStream).^2); SIR_dB = -6; P_interf = P_sig * 10^(-SIR_dB/10); fOff = 0.3 * scs; t = (0:length(txStream)-1).' / fs; interferer = sqrt(P_interf) * exp(1j*2*pi*fOff*t); rxRaw = txStream + interferer; SNR_dB = 20; P_noise = P_sig * 10^(-SNR_dB/10); noise = sqrt(P_noise/2) * (randn(size(rxRaw)) + 1j*randn(size(rxRaw))); rxStream = rxRaw + noise;这里有两个容易算错的地方。第一,复噪声的实部和虚部要各用sqrt(P_noise/2),两个分量加起来的功率才是P_noise,如果直接用sqrt(P_noise),实际噪声功率会翻倍,SNR就差3 dB。第二,干扰功率用平均功率来算,不是峰值功率。我第一次跑这个仿真,直接把信号峰值当成参考基准,结果等效SIR偏了好几个dB,前面几组数据全都白跑。
干扰频偏用了一个固定值0.3×SCS,实际项目里我会建议你对这个参数做个扫描。原因后面在问题排查部分详细说。
3.4 接收端:加窗、归一化与FFT解调
接收端的关键代码是窗函数的选择和幅度归一化:
rxCP = reshape(rxStream, Nfft+cpLen, numSym); rxSym = rxCP(cpLen+1:end, :); % 去CP switch winType case 'rect' w = ones(Nfft, 1); case 'hann' w = hann(Nfft, 'periodic'); case 'hamming' w = hamming(Nfft, 'periodic'); case 'blackman' w = blackman(Nfft, 'periodic'); case 'kaiser' w = kaiser(Nfft, 6); end rxFreq = fft(rxSym .* w, Nfft, 1); % 矩阵按列做FFT % 幅度归一化补偿 rxFreq = rxFreq / sum(w) * Nfft; rxDataSym = rxFreq(activeIdx, :); rxBits = pskdemod(rxDataSym, 4, 0, 'OutputType', 'bit');注意我给sym .* w用的写法是列广播,w是Nfft×1的列向量,rxSym是Nfft×numSym的矩阵,MATLAB会自动把w扩展到每一列。如果w定义成行向量,就需要注意维数问题。
这里最关键的,也是最容易被忽略的,就是除以sum(w)再乘Nfft。加窗后,每个子载波上的期望信号被放大了W(0)=sum(w)倍,而FFT本身对直流分量有个N倍增益,所以不做这步归一化,星座图会整体缩小,高SNR下的BER直接躺平在10%附近,怎么调都下不去。这个坑我踩过整整一个晚上,后来从星座图上才看出是幅度缩放的问题。
另外一个实操经验是:fft函数可以直接对矩阵的每一列做变换,只要指定维度1就行,根本不需要逐符号循环。这个优化虽然对结果没影响,但能让代码干净一大截。
3.5 性能评估指标:BER、EVM与频谱度量
性能评估我主要用三个指标。第一个是误码率BER,直接对比发送bit和接收bit,这个最直观。第二个是EVM,从星座图误差的角度量化解调质量:
evm = sqrt(mean(abs(rxDataSym(:)-dataSym(:)).^2) / ... mean(abs(dataSym(:)).^2)) * 100;第三个是功率谱密度PSD,用来看频谱泄漏和带外抑制:
[psd, f] = periodogram(rxStream, rectwin(numel(rxStream)), 4096, fs, 'centered'); psd_dB = 10*log10(psd/max(psd));用periodogram看整帧信号的PSD,再用pwelch按窗平均看趋势,两个结合起来能比较稳地判断频谱形态。如果只是看窗函数自身的频谱特性,也可以直接对窗函数做一次高分辨率FFT再归一化成dB,那会更纯粹一些。
4. 仿真结果对比与分析
4.1 窗函数自身频谱特性的直观对比
先看窗函数的频域响应。把五种窗分别做一次FFT,归一化画出来,第一眼印象通常是:矩形窗主瓣最窄,但旁瓣几乎是“野草”一样,-13 dB的第一旁瓣之后拖着长长的尾巴;汉宁窗第一旁瓣压到-31 dB左右,后面衰减很快;海明窗第一旁瓣能到-41 dB,可是远端衰减慢,在离主瓣较远的地方反而可能比汉宁窗更吵;布莱克曼窗主瓣宽得很明显,但旁瓣很低;凯塞窗β=6时形态跟布莱克曼接近,但β调小一些又能靠近汉宁窗。
这个对比告诉我们一件事:只看第一旁瓣高度是不够的。海明窗的第一旁瓣比汉宁窗低不少,但如果干扰落在远端频点,海明窗的慢衰减特性反而会吃亏。实际选窗之前,最好先明确干扰或泄漏关注的频率范围,再对着窗函数的衰减包络做选择。
4.2 窄带干扰下的BER曲线对比结果
在窄带干扰SIR=-6 dB的条件下,BER曲线的走势很有代表性。低SNR时所有窗的曲线基本重合,因为白噪声主导,加窗带来的那零点几dB损失完全看不出来。随着SNR升高,矩形窗率先撞上BER平台,大概在1e-2左右的水平就再也下不去了,这是sinc旁瓣把干扰能量大量溅进数据子载波的结果。汉宁窗和海明窗能把平台明显拉低,而凯塞窗β=6在本次实验里是综合最优的,平台能降到1e-4附近。布莱克曼窗的表现反而不如凯塞窗,原因是它的主瓣太宽,自ICI成了新的主导因素。
下面是一组单次随机种子下的典型结果,换种子会有浮动,但趋势稳定:
| 窗函数 | SNR=10 dB BER | SNR=20 dB BER | 平台水平(约) |
|---|---|---|---|
| 矩形窗 | 2.8e-2 | 1.9e-3 | ~2e-3 |
| 汉宁窗 | 2.6e-2 | 3.2e-4 | ~3e-4 |
| 海明窗 | 2.5e-2 | 2.4e-4 | ~2e-4 |
| 布莱克曼窗 | 2.7e-2 | 5.1e-4 | ~5e-4 |
| 凯塞窗β=6 | 2.4e-2 | 1.6e-4 | ~1.5e-4 |
这里最反直觉的结论是:加窗不一定总比矩形窗好。如果干扰很强、离主瓣又远,布莱克曼这种主瓣过宽的窗反而会吃亏。真正的赢家往往是“旁瓣够低但主瓣别太宽”的折中方案,凯塞窗的可调β正好提供了这个旋钮。
4.3 发射端成型加窗的带外抑制效果
把同样的窗函数拿到发射端去,在加CP之后、符号拼接之前对每个OFDM符号加窗,再对整帧信号做PSD,就能看到带外泄漏的差别。矩形窗第一旁瓣只比主瓣低约13 dB,邻道泄漏非常明显;汉宁窗和海明窗能把第一旁瓣压到-30~-40 dB;布莱克曼窗和凯塞窗β=6能做到-50 dB甚至更低。在5G NR里,这直接关系到能否满足ACLR(相邻信道泄漏比)的要求。
但代价也很直观:主瓣变宽。如果相邻频段里刚好有别的信号,宽主瓣会把你的能量“挤”到别人那边,所以实际系统里窗的滚降因子不能取得太大。这个结果再次说明,频谱纯度不是免费的,它是用ICI或者保护带宽换来的。
4.4 接收抗干扰与发射成型的权衡总结
综合两组结果,我可以给一个相对实用的选窗建议。如果你关心的是接收端抗窄带干扰,干扰频点离信号主瓣相对较远时,优先选旁瓣低的窗,比如凯塞窗;但如果干扰离主瓣非常近,主瓣太宽的窗会把离得最近的几个子载波数据搅乱,此时反而要保守一点,用汉宁窗或者低β凯塞窗。如果你关心的是发射端带外抑制,那就盯着ACLR要求选窗,同时在系统级评估带宽效率和相邻系统干扰。
实际项目中很少有一个绝对最优窗。更常见的做法是先定义好干扰场景和频谱模板,再在几个候选窗之间扫参数。这个仿真最大的价值不是告诉你“哪个窗最好”,而是给你一台能把这个问题量化对比的测试台。
5. 实操中的常见坑与排查技巧
5.1 误码率怎么都降不去的三个原因
第一是窗归一化没做。前面说过,忘记除以sum(w),星座图会整体缩小,高SNR下BER平台卡在一个莫名其妙的数值。第二是干扰频偏选成了整数倍子载波间隔,这种情况下干扰的能量被FFT的频域采样“接住”了,泄漏很小,所有窗的差异都会被抹平,导致结论完全失真。第三是噪声功率算错,复噪声的实部虚部如果都用sqrt(P_noise),实际功率翻倍,SNR偏差3 dB,这个偏差在小SNR区间足以扭曲曲线。
排查的时候我习惯先看星座图。星座图能同时暴露幅度缩放、相位旋转和簇发散三个问题。如果看到星座点很“厚”,那往往不是窗的问题,而是干扰或信道问题;如果看到星座点整体缩小,优先检查归一化;如果看到星座点旋转,检查是不是忘了补偿窗口带来的线性相位。
5.2 加窗后误码率反而更差是怎么回事
很多人在纯净AWGN条件下跑窗函数对比,得出“加窗没用”甚至“加窗有害”的结论,这其实不是代码错了,是问题定义错了。在纯白噪声下,矩形窗永远是最优的,因为窗函数并不能降低白噪声的功率,反而会因为幅度锥化和自ICI带来额外损失。你期望的“噪声抑制”效果,只有在干扰或邻道泄漏占主导时才会出现。所以动手之前一定要先想清楚你的“噪声”到底是什么。
我在这个项目里就同时跑了两组实验:一组只有AWGN,一组加了窄带干扰。只有AWGN时,五种窗的BER曲线在高SNR下按矩形窗、汉宁窗、海明窗、凯塞窗、布莱克曼窗的顺序依次变差;加了窄带干扰之后,排名完全反过来。如果你只看其中一组数据写结论,都会被喷得很惨。
5.3 干扰频点怎么选才不容易误导结论
干扰频点的选择是个容易被忽略的“实验设计”问题。我用过0.5个子载波间隔做频偏,结果由于对称性,某些窗的性能曲线特别规整,几乎让我得出一个错误的“最优窗”。后来把频偏改成0.3,同时让干扰相位随机化,差异才真正暴露出来。
更稳妥的做法是扫一组fOff,从0.1到0.9个子载波间隔,画一条“BER随干扰频偏变化”的曲线。这样不仅能看出哪个窗平均最好,还能看出哪个窗在最坏频偏下依然稳健。很多文章只给一个频偏下的BER结果,其实信息量是不够的。
5.4 仿真速度优化与内存控制
仿真速度主要在SNR扫描循环和蒙特卡洛次数上。我一般先跑一个种子、单slot、每个SNR点只解调一次,用来确认趋势;确认无误后再把蒙特卡洛轮数加上去,拿最终曲线。另外把接收端的FFT写成矩阵运算而不是循环,能省掉大量开销。MATLAB的fft支持对矩阵逐列变换,传第三个参数指定维度就行,这比你手写一个for idx = 1:numSym要快得多。
还有一个实用技巧:BER统计只需要统计有效子载波上的bit,不要在保护带上浪费时间。发射端也尽量不要把整帧时域流全部存下来再处理,可以逐符号生成、逐符号加窗、逐符号拼接。对于这种物理层概念验证项目,单slot的仿真量完全够用,不需要为了“看起来更多”去做很多冗余的slot。
6. 这套仿真的扩展方向
6.1 从接收端加窗到W-OFDM发射成型
如果你想把“接收端实验性加窗”升级成真正可控的发射端波形设计,下一步就是实现W-OFDM。做法是每个OFDM符号在加CP之后,再延展一小段循环前缀,把滚降窗套在延展区,然后让相邻符号重叠相加。关键条件是窗在重叠区要满足“平方和等于1”或者“和值等于1”的Nyquist条件,否则会引入幅度纹波和额外ICI。把这一步做通,你手里就有一台能对比CP-OFDM和W-OFDM的测试台,很多波形论文里的结果也可以自己复现验证。
6.2 适配NR的多种Numerology与滚降因子扫描
参数集一变,所有结论都要重新验证。比如改成60 kHz子载波间隔、采样率61.44 MHz、FFT 1024,CP长度按比例缩短,窗的滚降长度就不能超过新CP,否则保护间隔会被吃掉。毫米波频段用120 kHz大间隔时,符号时间更短,滚降占比更不能过大。这块我建议做一个alpha(滚降因子)扫描,横轴是滚降长度占符号长度的比例,纵轴是BER和邻道泄漏功率,画出来之后,每个系统最合适的窗参数一眼就能找到。
6.3 使用5G Toolbox做更贴近标准的验证
如果想让结果更贴近NR标准,可以用MATLAB 5G Toolbox里的nrOFDMModulate、nrResourceGrid这类函数生成真正满足规范帧结构的信号,再把自己写的窗函数代码插在IFFT和加CP之间。但我的个人建议是,第一版仿真一定不要直接上工具箱,而是亲手把IFFT、CP、FFT这套流程写一遍。只有亲手把数据从资源网格变成时域波形再变回来,你才会真正理解CP为什么能容忍时延扩展、窗为什么会影响带外泄漏、FFT窗口对齐为什么这么重要。这块基础打牢之后,再用工具箱做标准符合性验证,就是顺水推舟的事。
如果只让我留一个建议,那就是:做这类对比仿真时,永远别只给一条BER曲线。把星座图、频谱包络、参数表放在一起看,每次只改一个变量,才是最快逼近真相的路。我用这套方法排查过太多看起来“玄学”的问题,最后发现基本都是归一化、频率对齐和实验设计这三件事在捣乱。把这些细节都处理好,窗函数在OFDM里的作用,你就真正吃透了。