mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
chore(tx): сняты зонды трассировки — пейсинг проверен на железе
Всплесков на передаче нет, инструмент своё отработал и больше не нужен в горячем пути. Снято ровно то, что ставилd209697, и ничего сверх: * удалён юнит TxTrace.pas (кольцо + сброс дампа + TraceInit); * TCIAdapter: точки съёма маркеров и TX-аудио, поля FTxTraceLast/FTxTraceAudio; * HPSDRNetwork: DUC-DRY/DUC-RUN и переменная DryFrom (жила только для них), вместе с ними ушли из uses TxTrace и PlatformUtils — их добавлял тот же коммит, MonotonicUs в этом файле больше нигде не встречается; * RadioController: PTT-метка в SetMOX и сброс дампа на снятии PTT; * TraceInit из ewsdr.lpr и ewsdrd.lpr. Каждая удалённая строка стояла под `if TraceOn` — вычислений, от которых зависит тракт, в них не было. ★PlatformUtils.MonotonicUs НЕ тронут: на нём абсолютные дедлайны планировщика TX, это не отладка. Скрипт разбора test/hpsdr/txtrace_scan.py оставлен, формат дампа не менялся; в README стенда записан рецепт возврата зондов изd209697. Проверено: lazbuild --ws=qt6 и build-ewsdrd.sh собираются, test/tci 260/260, test/cat 50/50 — те же числа, что и с зондами. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bkwwyj7xVRrqnSVEseTRfV
This commit is contained in:
+1
-15
@@ -19,7 +19,7 @@ uses
|
||||
{$ELSE}
|
||||
Sockets, BaseUnix,
|
||||
{$ENDIF}
|
||||
HPSDRProtocol, SyncObjs, Settings, RadioBackend, TxTrace, PlatformUtils;
|
||||
HPSDRProtocol, SyncObjs, Settings, RadioBackend;
|
||||
|
||||
const
|
||||
DUC_TX_QUEUE_SIZE = 256; // power of two, bounded latency on TX underrun/overrun
|
||||
@@ -509,7 +509,6 @@ var
|
||||
NowTick: QWord;
|
||||
ElapsedMs: QWord;
|
||||
WaitMs: Integer;
|
||||
DryFrom: Int64; // приборы: начало простоя очереди, мкс
|
||||
{$ENDIF}
|
||||
|
||||
{$IFDEF WINDOWS}
|
||||
@@ -581,18 +580,12 @@ begin
|
||||
{$ELSE}
|
||||
VirtualSamples := 0;
|
||||
LastTick := GetTickCount64;
|
||||
DryFrom := 0;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
if not FNet.DequeueDUCIQ(Pkt) then
|
||||
begin
|
||||
DrainVirtualFIFO;
|
||||
if TraceOn and FNet.FIsTransmitting and (DryFrom = 0) then
|
||||
begin
|
||||
DryFrom := MonotonicUs;
|
||||
TR(trkDucDry, VirtualSamples, FNet.FDUCIQCount);
|
||||
end;
|
||||
// Очередь пуста. НЕ инжектируем нулевые пакеты — 1.25мс тишины прямо
|
||||
// в поток сэмплов = разрыв данных = всплеск на водопаде/в эфире. Как
|
||||
// dl1ycf pihpsdr (txiq_thread: пустая очередь → просто sem_wait): ждём
|
||||
@@ -611,13 +604,6 @@ begin
|
||||
if not FNet.FIsTransmitting then
|
||||
Continue;
|
||||
|
||||
if TraceOn and (DryFrom <> 0) then
|
||||
begin
|
||||
TR(trkDucRun, Integer(MonotonicUs - DryFrom), VirtualSamples,
|
||||
FNet.FDUCIQCount);
|
||||
DryFrom := 0;
|
||||
end;
|
||||
|
||||
FNet.PackSeqBytes(Pkt.Seq, FNet.NextSeq(FNet.FSeqDUCIQ));
|
||||
FNet.DoSendTo(FNet.FSocket, Pkt, SizeOf(Pkt), FNet.FDevice.IPAddress,
|
||||
FNet.FPortDUCIQ);
|
||||
|
||||
+1
-5
@@ -51,7 +51,7 @@ uses
|
||||
HPSDRProtocol, HPSDRNetwork, RadioBackend, PlutoBackend, IIOBindings,
|
||||
WDSPEngine, AudioOutput, AudioInput, BeaconDecoder, BeaconFEC, DMRDecoder,
|
||||
Settings, ChannelStore, FMRepeater, BoardUtils, DeviceStore, CWMorse, CWKeyer,
|
||||
CWDecoder, TxTrace;
|
||||
CWDecoder;
|
||||
|
||||
// Панадаптеры (этап 3): потолок MAX_PANS живёт в WDSPEngine (общий для
|
||||
// движка/контроллера/UI). Фактический лимит бэкенда — Caps.MaxPans (Pluto=1).
|
||||
@@ -6303,9 +6303,6 @@ begin
|
||||
// прекратить — ключ обязан отпуститься немедленно.
|
||||
if (not On_) and CWXBusy then CWXAbort;
|
||||
FTransmitting := On_;
|
||||
if TraceOn and (On_ <> WasTransmitting) then
|
||||
if Assigned(FDSPEngine) then TR(trkPtt, Ord(On_), Ord(FDSPEngine.TXMicSource))
|
||||
else TR(trkPtt, Ord(On_), -1);
|
||||
// 2TON: прямое снятие MOX (кнопкой/CAT/web) гасит и двухтональник.
|
||||
if (not On_) and FTwoTone then
|
||||
begin
|
||||
@@ -6446,7 +6443,6 @@ begin
|
||||
// (оператор держит T/R руками) — вооружение обязано пойти за этой кнопкой.
|
||||
SyncCWKeyer;
|
||||
Changed(rfTransmitting);
|
||||
if TraceOn and WasTransmitting and (not On_) then TraceDump('tx');
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetTune(On_: Boolean);
|
||||
|
||||
+1
-21
@@ -44,7 +44,7 @@ interface
|
||||
uses
|
||||
Classes, SysUtils, DateUtils, Math, SyncObjs,
|
||||
RadioController, RadioBackend, WDSPEngine, Settings, PlatformUtils,
|
||||
DXSpotStore, TCIProtocol, TCIServer, TCIStreams, TxTrace;
|
||||
DXSpotStore, TCIProtocol, TCIServer, TCIStreams;
|
||||
|
||||
const
|
||||
// ★Приёмник TCI: 0 — главный тракт (каналы A/B = VFO A/B), N — слайс СЛОТА
|
||||
@@ -242,8 +242,6 @@ type
|
||||
FTxLeadKeep: Integer; // ★он же, но ПЕРЕЖИВАЮЩИЙ конец передачи
|
||||
FTxLeadRate: Integer; // частота, при которой аванс измерен
|
||||
FTxLeadLowUs: Int64; // с какого момента оценка держится НИЖЕ аванса
|
||||
FTxTraceLast: Int64; // приборы: время прошлого маркера
|
||||
FTxTraceAudio: Int64; // приборы: время прошлого блока аудио
|
||||
FTxLastRxUs: Int64; // когда пришёл прошлый блок (границы пачек)
|
||||
// Рабочие буферы разбора TX-блока. Полем, а не на стеке: развёрнутый в
|
||||
// 48 кГц блок — это сотни килобайт, и класть их в стек потока клиента
|
||||
@@ -3754,7 +3752,6 @@ begin
|
||||
|
||||
TCIFillHeader(H, tstTXChrono, Rx, Rate, ST, Q, Chans);
|
||||
Client.SendBinNow(H);
|
||||
if TraceOn then TR(trkNote, -4, Q, Rx);
|
||||
end;
|
||||
|
||||
procedure TTCIAdapter.TxPublishLead(Rate: Integer);
|
||||
@@ -3843,7 +3840,6 @@ begin
|
||||
try
|
||||
if not Active then
|
||||
begin
|
||||
if TraceOn and FTxRunning then TR(trkNote, -3, Round(FTxInFlight), 0);
|
||||
FTxRunning := False;
|
||||
FServer.TxTickPeriodMs := 0;
|
||||
Exit;
|
||||
@@ -3886,7 +3882,6 @@ begin
|
||||
(TCI_TX_LEAD_MAX_MS * Rate) div 1000);
|
||||
if LeadWant > FTxLead then
|
||||
begin
|
||||
if TraceOn then TR(trkNote, -2, LeadWant, FTxLead);
|
||||
FTxLead := LeadWant;
|
||||
FTxLeadLowUs := 0;
|
||||
TxPublishLead(Rate);
|
||||
@@ -3899,7 +3894,6 @@ begin
|
||||
if FTxLeadLowUs = 0 then FTxLeadLowUs := NowUs
|
||||
else if NowUs - FTxLeadLowUs > TCI_TX_LEAD_DOWN_MS * 1000 then
|
||||
begin
|
||||
if TraceOn then TR(trkNote, -2, LeadWant, FTxLead);
|
||||
FTxLead := LeadWant;
|
||||
FTxLeadLowUs := NowUs;
|
||||
TxPublishLead(Rate);
|
||||
@@ -3946,7 +3940,6 @@ begin
|
||||
begin
|
||||
// Прощаем ровно один квант и начинаем выдержку заново: поздний ответ
|
||||
// безопасен, он уведёт долг в минус и сам притормозит выдачу.
|
||||
if TraceOn then TR(trkNote, -1, Round(FTxInFlight), FTxWindowQ);
|
||||
FTxInFlight := Max(0, FTxInFlight - Q);
|
||||
// ★Срок сдвигаем НА выдержку, а не на «сейчас». Зависших кредитов может
|
||||
// быть несколько, и прощение по одному за выдержку живого времени
|
||||
@@ -3984,13 +3977,6 @@ begin
|
||||
FTxInFlight := FTxInFlight + Q;
|
||||
TxNoteRequest(NowUs);
|
||||
Inc(Sent);
|
||||
if TraceOn then
|
||||
begin
|
||||
if FTxTraceLast = 0 then FTxTraceLast := NowUs;
|
||||
TR(trkChrono, Q, Round(FTxOwed - FTxInFlight),
|
||||
Integer(NowUs - FTxTraceLast));
|
||||
FTxTraceLast := NowUs;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
FTxLock.Leave;
|
||||
@@ -4104,12 +4090,6 @@ begin
|
||||
FTxProgressUs := NowUs;
|
||||
FTxArmed := True;
|
||||
TxNoteReply(NowUs);
|
||||
if TraceOn then
|
||||
begin
|
||||
if FTxTraceAudio = 0 then FTxTraceAudio := NowUs;
|
||||
TR(trkTxAudio, Integer(NowUs - FTxTraceAudio), Integer(H.DataLength), k);
|
||||
FTxTraceAudio := NowUs;
|
||||
end;
|
||||
|
||||
if (FTxInterp = nil) or (FTxInRate <> Rate) then
|
||||
begin
|
||||
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
unit TxTrace;
|
||||
|
||||
{
|
||||
TxTrace.pas — кольцевая трассировка TX-тракта.
|
||||
|
||||
★Временный диагностический юнит. Выключен по умолчанию, включается
|
||||
переменной окружения EWSDR_TXTRACE=1; при выключенной трассировке TR() —
|
||||
одна проверка Boolean. Никакого ввода-вывода в горячем пути: кольцо
|
||||
сбрасывается на диск в конце каждой передачи.
|
||||
|
||||
Зачем он снова здесь: всплески на передаче вернулись после того, как первая
|
||||
редакция зондов была удалена, и без них видно только то, что видит глаз на
|
||||
водопаде. Прибор смотрит ровно на подачу TX-аудио по TCI и на очередь DUC.
|
||||
}
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE Delphi}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
const
|
||||
trkPtt = 0; // A=1 старт передачи / 0 конец; B=источник модуляции
|
||||
trkChrono = 7; // A=запрошено кадров; B=непокрытый долг; C=пауза от прошлого маркера, мкс
|
||||
trkTxAudio = 8; // A=пауза от прошлого блока, мкс; B=length в шапке; C=кадров после интерполяции
|
||||
trkDucDry = 10; // A=виртуальный уровень FIFO радио; B=глубина очереди
|
||||
trkDucRun = 11; // A=длительность простоя очереди, мкс; B=уровень FIFO; C=глубина очереди
|
||||
trkNote = 12; // A=-1 прощение кредита, -2 смена аванса, -3 в полёте на снятии PTT, -4 предварительный маркер
|
||||
|
||||
var
|
||||
TraceOn: Boolean = False;
|
||||
|
||||
procedure TraceInit;
|
||||
procedure TR(Kind: Integer; A: Integer = 0; B: Integer = 0; C: Integer = 0);
|
||||
function TraceDump(const Reason: string): string;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
SysUtils, Math, PlatformUtils;
|
||||
|
||||
const
|
||||
TRACE_SIZE = 16384;
|
||||
TRACE_MASK = TRACE_SIZE - 1;
|
||||
|
||||
type
|
||||
TTraceRec = record
|
||||
T: Int64;
|
||||
Kind: Integer;
|
||||
A, B, C: Integer;
|
||||
end;
|
||||
|
||||
var
|
||||
Ring: array[0..TRACE_SIZE - 1] of TTraceRec;
|
||||
RingSeq: LongInt = 0;
|
||||
Epoch: Int64 = 0;
|
||||
|
||||
const
|
||||
TRACE_NAMES: array[0..12] of string = (
|
||||
'PTT', 'RECV-GAP', 'HANDLER', 'HP-SYNC', 'WIDEBAND', 'TX-TICK',
|
||||
'TX-PHASE', 'CHRONO', 'TX-AUDIO', 'PULL-MIC', 'DUC-DRY', 'DUC-RUN', 'NOTE');
|
||||
|
||||
procedure TraceInit;
|
||||
var
|
||||
S: string;
|
||||
begin
|
||||
S := LowerCase(Trim(GetEnvironmentVariable('EWSDR_TXTRACE')));
|
||||
TraceOn := (S = '1') or (S = 'true') or (S = 'yes') or (S = 'on');
|
||||
Epoch := MonotonicUs;
|
||||
end;
|
||||
|
||||
procedure TR(Kind: Integer; A: Integer = 0; B: Integer = 0; C: Integer = 0);
|
||||
var
|
||||
Idx: LongInt;
|
||||
P: ^TTraceRec;
|
||||
begin
|
||||
if not TraceOn then Exit;
|
||||
Idx := InterlockedIncrement(RingSeq) - 1;
|
||||
P := @Ring[Idx and TRACE_MASK];
|
||||
P^.T := MonotonicUs;
|
||||
P^.Kind := Kind;
|
||||
P^.A := A;
|
||||
P^.B := B;
|
||||
P^.C := C;
|
||||
end;
|
||||
|
||||
function Slug(const S: string): string;
|
||||
var i: Integer;
|
||||
begin
|
||||
Result := '';
|
||||
for i := 1 to Length(S) do
|
||||
if S[i] in ['a'..'z', 'A'..'Z', '0'..'9', '-', '_'] then
|
||||
Result := Result + S[i];
|
||||
if Result <> '' then Result := '-' + Copy(Result, 1, 24);
|
||||
end;
|
||||
|
||||
function TraceDump(const Reason: string): string;
|
||||
var
|
||||
F: TextFile;
|
||||
i, First: LongInt;
|
||||
Total: LongInt;
|
||||
R: TTraceRec;
|
||||
Name: string;
|
||||
begin
|
||||
Result := '';
|
||||
if not TraceOn then Exit;
|
||||
Total := RingSeq;
|
||||
if Total <= 0 then Exit;
|
||||
|
||||
Result := GetAppCfgDir + 'txtrace-' +
|
||||
FormatDateTime('yyyymmdd-hhnnss', Now) + Slug(Reason) + '.txt';
|
||||
AssignFile(F, Result);
|
||||
try
|
||||
Rewrite(F);
|
||||
try
|
||||
WriteLn(F, '# ewsdr TX trace, повод: ', Reason);
|
||||
WriteLn(F, '# всего событий: ', Total, ', в кольце: ',
|
||||
Min(Total, TRACE_SIZE));
|
||||
WriteLn(F, '# t_ms событие A B C');
|
||||
First := Total - Min(Total, TRACE_SIZE);
|
||||
for i := First to Total - 1 do
|
||||
begin
|
||||
R := Ring[i and TRACE_MASK];
|
||||
if (R.Kind < Low(TRACE_NAMES)) or (R.Kind > High(TRACE_NAMES)) then
|
||||
Name := '?'
|
||||
else
|
||||
Name := TRACE_NAMES[R.Kind];
|
||||
WriteLn(F, Format('%12.3f %-9s %9d %9d %9d',
|
||||
[(R.T - Epoch) / 1000.0, Name, R.A, R.B, R.C]));
|
||||
end;
|
||||
finally
|
||||
CloseFile(F);
|
||||
end;
|
||||
except
|
||||
on E: Exception do Result := '';
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -18,12 +18,11 @@ uses
|
||||
Forms, LazOpenGLContextEx,
|
||||
MainForm, AudioOutput, DeviceForm, FlatButton, FreqDisplay, VfoOverlay,
|
||||
WDSP, WDSPEngine, WebPageHtml, WebServer, WebUtils, WinFirewall, WsClient,
|
||||
Settings, RadioController, TxTrace;
|
||||
Settings, RadioController;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
begin
|
||||
TraceInit;
|
||||
EnableProcessDpiAwareness;
|
||||
RequireDerivedFormResource := True;
|
||||
Application.Title:='EWSDR';
|
||||
|
||||
+1
-2
@@ -28,7 +28,7 @@ uses
|
||||
{$IFDEF UNIX}cthreads, BaseUnix,{$ENDIF}
|
||||
Classes, SysUtils, Math,
|
||||
RadioController, WebServer, WebAdapter, WDSPEngine,
|
||||
HPSDRNetwork, Settings, WDSP, PlatformUtils, DeviceStore, BoardUtils, TxTrace;
|
||||
HPSDRNetwork, Settings, WDSP, PlatformUtils, DeviceStore, BoardUtils;
|
||||
|
||||
const
|
||||
PUSH_INTERVAL_MS = 50; // период исходящего web-зеркала (как GUI-таймер)
|
||||
@@ -490,7 +490,6 @@ begin
|
||||
end;
|
||||
|
||||
begin
|
||||
TraceInit;
|
||||
OnGetApplicationName := @DaemonAppName; // ДО создания контроллера/настроек!
|
||||
Log('EWSDR daemon starting (headless).');
|
||||
GHost := THeadlessHost.Create;
|
||||
|
||||
+12
-1
@@ -107,8 +107,19 @@ TX голодала, в эфире дыры. Тут же диапазон глу
|
||||
|
||||
## Приборы внутри процесса: кольцевая трассировка TX-тракта
|
||||
|
||||
★**Сняты из кода 2026-08-24** — пейсинг TX проверен на железе, всплесков нет, и
|
||||
инструмент больше не нужен в горячем пути. Последняя редакция зондов лежит в
|
||||
коммите `d209697`; вернуть их целиком:
|
||||
|
||||
git show d209697 -- TxTrace.pas | git apply # сам юнит
|
||||
git show <коммит снятия> | git apply -R # точки съёма и TraceInit
|
||||
|
||||
Скрипт разбора (`txtrace_scan.py`) оставлен: формат дампа не менялся, и при
|
||||
возврате зондов он работает как прежде. Ниже — описание прибора на случай
|
||||
возврата.
|
||||
|
||||
Дамп на проводе видит ПАУЗУ в потоке DUC, но не видит, кто её сделал. Для этого
|
||||
в код вписано кольцо трассировки (`TxTrace.pas`), выключенное по умолчанию.
|
||||
в код вписывалось кольцо трассировки (`TxTrace.pas`), выключенное по умолчанию.
|
||||
|
||||
Включение — переменной окружения, кольцо сбрасывается на диск в конце КАЖДОЙ
|
||||
передачи (после снятия PTT, чтобы файл не задержал выход из эфира):
|
||||
|
||||
Reference in New Issue
Block a user