Thetis SDR: Direct2D 1.1, новые палитры и тонкая калибровка железа
Автор: Юрий (eu2av)
Дата: 2026-07-04
Завершив плотную работу по стабилизации прошивки для трансивера Anvelina PRO III (Cyclone IV E), где мы привели в порядок DSP-тракт, устранили наводки АЦП и настроили тактирование, я переключился на хостовую часть. Настала очередь исходников самого Thetis SDR.
Работа с чужим (и своим) кодом — это всегда поиск баланса между «хочется нового» и «главное — не сломать стабильность». В этом материале я хочу без прикрас рассказать о том, что было сделано в последних коммитах: как мы мигрировали графический движок, почему 16-битный рендеринг оказался не такой простой задачей, как нарисовали новые цветовые схемы и, самое главное, как добавили гибкие инструменты для калибровки реального «железа» прямо из интерфейса.
Графический движок: переход на Direct2D 1.1 и честный взгляд на 16-бит
Исторически водопад в Thetis рисовался через legacy-интерфейс ID2D1RenderTarget из Direct2D 1.0. Он работал годами, но имел фундаментальное ограничение: только 8-битные поверхности (256 уровней на канал). На современных мониторах это иногда давало легкий «бандинг» (полосы) на плавных градиентах шума.
Мы выполнили миграцию на Direct2D 1.1, перейдя на ID2D1DeviceContext. Это дало устойчивость к сбоям GPU (device removal) и открыло дверь к использованию 16-битных форматов (R16G16B16A16_Float, 65 536 уровней).
Но здесь я хочу быть предельно честным: 16-бит — это пока эксперимент.
На бумаге 16-бит выглядит идеально. На практике, при переключении (например, на моем 2K-мониторе), картина меняется не так, как ожидаешь:
- Толщина линий. Direct2D в 16-бит float оперирует в линейном цветовом пространстве (linear RGB). Покрытие пикселей для антиалиасинга считается иначе, из-за чего линии на панадаптере и сетке визуально становятся в 1.5–2 раза толще.
- Яркость и контраст. Картинка становится светлее, но субъективно менее контрастной, так как sRGB-гамма, к которой привык глаз, в linear space работает иначе.
Чтобы это хотя бы частично компенсировать, пришлось писать костыли: функцию sw() для масштабирования толщины линий под linear coverage, преобразование sRGB→linear для цветов и альфа-канала, а также корректировку альфа-блендинга (pow(a, 1.6)) для оверлеев фильтров.
Резюме по графике: На данный момент рекомендуемым и стабильным режимом остается 8-бит (B8G8R8A8_UNorm). В нем все цвета, линии и оверлеи работают ровно так, как задумано. 16-бит оставлен в настройках как опция для энтузиастов, которые хотят протестировать формат и помочь собрать обратную связь для дальнейшей доработки. Переключение работает «на лету», без перезапуска.
Новые цветовые схемы и уровни качества
Вместо жесткого кодирования градиентов мы внедрили multi-stop интерполятор (256-шаговый LUT). Это позволило добавить три новые схемы окраски, которые сразу стали доступны в выпадающих списках RX1/RX2/TX:
- Console 256 — насыщенный радужный градиент в стиле SDR Console. Сделана схемой по умолчанию.
- Thermal 256 — «тепловизорная» гамма (от индиго к белому), отлично подходит для выделения узких сигналов на фоне шума.
- DeepBlue 256 — прохладный контрастный градиент для любителей сине-голубой гаммы.
Также мы переосмыслили настройки качества. Вместо неочевидного «Palette Resolution» появились видимые пресеты, которые работают в float-конвейере:
- Classic (по умолчанию) — чистый рендеринг.
- Vivid — увеличение насыщенности на 30%.
- Sharp — Vivid + 25% контраста по S-кривой.
- Ultra — максимальный контраст + автоматический упорядоченный дизеринг (Bayer 8×8), который окончательно «съедает» любые остаточные артефакты квантования.

Устранение давних артефактов отрисовки
В процессе рефакторинга удалось закрыть пару багов, которые жили в коде годами:
1. Тусклый водопад после выбора Custom.
Если схема Custom не была полностью загружена, происходил выход из switch с пустым буфером. Нулевой пиксель (черный цвет) записывался в битмапу каждый кадр, визуально «затирая» водопад. Решение: внедрен флаг skipRow, который полностью пропускает пайплайн записи, если схема не сформировала валидный цвет.
2. Моргание индикатора ADC Overload при переходе TX → RX.
При переключении из передачи в прием первый опрос статуса считывал «залипший» (latched) флаг перегрузки АЦП из native-кода. Мы не стали трогать FPGA, а решили задачу на уровне C#: метод UIMOXChangedFalse() теперь корректно сбрасывает native latch через NetworkIO.getAndResetADC_Overload(), очищая устаревшее состояние перед началом приема.
Per-Monitor DPI Awareness для 2K и HiDPI
На мониторах с разрешением 2K и выше водопад мог выглядеть мыльным, так как процесс не был объявлен DPI-aware, и DWM compositor банально растягивал логическое разрешение до физического.
Мы добавили переключатель DPI Awareness (HiDPI/2K) на вкладке Display. При его включении Thetis вызывает SetProcessDpiAwarenessContext(PER_MONITOR_V2), заставляя swap-chain и битмапы водопада сразу рендериться в физическом разрешении монитора. Картинка становится кристально резкой. Если на какой-то специфической системе UI «поедет», галочку можно снять и перезапустить программу.
Аппаратные калибровки: компенсация физики
Программная часть должна максимально точно отражать физические процессы. В последних обновлениях мы добавили ряд инструментов для тонкой калибровки железа прямо из интерфейса Thetis.
1. Пополосная калибровка детектора мощности (Det. Cal.)
Стандартная калибровка мощности в Thetis является глобальной и не учитывает частотную зависимость направленного ответвителя (tandem-match coupler). На НЧ-бендах (160/80/40м) показания могли занижаться или завышаться на 4–12%, так как детектор физически выдает другое напряжение.
Мы добавили новую вкладку Det. Cal. в настройках PA, где для каждого бенда (от 160м до 6м) можно задать множитель k. Формула мощности теперь выглядит как watts_corr = V² · k / bridge_volt. Поскольку множитель применяется одинаково к FWD и REV, расчет КСВ (который зависит от их соотношения) остается физически корректным.
Для трансиверов семейства Anvelina PRO3 / ANAN7000D уже зашиты factory-значения, полученные при калибровке по внешнему ваттметру. Кнопка «Reset to Defaults» позволяет вернуть их в один клик.
2. Калибровка напряжений (PA Volts и Supply 13.8V)
Аналогично току, мы добавили per-device множители для трактов напряжения (PAVoltCal для АЦП AIN3 и SupplyVoltCal для АЦП AIN6). Если после аппаратных модификаций (например, изменения dummy-conversion в прошивке ПЛИС) показания чуть сместились, пользователь может ввести поправочный коэффициент прямо в меню калибровки.
Например, если Thetis показывает 13.9В при реальных 13.8В, достаточно ввести множитель 0.993 (13.8 / 13.9). Никакой перекомпиляции кода или правки конфигурационных файлов — всё делается через удобный UI.
3. Пополосная калибровка детектора мощности (Det. Cal.)
Стандартная калибровка мощности в Thetis является глобальной и не учитывает частотную зависимость направленного ответвителя (tandem-match coupler). На НЧ-бендах (160/80/40м) показания могли занижаться или завышаться на 4–12%, так как детектор физически выдает другое напряжение на разных частотах.
Пример из практики: на 160м при реальных 100 Вт в нагрузке Thetis показывал ~91 Вт (занижение на 9%). При этом КСВ оставался корректным, поскольку искажение одинаково влияет на FWD и REV.
Мы добавили новую вкладку «Det. Cal.» в настройках PA (рядом с «PA Gain» и «Watt Meter»), где для каждого бенда (от 160м до 6м) можно задать поправочный множитель k:
k = 1.00— коррекции нет (по умолчанию)k > 1.00— увеличивает показания (если занижало)k < 1.00— уменьшает показания (если завышало)
Формула расчета мощности теперь выглядит как:
watts_corr = V² · k / bridge_volt
Поскольку множитель применяется одинаково к FWD и REV, расчет КСВ (который зависит от их соотношения) остается физически корректным.
Factory-калибровка для Anvelina PRO3 / ANAN7000D
Для трансиверов семейства Anvelina PRO3 уже зашиты заводские значения, полученные при калибровке по внешнему ваттметру в dummy load:
| Бенд | Множитель k | Компенсация |
|---|---|---|
| 160m | 0.91 | ~9% завышения |
| 80m | 0.92 | ~8% завышения |
| 40m | 0.96 | ~4% завышения |
| 60m – 6m | 1.00 | без коррекции |
Кнопка «Reset to Defaults» позволяет в один клик вернуть эти значения, если пользователь экспериментировал с настройками.
Ручная калибровка для других моделей
Если у вас другая модель трансивера или вы хотите откалибровать под свой конкретный экземпляр:
- Setup → PA Settings → вкладка «Det. Cal.»
- На опорном бенде (например, 20м) оставьте k = 1.00
- Подайте калиброванную мощность (например, 100 Вт) через dummy load
- Сравните показания Thetis с внешним ваттметром
- Если Thetis показывает X Вт при реальных 100 Вт →
k = 100 / X- Пример: показывает 110 Вт → k = 100/110 = 0.91
- Пример: показывает 95 Вт → k = 100/95 = 1.05
- OK → настройки сохранятся в database.xml
Важно: эта калибровка работает на стороне хостового ПО (C#) и не требует изменений в прошивке FPGA. Она дополняет FPGA-работу по устранению дрожания показаний (dummy-conversion в ADC78H90), которую мы делали ранее.
Заключение
Внесенные изменения — это планомерная эволюция кодовой базы Thetis. Переход на Direct2D 1.1, новые цветовые схемы, per-band калибровки и устранение longstanding-артефактов уже доступны всем пользователям и работают стабильно. 16-битный конвейер — отдельная история: как эксперимент он запущен, и мы приглашаем сообщество помочь довести его до состояния, когда он станет полноценной и бескомпромиссной заменой 8-битного режима.
Коллеги, тестируйте новые схемы, калибруйте свое железо и делитесь впечатлениями. Отдельный вопрос к тем, кто работал с Direct2D 1.1 и 16-битными swap-chain: как вы элегантно решаете проблему толщины линий и гаммы в linear RGB без написания кучи компенсирующих костылей? Будем благодарны за любые идеи.
Скачать установщик можно тут: https://eu2av.net/download/file.php?id=2063
ссылка на гигхаб (исходный код) https://github.com/eu2av/OpenHPSDR-Thetis-Enhanced
73!
Yurij eu2av

