mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
rx2 support work
This commit is contained in:
+67
-12
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user