diff --git a/PlutoBackend.pas b/PlutoBackend.pas index 191d4c3..a9b6ce3 100644 --- a/PlutoBackend.pas +++ b/PlutoBackend.pas @@ -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;