From ae1a7d9a6d1914849d4a183ea5f402dd2a963e5a Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Tue, 26 May 2026 17:52:02 +0300 Subject: [PATCH] Add wideband spectrum fill option --- MainForm.pas | 16 ++++++++++++--- Settings.pas | 4 ++++ SettingsForm.pas | 30 ++++++++++++++++++++-------- WidebandView.pas | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/MainForm.pas b/MainForm.pas index 15b8142..c69c4d4 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -276,6 +276,7 @@ type FShowSpectrum: Boolean; FShowWaterfall: Boolean; FShowWideband: Boolean; + FWidebandFill: Boolean; FDisplayFPS: Integer; FWaterfallDirty: Boolean; FWidebandDirty: Boolean; @@ -734,7 +735,8 @@ type procedure SendDUCSpecificFromSettings; function PullSoundCardMic(MaxN: Integer): Integer; function BuildMicLineSelectByte: Byte; - procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband: Boolean); + procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband, + WidebandFill: Boolean); procedure ApplyFPS(FPS: Integer); procedure ApplyFreqMhzDigits(Digits: Integer); @@ -1160,6 +1162,7 @@ begin Result.ShowSpectrum := FShowSpectrum; Result.ShowWaterfall := FShowWaterfall; Result.ShowWideband := FShowWideband; + Result.WidebandFill := FWidebandFill; Result.DisplayDuplex := FDisplayDuplex; Result.DisplayFPS := FDisplayFPS; Result.LightTheme := FLightTheme; @@ -1478,6 +1481,7 @@ begin FShowSpectrum := True; FShowWaterfall := True; FShowWideband := False; + FWidebandFill := False; FDisplayFPS := 60; FFreqMhzDigits := 3; FSplitterRatio := 0.40; @@ -4234,6 +4238,9 @@ begin FShowSpectrum := G_Settings.ShowSpectrum; FShowWaterfall := G_Settings.ShowWaterfall; FShowWideband := G_Settings.ShowWideband; + FWidebandFill := G_Settings.WidebandFill; + if Assigned(FWidebandView) then + FWidebandView.SetFillSpectrum(FWidebandFill); FDisplayDuplex := G_Settings.DisplayDuplex; if BtnDUP <> nil then StyleButton(BtnDUP, FDisplayDuplex); if G_Settings.DisplayFPS > 0 then @@ -7274,11 +7281,14 @@ begin end; procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall, - ShowWideband: Boolean); + ShowWideband, WidebandFill: Boolean); begin FShowSpectrum := ShowSpectrum; FShowWaterfall := ShowWaterfall; FShowWideband := ShowWideband; + FWidebandFill := WidebandFill; + if Assigned(FWidebandView) then + FWidebandView.SetFillSpectrum(FWidebandFill); if Assigned(FNetwork) and FNetwork.Connected then FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32); ResizeSpectrumPanels; @@ -7365,7 +7375,7 @@ begin FCATLastGlobal.CATTcpPort); // Перечисляем PA устройства SF.RefreshAudioDevices(FAudioOut, FAudioIn); - SF.LoadVisibility(FShowSpectrum, FShowWaterfall, FShowWideband); + SF.LoadVisibility(FShowSpectrum, FShowWaterfall, FShowWideband, FWidebandFill); SF.LoadWfAGCNF(FWfAGCEnabled, FWfNFEnabled); SF.LoadADCSettings(FDitherEnabled, FRandomEnabled); SF.LoadWebSettings(FWebEnabled, FWebPort, FWebBindAddr, FWebUser, FWebPass); diff --git a/Settings.pas b/Settings.pas index 1ba132e..b1b09be 100644 --- a/Settings.pas +++ b/Settings.pas @@ -217,6 +217,7 @@ type ShowSpectrum: Boolean; // True = draw spectrum on main form ShowWaterfall: Boolean; // True = draw waterfall on main form ShowWideband: Boolean; // True = draw raw ADC wideband pane on main form + WidebandFill: Boolean; // True = fill wideband spectrum with gradient DisplayDuplex: Boolean; // DUP: при TX показывать RX-водопад, TX-фильтр overlay'ем // --- Display FPS --- DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100) @@ -392,6 +393,7 @@ begin G.ShowSpectrum := True; G.ShowWaterfall := True; G.ShowWideband := False; + G.WidebandFill := False; G.DisplayDuplex := False; G.DisplayFPS := 60; G.FreqMhzDigits := 3; @@ -607,6 +609,7 @@ begin G.ShowSpectrum := JB(GObj,'show_spectrum',True); G.ShowWaterfall := JB(GObj,'show_waterfall',True); G.ShowWideband := JB(GObj,'show_wideband',False); + G.WidebandFill := JB(GObj,'wideband_fill',False); G.DisplayDuplex := JB(GObj,'display_duplex',False); G.DisplayFPS := JI(GObj,'display_fps',60); G.LightTheme := JB(GObj,'light_theme',False); @@ -693,6 +696,7 @@ begin JW(O,'show_spectrum',G.ShowSpectrum); JW(O,'show_waterfall',G.ShowWaterfall); JW(O,'show_wideband',G.ShowWideband); + JW(O,'wideband_fill',G.WidebandFill); JW(O,'display_duplex',G.DisplayDuplex); JW(O,'display_fps',G.DisplayFPS); JW(O,'light_theme',G.LightTheme); diff --git a/SettingsForm.pas b/SettingsForm.pas index 06ffeee..0440926 100644 --- a/SettingsForm.pas +++ b/SettingsForm.pas @@ -49,7 +49,8 @@ type TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object; TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object; TOnAudioBufferChange = procedure(BufferSize: Integer) of object; - TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall, ShowWideband: Boolean) of object; + TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall, ShowWideband, + WidebandFill: Boolean) of object; TOnFPSChange = procedure(FPS: Integer) of object; TOnThemeChange = procedure(LightTheme: Boolean) of object; TOnWfAGCNFChange = procedure(WfAGC, WfNF: Boolean) of object; @@ -121,6 +122,7 @@ type FChkShowSpectrum: TFlatCheckBox; FChkShowWaterfall: TFlatCheckBox; FChkShowWideband: TFlatCheckBox; + FChkWidebandFill: TFlatCheckBox; FCmbFPS: TFlatComboBox; FChkLightTheme: TFlatCheckBox; FCmbFreqMhzDigits: TFlatComboBox; @@ -372,7 +374,8 @@ type procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput); procedure LoadAudioBufferSize(BufferSize: Integer); - procedure LoadVisibility(ShowSpectrum, ShowWaterfall, ShowWideband: Boolean); + procedure LoadVisibility(ShowSpectrum, ShowWaterfall, ShowWideband, + WidebandFill: Boolean); procedure LoadFPS(FPS: Integer); procedure LoadLightTheme(ALight: Boolean); procedure LoadFreqMhzDigits(Digits: Integer); @@ -801,7 +804,7 @@ begin Font.Color := CLR_TEXTDIM; end; - Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 86); + Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 112); FChkShowSpectrum := TFlatCheckBox.Create(Self); FChkShowSpectrum.Parent := Grp; @@ -833,7 +836,17 @@ begin FChkShowWideband.Font.Size := 9; FChkShowWideband.OnChange := OnVisibilityChkChange; - Grp := MakeGroupPanel(FPageDisplay, 'Performance', MARGIN, 186, 650, 92); + FChkWidebandFill := TFlatCheckBox.Create(Self); + FChkWidebandFill.Parent := Grp; + FChkWidebandFill.Caption := 'Wideband gradient fill'; + FChkWidebandFill.SetBounds(PAD + (CHKW + 20) * 2, R1 + 28, CHKW, 22); + FChkWidebandFill.Checked := False; + FChkWidebandFill.Font.Color := CLR_TEXT; + FChkWidebandFill.Font.Name := UI_FONT; + FChkWidebandFill.Font.Size := 9; + FChkWidebandFill.OnChange := OnVisibilityChkChange; + + Grp := MakeGroupPanel(FPageDisplay, 'Performance', MARGIN, 212, 650, 92); MakeLbl(Grp, 'Refresh rate', PAD, R1 + 5, LW); FCmbFPS := MakeCombo(Grp, CX, R1, 140, OnFPSCmbChange); @@ -841,7 +854,7 @@ begin FCmbFPS.Items.Add(FPS_NAMES[i]); FCmbFPS.ItemIndex := 8; - Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 292, 650, 68); + Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 318, 650, 68); FChkLightTheme := TFlatCheckBox.Create(Self); FChkLightTheme.Parent := Grp; @@ -853,7 +866,7 @@ begin FChkLightTheme.Font.Size := 9; FChkLightTheme.OnChange := OnLightThemeChkChange; - Grp := MakeGroupPanel(FPageDisplay, 'VFO Display', MARGIN, 378, 650, 92); + Grp := MakeGroupPanel(FPageDisplay, 'VFO Display', MARGIN, 404, 650, 92); MakeLbl(Grp, 'Frequency range', PAD, R1 + 5, LW); FCmbFreqMhzDigits := MakeCombo(Grp, CX, R1, 240, OnFreqMhzDigitsCmbChange); @@ -2313,13 +2326,14 @@ begin end; procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall, - ShowWideband: Boolean); + ShowWideband, WidebandFill: Boolean); begin FLoading := True; try FChkShowSpectrum.Checked := ShowSpectrum; FChkShowWaterfall.Checked := ShowWaterfall; FChkShowWideband.Checked := ShowWideband; + FChkWidebandFill.Checked := WidebandFill; finally FLoading := False; end; @@ -2341,7 +2355,7 @@ begin if FLoading then Exit; if not Assigned(FOnVisibilityChange) then Exit; FOnVisibilityChange(FChkShowSpectrum.Checked, FChkShowWaterfall.Checked, - FChkShowWideband.Checked); + FChkShowWideband.Checked, FChkWidebandFill.Checked); end; procedure TSettingsForm.OnLightThemeChkChange(Sender: TObject); diff --git a/WidebandView.pas b/WidebandView.pas index 922808e..89d1dc0 100644 --- a/WidebandView.pas +++ b/WidebandView.pas @@ -39,6 +39,7 @@ type FSourceStartHz: Double; FSourceEndHz: Double; FMarkerHz: Double; + FFillSpectrum: Boolean; FWDSPAnalyzerOpen: Boolean; FWDSPAnalyzerConfigured: Boolean; FWDSPAnalyzerID: Integer; @@ -64,6 +65,7 @@ type procedure DrawHamBand(C: TCanvas; PlotW, H: Integer; F1, F2: Double; const Name: string; AColor: TColor); procedure DrawFrequencyMarker(C: TCanvas; PlotW, H: Integer); + procedure DrawSpectrumGradient(const SpPts: array of TPoint; PlotW, H: Integer); procedure UploadBitmapToGL; public constructor Create; @@ -73,6 +75,7 @@ type function SetFrequencyView(ViewStartHz, ViewEndHz, SourceStartHz, SourceEndHz: Double): Boolean; function SetMarkerHz(AHz: Double): Boolean; + procedure SetFillSpectrum(AFill: Boolean); function TryPixelToFrequency(PixelX, PanelWidth: Integer; out FreqHz: Double): Boolean; procedure SetSamples(const Samples: array of SmallInt; Count: Integer); @@ -111,6 +114,7 @@ begin FSourceStartHz := 0.0; FSourceEndHz := FSampleRateHz * 0.5; FMarkerHz := 0.0; + FFillSpectrum := False; FWDSPAnalyzerOpen := False; FWDSPAnalyzerConfigured := False; FWDSPAnalyzerID := WB_WDSP_ID; @@ -187,6 +191,13 @@ begin Result := True; end; +procedure TWidebandView.SetFillSpectrum(AFill: Boolean); +begin + if FFillSpectrum = AFill then Exit; + FFillSpectrum := AFill; + FDirty := True; +end; + function TWidebandView.TryPixelToFrequency(PixelX, PanelWidth: Integer; out FreqHz: Double): Boolean; var @@ -511,6 +522,44 @@ begin C.LineTo(X, H); end; +procedure TWidebandView.DrawSpectrumGradient(const SpPts: array of TPoint; + PlotW, H: Integer); +var + Row, Col, Alpha, YMin, Off: Integer; + RowPtr: PByte; + GradB, GradG, GradR: Byte; +begin + if (PlotW <= 0) or (H <= 0) or (Length(SpPts) < PlotW) then Exit; + YMin := H; + for Col := 0 to PlotW - 1 do + if SpPts[Col].Y < YMin then YMin := SpPts[Col].Y; + if YMin >= H then Exit; + + FBitmap.BeginUpdate(False); + try + for Row := YMin to H - 1 do + begin + Alpha := Round((1.0 - Sqr((Row - YMin) / Max(1.0, H - YMin - 1.0))) * 200); + if Alpha <= 0 then Continue; + if Alpha > 200 then Alpha := 200; + GradB := Byte(FTheme.SpecGradB * Alpha div 200); + GradG := Byte(FTheme.SpecGradG * Alpha div 200); + GradR := Byte(FTheme.SpecGradR * Alpha div 200); + RowPtr := PByte(FBitmap.ScanLine[Row]); + for Col := 0 to PlotW - 1 do + if SpPts[Col].Y <= Row then + begin + Off := Col * 4; + RowPtr[Off] := (GradB * Alpha + RowPtr[Off] * (200 - Alpha)) div 200; + RowPtr[Off + 1] := (GradG * Alpha + RowPtr[Off + 1] * (200 - Alpha)) div 200; + RowPtr[Off + 2] := (GradR * Alpha + RowPtr[Off + 2] * (200 - Alpha)) div 200; + end; + end; + finally + FBitmap.EndUpdate(False); + end; +end; + procedure TWidebandView.DrawCPU(W, H: Integer); var C: TCanvas; @@ -586,6 +635,8 @@ begin Y := EnsureRange(Y, 1, TopH); FPoints[X] := Point(X, Y); end; + if FFillSpectrum then + DrawSpectrumGradient(FPoints, PlotW, H); C.Pen.Color := TColor($0040FF80); C.Polyline(FPoints); end;