diff --git a/HPSDRNetwork.pas b/HPSDRNetwork.pas index cb2e938..b3ffbcb 100644 --- a/HPSDRNetwork.pas +++ b/HPSDRNetwork.pas @@ -1390,9 +1390,11 @@ begin // Если активен XVTR с RX antenna override (XvtrRxAnt 1..3) — он переопределяет // RX антенну. TX антенна для XVTR всё равно берётся из Alex.TxAnt[band] — // обычно та же что для основного HF-band за этой IF-частотой. - BandIdx := FreqToBandIndex(RXFreqHz); + BandIdx := FreqToBandIndex(RXFreqHz); // RX-банд (антенна RX + вторичный вход) + // TX-антенна — по TX-частоте (кросс-банд мультислайс-TX: TX-слайс может быть + // в другом диапазоне, чем RX). RX-антенна — по RX-частоте. if Transmitting then - AntVal := Alex.TxAnt[BandIdx] + AntVal := Alex.TxAnt[FreqToBandIndex(TXFreqHz)] else if (XvtrRxAnt >= 1) and (XvtrRxAnt <= 3) then AntVal := XvtrRxAnt else diff --git a/MainForm.pas b/MainForm.pas index 08ea2a1..eb15f3a 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -573,6 +573,7 @@ type procedure OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string); procedure OnVfoOverlaySplit(Sender: TObject); procedure OnVfoOverlayTxSelect(Sender: TObject); + procedure RefreshTxIndicators; // TX-бейджи всех флагов procedure OnVfoOverlaySliceSelect(Sender: TObject); // выбрать слайс активным procedure OnVfoOverlayClose(Sender: TObject); // × — удалить слайс procedure OnVfoOverlayInvalidate(Sender: TObject); @@ -1351,6 +1352,7 @@ begin FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex; FSpecView.TXOverlay := FController.FTransmitting; ApplySpecViewGridFromState; // сетка спектра под активный тракт + RefreshTxIndicators; // TX-бейджи флагов (какой слайс передаёт) end; // (DUC-очередь сбрасывает SetMOX) rfTuning: StyleButton(BtnTUN, FController.FTuning); @@ -5385,10 +5387,19 @@ end; procedure TMainForm.OnVfoOverlayTxSelect(Sender: TObject); begin - // TODO(слайсы): выбор TX-слайса при полноценной мультислайс-передаче. - // Пока передаёт только главный канал — клик по TX неглавного игнорируем. - if CurSliceId <> 0 then Exit; + // Мультислайс-TX: клик по TX-бейджу делает слайс источником передачи + // (эксклюзивно). CurSliceId=0 — главный VFO. + FController.SetTxSlice(CurSliceId); // → Changed(rfTransmitting) → RefreshTxIndicators +end; + +procedure TMainForm.RefreshTxIndicators; +// Обновить TX-бейджи (TxSel/Tx) на всех флагах всех панов после смены +// TX-слайса / состояния передачи. +var i: Integer; +begin FPan.PushMainFlagExtState; + for i := 0 to MAX_PANS - 1 do + if FPans[i] <> nil then FPans[i].PushAllSliceFlagStates; end; procedure TMainForm.OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string); diff --git a/PanafallPanel.pas b/PanafallPanel.pas index ce93591..35d10d7 100644 --- a/PanafallPanel.pas +++ b/PanafallPanel.pas @@ -1315,7 +1315,10 @@ begin O.SetState(S.Mode, BW, S.TargetHz, FController.SliceSMeter(SliceId)); O.SetDSPState(0, 0, False, False, FController.FAGCMode); O.SetSquelchState(S.FMSQOn, S.FMSQLevel); - O.SetExtState(Round(S.Volume * 100), S.Muted, False, False, False); + // TxSel: слайс — источник передачи; Tx: передача идёт именно с него. + O.SetExtState(Round(S.Volume * 100), S.Muted, False, + FController.FTransmitting and (FController.FTxSliceId = SliceId), + FController.FTxSliceId = SliceId); // Список аудио-устройств (общий PA-список) + текущее устройство слайса. if Assigned(FController.FAudioOut) then begin @@ -1342,9 +1345,11 @@ end; procedure TPanafallPanel.PushMainFlagExtState; begin if not (Assigned(FMainFlag) and FMainFlag.Visible) or (FController = nil) then Exit; - // TxSel: этот слайс выбран для передачи. Пока TX только главный → всегда True. + // Главный VFO = «слайс A» (id 0). TxSel/Tx активны, когда TX-источник = главный. FMainFlag.SetExtState(FController.ActiveVolume, FController.FMuted, - FController.FSplitTxB, FController.FTransmitting, True); + FController.FSplitTxB, + FController.FTransmitting and (FController.FTxSliceId = 0), + FController.FTxSliceId = 0); end; procedure TPanafallPanel.PushMainFlagAudioDevices; diff --git a/RadioController.pas b/RadioController.pas index c1f08ba..9940eb4 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -263,6 +263,7 @@ type FTransmitting: Boolean; FTuning: Boolean; FSplitTxB: Boolean; + FTxSliceId: Integer; // 0 = главный VFO (A/B split); >0 = слайс-источник TX FVfoLock: Boolean; // блокировка перестройки VFO (Andromeda Lock) FDisplayDuplex: Boolean; // QO-100 full-duplex self-monitor: True — RX-аудио глушится на TX (по умолч.), @@ -427,6 +428,13 @@ type // uplink-LO не подвержен дрейфу LNB). function XvtrTranslateTX(VisibleHz: Double): Double; function ActiveTXFreqHz: Double; + // Режим передающего тракта: слайс-источник TX (FTxSliceId) либо главный FMode. + function ActiveTXMode: Integer; + // Диапазон для калибровки драйва/антенны TX (банд TX-слайса при кросс-банде). + function TxBandIdx: Integer; + // Выбор слайса-источника передачи (0 = главный VFO). Эксклюзивно. + procedure SetTxSlice(SliceId: Integer); + property TxSliceId: Integer read FTxSliceId; // Складывает коррекцию маяка dLock в LOError активного трансвертера (живая // калибровка LNB), ретюнит радио и троттлингом сохраняет на диск. procedure ApplyBeaconLOError(dLock: Double); @@ -1489,6 +1497,8 @@ var idx: Integer; begin idx := FindSliceIndex(Id); if idx < 0 then Exit; + // Удаляемый слайс был источником TX — вернуть передачу на главный VFO. + if FTxSliceId = Id then SetTxSlice(0); if Assigned(FDSPEngine) then FDSPEngine.RemoveSlice(Id); if FSlices[idx].Audio <> nil then begin @@ -1757,7 +1767,12 @@ begin end; function TRadioController.ActiveTXFreqHz: Double; +var S: TCtrlSlice; begin + // Мультислайс-TX: если выбран слайс-источник — передаём на его частоте + // (слайсы не несут repeater-конфиг, FM-сдвиг не применяем). + if (FTxSliceId > 0) and GetSlice(FTxSliceId, S) then + Exit(S.TargetHz); // TX-VFO задаётся ТОЛЬКО селектором FSplitTxB (UI: кнопки TX-A/TX-B), // независимо от активного RX-VFO. Раньше при не-split TX ошибочно следовал // за активным RX (TX-A горит, но передача шла на B при активном B). @@ -1770,6 +1785,41 @@ begin end; end; +function TRadioController.ActiveTXMode: Integer; +var S: TCtrlSlice; +begin + if (FTxSliceId > 0) and GetSlice(FTxSliceId, S) then Result := S.Mode + else Result := FMode; +end; + +function TRadioController.TxBandIdx: Integer; +begin + // При TX на слайсе другого диапазона (только КВ) калибровка/антенна — по + // банду TX-частоты, иначе главный банд. + Result := FCurrentBand; + if (FTxSliceId > 0) and (FCurrentXvtr < 0) then + begin + Result := FreqToBandIdx(ActiveTXFreqHz, IsPluto); + if (Result < 0) or (Result >= CFG_BAND_COUNT) then Result := FCurrentBand; + end; +end; + +procedure TRadioController.SetTxSlice(SliceId: Integer); +var S: TCtrlSlice; +begin + if SliceId < 0 then SliceId := 0; + // Слайс-источник должен существовать (0 = главный всегда валиден). + if (SliceId > 0) and not GetSlice(SliceId, S) then Exit; + if FTxSliceId = SliceId then Exit; + FTxSliceId := SliceId; + // Передающий тракт под режим нового источника (idle-канал, применится к PTT). + if FWDSPReady and Assigned(FDSPEngine) then + FDSPEngine.SetTXMode(ActiveTXMode); + FDriveLevel := CalcDriveByte; // калибровка драйва банда TX-слайса + PushNetworkState; // TX-частота/Alex через ActiveTXFreqHz/TxBandIdx + Changed(rfTransmitting); // UI: перерисовать TX-бейджи флагов/оверлей +end; + // --------------------------------------------------------------------------- // QO-100 beacon lock // --------------------------------------------------------------------------- @@ -2118,8 +2168,8 @@ begin FXvtrSettings.Entries[FCurrentXvtr].Enabled; if XvtrActive then Cal := FVHFBandCal[FCurrentXvtr] - else if (FCurrentBand >= 0) and (FCurrentBand < CFG_BAND_COUNT) then - Cal := FPABandCal[FCurrentBand] + else if (TxBandIdx >= 0) and (TxBandIdx < CFG_BAND_COUNT) then + Cal := FPABandCal[TxBandIdx] // банд TX-слайса при кросс-банд TX else Cal := 38.8; @@ -3581,7 +3631,7 @@ var begin // Safety: блокируем TX на бэндах с DoNotTx (Alex) и на RX-only трансвертере. if On_ and FDevConnected and (FCurrentXvtr < 0) - and FAlexSettings.DoNotTx[EnsureRange(FCurrentBand, 0, 10)] then + 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 @@ -3640,6 +3690,9 @@ begin // В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX, чтобы // TX leakage не накапливался в RXA pipeline / FFT-истории анализатора. FDSPEngine.KeepRXDuringTX := FDisplayDuplex; + // Мультислайс-TX: передающий тракт под режим слайса-источника (SetMode мог + // оставить главный режим). Ставим перед стартом TX-потока. + if FTransmitting then FDSPEngine.SetTXMode(ActiveTXMode); FDSPEngine.SetTXRun(FTransmitting); if WasTransmitting and (not FTransmitting) and (not FDisplayDuplex) then begin @@ -3677,7 +3730,7 @@ begin // Safety: те же блокировки, что и в SetMOX — TUN это TX через WDSP PostGen-тон, // он тоже должен молчать на бэндах с DoNotTx и на RX-only XVTR. if On_ and FDevConnected and (FCurrentXvtr < 0) - and FAlexSettings.DoNotTx[EnsureRange(FCurrentBand, 0, 10)] then Exit; + 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_ then diff --git a/ewsdr.lpi b/ewsdr.lpi index c17cf9f..29f6e11 100644 --- a/ewsdr.lpi +++ b/ewsdr.lpi @@ -17,9 +17,9 @@ - + - + @@ -96,6 +96,7 @@ +