Pozdravljeni Habr.
Verjetno je marsikdo, ki je kupil uro ali vremensko postajo, na embalaži videl logotip radijsko vodene ure ali celo atomske ure. To je zelo priročno, saj morate samo postaviti uro na mizo in čez nekaj časa se bo samodejno prilagodila točnemu času.
Ugotovimo, kako deluje, in napišimo dekoder v Pythonu.
Obstajajo različni sistemi za sinhronizacijo časa. V Evropi je najbolj priljubljen nemški sistem
Vse spodaj napisano bo o DCF77.
Sprejem signala
DCF77 je dolgovalovna postaja, ki deluje na frekvenci 77.5 kHz in oddaja signale v amplitudni modulaciji. Postaja 50KW se nahaja 25 km od Frankfurta, obratovati je začela leta 1959, leta 1973 pa so točnemu času dodali datumski podatek. Valovna dolžina pri frekvenci 77 KHz je zelo dolga, zato so tudi dimenzije antenskega polja precej spodobne (fotografija iz Wikipedije):
S takšno anteno in vhodno močjo sprejemno območje pokriva skoraj vso Evropo, Belorusijo, Ukrajino in del Rusije.
Vsak lahko posname signal. Če želite to narediti, pojdite na spletni sprejemnik
Tam pritisnemo gumb za prenos in posnamemo nekaj minut dolg fragment. Seveda, če imate "pravi" sprejemnik, ki lahko snema frekvenco 77.5 KHz, ga lahko uporabite.
Seveda s sprejemanjem radijskih časovnih signalov prek interneta ne bomo prejeli zares točnega časa - signal se prenaša z zamikom. Toda naš cilj je le razumeti strukturo signala, za to pa je internetni posnetek več kot dovolj. V resničnem življenju se za sprejem in dekodiranje seveda uporabljajo specializirane naprave, o katerih bomo razpravljali v nadaljevanju.
Tako, posnetek smo prejeli, začnimo z obdelavo.
Dekodiranje signala
Naložimo datoteko s Pythonom in si oglejmo njeno strukturo:
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()
Vidimo tipično amplitudno modulacijo:
Za poenostavitev dekodiranja vzemimo ovojnico signala s Hilbertovo transformacijo:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Povečan rezultat:
Zgladimo emisije hrupa z uporabo nizkopasovnega filtra in hkrati izračunajmo povprečno vrednost, ki bo kasneje uporabna pri razčlenjevanju.
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 (rumena črta): skoraj pravokotni signal, ki ga je precej enostavno analizirati.
Razčlenjevanje
Najprej morate dobiti zaporedje bitov. Sama struktura signala je zelo preprosta.
Impulzi so razdeljeni na sekundne intervale. Če je razdalja med impulzi 0.1 s (tj. dolžina samega impulza je 0.9 s), dodajte »0« zaporedju bitov; če je razdalja 0.2 s (tj. dolžina je 0.8 s), dodajte »1«. Konec vsake minute je označen z "dolgim" impulzom, dolgim 2 s, zaporedje bitov se ponastavi na nič in polnjenje se začne znova.
Zgoraj je enostavno napisati v Pythonu.
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
Kot rezultat dobimo zaporedje bitov, v našem primeru za dve sekundi izgleda takole:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Mimogrede, zanimivo je, da ima signal tudi "drugo plast" podatkov. Bitno zaporedje je tudi kodirano z uporabo
Naš zadnji korak: pridobivanje dejanskih podatkov. Biti se prenašajo enkrat na sekundo, tako da imamo skupno 59 bitov, v katerih je zakodiranih kar nekaj informacij:
Biti so opisani v
Za tiste, ki želijo eksperimentirati sami, je koda za dekodiranje navedena pod spojlerjem.
Izvorna koda
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)
Ko zaženemo program, bomo videli izpis, podoben temu:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Pravzaprav je to vsa čarovnija. Prednost takega sistema je v tem, da je dekodiranje izredno preprosto in ga lahko izvedemo na vsakem, še tako preprostem mikrokrmilniku. Preprosto preštejemo dolžino impulzov, zberemo 60 bitov in na koncu vsake minute dobimo točen čas. V primerjavi z drugimi načini časovne sinhronizacije (GPS, na primer, ali bog ne daj, internet:), takšna radijska sinhronizacija ne potrebuje skoraj nič elektrike - običajna domača vremenska postaja na primer deluje približno eno leto na 2 AA bateriji. Zato so tudi ročne ure izdelane z radijsko sinhronizacijo, da seveda ne omenjamo stenskih ur ali uličnih postajnih ur.
Priročnost in preprostost DCF pritegneta tudi ljubitelje DIY. Za samo 10-20 $ lahko kupite že pripravljen antenski modul z že pripravljenim sprejemnikom in TTL izhodom, ki ga lahko povežete z Arduino ali drugim krmilnikom.
Že napisano za Arduino
Kdor želi, lahko svojo staro babičino uro celo nadgradi z namestitvijo novega mehanizma z radijsko sinhronizacijo:
Enega lahko najdete na ebayu s ključnimi besedami "Radio Controlled Movement".
In končno, življenjski trik za tiste, ki so prebrali do tukaj. Tudi če v naslednjih nekaj tisoč km ni nobenega oddajnika radijskega signala, takšnega signala ni težko ustvariti sam. V Googlu Play obstaja program, imenovan »DCF77 Emulator«, ki oddaja signal v slušalke. Po besedah avtorja, če žico slušalk ovijete okoli ure, bodo le-te pobrale signal (zanimivo, kako, saj navadne slušalke ne bodo proizvedle 77KHz signala, ampak je sprejem verjetno posledica harmonikov). Na Androidu 9 mi program sploh ni delal - enostavno ni bilo zvoka (ali pa ga morda nisem slišal - navsezadnje je 77KHz :), mogoče pa bo imel kdo več sreče. Nekateri pa si naredijo polnopravni generator signala DCF, ki ga je enostavno narediti na istem Arduinu ali ESP32:
(vir
Zaključek
Sistem DCF se je izkazal za zelo preprostega in priročnega. S pomočjo enostavnega in poceni sprejemnika imate točen čas vedno in povsod, seveda v recepciji. Zdi se, da bo kljub vsesplošni digitalizaciji in internetu stvari po takih preprostih rešitvah še dolgo povpraševanje.
Vir: www.habr.com