mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
feature(calibration): офсет спектра/водопада на диапазон и на трансвертер
Вторая калибровочная таблица рядом с S-метром (паритет с Display Cal Offset в Thetis): у display-тракта своё окно/детектор, и его уровень расходится с показаниями стрелки на пару дБ, поэтому таблицы раздельные. - Settings: DispBandDB[12] / DispXvtrDB[8] (JSON cal_disp_band_i / cal_disp_xvtr_i, дефолт 0 — картинка не меняется до первой правки). - WDSPEngine: колбэк OnDispCal(PanId) — движок раз в кадр спрашивает офсет в UpdateSpectrum и прибавляет к пикселям (главный спектр/водопад в копирующем цикле, паны in-place при ненулевом офсете). На TX-дисплее не применяется. Так обошлись без хуков на каждое присваивание FCenterFreq. - RadioController: общий CalLookup(Hz, BandTbl, XvtrTbl) — на нём SMeterCalFor и DispCalFor(PanId): главный дисплей по FCenterFreq, пан по FPanFreqHz[p], т.е. кросс-банд паны получают свой офсет. Колбэк вешается в CreateEngines, поэтому работает и в GUI, и в ewsdrd; web-зеркало получает готовые кадры. - SettingsForm: группа «Spectrum / waterfall» с той же разбивкой (диапазоны + включённые трансвертеры активного железа) и кнопка «= S-meter» — копия таблицы стрелки как старт калибровки; общая раскладка сеток вынесена в LayoutCalOffsets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+169
-55
@@ -175,6 +175,14 @@ type
|
||||
FLblSMXvtr: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
||||
FLblSMBandHdr: TLabel;
|
||||
FLblSMXvtrHdr: TLabel;
|
||||
FCalGrpDisp: TPanel; // Spectrum / waterfall
|
||||
FEdDispBand: array[0..CFG_BAND_COUNT-1] of TFlatFloatSpinEdit; // офсет спектра на диапазон
|
||||
FLblDispBand: array[0..CFG_BAND_COUNT-1] of TLabel;
|
||||
FEdDispXvtr: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit; // офсет спектра на трансвертер
|
||||
FLblDispXvtr: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
||||
FLblDispBandHdr: TLabel;
|
||||
FLblDispXvtrHdr: TLabel;
|
||||
FBtnDispFromSM: TFlatButton; // «= S-meter» — копия таблицы
|
||||
FCalIsPluto: Boolean; // активное железо — AD936x
|
||||
FCalXvtrOn: array[0..CFG_XVTR_COUNT-1] of Boolean; // слот трансвертера включён
|
||||
FCalXvtrName: array[0..CFG_XVTR_COUNT-1] of string; // подпись слота
|
||||
@@ -370,6 +378,15 @@ type
|
||||
procedure BuildPATab;
|
||||
procedure BuildCalibrationTab;
|
||||
procedure RelayoutCalTab;
|
||||
// Раскладка блока офсетов «на диапазон + на трансвертер» внутри группы
|
||||
// (общая для S-метра и спектра). Возвращает нужную высоту группы.
|
||||
function LayoutCalOffsets(const BandLbl: array of TLabel;
|
||||
const BandEd: array of TFlatFloatSpinEdit;
|
||||
XvtrHdr: TLabel;
|
||||
const XvtrLbl: array of TLabel;
|
||||
const XvtrEd: array of TFlatFloatSpinEdit;
|
||||
Y0: Integer): Integer;
|
||||
procedure OnCalDispFromSMClick(Sender: TObject);
|
||||
procedure CollectCalFromUI;
|
||||
procedure OnCalAnyChange(Sender: TObject);
|
||||
procedure BuildCATTab;
|
||||
@@ -2066,6 +2083,53 @@ begin
|
||||
FEdSMXvtr[i].Tag := i;
|
||||
end;
|
||||
|
||||
// ── Spectrum / waterfall ────────────────────────────────────────────────
|
||||
// Отдельная от S-метра калибровка (Display Cal Offset в Thetis): у display-
|
||||
// тракта своё окно/детектор, поэтому уровень трассы и показания стрелки
|
||||
// расходятся на пару дБ. Сдвиг двигает ВСЮ трассу (и шумовую полку, и
|
||||
// сигналы) — это калибровка уровня, а не подавление шума.
|
||||
FCalGrpDisp := MakeGroupPanel(FPageCalib, 'Spectrum / waterfall',
|
||||
CAL_MARGIN, GTop, CAL_GRP_W, GRP3_H0);
|
||||
Grp := FCalGrpDisp;
|
||||
|
||||
FLblDispBandHdr := MakeLbl(Grp,
|
||||
'Offset (dB) added to spectrum/waterfall levels — per band (moves the whole '
|
||||
+ 'trace incl. noise floor).', CAL_PAD, 30, 650);
|
||||
|
||||
FBtnDispFromSM := TFlatButton.Create(Self);
|
||||
FBtnDispFromSM.Parent := Grp;
|
||||
FBtnDispFromSM.Caption := '= S-meter';
|
||||
FBtnDispFromSM.SetBounds(CAL_GRP_W - 130, 24, 112, BTN_H);
|
||||
FBtnDispFromSM.OnClick := OnCalDispFromSMClick;
|
||||
FBtnDispFromSM.ClrNorm := CLR_PANEL;
|
||||
FBtnDispFromSM.ClrBorder := TColor($00404040);
|
||||
FBtnDispFromSM.ClrHot := TColor($00303030);
|
||||
FBtnDispFromSM.ClrActive := TColor($00003300);
|
||||
FBtnDispFromSM.ClrText := CLR_TEXT;
|
||||
FBtnDispFromSM.ClrTextAct := CLR_ACCENT;
|
||||
FBtnDispFromSM.Font.Name := UI_FONT;
|
||||
FBtnDispFromSM.Font.Size := 9;
|
||||
FBtnDispFromSM.Hint := 'Copy the S-meter offsets into this table';
|
||||
FBtnDispFromSM.ShowHint := True;
|
||||
|
||||
for i := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
FLblDispBand[i] := MakeLbl(Grp, '', 0, 0, CAL_SM_LBL_W);
|
||||
FEdDispBand[i] := MakeCalSpin(Grp, 0, 0, -30.0, 30.0, 0.1, 1, 0.0);
|
||||
FEdDispBand[i].Tag := i;
|
||||
end;
|
||||
|
||||
FLblDispXvtrHdr := MakeLbl(Grp, 'Per transverter slot', CAL_PAD, 0, 300);
|
||||
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
FLblDispXvtr[i] := MakeLbl(Grp, '', 0, 0, CAL_SM_LBL_W);
|
||||
FLblDispXvtr[i].AutoSize := False;
|
||||
FLblDispXvtr[i].ShowHint := True;
|
||||
FEdDispXvtr[i] := MakeCalSpin(Grp, 0, 0, -30.0, 30.0, 0.1, 1, 0.0);
|
||||
FEdDispXvtr[i].Tag := i;
|
||||
end;
|
||||
|
||||
RelayoutCalTab; // стартовая раскладка (HF-план, трансвертеры скрыты)
|
||||
|
||||
FLoading := False;
|
||||
@@ -2077,23 +2141,21 @@ procedure TSettingsForm.RelayoutCalTab;
|
||||
// датчики питания — свойства Alex-моста openHPSDR, у AD936x такой телеметрии
|
||||
// нет, поэтому обе группы прячутся, а S-метр поднимается наверх.
|
||||
var
|
||||
i, r, c, X, Y, GTop, BandRows, XvtrRows, Slot, H: Integer;
|
||||
i, r, c, X, Y, GTop, HSM, HDisp: Integer;
|
||||
Nm: string;
|
||||
begin
|
||||
// Гард: вкладка ещё строится (или LoadXvtrSettings пришёл раньше неё).
|
||||
if (FCalGrpDet = nil) or (FCalGrpVA = nil) or (FCalGrpSM = nil)
|
||||
or (FLblSMXvtrHdr = nil) then Exit;
|
||||
or (FCalGrpDisp = nil) or (FLblDispXvtrHdr = nil) then Exit;
|
||||
|
||||
// ---- строки диапазонов: позиция по индексу плана, пустые слоты скрыты ----
|
||||
BandRows := 0;
|
||||
// ---- подписи/видимость строк Det. Cal. (своя сетка, уже с подсказкой) ----
|
||||
for i := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
Nm := BandName(i, FCalIsPluto);
|
||||
r := i div CAL_COLS;
|
||||
c := i mod CAL_COLS;
|
||||
|
||||
X := CAL_PAD + c * (CAL_LBL_W + CAL_ED_W + 18);
|
||||
Y := CAL_R1 + CAL_ROW_H + 46 + r * CAL_ROW_H;
|
||||
X := CAL_PAD + c * (CAL_LBL_W + CAL_ED_W + 18);
|
||||
Y := CAL_R1 + CAL_ROW_H + 46 + r * CAL_ROW_H;
|
||||
if FLblPADetCal[i] <> nil then
|
||||
begin
|
||||
FLblPADetCal[i].Caption := Nm + ':';
|
||||
@@ -2105,51 +2167,13 @@ begin
|
||||
FEdPADetCal[i].SetBounds(X + CAL_LBL_W, Y, CAL_ED_W, BTN_H + 2);
|
||||
FEdPADetCal[i].Visible := Nm <> '';
|
||||
end;
|
||||
|
||||
X := CAL_PAD + c * (CAL_SM_LBL_W + CAL_ED_W + 14);
|
||||
Y := 52 + r * CAL_ROW_H;
|
||||
if FLblSMBand[i] <> nil then
|
||||
begin
|
||||
FLblSMBand[i].Caption := Nm + ':';
|
||||
FLblSMBand[i].SetBounds(X, Y + 5, CAL_SM_LBL_W, 18);
|
||||
FLblSMBand[i].Visible := Nm <> '';
|
||||
end;
|
||||
if FEdSMBand[i] <> nil then
|
||||
begin
|
||||
FEdSMBand[i].SetBounds(X + CAL_SM_LBL_W, Y, CAL_ED_W, BTN_H + 2);
|
||||
FEdSMBand[i].Visible := Nm <> '';
|
||||
end;
|
||||
if Nm <> '' then BandRows := r + 1;
|
||||
end;
|
||||
|
||||
// ---- строки трансвертеров: только включённые слоты, без дырок ----
|
||||
Y := 52 + BandRows * CAL_ROW_H + 6;
|
||||
FLblSMXvtrHdr.SetBounds(CAL_PAD, Y, 300, 18);
|
||||
Slot := 0;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
if FCalXvtrOn[i] then
|
||||
begin
|
||||
r := Slot div CAL_COLS;
|
||||
c := Slot mod CAL_COLS;
|
||||
X := CAL_PAD + c * (CAL_SM_LBL_W + CAL_ED_W + 14);
|
||||
if FCalXvtrName[i] <> '' then Nm := FCalXvtrName[i]
|
||||
else Nm := 'XVTR' + IntToStr(i);
|
||||
FLblSMXvtr[i].Caption := Nm + ':';
|
||||
FLblSMXvtr[i].Hint := 'XVTR' + IntToStr(i) + ' (' + Nm + ')';
|
||||
FLblSMXvtr[i].SetBounds(X, Y + 24 + r * CAL_ROW_H + 5, CAL_SM_LBL_W, 18);
|
||||
FEdSMXvtr[i].SetBounds(X + CAL_SM_LBL_W, Y + 24 + r * CAL_ROW_H,
|
||||
CAL_ED_W, BTN_H + 2);
|
||||
Inc(Slot);
|
||||
end;
|
||||
FLblSMXvtr[i].Visible := FCalXvtrOn[i];
|
||||
FEdSMXvtr[i].Visible := FCalXvtrOn[i];
|
||||
end;
|
||||
XvtrRows := (Slot + CAL_COLS - 1) div CAL_COLS;
|
||||
FLblSMXvtrHdr.Visible := Slot > 0;
|
||||
|
||||
if Slot > 0 then H := Y + 24 + XvtrRows * CAL_ROW_H + 12
|
||||
else H := Y + 12;
|
||||
// ---- таблицы офсетов (S-метр и спектр) — одинаковая геометрия ----
|
||||
HSM := LayoutCalOffsets(FLblSMBand, FEdSMBand, FLblSMXvtrHdr,
|
||||
FLblSMXvtr, FEdSMXvtr, 52);
|
||||
HDisp := LayoutCalOffsets(FLblDispBand, FEdDispBand, FLblDispXvtrHdr,
|
||||
FLblDispXvtr, FEdDispXvtr, 52);
|
||||
|
||||
// ---- вертикальная укладка групп ----
|
||||
GTop := 86;
|
||||
@@ -2162,7 +2186,89 @@ begin
|
||||
FCalGrpVA.SetBounds(CAL_MARGIN, GTop, CAL_GRP_W, CAL_GRP2_H);
|
||||
GTop := GTop + CAL_GRP2_H + 14;
|
||||
end;
|
||||
FCalGrpSM.SetBounds(CAL_MARGIN, GTop, CAL_GRP_W, H);
|
||||
FCalGrpSM.SetBounds(CAL_MARGIN, GTop, CAL_GRP_W, HSM);
|
||||
GTop := GTop + HSM + 14;
|
||||
FCalGrpDisp.SetBounds(CAL_MARGIN, GTop, CAL_GRP_W, HDisp);
|
||||
end;
|
||||
|
||||
function TSettingsForm.LayoutCalOffsets(const BandLbl: array of TLabel;
|
||||
const BandEd: array of TFlatFloatSpinEdit; XvtrHdr: TLabel;
|
||||
const XvtrLbl: array of TLabel; const XvtrEd: array of TFlatFloatSpinEdit;
|
||||
Y0: Integer): Integer;
|
||||
// Строки диапазонов стоят по индексу плана (пустой слот просто скрыт),
|
||||
// слоты трансвертеров — только включённые и без дырок.
|
||||
var
|
||||
i, r, c, X, Y, Slot, BandRows, XvtrRows: Integer;
|
||||
Nm: string;
|
||||
begin
|
||||
BandRows := 0;
|
||||
for i := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
Nm := BandName(i, FCalIsPluto);
|
||||
r := i div CAL_COLS;
|
||||
c := i mod CAL_COLS;
|
||||
X := CAL_PAD + c * (CAL_SM_LBL_W + CAL_ED_W + 14);
|
||||
Y := Y0 + r * CAL_ROW_H;
|
||||
if BandLbl[i] <> nil then
|
||||
begin
|
||||
BandLbl[i].Caption := Nm + ':';
|
||||
BandLbl[i].SetBounds(X, Y + 5, CAL_SM_LBL_W, 18);
|
||||
BandLbl[i].Visible := Nm <> '';
|
||||
end;
|
||||
if BandEd[i] <> nil then
|
||||
begin
|
||||
BandEd[i].SetBounds(X + CAL_SM_LBL_W, Y, CAL_ED_W, BTN_H + 2);
|
||||
BandEd[i].Visible := Nm <> '';
|
||||
end;
|
||||
if Nm <> '' then BandRows := r + 1;
|
||||
end;
|
||||
|
||||
Y := Y0 + BandRows * CAL_ROW_H + 6;
|
||||
XvtrHdr.SetBounds(CAL_PAD, Y, 300, 18);
|
||||
Slot := 0;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
if FCalXvtrOn[i] then
|
||||
begin
|
||||
r := Slot div CAL_COLS;
|
||||
c := Slot mod CAL_COLS;
|
||||
X := CAL_PAD + c * (CAL_SM_LBL_W + CAL_ED_W + 14);
|
||||
if FCalXvtrName[i] <> '' then Nm := FCalXvtrName[i]
|
||||
else Nm := 'XVTR' + IntToStr(i);
|
||||
XvtrLbl[i].Caption := Nm + ':';
|
||||
XvtrLbl[i].Hint := 'XVTR' + IntToStr(i) + ' (' + Nm + ')';
|
||||
XvtrLbl[i].SetBounds(X, Y + 24 + r * CAL_ROW_H + 5, CAL_SM_LBL_W, 18);
|
||||
XvtrEd[i].SetBounds(X + CAL_SM_LBL_W, Y + 24 + r * CAL_ROW_H,
|
||||
CAL_ED_W, BTN_H + 2);
|
||||
Inc(Slot);
|
||||
end;
|
||||
XvtrLbl[i].Visible := FCalXvtrOn[i];
|
||||
XvtrEd[i].Visible := FCalXvtrOn[i];
|
||||
end;
|
||||
XvtrRows := (Slot + CAL_COLS - 1) div CAL_COLS;
|
||||
XvtrHdr.Visible := Slot > 0;
|
||||
|
||||
if Slot > 0 then Result := Y + 24 + XvtrRows * CAL_ROW_H + 12
|
||||
else Result := Y + 12;
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.OnCalDispFromSMClick(Sender: TObject);
|
||||
// Стартовая точка калибровки дисплея: те же цифры, что у стрелки,
|
||||
// дальше доводятся руками по шумовой полке/эталонному генератору.
|
||||
var i: Integer;
|
||||
begin
|
||||
FLoading := True;
|
||||
try
|
||||
for i := 0 to CFG_BAND_COUNT - 1 do
|
||||
if (FEdDispBand[i] <> nil) and (FEdSMBand[i] <> nil) then
|
||||
FEdDispBand[i].Value := FEdSMBand[i].Value;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
if (FEdDispXvtr[i] <> nil) and (FEdSMXvtr[i] <> nil) then
|
||||
FEdDispXvtr[i].Value := FEdSMXvtr[i].Value;
|
||||
finally
|
||||
FLoading := False;
|
||||
end;
|
||||
OnCalAnyChange(FBtnDispFromSM);
|
||||
end;
|
||||
|
||||
procedure TSettingsForm.SetCalibrationBackend(IsPluto: Boolean);
|
||||
@@ -2177,11 +2283,15 @@ begin
|
||||
FCalConfig.PwrDetModel := FCmbPwrDetModel.ItemIndex;
|
||||
for i := 0 to CFG_BAND_COUNT - 1 do
|
||||
begin
|
||||
if FEdPADetCal[i] <> nil then FCalConfig.PADetCal[i] := FEdPADetCal[i].Value;
|
||||
if FEdSMBand[i] <> nil then FCalConfig.SMeterBandDB[i] := FEdSMBand[i].Value;
|
||||
if FEdPADetCal[i] <> nil then FCalConfig.PADetCal[i] := FEdPADetCal[i].Value;
|
||||
if FEdSMBand[i] <> nil then FCalConfig.SMeterBandDB[i] := FEdSMBand[i].Value;
|
||||
if FEdDispBand[i] <> nil then FCalConfig.DispBandDB[i] := FEdDispBand[i].Value;
|
||||
end;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
if FEdSMXvtr[i] <> nil then FCalConfig.SMeterXvtrDB[i] := FEdSMXvtr[i].Value;
|
||||
begin
|
||||
if FEdSMXvtr[i] <> nil then FCalConfig.SMeterXvtrDB[i] := FEdSMXvtr[i].Value;
|
||||
if FEdDispXvtr[i] <> nil then FCalConfig.DispXvtrDB[i] := FEdDispXvtr[i].Value;
|
||||
end;
|
||||
FCalConfig.SupplyVoltCal := FEdSupplyVoltCal.Value;
|
||||
FCalConfig.AmpVoffMV := FEdAmpVoff.Value;
|
||||
FCalConfig.AmpSensMVA := FEdAmpSens.Value;
|
||||
@@ -2226,9 +2336,13 @@ begin
|
||||
begin
|
||||
if FEdPADetCal[i] <> nil then FEdPADetCal[i].Value := C.PADetCal[i];
|
||||
if FEdSMBand[i] <> nil then FEdSMBand[i].Value := C.SMeterBandDB[i];
|
||||
if FEdDispBand[i] <> nil then FEdDispBand[i].Value := C.DispBandDB[i];
|
||||
end;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
if FEdSMXvtr[i] <> nil then FEdSMXvtr[i].Value := C.SMeterXvtrDB[i];
|
||||
begin
|
||||
if FEdSMXvtr[i] <> nil then FEdSMXvtr[i].Value := C.SMeterXvtrDB[i];
|
||||
if FEdDispXvtr[i] <> nil then FEdDispXvtr[i].Value := C.DispXvtrDB[i];
|
||||
end;
|
||||
FEdSupplyVoltCal.Value := C.SupplyVoltCal;
|
||||
FEdAmpVoff.Value := C.AmpVoffMV;
|
||||
FEdAmpSens.Value := C.AmpSensMVA;
|
||||
|
||||
Reference in New Issue
Block a user