mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
- Settings.pas: add root-level SaveCATSettings/LoadCATSettings so CAT config is stored independently of device MAC. - MainForm.pas FormCreate: initialize CAT defaults, load from saved 'cat' JSON section, and apply immediately after InitCATEngine — so SettingsForm shows correct values before the transceiver connects. - MainForm.pas OnCATSettingsChange: always save to root 'cat' section (not only when device is connected) so changes persist immediately. - Default TCP CAT port changed from 4992 to 19090 everywhere. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
625 lines
23 KiB
ObjectPascal
625 lines
23 KiB
ObjectPascal
unit Settings;
|
|
{
|
|
Settings.pas — JSON-конфигурация HPSDR SDR приложения.
|
|
Структура hpsdr_settings.json:
|
|
{ "AA:BB:CC:DD:EE:FF": { "global": {...}, "bands": {"5": {...}} } }
|
|
}
|
|
{$mode objfpc}{$H+}
|
|
interface
|
|
uses SysUtils, Classes, Math, fpJSON, jsonparser, jsonscanner;
|
|
|
|
const
|
|
SETTINGS_FILE = 'hpsdr_settings.json';
|
|
CFG_BAND_COUNT = 11;
|
|
CFG_BAND_DEFAULT_FREQ: array[0..CFG_BAND_COUNT-1] of Double = (
|
|
1900000, 3750000, 5357000, 7100000, 10125000,
|
|
14200000, 18120000, 21200000, 24940000, 28500000, 50150000);
|
|
CFG_BAND_DEFAULT_MODE: array[0..CFG_BAND_COUNT-1] of Integer = (
|
|
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
|
|
|
|
type
|
|
TBandSettings = record
|
|
VfoA: Double;
|
|
VfoB: Double;
|
|
Mode: Integer;
|
|
FilterIdx: Integer;
|
|
FilterBW: Integer;
|
|
AGCMode: Integer;
|
|
AGCTop: Integer;
|
|
CTun: Boolean;
|
|
SpanHz: Double;
|
|
end;
|
|
|
|
TGlobalSettings = record
|
|
Volume: Integer;
|
|
DriveLevel: Integer;
|
|
ActiveVfo: Integer;
|
|
NRMode: Integer;
|
|
NBMode: Integer;
|
|
SNBEnabled: Boolean;
|
|
ANFEnabled: Boolean;
|
|
AGCSlope: Integer;
|
|
AGCHangThreshold: Integer;
|
|
WfAGCEnabled: Boolean;
|
|
WfNFEnabled: Boolean;
|
|
LastBand: Integer;
|
|
SampleRate: Integer; // глобальный — один для всех диапазонов
|
|
WindowLeft: Integer;
|
|
WindowTop: Integer;
|
|
WindowWidth: Integer;
|
|
WindowHeight: Integer;
|
|
// --- Display settings ---
|
|
FFTSize: Integer; // 4096..262144
|
|
WindowType: Integer; // 0=Rect,1=BH4,2=Hann,3=FlatTop,4=Hamming,5=Kaiser,6=BH7
|
|
SpecDetector: Integer; // 0=Peak,1=Rosenfell,2=Average,3=Sample,4=RMS
|
|
SpecAvgMode: Integer; // 0=None,1=Recursive,2=TimeWindow,3=LogRecursive
|
|
SpecAvgTimeMS: Double; // averaging time in milliseconds
|
|
WfDetector: Integer; // waterfall detector
|
|
WfAvgMode: Integer; // waterfall average mode
|
|
WfAvgTimeMS: Double; // waterfall averaging time in milliseconds
|
|
WfManualHigh: Double; // manual waterfall high threshold
|
|
WfManualLow: Double; // manual waterfall low threshold
|
|
WfAGCOffset: Double; // AGC offset in dB
|
|
SpecRefLevel: Double; // dBm reference (top of scale), e.g. -20
|
|
SpecRange: Double; // dB range, e.g. 110 (bottom = ref - range)
|
|
SpecGridStep: Double; // grid step in dB, e.g. 10
|
|
// --- Audio settings ---
|
|
AudioOutDevice: string; // PortAudio output device name ('' = default)
|
|
AudioSampleRate: Integer; // PortAudio stream sample rate (e.g. 48000)
|
|
AudioInDevice: string; // PortAudio input device name for TX mic ('' = none)
|
|
// --- Visibility settings ---
|
|
ShowSpectrum: Boolean; // True = draw spectrum on main form
|
|
ShowWaterfall: Boolean; // True = draw waterfall on main form
|
|
// --- Display FPS ---
|
|
DisplayFPS: Integer; // spectrum/waterfall timer fps (1..100)
|
|
// --- PA (Power Amplifier) settings ---
|
|
PAMaxPower: Double; // максимальная выходная мощность, Вт (5..200)
|
|
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
|
// --- CAT (Computer Aided Transceiver) settings ---
|
|
// До 4 последовательных портов (Linux: /dev/ttyS0, Windows: COM1 и т.д.)
|
|
CATSerialEnabled: array[0..3] of Boolean;
|
|
CATSerialPort: array[0..3] of string; // имя порта ОС
|
|
CATSerialBaud: array[0..3] of Integer; // скорость: 1200..115200
|
|
CATSerialDataBits: array[0..3] of Integer; // 7 или 8
|
|
CATSerialStopBits: array[0..3] of Integer; // 1 или 2
|
|
CATSerialParity: array[0..3] of Integer; // 0=None, 1=Odd, 2=Even
|
|
// TCP CAT сервер (один, многоклиентский)
|
|
CATTcpEnabled: Boolean;
|
|
CATTcpPort: Integer; // порт, по умолчанию 19090
|
|
end;
|
|
|
|
TSettingsManager = class
|
|
private
|
|
FFilePath: string;
|
|
FRoot: TJSONObject;
|
|
function EnsureObj(P: TJSONObject; const K: string): TJSONObject;
|
|
function GetDevObj(const M: string): TJSONObject;
|
|
function GetGlobalObj(D: TJSONObject): TJSONObject;
|
|
function GetBandObj(D: TJSONObject; Idx: Integer): TJSONObject;
|
|
function JI(O: TJSONObject; const K: string; Def: Integer): Integer;
|
|
function JD(O: TJSONObject; const K: string; Def: Double): Double;
|
|
function JB(O: TJSONObject; const K: string; Def: Boolean): Boolean;
|
|
function JS(O: TJSONObject; const K: string; const Def: string): string;
|
|
procedure JW(O: TJSONObject; const K: string; V: Integer); overload;
|
|
procedure JW(O: TJSONObject; const K: string; V: Double); overload;
|
|
procedure JW(O: TJSONObject; const K: string; V: Boolean); overload;
|
|
procedure JWS(O: TJSONObject; const K: string; const V: string);
|
|
public
|
|
constructor Create(const FilePath: string = SETTINGS_FILE);
|
|
destructor Destroy; override;
|
|
procedure Load;
|
|
procedure Save;
|
|
function LoadDevice(const MAC: array of Byte;
|
|
out G: TGlobalSettings;
|
|
var Bands: array of TBandSettings): Boolean;
|
|
procedure SaveGlobal(const MAC: array of Byte; const G: TGlobalSettings);
|
|
procedure SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
|
const B: TBandSettings);
|
|
function LoadBand(const MAC: array of Byte; BandIdx: Integer;
|
|
out B: TBandSettings): Boolean;
|
|
class function MacToStr(const MAC: array of Byte): string;
|
|
class procedure DefaultBand(BandIdx: Integer; out B: TBandSettings);
|
|
class procedure DefaultGlobal(out G: TGlobalSettings);
|
|
// Размер окна — не привязан к MAC, хранится в корне JSON
|
|
procedure SaveWindowBounds(L, T, W, H: Integer);
|
|
procedure LoadWindowBounds(out L, T, W, H: Integer);
|
|
procedure SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);
|
|
function LoadStartupPreview(out VfoA, VfoB: Double; out SampleRate: Integer): Boolean;
|
|
// CAT settings — не привязаны к MAC, хранятся в корне JSON (доступны до подключения)
|
|
procedure SaveCATSettings(const G: TGlobalSettings);
|
|
procedure LoadCATSettings(var G: TGlobalSettings);
|
|
end;
|
|
|
|
implementation
|
|
|
|
function LegacyAverageTimeMS(FPS, AvgMode, AvgCount: Integer;
|
|
Backmult, DefaultMS: Double): Double;
|
|
var
|
|
FPSv: Integer;
|
|
begin
|
|
if AvgMode <= 0 then
|
|
Exit(0.0);
|
|
|
|
FPSv := EnsureRange(FPS, 1, 100);
|
|
if (Backmult > 0.0) and (Backmult < 1.0) then
|
|
Result := -1000.0 / (FPSv * Ln(Backmult))
|
|
else
|
|
Result := (1000.0 * Max(1, AvgCount)) / FPSv;
|
|
|
|
if IsNan(Result) or IsInfinite(Result) or (Result <= 0.0) then
|
|
Result := DefaultMS;
|
|
|
|
Result := EnsureRange(Result, 10.0, 5000.0);
|
|
end;
|
|
|
|
class function TSettingsManager.MacToStr(const MAC: array of Byte): string;
|
|
begin
|
|
Result := Format('%02X:%02X:%02X:%02X:%02X:%02X',
|
|
[MAC[0],MAC[1],MAC[2],MAC[3],MAC[4],MAC[5]]);
|
|
end;
|
|
|
|
class procedure TSettingsManager.DefaultBand(BandIdx: Integer; out B: TBandSettings);
|
|
begin
|
|
FillChar(B, SizeOf(B), 0);
|
|
B.VfoA := CFG_BAND_DEFAULT_FREQ[BandIdx];
|
|
B.VfoB := CFG_BAND_DEFAULT_FREQ[BandIdx];
|
|
B.Mode := CFG_BAND_DEFAULT_MODE[BandIdx];
|
|
B.FilterIdx := 5;
|
|
B.FilterBW := 2700;
|
|
B.AGCMode := 1;
|
|
B.AGCTop := 90;
|
|
B.CTun := False;
|
|
B.SpanHz := 192000;
|
|
end;
|
|
|
|
class procedure TSettingsManager.DefaultGlobal(out G: TGlobalSettings);
|
|
var i: Integer;
|
|
begin
|
|
FillChar(G, SizeOf(G), 0);
|
|
G.Volume := 70; G.DriveLevel := 50; G.ActiveVfo := 0;
|
|
G.NRMode := 0; G.NBMode := 0; G.SNBEnabled := False; G.ANFEnabled := False;
|
|
G.AGCSlope := 0; G.AGCHangThreshold := 100; G.LastBand := 5;
|
|
G.WfAGCEnabled := True;
|
|
G.WfNFEnabled := False;
|
|
G.SampleRate := 192000;
|
|
G.PAMaxPower := 100.0;
|
|
for i := 0 to CFG_BAND_COUNT-1 do G.PABandCal[i] := 100.0;
|
|
// Display defaults
|
|
G.FFTSize := 131072;
|
|
G.WindowType := 2; // Hann
|
|
G.SpecDetector := 0; // Peak
|
|
G.SpecAvgMode := 3; // Log Recursive
|
|
G.SpecAvgTimeMS := 30.0;
|
|
G.WfDetector := 0; // Peak
|
|
G.WfAvgMode := 3; // Log Recursive
|
|
G.WfAvgTimeMS := 120.0;
|
|
G.WfManualHigh := -80.0;
|
|
G.WfManualLow := -130.0;
|
|
G.WfAGCOffset := 0.0;
|
|
G.SpecRefLevel := -20.0;
|
|
G.SpecRange := 110.0;
|
|
G.SpecGridStep := 10.0;
|
|
G.AudioOutDevice := '';
|
|
G.AudioSampleRate := 48000;
|
|
G.AudioInDevice := '';
|
|
G.ShowSpectrum := True;
|
|
G.ShowWaterfall := True;
|
|
G.DisplayFPS := 60;
|
|
// CAT defaults
|
|
for i := 0 to 3 do begin
|
|
G.CATSerialEnabled[i] := False;
|
|
{$IFDEF MSWINDOWS}
|
|
G.CATSerialPort[i] := 'COM' + IntToStr(i+1);
|
|
{$ELSE}
|
|
G.CATSerialPort[i] := '/dev/ttyS' + IntToStr(i);
|
|
{$ENDIF}
|
|
G.CATSerialBaud[i] := 9600;
|
|
G.CATSerialDataBits[i] := 8;
|
|
G.CATSerialStopBits[i] := 1;
|
|
G.CATSerialParity[i] := 0; // None
|
|
end;
|
|
G.CATTcpEnabled := False;
|
|
G.CATTcpPort := 19090;
|
|
end;
|
|
|
|
constructor TSettingsManager.Create(const FilePath: string);
|
|
begin
|
|
inherited Create;
|
|
FFilePath := FilePath;
|
|
FRoot := TJSONObject.Create;
|
|
end;
|
|
|
|
destructor TSettingsManager.Destroy;
|
|
begin
|
|
FRoot.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSettingsManager.Load;
|
|
var F: TFileStream; P: TJSONParser; D: TJSONData;
|
|
begin
|
|
if not FileExists(FFilePath) then Exit;
|
|
try
|
|
F := TFileStream.Create(FFilePath, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
P := TJSONParser.Create(F, [joUTF8]);
|
|
try
|
|
D := P.Parse;
|
|
if (D <> nil) and (D is TJSONObject) then
|
|
begin FRoot.Free; FRoot := TJSONObject(D); end
|
|
else
|
|
D.Free;
|
|
finally P.Free; end;
|
|
finally F.Free; end;
|
|
except
|
|
FreeAndNil(FRoot);
|
|
FRoot := TJSONObject.Create;
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsManager.Save;
|
|
var
|
|
S: string;
|
|
F: TFileStream;
|
|
Buf: TBytes;
|
|
begin
|
|
try
|
|
S := FRoot.FormatJSON([], 2);
|
|
Buf := TEncoding.UTF8.GetBytes(S);
|
|
F := TFileStream.Create(FFilePath, fmCreate);
|
|
try
|
|
if Length(Buf) > 0 then
|
|
F.WriteBuffer(Buf[0], Length(Buf));
|
|
finally F.Free; end;
|
|
except end;
|
|
end;
|
|
|
|
function TSettingsManager.EnsureObj(P: TJSONObject; const K: string): TJSONObject;
|
|
var D: TJSONData; Idx: Integer;
|
|
begin
|
|
D := P.Find(K);
|
|
if (D <> nil) and (D is TJSONObject) then
|
|
Result := TJSONObject(D)
|
|
else
|
|
begin
|
|
if D <> nil then begin Idx := P.IndexOfName(K); if Idx >= 0 then P.Delete(Idx); end;
|
|
Result := TJSONObject.Create;
|
|
P.Add(K, Result);
|
|
end;
|
|
end;
|
|
|
|
function TSettingsManager.GetDevObj(const M: string): TJSONObject;
|
|
begin Result := EnsureObj(FRoot, M); end;
|
|
|
|
function TSettingsManager.GetGlobalObj(D: TJSONObject): TJSONObject;
|
|
begin Result := EnsureObj(D, 'global'); end;
|
|
|
|
function TSettingsManager.GetBandObj(D: TJSONObject; Idx: Integer): TJSONObject;
|
|
begin Result := EnsureObj(EnsureObj(D, 'bands'), IntToStr(Idx)); end;
|
|
|
|
function TSettingsManager.JI(O: TJSONObject; const K: string; Def: Integer): Integer;
|
|
var D: TJSONData;
|
|
begin D := O.Find(K); if D<>nil then try Result:=D.AsInteger; except Result:=Def; end else Result:=Def; end;
|
|
|
|
function TSettingsManager.JD(O: TJSONObject; const K: string; Def: Double): Double;
|
|
var D: TJSONData;
|
|
begin D := O.Find(K); if D<>nil then try Result:=D.AsFloat; except Result:=Def; end else Result:=Def; end;
|
|
|
|
function TSettingsManager.JB(O: TJSONObject; const K: string; Def: Boolean): Boolean;
|
|
var D: TJSONData;
|
|
begin D := O.Find(K); if D<>nil then try Result:=D.AsBoolean; except Result:=Def; end else Result:=Def; end;
|
|
|
|
procedure TSettingsManager.JW(O: TJSONObject; const K: string; V: Integer);
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := O.IndexOfName(K); if Idx >= 0 then O.Delete(Idx);
|
|
O.Add(K, V);
|
|
end;
|
|
|
|
procedure TSettingsManager.JW(O: TJSONObject; const K: string; V: Double);
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := O.IndexOfName(K); if Idx >= 0 then O.Delete(Idx);
|
|
O.Add(K, V);
|
|
end;
|
|
|
|
procedure TSettingsManager.JW(O: TJSONObject; const K: string; V: Boolean);
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := O.IndexOfName(K); if Idx >= 0 then O.Delete(Idx);
|
|
O.Add(K, V);
|
|
end;
|
|
|
|
function TSettingsManager.JS(O: TJSONObject; const K: string; const Def: string): string;
|
|
var D: TJSONData;
|
|
begin D := O.Find(K); if D<>nil then try Result:=D.AsString; except Result:=Def; end else Result:=Def; end;
|
|
|
|
procedure TSettingsManager.JWS(O: TJSONObject; const K: string; const V: string);
|
|
var Idx: Integer;
|
|
begin
|
|
Idx := O.IndexOfName(K); if Idx >= 0 then O.Delete(Idx);
|
|
O.Add(K, V);
|
|
end;
|
|
|
|
function TSettingsManager.LoadDevice(const MAC: array of Byte;
|
|
out G: TGlobalSettings;
|
|
var Bands: array of TBandSettings): Boolean;
|
|
var MacStr: string; DevObj,GObj,BObj: TJSONObject; i: Integer;
|
|
begin
|
|
MacStr := MacToStr(MAC);
|
|
Result := FRoot.Find(MacStr) <> nil;
|
|
DevObj := GetDevObj(MacStr);
|
|
GObj := GetGlobalObj(DevObj);
|
|
|
|
G.Volume := JI(GObj,'volume',70);
|
|
G.DriveLevel := JI(GObj,'drive_level',50);
|
|
G.ActiveVfo := JI(GObj,'active_vfo',0);
|
|
G.NRMode := JI(GObj,'nr_mode',Ord(JB(GObj,'nr_enabled',False)));
|
|
G.NBMode := JI(GObj,'nb_mode',Ord(JB(GObj,'nb_enabled',False)));
|
|
G.SNBEnabled := JB(GObj,'snb_enabled',False);
|
|
G.ANFEnabled := JB(GObj,'anf_enabled',False);
|
|
G.AGCSlope := JI(GObj,'agc_slope',0);
|
|
G.AGCHangThreshold := JI(GObj,'agc_hang_threshold',100);
|
|
G.WfAGCEnabled := JB(GObj,'wf_agc_enabled',True);
|
|
G.WfNFEnabled := JB(GObj,'wf_nf_enabled',False);
|
|
G.LastBand := JI(GObj,'last_band',5);
|
|
G.SampleRate := JI(GObj,'sample_rate',192000);
|
|
// Display settings
|
|
G.FFTSize := JI(GObj,'fft_size',131072);
|
|
G.WindowType := JI(GObj,'win_type',2);
|
|
G.SpecDetector := JI(GObj,'spec_detector',0);
|
|
G.SpecAvgMode := JI(GObj,'spec_avg_mode',3);
|
|
if GObj.Find('spec_avg_time_ms') <> nil then
|
|
G.SpecAvgTimeMS := JD(GObj,'spec_avg_time_ms',30.0)
|
|
else if (GObj.Find('spec_avg_count') <> nil) or
|
|
(GObj.Find('spec_backmult') <> nil) then
|
|
begin
|
|
G.SpecAvgTimeMS := LegacyAverageTimeMS(
|
|
JI(GObj,'display_fps',60),
|
|
G.SpecAvgMode,
|
|
JI(GObj,'spec_avg_count',1),
|
|
JD(GObj,'spec_backmult',0.0),
|
|
30.0);
|
|
end
|
|
else
|
|
G.SpecAvgTimeMS := 30.0;
|
|
G.WfDetector := JI(GObj,'wf_detector',0);
|
|
G.WfAvgMode := JI(GObj,'wf_avg_mode',3);
|
|
if GObj.Find('wf_avg_time_ms') <> nil then
|
|
G.WfAvgTimeMS := JD(GObj,'wf_avg_time_ms',120.0)
|
|
else if (GObj.Find('wf_avg_count') <> nil) or
|
|
(GObj.Find('wf_backmult') <> nil) then
|
|
begin
|
|
G.WfAvgTimeMS := LegacyAverageTimeMS(
|
|
JI(GObj,'display_fps',60),
|
|
G.WfAvgMode,
|
|
JI(GObj,'wf_avg_count',2),
|
|
JD(GObj,'wf_backmult',0.25),
|
|
120.0);
|
|
end
|
|
else
|
|
G.WfAvgTimeMS := 120.0;
|
|
G.WfManualHigh := JD(GObj,'wf_manual_high',-80.0);
|
|
G.WfManualLow := JD(GObj,'wf_manual_low',-130.0);
|
|
G.WfAGCOffset := JD(GObj,'wf_agc_offset',0.0);
|
|
G.SpecRefLevel := JD(GObj,'spec_ref_level',-20.0);
|
|
G.SpecRange := JD(GObj,'spec_range',110.0);
|
|
G.SpecGridStep := JD(GObj,'spec_grid_step',10.0);
|
|
// Audio settings
|
|
G.AudioOutDevice := JS(GObj,'audio_out_device','');
|
|
G.AudioSampleRate := JI(GObj,'audio_sample_rate',48000);
|
|
G.AudioInDevice := JS(GObj,'audio_in_device','');
|
|
// Visibility settings
|
|
G.ShowSpectrum := JB(GObj,'show_spectrum',True);
|
|
G.ShowWaterfall := JB(GObj,'show_waterfall',True);
|
|
G.DisplayFPS := JI(GObj,'display_fps',60);
|
|
// PA settings
|
|
G.PAMaxPower := JD(GObj,'pa_max_power',100.0);
|
|
for i := 0 to CFG_BAND_COUNT-1 do
|
|
G.PABandCal[i] := JD(GObj,'pa_band_cal_'+IntToStr(i),100.0);
|
|
// CAT serial settings
|
|
for i := 0 to 3 do begin
|
|
G.CATSerialEnabled[i] := JB(GObj,'cat_serial_en_'+IntToStr(i), False);
|
|
{$IFDEF MSWINDOWS}
|
|
G.CATSerialPort[i] := JS(GObj,'cat_serial_port_'+IntToStr(i), 'COM'+IntToStr(i+1));
|
|
{$ELSE}
|
|
G.CATSerialPort[i] := JS(GObj,'cat_serial_port_'+IntToStr(i), '/dev/ttyS'+IntToStr(i));
|
|
{$ENDIF}
|
|
G.CATSerialBaud[i] := JI(GObj,'cat_serial_baud_'+IntToStr(i), 9600);
|
|
G.CATSerialDataBits[i] := JI(GObj,'cat_serial_dbits_'+IntToStr(i), 8);
|
|
G.CATSerialStopBits[i] := JI(GObj,'cat_serial_sbits_'+IntToStr(i), 1);
|
|
G.CATSerialParity[i] := JI(GObj,'cat_serial_parity_'+IntToStr(i), 0);
|
|
end;
|
|
G.CATTcpEnabled := JB(GObj,'cat_tcp_enabled', False);
|
|
G.CATTcpPort := JI(GObj,'cat_tcp_port', 19090);
|
|
|
|
for i := 0 to CFG_BAND_COUNT-1 do
|
|
begin
|
|
DefaultBand(i, Bands[i]);
|
|
BObj := GetBandObj(DevObj, i);
|
|
Bands[i].VfoA := JD(BObj,'vfo_a', Bands[i].VfoA);
|
|
Bands[i].VfoB := JD(BObj,'vfo_b', Bands[i].VfoB);
|
|
Bands[i].Mode := JI(BObj,'mode', Bands[i].Mode);
|
|
Bands[i].FilterIdx := JI(BObj,'filter_idx', Bands[i].FilterIdx);
|
|
Bands[i].FilterBW := JI(BObj,'filter_bw', Bands[i].FilterBW);
|
|
Bands[i].AGCMode := JI(BObj,'agc_mode', Bands[i].AGCMode);
|
|
Bands[i].AGCTop := JI(BObj,'agc_top', Bands[i].AGCTop);
|
|
Bands[i].CTun := JB(BObj,'ctun', Bands[i].CTun);
|
|
Bands[i].SpanHz := JD(BObj,'span_hz', Bands[i].SpanHz);
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsManager.SaveGlobal(const MAC: array of Byte;
|
|
const G: TGlobalSettings);
|
|
var O: TJSONObject; i: Integer;
|
|
begin
|
|
O := GetGlobalObj(GetDevObj(MacToStr(MAC)));
|
|
JW(O,'volume',G.Volume); JW(O,'drive_level',G.DriveLevel);
|
|
JW(O,'active_vfo',G.ActiveVfo);
|
|
JW(O,'nr_mode',G.NRMode); JW(O,'nb_mode',G.NBMode);
|
|
JW(O,'nr_enabled',G.NRMode <> 0); JW(O,'nb_enabled',G.NBMode <> 0);
|
|
JW(O,'snb_enabled',G.SNBEnabled);
|
|
JW(O,'anf_enabled',G.ANFEnabled);
|
|
JW(O,'agc_slope',G.AGCSlope);
|
|
JW(O,'agc_hang_threshold',G.AGCHangThreshold);
|
|
JW(O,'wf_agc_enabled',G.WfAGCEnabled);
|
|
JW(O,'wf_nf_enabled',G.WfNFEnabled);
|
|
JW(O,'last_band',G.LastBand);
|
|
JW(O,'sample_rate',G.SampleRate);
|
|
// Display settings
|
|
JW(O,'fft_size',G.FFTSize);
|
|
JW(O,'win_type',G.WindowType);
|
|
JW(O,'spec_detector',G.SpecDetector);
|
|
JW(O,'spec_avg_mode',G.SpecAvgMode);
|
|
JW(O,'spec_avg_time_ms',G.SpecAvgTimeMS);
|
|
JW(O,'wf_detector',G.WfDetector);
|
|
JW(O,'wf_avg_mode',G.WfAvgMode);
|
|
JW(O,'wf_avg_time_ms',G.WfAvgTimeMS);
|
|
JW(O,'wf_manual_high',G.WfManualHigh);
|
|
JW(O,'wf_manual_low',G.WfManualLow);
|
|
JW(O,'wf_agc_offset',G.WfAGCOffset);
|
|
JW(O,'spec_ref_level',G.SpecRefLevel);
|
|
JW(O,'spec_range',G.SpecRange);
|
|
JW(O,'spec_grid_step',G.SpecGridStep);
|
|
// Audio settings
|
|
JWS(O,'audio_out_device',G.AudioOutDevice);
|
|
JW(O,'audio_sample_rate',G.AudioSampleRate);
|
|
JWS(O,'audio_in_device',G.AudioInDevice);
|
|
// Visibility settings
|
|
JW(O,'show_spectrum',G.ShowSpectrum);
|
|
JW(O,'show_waterfall',G.ShowWaterfall);
|
|
JW(O,'display_fps',G.DisplayFPS);
|
|
// PA settings
|
|
JW(O,'pa_max_power',G.PAMaxPower);
|
|
for i := 0 to CFG_BAND_COUNT-1 do
|
|
JW(O,'pa_band_cal_'+IntToStr(i),G.PABandCal[i]);
|
|
// CAT serial settings
|
|
for i := 0 to 3 do begin
|
|
JW(O,'cat_serial_en_'+IntToStr(i), G.CATSerialEnabled[i]);
|
|
JWS(O,'cat_serial_port_'+IntToStr(i),G.CATSerialPort[i]);
|
|
JW(O,'cat_serial_baud_'+IntToStr(i), G.CATSerialBaud[i]);
|
|
JW(O,'cat_serial_dbits_'+IntToStr(i),G.CATSerialDataBits[i]);
|
|
JW(O,'cat_serial_sbits_'+IntToStr(i),G.CATSerialStopBits[i]);
|
|
JW(O,'cat_serial_parity_'+IntToStr(i),G.CATSerialParity[i]);
|
|
end;
|
|
JW(O,'cat_tcp_enabled',G.CATTcpEnabled);
|
|
JW(O,'cat_tcp_port',G.CATTcpPort);
|
|
end;
|
|
|
|
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
|
const B: TBandSettings);
|
|
var O: TJSONObject;
|
|
begin
|
|
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
|
O := GetBandObj(GetDevObj(MacToStr(MAC)), BandIdx);
|
|
JW(O,'vfo_a',B.VfoA); JW(O,'vfo_b',B.VfoB);
|
|
JW(O,'mode',B.Mode);
|
|
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);
|
|
end;
|
|
|
|
function TSettingsManager.LoadBand(const MAC: array of Byte; BandIdx: Integer;
|
|
out B: TBandSettings): Boolean;
|
|
var MacStr: string; O: TJSONObject;
|
|
begin
|
|
DefaultBand(BandIdx, B);
|
|
MacStr := MacToStr(MAC);
|
|
Result := FRoot.Find(MacStr) <> nil;
|
|
if not Result then Exit;
|
|
O := GetBandObj(GetDevObj(MacStr), BandIdx);
|
|
B.VfoA := JD(O,'vfo_a', B.VfoA);
|
|
B.VfoB := JD(O,'vfo_b', B.VfoB);
|
|
B.Mode := JI(O,'mode', B.Mode);
|
|
B.FilterIdx := JI(O,'filter_idx', B.FilterIdx);
|
|
B.FilterBW := JI(O,'filter_bw', B.FilterBW);
|
|
B.AGCMode := JI(O,'agc_mode', B.AGCMode);
|
|
B.AGCTop := JI(O,'agc_top', B.AGCTop);
|
|
B.CTun := JB(O,'ctun', B.CTun);
|
|
B.SpanHz := JD(O,'span_hz', B.SpanHz);
|
|
end;
|
|
|
|
procedure TSettingsManager.SaveWindowBounds(L, T, W, H: Integer);
|
|
var O: TJSONObject;
|
|
begin
|
|
O := EnsureObj(FRoot, 'window');
|
|
JW(O, 'left', L);
|
|
JW(O, 'top', T);
|
|
JW(O, 'width', W);
|
|
JW(O, 'height', H);
|
|
end;
|
|
|
|
procedure TSettingsManager.LoadWindowBounds(out L, T, W, H: Integer);
|
|
var O: TJSONObject;
|
|
begin
|
|
L := 80; T := 80; W := 1400; H := 900;
|
|
if FRoot.Find('window') = nil then Exit;
|
|
O := EnsureObj(FRoot, 'window');
|
|
L := JI(O, 'left', 80);
|
|
T := JI(O, 'top', 80);
|
|
W := JI(O, 'width', 1400);
|
|
H := JI(O, 'height', 900);
|
|
end;
|
|
|
|
procedure TSettingsManager.SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);
|
|
var O: TJSONObject;
|
|
begin
|
|
O := EnsureObj(FRoot, 'startup');
|
|
JW(O, 'vfo_a', VfoA);
|
|
JW(O, 'vfo_b', VfoB);
|
|
JW(O, 'sample_rate', SampleRate);
|
|
end;
|
|
|
|
function TSettingsManager.LoadStartupPreview(out VfoA, VfoB: Double; out SampleRate: Integer): Boolean;
|
|
var O: TJSONObject;
|
|
begin
|
|
Result := FRoot.Find('startup') <> nil;
|
|
if not Result then Exit;
|
|
|
|
O := EnsureObj(FRoot, 'startup');
|
|
VfoA := JD(O, 'vfo_a', 14200000);
|
|
VfoB := JD(O, 'vfo_b', 7100000);
|
|
SampleRate := JI(O, 'sample_rate', 192000);
|
|
end;
|
|
|
|
procedure TSettingsManager.SaveCATSettings(const G: TGlobalSettings);
|
|
var O: TJSONObject; i: Integer;
|
|
begin
|
|
O := EnsureObj(FRoot, 'cat');
|
|
JW(O, 'tcp_en', G.CATTcpEnabled);
|
|
JW(O, 'tcp_port', G.CATTcpPort);
|
|
for i := 0 to 3 do
|
|
begin
|
|
JW(O, 'ser_en_' +IntToStr(i), G.CATSerialEnabled[i]);
|
|
JWS(O, 'ser_port_' +IntToStr(i), G.CATSerialPort[i]);
|
|
JW(O, 'ser_baud_' +IntToStr(i), G.CATSerialBaud[i]);
|
|
JW(O, 'ser_dbits_' +IntToStr(i), G.CATSerialDataBits[i]);
|
|
JW(O, 'ser_sbits_' +IntToStr(i), G.CATSerialStopBits[i]);
|
|
JW(O, 'ser_parity_'+IntToStr(i), G.CATSerialParity[i]);
|
|
end;
|
|
end;
|
|
|
|
procedure TSettingsManager.LoadCATSettings(var G: TGlobalSettings);
|
|
var O: TJSONObject; i: Integer;
|
|
begin
|
|
if FRoot.Find('cat') = nil then Exit;
|
|
O := EnsureObj(FRoot, 'cat');
|
|
G.CATTcpEnabled := JB(O, 'tcp_en', False);
|
|
G.CATTcpPort := JI(O, 'tcp_port', 19090);
|
|
for i := 0 to 3 do
|
|
begin
|
|
G.CATSerialEnabled[i] := JB(O, 'ser_en_' +IntToStr(i), False);
|
|
{$IFDEF MSWINDOWS}
|
|
G.CATSerialPort[i] := JS(O, 'ser_port_' +IntToStr(i), 'COM'+IntToStr(i+1));
|
|
{$ELSE}
|
|
G.CATSerialPort[i] := JS(O, 'ser_port_' +IntToStr(i), '/dev/ttyS'+IntToStr(i));
|
|
{$ENDIF}
|
|
G.CATSerialBaud[i] := JI(O, 'ser_baud_' +IntToStr(i), 9600);
|
|
G.CATSerialDataBits[i] := JI(O, 'ser_dbits_' +IntToStr(i), 8);
|
|
G.CATSerialStopBits[i] := JI(O, 'ser_sbits_' +IntToStr(i), 1);
|
|
G.CATSerialParity[i] := JI(O, 'ser_parity_'+IntToStr(i), 0);
|
|
end;
|
|
end;
|
|
|
|
end.
|