mirror of
https://github.com/SDRSharpR/SDRSharper.git
synced 2026-01-07 08:49:57 +01:00
1110 lines
26 KiB
C#
1110 lines
26 KiB
C#
using System;
|
|
|
|
namespace SDRSharp.Radio
|
|
{
|
|
public sealed class Vfo
|
|
{
|
|
private const float TimeConst = 0.001f;
|
|
|
|
public const int DefaultCwSideTone = 600;
|
|
|
|
public const int DefaultSSBBandwidth = 2400;
|
|
|
|
public const int DefaultWFMBandwidth = 180000;
|
|
|
|
public const int MinSSBAudioFrequency = 400;
|
|
|
|
public const int MinBCAudioFrequency = 20;
|
|
|
|
public const int MaxBCAudioFrequency = 16000;
|
|
|
|
public const int MaxNFMBandwidth = 15000;
|
|
|
|
public const int MinNFMAudioFrequency = 300;
|
|
|
|
public const int EnvelopeFrequency = 6000;
|
|
|
|
private readonly AutomaticGainControl _agc = new AutomaticGainControl();
|
|
|
|
private readonly AutomaticGainControl _agcX = new AutomaticGainControl();
|
|
|
|
private readonly AutomaticGainControl _agcY = new AutomaticGainControl();
|
|
|
|
private readonly AutomaticGainControl _agcEnv = new AutomaticGainControl();
|
|
|
|
private readonly DownConverter _downConverter = new DownConverter();
|
|
|
|
private readonly DownConverter _downConverter2 = new DownConverter();
|
|
|
|
private readonly AmDetector _amDetector = new AmDetector();
|
|
|
|
private readonly SamDetector _samDetector = new SamDetector();
|
|
|
|
private readonly FmDetector _fmDetector = new FmDetector();
|
|
|
|
private readonly PmDetector _pmDetector = new PmDetector();
|
|
|
|
private readonly SsbDetector _lsbDetector = new SsbDetector(SsbDetector.Mode.LSB);
|
|
|
|
private readonly SsbDetector _usbDetector = new SsbDetector(SsbDetector.Mode.USB);
|
|
|
|
private readonly CwDetector _cwDetector = new CwDetector();
|
|
|
|
private readonly DsbDetector _dsbDetector = new DsbDetector();
|
|
|
|
private readonly StereoDecoder _stereoDecoder = new StereoDecoder();
|
|
|
|
private readonly RdsDecoder _rdsDecoder = new RdsDecoder();
|
|
|
|
private IQFirFilter _realIqFilter;
|
|
|
|
private CpxFirFilter _cpxIqFilter;
|
|
|
|
private readonly FirFilter _audioFilter = new FirFilter();
|
|
|
|
private readonly FirFilter _rFilter = new FirFilter();
|
|
|
|
private readonly FirFilter _lFilter = new FirFilter();
|
|
|
|
private readonly VfoHookManager _hookManager;
|
|
|
|
private CpxBandFilter _envFilter = new CpxBandFilter();
|
|
|
|
private CpxBandFilter[] _notchFilter = new CpxBandFilter[4]
|
|
{
|
|
new CpxBandFilter(),
|
|
new CpxBandFilter(),
|
|
new CpxBandFilter(),
|
|
new CpxBandFilter()
|
|
};
|
|
|
|
private int _notch = -1;
|
|
|
|
private int _notchWidth;
|
|
|
|
private int _notchFrequency;
|
|
|
|
private DcRemover _dcRemover = new DcRemover(0.001f);
|
|
|
|
private IQDecimator _baseBandDecimator;
|
|
|
|
private IQDecimator _envelopeDecimator;
|
|
|
|
private DetectorType _detectorType;
|
|
|
|
private DetectorType _actualDetectorType;
|
|
|
|
private WindowType _windowType;
|
|
|
|
private double _sampleRate;
|
|
|
|
private int _bandwidth;
|
|
|
|
private int _frequency;
|
|
|
|
private int _frequencyOffset;
|
|
|
|
private int _filterOrder;
|
|
|
|
private bool _needNewFilters;
|
|
|
|
private int _decimationStageCount;
|
|
|
|
private int _baseBandDecimationStageCount;
|
|
|
|
private int _audioDecimationStageCount;
|
|
|
|
private int _envelopeDecimationStageCount;
|
|
|
|
private bool _needNewDecimators;
|
|
|
|
private bool _decimationModeHasChanged;
|
|
|
|
private int _cwToneShift;
|
|
|
|
private bool _needConfigure;
|
|
|
|
private bool _useAgc;
|
|
|
|
private float _agcThreshold;
|
|
|
|
private float _agcDecay;
|
|
|
|
private float _agcSlope;
|
|
|
|
private bool _agcUseHang;
|
|
|
|
private int _squelchThreshold;
|
|
|
|
private bool _stereo;
|
|
|
|
private bool _filterAudio;
|
|
|
|
private UnsafeBuffer _rawAudioBuffer;
|
|
|
|
private unsafe float* _rawAudioPtr;
|
|
|
|
private UnsafeBuffer _envBuf;
|
|
|
|
private unsafe Complex* _envPtr;
|
|
|
|
private UnsafeBuffer _rBuf;
|
|
|
|
private UnsafeBuffer _lBuf;
|
|
|
|
private unsafe float* _rPtr;
|
|
|
|
private unsafe float* _lPtr;
|
|
|
|
private DemodType _demodX;
|
|
|
|
private DemodType _demodY;
|
|
|
|
private float _rfGain = 0.05f;
|
|
|
|
public DetectorType DetectorType
|
|
{
|
|
get
|
|
{
|
|
return this._detectorType;
|
|
}
|
|
set
|
|
{
|
|
if (value != this._detectorType)
|
|
{
|
|
this._decimationModeHasChanged = ((this._detectorType == DetectorType.WFM && value != DetectorType.WFM) || (this._detectorType != DetectorType.WFM && value == DetectorType.WFM));
|
|
if (this._decimationModeHasChanged)
|
|
{
|
|
this._needNewDecimators = true;
|
|
}
|
|
this._detectorType = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public double RFgain
|
|
{
|
|
set
|
|
{
|
|
this._rfGain = (float)Math.Pow(10.0, value / 20.0);
|
|
}
|
|
}
|
|
|
|
public float FreqErr => this._pmDetector.FreqErr;
|
|
|
|
public DemodType DemodX
|
|
{
|
|
get
|
|
{
|
|
return this._demodX;
|
|
}
|
|
set
|
|
{
|
|
this._demodX = value;
|
|
}
|
|
}
|
|
|
|
public DemodType DemodY
|
|
{
|
|
get
|
|
{
|
|
return this._demodY;
|
|
}
|
|
set
|
|
{
|
|
this._demodY = value;
|
|
}
|
|
}
|
|
|
|
public int Frequency
|
|
{
|
|
get
|
|
{
|
|
return this._frequency;
|
|
}
|
|
set
|
|
{
|
|
if (this._frequency != value)
|
|
{
|
|
this._frequency = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public double DownConverterFrequency => this._downConverter.Frequency;
|
|
|
|
public int FilterOrder
|
|
{
|
|
get
|
|
{
|
|
return this._filterOrder;
|
|
}
|
|
set
|
|
{
|
|
if (this._filterOrder != value)
|
|
{
|
|
this._filterOrder = value;
|
|
this._needNewFilters = true;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public double SampleRate
|
|
{
|
|
get
|
|
{
|
|
return this._sampleRate;
|
|
}
|
|
set
|
|
{
|
|
if (this._sampleRate != value)
|
|
{
|
|
this._sampleRate = value;
|
|
this._needNewDecimators = true;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public WindowType WindowType
|
|
{
|
|
get
|
|
{
|
|
return this._windowType;
|
|
}
|
|
set
|
|
{
|
|
if (this._windowType != value)
|
|
{
|
|
this._windowType = value;
|
|
this._needNewFilters = true;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int Bandwidth
|
|
{
|
|
get
|
|
{
|
|
return this._bandwidth;
|
|
}
|
|
set
|
|
{
|
|
if (this._bandwidth != value)
|
|
{
|
|
this._bandwidth = value;
|
|
this._needNewFilters = true;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int FrequencyOffset
|
|
{
|
|
get
|
|
{
|
|
return this._frequency;
|
|
}
|
|
set
|
|
{
|
|
if (this._frequencyOffset != value)
|
|
{
|
|
this._frequencyOffset = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool UseAGC
|
|
{
|
|
get
|
|
{
|
|
return this._useAgc;
|
|
}
|
|
set
|
|
{
|
|
this._useAgc = value;
|
|
}
|
|
}
|
|
|
|
public float AgcThreshold
|
|
{
|
|
get
|
|
{
|
|
return this._agcThreshold;
|
|
}
|
|
set
|
|
{
|
|
if (this._agcThreshold != value)
|
|
{
|
|
this._agcThreshold = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public float AgcDecay
|
|
{
|
|
get
|
|
{
|
|
return this._agcDecay;
|
|
}
|
|
set
|
|
{
|
|
if (this._agcDecay != value)
|
|
{
|
|
this._agcDecay = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public float AgcSlope
|
|
{
|
|
get
|
|
{
|
|
return this._agcSlope;
|
|
}
|
|
set
|
|
{
|
|
if (this._agcSlope != value)
|
|
{
|
|
this._agcSlope = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool AgcHang
|
|
{
|
|
get
|
|
{
|
|
return this._agcUseHang;
|
|
}
|
|
set
|
|
{
|
|
if (this._agcUseHang != value)
|
|
{
|
|
this._agcUseHang = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SquelchThreshold
|
|
{
|
|
get
|
|
{
|
|
return this._squelchThreshold;
|
|
}
|
|
set
|
|
{
|
|
if (this._squelchThreshold != value)
|
|
{
|
|
this._squelchThreshold = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsSquelchOpen
|
|
{
|
|
get
|
|
{
|
|
if (this._actualDetectorType == DetectorType.NFM && this._fmDetector.IsSquelchOpen)
|
|
{
|
|
goto IL_0042;
|
|
}
|
|
if (this._actualDetectorType == DetectorType.AM && this._amDetector.IsSquelchOpen)
|
|
{
|
|
goto IL_0042;
|
|
}
|
|
if (this._actualDetectorType == DetectorType.SAM)
|
|
{
|
|
return this._amDetector.IsSquelchOpen;
|
|
}
|
|
return false;
|
|
IL_0042:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public int DecimationStageCount
|
|
{
|
|
get
|
|
{
|
|
return this._decimationStageCount;
|
|
}
|
|
set
|
|
{
|
|
if (this._decimationStageCount != value)
|
|
{
|
|
this._decimationStageCount = Math.Abs(value);
|
|
this._needNewDecimators = true;
|
|
this._needConfigure = true;
|
|
this.setDecimationCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int BaseBandDecimationStageCount => this._baseBandDecimationStageCount;
|
|
|
|
public int EnvelopeDecimationStageCount => this._envelopeDecimationStageCount;
|
|
|
|
public int CWToneShift
|
|
{
|
|
get
|
|
{
|
|
return this._cwToneShift;
|
|
}
|
|
set
|
|
{
|
|
if (this._cwToneShift != value)
|
|
{
|
|
this._cwToneShift = value;
|
|
this._needNewFilters = true;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Stereo
|
|
{
|
|
get
|
|
{
|
|
return this._stereo;
|
|
}
|
|
set
|
|
{
|
|
if (this._stereo != value)
|
|
{
|
|
this._stereo = value;
|
|
this._needConfigure = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool SignalIsStereo
|
|
{
|
|
get
|
|
{
|
|
if (this._actualDetectorType == DetectorType.WFM && this._stereo)
|
|
{
|
|
return this._stereoDecoder.IsPllLocked;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public string RdsStationName => this._rdsDecoder.ProgramService;
|
|
|
|
public string RdsStationText => this._rdsDecoder.RadioText;
|
|
|
|
public bool FilterAudio
|
|
{
|
|
get
|
|
{
|
|
return this._filterAudio;
|
|
}
|
|
set
|
|
{
|
|
this._filterAudio = value;
|
|
this._stereoDecoder.DoFiltering = value;
|
|
}
|
|
}
|
|
|
|
public Vfo(VfoHookManager hookManager = null)
|
|
{
|
|
this._hookManager = hookManager;
|
|
this._bandwidth = 2400;
|
|
this._filterOrder = 500;
|
|
this._needConfigure = true;
|
|
}
|
|
|
|
public void SetNotch(int notch, int offset, int width)
|
|
{
|
|
if (this._notchFilter[notch] != null && this._notch == notch && this._notchFrequency == this._frequency + offset && this._notchWidth == width)
|
|
{
|
|
return;
|
|
}
|
|
this._notch = notch;
|
|
this._notchFrequency = offset;
|
|
this._notchWidth = width;
|
|
this._needConfigure = true;
|
|
this._needNewFilters = true;
|
|
}
|
|
|
|
public void RdsReset()
|
|
{
|
|
this._rdsDecoder.Reset();
|
|
}
|
|
|
|
private void configure()
|
|
{
|
|
this._actualDetectorType = this._detectorType;
|
|
this._downConverter.SampleRate = this._sampleRate;
|
|
this._downConverter.Frequency = (double)this._frequency;
|
|
this._downConverter2.SampleRate = this._sampleRate;
|
|
this._downConverter2.Frequency = (double)(this._frequency - 6000);
|
|
if (this._needNewDecimators)
|
|
{
|
|
this.setDecimationCount();
|
|
this._baseBandDecimator = new IQDecimator(this._baseBandDecimationStageCount, this._sampleRate, false, Utils.ProcessorCount > 1);
|
|
this._envelopeDecimator = new IQDecimator(this._envelopeDecimationStageCount, this._sampleRate, false, Utils.ProcessorCount > 1);
|
|
if (this._hookManager != null)
|
|
{
|
|
this._hookManager.SetProcessorSampleRate(ProcessorType.RawIQ, this._sampleRate);
|
|
this._hookManager.SetProcessorSampleRate(ProcessorType.FrequencyTranslatedIQ, this._sampleRate);
|
|
this._hookManager.SetProcessorSampleRate(ProcessorType.DecimatedAndFilteredIQ, this._sampleRate / (double)(1 << this._baseBandDecimationStageCount));
|
|
this._hookManager.SetProcessorSampleRate(ProcessorType.DemodulatorOutput, this._sampleRate / (double)(1 << this._baseBandDecimationStageCount));
|
|
this._hookManager.SetProcessorSampleRate(ProcessorType.FilteredAudioOutput, this._sampleRate / (double)(1 << this._decimationStageCount));
|
|
}
|
|
this._needNewFilters = true;
|
|
}
|
|
if (this._needNewFilters)
|
|
{
|
|
this.initFilters();
|
|
this._samDetector.Range = (float)(this._bandwidth / 2);
|
|
}
|
|
if (this._needNewDecimators)
|
|
{
|
|
double num = this._sampleRate / Math.Pow(2.0, (double)this._baseBandDecimationStageCount);
|
|
this._usbDetector.SampleRate = num;
|
|
this._lsbDetector.SampleRate = num;
|
|
this._cwDetector.SampleRate = num;
|
|
this._fmDetector.SampleRate = num;
|
|
this._pmDetector.SampleRate = num;
|
|
this._samDetector.SampleRate = (float)num;
|
|
this._samDetector.BandWidth = 500f;
|
|
this._samDetector.LockTime = 2f;
|
|
this._samDetector.LockTreshold = 3f;
|
|
this._stereoDecoder.Configure(this._fmDetector.SampleRate, this._audioDecimationStageCount);
|
|
this._rdsDecoder.SampleRate = this._fmDetector.SampleRate;
|
|
}
|
|
this._fmDetector.SquelchThreshold = this._squelchThreshold;
|
|
this._amDetector.SquelchThreshold = this._squelchThreshold;
|
|
this._stereoDecoder.ForceMono = !this._stereo;
|
|
switch (this._actualDetectorType)
|
|
{
|
|
case DetectorType.AM:
|
|
case DetectorType.DSB:
|
|
case DetectorType.SAM:
|
|
this._downConverter.Frequency += (double)this._frequencyOffset;
|
|
break;
|
|
case DetectorType.USB:
|
|
this._usbDetector.BfoFrequency = -this._bandwidth / 2;
|
|
this._downConverter.Frequency -= (double)this._usbDetector.BfoFrequency;
|
|
break;
|
|
case DetectorType.LSB:
|
|
this._lsbDetector.BfoFrequency = -this._bandwidth / 2;
|
|
this._downConverter.Frequency += (double)this._lsbDetector.BfoFrequency;
|
|
break;
|
|
case DetectorType.CW:
|
|
this._cwDetector.BfoFrequency = this._cwToneShift;
|
|
this._downConverter.Frequency += (double)this._frequencyOffset;
|
|
break;
|
|
case DetectorType.NFM:
|
|
this._fmDetector.Mode = FmMode.Narrow;
|
|
this._downConverter.Frequency += (double)this._frequencyOffset;
|
|
break;
|
|
case DetectorType.WFM:
|
|
this._fmDetector.Mode = FmMode.Wide;
|
|
this._downConverter.Frequency += (double)this._frequencyOffset;
|
|
break;
|
|
}
|
|
this._agc.SampleRate = this._sampleRate / Math.Pow(2.0, (double)this._decimationStageCount);
|
|
this._agc.Decay = this._agcDecay;
|
|
this._agc.Slope = this._agcSlope;
|
|
this._agc.Threshold = this._agcThreshold;
|
|
this._agc.UseHang = this._agcUseHang;
|
|
this._agcX.SampleRate = this._sampleRate / Math.Pow(2.0, (double)this._decimationStageCount);
|
|
this._agcX.Decay = this._agcDecay;
|
|
this._agcX.Slope = this._agcSlope;
|
|
this._agcX.Threshold = this._agcThreshold;
|
|
this._agcX.UseHang = this._agcUseHang;
|
|
this._agcY.SampleRate = this._sampleRate / Math.Pow(2.0, (double)this._decimationStageCount);
|
|
this._agcY.Decay = this._agcDecay;
|
|
this._agcY.Slope = this._agcSlope;
|
|
this._agcY.Threshold = this._agcThreshold;
|
|
this._agcY.UseHang = this._agcUseHang;
|
|
this._agcEnv.SampleRate = this._sampleRate / Math.Pow(2.0, (double)this._decimationStageCount);
|
|
this._agcEnv.Decay = this._agcDecay;
|
|
this._agcEnv.Slope = this._agcSlope;
|
|
this._agcEnv.Threshold = this._agcThreshold;
|
|
this._agcEnv.UseHang = this._agcUseHang;
|
|
this._decimationModeHasChanged = false;
|
|
this._needNewDecimators = false;
|
|
this._needNewFilters = false;
|
|
}
|
|
|
|
private void setDecimationCount()
|
|
{
|
|
if (this._actualDetectorType == DetectorType.WFM)
|
|
{
|
|
double num = this._sampleRate / Math.Pow(2.0, (double)this._decimationStageCount);
|
|
this._audioDecimationStageCount = 0;
|
|
while (num * Math.Pow(2.0, (double)this._audioDecimationStageCount) < 180000.0 && this._audioDecimationStageCount < this._decimationStageCount)
|
|
{
|
|
this._audioDecimationStageCount++;
|
|
}
|
|
this._baseBandDecimationStageCount = this._decimationStageCount - this._audioDecimationStageCount;
|
|
}
|
|
else
|
|
{
|
|
this._baseBandDecimationStageCount = this._decimationStageCount;
|
|
this._audioDecimationStageCount = 0;
|
|
}
|
|
long num2 = (long)this._sampleRate;
|
|
this._envelopeDecimationStageCount = 0;
|
|
while (this._envelopeDecimationStageCount < 16)
|
|
{
|
|
num2 /= 2;
|
|
if (num2 < 48000)
|
|
{
|
|
break;
|
|
}
|
|
this._envelopeDecimationStageCount++;
|
|
}
|
|
Console.WriteLine("decimation=" + this._decimationStageCount.ToString() + ", basebandDecimation=" + this._baseBandDecimationStageCount.ToString() + ", audioDecimation=" + this._audioDecimationStageCount.ToString());
|
|
}
|
|
|
|
private void initFilters()
|
|
{
|
|
int num = this._bandwidth / 2;
|
|
int filterOrder = (this._actualDetectorType == DetectorType.WFM) ? 60 : this._filterOrder;
|
|
float[] coefficients = FilterBuilder.MakeLowPassKernel(this._sampleRate / Math.Pow(2.0, (double)this._baseBandDecimationStageCount), filterOrder, (double)num, this._windowType);
|
|
if (this._realIqFilter == null)
|
|
{
|
|
this._realIqFilter = new IQFirFilter(coefficients, this._actualDetectorType == DetectorType.WFM, 1);
|
|
}
|
|
else
|
|
{
|
|
this._realIqFilter.SetCoefficients(coefficients);
|
|
}
|
|
if (this._cpxIqFilter == null)
|
|
{
|
|
this._cpxIqFilter = new CpxFirFilter(coefficients);
|
|
}
|
|
else
|
|
{
|
|
this._cpxIqFilter.SetCoefficients(coefficients);
|
|
}
|
|
this._envFilter.MakeCoefficients(this._sampleRate / Math.Pow(2.0, (double)this._envelopeDecimationStageCount), 6000, num, this._windowType, false);
|
|
if (this._notch >= 0)
|
|
{
|
|
this._notchFilter[this._notch].MakeCoefficients(this._sampleRate / Math.Pow(2.0, (double)this._baseBandDecimationStageCount), this._notchFrequency, this._notchWidth, this._windowType, true);
|
|
this._notch = -1;
|
|
}
|
|
int num2 = 0;
|
|
int num3 = 10000;
|
|
switch (this._actualDetectorType)
|
|
{
|
|
case DetectorType.AM:
|
|
case DetectorType.SAM:
|
|
num2 = 20;
|
|
num3 = Math.Min(this._bandwidth / 2, 16000);
|
|
break;
|
|
case DetectorType.CW:
|
|
num2 = this._cwToneShift - this._bandwidth / 2;
|
|
num3 = this._cwToneShift + this._bandwidth / 2;
|
|
break;
|
|
case DetectorType.LSB:
|
|
case DetectorType.USB:
|
|
num2 = 400;
|
|
num3 = this._bandwidth;
|
|
break;
|
|
case DetectorType.DSB:
|
|
num2 = 400;
|
|
num3 = this._bandwidth / 2;
|
|
break;
|
|
case DetectorType.NFM:
|
|
num2 = 300;
|
|
num3 = this._bandwidth / 2;
|
|
break;
|
|
}
|
|
coefficients = FilterBuilder.MakeBandPassKernel(this._sampleRate / Math.Pow(2.0, (double)(this._baseBandDecimationStageCount + this._audioDecimationStageCount)), this._filterOrder, (double)num2, (double)num3, this._windowType);
|
|
this._audioFilter.SetCoefficients(coefficients);
|
|
this._rFilter.SetCoefficients(coefficients);
|
|
this._lFilter.SetCoefficients(coefficients);
|
|
}
|
|
|
|
public unsafe int ProcessBuffer(Complex* iqBuffer, float* audioBuffer, int iqLen, float* xBuf, float* yBuf, float* envelopBuf)
|
|
{
|
|
if (this._needConfigure)
|
|
{
|
|
this.configure();
|
|
this._needConfigure = false;
|
|
}
|
|
if (this._demodX == DemodType.Envelope || this._demodY == DemodType.Envelope || envelopBuf != null)
|
|
{
|
|
if (this._envBuf == null || this._envBuf.Length != iqLen)
|
|
{
|
|
if (this._envBuf != null)
|
|
{
|
|
this._envBuf.Dispose();
|
|
}
|
|
this._envBuf = UnsafeBuffer.Create(iqLen, sizeof(Complex));
|
|
this._envPtr = (Complex*)(void*)this._envBuf;
|
|
}
|
|
Utils.Memcpy(this._envPtr, iqBuffer, iqLen * sizeof(Complex));
|
|
this._downConverter2.Process(this._envPtr, iqLen);
|
|
int num = iqLen;
|
|
if (this._envelopeDecimator.StageCount > 0)
|
|
{
|
|
this._envelopeDecimator.Process(this._envPtr, iqLen);
|
|
num = iqLen >> this._envelopeDecimationStageCount;
|
|
}
|
|
this._envFilter.Process(this._envPtr, num);
|
|
if (this._demodX == DemodType.Envelope)
|
|
{
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
xBuf[i] = this._envPtr[i].Real * 0.005f;
|
|
}
|
|
this.doAgcAndGain(this._agcX, xBuf, num);
|
|
if (this._useAgc)
|
|
{
|
|
for (int j = 0; j < num; j++)
|
|
{
|
|
xBuf[j] *= 0.33f;
|
|
}
|
|
}
|
|
}
|
|
if (this._demodY == DemodType.Envelope)
|
|
{
|
|
for (int k = 0; k < num; k++)
|
|
{
|
|
yBuf[k] = this._envPtr[k].Imag * 0.005f;
|
|
}
|
|
this.doAgcAndGain(this._agcY, yBuf, num);
|
|
if (this._useAgc)
|
|
{
|
|
for (int l = 0; l < num; l++)
|
|
{
|
|
yBuf[l] *= 0.33f;
|
|
}
|
|
}
|
|
}
|
|
if (envelopBuf != null)
|
|
{
|
|
for (int m = 0; m < num; m++)
|
|
{
|
|
envelopBuf[m] = this._envPtr[m].Imag * 0.005f;
|
|
}
|
|
this.doAgcAndGain(this._agcEnv, envelopBuf, num);
|
|
if (this._useAgc)
|
|
{
|
|
for (int n = 0; n < num; n++)
|
|
{
|
|
envelopBuf[n] *= 0.33f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._downConverter.Process(iqBuffer, iqLen);
|
|
if (this._hookManager != null)
|
|
{
|
|
this._hookManager.ProcessFrequencyTranslatedIQ(iqBuffer, iqLen);
|
|
}
|
|
int num2 = iqLen;
|
|
if (this._baseBandDecimator.StageCount > 0)
|
|
{
|
|
this._baseBandDecimator.Process(iqBuffer, iqLen);
|
|
num2 = iqLen >> this._baseBandDecimationStageCount;
|
|
}
|
|
if (!Utils.FastConvolve)
|
|
{
|
|
this._realIqFilter.Process(iqBuffer, num2);
|
|
}
|
|
else
|
|
{
|
|
this._cpxIqFilter.Process(iqBuffer, num2);
|
|
}
|
|
if (this._hookManager != null)
|
|
{
|
|
this._hookManager.ProcessDecimatedAndFilteredIQ(iqBuffer, num2);
|
|
}
|
|
for (int num3 = 0; num3 <= 3; num3++)
|
|
{
|
|
if (this._notchFilter[num3] != null && this._notchFilter[num3].Width > 0)
|
|
{
|
|
this._notchFilter[num3].Process(iqBuffer, num2);
|
|
}
|
|
}
|
|
if (this._actualDetectorType == DetectorType.RAW)
|
|
{
|
|
Utils.Memcpy(audioBuffer, iqBuffer, num2 * sizeof(Complex));
|
|
}
|
|
else
|
|
{
|
|
if (this._rawAudioBuffer == null || this._rawAudioBuffer.Length != num2)
|
|
{
|
|
if (this._rawAudioBuffer != null)
|
|
{
|
|
this._rawAudioBuffer.Dispose();
|
|
}
|
|
this._rawAudioBuffer = UnsafeBuffer.Create(num2, 4);
|
|
this._rawAudioPtr = (float*)(void*)this._rawAudioBuffer;
|
|
}
|
|
if (this.DetectorType == DetectorType.SAM && (this._rBuf == null || this._rBuf.Length != num2))
|
|
{
|
|
if (this._rBuf != null)
|
|
{
|
|
this._rBuf.Dispose();
|
|
}
|
|
if (this._lBuf != null)
|
|
{
|
|
this._lBuf.Dispose();
|
|
}
|
|
this._rBuf = UnsafeBuffer.Create(num2, 4);
|
|
this._lBuf = UnsafeBuffer.Create(num2, 4);
|
|
this._rPtr = (float*)(void*)this._rBuf;
|
|
this._lPtr = (float*)(void*)this._lBuf;
|
|
}
|
|
if (this._actualDetectorType != DetectorType.WFM)
|
|
{
|
|
Vfo.scaleIQ(iqBuffer, num2);
|
|
}
|
|
}
|
|
int num4 = num2 * 2;
|
|
this.demodulate(iqBuffer, xBuf, yBuf, num2);
|
|
if (this._demodX == DemodType.AM)
|
|
{
|
|
this.doAgcAndGain(this._agcX, xBuf, num2);
|
|
}
|
|
if (this._demodY == DemodType.AM)
|
|
{
|
|
this.doAgcAndGain(this._agcY, yBuf, num2);
|
|
}
|
|
if (this._actualDetectorType == DetectorType.RAW)
|
|
{
|
|
return num2;
|
|
}
|
|
if (this._actualDetectorType == DetectorType.SAM && this._stereo)
|
|
{
|
|
if (Utils.Chk1)
|
|
{
|
|
if (this._filterAudio)
|
|
{
|
|
this._rFilter.Process(this._rPtr, num2);
|
|
}
|
|
this.doAgcAndGain(this._agcX, this._rPtr, num2);
|
|
if (this._filterAudio)
|
|
{
|
|
this._lFilter.Process(this._lPtr, num2);
|
|
}
|
|
this.doAgcAndGain(this._agcY, this._lPtr, num2);
|
|
}
|
|
int num5 = 0;
|
|
for (int num6 = 0; num6 < num2; num6++)
|
|
{
|
|
audioBuffer[num5++] = this._rPtr[num6];
|
|
audioBuffer[num5++] = this._lPtr[num6];
|
|
}
|
|
if (!Utils.Chk1)
|
|
{
|
|
if (this._filterAudio)
|
|
{
|
|
this._audioFilter.Process(audioBuffer, num2 * 2);
|
|
}
|
|
this.doAgcAndGain(this._agc, audioBuffer, num2 * 2);
|
|
}
|
|
return num2;
|
|
}
|
|
if (this._hookManager != null)
|
|
{
|
|
this._hookManager.ProcessDemodulatorOutput(this._rawAudioPtr, num2);
|
|
}
|
|
if (this._actualDetectorType != DetectorType.WFM)
|
|
{
|
|
if (this._filterAudio)
|
|
{
|
|
this._audioFilter.Process(this._rawAudioPtr, num2);
|
|
}
|
|
if (this._actualDetectorType != 0)
|
|
{
|
|
this.doAgcOrGain(this._agc, this._rawAudioPtr, num2);
|
|
}
|
|
}
|
|
if (this._actualDetectorType == DetectorType.AM)
|
|
{
|
|
this._dcRemover.Process(this._rawAudioPtr, num2);
|
|
}
|
|
if (this._actualDetectorType != DetectorType.WFM)
|
|
{
|
|
Vfo.monoToStereo(this._rawAudioPtr, audioBuffer, num2);
|
|
}
|
|
else
|
|
{
|
|
this._rdsDecoder.Process(this._rawAudioPtr, num2);
|
|
this._stereoDecoder.Process(this._rawAudioPtr, audioBuffer, num2);
|
|
num4 >>= this._audioDecimationStageCount;
|
|
}
|
|
if (this._hookManager != null)
|
|
{
|
|
this._hookManager.ProcessFilteredAudioOutput(audioBuffer, num4);
|
|
}
|
|
return num2;
|
|
}
|
|
|
|
private unsafe void doAgcOrGain(AutomaticGainControl agc, float* buffer, int length)
|
|
{
|
|
if (this._useAgc)
|
|
{
|
|
agc.Process(buffer, length);
|
|
}
|
|
else
|
|
{
|
|
this.DoRFgain(buffer, length);
|
|
}
|
|
}
|
|
|
|
private unsafe void doAgcAndGain(AutomaticGainControl agc, float* buffer, int length)
|
|
{
|
|
if (this._useAgc)
|
|
{
|
|
agc.Process(buffer, length);
|
|
}
|
|
this.DoRFgain(buffer, length);
|
|
}
|
|
|
|
private unsafe void DoRFgain(float* buffer, int length)
|
|
{
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
buffer[i] *= this._rfGain;
|
|
}
|
|
}
|
|
|
|
private unsafe static void scaleIQ(Complex* buffer, int length)
|
|
{
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
buffer[i].Real *= 0.01f;
|
|
buffer[i].Imag *= 0.01f;
|
|
}
|
|
}
|
|
|
|
private unsafe static void monoToStereo(float* input, float* output, int inputLength)
|
|
{
|
|
for (int i = 0; i < inputLength; i++)
|
|
{
|
|
float* intPtr = output;
|
|
output = intPtr + 1;
|
|
*intPtr = *input;
|
|
float* intPtr2 = output;
|
|
output = intPtr2 + 1;
|
|
*intPtr2 = *input;
|
|
input++;
|
|
}
|
|
}
|
|
|
|
private unsafe void demodulate(Complex* iq, float* xBuf, float* yBuf, int length)
|
|
{
|
|
switch (this._demodX)
|
|
{
|
|
case DemodType.AM:
|
|
this._amDetector.Demodulate(iq, xBuf, length);
|
|
break;
|
|
case DemodType.FM:
|
|
this._fmDetector.Demodulate(iq, xBuf, length);
|
|
break;
|
|
case DemodType.PM:
|
|
this._pmDetector.Demodulate(iq, xBuf, length);
|
|
break;
|
|
case DemodType.IQ:
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
xBuf[i] = iq[i].Real;
|
|
}
|
|
break;
|
|
}
|
|
if (this._demodY == this._demodX && this.DemodX != 0)
|
|
{
|
|
Utils.Memcpy(yBuf, xBuf, length * 4);
|
|
}
|
|
else
|
|
{
|
|
switch (this._demodY)
|
|
{
|
|
case DemodType.AM:
|
|
this._amDetector.Demodulate(iq, yBuf, length);
|
|
break;
|
|
case DemodType.FM:
|
|
this._fmDetector.Demodulate(iq, yBuf, length);
|
|
break;
|
|
case DemodType.PM:
|
|
this._pmDetector.Demodulate(iq, yBuf, length);
|
|
break;
|
|
case DemodType.IQ:
|
|
for (int j = 0; j < length; j++)
|
|
{
|
|
yBuf[j] = iq[j].Real;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
switch (this._actualDetectorType)
|
|
{
|
|
case DetectorType.RAW:
|
|
break;
|
|
case DetectorType.NFM:
|
|
case DetectorType.WFM:
|
|
if (this._demodX == DemodType.FM)
|
|
{
|
|
Utils.Memcpy(this._rawAudioPtr, xBuf, length * 4);
|
|
}
|
|
else if (this._demodY == DemodType.FM)
|
|
{
|
|
Utils.Memcpy(this._rawAudioPtr, yBuf, length * 4);
|
|
}
|
|
else
|
|
{
|
|
this._fmDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
}
|
|
break;
|
|
case DetectorType.AM:
|
|
if (this._demodX == DemodType.AM)
|
|
{
|
|
Utils.Memcpy(this._rawAudioPtr, xBuf, length * 4);
|
|
}
|
|
else if (this._demodY == DemodType.AM)
|
|
{
|
|
Utils.Memcpy(this._rawAudioPtr, yBuf, length * 4);
|
|
}
|
|
else
|
|
{
|
|
this._amDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
}
|
|
break;
|
|
case DetectorType.SAM:
|
|
if (!this._stereo)
|
|
{
|
|
this._samDetector.Demodulate(iq, this._rawAudioPtr, length, false, false);
|
|
}
|
|
else
|
|
{
|
|
this._samDetector.Demodulate(iq, this._rPtr, this._lPtr, length);
|
|
}
|
|
break;
|
|
case DetectorType.DSB:
|
|
this._dsbDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
break;
|
|
case DetectorType.LSB:
|
|
this._lsbDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
break;
|
|
case DetectorType.USB:
|
|
this._usbDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
break;
|
|
case DetectorType.CW:
|
|
this._cwDetector.Demodulate(iq, this._rawAudioPtr, length);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|