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:
2026-06-17 12:06:59 +03:00
co-authored by Claude Opus 4.8
parent a988849d3e
commit 24f74357cd
6 changed files with 228 additions and 3 deletions
+47
View File
@@ -58,6 +58,7 @@ type
rfVfoA, rfVfoB, rfActiveVfo,
rfMode, rfFilter, rfFilterBW,
rfAGCMode, rfAGCTop,
rfRxGain, // Pluto: hw-gain (auto/manual dB)
rfVolume, rfMute, rfDrive, rfAtten,
rfBand, rfXvtr, rfBandRestore, // rfBandRestore: явная смена диапазона (сброс Wf-avg в UI)
rfCenterFreq, rfSpan, rfSampleRate,
@@ -95,6 +96,8 @@ type
ActiveVfo: Integer;
Mode, Filter, FilterBW: Integer;
AGCMode, AGCTop: Integer;
RxGainMode: Integer;
RxGainDb: Integer;
Volume, Drive, Atten: Integer;
Band, Xvtr: Integer;
Muted, CTun, Duplex: Boolean;
@@ -164,6 +167,8 @@ type
FFilterBW: Integer;
FAGCMode: 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;
FCenterFreq: Double;
FSpanHz: Double;
@@ -395,6 +400,13 @@ type
procedure SetAGCTop(DB: 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 / аттенюатор
procedure SetVolume(V: Integer);
procedure VolumeBy(Delta: Integer);
@@ -486,6 +498,7 @@ begin
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
FMode := 1; FFilter := 5; FFilterBW := 2700;
FAGCMode := 1; FAGCTop := 90;
FRxGainMode := 2; FRxGainDb := 40; // Pluto: slow_attack по умолчанию
FVolume := 70; FDrivePercent := 50; FDriveLevel := 0; FAtten := 0;
FCenterFreq := FVfoA; FSpanHz := 192000; FSampleRate := 192000;
FCurrentBand := 5; FCurrentXvtr := -1;
@@ -863,6 +876,7 @@ begin
Result.SampleRate := FSampleRate; Result.ActiveVfo := FActiveVfo;
Result.Mode := FMode; Result.Filter := FFilter; Result.FilterBW := FFilterBW;
Result.AGCMode := FAGCMode; Result.AGCTop := FAGCTop;
Result.RxGainMode := FRxGainMode; Result.RxGainDb := FRxGainDb;
Result.Volume := FVolume; Result.Drive := FDrivePercent; Result.Atten := FAtten;
Result.Band := FCurrentBand; Result.Xvtr := FCurrentXvtr;
Result.Muted := FMuted; Result.CTun := FCTun; Result.Duplex := FDisplayDuplex;
@@ -1121,6 +1135,8 @@ begin
Result.LastBand := FCurrentBand;
Result.LastXvtr := FCurrentXvtr;
Result.SampleRate := FSampleRate;
Result.RxGainMode := FRxGainMode;
Result.RxGainDb := FRxGainDb;
if FWDSPReady then
begin
Result.FFTSize := FDSPEngine.FFTSize;
@@ -1633,6 +1649,33 @@ end;
procedure TRadioController.AGCTopBy(Delta: Integer);
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);
begin
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);
// Применяем сохранённое состояние динамика/отправки аудио (мьют byte 1400 bit1).
FNetwork.SetSpeakerAudio(FSendAudioToRadio);
// RX hw-gain в бэкенд (Pluto). HPSDR — no-op.
ApplyRxGainToBackend;
SendDUCSpecificFromSettings;
// Применяем TX-цепь (фильтр, mic gain, EQ, leveler, ALC, comp, ...).
ApplyTXSettingsToDSP;
@@ -2374,6 +2419,8 @@ begin
FSampleRate := 1536000;
FSpanHz := FSampleRate;
end;
FRxGainMode := EnsureRange(FLoadedGlobal.RxGainMode, 0, 3);
FRxGainDb := EnsureRange(FLoadedGlobal.RxGainDb, 0, 73);
FNRMode := EnsureRange(FLoadedGlobal.NRMode, 0, 4);
FNBMode := EnsureRange(FLoadedGlobal.NBMode, 0, 2);
FSNB := FLoadedGlobal.SNBEnabled;