Zpět na domů

WSPR protokol: Python kódování volacích značek

WSPR — protokol pro analýzu šíření slabých KV signálů s pomocí distribuované sítě majáků. Popisuje strukturu: 4-FSK, 50bitová zprávy, Python implementace kodeků pro volací značky a lokátory. Příklady tříd MsgItem a WSPRCallsign.

WSPR na Pythonu: kódování ionosférických majáků
Advertisement 728x90

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í.

Google AdInline article slot

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:

Google AdInline article slot
  • 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:

Google AdInline article slot
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: R9FEU260587010 (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

Advertisement 728x90

Číst dál