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