Add configurable VFO frequency range with GHz support

- Settings > Display: new "VFO Display" control to select max frequency
  range (999 MHz / 9.999 GHz / 99.999 GHz), persisted as freq_mhz_digits
- FreqDisplay: extend digit map [0..10] to support up to 11 digit positions;
  keyboard navigation adapts to MinMhzDigits dynamically
- MainForm/LayoutTopVfoBlock: VFO panel width scales automatically with
  selected digit count using dynamic text measurement
- Web VFO: fix broken HTML (class attr not closed before data-s — wheel and
  hover were completely non-functional); rewrite fmtVfo to use variable MHz
  digit count synced from server state; fix missing </span> for .vfo-hz
- WebServer: publish freq_mhz_digits in state JSON; updated from
  ApplyFreqMhzDigits so desktop and web stay in sync
- Fix Int32 overflow on frequencies above ~2.15 GHz: CATEngine.Pad V param
  Int64, FormatFreq/FormatFreqSV Mhz var Int64 (Mhz*1000000 overflowed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 13:13:52 +03:00
co-authored by Claude Sonnet 4.6
parent 5ac7ddc976
commit efc21b92e6
8 changed files with 105 additions and 35 deletions
+28 -5
View File
@@ -278,6 +278,7 @@ type
FBandCache: array[0..CFG_BAND_COUNT-1] of TBandSettings; // кэш диапазонов
FDevConnected: Boolean; // True после первого подключения
FLightTheme: Boolean; // True = светлая тема
FFreqMhzDigits: Integer; // 3=999MHz, 4=9.999GHz, 5=99.999GHz
FCenterFreq: Double; // центр спектра = LO (DDC). При CTUN ON не меняется при кручении VFO
FSpanHz: Double;
FSampleRate: Integer; // текущий DDC sample rate (Hz)
@@ -630,6 +631,7 @@ type
function BuildMicLineSelectByte: Byte;
procedure ApplyVisibility(ShowSpectrum, ShowWaterfall: Boolean);
procedure ApplyFPS(FPS: Integer);
procedure ApplyFreqMhzDigits(Digits: Integer);
published
// Обработчики событий формы — должны быть в published для RTTI/LFM
@@ -919,6 +921,7 @@ begin
Result.DisplayDuplex := FDisplayDuplex;
Result.DisplayFPS := FDisplayFPS;
Result.LightTheme := FLightTheme;
Result.FreqMhzDigits := FFreqMhzDigits;
// Audio device names
Result.AudioOutDevice := FAudioOutDevName;
Result.AudioInDevice := FAudioInDevName;
@@ -1182,6 +1185,7 @@ begin
FShowSpectrum := True;
FShowWaterfall := True;
FDisplayFPS := 60;
FFreqMhzDigits := 3;
FSplitterRatio := 0.40;
FSplitterDrag := False;
@@ -1992,7 +1996,7 @@ begin
Canvas.Font.Style := [fsBold];
FreqDispA.Left := VFO_BTN_W + VFO_BTN_FREQ_GAP - VFO_FREQ_LEFT_PAD;
FreqDispB.Left := VFO_B_LEFT_PAD + VFO_BTN_W + VFO_BTN_FREQ_GAP - VFO_FREQ_LEFT_PAD;
FreqTextW := Canvas.TextWidth('000.000.000');
FreqTextW := Canvas.TextWidth(StringOfChar('0', Max(3, FFreqMhzDigits)) + '.000.000');
NeedFreqAW := FreqDispA.Left + FreqTextW + VFO_FREQ_LEFT_PAD + VFO_FREQ_RIGHT_PAD;
NeedFreqBW := FreqDispB.Left + FreqTextW + VFO_FREQ_LEFT_PAD + VFO_FREQ_RIGHT_PAD;
TopVfoAW := NeedFreqAW;
@@ -2448,12 +2452,13 @@ end;
function TMainForm.FormatFreq(Hz: Double): string;
var
Mhz, KHz, Rest: Integer;
Mhz: Int64;
KHz, Rest: Integer;
begin
Mhz := Trunc(Hz / 1000000);
KHz := Trunc((Hz - Mhz * 1000000) / 1000);
Rest := Trunc(Hz) mod 1000;
Result := Format('%3d.%3.3d.%3.3d', [Mhz, KHz, Rest]);
Result := Format('%d.%3.3d.%3.3d', [Mhz, KHz, Rest]);
end;
procedure TMainForm.UpdateVfoDisplay;
@@ -3568,6 +3573,7 @@ begin
if BtnDUP <> nil then StyleButton(BtnDUP, FDisplayDuplex);
if G_Settings.DisplayFPS > 0 then
ApplyFPS(G_Settings.DisplayFPS);
ApplyFreqMhzDigits(EnsureRange(G_Settings.FreqMhzDigits, 3, 5));
// Настройки дисплея и сетки
if G_Settings.FFTSize > 0 then
begin
@@ -6116,6 +6122,21 @@ begin
FDSPEngine.SetDisplayFPS(FPS);
end;
procedure TMainForm.ApplyFreqMhzDigits(Digits: Integer);
const
MaxFreqs: array[3..5] of Int64 = (999999999, 9999999999, 99999999999);
begin
Digits := EnsureRange(Digits, 3, 5);
FFreqMhzDigits := Digits;
FreqDispA.MinMhzDigits := Digits;
FreqDispB.MinMhzDigits := Digits;
FreqDispA.MaxFreq := MaxFreqs[Digits];
FreqDispB.MaxFreq := MaxFreqs[Digits];
if Assigned(FWebServer) then
FWebServer.FreqMhzDigits := Digits;
LayoutTopVfoBlock;
end;
procedure TMainForm.BtnSettingsClick(Sender: TObject);
var
SF: TSettingsForm;
@@ -6133,8 +6154,9 @@ begin
SF.OnFPSChange := ApplyFPS;
SF.OnPAChange := OnPASettingsChange;
SF.OnCATChange := OnCATSettingsChange;
SF.OnThemeChange := SetLightTheme;
SF.OnTXChange := OnTXSettingsChange;
SF.OnThemeChange := SetLightTheme;
SF.OnFreqMhzDigitsChange := ApplyFreqMhzDigits;
SF.OnTXChange := OnTXSettingsChange;
SF.OnAlexChange := OnAlexSettingsChange;
SF.OnXvtrChange := OnXvtrSettingsChange;
end;
@@ -6158,6 +6180,7 @@ begin
SF.LoadVisibility(FShowSpectrum, FShowWaterfall);
SF.LoadFPS(FDisplayFPS);
SF.LoadLightTheme(FLightTheme);
SF.LoadFreqMhzDigits(FFreqMhzDigits);
// Загружаем текущие значения
if FWDSPReady then
SF.LoadValues(