mirror of https://github.com/pvnis/srsRAN_4G.git
phy: add Cedron freq estimation algorithm
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
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue