DCF77: How does the accurate time signal transmission system work?

    Hi Habr.

    Probably many people who buy a watch or weather station saw the Radio Controlled Clock logo or even Atomic Clock on the packaging. This is very convenient, because it is enough to put the clock on the table, and after a while they will automatically adjust to the exact time.

    Let's see how it works and write a decoder in Python.

    There are different time synchronization systems. The most popular in Europe is the German DCF-77 system , in Japan there is its own JJY system , in the USA there is a WWVB system, etc. Further, the story will be about DCF77, as the most relevant and available for reception in some places in the European part of Russia and neighboring countries (residents of the Far East may have the opposite opinion, however they, in turn, can receive and analyze the Japanese signal;).

    Everything written below will be about DCF77.

    Signal reception

    DCF77 is a longwave station operating at a frequency of 77.5KHz, and transmitting signals in amplitude modulation. The station with a capacity of 50 kW is located 25 km from Frankfurt, it began work back in 1959, in 1973 the date information was added to the exact time. The wavelength at a frequency of 77KHz is very large, so the dimensions of the antenna field are also very decent (photo from Wikipedia):

    With such an antenna and power input, the reception area covers almost the whole of Europe, Belarus, Ukraine and part of Russia.

    Everyone can record a signal. To do this, just go to the online receiver http://websdr.ewi.utwente.nl:8901/ , select the frequency there 76.5KHz and USB-modulation. A picture of something like this should open:

    There we press the download button and record a fragment several minutes long. Of course, if you have a “real” receiver capable of recording a frequency of 77.5KHz, you can use it.

    Of course, when we receive radio signals of the exact time via the Internet, we will not get really accurate time - the signal is transmitted with a delay. But our goal is only to understand the structure of the signal, for this Internet recording is more than enough. In real life, of course, specialized devices for receiving and decoding are used, they will be discussed below.

    So, we got the record, let's start processing it.

    Signal decoding

    Download the file using Python and see its structure:
    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")

    We see a typical amplitude modulation:

    To simplify decoding, we take the envelope of the signal using the Hilbert transform:

    analytic_signal = signal.hilbert(data)
    A = np.abs(analytic_signal)

    The result in an enlarged form:

    We smooth out emissions from interference using a low-pass filter, at the same time we calculate the average value, it will come in handy later for 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

    Result (yellow line): an almost rectangular signal that is fairly easy to analyze.


    First you need to get the bit sequence. The signal structure itself is very simple.

    Pulses are divided into second intervals. If the distance between pulses is 0.1 s (ie the length of the pulse itself is 0.9 s), add “0” to the bit sequence, if the distance is 0.2 s (ie the length is 0.8 s), add “1”. The end of each minute is indicated by a “long” pulse, 2s long, the bit sequence is reset to zero, and filling starts again.

    The above is easy to write 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:
                bits_str = ""
        pos += 1

    As a result, we get a sequence of bits, in our example for two minutes it looks like this: By the way, it’s interesting that the signal also has a “second layer” of data. The bit sequence is also encoded using phase modulation . Theoretically, this should provide more stable decoding even in the case of a weakened signal. Our last step: get the actual data. Bits are transmitted once per second, so we have only 59 bits in which a lot of information is encoded: The bits are described on Wikipedia , and they are quite curious. The first 15 bits are not used, although there were plans to use for warning systems and civil defense


    . Bit A1 indicates that in the next hour the clock will be set to daylight saving time. Bit A2 indicates that an extra second will be added in the next hour , which is sometimes used to correct time in accordance with the rotation of the Earth. The remaining bits encode hours, minutes, and date.

    For those who want to experiment on their own, the code for decoding is given under the spoiler.

    def decode(bits):
        if bits[0] != '0' or bits[20] != '1':
        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)

    Running the program, we will see something like this conclusion: Actually, that’s all magic. The advantage of such a system is that decoding is extremely simple, and can be done on any, the most uncomplicated microcontroller. Just count the length of the pulses, accumulate 60 bits, and at the end of each minute we get the exact time. Compared to other methods of time synchronization (GPS, for example, or God forbid, the Internet :), such radio synchronization practically does not require electricity - for example, an ordinary home weather station works for about a year from 2 AA batteries. Therefore, even a wristwatch is made with radio synchronization, not to mention of course, about a wall or street station.

    Tuesday, 26.03.19, 21:41
    Tuesday, 26.03.19, 21:42

    The convenience and simplicity of DCF attract lovers of homemade products. For only $ 10-20, you can buy a ready-made module from an antenna with a ready-made receiver and TTL-output, which can be connected to an Arduino or other controller.

    For Arduino, ready-made libraries have already been written . However, it’s already known that no matter what you do on the microcontroller, you get either a clock or a weather station. With such a device, getting the exact time is really easy, provided you are in the reception area. Well, you can hang the inscription “Atomic Clock” on the watch, and at the same time explain to everyone who wants it, that the device is really synchronized using an atomic clock.

    Those who wish can even upgrade their old grandmother’s watches by installing a new mechanism with radio synchronization in them:

    You can find one on ebay using the keywords “Radio Controlled Movement”.

    And finally, a life hack for those who have read here. Even if there is not a single transmitter of a radio signal in the next couple of thousand km, such a signal is easy to generate independently. Google Play has a program called "DCF77 Emulator" that outputs a signal to the headphones. According to the author, if you wrap the headphone wire around the watch, they will catch a signal (I wonder how, because ordinary headphones will not give a 77KHz signal, but probably the reception comes from harmonics). My program didn’t work on Android 9 at all - there was simply no sound (or maybe I didn’t hear it - 77KHz, after all :), but maybe someone will be more lucky. Some, however, make themselves a full-fledged DCF signal generator, which is easy to do on the same Arduino or ESP32:

    (sourcesgfantasytoys.wordpress.com/2015/05/13/synchronize-radio-controlled-watch-without-access )


    The DCF system turned out to be really quite simple and convenient. With the help of a simple and cheap receiver, you can have the exact time anytime, anywhere, of course, in the reception area. It seems that even despite the widespread digitalization and the “Internet of things”, such simple solutions will be in demand for a long time to come.

    Also popular now: