Простой SDR в помощь ICOM 9700
Недавно увлёкся приёмом телеметрии и SSTV со спутников, благо сейчас энтузиасты организуют различные квесты и акции, используя ресурсы спутников «UMKA-1 (RS40S)», «VIZARD-METEO (RS38S)», «NANOZOND-1 (RS49S)» и «UTMN-2 (RS27S)». Подробнее о них можно посмотреть на сайте R4UAB.
Для приёма телеметрии спутников обычно достаточно простого приемника RTL-SDR, но я уже старый радиолюбитель и обзавёлся навороченным японским трансивером ICOM IC-9700, с помощью которого и собирался заняться приёмом. Но тут вышел облом — трансивер как-то обрабатывает выходной аудиосигнал, что его уже нельзя декодировать пакетным модемом. Выход из положения был найден в создании своего простого SDR приёмника. Подробности под катом.
Для решения проблемы с сигналом нужно было как-то отрегулировать выходные аудиофильтры трансивера, но такой возможности не оказалось. Трансиверы Icom IC-9700 и Icom IC-705 при работе в режиме FM обладают только стандартным набором из 3 фильтров, которые никак не регулируются. Этот путь оказался тупиковым.
Но выяснилась другая возможность — у этих трансиверов есть способ выводить вместо звука сигнал промежуточной частоты приёмного тракта. Частота ПЧ составляет 12 кГц, что очень удобно для последующей обработки. Обычно пользователи Icom не используют эту возможность, но мне она оказалась очень кстати.
Стоит отметить, что поиск на форумах информации о том, как работать с этой ПЧ, создал больше вопросов, чем ответов. Одни пишут, что в стерео режиме выводится сигнал IQ, другие пишут, что это просто ПЧ трансивера. В итоге пришлось идти путём проб и ошибок. Я взял программу HDSDR, настроил её так, чтоб она получала входной сигнал с аудиокарты трансивера, подключенного по USB. Установил приём на частоту 12 кГц в режиме ЧМ и случилось чудо — всё прекрасно декодировалось и я смог принимать FM станции. Настроив выход HDSDR на виртуальный аудиокабель и подключив его выход к UZ7HO SoundModem удалось-таки декодировать пакеты со спутников. На этом можно было бы и успокоиться, но использование HDSDR для этой цели мне показалось избыточным. У программы много настроек, легко что-то сбить и не заметить, да и места на экране она занимает порядочно. В итоге решил сделать свой минималистичный SDR приёмник, который будет служить лишь одной цели — помогать Icom 9700 в приёме пакетов со спутников.
Ранее я не писал сам SDR приёмники, поскольку взгляд в сторону учебников по цифровой обработке сигналов вызывал у меня благоговейный ужас. У меня создалось впечатление, что, не прочитав минимум пару сотен страниц формул и не разобравшись во всех этих зонах Найквиста, преобразованиях Гильберта и Фурье, невозможно сделать что-то полезное. Однако это не совсем так. При ближайшем рассмотрении оказалось, что для реализации всех этих преобразований нужно совсем мало очень простого кода. Или вот, например, для создания цифровых фильтров в учебниках пишут, что нужно обязательно взять что-то типа Matlab или SkiPy, там провести моделирование и только потом закодировать цифровой фильтр. Конечно, можно всё делать именно так, но, как выяснилось, в современных библиотеках уже есть готовые функции, которые сразу синтезируют «достаточно хороший» цифровой фильтр без необходимости предварительного моделирования в научных пакетах.
Я решил написать всё на Rust и по этой причине сразу отправился на crates.io искать подходящие библиотеки. Трудно представить, что до меня никто не решал задач создания SDR, поэтому я надеялся найти на этом сайте всё необходимое. И не ошибся. Для работы со звуковыми картами есть крейт CPAL, который, кстати, может работать в разных операционных системах. С помощью функций этого крейта я организовал ввод ПЧ и вывод декодированного звука. Для реализации приёмника есть крейт с незамысловатым названием SDR. О нём я рассажу поподробнее.
В крейте SDR есть функции смесителей, фильтров и нужного мне декодера FM. Я уже было совсем обрадовался, но рано. Как оказалось при детальном рассмотрении, функции смесителей не работают с вещественными числами, а качество работы декодера FM меня не устроило, да и не смог я найти к нему внятного описания в книгах по ЦОС. Пока я искал как декодировать FM сигнал, я нашёл 4 разных алгоритма, но ни один из них не был похож на тот, что используется в крейте SDR. В итоге я остановился на таком варианте декодера:
При всей непонятности нарисованного, всё это описывается буквально пятью строками кода.
Перед тем как декодировать сигнал, его сначала нужно перенести с несущей ПЧ в 12 кГц в 0 и отфильтровать. При этом образуются так не любимые обычными радиолюбителями сигналы с отрицательной частотой. Лучше даже не пытаться понять, что это такое:). А вот так выглядит подготовка сигнала к декодированию.
Она включает смешивание с сигналами 12 кГц с последующей фильтрацией ФНЧ. Поскольку частота дискретизации у нас 48 кГц, а частота несущей 12 кГц, т.е. ¼, то используется простой хак, когда сигнал для перевода в вид IQ просто умножается на последовательность 0, 1, -1, что работает просто и быстро. Схема в реальности занимает больше места, чем код, который её реализует.
О фильтрах. Это единственное, что в итоге я использовал из крейта SDR. Для создания ФНЧ нужно знать количество коэффициентов фильтра и частоту среза. Количество коэффициентов было оценено по эвристикам и ещё подсмотрено в проектах других SDR, поэтому обошёлся без Matlab. С частотами среза цифровых фильтров FIR я столкнулся впервые. Они задаются не фактической частотой, а долей от частоты дискретизации. В нашем случае это 48 кГц. Фильтры для сигналов IQ должны обеспечить полосу пропускания 12 кГц, но половина этой полосы находится в области отрицательных частот, поэтому частота среза фильтров составляет около 6 кГц. Я использовал частоту среза 0,12. На выходе демодулятора тоже есть ещё один ФНЧ с частотой среза в 12 кГц, т.е. 0,25 говоря по-цифровому.
Вот и весь приёмник. Еще я добавил ему возможность устанавливать из командной строки звуковые интерфейсы для ввода и вывода звука. Вот как выглядит запуск приемника на моём компьютере:
decodfm -i "Microphone (2- USB Audio CODEC )" -o "CABLE-A Input (VB-Audio Cable A)"
Больше никаких настроек не предусмотрено и сломаться нечему. Но рано я радовался, на моём компьютере установлен антивирус Касперского. Он предотвратил запуск моего SDR. Но чтоб это понять, пришлось пару часов в отладчике посидеть. Сам антивирус ничего не сообщал, просто программа вылетала с ошибкой:
Error: A backend-specific error has occurred: Параметр задан неверно. (0×80070057)
Пришлось зайти в настройки Касперского и внести свою программу в список исключений. После этого всё сразу заработало. А вот так выглядят декодированные пакеты в программе UZ7HO:
Попутно оценил качество оптимизации кода компилятором Rust. Если отладочная версия программы загружает процессор на 10–12%, то релизная версия с включенными оптимизациями не загружает процессор более чем на 2%. Т.е. SDR можно запросто запускать фоновым процессом при старте компьютера как демон и больше не вспоминать о его существовании.
Прочитавшим спасибо за внимание! Программу с исходниками можно найти здесь: https://github.com/lesha108/decodfm