mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feature(calibration): частотная калибровка опорника (ppm) + TO Error в слоте QO-100
Пункт 3 плана. Ручные подгонки остаются: ppm работает ПОД ними, а не вместо — уход тракта где-то больше, где-то меньше. - Settings: TCalibration.FreqCalPPM (JSON cal_freq_ppm, +-100 ppm, per-device). Смысл знака: «опора выше номинала на N ppm», положительное значение поднимает показываемые частоты. - RadioBackend: виртуальный SetFreqCalPPM. - HPSDRNetwork: CalFreq(Hz) = Hz / (1 + ppm) в частотном слове — все четыре точки (главный DDC, DDC панов, feedback-DDC PureSignal, DUC передачи). Тактовый генератор радио менять нечем, поэтому правим запрос. - PlutoBackend: штатный xo_correction в ad9361-phy (device-attr биндинги добавлены в IIOBindings). Номинал XO снимается при коннекте с округлением до 100 кГц, чтобы наша же коррекция с прошлой сессии не накапливалась; после записи LO переписываются, иначе правка ждала бы следующей перестройки. Одна цифра лечит RX и TX на всех диапазонах. - QO-100: TXvtrEntry.TxLOError (JSON tx_lo_error) + поле «TO Error (Hz)» на странице QO-100. Сам транспондер-offset держим стандартным (8089.5 МГц), уход тракта правим поправкой; ActiveTXFreqHz учитывает её, подсказка внизу страницы считает TX-частоту уже с ней. - UI-фиксы вкладки Calibration: длинные подсказки переведены на несколько строк (WordWrap — TLabel по умолчанию режется по краю группы), заголовки таблиц укорочены и не заезжают под кнопку «= S-meter»; RelayoutCalTab сбрасывает прокрутку страницы перед перекладкой (SetBounds в TScrollBox задаёт координаты относительно прокрученного клиента). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,8 @@ type
|
||||
FTXFreq: Double;
|
||||
FSampleRate: Integer;
|
||||
FTransmitting: Boolean;
|
||||
FFreqCalPPM: Double; // частотная калибровка опорника, ppm (xo_correction)
|
||||
FXoNomHz: Int64; // номинал XO, Гц (снимается при коннекте, 0 = не знаем)
|
||||
FGainMode: string; // gain_control_mode (manual/slow_attack/...)
|
||||
FGainDb: Integer; // hardwaregain при ручном режиме
|
||||
|
||||
@@ -118,6 +120,7 @@ type
|
||||
GainDb: Double): Boolean;
|
||||
|
||||
// Низкоуровневые помощники применения атрибутов к ad9361-phy
|
||||
procedure ApplyFreqCal;
|
||||
procedure ApplyRxLO(Hz: Double);
|
||||
procedure ApplyTxLO(Hz: Double);
|
||||
procedure SetTxLOPowerdown(PoweredOn: Boolean); // TX LO вкл/выкл (по PTT)
|
||||
@@ -177,6 +180,7 @@ type
|
||||
procedure ClearDUCIQQueue; override;
|
||||
function ReadTelemetry(out TempC, SocTempC, RSSIdB: Double): Boolean; override;
|
||||
function RxGainNowDb: Double; override;
|
||||
procedure SetFreqCalPPM(P: Double); override;
|
||||
end;
|
||||
|
||||
// Стабильный 6-байтовый ключ (synthetic MAC) из serial/URI — для per-device
|
||||
@@ -410,6 +414,7 @@ begin
|
||||
FSampleRate := 1536000;
|
||||
FGainMode := 'slow_attack'; // аппаратный AGC по умолчанию
|
||||
FGainDb := 40;
|
||||
FFreqCalPPM := 0.0; FXoNomHz := 0;
|
||||
FRxBuf := nil; FRxChI := nil; FRxChQ := nil; FRxThread := nil;
|
||||
FStreaming := False; FRxSeq := 0;
|
||||
FTxBuf := nil; FTxChI := nil; FTxChQ := nil; FTxThread := nil;
|
||||
@@ -635,6 +640,7 @@ begin
|
||||
ApplyTxMux;
|
||||
ApplyGain(True);
|
||||
ApplyTxAtten; // TX заглушён (макс. аттенюация) до явного PTT
|
||||
ApplyFreqCal; // частотная калибровка опорника (xo_correction)
|
||||
StartTelemetry;
|
||||
Result := True;
|
||||
end;
|
||||
@@ -656,6 +662,40 @@ end;
|
||||
|
||||
// ---- Применение атрибутов к ad9361-phy ----
|
||||
|
||||
procedure TPlutoBackend.ApplyFreqCal;
|
||||
// xo_correction — частота опорного XO в Гц, какой её считает драйвер. Правка
|
||||
// разъезжается на ВСЁ: RX LO, TX LO, sample rate — один параметр вместо
|
||||
// подгонки offset'ов по трансвертерам.
|
||||
// Номинал снимаем округлением прочитанного до 100 кГц: в железе могла
|
||||
// остаться наша же коррекция с прошлой сессии (она сбрасывается только по
|
||||
// перезагрузке платы), а ±100 ppm от 40 МГц — это ±4 кГц, округление их
|
||||
// съедает и компаундинга не будет.
|
||||
var
|
||||
V: clonglong;
|
||||
begin
|
||||
if (FPhy = nil) or (not IIOLoaded)
|
||||
or (not Assigned(iio_device_attr_read_longlong))
|
||||
or (not Assigned(iio_device_attr_write_longlong)) then Exit;
|
||||
if FXoNomHz = 0 then
|
||||
begin
|
||||
if iio_device_attr_read_longlong(FPhy, 'xo_correction', V) <> 0 then Exit;
|
||||
if V <= 0 then Exit;
|
||||
FXoNomHz := (Int64(V) + 50000) div 100000 * 100000;
|
||||
end;
|
||||
iio_device_attr_write_longlong(FPhy, 'xo_correction',
|
||||
Round(FXoNomHz * (1.0 + FFreqCalPPM * 1.0E-6)));
|
||||
// Синтезаторы считают делители в момент записи частоты — переписываем LO,
|
||||
// иначе правка подхватится только на следующей перестройке.
|
||||
if FRXFreq > 0 then ApplyRxLO(FRXFreq);
|
||||
if FTXFreq > 0 then ApplyTxLO(FTXFreq);
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.SetFreqCalPPM(P: Double);
|
||||
begin
|
||||
FFreqCalPPM := P;
|
||||
ApplyFreqCal;
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.ApplyRxLO(Hz: Double);
|
||||
var Ch: Piio_channel;
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user