From daa8043a2340aff93074c4f19d45ca39c0c89af7 Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Tue, 26 May 2026 18:01:58 +0300 Subject: [PATCH] Render wideband spectrum with OpenGL primitives --- WidebandView.pas | 170 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 142 insertions(+), 28 deletions(-) diff --git a/WidebandView.pas b/WidebandView.pas index 89d1dc0..97e4e32 100644 --- a/WidebandView.pas +++ b/WidebandView.pas @@ -30,6 +30,7 @@ type FPoints: array of TPoint; FGLTex: GLuint; FDirty: Boolean; + FGLBackgroundDirty: Boolean; FSampleRateHz: Double; FRefLevel: Double; FRange: Double; @@ -51,10 +52,14 @@ type function ComputeSpectrumWDSP(const Samples: array of SmallInt; Count: Integer): Boolean; procedure CloseWDSPAnalyzer; - procedure DrawCPU(W, H: Integer); + function BuildSpectrumPoints(PlotW, H: Integer): Boolean; + procedure DrawCPU(W, H: Integer; IncludeSpectrum: Boolean = True); procedure PaintCPU(C: TCanvas); procedure PaintGL(C: TOpenGLControl); procedure ColorToGL(AColor: TColor; out R, G, B: GLFloat); + procedure DrawSpectrumGL(PlotW, H: Integer); + procedure DrawHamBandGL(PlotW, H: Integer; F1, F2: Double; AColor: TColor); + procedure DrawHamBandsGL(PlotW, H: Integer); function SourceFreqToBin(FreqHz: Double; BinCount: Integer): Integer; function FreqToX(FreqHz: Double; W: Integer): Integer; function GridStepHz(W: Integer): Double; @@ -123,6 +128,7 @@ begin SetLength(FWDSPPixels, WB_WDSP_PIXELS); FGLTex := 0; FDirty := True; + FGLBackgroundDirty := True; end; destructor TWidebandView.Destroy; @@ -139,6 +145,7 @@ procedure TWidebandView.SetTheme(const T: TAppTheme); begin FTheme := T; FDirty := True; + FGLBackgroundDirty := True; end; procedure TWidebandView.SetSampleRateHz(AHz: Double); @@ -180,6 +187,7 @@ begin FSourceStartHz := SourceStartHz; FSourceEndHz := SourceEndHz; FDirty := True; + FGLBackgroundDirty := True; Result := True; end; @@ -218,7 +226,10 @@ begin W := Max(1, W); H := Max(1, H); if (FBitmap.Width <> W) or (FBitmap.Height <> H) then + begin FBitmap.SetSize(W, H); + FGLBackgroundDirty := True; + end; end; procedure TWidebandView.SetBitmapSize(W, H: Integer); @@ -560,11 +571,42 @@ begin end; end; -procedure TWidebandView.DrawCPU(W, H: Integer); +function TWidebandView.BuildSpectrumPoints(PlotW, H: Integer): Boolean; +var + X, Y, Bin, N, TopH: Integer; + DB, InvRange, SrcHz, Nyq: Double; +begin + Result := False; + N := Length(FData); + if (N <= 1) or (PlotW <= 0) or (H <= 0) then Exit; + TopH := Max(1, H - 1); + InvRange := 1.0 / Max(1.0, FRange); + SetLength(FPoints, PlotW); + Nyq := FSampleRateHz * 0.5; + if Nyq <= 0.0 then Nyq := 61440000.0; + for X := 0 to PlotW - 1 do + begin + SrcHz := FSourceStartHz + + X / Max(1, PlotW - 1) * (FSourceEndHz - FSourceStartHz); + if (SrcHz < 0.0) or (SrcHz > Nyq) then + DB := -200.0 + else + begin + Bin := SourceFreqToBin(SrcHz, N); + DB := FData[Bin]; + end; + Y := Round((FRefLevel - DB) * InvRange * TopH); + Y := EnsureRange(Y, 1, TopH); + FPoints[X] := Point(X, Y); + end; + Result := True; +end; + +procedure TWidebandView.DrawCPU(W, H: Integer; IncludeSpectrum: Boolean); var C: TCanvas; - X, Y, Bin, N, TopH, PlotW, ScaleX: Integer; - DB, DBMin, InvRange, StepHz, FreqHz, SrcHz, Nyq: Double; + X, Y, TopH, PlotW, ScaleX: Integer; + DB, DBMin, InvRange, StepHz, FreqHz: Double; begin EnsureBitmap(W, H); C := FBitmap.Canvas; @@ -614,27 +656,8 @@ begin DB := DB - 20.0; end; - N := Length(FData); - if N > 1 then + if IncludeSpectrum and BuildSpectrumPoints(PlotW, H) then begin - SetLength(FPoints, PlotW); - Nyq := FSampleRateHz * 0.5; - if Nyq <= 0.0 then Nyq := 61440000.0; - for X := 0 to PlotW - 1 do - begin - SrcHz := FSourceStartHz + - X / Max(1, PlotW - 1) * (FSourceEndHz - FSourceStartHz); - if (SrcHz < 0.0) or (SrcHz > Nyq) then - DB := -200.0 - else - begin - Bin := SourceFreqToBin(SrcHz, N); - DB := FData[Bin]; - end; - Y := Round((FRefLevel - DB) * InvRange * TopH); - Y := EnsureRange(Y, 1, TopH); - FPoints[X] := Point(X, Y); - end; if FFillSpectrum then DrawSpectrumGradient(FPoints, PlotW, H); C.Pen.Color := TColor($0040FF80); @@ -645,7 +668,10 @@ begin C.Font.Color := FTheme.Text; C.TextOut(6, 4, 'Wideband'); - FDirty := False; + if IncludeSpectrum then + FDirty := False + else + FGLBackgroundDirty := False; end; procedure TWidebandView.DrawRuler; @@ -716,6 +742,90 @@ begin B := ((AColor shr 16) and $FF) / 255.0; end; +procedure TWidebandView.DrawSpectrumGL(PlotW, H: Integer); +var + I: Integer; + R, G, B: GLFloat; + ATop, ABot: GLFloat; +begin + if not BuildSpectrumPoints(PlotW, H) then Exit; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if FFillSpectrum then + begin + R := FTheme.SpecGradR / 255.0; + G := FTheme.SpecGradG / 255.0; + B := FTheme.SpecGradB / 255.0; + glBegin(GL_TRIANGLE_STRIP); + for I := 0 to PlotW - 1 do + begin + ATop := 0.44; + ABot := 0.02; + glColor4f(R, G, B, ATop); + glVertex2f(FPoints[I].X, H - FPoints[I].Y); + glColor4f(R, G, B, ABot); + glVertex2f(FPoints[I].X, 0); + end; + glEnd; + end; + + glColor4f(0.25, 1.0, 0.50, 1.0); + glBegin(GL_LINE_STRIP); + for I := 0 to PlotW - 1 do + glVertex2f(FPoints[I].X, H - FPoints[I].Y); + glEnd; + + glDisable(GL_BLEND); +end; + +procedure TWidebandView.DrawHamBandGL(PlotW, H: Integer; F1, F2: Double; + AColor: TColor); +var + X1, X2: Integer; + R, G, B: GLFloat; +begin + X1 := FreqToX(F1, PlotW); + X2 := FreqToX(F2, PlotW); + if (X2 <= 0) or (X1 >= PlotW) then Exit; + X1 := EnsureRange(X1, 0, PlotW - 1); + X2 := EnsureRange(X2, 0, PlotW - 1); + if X2 <= X1 then Exit; + ColorToGL(AColor, R, G, B); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(R, G, B, 0.34); + glBegin(GL_QUADS); + glVertex2f(X1, 0); + glVertex2f(X2 + 1, 0); + glVertex2f(X2 + 1, H); + glVertex2f(X1, H); + glEnd; + glColor4f(R, G, B, 0.46); + glBegin(GL_QUADS); + glVertex2f(X1, H - Min(H, 15)); + glVertex2f(X2 + 1, H - Min(H, 15)); + glVertex2f(X2 + 1, H); + glVertex2f(X1, H); + glEnd; + glDisable(GL_BLEND); +end; + +procedure TWidebandView.DrawHamBandsGL(PlotW, H: Integer); +begin + DrawHamBandGL(PlotW, H, 1810000, 2000000, TColor($003B8F5A)); + DrawHamBandGL(PlotW, H, 3500000, 3800000, TColor($004D8F3B)); + DrawHamBandGL(PlotW, H, 5258500, 5403500, TColor($00688F3B)); + DrawHamBandGL(PlotW, H, 7000000, 7300000, TColor($00808D34)); + DrawHamBandGL(PlotW, H, 10100000, 10150000, TColor($008F7834)); + DrawHamBandGL(PlotW, H, 14000000, 14350000, TColor($008F5D34)); + DrawHamBandGL(PlotW, H, 18068000, 18168000, TColor($008F4934)); + DrawHamBandGL(PlotW, H, 21000000, 21450000, TColor($008C3E58)); + DrawHamBandGL(PlotW, H, 24890000, 24990000, TColor($007A3E8C)); + DrawHamBandGL(PlotW, H, 28000000, 29700000, TColor($005B4B9A)); + DrawHamBandGL(PlotW, H, 50000000, 51990000, TColor($003C729A)); +end; + procedure TWidebandView.UploadBitmapToGL; var X, Y, I: Integer; @@ -767,9 +877,9 @@ begin SetBitmapSize(W, H); NeedUpload := True; end; - if FDirty then + if FGLBackgroundDirty then begin - DrawCPU(W, H); + DrawCPU(W, H, False); NeedUpload := True; end; if FGLTex = 0 then @@ -797,9 +907,13 @@ begin glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + PlotW := Max(1, W - WB_DB_SCALE_W); + DrawSpectrumGL(PlotW, H); + DrawHamBandsGL(PlotW, H); + FDirty := False; + if (FMarkerHz >= FViewStartHz) and (FMarkerHz <= FViewEndHz) then begin - PlotW := Max(1, W - WB_DB_SCALE_W); X := EnsureRange(FreqToX(FMarkerHz, PlotW), 0, PlotW - 1); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);