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
+29 -3
View File
@@ -94,6 +94,7 @@ type
Transmitting, PAEnabled, AlexEnabled: Boolean); override;
procedure SendFullHP; override;
procedure SendPTT(Active: Boolean); override;
procedure SetRxGain(ModeIdx: Integer; GainDb: Integer); override;
end;
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
@@ -446,12 +447,25 @@ begin
if (FPhy = nil) or (Hz < PLUTO_MIN_SR) then Exit;
WasStreaming := FStreaming;
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);
if Ch <> nil then
begin
iio_channel_attr_write_longlong(Ch, 'sampling_frequency', Hz);
iio_channel_attr_write_longlong(Ch, 'rf_bandwidth', Hz);
end;
if WasStreaming then StartRX;
end;
@@ -552,4 +566,16 @@ begin
// Реальный RX↔TX (вкл/выкл стримов) — фаза 4.
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.