FSK信号解调故障:频率检测异常导致解调比特流错误
FSK信号解调故障:频率检测异常导致解调比特流错误
看起来你在FSK信号的频率估计和解码环节都遇到了问题,咱们一步步梳理可能的核心原因和改进方向:
先揪出最可能的致命错误
你代码里的采样率设置有严重问题!注释写着fs = 46242 # I use the bandwith of my signal——fs必须是采集IQ信号时的实际采样率(比如软件无线电设备的采样率,如1MSPS、2.4MSPS),不是信号的带宽。采样率错误会导致所有频率计算、符号时长的采样点步长完全失真,这大概率是你解调失败的核心原因之一。
频率估计函数的问题与改进
你的频率检测逻辑存在频谱泄漏、峰值阈值不合理等问题,导致无法准确捕获FSK的目标频率:
问题点
- 未加窗处理:FFT前没有加汉宁窗/汉明窗,导致FSK信号的频谱旁瓣过高,可能让峰值检测到错误的频率位置。
- 峰值阈值过松:用
np.max(...) /10作为高度阈值,容易把噪声峰误判为信号峰,尤其是当信号有较强旁瓣时。 - 正频率过滤逻辑冗余:先找峰再过滤正频率,不如先只保留正频率频谱再找峰,逻辑更清晰。
改进后的频率估计函数
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)
解调函数的核心问题与修复
你的解码逻辑忽略了相位解缠绕,这会导致瞬时频率出现大量异常值,完全无法代表真实的信号频率:
问题点
- 未做相位解缠绕:
np.angle(signal)得到的相位会在±π之间跳变,np.diff计算的瞬时频率会出现巨大的跳变(比如从fs/2跳到-fs/2),导致每个符号段的均值频率完全错误。 - 瞬时频率未平滑:没有做低通滤波,噪声会干扰均值频率的计算。
- 循环越界风险:
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)
调试建议
- 验证采样率:先确认fs的正确值,这是所有计算的基础,采样率错误后续优化都无意义。
- 可视化频谱:在
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() - 查看瞬时频率:在解调函数里绘制平滑后的瞬时频率,确认没有异常跳变:
plt.plot(instantaneous_frequency_smoothed) plt.xlabel("Sample Index") plt.ylabel("Instantaneous Frequency (Hz)") plt.show()
备注:内容来源于stack exchange,提问作者Bast38
相关产品推荐
相关产品推荐

