feature(rx): плавный аттенюатор 0..31 дБ в UI, память на диапазон, компенсация уровней

Аттенюатор был только в web и только тремя ступенями (0/-10/-20), а его дБ
никуда не возвращались — при ATT 20 дБ S-метр и спектр уезжали на 20 дБ, и
свежая калибровка уровней была верна лишь при ATT=0.

- RadioController: SetAtten(Db) 0..31 (как умеет железо) вместо индекса *10.
  AttenCalDB входит в SMeterCalFor и DispCalFor — стрелка, спектр, водопад и
  паны компенсируются, шкала dBm остаётся абсолютной (как Thetis/piHPSDR).
  У AD936x аттенюатора нет, там поправка 0 (усиление тракта — hardwaregain).
- Память на диапазон/слот трансвертера по образцу RxGainMode/RxGainDb:
  TBandSettings.AttenDB (band 'atten_db') и TXvtrEntry.LastAttenDB
  ('last_atten_db'); восстановление и отправка в железо — в ApplyBandDSP,
  сбор — в MakeBandSettings/SaveCurrentBand, вход в слот — через B.AttenDB.
  SetAtten сразу кладёт значение в кэш диапазона/слота (JSON пишется общим
  сохранением, а не на каждый шаг ползунка — как у drive).
- MainForm: ряд «ATT [ползунок] NNdB» в RX-блоке левой панели, отступы как у
  VOL; на Pluto ряд скрыт и RX-блок ужимается (LayoutLeftPanel двигает
  панели ниже). Внешние изменения приходят по rfAtten.
- Web: select → ползунок 0..31 с цифрой, attn_idx -> attn_db, cmd attn {db:}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 11:41:21 +03:00
co-authored by Claude Opus 4.8
parent 366d4c9821
commit 6de8c00689
5 changed files with 127 additions and 22 deletions
+68 -2
View File
@@ -347,6 +347,11 @@ type
FPSPopup: TObject; // TPureSignalPopup (cast при использовании)
PanelRXBlock: TPanel;
// Шаговый аттенюатор ADC (openHPSDR): плавные 0..31 дБ. На Pluto ряд
// скрыт — там усиление тракта живёт в блоке RF AGC.
LblAtt: TLabel;
LblAttVal: TLabel;
TrkAtt: TFlatSlider;
BtnChannels: TFlatButton;
FChannelsDropDown: TFlatDropDown;
FChannelsForm: TObject; // TChannelsForm (cast при использовании)
@@ -706,6 +711,8 @@ type
procedure BtnAGCModeClick(Sender: TObject);
procedure TrkAGCChange(Sender: TObject);
procedure TrkRxGainChange(Sender: TObject);
procedure TrkAttChange(Sender: TObject);
procedure UpdateAttLabel;
// (BtnPlutoGainClick объявлен выше)
procedure MeterTimerTick(Sender: TObject);
procedure SpectrumTimerTick(Sender: TObject);
@@ -762,6 +769,13 @@ uses SettingsForm;
{$R *.lfm}
const
// Высота RX-блока левой панели: с рядом ATT (openHPSDR) и без него (Pluto —
// там усиление тракта в блоке RF AGC). Переключает LayoutLeftPanel, он же
// двигает панели ниже.
RX_BLOCK_H = 96;
RX_BLOCK_H_ATT = 120;
function TMainForm.LeftPanelButtonWidth(PanelWidth, ColCount,
ColIndex: Integer): Integer;
var
@@ -1505,6 +1519,12 @@ begin
TrkVolume.Position := FController.ActiveVolume; // RX или TX-монитор
rfRxGain:
UpdateRxGainUI;
rfAtten:
if TrkAtt <> nil then
begin
TrkAtt.Position := FController.FAtten;
UpdateAttLabel;
end;
rfDrive:
begin
TrkDrive.Position := FController.FDrivePercent; // слайдер ← drive%
@@ -2028,7 +2048,7 @@ begin
// RX block: CTUN + VOL + NR/NB/SNB/ANF/MUTE
PanelRXBlock := TPanel.Create(Self);
PanelRXBlock.Parent := PanelLeft;
PanelRXBlock.SetBounds(0, Y, LEFT_W, 96);
PanelRXBlock.SetBounds(0, Y, LEFT_W, RX_BLOCK_H_ATT);
PanelRXBlock.BevelOuter := bvNone;
MakeLbl(PanelRXBlock, 'RX', 4, 2);
@@ -2084,7 +2104,29 @@ begin
LeftPanelButtonLeft(LEFT_W, 5, 4), 70,
LeftPanelButtonWidth(LEFT_W, 5, 4), BTN_SM, BtnMuteClick);
Inc(Y, 100);
// Шаговый аттенюатор ADC — плавный 0..31 дБ (железо openHPSDR так и умеет,
// ступени 0/10/20 были только в web-UI). Показания S-метра/спектра на эти
// дБ компенсируются в контроллере, шкала dBm остаётся абсолютной.
LblAtt := TLabel.Create(Self);
LblAtt.Parent := PanelRXBlock; LblAtt.Left := 4; LblAtt.Top := 106;
LblAtt.Caption := 'ATT'; LblAtt.Font.Color := CLR_TEXTDIM;
LblAtt.Font.Name := 'Courier New'; LblAtt.Font.Size := 7;
TrkAtt := TFlatSlider.Create(Self);
TrkAtt.Parent := PanelRXBlock; TrkAtt.Left := 34; TrkAtt.Top := 102;
TrkAtt.Width := LEFT_W - 76; TrkAtt.Height := 16;
TrkAtt.Min := 0; TrkAtt.Max := 31; // ADC step attenuator, дБ
TrkAtt.Position := FController.FAtten;
TrkAtt.OnChange := TrkAttChange;
LblAttVal := TLabel.Create(Self);
LblAttVal.Parent := PanelRXBlock;
LblAttVal.Left := LEFT_W - 38; LblAttVal.Top := 106;
LblAttVal.Font.Color := CLR_TEXT; LblAttVal.Font.Name := 'Courier New';
LblAttVal.Font.Size := 7;
UpdateAttLabel;
Inc(Y, RX_BLOCK_H_ATT + 4);
// AGC controls
PanelAGC := TPanel.Create(Self);
@@ -7933,6 +7975,19 @@ begin
FController.SetRxGain(TrkRxGain.Position); // переводит в manual
end;
procedure TMainForm.TrkAttChange(Sender: TObject);
begin
if FSyncingFromController then Exit;
FController.SetAtten(TrkAtt.Position);
UpdateAttLabel;
end;
procedure TMainForm.UpdateAttLabel;
begin
if LblAttVal <> nil then
LblAttVal.Caption := Format('%ddB', [FController.FAtten]);
end;
procedure TMainForm.BtnPlutoGainClick(Sender: TObject);
begin
if FSyncingFromController then Exit;
@@ -7966,6 +8021,17 @@ procedure TMainForm.LayoutLeftPanel(Pluto: Boolean);
var baseTop: Integer;
begin
if (PanelPlutoAGC = nil) or (PanelAGC = nil) or (PanelBands = nil) then Exit;
// Шаговый аттенюатор — только у openHPSDR; у AD936x усиление тракта в блоке
// RF AGC ниже, поэтому ряд ATT убираем и RX-блок ужимаем.
if (TrkAtt <> nil) and (LblAtt <> nil) and (LblAttVal <> nil) then
begin
TrkAtt.Visible := not Pluto;
LblAtt.Visible := not Pluto;
LblAttVal.Visible := not Pluto;
if Pluto then PanelRXBlock.Height := RX_BLOCK_H
else PanelRXBlock.Height := RX_BLOCK_H_ATT;
PanelAGC.Top := PanelRXBlock.Top + PanelRXBlock.Height + 4;
end;
baseTop := PanelAGC.Top + PanelAGC.Height + 4;
PanelPlutoAGC.Top := baseTop;
PanelPlutoAGC.Visible := Pluto;