Add wideband spectrum fill option

This commit is contained in:
2026-05-26 17:52:02 +03:00
parent f61290ad3d
commit ae1a7d9a6d
4 changed files with 90 additions and 11 deletions
+13 -3
View File
@@ -276,6 +276,7 @@ type
FShowSpectrum: Boolean; FShowSpectrum: Boolean;
FShowWaterfall: Boolean; FShowWaterfall: Boolean;
FShowWideband: Boolean; FShowWideband: Boolean;
FWidebandFill: Boolean;
FDisplayFPS: Integer; FDisplayFPS: Integer;
FWaterfallDirty: Boolean; FWaterfallDirty: Boolean;
FWidebandDirty: Boolean; FWidebandDirty: Boolean;
@@ -734,7 +735,8 @@ type
procedure SendDUCSpecificFromSettings; procedure SendDUCSpecificFromSettings;
function PullSoundCardMic(MaxN: Integer): Integer; function PullSoundCardMic(MaxN: Integer): Integer;
function BuildMicLineSelectByte: Byte; function BuildMicLineSelectByte: Byte;
procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband: Boolean); procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband,
WidebandFill: Boolean);
procedure ApplyFPS(FPS: Integer); procedure ApplyFPS(FPS: Integer);
procedure ApplyFreqMhzDigits(Digits: Integer); procedure ApplyFreqMhzDigits(Digits: Integer);
@@ -1160,6 +1162,7 @@ begin
Result.ShowSpectrum := FShowSpectrum; Result.ShowSpectrum := FShowSpectrum;
Result.ShowWaterfall := FShowWaterfall; Result.ShowWaterfall := FShowWaterfall;
Result.ShowWideband := FShowWideband; Result.ShowWideband := FShowWideband;
Result.WidebandFill := FWidebandFill;
Result.DisplayDuplex := FDisplayDuplex; Result.DisplayDuplex := FDisplayDuplex;
Result.DisplayFPS := FDisplayFPS; Result.DisplayFPS := FDisplayFPS;
Result.LightTheme := FLightTheme; Result.LightTheme := FLightTheme;
@@ -1478,6 +1481,7 @@ begin
FShowSpectrum := True; FShowSpectrum := True;
FShowWaterfall := True; FShowWaterfall := True;
FShowWideband := False; FShowWideband := False;
FWidebandFill := False;
FDisplayFPS := 60; FDisplayFPS := 60;
FFreqMhzDigits := 3; FFreqMhzDigits := 3;
FSplitterRatio := 0.40; FSplitterRatio := 0.40;
@@ -4234,6 +4238,9 @@ begin
FShowSpectrum := G_Settings.ShowSpectrum; FShowSpectrum := G_Settings.ShowSpectrum;
FShowWaterfall := G_Settings.ShowWaterfall; FShowWaterfall := G_Settings.ShowWaterfall;
FShowWideband := G_Settings.ShowWideband; FShowWideband := G_Settings.ShowWideband;
FWidebandFill := G_Settings.WidebandFill;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FWidebandFill);
FDisplayDuplex := G_Settings.DisplayDuplex; FDisplayDuplex := G_Settings.DisplayDuplex;
if BtnDUP <> nil then StyleButton(BtnDUP, FDisplayDuplex); if BtnDUP <> nil then StyleButton(BtnDUP, FDisplayDuplex);
if G_Settings.DisplayFPS > 0 then if G_Settings.DisplayFPS > 0 then
@@ -7274,11 +7281,14 @@ begin
end; end;
procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall, procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall,
ShowWideband: Boolean); ShowWideband, WidebandFill: Boolean);
begin begin
FShowSpectrum := ShowSpectrum; FShowSpectrum := ShowSpectrum;
FShowWaterfall := ShowWaterfall; FShowWaterfall := ShowWaterfall;
FShowWideband := ShowWideband; FShowWideband := ShowWideband;
FWidebandFill := WidebandFill;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FWidebandFill);
if Assigned(FNetwork) and FNetwork.Connected then if Assigned(FNetwork) and FNetwork.Connected then
FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32); FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32);
ResizeSpectrumPanels; ResizeSpectrumPanels;
@@ -7365,7 +7375,7 @@ begin
FCATLastGlobal.CATTcpPort); FCATLastGlobal.CATTcpPort);
// Перечисляем PA устройства // Перечисляем PA устройства
SF.RefreshAudioDevices(FAudioOut, FAudioIn); SF.RefreshAudioDevices(FAudioOut, FAudioIn);
SF.LoadVisibility(FShowSpectrum, FShowWaterfall, FShowWideband); SF.LoadVisibility(FShowSpectrum, FShowWaterfall, FShowWideband, FWidebandFill);
SF.LoadWfAGCNF(FWfAGCEnabled, FWfNFEnabled); SF.LoadWfAGCNF(FWfAGCEnabled, FWfNFEnabled);
SF.LoadADCSettings(FDitherEnabled, FRandomEnabled); SF.LoadADCSettings(FDitherEnabled, FRandomEnabled);
SF.LoadWebSettings(FWebEnabled, FWebPort, FWebBindAddr, FWebUser, FWebPass); SF.LoadWebSettings(FWebEnabled, FWebPort, FWebBindAddr, FWebUser, FWebPass);
+4
View File
@@ -217,6 +217,7 @@ type
ShowSpectrum: Boolean; // True = draw spectrum on main form ShowSpectrum: Boolean; // True = draw spectrum on main form
ShowWaterfall: Boolean; // True = draw waterfall on main form ShowWaterfall: Boolean; // True = draw waterfall on main form
ShowWideband: Boolean; // True = draw raw ADC wideband pane 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'ем DisplayDuplex: Boolean; // DUP: при TX показывать RX-водопад, TX-фильтр overlay'ем
// --- Display FPS --- // --- Display FPS ---
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100) DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
@@ -392,6 +393,7 @@ begin
G.ShowSpectrum := True; G.ShowSpectrum := True;
G.ShowWaterfall := True; G.ShowWaterfall := True;
G.ShowWideband := False; G.ShowWideband := False;
G.WidebandFill := False;
G.DisplayDuplex := False; G.DisplayDuplex := False;
G.DisplayFPS := 60; G.DisplayFPS := 60;
G.FreqMhzDigits := 3; G.FreqMhzDigits := 3;
@@ -607,6 +609,7 @@ begin
G.ShowSpectrum := JB(GObj,'show_spectrum',True); G.ShowSpectrum := JB(GObj,'show_spectrum',True);
G.ShowWaterfall := JB(GObj,'show_waterfall',True); G.ShowWaterfall := JB(GObj,'show_waterfall',True);
G.ShowWideband := JB(GObj,'show_wideband',False); G.ShowWideband := JB(GObj,'show_wideband',False);
G.WidebandFill := JB(GObj,'wideband_fill',False);
G.DisplayDuplex := JB(GObj,'display_duplex',False); G.DisplayDuplex := JB(GObj,'display_duplex',False);
G.DisplayFPS := JI(GObj,'display_fps',60); G.DisplayFPS := JI(GObj,'display_fps',60);
G.LightTheme := JB(GObj,'light_theme',False); G.LightTheme := JB(GObj,'light_theme',False);
@@ -693,6 +696,7 @@ begin
JW(O,'show_spectrum',G.ShowSpectrum); JW(O,'show_spectrum',G.ShowSpectrum);
JW(O,'show_waterfall',G.ShowWaterfall); JW(O,'show_waterfall',G.ShowWaterfall);
JW(O,'show_wideband',G.ShowWideband); JW(O,'show_wideband',G.ShowWideband);
JW(O,'wideband_fill',G.WidebandFill);
JW(O,'display_duplex',G.DisplayDuplex); JW(O,'display_duplex',G.DisplayDuplex);
JW(O,'display_fps',G.DisplayFPS); JW(O,'display_fps',G.DisplayFPS);
JW(O,'light_theme',G.LightTheme); JW(O,'light_theme',G.LightTheme);
+22 -8
View File
@@ -49,7 +49,8 @@ type
TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object; TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object; TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
TOnAudioBufferChange = procedure(BufferSize: Integer) 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; TOnFPSChange = procedure(FPS: Integer) of object;
TOnThemeChange = procedure(LightTheme: Boolean) of object; TOnThemeChange = procedure(LightTheme: Boolean) of object;
TOnWfAGCNFChange = procedure(WfAGC, WfNF: Boolean) of object; TOnWfAGCNFChange = procedure(WfAGC, WfNF: Boolean) of object;
@@ -121,6 +122,7 @@ type
FChkShowSpectrum: TFlatCheckBox; FChkShowSpectrum: TFlatCheckBox;
FChkShowWaterfall: TFlatCheckBox; FChkShowWaterfall: TFlatCheckBox;
FChkShowWideband: TFlatCheckBox; FChkShowWideband: TFlatCheckBox;
FChkWidebandFill: TFlatCheckBox;
FCmbFPS: TFlatComboBox; FCmbFPS: TFlatComboBox;
FChkLightTheme: TFlatCheckBox; FChkLightTheme: TFlatCheckBox;
FCmbFreqMhzDigits: TFlatComboBox; FCmbFreqMhzDigits: TFlatComboBox;
@@ -372,7 +374,8 @@ type
procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput); procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
procedure LoadAudioBufferSize(BufferSize: Integer); procedure LoadAudioBufferSize(BufferSize: Integer);
procedure LoadVisibility(ShowSpectrum, ShowWaterfall, ShowWideband: Boolean); procedure LoadVisibility(ShowSpectrum, ShowWaterfall, ShowWideband,
WidebandFill: Boolean);
procedure LoadFPS(FPS: Integer); procedure LoadFPS(FPS: Integer);
procedure LoadLightTheme(ALight: Boolean); procedure LoadLightTheme(ALight: Boolean);
procedure LoadFreqMhzDigits(Digits: Integer); procedure LoadFreqMhzDigits(Digits: Integer);
@@ -801,7 +804,7 @@ begin
Font.Color := CLR_TEXTDIM; Font.Color := CLR_TEXTDIM;
end; end;
Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 86); Grp := MakeGroupPanel(FPageDisplay, 'Visible Panes', MARGIN, 86, 650, 112);
FChkShowSpectrum := TFlatCheckBox.Create(Self); FChkShowSpectrum := TFlatCheckBox.Create(Self);
FChkShowSpectrum.Parent := Grp; FChkShowSpectrum.Parent := Grp;
@@ -833,7 +836,17 @@ begin
FChkShowWideband.Font.Size := 9; FChkShowWideband.Font.Size := 9;
FChkShowWideband.OnChange := OnVisibilityChkChange; 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); MakeLbl(Grp, 'Refresh rate', PAD, R1 + 5, LW);
FCmbFPS := MakeCombo(Grp, CX, R1, 140, OnFPSCmbChange); FCmbFPS := MakeCombo(Grp, CX, R1, 140, OnFPSCmbChange);
@@ -841,7 +854,7 @@ begin
FCmbFPS.Items.Add(FPS_NAMES[i]); FCmbFPS.Items.Add(FPS_NAMES[i]);
FCmbFPS.ItemIndex := 8; FCmbFPS.ItemIndex := 8;
Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 292, 650, 68); Grp := MakeGroupPanel(FPageDisplay, 'Appearance', MARGIN, 318, 650, 68);
FChkLightTheme := TFlatCheckBox.Create(Self); FChkLightTheme := TFlatCheckBox.Create(Self);
FChkLightTheme.Parent := Grp; FChkLightTheme.Parent := Grp;
@@ -853,7 +866,7 @@ begin
FChkLightTheme.Font.Size := 9; FChkLightTheme.Font.Size := 9;
FChkLightTheme.OnChange := OnLightThemeChkChange; 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); MakeLbl(Grp, 'Frequency range', PAD, R1 + 5, LW);
FCmbFreqMhzDigits := MakeCombo(Grp, CX, R1, 240, OnFreqMhzDigitsCmbChange); FCmbFreqMhzDigits := MakeCombo(Grp, CX, R1, 240, OnFreqMhzDigitsCmbChange);
@@ -2313,13 +2326,14 @@ begin
end; end;
procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall, procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall,
ShowWideband: Boolean); ShowWideband, WidebandFill: Boolean);
begin begin
FLoading := True; FLoading := True;
try try
FChkShowSpectrum.Checked := ShowSpectrum; FChkShowSpectrum.Checked := ShowSpectrum;
FChkShowWaterfall.Checked := ShowWaterfall; FChkShowWaterfall.Checked := ShowWaterfall;
FChkShowWideband.Checked := ShowWideband; FChkShowWideband.Checked := ShowWideband;
FChkWidebandFill.Checked := WidebandFill;
finally finally
FLoading := False; FLoading := False;
end; end;
@@ -2341,7 +2355,7 @@ begin
if FLoading then Exit; if FLoading then Exit;
if not Assigned(FOnVisibilityChange) then Exit; if not Assigned(FOnVisibilityChange) then Exit;
FOnVisibilityChange(FChkShowSpectrum.Checked, FChkShowWaterfall.Checked, FOnVisibilityChange(FChkShowSpectrum.Checked, FChkShowWaterfall.Checked,
FChkShowWideband.Checked); FChkShowWideband.Checked, FChkWidebandFill.Checked);
end; end;
procedure TSettingsForm.OnLightThemeChkChange(Sender: TObject); procedure TSettingsForm.OnLightThemeChkChange(Sender: TObject);
+51
View File
@@ -39,6 +39,7 @@ type
FSourceStartHz: Double; FSourceStartHz: Double;
FSourceEndHz: Double; FSourceEndHz: Double;
FMarkerHz: Double; FMarkerHz: Double;
FFillSpectrum: Boolean;
FWDSPAnalyzerOpen: Boolean; FWDSPAnalyzerOpen: Boolean;
FWDSPAnalyzerConfigured: Boolean; FWDSPAnalyzerConfigured: Boolean;
FWDSPAnalyzerID: Integer; FWDSPAnalyzerID: Integer;
@@ -64,6 +65,7 @@ type
procedure DrawHamBand(C: TCanvas; PlotW, H: Integer; F1, F2: Double; procedure DrawHamBand(C: TCanvas; PlotW, H: Integer; F1, F2: Double;
const Name: string; AColor: TColor); const Name: string; AColor: TColor);
procedure DrawFrequencyMarker(C: TCanvas; PlotW, H: Integer); procedure DrawFrequencyMarker(C: TCanvas; PlotW, H: Integer);
procedure DrawSpectrumGradient(const SpPts: array of TPoint; PlotW, H: Integer);
procedure UploadBitmapToGL; procedure UploadBitmapToGL;
public public
constructor Create; constructor Create;
@@ -73,6 +75,7 @@ type
function SetFrequencyView(ViewStartHz, ViewEndHz, SourceStartHz, function SetFrequencyView(ViewStartHz, ViewEndHz, SourceStartHz,
SourceEndHz: Double): Boolean; SourceEndHz: Double): Boolean;
function SetMarkerHz(AHz: Double): Boolean; function SetMarkerHz(AHz: Double): Boolean;
procedure SetFillSpectrum(AFill: Boolean);
function TryPixelToFrequency(PixelX, PanelWidth: Integer; function TryPixelToFrequency(PixelX, PanelWidth: Integer;
out FreqHz: Double): Boolean; out FreqHz: Double): Boolean;
procedure SetSamples(const Samples: array of SmallInt; Count: Integer); procedure SetSamples(const Samples: array of SmallInt; Count: Integer);
@@ -111,6 +114,7 @@ begin
FSourceStartHz := 0.0; FSourceStartHz := 0.0;
FSourceEndHz := FSampleRateHz * 0.5; FSourceEndHz := FSampleRateHz * 0.5;
FMarkerHz := 0.0; FMarkerHz := 0.0;
FFillSpectrum := False;
FWDSPAnalyzerOpen := False; FWDSPAnalyzerOpen := False;
FWDSPAnalyzerConfigured := False; FWDSPAnalyzerConfigured := False;
FWDSPAnalyzerID := WB_WDSP_ID; FWDSPAnalyzerID := WB_WDSP_ID;
@@ -187,6 +191,13 @@ begin
Result := True; Result := True;
end; end;
procedure TWidebandView.SetFillSpectrum(AFill: Boolean);
begin
if FFillSpectrum = AFill then Exit;
FFillSpectrum := AFill;
FDirty := True;
end;
function TWidebandView.TryPixelToFrequency(PixelX, PanelWidth: Integer; function TWidebandView.TryPixelToFrequency(PixelX, PanelWidth: Integer;
out FreqHz: Double): Boolean; out FreqHz: Double): Boolean;
var var
@@ -511,6 +522,44 @@ begin
C.LineTo(X, H); C.LineTo(X, H);
end; 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); procedure TWidebandView.DrawCPU(W, H: Integer);
var var
C: TCanvas; C: TCanvas;
@@ -586,6 +635,8 @@ begin
Y := EnsureRange(Y, 1, TopH); Y := EnsureRange(Y, 1, TopH);
FPoints[X] := Point(X, Y); FPoints[X] := Point(X, Y);
end; end;
if FFillSpectrum then
DrawSpectrumGradient(FPoints, PlotW, H);
C.Pen.Color := TColor($0040FF80); C.Pen.Color := TColor($0040FF80);
C.Polyline(FPoints); C.Polyline(FPoints);
end; end;