Tere Habr.
Tõenäoliselt on paljud kella või ilmajaama soetajad näinud pakendil raadio teel juhitavat kella või isegi aatomikella logo. See on väga mugav, sest peate lihtsalt kella lauale panema ja mõne aja pärast kohandub see automaatselt täpse kellaajaga.
Mõelgem välja, kuidas see töötab, ja kirjutame Pythonis dekoodri.
Aja sünkroonimise süsteeme on erinevaid. Euroopas on kõige populaarsem Saksa süsteem
Kõik allpool kirjutatud puudutab DCF77.
Signaali vastuvõtt
DCF77 on pikalainejaam, mis töötab sagedusel 77.5 kHz ja edastab signaale amplituudmodulatsioonis. 50KW jaam asub Frankfurdist 25 km kaugusel, see alustas tööd 1959. aastal ning 1973. aastal lisati täpsele kellaajale ka kuupäevainfo. Lainepikkus 77 KHz sagedusel on väga pikk, seega on ka antennivälja mõõtmed üsna korralikud (foto Wikipediast):
Sellise antenni ja toitesisendiga katab vastuvõtuala peaaegu kogu Euroopa, Valgevene, Ukraina ja osa Venemaast.
Igaüks saab signaali salvestada. Selleks minge lihtsalt võrgus olevale vastuvõtjale
Seal vajutame allalaadimisnuppu ja salvestame mitme minuti pikkuse fragmendi. Muidugi, kui teil on "päris" vastuvõtja, mis suudab salvestada 77.5 kHz sagedust, saate seda kasutada.
Interneti kaudu raadioajasignaale vastu võttes ei saa me muidugi päris täpset aega – signaal edastatakse hilinemisega. Kuid meie eesmärk on mõista ainult signaali struktuuri, selleks on Interneti-salvestusest enam kui piisav. Reaalses elus kasutatakse vastuvõtmiseks ja dekodeerimiseks muidugi spetsiaalseid seadmeid, neid käsitletakse allpool.
Niisiis, oleme salvestise kätte saanud, hakkame seda töötlema.
Signaali dekodeerimine
Laadime faili Pythoni abil ja vaatame selle struktuuri:
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()
Näeme tüüpilist amplituudmodulatsiooni:
Dekodeerimise lihtsustamiseks võtame signaali mähisjoone Hilberti teisenduse abil:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Suurendatud tulemus:
Silume madalpääsfiltri abil müraemissioone ja arvutame samal ajal keskmise väärtuse, millest on hiljem parsimisel kasu.
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
Tulemus (kollane joon): peaaegu ruutlaine signaal, mida on üsna lihtne analüüsida.
Parsimine
Kõigepealt peate saama bitijärjestuse. Signaali struktuur ise on väga lihtne.
Impulssid on jagatud teiseks intervalliks. Kui impulsside vaheline kaugus on 0.1 s (st impulsi enda pikkus on 0.9 s), lisage bitijadale "0", kui kaugus on 0.2 s (st pikkus on 0.8 s), lisage "1". Iga minuti lõppu näitab 2 sekundi pikkune “pikk” impulss, bitijada lähtestatakse nullile ja täitmine algab uuesti.
Ülaltoodud on Pythonis lihtne kirjutada.
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
Selle tulemusel saame bittide jada, meie näites näeb see kahe sekundi jooksul välja järgmine:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Muide, on huvitav, et signaalil on ka "teine kiht". Bitijada kodeeritakse ka kasutades
Meie viimane samm: tegelike andmete hankimine. Bite edastatakse üks kord sekundis, seega on meil kokku 59 bitti, millesse on kodeeritud üsna palju teavet:
Bite on kirjeldatud artiklis
Neile, kes soovivad omal käel katsetada, on dekodeerimiskood antud spoileri all.
Lähtekood
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)
Kui käivitame programmi, näeme väljundit, mis on sarnane sellele:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Tegelikult on see kõik maagia. Sellise süsteemi eeliseks on see, et dekodeerimine on ülilihtne ja seda saab teha mis tahes, isegi kõige lihtsama mikrokontrolleriga. Lihtsalt loendame impulsside pikkuse, kogume 60 bitti ja iga minuti lõpus saame täpse aja. Võrreldes teiste aja sünkroniseerimise meetoditega (GPS näiteks või jumal hoidku Internet:), ei vaja selline raadiosünkroniseerimine praktiliselt üldse elektrit – näiteks tavaline kodune ilmajaam töötab umbes aasta 2 AA patareiga. Seetõttu tehakse raadiosünkroniseerimisega isegi käekellasid, rääkimata muidugi seina- või tänavajaamakelladest.
DCF-i mugavus ja lihtsus köidavad ka isetegemise entusiaste. Vaid 10-20 dollari eest saab osta valmis vastuvõtja ja TTL väljundiga valmis antennimooduli, mille saab ühendada Arduino või muu kontrolleriga.
Arduino jaoks juba kirjutatud
Soovijad saavad oma vana vanaema käekella isegi uuendada, paigaldades uue raadiosünkroniseerimisega mehhanismi:
Selle leiate ebayst märksõnade "Radio Controlled Movement" abil.
Ja lõpetuseks elulugu neile, kes on siiani lugenud. Isegi kui lähema paari tuhande km jooksul pole ainsatki raadiosignaali saatjat, pole sellist signaali ise keeruline tekitada. Google Plays on programm nimega "DCF77 Emulator", mis väljastab signaali kõrvaklappidesse. Autori sõnul võtavad kõrvaklappide juhe ümber kella kinni (huvitav, kuidas, sest tavalised kõrvaklapid ei tooda 77KHz signaali, kuid vastuvõtt on tõenäoliselt tingitud harmoonilistest). Android 9 puhul ei töötanud programm minu jaoks üldse - heli lihtsalt polnud (või võib-olla ma ei kuulnud seda - see on ju 77KHz:), aga võib-olla läheb kellelgi paremini õnne. Mõned aga teevad end täisväärtuslikuks DCF-signaaligeneraatoriks, mida on lihtne teha samal Arduino või ESP32 peal:
(allikas
Järeldus
DCF-süsteem osutus tõesti üsna lihtsaks ja mugavaks. Lihtsa ja soodsa ressiiveri abil saad alati ja igal pool täpse kellaaja, loomulikult vastuvõtualal. Tundub, et isegi laialt levinud digitaliseerimisest ja asjade internetist hoolimata on selliste lihtsate lahenduste järele nõudlus veel kaua.
Allikas: www.habr.com