mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +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:
+31
-4
@@ -452,6 +452,10 @@ type
|
||||
procedure OnAudioReady(const Left, Right: array of Single; Count: Integer);
|
||||
procedure OnDemodAudioReady(const Left, Right: array of Single; Count: Integer);
|
||||
procedure OnDMRAudioReady(const PCM: array of Single; Count: Integer);
|
||||
// Общая выборка калибровочной таблицы «диапазон/трансвертер» по частоте.
|
||||
function CalLookup(VisibleHz: Double;
|
||||
const BandTbl: array of Double;
|
||||
const XvtrTbl: array of Double): Double;
|
||||
procedure OnSpectrumReady(const Pixels: array of Single; Count: Integer);
|
||||
procedure OnWaterfallReady(const Pixels: array of Single; Count: Integer);
|
||||
procedure OnMicPacket(const Data: TMicDataPacket);
|
||||
@@ -628,6 +632,9 @@ type
|
||||
function ReadSMeterDBm: Double;
|
||||
// Офсет S-метра (дБ) для видимой частоты: слот трансвертера или диапазон.
|
||||
function SMeterCalFor(VisibleHz: Double): Double;
|
||||
// Офсет спектра/водопада (дБ) для дисплея: PanId 0 = главный, 1..N — пан.
|
||||
// Колбэк движка (display-поток), см. TOnDispCalQuery.
|
||||
function DispCalFor(PanId: Integer): Double;
|
||||
procedure SetSliceDevice(Id, DevIndex: Integer; const DevName: string);
|
||||
function SliceCount: Integer;
|
||||
function GetSlice(Id: Integer; out S: TCtrlSlice): Boolean;
|
||||
@@ -975,6 +982,9 @@ begin
|
||||
FDSPEngine.OnSliceAudio := OnSliceAudioReady;
|
||||
FDSPEngine.OnSliceDemodAudio := OnSliceDemodAudioReady;
|
||||
FDSPEngine.OnDemodAudio := OnDemodAudioReady;
|
||||
// Калибровка уровня спектра/водопада на диапазон/трансвертер — движок
|
||||
// спрашивает её раз в кадр (сам он про диапазоны не знает).
|
||||
FDSPEngine.OnDispCal := DispCalFor;
|
||||
FDSPEngine.SetDisplayResLimit(FDisplayPixels);
|
||||
|
||||
// QO-100 beacon lock: эталон частоты = декодер (см. ServiceBeaconLock).
|
||||
@@ -2060,7 +2070,8 @@ begin
|
||||
Result := FDSPEngine.GetSMeterDBm + SMeterCalFor(ActiveVfoHz);
|
||||
end;
|
||||
|
||||
function TRadioController.SMeterCalFor(VisibleHz: Double): Double;
|
||||
function TRadioController.CalLookup(VisibleHz: Double;
|
||||
const BandTbl: array of Double; const XvtrTbl: array of Double): Double;
|
||||
// Трансвертер имеет приоритет над диапазоном: его окно частот — видимые
|
||||
// (не IF), и усиление тракта задаёт конвертер/LNB, а не КВ-вход трансивера.
|
||||
// Индекс диапазона — по плану активного железа (HF openHPSDR / VHF-UHF Pluto).
|
||||
@@ -2071,14 +2082,30 @@ begin
|
||||
and FXvtrSettings.Entries[FCurrentXvtr].Enabled
|
||||
and (VisibleHz >= FXvtrSettings.Entries[FCurrentXvtr].FreqBegin)
|
||||
and (VisibleHz <= FXvtrSettings.Entries[FCurrentXvtr].FreqEnd) then
|
||||
Exit(FCal.SMeterXvtrDB[FCurrentXvtr]);
|
||||
Exit(XvtrTbl[FCurrentXvtr]);
|
||||
for i := 0 to CFG_XVTR_COUNT-1 do
|
||||
if FXvtrSettings.Entries[i].Enabled
|
||||
and (VisibleHz >= FXvtrSettings.Entries[i].FreqBegin)
|
||||
and (VisibleHz <= FXvtrSettings.Entries[i].FreqEnd) then
|
||||
Exit(FCal.SMeterXvtrDB[i]);
|
||||
Exit(XvtrTbl[i]);
|
||||
Bi := FreqToBandIdx(VisibleHz, IsPluto);
|
||||
if (Bi >= 0) and (Bi < CFG_BAND_COUNT) then Result := FCal.SMeterBandDB[Bi];
|
||||
if (Bi >= 0) and (Bi < CFG_BAND_COUNT) then Result := BandTbl[Bi];
|
||||
end;
|
||||
|
||||
function TRadioController.SMeterCalFor(VisibleHz: Double): Double;
|
||||
begin
|
||||
Result := CalLookup(VisibleHz, FCal.SMeterBandDB, FCal.SMeterXvtrDB);
|
||||
end;
|
||||
|
||||
function TRadioController.DispCalFor(PanId: Integer): Double;
|
||||
// Зовётся из display-потока движка раз в кадр. Главный дисплей калибруем по
|
||||
// центру полосы, доп. пан — по своей частоте (паны бывают кросс-банд).
|
||||
var Hz: Double;
|
||||
begin
|
||||
if (PanId > 0) and (PanId < MAX_PANS) then Hz := FPanFreqHz[PanId]
|
||||
else Hz := FCenterFreq;
|
||||
if Hz <= 0 then Exit(0.0);
|
||||
Result := CalLookup(Hz, FCal.DispBandDB, FCal.DispXvtrDB);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceDevice(Id, DevIndex: Integer;
|
||||
|
||||
@@ -345,6 +345,11 @@ type
|
||||
// Pluto); таблицы не пересекаются, т.к. секция JSON ключуется по MAC.
|
||||
SMeterBandDB: array[0..CFG_BAND_COUNT-1] of Double;
|
||||
SMeterXvtrDB: array[0..CFG_XVTR_COUNT-1] of Double;
|
||||
// Офсет спектра/водопада, дБ — та же разбивка. Отдельно от S-метра (как
|
||||
// Display Cal Offset в Thetis): у display-тракта своё окно/детектор, и его
|
||||
// уровень отличается от показаний стрелки на пару дБ.
|
||||
DispBandDB: array[0..CFG_BAND_COUNT-1] of Double;
|
||||
DispXvtrDB: array[0..CFG_XVTR_COUNT-1] of Double;
|
||||
end;
|
||||
|
||||
TGlobalSettings = record
|
||||
@@ -905,6 +910,13 @@ begin
|
||||
for i := 0 to CFG_XVTR_COUNT-1 do
|
||||
G.Cal.SMeterXvtrDB[i] :=
|
||||
EnsureRange(JD(GObj,'cal_smeter_xvtr_'+IntToStr(i), SMLegacy), -30.0, 30.0);
|
||||
// Спектр/водопад: своя таблица офсетов (дефолт 0 — как было).
|
||||
for i := 0 to CFG_BAND_COUNT-1 do
|
||||
G.Cal.DispBandDB[i] :=
|
||||
EnsureRange(JD(GObj,'cal_disp_band_'+IntToStr(i), 0.0), -30.0, 30.0);
|
||||
for i := 0 to CFG_XVTR_COUNT-1 do
|
||||
G.Cal.DispXvtrDB[i] :=
|
||||
EnsureRange(JD(GObj,'cal_disp_xvtr_'+IntToStr(i), 0.0), -30.0, 30.0);
|
||||
|
||||
for i := 0 to CFG_BAND_COUNT-1 do
|
||||
begin
|
||||
@@ -1025,6 +1037,10 @@ begin
|
||||
JW(O,'cal_smeter_band_'+IntToStr(i),G.Cal.SMeterBandDB[i]);
|
||||
for i := 0 to CFG_XVTR_COUNT-1 do
|
||||
JW(O,'cal_smeter_xvtr_'+IntToStr(i),G.Cal.SMeterXvtrDB[i]);
|
||||
for i := 0 to CFG_BAND_COUNT-1 do
|
||||
JW(O,'cal_disp_band_'+IntToStr(i),G.Cal.DispBandDB[i]);
|
||||
for i := 0 to CFG_XVTR_COUNT-1 do
|
||||
JW(O,'cal_disp_xvtr_'+IntToStr(i),G.Cal.DispXvtrDB[i]);
|
||||
end;
|
||||
|
||||
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
||||
|
||||
+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;
|
||||
|
||||
+28
-2
@@ -191,6 +191,11 @@ type
|
||||
// SliceId — логический id (из RadioController), не WDSP-канал.
|
||||
TOnSliceAudio = procedure(SliceId: Integer; const Left, Right: array of Single;
|
||||
Count: Integer) of object;
|
||||
// Калибровочный офсет спектра/водопада (дБ) для дисплея: PanId 0 = главный,
|
||||
// 1..MAX_PANS-1 = доп. панадаптер. Спрашивается раз в кадр из display-потока:
|
||||
// величина зависит от диапазона/трансвертера, о которых знает только
|
||||
// TRadioController (аналог SMeterCalFor для стрелки).
|
||||
TOnDispCalQuery = function(PanId: Integer): Double of object;
|
||||
// Пиксели спектра/водопада доп. пана — из display-потока (как FOnSpectrum/
|
||||
// FOnWaterfall главного). Внутри колбэка НЕ звать slice/pan API движка.
|
||||
TOnPanPixels = procedure(PanId: Integer; const Pixels: array of Single;
|
||||
@@ -331,6 +336,7 @@ type
|
||||
FOnDemodAudio:TOnAudioReady; // pre-volume/mute tap for digital decoders
|
||||
FOnSpectrum: TOnSpectrumReady;
|
||||
FOnWaterfall: TOnWaterfallReady;
|
||||
FOnDispCal: TOnDispCalQuery;
|
||||
FOnTXIQ: TOnTXIQReady;
|
||||
|
||||
// --- Мультислайсы ---
|
||||
@@ -756,6 +762,8 @@ type
|
||||
property OnSliceAudio: TOnSliceAudio read FOnSliceAudio write FOnSliceAudio;
|
||||
property OnSliceDemodAudio: TOnSliceAudio
|
||||
read FOnSliceDemodAudio write FOnSliceDemodAudio;
|
||||
// Офсет дисплея (дБ) на кадр — см. TOnDispCalQuery. nil = без коррекции.
|
||||
property OnDispCal: TOnDispCalQuery read FOnDispCal write FOnDispCal;
|
||||
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
||||
property OnWaterfall: TOnWaterfallReady read FOnWaterfall write FOnWaterfall;
|
||||
// Пиксели доп. панов (display-поток). UI вешает при появлении панов (3.3).
|
||||
@@ -3810,18 +3818,24 @@ var
|
||||
Flag: Integer;
|
||||
i, N, p: Integer;
|
||||
DispID: Integer;
|
||||
Cal: Single;
|
||||
GotSpec, GotWf: Boolean;
|
||||
begin
|
||||
if not FInitialized or not FAnalyzerOpen then Exit;
|
||||
N := EnsureRange(FDisplayPixelCount, 1, SPECTRUM_PIXELS);
|
||||
DispID := FActiveDisplayID; // RX_DISP_ID или TX_DISP_ID, зависит от FTXActive
|
||||
|
||||
// Калибровка дисплея (дБ) — сдвиг всей трассы, спрашиваем раз в кадр.
|
||||
// На TX-дисплее коррекция входного тракта не при чём.
|
||||
if Assigned(FOnDispCal) and (DispID = RX_DISP_ID) then Cal := FOnDispCal(0)
|
||||
else Cal := 0;
|
||||
|
||||
Flag := 0;
|
||||
GetPixels(DispID, 0, @PixBuf[0], @Flag);
|
||||
if Flag <> 0 then
|
||||
begin
|
||||
for i := 0 to N - 1 do
|
||||
FSpectrumPixels[i] := PixBuf[i];
|
||||
FSpectrumPixels[i] := PixBuf[i] + Cal;
|
||||
if Assigned(FOnSpectrum) then
|
||||
FOnSpectrum(FSpectrumPixels, N);
|
||||
end;
|
||||
@@ -3831,7 +3845,7 @@ begin
|
||||
if Flag <> 0 then
|
||||
begin
|
||||
for i := 0 to N - 1 do
|
||||
FWaterfallPixels[i] := PixBuf[i];
|
||||
FWaterfallPixels[i] := PixBuf[i] + Cal;
|
||||
if Assigned(FOnWaterfall) then
|
||||
FOnWaterfall(FWaterfallPixels, N);
|
||||
end;
|
||||
@@ -3858,6 +3872,18 @@ begin
|
||||
finally
|
||||
FSliceLock.Leave;
|
||||
end;
|
||||
// Пан может стоять на своём диапазоне — офсет спрашиваем по его PanId.
|
||||
if GotSpec or GotWf then
|
||||
begin
|
||||
if Assigned(FOnDispCal) then Cal := FOnDispCal(p) else Cal := 0;
|
||||
if Cal <> 0 then
|
||||
begin
|
||||
if GotSpec then
|
||||
for i := 0 to N - 1 do PixBuf[i] := PixBuf[i] + Cal;
|
||||
if GotWf then
|
||||
for i := 0 to N - 1 do WfBuf[i] := WfBuf[i] + Cal;
|
||||
end;
|
||||
end;
|
||||
if GotSpec and Assigned(FOnPanSpectrum) then
|
||||
FOnPanSpectrum(p, PixBuf, N);
|
||||
if GotWf and Assigned(FOnPanWaterfall) then
|
||||
|
||||
Reference in New Issue
Block a user