diff --git a/RadioController.pas b/RadioController.pas index bb63a69..3a44aca 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -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-тракта — первые блоки уже с тонами.