mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat: QO-100 central beacon BPSK decoder (Milestone 1 — stable demod)
Декодер центрального маяка QO-100 (10489.750, AO-40 BPSK 400 бод): фронтенд до мягких символов + констелляция, с устойчивым захватом несущей/символа. Цепочка (BeaconDecoder.pas): NCO-снос смещения → boxcar-децим до 9600 → RRC матч-фильтр → AGC-нормировка → Gardner timing → Costas BPSK с frequency offload в пред-RRC NCO. squaring-FFT оставлена индикатором несущей (prom). Ключевые решения по итогам эфирной отладки: - invert=True для тракта QO-100 (LNB+трансвертер зеркалит спектр). - Декодер развязан от beacon-lock (тот ретюнил железо, рвал IQ). - AGC-нормировка петель: сигнал ~-60 dBFS замораживал Costas/Gardner. - Frequency offload: накопл. снос Costas стекает в FResidInc (без потолка ±π/символ) → держит ошибку наведения (1пкс≈300Гц) + дрейф LNB. Интеграция: тап IQ в WDSPEngine.PushIQItemToDSP; RadioController владеет декодером, клик-наведение (ПКМ BEACON + ЛКМ/Shift+ЛКМ по спектру); узкий фильтр-маркер в SpectrumView/GL; окно констелляции BeaconScopeForm; файловый лог HOME/ewsdr_beacon.log для диагностики. Milestone 2 (далее): FEC — Viterbi k=7 + RS(160,128) + интерливер + дескремблер. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+30
-3
@@ -77,6 +77,10 @@ type
|
||||
FBeaconMarkActive: Boolean;
|
||||
FBeaconRefFreq: Double; // опорная частота маяка (где ДОЛЖЕН быть)
|
||||
FBeaconTrkFreq: Double; // отслеживаемый центроид маяка сейчас
|
||||
// узкий фильтр-маркер декодера (наведение пользователем)
|
||||
FBeaconDecActive: Boolean;
|
||||
FBeaconDecFreq: Double;
|
||||
FBeaconDecHalf: Double; // полуширина фильтра, Гц
|
||||
// ── Spectrum buffer ───────────────────────────────────────────────────────
|
||||
FSpectrumBuf: array[0..1023] of Single;
|
||||
FSpectrumBufCount: Integer;
|
||||
@@ -190,6 +194,7 @@ type
|
||||
// QO-100 beacon-маркеры: опорная частота (зелёная) + позиция трекера (оранж).
|
||||
// Спектр перерисовывается каждый кадр, поэтому отдельный invalidate не нужен.
|
||||
procedure SetBeaconMarkers(Active: Boolean; RefHz, TrkHz: Double);
|
||||
procedure SetBeaconDecMarker(Active: Boolean; FreqHz, HalfHz: Double);
|
||||
|
||||
// ── S-метр ────────────────────────────────────────────────────────────────
|
||||
property LastSMeter: Double read GetLastSMeter write SetLastSMeter;
|
||||
@@ -501,6 +506,13 @@ begin
|
||||
FBeaconTrkFreq := TrkHz;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.SetBeaconDecMarker(Active: Boolean; FreqHz, HalfHz: Double);
|
||||
begin
|
||||
FBeaconDecActive := Active;
|
||||
FBeaconDecFreq := FreqHz;
|
||||
FBeaconDecHalf := HalfHz;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.DrawBeaconMarkers(C: TCanvas; W, H: Integer);
|
||||
// Две вертикали: опорная частота маяка (зелёная пунктир) и отслеживаемый
|
||||
// центроид (оранжевая сплошная). Расхождение видно глазом → понятно, сел ли
|
||||
@@ -514,9 +526,24 @@ procedure TSpectrumView.DrawBeaconMarkers(C: TCanvas; W, H: Integer);
|
||||
C.Pen.Color := Col; C.Pen.Width := 1; C.Pen.Style := Style;
|
||||
C.MoveTo(x, 0); C.LineTo(x, H);
|
||||
end;
|
||||
var
|
||||
xc, x1, x2: Integer;
|
||||
begin
|
||||
Vline(FBeaconRefFreq, clLime, psDot); // где маяк ДОЛЖЕН быть
|
||||
Vline(FBeaconTrkFreq, TColor($000AA5FF), psSolid); // отслеживаемый центроид (оранж)
|
||||
if FBeaconMarkActive then
|
||||
begin
|
||||
Vline(FBeaconRefFreq, clLime, psDot); // где маяк ДОЛЖЕН быть
|
||||
Vline(FBeaconTrkFreq, TColor($000AA5FF), psSolid); // отслеживаемый центроид (оранж)
|
||||
end;
|
||||
// Узкий фильтр-маркер декодера: жёлтые грани полосы + центральная линия.
|
||||
if FBeaconDecActive and (FSpanHz > 0) then
|
||||
begin
|
||||
xc := Round((FBeaconDecFreq - (FCenterFreq - FSpanHz / 2)) / FSpanHz * W);
|
||||
x1 := Round((FBeaconDecFreq - FBeaconDecHalf - (FCenterFreq - FSpanHz / 2)) / FSpanHz * W);
|
||||
x2 := Round((FBeaconDecFreq + FBeaconDecHalf - (FCenterFreq - FSpanHz / 2)) / FSpanHz * W);
|
||||
BlendBand(x1, x2, H, $FF, $D0, $20, 48); // полупрозрачная полоса
|
||||
C.Pen.Color := TColor($0020D0FF); C.Pen.Width := 1; C.Pen.Style := psSolid;
|
||||
if (xc >= 0) and (xc < W) then begin C.MoveTo(xc, 0); C.LineTo(xc, H); end;
|
||||
end;
|
||||
C.Pen.Style := psSolid;
|
||||
end;
|
||||
|
||||
@@ -975,7 +1002,7 @@ begin
|
||||
end;
|
||||
|
||||
if FMarkerActive then DrawMarkerLine(C, W, H);
|
||||
if FBeaconMarkActive then DrawBeaconMarkers(C, W, H);
|
||||
if FBeaconMarkActive or FBeaconDecActive 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