Принимаем и декодируем передачи SSTV с МКС

PythonProgrammingDevelopment of communication systemsPopular scienceAstronautics
Tutorial

Привет, Хабр.

Как было написано в недавнем анонсе, с 24 по 31 декабря производится передача изображений формата SSTV с МКС. Передача идет в радиолюбительском диапазоне на частоте 145.800 МГц и принять её может любой желающий.

МКС (c) https://en.wikipedia.org/wiki/International_Space_Station
МКС (c) https://en.wikipedia.org/wiki/International_Space_Station

Посмотрим, как это работает и как такой сигнал можно декодировать.

Общие сведения

Передача изображений с МКС производится в рамках проекта "О Гагарине из космоса", вполне благородная цель которого - привлечь внимание к космонавтике. Передачи осуществляются несколько раз в год, любой желающий в любой точке мира может принять изображения формата SSTV на частоте 145.8 МГц. SSTV (Slow-scan television) - это формат передачи изображений с малой скоростью. Сам формат появился весьма давно, таким способом передавались еще изображения обратной стороны Луны со станции Луна-3. Гораздо позже SSTV нашел популярность у радиолюбителей, т.к. позволял передавать изображения на коротких волнах при помощи обычного радиопередатчика. С некоторыми изменениями формат SSTV (а точнее, несколько, их около десятка разновидностей) дожил и до наших дней. Когда-то прием таких изображений был доступен лишь на специальной и дорогостоящей аппаратуре, сейчас это можно сделать с помощью компьютера, бесплатной программы-декодера и приемника RTL-SDR ценой 35$. Изображения могут приниматься как на КВ, так и на УКВ, дальше речь пойдет только об SSTV с МКС.

Прием

Т.к. сигналы передаются с космической станции, первым делом мы должны узнать точное время приема. Когда-то давно для этого использовали программу Orbitron, но сейчас гораздо проще открыть сайт n2yo.com и посмотреть ближайшее время пролета.

На частоты в правом верхнем углу можно не смотреть, они к SSTV отношения не имеют. Нам нужна частота 145.800 МГц, разумеется нужен приемник, подойдет например самый дешевый RTL-SDR V3. За 35$ можно купить такой набор, который вполне подойдет для первого знакомства с радио:

Если все было сделано правильно, мы должны увидеть примерно такой сигнал:

Для записи нужно использовать частотную модуляцию, но можно декодировать и в реальном времени, если использовать программу virtal audio card для перенаправления звука из SDR в программу-декодер.

Декодирование

Существует много программ для декодирования SSTV, которые позволяют сделать всё в 2-3 клика мышью. Мне все же интересен сам формат передачи, поэтому посмотрим как его можно декодировать вручную.

Для начала можно посмотреть на спектр сигнала:

SSTV это аналоговый формат, строки передаются последовательно, длительность передачи одной строки в PD-120 (в этом формате идут передачи с МКС) составляет 0.5с. Используется частотная модуляция, где частота меняется в зависимости от яркости и уровню черного соответствует 1500 Гц, а уровню белого 2300 Гц.

Загрузим изображение и используем band-pass фильтр, чтобы убрать все лишнее:

import scipy.io.wavfile as wav
import scipy.signal as signal
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image


fs, data = wav.read('HDSDR_20201228_075406Z_145803kHz_AF.wav')

def butter_bandpass(lowcut, highcut, fs, order=5):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq
    b, a = signal.butter(order, [low, high], btype='band')
    return b, a


def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
    b, a = butter_bandpass(lowcut, highcut, fs, order=order)
    y = signal.lfilter(b, a, data)
    return y

data1 = butter_bandpass_filter(data, 1400, 2200, fs, order=4)

Применим к изображению частотный демодулятор. Я не нашел готовой библиотеки, поэтому просто вычисляю частоту напрямую, по переходам через ноль:

data_fsk = np.zeros(len(data1))

pos1 = 0
for p in range(0, len(data1)-1):
    if np.sign(data1[p]) != np.sign(data1[p+1]):
        pr = p - pos1
        data_fsk[pos1:p] = np.full(p - pos1, pr)
        pos1 = p

"Развернем" данные в виде 2D-изображения:

frame_width = int(0.5*fs) + 203
w, h = frame_width, data_fsk.shape[0]//frame_width
image = Image.new('RGB', (w, h))

data_2d = data_fsk[:w*h].reshape((h,w))
for py in range(h):
    for px in range(w):
        lum = int(data_2d[py][px]*16)
        if lum < 0: lum = 0
        if lum > 255: lum = 255
        image.putpixel((px, py), (lum, lum, lum))

Выведем изображение на экран:

plt.imshow(image)
plt.show()

Мы видим довольно любопытную картинку. В одной строке передается 4 кадра. Это цветное изображение формата YCrCb, точнее сказать, Y1CrCbY2 - в одной линии передаются каналы яркости для двух строк и общий канал цвета, таким образом, цветовое разрешение картинки вдвое меньше чем яркостное. Итоговое разрешение составляет 640х480 - всего передается 240 линий, но в каждой линии, как можно видеть, хранится 2 строки.

Кстати, одна строка передается за 0.5с, что при 240 строках дает время передачи 120с, т.е. 2 минуты. Название формата PD-120 также указывает нам на эту величину.

Далее остается выполнить преобразование YCrCb => RGB:

image_rgb = Image.new('RGB', (w//4, 2*h))
for py in range(h):
    for px in range(int(0.125*fs)):
            # PD-120 – 640×480, 190 ?s/pixel
            k = 32
            y0 = 255 - k*data_2d[py][px]
            cr = 255 - k*data_2d[py][px + int(0.1216*fs)]
            cb = 255 - k*data_2d[py][px + 2*int(0.1216*fs)]
            y1 = 255 - k*data_2d[py][px + 3*int(0.1216*fs)]
            image_rgb.putpixel((px, 2*py), (int(y0 + 1.402 * cr), int(y0 - 0.34414 * cb - 0.71414 * cr), int(y0 + 1.772 * cb)))
            image_rgb.putpixel((px, 2*py + 1), (int(y1 + 1.402 * cr), int(y1 - 0.34414 * cb - 0.71414 * cr), int(y1 + 1.772 * cb)))

Это простейший код, без какого-либо выравнивания границ и нормализации уровней, некоторые цвета различить вполне можно:

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

Заключение

Как можно видеть, все не так уж сложно и вполне интересно. Желающие могут попробовать принять изображения с МКС самостоятельно, судя по анонсам, передача продлится до 31 декабря (и будем надеяться, традиция продолжится и в следующем году). Разумеется, не обязательно использовать Python, готовые декодеры SSTV можно найти для любой платформы.

Также стоит поблагодарить всех участников программы ARISS (Amateur Radio on the International Space Station) за проведение и поддержку подобных передач. Это отличная возможность для радиолюбителей, школьников и студентов прикоснуться к освоению космоса.

Tags:МКСISSSSTVham radio
Hubs: Python Programming Development of communication systems Popular science Astronautics
+25
5.8k 39
Comments 28

Popular right now

Python для веб-разработки
March 5, 202159,400 ₽SkillFactory
Python QA Engineer
March 16, 202160,000 ₽OTUS
Специалист по Data Science
March 18, 2021104,000 ₽Яндекс.Практикум
Python-разработчик
March 23, 202199,000 ₽Яндекс.Практикум

Top of the last 24 hours