Olá Habr.
Provavelmente muitos que compram um relógio ou estação meteorológica viram o logotipo do Relógio Controlado por Rádio ou mesmo do Relógio Atômico na embalagem. Isso é muito prático, pois basta colocar o relógio sobre a mesa e depois de um tempo ele se ajustará automaticamente à hora exata.
Vamos descobrir como funciona e escrever um decodificador em Python.
Existem diferentes sistemas de sincronização de tempo. O mais popular na Europa é o sistema alemão
Tudo o que está escrito abaixo será sobre o DCF77.
Recepção de sinal
DCF77 é uma estação de ondas longas operando na frequência de 77.5 kHz e transmitindo sinais em modulação de amplitude. A estação 50KW está localizada a 25 km de Frankfurt, começou a operar em 1959, e em 1973 foram acrescentadas informações de data à hora exata. O comprimento de onda na frequência de 77 KHz é muito longo, então as dimensões do campo da antena também são bastante decentes (foto da Wikipedia):
Com tal antena e entrada de energia, a área de recepção cobre quase toda a Europa, Bielorrússia, Ucrânia e parte da Rússia.
Qualquer um pode gravar um sinal. Para fazer isso, basta acessar o receptor online
Lá pressionamos o botão de download e gravamos um fragmento de vários minutos. Claro, se você tiver um receptor “real” capaz de gravar a frequência de 77.5 KHz, você poderá usá-lo.
É claro que, ao receber sinais de tempo de rádio pela Internet, não receberemos um tempo verdadeiramente preciso - o sinal é transmitido com atraso. Mas nosso objetivo é apenas entender a estrutura do sinal, para isso a gravação pela Internet é mais que suficiente. Na vida real, é claro, são usados dispositivos especializados para recepção e decodificação; eles serão discutidos abaixo.
Então, recebemos a gravação, vamos começar a processá-la.
Decodificação de Sinal
Vamos carregar o arquivo usando Python e ver sua 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 modulação de amplitude típica:
Para simplificar a decodificação, vamos pegar o envelope do sinal usando a transformada de Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Resultado ampliado:
Vamos suavizar as emissões de ruído usando um filtro passa-baixa e, ao mesmo tempo, calcular o valor médio, que será útil posteriormente para 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 (linha amarela): um sinal de onda quase quadrada e bastante fácil de analisar.
Análise
Primeiro você precisa obter a sequência de bits. A estrutura do sinal em si é muito simples.
Os pulsos são divididos em segundos intervalos. Se a distância entre os pulsos for 0.1s (ou seja, a duração do pulso em si for 0.9s), adicione “0” à sequência de bits; se a distância for 0.2s (ou seja, a duração for 0.8s), adicione “1”. O final de cada minuto é indicado por um pulso “longo”, de 2s de duração, a sequência de bits é zerada e o enchimento começa novamente.
O texto acima é fácil de escrever em 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 uma sequência de bits, em nosso exemplo por dois segundos fica assim:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Aliás, é interessante que o sinal também possua uma “segunda camada” de dados. A sequência de bits também é codificada usando
Nossa última etapa: obter os dados reais. Os bits são transmitidos uma vez por segundo, então temos um total de 59 bits, nos quais muita informação é codificada:
Os bits são descritos em
Para quem deseja experimentar por conta própria, o código de decodificação é fornecido abaixo do 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)
Quando executarmos o programa, veremos uma saída semelhante a esta:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
Na verdade, essa é toda a magia. A vantagem de tal sistema é que a decodificação é extremamente simples e pode ser feita em qualquer microcontrolador, mesmo no mais simples. Simplesmente contamos a duração dos pulsos, acumulamos 60 bits e ao final de cada minuto obtemos a hora exata. Em comparação com outros métodos de sincronização de tempo (GPS, por exemplo, ou Deus me livre, a Internet :), essa sincronização de rádio praticamente não requer eletricidade - por exemplo, uma estação meteorológica doméstica normal funciona por cerca de um ano com 2 baterias AA. Portanto, até os relógios de pulso são feitos com sincronização de rádio, sem falar, é claro, dos relógios de parede ou de estação de rua.
A conveniência e simplicidade do DCF também atraem os entusiastas do DIY. Por apenas US$ 10-20 você pode comprar um módulo de antena pronto com um receptor pronto e saída TTL, que pode ser conectado a um Arduino ou outro controlador.
Já escrito para Arduino
Quem quiser pode até atualizar o relógio da velha avó instalando um novo mecanismo com sincronização de rádio:
Você pode encontrar um no ebay usando as palavras-chave “Movimento controlado por rádio”.
E, finalmente, um truque para quem leu até aqui. Mesmo que não haja um único transmissor de sinal de rádio nos próximos milhares de quilômetros, não é difícil gerar esse sinal sozinho. Existe um programa no Google Play chamado “DCF77 Emulator” que envia o sinal para fones de ouvido. Segundo o autor, se você enrolar o fio dos fones de ouvido em volta do relógio, eles captarão o sinal (é interessante como, porque fones de ouvido comuns não produzirão sinal de 77KHz, mas a recepção provavelmente se deve a harmônicos). No Android 9, o programa não funcionou para mim - simplesmente não havia som (ou talvez eu não tenha ouvido - afinal são 77KHz :), mas talvez alguém tenha mais sorte. Alguns, entretanto, tornam-se um gerador de sinal DCF completo, que é fácil de fazer no mesmo Arduino ou ESP32:
(fonte
Conclusão
O sistema DCF revelou-se bastante simples e conveniente. Com a ajuda de um receptor simples e barato, você pode ter a hora exata sempre e em qualquer lugar, claro, na área de recepção. Parece que mesmo apesar da digitalização generalizada e da Internet das Coisas, essas soluções simples serão procuradas durante muito tempo.
Fonte: habr.com