mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
Add VHF XVTR PA calibration; fix TX drive formula and stale FDriveLevel
PA calibration per band (HF and VHF): - Add VHFBandCal[0..7] in TGlobalSettings (saved as vhf_cal_0..7 in JSON) - Default HF cal updated to Thetis HERMES defaults (38.8-41.3 dB per band) - Default VHF cal = 56.2 dB (Thetis HERMES XVTR default) - SettingsForm: second group in PA tab "VHF Calibration (Transverters)" with 8 spinboxes VHF0..VHF7; labels dynamically show XVTR slot ButtonText when LoadXvtrSettings is called (e.g. "VHF0 (2m):") TX drive formula (CalcDriveByte) rewritten to match Thetis exactly: drive_byte = min(sqrt(10^((10*log10(pwr_W*1000) - Cal)/10) * 0.05) / 0.8, 1.0) * 1.02 * 255 Cal = PA gain in dB (not a percentage); matches Thetis verified output (Cal=63.1 + slider=100% + PAMax=100W → drive_byte=16, verified against Thetis) Old formula Pos/100*Cal/100*255 was wrong: treated dB gain as linear percent Fix stale FDriveLevel not updated on band/XVTR switch: - Startup: FCurrentBand must be set before CalcDriveByte (was reversed) - RestoreBand: recalculate FDriveLevel before ApplyVfoA network update - ActivateXvtrBand: recalculate after FCurrentXvtr changes - ApplyVfoA: recalculate when VFO crosses HF band boundary - FreqDispBChanged: same for VFO-B band changes - ApplyMOX(True): safety recalculate at PTT engage time Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+68
-33
@@ -209,6 +209,7 @@ type
|
||||
FDriveLevel: Byte;
|
||||
FPAMaxPower: Double;
|
||||
FPABandCal: array[0..BAND_COUNT-1] of Double; // калибровка 38.8..100.0 на диапазон
|
||||
FVHFBandCal: array[0..CFG_XVTR_COUNT-1] of Double; // калибровка VHF/XVTR слотов
|
||||
FRunning: Boolean;
|
||||
FTransmitting: Boolean;
|
||||
FSplitTxB: Boolean;
|
||||
@@ -658,6 +659,7 @@ type
|
||||
procedure ApplyMOX(Active: Boolean);
|
||||
procedure OnTXIQReady(const Buf: array of Double; Count: Integer);
|
||||
procedure OnPASettingsChange(MaxPower: Double; const BandCal: array of Double);
|
||||
procedure OnVHFCalSettingsChange(const VHFCal: array of Double);
|
||||
procedure OnTXSettingsChange(const T: TTXSettings);
|
||||
procedure BtnAGCModeClick(Sender: TObject);
|
||||
procedure TrkAGCChange(Sender: TObject);
|
||||
@@ -947,6 +949,8 @@ begin
|
||||
Result.PAMaxPower := FPAMaxPower;
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
Result.PABandCal[i] := FPABandCal[i];
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
Result.VHFBandCal[i] := FVHFBandCal[i];
|
||||
Result.ActiveVfo := FActiveVfo;
|
||||
Result.NRMode := BtnNR.Tag;
|
||||
Result.NBMode := BtnNB.Tag;
|
||||
@@ -1118,6 +1122,8 @@ begin
|
||||
FVfoB := B.VfoB;
|
||||
FreqDispB.Frequency := Round(FVfoB);
|
||||
|
||||
// Пересчитываем drive byte для нового диапазона перед отправкой в сеть
|
||||
FDriveLevel := CalcDriveByte;
|
||||
// ApplyVfoA обновит сеть, FreqDisp, shift, перерисует
|
||||
ApplyVfoA(Round(B.VfoA));
|
||||
end;
|
||||
@@ -1157,7 +1163,8 @@ begin
|
||||
FSpecDrag := False;
|
||||
FDriveLevel := 0;
|
||||
FPAMaxPower := 100.0;
|
||||
for i := 0 to BAND_COUNT - 1 do FPABandCal[i] := 100.0;
|
||||
for i := 0 to BAND_COUNT - 1 do FPABandCal[i] := CFG_PA_BAND_CAL_DEFAULT[i];
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do FVHFBandCal[i] := CFG_VHF_CAL_DEFAULT;
|
||||
FRunning := False;
|
||||
FTransmitting := False;
|
||||
FSplitTxB := False;
|
||||
@@ -3731,10 +3738,12 @@ begin
|
||||
FPAMaxPower := G_Settings.PAMaxPower;
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
FPABandCal[i] := G_Settings.PABandCal[i];
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
FVHFBandCal[i] := G_Settings.VHFBandCal[i];
|
||||
// Slider position (0..100) and calibrated drive byte
|
||||
TrkDrive.Position := EnsureRange(G_Settings.DriveLevel, 0, 100);
|
||||
FDriveLevel := CalcDriveByte;
|
||||
FCurrentBand := G_Settings.LastBand;
|
||||
FDriveLevel := CalcDriveByte;
|
||||
// SampleRate — глобальный, загружаем до RestoreBand
|
||||
if G_Settings.SampleRate > 0 then
|
||||
begin
|
||||
@@ -4098,6 +4107,7 @@ begin
|
||||
StyleButton(BtnBand[FCurrentBand], False);
|
||||
FCurrentBand := BandIdx;
|
||||
StyleButton(BtnBand[FCurrentBand], True);
|
||||
FDriveLevel := CalcDriveByte;
|
||||
end;
|
||||
FSpecView.InvalidateRulerCache;
|
||||
SyncSpecViewFreq;
|
||||
@@ -4297,6 +4307,7 @@ begin
|
||||
StyleButton(BtnBand[FCurrentBand], False);
|
||||
FCurrentBand := BandIdx;
|
||||
StyleButton(BtnBand[FCurrentBand], True);
|
||||
FDriveLevel := CalcDriveByte;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -5002,6 +5013,7 @@ begin
|
||||
Exit;
|
||||
WasTransmitting := FTransmitting;
|
||||
FTransmitting := Active;
|
||||
if Active then FDriveLevel := CalcDriveByte;
|
||||
FDUCPendingCount := 0;
|
||||
if FTransmitting then
|
||||
begin
|
||||
@@ -5907,31 +5919,33 @@ begin
|
||||
end;
|
||||
|
||||
function TMainForm.CalcDriveByte: Byte;
|
||||
// Thetis-compatible formula:
|
||||
// drive_byte = int( min(target_volts/0.8, 1.0) * 1.02 * 255 )
|
||||
// where:
|
||||
// target_volts = sqrt( 10^((target_dBm)/10) * 0.05 ) [E=sqrt(P*R), R=50, P in mW]
|
||||
// target_dBm = 10*log10(pwr_W * 1000) - Cal
|
||||
// pwr_W = Pos/100 * FPAMaxPower
|
||||
// Cal = per-band PA gain in dB (HF: ~38.8-41.3, VHF default: ~56.2)
|
||||
var
|
||||
Cal: Double;
|
||||
Pos: Integer;
|
||||
XvtrActive: Boolean;
|
||||
Cal: Double;
|
||||
Pos: Integer;
|
||||
XvtrActive: Boolean;
|
||||
pwr_W: Double;
|
||||
target_dBm: Double;
|
||||
target_volts: Double;
|
||||
audio_vol: Double;
|
||||
begin
|
||||
if (FCurrentBand >= 0) and (FCurrentBand < BAND_COUNT) and (FPABandCal[FCurrentBand] > 0.0) then
|
||||
Cal := FPABandCal[FCurrentBand]
|
||||
else
|
||||
Cal := 100.0;
|
||||
|
||||
XvtrActive := (FCurrentXvtr >= 0) and (FCurrentXvtr < CFG_XVTR_COUNT) and
|
||||
FXvtrSettings.Entries[FCurrentXvtr].Enabled;
|
||||
|
||||
// Pos — итоговый процент драйва 0..100 ДО per-band калибровки.
|
||||
//
|
||||
// Логика (как в Thetis):
|
||||
// • Регулярный TX:
|
||||
// HF → Pos = TrkDrive.Position
|
||||
// XVTR → Pos = TrkDrive.Position × (XVTR.TXPower / 100)
|
||||
// (TXPower работает как верхний предел драйва на трансверторе)
|
||||
// • TUN (кнопка):
|
||||
// HF → Pos = TUNLevel (TTXSettings.TUNLevel)
|
||||
// XVTR, UseXVTRTunePower=False → Pos = TUNLevel (поведение как HF)
|
||||
// XVTR, UseXVTRTunePower=True → Pos = XVTR.TXPower
|
||||
// (галка ЗАМЕНЯЕТ TUNLevel на XVTR power — Thetis-семантика)
|
||||
if XvtrActive then
|
||||
Cal := FVHFBandCal[FCurrentXvtr]
|
||||
else if (FCurrentBand >= 0) and (FCurrentBand < BAND_COUNT) then
|
||||
Cal := FPABandCal[FCurrentBand]
|
||||
else
|
||||
Cal := 38.8;
|
||||
|
||||
// Pos — итоговый процент драйва 0..100
|
||||
if FTuning then
|
||||
begin
|
||||
if XvtrActive and FXvtrSettings.UseXVTRTunePower then
|
||||
@@ -5942,13 +5956,18 @@ begin
|
||||
else
|
||||
begin
|
||||
if XvtrActive then
|
||||
Pos := Round(TrkDrive.Position
|
||||
* FXvtrSettings.Entries[FCurrentXvtr].TXPower / 100.0)
|
||||
Pos := Round(TrkDrive.Position * FXvtrSettings.Entries[FCurrentXvtr].TXPower / 100.0)
|
||||
else
|
||||
Pos := TrkDrive.Position;
|
||||
end;
|
||||
|
||||
Result := Round(Pos / 100.0 * Cal / 100.0 * 255.0);
|
||||
if Pos <= 0 then begin Result := 0; Exit; end;
|
||||
|
||||
pwr_W := Pos / 100.0 * FPAMaxPower;
|
||||
target_dBm := 10.0 * Log10(pwr_W * 1000.0) - Cal;
|
||||
target_volts := Sqrt(Power(10.0, target_dBm * 0.1) * 0.05);
|
||||
audio_vol := Min(target_volts / 0.8, 1.0);
|
||||
Result := Min(Round(audio_vol * 1.02 * 255.0), 255);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnTXIQReady(const Buf: array of Double; Count: Integer);
|
||||
@@ -6002,6 +6021,21 @@ begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FDriveLevel, FTransmitting, True, True);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnVHFCalSettingsChange(const VHFCal: array of Double);
|
||||
var i: Integer;
|
||||
begin
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
begin
|
||||
if i <= High(VHFCal) then
|
||||
FVHFBandCal[i] := VHFCal[i]
|
||||
else
|
||||
FVHFBandCal[i] := CFG_VHF_CAL_DEFAULT;
|
||||
end;
|
||||
FDriveLevel := CalcDriveByte;
|
||||
if FNetwork.Connected and FNetwork.Running then
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FDriveLevel, FTransmitting, True, True);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnTXSettingsChange(const T: TTXSettings);
|
||||
// Любая правка в Settings→Transmit летит сюда: обновляем FTXSettings,
|
||||
// перешиваем WDSP TX-цепь и (если поменялись mic-биты) перепосылаем DUC Specific.
|
||||
@@ -6097,11 +6131,12 @@ begin
|
||||
end;
|
||||
|
||||
// Передаёт XVTR-режим в сетевой слой. Вызывается при смене XVTR или его
|
||||
// настроек. Управляет XVTR Enable bit, DDC IN routing, T/R relay suppression.
|
||||
// настроек. Управляет XVTR Enable bit, T/R relay suppression.
|
||||
// XVTR DDC In (Alex bits 8+11) управляется через галочку XVTR в Antenna/Alex
|
||||
// tab для IF-диапазона (Alex.RxOnly[IF_band]=3) — аналогично Thetis.
|
||||
procedure TMainForm.ApplyXvtrToNetwork;
|
||||
var
|
||||
En: Boolean;
|
||||
UseDDCIn: Boolean;
|
||||
DisablePA: Boolean;
|
||||
RxAnt: Byte;
|
||||
begin
|
||||
@@ -6109,16 +6144,15 @@ begin
|
||||
FXvtrSettings.Entries[FCurrentXvtr].Enabled then
|
||||
begin
|
||||
En := True;
|
||||
UseDDCIn := True;
|
||||
DisablePA := FXvtrSettings.Entries[FCurrentXvtr].DisablePA;
|
||||
RxAnt := FXvtrSettings.Entries[FCurrentXvtr].RXAntenna;
|
||||
end
|
||||
else
|
||||
begin
|
||||
En := False; UseDDCIn := False; DisablePA := False; RxAnt := 0;
|
||||
En := False; DisablePA := False; RxAnt := 0;
|
||||
end;
|
||||
if FNetwork.Connected then
|
||||
FNetwork.SetXvtrMode(En, UseDDCIn, DisablePA, RxAnt);
|
||||
FNetwork.SetXvtrMode(En, DisablePA, RxAnt);
|
||||
end;
|
||||
|
||||
// Перестраивает динамические XVTR-кнопки в PanelBands. Удаляет старые,
|
||||
@@ -6289,9 +6323,8 @@ begin
|
||||
FCenterFreq := Vis;
|
||||
FreqDispA.Frequency := Round(FVfoA);
|
||||
if FWDSPReady then FDSPEngine.SetShift(0.0);
|
||||
// Применяем драйв (если есть кастомный TX power для этого XVTR)
|
||||
// Сделано на стороне ActiveTXFreqHz / TX path — здесь только обновляем дисплей.
|
||||
// Отправляем в сеть с трансляцией
|
||||
// Пересчитываем drive byte для нового XVTR слота и отправляем в сеть
|
||||
FDriveLevel := CalcDriveByte;
|
||||
if FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz),
|
||||
@@ -6683,6 +6716,7 @@ begin
|
||||
SF.OnVisibilityChange := ApplyVisibility;
|
||||
SF.OnFPSChange := ApplyFPS;
|
||||
SF.OnPAChange := OnPASettingsChange;
|
||||
SF.OnVHFCalChange := OnVHFCalSettingsChange;
|
||||
SF.OnCATChange := OnCATSettingsChange;
|
||||
SF.OnThemeChange := SetLightTheme;
|
||||
SF.OnFreqMhzDigitsChange := ApplyFreqMhzDigits;
|
||||
@@ -6699,6 +6733,7 @@ begin
|
||||
IfThen(FNetwork.Connected, FNetwork.Device.BoardType, FPendingBoardType));
|
||||
SF.LoadXvtrSettings(FXvtrSettings);
|
||||
SF.LoadPASettings(FPAMaxPower, FPABandCal);
|
||||
SF.LoadVHFCalSettings(FVHFBandCal);
|
||||
SF.LoadCATSettings(
|
||||
FCATLastGlobal.CATSerialEnabled,
|
||||
FCATLastGlobal.CATSerialPort,
|
||||
|
||||
Reference in New Issue
Block a user