mirror of
https://github.com/SDRSharpR/SDRSharper.git
synced 2025-12-06 04:12:02 +01:00
184 lines
4.9 KiB
C#
184 lines
4.9 KiB
C#
using System;
|
|
|
|
namespace SDRSharp.Radio
|
|
{
|
|
public class RdsDecoder
|
|
{
|
|
private const int PllDefaultFrequency = 57000;
|
|
|
|
private const int PllRange = 12;
|
|
|
|
private const int PllBandwith = 1;
|
|
|
|
private const float PllZeta = 0.707f;
|
|
|
|
private const float PllLockTime = 0.5f;
|
|
|
|
private const float PllLockThreshold = 3.2f;
|
|
|
|
private const float RdsBitRate = 1187.5f;
|
|
|
|
private readonly IQFirFilter _baseBandFilter = new IQFirFilter(null, false, 1);
|
|
|
|
private readonly FirFilter _matchedFilter = new FirFilter();
|
|
|
|
private readonly RdsDetectorBank _bitDecoder = new RdsDetectorBank();
|
|
|
|
private unsafe readonly Pll* _pll;
|
|
|
|
private readonly UnsafeBuffer _pllBuffer;
|
|
|
|
private unsafe readonly Oscillator* _osc;
|
|
|
|
private readonly UnsafeBuffer _oscBuffer;
|
|
|
|
private unsafe readonly IirFilter* _syncFilter;
|
|
|
|
private readonly UnsafeBuffer _syncFilterBuffer;
|
|
|
|
private UnsafeBuffer _rawBuffer;
|
|
|
|
private unsafe Complex* _rawPtr;
|
|
|
|
private UnsafeBuffer _magBuffer;
|
|
|
|
private unsafe float* _magPtr;
|
|
|
|
private UnsafeBuffer _dataBuffer;
|
|
|
|
private unsafe float* _dataPtr;
|
|
|
|
private IQDecimator _decimator;
|
|
|
|
private double _sampleRate;
|
|
|
|
private double _demodulationSampleRate;
|
|
|
|
private int _decimationFactor;
|
|
|
|
private float _lastSync;
|
|
|
|
private float _lastData;
|
|
|
|
private float _lastSyncSlope;
|
|
|
|
private bool _lastBit;
|
|
|
|
public double SampleRate
|
|
{
|
|
get
|
|
{
|
|
return this._sampleRate;
|
|
}
|
|
set
|
|
{
|
|
if (value != this._sampleRate)
|
|
{
|
|
this._sampleRate = value;
|
|
this.Configure();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string RadioText => this._bitDecoder.RadioText;
|
|
|
|
public string ProgramService => this._bitDecoder.ProgramService;
|
|
|
|
public ushort PICode => this._bitDecoder.PICode;
|
|
|
|
public unsafe RdsDecoder()
|
|
{
|
|
this._pllBuffer = UnsafeBuffer.Create(sizeof(Pll));
|
|
this._pll = (Pll*)(void*)this._pllBuffer;
|
|
this._oscBuffer = UnsafeBuffer.Create(sizeof(Oscillator));
|
|
this._osc = (Oscillator*)(void*)this._oscBuffer;
|
|
this._syncFilterBuffer = UnsafeBuffer.Create(sizeof(IirFilter));
|
|
this._syncFilter = (IirFilter*)(void*)this._syncFilterBuffer;
|
|
}
|
|
|
|
private unsafe void Configure()
|
|
{
|
|
this._osc->SampleRate = this._sampleRate;
|
|
this._osc->Frequency = 57000.0;
|
|
int i;
|
|
for (i = 0; this._sampleRate >= 20000.0 * Math.Pow(2.0, (double)i); i++)
|
|
{
|
|
}
|
|
this._decimator = new IQDecimator(i, this._sampleRate, true, false);
|
|
this._decimationFactor = (int)Math.Pow(2.0, (double)i);
|
|
this._demodulationSampleRate = this._sampleRate / (double)this._decimationFactor;
|
|
float[] coefficients = FilterBuilder.MakeLowPassKernel(this._demodulationSampleRate, 200, 2500.0, WindowType.BlackmanHarris4);
|
|
this._baseBandFilter.SetCoefficients(coefficients);
|
|
this._pll->SampleRate = (float)this._demodulationSampleRate;
|
|
this._pll->DefaultFrequency = 0f;
|
|
this._pll->Range = 12f;
|
|
this._pll->Bandwidth = 1f;
|
|
this._pll->Zeta = 0.707f;
|
|
this._pll->LockTime = 0.5f;
|
|
this._pll->LockThreshold = 3.2f;
|
|
int length = (int)(this._demodulationSampleRate / 1187.5) | 1;
|
|
coefficients = FilterBuilder.MakeSin(this._demodulationSampleRate, 1187.5, length);
|
|
this._matchedFilter.SetCoefficients(coefficients);
|
|
this._syncFilter->Init(IirFilterType.BandPass, 1187.5, this._demodulationSampleRate, 500);
|
|
}
|
|
|
|
public unsafe void Reset()
|
|
{
|
|
this._bitDecoder.Reset();
|
|
this._syncFilter->Reset();
|
|
}
|
|
|
|
public unsafe void Process(float* baseBand, int length)
|
|
{
|
|
if (this._rawBuffer == null || this._rawBuffer.Length != length)
|
|
{
|
|
this._rawBuffer = UnsafeBuffer.Create(length, sizeof(Complex));
|
|
this._rawPtr = (Complex*)(void*)this._rawBuffer;
|
|
}
|
|
if (this._magBuffer == null || this._magBuffer.Length != length)
|
|
{
|
|
this._magBuffer = UnsafeBuffer.Create(length, 4);
|
|
this._magPtr = (float*)(void*)this._magBuffer;
|
|
}
|
|
if (this._dataBuffer == null || this._dataBuffer.Length != length)
|
|
{
|
|
this._dataBuffer = UnsafeBuffer.Create(length, 4);
|
|
this._dataPtr = (float*)(void*)this._dataBuffer;
|
|
}
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
this._osc->Tick();
|
|
Complex.Mul(ref this._rawPtr[i], this._osc->Out, baseBand[i]);
|
|
}
|
|
this._decimator.Process(this._rawPtr, length);
|
|
length /= this._decimationFactor;
|
|
this._baseBandFilter.Process(this._rawPtr, length);
|
|
for (int j = 0; j < length; j++)
|
|
{
|
|
this._dataPtr[j] = this._pll->Process(this._rawPtr[j]).Imag;
|
|
}
|
|
this._matchedFilter.Process(this._dataPtr, length);
|
|
for (int k = 0; k < length; k++)
|
|
{
|
|
this._magPtr[k] = Math.Abs(this._dataPtr[k]);
|
|
}
|
|
this._syncFilter->Process(this._magPtr, length);
|
|
for (int l = 0; l < length; l++)
|
|
{
|
|
float lastData = this._dataPtr[l];
|
|
float num = this._magPtr[l];
|
|
float num2 = num - this._lastSync;
|
|
this._lastSync = num;
|
|
if (num2 < 0f && this._lastSyncSlope * num2 < 0f)
|
|
{
|
|
bool flag = this._lastData > 0f;
|
|
this._bitDecoder.Process(flag ^ this._lastBit);
|
|
this._lastBit = flag;
|
|
}
|
|
this._lastData = lastData;
|
|
this._lastSyncSlope = num2;
|
|
}
|
|
}
|
|
}
|
|
}
|