mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat: pluto telemetry — show both temperatures (transceiver + Zynq SoC)
Pluto exposes two temperature sensors: the AD9361 transceiver (ad9361-phy temp0/input, milli-°C) and the Zynq SoC/FPGA (xadc temp0, raw+offset+scale → C = (raw+offset)*scale/1000). Only the former was read. Now both are read and shown as "Temp 37/53C" (chip/SoC) in the desktop status bar and web UI. - ReadTelemetry gains an out SocTempC param (base + Pluto override); Pluto reads xadc via the IIO context. - Controller stores FLastPlutoSocTempC; SoC temp validated with a plausibility window (no slew-limit — stable local sensor, unlike the AD9361 sensor that spikes during mid-conversion). - Status formatting handles one or both temps in MainForm and WebAdapter. - Widen desktop status-bar field 3 (150→190) so both temps + RSSI fit; web layout unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -3240,8 +3240,14 @@ begin
|
|||||||
begin
|
begin
|
||||||
if not FController.FRunning then begin SetStatusText(3, 'Pluto --'); Exit; end;
|
if not FController.FRunning then begin SetStatusText(3, 'Pluto --'); Exit; end;
|
||||||
S := '';
|
S := '';
|
||||||
if FController.FLastPlutoTempC > TELEMETRY_NONE then
|
// Temp X/YC: X — трансивер AD9361, Y — Zynq SoC/FPGA (xadc).
|
||||||
S := Format('Temp %.0fC', [FController.FLastPlutoTempC]);
|
if (FController.FLastPlutoTempC > TELEMETRY_NONE)
|
||||||
|
and (FController.FLastPlutoSocTempC > TELEMETRY_NONE) then
|
||||||
|
S := Format('Temp %.0f/%.0fC', [FController.FLastPlutoTempC, FController.FLastPlutoSocTempC])
|
||||||
|
else if FController.FLastPlutoTempC > TELEMETRY_NONE then
|
||||||
|
S := Format('Temp %.0fC', [FController.FLastPlutoTempC])
|
||||||
|
else if FController.FLastPlutoSocTempC > TELEMETRY_NONE then
|
||||||
|
S := Format('Temp %.0fC', [FController.FLastPlutoSocTempC]);
|
||||||
if FController.FLastPlutoRSSIdB > TELEMETRY_NONE then
|
if FController.FLastPlutoRSSIdB > TELEMETRY_NONE then
|
||||||
begin
|
begin
|
||||||
if S <> '' then S := S + ' ';
|
if S <> '' then S := S + ' ';
|
||||||
|
|||||||
+36
-12
@@ -128,7 +128,7 @@ type
|
|||||||
procedure SetTxAtten(Db: Double); override;
|
procedure SetTxAtten(Db: Double); override;
|
||||||
procedure SendDUCIQ(const IData, QData: array of Integer); override;
|
procedure SendDUCIQ(const IData, QData: array of Integer); override;
|
||||||
procedure ClearDUCIQQueue; override;
|
procedure ClearDUCIQQueue; override;
|
||||||
function ReadTelemetry(out TempC, RSSIdB: Double): Boolean; override;
|
function ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
||||||
@@ -904,19 +904,23 @@ begin
|
|||||||
LeaveCriticalSection(FTxLock);
|
LeaveCriticalSection(FTxLock);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TPlutoBackend.ReadTelemetry(out TempC, RSSIdB: Double): Boolean;
|
function TPlutoBackend.ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean;
|
||||||
// Опрос аппаратной телеметрии ad9361-phy (вызывается контроллером, не из RX-потока):
|
// Опрос аппаратной телеметрии (вызывается контроллером, не из RX-потока):
|
||||||
// temp0/input — температура чипа в миллиградусах C (longlong);
|
// ad9361-phy temp0/input — температура трансивера в миллиградусах C (longlong);
|
||||||
// voltage0/rssi — RSSI входного тракта в дБ (double; строка вида '12.34 dB',
|
// xadc temp0 — температура Zynq SoC/FPGA; сырой raw + offset (longlong)
|
||||||
// libiio парсит ведущее число). Валиден только во время приёма.
|
// + scale (double), C = (raw + offset) * scale / 1000;
|
||||||
|
// ad9361-phy voltage0/rssi — RSSI входного тракта в дБ (double; строка '12.34 dB',
|
||||||
|
// libiio парсит ведущее число). Валиден только во время приёма.
|
||||||
var
|
var
|
||||||
Ch: Piio_channel;
|
Ch, ChX: Piio_channel;
|
||||||
raw: clonglong;
|
XDev: Piio_device;
|
||||||
d: cdouble;
|
raw, offs: clonglong;
|
||||||
|
scale, d: cdouble;
|
||||||
begin
|
begin
|
||||||
TempC := TELEMETRY_NONE;
|
TempC := TELEMETRY_NONE;
|
||||||
RSSIdB := TELEMETRY_NONE;
|
SocTempC := TELEMETRY_NONE;
|
||||||
Result := False;
|
RSSIdB := TELEMETRY_NONE;
|
||||||
|
Result := False;
|
||||||
if (not FConnected) or (FPhy = nil) or (not IIOLoaded) then Exit;
|
if (not FConnected) or (FPhy = nil) or (not IIOLoaded) then Exit;
|
||||||
|
|
||||||
Ch := iio_device_find_channel(FPhy, 'temp0', 0);
|
Ch := iio_device_find_channel(FPhy, 'temp0', 0);
|
||||||
@@ -926,6 +930,26 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Температура Zynq SoC/FPGA — отдельное IIO-устройство 'xadc' (всегда присутствует).
|
||||||
|
// Канал отдаёт сырой код + offset + scale (в отличие от ad9361, дающего милли-°C):
|
||||||
|
// T(°C) = (raw + offset) * scale / 1000.
|
||||||
|
if FCtx <> nil then
|
||||||
|
begin
|
||||||
|
XDev := iio_context_find_device(FCtx, 'xadc');
|
||||||
|
if XDev <> nil then
|
||||||
|
begin
|
||||||
|
ChX := iio_device_find_channel(XDev, 'temp0', 0);
|
||||||
|
if (ChX <> nil) and Assigned(iio_channel_attr_read_double)
|
||||||
|
and (iio_channel_attr_read_longlong(ChX, 'raw', raw) = 0)
|
||||||
|
and (iio_channel_attr_read_longlong(ChX, 'offset', offs) = 0)
|
||||||
|
and (iio_channel_attr_read_double(ChX, 'scale', scale) = 0) then
|
||||||
|
begin
|
||||||
|
SocTempC := (raw + offs) * scale / 1000.0;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
// RSSI имеет смысл только при активном RX-стриме (иначе чтение даёт ошибку/мусор).
|
// RSSI имеет смысл только при активном RX-стриме (иначе чтение даёт ошибку/мусор).
|
||||||
if FStreaming then
|
if FStreaming then
|
||||||
begin
|
begin
|
||||||
|
|||||||
+9
-7
@@ -154,9 +154,10 @@ type
|
|||||||
procedure SetSpeakerAudio(AEnabled: Boolean); virtual;
|
procedure SetSpeakerAudio(AEnabled: Boolean); virtual;
|
||||||
|
|
||||||
// Аппаратная телеметрия, не приходящая в потоке (опрашивается контроллером).
|
// Аппаратная телеметрия, не приходящая в потоке (опрашивается контроллером).
|
||||||
// Pluto/AD936x: температура чипа (°C) и RSSI приёмника (дБ). HPSDR — нет такой
|
// Pluto/AD936x: температура трансивера (TempC) и Zynq SoC/FPGA (SocTempC) в °C,
|
||||||
// (использует HP-status), возвращает False. Недоступные значения = TELEMETRY_NONE.
|
// RSSI приёмника (дБ). HPSDR — нет такой (использует HP-status), возвращает False.
|
||||||
function ReadTelemetry(out TempC, RSSIdB: Double): Boolean; virtual;
|
// Недоступные значения = TELEMETRY_NONE.
|
||||||
|
function ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean; virtual;
|
||||||
|
|
||||||
// ---- Свойства состояния (полиморфные через виртуальные геттеры) ----
|
// ---- Свойства состояния (полиморфные через виртуальные геттеры) ----
|
||||||
property Connected: Boolean read GetConnected;
|
property Connected: Boolean read GetConnected;
|
||||||
@@ -246,11 +247,12 @@ procedure TRadioBackend.SetSpeakerAudio(AEnabled: Boolean);
|
|||||||
begin
|
begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TRadioBackend.ReadTelemetry(out TempC, RSSIdB: Double): Boolean;
|
function TRadioBackend.ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean;
|
||||||
begin
|
begin
|
||||||
TempC := TELEMETRY_NONE;
|
TempC := TELEMETRY_NONE;
|
||||||
RSSIdB := TELEMETRY_NONE;
|
SocTempC := TELEMETRY_NONE;
|
||||||
Result := False;
|
RSSIdB := TELEMETRY_NONE;
|
||||||
|
Result := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TRadioBackend.SetTxAtten(Db: Double);
|
procedure TRadioBackend.SetTxAtten(Db: Double);
|
||||||
|
|||||||
+15
-7
@@ -279,9 +279,10 @@ type
|
|||||||
// ---- Телеметрия (наполняется в Фазе 3/4 вместе с сетевыми колбэками) ----
|
// ---- Телеметрия (наполняется в Фазе 3/4 вместе с сетевыми колбэками) ----
|
||||||
FLastSMeter, FLastFwdW, FLastSWR, FLastSupplyV, FLastSupplyA: Double;
|
FLastSMeter, FLastFwdW, FLastSWR, FLastSupplyV, FLastSupplyA: Double;
|
||||||
FLastPLLLock: Boolean;
|
FLastPLLLock: Boolean;
|
||||||
// Pluto-телеметрия (нет HP-status): температура чипа °C / RSSI приёмника дБ.
|
// Pluto-телеметрия (нет HP-status): температура трансивера °C / Zynq SoC °C /
|
||||||
// Опрашиваются ServicePlutoTelemetry с UI-таймера; TELEMETRY_NONE = нет данных.
|
// RSSI приёмника дБ. Опрашиваются ServicePlutoTelemetry с UI-таймера;
|
||||||
FLastPlutoTempC, FLastPlutoRSSIdB: Double;
|
// TELEMETRY_NONE = нет данных.
|
||||||
|
FLastPlutoTempC, FLastPlutoSocTempC, FLastPlutoRSSIdB: Double;
|
||||||
FPlutoTeleTick: QWord; // GetTickCount64 последнего опроса (троттлинг)
|
FPlutoTeleTick: QWord; // GetTickCount64 последнего опроса (троттлинг)
|
||||||
FPlutoTempBad: Integer; // подряд отброшенных выбросов температуры (анти-залипание)
|
FPlutoTempBad: Integer; // подряд отброшенных выбросов температуры (анти-залипание)
|
||||||
|
|
||||||
@@ -602,7 +603,8 @@ begin
|
|||||||
FFMStepOn := True;
|
FFMStepOn := True;
|
||||||
FLocalAudio := True; // GUI: звук на локальную карту; демон выключит явно
|
FLocalAudio := True; // GUI: звук на локальную карту; демон выключит явно
|
||||||
FLastSMeter := -130; FLastSWR := 1; FLastSupplyV := -1; FLastSupplyA := -1;
|
FLastSMeter := -130; FLastSWR := 1; FLastSupplyV := -1; FLastSupplyA := -1;
|
||||||
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoRSSIdB := TELEMETRY_NONE;
|
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoSocTempC := TELEMETRY_NONE;
|
||||||
|
FLastPlutoRSSIdB := TELEMETRY_NONE;
|
||||||
FPlutoTeleTick := 0; FPlutoTempBad := 0;
|
FPlutoTeleTick := 0; FPlutoTempBad := 0;
|
||||||
FillChar(FDevMAC, SizeOf(FDevMAC), 0);
|
FillChar(FDevMAC, SizeOf(FDevMAC), 0);
|
||||||
end;
|
end;
|
||||||
@@ -2809,7 +2811,8 @@ begin
|
|||||||
end;
|
end;
|
||||||
FRXPacketCount := 0;
|
FRXPacketCount := 0;
|
||||||
FActiveDDC := 0;
|
FActiveDDC := 0;
|
||||||
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoRSSIdB := TELEMETRY_NONE;
|
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoSocTempC := TELEMETRY_NONE;
|
||||||
|
FLastPlutoRSSIdB := TELEMETRY_NONE;
|
||||||
FPlutoTeleTick := 0; FPlutoTempBad := 0;
|
FPlutoTeleTick := 0; FPlutoTempBad := 0;
|
||||||
Changed(rfRunning); // безусловный сброс run-кнопки/MOX.Enabled/sleep (как старый хвост STOP)
|
Changed(rfRunning); // безусловный сброс run-кнопки/MOX.Enabled/sleep (как старый хвост STOP)
|
||||||
end;
|
end;
|
||||||
@@ -3201,14 +3204,14 @@ const
|
|||||||
TEMP_STEP_C = 12.0; // макс. физичный шаг между опросами (тепловая инерция чипа)
|
TEMP_STEP_C = 12.0; // макс. физичный шаг между опросами (тепловая инерция чипа)
|
||||||
TEMP_FORCE = 3; // принять «выброс», если держится N раз подряд (не залипаем)
|
TEMP_FORCE = 3; // принять «выброс», если держится N раз подряд (не залипаем)
|
||||||
var
|
var
|
||||||
t, r: Double;
|
t, s, r: Double;
|
||||||
now_: QWord;
|
now_: QWord;
|
||||||
begin
|
begin
|
||||||
if (FNetwork = nil) or (not FRunning) or (FNetwork.Caps.Kind <> bkPluto) then Exit;
|
if (FNetwork = nil) or (not FRunning) or (FNetwork.Caps.Kind <> bkPluto) then Exit;
|
||||||
now_ := GetTickCount64;
|
now_ := GetTickCount64;
|
||||||
if (FPlutoTeleTick <> 0) and (now_ - FPlutoTeleTick < POLL_MS) then Exit;
|
if (FPlutoTeleTick <> 0) and (now_ - FPlutoTeleTick < POLL_MS) then Exit;
|
||||||
FPlutoTeleTick := now_;
|
FPlutoTeleTick := now_;
|
||||||
if not FNetwork.ReadTelemetry(t, r) then Exit;
|
if not FNetwork.ReadTelemetry(t, s, r) then Exit;
|
||||||
|
|
||||||
// Температура: датчик AD9361 иногда отдаёт мусор (мид-конверсия / контеншн на
|
// Температура: датчик AD9361 иногда отдаёт мусор (мид-конверсия / контеншн на
|
||||||
// USB-контексте с RX-потоком) — наблюдалось редкое -15 °C. Фильтруем: жёсткое
|
// USB-контексте с RX-потоком) — наблюдалось редкое -15 °C. Фильтруем: жёсткое
|
||||||
@@ -3232,6 +3235,11 @@ begin
|
|||||||
// вне окна правдоподобия — молча отбрасываем, держим прошлое валидное значение
|
// вне окна правдоподобия — молча отбрасываем, держим прошлое валидное значение
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Температура Zynq SoC (xadc): стабильный локальный датчик (не подвержен мусору
|
||||||
|
// мид-конверсии AD9361) — достаточно окна правдоподобия без slew-limit.
|
||||||
|
if (s > TELEMETRY_NONE) and (s >= TEMP_MIN_C) and (s <= TEMP_MAX_C) then
|
||||||
|
FLastPlutoSocTempC := s;
|
||||||
|
|
||||||
// RSSI быстро меняется с сигналом — без сглаживания; обновляем, только если валиден.
|
// RSSI быстро меняется с сигналом — без сглаживания; обновляем, только если валиден.
|
||||||
if r > TELEMETRY_NONE then FLastPlutoRSSIdB := r;
|
if r > TELEMETRY_NONE then FLastPlutoRSSIdB := r;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -34,7 +34,8 @@ const
|
|||||||
SEQ_MIN_W = 90;
|
SEQ_MIN_W = 90;
|
||||||
STATUS_ORDER: array[0..7] of Integer = (2, 0, 1, 3, 7, 4, 5, 6);
|
STATUS_ORDER: array[0..7] of Integer = (2, 0, 1, 3, 7, 4, 5, 6);
|
||||||
// поле 7: PLL (openHPSDR) ИЛИ статус QO-100 beacon lock (Pluto) — шире под "BCN LOCK …".
|
// поле 7: PLL (openHPSDR) ИЛИ статус QO-100 beacon lock (Pluto) — шире под "BCN LOCK …".
|
||||||
BASE_W: array[0..7] of Integer = (280, 160, 170, 150, 190, 180, 130, 200);
|
// поле 3: Supply (HPSDR) ИЛИ две температуры Pluto "Temp 37/53C RSSI -89dB" — шире.
|
||||||
|
BASE_W: array[0..7] of Integer = (280, 160, 170, 190, 190, 180, 130, 200);
|
||||||
INITIAL_TEXT: array[0..7] of string = (
|
INITIAL_TEXT: array[0..7] of string = (
|
||||||
'Board --',
|
'Board --',
|
||||||
'IP --',
|
'IP --',
|
||||||
|
|||||||
+9
-3
@@ -460,10 +460,16 @@ begin
|
|||||||
|
|
||||||
if FController.IsPluto then
|
if FController.IsPluto then
|
||||||
begin
|
begin
|
||||||
// Pluto: нет PA/supply — температура чипа + RSSI приёмника (telemetry-поле).
|
// Pluto: нет PA/supply — температуры (трансивер/SoC) + RSSI (telemetry-поле).
|
||||||
|
// Temp X/YC: X — трансивер AD9361, Y — Zynq SoC/FPGA (xadc).
|
||||||
SupplyText := '';
|
SupplyText := '';
|
||||||
if FController.FLastPlutoTempC > TELEMETRY_NONE then
|
if (FController.FLastPlutoTempC > TELEMETRY_NONE)
|
||||||
SupplyText := Format('Temp %.0fC', [FController.FLastPlutoTempC]);
|
and (FController.FLastPlutoSocTempC > TELEMETRY_NONE) then
|
||||||
|
SupplyText := Format('Temp %.0f/%.0fC', [FController.FLastPlutoTempC, FController.FLastPlutoSocTempC])
|
||||||
|
else if FController.FLastPlutoTempC > TELEMETRY_NONE then
|
||||||
|
SupplyText := Format('Temp %.0fC', [FController.FLastPlutoTempC])
|
||||||
|
else if FController.FLastPlutoSocTempC > TELEMETRY_NONE then
|
||||||
|
SupplyText := Format('Temp %.0fC', [FController.FLastPlutoSocTempC]);
|
||||||
if FController.FLastPlutoRSSIdB > TELEMETRY_NONE then
|
if FController.FLastPlutoRSSIdB > TELEMETRY_NONE then
|
||||||
begin
|
begin
|
||||||
if SupplyText <> '' then SupplyText := SupplyText + ' ';
|
if SupplyText <> '' then SupplyText := SupplyText + ' ';
|
||||||
|
|||||||
Reference in New Issue
Block a user