При включённой опции TUN в трансвертере брал мощность слота ВМЕСТО глобального
TUN Level, из-за чего ручка «Tune level» на вкладке Transmit в трансвертере
была мёртвой, а настройка уходила в эфир на мощности слота (у 70cm и QO-100 —
все 100 %).
Теперь уровень всегда от TUN Level, а слотовая TX Power работает множителем:
Pos = TUNLevel * слот/100 — та же формула, по которой в трансвертере уже
масштабировался обычный drive. Смысл поля становится единым: «сколько мощности
трансивера разрешено этому трансвертеру», и к настройке это относится так же,
как к передаче.
Галка сохранена (кому нужен неотмасштабированный TUN), но переименована в
«Scale TUN by XVTR power» — прежнее название описывало снятое поведение.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
«Default» строился через TXProfileCapture(живые настройки), поэтому сброс,
сделанный на профиле ESSB 5k, оставлял в нём High cut 5000 — то есть Factory
не сбрасывал ровно то, ради чего его жмут. Правило «заводское значит
заводское» было применено только к EQ (FlatEQ), теперь ко всему тембру.
База заводских профилей — DefaultTX, поверх копируется только живая обвязка
микрофона: разъём и режим (Mic In/Line In, boost, bias, PTT, Tip/Ring),
Line In gain и Mic gain. Их сброс сорвал бы настройку микшера. Мощность тоже
остаётся живой — скачок drive ушёл бы в усилитель. Всё остальное (полоса,
компрессор, leveler, ALC, phase rotator, EQ, AM carrier, TUN level) заводское.
Текст подтверждения и подсказка обещали, что полоса и динамика останутся как
есть, — переписаны под новое поведение.
Проверено прогоном: с живыми ESSB 5k + компрессор 12 дБ + Line In + mic gain
22.5 заводской Default выходит 200/3100, comp off, EQ ровный, а Line In,
mic gain и drive сохраняются.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Страница была единственной с русскими подписями — переведены подсказки всех
четырёх подвкладок, надпись «Profile drive: N %» и диалоги профиля (New /
Rename / Delete / Factory / лимит). Строковых литералов с кириллицей в
SettingsForm больше нет.
Группа Microphone приведена к сетке страницы: подписи в колонке PAD, поля в
колонке CX (комбобокс Mic jack и спин Mic gain теперь в одной колонке),
Connector переехал во вторую пару колонок к Mic gain — на не-Orion он скрыт и
дыры не оставляет, поэтому отдельная третья строка не нужна (176 → 134 px).
Тумблеры boost/bias/PTT расставлены с равным шагом; пара «Line In gain»
занимает место чекбокса boost и заканчивается до «Mic bias».
Английские подписи длиннее русских — все проверены рендером формы offscreen,
в группы вписываются.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ключ ко всему — семантика EQ в WDSP (wdsp/eq.c, eq_impulse + TXA.c):
F[1..n] это УЗЛЫ ломаной АЧХ, между ними интерполяция по децибелам, а за
крайними узлами при ctfmode=0 (так создаётся eqp у TXA, мы его не меняем)
идёт кумулятивный скат (f/f0)^4 на каждый бин — фактически обрыв. Значит
включённый EQ работает как второй полосовой фильтр, и верхний узел обязан
лежать за верхней кромкой TX-фильтра.
Settings: заводские ESSB несут свою кривую (AddEQ) — ESSB 3.5k узлы
100..3600, ESSB 5k узлы 100..5100, преамп в минус (EQ стоит до компрессора
и ALC). EQ у Default/SSB DX/SSB Wide сбрасывается в заводской ноль (FlatEQ),
а не наследует живой, иначе «сброс к заводским» не сбрасывал бы тембр.
Сетка узлов вынесена в модульную DEF_EQ_FREQS.
WDSPEngine: единая точка пуша EQ (PushTXEQProfile). Починен 3-полосный
режим: раньше в WDSP уходили первые три узла 10-полосной сетки (100/200/400)
и включённый EQ убивал весь голос выше 400 Гц; теперь legacy-раскладка
самого WDSP (150/400/1500/6000, как SetTXAGrphEQ).
EqualizerControl: кривая — точный порт eq_impulse вместо гауссовых горбов
(сверено с C численно, расхождение 0.0 дБ), рисуется без клампа ±15, чтобы
обрыв за крайними узлами был виден. В 3-полосном режиме ручки стоят на
150/1500/6000 и по частоте не таскаются, сетка профиля при этом цела.
RadioController/SettingsForm/MainForm: кнопка Factory — пересборка заводского
набора с применением Default. Без неё обновлённые заводские профили не доедут
до тех, у кого секция tx_profiles уже записана: она читается, а не создаётся.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Профиль — именованный снимок «как я звучу», переключаемый одним кликом
(идиома FlexRadio, состав по таблице TXProfile у Thetis).
Граница «профиль ↔ устройство»:
- в профиль: микрофонный вход (jack/boost/bias/PTT/Tip-Ring/Line-in gain),
MicGain, кромки TX-фильтра, компрессор/leveler/ALC/phase rotator, EQ,
AM carrier, Drive и Tune level;
- у устройства остаются ATT on TX, FIR/фаза/окно фильтра (это задержка
тракта, а не тембр), TUNFreq, FM/CTCSS, TX Display, TX Grid и все
параметры PureSignal — смена профиля не должна перерисовывать спектр
и ломать калибровку PS.
Кнопки «сохранить» нет: активный профиль ведётся за живым состоянием
(StoreActiveTXProfile из OnTXSettingsChange и SetDrive) — как и все
остальные настройки в EWSDR, применяемые мгновенно.
Settings.pas: TTXProfile/TTXProfileList, TXProfileCapture/TXProfileApply
(единственные две точки, знающие состав профиля), DefaultTXProfiles
(Default/SSB DX/SSB Wide/ESSB 3.5k/ESSB 5k — наследуют mic-вход и
мощность от текущих настроек), Load/SaveTXProfiles (секция tx_profiles
под MAC). Секции нет — заводской набор строится из текущих настроек,
поэтому миграция старого конфига не меняет звук.
RadioController: FTXProfiles, rfTXProfile, SelectTXProfile (снимок → DSP
+ перепосыл DUC Specific при смене mic-байта + SetDrive + персист),
Add/Rename/Delete (последний профиль не удаляется).
MainForm: кнопка профиля в свободных колонках ряда DUP/RX MUTE — блок TX
не вырос по высоте; ЛКМ — список, ПКМ — Settings→Transmit→Profile.
SettingsForm: страница Transmit разбита на подвкладки Profile / EQ /
Hardware / Display + полоса профиля сверху; свиток 2000 px вместо этого
раскладывается на четыре коротких экрана.
Проверено: round-trip персиста отдельным тестом (все поля, активный
индекс, усечение списка) и offscreen-снимки всех четырёх подвкладок.
На железе не проверено.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Веб-пульт рисовал заметно грубее десктопа, и часть причин копилась давно.
Клиент вообще не разбирал бинарный фрейм 0x57: водопад рисовался из кадра
СПЕКТРА, а собственный поток водопада (свой детектор и усреднение
wf_avg_time_ms) уходил в никуда, занимая половину трафика. Теперь водопад
идёт из своего потока.
Адаптер слал константу 1024 вместо реального числа точек. Разрешение
анализатора = ширина панадаптера, и при узкой панели (грид из двух колонок,
маленькое окно) кадр короче — хвост буфера, нули = 0 dBm, уезжал клиенту
белой полосой, а частотная шкала сжималась. В демоне это обходили
DAEMON_SPEC_WIDTH. Длина фрейма теперь равна числу заполненных точек;
клиент и так считал N из byteLength.
Децимация под потолок протокола делила «i*Count div N» — на нецелом
отношении соседние группы получались по 1 и 2 точки, а смещение
max-детектора зависит от размера группы: по спектру шла гребёнка ±3 дБ,
в водопаде — вертикальная рябь. Заменено на целочисленный фактор
(группы одинаковой ширины).
Водопад терял 2 кадра из 3: анализатор даёт до 60 кадров/с, web шлёт 20
строк/с, а бралась просто «последняя». Введено двухступенчатое накопление
max-hold (DSP-поток → PushState → PushLoop) по идиоме
TWaterfallView.SetWaterfallData — короткие посылки CW/FT8 больше не
проваливаются между строками.
Отдельно исправлено в headless: PushState демона (20 Гц) и PushLoop (20 Гц)
идут вразнобой, и на дрейфе фаз одна и та же строка уезжала клиенту дважды
(водопад двоил и полз рывками), а на остановленном RX прокручивалась
застывшая строка. Фрейм 'W' теперь отправляется только при реально пришедшем
от DSP кадре (FWfAccumFresh → WfNew → FWfFresh). Спектр так не гейтится —
это живая кривая.
Рендер в браузере: значение бина для пикселя берётся максимумом по диапазону
вместо «ближайшего» (узкие сигналы мерцали и пропадали на узком экране), при
бинах реже пикселей — интерполяция; сглаживание спектра вынесено отдельным
проходом по всем бинам (в цикле по пикселям часть бинов не обновлялась
вовсе); строка водопада рисуется из сырых бинов, IIR ~200 мс остался только
для слежения за уровнями AGC — он размазывал водопад по вертикали.
Потолок точек кадра вынесен в настройку web.spec_pixels (Settings →
Advanced → Web Server → Spectrum: 1024/2048/4096, дефолт 1024). Буферы
кадра и WS-фреймы выросли под 4096 (WEB_SPEC_MAX, синхронно с
WDSPEngine.SPECTRUM_PIXELS). В демоне эта же настройка задаёт ширину
анализатора — рендера там нет, поэтому DAEMON_SPEC_WIDTH убран.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Вся раскладка окон, построенных кодом (SettingsForm, DeviceForm), была
захардкожена в пикселях под неявные 96 DPI — на Windows 125-200% или
HiDPI-десктопах контролы физически не росли вместе с укрупнившимся
шрифтом. Добавлено масштабирование SetBounds через MulDiv(V,
Screen.PixelsPerInch, 96) на каждой листовой точке потребления
(const-блоки раскладки не трогались).
Общий DpiScale вынесен в новый юнит DpiUtils.pas — убрана дублированная
копия одноимённого приватного метода в 9 классах (FlatCheckBox,
FlatComboBox, FlatListBox, FlatSpinEdit, FlatFloatSpinEdit, FlatEdit,
FlatRadioButton, FlatPopupMenu, SettingsForm, DeviceForm) и инлайн-MulDiv
без обёртки в MainForm.pas/PanafallPanel.pas.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Byte 1401 High Priority packet: per-band (HF 160m-6m) и per-slot (XVTR)
RX/TX-маски на 7 пинов + TX pin action (MOX/TUNE/2TON и комбинации).
Только openHPSDR — у Pluto/AD936x такого выхода нет, вкладка Settings
скрывается на этом бэкенде. Настройки — вкладка "OC Control" в SettingsForm.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
У каждого слайса B..G — свои Audio out / Audio in рядом с его CAT-портом.
Списки те же, что у главных комбо; пустой выбор (Default/None) = прежнее
поведение, общий выход/вход приложения.
Это устройство СЛОТА (буквы), а не шаблон:
• смена настройки применяется к слайсу, стоящему на слоте, немедленно
(SetSliceSlotAudio) — как и вся остальная форма настроек EWSDR;
• слайс, который встанет на слот позже (создание/restore из персиста),
получает то же устройство: слот сильнее аргумента AddSlice;
• выбор во вкладке AUD флага пишется обратно в настройку слота
(StoreSliceSlotAudio) — вкладка всегда показывает реальность, а не
вторую, конкурирующую истину.
Хранятся ИМЕНА PortAudio-устройств, а не индексы: порядок перечисления
между запусками плавает. Имя устройства, которого сейчас нет в системе,
не теряется — комбо добавляет его в список и держит выбранным.
ApplySliceSlotAudio зовётся на старте и на смене устройства (rfDevice):
слайсы восстанавливаются позже, слот должен знать свои звуковухи заранее.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Внешняя программа управляет отдельным слайсом как отдельным трансивером
через свой TCP-порт. COM-порты и 19090 остаются за главным приёмником
(слайс A) и не тронуты.
Протокол не дублируется: TCATEngine — чистый парсер поверх записи
callback'ов TCATContext, TCATTcpServer — транспорт поверх движка, поэтому
слайс-CAT = тот же движок с другим контекстом. Новый TCATSliceEndpoint
(движок + TCP-сервер на слот), массивом владеет TCATAdapter. Привязка к
СЛОТУ (буква B..G), не к Id: порт живёт, даже когда слайса нет (PS0).
Auto TX = «Auto Switch TX Slice» из SmartSDR CAT. Вся логика в одной точке
— TRadioController.RequestSliceTx, исполняется в потоке контроллера:
• пока кто-то уже в эфире (любой источник) — заявка игнорируется,
перехвата передачи нет никогда;
• без Auto TX слайс передаёт, только если уже выбран TX-источником;
• RX; снимает только СВОЮ передачу;
• TX-источник после отпускания остаётся на слайсе (как в SmartSDR).
На флаге слайса бейдж TX → AutoTX.
Частота слайса — TuneSliceInBand: свобода в пределах ВКЛЮЧЁННОГО диапазона
(трансвертер → его FreqBegin/FreqEnd, иначе band-план), другой диапазон —
отказ. Вне захваченной полосы окно DDC переезжает: доп. пан — центром на
цель (rfPanFreq → UI перекладывает флаги/шапку/зум-бар), пан 0 — центр
посередине между целью и главным VFO и только если оба влезают (общее
железное окно, главный приёмник не оглушаем).
Настройки: вкладка Slices (enable/порт 19091../Auto TX на слайс), персист
в секции cat + device-blob.
Попутно:
• ZZFL/ZZFH больше не заглушки — новые callback'и кромок фильтра
(у главного nil → прежний дефолт режима), хелпер SignedPad;
• FilterIdxFor, AGCModeToUI, BoardUtils.BandEdges;
• DSP-состояние слайса (NR/NB/SNB/ANF) зеркалится в TCtrlSlice — флаг
слайса рисовал нули и AGC главного;
• FUIReady в TMainForm.OnControllerState: адаптеры, создаваемые по ходу
FormCreate, больше не могут уронить старт рендером до постройки
виджетов (на этом падал Auto TX из настроек).
Проверено вживую: все порты 19091..19096 слушают, PS;/ZZFA;/MD;/ID;/SM0;/
ZZTX; отвечают; старт со всеми включёнными слайсами чист под gdb.
Документ — doc/CAT_SLICES.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Боковая TX теперь строится ЕДИНЫМ конвертером знака TXSignedEdges (порт
Thetis UpdateTXLowHighFilterForMode): магнитуда TX-полосы → знаковые края bp0
по боковой режима. Через него идут ApplyTXChainSettings и SetTXFilterFull —
LSB/DIGL больше не уходят в USB, когда пуш оказывается последним (MOX).
Убраны оба SetTXABandpassRun(1): этот сеттер дёргает bp1 (aux), а не bp0.
При выкл. компрессоре bp1 держал стартовую полосу (−5000,−100) и глушил вход
модулятора — FM/WFM/FMRAW не заводились без «тычка» компрессором. bp1 отдан
компрессору через TXASetupBPFilters, как в Thetis.
Оркестрация сведена: ApplyTXMode (SetTXAMode+ApplyTXModeSettings) — единая
точка TX-перехода для Open/SetMode/SetTXMode (в т.ч. слайс-TX);
ApplyRXModeSettings (WFM/DMR/FMRAW) — для главного RXA и слайсов. Убраны
ручной DMR-инлайн в SetMode и двойной вызов WFM; ApplyWFMSettings→ApplyTXWFMSettings.
Дефолт TX-полосы 100/2800 → 200/3100 (Thetis-паритет).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Пункт 3 плана. Ручные подгонки остаются: ppm работает ПОД ними, а не вместо —
уход тракта где-то больше, где-то меньше.
- Settings: TCalibration.FreqCalPPM (JSON cal_freq_ppm, +-100 ppm, per-device).
Смысл знака: «опора выше номинала на N ppm», положительное значение
поднимает показываемые частоты.
- RadioBackend: виртуальный SetFreqCalPPM.
- HPSDRNetwork: CalFreq(Hz) = Hz / (1 + ppm) в частотном слове — все четыре
точки (главный DDC, DDC панов, feedback-DDC PureSignal, DUC передачи).
Тактовый генератор радио менять нечем, поэтому правим запрос.
- PlutoBackend: штатный xo_correction в ad9361-phy (device-attr биндинги
добавлены в IIOBindings). Номинал XO снимается при коннекте с округлением
до 100 кГц, чтобы наша же коррекция с прошлой сессии не накапливалась;
после записи LO переписываются, иначе правка ждала бы следующей перестройки.
Одна цифра лечит RX и TX на всех диапазонах.
- QO-100: TXvtrEntry.TxLOError (JSON tx_lo_error) + поле «TO Error (Hz)» на
странице QO-100. Сам транспондер-offset держим стандартным (8089.5 МГц),
уход тракта правим поправкой; ActiveTXFreqHz учитывает её, подсказка внизу
страницы считает TX-частоту уже с ней.
- UI-фиксы вкладки Calibration: длинные подсказки переведены на несколько
строк (WordWrap — TLabel по умолчанию режется по краю группы), заголовки
таблиц укорочены и не заезжают под кнопку «= S-meter»; RelayoutCalTab
сбрасывает прокрутку страницы перед перекладкой (SetBounds в TScrollBox
задаёт координаты относительно прокрученного клиента).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Вторая калибровочная таблица рядом с S-метром (паритет с Display Cal Offset
в Thetis): у display-тракта своё окно/детектор, и его уровень расходится с
показаниями стрелки на пару дБ, поэтому таблицы раздельные.
- Settings: DispBandDB[12] / DispXvtrDB[8] (JSON cal_disp_band_i /
cal_disp_xvtr_i, дефолт 0 — картинка не меняется до первой правки).
- WDSPEngine: колбэк OnDispCal(PanId) — движок раз в кадр спрашивает офсет в
UpdateSpectrum и прибавляет к пикселям (главный спектр/водопад в копирующем
цикле, паны in-place при ненулевом офсете). На TX-дисплее не применяется.
Так обошлись без хуков на каждое присваивание FCenterFreq.
- RadioController: общий CalLookup(Hz, BandTbl, XvtrTbl) — на нём SMeterCalFor
и DispCalFor(PanId): главный дисплей по FCenterFreq, пан по FPanFreqHz[p],
т.е. кросс-банд паны получают свой офсет. Колбэк вешается в CreateEngines,
поэтому работает и в GUI, и в ewsdrd; web-зеркало получает готовые кадры.
- SettingsForm: группа «Spectrum / waterfall» с той же разбивкой (диапазоны +
включённые трансвертеры активного железа) и кнопка «= S-meter» — копия
таблицы стрелки как старт калибровки; общая раскладка сеток вынесена в
LayoutCalOffsets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Вместо одного офсета на всё устройство — таблицы «на диапазон» и «на слот
трансвертера»; состав таблиц задаёт активное железо.
- Settings: SMeterOffsetDB -> SMeterBandDB[12] / SMeterXvtrDB[8] (JSON
cal_smeter_band_i / cal_smeter_xvtr_i). Легаси-ключ cal_smeter_db становится
дефолтом всех слотов — показания после обновления не меняются. Разделение
Pluto/openHPSDR даёт сама секция JSON: она ключуется по MAC устройства
(у Pluto — synthetic MAC из serial).
- Офсет убран из WDSPEngine (FSMeterCalDB): движок не знает ни диапазонов, ни
трансвертеров. Теперь TRadioController.SMeterCalFor(VisibleHz) — окно
включённого XVTR-слота приоритетнее диапазона, индекс диапазона по плану
железа (FreqToBandIdx с IsPluto); ReadSMeterDBm для главного S-метра
(MainForm + ewsdrd) и SliceSMeter по TargetHz слайса — паны N на других
диапазонах считаются по своему банду, а не по главному.
- SettingsForm: таблицы на 12 слотов, подписи/видимость/раскладку ставит
RelayoutCalTab; SetCalibrationBackend(IsPluto) вызывается рядом с
SetAntennaBackend. У openHPSDR — HF-план, у Pluto — VHF/UHF-план и скрытые
группы Power/SWR detector и Supply V/A (нет Alex-моста и его телеметрии).
Слоты трансвертеров показываются только включённые, с их именами.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- шапка колонок: «RX»/«TX» по центру своей пары, номера разъёмов отдельной
строкой ниже (не налезают на рамку группы);
- строки трансвертеров пакуются подряд (раньше выключенные слоты занимали
место → дыра между ADS-B и QO-100), высота группы — по последней видимой;
- RX- и TX-пары разнесены, шире колонка названий, крупнее шаг строки;
- дим-заголовок «Transverters» над секцией слотов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Второй разъём у AD936x-плат — это второй КАНАЛ чипа (не порт B первого), и в
1R1T-образе он выбирается мультиплексором AD9361: debug-параметры
adi,1rx-1tx-mode-use-rx-num/-tx-num + биты RX1/RX2, TX1/TX2 регистров 0x3/0x2
(как switch_rfinput.sh/switch_rfoutput.sh прошивки). rf_port_select тут ни при
чём — прошивка его к тому же блокирует.
- IIOBindings: debug-атрибуты + прямой доступ к регистрам.
- PlutoBackend: детект второго разъёма (2R2T-каналы либо мультиплексор + модель
платы), SetAntPorts(RxPort,TxPort), ApplyRxMux/ApplyTxMux; Caps.HasAntSwitch.
⚠ApplyGain(Force): драйвер пропускает запись gain_control_mode, если считает
режим неизменным, — а у нового канала AGC-регистры не запрограммированы (в
железе крутится auto-AGC, hardwaregain отвергается EOPNOTSUPP). Форсируем
перепрог через другой режим после смены разъёма и после ad9361_set_bb_rate.
- RadioBackend: TBackendCaps.HasAntSwitch + virtual SetAntPorts (HPSDR — no-op,
там антеннами рулит Alex).
- Settings: секция ant936x (разъём RX/TX на диапазон и на слот трансвертера);
hw-AGC (режим + manual gain) переехал в TBandSettings/TXvtrEntry — теперь он
свой на каждом диапазоне и трансвертере.
- RadioController: FAnt936x + ApplyAntPorts (RestoreBand/ActivateXvtr/
StartRunning); ApplyBandDSP восстанавливает hw-AGC диапазона.
- SettingsForm: вкладка Antenna — Alex-таблица (openHPSDR) либо таблица
разъёмов AD936x по диапазонам/трансвертерам.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SPECTRUM_PIXELS 1024→4096 + настройка display_pixels (лимит точек
анализатора, SetDisplayResLimit); web-зеркало остаётся 1024 —
max-децимация в SnapshotPixels
- палитры водопада Classic/Inferno/Turbo + регулятор gamma (в LUT),
настройки wf_palette/wf_gamma, страница Waterfall → группа Rendering
- GL-водопад переведён на общий LUT/AGC-уровни базового класса
(убраны дубли ColorForDB/UpdateLevels)
- явные glViewport в GL-вьюхах умножаются на PixelScale (HiDPI),
текстура водопада — в физической ширине
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bidirectional Apache Labs Andromeda/G2 front-panel protocol on top of CAT.
Panel logic lives in dedicated units; existing files get only thin hooks.
New units:
- AndromedaMap.pas — data only: action enums + default button/encoder
layout tables and LED (ZZZI) numbers (mirroring the Thetis defaults)
- AndromedaPanel.pas — TAndromedaPanel: inbound panel events -> controller
actions (marshalled via Invoke), state subscription -> ZZZI push via SendProc
Hooks:
- CATEngine: parse ZZZD/ZZZU/ZZZE/ZZZP/ZZZS into generic TCATContext callbacks
- CATAdapter: creates the panel, routes engine callbacks to it, supplies the
SendProc to the Andromeda port, AddStateListener, ZZZS handshake
- CATSerial: Andromeda flag in port config + HasAndromeda/SendToAndromeda
- RadioController: AddStateListener (multicast, does not steal MainForm's
OnStateChanged)
- Settings/SettingsForm/MainForm: CATSerialAndromeda[0..3] + persistence + checkbox
First version: serial transport, hardcoded default mapping, LED indicators
(MOX/Tune/CTUN/VFO A-B/Split/NB/NR/SNB/ANF/Squelch). Doc: doc/ANDROMEDA.md.
TODO: default button mapping does not match the physical panel (hardcoded) —
to be fixed later; also ZZMF/LCD text, TCP G2V2 transport, configurable mapping.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose the QO-100 slot's LOError (Hz) on the QO-100 settings page,
placed right after LNB LO. Maps to Entries[QO100_SLOT].LOError — the
live LNB-drift calibration that the beacon lock folds its correction
into. Group height extended to 8 rows and the info label shifted down.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
QO-100 reuses the transverter path (VFO/band-stack/beacon-lock untouched)
with a split LO for the geostationary transponder:
- TXvtrEntry += FullDuplexTx + TxLOOffset (persisted). Reserve the last
XVTR slot (QO100_SLOT) for QO-100; migration restores the template when
the slot is unset or has empty offsets (fixes old configs showing zeros).
- XvtrTranslateTX: TX LO = visible - TxLOOffset (no LNB LOError) for QO-100;
identical to RX translate for normal transverters. Switched all TX-side
translate calls (controller + MainForm).
- SetDuplex wired (live KeepRXDuringTX); auto display-duplex on QO-100 entry.
- New "QO-100" settings page (LNB LO / transponder offset / downlink edges /
RX gain / TX ceiling + live RX-IF/TX readout); QO slot hidden from the
transverter table. Reuses OnXvtrChange — no MainForm wiring needed.
- doc/PLUTO_INTEGRATION_PLAN.md: phase 5 done.
Builds clean (headless + qt6 GUI). Not yet verified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds on the beacon-lock baseline: keeps the proven display-FFT control
path (hardware-LO trim folded into the active transverter LOError), and
hardens it. The narrowband NCO/FLL scaffold (BeaconLock.pas) is removed —
it was never in the control path.
- RadioController:
* Persist live: each correction folds straight into the active xvtr
LOError (visible in Settings, survives restart as LNB calibration);
disk writes throttled. FBeaconLockHz is now only a session indicator.
* Robust tracking vs nearby signals: narrow ±2 kHz gate while locked
(±6 kHz only for acquisition), slew-rate outlier rejection (a sudden
centroid jump = interferer, not slow LNB drift → ignored), and lobe
shape validation in MeasureBeaconFFT (width + power symmetry) so a CW
carrier / one-sided neighbour isn't mistaken for the BPSK beacon.
Prominence measured over the in-window noise floor (gate-width robust).
* Fast convergence: feed-forward (predict post-retune position so the
window follows the beacon through the jump) + near-deadbeat correction
on the manual click → one click locks instead of repeated tapping.
* Single central BPSK beacon (10489.750); dead CoarseBeaconOffset and
unused throttle counters removed; stale NCO/FLL/4 Hz comments fixed.
- WDSPEngine: drop the per-sample TBeaconTracker RX-IQ tap (tracker gone).
- MainForm: BEACON is now a plain button, shown only on Pluto in a
transverter. Lock status (off / click / search / LOCK + correction +
prominence) moves to status-bar field 7 in place of PLL on Pluto;
openHPSDR keeps PLL there.
- SettingsForm/StatusBar: widen LO Error field to ±10 MHz (QO-100 LNBs
drift hundreds of kHz) and widen status field 7 for the beacon text.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a "Send audio stream to radio (built-in speaker)" checkbox on the
Settings → Audio tab. It is a single switch for the radio's built-in
speaker:
- ON — RX audio is streamed to the radio via the DDC Audio packet
(port 1028, 64 L/R pairs per packet) and the speaker is unmuted
(High Priority byte 1400 bit1 = 0).
- OFF — speaker is muted (byte 1400 bit1 = 1, ANAN-8000DLE IO1 output)
and no audio stream is sent.
The float->int16 conversion and 64-pair packet accumulation live in
THPSDRNetwork (SendSpeakerAudio/SetSpeakerAudio), next to SendDDCAudio;
MainForm only reads the setting and forwards the audio. State is
persisted per-MAC (send_audio_to_radio, default off) and re-applied on
each radio start.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the existing Wideband gradient fill option for the main spectrum:
a "Spectrum gradient fill" checkbox on the Display settings tab gates the
under-curve gradient in TSpectrumView.DrawSpectrum. The curve, grid,
filter band and VFO markers always draw; only the gradient is optional.
Persisted as spectrum_fill in settings JSON (default True, preserving
current appearance). Wired through Settings -> SettingsForm visibility
event -> MainForm -> TSpectrumView.FillSpectrum, same path as
WidebandFill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RefreshAudioDevices was firing OnRXDevChange/OnTXDevChange when restoring
the combo selection, which called ApplyAudioDevice(-1,'') and wiped
FAudioOutDevName/FAudioInDevName before LoadValues could use them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove manual "Mic Source" (Radio/SoundCard) setting from UI and storage;
source is now determined automatically in ApplyMOX: HW PTT → txmsRadio,
MOX button/CAT → txmsSoundCard if audio device configured, else txmsRadio
- Replace "Line in" checkbox with "Mic jack" combo (Mic In / Line In);
when Line In is selected, Mic Boost is hidden and Line In Gain (-34.5..+12 dB,
step 1.5 dB) is shown and sent as DUC Specific byte 51
- Add Tip/Ring connector selector (Orion/OrionMkII boards 4/5 only, byte 50 bit 3);
control is hidden for other board types
- Fix byte 50 bit 1 (Mic Boost) suppressed when Line In is active
- Fix byte 51 comment polarity (was reversed: 0=+12dB; correct: 0=-34.5dB)
- New JSON fields: mic_line_in, mic_tip_ring, line_in_gain_db; mic_source removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PA calibration per band (HF and VHF):
- Add VHFBandCal[0..7] in TGlobalSettings (saved as vhf_cal_0..7 in JSON)
- Default HF cal updated to Thetis HERMES defaults (38.8-41.3 dB per band)
- Default VHF cal = 56.2 dB (Thetis HERMES XVTR default)
- SettingsForm: second group in PA tab "VHF Calibration (Transverters)" with
8 spinboxes VHF0..VHF7; labels dynamically show XVTR slot ButtonText
when LoadXvtrSettings is called (e.g. "VHF0 (2m):")
TX drive formula (CalcDriveByte) rewritten to match Thetis exactly:
drive_byte = min(sqrt(10^((10*log10(pwr_W*1000) - Cal)/10) * 0.05) / 0.8, 1.0) * 1.02 * 255
Cal = PA gain in dB (not a percentage); matches Thetis verified output
(Cal=63.1 + slider=100% + PAMax=100W → drive_byte=16, verified against Thetis)
Old formula Pos/100*Cal/100*255 was wrong: treated dB gain as linear percent
Fix stale FDriveLevel not updated on band/XVTR switch:
- Startup: FCurrentBand must be set before CalcDriveByte (was reversed)
- RestoreBand: recalculate FDriveLevel before ApplyVfoA network update
- ActivateXvtrBand: recalculate after FCurrentXvtr changes
- ApplyVfoA: recalculate when VFO crosses HF band boundary
- FreqDispBChanged: same for VFO-B band changes
- ApplyMOX(True): safety recalculate at PTT engage time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FM modulation:
- Replace 10 generic FM filter buttons with NFM (2.5kHz dev/11kHz BW) and FM
(5.0kHz dev/16kHz BW); deviation applied to both RX and TX via WDSP
- Add FM squelch panel (SQL toggle + slider 0..100, per-band save/restore)
with WDSP SetRXAFMSQRun/SetRXAFMSQThreshold (threshold = level/200.0)
- Add CTCSS panel with on/off toggle and custom flat-button tone picker popup
(38 standard tones 67.0..250.3 Hz, default 67.0); tone selection and enable
state saved per band via SetTXACTCSS*
- FM panels (SQL, CTCSS) shown only in FM mode, layout via RelayoutBelowBands
- PanelFilter height shrinks in FM mode so panels sit tight below filters
- FlatButton: handle CMTextChanged to invalidate on Caption change
ADC settings (Dither/Random):
- Add DitherEnabled/RandomEnabled to TGlobalSettings (default true)
- Pass as bitmask to ConfigureDDCs DDC Specific packet bytes 5/6
- Exposed in Settings → Advanced tab with live apply via ApplyADCSettings
Web server settings:
- Add TWebSettings (enabled, port, bind_addr, user, pass) stored globally
- WebServer: configurable port/bind IP via ParseIPv4; Reconfigure method
- Exposed in Settings → Advanced tab with live apply via ApplyWebSettings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
XVTR RF (xvtr_rf in Thetis) is dead code — GetXVTRRF() is never called.
Remove the field, UI column, save/load from TXvtrEntry. UseDDCIn is now
always True when in XVTR mode (previous default behaviour, XVTRRF=False).
Byte 1400 bit 0 (XVTR Enable / VHF T/R relay output) now follows Thetis:
- XVTR mode: set only when DisablePA=True (external T/R relay needed)
- HF mode: set when Alex.EnableXvtrHf is checked (Antenna tab)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaced the WF AGC and WF NF toggle buttons in the main panel with
"Waterfall AGC" and "Waterfall NF" checkboxes in the Settings form
Waterfall tab (AGC & Noise Floor group). Settings are global, defaults
remain AGC=on, NF=off.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings > Display: new "VFO Display" control to select max frequency
range (999 MHz / 9.999 GHz / 99.999 GHz), persisted as freq_mhz_digits
- FreqDisplay: extend digit map [0..10] to support up to 11 digit positions;
keyboard navigation adapts to MinMhzDigits dynamically
- MainForm/LayoutTopVfoBlock: VFO panel width scales automatically with
selected digit count using dynamic text measurement
- Web VFO: fix broken HTML (class attr not closed before data-s — wheel and
hover were completely non-functional); rewrite fmtVfo to use variable MHz
digit count synced from server state; fix missing </span> for .vfo-hz
- WebServer: publish freq_mhz_digits in state JSON; updated from
ApplyFreqMhzDigits so desktop and web stay in sync
- Fix Int32 overflow on frequencies above ~2.15 GHz: CATEngine.Pad V param
Int64, FormatFreq/FormatFreqSV Mhz var Int64 (Mhz*1000000 overflowed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BtnTUN рядом с MOX. Включает SetTXAPostGenRun(TXA, 1) с тоном
(mode=0, mag=1.0, freq из настроек) и активирует MOX. PTT/HW
игнорируются во время tune.
- Drive в режиме TUN использует FTXSettings.TUNLevel вместо слайдера,
per-band калибровка FPABandCal применяется одинаково.
- Settings → Transmit → Tune (TUN): TUNLevel 0..100% (def 10),
TUNFreq 100..5000 Hz (def 750). Live-обновление во время tune.
- DUP: локальный режим отображения, при TX данные RX1, поверх рисуется
TX-фильтр. Кнопка на левой панели рядом с CTUN. Аппаратной командой
не является. Отдельный SetDisplaySourceTX в WDSPEngine, разделение
TXMode (data) и TXOverlay (filter band) в SpectrumView.
- ATT on TX: 0..31 dB, default 31 (Thetis-style). UI на вкладке Transmit
в группе Microphone. Передаётся в DUC Specific bytes 57/58/59
(StepAtten0/1/2). Перепосылается при изменении.
Декаплинг TX от RX sample rate (как в Thetis):
- WDSPEngine.pas: константа TX_SAMPLE_RATE=192000, поля FTXSampleRate/FTXOutBufSize.
TXA OpenChannel теперь использует FTXSampleRate как out_rate, TX-анализатор
через SetDisplaySampleRate(TX_DISP_ID, FTXSampleRate). ProcessTXBlock работает
с FTXOutBufSize вместо FBufSize. Public TXSampleRate для MainForm.
- MainForm.SendDUCSpecificFromSettings: DUC0Rate теперь берётся из
FDSPEngine.TXSampleRate, а не из RX-настроек — TX всегда 192k независимо от
выбранного RX-rate (48/96/192/384k).
DUC Specific cache + keepalive resend (HPSDRNetwork.pas):
- FCachedDUCSpec/FCachedDUCValid параллельно DDC-кэшу.
- KeepAlive thread в первые 5s после Run=1 переотправляет и DDC, и DUC
Specific — устраняет гонку, когда DUC настройки терялись железом.
- Disconnect сбрасывает оба valid-флага.
Per-device TX grid settings (Thetis-style):
- Settings.pas: TXSpecRefLevel/TXSpecRange/TXSpecGridStep в TTXSettings
(defaults 0.0/80.0/10.0). LoadTX/SaveTX — ключи tx_spec_ref_level/range/grid_step.
- SettingsForm.pas: новая группа \"TX Grid\" во вкладке Transmit
(RefLevel edit, Range edit, GridStep combo). CollectTXFromUI читает значения,
LoadTXSettings заполняет UI.
- MainForm.pas: поля FTXSpecRefLevel/Range/GridStep, ApplySpecViewGridFromState
пушит в FSpecView либо TX, либо RX параметры в зависимости от FTransmitting.
ApplyGridParams (RX-путь) уважает FTransmitting и не перебивает TX-grid.
OnTXSettingsChange при активном TX моментально применяет новый grid.
TX-aware spectrum/waterfall с CTUN-выравниванием (SpectrumView.pas):
- Поля FTXMode/FTXFreq/FTXSpanHz/FTXVfoIndex. TXSpanHz=192000 при создании.
- DrawSpectrum/DrawWaterfall в FTXMode мапят данные TX-анализатора (192k вокруг
FTXFreq) в RX-координаты FCenterFreq/FSpanHz. Формула:
freq = FCenterFreq - FSpanHz/2 + x*FSpanHz/W
idx = (freq-FTXFreq + TXSpanHz/2)/TXSpanHz * (SrcCount-1)
Вне диапазона — спектр уходит в -200 dB; водопад копирует пиксель из
предыдущей строки (FWfPixels[X+W]) — вместо чёрной полосы продолжает
скроллиться старая RX-картинка.
- Эффект: при CTUN ON TX-сигнал рисуется на полосе фильтра (на VFO), а не в
центре. При RX rate ≠ 192k частотная сетка совпадает между RX и TX
автоматически (ruler всегда в RX-координатах).
- MainForm.SyncSpecViewFreq/ApplyMOX обновляют TXFreq/TXVfoIndex/TXMode при
каждом PTT и смене VFO; учитывается split (FSplitTxB → TXVfoIndex=1).
Alpha-blended filter bands (визуальный feedback TX/RX):
- SpectrumView: PixelFormat:=pf32bit, процедура BlendBand через ScanLine[Y]
внутри BeginUpdate/EndUpdate. Альфа-смешение по формуле
(a*src + (255-a)*dst) / 255 для каждого канала BGR.
- В FTXMode полоса фильтра становится полупрозрачно красной. Если split
(FTXVfoIndex<>FActiveVfo): на RX-частоте полоса зелёная, на TX-частоте —
красная (CalcFilterBandX вычисляет границы для произвольной VFO-freq).
- При FTXMode=False — старое поведение (сплошная заливка SpecFilter).
Build: lazbuild --ws=qt6 ewsdr.lpi — clean, 0 errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Settings: TTXSettings record + LoadTX/SaveTX, JSON section "tx" per MAC
- WDSPEngine: separate TX_DISP_ID analyzer with own FFT/Window/Det/Avg
parameters, ApplyTXAnalyzerSettings + setters
- WDSPEngine: full TX chain setters (Filter/Compressor/Leveler/ALC/
PhaseRot/EQ/AMCarrier/FM/CTCSS) + ApplyTXChainSettings
- WDSPEngine: ProcessTXBlock now feeds TX analyzer in DISPLAY_BLOCK_SIZE
chunks (was passing whole FTXOut → analyzer ignored most of it)
- WDSPEngine: ActiveDisplayID switches RX/TX in UpdateSpectrum
- WDSPEngine: RX audio muted while TX active to avoid local feedback
- WDSPEngine: default EQ frequencies in Create (zero freqs broke TX chain)
- WDSPEngine: PushTXMicSampleD + OnPullMicSamples for sound-card path
- AudioInput: Available counter for non-blocking pull
- MainForm: PullSoundCardMic, BuildMicLineSelectByte, SendDUCSpecific
with mic bias/boost/line/PTT bits, OnTXSettingsChange
- MainForm: auto-switch MicSource on TX device select, TX diagnostic
in StatusBar panel [5]
- SettingsForm: new Transmit nav page with Microphone, Filter, Dynamic
processing, EQ, AM/FM/CTCSS, TX Display sections
NOT YET WORKING: sound-card mic produces ring activity but signal still
not visible on TX spectrum/waterfall — needs further investigation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- AppTheme.pas: новый файл, TAppTheme record + DarkTheme()/LightTheme()
- FlatSlider: цвета через SetThemeColors вместо хардкода
- SpectrumView: все цвета из TAppTheme (SetTheme)
- SettingsForm: ApplyTheme(T) с рекурсивным обходом контролов
- DeviceForm: SetTheme(T) — перекраска всех элементов включая TEdit
- Settings: SaveTheme/LoadTheme в корневой секции preferences (не привязано к устройству)
- MainForm: тема загружается при старте, сохраняется при изменении
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings.pas: add root-level SaveCATSettings/LoadCATSettings so CAT
config is stored independently of device MAC.
- MainForm.pas FormCreate: initialize CAT defaults, load from saved
'cat' JSON section, and apply immediately after InitCATEngine — so
SettingsForm shows correct values before the transceiver connects.
- MainForm.pas OnCATSettingsChange: always save to root 'cat' section
(not only when device is connected) so changes persist immediately.
- Default TCP CAT port changed from 4992 to 19090 everywhere.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New units: CATEngine (Kenwood + Thetis ZZ* commands), CATSerial
(up to 4 serial ports), CATTcp (multi-client TCP server on port 4992).
MainForm wired with TCATContext callbacks reusing existing WebOn* handlers.
SettingsForm gets a new CAT tab with 4 serial port groups and TCP section.
Settings.pas extended with CAT config fields (per-device, JSON-persisted).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>