
简介面向通信工程与信号处理学习者的单边带调制代码包使用MATLAB实现完整覆盖下边带与上边带两种模式包含信号生成、预滤波、混频、边带选频以及接收端解调恢复等核心环节。压缩包体积仅4KB共8个文件其中4个脚本分别对应SSB、AM、FM、PM四种调制方式可直接运行观察输出波形4个文本文件用于讲解调制原理、滤波设计思路和关键参数方便配合代码阅读。通过分析源码可深入理解单边带调制为何能比常规调幅节省一半带宽以及如何利用低通或高通滤波器得到下边带或上边带信号。文档还给出常见滤波器参数与边带选择方法便于课程设计或实验报告中作对比分析。目前已有69人学习资源小巧轻量适合通信原理课程设计、调制备考或MATLAB信号处理入门时作为实践参考。1. SSB 调制不只是省一半带宽LSB 和 USB 的取舍才是关键拿到SSB.rar_LSB_modulation_ssb_usb这样的命名第一反应是解压看代码但真正值钱的不是压缩包里的几行脚本而是它背后那套完整的单边带调制链路。SSB 的工程魅力在于用 Hilbert 变换构造正交支路再通过符号组合把上边带或下边带从频谱上抹掉整条信号链从数学到代码都不复杂但参数一旦设错出来的频谱会同时带着镜像和载波泄漏怎么看怎么不像 SSB。这篇博文从频谱结构开始讲直接落到 Python 实现、Hilbert 变换参数选择、GNU Radio 链路搭建最后聊几个让镜像抑制比超过 40dB 的工程细节。LSB 和 USB 的调制与解调只是相差一个符号但对噪声、频偏和接收机结构的影响完全不同这部分我会单独拉出来说清楚。适合正在做软件无线电、短波解调或者数字信号处理课程实验的开发者拿着这份材料能在半小时内跑通完整链路。2. SSB 的原理与 LSB/USB 的频率结构从 DSB 到 Hilbert 变换2.1 DSB 浪费了一半功率SSB 把频谱利用率翻倍调幅AM和双边带调制DSB都是把基带信号直接乘到载波上。设基带信号为 m(t)载波为 cos(2πf_c t)DSB 的输出是 m(t)·cos(2πf_c t)频谱上出现上下两个镜像边带每个边带都携带完整的幅度和相位信息。问题在于两个边带信息完全重复发射机把功率平分给两个冗余的边带信道带宽也白白占用两倍。SSB 的核心思路就是只保留其中一个边带——留下上边带叫 USBUpper Side Band保留下边带叫 LSBLower Side Band另一个边带连同载波一起滤掉或抵消掉。带宽直接减半功率全部注入有效边带这也是短波通信和业余无线电里 SSB 成为语音主力调制方式的原因。LSB 和 USB 在频谱上是镜像对称的单看幅度谱看不出区别只有结合载波频率和信号的相对位置才能判断信号位于载频之上是 USB位于载频之下是 LSB。这个判断规则在后续解调和频偏分析里非常重要很多工程问题都出在收发两端选错了边带。2.2 解析信号与希尔伯特变换移相法的数学基础要从实信号里只保留一个边带最直接的方法是构造一个只在单侧频域有能量的复信号这就是解析信号。对实信号 m(t)其解析信号定义为z(t) m(t) j·m̂(t)其中 m̂(t) 是 m(t) 的 Hilbert 变换。Hilbert 变换的本质是一个全通滤波器幅度响应恒为 1相位在正频率上滞后 90 度在负频率上超前 90 度。经过这样的变换解析信号的频谱只在正频域有分量负频域被完全抵消。关键操作来了USB 输出y(t) m(t)·cos(2πf_c t) - m̂(t)·sin(2πf_c t)LSB 输出y(t) m(t)·cos(2πf_c t) m̂(t)·sin(2πf_c t)从频域看USB 是解析信号移到载频后取实部LSB 是共轭解析信号移到载频后取实部。相差一个符号数学上一目了然。这比直接拿滤波器切边带更优雅因为不需要设计锐截止的边带滤波器尤其当基带信号频率很低时边带间距极小滤波器法几乎不可用。2.3 三种工程实现路线对比实现方法核心机制优势劣势适用场景滤波法先 DSB 调制再用带通滤波器切掉一个边带实现简单不涉及 Hilbert 变换过渡带要求极窄基带在低频时难以设计基带信号频率较高、带宽较大的场景移相法HartleyHilbert 变换移相 90 度加减法抵消一个边带边带抑制不受基带频率限制适合任意低频分量需要精确的 90 度移相器I/Q 幅度不平衡会影响抑制比语音、低速数据软件无线电中最常用Weaver 法两次混频通过零频滤波实现单边带避免宽频移相网络需要两个本振结构更复杂宽带 SSB 调制高精度仪器仪表软件无线电里我基本只推荐移相法因为数字 Hilbert 变换器用 FIR 滤波器就能做到精确 90 度移相幅度响应可以拉得很平比模拟移相器稳定得多。下面章节的实现全部基于移相法。3. 用 Python 实现 USB 与 LSB 调制Hilbert 变换的参数决定镜像抑制性能3.1 生成测试基带信号和载波实验信号我用多音信号而不是单一正弦波因为单音信号看不出边带内的频率结构也不便于观察滤波效果。下面的代码生成两个频率分量的基带信号import numpy as np from scipy.signal import hilbert, firwin, lfilter import matplotlib.pyplot as plt fs 12000 # 采样率 12kHz t np.arange(0, 1, 1/fs) # 1秒 # 基带信号由 300Hz 和 1000Hz 两个正弦叠加 m_t 0.8 * np.cos(2*np.pi*300*t) 0.5 * np.cos(2*np.pi*1000*t) # 载波频率设为 3000Hz采样率至少 4 倍载频以上 fc 3000 carrier np.cos(2*np.pi*fc*t)采样率和载波频率的选择不是随意的。12kHz 采样率下 3kHz 载波对应载波位于奈奎斯特频率的四分之一既能容纳上边带3kHz~4kHz的完整频域又能给 Hilbert 滤波器留出足够的设计余量。载波频率过高会把边带顶到采样率上限附近低通滤波器的阶数会变得非常大。3.2 移相法实现 SSB 调制的核心代码这是整个 SSB 链路最关键的一段希尔伯特变换的对象、符号组合的方向决定了输出是 LSB 还是 USB# 希尔伯特变换得到 90 度相移后的正交信号 m_hat np.imag(hilbert(m_t)) # 载波正交分量 carrier_quad np.sin(2*np.pi*fc*t) # USB 同相分量 * cos - 正交分量 * sin usb_signal m_t * carrier - m_hat * carrier_quad # LSB 同相分量 * cos 正交分量 * sin lsb_signal m_t * carrier m_hat * carrier_quad逻辑说明hilbert函数返回解析信号实数部分是原信号本身虚数部分是原信号的 Hilbert 变换。USB 的构造是让解析信号 z(t) 与复载波 exp(j2πf_c t) 相乘再取实部LSB 则是解析信号与共轭载波 exp(-j2πf_c t) 相乘再取实部。公式里 sin 前面的符号差就是决定取哪边边带的开关。参数说明hilbert在 scipy 里使用 FFT 实现输出与信号等长无群延迟。但它默认将整个信号当作周期信号处理非周期信号会在首尾产生边界效应。实际工程中我会把信号分段加窗处理或者预先用hann窗淡入淡出首尾 10ms否则调制后频谱两端会出现异常分量。3.3 验证频谱确认只有一条边带调制完成后必须用频谱验证结果这一步能直接发现符号错误、载波泄漏和镜像抑制不足的问题def plot_spectrum(signal, name): nfft 4096 spec np.fft.fftshift(np.fft.fft(signal, nfft)) freq np.linspace(-fs/2, fs/2, nfft) plt.plot(freq, 20*np.log10(np.abs(spec)/np.max(np.abs(spec)) 1e-12)) plt.title(name) plt.xlabel(频率 (Hz)) plt.ylabel(归一化幅度 (dB)) plt.ylim(-80, 5) plt.show() plot_spectrum(m_t, 基带信号) plot_spectrum(usb_signal, USB 调制信号) plot_spectrum(lsb_signal, LSB 调制信号)观察点有三个。第一USB 信号频谱在 3kHz 附近只有一个峰带3kHz 以下应该比主峰低至少 40dB如果低不下去说明 Hilbert 变换的幅度不平衡。第二3kHz 位置如果有明显谱线说明存在载波泄漏原因是 m_t 均值没有归零。第三LSB 的频谱构型和 USB 是镜像关系用np.fft.fftshift后能明显看到边带在载频左侧。我见过最多的错误是直接把hilbert(m_t)的虚部丢掉用实部做正交支路那样得到的不是移相 90 度而是零度边带根本没抵消。检查方法很简单把m_hat画出来看它和m_t是否具有相同幅度但相位偏移约 90 度从零交叉点可以直观判断。3.4 滤波法实现的对比代码滤波法在工程中常用于基带频率较高的场景配合移相法做对比也能验证 SSB 频谱结构。先做 DSB 调制再用 FIR 带通滤波器把不需要的边带滤除# DSB 调制 dsb m_t * carrier # 设计带通滤波器USB 通带 3000~5000Hz过渡带 500Hz # 滤波器阶数越高过渡带越窄但计算量和延迟都变大 taps firwin(257, [3500, 4500], pass_zeroFalse, fsfs) # 滤波得到 USB 信号注意滤波会引入群延迟约 128 个采样点 usb_filtered lfilter(taps, 1, dsb)参数说明firwin的第一个参数 257 是滤波器阶数对应群延迟约 128 个采样点若后续做同步解调需要补偿这个延迟pass_zeroFalse表示带通滤波器[3500, 4500]是通带频率也就是只让载波上方的 3.5kHz~4.5kHz 通过。滤波法的优势在于直观过渡带宽度和边带间隔直接由滤波器参数约束但对低频基带信号比如 100Hz 以下的语音成分边带距载波太近滤波器阶数需要数千阶才能把镜像压到 40dB 以下实际工程中不推荐。4. 从离线脚本到实时链路用 GNU Radio 搭 SSB 收发流程4.1 采样率和滤波器参数的配合离线 Python 脚本再快也只是验证算法要处理真实无线电信号需要完整的 SDR 链路。以 HackRF 和 GNU Radio 为例先把参数定下来基带采样率SDR 内部 I/Q 采样率通常设为 2MHz目标信号带宽语音 SSB 带宽约 2.4kHz基带滤波配合抽取器把 2MHz 信号抽取到 48kHz这里的核心思路是在高采样率下完成射频接收然后逐级抽取到基带。如果直接把 2MHz 信号送到 Hilbert 滤波器计算量太大实时性无法保证。我通常的做法是先用一个带宽 20kHz 的低通滤波器完成信道选择再抽取到 48kHz 做 SSB 解调。GNU Radio 的流程图如下RTL-SDR/HackRF 源 → 低通滤波器 → 抽取 → 复数移频把载波搬到零频→ 希尔伯特变换 → 取实部 → 低通 → 音频输出。4.2 GNU Radio 关键块参数接收链路里最重要的块依次是Rational Resampler设置抽取率 40 左右将 2MHz 降到 50kHzFrequency Xlating FIR Filter同时完成混频、低通、抽取三件事参数设置为中心频率设为载波偏移量低通截止频率设为 5kHz过渡带 2kHzHilbert块GNU Radio 中使用hilbert_fc输入是浮点信号输出为复数实部和虚部构成解析信号Complex To Real取解析信号实部得到解调后的音频# 这里给出对应的 GNU Radio Companion 参数表等效 Python 实现 from gnuradio.filter import firdes # Frequency Xlating FIR Filter 参数 samp_rate 2_000_000 # 目标信号中心频率偏移接收 USB 信号时本振设低 3000Hz center_freq -3000 taps firdes.low_pass(1, samp_rate, 8000, 2000, firdes.WIN_HAMMING) # 抽取率 decim 40参数说明center_freq为负值表示把频谱向负方向搬移3kHz 的信号才会被移到零频firdes.low_pass的第三个参数是截止频率 8kHz第四个参数是过渡带 2kHz窗口选择 Hamming 平衡了阻带衰减和过渡带宽。抽取率 40 后输出采样率刚好 50kHz希尔伯特滤波器的计算量在 PC 上毫无压力。4.3 发射端 I/Q 平衡和镜像抑制发射端同样用 GNU Radio 搭建时最头疼的不是 SSB 调制本身而是 I/Q 不平衡。HackRF 的发射链路中I 路和 Q 路的幅度不一致、相位不严格正交会在镜像频率位置产生一个虚假边带。测量方法发送一个点频单音 SSB 信号在频谱仪上观察镜像频率的功率。正常情况下镜像应该比主信号低至少 30dB如果达不到需要在 GNU Radio 里加两个可调增益和相位补偿块分别调节 I 路增益、Q 路增益以及 I/Q 相位偏差。这个校准过程虽然繁琐但直接决定了最终信号质量。# 发射端 I/Q 补偿伪代码实测调参用 # i_gain 和 q_gain 手动调整到镜像最小 # phase_corr 用于补偿 I/Q 相位非正交 iq_corrected_real i_signal * i_gain - q_signal * np.sin(phase_corr) iq_corrected_imag q_signal * q_gain5. 解调方向判断怎么确认收到的是 USB 还是 LSB5.1 同步解调与希尔伯特解调的实现差异SSB 信号不含载波直接用包络检波会得到严重失真的信号。标准的解调方法是同步解调接收端生成与发射端同频同相的载波乘以接收信号后再低通滤波。但实际中频率不可能完全同步残余频偏会让解调后的信号频谱整体偏移语音会变成「鸭叫」数据信号会完全错乱。希尔伯特解调是更鲁棒的方案在软件接收机里更常用def demod_ssb(rx_signal): # 解析信号实部为原信号虚部为希尔伯特变换 analytic hilbert(rx_signal) # 把解析信号搬到零频-fc 对应解调 LSBfc 对应解调 USB # 这里假设接收信号中心频率在 fc且已知是 USB fc 3000 t np.arange(len(rx_signal)) / fs baseband_complex analytic * np.exp(-2j*np.pi*fc*t) # 取实部得到基带信号 return np.real(baseband_complex)关键点是np.exp(-2j*np.pi*fc*t)前面的符号。解析信号只保留正频域分量乘以负频率复指数是把频谱向左搬移。USB 信号在载频上方搬移后正频域分量正好落在零频解调成功如果对 LSB 信号也这么做频谱会被搬到负频域取实部后得到的是频率翻转的基带信号。5.2 上下边带对频偏的敏感性差异LSB 和 USB 的频域结构互为镜像这导致它们在频率偏移时表现出相反的行为。接收 USB 信号时本振偏低一个 Δf解调后基带频率整体偏高 Δf接收 LSB 信号时同样偏低 Δf解调后基带频率反而偏低 Δf。对于语音通信USB 频偏表现为音调变尖LSB 频偏表现为音调变钝。用 SDR 接收短波信号时经常看到频率表上同一个电台用 LSB 和 USB 都能听到但一个正常一个尖锐就是因为这个镜像关系。实际项目里如果不知道电台用哪个边带我的判断策略是先听声音再配合频谱图看信号集中的位置。在瀑布图频谱随时间变化图上能量集中在载频之上就是 USB反之是 LSB。自动化判断可以计算信号频谱的质心质心频率高于载频即为 USB 信号。5.3 实测同时发射 USB 和 LSB 对比解调输出为了直观验证解调方向和边带的关系我常做这样一个实验用一个信号源同时发射 USB 和 LSB 信号接收端分别用发端代码里的usb_signal和lsb_signal做解调。结果如下表发射边带解调方式输出结果USB正频率搬移乘 e^{-j2πfc t}正常还原USB负频率搬移乘 e^{j2πfc t}频谱翻转不可懂LSB正频率搬移频谱翻转不可懂LSB负频率搬移正常还原频谱翻转的本质是把高频分量映射到低频低频映射到高频。说话声还能勉强辨认但音乐和数据完全不可用。这一节的结论很直接解调前先确认边带方向比一切参数优化都重要。6. 工程落地让镜像抑制比稳定超过 40dB 的三个细节第一Hilbert 变换器一定要做群延迟补偿。FFT 实现的 Hilbert 变换虽然有固定群延迟但分段处理后会在边界引入跳变我通常把输入信号前后各延长 256 个采样点Hilbert 变换后截掉这 256 个点两边对齐再参与乘法。实测对比中不做补偿时镜像抑制比只有 28~32dB补偿后可以稳定维持在 40dB 以上。第二基带信号直流偏置要清零。移相法公式里如果 m(t) 含有直流分量相当于发射一个残余载波SSB 解调端会有一个固定频率的干扰音。处理方法是在调制前减去信号均值或者用高通滤波器把 20Hz 以下的成分滤除。这个排查起来很隐蔽因为人耳对高频干扰更敏感低频干扰容易被忽略。第三实测验证顺序固定为先发单音校频偏发送端发出 1000Hz 单音接收端解调后看频率是否正确再做镜像校准观察频谱仪上镜像分量是否小于 -40dB最后才接入真实语音或数据。三步缺一不可顺序也不变——频偏没校准时做的镜像校准是无效的因为两个镜像在错误频率下无法准确测量。Hilbert 变换符号和载波正交分量符号写反、基带信号带直流、分段信号未做边界处理这三个坑占了 SSB 实现问题的大多数调试时照着这个清单排查比从头读代码更快。本文还有配套的精品资源点击获取