Как стать автором
Обновить

День Радио: смотрим видео на радиоспектре через SDR

Время на прочтение5 мин
Количество просмотров11K


Привет, Хабр! На дворе День радио, а значит у нас есть отличный повод сделать что-нибудь интересное. На днях мой взгляд упал на пылившийся в углу SDR-приемник, и тут понеслось.


Вообще spectrum painting, или рисование картинок на SDR-спектрограмме — развлечение довольно старое и нехитрое: берем картинку, прогоняем каждую строку через обратное преобразование Фурье и строку за строкой посылаем получившийся сигнал в эфир. Вот, например, готовый проект на гитхабе, который сразу генерирует выходной файл для HackRF или BladeRF. Но вот у меня, скажем, нет ни HackRF, ни чего-либо похожего, а передать картинку хочется, да как-нибудь попроще. Как же быть?


В принципе, можно было бы использовать простенький лабораторный генератор или DDS, но обычно их выходной буфер не превышает нескольких килобайт, в то время, как видео легко может занять десятки мегабайт. Постойте, но ведь у нас под рукой и так есть отличный девайс для воспроизведения длинных файлов — аудиовыход компьютера! Остается взять генератор несущей радиочастоты (в моем случае это 27 МГц) и промодулировать ее записью с компьютера на смесителе.


Смеситель (mixer) — это нелинейный элемент с двумя входами и одним выходом, который складывает или вычитает друг из друга входные частоты. Когда мы хотим послушать радио на 101.6 МГц на карманном приемнике, тот выставляет частоту гетеродина на 101.5 МГц и вычитает ее из несущей при помощи смесителя, понижая частоту до 100 кГц и завершая демодуляцию на ней. Мы будем делать то же самое, только в другую сторону: прибавим 27 МГц несущей к 10-20 кГц аудиосигнала, в котором закодирована картинка. На языке SDR-спектрограмм это означает, что картинка просто сместится вверх по частоте на 27 МГц.



Простейший смеситель можно собрать из диодов. Но мне было лень, и я взял какой-то готовый от MiniCircuits.



Белая коробочка — это и есть смеситель. Слева от него Red Pitaya в роли генератора несущей.


Тут начинаются нюансы. Первый заключается в том, что аудиокарта выдает сигнал только до 20 кГц, а промышленные смесители не оптимизированы для таких низких частот. Чтобы получить приемлемый сигнал на выходе, придется усилить сигналы на входе, что неизбежно приведет к нелинейностям — иными словами, генерации суммарных и разностных частот из самого аудиосигнала:



Это ограничивает рабочий диапазон от 10 до 20 кГц: выше 20 кГц не работает аудиокарта, ниже 10 кГц появляется разностный сигнал от аудиочастот (мы его еще увидим).


Второй нюанс заключается в том, что преобразование Фурье — комплексное, а в частотном, да и временном представлении сигнала существенную роль играют не только интенсивности, но и фазы. На практике чаще всего работают не с комплексными числами, а с IQ-представлением в двух ортогональных квадратурах, повернутых на 90 градусов. Например, в SDR-приемнике стоят два смесителя, которые сбивают сигнал с частотой гетеродина, повернутой на 90 градусов:



А в передатчике HackRF стоит микросхема MAX2839, которая сбивает I и Q компоненты сигнала с двумя ортогональными несущими на двух смесителях, суммирует их выходные сигналы и отправляет их дальше в антенный тракт:



В нашей поделке есть только один смеситель, а значит мы передаем сигнал только в одной квадратуре, то есть без учета фаз. Фурье-преобразование такого сигнала симметрично, а значит, помимо заветной картинки справа от несущей мы также увидим ее зеркальную реплику слева от несущей, и потеряем половину интенсивности. Это не страшно, но сигнал немного ослабит.


Переходим к вычислениям. Давайте передавать картинку стандартным битрейтом в 44100 сэмплов в секунду (профессор Котельников напоминает нам, что для 20 кГц больше и не понадобится), а одну строку картинки будем передавать за 2048 точек. Из-за вышеупомянутых нелинейных эффектов и зеркальной реплики мы можем использовать только четверть из них, но вообще лучше сделать картинку еще меньше. Пусть будет 256 пикселей.



Вот, в принципе, и все. Код очень похож на spectrum painter


А вот и сам код
import numpy as np
import scipy
import imageio.v2 as img

filename = 'img.png'
sr = 44100 # Hz
nfft = 2048
start_pix = nfft-256-1-256 # from which the image will be embedded

# Load the image
pic = np.float32(img.imread(filename)[:,:,0])

# Embed image starting from the given pixel
fftall = np.zeros((ffts.shape[0], nfft))
fftall[:, start_pix:(start_pix+pic.shape[1])] = pic

# Generate random phase vectors for the FFT bins
# This is important to prevent high peaks in the output
phases = 2*np.pi*np.random.rand(*fftall.shape)
rffts = fftall * np.exp(1j*phases)

# Perform the FFT per image line
ifft = np.fft.ifft(np.fft.ifftshift(rffts, axes=1), axis=1) / np.sqrt(float(nfft))

# Concatenate lines to form the final signal, take one quadrature, and normalize
signal = normalize(np.real(ifft.flatten()))
signal_norm = np.float32(signal / max(signal.max(), -signal.min()))

# Write the signal to a wav file
scipy.io.wavfile.write(f'out.wav', sr, signal_norm)

Ну что, пора запустить Doom? А вот и нет: Doom запускают программисты. Инженеры запускают Bad Apple!



Что еще видно в спектре

Как я и говорил выше, реальный сигнал без мнимой компоненты дает зеркальное изображение относительно несущей, в точности как при амплитудной модуляции. Хотя оно оказалось заметно тусклее, чем ожидалось, вероятно, из-за нелинейностей. По той же причине появляется шум рядом с несущей (на разностях частот из картинки) и выше 22 кГц (на их второй гармонике).



Выглядит неплохо, но я здорово ускорил это видео. На самом деле передача одного кадра занимает почти 8 секунд! А можно ли сделать что-то более похожее на настоящее видео? Хотя бы пять кадров в секунду? Давайте считать.


Имеет ли смысл увеличивать sampling rate (SR) передатчика? Он определяет максимальную ширину спектра, в то время, как наша картинка занимает ширину Δf. При ширине картинки в W пикселей полная ширина спектра составит



После преобразования Фурье количество точек останется таким же, а значит, на передачу одной строчки изображения уйдет



Ага, то есть время на передачу одной строки вообще не зависит от sampling rate! А значит, увеличивать его никакого смысла нет. Время на передачу всего кадра размером W х H составит



Получается, для T = 0.2 с (5 кадров в секунду) и Δf = 20 — 10 кГц = 10 кГц один кадр не сможет превышать где-то 40 х 40 пикселей. В принципе с этим уже можно работать.


И, наконец, самый главный вопрос: а можно ли действительно смотреть видео на SDR, а не просто проматывать пленку? А почему бы и нет: например, можно вставить в спектр синхроимпульсы перед началом каждого кадра и написать простенький интерфейс для приемника, который будет их распознавать. А можно просто взять другую программу для работы с SDR. Например, SDRAngel перерисовывает спектрограмму заданое количество раз в секунду:



Остаeтся подобрать sampling rate так, чтобы один кадр занимал ровно одну пятую секунды, и чтобы видео не плыло вверх-вниз как на непрогретом ламповом телевизоре. Размер моего кадра составляет 51х32 пикселя, поэтому для преобразования Фурье хватит длины выборки длиной 256 (чуть больше 51х4). В этом случае sampling rate будет равен 256 x 32 x 5 = 40 960 сэмплов в секунду.



Ну что, попробуем?



С Днем Радио! Всем 73!

Теги:
Хабы:
+124
Комментарии18

Публикации

Истории

Ближайшие события