mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat: Pluto RX hw-gain control + AD9361 cold-init (FIR via libad9361)
- RX hw-gain (Pluto/AD936x): backend SetRxGain(ModeIdx, GainDb) — gain_control_mode (manual/fast_attack/slow_attack/hybrid) + hardwaregain. Controller FRxGainMode/ FRxGainDb, commands SetRxGainMode/SetRxGain/RxGainBy, rfRxGain event, snapshot, applied in StartRunning. Persisted per-device (rx_gain_mode/rx_gain_db). HPSDR unaffected (backend no-op). - UI: separate "RF AGC" panel below the (unchanged) WDSP AGC block, shown only for Pluto — FAST/SLOW/HYB/MAN mode buttons + manual GAIN slider. LayoutLeftPanel reflows lower panels via RelayoutBelowBands; no layout shift on HPSDR. - Cold-init fix: direct sampling_frequency write doesn't load the AD9361 FIR decimation filter, so a cold-booted Pluto showed a wide centre spike that didn't track tuning (worked only after SDR Console pre-initialised it). Add optional libad9361 binding (dynamic) and call ad9361_set_bb_rate() in ApplySampleRate, which configures BBPLL + loads the FIR; falls back to direct write if absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,13 @@ var
|
|||||||
// ---- Утилиты ----
|
// ---- Утилиты ----
|
||||||
iio_strerror: procedure(err: cint; dst: PAnsiChar; len: csize_t); cdecl;
|
iio_strerror: procedure(err: cint; dst: PAnsiChar; len: csize_t); cdecl;
|
||||||
|
|
||||||
|
// ---- libad9361 (опционально) ----
|
||||||
|
// ad9361_set_bb_rate: настраивает BBPLL + ЗАГРУЖАЕТ FIR-таблицу под частоту.
|
||||||
|
// Без него прямая запись sampling_frequency на холодную даёт мусор (битый FIR).
|
||||||
|
// nil если libad9361 не загружена.
|
||||||
|
ad9361_set_bb_rate: function(dev: Piio_device; rate: culong): cint; cdecl;
|
||||||
|
ad9361_set_trx_fir_enable: function(dev: Piio_device; enable: cint): cint; cdecl;
|
||||||
|
|
||||||
// Загружает libiio (один раз). True — готово к использованию. Идемпотентно.
|
// Загружает libiio (один раз). True — готово к использованию. Идемпотентно.
|
||||||
function IIOLoad: Boolean;
|
function IIOLoad: Boolean;
|
||||||
// True, если libiio загружена и символы разрешены.
|
// True, если libiio загружена и символы разрешены.
|
||||||
@@ -95,16 +102,20 @@ implementation
|
|||||||
|
|
||||||
var
|
var
|
||||||
GLib: TLibHandle = NilHandle;
|
GLib: TLibHandle = NilHandle;
|
||||||
|
GLibAD: TLibHandle = NilHandle; // libad9361 (опционально)
|
||||||
GLoaded: Boolean = False;
|
GLoaded: Boolean = False;
|
||||||
|
|
||||||
const
|
const
|
||||||
{$IFDEF WINDOWS}
|
{$IFDEF WINDOWS}
|
||||||
IIO_LIBS: array[0..1] of string = ('libiio.dll', 'iio.dll');
|
IIO_LIBS: array[0..1] of string = ('libiio.dll', 'iio.dll');
|
||||||
|
AD_LIBS: array[0..1] of string = ('libad9361.dll', 'ad9361.dll');
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
{$IFDEF DARWIN}
|
{$IFDEF DARWIN}
|
||||||
IIO_LIBS: array[0..1] of string = ('libiio.dylib', 'libiio.1.dylib');
|
IIO_LIBS: array[0..1] of string = ('libiio.dylib', 'libiio.1.dylib');
|
||||||
|
AD_LIBS: array[0..1] of string = ('libad9361.dylib', 'libad9361.0.dylib');
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
IIO_LIBS: array[0..1] of string = ('libiio.so', 'libiio.so.0');
|
IIO_LIBS: array[0..1] of string = ('libiio.so', 'libiio.so.0');
|
||||||
|
AD_LIBS: array[0..1] of string = ('libad9361.so', 'libad9361.so.0');
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
@@ -163,6 +174,21 @@ begin
|
|||||||
iio_buffer_step := Sym('iio_buffer_step');
|
iio_buffer_step := Sym('iio_buffer_step');
|
||||||
iio_buffer_end := Sym('iio_buffer_end');
|
iio_buffer_end := Sym('iio_buffer_end');
|
||||||
iio_strerror := Sym('iio_strerror');
|
iio_strerror := Sym('iio_strerror');
|
||||||
|
|
||||||
|
// libad9361 — опционально (ad9361_set_bb_rate грузит FIR). Отсутствие не
|
||||||
|
// фатально: ApplySampleRate откатится на прямую запись sampling_frequency.
|
||||||
|
if GLibAD = NilHandle then
|
||||||
|
for I := 0 to High(AD_LIBS) do
|
||||||
|
begin
|
||||||
|
GLibAD := LoadLibrary(AD_LIBS[I]);
|
||||||
|
if GLibAD <> NilHandle then Break;
|
||||||
|
end;
|
||||||
|
if GLibAD <> NilHandle then
|
||||||
|
begin
|
||||||
|
ad9361_set_bb_rate := GetProcedureAddress(GLibAD, 'ad9361_set_bb_rate');
|
||||||
|
ad9361_set_trx_fir_enable := GetProcedureAddress(GLibAD, 'ad9361_set_trx_fir_enable');
|
||||||
|
end;
|
||||||
|
|
||||||
GLoaded := True;
|
GLoaded := True;
|
||||||
except
|
except
|
||||||
on E: Exception do
|
on E: Exception do
|
||||||
@@ -190,6 +216,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
|
if GLibAD <> NilHandle then
|
||||||
|
begin
|
||||||
|
UnloadLibrary(GLibAD);
|
||||||
|
GLibAD := NilHandle;
|
||||||
|
end;
|
||||||
if GLib <> NilHandle then
|
if GLib <> NilHandle then
|
||||||
begin
|
begin
|
||||||
UnloadLibrary(GLib);
|
UnloadLibrary(GLib);
|
||||||
|
|||||||
+107
@@ -259,6 +259,11 @@ type
|
|||||||
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
|
BtnAGCMode: array[0..4] of TFlatButton; // FAST MED SLOW LONG OFF
|
||||||
LblAGCTop: TLabel; // показывает значение уровня
|
LblAGCTop: TLabel; // показывает значение уровня
|
||||||
TrkAGC: TFlatSlider;
|
TrkAGC: TFlatSlider;
|
||||||
|
// RX hw-gain (Pluto) — ОТДЕЛЬНЫЙ блок под AGC (WDSP). Виден только для Pluto.
|
||||||
|
PanelPlutoAGC: TPanel;
|
||||||
|
BtnPlutoGain: array[0..3] of TFlatButton; // FAST/SLOW/HYB/MAN (Tag=режим)
|
||||||
|
TrkRxGain: TFlatSlider; // manual gain 0..73 dB
|
||||||
|
LblRxGain: TLabel; // значение усиления
|
||||||
TrkVolume: TFlatSlider;
|
TrkVolume: TFlatSlider;
|
||||||
BtnNR: TFlatButton;
|
BtnNR: TFlatButton;
|
||||||
BtnNB: TFlatButton;
|
BtnNB: TFlatButton;
|
||||||
@@ -328,6 +333,9 @@ type
|
|||||||
function ResolveDevice(const IP: string): THPSDRDevice;
|
function ResolveDevice(const IP: string): THPSDRDevice;
|
||||||
procedure RelabelBands; // подписи band-кнопок под план бэкенда
|
procedure RelabelBands; // подписи band-кнопок под план бэкенда
|
||||||
procedure ApplyBackendCapsToUI; // sample-rate пресеты + гейтинг полей по Caps
|
procedure ApplyBackendCapsToUI; // sample-rate пресеты + гейтинг полей по Caps
|
||||||
|
procedure UpdateRxGainUI; // рендер RX hw-gain (Pluto)
|
||||||
|
procedure LayoutLeftPanel(Pluto: Boolean); // показать/скрыть Pluto-блок + reflow
|
||||||
|
procedure BtnPlutoGainClick(Sender: TObject);
|
||||||
procedure DoConnectDevice(const Dev: THPSDRDevice);
|
procedure DoConnectDevice(const Dev: THPSDRDevice);
|
||||||
// Старт с уже выбранным устройством (preload rate + WDSP open + connect).
|
// Старт с уже выбранным устройством (preload rate + WDSP open + connect).
|
||||||
// Переиспользуется десктоп-диалогом и web-оверлеем — без модального диалога.
|
// Переиспользуется десктоп-диалогом и web-оверлеем — без модального диалога.
|
||||||
@@ -487,6 +495,8 @@ type
|
|||||||
procedure OnTXSettingsChange(const T: TTXSettings);
|
procedure OnTXSettingsChange(const T: TTXSettings);
|
||||||
procedure BtnAGCModeClick(Sender: TObject);
|
procedure BtnAGCModeClick(Sender: TObject);
|
||||||
procedure TrkAGCChange(Sender: TObject);
|
procedure TrkAGCChange(Sender: TObject);
|
||||||
|
procedure TrkRxGainChange(Sender: TObject);
|
||||||
|
// (BtnPlutoGainClick объявлен выше)
|
||||||
procedure MeterTimerTick(Sender: TObject);
|
procedure MeterTimerTick(Sender: TObject);
|
||||||
procedure SpectrumTimerTick(Sender: TObject);
|
procedure SpectrumTimerTick(Sender: TObject);
|
||||||
procedure RightPanelResize(Sender: TObject);
|
procedure RightPanelResize(Sender: TObject);
|
||||||
@@ -1225,6 +1235,8 @@ begin
|
|||||||
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FController.FAGCMode);
|
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FController.FAGCMode);
|
||||||
rfVolume:
|
rfVolume:
|
||||||
TrkVolume.Position := FController.FVolume;
|
TrkVolume.Position := FController.FVolume;
|
||||||
|
rfRxGain:
|
||||||
|
UpdateRxGainUI;
|
||||||
rfDrive:
|
rfDrive:
|
||||||
begin
|
begin
|
||||||
TrkDrive.Position := FController.FDrivePercent; // слайдер ← drive%
|
TrkDrive.Position := FController.FDrivePercent; // слайдер ← drive%
|
||||||
@@ -1397,6 +1409,7 @@ const
|
|||||||
BTN_H = 26;
|
BTN_H = 26;
|
||||||
BTN_SM = 24;
|
BTN_SM = 24;
|
||||||
LEFT_W = 232;
|
LEFT_W = 232;
|
||||||
|
PLUTO_AGC_H = 64; // высота отдельного Pluto RF-AGC блока (под AGC)
|
||||||
TOP_VFO_W = 244;
|
TOP_VFO_W = 244;
|
||||||
TOP_VFO_FREQ_H = 47;
|
TOP_VFO_FREQ_H = 47;
|
||||||
var
|
var
|
||||||
@@ -1759,6 +1772,47 @@ begin
|
|||||||
|
|
||||||
Inc(Y, 74);
|
Inc(Y, 74);
|
||||||
|
|
||||||
|
// --- Pluto RF AGC/gain — отдельный блок ПОД блоком AGC (WDSP). Виден только
|
||||||
|
// когда подключён Pluto-бэкенд; раскладку панелей ниже сдвигает LayoutLeftPanel.
|
||||||
|
PanelPlutoAGC := TPanel.Create(Self);
|
||||||
|
PanelPlutoAGC.Parent := PanelLeft;
|
||||||
|
PanelPlutoAGC.SetBounds(0, Y, LEFT_W, PLUTO_AGC_H);
|
||||||
|
PanelPlutoAGC.BevelOuter := bvNone;
|
||||||
|
PanelPlutoAGC.Visible := False;
|
||||||
|
MakeLbl(PanelPlutoAGC, 'RF AGC', 4, 2);
|
||||||
|
// Режимы AD936x: FAST/SLOW/HYB/MAN. Tag = индекс режима (1/2/3/0).
|
||||||
|
BtnPlutoGain[0] := MakeBtn(PanelPlutoAGC, 'FAST',
|
||||||
|
LeftPanelButtonLeft(LEFT_W, 4, 0), 16, LeftPanelButtonWidth(LEFT_W, 4, 0),
|
||||||
|
BTN_SM, BtnPlutoGainClick);
|
||||||
|
BtnPlutoGain[0].Tag := 1;
|
||||||
|
BtnPlutoGain[1] := MakeBtn(PanelPlutoAGC, 'SLOW',
|
||||||
|
LeftPanelButtonLeft(LEFT_W, 4, 1), 16, LeftPanelButtonWidth(LEFT_W, 4, 1),
|
||||||
|
BTN_SM, BtnPlutoGainClick);
|
||||||
|
BtnPlutoGain[1].Tag := 2;
|
||||||
|
BtnPlutoGain[2] := MakeBtn(PanelPlutoAGC, 'HYB',
|
||||||
|
LeftPanelButtonLeft(LEFT_W, 4, 2), 16, LeftPanelButtonWidth(LEFT_W, 4, 2),
|
||||||
|
BTN_SM, BtnPlutoGainClick);
|
||||||
|
BtnPlutoGain[2].Tag := 3;
|
||||||
|
BtnPlutoGain[3] := MakeBtn(PanelPlutoAGC, 'MAN',
|
||||||
|
LeftPanelButtonLeft(LEFT_W, 4, 3), 16, LeftPanelButtonWidth(LEFT_W, 4, 3),
|
||||||
|
BTN_SM, BtnPlutoGainClick);
|
||||||
|
BtnPlutoGain[3].Tag := 0;
|
||||||
|
// Manual gain slider.
|
||||||
|
MakeLbl(PanelPlutoAGC, 'GAIN', 4, 44);
|
||||||
|
TrkRxGain := TFlatSlider.Create(Self);
|
||||||
|
TrkRxGain.Parent := PanelPlutoAGC; TrkRxGain.Left := 56; TrkRxGain.Top := 40;
|
||||||
|
TrkRxGain.Width := LEFT_W - 96; TrkRxGain.Height := 16;
|
||||||
|
TrkRxGain.Min := 0; TrkRxGain.Max := 73; // AD936x hardwaregain dB
|
||||||
|
TrkRxGain.Position := FController.FRxGainDb;
|
||||||
|
TrkRxGain.OnChange := TrkRxGainChange;
|
||||||
|
LblRxGain := TLabel.Create(Self);
|
||||||
|
LblRxGain.Parent := PanelPlutoAGC;
|
||||||
|
LblRxGain.Left := LEFT_W - 38; LblRxGain.Top := 44;
|
||||||
|
LblRxGain.Caption := Format('%ddB', [FController.FRxGainDb]);
|
||||||
|
LblRxGain.Font.Color := CLR_TEXT; LblRxGain.Font.Name := 'Courier New';
|
||||||
|
LblRxGain.Font.Size := 7;
|
||||||
|
// НЕ Inc(Y): панель скрыта и наложена на позицию Bands; LayoutLeftPanel сдвинет.
|
||||||
|
|
||||||
// Band selector. Высота резервирует 2 дополнительных ряда под XVTR-кнопки
|
// Band selector. Высота резервирует 2 дополнительных ряда под XVTR-кнопки
|
||||||
// (создаются динамически в RebuildXvtrButtons). В каждом ряду до 6 кнопок,
|
// (создаются динамически в RebuildXvtrButtons). В каждом ряду до 6 кнопок,
|
||||||
// итого до 12 XVTR-кнопок (наш CFG_XVTR_COUNT = 8 умещается с запасом).
|
// итого до 12 XVTR-кнопок (наш CFG_XVTR_COUNT = 8 умещается с запасом).
|
||||||
@@ -3540,6 +3594,10 @@ begin
|
|||||||
FSampleRateOverlay.SetRatePresetsDefault; // HPSDR 48k..1536k
|
FSampleRateOverlay.SetRatePresetsDefault; // HPSDR 48k..1536k
|
||||||
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
|
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Отдельный Pluto RF-AGC блок под AGC: показываем и сдвигаем панели ниже.
|
||||||
|
LayoutLeftPanel(Caps.HasHWGain);
|
||||||
|
if Caps.HasHWGain then UpdateRxGainUI;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.DoConnectDevice(const Dev: THPSDRDevice);
|
procedure TMainForm.DoConnectDevice(const Dev: THPSDRDevice);
|
||||||
@@ -5252,6 +5310,55 @@ begin
|
|||||||
FController.SetAGCTop(TrkAGC.Position);
|
FController.SetAGCTop(TrkAGC.Position);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.TrkRxGainChange(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if FSyncingFromController then Exit;
|
||||||
|
FController.SetRxGain(TrkRxGain.Position); // переводит в manual
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.BtnPlutoGainClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if FSyncingFromController then Exit;
|
||||||
|
FController.SetRxGainMode((Sender as TFlatButton).Tag); // Tag = индекс режима
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.UpdateRxGainUI;
|
||||||
|
// Рендер Pluto RF-AGC блока: подсветка режима + слайдер/значение manual gain.
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
if PanelPlutoAGC = nil then Exit;
|
||||||
|
FSyncingFromController := True;
|
||||||
|
try
|
||||||
|
for i := 0 to 3 do
|
||||||
|
if BtnPlutoGain[i] <> nil then
|
||||||
|
StyleButton(BtnPlutoGain[i], BtnPlutoGain[i].Tag = FController.FRxGainMode);
|
||||||
|
TrkRxGain.Position := FController.FRxGainDb;
|
||||||
|
TrkRxGain.Enabled := (FController.FRxGainMode = 0); // активен только в manual
|
||||||
|
if FController.FRxGainMode = 0 then
|
||||||
|
LblRxGain.Caption := Format('%ddB', [FController.FRxGainDb])
|
||||||
|
else
|
||||||
|
LblRxGain.Caption := 'AGC';
|
||||||
|
finally
|
||||||
|
FSyncingFromController := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.LayoutLeftPanel(Pluto: Boolean);
|
||||||
|
// Показывает/скрывает Pluto RF-AGC блок под AGC и сдвигает панели ниже
|
||||||
|
// (Bands и далее через RelayoutBelowBands).
|
||||||
|
var baseTop: Integer;
|
||||||
|
begin
|
||||||
|
if (PanelPlutoAGC = nil) or (PanelAGC = nil) or (PanelBands = nil) then Exit;
|
||||||
|
baseTop := PanelAGC.Top + PanelAGC.Height + 4;
|
||||||
|
PanelPlutoAGC.Top := baseTop;
|
||||||
|
PanelPlutoAGC.Visible := Pluto;
|
||||||
|
if Pluto then
|
||||||
|
PanelBands.Top := baseTop + PanelPlutoAGC.Height + 2
|
||||||
|
else
|
||||||
|
PanelBands.Top := baseTop;
|
||||||
|
RelayoutBelowBands;
|
||||||
|
end;
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// DSP/Audio callbacks — вызываются из рабочих потоков
|
// DSP/Audio callbacks — вызываются из рабочих потоков
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|||||||
+29
-3
@@ -94,6 +94,7 @@ type
|
|||||||
Transmitting, PAEnabled, AlexEnabled: Boolean); override;
|
Transmitting, PAEnabled, AlexEnabled: Boolean); override;
|
||||||
procedure SendFullHP; override;
|
procedure SendFullHP; override;
|
||||||
procedure SendPTT(Active: Boolean); override;
|
procedure SendPTT(Active: Boolean); override;
|
||||||
|
procedure SetRxGain(ModeIdx: Integer; GainDb: Integer); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
||||||
@@ -446,12 +447,25 @@ begin
|
|||||||
if (FPhy = nil) or (Hz < PLUTO_MIN_SR) then Exit;
|
if (FPhy = nil) or (Hz < PLUTO_MIN_SR) then Exit;
|
||||||
WasStreaming := FStreaming;
|
WasStreaming := FStreaming;
|
||||||
if WasStreaming then StopRX; // менять rate с открытым буфером нельзя
|
if WasStreaming then StopRX; // менять rate с открытым буфером нельзя
|
||||||
|
|
||||||
|
// Предпочтительно ad9361_set_bb_rate: настраивает BBPLL И загружает FIR-таблицу
|
||||||
|
// децимации под частоту. Без этого на холодную (битый/дефолтный FIR) спектр —
|
||||||
|
// широкая палка по центру, не реагирует на перестройку. Fallback — прямая
|
||||||
|
// запись sampling_frequency (работает, если FIR уже корректен).
|
||||||
|
if Assigned(ad9361_set_bb_rate) then
|
||||||
|
ad9361_set_bb_rate(FPhy, culong(Hz))
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Ch := iio_device_find_channel(FPhy, 'voltage0', 0);
|
||||||
|
if Ch <> nil then
|
||||||
|
iio_channel_attr_write_longlong(Ch, 'sampling_frequency', Hz);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// RF-полоса отдельно (set_bb_rate её не трогает).
|
||||||
Ch := iio_device_find_channel(FPhy, 'voltage0', 0);
|
Ch := iio_device_find_channel(FPhy, 'voltage0', 0);
|
||||||
if Ch <> nil then
|
if Ch <> nil then
|
||||||
begin
|
|
||||||
iio_channel_attr_write_longlong(Ch, 'sampling_frequency', Hz);
|
|
||||||
iio_channel_attr_write_longlong(Ch, 'rf_bandwidth', Hz);
|
iio_channel_attr_write_longlong(Ch, 'rf_bandwidth', Hz);
|
||||||
end;
|
|
||||||
if WasStreaming then StartRX;
|
if WasStreaming then StartRX;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -552,4 +566,16 @@ begin
|
|||||||
// Реальный RX↔TX (вкл/выкл стримов) — фаза 4.
|
// Реальный RX↔TX (вкл/выкл стримов) — фаза 4.
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TPlutoBackend.SetRxGain(ModeIdx: Integer; GainDb: Integer);
|
||||||
|
begin
|
||||||
|
case ModeIdx of
|
||||||
|
1: FGainMode := 'fast_attack';
|
||||||
|
2: FGainMode := 'slow_attack';
|
||||||
|
3: FGainMode := 'hybrid';
|
||||||
|
else FGainMode := 'manual';
|
||||||
|
end;
|
||||||
|
FGainDb := GainDb;
|
||||||
|
if FConnected then ApplyGain;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ type
|
|||||||
procedure SendDUCSpecific(const Pkt: TDUCSpecificPacket); virtual;
|
procedure SendDUCSpecific(const Pkt: TDUCSpecificPacket); virtual;
|
||||||
procedure SendHighPriority(const Pkt: THighPriorityPacket); virtual;
|
procedure SendHighPriority(const Pkt: THighPriorityPacket); virtual;
|
||||||
procedure SetStepAtten(dB: Byte); virtual;
|
procedure SetStepAtten(dB: Byte); virtual;
|
||||||
|
// RX-усиление (Pluto/AD936x). ModeIdx: 0=manual, 1=fast_attack, 2=slow_attack,
|
||||||
|
// 3=hybrid. GainDb применяется только в manual. HPSDR — no-op.
|
||||||
|
procedure SetRxGain(ModeIdx: Integer; GainDb: Integer); virtual;
|
||||||
procedure SetAlexConfig(const A: TAlexSettings); virtual;
|
procedure SetAlexConfig(const A: TAlexSettings); virtual;
|
||||||
procedure SetXvtrMode(AEnable, ADisablePA: Boolean; ARxAnt: Byte); virtual;
|
procedure SetXvtrMode(AEnable, ADisablePA: Boolean; ARxAnt: Byte); virtual;
|
||||||
procedure SendDDCAudio(const LeftRight: array of SmallInt); virtual;
|
procedure SendDDCAudio(const LeftRight: array of SmallInt); virtual;
|
||||||
@@ -204,6 +207,10 @@ procedure TRadioBackend.SetStepAtten(dB: Byte);
|
|||||||
begin
|
begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRadioBackend.SetRxGain(ModeIdx: Integer; GainDb: Integer);
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TRadioBackend.SetAlexConfig(const A: TAlexSettings);
|
procedure TRadioBackend.SetAlexConfig(const A: TAlexSettings);
|
||||||
begin
|
begin
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ type
|
|||||||
rfVfoA, rfVfoB, rfActiveVfo,
|
rfVfoA, rfVfoB, rfActiveVfo,
|
||||||
rfMode, rfFilter, rfFilterBW,
|
rfMode, rfFilter, rfFilterBW,
|
||||||
rfAGCMode, rfAGCTop,
|
rfAGCMode, rfAGCTop,
|
||||||
|
rfRxGain, // Pluto: hw-gain (auto/manual dB)
|
||||||
rfVolume, rfMute, rfDrive, rfAtten,
|
rfVolume, rfMute, rfDrive, rfAtten,
|
||||||
rfBand, rfXvtr, rfBandRestore, // rfBandRestore: явная смена диапазона (сброс Wf-avg в UI)
|
rfBand, rfXvtr, rfBandRestore, // rfBandRestore: явная смена диапазона (сброс Wf-avg в UI)
|
||||||
rfCenterFreq, rfSpan, rfSampleRate,
|
rfCenterFreq, rfSpan, rfSampleRate,
|
||||||
@@ -95,6 +96,8 @@ type
|
|||||||
ActiveVfo: Integer;
|
ActiveVfo: Integer;
|
||||||
Mode, Filter, FilterBW: Integer;
|
Mode, Filter, FilterBW: Integer;
|
||||||
AGCMode, AGCTop: Integer;
|
AGCMode, AGCTop: Integer;
|
||||||
|
RxGainMode: Integer;
|
||||||
|
RxGainDb: Integer;
|
||||||
Volume, Drive, Atten: Integer;
|
Volume, Drive, Atten: Integer;
|
||||||
Band, Xvtr: Integer;
|
Band, Xvtr: Integer;
|
||||||
Muted, CTun, Duplex: Boolean;
|
Muted, CTun, Duplex: Boolean;
|
||||||
@@ -164,6 +167,8 @@ type
|
|||||||
FFilterBW: Integer;
|
FFilterBW: Integer;
|
||||||
FAGCMode: Integer;
|
FAGCMode: Integer;
|
||||||
FAGCTop: Integer;
|
FAGCTop: Integer;
|
||||||
|
FRxGainMode: Integer; // Pluto hw-gain: 0=manual,1=fast,2=slow,3=hybrid
|
||||||
|
FRxGainDb: Integer; // Pluto manual gain (дБ, 0..73)
|
||||||
FCTun: Boolean;
|
FCTun: Boolean;
|
||||||
FCenterFreq: Double;
|
FCenterFreq: Double;
|
||||||
FSpanHz: Double;
|
FSpanHz: Double;
|
||||||
@@ -395,6 +400,13 @@ type
|
|||||||
procedure SetAGCTop(DB: Integer);
|
procedure SetAGCTop(DB: Integer);
|
||||||
procedure AGCTopBy(Delta: Integer);
|
procedure AGCTopBy(Delta: Integer);
|
||||||
|
|
||||||
|
// RX hw-gain (Pluto): режим (0=manual,1=fast,2=slow,3=hybrid) / ручной дБ.
|
||||||
|
// На HPSDR — no-op в бэкенде.
|
||||||
|
procedure SetRxGainMode(M: Integer);
|
||||||
|
procedure SetRxGain(Db: Integer);
|
||||||
|
procedure RxGainBy(Delta: Integer);
|
||||||
|
procedure ApplyRxGainToBackend; // FRxGain* → FNetwork.SetRxGain
|
||||||
|
|
||||||
// Громкость / mute / drive / аттенюатор
|
// Громкость / mute / drive / аттенюатор
|
||||||
procedure SetVolume(V: Integer);
|
procedure SetVolume(V: Integer);
|
||||||
procedure VolumeBy(Delta: Integer);
|
procedure VolumeBy(Delta: Integer);
|
||||||
@@ -486,6 +498,7 @@ begin
|
|||||||
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
|
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
|
||||||
FMode := 1; FFilter := 5; FFilterBW := 2700;
|
FMode := 1; FFilter := 5; FFilterBW := 2700;
|
||||||
FAGCMode := 1; FAGCTop := 90;
|
FAGCMode := 1; FAGCTop := 90;
|
||||||
|
FRxGainMode := 2; FRxGainDb := 40; // Pluto: slow_attack по умолчанию
|
||||||
FVolume := 70; FDrivePercent := 50; FDriveLevel := 0; FAtten := 0;
|
FVolume := 70; FDrivePercent := 50; FDriveLevel := 0; FAtten := 0;
|
||||||
FCenterFreq := FVfoA; FSpanHz := 192000; FSampleRate := 192000;
|
FCenterFreq := FVfoA; FSpanHz := 192000; FSampleRate := 192000;
|
||||||
FCurrentBand := 5; FCurrentXvtr := -1;
|
FCurrentBand := 5; FCurrentXvtr := -1;
|
||||||
@@ -863,6 +876,7 @@ begin
|
|||||||
Result.SampleRate := FSampleRate; Result.ActiveVfo := FActiveVfo;
|
Result.SampleRate := FSampleRate; Result.ActiveVfo := FActiveVfo;
|
||||||
Result.Mode := FMode; Result.Filter := FFilter; Result.FilterBW := FFilterBW;
|
Result.Mode := FMode; Result.Filter := FFilter; Result.FilterBW := FFilterBW;
|
||||||
Result.AGCMode := FAGCMode; Result.AGCTop := FAGCTop;
|
Result.AGCMode := FAGCMode; Result.AGCTop := FAGCTop;
|
||||||
|
Result.RxGainMode := FRxGainMode; Result.RxGainDb := FRxGainDb;
|
||||||
Result.Volume := FVolume; Result.Drive := FDrivePercent; Result.Atten := FAtten;
|
Result.Volume := FVolume; Result.Drive := FDrivePercent; Result.Atten := FAtten;
|
||||||
Result.Band := FCurrentBand; Result.Xvtr := FCurrentXvtr;
|
Result.Band := FCurrentBand; Result.Xvtr := FCurrentXvtr;
|
||||||
Result.Muted := FMuted; Result.CTun := FCTun; Result.Duplex := FDisplayDuplex;
|
Result.Muted := FMuted; Result.CTun := FCTun; Result.Duplex := FDisplayDuplex;
|
||||||
@@ -1121,6 +1135,8 @@ begin
|
|||||||
Result.LastBand := FCurrentBand;
|
Result.LastBand := FCurrentBand;
|
||||||
Result.LastXvtr := FCurrentXvtr;
|
Result.LastXvtr := FCurrentXvtr;
|
||||||
Result.SampleRate := FSampleRate;
|
Result.SampleRate := FSampleRate;
|
||||||
|
Result.RxGainMode := FRxGainMode;
|
||||||
|
Result.RxGainDb := FRxGainDb;
|
||||||
if FWDSPReady then
|
if FWDSPReady then
|
||||||
begin
|
begin
|
||||||
Result.FFTSize := FDSPEngine.FFTSize;
|
Result.FFTSize := FDSPEngine.FFTSize;
|
||||||
@@ -1633,6 +1649,33 @@ end;
|
|||||||
procedure TRadioController.AGCTopBy(Delta: Integer);
|
procedure TRadioController.AGCTopBy(Delta: Integer);
|
||||||
begin SetAGCTop(FAGCTop + Delta); end;
|
begin SetAGCTop(FAGCTop + Delta); end;
|
||||||
|
|
||||||
|
procedure TRadioController.ApplyRxGainToBackend;
|
||||||
|
begin
|
||||||
|
if Assigned(FNetwork) and FNetwork.Connected then
|
||||||
|
FNetwork.SetRxGain(FRxGainMode, FRxGainDb);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SetRxGainMode(M: Integer);
|
||||||
|
begin
|
||||||
|
if M < 0 then M := 0 else if M > 3 then M := 3;
|
||||||
|
FRxGainMode := M;
|
||||||
|
ApplyRxGainToBackend;
|
||||||
|
Changed(rfRxGain);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SetRxGain(Db: Integer);
|
||||||
|
begin
|
||||||
|
if Db < 0 then Db := 0 else if Db > 73 then Db := 73;
|
||||||
|
FRxGainDb := Db;
|
||||||
|
// Ручная установка усиления подразумевает manual-режим (0).
|
||||||
|
FRxGainMode := 0;
|
||||||
|
ApplyRxGainToBackend;
|
||||||
|
Changed(rfRxGain);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.RxGainBy(Delta: Integer);
|
||||||
|
begin SetRxGain(FRxGainDb + Delta); end;
|
||||||
|
|
||||||
procedure TRadioController.SetVolume(V: Integer);
|
procedure TRadioController.SetVolume(V: Integer);
|
||||||
begin
|
begin
|
||||||
if V < 0 then V := 0 else if V > 100 then V := 100;
|
if V < 0 then V := 0 else if V > 100 then V := 100;
|
||||||
@@ -2284,6 +2327,8 @@ begin
|
|||||||
FNetwork.ConfigureDDCs(1, FSampleRate div 1000, 0, FDitherEnabled, FRandomEnabled);
|
FNetwork.ConfigureDDCs(1, FSampleRate div 1000, 0, FDitherEnabled, FRandomEnabled);
|
||||||
// Применяем сохранённое состояние динамика/отправки аудио (мьют byte 1400 bit1).
|
// Применяем сохранённое состояние динамика/отправки аудио (мьют byte 1400 bit1).
|
||||||
FNetwork.SetSpeakerAudio(FSendAudioToRadio);
|
FNetwork.SetSpeakerAudio(FSendAudioToRadio);
|
||||||
|
// RX hw-gain в бэкенд (Pluto). HPSDR — no-op.
|
||||||
|
ApplyRxGainToBackend;
|
||||||
SendDUCSpecificFromSettings;
|
SendDUCSpecificFromSettings;
|
||||||
// Применяем TX-цепь (фильтр, mic gain, EQ, leveler, ALC, comp, ...).
|
// Применяем TX-цепь (фильтр, mic gain, EQ, leveler, ALC, comp, ...).
|
||||||
ApplyTXSettingsToDSP;
|
ApplyTXSettingsToDSP;
|
||||||
@@ -2374,6 +2419,8 @@ begin
|
|||||||
FSampleRate := 1536000;
|
FSampleRate := 1536000;
|
||||||
FSpanHz := FSampleRate;
|
FSpanHz := FSampleRate;
|
||||||
end;
|
end;
|
||||||
|
FRxGainMode := EnsureRange(FLoadedGlobal.RxGainMode, 0, 3);
|
||||||
|
FRxGainDb := EnsureRange(FLoadedGlobal.RxGainDb, 0, 73);
|
||||||
FNRMode := EnsureRange(FLoadedGlobal.NRMode, 0, 4);
|
FNRMode := EnsureRange(FLoadedGlobal.NRMode, 0, 4);
|
||||||
FNBMode := EnsureRange(FLoadedGlobal.NBMode, 0, 2);
|
FNBMode := EnsureRange(FLoadedGlobal.NBMode, 0, 2);
|
||||||
FSNB := FLoadedGlobal.SNBEnabled;
|
FSNB := FLoadedGlobal.SNBEnabled;
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ type
|
|||||||
LastBand: Integer;
|
LastBand: Integer;
|
||||||
LastXvtr: Integer; // -1 = HF, 0..CFG_XVTR_COUNT-1 = активный трансвертер при выходе
|
LastXvtr: Integer; // -1 = HF, 0..CFG_XVTR_COUNT-1 = активный трансвертер при выходе
|
||||||
SampleRate: Integer; // глобальный — один для всех диапазонов
|
SampleRate: Integer; // глобальный — один для всех диапазонов
|
||||||
|
RxGainMode: Integer; // Pluto: 0=manual,1=fast,2=slow,3=hybrid
|
||||||
|
RxGainDb: Integer; // Pluto: manual gain дБ
|
||||||
WindowLeft: Integer;
|
WindowLeft: Integer;
|
||||||
WindowTop: Integer;
|
WindowTop: Integer;
|
||||||
WindowWidth: Integer;
|
WindowWidth: Integer;
|
||||||
@@ -401,6 +403,7 @@ begin
|
|||||||
G.WfAGCEnabled := True;
|
G.WfAGCEnabled := True;
|
||||||
G.WfNFEnabled := False;
|
G.WfNFEnabled := False;
|
||||||
G.SampleRate := 192000;
|
G.SampleRate := 192000;
|
||||||
|
G.RxGainMode := 2; G.RxGainDb := 40;
|
||||||
G.PAMaxPower := 100.0;
|
G.PAMaxPower := 100.0;
|
||||||
for i := 0 to CFG_BAND_COUNT-1 do G.PABandCal[i] := CFG_PA_BAND_CAL_DEFAULT[i];
|
for i := 0 to CFG_BAND_COUNT-1 do G.PABandCal[i] := CFG_PA_BAND_CAL_DEFAULT[i];
|
||||||
for i := 0 to CFG_XVTR_COUNT-1 do G.VHFBandCal[i] := CFG_VHF_CAL_DEFAULT;
|
for i := 0 to CFG_XVTR_COUNT-1 do G.VHFBandCal[i] := CFG_VHF_CAL_DEFAULT;
|
||||||
@@ -597,6 +600,8 @@ begin
|
|||||||
G.LastBand := JI(GObj,'last_band',5);
|
G.LastBand := JI(GObj,'last_band',5);
|
||||||
G.LastXvtr := JI(GObj,'last_xvtr',-1);
|
G.LastXvtr := JI(GObj,'last_xvtr',-1);
|
||||||
G.SampleRate := JI(GObj,'sample_rate',192000);
|
G.SampleRate := JI(GObj,'sample_rate',192000);
|
||||||
|
G.RxGainMode := JI(GObj,'rx_gain_mode',2);
|
||||||
|
G.RxGainDb := JI(GObj,'rx_gain_db',40);
|
||||||
// Display settings
|
// Display settings
|
||||||
G.FFTSize := JI(GObj,'fft_size',131072);
|
G.FFTSize := JI(GObj,'fft_size',131072);
|
||||||
G.WindowType := JI(GObj,'win_type',2);
|
G.WindowType := JI(GObj,'win_type',2);
|
||||||
@@ -712,6 +717,8 @@ begin
|
|||||||
JW(O,'last_band',G.LastBand);
|
JW(O,'last_band',G.LastBand);
|
||||||
JW(O,'last_xvtr',G.LastXvtr);
|
JW(O,'last_xvtr',G.LastXvtr);
|
||||||
JW(O,'sample_rate',G.SampleRate);
|
JW(O,'sample_rate',G.SampleRate);
|
||||||
|
JW(O,'rx_gain_mode',G.RxGainMode);
|
||||||
|
JW(O,'rx_gain_db',G.RxGainDb);
|
||||||
// Display settings
|
// Display settings
|
||||||
JW(O,'fft_size',G.FFTSize);
|
JW(O,'fft_size',G.FFTSize);
|
||||||
JW(O,'win_type',G.WindowType);
|
JW(O,'win_type',G.WindowType);
|
||||||
|
|||||||
Reference in New Issue
Block a user