用 HackRF + GNU Radio 制作 FM 发射机:从 WAV 到 103 MHz 空中信号

用 HackRF + GNU Radio 制作 FM 发射机:从 WAV 到 103 MHz 空中信号

作者:charlie

关键词:HackRF、GNU Radio 3.10、SoapySDR、analog_wfm_tx、FM 广播调制、采样率链

摘要

本文记录如何用一块 HackRF One/Pro 配合 GNU Radio,把一个普通 WAV 音频文件调制成标准 FM 广播信号并发射到空中(默认 103 MHz),用普通收音机即可接收。文章给出完整可运行的流图 fm_tx.grc,并重点讲清三件事:(1) FM 调制与卡森带宽准则,为什么 max_dev=75 kHz、quad_rate 必须是 audio_rate 的整数倍;(2) GNU Radio analog_wfm_tx 的内部插值(×5)与预加重(tau=75 µs);(3) SoapySDR 下 HackRF 在 1 MHz 采样率发射会触发 TIMEOUT bug ,因此必须用 2 MHz 采样率,并用 rational_resampler 的 25/3 把 240 kHz 提到 2 MHz。文末附合规声明:本方案仅用于低功率近场教学测试。


1. 背景

FM 广播频段为 88--108 MHz,单声道基带音频带宽约 15 kHz,峰值频偏 75 kHz。HackRF 覆盖 1 MHz--6 GHz、半双工、8 位量化,既能接收也能发射,是做"软件定义 FM 发射机"的低成本平台。

目标链路非常短:

复制代码
WAV 文件 (48k, 单声道)
   │
   ▼  analog_wfm_tx  (FM 调制, 插值 ×5)
240 kHz 复数基带
   │
   ▼  rational_resampler_ccc (25/3)
2 MHz 复数基带
   │
   ▼  soapy_hackrf_sink (TX, fc = freq×1e6, vga=16)
空中 103 MHz FM 信号
   │
   └─► qtgui_sink_x  (本地频谱自检)

2. 理论基础

2.1 FM 调制与卡森带宽准则

在频率调制中,载波的瞬时频率正比于消息信号的幅度:

复制代码
s(t) = A·cos( 2π·fc·t + 2π·kf·∫ m(τ)dτ )
  • kf 决定频偏灵敏度;实际工程用峰值频偏 Δf 描述:max_dev = 75000 Hz。

  • 根据 Carson 带宽准则,已调信号 98% 功率落在:

    BW ≈ 2·(Δf + f_m,max)

FM 广播 Δf = 75 kHz,基带最高 f_m,max ≈ 15 kHz,故 BW ≈ 2×(75+15) = 180 kHz。这就是 quad_rate = 240 kHz(留有余量,且为 audio_rate 的 5 倍)的来源。

2.2 预加重 / 去加重(tau = 75 µs)

高频噪声在 FM 解调后会被提升,因此发射端对高频做预加重 、接收端做去加重,以换得信噪比改善。时间常数分两派:

地区 tau
美国 / 日本 / 韩国等 75 µs
欧洲 / 中国等 50 µs

analog_wfm_tx 的 tau = 75e-6 对应 75 µs 预加重(美规)。若面向欧规设备,应改为 50e-6。fh = -1.0 表示关闭发射端的高通滤波器(不额外滤直流)。

2.3 GNU Radio analog_wfm_tx 原理

analog_wfm_tx 内部做四件事:

  1. 重采样 :内置插值因子固定为 5 ,所以要求 quad_rate = 5 × audio_rate(本例 48k → 240k)。它把音频上采样后再调制,保证每个音频样本有足够多的复数采样刻画频率变化。
  2. 预加重 :按 tau 对音频做一阶高通式加权。
  3. 相位累加 FM 调制 :对每个输出采样累加相位增量 Δφ = 2π·(kf·m[n])/fs,再取 cos/sin 得到复数基带 I + jQ。这正是"间接调频"------用积分+正交本振实现,避免直接调 VCO。
  4. 限幅/缩放:输出幅度归一化到适合 DAC 的范围。

2.4 采样率链与 HackRF 限制(关键坑)

HackRF 基带采样率规格为 2--20 MS/s 。1 MHz 在规格外,且 SoapySDR 的 HackRF TX 在 1 MHz 会触发 TIMEOUT bug (底层 hackrf 库在过低的采样率下 TX 超时,流图卡死/丢数)。因此:

  • wfm_tx 自然产出 240 kHz;
  • 用 rational_resampler_ccc(interp=25, decim=3)把它提到 240k × 25/3 = 2,000,000 = 2 MHz,正好落在 HackRF 合法区间下沿;
  • HackRF samp_rate 设为 2 MHz,center_freq = freq × 1e6。

若强行把 samp_rate 设 1 MHz,流图能"跑起来"但发射会间歇卡死且频谱异常------这是 Soapy/HackRF 的已知问题,不是你流图写错。

2.5 法律与责任(务必阅读)

在 88--108 MHz 发射信号,绝大多数国家/地区都需要电台执照;未经许可的 FM 广播发射可能违法并干扰合法广播。本文设置:

  • amp = False(关闭 HackRF 板载放大器);
  • vga = 16(适中增益,低功率);
  • 配合短天线、近场测试(发射机旁放一台收音机验证)。

请仅在合法授权、低功率、封闭/近场场景使用本方案,并遵守当地无线电法规。作者与本文不对任何违规使用负责。


3. 实践:流图搭建

3.1 音频准备

analog_wfm_tx 要求单声道且采样率与 audio_rate 一致。用 ffmpeg 把任意音频转成 48 kHz 单声道 WAV:

bash 复制代码
ffmpeg -i in.wav -ar 48000 -ac 1 fm_input.wav

流图中 blocks_wavfile_source 指向 /path/to/fm_input.wav,repeat=True(循环播放,方便反复测试)。

3.2 流图关键参数

变量 / 模块 值 说明
freq 88--108 MHz 滑杆,默认 103 发射中心频率
samp_rate 2,000,000 HackRF TX 采样率(≥2M,避开 1M bug)
audio_rate 48,000 必须与 WAV 一致
quad_rate 240,000 = 5 × audio_rate
analog_wfm_tx.max_dev 75,000 峰值频偏 75 kHz(FM 广播标准)
analog_wfm_tx.tau 75e-6 75 µs 预加重
rational_resampler interp=25, decim=3 240k → 2M
soapy_hackrf_sink.vga 16 低功率增益
soapy_hackrf_sink.amp False 关闭板载放大

3.3 关键模块源码(来自 fm_tx.grc)

WAV 源(循环播放):

yaml 复制代码
- name: wav_source_0
  id: blocks_wavfile_source
  parameters:
    file: /path/to/fm_input.wav
    nchan: '1'
    repeat: 'True'

FM 调制核心:

yaml 复制代码
- name: analog_wfm_tx_0
  id: analog_wfm_tx
  parameters:
    audio_rate: audio_rate      # 48000
    quad_rate: quad_rate        # 240000  (= 5 × audio_rate)
    max_dev: '75000'            # 峰值频偏 75 kHz
    tau: 75e-6                  # 75 µs 预加重
    fh: '-1.0'                  # 关闭发射高通

采样率提升到 2 MHz(绕过 1M TX bug):

yaml 复制代码
- name: rational_resampler_ccc_0
  id: rational_resampler_xxx
  parameters:
    type: ccc
    interp: '25'                # 240000 × 25 / 3 = 2,000,000
    decim: '3'
    taps: '[]'

HackRF 发射与本地频谱自检:

yaml 复制代码
- name: soapy_hackrf_sink_0
  id: soapy_hackrf_sink
  parameters:
    type: fc32
    center_freq: freq*1e6       # 103 MHz
    samp_rate: samp_rate        # 2,000,000
    vga: '16'
    amp: 'False'                # 低功率,合规近场测试
    bandwidth: '0'

- name: qtgui_sink_x_0
  id: qtgui_sink_x
  parameters:
    type: complex
    fc: freq*1e6
    bw: samp_rate
    fftsize: '1024'
    plotfreq: 'True'
    plotwaterfall: 'True'

连接关系(等价于生成的 fm_tx.py):

python 复制代码
# 信号流:WAV → FM调制 → 重采样(→2M) → HackRF TX,并旁路一份给频谱仪
self.connect((self.wav_source_0, 0), (self.analog_wfm_tx_0, 0))
self.connect((self.analog_wfm_tx_0, 0), (self.rational_resampler_ccc_0, 0))
self.connect((self.rational_resampler_ccc_0, 0), (self.soapy_hackrf_sink_0, 0))
self.connect((self.rational_resampler_ccc_0, 0), (self.qtgui_sink_x_0, 0))

4. 运行与自检

启动(GRC 或 grcc 生成脚本):

bash 复制代码
grcc -o /tmp fm_tx.grc && python3 /tmp/fm_tx.py
# 或直接用 GRC 的 "Run"

自检验证两步走:

  1. 本地频谱自检 :qtgui_sink_x 应显示以 103 MHz 为中心、宽约 180--200 kHz 的 FM 谱(典型"双驼峰" Carrier + 对称边带)。若谱很窄或卡死,多半是踩了 1M bug------确认 samp_rate=2M。
  2. 真实接收 :把一台 FM 收音机调到 103.0 MHz,靠近 HackRF 天线,应清晰听到 WAV 里的声音(循环播放便于确认)。听感发闷/高频不足,检查 tau 与接收机去加重是否匹配(美规 75 µs 对 75 µs)。

小贴士:freq 用 counter_slider 可在运行中平滑改频;改到 88--108 内任意值,收音机同步调台即可验证。


5. 总结与踩坑清单

核心结论

  1. FM 广播调制 = 相位累加的数字 FM;quad_rate 必须是 audio_rate 的 5 倍 (analog_wfm_tx 内置插值 ×5)。
  2. 峰值频偏 75 kHz + 卡森准则 ⇒ 占用带宽约 180 kHz,决定了 quad_rate=240k 的选取。
  3. HackRF TX 不能用 1 MHz 采样率 (Soapy TIMEOUT bug),必须 ≥2 MHz;用 rational_resampler 25/3 把 240k 升到 2M 是标准解法。
  4. 预加重 tau=75 µs 对应美规;面向欧规设备改 50 µs。

踩坑清单

  • 收音机收到但声音发尖/失真 → max_dev 与收音机期望不一致,或音频本身超出 15 kHz;确认 max_dev=75k。
  • 收音机高频发闷 → tau 不匹配(发射 75 µs 配欧规 50 µs 收音机会有差异)。
  • 流图卡死 / 频谱断续 → 几乎都是 samp_rate=1M 触发 Soapy/HackRF TX TIMEOUT;改成 2M。
  • 完全收不到 → 先确认 amp=False 时增益足够近场;天线用 88--108 MHz 鞭状天线;收音机与 HackRF 距离过远(低功率下十几厘米~一米内最佳)。
  • 音频杂音大 → WAV 不是 48k 单声道;用 ffmpeg 重转,并保证 audio_rate 与文件一致。

适用与局限

  • 适用:FM 广播调制教学、近场音频链路演示、SDR 发射链路验证。
  • 局限:HackRF 8 位 DAC,音质远不及专业 FM 激励器;半双工,不能收发同时;发射须合规、低功率、近场;立体声(MPX/Pilot 19 kHz)未实现,本文为单声道 FM。

合规声明(重申)

在 FM 广播频段发射通常需无线电执照。本方案默认 amp=False、vga=16、循环近场测试,仅供教学与合法授权场景。使用者须自行确保符合当地法规,作者对任何滥用后果不承担责任。

参考

  • GNU Radio analog.wfm_tx 文档(插值因子、预加重、频偏参数)
  • Carson, J. R. --- "Notes on the Theory of Modulation" (1922),卡森带宽准则
  • HackRF One 规格(采样率 2--20 MS/s)、SoapySDR HackRF 模块
  • ITU / 当地无线电管理法规(FM 广播发射授权要求)
相关推荐
土星云SaturnCloud6 小时前
土星云智慧服务区方案:AI视觉+边缘计算+视频存管一体化的全场景智能化落地解析
服务器·人工智能·ai·边缘计算
liqingdi4378 小时前
[SWPUCTF 2023 秋季新生赛]Easy SMC
服务器·前端·javascript
自养Agent8 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
派小心.9 小时前
网站统计采样数据的误差核对
大数据·服务器·前端·数据库·人工智能·小程序·数据分析
A心有千千结10 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
fengkai454510 小时前
十三、Zabbix 监控
运维·服务器·zabbix·监控·网络设备
数字新视界10 小时前
机房动环监控助力智能管理
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
算力小木10 小时前
#标题:5090 vs 4090D 到底差多少?我拿八卡实测了一周
服务器
数据中心的那点事儿10 小时前
艾特网能:GPU算力服务器冷却系统配套设计介绍
运维·服务器·gpu算力
广州灵眸科技有限公司10 小时前
单芯片同时跑 Linux 与 RT-Thread:RV1126B的AMP异构方案完整拆解
linux·运维·服务器