Skip to content

Commit 2cfb8e8

Browse files
committed
Добавлены расширения фильтров FIR и IIR для чередующихся выборок IQ int16 с малым порядком следования
- Реализованы расширения SampleIQInt16LeFirFilterExtensions с методами фильтрации FIR чередующихся данных IQ int16 с малым порядком следования. - Реализованы расширения SampleIQInt16LeIirFilterExtensions с методами внутренней фильтрации чередующихся данных IQ int16 в порядке убывания. - Добавлены модульные тесты для расширений фильтрации FIR и IIR для обеспечения корректности и проверки на соответствие независимой фильтрации каналов. - Включены тесты для потоковой обработки и асинхронных методов фильтрации. - Добавлена проверка длины входного буфера, чтобы убедиться, что она кратна 4.
1 parent ecb46d7 commit 2cfb8e8

8 files changed

Lines changed: 1133 additions & 0 deletions

File tree

MathCore.DSP/Filters/ComplexFIR.cs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System.Collections.ObjectModel;
2+
3+
using static System.Array;
4+
5+
namespace MathCore.DSP.Filters;
6+
7+
/// <summary>Комплексный фильтр с конечной импульсной характеристикой для IQ-потока</summary>
8+
public class ComplexFIR
9+
{
10+
/// <summary>Импульсная характеристика</summary>
11+
private readonly double[] _ImpulseResponse;
12+
13+
/// <summary>Состояние синфазного канала</summary>
14+
private readonly double[] _StateI;
15+
16+
/// <summary>Состояние квадратурного канала</summary>
17+
private readonly double[] _StateQ;
18+
19+
/// <summary>Импульсная характеристика</summary>
20+
public ReadOnlyCollection<double> ImpulseResponse => AsReadOnly(_ImpulseResponse);
21+
22+
/// <summary>Порядок фильтра</summary>
23+
public int Order => _ImpulseResponse.Length - 1;
24+
25+
/// <summary>Инициализация нового комплексного FIR-фильтра</summary>
26+
/// <param name="ImpulseResponse">Отсчёты импульсной характеристики фильтра</param>
27+
public ComplexFIR(double[] ImpulseResponse)
28+
{
29+
ArgumentNullException.ThrowIfNull(ImpulseResponse);
30+
if (ImpulseResponse.Length == 0)
31+
throw new ArgumentException("Размер массива импульсной характеристики должен быть больше 0", nameof(ImpulseResponse));
32+
33+
_ImpulseResponse = ImpulseResponse;
34+
_StateI = new double[ImpulseResponse.Length];
35+
_StateQ = new double[ImpulseResponse.Length];
36+
}
37+
38+
/// <summary>Обработать один комплексный отсчёт</summary>
39+
/// <param name="Sample">Входной комплексный отсчёт</param>
40+
/// <returns>Выходной комплексный отсчёт</returns>
41+
public Complex Process(Complex Sample)
42+
{
43+
var i_filtered = _StateI.FilterSample(_ImpulseResponse, Sample.Re);
44+
var q_filtered = _StateQ.FilterSample(_ImpulseResponse, Sample.Im);
45+
46+
return new(i_filtered, q_filtered);
47+
}
48+
49+
/// <summary>Обработать последовательность комплексных отсчётов</summary>
50+
/// <param name="Samples">Последовательность входных отсчётов</param>
51+
/// <returns>Последовательность выходных отсчётов</returns>
52+
public IEnumerable<Complex> Process(IEnumerable<Complex> Samples)
53+
{
54+
ArgumentNullException.ThrowIfNull(Samples);
55+
56+
foreach (var sample in Samples)
57+
yield return Process(sample);
58+
}
59+
60+
/// <summary>Обработать блок комплексных отсчётов</summary>
61+
/// <param name="Samples">Входной блок отсчётов</param>
62+
/// <param name="Destination">Выходной буфер</param>
63+
public void Process(ReadOnlySpan<Complex> Samples, Span<Complex> Destination)
64+
{
65+
if (Destination.Length < Samples.Length)
66+
throw new ArgumentException("Размер буфера назначения должен быть не меньше размера входного блока", nameof(Destination));
67+
68+
for (var i = 0; i < Samples.Length; i++)
69+
Destination[i] = Process(Samples[i]);
70+
}
71+
72+
/// <summary>Сбросить состояние фильтра</summary>
73+
public void Reset()
74+
{
75+
Clear(_StateI, 0, _StateI.Length);
76+
Clear(_StateQ, 0, _StateQ.Length);
77+
}
78+
79+
/// <summary>Получить комплексный коэффициент передачи фильтра</summary>
80+
/// <param name="f">Нормированная частота</param>
81+
/// <returns>Значение коэффициента передачи</returns>
82+
public Complex FrequencyResponse(double f) => _ImpulseResponse.FrequencyResponse(f);
83+
}

MathCore.DSP/Filters/ComplexIIR.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System.Collections.ObjectModel;
2+
3+
using static System.Array;
4+
5+
namespace MathCore.DSP.Filters;
6+
7+
/// <summary>Комплексный фильтр с бесконечной импульсной характеристикой для IQ-потока</summary>
8+
public class ComplexIIR
9+
{
10+
/// <summary>Коэффициенты прямой связи</summary>
11+
private readonly double[] _B;
12+
13+
/// <summary>Коэффициенты обратной связи</summary>
14+
private readonly double[] _A;
15+
16+
/// <summary>Состояние синфазного канала</summary>
17+
private readonly double[] _StateI;
18+
19+
/// <summary>Состояние квадратурного канала</summary>
20+
private readonly double[] _StateQ;
21+
22+
/// <summary>Массив коэффициентов полинома числителя</summary>
23+
public ReadOnlyCollection<double> B => AsReadOnly(_B);
24+
25+
/// <summary>Массив коэффициентов полинома знаменателя</summary>
26+
public ReadOnlyCollection<double> A => AsReadOnly(_A);
27+
28+
/// <summary>Порядок фильтра</summary>
29+
public int Order => _A.Length - 1;
30+
31+
/// <summary>Инициализация нового комплексного IIR-фильтра</summary>
32+
/// <param name="B">Массив коэффициентов полинома числителя</param>
33+
/// <param name="A">Массив коэффициентов полинома знаменателя</param>
34+
public ComplexIIR(double[] B, double[] A)
35+
{
36+
ArgumentNullException.ThrowIfNull(B);
37+
ArgumentNullException.ThrowIfNull(A);
38+
39+
if (B.Length == 0)
40+
throw new ArgumentException("Размер массива коэффициентов числителя должен быть больше 0", nameof(B));
41+
42+
if (A.Length < 2)
43+
throw new ArgumentException("Размер массива коэффициентов знаменателя должен быть больше 1", nameof(A));
44+
45+
if (B.Length > A.Length)
46+
throw new ArgumentException("Размер массива коэффициентов полинома числителя должен быть меньше, либо равен размеру массива коэффициентов полинома знаменателя");
47+
48+
_B = B;
49+
_A = A;
50+
_StateI = new double[A.Length];
51+
_StateQ = new double[A.Length];
52+
}
53+
54+
/// <summary>Обработать один комплексный отсчёт</summary>
55+
/// <param name="Sample">Входной комплексный отсчёт</param>
56+
/// <returns>Выходной комплексный отсчёт</returns>
57+
public Complex Process(Complex Sample)
58+
{
59+
var i_filtered = _StateI.FilterSample(_A, _B, Sample.Re);
60+
var q_filtered = _StateQ.FilterSample(_A, _B, Sample.Im);
61+
62+
return new(i_filtered, q_filtered);
63+
}
64+
65+
/// <summary>Обработать последовательность комплексных отсчётов</summary>
66+
/// <param name="Samples">Последовательность входных отсчётов</param>
67+
/// <returns>Последовательность выходных отсчётов</returns>
68+
public IEnumerable<Complex> Process(IEnumerable<Complex> Samples)
69+
{
70+
ArgumentNullException.ThrowIfNull(Samples);
71+
72+
foreach (var sample in Samples)
73+
yield return Process(sample);
74+
}
75+
76+
/// <summary>Обработать блок комплексных отсчётов</summary>
77+
/// <param name="Samples">Входной блок отсчётов</param>
78+
/// <param name="Destination">Выходной буфер</param>
79+
public void Process(ReadOnlySpan<Complex> Samples, Span<Complex> Destination)
80+
{
81+
if (Destination.Length < Samples.Length)
82+
throw new ArgumentException("Размер буфера назначения должен быть не меньше размера входного блока", nameof(Destination));
83+
84+
for (var i = 0; i < Samples.Length; i++)
85+
Destination[i] = Process(Samples[i]);
86+
}
87+
88+
/// <summary>Сбросить состояние фильтра</summary>
89+
public void Reset()
90+
{
91+
Clear(_StateI, 0, _StateI.Length);
92+
Clear(_StateQ, 0, _StateQ.Length);
93+
}
94+
95+
/// <summary>Получить комплексный коэффициент передачи фильтра</summary>
96+
/// <param name="f">Нормированная частота</param>
97+
/// <returns>Значение коэффициента передачи</returns>
98+
public Complex FrequencyResponse(double f) => DoubleArrayDSPExtensions.FrequencyResponse(_A, _B, f);
99+
}

0 commit comments

Comments
 (0)