Salut, Habr.
Probabil că mulți dintre cei care cumpără ceasuri sau stații meteorologice au văzut pe ambalaj logo-ul Radio Controlled Clock sau chiar Atomic Clock. Acest lucru este foarte practic, deoarece este suficient să pui ceasul pe masă și, după un timp, acesta se va regla automat la ora exactă.

Să vedem cum funcționează și să scriem un decodor în Python.
Există diferite sisteme de sincronizare a timpului. Cel mai popular din Europa este sistemul german , în Japonia există propriul sistem , în Statele Unite este sistemul , și așa mai departe. Aici vom discuta despre DCF77, cum fiind cel mai relevant și ușor de recepționat în anumite locuri din partea europeană a Rusiei și din țările vecine (locuitorii din Extremul Orient pot avea o opinie opusă, totuși ei pot recepționa și analiza semnalul japonez;).
Tot ceea ce urmează este despre DCF77.
Recepția semnalului
DCF77 este o stație de lungă undă, operând pe frecvența de 77,5 kHz, care transmite semnale în modulație de amplitudine. Stația, cu o putere de 50 kW, este situată la 25 km de Frankfurt și a început să funcționeze încă din 1959, iar în 1973 informațiile despre dată au fost adăugate la ora exactă. Lungimea de undă la frecvența de 77 kHz este foarte mare, așadar dimensiunile câmpului antenei sunt, de asemenea, destul de considerabile (foto din Wikipedia):

Cu o astfel de antenă și puterea livrată, zona de recepție acoperă practic toată Europa, Belarus, Ucraina și o parte din Rusia.

Oricine poate înregistra semnalul. Este suficient să accesați un receptor online , să selectați acolo frecvența de 76,5 kHz și modularea USB. Ar trebui să apară o imagine similară cu aceasta:

Acolo, apăsăm butonul download și înregistrăm un fragment de câteva minute. Desigur, având un „receptor adevărat”, capabil să înregistreze frecvența de 77,5 kHz, îl putem folosi și pe acesta.
Desigur, recepționând semnalele de radio ale timpului exact prin Internet, nu vom obține cu adevărat ora exactă — semnalul este transmis cu o întârziere. Dar scopul nostru este doar să înțelegem structura semnalului; pentru aceasta, înregistrările de pe Internet sunt mai mult decât suficiente. În realitate, desigur, sunt utilizate dispozitive specializate pentru recepție și decodare, despre care se va vorbi mai jos.
Așadar, am obținut înregistrarea, să începem procesarea ei.
Decodificarea semnalului
Vom încărca fișierul folosind Python și vom examina structura sa:
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()
Observăm o modulare de amplitudine tipică:

Pentru a simplifica decodarea, vom lua în considerare anvelopa semnalului utilizând transformarea Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000]) Rezultatul în mărire:

Vom netezi erorile cauzate de interferențe folosind un filtru low-pass, în același timp vom calcula media, care va fi utilă ulterior pentru parsare.
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 Rezultatul (linia galbenă): semnal practic dreptunghiular, care este destul de ușor de analizat.

Parsing
Mai întâi, trebuie să obținem secvența de biți. Structura semnalului în sine este foarte simplă.

Impulsurile sunt împărțite în intervale de o secundă. Dacă distanța dintre impulsuri este de 0.1s (deci lungimea impulsului este de 0.9s), adăugăm „0” la secvența de biți, dacă distanța este de 0.2s (deci lungimea este de 0.8s), adăugăm „1”. Sfârșitul fiecărei minute este marcat de un impuls „lung”, de 2s, iar secvența de biți este resetată, și umplerea începe din nou.
Ceea ce este scris mai sus nu este greu de implementat în Python.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] avg:
# Început semnal
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Sfârșit semnal
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
Ca rezultat, obținem o secvență de biți, în cazul nostru pentru două secunde arată astfel:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
De fapt, este interesant că semnalul conține și un „al doilea strat” de date. Secvența de biți este, de asemenea, codificată prin . Teoretic, aceasta ar trebui să asigure o decodare mai robustă chiar și în cazul unui semnal slăbit.
Ultimul nostru pas: a obține datele propriu-zise. Biții sunt transmiși o dată pe secundă, așa că avem în total 59 de biți, în care este codificată destul de multă informație:

Biții sunt descriși în , și sunt destul de curioase. Primele 15 biți nu sunt folosiți, deși au existat planuri de utilizare pentru sistemele de alarmă și apărare civilă. Bitul A1 indică faptul că, în urmora oră, ceasul va fi setat pe ora de vară. Bitul A2 indică faptul că, în următoarea oră, va fi adăugată , care este uneori folosită pentru a corecta timpul în funcție de rotația Pământului. Ceilalți biți codifică orele, minutele, secunde și data.

Pentru cei care doresc să experimenteze singuri, codul pentru decodare este oferit sub spoiler.
Codul sursă
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 = ('Duminică', 'Luni', 'Marți', 'Miercuri', 'Joi', 'Vineri', 'Sâmbătă', 'Duminică')
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)
Rulând programul, vom vedea aproximativ această ieșire:
0011110110111000001011000001010000100110010101100010011000
Marți, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Marți, 26.03.19, 21:42
Ei bine, cam asta este toată magia. Avantajul acestui sistem este că decodarea este extrem de simplă și poate fi realizată pe orice microcontroler simplu. Pur și simplu numărăm lungimea impulsurilor, acumulăm 60 de biți și, la sfârșitul fiecărei minute, obținem timpul exact. Comparativ cu alte metode de sincronizare a timpului (de exemplu, GPS sau, Doamne ferește, Internet:), această sincronizare radio practic nu necesită energie electrică — ca exemplu, o stație meteorologică obișnuită funcționează aproximativ un an cu 2 baterii AA. De aceea, sincronizarea radio este utilizată chiar și în ceasurile de mână, iar cu atât mai mult în ceasurile de perete sau la stațiile de tren.
Conveniența și simplitatea DCF atrag și iubitorii de DIY. Doar cu 10-20$ poți cumpăra un modul gata făcut din antenă cu un receptor gata și ieșire TTL, care poate fi conectat la Arduino sau alt controler.

Pentru Arduino, deja au fost scrise și Cu toate acestea, este deja cunoscut — indiferent ce faci pe un microcontroler, obții fie un ceas, fie o stație meteorologică. Cu un astfel de dispozitiv, obținerea timpului exact nu este deloc complicată, dacă, bineînțeles, te afli în zona de recepție. Și poți să agăți pe ceas o inscripție „Ceas Atomic” și, în același timp, să explici tuturor doritorilor că dispozitivul se sincronizează cu ajutorul ceasurilor atomice.
Cei doritori pot chiar să îmbunătățească ceasurile vechi ale bunicii, instalând în ele un nou mecanism cu radiotranziție:

Un astfel de mecanism poate fi găsit pe ebay căutând după cuvintele cheie „Radio Controlled Movement”.
Și, în sfârșit, un truc pentru cei care au citit până aici. Chiar dacă în următoarele câteva mii de km nu există niciun emițător de semnal radio, un astfel de semnal poate fi generat ușor de unul singur. În Google Play există o aplicație numită „DCF77 Emulator”, care emite un semnal prin căști. Potrivit autorului, dacă înfășori cablul căștilor în jurul ceasului, acesta va capta semnalul (interesant cum, având în vedere că căștile obișnuite nu vor emite un semnal de 77 kHz, dar probabil receptarea se face prin armonici). La mine pe Android 9, aplicația nu a funcționat deloc — pur și simplu nu a avut sunet (sau poate că nu l-am auzit — 77 kHz, totuși :), dar poate altcuiva îi va fi mai bine. Unii, de altfel, își construiesc și un generator complet de semnale DCF, care nu este greu de realizat pe același Arduino sau ESP32:

(sursa )
Concluzie
Sistemul DCF s-a dovedit a fi într-adevăr destul de simplu și convenabil. Cu ajutorul unui receptor simplu și ieftin, se poate avea timpul exact întotdeauna și pretutindeni, desigur, în zona de recepție. Se crede că, chiar și în ciuda digitalizării răspândite și a „internetului lucrurilor”, astfel de soluții simple vor fi solicitate încă mult timp.
Sursa: habr.com
