mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat: Pluto/AD936x SDR backend (discovery, RX, VHF band plan)
Add ADALM-Pluto / AD9361 support as a second hardware backend alongside openHPSDR, sharing the WDSP DSP pipeline and the existing controller API (UI stays decoupled from logic). - RadioBackend.pas: abstract TRadioBackend + TBackendCaps + TRadioDevice (Kind/URI/Serial). THPSDRNetwork now derives from it (state via virtual getters); TRadioController.FNetwork is the base type. - IIOBindings.pas: dynamic libiio loader (runs without libiio present). - PlutoBackend.pas: scan/probe-by-URI, connect, LO/rate/bandwidth/gain control, RX streaming thread (int16->24bit BE -> OnDDCIQ), Q conjugated to match WDSP IQ convention. Verified on LibreSDR (AD9361) over network. - Unified discovery: TDiscoverThread scans both backends; network Plutos found via direct ProbeURI (no mDNS needed). ConnectDevice dispatches by Dev.Kind (EnsureBackend swaps backend, preserving callbacks). - DeviceStore/DeviceForm: persist Kind/URI/Serial; save Pluto via AddSavedPluto so saved/autostart devices reconnect across restarts. - VHF/UHF band plan (BoardUtils, kind-aware): 6m..ADS-B for Pluto; fixes HF clamps (band detect, mouse-wheel 60 MHz cap, freq-display max). - SampleRateOverlay: configurable presets (Pluto 576k..5760k, >520 ksps), auto-width to fit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+48
-19
@@ -22,6 +22,11 @@ type
|
||||
TSampleRateOverlay = class(TComponent)
|
||||
private
|
||||
FCurrentRate: Integer;
|
||||
// Набор пресетов (зависит от бэкенда): HPSDR 48k..1536k или Pluto >560k.
|
||||
// До 10 слотов (Pluto может иметь 8: 576k..5760k).
|
||||
FRates: array[0..9] of Integer;
|
||||
FNames: array[0..9] of string;
|
||||
FRateCount: Integer;
|
||||
FPanelHidden: Boolean;
|
||||
FOnSpanSelect: TSpanSelectEvent;
|
||||
FOnHidePanel: THidePanelEvent;
|
||||
@@ -31,8 +36,8 @@ type
|
||||
FWidth: Integer;
|
||||
FHeight: Integer;
|
||||
|
||||
FHitRects: array[0..6] of TRect; // [0]=hide button, [1..6]=span buttons
|
||||
FHitValues: array[0..6] of Integer; // 0=hide, else sample rate
|
||||
FHitRects: array[0..10] of TRect; // [0]=hide button, [1..]=span buttons
|
||||
FHitValues: array[0..10] of Integer; // 0=hide, else sample rate
|
||||
FHitCount: Integer;
|
||||
FHotIdx: Integer;
|
||||
|
||||
@@ -67,6 +72,9 @@ type
|
||||
function HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||
function HandleMouseLeave: Boolean;
|
||||
procedure SetCurrentRate(Rate: Integer);
|
||||
// Задаёт пресеты (Гц, до 6 шт.); имена формируются как '<kHz>k'.
|
||||
procedure SetRatePresets(const Rates: array of Integer);
|
||||
procedure SetRatePresetsDefault; // HPSDR 48k..1536k
|
||||
procedure SetPanelHidden(Hidden: Boolean);
|
||||
property Left: Integer read FLeft;
|
||||
property Top: Integer read FTop;
|
||||
@@ -139,14 +147,15 @@ begin
|
||||
C.Font.Name := 'Courier New';
|
||||
C.Font.Size := 7;
|
||||
C.Font.Style := [];
|
||||
Result := C.TextWidth(Name) + 10;
|
||||
if Result < 36 then Result := 36;
|
||||
Result := C.TextWidth(Name) + 8;
|
||||
if Result < 30 then Result := 30;
|
||||
end;
|
||||
|
||||
constructor TSampleRateOverlay.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FCurrentRate := 192000;
|
||||
SetRatePresetsDefault;
|
||||
FPanelHidden := False;
|
||||
FHitCount := 0;
|
||||
FHotIdx := -1;
|
||||
@@ -207,10 +216,10 @@ begin
|
||||
TH := C.TextHeight('A');
|
||||
|
||||
ContentW := PAD_X + HIDE_W + BTN_GAP;
|
||||
for I := 0 to High(SPAN_NAMES) do
|
||||
for I := 0 to FRateCount-1 do
|
||||
begin
|
||||
Inc(ContentW, SpanBtnWidth(SPAN_NAMES[I], C));
|
||||
if I < High(SPAN_NAMES) then Inc(ContentW, BTN_GAP);
|
||||
Inc(ContentW, SpanBtnWidth(FNames[I], C));
|
||||
if I < FRateCount-1 then Inc(ContentW, BTN_GAP);
|
||||
end;
|
||||
Inc(ContentW, PAD_X);
|
||||
if ContentW > W then ContentW := W;
|
||||
@@ -247,11 +256,11 @@ begin
|
||||
RegisterHit(HR, 0);
|
||||
Inc(X, HIDE_W + BTN_GAP);
|
||||
|
||||
for I := 0 to High(SPAN_RATES) do
|
||||
for I := 0 to FRateCount-1 do
|
||||
begin
|
||||
BtnW := SpanBtnWidth(SPAN_NAMES[I], C);
|
||||
BtnW := SpanBtnWidth(FNames[I], C);
|
||||
HR := Rect(X, OriginY + PAD_Y, X + BtnW, OriginY + PAD_Y + SPAN_H);
|
||||
IsAct := SPAN_RATES[I] = FCurrentRate;
|
||||
IsAct := FRates[I] = FCurrentRate;
|
||||
IsHot := FHotIdx = I + 1;
|
||||
|
||||
if IsAct then BlendRect(Target, HR, $20, $5A, $78, 220)
|
||||
@@ -262,13 +271,13 @@ begin
|
||||
C.Brush.Style := bsClear;
|
||||
C.Rectangle(HR);
|
||||
|
||||
TW := C.TextWidth(SPAN_NAMES[I]);
|
||||
TW := C.TextWidth(FNames[I]);
|
||||
C.Font.Color := CLR_TEXT;
|
||||
C.Brush.Style := bsClear;
|
||||
C.TextOut(HR.Left + (BtnW - TW) div 2,
|
||||
HR.Top + (SPAN_H - TH) div 2, SPAN_NAMES[I]);
|
||||
HR.Top + (SPAN_H - TH) div 2, FNames[I]);
|
||||
|
||||
RegisterHit(HR, SPAN_RATES[I]);
|
||||
RegisterHit(HR, FRates[I]);
|
||||
Inc(X, BtnW + BTN_GAP);
|
||||
end;
|
||||
end;
|
||||
@@ -278,10 +287,10 @@ var I, CW: Integer;
|
||||
begin
|
||||
C.Font.Name := 'Courier New'; C.Font.Size := 7; C.Font.Style := [];
|
||||
CW := PAD_X + HIDE_W + BTN_GAP;
|
||||
for I := 0 to High(SPAN_NAMES) do
|
||||
for I := 0 to FRateCount-1 do
|
||||
begin
|
||||
Inc(CW, SpanBtnWidth(SPAN_NAMES[I], C));
|
||||
if I < High(SPAN_NAMES) then Inc(CW, BTN_GAP);
|
||||
Inc(CW, SpanBtnWidth(FNames[I], C));
|
||||
if I < FRateCount-1 then Inc(CW, BTN_GAP);
|
||||
end;
|
||||
Inc(CW, PAD_X);
|
||||
if CW > AvailW then CW := AvailW;
|
||||
@@ -444,10 +453,10 @@ begin
|
||||
Target.Canvas.Font.Size := 7;
|
||||
Target.Canvas.Font.Style := [];
|
||||
ContentW := PAD_X + HIDE_W + BTN_GAP;
|
||||
for I := 0 to High(SPAN_NAMES) do
|
||||
for I := 0 to FRateCount-1 do
|
||||
begin
|
||||
Inc(ContentW, SpanBtnWidth(SPAN_NAMES[I], Target.Canvas));
|
||||
if I < High(SPAN_NAMES) then Inc(ContentW, BTN_GAP);
|
||||
Inc(ContentW, SpanBtnWidth(FNames[I], Target.Canvas));
|
||||
if I < FRateCount-1 then Inc(ContentW, BTN_GAP);
|
||||
end;
|
||||
Inc(ContentW, PAD_X);
|
||||
ContentW := Min(ContentW, W);
|
||||
@@ -524,6 +533,26 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSampleRateOverlay.SetRatePresets(const Rates: array of Integer);
|
||||
var i, n: Integer;
|
||||
begin
|
||||
n := Length(Rates);
|
||||
if n > 10 then n := 10;
|
||||
FRateCount := n;
|
||||
for i := 0 to n - 1 do
|
||||
begin
|
||||
FRates[i] := Rates[i];
|
||||
FNames[i] := IntToStr(Rates[i] div 1000) + 'k';
|
||||
end;
|
||||
FCacheDirty := True;
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TSampleRateOverlay.SetRatePresetsDefault;
|
||||
begin
|
||||
SetRatePresets(SPAN_RATES);
|
||||
end;
|
||||
|
||||
procedure TSampleRateOverlay.SetCurrentRate(Rate: Integer);
|
||||
begin
|
||||
if FCurrentRate = Rate then Exit;
|
||||
|
||||
Reference in New Issue
Block a user