昨天的问题,有个读者询问:

好!
我觉得这是真真正正的读过思考过,也是一个好问题~其实这是在考虑 相干采样时如何避免重复模式 的问题。
我们先看条件:
相干采样要求:
即在采样窗口里恰好包含 C 个整数周期。这样 FFT 就不会发生谱泄漏。
有的教材或论文会建议:
让 C(周期数)是质数,或者让 N(采样点数)与 C(周期数)互质,
目的:避免“相位点重复模式”;如果 和 有公因数,信号的采样点可能在窗口内呈现某种重复格局(比如相位只取有限几个点),会让拟合或谐波分析“退化”。
举例:
,,,则 C=1。相当于只采了 1 个周期,FFT 会有很强的不确定性。
如果 C=2,N=40,信号其实就是重复两遍,频域信息有限;若 C=质数(比如 4093),采样点遍布整个正弦周期的不同相位,重复性最小。

A

B

C
上面这个 可视化 demo 展示了 3 种“相干采样”场景下,相位点分布与 FFT 峰值 的差异:
Case A(gcd(N,C)>1,示例 N=64、C=16)相位点在单位圆上出现强重复(只占据少数相位),虽然 FFT 峰仍然精确落在基频 bin,但“相位采样不充分”,对某些拟合/非正弦分析会不利。
Case B(互质,N=64、C=15)相位点在 0~2π 均匀铺开,没有重复模式;FFT 峰依旧精确对齐。这是工程上非常推荐的选择。
Case C(C 取质数且与 N 互质,N=64、C=17)同样均匀铺开,且质数 C 更容易避免隐藏的周期性重复。对于高精度拟合/谐波分析更“稳”。
结论:
相干采样(C 为整数)保证了不泄漏、峰值正落 bin;让 C 与 N 互质(甚至 C 选质数),能最大化相位覆盖度、避免“相位点重复模式”,对sine fit、THD、相位估计更友好;
工程实践上:固定 N(比如 65536),为每个采样率 Fs 选一个互质/质数的 C,把信号源频率设成
就能让峰值稳在 1 kHz 对应的 bin,同时避免重复采样相位。
FFT 分辨率:比“质数”更重要。工程上优先考虑 N 要多大(够长时间窗)。
FFT 高效计算:常常把 N 选成 2 的幂次,这和“质数周期”有矛盾。
我个人觉得大多数情况下,即便 C 不是质数,只要 N 够大,频率估计/幅值恢复都不会受太大影响。不强制必须质数,关键是 C 和 N 互质,能避免重复采样点模式;工程里优先满足 长时间窗 / FFT 效率,再在可行范围里把 C 选成质数(或至少与 N 互质),是更稳妥的做法。
import numpy as np
import matplotlib.pyplot as plt
from math import gcd
def demo_case(N, C, title):
"""
N: 采样点数
C: 窗口内周期数(相干采样:C 整数)
f0 = C/N * Fs
"""
Fs = 1.0 # 只看相对关系即可,设 Fs=1,f0=C/N
f0 = C / N * Fs
t = np.arange(N) / Fs
x = np.sin(2*np.pi*f0*t)
# 计算相位分布(mod 2π)以及相位点个数
phase = (2*np.pi*f0*t) % (2*np.pi)
unique_phases = len(np.unique(np.round(phase, 12))) # 去重
g = gcd(N, C)
# FFT(相干,无泄漏),看峰是否正落 bin
X = np.fft.rfft(x) / (N/2) # 简单归一化
freqs = np.fft.rfftfreq(N, d=1/Fs)
mag = 20*np.log10(np.abs(X) + 1e-15)
# 绘图
fig, axes = plt.subplots(1, 3, figsize=(13, 3.5))
# (1) 相位分布在单位圆
theta = np.linspace(0, 2*np.pi, 400)
axes[0].plot(np.cos(theta), np.sin(theta)) # 单位圆
axes[0].plot(np.cos(phase), np.sin(phase), '.', ms=6)
axes[0].set_aspect('equal', 'box')
axes[0].set_title(f"{title}\nN={N}, C={C}, gcd(N,C)={g}\nunique phases={unique_phases}")
axes[0].set_xlabel("cos(phase)")
axes[0].set_ylabel("sin(phase)")
axes[0].grid(True)
# (2) 时间域(显示前两周期)
T0 = 2 / f0 # 两个周期的时长
idx = t <= T0
axes[1].plot(t[idx], x[idx], 'o-')
axes[1].set_title("Time domain (first 2 periods)")
axes[1].set_xlabel("time")
axes[1].set_ylabel("amplitude")
axes[1].grid(True)
# (3) 频域
axes[2].plot(freqs, mag, '-')
axes[2].axvline(f0, ls='--')
axes[2].set_xlim(0, 4*f0) # 看 0~4f0 的范围
axes[2].set_title("FFT magnitude (dB)")
axes[2].set_xlabel("frequency (bins*Fs)")
axes[2].set_ylabel("dB")
axes[2].grid(True)
plt.tight_layout()
plt.show()
# 三个对比用例:
# A) gcd(N,C)>1:相位会重复,unique phases 少(例如 N=64, C=16, gcd=16)
demo_case(N=64, C=16, title="Case A: 相位点强重复(gcd>1)")
# B) 互质:gcd(N,C)=1,相位分布最均匀(N=64, C=15)
demo_case(N=64, C=15, title="Case B: 互质(相位均匀展开)")
# C) C 为质数且与 N 互质(N=64, C=17)
demo_case(N=64, C=17, title="Case C: C取质数且互质(更稳)")