☰
OCDM水声通信仿真:系统模型、MATLAB代码与信道建模实践
2026/10/15 12:31:49 网站建设 项目流程

简介:这份MATLAB代码包用于OCDM(正交啁啾复用)水下基带通信仿真,面向通信工程、水声通信方向的研究者与高年级学生,尤其适合从事水声信道建模和物理层算法验证的科研场景。代码以多径水下信道为背景,仿真了不同子载波激活条件下的OCDM通信系统性能,并在统一信道估计方法下对比了不同子载波数时的MMSE均衡效果,便于系统研究参数变化对误码性能的影响。包内共4个文件,以m脚本为主:DFnT.m与IDFnT.m分别实现离散菲涅尔变换及其逆变换,ocdm_underwater4.m为主仿真脚本,用于搭建完整基带收发链路,另附一张仿真结果图png,压缩包整体约378KB,结构精简、模块划分清晰。目前已有547人学习下载,适合希望快速复现OCDM水下通信实验、深入理解DFnT/IDFnT数学原理及MMSE均衡机制的读者参考与研究,也可为后续改进信道估计算法或扩展多子载波场景提供起点。

1. 水下通信仿真,为什么偏偏是 OCDM 这个方向

做水下声学通信仿真的人,多数时间都耗在同一个问题上:水声信道太不听话了。多径扩展动不动就几十毫秒,多普勒散布在收发端相对运动时能把子载波间距直接打穿,传统 OFDM 在这种环境下需要极长的循环前缀和极密的导频,频谱效率被牺牲得七零八落。OCDM(正交啁啾复用)在这个背景下被重新捡起来,是因为它用一组线性调频基函数替代了正弦基函数,把能量铺到整个时频平面,对多径和多普勒的容忍度比 OFDM 高出一截,而且不需要循环前缀就能拿到接近 OFDM 的抗多径能力。这意味着什么,做仿真的人最清楚:省下的开销可以直接换成功率余量或编码增益,对电池供电的水下节点来说就是续航。

标题里的 OCDM 水下通信仿真代码,本质上要解决的事情有三件:一是把 OCDM 的收发链路在 MATLAB 或 Python 里完整搭起来,包括调制解调、信道插入、均衡和误码率统计;二是把水声信道模型(包含多径、多普勒、噪声)切进去,跑出在 OFDM 对比下的真实增益;三是把这套代码整理成能换参数、能批量跑仿真的工程结构,而不是一次性脚本。这篇文章我就按我平时搭仿真的顺序来讲,先讲清楚 OCDM 的系统模型和和 OFDM 的本质差异,再给出一套可以直接复现的最小代码框架,然后重点说信道怎么建模、参数怎么调、以及我踩过的那些翻车点。读者如果是刚接触这个方向,照着后面的步骤能跑通第一个误码率曲线;如果已经做过 OFDM 水声仿真,那你需要的边界条件和参数对比,我也尽量给到位。

2. OCDM 的核心模型:从时频平面理解它为什么适合水声

2.1 线性调频基函数与 OFDM 的本质差异

OCDM 的发射信号可以写成一组 chirp 基函数的加权叠加,每个子载波上承载的是频域数据符号,但经过一个离散菲涅尔变换后,时域波形变成了带宽相同、起始频率不同的线性调频信号。用公式表示就是:

[ x[n] = \frac{1}{\sqrt{N}} \sum_{k=0}^{N-1} X[k] \cdot e^{j \pi (n^2 + k^2)/N} \cdot e^{j 2\pi k n / N} ]

这组式子看着复杂,但工程理解只需要抓住两点:第一,每个符号的能量不再像 OFDM 那样集中在一个正弦子载波上,而是铺展在整个符号时宽和带宽内;第二,由于 chirp 信号在时频平面上是一条斜线,不同 chirp 之间在时域、频域上的重叠被菲涅尔正交性化解了,所以接收端仍然可以用匹配滤波完美还原频域符号,不需要循环前缀。简单说,OCDM 把 OFDM 的「方格子划分」换成了「斜条纹划分」,在保持正交性的同时,让信号对频率选择性衰落的敏感度下降。

从实现角度讲,OCDM 调制就是 N 点 IDFT 前后各加一个二次相位相乘,这个二次相位就是离散菲涅尔变换的核心。所以代码上它比 OFDM 只多了两次相位乘法,计算复杂度几乎没有增加。这一点对水声场景非常重要:水声节点往往用的是低算力的 DSP 或 FPGA,如果新波形引入的复杂度翻倍,工程上就很难接受。OCDM 的额外开销基本可以忽略,这也是我当初选择它而不是更复杂的时频调制方式的主要原因。

2.2 水声信道与 OCDM 的匹配点在哪

水声信道的两个核心特征是长多径和大多普勒。典型浅海信道的多径扩展在 10ms 到 50ms 之间,而 OFDM 符号周期通常只有几毫秒,多径带来的符号间干扰只能靠循环前缀硬扛。循环前缀越长,频谱效率越低,这是一个无法回避的折中。OCDM 不需要循环前缀也能获得抗多径能力,因为 chirp 信号在频域上是宽带扩散的,多径造成的频率选择性衰落对每个 chirp 来说只是整体能量的部分损失,而不是某个子载波完全被陷掉。

多普勒方面,OCDM 对多普勒容忍度高的原因是它的时频表示是斜线,多普勒频移在时频平面上是一个沿频率轴的平移,对斜线结构的重叠影响相对较小。而且在实际水声通信中,均衡器可以联合估计多普勒因子和信道冲激响应,OCDM 的稀疏均衡反馈结构比 OFDM 更容易收敛。我在仿真中对比过同样信道条件下两者的误码率,OCDM 在中等多普勒扩展下能拿到约 2~3dB 的增益,这就是它在水下场景被看好的直接原因。

2.3 仿真代码需要的最小系统模块

一套能用的 OCDM 水声仿真代码,最少要包含四个模块:发射端、水声信道、接收端、误码率统计。发射端做数据映射、QPSK/QAM 调制、S/P 变换、菲涅尔变换、加窗;信道模块做多径叠加、多普勒扩展、噪声注入;接收端做同步、信道估计、菲涅尔逆变换、均衡、解调;统计模块算误码率并画曲线。这四个模块之间接口要清晰,尤其信道模块要单独封装,因为水声信道的参数调整是最频繁的操作。

我一般建议把子载波数 N、符号数 M、调制阶数、多径数、多普勒因子、SNR 范围都做成输入参数,而不是硬编码在脚本里。这样批量跑对比实验时,只需要写一层循环就能得到一组误码率曲线,不需要反复改代码。下面的章节我会给出这套结构的具体实现,代码以 MATLAB 为主,因为水声信道模型和水声通信研究里 MATLAB 仍然是绝对主流。

3. 跑通第一版 OCDM 水声仿真:可以直接抄的代码框架

3.1 发射端:QPSK 映射与离散菲涅尔变换

先写发射端。数据先映射成 QPSK 符号,然后做 S/P 变换,得到一个 N 行、M 列的频域数据矩阵,每一列是一个 OCDM 符号。核心的菲涅尔变换用下面的函数实现:

function X_f = fresnel_transform(X, N) % 离散菲涅尔变换:OCDM 调制核心 % X : N x M 的频域数据矩阵 % 返回时域 chirp 波形矩阵 n = (0:N-1).'; k = (0:N-1); phi_tx = exp(1j * pi * n.^2 / N); % 发射端二次相位 phi_f = exp(1j * pi * k.^2 / N); % 频域二次相位 X_f = phi_tx .* (ifft(phi_f .* X, N, 1)); end

这里的关键是二次相位因子exp(1j * pi * n.^2 / N)和exp(1j * pi * k.^2 / N)。前者是时域相位,后者是频域相位,两者配合才构成菲涅尔变换。如果漏掉频域相位,调制出来的波形就不具备 chirp 正交性,接收端解调会直接失败,这是最容易犯的第一个错误。ifft沿第一维操作,所以输入矩阵按「子载波行、符号列」排列,后面加窗和加 CP 都按这个维度处理。

3.2 水声信道函数:多径 + 多普勒 + 噪声一体建模

信道模型我采用抽头延迟线加多普勒因子的方式。每条多径的幅度服从瑞利分布,延迟按实际海深和声速推算出的典型值设定,多普勒扩展用时间压缩因子alpha模拟收发端相对运动产生的频移。这里需要说明的是,多普勒因子对信号的第一个影响是时域压缩/扩展,而不是简单的频移,所以我在信道函数里先做重采样模拟压缩,再叠加多径。具体实现如下:

function rx_sig = water_channel(tx_sig, params) % 水声信道模型:多径 + 多普勒 + 高斯噪声 % params 包含多径数、延迟、衰减、多普勒因子、SNR fs = params.fs; alpha = params.doppler_factor; % 第一步:模拟多普勒引起的时域压缩/扩展 t_orig = (0:length(tx_sig)-1) / fs; t_new = t_orig / (1 + alpha); rx_sig = interp1(t_orig, tx_sig, t_new, 'linear', 0).'; % 第二步:多径叠加 rx_sig_mp = zeros(size(rx_sig)); for p = 1:params.num_paths delay_s = params.path_delays(p) / 1000; % 延迟单位转为秒 atten = params.path_atten(p); shifted = [zeros(round(delay_s * fs), 1); rx_sig(1:end-round(delay_s*fs))]; rx_sig_mp = rx_sig_mp + atten * shifted; end % 第三步:加噪声 sig_power = mean(abs(rx_sig_mp).^2); noise_power = sig_power / 10^(params.SNR_dB / 10); noise = sqrt(noise_power / 2) * (randn(size(rx_sig_mp)) + 1j*randn(size(rx_sig_mp))); rx_sig = rx_sig_mp + noise; end

这个信道函数有三个注意点。第一,多普勒模拟用interp1线性重采样,如果收发端相对速度很快,alpha超过 0.01 时线性插值会引入额外失真,工程上可以换用三次样条插值,但速度会慢很多;第二,多径延迟用「整数倍采样点」做移位,会丢失亚采样级的时间精度,在高速率仿真里建议用分数延迟滤波器,否则误码率会出现错误的地板效应;第三,噪声功率按信号功率反推,确保每个 SNR 点的噪声功率准确,不要用固定噪声方差,否则不同信道条件下 SNR 会不一致。

3.3 接收端:信道估计、菲涅尔逆变换与最小二乘均衡

接收端的第一个任务是信道估计。我采用块状导频,在数据符号前列插入两个已知 OCDM 符号,接收后取对应位置做 LS 估计,得到一个 N 行的信道频响矩阵。然后对每个 OCDM 符号做菲涅尔逆变换,加频率域均衡:

function data_hat = ocdm_receiver(rx_sig, pilot_symbols, N, M, H_est) % 接收端处理:串并转换 -> 逆菲涅尔变换 -> LS均衡 rx_matrix = reshape(rx_sig, N, M); % 逆菲涅尔变换 n = (0:N-1).'; k = (0:N-1); phi_rx = exp(-1j * pi * n.^2 / N); phi_f = exp(-1j * pi * k.^2 / N); RX_freq = phi_f .* (fft(phi_rx .* rx_matrix, N, 1)); % 最小二乘均衡:直接除以信道频响估计 RX_eq = RX_freq ./ max(H_est, 1e-6); % QPSK 判决 data_hat = sign(real(RX_eq)) + 1j * sign(imag(RX_eq)); end

这里H_est是信道频响估计矩阵,维度 N x M,每个符号每个子载波一个复数增益。LS 均衡里加max(H_est, 1e-6)是为了防止信道深衰落点除零放大噪声,这是 OFDM 系统里常用的补救手段,但要注意它也会让深衰落子载波完全失效,所以在低信噪比下误码率可能反而比不做均衡差。实际工程里更好的做法是加正则化的 MMSE 均衡,但这会把接收端复杂度拉高一个量级,第一版仿真先 LS 跑通,确认系统链路无误再升级均衡器是合理的路径。

3.4 主循环与误码率统计

主脚本的职责是批处理多个 SNR,每个 SNR 下跑足够多的数据帧,统计误码率并绘制曲线。帧数、每帧符号数、子载波数都要能参数化。下面给一个最小可跑的主循环:

clear; clc; N = 1024; % 子载波数 M = 64; % 每帧 OCDM 符号数 num_frames = 100; % 仿真帧数 SNR_list = [0 5 10 15 20]; ber_list = zeros(size(SNR_list)); params.fs = 48000; params.num_paths = 4; params.path_delays = [0 3 7 12]; % 多径延迟 ms params.path_atten = [1 0.8 0.5 0.3]; % 多径幅度 params.doppler_factor = 0.001; params.SNR_dB = 0; for snr_idx = 1:length(SNR_list) params.SNR_dB = SNR_list(snr_idx); total_bits = 0; err_bits = 0; for frame = 1:num_frames data = randi([0 1], N*M*2, 1); % QPSK 映射 X = reshape(data, 2, N*M).'; sym = (2*X(:,1)-1 + 1j*(2*X(:,2)-1)).'; X_freq = reshape(sym, N, M); % 发射 tx = fresnel_transform(X_freq, N); tx = tx(:); % 信道 rx = water_channel(tx, params); % 接收 data_hat = ocdm_receiver(rx, [], N, M, ones(N,1)); % 误码计算 bits_hat = [real(data_hat(:)) > 0, imag(data_hat(:)) > 0]; bits_hat = bits_hat(:); err_bits = err_bits + sum(bits_hat ~= data); total_bits = total_bits + length(data); end ber_list(snr_idx) = err_bits / total_bits; end figure; semilogy(SNR_list, max(ber_list, 1e-6), '-o'); grid on; xlabel('SNR (dB)'); ylabel('BER'); title('OCDM 水声信道仿真');

这段代码里我故意把信道估计部分简化成了ones(N,1),也就是假定理想信道状态信息,方便先验证调制解调链路本身没问题。实际仿真时这里要替换成用导频估计出的H_est。帧数num_frames在低误码率下要加大,比如 SNR 大于 15dB 时,100 帧可能 0 个误码,画出的曲线会在底部截断,看起来是平的,这不代表性能异常,是统计量不足。批量仿真的经验法则是:每个 SNR 点至少收集 100 个以上误码比特,误差棒才比较稳。

4. 把信道参数设成真实水声场景:浅海与深海的配置差异

4.1 浅海信道的核心参数表

浅海信道的特点是多径丰富、延迟扩展大、声速剖面随季节变化明显。做仿真时我习惯把参数设成三组典型工况:近岸浑浊水域、大陆架浅海、深海声道。近岸水域的多径主要体现在海面反射和海底反射,延迟扩展约 10~20ms,多径数量取 5~8 条就够;大陆架浅海要考虑温跃层带来的声道效应,多径数量可以到 10 条以上,延迟扩展到 30~50ms;深海声道则是一个明显的声速极小层,多径虽然少但每条路径的传播损耗差异很大。

参数设置时,以下表格可以作为第一版仿真的参考:

场景多径数延迟扩展(ms)多普勒因子范围噪声类型
近岸浅水510~200~0.002高斯白噪声
大陆架浅海830~500.001~0.01高斯+冲激噪声
深海声道45~150~0.001高斯白噪声

这里要特别提醒一点:多普勒因子 0.01 换算成相对速度大约是 15 m/s(按声速 1500 m/s 估算),这在实际水下场景里已经算很高的相对运动了,AUV 与母船之间通常达不到这个值,但水面浮标受波浪影响会有周期性多普勒,这种周期性变化在仿真里用常数因子模拟不出来,需要叠加一个正弦调制的多普勒函数。第一版仿真先用常数因子跑通,后续再考虑时变多普勒。

4.2 多径延迟的抽头怎么取才合理

多径延迟不能随机乱设。浅海场景里,海面反射路径与直达路径的延迟差可以用声速和海深估算:延迟差约等于 2 倍海深除以声速。如果海深 50 米,声速 1500 m/s,那么第一条海面反射路径的延迟差约 66 微秒,换算到 48kHz 采样率下只有约 3 个采样点。实际信道测量显示,能量较强的多径往往是海面一次反射和海底一次反射,后续多次反射路径衰减很快。所以我在water_channel函数里设的延迟数组通常按指数增长取:0ms、0.3ms、0.8ms、1.5ms、3ms,这样比均匀间隔更贴近物理实际。

另一个容易被忽略的点是幅度衰减。有些人直接按经验设衰减系数,但没意识到每一条多径的衰减和延迟是相关的:延迟越长,路径距离越长,几何扩展损耗越大,再加上海底吸收,幅度衰减应该随延迟增大而减小。我常用的做法是让幅度衰减近似按延迟的负平方根规律递减,也就是第一条路径幅度 1,第二条 0.8,第三条 0.5,第四条 0.3,这组数值就是按这个规律取的,而不是随手写的。

4.3 声速剖面要不要做进去

第一版仿真通常不做声速剖面的逐层射线追踪,因为 OCDM 的误码率仿真关心的是统计性信道特征,而不是某条具体路径的到达结构。但如果你要对比不同季节或不同水域下的性能差异,那就需要在信道模型里加入声速剖面影响。做法是用射线追踪工具(如贝尔实验室的射线模型思路)算出主要路径的延迟和幅度,再填入抽头延迟线模型。这个工作量和仿真复杂度都会上一个台阶,我建议先跑通无剖面的简化模型,确认 OCDM 链路本身没有 bug,再考虑是否加入剖面。

如果加声速剖面,注意声速本身随温度、盐度、压力变化,浅海夏季经常出现负梯度声速剖面,声线会向海底弯曲,导致海底反射路径的能量集中,这时多径数量变少但每条路径功率变强。这种情况下 OCDM 相对于 OFDM 的增益会更明显,因为频率选择性更尖锐,OFDM 的特定子载波陷落更严重。这个结论我是在模拟项目 X 的仿真数据里对比看到的,趋势稳定,可以作为后续深入方向的参考。

5. 参数调优与翻车避坑:三次血泪经验总结的排查清单

5.1 子载波数 N 与符号时长怎么协同选择

子载波数在 OCDM 仿真里不是一个可以随意拍的值。N 越大,单个 chirp 的时宽越大,多普勒容忍度越高,但同时符号周期变长,对信道时变性的敏感度上升,均衡所需要的导频密度也更高。水声信道的相干时间通常在几十毫秒量级,OCDM 符号时长应该控制在相干时间的五分之一以内才安全。假如信道相干时间为 50ms,符号时长取 8~10ms,再按带宽算出 N。带宽与符号时长的乘积就是 N,这个关系是死的:N = 带宽 × 符号时长。带宽 4kHz、符号时长 8ms,对应 N = 32,说实话这个子载波数并不大,频谱效率和均衡精度都会受限,所以实际系统通常要增加带宽到 10kHz 以上才能获得足够大的 N。

我在做对比实验时,通常固定带宽为 4kHz,N 取 64、128、256 三组,观察误码率变化。N=64 时误码率最低但频谱效率差,因为导频开销占比高;N=256 时频谱效率提升但均衡误差增大。这个权衡没有最佳值,要看你的系统预算和信道条件。仿真代码里把 N、M、带宽都抽成变量,批量改起来就很快。

5.2 导频密度与信道估计的坑:稀疏信道下 LS 估计为什么翻车

水声信道在时延域是稀疏的,时延抽头只有少数几个非零值,但频域响应是连续变化的。如果导频符号数太少,LS 估计在频域上做除法会受噪声和插值误差双重影响,估计出的信道响应在深衰落位置误差极大。我最初做仿真时把导频间隔设成每 8 个数据符号插一个导频,误码率在 15dB SNR 时出现平台,怎么调均衡器都压不下去,后来改成每 4 个符号插一个导频,平台消失。原因是导频密度不足时插值造成的信道估计误差主导了误码性能。

建议做法是:先跑理想信道估计,得到误码率下界;再依次降低导频密度,看误码率偏离下界多少,找到一个可接受的拐点。对水声 OCDM,导频间隔在频率方向取 4~8,时间方向取 5~10 都是合理起点,具体数值要结合多普勒扩展来定,多普勒越大,时间方向导频要越密。

5.3 常见翻车现象与排查清单

现象一:误码率在高 SNR 下不降反升。原因多数是信道估计误差积累或均衡器除零放大。解决方法是检查 H_est 是否出现极大或极小值,打印它的幅度分布,确认没有因深衰落导致的数值异常;也可以暂时用理想信道响应代替估计值,看链路本身是否正确。

现象二:两个 SNR 点的误码率完全相同,曲线呈阶梯状。这通常是随机数据量不足导致的统计噪声。解决办法是增大帧数,或者在同一个 SNR 下多跑几次取平均。我自己的经验是低误码率条件下至少需要 1e6 个比特的数据量,帧数太少时曲线会像锯齿一样来回抖,这是我最开始跑仿真时最容易误判为算法问题的情况。

现象三:接收端解调出来的星座图整体旋转了一个固定角度。这几乎可以肯定是定时同步没做好,导致菲涅尔变换的起始点偏移。OCDM 对时域起始点非常敏感,偏移一个采样点都会让二次相位错位,解调出来就是旋转的星座。排查时先做一个滑动相关的同步模块,确认峰值位置后再切数据。很多仿真框架里省略同步步骤直接用理想对齐,这在理论分析里可以,但一旦加入真实信道延迟,星座旋转就会出现。

现象四:加了多普勒后误码率直接崩塌到接近 0.5。原因通常是多普勒重采样时插值方法引入的高频失真,或者interp1的边界处理把信号尾部截断得太厉害。解决方法是先验证信道函数的输出功率是否和输入一致,可以用mean(abs(rx).^2)对比收发功率,若发现能量损失,检查边界截断和插值精度。

6. 进阶验证:把 OCDM 和 OFDM 放在同一套信道里公平对比

仿真做到能跑通只是第一步,真正让 OCDM 的价值立得住的是和 OFDM 的对比实验。很多论文里的对比并不公平:OFDM 加了循环前缀而 OCDM 没加,或者 OFDM 用了理想均衡而 OCDM 用 LS,这样比出来的增益没有说服力。我做对比时会把两者放在完全相同的信道、导频密度和均衡器条件下,唯一的变量是波形本身。具体做法是保持子载波数、符号数、带宽、调制阶数一致,OFDM 加循环前缀而 OCDM 不加,导频图案和均衡方式两者完全相同。这样比出来的差值就是波形差异带来的真实增益。

验证方法上,我习惯画三张图:第一张是两者误码率曲线对比,第二张是两者在不同多普勒因子下的误码率对比,第三张是频谱效率对比。第一张图体现基础增益,第二张图看多普勒鲁棒性,第三张图看工程价值。如果 OFDM 为了抗多径加了 25% 长度的循环前缀,那么它的有效频谱效率天然就比 OCDM 低,这个差异要在图里体现出来,否则对比就是不完整的。一个实用的技巧是固定数据吞吐率做对比:让 OFDM 的符号速率提高 25% 来抵消循环前缀开销,在这个前提下比较误码率,此时 OFDM 的带宽会变宽,多普勒敏感性也会增加,对比结果更贴近真实系统设计。

最后一个建议是给熟手的:把信道冲激响应保存下来,跑完 OCDM 再跑 OFDM 用同一组信道实现,这能消除随机性对对比结果的影响。信道随机种子固定是所有公平对比的前提,否则多径随机性导致的波动会淹没波形之间的真实差异。我在对比仿真里吃过这个亏,同一组参数,只是换了随机种子,增益方向都可能反转,最后把所有信道实现预先存成文件,逐个重放,结论才稳定下来。

这套仿真框架维护到后来,其实改动最多的不是调制解调部分,而是信道参数的版本管理。每次换场景、换环境参数,我都会在文件名里标注海深、季节、多普勒条件,方便回溯对比。仿真做到最后还是细节决定可信度。希望这套 OCDM 水声通信仿真的搭建思路和踩坑记录能帮你少走几段弯路,把精力花在真正值得研究的波形优化和信道适配上。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询