fix: Pluto TX buffer scaled by sample-rate (SoapyPlutoSDR model)

Fixed 4096-pair TX buffer was too small for full-duplex above ~1 Msps
("not enough ... samples getting dropped" per SoapyPlutoSDR), causing
clicks/dropouts. Now sized as round(rate/60) rounded up to a power of 2,
clamped [4096..65536] (576k→16384, 1536k→32768). FIFO cushion (~4 buffers)
resizes to match in StartTX.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 20:20:41 +03:00
co-authored by Claude Opus 4.8
parent 3fe5cf3fad
commit 071ffa476d
+30 -4
View File
@@ -68,6 +68,7 @@ type
FTxThread: TThread;
FTxStreaming: Boolean;
FTxRatio: Integer; // FSampleRate div 192000 (целочисленный апсемпл)
FTxBufPairs: Integer; // размер TX-буфера в IQ-парах (по sample-rate, см. TxBufPairsForRate)
FTxPrevI: LongInt; // последний 24-бит сэмпл (линейная интерполяция между вызовами)
FTxPrevQ: LongInt;
// FIFO int16-пар @ device-rate (кольцо): продюсер — SendDUCIQ, консьюмер — push-поток.
@@ -140,7 +141,11 @@ const
RX_BUF_SAMPLES = 8192; // IQ-пар за один refill (~5.3 мс @ 1.536 MS/s)
PAIRS_PER_PKT = 238; // как DDC IQ HPSDR (238*6 = 1428 байт IQData)
TX_SRC_RATE = 192000; // фиксированный TX-рейт WDSP (TX_SAMPLE_RATE)
TX_BUF_PAIRS = 4096; // IQ-пар в одном TX-буфере push (~2.7 мс @ 1.536 MS/s)
// Размер TX-буфера масштабируется от sample-rate (как SoapyPlutoSDR):
// round(rate/60) → следующая степень 2. Фикс 4096 «недостаточно для full-duplex
// выше 1 Msps — сэмплы дропаются» (комментарий драйвера). Минимум — TX_BUF_MIN.
TX_BUF_MIN = 4096; // нижняя граница (~7 мс @ 576k)
TX_BUF_MAX = 65536; // верхняя граница (защита от гигантских буферов)
PLUTO_TX_OFF = -89.75; // макс. аттенюация AD9361 — TX практически выключен
type
@@ -285,6 +290,21 @@ begin
end;
end;
function TxBufPairsForRate(Rate: Integer): Integer;
// Размер TX-буфера в IQ-парах под device-rate (как SoapyPlutoSDR): round(rate/60)
// округлённое вверх до степени 2, с клампом [TX_BUF_MIN..TX_BUF_MAX].
// 576k→16384, 1536k→32768, 3072k→65536.
var
Target, P: Integer;
begin
Target := (Rate + 30) div 60; // round(rate/60)
P := TX_BUF_MIN;
while P < Target do P := P shl 1; // следующая степень 2 ≥ Target
if P < TX_BUF_MIN then P := TX_BUF_MIN;
if P > TX_BUF_MAX then P := TX_BUF_MAX;
Result := P;
end;
function PlutoSyntheticMAC(const S: string): TBytes;
var
H: QWord;
@@ -323,8 +343,10 @@ begin
FTxBuf := nil; FTxChI := nil; FTxChQ := nil; FTxThread := nil;
FTxStreaming := False; FTxRatio := 8; FTxPrevI := 0; FTxPrevQ := 0;
FTxAttDb := PLUTO_TX_OFF; // на старте TX выключен (макс. аттенюация)
// FIFO на ~4 TX-буфера запаса под джиттер продюсера/консьюмера.
FTxFifoCap := TX_BUF_PAIRS * 2 * 4;
// FIFO на ~4 TX-буфера запаса под джиттер продюсера/консьюмера. Окончательный
// размер выставляется в StartTX по фактическому device-rate (TxBufPairsForRate).
FTxBufPairs := TxBufPairsForRate(FSampleRate);
FTxFifoCap := FTxBufPairs * 2 * 4;
SetLength(FTxFifo, FTxFifoCap);
FTxFifoHead := 0; FTxFifoTail := 0; FTxFifoCount := 0;
InitCriticalSection(FTxLock);
@@ -645,10 +667,14 @@ begin
FTxRatio := FSampleRate div TX_SRC_RATE;
if FTxRatio < 1 then FTxRatio := 1;
FTxPrevI := 0; FTxPrevQ := 0;
// Размер push-буфера под текущий rate + FIFO на ~4 таких буфера запаса.
FTxBufPairs := TxBufPairsForRate(FSampleRate);
EnterCriticalSection(FTxLock);
FTxFifoCap := FTxBufPairs * 2 * 4;
if Length(FTxFifo) <> FTxFifoCap then SetLength(FTxFifo, FTxFifoCap);
FTxFifoHead := 0; FTxFifoTail := 0; FTxFifoCount := 0;
LeaveCriticalSection(FTxLock);
FTxBuf := iio_device_create_buffer(FTxDev, TX_BUF_PAIRS, False);
FTxBuf := iio_device_create_buffer(FTxDev, FTxBufPairs, False);
if FTxBuf = nil then begin FLastError := 'TX create_buffer failed'; Exit; end;
ApplyTxAtten; // стартуем с текущей аттенюации (по умолчанию OFF до PTT)
FTxStreaming := True;