Add FM mode improvements: NFM/FM filters, CTCSS, squelch; ADC and web settings

FM modulation:
- Replace 10 generic FM filter buttons with NFM (2.5kHz dev/11kHz BW) and FM
  (5.0kHz dev/16kHz BW); deviation applied to both RX and TX via WDSP
- Add FM squelch panel (SQL toggle + slider 0..100, per-band save/restore)
  with WDSP SetRXAFMSQRun/SetRXAFMSQThreshold (threshold = level/200.0)
- Add CTCSS panel with on/off toggle and custom flat-button tone picker popup
  (38 standard tones 67.0..250.3 Hz, default 67.0); tone selection and enable
  state saved per band via SetTXACTCSS*
- FM panels (SQL, CTCSS) shown only in FM mode, layout via RelayoutBelowBands
- PanelFilter height shrinks in FM mode so panels sit tight below filters
- FlatButton: handle CMTextChanged to invalidate on Caption change

ADC settings (Dither/Random):
- Add DitherEnabled/RandomEnabled to TGlobalSettings (default true)
- Pass as bitmask to ConfigureDDCs DDC Specific packet bytes 5/6
- Exposed in Settings → Advanced tab with live apply via ApplyADCSettings

Web server settings:
- Add TWebSettings (enabled, port, bind_addr, user, pass) stored globally
- WebServer: configurable port/bind IP via ParseIPv4; Reconfigure method
- Exposed in Settings → Advanced tab with live apply via ApplyWebSettings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 19:01:17 +03:00
co-authored by Claude Sonnet 4.6
parent 1368f249c4
commit 68d05657e6
7 changed files with 698 additions and 43 deletions
+66
View File
@@ -29,6 +29,8 @@ type
AGCTop: Integer;
CTun: Boolean;
SpanHz: Double;
FMSQOn: Boolean;
FMSQLevel: Integer; // 0..100 → WDSP threshold 0.0..0.5
end;
// TX-настройки per-device. Хранятся в JSON-секции "tx" под MAC,
@@ -104,6 +106,15 @@ type
TXSpecGridStep: Double; // grid step in dB, e.g. 10
end;
// Web-сервер — глобальные настройки (не привязаны к устройству, секция "web").
TWebSettings = record
Enabled: Boolean;
Port: Integer; // 1..65535
BindAddr: string; // e.g. '0.0.0.0' (все интерфейсы)
User: string;
Pass: string;
end;
// Transverter (XVTR) entry — один трансвертер.
// Видимая частота VFO в [FreqBegin..FreqEnd] транслируется в IF-частоту,
// которую слышит трансивер: f_IF = f_visible - LOOffset + LOError.
@@ -197,6 +208,9 @@ type
// --- PA (Power Amplifier) settings ---
PAMaxPower: Double; // максимальная выходная мощность, Вт (5..200)
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
// --- ADC settings ---
DitherEnabled: Boolean; // DDC Specific byte 5 — уменьшает интермодуляцию
RandomEnabled: Boolean; // DDC Specific byte 6 — рандомизация ADC
// --- CAT (Computer Aided Transceiver) settings ---
// До 4 последовательных портов (Linux: /dev/ttyS0, Windows: COM1 и т.д.)
CATSerialEnabled: array[0..3] of Boolean;
@@ -264,6 +278,10 @@ type
class procedure DefaultXvtr(out X: TXvtrSettings);
function LoadXvtr(const MAC: array of Byte; out X: TXvtrSettings): Boolean;
procedure SaveXvtr(const MAC: array of Byte; const X: TXvtrSettings);
// Web-сервер — глобальные настройки (секция "web" в корне JSON).
class procedure DefaultWeb(out W: TWebSettings);
procedure LoadWebSettings(out W: TWebSettings);
procedure SaveWebSettings(const W: TWebSettings);
// Тема — глобальная настройка, не привязана к устройству
procedure SaveTheme(ALightTheme: Boolean);
function LoadTheme: Boolean;
@@ -309,6 +327,8 @@ begin
B.AGCTop := 90;
B.CTun := False;
B.SpanHz := 192000;
B.FMSQOn := False;
B.FMSQLevel := 30;
end;
class procedure TSettingsManager.DefaultGlobal(out G: TGlobalSettings);
@@ -362,6 +382,9 @@ begin
end;
G.CATTcpEnabled := False;
G.CATTcpPort := 19090;
// ADC — включены по умолчанию: поддерживаются на Hermes, Angelia, Orion
G.DitherEnabled := True;
G.RandomEnabled := True;
end;
constructor TSettingsManager.Create(const FilePath: string);
@@ -578,6 +601,9 @@ begin
end;
G.CATTcpEnabled := JB(GObj,'cat_tcp_enabled', False);
G.CATTcpPort := JI(GObj,'cat_tcp_port', 19090);
// ADC settings
G.DitherEnabled := JB(GObj,'dither_enabled', True);
G.RandomEnabled := JB(GObj,'random_enabled', True);
for i := 0 to CFG_BAND_COUNT-1 do
begin
@@ -654,6 +680,9 @@ begin
end;
JW(O,'cat_tcp_enabled',G.CATTcpEnabled);
JW(O,'cat_tcp_port',G.CATTcpPort);
// ADC settings
JW(O,'dither_enabled',G.DitherEnabled);
JW(O,'random_enabled',G.RandomEnabled);
end;
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
@@ -667,6 +696,7 @@ begin
JW(O,'filter_idx',B.FilterIdx); JW(O,'filter_bw',B.FilterBW);
JW(O,'agc_mode',B.AGCMode); JW(O,'agc_top',B.AGCTop);
JW(O,'ctun',B.CTun); JW(O,'span_hz',B.SpanHz);
JW(O,'fmsq_on',B.FMSQOn); JW(O,'fmsq_level',B.FMSQLevel);
end;
function TSettingsManager.LoadBand(const MAC: array of Byte; BandIdx: Integer;
@@ -687,6 +717,8 @@ begin
B.AGCTop := JI(O,'agc_top', B.AGCTop);
B.CTun := JB(O,'ctun', B.CTun);
B.SpanHz := JD(O,'span_hz', B.SpanHz);
B.FMSQOn := JB(O,'fmsq_on', B.FMSQOn);
B.FMSQLevel := JI(O,'fmsq_level', B.FMSQLevel);
end;
procedure TSettingsManager.SaveWindowBounds(L, T, W, H: Integer);
@@ -1137,6 +1169,40 @@ begin
end;
end;
class procedure TSettingsManager.DefaultWeb(out W: TWebSettings);
begin
W.Enabled := True;
W.Port := 8080;
W.BindAddr := '0.0.0.0';
W.User := 'admin';
W.Pass := 'hpsdr';
end;
procedure TSettingsManager.LoadWebSettings(out W: TWebSettings);
var O: TJSONObject;
begin
DefaultWeb(W);
if FRoot.Find('web') = nil then Exit;
O := EnsureObj(FRoot, 'web');
W.Enabled := JB(O, 'enabled', True);
W.Port := EnsureRange(JI(O, 'port', 8080), 1, 65535);
W.BindAddr := JS(O, 'bind_addr', '0.0.0.0');
W.User := JS(O, 'user', 'admin');
W.Pass := JS(O, 'pass', 'hpsdr');
end;
procedure TSettingsManager.SaveWebSettings(const W: TWebSettings);
var O: TJSONObject;
begin
O := EnsureObj(FRoot, 'web');
JW(O, 'enabled', W.Enabled);
JW(O, 'port', W.Port);
JWS(O, 'bind_addr', W.BindAddr);
JWS(O, 'user', W.User);
JWS(O, 'pass', W.Pass);
Save;
end;
procedure TSettingsManager.SaveTheme(ALightTheme: Boolean);
var O: TJSONObject;
begin