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