Python 实现 WSPR 协议:编码示例详解
WSPR 协议采用 4-FSK 调制方式,在噪声基底以下传输数据。每次发射周期为 110.59 秒,仅占用 5.85 Hz 带宽,并通过卷积编码(码率 ½)将完整消息压缩为 50 比特。发射机在每个偶数分钟整点(±1 秒内)启动,并在指定的 200 Hz 频带内随机选择发射频率。
消息内容包括电台呼号、4 字符 QTH 定位网格(如 PM96)、以及以 dBm 为单位的发射功率。接收端完成解码后生成报告,并在每轮发射周期剩余的 10 秒内上传至 [wsprnet.org](https://wsprnet.org)。
短波传播的物理原理
短波(HF)频段(3–30 MHz)依赖电离层作为天然波导。信号在距地表 140–1000 公里高度发生折射:部分能量逸散至太空,其余则反射回地面。经多次“跳跃”(multi-hop),可实现全球覆盖。
电离层状态受太阳活动强度、昼夜及季节影响显著:
- 80 米波段:夜间通信效果最佳;
- 40 米波段:夜间远距离稳定,白天仅适合短距通信;
- 20 米波段:日间远距离通信可靠性高;
- 10 米波段:仅在太阳活动峰值期开通。
关键细节:因反射角过陡,极短距离的直射路径往往难以实现。
协议分层架构
WSPR 将处理流程划分为三层:
- 应用层:承载呼号、定位网格、发射功率;
- 表示层:使用定制字符表将原始数据打包为 50 比特;
- 链路层:执行 4-FSK 调制,并集成同步机制与前向纠错功能。
所有发射严格同步于协调世界时(UTC);信标台采用伪随机调度——每小时最多激活 7 个时隙。
表示层:字符表与编解码器
数据被编码为预定义字符表中的索引值:
import string
CHAR_TABLE_NUMERIC = string.digits
CHAR_TABLE_LETTERS = string.ascii_uppercase
WSPR_CHAR_TABLE_ALPHANUM = f'{CHAR_TABLE_NUMERIC}{CHAR_TABLE_LETTERS}'
WSPR_CHAR_TABLE_ALPHANUM_SPACE = f'{CHAR_TABLE_NUMERIC}{CHAR_TABLE_LETTERS} '
辅助转换函数:
def nchar(c: str, table: str) -> int:
return table.find(c)
def charn(c: int, table: str) -> str:
return table[c]
def ct_decode(ct: str, val: int, l: int) -> str:
s = ''
ct_l = len(ct)
for i in range(l):
s = charn(val % ct_l, ct) + s
val //= ct_l
return s
抽象接口:MsgItem
消息组件的基类:
from abc import ABCMeta, abstractmethod
import typing
class MsgItem(metaclass=ABCMeta):
__slots__ = ('val_str', 'val_int')
def __init__(self, val: typing.Union[str, int]):
if not self.validate(val):
raise ValueError('验证失败')
if isinstance(val, str):
self.val_str = val.strip()
self.val_int = self.to_int()
elif isinstance(val, int):
self.val_int = val
self.val_str = self.to_str()
else:
raise TypeError(f'不支持的数据类型 {type(val)}')
@classmethod
@abstractmethod
def _validate_str(cls, val: str) -> bool:
...
@classmethod
@abstractmethod
def _validate_int(cls, val: int) -> bool:
...
@classmethod
def validate(cls, val: typing.Union[str, int]) -> bool:
if isinstance(val, str):
return cls._validate_str(val)
elif isinstance(val, int):
return cls._validate_int(val)
return False
@abstractmethod
def to_int(self) -> int:
...
@abstractmethod
def to_str(self) -> str:
...
@property
def as_str(self):
return self.val_str
@property
def as_int(self):
return self.val_int
呼号编码
呼号共 6 位,每位映射至独立字符集:
WSPR_BASECALL_CHAR_MAP = [
WSPR_CHAR_TABLE_ALPHANUM_SPACE,
WSPR_CHAR_TABLE_ALPHANUM,
WSPR_CHAR_TABLE_NUMERIC,
WSPR_CHAR_TABLE_LETTERS_SPACE,
WSPR_CHAR_TABLE_LETTERS_SPACE,
WSPR_CHAR_TABLE_LETTERS_SPACE
]
WSPRCallsign 类实现:
class WSPRCallsign(MsgItem):
@classmethod
def _validate_str(cls, val: str) -> bool:
return len(val) <= 6
@classmethod
def _validate_int(cls, val: int) -> bool:
return val < 262177560
def to_int(self) -> int:
return hash(self)
def to_str(self) -> str:
return ct_map_decode(WSPR_BASECALL_CHAR_MAP, self.val_int).strip()
@staticmethod
def _normalize_cs(cs: str) -> str:
return ' ' * (6 - len(cs)) + cs
def __hash__(self):
cs_norm = self._normalize_cs(self.val_str)
return ct_map_encode(WSPR_BASECALL_CHAR_MAP, cs_norm)
示例:R9FEU → 260587010(二进制:0b1111100010000011111000000010)。
QTH 定位网格编码
定位网格为 4 字符字符串(2 字母 + 2 数字),对应一个 180×180 公里的地理网格单元。其编码直接映射至全球大地坐标系。
核心要点:
- WSPR 凭借 4-FSK 调制与强纠错能力,可检测远低于噪声基底的微弱信号;
- 基于 UTC 的 2 分钟同步时隙机制,彻底避免发射冲突;
- 50 比特有效载荷分配:呼号(22 比特)、定位网格(15 比特)、发射功率(7 比特)、消息类型(1 比特)、CRC 校验(4 比特);
- 信标台与接收报告系统协同构建实时全球传播图谱;
- Python 实现支持本地离线测试与编码逻辑验证,大幅降低开发门槛。
— Editorial Team
暂无评论。