mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
add nb nr
This commit is contained in:
+161
-50
@@ -315,6 +315,7 @@ type
|
||||
TrkVolume: TTrackBar;
|
||||
BtnNR: TFlatButton;
|
||||
BtnNB: TFlatButton;
|
||||
BtnSNB: TFlatButton;
|
||||
BtnANF: TFlatButton;
|
||||
BtnMute: TFlatButton;
|
||||
|
||||
@@ -436,8 +437,14 @@ type
|
||||
procedure BtnVfoBCopyAClick(Sender: TObject);
|
||||
procedure BtnMOXClick(Sender: TObject);
|
||||
procedure BtnMuteClick(Sender: TObject);
|
||||
procedure UpdateNRButton;
|
||||
procedure UpdateNBButton;
|
||||
procedure UpdateSNBButton;
|
||||
procedure UpdateANFButton;
|
||||
procedure ApplyNoiseFilterButtonsToDSP;
|
||||
procedure BtnNRClick(Sender: TObject);
|
||||
procedure BtnNBClick(Sender: TObject);
|
||||
procedure BtnSNBClick(Sender: TObject);
|
||||
procedure BtnANFClick(Sender: TObject);
|
||||
procedure BtnSpanClick(Sender: TObject);
|
||||
// Веб-интерфейс: callbacks от TWebServer
|
||||
@@ -454,8 +461,9 @@ type
|
||||
procedure WebOnRun(On_: Boolean);
|
||||
procedure WebOnMute(On_: Boolean);
|
||||
procedure WebOnCtun(On_: Boolean);
|
||||
procedure WebOnNR(On_: Boolean);
|
||||
procedure WebOnNB(On_: Boolean);
|
||||
procedure WebOnNR(Mode: Integer);
|
||||
procedure WebOnNB(Mode: Integer);
|
||||
procedure WebOnSNB(On_: Boolean);
|
||||
procedure WebOnANF(On_: Boolean);
|
||||
// Synchronize-обёртки (выполняются в UI-потоке)
|
||||
procedure SyncWebFreq;
|
||||
@@ -473,6 +481,7 @@ type
|
||||
procedure SyncWebCtun;
|
||||
procedure SyncWebNR;
|
||||
procedure SyncWebNB;
|
||||
procedure SyncWebSNB;
|
||||
procedure SyncWebANF;
|
||||
procedure BtnWfAGCClick(Sender: TObject);
|
||||
procedure BtnWfNFClick(Sender: TObject);
|
||||
@@ -618,9 +627,10 @@ begin
|
||||
Result.Volume := FVolume;
|
||||
Result.DriveLevel := FDriveLevel;
|
||||
Result.ActiveVfo := FActiveVfo;
|
||||
Result.NREnabled := BtnNR.Tag = 1;
|
||||
Result.NBEnabled := BtnNB.Tag = 1;
|
||||
Result.ANFEnabled := BtnANF.Tag = 1;
|
||||
Result.NRMode := BtnNR.Tag;
|
||||
Result.NBMode := BtnNB.Tag;
|
||||
Result.SNBEnabled := BtnSNB.Tag <> 0;
|
||||
Result.ANFEnabled := BtnANF.Tag <> 0;
|
||||
Result.AGCSlope := 0;
|
||||
Result.AGCHangThreshold := 100;
|
||||
Result.WfAGCEnabled := FWfAGCEnabled;
|
||||
@@ -717,7 +727,8 @@ end;
|
||||
|
||||
procedure TMainForm.FormCreate(Sender: TObject);
|
||||
var
|
||||
bi_: Integer;
|
||||
bi_, StartupRate: Integer;
|
||||
StartupVfoA, StartupVfoB: Double;
|
||||
begin
|
||||
FVfoA := 14200000;
|
||||
FVfoB := 7100000;
|
||||
@@ -757,6 +768,17 @@ begin
|
||||
// Загружаем JSON настройки
|
||||
FSettings := TSettingsManager.Create;
|
||||
FSettings.Load;
|
||||
if FSettings.LoadStartupPreview(StartupVfoA, StartupVfoB, StartupRate) then
|
||||
begin
|
||||
FVfoA := StartupVfoA;
|
||||
FVfoB := StartupVfoB;
|
||||
FCenterFreq := FVfoA;
|
||||
if StartupRate > 0 then
|
||||
begin
|
||||
FSampleRate := StartupRate;
|
||||
FSpanHz := StartupRate;
|
||||
end;
|
||||
end;
|
||||
// Восстанавливаем размер/позицию окна при старте (до подключения устройства)
|
||||
RestoreWindowBounds;
|
||||
// S-метр позиционируем после рестора размера окна
|
||||
@@ -780,6 +802,7 @@ begin
|
||||
FWebServer.OnCtun := WebOnCtun;
|
||||
FWebServer.OnNR := WebOnNR;
|
||||
FWebServer.OnNB := WebOnNB;
|
||||
FWebServer.OnSNB := WebOnSNB;
|
||||
FWebServer.OnANF := WebOnANF;
|
||||
FWebServer.Start;
|
||||
FSMeterPeak := -130;
|
||||
@@ -824,6 +847,12 @@ begin
|
||||
FNetwork.OnMicPacket := OnMicPacketCB;
|
||||
|
||||
BuildUI;
|
||||
StyleSpanButton(BtnSpan48k, FSampleRate = 48000);
|
||||
StyleSpanButton(BtnSpan96k, FSampleRate = 96000);
|
||||
StyleSpanButton(BtnSpan192k, FSampleRate = 192000);
|
||||
StyleSpanButton(BtnSpan384k, FSampleRate = 384000);
|
||||
StyleSpanButton(BtnSpan768k, FSampleRate = 768000);
|
||||
StyleSpanButton(BtnSpan1536k, FSampleRate = 1536000);
|
||||
ResizeSMeter;
|
||||
ApplyDarkTheme;
|
||||
UpdateVfoDisplay;
|
||||
@@ -835,7 +864,7 @@ begin
|
||||
// BufSize=512: FBufSize=2048 @ 192kHz, FAudioBufSize=512 @ 48kHz.
|
||||
// Уменьшение с 1024 вдвое сокращает время построения downsampler в OpenChannel RXA
|
||||
// (~1300ms → ~650ms). Латентность: 512/48000 ≈ 10.7ms — допустимо для SDR.
|
||||
FDSPEngine := TWDSPEngine.Create(192000, 48000, 512);
|
||||
FDSPEngine := TWDSPEngine.Create(FSampleRate, 48000, 512);
|
||||
FDSPEngine.OnAudio := OnAudioReady;
|
||||
FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||
|
||||
@@ -898,6 +927,7 @@ begin
|
||||
FSettings.SaveGlobal(FDevMAC, MakeGlobalSettings);
|
||||
end;
|
||||
// Размер окна сохраняем всегда (не зависит от подключения)
|
||||
FSettings.SaveStartupPreview(FVfoA, FVfoB, FSampleRate);
|
||||
FSettings.SaveWindowBounds(Left, Top, Width, Height);
|
||||
FSettings.Save;
|
||||
FSettings.Free;
|
||||
@@ -1133,22 +1163,21 @@ begin
|
||||
PanelRX.Parent := PanelLeft;
|
||||
PanelRX.SetBounds(0, Y, LEFT_W, 120);
|
||||
PanelRX.BevelOuter := bvNone;
|
||||
MakeLbl(PanelRX, 'RX', 4, 2);
|
||||
|
||||
// AGC mode — 5 кнопок в ряд
|
||||
MakeLbl(PanelRX, 'AGC', 4, 18);
|
||||
W := (LEFT_W - 36) div 5;
|
||||
BtnAGCMode[0] := MakeBtn(PanelRX,'FAST', 34+0*(W+1),14,W,BTN_SM,BtnAGCModeClick); BtnAGCMode[0].Tag:=0;
|
||||
BtnAGCMode[1] := MakeBtn(PanelRX,'MED', 34+1*(W+1),14,W,BTN_SM,BtnAGCModeClick); BtnAGCMode[1].Tag:=1;
|
||||
BtnAGCMode[2] := MakeBtn(PanelRX,'SLOW', 34+2*(W+1),14,W,BTN_SM,BtnAGCModeClick); BtnAGCMode[2].Tag:=2;
|
||||
BtnAGCMode[3] := MakeBtn(PanelRX,'LONG', 34+3*(W+1),14,W,BTN_SM,BtnAGCModeClick); BtnAGCMode[3].Tag:=3;
|
||||
BtnAGCMode[4] := MakeBtn(PanelRX,'OFF', 34+4*(W+1),14,W,BTN_SM,BtnAGCModeClick); BtnAGCMode[4].Tag:=4;
|
||||
MakeLbl(PanelRX, 'AGC', 4, 4);
|
||||
W := (LEFT_W - 10) div 5;
|
||||
BtnAGCMode[0] := MakeBtn(PanelRX,'FAST', 2, 22, W, BTN_SM, BtnAGCModeClick); BtnAGCMode[0].Tag:=0;
|
||||
BtnAGCMode[1] := MakeBtn(PanelRX,'MED', W+4, 22, W, BTN_SM, BtnAGCModeClick); BtnAGCMode[1].Tag:=1;
|
||||
BtnAGCMode[2] := MakeBtn(PanelRX,'SLOW', 2*W+6, 22, W, BTN_SM, BtnAGCModeClick); BtnAGCMode[2].Tag:=2;
|
||||
BtnAGCMode[3] := MakeBtn(PanelRX,'LONG', 3*W+8, 22, W, BTN_SM, BtnAGCModeClick); BtnAGCMode[3].Tag:=3;
|
||||
BtnAGCMode[4] := MakeBtn(PanelRX,'OFF', 4*W+10, 22, W, BTN_SM, BtnAGCModeClick); BtnAGCMode[4].Tag:=4;
|
||||
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FAGCMode);
|
||||
|
||||
// AGC level slider
|
||||
MakeLbl(PanelRX, 'THRESH', 4, 41);
|
||||
MakeLbl(PanelRX, 'THRESH', 4, 50);
|
||||
TrkAGC := TTrackBar.Create(Self);
|
||||
TrkAGC.Parent := PanelRX; TrkAGC.Left := 56; TrkAGC.Top := 37;
|
||||
TrkAGC.Parent := PanelRX; TrkAGC.Left := 56; TrkAGC.Top := 46;
|
||||
TrkAGC.Width := LEFT_W - 96; TrkAGC.Height := 20;
|
||||
TrkAGC.Min := 20; TrkAGC.Max := 120; // 20..120 → −20..-120 dBm
|
||||
TrkAGC.Position := FAGCTop;
|
||||
@@ -1157,26 +1186,27 @@ begin
|
||||
|
||||
LblAGCTop := TLabel.Create(Self);
|
||||
LblAGCTop.Parent := PanelRX;
|
||||
LblAGCTop.Left := LEFT_W - 38; LblAGCTop.Top := 41;
|
||||
LblAGCTop.Left := LEFT_W - 38; LblAGCTop.Top := 50;
|
||||
LblAGCTop.Caption := Format('-%ddB', [FAGCTop]);
|
||||
LblAGCTop.Font.Color := CLR_TEXT; LblAGCTop.Font.Name := 'Courier New';
|
||||
LblAGCTop.Font.Size := 7;
|
||||
|
||||
// VOL
|
||||
MakeLbl(PanelRX, 'VOL', 4, 65);
|
||||
MakeLbl(PanelRX, 'VOL', 4, 74);
|
||||
TrkVolume := TTrackBar.Create(Self);
|
||||
TrkVolume.Parent := PanelRX; TrkVolume.Left := 34; TrkVolume.Top := 61;
|
||||
TrkVolume.Parent := PanelRX; TrkVolume.Left := 34; TrkVolume.Top := 70;
|
||||
TrkVolume.Width := LEFT_W - 38; TrkVolume.Height := 20;
|
||||
TrkVolume.Min := 0; TrkVolume.Max := 100;
|
||||
TrkVolume.Position := FVolume; TrkVolume.TickStyle := tsNone;
|
||||
TrkVolume.OnChange := TrkVolumeChange;
|
||||
|
||||
// NR NB ANF MUTE
|
||||
W := (LEFT_W - 8) div 4;
|
||||
BtnNR := MakeBtn(PanelRX, 'NR', 2, 88, W, BTN_SM, BtnNRClick);
|
||||
BtnNB := MakeBtn(PanelRX, 'NB', W+4, 88, W, BTN_SM, BtnNBClick);
|
||||
BtnANF := MakeBtn(PanelRX, 'ANF',2*W+6, 88, W, BTN_SM, BtnANFClick);
|
||||
BtnMute:= MakeBtn(PanelRX, 'MUTE',3*W+8,88, W, BTN_SM, BtnMuteClick);
|
||||
// NR NB SNB ANF MUTE
|
||||
W := (LEFT_W - 10) div 5;
|
||||
BtnNR := MakeBtn(PanelRX, 'NR', 2, 96, W, BTN_SM, BtnNRClick);
|
||||
BtnNB := MakeBtn(PanelRX, 'NB', W+4, 96, W, BTN_SM, BtnNBClick);
|
||||
BtnSNB := MakeBtn(PanelRX, 'SNB', 2*W+6, 96, W, BTN_SM, BtnSNBClick);
|
||||
BtnANF := MakeBtn(PanelRX, 'ANF', 3*W+8, 96, W, BTN_SM, BtnANFClick);
|
||||
BtnMute:= MakeBtn(PanelRX, 'MUTE', 4*W+10, 96, W, BTN_SM, BtnMuteClick);
|
||||
|
||||
Inc(Y, 124);
|
||||
|
||||
@@ -2661,7 +2691,7 @@ begin
|
||||
FCurrentBand,
|
||||
FRunning and FNetwork.Connected,
|
||||
FRunning, FMuted, FCTun,
|
||||
BtnNR.Tag = 1, BtnNB.Tag = 1, BtnANF.Tag = 1,
|
||||
BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0,
|
||||
FCenterFreq, FFilter);
|
||||
end;
|
||||
|
||||
@@ -3067,9 +3097,10 @@ begin
|
||||
FSampleRate := G_Settings.SampleRate;
|
||||
FSpanHz := FSampleRate;
|
||||
end;
|
||||
BtnNR.Tag := Ord(G_Settings.NREnabled); StyleButton(BtnNR, G_Settings.NREnabled);
|
||||
BtnNB.Tag := Ord(G_Settings.NBEnabled); StyleButton(BtnNB, G_Settings.NBEnabled);
|
||||
BtnANF.Tag := Ord(G_Settings.ANFEnabled); StyleButton(BtnANF, G_Settings.ANFEnabled);
|
||||
BtnNR.Tag := EnsureRange(G_Settings.NRMode, 0, 4); UpdateNRButton;
|
||||
BtnNB.Tag := EnsureRange(G_Settings.NBMode, 0, 2); UpdateNBButton;
|
||||
BtnSNB.Tag := Ord(G_Settings.SNBEnabled); UpdateSNBButton;
|
||||
BtnANF.Tag := Ord(G_Settings.ANFEnabled); UpdateANFButton;
|
||||
FWfAGCEnabled := G_Settings.WfAGCEnabled; StyleButton(BtnWfAGC, FWfAGCEnabled);
|
||||
FWfNFEnabled := G_Settings.WfNFEnabled; StyleButton(BtnWfNF, FWfNFEnabled);
|
||||
RestoreBand(FCurrentBand);
|
||||
@@ -3109,6 +3140,7 @@ begin
|
||||
FDSPEngine.SetMode(FMode);
|
||||
FDSPEngine.SetVolume(FVolume / 100.0);
|
||||
ApplyModeFilter;
|
||||
ApplyNoiseFilterButtonsToDSP;
|
||||
// ChangeSampleRate пересоздаёт WDSP-канал — восстанавливаем все настройки
|
||||
FDSPEngine.SetAGCTop(FAGCTop);
|
||||
FDSPEngine.SetAGC(TWDSPAGCMode(FAGCMode), 50.0);
|
||||
@@ -3617,28 +3649,81 @@ begin
|
||||
if FWDSPReady then FDSPEngine.SetMute(FMuted);
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateNRButton;
|
||||
begin
|
||||
case BtnNR.Tag of
|
||||
1: BtnNR.Caption := 'NR';
|
||||
2: BtnNR.Caption := 'NR2';
|
||||
3: BtnNR.Caption := 'NR3';
|
||||
4: BtnNR.Caption := 'NR4';
|
||||
else
|
||||
BtnNR.Caption := 'NR';
|
||||
end;
|
||||
StyleButton(BtnNR, BtnNR.Tag <> 0);
|
||||
BtnNR.Invalidate;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateNBButton;
|
||||
begin
|
||||
case BtnNB.Tag of
|
||||
1: BtnNB.Caption := 'NB';
|
||||
2: BtnNB.Caption := 'NB2';
|
||||
else
|
||||
BtnNB.Caption := 'NB';
|
||||
end;
|
||||
StyleButton(BtnNB, BtnNB.Tag <> 0);
|
||||
BtnNB.Invalidate;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateSNBButton;
|
||||
begin
|
||||
BtnSNB.Caption := 'SNB';
|
||||
StyleButton(BtnSNB, BtnSNB.Tag <> 0);
|
||||
BtnSNB.Invalidate;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateANFButton;
|
||||
begin
|
||||
BtnANF.Caption := 'ANF';
|
||||
StyleButton(BtnANF, BtnANF.Tag <> 0);
|
||||
BtnANF.Invalidate;
|
||||
end;
|
||||
|
||||
procedure TMainForm.ApplyNoiseFilterButtonsToDSP;
|
||||
begin
|
||||
if not FWDSPReady then Exit;
|
||||
FDSPEngine.SetNRMode(BtnNR.Tag);
|
||||
FDSPEngine.SetNBMode(BtnNB.Tag);
|
||||
FDSPEngine.SetSNB(BtnSNB.Tag <> 0);
|
||||
FDSPEngine.SetANF(BtnANF.Tag <> 0);
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnNRClick(Sender: TObject);
|
||||
begin
|
||||
// Используем BtnNR напрямую — корректно работает и при вызове из веб-синхронизации (Sender=nil)
|
||||
BtnNR.Tag := 1 - BtnNR.Tag;
|
||||
StyleButton(BtnNR, BtnNR.Tag = 1);
|
||||
if FWDSPReady then FDSPEngine.SetNR(BtnNR.Tag = 1);
|
||||
BtnNR.Tag := (BtnNR.Tag + 1) mod 5;
|
||||
UpdateNRButton;
|
||||
if FWDSPReady then FDSPEngine.SetNRMode(BtnNR.Tag);
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnNBClick(Sender: TObject);
|
||||
begin
|
||||
// Используем BtnNB напрямую — корректно работает и при вызове из веб-синхронизации (Sender=nil)
|
||||
BtnNB.Tag := 1 - BtnNB.Tag;
|
||||
StyleButton(BtnNB, BtnNB.Tag = 1);
|
||||
if FWDSPReady then FDSPEngine.SetNB(BtnNB.Tag = 1);
|
||||
BtnNB.Tag := (BtnNB.Tag + 1) mod 3;
|
||||
UpdateNBButton;
|
||||
if FWDSPReady then FDSPEngine.SetNBMode(BtnNB.Tag);
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnSNBClick(Sender: TObject);
|
||||
begin
|
||||
BtnSNB.Tag := 1 - BtnSNB.Tag;
|
||||
UpdateSNBButton;
|
||||
if FWDSPReady then FDSPEngine.SetSNB(BtnSNB.Tag <> 0);
|
||||
end;
|
||||
|
||||
procedure TMainForm.BtnANFClick(Sender: TObject);
|
||||
begin
|
||||
// Используем BtnANF напрямую — корректно работает и при вызове из веб-синхронизации (Sender=nil)
|
||||
BtnANF.Tag := 1 - BtnANF.Tag;
|
||||
StyleButton(BtnANF, BtnANF.Tag = 1);
|
||||
if FWDSPReady then FDSPEngine.SetANF(BtnANF.Tag = 1);
|
||||
UpdateANFButton;
|
||||
if FWDSPReady then FDSPEngine.SetANF(BtnANF.Tag <> 0);
|
||||
end;
|
||||
|
||||
|
||||
@@ -3766,6 +3851,7 @@ begin
|
||||
FDSPEngine.SetMode(FMode);
|
||||
FDSPEngine.SetVolume(FVolume / 100.0);
|
||||
ApplyModeFilter;
|
||||
ApplyNoiseFilterButtonsToDSP;
|
||||
// ChangeSampleRate пересоздаёт WDSP-канал — восстанавливаем AGC
|
||||
FDSPEngine.SetAGCTop(FAGCTop);
|
||||
FDSPEngine.SetAGC(TWDSPAGCMode(FAGCMode), 50.0);
|
||||
@@ -4046,32 +4132,57 @@ end;
|
||||
|
||||
// ── NR ────────────────────────────────────────────────────────────────────
|
||||
|
||||
procedure TMainForm.WebOnNR(On_: Boolean);
|
||||
procedure TMainForm.WebOnNR(Mode: Integer);
|
||||
begin
|
||||
FWebSyncBool := On_;
|
||||
FWebSyncInt := Mode;
|
||||
FWebSyncM := SyncWebNR;
|
||||
TThread.Synchronize(nil, FWebSyncM);
|
||||
end;
|
||||
|
||||
procedure TMainForm.SyncWebNR;
|
||||
begin
|
||||
if FWebSyncBool <> (BtnNR.Tag = 1) then
|
||||
BtnNRClick(BtnNR);
|
||||
if FWebSyncInt < 0 then FWebSyncInt := 0;
|
||||
if FWebSyncInt > 4 then FWebSyncInt := 4;
|
||||
if BtnNR.Tag <> FWebSyncInt then
|
||||
begin
|
||||
BtnNR.Tag := FWebSyncInt;
|
||||
UpdateNRButton;
|
||||
if FWDSPReady then FDSPEngine.SetNRMode(BtnNR.Tag);
|
||||
end;
|
||||
end;
|
||||
|
||||
// ── NB ────────────────────────────────────────────────────────────────────
|
||||
|
||||
procedure TMainForm.WebOnNB(On_: Boolean);
|
||||
procedure TMainForm.WebOnNB(Mode: Integer);
|
||||
begin
|
||||
FWebSyncBool := On_;
|
||||
FWebSyncInt := Mode;
|
||||
FWebSyncM := SyncWebNB;
|
||||
TThread.Synchronize(nil, FWebSyncM);
|
||||
end;
|
||||
|
||||
procedure TMainForm.SyncWebNB;
|
||||
begin
|
||||
if FWebSyncBool <> (BtnNB.Tag = 1) then
|
||||
BtnNBClick(BtnNB);
|
||||
if FWebSyncInt < 0 then FWebSyncInt := 0;
|
||||
if FWebSyncInt > 2 then FWebSyncInt := 2;
|
||||
if BtnNB.Tag <> FWebSyncInt then
|
||||
begin
|
||||
BtnNB.Tag := FWebSyncInt;
|
||||
UpdateNBButton;
|
||||
if FWDSPReady then FDSPEngine.SetNBMode(BtnNB.Tag);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.WebOnSNB(On_: Boolean);
|
||||
begin
|
||||
FWebSyncBool := On_;
|
||||
FWebSyncM := SyncWebSNB;
|
||||
TThread.Synchronize(nil, FWebSyncM);
|
||||
end;
|
||||
|
||||
procedure TMainForm.SyncWebSNB;
|
||||
begin
|
||||
if FWebSyncBool <> (BtnSNB.Tag <> 0) then
|
||||
BtnSNBClick(BtnSNB);
|
||||
end;
|
||||
|
||||
// ── ANF ───────────────────────────────────────────────────────────────────
|
||||
@@ -4085,7 +4196,7 @@ end;
|
||||
|
||||
procedure TMainForm.SyncWebANF;
|
||||
begin
|
||||
if FWebSyncBool <> (BtnANF.Tag = 1) then
|
||||
if FWebSyncBool <> (BtnANF.Tag <> 0) then
|
||||
BtnANFClick(BtnANF);
|
||||
end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user