Olá Habr.
Certamente muitos que compram relógios ou estações meteorológicas já viram o logotipo Rádio Controlado ou até mesmo Relógio Atômico na embalagem. Isso é bastante conveniente, pois basta colocar o relógio na mesa e, após algum tempo, ele se ajustará automaticamente para a hora exata.

Vamos entender como isso funciona e escrever um decodificador na linguagem Python.
Existem diferentes sistemas de sincronização de tempo. O mais popular na Europa é o sistema alemão , no Japão existe seu próprio sistema , nos EUA existe o sistema , entre outros. A seguir, falaremos sobre DCF77, pois é o mais relevante e acessível para recepção em algumas regiões da parte europeia da Rússia e países vizinhos (os habitantes do Extremo Oriente podem ter uma opinião diferente, embora eles possam, por sua vez, receber e analisar o sinal japonês;).
Tudo que será escrito a seguir será sobre DCF77.
Recepção do sinal
DCF77 é uma estação de ondas longas que opera na frequência de 77,5 kHz, transmitindo sinais em modulação de amplitude. A estação, com uma potência de 50 kW, está localizada a 25 km de Frankfurt, começou a operar em 1959 e, em 1973, informações sobre a data foram adicionadas ao tempo exato. O comprimento de onda na frequência de 77 kHz é bastante longo, portanto, o tamanho do campo de antena também é considerável (foto da Wikipédia):

Com essa antena e a potência fornecida, a área de recepção abrange praticamente toda a Europa, Belarus, Ucrânia e parte da Rússia.

Qualquer um pode gravar o sinal. Para isso, basta acessar um receptor online , escolher lá a frequência de 76,5 kHz e modulação USB. Deve abrir uma imagem semelhante a esta:

Lá também, pressione o botão de download e grave um fragmento com duração de alguns minutos. É claro que, com um receptor "real", capaz de gravar a frequência de 77,5 kHz, é possível utilizá-lo.
Claro, ao receber sinais de rádio de hora exata via Internet, não obteremos realmente a hora exata — o sinal é transmitido com atraso. Mas nosso objetivo é entender a estrutura do sinal, para isso, gravações online são mais do que suficientes. Na realidade, claro, são utilizados dispositivos especializados para recepção e decodificação, sobre os quais falaremos a seguir.
Então, obtemos a gravação, vamos começar a processá-la.
Decodificação do sinal
Vamos carregar o arquivo usando Python e examinar 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()
Observamos uma modulação de amplitude típica:

Para simplificar a decodificação, vamos obter o envoltório do sinal usando a transformação de Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000]) O resultado ampliado:

Vamos suavizar os picos causados por ruídos usando um filtro passa-baixa, e ao mesmo tempo calcular a média, que será útil mais 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 O resultado (linha amarela): um sinal praticamente retangular, que é bastante fácil de analisar.

Análise
Primeiro, precisamos obter a sequência de bits. A estrutura do sinal em si é muito simples.

Os pulsos são divididos em intervalos de um segundo. Se a distância entre os pulsos é de 0,1 s (ou seja, o comprimento do pulso é de 0,9 s), adicionamos um '0' à sequência de bits; se a distância for de 0,2 s (ou seja, o comprimento é de 0,8 s), adicionamos um '1'. O fim de cada minuto é marcado por um pulso 'longo' de 2 s, e a sequência de bits é reiniciada.
O que foi mencionado acima não é difícil de programar em Python.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] avg:
# Início do sinal
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Fim do sinal
sig_stop = pos
diff = sig_stop - sig_start
if diff 0.85*sample_rate and diff 1.5*sample_rate:
print(bits_str)
bits_str = ""
pos += 1
O resultado é uma sequência de bits, que em nosso exemplo, para dois segundos, parece assim:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Aliás, é interessante notar que o sinal contém uma 'segunda camada' de dados. A sequência de bits também é codificada usando . Teoricamente, isso deve garantir uma decodificação mais robusta, mesmo no caso de um sinal fraco.
Nosso último passo: obter os próprios dados. Os bits são transmitidos uma vez por segundo, então temos apenas 59 bits, nos quais está codificada uma quantidade considerável de informações:

Os bits estão descritos em , e eles são bastante curiosos. Os primeiros 15 bits não são utilizados, embora houvesse planos de usá-los para sistemas de alerta e defesa civil. O bit A1 indica que, na próxima hora, os relógios serão adiantados para o horário de verão. O bit A2 indica que, na próxima hora, será adicionada , que às vezes é usada para corrigir o tempo de acordo com a rotação da Terra. Os outros bits codificam horas, minutos, segundos e data.

Para aqueles que desejam experimentar por conta própria, o código para decodificação está listado abaixo do spoiler.
Código fonte
def decode(bits):
if bits[0] != '0' or bits[20] != '1':
return
minutos, horas, dia_do_mês, dia_da_semana, mês, ano = map(convert_block,
(bits[21:28], bits[29:35], bits[36:42], bits[42:45],
bits[45:50], bits[50:58]))
dias = ('Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado', 'Domingo')
print('{dow}, {dom:02}.{mon:02}.{y}, {h:02}:{m:02}'.format(h=horas, m=minutos, dow=dias[dia_da_semana],
dom=dia_do_mês, mon=mês, y=ano))
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)
Ao executar o programa, veremos uma saída aproximada como esta:
0011110110111000001011000001010000100110010101100010011000
Terça-feira, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Terça-feira, 26.03.19, 21:42
Eis a mágica. A vantagem desse sistema é que a decodificação é extremamente simples, e pode ser feita em qualquer controlador microprocessador básico. Contamos apenas a duração dos pulsos, acumulamos 60 bits e, no final de cada minuto, obtemos o tempo exato. Comparado a outros métodos de sincronização de tempo (como GPS, ou, Deus me livre, Internet:), essa sincronização por rádio praticamente não requer eletricidade — por exemplo, uma estação meteorológica doméstica comum funciona cerca de um ano com 2 pilhas AA. Por isso, até relógios de pulso utilizam sincronização por rádio, sem mencionar, claro, relógios de parede ou de estações ao ar livre.
A conveniência e simplicidade do DCF atraem também os entusiastas do faça-você-mesmo. Por apenas 10-20$, é possível comprar um módulo pronto com antena e receptor pronto, que pode ser conectado a um Arduino ou outro controlador.

Para Arduino, já foram escritas e . No entanto, é amplamente conhecido — não importa o que você faça com um microcontrolador, o resultado será sempre um relógio ou uma estação meteorológica. Com um dispositivo assim, obter a hora exata realmente não é difícil, desde que você esteja na zona de recepção. E também é possível colocar uma inscrição de «Relógio Atômico» e explicar a todos que o dispositivo realmente se sincroniza por meio de relógios atômicos.
Os interessados podem até atualizar os relógios antigos da vovó, instalando um novo mecanismo com sincronização por rádio:

Você pode encontrar um desses no ebay usando as palavras-chave «Movimento Controlado por Rádio».
E finalmente, uma dica para quem leu até aqui. Mesmo que não haja nenhum transmissor de sinal de rádio a milhares de quilômetros, gerar tal sinal por conta própria não é difícil. No Google Play, existe um programa chamado «Emulador DCF77», que emite o sinal pelos fones de ouvido. Segundo o autor, se você enrolar o fio dos fones ao redor do relógio, ele captará o sinal (interessante como, já que fones comuns não emitem o sinal de 77kHz, mas provavelmente a recepção ocorre por meio de harmônicos). No meu Android 9, o programa não funcionou de forma alguma — simplesmente não havia som (ou talvez eu não tenha ouvido — 77kHz, né:), mas talvez tenha mais sorte com alguém. Alguns, no entanto, fazem também um gerador completo de sinais DCF, que é fácil de construir com a mesma Arduino ou ESP32:

(fonte )
Conclusão
O sistema DCF se provou realmente bastante simples e conveniente. Com um receptor barato e fácil de usar, é possível ter sempre a hora exata, claro, na zona de recepção. Acredito que, mesmo apesar da digitalização generalizada e da «internet das coisas», tais soluções simples ainda serão procuradas por um bom tempo.
Fonte: habr.com
