mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
582 lines
22 KiB
ObjectPascal
582 lines
22 KiB
ObjectPascal
unit WebAdapter;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
// Адаптер веб-интерфейса: мост между TWebServer (входящие команды из WS-потока)
|
|
// и TRadioController (ядро). Раньше эти WebOnXxx/SyncWebXxx жили в TMainForm —
|
|
// вынесены сюда, чтобы развязать UI от веба и подготовить headless-демон.
|
|
//
|
|
// Маршалинг: входящие команды приходят из потока веб-сервера; выполняем их в
|
|
// потоке контроллера через FController.Invoke (GUI = TThread.Synchronize; демон
|
|
// подставит свою очередь). Synchronize блокирующий и последовательный — поля
|
|
// FSync* для передачи параметров безопасны (гонок нет).
|
|
//
|
|
// Часть команд (тюнинг VFO/диапазон/MOX/...) идёт через UI-обёртки MainForm,
|
|
// которые ещё не headless — для них есть IWebHost (его реализует хозяин). По мере
|
|
// переноса оркестрации в контроллер интерфейс будет сужаться.
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
RadioController, WebServer, WDSPEngine, HPSDRNetwork, HPSDRProtocol, RadioBackend, BoardUtils, Settings;
|
|
|
|
type
|
|
// UI/оркестрационные операции, которые адаптер дёргает у хозяина (пока не
|
|
// headless — десктоп: методы MainForm; демон: своя реализация). Наполняется
|
|
// в WA2 по мере переноса host-зависимых хендлеров.
|
|
IWebHost = interface
|
|
['{B3A1F7C2-9D54-4E18-9C6B-2E5A7F0D1A33}']
|
|
end;
|
|
|
|
{ TWebAdapter }
|
|
TWebAdapter = class
|
|
private
|
|
FController: TRadioController;
|
|
FServer: TWebServer;
|
|
FHost: IWebHost;
|
|
// Поля для передачи параметров команды в Sync-метод (через Invoke).
|
|
FSyncInt: Integer;
|
|
FSyncBool: Boolean;
|
|
FSyncFreq: Double;
|
|
// Sync-методы (выполняются в потоке контроллера через Invoke).
|
|
procedure SyncMode;
|
|
procedure SyncFilter;
|
|
procedure SyncAGC;
|
|
procedure SyncAGCTop;
|
|
procedure SyncVolume;
|
|
procedure SyncDrive;
|
|
procedure SyncSpan;
|
|
procedure SyncBand;
|
|
procedure SyncXvtrBand;
|
|
procedure SyncWfAGC;
|
|
procedure SyncWfNF;
|
|
procedure SyncMute;
|
|
procedure SyncNR;
|
|
procedure SyncNB;
|
|
procedure SyncSNB;
|
|
procedure SyncANF;
|
|
procedure SyncFreq;
|
|
procedure SyncFreqA;
|
|
procedure SyncFreqB;
|
|
procedure SyncCenter;
|
|
procedure SyncAttn;
|
|
procedure SyncActiveVfo;
|
|
procedure SyncCtun;
|
|
procedure SyncMOX;
|
|
procedure SyncTun;
|
|
procedure SyncFMStep;
|
|
procedure SyncBeacon;
|
|
procedure SyncBeaconSeed;
|
|
procedure SyncRxGainMode;
|
|
procedure SyncRxGain;
|
|
procedure SyncRxMute;
|
|
function BeaconStatusText: string; // компактный статус (как десктоп поле 7)
|
|
public
|
|
constructor Create(AController: TRadioController; AServer: TWebServer;
|
|
AHost: IWebHost);
|
|
|
|
// Колбэки веб-сервера (навешиваются хозяином на FServer.OnXxx).
|
|
procedure OnMode(Mode: Integer);
|
|
procedure OnFilter(BW: Integer);
|
|
procedure OnAGC(Mode: Integer);
|
|
procedure OnAGCTop(DB: Integer);
|
|
procedure OnVolume(V: Integer);
|
|
procedure OnDrive(V: Integer);
|
|
procedure OnSpan(Hz: Integer);
|
|
procedure OnBand(Idx: Integer);
|
|
procedure OnXvtrBand(Idx: Integer);
|
|
procedure OnWfAGC(On_: Boolean);
|
|
procedure OnWfNF(On_: Boolean);
|
|
procedure OnMute(On_: Boolean);
|
|
procedure OnNR(Mode: Integer);
|
|
procedure OnNB(Mode: Integer);
|
|
procedure OnSNB(On_: Boolean);
|
|
procedure OnANF(On_: Boolean);
|
|
procedure OnFreq(Hz: Double);
|
|
procedure OnFreqA(Hz: Double);
|
|
procedure OnFreqB(Hz: Double);
|
|
procedure OnCenter(Hz: Double);
|
|
procedure OnAttn(Idx: Integer);
|
|
procedure OnActiveVfo(Idx: Integer);
|
|
procedure OnCtun(On_: Boolean);
|
|
procedure OnMOX(On_: Boolean);
|
|
procedure OnTun(On_: Boolean);
|
|
procedure OnFMStep(Idx: Integer);
|
|
// QO-100 beacon: вкл/выкл лок и наведение по клику в спектре (frac 0..1).
|
|
procedure OnBeacon(On_: Boolean);
|
|
procedure OnBeaconSeed(Frac: Double);
|
|
procedure OnRxGainMode(Mode: Integer);
|
|
procedure OnRxGain(Db: Integer);
|
|
procedure OnRxMute(On_: Boolean);
|
|
procedure OnClientActiveChanged(Active: Boolean);
|
|
// TX-mic от web-клиента → WDSP. Вызывается из WS-потока; без маршалинга —
|
|
// подача в движок thread-safe (как OnMicPacket/OnDDCIQ контроллера).
|
|
procedure OnMic(Samples: PSingle; Count: Integer);
|
|
|
|
// RX-аудио-перехват: навешивается на FController.OnAudioConsume. Если есть
|
|
// активный web-клиент — сводим в моно и шлём ему, возвращаем True (локальный
|
|
// AudioOut пропускается контроллером). Вызывается из DSP-потока.
|
|
function AudioConsume(const Left, Right: array of Single;
|
|
Count: Integer): Boolean;
|
|
|
|
// Исходящее зеркало: периодический снимок состояния + спектр/водопад в web.
|
|
// Зовётся хозяином из GUI-таймера; всё (включая последний кадр спектра/
|
|
// водопада) читается из FController. Сервер сам гейтит по FRunning и шлёт
|
|
// активным клиентам.
|
|
procedure PushState;
|
|
end;
|
|
|
|
implementation
|
|
|
|
constructor TWebAdapter.Create(AController: TRadioController;
|
|
AServer: TWebServer; AHost: IWebHost);
|
|
begin
|
|
inherited Create;
|
|
FController := AController;
|
|
FServer := AServer;
|
|
FHost := AHost;
|
|
end;
|
|
|
|
// ── Входящие команды: запоминаем параметр, выполняем в потоке контроллера ─────
|
|
|
|
procedure TWebAdapter.OnMode(Mode: Integer);
|
|
begin FSyncInt := Mode; FController.Invoke(@SyncMode); end;
|
|
|
|
procedure TWebAdapter.SyncMode;
|
|
begin
|
|
if (FSyncInt < 0) or (FSyncInt > MODE_DMR) then Exit;
|
|
FController.SetMode(FSyncInt); // дефолт фильтра + DSP + cache; рендер через rfMode
|
|
end;
|
|
|
|
procedure TWebAdapter.OnFilter(BW: Integer);
|
|
begin FSyncInt := BW; FController.Invoke(@SyncFilter); end;
|
|
|
|
procedure TWebAdapter.SyncFilter;
|
|
begin
|
|
// Web-клиент шлёт полосу в Гц (кнопки панели фильтров = пресетные значения);
|
|
// команда применяет её и подсвечивает ближайший пресет (рендер — rfFilter).
|
|
FController.SetFilterBW(FSyncInt);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnAGC(Mode: Integer);
|
|
begin FSyncInt := Mode; FController.Invoke(@SyncAGC); end;
|
|
|
|
procedure TWebAdapter.SyncAGC;
|
|
begin
|
|
FController.SetAGCMode(FSyncInt); // единый маппинг (исправляет старый web-баг)
|
|
end;
|
|
|
|
procedure TWebAdapter.OnAGCTop(DB: Integer);
|
|
begin FSyncInt := DB; FController.Invoke(@SyncAGCTop); end;
|
|
|
|
procedure TWebAdapter.SyncAGCTop;
|
|
begin
|
|
FController.SetAGCTop(FSyncInt); // клампит, применяет, OnControllerState обновит UI
|
|
end;
|
|
|
|
procedure TWebAdapter.OnVolume(V: Integer);
|
|
begin FSyncInt := V; FController.Invoke(@SyncVolume); end;
|
|
|
|
procedure TWebAdapter.SyncVolume;
|
|
begin FController.SetVolume(FSyncInt); end;
|
|
|
|
procedure TWebAdapter.OnDrive(V: Integer);
|
|
begin FSyncInt := V; FController.Invoke(@SyncDrive); end;
|
|
|
|
procedure TWebAdapter.SyncDrive;
|
|
begin FController.SetDrive(FSyncInt); end; // rfDrive снимет память канала (ChannelController)
|
|
|
|
procedure TWebAdapter.OnBand(Idx: Integer);
|
|
begin FSyncInt := Idx; FController.Invoke(@SyncBand); end;
|
|
|
|
procedure TWebAdapter.SyncBand;
|
|
begin FController.SetBand(FSyncInt); end; // XVTR-выход + persist + restore — в команде
|
|
|
|
procedure TWebAdapter.OnXvtrBand(Idx: Integer);
|
|
begin FSyncInt := Idx; FController.Invoke(@SyncXvtrBand); end;
|
|
|
|
procedure TWebAdapter.SyncXvtrBand;
|
|
begin
|
|
if FSyncInt < 0 then begin FController.SetXvtrBand(-1); Exit; end;
|
|
if FSyncInt >= CFG_XVTR_COUNT then Exit;
|
|
if not FController.FXvtrSettings.Entries[FSyncInt].Enabled then Exit;
|
|
if FSyncInt = FController.FCurrentXvtr then Exit;
|
|
if FController.FDevConnected then
|
|
begin FController.SaveCurrentBand; FController.FSettings.Save; end;
|
|
FController.SetXvtrBand(FSyncInt); // ActivateXvtr (рендер/wideband/web — rfXvtr)
|
|
end;
|
|
|
|
procedure TWebAdapter.OnSpan(Hz: Integer);
|
|
begin FSyncInt := Hz; FController.Invoke(@SyncSpan); end;
|
|
|
|
procedure TWebAdapter.SyncSpan;
|
|
// span ≡ sample rate. Валидируем по пресетам текущего бэкенда (единый источник,
|
|
// как десктоп-оверлей) — иначе Pluto-рейты (960k/2304k/…) отбрасывались бы.
|
|
// SetSampleRate сам пересоздаёт WDSP-канал + персистит (idempotent при том же rate).
|
|
var
|
|
Presets: TBackendRateArray;
|
|
i: Integer;
|
|
begin
|
|
Presets := FController.SampleRatePresets;
|
|
for i := 0 to High(Presets) do
|
|
if Presets[i] = FSyncInt then
|
|
begin
|
|
FController.SetSampleRate(FSyncInt);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TWebAdapter.OnWfAGC(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncWfAGC); end;
|
|
|
|
procedure TWebAdapter.SyncWfAGC;
|
|
begin FController.SetWfAGC(FSyncBool); end;
|
|
|
|
procedure TWebAdapter.OnWfNF(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncWfNF); end;
|
|
|
|
procedure TWebAdapter.SyncWfNF;
|
|
begin FController.SetWfNF(FSyncBool); end;
|
|
|
|
procedure TWebAdapter.OnMute(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncMute); end;
|
|
|
|
procedure TWebAdapter.SyncMute;
|
|
begin FController.SetMute(FSyncBool); end; // идемпотентно; OnControllerState обновит UI
|
|
|
|
procedure TWebAdapter.OnNR(Mode: Integer);
|
|
begin FSyncInt := Mode; FController.Invoke(@SyncNR); end;
|
|
|
|
procedure TWebAdapter.SyncNR;
|
|
begin FController.SetNR(FSyncInt); end; // клампит и применяет; рендер через rfNR
|
|
|
|
procedure TWebAdapter.OnNB(Mode: Integer);
|
|
begin FSyncInt := Mode; FController.Invoke(@SyncNB); end;
|
|
|
|
procedure TWebAdapter.SyncNB;
|
|
begin FController.SetNB(FSyncInt); end;
|
|
|
|
procedure TWebAdapter.OnSNB(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncSNB); end;
|
|
|
|
procedure TWebAdapter.SyncSNB;
|
|
begin FController.SetSNB(FSyncBool); end;
|
|
|
|
procedure TWebAdapter.OnANF(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncANF); end;
|
|
|
|
procedure TWebAdapter.SyncANF;
|
|
begin FController.SetANF(FSyncBool); end;
|
|
|
|
procedure TWebAdapter.OnFreq(Hz: Double);
|
|
begin FSyncFreq := Hz; FController.Invoke(@SyncFreq); end;
|
|
|
|
procedure TWebAdapter.SyncFreq;
|
|
begin
|
|
// "freq" для АКТИВНОГО VFO. SetVfoA/SetVfoB несут CTUN/band-detect/сеть +
|
|
// канальную оркестрацию (через OnAfterTune-хук); рендер — rfVfoA/rfVfoB.
|
|
if FController.FActiveVfo = 0 then FController.SetVfoA(FSyncFreq)
|
|
else FController.SetVfoB(FSyncFreq);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnFreqA(Hz: Double);
|
|
begin FSyncFreq := Hz; FController.Invoke(@SyncFreqA); end;
|
|
|
|
procedure TWebAdapter.SyncFreqA;
|
|
begin FController.SetVfoA(FSyncFreq); end;
|
|
|
|
procedure TWebAdapter.OnFreqB(Hz: Double);
|
|
begin FSyncFreq := Hz; FController.Invoke(@SyncFreqB); end;
|
|
|
|
procedure TWebAdapter.SyncFreqB;
|
|
begin
|
|
// Унифицировано с десктопом: команда хранит частоту, при активном B
|
|
// перестраивает приёмник (CTUN-aware) + сеть; рендер — через rfVfoB/rfBand.
|
|
FController.SetVfoB(FSyncFreq);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnCenter(Hz: Double);
|
|
begin FSyncFreq := Hz; FController.Invoke(@SyncCenter); end;
|
|
|
|
procedure TWebAdapter.SyncCenter;
|
|
begin
|
|
// Сдвигаем DDC-центр без смены VFO (аналог CTUN-drag в десктопе).
|
|
FController.SetCenter(FSyncFreq);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnAttn(Idx: Integer);
|
|
begin FSyncInt := Idx; FController.Invoke(@SyncAttn); end;
|
|
|
|
procedure TWebAdapter.SyncAttn;
|
|
begin FController.SetAtten(FSyncInt); end;
|
|
|
|
procedure TWebAdapter.OnActiveVfo(Idx: Integer);
|
|
begin FSyncInt := Idx; FController.Invoke(@SyncActiveVfo); end;
|
|
|
|
procedure TWebAdapter.SyncActiveVfo;
|
|
begin
|
|
if (FSyncInt < 0) or (FSyncInt > 1) then Exit;
|
|
if FSyncInt = FController.FActiveVfo then Exit;
|
|
FController.SetActiveVfo(FSyncInt); // перестройка RX-VFO; рендер rfActiveVfo
|
|
end;
|
|
|
|
procedure TWebAdapter.OnCtun(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncCtun); end;
|
|
|
|
procedure TWebAdapter.SyncCtun;
|
|
begin
|
|
if FSyncBool <> FController.FCTun then
|
|
FController.SetCTun(FSyncBool); // band cache + center/shift + сеть; рендер rfCTun
|
|
end;
|
|
|
|
procedure TWebAdapter.OnMOX(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncMOX); end;
|
|
|
|
procedure TWebAdapter.SyncMOX;
|
|
begin
|
|
// FWebClientActive держится актуальным OnClientActiveChanged — выбор mic-source.
|
|
FController.SetMOX(FSyncBool); // ядро TX (safety/PTT/DSP); рендер rfTransmitting
|
|
end;
|
|
|
|
procedure TWebAdapter.OnTun(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncTun); end;
|
|
|
|
procedure TWebAdapter.SyncTun;
|
|
begin
|
|
FController.SetTune(FSyncBool); // ядро TUN (safety/тон/drive/MOX); рендер rfTuning
|
|
end;
|
|
|
|
procedure TWebAdapter.OnFMStep(Idx: Integer);
|
|
begin FSyncInt := Idx; FController.Invoke(@SyncFMStep); end;
|
|
|
|
procedure TWebAdapter.SyncFMStep;
|
|
begin
|
|
FController.SetFMStep(FSyncInt); // клампит 0..3 внутри; рендер rfFMStepIdx
|
|
FController.SaveCurrentBand;
|
|
end;
|
|
|
|
procedure TWebAdapter.OnBeacon(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncBeacon); end;
|
|
|
|
procedure TWebAdapter.SyncBeacon;
|
|
begin
|
|
// Вкл/выкл лок маяка (= запуск декодера + трим LOError). Идемпотентно.
|
|
FController.SetBeaconLock(FSyncBool);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnBeaconSeed(Frac: Double);
|
|
begin FSyncFreq := Frac; FController.Invoke(@SyncBeaconSeed); end;
|
|
|
|
procedure TWebAdapter.SyncBeaconSeed;
|
|
var Hz, VC, VS: Double;
|
|
begin
|
|
// Frac (доля 0..1 по ширине спектра) → абс. display-Hz от ЖИВОГО видимого окна
|
|
// контроллера (зеркалит десктоп PixelToFreq с учётом зума). Так наведение
|
|
// точно, даже когда LO ретюнится локом и клиентский center отстаёт.
|
|
if (FSyncFreq < 0) or (FSyncFreq > 1) then Exit;
|
|
FController.GetViewWindow(VC, VS);
|
|
Hz := VC + (FSyncFreq - 0.5) * VS;
|
|
FController.BeaconSeedAtHz(Hz);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnRxGainMode(Mode: Integer);
|
|
begin FSyncInt := Mode; FController.Invoke(@SyncRxGainMode); end;
|
|
|
|
procedure TWebAdapter.SyncRxGainMode;
|
|
begin
|
|
// Pluto hw-AGC: 0=manual,1=fast,2=slow,3=hybrid (рендер через rfRxGain).
|
|
FController.SetRxGainMode(FSyncInt);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnRxGain(Db: Integer);
|
|
begin FSyncInt := Db; FController.Invoke(@SyncRxGain); end;
|
|
|
|
procedure TWebAdapter.SyncRxGain;
|
|
begin
|
|
// Ручной hw-gain (0..73 дБ); SetRxGain переводит режим в manual.
|
|
FController.SetRxGain(FSyncInt);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnRxMute(On_: Boolean);
|
|
begin FSyncBool := On_; FController.Invoke(@SyncRxMute); end;
|
|
|
|
procedure TWebAdapter.SyncRxMute;
|
|
begin
|
|
// QO-100 self-monitor: RX-аудио на TX (toggle, рендер через rfRxMuteOnTx).
|
|
FController.SetRxMuteOnTx(FSyncBool);
|
|
end;
|
|
|
|
procedure TWebAdapter.OnClientActiveChanged(Active: Boolean);
|
|
// Из потока веб-сервера при connect/disconnect клиента. Простая запись флага —
|
|
// ядро (SetMOX, в т.ч. HWPTT-путь) выбирает по нему mic-source. Без маршалинга.
|
|
begin
|
|
FController.FWebClientActive := Active;
|
|
end;
|
|
|
|
procedure TWebAdapter.OnMic(Samples: PSingle; Count: Integer);
|
|
var
|
|
Buf: array[0..5759] of Double;
|
|
SP: PSingle;
|
|
i, n: Integer;
|
|
begin
|
|
if not FController.FWDSPReady then Exit;
|
|
n := Count;
|
|
if n > Length(Buf) then n := Length(Buf);
|
|
SP := Samples;
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
Buf[i] := SP^;
|
|
Inc(SP);
|
|
end;
|
|
FController.FDSPEngine.PushTXMicSamplesD(Buf, n);
|
|
end;
|
|
|
|
function TWebAdapter.AudioConsume(const Left, Right: array of Single;
|
|
Count: Integer): Boolean;
|
|
var
|
|
MonoBuf: array[0..1023] of Single;
|
|
i, n: Integer;
|
|
begin
|
|
Result := False;
|
|
if not (Assigned(FServer) and FServer.WebClientActive) then Exit;
|
|
n := Count;
|
|
if n > Length(MonoBuf) then n := Length(MonoBuf);
|
|
for i := 0 to n - 1 do
|
|
MonoBuf[i] := (Left[i] + Right[i]) * 0.5;
|
|
FServer.PushAudio(@MonoBuf[0], n);
|
|
Result := True;
|
|
end;
|
|
|
|
// ── Исходящее зеркало состояния ──────────────────────────────────────────────
|
|
|
|
function TWebAdapter.BeaconStatusText: string;
|
|
// Зеркалит TMainForm.BeaconStatusText (поле 7 десктопа). Pluto в трансвертере.
|
|
begin
|
|
if not FController.BeaconLockEnabled then Exit('BCN: off');
|
|
if FController.BeaconDecodeFreqHz <= 0 then Exit('BCN: click beacon');
|
|
case FController.BeaconState of
|
|
bsLock: Result := Format('BCN LOCK %.0fHz /%.0fdB',
|
|
[FController.BeaconCorrectionHz, FController.BeaconSNRdB]);
|
|
bsSearch: Result := Format('BCN sync /%.0fdB', [FController.BeaconSNRdB]);
|
|
else Result := 'BCN --';
|
|
end;
|
|
end;
|
|
|
|
procedure TWebAdapter.PushState;
|
|
var
|
|
StatusText, BoardText, IPText, SupplyText: string;
|
|
PLLText, RXText, TXText, SeqText: string;
|
|
ViewCenter, ViewSpan: Double;
|
|
begin
|
|
// Видимое окно (центр+span) с учётом зума: пиксели спектра уже зумлены
|
|
// анализатором, web-метки/маппинг должны соответствовать.
|
|
FController.GetViewWindow(ViewCenter, ViewSpan);
|
|
if FController.FNetwork.Connected then
|
|
begin
|
|
if FController.FRunning then StatusText := 'Running'
|
|
else StatusText := 'Connected';
|
|
BoardText := 'Board: ' + FController.BoardDisplayName;
|
|
IPText := 'IP: ' + FController.FNetwork.Device.IPAddress;
|
|
end
|
|
else
|
|
begin
|
|
StatusText := 'Disconnected';
|
|
BoardText := 'Board --';
|
|
IPText := 'IP --';
|
|
end;
|
|
|
|
if not FController.FRunning then
|
|
begin
|
|
SupplyText := 'Supply --';
|
|
PLLText := 'PLL --';
|
|
end
|
|
else
|
|
begin
|
|
if FController.FLastPLLLock then PLLText := 'PLL OK'
|
|
else PLLText := 'PLL?';
|
|
|
|
if FController.IsPluto then
|
|
begin
|
|
// Pluto: нет PA/supply — температуры (трансивер/SoC) + RSSI (telemetry-поле).
|
|
// Temp X/YC: X — трансивер AD9361, Y — Zynq SoC/FPGA (xadc).
|
|
SupplyText := '';
|
|
if (FController.FLastPlutoTempC > TELEMETRY_NONE)
|
|
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
|
|
begin
|
|
if SupplyText <> '' then SupplyText := SupplyText + ' ';
|
|
SupplyText := SupplyText + Format('RSSI %.0fdB', [FController.FLastPlutoRSSIdB]);
|
|
end;
|
|
if SupplyText = '' then SupplyText := 'Pluto --';
|
|
end
|
|
else if not BoardSupportsSupplyVoltage(FController.FNetwork.Device.BoardType) then
|
|
SupplyText := 'Supply n/a'
|
|
else if FController.FLastSupplyV >= 0 then
|
|
begin
|
|
if FController.FLastSupplyA >= 0 then
|
|
SupplyText := Format('Supply %.1fV %.1fA', [FController.FLastSupplyV, FController.FLastSupplyA])
|
|
else
|
|
SupplyText := Format('Supply %.1fV', [FController.FLastSupplyV]);
|
|
end
|
|
else
|
|
SupplyText := 'Supply --';
|
|
end;
|
|
|
|
if FController.FRunning then RXText := 'RX running'
|
|
else RXText := 'RX idle';
|
|
|
|
if FController.FTuning then TXText := 'TX tune'
|
|
else if FController.FTransmitting then TXText := 'TX active'
|
|
else TXText := 'TX idle';
|
|
|
|
if FController.FSeqErrorCount > 0 then
|
|
SeqText := Format('SEQ ERR DDC%d', [FController.FLastSeqErrorDDC])
|
|
else if FController.FRunning then
|
|
SeqText := 'SEQ OK'
|
|
else
|
|
SeqText := 'SEQ --';
|
|
|
|
// QO-100 beacon lock — отдельный канал состояния (видим только на Pluto в XVTR).
|
|
FServer.SetBeaconStatus(
|
|
FController.IsPluto and (FController.FCurrentXvtr >= 0),
|
|
FController.BeaconLockEnabled,
|
|
BeaconStatusText,
|
|
FController.BeaconRefHz,
|
|
FController.BeaconTrackedFreqHz);
|
|
|
|
// Pluto RF-AGC (виден при HasHWGain = Pluto подключён) + QO-100 RX MUTE (InQO100).
|
|
FServer.SetRxGainStatus(
|
|
FController.BackendCaps.HasHWGain,
|
|
FController.FRxGainMode, FController.FRxGainDb);
|
|
FServer.SetRxMuteStatus(
|
|
FController.InQO100,
|
|
FController.FRxMuteOnTx);
|
|
|
|
FServer.PushSpectrum(
|
|
FController.FSpectrumBuf, 1024,
|
|
FController.FWaterfallBuf,
|
|
FController.FLastSMeter,
|
|
FController.FVfoA, FController.FMode, FController.FFilterBW, FController.FAGCMode, FController.FAGCTop,
|
|
ViewSpan, FController.ActiveVolume,
|
|
FController.FWfAGCEnabled, FController.FWfNFEnabled,
|
|
FController.FCurrentBand,
|
|
FController.FRunning and FController.FNetwork.Connected,
|
|
FController.FRunning, FController.FMuted, FController.FCTun,
|
|
FController.FNRMode, FController.FNBMode, FController.FSNB, FController.FANF,
|
|
ViewCenter, FController.FFilter,
|
|
FController.FVfoB, FController.FActiveVfo,
|
|
FController.FTransmitting, FController.FDrivePercent,
|
|
FController.FAtten, FController.FTuning, FController.FDisplayDuplex,
|
|
FController.FLastFwdW, FController.FLastSWR, FController.FPAMaxPower,
|
|
StatusText, BoardText, IPText, SupplyText,
|
|
PLLText, RXText, TXText, SeqText);
|
|
end;
|
|
|
|
end.
|