mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feature(settings): вкладка Calibration — детектор мощности/КСВ, V/A, S-метр
Паритет с Thetis-Enhanced (Det. Cal. / Volts&Amps правки): - TCalibration в TGlobalSettings (JSON cal_*): модель детектора Alex-моста, per-band множитель FWD/REV 0.5..2.0, множитель напряжения, Voff/Sens датчика тока, офсет S-метра. - AlexDetToWatts: константы Thetis computeAlexFwdPower/computeRefPower по моделям (ANAN-100 / 200D / 7000DLE-Anvelina Pro 3 / 8000DLE-Orion MkII, 6m-спецкейс REV). Дефолт = прежняя формула ANAN-100 (поведение не меняется). - OnHPStatus: FWD и REV через модельную формулу x множитель банда; КСВ = (1+p)/(1-p), p=sqrt(RevW/FwdW) (Thetis) вместо отношения сырых ADC; напряжение x SupplyVoltCal; ток с калибруемыми Voff/Sens. - WDSPEngine.SMeterCalDB: офсет в GetSMeterDBm/GetSliceSMeter (главный S-метр, slice-флаги, web). - Выбор модели в комбо подставляет модельные дефолты датчика тока (Anvelina Pro 3/7000DLE: 340 мВ / 88 мВ/А — Thetis GetDefaultVoltCalibration). - Фиксы: гард FLoading при постройке вкладки (SetItemIndex стреляет OnChange на программном присвоении -> AV на nil-контролах), FPageCalib/FNavCalib в списках SelectPage (вкладка была невидимой). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+65
-6
@@ -400,7 +400,9 @@ function BoardSupportsSupplyCurrent(BoardType: Integer): Boolean;
|
|||||||
|
|
||||||
// Convert raw ADC value to supply current in Amps (Board 5 / Orion MkII only)
|
// Convert raw ADC value to supply current in Amps (Board 5 / Orion MkII only)
|
||||||
// Uses UserADC1 (bytes 55-56). Returns -1 for unsupported boards.
|
// Uses UserADC1 (bytes 55-56). Returns -1 for unsupported boards.
|
||||||
function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer): Double;
|
// VoffMV/SensMVA — калибровка датчика (Thetis AmpVoff/AmpSens, ACS712-style).
|
||||||
|
function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer;
|
||||||
|
VoffMV: Double = 360.0; SensMVA: Double = 120.0): Double;
|
||||||
|
|
||||||
// Convert raw ADC value to RF power in Watts (ANAN-100)
|
// Convert raw ADC value to RF power in Watts (ANAN-100)
|
||||||
function ADCToWatts100(RawADC: Word): Double;
|
function ADCToWatts100(RawADC: Word): Double;
|
||||||
@@ -408,6 +410,16 @@ function ADCToWatts100(RawADC: Word): Double;
|
|||||||
// Convert raw ADC value to RF power in Watts (ANAN-10)
|
// Convert raw ADC value to RF power in Watts (ANAN-10)
|
||||||
function ADCToWatts10(RawADC: Word): Double;
|
function ADCToWatts10(RawADC: Word): Double;
|
||||||
|
|
||||||
|
// Alex bridge FWD/REV detector → Watts, константы Thetis computeAlexFwdPower/
|
||||||
|
// computeRefPower по модели трансивера:
|
||||||
|
// 0 = ANAN-10/100/100B/100D (Vref 3.3V)
|
||||||
|
// 1 = ANAN-200D / Orion (Vref 5.0V)
|
||||||
|
// 2 = ANAN-7000DLE / Anvelina Pro 3 / G2 / Red Pitaya
|
||||||
|
// 3 = ANAN-8000DLE / Orion MkII
|
||||||
|
// IsRev — REV-детектор (свой bridge_volt/offset), Is6m — 6m-коррекция REV.
|
||||||
|
function AlexDetToWatts(RawADC: Word; DetModel: Integer;
|
||||||
|
IsRev, Is6m: Boolean): Double;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
procedure PackU32BE(Value: LongWord; out B: array of Byte; Offset: Integer);
|
procedure PackU32BE(Value: LongWord; out B: array of Byte; Offset: Integer);
|
||||||
@@ -476,14 +488,15 @@ begin
|
|||||||
Result := BoardType = 5;
|
Result := BoardType = 5;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer): Double;
|
function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer;
|
||||||
|
VoffMV: Double; SensMVA: Double): Double;
|
||||||
begin
|
begin
|
||||||
// Board 5 (Orion MkII): UserADC1, Vref=5V → mV = adc/4095*5000.
|
// Board 5 (Orion MkII): UserADC1, Vref=5V → mV = adc/4095*5000.
|
||||||
// Current sensor: Voff=360mV, Sens=120mV/A (Thetis defaults, ACS712-style).
|
// Formula: amps = (mV - Voff) / Sens. Дефолты Voff=360мВ, Sens=120мВ/А
|
||||||
// Formula: amps = (mV - Voff) / Sens.
|
// (Thetis, ACS712-style); переопределяются калибровкой.
|
||||||
if BoardType = 5 then
|
if (BoardType = 5) and (SensMVA > 0) then
|
||||||
begin
|
begin
|
||||||
Result := ((RawADC / 4095.0) * 5000.0 - 360.0) / 120.0;
|
Result := ((RawADC / 4095.0) * 5000.0 - VoffMV) / SensMVA;
|
||||||
if Result < 0.0 then Result := 0.0;
|
if Result < 0.0 then Result := 0.0;
|
||||||
end else
|
end else
|
||||||
Result := -1.0;
|
Result := -1.0;
|
||||||
@@ -507,4 +520,50 @@ begin
|
|||||||
Result := (V * V) / 0.09;
|
Result := (V * V) / 0.09;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function AlexDetToWatts(RawADC: Word; DetModel: Integer;
|
||||||
|
IsRev, Is6m: Boolean): Double;
|
||||||
|
var
|
||||||
|
Bridge, Vref, V: Double;
|
||||||
|
CalOff: Integer;
|
||||||
|
begin
|
||||||
|
// Константы из Thetis (console.cs computeAlexFwdPower / computeRefPower).
|
||||||
|
// W = ((ADC - CalOff)/4095 * Vref)^2 / Bridge.
|
||||||
|
case DetModel of
|
||||||
|
1: begin // ANAN-200D / Orion
|
||||||
|
Vref := 5.0;
|
||||||
|
if IsRev then begin
|
||||||
|
Bridge := 0.108; if Is6m then Bridge := 0.5;
|
||||||
|
CalOff := 2;
|
||||||
|
end else begin
|
||||||
|
Bridge := 0.108; CalOff := 4;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
2: begin // ANAN-7000DLE / Anvelina Pro 3 / G2 / Red Pitaya
|
||||||
|
Vref := 5.0;
|
||||||
|
if IsRev then begin
|
||||||
|
Bridge := 0.15; if Is6m then Bridge := 0.7;
|
||||||
|
CalOff := 28;
|
||||||
|
end else begin
|
||||||
|
Bridge := 0.12; CalOff := 32;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
3: begin // ANAN-8000DLE / Orion MkII
|
||||||
|
Vref := 5.0; Bridge := 0.08;
|
||||||
|
if IsRev then CalOff := 16 else CalOff := 18;
|
||||||
|
end;
|
||||||
|
else begin // 0: ANAN-10/100/100B/100D
|
||||||
|
Vref := 3.3;
|
||||||
|
if IsRev then begin
|
||||||
|
Bridge := 0.095; if Is6m then Bridge := 0.5;
|
||||||
|
CalOff := 3;
|
||||||
|
end else begin
|
||||||
|
Bridge := 0.095; CalOff := 6;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
V := ((RawADC - CalOff) / 4095.0) * Vref;
|
||||||
|
if V < 0 then V := 0;
|
||||||
|
Result := (V * V) / Bridge;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -698,6 +698,7 @@ type
|
|||||||
function DefaultMicSource: TTXMicSource;
|
function DefaultMicSource: TTXMicSource;
|
||||||
procedure ApplyMOX(Active: Boolean);
|
procedure ApplyMOX(Active: Boolean);
|
||||||
procedure OnPASettingsChange(MaxPower: Double; const BandCal: array of Double);
|
procedure OnPASettingsChange(MaxPower: Double; const BandCal: array of Double);
|
||||||
|
procedure OnCalibrationSettingsChange(const C: TCalibration);
|
||||||
procedure OnPlutoTxMaxAttChange(MaxAttDb: Double);
|
procedure OnPlutoTxMaxAttChange(MaxAttDb: Double);
|
||||||
procedure OnAnt936xChange(const A: TAD936xAnt); // разъёмы AD936x (Antenna)
|
procedure OnAnt936xChange(const A: TAD936xAnt); // разъёмы AD936x (Antenna)
|
||||||
procedure OnVHFCalSettingsChange(const VHFCal: array of Double);
|
procedure OnVHFCalSettingsChange(const VHFCal: array of Double);
|
||||||
@@ -7413,6 +7414,12 @@ begin
|
|||||||
UpdateDriveLabel; // изменился потолок → пересчитать дБ-подпись drive
|
UpdateDriveLabel; // изменился потолок → пересчитать дБ-подпись drive
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.OnCalibrationSettingsChange(const C: TCalibration);
|
||||||
|
begin
|
||||||
|
// Логика (санитизация/офсет S-метра в DSP/persist) — в контроллере.
|
||||||
|
FController.SetCalibration(C);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TMainForm.OnAnt936xChange(const A: TAD936xAnt);
|
procedure TMainForm.OnAnt936xChange(const A: TAD936xAnt);
|
||||||
// Вкладка Antenna (AD936x): таблица «диапазон → разъём». Контроллер сохраняет её
|
// Вкладка Antenna (AD936x): таблица «диапазон → разъём». Контроллер сохраняет её
|
||||||
// и сразу применяет разъёмы текущего диапазона/трансвертера.
|
// и сразу применяет разъёмы текущего диапазона/трансвертера.
|
||||||
@@ -8226,6 +8233,7 @@ begin
|
|||||||
SF.OnVisibilityChange := ApplyVisibility;
|
SF.OnVisibilityChange := ApplyVisibility;
|
||||||
SF.OnFPSChange := ApplyFPS;
|
SF.OnFPSChange := ApplyFPS;
|
||||||
SF.OnPAChange := OnPASettingsChange;
|
SF.OnPAChange := OnPASettingsChange;
|
||||||
|
SF.OnCalibrationChange := OnCalibrationSettingsChange;
|
||||||
SF.OnPlutoTxChange := OnPlutoTxMaxAttChange;
|
SF.OnPlutoTxChange := OnPlutoTxMaxAttChange;
|
||||||
SF.OnAnt936xChange := OnAnt936xChange;
|
SF.OnAnt936xChange := OnAnt936xChange;
|
||||||
SF.OnVHFCalChange := OnVHFCalSettingsChange;
|
SF.OnVHFCalChange := OnVHFCalSettingsChange;
|
||||||
@@ -8251,6 +8259,7 @@ begin
|
|||||||
SF.LoadAnt936x(FController.Ant936x, FController.FXvtrSettings);
|
SF.LoadAnt936x(FController.Ant936x, FController.FXvtrSettings);
|
||||||
SF.LoadXvtrSettings(FController.FXvtrSettings);
|
SF.LoadXvtrSettings(FController.FXvtrSettings);
|
||||||
SF.LoadPASettings(FController.FPAMaxPower, FController.FPABandCal);
|
SF.LoadPASettings(FController.FPAMaxPower, FController.FPABandCal);
|
||||||
|
SF.LoadCalibration(FController.FCal);
|
||||||
SF.LoadPlutoTxMaxAtt(FController.FPlutoTxMaxAttDb);
|
SF.LoadPlutoTxMaxAtt(FController.FPlutoTxMaxAttDb);
|
||||||
SF.LoadVHFCalSettings(FController.FVHFBandCal);
|
SF.LoadVHFCalSettings(FController.FVHFBandCal);
|
||||||
SF.LoadCATSettings(
|
SF.LoadCATSettings(
|
||||||
|
|||||||
+41
-7
@@ -375,6 +375,10 @@ type
|
|||||||
FBandCache: array[0..CFG_BAND_COUNT-1] of TBandSettings;
|
FBandCache: array[0..CFG_BAND_COUNT-1] of TBandSettings;
|
||||||
FPABandCal: array[0..CFG_BAND_COUNT-1] of Double;
|
FPABandCal: array[0..CFG_BAND_COUNT-1] of Double;
|
||||||
FVHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double;
|
FVHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double;
|
||||||
|
// Калибровки (детектор мощности/КСВ, напряжение/ток, S-метр) — вкладка
|
||||||
|
// Calibration. Читается сетевым потоком в OnHPStatus (double-поля, torn
|
||||||
|
// read безопасен как у FPABandCal); менять через SetCalibration.
|
||||||
|
FCal: TCalibration;
|
||||||
FDevMAC: array[0..5] of Byte;
|
FDevMAC: array[0..5] of Byte;
|
||||||
// Глобальные настройки устройства, загруженные ConnectDevice. Хранятся для
|
// Глобальные настройки устройства, загруженные ConnectDevice. Хранятся для
|
||||||
// UI-рендера полей, которых нет в контроллере (DisplayFPS/FreqMhzDigits/CAT).
|
// UI-рендера полей, которых нет в контроллере (DisplayFPS/FreqMhzDigits/CAT).
|
||||||
@@ -681,6 +685,7 @@ type
|
|||||||
function PlutoTxAttForDrive: Double; // дБ<0 под текущий FDrivePercent
|
function PlutoTxAttForDrive: Double; // дБ<0 под текущий FDrivePercent
|
||||||
procedure ApplyPlutoTxAtten; // ставит att по FTransmitting/FTuning
|
procedure ApplyPlutoTxAtten; // ставит att по FTransmitting/FTuning
|
||||||
procedure SetPlutoTxMaxAtt(Db: Double); // потолок на drive=100% (внешний усилитель)
|
procedure SetPlutoTxMaxAtt(Db: Double); // потолок на drive=100% (внешний усилитель)
|
||||||
|
procedure SetCalibration(const C: TCalibration); // вкладка Calibration → FCal + офсет S-метра в DSP
|
||||||
procedure ApplyWDSPDrive(Pct: Integer); // цифровой drive WDSP: Pluto=1.0 (мощность — атт), HPSDR=Pct/100
|
procedure ApplyWDSPDrive(Pct: Integer); // цифровой drive WDSP: Pluto=1.0 (мощность — атт), HPSDR=Pct/100
|
||||||
|
|
||||||
// Диапазоны / трансвертеры / спан
|
// Диапазоны / трансвертеры / спан
|
||||||
@@ -943,6 +948,7 @@ begin
|
|||||||
FFMStepOn := True;
|
FFMStepOn := True;
|
||||||
FRawFMDeviation := RAW_FM_DEV_DEFAULT;
|
FRawFMDeviation := RAW_FM_DEV_DEFAULT;
|
||||||
FLocalAudio := True; // GUI: звук на локальную карту; демон выключит явно
|
FLocalAudio := True; // GUI: звук на локальную карту; демон выключит явно
|
||||||
|
TSettingsManager.DefaultCalibration(FCal);
|
||||||
FLastSMeter := -130; FLastSWR := 1; FLastSupplyV := -1; FLastSupplyA := -1;
|
FLastSMeter := -130; FLastSWR := 1; FLastSupplyV := -1; FLastSupplyA := -1;
|
||||||
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoSocTempC := TELEMETRY_NONE;
|
FLastPlutoTempC := TELEMETRY_NONE; FLastPlutoSocTempC := TELEMETRY_NONE;
|
||||||
FLastPlutoRSSIdB := TELEMETRY_NONE;
|
FLastPlutoRSSIdB := TELEMETRY_NONE;
|
||||||
@@ -965,6 +971,7 @@ begin
|
|||||||
FDSPEngine.OnSliceDemodAudio := OnSliceDemodAudioReady;
|
FDSPEngine.OnSliceDemodAudio := OnSliceDemodAudioReady;
|
||||||
FDSPEngine.OnDemodAudio := OnDemodAudioReady;
|
FDSPEngine.OnDemodAudio := OnDemodAudioReady;
|
||||||
FDSPEngine.SetDisplayResLimit(FDisplayPixels);
|
FDSPEngine.SetDisplayResLimit(FDisplayPixels);
|
||||||
|
FDSPEngine.SMeterCalDB := FCal.SMeterOffsetDB;
|
||||||
|
|
||||||
// QO-100 beacon lock: эталон частоты = декодер (см. ServiceBeaconLock).
|
// QO-100 beacon lock: эталон частоты = декодер (см. ServiceBeaconLock).
|
||||||
FBeaconRefHz := 10489750000.0; // центральный BPSK-маяк NB-транспондера (downlink)
|
FBeaconRefHz := 10489750000.0; // центральный BPSK-маяк NB-транспондера (downlink)
|
||||||
@@ -1186,10 +1193,12 @@ procedure TRadioController.OnHPStatus(const Status: THighPriorityStatus);
|
|||||||
const
|
const
|
||||||
SWR_MIN_FWD_W = 1.0; // ниже — fwd это ADC-шум, SWR не считаем
|
SWR_MIN_FWD_W = 1.0; // ниже — fwd это ADC-шум, SWR не считаем
|
||||||
SWR_CAP = 9.9;
|
SWR_CAP = 9.9;
|
||||||
|
BAND_6M = 10; // индекс 6m в HF-плане (160m..6m)
|
||||||
var
|
var
|
||||||
FwdPwr, RevPwr: Word;
|
FwdPwr, RevPwr: Word;
|
||||||
SupplyV, SupplyA, FwdW, SWRV, Rho: Double;
|
SupplyV, SupplyA, FwdW, RevW, SWRV, Rho, DetK: Double;
|
||||||
IsTx: Boolean;
|
Bi: Integer;
|
||||||
|
Is6m, IsTx: Boolean;
|
||||||
begin
|
begin
|
||||||
FwdPwr := (Status.FwdPwrAlex0Hi shl 8) or Status.FwdPwrAlex0Lo;
|
FwdPwr := (Status.FwdPwrAlex0Hi shl 8) or Status.FwdPwrAlex0Lo;
|
||||||
RevPwr := (Status.RevPwrAlex0Hi shl 8) or Status.RevPwrAlex0Lo;
|
RevPwr := (Status.RevPwrAlex0Hi shl 8) or Status.RevPwrAlex0Lo;
|
||||||
@@ -1208,19 +1217,31 @@ begin
|
|||||||
SupplyV := ADCToSupplyVolts(
|
SupplyV := ADCToSupplyVolts(
|
||||||
(Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo,
|
(Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo,
|
||||||
FNetwork.Device.BoardType);
|
FNetwork.Device.BoardType);
|
||||||
|
if SupplyV >= 0 then SupplyV := SupplyV * FCal.SupplyVoltCal;
|
||||||
|
|
||||||
if BoardSupportsSupplyCurrent(FNetwork.Device.BoardType) then
|
if BoardSupportsSupplyCurrent(FNetwork.Device.BoardType) then
|
||||||
SupplyA := ADCToSupplyCurrent(
|
SupplyA := ADCToSupplyCurrent(
|
||||||
(Status.UserADC1Hi shl 8) or Status.UserADC1Lo,
|
(Status.UserADC1Hi shl 8) or Status.UserADC1Lo,
|
||||||
FNetwork.Device.BoardType);
|
FNetwork.Device.BoardType,
|
||||||
|
FCal.AmpVoffMV, FCal.AmpSensMVA);
|
||||||
end;
|
end;
|
||||||
FwdW := ADCToWatts100(FwdPwr);
|
// Детектор Alex-моста: константы по модели (вкладка Calibration) + per-band
|
||||||
|
// множитель. Множитель общий для FWD/REV, поэтому КСВ от него не зависит
|
||||||
|
// (как Thetis PABandCalMult).
|
||||||
|
Bi := TxBandIdx;
|
||||||
|
Is6m := Bi = BAND_6M;
|
||||||
|
DetK := 1.0;
|
||||||
|
if (Bi >= 0) and (Bi < CFG_BAND_COUNT) and (FCal.PADetCal[Bi] > 0) then
|
||||||
|
DetK := FCal.PADetCal[Bi];
|
||||||
|
FwdW := AlexDetToWatts(FwdPwr, FCal.PwrDetModel, False, Is6m) * DetK;
|
||||||
|
RevW := AlexDetToWatts(RevPwr, FCal.PwrDetModel, True, Is6m) * DetK;
|
||||||
// FTransmitting — программный флаг; HPS_PTT — вход HW-PTT (footswitch/mic).
|
// FTransmitting — программный флаг; HPS_PTT — вход HW-PTT (footswitch/mic).
|
||||||
IsTx := FTransmitting or ((Status.StatusBits and HPS_PTT) <> 0);
|
IsTx := FTransmitting or ((Status.StatusBits and HPS_PTT) <> 0);
|
||||||
// SWR = (1+ρ)/(1-ρ), ρ = adc_r/adc_f; только при значимой fwd-мощности.
|
// SWR = (1+ρ)/(1-ρ), ρ = sqrt(RevW/FwdW) (Thetis); только при значимой
|
||||||
if IsTx and (FwdW >= SWR_MIN_FWD_W) and (FwdPwr > 0) then
|
// fwd-мощности.
|
||||||
|
if IsTx and (FwdW >= SWR_MIN_FWD_W) then
|
||||||
begin
|
begin
|
||||||
Rho := RevPwr / FwdPwr;
|
Rho := Sqrt(RevW / FwdW);
|
||||||
if Rho >= 0.99 then
|
if Rho >= 0.99 then
|
||||||
SWRV := SWR_CAP
|
SWRV := SWR_CAP
|
||||||
else
|
else
|
||||||
@@ -2794,6 +2815,7 @@ begin
|
|||||||
Result.PlutoTxMaxAttDb := FPlutoTxMaxAttDb;
|
Result.PlutoTxMaxAttDb := FPlutoTxMaxAttDb;
|
||||||
for i := 0 to CFG_BAND_COUNT - 1 do Result.PABandCal[i] := FPABandCal[i];
|
for i := 0 to CFG_BAND_COUNT - 1 do Result.PABandCal[i] := FPABandCal[i];
|
||||||
for i := 0 to CFG_XVTR_COUNT - 1 do Result.VHFBandCal[i] := FVHFBandCal[i];
|
for i := 0 to CFG_XVTR_COUNT - 1 do Result.VHFBandCal[i] := FVHFBandCal[i];
|
||||||
|
Result.Cal := FCal;
|
||||||
Result.ActiveVfo := FActiveVfo;
|
Result.ActiveVfo := FActiveVfo;
|
||||||
Result.NRMode := FNRMode;
|
Result.NRMode := FNRMode;
|
||||||
Result.NBMode := FNBMode;
|
Result.NBMode := FNBMode;
|
||||||
@@ -3539,6 +3561,17 @@ begin
|
|||||||
SaveGlobalSettings; // персист потолка (per-device)
|
SaveGlobalSettings; // персист потолка (per-device)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SetCalibration(const C: TCalibration);
|
||||||
|
begin
|
||||||
|
FCal := C;
|
||||||
|
// Санитизация: битый конфиг не должен дать деление на ноль/абсурдные ватты.
|
||||||
|
FCal.PwrDetModel := EnsureRange(FCal.PwrDetModel, 0, 3);
|
||||||
|
if FCal.AmpSensMVA <= 0 then FCal.AmpSensMVA := 120.0;
|
||||||
|
if Assigned(FDSPEngine) then
|
||||||
|
FDSPEngine.SMeterCalDB := FCal.SMeterOffsetDB;
|
||||||
|
SaveGlobalSettings; // персист (гейт по FDevConnected внутри)
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TRadioController.ApplyWDSPDrive(Pct: Integer);
|
procedure TRadioController.ApplyWDSPDrive(Pct: Integer);
|
||||||
// На Pluto мощность задаётся ОДИН раз — железной аттенюацией (PlutoTxAttForDrive),
|
// На Pluto мощность задаётся ОДИН раз — железной аттенюацией (PlutoTxAttForDrive),
|
||||||
// поэтому цифровой уровень WDSP держим на максимуме (1.0), а не режем по drive%.
|
// поэтому цифровой уровень WDSP держим на максимуме (1.0), а не режем по drive%.
|
||||||
@@ -5018,6 +5051,7 @@ begin
|
|||||||
FPABandCal[i] := FLoadedGlobal.PABandCal[i];
|
FPABandCal[i] := FLoadedGlobal.PABandCal[i];
|
||||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||||
FVHFBandCal[i] := FLoadedGlobal.VHFBandCal[i];
|
FVHFBandCal[i] := FLoadedGlobal.VHFBandCal[i];
|
||||||
|
SetCalibration(FLoadedGlobal.Cal);
|
||||||
// drive% (0..100) — раньше грузился через TrkDrive.Position; теперь напрямую,
|
// drive% (0..100) — раньше грузился через TrkDrive.Position; теперь напрямую,
|
||||||
// слайдер выставит rfDevice-рендер. Калиброванный байт считаем после FCurrentBand.
|
// слайдер выставит rfDevice-рендер. Калиброванный байт считаем после FCurrentBand.
|
||||||
FDrivePercent := EnsureRange(FLoadedGlobal.DriveLevel, 0, 100);
|
FDrivePercent := EnsureRange(FLoadedGlobal.DriveLevel, 0, 100);
|
||||||
|
|||||||
@@ -330,6 +330,18 @@ type
|
|||||||
EnableXvtrHf: Boolean; // режим XVTR HF
|
EnableXvtrHf: Boolean; // режим XVTR HF
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Калибровки (вкладка Calibration): детектор мощности/КСВ Alex-моста,
|
||||||
|
// напряжение/ток питания, S-метр. Модель детектора задаёт константы моста
|
||||||
|
// (Vref/bridge_volt/ADC-offset, как в Thetis computeAlexFwdPower).
|
||||||
|
TCalibration = record
|
||||||
|
PwrDetModel: Integer; // 0=ANAN-10/100 (3.3V), 1=ANAN-200D, 2=ANAN-7000DLE/Anvelina Pro 3, 3=ANAN-8000DLE/Orion MkII
|
||||||
|
PADetCal: array[0..CFG_BAND_COUNT-1] of Double; // множитель FWD/REV на банд, 0.50..2.00 (1.0 = без коррекции)
|
||||||
|
SupplyVoltCal: Double; // множитель напряжения питания (1.0 = без коррекции)
|
||||||
|
AmpVoffMV: Double; // сдвиг нуля датчика тока, мВ (Thetis default 360)
|
||||||
|
AmpSensMVA: Double; // чувствительность датчика тока, мВ/А (Thetis default 120)
|
||||||
|
SMeterOffsetDB: Double; // офсет S-метра, дБ (прибавляется к WDSP dBm)
|
||||||
|
end;
|
||||||
|
|
||||||
TGlobalSettings = record
|
TGlobalSettings = record
|
||||||
Volume: Integer;
|
Volume: Integer;
|
||||||
TxMonVolume: Integer; // громкость self-monitor RX-аудио на TX (RX MUTE off)
|
TxMonVolume: Integer; // громкость self-monitor RX-аудио на TX (RX MUTE off)
|
||||||
@@ -397,6 +409,8 @@ type
|
|||||||
PlutoTxMaxAttDb: Double; // Pluto: TX-аттенюация на drive=100%, дБ<0 (дефолт -10)
|
PlutoTxMaxAttDb: Double; // Pluto: TX-аттенюация на drive=100%, дБ<0 (дефолт -10)
|
||||||
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
PABandCal: array[0..CFG_BAND_COUNT-1] of Double; // калибровка на диапазон 38.8..100.0
|
||||||
VHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double; // калибровка для XVTR слотов 38.8..100.0
|
VHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double; // калибровка для XVTR слотов 38.8..100.0
|
||||||
|
// --- Calibration (детектор мощности/КСВ, напряжение/ток, S-метр) ---
|
||||||
|
Cal: TCalibration;
|
||||||
// --- ADC settings ---
|
// --- ADC settings ---
|
||||||
DitherEnabled: Boolean; // DDC Specific byte 5 — уменьшает интермодуляцию
|
DitherEnabled: Boolean; // DDC Specific byte 5 — уменьшает интермодуляцию
|
||||||
RandomEnabled: Boolean; // DDC Specific byte 6 — рандомизация ADC
|
RandomEnabled: Boolean; // DDC Specific byte 6 — рандомизация ADC
|
||||||
@@ -454,6 +468,7 @@ type
|
|||||||
class function MacToStr(const MAC: array of Byte): string;
|
class function MacToStr(const MAC: array of Byte): string;
|
||||||
class procedure DefaultBand(BandIdx: Integer; out B: TBandSettings);
|
class procedure DefaultBand(BandIdx: Integer; out B: TBandSettings);
|
||||||
class procedure DefaultGlobal(out G: TGlobalSettings);
|
class procedure DefaultGlobal(out G: TGlobalSettings);
|
||||||
|
class procedure DefaultCalibration(out C: TCalibration);
|
||||||
class procedure DefaultTX(out T: TTXSettings);
|
class procedure DefaultTX(out T: TTXSettings);
|
||||||
// Размер окна — не привязан к MAC, хранится в корне JSON
|
// Размер окна — не привязан к MAC, хранится в корне JSON
|
||||||
procedure SaveWindowBounds(L, T, W, H, DPI: Integer; Maximized: Boolean);
|
procedure SaveWindowBounds(L, T, W, H, DPI: Integer; Maximized: Boolean);
|
||||||
@@ -614,6 +629,19 @@ begin
|
|||||||
// ADC — включены по умолчанию: поддерживаются на Hermes, Angelia, Orion
|
// ADC — включены по умолчанию: поддерживаются на Hermes, Angelia, Orion
|
||||||
G.DitherEnabled := True;
|
G.DitherEnabled := True;
|
||||||
G.RandomEnabled := True;
|
G.RandomEnabled := True;
|
||||||
|
DefaultCalibration(G.Cal);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class procedure TSettingsManager.DefaultCalibration(out C: TCalibration);
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
FillChar(C, SizeOf(C), 0);
|
||||||
|
C.PwrDetModel := 0; // ANAN-10/100 — совпадает с прежней зашитой формулой
|
||||||
|
for i := 0 to CFG_BAND_COUNT-1 do C.PADetCal[i] := 1.0;
|
||||||
|
C.SupplyVoltCal := 1.0;
|
||||||
|
C.AmpVoffMV := 360.0;
|
||||||
|
C.AmpSensMVA := 120.0;
|
||||||
|
C.SMeterOffsetDB := 0.0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TSettingsManager.Create(const FilePath: string);
|
constructor TSettingsManager.Create(const FilePath: string);
|
||||||
@@ -856,6 +884,14 @@ begin
|
|||||||
// ADC settings
|
// ADC settings
|
||||||
G.DitherEnabled := JB(GObj,'dither_enabled', True);
|
G.DitherEnabled := JB(GObj,'dither_enabled', True);
|
||||||
G.RandomEnabled := JB(GObj,'random_enabled', True);
|
G.RandomEnabled := JB(GObj,'random_enabled', True);
|
||||||
|
// Calibration (детектор мощности/КСВ, напряжение/ток, S-метр)
|
||||||
|
G.Cal.PwrDetModel := EnsureRange(JI(GObj,'cal_pwr_det_model', 0), 0, 3);
|
||||||
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
|
G.Cal.PADetCal[i] := EnsureRange(JD(GObj,'cal_pa_det_'+IntToStr(i), 1.0), 0.5, 2.0);
|
||||||
|
G.Cal.SupplyVoltCal := EnsureRange(JD(GObj,'cal_supply_volt', 1.0), 0.1, 5.0);
|
||||||
|
G.Cal.AmpVoffMV := EnsureRange(JD(GObj,'cal_amp_voff_mv', 360.0), 0.0, 2000.0);
|
||||||
|
G.Cal.AmpSensMVA := EnsureRange(JD(GObj,'cal_amp_sens_mva', 120.0), 1.0, 1000.0);
|
||||||
|
G.Cal.SMeterOffsetDB := EnsureRange(JD(GObj,'cal_smeter_db', 0.0), -30.0, 30.0);
|
||||||
|
|
||||||
for i := 0 to CFG_BAND_COUNT-1 do
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
begin
|
begin
|
||||||
@@ -965,6 +1001,14 @@ begin
|
|||||||
// ADC settings
|
// ADC settings
|
||||||
JW(O,'dither_enabled',G.DitherEnabled);
|
JW(O,'dither_enabled',G.DitherEnabled);
|
||||||
JW(O,'random_enabled',G.RandomEnabled);
|
JW(O,'random_enabled',G.RandomEnabled);
|
||||||
|
// Calibration
|
||||||
|
JW(O,'cal_pwr_det_model',G.Cal.PwrDetModel);
|
||||||
|
for i := 0 to CFG_BAND_COUNT-1 do
|
||||||
|
JW(O,'cal_pa_det_'+IntToStr(i),G.Cal.PADetCal[i]);
|
||||||
|
JW(O,'cal_supply_volt',G.Cal.SupplyVoltCal);
|
||||||
|
JW(O,'cal_amp_voff_mv',G.Cal.AmpVoffMV);
|
||||||
|
JW(O,'cal_amp_sens_mva',G.Cal.AmpSensMVA);
|
||||||
|
JW(O,'cal_smeter_db',G.Cal.SMeterOffsetDB);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
procedure TSettingsManager.SaveBand(const MAC: array of Byte; BandIdx: Integer;
|
||||||
|
|||||||
+216
-5
@@ -41,6 +41,8 @@ type
|
|||||||
TOnAnt936xChange = procedure(const A: TAD936xAnt) of object;
|
TOnAnt936xChange = procedure(const A: TAD936xAnt) of object;
|
||||||
// VHF (XVTR) PA calibration: 8 slots, 38.8..100.0
|
// VHF (XVTR) PA calibration: 8 slots, 38.8..100.0
|
||||||
TOnVHFCalChange = procedure(const VHFCal: array of Double) of object;
|
TOnVHFCalChange = procedure(const VHFCal: array of Double) of object;
|
||||||
|
// Калибровки (вкладка Calibration): детектор мощности/КСВ, напряжение/ток, S-метр
|
||||||
|
TOnCalibrationChange = procedure(const C: TCalibration) of object;
|
||||||
// TX-настройки целиком (любая правка → один callback с актуальной структурой)
|
// TX-настройки целиком (любая правка → один callback с актуальной структурой)
|
||||||
TOnTXSettingsChange = procedure(const T: TTXSettings) of object;
|
TOnTXSettingsChange = procedure(const T: TTXSettings) of object;
|
||||||
|
|
||||||
@@ -150,6 +152,18 @@ type
|
|||||||
FEdVHFCal: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit;
|
FEdVHFCal: array[0..CFG_XVTR_COUNT-1] of TFlatFloatSpinEdit;
|
||||||
FVHFCalLabels: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
FVHFCalLabels: array[0..CFG_XVTR_COUNT-1] of TLabel;
|
||||||
|
|
||||||
|
// ---- Calibration tab controls ----
|
||||||
|
FPageCalib: TScrollBox;
|
||||||
|
FNavCalib: TFlatButton;
|
||||||
|
FCmbPwrDetModel: TFlatComboBox; // модель детектора Alex-моста
|
||||||
|
FEdPADetCal: array[0..10] of TFlatFloatSpinEdit; // множитель FWD/REV на банд
|
||||||
|
FEdSupplyVoltCal: TFlatFloatSpinEdit; // множитель напряжения питания
|
||||||
|
FEdAmpVoff: TFlatFloatSpinEdit; // датчик тока: сдвиг нуля, мВ
|
||||||
|
FEdAmpSens: TFlatFloatSpinEdit; // датчик тока: мВ/А
|
||||||
|
FEdSMeterOffset: TFlatFloatSpinEdit; // офсет S-метра, дБ
|
||||||
|
FCalConfig: TCalibration;
|
||||||
|
FOnCalibrationChange: TOnCalibrationChange;
|
||||||
|
|
||||||
// ---- Transmit tab controls ----
|
// ---- Transmit tab controls ----
|
||||||
// Текущее состояние, отдаётся в OnTXChange целиком при любой правке.
|
// Текущее состояние, отдаётся в OnTXChange целиком при любой правке.
|
||||||
FTX: TTXSettings;
|
FTX: TTXSettings;
|
||||||
@@ -337,6 +351,9 @@ type
|
|||||||
procedure BuildDisplayTab;
|
procedure BuildDisplayTab;
|
||||||
procedure BuildRX1Tab;
|
procedure BuildRX1Tab;
|
||||||
procedure BuildPATab;
|
procedure BuildPATab;
|
||||||
|
procedure BuildCalibrationTab;
|
||||||
|
procedure CollectCalFromUI;
|
||||||
|
procedure OnCalAnyChange(Sender: TObject);
|
||||||
procedure BuildCATTab;
|
procedure BuildCATTab;
|
||||||
procedure BuildTransmitTab;
|
procedure BuildTransmitTab;
|
||||||
procedure BuildAlexTab;
|
procedure BuildAlexTab;
|
||||||
@@ -445,6 +462,7 @@ type
|
|||||||
procedure LoadFreqMhzDigits(Digits: Integer);
|
procedure LoadFreqMhzDigits(Digits: Integer);
|
||||||
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
procedure LoadPASettings(MaxPower: Double; const BandCal: array of Double);
|
||||||
procedure LoadPlutoTxMaxAtt(MaxAttDb: Double);
|
procedure LoadPlutoTxMaxAtt(MaxAttDb: Double);
|
||||||
|
procedure LoadCalibration(const C: TCalibration);
|
||||||
// Вкладка Antenna: Alex-таблица (openHPSDR) либо таблица разъёмов AD936x.
|
// Вкладка Antenna: Alex-таблица (openHPSDR) либо таблица разъёмов AD936x.
|
||||||
procedure SetAntennaBackend(IsAD936x, HasAntSwitch: Boolean);
|
procedure SetAntennaBackend(IsAD936x, HasAntSwitch: Boolean);
|
||||||
// Таблица разъёмов + имена/видимость слотов трансвертеров.
|
// Таблица разъёмов + имена/видимость слотов трансвертеров.
|
||||||
@@ -474,6 +492,7 @@ type
|
|||||||
property OnPlutoTxChange: TOnPlutoTxChange read FOnPlutoTxChange write FOnPlutoTxChange;
|
property OnPlutoTxChange: TOnPlutoTxChange read FOnPlutoTxChange write FOnPlutoTxChange;
|
||||||
property OnAnt936xChange: TOnAnt936xChange read FOnAnt936xChange write FOnAnt936xChange;
|
property OnAnt936xChange: TOnAnt936xChange read FOnAnt936xChange write FOnAnt936xChange;
|
||||||
property OnVHFCalChange: TOnVHFCalChange read FOnVHFCalChange write FOnVHFCalChange;
|
property OnVHFCalChange: TOnVHFCalChange read FOnVHFCalChange write FOnVHFCalChange;
|
||||||
|
property OnCalibrationChange: TOnCalibrationChange read FOnCalibrationChange write FOnCalibrationChange;
|
||||||
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
property OnDisplayChange: TOnDisplayParamChange read FOnDisplayChange write FOnDisplayChange;
|
||||||
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
property OnWaterfallChange: TOnWaterfallParamChange read FOnWaterfallChange write FOnWaterfallChange;
|
||||||
property OnWfRenderChange: TOnWfRenderChange read FOnWfRenderChange write FOnWfRenderChange;
|
property OnWfRenderChange: TOnWfRenderChange read FOnWfRenderChange write FOnWfRenderChange;
|
||||||
@@ -704,11 +723,12 @@ begin
|
|||||||
FNavWaterfall := MakeNavButton('Waterfall', 182);
|
FNavWaterfall := MakeNavButton('Waterfall', 182);
|
||||||
FNavTransmit := MakeNavButton('Transmit', 218);
|
FNavTransmit := MakeNavButton('Transmit', 218);
|
||||||
FNavPA := MakeNavButton('Power Amplifier', 254);
|
FNavPA := MakeNavButton('Power Amplifier', 254);
|
||||||
FNavAdvanced := MakeNavButton('Advanced', 290);
|
FNavCalib := MakeNavButton('Calibration', 290);
|
||||||
FNavCAT := MakeNavButton('CAT', 326);
|
FNavAdvanced := MakeNavButton('Advanced', 326);
|
||||||
FNavAlex := MakeNavButton('Antenna / Alex', 362);
|
FNavCAT := MakeNavButton('CAT', 362);
|
||||||
FNavXvtr := MakeNavButton('Transverter', 398);
|
FNavAlex := MakeNavButton('Antenna / Alex', 398);
|
||||||
FNavQO := MakeNavButton('QO-100', 434);
|
FNavXvtr := MakeNavButton('Transverter', 434);
|
||||||
|
FNavQO := MakeNavButton('QO-100', 470);
|
||||||
|
|
||||||
FContentPanel := TPanel.Create(Self);
|
FContentPanel := TPanel.Create(Self);
|
||||||
FContentPanel.Parent := Self;
|
FContentPanel.Parent := Self;
|
||||||
@@ -722,6 +742,7 @@ begin
|
|||||||
FPageWaterfall := MakeScrollPage;
|
FPageWaterfall := MakeScrollPage;
|
||||||
FPageTransmit := MakeScrollPage;
|
FPageTransmit := MakeScrollPage;
|
||||||
FPagePA := MakeScrollPage;
|
FPagePA := MakeScrollPage;
|
||||||
|
FPageCalib := MakeScrollPage;
|
||||||
FPageAdvanced := MakeScrollPage;
|
FPageAdvanced := MakeScrollPage;
|
||||||
FPageCAT := MakeScrollPage;
|
FPageCAT := MakeScrollPage;
|
||||||
FPageAlex := MakeScrollPage;
|
FPageAlex := MakeScrollPage;
|
||||||
@@ -730,12 +751,14 @@ begin
|
|||||||
FAlexBoardType := 0;
|
FAlexBoardType := 0;
|
||||||
TSettingsManager.DefaultAlex(FAlexConfig);
|
TSettingsManager.DefaultAlex(FAlexConfig);
|
||||||
TSettingsManager.DefaultXvtr(FXvtrConfig);
|
TSettingsManager.DefaultXvtr(FXvtrConfig);
|
||||||
|
TSettingsManager.DefaultCalibration(FCalConfig);
|
||||||
|
|
||||||
BuildAudioTab;
|
BuildAudioTab;
|
||||||
BuildDisplayTab;
|
BuildDisplayTab;
|
||||||
BuildRX1Tab;
|
BuildRX1Tab;
|
||||||
BuildTransmitTab;
|
BuildTransmitTab;
|
||||||
BuildPATab;
|
BuildPATab;
|
||||||
|
BuildCalibrationTab;
|
||||||
BuildAdvancedTab;
|
BuildAdvancedTab;
|
||||||
BuildCATTab;
|
BuildCATTab;
|
||||||
BuildAlexTab;
|
BuildAlexTab;
|
||||||
@@ -768,6 +791,7 @@ begin
|
|||||||
else if Sender = FNavWaterfall then SelectPage(FPageWaterfall, FNavWaterfall)
|
else if Sender = FNavWaterfall then SelectPage(FPageWaterfall, FNavWaterfall)
|
||||||
else if Sender = FNavTransmit then SelectPage(FPageTransmit, FNavTransmit)
|
else if Sender = FNavTransmit then SelectPage(FPageTransmit, FNavTransmit)
|
||||||
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
|
||||||
|
else if Sender = FNavCalib then SelectPage(FPageCalib, FNavCalib)
|
||||||
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
|
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
|
||||||
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT)
|
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT)
|
||||||
else if Sender = FNavAlex then SelectPage(FPageAlex, FNavAlex)
|
else if Sender = FNavAlex then SelectPage(FPageAlex, FNavAlex)
|
||||||
@@ -783,6 +807,7 @@ begin
|
|||||||
FPageWaterfall.Visible:= APage = FPageWaterfall;
|
FPageWaterfall.Visible:= APage = FPageWaterfall;
|
||||||
FPageTransmit.Visible := APage = FPageTransmit;
|
FPageTransmit.Visible := APage = FPageTransmit;
|
||||||
FPagePA.Visible := APage = FPagePA;
|
FPagePA.Visible := APage = FPagePA;
|
||||||
|
FPageCalib.Visible := APage = FPageCalib;
|
||||||
FPageAdvanced.Visible := APage = FPageAdvanced;
|
FPageAdvanced.Visible := APage = FPageAdvanced;
|
||||||
FPageCAT.Visible := APage = FPageCAT;
|
FPageCAT.Visible := APage = FPageCAT;
|
||||||
FPageAlex.Visible := APage = FPageAlex;
|
FPageAlex.Visible := APage = FPageAlex;
|
||||||
@@ -795,6 +820,7 @@ begin
|
|||||||
FNavWaterfall.Active:= ANav = FNavWaterfall;
|
FNavWaterfall.Active:= ANav = FNavWaterfall;
|
||||||
FNavTransmit.Active := ANav = FNavTransmit;
|
FNavTransmit.Active := ANav = FNavTransmit;
|
||||||
FNavPA.Active := ANav = FNavPA;
|
FNavPA.Active := ANav = FNavPA;
|
||||||
|
FNavCalib.Active := ANav = FNavCalib;
|
||||||
FNavAdvanced.Active := ANav = FNavAdvanced;
|
FNavAdvanced.Active := ANav = FNavAdvanced;
|
||||||
FNavCAT.Active := ANav = FNavCAT;
|
FNavCAT.Active := ANav = FNavCAT;
|
||||||
FNavAlex.Active := ANav = FNavAlex;
|
FNavAlex.Active := ANav = FNavAlex;
|
||||||
@@ -1859,6 +1885,191 @@ begin
|
|||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.BuildCalibrationTab;
|
||||||
|
// Вкладка Calibration (паритет с Thetis-Enhanced): модель детектора мощности
|
||||||
|
// Alex-моста + множитель FWD/REV на банд (Det. Cal.), калибровка напряжения/
|
||||||
|
// тока питания (PAVoltCal/AmpVoff/AmpSens) и офсет S-метра.
|
||||||
|
const
|
||||||
|
MARGIN = 22;
|
||||||
|
GRP_PAD = 18;
|
||||||
|
LBL_W = 54;
|
||||||
|
ED_W = 72;
|
||||||
|
COL_W = LBL_W + ED_W + 14;
|
||||||
|
COLS = 4;
|
||||||
|
ROW_H = 32;
|
||||||
|
R1 = 42;
|
||||||
|
GRP1_H = 236;
|
||||||
|
GRP2_H = 156;
|
||||||
|
GRP3_H = 92;
|
||||||
|
VA_LBL_W = 220;
|
||||||
|
|
||||||
|
function MakeCalSpin(AParent: TWinControl; ALeft, ATop: Integer;
|
||||||
|
AMin, AMax, AInc: Double; ADec: Integer;
|
||||||
|
ADefault: Double): TFlatFloatSpinEdit;
|
||||||
|
var E: TFlatFloatSpinEdit;
|
||||||
|
begin
|
||||||
|
E := TFlatFloatSpinEdit.Create(Self);
|
||||||
|
E.Parent := AParent;
|
||||||
|
E.SetBounds(ALeft, ATop, ED_W, BTN_H + 2);
|
||||||
|
E.Color := CLR_INPUT;
|
||||||
|
E.Font.Color := CLR_INPUT_TEXT;
|
||||||
|
E.Font.Name := UI_FONT;
|
||||||
|
E.Font.Size := 9;
|
||||||
|
E.MinValue := AMin;
|
||||||
|
E.MaxValue := AMax;
|
||||||
|
E.Increment := AInc;
|
||||||
|
E.DecimalPlaces := ADec;
|
||||||
|
E.Value := ADefault;
|
||||||
|
E.OnChange := OnCalAnyChange;
|
||||||
|
Result := E;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
Grp: TPanel;
|
||||||
|
Lbl: TLabel;
|
||||||
|
i, r, c, X, Y, GTop: Integer;
|
||||||
|
begin
|
||||||
|
// Гард: SetItemIndex у TFlatComboBox стреляет OnChange и при программном
|
||||||
|
// присвоении — без гарда OnCalAnyChange дёрнется из «ItemIndex := 0» ниже,
|
||||||
|
// когда остальные контролы вкладки ещё nil → AV в CollectCalFromUI.
|
||||||
|
FLoading := True;
|
||||||
|
|
||||||
|
with TLabel.Create(Self) do
|
||||||
|
begin
|
||||||
|
Parent := FPageCalib;
|
||||||
|
Caption := 'Calibration';
|
||||||
|
SetBounds(MARGIN, 18, 400, 28);
|
||||||
|
Font.Name := UI_FONT;
|
||||||
|
Font.Size := 16;
|
||||||
|
Font.Style := [fsBold];
|
||||||
|
Font.Color := CLR_TEXT;
|
||||||
|
end;
|
||||||
|
|
||||||
|
with TLabel.Create(Self) do
|
||||||
|
begin
|
||||||
|
Parent := FPageCalib;
|
||||||
|
Caption := 'Power/SWR detector, supply voltage/current and S-meter calibration.';
|
||||||
|
SetBounds(MARGIN, 48, 620, 20);
|
||||||
|
Font.Name := UI_FONT;
|
||||||
|
Font.Size := 9;
|
||||||
|
Font.Color := CLR_TEXTDIM;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ── Power / SWR detector ────────────────────────────────────────────────
|
||||||
|
GTop := 86;
|
||||||
|
Grp := MakeGroupPanel(FPageCalib, 'Power / SWR detector', MARGIN, GTop, 700, GRP1_H);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Detector model', GRP_PAD, R1 + 6, 132);
|
||||||
|
FCmbPwrDetModel := MakeCombo(Grp, GRP_PAD + 150, R1, 280, OnCalAnyChange);
|
||||||
|
FCmbPwrDetModel.Items.Add('ANAN-10/100/100B/100D (3.3V)');
|
||||||
|
FCmbPwrDetModel.Items.Add('ANAN-200D / Orion');
|
||||||
|
FCmbPwrDetModel.Items.Add('ANAN-7000DLE / Anvelina Pro 3 / G2');
|
||||||
|
FCmbPwrDetModel.Items.Add('ANAN-8000DLE / Orion MkII');
|
||||||
|
FCmbPwrDetModel.ItemIndex := 0;
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp,
|
||||||
|
'FWD/REV multiplier per band: 1.00 = no correction, >1 if meter under-reads '
|
||||||
|
+ '(e.g. 88 W shown at real 100 W -> 1.14). SWR is not affected.',
|
||||||
|
GRP_PAD, R1 + ROW_H + 10, 650);
|
||||||
|
|
||||||
|
for i := 0 to 10 do
|
||||||
|
begin
|
||||||
|
r := i div COLS;
|
||||||
|
c := i mod COLS;
|
||||||
|
X := GRP_PAD + c * (COL_W + 4);
|
||||||
|
Y := R1 + ROW_H + 46 + r * ROW_H;
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, PA_BAND_NAMES[i] + ':', X, Y + 5, LBL_W);
|
||||||
|
|
||||||
|
FEdPADetCal[i] := MakeCalSpin(Grp, X + LBL_W, Y, 0.50, 2.00, 0.01, 2, 1.00);
|
||||||
|
FEdPADetCal[i].Tag := i;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ── Supply voltage / current ────────────────────────────────────────────
|
||||||
|
GTop := GTop + GRP1_H + 14;
|
||||||
|
Grp := MakeGroupPanel(FPageCalib, 'Supply voltage / current', MARGIN, GTop, 700, GRP2_H);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Voltage multiplier (1.000 = no correction)', GRP_PAD, R1 + 6, VA_LBL_W);
|
||||||
|
FEdSupplyVoltCal := MakeCalSpin(Grp, GRP_PAD + VA_LBL_W + 10, R1, 0.100, 5.000, 0.001, 3, 1.000);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Current sensor zero offset (mV)', GRP_PAD, R1 + ROW_H + 6, VA_LBL_W);
|
||||||
|
FEdAmpVoff := MakeCalSpin(Grp, GRP_PAD + VA_LBL_W + 10, R1 + ROW_H, 0.0, 2000.0, 1.0, 0, 360.0);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Current sensor sensitivity (mV/A)', GRP_PAD, R1 + 2 * ROW_H + 6, VA_LBL_W);
|
||||||
|
FEdAmpSens := MakeCalSpin(Grp, GRP_PAD + VA_LBL_W + 10, R1 + 2 * ROW_H, 1.0, 1000.0, 0.1, 1, 120.0);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Defaults 360 mV / 120 mV/A (ACS712-style, Orion MkII)',
|
||||||
|
GRP_PAD + VA_LBL_W + 10 + ED_W + 14, R1 + ROW_H + 6, 320);
|
||||||
|
|
||||||
|
// ── S-meter ─────────────────────────────────────────────────────────────
|
||||||
|
GTop := GTop + GRP2_H + 14;
|
||||||
|
Grp := MakeGroupPanel(FPageCalib, 'S-meter', MARGIN, GTop, 700, GRP3_H);
|
||||||
|
|
||||||
|
Lbl := MakeLbl(Grp, 'Offset (dB), added to all S-meter readings', GRP_PAD, R1 + 6, VA_LBL_W);
|
||||||
|
FEdSMeterOffset := MakeCalSpin(Grp, GRP_PAD + VA_LBL_W + 10, R1, -30.0, 30.0, 0.1, 1, 0.0);
|
||||||
|
|
||||||
|
FLoading := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.CollectCalFromUI;
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
FCalConfig.PwrDetModel := FCmbPwrDetModel.ItemIndex;
|
||||||
|
// UI редактирует HF-банды 0..10; слот 11 (пустой в HF-плане) не трогаем.
|
||||||
|
for i := 0 to 10 do
|
||||||
|
FCalConfig.PADetCal[i] := FEdPADetCal[i].Value;
|
||||||
|
FCalConfig.SupplyVoltCal := FEdSupplyVoltCal.Value;
|
||||||
|
FCalConfig.AmpVoffMV := FEdAmpVoff.Value;
|
||||||
|
FCalConfig.AmpSensMVA := FEdAmpSens.Value;
|
||||||
|
FCalConfig.SMeterOffsetDB := FEdSMeterOffset.Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.OnCalAnyChange(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if FLoading then Exit;
|
||||||
|
// Смена модели детектора подставляет модельные дефолты датчика тока
|
||||||
|
// (Thetis GetDefaultVoltCalibration): ANAN-7000DLE/Anvelina Pro 3 — 340/88,
|
||||||
|
// остальные — 360/120. Правится руками после, если датчик нестандартный.
|
||||||
|
if Sender = FCmbPwrDetModel then
|
||||||
|
begin
|
||||||
|
FLoading := True;
|
||||||
|
try
|
||||||
|
if FCmbPwrDetModel.ItemIndex = 2 then
|
||||||
|
begin
|
||||||
|
FEdAmpVoff.Value := 340.0;
|
||||||
|
FEdAmpSens.Value := 88.0;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
FEdAmpVoff.Value := 360.0;
|
||||||
|
FEdAmpSens.Value := 120.0;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
FLoading := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
CollectCalFromUI;
|
||||||
|
if Assigned(FOnCalibrationChange) then
|
||||||
|
FOnCalibrationChange(FCalConfig);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.LoadCalibration(const C: TCalibration);
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
FLoading := True;
|
||||||
|
try
|
||||||
|
FCalConfig := C;
|
||||||
|
FCmbPwrDetModel.ItemIndex := EnsureRange(C.PwrDetModel, 0, 3);
|
||||||
|
for i := 0 to 10 do
|
||||||
|
FEdPADetCal[i].Value := C.PADetCal[i];
|
||||||
|
FEdSupplyVoltCal.Value := C.SupplyVoltCal;
|
||||||
|
FEdAmpVoff.Value := C.AmpVoffMV;
|
||||||
|
FEdAmpSens.Value := C.AmpSensMVA;
|
||||||
|
FEdSMeterOffset.Value := C.SMeterOffsetDB;
|
||||||
|
finally
|
||||||
|
FLoading := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TSettingsForm.BuildAdvancedTab;
|
procedure TSettingsForm.BuildAdvancedTab;
|
||||||
const
|
const
|
||||||
MARGIN = 22;
|
MARGIN = 22;
|
||||||
|
|||||||
+6
-2
@@ -451,6 +451,7 @@ type
|
|||||||
FTXMode: Integer; // may differ from RX when a slice is TX source
|
FTXMode: Integer; // may differ from RX when a slice is TX source
|
||||||
FRawFMDeviation: Double;
|
FRawFMDeviation: Double;
|
||||||
FSMeter: Double;
|
FSMeter: Double;
|
||||||
|
FSMeterCalDB: Double; // калибровочный офсет S-метра, дБ (вкладка Calibration)
|
||||||
FFilterLow: Integer;
|
FFilterLow: Integer;
|
||||||
FFilterHigh: Integer;
|
FFilterHigh: Integer;
|
||||||
FAGCMode: TWDSPAGCMode;
|
FAGCMode: TWDSPAGCMode;
|
||||||
@@ -743,6 +744,8 @@ type
|
|||||||
property SNBEnabled: Boolean read FSNBEnabled;
|
property SNBEnabled: Boolean read FSNBEnabled;
|
||||||
property LastError: string read FLastError;
|
property LastError: string read FLastError;
|
||||||
property SMeter: Double read FSMeter;
|
property SMeter: Double read FSMeter;
|
||||||
|
// Офсет S-метра из вкладки Calibration; применяется в GetSMeterDBm/GetSliceSMeter
|
||||||
|
property SMeterCalDB: Double read FSMeterCalDB write FSMeterCalDB;
|
||||||
property FFTSize: Integer read FFFTSize;
|
property FFTSize: Integer read FFFTSize;
|
||||||
property WindowType: Integer read FWindowType;
|
property WindowType: Integer read FWindowType;
|
||||||
property SpecDetector: Integer read FSpecDetector;
|
property SpecDetector: Integer read FSpecDetector;
|
||||||
@@ -1245,6 +1248,7 @@ begin
|
|||||||
FAGCTop := -90.0;
|
FAGCTop := -90.0;
|
||||||
FVolume := 0.7;
|
FVolume := 0.7;
|
||||||
FSMeter := -130.0;
|
FSMeter := -130.0;
|
||||||
|
FSMeterCalDB := 0.0;
|
||||||
FNRMode := NR_MODE_OFF;
|
FNRMode := NR_MODE_OFF;
|
||||||
FNBMode := NB_MODE_OFF;
|
FNBMode := NB_MODE_OFF;
|
||||||
FSNBEnabled := False;
|
FSNBEnabled := False;
|
||||||
@@ -2827,7 +2831,7 @@ begin
|
|||||||
FSliceLock.Enter;
|
FSliceLock.Enter;
|
||||||
try
|
try
|
||||||
idx := FindSliceIdx(Id);
|
idx := FindSliceIdx(Id);
|
||||||
if idx >= 0 then Result := FSlices[idx].SMeter;
|
if idx >= 0 then Result := FSlices[idx].SMeter + FSMeterCalDB;
|
||||||
finally
|
finally
|
||||||
FSliceLock.Leave;
|
FSliceLock.Leave;
|
||||||
end;
|
end;
|
||||||
@@ -3880,7 +3884,7 @@ function TWDSPEngine.GetSMeterDBm: Double;
|
|||||||
begin
|
begin
|
||||||
// НЕ вызываем GetRXAMeter здесь повторно — значение уже обновлено
|
// НЕ вызываем GetRXAMeter здесь повторно — значение уже обновлено
|
||||||
// в DSP-колбэке. Повторный вызов сбрасывает аккумулятор WDSP → 0.
|
// в DSP-колбэке. Повторный вызов сбрасывает аккумулятор WDSP → 0.
|
||||||
Result := FSMeter;
|
Result := FSMeter + FSMeterCalDB;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user