用 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 kHzquad_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_txtau = 75e-6 对应 75 µs 预加重(美规)。若面向欧规设备,应改为 50e-6fh = -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.wavrepeat=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)。

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


5. 总结与踩坑清单

核心结论

  1. FM 广播调制 = 相位累加的数字 FM;quad_rate 必须是 audio_rate5 倍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=Falsevga=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 广播发射授权要求)
相关推荐
我星期八休息2 小时前
网络编程—NAT、代理服务与内网穿透
linux·服务器·网络·网络协议
袅沫3 小时前
Nginx中部署多个前端项目——区分路径
服务器·前端
fei_sun3 小时前
UVM中的TLM1.0通信
运维·服务器·uvm
MartinYeung53 小时前
[论文学习]MCPTox:面向真实世界MCP服务器的工具投毒攻击基准测试
运维·服务器·学习
Escalating_xu4 小时前
【Linux】基础 I/O 深度解析:FILE、文件描述符、open/read/write、重定向与缓冲区
java·linux·服务器
城管不管4 小时前
MySQL 慢查询完整排查
java·服务器·jvm·数据库·mysql·spring·面试
进击的码力4 小时前
win11 使用ssh 遇到权限too open 无法登录服务器的问题
运维·服务器·ssh
烛之武5 小时前
Linux 命令速查表
linux·运维·服务器
进击的荆棘5 小时前
Linux——基础开发工具(下)
linux·运维·服务器·linux工具