Salut Habr.
Probabil că mulți dintre cei care achiziționează un ceas sau o stație meteo au văzut pe ambalaj ceasul controlat radio sau chiar sigla ceasului atomic. Acest lucru este foarte convenabil, deoarece trebuie doar să puneți ceasul pe masă și, după un timp, se va ajusta automat la ora exactă.
Să ne dăm seama cum funcționează și să scriem un decodor în Python.
Există diferite sisteme de sincronizare a timpului. Cel mai popular în Europa este sistemul german
Tot ce este scris mai jos va fi despre DCF77.
Recepția semnalului
DCF77 este o stație cu undă lungă care funcționează la o frecvență de 77.5 kHz și transmite semnale în modulație de amplitudine. Stația de 50KW este situată la 25 km de Frankfurt, a început să funcționeze în 1959, iar în 1973 au fost adăugate informații despre data exactă. Lungimea de undă la o frecvență de 77 KHz este foarte mare, așa că dimensiunile câmpului antenei sunt și ele destul de decente (foto de pe Wikipedia):
Cu o astfel de antenă și putere de intrare, zona de recepție acoperă aproape toată Europa, Belarus, Ucraina și o parte a Rusiei.
Oricine poate înregistra un semnal. Pentru a face acest lucru, trebuie doar să accesați receptorul online
Acolo apăsăm butonul de descărcare și înregistrăm un fragment de câteva minute. Desigur, dacă aveți un receptor „adevărat” capabil să înregistreze frecvența de 77.5 KHz, îl puteți folosi.
Desigur, primind semnale de timp radio prin Internet, nu vom primi ora cu adevărat exactă - semnalul este transmis cu întârziere. Dar scopul nostru este doar să înțelegem structura semnalului; pentru aceasta, înregistrarea pe Internet este mai mult decât suficientă. În viața reală, desigur, dispozitivele specializate sunt folosite pentru recepție și decodare; acestea vor fi discutate mai jos.
Deci, am primit înregistrarea, să începem să o procesăm.
Decodificarea semnalului
Să încărcăm fișierul folosind Python și să vedem structura acestuia:
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()
Vedem modulația tipică de amplitudine:
Pentru a simplifica decodarea, să luăm anvelopa semnalului folosind transformarea Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Rezultat mărit:
Să netezim emisiile de zgomot folosind un filtru trece-jos și, în același timp, să calculăm valoarea medie, care va fi utilă mai târziu pentru analiza.
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
Rezultat (linia galbenă): un semnal de undă aproape pătrată care este destul de ușor de analizat.
Analizare
Mai întâi trebuie să obțineți secvența de biți. Structura semnalului în sine este foarte simplă.
Pulsurile sunt împărțite în intervale secunde. Dacă distanța dintre impulsuri este de 0.1 s (adică lungimea impulsului în sine este de 0.9 s), adăugați „0” la secvența de biți; dacă distanța este de 0.2 s (adică lungimea este de 0.8 s), adăugați „1”. Sfârșitul fiecărui minut este indicat printr-un impuls „lung”, de 2 secunde, secvența de biți este resetată la zero și umplerea începe din nou.
Cele de mai sus sunt ușor de scris în Python.
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
Ca rezultat, obținem o secvență de biți, în exemplul nostru pentru două secunde arată astfel:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Apropo, este interesant că semnalul are și un „al doilea strat” de date. Secvența de biți este, de asemenea, codificată folosind
Ultimul nostru pas: obținerea datelor reale. Biții sunt transmisi o dată pe secundă, deci avem un total de 59 de biți, în care sunt codificate destul de multe informații:
Biții sunt descriși în
Pentru cei care doresc să experimenteze singuri, codul de decodare este dat sub spoiler.
Cod 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 = ('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)
Când rulăm programul, vom vedea rezultate similare cu aceasta:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
De fapt, asta e toată magia. Avantajul unui astfel de sistem este că decodarea este extrem de simplă și se poate face pe orice, chiar și pe cel mai simplu microcontroler. Pur și simplu numărăm lungimea impulsurilor, acumulăm 60 de biți și la sfârșitul fiecărui minut obținem timpul exact. În comparație cu alte metode de sincronizare a orei (GPS, de exemplu, sau Doamne ferește, Internet:), o astfel de sincronizare radio nu necesită practic electricitate - de exemplu, o stație meteo obișnuită de acasă funcționează aproximativ un an cu 2 baterii AA. Prin urmare, chiar și ceasurile de mână sunt realizate cu sincronizare radio, ca să nu mai vorbim, bineînțeles, de ceasurile de perete sau ceasurile de stație stradală.
Comoditatea și simplitatea DCF atrag, de asemenea, pasionații de bricolaj. Pentru doar 10-20 USD puteți cumpăra un modul de antenă gata făcut cu un receptor gata făcut și ieșire TTL, care poate fi conectat la un Arduino sau alt controler.
Deja scris pentru Arduino
Cei care doresc pot chiar upgrade ceasul vechiului bunicii lor instalând un nou mecanism cu sincronizare radio:
Puteți găsi unul pe ebay folosind cuvintele cheie „Mișcare controlată radio”.
Și în sfârșit, un truc de viață pentru cei care au citit până aici. Chiar dacă nu există un singur transmițător de semnal radio în următoarele două mii de km, nu este dificil să generați singur un astfel de semnal. Există un program pe Google Play numit „DCF77 Emulator” care transmite semnalul către căști. Potrivit autorului, dacă înfășurați firul căștilor în jurul ceasului, acestea vor capta semnalul (este interesant cum, deoarece căștile obișnuite nu vor produce un semnal de 77KHz, dar recepția se datorează probabil armonicilor). Pe Android 9, programul nu a funcționat deloc pentru mine - pur și simplu nu era niciun sunet (sau poate nu l-am auzit - este 77KHz, la urma urmei:), dar poate cineva va avea mai mult noroc. Unii, totuși, se fac un generator de semnal DCF cu drepturi depline, care este ușor de realizat pe același Arduino sau ESP32:
(o sursă
Concluzie
Sistemul DCF sa dovedit a fi destul de simplu și convenabil. Cu ajutorul unui receptor simplu si ieftin, poti avea ora exacta mereu si oriunde, bineinteles in zona receptiei. Se pare că, chiar și în ciuda digitalizării pe scară largă și a internetului obiectelor, astfel de soluții simple vor fi solicitate mult timp.
Sursa: www.habr.com