From 68ad4960eb2a7f933da1931b13d9d90afd6c4928 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 23 Jan 2020 13:28:47 +0100 Subject: [PATCH] SRSLTE: added holding time before AGC starts measuring after setting gain --- lib/include/srslte/phy/agc/agc.h | 54 +++++--- lib/src/phy/agc/agc.c | 225 +++++++++++++++++-------------- 2 files changed, 160 insertions(+), 119 deletions(-) diff --git a/lib/include/srslte/phy/agc/agc.h b/lib/include/srslte/phy/agc/agc.h index cfb56c1b1..e68fc0609 100644 --- a/lib/include/srslte/phy/agc/agc.h +++ b/lib/include/srslte/phy/agc/agc.h @@ -38,31 +38,53 @@ #include "srslte/config.h" #define SRSLTE_AGC_CALLBACK(NAME) void(NAME)(void* h, float gain_db) -#define SRSLTE_AGC_DEFAULT_TARGET 0.3 -#define SRSLTE_AGC_DEFAULT_BW 0.7 +#define SRSLTE_AGC_DEFAULT_TARGET (0.3f) /* Average RMS target or maximum peak target*/ +#define SRSLTE_AGC_DEFAULT_BW (0.3f) /* Moving average coefficient */ +#define SRSLTE_AGC_HOLD_COUNT (20) /* Number of frames to wait after setting the gain before start measuring */ +#define SRSLTE_AGC_MIN_MEASUREMENTS (10) /* Minimum number of measurements */ +#define SRSLTE_AGC_MIN_GAIN_OFFSET (2.0f) /* Mimum of gain offset to set the radio gain */ typedef enum SRSLTE_API { SRSLTE_AGC_MODE_ENERGY = 0, SRSLTE_AGC_MODE_PEAK_AMPLITUDE } srslte_agc_mode_t; +/* + * The AGC has been implemented using 3 states: + * - init: it simply starts the process of measuring + * - measure: performs a minimum of SRSLTE_AGC_MIN_MEASUREMENTS and does not set the gain until it needs + * SRSLTE_AGC_MIN_GAIN_OFFSET dB more of gain. The gain is set in the enter hold transition. + * - hold: waits for SRSLTE_AGC_HOLD_COUNT frames as a Rx gain transition period. After this period, it enters measure + * state. + * + * FSM abstraction: + * + * +------+ Enter measure +---------+ Enter hold +------+ + * | init | ---------------->| Measure |--------------->| Hold | + * +------+ +---------+ +------+ + * ^ Enter measure | + * +-------------------------+ + */ + +typedef enum { SRSLTE_AGC_STATE_INIT = 0, SRSLTE_AGC_STATE_MEASURE, SRSLTE_AGC_STATE_HOLD } srslte_agc_state_t; + typedef struct SRSLTE_API { float bandwidth; - float gain; + float gain_db; + float gain_offset_db; float min_gain_db; float max_gain_db; float default_gain_db; float y_out; - bool lock; bool isfirst; void* uhd_handler; SRSLTE_AGC_CALLBACK(*set_gain_callback); - srslte_agc_mode_t mode; - float target; - uint32_t nof_frames; - uint32_t frame_cnt; - float* y_tmp; + srslte_agc_mode_t mode; + float target; + uint32_t nof_frames; + uint32_t frame_cnt; + uint32_t hold_cnt; + float* y_tmp; + srslte_agc_state_t state; } srslte_agc_t; -SRSLTE_API int srslte_agc_init(srslte_agc_t* q, srslte_agc_mode_t mode); - SRSLTE_API int srslte_agc_init_acc(srslte_agc_t* q, srslte_agc_mode_t mode, uint32_t nof_frames); SRSLTE_API int srslte_agc_init_uhd(srslte_agc_t* q, @@ -77,20 +99,10 @@ SRSLTE_API void srslte_agc_reset(srslte_agc_t* q); SRSLTE_API void srslte_agc_set_gain_range(srslte_agc_t* q, float min_gain_db, float max_gain_db); -SRSLTE_API void srslte_agc_set_bandwidth(srslte_agc_t* q, float bandwidth); - -SRSLTE_API void srslte_agc_set_target(srslte_agc_t* q, float target); - -SRSLTE_API float srslte_agc_get_rssi(srslte_agc_t* q); - -SRSLTE_API float srslte_agc_get_output_level(srslte_agc_t* q); - SRSLTE_API float srslte_agc_get_gain(srslte_agc_t* q); SRSLTE_API void srslte_agc_set_gain(srslte_agc_t* q, float init_gain_value_db); -SRSLTE_API void srslte_agc_lock(srslte_agc_t* q, bool enable); - SRSLTE_API void srslte_agc_process(srslte_agc_t* q, cf_t* signal, uint32_t len); #endif // SRSLTE_AGC_H diff --git a/lib/src/phy/agc/agc.c b/lib/src/phy/agc/agc.c index 309326211..b45216700 100644 --- a/lib/src/phy/agc/agc.c +++ b/lib/src/phy/agc/agc.c @@ -20,22 +20,11 @@ */ #include -#include -#include -#include -#include - -#include "srslte/phy/utils/debug.h" #include "srslte/phy/agc/agc.h" #include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/vector.h" -int srslte_agc_init(srslte_agc_t* q, srslte_agc_mode_t mode) -{ - return srslte_agc_init_acc(q, mode, 0); -} - int srslte_agc_init_acc(srslte_agc_t* q, srslte_agc_mode_t mode, uint32_t nof_frames) { bzero(q, sizeof(srslte_agc_t)); @@ -44,7 +33,7 @@ int srslte_agc_init_acc(srslte_agc_t* q, srslte_agc_mode_t mode, uint32_t nof_fr q->max_gain_db = 90.0; q->min_gain_db = 0.0; if (nof_frames > 0) { - q->y_tmp = srslte_vec_malloc(sizeof(float) * nof_frames); + q->y_tmp = srslte_vec_f_malloc(nof_frames); if (!q->y_tmp) { return SRSLTE_ERROR; } @@ -81,11 +70,9 @@ void srslte_agc_free(srslte_agc_t* q) void srslte_agc_reset(srslte_agc_t* q) { + q->state = SRSLTE_AGC_STATE_INIT; q->bandwidth = SRSLTE_AGC_DEFAULT_BW; - q->lock = false; - q->gain = srslte_convert_dB_to_power(q->default_gain_db); - q->y_out = 1.0; - q->isfirst = true; + q->gain_db = q->default_gain_db; if (q->set_gain_callback && q->uhd_handler) { q->set_gain_callback(q->uhd_handler, q->default_gain_db); } @@ -94,115 +81,157 @@ void srslte_agc_reset(srslte_agc_t* q) void srslte_agc_set_gain_range(srslte_agc_t* q, float min_gain_db, float max_gain_db) { if (q) { - q->min_gain_db = min_gain_db; - q->max_gain_db = max_gain_db; + q->min_gain_db = min_gain_db; + q->max_gain_db = max_gain_db; q->default_gain_db = (max_gain_db + min_gain_db) / 2.0f; } } -void srslte_agc_set_bandwidth(srslte_agc_t* q, float bandwidth) +float srslte_agc_get_gain(srslte_agc_t* q) { - q->bandwidth = bandwidth; + return q->gain_db; } -void srslte_agc_set_target(srslte_agc_t* q, float target) +void srslte_agc_set_gain(srslte_agc_t* q, float init_gain_value_db) { - q->target = target; + q->gain_db = init_gain_value_db; } -float srslte_agc_get_rssi(srslte_agc_t* q) +/* + * Transition functions + */ +static inline void agc_enter_state_hold(srslte_agc_t* q) { - return q->target / q->gain; -} + // Bound gain in dB + float gain_db = q->gain_db + q->gain_offset_db; + if (gain_db < q->min_gain_db) { + gain_db = q->min_gain_db + 5.0; + INFO("Warning: Rx signal strength is too high. Forcing minimum Rx gain %.2fdB\n", gain_db); + } else if (gain_db > q->max_gain_db) { + gain_db = q->max_gain_db; + INFO("Warning: Rx signal strength is too weak. Forcing maximum Rx gain %.2fdB\n", gain_db); + } else if (isinf(gain_db) || isnan(gain_db)) { + gain_db = q->default_gain_db; + INFO("Warning: AGC went to an unknown state. Setting Rx gain to %.2fdB\n", gain_db); + } -float srslte_agc_get_output_level(srslte_agc_t* q) -{ - return q->y_out; -} + // Set gain + q->set_gain_callback(q->uhd_handler, gain_db); + q->gain_db = gain_db; -float srslte_agc_get_gain(srslte_agc_t* q) -{ - return q->gain; + // Set holding period + q->hold_cnt = 0; + q->state = SRSLTE_AGC_STATE_HOLD; } -void srslte_agc_set_gain(srslte_agc_t* q, float init_gain_value_db) +static inline void agc_enter_state_measure(srslte_agc_t* q) { - q->gain = srslte_convert_dB_to_power(init_gain_value_db); + q->hold_cnt = 0; + q->isfirst = true; + q->state = SRSLTE_AGC_STATE_MEASURE; } -void srslte_agc_lock(srslte_agc_t* q, bool enable) +/* + * Running state functions + */ +static inline void agc_run_state_init(srslte_agc_t* q) { - q->lock = enable; + agc_enter_state_measure(q); } -void srslte_agc_process(srslte_agc_t* q, cf_t* signal, uint32_t len) +static inline void agc_run_state_measure(srslte_agc_t* q, cf_t* signal, uint32_t len) { - if (!q->lock) { - float gain_db = srslte_convert_power_to_dB(q->gain); - - float y = 0; - // Apply current gain to input signal - if (!q->uhd_handler) { - srslte_vec_sc_prod_cfc(signal, q->gain, signal, len); - } else { - if (gain_db < q->min_gain_db) { - gain_db = q->min_gain_db + 5.0; - INFO("Warning: Rx signal strength is too high. Forcing minimum Rx gain %.2fdB\n", gain_db); - } else if (gain_db > q->max_gain_db) { - gain_db = q->max_gain_db; - INFO("Warning: Rx signal strength is too weak. Forcing maximum Rx gain %.2fdB\n", gain_db); - } else if (isinf(gain_db) || isnan(gain_db)) { - gain_db = q->default_gain_db; - INFO("Warning: AGC went to an unknown state. Setting Rx gain to %.2fdB\n", gain_db); - } - // Set gain - q->set_gain_callback(q->uhd_handler, gain_db); - q->gain = srslte_convert_dB_to_power(gain_db); - } - float* t; - switch (q->mode) { - case SRSLTE_AGC_MODE_ENERGY: - y = sqrtf(crealf(srslte_vec_dot_prod_conj_ccc(signal, signal, len)) / len); - break; - case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: - t = (float*)signal; - y = t[srslte_vec_max_fi(t, 2 * len)]; // take only positive max to avoid abs() (should be similar) - break; - default: - ERROR("Unsupported AGC mode\n"); - return; - } + // Perform measurement of the frame + float y = 0; + float* t; + switch (q->mode) { + case SRSLTE_AGC_MODE_ENERGY: + y = sqrtf(crealf(srslte_vec_dot_prod_conj_ccc(signal, signal, len)) / len); + break; + case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: + t = (float*)signal; + y = t[srslte_vec_max_fi(t, 2 * len)]; // take only positive max to avoid abs() (should be similar) + break; + default: + ERROR("Unsupported AGC mode\n"); + return; + } - if (q->nof_frames > 0) { - q->y_tmp[q->frame_cnt++] = y; - if (q->frame_cnt == q->nof_frames) { - q->frame_cnt = 0; - switch (q->mode) { - case SRSLTE_AGC_MODE_ENERGY: - y = srslte_vec_acc_ff(q->y_tmp, q->nof_frames) / q->nof_frames; - break; - case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: - y = q->y_tmp[srslte_vec_max_fi(q->y_tmp, q->nof_frames)]; - break; - default: - ERROR("Unsupported AGC mode\n"); - return; - } + // Perform averaging if configured + if (q->nof_frames > 0) { + q->y_tmp[q->frame_cnt++] = y; + if (q->frame_cnt == q->nof_frames) { + q->frame_cnt = 0; + switch (q->mode) { + case SRSLTE_AGC_MODE_ENERGY: + y = srslte_vec_acc_ff(q->y_tmp, q->nof_frames) / q->nof_frames; + break; + case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: + y = q->y_tmp[srslte_vec_max_fi(q->y_tmp, q->nof_frames)]; + break; + default: + ERROR("Unsupported AGC mode\n"); + return; } } + } - if (q->isfirst) { - q->y_out = y; - q->isfirst = false; - } else { - if (q->frame_cnt == 0) { - q->y_out = (1 - q->bandwidth) * q->y_out + q->bandwidth * y; - if (!q->lock) { - q->gain *= q->target / q->y_out; - } - INFO("AGC gain: %.2f y_out=%.3f, y=%.3f target=%.1f\n", gain_db, q->y_out, y, q->target); - } + // Update gain + if (q->isfirst) { + q->y_out = y; + q->isfirst = false; + q->gain_offset_db = 0.0f; + } else { + if (q->frame_cnt == 0) { + q->y_out = SRSLTE_VEC_EMA(y, q->y_out, q->bandwidth); + q->gain_offset_db = srslte_convert_amplitude_to_dB(q->target) - srslte_convert_amplitude_to_dB(q->y_out); + INFO("AGC gain offset: %.2f y_out=%.3f, y=%.3f target=%.1f\n", q->gain_offset_db, q->y_out, y, q->target); } } + + // Check minimum of frame measurements count + if (q->hold_cnt < SRSLTE_AGC_MIN_MEASUREMENTS) { + // Increment hold counter + q->hold_cnt++; + } else if (fabsf(q->gain_offset_db) > SRSLTE_AGC_MIN_GAIN_OFFSET) { + // Wait for a minimum of measurements and a minimum gain offset before setting gain + agc_enter_state_hold(q); + } +} + +static inline void agc_run_state_hold(srslte_agc_t* q) +{ + // Increment holding counter + q->hold_cnt++; + + // Check holding counter + if (q->hold_cnt >= SRSLTE_AGC_HOLD_COUNT) { + // Enter state measure + agc_enter_state_measure(q); + } +} + +/* + * AGC FSM entry function + */ +void srslte_agc_process(srslte_agc_t* q, cf_t* signal, uint32_t len) +{ + // Apply current gain to input signal + if (!q->uhd_handler) { + srslte_vec_sc_prod_cfc(signal, srslte_convert_dB_to_amplitude(q->gain_db), signal, len); + } + + // Run FSM state + switch (q->state) { + case SRSLTE_AGC_STATE_HOLD: + agc_run_state_hold(q); + break; + case SRSLTE_AGC_STATE_MEASURE: + agc_run_state_measure(q, signal, len); + break; + case SRSLTE_AGC_STATE_INIT: + default: + agc_run_state_init(q); + } }