Hei Habr.
Todennäköisesti monet kellon tai sääaseman ostajat ovat nähneet pakkauksessa radio-ohjattavan kellon tai jopa atomikellon logon. Tämä on erittäin kätevää, koska sinun tarvitsee vain laittaa kello pöydälle, ja hetken kuluttua se mukautuu automaattisesti tarkkaan aikaan.
Selvitetään kuinka se toimii ja kirjoitetaan dekooderi Pythonissa.
Aikasynkronointijärjestelmiä on erilaisia. Suosituin Euroopassa on saksalainen järjestelmä
Kaikki alla kirjoitettu koskee DCF77:ää.
Signaalin vastaanotto
DCF77 on pitkän aallon asema, joka toimii 77.5 kHz:n taajuudella ja lähettää signaaleja amplitudimodulaatiolla. 50 kW:n asema sijaitsee 25 km:n päässä Frankfurtista, se aloitti toimintansa vuonna 1959, ja vuonna 1973 tarkkaan aikaan lisättiin päivämäärätiedot. Aallonpituus 77 KHz:n taajuudella on erittäin pitkä, joten antennikentän mitat ovat myös melko kunnolliset (kuva Wikipediasta):
Tällaisella antennilla ja tehotulolla vastaanottoalue kattaa lähes koko Euroopan, Valko-Venäjän, Ukrainan ja osan Venäjää.
Kuka tahansa voi tallentaa signaalin. Voit tehdä tämän siirtymällä online-vastaanottimeen
Siellä painamme latauspainiketta ja tallennamme muutaman minuutin pituisen fragmentin. Tietenkin, jos sinulla on "oikea" vastaanotin, joka pystyy tallentamaan 77.5 kHz:n taajuuden, voit käyttää sitä.
Vastaanottamalla radioaikasignaaleja Internetin kautta emme tietenkään saa todella tarkkaa aikaa - signaali lähetetään viiveellä. Mutta tavoitteemme on vain ymmärtää signaalin rakenne; tähän Internet-tallennus on enemmän kuin tarpeeksi. Todellisessa elämässä tietysti käytetään erikoislaitteita vastaanottamiseen ja dekoodaukseen; niitä käsitellään alla.
Joten, olemme vastaanottaneet tallenteen, aloitetaan sen käsittely.
Signaalin dekoodaus
Ladataan tiedosto Pythonilla ja katsotaan sen rakenne:
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äemme tyypillisen amplitudimodulaation:
Dekoodauksen yksinkertaistamiseksi otetaan signaalin verhokäyrä Hilbert-muunnoksen avulla:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Laajennettu tulos:
Tasoitetaan melupäästöjä alipäästösuodattimella ja lasketaan samalla keskiarvo, josta on hyötyä myöhemmin jäsennyksessä.
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
Tulos (keltainen viiva): melkein neliöaaltosignaali, joka on melko helppo analysoida.
Jäsentäminen
Ensin sinun on hankittava bittisekvenssi. Itse signaalin rakenne on hyvin yksinkertainen.
Pulssit on jaettu kakkosjaksoihin. Jos pulssien välinen etäisyys on 0.1 s (eli itse pulssin pituus on 0.9 s), lisää bittijonoon "0"; jos etäisyys on 0.2 s (eli pituus on 0.8 s), lisää "1". Jokaisen minuutin päättymisen ilmaisee "pitkä" pulssi, 2s pitkä, bittisekvenssi nollataan ja täyttö alkaa uudelleen.
Yllä oleva on helppo kirjoittaa Pythonilla.
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
Tämän seurauksena saamme bittisarjan, esimerkissämme kahden sekunnin ajan se näyttää tältä:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Muuten, on mielenkiintoista, että signaalilla on myös "toinen kerros" dataa. Bittisekvenssi koodataan myös käyttämällä
Viimeinen vaiheemme: todellisten tietojen saaminen. Bittejä lähetetään kerran sekunnissa, joten meillä on yhteensä 59 bittiä, joihin on koodattu melko paljon tietoa:
Bitit on kuvattu kohdassa
Niille, jotka haluavat kokeilla itse, dekoodauskoodi annetaan spoilerin alla.
Lähdekoodi
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)
Kun suoritamme ohjelman, näemme tämän kaltaisen tulosteen:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Itse asiassa se on kaikki taika. Tällaisen järjestelmän etuna on, että dekoodaus on erittäin yksinkertaista ja se voidaan tehdä millä tahansa, jopa yksinkertaisimmalla mikro-ohjaimella. Laskemme yksinkertaisesti pulssien pituuden, keräämme 60 bittiä ja jokaisen minuutin lopussa saamme tarkan ajan. Verrattuna muihin aikasynkronointimenetelmiin (esim. GPS tai varjelkoon Internet:), tällainen radiosynkronointi ei vaadi käytännössä sähköä - esimerkiksi tavallinen kotisääasema toimii noin vuoden kahdella AA-paristolla. Siksi jopa rannekellot valmistetaan radiosynkronoinnilla, puhumattakaan tietysti seinäkelloista tai katuasemakelloista.
DCF:n mukavuus ja yksinkertaisuus houkuttelevat myös tee-se-itse-harrastajia. Vain 10-20 dollarilla voit ostaa valmiin antennimoduulin valmiilla vastaanottimella ja TTL-lähdöllä, joka voidaan liittää Arduinoon tai muuhun ohjaimeen.
Kirjoitettu jo Arduinolle
Halukkaat voivat jopa päivittää vanhan isoäitikellonsa asentamalla uuden mekanismin radiosynkronoinnilla:
Löydät sellaisen eBaysta avainsanoilla "Radio Controlled Movement".
Ja lopuksi elämänhack niille, jotka ovat lukeneet tähän asti. Vaikka seuraavan parin tuhannen km:n sisällä ei olisi yhtään radiosignaalilähetintä, sellaisen signaalin luominen itse ei ole vaikeaa. Google Playssa on ohjelma nimeltä "DCF77 Emulator", joka lähettää signaalin kuulokkeisiin. Kirjoittajan mukaan, jos kiedät kuulokkeiden langan kellon ympärille, ne poimivat signaalin (mielenkiintoista miten, koska tavalliset kuulokkeet eivät tuota 77KHz signaalia, mutta vastaanotto johtuu luultavasti harmonisista). Android 9:ssä ohjelma ei toiminut ollenkaan minulle - ääntä ei yksinkertaisesti kuulunut (tai ehkä en kuullut sitä - se on loppujen lopuksi 77KHz:), mutta ehkä jollain on parempi onni. Jotkut kuitenkin tekevät itsestään täysimittaisen DCF-signaaligeneraattorin, joka on helppo tehdä samalla Arduinolla tai ESP32:lla:
(lähde
Johtopäätös
DCF-järjestelmä osoittautui todella yksinkertaiseksi ja käteväksi. Yksinkertaisen ja halvan vastaanottimen avulla saat tarkan ajan aina ja kaikkialla, tietysti vastaanottoalueella. Vaikuttaa siltä, että laajasta digitalisaatiosta ja esineiden internetistä huolimatta tällaisille yksinkertaisille ratkaisuille on kysyntää vielä pitkään.
Lähde: will.com