update
This commit is contained in:
@@ -13,10 +13,199 @@
|
||||
#include "nr_common.h"
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define C_SRS_NUMBER (64)
|
||||
#define B_SRS_NUMBER (4)
|
||||
|
||||
void nr_pdsch_rank_capacity_scores(const float eigenvalues[NR_MAX_NB_LAYERS],
|
||||
uint8_t max_rank,
|
||||
float noise_power,
|
||||
float scores[NR_MAX_NB_LAYERS])
|
||||
{
|
||||
AssertFatal(max_rank >= 1 && max_rank <= NR_MAX_NB_LAYERS,
|
||||
"Invalid maximum PDSCH rank %u\n",
|
||||
max_rank);
|
||||
const float finite_noise = isfinite(noise_power) && noise_power > 0.0f ? noise_power : 1.0f;
|
||||
for (int rank = 1; rank <= NR_MAX_NB_LAYERS; rank++) {
|
||||
scores[rank - 1] = 0.0f;
|
||||
if (rank > max_rank)
|
||||
continue;
|
||||
for (int layer = 0; layer < rank; layer++) {
|
||||
const float eigenvalue = isfinite(eigenvalues[layer]) && eigenvalues[layer] > 0.0f
|
||||
? eigenvalues[layer]
|
||||
: 0.0f;
|
||||
scores[rank - 1] += log2f(1.0f + eigenvalue / (rank * finite_noise));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t nr_pdsch_select_rank(const float scores[NR_MAX_NB_LAYERS],
|
||||
uint8_t max_rank,
|
||||
float relative_margin)
|
||||
{
|
||||
AssertFatal(max_rank >= 1 && max_rank <= NR_MAX_NB_LAYERS,
|
||||
"Invalid maximum PDSCH rank %u\n",
|
||||
max_rank);
|
||||
const float margin = isfinite(relative_margin) && relative_margin > 0.0f ? relative_margin : 0.0f;
|
||||
uint8_t best_rank = 1;
|
||||
float best_score = isfinite(scores[0]) ? scores[0] : 0.0f;
|
||||
for (uint8_t rank = 2; rank <= max_rank; rank++) {
|
||||
const float candidate = isfinite(scores[rank - 1]) ? scores[rank - 1] : 0.0f;
|
||||
if (candidate > best_score * (1.0f + margin)) {
|
||||
best_rank = rank;
|
||||
best_score = candidate;
|
||||
}
|
||||
}
|
||||
return best_rank;
|
||||
}
|
||||
|
||||
uint8_t nr_type1_sp_4x1_i11_count(uint8_t layers)
|
||||
{
|
||||
switch (layers) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
return 16;
|
||||
case 7:
|
||||
case 8:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t nr_type1_sp_4x1_i13_count(uint8_t layers)
|
||||
{
|
||||
switch (layers) {
|
||||
case 1:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
return 1;
|
||||
case 2:
|
||||
return 4;
|
||||
case 3:
|
||||
case 4:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t nr_type1_sp_4x1_i2_count(uint8_t layers)
|
||||
{
|
||||
if (layers == 1)
|
||||
return 4;
|
||||
return layers >= 2 && layers <= 8 ? 2 : 0;
|
||||
}
|
||||
|
||||
bool nr_type1_sp_4x1_weights(uint8_t layers,
|
||||
uint8_t i11,
|
||||
uint8_t i13,
|
||||
uint8_t i2,
|
||||
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS])
|
||||
{
|
||||
memset(weights, 0, NR_MAX_NB_LAYERS * NR_MAX_CSI_PORTS * sizeof(**weights));
|
||||
const uint8_t i11_count = nr_type1_sp_4x1_i11_count(layers);
|
||||
const uint8_t i13_count = nr_type1_sp_4x1_i13_count(layers);
|
||||
const uint8_t i2_count = nr_type1_sp_4x1_i2_count(layers);
|
||||
if (i11_count == 0 || i11 >= i11_count || i13 >= i13_count || i2 >= i2_count)
|
||||
return false;
|
||||
|
||||
/* TS 38.214 Tables 5.2.2.2.1-5--12, specialized to the one-dimensional
|
||||
* N1=4 panel. O1=4, so adjacent orthogonal DFT beams are four indices
|
||||
* apart. Values 0..3 below encode 1, -1, phi, and -phi respectively. */
|
||||
uint8_t beam_offset[NR_MAX_NB_LAYERS] = {0};
|
||||
uint8_t polarization[NR_MAX_NB_LAYERS] = {0};
|
||||
switch (layers) {
|
||||
case 1:
|
||||
polarization[0] = 2;
|
||||
break;
|
||||
case 2:
|
||||
beam_offset[1] = 4 * i13;
|
||||
polarization[0] = 2;
|
||||
polarization[1] = 3;
|
||||
break;
|
||||
case 3:
|
||||
case 4: {
|
||||
const uint8_t offset = 4 * (i13 + 1);
|
||||
beam_offset[1] = offset;
|
||||
beam_offset[3] = offset;
|
||||
polarization[0] = 2;
|
||||
polarization[1] = 2;
|
||||
polarization[2] = 3;
|
||||
polarization[3] = 3;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
const uint8_t offsets[] = {0, 0, 4, 4, 8};
|
||||
const uint8_t polarizations[] = {2, 3, 0, 1, 0};
|
||||
memcpy(beam_offset, offsets, sizeof(offsets));
|
||||
memcpy(polarization, polarizations, sizeof(polarizations));
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
const uint8_t offsets[] = {0, 0, 4, 4, 8, 8};
|
||||
const uint8_t polarizations[] = {2, 3, 2, 3, 0, 1};
|
||||
memcpy(beam_offset, offsets, sizeof(offsets));
|
||||
memcpy(polarization, polarizations, sizeof(polarizations));
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
const uint8_t offsets[] = {0, 0, 4, 8, 8, 12, 12};
|
||||
const uint8_t polarizations[] = {2, 3, 2, 0, 1, 0, 1};
|
||||
memcpy(beam_offset, offsets, sizeof(offsets));
|
||||
memcpy(polarization, polarizations, sizeof(polarizations));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
const uint8_t offsets[] = {0, 0, 4, 4, 8, 8, 12, 12};
|
||||
const uint8_t polarizations[] = {2, 3, 2, 3, 0, 1, 0, 1};
|
||||
memcpy(beam_offset, offsets, sizeof(offsets));
|
||||
memcpy(polarization, polarizations, sizeof(polarizations));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const float scale = 1.0f / sqrtf((float)layers);
|
||||
const float phi_re = cosf((float)M_PI * i2 / 2.0f);
|
||||
const float phi_im = sinf((float)M_PI * i2 / 2.0f);
|
||||
for (int layer = 0; layer < layers; layer++) {
|
||||
const int beam = i11 + beam_offset[layer];
|
||||
float pol_re = 1.0f;
|
||||
float pol_im = 0.0f;
|
||||
switch (polarization[layer]) {
|
||||
case 1:
|
||||
pol_re = -1.0f;
|
||||
break;
|
||||
case 2:
|
||||
pol_re = phi_re;
|
||||
pol_im = phi_im;
|
||||
break;
|
||||
case 3:
|
||||
pol_re = -phi_re;
|
||||
pol_im = -phi_im;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int element = 0; element < 4; element++) {
|
||||
const float angle = 2.0f * (float)M_PI * element * beam / 16.0f;
|
||||
const float beam_re = cosf(angle);
|
||||
const float beam_im = sinf(angle);
|
||||
weights[layer][element].r = lrintf(SHRT_MAX * scale * beam_re);
|
||||
weights[layer][element].i = lrintf(SHRT_MAX * scale * beam_im);
|
||||
weights[layer][element + 4].r = lrintf(SHRT_MAX * scale * (pol_re * beam_re - pol_im * beam_im));
|
||||
weights[layer][element + 4].i = lrintf(SHRT_MAX * scale * (pol_re * beam_im + pol_im * beam_re));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TS 38.211 Table 6.4.1.4.3-1: SRS bandwidth configuration */
|
||||
static const unsigned short srs_bandwidth_config[C_SRS_NUMBER][B_SRS_NUMBER][2] = {
|
||||
/* B_SRS = 0 B_SRS = 1 B_SRS = 2 B_SRS = 3 */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef __COMMON_UTILS_NR_NR_COMMON__H__
|
||||
#define __COMMON_UTILS_NR_NR_COMMON__H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "assertions.h"
|
||||
@@ -93,6 +94,63 @@ static inline uint8_t nr_pdsch_layers_per_codeword(uint8_t layers, uint8_t codew
|
||||
return layers / 2 + (codeword == 1 ? layers % 2 : 0);
|
||||
}
|
||||
|
||||
/* A two-TB PDSCH has two separate HARQ-ACK decisions even though both TBs
|
||||
* share one HARQ process. ACKs for padding-only replacement codewords must
|
||||
* not clear the other TB's pending retransmission. */
|
||||
static inline uint8_t nr_pdsch_pending_after_ack(uint8_t pending_mask,
|
||||
uint8_t active_mask,
|
||||
uint8_t ack_mask)
|
||||
{
|
||||
AssertFatal(active_mask == 1 || active_mask == 3, "Invalid active codeword mask %u\n", active_mask);
|
||||
const uint8_t pending = pending_mask ? pending_mask : active_mask;
|
||||
return pending & ~(ack_mask & active_mask);
|
||||
}
|
||||
|
||||
/** Compute equal-power spatial-capacity scores for candidate PDSCH ranks.
|
||||
*
|
||||
* eigenvalues contains the descending eigenvalues of H^H H. The score for
|
||||
* rank r is sum_i log2(1 + eigenvalue_i / (r * noise_power)): total TX power
|
||||
* is held constant and divided equally among the r layers. Scores estimate
|
||||
* the useful spatial multiplexing opportunity; MCS, coding, and scheduling
|
||||
* overhead are intentionally left to MAC.
|
||||
*/
|
||||
void nr_pdsch_rank_capacity_scores(const float eigenvalues[NR_MAX_NB_LAYERS],
|
||||
uint8_t max_rank,
|
||||
float noise_power,
|
||||
float scores[NR_MAX_NB_LAYERS]);
|
||||
|
||||
/** Select the lowest rank that is materially better than all lower ranks.
|
||||
* relative_margin is expressed as a fraction (0.05 means a five-percent
|
||||
* required improvement), which avoids switching rank for negligible model
|
||||
* gains. Returns a one-based rank.
|
||||
*/
|
||||
uint8_t nr_pdsch_select_rank(const float scores[NR_MAX_NB_LAYERS],
|
||||
uint8_t max_rank,
|
||||
float relative_margin);
|
||||
|
||||
/** Number of valid i_1_1 values for the rank-1--8 Type-I single-panel
|
||||
* codebooks with (N1,N2,O1,O2)=(4,1,4,1). This is the eight-port geometry
|
||||
* used by the experimental rank-8 branch. Returns zero for invalid ranks.
|
||||
*/
|
||||
uint8_t nr_type1_sp_4x1_i11_count(uint8_t layers);
|
||||
|
||||
/** Number of valid i_1_3 and i_2 values in the same codebook. */
|
||||
uint8_t nr_type1_sp_4x1_i13_count(uint8_t layers);
|
||||
uint8_t nr_type1_sp_4x1_i2_count(uint8_t layers);
|
||||
|
||||
/** Build one TS 38.214 Type-I single-panel rank-1--8 precoder for the
|
||||
* eight-port (N1,N2,O1,O2)=(4,1,4,1), codebookMode=1 geometry.
|
||||
*
|
||||
* weights is indexed [layer][antenna port]. Coefficients use OAI's existing
|
||||
* Q15 codebook convention (1/sqrt(layers) scaling); unused entries are zero.
|
||||
* Returns false when layers, i11, i13, or i2 is outside this codebook.
|
||||
*/
|
||||
bool nr_type1_sp_4x1_weights(uint8_t layers,
|
||||
uint8_t i11,
|
||||
uint8_t i13,
|
||||
uint8_t i2,
|
||||
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS]);
|
||||
|
||||
// Since the IQ samples are represented by SQ15 R+I (see https://en.wikipedia.org/wiki/Q_(number_format)) we need to compensate when
|
||||
// calcualting signal energy. Instead of shifting each sample right by 15, we can normalize the result in dB scale once its
|
||||
// calcualted. Signal energy is calculated using RMS^2, where each sample is squared before taking the average of the sum, therefore
|
||||
|
||||
@@ -5,3 +5,6 @@ target_link_libraries(test_nr_common PRIVATE nr_common GTest::gtest minimal_lib)
|
||||
add_dependencies(tests test_nr_common)
|
||||
add_test(NAME test_nr_common
|
||||
COMMAND ./test_nr_common)
|
||||
|
||||
add_executable(nr_rank_selector_benchmark nr_rank_selector_benchmark.cpp)
|
||||
target_link_libraries(nr_rank_selector_benchmark PRIVATE nr_common minimal_lib)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" {
|
||||
#include "nr_common.h"
|
||||
}
|
||||
|
||||
struct rank_scenario_t {
|
||||
const char *name;
|
||||
float eigenvalues[NR_MAX_NB_LAYERS];
|
||||
float noise_power;
|
||||
};
|
||||
|
||||
static uint8_t legacy_rank(const float eigenvalues[NR_MAX_NB_LAYERS])
|
||||
{
|
||||
uint8_t rank = 1;
|
||||
for (int layer = 1; layer < NR_MAX_NB_LAYERS; layer++) {
|
||||
if (eigenvalues[layer] <= 0.0f
|
||||
|| 10.0f * std::log10(eigenvalues[0] / eigenvalues[layer]) >= 8.0f)
|
||||
break;
|
||||
rank++;
|
||||
}
|
||||
return rank;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const rank_scenario_t scenarios[] = {
|
||||
{"dominant_one", {100.0f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f}, 1.0f},
|
||||
{"four_modes", {100.0f, 80.0f, 60.0f, 40.0f, 0.01f, 0.01f, 0.01f, 0.01f}, 1.0f},
|
||||
{"eight_modes", {100.0f, 95.0f, 90.0f, 85.0f, 80.0f, 75.0f, 70.0f, 65.0f}, 1.0f},
|
||||
{"balanced_low_snr", {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, 10.0f},
|
||||
{"legacy_false_high_rank", {1.0f, 0.16f, 0.16f, 0.16f, 0.16f, 0.16f, 0.16f, 0.16f}, 10.0f},
|
||||
{"weak_eighth", {100.0f, 90.0f, 80.0f, 70.0f, 60.0f, 50.0f, 40.0f, 5.0f}, 1.0f},
|
||||
};
|
||||
|
||||
std::puts("scenario,policy,selected_rank,selected_capacity_bps_hz");
|
||||
for (const rank_scenario_t &scenario : scenarios) {
|
||||
float scores[NR_MAX_NB_LAYERS] = {};
|
||||
nr_pdsch_rank_capacity_scores(scenario.eigenvalues,
|
||||
NR_MAX_NB_LAYERS,
|
||||
scenario.noise_power,
|
||||
scores);
|
||||
const uint8_t capacity_rank = nr_pdsch_select_rank(scores, NR_MAX_NB_LAYERS, 0.05f);
|
||||
const uint8_t legacy = legacy_rank(scenario.eigenvalues);
|
||||
const uint8_t policies[] = {capacity_rank, legacy, 1, 4, 8};
|
||||
const char *policy_names[] = {"capacity", "legacy", "fixed1", "fixed4", "fixed8"};
|
||||
for (int policy = 0; policy < 5; policy++) {
|
||||
const uint8_t rank = std::min<uint8_t>(policies[policy], NR_MAX_NB_LAYERS);
|
||||
std::printf("%s,%s,%u,%.6f\n",
|
||||
scenario.name,
|
||||
policy_names[policy],
|
||||
rank,
|
||||
scores[rank - 1]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <gtest/gtest.h>
|
||||
extern "C" {
|
||||
#include "nr_common.h"
|
||||
@@ -37,6 +38,113 @@ TEST(nr_common, pdsch_codeword_layer_split)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(nr_common, pdsch_two_codeword_ack_tracks_only_pending_transport_blocks)
|
||||
{
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(0, 3, 0), 3); // both NACK or DTX
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(0, 3, 1), 2); // CW0 ACK, CW1 NACK
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(0, 3, 2), 1); // CW0 NACK, CW1 ACK
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(0, 3, 3), 0); // both ACK
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(2, 3, 1), 2); // padding CW0 ACK is irrelevant
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(2, 3, 2), 0); // pending CW1 ACK finishes
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(1, 3, 2), 1); // padding CW1 ACK is irrelevant
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(1, 3, 1), 0); // pending CW0 ACK finishes
|
||||
EXPECT_EQ(nr_pdsch_pending_after_ack(0, 1, 1), 0); // one-codeword legacy HARQ
|
||||
}
|
||||
|
||||
TEST(nr_common, pdsch_rank_capacity_prefers_independent_modes)
|
||||
{
|
||||
float eigenvalues[NR_MAX_NB_LAYERS];
|
||||
for (float &eigenvalue : eigenvalues)
|
||||
eigenvalue = 100.0f;
|
||||
float scores[NR_MAX_NB_LAYERS] = {};
|
||||
nr_pdsch_rank_capacity_scores(eigenvalues, NR_MAX_NB_LAYERS, 1.0f, scores);
|
||||
|
||||
for (int rank = 1; rank < NR_MAX_NB_LAYERS; rank++)
|
||||
EXPECT_GT(scores[rank], scores[rank - 1]);
|
||||
EXPECT_EQ(nr_pdsch_select_rank(scores, NR_MAX_NB_LAYERS, 0.05f), 8);
|
||||
}
|
||||
|
||||
TEST(nr_common, pdsch_rank_capacity_rejects_weak_modes)
|
||||
{
|
||||
float eigenvalues[NR_MAX_NB_LAYERS] = {100.0f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f};
|
||||
float scores[NR_MAX_NB_LAYERS] = {};
|
||||
nr_pdsch_rank_capacity_scores(eigenvalues, NR_MAX_NB_LAYERS, 1.0f, scores);
|
||||
|
||||
EXPECT_EQ(nr_pdsch_select_rank(scores, NR_MAX_NB_LAYERS, 0.05f), 1);
|
||||
}
|
||||
|
||||
TEST(nr_common, pdsch_rank_capacity_respects_rank_and_gain_margin)
|
||||
{
|
||||
const float scores[NR_MAX_NB_LAYERS] = {10.0f, 10.4f, 10.6f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f};
|
||||
EXPECT_EQ(nr_pdsch_select_rank(scores, 3, 0.05f), 3);
|
||||
EXPECT_EQ(nr_pdsch_select_rank(scores, 2, 0.05f), 1);
|
||||
}
|
||||
|
||||
TEST(nr_common, type1_sp_4x1_parameter_ranges)
|
||||
{
|
||||
constexpr uint8_t expected_i11[] = {16, 16, 16, 16, 16, 16, 8, 8};
|
||||
constexpr uint8_t expected_i13[] = {1, 4, 3, 3, 1, 1, 1, 1};
|
||||
constexpr uint8_t expected_i2[] = {4, 2, 2, 2, 2, 2, 2, 2};
|
||||
for (uint8_t layers = 1; layers <= NR_MAX_NB_LAYERS; layers++) {
|
||||
EXPECT_EQ(nr_type1_sp_4x1_i11_count(layers), expected_i11[layers - 1]);
|
||||
EXPECT_EQ(nr_type1_sp_4x1_i13_count(layers), expected_i13[layers - 1]);
|
||||
EXPECT_EQ(nr_type1_sp_4x1_i2_count(layers), expected_i2[layers - 1]);
|
||||
}
|
||||
|
||||
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS];
|
||||
EXPECT_FALSE(nr_type1_sp_4x1_weights(0, 0, 0, 0, weights));
|
||||
EXPECT_FALSE(nr_type1_sp_4x1_weights(8, 8, 0, 0, weights));
|
||||
EXPECT_FALSE(nr_type1_sp_4x1_weights(8, 0, 1, 0, weights));
|
||||
EXPECT_FALSE(nr_type1_sp_4x1_weights(8, 0, 0, 2, weights));
|
||||
}
|
||||
|
||||
TEST(nr_common, type1_sp_4x1_precoders_are_orthogonal)
|
||||
{
|
||||
for (uint8_t layers = 1; layers <= NR_MAX_NB_LAYERS; layers++) {
|
||||
const uint8_t i11_count = nr_type1_sp_4x1_i11_count(layers);
|
||||
for (uint8_t i11 = 0; i11 < i11_count; i11++) {
|
||||
for (uint8_t i13 = 0; i13 < nr_type1_sp_4x1_i13_count(layers); i13++) {
|
||||
for (uint8_t i2 = 0; i2 < nr_type1_sp_4x1_i2_count(layers); i2++) {
|
||||
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS];
|
||||
ASSERT_TRUE(nr_type1_sp_4x1_weights(layers, i11, i13, i2, weights));
|
||||
const int64_t expected_norm = 8LL * SHRT_MAX * SHRT_MAX / layers;
|
||||
const int64_t tolerance = expected_norm / 1000;
|
||||
for (int a = 0; a < layers; a++) {
|
||||
int64_t diagonal = 0;
|
||||
for (int port = 0; port < 8; port++)
|
||||
diagonal += (int64_t)weights[a][port].r * weights[a][port].r
|
||||
+ (int64_t)weights[a][port].i * weights[a][port].i;
|
||||
EXPECT_NEAR((double)diagonal, (double)expected_norm, (double)tolerance);
|
||||
for (int b = a + 1; b < layers; b++) {
|
||||
int64_t inner_re = 0;
|
||||
int64_t inner_im = 0;
|
||||
for (int port = 0; port < 8; port++) {
|
||||
inner_re += (int64_t)weights[a][port].r * weights[b][port].r
|
||||
+ (int64_t)weights[a][port].i * weights[b][port].i;
|
||||
inner_im += (int64_t)weights[a][port].r * weights[b][port].i
|
||||
- (int64_t)weights[a][port].i * weights[b][port].r;
|
||||
}
|
||||
EXPECT_NEAR((double)inner_re, 0.0, (double)tolerance);
|
||||
EXPECT_NEAR((double)inner_im, 0.0, (double)tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(nr_common, type1_sp_4x1_i2_rotates_cross_polarization)
|
||||
{
|
||||
c16_t i2_zero[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS];
|
||||
c16_t i2_one[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS];
|
||||
ASSERT_TRUE(nr_type1_sp_4x1_weights(8, 0, 0, 0, i2_zero));
|
||||
ASSERT_TRUE(nr_type1_sp_4x1_weights(8, 0, 0, 1, i2_one));
|
||||
EXPECT_EQ(i2_zero[0][4].i, 0);
|
||||
EXPECT_EQ(i2_one[0][4].r, 0);
|
||||
EXPECT_EQ(i2_zero[0][4].r, i2_one[0][4].i);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user