phy: add Cedron freq estimation algorithm

master
Piotr Gawlowicz 1 year ago committed by Piotr
parent cb581e3b55
commit 4eb990c9c6

@ -0,0 +1,39 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_CEDRON_FREQ_ESTIMATOR_H
#define SRSRAN_CEDRON_FREQ_ESTIMATOR_H
#include "srsran/config.h"
#include "srsran/phy/common/phy_common.h"
#include <stdio.h>
typedef struct {
// DFT
void* in; // Input buffer
void* out; // Output buffer
void* p; // DFT plan
cf_t* X; // Output buffer as cf_t*
int init_size;
int fft_size; // Currently used FFT size
} srsran_cedron_freq_est_t;
SRSRAN_API int srsran_cedron_freq_est_init(srsran_cedron_freq_est_t* q, uint32_t nof_prbs);
SRSRAN_API void srsran_cedron_freq_est_free(srsran_cedron_freq_est_t* q);
SRSRAN_API int srsran_cedron_freq_est_replan_c(srsran_cedron_freq_est_t* q, int new_dft_points);
SRSRAN_API float srsran_cedron_freq_estimate(srsran_cedron_freq_est_t* q, const cf_t* x, int len);
#endif // SRSRAN_CEDRON_FREQ_ESTIMATOR_H

@ -29,6 +29,7 @@
#include "srsran/config.h"
#include "srsran/phy/ch_estimation/cedron_freq_estimator.h"
#include "srsran/phy/ch_estimation/chest_common.h"
#include "srsran/phy/ch_estimation/refsignal_ul.h"
#include "srsran/phy/common/phy_common.h"
@ -76,6 +77,7 @@ typedef struct {
srsran_interp_linsrsran_vec_t srsran_interp_linvec;
srsran_cedron_freq_est_t srsran_cedron_freq_est;
} srsran_chest_ul_t;
SRSRAN_API int srsran_chest_ul_init(srsran_chest_ul_t* q, uint32_t max_prb);

@ -69,6 +69,7 @@ typedef struct SRSRAN_API {
float threshold_data_valid_format3;
float threshold_dmrs_detection;
bool meas_ta_en;
bool use_cedron_alg;
// PUCCH configuration generated during a call to encode/decode
srsran_pucch_format_t format;

@ -72,6 +72,7 @@ typedef struct SRSRAN_API {
bool meas_epre_en;
bool meas_ta_en;
bool use_cedron_alg;
bool meas_evm_en;
} srsran_pusch_cfg_t;

@ -37,6 +37,7 @@ extern "C" {
#include "srsran/phy/common/timestamp.h"
#include "srsran/phy/utils/phy_logger.h"
#include "srsran/phy/ch_estimation/cedron_freq_estimator.h"
#include "srsran/phy/ch_estimation/chest_dl.h"
#include "srsran/phy/ch_estimation/chest_ul.h"
#include "srsran/phy/ch_estimation/csi_rs.h"

@ -0,0 +1,154 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include <complex.h>
#include <math.h>
#include <stdio.h>
#include "srsran/config.h"
#include "srsran/phy/ch_estimation/cedron_freq_estimator.h"
#include "srsran/phy/utils/vector_simd.h"
#include "srsran/srsran.h"
#include <fftw3.h>
static pthread_mutex_t freq_est_fft_mutex = PTHREAD_MUTEX_INITIALIZER;
int srsran_cedron_freq_est_init(srsran_cedron_freq_est_t* q, uint32_t nof_prbs)
{
int ret = SRSRAN_ERROR_INVALID_INPUTS;
int N = SRSRAN_MAX_PRB * SRSRAN_NRE;
if (q != NULL) {
bzero(q, sizeof(srsran_cedron_freq_est_t));
q->init_size = N;
q->fft_size = nof_prbs * SRSRAN_NRE;
q->in = fftwf_malloc(sizeof(fftwf_complex) * N);
if (!q->in) {
perror("fftwf_malloc");
goto clean_exit;
}
q->out = fftwf_malloc(sizeof(fftwf_complex) * N);
if (!q->out) {
perror("fftwf_malloc");
goto clean_exit;
}
pthread_mutex_lock(&freq_est_fft_mutex);
q->p = fftwf_plan_dft_1d(q->fft_size, q->in, q->out, FFTW_FORWARD, 0U);
pthread_mutex_unlock(&freq_est_fft_mutex);
if (!q->p) {
perror("fftwf_plan_dft_1d");
goto clean_exit;
}
q->X = q->out;
}
ret = SRSRAN_SUCCESS;
clean_exit:
if (ret != SRSRAN_SUCCESS) {
srsran_cedron_freq_est_free(q);
}
return ret;
}
void srsran_cedron_freq_est_free(srsran_cedron_freq_est_t* q)
{
if (!q) {
return;
}
pthread_mutex_lock(&freq_est_fft_mutex);
if (q->in) {
fftwf_free(q->in);
}
if (q->out) {
fftwf_free(q->out);
}
if (q->p) {
fftwf_destroy_plan(q->p);
q->p = NULL;
}
q->X = NULL;
pthread_mutex_unlock(&freq_est_fft_mutex);
bzero(q, sizeof(srsran_cedron_freq_est_t));
}
int srsran_cedron_freq_est_replan_c(srsran_cedron_freq_est_t* q, int new_dft_points)
{
// No change in size, skip re-planning
if (q->fft_size == new_dft_points) {
return 0;
}
pthread_mutex_lock(&freq_est_fft_mutex);
if (q->p) {
fftwf_destroy_plan(q->p);
q->p = NULL;
}
q->p = fftwf_plan_dft_1d(new_dft_points, q->in, q->out, FFTW_FORWARD, FFTW_MEASURE);
pthread_mutex_unlock(&freq_est_fft_mutex);
if (!q->p) {
return -1;
}
q->fft_size = new_dft_points;
return 0;
}
float srsran_cedron_freq_estimate(srsran_cedron_freq_est_t* q, const cf_t* x, int N)
{
/*
* Three Bin Exact Frequency Formulas for a Pure Complex Tone in a DFT
* Cedron Dawg
* https://www.dsprelated.com/showarticle/1043.php
*/
const float TWOPI = 2.0f * (float)M_PI;
cf_t Z[3], R1, num, den, ratio;
float alpha, f_est;
int32_t k_max;
if (N != q->fft_size) {
srsran_cedron_freq_est_replan_c(q, N);
}
memcpy(q->in, x, sizeof(cf_t) * N);
fftwf_execute(q->p);
k_max = srsran_vec_max_ci_simd(q->X, N);
if (k_max == 0) {
Z[0] = q->X[N - 1];
Z[1] = q->X[0];
Z[2] = q->X[1];
} else if (k_max == N - 1) {
Z[0] = q->X[N - 2];
Z[1] = q->X[N - 1];
Z[2] = q->X[0];
} else {
Z[0] = q->X[k_max - 1];
Z[1] = q->X[k_max];
Z[2] = q->X[k_max + 1];
}
R1 = cexpf(-1.0 * _Complex_I * TWOPI / N);
num = -R1 * Z[0] + (1 + R1) * Z[1] - Z[2];
den = -Z[0] + (1 + R1) * Z[1] - R1 * Z[2];
srsran_vec_div_ccc_simd(&num, &den, &ratio, 1);
alpha = atan2f(__imag__(ratio), __real__(ratio));
if (k_max > floor(N / 2)) {
k_max = -(N - k_max);
}
f_est = 1.0 * k_max / N + alpha * M_1_PI * 0.5f;
return -f_est;
}

@ -19,6 +19,7 @@
#include <strings.h>
#include "srsran/config.h"
#include "srsran/phy/ch_estimation/cedron_freq_estimator.h"
#include "srsran/phy/ch_estimation/chest_ul.h"
#include "srsran/phy/dft/dft_precoding.h"
#include "srsran/phy/utils/convolution.h"
@ -89,6 +90,11 @@ int srsran_chest_ul_init(srsran_chest_ul_t* q, uint32_t max_prb)
ERROR("Error allocating memory for pregenerated signals");
goto clean_exit;
}
if (srsran_cedron_freq_est_init(&q->srsran_cedron_freq_est, max_prb)) {
ERROR("Error initializing cedron freq estimation algorithm.");
goto clean_exit;
}
}
ret = SRSRAN_SUCCESS;
@ -108,6 +114,7 @@ void srsran_chest_ul_free(srsran_chest_ul_t* q)
free(q->tmp_noise);
}
srsran_interp_linear_vector_free(&q->srsran_interp_linvec);
srsran_cedron_freq_est_free(&q->srsran_cedron_freq_est);
if (q->pilot_estimates) {
free(q->pilot_estimates);
@ -284,6 +291,7 @@ static void chest_ul_estimate(srsran_chest_ul_t* q,
uint32_t nrefs_sym,
uint32_t stride,
bool meas_ta_en,
bool use_cedron_alg,
bool write_estimates,
uint32_t n_prb[SRSRAN_NOF_SLOTS_PER_SF],
srsran_chest_ul_res_t* res)
@ -301,9 +309,15 @@ static void chest_ul_estimate(srsran_chest_ul_t* q,
float ta_err = 0.0f;
if (meas_ta_en) {
for (int i = 0; i < nslots; i++) {
if (use_cedron_alg) {
ta_err +=
srsran_cedron_freq_estimate(&q->srsran_cedron_freq_est, &q->pilot_estimates[i * nrefs_sym], nrefs_sym) /
nslots;
} else {
ta_err += srsran_vec_estimate_frequency(&q->pilot_estimates[i * nrefs_sym], nrefs_sym) / nslots;
}
}
}
// Calculate actual time alignment error in micro-seconds
if (isnormal(ta_err) && stride > 0) {
@ -403,7 +417,8 @@ int srsran_chest_ul_estimate_pusch(srsran_chest_ul_t* q,
nrefs_sf);
// Estimate
chest_ul_estimate(q, SRSRAN_NOF_SLOTS_PER_SF, nrefs_sym, 1, cfg->meas_ta_en, true, cfg->grant.n_prb, res);
chest_ul_estimate(
q, SRSRAN_NOF_SLOTS_PER_SF, nrefs_sym, 1, cfg->meas_ta_en, cfg->use_cedron_alg, true, cfg->grant.n_prb, res);
return 0;
}
@ -506,10 +521,16 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q,
float ta_err = 0.0f;
for (int ns = 0; ns < SRSRAN_NOF_SLOTS_PER_SF; ns++) {
for (int i = 0; i < n_rs; i++) {
if (cfg->use_cedron_alg) {
ta_err += srsran_cedron_freq_estimate(
&q->srsran_cedron_freq_est, &q->pilot_estimates[(i + ns * n_rs) * SRSRAN_NRE], SRSRAN_NRE) /
(float)(SRSRAN_NOF_SLOTS_PER_SF * n_rs);
} else {
ta_err += srsran_vec_estimate_frequency(&q->pilot_estimates[(i + ns * n_rs) * SRSRAN_NRE], SRSRAN_NRE) /
(float)(SRSRAN_NOF_SLOTS_PER_SF * n_rs);
}
}
}
// Calculate actual time alignment error in micro-seconds
if (isnormal(ta_err)) {
@ -611,7 +632,7 @@ int srsran_chest_ul_estimate_srs(srsran_chest_ul_t* q,
// Estimate
uint32_t n_prb[2] = {};
chest_ul_estimate(q, 1, n_srs_re, 1, true, false, n_prb, res);
chest_ul_estimate(q, 1, n_srs_re, 1, true, false, false, n_prb, res);
return SRSRAN_SUCCESS;
}

@ -21,6 +21,9 @@ add_test(dft_dc dft_test -b -d) # Backwards first & handle dc internally
add_test(dft_odd dft_test -N 255) # Odd-length
add_test(dft_odd_dc dft_test -N 255 -b -d) # Odd-length, backwards first, handle dc
add_executable(freq_est_test freq_est_test.c)
target_link_libraries(freq_est_test srsran_phy)
########################################################################
# Algebra TEST
########################################################################

@ -0,0 +1,154 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/random.h"
#include <complex.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "srsran/phy/ch_estimation/cedron_freq_estimator.h"
#include "srsran/phy/dft/dft.h"
#include "srsran/phy/utils/vector.h"
static bool verbose = false;
#define MAXIMUM_ERROR (1e-6f)
double elapsed_us(struct timeval* ts_start, struct timeval* ts_end)
{
if (ts_end->tv_usec > ts_start->tv_usec) {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec) * 1000000 + (double)ts_end->tv_usec -
(double)ts_start->tv_usec;
} else {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec - 1) * 1000000 + ((double)ts_end->tv_usec + 1000000) -
(double)ts_start->tv_usec;
}
}
#define RUN_TEST(FUNCTION) \
do { \
int nof_prb; \
struct timeval start, end; \
gettimeofday(&start, NULL); \
bool passed_ = true; \
for (nof_prb = 1; nof_prb < SRSRAN_MAX_PRB; nof_prb++) { \
passed_ &= FUNCTION(nof_prb); \
} \
gettimeofday(&end, NULL); \
if (verbose) \
printf("%32s: %s ... %6.2f us/call\n", \
#FUNCTION, \
(passed_) ? "Pass" : "Fail", \
elapsed_us(&start, &end) / SRSRAN_MAX_PRB); \
passed &= passed_; \
} while (false)
static bool test_cedron_estimate_frequency(int nof_prbs)
{
srsran_cedron_freq_est_t srsran_cedron_freq_est;
float freq_gold = 0.2f;
float freq = 0.0f;
float mse = 0.0f;
uint32_t nof_sym = nof_prbs * SRSRAN_NRE;
cf_t* x = srsran_vec_malloc(sizeof(cf_t) * nof_sym);
if (srsran_cedron_freq_est_init(&srsran_cedron_freq_est, nof_prbs)) {
ERROR("Error initializing cedron freq estimation algorithm.");
return false;
}
for (int i = 0; i < nof_sym; i++) {
x[i] = cexpf(-I * 2.0f * M_PI * (float)i * freq_gold);
}
freq = srsran_cedron_freq_estimate(&srsran_cedron_freq_est, x, nof_sym);
mse = fabsf(freq - freq_gold);
free(x);
srsran_cedron_freq_est_free(&srsran_cedron_freq_est);
if (verbose)
printf("Nof PRBs %i, mse %f\n", nof_prbs, mse);
return (mse < MAXIMUM_ERROR);
}
bool test_real_signal(void)
{
uint32_t nof_prbs = 4;
uint32_t nrefs_sym = nof_prbs * SRSRAN_NRE;
float ta_err_cedron = 0;
float ta_err_srs = 0;
srsran_cedron_freq_est_t srsran_cedron_freq_est;
// Sniffed UL REF signal with low SNR.
cf_t cp_pilots[48] = {22.162853 - 26.839521 * 1i, -12.896494 + 3.750004 * 1i, 43.889961 + 7.452690 * 1i,
36.788181 + 3.699238 * 1i, 19.841988 + 2.327892 * 1i, -8.030174 + 15.597110 * 1i,
23.685257 + 9.359170 * 1i, -0.184066 + 14.776085 * 1i, 54.138931 + 14.602448 * 1i,
33.998699 + 11.438558 * 1i, 8.634534 + 23.158798 * 1i, 11.593168 + 14.001324 * 1i,
-4.070977 - 28.250189 * 1i, 18.821701 + 1.274709 * 1i, -2.113699 - 2.322813 * 1i,
-1.980798 - 2.809317 * 1i, -16.248312 + 16.282543 * 1i, 4.916372 - 8.317366 * 1i,
19.537739 + 5.440768 * 1i, 19.273443 + 21.419304 * 1i, 9.158796 - 14.670293 * 1i,
12.963399 + 16.209164 * 1i, -10.091204 - 0.774263 * 1i, 52.113579 - 62.882523 * 1i,
-45.814278 - 3.351721 * 1i, 16.937546 + 32.659332 * 1i, -2.446608 + 2.216692 * 1i,
-13.836332 + 19.213146 * 1i, -21.508173 + 43.013851 * 1i, -21.323523 + 21.740101 * 1i,
-2.203827 - 12.458035 * 1i, 0.313410 - 8.307796 * 1i, -15.429630 + 14.476921 * 1i,
-8.512527 + 34.065918 * 1i, -16.693293 + 31.356386 * 1i, -34.033825 + 5.859118 * 1i,
-11.836067 + 20.825031 * 1i, -24.690987 + 41.358925 * 1i, -11.794442 + 3.393625 * 1i,
-18.838444 + 9.678068 * 1i, 7.530683 + 42.732479 * 1i, -17.050388 + 32.361870 * 1i,
-3.941456 + 13.747462 * 1i, -19.360886 + 11.063116 * 1i, -16.969175 + 30.928513 * 1i,
-14.056345 - 35.506645 * 1i, -23.354206 - 9.430195 * 1i, 3.566646 - 14.499187 * 1i};
if (srsran_cedron_freq_est_init(&srsran_cedron_freq_est, nof_prbs)) {
ERROR("Error initializing cedron freq estimation algorithm.");
return false;
}
ta_err_cedron = srsran_cedron_freq_estimate(&srsran_cedron_freq_est, cp_pilots, nrefs_sym);
ta_err_srs = srsran_vec_estimate_frequency(cp_pilots, nrefs_sym);
if (verbose) {
printf("Cedron ta_err = %f \n", ta_err_cedron);
printf("SRS ta_err = %f \n", ta_err_srs);
}
ta_err_cedron /= 15e3f; // Convert from normalized frequency to seconds
ta_err_cedron *= 1e6f; // Convert to micro-seconds
ta_err_srs /= 15e3f; // Convert from normalized frequency to seconds
ta_err_srs *= 1e6f; // Convert to micro-seconds
if (verbose) {
printf("Cedron TA_err %.1f \n", ta_err_srs);
printf("SRS TA_err %.1f \n", ta_err_srs);
}
srsran_cedron_freq_est_free(&srsran_cedron_freq_est);
return true;
}
int main(int argc, char** argv)
{
bool passed = true;
int ret = SRSRAN_SUCCESS;
RUN_TEST(test_cedron_estimate_frequency);
passed &= test_real_signal();
printf("%s!\n", (passed) ? "Ok" : "Failed");
if (!passed) {
ret = SRSRAN_ERROR;
}
return ret;
}

@ -424,6 +424,7 @@ enable = false
# s1_setup_max_retries: Maximum amount of retries to setup the S1AP connection. If this value is exceeded, an alarm is written to the log. -1 means infinity.
# s1_connect_timer: Connection Retry Timer for S1 connection (seconds)
# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings
# use_cedron_f_est_alg: Whether to use Cedron algorithm for TA estimation or not (Default: false)
#####################################################################
[expert]
#pusch_max_its = 8 # These are half iterations
@ -462,3 +463,4 @@ enable = false
#s1_connect_timer = 10
#rx_gain_offset = 62
#mac_prach_bi = 0
#use_cedron_f_est_alg = false

@ -63,6 +63,7 @@ struct phy_args_t {
bool pusch_meas_evm = false;
bool pusch_meas_ta = true;
bool pucch_meas_ta = true;
bool use_cedron_alg = false;
uint32_t nof_prach_threads = 1;
bool extended_cp = false;
srsran::channel::args_t dl_channel_args;

@ -283,6 +283,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.sctp_max_init_timeo)", bpo::value<int32_t>(&args->stack.s1ap.sctp_max_init_timeo)->default_value(5000), "Maximum SCTP init timeout.")
("expert.rx_gain_offset", bpo::value<float>(&args->phy.rx_gain_offset)->default_value(62), "RX Gain offset to add to rx_gain to calibrate RSRP readings")
("expert.mac_prach_bi", bpo::value<uint32_t>(&args->stack.mac.prach_bi)->default_value(0), "Backoff Indicator to reduce contention in the PRACH channel")
("expert.use_cedron_f_est_alg", bpo::value<bool>(&args->phy.use_cedron_alg)->default_value(false), "Whether to use Cedron freq estimation algorithm or not")
// eMBMS section
("embms.enable", bpo::value<bool>(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB")

@ -90,6 +90,7 @@ inline void phy_ue_db::_set_common_config_rnti(uint16_t rnti, srsran::phy_cfg_t&
phy_cfg.ul_cfg.pusch.meas_time_en = true;
phy_cfg.ul_cfg.pusch.meas_epre_en = phy_args->pusch_meas_epre;
phy_cfg.ul_cfg.pusch.meas_ta_en = phy_args->pusch_meas_ta;
phy_cfg.ul_cfg.pusch.use_cedron_alg = phy_args->use_cedron_alg;
phy_cfg.ul_cfg.pusch.meas_evm_en = phy_args->pusch_meas_evm;
phy_cfg.ul_cfg.pusch.max_nof_iterations = phy_args->pusch_max_its;
phy_cfg.ul_cfg.pucch.threshold_format1 = SRSRAN_PUCCH_DEFAULT_THRESHOLD_FORMAT1;
@ -98,6 +99,7 @@ inline void phy_ue_db::_set_common_config_rnti(uint16_t rnti, srsran::phy_cfg_t&
phy_cfg.ul_cfg.pucch.threshold_data_valid_format3 = SRSRAN_PUCCH_DEFAULT_THRESHOLD_FORMAT3;
phy_cfg.ul_cfg.pucch.threshold_dmrs_detection = SRSRAN_PUCCH_DEFAULT_THRESHOLD_DMRS;
phy_cfg.ul_cfg.pucch.meas_ta_en = phy_args->pucch_meas_ta;
phy_cfg.ul_cfg.pucch.use_cedron_alg = phy_args->use_cedron_alg;
}
inline uint32_t phy_ue_db::_get_ue_cc_idx(uint16_t rnti, uint32_t enb_cc_idx) const

Loading…
Cancel
Save