mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
init
This commit is contained in:
+336
@@ -0,0 +1,336 @@
|
||||
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, 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;
|
||||
NREnabled: Boolean;
|
||||
NBEnabled: Boolean;
|
||||
ANFEnabled: Boolean;
|
||||
AGCSlope: Integer;
|
||||
AGCHangThreshold: Integer;
|
||||
WfAGCEnabled: Boolean;
|
||||
WfNFEnabled: Boolean;
|
||||
LastBand: Integer;
|
||||
SampleRate: Integer; // глобальный — один для всех диапазонов
|
||||
WindowLeft: Integer;
|
||||
WindowTop: Integer;
|
||||
WindowWidth: Integer;
|
||||
WindowHeight: Integer;
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
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);
|
||||
begin
|
||||
FillChar(G, SizeOf(G), 0);
|
||||
G.Volume := 70; G.DriveLevel := 50; G.ActiveVfo := 0;
|
||||
G.AGCSlope := 0; G.AGCHangThreshold := 100; G.LastBand := 5;
|
||||
G.SampleRate := 192000;
|
||||
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.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.NREnabled := JB(GObj,'nr_enabled',False);
|
||||
G.NBEnabled := JB(GObj,'nb_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',False);
|
||||
G.WfNFEnabled := JB(GObj,'wf_nf_enabled',False);
|
||||
G.LastBand := JI(GObj,'last_band',5);
|
||||
G.SampleRate := JI(GObj,'sample_rate',192000);
|
||||
|
||||
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;
|
||||
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_enabled',G.NREnabled); JW(O,'nb_enabled',G.NBEnabled);
|
||||
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);
|
||||
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;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user