Ola Habr.
Probablemente moitos dos que compran un reloxo ou unha estación meteorolóxica viron o reloxo radiocontrolado ou mesmo o logotipo do reloxo atómico na embalaxe. Isto é moi cómodo, porque só tes que poñer o reloxo sobre a mesa e despois dun tempo axustarase automaticamente á hora exacta.
Imos descubrir como funciona e escribir un decodificador en Python.
Existen diferentes sistemas de sincronización horaria. O máis popular en Europa é o sistema alemán
Todo o escrito a continuación será sobre o DCF77.
Recepción do sinal
DCF77 é unha estación de onda longa que opera a unha frecuencia de 77.5 kHz e transmite sinais en modulación de amplitude. A estación de 50KW está situada a 25 km de Frankfurt, comezou a funcionar en 1959 e en 1973 engadiuse a información da data á hora exacta. A lonxitude de onda a unha frecuencia de 77 KHz é moi longa, polo que as dimensións do campo da antena tamén son bastante decentes (foto da Wikipedia):
Con tal antena e entrada de enerxía, a zona de recepción abrangue case toda Europa, Bielorrusia, Ucraína e parte de Rusia.
Calquera pode gravar un sinal. Para iso, só tes que ir ao receptor en liña
Alí prememos o botón de descarga e gravamos un fragmento de varios minutos de duración. Por suposto, se tes un receptor "real" capaz de gravar a frecuencia de 77.5 KHz, podes usalo.
Por suposto, ao recibir sinais de tempo de radio a través de Internet, non recibiremos a hora verdadeiramente precisa: o sinal transmítese cun atraso. Pero o noso obxectivo é só comprender a estrutura do sinal; para iso, a gravación en Internet é máis que suficiente. Na vida real, por suposto, utilízanse dispositivos especializados para recibir e decodificar; discutiranse a continuación.
Entón, recibimos a gravación, imos comezar a procesala.
Decodificación de sinal
Carguemos o ficheiro usando Python e vexamos a súa estrutura:
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()
Vemos a modulación de amplitude típica:
Para simplificar a decodificación, tomemos a envolvente do sinal usando a transformada de Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Resultado ampliado:
Suavizar as emisións de ruído mediante un filtro paso baixo e, ao mesmo tempo, calcular o valor medio, que será útil máis tarde para a análise.
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
Resultado (liña amarela): un sinal de onda case cadrada que é bastante doado de analizar.
Analizando
Primeiro cómpre obter a secuencia de bits. A propia estrutura do sinal é moi sinxela.
Os pulsos divídense en segundos intervalos. Se a distancia entre os pulsos é de 0.1 s (é dicir, a lonxitude do pulso é de 0.9 s), engade "0" á secuencia de bits; se a distancia é de 0.2 s (é dicir, a lonxitude é de 0.8 s), engade "1". O final de cada minuto indícase cun pulso "longo", de 2 segundos de duración, a secuencia de bits restablece a cero e o recheo comeza de novo.
O anterior é fácil de escribir en 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
Como resultado, obtemos unha secuencia de bits, no noso exemplo durante dous segundos parece isto:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Por certo, é interesante que o sinal tamén teña unha "segunda capa" de datos. A secuencia de bits tamén se codifica usando
O noso último paso: obter os datos reais. Os bits transmítense unha vez por segundo, polo que temos un total de 59 bits, nos que se codifica bastante información:
Os bits descríbense en
Para aqueles que queiran experimentar por conta propia, o código de decodificación aparece baixo o spoiler.
Código fonte
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)
Cando executamos o programa, veremos unha saída similar a esta:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
En realidade, esa é toda a maxia. A vantaxe deste sistema é que a decodificación é extremadamente sinxela e pódese facer en calquera, incluso no microcontrolador máis sinxelo. Simplemente contamos a duración dos pulsos, acumulamos 60 bits e ao final de cada minuto obtemos a hora exacta. En comparación con outros métodos de sincronización horaria (GPS, por exemplo, ou Deus o libre, Internet:), esa sincronización de radio practicamente non require electricidade; por exemplo, unha estación meteorolóxica doméstica funciona durante aproximadamente un ano con 2 pilas AA. Polo tanto, incluso os reloxos de pulso están feitos con sincronización de radio, sen esquecer, por suposto, os reloxos de parede ou os reloxos das estacións de rúa.
A comodidade e a sinxeleza de DCF tamén atraen aos entusiastas do bricolaxe. Por só $ 10-20 podes mercar un módulo de antena preparado cun receptor preparado e saída TTL, que se pode conectar a un Arduino ou outro controlador.
Xa está escrito para Arduino
Os que o desexen poden incluso actualizar o reloxo da súa vella avoa instalando un novo mecanismo con sincronización de radio:
Podes atopar un en ebay usando as palabras clave "Movemento radiocontrolado".
E, finalmente, un truco de vida para os que leron ata aquí. Aínda que non haxa un só transmisor de sinal de radio nos próximos mil quilómetros, non é difícil xerar tal sinal vostede mesmo. Hai un programa en Google Play chamado "DCF77 Emulator" que envía o sinal aos auriculares. Segundo o autor, se envolves o fío dos auriculares ao reloxo, captarán o sinal (é interesante como, porque os auriculares normais non producirán un sinal de 77 KHz, pero a recepción probablemente débese aos harmónicos). En Android 9, o programa non funcionou en absoluto para min - simplemente non había son (ou quizais non o escoitei - é 77KHz, despois de todo:), pero quizais alguén teña mellor sorte. Algúns, con todo, convértense nun xerador de sinal DCF completo, que é fácil de facer no mesmo Arduino ou ESP32:
(fonte
Conclusión
O sistema DCF resultou ser moi sinxelo e cómodo. Coa axuda dun receptor sinxelo e barato, podes ter a hora exacta sempre e en todas partes, por suposto na zona de recepción. Parece que aínda a pesar da dixitalización xeneralizada e da Internet das Cousas, solucións tan sinxelas serán demandadas durante moito tempo.
Fonte: www.habr.com