核心概念:什么是“数字下变频”?
简单说,天线接收到的信号频率通常很高(比如图中的 75MHz),就像在一辆高速飞驰的列车上。但是我们的计算机(DSP/FPGA)想要仔细处理这个信号,最好让它停下来,或者变慢一点(变成 0Hz 附近的基带信号)。
数字下变频(DDC)的任务就是:把高频信号“搬”到零频率(0Hz),并把多余的“废料”切掉,最后降低数据量以便于处理。
第一部分:图解流程(对应图 25.10)
请对照你上传的第一张图(带有 1-9 步骤的图),我们一步步来看:
第一阶段:数字化(把模拟信号变成数字)
-
第 1 行(实数 IF 信号): 这是原始信号。注意看,它有两个梯形块,一个在正频率(右边),一个在负频率(左边)。
-
通俗点: 在信号处理的世界里,实数信号(真实存在的电压)在频谱上总是像照镜子一样成对出现的。你看到 +75MHz 有东西,-75MHz 也一定有个一模一样的影子。
-
-
第 2 行(采样波形): 这是一个频率为 100MHz 的采样时钟。就像相机的快门,每秒咔嚓 1 亿次。
-
第 3 行(采样后的信号): 重点来了! 采样的在频域的效果就是**“无限复制”**。
-
原始信号(第1行)被不断地向左、向右复制。
-
你可以理解为:原来的信号在 +75 和 -75。现在以 100MHz 为周期进行搬移。
-
比如:-75 + 100 = 25MHz;+75 - 100 = -25MHz。
-
所以你看第 3 行,信号变得密密麻麻,但在中心附近(0Hz左右)出现了两个新的梯形(其实是在 ±25MHz 处)。
-
第二阶段:搬运(混频/变频)
-
第 4 行(-75MHz 复数单音): 这是一个“搬运工”。它的作用是把整个频谱向左移动 75MHz。
-
为什么是复数? 只有复数信号才能只有“单边”频率(你看它只有左边那个箭头,右边没有)。这就像一个单向传送带。
-
-
第 5 行(频移后的信号): 这是第 3 行和第 4 行作用的结果。
-
第 3 行的所有东西都向左平移了 75MHz。
-
关键点: 原来在 +75MHz 的那个梯形,向左移 75MHz,正好落在了 0Hz(中间虚线) 的位置!
-
这就是我们的目的:把高频信号搬到了 0Hz(基带)。
-
第三阶段:清理(滤波)
-
第 6 行(低通滤波器响应): 这是一个“筛子”或者“闸门”。它只允许中间 0Hz 附近的信号通过,两边高频的都要挡住。
-
第 7 行(复数基带信号): 经过筛子后,旁边乱七八糟的重复波形都没了,只剩下我们要的那个停在 0Hz 的梯形波。
-
这时候信号已经很干净了,但是数据量还很大(因为采样率还是 100MHz)。
-
第四阶段:减负(抽取)
-
第 8 行(二抽一): 既然信号已经很慢了(在 0Hz 附近,带宽很窄),我们不需要那么快的快门(100MHz)去记录它了,太浪费存储空间。
-
我们决定“隔一个扔一个”,这就是“二抽一”。现在的采样率变成了 50MHz。
-
-
第 9 行(最终结果): 这就是最终处理好的信号:
-
位置在 0Hz(基带,方便分析)。
-
没有多余的干扰。
-
数据量只有原来的一半(50MHz),计算压力小。
-
第二部分:为什么要这么做?(对应后两张文字图的解释)
这里的文字主要讲了两个好处:
1. 信噪比(SNR)增益(为什么滤波能变清晰?)
图 3 提到:“低通滤波器还减小带外噪声……SNR 增加了 3dB”。
-
通俗解释: 想象你在一个嘈杂的房间里录音(信号+全频段噪声)。 当你用“低通滤波器”把高频部分切掉时,你也顺带把那部分的噪音给切掉了。但是你的人声(信号)还在。
-
信号没变,噪音变少了 = 信噪比(SNR)提高了。
-
书中计算:带宽减少一半(1/2),噪音能量减少一半,信噪比提高 3dB。这对雷达检测微弱目标非常有用。
-
2. 比起模拟电路的好处
图 3 最后一段提到:“与模拟下变频相比……避免了 I/Q 失配、直流漂移……”
-
通俗解释:
-
模拟电路(旧方法): 需要用真实的电容、电感、混频器。这些硬件容易受温度影响,而且很难造得两个一模一样(I路和Q路很难平衡),这会导致信号失真。
-
数字电路(新方法): 全是数学计算(乘法和加法)。数学是不会受温度影响的,1+1永远等于2。所以数字下变频非常精准、稳定,没有硬件误差。
-
总结
这段书其实就在讲一个**“榨汁”**的过程:
-
连皮带肉扔进去(100MHz 采样)。
-
对准位置(移频到 0Hz)。
-
过滤掉渣滓(低通滤波,去掉无用频率和噪音,提高 SNR)。
-
浓缩(抽取,降低数据率)。
为什么实数是双边的
1. 核心直觉:实数只是“旋转”的投影
想象一个场景:
桌子上有一个旋转的轮盘(复数信号),上面插着一根筷子。当你从侧面用灯光照过去,墙上会投射出这根筷子的影子(实数信号)。
-
轮盘(复数信号): 它在实实在在地旋转,它包含完整的信息(幅度和相位)。如果它逆时针转,我们叫它正频率。
-
墙上的影子(实数信号): 它只是上下移动(或者左右摆动)。
现在问题来了:如果我只给你看墙上的影子(实数信号),让你还原出轮盘是怎么转的,你会发现有两种可能:
-
轮盘是逆时针转的(正频率)。
-
轮盘是顺时针转的(负频率)。
因为这两种转法,投射在墙上的影子完全一模一样!
为了数学上的严谨,欧拉(一位伟大的数学家)发现了一个天才的处理办法:
与其去猜到底是顺时针还是逆时针,不如假设它们同时存在!
也就是说:一个实实在在的摆动(实数信号),可以被看作是两个相反方向旋转的轮盘的“合成”。
-
一个轮盘向左转(正频率)。
-
一个轮盘向右转(负频率)。
-
当它们转速相同、方向相反时,它们的“虚部”(侧向的力)互相抵消了,只剩下“实部”(上下的力)叠加在一起。
这就是为什么实数信号是双边的:
任何一个实数信号,在数学描述上,都必须由“一半正频率”和“一半负频率”共同组成。如果你把负频率的那一半扔掉,它就不再是实数,而变成复数了(就像只剩下一个轮盘在转)。
2. 数学上的“证据”
即使你数学不好,看这个简单的公式也能明白(这是著名的欧拉公式的变形):

3. 回到你的图中
请看你第一张图的 第 1 行(实数 IF 信号):
-
因为它是实数(现实中存在的电压波形),所以它在频域上必须是对称的。
-
右边有一个“梯形块”(正频率的成分)。
-
左边必须有一个镜像的“梯形块”(负频率的成分)。
-
这就是为什么图上画了左右两块。
对比第 4 行(-75MHz 复数单音):
-
这行字特意写了是**“复数”**。
-
所以它不需要对称,它只有一边(只有负频率那边有一个尖头)。这意味着它是一个真正的、单纯在旋转的轮盘,而不是墙上的影子。
总结
“实数是双边的” 这句话的意思是:为了用数学完美的描述现实世界中忽上忽下的震动,我们需要假设存在正反两个方向的旋转能量。负频率不是物理存在的“负数次数”,它是为了平衡数学方程而存在的“镜像分身”
数字下变频的python实现
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, lfilter
# =================配置区域=================
# 设置中文字体,防止画图时中文显示为方框
# Windows通常使用 'SimHei' (黑体), Mac 可能需要 'Arial Unicode MS'
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 保存图片的文件夹路径(默认当前目录)
SAVE_PREFIX = "DDC_Step_"
print("🚀 === 数字下变频 (DDC) 仿真演示开始 ===\n")
# ================= 0. 基础参数设置 =================
# 为了模拟真实世界看似连续的波形,我们用超高采样率
fs_analog = 1000e6 # 模拟世界的时间精度: 1000 MHz
duration = 20e-6 # 信号持续时间: 20微秒
t_analog = np.arange(0, duration, 1 / fs_analog)
# 关键参数 (与书中保持一致)
f_center = 75e6 # 信号中心频率: 75 MHz
bw_signal = 10e6 # 信号带宽: 10 MHz
fs_adc = 100e6 # ADC采样率: 100 MHz
fs_final = 50e6 # 最终抽取后的采样率: 50 MHz
print(f"1. 设定参数: 信号都在 {f_center / 1e6} MHz, ADC采样率 {fs_adc / 1e6} MHz")
# ================= 1. 制造模拟信号 (IF信号) =================
# 我们制造一个位于 75MHz 的噪声信号,这就是天线接收到的东西
np.random.seed(42)
white_noise = np.random.randn(len(t_analog))
# 使用带通滤波器把噪声捏成我们要的形状 (70MHz - 80MHz)
b_analog = firwin(101, [f_center - bw_signal / 2, f_center + bw_signal / 2],
fs=fs_analog, pass_zero=False)
analog_signal = lfilter(b_analog, 1, white_noise)
print(f"2. 模拟信号生成完毕。它是实数信号,所以在频谱上是左右对称的 (±75MHz)。")
# ================= 2. ADC 采样 (数字化) =================
# 模拟世界(1000M) -> 数字世界(100M),每10个点取1个
decim_factor_adc = int(fs_analog / fs_adc)
digital_signal = analog_signal[::decim_factor_adc]
t_digital = t_analog[::decim_factor_adc]
# 【原理解析】
# 信号原本在 75MHz。采样率是 100MHz。
# 根据采样定理,超过 fs/2 (50MHz) 的信号会发生“混叠”。
# 计算公式: |75 - 100| = 25 MHz。
# 所以,在数字世界里,这个信号看起来跑到了 25MHz 的位置!
print(f"3. ADC采样完毕。注意:因为采样率不够高,75MHz的信号'伪装'成了 25MHz (混叠现象)。")
# ================= 3. 下变频核心: 混频 (Mixing) =================
# 我们想要把信号搬到 0Hz。
# 既然现在的信号看起来在 25MHz (由75混叠而来),我们需要把它移回来吗?
# 书中策略:使用 -75MHz 的 NCO (数字本振)。
# 在 100MHz 采样率下,-75MHz 其实等同于 +25MHz (因为 -75 + 100 = 25)。
# 所以我们其实是用一个 25MHz 的本振去对准那个 25MHz 的信号。
# 生成本振信号 (复数信号)
nco = np.exp(-1j * 2 * np.pi * f_center * t_digital)
mixed_signal = digital_signal * nco
print(f"4. 混频完毕。我们将信号频谱整体移动了 -75MHz。")
print(f" 结果:原本在 75MHz 的分量 -> 搬到了 0Hz (我们想要的)。")
print(f" 原本在 -75MHz 的镜像分量 -> 搬到了 -150MHz (也就是 ±50MHz 处,这是废料)。")
# ================= 4. 核心: 低通滤波 (Filtering) =================
# 现在的信号里有两个东西:
# 1. 在 0Hz 的有用信号。
# 2. 在 50MHz (即-150MHz) 的无用高频废料。
# 我们用一个低通滤波器,只保留 ±5MHz 的内容。
num_taps = 61
lpf_cutoff = 8e6 # 截止频率设为 8MHz,稍微留点余量
b_lpf = firwin(num_taps, lpf_cutoff, fs=fs_adc)
filtered_signal = lfilter(b_lpf, 1, mixed_signal)
print(f"5. 滤波完毕。高频废料被切除,只剩下 0Hz 附近的纯净基带信号。")
# ================= 5. 核心: 抽取 (Decimation) =================
# 现在信号带宽很窄,不需要 100MHz 这么快的采样率了。
# 我们每隔一个点留一个 (100M -> 50M)
final_signal = filtered_signal[::2]
print(f"6. 抽取完毕。数据量减少一半,便于后续处理器处理。")
# ================= 可视化绘图函数 =================
def plot_and_save(signal, fs, step_num, title, filename_suffix, center_line=None):
plt.figure(figsize=(10, 4))
# 计算频谱
n = len(signal)
freqs = np.fft.fftshift(np.fft.fftfreq(n, d=1 / fs))
mag = np.fft.fftshift(np.abs(np.fft.fft(signal)))
mag = mag / np.max(mag) # 归一化,最高点定为1
# 转换为 MHz
freqs_mhz = freqs / 1e6
plt.plot(freqs_mhz, mag, color='tab:blue', linewidth=1.5)
plt.title(f"步骤 {step_num}: {title}", fontsize=14)
plt.xlabel("频率 (MHz)", fontsize=12)
plt.ylabel("幅度 (归一化)", fontsize=12)
plt.grid(True, alpha=0.3)
# 画出 0Hz 中心线
plt.axvline(0, color='black', linestyle='--', alpha=0.5)
# 如果有特定频率需要标记
if center_line:
plt.axvline(center_line, color='red', linestyle=':', label=f'关注频率 {center_line}MHz')
plt.legend()
plt.tight_layout()
# 保存图片
filename = f"{SAVE_PREFIX}{step_num}_{filename_suffix}.png"
plt.savefig(filename, dpi=150)
print(f" [图片已保存]: {filename}")
# plt.show() # 如果你在本地运行,可以取消注释这一行
# ================= 执行绘图 =================
# 图1:模拟信号
plot_and_save(analog_signal, fs_analog, 1,
"模拟 IF 信号 (真实存在的实数信号,双边对称)", "Analog", center_line=75)
# 图2:ADC采样后
# 重点:画图范围限制在 -50 到 50,因为采样率只有 100
plot_and_save(digital_signal, fs_adc, 2,
"ADC采样后 (注意:75MHz混叠到了-25MHz位置)", "Sampled", center_line=-25)
# 图3:混频后
plot_and_save(mixed_signal, fs_adc, 3,
"数字混频后 (目标信号被搬运到了 0Hz)", "Mixed", center_line=0)
# 图4:滤波后
plot_and_save(filtered_signal, fs_adc, 4,
"低通滤波后 (杂波消失,波形变光滑)", "Filtered", center_line=0)
# 图5:抽取后
# 采样率变了,X轴会自动调整
plot_and_save(final_signal, fs_final, 5,
"二抽一后 (最终结果,数据量减半)", "Decimated", center_line=0)
print("\n🎉 全部完成!请查看当前目录下的 PNG 图片文件。")
运行结果
1. 设定参数: 信号都在 75.0 MHz, ADC采样率 100.0 MHz
2. 模拟信号生成完毕。它是实数信号,所以在频谱上是左右对称的 (±75MHz)。
3. ADC采样完毕。注意:因为采样率不够高,75MHz的信号'伪装'成了 25MHz (混叠现象)。
4. 混频完毕。我们将信号频谱整体移动了 -75MHz。
结果:原本在 75MHz 的分量 -> 搬到了 0Hz (我们想要的)。
原本在 -75MHz 的镜像分量 -> 搬到了 -150MHz (也就是 ±50MHz 处,这是废料)。
5. 滤波完毕。高频废料被切除,只剩下 0Hz 附近的纯净基带信号。
6. 抽取完毕。数据量减少一半,便于后续处理器处理。
[图片已保存]: DDC_Step_1_Analog.png
[图片已保存]: DDC_Step_2_Sampled.png
[图片已保存]: DDC_Step_3_Mixed.png
[图片已保存]: DDC_Step_4_Filtered.png
[图片已保存]: DDC_Step_5_Decimated.png






87

被折叠的 条评论
为什么被折叠?



