Zdravo Habr.
Vjerovatno su mnogi koji kupuju sat ili meteorološku stanicu vidjeli radio kontrolirani sat ili čak logotip atomskog sata na pakovanju. Ovo je vrlo zgodno, jer samo treba da stavite sat na sto, a nakon nekog vremena on će se automatski prilagoditi tačnom vremenu.
Hajde da shvatimo kako to radi i napišemo dekoder u Pythonu.
Postoje različiti sistemi za sinhronizaciju vremena. Najpopularniji u Evropi je njemački sistem
Sve napisano u nastavku bit će o DCF77.
Prijem signala
DCF77 je dugovalna stanica koja radi na frekvenciji od 77.5 kHz i emituje signale u amplitudnoj modulaciji. Stanica od 50KW nalazi se 25 km od Frankfurta, počela je sa radom 1959. godine, a 1973. godine dodana je informacija o tačnom vremenu. Talasna dužina na frekvenciji od 77 KHz je veoma duga, tako da su i dimenzije antenskog polja sasvim pristojne (fotografija sa Wikipedije):
Sa takvom antenom i ulazom za napajanje, prijemno područje pokriva gotovo cijelu Evropu, Bjelorusiju, Ukrajinu i dio Rusije.
Svako može snimiti signal. Da biste to učinili, samo idite na mrežni prijemnik
Tamo pritisnemo dugme za preuzimanje i snimimo fragment dug nekoliko minuta. Naravno, ako imate “pravi” prijemnik koji može snimiti frekvenciju od 77.5 KHz, možete to koristiti.
Naravno, primanjem radio signala vremena putem interneta, nećemo dobiti istinski tačno vrijeme - signal se prenosi sa zakašnjenjem. Ali naš cilj je samo razumjeti strukturu signala, za to je više nego dovoljan internet snimak. U stvarnom životu, naravno, za prijem i dekodiranje koriste se specijalizirani uređaji, o njima će biti riječi u nastavku.
Dakle, primili smo snimak, hajde da ga obradimo.
Signal Decoding
Učitajmo datoteku koristeći Python i vidimo njenu strukturu:
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čnu amplitudnu modulaciju:
Da pojednostavimo dekodiranje, uzmimo omotnicu signala koristeći Hilbertovu transformaciju:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Uvećani rezultat:
Hajde da izgladimo emisije buke pomoću niskopropusnog filtera, a u isto vrijeme izračunamo prosječnu vrijednost, što će nam kasnije biti od koristi za raščlanjivanje.
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 (žuta linija): signal gotovo pravokutnog talasa koji je prilično lako analizirati.
Parsing
Prvo morate dobiti sekvencu bitova. Sama struktura signala je vrlo jednostavna.
Impulsi su podijeljeni u sekundarne intervale. Ako je razmak između impulsa 0.1s (tj. dužina samog impulsa je 0.9s), dodajte "0" sekvenci bitova; ako je udaljenost 0.2s (tj. dužina je 0.8s), dodajte "1". Kraj svake minute je označen “dugim” impulsom, dugim 2s, sekvenca bitova se resetuje na nulu i punjenje počinje ponovo.
Gore je lako napisati u 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
Kao rezultat, dobijamo niz bitova, u našem primjeru za dvije sekunde to izgleda ovako:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Inače, zanimljivo je da signal ima i "drugi sloj" podataka. Slijed bitova je također kodiran pomoću
Naš posljednji korak: dobivanje stvarnih podataka. Bitovi se prenose jednom u sekundi, tako da imamo ukupno 59 bitova, u kojima je kodirano dosta informacija:
Bitovi su opisani u
Za one koji žele sami eksperimentirati, kod za dekodiranje je dat ispod spojlera.
Izvor
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)
Kada pokrenemo program, vidjet ćemo izlaz sličan ovome:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Zapravo, to je sva magija. Prednost ovakvog sistema je što je dekodiranje izuzetno jednostavno i može se obaviti na bilo kom, pa i najjednostavnijem mikrokontroleru. Jednostavno brojimo dužinu impulsa, akumuliramo 60 bita i na kraju svake minute dobijamo tačno vrijeme. U poređenju sa drugim metodama vremenske sinhronizacije (GPS, na primer, ili ne daj Bože, Internet:), za takvu radio sinhronizaciju nije potrebna praktički nikakva struja - na primer, obična kućna meteorološka stanica radi oko godinu dana na 2 AA baterije. Stoga se čak i ručni satovi prave sa radio sinhronizacijom, a da ne spominjemo, naravno, zidne satove ili satove uličnih stanica.
Pogodnost i jednostavnost DCF-a također privlače DIY entuzijaste. Za samo 10-20 dolara možete kupiti gotov antenski modul sa gotovim prijemnikom i TTL izlazom, koji se može spojiti na Arduino ili drugi kontroler.
Već napisano za Arduino
Oni koji žele mogu čak i nadograditi stari bakin sat ugradnjom novog mehanizma sa radio sinhronizacijom:
Možete ga pronaći na ebayu koristeći ključne riječi “Radio Controlled Movement”.
I na kraju, lajf hak za one koji su čitali dovde. Čak i ako u sljedećih nekoliko hiljada km nema niti jednog odašiljača radio signala, nije teško sami generirati takav signal. Na Google Play-u postoji program pod nazivom “DCF77 Emulator” koji šalje signal u slušalice. Prema autoru, ako omotate žicu slušalica oko sata, one će uhvatiti signal (zanimljivo je kako, jer obične slušalice neće proizvoditi 77KHz signal, već je prijem vjerovatno zbog harmonika). Na Androidu 9 meni program uopće nije radio - jednostavno nije bilo zvuka (ili ga možda nisam čuo - ipak je 77KHz :)), ali možda će neko imati više sreće. Neki, međutim, prave sebi potpuni generator DCF signala, koji je lako napraviti na istom Arduinu ili ESP32:
(izvor
zaključak
Pokazalo se da je DCF sistem zaista prilično jednostavan i praktičan. Uz pomoć jednostavnog i jeftinog prijemnika možete imati tačno vrijeme uvijek i svugdje, naravno na recepciji. Čini se da će čak i uprkos raširenoj digitalizaciji i Internetu stvari, ovakva jednostavna rješenja biti tražena još dugo.
izvor: www.habr.com