You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FSK信号解调故障:频率检测异常导致解调比特流错误

FSK信号解调故障:频率检测异常导致解调比特流错误

看起来你在FSK信号的频率估计和解码环节都遇到了问题,咱们一步步梳理可能的核心原因和改进方向:

先揪出最可能的致命错误

你代码里的采样率设置有严重问题!注释写着fs = 46242 # I use the bandwith of my signal——fs必须是采集IQ信号时的实际采样率(比如软件无线电设备的采样率,如1MSPS、2.4MSPS),不是信号的带宽。采样率错误会导致所有频率计算、符号时长的采样点步长完全失真,这大概率是你解调失败的核心原因之一。

频率估计函数的问题与改进

你的频率检测逻辑存在频谱泄漏、峰值阈值不合理等问题,导致无法准确捕获FSK的目标频率:

问题点

  1. 未加窗处理:FFT前没有加汉宁窗/汉明窗,导致FSK信号的频谱旁瓣过高,可能让峰值检测到错误的频率位置。
  2. 峰值阈值过松:用np.max(...) /10作为高度阈值,容易把噪声峰误判为信号峰,尤其是当信号有较强旁瓣时。
  3. 正频率过滤逻辑冗余:先找峰再过滤正频率,不如先只保留正频率频谱再找峰,逻辑更清晰。

改进后的频率估计函数

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks, butter, filtfilt
from tkinter import filedialog

def estimate_frequencies(signal, fs, niveau_fsk):
    # 加汉宁窗减少频谱泄漏
    window = np.hanning(len(signal))
    windowed_signal = signal * window
    
    fft_result = np.fft.fft(windowed_signal)
    fft_freq = np.fft.fftfreq(len(windowed_signal), 1/fs)

    # 仅保留正频率部分的频谱和频率轴
    positive_mask = fft_freq > 0
    half_spectrum = fft_result[positive_mask]
    half_freqs = fft_freq[positive_mask]
    spectrum_amp = np.abs(half_spectrum)

    # 调整峰高阈值为最大幅度的30%(可根据实际信号调整)
    peak_height = np.max(spectrum_amp) * 0.3
    peaks, _ = find_peaks(spectrum_amp, height=peak_height)

    # 按幅度从大到小排序,取前niveau_fsk个峰
    peak_amps = spectrum_amp[peaks]
    sorted_peak_indices = np.argsort(-peak_amps)[:niveau_fsk]
    estimated_freqs = half_freqs[peaks[sorted_peak_indices]]

    # 返回排序后的频率,方便后续解码匹配
    return np.sort(estimated_freqs)

解调函数的核心问题与修复

你的解码逻辑忽略了相位解缠绕,这会导致瞬时频率出现大量异常值,完全无法代表真实的信号频率:

问题点

  1. 未做相位解缠绕:np.angle(signal)得到的相位会在±π之间跳变,np.diff计算的瞬时频率会出现巨大的跳变(比如从fs/2跳到-fs/2),导致每个符号段的均值频率完全错误。
  2. 瞬时频率未平滑:没有做低通滤波,噪声会干扰均值频率的计算。
  3. 循环越界风险:instantaneous_frequency比原信号短1个点(因为np.diff),循环时可能会取到越界的片段。

改进后的解调函数

def demodulate_fsk(signal, fs, freqs, symbol_duration):
    # 对相位做解缠绕,消除±π的跳变
    instantaneous_phase = np.unwrap(np.angle(signal))
    # 计算瞬时频率
    instantaneous_frequency = np.diff(instantaneous_phase) * fs / (2.0 * np.pi)

    # 低通滤波平滑瞬时频率,过滤噪声
    symbol_rate = 1 / symbol_duration
    # 截止频率设为符号速率的2倍,保留信号成分的同时过滤高频噪声
    def butter_lowpass(cutoff, fs, order=3):
        nyq = 0.5 * fs
        normal_cutoff = cutoff / nyq
        b, a = butter(order, normal_cutoff, btype='low', analog=False)
        return b, a
    b, a = butter_lowpass(symbol_rate * 2, fs)
    instantaneous_frequency_smoothed = filtfilt(b, a, instantaneous_frequency)

    decoded_bits = []
    step_size = int(symbol_duration * fs)
    max_idx = len(instantaneous_frequency_smoothed)

    # 循环处理每个符号段,避免越界
    for i in range(0, max_idx, step_size):
        segment_end = min(i + step_size, max_idx)
        segment = instantaneous_frequency_smoothed[i:segment_end]
        mean_freq = np.mean(segment)
        # 找到与均值频率最接近的目标频率
        decoded_bit = np.argmin(np.abs(mean_freq - freqs))
        decoded_bits.append(decoded_bit)
    
    return np.array(decoded_bits)

主程序的补充优化

# 关键:修改为采集IQ信号时的实际采样率,不是带宽!
# 比如如果用RTL-SDR采集,可能是1e6(1MSPS),请确认后修改
fs = 46242  # 这里务必替换为正确的实际采样率!
niveau_fsk = 2
symbol_duration = 1 / 19608.5  # 符号周期=1/符号速率,确认符号速率是否正确

filename = filedialog.askopenfilename(title="Selection d'un fichier IQ")
# 读取int16格式的IQ数据,转换为complex64
signal = np.fromfile(filename, dtype=np.int16).astype(np.float32).view(np.complex64)
# 归一化IQ信号到[-1, 1]区间,避免数值范围问题
signal = signal / 32767.0

# 估计FSK频率
freqs = estimate_frequencies(signal, fs, niveau_fsk)
print("Fréquences estimées :", freqs)

# 解调FSK信号
decoded_bits = demodulate_fsk(signal, fs, freqs, symbol_duration)
print(decoded_bits)

调试建议

  1. 验证采样率:先确认fs的正确值,这是所有计算的基础,采样率错误后续优化都无意义。
  2. 可视化频谱:在estimate_frequencies函数末尾添加绘图代码,看看检测到的峰值是否是真实的FSK频率:
    plt.plot(half_freqs, spectrum_amp)
    plt.scatter(estimated_freqs, spectrum_amp[np.where(np.in1d(half_freqs, estimated_freqs))], color='red', s=100)
    plt.xlabel("Frequency (Hz)")
    plt.ylabel("Amplitude")
    plt.show()
    
  3. 查看瞬时频率:在解调函数里绘制平滑后的瞬时频率,确认没有异常跳变:
    plt.plot(instantaneous_frequency_smoothed)
    plt.xlabel("Sample Index")
    plt.ylabel("Instantaneous Frequency (Hz)")
    plt.show()
    

备注:内容来源于stack exchange,提问作者Bast38

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:04:52