Szia Habr!
Valószínűleg sokan, akik karórát vagy meteorológiai állomást vásárolnak, láthatták a Radio Controlled Clock vagy akár az atomóra logót a csomagoláson. Ez nagyon kényelmes, mert elég az órát az asztalra tenni, és egy idő után automatikusan beáll a pontos időre.

Kitaláljuk, hogyan működik, és írjunk egy dekódert Pythonban.
Különféle időszinkronizációs rendszerek léteznek. Európában a legnépszerűbb a német rendszer Japánnak saját rendszere van , az USA-ban van egy rendszer , stb. Továbbá a sztori a DCF77-ről fog szólni, mint Oroszország európai részének és a szomszédos országok egyes helyein a legrelevánsabb és a leginkább elérhető fogadtatásra (a Távol-Kelet lakóinak ellenkező véleménye lehet, ők viszont fogadni és elemezni a japán jelet;).
Az alábbiakban leírtak a DCF77-ről szólnak.
Jel vétel
A DCF77 egy hosszúhullámú állomás, amely 77.5 kHz-en működik, és AM jeleket ad. Az 50 kW teljesítményű állomás Frankfurttól 25 km-re található, 1959-ben kezdte meg a munkát, 1973-ban a pontos időponthoz a dátumra vonatkozó információkat is hozzáadták. A 77 kHz-es frekvencia hullámhossza nagyon nagy, így az antennamező méretei is nagyon megfelelőek (fotó a Wikipédiából):

Ilyen antennával és bemeneti teljesítménnyel a vételi körzet szinte egész Európát, Fehéroroszországot, Ukrajnát és Oroszország egy részét lefedi.

Bárki rögzíthet. Ehhez lépjen az online vevőhöz , válassza ki a 76.5 KHz-es frekvenciát és ott az USB modulációt. Egy képnek valahogy így kell megjelennie:

Ugyanitt megnyomjuk a letöltés gombot és rögzítünk egy pár perces töredéket. Természetesen, ha van egy "igazi" vevő, amely képes 77.5 kHz-es frekvencia rögzítésére, akkor használhatja.
Természetesen a pontos idejű rádiójelek interneten keresztül történő vételekor nem kapunk igazán pontos időt - a jel késleltetéssel kerül továbbításra. De a célunk csak az, hogy megértsük a jel szerkezetét, ehhez bőven elég az internetes felvétel. A való életben természetesen speciális eszközöket használnak a fogadásra és a dekódolásra, ezeket az alábbiakban tárgyaljuk.
Szóval, megkaptuk a rekordot, kezdjük el feldolgozni.
Jel dekódolás
Töltsük be a fájlt Pythonnal, és nézzük meg a 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 a jel burkológörbéjét a Hilbert transzformációval vesszük:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Kibontott eredmény:

Simítsuk ki a zajkibocsátást aluláteresztő szűrővel, egyúttal számoljuk ki az átlagértéket, később jól fog jönni az elemzéshez.
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): egy majdnem téglalap alakú jel, amelyet meglehetősen könnyű elemezni.

Elemzés
Először meg kell szereznie a bitsorozatot. Maga a jelszerkezet nagyon egyszerű.

Az impulzusok második intervallumokra vannak osztva. Ha az impulzusok közötti távolság 0.1 s (azaz magának az impulzusnak a hossza 0.9 s), akkor a bitsorozathoz „0”-t adunk, ha a távolság 0.2 s (azaz a hossza 0.8 s), akkor hozzáadjuk „1”. Minden perc végét "hosszú" impulzus jelzi, 2 s hosszú, a bitsorozat nullázódik, és a kitöltés újra kezdődik.
A fentiek könnyen megírhatók Pythonban.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] < avg and A[pos+1] > avg:
# Signal begin
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Signal end
sig_stop = pos
diff = sig_stop - sig_start
if diff < 0.85*sample_rate:
bits_str += "1"
if diff > 0.85*sample_rate and diff < 1.25*sample_rate:
bits_str += "0"
if diff > 1.5*sample_rate:
print(bits_str)
bits_str = ""
pos += 1
Ennek eredményeként bitsorozatot kapunk, példánkban két másodpercig így néz ki:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Egyébként érdekes, hogy a jelben van egy „második réteg” is. A bitsorozat is kódolva van . Elméletileg ennek robusztusabb dekódolást kellene biztosítania még gyengített jel esetén is.
Utolsó lépésünk: a tényleges adatok beszerzése. A bitek átvitele másodpercenként egyszer történik, így csak 59 bitünk van, amibe elég sok információ van kódolva:

A bitek leírása a és eléggé kíváncsiak. Az első 15 bitet nem használják, bár tervezték, hogy hangosbeszélő és polgári védelmi rendszerekhez használják őket. Az A1 bit azt jelzi, hogy az óra a következő órában nyári időszámításra vált. Az A2 bit azt jelzi, hogy a következő óra hozzáadásra kerül , amivel néha a Föld forgása szerint korrigálják az időt. A fennmaradó bitek órákat, perceket, másodperceket és dátumot kódolnak.

Aki önállóan szeretne kísérletezni, annak 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 = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
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 program futtatásakor valami ehhez hasonló kimenetet fogunk látni:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Valójában ez az egész varázslat. Egy ilyen rendszer előnye, hogy a dekódolás rendkívül egyszerű, és bármely, a legegyszerűbb mikrokontrolleren elvégezhető. Csak megszámoljuk az impulzusok hosszát, összegyűjtünk 60 bitet, és minden perc végén megkapjuk a pontos időt. Az időszinkronizálás egyéb módszereivel (például GPS-szel, vagy ne adj isten, az internettel :) összehasonlítva az ilyen rádiós szinkronizáláshoz gyakorlatilag nincs szükség áramra - például egy átlagos otthoni időjárás-állomás körülbelül egy évig működik 2 AA elemmel. Ezért még a karórák is készülnek rádiós szinkronizálással, nem beszélve persze a faliórákról vagy az utcai állomások óráiról.
A DCF kényelme és egyszerűsége a barkácsolás szerelmeseit is vonzza. Mindössze 10-20 dollárért vásárolhatunk kész antennamodult, kész vevővel és TTL kimenettel, ami Arduino-hoz vagy más kontrollerhez köthető.

A már megírt Arduino-hoz és . Az viszont már ismert, hogy bármit is csinálsz a mikrokontrolleren, vagy órát vagy meteorológiai állomást kapsz. Egy ilyen eszközzel nagyon egyszerű a pontos idő megállapítása, kivéve persze, ha éppen a recepción tartózkodik. Nos, az órára felakaszthatja az „Atomic Clock” feliratot, és egyúttal elmagyarázhatja mindenkinek, hogy a készülék valóban atomóra segítségével szinkronizálódik.
Azok, akik szeretnének, akár a régi nagymama óráját is frissíthetik egy új, rádiós szinkronizálással ellátott mechanizmus telepítésével:

Az eBay-en a "Radio Controlled Movement" kulcsszavak használatával találhat ilyet.
És végül egy hasznos tipp azoknak, akik idáig elolvasták. Még ha pár ezer kilométeres körzetben egyetlen rádióadó sincs is, könnyen generálhatsz egyet magadnak. Van egy alkalmazás a Google Playen "DCF77 Emulator" néven, ami jelet küld a fejhallgatónak. A szerző azt állítja, hogy ha a fejhallgató kábelét az óra köré tekerjük, az fogja a jelet (vajon hogyan, mivel a hagyományos fejhallgatók nem adnak ki 77 kHz-es jelet, de valószínűleg a felharmonikusok miatt van). Az én órámon... Android A 9-es program egyáltalán nem működött – egyszerűen nem volt hang (vagy talán csak én nem hallottam – végül is 77 kHz-es. :)), de talán valaki másnak nagyobb szerencséje lesz. Vannak azonban, akik saját, teljes értékű DCF jelgenerátort építenek, amit könnyű elkészíteni egy Arduino vagy ESP32 segítségével:

(forrás )
Következtetés
A DCF rendszer nagyon egyszerűnek és kényelmesnek bizonyult. Egy egyszerű és olcsó vevő segítségével mindig és mindenhol pontos időt kaphat, természetesen a recepción. Úgy tűnik, a széles körben elterjedt digitalizáció és a „dolgok internete” ellenére is az ilyen egyszerű megoldásokra még sokáig lesz kereslet.
Forrás: will.com
