Одним из интересных протоколов передачи данных, разработанных в прошлом веке, является APT (автоматическая передача изображений). Он используется для передачи изображений земной поверхности из космоса, и, что гораздо интереснее для нас, радиолюбителям под силу прием сигналов APT.

Общая информация

Метеорологические спутники NOAA, которые мы попытаемся расшифровать, относятся к серии TIROS (телевизионные инфракрасные спутники наблюдения), первый из которых был запущен в 1960 году. В настоящее время в эксплуатации находятся 3 спутника (NOAA-15, NOAA-18). и NOAA-19, самый старый из которых, NOAA-15 работает с 1998 г.). Спутники обращаются вокруг Земли на высоте около 850 км и совершают один оборот примерно за 1,5 часа. На борту есть различные датчики, но нам будет интересно получать метеорологические изображения. И есть два варианта. Самый простой способ приема - получить аналоговый сигнал в формате APT на частоте 137 МГц. Спутники также передают изображения в формате HRPT (High-resolution Picture Transmission) на частоте 1,7 ГГц. Доступны HRPT-декодеры, но требуется антенна с высоким коэффициентом усиления, установленная на специальном трекере, что сложнее и дороже.

Согласно международным конвенциям, все метеорологические данные открыты, и любой может принимать сигналы NOAA. На Medium уже было несколько статей о приеме NOAA вот так, но все они обычно сводятся к подключите приемник, запустите программу, получите изображение, без объяснения того, как это работает на самом деле. Давайте попробуем спуститься на один логический уровень вниз и посмотреть, как работает обработка данных изнутри.

Давайте начнем.

Прием

Самый первый и очевидный. Спутник не геостационарный, он движется по небу, поэтому нам нужно дождаться времени приема. Самый простой способ - воспользоваться онлайн-сервисом n2yo.com, который позволяет рассчитать время пролета любого спутника. Вот пример NOAA 19:

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

Конечно, для приема радиосигнала вам понадобится приемник. Самая дешевая версия RTL-SDR V3 за 35 долларов подойдет для этой задачи, я использовал лучшую модель SDRPlay со следующими настройками:

Как видите, я установил значение decimation на максимум, что позволяет получить максимальный динамический диапазон. Уровни усиления LNA и Gain следует выбирать в зависимости от антенны. Спутники NOAA-15, NOAA-18 и NOAA-19 передают данные на частотах 137,620, 137,9125 и 137,100 МГц соответственно. Сам сигнал имеет полосу пропускания около 50 кГц, и если все было сделано правильно, в определенный момент времени сигнал должен появиться в спектре:

Интересно отметить наклон линий из-за эффекта Доплера - спутник летает вокруг земного шара, и это еще одно хорошее доказательство кривизны Земли;)

Что касается приема, то самое замечательное в спутниках NOAA то, что прием доступен для новичков. Сигнал в принципе можно услышать на любой антенне, даже на самой простой от телевизора, но для получения хорошей картинки очень важно качество антенны. При плохой антенне (как у меня :) картинка будет менее контрастной, но этого достаточно, чтобы продемонстрировать работу декодера. Приятную антенну на 137 МГц можно купить или сделать из сантехники и медных труб, пример можно посмотреть здесь. Однако тема статьи - цифровая обработка сигналов, а не DIY, желающие могут самостоятельно подобрать подходящую конструкцию антенны.

Итак, мы должны начать запись в нужное время, используя режим FM и полосу пропускания 50 кГц. В результате должен получиться файл WAV продолжительностью около 10 минут, периодические импульсы должны быть четко слышны. Теперь можно приступить к декодированию.

Расшифровка

Шаг 1. Загрузим файл WAV с помощью библиотеки scipy. Я показываю только фрагмент от 20 до 21 секунды, иначе рендеринг будет слишком долгим.

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

fs, data = wav.read('HDSDR_20201227_070306Z_137100kHz_AF.wav')
data_crop = data[20*fs:21*fs]
plt.figure(figsize=(12,4))
plt.plot(data_crop)
plt.xlabel("Samples")
plt.ylabel("Amplitude")
plt.title("Signal")
plt.show()

Должен быть виден периодический сигнал:

Шаг 2. Для ускорения декодирования уменьшим частоту дискретизации в 4 раза, отбросив ненужные значения:

resample = 4
data = data[::resample]
fs = fs//resample

Шаг 3. Изображение передается с амплитудной модуляцией, для преобразования в AM применим преобразование Гильберта:

def hilbert(data):
    analytical_signal = signal.hilbert(data)
    amplitude_envelope = np.abs(analytical_signal)
    return amplitude_envelope
data_am = hilbert(data)

Можем вывести картинку и убедиться, что у нас есть огибающая сигнала:

Шаг 4. Последний шаг. Собственно, расшифровка уже была закончена. Сами данные передаются в аналоговом формате, поэтому цвет каждого пикселя зависит от уровня сигнала. Мы можем «преобразовать» данные в 2D-изображение, из описания формата известно, что одна строка передается за 0,5 с:

from PIL import Image
frame_width = int(0.5*fs)
w, h = frame_width, data_am.shape[0]//frame_width
image = Image.new('RGB', (w, h))
px, py = 0, 0
for p in range(data_am.shape[0]):
    lum = int(data_am[p]//32 - 32)
    if lum < 0: lum = 0
    if lum > 255: lum = 255
    image.putpixel((px, py), (0, lum, 0))
    px += 1
    if px >= w:
        if (py % 50) == 0:
            print(f"Line saved {py} of {h}")
        px = 0
        py += 1
        if py >= h:
            break

Функция putpixel - не самый быстрый способ работы с изображениями, и код можно ускорить в 10 раз с помощью numpy.reshape и Image.fromarray , но так выглядит более понятным. Чтобы преобразовать амплитуду сигнала в диапазон яркости 0..255, значения делятся на 32, для другой антенны значение, возможно, придется изменить.

Для удобства просмотра изменим размер изображения и отобразим его:

image = image.resize((w, 4*h))
plt.imshow(image)
plt.show()

Если все было сделано правильно, у нас должно получиться примерно следующее:

Олдскульный зеленый цвет был выбран просто для развлечения, желающие могут изменить цветовую гамму, изменив параметры метода putpixel. Что мы видим на экране? В формате APT передаются два канала. Длинноволновое ИК-излучение (10,8 микрометра) передается на одну половину кадра, ближнее / средневолновое ИК-излучение (0,86 или 3,75 микрометра) передается на другую половину кадра. Режим выбирается в зависимости от того, передает ли спутник ночное или дневное изображение. В данных также есть временные маркеры и телеметрия, желающие могут увидеть описание формата APT более подробно (также может быть полезно описание на sigidwiki и pdf на сайте noaasis.noaa.gov). Программные декодеры могут использовать эти маркеры для настройки изображения, но это может не работать для слабых сигналов. Приведенный выше код никогда не выходит из синхронизации, поскольку синхронизации нет вообще, даже самый слабый сигнал будет виден, хотя и с меньшей контрастностью.

Заключение

Интересно отметить, что в мире не так много действующих систем радиосвязи, сигнал которых можно декодировать с помощью 20 строк кода. Спутникам NOAA около 20 лет, и когда они, наконец, выйдут из эксплуатации, новые, скорее всего, будут цифровыми, а формат будет гораздо более сложным (новый российский спутник Метеор-М2 уже передает цифровые данные на частоте 137 МГц). Так что тем, кто хочет попробовать что-то простое для расшифровки, можно посоветовать поторопиться.

Конечно, нет необходимости декодировать сигналы NOAA только с использованием Python, существует достаточно программных декодеров, которые могут выполнять эту работу и даже иметь приличные функции, такие как размещение границ стран и меток городов, рендеринг псевдоцветов и т. Д. Но это может быть тоже интересно сделать расшифровку с нуля.