Hei Habr.
Sannsynligvis har mange som kjøper en klokke eller en værstasjon sett radiokontrollert klokke eller til og med Atomic Clock-logoen på emballasjen. Dette er veldig praktisk, fordi det er nok å sette klokken på bordet, og etter en stund vil den automatisk justere seg til den nøyaktige tiden.
La oss finne ut hvordan det fungerer og skrive en dekoder i Python.
Det finnes forskjellige tidssynkroniseringssystemer. Det mest populære i Europa er det tyske systemet
Alt som er skrevet nedenfor vil handle om DCF77.
Signalmottak
DCF77 er en langbølgestasjon som opererer ved 77.5KHz og sender AM-signaler. Stasjonen med en kapasitet på 50 kW ligger 25 km fra Frankfurt, den begynte å jobbe i 1959, i 1973 ble informasjon om datoen lagt til det nøyaktige klokkeslettet. Bølgelengden ved en frekvens på 77 kHz er veldig stor, så dimensjonene til antennefeltet er også veldig anstendige (foto fra Wikipedia):
Med en slik antenne og inngangskraft dekker mottaksområdet nesten hele Europa, Hviterussland, Ukraina og deler av Russland.
Hvem som helst kan ta opp. For å gjøre dette, bare gå til nettmottakeren
På samme sted trykker vi på nedlastingsknappen og tar opp et fragment som er noen minutter langt. Selvfølgelig, hvis du har en "ekte" mottaker som er i stand til å ta opp en frekvens på 77.5 kHz, kan du bruke den.
Når vi mottar nøyaktige tidsradiosignaler over Internett, vil vi selvfølgelig ikke få en virkelig nøyaktig tid - signalet overføres med en forsinkelse. Men målet vårt er bare å forstå strukturen til signalet, for dette er internettopptaket mer enn nok. I det virkelige liv brukes selvfølgelig spesialiserte enheter for mottak og dekoding, de vil bli diskutert nedenfor.
Så vi mottok posten, la oss begynne å behandle den.
Signalavkoding
La oss laste filen med Python og se strukturen:
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()
Vi ser en typisk amplitudemodulasjon:
For å forenkle dekodingen tar vi signalkonvolutten ved å bruke Hilbert-transformasjonen:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Utvidet resultat:
La oss jevne ut støyutslipp ved hjelp av et lavpassfilter, samtidig beregne gjennomsnittsverdien, det vil komme godt med senere for parsing.
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
Resultat (gul linje): et nesten rektangulært signal som er ganske enkelt å analysere.
Parsing
Først må du få bitsekvensen. Selve signalstrukturen er veldig enkel.
Pulsene er delt inn i andre intervaller. Hvis avstanden mellom pulsene er 0.1 s (dvs. lengden på selve pulsen er 0.9 s), legger vi til "0" til bitsekvensen, hvis avstanden er 0.2 s (dvs. lengden er 0.8 s), legger vi til "1". Slutten av hvert minutt indikeres med en "lang" puls, 2 s lang, bitsekvensen tilbakestilles til null, og fyllingen starter på nytt.
Ovenstående er lett å skrive i 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
Som et resultat får vi en sekvens av biter, i vårt eksempel i to sekunder ser det slik ut:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Forresten, det er interessant at det også er et "andre lag" med data i signalet. Bitsekvensen er også kodet med
Vårt siste trinn: få de faktiske dataene. Bits sendes en gang per sekund, så vi har bare 59 biter, der ganske mye informasjon er kodet:
Bitene er beskrevet i
For de som ønsker å eksperimentere på egenhånd, er dekodingskoden gitt under spoileren.
Kildekode
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)
Når vi kjører programmet, vil vi se noe som denne utgangen:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Det er faktisk all magien. Fordelen med et slikt system er at dekodingen er ekstremt enkel, og kan gjøres på hvilken som helst, den mest ukompliserte mikrokontrolleren. Vi teller bare lengden på pulsene, akkumulerer 60 biter, og på slutten av hvert minutt får vi den nøyaktige tiden. Sammenlignet med andre metoder for tidssynkronisering (GPS, for eksempel, eller Gud forby, Internett :), krever slik radiosynkronisering praktisk talt ikke strøm - for eksempel fungerer en vanlig værstasjon i hjemmet i omtrent et år på 2 AA-batterier. Derfor er selv armbåndsur laget med radiosynkronisering, for ikke å snakke om, selvfølgelig, veggklokker eller gatestasjonsklokker.
Bekvemmeligheten og enkelheten til DCF tiltrekker også DIY-entusiaster. For kun $10-20 kan du kjøpe en ferdig antennemodul med en ferdig mottaker og en TTL-utgang som kan kobles til en Arduino eller annen kontroller.
For Arduino allerede skrevet og
De som ønsker det kan til og med oppgradere den gamle bestemorklokken ved å installere en ny mekanisme med radiosynkronisering:
Du kan finne en på ebay ved å bruke søkeordene "Radio Controlled Movement".
Og til slutt, et life hack for de som har lest så langt. Selv om det ikke er en eneste radiosignalsender i løpet av de neste par tusen kilometerne, er et slikt signal enkelt å generere på egenhånd. Det er et program på Google Play kalt "DCF77 Emulator" som sender ut et signal til hodetelefonene. Ifølge forfatteren, hvis du pakker hodetelefonledningen døgnet rundt, vil de fange opp signalet (jeg lurer på hvordan, for vanlige hodetelefoner vil ikke gi ut et 77KHz-signal, men sannsynligvis skyldes mottaket harmoniske). Programmet fungerte ikke for meg på Android 9 i det hele tatt - det var rett og slett ingen lyd (eller kanskje jeg ikke hørte det - 77KHz, tross alt :), men kanskje noen vil være mer heldige. Noen gjør seg imidlertid til en fullverdig DCF-signalgenerator, som er lett å gjøre på samme Arduino eller ESP32:
(kilde
Konklusjon
DCF-systemet viste seg å være ganske enkelt og praktisk. Ved hjelp av en enkel og billig mottaker kan du ha nøyaktig tid alltid og overalt, selvfølgelig i resepsjonsområdet. Det ser ut til at selv til tross for den utbredte digitaliseringen og «tingenes internett», vil slike enkle løsninger være etterspurt i lang tid fremover.
Kilde: www.habr.com