Сегодня мы продолжим тему SDR-приема и обработки сигналов. Приемом аналогового ТВ я заинтересовался совершенно случайно, после вопроса одного из читателей. Однако это оказалось не так просто, из-за банального отсутствия образцов сигнала — во многих местах аналоговое ТВ уже отключено. Читатель даже прислал запись с RTL-SDR, однако ширина записи у RTL порядка 2МГц, в то время как полоса ТВ-сигнала занимает около 8МГц, и на записи было ничего не понятно. В итоге, тема была надолго заброшена, и наконец, только сейчас, в очередную поездку к родственникам я взял с собой SDRPlay, и настроившись на частоты ТВ-каналов, увидел на экране искомый сигнал.
Небольшая программа на Python, и все работает:

Для тех, кому интересны подробности, продолжение под катом.
Теория
В те давние послевоенные годы, когда о цифровой передаче сигналов знали только в секретных лабораториях, но люди уже хотели смотреть ТВ, существовали три конкурирующих аналоговых стандарта. Первым был американский NTSC (National Television System Committee), который разрабатывался еще с 40х годов, был «заточен» под американскую частоту сети 60Гц и имел вертикальное разрешение всего лишь в 486 строк. Чуть позже в Германии стал разрабатываться стандарт PAL (Phase Alternating Line), который был немного лучше американского (разрешение «целых» 576 строк и ориентированность на европейскую частоту сети 50Гц), и еще чуть позже появился французский SECAM (Séquentiel couleur à mémoire). В нем были устранены некоторые недостатки PAL, которые касались передачи цвета, ну и есть версия, что принятие двух стандартов также было политическим решением, чтобы жители одних стран не могли смотреть передачи из стран других (до единого Евросоюза и Шенгена было еще около 50 лет). Так или иначе, но весь мир оказался разделен примерно так:

Т.к. Хабр все-таки сайт русскоязычный, то в дальнейшем мы будем рассматривать именно SECAM, хотя если кто-то пришлет образец сигнала PAL, тоже было бы интересно.
Спектр SECAM, если верить старинным свиткам, выглядит следующим образом:

Слева, на частоте F0, находится амплитудно-модулированный яркостный (L) сигнал. Это фактически черно-белое изображение, которое до сих пор может быть показано на старом теплом и ламповом черно-белом ТВ. Проблема Legacy и наличия у пользователей старых девайсов существовала уже тогда, так что цветовой канал был добавлен отдельно, без потери совместимости со старыми телеприемниками. Два канала цвета передавались поочередно в частотной модуляции на частотах 4.25 и 4.406МГц. И наконец, еще выше по частоте отдельно передавался звук, также в частотной модуляции.
Кстати, с приемом ТВ в Петербурге есть забавный момент. Как отрапортовали российские СМИ, аналоговое ТВ было отключено еще в октябре:

Однако это касается лишь государственных каналов, коммерческие никто не принуждает отключать свое вещание. По крайней мере, на момент написания статьи (декабрь 2019) примерно 5-6 каналов еще доступны в «аналоге» прямо в центре Питера. Но сколько это продлится, неизвестно, так что желающим записать «для истории» образцы сигналов наверное все-таки стоит поторопиться.
Наконец, настала пора включить SDR и посмотреть, что мы имеем в реале:

Звуковой канал сложности не представляет, на него можно просто навестись «мышкой» в HDSDR, выбрать FM с шириной полосы порядка 50КГц и послушать. Мы начнем декодирование с канала яркостного, это позволит нам получить готовую «картинку».
Декодирование
Как было написано выше, сигналы яркости передаются в АМ. Чтобы не писать декодер самостоятельно, воспользуемся GNU Radio — перенесем спектр на нулевую частоту, запустим АМ-декодер и сохраним результат в файл.

Теперь мы можем открыть сохраненный файл в Python:
import numpy as np import matplotlib.pyplot as plt lum_data = np.fromfile("pal_lum.raw", dtype='int32') lum_data = -lum_data - 4700 fs = 9000000//2 x_time = np.linspace(0, len(lum_data)/fs, num=len(lum_data)) plt.plot(x_time, lum_data)
Мы видим на экране последовательность из 4х кадров.

Длина одного кадра 0.02с — это как раз 1/50 — кратно частоте сети 50Гц, сигналы которой служат в качестве «тактового генератора» (не забываем, что сигнал аналоговый). За каждый кадр передается 320 строк — развертка у нас черезстрочная, так что итоговая частота кадров составляет 25Гц.
Посмотрим отдельные строки подробнее:

Как можно видеть, началу каждой строки соответствует «синхроимпульс», затем размах сигнала соответствует текущим значениям яркости в данной строке. Все довольно просто, и вероятно, практически без изменений такой сигнал и подавался на электронно-лучевую трубку ТВ.
Остальное дело техники. Создаем в памяти изображение и копируем в него два кадра, т.к. развертка у нас черезстрочная. Размах сигнала не превышает +200, что позволяет нам записать эти значения напрямую как цвета RGB.
# Output image frame_size = fs*1//50 img_x, img_y = 320, 650 img_size = (img_y, img_x, 3) img_data = np.zeros(img_size, dtype=np.uint8) img_data.fill(255) frame_num = 0 # Frame #1 pos_x, pos_y = 0, 0 for px in range(frame_num*frame_size, (frame_num+1)*frame_size): val = lum_data[px] if val < 0: val = 0 if val > 255: val = 255 img_data[pos_y][pos_x] = (0, val, 0) pos_x += 1 if lum_data[px] <= 0 and lum_data[px+1] > 0: pos_x = 0 pos_y += 2 print("Scan lines 1:", pos_y) # Frame #2 pos_x, pos_y = 0, 0 for px in range((frame_num+1)*frame_size, (frame_num+2)*frame_size): val = lum_data[px] if val < 0: val = 0 if val > 255: val = 255 img_data[pos_y+1][pos_x] = (0, val, 0) pos_x += 1 if lum_data[px] <= 0 and lum_data[px+1] > 0: pos_x = 0 pos_y += 2 img_resized = cv2.resize(img_data, dsize=(3*img_x, img_y), interpolation=cv2.INTER_CUBIC) plt.imshow(img_resized, interpolation='nearest')
Как можно видеть, я использую переход через ноль для детектирования начала новой строки. Картинка оказалась сжатой по вертикали, это зависит в данном случае от частоты дискретизации SDR, в итоге я просто сделал ресайз в ширину.
Окончательный результат на анимации из 10 кадров (больше не принимает файловый архив хабра):

Заключение
Подобные стандарты интересно анализировать, т.к. во-первых, они довольно-таки простые для реализации, во-вторых, их изучение представляет еще и отчасти исторический интерес. Разумеется, цели сделать полноценный софтовый ТВ-тюнер у меня не было, так что код приведен в минимально-работоспособном виде.
Если оценки статьи будут положительны, во второй части можно будет рассмотреть работу с цветом, и вывести полноценную цветную картинку.
Для желающих поэкспериментировать самостоятельно, IQ-файл можно скачать по ссылке.
Всем удачных экспериментов.
ссылка на оригинал статьи https://habr.com/ru/post/482014/
Добавить комментарий