Ciao Habr.
Probabilmente molti, acquistando orologi o stazioni meteorologiche, hanno visto sulla confezione il logo Radio Controlled Clock o anche Atomic Clock. È molto comodo, poiché basta posizionare l'orologio sul tavolo e, dopo un po', si sincronizzerà automaticamente all'ora esatta.

Analizziamo come funziona e scriviamo un decoder in Python.
Esistono diversi sistemi di sincronizzazione del tempo. Il più popolare in Europa è il sistema tedesco , in Giappone c'è il proprio sistema , negli Stati Uniti c'è un sistema , e così via. Di seguito parleremo di DCF77, come il più attuale e accessibile per la ricezione in alcune zone della parte europea della Russia e dei paesi vicini (i residenti del Far East potrebbero avere un'opinione opposta, ma a loro volta possono ricevere e analizzare il segnale giapponese;).
Tutto ciò che segue sarà su DCF77.
Ricezione del segnale
DCF77 è una stazione a onda lunga che opera sulla frequenza di 77.5 kHz, trasmettendo segnali in modulazione di ampiezza. La stazione, con una potenza di 50 kW, si trova a 25 km da Francoforte ed è attiva dal 1959; nel 1973 sono state aggiunte informazioni sulla data al tempo esatto. La lunghezza d'onda a 77 kHz è piuttosto grande, quindi anche le dimensioni del campo antenne sono considerevoli (foto da Wikipedia):

Con una tale antenna e potenza fornita, l'area di ricezione copre praticamente tutta l'Europa, la Bielorussia, l'Ucraina e parte della Russia.

Chiunque può registrare il segnale. È sufficiente accedere a un ricevitore online , selezionare la frequenza di 76.5 kHz e la modulazione USB. Dovrebbe aprirsi un'immagine simile a questa:

Lì dobbiamo cliccare il pulsante di download e registrare un frammento della durata di alcuni minuti. Naturalmente, se si dispone di un 'vero' ricevitore in grado di registrare la frequenza di 77.5 kHz, si può utilizzare anche quello.
Certo, ricevendo segnali radio del tempo preciso tramite Internet, non otterremo un tempo davvero preciso — il segnale viene trasmesso con ritardo. Ma il nostro obiettivo è solo comprendere la struttura del segnale, per questo le registrazioni da Internet sono più che sufficienti. In realtà, vengono utilizzati dispositivi specializzati per la ricezione e la decodifica, di cui si parlerà più avanti.
Dunque, abbiamo ottenuto una registrazione, procediamo con la sua elaborazione.
Decodifica del segnale
Carichiamo il file utilizzando Python e guardiamo la sua struttura:
da 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()
Vediamo una tipica modulazione di ampiezza:

Per semplificare la decodifica, prendiamo l'inviluppo del segnale mediante la trasformazione di Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000]) Risultato ingrandito:

Smorziamo i picchi del rumore utilizzando un filtro passa-basso e calcoliamo la media, che ci sarà utile in seguito per il parsing.
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 Risultato (linea gialla): un segnale quasi rettangolare, piuttosto facile da analizzare.

Parsing
Per prima cosa dobbiamo ottenere una sequenza di bit. La struttura del segnale è molto semplice.

Gli impulsi sono divisi in intervalli di un secondo. Se la distanza tra gli impulsi è di 0.1s (cioè la durata dell'impulso è di 0.9s), aggiungiamo "0" alla sequenza di bit; se la distanza è di 0.2s (cioè la durata è di 0.8s), aggiungiamo "1". La fine di ogni minuto è contrassegnata da un "lungo" impulso di 2s, durante il quale la sequenza di bit viene azzerata e il riempimento ricomincia da capo.
Quanto sopra non è difficile da scrivere in Python.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] avg:
# Inizio segnale
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Fine segnale
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
Di conseguenza otteniamo una sequenza di bit, che nel nostro esempio per due secondi appare così:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
È interessante notare che nel segnale ci sono anche un "secondo strato" di dati. La sequenza di bit è anch'essa codificata mediante . Teoricamente, questo dovrebbe garantire una decodifica più robusta anche in caso di segnale debole.
Il nostro ultimo passo: ottenere i dati veri e propri. I bit vengono trasmessi una volta al secondo, quindi abbiamo solo 59 bit, in cui è codificata una grande quantità di informazioni:

I bit sono descritti in , e sono piuttosto curiosi. I primi 15 bit non vengono utilizzati, anche se c'erano piani per utilizzarli per sistemi di allerta e difesa civile. Il bit A1 indica che nell'ora successiva l'orologio verrà spostato all'ora legale. Il bit A2 indica che nell'ora successiva verrà aggiunta , che a volte è utilizzato per correggere il tempo in base alla rotazione della Terra. Gli altri bit codificano ore, minuti, secondi e data.

Per coloro che vorranno sperimentare autonomamente, il codice per la decodifica è fornito sotto spoiler.
Codice sorgente
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 = ('Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato', 'Domenica')
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)
Eseguendo il programma, vedremo un output simile a questo:
0011110110111000001011000001010000100110010101100010011000
Martedì, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Martedì, 26.03.19, 21:42
Ecco, in effetti, tutta la magia. Il vantaggio di questo sistema è che la decodifica è estremamente semplice e può essere fatta su qualsiasi microcontrollore, anche il più semplice. Basta contare la lunghezza degli impulsi, accumulare 60 bit e alla fine di ogni minuto otteniamo l'ora esatta. Rispetto ad altri metodi di sincronizzazione dell'ora (come il GPS, o peggio ancora, Internet :), questa sincronizzazione radio richiede praticamente zero energia — per esempio, una comune stazione meteorologica domestica funziona circa un anno con 2 batterie AA. Pertanto, con la sincronizzazione radio si realizzano anche orologi da polso, per non parlare, naturalmente, di quelli da parete o degli orologi da stazione stradale.
La comodità e semplicità del DCF attraggono anche gli appassionati di fai da te. Con soli 10-20$ è possibile acquistare un modulo completo con antenna e ricevitore pronto, dotato di uscita TTL, che può essere collegato a Arduino o ad un altro controllore.

Per Arduino sono già state scritte e . Tuttavia, è noto che ciò che si fa con un microcontrollore produce sempre orologi o stazioni meteorologiche. Con un dispositivo del genere, ottenere l'ora esatta non è affatto difficile, a condizione di trovarsi nella zona di ricezione. Si può anche appendere un'etichetta all'orologio con la scritta «Atomic Clock» e spiegare a tutti coloro che sono interessati che il dispositivo si sincronizza effettivamente attraverso orologi atomici.
Coloro che lo desiderano possono persino aggiornare i vecchi orologi della nonna, installando in essi un nuovo meccanismo di radio-sincronizzazione:

Un tale meccanismo può essere trovato su ebay cercando le parole chiave «Radio Controlled Movement».
E infine, un hack per chi è arrivato fin qui. Anche se nei prossimi migliaia di chilometri non c'è alcun trasmettitore di segnali radio, è piuttosto semplice generare un tale segnale da soli. Su Google Play esiste un'applicazione chiamata «DCF77 Emulator», che emette il segnale nelle cuffie. Secondo l'autore, se si avvolge il filo delle cuffie attorno all'orologio, questo catturerà il segnale (è interessante come, dato che le cuffie normali non emetteranno un segnale a 77kHz, ma probabilmente la ricezione avviene grazie agli armonici). Sul mio Android 9 l'app non ha funzionato affatto — semplicemente non c'era suono (o forse non lo sentivo — sono 77kHz:), ma magari qualcuno avrà più fortuna. Alcuni, però, realizzano anche un generatore completo di segnali DCF, che non è difficile costruire con la stessa Arduino o ESP32:

(fonte )
Conclusione
Il sistema DCF si è rivelato realmente piuttosto semplice e pratico. Con un ricevitore semplice ed economico, si può avere sempre e ovunque l'ora esatta, naturalmente nella zona di ricezione. Si può ritenere che, nonostante la digitalizzazione diffusa e l'«Internet delle cose», tali soluzioni semplici saranno ancora richieste a lungo.
Fonte: habr.com
