DCF77: Jak działa system sygnalizacji czasu?

Witaj Habr.

Prawdopodobnie wielu nabywców zegarka lub stacji pogodowej widziało na opakowaniu zegar sterowany radiowo lub nawet zegar atomowy. Jest to bardzo wygodne, ponieważ wystarczy położyć zegar na stole, a po chwili automatycznie ustawi się on na dokładną godzinę.
DCF77: Jak działa system sygnalizacji czasu?

Zobaczmy, jak to działa i napiszmy dekoder w Pythonie.

Istnieją różne systemy synchronizacji czasu. Najpopularniejszy w Europie jest system niemiecki DCF-77, Japonia ma swój własny system JJY, w USA istnieje system WWVB, i tak dalej. Następnie opowieść będzie dotyczyć DCF77, jako najbardziej aktualnego i dostępnego do odbioru w niektórych miejscach europejskiej części Rosji i krajów sąsiednich (mieszkańcy Dalekiego Wschodu mogą mieć przeciwne zdanie, jednak oni z kolei mogą otrzymać i przeanalizuj sygnał japoński;).

Wszystko, co zostanie napisane poniżej, będzie dotyczyć DCF77.

Odbiór sygnału

DCF77 jest stacją długofalową pracującą na częstotliwości 77.5 kHz i nadającą sygnały w modulacji amplitudowej. Stacja o mocy 50 kW zlokalizowana jest 25 km od Frankfurtu, rozpoczęła działalność w 1959 r., a w 1973 r. do dokładnej godziny dodano datę. Długość fali przy częstotliwości 77 KHz jest bardzo duża, więc wymiary pola anteny też są całkiem przyzwoite (zdjęcie z Wikipedii):
DCF77: Jak działa system sygnalizacji czasu?

Przy takiej antenie i poborze mocy zasięg odbioru obejmuje niemal całą Europę, Białoruś, Ukrainę i część Rosji.

DCF77: Jak działa system sygnalizacji czasu?

Każdy może nagrać sygnał. W tym celu wystarczy udać się do odbiornika online http://websdr.ewi.utwente.nl:8901/, wybierz tam częstotliwość 76.5 KHz i modulację USB. Powinien otworzyć się obraz wyglądający mniej więcej tak:

DCF77: Jak działa system sygnalizacji czasu?

Tam wciskamy przycisk pobierania i nagrywamy kilkuminutowy fragment. Oczywiście, jeśli masz „prawdziwy” odbiornik zdolny do nagrywania częstotliwości 77.5 kHz, możesz go użyć.

Oczywiście odbierając radiowe sygnały czasu przez Internet, nie otrzymamy naprawdę dokładnego czasu – sygnał jest nadawany z opóźnieniem. Ale naszym celem jest jedynie zrozumienie struktury sygnału, w tym celu nagranie internetowe jest więcej niż wystarczające. W prawdziwym życiu do odbioru i dekodowania używane są oczywiście specjalistyczne urządzenia, które zostaną omówione poniżej.

Otrzymaliśmy więc nagranie, zacznijmy je przetwarzać.

Dekodowanie sygnału

Załadujmy plik za pomocą Pythona i zobaczmy jego strukturę:

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()

Widzimy typową modulację amplitudy:
DCF77: Jak działa system sygnalizacji czasu?

Aby uprościć dekodowanie, weźmy obwiednię sygnału za pomocą transformaty Hilberta:

analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])

Powiększony wynik:
DCF77: Jak działa system sygnalizacji czasu?

Wygładźmy emisję szumów za pomocą filtra dolnoprzepustowego, a jednocześnie obliczmy wartość średnią, która przyda się później do analizy.

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

Wynik (żółta linia): sygnał o fali prawie prostokątnej, który jest dość łatwy do analizy.
DCF77: Jak działa system sygnalizacji czasu?

Rozbiór gramatyczny zdania

Najpierw musisz uzyskać sekwencję bitów. Sama struktura sygnału jest bardzo prosta.
DCF77: Jak działa system sygnalizacji czasu?

Impulsy są podzielone na sekundy. Jeżeli odległość pomiędzy impulsami wynosi 0.1 s (tj. długość samego impulsu wynosi 0.9 s), do sekwencji bitów należy dodać „0”, jeżeli odległość wynosi 0.2 s (tj. długość wynosi 0.8 s), należy dodać „1”. Koniec każdej minuty sygnalizowany jest „długim” impulsem trwającym 2 s, sekwencja bitów jest resetowana do zera i napełnianie rozpoczyna się od nowa.

Powyższe można łatwo napisać w Pythonie.

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

W rezultacie otrzymujemy ciąg bitów, w naszym przykładzie przez dwie sekundy wygląda to tak:

0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000

Swoją drogą ciekawe, że sygnał ma też „drugą warstwę” danych. Sekwencja bitów jest również kodowana przy użyciu modulacja fazy. Teoretycznie powinno to zapewnić solidniejsze dekodowanie nawet w przypadku osłabionego sygnału.

Nasz ostatni krok: uzyskanie rzeczywistych danych. Bity przesyłane są raz na sekundę, więc mamy w sumie 59 bitów, w których zakodowanych jest całkiem sporo informacji:
DCF77: Jak działa system sygnalizacji czasu?

Bity są opisane w Wikipediai są dość ciekawi. Pierwsze 15 bitów nie jest używanych, choć planowano wykorzystać je w systemach ostrzegania i obronie cywilnej. Bit A1 wskazuje, że w ciągu następnej godziny zegar przejdzie na czas letni. Bit A2 wskazuje, że dodatkowy sekunda przestępna, który jest czasami używany do dostosowania czasu w zależności od obrotu Ziemi. Pozostałe bity kodują godziny, minuty, sekundy i datę.

DCF77: Jak działa system sygnalizacji czasu?

Dla tych, którzy chcą poeksperymentować samodzielnie, kod dekodujący podany jest pod spoilerem.
Kod źródłowy

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)

Kiedy uruchomimy program, zobaczymy dane wyjściowe podobne do tego:

0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42

Właściwie to cała magia. Zaletą takiego układu jest to, że dekodowanie jest niezwykle proste i można je wykonać na dowolnym, nawet najprostszym mikrokontrolerze. Po prostu liczymy długość impulsów, gromadzimy 60 bitów i na koniec każdej minuty otrzymujemy dokładny czas. W porównaniu do innych metod synchronizacji czasu (na przykład GPS, czy nie daj Boże Internet :), taka synchronizacja radiowa praktycznie nie wymaga prądu - np. zwykła domowa stacja pogodowa działa około roku na 2 bateriach AA. Dlatego nawet zegarki na rękę są produkowane z synchronizacją radiową, nie mówiąc już o zegarkach ściennych czy zegarkach ulicznych.

Wygoda i prostota DCF przyciągają także miłośników majsterkowania. Już za 10-20 dolarów można kupić gotowy moduł antenowy z gotowym odbiornikiem i wyjściem TTL, który można podłączyć do Arduino lub innego kontrolera.
DCF77: Jak działa system sygnalizacji czasu?

Już napisany dla Arduino gotowe biblioteki. Jednak już wiadomo, że niezależnie od tego, co zrobisz z mikrokontrolerem, otrzymasz albo zegar, albo stację pogodową. Dzięki takiemu urządzeniu ustalenie dokładnego czasu jest naprawdę łatwe, pod warunkiem oczywiście, że znajdujesz się w recepcji. Cóż, możesz powiesić na swoim zegarku napis „Zegar Atomowy” i jednocześnie wyjaśniać wszystkim, że urządzenie tak naprawdę jest zsynchronizowane za pomocą zegara atomowego.

Chętni mogą nawet ulepszyć zegarek swojej starej babci, instalując nowy mechanizm z synchronizacją radiową:

DCF77: Jak działa system sygnalizacji czasu?

Można go znaleźć w serwisie eBay, używając słów kluczowych „Ruch sterowany radiowo”.

I na koniec lifehack dla tych, którzy przeczytali tak daleko. Nawet jeśli w promieniu kilku tysięcy kilometrów nie ma ani jednego nadajnika sygnału radiowego, samodzielne wygenerowanie takiego sygnału nie jest trudne. W Google Play dostępny jest program o nazwie „Emulator DCF77”, który wysyła sygnał do słuchawek. Zdaniem autora, jeśli owiniemy przewód słuchawek wokół zegarka, to one odbiorą sygnał (ciekawe jak, bo zwykłe słuchawki nie wygenerują sygnału o częstotliwości 77 kHz, ale za odbiór odpowiadają prawdopodobnie harmoniczne). Na Androidzie 9 program w ogóle mi nie zadziałał - po prostu nie było dźwięku (a może go nie słyszałem - w końcu to 77 KHz:), ale może komuś się poszczęści. Niektórzy jednak robią z siebie pełnoprawny generator sygnału DCF, który łatwo wykonać na tym samym Arduino lub ESP32:

DCF77: Jak działa system sygnalizacji czasu?
(źródło sgfantasytoys.wordpress.com/2015/05/13/synchronize-radio-watch-control-without-access)

wniosek

System DCF okazał się naprawdę prosty i wygodny. Za pomocą prostego i taniego odbiornika możesz zawsze i wszędzie mieć dokładny czas, oczywiście w recepcji. Wydaje się, że nawet pomimo powszechnej cyfryzacji i Internetu Rzeczy na takie proste rozwiązania jeszcze długo będzie zapotrzebowanie.

Źródło: www.habr.com

Dodaj komentarz