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
20namespace webrtc {
21namespace {
22
23const int kChunkSizeMs = 10;
24const int kSampleRateHz = 16000;
25
26SphericalPointf AzimuthToSphericalPoint(float azimuth_radians) {
27  return SphericalPointf(azimuth_radians, 0.f, 1.f);
28}
29
30void 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
46void 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
53TEST(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
67TEST(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