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