Hallo Habr.
Waarschijnlijk hebben velen die een horloge of weerstation kopen het Radio Controlled Clock- of zelfs het Atomic Clock-logo op de verpakking gezien. Dit is erg handig, omdat je de klok gewoon op tafel hoeft te zetten en na een tijdje zich automatisch aanpast aan de exacte tijd.
Laten we uitzoeken hoe het werkt en een decoder in Python schrijven.
Er zijn verschillende tijdsynchronisatiesystemen. Het meest populaire in Europa is het Duitse systeem
Alles wat hieronder geschreven wordt gaat over de DCF77.
Signaalontvangst
DCF77 is een langegolfstation dat werkt op een frequentie van 77.5 kHz en signalen uitzendt in amplitudemodulatie. Het 50KW-station ligt op 25 km van Frankfurt, het werd in 1959 in gebruik genomen en in 1973 werd datuminformatie aan de exacte tijd toegevoegd. De golflengte bij een frequentie van 77 KHz is erg lang, dus de afmetingen van het antenneveld zijn ook behoorlijk behoorlijk (foto van Wikipedia):
Met een dergelijke antenne en stroomingang bestrijkt het ontvangstgebied vrijwel heel Europa, Wit-Rusland, Oekraïne en een deel van Rusland.
Iedereen kan een signaal opnemen. Om dit te doen, gaat u gewoon naar de online ontvanger
Daar drukken we op de downloadknop en nemen een fragment van enkele minuten op. Als je een ‘echte’ ontvanger hebt die de frequentie van 77.5 kHz kan opnemen, kun je die natuurlijk gebruiken.
Door radiotijdsignalen via internet te ontvangen, ontvangen we uiteraard geen echt nauwkeurige tijd; het signaal wordt met vertraging verzonden. Maar ons doel is alleen om de structuur van het signaal te begrijpen; hiervoor is de internetopname meer dan voldoende. In het echte leven worden natuurlijk gespecialiseerde apparaten gebruikt voor ontvangst en decodering; deze zullen hieronder worden besproken.
Dus we hebben de opname ontvangen, laten we beginnen met het verwerken ervan.
Signaaldecodering
Laten we het bestand laden met Python en de structuur bekijken:
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()
We zien typische amplitudemodulatie:
Om het decoderen te vereenvoudigen, nemen we de signaalomhullende met behulp van de Hilbert-transformatie:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Uitvergroot resultaat:
Laten we de geluidsemissies gladstrijken met behulp van een laagdoorlaatfilter en tegelijkertijd de gemiddelde waarde berekenen, die later nuttig zal zijn bij het parseren.
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
Resultaat (gele lijn): een bijna blokgolfsignaal dat vrij eenvoudig te analyseren is.
Parseren
Eerst moet je de bitreeks verkrijgen. De signaalstructuur zelf is heel eenvoudig.
De pulsen zijn verdeeld in tweede intervallen. Als de afstand tussen de pulsen 0.1s is (d.w.z. de lengte van de puls zelf is 0.9s), voeg dan “0” toe aan de bitreeks; als de afstand 0.2s is (d.w.z. de lengte is 0.8s), voeg dan “1” toe. Het einde van elke minuut wordt aangegeven door een “lange” puls van 2 seconden, de bitreeks wordt teruggezet op nul en het vullen begint opnieuw.
Bovenstaande is eenvoudig te schrijven in 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
Als resultaat krijgen we een reeks bits, in ons voorbeeld ziet het er gedurende twee seconden als volgt uit:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Het is trouwens interessant dat het signaal ook een ‘tweede laag’ met gegevens heeft. De bitreeks wordt ook gecodeerd met behulp van
Onze laatste stap: het verkrijgen van de daadwerkelijke gegevens. Bits worden één keer per seconde verzonden, dus we hebben in totaal 59 bits, waarin behoorlijk wat informatie is gecodeerd:
De bits worden beschreven in
Voor degenen die zelf willen experimenteren, staat de decodeercode onder de spoiler.
Bron
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)
Wanneer we het programma uitvoeren, zien we een uitvoer die er ongeveer zo uitziet:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Eigenlijk is dat allemaal magie. Het voordeel van een dergelijk systeem is dat het decoderen uiterst eenvoudig is en op elke, zelfs de eenvoudigste microcontroller kan worden uitgevoerd. We tellen eenvoudigweg de lengte van de pulsen, verzamelen 60 bits en aan het einde van elke minuut krijgen we de exacte tijd. Vergeleken met andere methoden van tijdsynchronisatie (bijvoorbeeld GPS of God verhoede het internet :)) vereist een dergelijke radiosynchronisatie vrijwel geen elektriciteit - een gewoon weerstation voor thuis werkt bijvoorbeeld ongeveer een jaar op 2 AA-batterijen. Daarom worden zelfs polshorloges gemaakt met radiosynchronisatie, om nog maar te zwijgen van wandhorloges of straatstationhorloges.
Het gemak en de eenvoud van DCF trekken ook doe-het-zelvers aan. Voor slechts $ 10-20 kun je een kant-en-klare antennemodule kopen met een kant-en-klare ontvanger en TTL-uitgang, die kan worden aangesloten op een Arduino of andere controller.
Al geschreven voor Arduino
Wie dat wenst, kan zelfs het horloge van zijn oude grootmoeder upgraden door een nieuw mechanisme met radiosynchronisatie te installeren:
Je kunt er een vinden op eBay met de trefwoorden "Radiogestuurde beweging".
En tot slot een lifehack voor degenen die tot nu toe hebben gelezen. Zelfs als er de komende paar duizend km geen enkele radiosignaalzender is, is het niet moeilijk om zelf zo'n signaal te genereren. Er is een programma op Google Play genaamd “DCF77 Emulator” dat het signaal naar een hoofdtelefoon stuurt. Volgens de auteur zullen ze, als je de draad van de hoofdtelefoon om het horloge wikkelt, het signaal oppikken (het is interessant hoe, omdat gewone hoofdtelefoons geen 77 kHz-signaal produceren, maar de ontvangst waarschijnlijk te wijten is aan harmonischen). Op Android 9 werkte het programma helemaal niet voor mij - er was gewoon geen geluid (of misschien heb ik het niet gehoord - het is tenslotte 77 kHz :)), maar misschien heeft iemand meer geluk. Sommigen maken zichzelf echter tot een volwaardige DCF-signaalgenerator, die eenvoudig op dezelfde Arduino of ESP32 te maken is:
(bron
Conclusie
Het DCF-systeem bleek eigenlijk heel eenvoudig en handig. Met behulp van een eenvoudige en goedkope ontvanger heeft u altijd en overal de exacte tijd, uiteraard in de ontvangstruimte. Het lijkt erop dat zelfs ondanks de wijdverbreide digitalisering en het internet der dingen er nog lang vraag zal zijn naar dergelijke eenvoudige oplossingen.
Bron: www.habr.com