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 @@
+