diff --git a/MainForm.pas b/MainForm.pas index e87daad..f57425a 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -416,6 +416,7 @@ type // Public UI update (called from sync objects) procedure UpdateTXMeters; + procedure UpdateTXControlAvailability; procedure UpdateStatusField7; // поле 7: PLL (HPSDR) или beacon (Pluto) procedure UpdateStatusField3; // поле 3: Supply (HPSDR) или Temp/RSSI (Pluto) function BeaconStatusText: string; // компактный статус QO-100 beacon lock @@ -1362,6 +1363,7 @@ begin (FController.FTxSliceId = 0); ApplySpecViewGridFromState; // сетка спектра под активный тракт RefreshTxIndicators; // TX-бейджи флагов (какой слайс передаёт) + UpdateTXControlAvailability; end; // (DUC-очередь сбрасывает SetMOX) rfTuning: StyleButton(BtnTUN, FController.FTuning); @@ -1374,7 +1376,7 @@ begin InhibitSleep; BtnStartStop.Caption := 'STOP'; StyleButton(BtnStartStop, True); - BtnMOX.Enabled := True; + UpdateTXControlAvailability; UpdateAddPanButton; // «⊞» доступна только в Run ResizeSpectrumPanels; RestorePanWorkspace; // паны+слайсы прошлой сессии @@ -1384,7 +1386,7 @@ begin UninhibitSleep; BtnStartStop.Caption := 'START'; StyleButton(BtnStartStop, False); - BtnMOX.Enabled := False; + UpdateTXControlAvailability; // Снимок панов уже сделан хуком OnBeforeStop=SavePanWorkspace (до // гашения DDC в StopRunning). Здесь только разрешаем повторный // restore на следующем старте. @@ -1460,6 +1462,7 @@ begin FController.FFilterBW := SavedFilterBW; for i := 0 to FILT_COUNT - 1 do StyleButton(BtnFilter[i], i = FController.FFilter); SyncSpecViewFreq; // иначе полоса фильтра рисуется под старый режим + UpdateTXControlAvailability; end; rfFilter: begin @@ -1945,6 +1948,7 @@ begin BtnTUN := MakeBtn(PanelTX, 'TUN', LeftPanelButtonLeft(LEFT_W, 4, 1), 16, LeftPanelButtonWidth(LEFT_W, 4, 1), BTN_SM, BtnTUNClick); + UpdateTXControlAvailability; BtnDUP := MakeBtn(PanelTX, 'DUP', LeftPanelButtonLeft(LEFT_W, 4, 0), 44, @@ -3369,7 +3373,7 @@ begin UninhibitSleep; FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0); SetRadioOfflineStatus('Hardware timeout'); - BtnMOX.Enabled := False; + UpdateTXControlAvailability; Exit; end; // После первого пакета — следим чтобы поток не прерывался более 3 сек @@ -3381,7 +3385,7 @@ begin UninhibitSleep; FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0); SetRadioOfflineStatus('Connection lost'); - BtnMOX.Enabled := False; + UpdateTXControlAvailability; Exit; end; end; @@ -3516,6 +3520,16 @@ begin UpdateStatusField3; // Supply (openHPSDR) или Temp/RSSI (Pluto) end; +procedure TMainForm.UpdateTXControlAvailability; +var Allowed: Boolean; +begin + if (BtnMOX = nil) or (BtnTUN = nil) or (FController = nil) then Exit; + Allowed := FController.FRunning and + (FController.ActiveTXMode <> MODE_DMR); + BtnMOX.Enabled := Allowed; + BtnTUN.Enabled := Allowed; +end; + procedure TMainForm.UpdateStatusField3; // Поле 3 статус-бара. openHPSDR — supply V/A (HP status). Pluto — температура // чипа и RSSI приёмника (нет PA → supply неприменим). Значения берём из diff --git a/RadioController.pas b/RadioController.pas index d0e1753..373c536 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -2145,6 +2145,13 @@ begin if S.Mode = MODE_DMR then Exit; end; if FTxSliceId = SliceId then Exit; + // Selecting main A while it is in DMR is allowed as an RX selection, but it + // cannot inherit an active carrier from a previously selected TX slice. + if (SliceId = 0) and (FMode = MODE_DMR) then + begin + if FTuning then SetTune(False) + else if FTransmitting then SetMOX(False); + end; FTxSliceId := SliceId; // Передающий тракт под режим нового источника (idle-канал, применится к PTT). if FWDSPReady and Assigned(FDSPEngine) then @@ -3143,6 +3150,13 @@ end; procedure TRadioController.SetMode(M: Integer); begin FMode := M; + // Entering receive-only DMR on the selected main TX source immediately + // releases an existing carrier. SetTune(False) also releases MOX. + if ActiveTXMode = MODE_DMR then + begin + if FTuning then SetTune(False) + else if FTransmitting then SetMOX(False); + end; if Assigned(FDMRDec) then FDMRDec.SetEnabled(FMode = MODE_DMR); ApplyModeDefaults; // дефолтный фильтр/девиация под режим FBandCache[FCurrentBand].Mode := FMode; @@ -4026,6 +4040,9 @@ procedure TRadioController.SetMOX(On_: Boolean); var WasTransmitting: Boolean; begin + // DMR support is receive-only. This guard also covers CAT, web and hardware + // PTT. A selected non-DMR TX slice remains valid while main A receives DMR. + 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 @@ -4128,6 +4145,7 @@ end; procedure TRadioController.SetTune(On_: Boolean); begin if On_ = FTuning then Exit; + if On_ and (ActiveTXMode = MODE_DMR) then Exit; // Safety: те же блокировки, что и в SetMOX — TUN это TX через WDSP PostGen-тон, // он тоже должен молчать на бэндах с DoNotTx и на RX-only XVTR. if On_ and FDevConnected and (FCurrentXvtr < 0)