Helló Habr.
Valószínűleg sokan, akik órát vagy időjárás-állomást vásárolnak, látták a csomagolásukon a Radio Controlled Clock vagy az Atomic Clock logót. Ez rendkívül kényelmes, mivel elég csak az órát az asztalra tenni, és egy idő után automatikusan beáll az pontos időre.

Nézzük meg, hogyan működik ez, és írjunk egy dekódert Python nyelven.
Különböző időszinkronizáló rendszerek léteznek. A legelterjedtebb Európában a német rendszer, , Japánban saját rendszerük van, , az Egyesült Államokban pedig létezik rendszer, , és így tovább. A következőekben a DCF77-ről lesz szó, mint a legrelevánsabb és legjobban fogható jelekről az európai Oroszország és szomszédos országok egyes részein (a Távol-Kelet lakosai talán más véleményen vannak, de ők is képesek fogadni és elemezni a japán jelet;).
Minden, ami később következik, a DCF77-ről szól.
Jelvétel
A DCF77 egy hosszúhullámú állomás, ami a 77.5kHz frekvencián működik, és amplitúdómodulált jeleket ad át. Az állomás 50kW teljesítményű, Frankfurt 25 km-es távolságában található, 1959 óta működik, 1973-ban a pontos időhöz csatlakozott a dátum információ is. A 77kHz frekvencián a hullámhossz meglehetősen nagy, így az antenna mező mérete is meglehetősen jelentős (fotó a Wikipédiáról):

Egy ilyen antennával és a leadott teljesítménnyel a vételi zóna gyakorlatilag egész Európát, Fehéroroszországot, Ukrajnát és Oroszország egy részét átfogja.

Mindenki rögzíthet jelet. Ehhez elegendő, ha belépünk egy online vevőre, , ott válasszuk ki a 76.5kHz-es frekvenciát és az USB-modulációt. Egy képernyő kell, hogy megjelenjen, ami körülbelül ilyen:

Ott nyomjuk meg a letöltés gombot, és rögzítünk egy néhány perces szakaszt. Természetesen, ha „igazi” vevőnk van, ami képes rögzíteni a 77.5kHz frekvenciát, azt is használhatjuk.
Természetesen, ha pontos időjeleket fogadunk az interneten keresztül, nem fogunk valóban pontos időt kapni — a jel késlekedéssel érkezik. De a célunk csupán az, hogy megértsük a jel struktúráját, ehhez az internetes felvételek bőven elegendőek. A valóságban természetesen speciális eszközöket használnak a jelek fogadására és dekódolására, ezekről később lesz szó.
Nos, megkaptuk a felvételt, kezdjük el annak feldolgozását.
A jel dekódolása
Feltöltjük a fájlt Python segítségével, és megnézzük annak szerkezetét:
from scipy.io import wavfile
from scipy import signal
import matplotlib.pyplot as plt
import numpy as np
sample_rate, data = wavfile.read("dcf_websdr_2019-03-26T20_25_34Z_76.6kHz.wav")
plt.plot(data[:100000])
plt.show()
Tipikus amplitúdómodulációt látunk:

A dekódolás egyszerűsítése érdekében vegyük a jel körvonalát a Hilbert-transzformáció segítségével:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000]) A kibővített eredmény:

Simítjuk a zavaroktól származó kiugrásokat aluláteresztő szűrő segítségével, és kiszámítjuk az átlagos értéket, amely később hasznos lesz a parse-oláshoz.
b, a = signal.butter(2, 20.0/sample_rate)
zi = signal.lfilter_zi(b, a)
A, _ = signal.lfilter(b, a, A, zi=zi*A[0])
avg = (np.amax(A) + np.amin(A))/2 Eredmény (sárga vonal): szinte téglalap alakú jel, amelyet meglehetősen könnyű elemezni.

Parse-olás
Először meg kell szerezni a bitfolyamot. A jel szerkezete nagyon egyszerű.

A impulzusokat másodperces intervallumokra osztják. Ha az impulzusok közötti távolság 0,1 s (azaz az impulzus hossza 0,9 s), akkor a bitfolyamhoz "0"-t adunk hozzá; ha a távolság 0,2 s (azaz a hossza 0,8 s), akkor "1"-et adunk hozzá. Minden perc végén "hosszú" impulzussal jelöljük, amely 2 s hosszú, ekkor a bitfolyam nullázódik, és a kitöltés újra kezdődik.
A fentieket nem nehéz Python nyelven megírni.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] avg:
# Jel kezdete
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Jel vége
sig_stop = pos
diff = sig_stop - sig_start
if diff 0.85*sample_rate and diff 1.5*sample_rate:
print(bits_str)
bits_str = ""
pos += 1
Az eredmény egy bitfolyam, a mi példánkban két másodperc alatt ez így néz ki:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Érdekes módon a jelben van egy "második réteg" adat is. A bitfolyamot szintén kódolják Elméletileg ez biztosítania kell a stabilabb dekódolást még gyengített jel esetén is.
Az utolsó lépésünk: megszerezni magukat az adatokat. A bitek másodpercenként kerülnek átadásra, tehát összesen 59 bitet kapunk, amelyben elég sok információ van kódolva:

A biteket a következőképpen írják le: , és eléggé kíváncsiak. Az első 15 bit nem használatos, bár voltak tervek, hogy figyelmeztető rendszerekhez és polgári védelmi célokra használják. Az A1 bit jelzi, hogy a következő órában az órát nyári időszámításra állítják. Az A2 bit azt jelzi, hogy a következő órában egy , amelyet néha az idő korrekciójára használnak a Föld forgása szerint. A többi bit az órát, perceket, másodperceket és a dátumot kódolja.

Azok számára, akik saját maguk szeretnének kísérletezni, a dekódoló kód a spoiler alatt található.
Forráskód
def decode(bits):
if bits[0] != '0' or bits[20] != '1':
return
minutes, hours, day_of_month, weekday, month, year = map(convert_block,
(bits[21:28], bits[29:35], bits[36:42], bits[42:45],
bits[45:50], bits[50:58]))
days = ('Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat', 'Vasárnap')
print('{dow}, {dom:02}.{mon:02}.{y}, {h:02}:{m:02}'.format(h=hours, m=minutes, dow=days[weekday],
dom=day_of_month, mon=month, y=year))
def convert_ones(bits):
return sum(2**i for i, bit in enumerate(bits) if bit == '1')
def convert_tens(bits):
return 10*convert_ones(bits)
def right_parity(bits, parity_bit):
num_of_ones = sum(int(bit) for bit in bits)
return num_of_ones % 2 == int(parity_bit)
def convert_block(bits, parity=False):
if parity and not right_parity(bits[:-1], bits[-1]):
return -1
ones = bits[:4]
tens = bits[4:]
return convert_tens(tens) + convert_ones(ones)
A programot futtatva körülbelül ilyen kimenetet fogunk látni:
0011110110111000001011000001010000100110010101100010011000
Kedd, 2019. március 26., 21:41
0001111100110110001010100001010000100110010101100010011000
Kedd, 2019. március 26., 21:42
Nos, ez az egész varázslat. Ennek a rendszernek az előnye az, hogy a dekódolás rendkívül egyszerű, és bármilyen, még a legkisebb mikrokontrolleren is elvégezhető. Csak számoljuk az impulzusok hosszát, gyűjtjük a 60 bitet, és minden perc végén pontos időt kapunk. Más időszinkronizálási módszerekkel (például GPS vagy Isten ne adja, Internet:) összehasonlítva ez a rádiós szinkronizálás szinte nem igényel energiát — példának okáért, egy átlagos otthoni meteorológiai állomás körülbelül egy évet működik 2 AA elemről. Ezért a rádiós szinkronizálást még karórákban is használják, nem beszélve a faliórákról vagy a nyílt pályaudvarokról.
A DCF kényelme és egyszerűsége vonzó az otthoni barkácsolók számára is. Mindössze 10-20 dollárért lehet kész modult vásárolni antennával, kész vevőegységgel és TTL kimenettel, amely az Arduinóhoz vagy más mikrovezérlőhöz csatlakoztatható.

Az Arduinóhoz már elkészítettek és . Azért, ahogy azt már tudni is lehet – amit csak csinálunk a mikrokontrollerrel, az vagy óra, vagy meteorológiai állomás lesz. Ezzel az eszközzel valóban nem nehéz pontos időt kapni, persze, ha az adási zónában vagyunk. És a órára rá is lehet írni, hogy „Atomic Clock”, és egyúttal elmagyarázni mindenkinek, hogy az eszköz valóban atomórák segítségével szinkronizálódik.
Akik szeretnék, még a régi nagymama órát is fel lehet újítani egy új rádiószinkronizáló mechanizmus beszerelésével:

Ilyet találhatunk az ebay-en a „Radio Controlled Movement” kulcsszavakkal.
Végül, egy tipp azoknak, akik eddig eljutottak. Még ha a legközelebbi pár ezer kilométeren sincs egy rádiójelet adó adóállomás, ezt a jelet könnyen meg lehet generálni saját magunk is. A Google Play-en van egy „DCF77 Emulator” nevű program, amely jelet ad a fülhallgatókra. Az író állítása szerint, ha a fülhallgató vezetéket az órák köré tekerjük, azok elkapják a jelet (érdekes hogyan, hiszen a szokásos fülhallgatók nem adnak ki 77KHz jelet, de valószínűleg a harmonikusok segítségével történik a vétel). Nálam az Android 9-en a program egyáltalán nem működött — egyszerűen nem volt hang (vagy lehet, hogy nem hallottam — hiszen 77KHz:), de lehet, hogy másnak szerencsésebb lesz. Egyesek egyébként teljes DCF jelgenerátort is készítenek, ami könnyen megvalósítható ugyanazzal az Arduinoval vagy ESP32-vel:

(forrás )
Zárás
A DCF rendszer valóban meglehetősen egyszerű és kényelmes. Egy egyszerű és olcsó vevő segítségével mindig és mindenhol pontos időt lehet szerezni, természetesen az adási zónában. Úgy gondolom, hogy a digitális megoldások és az „okos eszközök” ellenére ezek az egyszerű megoldások még sokáig keresettek lesznek.
Forrás: habr.com
