摩斯密码的诞生与本质
摩斯密码(Morse Code)由萨缪尔·摩斯与阿尔弗雷德·维尔于 1830 年代发明,最初用于电报传输。它本质上是一种用长短信号的组合表示字符的二进制编码系统:
.(dit / dot):短信号-(dah / dash):长信号,时长为短信号的 3 倍
每个字符由若干点划组合而成,例如 S = ...、O = ---,因此 SOS 的摩斯码为 ... --- ...,这也是最广为人知的国际求救信号。
配套工具:摩斯密码编解码工具
国际摩斯密码编码表
国际摩斯密码(ITU-R M.1677)覆盖拉丁字母、数字与常用标点:
字母
| 字符 | 码 | 字符 | 码 | 字符 | 码 |
|---|---|---|---|---|---|
| A | .- | J | .--- | S | ... |
| B | -... | K | -.- | T | - |
| C | -.-. | L | .-.. | U | ..- |
| D | -.. | M | -- | V | ...- |
| E | . | N | -. | W | .-- |
| F | ..-. | O | --- | X | -..- |
| G | --. | P | .--. | Y | -.-- |
| H | .... | Q | --.- | Z | --.. |
| I | .. | R | .-. |
数字
| 数字 | 码 | 数字 | 码 |
|---|---|---|---|
| 1 | .---- | 6 | -.... |
| 2 | ..--- | 7 | --... |
| 3 | ...-- | 8 | ---.. |
| 4 | ....- | 9 | ----. |
| 5 | ..... | 0 | ----- |
常用标点
| 标点 | 码 | 标点 | 码 |
|---|---|---|---|
. | .-.-.- | ? | ..--.. |
, | --..-- | ' | .----. |
! | -.-.-- | / | -..-. |
( | -.--. | ) | -.--.- |
& | .-... | : | ---... |
; | -.-.-. | = | -...- |
+ | .-.-. | - | -....- |
_ | ..--.-- | " | .-..-. |
@ | .--.-. |
PARIS 标准与 WPM 速度
什么是 PARIS 标准?
摩斯密码的速度单位是 WPM(Words Per Minute,每分钟字数)。但”一个字”到底有多长?业界采用 PARIS 作为标准字:
P = .--. (4 个信号 + 3 个字符内间隔 = 8 单位)
A = .- (2 个信号 + 1 个间隔 = 4 单位,但末尾再加间隔)
R = .-. (3 个信号 + 2 个间隔 = 7 单位)
I = .. (2 个信号 + 1 个间隔 = 4 单位)
S = ... (3 个信号 + 2 个间隔 = 7 单位)
单词间隔 = 7 单位
经过精确计算,PARIS 一个字的总时长 = 50 个 dot 单位。因此:
WPM = 60 秒 / 一个 PARIS 字的时长
一个 PARIS 字的时长 = 50 × dot 时长
→ dot 时长 = 60 / (WPM × 50) = 1.2 / WPM 秒 = 1200 / WPM 毫秒
时序规则
基于 dot 时长,所有时序都有精确比例:
| 元素 | 时长(单位:dot) | 20 WPM 时的毫秒数 |
|---|---|---|
dot (.) | 1 | 60 ms |
dash (-) | 3 | 180 ms |
| 字符内间隔(点划之间) | 1 | 60 ms |
| 字符间隔(字母之间) | 3 | 180 ms |
| 单词间隔(词之间) | 7 | 420 ms |
WPM 速度参考
| WPM | dot 时长 | 适用场景 |
|---|---|---|
| 5 | 240 ms | 初学者练习 |
| 13 | 92 ms | 业余无线电入门级 |
| 20 | 60 ms | 业余无线电标准速度 |
| 30 | 40 ms | 熟练操作员 |
| 40+ | 30 ms | 高速 Contest 比赛 |
分隔符约定
摩斯密码的纯点划字符串无法直接区分字符与单词,必须依赖分隔符:
字符内分隔
同一字符的点划之间无任何符号,紧密相连:
A=.-(dot 紧接 dash,无分隔)
字符间分隔
不同字符之间用空格分隔:
SOS=... --- ...(字符之间各一个空格)
单词间分隔
不同单词之间用 / 或多个空格分隔:
HELLO WORLD=.... . .-.. .-.. --- / .-- --- .-. .-.. -..
容错处理
实际使用中分隔符并不统一,优秀的解码器应当容忍:
...---...(无分隔)→ 应识别为SOS... --- ...(多空格)→ 等价于单空格... / --- / ...(/两侧带空格)→ 等价于单词分隔...|---|...(用|代替/)→ 等价于单词分隔
配套工具的解码逻辑先做归一化:将 | 替换为 /,再统一 / 格式,最后按 / 切分单词、按空格切分字符。
Web Audio API 音频合成原理
摩斯密码的音频播放是工具实现的核心难点。我们采用 Web Audio API 的 OscillatorNode + GainNode 组合生成纯正弦波 beep。
为什么不用 <audio> 标签?
- 体积成本:预录音频文件会增加数十 KB 体积
- 灵活度:WPM、音高、波形调整需要实时计算
- 精度控制:精确到毫秒的时序调度
AudioContext 调度模型
Web Audio API 采用图模型:音频节点连接成链,最终输出到 ctx.destination(扬声器)。所有时间都基于 ctx.currentTime,这是绝对时间轴,不受主线程阻塞影响。
// 创建音频上下文(浏览器策略要求用户交互后才能恢复)
const ctx = new AudioContext();
if (ctx.state === 'suspended') {
await ctx.resume();
}
// 调度一个 beep:在 startTime 开始,持续 duration 秒
function scheduleBeep(ctx, startTime, duration, frequency) {
const oscillator = ctx.createOscillator();
const gain = ctx.createGain();
oscillator.type = 'sine'; // 正弦波,音色纯净
oscillator.frequency.value = frequency; // 频率(Hz),常见 600 Hz
// ADSR 包络简化版:快速淡入 + 持续 + 快速淡出
// 关键作用:避免波形突变产生的"爆音"(click artifact)
const fadeTime = Math.min(0.005, duration / 4);
gain.gain.setValueAtTime(0, startTime);
gain.gain.linearRampToValueAtTime(0.3, startTime + fadeTime);
gain.gain.setValueAtTime(0.3, startTime + duration - fadeTime);
gain.gain.linearRampToValueAtTime(0, startTime + duration);
oscillator.connect(gain);
gain.connect(ctx.destination);
oscillator.start(startTime);
oscillator.stop(startTime + duration);
}
为什么需要淡入淡出?
正弦波在 t=0 时若直接拉到 0.3 振幅,会产生阶跃信号,频谱上会引入宽带噪声,听感上为”啪”的爆音。linearRampToValueAtTime 在 5 ms 内线性爬升振幅,让波形平滑过渡,听感上为纯净的”嘀”声。
时序累加
摩斯密码的每个元素必须严格按时长累加:
const dot = 1200 / wpm; // 基本单位(毫秒)
let timeOffset = ctx.currentTime + 0.05; // 留 50 ms 缓冲
for (const ch of morseString) {
if (ch === '.') {
scheduleBeep(ctx, timeOffset, dot / 1000, freq);
timeOffset += dot / 1000 + dot / 1000; // 信号 + 1 dot 间隔
} else if (ch === '-') {
scheduleBeep(ctx, timeOffset, (3 * dot) / 1000, freq);
timeOffset += (3 * dot) / 1000 + dot / 1000; // 信号 + 1 dot 间隔
} else if (ch === ' ') {
timeOffset += (2 * dot) / 1000; // 已含 1 dot 间隔,补 2 dot 凑 3 dot 字符间隔
} else if (ch === '/') {
timeOffset += (4 * dot) / 1000; // 已含 3 dot 字符间隔,补 4 dot 凑 7 dot 单词间隔
}
}
TypeScript 严格模式下的陷阱
在 strict 模式下,useRef<AudioContext | null>(null) 创建的 ref 类型为 MutableRefObject<AudioContext | null>。常见错误:
// ❌ 错误写法:let 变量在 await 后丢失 null 收窄
let ctx = audioCtxRef.current;
if (!ctx) {
ctx = new AudioContext(); // 此处赋值
}
// 此处 ctx 仍是 AudioContext | null(await 后 let 收窄失效)
await ctx.resume(); // TS 报错:possibly null
// ✅ 正确写法:用 const 在赋值后保持收窄
if (!audioCtxRef.current) {
audioCtxRef.current = new AudioContext();
}
const ctx = audioCtxRef.current; // const 在条件分支后保持收窄
if (!ctx) return;
await ctx.resume(); // TS 通过
此外,旧版 Safari 使用 webkitAudioContext 而非 AudioContext,需做兼容:
const Ctx = typeof AudioContext !== 'undefined'
? AudioContext
: (window as any).webkitAudioContext;
if (!Ctx) return;
audioCtxRef.current = new Ctx();
中文摩斯密码的局限性
摩斯密码原生不支持中文字符,因为它是为 26 个拉丁字母设计的。中文摩斯码的几种历史方案都有明显缺陷:
方案一:电码本(四位数字)
早期中文电报采用四位数字编码,每个汉字映射为 4 位数字,再转为摩斯码:
中 = 0022 → ----- ----- ..--- ..---
文 = 2429 → ..--- ....- ..--- ------
缺点:
- 需要查阅电码本,无法直观记忆
- 一个汉字要发 16 个点划,效率极低
- 电码本有 9000+ 字,无法扩展
方案二:拼音转写
将中文转为拼音,再用拉丁字母摩斯码:
中文 = ZHONG WEN → --.. .... --- -. --. .-- . -.
缺点:
- 同音字无法区分(“中”和”钟”都是 ZHONG)
- 拼音本身有歧义(ZH/CH/SH 的双字母表示)
- 失去汉字的语义信息
方案三:Unicode 码点
理论上可以将汉字的 Unicode 码点转为数字再编码,但实际不可行:
中 = U+4E2D → 4E2D → ....- . ..--- -..
缺点:
- 一个汉字 6-8 个点划,无意义序列难以人工解码
- 完全失去可读性
工具实现策略
配套工具采用保守策略:仅支持 ASCII 字符集(A-Z、0-9、常用标点)。输入中文时给出明确警告:
以下字符无摩斯码对应,已跳过:你 好
这避免了”伪摩斯码”的误导,同时通过警告提示用户输入范围。
实际应用场景
1. 紧急求救(SOS)
SOS 是国际通用的求救信号(... --- ...),其特点是三短三长三短的节奏感强,便于识别。在无通信设备的场景下,可用光源(手电筒)、声音(敲击)、反光镜发送。
工具可用于:
- 教学演示 SOS 节奏
- 验证练习是否正确
- 调整不同 WPM 下的听感
2. 业余无线电(Ham Radio)
业余无线电爱好者至今仍在使用摩斯密码(CW 模式),原因:
- 极低信噪比下仍可通信:当语音无法辨识时,CW 信号仍能解码
- 频谱效率高:极窄带宽(约 100 Hz)即可传输
- 跨国通用:无语言障碍,纯符号通信
- 法规要求:某些频段或等级的执照要求掌握 CW
工具可作为 CW 学习辅助,调整 WPM 从 5(初学)到 20(标准)再到 30+(高级)循序渐进。
3. 教学与娱乐
- STEM 教育:编程教学中用作”编码解码”的入门案例,概念简单但涉及映射、分隔符、时序
- 密室逃脱 / 解谜游戏:摩斯密码是经典的解谜元素
- 影视致敬:理解电影中摩斯密码情节(如《星际穿越》的”Stay”)
4. 无障碍辅助
视障人士有时使用摩斯密码作为输入方式(通过敲击触发字符)。Web 工具可用于学习与练习。
5. 嵌入式与 IoT
嵌入式设备常用 LED 或蜂鸣器输出状态码,摩斯密码是紧凑的编码方案:
设备故障 = ERROR → . .-. .-. --- .-.
工程师可用工具快速生成对应摩斯码与音频,验证设备输出。
性能与体积考量
算法复杂度
- 编码:O(n),遍历字符串查表
- 解码:O(n),遍历摩斯码字符串查反向表
- 音频调度:O(n),n 为摩斯码字符数,每个字符一次
scheduleBeep调用
内存占用
- 编码表:约 60 个键值对,< 2 KB
- 反向表:
Object.fromEntries一次构建,< 2 KB - AudioContext:单例复用,约几 MB(浏览器实现)
Bundle 体积
整套工具(含 React 组件 + 编解码逻辑 + Web Audio 调度)压缩后约 5-7 KB,远低于 200 KB 的红线。
与其他编码工具的对比
| 工具 | 字符集 | 用途 | 是否双向 |
|---|---|---|---|
| Base64 | 任意二进制 | 数据传输 | 是 |
| Hex | 任意二进制 | 调试、低层查看 | 是 |
| 摩斯密码 | ASCII 字母数字标点 | 音频、电报、教学 | 是 |
| URL 编码 | URL 中的特殊字符 | URL 参数传递 | 是 |
| HTML 实体 | HTML 中的特殊字符 | 防 XSS、显示特殊字符 | 是 |
摩斯密码的独特价值在于音频输出与时序调度,是其他编码工具所不具备的。它更偏向”通信”而非”数据表示”。
总结
摩斯密码看似过时,实则在应急通信、业余无线电、教学、IoT 等场景仍有实用价值。本文配套的摩斯密码编解码工具实现了:
- 双向编解码:文本 ↔ 摩斯码,容错处理多种分隔符
- 音频播放:基于 Web Audio API,精确遵循 PARIS 标准时序
- 可调速度:5-40 WPM,覆盖初学到熟练
- 可调音高:300-1000 Hz,适配不同听感偏好
- 速查表:内置完整字母数字标点对照
- 中文提示:输入非 ASCII 字符时给出明确警告
全部计算在浏览器本地完成,零网络请求、零依赖、零追踪,适用于学习、教学、应急通信练习等场景。