WSPR: Implementace protokolu v Pythonu s příklady kódování
Protokol WSPR používá modulaci 4-FSK k přenosu dat pod úrovní šumu. Délka vysílacího cyklu je 110,59 sekundy, šířka pásma činí 5,85 Hz a zpráva je zabalená do 50 bitů pomocí konvolučního kódu (rychlost ½). Vysílače se aktivují na začátku každé sudé minuty s přesností lepší než ±1 sekunda a náhodně vybírají frekvenci uvnitř 200-Hz pásmového okna.
Přenášená data obsahují povolový znak stanice, čtyřznakový QTH-lokátor a výkon v dBm. Přijímače dekodují signály, generují zprávy a odesílají je na wsprnet.org během zbývajících 10 sekund vysílacího slotu.
Fyzikální principy šíření signálů
Krátkovlnné pásmo (3–30 MHz) využívá ionosféru jako přirozený vlnovod. Signály se lámou ve výšce 140–1000 km: část unikne do vesmíru, část se odrazí zpět k Zemi. Násobné odrazy (tzv. hops) umožňují globální pokrytí.
Stav ionosféry závisí na sluneční aktivitě, denní době a ročním období:
- 80 m: spojení převážně v noci;
- 40 m: dlouhé vzdálenosti v noci, krátké ve dne;
- 20 m: dlouhé vzdálenosti ve dne;
- 10 m: efektivní jen při maximální sluneční aktivitě.
Zvláštnost: přímé spojení na krátké vzdálenosti je obtížné kvůli velmi ostrému úhlu odrazu od ionosféry.
Struktura protokolu podle vrstev
WSPR rozděluje zpracování do tří logických vrstev:
- Aplikační vrstva: povolový znak, lokátor, výkon;
- Vrstva reprezentace: zabalení do 50 bitů pomocí specializovaných abeced;
- Kanálová vrstva: modulace 4-FSK s synchronizací a opravou chyb.
Všechna vysílání jsou synchronizována podle UTC; majáky pracují náhodně – až 7 slotů za hodinu.
Vrstva reprezentace: abecedy a kodeky
Data jsou kódována jako indexy v předdefinovaných tabulkách:
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} '
Funkce pro převod mezi znaky a čísly:
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
Abstraktní rozhraní MsgItem
Základní třída pro komponenty zprávy:
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('Chyba ověření')
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'Nepodporovaný typ dat {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
Kódování povolového znaku
Schéma: 6 pozic s různými abecedami:
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
]
Třída 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)
Příklad: R9FEU → 260587010 (binárně 0b1111100010000011111000000010).
Kódování QTH-lokátoru
Lokátor má 4 znaky (2 písmena + 2 číslice) a odpovídá čtverci o rozměru 180×180 km. Kódovací vzorec vychází z globální mřížky.
Důležité poznámky:
- WSPR detekuje signály pod úrovní šumu díky kombinaci 4-FSK a robustní korekci chyb;
- Synchronizace podle UTC a dvouminutové sloty minimalizují kolize;
- Zabalení do 50 bitů: povolový znak (22 bitů), lokátor (15 bitů), výkon (7 bitů), typ (1 bit), CRC (4 bity);
- Majáky a reportéři společně vytvářejí globální mapu šíření signálů;
- Implementace v Pythonu umožňuje lokální testování kódování bez potřeby hardwaru.
— Editorial Team
Zatím žádné komentáře.