diff --git a/HPSDRNetwork.pas b/HPSDRNetwork.pas index ed7331b..1197059 100644 --- a/HPSDRNetwork.pas +++ b/HPSDRNetwork.pas @@ -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); diff --git a/RadioController.pas b/RadioController.pas index ce7fbfb..babcc53 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -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); diff --git a/TCIAdapter.pas b/TCIAdapter.pas index 1a726dd..2302b8b 100644 --- a/TCIAdapter.pas +++ b/TCIAdapter.pas @@ -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 diff --git a/TxTrace.pas b/TxTrace.pas deleted file mode 100644 index 2e4cb1a..0000000 --- a/TxTrace.pas +++ /dev/null @@ -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. diff --git a/ewsdr.lpr b/ewsdr.lpr index 6f8b65f..317be48 100644 --- a/ewsdr.lpr +++ b/ewsdr.lpr @@ -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'; diff --git a/ewsdrd.lpr b/ewsdrd.lpr index 5dd6599..f04e51c 100644 --- a/ewsdrd.lpr +++ b/ewsdrd.lpr @@ -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; diff --git a/test/hpsdr/README.md b/test/hpsdr/README.md index a6d646a..6089f09 100644 --- a/test/hpsdr/README.md +++ b/test/hpsdr/README.md @@ -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, чтобы файл не задержал выход из эфира):