mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
feat(pluto): выбор антенного разъёма RX/TX на диапазон (LibreSDR/AntSDR)
Второй разъём у AD936x-плат — это второй КАНАЛ чипа (не порт B первого), и в 1R1T-образе он выбирается мультиплексором AD9361: debug-параметры adi,1rx-1tx-mode-use-rx-num/-tx-num + биты RX1/RX2, TX1/TX2 регистров 0x3/0x2 (как switch_rfinput.sh/switch_rfoutput.sh прошивки). rf_port_select тут ни при чём — прошивка его к тому же блокирует. - IIOBindings: debug-атрибуты + прямой доступ к регистрам. - PlutoBackend: детект второго разъёма (2R2T-каналы либо мультиплексор + модель платы), SetAntPorts(RxPort,TxPort), ApplyRxMux/ApplyTxMux; Caps.HasAntSwitch. ⚠ApplyGain(Force): драйвер пропускает запись gain_control_mode, если считает режим неизменным, — а у нового канала AGC-регистры не запрограммированы (в железе крутится auto-AGC, hardwaregain отвергается EOPNOTSUPP). Форсируем перепрог через другой режим после смены разъёма и после ad9361_set_bb_rate. - RadioBackend: TBackendCaps.HasAntSwitch + virtual SetAntPorts (HPSDR — no-op, там антеннами рулит Alex). - Settings: секция ant936x (разъём RX/TX на диапазон и на слот трансвертера); hw-AGC (режим + manual gain) переехал в TBandSettings/TXvtrEntry — теперь он свой на каждом диапазоне и трансвертере. - RadioController: FAnt936x + ApplyAntPorts (RestoreBand/ActivateXvtr/ StartRunning); ApplyBandDSP восстанавливает hw-AGC диапазона. - SettingsForm: вкладка Antenna — Alex-таблица (openHPSDR) либо таблица разъёмов AD936x по диапазонам/трансвертерам. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+226
-18
@@ -58,6 +58,24 @@ type
|
||||
FRxThread: TThread;
|
||||
FStreaming: Boolean;
|
||||
FRxSeq: LongWord;
|
||||
// Второй антенный вход (RX2). Два разных механизма — какой доступен, зависит
|
||||
// от прошивки/FPGA-образа платы:
|
||||
// • 2R2T-образ: второй канал инстанцирован, в cf-ad9361-lpc есть voltage2/3 →
|
||||
// выбор = пара стрим-каналов (FRx2Avail). Задел на второй пан (этап 3.6).
|
||||
// • 1R1T-образ (напр. LibreSDR/tezuka): второй канал в FPGA не разведён, но
|
||||
// внутри AD9361 есть мультиплексор — какой физический разъём кормит
|
||||
// единственный тракт. Выбор = debug-атрибуты 'adi,1rx-1tx-mode-use-rx-num'/
|
||||
// '-tx-num' (1|2) + 'initialize'=1 (драйвер заново прогоняет ad9361_setup
|
||||
// из pdata). ⚠Сырые записи битов RX1/RX2 в регистры 0x2/0x3 (как в
|
||||
// switch_rfinput.sh прошивки) НЕ годятся: тракт переключается, а учёт
|
||||
// драйвера (gain_control_mode/agc_mode на канал) остаётся от старого —
|
||||
// hardwaregain потом отвергается («Rx gain can be set in MGC mode only»),
|
||||
// а в железе продолжает крутиться AGC. rf_port_select тут ни при чём:
|
||||
// RX2-разъём разведён на второй КАНАЛ, а не на порт B первого.
|
||||
FRx2Avail: Boolean; // 2R2T: в cf-ad9361-lpc есть voltage2/voltage3
|
||||
FRxMuxAvail: Boolean; // 1R1T: доступен RX/TX-мультиплексор AD9361
|
||||
FRxChanSel: Byte; // 0 = RX1, 1 = RX2
|
||||
FTxChanSel: Byte; // 0 = TX1, 1 = TX2
|
||||
|
||||
// TX-стрим (cf-ad9361-dds-core-lpc): I/Q output-каналы, буфер, поток push.
|
||||
// Full-duplex: стрим живёт всё время Run; на приёме гоним нули (мощности нет
|
||||
@@ -103,8 +121,19 @@ type
|
||||
procedure SetTxLOPowerdown(PoweredOn: Boolean); // TX LO вкл/выкл (по PTT)
|
||||
procedure ApplySampleRate(Hz: Integer);
|
||||
procedure BuildTxResampler; // (пере)строить полифазные коэффициенты под FTxRatio
|
||||
procedure ApplyGain;
|
||||
// Force=True — перед записью режима пишем ДРУГОЙ режим: драйвер пропускает
|
||||
// перепрог AGC-регистров, если считает режим неизменным, а после смены
|
||||
// антенного мультиплексора настройки AGC нового канала ещё не запрограммированы
|
||||
// (в железе крутится auto-AGC, hardwaregain отвергается с EOPNOTSUPP).
|
||||
procedure ApplyGain(Force: Boolean = False);
|
||||
procedure ApplyTxAtten;
|
||||
// Имя phy-канала управления выбранным RX-трактом (гейн/RSSI/rf_bandwidth):
|
||||
// 'voltage0' = RX1, 'voltage1' = RX2 (только 2R2T; в 1R1T канал всегда один).
|
||||
function RxPhyChanName: string;
|
||||
// 1R1T: переключить мультиплексор AD9361 на выбранные разъёмы (см. FRxMuxAvail).
|
||||
// Дёшево: debug-атрибут (маппинг каналов у драйвера) + биты регистра 0x3/0x2.
|
||||
procedure ApplyRxMux;
|
||||
procedure ApplyTxMux;
|
||||
procedure StartRX;
|
||||
procedure StopRX;
|
||||
procedure StartTX;
|
||||
@@ -139,6 +168,7 @@ type
|
||||
Transmitting, PAEnabled, AlexEnabled: Boolean); override;
|
||||
procedure SendFullHP; override;
|
||||
procedure SendPTT(Active: Boolean); override;
|
||||
procedure SetAntPorts(RxPort, TxPort: Byte); override;
|
||||
procedure SetRxGain(ModeIdx: Integer; GainDb: Integer); override;
|
||||
procedure SetTxAtten(Db: Double); override;
|
||||
procedure SendDUCIQ(const IData, QData: array of Integer); override;
|
||||
@@ -171,6 +201,10 @@ const
|
||||
TX_FIR_CUT = 40000; // частота среза анти-имидж ФНЧ, Гц (покрывает SSB/NFM/AM/data)
|
||||
PLUTO_TX_OFF = -89.75; // макс. аттенюация AD9361 — TX практически выключен
|
||||
|
||||
// Детект второго антенного входа (тела ниже — нужны до Connect).
|
||||
function BoardHasSecondRxPort(const Model: string): Boolean; forward;
|
||||
function DebugAttrExists(Dev: Piio_device; const Attr: string): Boolean; forward;
|
||||
|
||||
type
|
||||
// RX-поток: блокирующий refill libiio → конверсия int16→24bit BE → OnDDCIQ.
|
||||
// Работает с private-полями TPlutoBackend (одна единица компиляции).
|
||||
@@ -432,11 +466,14 @@ begin
|
||||
Result.RatePresets[7] := 5760000;
|
||||
Result.MinFreqHz := PLUTO_MIN_HZ;
|
||||
Result.MaxFreqHz := PLUTO_MAX_HZ;
|
||||
// Один RX-тракт — только главный пан. (Pluto+ 2r2t = MaxPans 2 с общим
|
||||
// RX LO, IndependentPanFreq=False — этап 3.6.)
|
||||
// Стримим ровно один тракт ⇒ только главный пан. Одновременный приём с двух
|
||||
// антенн (второй пан на RX2, общий RX LO) — этап 3.6, нужен 2R2T-образ.
|
||||
Result.MaxPans := 1;
|
||||
Result.IndependentPanFreq := False;
|
||||
Result.NumADCs := 1;
|
||||
// Пан один ⇒ ADC-селектор панов не при чём: у AD936x «антенна» — это выбор
|
||||
// физических разъёмов (RX1/RX2, TX1/TX2) на диапазон, вкладка Antenna настроек.
|
||||
Result.NumADCs := 1;
|
||||
Result.HasAntSwitch := FRx2Avail or FRxMuxAvail;
|
||||
end;
|
||||
|
||||
function TPlutoBackend.ProbeURI(const AURI: string; out Dev: TRadioDevice): Boolean;
|
||||
@@ -536,6 +573,7 @@ end;
|
||||
function TPlutoBackend.Connect(const Dev: TRadioDevice): Boolean;
|
||||
var
|
||||
M: TBytes;
|
||||
PV: PAnsiChar;
|
||||
begin
|
||||
Result := False;
|
||||
if not IIOLoad then begin FLastError := 'libiio not available'; Exit; end;
|
||||
@@ -566,11 +604,33 @@ begin
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// 2R2T-образ — по НАЛИЧИЮ каналов второго тракта (клоны не отличить по строкам
|
||||
// описания). Управление RX2 (гейн/RSSI) — phy input voltage1, требуем и его.
|
||||
FRx2Avail := (FRxDev <> nil) and
|
||||
(iio_device_find_channel(FRxDev, 'voltage2', 0) <> nil) and
|
||||
(iio_device_find_channel(FRxDev, 'voltage3', 0) <> nil) and
|
||||
(iio_device_find_channel(FPhy, 'voltage1', 0) <> nil);
|
||||
// Модель берём из ЖИВОГО контекста: в записи устройства она могла приехать из
|
||||
// сохранёнки/фолбэка ProbeURI ('AD936x SDR') и не содержать имени платы.
|
||||
PV := iio_context_get_attr_value(FCtx, 'hw_model');
|
||||
if PV <> nil then FDevice.Model := string(PV);
|
||||
// 1R1T-образ: RX-мультиплексор AD9361. Гейт по МОДЕЛИ — у стокового Pluto
|
||||
// второй вход никуда не разведён (кремний двухканальный, а SMA один), так что
|
||||
// селектор там показывать нельзя: он дал бы «мёртвую» антенну.
|
||||
FRxMuxAvail := (not FRx2Avail) and BoardHasSecondRxPort(FDevice.Model) and
|
||||
Assigned(iio_device_debug_attr_write) and
|
||||
DebugAttrExists(FPhy, 'adi,1rx-1tx-mode-use-rx-num') and
|
||||
DebugAttrExists(FPhy, 'adi,1rx-1tx-mode-use-tx-num') and
|
||||
DebugAttrExists(FPhy, 'initialize');
|
||||
if not (FRx2Avail or FRxMuxAvail) then FRxChanSel := 0;
|
||||
|
||||
FConnected := True;
|
||||
// Приводим железо в известное состояние: валидный rate + усиление.
|
||||
// Приводим железо в известное состояние: валидный rate + вход + усиление.
|
||||
if FSampleRate < PLUTO_MIN_SR then FSampleRate := PLUTO_DEF_SR;
|
||||
ApplySampleRate(FSampleRate);
|
||||
ApplyGain;
|
||||
ApplyRxMux; // разъёмы могли остаться от прошлой сессии (состояние в чипе)
|
||||
ApplyTxMux;
|
||||
ApplyGain(True);
|
||||
ApplyTxAtten; // TX заглушён (макс. аттенюация) до явного PTT
|
||||
StartTelemetry;
|
||||
Result := True;
|
||||
@@ -651,8 +711,9 @@ begin
|
||||
iio_channel_attr_write_longlong(Ch, 'sampling_frequency', Hz);
|
||||
end;
|
||||
|
||||
// RF-полоса отдельно (set_bb_rate её не трогает).
|
||||
Ch := iio_device_find_channel(FPhy, 'voltage0', 0);
|
||||
// RF-полоса отдельно (set_bb_rate её не трогает) — на phy-канале выбранного
|
||||
// приёмного тракта (у 2R2T они независимы).
|
||||
Ch := iio_device_find_channel(FPhy, PAnsiChar(AnsiString(RxPhyChanName)), 0);
|
||||
if Ch <> nil then
|
||||
iio_channel_attr_write_longlong(Ch, 'rf_bandwidth', Hz);
|
||||
|
||||
@@ -698,12 +759,129 @@ begin
|
||||
for k := 0 to TX_FIR_TAPS - 1 do begin FTxDlI[k] := 0; FTxDlQ[k] := 0; end;
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.ApplyGain;
|
||||
var Ch: Piio_channel;
|
||||
function BoardHasSecondRxPort(const Model: string): Boolean;
|
||||
// Есть ли у платы физический разъём RX2. Кремний AD9361/AD9363 двухканальный на
|
||||
// ЛЮБОЙ плате, включая стоковый Pluto, — но у Pluto второй канал никуда не выведен,
|
||||
// поэтому переключать на него нельзя (антенны там нет). Список плат с двумя RX SMA.
|
||||
const
|
||||
BOARDS: array[0..4] of string = ('libresdr', 'antsdr', 'pluto+', 'plutoplus', 'e200');
|
||||
var
|
||||
M: string;
|
||||
i: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
M := LowerCase(StringReplace(Model, ' ', '', [rfReplaceAll]));
|
||||
for i := Low(BOARDS) to High(BOARDS) do
|
||||
if Pos(BOARDS[i], M) > 0 then Exit(True);
|
||||
end;
|
||||
|
||||
function DebugAttrExists(Dev: Piio_device; const Attr: string): Boolean;
|
||||
var Buf: array[0..63] of AnsiChar;
|
||||
begin
|
||||
Result := Assigned(iio_device_debug_attr_read) and (Dev <> nil) and
|
||||
(iio_device_debug_attr_read(Dev, PAnsiChar(AnsiString(Attr)),
|
||||
@Buf[0], SizeOf(Buf)) >= 0);
|
||||
end;
|
||||
|
||||
function TPlutoBackend.RxPhyChanName: string;
|
||||
// Управляющий phy-канал приёмного тракта: RX1 = input voltage0, RX2 = voltage1.
|
||||
// В 1R1T-образе канал ровно один (voltage0) — мультиплексор меняет вход ПОД ним,
|
||||
// поэтому гейн/RSSI остаются на voltage0.
|
||||
begin
|
||||
if (FRxChanSel = 1) and FRx2Avail then Result := 'voltage1'
|
||||
else Result := 'voltage0';
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.ApplyRxMux;
|
||||
// Приёмный разъём: debug-параметр (по нему драйвер маппит gain-атрибуты на канал)
|
||||
// + биты RX1/RX2 (маска 0xC0) регистра 0x003 — как switch_rfinput.sh прошивки.
|
||||
// ⚠Вызывающий обязан следом сделать ApplyGain(True): AGC-регистры нового канала
|
||||
// ещё не запрограммированы (см. ApplyGain).
|
||||
var
|
||||
Val: cuint32;
|
||||
RxNum: Integer;
|
||||
begin
|
||||
if not (FConnected and FRxMuxAvail and (FPhy <> nil)) then Exit;
|
||||
if FRxChanSel = 1 then RxNum := 2 else RxNum := 1;
|
||||
iio_device_debug_attr_write(FPhy, 'adi,1rx-1tx-mode-use-rx-num',
|
||||
PAnsiChar(AnsiString(IntToStr(RxNum))));
|
||||
Val := 0;
|
||||
if iio_device_reg_read(FPhy, $3, Val) <> 0 then Exit;
|
||||
Val := Val and $3F;
|
||||
if FRxChanSel = 1 then Val := Val or $80 // RX2
|
||||
else Val := Val or $40; // RX1
|
||||
iio_device_reg_write(FPhy, $3, Val);
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.ApplyTxMux;
|
||||
// То же для передачи (switch_rfoutput.sh): '-tx-num' + биты TX1/TX2 регистра 0x002.
|
||||
var
|
||||
Val: cuint32;
|
||||
TxNum: Integer;
|
||||
begin
|
||||
if not (FConnected and FRxMuxAvail and (FPhy <> nil)) then Exit;
|
||||
if FTxChanSel = 1 then TxNum := 2 else TxNum := 1;
|
||||
iio_device_debug_attr_write(FPhy, 'adi,1rx-1tx-mode-use-tx-num',
|
||||
PAnsiChar(AnsiString(IntToStr(TxNum))));
|
||||
Val := 0;
|
||||
if iio_device_reg_read(FPhy, $2, Val) <> 0 then Exit;
|
||||
Val := Val and $3F;
|
||||
if FTxChanSel = 1 then Val := Val or $80 // TX2
|
||||
else Val := Val or $40; // TX1
|
||||
iio_device_reg_write(FPhy, $2, Val);
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.SetAntPorts(RxPort, TxPort: Byte);
|
||||
// Выбор физических разъёмов: 0 = RX1/TX1, 1 = RX2/TX2. Зовётся контроллером при
|
||||
// смене диапазона/трансвертера (антенна — свойство диапазона, см. вкладку Antenna).
|
||||
var
|
||||
RxChanged, TxChanged: Boolean;
|
||||
begin
|
||||
if RxPort > 1 then RxPort := 0;
|
||||
if TxPort > 1 then TxPort := 0;
|
||||
if not (FRx2Avail or FRxMuxAvail) then Exit; // одна пара разъёмов — нечего выбирать
|
||||
RxChanged := RxPort <> FRxChanSel;
|
||||
TxChanged := TxPort <> FTxChanSel;
|
||||
if not (RxChanged or TxChanged) then Exit;
|
||||
FRxChanSel := RxPort;
|
||||
FTxChanSel := TxPort;
|
||||
if not FConnected then Exit;
|
||||
if FRx2Avail then
|
||||
begin
|
||||
// 2R2T-образ: разъём = ДРУГАЯ пара стрим-каналов ⇒ пересоздать буферы, затем
|
||||
// вернуть гейн/аттенюацию (у 2R2T они ещё и живут на phy-канале своего тракта).
|
||||
if RxChanged and FStreaming then begin StopRX; StartRX; end;
|
||||
if TxChanged and FTxStreaming then begin StopTX; StartTX; end;
|
||||
ApplyGain;
|
||||
ApplyTxAtten;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// 1R1T: мультиплексор внутри чипа — стримы/LO/rate не трогаем (дёшево).
|
||||
// Обе записи ДО восстановления усиления, и гейн — форсированный: у нового
|
||||
// канала AGC-регистры ещё не запрограммированы.
|
||||
if RxChanged then ApplyRxMux;
|
||||
if TxChanged then ApplyTxMux;
|
||||
ApplyGain(True);
|
||||
ApplyTxAtten;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.ApplyGain(Force: Boolean);
|
||||
var Ch: Piio_channel; Other: string;
|
||||
begin
|
||||
if FPhy = nil then Exit;
|
||||
Ch := iio_device_find_channel(FPhy, 'voltage0', 0); // RX gain — input voltage0
|
||||
// Усиление/режим AGC — поканальные: пишем в тракт выбранной антенны.
|
||||
Ch := iio_device_find_channel(FPhy, PAnsiChar(AnsiString(RxPhyChanName)), 0);
|
||||
if Ch = nil then Exit;
|
||||
if Force then
|
||||
begin
|
||||
// Прогоняем через другой режим — иначе драйвер сочтёт режим неизменным и не
|
||||
// перепрограммирует AGC-регистры канала (после смены мультиплексора там
|
||||
// остаётся auto-AGC, а hardwaregain отвергается: «Rx gain can be set in MGC only»).
|
||||
if FGainMode = 'manual' then Other := 'slow_attack' else Other := 'manual';
|
||||
iio_channel_attr_write(Ch, 'gain_control_mode', PAnsiChar(AnsiString(Other)));
|
||||
end;
|
||||
iio_channel_attr_write(Ch, 'gain_control_mode', PAnsiChar(AnsiString(FGainMode)));
|
||||
if FGainMode = 'manual' then
|
||||
iio_channel_attr_write_longlong(Ch, 'hardwaregain', FGainDb);
|
||||
@@ -721,10 +899,17 @@ begin
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.StartRX;
|
||||
var Base: Integer; ChI, ChQ: string;
|
||||
begin
|
||||
if FStreaming or not FConnected or (FRxDev = nil) then Exit;
|
||||
FRxChI := iio_device_find_channel(FRxDev, 'voltage0', 0);
|
||||
FRxChQ := iio_device_find_channel(FRxDev, 'voltage1', 0);
|
||||
// Пара стрим-каналов выбранного тракта: RX1 = voltage0/1, RX2 = voltage2/3.
|
||||
// Включаем ТОЛЬКО её — второй тракт не стримим (иначе вдвое трафик/CPU).
|
||||
Base := 0;
|
||||
if (FRxChanSel = 1) and FRx2Avail then Base := 2;
|
||||
ChI := 'voltage' + IntToStr(Base);
|
||||
ChQ := 'voltage' + IntToStr(Base + 1);
|
||||
FRxChI := iio_device_find_channel(FRxDev, PAnsiChar(AnsiString(ChI)), 0);
|
||||
FRxChQ := iio_device_find_channel(FRxDev, PAnsiChar(AnsiString(ChQ)), 0);
|
||||
if (FRxChI = nil) or (FRxChQ = nil) then
|
||||
begin FLastError := 'RX I/Q channels not found'; Exit; end;
|
||||
iio_channel_enable(FRxChI);
|
||||
@@ -753,13 +938,26 @@ begin
|
||||
iio_buffer_destroy(FRxBuf);
|
||||
FRxBuf := nil;
|
||||
end;
|
||||
// Снять enable с пары каналов: иначе после смены антенного входа (2R2T) в
|
||||
// новом буфере окажутся включёнными оба тракта — чередование I1,Q1,I2,Q2.
|
||||
if FRxChI <> nil then iio_channel_disable(FRxChI);
|
||||
if FRxChQ <> nil then iio_channel_disable(FRxChQ);
|
||||
FRxChI := nil;
|
||||
FRxChQ := nil;
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.StartTX;
|
||||
var Base: Integer; ChI, ChQ: string;
|
||||
begin
|
||||
if FTxStreaming or not FConnected or (FTxDev = nil) then Exit;
|
||||
FTxChI := iio_device_find_channel(FTxDev, 'voltage0', 1); // output I
|
||||
FTxChQ := iio_device_find_channel(FTxDev, 'voltage1', 1); // output Q
|
||||
// 2R2T-образ: пара выходных каналов выбранного разъёма (TX1 = voltage0/1,
|
||||
// TX2 = voltage2/3). В 1R1T пара всегда одна — разъём переключает ApplyTxMux.
|
||||
Base := 0;
|
||||
if (FTxChanSel = 1) and FRx2Avail then Base := 2;
|
||||
ChI := 'voltage' + IntToStr(Base);
|
||||
ChQ := 'voltage' + IntToStr(Base + 1);
|
||||
FTxChI := iio_device_find_channel(FTxDev, PAnsiChar(AnsiString(ChI)), 1); // output I
|
||||
FTxChQ := iio_device_find_channel(FTxDev, PAnsiChar(AnsiString(ChQ)), 1); // output Q
|
||||
if (FTxChI = nil) or (FTxChQ = nil) then
|
||||
begin FLastError := 'TX I/Q channels not found'; Exit; end;
|
||||
iio_channel_enable(FTxChI);
|
||||
@@ -805,11 +1003,20 @@ end;
|
||||
procedure TPlutoBackend.ConfigureDDCs(NumDDCs: Byte; SampleRate: Word;
|
||||
ADCSource: Byte; DitherEnabled, RandomEnabled: Boolean);
|
||||
begin
|
||||
// SampleRate приходит в ksps (как у HPSDR ConfigureDDCs). Pluto — в Гц.
|
||||
// ADCSource (АЦП-источник HPSDR) у AD936x смысла не имеет — антенные разъёмы
|
||||
// переключает SetAntPorts. SampleRate приходит в ksps (как у HPSDR), Pluto — в Гц.
|
||||
// Контроллер может прислать HPSDR-наследие (<520k) — клампим к дефолту.
|
||||
FSampleRate := Integer(SampleRate) * 1000;
|
||||
if FSampleRate < PLUTO_MIN_SR then FSampleRate := PLUTO_DEF_SR;
|
||||
if FConnected then ApplySampleRate(FSampleRate); // перезапустит RX при смене
|
||||
if not FConnected then Exit;
|
||||
ApplySampleRate(FSampleRate); // перезапустит RX при смене
|
||||
// ad9361_set_bb_rate перепрошивает FIR/BBPLL и сбрасывает усиление/AGC к
|
||||
// дефолтам — возвращаем разъёмы и форсированно гейн + TX-аттенюацию (иначе
|
||||
// в железе остаётся auto-AGC: высокая шумовая полка).
|
||||
ApplyRxMux;
|
||||
ApplyTxMux;
|
||||
ApplyGain(True);
|
||||
ApplyTxAtten;
|
||||
end;
|
||||
|
||||
procedure TPlutoBackend.SetRunAndFreq(Run: Boolean; DDC0FreqHz, DUCFreqHz: Double;
|
||||
@@ -990,9 +1197,10 @@ begin
|
||||
end;
|
||||
|
||||
// RSSI имеет смысл только при активном RX-стриме (иначе чтение даёт ошибку/мусор).
|
||||
// Читаем с phy-канала выбранного приёмного тракта (RX1/RX2 — независимы).
|
||||
if FStreaming then
|
||||
begin
|
||||
Ch := iio_device_find_channel(FPhy, 'voltage0', 0);
|
||||
Ch := iio_device_find_channel(FPhy, PAnsiChar(AnsiString(RxPhyChanName)), 0);
|
||||
if (Ch <> nil) and Assigned(iio_channel_attr_read_double)
|
||||
and (iio_channel_attr_read_double(Ch, 'rssi', d) = 0) then
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user