Phase 5 (batch 28): move display state into the controller

Move the 14 persisted display-state fields (FShow*/FSpec*/FWf*/FTXSpec*/
FWidebandFill/FSpectrumFill) from MainForm into TRadioController so a
headless device load (DoConnectDevice) can apply them without the UI. Pure
field relocation: every reader/writer now addresses FController.F*, render
still happens in MainForm against FSpecView/FWidebandView. Compiler-verified
complete; behavior preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-08 11:15:58 +03:00
co-authored by Claude Opus 4.8
parent 9fdfc67f65
commit 1fdbe27b96
2 changed files with 125 additions and 124 deletions
+109 -124
View File
@@ -153,27 +153,12 @@ type
FSpectrumWidth: Integer; // актуальная ширина для FDSPEngine и resize-детектора
FSpectrumHeight: Integer;
FWaterfallHeight:Integer;
FShowSpectrum: Boolean;
FShowWaterfall: Boolean;
FShowWideband: Boolean;
FWidebandFill: Boolean;
FSpectrumFill: Boolean;
FDisplayFPS: Integer;
FWaterfallDirty: Boolean;
FWidebandDirty: Boolean;
FPreChannelPower: Integer; // мощность до применения канала (-1 = не сохранена)
FWfManualHigh: Double;
FWfManualLow: Double;
FWfAGCOffset: Double;
// Spectrum display settings (for settings save/load)
FSpecRefLevel: Double;
FSpecRange: Double;
FSpecGridStep: Double;
// TX-specific grid (Thetis-style: при TX рисуем спектр в своих границах).
// Применяются к FSpecView когда FTransmitting=True; иначе используются RX-значения.
FTXSpecRefLevel: Double;
FTXSpecRange: Double;
FTXSpecGridStep: Double;
// Display state (FShow*/FSpec*/FWf*/FTXSpec*/FController.FWidebandFill/FController.FSpectrumFill)
// переехало в TRadioController (нужно headless-load в DoConnectDevice).
// DSP data buffers (copy of last DSP output, for WebServer.PushSpectrum)
FSpectrumBuf: array[0..1023] of Single;
FWaterfallBuf: array[0..1023] of Single;
@@ -803,18 +788,18 @@ begin
Result.WfAvgMode := 3;
Result.WfAvgTimeMS := 120.0;
end;
Result.WfManualHigh := FWfManualHigh;
Result.WfManualLow := FWfManualLow;
Result.WfAGCOffset := FWfAGCOffset;
Result.SpecRefLevel := FSpecRefLevel;
Result.SpecRange := FSpecRange;
Result.SpecGridStep := FSpecGridStep;
Result.WfManualHigh := FController.FWfManualHigh;
Result.WfManualLow := FController.FWfManualLow;
Result.WfAGCOffset := FController.FWfAGCOffset;
Result.SpecRefLevel := FController.FSpecRefLevel;
Result.SpecRange := FController.FSpecRange;
Result.SpecGridStep := FController.FSpecGridStep;
Result.AudioSampleRate := FController.FAudioOut.SampleRate;
Result.ShowSpectrum := FShowSpectrum;
Result.ShowWaterfall := FShowWaterfall;
Result.ShowWideband := FShowWideband;
Result.WidebandFill := FWidebandFill;
Result.SpectrumFill := FSpectrumFill;
Result.ShowSpectrum := FController.FShowSpectrum;
Result.ShowWaterfall := FController.FShowWaterfall;
Result.ShowWideband := FController.FShowWideband;
Result.WidebandFill := FController.FWidebandFill;
Result.SpectrumFill := FController.FSpectrumFill;
Result.DisplayDuplex := FController.FDisplayDuplex;
Result.DisplayFPS := FDisplayFPS;
Result.LightTheme := FLightTheme;
@@ -917,9 +902,9 @@ begin
FController.FSampleRate := 192000;
FController.FCurrentBand := 5; // 20m по умолчанию
// Waterfall normalization
FWfManualHigh := -80.0;
FWfManualLow := -130.0;
FWfAGCOffset := 0.0;
FController.FWfManualHigh := -80.0;
FController.FWfManualLow := -130.0;
FController.FWfAGCOffset := 0.0;
FController.FWfAGCEnabled := True;
FController.FWfNFEnabled := False;
FController.FDitherEnabled := True;
@@ -942,13 +927,13 @@ begin
FWaterfallFrameCounter := 0;
FController.FDevConnected := False;
// Spectrum grid settings defaults
FSpecRefLevel := -20.0;
FSpecRange := 110.0;
FSpecGridStep := 10.0;
FController.FSpecRefLevel := -20.0;
FController.FSpecRange := 110.0;
FController.FSpecGridStep := 10.0;
// TX grid defaults (синхронно с TSettingsManager.DefaultTX)
FTXSpecRefLevel := 0.0;
FTXSpecRange := 80.0;
FTXSpecGridStep := 10.0;
FController.FTXSpecRefLevel := 0.0;
FController.FTXSpecRange := 80.0;
FController.FTXSpecGridStep := 10.0;
FSettingsForm := nil;
FChannelsForm := nil;
FActiveChannelIdx := -1;
@@ -1068,11 +1053,11 @@ begin
FVfoOverlay.OnInvalidate := OnVfoOverlayInvalidate;
FVfoOverlay.Visible := False;
FPanelHidden := False;
FShowSpectrum := True;
FShowWaterfall := True;
FShowWideband := False;
FWidebandFill := False;
FSpectrumFill := True;
FController.FShowSpectrum := True;
FController.FShowWaterfall := True;
FController.FShowWideband := False;
FController.FWidebandFill := False;
FController.FSpectrumFill := True;
FDisplayFPS := 60;
FFreqMhzDigits := 3;
FSplitterRatio := 0.40;
@@ -1112,12 +1097,12 @@ begin
FSpecView.AGCTop := FController.FAGCTop;
FSpecView.WfAGCEnabled := FController.FWfAGCEnabled;
FSpecView.WfNFEnabled := FController.FWfNFEnabled;
FSpecView.WfManualHigh := FWfManualHigh;
FSpecView.WfManualLow := FWfManualLow;
FSpecView.WfAGCOffset := FWfAGCOffset;
FSpecView.SpecRefLevel := FSpecRefLevel;
FSpecView.SpecRange := FSpecRange;
FSpecView.SpecGridStep := FSpecGridStep;
FSpecView.WfManualHigh := FController.FWfManualHigh;
FSpecView.WfManualLow := FController.FWfManualLow;
FSpecView.WfAGCOffset := FController.FWfAGCOffset;
FSpecView.SpecRefLevel := FController.FSpecRefLevel;
FSpecView.SpecRange := FController.FSpecRange;
FSpecView.SpecGridStep := FController.FSpecGridStep;
// TX overlay: ширина окна = TX_SAMPLE_RATE из движка (фикс 192k)
FSpecView.TXSpanHz := WDSPEngine.TX_SAMPLE_RATE;
FSpecView.WfFrameInterval := FWaterfallFrameInterval;
@@ -2425,7 +2410,7 @@ begin
UpdateWidebandFrequencyView;
WideH := 0;
WideSpecH := 0;
if FShowWideband then
if FController.FShowWideband then
WideH := Min(MulDiv(WB_H, Screen.PixelsPerInch, 96), Max(40, RH div 2));
if WideH > 0 then
WideSpecH := Max(1, WideH - RULER_H);
@@ -2433,8 +2418,8 @@ begin
if PbWideband <> nil then
begin
PbWideband.SetBounds(0, 0, RW, WideSpecH);
PbWideband.Visible := FShowWideband and (WideSpecH > 0);
if FShowWideband and (RW > 0) and (WideSpecH > 0) then
PbWideband.Visible := FController.FShowWideband and (WideSpecH > 0);
if FController.FShowWideband and (RW > 0) and (WideSpecH > 0) then
begin
FWidebandView.SetBitmapSize(RW, WideSpecH);
PbWideband.Invalidate;
@@ -2442,9 +2427,9 @@ begin
end;
if PbWidebandRuler <> nil then
begin
PbWidebandRuler.SetBounds(0, WideSpecH, RW, IfThen(FShowWideband and (WideH > 0), RULER_H, 0));
PbWidebandRuler.Visible := FShowWideband and (WideH > 0);
if FShowWideband and (RW > 0) and (WideH > 0) then
PbWidebandRuler.SetBounds(0, WideSpecH, RW, IfThen(FController.FShowWideband and (WideH > 0), RULER_H, 0));
PbWidebandRuler.Visible := FController.FShowWideband and (WideH > 0);
if FController.FShowWideband and (RW > 0) and (WideH > 0) then
begin
FWidebandView.SetRulerSize(RW, RULER_H);
PbWidebandRuler.Invalidate;
@@ -2454,7 +2439,7 @@ begin
ResizeSMeter;
// ---- Оба скрыты ----
if (not FShowSpectrum) and (not FShowWaterfall) then
if (not FController.FShowSpectrum) and (not FController.FShowWaterfall) then
begin
PbSpectrum.Visible := False;
PbRuler.Visible := False;
@@ -2468,7 +2453,7 @@ begin
end;
// ---- Только спектр (водопад скрыт) ----
if FShowSpectrum and (not FShowWaterfall) then
if FController.FShowSpectrum and (not FController.FShowWaterfall) then
begin
AvailH := RH - TopOff - RULER_H;
if AvailH < MIN_SH then AvailH := MIN_SH;
@@ -2497,7 +2482,7 @@ begin
end;
// ---- Только водопад (спектр скрыт): линейка под водопадом ----
if (not FShowSpectrum) and FShowWaterfall then
if (not FController.FShowSpectrum) and FController.FShowWaterfall then
begin
AvailH := RH - TopOff - RULER_H;
if AvailH < MIN_WH then AvailH := MIN_WH;
@@ -2944,7 +2929,7 @@ begin
// FSplitTxB (TX-A/TX-B), независимо от активного RX-VFO.
if FController.FSplitTxB then FSpecView.TXVfoIndex := 1
else FSpecView.TXVfoIndex := 0;
if UpdateWidebandFrequencyView and FShowWideband and (PbWideband <> nil) then
if UpdateWidebandFrequencyView and FController.FShowWideband and (PbWideband <> nil) then
PbWideband.Invalidate;
end;
@@ -3030,10 +3015,10 @@ begin
// PbSpectrum.Height может быть ненулевым при FSpectrumHeight=0,
// что вызвало бы сброс буфера водопада на каждом тике.
if (FSpectrumWidth = 0) or
(FShowWideband and (PbWideband.Width <> FSpectrumWidth)) or
(FShowSpectrum and ((PbSpectrum.Width <> FSpectrumWidth) or
(FController.FShowWideband and (PbWideband.Width <> FSpectrumWidth)) or
(FController.FShowSpectrum and ((PbSpectrum.Width <> FSpectrumWidth) or
(PbSpectrum.Height <> FSpectrumHeight))) or
(FShowWaterfall and not FShowSpectrum and
(FController.FShowWaterfall and not FController.FShowSpectrum and
((PbWaterfall.Width <> FSpectrumWidth) or
(PbWaterfall.Height <> FWaterfallHeight))) then
ResizeSpectrumPanels;
@@ -3117,7 +3102,7 @@ begin
PbWaterfall.Invalidate;
FWaterfallDirty := False;
end;
if FWidebandDirty and FShowWideband then
if FWidebandDirty and FController.FShowWideband then
begin
PbWideband.Invalidate;
FWidebandDirty := False;
@@ -3325,7 +3310,7 @@ end;
procedure TMainForm.OnWidebandCB(ADCIndex: Integer;
const Samples: array of SmallInt; Count: Integer);
begin
if (not FShowWideband) or (FWidebandView = nil) then Exit;
if (not FController.FShowWideband) or (FWidebandView = nil) then Exit;
FWidebandView.SetSamples(Samples, Count);
FWidebandDirty := True;
end;
@@ -3806,10 +3791,10 @@ begin
RebuildXvtrButtons;
ApplyXvtrToNetwork;
PushXvtrToWeb;
FTXSpecRefLevel := FController.FTXSettings.TXSpecRefLevel;
FTXSpecRange := FController.FTXSettings.TXSpecRange;
FController.FTXSpecRefLevel := FController.FTXSettings.TXSpecRefLevel;
FController.FTXSpecRange := FController.FTXSettings.TXSpecRange;
if FController.FTXSettings.TXSpecGridStep > 0 then
FTXSpecGridStep := FController.FTXSettings.TXSpecGridStep;
FController.FTXSpecGridStep := FController.FTXSettings.TXSpecGridStep;
FController.FVolume := G_Settings.Volume;
FController.FActiveVfo := G_Settings.ActiveVfo;
// PA settings
@@ -3839,23 +3824,23 @@ begin
FController.FDitherEnabled := G_Settings.DitherEnabled;
FController.FRandomEnabled := G_Settings.RandomEnabled;
FController.FSendAudioToRadio := G_Settings.SendAudioToRadio;
FWfManualHigh := G_Settings.WfManualHigh;
FWfManualLow := G_Settings.WfManualLow;
FWfAGCOffset := G_Settings.WfAGCOffset;
FController.FWfManualHigh := G_Settings.WfManualHigh;
FController.FWfManualLow := G_Settings.WfManualLow;
FController.FWfAGCOffset := G_Settings.WfAGCOffset;
FSpecView.WfAGCEnabled := FController.FWfAGCEnabled;
FSpecView.WfNFEnabled := FController.FWfNFEnabled;
FSpecView.WfManualHigh := FWfManualHigh;
FSpecView.WfManualLow := FWfManualLow;
FSpecView.WfAGCOffset := FWfAGCOffset;
FSpecView.WfManualHigh := FController.FWfManualHigh;
FSpecView.WfManualLow := FController.FWfManualLow;
FSpecView.WfAGCOffset := FController.FWfAGCOffset;
FSpecView.ResetWfAvgBuf;
FShowSpectrum := G_Settings.ShowSpectrum;
FShowWaterfall := G_Settings.ShowWaterfall;
FShowWideband := G_Settings.ShowWideband;
FWidebandFill := G_Settings.WidebandFill;
FController.FShowSpectrum := G_Settings.ShowSpectrum;
FController.FShowWaterfall := G_Settings.ShowWaterfall;
FController.FShowWideband := G_Settings.ShowWideband;
FController.FWidebandFill := G_Settings.WidebandFill;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FWidebandFill);
FSpectrumFill := G_Settings.SpectrumFill;
FSpecView.FillSpectrum := FSpectrumFill;
FWidebandView.SetFillSpectrum(FController.FWidebandFill);
FController.FSpectrumFill := G_Settings.SpectrumFill;
FSpecView.FillSpectrum := FController.FSpectrumFill;
FController.FDisplayDuplex := G_Settings.DisplayDuplex;
if BtnDUP <> nil then StyleButton(BtnDUP, FController.FDisplayDuplex);
if G_Settings.DisplayFPS > 0 then
@@ -3864,12 +3849,12 @@ begin
// Настройки дисплея и сетки
if G_Settings.FFTSize > 0 then
begin
FSpecRefLevel := G_Settings.SpecRefLevel;
FSpecRange := G_Settings.SpecRange;
if G_Settings.SpecGridStep > 0 then FSpecGridStep := G_Settings.SpecGridStep;
FSpecView.SpecRefLevel := FSpecRefLevel;
FSpecView.SpecRange := FSpecRange;
FSpecView.SpecGridStep := FSpecGridStep;
FController.FSpecRefLevel := G_Settings.SpecRefLevel;
FController.FSpecRange := G_Settings.SpecRange;
if G_Settings.SpecGridStep > 0 then FController.FSpecGridStep := G_Settings.SpecGridStep;
FSpecView.SpecRefLevel := FController.FSpecRefLevel;
FSpecView.SpecRange := FController.FSpecRange;
FSpecView.SpecGridStep := FController.FSpecGridStep;
InvalidateGridCache;
if FController.FWDSPReady then
begin
@@ -3938,7 +3923,7 @@ begin
GenPkt.PAConfig := $01;
GenPkt.WBPort[0] := (PORT_WIDEBAND_ADC0 shr 8) and $FF;
GenPkt.WBPort[1] := PORT_WIDEBAND_ADC0 and $FF;
GenPkt.WBEnable := IfThen(FShowWideband, 1, 0);
GenPkt.WBEnable := IfThen(FController.FShowWideband, 1, 0);
GenPkt.WBSamplesPerPkt[0] := 512 shr 8;
GenPkt.WBSamplesPerPkt[1] := 512 and $FF;
GenPkt.WBSampleSize := 16;
@@ -3949,7 +3934,7 @@ begin
else
GenPkt.AlexEnable := $01;
FController.FNetwork.SendGeneralPacket(GenPkt);
FController.FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32);
FController.FNetwork.ConfigureWideband(0, FController.FShowWideband, 512, 16, 70, 32);
if FController.FNetwork.Device.BoardType in [3, 4, 5] then
FController.FActiveDDC := 2
@@ -5798,16 +5783,16 @@ begin
OldAtt := FController.FTXSettings.AttOnTX;
OldTUNLevel := FController.FTXSettings.TUNLevel;
OldTUNFreq := FController.FTXSettings.TUNFreq;
GridChanged := (FTXSpecRefLevel <> T.TXSpecRefLevel)
or (FTXSpecRange <> T.TXSpecRange)
or (FTXSpecGridStep <> T.TXSpecGridStep);
GridChanged := (FController.FTXSpecRefLevel <> T.TXSpecRefLevel)
or (FController.FTXSpecRange <> T.TXSpecRange)
or (FController.FTXSpecGridStep <> T.TXSpecGridStep);
FController.FTXSettings := T;
NewMicByte := BuildMicLineSelectByte;
// TX-grid в момент правки — обновляем рабочие поля и (если на передаче) пушим в FSpecView
FTXSpecRefLevel := FController.FTXSettings.TXSpecRefLevel;
FTXSpecRange := FController.FTXSettings.TXSpecRange;
FController.FTXSpecRefLevel := FController.FTXSettings.TXSpecRefLevel;
FController.FTXSpecRange := FController.FTXSettings.TXSpecRange;
if FController.FTXSettings.TXSpecGridStep > 0 then
FTXSpecGridStep := FController.FTXSettings.TXSpecGridStep;
FController.FTXSpecGridStep := FController.FTXSettings.TXSpecGridStep;
if GridChanged and FController.FTransmitting then
ApplySpecViewGridFromState;
ApplyTXSettingsToDSP;
@@ -6227,21 +6212,21 @@ procedure TMainForm.ApplyWaterfallParams(WfDet, WfAvgMode: Integer;
begin
if FController.FWDSPReady then
FController.FDSPEngine.SetWaterfallDisplay(WfDet, WfAvgMode, WfAvgTimeMS);
FWfManualHigh := WfHigh;
FWfManualLow := WfLow;
FWfAGCOffset := WfAGCOffset;
FSpecView.WfManualHigh := FWfManualHigh;
FSpecView.WfManualLow := FWfManualLow;
FSpecView.WfAGCOffset := FWfAGCOffset;
FController.FWfManualHigh := WfHigh;
FController.FWfManualLow := WfLow;
FController.FWfAGCOffset := WfAGCOffset;
FSpecView.WfManualHigh := FController.FWfManualHigh;
FSpecView.WfManualLow := FController.FWfManualLow;
FSpecView.WfAGCOffset := FController.FWfAGCOffset;
FSpecView.ResetWfAvgBuf;
FSpectrumDirty := True;
end;
procedure TMainForm.ApplyGridParams(RefLevel, Range, GridStep: Double);
begin
FSpecRefLevel := RefLevel;
FSpecRange := Range;
if GridStep > 0 then FSpecGridStep := GridStep;
FController.FSpecRefLevel := RefLevel;
FController.FSpecRange := Range;
if GridStep > 0 then FController.FSpecGridStep := GridStep;
// На передаче FSpecView показывает TX-сетку — RX-правка не должна её перетирать.
if not FController.FTransmitting then ApplySpecViewGridFromState;
end;
@@ -6251,14 +6236,14 @@ begin
// При DUP во время TX данные приходят от RX-анализатора → используем RX-сетку.
if FController.FTransmitting and not FController.FDisplayDuplex then
begin
FSpecView.SpecRefLevel := FTXSpecRefLevel;
FSpecView.SpecRange := FTXSpecRange;
FSpecView.SpecGridStep := FTXSpecGridStep;
FSpecView.SpecRefLevel := FController.FTXSpecRefLevel;
FSpecView.SpecRange := FController.FTXSpecRange;
FSpecView.SpecGridStep := FController.FTXSpecGridStep;
end else
begin
FSpecView.SpecRefLevel := FSpecRefLevel;
FSpecView.SpecRange := FSpecRange;
FSpecView.SpecGridStep := FSpecGridStep;
FSpecView.SpecRefLevel := FController.FSpecRefLevel;
FSpecView.SpecRange := FController.FSpecRange;
FSpecView.SpecGridStep := FController.FSpecGridStep;
end;
InvalidateGridCache;
FSpectrumDirty := True;
@@ -6327,16 +6312,16 @@ end;
procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall,
ShowWideband, WidebandFill, SpectrumFill: Boolean);
begin
FShowSpectrum := ShowSpectrum;
FShowWaterfall := ShowWaterfall;
FShowWideband := ShowWideband;
FWidebandFill := WidebandFill;
FSpectrumFill := SpectrumFill;
FController.FShowSpectrum := ShowSpectrum;
FController.FShowWaterfall := ShowWaterfall;
FController.FShowWideband := ShowWideband;
FController.FWidebandFill := WidebandFill;
FController.FSpectrumFill := SpectrumFill;
if Assigned(FWidebandView) then
FWidebandView.SetFillSpectrum(FWidebandFill);
FSpecView.FillSpectrum := FSpectrumFill;
FWidebandView.SetFillSpectrum(FController.FWidebandFill);
FSpecView.FillSpectrum := FController.FSpectrumFill;
if Assigned(FController.FNetwork) and FController.FNetwork.Connected then
FController.FNetwork.ConfigureWideband(0, FShowWideband, 512, 16, 70, 32);
FController.FNetwork.ConfigureWideband(0, FController.FShowWideband, 512, 16, 70, 32);
ResizeSpectrumPanels;
end;
@@ -6422,8 +6407,8 @@ begin
FCATLastGlobal.CATTcpPort);
// Перечисляем PA устройства
SF.RefreshAudioDevices(FController.FAudioOut, FController.FAudioIn);
SF.LoadVisibility(FShowSpectrum, FShowWaterfall, FShowWideband, FWidebandFill,
FSpectrumFill);
SF.LoadVisibility(FController.FShowSpectrum, FController.FShowWaterfall, FController.FShowWideband, FController.FWidebandFill,
FController.FSpectrumFill);
SF.LoadWfAGCNF(FController.FWfAGCEnabled, FController.FWfNFEnabled);
SF.LoadADCSettings(FController.FDitherEnabled, FController.FRandomEnabled);
SF.LoadWebSettings(FWebEnabled, FWebPort, FWebBindAddr, FWebUser, FWebPass);
@@ -6443,16 +6428,16 @@ begin
FController.FDSPEngine.WfDetector,
FController.FDSPEngine.WfAvgMode,
FController.FDSPEngine.WfAvgTimeMS,
FWfManualHigh,
FWfManualLow,
FWfAGCOffset,
FSpecRefLevel, FSpecRange, FSpecGridStep,
FController.FWfManualHigh,
FController.FWfManualLow,
FController.FWfAGCOffset,
FController.FSpecRefLevel, FController.FSpecRange, FController.FSpecGridStep,
FController.FAudioOutDevName, FController.FAudioInDevName)
else
SF.LoadValues(
131072, 2, 0, 3, 30.0,
0, 3, 120.0, FWfManualHigh, FWfManualLow, FWfAGCOffset,
FSpecRefLevel, FSpecRange, FSpecGridStep,
0, 3, 120.0, FController.FWfManualHigh, FController.FWfManualLow, FController.FWfAGCOffset,
FController.FSpecRefLevel, FController.FSpecRange, FController.FSpecGridStep,
FController.FAudioOutDevName, FController.FAudioInDevName);
SF.Show;
end;
+16
View File
@@ -175,6 +175,22 @@ type
FDitherEnabled: Boolean;
FRandomEnabled: Boolean;
// ---- Display state (persist в TGlobalSettings; рендерится FSpecView/Wideband) ----
FShowSpectrum: Boolean;
FShowWaterfall: Boolean;
FShowWideband: Boolean;
FWidebandFill: Boolean;
FSpectrumFill: Boolean;
FWfManualHigh: Double;
FWfManualLow: Double;
FWfAGCOffset: Double;
FSpecRefLevel: Double;
FSpecRange: Double;
FSpecGridStep: Double;
FTXSpecRefLevel: Double; // TX-сетка (Thetis-style): применяется при FTransmitting
FTXSpecRange: Double;
FTXSpecGridStep: Double;
// ---- FM ----
FFMDeviation: Double;
FFMCTCSSOn: Boolean;