From 055873a43a2c3d83ecd8529469b03b1ba859367d Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 28 Apr 2026 13:31:39 +0300 Subject: [PATCH] Fix IQ data flow: send HP Run=1 before DDC/DUC Specific packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The emulator (and real hardware) creates ddc_specific_thread (port 1025), duc_specific_thread (port 1026), and rx_thread[0..3] (ports 1035+) only after receiving HP Run=1. Previously ConfigureDDCs and SendDUCSpecific were sent before SetRunAndFreq(True), so both packets arrived at closed ports and were silently dropped — ddcenable remained -1, rx_thread slept forever, no IQ data ever arrived, and the 5-second timeout fired Run=0. Fix: reorder DoConnectDevice to send Run=1 first, then DDC/DUC Specific. Also add keepalive resend of the cached DDC Specific packet every 500 ms for the first 5 seconds after start, to handle thread-startup races on real hardware over the network. Co-Authored-By: Claude Sonnet 4.6 --- HPSDRNetwork.pas | 17 +++++++++++++++++ MainForm.pas | 12 ++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/HPSDRNetwork.pas b/HPSDRNetwork.pas index 7b5b4de..8fdb454 100644 --- a/HPSDRNetwork.pas +++ b/HPSDRNetwork.pas @@ -121,6 +121,11 @@ type FPortDUCIQ: Word; FDirectIP: string; // для unicast discovery + // Кэш DDC Specific — keepalive повторно шлёт первые 5 сек после старта + FCachedDDCSpec: TDDCSpecificPacket; + FCachedDDCValid: Boolean; + FResendCount: Integer; + // Текущее состояние для построения HP пакетов FCurrentRXFreq: Double; FCurrentTXFreq: Double; @@ -398,6 +403,15 @@ begin if not FNet.FConnected then Continue; // Отправляем полный HP с частотами и ALEX — как piHPSDR FNet.SendFullHP; + // Повторно отправляем DDC Specific первые 5 сек после Run=1 (каждые 500 мс). + // ddc_specific_thread на эмуляторе/железе стартует ПОСЛЕ получения Run=1, + // поэтому первый ConfigureDDCs может прийти раньше чем порт 1025 откроется. + if FNet.FRunning and FNet.FCachedDDCValid then + begin + Inc(FNet.FResendCount); + if (FNet.FResendCount <= 100) and (FNet.FResendCount mod 10 = 2) then + FNet.SendDDCSpecific(FNet.FCachedDDCSpec); + end; end; end; @@ -914,6 +928,9 @@ begin end; SendDDCSpecific(Pkt); + FCachedDDCSpec := Pkt; + FCachedDDCValid := True; + FResendCount := 0; end; procedure THPSDRNetwork.SendDUCSpecific(const Pkt: TDUCSpecificPacket); diff --git a/MainForm.pas b/MainForm.pas index 853543b..e9d3e5f 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -2827,8 +2827,6 @@ begin else FActiveDDC := 0; - FNetwork.ConfigureDDCs(1, FSampleRate div 1000, 0); - // Синхронизируем samplerate WDSP если нужно if FWDSPReady and (FDSPEngine.SampleRate <> FSampleRate) then begin @@ -2849,7 +2847,7 @@ begin end; FSpecView.AGCTop := FAGCTop; - // DUC specific + // DUC specific packet (строим здесь, отправляем после Run=1) FillChar(DUCPkt, SizeOf(DUCPkt), 0); DUCPkt.NumDACs := 1; DUCPkt.SidetoneLevel := 50; @@ -2860,14 +2858,20 @@ begin DUCPkt.DUC0RateHi := Hi(192); DUCPkt.DUC0RateLo := Lo(192); DUCPkt.DUC0Bits := 24; - FNetwork.SendDUCSpecific(DUCPkt); + // Сначала отправляем Run=1 — эмулятор/железо создают ddc_specific_thread + // (порт 1025) и rx_thread (порт 1035+) только после получения HP Run=1. + // DDC Specific и DUC Specific отправляем ПОСЛЕ, иначе порты ещё не открыты. FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, False, True, True); FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel); FRunning := True; FRXStartTime := GetTickCount64; FRXLastPktTime := 0; + // DDC и DUC Specific — теперь потоки эмулятора слушают на своих портах + FNetwork.ConfigureDDCs(1, FSampleRate div 1000, 0); + FNetwork.SendDUCSpecific(DUCPkt); + // Принудительно обновляем AGC линии — гарантированно после всех SetAGC вызовов if FWDSPReady then FDSPEngine.UpdateAGCLines(FSpectrumWidth); // ещё не получили ни одного пакета