From 9f979c9b1f6f88f7af5e6bd50db8e309e9b5c3f2 Mon Sep 17 00:00:00 2001 From: pranesh-r-ps Date: Wed, 28 Feb 2024 13:30:46 +0000 Subject: [PATCH 1/2] Add channel estimation and equalization integration test --- .../ChannelEqualization/main.cpp | 0 .../channel_verification.hpp | 410 +++++++++--------- 2 files changed, 205 insertions(+), 205 deletions(-) mode change 100644 => 100755 test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp diff --git a/test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp b/test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp old mode 100644 new mode 100755 diff --git a/test/ChannelEstimationAndEqualization/channel_verification.hpp b/test/ChannelEstimationAndEqualization/channel_verification.hpp index f955b2e..d635fff 100644 --- a/test/ChannelEstimationAndEqualization/channel_verification.hpp +++ b/test/ChannelEstimationAndEqualization/channel_verification.hpp @@ -1,205 +1,205 @@ -/* - Arm RAN Acceleration Library - Copyright 2024 Arm Limited and/or its affiliates -*/ -#include "armral.h" -#include "channel_generation.hpp" - -/** - * This header file contains the required functions to verify the results. - */ - -/* - * Define the maximum tolerable Error Vector Magnitude (EVM) for the test case - * to pass. - * - * The maximum acceptable EVM is 8%, according to section 6.4.2.1 of the 3GPP TS - * 38.101-2 document. The defined EVM limit can vary depending on the use case - * but should be less than 8%. - */ -#define EVM_LIMIT 0.04 - -namespace armral::verify_channel { -/* - * Function to calculate the error vector magnitude (EVM) between the reference - * data and the estimated data. - */ -float32_t get_evm(uint32_t size, armral_cmplx_f32_t *p_ref, - armral_cmplx_f32_t *p_est) { - float32_t evm = 0; - armral_cmplx_f32_t temp; - for (uint32_t idx = 0; idx < size; idx++) { - temp.re = p_ref[idx].re - p_est[idx].re; - temp.im = p_ref[idx].im - p_est[idx].im; - evm += temp.re * temp.re + temp.im * temp.im; - } - return (evm / size); -} - -/* - * Function to verify if the channel estimates are close enough to the - * reference channel. The accuracy is measure in terms of mean squared error. - * - * The Error Vector Magnitude (EVM) limit is defined to be a value less than 8% - * according to section 6.4.2.1 of the 3GPP TS 38.101-2 document. - */ -uint16_t check_result(armral_config_ce_params ce_params, - armral_cmplx_f32_t *p_dst_h, - armral_cmplx_f32_t *p_dst_ref_h) { - uint32_t h_size = ce_params.num_symbols * ce_params.num_sc * - ce_params.num_tx * ce_params.num_rx; - float32_t mean_sq_error = get_evm(h_size, p_dst_ref_h, p_dst_h); - printf("Mean squared error = %f, EVM limit = %f\t", mean_sq_error, EVM_LIMIT); - if (mean_sq_error > EVM_LIMIT) { - printf("Test case FAIL\n"); - } else { - printf("Test case PASS\n"); - } - return (uint16_t)(mean_sq_error > EVM_LIMIT); -} - -/* - * Function to perform equalization on the pilot data and display the equalizer - * output. This is a sample function to demonstrate the integration of the - * channel equalization module with the channel estimation module. - */ -void equalize_pilot_data(armral_config_ce_params ce_params, - armral_cmplx_f32_t *p_src_h, - armral_cmplx_f32_t *p_src_y, float32_t *noise_est, - armral_cmplx_f32_t *p_dst_x_tild) { - uint32_t x_mat_size = ce_params.num_tx; - uint32_t y_mat_size = ce_params.num_rx; - uint32_t h_mat_size = ce_params.num_tx * ce_params.num_rx; - - for (uint32_t i = 0; i < ce_params.num_sc; i++) { - for (uint32_t j = 0; j < ce_params.num_pilot_td; j++) { - uint32_t x_idx = (i * ce_params.num_pilot_td + j) * x_mat_size; - uint32_t y_idx = (i * ce_params.num_pilot_td + j) * y_mat_size; - uint32_t h_idx = - (i * ce_params.num_symbols + ce_params.pilot_time_indices[j]) * - h_mat_size; - armral_cmplx_channel_equalization_f32( - ce_params.num_tx, ce_params.num_rx, 1, (p_src_h + h_idx), - (p_src_y + y_idx), noise_est, (p_dst_x_tild + x_idx)); - } - } -} - -/* - * Function to run the channel estimation function for all the possible values - * of input parameters. The function returns a boolean value of `true` only - * if all the valid test cases pass. The function also performs equalization - * with the estimated channel if the value of the argument `equalize_data` is - * `true`. - */ -template -bool run_all_tests( - char const *function_name, bool equalize_data, - ChannelEstimationFunction channel_estimation_function_under_test) { - if (equalize_data) { - printf("\nTesting integration framework for %s\n", function_name); - } else { - printf("\nTesting framework for %s\n", function_name); - } - - bool passed = true; - const uint16_t par_list_num_td_pilots[] = {1, 2, 4}; - const uint16_t par_list_num_fd_pilots[] = {12, 24}; - const uint16_t par_list_num_antennae[] = {1, 2, 3, 4, 8, 16}; - uint16_t num_test_failed = 0; - float32_t noise_pwr = 0; - - // Run tests for different number of pilots in time and frequency domain - for (uint16_t num_td_pilot : par_list_num_td_pilots) { - for (uint16_t num_fd_pilot : par_list_num_fd_pilots) { - for (uint16_t num_tx_antenna : par_list_num_antennae) { - for (uint16_t num_rx_antenna : par_list_num_antennae) { - // Set parameters - armral_config_ce_params ce_params = - armral::channel_generate::fill_ce_params( - num_td_pilot, num_fd_pilot, num_tx_antenna, num_rx_antenna); - printf("\nNum TD pilots = %d, Num SC = %d, Num TX = %d, Num RX = %d, " - "Num Chnls = %d\n", - ce_params.num_pilot_td, ce_params.num_sc, ce_params.num_tx, - ce_params.num_rx, ce_params.num_chnls); - - // Declare variables for channel and pilot signals - uint32_t h_size = ce_params.num_sc * ce_params.num_symbols * - ce_params.num_tx * ce_params.num_rx; - uint32_t x_size = - ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_tx; - uint32_t y_size = - ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_rx; - std::vector p_dst_ref_h(h_size); - std::vector p_dst_h(h_size); - std::vector p_src_x(x_size); - std::vector p_src_y(y_size); - - // Create reference matrices - armral::channel_generate::generate_reference_channel( - ce_params, p_dst_ref_h.data()); - - // Generate pilot signals and observations - armral::channel_generate::generate_pilot_data( - ce_params, p_src_x.data(), p_src_y.data(), p_dst_ref_h.data()); - - // Execute the function - armral_status ret_status = channel_estimation_function_under_test( - ce_params, p_src_x.data(), p_src_y.data(), p_dst_h.data(), - &noise_pwr); - if (ret_status == ARMRAL_SUCCESS) { - // Compare the channel estimate with the reference channel - printf("Channel Estimation:\t"); - num_test_failed += - check_result(ce_params, p_dst_h.data(), p_dst_ref_h.data()); - if (equalize_data) { - printf("Integration Check:\t"); - // Equalize the received signal with respect to both estimated and - // reference channels to compare the EVMs - std::vector p_dst_x_tild(x_size); - equalize_pilot_data(ce_params, p_dst_h.data(), p_src_y.data(), - &noise_pwr, p_dst_x_tild.data()); - float32_t evm_est_ch = - get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); - equalize_pilot_data(ce_params, p_dst_ref_h.data(), p_src_y.data(), - &noise_pwr, p_dst_x_tild.data()); - float32_t evm_ref_ch = - get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); - // Compare the EVMs with estimated and reference channel - // The EVM limit is the same as that for channel estimation - float32_t evm_diff = fabs(evm_est_ch - evm_ref_ch); - printf("EVM difference = %f, EVM limit = %f\t\t", evm_diff, - EVM_LIMIT); - if (evm_diff > EVM_LIMIT) { - printf("Test case FAIL\n"); - } else { - printf("Test case PASS\n"); - } - num_test_failed += (uint16_t)(evm_diff > EVM_LIMIT); - } - } else if (ret_status == ARMRAL_ARGUMENT_ERROR) { - if (ce_params.num_pilot_fd < ce_params.num_tx) { - // This configuration is not supported - printf("Invalid argument\n"); - } else { - printf("Argument Error\n"); - num_test_failed++; - } - } else { - // Unknown return values are considered as failed case - printf("Unknown result:\tTest case failed\n"); - num_test_failed++; - } - } - } - } - } - - // Test passed if all cases passed - printf("\nNumber of failed cases = %d\n", num_test_failed); - printf("\nChannel Estimation test completed\n"); - passed = (num_test_failed == 0); - return passed; -} - -} // namespace armral::verify_channel +/* + Arm RAN Acceleration Library + Copyright 2024 Arm Limited and/or its affiliates +*/ +#include "armral.h" +#include "channel_generation.hpp" + +/** + * This header file contains the required functions to verify the results. + */ + +/* + * Define the maximum tolerable Error Vector Magnitude (EVM) for the test case + * to pass. + * + * The maximum acceptable EVM is 8%, according to section 6.4.2.1 of the 3GPP TS + * 38.101-2 document. The defined EVM limit can vary depending on the use case + * but should be less than 8%. + */ +#define EVM_LIMIT 0.04 + +namespace armral::verify_channel { +/* + * Function to calculate the error vector magnitude (EVM) between the reference + * data and the estimated data. + */ +float32_t get_evm(uint32_t size, armral_cmplx_f32_t *p_ref, + armral_cmplx_f32_t *p_est) { + float32_t evm = 0; + armral_cmplx_f32_t temp; + for (uint32_t idx = 0; idx < size; idx++) { + temp.re = p_ref[idx].re - p_est[idx].re; + temp.im = p_ref[idx].im - p_est[idx].im; + evm += temp.re * temp.re + temp.im * temp.im; + } + return (evm / size); +} + +/* + * Function to verify if the channel estimates are close enough to the + * reference channel. The accuracy is measure in terms of mean squared error. + * + * The Error Vector Magnitude (EVM) limit is defined to be a value less than 8% + * according to section 6.4.2.1 of the 3GPP TS 38.101-2 document. + */ +uint16_t check_result(armral_config_ce_params ce_params, + armral_cmplx_f32_t *p_dst_h, + armral_cmplx_f32_t *p_dst_ref_h) { + uint32_t h_size = ce_params.num_symbols * ce_params.num_sc * + ce_params.num_tx * ce_params.num_rx; + float32_t mean_sq_error = get_evm(h_size, p_dst_ref_h, p_dst_h); + printf("Mean squared error = %f, EVM limit = %f\t", mean_sq_error, EVM_LIMIT); + if (mean_sq_error > EVM_LIMIT) { + printf("Test case FAIL\n"); + } else { + printf("Test case PASS\n"); + } + return (uint16_t)(mean_sq_error > EVM_LIMIT); +} + +/* + * Function to perform equalization on the pilot data and display the equalizer + * output. This is a sample function to demonstrate the integration of the + * channel equalization module with the channel estimation module. + */ +void equalize_pilot_data(armral_config_ce_params ce_params, + armral_cmplx_f32_t *p_src_h, + armral_cmplx_f32_t *p_src_y, float32_t *noise_est, + armral_cmplx_f32_t *p_dst_x_tild) { + uint32_t x_mat_size = ce_params.num_tx; + uint32_t y_mat_size = ce_params.num_rx; + uint32_t h_mat_size = ce_params.num_tx * ce_params.num_rx; + + for (uint32_t i = 0; i < ce_params.num_sc; i++) { + for (uint32_t j = 0; j < ce_params.num_pilot_td; j++) { + uint32_t x_idx = (i * ce_params.num_pilot_td + j) * x_mat_size; + uint32_t y_idx = (i * ce_params.num_pilot_td + j) * y_mat_size; + uint32_t h_idx = + (i * ce_params.num_symbols + ce_params.pilot_time_indices[j]) * + h_mat_size; + armral_cmplx_channel_equalization_f32( + ce_params.num_tx, ce_params.num_rx, 1, (p_src_h + h_idx), + (p_src_y + y_idx), noise_est, (p_dst_x_tild + x_idx)); + } + } +} + +/* + * Function to run the channel estimation function for all the possible values + * of input parameters. The function returns a boolean value of `true` only + * if all the valid test cases pass. The function also performs equalization + * with the estimated channel if the value of the argument `equalize_data` is + * `true`. + */ +template +bool run_all_tests( + char const *function_name, bool equalize_data, + ChannelEstimationFunction channel_estimation_function_under_test) { + if (equalize_data) { + printf("\nTesting integration framework for %s\n", function_name); + } else { + printf("\nTesting framework for %s\n", function_name); + } + + bool passed = true; + const uint16_t par_list_num_td_pilots[] = {1, 2, 4}; + const uint16_t par_list_num_fd_pilots[] = {12, 24}; + const uint16_t par_list_num_antennae[] = {1, 2, 3, 4, 8, 16}; + uint16_t num_test_failed = 0; + float32_t noise_pwr = 0; + + // Run tests for different number of pilots in time and frequency domain + for (uint16_t num_td_pilot : par_list_num_td_pilots) { + for (uint16_t num_fd_pilot : par_list_num_fd_pilots) { + for (uint16_t num_tx_antenna : par_list_num_antennae) { + for (uint16_t num_rx_antenna : par_list_num_antennae) { + // Set parameters + armral_config_ce_params ce_params = + armral::channel_generate::fill_ce_params( + num_td_pilot, num_fd_pilot, num_tx_antenna, num_rx_antenna); + printf("\nNum TD pilots = %d, Num SC = %d, Num TX = %d, Num RX = %d, " + "Num Chnls = %d\n", + ce_params.num_pilot_td, ce_params.num_sc, ce_params.num_tx, + ce_params.num_rx, ce_params.num_chnls); + + // Declare variables for channel and pilot signals + uint32_t h_size = ce_params.num_sc * ce_params.num_symbols * + ce_params.num_tx * ce_params.num_rx; + uint32_t x_size = + ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_tx; + uint32_t y_size = + ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_rx; + std::vector p_dst_ref_h(h_size); + std::vector p_dst_h(h_size); + std::vector p_src_x(x_size); + std::vector p_src_y(y_size); + + // Create reference matrices + armral::channel_generate::generate_reference_channel( + ce_params, p_dst_ref_h.data()); + + // Generate pilot signals and observations + armral::channel_generate::generate_pilot_data( + ce_params, p_src_x.data(), p_src_y.data(), p_dst_ref_h.data()); + + // Execute the function + armral_status ret_status = channel_estimation_function_under_test( + ce_params, p_src_x.data(), p_src_y.data(), p_dst_h.data(), + &noise_pwr); + if (ret_status == ARMRAL_SUCCESS) { + // Compare the channel estimate with the reference channel + printf("Channel Estimation:\t"); + num_test_failed += + check_result(ce_params, p_dst_h.data(), p_dst_ref_h.data()); + if (equalize_data) { + printf("Integration Check:\t"); + // Equalize the received signal with respect to both estimated and + // reference channels to compare the EVMs + std::vector p_dst_x_tild(x_size); + equalize_pilot_data(ce_params, p_dst_h.data(), p_src_y.data(), + &noise_pwr, p_dst_x_tild.data()); + float32_t evm_est_ch = + get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); + equalize_pilot_data(ce_params, p_dst_ref_h.data(), p_src_y.data(), + &noise_pwr, p_dst_x_tild.data()); + float32_t evm_ref_ch = + get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); + // Compare the EVMs with estimated and reference channel + // The EVM limit is the same as that for channel estimation + float32_t evm_diff = fabs(evm_est_ch - evm_ref_ch); + printf("EVM difference = %f, EVM limit = %f\t\t", evm_diff, + EVM_LIMIT); + if (evm_diff > EVM_LIMIT) { + printf("Test case FAIL\n"); + } else { + printf("Test case PASS\n"); + } + num_test_failed += (uint16_t)(evm_diff > EVM_LIMIT); + } + } else if (ret_status == ARMRAL_ARGUMENT_ERROR) { + if (ce_params.num_pilot_fd < ce_params.num_tx) { + // This configuration is not supported + printf("Invalid argument\n"); + } else { + printf("Argument Error\n"); + num_test_failed++; + } + } else { + // Unknown return values are considered as failed case + printf("Unknown result:\tTest case failed\n"); + num_test_failed++; + } + } + } + } + } + + // Test passed if all cases passed + printf("\nNumber of failed cases = %d\n", num_test_failed); + printf("\nChannel Estimation test completed\n"); + passed = (num_test_failed == 0); + return passed; +} + +} // namespace armral::verify_channel -- GitLab From ec72fa7f34a1e2a607b6705c9bf56cc521f85bb2 Mon Sep 17 00:00:00 2001 From: pranesh-r-ps Date: Wed, 13 Mar 2024 15:47:41 +0530 Subject: [PATCH 2/2] Optimized Channel Estimation Module --- .../arm_channel_estimation.cpp | 44 +- .../ChannelEqualization/main.cpp | 0 .../channel_verification.hpp | 410 +++++++++--------- 3 files changed, 235 insertions(+), 219 deletions(-) mode change 100755 => 100644 test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp diff --git a/src/UpperPHY/ChannelEstimation/arm_channel_estimation.cpp b/src/UpperPHY/ChannelEstimation/arm_channel_estimation.cpp index e31b12f..f4039fe 100644 --- a/src/UpperPHY/ChannelEstimation/arm_channel_estimation.cpp +++ b/src/UpperPHY/ChannelEstimation/arm_channel_estimation.cpp @@ -361,26 +361,32 @@ lmmse_f32(armral_cmplx_f32_t *p_src_x, armral_cmplx_f32_t *p_src_y, return ARMRAL_SUCCESS; } +// Interpolate values for the target symbol from pilot symbols void linear_interpolation_f32(armral_cmplx_f32_t *pilot_1, armral_cmplx_f32_t *pilot_2, armral_cmplx_f32_t *target_re, uint16_t rows, uint16_t cols, uint16_t pilot_1_idx, uint16_t pilot_2_idx, uint16_t target_idx) { - for (uint16_t i = 0; i < rows * cols; i++) { - float32_t slope_re = - (pilot_2[i].re - pilot_1[i].re) / (pilot_2_idx - pilot_1_idx); - float32_t slope_im = - (pilot_2[i].im - pilot_1[i].im) / (pilot_2_idx - pilot_1_idx); - target_re[i].re = pilot_1[i].re + slope_re * (target_idx - pilot_1_idx); - target_re[i].im = pilot_1[i].im + slope_im * (target_idx - pilot_1_idx); + float32_t m_denom = 1.0 / (pilot_2_idx - pilot_1_idx); + float32_t t_x = (target_idx - pilot_1_idx); + uint16_t idx = 0; + + for (uint16_t i = (rows * cols); i > 0; i--) { + float32_t slope_re = (pilot_2[idx].re - pilot_1[idx].re) * m_denom; + float32_t slope_im = (pilot_2[idx].im - pilot_1[idx].im) * m_denom; + target_re[idx].re = pilot_1[idx].re + slope_re * t_x; + target_re[idx].im = pilot_1[idx].im + slope_im * t_x; + idx++; } } // If there is only one pilot symbol copy it to the rest of the REs void copy_pilot_f32(armral_cmplx_f32_t *pilot_re, armral_cmplx_f32_t *target_re, uint16_t rows, uint16_t cols) { - for (uint16_t i = 0; i < rows * cols; i++) { + uint16_t i = 0; + for (uint16_t j = rows * cols; j > 0; j--) { target_re[i] = pilot_re[i]; + i++; } } @@ -393,11 +399,13 @@ void linear_interpolation_per_dimension_f32( uint16_t pilot_idx; uint32_t pilot1_re_idx = pilot_indices[0] * re_idx_stride + re_idx_offset; uint32_t pilot2_re_idx = pilot_indices[1] * re_idx_stride + re_idx_offset; - for (uint16_t idx = 0; idx < pilot_indices[0]; idx++) { + uint16_t idx = 0; + for (uint16_t j = pilot_indices[0]; j > 0; j--) { uint32_t re_idx = idx * re_idx_stride + re_idx_offset; linear_interpolation_f32(p_dst_h + pilot1_re_idx, p_dst_h + pilot2_re_idx, p_dst_h + re_idx, rows, cols, pilot_indices[0], pilot_indices[1], idx); + idx++; } // Interpolation between first and last pilot @@ -405,8 +413,8 @@ void linear_interpolation_per_dimension_f32( pilot1_re_idx = pilot_indices[pilot_idx] * re_idx_stride + re_idx_offset; pilot2_re_idx = pilot_indices[pilot_idx + 1] * re_idx_stride + re_idx_offset; - for (uint16_t idx = pilot_indices[pilot_idx] + 1; - idx < pilot_indices[pilot_idx + 1]; idx++) { + for (idx = pilot_indices[pilot_idx] + 1; idx < pilot_indices[pilot_idx + 1]; + idx++) { uint32_t re_idx = idx * re_idx_stride + re_idx_offset; linear_interpolation_f32(p_dst_h + pilot1_re_idx, p_dst_h + pilot2_re_idx, p_dst_h + re_idx, rows, cols, @@ -418,12 +426,15 @@ void linear_interpolation_per_dimension_f32( // Extrapolation after last pilots pilot1_re_idx = pilot_indices[pilot_idx - 1] * re_idx_stride + re_idx_offset; pilot2_re_idx = pilot_indices[pilot_idx] * re_idx_stride + re_idx_offset; - for (uint16_t idx = pilot_indices[pilot_idx] + 1; idx < max_idx; idx++) { + idx = pilot_indices[pilot_idx] + 1; + uint16_t loop_count = max_idx - (pilot_indices[pilot_idx] + 1); + for (uint16_t j = loop_count; j > 0; j--) { uint32_t re_idx = idx * re_idx_stride + re_idx_offset; linear_interpolation_f32(p_dst_h + pilot1_re_idx, p_dst_h + pilot2_re_idx, p_dst_h + re_idx, rows, cols, pilot_indices[pilot_idx - 1], pilot_indices[pilot_idx], idx); + idx++; } } @@ -436,15 +447,20 @@ void copy_pilot_per_dimension_f32(armral_cmplx_f32_t *p_dst_h, uint32_t pilot_re_idx = pilot_idx * re_idx_stride + re_idx_offset; // REs before the pilot - for (uint16_t idx = 0; idx < pilot_idx; idx++) { + uint16_t idx = 0; + for (uint16_t j = pilot_idx; j > 0; j--) { uint32_t re_idx = idx * re_idx_stride + re_idx_offset; copy_pilot_f32(p_dst_h + pilot_re_idx, p_dst_h + re_idx, rows, cols); + idx++; } // REs after the pilot - for (uint16_t idx = pilot_idx + 1; idx < max_idx; idx++) { + idx = pilot_idx + 1; + uint16_t loop_count1 = max_idx - pilot_idx - 1; + for (uint16_t j = loop_count1; j > 0; j--) { uint32_t re_idx = idx * re_idx_stride + re_idx_offset; copy_pilot_f32(p_dst_h + pilot_re_idx, p_dst_h + re_idx, rows, cols); + idx++; } } diff --git a/test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp b/test/ChannelEstimationAndEqualization/ChannelEqualization/main.cpp old mode 100755 new mode 100644 diff --git a/test/ChannelEstimationAndEqualization/channel_verification.hpp b/test/ChannelEstimationAndEqualization/channel_verification.hpp index d635fff..f955b2e 100644 --- a/test/ChannelEstimationAndEqualization/channel_verification.hpp +++ b/test/ChannelEstimationAndEqualization/channel_verification.hpp @@ -1,205 +1,205 @@ -/* - Arm RAN Acceleration Library - Copyright 2024 Arm Limited and/or its affiliates -*/ -#include "armral.h" -#include "channel_generation.hpp" - -/** - * This header file contains the required functions to verify the results. - */ - -/* - * Define the maximum tolerable Error Vector Magnitude (EVM) for the test case - * to pass. - * - * The maximum acceptable EVM is 8%, according to section 6.4.2.1 of the 3GPP TS - * 38.101-2 document. The defined EVM limit can vary depending on the use case - * but should be less than 8%. - */ -#define EVM_LIMIT 0.04 - -namespace armral::verify_channel { -/* - * Function to calculate the error vector magnitude (EVM) between the reference - * data and the estimated data. - */ -float32_t get_evm(uint32_t size, armral_cmplx_f32_t *p_ref, - armral_cmplx_f32_t *p_est) { - float32_t evm = 0; - armral_cmplx_f32_t temp; - for (uint32_t idx = 0; idx < size; idx++) { - temp.re = p_ref[idx].re - p_est[idx].re; - temp.im = p_ref[idx].im - p_est[idx].im; - evm += temp.re * temp.re + temp.im * temp.im; - } - return (evm / size); -} - -/* - * Function to verify if the channel estimates are close enough to the - * reference channel. The accuracy is measure in terms of mean squared error. - * - * The Error Vector Magnitude (EVM) limit is defined to be a value less than 8% - * according to section 6.4.2.1 of the 3GPP TS 38.101-2 document. - */ -uint16_t check_result(armral_config_ce_params ce_params, - armral_cmplx_f32_t *p_dst_h, - armral_cmplx_f32_t *p_dst_ref_h) { - uint32_t h_size = ce_params.num_symbols * ce_params.num_sc * - ce_params.num_tx * ce_params.num_rx; - float32_t mean_sq_error = get_evm(h_size, p_dst_ref_h, p_dst_h); - printf("Mean squared error = %f, EVM limit = %f\t", mean_sq_error, EVM_LIMIT); - if (mean_sq_error > EVM_LIMIT) { - printf("Test case FAIL\n"); - } else { - printf("Test case PASS\n"); - } - return (uint16_t)(mean_sq_error > EVM_LIMIT); -} - -/* - * Function to perform equalization on the pilot data and display the equalizer - * output. This is a sample function to demonstrate the integration of the - * channel equalization module with the channel estimation module. - */ -void equalize_pilot_data(armral_config_ce_params ce_params, - armral_cmplx_f32_t *p_src_h, - armral_cmplx_f32_t *p_src_y, float32_t *noise_est, - armral_cmplx_f32_t *p_dst_x_tild) { - uint32_t x_mat_size = ce_params.num_tx; - uint32_t y_mat_size = ce_params.num_rx; - uint32_t h_mat_size = ce_params.num_tx * ce_params.num_rx; - - for (uint32_t i = 0; i < ce_params.num_sc; i++) { - for (uint32_t j = 0; j < ce_params.num_pilot_td; j++) { - uint32_t x_idx = (i * ce_params.num_pilot_td + j) * x_mat_size; - uint32_t y_idx = (i * ce_params.num_pilot_td + j) * y_mat_size; - uint32_t h_idx = - (i * ce_params.num_symbols + ce_params.pilot_time_indices[j]) * - h_mat_size; - armral_cmplx_channel_equalization_f32( - ce_params.num_tx, ce_params.num_rx, 1, (p_src_h + h_idx), - (p_src_y + y_idx), noise_est, (p_dst_x_tild + x_idx)); - } - } -} - -/* - * Function to run the channel estimation function for all the possible values - * of input parameters. The function returns a boolean value of `true` only - * if all the valid test cases pass. The function also performs equalization - * with the estimated channel if the value of the argument `equalize_data` is - * `true`. - */ -template -bool run_all_tests( - char const *function_name, bool equalize_data, - ChannelEstimationFunction channel_estimation_function_under_test) { - if (equalize_data) { - printf("\nTesting integration framework for %s\n", function_name); - } else { - printf("\nTesting framework for %s\n", function_name); - } - - bool passed = true; - const uint16_t par_list_num_td_pilots[] = {1, 2, 4}; - const uint16_t par_list_num_fd_pilots[] = {12, 24}; - const uint16_t par_list_num_antennae[] = {1, 2, 3, 4, 8, 16}; - uint16_t num_test_failed = 0; - float32_t noise_pwr = 0; - - // Run tests for different number of pilots in time and frequency domain - for (uint16_t num_td_pilot : par_list_num_td_pilots) { - for (uint16_t num_fd_pilot : par_list_num_fd_pilots) { - for (uint16_t num_tx_antenna : par_list_num_antennae) { - for (uint16_t num_rx_antenna : par_list_num_antennae) { - // Set parameters - armral_config_ce_params ce_params = - armral::channel_generate::fill_ce_params( - num_td_pilot, num_fd_pilot, num_tx_antenna, num_rx_antenna); - printf("\nNum TD pilots = %d, Num SC = %d, Num TX = %d, Num RX = %d, " - "Num Chnls = %d\n", - ce_params.num_pilot_td, ce_params.num_sc, ce_params.num_tx, - ce_params.num_rx, ce_params.num_chnls); - - // Declare variables for channel and pilot signals - uint32_t h_size = ce_params.num_sc * ce_params.num_symbols * - ce_params.num_tx * ce_params.num_rx; - uint32_t x_size = - ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_tx; - uint32_t y_size = - ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_rx; - std::vector p_dst_ref_h(h_size); - std::vector p_dst_h(h_size); - std::vector p_src_x(x_size); - std::vector p_src_y(y_size); - - // Create reference matrices - armral::channel_generate::generate_reference_channel( - ce_params, p_dst_ref_h.data()); - - // Generate pilot signals and observations - armral::channel_generate::generate_pilot_data( - ce_params, p_src_x.data(), p_src_y.data(), p_dst_ref_h.data()); - - // Execute the function - armral_status ret_status = channel_estimation_function_under_test( - ce_params, p_src_x.data(), p_src_y.data(), p_dst_h.data(), - &noise_pwr); - if (ret_status == ARMRAL_SUCCESS) { - // Compare the channel estimate with the reference channel - printf("Channel Estimation:\t"); - num_test_failed += - check_result(ce_params, p_dst_h.data(), p_dst_ref_h.data()); - if (equalize_data) { - printf("Integration Check:\t"); - // Equalize the received signal with respect to both estimated and - // reference channels to compare the EVMs - std::vector p_dst_x_tild(x_size); - equalize_pilot_data(ce_params, p_dst_h.data(), p_src_y.data(), - &noise_pwr, p_dst_x_tild.data()); - float32_t evm_est_ch = - get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); - equalize_pilot_data(ce_params, p_dst_ref_h.data(), p_src_y.data(), - &noise_pwr, p_dst_x_tild.data()); - float32_t evm_ref_ch = - get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); - // Compare the EVMs with estimated and reference channel - // The EVM limit is the same as that for channel estimation - float32_t evm_diff = fabs(evm_est_ch - evm_ref_ch); - printf("EVM difference = %f, EVM limit = %f\t\t", evm_diff, - EVM_LIMIT); - if (evm_diff > EVM_LIMIT) { - printf("Test case FAIL\n"); - } else { - printf("Test case PASS\n"); - } - num_test_failed += (uint16_t)(evm_diff > EVM_LIMIT); - } - } else if (ret_status == ARMRAL_ARGUMENT_ERROR) { - if (ce_params.num_pilot_fd < ce_params.num_tx) { - // This configuration is not supported - printf("Invalid argument\n"); - } else { - printf("Argument Error\n"); - num_test_failed++; - } - } else { - // Unknown return values are considered as failed case - printf("Unknown result:\tTest case failed\n"); - num_test_failed++; - } - } - } - } - } - - // Test passed if all cases passed - printf("\nNumber of failed cases = %d\n", num_test_failed); - printf("\nChannel Estimation test completed\n"); - passed = (num_test_failed == 0); - return passed; -} - -} // namespace armral::verify_channel +/* + Arm RAN Acceleration Library + Copyright 2024 Arm Limited and/or its affiliates +*/ +#include "armral.h" +#include "channel_generation.hpp" + +/** + * This header file contains the required functions to verify the results. + */ + +/* + * Define the maximum tolerable Error Vector Magnitude (EVM) for the test case + * to pass. + * + * The maximum acceptable EVM is 8%, according to section 6.4.2.1 of the 3GPP TS + * 38.101-2 document. The defined EVM limit can vary depending on the use case + * but should be less than 8%. + */ +#define EVM_LIMIT 0.04 + +namespace armral::verify_channel { +/* + * Function to calculate the error vector magnitude (EVM) between the reference + * data and the estimated data. + */ +float32_t get_evm(uint32_t size, armral_cmplx_f32_t *p_ref, + armral_cmplx_f32_t *p_est) { + float32_t evm = 0; + armral_cmplx_f32_t temp; + for (uint32_t idx = 0; idx < size; idx++) { + temp.re = p_ref[idx].re - p_est[idx].re; + temp.im = p_ref[idx].im - p_est[idx].im; + evm += temp.re * temp.re + temp.im * temp.im; + } + return (evm / size); +} + +/* + * Function to verify if the channel estimates are close enough to the + * reference channel. The accuracy is measure in terms of mean squared error. + * + * The Error Vector Magnitude (EVM) limit is defined to be a value less than 8% + * according to section 6.4.2.1 of the 3GPP TS 38.101-2 document. + */ +uint16_t check_result(armral_config_ce_params ce_params, + armral_cmplx_f32_t *p_dst_h, + armral_cmplx_f32_t *p_dst_ref_h) { + uint32_t h_size = ce_params.num_symbols * ce_params.num_sc * + ce_params.num_tx * ce_params.num_rx; + float32_t mean_sq_error = get_evm(h_size, p_dst_ref_h, p_dst_h); + printf("Mean squared error = %f, EVM limit = %f\t", mean_sq_error, EVM_LIMIT); + if (mean_sq_error > EVM_LIMIT) { + printf("Test case FAIL\n"); + } else { + printf("Test case PASS\n"); + } + return (uint16_t)(mean_sq_error > EVM_LIMIT); +} + +/* + * Function to perform equalization on the pilot data and display the equalizer + * output. This is a sample function to demonstrate the integration of the + * channel equalization module with the channel estimation module. + */ +void equalize_pilot_data(armral_config_ce_params ce_params, + armral_cmplx_f32_t *p_src_h, + armral_cmplx_f32_t *p_src_y, float32_t *noise_est, + armral_cmplx_f32_t *p_dst_x_tild) { + uint32_t x_mat_size = ce_params.num_tx; + uint32_t y_mat_size = ce_params.num_rx; + uint32_t h_mat_size = ce_params.num_tx * ce_params.num_rx; + + for (uint32_t i = 0; i < ce_params.num_sc; i++) { + for (uint32_t j = 0; j < ce_params.num_pilot_td; j++) { + uint32_t x_idx = (i * ce_params.num_pilot_td + j) * x_mat_size; + uint32_t y_idx = (i * ce_params.num_pilot_td + j) * y_mat_size; + uint32_t h_idx = + (i * ce_params.num_symbols + ce_params.pilot_time_indices[j]) * + h_mat_size; + armral_cmplx_channel_equalization_f32( + ce_params.num_tx, ce_params.num_rx, 1, (p_src_h + h_idx), + (p_src_y + y_idx), noise_est, (p_dst_x_tild + x_idx)); + } + } +} + +/* + * Function to run the channel estimation function for all the possible values + * of input parameters. The function returns a boolean value of `true` only + * if all the valid test cases pass. The function also performs equalization + * with the estimated channel if the value of the argument `equalize_data` is + * `true`. + */ +template +bool run_all_tests( + char const *function_name, bool equalize_data, + ChannelEstimationFunction channel_estimation_function_under_test) { + if (equalize_data) { + printf("\nTesting integration framework for %s\n", function_name); + } else { + printf("\nTesting framework for %s\n", function_name); + } + + bool passed = true; + const uint16_t par_list_num_td_pilots[] = {1, 2, 4}; + const uint16_t par_list_num_fd_pilots[] = {12, 24}; + const uint16_t par_list_num_antennae[] = {1, 2, 3, 4, 8, 16}; + uint16_t num_test_failed = 0; + float32_t noise_pwr = 0; + + // Run tests for different number of pilots in time and frequency domain + for (uint16_t num_td_pilot : par_list_num_td_pilots) { + for (uint16_t num_fd_pilot : par_list_num_fd_pilots) { + for (uint16_t num_tx_antenna : par_list_num_antennae) { + for (uint16_t num_rx_antenna : par_list_num_antennae) { + // Set parameters + armral_config_ce_params ce_params = + armral::channel_generate::fill_ce_params( + num_td_pilot, num_fd_pilot, num_tx_antenna, num_rx_antenna); + printf("\nNum TD pilots = %d, Num SC = %d, Num TX = %d, Num RX = %d, " + "Num Chnls = %d\n", + ce_params.num_pilot_td, ce_params.num_sc, ce_params.num_tx, + ce_params.num_rx, ce_params.num_chnls); + + // Declare variables for channel and pilot signals + uint32_t h_size = ce_params.num_sc * ce_params.num_symbols * + ce_params.num_tx * ce_params.num_rx; + uint32_t x_size = + ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_tx; + uint32_t y_size = + ce_params.num_sc * ce_params.num_pilot_td * ce_params.num_rx; + std::vector p_dst_ref_h(h_size); + std::vector p_dst_h(h_size); + std::vector p_src_x(x_size); + std::vector p_src_y(y_size); + + // Create reference matrices + armral::channel_generate::generate_reference_channel( + ce_params, p_dst_ref_h.data()); + + // Generate pilot signals and observations + armral::channel_generate::generate_pilot_data( + ce_params, p_src_x.data(), p_src_y.data(), p_dst_ref_h.data()); + + // Execute the function + armral_status ret_status = channel_estimation_function_under_test( + ce_params, p_src_x.data(), p_src_y.data(), p_dst_h.data(), + &noise_pwr); + if (ret_status == ARMRAL_SUCCESS) { + // Compare the channel estimate with the reference channel + printf("Channel Estimation:\t"); + num_test_failed += + check_result(ce_params, p_dst_h.data(), p_dst_ref_h.data()); + if (equalize_data) { + printf("Integration Check:\t"); + // Equalize the received signal with respect to both estimated and + // reference channels to compare the EVMs + std::vector p_dst_x_tild(x_size); + equalize_pilot_data(ce_params, p_dst_h.data(), p_src_y.data(), + &noise_pwr, p_dst_x_tild.data()); + float32_t evm_est_ch = + get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); + equalize_pilot_data(ce_params, p_dst_ref_h.data(), p_src_y.data(), + &noise_pwr, p_dst_x_tild.data()); + float32_t evm_ref_ch = + get_evm(x_size, p_src_x.data(), p_dst_x_tild.data()); + // Compare the EVMs with estimated and reference channel + // The EVM limit is the same as that for channel estimation + float32_t evm_diff = fabs(evm_est_ch - evm_ref_ch); + printf("EVM difference = %f, EVM limit = %f\t\t", evm_diff, + EVM_LIMIT); + if (evm_diff > EVM_LIMIT) { + printf("Test case FAIL\n"); + } else { + printf("Test case PASS\n"); + } + num_test_failed += (uint16_t)(evm_diff > EVM_LIMIT); + } + } else if (ret_status == ARMRAL_ARGUMENT_ERROR) { + if (ce_params.num_pilot_fd < ce_params.num_tx) { + // This configuration is not supported + printf("Invalid argument\n"); + } else { + printf("Argument Error\n"); + num_test_failed++; + } + } else { + // Unknown return values are considered as failed case + printf("Unknown result:\tTest case failed\n"); + num_test_failed++; + } + } + } + } + } + + // Test passed if all cases passed + printf("\nNumber of failed cases = %d\n", num_test_failed); + printf("\nChannel Estimation test completed\n"); + passed = (num_test_failed == 0); + return passed; +} + +} // namespace armral::verify_channel -- GitLab