Improved UE measurements. Calibrated for gain 40 dB

This commit is contained in:
ismagom 2014-11-26 17:24:10 +00:00
parent 632e841dbe
commit 445fc639e8
8 changed files with 82 additions and 63 deletions

View File

@ -41,6 +41,11 @@
#include "liblte/cuhd/cuhd.h" #include "liblte/cuhd/cuhd.h"
#include "cell_search_utils.h" #include "cell_search_utils.h"
#define B210_DEFAULT_GAIN 40.0
#define B210_DEFAULT_GAIN_CORREC 76.0 // Gain of the Rx chain when the gain is set to 40
float gain_offset = B210_DEFAULT_GAIN_CORREC;
cell_detect_cfg_t cell_detect_config = { cell_detect_cfg_t cell_detect_config = {
500, // nof_frames_total 500, // nof_frames_total
50, // nof_frames_detected 50, // nof_frames_detected
@ -64,7 +69,7 @@ void args_default(prog_args_t *args) {
args->force_N_id_2 = -1; // Pick the best args->force_N_id_2 = -1; // Pick the best
args->uhd_args = ""; args->uhd_args = "";
args->uhd_freq = -1.0; args->uhd_freq = -1.0;
args->uhd_gain = 60.0; args->uhd_gain = B210_DEFAULT_GAIN;
} }
void usage(prog_args_t *args, char *prog) { void usage(prog_args_t *args, char *prog) {
@ -141,7 +146,7 @@ int main(int argc, char **argv) {
int n; int n;
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN]; uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
uint32_t sfn_offset; uint32_t sfn_offset;
float rssi=0, rsrp=0, rsrq=0, snr=0; float rssi_utra=0,rssi=0, rsrp=0, rsrq=0, snr=0;
cf_t *nullce[MAX_PORTS]; cf_t *nullce[MAX_PORTS];
for (int i=0;i<MAX_PORTS;i++) { for (int i=0;i<MAX_PORTS;i++) {
@ -264,19 +269,22 @@ int main(int argc, char **argv) {
chest_dl_estimate(&chest, sf_symbols, nullce, ue_sync_get_sfidx(&ue_sync)); chest_dl_estimate(&chest, sf_symbols, nullce, ue_sync_get_sfidx(&ue_sync));
rssi = VEC_CMA(chest_dl_get_rssi(&chest),rssi,nframes);
rsrq = VEC_CMA(chest_dl_get_rsrq(&chest),rsrq,nframes); rssi = VEC_CMA(vec_avg_power_cf(sf_buffer,SF_LEN(lte_symbol_sz(cell.nof_prb))),rssi,nframes);
rssi_utra = VEC_CMA(chest_dl_get_rssi(&chest),rssi_utra,nframes);
rsrq = VEC_EMA(chest_dl_get_rsrq(&chest),rsrq,0.001);
rsrp = VEC_CMA(chest_dl_get_rsrp(&chest),rsrp,nframes); rsrp = VEC_CMA(chest_dl_get_rsrp(&chest),rsrp,nframes);
snr = VEC_CMA(chest_dl_get_snr(&chest),snr,nframes); snr = VEC_CMA(chest_dl_get_snr(&chest),snr,nframes);
nframes++; nframes++;
// Plot and Printf // Plot and Printf
if ((nframes%10) == 0) { if ((nframes%10) == 0) {
printf("CFO: %+8.4f KHz, SFO: %+8.4f Khz, RSSI: %+5.1f dBm, " printf("CFO: %+8.4f KHz, SFO: %+8.4f Khz, RSSI: %5.1f dBm, RSSI/ref-symbol: %+5.1f dBm, "
"RSRP: %+5.1f dBm, RSRQ: %5.1f dB, SNR: %5.1f dB\r", "RSRP: %+5.1f dBm, RSRQ: %5.1f dB, SNR: %5.1f dB\r",
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000, ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
10*log10(rssi*1000/4/cell.nof_prb/12/2)-prog_args.uhd_gain, 10*log10(rssi*1000)-gain_offset,
10*log10(rsrp*1000)-prog_args.uhd_gain, 10*log10(rssi_utra*1000)-gain_offset,
10*log10(rsrp*1000)-gain_offset,
10*log10(rsrq), 10*log10(snr)); 10*log10(rsrq), 10*log10(snr));
} }
break; break;

View File

@ -253,8 +253,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout); fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
} }
nof_trials++; nof_trials++;
snr = VEC_CMA(chest_dl_get_snr(&ue_dl.chest), snr, nof_trials);
} }
snr = VEC_EMA(chest_dl_get_snr(&ue_dl.chest), snr, 0.01);
} }
} }
if (ue_sync_get_sfidx(&ue_sync) == 9) { if (ue_sync_get_sfidx(&ue_sync) == 9) {

View File

@ -69,7 +69,7 @@ typedef struct {
interp_linvec_t interp_linvec; interp_linvec_t interp_linvec;
interp_lin_t interp_lin; interp_lin_t interp_lin;
float rssi; float rssi[MAX_PORTS];
float rsrp[MAX_PORTS]; float rsrp[MAX_PORTS];
float noise_estimate[MAX_PORTS]; float noise_estimate[MAX_PORTS];
} chest_dl_t; } chest_dl_t;

View File

@ -36,10 +36,10 @@
typedef _Complex float cf_t; typedef _Complex float cf_t;
// Cumulative moving average // Cumulative moving average
#define VEC_CMA(data, average, n) ((data) + ((data) - (average)) / ((n)+1)) #define VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n)+1))
// Exponential moving average // Exponential moving average
#define VEC_EMA(data, average, alpha) ((alpha)*(data)+(1-alpha)*(average)) #define VEC_EMA(data, average, alpha) (average)==0?(data):((alpha)*(data)+(1-alpha)*(average))
/** Return the sum of all the elements */ /** Return the sum of all the elements */
LIBLTE_API int vec_acc_ii(int *x, uint32_t len); LIBLTE_API int vec_acc_ii(int *x, uint32_t len);

View File

@ -280,26 +280,21 @@ static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t port_id)
} }
} }
float chest_dl_rssi(lte_cell_t cell, cf_t *input) { float chest_dl_rssi(chest_dl_t *q, cf_t *input, uint32_t port_id) {
float rssi = 0; uint32_t l;
uint32_t l, p;
uint32_t loop_ports = cell.nof_ports>2?2:1;
for (p=0;p<loop_ports;p++) { float rssi = 0;
uint32_t nsymbols = refsignal_cs_nof_symbols(2*p); uint32_t nsymbols = refsignal_cs_nof_symbols(port_id);
for (l=0;l<nsymbols;l++) { for (l=0;l<nsymbols;l++) {
cf_t *tmp = &input[refsignal_nsymbol(l,cell.cp, 2*p) * cell.nof_prb * RE_X_RB]; cf_t *tmp = &input[refsignal_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB];
rssi += crealf(vec_dot_prod_conj_ccc(tmp, tmp, cell.nof_prb * RE_X_RB)); rssi += vec_dot_prod_conj_ccc(tmp,tmp,q->cell.nof_prb * RE_X_RB);
} }
} return rssi/nsymbols;
return rssi;
} }
float chest_dl_rsrp(chest_dl_t *q, uint32_t port_id) { float chest_dl_rsrp(chest_dl_t *q, uint32_t port_id) {
return crealf(vec_dot_prod_conj_ccc(q->pilot_estimates_average[port_id], return vec_avg_power_cf(q->pilot_recv_signal[port_id],
q->pilot_estimates_average[port_id], REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)))
/ REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id);
} }
int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id) int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id)
@ -307,16 +302,23 @@ int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx
/* Get references from the input signal */ /* Get references from the input signal */
refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal[port_id]); refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal[port_id]);
/* Compute RSRP for the references in this port */
if (port_id == 0) {
q->rsrp[port_id] = chest_dl_rsrp(q, port_id);
}
/* compute rssi */
if (port_id == 0) {
q->rssi[port_id] = chest_dl_rssi(q, input, port_id);
}
/* Use the known CSR signal to compute Least-squares estimates */ /* Use the known CSR signal to compute Least-squares estimates */
vec_prod_conj_ccc(q->pilot_recv_signal[port_id], q->csr_signal.pilots[port_id/2][sf_idx], vec_prod_conj_ccc(q->pilot_recv_signal[port_id], q->csr_signal.pilots[port_id/2][sf_idx],
q->pilot_estimates[port_id], REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)); q->pilot_estimates[port_id], REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
/* Average pilot estimates */ /* Average pilot estimates */
average_pilots(q, port_id); average_pilots(q, port_id);
/* Compute RSRP for the references in this port */
q->rsrp[port_id] = chest_dl_rsrp(q, port_id);
/* Interpolate to create channel estimates for all resource grid */ /* Interpolate to create channel estimates for all resource grid */
if (ce != NULL) { if (ce != NULL) {
interpolate_pilots(q, ce, port_id); interpolate_pilots(q, ce, port_id);
@ -334,8 +336,6 @@ int chest_dl_estimate(chest_dl_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t
for (port_id=0;port_id<q->cell.nof_ports;port_id++) { for (port_id=0;port_id<q->cell.nof_ports;port_id++) {
chest_dl_estimate_port(q, input, ce[port_id], sf_idx, port_id); chest_dl_estimate_port(q, input, ce[port_id], sf_idx, port_id);
} }
/* compute rssi */
q->rssi = chest_dl_rssi(q->cell, input);
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} }
@ -346,21 +346,28 @@ float chest_dl_get_noise_estimate(chest_dl_t *q) {
float chest_dl_get_snr(chest_dl_t *q) { float chest_dl_get_snr(chest_dl_t *q) {
float noise = chest_dl_get_noise_estimate(q); float noise = chest_dl_get_noise_estimate(q);
if (noise) { if (noise) {
return chest_dl_get_rssi(q)/(noise*2*q->cell.nof_ports*lte_symbol_sz(q->cell.nof_prb)); return chest_dl_get_rssi(q)/(noise);//*2*q->cell.nof_ports*lte_symbol_sz(q->cell.nof_prb));
} else { } else {
return 0.0; return 0.0;
} }
} }
float chest_dl_get_rssi(chest_dl_t *q) { float chest_dl_get_rssi(chest_dl_t *q) {
return q->rssi; return 4*q->rssi[0]/q->cell.nof_prb/RE_X_RB;
} }
/* q->rssi[0] is the average power in all RE in all symbol containing references for port 0 . q->rssi[0]/q->cell.nof_prb is the average power per PRB
* q->rsrp[0] is the average power of RE containing references only (for port 0).
*/
float chest_dl_get_rsrq(chest_dl_t *q) { float chest_dl_get_rsrq(chest_dl_t *q) {
return (4*q->cell.nof_ports*q->cell.nof_prb) * chest_dl_get_rsrp(q) / q->rssi; return q->cell.nof_prb*q->rsrp[0] / q->rssi[0];
} }
float chest_dl_get_rsrp(chest_dl_t *q) { float chest_dl_get_rsrp(chest_dl_t *q) {
return vec_acc_ff(q->rsrp, q->cell.nof_ports)/q->cell.nof_ports; // return linear average from port 0 only
return q->rsrp[0];
// return linear average from all ports
//return vec_acc_ff(q->rsrp, q->cell.nof_ports)/q->cell.nof_ports;
} }

View File

@ -34,6 +34,9 @@
#include "liblte/phy/sync/sync.h" #include "liblte/phy/sync/sync.h"
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#define MEANENERGY_EMA_ALPHA 0.5
#define MEANPEAK_EMA_ALPHA 0.2
static bool fft_size_isvalid(uint32_t fft_size) { static bool fft_size_isvalid(uint32_t fft_size) {
if (fft_size >= FFT_SIZE_MIN && fft_size <= FFT_SIZE_MAX && (fft_size%64) == 0) { if (fft_size >= FFT_SIZE_MIN && fft_size <= FFT_SIZE_MAX && (fft_size%64) == 0) {
@ -55,7 +58,8 @@ int sync_init(sync_t *q, uint32_t frame_size, uint32_t fft_size) {
bzero(q, sizeof(sync_t)); bzero(q, sizeof(sync_t));
q->detect_cp = true; q->detect_cp = true;
q->normalize_en = true; q->normalize_en = true;
q->mean_energy = 1.0; q->mean_energy = 0.0;
q->mean_peak_value = 0.0;
q->sss_en = true; q->sss_en = true;
q->N_id_2 = 1000; q->N_id_2 = 1000;
q->N_id_1 = 1000; q->N_id_1 = 1000;
@ -261,22 +265,26 @@ int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_posit
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
if (q->normalize_en && if (q->normalize_en &&
peak_pos < q->frame_size &&
peak_pos + find_offset >= q->fft_size ) peak_pos + find_offset >= q->fft_size )
{ {
/* Compute the energy of the received PSS sequence to normalize */ /* Compute the energy of the received PSS sequence to normalize */
energy = sqrtf(vec_avg_power_cf(&input[find_offset+peak_pos-q->fft_size], q->fft_size)); energy = sqrtf(vec_avg_power_cf(&input[find_offset+peak_pos-q->fft_size], q->fft_size));
q->mean_energy = VEC_CMA(energy, q->mean_energy, q->frame_cnt); q->mean_energy = VEC_EMA(energy, q->mean_energy, MEANENERGY_EMA_ALPHA);
} else { } else {
if (q->mean_energy == 0.0) { if (q->mean_energy == 0.0) {
q->mean_energy = 1.0; energy = 1.0;
} else {
energy = q->mean_energy;
} }
energy = q->mean_energy;
} }
/* Normalize and compute mean peak value */ /* Normalize and compute mean peak value */
q->peak_value = peak_unnormalized/energy; if (q->mean_energy) {
q->mean_peak_value = VEC_CMA(q->peak_value, q->mean_peak_value, q->frame_cnt); q->peak_value = peak_unnormalized/q->mean_energy;
} else {
q->peak_value = peak_unnormalized/energy;
}
q->mean_peak_value = VEC_EMA(q->peak_value, q->mean_peak_value, MEANPEAK_EMA_ALPHA);
q->frame_cnt++; q->frame_cnt++;
if (peak_position) { if (peak_position) {
@ -284,7 +292,7 @@ int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_posit
} }
/* If peak is over threshold, compute CFO and SSS */ /* If peak is over threshold, compute CFO and SSS */
if (q->peak_value >= q->threshold) { if (q->peak_value >= q->threshold) {
// Set an invalid N_id_1 indicating SSS is yet to be detected // Set an invalid N_id_1 indicating SSS is yet to be detected
q->N_id_1 = 1000; q->N_id_1 = 1000;
@ -296,9 +304,7 @@ int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_posit
} }
} }
// Make sure we have enough space to estimate CFO // Make sure we have enough space to estimate CFO
if (peak_pos < q->frame_size && if (peak_pos + find_offset >= q->fft_size) {
peak_pos + find_offset >= q->fft_size)
{
q->cfo = pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]); q->cfo = pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]);
} else { } else {
INFO("No space for CFO computation. Frame starts at \n",peak_pos); INFO("No space for CFO computation. Frame starts at \n",peak_pos);
@ -309,8 +315,9 @@ int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_posit
ret = 0; ret = 0;
} }
INFO("SYNC ret=%d N_id_2=%d pos=%d peak=%.2f/%.2f=%.2f threshold=%.2f sf_idx=%d offset=%d\n", INFO("SYNC ret=%d N_id_2=%d pos=%d peak=%.2f/%.2f=%.2f mean_energy=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f KHz\n",
ret, q->N_id_2, peak_pos, peak_unnormalized,energy,q->peak_value, q->threshold, q->sf_idx, find_offset); ret, q->N_id_2, peak_pos, peak_unnormalized*1000,energy*1000,q->peak_value, q->mean_energy*1000,
q->threshold, q->sf_idx, 15*q->cfo);
} else if (lte_N_id_2_isvalid(q->N_id_2)) { } else if (lte_N_id_2_isvalid(q->N_id_2)) {
fprintf(stderr, "Must call sync_set_N_id_2() first!\n"); fprintf(stderr, "Must call sync_set_N_id_2() first!\n");

View File

@ -46,10 +46,12 @@ cf_t dummy[MAX_TIME_OFFSET];
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) #define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp) #define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
#define FIND_THRESHOLD 1.4 #define FIND_THRESHOLD 1.5
#define TRACK_THRESHOLD 0.7 #define TRACK_THRESHOLD 0.8
#define TRACK_MAX_LOST 10 #define TRACK_MAX_LOST 10
#define CFO_EMA_ALPHA 0.01
int ue_sync_init(ue_sync_t *q, int ue_sync_init(ue_sync_t *q,
lte_cell_t cell, lte_cell_t cell,
@ -145,7 +147,7 @@ float ue_sync_get_cfo(ue_sync_t *q) {
} }
float ue_sync_get_sfo(ue_sync_t *q) { float ue_sync_get_sfo(ue_sync_t *q) {
return 1000*q->mean_time_offset; return 5000*q->mean_time_offset;
} }
void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) { void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) {
@ -209,9 +211,9 @@ int track_peak_ok(ue_sync_t *q, uint32_t track_idx) {
} }
/* compute cumulative moving average CFO */ /* compute cumulative moving average CFO */
q->cur_cfo = VEC_CMA(sync_get_cfo(&q->strack), q->cur_cfo, q->frame_ok_cnt); q->cur_cfo = VEC_EMA(sync_get_cfo(&q->strack), q->cur_cfo, CFO_EMA_ALPHA);
/* compute cumulative moving average time offset */ /* compute cumulative moving average time offset */
q->mean_time_offset = (float) VEC_CMA((float) q->time_offset, q->mean_time_offset, q->frame_ok_cnt); q->mean_time_offset = (float) VEC_CMA((float) q->time_offset, q->mean_time_offset, q->frame_total_cnt);
q->peak_idx = CURRENT_SFLEN/2 + q->time_offset; q->peak_idx = CURRENT_SFLEN/2 + q->time_offset;
q->frame_ok_cnt++; q->frame_ok_cnt++;
@ -356,8 +358,8 @@ void ue_sync_reset(ue_sync_t *q) {
q->frame_ok_cnt = 0; q->frame_ok_cnt = 0;
q->frame_no_cnt = 0; q->frame_no_cnt = 0;
q->frame_total_cnt = 0; q->frame_total_cnt = 0;
q->cur_cfo = 0; q->cur_cfo = 0.0;
q->mean_time_offset = 0; q->mean_time_offset = 0.0;
q->time_offset = 0; q->time_offset = 0;
#ifdef MEASURE_EXEC_TIME #ifdef MEASURE_EXEC_TIME
q->mean_exec_time = 0; q->mean_exec_time = 0;

View File

@ -475,12 +475,7 @@ float vec_dot_prod_fff(float *x, float *y, uint32_t len) {
float vec_avg_power_cf(cf_t *x, uint32_t len) { float vec_avg_power_cf(cf_t *x, uint32_t len) {
int j; return crealf(vec_dot_prod_conj_ccc(x,x,len)) / len;
float power = 0;
for (j=0;j<len;j++) {
power += crealf(x[j]*conjf(x[j]));
}
return power / len;
} }
void vec_abs_cf(cf_t *x, float *abs, uint32_t len) { void vec_abs_cf(cf_t *x, float *abs, uint32_t len) {