解码空中电波的“达芬奇密码”:ggmorse 与 CW Skimmer 摩尔斯电码机器识别深度剖析
引言:当百年无线电撞上现代计算
在现代通信技术已迈入 5G/6G 与低轨卫星互联网时代的今天,拥有近两百年历史的**摩尔斯电码(Morse Code / CW,Continuous Wave)**依然在业余无线电(HAM Radio)、应急救灾通信、航空海事底线保障以及特定专业通信等领域占据着不可替代的独特地位。
CW 信号凭借极窄的信号带宽(通常仅数十至数百赫兹)和极高的功率频谱密度,拥有难以置信的抗弱信号与极端信道穿越能力。老练的无线电报务员能够从如同雷暴般杂乱的电离层背景噪声(QRN)、多径衰落(QSB)以及临频串音(QRM)中,凭借人耳的生理心理声学滤波分辨出微弱的“滴答(dit/dah)”声。
然而,对于计算机而言,CW 自动识别与解调却是一个极其棘手的混合信号处理与模式识别问题:
在开源界与无线电工程界,有两套极具代表性的技术路线和经典方案:
- CW Skimmer:业余无线电领域的工业级传奇,基于时域窄带滤波 + 自适应 AGC + 双门限 Schmitt 触发器 + 环形时钟队列估计;
- ggmorse:由顶尖开源极客 Georgi Gerganov(llama.cpp、whisper.cpp 之父)打造的现代解调库,基于短时傅里叶变换(STFFT)时频分析 + 宽带载频自适应盲捕获 + 统计特征提取。
本文将深入拆解这两大解调引擎的数学底座、核心算法与工程实现,剖析它们如何将空中漂浮的音频流精准还原为 ASCII 字符。
第一章 摩尔斯电码的时序基石与机器识别挑战
1.1 标准 PARIS 编码模型
摩尔斯电码以“基本时间单元(Time Unit,简称
1
T
1T
1T 或 Dot Length)”为基准构建时序:
- 点(Dit / Dot):1 个时间单元(
1
T
1T
1T); - 划(Dah / Dash):3 个时间单元(
3
T
3T
3T); - 符号内间隔(Intra-character Space):1 个时间单元(
1
T
1T
1T 的静音); - 字符间间隔(Inter-character Space):3 个时间单元(
3
T
3T
3T 的静音); - 词间间隔(Word Space):7 个时间单元(
7
T
7T
7T 的静音)。
国际标准以单词 PARIS (含末尾词间隔)作为码速计算的基准。发送一次完整标准的 PARIS 共需要 50 个时间单元:
PARIS
=
P
(
14
T
)
⏟
⋅
−
−
⋅
+
A
(
8
T
)
⏟
⋅
−
+
R
(
10
T
)
⏟
⋅
−
⋅
+
I
(
4
T
)
⏟
⋅
⋅
+
S
(
8
T
)
⏟
⋅
⋅
⋅
+
Word Space
(
6
T
)
⏟
补齐至
7
T
=
50
T
\\text{PARIS} = \\underbrace{P(14T)}_{\\cdot–\\cdot} + \\underbrace{A(8T)}_{\\cdot-} + \\underbrace{R(10T)}_{\\cdot-\\cdot} + \\underbrace{I(4T)}_{\\cdot\\cdot} + \\underbrace{S(8T)}_{\\cdot\\cdot\\cdot} + \\underbrace{\\text{Word Space}(6T)}_{\\text{补齐至 } 7T} = 50T
PARIS=⋅−−⋅
P(14T)+⋅−
A(8T)+⋅−⋅
R(10T)+⋅⋅
I(4T)+⋅⋅⋅
S(8T)+补齐至 7T
Word Space(6T)=50T
由此推导出发送速率
WPM
\\text{WPM}
WPM(Words Per Minute)与基本单元时序
T
dot
T_{\\text{dot}}
Tdot 的物理对应关系:
T
dot
=
60
秒
50
×
WPM
=
1200
WPM
(
ms
)
T_{\\text{dot}} = \\frac{60 \\text{ 秒}}{50 \\times \\text{WPM}} = \\frac{1200}{\\text{WPM}} \\quad (\\text{ms})
Tdot=50×WPM60 秒=WPM1200(ms)
例如:
-
20
WPM
20\\text{ WPM}
20 WPM 下,T
dot
=
60
ms
T_{\\text{dot}} = 60\\text{ ms}
Tdot=60 ms; -
40
WPM
40\\text{ WPM}
40 WPM 的高速发报下,T
dot
=
30
ms
T_{\\text{dot}} = 30\\text{ ms}
Tdot=30 ms; -
60
WPM
60\\text{ WPM}
60 WPM 的超高速军用自动机发报下,T
dot
=
20
ms
T_{\\text{dot}} = 20\\text{ ms}
Tdot=20 ms。
音频包络: ┌───┐ ┌───────┐ ┌───┐
波形振幅: │ 1T│ 1T │ 3T │ 3T │ 1T│
└───┘ └───────┘ └───┘
状态判决: [ Dot ] [ Dash ] [CharGap] [ Dot ]
时间轴 ─────────────────────────────────────────►
1.2 机器识别的三道物理难关
要将现实中的模拟音频信号解调为符合 PARIS 模型的离散字符,机器面临以下瓶颈:
1
:
2.3
1:2.3
1:2.3 到
1
:
3.8
1:3.8
1:3.8 之间飘动,要求时钟自愈算法具有极高的弹性容差。
第二章 CW Skimmer:经典的射频时域自适应判决艺术
2.1 CW Skimmer 的技术渊源
CW Skimmer 最初由 Alex Shovkoplyas(VE3NEA,Afreet Software)开发,是业余无线电比赛与 DX 远距离通联领域的“跨时代神器”。它配合 SDR 硬件,能在数十 kHz 甚至数百 kHz 的带宽内,同时对数十个电台的摩尔斯电码进行多通道并行自动解码。
其单信道判决核心经开源社区(如 JvanKatwijk 等)提取与重构,形成了一套纯粹、极轻量、高度确定性的时域状态机解码模型。
2.2 核心处理管道(Pipeline)
[原始 PCM 音频] (如 8kHz)
│
▼
[Biquad 二阶 IIR 带通滤波] ────── 滤除带外噪底,锁定中心载频 (如 700Hz, Q=4.0)
│
▼
[包络检波与 2ms 降采样] ────── 提取幅值绝对值,平滑为 500Hz 帧率包络
│
▼
[非对称快充慢放自适应 AGC] ─── 动态跟踪信号峰值,抗深衰落
│
▼
[Schmitt 双门限状态机] ──────── 消除边缘抖动,输出连续 Tone 与 Space 的持续时长
│
▼
[32 单元环形时钟队列] ──────── 统计排序,自适应估计基本 Dot Length(WPM 时钟恢复)
│
▼
[点划模式匹配与查表解码] ───── 判定字符与词空格,输出 ASCII 字符
2.3 关键算法原理解密
(1) 带通预处理与瞬态平衡
针对目标载频
f
0
f_0
f0 与采样率
f
s
f_s
fs,构建标准二阶双二阶(Biquad)带通滤波器:
ω
0
=
2
π
f
0
f
s
,
α
=
sin
(
ω
0
)
2
Q
\\omega_0 = 2\\pi \\frac{f_0}{f_s}, \\quad \\alpha = \\frac{\\sin(\\omega_0)}{2Q}
ω0=2πfsf0,α=2Qsin(ω0) 在工程上,
Q
Q
Q 因子通常选在
3.0
∼
5.0
3.0 \\sim 5.0
3.0∼5.0 之间(如
Q
=
4.0
Q=4.0
Q=4.0)。过高的
Q
Q
Q 值会导致窄带滤波器产生严重的瞬态振铃响应(Ringing Effect),破坏快速点划的起振沿与截断沿;过低则无法抑制邻频串扰。
(2) 非对称指数衰减 AGC(Peak Envelope Decay)
信号电平受电离层衰落影响剧烈,必须建立动态参考幅值。核心使用双时间常数衰减平均(Decaying Average):
peak
←
{
decayingAverage
(
peak
,
v
,
50.0
)
当
v
>
peak
(
快速充电
)
decayingAverage
(
peak
,
v
,
500.0
)
当
v
≤
peak
(
缓慢放电
)
\\text{peak} \\leftarrow \\begin{cases} \\text{decayingAverage}(\\text{peak}, v, 50.0) & \\text{当 } v > \\text{peak} \\quad (\\text{快速充电}) \\\\ \\text{decayingAverage}(\\text{peak}, v, 500.0) & \\text{当 } v \\le \\text{peak} \\quad (\\text{缓慢放电}) \\end{cases}
peak←{decayingAverage(peak,v,50.0)decayingAverage(peak,v,500.0)当 v>peak(快速充电)当 v≤peak(缓慢放电) 这种“快速跟峰、慢速保底”的机制,使得突发的大信号能立即拉高增益阈值,而在间隙静音期阈值缓慢下降,避免本底噪声被误判为信号。
(3) 施密特双门限触发器(Schmitt Trigger)
为防止音频在跳变沿因噪声产生“毛刺抖动”,CW Skimmer 采用三态双门限滞回状态机:
// 伪代码逻辑演示
switch (cwState) {
case ModeIdle:
if (value > 0.77f * agcPeak) { // 上门限开启
cwState = ModeTone;
cwStartTime = currentTime;
}
break;
case ModeTone:
if (value < 0.43f * agcPeak) { // 下门限截断
int duration = currentTime – cwStartTime;
if (duration >= 3) { // 过滤 <6ms 极小毛刺
cwState = ModeSpace;
add(ModeTone, duration);
}
}
break;
case ModeSpace:
if (value > 0.67f * agcPeak) { // 再次起振
int duration = currentTime – cwStartTime;
if (duration >= 4) {
cwState = ModeTone;
add(ModeSpace, duration);
}
}
break;
}
(4) 环形时钟估计:无锁相环的离散统计学
CW Skimmer 并不依赖复杂的锁相环(PLL),而是将最近测得的 Tone 与 Space 持续时长塞入一个 32 单元的循环队列。 当缓冲区积累了至少 14 个元素时,算法将队列中的正负时长抽取出来并进行排序:
SpaceGuess
\\text{SpaceGuess}
SpaceGuess 与最大空格的比例关系;
600
WPM
\\frac{600}{\\text{WPM}}
WPM600 帧;反算当前码速:
WPM
=
600
SpaceGuess
\\text{WPM} = \\frac{600}{\\text{SpaceGuess}}
WPM=SpaceGuess600
1.5
×
SpaceGuess
1.5 \\times \\text{SpaceGuess}
1.5×SpaceGuess 判定为划(-),否则判定为点(.);当空格超过
2
×
SpaceGuess
2 \\times \\text{SpaceGuess}
2×SpaceGuess 时判定字符切分,并进行字典树/散列查表。
第三章 ggmorse:Georgi Gerganov 的现代频域时空解调
3.1 极客之作:gg 系列的信号处理分支
开发出广为人知的 llama.cpp 和 whisper.cpp 的天才开发者 Georgi Gerganov,不仅在 LLM 领域声名卓著,更是音频通信与 DSP 的老牌专家(如知名超声波跨设备通信项目 ggwave)。
ggmorse 是他针对短波及声学麦克风信道研发的轻量级 C++ 莫尔斯解码库。与 CW Skimmer 依赖固定的中心频率带通不同,ggmorse 具备全自动载频盲捕获(Automatic Pitch Hunting)和全自适应动态门限解调能力。
ggmorse 核心架构全景
[麦克风/原生 PCM 流] ── (例如 8kHz/16kHz/48kHz, I16 或 F32)
│
▼
┌──────────────────────────────────────────────┐
│ 滑动短时傅里叶变换 (STFFT) 频谱计算引擎 │
│ – 汉明窗 / 汉恩窗加权 │
│ – 频域能量矩阵 (Spectrogram Matrix) │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 载频盲捕获与多峰跟踪 (Pitch Estimator) │
│ – 搜索频段: [200 Hz ~ 1200 Hz] │
│ – 动态跟踪 SNR 峰值频点 │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 时间-功率包络切片与自适应阈值提取 │
│ – 背景噪声能量统计与信噪比估算 │
│ – 基于时变功率谱的二值化判定 │
└──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 时序自相关 / 直方图时钟自愈 (5 ~ 55 WPM) │
│ – 动态恢复 Dit/Dah 门限 │
│ – 状态转移与字符流精准重组 (含采样点偏移) │
└──────────────────────────────────────────────┘
3.2 核心优势与算法机制
(1) STFFT 频域全景图:告别对频烦恼
传统方法需要操作员或者软件预先知道“电台发射的具体音调是多少 Hz”。如果对方频率偏移了 50 Hz,窄带带通滤波器就会产生剧烈的衰减。
ggmorse 直接在频域做文章:
- 内部持续维护一个滑动短时傅里叶变换帧矩阵;
- 在
200
Hz
∼
1200
Hz
200\\text{ Hz} \\sim 1200\\text{ Hz}
200 Hz∼1200 Hz 的典型 CW 音频窗口内计算各频带的能量谱密度; - 运用最大后验或谱峰检测,实时定位能量最集中且具备 CW 开关通断特性的载波 Bin。即使电台有温漂或报务员更换了副载频,ggmorse 也能在几个符号周期内自动“咬住”新频率。
(2) 精确的采样点偏移(Sample Offset)溯源
在 ggmorse.h 中,API 设计展现了对流式音频与多媒体同步的深厚考量:
struct RxDataWithOffset {
char ch; // 解调出的字符 (如 'A', '1')
uint64_t sampleIndex; // 对应的音频流绝对物理采样点索引!
};
这一设计使得解调出的文字可以与原始音频的波形毫秒级对齐,在诸如电报打字机 UI 联动、频谱瀑布图注记标记、录音剪辑定位等场景中发挥巨大价值。
(3) 现代 C++ 架构与零外部依赖
- 纯 C++ 编写,内部无庞大的第三方矩阵运算库依赖;
- 具备优雅的跨平台兼容性,在轻度配置下可无缝编译运行在 iOS/Android 移动端、树莓派嵌入式板卡乃至 WebAssembly 浏览器环境。
第四章 深度对比:两代解调引擎的技术天平
| 主导域 | 时域分析(Time-Domain Envelope) | 频域/时频联合(STFFT Spectrogram) |
| 计算复杂度 | 极低(
O ( N ) O(N) O(N) 级运算),几乎全为乘加与简单比较 |
中等(需 FFT 计算),依赖滑动频域窗与峰值扫描 |
| 载频自适应能力 | 较弱:依赖预设中心频率,超出带通范围需外部调度 | 极强:自动在
200 Hz ∼ 1.2 kHz 200\\text{ Hz} \\sim 1.2\\text{ kHz} 200 Hz∼1.2 kHz 内扫频捕获 |
| 码速适应区间 | 原生约
5 ∼ 40 WPM 5 \\sim 40\\text{ WPM} 5∼40 WPM(经工程优化可达 70 WPM 70\\text{ WPM} 70 WPM) |
自动跟踪
5 ∼ 55 WPM 5 \\sim 55\\text{ WPM} 5∼55 WPM |
| 内存与堆开销 | 极小,32 长度静态环形数组与数个标量状态 | 需驻留 FFT 帧缓冲与时频矩阵 |
| 抗强邻频干扰(QRM) | 优异:若中心频点准确,高阶窄带滤波抑制强 | 依赖频域分辨率,极近强干扰可能影响主峰判断 |
| 弱信号/变频信号捕获 | 容易因载频漂移导致包络跌出 Schmitt 门限 | 即使信号弱或有频漂,时频图能量追踪依然强健 |
| 典型适用场景 | 确定性通道、DSP 硬件协处理、多路固定载频并行推流 | 开放麦克风拾音、手持终端、频点不定的扫频辅助解调 |
第五章 工业级工程实践:双引擎混合容错架构
在专业电报信号监视与智能报房系统中,单一算法往往难以通吃所有极端场景:
- 场景 A:信号频率完全稳定,但背景有严重的突发性天电脉冲;
- 场景 B:手发报速度忽快忽慢,甚至有数十赫兹的音频调频现象;
- 场景 C:低功耗嵌入式网关对 CPU 占有率有着严苛的上限要求。
在实际工业与国防工程设计中,我们经常构建分层混合仲裁管道(Hybrid Arbitration Pipeline):
[双声道/单声道 PCM 音频流 (8kHz)]
│
┌──────────────┴──────────────┐
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ ggmorse 引擎 │ │ CW Skimmer 引擎 │
│ (时频宽带动态分析) │ │ (窄带自适应时钟) │
└───────────────────┘ └───────────────────┘
│ (粗捕获输出) │
│ 实时频点 $f_p$ │ 实时置信度 $C_s$
▼ │
┌───────────────────┐ │
│ 动态闭环反馈环路 │ │
│ 更新 Skimmer 载频 ├───────────────────┤
│ (Auto-Tune Biquad)│ │
└───────────────────┘ │
│ │
└──────────────┬──────────────┘
▼
┌───────────────────────┐
│ 智能加权仲裁决策 │
│ (Levenshtein / SSOT) │
└───────────────────────┘
│
▼
[标准 ASCII 字符流]
[4908 / 八八表战术解算]
工程实战要点分享:
1.0
∼
1.5
1.0 \\sim 1.5
1.0∼1.5 秒的零采样值(Zero-samples Padding),强制驱动状态机走出空闲跳变,吐出残留字符。
结语:经典通信与 AI 时代的交汇
从纯粹数学与离散统计构建的 CW Skimmer,到融合现代时频分析思想的 ggmorse,我们看到了两代软件工程师在对抗无线信道噪声、解密古老电码过程中的智慧火花。
如今,随着 TinyML、深度卷积循环网络(CRNN)以及声学大模型(如 Whisper 微调模型)的兴起,基于神经网络的端到端 CW 解码也逐步进入前沿实验室。然而,CW Skimmer 与 ggmorse 这类经典算法凭借微秒级极低延迟、几百 KB 的极小内存开销、以及零黑盒的算法可解释性,依然是高可靠通信系统中不可动摇的中流砥柱。
电波不息,滴答永鸣。向所有在噪声与星空之间追寻微弱信号的工程师与 HAM 致敬!
本文源码参考与延伸阅读:
- ggerganov/ggmorse GitHub Repository
- JvanKatwijk/cwskimmer GitHub Repository
- [ARRL The ARRL Handbook for Radio Communications]
网硕互联帮助中心



评论前必须登录!
注册