2026/9/20 12:30:07

基于Python的AFDM波形仿真:从chirp基理论到6G候选波形实现

基于Python的AFDM波形仿真:从chirp基理论到6G候选波形实现 简介面向无线通信、信号处理与6G技术研究者的AFDM波形理论解析源码包系统展示仿射域多载波波形的数学建模与仿真思路重点解决高多普勒频移和大时延扩展下OFDM性能下降的问题适合高校研究生、通信算法工程师快速建立对候选波形的整体认知。zip压缩包共8个文件以Python仿真脚本为主体配合依赖说明与仿真结果txt以及时域波形图、收发端星座图png和轻量配置文件整体仅306KB结构精简便于直接运行与二次修改适合课程设计或科研复现参考。已有245人学习下载。通过源码可复现AFDM连续时间信号模型、离散化矩阵表示、子载波干扰特性及时延-多普勒域分析还能对照发射与接收星座图检验调制解调流程并结合高铁、无人机、卫星通信等场景理解其应用价值对于后续参数优化、低复杂度算法探索或6G融合研究是一份可扩展的代码起点。 AFDM这个缩写最近在通信波形研究圈子里被提到的频率越来越高。全称是Affine Frequency Division Multiplexing仿射频率分复用属于面向6G候选波形讨论时绕不开的一类新方案。我最近把AFDM从理论推导到可运行的Python仿真工程完整过了一遍顺手整理了一套项目源码这篇文章就围绕这段经历来写不做论文式复述而是把AFDM波形理论解析中的关键点、我在写源码时如何把公式落成代码以及复现过程中踩过的坑一次性讲清楚。适合正打算看6G波形方向、或者想拿AFDM练手做物理层仿真的朋友参考。先说结论AFDM不是玄学它本质上是把OFDM那组复数指数基换成了chirp基再用两个额外参数去主动适配信道在时延-多普勒维度上的扩展特性。好处是高速移动场景下不依赖外部级联编码也能拿到分集增益。这套思路和OTFS有相似之处但AFDM保留了OFDM的单载波块结构复杂度可控实际工程友好度更高。下面按理论、源码、踩坑三个角度来聊尽量把每个“为什么”都交代清楚。1. 为什么还要啃一种新波形1.1 OFDM在高速场景下的真实瓶颈OFDM统治了4G和5G的大部分物理层设计在LTE、Wi-Fi里面几乎是绝对主力。它的思路很成熟把宽带信道拆成一堆窄带平坦子信道用循环前缀吸收时延扩展接收端做一次FFT就能均衡。问题在于这套设计默认信道在一个OFDM符号块内基本不变。一旦终端或反射体高速运动多普勒频移会把信道在单个符号块内“搅动”起来子载波之间的正交性就会崩坏产生载波间干扰。实际体验就像高铁上视频会议断断续续不是网速不够而是信道变化太快OFDM在时间维度上收到的符号已经被打乱了。要硬扛这个问题要么缩短符号长度牺牲频谱效率要么加密导频做插值均衡但代价都很大。1.2 AFDM的差异化定位用chirp基替代复指数基AFDM属于多载波调制家族但它在数据块上做的不是传统傅里叶变换而是离散仿射傅里叶变换。名字里这个“仿射”体现在变换核上除了常规的线性相位项还引入了两个二次相位项。二次相位在物理上就等价于频率随时间线性扫描也就是chirp信号。用chirp基承载数据的直接结果是发送信号在时域上有一种“扫频”特性它能主动匹配信道在时延-多普勒平面上的能量散布。和OTFS一样AFDM也在时延-多普勒域上获得分集但和OTFS的二维网格变换不同AFDM只在一维序列上做变换接收机结构更接近OFDM去CP、做DAFT、单抽头均衡复杂度没有跳到二维匹配滤波的量级。这一点是我后来写代码时体会最深的。1.3 这套仿真工程能拿来做什么源码工程不是论文空壳它解决几件实际事一是验证AFDM在高斯信道和双选择性信道下的理论误码率二是和OFDM在相同频谱效率、相同信噪比定义下做直观对比三是给后续扩展留接口比如接信道估计、做MIMO扩展、看PAPR优化。既可以用它复现论文曲线也能当课程设计的参考实现。2. AFDM理论必须吃透的几个点2.1 DAFFT变换到底在做什么AFDM的调制核心是离散仿射傅里叶变换。发射端对频域符号序列做逆变换生成时域发送序列简化后的表达式为s[n] (1 / sqrt(N)) * sum_m x[m] * exp(j * 2 * pi * (c1 * n^2 n * m / N c2 * m^2))这里N是子载波数也就是一个块内的符号数。对比OFDM的IDFT多出了两项c1乘n的平方c2乘m的平方。n是时域采样序号m是数据序号。c1项让每个时域采样点携带一个随时间变化的二次相位这是chirp特性的来源c2项则是在数据域上做二次相位旋转。接收端解调就是反过来先做DAFT正变换再对每个子载波做单抽头均衡。把这个变换写成矩阵形式就是s A^H * x其中A是DAFT矩阵。由于核函数是正交的A^H * A等于单位阵理论上只要参数选择正确调制解调就是无损可逆的。这也是我最早验证对错的一个入口先不看信道直接让信号过调制解调链路如果输出和输入不一致说明c1、c2或者矩阵构造有问题。2.2 chirp参数c1、c2与信道之间的对应关系两个chirp参数不是拍脑袋取的。通过推导输入输出关系可以得出等效信道矩阵的循环结构c1和c2分别影响矩阵在“多普勒方向”和“时延方向”上的采样位置。直观理解可以认为这对参数相当于在时延-多普勒平面上给信号打了一个旋转角选对了角度信号的能量才能铺满整个分集维度。实际操作上c2一般取有理数常用形式是c2 (N - 1) / N或者按论文推导取特定分数c1则和信道多普勒扩展的整数部分相关需要根据最大多普勒频移估计来配置。更严格的条件是让2N倍的c1与N互质或满足论文里给出的全分集约束。这里我不展开全部推导但给一个结论如果你的BER曲线不见改善先查c1有没有跟着信道多普勒走这是最常见的翻车原因。2.3 全分集增益为什么会存在全分集这句话听起来玄实际含义是“一个符号经历的所有独立衰落路径在接收端都能被收集起来并有效合并”。OFDM把符号摊在多条子载波上每条子载波看到的信道可能很差AFDM因为chirp基的存在让同一个符号在时频平面上扩散开多条路径的副本会在接收端被加权合并等效信道变成一个多维叠加深度衰落的概率显著下降。打个比方你在演唱会现场听主唱唱歌直达声和四周墙壁的反射声到达耳朵的时间不一样但大脑会自动把这些声音综合起来就算某个方向被柱子挡住你还是能听清。AFDM就相当于给接收机装了一副“定向收集”的耳朵把多径从干扰变成了可用资源这是它在高速场景下比OFDM能打的核心原因。3. 项目源码从公式到可跑的仿真3.1 工程结构与运行方式这套源码用Python加NumPy实现核心逻辑依赖库很少便于阅读和二次修改。目录结构很简单afdm_sim/ ├── modem.py # AFDM调制解调、DAFT矩阵生成 ├── channel.py # 双选择性信道生成 ├── ber_sim.py # BER仿真主流程 ├── compare_ofdm.py # OFDM对比脚本 └── run_sim.py # 一键运行入口建议第一次跑的时候直接把N设成64或128信噪比从0dB扫到20dB。不要一上来就用1024个子载波因为后面的实现默认用矩阵乘N大了内存会很尴尬。小N先把流程跑通再切换到快速算法这是最省时间的路线。3.2 调制解调核心代码实现调制端最直观的实现就是根据DAFT逆变换公式逐项累加。我早期写的版本是这样的import numpy as np def afdm_modulate(x, N, c1, c2): AFDM调制频域符号序列 - 时域发送序列 x: 长度为N的复数符号序列 n np.arange(N) s np.zeros(N, dtypecomplex) for m_idx in range(N): phase np.exp(2j * np.pi * ( c1 * n**2 n * m_idx / N c2 * m_idx**2 )) s x[m_idx] * phase return s / np.sqrt(N)这段代码里最容易写错的是相位项的符号和归一化系数。DAFT矩阵本身是幺正的所以调制端要除以根号N接收端也要除以根号N两边对称才能保证能量守恒。如果少除一个根号N发射功率会比预期高一倍后面所有信噪比都会对不上这是我在仿真里查出过的问题之一。解调就是DAFT正变换本质上只需要把指数项的符号取反def afdm_demodulate(r, N, c1, c2): AFDM解调时域接收序列 - 变换域符号序列 n np.arange(N) X_hat np.zeros(N, dtypecomplex) for m_idx in range(N): phase np.exp(-2j * np.pi * ( c1 * n**2 n * m_idx / N c2 * m_idx**2 )) X_hat[m_idx] np.sum(r * phase) return X_hat / np.sqrt(N)注意这里假设接收端的c1、c2和发射端完全一致。实际工程里这是换算出来的信道参数需要考虑估计误差但仿真阶段先定死参数便于专注理解信道的信噪比影响。后面接信道估计时再把c1、c2变成动态配置即可。3.3 双选择性信道的仿真设计双选择性信道要同时体现频率选择性和时间选择性工程上常用多径抽头模型来实现。第p条径有独立的复增益、时延和多普勒频移离散化后对发送符号逐点叠加def apply_channel(s, params, fs): params: 每条径的 (幅度, 时延采样点数, 多普勒频移Hz) fs: 采样率 N len(s) r np.zeros(N, dtypecomplex) for amp, delay, doppler in params: idx np.arange(N) - delay valid (idx 0) (idx N) r[valid] amp * s[idx[valid]] * np.exp( 2j * np.pi * doppler * np.arange(N)[valid] / fs ) return r这里有两个注意点。一是时延不一定是整数个采样点理想情况下要做分数时延滤波但仿真入门阶段先取整数时延即可差别不大二是多普勒频移会让信道在单个符号块内变化所以必须在逐点层面乘上旋转因子不能只在块级别乘一个常数否则就退化成纯频率选择性信道了测不出AFDM的优势。在打噪声时要按信号平均功率来折算噪声功率比较稳妥的做法是snr_linear 10 ** (snr_db / 10) signal_power np.mean(np.abs(rx_signal) ** 2) noise_var signal_power / snr_linear noise np.sqrt(noise_var / 2) * ( np.random.randn(N) 1j * np.random.randn(N) )复噪声在实部和虚部的方差各是噪声功率的一半很多刚开始做通信仿真的人会在这里漏掉1/2导致最终BER曲线和理论值整体偏移好几个dB。3.4 快速实现把DAFT拆成FFT上面这段双重循环只适合验证小规模链路。N等于256时肉眼可见地卡N到了1024基本没法接受。DAFT虽然多两个二次相位项但可以拆成“三个对角相位矩阵夹一个FFT”的结构复杂度从O(N^2)降到O(N logN)。拆法思路是这样把指数项里的nm/N单独拿出来做FFTc1n^2和c2m^2作为前后两端的相位旋转。用代码表示就是def afdm_modulate_fast(x, N, c1, c2): m np.arange(N) n np.arange(N) phase1 np.exp(2j * np.pi * c2 * m**2) # 数据域相位 phase2 np.exp(2j * np.pi * c1 * n**2) # 时域相位 X x * phase1 S np.fft.fft(X, normortho) return S * phase2 def afdm_demodulate_fast(r, N, c1, c2): m np.arange(N) n np.arange(N) phase1 np.exp(-2j * np.pi * c2 * m**2) phase2 np.exp(-2j * np.pi * c1 * n**2) Y r * np.conj(phase2) X np.fft.ifft(Y, normortho) return X * np.conj(phase1)注意相位符号和配合。用这个实现跑N等于1024、几千个符号也不会有压力。我在实际工程里默认用快速版本只在调试时用矩阵版本核对结果。3.5 OFDM对比实验设计对比实验最容易出问题的地方是没有保证两个系统消耗同样多的频谱和时间资源。我做的对比设计是AFDM和OFDM都用64个子载波符号块长度一致循环前缀长度一致调制阶数同为QPSK或16QAM信道完全复用同一条多普勒多径信道。这样两者的频谱效率基本一致BER差异才能公平比较。在这个设定下低速场景比如多普勒频移10Hz时AFDM和OFDM的BER曲线基本重合这很正常不要怀疑实现错了。等把多普勒拉到200Hz甚至500Hz时差值才会拉开OFDM会出现明显的误差平台AFDM的曲线会继续下降这就是chirp基带来的分集增益在起作用。4. 复现过程中的高频问题与排查技巧4.1 chirp参数乱选BER曲线完全没有下降趋势见过很多次的现象c1、c2随便取数仿真跑完发现BER在10dB以上就开始平了怎么提信噪比都没用。这基本就是参数和信道不匹配。c1要对应多普勒扩展c2要符合时延扩展下的全分集条件不能当固定值用。我调参的经验是先固定c2为论文推荐值只扫描c1看BER随多普勒变化是否单调改善找到拐点后再联合调c2。建议在代码里加一段自检先生成DAFT矩阵检查A^H A和单位阵的误差如果误差大于1e-8说明参数组合或矩阵实现有误后面的仿真结果都不用看。4.2 归一化和信噪比定义不一致前面提到过少除根号N和复噪声方差漏半的问题这两类错误都比较隐蔽因为单看时域波形好像没毛病。排查技巧是先用纯高斯白噪声信道跑一组BER作为基线和QPSK理论BER曲线对比如果差了3dB或5dB这种固定偏移多半是噪声方差或功率归一化写错了。对不上就一步步打印信号功率、噪声功率、实际SNR别凭感觉改系数。4.3 矩阵实现太慢仿真跑不起来这是一个很实际的工程问题。如果是验证算法逻辑N取64或128矩阵版完全够用但要做BER扫描每个信噪比点要跑上万帧矩阵版就会非常浪费时间。我后来把所有关键链路换成FFT拆分版单帧时间从毫秒级降到微秒级整个扫描才变得可操作。建议新上手的人直接写快速版本但保留一个矩阵版本作为验证基准两边输出逐一对比确保全部一致。4.4 常见问题速查表问题现象可能原因解决办法BER曲线在高SNR下平掉c1/c2与信道不匹配用自检脚本验证DAFT矩阵再扫描c1BER比理论值差3dB或5dB噪声方差或功率归一化错误检查复噪声方差是否除以2检查归一化系数与OFDM对比无差异多普勒设置太低拉大多普勒频移到200Hz以上N增大后仿真极慢使用了双重循环或矩阵乘切换到FFT分解实现接收端解调后星座图旋转收发端c1/c2符号不一致统一采用exp(j...)正向、exp(-j...)反向的约定过信道后总功率暴增没有对多径增益做归一化对信道抽头功率做总和归一化处理5. 给想继续深挖的朋友留几条思路我个人在把AFDM源码跑通之后最大的感受是这套框架搭起来不难真正的功夫在物理参数和数学参数之间的对应关系上。后面如果再往前推进可以尝试在接收端插入基于导频的信道估计模块看看c1、c2在不知道精确多普勒时还能不能稳住性能也可以把单发单收的链路扩展到MIMO-AFDM研究不同天线配置下的分集表现。还有一条线是PAPR优化chirp信号天然会改变峰均比但具体怎么利用这个特性扬长避短目前论文里也还在探索。最后再分享一个我自己常用的调试习惯不要一上来就跑完整BER曲线先把调制解调链路在无信道状态下测通再逐步加入噪声、加多径、加多普勒每一步都对可视化结果做一次确认。这样出了问题能很快定位在哪一层而不是面对一堆乱糟糟的曲线无从下手。AFDM的源码实现本身并不神秘熟悉了这组二次相位变换之后你会发现它和OFDM之间的距离比想象中要近得多。本文还有配套的精品资源点击获取