Add wideband display pane

This commit is contained in:
2026-05-26 16:24:07 +03:00
parent 762fad420e
commit e0d49c7bfa
5 changed files with 1139 additions and 12 deletions
+129 -3
View File
@@ -91,6 +91,9 @@ type
const Data: TDDCIQPacket) of object;
TOnMicPacket = procedure(const Data: TMicDataPacket) of object;
TOnHPStatus = procedure(const Status: THighPriorityStatus) of object;
TOnWidebandFrame = procedure(ADCIndex: Integer;
const Samples: array of SmallInt;
Count: Integer) of object;
{ THPSDRNetwork }
THPSDRNetwork = class
@@ -117,6 +120,7 @@ type
FOnDDCIQ: TOnDDCIQPacket;
FOnMic: TOnMicPacket;
FOnHPStatus: TOnHPStatus;
FOnWideband: TOnWidebandFrame;
FPortDDCSpec: Word;
FPortDUCSpec: Word;
@@ -124,6 +128,16 @@ type
FPortDDCAudio: Word;
FPortDUCIQ: Word;
FDirectIP: string; // для unicast discovery
FWidebandADC: Integer;
FWidebandEnabled: Boolean;
FWBPacketsPerFrame: Integer;
FWBSamplesPerPacket: Integer;
FWBSampleBits: Integer;
FWBUpdateRateMS: Integer;
FWBFrame: array of SmallInt;
FWBFrameCount: Integer;
FWBLastSeq: LongWord;
FWBSeqValid: Boolean;
// Кэш DDC/DUC Specific — keepalive повторно шлёт первые 5 сек после старта.
// duc_specific_thread эмулятора биндится на 1026 только после HP Run=1,
@@ -178,6 +192,7 @@ type
procedure HandleHPStatus(const Buf: array of Byte; Len: Integer);
procedure HandleDDCIQ(const Buf: array of Byte; Len: Integer; DDCIdx: Integer);
procedure HandleMicData(const Buf: array of Byte; Len: Integer);
procedure HandleWidebandData(const Buf: array of Byte; Len: Integer; ADCIdx: Integer);
procedure StartThreads;
procedure StopThreads;
procedure ResetDUCIQQueue;
@@ -195,6 +210,9 @@ type
procedure Disconnect;
procedure SendGeneralPacket(const Pkt: TGeneralPacket);
procedure ConfigureWideband(ADCIndex: Integer; Enabled: Boolean;
SamplesPerPacket: Integer = 512; SampleBits: Integer = 16;
UpdateRateMS: Integer = 70; PacketsPerFrame: Integer = 32);
procedure SendDDCSpecific(const Pkt: TDDCSpecificPacket);
procedure ConfigureDDCs(NumDDCs: Byte; SampleRate: Word; ADCSource: Byte = 0;
DitherEnabled: Boolean = True; RandomEnabled: Boolean = True);
@@ -227,6 +245,7 @@ type
property OnDDCIQ: TOnDDCIQPacket read FOnDDCIQ write FOnDDCIQ;
property OnMicPacket: TOnMicPacket read FOnMic write FOnMic;
property OnHPStatus: TOnHPStatus read FOnHPStatus write FOnHPStatus;
property OnWideband: TOnWidebandFrame read FOnWideband write FOnWideband;
property DirectIP: string read FDirectIP write FDirectIP; // unicast discovery
end;
@@ -367,6 +386,8 @@ begin
PORT_MIC_DATA:
if Len >= 132 then
FNet.HandleMicData(Buf, Len);
PORT_WIDEBAND_ADC0 .. PORT_WIDEBAND_ADC0 + MAX_ADCS - 1:
FNet.HandleWidebandData(Buf, Len, SrcPort - PORT_WIDEBAND_ADC0);
PORT_DDC0_IQ .. PORT_DDC0_IQ + MAX_DDCS - 1:
FNet.HandleDDCIQ(Buf, Len, SrcPort - PORT_DDC0_IQ);
end;
@@ -577,6 +598,14 @@ begin
FPortHPFromPC := PORT_HP_FROM_PC;
FPortDDCAudio := PORT_DDC_AUDIO;
FPortDUCIQ := PORT_DUC_IQ;
FWidebandADC := 0;
FWidebandEnabled := False;
FWBPacketsPerFrame := 32;
FWBSamplesPerPacket := 512;
FWBSampleBits := 16;
FWBUpdateRateMS := 70;
FWBFrameCount := 0;
FWBSeqValid := False;
FCurrentRXFreq := 7100000;
FCurrentTXFreq := 7100000;
FCurrentDrive := 0;
@@ -1020,6 +1049,56 @@ begin
FOnMic(Pkt); // прямой вызов из receive thread — TX обработка не касается UI
end;
procedure THPSDRNetwork.HandleWidebandData(const Buf: array of Byte; Len: Integer;
ADCIdx: Integer);
var
Seq: LongWord;
I, SamplesInPacket, TargetCount, S: Integer;
begin
if (not FWidebandEnabled) or (ADCIdx <> FWidebandADC) or
(FWBSampleBits <> 16) or (Len < 6) then Exit;
SamplesInPacket := (Len - 4) div 2;
if SamplesInPacket <= 0 then Exit;
if FWBSamplesPerPacket > 0 then
SamplesInPacket := Min(SamplesInPacket, FWBSamplesPerPacket);
Seq := (LongWord(Buf[0]) shl 24) or (LongWord(Buf[1]) shl 16) or
(LongWord(Buf[2]) shl 8) or LongWord(Buf[3]);
if Seq = 0 then
begin
FWBFrameCount := 0;
FWBSeqValid := True;
end
else if FWBSeqValid and (Seq <> FWBLastSeq + 1) then
begin
FWBFrameCount := 0;
FWBSeqValid := False;
Exit;
end;
FWBLastSeq := Seq;
FWBSeqValid := True;
TargetCount := Max(1, FWBPacketsPerFrame) * Max(1, FWBSamplesPerPacket);
if Length(FWBFrame) <> TargetCount then
SetLength(FWBFrame, TargetCount);
for I := 0 to SamplesInPacket - 1 do
begin
if FWBFrameCount >= TargetCount then Break;
S := (SmallInt(ShortInt(Buf[4 + I * 2])) shl 8) or Buf[5 + I * 2];
FWBFrame[FWBFrameCount] := SmallInt(S);
Inc(FWBFrameCount);
end;
if (FWBFrameCount >= TargetCount) and Assigned(FOnWideband) then
begin
FOnWideband(ADCIdx, FWBFrame, FWBFrameCount);
FWBFrameCount := 0;
FWBSeqValid := False;
end;
end;
// ---------------------------------------------------------------------------
// Отправка пакетов
// ---------------------------------------------------------------------------
@@ -1029,10 +1108,51 @@ var
B: TGeneralPacket;
begin
B := Pkt;
B.WBPort[0] := (PORT_WIDEBAND_ADC0 shr 8) and $FF;
B.WBPort[1] := PORT_WIDEBAND_ADC0 and $FF;
if FWidebandEnabled then
B.WBEnable := 1 shl EnsureRange(FWidebandADC, 0, 7)
else
B.WBEnable := 0;
B.WBSamplesPerPkt[0] := (FWBSamplesPerPacket shr 8) and $FF;
B.WBSamplesPerPkt[1] := FWBSamplesPerPacket and $FF;
B.WBSampleSize := FWBSampleBits;
B.WBUpdateRate := FWBUpdateRateMS;
B.WBPacketsPerFrame := FWBPacketsPerFrame;
PackSeqBytes(B.Seq, NextSeq(FSeqGeneral));
DoSendTo(FSocket, B, SizeOf(B), FDevice.IPAddress, PORT_COMMAND);
end;
procedure THPSDRNetwork.ConfigureWideband(ADCIndex: Integer; Enabled: Boolean;
SamplesPerPacket: Integer; SampleBits: Integer; UpdateRateMS: Integer;
PacketsPerFrame: Integer);
var
Gen: TGeneralPacket;
begin
FWidebandADC := EnsureRange(ADCIndex, 0, 7);
FWidebandEnabled := Enabled;
FWBSamplesPerPacket := EnsureRange(SamplesPerPacket, 1, 4096);
FWBSampleBits := EnsureRange(SampleBits, 1, 32);
FWBUpdateRateMS := EnsureRange(UpdateRateMS, 0, 255);
FWBPacketsPerFrame := EnsureRange(PacketsPerFrame, 1, 255);
FWBFrameCount := 0;
FWBSeqValid := False;
if FConnected then
begin
FillChar(Gen, SizeOf(Gen), 0);
Gen.Command := CMD_GENERAL;
Gen.Flags37 := $08;
Gen.Flags38 := $01;
Gen.PAConfig := $01;
if FDevice.BoardType = BOARD_ORION_MK2 then
Gen.AlexEnable := $03
else
Gen.AlexEnable := $01;
SendGeneralPacket(Gen);
SendFullHP;
end;
end;
procedure THPSDRNetwork.SendDDCSpecific(const Pkt: TDDCSpecificPacket);
var
B: TDDCSpecificPacket;
@@ -1078,12 +1198,16 @@ end;
// XVTR без усиления.
// XvtrActive — True когда активен XVTR-диапазон; разрешает биты 8+11
// (стандарт) или 8+14 (Orion2) из Alex.RxOnly[IF_band]=3.
// WidebandBypass — True когда открыт raw ADC wideband. Как Thetis, раскрываем
// RX front-end через HF bypass, иначе видна только полоса
// текущего HPF/BPF, а не весь ADC span.
function CalcAlex0(RXFreqHz, TXFreqHz: Double;
Transmitting, IsOrion2: Boolean;
const Alex: TAlexSettings;
XvtrRxAnt: Byte = 0;
XvtrDisablePA: Boolean = False;
XvtrActive: Boolean = False): LongWord;
XvtrActive: Boolean = False;
WidebandBypass: Boolean = False): LongWord;
var
txf: Double;
BandIdx: Integer;
@@ -1100,7 +1224,9 @@ begin
Result := Result or $08000000; // bit 27: T/R relay
// HPF (ANAN-100/200) или BPF (ANAN-7000/8000/Saturn/G2) — по RX-частоте.
if IsOrion2 then
if WidebandBypass and (not Transmitting) then
Result := Result or $00001000 // bit 12: HF/front-end bypass for WB
else if IsOrion2 then
begin
// Band-pass filters — Orion MkII 5.2, ANAN-7000DLE, Saturn, ANAN-G2
if RXFreqHz < 1500000 then Result := Result or $00001000 // bit 12: HF Bypass
@@ -1402,7 +1528,7 @@ begin
Alex0 := CalcAlex0(FCurrentRXFreq, FCurrentTXFreq,
FIsTransmitting, IsOrion2, FAlexConfig,
FXvtrRxAntOverride, FXvtrDisablePA,
FXvtrEnable);
FXvtrEnable, FWidebandEnabled);
Buf[1432] := (Alex0 shr 24) and $FF;
Buf[1433] := (Alex0 shr 16) and $FF;
Buf[1434] := (Alex0 shr 8) and $FF;