1 /*
2 * Copyright (c) 2024, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12
13 #include "iamf/cli/ambisonic_encoder/associated_legendre_polynomials_generator.h"
14
15 #include <cmath>
16 #include <cstddef>
17 #include <vector>
18
19 #include "gtest/gtest.h"
20 #include "iamf/cli/ambisonic_encoder/ambisonic_utils.h"
21
22 namespace iamf_tools {
23 namespace {
24
25 // Tolerated test error margin for single-precision floating points.
26 const float kEpsilon = 1e-5f;
27
28 // Generates associated Legendre polynomials up to and including the 4th degree,
29 // with the Condon-Shortley phase and negative orders included.
GenerateExpectedValuesFourthDegree(float x)30 std::vector<float> GenerateExpectedValuesFourthDegree(float x) {
31 // From http://en.wikipedia.org/wiki/Associated_Legendre_polynomials
32 // Comments are (degree, order).
33 const std::vector<float> expected_values = {
34 1.0f, // (0, 0)
35 0.5f * std::sqrt(1.0f - x * x), // (1, -1)
36 x, // (1, 0)
37 -std::sqrt(1.0f - x * x), // (1, 1)
38 1.0f / 8.0f * (1.0f - x * x), // (2, -2)
39 0.5f * x * std::sqrt(1.0f - x * x), // (2, -1)
40 0.5f * (3.0f * x * x - 1.0f), // (2, 0)
41 -3.0f * x * std::sqrt(1.0f - x * x), // (2, 1)
42 3.0f * (1.0f - x * x), // (2, 2)
43 15.0f / 720.0f * std::pow(1.0f - x * x, 3.0f / 2.0f), // (3, -3)
44 15.0f / 120.0f * x * (1.0f - x * x), // (3, -2)
45 3.0f / 24.0f * (5.0f * x * x - 1.0f) *
46 std::sqrt(1.0f - x * x), // (3, -1)
47 0.5f * (5.0f * IntegerPow(x, 3) - 3.0f * x), // (3, 0)
48 -3.0f / 2.0f * (5.0f * x * x - 1.0f) * std::sqrt(1.0f - x * x), // (3, 1)
49 15.0f * x * (1.0f - x * x), // (3, 2)
50 -15.0f * std::pow(1.0f - x * x, 3.0f / 2.0f), // (3, 3)
51 105.0f / 40320.0f * IntegerPow(1.0f - x * x, 2), // (4, -4)
52 105.0f / 5040.0f * x * std::pow(1.0f - x * x, 3.0f / 2.0f), // (4, -3)
53 15.0f / 720.0f * (7.0f * x * x - 1.0f) * (1.0f - x * x), // (4, -2)
54 5.0f / 40.0f * (7.0f * IntegerPow(x, 3) - 3.0f * x) *
55 std::sqrt(1.0f - x * x), // (4, -1)
56 1.0f / 8.0f *
57 (35.0f * IntegerPow(x, 4) - 30.0f * x * x + 3.0f), // (4, 0)
58 -5.0f / 2.0f * (7.0f * IntegerPow(x, 3) - 3.0f * x) *
59 std::sqrt(1.0f - x * x), // (4, 1)
60 15.0f / 2.0f * (7.0f * x * x - 1.0f) * (1.0f - x * x), // (4, 2)
61 -105.0f * x * std::pow(1.0f - x * x, 3.0f / 2.0f), // (4, 3)
62 105.0f * IntegerPow(1.0f - x * x, 2) // (4, 4)
63 };
64
65 return expected_values;
66 }
67
68 // Tests that the values given by GetIndex are successive indices (n, n+1, n+2,
69 // and so on).
TEST(AssociatedLegendrePolynomialsGeneratorTest,GetIndex_SuccessiveIndices)70 TEST(AssociatedLegendrePolynomialsGeneratorTest, GetIndex_SuccessiveIndices) {
71 const int kMaxDegree = 5;
72 const AssociatedLegendrePolynomialsGenerator alp_generator(kMaxDegree, false,
73 true);
74 int last_index = -1;
75 for (int degree = 0; degree <= kMaxDegree; ++degree) {
76 for (int order = -degree; order <= degree; ++order) {
77 int index = static_cast<int>(alp_generator.GetIndex(degree, order));
78 EXPECT_EQ(last_index + 1, index);
79 last_index = index;
80 }
81 }
82 }
83
84 // Tests that the zeroth-degree, zeroth-order ALP is always 1.
TEST(AssociatedLegendrePolynomialsGeneratorTest,Generate_ZerothElementIsOne)85 TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_ZerothElementIsOne) {
86 const int kMaxDegree = 10;
87 for (int max_degree = 0; max_degree <= kMaxDegree; ++max_degree) {
88 for (int condon_shortley_phase = 0; condon_shortley_phase <= 1;
89 ++condon_shortley_phase) {
90 for (int compute_negative_order = 0; compute_negative_order <= 1;
91 ++compute_negative_order) {
92 AssociatedLegendrePolynomialsGenerator alp_generator(
93 max_degree, condon_shortley_phase != 0,
94 compute_negative_order != 0);
95
96 const float kVariableStep = 0.2f;
97 for (float x = -1.0f; x <= 1.0f; x += kVariableStep) {
98 const std::vector<float> values = alp_generator.Generate(x);
99
100 EXPECT_NEAR(values[0], 1.0f, kEpsilon);
101 }
102 }
103 }
104 }
105 }
106
107 // Tests that the polynomials generated are correct until the 4th degree.
TEST(AssociatedLegendrePolynomialsGeneratorTest,Generate_CorrectFourthDegree)108 TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_CorrectFourthDegree) {
109 const int kMaxDegree = 4;
110 const bool kCondonShortleyPhase = true;
111 const bool kComputeNegativeOrder = true;
112 const AssociatedLegendrePolynomialsGenerator alp_generator(
113 kMaxDegree, kCondonShortleyPhase, kComputeNegativeOrder);
114
115 const float kVariableStep = 0.05f;
116 for (float x = -1.0f; x <= 1.0f; x += kVariableStep) {
117 const std::vector<float> generated_values = alp_generator.Generate(x);
118 const std::vector<float> expected_values =
119 GenerateExpectedValuesFourthDegree(x);
120 ASSERT_EQ(expected_values.size(), generated_values.size());
121 for (size_t i = 0; i < expected_values.size(); ++i) {
122 EXPECT_NEAR(generated_values[i], expected_values[i], kEpsilon)
123 << " at index " << i;
124 }
125 }
126 }
127
128 // Tests that the Condon-Shortley phase is correctly applied.
TEST(AssociatedLegendrePolynomialsGeneratorTest,Generate_CondonShortleyPhase)129 TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_CondonShortleyPhase) {
130 const int kMaxDegree = 10;
131 const float kValue = 0.12345f;
132 for (int max_degree = 0; max_degree <= kMaxDegree; ++max_degree) {
133 for (int compute_negative_order = 0; compute_negative_order <= 1;
134 ++compute_negative_order) {
135 const AssociatedLegendrePolynomialsGenerator alp_generator_without_phase(
136 max_degree, false, compute_negative_order != 0);
137 const std::vector<float> values_without_phase =
138 alp_generator_without_phase.Generate(kValue);
139
140 const AssociatedLegendrePolynomialsGenerator alp_generator_with_phase(
141 max_degree, true, compute_negative_order != 0);
142 const std::vector<float> values_with_phase =
143 alp_generator_with_phase.Generate(kValue);
144
145 ASSERT_EQ(values_with_phase.size(), values_without_phase.size());
146 for (int degree = 0; degree <= max_degree; ++degree) {
147 const int start_order = compute_negative_order ? -degree : 0;
148 for (int order = start_order; order <= degree; ++order) {
149 const size_t index =
150 alp_generator_without_phase.GetIndex(degree, order);
151 const float expected = values_without_phase[index] *
152 std::pow(-1.0f, static_cast<float>(order));
153 EXPECT_NEAR(values_with_phase[index], expected, kEpsilon)
154 << " at degree " << degree << " and order " << order;
155 }
156 }
157 }
158 }
159 }
160
161 } // namespace
162 } // namespace iamf_tools
163