ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于高阶循环谱的MPSK载波频率估计与MATLAB实现

基于高阶循环谱的MPSK载波频率估计与MATLAB实现 简介针对MPSK信号在非理想信道下难以精确同步载波的问题这份MATLAB实现以高阶循环谱为工具给出了一套完整的载波频率估计方案。内容覆盖BPSK、QPSK、8PSK等常见相移键控信号兼顾理论阐述与可运行代码从调制信号生成、随机频偏引入、循环累积量计算到基于峰值检测的频偏估计各环节均有独立脚本模块方便分段验证和修改参数。压缩包共4个文件其中包括3个M脚本和1个ASV自动备份文件整体大小仅有3KBM脚本分别承担信号构造、循环谱计算与仿真验证等职责结构精简、没有额外依赖代码注释清晰、流程直观能够直观展示高阶循环谱如何抑制噪声并还原被频偏破坏的相位信息。目前已有两百六十余人学习参考适合通信工程、信号处理等方向的学生、工程师和科研人员快速入手。借助这套源码读者不仅可以掌握循环谱频率估计的实现细节还能以此为基础设计更稳健的载波恢复策略提升通信系统的同步性能。1. 为什么 MPSK 载波频率估计要选高阶循环谱在通信侦察、频谱监测和突发信号解调里MPSK 信号的载波频率估计是第一道工序。传统做法是直接对接收信号做 FFT 取峰值或者用 Costas 环跟踪载波。前者在低信噪比下被噪声谱淹没后者对频偏范围和符号跳变敏感信号一短就锁不住。高阶循环谱估计走的是另一条路MPSK 信号经过非线性变换后会在 M 倍载频处产生一条与调制相位无关的离散谱线而高斯噪声和大多数平稳干扰的四阶累积量为零所以这条谱线可以在很低信噪比下被检测出来。这套思路在 MATLAB 里实现并不复杂核心是生成信号、做 M 次方、取频谱、找峰值再除以 M。下面从循环平稳原理开始一步步落到可复现的代码与参数调优顺便指出最容易让结果翻车的几个边界条件。2. MPSK 信号的高阶循环谱基础与估计思路2.1 循环平稳性从哪里来为什么高阶谱能抑制噪声MPSK 信号的复基带形式是 s(t) ∑ a_k g(t-kT) e^{j(2πf_c t φ_0)}其中 a_k 是取值在单位圆上的 M 个等概符号g(t) 是脉冲成型滤波器T 是符号周期。符号序列本身的均值不是周期函数但自相关函数关于 T 呈周期时变所以信号是循环平稳的。用二阶循环谱可以观察到符号速率位置的谱线但在低信噪比时这条谱线受噪声和调制状态影响较大对载频的指示也不够直接。BPSK 和 QPSK 的部分二阶循环累积量还会在某些循环频率处相互抵消导致传统二阶方法在特定调制方式下失效。高阶循环累积量的优势来自高斯噪声的统计特征高斯过程的四阶及以上累积量恒为零。因此对接收信号做非线性变换后噪声项会被大幅抑制而信号中的确定性周期分量会被保留。具体到 MPSK符号 a_k 满足 a_k^M 1所以接收信号经过 M 次方运算后调制相位被完全消除只留下一个载波频率倍频后的复正弦分量。这个分量的频率是 M·f_c幅度与调制阶数和脉冲成型有关但不再随机跳变。M 阶循环累积量在循环频率 α 0 处的切片在数学上可以退化为对 x^M(t) 做时间平均谱分析这就是工程实现的理论依据。2.2 频率映射关系与适用边界设接收机输出的复信号为 x(t) e^{j(2πf_c t θ(t))} n(t)其中 θ(t) 取 MPSK 的 M 个相位状态之一。对 x(t) 做 M 次方后理想情况下 x^M(t) e^{j2πMf_c t}因为 Mθ(t) 是 2π 的整数倍。对 x^M(t) 做谱估计峰值位置 f_peak 满足 f_peak M·f_c于是载波频率估计值就是 f_c_hat f_peak / M。这条映射关系非常简洁但有一个硬性约束在数字域中f_peak 必须落在采样率 fs 的奈奎斯特区间内否则会发生频谱折叠峰值出现在镜像位置除回 M 后得到错误结果。对于复信号要求 M·f_c fs/2也就是说载波频率要留出足够的过采样余量。不同调制阶数需要的循环谱阶数不同这一点在选型时最容易出错。下表列出常见 MPSK 的阶数选择和载波上限约束实际设置时建议载波频率再留 20% 余量避免滚降谱和窗函数旁瓣把峰值推到边界外。调制方式调制阶数 M需要的循环谱阶数峰值谱线位置复信号下载波上限BPSK22 或 42f_c 或 4f_cfs/4 或 fs/8QPSK444f_cfs/88PSK888f_cfs/16先做一个最小验证脚本确认映射关系本身没有问题。这里用恒定相位模拟调制项被消除后的理想条件检查 f_peak/4 是否回推正确。fs 50000; % 采样率 50 kHz fc 8000; % 载波频率 8 kHz N 100000; % 点数 t (0:N-1)/fs; x exp(1j*2*pi*fc*t 1j*pi/4); % 恒定相位模拟 MPSK 相位被消除后的单音 x4 x.^4; % 四阶非线性变换 X4 fftshift(fft(x4, N)); f_axis (-N/2:N/2-1)*fs/N; [~, idx] max(abs(X4)); f_peak f_axis(idx); fc_est f_peak / 4; fprintf(峰值频率: %.2f kHz, 估计载频: %.2f kHz\n, f_peak/1e3, fc_est/1e3);这段代码验证的是算法核心x^4 的峰值频率除以 4 必须等于原始载频。如果这里都不对后面加上调制、噪声和脉冲成型后不可能得到正确结果。参数方面N 取 100000 是为了让 FFT 频率分辨率达到 0.5 Hz避免峰值搜索误差如果不关心精度取 10000 点也能工作但建议先用高分辨率确认映射关系。恒定相位这一个条件非常关键它省去了随机符号的影响专门用来检查倍频链路。2.3 为什么不用二阶循环谱直接估计载频二阶循环谱的谱相关密度在循环频率等于符号速率整数倍的位置有能量谱频率轴上的峰值位置和载波频率相关但实际估计时需要在二维平面上搜索计算量远大于一维 FFT。另一个问题是BPSK 的二阶循环谱在某些循环频率处有较强的离散谱线而 QPSK 有部分项相互抵消导致同一套二阶估计算法无法对所有 MPSK 通用。M 阶循环谱把调制阶数作为参数对 BPSK、QPSK、8PSK 分别选择 2、4、8 阶算法框架不变只是指数不同。对于通信侦察这种对调制方式不确定的场景通常先做调制识别得到 M再选用对应阶数做频率估计如果完全没有先验信息也可以用四阶循环谱先探测 QPSK/BPSK 类信号再根据峰值宽度判断是否需要换更高阶。3. 用 MATLAB 实现 MPSK 高阶循环谱载波频率估计3.1 生成带噪 MPSK 中频信号仿真要自洽第一步先产生一个已知真实载频的 MPSK 信号。下面以 QPSK 为例符号速率取 10 kHz采样率 100 kHz每个符号 10 个采样点载波频率 12 kHz满足 4×12 kHz 100 kHz 的约束。脉冲成型用平方根升余弦滤波器滚降系数 0.35滚降系数会影响信号带宽但不影响载频峰值位置只影响谱线附近的本底噪声。clear; clc; fs 100e3; % 采样率 100 kHz T_total 0.01; % 信号时长 10 ms N fs * T_total; % 总点数 fc 12e3; % 载波频率 12 kHz M 4; % QPSK 调制阶数 Rs 10e3; % 符号速率 10 kHz sps fs / Rs; % 每符号采样数 10 snr_dB 10; % 信噪比 10 dB % 生成随机 QPSK 符号 data randi([0 M-1], 1, round(T_total*Rs)); mod_sym pskmod(data, M, pi/M, gray); % 上采样并通过平方根升余弦滤波器 up_sym upsample(mod_sym, sps); h rcosdesign(0.35, 6, sps, sqrt); baseband filter(h, 1, up_sym); % 截取有效长度避免滤波器延迟导致长度不匹配 baseband baseband(1:N); % 上变频到中频 t (0:N-1)/fs; tx baseband .* exp(1j*2*pi*fc*t); % 加高斯白噪声 rx awgn(tx, snr_dB, measured);代码里的几个参数值得注意sps 必须取整数非整数会导致 upsample 后的长度对不上也会让后续符号速率相关分析失去参考点。rcosdesign 的第三个参数 sps 是滤波器每个符号的采样数这里取 10滤波器长度为 6 个符号周期也就是 60 个抽头。baseband 最终要截断到 N 点因为滤波器延迟会额外产生若干点如果不截断上变频后信号长度会比 t 多出来矩阵维度会报错。3.2 四阶循环谱 α0 切片估计接收信号 rx 是复中频信号带噪声。估计载波频率的完整流程分四步对 rx 做 M 次方去除直流分段加窗做 FFT搜索正频率范围内最大峰值。分段平均是循环谱估计里常用的时域平滑操作能降低单帧 FFT 的方差代价是频率分辨率下降。下面代码把 0.01 秒信号分成 5 段每段 2000 点50% 重叠最后对幅度谱取平均。% 高阶循环谱估计四阶循环累积量 alpha0 切片 xM rx.^M; % 消除调制相位得到 M 倍载频附近谱线 xM xM - mean(xM); % 去掉直流分量抑制零频干扰 % 分段加窗平均 seg_len 2000; overlap 0.5; hop round(seg_len * (1 - overlap)); seg_start 1:hop:length(xM)-seg_len1; num_seg length(seg_start); win hann(seg_len).; X_seg zeros(1, seg_len); for k 1:num_seg seg xM(seg_start(k):seg_start(k)seg_len-1); X fftshift(fft(seg .* win, seg_len)); X_seg X_seg abs(X); end X_avg X_seg / num_seg; % 取正频率部分搜索峰值 f_axis (-seg_len/2 : seg_len/2-1) * fs / seg_len; half seg_len/2 1; [~, idx] max(X_avg(half:end)); f_peak f_axis(idx half - 1); fc_est f_peak / M; fprintf(真实载频: %.2f kHz, 估计载频: %.2f kHz, 误差: %.2f Hz\n, ... fc/1e3, fc_est/1e3, abs(fc_est - fc));逻辑说明xM 的幅度谱峰值理论上出现在 4×12 kHz 48 kHz 处。减均值这一步很重要因为 x^M 里包含一个不随频率变化的直流分量它对应随机符号取平均后的残余项如果不减去峰值可能被错误判到 0 Hz 附近。分段长度 2000 点对应的频率分辨率是 fs/seg_len 50 Hz对 12 kHz 载波来说相对误差约 0.4%足以满足粗估计需求。如果想要更高分辨率可以增加 seg_len但段数会减少噪声方差变大实际使用时应根据信号长度折中。这里把“四阶循环谱估计”落到了具体操作上x^M 的周期图就是循环累积量在 α0 处的切片估计。对于高阶循环谱的严格定义还涉及延迟维度的多维傅里叶变换但载波频率信息集中在零延迟项上因此这个简化在工程上是稳定可靠的。3.3 参数表与关键选型依据下面这张表汇总了完整实现里需要显式设置的参数每个参数都直接影响峰值位置或估计精度。参数名示例值作用影响fs100 kHz采样率决定频率范围过高增加运算量过低导致 M·fc 折叠fc12 kHz真实载波频率必须满足 M·fc fs/2M4调制阶数也是循环谱阶数阶数不匹配时峰值不明显sps10每符号采样数决定脉冲成型长度和带宽snr_dB10信噪比低于 0 dB 时需要更多分段平均seg_len2000分段 FFT 长度越大频率分辨率越高但段数越少overlap0.5重叠率提高段利用率降低估计方差winhann窗函数抑制频谱泄漏但主瓣展宽选型时最容易忽略的是 fc 上限。很多人直接把 fc 设在 fs/4 附近结果 QPSK 做完四次方后峰值落在 fs/2 之外折叠到了负频率搜索正频率时找不到正确峰值。建议先按表里的上限计算再留 20% 余量。另一个常见问题是 M 与调制阶数不匹配比如对 8PSK 使用四阶循环谱a_k^4 不等于 1调制相位没有被完全消除峰值会展宽甚至消失此时必须把代码里的 M 改成 8。4. 仿真验证与参数调优稳、准、不翻车4.1 数据长度、分段数和频率分辨率的取舍估计误差主要由两部分组成FFT 栅栏效应带来的峰值定位误差以及噪声导致的峰值偏移。栅栏效应可以通过增加分段长度来降低比如把 seg_len 从 2000 提到 5000频率分辨率从 50 Hz 降到 20 Hz。但信号总长度是固定的分段变长意味着段数变少平均次数减少噪声对峰值的扰动反而可能变大。一个实用的经验是让分段数至少为 5同时保证 seg_len 足够覆盖几十个符号周期。对于 10 kHz 符号速率2000 点对应 0.02 秒包含 200 个符号统计量已经足够好。如果要同时得到高分辨率和平滑方差可以两种做法叠加先用短分段粗估计峰值位置再用长分段或者直接对粗估计附近的窄带做 CZT 细化。下面这段代码展示了如何用大点数 FFT 在粗估计附近做局部细化。% 粗估计结果 fc_coarse 已得到 search_span 200; % 在粗估计附近 ±200 Hz 内细化 f_start fc_coarse * M - search_span; f_end fc_coarse * M search_span; % 用 chirp-z 变换细化 M*fc 附近的谱 N_total length(xM); fo f_start / fs; % 归一化起始频率 f_step 2 * search_span / fs / 10000; % 细化 10000 个点 Xz czt(xM, 10000, fo, f_step); f_z linspace(f_start, f_end, 10000); [~, idx] max(abs(Xz)); f_peak_refine f_z(idx); fc_est_refine f_peak_refine / M;逻辑说明czt 可以在任意频率区间上做线性调频变换不必改变整个信号的分辨率。它比直接对整段数据做超长 FFT 更省内存因为只计算感兴趣区间。这里搜索带宽是 ±200 Hz细化 10000 个点等效分辨率约 0.04 Hz已经完全超过通信侦察对载频粗估计的需求。参数 search_span 不能设得太大否则 czt 计算量线性增长且可能把其他干扰峰也引入搜索范围。4.2 载波频偏、相位偏移和符号滚降的影响接收信号中常见的相位偏移 φ0 会被 M 次方放大 M 倍变成 Mφ0但这个相位只影响谱峰的复数角度不影响幅度峰值的位置所以频率估计对载波初始相位完全不敏感。频偏本身正是要估计的量算法会在 M·(fcΔf) 处给出峰值除以 M 后得到真实的包含频偏的载波频率这一点对突发解调前的自动频率控制非常有用。符号滚降系数影响的是信号带宽和旁瓣电平。滚降越小频谱越接近理想低通M 次方后载波谱线两侧泄漏越少峰值越尖锐但低滚降会放大定时误差的影响。实际系统中滚降系数在 0.2 到 0.5 之间对频率估计影响不大但如果发现峰值旁边出现对称的小峰可以检查是否由脉冲成型引起的频谱复制造成。符号速率与载波频率之间的相对关系也会影响估计。当 sps 较小时信号带宽占比较大M 次方后频谱在 M·fc 附近可能出现调制残余旁瓣干扰峰值搜索。这种情况建议先用较高阶数的带通滤波把信号带宽限制在 M·fc 附近。相反如果 sps 很大信号是窄带的峰值非常清晰但计算量增加。4.3 三个必调参数和对应错误表现实际调试时下面三个参数最值得优先检查它们各自的失败模式也比较明显。参数错误表现原因与对策M峰值出现在非预期位置或者尖峰变成平顶调制阶数与循环谱阶数不匹配改成对应阶数fc搜索结果始终不对且峰值随信噪比跳变M·fc 超过 fs/2降低 fc 或提高 fsseg_len估计结果抖动大几次运行差几百 Hz分段太长导致段数不足减少 seg_len 或提高重叠率第一行是最隐蔽的问题。QPSK 用二阶层常见做法是搜索 x^2 的峰值但 QPSK 的二次方后调制相位变成 0 和 π 交替峰值展宽频率估计误差大。如果用四次方峰值立刻尖锐。反过来BPSK 用四次方也可以但多做了两次方运算噪声混叠的机会增加因此最佳做法是用和调制阶数一致的 M。第二行的混叠问题可以通过打印 xM 的整个频谱来验证如果峰值出现在负频率或者刚好在 ±fs/2 边界基本可以确定是载波设置不当。第三行是方差和分辨率的经典矛盾信号总长度只有 10000 点时seg_len 取 8000 会得到 3 段平均作用很弱这时把 seg_len 降到 2000overlap 提到 0.75估计稳定性会明显改善。4.4 低信噪比下的实际优化脚本把上述经验合并成一个更完整的估计函数适合 SNR 低于 5 dB 的场景。这个函数用分段平均提高稳健性用抛物线插值狠压栅栏误差最后返回载频估计值。function fc_est mpsk_freq_est_hoc(rx, fs, M, seg_len, overlap, fc_guess) % rx 接收信号复基带或复中频 % fs 采样率 % M 调制阶数 % seg_len 分段长度 % overlap 重叠率0~1 % fc_guess 载波粗估计用于限制搜索范围 xM rx.^M - mean(rx.^M); hop round(seg_len * (1 - overlap)); seg_start 1:hop:length(xM)-seg_len1; X zeros(1, seg_len); win hann(seg_len, periodic).; for k 1:length(seg_start) seg xM(seg_start(k):seg_start(k)seg_len-1); X X abs(fftshift(fft(seg .* win, seg_len))); end X X / length(seg_start); f_axis (-seg_len/2:seg_len/2-1) * fs / seg_len; search_band 1000; % 在粗估计附近 ±1 kHz 搜索 idx_range find(abs(f_axis - M*fc_guess) search_band); [~, idx] max(X(idx_range)); idx_global idx_range(idx); % 抛物线插值修正 if idx_global 1 idx_global seg_len y1 X(idx_global-1); y2 X(idx_global); y3 X(idx_global1); delta 0.5 * (y1 - y3) / (y1 - 2*y2 y3); f_peak f_axis(idx_global) delta * (fs/seg_len); else f_peak f_axis(idx_global); end fc_est f_peak / M; end逻辑说明函数先做 M 次方和去直流再做分段平均得到稳定幅度谱。搜索范围限制在 M·fc_guess 附近 ±1 kHz这个 fc_guess 可以来自上一轮粗估计也可以来自其他频率粗测算法。抛物线插值利用了离散谱峰周围三个点的幅度关系估计出真实峰值的子格点位置把栅栏误差从半个频率分辨率降到零点几个分辨率。注意插值公式只在峰值不是出现在频谱边界时有效所以有边界判断。5. 用抛物线插值和自适应阶数把估计误差压进 Hz 级到了最终使用阶段载波频率估计通常还要再接自动频率控制或解调器误差要求明显高于仿真场景。这里有一个具体技巧把抛物线插值换成更精细的“局部缩放”搜索即先用分段平均得到粗峰再对粗峰周围一小段区间做 chirp-z 变换。前面第 4 章给出的 czt 代码已经演示了思路实际落地时可以把细化带宽设成粗峰估计误差的 5 倍比如粗估计误差约 50 Hz细化带宽取 ±250 Hz细化点数在 2000 到 5000 之间这时等效分辨率可以达到 0.1 Hz 到 0.25 Hz。这样处理后的 fc_est 对后续 PSK 解调而言基本不会成为瓶颈。另一个容易被忽略的技巧是自适应选择循环谱阶数。如果接收信号调制方式未知可以先计算 M 2、4、8 三条谱线的峰度指标取峰度最大者作为最终阶数。峰度计算并不复杂就是用幅度谱峰值附近的能量除以总能量调制匹配的阶数会得到明显更高的集中度。下面的代码展示了这个判断思路。function best_M detect_order_hoc(rx, fs, M_list) % rx 接收信号fs 采样率M_list 候选调制阶数向量 peak_metric zeros(size(M_list)); for k 1:length(M_list) M M_list(k); xM rx.^M - mean(rx.^M); N length(xM); X abs(fftshift(fft(xM, N))); f_axis (-N/2:N/2-1)*fs/N; half_idx N/21:N; [~, mid] max(X(half_idx)); peak_i half_idx(mid); % 以峰值周围 5 个点为信号能量其余视为噪声 local peak_i-2:peak_i2; if all(local 1) all(local N) sig_power sum(X(local).^2); else sig_power X(peak_i)^2; end total_power sum(X.^2); peak_metric(k) sig_power / total_power; end [~, idx] max(peak_metric); best_M M_list(idx); end这个 detector 的核心思想是匹配的阶数会让能量高度集中在单根谱线附近不匹配的阶数则会把能量散布到多个频率位置。实际使用中候选 M_list 一般设成 [2 4 8]对应 BPSK、QPSK、8PSK 三类常见信号。如果接收信号里存在多径或者相位噪声较强峰度指标会下降但相对大小依然正确因此这个技巧在低信噪比下也成立。最后再确认一次载波上限检测出的 best_M 必须满足 best_M·fc_max fs/2否则可以放弃该候选阶数或降低采样率重新分析。这套流程跑完输出的 fc_est 通常能稳定落在真实载频附近很小范围内足够直接喂给后续载波同步环路作为初始频差。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进