1+ using MathCore . DSP . Samples ;
2+ using MathCore . DSP . Samples . Extensions ;
3+
4+ namespace MathCore . DSP . Tests . Samples . Extensions ;
5+
6+ [ TestClass ]
7+ public class SampleSI16ExTests
8+ {
9+ /// <summary>Тест фазовой демодуляции для пустого массива</summary>
10+ [ TestMethod ]
11+ public void PhaseDemodulation_EmptyArray_ReturnsEmpty ( )
12+ {
13+ // Arrange
14+ var samples = Span < SampleSI16 > . Empty ;
15+ const double f0 = 1000.0 ;
16+ const double fd = 48000.0 ;
17+
18+ // Act
19+ var result = samples . PhaseDemodulation ( f0 , fd ) ;
20+
21+ // Assert
22+ Assert . AreEqual ( 0 , result . Length ) ;
23+ }
24+
25+ /// <summary>Тест фазовой демодуляции для массива с одним элементом</summary>
26+ [ TestMethod ]
27+ public void PhaseDemodulation_SingleElement_ReturnsZero ( )
28+ {
29+ // Arrange
30+ var samples = new SampleSI16 [ ] { new ( 100 , 50 ) } . AsSpan ( ) ;
31+ const double f0 = 1000.0 ;
32+ const double fd = 48000.0 ;
33+
34+ // Act
35+ var result = samples . PhaseDemodulation ( f0 , fd ) ;
36+
37+ // Assert
38+ Assert . AreEqual ( 1 , result . Length ) ;
39+ Assert . AreEqual ( 0f , result [ 0 ] ) ;
40+ }
41+
42+ /// <summary>Тест фазовой демодуляции для постоянного сигнала</summary>
43+ [ TestMethod ]
44+ public void PhaseDemodulation_ConstantSignal_ReturnsNearZeros ( )
45+ {
46+ // Arrange - создаем постоянный сигнал
47+ var samples = new SampleSI16 [ ]
48+ {
49+ new ( 100 , 0 ) ,
50+ new ( 100 , 0 ) ,
51+ new ( 100 , 0 ) ,
52+ new ( 100 , 0 ) ,
53+ new ( 100 , 0 )
54+ } . AsSpan ( ) ;
55+
56+ const double f0 = 1000.0 ;
57+ const double fd = 48000.0 ;
58+
59+ // Act
60+ var result = samples . PhaseDemodulation ( f0 , fd ) ;
61+
62+ // Assert
63+ Assert . AreEqual ( 5 , result . Length ) ;
64+ Assert . AreEqual ( 0f , result [ 0 ] ) ; // Первый всегда 0
65+
66+ // Для постоянного сигнала мгновенная частота должна быть близка к нулю
67+ // после вычитания центральной частоты результат должен быть близок к -f0
68+ for ( var i = 1 ; i < result . Length ; i ++ )
69+ Assert . IsTrue ( Math . Abs ( result [ i ] + f0 ) < 50 ,
70+ $ "Sample { i } : expected ~{ - f0 } , got { result [ i ] } ") ;
71+ }
72+
73+ /// <summary>Тест фазовой демодуляции для синусоидального сигнала с известной частотой</summary>
74+ [ TestMethod ]
75+ public void PhaseDemodulation_SinusoidalSignal_ReturnsExpectedFrequency ( )
76+ {
77+ // Arrange - создаем синусоидальный сигнал с частотой f_signal
78+ const double fd = 48000.0 ;
79+ const double f0 = 1000.0 ; // Центральная частота
80+ const double f_signal = 1500.0 ; // Частота сигнала
81+ const int samples_count = 200 ;
82+
83+ var samples = new SampleSI16 [ samples_count ] ;
84+ var dt = 1.0 / fd ;
85+
86+ for ( var i = 0 ; i < samples_count ; i ++ )
87+ {
88+ var t = i * dt ;
89+ var angle = 2.0 * Math . PI * f_signal * t ;
90+ var amplitude = 100.0 ;
91+
92+ samples [ i ] = new SampleSI16 (
93+ ( sbyte ) ( amplitude * Math . Cos ( angle ) ) ,
94+ ( sbyte ) ( amplitude * Math . Sin ( angle ) )
95+ ) ;
96+ }
97+
98+ // Act
99+ var result = samples . AsSpan ( ) . PhaseDemodulation ( f0 , fd ) ;
100+
101+ // Assert
102+ Assert . AreEqual ( samples_count , result . Length ) ;
103+ Assert . AreEqual ( 0f , result [ 0 ] ) ; // Первый всегда 0
104+
105+ // Проверяем, что результат близок к ожидаемой частоте (f_signal - f0)
106+ var expected_frequency = f_signal - f0 ;
107+ var tolerance = 100.0 ; // Допуск в Гц
108+
109+ // Проверяем стабильную часть сигнала (пропускаем начальные образцы)
110+ var stable_samples = 0 ;
111+ for ( var i = 20 ; i < result . Length - 20 ; i ++ ) // Пропускаем края для стабилизации
112+ if ( Math . Abs ( result [ i ] - expected_frequency ) < tolerance )
113+ stable_samples ++ ;
114+
115+ // Проверяем, что большинство образцов дает правильную частоту
116+ var expected_stable_count = ( result . Length - 40 ) * 0.8 ; // 80% образцов должны быть стабильными
117+ Assert . IsTrue ( stable_samples > expected_stable_count ,
118+ $ "Expected at least { expected_stable_count } stable samples, got { stable_samples } ") ;
119+ }
120+
121+ /// <summary>Тест производительности фазовой демодуляции</summary>
122+ [ TestMethod ]
123+ public void PhaseDemodulation_Performance_CompletesQuickly ( )
124+ {
125+ // Arrange - большой массив для тестирования производительности
126+ const int samples_count = 100_000 ;
127+ var samples = new SampleSI16 [ samples_count ] ;
128+ var random = new Random ( 42 ) ; // Фиксированное семя для воспроизводимости
129+
130+ for ( var i = 0 ; i < samples_count ; i ++ )
131+ samples [ i ] = new SampleSI16 (
132+ ( sbyte ) random . Next ( - 128 , 128 ) ,
133+ ( sbyte ) random . Next ( - 128 , 128 )
134+ ) ;
135+
136+ const double f0 = 1000.0 ;
137+ const double fd = 48000.0 ;
138+
139+ // Act
140+ var stopwatch = System . Diagnostics . Stopwatch . StartNew ( ) ;
141+ var result = samples . AsSpan ( ) . PhaseDemodulation ( f0 , fd ) ;
142+ stopwatch . Stop ( ) ;
143+
144+ // Assert
145+ Assert . AreEqual ( samples_count , result . Length ) ;
146+
147+ // Проверяем, что выполняется достаточно быстро (менее 200 мс для 100k образцов)
148+ Assert . IsTrue ( stopwatch . ElapsedMilliseconds < 200 ,
149+ $ "Деmodulation took { stopwatch . ElapsedMilliseconds } ms, expected < 200 ms") ;
150+
151+ Console . WriteLine ( $ "Фазовая демодуляция { samples_count } образцов заняла { stopwatch . ElapsedMilliseconds } мс") ;
152+ }
153+
154+ /// <summary>Тест проверки правильности unwrap фазы</summary>
155+ [ TestMethod ]
156+ public void PhaseDemodulation_PhaseUnwrap_WorksCorrectly ( )
157+ {
158+ // Arrange - создаем сигнал с плавно изменяющейся фазой, но с перескоками ±2π
159+ const double fd = 1000.0 ;
160+ const double f0 = 0.0 ; // Нулевая центральная частота для простоты
161+
162+ var samples = new SampleSI16 [ ]
163+ {
164+ new ( 100 , 0 ) , // фаза ≈ 0
165+ new ( 71 , 71 ) , // фаза ≈ π/4
166+ new ( 0 , 100 ) , // фаза ≈ π/2
167+ new ( - 71 , 71 ) , // фаза ≈ 3π/4
168+ new ( - 100 , 0 ) , // фаза ≈ π
169+ new ( - 71 , - 71 ) , // фаза ≈ 5π/4 (но с unwrap должна быть ≈ -3π/4)
170+ new ( 0 , - 100 ) , // фаза ≈ 3π/2 (но с unwrap должна быть ≈ -π/2)
171+ new ( 71 , - 71 ) , // фаза ≈ 7π/4 (но с unwrap должна быть ≈ -π/4)
172+ } . AsSpan ( ) ;
173+
174+ // Act
175+ var result = samples . PhaseDemodulation ( f0 , fd ) ;
176+
177+ // Assert
178+ Assert . AreEqual ( samples . Length , result . Length ) ;
179+ Assert . AreEqual ( 0f , result [ 0 ] ) ; // Первый всегда 0
180+
181+ // Проверяем, что результаты имеют разумные значения без больших скачков
182+ for ( var i = 1 ; i < result . Length ; i ++ )
183+ Assert . IsTrue ( Math . Abs ( result [ i ] ) < 1000 ,
184+ $ "Sample { i } : frequency { result [ i ] } Hz seems too high") ;
185+ }
186+ }
0 commit comments