mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(puresignal): PureSignal-предыскажение для openHPSDR P2 + кнопки PS/2TON
Протокол (по pihpsdr new_protocol.c): на PTT при PS DDC0<-ADC0 (RX-feedback), DDC1<-ADC2 (TX DAC loopback), 192к/24бит, sync DDC1->DDC0 (byte 1363=0x02, interleaved в потоке DDC0), частоты DDC0/1 = TX в HP-пакете, Alex0 bit18 (мост обратной связи). Caps.HasPureSignal для плат с DDCBase=2 (Angelia/Orion/Orion2/Saturn); попутно NumADCs=2 для Angelia. WDSPEngine: PushPSFeedback (деинтерлив -> pscc(TXA, tx=DDC1, rx=DDC0) из сетевого потока), PS-параметры calcc в кэше с переприменением после пересоздания TXA; SetTXTwoTone (PostGen mode 1, 700/1900, 0.49+0.49, инверсия LSB/CWL/DIGL, мьют mic как TUN). RadioController: state machine команд (порт Thetis PSForm timer1) в PureSignalTick @10Гц + auto-attenuate (AttOnTX по уровню feedback, цель ~152, через DUC Specific); SetPSMox и перестройка feedback-DDC на T/R; маршрутизация DDC0 в pscc из OnDDCIQ; SetTwoTone с гейтами safety. UI: кнопки PS (ЛКМ = поповер настроек, ПКМ = быстрый toggle) и 2TON в блоке TX; поповер TPureSignalPopup (дочерний контрол, Wayland-safe): PS ON/OFF, CAL, RESET, 2TON, лампа/уровень feedback, cals, corr, GetPk/SetPk, MOX/CAL wait, PH#, Auto-atten, Relax ptol, Pin/Map/Stbl, Tint, тона двухтональника. Персист ps_* в tx-секции per-device. Не проверено на железе; Save/Restore коррекции не реализованы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
unit PureSignalPopup;
|
||||
|
||||
{
|
||||
TPureSignalPopup — поповер управления и настроек PureSignal, открывается
|
||||
ПКМ по кнопке PS на левой панели. Управление: PS (авто-калибровка вкл/выкл),
|
||||
CAL (одиночная калибровка), RESET (сброс коррекции), 2TON. Статус: лампа и
|
||||
уровень feedback (оптимум ~152), счётчик калибровок, лампа коррекции,
|
||||
GetPk/SetPk. Параметры calcc (MOX/CAL wait, PH#, Relax ptol, Auto-atten,
|
||||
Pin/Map/Stbl, Ints, тона 2TON) пишутся в FTXSettings.PS* живьём:
|
||||
ApplyPSSettingsToDSP + OnChanged (персист в MainForm).
|
||||
|
||||
Реализован как ДОЧЕРНИЙ контрол верхнеуровневой формы (не отдельное окно) —
|
||||
как TPanDisplayPopup: Wayland не даёт клиенту позиционировать top-level окна.
|
||||
}
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE Delphi}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Math, Types, Controls, ExtCtrls, StdCtrls, Graphics, Forms,
|
||||
RadioController, Settings,
|
||||
FlatButton, FlatComboBox, FlatCheckBox, FlatSpinEdit, FlatFloatSpinEdit;
|
||||
|
||||
type
|
||||
TPureSignalPopup = class(TCustomControl)
|
||||
private
|
||||
FController: TRadioController; // не владеем
|
||||
FLoading: Boolean; // подавляет Apply при программной заливке
|
||||
FBg: TPanel;
|
||||
FTitle: TLabel;
|
||||
FBtnClose: TFlatButton;
|
||||
// Управление
|
||||
FBtnPS: TFlatButton;
|
||||
FBtnCal: TFlatButton;
|
||||
FBtnReset: TFlatButton;
|
||||
FBtn2Ton: TFlatButton;
|
||||
// Статус
|
||||
FFbLamp: TPanel;
|
||||
FLbFbVal: TLabel;
|
||||
FLbCals: TLabel;
|
||||
FCorrLamp: TPanel;
|
||||
FLbGetPk: TLabel;
|
||||
FLbAtt: TLabel;
|
||||
// Параметры
|
||||
FSePk: TFlatFloatSpinEdit;
|
||||
FBtnDefPk: TFlatButton;
|
||||
FSeMoxWait: TFlatFloatSpinEdit;
|
||||
FSeCalWait: TFlatFloatSpinEdit;
|
||||
FSePhNum: TFlatSpinEdit;
|
||||
FChkRelax: TFlatCheckBox;
|
||||
FChkAutoAtt:TFlatCheckBox;
|
||||
FChkPin: TFlatCheckBox;
|
||||
FChkMap: TFlatCheckBox;
|
||||
FChkStbl: TFlatCheckBox;
|
||||
FCbInts: TFlatComboBox;
|
||||
FSeTT1: TFlatSpinEdit;
|
||||
FSeTT2: TFlatSpinEdit;
|
||||
FOnChanged: TNotifyEvent;
|
||||
|
||||
function MakeLbl(const Cap: string; ALeft, ATop, AW: Integer): TLabel;
|
||||
procedure Apply(Sender: TObject); // контролы → FTXSettings.PS* → WDSP
|
||||
procedure PSClick(Sender: TObject);
|
||||
procedure CalClick(Sender: TObject);
|
||||
procedure ResetClick(Sender: TObject);
|
||||
procedure TwoToneClick(Sender: TObject);
|
||||
procedure DefPkClick(Sender: TObject);
|
||||
procedure CloseClick(Sender: TObject);
|
||||
protected
|
||||
procedure Paint; override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
procedure LoadFrom(C: TRadioController);
|
||||
// Обновление статусных ламп/значений (звать с UI-таймера пока Visible
|
||||
// и по rfPureSignal/rfTwoTone).
|
||||
procedure RefreshStatus;
|
||||
// Показать под контролом-якорем (кнопкой PS) как дочерний контрол формы.
|
||||
procedure PopupBelow(C: TRadioController; Anchor: TControl);
|
||||
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
{$IFDEF WINDOWS}
|
||||
UI_FONT = 'Segoe UI';
|
||||
{$ELSE}
|
||||
UI_FONT = 'Sans';
|
||||
{$ENDIF}
|
||||
CLR_BG = TColor($00303030); // рамка (1px кайма формы)
|
||||
CLR_PANEL = TColor($001A1A1A);
|
||||
CLR_TEXT = TColor($00E0E0E0);
|
||||
CLR_TEXTDIM = TColor($00888888);
|
||||
CLR_ACCENT = TColor($0040FF80);
|
||||
CLR_INPUT = TColor($00FFFFFF);
|
||||
CLR_INPUT_TEXT = TColor($00202020);
|
||||
|
||||
// Лампа feedback (пороги Thetis: >181 горячо, 129..181 оптимум, 91..128
|
||||
// слабовато, <=90 нет сигнала). TColor = BGR.
|
||||
CLR_FB_HOT = TColor($00FF9030); // голубой: слишком горячо (атт. мал)
|
||||
CLR_FB_OK = TColor($0000FF00); // зелёный: оптимум
|
||||
CLR_FB_LOW = TColor($0000FFFF); // жёлтый: слабовато
|
||||
CLR_FB_NONE = TColor($000000C0); // красный: нет feedback
|
||||
CLR_LAMP_OFF= TColor($00101010);
|
||||
|
||||
POP_W = 272;
|
||||
PAD = 10;
|
||||
LBLW = 92;
|
||||
CTLX = 108;
|
||||
ROWH = 24;
|
||||
|
||||
constructor TPureSignalPopup.Create(AOwner: TComponent);
|
||||
var
|
||||
Y: Integer;
|
||||
|
||||
function NextRow: Integer;
|
||||
begin
|
||||
Result := Y;
|
||||
Inc(Y, ROWH + 4);
|
||||
end;
|
||||
|
||||
function MakeIntSpin(ATop, AMin, AMax, AInc: Integer): TFlatSpinEdit;
|
||||
begin
|
||||
Result := TFlatSpinEdit.Create(Self);
|
||||
Result.Parent := FBg;
|
||||
Result.SetBounds(CTLX, ATop, POP_W - CTLX - PAD, ROWH - 2);
|
||||
Result.MinValue := AMin;
|
||||
Result.MaxValue := AMax;
|
||||
Result.Increment := AInc;
|
||||
Result.Color := CLR_INPUT;
|
||||
Result.Font.Color := CLR_INPUT_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := Apply;
|
||||
end;
|
||||
|
||||
function MakeFloatSpin(ATop: Integer; AMin, AMax, AInc: Double;
|
||||
ADec: Integer): TFlatFloatSpinEdit;
|
||||
begin
|
||||
Result := TFlatFloatSpinEdit.Create(Self);
|
||||
Result.Parent := FBg;
|
||||
Result.SetBounds(CTLX, ATop, POP_W - CTLX - PAD, ROWH - 2);
|
||||
Result.MinValue := AMin;
|
||||
Result.MaxValue := AMax;
|
||||
Result.Increment := AInc;
|
||||
Result.DecimalPlaces := ADec;
|
||||
Result.Color := CLR_INPUT;
|
||||
Result.Font.Color := CLR_INPUT_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := Apply;
|
||||
end;
|
||||
|
||||
function MakeChk(const Cap: string; ALeft, ATop, AW: Integer): TFlatCheckBox;
|
||||
begin
|
||||
Result := TFlatCheckBox.Create(Self);
|
||||
Result.Parent := FBg;
|
||||
Result.SetBounds(ALeft, ATop, AW, ROWH - 4);
|
||||
Result.Caption := Cap;
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
Result.OnChange := Apply;
|
||||
end;
|
||||
|
||||
function MakeLamp(ALeft, ATop: Integer): TPanel;
|
||||
begin
|
||||
Result := TPanel.Create(Self);
|
||||
Result.Parent := FBg;
|
||||
Result.SetBounds(ALeft, ATop, 16, 16);
|
||||
Result.BevelOuter := bvNone;
|
||||
Result.Color := CLR_LAMP_OFF;
|
||||
Result.Caption := '';
|
||||
end;
|
||||
|
||||
var
|
||||
BW: Integer;
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
ControlStyle := ControlStyle + [csOpaque];
|
||||
Visible := False;
|
||||
Color := CLR_BG;
|
||||
Font.Name := UI_FONT;
|
||||
Font.Size := 9;
|
||||
Font.Color := CLR_TEXT;
|
||||
FLoading := False;
|
||||
|
||||
FBg := TPanel.Create(Self);
|
||||
FBg.Parent := Self;
|
||||
FBg.BevelOuter := bvNone;
|
||||
FBg.Color := CLR_PANEL;
|
||||
FBg.SetBounds(1, 1, POP_W - 2, 10);
|
||||
|
||||
Y := PAD;
|
||||
|
||||
FTitle := MakeLbl('PureSignal', PAD, NextRow + 2, POP_W - 2 * PAD - 18);
|
||||
FTitle.Font.Color := CLR_ACCENT;
|
||||
FTitle.Font.Style := [fsBold];
|
||||
FBtnClose := MakeFlatBtn(FBg, '×', POP_W - PAD - 16, PAD, 16, 16, CloseClick);
|
||||
FBtnClose.ClrText := CLR_TEXTDIM;
|
||||
FBtnClose.ClrTextAct := CLR_TEXT;
|
||||
|
||||
// ---- Управление: PS / CAL / RESET / 2TON ----
|
||||
BW := (POP_W - 2 * PAD - 2 * 6) div 3;
|
||||
FBtnPS := MakeFlatBtn(FBg, 'PS ON', PAD, Y, BW, ROWH, PSClick);
|
||||
FBtnCal := MakeFlatBtn(FBg, 'CAL', PAD + BW + 6, Y, BW, ROWH, CalClick);
|
||||
FBtnReset := MakeFlatBtn(FBg, 'RESET', PAD + 2 * (BW + 6), Y, BW, ROWH, ResetClick);
|
||||
NextRow;
|
||||
FBtn2Ton := MakeFlatBtn(FBg, '2TON', PAD, Y, BW, ROWH, TwoToneClick);
|
||||
// ---- Статус: лампа + FB + cals + corr в одной строке с 2TON ----
|
||||
FFbLamp := MakeLamp(PAD + BW + 6, Y + 4);
|
||||
FLbFbVal := MakeLbl('FB: —', PAD + BW + 28, Y + 4, 68);
|
||||
FCorrLamp := MakeLamp(POP_W - PAD - 62, Y + 4);
|
||||
MakeLbl('corr', POP_W - PAD - 42, Y + 4, 36).Font.Color := CLR_TEXTDIM;
|
||||
NextRow;
|
||||
FLbCals := MakeLbl('cals: 0', PAD, Y + 2, 90);
|
||||
FLbCals.Font.Color := CLR_TEXTDIM;
|
||||
FLbAtt := MakeLbl('TX ATT: — dB', PAD + 96, Y + 2, POP_W - 2 * PAD - 96);
|
||||
FLbAtt.Font.Color := CLR_TEXTDIM;
|
||||
Inc(Y, 20);
|
||||
|
||||
MakeLbl('— Calibration —', PAD, Y + 3, POP_W - 2 * PAD).Font.Color := CLR_TEXTDIM;
|
||||
NextRow;
|
||||
|
||||
// ---- GetPk / SetPk ----
|
||||
FLbGetPk := MakeLbl('GetPk: —', PAD, Y + 3, LBLW);
|
||||
FSePk := MakeFloatSpin(Y, 0.05, 1.00, 0.01, 4);
|
||||
FSePk.Width := FSePk.Width - 42;
|
||||
FBtnDefPk := MakeFlatBtn(FBg, 'DEF', POP_W - PAD - 38, Y, 38, ROWH - 2, DefPkClick);
|
||||
NextRow;
|
||||
|
||||
MakeLbl('MOX wait, s', PAD, Y + 3, LBLW);
|
||||
FSeMoxWait := MakeFloatSpin(Y, 0.0, 0.9, 0.1, 1);
|
||||
NextRow;
|
||||
MakeLbl('CAL wait, s', PAD, Y + 3, LBLW);
|
||||
FSeCalWait := MakeFloatSpin(Y, 0.0, 10.0, 0.5, 1);
|
||||
NextRow;
|
||||
MakeLbl('PH# delay, ns', PAD, Y + 3, LBLW);
|
||||
FSePhNum := MakeIntSpin(Y, 0, 1000, 10);
|
||||
NextRow;
|
||||
|
||||
FChkAutoAtt := MakeChk('Auto-attenuate', PAD, Y + 2, 130);
|
||||
FChkRelax := MakeChk('Relax tolerance', PAD + 134, Y + 2, POP_W - 2 * PAD - 134);
|
||||
NextRow;
|
||||
FChkPin := MakeChk('Pin', PAD, Y + 2, 56);
|
||||
FChkMap := MakeChk('Map', PAD + 62, Y + 2, 56);
|
||||
FChkStbl := MakeChk('Stbl', PAD + 124, Y + 2, 60);
|
||||
NextRow;
|
||||
|
||||
MakeLbl('Tint (ints/spi)', PAD, Y + 3, LBLW);
|
||||
FCbInts := TFlatComboBox.Create(Self);
|
||||
FCbInts.Parent := FBg;
|
||||
FCbInts.SetBounds(CTLX, Y, POP_W - CTLX - PAD, ROWH - 2);
|
||||
FCbInts.Items.Add('16 / 256');
|
||||
FCbInts.Items.Add('8 / 512');
|
||||
FCbInts.Items.Add('4 / 1024');
|
||||
FCbInts.Color := CLR_INPUT;
|
||||
FCbInts.Font.Color := CLR_INPUT_TEXT;
|
||||
FCbInts.Font.Name := UI_FONT;
|
||||
FCbInts.Font.Size := 9;
|
||||
FCbInts.OnChange := Apply;
|
||||
NextRow;
|
||||
|
||||
MakeLbl('— Two-tone, Hz —', PAD, Y + 3, POP_W - 2 * PAD).Font.Color := CLR_TEXTDIM;
|
||||
NextRow;
|
||||
MakeLbl('F1 / F2', PAD, Y + 3, LBLW);
|
||||
FSeTT1 := MakeIntSpin(Y, 100, 5000, 100);
|
||||
FSeTT1.Width := (POP_W - CTLX - PAD - 6) div 2;
|
||||
FSeTT2 := MakeIntSpin(Y, 100, 5000, 100);
|
||||
FSeTT2.SetBounds(CTLX + FSeTT1.Width + 6, Y, (POP_W - CTLX - PAD - 6) div 2, ROWH - 2);
|
||||
NextRow;
|
||||
|
||||
FBg.Height := Y - 1 + PAD;
|
||||
Width := POP_W;
|
||||
Height := Y + 1 + PAD;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.Paint;
|
||||
begin
|
||||
Canvas.Brush.Style := bsSolid;
|
||||
Canvas.Brush.Color := CLR_BG;
|
||||
Canvas.Pen.Style := psClear;
|
||||
Canvas.FillRect(0, 0, Width, Height);
|
||||
end;
|
||||
|
||||
function TPureSignalPopup.MakeLbl(const Cap: string; ALeft, ATop, AW: Integer): TLabel;
|
||||
begin
|
||||
Result := TLabel.Create(Self);
|
||||
Result.Parent := FBg;
|
||||
Result.Caption := Cap;
|
||||
Result.Left := ALeft;
|
||||
Result.Top := ATop;
|
||||
if AW > 0 then Result.Width := AW;
|
||||
Result.Font.Color := CLR_TEXT;
|
||||
Result.Font.Name := UI_FONT;
|
||||
Result.Font.Size := 9;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.LoadFrom(C: TRadioController);
|
||||
begin
|
||||
FController := C;
|
||||
if C = nil then Exit;
|
||||
FLoading := True;
|
||||
try
|
||||
FSePk.Value := EnsureRange(C.FTXSettings.PSHWPeak, 0.05, 1.00);
|
||||
FSeMoxWait.Value := EnsureRange(C.FTXSettings.PSMoxDelay, 0.0, 0.9);
|
||||
FSeCalWait.Value := EnsureRange(C.FTXSettings.PSLoopDelay, 0.0, 10.0);
|
||||
FSePhNum.Value := EnsureRange(C.FTXSettings.PSTXDelayNs, 0, 1000);
|
||||
FChkRelax.Checked := C.FTXSettings.PSRelaxPtol;
|
||||
FChkAutoAtt.Checked := C.FTXSettings.PSAutoAtten;
|
||||
FChkPin.Checked := C.FTXSettings.PSPin;
|
||||
FChkMap.Checked := C.FTXSettings.PSMap;
|
||||
FChkStbl.Checked := C.FTXSettings.PSStabilize;
|
||||
FCbInts.ItemIndex := EnsureRange(C.FTXSettings.PSIntsIdx, 0, 2);
|
||||
FSeTT1.Value := EnsureRange(C.FTXSettings.PSTTFreq1, 100, 5000);
|
||||
FSeTT2.Value := EnsureRange(C.FTXSettings.PSTTFreq2, 100, 5000);
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.Apply(Sender: TObject);
|
||||
begin
|
||||
if FLoading or (FController = nil) then Exit;
|
||||
FController.FTXSettings.PSHWPeak := FSePk.Value;
|
||||
FController.FTXSettings.PSMoxDelay := FSeMoxWait.Value;
|
||||
FController.FTXSettings.PSLoopDelay := FSeCalWait.Value;
|
||||
FController.FTXSettings.PSTXDelayNs := FSePhNum.Value;
|
||||
FController.FTXSettings.PSRelaxPtol := FChkRelax.Checked;
|
||||
FController.FTXSettings.PSAutoAtten := FChkAutoAtt.Checked;
|
||||
FController.FTXSettings.PSPin := FChkPin.Checked;
|
||||
FController.FTXSettings.PSMap := FChkMap.Checked;
|
||||
FController.FTXSettings.PSStabilize := FChkStbl.Checked;
|
||||
FController.FTXSettings.PSIntsIdx := EnsureRange(FCbInts.ItemIndex, 0, 2);
|
||||
FController.FTXSettings.PSTTFreq1 := FSeTT1.Value;
|
||||
FController.FTXSettings.PSTTFreq2 := FSeTT2.Value;
|
||||
FController.ApplyPSSettingsToDSP;
|
||||
if Assigned(FOnChanged) then FOnChanged(Self);
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.PSClick(Sender: TObject);
|
||||
begin
|
||||
if FController = nil then Exit;
|
||||
FController.SetPureSignal(not FController.FPSEnabled);
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.CalClick(Sender: TObject);
|
||||
begin
|
||||
if FController = nil then Exit;
|
||||
FController.PSSingleCal;
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.ResetClick(Sender: TObject);
|
||||
begin
|
||||
if FController = nil then Exit;
|
||||
FController.PSReset;
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.TwoToneClick(Sender: TObject);
|
||||
begin
|
||||
if FController = nil then Exit;
|
||||
FController.SetTwoTone(not FController.FTwoTone);
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.DefPkClick(Sender: TObject);
|
||||
// Дефолт SetPk для протокола 2 (Thetis clsHardwareSpecific.PSDefaultPeak).
|
||||
begin
|
||||
FSePk.Value := 0.2899; // OnChange → Apply
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.CloseClick(Sender: TObject);
|
||||
begin
|
||||
Hide;
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.RefreshStatus;
|
||||
var
|
||||
FB: Integer;
|
||||
LampClr: TColor;
|
||||
begin
|
||||
if (FController = nil) or not Visible then Exit;
|
||||
|
||||
FBtnPS.Active := FController.FPSEnabled;
|
||||
if FController.FPSEnabled then FBtnPS.Caption := 'PS ON'
|
||||
else FBtnPS.Caption := 'PS OFF';
|
||||
FBtnCal.Active := FController.PSSingleCalActive;
|
||||
FBtn2Ton.Active := FController.FTwoTone;
|
||||
|
||||
FB := FController.PSFeedbackLevel;
|
||||
if FController.FPSFeedbackOn then
|
||||
begin
|
||||
if FB > 181 then LampClr := CLR_FB_HOT
|
||||
else if FB > 128 then LampClr := CLR_FB_OK
|
||||
else if FB > 90 then LampClr := CLR_FB_LOW
|
||||
else LampClr := CLR_FB_NONE;
|
||||
end
|
||||
else
|
||||
LampClr := CLR_LAMP_OFF;
|
||||
if FFbLamp.Color <> LampClr then FFbLamp.Color := LampClr;
|
||||
FLbFbVal.Caption := Format('FB: %d', [FB]);
|
||||
|
||||
if FController.PSCorrecting then
|
||||
begin
|
||||
if FCorrLamp.Color <> CLR_FB_OK then FCorrLamp.Color := CLR_FB_OK;
|
||||
end
|
||||
else if FCorrLamp.Color <> CLR_LAMP_OFF then
|
||||
FCorrLamp.Color := CLR_LAMP_OFF;
|
||||
|
||||
FLbCals.Caption := Format('cals: %d', [FController.PSCalCount]);
|
||||
FLbAtt.Caption := Format('TX ATT: %d dB', [FController.FTXSettings.AttOnTX]);
|
||||
FLbGetPk.Caption := Format('GetPk: %.3f', [FController.PSMaxTXVal]);
|
||||
end;
|
||||
|
||||
procedure TPureSignalPopup.PopupBelow(C: TRadioController; Anchor: TControl);
|
||||
var
|
||||
Root: TCustomForm;
|
||||
L, T: Integer;
|
||||
Cl: TPoint;
|
||||
begin
|
||||
if Anchor = nil then Exit;
|
||||
Root := GetParentForm(Anchor);
|
||||
if Root = nil then Exit;
|
||||
LoadFrom(C);
|
||||
// Позиция под якорем в КЛИЕНТСКИХ координатах формы (не top-level окно!).
|
||||
Cl := Root.ScreenToClient(Anchor.ClientToScreen(Point(0, Anchor.Height + 2)));
|
||||
L := Cl.X;
|
||||
T := Cl.Y;
|
||||
if L + Width > Root.ClientWidth then L := Root.ClientWidth - Width;
|
||||
if L < 0 then L := 0;
|
||||
if T + Height > Root.ClientHeight then
|
||||
T := Root.ScreenToClient(Anchor.ClientToScreen(Point(0, 0))).Y - Height; // вверх
|
||||
if T < 0 then T := 0;
|
||||
Parent := Root;
|
||||
SetBounds(L, T, Width, Height);
|
||||
Visible := True;
|
||||
BringToFront;
|
||||
RefreshStatus;
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user