不同频率的穿越率

image


import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert

# 设置中文字体,避免中文显示为方块
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ============================================================
# 1. 参数设置
# ============================================================
fs = 4000.0                  # 采样频率 (Hz)
T = 15.0                     # 总时长 (s)
N = int(fs * T)              # 采样点数
t = np.linspace(0, T, N, endpoint=False)  # 时间轴

# 频率轴(只取正频率部分,rfft 对应)
freq = np.fft.rfftfreq(N, d=1/fs)         # 频率 (Hz)
omega = 2 * np.pi * freq                  # 角频率 (rad/s)

# ============================================================
# 2. 高斯型谱密度(不同中心频率)
# ============================================================
def spectrum_gaussian(f0, Delta_f):
    """
    生成高斯型窄带谱密度。
    参数:
        f0      : 中心频率 (Hz)
        Delta_f : 带宽 (Hz)
    返回:
        S       : 谱密度数组,长度与 omega 相同
    """
    omega0 = 2 * np.pi * f0                # 中心角频率
    sigma_w = np.pi * Delta_f / 3          # 高斯谱的标准差(角频率)
    # 高斯函数:S(ω) = exp(-(ω-ω0)^2 / (2σ_w^2))
    S = np.exp(-(omega - omega0)**2 / (2*sigma_w**2))
    S[omega < 0] = 0                       # 负频率部分置零
    return S

def generate_process(S_omega, N, fs):
    """
    根据给定的单边谱密度 S_omega 生成平稳高斯过程。
    方法:在频域赋予随机相位,再做逆傅里叶变换。
    """
    # 构造双边谱:正频率 + 负频率(对称)
    S_full = np.concatenate([S_omega, S_omega[-2:0:-1]])
    # 随机相位,均匀分布在 [0, 2π)
    phase = np.random.uniform(0, 2*np.pi, len(S_full))
    # 频域幅值:sqrt(S) * exp(i*phase)
    X = np.sqrt(S_full) * np.exp(1j * phase)
    # 逆傅里叶变换得到时域信号
    x = np.fft.ifft(X).real
    # 归一化,使方差与谱密度积分一致
    x = x * np.sqrt(fs * N)
    # 去除均值,确保零均值
    return x - np.mean(x)

def count_crossings(y, fs, level=0.0):
    """
    统计正穿越 level 的次数,并返回频率 (Hz)。
    正穿越定义:y[i] < level 且 y[i+1] >= level
    """
    idx = np.where((y[:-1] < level) & (y[1:] >= level))[0]
    return len(idx) / (len(y) / fs)

def compute_theory(S_omega, fs, N):
    """
    由谱密度直接计算理论穿零频率。
    ν0 = (1/2π) * sqrt(m2 / m0)
    其中 m0 = ∫S(ω)dω, m2 = ∫ω^2 S(ω)dω
    """
    m0 = np.sum(S_omega) * (fs / N)        # 零阶谱矩
    m2 = np.sum(omega**2 * S_omega) * (fs / N)  # 二阶谱矩
    return (1/(2*np.pi)) * np.sqrt(m2 / m0)

np.random.seed(42)            # 固定随机种子,保证结果可复现

# ============================================================
# 3. 不同中心频率,同一高斯谱型
# ============================================================
f0_list = [30, 50, 80, 120, 180, 250]   # 6 个中心频率 (Hz)
Delta_f = 10.0                           # 带宽固定为 10 Hz
colors = plt.cm.viridis(np.linspace(0, 1, len(f0_list)))  # 颜色渐变

results = []                             # 存储每个频率的结果
for f0, color in zip(f0_list, colors):
    S = spectrum_gaussian(f0, Delta_f)   # 生成谱密度
    y = generate_process(S, N, fs)       # 生成时域过程
    nu0_emp = count_crossings(y, fs, 0.0)  # 实测穿零频率
    nu0_theory = compute_theory(S, fs, N)  # 理论穿零频率
    results.append({
        'f0': f0, 'S': S, 'y': y, 'color': color,
        'nu0_emp': nu0_emp, 'nu0_theory': nu0_theory
    })
    # 打印对比结果
    print(f"f0={f0:3d} Hz: ν0实测={nu0_emp:6.2f} Hz, ν0理论={nu0_theory:6.2f} Hz")

# ============================================================
# 4. 单张大图:每个频率一个子图
# ============================================================
fig = plt.figure(figsize=(20, 14))
fig.suptitle('不同中心频率的高斯谱:穿越率变化', fontsize=18, fontweight='bold', y=0.98)

# GridSpec: 3 行 6 列,hspace/wspace 控制子图间距
gs = fig.add_gridspec(3, 6, hspace=0.5, wspace=0.4,
                      left=0.05, right=0.98, top=0.93, bottom=0.05)

# ---- 第一行:6个谱密度,各自一个子图 ----
for i, res in enumerate(results):
    ax = fig.add_subplot(gs[0, i])
    ax.plot(freq, res['S'], color=res['color'], linewidth=1.8)     # 画谱密度曲线
    ax.fill_between(freq, 0, res['S'], color=res['color'], alpha=0.2)  # 填充下方区域
    ax.axvline(res['f0'], color='k', linestyle='--', linewidth=1.0, alpha=0.5)  # 标注中心频率
    ax.set_title(f'谱密度 f0={res["f0"]} Hz', fontsize=10)
    ax.set_xlabel('频率 (Hz)')
    ax.set_ylabel('S(ω)')
    ax.set_xlim(0, 300)                  # 限制频率范围
    ax.grid(True, alpha=0.3)

# ---- 第二行:6个时间波形,各自一个子图 ----
for i, res in enumerate(results):
    ax = fig.add_subplot(gs[1, i])
    t_show = t < 0.3                     # 只显示前 0.3 秒,便于观察波形
    ax.plot(t[t_show], res['y'][t_show], color=res['color'], linewidth=0.8)
    ax.set_title(f'波形 f0={res["f0"]} Hz\nν0={res["nu0_emp"]:.1f} Hz', fontsize=10)
    ax.set_xlabel('时间 (s)')
    ax.set_ylabel('y(t)')
    ax.grid(True, alpha=0.3)

# ---- 第三行:穿越率对比 ----
# 3.1 穿越率柱状图(占 2 列)
ax = fig.add_subplot(gs[2, 0:2])
f0_arr = [r['f0'] for r in results]
nu0_emp_arr = [r['nu0_emp'] for r in results]
nu0_theory_arr = [r['nu0_theory'] for r in results]
x = np.arange(len(f0_arr))
width = 0.35
ax.bar(x - width/2, nu0_emp_arr, width, label='实测', color='steelblue')
ax.bar(x + width/2, nu0_theory_arr, width, label='理论', color='coral')
ax.set_xticks(x)
ax.set_xticklabels([f'{f}' for f in f0_arr])
ax.plot(x, f0_arr, 'k--o', markersize=6, label='ν0 = f0')   # 理想线
ax.set_title('③ 穿越率对比', fontsize=12)
ax.set_xlabel('中心频率 f0 (Hz)')
ax.set_ylabel('ν0⁺ (Hz)')
ax.legend()
ax.grid(True, alpha=0.3)

# 3.2 穿越率 vs 中心频率(散点 + 拟合线,占 2 列)
ax = fig.add_subplot(gs[2, 2:4])
ax.plot(f0_arr, nu0_emp_arr, 'bo-', markersize=10, label='实测')
ax.plot(f0_arr, nu0_theory_arr, 'r--', linewidth=2, label='理论')
ax.plot(f0_arr, f0_arr, 'k:', linewidth=2, label='ν0 = f0')  # 理想线
ax.set_title('④ 穿越率 vs 中心频率', fontsize=12)
ax.set_xlabel('中心频率 f0 (Hz)')
ax.set_ylabel('ν0⁺ (Hz)')
ax.legend()
ax.grid(True, alpha=0.3)

# 3.3 归一化穿越率(占 2 列)
ax = fig.add_subplot(gs[2, 4:6])
ratio = np.array(nu0_emp_arr) / np.array(f0_arr)   # ν0 / f0
ax.plot(f0_arr, ratio, 'bo-', markersize=10)
ax.axhline(1.0, color='r', linestyle='--', linewidth=2, label='ν0/f0 = 1')  # 理想值
ax.set_title('⑤ 归一化穿越率 ν0/f0', fontsize=12)
ax.set_xlabel('中心频率 f0 (Hz)')
ax.set_ylabel('ν0⁺ / f0')
ax.set_ylim(0.9, 1.1)                  # 限制纵轴范围,突出偏离程度
ax.legend()
ax.grid(True, alpha=0.3)

plt.show()
posted @ 2026-10-13 11:14  redufa  阅读(5)  评论(0)    收藏  举报