1 /*
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 // MSVC++ requires this to be set before any other includes to get M_PI.
12 #define _USE_MATH_DEFINES
13
14 #include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h"
15
16 #include <math.h>
17
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace webrtc {
21 namespace {
22
23 const int kChunkSizeMs = 10;
24 const int kSampleRateHz = 16000;
25
AzimuthToSphericalPoint(float azimuth_radians)26 SphericalPointf AzimuthToSphericalPoint(float azimuth_radians) {
27 return SphericalPointf(azimuth_radians, 0.f, 1.f);
28 }
29
Verify(NonlinearBeamformer * bf,float target_azimuth_radians)30 void Verify(NonlinearBeamformer* bf, float target_azimuth_radians) {
31 EXPECT_TRUE(bf->IsInBeam(AzimuthToSphericalPoint(target_azimuth_radians)));
32 EXPECT_TRUE(bf->IsInBeam(AzimuthToSphericalPoint(
33 target_azimuth_radians - NonlinearBeamformer::kHalfBeamWidthRadians +
34 0.001f)));
35 EXPECT_TRUE(bf->IsInBeam(AzimuthToSphericalPoint(
36 target_azimuth_radians + NonlinearBeamformer::kHalfBeamWidthRadians -
37 0.001f)));
38 EXPECT_FALSE(bf->IsInBeam(AzimuthToSphericalPoint(
39 target_azimuth_radians - NonlinearBeamformer::kHalfBeamWidthRadians -
40 0.001f)));
41 EXPECT_FALSE(bf->IsInBeam(AzimuthToSphericalPoint(
42 target_azimuth_radians + NonlinearBeamformer::kHalfBeamWidthRadians +
43 0.001f)));
44 }
45
AimAndVerify(NonlinearBeamformer * bf,float target_azimuth_radians)46 void AimAndVerify(NonlinearBeamformer* bf, float target_azimuth_radians) {
47 bf->AimAt(AzimuthToSphericalPoint(target_azimuth_radians));
48 Verify(bf, target_azimuth_radians);
49 }
50
51 } // namespace
52
TEST(NonlinearBeamformerTest,AimingModifiesBeam)53 TEST(NonlinearBeamformerTest, AimingModifiesBeam) {
54 std::vector<Point> array_geometry;
55 array_geometry.push_back(Point(-0.025f, 0.f, 0.f));
56 array_geometry.push_back(Point(0.025f, 0.f, 0.f));
57 NonlinearBeamformer bf(array_geometry);
58 bf.Initialize(kChunkSizeMs, kSampleRateHz);
59 // The default constructor parameter sets the target angle to PI / 2.
60 Verify(&bf, static_cast<float>(M_PI) / 2.f);
61 AimAndVerify(&bf, static_cast<float>(M_PI) / 3.f);
62 AimAndVerify(&bf, 3.f * static_cast<float>(M_PI) / 4.f);
63 AimAndVerify(&bf, static_cast<float>(M_PI) / 6.f);
64 AimAndVerify(&bf, static_cast<float>(M_PI));
65 }
66
TEST(NonlinearBeamformerTest,InterfAnglesTakeAmbiguityIntoAccount)67 TEST(NonlinearBeamformerTest, InterfAnglesTakeAmbiguityIntoAccount) {
68 {
69 // For linear arrays there is ambiguity.
70 std::vector<Point> array_geometry;
71 array_geometry.push_back(Point(-0.1f, 0.f, 0.f));
72 array_geometry.push_back(Point(0.f, 0.f, 0.f));
73 array_geometry.push_back(Point(0.2f, 0.f, 0.f));
74 NonlinearBeamformer bf(array_geometry);
75 bf.Initialize(kChunkSizeMs, kSampleRateHz);
76 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
77 EXPECT_FLOAT_EQ(M_PI / 2.f - bf.away_radians_,
78 bf.interf_angles_radians_[0]);
79 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_,
80 bf.interf_angles_radians_[1]);
81 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f));
82 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
83 EXPECT_FLOAT_EQ(M_PI - bf.away_radians_ / 2.f,
84 bf.interf_angles_radians_[0]);
85 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]);
86 }
87 {
88 // For planar arrays with normal in the xy-plane there is ambiguity.
89 std::vector<Point> array_geometry;
90 array_geometry.push_back(Point(-0.1f, 0.f, 0.f));
91 array_geometry.push_back(Point(0.f, 0.f, 0.f));
92 array_geometry.push_back(Point(0.2f, 0.f, 0.f));
93 array_geometry.push_back(Point(0.1f, 0.f, 0.2f));
94 array_geometry.push_back(Point(0.f, 0.f, -0.1f));
95 NonlinearBeamformer bf(array_geometry);
96 bf.Initialize(kChunkSizeMs, kSampleRateHz);
97 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
98 EXPECT_FLOAT_EQ(M_PI / 2.f - bf.away_radians_,
99 bf.interf_angles_radians_[0]);
100 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_,
101 bf.interf_angles_radians_[1]);
102 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f));
103 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
104 EXPECT_FLOAT_EQ(M_PI - bf.away_radians_ / 2.f,
105 bf.interf_angles_radians_[0]);
106 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]);
107 }
108 {
109 // For planar arrays with normal not in the xy-plane there is no ambiguity.
110 std::vector<Point> array_geometry;
111 array_geometry.push_back(Point(0.f, 0.f, 0.f));
112 array_geometry.push_back(Point(0.2f, 0.f, 0.f));
113 array_geometry.push_back(Point(0.f, 0.1f, -0.2f));
114 NonlinearBeamformer bf(array_geometry);
115 bf.Initialize(kChunkSizeMs, kSampleRateHz);
116 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
117 EXPECT_FLOAT_EQ(M_PI / 2.f - bf.away_radians_,
118 bf.interf_angles_radians_[0]);
119 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_,
120 bf.interf_angles_radians_[1]);
121 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f));
122 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
123 EXPECT_FLOAT_EQ(-bf.away_radians_ / 2.f, bf.interf_angles_radians_[0]);
124 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]);
125 }
126 {
127 // For arrays which are not linear or planar there is no ambiguity.
128 std::vector<Point> array_geometry;
129 array_geometry.push_back(Point(0.f, 0.f, 0.f));
130 array_geometry.push_back(Point(0.1f, 0.f, 0.f));
131 array_geometry.push_back(Point(0.f, 0.2f, 0.f));
132 array_geometry.push_back(Point(0.f, 0.f, 0.3f));
133 NonlinearBeamformer bf(array_geometry);
134 bf.Initialize(kChunkSizeMs, kSampleRateHz);
135 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
136 EXPECT_FLOAT_EQ(M_PI / 2.f - bf.away_radians_,
137 bf.interf_angles_radians_[0]);
138 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_,
139 bf.interf_angles_radians_[1]);
140 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f));
141 EXPECT_EQ(2u, bf.interf_angles_radians_.size());
142 EXPECT_FLOAT_EQ(-bf.away_radians_ / 2.f, bf.interf_angles_radians_[0]);
143 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]);
144 }
145 }
146
147 } // namespace webrtc
148