扫频式频谱仪中频算法仿真代码

打开MATLAB敲下第一行代码的时候,我盯着屏幕上闪烁的光标突然意识到——这玩意儿和真实的频谱仪之间,隔着的可能不只是几行代码的距离。扫频式频谱仪的中频处理就像个黑盒子,今天咱们就试着用代码撬开它看看。

先来段混频的骚操作:

def mixer(if_signal, lo_freq, sample_rate):
    t = np.arange(len(if_signal)) / sample_rate
    lo_i = np.cos(2 * np.pi * lo_freq * t)
    lo_q = -np.sin(2 * np.pi * lo_freq * t)
    return if_signal * lo_i, if_signal * lo_q

这代码看着简单,实际上正交本振信号的负号藏着玄机。有次我把负号写反了,结果相位谱直接放飞自我,调了三天才发现是这里的问题。注意那个-sin可不是随便写的,它保证了I/Q两路信号的正交性,就像咖啡配奶泡的黄金组合。

接下来轮到FIR滤波器登场:

% 设计带通滤波器
filt_order = 64;
nyq = sample_rate/2;
b = fir1(filt_order, [flow/nyq, fhigh/nyq], 'bandpass', hamming(filt_order+1));
filtered_i = filter(b, 1, i_mixed);
filtered_q = filter(b, 1, q_mixed);

这里有个坑爹的地方:滤波器阶数必须足够高才能保证陡峭的滚降。但阶数太高又会导致实时处理时延迟爆炸,就像用高射炮打蚊子。有次为了抓个小信号把阶数设到256,结果扫频速度慢得能泡碗面。

扫频式频谱仪中频算法仿真代码

检波环节最容易被轻视:

def detector(i, q):
    magnitude = np.sqrt(i**2 + q**2)
    window_size = int(sample_rate / rbw)
    return uniform_filter1d(magnitude, window_size, mode='nearest')

这个滑动窗最大值滤波才是频谱显示的真正灵魂。RBW(分辨率带宽)参数直接决定窗口大小,搞不好会把脉冲信号显示成小山包。记得有次参数设错,1MHz的RBW硬是把10kHz的信号给吞了,现场演示直接翻车。

最后拼装整个处理链:

def sweep_processor(rf_signal, start_freq, stop_freq, rbw):
    spectrum = []
    for current_lo in np.linspace(start_freq, stop_freq, 1000):
        i, q = mixer(rf_signal, current_lo, sample_rate)
        # 下变频到零中频
        i_low = low_pass_filter(i)
        q_low = low_pass_filter(q)
        # 检波后处理
        detected = detector(i_low, q_low)
        spectrum.append(np.max(detected))
    return spectrum

这个循环扫频的过程就像用放大镜一寸寸检查频谱。调试时建议先拿单频信号试刀,比如生成个10MHz+20MHz的双音信号,看看频谱图上能不能准确跳出两个尖峰。

仿真跑起来后,突然发现实际频谱仪的工程师真不容易——那些丝般顺滑的频谱显示背后,藏着无数个深夜调试滤波器的黑眼圈。代码虽然能仿真个大概,但真实世界的电磁噪声、相位噪声这些幺蛾子,可不是几行Python能完全复现的。不过至少下次用频谱仪时,看到跳动的轨迹线,能会心一笑:这哥们儿现在正执行着和我差不多的算法呢。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐