fix(cw): запрет передачи действует и на кейер прошивки (RX-only XVTR, DoNotTx)

Аудит трансвертерного режима против дефектов этой сессии нашёл ту же болезнь,
что и всё остальное: защита жила только на пути через софтовый MOX.

Бэнд с DoNotTx (Alex) и RX-only слот трансвертера проверялись тремя копиями
кода — в SetMOX, SetTune и SetTwoTone. С кейером в прошивке ни одна из них не
исполняется: на приёмном трансвертере замыкание ключа выходило в эфир, а там
на выходе обычно вход конвертера, а не антенна.

Три копии сведены в TXProhibited; CWTXActive её учитывает, поэтому байт 5
обнуляется и кейер разоружается там, где передавать нельзя. Передача текста
гейтится тем же условием.

Вооружение сделано самовосстанавливающимся: SyncCWKeyer идемпотентен (пара
сравнений плюс ранний выход внутри SetCWKeyerArmed) и вызывается ещё и из
разбора HP-статуса. Разоружить кейер может заход в RX-only слот, переход на
запрещённый бэнд или правка Alex; перечислять такие точки поимённо — гарантия
однажды пропустить очередную, на чём уже дважды обожглись за сегодня.

Остальное в трансвертере проверено и чисто: сдвиг pitch переживает
XvtrTranslateTX (включая split-LO QO-100), множитель мощности слота и
VHF-калибровка уже входят в CalcDriveByte и потому уезжают в байт 345,
OC-выходы идут за ключом через RadioKeyed, ActivateXvtr толкает верную частоту
DUC. Инвертирующие трансвертеры не поддержаны нигде в проекте — это давнее
общее ограничение, не регресс телеграфа.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 14:09:05 +03:00
co-authored by Claude Opus 5
parent e87ca6b186
commit 45d10698ee
+34 -19
View File
@@ -609,6 +609,7 @@ type
procedure SendDUCSpecificFromSettings; // DUC Specific (mic/keyer/atten) в сеть
procedure ApplyTXSettingsToDSP; // TX-цепь (фильтр/gain/EQ/comp/...) → WDSP
// ---- Телеграф ----------------------------------------------------------
function TXProhibited: Boolean; // DoNotTx-бэнд / RX-only XVTR
function CWTXActive: Boolean; // передачей владеет FPGA-кейер
function CWOptionsByte: Byte; // DUC Specific байт 5
procedure SyncCWKeyer; // байт 5 под текущий TX-режим
@@ -1573,6 +1574,9 @@ begin
Changed(rfADCOverload);
end;
// Вооружение кейера — под текущий контекст (бэнд/трансвертер/Alex могли
// поменяться мимо SetMode и SetTxSlice). Вызов идемпотентный, см. SyncCWKeyer.
SyncCWKeyer;
// ★Телеграф с кейером в прошивке: HPS_PTT здесь — ЭХО собственной манипуляции
// (при break-in железо само поднимает T/R на каждую посылку), а не действие
// оператора. Гонять по нему SetMOX нельзя: на каждой точке пересобирался бы
@@ -5097,12 +5101,25 @@ begin
FDSPEngine.SetANF(FANF);
end;
function TRadioController.TXProhibited: Boolean;
// Единая точка запретов передачи: диапазон с DoNotTx (Alex) и RX-only слот
// трансвертера. Раньше эта пара была скопирована в SetMOX/SetTune/SetTwoTone —
// то есть жила ТОЛЬКО на пути через софтовый MOX. С кейером в прошивке этого
// пути нет вовсе, и на RX-only трансвертере ключ спокойно выходил в эфир.
begin
Result := (FDevConnected and (FCurrentXvtr < 0)
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)])
or ((FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly);
end;
function TRadioController.CWTXActive: Boolean;
// «Телеграфом управляет железо»: режим ИСТОЧНИКА передачи — CW и кейер в
// прошивке. В этом состоянии голосовой TXA не запускается вовсе (эталон
// Thetis, console.cs:29570), а точки/тире формирует FPGA.
begin
Result := (ActiveTXMode in [MODE_CWL, MODE_CWU]) and FCWSettings.FWKeyer;
Result := (ActiveTXMode in [MODE_CWL, MODE_CWU]) and FCWSettings.FWKeyer
and (not TXProhibited); // где передавать нельзя — кейер разоружён
end;
function TRadioController.CWOptionsByte: Byte;
@@ -5509,13 +5526,8 @@ begin
// DMR support is receive-only. This guard also covers CAT, web and hardware
// PTT. FM RAW is a flat external-modem transmit path.
if On_ and (ActiveTXMode = MODE_DMR) then Exit;
// Safety: блокируем TX на бэндах с DoNotTx (Alex) и на RX-only трансвертере.
if On_ and FDevConnected and (FCurrentXvtr < 0)
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then
Exit;
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then
Exit;
// Safety: бэнды с DoNotTx (Alex) и RX-only трансвертер — см. TXProhibited.
if On_ and TXProhibited then Exit;
WasTransmitting := FTransmitting;
// Снятие MOX руками обрывает программную передачу текста: оператор решил
// прекратить — ключ обязан отпуститься немедленно.
@@ -5649,10 +5661,7 @@ begin
if On_ and (ActiveTXMode in [MODE_DMR, MODE_FMRAW]) then Exit;
// Safety: те же блокировки, что и в SetMOX — TUN это TX через WDSP PostGen-тон,
// он тоже должен молчать на бэндах с DoNotTx и на RX-only XVTR.
if On_ and FDevConnected and (FCurrentXvtr < 0)
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then Exit;
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
if On_ and TXProhibited then Exit;
if On_ and FTwoTone then SetTwoTone(False); // TUN поверх 2TON — гасим тона
if On_ then
begin
@@ -5759,7 +5768,14 @@ procedure TRadioController.SyncCWKeyer;
// TX-слайса обязана его перепослать: вне телеграфа он должен стать нулевым,
// иначе прошивка поднимет PTT на замыкание ключа посреди SSB.
begin
if not (ActiveTXMode in [MODE_CWL, MODE_CWU]) then CWXAbort;
// Дёшево и идемпотентно: пара сравнений плюс ранний выход внутри
// SetCWKeyerArmed. Поэтому зовётся не только на смене режима/источника, но и
// из разбора HP-статуса — вооружение кейера обязано быть САМОВОССТАНАВЛИВАЮЩИМСЯ.
// Разоружить его может что угодно: заход в RX-only слот трансвертера, переход
// на бэнд с DoNotTx, правка Alex. Перечислять такие точки поимённо — гарантия
// однажды пропустить очередную (ровно так уже вышло с DUC-частотой и мощностью).
if (not (ActiveTXMode in [MODE_CWL, MODE_CWU]))
and Assigned(FCWSender) and FCWSender.Busy then CWXAbort;
// Вооружённый кейер = железо может выйти в эфир само ⇒ бэкенд обязан класть
// уровень мощности в КАЖДЫЙ HP-кадр, а не только на передаче.
if Assigned(FNetwork) then FNetwork.SetCWKeyerArmed(CWTXActive);
@@ -5785,8 +5801,10 @@ procedure TRadioController.CWXSend(const Text: string);
// выключен — оператор держит MOX руками, как при ручном T/R.
begin
if Text = '' then Exit;
if not (ActiveTXMode in [MODE_CWL, MODE_CWU]) then Exit;
if not FCWSettings.FWKeyer then Exit;
// CWTXActive, а не просто режим: он же учитывает запрет передачи (RX-only
// слот трансвертера, бэнд с DoNotTx) — там кейер разоружён, и дёргать бит
// CWX незачем.
if not CWTXActive then Exit;
if not Assigned(FCWSender) then
FCWSender := TCWSender.Create(Self.CWXKeyEvent);
FCWSender.SetSpeed(FCWSettings.Speed, FCWSettings.Weight);
@@ -6132,10 +6150,7 @@ begin
if On_ and (ActiveTXMode in [MODE_DMR, MODE_FMRAW]) then Exit;
if On_ and FTuning then Exit; // не поверх TUN-тона
// Safety: те же блокировки, что SetMOX/SetTune.
if On_ and FDevConnected and (FCurrentXvtr < 0)
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then Exit;
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
if On_ and TXProhibited then Exit;
if On_ then
begin
// Генератор ДО запуска TX-тракта — первые блоки уже с тонами.