From 45d10698eedceb13164517f0a22b10a0f3084e91 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 10 Aug 2026 14:09:05 +0300 Subject: [PATCH] =?UTF-8?q?fix(cw):=20=D0=B7=D0=B0=D0=BF=D1=80=D0=B5=D1=82?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D1=83=D0=B5=D1=82=20=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=B5=D0=B9=D0=B5=D1=80=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B2=D0=BA=D0=B8=20(RX-only=20XVTR,=20DoNot?= =?UTF-8?q?Tx)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Аудит трансвертерного режима против дефектов этой сессии нашёл ту же болезнь, что и всё остальное: защита жила только на пути через софтовый 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 --- RadioController.pas | 53 +++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 19 deletions(-) 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-тракта — первые блоки уже с тонами.