简介:本资源面向无线通信与信号处理方向的学习者与研究人员,提供基于OFDM与OTFS两种调制架构的多径衰落信道系统仿真实现,用于对比评估高速移动场景下的传输性能。仿真构建了含5条独立路径的多径信道模型,最大时延扩展2微秒、多普勒扩展0至500赫兹,并采用Jakes模型模拟时间选择性衰落,评估指标覆盖误码率、吞吐量、峰均功率比与带外频谱泄漏。资源包共14个文件,约21KB,以m脚本为主体,涵盖OFDM调制解调、ISFFT/SFFT变换、多径信道生成、均衡与数据生成等核心模块,另含zbak备份文件、说明文档及一个备份压缩包,便于对照阅读与二次修改。已有62人学习下载。读者可借此完整复现两种系统的性能对比流程,理解OTFS在高速移动下误码率降低约两个数量级、吞吐量损失减少约40%的机理,并掌握信道估计误差下的鲁棒性分析方法。
1. 从一条多径信道说起:这套 OFDM/OTFS 仿真到底能帮你验证什么
做无线通信仿真的同行大多有过这种体验:在城区、室内或高速场景下,接收端星座图糊成一团,误码率曲线怎么调都下不来。问题往往不在调制解调本身,而在多径衰落信道把符号拖出了保护间隔。这套基于 MATLAB 的 OFDM 与 OTFS 多径衰落信道无线通信系统仿真,解决的正是这个痛点——它把两条主流技术路线放进同一个可对比的框架里,让你能亲手看到 OFDM 在时变多径下的边界,以及 OTFS 在时延-多普勒域做均衡的差异。适合通信专业学生做课程设计、算法工程师做链路预研、以及需要快速验证波形方案的研究人员。关键词 matlab ofdm 在这里不是标签,而是你打开工程后第一眼要面对的入口函数。
2. OFDM 链路搭建:从子载波映射到循环前缀的完整参数链
2.1 为什么先搭 OFDM 而不是直接上 OTFS
OTFS 的接收端处理建立在 OTFS 调制与解调之上,而 OTFS 的等效输入输出关系又常被拿来和 OFDM 对比。如果一上来就调 OTFS,你很难判断误码是来自信道估计、均衡还是域变换本身。先把 OFDM 链路跑通,相当于给自己留了一条基准线。常见做法是固定子载波数、循环前缀长度和调制阶数,只改变信道多径数目与最大多普勒频移,观察误码率曲线的拐点。这样后面切到 OTFS 时,任何性能变化都能归因到波形本身,而不是参数没对齐。
我一般会把 OFDM 部分拆成发射、信道、接收三段。发射端负责 QAM 映射、IFFT、加 CP;信道段用抽头延迟线模型叠加多径增益和时变相位;接收端做去 CP、FFT、信道估计与迫零或 MMSE 均衡。每一段都留出可调参数,方便你后面做消融实验。
2.2 发射端:子载波间隔与 FFT 点数怎么定
子载波间隔直接决定符号周期,也决定你对多普勒的容忍度。热词里反复出现的 ofdm 子载波间隔不是噱头,它和移动速度、载波频率一起决定了载波间干扰的严重程度。一个合格从业者会先算多普勒频移,再反推子载波间隔至少要比多普勒大一个数量级,否则 ICI 会把星座点抹开。
% OFDM 发射端核心参数 N_fft = 1024; % FFT 点数,决定子载波总数 N_cp = 128; % 循环前缀长度,需大于最大时延扩展 M = 16; % 16-QAM 调制阶数 num_symbols = 100; % OFDM 符号数 subcarrier_spacing = 15e3; % 子载波间隔 15 kHz,常见配置 % 生成随机比特并做 QAM 映射 data_bits = randi([0 1], log2(M)*N_fft*num_symbols, 1); data_sym = qammod(data_bits, M, 'UnitAveragePower', true); data_sym = reshape(data_sym, N_fft, num_symbols); % IFFT 变换到时域并加循环前缀 tx_time = ifft(data_sym, N_fft); tx_cp = [tx_time(end-N_cp+1:end, :); tx_time];这段代码里 N_fft 取 1024 是折中:点数太小频率分辨率不够,太大运算量上去且对相位噪声更敏感。N_cp 取 128 对应约 8.5 微秒的保护间隔,能覆盖一般城区多径的时延扩展。subcarrier_spacing 设为 15 kHz 是参考常见蜂窝配置,如果你要模拟高速场景,这个值需要放大到 30 kHz 或 60 kHz。qammod 的 UnitAveragePower 参数保证符号平均功率归一,否则后面信噪比计算会偏。
2.3 多径衰落信道:抽头延迟线模型与多普勒叠加
多径信道不是简单加几个延迟副本,每条径的增益和相位随时间变化,这才是衰落的来源。常见做法是用 Jakes 模型或滤波高斯噪声生成每条径的时变系数,再按延迟抽头叠加。
% 多径信道:抽头延迟线 + 多普勒 path_delays = [0 2 4 6]; % 各径延迟(采样点) path_gains_dB = [0 -3 -6 -9]; % 各径平均功率(dB) path_gains = 10.^(path_gains_dB/20); fd = 100; % 最大多普勒频移 Hz t = (0:size(tx_cp,1)-1)/subcarrier_spacing/N_fft; % 为每条径生成时变瑞利系数 rayleigh_coeff = zeros(length(path_delays), length(t)); for p = 1:length(path_delays) rayleigh_coeff(p,:) = path_gains(p) * ... (randn(1,length(t)) + 1j*randn(1,length(t)))/sqrt(2); end % 按延迟抽头叠加 rx_signal = zeros(size(tx_cp,1)+max(path_delays), size(tx_cp,2)); for p = 1:length(path_delays) delayed = [zeros(path_delays(p), size(tx_cp,2)); tx_cp(1:end-path_delays(p), :)]; rx_signal = rx_signal + rayleigh_coeff(p,:).' .* delayed; endpath_delays 用采样点表示,换算成时间要除以采样率。path_gains_dB 给的是各径平均功率,实际系数还要乘上随机相位。fd 取 100 Hz 对应约 2 GHz 载波下 54 km/h 的移动速度,这个值直接决定信道变化快慢。注意这里用 randn 生成的是准静态瑞利,严格来说应该用多普勒滤波器塑形,但作为教学仿真够用,后面做 OTFS 对比时再换成时变模型。
2.4 接收端:去 CP、FFT 与均衡的落地顺序
接收端顺序不能乱:先去 CP,再 FFT,然后做信道估计,最后均衡。很多人把均衡放在 FFT 之前,结果域没对齐,误码率下不来还找不到原因。
% 接收端:去 CP、FFT、LS 信道估计与 MMSE 均衡 rx_no_cp = rx_signal(N_cp+1:N_cp+N_fft, :); rx_freq = fft(rx_no_cp, N_fft); % 用导频做 LS 估计(假设首符号为导频) H_est = rx_freq(:,1) ./ data_sym(:,1); H_est = repmat(H_est, 1, num_symbols); % MMSE 均衡 snr_linear = 10^(20/10); equalized = rx_freq .* conj(H_est) ./ (abs(H_est).^2 + 1/snr_linear); % 解调并计算误码率 rx_bits = qamdemod(equalized(:), M, 'UnitAveragePower', true); [~, ber] = biterr(data_bits, rx_bits);H_est 用首符号做 LS 估计是最简方案,实际工程里会用块状或梳状导频加插值。MMSE 均衡里的 snr_linear 是信噪比线性值,这个参数设错会让均衡过度或不足。equalized 计算完后要 reshape 回符号流再解调。ber 是最终验证指标,但别只看一个点,要扫信噪比画曲线。
3. OTFS 接入:时延-多普勒域变换与接收端均衡的实操路径
3.1 OTFS 和 OFDM 的本质差异在哪
OFDM 在时频域把符号铺开,多径带来的时延扩展靠 CP 吸收,但多普勒会让子载波间正交性破坏。OTFS 换了个思路:把符号放在时延-多普勒域,再通过二维变换映射到时频域。这样每条径在时延-多普勒域里变成一个可分辨的点,均衡从一维卷积变成二维抽头,稀疏性更好。热词里的 otfs 之所以受关注,就是它在高速场景下对多普勒的鲁棒性比 OFDM 强。
但 OTFS 不是免费午餐。它的接收端要做辛有限傅里叶变换和逆变换,运算量比 OFDM 大,而且信道估计要在时延-多普勒域做,导频设计更复杂。如果你的场景多普勒不大,OFDM 加足够 CP 就够用,没必要上 OTFS。
3.2 时延-多普勒域映射:ISFFT 与海森堡变换
OTFS 调制分两步:先把时延-多普勒域符号做逆辛有限傅里叶变换变到时频域,再做海森堡变换变成时域波形。MATLAB 里可以用矩阵乘法直接实现。
% OTFS 调制:ISFFT + 海森堡变换 N = 64; % 时延维度点数 M = 64; % 多普勒维度点数 X_dd = randn(N, M) + 1j*randn(N, M); % 时延-多普勒域符号 % 逆辛有限傅里叶变换(ISFFT) X_tf = ifft(fft(X_dd, N, 1), M, 2) * sqrt(N*M); % 海森堡变换:沿时延维度做 IFFT 得到时域信号 tx_otfs = ifft(X_tf, N, 1); tx_otfs = tx_otfs(:);X_dd 是时延-多普勒域符号矩阵,N 和 M 分别对应时延和多普勒分辨率。ISFFT 先沿时延维做 FFT,再沿多普勒维做 IFFT,最后乘 sqrt(N*M) 做能量归一。海森堡变换沿时延维做 IFFT 得到时域样本。注意这里没有加 CP,实际系统里 OTFS 也需要保护间隔,通常是在时域加或通过零填充实现。
3.3 接收端:维格纳变换与二维均衡
接收端是发射端的逆过程:先做维格纳变换回到时频域,再做辛有限傅里叶变换回到时延-多普勒域,然后在那个域里做均衡。
% OTFS 接收:维格纳变换 + SFFT + 二维均衡 rx_otfs = reshape(rx_signal_otfs, N, M); Y_tf = fft(rx_otfs, N, 1); % 辛有限傅里叶变换(SFFT) Y_dd = fft(ifft(Y_tf, M, 2), N, 1) / sqrt(N*M); % 时延-多普勒域 MMSE 均衡 H_dd = fft(ifft(H_est_otfs, M, 2), N, 1) / sqrt(N*M); Y_eq = Y_dd .* conj(H_dd) ./ (abs(H_dd).^2 + 1/snr_linear); % 解调 rx_bits_otfs = qamdemod(Y_eq(:), M, 'UnitAveragePower', true);维格纳变换沿时延维做 FFT,SFFT 再沿多普勒维做 IFFT 和时延维 FFT。H_dd 是时延-多普勒域的信道响应,理想情况下它是稀疏的,只有少数几个抽头非零。MMSE 均衡公式和 OFDM 里一样,但作用在二维网格上。Y_eq 拉直后解调,再算误码率。
3.4 两条链路的对比实验怎么设计
对比实验要控制变量:相同的信噪比范围、相同的多径数目、相同的多普勒频移。唯一变的是波形。建议扫信噪比从 0 到 30 dB,每个点跑至少 100 次蒙特卡洛,取平均误码率。
| 对比项 | OFDM | OTFS |
|---|---|---|
| 变换域 | 时频域 | 时延-多普勒域 |
| 多普勒鲁棒性 | 一般,依赖子载波间隔 | 较强,稀疏均衡 |
| 运算复杂度 | 低 | 高,多两次二维变换 |
| 导频开销 | 较低 | 较高,需二维导频 |
| 适用场景 | 低速到中速 | 高速、高多普勒 |
表格里运算复杂度是定性判断,实际要看 N 和 M 的取值。导频开销方面,OTFS 需要在时延-多普勒域铺导频,二维插值比一维复杂。如果你的场景移动速度低于 100 km/h,OFDM 加 30 kHz 子载波间隔通常够用,OTFS 的优势要到 300 km/h 以上才明显。
4. 避坑与排查:仿真跑不通时先看这五条
4.1 误码率曲线不降反升
现象:信噪比从 10 dB 加到 20 dB,误码率反而从 1e-2 涨到 5e-2。原因通常是信道估计用了错误的导频位置,或者均衡时信噪比参数设成了线性值但代码里当 dB 用。解决:检查 snr_linear 的计算,确认 10^(snr_dB/10) 没写错;再核对导频符号是否在接收端被正确提取,别把数据符号当导频。
4.2 星座图旋转但误码率正常
现象:解调后星座图整体旋转了一个角度,但误码率没明显恶化。原因是信道估计的相位有固定偏移,常见于 LS 估计时导频功率没归一。解决:在 H_est 计算后做相位校正,或者改用 MMSE 估计并加入噪声功率项。这个坑不影响误码率但会影响后续波束赋形实验,别忽略。
4.3 OTFS 均衡后符号全乱
现象:OTFS 接收端 SFFT 之后符号完全对不上,误码率接近 0.5。原因多半是 ISFFT 和 SFFT 的归一化因子不匹配,或者时延维和多普勒维的 FFT 方向搞反了。解决:对照发射端逐行检查,ISFFT 是先 FFT 后 IFFT,SFFT 是先 IFFT 后 FFT,归一化因子都是 sqrt(N*M)。建议先用单位信道跑一遍,确认变换对是恒等再叠多径。
4.4 循环前缀长度不够导致符号间干扰
现象:OFDM 误码率在高信噪比下出现地板,怎么加功率都下不去。原因是 CP 长度小于最大时延扩展,前一个符号拖到当前符号的 FFT 窗口里。解决:把 N_cp 加到大于最大时延对应的采样点数,或者减少多径数目。常见做法是先算最大时延扩展,再留 20% 余量。
4.5 蒙特卡洛次数太少导致曲线抖动
现象:误码率曲线锯齿状,不同次运行结果差一个数量级。原因是每个信噪比点只跑了几次,随机性没被平均掉。解决:每个点至少跑 100 次独立信道实现,高信噪比下要跑到 1000 次,因为误码事件本身稀疏。我一般会固定随机种子,方便复现,但对比实验时要用不同种子跑多组。
5. 进阶技巧:用误码率地板和复杂度曲线判断该选哪条链路
跑通两条链路之后,真正有价值的是知道什么时候选 OFDM、什么时候选 OTFS。我的习惯是画两张图:一张是误码率随信噪比变化的曲线,另一张是误码率随多普勒频移变化的曲线。前者告诉你功率效率,后者告诉你速度容忍度。
具体做法是固定信噪比 20 dB,把多普勒频移从 0 扫到 1000 Hz,每个点记录误码率。OFDM 的曲线会在某个多普勒值之后急剧上升,这个拐点就是它的边界。OTFS 的拐点通常更靠后,但低多普勒时它的误码率可能比 OFDM 略高,因为二维变换引入了额外噪声。如果你看到 OTFS 在低速下反而差,别急着否定它,先检查导频开销和均衡器参数。
另一个技巧是看误码率地板。OFDM 在多径严重时会出现地板,加功率没用,因为 ICI 是乘性干扰。OTFS 的地板通常更低,但前提是时延-多普勒域的信道估计足够准。我一般会在地板出现时,把信道估计从 LS 换成 MMSE,再对比一次。如果地板消失,说明是估计误差主导;如果还在,说明是波形本身的极限。
% 多普勒扫描:固定 SNR,扫 fd 记录误码率 fd_list = 0:100:1000; ber_ofdm = zeros(size(fd_list)); ber_otfs = zeros(size(fd_list)); for k = 1:length(fd_list) fd = fd_list(k); % 重新生成信道并跑 OFDM 和 OTFS [ber_ofdm(k), ber_otfs(k)] = run_both_links(fd, 20); end semilogy(fd_list, ber_ofdm, 'b-o', fd_list, ber_otfs, 'r-s'); xlabel('多普勒频移 (Hz)'); ylabel('误码率'); legend('OFDM', 'OTFS'); grid on;run_both_links 是我封装的一个函数,输入多普勒和信噪比,输出两条链路的误码率。fd_list 从 0 到 1000 Hz 覆盖了从静止到高速的典型场景。semilogy 用对数纵轴,方便看数量级变化。这张图出来之后,选型就有依据了:如果你的目标场景多普勒在 200 Hz 以下,OFDM 够用且复杂度低;超过 500 Hz,OTFS 的优势才值得那份额外运算量。
从那以后我每次做波形选型,都强制先跑一遍多普勒扫描,再决定要不要上 OTFS。这个习惯帮我省了不少返工时间。希望帮到你。
本文还有配套的精品资源,点击获取