mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat: QO-100 beacon lock — stabilise LNB drift to NB transponder beacon
Click the beacon on the spectrum; the controller tracks it and continuously trims the transverter LO so the whole downlink stays put as the LNB drifts. - BeaconLock.pas: TBeaconTracker scaffold (narrowband NCO/FLL) — kept but no longer in the control path (FLL diverged on the suppressed-carrier BPSK beacon + Pluto DC offset). Measurement now uses the proven display-FFT path. - RadioController: FBeaconLockHz folded into XvtrTranslate (on top of LOError). MeasureBeaconFFT finds the beacon by POWER-WEIGHTED CENTROID of the lobe (BPSK has a suppressed carrier → broad symmetric ~2.4 kHz lobe, not a line; a peak-finder jitters, the centroid sits on the carrier). Track position in ABSOLUTE display Hz (FBeaconTrackHz) so scrolling the waterfall/centre never moves the search window off the beacon. Closed loop: e = tracked - ref, d(e)/d(LockHz) = -1 -> LockHz += gain*e; settle cooldown after each correction (display-FFT lags the LO retune -> avoids overshoot); deadband 75 Hz + centroid/prominence smoothing + lock hysteresis for a steady lock. Correction applied only after the user clicks (manual seed), so a crowded band can't pull a neighbour to the reference. - WDSPEngine: SetBeaconTracker + RX-IQ tap (tracker idle now, ~1 check/sample). - SpectrumView + GL: beacon markers — green = reference (10489.750), orange = tracked centroid. - MainForm: BEACON button (RX block, XVTR-only). Flow: BEACON -> "CLICK BCN" -> click beacon -> "L<corr> /<prom>". ServiceBeaconLock driven by MeterTimer. Reference defaults to the middle BPSK beacon 10489.750. Tested on-air against Es'hail-2: locks and holds, survives waterfall drag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,10 @@ type
|
||||
// ── Marker ────────────────────────────────────────────────────────────────
|
||||
FMarkerActive: Boolean;
|
||||
FMarkerX: Integer;
|
||||
// ── QO-100 beacon markers (диагностика лока) ──────────────────────────────
|
||||
FBeaconMarkActive: Boolean;
|
||||
FBeaconRefFreq: Double; // опорная частота маяка (где ДОЛЖЕН быть)
|
||||
FBeaconTrkFreq: Double; // где сидит трекер (NCO) сейчас
|
||||
// ── Spectrum buffer ───────────────────────────────────────────────────────
|
||||
FSpectrumBuf: array[0..1023] of Single;
|
||||
FSpectrumBufCount: Integer;
|
||||
@@ -99,6 +103,7 @@ type
|
||||
procedure SetFillSpectrum(V: Boolean);
|
||||
procedure DrawSpectrumGradient(const SpPts: array of TPoint; W, H: Integer);
|
||||
procedure DrawMarkerLine(C: TCanvas; W, H: Integer);
|
||||
procedure DrawBeaconMarkers(C: TCanvas; W, H: Integer);
|
||||
procedure BlendBand(X1, X2, H: Integer; R, G, B, Alpha: Byte);
|
||||
procedure CopyGridToSpectrum(W, H: Integer);
|
||||
procedure DrawADCOverloadOverlay(C: TCanvas; W, H: Integer);
|
||||
@@ -182,6 +187,10 @@ type
|
||||
property MarkerActive: Boolean read FMarkerActive write SetMarkerActive;
|
||||
property MarkerX: Integer read FMarkerX write SetMarkerX;
|
||||
|
||||
// QO-100 beacon-маркеры: опорная частота (зелёная) + позиция трекера (оранж).
|
||||
// Спектр перерисовывается каждый кадр, поэтому отдельный invalidate не нужен.
|
||||
procedure SetBeaconMarkers(Active: Boolean; RefHz, TrkHz: Double);
|
||||
|
||||
// ── S-метр ────────────────────────────────────────────────────────────────
|
||||
property LastSMeter: Double read GetLastSMeter write SetLastSMeter;
|
||||
property SMeterPeak: Double read GetSMeterPeak write SetSMeterPeak;
|
||||
@@ -485,6 +494,32 @@ begin
|
||||
C.TextOut(MX - 4 - C.TextWidth(MarkerLbl), 4, MarkerLbl);
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.SetBeaconMarkers(Active: Boolean; RefHz, TrkHz: Double);
|
||||
begin
|
||||
FBeaconMarkActive := Active;
|
||||
FBeaconRefFreq := RefHz;
|
||||
FBeaconTrkFreq := TrkHz;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.DrawBeaconMarkers(C: TCanvas; W, H: Integer);
|
||||
// Две вертикали: опорная частота маяка (зелёная пунктир) и текущая позиция
|
||||
// трекера (оранжевая сплошная). Расхождение видно глазом → понятно, на пик
|
||||
// сел трекер или на шум.
|
||||
procedure Vline(FreqHz: Double; Col: TColor; Style: TPenStyle);
|
||||
var x: Integer;
|
||||
begin
|
||||
if FSpanHz <= 0 then Exit;
|
||||
x := Round((FreqHz - (FCenterFreq - FSpanHz / 2)) / FSpanHz * W);
|
||||
if (x < 0) or (x >= W) then Exit;
|
||||
C.Pen.Color := Col; C.Pen.Width := 1; C.Pen.Style := Style;
|
||||
C.MoveTo(x, 0); C.LineTo(x, H);
|
||||
end;
|
||||
begin
|
||||
Vline(FBeaconRefFreq, clLime, psDot); // где маяк ДОЛЖЕН быть
|
||||
Vline(FBeaconTrkFreq, TColor($000AA5FF), psSolid); // где сидит трекер (оранж)
|
||||
C.Pen.Style := psSolid;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.BlendBand(X1, X2, H: Integer; R, G, B, Alpha: Byte);
|
||||
var
|
||||
Y, X, InvA: Integer;
|
||||
@@ -940,6 +975,7 @@ begin
|
||||
end;
|
||||
|
||||
if FMarkerActive then DrawMarkerLine(C, W, H);
|
||||
if FBeaconMarkActive then DrawBeaconMarkers(C, W, H);
|
||||
DrawADCOverloadOverlay(C, W, H);
|
||||
if Assigned(FSampleRateOverlay) then
|
||||
FSampleRateOverlay.DrawOverlay(FSpectrumBitmap, C, W, H);
|
||||
|
||||
Reference in New Issue
Block a user