Hola habr
Probablemente, muchas personas que compran un reloj o estación meteorológica vieron el logotipo del Reloj Radio Controlado o incluso el Reloj Atómico en el paquete. Esto es muy conveniente, porque es suficiente para poner el reloj sobre la mesa, y después de un tiempo se ajustarán automáticamente a la hora exacta.

Veamos cómo funciona y escriba un decodificador en Python.
Existen diferentes sistemas de sincronización horaria. El más popular en Europa es el sistema alemán
DCF-77 , Japón tiene su propio sistema
JJY , Estados Unidos tiene el sistema
WWVB , y así sucesivamente. Además, la historia será sobre DCF77, como el más relevante y disponible para la recepción en algunos lugares de la parte europea de Rusia y los países vecinos (los residentes del Lejano Oriente pueden tener la opinión opuesta, sin embargo, a su vez, pueden recibir y analizar la señal japonesa;).
Todo lo escrito a continuación será sobre DCF77.
Recepción de la señal
DCF77 es una estación de onda larga que opera a una frecuencia de 77.5KHz y transmite señales en modulación de amplitud. La estación con una capacidad de 50 kW se encuentra a 25 km de Frankfurt, comenzó a funcionar en 1959, en 1973 la información de la fecha se agregó a la hora exacta. La longitud de onda a una frecuencia de 77KHz es muy grande, por lo que las dimensiones del campo de la antena también son muy decentes (foto de Wikipedia):

Con dicha antena y entrada de energía, el área de recepción cubre casi toda Europa, Bielorrusia, Ucrania y parte de Rusia.

Todos pueden grabar una señal. Para hacer esto, solo vaya al receptor en línea
http://websdr.ewi.utwente.nl:8901/ , seleccione la frecuencia allí 76.5KHz y la modulación USB. Debería abrirse una imagen de algo como esto:

Allí presionamos el botón de descarga y grabamos un fragmento de varios minutos de duración. Por supuesto, si tiene un receptor "real" capaz de grabar una frecuencia de 77.5KHz, puede usarlo.
Por supuesto, cuando recibamos señales de radio de la hora exacta a través de Internet, no obtendremos la hora exacta: la señal se retrasa. Pero nuestro objetivo es solo entender la estructura de la señal; para esto, la grabación de Internet es más que suficiente. En la vida real, por supuesto, se utilizan dispositivos especializados para recibir y decodificar, se discutirán a continuación.
Entonces, tenemos el registro, comencemos a procesarlo.
Decodificación de señal
Descargue el archivo usando Python y vea su estructura:
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 una modulación de amplitud típica:

Para simplificar la decodificación, tomamos la envolvente de la señal usando la transformación de Hilbert:
analytic_signal = signal.hilbert(data) A = np.abs(analytic_signal) plt.plot(A[:100000])
Resultado en una vista ampliada:

Suavizamos las emisiones de la interferencia usando un filtro de paso bajo, al mismo tiempo que calculamos el valor promedio, será útil más tarde para el análisis.
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 (línea amarilla): una señal casi rectangular que es bastante fácil de analizar.

Analizando
Primero necesitas obtener la secuencia de bits. La estructura de la señal en sí es muy simple.

Los pulsos se dividen en segundos intervalos. Si la distancia entre pulsos es 0.1 s (es decir, la longitud del pulso es 0.9 s), agregue "0" a la secuencia de bits, si la distancia es 0.2 s (es decir, 0.8 s de longitud), agregue "1". El final de cada minuto se indica mediante un pulso "largo", de 2 segundos de duración, la secuencia de bits se restablece a cero y el llenado comienza nuevamente.
Lo anterior es 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:
Como resultado, obtenemos una secuencia de bits, en nuestro ejemplo durante dos minutos se ve así:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Por cierto, es interesante que haya una "segunda capa" de datos en la señal. La secuencia de bits también se codifica mediante
modulación de fase . Teóricamente, esto debería proporcionar una decodificación más estable incluso en el caso de una señal debilitada.
Nuestro último paso: obtener los datos reales. Los bits se transmiten una vez por segundo, por lo que solo tenemos 59 bits, en los que se codifica mucha información:

Los bits se describen en
Wikipedia , y son bastante curiosos. Los primeros 15 bits no se usan, aunque había planes para usar en sistemas de advertencia
y defensa civil . El bit A1 indica que en la próxima hora el reloj estará configurado para el horario de verano. El bit A2 indica que se agregará un
segundo adicional en la próxima hora, que a veces se utiliza para corregir el tiempo de acuerdo con la rotación de la Tierra. Los bits restantes codifican horas, minutos y fecha.

Para aquellos que quieran experimentar por su cuenta, el código para la decodificación se proporciona bajo el spoiler.
Código fuente 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)
Al ejecutar el programa, veremos algo como esto:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
En realidad, eso es todo magia. La ventaja de un sistema de este tipo es que la decodificación es extremadamente simple y se puede realizar en cualquiera de los microcontroladores más sencillos. Simplemente cuente la longitud de los pulsos, acumule 60 bits y al final de cada minuto obtengamos el tiempo exacto. En comparación con otros métodos de sincronización horaria (GPS, por ejemplo, o Dios no lo quiera, Internet :), tal sincronización de radio prácticamente no requiere electricidad; por ejemplo, una estación meteorológica doméstica normal funciona durante aproximadamente un año con 2 baterías AA. Por lo tanto, incluso un reloj de pulsera está hecho con sincronización de radio, sin mencionar, por supuesto, sobre una pared o estación de calle.
La conveniencia y la simplicidad de DCF atraen a los amantes de los productos caseros. Por solo $ 10-20, puede comprar un módulo listo para usar de una antena con un receptor listo y salida TTL, que puede conectarse a un Arduino u otro controlador.

Para Arduino,
las bibliotecas preparadas ya se han escrito. Sin embargo, ya se sabe que no importa lo que haga en el microcontrolador, obtendrá un reloj o una estación meteorológica. Con dicho dispositivo, obtener la hora exacta es realmente fácil, siempre que se encuentre en el área de recepción. Bueno, puede colgar la inscripción "Reloj atómico" en el reloj y, al mismo tiempo, explicar a todos los que lo deseen que el dispositivo está realmente sincronizado con un reloj atómico.
Aquellos que lo deseen pueden incluso actualizar los relojes de sus abuelas instalando un nuevo mecanismo con sincronización de radio en ellos:

Puede encontrar uno en eBay usando las palabras clave "Movimiento controlado por radio".
Y finalmente, un truco de vida para aquellos que han leído aquí. Incluso si no hay un solo transmisor de una señal de radio en los próximos dos mil kilómetros, dicha señal es fácil de generar de forma independiente. Google Play tiene un programa llamado "Emulador DCF77" que emite una señal a los auriculares. Según el autor, si enrollas el cable de los auriculares alrededor del reloj, captarán una señal (me pregunto cómo, porque los auriculares normales no emitirán una señal de 77KHz, pero probablemente la recepción provenga de los armónicos). Mi programa no funcionaba en Android 9, simplemente no había sonido (o tal vez no lo escuché, 77KHz, después de todo :), pero tal vez alguien tenga más suerte. Algunos, sin embargo, se convierten en un generador de señal DCF completo, que es fácil de hacer en el mismo Arduino o ESP32:

(fuente
sgfantasytoys.wordpress.com/2015/05/13/synchronize-radio-controlled-watch-without-access )
Conclusión
El sistema DCF resultó ser realmente bastante simple y conveniente. Con la ayuda de un receptor simple y barato, puede tener una hora precisa en cualquier momento, en cualquier lugar, por supuesto, en el área de recepción. Parece que incluso a pesar de la digitalización generalizada y el "Internet de las cosas", se necesitarán soluciones tan simples durante mucho tiempo.