feat(tx): self-monitor RX-аудио на TX в любом DUP + меньше TX pre-roll

RX MUTE (self-monitor даунлинка на передаче): кнопка теперь доступна на
ВСЕХ бэкендах, а не только Pluto/QO-100. Активна только в DUP; без DUP
кнопка disabled и звук на TX не проходит (движок гейтит по
FKeepRXDuringTX=FDisplayDuplex — двойная защита: аудио-гейт + RXA не
кормится). Добавлен RadioController.DisplayDuplex. TFlatButton теперь
рисует disabled-состояние приглушённым (текст/рамка к фону).

TX pre-roll 8144->6096 (2 блока подушки вместо 3): обоснование «нужно 3
блока» опиралось на щелчок ~0.5с после старта, который на деле оказался
bfo-глитчом тона (устранён f2d0519), а не underrun'ом подушки. Латентность
старта TX ~-10мс; startup-щелчков под нагрузкой DUP не слышно (подтверждено).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 16:09:26 +03:00
co-authored by Claude Opus 4.8
parent f2d0519c9e
commit ff2082ab04
3 changed files with 49 additions and 21 deletions
+12 -10
View File
@@ -1349,6 +1349,7 @@ begin
// через SetDuplex → rfDuplex). Раньше QO-100 включал DUP, но кнопку не
// подсвечивал → визуал расходился с состоянием.
if BtnDUP <> nil then StyleButton(BtnDUP, FController.FDisplayDuplex);
UpdateRxMuteButton; // активность RX MUTE зависит от DUP
// На передаче переключаем источник пикселей и сбрасываем буферы, чтобы
// старые TX/RX bin'ы не оставались на следующем кадре.
if FController.FTransmitting then
@@ -1791,14 +1792,14 @@ begin
BtnDUP.Tag := 0;
StyleButton(BtnDUP, FController.FDisplayDuplex);
// QO-100 self-monitor: рядом с DUP, col1. Виден только на Pluto в full-duplex
// транспондере. ON (подсвечена, умолч.) = RX-аудио глушится на TX; OFF = слышим
// свой downlink со спутника.
// Self-monitor RX-аудио на TX: рядом с DUP, col1. Видна на любом бэкенде,
// активна только в DUP (см. UpdateRxMuteButton). ON (подсвечена, умолч.) =
// RX-аудио глушится на TX; OFF = слышим свой downlink во время передачи.
BtnRxMute := MakeBtn(PanelTX, 'RX MUTE',
LeftPanelButtonLeft(LEFT_W, 4, 1), 44,
LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnRxMuteClick);
BtnRxMute.Visible := False;
StyleButton(BtnRxMute, FController.FRxMuteOnTx);
UpdateRxMuteButton; // видимость/активность по DUP
LblDrv := TLabel.Create(Self);
LblDrv.Parent := PanelTX; LblDrv.Left := 4; LblDrv.Top := 77;
@@ -4203,14 +4204,15 @@ begin
end;
procedure TMainForm.UpdateRxMuteButton;
// Кнопка RX MUTE видна только на Pluto в full-duplex транспондере (QO-100).
// ON (подсвечена) = RX-аудио глушится на TX; OFF = self-monitor (слышим себя).
var Vis: Boolean;
// Кнопка RX MUTE видна на ЛЮБОМ бэкенде, но АКТИВНА только в DUP (self-monitor
// осмыслен лишь в full-duplex; без DUP звук на TX и так не проходит — движок
// гейтит по FKeepRXDuringTX=FDisplayDuplex). ON (подсвечена) = RX-аудио глушится
// на TX; OFF = слышим свой downlink во время передачи.
begin
if BtnRxMute = nil then Exit;
Vis := FController.InQO100;
BtnRxMute.Visible := Vis;
if Vis then StyleButton(BtnRxMute, FController.FRxMuteOnTx);
BtnRxMute.Visible := True;
BtnRxMute.Enabled := FController.DisplayDuplex;
StyleButton(BtnRxMute, FController.FRxMuteOnTx);
end;
procedure TMainForm.UpdateBandPlanOverlay;