mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
DUP (display duplex) + ATT on TX
- DUP: локальный режим отображения, при TX данные RX1, поверх рисуется TX-фильтр. Кнопка на левой панели рядом с CTUN. Аппаратной командой не является. Отдельный SetDisplaySourceTX в WDSPEngine, разделение TXMode (data) и TXOverlay (filter band) в SpectrumView. - ATT on TX: 0..31 dB, default 31 (Thetis-style). UI на вкладке Transmit в группе Microphone. Передаётся в DUC Specific bytes 57/58/59 (StepAtten0/1/2). Перепосылается при изменении.
This commit is contained in:
+67
-4
@@ -182,6 +182,9 @@ type
|
|||||||
FRunning: Boolean;
|
FRunning: Boolean;
|
||||||
FTransmitting: Boolean;
|
FTransmitting: Boolean;
|
||||||
FSplitTxB: Boolean;
|
FSplitTxB: Boolean;
|
||||||
|
// DUP: при TX продолжаем показывать RX1-водопад/панораму, а TX-фильтр
|
||||||
|
// и TX-линии рисуем поверх. Аппаратной командой не является.
|
||||||
|
FDisplayDuplex: Boolean;
|
||||||
FHWPTTActive: Boolean; // True = hardware PTT нажата
|
FHWPTTActive: Boolean; // True = hardware PTT нажата
|
||||||
FMuted: Boolean;
|
FMuted: Boolean;
|
||||||
FVolume: Integer;
|
FVolume: Integer;
|
||||||
@@ -304,6 +307,7 @@ type
|
|||||||
PanelFilter: TPanel;
|
PanelFilter: TPanel;
|
||||||
BtnFilter: array[0..FILT_COUNT-1] of TFlatButton;
|
BtnFilter: array[0..FILT_COUNT-1] of TFlatButton;
|
||||||
BtnCTun: TFlatButton;
|
BtnCTun: TFlatButton;
|
||||||
|
BtnDUP: TFlatButton;
|
||||||
|
|
||||||
PanelRX: TPanel;
|
PanelRX: TPanel;
|
||||||
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
|
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
|
||||||
@@ -413,6 +417,8 @@ type
|
|||||||
procedure BtnModeClick(Sender: TObject);
|
procedure BtnModeClick(Sender: TObject);
|
||||||
procedure BtnFilterClick(Sender: TObject);
|
procedure BtnFilterClick(Sender: TObject);
|
||||||
procedure BtnCTunClick(Sender: TObject);
|
procedure BtnCTunClick(Sender: TObject);
|
||||||
|
procedure BtnDUPClick(Sender: TObject);
|
||||||
|
procedure ApplyDUP(Active: Boolean);
|
||||||
procedure UpdateFilterButtons;
|
procedure UpdateFilterButtons;
|
||||||
procedure PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
procedure PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
@@ -863,6 +869,7 @@ begin
|
|||||||
Result.AudioSampleRate := FAudioOut.SampleRate;
|
Result.AudioSampleRate := FAudioOut.SampleRate;
|
||||||
Result.ShowSpectrum := FShowSpectrum;
|
Result.ShowSpectrum := FShowSpectrum;
|
||||||
Result.ShowWaterfall := FShowWaterfall;
|
Result.ShowWaterfall := FShowWaterfall;
|
||||||
|
Result.DisplayDuplex := FDisplayDuplex;
|
||||||
Result.DisplayFPS := FDisplayFPS;
|
Result.DisplayFPS := FDisplayFPS;
|
||||||
Result.LightTheme := FLightTheme;
|
Result.LightTheme := FLightTheme;
|
||||||
// Audio device names
|
// Audio device names
|
||||||
@@ -987,6 +994,7 @@ begin
|
|||||||
FRunning := False;
|
FRunning := False;
|
||||||
FTransmitting := False;
|
FTransmitting := False;
|
||||||
FSplitTxB := False;
|
FSplitTxB := False;
|
||||||
|
FDisplayDuplex := False;
|
||||||
FMuted := False;
|
FMuted := False;
|
||||||
FVolume := 70;
|
FVolume := 70;
|
||||||
FCenterFreq := FVfoA;
|
FCenterFreq := FVfoA;
|
||||||
@@ -1541,11 +1549,16 @@ begin
|
|||||||
|
|
||||||
Inc(Y, 76);
|
Inc(Y, 76);
|
||||||
|
|
||||||
// CTUN button
|
// CTUN / DUP buttons (display-related)
|
||||||
BtnCTun := MakeBtn(PanelLeft, 'CTUN', 2, Y, LEFT_W div 3 - 2, BTN_H, BtnCTunClick);
|
BtnCTun := MakeBtn(PanelLeft, 'CTUN', 2, Y, LEFT_W div 3 - 2, BTN_H, BtnCTunClick);
|
||||||
BtnCTun.Tag := 0;
|
BtnCTun.Tag := 0;
|
||||||
StyleButton(BtnCTun, FCTun);
|
StyleButton(BtnCTun, FCTun);
|
||||||
|
|
||||||
|
BtnDUP := MakeBtn(PanelLeft, 'DUP',
|
||||||
|
2 + (LEFT_W div 3) + 0, Y, LEFT_W div 3 - 2, BTN_H, BtnDUPClick);
|
||||||
|
BtnDUP.Tag := 0;
|
||||||
|
StyleButton(BtnDUP, FDisplayDuplex);
|
||||||
|
|
||||||
Inc(Y, BTN_H + 4);
|
Inc(Y, BTN_H + 4);
|
||||||
|
|
||||||
// RX controls
|
// RX controls
|
||||||
@@ -2215,6 +2228,7 @@ begin
|
|||||||
for i := 0 to FILT_COUNT-1 do StyleButton(BtnFilter[i], BtnFilter[i].Active);
|
for i := 0 to FILT_COUNT-1 do StyleButton(BtnFilter[i], BtnFilter[i].Active);
|
||||||
for i := 0 to 4 do StyleButton(BtnAGCMode[i],BtnAGCMode[i].Active);
|
for i := 0 to 4 do StyleButton(BtnAGCMode[i],BtnAGCMode[i].Active);
|
||||||
StyleButton(BtnCTun, BtnCTun.Active);
|
StyleButton(BtnCTun, BtnCTun.Active);
|
||||||
|
StyleButton(BtnDUP, BtnDUP.Active);
|
||||||
StyleButton(BtnNR, BtnNR.Active);
|
StyleButton(BtnNR, BtnNR.Active);
|
||||||
StyleButton(BtnNB, BtnNB.Active);
|
StyleButton(BtnNB, BtnNB.Active);
|
||||||
StyleButton(BtnSNB, BtnSNB.Active);
|
StyleButton(BtnSNB, BtnSNB.Active);
|
||||||
@@ -2910,6 +2924,12 @@ begin
|
|||||||
DUCPkt.DUC0RateLo := Lo(RateKsps);
|
DUCPkt.DUC0RateLo := Lo(RateKsps);
|
||||||
DUCPkt.DUC0Bits := 24;
|
DUCPkt.DUC0Bits := 24;
|
||||||
DUCPkt.MicLineSelect := BuildMicLineSelectByte;
|
DUCPkt.MicLineSelect := BuildMicLineSelectByte;
|
||||||
|
// ATT on TX — RX-ADC step attenuator во время передачи (Thetis-style protection).
|
||||||
|
// Дублируем значение во все три ADC-байта: на однопотоковых платах активен только
|
||||||
|
// ADC0, на Saturn/Hermes Lite 2 — могут использоваться ADC1/ADC2.
|
||||||
|
DUCPkt.StepAtten0 := Byte(EnsureRange(FTXSettings.AttOnTX, 0, 31));
|
||||||
|
DUCPkt.StepAtten1 := DUCPkt.StepAtten0;
|
||||||
|
DUCPkt.StepAtten2 := DUCPkt.StepAtten0;
|
||||||
FNetwork.SendDUCSpecific(DUCPkt);
|
FNetwork.SendDUCSpecific(DUCPkt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -3352,6 +3372,8 @@ begin
|
|||||||
FSpecView.ResetWfAvgBuf;
|
FSpecView.ResetWfAvgBuf;
|
||||||
FShowSpectrum := G_Settings.ShowSpectrum;
|
FShowSpectrum := G_Settings.ShowSpectrum;
|
||||||
FShowWaterfall := G_Settings.ShowWaterfall;
|
FShowWaterfall := G_Settings.ShowWaterfall;
|
||||||
|
FDisplayDuplex := G_Settings.DisplayDuplex;
|
||||||
|
if BtnDUP <> nil then StyleButton(BtnDUP, FDisplayDuplex);
|
||||||
if G_Settings.DisplayFPS > 0 then
|
if G_Settings.DisplayFPS > 0 then
|
||||||
ApplyFPS(G_Settings.DisplayFPS);
|
ApplyFPS(G_Settings.DisplayFPS);
|
||||||
// Настройки дисплея и сетки
|
// Настройки дисплея и сетки
|
||||||
@@ -3866,6 +3888,35 @@ begin
|
|||||||
PbWaterfall.Invalidate;
|
PbWaterfall.Invalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// DUP toggle (Thetis-style local display mode — без аппаратной команды)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
procedure TMainForm.ApplyDUP(Active: Boolean);
|
||||||
|
begin
|
||||||
|
FDisplayDuplex := Active;
|
||||||
|
StyleButton(BtnDUP, FDisplayDuplex);
|
||||||
|
// Если сейчас идёт передача — переключаем источник пикселей и сбрасываем
|
||||||
|
// буферы, чтобы старые TX/RX bin'ы не оставались на следующем кадре.
|
||||||
|
if FTransmitting then
|
||||||
|
begin
|
||||||
|
if FWDSPReady then
|
||||||
|
FDSPEngine.SetDisplaySourceTX(not FDisplayDuplex);
|
||||||
|
FSpecView.TXMode := FTransmitting and not FDisplayDuplex;
|
||||||
|
FSpecView.ResetSpectrumBuf;
|
||||||
|
FSpecView.ResetWfAvgBuf;
|
||||||
|
ApplySpecViewGridFromState;
|
||||||
|
FSpecView.DrawSpectrum;
|
||||||
|
FSpecView.DrawWaterfall;
|
||||||
|
PbSpectrum.Invalidate;
|
||||||
|
PbWaterfall.Invalidate;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.BtnDUPClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
ApplyDUP(not FDisplayDuplex);
|
||||||
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Клик/драг по спектру/водопаду
|
// Клик/драг по спектру/водопаду
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -4261,11 +4312,20 @@ begin
|
|||||||
FSpecView.TXFreq := ActiveTXFreqHz;
|
FSpecView.TXFreq := ActiveTXFreqHz;
|
||||||
if FSplitTxB then FSpecView.TXVfoIndex := 1
|
if FSplitTxB then FSpecView.TXVfoIndex := 1
|
||||||
else FSpecView.TXVfoIndex := FActiveVfo;
|
else FSpecView.TXVfoIndex := FActiveVfo;
|
||||||
FSpecView.TXMode := FTransmitting;
|
// DUP: данные остаются от RX-анализатора (TXMode=False), но TX-полоса
|
||||||
|
// фильтра рисуется поверх через TXOverlay. Без DUP при TX оба флага True.
|
||||||
|
FSpecView.TXMode := FTransmitting and not FDisplayDuplex;
|
||||||
|
FSpecView.TXOverlay := FTransmitting;
|
||||||
// Перерисовываем сетку спектра под активный тракт (Thetis-style).
|
// Перерисовываем сетку спектра под активный тракт (Thetis-style).
|
||||||
ApplySpecViewGridFromState;
|
ApplySpecViewGridFromState;
|
||||||
if FWDSPReady then
|
if FWDSPReady then
|
||||||
|
begin
|
||||||
FDSPEngine.SetTXRun(FTransmitting);
|
FDSPEngine.SetTXRun(FTransmitting);
|
||||||
|
// SetTXRun переключает FActiveDisplayID на TX_DISP_ID; при DUP возвращаем
|
||||||
|
// источник на RX, чтобы видеть приём во время передачи.
|
||||||
|
if FTransmitting and FDisplayDuplex then
|
||||||
|
FDSPEngine.SetDisplaySourceTX(False);
|
||||||
|
end;
|
||||||
if FNetwork.Connected and FNetwork.Running then
|
if FNetwork.Connected and FNetwork.Running then
|
||||||
begin
|
begin
|
||||||
FNetwork.UpdateState(FCenterFreq, ActiveTXFreqHz, FDriveLevel,
|
FNetwork.UpdateState(FCenterFreq, ActiveTXFreqHz, FDriveLevel,
|
||||||
@@ -5029,9 +5089,11 @@ procedure TMainForm.OnTXSettingsChange(const T: TTXSettings);
|
|||||||
// перешиваем WDSP TX-цепь и (если поменялись mic-биты) перепосылаем DUC Specific.
|
// перешиваем WDSP TX-цепь и (если поменялись mic-биты) перепосылаем DUC Specific.
|
||||||
var
|
var
|
||||||
OldMicByte, NewMicByte: Byte;
|
OldMicByte, NewMicByte: Byte;
|
||||||
|
OldAtt: Integer;
|
||||||
GridChanged: Boolean;
|
GridChanged: Boolean;
|
||||||
begin
|
begin
|
||||||
OldMicByte := BuildMicLineSelectByte;
|
OldMicByte := BuildMicLineSelectByte;
|
||||||
|
OldAtt := FTXSettings.AttOnTX;
|
||||||
GridChanged := (FTXSpecRefLevel <> T.TXSpecRefLevel)
|
GridChanged := (FTXSpecRefLevel <> T.TXSpecRefLevel)
|
||||||
or (FTXSpecRange <> T.TXSpecRange)
|
or (FTXSpecRange <> T.TXSpecRange)
|
||||||
or (FTXSpecGridStep <> T.TXSpecGridStep);
|
or (FTXSpecGridStep <> T.TXSpecGridStep);
|
||||||
@@ -5045,7 +5107,7 @@ begin
|
|||||||
if GridChanged and FTransmitting then
|
if GridChanged and FTransmitting then
|
||||||
ApplySpecViewGridFromState;
|
ApplySpecViewGridFromState;
|
||||||
ApplyTXSettingsToDSP;
|
ApplyTXSettingsToDSP;
|
||||||
if OldMicByte <> NewMicByte then
|
if (OldMicByte <> NewMicByte) or (OldAtt <> FTXSettings.AttOnTX) then
|
||||||
SendDUCSpecificFromSettings;
|
SendDUCSpecificFromSettings;
|
||||||
// Сохраняем сразу — настройки per-device, попадают в hpsdr_settings.json
|
// Сохраняем сразу — настройки per-device, попадают в hpsdr_settings.json
|
||||||
if FDevConnected then
|
if FDevConnected then
|
||||||
@@ -5226,7 +5288,8 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.ApplySpecViewGridFromState;
|
procedure TMainForm.ApplySpecViewGridFromState;
|
||||||
begin
|
begin
|
||||||
if FTransmitting then
|
// При DUP во время TX данные приходят от RX-анализатора → используем RX-сетку.
|
||||||
|
if FTransmitting and not FDisplayDuplex then
|
||||||
begin
|
begin
|
||||||
FSpecView.SpecRefLevel := FTXSpecRefLevel;
|
FSpecView.SpecRefLevel := FTXSpecRefLevel;
|
||||||
FSpecView.SpecRange := FTXSpecRange;
|
FSpecView.SpecRange := FTXSpecRange;
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ type
|
|||||||
MicPTTEnabled: Boolean; // bit2: Mic PTT enable
|
MicPTTEnabled: Boolean; // bit2: Mic PTT enable
|
||||||
// Mic gain (dB, применяется в WDSP PanelGain1)
|
// Mic gain (dB, применяется в WDSP PanelGain1)
|
||||||
MicGainDB: Double; // -10..+30
|
MicGainDB: Double; // -10..+30
|
||||||
|
// ATT on TX — ослабление RX-ADC во время передачи (защита от утечки TX).
|
||||||
|
// DUC Specific bytes 57/58/59 (StepAtten 0/1/2). Thetis default: 31 dB.
|
||||||
|
AttOnTX: Integer; // 0..31 dB
|
||||||
// TX bandpass filter
|
// TX bandpass filter
|
||||||
FilterLow: Integer; // Hz, для SSB обычно 100
|
FilterLow: Integer; // Hz, для SSB обычно 100
|
||||||
FilterHigh: Integer; // Hz, для SSB обычно 2800
|
FilterHigh: Integer; // Hz, для SSB обычно 2800
|
||||||
@@ -135,6 +138,7 @@ type
|
|||||||
// --- Visibility settings ---
|
// --- Visibility settings ---
|
||||||
ShowSpectrum: Boolean; // True = draw spectrum on main form
|
ShowSpectrum: Boolean; // True = draw spectrum on main form
|
||||||
ShowWaterfall: Boolean; // True = draw waterfall on main form
|
ShowWaterfall: Boolean; // True = draw waterfall on main form
|
||||||
|
DisplayDuplex: Boolean; // DUP: при TX показывать RX-водопад, TX-фильтр overlay'ем
|
||||||
// --- Display FPS ---
|
// --- Display FPS ---
|
||||||
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
|
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
|
||||||
// --- Theme ---
|
// --- Theme ---
|
||||||
@@ -280,6 +284,7 @@ begin
|
|||||||
G.AudioInDevice := '';
|
G.AudioInDevice := '';
|
||||||
G.ShowSpectrum := True;
|
G.ShowSpectrum := True;
|
||||||
G.ShowWaterfall := True;
|
G.ShowWaterfall := True;
|
||||||
|
G.DisplayDuplex := False;
|
||||||
G.DisplayFPS := 60;
|
G.DisplayFPS := 60;
|
||||||
// CAT defaults
|
// CAT defaults
|
||||||
for i := 0 to 3 do begin
|
for i := 0 to 3 do begin
|
||||||
@@ -488,6 +493,7 @@ begin
|
|||||||
// Visibility settings
|
// Visibility settings
|
||||||
G.ShowSpectrum := JB(GObj,'show_spectrum',True);
|
G.ShowSpectrum := JB(GObj,'show_spectrum',True);
|
||||||
G.ShowWaterfall := JB(GObj,'show_waterfall',True);
|
G.ShowWaterfall := JB(GObj,'show_waterfall',True);
|
||||||
|
G.DisplayDuplex := JB(GObj,'display_duplex',False);
|
||||||
G.DisplayFPS := JI(GObj,'display_fps',60);
|
G.DisplayFPS := JI(GObj,'display_fps',60);
|
||||||
G.LightTheme := JB(GObj,'light_theme',False);
|
G.LightTheme := JB(GObj,'light_theme',False);
|
||||||
// PA settings
|
// PA settings
|
||||||
@@ -565,6 +571,7 @@ begin
|
|||||||
// Visibility settings
|
// Visibility settings
|
||||||
JW(O,'show_spectrum',G.ShowSpectrum);
|
JW(O,'show_spectrum',G.ShowSpectrum);
|
||||||
JW(O,'show_waterfall',G.ShowWaterfall);
|
JW(O,'show_waterfall',G.ShowWaterfall);
|
||||||
|
JW(O,'display_duplex',G.DisplayDuplex);
|
||||||
JW(O,'display_fps',G.DisplayFPS);
|
JW(O,'display_fps',G.DisplayFPS);
|
||||||
JW(O,'light_theme',G.LightTheme);
|
JW(O,'light_theme',G.LightTheme);
|
||||||
// PA settings
|
// PA settings
|
||||||
@@ -721,6 +728,8 @@ begin
|
|||||||
T.MicLineIn := False;
|
T.MicLineIn := False;
|
||||||
T.MicPTTEnabled := False;
|
T.MicPTTEnabled := False;
|
||||||
T.MicGainDB := 10.0;
|
T.MicGainDB := 10.0;
|
||||||
|
// ATT on TX — Thetis default 31 dB (полная защита RX-ADC во время передачи)
|
||||||
|
T.AttOnTX := 31;
|
||||||
// SSB-фильтр по умолчанию
|
// SSB-фильтр по умолчанию
|
||||||
T.FilterLow := 100;
|
T.FilterLow := 100;
|
||||||
T.FilterHigh := 2800;
|
T.FilterHigh := 2800;
|
||||||
@@ -794,6 +803,7 @@ begin
|
|||||||
T.MicLineIn := JB(O,'mic_line_in', T.MicLineIn);
|
T.MicLineIn := JB(O,'mic_line_in', T.MicLineIn);
|
||||||
T.MicPTTEnabled := JB(O,'mic_ptt_enabled', T.MicPTTEnabled);
|
T.MicPTTEnabled := JB(O,'mic_ptt_enabled', T.MicPTTEnabled);
|
||||||
T.MicGainDB := JD(O,'mic_gain_db', T.MicGainDB);
|
T.MicGainDB := JD(O,'mic_gain_db', T.MicGainDB);
|
||||||
|
T.AttOnTX := EnsureRange(JI(O,'att_on_tx', T.AttOnTX), 0, 31);
|
||||||
T.FilterLow := JI(O,'filter_low', T.FilterLow);
|
T.FilterLow := JI(O,'filter_low', T.FilterLow);
|
||||||
T.FilterHigh := JI(O,'filter_high', T.FilterHigh);
|
T.FilterHigh := JI(O,'filter_high', T.FilterHigh);
|
||||||
T.FilterNC := JI(O,'filter_nc', T.FilterNC);
|
T.FilterNC := JI(O,'filter_nc', T.FilterNC);
|
||||||
@@ -848,6 +858,7 @@ begin
|
|||||||
JW(O,'mic_line_in', T.MicLineIn);
|
JW(O,'mic_line_in', T.MicLineIn);
|
||||||
JW(O,'mic_ptt_enabled', T.MicPTTEnabled);
|
JW(O,'mic_ptt_enabled', T.MicPTTEnabled);
|
||||||
JW(O,'mic_gain_db', T.MicGainDB);
|
JW(O,'mic_gain_db', T.MicGainDB);
|
||||||
|
JW(O,'att_on_tx', T.AttOnTX);
|
||||||
JW(O,'filter_low', T.FilterLow);
|
JW(O,'filter_low', T.FilterLow);
|
||||||
JW(O,'filter_high', T.FilterHigh);
|
JW(O,'filter_high', T.FilterHigh);
|
||||||
JW(O,'filter_nc', T.FilterNC);
|
JW(O,'filter_nc', T.FilterNC);
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ type
|
|||||||
FChkTXMicLineIn: TCheckBox;
|
FChkTXMicLineIn: TCheckBox;
|
||||||
FChkTXMicPTT: TCheckBox;
|
FChkTXMicPTT: TCheckBox;
|
||||||
FEdTXMicGain: TFloatSpinEdit;
|
FEdTXMicGain: TFloatSpinEdit;
|
||||||
|
FEdTXAttOnTX: TSpinEdit;
|
||||||
// Filter
|
// Filter
|
||||||
FEdTXFiltLow: TSpinEdit;
|
FEdTXFiltLow: TSpinEdit;
|
||||||
FEdTXFiltHigh: TSpinEdit;
|
FEdTXFiltHigh: TSpinEdit;
|
||||||
@@ -952,6 +953,11 @@ begin
|
|||||||
MakeLbl(Grp, 'Mic gain (dB)', PAD, R1 + 3 * STEP + 5, LW);
|
MakeLbl(Grp, 'Mic gain (dB)', PAD, R1 + 3 * STEP + 5, LW);
|
||||||
FEdTXMicGain := MkFloat(Grp, CX, R1 + 3 * STEP, 10.0, -20.0, 60.0, 0.5);
|
FEdTXMicGain := MkFloat(Grp, CX, R1 + 3 * STEP, 10.0, -20.0, 60.0, 0.5);
|
||||||
|
|
||||||
|
// ATT on TX — RX-ADC ослабление во время передачи (DUC Specific bytes 57/58/59).
|
||||||
|
// Thetis-style default 31 dB. Помещаем на той же строке, что и Mic gain.
|
||||||
|
MakeLbl(Grp, 'ATT on TX (dB)', PAD + 320, R1 + 3 * STEP + 5, 110);
|
||||||
|
FEdTXAttOnTX := MkSpin(Grp, PAD + 320 + 120, R1 + 3 * STEP, 0, 31, 31);
|
||||||
|
|
||||||
// ---- TX bandpass filter ----
|
// ---- TX bandpass filter ----
|
||||||
Inc(Y, 220 + 12);
|
Inc(Y, 220 + 12);
|
||||||
Grp := MakeGroupPanel(FPageTransmit, 'TX Filter (bandpass)', MARGIN, Y, 700, 200);
|
Grp := MakeGroupPanel(FPageTransmit, 'TX Filter (bandpass)', MARGIN, Y, 700, 200);
|
||||||
@@ -1131,6 +1137,7 @@ begin
|
|||||||
if FChkTXMicLineIn <> nil then FTX.MicLineIn := FChkTXMicLineIn.Checked;
|
if FChkTXMicLineIn <> nil then FTX.MicLineIn := FChkTXMicLineIn.Checked;
|
||||||
if FChkTXMicPTT <> nil then FTX.MicPTTEnabled := FChkTXMicPTT.Checked;
|
if FChkTXMicPTT <> nil then FTX.MicPTTEnabled := FChkTXMicPTT.Checked;
|
||||||
if FEdTXMicGain <> nil then FTX.MicGainDB := FEdTXMicGain.Value;
|
if FEdTXMicGain <> nil then FTX.MicGainDB := FEdTXMicGain.Value;
|
||||||
|
if FEdTXAttOnTX <> nil then FTX.AttOnTX := FEdTXAttOnTX.Value;
|
||||||
if FEdTXFiltLow <> nil then FTX.FilterLow := FEdTXFiltLow.Value;
|
if FEdTXFiltLow <> nil then FTX.FilterLow := FEdTXFiltLow.Value;
|
||||||
if FEdTXFiltHigh <> nil then FTX.FilterHigh := FEdTXFiltHigh.Value;
|
if FEdTXFiltHigh <> nil then FTX.FilterHigh := FEdTXFiltHigh.Value;
|
||||||
if FCmbTXFiltNC <> nil then
|
if FCmbTXFiltNC <> nil then
|
||||||
@@ -1215,6 +1222,7 @@ begin
|
|||||||
if FChkTXMicLineIn <> nil then FChkTXMicLineIn.Checked := T.MicLineIn;
|
if FChkTXMicLineIn <> nil then FChkTXMicLineIn.Checked := T.MicLineIn;
|
||||||
if FChkTXMicPTT <> nil then FChkTXMicPTT.Checked := T.MicPTTEnabled;
|
if FChkTXMicPTT <> nil then FChkTXMicPTT.Checked := T.MicPTTEnabled;
|
||||||
if FEdTXMicGain <> nil then FEdTXMicGain.Value := T.MicGainDB;
|
if FEdTXMicGain <> nil then FEdTXMicGain.Value := T.MicGainDB;
|
||||||
|
if FEdTXAttOnTX <> nil then FEdTXAttOnTX.Value := EnsureRange(T.AttOnTX, 0, 31);
|
||||||
if FEdTXFiltLow <> nil then FEdTXFiltLow.Value := T.FilterLow;
|
if FEdTXFiltLow <> nil then FEdTXFiltLow.Value := T.FilterLow;
|
||||||
if FEdTXFiltHigh <> nil then FEdTXFiltHigh.Value := T.FilterHigh;
|
if FEdTXFiltHigh <> nil then FEdTXFiltHigh.Value := T.FilterHigh;
|
||||||
if FCmbTXFiltNC <> nil then FCmbTXFiltNC.Text := IntToStr(T.FilterNC);
|
if FCmbTXFiltNC <> nil then FCmbTXFiltNC.Text := IntToStr(T.FilterNC);
|
||||||
|
|||||||
+8
-2
@@ -89,6 +89,10 @@ type
|
|||||||
FTXFreq: Double; // центр TX-сигнала (Hz)
|
FTXFreq: Double; // центр TX-сигнала (Hz)
|
||||||
FTXSpanHz: Double; // ширина TX-окна (192000 для Hermes/Saturn)
|
FTXSpanHz: Double; // ширина TX-окна (192000 для Hermes/Saturn)
|
||||||
FTXVfoIndex: Integer; // 0=A, 1=B — какой VFO привязан к TX (для split)
|
FTXVfoIndex: Integer; // 0=A, 1=B — какой VFO привязан к TX (для split)
|
||||||
|
// FTXOverlay — независимый от FTXMode флаг: «нарисовать TX-фильтр поверх
|
||||||
|
// данных». DUP-режим: FTXMode=False (данные RX), но FTXOverlay=True (TX-полоса
|
||||||
|
// красным сверху). Без DUP при TX оба флага True. Без TX оба False.
|
||||||
|
FTXOverlay: Boolean;
|
||||||
|
|
||||||
// ── Marker ────────────────────────────────────────────────────────────────
|
// ── Marker ────────────────────────────────────────────────────────────────
|
||||||
FMarkerActive: Boolean;
|
FMarkerActive: Boolean;
|
||||||
@@ -167,6 +171,7 @@ type
|
|||||||
property TXFreq: Double read FTXFreq write FTXFreq;
|
property TXFreq: Double read FTXFreq write FTXFreq;
|
||||||
property TXSpanHz: Double read FTXSpanHz write FTXSpanHz;
|
property TXSpanHz: Double read FTXSpanHz write FTXSpanHz;
|
||||||
property TXVfoIndex: Integer read FTXVfoIndex write FTXVfoIndex;
|
property TXVfoIndex: Integer read FTXVfoIndex write FTXVfoIndex;
|
||||||
|
property TXOverlay: Boolean read FTXOverlay write FTXOverlay;
|
||||||
|
|
||||||
// ── Маркер ────────────────────────────────────────────────────────────────
|
// ── Маркер ────────────────────────────────────────────────────────────────
|
||||||
property MarkerActive: Boolean read FMarkerActive write FMarkerActive;
|
property MarkerActive: Boolean read FMarkerActive write FMarkerActive;
|
||||||
@@ -414,6 +419,7 @@ begin
|
|||||||
FTXFreq := 0.0;
|
FTXFreq := 0.0;
|
||||||
FTXSpanHz := 192000.0;
|
FTXSpanHz := 192000.0;
|
||||||
FTXVfoIndex := 0;
|
FTXVfoIndex := 0;
|
||||||
|
FTXOverlay := False;
|
||||||
FSpectrumBufCount := 1024;
|
FSpectrumBufCount := 1024;
|
||||||
FWaterfallBufCount := 1024;
|
FWaterfallBufCount := 1024;
|
||||||
FWfFrameInterval := 2;
|
FWfFrameInterval := 2;
|
||||||
@@ -774,7 +780,7 @@ begin
|
|||||||
// (для split-режима TX-полоса рисуется без edges чтобы визуально отличалась).
|
// (для split-режима TX-полоса рисуется без edges чтобы визуально отличалась).
|
||||||
if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, X1, X2, VfoX)
|
if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, X1, X2, VfoX)
|
||||||
else CalcFilterBandX(FVfoB, W, X1, X2, VfoX);
|
else CalcFilterBandX(FVfoB, W, X1, X2, VfoX);
|
||||||
if FTXMode then
|
if FTXOverlay then
|
||||||
begin
|
begin
|
||||||
if FTXVfoIndex <> FActiveVfo then
|
if FTXVfoIndex <> FActiveVfo then
|
||||||
begin
|
begin
|
||||||
@@ -881,7 +887,7 @@ begin
|
|||||||
C.Brush.Color := FTheme.SpecVfoCursor; C.Brush.Style := bsSolid; C.Pen.Width := 1;
|
C.Brush.Color := FTheme.SpecVfoCursor; C.Brush.Style := bsSolid; C.Pen.Width := 1;
|
||||||
C.Polygon([Point(VfoX-5,0), Point(VfoX+5,0), Point(VfoX,8)]);
|
C.Polygon([Point(VfoX-5,0), Point(VfoX+5,0), Point(VfoX,8)]);
|
||||||
// Split TX: рисуем края + cursor для TX-VFO красным, чтобы было видно где идёт передача
|
// Split TX: рисуем края + cursor для TX-VFO красным, чтобы было видно где идёт передача
|
||||||
if FTXMode and (FTXVfoIndex <> FActiveVfo) then
|
if FTXOverlay and (FTXVfoIndex <> FActiveVfo) then
|
||||||
begin
|
begin
|
||||||
C.Pen.Color := TColor($002030E0); C.Pen.Width := 1; C.Pen.Style := psSolid;
|
C.Pen.Color := TColor($002030E0); C.Pen.Width := 1; C.Pen.Style := psSolid;
|
||||||
C.MoveTo(TXX1, 0); C.LineTo(TXX1, H);
|
C.MoveTo(TXX1, 0); C.LineTo(TXX1, H);
|
||||||
|
|||||||
@@ -350,6 +350,10 @@ type
|
|||||||
procedure SetDriveLevel(Level: Double);
|
procedure SetDriveLevel(Level: Double);
|
||||||
procedure SetMicGain(GainDB: Double);
|
procedure SetMicGain(GainDB: Double);
|
||||||
procedure SetTXRun(Run: Boolean);
|
procedure SetTXRun(Run: Boolean);
|
||||||
|
// DUP: переключить ИСТОЧНИК пикселей display'а независимо от TX-аудио
|
||||||
|
// (SetTXRun меняет и аудио, и источник; этот метод позволяет вернуть
|
||||||
|
// RX-источник пока TX-аудио продолжает работать).
|
||||||
|
procedure SetDisplaySourceTX(UseTX: Boolean);
|
||||||
|
|
||||||
// Расширенное TX управление (применяются мгновенно если канал открыт)
|
// Расширенное TX управление (применяются мгновенно если канал открыт)
|
||||||
procedure SetTXFilterFull(Low, High, NC, MP, Window: Integer);
|
procedure SetTXFilterFull(Low, High, NC, MP, Window: Integer);
|
||||||
@@ -1502,6 +1506,20 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TWDSPEngine.SetDisplaySourceTX(UseTX: Boolean);
|
||||||
|
begin
|
||||||
|
// Тот же сброс позиций, что и в SetTXRun — analyzer-буферы у обоих DispID
|
||||||
|
// независимы, но мы переключаем источник, и старые байты в накапливающем
|
||||||
|
// буфере на новой стороне могут повести себя как «всплеск», если их не
|
||||||
|
// обнулить.
|
||||||
|
FDispPos := 0;
|
||||||
|
FTXDispPos := 0;
|
||||||
|
if UseTX then
|
||||||
|
FActiveDisplayID := TX_DISP_ID
|
||||||
|
else
|
||||||
|
FActiveDisplayID := RX_DISP_ID;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TWDSPEngine.ProcessTXBlock;
|
procedure TWDSPEngine.ProcessTXBlock;
|
||||||
// Вызывается из TTXDSPThread — обрабатывает один блок FAudioBufSize mic сэмплов
|
// Вызывается из TTXDSPThread — обрабатывает один блок FAudioBufSize mic сэмплов
|
||||||
// через WDSP TXA, выдаёт FTXOutBufSize IQ пар @ FTXSampleRate в FOnTXIQ callback.
|
// через WDSP TXA, выдаёт FTXOutBufSize IQ пар @ FTXSampleRate в FOnTXIQ callback.
|
||||||
|
|||||||
Reference in New Issue
Block a user