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
+31
View File
@@ -84,6 +84,13 @@ var
// ---- Утилиты ----
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 — готово к использованию. Идемпотентно.
function IIOLoad: Boolean;
// True, если libiio загружена и символы разрешены.
@@ -95,16 +102,20 @@ implementation
var
GLib: TLibHandle = NilHandle;
GLibAD: TLibHandle = NilHandle; // libad9361 (опционально)
GLoaded: Boolean = False;
const
{$IFDEF WINDOWS}
IIO_LIBS: array[0..1] of string = ('libiio.dll', 'iio.dll');
AD_LIBS: array[0..1] of string = ('libad9361.dll', 'ad9361.dll');
{$ELSE}
{$IFDEF DARWIN}
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}
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}
@@ -163,6 +174,21 @@ begin
iio_buffer_step := Sym('iio_buffer_step');
iio_buffer_end := Sym('iio_buffer_end');
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;
except
on E: Exception do
@@ -190,6 +216,11 @@ begin
end;
finalization
if GLibAD <> NilHandle then
begin
UnloadLibrary(GLibAD);
GLibAD := NilHandle;
end;
if GLib <> NilHandle then
begin
UnloadLibrary(GLib);