mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(puresignal): PureSignal-предыскажение для openHPSDR P2 + кнопки PS/2TON
Протокол (по pihpsdr new_protocol.c): на PTT при PS DDC0<-ADC0 (RX-feedback), DDC1<-ADC2 (TX DAC loopback), 192к/24бит, sync DDC1->DDC0 (byte 1363=0x02, interleaved в потоке DDC0), частоты DDC0/1 = TX в HP-пакете, Alex0 bit18 (мост обратной связи). Caps.HasPureSignal для плат с DDCBase=2 (Angelia/Orion/Orion2/Saturn); попутно NumADCs=2 для Angelia. WDSPEngine: PushPSFeedback (деинтерлив -> pscc(TXA, tx=DDC1, rx=DDC0) из сетевого потока), PS-параметры calcc в кэше с переприменением после пересоздания TXA; SetTXTwoTone (PostGen mode 1, 700/1900, 0.49+0.49, инверсия LSB/CWL/DIGL, мьют mic как TUN). RadioController: state machine команд (порт Thetis PSForm timer1) в PureSignalTick @10Гц + auto-attenuate (AttOnTX по уровню feedback, цель ~152, через DUC Specific); SetPSMox и перестройка feedback-DDC на T/R; маршрутизация DDC0 в pscc из OnDDCIQ; SetTwoTone с гейтами safety. UI: кнопки PS (ЛКМ = поповер настроек, ПКМ = быстрый toggle) и 2TON в блоке TX; поповер TPureSignalPopup (дочерний контрол, Wayland-safe): PS ON/OFF, CAL, RESET, 2TON, лампа/уровень feedback, cals, corr, GetPk/SetPk, MOX/CAL wait, PH#, Auto-atten, Relax ptol, Pin/Map/Stbl, Tint, тона двухтональника. Персист ps_* в tx-секции per-device. Не проверено на железе; Save/Restore коррекции не реализованы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+209
-2
@@ -375,6 +375,31 @@ type
|
||||
// PostGen стоит ПОСЛЕ ALC — голос из микрофона складывался бы с тоном
|
||||
// поверх полной шкалы (тон ~0.98) → клип 24-бит → сплэттер на спектре.
|
||||
FTXToneOn: Boolean;
|
||||
// 2TON активен (PostGen two-tone 700/1900 Гц для IMD/PureSignal-теста):
|
||||
// mic-вход глушится так же, как при TUN.
|
||||
FTXTTOn: Boolean;
|
||||
|
||||
// ---- PureSignal ----
|
||||
// FPSArmed: PS включён контроллером — PushPSFeedback подаёт feedback в
|
||||
// pscc (WDSP calcc). Пишется из UI-потока, читается в сетевом.
|
||||
FPSArmed: Boolean;
|
||||
// Деинтерливленные буферы feedback-пакета: interleaved I/Q пары каждого
|
||||
// потока (≤119 пар на поток при 238 парах в пакете; 256 пар с запасом).
|
||||
// Используются только сетевым потоком.
|
||||
FPSTxBuf: array[0..511] of Double; // DDC1 = TX DAC (чистый эталон)
|
||||
FPSRxBuf: array[0..511] of Double; // DDC0 = RX feedback (после PA)
|
||||
// Кэш PS-параметров: применяются в ApplyTXChainSettings (т.е. и после
|
||||
// каждого пересоздания TXA-канала в Open/ChangeSampleRate).
|
||||
FPSMoxDelay: Double;
|
||||
FPSLoopDelay: Double;
|
||||
FPSTXDelayNs: Integer;
|
||||
FPSRelaxPtol: Boolean;
|
||||
FPSPin: Boolean;
|
||||
FPSMap: Boolean;
|
||||
FPSStabilize: Boolean;
|
||||
FPSInts: Integer;
|
||||
FPSSpi: Integer;
|
||||
FPSHWPeak: Double;
|
||||
|
||||
// Кэшированные TX-параметры — применяются после OpenChannel(TXA) и при изменении.
|
||||
// Для Filter/Compressor/Leveler/ALC/PhaseRot/EQ/AM/FM/CTCSS.
|
||||
@@ -501,6 +526,7 @@ type
|
||||
procedure ProcessTXBlock; // вызывается из TTXDSPThread
|
||||
procedure FeedTXDisplaySample(const I, Q: Double);
|
||||
procedure ApplyTXChainSettings; // прокидывает все FTX* поля в WDSP
|
||||
procedure ApplyPSParams; // прокидывает FPS*-параметры в WDSP calcc
|
||||
procedure ApplyRXAnalyzerSettings; // применяет RX FFT/Window/Det/Avg к RX_DISP_ID
|
||||
procedure ApplyTXAnalyzerSettings; // применяет TX FFT/Window/Det/Avg к TX_DISP_ID
|
||||
|
||||
@@ -606,6 +632,29 @@ type
|
||||
// TUN: WDSP TXA PostGen — гонит чистый тон в DSP-цепь после всей
|
||||
// обработки. Mag — амплитуда в [0..1], Freq — Hz (audio).
|
||||
procedure SetTXTone(Enabled: Boolean; FreqHz, Mag: Double);
|
||||
// 2TON: PostGen two-tone (IMD-тест / калибровка PureSignal).
|
||||
// Тона Freq1/Freq2 Гц (для LSB/CWL/DIGL знак инвертируется), амплитуды
|
||||
// по 0.49 (сумма чуть ниже полной шкалы, как Thetis/pihpsdr).
|
||||
procedure SetTXTwoTone(Enabled: Boolean; Freq1, Freq2: Double);
|
||||
|
||||
// ---- PureSignal (WDSP calcc на TXA_CHAN) ----
|
||||
// Армирование подачи feedback в pscc (кнопка PS). Сама калибровка
|
||||
// управляется PSControl (state machine в контроллере).
|
||||
procedure SetPureSignalArmed(Armed: Boolean);
|
||||
// Feedback-пакет синхронизированного DDC0 (interleaved: пара DDC0 =
|
||||
// RX-feedback, пара DDC1 = TX DAC, 24-bit BE). Вызывается из СЕТЕВОГО
|
||||
// потока (как Thetis/pihpsdr — pscc потокобезопасен относительно
|
||||
// fexchange0 TX-потока). TotalPairs — всего IQ-пар в пакете (оба потока).
|
||||
procedure PushPSFeedback(const Buf: array of Byte; DataOffset: Integer;
|
||||
TotalPairs: Integer);
|
||||
// Тонкие врапперы WDSP PS API (все на TXA_CHAN, guard FInitialized):
|
||||
procedure PSControl(Reset, Mancal, Automode, Turnon: Integer);
|
||||
procedure PSSetMox(Mox: Boolean);
|
||||
procedure PSGetInfo(var Info: array of Integer); // 16 int
|
||||
function PSGetMaxTX: Double;
|
||||
procedure PSSetParams(MoxDelay, LoopDelay: Double; TXDelayNs: Integer;
|
||||
RelaxPtol: Boolean; Pin, Map, Stabilize: Boolean;
|
||||
Ints, Spi: Integer; HWPeak: Double);
|
||||
|
||||
// Расширенное TX управление (применяются мгновенно если канал открыт)
|
||||
procedure SetTXFilterFull(Low, High, NC, MP, Window: Integer);
|
||||
@@ -1197,7 +1246,20 @@ begin
|
||||
FTXMicTail := 0;
|
||||
FTXActive := False;
|
||||
FTXToneOn := False;
|
||||
FTXTTOn := False;
|
||||
FKeepRXDuringTX := False;
|
||||
// PureSignal — дефолты как Thetis PSForm / clsHardwareSpecific (P2)
|
||||
FPSArmed := False;
|
||||
FPSMoxDelay := 0.2; // с — задержка после MOX до сбора данных
|
||||
FPSLoopDelay := 0.0; // с — пауза между итерациями калибровки
|
||||
FPSTXDelayNs := 150; // нс — компенсация задержки TX-тракта
|
||||
FPSRelaxPtol := False;
|
||||
FPSPin := True;
|
||||
FPSMap := True;
|
||||
FPSStabilize := False;
|
||||
FPSInts := 16;
|
||||
FPSSpi := 256;
|
||||
FPSHWPeak := 0.2899; // протокол 2 (Saturn: 0.6121)
|
||||
FMonitorRXDuringTX := False;
|
||||
FPostTXMuteUntil := 0;
|
||||
FActiveDisplayID := RX_DISP_ID;
|
||||
@@ -1661,6 +1723,10 @@ var
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
|
||||
// PureSignal: снять подачу feedback ДО закрытия TXA — pscc зовётся из
|
||||
// сетевого потока напрямую и не должен попасть в закрываемый канал.
|
||||
FPSArmed := False;
|
||||
|
||||
// Останавливаем DSP поток перед закрытием WDSP каналов
|
||||
if Assigned(FDSPThread) then
|
||||
begin
|
||||
@@ -3139,6 +3205,35 @@ begin
|
||||
SetTXAPostGenRun(TXA_CHAN, 0);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetTXTwoTone(Enabled: Boolean; Freq1, Freq2: Double);
|
||||
// 2TON: двухтональник PostGen (mode 1) для IMD-теста и проверки PureSignal.
|
||||
// Амплитуды 0.49+0.49 — сумма чуть ниже полной шкалы (как Thetis/pihpsdr).
|
||||
var
|
||||
F1, F2: Double;
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
FTXTTOn := Enabled; // мьют mic-входа (см. ProcessTXBlock)
|
||||
if Enabled then
|
||||
begin
|
||||
// Для нижней боковой тона должны лежать НИЖЕ несущей.
|
||||
if (FMode = MODE_LSB) or (FMode = MODE_CWL) or (FMode = MODE_DIGL) then
|
||||
begin
|
||||
F1 := -Abs(Freq1);
|
||||
F2 := -Abs(Freq2);
|
||||
end
|
||||
else
|
||||
begin
|
||||
F1 := Abs(Freq1);
|
||||
F2 := Abs(Freq2);
|
||||
end;
|
||||
SetTXAPostGenMode(TXA_CHAN, 1); // 1 = two-tone
|
||||
SetTXAPostGenTTFreq(TXA_CHAN, F1, F2);
|
||||
SetTXAPostGenTTMag(TXA_CHAN, 0.49, 0.49);
|
||||
SetTXAPostGenRun(TXA_CHAN, 1);
|
||||
end else
|
||||
SetTXAPostGenRun(TXA_CHAN, 0);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetDisplaySourceTX(UseTX: Boolean);
|
||||
begin
|
||||
// Тот же сброс позиций, что и в SetTXRun — analyzer-буферы у обоих DispID
|
||||
@@ -3179,9 +3274,9 @@ begin
|
||||
tail := (tail + 1) and (TX_MIC_RING - 1);
|
||||
end;
|
||||
FTXMicTail := tail; // Атомарно обновляем tail (один writer — TX thread)
|
||||
// TUN: ринг потребляем (иначе поток зациклится на «есть полный блок»),
|
||||
// TUN/2TON: ринг потребляем (иначе поток зациклится на «есть полный блок»),
|
||||
// но в TXA подаём тишину — тон PostGen должен идти без голоса поверх.
|
||||
if FTXToneOn then
|
||||
if FTXToneOn or FTXTTOn then
|
||||
FillChar(FTXIn[0], FAudioBufSize * 2 * SizeOf(Double), 0);
|
||||
end else
|
||||
begin
|
||||
@@ -3335,6 +3430,118 @@ begin
|
||||
// CTCSS
|
||||
SetTXACTCSSFreq(TXA_CHAN, FTXCTCSSFreq);
|
||||
SetTXACTCSSRun(TXA_CHAN, Ord(FTXCTCSSOn));
|
||||
|
||||
// PureSignal (calcc живёт внутри TXA-канала — после пересоздания канала
|
||||
// все его параметры надо прокинуть заново)
|
||||
ApplyPSParams;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.ApplyPSParams;
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
// Feedback приходит с DDC0/1 @ 192 кС/с (фиксированный PS-rate протокола 2)
|
||||
SetPSFeedbackRate(TXA_CHAN, TX_SAMPLE_RATE);
|
||||
SetPSMoxDelay(TXA_CHAN, FPSMoxDelay);
|
||||
SetPSLoopDelay(TXA_CHAN, FPSLoopDelay);
|
||||
SetPSTXDelay(TXA_CHAN, FPSTXDelayNs * 1.0e-9);
|
||||
if FPSRelaxPtol then
|
||||
SetPSPtol(TXA_CHAN, 0.400)
|
||||
else
|
||||
SetPSPtol(TXA_CHAN, 0.800);
|
||||
SetPSPinMode(TXA_CHAN, Ord(FPSPin));
|
||||
SetPSMapMode(TXA_CHAN, Ord(FPSMap));
|
||||
SetPSStabilize(TXA_CHAN, Ord(FPSStabilize));
|
||||
SetPSIntsAndSpi(TXA_CHAN, FPSInts, FPSSpi);
|
||||
SetPSHWPeak(TXA_CHAN, FPSHWPeak);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PSSetParams(MoxDelay, LoopDelay: Double; TXDelayNs: Integer;
|
||||
RelaxPtol: Boolean; Pin, Map, Stabilize: Boolean; Ints, Spi: Integer;
|
||||
HWPeak: Double);
|
||||
begin
|
||||
FPSMoxDelay := MoxDelay;
|
||||
FPSLoopDelay := LoopDelay;
|
||||
FPSTXDelayNs := TXDelayNs;
|
||||
FPSRelaxPtol := RelaxPtol;
|
||||
FPSPin := Pin;
|
||||
FPSMap := Map;
|
||||
FPSStabilize := Stabilize;
|
||||
if Ints > 0 then FPSInts := Ints;
|
||||
if Spi > 0 then FPSSpi := Spi;
|
||||
if HWPeak > 0 then FPSHWPeak := HWPeak;
|
||||
ApplyPSParams;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetPureSignalArmed(Armed: Boolean);
|
||||
begin
|
||||
FPSArmed := Armed;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PushPSFeedback(const Buf: array of Byte;
|
||||
DataOffset: Integer; TotalPairs: Integer);
|
||||
// Сетевой поток: interleaved 24-bit BE пары [DDC0 I,Q][DDC1 I,Q]... →
|
||||
// деинтерливим и кормим WDSP pscc (как pihpsdr process_ps_iq_data):
|
||||
// DDC0 = RX-feedback (искажённый сигнал после PA), DDC1 = TX DAC (эталон).
|
||||
var
|
||||
i, n, Pos: Integer;
|
||||
IR, QR: LongInt;
|
||||
P: PByte;
|
||||
const
|
||||
SCALE = 1.0 / 8388608.0;
|
||||
begin
|
||||
if not (FPSArmed and FInitialized) then Exit;
|
||||
n := TotalPairs div 2; // пар на каждый поток
|
||||
if n > 256 then n := 256; // ёмкость FPSTx/RxBuf
|
||||
if n <= 0 then Exit;
|
||||
if (DataOffset + n * 2 * 6) > Length(Buf) then Exit;
|
||||
|
||||
P := @Buf[DataOffset];
|
||||
for i := 0 to n * 2 - 1 do
|
||||
begin
|
||||
IR := (LongInt(P[0]) shl 16) or (LongInt(P[1]) shl 8) or LongInt(P[2]);
|
||||
QR := (LongInt(P[3]) shl 16) or (LongInt(P[4]) shl 8) or LongInt(P[5]);
|
||||
Inc(P, 6);
|
||||
if (IR and $800000) <> 0 then IR := IR or LongInt($FF000000);
|
||||
if (QR and $800000) <> 0 then QR := QR or LongInt($FF000000);
|
||||
Pos := i shr 1; // индекс пары в потоке
|
||||
if (i and 1) = 0 then
|
||||
begin
|
||||
FPSRxBuf[Pos * 2] := IR * SCALE; // чётные = DDC0 (RX feedback)
|
||||
FPSRxBuf[Pos * 2 + 1] := QR * SCALE;
|
||||
end
|
||||
else
|
||||
begin
|
||||
FPSTxBuf[Pos * 2] := IR * SCALE; // нечётные = DDC1 (TX DAC)
|
||||
FPSTxBuf[Pos * 2 + 1] := QR * SCALE;
|
||||
end;
|
||||
end;
|
||||
|
||||
pscc(TXA_CHAN, n, @FPSTxBuf[0], @FPSRxBuf[0]);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PSControl(Reset, Mancal, Automode, Turnon: Integer);
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
SetPSControl(TXA_CHAN, Reset, Mancal, Automode, Turnon);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PSSetMox(Mox: Boolean);
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
SetPSMox(TXA_CHAN, Ord(Mox));
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PSGetInfo(var Info: array of Integer);
|
||||
begin
|
||||
if (not FInitialized) or (Length(Info) < 16) then Exit;
|
||||
GetPSInfo(TXA_CHAN, @Info[0]);
|
||||
end;
|
||||
|
||||
function TWDSPEngine.PSGetMaxTX: Double;
|
||||
begin
|
||||
Result := 0.0;
|
||||
if not FInitialized then Exit;
|
||||
GetPSMaxTX(TXA_CHAN, @Result);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetTXFilterFull(Low, High, NC, MP, Window: Integer);
|
||||
|
||||
Reference in New Issue
Block a user