mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(slices): мультислайс-TX — выбор TX-слайса, кросс-банд (Flex-паритет)
- RadioController: FTxSliceId (0=главный VFO, >0=слайс), SetTxSlice эксклюзивно; ActiveTXFreqHz/ActiveTXMode с TX-слайса, режим применяется в SetMOX(True) перед SetTXRun; TxBandIdx для PA-кал и safety DoNotTx при кросс-банде; RemoveSlice сбрасывает TX-слайс. - HPSDRNetwork: TX-антенна выбиралась по RX-бэнду → по TX-частоте (TxAnt[FreqToBandIndex(TXFreqHz)]). - MainForm/PanafallPanel: клик TX на флаге слайса → SetTxSlice, TX-бейджи (янтарь=выбран, красный=передаёт), RefreshTxIndicators по rfTransmitting. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -1390,9 +1390,11 @@ begin
|
|||||||
// Если активен XVTR с RX antenna override (XvtrRxAnt 1..3) — он переопределяет
|
// Если активен XVTR с RX antenna override (XvtrRxAnt 1..3) — он переопределяет
|
||||||
// RX антенну. TX антенна для XVTR всё равно берётся из Alex.TxAnt[band] —
|
// RX антенну. TX антенна для XVTR всё равно берётся из Alex.TxAnt[band] —
|
||||||
// обычно та же что для основного HF-band за этой IF-частотой.
|
// обычно та же что для основного HF-band за этой IF-частотой.
|
||||||
BandIdx := FreqToBandIndex(RXFreqHz);
|
BandIdx := FreqToBandIndex(RXFreqHz); // RX-банд (антенна RX + вторичный вход)
|
||||||
|
// TX-антенна — по TX-частоте (кросс-банд мультислайс-TX: TX-слайс может быть
|
||||||
|
// в другом диапазоне, чем RX). RX-антенна — по RX-частоте.
|
||||||
if Transmitting then
|
if Transmitting then
|
||||||
AntVal := Alex.TxAnt[BandIdx]
|
AntVal := Alex.TxAnt[FreqToBandIndex(TXFreqHz)]
|
||||||
else if (XvtrRxAnt >= 1) and (XvtrRxAnt <= 3) then
|
else if (XvtrRxAnt >= 1) and (XvtrRxAnt <= 3) then
|
||||||
AntVal := XvtrRxAnt
|
AntVal := XvtrRxAnt
|
||||||
else
|
else
|
||||||
|
|||||||
+14
-3
@@ -573,6 +573,7 @@ type
|
|||||||
procedure OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
|
procedure OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
|
||||||
procedure OnVfoOverlaySplit(Sender: TObject);
|
procedure OnVfoOverlaySplit(Sender: TObject);
|
||||||
procedure OnVfoOverlayTxSelect(Sender: TObject);
|
procedure OnVfoOverlayTxSelect(Sender: TObject);
|
||||||
|
procedure RefreshTxIndicators; // TX-бейджи всех флагов
|
||||||
procedure OnVfoOverlaySliceSelect(Sender: TObject); // выбрать слайс активным
|
procedure OnVfoOverlaySliceSelect(Sender: TObject); // выбрать слайс активным
|
||||||
procedure OnVfoOverlayClose(Sender: TObject); // × — удалить слайс
|
procedure OnVfoOverlayClose(Sender: TObject); // × — удалить слайс
|
||||||
procedure OnVfoOverlayInvalidate(Sender: TObject);
|
procedure OnVfoOverlayInvalidate(Sender: TObject);
|
||||||
@@ -1351,6 +1352,7 @@ begin
|
|||||||
FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex;
|
FSpecView.TXMode := FController.FTransmitting and not FController.FDisplayDuplex;
|
||||||
FSpecView.TXOverlay := FController.FTransmitting;
|
FSpecView.TXOverlay := FController.FTransmitting;
|
||||||
ApplySpecViewGridFromState; // сетка спектра под активный тракт
|
ApplySpecViewGridFromState; // сетка спектра под активный тракт
|
||||||
|
RefreshTxIndicators; // TX-бейджи флагов (какой слайс передаёт)
|
||||||
end; // (DUC-очередь сбрасывает SetMOX)
|
end; // (DUC-очередь сбрасывает SetMOX)
|
||||||
rfTuning:
|
rfTuning:
|
||||||
StyleButton(BtnTUN, FController.FTuning);
|
StyleButton(BtnTUN, FController.FTuning);
|
||||||
@@ -5385,10 +5387,19 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.OnVfoOverlayTxSelect(Sender: TObject);
|
procedure TMainForm.OnVfoOverlayTxSelect(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
// TODO(слайсы): выбор TX-слайса при полноценной мультислайс-передаче.
|
// Мультислайс-TX: клик по TX-бейджу делает слайс источником передачи
|
||||||
// Пока передаёт только главный канал — клик по TX неглавного игнорируем.
|
// (эксклюзивно). CurSliceId=0 — главный VFO.
|
||||||
if CurSliceId <> 0 then Exit;
|
FController.SetTxSlice(CurSliceId); // → Changed(rfTransmitting) → RefreshTxIndicators
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.RefreshTxIndicators;
|
||||||
|
// Обновить TX-бейджи (TxSel/Tx) на всех флагах всех панов после смены
|
||||||
|
// TX-слайса / состояния передачи.
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
FPan.PushMainFlagExtState;
|
FPan.PushMainFlagExtState;
|
||||||
|
for i := 0 to MAX_PANS - 1 do
|
||||||
|
if FPans[i] <> nil then FPans[i].PushAllSliceFlagStates;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
|
procedure TMainForm.OnVfoOverlayAudioDevice(DevIndex: Integer; const DevName: string);
|
||||||
|
|||||||
+8
-3
@@ -1315,7 +1315,10 @@ begin
|
|||||||
O.SetState(S.Mode, BW, S.TargetHz, FController.SliceSMeter(SliceId));
|
O.SetState(S.Mode, BW, S.TargetHz, FController.SliceSMeter(SliceId));
|
||||||
O.SetDSPState(0, 0, False, False, FController.FAGCMode);
|
O.SetDSPState(0, 0, False, False, FController.FAGCMode);
|
||||||
O.SetSquelchState(S.FMSQOn, S.FMSQLevel);
|
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-список) + текущее устройство слайса.
|
// Список аудио-устройств (общий PA-список) + текущее устройство слайса.
|
||||||
if Assigned(FController.FAudioOut) then
|
if Assigned(FController.FAudioOut) then
|
||||||
begin
|
begin
|
||||||
@@ -1342,9 +1345,11 @@ end;
|
|||||||
procedure TPanafallPanel.PushMainFlagExtState;
|
procedure TPanafallPanel.PushMainFlagExtState;
|
||||||
begin
|
begin
|
||||||
if not (Assigned(FMainFlag) and FMainFlag.Visible) or (FController = nil) then Exit;
|
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,
|
FMainFlag.SetExtState(FController.ActiveVolume, FController.FMuted,
|
||||||
FController.FSplitTxB, FController.FTransmitting, True);
|
FController.FSplitTxB,
|
||||||
|
FController.FTransmitting and (FController.FTxSliceId = 0),
|
||||||
|
FController.FTxSliceId = 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPanafallPanel.PushMainFlagAudioDevices;
|
procedure TPanafallPanel.PushMainFlagAudioDevices;
|
||||||
|
|||||||
+57
-4
@@ -263,6 +263,7 @@ type
|
|||||||
FTransmitting: Boolean;
|
FTransmitting: Boolean;
|
||||||
FTuning: Boolean;
|
FTuning: Boolean;
|
||||||
FSplitTxB: Boolean;
|
FSplitTxB: Boolean;
|
||||||
|
FTxSliceId: Integer; // 0 = главный VFO (A/B split); >0 = слайс-источник TX
|
||||||
FVfoLock: Boolean; // блокировка перестройки VFO (Andromeda Lock)
|
FVfoLock: Boolean; // блокировка перестройки VFO (Andromeda Lock)
|
||||||
FDisplayDuplex: Boolean;
|
FDisplayDuplex: Boolean;
|
||||||
// QO-100 full-duplex self-monitor: True — RX-аудио глушится на TX (по умолч.),
|
// QO-100 full-duplex self-monitor: True — RX-аудио глушится на TX (по умолч.),
|
||||||
@@ -427,6 +428,13 @@ type
|
|||||||
// uplink-LO не подвержен дрейфу LNB).
|
// uplink-LO не подвержен дрейфу LNB).
|
||||||
function XvtrTranslateTX(VisibleHz: Double): Double;
|
function XvtrTranslateTX(VisibleHz: Double): Double;
|
||||||
function ActiveTXFreqHz: 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 активного трансвертера (живая
|
// Складывает коррекцию маяка dLock в LOError активного трансвертера (живая
|
||||||
// калибровка LNB), ретюнит радио и троттлингом сохраняет на диск.
|
// калибровка LNB), ретюнит радио и троттлингом сохраняет на диск.
|
||||||
procedure ApplyBeaconLOError(dLock: Double);
|
procedure ApplyBeaconLOError(dLock: Double);
|
||||||
@@ -1489,6 +1497,8 @@ var idx: Integer;
|
|||||||
begin
|
begin
|
||||||
idx := FindSliceIndex(Id);
|
idx := FindSliceIndex(Id);
|
||||||
if idx < 0 then Exit;
|
if idx < 0 then Exit;
|
||||||
|
// Удаляемый слайс был источником TX — вернуть передачу на главный VFO.
|
||||||
|
if FTxSliceId = Id then SetTxSlice(0);
|
||||||
if Assigned(FDSPEngine) then FDSPEngine.RemoveSlice(Id);
|
if Assigned(FDSPEngine) then FDSPEngine.RemoveSlice(Id);
|
||||||
if FSlices[idx].Audio <> nil then
|
if FSlices[idx].Audio <> nil then
|
||||||
begin
|
begin
|
||||||
@@ -1757,7 +1767,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TRadioController.ActiveTXFreqHz: Double;
|
function TRadioController.ActiveTXFreqHz: Double;
|
||||||
|
var S: TCtrlSlice;
|
||||||
begin
|
begin
|
||||||
|
// Мультислайс-TX: если выбран слайс-источник — передаём на его частоте
|
||||||
|
// (слайсы не несут repeater-конфиг, FM-сдвиг не применяем).
|
||||||
|
if (FTxSliceId > 0) and GetSlice(FTxSliceId, S) then
|
||||||
|
Exit(S.TargetHz);
|
||||||
// TX-VFO задаётся ТОЛЬКО селектором FSplitTxB (UI: кнопки TX-A/TX-B),
|
// TX-VFO задаётся ТОЛЬКО селектором FSplitTxB (UI: кнопки TX-A/TX-B),
|
||||||
// независимо от активного RX-VFO. Раньше при не-split TX ошибочно следовал
|
// независимо от активного RX-VFO. Раньше при не-split TX ошибочно следовал
|
||||||
// за активным RX (TX-A горит, но передача шла на B при активном B).
|
// за активным RX (TX-A горит, но передача шла на B при активном B).
|
||||||
@@ -1770,6 +1785,41 @@ begin
|
|||||||
end;
|
end;
|
||||||
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
|
// QO-100 beacon lock
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -2118,8 +2168,8 @@ begin
|
|||||||
FXvtrSettings.Entries[FCurrentXvtr].Enabled;
|
FXvtrSettings.Entries[FCurrentXvtr].Enabled;
|
||||||
if XvtrActive then
|
if XvtrActive then
|
||||||
Cal := FVHFBandCal[FCurrentXvtr]
|
Cal := FVHFBandCal[FCurrentXvtr]
|
||||||
else if (FCurrentBand >= 0) and (FCurrentBand < CFG_BAND_COUNT) then
|
else if (TxBandIdx >= 0) and (TxBandIdx < CFG_BAND_COUNT) then
|
||||||
Cal := FPABandCal[FCurrentBand]
|
Cal := FPABandCal[TxBandIdx] // банд TX-слайса при кросс-банд TX
|
||||||
else
|
else
|
||||||
Cal := 38.8;
|
Cal := 38.8;
|
||||||
|
|
||||||
@@ -3581,7 +3631,7 @@ var
|
|||||||
begin
|
begin
|
||||||
// Safety: блокируем TX на бэндах с DoNotTx (Alex) и на RX-only трансвертере.
|
// Safety: блокируем TX на бэндах с DoNotTx (Alex) и на RX-only трансвертере.
|
||||||
if On_ and FDevConnected and (FCurrentXvtr < 0)
|
if On_ and FDevConnected and (FCurrentXvtr < 0)
|
||||||
and FAlexSettings.DoNotTx[EnsureRange(FCurrentBand, 0, 10)] then
|
and FAlexSettings.DoNotTx[EnsureRange(TxBandIdx, 0, 10)] then
|
||||||
Exit;
|
Exit;
|
||||||
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
|
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
|
||||||
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then
|
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then
|
||||||
@@ -3640,6 +3690,9 @@ begin
|
|||||||
// В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX, чтобы
|
// В non-DUP RX-IQ пакеты дропаются на входе DSP-потока во время TX, чтобы
|
||||||
// TX leakage не накапливался в RXA pipeline / FFT-истории анализатора.
|
// TX leakage не накапливался в RXA pipeline / FFT-истории анализатора.
|
||||||
FDSPEngine.KeepRXDuringTX := FDisplayDuplex;
|
FDSPEngine.KeepRXDuringTX := FDisplayDuplex;
|
||||||
|
// Мультислайс-TX: передающий тракт под режим слайса-источника (SetMode мог
|
||||||
|
// оставить главный режим). Ставим перед стартом TX-потока.
|
||||||
|
if FTransmitting then FDSPEngine.SetTXMode(ActiveTXMode);
|
||||||
FDSPEngine.SetTXRun(FTransmitting);
|
FDSPEngine.SetTXRun(FTransmitting);
|
||||||
if WasTransmitting and (not FTransmitting) and (not FDisplayDuplex) then
|
if WasTransmitting and (not FTransmitting) and (not FDisplayDuplex) then
|
||||||
begin
|
begin
|
||||||
@@ -3677,7 +3730,7 @@ begin
|
|||||||
// Safety: те же блокировки, что и в SetMOX — TUN это TX через WDSP PostGen-тон,
|
// Safety: те же блокировки, что и в SetMOX — TUN это TX через WDSP PostGen-тон,
|
||||||
// он тоже должен молчать на бэндах с DoNotTx и на RX-only XVTR.
|
// он тоже должен молчать на бэндах с DoNotTx и на RX-only XVTR.
|
||||||
if On_ and FDevConnected and (FCurrentXvtr < 0)
|
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)
|
if On_ and (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT)
|
||||||
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
|
and FXvtrSettings.Entries[FCurrentXvtr].RXOnly then Exit;
|
||||||
if On_ then
|
if On_ then
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<UseVersionInfo Value="True"/>
|
<UseVersionInfo Value="True"/>
|
||||||
<AutoIncrementBuild Value="True"/>
|
<AutoIncrementBuild Value="True"/>
|
||||||
<MinorVersionNr Value="9"/>
|
<MinorVersionNr Value="9"/>
|
||||||
<BuildNr Value="257"/>
|
<BuildNr Value="258"/>
|
||||||
</VersionInfo>
|
</VersionInfo>
|
||||||
<MacroValues Count="76">
|
<MacroValues Count="77">
|
||||||
<Macro1 Name="LCLWidgetType" Value="qt6"/>
|
<Macro1 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro2 Name="LCLWidgetType" Value="qt6"/>
|
<Macro2 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro3 Name="LCLWidgetType" Value="qt6"/>
|
<Macro3 Name="LCLWidgetType" Value="qt6"/>
|
||||||
@@ -96,6 +96,7 @@
|
|||||||
<Macro74 Name="LCLWidgetType" Value="qt6"/>
|
<Macro74 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro75 Name="LCLWidgetType" Value="qt6"/>
|
<Macro75 Name="LCLWidgetType" Value="qt6"/>
|
||||||
<Macro76 Name="LCLWidgetType" Value="qt6"/>
|
<Macro76 Name="LCLWidgetType" Value="qt6"/>
|
||||||
|
<Macro77 Name="LCLWidgetType" Value="qt6"/>
|
||||||
</MacroValues>
|
</MacroValues>
|
||||||
<BuildModes>
|
<BuildModes>
|
||||||
<Item Name="Debug" Default="True"/>
|
<Item Name="Debug" Default="True"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user