Хабрахабр

DCF77: как работает система передачи сигналов точного времени?

Привет Хабр.

Это весьма удобно, ведь достаточно поставить часы на стол, и они через некоторое время автоматически настроятся на точное время.
Наверное многие, приобретающие часы или метеостанцию, видели на упаковке логотип Radio Controlled Clock или даже Atomic Clock.

Наиболее популярная в Европе — немецкая система DCF-77, в Японии есть своя система JJY, в США есть система WWVB, и так далее. Разберемся как это работает и напишем декодер на языке Python.
Существуют разные системы синхронизации времени. Далее рассказ будет о DCF77, как о наиболее актуальной и доступной для приема в некоторых местах европейской части России и соседних странах (у жителей Дальнего Востока может быть противоположное мнение, впрочем они в свою очередь могут принять и проанализировать японский сигнал;).

Все написанное далее, будет про DCF77.

Прием сигнала

DCF77 это длинноволновая станция, работающая на частоте 77.5КГц, и передающая сигналы в амплитудной модуляции. Станция мощностью 50КВт расположена в 25км от Франкфурта, она начала работу еще в 1959 году, в 1973 к точному времени была добавлена информация о дате. Длина волны при частоте 77КГц весьма большая, поэтому размеры антенного поля тоже весьма приличные (фото из Википедии):

При такой антенне и подводимой мощности, зона приема охватывает практически всю Европу, Белоруссию, Украину и часть России.

Для этого достаточно зайти на онлайн-приемник http://websdr.ewi.utwente.nl:8901/, выбрать там частоту 76. Записать сигнал может каждый. Должна открыться картинка примерно типа такой: 5КГц и USB-модуляцию.

Разумеется, при наличии «настоящего» приемника, способного записать частоту 77. Там же нажимаем кнопку download и записываем фрагмент длиной в несколько минут. 5КГц, можно использовать и его.

Но наша цель лишь понять структуру сигнала, для этого интернет-записи более чем достаточно. Конечно, принимая радиосигналы точного времени через Интернет, мы не получим действительно точное время — сигнал передается с задержкой. В реале конечно, используются специализированные устройства для приема и декодирования, о них будет сказано ниже.

Итак, мы получили запись, приступим к ее обработке.

Декодирование сигнала

Загрузим файл с помощью Python и посмотрим его структуру:

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

Мы видим типичную амплитудную модуляцию:

Для упрощения декодирования возьмем огибающую сигнала с помощью преобразования Гильберта:

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

Результат в увеличенном виде:

Сгладим выбросы от помех с помощью low-pass фильтра, заодно вычислим среднее значение, оно пригодится потом для парсинга.

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

Результат (желтая линия): практически прямоугольный сигнал, который довольно легко анализировать.

Парсинг

Сначала нужно получить битовую последовательность. Сама структура сигнала очень проста.

Если расстояние между импульсами составляет 0. Импульсы поделены на секундные интервалы. длина самого импульса 0. 1с (т.е. 2с (т.е. 9с), к битовой последовательности добавляем «0», если расстояние составляет 0. 8с), добавляем «1». длина 0. Конец каждой минуты обозначается «длинным» импульсом, длиной 2с, битовая последовательность при этом обнуляется, и заполнение начинается заново.

Вышенаписанное несложно записать на языке 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

В результате получаем последовательность бит, в нашем примере для двух секунд она выглядит так:

0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000

Последовательность бит также закодирована с помощью фазовой модуляции. Кстати интересно, что в сигнале есть и «второй слой» данных. Теоретически, это должно обеспечивать более устойчивое декодирование даже в случае ослабленного сигнала.

Биты передаются раз в секунду, так что мы имеем всего 59 бит, в которых закодировано достаточно много информации:
Наш последний шаг: получить собственно данные.

Первые 15 бит не используются, хотя были планы использовать для систем оповещения и гражданской обороны. Биты описаны в Википедии, и они довольно любопытны. Бит А2 указывает, что в следующий час будет добавлена дополнительная секунда, которая иногда используется для коррекции времени в соответствии с вращением Земли. Бит A1 указывает на то, что в следующий час часы будут переведены на летнее время. Остальные биты кодируют часы, минуты, секунды и дату.

Для тех, кто захочет поэкспериментировать самостоятельно, код для декодирования приведен под спойлером.

Исходный код

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(', {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)

Запустив программу, мы увидим примерно такой вывод:

03. 0011110110111000001011000001010000100110010101100010011000
Tuesday, 26. 03. 19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26. 19, 21:42

Плюс такой системы в том, что декодирование чрезвычайно простое, и может быть сделано на любом, самом несложном микроконтроллере. Собственно, вот и вся магия. По сравнению с другими способами синхронизации времени (GPS например, или не дай бог, Интернет:), такая радиосинхронизация практически не требует электроэнергии — для примера, обычная домашняя метеостанция работает около года от 2х батареек АА. Просто считаем длину импульсов, накапливаем 60 бит, и в конце каждой минуты получаем точное время. Поэтому с радиосинхронизацией делают даже наручные часы, не говоря уже конечно, о настенных или об уличных вокзальных.

Всего за 10-20$ можно купить готовый модуль из антенны с готовым приемником и TTL-выходом, который можно подключить к Arduino или другому контроллеру.
Удобство и простота DCF привлекают и любителей самоделок.

Впрочем, и так известно — что ни делай на микроконтроллере, получаются либо часы, либо метеостанция. Для Arduino уже написаны и готовые библиотеки. Ну и можно повесить на часы надпись «Atomic Clock», и заодно объяснять всем желающим, что устройство действительно синхронизируется с помощью атомных часов. С таким устройством получать точное время действительно несложно, если конечно находиться в зоне приема.

Желающие могут даже проапгрейдить старые бабушкины часы, установив в них новый механизм с радиосинхронизацией:

Найти такой можно на ebay по ключевым словам «Radio Controlled Movement».

Даже если в ближайших паре тысяч км нет ни одного передатчика радиосигнала, такой сигнал несложно сгенерировать самостоятельно. И наконец, лайфхак для тех, кто дочитал досюда. По заверениям автора, если обмотать провод наушников вокруг часов, они поймают сигнал (интересно как, ведь обычные наушники не выдадут сигнал 77КГц, но вероятно прием идет за счет гармоник). В Google Play есть программа с названием «DCF77 Emulator», которая выводит сигнал на наушники. Некоторые впрочем, делают себе и полноценный генератор сигналов DCF, который несложно сделать на той же Arduino или ESP32: У меня на Android 9 программа не заработала совсем — просто не было звука (а может я его не слышал — 77КГц ведь:), но может кому-то повезет больше.


(источник sgfantasytoys.wordpress.com/2015/05/13/synchronize-radio-controlled-watch-without-access)

Заключение

Система DCF, оказалась действительно вполне простой и удобной. С помощью несложного и дешевого приемника можно иметь точное время всегда и везде, разумеется в зоне приема. Думается, даже несмотря на повсеместную цифровизацию и «интернет вещей», такие простые решения будут востребованы еще долго.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть