Ciao Habr.
Probabilmente molti di coloro che acquistano un orologio o una stazione meteorologica hanno visto il logo dell'orologio radiocontrollato o addirittura dell'orologio atomico sulla confezione. Questo è molto comodo, perché devi solo mettere l'orologio sul tavolo e dopo un po' si regolerà automaticamente sull'ora esatta.
Scopriamo come funziona e scriviamo un decodificatore in Python.
Esistono diversi sistemi di sincronizzazione dell'ora. Il più popolare in Europa è il sistema tedesco
Tutto ciò che è scritto di seguito riguarderà il DCF77.
Ricezione del segnale
DCF77 è una stazione a onde lunghe che opera alla frequenza di 77.5 kHz e trasmette segnali in modulazione di ampiezza. La stazione da 50KW si trova a 25 km da Francoforte, ha iniziato a funzionare nel 1959 e nel 1973 è stata aggiunta l'informazione sulla data all'ora esatta. La lunghezza d'onda alla frequenza di 77 KHz è molto lunga, quindi anche le dimensioni del campo dell'antenna sono abbastanza decenti (foto da Wikipedia):
Con tale antenna e potenza assorbita, l'area di ricezione copre quasi tutta l'Europa, la Bielorussia, l'Ucraina e parte della Russia.
Chiunque può registrare un segnale. Per fare ciò, basta andare al ricevitore online
Lì premiamo il pulsante di download e registriamo un frammento della durata di diversi minuti. Naturalmente, se disponi di un ricevitore “reale” in grado di registrare la frequenza di 77.5 KHz, puoi utilizzare quello.
Naturalmente, ricevendo segnali orari radio tramite Internet, non riceveremo un orario veramente preciso: il segnale viene trasmesso con un ritardo. Ma il nostro obiettivo è solo quello di comprendere la struttura del segnale, per questo la registrazione su Internet è più che sufficiente. Nella vita reale, ovviamente, vengono utilizzati dispositivi specializzati per la ricezione e la decodifica; verranno discussi di seguito.
Quindi, abbiamo ricevuto la registrazione, iniziamo a elaborarla.
Decodifica del segnale
Carichiamo il file utilizzando Python e vediamo la sua struttura:
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()
Vediamo una tipica modulazione di ampiezza:
Per semplificare la decodifica, prendiamo l'inviluppo del segnale utilizzando la trasformata di Hilbert:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
Risultato ingrandito:
Attenuiamo le emissioni di rumore utilizzando un filtro passa-basso e allo stesso tempo calcoliamo il valore medio, che sarà utile in seguito per l'analisi.
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 ad onda quasi quadra abbastanza facile da analizzare.
parsing
Per prima cosa devi ottenere la sequenza di bit. La struttura del segnale in sé è molto semplice.
Gli impulsi sono divisi in secondi intervalli. Se la distanza tra gli impulsi è 0.1 s (ovvero la durata dell’impulso stesso è 0.9 s), aggiungere “0” alla sequenza di bit; se la distanza è 0.2 s (ovvero la lunghezza è 0.8 s), aggiungere “1”. La fine di ogni minuto è indicata da un impulso “lungo”, della durata di 2 s, la sequenza di bit viene azzerata e il riempimento ricomincia.
Quanto sopra è facile da scrivere in 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
Di conseguenza, otteniamo una sequenza di bit, nel nostro esempio per due secondi appare così:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
A proposito, è interessante che il segnale abbia anche un “secondo livello” di dati. Anche la sequenza di bit viene codificata utilizzando
Il nostro ultimo passo: ottenere i dati effettivi. I bit vengono trasmessi una volta al secondo, quindi abbiamo un totale di 59 bit, in cui sono codificate molte informazioni:
I bit sono descritti in
Per chi volesse sperimentare in proprio, il codice di decodifica è riportato 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 = ('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 eseguiamo il programma, vedremo un output simile a questo:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
In realtà, questa è tutta la magia. Il vantaggio di un tale sistema è che la decodifica è estremamente semplice e può essere eseguita su qualsiasi microcontrollore, anche il più semplice. Contiamo semplicemente la lunghezza degli impulsi, accumuliamo 60 bit e alla fine di ogni minuto otteniamo l'ora esatta. Rispetto ad altri metodi di sincronizzazione dell'ora (ad esempio GPS o, Dio non voglia, Internet:), questa sincronizzazione radio non richiede praticamente alcuna corrente elettrica - ad esempio, una normale stazione meteorologica domestica funziona per circa un anno con 2 batterie AA. Pertanto anche gli orologi da polso sono realizzati con sincronizzazione radio, per non parlare, ovviamente, degli orologi da parete o degli orologi da stazione stradale.
La comodità e la semplicità del DCF attirano anche gli appassionati del fai da te. Per soli $ 10-20 puoi acquistare un modulo antenna già pronto con un ricevitore già pronto e un'uscita TTL, che può essere collegato ad un Arduino o ad un altro controller.
Già scritto per Arduino
Chi lo desidera può anche aggiornare l'orologio della nonna installando un nuovo meccanismo con sincronizzazione radio:
Puoi trovarne uno su eBay usando le parole chiave "Movimento radiocontrollato".
E infine, un trucchetto per coloro che hanno letto fin qui. Anche se nel giro di un paio di migliaia di chilometri non c'è un solo trasmettitore di segnale radio, non è difficile generare da soli un segnale del genere. C'è un programma su Google Play chiamato "DCF77 Emulator" che trasmette il segnale alle cuffie. Secondo l'autore, se si avvolge il filo delle cuffie attorno all'orologio, queste capteranno il segnale (è interessante come, perché le normali cuffie non producono un segnale a 77 KHz, ma la ricezione è probabilmente dovuta alle armoniche). Su Android 9, il programma non ha funzionato affatto per me - semplicemente non c'era alcun suono (o forse non l'ho sentito - dopotutto è 77KHz :), ma forse qualcuno avrà più fortuna. Alcuni, tuttavia, si realizzano un generatore di segnali DCF a tutti gli effetti, che è facile da realizzare sullo stesso Arduino o ESP32:
(fonte
conclusione
Il sistema DCF si è rivelato davvero semplice e conveniente. Con l'aiuto di un ricevitore semplice ed economico potrete avere l'ora esatta sempre e ovunque, ovviamente nella zona della reception. Sembra che, nonostante la diffusa digitalizzazione e l’Internet delle cose, soluzioni così semplici saranno richieste ancora per molto tempo.
Fonte: habr.com