mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
feat: QO-100 mode — separate RX/TX LO + dedicated settings page
QO-100 reuses the transverter path (VFO/band-stack/beacon-lock untouched) with a split LO for the geostationary transponder: - TXvtrEntry += FullDuplexTx + TxLOOffset (persisted). Reserve the last XVTR slot (QO100_SLOT) for QO-100; migration restores the template when the slot is unset or has empty offsets (fixes old configs showing zeros). - XvtrTranslateTX: TX LO = visible - TxLOOffset (no LNB LOError) for QO-100; identical to RX translate for normal transverters. Switched all TX-side translate calls (controller + MainForm). - SetDuplex wired (live KeepRXDuringTX); auto display-duplex on QO-100 entry. - New "QO-100" settings page (LNB LO / transponder offset / downlink edges / RX gain / TX ceiling + live RX-IF/TX readout); QO slot hidden from the transverter table. Reuses OnXvtrChange — no MainForm wiring needed. - doc/PLUTO_INTEGRATION_PLAN.md: phase 5 done. Builds clean (headless + qt6 GUI). Not yet verified on hardware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+37
-7
@@ -347,6 +347,10 @@ type
|
||||
// активного трансвертора (LOOffset/LOError).
|
||||
// ActiveTXFreqHz — текущая TX-частота (split/active VFO + FM-репитер сдвиг).
|
||||
function XvtrTranslate(VisibleHz: Double): Double;
|
||||
// XvtrTranslateTX — то же для ПЕРЕДАЧИ. Обычный трансвертер: = XvtrTranslate.
|
||||
// QO-100 (FullDuplexTx): отдельный TX-LO (visible-TxLOOffset, без LOError —
|
||||
// uplink-LO не подвержен дрейфу LNB).
|
||||
function XvtrTranslateTX(VisibleHz: Double): Double;
|
||||
function ActiveTXFreqHz: Double;
|
||||
// Измерение маяка по display-FFT в окне ±WinHz вокруг ExpectOff (baseband):
|
||||
// центроид лепестка → уточнённое смещение, «высота» пика над средним окна
|
||||
@@ -629,7 +633,7 @@ procedure TRadioController.PushNetworkState;
|
||||
begin
|
||||
if Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz),
|
||||
FDriveLevel, FTransmitting, True, True);
|
||||
FNetwork.SendFullHP;
|
||||
end;
|
||||
@@ -979,6 +983,22 @@ begin
|
||||
+ FXvtrSettings.Entries[FCurrentXvtr].LOError;
|
||||
end;
|
||||
|
||||
function TRadioController.XvtrTranslateTX(VisibleHz: Double): Double;
|
||||
begin
|
||||
Result := VisibleHz;
|
||||
if (FCurrentXvtr < 0) or (FCurrentXvtr >= CFG_XVTR_COUNT) then Exit;
|
||||
if not FXvtrSettings.Entries[FCurrentXvtr].Enabled then Exit;
|
||||
// QO-100 / split-LO: TX идёт на собственный uplink-LO (visible - TxLOOffset),
|
||||
// без LOError — дрейф LNB к передаче отношения не имеет.
|
||||
if FXvtrSettings.Entries[FCurrentXvtr].FullDuplexTx then
|
||||
Result := VisibleHz - FXvtrSettings.Entries[FCurrentXvtr].TxLOOffset
|
||||
else
|
||||
// Обычный трансвертер: RX=TX LO (с калибровкой LOError) — как раньше.
|
||||
Result := VisibleHz
|
||||
- FXvtrSettings.Entries[FCurrentXvtr].LOOffset
|
||||
+ FXvtrSettings.Entries[FCurrentXvtr].LOError;
|
||||
end;
|
||||
|
||||
function TRadioController.ActiveTXFreqHz: Double;
|
||||
begin
|
||||
// TX-VFO задаётся ТОЛЬКО селектором FSplitTxB (UI: кнопки TX-A/TX-B),
|
||||
@@ -2238,6 +2258,8 @@ begin
|
||||
if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetShift(0.0);
|
||||
end;
|
||||
FDriveLevel := CalcDriveByte;
|
||||
// QO-100 / split-LO: авто display-duplex (мониторинг своего downlink при TX).
|
||||
if E.FullDuplexTx and not FDisplayDuplex then SetDuplex(True);
|
||||
PushNetworkState;
|
||||
|
||||
Changed(rfXvtr);
|
||||
@@ -2342,7 +2364,15 @@ begin
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetDuplex(On_: Boolean);
|
||||
begin FDisplayDuplex := On_; Changed(rfDuplex); { TODO wiring } end;
|
||||
begin
|
||||
FDisplayDuplex := On_;
|
||||
// Применяем сразу: при передаче держим RX-IQ в DSP-цепи (full-duplex монитор —
|
||||
// для QO-100 слышим свой downlink через спутник). SetMOX тоже это выставляет,
|
||||
// но здесь — чтобы тумблер DUP действовал и в момент передачи.
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
FDSPEngine.KeepRXDuringTX := FDisplayDuplex;
|
||||
Changed(rfDuplex);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetWfAGC(On_: Boolean);
|
||||
begin FWfAGCEnabled := On_; Changed(rfWfAGC); end;
|
||||
@@ -2498,7 +2528,7 @@ begin
|
||||
if FTransmitting and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.ClearDUCIQQueue;
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz),
|
||||
FDriveLevel, True, True, True);
|
||||
FNetwork.SendFullHP; // 1й пакет: Alex bit27=1 (T/R relay), drive — без PTT
|
||||
FNetwork.SendPTT(True); // 2й пакет: PTT=1 после переключения реле
|
||||
@@ -2548,7 +2578,7 @@ begin
|
||||
if (not FTransmitting) and Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.SendPTT(False); // 1й пакет: PTT=0 — сначала снимаем PTT
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz),
|
||||
FDriveLevel, False, True, True);
|
||||
FNetwork.ClearDUCIQQueue;
|
||||
FNetwork.SendFullHP; // 2й пакет: Alex bit27=0 (T/R relay RX)
|
||||
@@ -2588,7 +2618,7 @@ begin
|
||||
FDSPEngine.SetDriveLevel(FDrivePercent / 100.0);
|
||||
if Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz),
|
||||
FDriveLevel, FTransmitting, True, True);
|
||||
FNetwork.SendFullHP;
|
||||
end;
|
||||
@@ -2739,7 +2769,7 @@ begin
|
||||
// Сначала отправляем Run=1 — эмулятор/железо создают ddc_specific_thread
|
||||
// (порт 1025) и rx_thread (порт 1035+) только после получения HP Run=1.
|
||||
// DDC Specific и DUC Specific отправляем ПОСЛЕ, иначе порты ещё не открыты.
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FDriveLevel, False, True, True);
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz), FDriveLevel, False, True, True);
|
||||
FNetwork.SetRunAndFreq(True, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), FDriveLevel);
|
||||
FRunning := True;
|
||||
FRXStartTime := GetTickCount64;
|
||||
@@ -2777,7 +2807,7 @@ procedure TRadioController.StopRunning;
|
||||
begin
|
||||
if FRunning then
|
||||
begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), 0, False, True, True);
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslateTX(ActiveTXFreqHz), 0, False, True, True);
|
||||
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
|
||||
FRunning := False;
|
||||
FTransmitting := False;
|
||||
|
||||
Reference in New Issue
Block a user