rx2 support work

This commit is contained in:
2026-05-27 11:29:39 +03:00
parent 047c9b0997
commit c2ca52511c
4 changed files with 1290 additions and 258 deletions
+67 -12
View File
@@ -151,6 +151,8 @@ type
// Текущее состояние для построения HP пакетов
FCurrentRXFreq: Double;
FCurrentTXFreq: Double;
FDDCFreq: array[0..MAX_DDCS - 1] of Double;
FDDCEnabled: array[0..MAX_DDCS - 1] of Boolean;
FCurrentDrive: Byte;
FIsTransmitting: Boolean;
FPTTActive: Boolean; // фактический PTT в HP-пакете (отдельно от Alex T/R)
@@ -216,12 +218,18 @@ type
procedure SendDDCSpecific(const Pkt: TDDCSpecificPacket);
procedure ConfigureDDCs(NumDDCs: Byte; SampleRate: Word; ADCSource: Byte = 0;
DitherEnabled: Boolean = True; RandomEnabled: Boolean = True);
procedure ConfigureDDCReceivers(ReceiverCount: Byte; SampleRate: Word;
RX1ADCSource: Byte = 0; RX2ADCSource: Byte = 0;
DitherEnabled: Boolean = True;
RandomEnabled: Boolean = True;
RX2SampleRate: Word = 0);
procedure SendDUCSpecific(const Pkt: TDUCSpecificPacket);
procedure SendHighPriority(const Pkt: THighPriorityPacket);
procedure SetRunAndFreq(Run: Boolean; DDC0FreqHz, DUCFreqHz: Double;
DriveLevel: Byte = 100);
procedure UpdateState(RXFreqHz, TXFreqHz: Double; DriveLevel: Byte;
Transmitting, PAEnabled, AlexEnabled: Boolean);
procedure SetDDCFrequency(DDCIndex: Integer; FreqHz: Double);
procedure SetStepAtten(dB: Byte);
procedure SetAlexConfig(const A: TAlexSettings);
// Включение/настройка XVTR-режима. Все параметры применяются мгновенно
@@ -608,6 +616,8 @@ begin
FWBSeqValid := False;
FCurrentRXFreq := 7100000;
FCurrentTXFreq := 7100000;
FillChar(FDDCEnabled, SizeOf(FDDCEnabled), 0);
FDDCFreq[0] := FCurrentRXFreq;
FCurrentDrive := 0;
FIsTransmitting := False;
FPAEnabled := True;
@@ -1376,15 +1386,29 @@ end;
procedure THPSDRNetwork.ConfigureDDCs(NumDDCs: Byte; SampleRate: Word;
ADCSource: Byte;
DitherEnabled: Boolean; RandomEnabled: Boolean);
begin
ConfigureDDCReceivers(NumDDCs, SampleRate, ADCSource, ADCSource,
DitherEnabled, RandomEnabled);
end;
procedure THPSDRNetwork.ConfigureDDCReceivers(ReceiverCount: Byte; SampleRate: Word;
RX1ADCSource: Byte; RX2ADCSource: Byte;
DitherEnabled: Boolean; RandomEnabled: Boolean;
RX2SampleRate: Word);
var
Pkt: TDDCSpecificPacket;
i, ddc: Integer;
DDCBase: Integer;
ADCSource: Byte;
DDCRate: Word;
begin
FillChar(Pkt, SizeOf(Pkt), 0);
// ANAN-7000/8000/Saturn имеют 2 ADC
if FDevice.BoardType in [4, 5, 10] then // ORION=4, ORION2=5, SATURN=10
if ReceiverCount < 1 then ReceiverCount := 1;
if ReceiverCount > 2 then ReceiverCount := 2;
// ANAN-100D/200D/7000/8000/Saturn имеют второй ADC/RX front-end.
if FDevice.BoardType in [3, 4, 5, 10] then
Pkt.NumADCs := 2
else
Pkt.NumADCs := 1;
@@ -1402,9 +1426,17 @@ begin
else
DDCBase := 0;
for i := 0 to NumDDCs - 1 do
FillChar(FDDCEnabled, SizeOf(FDDCEnabled), 0);
for i := 0 to ReceiverCount - 1 do
begin
ddc := DDCBase + i;
ADCSource := RX1ADCSource;
if i = 1 then ADCSource := RX2ADCSource;
if ADCSource >= Pkt.NumADCs then ADCSource := 0;
DDCRate := SampleRate;
if (i = 1) and (RX2SampleRate > 0) then DDCRate := RX2SampleRate;
FDDCEnabled[ddc] := True;
// Enable bit для DDC ddc
Pkt.DDCEnable[ddc div 8] := Pkt.DDCEnable[ddc div 8] or Byte(1 shl (ddc mod 8));
// Config: 6 байт на DDC начиная с offset 17 в пакете → в массиве DDCConfig
@@ -1412,8 +1444,8 @@ begin
// DDCConfig[ddc*6 + 1..2] = sample rate / 1000 (ksps)
// DDCConfig[ddc*6 + 5] = bits per sample
Pkt.DDCConfig[ddc * 6] := ADCSource;
Pkt.DDCConfig[ddc * 6 + 1] := (SampleRate shr 8) and $FF;
Pkt.DDCConfig[ddc * 6 + 2] := SampleRate and $FF;
Pkt.DDCConfig[ddc * 6 + 1] := (DDCRate shr 8) and $FF;
Pkt.DDCConfig[ddc * 6 + 2] := DDCRate and $FF;
Pkt.DDCConfig[ddc * 6 + 3] := 0;
Pkt.DDCConfig[ddc * 6 + 4] := 0;
Pkt.DDCConfig[ddc * 6 + 5] := 24; // 24 bits per sample
@@ -1451,6 +1483,8 @@ procedure THPSDRNetwork.UpdateState(RXFreqHz, TXFreqHz: Double;
Transmitting, PAEnabled, AlexEnabled: Boolean);
begin
FCurrentRXFreq := RXFreqHz;
FDDCFreq[0] := RXFreqHz;
FDDCFreq[2] := RXFreqHz;
FCurrentTXFreq := TXFreqHz;
FCurrentDrive := DriveLevel;
FIsTransmitting := Transmitting;
@@ -1458,6 +1492,15 @@ begin
FAlexEnabled := AlexEnabled;
end;
procedure THPSDRNetwork.SetDDCFrequency(DDCIndex: Integer; FreqHz: Double);
begin
if (DDCIndex < Low(FDDCFreq)) or (DDCIndex > High(FDDCFreq)) then Exit;
FDDCFreq[DDCIndex] := FreqHz;
if (DDCIndex = 0) or (DDCIndex = 2) then
FCurrentRXFreq := FreqHz;
if FConnected then SendFullHP;
end;
procedure THPSDRNetwork.SetStepAtten(dB: Byte);
begin
if dB > 31 then dB := 31;
@@ -1473,6 +1516,9 @@ var
Alex1: LongWord;
IsOrion2: Boolean;
DDCBase: Integer; // 0 для HERMES, 2 для ORION/ORION2/ANGELIA
DDCIdx: Integer;
DDCF: Double;
RX2Freq: Double;
begin
if not FConnected then Exit;
@@ -1504,12 +1550,19 @@ begin
else
DDCBase := 0;
// DDC RX frequency (bytes 9 + DDC*4)
Ph := FreqToPhaseWord(FCurrentRXFreq);
Buf[9 + DDCBase*4] := (Ph shr 24) and $FF;
Buf[10 + DDCBase*4] := (Ph shr 16) and $FF;
Buf[11 + DDCBase*4] := (Ph shr 8) and $FF;
Buf[12 + DDCBase*4] := Ph and $FF;
// DDC RX frequencies (bytes 9 + DDC*4)
if FDDCFreq[DDCBase] <= 0 then FDDCFreq[DDCBase] := FCurrentRXFreq;
for DDCIdx := 0 to MAX_DDCS - 1 do
if FDDCEnabled[DDCIdx] or (DDCIdx = DDCBase) then
begin
DDCF := FDDCFreq[DDCIdx];
if DDCF <= 0 then DDCF := FCurrentRXFreq;
Ph := FreqToPhaseWord(DDCF);
Buf[9 + DDCIdx*4] := (Ph shr 24) and $FF;
Buf[10 + DDCIdx*4] := (Ph shr 16) and $FF;
Buf[11 + DDCIdx*4] := (Ph shr 8) and $FF;
Buf[12 + DDCIdx*4] := Ph and $FF;
end;
// DUC TX frequency (bytes 329-332)
Ph := FreqToPhaseWord(FCurrentTXFreq);
@@ -1537,7 +1590,9 @@ begin
// ALEX1 filter bits (bytes 1428-1431) — только Orion2/Saturn
if IsOrion2 then
begin
Alex1 := CalcAlex1(FCurrentRXFreq, FCurrentTXFreq,
RX2Freq := FDDCFreq[DDCBase + 1];
if RX2Freq <= 0 then RX2Freq := FCurrentRXFreq;
Alex1 := CalcAlex1(RX2Freq, FCurrentTXFreq,
FIsTransmitting, FAlexConfig,
FXvtrDisablePA);
Buf[1428] := (Alex1 shr 24) and $FF;
+947 -15
View File
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -26,6 +26,7 @@ type
FOnSpanSelect: TSpanSelectEvent;
FOnHidePanel: THidePanelEvent;
FOnInvalidate: TNotifyEvent;
FFirstButtonCaption: string;
FLeft: Integer;
FTop: Integer;
FWidth: Integer;
@@ -54,6 +55,7 @@ type
property Width: Integer read FWidth;
property Height: Integer read FHeight;
property HotIdx: Integer read FHotIdx;
property FirstButtonCaption: string read FFirstButtonCaption write FFirstButtonCaption;
property OnSpanSelect: TSpanSelectEvent read FOnSpanSelect write FOnSpanSelect;
property OnHidePanel: THidePanelEvent read FOnHidePanel write FOnHidePanel;
property OnInvalidate: TNotifyEvent read FOnInvalidate write FOnInvalidate;
@@ -200,7 +202,12 @@ begin
C.Brush.Style := bsClear;
C.Rectangle(HR);
C.Font.Size := 8;
if FPanelHidden then Cap := '>' else Cap := '<';
if FFirstButtonCaption <> '' then
Cap := FFirstButtonCaption
else if FPanelHidden then
Cap := '>'
else
Cap := '<';
TW := C.TextWidth(Cap);
C.Font.Color := CLR_TEXT;
C.Brush.Style := bsClear;
+268 -230
View File
@@ -229,6 +229,11 @@ type
// не меняется.
FPostTXMuteUntil: QWord;
FActiveDisplayID: Integer; // RX_DISP_ID или TX_DISP_ID
FRXChan: Integer;
FTXChan: Integer;
FRXDispID: Integer;
FTXDispID: Integer;
FTXEnabled: Boolean;
FTXMicSource: TTXMicSource;
FOnPullMic: TPullMicSamplesFunc;
@@ -323,7 +328,12 @@ type
public
constructor Create(SampleRate: Integer = 192000;
AudioRate: Integer = 48000;
BufSize: Integer = DSP_BUFSIZE);
BufSize: Integer = DSP_BUFSIZE;
RXChan: Integer = RXA_CHAN;
TXChan: Integer = TXA_CHAN;
RXDispID: Integer = RX_DISP_ID;
TXDispID: Integer = TX_DISP_ID;
EnableTX: Boolean = True);
destructor Destroy; override;
// Открыть DSP каналы
@@ -479,7 +489,7 @@ type
implementation
// ===========================================================================
// TX DSP поток — читает mic ring buffer, вызывает fexchange0(TXA_CHAN),
// TX DSP поток — читает mic ring buffer, вызывает fexchange0(FTXChan),
// сигнализирует о готовых IQ данных через FOnTXIQ callback.
// Аналог tx_thread в piHPSDR/transmitter.c
// ===========================================================================
@@ -715,11 +725,17 @@ end;
// Constructor / Destructor
// ---------------------------------------------------------------------------
constructor TWDSPEngine.Create(SampleRate, AudioRate, BufSize: Integer);
constructor TWDSPEngine.Create(SampleRate, AudioRate, BufSize, RXChan, TXChan,
RXDispID, TXDispID: Integer; EnableTX: Boolean);
begin
inherited Create;
FSampleRate := SampleRate;
FAudioRate := AudioRate;
FRXChan := RXChan;
FTXChan := TXChan;
FRXDispID := RXDispID;
FTXDispID := TXDispID;
FTXEnabled := EnableTX;
// BufSize = dsp_size @ AudioRate (внутренний DSP буфер, piHPSDR buffer_size = 1024)
// FBufSize = in_size @ SampleRate = BufSize * (SampleRate/AudioRate)
// piHPSDR: in_size = buffer_size * sample_rate/48000 = 1024*4 = 4096 @ 192kHz
@@ -784,7 +800,7 @@ begin
FTXActive := False;
FKeepRXDuringTX := False;
FPostTXMuteUntil := 0;
FActiveDisplayID := RX_DISP_ID;
FActiveDisplayID := FRXDispID;
FTXMicSource := txmsRadio;
FOnPullMic := nil;
FTXThread := nil;
@@ -865,19 +881,22 @@ begin
if FAnalyzerOpen then Exit;
Success := -1;
XCreateAnalyzer(RX_DISP_ID, @Success, 262144, 1, 1, nil);
XCreateAnalyzer(FRXDispID, @Success, 262144, 1, 1, nil);
if Success <> 0 then
begin
FLastError := Format('XCreateAnalyzer(RX) failed: %d', [Success]);
Exit;
end;
// Отдельный analyzer для TX-спектра/водопада. Ошибку логируем в LastError,
// но не считаем фатальной — RX без TX-display продолжает работать.
Success := -1;
XCreateAnalyzer(TX_DISP_ID, @Success, 262144, 1, 1, nil);
if Success <> 0 then
FLastError := Format('XCreateAnalyzer(TX) failed: %d', [Success]);
if FTXEnabled then
begin
// Отдельный analyzer для TX-спектра/водопада. Ошибку логируем в LastError,
// но не считаем фатальной — RX без TX-display продолжает работать.
Success := -1;
XCreateAnalyzer(FTXDispID, @Success, 262144, 1, 1, nil);
if Success <> 0 then
FLastError := Format('XCreateAnalyzer(TX) failed: %d', [Success]);
end;
FFlp[0] := 0;
ApplyAnalyzerSettings;
@@ -888,7 +907,7 @@ begin
end;
procedure TWDSPEngine.ApplyRXAnalyzerSettings;
// Применяет FFFTSize / FWindowType / FSpec* / FWf* к RX_DISP_ID.
// Применяет FFFTSize / FWindowType / FSpec* / FWf* к FRXDispID.
var
AnalyzerFFT, AvgCount, WfAvgCount, MaxW, Overlap, DisplayPixels: Integer;
Backmult, WfBackmult: Double;
@@ -905,24 +924,24 @@ begin
AverageTimeToParams(FSpecAvgMode, FSpecAvgTimeMS, AvgCount, Backmult);
AverageTimeToParams(FWfAvgMode, FWfAvgTimeMS, WfAvgCount, WfBackmult);
SetAnalyzer(RX_DISP_ID, 2, 1, 1, @FFlp[0], AnalyzerFFT, DISPLAY_BLOCK_SIZE,
SetAnalyzer(FRXDispID, 2, 1, 1, @FFlp[0], AnalyzerFFT, DISPLAY_BLOCK_SIZE,
FWindowType, 14.0, Overlap, 0, 0.0, 0.0, DisplayPixels,
1, 0, 0.0, 0.0, MaxW);
SetDisplayDetectorMode(RX_DISP_ID, 0, FSpecDetector);
SetDisplayAverageMode (RX_DISP_ID, 0, FSpecAvgMode);
SetDisplayNumAverage (RX_DISP_ID, 0, AvgCount);
SetDisplayAvBackmult (RX_DISP_ID, 0, Backmult);
SetDisplayNormOneHz (RX_DISP_ID, 0, UsePanNormOneHz(FSpecDetector));
SetDisplayDetectorMode(RX_DISP_ID, 1, FWfDetector);
SetDisplayAverageMode (RX_DISP_ID, 1, FWfAvgMode);
SetDisplayNumAverage (RX_DISP_ID, 1, WfAvgCount);
SetDisplayAvBackmult (RX_DISP_ID, 1, WfBackmult);
SetDisplayNormOneHz (RX_DISP_ID, 1, 0);
SetDisplaySampleRate (RX_DISP_ID, FSampleRate);
SetDisplayDetectorMode(FRXDispID, 0, FSpecDetector);
SetDisplayAverageMode (FRXDispID, 0, FSpecAvgMode);
SetDisplayNumAverage (FRXDispID, 0, AvgCount);
SetDisplayAvBackmult (FRXDispID, 0, Backmult);
SetDisplayNormOneHz (FRXDispID, 0, UsePanNormOneHz(FSpecDetector));
SetDisplayDetectorMode(FRXDispID, 1, FWfDetector);
SetDisplayAverageMode (FRXDispID, 1, FWfAvgMode);
SetDisplayNumAverage (FRXDispID, 1, WfAvgCount);
SetDisplayAvBackmult (FRXDispID, 1, WfBackmult);
SetDisplayNormOneHz (FRXDispID, 1, 0);
SetDisplaySampleRate (FRXDispID, FSampleRate);
end;
procedure TWDSPEngine.ApplyTXAnalyzerSettings;
// Применяет FTXFFTSize / FTXWindowType / FTXSpec* / FTXWf* к TX_DISP_ID.
// Применяет FTXFFTSize / FTXWindowType / FTXSpec* / FTXWf* к FTXDispID.
// FFT/Window/Det/Avg отдельные от RX — TX-спектр обычно нужен мельче по FFT
// (TX-полоса узкая) и с другим усреднением (быстрее реагировать на голос).
var
@@ -931,6 +950,7 @@ var
EffectiveFPS: Double;
const KEEP_TIME_SEC = 0.10;
begin
if not FTXEnabled then Exit;
AnalyzerFFT := NormalizeFFTSize(FTXFFTSize);
FTXDispPos := 0;
MaxW := AnalyzerFFT + Round(Min(KEEP_TIME_SEC * FTXSampleRate,
@@ -941,27 +961,28 @@ begin
AverageTimeToParams(FTXSpecAvgMode, FTXSpecAvgTimeMS, AvgCount, Backmult);
AverageTimeToParams(FTXWfAvgMode, FTXWfAvgTimeMS, WfAvgCount, WfBackmult);
SetAnalyzer(TX_DISP_ID, 2, 1, 1, @FFlp[0], AnalyzerFFT, DISPLAY_BLOCK_SIZE,
SetAnalyzer(FTXDispID, 2, 1, 1, @FFlp[0], AnalyzerFFT, DISPLAY_BLOCK_SIZE,
FTXWindowType, 14.0, Overlap, 0, 0.0, 0.0, DisplayPixels,
1, 0, 0.0, 0.0, MaxW);
SetDisplayDetectorMode(TX_DISP_ID, 0, FTXSpecDetector);
SetDisplayAverageMode (TX_DISP_ID, 0, FTXSpecAvgMode);
SetDisplayNumAverage (TX_DISP_ID, 0, AvgCount);
SetDisplayAvBackmult (TX_DISP_ID, 0, Backmult);
SetDisplayNormOneHz (TX_DISP_ID, 0, UsePanNormOneHz(FTXSpecDetector));
SetDisplayDetectorMode(TX_DISP_ID, 1, FTXWfDetector);
SetDisplayAverageMode (TX_DISP_ID, 1, FTXWfAvgMode);
SetDisplayNumAverage (TX_DISP_ID, 1, WfAvgCount);
SetDisplayAvBackmult (TX_DISP_ID, 1, WfBackmult);
SetDisplayNormOneHz (TX_DISP_ID, 1, 0);
SetDisplaySampleRate (TX_DISP_ID, FTXSampleRate);
SetDisplayDetectorMode(FTXDispID, 0, FTXSpecDetector);
SetDisplayAverageMode (FTXDispID, 0, FTXSpecAvgMode);
SetDisplayNumAverage (FTXDispID, 0, AvgCount);
SetDisplayAvBackmult (FTXDispID, 0, Backmult);
SetDisplayNormOneHz (FTXDispID, 0, UsePanNormOneHz(FTXSpecDetector));
SetDisplayDetectorMode(FTXDispID, 1, FTXWfDetector);
SetDisplayAverageMode (FTXDispID, 1, FTXWfAvgMode);
SetDisplayNumAverage (FTXDispID, 1, WfAvgCount);
SetDisplayAvBackmult (FTXDispID, 1, WfBackmult);
SetDisplayNormOneHz (FTXDispID, 1, 0);
SetDisplaySampleRate (FTXDispID, FTXSampleRate);
end;
procedure TWDSPEngine.ApplyAnalyzerSettings;
// Совместимый вход: применяет настройки и к RX, и к TX analyzer'у.
begin
ApplyRXAnalyzerSettings;
ApplyTXAnalyzerSettings;
if FTXEnabled then
ApplyTXAnalyzerSettings;
end;
procedure TWDSPEngine.CloseAnalyzer;
@@ -973,8 +994,9 @@ begin
FDisplayThread.WaitFor;
FreeAndNil(FDisplayThread);
end;
DestroyAnalyzer(RX_DISP_ID);
DestroyAnalyzer(TX_DISP_ID);
DestroyAnalyzer(FRXDispID);
if FTXEnabled then
DestroyAnalyzer(FTXDispID);
FAnalyzerOpen := False;
end;
@@ -1011,7 +1033,7 @@ begin
end;
OpenChannel(
RXA_CHAN,
FRXChan,
FBufSize,
FAudioBufSize,
FSampleRate,
@@ -1023,45 +1045,49 @@ begin
0
);
create_anbEXT(RXA_CHAN, 1, FBufSize, FSampleRate, 0.0001, 0.0001, 0.0001, 0.05, 20);
create_nobEXT(RXA_CHAN, 1, 0, FBufSize, FSampleRate, 0.0001, 0.0001, 0.0001, 0.05, 20);
create_anbEXT(FRXChan, 1, FBufSize, FSampleRate, 0.0001, 0.0001, 0.0001, 0.05, 20);
create_nobEXT(FRXChan, 1, 0, FBufSize, FSampleRate, 0.0001, 0.0001, 0.0001, 0.05, 20);
SetRXABandpassWindow(RXA_CHAN, 1);
SetRXAMode(RXA_CHAN, ModeToWDSP(FMode));
SetRXAShiftRun(RXA_CHAN, 1);
SetRXAShiftFreq(RXA_CHAN, 0.0);
RXASetPassband(RXA_CHAN, FFilterLow, FFilterHigh);
SetRXABandpassWindow(FRXChan, 1);
SetRXAMode(FRXChan, ModeToWDSP(FMode));
SetRXAShiftRun(FRXChan, 1);
SetRXAShiftFreq(FRXChan, 0.0);
RXASetPassband(FRXChan, FFilterLow, FFilterHigh);
SetAGC(FAGCMode, 50.0);
SetRXAPanelGain1(RXA_CHAN, FVolume);
SetRXAPanelSelect(RXA_CHAN, 3);
SetRXAPanelRun(RXA_CHAN, 1);
SetChannelState(RXA_CHAN, 1, 0);
SetRXAPanelGain1(FRXChan, FVolume);
SetRXAPanelSelect(FRXChan, 3);
SetRXAPanelRun(FRXChan, 1);
SetChannelState(FRXChan, 1, 0);
// TXA: mic @ FAudioRate → IQ @ FTXSampleRate (фиксированный DUC rate, не зависит от RX).
// out_size = FAudioBufSize * FTXSampleRate/FAudioRate = FTXOutBufSize (4096 @ 192k tx).
// FTXOut уже имеет размер FTXOutBufSize*2.
OpenChannel(
TXA_CHAN,
FAudioBufSize,
FAudioBufSize,
FAudioRate,
FAudioRate,
FTXSampleRate,
1,
0,
0.010, 0.010, 0.010, 0.010,
0
);
if FTXEnabled then
begin
// TXA: mic @ FAudioRate → IQ @ FTXSampleRate (фиксированный DUC rate, не зависит от RX).
// out_size = FAudioBufSize * FTXSampleRate/FAudioRate = FTXOutBufSize (4096 @ 192k tx).
// FTXOut уже имеет размер FTXOutBufSize*2.
OpenChannel(
FTXChan,
FAudioBufSize,
FAudioBufSize,
FAudioRate,
FAudioRate,
FTXSampleRate,
1,
0,
0.010, 0.010, 0.010, 0.010,
0
);
SetTXAMode(TXA_CHAN, ModeToWDSP(FMode));
SetTXAPanelRun(TXA_CHAN, 1);
// PatchPanel: 1=Q, 2=I. Mic подаётся в I (FTXIn[i*2]), Q=0 — выбираем I.
SetTXAPanelSelect(TXA_CHAN, 2);
SetTXAMode(FTXChan, ModeToWDSP(FMode));
SetTXAPanelRun(FTXChan, 1);
// PatchPanel: 1=Q, 2=I. Mic подаётся в I (FTXIn[i*2]), Q=0 — выбираем I.
SetTXAPanelSelect(FTXChan, 2);
end;
FInitialized := True;
// Прокидываем все TX-параметры из FTX*-полей: фильтр, mic gain, EQ,
// Leveler, ALC, Compressor, Phase Rotator, AM Carrier, FM, CTCSS.
ApplyTXChainSettings;
if FTXEnabled then
ApplyTXChainSettings;
ApplyNoiseFilterState;
Result := True;
@@ -1094,10 +1120,11 @@ begin
end;
CloseAnalyzer;
destroy_anbEXT(RXA_CHAN);
destroy_nobEXT(RXA_CHAN);
CloseChannel(RXA_CHAN);
CloseChannel(TXA_CHAN);
destroy_anbEXT(FRXChan);
destroy_nobEXT(FRXChan);
CloseChannel(FRXChan);
if FTXEnabled then
CloseChannel(FTXChan);
FInitialized := False;
end;
@@ -1179,7 +1206,7 @@ procedure TWDSPEngine.PushIQItemToDSP(const Item: TIQQueueItem);
// Вызывается из DSP потока — декодирует IQ и накапливает до FBufSize.
// В non-DUP TX (FTXActive and not FKeepRXDuringTX) полностью пропускаем
// IQ-пакет: не кормим RX-анализатор и не накапливаем для fexchange0.
// Иначе TX leakage из DDC оседает в RXA pipeline / RX_DISP_ID FFT-истории
// Иначе TX leakage из DDC оседает в RXA pipeline / FRXDispID FFT-истории
// и звучит/виден на водопаде первые секунды после возврата на RX.
var
i, Pos: Integer;
@@ -1231,7 +1258,7 @@ begin
if FDispPos >= DISPLAY_BLOCK_SIZE then
begin
Spectrum0(1, RX_DISP_ID, 0, 0, @FDispBuf[0]);
Spectrum0(1, FRXDispID, 0, 0, @FDispBuf[0]);
FDispPos := 0;
end;
end;
@@ -1246,7 +1273,7 @@ begin
if FTXDispPos >= DISPLAY_BLOCK_SIZE then
begin
Spectrum0(1, TX_DISP_ID, 0, 0, @FTXDispBuf[0]);
Spectrum0(1, FTXDispID, 0, 0, @FTXDispBuf[0]);
FTXDispPos := 0;
end;
end;
@@ -1270,12 +1297,12 @@ begin
// DSP обработка
Err := 0;
fexchange0(RXA_CHAN, @FRXIn[0], @FRXOut[0], @Err);
fexchange0(FRXChan, @FRXIn[0], @FRXOut[0], @Err);
// S-meter: читаем ОДИН раз в DSP-колбэке и кешируем в FSMeter.
// GetRXAMeter сбрасывает аккумулятор после вызова, поэтому второй
// вызов вернёт 0/-∞ — отсюда "падение до нуля". Только здесь!
FSMeter := GetRXAMeter(RXA_CHAN, RXA_S_AV);
FSMeter := GetRXAMeter(FRXChan, RXA_S_AV);
// Аудио колбэк — FAudioBufSize сэмплов @ FAudioRate (после децимации 4:1).
// При TX RX-аудио глушим (как в Thetis/piHPSDR при не-duplex MOX),
@@ -1309,8 +1336,9 @@ procedure TWDSPEngine.SetMode(Mode: Integer);
begin
FMode := Mode;
if not FInitialized then Exit;
SetRXAMode(RXA_CHAN, ModeToWDSP(Mode));
SetTXAMode(TXA_CHAN, ModeToWDSP(Mode));
SetRXAMode(FRXChan, ModeToWDSP(Mode));
if FTXEnabled then
SetTXAMode(FTXChan, ModeToWDSP(Mode));
// НЕ вызываем ApplyDefaultFilter — фильтр устанавливается явно из UI
end;
@@ -1320,7 +1348,7 @@ begin
FFilterHigh := High;
if not FInitialized then Exit;
// RXASetPassband — правильный unified API, пересчитывает фильтр целиком
RXASetPassband(RXA_CHAN, Low, High);
RXASetPassband(FRXChan, Low, High);
end;
procedure TWDSPEngine.SetAGC(Mode: TWDSPAGCMode; FixedGain: Double);
@@ -1329,40 +1357,40 @@ begin
if not FInitialized then Exit;
// Точно по piHPSDR receiver.c: set_agc()
SetRXAAGCMode(RXA_CHAN, Ord(Mode));
SetRXAAGCSlope(RXA_CHAN, FAGCSlope);
SetRXAAGCTop(RXA_CHAN, FAGCTop);
SetRXAAGCMode(FRXChan, Ord(Mode));
SetRXAAGCSlope(FRXChan, FAGCSlope);
SetRXAAGCTop(FRXChan, FAGCTop);
case Mode of
agcOff:
SetRXAAGCFixed(RXA_CHAN, FixedGain);
SetRXAAGCFixed(FRXChan, FixedGain);
agcLong: begin
SetRXAAGCAttack(RXA_CHAN, 2);
SetRXAAGCHang(RXA_CHAN, 2000);
SetRXAAGCDecay(RXA_CHAN, 2000);
SetRXAAGCHangThreshold(RXA_CHAN, FAGCHangThreshold);
SetRXAAGCAttack(FRXChan, 2);
SetRXAAGCHang(FRXChan, 2000);
SetRXAAGCDecay(FRXChan, 2000);
SetRXAAGCHangThreshold(FRXChan, FAGCHangThreshold);
end;
agcSlow: begin
SetRXAAGCAttack(RXA_CHAN, 2);
SetRXAAGCHang(RXA_CHAN, 1000);
SetRXAAGCDecay(RXA_CHAN, 500);
SetRXAAGCHangThreshold(RXA_CHAN, FAGCHangThreshold);
SetRXAAGCAttack(FRXChan, 2);
SetRXAAGCHang(FRXChan, 1000);
SetRXAAGCDecay(FRXChan, 500);
SetRXAAGCHangThreshold(FRXChan, FAGCHangThreshold);
end;
agcMedium: begin
SetRXAAGCAttack(RXA_CHAN, 2);
SetRXAAGCHang(RXA_CHAN, 0);
SetRXAAGCDecay(RXA_CHAN, 250);
SetRXAAGCHangThreshold(RXA_CHAN, 100);
SetRXAAGCAttack(FRXChan, 2);
SetRXAAGCHang(FRXChan, 0);
SetRXAAGCDecay(FRXChan, 250);
SetRXAAGCHangThreshold(FRXChan, 100);
end;
agcFast: begin
SetRXAAGCAttack(RXA_CHAN, 2);
SetRXAAGCHang(RXA_CHAN, 0);
SetRXAAGCDecay(RXA_CHAN, 50);
SetRXAAGCHangThreshold(RXA_CHAN, 100);
SetRXAAGCAttack(FRXChan, 2);
SetRXAAGCHang(FRXChan, 0);
SetRXAAGCDecay(FRXChan, 50);
SetRXAAGCHangThreshold(FRXChan, 100);
end;
end;
@@ -1374,14 +1402,14 @@ procedure TWDSPEngine.SetShift(ShiftHz: Double);
begin
FShiftHz := ShiftHz;
if not FInitialized then Exit;
SetRXAShiftFreq(RXA_CHAN, ShiftHz);
SetRXAShiftFreq(FRXChan, ShiftHz);
end;
procedure TWDSPEngine.SetAGCTop(TopDBm: Double);
begin
FAGCTop := TopDBm;
if not FInitialized then Exit;
SetRXAAGCTop(RXA_CHAN, TopDBm);
SetRXAAGCTop(FRXChan, TopDBm);
UpdateAGCLines(FLastSpectrumW);
end;
@@ -1389,14 +1417,14 @@ procedure TWDSPEngine.SetAGCSlope(Slope: Integer);
begin
FAGCSlope := Slope;
if not FInitialized then Exit;
SetRXAAGCSlope(RXA_CHAN, Slope);
SetRXAAGCSlope(FRXChan, Slope);
end;
procedure TWDSPEngine.SetAGCHangThreshold(Threshold: Integer);
begin
FAGCHangThreshold := Threshold;
if not FInitialized then Exit;
SetRXAAGCHangThreshold(RXA_CHAN, Threshold);
SetRXAAGCHangThreshold(FRXChan, Threshold);
UpdateAGCLines(FLastSpectrumW);
end;
@@ -1413,8 +1441,8 @@ procedure TWDSPEngine.UpdateAGCLines(SpectrumW: Integer);
begin
if not FInitialized then Exit;
if SpectrumW <= 0 then Exit; // окно ещё не показано — не читаем
GetRXAAGCHangLevel(RXA_CHAN, @FAGCHangLevel);
GetRXAAGCThresh(RXA_CHAN, @FAGCThresh,
GetRXAAGCHangLevel(FRXChan, @FAGCHangLevel);
GetRXAAGCThresh(FRXChan, @FAGCThresh,
SpectrumW, // реальная ширина дисплея в пикселях
FSampleRate);
end;
@@ -1423,65 +1451,65 @@ procedure TWDSPEngine.ApplyNRState;
begin
if not FInitialized then Exit;
SetRXAANRVals(RXA_CHAN, 64, 16, 16e-4, 10e-7);
SetRXAANRPosition(RXA_CHAN, FILTER_NR_AGC_POSITION);
SetRXAANRRun(RXA_CHAN, Ord(FNRMode = NR_MODE_1));
SetRXAANRVals(FRXChan, 64, 16, 16e-4, 10e-7);
SetRXAANRPosition(FRXChan, FILTER_NR_AGC_POSITION);
SetRXAANRRun(FRXChan, Ord(FNRMode = NR_MODE_1));
SetRXAEMNRPosition(RXA_CHAN, FILTER_NR_AGC_POSITION);
SetRXAEMNRgainMethod(RXA_CHAN, FILTER_NR2_GAIN_METHOD);
SetRXAEMNRnpeMethod(RXA_CHAN, FILTER_NR2_NPE_METHOD);
SetRXAEMNRtrainZetaThresh(RXA_CHAN, FILTER_NR2_TRAINED_THRESHOLD);
SetRXAEMNRtrainT2(RXA_CHAN, FILTER_NR2_TRAINED_T2);
SetRXAEMNRpost2Taper(RXA_CHAN, FILTER_NR2_POST_TAPER);
SetRXAEMNRpost2Nlevel(RXA_CHAN, FILTER_NR2_POST_NLEVEL);
SetRXAEMNRpost2Factor(RXA_CHAN, FILTER_NR2_POST_FACTOR);
SetRXAEMNRpost2Rate(RXA_CHAN, FILTER_NR2_POST_RATE);
SetRXAEMNRaeRun(RXA_CHAN, 1);
SetRXAEMNRpost2Run(RXA_CHAN, FILTER_NR2_POST_RUN);
SetRXAEMNRRun(RXA_CHAN, Ord(FNRMode = NR_MODE_2));
SetRXAEMNRPosition(FRXChan, FILTER_NR_AGC_POSITION);
SetRXAEMNRgainMethod(FRXChan, FILTER_NR2_GAIN_METHOD);
SetRXAEMNRnpeMethod(FRXChan, FILTER_NR2_NPE_METHOD);
SetRXAEMNRtrainZetaThresh(FRXChan, FILTER_NR2_TRAINED_THRESHOLD);
SetRXAEMNRtrainT2(FRXChan, FILTER_NR2_TRAINED_T2);
SetRXAEMNRpost2Taper(FRXChan, FILTER_NR2_POST_TAPER);
SetRXAEMNRpost2Nlevel(FRXChan, FILTER_NR2_POST_NLEVEL);
SetRXAEMNRpost2Factor(FRXChan, FILTER_NR2_POST_FACTOR);
SetRXAEMNRpost2Rate(FRXChan, FILTER_NR2_POST_RATE);
SetRXAEMNRaeRun(FRXChan, 1);
SetRXAEMNRpost2Run(FRXChan, FILTER_NR2_POST_RUN);
SetRXAEMNRRun(FRXChan, Ord(FNRMode = NR_MODE_2));
SetRXARNNRPosition(RXA_CHAN, FILTER_NR_AGC_POSITION);
SetRXARNNRRun(RXA_CHAN, Ord(FNRMode = NR_MODE_3));
SetRXARNNRPosition(FRXChan, FILTER_NR_AGC_POSITION);
SetRXARNNRRun(FRXChan, Ord(FNRMode = NR_MODE_3));
SetRXASBNRreductionAmount(RXA_CHAN, FILTER_NR4_REDUCTION_AMOUNT);
SetRXASBNRsmoothingFactor(RXA_CHAN, FILTER_NR4_SMOOTHING_FACTOR);
SetRXASBNRwhiteningFactor(RXA_CHAN, FILTER_NR4_WHITENING_FACTOR);
SetRXASBNRnoiseRescale(RXA_CHAN, FILTER_NR4_NOISE_RESCALE);
SetRXASBNRpostFilterThreshold(RXA_CHAN, FILTER_NR4_POST_THRESHOLD);
SetRXASBNRnoiseScalingType(RXA_CHAN, FILTER_NR4_NOISE_SCALING_TYPE);
SetRXASBNRPosition(RXA_CHAN, FILTER_NR_AGC_POSITION);
SetRXASBNRRun(RXA_CHAN, Ord(FNRMode = NR_MODE_4));
SetRXASBNRreductionAmount(FRXChan, FILTER_NR4_REDUCTION_AMOUNT);
SetRXASBNRsmoothingFactor(FRXChan, FILTER_NR4_SMOOTHING_FACTOR);
SetRXASBNRwhiteningFactor(FRXChan, FILTER_NR4_WHITENING_FACTOR);
SetRXASBNRnoiseRescale(FRXChan, FILTER_NR4_NOISE_RESCALE);
SetRXASBNRpostFilterThreshold(FRXChan, FILTER_NR4_POST_THRESHOLD);
SetRXASBNRnoiseScalingType(FRXChan, FILTER_NR4_NOISE_SCALING_TYPE);
SetRXASBNRPosition(FRXChan, FILTER_NR_AGC_POSITION);
SetRXASBNRRun(FRXChan, Ord(FNRMode = NR_MODE_4));
end;
procedure TWDSPEngine.ApplyNBState;
begin
if not FInitialized then Exit;
SetEXTANBTau(RXA_CHAN, FILTER_NB_TAU);
SetEXTANBHangtime(RXA_CHAN, FILTER_NB_HANGTIME);
SetEXTANBAdvtime(RXA_CHAN, FILTER_NB_ADVTIME);
SetEXTANBThreshold(RXA_CHAN, FILTER_NB_THRESHOLD);
SetEXTANBRun(RXA_CHAN, Ord(FNBMode = NB_MODE_1));
SetEXTANBTau(FRXChan, FILTER_NB_TAU);
SetEXTANBHangtime(FRXChan, FILTER_NB_HANGTIME);
SetEXTANBAdvtime(FRXChan, FILTER_NB_ADVTIME);
SetEXTANBThreshold(FRXChan, FILTER_NB_THRESHOLD);
SetEXTANBRun(FRXChan, Ord(FNBMode = NB_MODE_1));
SetEXTNOBMode(RXA_CHAN, FILTER_NB2_MODE);
SetEXTNOBTau(RXA_CHAN, FILTER_NB_TAU);
SetEXTNOBHangtime(RXA_CHAN, FILTER_NB_HANGTIME);
SetEXTNOBAdvtime(RXA_CHAN, FILTER_NB_ADVTIME);
SetEXTNOBThreshold(RXA_CHAN, FILTER_NB_THRESHOLD);
SetEXTNOBRun(RXA_CHAN, Ord(FNBMode = NB_MODE_2));
SetEXTNOBMode(FRXChan, FILTER_NB2_MODE);
SetEXTNOBTau(FRXChan, FILTER_NB_TAU);
SetEXTNOBHangtime(FRXChan, FILTER_NB_HANGTIME);
SetEXTNOBAdvtime(FRXChan, FILTER_NB_ADVTIME);
SetEXTNOBThreshold(FRXChan, FILTER_NB_THRESHOLD);
SetEXTNOBRun(FRXChan, Ord(FNBMode = NB_MODE_2));
end;
procedure TWDSPEngine.ApplySNBState;
begin
if not FInitialized then Exit;
SetRXASNBARun(RXA_CHAN, Ord(FSNBEnabled));
SetRXASNBARun(FRXChan, Ord(FSNBEnabled));
end;
procedure TWDSPEngine.ApplyANFState;
begin
if not FInitialized then Exit;
SetRXAANFPosition(RXA_CHAN, FILTER_NR_AGC_POSITION);
SetRXAANFRun(RXA_CHAN, Ord(FANFEnabled));
SetRXAANFPosition(FRXChan, FILTER_NR_AGC_POSITION);
SetRXAANFRun(FRXChan, Ord(FANFEnabled));
end;
procedure TWDSPEngine.ApplyNoiseFilterState;
@@ -1535,7 +1563,7 @@ end;
procedure TWDSPEngine.SetRXFMDeviation(Deviation: Double);
begin
if not FInitialized then Exit;
SetRXAFMDeviation(RXA_CHAN, Deviation);
SetRXAFMDeviation(FRXChan, Deviation);
end;
procedure TWDSPEngine.SetFMSquelch(Enable: Boolean; Level: Integer);
@@ -1545,25 +1573,25 @@ begin
if Enable then
begin
Threshold := Level / 200.0; // 0..100 → 0.0..0.5
SetRXAFMSQThreshold(RXA_CHAN, Threshold);
SetRXAFMSQRun(RXA_CHAN, 1);
SetRXAFMSQThreshold(FRXChan, Threshold);
SetRXAFMSQRun(FRXChan, 1);
end
else
SetRXAFMSQRun(RXA_CHAN, 0);
SetRXAFMSQRun(FRXChan, 0);
end;
procedure TWDSPEngine.SetVolume(Vol: Double);
begin
FVolume := Max(0.0, Min(1.0, Vol));
if not FInitialized then Exit;
SetRXAPanelGain1(RXA_CHAN, FVolume);
SetRXAPanelGain1(FRXChan, FVolume);
end;
procedure TWDSPEngine.SetMute(Mute: Boolean);
begin
FMuted := Mute;
if not FInitialized then Exit;
SetRXAPanelRun(RXA_CHAN, Ord(not Mute));
SetRXAPanelRun(FRXChan, Ord(not Mute));
end;
// ---------------------------------------------------------------------------
@@ -1572,43 +1600,53 @@ end;
procedure TWDSPEngine.SetTXMode(Mode: Integer);
begin
if not FTXEnabled then Exit;
if not FInitialized then Exit;
SetTXAMode(TXA_CHAN, ModeToWDSP(Mode));
SetTXAMode(FTXChan, ModeToWDSP(Mode));
end;
procedure TWDSPEngine.SetTXFilter(Low, High: Integer);
begin
if not FTXEnabled then Exit;
if not FInitialized then Exit;
SetTXABandpassFreqs(TXA_CHAN, Low, High);
SetTXABandpassFreqs(FTXChan, Low, High);
end;
procedure TWDSPEngine.SetDriveLevel(Level: Double);
begin
if not FTXEnabled then Exit;
if not FInitialized then Exit;
SetTXAALCMaxGain(TXA_CHAN, Max(0.0, Min(1.0, Level)));
SetTXAALCMaxGain(FTXChan, Max(0.0, Min(1.0, Level)));
end;
procedure TWDSPEngine.SetMicGain(GainDB: Double);
begin
FTXMicGainDB := GainDB;
if not FTXEnabled then Exit;
if not FInitialized then Exit;
SetTXAPanelGain1(TXA_CHAN, Power(10.0, GainDB / 20.0));
SetTXAPanelGain1(FTXChan, Power(10.0, GainDB / 20.0));
end;
procedure TWDSPEngine.SetTXRun(Run: Boolean);
begin
if not FTXEnabled then
begin
FTXActive := False;
FActiveDisplayID := FRXDispID;
Exit;
end;
FTXActive := Run;
FDispPos := 0;
FTXDispPos := 0;
if Run then
FActiveDisplayID := TX_DISP_ID
FActiveDisplayID := FTXDispID
else
FActiveDisplayID := RX_DISP_ID;
FActiveDisplayID := FRXDispID;
if Run then
begin
if FInitialized then
SetChannelState(TXA_CHAN, 1, 0); // run, no delay
SetChannelState(FTXChan, 1, 0); // run, no delay
// Сброс mic ring buffer и запуск TX потока
FTXMicHead := 0;
FTXMicTail := 0;
@@ -1619,7 +1657,7 @@ begin
end;
end else begin
if FInitialized then
SetChannelState(TXA_CHAN, 0, 1); // stop, with slew
SetChannelState(FTXChan, 0, 1); // stop, with slew
// Останавливаем TX поток
if Assigned(FTXThread) then
begin
@@ -1647,12 +1685,12 @@ begin
SignedFreq := -Abs(FreqHz)
else
SignedFreq := Abs(FreqHz);
SetTXAPostGenMode(TXA_CHAN, 0); // 0 = tone
SetTXAPostGenToneFreq(TXA_CHAN, SignedFreq);
SetTXAPostGenToneMag(TXA_CHAN, Max(0.0, Min(1.0, Mag)));
SetTXAPostGenRun(TXA_CHAN, 1);
SetTXAPostGenMode(FTXChan, 0); // 0 = tone
SetTXAPostGenToneFreq(FTXChan, SignedFreq);
SetTXAPostGenToneMag(FTXChan, Max(0.0, Min(1.0, Mag)));
SetTXAPostGenRun(FTXChan, 1);
end else
SetTXAPostGenRun(TXA_CHAN, 0);
SetTXAPostGenRun(FTXChan, 0);
end;
procedure TWDSPEngine.SetDisplaySourceTX(UseTX: Boolean);
@@ -1664,9 +1702,9 @@ begin
FDispPos := 0;
FTXDispPos := 0;
if UseTX then
FActiveDisplayID := TX_DISP_ID
FActiveDisplayID := FTXDispID
else
FActiveDisplayID := RX_DISP_ID;
FActiveDisplayID := FRXDispID;
end;
procedure TWDSPEngine.ProcessTXBlock;
@@ -1702,9 +1740,9 @@ begin
end;
Err := 0;
fexchange0(TXA_CHAN, @FTXIn[0], @FTXOut[0], @Err);
fexchange0(FTXChan, @FTXIn[0], @FTXOut[0], @Err);
// TX сигнал → отдельный TX-analyzer (TX_DISP_ID).
// TX сигнал → отдельный TX-analyzer (FTXDispID).
// Подаём чанками по DISPLAY_BLOCK_SIZE — analyzer ожидает именно такой
// размер блока (см. SetAnalyzer). Если подавать FTXOut целиком
// (FTXOutBufSize пар), analyzer прочитает только первые DISPLAY_BLOCK_SIZE
@@ -1795,35 +1833,35 @@ begin
// Bandpass
if (FTXFilterLow > 0) and (FTXFilterHigh > FTXFilterLow) then
SetTXABandpassFreqs(TXA_CHAN, FTXFilterLow, FTXFilterHigh);
SetTXABandpassFreqs(FTXChan, FTXFilterLow, FTXFilterHigh);
if FTXFilterWindow >= 0 then
SetTXABandpassWindow(TXA_CHAN, FTXFilterWindow);
SetTXABandpassWindow(FTXChan, FTXFilterWindow);
if FTXFilterNC > 0 then
TXASetNC(TXA_CHAN, FTXFilterNC);
TXASetNC(FTXChan, FTXFilterNC);
if FTXFilterMP >= 0 then
TXASetMP(TXA_CHAN, FTXFilterMP);
TXASetMP(FTXChan, FTXFilterMP);
// Mic gain (panel gain1)
SetTXAPanelGain1(TXA_CHAN, Power(10.0, FTXMicGainDB / 20.0));
SetTXAPanelGain1(FTXChan, Power(10.0, FTXMicGainDB / 20.0));
// Compressor
SetTXACompressorRun(TXA_CHAN, Ord(FTXCompressorOn));
SetTXACompressorGain(TXA_CHAN, FTXCompressorGain);
SetTXACompressorRun(FTXChan, Ord(FTXCompressorOn));
SetTXACompressorGain(FTXChan, FTXCompressorGain);
// Leveler
SetTXALevelerSt(TXA_CHAN, Ord(FTXLevelerOn));
SetTXALevelerTop(TXA_CHAN, FTXLevelerTop);
SetTXALevelerDecay(TXA_CHAN, FTXLevelerDecay);
SetTXALevelerSt(FTXChan, Ord(FTXLevelerOn));
SetTXALevelerTop(FTXChan, FTXLevelerTop);
SetTXALevelerDecay(FTXChan, FTXLevelerDecay);
// ALC
SetTXAALCSt(TXA_CHAN, Ord(FTXALCOn));
SetTXAALCMaxGain(TXA_CHAN, FTXALCMaxGain);
SetTXAALCDecay(TXA_CHAN, FTXALCDecay);
SetTXAALCSt(FTXChan, Ord(FTXALCOn));
SetTXAALCMaxGain(FTXChan, FTXALCMaxGain);
SetTXAALCDecay(FTXChan, FTXALCDecay);
// Phase Rotator
SetTXAPHROTRun(TXA_CHAN, Ord(FTXPhaseRotOn));
SetTXAPHROTNstages(TXA_CHAN, FTXPhaseRotStages);
SetTXAPHROTCorner(TXA_CHAN, FTXPhaseRotFreq);
SetTXAPHROTRun(FTXChan, Ord(FTXPhaseRotOn));
SetTXAPHROTNstages(FTXChan, FTXPhaseRotStages);
SetTXAPHROTCorner(FTXChan, FTXPhaseRotFreq);
// EQ TX (10-band: [0]=preamp, [1..10]=bands)
for i := 0 to 10 do
@@ -1832,21 +1870,21 @@ begin
Freqs[i] := FTXEQFreqs[i];
Qs[i] := 0.707; // умолчание Q
end;
SetTXAEQProfile(TXA_CHAN, FTXEQNumBands, @Freqs[0], @Gains[0]);
SetTXAEQRun(TXA_CHAN, Ord(FTXEQOn));
SetTXAEQProfile(FTXChan, FTXEQNumBands, @Freqs[0], @Gains[0]);
SetTXAEQRun(FTXChan, Ord(FTXEQOn));
// AM
SetTXAAMCarrierLevel(TXA_CHAN, FTXAMCarrier);
SetTXAAMCarrierLevel(FTXChan, FTXAMCarrier);
// FM
SetTXAFMDeviation(TXA_CHAN, FTXFMDeviation);
SetTXAFMDeviation(FTXChan, FTXFMDeviation);
if (FTXFMLowCut < FTXFMHighCut) then
SetTXAFMAFFilter(TXA_CHAN, FTXFMLowCut, FTXFMHighCut);
SetTXAFMEmphPosition(TXA_CHAN, FTXFMEmphPos);
SetTXAFMAFFilter(FTXChan, FTXFMLowCut, FTXFMHighCut);
SetTXAFMEmphPosition(FTXChan, FTXFMEmphPos);
// CTCSS
SetTXACTCSSFreq(TXA_CHAN, FTXCTCSSFreq);
SetTXACTCSSRun(TXA_CHAN, Ord(FTXCTCSSOn));
SetTXACTCSSFreq(FTXChan, FTXCTCSSFreq);
SetTXACTCSSRun(FTXChan, Ord(FTXCTCSSOn));
end;
procedure TWDSPEngine.SetTXFilterFull(Low, High, NC, MP, Window: Integer);
@@ -1859,10 +1897,10 @@ begin
FFilterLow := Low;
FFilterHigh := High;
if not FInitialized then Exit;
SetTXABandpassFreqs(TXA_CHAN, Low, High);
SetTXABandpassWindow(TXA_CHAN, Window);
if NC > 0 then TXASetNC(TXA_CHAN, NC);
if MP >= 0 then TXASetMP(TXA_CHAN, MP);
SetTXABandpassFreqs(FTXChan, Low, High);
SetTXABandpassWindow(FTXChan, Window);
if NC > 0 then TXASetNC(FTXChan, NC);
if MP >= 0 then TXASetMP(FTXChan, MP);
end;
procedure TWDSPEngine.SetTXCompressor(Enable: Boolean; GainDB: Double);
@@ -1870,8 +1908,8 @@ begin
FTXCompressorOn := Enable;
FTXCompressorGain := GainDB;
if not FInitialized then Exit;
SetTXACompressorGain(TXA_CHAN, GainDB);
SetTXACompressorRun(TXA_CHAN, Ord(Enable));
SetTXACompressorGain(FTXChan, GainDB);
SetTXACompressorRun(FTXChan, Ord(Enable));
end;
procedure TWDSPEngine.SetTXLeveler(Enable: Boolean; TopDB: Double; DecayMS: Integer);
@@ -1880,9 +1918,9 @@ begin
FTXLevelerTop := TopDB;
FTXLevelerDecay := DecayMS;
if not FInitialized then Exit;
SetTXALevelerTop(TXA_CHAN, TopDB);
SetTXALevelerDecay(TXA_CHAN, DecayMS);
SetTXALevelerSt(TXA_CHAN, Ord(Enable));
SetTXALevelerTop(FTXChan, TopDB);
SetTXALevelerDecay(FTXChan, DecayMS);
SetTXALevelerSt(FTXChan, Ord(Enable));
end;
procedure TWDSPEngine.SetTXALC(Enable: Boolean; MaxGainDB: Double; DecayMS: Integer);
@@ -1891,9 +1929,9 @@ begin
FTXALCMaxGain := MaxGainDB;
FTXALCDecay := DecayMS;
if not FInitialized then Exit;
SetTXAALCMaxGain(TXA_CHAN, MaxGainDB);
SetTXAALCDecay(TXA_CHAN, DecayMS);
SetTXAALCSt(TXA_CHAN, Ord(Enable));
SetTXAALCMaxGain(FTXChan, MaxGainDB);
SetTXAALCDecay(FTXChan, DecayMS);
SetTXAALCSt(FTXChan, Ord(Enable));
end;
procedure TWDSPEngine.SetTXPhaseRot(Enable: Boolean; Stages: Integer; FreqHz: Double);
@@ -1902,9 +1940,9 @@ begin
FTXPhaseRotStages := Stages;
FTXPhaseRotFreq := FreqHz;
if not FInitialized then Exit;
SetTXAPHROTNstages(TXA_CHAN, Stages);
SetTXAPHROTCorner(TXA_CHAN, FreqHz);
SetTXAPHROTRun(TXA_CHAN, Ord(Enable));
SetTXAPHROTNstages(FTXChan, Stages);
SetTXAPHROTCorner(FTXChan, FreqHz);
SetTXAPHROTRun(FTXChan, Ord(Enable));
end;
procedure TWDSPEngine.SetTXEQ(Enable: Boolean; NumBands: Integer;
@@ -1924,15 +1962,15 @@ begin
F[i] := Freqs[i];
end;
if not FInitialized then Exit;
SetTXAEQProfile(TXA_CHAN, NumBands, @F[0], @G[0]);
SetTXAEQRun(TXA_CHAN, Ord(Enable));
SetTXAEQProfile(FTXChan, NumBands, @F[0], @G[0]);
SetTXAEQRun(FTXChan, Ord(Enable));
end;
procedure TWDSPEngine.SetTXAMCarrierLevel(Level: Double);
begin
FTXAMCarrier := Level;
if not FInitialized then Exit;
WDSP.SetTXAAMCarrierLevel(TXA_CHAN, Level);
WDSP.SetTXAAMCarrierLevel(FTXChan, Level);
end;
procedure TWDSPEngine.SetTXFMParams(Deviation: Double; LowCut, HighCut, EmphPos: Integer);
@@ -1942,10 +1980,10 @@ begin
FTXFMHighCut := HighCut;
FTXFMEmphPos := EmphPos;
if not FInitialized then Exit;
SetTXAFMDeviation(TXA_CHAN, Deviation);
SetTXAFMDeviation(FTXChan, Deviation);
if LowCut < HighCut then
SetTXAFMAFFilter(TXA_CHAN, LowCut, HighCut);
SetTXAFMEmphPosition(TXA_CHAN, EmphPos);
SetTXAFMAFFilter(FTXChan, LowCut, HighCut);
SetTXAFMEmphPosition(FTXChan, EmphPos);
end;
procedure TWDSPEngine.SetTXCTCSS(Enable: Boolean; FreqHz: Double);
@@ -1953,8 +1991,8 @@ begin
FTXCTCSSOn := Enable;
FTXCTCSSFreq := FreqHz;
if not FInitialized then Exit;
SetTXACTCSSFreq(TXA_CHAN, FreqHz);
SetTXACTCSSRun(TXA_CHAN, Ord(Enable));
SetTXACTCSSFreq(FTXChan, FreqHz);
SetTXACTCSSRun(FTXChan, Ord(Enable));
end;
function TWDSPEngine.TXMicRingAvail: Integer;
@@ -2016,7 +2054,7 @@ var
begin
if not FInitialized or not FAnalyzerOpen then Exit;
N := EnsureRange(FDisplayPixelCount, 1, SPECTRUM_PIXELS);
DispID := FActiveDisplayID; // RX_DISP_ID или TX_DISP_ID, зависит от FTXActive
DispID := FActiveDisplayID; // FRXDispID или FTXDispID, зависит от FTXActive
Flag := 0;
GetPixels(DispID, 0, @PixBuf[0], @Flag);
@@ -2176,11 +2214,11 @@ begin
FSpecAvgTimeMS := AvgTimeMS;
if not FAnalyzerOpen then Exit;
AverageTimeToParams(FSpecAvgMode, FSpecAvgTimeMS, AvgCount, Backmult);
SetDisplayDetectorMode(RX_DISP_ID, 0, FSpecDetector);
SetDisplayAverageMode(RX_DISP_ID, 0, FSpecAvgMode);
SetDisplayNumAverage(RX_DISP_ID, 0, AvgCount);
SetDisplayAvBackmult(RX_DISP_ID, 0, Backmult);
SetDisplayNormOneHz(RX_DISP_ID, 0, UsePanNormOneHz(FSpecDetector));
SetDisplayDetectorMode(FRXDispID, 0, FSpecDetector);
SetDisplayAverageMode(FRXDispID, 0, FSpecAvgMode);
SetDisplayNumAverage(FRXDispID, 0, AvgCount);
SetDisplayAvBackmult(FRXDispID, 0, Backmult);
SetDisplayNormOneHz(FRXDispID, 0, UsePanNormOneHz(FSpecDetector));
end;
procedure TWDSPEngine.SetWaterfallDisplay(Detector, AvgMode: Integer;
@@ -2200,11 +2238,11 @@ begin
FWfAvgTimeMS := AvgTimeMS;
if not FAnalyzerOpen then Exit;
AverageTimeToParams(FWfAvgMode, FWfAvgTimeMS, AvgCount, Backmult);
SetDisplayDetectorMode(RX_DISP_ID, 1, FWfDetector);
SetDisplayAverageMode(RX_DISP_ID, 1, FWfAvgMode);
SetDisplayNumAverage(RX_DISP_ID, 1, AvgCount);
SetDisplayAvBackmult(RX_DISP_ID, 1, Backmult);
SetDisplayNormOneHz(RX_DISP_ID, 1, 0);
SetDisplayDetectorMode(FRXDispID, 1, FWfDetector);
SetDisplayAverageMode(FRXDispID, 1, FWfAvgMode);
SetDisplayNumAverage(FRXDispID, 1, AvgCount);
SetDisplayAvBackmult(FRXDispID, 1, Backmult);
SetDisplayNormOneHz(FRXDispID, 1, 0);
end;
end.