Hallo Habr.
Wahrscheinlich haben viele, die eine Uhr oder eine Wetterstation kaufen, das Logo Radio Controlled Clock oder sogar Atomic Clock auf der Verpackung gesehen. Das ist sehr praktisch, denn man muss die Uhr nur auf den Tisch stellen, und sie stellt sich nach einer gewissen Zeit automatisch auf die genaue Uhrzeit ein.

Lassen Sie uns erklÀren, wie das funktioniert, und einen Decoder in der Programmiersprache Python schreiben.
Es gibt verschiedene Zeit-synchronisierungssysteme. Das bekannteste in Europa ist das deutsche System , in Japan gibt es ein eigenes System , in den USA gibt es ein System , und so weiter. Der folgende Bericht handelt von DCF77, da es das am meisten relevante und in einigen Teilen Europas, einschlieĂlich Russland und angrenzenden LĂ€ndern, empfangbare Signal ist (Bewohner des Fernen Ostens könnten eine andere Meinung haben, sie können jedoch den japanischen Signal empfangen und analysieren;).
Alles, was hier geschrieben wird, bezieht sich auf DCF77.
Signalempfang
DCF77 ist eine Langwellensstation, die auf einer Frequenz von 77,5 kHz arbeitet und Signale in Amplitudenmodulation ĂŒbertrĂ€gt. Die Station mit einer Leistung von 50 kW befindet sich 25 km von Frankfurt entfernt und nahm ihren Betrieb bereits 1959 auf; im Jahr 1973 wurde die Datumsinformation zur genauen Uhrzeit hinzugefĂŒgt. Die WellenlĂ€nge bei einer Frequenz von 77 kHz ist eher groĂ, daher sind die Abmessungen des Antennenfeldes ebenfalls ziemlich anstĂ€ndig (Foto aus Wikipedia):

Mit einer solchen Antenne und zugefĂŒhrter Leistung umfasst der Empfangsbereich fast ganz Europa, WeiĂrussland, die Ukraine und einen Teil Russlands.

Jeder kann das Signal aufzeichnen. Dazu muss man nur einen Online-EmpfÀnger nutzen , dort die Frequenz 76,5 kHz und die USB-Modulation auswÀhlen. Es sollte sich ein Bild öffnen, das etwa so aussieht:

Dort klicken wir auf die SchaltflĂ€che Download und zeichnen einen Abschnitt von mehreren Minuten auf. NatĂŒrlich kann man bei Vorhandensein eines âechtenâ EmpfĂ€ngers, der die Frequenz von 77,5 kHz aufzeichnen kann, auch diesen verwenden.
NatĂŒrlich erhalten wir, wenn wir Radiosignale mit exakter Uhrzeit ĂŒber das Internet empfangen, nicht die wirklich genaue Zeit â das Signal wird mit einer Verzögerung ĂŒbertragen. Aber unser Ziel ist es nur, die Struktur des Signals zu verstehen; dafĂŒr sind Internetaufzeichnungen mehr als ausreichend. In der RealitĂ€t werden natĂŒrlich spezialisierte GerĂ€te zum Empfang und zur Dekodierung verwendet, ĂŒber die unten gesprochen wird.
Also, wir haben eine Aufzeichnung erhalten, lassen Sie uns mit ihrer Analyse beginnen.
Signal dekodieren
Laden wir die Datei mit Python und schauen wir uns ihre Struktur an:
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()
Wir sehen eine typische Amplitudenmodulation:

Um die Decodierung zu vereinfachen, nehmen wir die HĂŒllkurve des Signals mit Hilfe der Hilbert-Transformation:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000]) Das Ergebnis in vergröĂerter Ansicht:

Wir glĂ€tten AusreiĂer durch ein Tiefpassfilter und berechnen gleichzeitig den Mittelwert, der spĂ€ter zum Parsen benötigt wird.
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 Das Ergebnis (gelbe Linie): ein nahezu rechteckiges Signal, das relativ leicht zu analysieren ist.

Parsing
Zuerst mĂŒssen wir die Bitfolge erhalten. Die Struktur des Signals ist sehr einfach.

Die Impulse sind in Sekundenintervalle unterteilt. Wenn der Abstand zwischen den Impulsen 0.1s betrĂ€gt (d.h. die LĂ€nge des Impulses 0.9s), fĂŒgen wir der Bitfolge "0" hinzu, wenn der Abstand 0.2s betrĂ€gt (d.h. die LĂ€nge 0.8s), fĂŒgen wir "1" hinzu. Das Ende jeder Minute wird durch einen "langen" Impuls von 2s LĂ€nge gekennzeichnet, die Bitfolge wird dabei zurĂŒckgesetzt und die FĂŒllung beginnt von neuem.
Das oben Geschriebene lÀsst sich leicht in Python umsetzen.
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] avg:
# Signalbeginn
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Signalende
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
Als Ergebnis erhalten wir eine Bitfolge, die in unserem Beispiel fĂŒr zwei Sekunden so aussieht:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
Ăbrigens ist es interessant, dass im Signal auch eine "zweite Schicht" von Daten existiert. Die Bitfolge ist ebenfalls kodiert mit . Theoretisch sollte dies eine stabilere Decodierung auch bei schwĂ€cheren Signalen ermöglichen.
Unser letzter Schritt: die eigentlichen Daten erhalten. Die Bits werden einmal pro Sekunde ĂŒbertragen, sodass wir insgesamt 59 Bits haben, die viele Informationen kodieren:

Die Bits sind wie folgt beschrieben , und sie sind ziemlich neugierig. Die ersten 15 Bits werden nicht verwendet, obwohl es PlĂ€ne gab, sie fĂŒr Warnsysteme und den Zivilschutz zu nutzen. Bit A1 zeigt an, dass die Uhren in der nĂ€chsten Stunde auf Sommerzeit umgestellt werden. Bit A2 zeigt an, dass in der nĂ€chsten Stunde eine , die manchmal zur Korrektur der Zeit gemÀà der Erdrotation verwendet wird, hinzugefĂŒgt wird. Die ĂŒbrigen Bits kodieren die Stunden, Minuten, Sekunden und das Datum.

FĂŒr alle, die selbst experimentieren möchten, ist der Code zum Dekodieren unter dem Spoiler aufgefĂŒhrt.
Quellcode
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 = ('Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag')
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)
Wenn wir das Programm ausfĂŒhren, werden wir etwa diese Ausgabe sehen:
0011110110111000001011000001010000100110010101100010011000
Dienstag, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Dienstag, 26.03.19, 21:42
Das ist eigentlich die ganze Magie. Der Vorteil eines solchen Systems liegt darin, dass die Dekodierung extrem einfach ist und auf jedem noch so einfachen Mikrocontroller durchgefĂŒhrt werden kann. Wir zĂ€hlen einfach die Pulsdauer, sammeln 60 Bits und am Ende jeder Minute erhalten wir die genaue Zeit. Im Vergleich zu anderen Methoden zur Zeitsynchronisation (zum Beispiel GPS oder, Gott bewahre, das Internet:) erfordert diese Radiosynchronisation praktisch keinen Strom â zum Beispiel funktioniert ein gewöhnliches HeimwettergerĂ€t etwa ein Jahr lang mit 2 AA-Batterien. Daher werden sogar Armbanduhren mit Radiosynchronisation hergestellt, ganz zu schweigen von Wand- oder StraĂenbahnhöfen.
Der Komfort und die Einfachheit von DCF ziehen auch Bastler an. FĂŒr nur 10-20 $ kann man ein fertiges Modul mit Antenne und fertigem EmpfĂ€nger sowie TTL-Ausgang kaufen, das man an Arduino oder einen anderen Controller anschlieĂen kann.

FĂŒr Arduino sind bereits passende . Es ist jedoch bekannt â egal was man mit einem Mikrocontroller macht, am Ende erhĂ€lt man entweder eine Uhr oder ein Wetterstation. Mit einem solchen GerĂ€t ist es tatsĂ€chlich nicht schwierig, die genaue Uhrzeit zu erhalten, vorausgesetzt, man befindet sich in Empfangsbereich. Man kann auch auf die Uhr den Schriftzug âAtomic Clockâ anbringen und gleichzeitig jedem, der es hören möchte, erklĂ€ren, dass das GerĂ€t tatsĂ€chlich mit Atomuhren synchronisiert wird.
Wer möchte, kann sogar die alten Uhren der GroĂmutter aufrĂŒsten, indem er einen neuen Mechanismus mit Funk-Synchronisation einbaut:

Einen solchen kann man bei ebay mit den SchlĂŒsselwörtern âRadio Controlled Movementâ finden.
Und schlieĂlich ein Lifehack fĂŒr diejenigen, die bis hierher gelesen haben. Selbst wenn in den nĂ€chsten paar tausend Kilometern kein einziger Radiosignal-Transmitter vorhanden ist, ist es nicht schwer, ein solches Signal selbst zu generieren. Im Google Play Store gibt es eine App mit dem Namen âDCF77 Emulatorâ, die ein Signal ĂŒber die Kopfhörer ausgibt. Laut Aussage des Autors, wenn man das Kabel der Kopfhörer um die Uhr wickelt, fangen sie das Signal ein (interessant, wie, denn normale Kopfhörer geben kein 77kHz-Signal aus, aber wahrscheinlich erfolgt der Empfang ĂŒber Harmonische). Bei mir auf Android 9 hat die App ĂŒberhaupt nicht funktioniert â es gab einfach keinen Ton (oder vielleicht habe ich ihn nicht gehört â schlieĂlich sind es 77kHz :), aber vielleicht hat jemand mehr GlĂŒck. Einige stellen sich ĂŒbrigens auch einen vollwertigen DCF-Signalgenerator her, den man recht einfach mit der gleichen Arduino oder ESP32 bauen kann:

(Quelle )
Fazit
Das DCF-System stellte sich als tatsĂ€chlich recht einfach und praktisch heraus. Mit einem einfachen und gĂŒnstigen EmpfĂ€nger kann man immer und ĂŒberall die genaue Uhrzeit haben, natĂŒrlich im Empfangsbereich. Ich denke, selbst trotz der allgegenwĂ€rtigen Digitalisierung und âInternet der Dingeâ werden solche einfachen Lösungen noch lange gefragt sein.
Quelle: habr.com
