mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
Align spectrum grid and ruler to FM tuning step
When FM mode is active, vertical grid lines and frequency labels snap to FM channel boundaries (FM_STEP_HZ) instead of the fixed 8-division layout. Grid and label density auto-adapt: lines thinner than 4px are thinned, labels are spaced so they never overlap (short tick shown between labeled lines). Grid cache invalidates on center-freq, step, or span changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2797,6 +2797,10 @@ begin
|
||||
FSpecView.SpanHz := FSpanHz;
|
||||
FSpecView.Mode := FMode;
|
||||
FSpecView.FilterBW := FFilterBW;
|
||||
if FMode = MODE_FM then
|
||||
FSpecView.FMGridStepHz := FM_STEP_HZ[FFMStepIdx]
|
||||
else
|
||||
FSpecView.FMGridStepHz := 0;
|
||||
// TX overlay следует за активной TX-частотой (учитывает split). При CTUN
|
||||
// спектр TX будет отрисован на VFO, а не в центре дисплея.
|
||||
FSpecView.TXFreq := ActiveTXFreqHz;
|
||||
@@ -4805,6 +4809,8 @@ begin
|
||||
if FBtnFMSteps[Idx] <> nil then
|
||||
FBtnFMSteps[Idx].Active := True;
|
||||
if FWebServer <> nil then FWebServer.FMStepIdx := Idx;
|
||||
if (FMode = MODE_FM) and (FSpecView <> nil) then
|
||||
FSpecView.FMGridStepHz := FM_STEP_HZ[Idx];
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnFMStepClick(Sender: TObject);
|
||||
|
||||
+93
-2
@@ -38,6 +38,9 @@ type
|
||||
FGridBitmap: TBitmap;
|
||||
FGridBitmapW: Integer;
|
||||
FGridBitmapH: Integer;
|
||||
FFMGridLastCenter: Double;
|
||||
FFMGridLastStepHz: Double;
|
||||
FFMGridLastSpan: Double;
|
||||
FRulerBitmap: TBitmap;
|
||||
FSpecGradImg: TLazIntfImage;
|
||||
FSpecGradBmp: TBitmap;
|
||||
@@ -79,6 +82,7 @@ type
|
||||
FSpecRefLevel: Double;
|
||||
FSpecRange: Double;
|
||||
FSpecGridStep: Double;
|
||||
FFMGridStepHz: Double;
|
||||
|
||||
// ── TX overlay (Thetis-style: при TX данные в FSpectrumBuf приходят
|
||||
// от TX-анализатора @ FTXSpanHz Гц вокруг FTXFreq, и должны быть
|
||||
@@ -128,6 +132,7 @@ type
|
||||
// ── Приватные методы рендеринга ───────────────────────────────────────────
|
||||
function ActiveVfoFreq: Double;
|
||||
function ScaleX(X, Total, Width: Integer): Integer;
|
||||
procedure SetFMGridStepHz(V: Double);
|
||||
procedure DrawSpectrumGradient(const SpPts: array of TPoint; W, H: Integer);
|
||||
procedure DrawMarkerLine(C: TCanvas; W, H: Integer);
|
||||
// Альфа-blending вертикальной полосы поверх FSpectrumBitmap (pf32bit, BGRA).
|
||||
@@ -167,6 +172,7 @@ type
|
||||
property SpecRefLevel: Double read FSpecRefLevel write FSpecRefLevel;
|
||||
property SpecRange: Double read FSpecRange write FSpecRange;
|
||||
property SpecGridStep: Double read FSpecGridStep write FSpecGridStep;
|
||||
property FMGridStepHz: Double read FFMGridStepHz write SetFMGridStepHz;
|
||||
|
||||
// ── TX overlay ────────────────────────────────────────────────────────────
|
||||
property TXMode: Boolean read FTXMode write FTXMode;
|
||||
@@ -404,6 +410,8 @@ begin
|
||||
FWfIntfImg := nil;
|
||||
FWfBitmapW := 0; FWfBitmapH := 0;
|
||||
FGridBitmapW := 0; FGridBitmapH := 0;
|
||||
FFMGridLastCenter := -1.0; FFMGridLastStepHz := -1.0; FFMGridLastSpan := -1.0;
|
||||
FFMGridStepHz := 0.0;
|
||||
FSpPtsLen := 0;
|
||||
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
|
||||
// Водопад AGC defaults
|
||||
@@ -467,6 +475,16 @@ begin
|
||||
else Result := Round(X / Total * Width);
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.SetFMGridStepHz(V: Double);
|
||||
begin
|
||||
if Abs(FFMGridStepHz - V) > 0.5 then
|
||||
begin
|
||||
FFMGridStepHz := V;
|
||||
InvalidateGridCache;
|
||||
InvalidateRulerCache;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSpectrumView.DrawMarkerLine(C: TCanvas; W, H: Integer);
|
||||
var MX: Integer; MarkerFreq: Double; MarkerLbl: string;
|
||||
begin
|
||||
@@ -734,6 +752,8 @@ var
|
||||
S0, S1: Integer;
|
||||
InvRange: Double;
|
||||
LabelBandW, SrcCount: Integer;
|
||||
GridFreqS, GridLine, pixPerStep: Double;
|
||||
N, gridMult: Integer;
|
||||
begin
|
||||
if FSpectrumBitmap = nil then Exit;
|
||||
W := FSpectrumBitmap.Width; H := FSpectrumBitmap.Height;
|
||||
@@ -744,9 +764,16 @@ begin
|
||||
InvRange := 1.0 / (DBmax - DBmin);
|
||||
|
||||
// ── 1. Фон+сетка из кэша ──────────────────────────────────────────────────
|
||||
if (FGridBitmapW <> W) or (FGridBitmapH <> H) then
|
||||
if (FGridBitmapW <> W) or (FGridBitmapH <> H) or
|
||||
((FFMGridStepHz > 0) and
|
||||
((Abs(FCenterFreq - FFMGridLastCenter) > 0.5) or
|
||||
(FFMGridStepHz <> FFMGridLastStepHz) or
|
||||
(Abs(FSpanHz - FFMGridLastSpan) > 1.0))) then
|
||||
begin
|
||||
FGridBitmapW := W; FGridBitmapH := H;
|
||||
FFMGridLastCenter := FCenterFreq;
|
||||
FFMGridLastStepHz := FFMGridStepHz;
|
||||
FFMGridLastSpan := FSpanHz;
|
||||
FGridBitmap.SetSize(W, H);
|
||||
GC := FGridBitmap.Canvas;
|
||||
PaintVerticalGradient(GC, W, H, FTheme.SpecGradTop, FTheme.SpecGradBot);
|
||||
@@ -767,6 +794,35 @@ begin
|
||||
GC.TextOut(1, Yp - 9, Format('%4.0f', [dB]));
|
||||
dB := dB - FSpecGridStep;
|
||||
end;
|
||||
if FFMGridStepHz > 0 then
|
||||
begin
|
||||
GridFreqS := FCenterFreq - FSpanHz / 2;
|
||||
pixPerStep := W * FFMGridStepHz / FSpanHz;
|
||||
if pixPerStep >= 1.0 then
|
||||
gridMult := Max(1, Ceil(4.0 / pixPerStep))
|
||||
else
|
||||
gridMult := 0;
|
||||
if gridMult > 0 then
|
||||
begin
|
||||
N := Ceil(GridFreqS / FFMGridStepHz);
|
||||
GridLine := N * FFMGridStepHz;
|
||||
while GridLine <= FCenterFreq + FSpanHz / 2 + 0.5 do
|
||||
begin
|
||||
if (N mod gridMult) = 0 then
|
||||
begin
|
||||
GX := Round((GridLine - GridFreqS) / FSpanHz * W);
|
||||
if (GX >= 0) and (GX < W) then
|
||||
begin
|
||||
GC.Pen.Color := FTheme.SpecGrid;
|
||||
GC.MoveTo(GX, 0); GC.LineTo(GX, H);
|
||||
end;
|
||||
end;
|
||||
Inc(N);
|
||||
GridLine := GridLine + FFMGridStepHz;
|
||||
end;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
for i := 0 to 8 do
|
||||
begin
|
||||
GX := ScaleX(i, 8, W);
|
||||
@@ -774,6 +830,7 @@ begin
|
||||
GC.MoveTo(GX, 0); GC.LineTo(GX, H);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
LCLIntf.BitBlt(C.Handle, 0, 0, W, H, FGridBitmap.Canvas.Handle, 0, 0, $CC0020);
|
||||
|
||||
// ── 2. Полоса фильтра ─────────────────────────────────────────────────────
|
||||
@@ -1074,8 +1131,9 @@ end;
|
||||
procedure TSpectrumView.DrawRuler;
|
||||
var
|
||||
C: TCanvas; W, H, i, X: Integer;
|
||||
FreqStart, FreqHz: Double; VfoX: Integer;
|
||||
FreqStart, FreqHz, GridLine, pixPerStep: Double; VfoX: Integer;
|
||||
Lbl: string; TW: Integer;
|
||||
N, labelMult: Integer;
|
||||
begin
|
||||
if FPbRuler = nil then Exit;
|
||||
W := FPbRuler.Width; H := FPbRuler.Height;
|
||||
@@ -1094,6 +1152,38 @@ begin
|
||||
C.Font.Name := 'Courier New'; C.Font.Size := 7;
|
||||
C.Brush.Style := bsClear;
|
||||
FreqStart := FCenterFreq - FSpanHz / 2;
|
||||
if FFMGridStepHz > 0 then
|
||||
begin
|
||||
pixPerStep := W * FFMGridStepHz / FSpanHz;
|
||||
if pixPerStep >= 1.0 then
|
||||
labelMult := Max(1, Ceil((C.TextWidth('000.000') + 6) / pixPerStep))
|
||||
else
|
||||
labelMult := MaxInt;
|
||||
N := Ceil(FreqStart / FFMGridStepHz);
|
||||
GridLine := N * FFMGridStepHz;
|
||||
while GridLine <= FCenterFreq + FSpanHz / 2 + 0.5 do
|
||||
begin
|
||||
X := Round((GridLine - FreqStart) / FSpanHz * W);
|
||||
if (X >= 0) and (X < W) then
|
||||
begin
|
||||
C.Pen.Color := FTheme.RulerBorder;
|
||||
if (N mod labelMult) = 0 then
|
||||
begin
|
||||
C.MoveTo(X, 0); C.LineTo(X, H div 2);
|
||||
Lbl := Format('%.3f', [GridLine / 1e6]);
|
||||
TW := C.TextWidth(Lbl);
|
||||
C.Font.Color := FTheme.RulerText;
|
||||
C.TextOut(X - TW div 2, H div 2 - 1, Lbl);
|
||||
end else
|
||||
begin
|
||||
C.MoveTo(X, 0); C.LineTo(X, H div 4);
|
||||
end;
|
||||
end;
|
||||
Inc(N);
|
||||
GridLine := GridLine + FFMGridStepHz;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
for i := 0 to 8 do
|
||||
begin
|
||||
X := ScaleX(i, 8, W);
|
||||
@@ -1105,6 +1195,7 @@ begin
|
||||
C.Font.Color := FTheme.RulerText;
|
||||
C.TextOut(X - TW div 2, H div 2 - 1, Lbl);
|
||||
end;
|
||||
end;
|
||||
VfoX := Round((ActiveVfoFreq - FCenterFreq + FSpanHz/2) / FSpanHz * W);
|
||||
if (VfoX >= 0) and (VfoX < W) then
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user