mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(tx): тихий детектор голодания очереди TX
Всплеск, рождённый нашим трактом, невозможен без одного события: очередь DUC
простаивает дольше подушки отправителя (DUC_FIFO_THROTTLE = 2000 отсчётов при
192 кГц = 10.42 мс). Съём 2026-08-21 это показал прямо: шесть всплесков — шесть
простоев 10.3-23.5 мс, а паузы 8 мс и короче не дали ни одного. Значит счётчик
таких простоев — полный детектор «наш/не наш», и оператору больше не нужно
называть интервал на глаз.
Новый юнит TxHealth.pas. Включён всегда: переменной окружения нет намеренно —
прибор, который надо не забыть включить, к моменту дефекта выключен.
* поток отправителя DUC мерит каждый простой очереди на передаче — одно чтение
монотонных часов на ПЕРЕХОД, ни файлов, ни локов (обе ветки, win и unix);
* планировщик TCI публикует рядом состояние пейсинга (аванс, долг, в полёте,
квант, шаг, период пачек клиента) — простыми записями, без лока: числа
диагностические, а лок в потоке отправителя DUC недопустим для тайминга;
* сетевой поток считает бит HPS_FIFO_EMPTY из HP-статуса радио — поле ГЛУБИНЫ
FIFO на этой прошивке статично (256) и веры ему нет, а бит мы не читали вовсе;
* ★журнал пишет ТОЛЬКО потребитель — таймер UI (100 мс) и цикл демона через
TxHealthService. В тракте передачи диска нет по построению.
Последнее — правило, оплаченное регрессом: прошлый прибор (TxTrace) сбрасывал
дамп синхронно в хвосте SetMOX, и эти десятки миллисекунд закрывали гонку в
пейсинге TCI (лечение — fa38fa7). Инструмент чинил дефект собой.
Журнал ~/.config/ewsdr/txhealth.log, строка на передачу: «чисто» либо «ОПАСНО
(первое на N мс)» плюс подробности с контекстом пейсинга. В статусной строке
UI — «·сухо N» за сеанс, чтобы не помнить, какая посылка была плохой.
Стенд: секция G в test/tci (порог подушки, сводка, момент от фронта PTT,
контекст рядом с событием, молчание вне передачи) — 276/276. Диск стенд не
трогает намеренно: берёт сводки через TxHealthTake/TxHealthDetail, мимо
TxHealthService, иначе писал бы в настоящий журнал пользователя.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bkwwyj7xVRrqnSVEseTRfV
This commit is contained in:
+23
-1
@@ -51,7 +51,7 @@ uses
|
||||
HPSDRProtocol, HPSDRNetwork, RadioBackend, PlutoBackend, IIOBindings,
|
||||
WDSPEngine, AudioOutput, AudioInput, BeaconDecoder, BeaconFEC, DMRDecoder,
|
||||
Settings, ChannelStore, FMRepeater, BoardUtils, DeviceStore, CWMorse, CWKeyer,
|
||||
CWDecoder;
|
||||
CWDecoder, TxHealth;
|
||||
|
||||
// Панадаптеры (этап 3): потолок MAX_PANS живёт в WDSPEngine (общий для
|
||||
// движка/контроллера/UI). Фактический лимит бэкенда — Caps.MaxPans (Pluto=1).
|
||||
@@ -1864,6 +1864,11 @@ begin
|
||||
FHPpPLL := (Status.StatusBits and HPS_PLL_LOCKED) <> 0;
|
||||
FHPpHWPTT := (Status.StatusBits and HPS_PTT) <> 0;
|
||||
FHPpOverload := Status.ADCOverload;
|
||||
// ★Жалоба самого радио: бит «DUC FIFO пуст». Поле ГЛУБИНЫ FIFO (байты 35-36)
|
||||
// на этой прошивке статично и веры ему нет, а бит мы не читали вовсе —
|
||||
// если прошивка его заполняет, это независимый свидетель голодания.
|
||||
if FTransmitting and ((Status.StatusBits and HPS_FIFO_EMPTY) <> 0) then
|
||||
TxHealthFifoEmpty;
|
||||
// Состояние ключа/манипулятора в разъёме трансивера (сетевой поток).
|
||||
// Касание обрывает программную передачу — как в Thetis/pihpsdr («key hit»
|
||||
// останавливает CAT CW), иначе оператор не может вклиниться. ★Обрываем по
|
||||
@@ -6292,6 +6297,7 @@ end;
|
||||
procedure TRadioController.SetMOX(On_: Boolean);
|
||||
var
|
||||
WasTransmitting: Boolean;
|
||||
TxhSrc: Integer;
|
||||
begin
|
||||
// DMR support is receive-only. This guard also covers CAT, web and hardware
|
||||
// PTT. FM RAW is a flat external-modem transmit path.
|
||||
@@ -6426,6 +6432,19 @@ begin
|
||||
if FTransmitting and FDisplayDuplex then
|
||||
FDSPEngine.SetDisplaySourceTX(False);
|
||||
end;
|
||||
// Детектор голодания очереди TX: фронт «в эфир» открывает передачу для
|
||||
// счётчиков. Стоит ПОСЛЕ блока движка — источник модуляции решается там.
|
||||
// Ввода-вывода тут нет и быть не может: журнал пишет потребитель (таймер UI /
|
||||
// цикл демона), см. TxHealth.pas.
|
||||
if FTransmitting and (not WasTransmitting) then
|
||||
begin
|
||||
if FTCIMicActive then TxhSrc := txhSrcTCI
|
||||
else if FWebMicActive then TxhSrc := txhSrcWeb
|
||||
else if Assigned(FDSPEngine) and (FDSPEngine.TXMicSource = txmsSoundCard) then
|
||||
TxhSrc := txhSrcCard
|
||||
else TxhSrc := txhSrcRadio;
|
||||
TxHealthPtt(True, TxhSrc);
|
||||
end;
|
||||
// Сетевая PTT-последовательность TX-off: сначала снимаем PTT, затем реле RX.
|
||||
if (not FTransmitting) and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
@@ -6443,6 +6462,9 @@ begin
|
||||
// (оператор держит T/R руками) — вооружение обязано пойти за этой кнопкой.
|
||||
SyncCWKeyer;
|
||||
Changed(rfTransmitting);
|
||||
// Передача кончилась — складываем сводку в память. Диск не трогаем (★прошлый
|
||||
// прибор писал дамп ровно здесь и тем самым прикрывал гонку в пейсинге).
|
||||
if WasTransmitting and (not FTransmitting) then TxHealthPtt(False, 0);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetTune(On_: Boolean);
|
||||
|
||||
Reference in New Issue
Block a user