mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feature(pluto): компенсация hardwaregain в уровнях + живое усиление в блоке RF AGC
На Pluto шкала dBm ничего не значила: усиление тракта 0..73 дБ входит в показания напрямую, а в режимах AGC железо крутит его само, и картинка «дышит» вместе с ним. - RadioBackend: виртуальный RxGainNowDb (TELEMETRY_NONE = регулируемого усиления нет, как у openHPSDR с его аттенюатором). - PlutoBackend: в MANUAL отдаёт FGainDb (точно, без опроса железа), в режимах AGC — hardwaregain из телеметрического потока (читается там же, где rssi, ~2 Гц, не блокируя UI). - RadioController: AttenCalDB -> FrontEndCalDB, общее понятие «регулируемое усиление входа»: openHPSDR +FAtten (аттенюатор режет), AD936x -hardwaregain (усиление поднимает). Постоянная часть тракта (LNB, кабель, конвертер) остаётся за таблицами калибровки на диапазон/трансвертер. - UI: в блоке RF AGC при режиме != MANUAL вместо надписи «AGC» — фактическое усиление («~38dB», тильда = измеренное), ползунок ездит сам. Видно, что цифра гуляет, а уровни на спектре стоят. Ограничения: FAST AGC крутит усиление быстрее опроса (2 Гц), для стабильной шкалы нужен MANUAL или SLOW; шумовая полка при смене усиления немного едет — это физика (input-referred NF), компенсацию проверять по сигналу. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+34
-4
@@ -109,11 +109,13 @@ type
|
||||
FTeleTemp: Double; // TELEMETRY_NONE, пока не прочитано
|
||||
FTeleSoc: Double;
|
||||
FTeleRssi: Double;
|
||||
FTeleGain: Double; // hardwaregain RX, дБ (в AGC-режимах крутит железо)
|
||||
FTeleValid: Boolean; // был ли хоть один успешный опрос
|
||||
procedure StartTelemetry;
|
||||
procedure StopTelemetry;
|
||||
// Блокирующий опрос железа: только из FTeleThread.
|
||||
function PollTelemetryBlocking(out TempC, SocTempC, RSSIdB: Double): Boolean;
|
||||
function PollTelemetryBlocking(out TempC, SocTempC, RSSIdB,
|
||||
GainDb: Double): Boolean;
|
||||
|
||||
// Низкоуровневые помощники применения атрибутов к ad9361-phy
|
||||
procedure ApplyRxLO(Hz: Double);
|
||||
@@ -174,6 +176,7 @@ type
|
||||
procedure SendDUCIQ(const IData, QData: array of Integer); override;
|
||||
procedure ClearDUCIQQueue; override;
|
||||
function ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean; override;
|
||||
function RxGainNowDb: Double; override;
|
||||
end;
|
||||
|
||||
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
||||
@@ -413,6 +416,7 @@ begin
|
||||
FTxStreaming := False; FTxRatio := 8;
|
||||
FTeleThread := nil; FTeleValid := False;
|
||||
FTeleTemp := TELEMETRY_NONE; FTeleSoc := TELEMETRY_NONE; FTeleRssi := TELEMETRY_NONE;
|
||||
FTeleGain := TELEMETRY_NONE;
|
||||
InitCriticalSection(FTeleLock);
|
||||
FTxAttDb := PLUTO_TX_OFF; // на старте TX выключен (макс. аттенюация)
|
||||
// FIFO на ~4 TX-буфера запаса под джиттер продюсера/консьюмера. Окончательный
|
||||
@@ -1133,7 +1137,8 @@ begin
|
||||
LeaveCriticalSection(FTxLock);
|
||||
end;
|
||||
|
||||
function TPlutoBackend.PollTelemetryBlocking(out TempC, SocTempC, RSSIdB: Double): Boolean;
|
||||
function TPlutoBackend.PollTelemetryBlocking(out TempC, SocTempC, RSSIdB,
|
||||
GainDb: Double): Boolean;
|
||||
// БЛОКИРУЮЩИЙ опрос железа — только из FTeleThread (каждое чтение = USB round-trip):
|
||||
// ad9361-phy temp0/input — температура трансивера в миллиградусах C (longlong);
|
||||
// xadc temp0 — температура Zynq SoC/FPGA; сырой raw + offset (longlong)
|
||||
@@ -1149,6 +1154,7 @@ begin
|
||||
TempC := TELEMETRY_NONE;
|
||||
SocTempC := TELEMETRY_NONE;
|
||||
RSSIdB := TELEMETRY_NONE;
|
||||
GainDb := TELEMETRY_NONE;
|
||||
Result := False;
|
||||
if (not FConnected) or (FPhy = nil) or (not IIOLoaded) then Exit;
|
||||
|
||||
@@ -1190,6 +1196,29 @@ begin
|
||||
RSSIdB := d;
|
||||
Result := True;
|
||||
end;
|
||||
// hardwaregain: в режимах AGC железо крутит усиление само, и без него шкала
|
||||
// dBm «дышит» вместе с ним (см. TRadioController.FrontEndCalDB).
|
||||
if (Ch <> nil) and Assigned(iio_channel_attr_read_double)
|
||||
and (iio_channel_attr_read_double(Ch, 'hardwaregain', d) = 0) then
|
||||
begin
|
||||
GainDb := d;
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPlutoBackend.RxGainNowDb: Double;
|
||||
// В ручном режиме значение известно точно и без опроса железа; в AGC-режимах
|
||||
// отдаём последний снимок телеметрии (обновляется ~2 Гц — быстрый AGC всё
|
||||
// равно шевелится быстрее, для стабильной шкалы нужен MANUAL или SLOW).
|
||||
begin
|
||||
if FGainMode = 'manual' then Exit(FGainDb);
|
||||
EnterCriticalSection(FTeleLock);
|
||||
try
|
||||
if FTeleValid and (FTeleGain > TELEMETRY_NONE) then Result := FTeleGain
|
||||
else Result := FGainDb;
|
||||
finally
|
||||
LeaveCriticalSection(FTeleLock);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -1244,7 +1273,7 @@ const
|
||||
POLL_MS = 500; // ~2 Гц: чаще незачем, каждый опрос ~30 мс USB-транзакций
|
||||
STEP_MS = 25; // мелкий шаг сна — чтобы Terminate отрабатывал быстро
|
||||
var
|
||||
t, s, r: Double;
|
||||
t, s, r, g: Double;
|
||||
ok: Boolean;
|
||||
Waited: Integer;
|
||||
begin
|
||||
@@ -1252,7 +1281,7 @@ begin
|
||||
begin
|
||||
if FBk.FConnected then
|
||||
begin
|
||||
ok := FBk.PollTelemetryBlocking(t, s, r);
|
||||
ok := FBk.PollTelemetryBlocking(t, s, r, g);
|
||||
if ok then
|
||||
begin
|
||||
EnterCriticalSection(FBk.FTeleLock);
|
||||
@@ -1260,6 +1289,7 @@ begin
|
||||
FBk.FTeleTemp := t;
|
||||
FBk.FTeleSoc := s;
|
||||
FBk.FTeleRssi := r;
|
||||
FBk.FTeleGain := g;
|
||||
FBk.FTeleValid := True;
|
||||
finally
|
||||
LeaveCriticalSection(FBk.FTeleLock);
|
||||
|
||||
Reference in New Issue
Block a user