New equalizer working

This commit is contained in:
ismagom 2014-11-11 18:20:09 +00:00
parent 61ebfaf3b2
commit f505a75382
38 changed files with 1023 additions and 1945 deletions

View File

@ -78,7 +78,7 @@ IF(CMAKE_COMPILER_IS_GNUCXX)
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE -g")
# IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wno-error=implicit-function-declaration -Wno-error=unused-but-set-variable")
# ENDIF(${CMAKE_BUILD_TYPE} STREQUAL "Debug")

View File

@ -37,13 +37,15 @@ CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_multiply_32f HAVE_VOLK_MULT_FLOAT_FUNCT
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_magnitude_32f HAVE_VOLK_MAG_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_magnitude_square_32f HAVE_VOLK_MAG_SQUARE_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_divide_32f HAVE_VOLK_DIVIDE_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_32f_dot_prod_32fc HAVE_VOLK_DOTPROD_FC_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_dot_prod_32fc HAVE_VOLK_DOTPROD_FC_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_32f_dot_prod_32fc HAVE_VOLK_DOTPROD_CFC_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_conjugate_dot_prod_32fc HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_dot_prod_32f HAVE_VOLK_DOTPROD_F_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32f_atan2_32f HAVE_VOLK_ATAN_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_convert_16i HAVE_VOLK_CONVERT_FI_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_32f_x2 HAVE_VOLK_DEINTERLEAVE_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_subtract_32f HAVE_VOLK_SUB_FLOAT_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_add_32f HAVE_VOLK_ADD_FLOAT_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_square_dist_32f HAVE_VOLK_SQUARE_DIST_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_real_32f HAVE_VOLK_DEINTERLEAVE_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_index_max_16u HAVE_VOLK_MAX_ABS_FUNCTION)
@ -66,6 +68,9 @@ ENDIF()
IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_ADD_FLOAT_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_ADD_FLOAT_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_MULT2_CONJ_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_CONJ_FUNCTION")
ENDIF()

View File

@ -128,7 +128,7 @@ int main(int argc, char **argv) {
void *uhd;
ue_dl_t ue_dl;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
uint32_t nframes=0;
uint32_t nof_trials = 0;
uint32_t sfn = 0; // system frame number
@ -136,6 +136,11 @@ int main(int argc, char **argv) {
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
uint32_t sfn_offset;
float rssi=0, rsrp=0, rsrq=0;
cf_t *nullce[MAX_PORTS];
for (int i=0;i<MAX_PORTS;i++) {
nullce[i] = NULL;
}
parse_args(&prog_args, argc, argv);
@ -180,7 +185,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error initiating FFT\n");
return -1;
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initiating channel estimator\n");
return -1;
}
@ -251,10 +256,11 @@ int main(int argc, char **argv) {
/* Run FFT for all subframe data */
lte_fft_run_sf(&fft, sf_buffer, sf_symbols);
chest_measure_sf(&chest, sf_symbols, ue_sync_get_sfidx(&ue_sync));
rssi = VEC_CMA(chest_rssi_sf(&chest, sf_symbols),rssi,nframes);
rsrq = VEC_CMA(chest_rsrq_sf(&chest, sf_symbols, ue_sync_get_sfidx(&ue_sync)),rsrq,nframes);
rsrp = VEC_CMA(chest_rsrp_sf(&chest, ue_sync_get_sfidx(&ue_sync)),rsrp,nframes);
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);
rsrp = VEC_CMA(chest_dl_get_rsrp(&chest),rsrp,nframes);
nframes++;
// Plot and Printf

View File

@ -242,8 +242,8 @@ int main(int argc, char **argv) {
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_packed[BCH_PAYLOAD_LEN/8];
ra_pdsch_t ra_dl;
ra_prb_t prb_alloc;
refsignal_t refs[NSLOTS_X_FRAME];
int i, n;
refsignal_cs_t csr_signal;
int i;
uint8_t *data;
cf_t *sf_symbols[MAX_PORTS];
dci_msg_t dci_msg;
@ -271,12 +271,7 @@ int main(int argc, char **argv) {
sss_generate(sss_signal0, sss_signal5, cell.id);
/* Generate CRS signals */
for (i = 0; i < NSLOTS_X_FRAME; i++) {
if (refsignal_init_LTEDL(&refs[i], 0, i, cell)) {
fprintf(stderr, "Error initiating CRS slot=%d\n", i);
return -1;
}
}
refsignal_cs_generate(&csr_signal, cell);
cell.phich_length = PHICH_NORM;
cell.phich_resources = R_1;
@ -346,9 +341,7 @@ int main(int argc, char **argv) {
pbch_encode(&pbch, bch_payload, sf_symbols);
}
for (n=0;n<2;n++) {
refsignal_put(&refs[2*sf_idx+n], &sf_buffer[n*sf_n_re/2]);
}
refsignal_cs_put_sf(cell, 0, csr_signal.pilots[0][sf_idx], sf_buffer);
pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx);

View File

@ -0,0 +1,57 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef CHEQ_DL_
#define CHEQ_DL_
#include <stdio.h>
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
typedef _Complex float cf_t;
/** Generic OFDM channel equalizer
*/
LIBLTE_API int cheq_zf(cf_t *input,
cf_t *ce,
cf_t *output,
uint32_t len);
LIBLTE_API int cheq_mmse(cf_t *input,
cf_t *ce,
cf_t *output,
uint32_t len,
float noise_estimate);
#endif

View File

@ -1,238 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef CHEST_
#define CHEST_
#include <stdio.h>
#include "liblte/config.h"
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/ch_estimation/refsignal.h"
#include "liblte/phy/common/phy_common.h"
typedef _Complex float cf_t; /* this is only a shortcut */
typedef void (*interpolate_fnc_t) (cf_t *input,
cf_t *output,
uint32_t M,
uint32_t len,
uint32_t off_st,
uint32_t off_end);
/** This is an OFDM channel estimator.
* It works with any reference signal pattern, provided by the object
* refsignal_t
* A 2-D filter is used for freq and time channel interpolation.
*
*/
/* Low-level API */
typedef struct LIBLTE_API {
uint32_t nof_ports;
uint32_t nof_re;
uint32_t nof_symbols;
refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME];
interp_t interp_time[MAX_PORTS];
interp_t interp_freq[MAX_PORTS];
}chest_t;
LIBLTE_API int chest_init(chest_t *q,
uint32_t nof_re,
uint32_t nof_symbols,
uint32_t nof_ports);
LIBLTE_API void chest_free(chest_t *q);
LIBLTE_API int chest_set_nof_ports(chest_t *q,
uint32_t nof_ports);
LIBLTE_API float chest_rsrp(chest_t *q,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API float chest_rsrp_sf(chest_t *q,
uint32_t sf_idx);
LIBLTE_API float chest_rssi(chest_t *q,
cf_t *input);
LIBLTE_API float chest_rssi_sf(chest_t *q,
cf_t *input);
LIBLTE_API float chest_rsrq(chest_t *q,
cf_t *input,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API float chest_rsrq_sf(chest_t *q,
cf_t *input,
uint32_t sf_idx);
LIBLTE_API int chest_measure_ref(chest_t *q,
cf_t *input,
uint32_t nslot,
uint32_t port_id,
uint32_t nref);
LIBLTE_API void chest_measure_slot(chest_t *q,
cf_t *input,
uint32_t nslot);
LIBLTE_API void chest_measure_sf(chest_t *q,
cf_t *input,
uint32_t sf_idx);
LIBLTE_API int chest_ce_slot_port(chest_t *q,
cf_t *input,
cf_t *ce,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API int chest_ce_sf_port(chest_t *q,
cf_t *input,
cf_t *ce,
uint32_t sf_idx,
uint32_t port_id);
LIBLTE_API int chest_ce_slot(chest_t *q,
cf_t *input,
cf_t *ce[MAX_PORTS],
uint32_t nslot);
LIBLTE_API int chest_ce_sf(chest_t *q,
cf_t *input,
cf_t *ce[MAX_PORTS],
uint32_t sf_idx);
LIBLTE_API void chest_fprint(chest_t *q,
FILE *stream,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API void chest_ref_fprint(chest_t *q,
FILE *stream,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API void chest_recvsig_fprint(chest_t *q,
FILE *stream,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API void chest_ce_fprint(chest_t *q,
FILE *stream,
uint32_t nslot,
uint32_t port_id);
LIBLTE_API int chest_ref_get_symbols(chest_t *q,
uint32_t port_id,
uint32_t nslot,
uint32_t l[2]);
/*********************************************************
*
* Downlink Channel Estimator
*
*********************************************************/
LIBLTE_API int chest_init_LTEDL(chest_t *q,
lte_cell_t cell);
LIBLTE_API int chest_ref_set_LTEDL_slot_port(chest_t *q,
uint32_t nslot,
uint32_t port_id,
lte_cell_t cell);
LIBLTE_API int chest_ref_set_LTEDL_slot(chest_t *q,
uint32_t nslot,
lte_cell_t cell);
LIBLTE_API int chest_ref_set_LTEDL(chest_t *q,
lte_cell_t cell);
/*********************************************************
*
* Uplink Channel Estimator
*
*********************************************************/
LIBLTE_API int chest_init_LTEUL(chest_t *q,
lte_cell_t cell);
LIBLTE_API int chest_ref_set_LTEUL_slot(chest_t *q,
uint32_t nslot,
lte_cell_t cell);
LIBLTE_API int chest_ref_set_LTEUL(chest_t *q,
lte_cell_t cell);
/* High-level API */
/** TODO: The high-level API has N interfaces, one for each port */
typedef struct LIBLTE_API{
chest_t obj;
struct chest_init {
int nof_symbols; // 7 for normal cp, 6 for extended
int nof_ports;
int nof_prb;
int cell_id; // set to -1 to init at runtime
} init;
cf_t *input;
int in_len;
struct chest_ctrl_in {
int sf_idx; // subframe id in the 10ms frame
} ctrl_in;
cf_t *output[MAX_PORTS];
int out_len[MAX_PORTS];
}chest_hl;
#define DEFAULT_FRAME_SIZE 2048
LIBLTE_API int chest_initialize(chest_hl* h);
LIBLTE_API int chest_work(chest_hl* hl);
LIBLTE_API int chest_stop(chest_hl* hl);
#endif

View File

@ -39,11 +39,6 @@
#include "liblte/phy/ch_estimation/refsignal_dl.h"
#include "liblte/phy/common/phy_common.h"
#define CHEST_RS_AVERAGE_TIME 0
#define CHEST_RS_AVERAGE_FREQ 3
/** 3GPP LTE Downlink channel estimator and equalizer.
* Estimates the channel in the resource elements transmitting references and interpolates for the rest
* of the resource grid.
@ -53,19 +48,30 @@
* This object depends on the refsignal_t object for creating the LTE CSR signal.
*/
#define CHEST_MAX_FILTER_FREQ_LEN 10
#define CHEST_MAX_FILTER_TIME_LEN 4
typedef struct {
lte_cell_t cell;
refsignal_cs_t csr_signal;
cf_t *pilot_estimates[MAX_PORTS];
cf_t *pilot_estimates_average[MAX_PORTS];
cf_t *pilot_recv_signal[MAX_PORTS];
cf_t *pilot_symbol_avg;
uint32_t filter_freq_len;
float filter_freq[CHEST_MAX_FILTER_FREQ_LEN];
uint32_t filter_time_len;
float filter_time[CHEST_MAX_FILTER_TIME_LEN];
cf_t *tmp_freqavg;
cf_t *tmp_timeavg[CHEST_MAX_FILTER_TIME_LEN];
interp_linvec_t interp_linvec;
interp_lin_t interp_lin;
interp_t interp_time[MAX_PORTS];
interp_t interp_freq[MAX_PORTS];
float rssi;
float rsrq;
float rsrp;
float rsrp[MAX_PORTS];
float noise_estimate[MAX_PORTS];
} chest_dl_t;
@ -74,6 +80,14 @@ LIBLTE_API int chest_dl_init(chest_dl_t *q,
LIBLTE_API void chest_dl_free(chest_dl_t *q);
LIBLTE_API int chest_dl_set_filter_freq(chest_dl_t *q,
float *filter,
uint32_t filter_len);
LIBLTE_API int chest_dl_set_filter_time(chest_dl_t *q,
float *filter,
uint32_t filter_len);
LIBLTE_API int chest_dl_estimate(chest_dl_t *q,
cf_t *input,
cf_t *ce[MAX_PORTS],
@ -85,16 +99,7 @@ LIBLTE_API int chest_dl_estimate_port(chest_dl_t *q,
uint32_t sf_idx,
uint32_t port_id);
LIBLTE_API int chest_dl_equalize_zf(chest_dl_t *q,
cf_t *input,
cf_t *ce[MAX_PORTS],
cf_t *output);
LIBLTE_API int chest_dl_equalize_mmse(chest_dl_t *q,
cf_t *input,
cf_t *ce[MAX_PORTS],
float *noise_estimate,
cf_t *output);
LIBLTE_API float chest_dl_get_noise_estimate(chest_dl_t *q);
LIBLTE_API float chest_dl_get_rssi(chest_dl_t *q);

View File

@ -1,92 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef REFSIGNAL_
#define REFSIGNAL_
/* Object to manage reference signals for OFDM channel equalization.
*
* It generates the reference signals for LTE.
*
*/
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
typedef _Complex float cf_t;
typedef struct LIBLTE_API{
uint32_t time_idx;
uint32_t freq_idx;
cf_t symbol;
}ref_t;
typedef struct LIBLTE_API{
uint32_t nof_refs; // number of reference signals
uint32_t *symbols_ref; // symbols with at least one reference
uint32_t nsymbols; // number of symbols with at least one reference
uint32_t voffset; // offset of the first reference in the freq domain
uint32_t nof_prb;
ref_t *refs;
cf_t *ch_est;
cf_t *recv_symbol;
} refsignal_t;
typedef struct LIBLTE_API {
float beta; // amplitude scaling
uint32_t delta_ss; // Set to 0 for PUCCH
uint32_t cyclic_shift;
uint32_t cyclic_shift_for_drms; /* From DCI 0. Set to 0 if no PDCCH with DCI 0 for the same TB
or if the initial PUSCH is semi-persisently scheduled or
if the initial PUSCH is scheduled by the RA response grant */
bool group_hopping_en;
bool sequence_hopping_en;
} refsignal_ul_cfg_t;
LIBLTE_API int refsignal_init_LTEDL(refsignal_t *q,
uint32_t port_id,
uint32_t nslot,
lte_cell_t cell);
LIBLTE_API int refsignal_init_LTEUL_drms_pusch(refsignal_t *q,
uint32_t nof_prb,
uint32_t prb_start,
uint32_t nslot,
lte_cell_t cell,
refsignal_ul_cfg_t *drms_cfg);
LIBLTE_API void refsignal_free(refsignal_t *q);
LIBLTE_API int refsignal_put(refsignal_t *q,
cf_t *slot_symbols);
#endif

View File

@ -41,13 +41,13 @@ typedef _Complex float cf_t;
#define REFSIGNAL_NUM_SF(nof_prb, port_id) (((port_id)<2?8:4)*(nof_prb))
#define REFSIGNAL_MAX_NUM_SF(nof_prb) REFSIGNAL_NUM_SF(nof_prb, 0)
#define REFSIGNAL_PILOT_IDX(i,l,ns,cell) (2*cell.nof_prb*((l)+2*((ns)%2))+(i))
#define REFSIGNAL_PILOT_IDX(i,l,cell) (2*cell.nof_prb*(l)+(i))
/** Cell-Specific Reference Signal */
typedef struct LIBLTE_API {
lte_cell_t cell;
cf_t *pilots[NSUBFRAMES_X_FRAME]; // save 2 reference symbols per slot
cf_t *pilots[2][NSUBFRAMES_X_FRAME]; // Saves the reference signal per subframe for ports 0,1 and ports 2,3
} refsignal_cs_t;
@ -58,29 +58,25 @@ LIBLTE_API void refsignal_cs_free(refsignal_cs_t *q);
LIBLTE_API int refsignal_cs_put_sf(lte_cell_t cell,
uint32_t port_id,
uint32_t sf_idx,
cf_t *pilots,
cf_t *sf_symbols);
LIBLTE_API int refsignal_cs_get_sf(lte_cell_t cell,
uint32_t port_id,
uint32_t sf_idx,
cf_t *sf_symbols,
cf_t *pilots);
LIBLTE_API uint32_t refsignal_fidx(lte_cell_t cell,
uint32_t ns,
uint32_t l,
uint32_t port_id,
uint32_t m);
LIBLTE_API uint32_t refsignal_nsymbol(lte_cell_t cell,
uint32_t ns,
uint32_t l);
LIBLTE_API uint32_t refsignal_nsymbol(uint32_t l,
lte_cp_t cp,
uint32_t port_id);
LIBLTE_API uint32_t refsignal_cs_v(uint32_t port_id,
uint32_t ns,
uint32_t symbol_id);
uint32_t ref_symbol_idx);
LIBLTE_API uint32_t refsignal_cs_nof_symbols(uint32_t port_id);

View File

@ -51,9 +51,7 @@
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/common/fft.h"
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/ch_estimation/refsignal.h"
#include "liblte/phy/ch_estimation/refsignal_dl.h"
#include "liblte/phy/resampling/interp.h"

View File

@ -34,69 +34,70 @@
typedef _Complex float cf_t;
typedef enum LIBLTE_API {LINEAR} interp_type_t;
typedef struct LIBLTE_API {
interp_type_t type;
float *in_mag;
float *in_arg;
float *in_mag0;
float *in_arg0;
float *in_mag1;
float *in_arg1;
float *out_mag;
float *out_arg;
float *out_arg2;
int16_t *table_idx;
cf_t *out_cexp;
cf_t *out_prod;
cf_t *cexptable;
uint32_t len;
uint32_t M;
}interp_t;
/************* STATIC LINEAR INTERPOLATION FUNCTIONS */
LIBLTE_API int interp_init(interp_t *q,
interp_type_t type,
uint32_t len,
uint32_t M);
LIBLTE_API cf_t interp_linear_onesample(cf_t input0,
cf_t input1);
LIBLTE_API void interp_free(interp_t *q);
LIBLTE_API cf_t interp_linear_onesample_cabs(cf_t input0,
cf_t input1);
LIBLTE_API void interp_run(interp_t *q,
cf_t *input,
cf_t *output);
LIBLTE_API void interp_run_offset(interp_t *q,
cf_t *input,
cf_t *output,
uint32_t off_st,
uint32_t off_end);
LIBLTE_API cf_t interp_linear_onesample(cf_t *input);
LIBLTE_API cf_t interp_linear_onesample2(cf_t *input);
LIBLTE_API void interp_linear_offset(cf_t *input,
cf_t *output,
uint32_t M,
uint32_t len,
uint32_t off_st,
uint32_t off_end);
LIBLTE_API void interp_linear_c(cf_t *input,
cf_t *output,
uint32_t M,
uint32_t len);
LIBLTE_API void interp_linear_offset_cabs(cf_t *input,
cf_t *output,
uint32_t M,
uint32_t len,
uint32_t off_st,
uint32_t off_end);
LIBLTE_API void interp_linear_f(float *input,
float *output,
uint32_t M,
uint32_t len);
/* Interpolation between vectors */
typedef struct {
cf_t *diff_vec;
uint32_t vector_len;
} interp_linvec_t;
LIBLTE_API int interp_linear_vector_init(interp_linvec_t *q,
uint32_t vector_len);
LIBLTE_API void interp_linear_vector_free(interp_linvec_t *q);
LIBLTE_API void interp_linear_vector(interp_linvec_t *q,
cf_t *in0,
cf_t *in1,
cf_t *between,
uint32_t M);
/* Interpolation within a vector */
typedef struct {
cf_t *diff_vec;
cf_t *diff_vec2;
float *ramp;
uint32_t vector_len;
uint32_t M;
} interp_lin_t;
LIBLTE_API int interp_linear_init(interp_lin_t *q,
uint32_t vector_len,
uint32_t M);
LIBLTE_API void interp_linear_free(interp_lin_t *q);
LIBLTE_API void interp_linear_offset(interp_lin_t *q,
cf_t *input,
cf_t *output,
uint32_t off_st,
uint32_t off_end);
#endif // INTERP_H

View File

@ -33,7 +33,7 @@
#include "liblte/config.h"
#include "liblte/phy/sync/sync.h"
#include "liblte/phy/sync/cfo.h"
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/phch/pbch.h"
#include "liblte/phy/common/fft.h"

View File

@ -36,7 +36,7 @@
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/common/fft.h"
#include "liblte/phy/common/phy_common.h"
@ -64,7 +64,7 @@ typedef struct LIBLTE_API {
pdsch_harq_t harq_process[NOF_HARQ_PROCESSES];
regs_t regs;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
lte_cell_t cell;

View File

@ -51,7 +51,7 @@
#include "liblte/config.h"
#include "liblte/phy/sync/sync.h"
#include "liblte/phy/sync/cfo.h"
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/phch/pbch.h"
#include "liblte/phy/common/fft.h"
@ -66,11 +66,11 @@
typedef struct LIBLTE_API {
sync_t sfind;
cf_t *slot1_symbols;
cf_t *sf_symbols;
cf_t *ce[MIB_MAX_PORTS];
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
pbch_t pbch;
uint8_t bch_payload[BCH_PAYLOAD_LEN];

View File

@ -33,7 +33,7 @@
#include "liblte/config.h"
#include "liblte/phy/sync/sync.h"
#include "liblte/phy/sync/cfo.h"
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/phch/pbch.h"
#include "liblte/phy/common/fft.h"

View File

@ -64,4 +64,16 @@ LIBLTE_API uint32_t conv_cc(cf_t *input,
uint32_t input_len,
uint32_t filter_len);
LIBLTE_API uint32_t conv_same_cf(cf_t *input,
float *filter,
cf_t *output,
uint32_t input_len,
uint32_t filter_len);
LIBLTE_API uint32_t conv_same_cc(cf_t *input,
cf_t *filter,
cf_t *output,
uint32_t input_len,
uint32_t filter_len);
#endif // CONVOLUTION_H_

View File

@ -63,10 +63,12 @@ LIBLTE_API void vec_save_file(char *filename, void *buffer, uint32_t len);
/* sum two vectors */
LIBLTE_API void vec_sum_ch(uint8_t *x, uint8_t *y, char *z, uint32_t len);
LIBLTE_API void vec_sum_fff(float *x, float *y, float *z, uint32_t len);
LIBLTE_API void vec_sum_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
/* substract two vectors z=x-y */
LIBLTE_API void vec_sub_fff(float *x, float *y, float *z, uint32_t len);
LIBLTE_API void vec_sub_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
/* Square distance */
LIBLTE_API void vec_square_dist(cf_t symbol, cf_t *points, float *distance, uint32_t npoints);
@ -91,6 +93,7 @@ LIBLTE_API void vec_prod_cfc(cf_t *x, float *y, cf_t *z, uint32_t len);
LIBLTE_API void vec_prod_conj_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
/* Dot-product */
LIBLTE_API cf_t vec_dot_prod_cfc(cf_t *x, float *y, uint32_t len);
LIBLTE_API cf_t vec_dot_prod_ccc(cf_t *x, cf_t *y, uint32_t len);
LIBLTE_API cf_t vec_dot_prod_conj_ccc(cf_t *x, cf_t *y, uint32_t len);
LIBLTE_API float vec_dot_prod_fff(float *x, float *y, uint32_t len);
@ -98,9 +101,6 @@ LIBLTE_API float vec_dot_prod_fff(float *x, float *y, uint32_t len);
/* z=x/y vector division (element-wise) */
LIBLTE_API void vec_div_ccc(cf_t *x, cf_t *y, float *y_mod, cf_t *z, uint32_t len);
/* z=x/y vector division with mod(y)=1 (element-wise) */
LIBLTE_API void vec_div_ccc_mod1(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
/* conjugate */
LIBLTE_API void vec_conj_cc(cf_t *x, cf_t *y, uint32_t len);

View File

@ -0,0 +1,53 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <complex.h>
#include <math.h>
#include "liblte/config.h"
#include "liblte/phy/ch_estimation/cheq.h"
#include "liblte/phy/utils/vector.h"
int cheq_zf(cf_t *input, cf_t *ce, cf_t *output, uint32_t len)
{
fprintf(stderr, "Not implemented\n");
return -1;
}
int cheq_mmse(cf_t *input, cf_t *ce, cf_t *output, uint32_t len, float noise_estimate)
{
fprintf(stderr, "Not implemented\n");
return -1;
}

View File

@ -1,503 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <strings.h>
#include <string.h>
#include <complex.h>
#include <math.h>
#include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
#define SF_SZ(q) (2 * SLOT_SZ(q))
#define VOLK_INTERP
void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
chest_ref_fprint(q, stream, nslot, port_id);
chest_recvsig_fprint(q, stream, nslot, port_id);
chest_ce_fprint(q, stream, nslot, port_id);
}
/* Sets the number of ports to estimate. nof_ports must be smaler than nof_ports
* used during the call to chest_init().
*/
int chest_set_nof_ports(chest_t *q, uint32_t nof_ports) {
if (nof_ports < q->nof_ports) {
q->nof_ports = nof_ports;
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
void chest_ref_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
int i;
fprintf(stream, "refs%d=[",port_id);
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].refs[i].symbol,
__imag__ q->refsignal[port_id][nslot].refs[i].symbol);
}
fprintf(stream, "];\n");
}
void chest_recvsig_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
int i;
fprintf(stream, "recvsig%d=[",port_id);
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
fprintf(stream, "%3.3f%+3.3fi, ", __real__ q->refsignal[port_id][nslot].recv_symbol[i],
__imag__ q->refsignal[port_id][nslot].recv_symbol[i]);
}
fprintf(stream, "];\n");
}
void chest_ce_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
int i;
fprintf(stream, "mag%d=[",port_id);
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
fprintf(stream, "%3.3f, ", cabsf(q->refsignal[port_id][nslot].ch_est[i]));
}
fprintf(stream, "];\nphase%d=[",port_id);
for (i=0;i<q->refsignal[port_id][nslot].nof_refs;i++) {
fprintf(stream, "%3.3f, ", atan2f(__imag__ q->refsignal[port_id][nslot].ch_est[i],
__real__ q->refsignal[port_id][nslot].ch_est[i]));
}
fprintf(stream, "];\n");
}
float chest_rsrp(chest_t *q, uint32_t nslot, uint32_t port_id) {
int nof_refs = q->refsignal[port_id][nslot].nof_refs;
cf_t *ch_est = q->refsignal[port_id][nslot].ch_est;
return crealf(vec_dot_prod_conj_ccc(ch_est, ch_est, nof_refs))/nof_refs;
}
float chest_rsrp_sf(chest_t *q, uint32_t sf_idx) {
int n,p;
float rsrp=0;
for (p=0;p<q->nof_ports;p++) {
for (n=0;n<2;n++) {
rsrp+=chest_rsrp(q, 2*sf_idx+n, p)/(2*q->nof_ports);
}
}
return rsrp;
}
float chest_rssi(chest_t *q, cf_t *input) {
float rssi = 0;
int i;
int l[2];
if (q->nof_symbols == CPNORM_NSYMB) {
l[0] = 0; l[1] = 4;
} else {
l[0] = 0; l[1] = 3;
}
for (i=0;i<2;i++) {
cf_t *tmp = &input[l[i]*q->nof_re];
rssi += crealf(vec_dot_prod_conj_ccc(tmp, tmp, q->nof_re));
}
return rssi;
}
float chest_rssi_sf(chest_t *q, cf_t *input) {
int n;
int slotsz = q->nof_symbols*q->nof_re;
float rssi=0;
for (n=0;n<2;n++) {
rssi += chest_rssi(q, &input[n*slotsz]);
}
return rssi;
}
float chest_rsrq(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id) {
return (q->nof_re/RE_X_RB) * chest_rsrp(q, nslot, port_id) / chest_rssi(q, input);
}
float chest_rsrq_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
return (4*q->nof_ports*q->nof_re/RE_X_RB) * chest_rsrp_sf(q, sf_idx) / chest_rssi_sf(q, input);
}
int chest_measure_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint32_t nref) {
int fidx, tidx;
cf_t known_ref, channel_ref;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
input != NULL &&
nslot < NSLOTS_X_FRAME &&
port_id < q->nof_ports)
{
if (nref < q->refsignal[port_id][nslot].nof_refs) {
fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index
tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index
known_ref = q->refsignal[port_id][nslot].refs[nref].symbol;
channel_ref = input[tidx * q->nof_re + fidx];
q->refsignal[port_id][nslot].recv_symbol[nref] = channel_ref;
DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, tidx * q->nof_re + fidx,
10*log10f(cabsf(channel_ref/known_ref)),
cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,
cargf(channel_ref/known_ref)/M_PI);
/* FIXME: compare with threshold */
if (channel_ref != 0) {
q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref;
} else {
q->refsignal[port_id][nslot].ch_est[nref] = 1e-6;
}
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
void chest_measure_slot_port(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id)
{
int i;
refsignal_t *r = &q->refsignal[port_id][nslot];
DEBUG("Estimating channel slot=%d port=%d using %d reference signals\n",
nslot, port_id, r->nof_refs);
for (i=0;i<r->nof_refs;i++) {
chest_measure_ref(q, input, nslot, port_id, i);
}
}
void chest_measure_slot(chest_t *q, cf_t *input, uint32_t nslot) {
int p;
for (p=0;p<q->nof_ports;p++) {
chest_measure_slot_port(q, input, nslot, p);
}
}
void chest_measure_sf(chest_t *q, cf_t *input, uint32_t sf_idx) {
int p, n, slotsz;
slotsz = q->nof_symbols*q->nof_re;
for (p=0;p<q->nof_ports;p++) {
for (n=0;n<2;n++) {
chest_measure_slot_port(q, &input[n*slotsz], 2*sf_idx+n, p);
}
}
}
/* Computes channel estimates for each reference in a slot and port.
* Saves the nof_prb * 12 * nof_symbols channel estimates in the array ce
*/
int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32_t port_id)
{
uint32_t i, j;
cf_t x[2], y[MAX_NSYMB];
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
input != NULL &&
nslot < NSLOTS_X_FRAME &&
port_id < q->nof_ports)
{
if (q->refsignal[port_id][nslot].nsymbols <= 2) {
refsignal_t *r = &q->refsignal[port_id][nslot];
chest_measure_slot_port(q, input, nslot, port_id);
/* interpolate the symbols with references
* in the freq domain */
for (i=0;i<r->nsymbols;i++) {
#ifdef VOLK_INTERP
interp_run_offset(&q->interp_freq[port_id],
&r->ch_est[i * r->nof_refs/2], &ce[r->symbols_ref[i] * q->nof_re],
r->voffset, RE_X_RB/2-r->voffset);
#else
interp_linear_offset(&r->ch_est[i * r->nof_refs/2],
&ce[r->symbols_ref[i] * q->nof_re], RE_X_RB/2,
r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset);
#endif
}
/* now interpolate in the time domain */
for (i=0;i<q->nof_re; i++) {
if (r->nsymbols > 1) {
for (j=0;j<r->nsymbols;j++) {
x[j] = ce[r->symbols_ref[j] * q->nof_re + i];
}
#ifdef VOLK_INTERP
interp_run_offset(&q->interp_time[port_id], x, y,
r->symbols_ref[0], 3);
#else
interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0],
2, r->symbols_ref[0], 3);
#endif
} else {
for (j=0;j<MAX_NSYMB;j++) {
y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
}
}
for (j=0;j<q->nof_symbols;j++) {
ce[j * q->nof_re + i] = y[j];
}
}
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
/* Computes channel estimates for each reference in a subframe and port id.
*/
int chest_ce_sf_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id) {
int n, slotsz, ret;
slotsz = q->nof_symbols*q->nof_re;
for (n=0;n<2;n++) {
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[n*slotsz], 2*sf_idx+n, port_id);
if (ret != LIBLTE_SUCCESS) {
return ret;
}
}
return LIBLTE_SUCCESS;
}
/* Computes channel estimates for each reference in a slot for all ports.
*/
int chest_ce_slot(chest_t *q, cf_t *input, cf_t **ce, uint32_t nslot) {
int p, ret;
for (p=0;p<q->nof_ports;p++) {
ret = chest_ce_slot_port(q, input, ce[p], nslot, p);
if (ret != LIBLTE_SUCCESS) {
return ret;
}
}
return LIBLTE_SUCCESS;
}
/* Computes channel estimates for each reference in a subframe for all ports.
*/
int chest_ce_sf(chest_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx) {
int p, n, slotsz, ret;
slotsz = q->nof_symbols*q->nof_re;
for (p=0;p<q->nof_ports;p++) {
for (n=0;n<2;n++) {
ret = chest_ce_slot_port(q, &input[n*slotsz], &ce[p][n*slotsz], 2*sf_idx+n, p);
if (ret != LIBLTE_SUCCESS) {
return ret;
}
}
}
return LIBLTE_SUCCESS;
}
int chest_init(chest_t *q, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
nof_ports <= MAX_PORTS)
{
bzero(q, sizeof(chest_t));
q->nof_ports = nof_ports;
q->nof_symbols = nof_symbols;
q->nof_re = nof_re;
INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n",
q->nof_symbols, q->nof_re, nof_ports);
ret = LIBLTE_SUCCESS;
}
return ret;
}
void chest_free(chest_t *q) {
int p, n;
for (p=0;p<q->nof_ports;p++) {
for (n=0;n<NSLOTS_X_FRAME;n++) {
refsignal_free(&q->refsignal[p][n]);
}
}
#ifdef VOLK_INTERP
for (p=0;p<MAX_PORTS;p++) {
interp_free(&q->interp_freq[p]);
interp_free(&q->interp_time[p]);
}
#endif
bzero(q, sizeof(chest_t));
}
/* Fills l[2] with the symbols in the slot nslot that contain references.
* returns the number of symbols with references (in the slot)
*/
int chest_ref_get_symbols(chest_t *q, uint32_t port_id, uint32_t nslot, uint32_t l[2]) {
if (q != NULL &&
port_id < MAX_PORTS &&
nslot < NSLOTS_X_FRAME)
{
memcpy(l, q->refsignal[port_id][nslot].symbols_ref, sizeof(uint32_t) * q->refsignal[port_id][nslot].nsymbols);
return q->refsignal[port_id][nslot].nsymbols;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/*********************************************************************
*
* Downlink Channel estimator
*
*********************************************************************/
int chest_init_LTEDL(chest_t *q, lte_cell_t cell) {
int ret;
ret = chest_init(q, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports);
if (ret != LIBLTE_SUCCESS) {
return ret;
} else {
return chest_ref_set_LTEDL(q, cell);
}
}
int chest_ref_set_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
port_id < MAX_PORTS &&
nslot < NSLOTS_X_FRAME)
{
ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell);
#ifdef VOLK_INTERP
if (ret == LIBLTE_SUCCESS) {
if (nslot == 0) {
ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2);
if (ret == LIBLTE_SUCCESS) {
ret = interp_init(&q->interp_time[port_id], LINEAR, 2,
q->refsignal[port_id][nslot].symbols_ref[1] - q->refsignal[port_id][nslot].symbols_ref[0]);
}
}
}
#endif
}
return ret;
}
int chest_ref_set_LTEDL_slot(chest_t *q, uint32_t nslot, lte_cell_t cell) {
int p, ret;
for (p=0;p<q->nof_ports;p++) {
ret = chest_ref_set_LTEDL_slot_port(q, nslot, p, cell);
if (ret != LIBLTE_SUCCESS) {
return ret;
}
}
return LIBLTE_SUCCESS;
}
int chest_ref_set_LTEDL(chest_t *q, lte_cell_t cell) {
int n, ret;
for (n=0;n<NSLOTS_X_FRAME;n++) {
ret = chest_ref_set_LTEDL_slot(q, n, cell);
if (ret != LIBLTE_SUCCESS) {
return ret;
}
}
return LIBLTE_SUCCESS;
}
/*********************************************************************
*
* TODO: Uplink Channel estimator
*
*
*********************************************************************/
/** High-level API
*/
int chest_initialize(chest_hl* h) {
lte_cell_t cell;
if (!h->init.nof_symbols) {
h->init.nof_symbols = CPNORM_NSYMB; // Normal CP
}
if (!h->init.nof_prb) {
h->init.nof_prb = 6;
}
cell.id = h->init.cell_id;
cell.nof_ports = h->init.nof_ports;
cell.nof_prb = h->init.nof_prb;
cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT;
if (chest_init_LTEDL(&h->obj, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
return 0;
}
/** This function must be called in an subframe basis (1ms) for LTE */
int chest_work(chest_hl* hl) {
chest_t *q = &hl->obj;
chest_ce_sf(q, hl->input, hl->output, hl->ctrl_in.sf_idx);
return 0;
}
int chest_stop(chest_hl* hl) {
chest_free(&hl->obj);
return 0;
}

View File

@ -32,13 +32,17 @@
#include <strings.h>
#include <string.h>
#include <complex.h>
#include <math.h>
#include "liblte/config.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/convolution.h"
#define CHEST_RS_AVERAGE_TIME 2
#define CHEST_RS_AVERAGE_FREQ 3
//#define VOLK_INTERP
/** 3GPP LTE Downlink channel estimator and equalizer.
* Estimates the channel in the resource elements transmitting references and interpolates for the rest
@ -63,11 +67,19 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
goto clean_exit;
}
q->pilot_symbol_avg = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb);
if (!q->pilot_symbol_avg) {
q->tmp_freqavg = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb);
if (!q->tmp_freqavg) {
perror("malloc");
goto clean_exit;
}
for (int i=0;i<CHEST_MAX_FILTER_TIME_LEN;i++) {
q->tmp_timeavg[i] = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb);
if (!q->tmp_timeavg[i]) {
perror("malloc");
goto clean_exit;
}
bzero(q->tmp_timeavg[i], sizeof(cf_t) * 2*cell.nof_prb);
}
for (int i=0;i<cell.nof_ports;i++) {
q->pilot_estimates[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb));
@ -75,21 +87,34 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
perror("malloc");
goto clean_exit;
}
q->pilot_estimates_average[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb));
if (!q->pilot_estimates_average[i]) {
perror("malloc");
goto clean_exit;
}
q->pilot_recv_signal[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb));
if (!q->pilot_recv_signal[i]) {
perror("malloc");
goto clean_exit;
}
#ifdef VOLK_INTERP
ret = interp_init(&q->interp_freq[i], LINEAR, 2*cell.nof_prb, RE_X_RB/2);
if (ret == LIBLTE_SUCCESS) {
ret = interp_init(&q->interp_time[i], LINEAR, 2, CP_NSYMB(cell.cp) - 3);
}
#endif
}
/* Init buffer for holding CE estimates averages */
if (interp_linear_vector_init(&q->interp_linvec, RE_X_RB*cell.nof_prb)) {
fprintf(stderr, "Error initializing vector interpolator\n");
goto clean_exit;
}
if (interp_linear_init(&q->interp_lin, 2*cell.nof_prb, RE_X_RB/2)) {
fprintf(stderr, "Error initializing interpolator\n");
goto clean_exit;
}
/* Set default time/freq filters */
float f[3]={0.15, 0.7, 0.15};
chest_dl_set_filter_freq(q, f, 3);
float t[2]={0.1, 0.9};
chest_dl_set_filter_time(q, t, 2);
q->cell = cell;
}
@ -107,10 +132,13 @@ void chest_dl_free(chest_dl_t *q)
{
refsignal_cs_free(&q->csr_signal);
if (q->pilot_symbol_avg) {
free(q->pilot_symbol_avg);
if (q->tmp_freqavg) {
free(q->tmp_freqavg);
}
interp_linear_vector_free(&q->interp_linvec);
interp_linear_free(&q->interp_lin);
for (int i=0;i<MAX_PORTS;i++) {
if (q->pilot_estimates[i]) {
free(q->pilot_estimates[i]);
@ -118,166 +146,163 @@ void chest_dl_free(chest_dl_t *q)
if (q->pilot_recv_signal[i]) {
free(q->pilot_recv_signal[i]);
}
#ifdef VOLK_INTERP
interp_free(&q->interp_freq[i]);
interp_free(&q->interp_time[i]);
#endif
}
}
#define pilot_est(idx) q->pilot_estimates[port_id][REFSIGNAL_PILOT_IDX(idx,l,ns,q->cell)]
int chest_dl_set_filter_freq(chest_dl_t *q, float *filter, uint32_t filter_len) {
if (filter_len <= CHEST_MAX_FILTER_FREQ_LEN) {
q->filter_freq_len = filter_len;
for (int i=0;i<filter_len;i++) {
q->filter_freq[i] = filter[i];
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR;
}
}
#if CHEST_RS_AVERAGE_TIME > 1
cf_t timeavg[CHEST_RS_AVERAGE_TIME-1][12];
int nof_timeavg=0;
#endif
int chest_dl_set_filter_time(chest_dl_t *q, float *filter, uint32_t filter_len) {
if (filter_len <= CHEST_MAX_FILTER_TIME_LEN) {
q->filter_time_len = filter_len;
for (int i=0;i<filter_len;i++) {
q->filter_time[i] = filter[i];
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR;
}
}
static void average_pilots(chest_dl_t *q, uint32_t sf_idx, uint32_t port_id)
#define pilot_est(idx) q->pilot_estimates[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)]
#define pilot_avg(idx) q->pilot_estimates_average[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)]
static void average_pilots(chest_dl_t *q, uint32_t port_id)
{
uint32_t ns, l;
int i;
int nref=2*q->cell.nof_prb;
uint32_t l, i;
/* For each symbol with pilots in a slot */
for (ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
bzero(q->pilot_symbol_avg, 2*q->cell.nof_prb);
/** Frequency average */
#if CHEST_RS_AVERAGE_FREQ > 1
const uint32_t M = CHEST_RS_AVERAGE_FREQ;
cf_t xint[CHEST_RS_AVERAGE_FREQ];
int j, k;
/* Extrapolate first M/2 samples */
for (i=M/2-1;i>=0;i--) {
k=0;
for (j=i+M/2;j>=0;j--) {
xint[k]=pilot_est(j);
k++;
}
for (;j>=i-M/2;j--) {
if (k>=2) {
xint[k] = interp_linear_onesample(&xint[k-2]);
k++;
}
}
q->pilot_symbol_avg[i] = vec_acc_cc(xint,M)/M;
//q->pilot_symbol_avg[i] = (pilot_est(0)+pilot_est(1))/2;
}
for (i=M/2;i<2*q->cell.nof_prb-M/2;i++) {
q->pilot_symbol_avg[i] = vec_acc_cc(&pilot_est(i-M/2),M)/M;
}
/* Extrapolate last M/2 samples */
for (;i<2*q->cell.nof_prb;i++) {
k=0;
for (j=i-M/2;j<2*q->cell.nof_prb;j++) {
xint[k]=pilot_est(j);
k++;
}
for (;k<M;k++) {
if (k>=2) {
xint[k] = interp_linear_onesample(&xint[k-2]);
}
}
q->pilot_symbol_avg[i] = vec_acc_cc(xint,M)/M;
//q->pilot_symbol_avg[i] = (pilot_est(i)+pilot_est(i+1))/2;
}
#else
memcpy(q->pilot_symbol_avg, &pilot_est(0), 2*q->cell.nof_prb*sizeof(cf_t));
#endif
/* Time average last symbols */
#if CHEST_RS_AVERAGE_TIME > 1
if (nof_timeavg<CHEST_RS_AVERAGE_TIME-1) {
memcpy(timeavg[nof_timeavg],q->pilot_symbol_avg, 2*q->cell.nof_prb * sizeof(cf_t));
nof_timeavg++;
} else {
bzero(&pilot_est(0),2*q->cell.nof_prb*sizeof(cf_t));
for (i=0;i<nof_timeavg;i++) {
vec_sum_ccc(timeavg[i],&pilot_est(0),&pilot_est(0),2*q->cell.nof_prb);
}
vec_sum_ccc(q->pilot_symbol_avg,&pilot_est(0),&pilot_est(0),2*q->cell.nof_prb);
vec_sc_prod_cfc(&pilot_est(0), 1.0/CHEST_RS_AVERAGE_TIME, &pilot_est(0), 2*q->cell.nof_prb);
for (i=0;i<nof_timeavg-1;i++) {
memcpy(timeavg[i],timeavg[i+1],2*q->cell.nof_prb*sizeof(cf_t));
}
memcpy(timeavg[i],q->pilot_symbol_avg,2*q->cell.nof_prb*sizeof(cf_t));
}
#else
memcpy(&pilot_est(0), q->pilot_symbol_avg, 2*q->cell.nof_prb * sizeof(cf_t));
#endif
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
if (q->filter_freq_len > 0) {
/* Filter pilot estimates in frequency */
conv_same_cf(&pilot_est(0), q->filter_freq, q->tmp_freqavg, nref, q->filter_freq_len);
/* Adjust extremes using linear interpolation */
q->tmp_freqavg[0] += interp_linear_onesample(pilot_est(1), pilot_est(0))
* q->filter_freq[q->filter_freq_len/2-1];
q->tmp_freqavg[nref-1] += interp_linear_onesample(pilot_est(nref-2), pilot_est(nref-1))
* q->filter_freq[q->filter_freq_len/2+1];
} else {
memcpy(q->tmp_freqavg, &pilot_est(0), nref * sizeof(cf_t));
}
}
}
/* Filter in time domain. */
if (q->filter_time_len > 0) {
/* Move last symbols */
for (i=0;i<q->filter_time_len-1;i++) {
memcpy(q->tmp_timeavg[i], q->tmp_timeavg[i+1], nref*sizeof(cf_t));
}
/* Put last symbol to buffer */
memcpy(q->tmp_timeavg[i], q->tmp_freqavg, nref*sizeof(cf_t));
static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t sf_idx, uint32_t port_id)
{
/* interpolate the symbols with references in the freq domain */
uint32_t ns, l, i,j;
cf_t x[2], y[MAX_NSYMB];
for (ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
uint32_t fidx_offset = refsignal_fidx(q->cell, ns, l, port_id, 0);
#ifdef VOLK_INTERP
interp_run_offset(&q->interp_freq[port_id],
&q->pilot_estimates[port_id][((ns%2)*2+l)*2*q->cell.nof_prb],
&ce[refsignal_nsymbol(q->cell,ns,l) * q->cell.nof_prb * RE_X_RB],
fidx_offset, RE_X_RB/2-fidx_offset);
#else
interp_linear_offset(&q->pilot_estimates[port_id][((ns%2)*2+l)*2*q->cell.nof_prb],
&ce[refsignal_nsymbol(q->cell,ns,l) * q->cell.nof_prb * RE_X_RB], RE_X_RB/2,
2*q->cell.nof_prb, fidx_offset, RE_X_RB/2-fidx_offset);
#endif
}
}
/* now interpolate in the time domain */
for (i=0;i<RE_X_RB*q->cell.nof_prb; i++) {
if (refsignal_cs_nof_symbols(port_id) > 1) {
for (ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
j=0;
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
x[j] = ce[refsignal_nsymbol(q->cell,ns,l) * q->cell.nof_prb * RE_X_RB + i];
j++;
}
#ifdef VOLK_INTERP
interp_run_offset(&q->interp_time[port_id], x, y,
0, CP_NSYMB(q->cell.cp) - 4);
#else
interp_linear_offset(x, y, CP_NSYMB(q->cell.cp) - 3,
2, 0, CP_NSYMB(q->cell.cp) - 4);
#endif
for (j=0;j<CP_NSYMB(q->cell.cp);j++) {
ce[(j+((ns%2)*CP_NSYMB(q->cell.cp))) * q->cell.nof_prb*RE_X_RB + i] = y[j];
}
/* Multiply all symbols by filter and add them */
bzero(&pilot_avg(0), nref * sizeof(cf_t));
for (i=0;i<q->filter_time_len;i++) {
vec_sc_prod_cfc(q->tmp_timeavg[i], q->filter_time[i], q->tmp_timeavg[i], nref);
vec_sum_ccc(q->tmp_timeavg[i], &pilot_avg(0), &pilot_avg(0), nref);
}
} else {
fprintf(stderr, "3/4 Ports interpolator not implemented\n");
exit(-1);
}
memcpy(&pilot_avg(0), q->tmp_freqavg, nref * sizeof(cf_t));
}
}
}
static float estimate_noise_port(chest_dl_t *q, uint32_t port_id) {
/* Use difference between averaged and noisy LS pilot estimates */
vec_sub_fff((float*) q->pilot_estimates_average[port_id], (float*) q->pilot_estimates[port_id],
(float*) q->pilot_estimates[port_id], 2*REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
/* compute noise power */
float noiseEst = vec_dot_prod_conj_ccc(q->pilot_estimates[port_id],
q->pilot_estimates[port_id],
REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id));
return noiseEst * sqrtf((float) q->filter_freq_len) / REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id);
}
#define cesymb(i) ce[SAMPLE_IDX(q->cell.nof_prb,i,0)]
static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t port_id)
{
/* interpolate the symbols with references in the freq domain */
uint32_t l;
uint32_t nsymbols = refsignal_cs_nof_symbols(port_id);
/* Interpolate in the frequency domain */
for (l=0;l<nsymbols;l++) {
uint32_t fidx_offset = refsignal_fidx(q->cell, l, port_id, 0);
interp_linear_offset(&q->interp_lin, &pilot_avg(0),
&ce[refsignal_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB],
fidx_offset, RE_X_RB/2-fidx_offset);
}
/* Now interpolate in the time domain between symbols */
if (nsymbols == 4) {
interp_linear_vector(&q->interp_linvec, &cesymb(0), &cesymb(4), &cesymb(1), 3);
interp_linear_vector(&q->interp_linvec, &cesymb(4), &cesymb(7), &cesymb(5), 2);
interp_linear_vector(&q->interp_linvec, &cesymb(7), &cesymb(11), &cesymb(8), 3);
interp_linear_vector(&q->interp_linvec, &cesymb(7), &cesymb(11), &cesymb(12), 2);
} else {
interp_linear_vector(&q->interp_linvec, &cesymb(8), &cesymb(1), &cesymb(0), 1);
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(8), &cesymb(2), 6);
interp_linear_vector(&q->interp_linvec, &cesymb(1), &cesymb(8), &cesymb(9), 5);
}
}
float chest_dl_rssi(lte_cell_t cell, cf_t *input) {
float rssi = 0;
uint32_t l, p;
uint32_t loop_ports = cell.nof_ports>2?2:1;
for (p=0;p<loop_ports;p++) {
uint32_t nsymbols = refsignal_cs_nof_symbols(2*p);
for (l=0;l<nsymbols;l++) {
cf_t *tmp = &input[refsignal_nsymbol(l,cell.cp, 2*p) * cell.nof_prb * RE_X_RB];
rssi += crealf(vec_dot_prod_conj_ccc(tmp, tmp, cell.nof_prb * RE_X_RB));
}
}
return rssi;
}
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],
q->pilot_estimates_average[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)
{
//filter2d_reset(&q->filter);
/* Get references from the input signal */
refsignal_cs_get_sf(q->cell, port_id, sf_idx, input, q->pilot_recv_signal[port_id]);
refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal[port_id]);
/* Use the known CSR signal to compute Least-squares estimates */
vec_div_ccc_mod1(q->pilot_recv_signal[port_id], q->csr_signal.pilots[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));
/* Average pilot estimates */
average_pilots(q, sf_idx, 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_pilots(q, ce, sf_idx, port_id);
if (ce != NULL) {
interpolate_pilots(q, ce, port_id);
}
q->noise_estimate[port_id] = estimate_noise_port(q, port_id);
return 0;
}
@ -288,19 +313,13 @@ 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++) {
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;
}
int chest_dl_equalize_zf(chest_dl_t *q, cf_t *input, cf_t *ce[MAX_PORTS], cf_t *output)
{
fprintf(stderr, "Not implemented\n");
return -1;
}
int chest_dl_equalize_mmse(chest_dl_t *q, cf_t *input, cf_t *ce[MAX_PORTS], float *noise_estimate, cf_t *output)
{
fprintf(stderr, "Not implemented\n");
return -1;
float chest_dl_get_noise_estimate(chest_dl_t *q) {
return vec_acc_ff(q->noise_estimate, q->cell.nof_ports)/q->cell.nof_ports;
}
float chest_dl_get_rssi(chest_dl_t *q) {
@ -308,10 +327,10 @@ float chest_dl_get_rssi(chest_dl_t *q) {
}
float chest_dl_get_rsrq(chest_dl_t *q) {
return q->rsrq;
return (4*q->cell.nof_ports*q->cell.nof_prb) * chest_dl_get_rsrp(q) / q->rssi;
}
float chest_dl_get_rsrp(chest_dl_t *q) {
return q->rsrp;
return vec_acc_ff(q->rsrp, q->cell.nof_ports)/q->cell.nof_ports;
}

View File

@ -1,382 +0,0 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <math.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <complex.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/ch_estimation/refsignal.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/common/sequence.h"
#include "ul_rs_tables.h"
#define idx(x, y) (l*nof_refs_x_symbol+i)
int refsignal_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id)
{
int v = -1;
switch (port_id) {
case 0:
if (symbol_id == 0) {
v = 0;
} else {
v = 3;
}
break;
case 1:
if (symbol_id == 0) {
v = 3;
} else {
v = 0;
}
break;
case 2:
v = 3 * (ns % 2);
break;
case 3:
v = 3 + 3 * (ns % 2);
break;
}
return v;
}
uint32_t refsignal_k(uint32_t m, uint32_t v, uint32_t cell_id)
{
return 6 * m + ((v + (cell_id % 6)) % 6);
}
int refsignal_put(refsignal_t * q, cf_t * slot_symbols)
{
uint32_t i;
uint32_t fidx, tidx;
if (q != NULL && slot_symbols != NULL) {
for (i = 0; i < q->nof_refs; i++) {
fidx = q->refs[i].freq_idx; // reference frequency index
tidx = q->refs[i].time_idx; // reference time index
slot_symbols[SAMPLE_IDX(q->nof_prb, tidx, fidx)] = q->refs[i].symbol;
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/** Initializes refsignal_t object according to 3GPP 36.211 6.10.1
*
*/
int refsignal_init_LTEDL(refsignal_t * q, uint32_t port_id, uint32_t nslot,
lte_cell_t cell)
{
uint32_t c_init;
uint32_t ns, l, lp[2];
uint32_t N_cp;
uint32_t i;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
sequence_t seq;
int v;
uint32_t mp;
uint32_t nof_refs_x_symbol, nof_ref_symbols;
if (q != NULL &&
port_id < MAX_PORTS &&
nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
bzero(q, sizeof(refsignal_t));
bzero(&seq, sizeof(sequence_t));
if (CP_ISNORM(cell.cp)) {
N_cp = 1;
} else {
N_cp = 0;
}
if (port_id < 2) {
nof_ref_symbols = 2;
lp[0] = 0;
lp[1] = CP_NSYMB(cell.cp) - 3;
} else {
nof_ref_symbols = 1;
lp[0] = 1;
}
nof_refs_x_symbol = 2 * cell.nof_prb;
q->nof_refs = nof_refs_x_symbol * nof_ref_symbols;
q->nsymbols = nof_ref_symbols;
q->voffset = cell.id % 6;
q->nof_prb = cell.nof_prb;
q->symbols_ref = malloc(sizeof(uint32_t) * nof_ref_symbols);
if (!q->symbols_ref) {
perror("malloc");
goto free_and_exit;
}
memcpy(q->symbols_ref, lp, sizeof(uint32_t) * nof_ref_symbols);
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
if (!q->refs) {
goto free_and_exit;
}
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
if (!q->ch_est) {
goto free_and_exit;
}
q->recv_symbol = vec_malloc(q->nof_refs * sizeof(cf_t));
if (!q->recv_symbol) {
goto free_and_exit;
}
ns = nslot;
for (l = 0; l < nof_ref_symbols; l++) {
c_init = 1024 * (7 * (ns + 1) + lp[l] + 1) * (2 * cell.id + 1)
+ 2 * cell.id + N_cp;
ret = sequence_LTE_pr(&seq, 2 * 2 * MAX_PRB, c_init);
if (ret != LIBLTE_SUCCESS) {
goto free_and_exit;
}
v = refsignal_v(port_id, ns, lp[l]);
for (i = 0; i < nof_refs_x_symbol; i++) {
mp = i + MAX_PRB - cell.nof_prb;
/* generate signal */
__real__ q->refs[idx(l, i)].symbol =
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2);
__imag__ q->refs[idx(l, i)].symbol =
(1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
/* mapping to resource elements */
q->refs[idx(l, i)].freq_idx = refsignal_k(i, (uint32_t) v, cell.id);
q->refs[idx(l, i)].time_idx = lp[l];
}
}
ret = LIBLTE_SUCCESS;
}
free_and_exit:
if (ret != LIBLTE_ERROR_INVALID_INPUTS) {
sequence_free(&seq);
}
if (ret == LIBLTE_ERROR) {
refsignal_free(q);
}
return ret;
}
// n_drms_2 table 5.5.2.1.1-1 from 36.211
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
// n_drms_1 table 5.5.2.1.1-2 from 36.211
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
refsignal_ul_cfg_t * cfg)
{
uint32_t i;
// Calculate u and v
uint32_t u, v;
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
if (cfg->group_hopping_en) {
sequence_t seq;
sequence_LTE_pr(&seq, cell_id / 30, 160);
uint32_t f_gh = 0;
for (i = 0; i < 8; i++) {
f_gh += seq.c[8 * ns + i] << i;
}
sequence_free(&seq);
u = ((f_gh%30) + f_ss) % 30;
} else {
u = f_ss % 30;
}
if (len < 6 * RE_X_RB) {
v = 0;
} else {
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
sequence_t seq;
sequence_LTE_pr(&seq, ((cell_id / 30) << 5) + f_ss, 20);
v = seq.c[ns];
sequence_free(&seq);
} else {
v = 0;
}
}
if (len >= 3 * RE_X_RB) {
uint32_t n_sz=0;
uint32_t q;
float q_hat;
/* get largest prime n_zc<len */
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
if (prime_numbers[i] < len) {
n_sz = prime_numbers[i];
break;
}
}
q_hat = (float) n_sz *(u + 1) / 31;
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
q = (uint32_t) (q_hat + 0.5) + v;
} else {
q = (uint32_t) (q_hat + 0.5) - v;
}
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
if (!x_q) {
perror("malloc");
return LIBLTE_ERROR;
}
for (i = 0; i < n_sz; i++) {
x_q[i] =
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
}
for (i = 0; i < len; i++) {
refs[i].symbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
}
free(x_q);
} else {
if (len == RE_X_RB) {
for (i = 0; i < len; i++) {
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
}
} else {
for (i = 0; i < len; i++) {
refs[i].symbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
}
}
}
return LIBLTE_SUCCESS;
}
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
*
*/
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
{
uint32_t i;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t n_prs;
uint32_t M_sc;
float alpha;
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
bzero(q, sizeof(refsignal_t));
M_sc = nof_prb * RE_X_RB;
q->nof_refs = M_sc;
q->nsymbols = 1;
q->voffset = cell.id % 6;
q->nof_prb = cell.nof_prb;
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
if (!q->symbols_ref) {
perror("malloc");
goto free_and_exit;
}
if (CP_ISNORM(cell.cp)) {
q->symbols_ref[0] = 3;
} else {
q->symbols_ref[0] = 2;
}
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
if (!q->refs) {
goto free_and_exit;
}
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
if (!q->ch_est) {
goto free_and_exit;
}
/* Calculate n_prs */
uint32_t c_init;
sequence_t seq;
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
if (ret != LIBLTE_SUCCESS) {
goto free_and_exit;
}
n_prs = 0;
for (i = 0; i < 8; i++) {
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
}
sequence_free(&seq);
// Calculate cyclic shift alpha
uint32_t n_cs =
(n_drms_1[cfg->cyclic_shift] +
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
alpha = 2 * M_PI * (n_cs) / 12;
if (rs_sequence(q->refs, M_sc, alpha, cell.id, nslot, cfg)) {
fprintf(stderr, "Error generating RS sequence\n");
goto free_and_exit;
}
/* mapping to resource elements */
for (i=0;i<M_sc;i++) {
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
q->refs[i].time_idx = q->symbols_ref[0];
}
ret = LIBLTE_SUCCESS;
}
free_and_exit:
if (ret == LIBLTE_ERROR) {
refsignal_free(q);
}
return ret;
}
void refsignal_free(refsignal_t * q)
{
if (q->symbols_ref) {
free(q->symbols_ref);
}
if (q->refs) {
free(q->refs);
}
if (q->ch_est) {
free(q->ch_est);
}
bzero(q, sizeof(refsignal_t));
}

View File

@ -38,29 +38,37 @@
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/common/sequence.h"
uint32_t refsignal_cs_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id)
uint32_t refsignal_cs_v(uint32_t port_id, uint32_t ref_symbol_idx)
{
uint32_t v = 0;
switch (port_id) {
case 0:
if (symbol_id == 0) {
if (!(ref_symbol_idx % 2)) {
v = 0;
} else {
v = 3;
}
break;
case 1:
if (symbol_id == 0) {
if (!(ref_symbol_idx % 2)) {
v = 3;
} else {
v = 0;
}
break;
case 2:
v = 3 * (ns % 2);
if (ref_symbol_idx < 2) {
v = 0;
} else {
v = 3;
}
break;
case 3:
v = 3 + 3 * (ns % 2);
if (ref_symbol_idx < 2) {
v = 3;
} else {
v = 6;
}
break;
}
return v;
@ -68,21 +76,30 @@ uint32_t refsignal_cs_v(uint32_t port_id, uint32_t ns, uint32_t symbol_id)
uint32_t refsignal_cs_nof_symbols(uint32_t port_id)
{
if (port_id < 2) {
return 2;
} else {
return 1;
}
}
static uint32_t lp(uint32_t l, lte_cp_t cp) {
if (l == 1) {
return CP_NSYMB(cp) - 3;
if (port_id < 2) {
return 4;
} else {
return 0;
return 2;
}
}
inline uint32_t refsignal_fidx(lte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) {
return 6*m + ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
}
inline uint32_t refsignal_nsymbol(uint32_t l, lte_cp_t cp, uint32_t port_id) {
if (port_id < 2) {
if (l % 2) {
return (l/2+1)*CP_NSYMB(cp) - 3;
} else {
return (l/2)*CP_NSYMB(cp);
}
} else {
return 1+l*CP_NSYMB(cp);
}
}
/** Allocates and precomputes the Cell-Specific Reference (CSR) signal for
* the 20 slots in a subframe
*/
@ -90,7 +107,7 @@ int refsignal_cs_generate(refsignal_cs_t * q, lte_cell_t cell)
{
uint32_t c_init;
uint32_t i, ns, l;
uint32_t i, ns, l, p;
uint32_t N_cp, mp;
sequence_t seq;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
@ -114,31 +131,38 @@ int refsignal_cs_generate(refsignal_cs_t * q, lte_cell_t cell)
q->cell = cell;
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
q->pilots[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(q->cell.nof_prb));
if (!q->pilots[i]) {
perror("malloc");
goto free_and_exit;
}
for (p=0;p<2;p++) {
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
q->pilots[p][i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(q->cell.nof_prb, 2*p));
if (!q->pilots[p][i]) {
perror("malloc");
goto free_and_exit;
}
}
}
for (ns=0;ns<NSLOTS_X_FRAME;ns++) {
for (l = 0; l < 2; l++) {
/* Compute sequence init value */
c_init = 1024 * (7 * (ns + 1) + lp(l,cell.cp) + 1) * (2 * cell.id + 1)
+ 2 * cell.id + N_cp;
for (p=0;p<2;p++) {
uint32_t nsymbols = refsignal_cs_nof_symbols(2*p)/2;
for (l = 0; l < nsymbols; l++) {
/* Compute sequence init value */
uint32_t lp = refsignal_nsymbol(l, cell.cp, 2*p);
c_init = 1024 * (7 * (ns + 1) + lp + 1) * (2 * cell.id + 1)
+ 2 * cell.id + N_cp;
/* generate sequence for this symbol and slot */
sequence_set_LTE_pr(&seq, c_init);
/* Compute signal */
for (i = 0; i < 2*q->cell.nof_prb; i++) {
mp = i + MAX_PRB - cell.nof_prb;
/* save signal */
q->pilots[p][ns/2][REFSIGNAL_PILOT_IDX(i,(ns%2)*nsymbols+l,q->cell)] =
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2) +
_Complex_I * (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
}
}
/* generate sequence for this symbol and slot */
sequence_set_LTE_pr(&seq, c_init);
/* Compute signal */
for (i = 0; i < 2*q->cell.nof_prb; i++) {
mp = i + MAX_PRB - cell.nof_prb;
/* save signal */
q->pilots[ns/2][REFSIGNAL_PILOT_IDX(i,l,ns,q->cell)] =
(1 - 2 * (float) seq.c[2 * mp]) / sqrt(2) +
_Complex_I * (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2);
}
}
}
sequence_free(&seq);
@ -155,51 +179,42 @@ free_and_exit:
/** Deallocates a refsignal_cs_t object allocated with refsignal_cs_init */
void refsignal_cs_free(refsignal_cs_t * q)
{
int i;
int i, p;
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
if (q->pilots[i]) {
free(q->pilots[i]);
}
}
for (p=0;p<2;p++) {
for (i=0;i<NSUBFRAMES_X_FRAME;i++) {
if (q->pilots[p][i]) {
free(q->pilots[p][i]);
}
}
}
bzero(q, sizeof(refsignal_cs_t));
}
inline uint32_t refsignal_fidx(lte_cell_t cell, uint32_t ns, uint32_t l, uint32_t port_id, uint32_t m) {
return 6*m + ((refsignal_cs_v(port_id, ns, lp(l,cell.cp)) + (cell.id % 6)) % 6);
}
inline uint32_t refsignal_nsymbol(lte_cell_t cell, uint32_t ns, uint32_t l) {
return (ns%2)*CP_NSYMB(cell.cp)+lp(l,cell.cp);
}
/* Maps a reference signal initialized with refsignal_cs_init() into an array of subframe symbols */
int refsignal_cs_put_sf(lte_cell_t cell, uint32_t port_id, uint32_t sf_idx,
cf_t *pilots, cf_t *sf_symbols)
int refsignal_cs_put_sf(lte_cell_t cell, uint32_t port_id, cf_t *pilots, cf_t *sf_symbols)
{
uint32_t i, l, ns;
uint32_t i, l;
uint32_t fidx;
if (lte_cell_isvalid(&cell) &&
lte_sfidx_isvalid(sf_idx) &&
lte_portid_isvalid(port_id) &&
pilots != NULL &&
sf_symbols != NULL)
{
for (ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
/* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, ns, lp(l,cell.cp)) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) {
uint32_t nsymbol = refsignal_nsymbol(cell, ns, l);
sf_symbols[SAMPLE_IDX(cell.nof_prb, nsymbol, fidx)] = pilots[REFSIGNAL_PILOT_IDX(i,l,ns,cell)];
fidx += 6; // 1 reference every 6 RE
DEBUG("Putting %d to %d (fidx: %d, lp:%d)\n",REFSIGNAL_PILOT_IDX(i,l,ns,cell), SAMPLE_IDX(cell.nof_prb, nsymbol, fidx),
fidx, nsymbol);
}
}
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id);
/* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) {
sf_symbols[SAMPLE_IDX(cell.nof_prb, nsymbol, fidx)] = pilots[REFSIGNAL_PILOT_IDX(i,l,cell)];
fidx += 6; // 1 reference every 6 RE
DEBUG("Putting %d to %d (fidx: %d, lp:%d)\n",
REFSIGNAL_PILOT_IDX(i,l,cell), SAMPLE_IDX(cell.nof_prb, nsymbol, fidx),
fidx, nsymbol);
}
}
return LIBLTE_SUCCESS;
} else {
@ -211,30 +226,26 @@ int refsignal_cs_put_sf(lte_cell_t cell, uint32_t port_id, uint32_t sf_idx,
* csr_signal[0] is the signal for the first OFDM symbol containing references and csr_signal[1] is the
* second OFDM symbol containing references (symbol 4 or 3 if port_id < 2)
*/
int refsignal_cs_get_sf(lte_cell_t cell, uint32_t port_id, uint32_t sf_idx,
cf_t *sf_symbols, cf_t *pilots)
int refsignal_cs_get_sf(lte_cell_t cell, uint32_t port_id, cf_t *sf_symbols, cf_t *pilots)
{
uint32_t i, l, ns;
uint32_t i, l;
uint32_t fidx;
if (lte_cell_isvalid(&cell) &&
lte_sfidx_isvalid(sf_idx) &&
lte_portid_isvalid(port_id) &&
pilots != NULL &&
sf_symbols != NULL)
{
for (ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
/* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, ns, lp(l,cell.cp)) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) {
uint32_t nsymbol = refsignal_nsymbol(cell, ns, l);
pilots[REFSIGNAL_PILOT_IDX(i,l,ns,cell)] = sf_symbols[SAMPLE_IDX(cell.nof_prb, nsymbol, fidx)];
fidx += 6; // 1 reference every 6 RE
DEBUG("Getting %d from %d (fidx: %d, lp:%d)\n",REFSIGNAL_PILOT_IDX(i,l,ns,cell), SAMPLE_IDX(cell.nof_prb, nsymbol, fidx),
fidx, nsymbol);
}
{
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id);
/* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) {
pilots[REFSIGNAL_PILOT_IDX(i,l,cell)] = sf_symbols[SAMPLE_IDX(cell.nof_prb, nsymbol, fidx)];
DEBUG("Getting %d from %d (fidx: %d, lp:%d)\n",REFSIGNAL_PILOT_IDX(i,l,cell),
SAMPLE_IDX(cell.nof_prb, nsymbol, fidx),
fidx, nsymbol);
fidx += RE_X_RB/2; // 2 references per PRB
}
}
return LIBLTE_SUCCESS;

View File

@ -0,0 +1,183 @@
#ifdef nocompile
// n_drms_2 table 5.5.2.1.1-1 from 36.211
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
// n_drms_1 table 5.5.2.1.1-2 from 36.211
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id,
refsignal_ul_cfg_t * cfg)
{
uint32_t i;
// Calculate u and v
uint32_t u, v;
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
printf("f_ss: %d\n", f_ss);
if (cfg->group_hopping_en) {
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
sequence_LTE_pr(&seq, 160, cell_id / 30);
uint32_t f_gh = 0;
for (i = 0; i < 8; i++) {
f_gh += (((uint32_t) seq.c[8 * ns + i]) << i);
}
printf("f_gh: %u\n", f_gh);
sequence_free(&seq);
u = ((f_gh%30) + f_ss) % 30;
} else {
u = f_ss % 30;
}
if (len < 6 * RE_X_RB) {
v = 0;
} else {
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) {
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
sequence_LTE_pr(&seq, 20, ((cell_id / 30) << 5) + f_ss);
v = seq.c[ns];
sequence_free(&seq);
} else {
v = 0;
}
}
printf("u: %d, v: %d\n", u, v);
if (len >= 3 * RE_X_RB) {
uint32_t n_sz;
uint32_t q;
float q_hat;
/* get largest prime n_zc<len */
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
if (prime_numbers[i] < len) {
n_sz = prime_numbers[i];
break;
}
}
printf("n_sz: %d\n", n_sz);
q_hat = (float) n_sz *(u + 1) / 31;
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
q = (uint32_t) (q_hat + 0.5) + v;
} else {
q = (uint32_t) (q_hat + 0.5) - v;
}
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
if (!x_q) {
perror("malloc");
return LIBLTE_ERROR;
}
for (i = 0; i < n_sz; i++) {
x_q[i] =
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz);
}
for (i = 0; i < len; i++) {
refs[i].simbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz];
}
free(x_q);
} else {
if (len == RE_X_RB) {
for (i = 0; i < len; i++) {
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i));
}
} else {
for (i = 0; i < len; i++) {
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i));
}
}
}
return LIBLTE_SUCCESS;
}
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
*
*/
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start,
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
{
uint32_t i;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t n_prs;
uint32_t M_sc;
float alpha;
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) {
bzero(q, sizeof(refsignal_t));
M_sc = nof_prb * RE_X_RB;
q->nof_refs = M_sc;
q->nsymbols = 1;
q->voffset = cell.id % 6;
q->nof_prb = cell.nof_prb;
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
if (!q->symbols_ref) {
perror("malloc");
goto free_and_exit;
}
if (CP_ISNORM(cell.cp)) {
q->symbols_ref[0] = 3;
} else {
q->symbols_ref[0] = 2;
}
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
if (!q->refs) {
goto free_and_exit;
}
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
if (!q->ch_est) {
goto free_and_exit;
}
/* Calculate n_prs */
uint32_t c_init;
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
if (ret != LIBLTE_SUCCESS) {
goto free_and_exit;
}
n_prs = 0;
for (i = 0; i < 8; i++) {
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
}
sequence_free(&seq);
// Calculate cyclic shift alpha
uint32_t n_cs =
(n_drms_1[cfg->cyclic_shift] +
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
alpha = 2 * M_PI * (n_cs) / 12;
printf("alpha: %g\n", alpha);
if (rs_sequence(q->refs, M_sc, alpha, nslot, cell.id, cfg)) {
fprintf(stderr, "Error generating RS sequence\n");
goto free_and_exit;
}
/* mapping to resource elements */
for (i=0;i<M_sc;i++) {
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
q->refs[i].time_idx = q->symbols_ref[0];
}
ret = LIBLTE_SUCCESS;
}
free_and_exit:
if (ret == LIBLTE_ERROR) {
refsignal_free(q);
}
return ret;
}
#endif

View File

@ -177,7 +177,7 @@ int main(int argc, char **argv) {
bzero(ce, sizeof(cf_t) * num_re);
bzero(h, sizeof(cf_t) * num_re);
refsignal_cs_put_sf(cell, n_port, sf_idx, eq.csr_signal.pilots[sf_idx], input);
refsignal_cs_put_sf(cell, n_port, eq.csr_signal.pilots[n_port/2][sf_idx], input);
for (i=0;i<2*CP_NSYMB(cell.cp);i++) {
for (j=0;j<cell.nof_prb * RE_X_RB;j++) {
@ -187,7 +187,14 @@ int main(int argc, char **argv) {
}
}
chest_dl_estimate_port(&eq, input, ce, sf_idx, n_port);
struct timeval t[3];
gettimeofday(&t[1], NULL);
for (int j=0;j<1000;j++) {
chest_dl_estimate_port(&eq, input, ce, sf_idx, n_port);
}
gettimeofday(&t[2], NULL);
get_time_interval(t);
printf("%f us\n", (float) t[0].tv_usec/1000);
mse_mag = mse_phase = 0;
for (i=0;i<num_re;i++) {

View File

@ -41,11 +41,13 @@
#define INPUT prhs[2]
#define NOF_INPUTS 3
#define SFIDX prhs[3]
#define FREQ_FILTER prhs[3]
#define TIME_FILTER prhs[4]
void help()
{
mexErrMsgTxt
("[estChannel] = liblte_chest(cell_id, nof_ports, inputSignal,[sf_idx])\n\n"
("[estChannel] = liblte_chest(cell_id, nof_ports, inputSignal,[sf_idx|freq_filter], [time_filter])\n\n"
" Returns a matrix of size equal to the inputSignal matrix with the channel estimates\n "
"for each resource element in inputSignal. The inputSignal matrix is the received Grid\n"
"of size nof_resource_elements x nof_ofdm_symbols.\n"
@ -62,7 +64,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
cf_t *input_signal;
cf_t *ce[MAX_PORTS];
double *outr0, *outi0, *outr1, *outi1;
float noiseAverage[10];
if (nrhs < NOF_INPUTS) {
help();
return;
@ -83,6 +86,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
} else if ((mxGetN(INPUT)%12)!=0) {
cell.cp = CPEXT;
} else {
mexErrMsgTxt("Invalid number of symbols\n");
help();
return;
}
@ -91,7 +95,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
mexErrMsgTxt("Error initiating channel estimator\n");
return;
}
int nsubframes;
if (cell.cp == CPNORM) {
nsubframes = mxGetN(INPUT)/14;
@ -102,12 +106,38 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
uint32_t sf_idx=0;
if (nsubframes == 1) {
if (nrhs != NOF_INPUTS+1) {
mexErrMsgTxt("Received 1 subframe. Need to provide subframe index.\n");
help();
return;
}
sf_idx = (uint32_t) *((double*) mxGetPr(SFIDX));
}
uint32_t filter_len = 0;
float *filter;
double *f;
if (nrhs > NOF_INPUTS && nsubframes == 10) {
if (nrhs >= NOF_INPUTS + 1) {
filter_len = mxGetNumberOfElements(FREQ_FILTER);
filter = malloc(sizeof(float) * filter_len);
f = (double*) mxGetPr(FREQ_FILTER);
for (i=0;i<filter_len;i++) {
filter[i] = (float) f[i];
}
chest_dl_set_filter_freq(&chest, filter, filter_len);
}
if (nrhs >= NOF_INPUTS + 2) {
filter_len = mxGetNumberOfElements(TIME_FILTER);
filter = malloc(sizeof(float) * filter_len);
f = (double*) mxGetPr(TIME_FILTER);
for (i=0;i<filter_len;i++) {
filter[i] = (float) f[i];
}
chest_dl_set_filter_time(&chest, filter, filter_len);
}
}
double *inr=(double *)mxGetPr(INPUT);
double *ini=(double *)mxGetPi(INPUT);
@ -124,7 +154,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
outr0 = mxGetPr(plhs[0]);
outi0 = mxGetPi(plhs[0]);
}
if (nlhs == 2) {
if (nlhs >= 2) {
plhs[1] = mxCreateDoubleMatrix(REFSIGNAL_MAX_NUM_SF(cell.nof_prb)*nsubframes, cell.nof_ports, mxCOMPLEX);
outr1 = mxGetPr(plhs[1]);
outi1 = mxGetPi(plhs[1]);
@ -150,6 +180,9 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
mexErrMsgTxt("Error running channel estimator\n");
return;
}
noiseAverage[sf]=chest_dl_get_noise_estimate(&chest);
if (nlhs >= 1) {
for (i=0;i<nof_re;i++) {
*outr0 = (double) crealf(ce[0][i]);
@ -160,12 +193,12 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
outi0++;
}
}
if (nlhs == 2) {
if (nlhs >= 2) {
for (int j=0;j<cell.nof_ports;j++) {
for (i=0;i<REFSIGNAL_NUM_SF(cell.nof_prb,j);i++) {
*outr1 = (double) crealf(chest.pilot_estimates[j][i]);
*outr1 = (double) crealf(chest.pilot_estimates_average[j][i]);
if (outi1) {
*outi1 = (double) cimagf(chest.pilot_estimates[j][i]);
*outi1 = (double) cimagf(chest.pilot_estimates_average[j][i]);
}
outr1++;
outi1++;
@ -173,6 +206,12 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
}
}
}
if (nlhs >= 3) {
plhs[2] = mxCreateDoubleMatrix(1, 1, mxREAL);
outr1 = mxGetPr(plhs[2]);
*outr1 = vec_acc_ff(noiseAverage,10)/10;
}
return;

View File

@ -55,7 +55,7 @@ filesource_t fsrc;
cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
pbch_t pbch;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
void usage(char *prog) {
printf("Usage: %s [vcoe] -i input_file\n", prog);
@ -121,14 +121,14 @@ int base_init() {
exit(-1);
}
fft_buffer = malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
fft_buffer = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!fft_buffer) {
perror("malloc");
return -1;
}
for (i=0;i<cell.nof_ports;i++) {
ce[i] = malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
ce[i] = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!ce[i]) {
perror("malloc");
return -1;
@ -140,7 +140,7 @@ int base_init() {
return -1;
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
@ -174,7 +174,7 @@ void base_free() {
for (i=0;i<cell.nof_ports;i++) {
free(ce[i]);
}
chest_free(&chest);
chest_dl_free(&chest);
lte_fft_free(&fft);
pbch_free(&pbch);
@ -184,6 +184,7 @@ int main(int argc, char **argv) {
uint8_t bch_payload[BCH_PAYLOAD_LEN];
int n;
uint32_t nof_tx_ports, sfn_offset;
cf_t *ce_slot1[MAX_PORTS];
if (argc < 3) {
usage(argv[0]);
@ -199,7 +200,7 @@ int main(int argc, char **argv) {
n = filesource_read(&fsrc, input_buffer, FLEN);
lte_fft_run_slot(&fft, &input_buffer[960], fft_buffer);
lte_fft_run_sf(&fft, input_buffer, fft_buffer);
if (fmatlab) {
fprintf(fmatlab, "outfft=");
@ -210,11 +211,15 @@ int main(int argc, char **argv) {
}
/* Get channel estimates for each port */
chest_ce_slot(&chest, fft_buffer, ce, 1);
chest_dl_estimate(&chest, fft_buffer, ce, 0);
INFO("Decoding PBCH\n", 0);
for (int i=0;i<MAX_PORTS;i++) {
ce_slot1[i] = &ce[i][SLOT_LEN_RE(cell.nof_prb, cell.cp)];
}
n = pbch_decode(&pbch, fft_buffer, ce, bch_payload, &nof_tx_ports, &sfn_offset);
n = pbch_decode(&pbch, &fft_buffer[SLOT_LEN_RE(cell.nof_prb, cell.cp)], ce_slot1, bch_payload, &nof_tx_ports, &sfn_offset);
base_free();
if (n < 0) {

View File

@ -55,7 +55,7 @@ cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
pcfich_t pcfich;
regs_t regs;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
void usage(char *prog) {
printf("Usage: %s [vcoe] -i input_file\n", prog);
@ -129,21 +129,21 @@ int base_init() {
exit(-1);
}
fft_buffer = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
fft_buffer = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!fft_buffer) {
perror("malloc");
return -1;
}
for (i=0;i<MAX_PORTS;i++) {
ce[i] = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
ce[i] = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!ce[i]) {
perror("malloc");
return -1;
}
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
@ -182,7 +182,7 @@ void base_free() {
for (i=0;i<MAX_PORTS;i++) {
free(ce[i]);
}
chest_free(&chest);
chest_dl_free(&chest);
lte_fft_free(&fft);
pcfich_free(&pcfich);
@ -191,7 +191,7 @@ void base_free() {
int main(int argc, char **argv) {
uint32_t cfi, distance;
int i, n;
int n;
if (argc < 3) {
usage(argv[0]);
@ -207,7 +207,7 @@ int main(int argc, char **argv) {
n = filesource_read(&fsrc, input_buffer, flen);
lte_fft_run_slot(&fft, input_buffer, fft_buffer);
lte_fft_run_sf(&fft, input_buffer, fft_buffer);
if (fmatlab) {
fprintf(fmatlab, "infft=");
@ -222,12 +222,7 @@ int main(int argc, char **argv) {
}
/* Get channel estimates for each port */
for (i=0;i<cell.nof_ports;i++) {
chest_ce_slot_port(&chest, fft_buffer, ce[i], 0, i);
if (fmatlab) {
chest_fprint(&chest, fmatlab, 0, i);
}
}
chest_dl_estimate(&chest, fft_buffer, ce, 0);
INFO("Decoding PCFICH\n", 0);

View File

@ -57,7 +57,7 @@ pdcch_t pdcch;
cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
regs_t regs;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
void usage(char *prog) {
printf("Usage: %s [vcfoe] -i input_file\n", prog);
@ -143,21 +143,21 @@ int base_init() {
exit(-1);
}
fft_buffer = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
fft_buffer = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!fft_buffer) {
perror("malloc");
return -1;
}
for (i=0;i<MAX_PORTS;i++) {
ce[i] = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
ce[i] = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!ce[i]) {
perror("malloc");
return -1;
}
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
@ -200,7 +200,7 @@ void base_free() {
for (i=0;i<MAX_PORTS;i++) {
free(ce[i]);
}
chest_free(&chest);
chest_dl_free(&chest);
lte_fft_free(&fft);
pdcch_free(&pdcch);
@ -243,7 +243,7 @@ int main(int argc, char **argv) {
if (nof_frames == 5) {
INFO("Reading %d samples sub-frame %d\n", flen, nof_frames);
lte_fft_run_slot(&fft, input_buffer, fft_buffer);
lte_fft_run_sf(&fft, input_buffer, fft_buffer);
if (fmatlab) {
fprintf(fmatlab, "infft%d=", nof_frames);
@ -258,12 +258,7 @@ int main(int argc, char **argv) {
}
/* Get channel estimates for each port */
for (i=0;i<cell.nof_ports;i++) {
chest_ce_slot_port(&chest, fft_buffer, ce[i], 2*nof_frames, i);
if (fmatlab) {
chest_fprint(&chest, fmatlab, 2*nof_frames, i);
}
}
chest_dl_estimate(&chest, fft_buffer, ce, nof_frames);
uint16_t crc_rem = 0;
for (i=0;i<nof_locations && crc_rem != rnti;i++) {

View File

@ -60,7 +60,7 @@ pdsch_harq_t harq_process;
cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
regs_t regs;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
void usage(char *prog) {
printf("Usage: %s [vcfoe] -i input_file\n", prog);
@ -146,21 +146,21 @@ int base_init() {
exit(-1);
}
fft_buffer = malloc(2 * CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
fft_buffer = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!fft_buffer) {
perror("malloc");
return -1;
}
for (i=0;i<MAX_PORTS;i++) {
ce[i] = malloc(2 * CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
ce[i] = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!ce[i]) {
perror("malloc");
return -1;
}
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
@ -215,7 +215,7 @@ void base_free() {
for (i=0;i<MAX_PORTS;i++) {
free(ce[i]);
}
chest_free(&chest);
chest_dl_free(&chest);
lte_fft_free(&fft);
pdcch_free(&pdcch);
@ -279,15 +279,7 @@ int main(int argc, char **argv) {
}
/* Get channel estimates for each port */
for (i=0;i<cell.nof_ports;i++) {
chest_ce_slot_port(&chest, fft_buffer, ce[i], 2*nof_frames, i);
chest_ce_slot_port(&chest, &fft_buffer[CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB],
&ce[i][CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB], 2*nof_frames+1, i);
if (fmatlab) {
chest_fprint(&chest, fmatlab, 2*nof_frames+1, i);
}
}
chest_dl_estimate(&chest, fft_buffer, ce, nof_frames);
uint16_t crc_rem = 0;
for (i=0;i<nof_locations && crc_rem != rnti;i++) {

View File

@ -56,7 +56,7 @@ cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
phich_t phich;
regs_t regs;
lte_fft_t fft;
chest_t chest;
chest_dl_t chest;
void usage(char *prog) {
printf("Usage: %s [vcoe] -i input_file\n", prog);
@ -152,21 +152,21 @@ int base_init() {
exit(-1);
}
fft_buffer = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
fft_buffer = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!fft_buffer) {
perror("malloc");
return -1;
}
for (i=0;i<MAX_PORTS;i++) {
ce[i] = malloc(CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
ce[i] = malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!ce[i]) {
perror("malloc");
return -1;
}
}
if (chest_init_LTEDL(&chest, cell)) {
if (chest_dl_init(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
return -1;
}
@ -205,7 +205,7 @@ void base_free() {
for (i=0;i<MAX_PORTS;i++) {
free(ce[i]);
}
chest_free(&chest);
chest_dl_free(&chest);
lte_fft_free(&fft);
phich_free(&phich);
@ -214,7 +214,7 @@ void base_free() {
int main(int argc, char **argv) {
uint32_t distance;
int i, n;
int n;
uint32_t ngroup, nseq, max_nseq;
uint8_t ack_rx;
@ -234,7 +234,7 @@ int main(int argc, char **argv) {
n = filesource_read(&fsrc, input_buffer, flen);
lte_fft_run_slot(&fft, input_buffer, fft_buffer);
lte_fft_run_sf(&fft, input_buffer, fft_buffer);
if (fmatlab) {
fprintf(fmatlab, "infft=");
@ -247,12 +247,7 @@ int main(int argc, char **argv) {
}
/* Get channel estimates for each port */
for (i=0;i<cell.nof_ports;i++) {
chest_ce_slot_port(&chest, fft_buffer, ce[i], 0, i);
if (fmatlab) {
chest_fprint(&chest, fmatlab, 0, i);
}
}
chest_dl_estimate(&chest, fft_buffer, ce, 0);
INFO("Decoding PHICH\n", 0);

View File

@ -28,193 +28,53 @@
#include <complex.h>
#include <math.h>
#include <stdlib.h>
#include <strings.h>
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#define TABLE_SIZE 1024
/*************** STATIC FUNCTIONS ***********************/
#ifdef TABLE_SIZE
#define ARG2IDX(arg) ((uint32_t) ((1+(arg)/M_PI)*TABLE_SIZE/2))
#define MYCEXP(arg) q->cexptable[ARG2IDX(arg)]
#else
#define MYCEXP(arg) (cosf(arg) + I*sinf(arg))
#endif
#define MAX_OFFSET 64
int interp_init(interp_t *q, interp_type_t type, uint32_t len, uint32_t M) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && len > 0 && M > 0) {
ret = LIBLTE_ERROR;
q->in_arg = vec_malloc(len * sizeof(float));
if (!q->in_arg) {
goto clean_and_exit;
}
q->in_mag = vec_malloc(len * sizeof(float));
if (!q->in_mag) {
goto clean_and_exit;
}
q->out_arg = vec_malloc((MAX_OFFSET + M * len) * sizeof(float));
if (!q->out_arg) {
goto clean_and_exit;
}
q->out_arg2 = vec_malloc((MAX_OFFSET + M * len) * sizeof(float));
if (!q->out_arg2) {
goto clean_and_exit;
}
q->table_idx = vec_malloc((MAX_OFFSET + M * len) * sizeof(int16_t));
if (!q->table_idx) {
goto clean_and_exit;
}
q->out_mag = vec_malloc((MAX_OFFSET + M * len) * sizeof(float));
if (!q->out_mag) {
goto clean_and_exit;
}
q->out_cexp = vec_malloc((MAX_OFFSET + M * len) * sizeof(cf_t));
if (!q->out_cexp) {
goto clean_and_exit;
}
q->out_prod = vec_malloc((MAX_OFFSET + M * len) * sizeof(cf_t));
if (!q->out_prod) {
goto clean_and_exit;
}
#ifdef TABLE_SIZE
q->cexptable = vec_malloc(TABLE_SIZE * sizeof(cf_t));
uint32_t i;
for (i=0;i<TABLE_SIZE;i++) {
q->cexptable[i] = cexpf(I*M_PI*(2*((float) i/TABLE_SIZE) - 1));
}
#endif
q->M = M;
q->len = len;
ret = LIBLTE_SUCCESS;
}
clean_and_exit:
if (ret == LIBLTE_ERROR) {
interp_free(q);
}
return ret;
}
void interp_free(interp_t *q) {
if (q) {
if (q->in_arg) {
free(q->in_arg);
}
if (q->in_mag) {
free(q->in_mag);
}
if (q->out_arg) {
free(q->out_arg);
}
if (q->out_cexp) {
free(q->out_cexp);
}
if (q->out_mag) {
free(q->out_mag);
}
if (q->out_prod) {
free(q->out_prod);
}
#ifdef TABLE_SIZE
if (q->cexptable) {
free(q->cexptable);
}
#endif
}
}
void interp_run_offset(interp_t *q, cf_t *input, cf_t *output, uint32_t off_st, uint32_t off_end) {
uint32_t i, j, n;
float mag0=0, mag1=0, arg0=0, arg1=0;
float dmag, darg;
uint32_t M = q->M;
uint32_t len1 = q->len-1;
if (off_st + off_end < MAX_OFFSET) {
vec_abs_cf(input, q->in_mag, q->len);
vec_arg_cf(input, q->in_arg, q->len);
mag0 = q->in_mag[0];
mag1 = q->in_mag[1];
arg0 = q->in_arg[0];
arg1 = q->in_arg[1];
dmag=(mag1-mag0)/M;
darg=(arg1-arg0)/M;
for (j=0;j<off_st;j++) {
q->out_mag[j] = mag0 - (j+1)*dmag;
q->out_arg[j] = arg0 - (j+1)*darg;
}
for (i=0;i<len1;i++) {
mag0 = q->in_mag[i];
mag1 = q->in_mag[i+1];
arg0 = q->in_arg[i];
arg1 = q->in_arg[i+1];
dmag=(mag1-mag0)/M;
darg=(arg1-arg0)/M;
for (j=0;j<M;j++) {
q->out_mag[i*M+j+off_st] = mag0 + j*dmag;
q->out_arg[i*M+j+off_st] = arg0 + j*darg;
}
}
if (q->len > 1) {
for (j=0;j<off_end;j++) {
q->out_mag[i*M+j+off_st] = mag1 + j*dmag;
q->out_arg[i*M+j+off_st] = arg1 + j*darg;
}
}
uint32_t len=i*M+j+off_st;
#ifdef TABLE_SIZE
vec_convert_fi(q->out_arg, q->table_idx, (float) TABLE_SIZE/2/M_PI, len);
for (n=0;n<len;n++) {
q->out_cexp[n] = q->cexptable[q->table_idx[n]+TABLE_SIZE/2];
}
#else
for (n=0;n<len;n++) {
q->out_cexp[n] = MYCEXP(q->out_arg[n]);
}
#endif
vec_prod_cfc(q->out_cexp, q->out_mag, output, len);
}
}
void interp_run(interp_t *q, cf_t *input, cf_t *output) {
interp_run_offset(q, input, output, 0, 1);
}
cf_t interp_linear_onesample(cf_t *input) {
cf_t interp_linear_onesample(cf_t input0, cf_t input1) {
float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0;
mag0 = cabsf(input[0]);
mag1 = cabsf(input[1]);
arg0 = cargf(input[0]);
arg1 = cargf(input[1]);
mag0 = cabsf(input0);
mag1 = cabsf(input1);
arg0 = cargf(input0);
arg1 = cargf(input1);
mag = 2*mag1 -mag0;
arg = 2*arg1-arg0;
return mag * cexpf(I * arg);
}
cf_t interp_linear_onesample2(cf_t *input) {
cf_t interp_linear_onesample_cabs(cf_t input0, cf_t input1) {
float re0=0, im0=0, re1=0, im1=0, re=0, im=0;
re0 = crealf(input[0]);
im0 = cimagf(input[1]);
re1 = crealf(input[0]);
im1 = cimagf(input[1]);
re0 = crealf(input0);
im0 = cimagf(input1);
re1 = crealf(input0);
im1 = cimagf(input1);
re = 2*re1-re0;
im = 2*im1-im0;
return (re+im*_Complex_I);
}
/* Performs 1st order integer linear interpolation */
void interp_linear_f(float *input, float *output, uint32_t M, uint32_t len) {
uint32_t i, j;
for (i=0;i<len-1;i++) {
for (j=0;j<M;j++) {
output[i*M+j] = input[i] + j * (input[i+1]-input[i]) / M;
}
}
}
/* Performs 1st order linear interpolation with out-of-bound interpolation */
void interp_linear_offset(cf_t *input, cf_t *output, uint32_t M, uint32_t len, uint32_t off_st, uint32_t off_end) {
void interp_linear_offset_cabs(cf_t *input, cf_t *output,
uint32_t M, uint32_t len,
uint32_t off_st, uint32_t off_end)
{
uint32_t i, j;
float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0;
@ -245,18 +105,108 @@ void interp_linear_offset(cf_t *input, cf_t *output, uint32_t M, uint32_t len, u
}
}
/* Performs 1st order linear interpolation */
void interp_linear_c(cf_t *input, cf_t *output, uint32_t M, uint32_t len) {
interp_linear_offset(input, output, M, len, 0, 1);
int interp_linear_vector_init(interp_linvec_t *q, uint32_t vector_len)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q) {
bzero(q, sizeof(interp_linvec_t));
ret = LIBLTE_SUCCESS;
q->diff_vec = vec_malloc(vector_len * sizeof(cf_t));
if (!q->diff_vec) {
perror("malloc");
return LIBLTE_ERROR;
}
q->vector_len = vector_len;
}
return ret;
}
void interp_linear_vector_free(interp_linvec_t *q) {
if (q->diff_vec) {
free(q->diff_vec);
}
}
/* Performs 1st order uint32_teger linear interpolation */
void interp_linear_f(float *input, float *output, uint32_t M, uint32_t len) {
void interp_linear_vector(interp_linvec_t *q, cf_t *in0, cf_t *in1, cf_t *between, uint32_t M)
{
uint32_t i;
vec_sub_ccc(in1, in0, q->diff_vec, q->vector_len);
vec_sc_prod_cfc(q->diff_vec, (float) 1/M, q->diff_vec, q->vector_len);
vec_sum_ccc(in0, q->diff_vec, between, q->vector_len);
for (i=0;i<M-1;i++) {
vec_sum_ccc(between, q->diff_vec, &between[q->vector_len], q->vector_len);
between += q->vector_len;
}
}
int interp_linear_init(interp_lin_t *q, uint32_t vector_len, uint32_t M)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q) {
bzero(q, sizeof(interp_lin_t));
ret = LIBLTE_SUCCESS;
q->diff_vec = vec_malloc(vector_len * sizeof(cf_t));
if (!q->diff_vec) {
perror("malloc");
return LIBLTE_ERROR;
}
q->diff_vec2 = vec_malloc(M * vector_len * sizeof(cf_t));
if (!q->diff_vec2) {
perror("malloc");
free(q->diff_vec);
return LIBLTE_ERROR;
}
q->ramp = vec_malloc(M * sizeof(float));
if (!q->ramp) {
perror("malloc");
free(q->ramp);
free(q->diff_vec);
return LIBLTE_ERROR;
}
for (int i=0;i<M;i++) {
q->ramp[i] = (float) i;
}
q->vector_len = vector_len;
q->M = M;
}
return ret;
}
void interp_linear_free(interp_lin_t *q) {
if (q->diff_vec) {
free(q->diff_vec);
}
}
void interp_linear_offset(interp_lin_t *q, cf_t *input, cf_t *output,
uint32_t off_st, uint32_t off_end)
{
uint32_t i, j;
for (i=0;i<len-1;i++) {
for (j=0;j<M;j++) {
output[i*M+j] = input[i] + j * (input[i+1]-input[i]) / M;
cf_t diff;
i=0;
for (j=0;j<off_st;j++) {
output[j] = input[i] + (j+1) * (input[i+1]-input[i]) / q->M;
}
vec_sub_ccc(&input[1], input, q->diff_vec, (q->vector_len-1));
vec_sc_prod_cfc(q->diff_vec, (float) 1/q->M, q->diff_vec, q->vector_len-1);
for (i=0;i<q->vector_len-1;i++) {
for (j=0;j<q->M;j++) {
output[i*q->M+j+off_st] = input[i];
q->diff_vec2[i*q->M+j] = q->diff_vec[i];
}
vec_prod_cfc(&q->diff_vec2[i*q->M],q->ramp,&q->diff_vec2[i*q->M],q->M);
}
vec_sum_ccc(&output[off_st], q->diff_vec2, &output[off_st], q->M*(q->vector_len-1));
if (q->vector_len > 1) {
diff = input[q->vector_len-1]-input[q->vector_len-2];
for (j=0;j<off_end;j++) {
output[i*q->M+j+off_st] = input[i] + j * diff / q->M;
}
}
}

View File

@ -31,7 +31,5 @@ TARGET_LINK_LIBRARIES(resample_arb_bench lte_phy)
ADD_TEST(resample resample_arb_test)
ADD_EXECUTABLE(interp_test_volk interp_test_volk.c)
TARGET_LINK_LIBRARIES(interp_test_volk lte_phy)

View File

@ -1,92 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <math.h>
#include <time.h>
#include "liblte/phy/phy.h"
typedef _Complex float cf_t;
int main(int argc, char **argv) {
uint32_t N = 1000; // Number of sinwave samples
interp_t interp;
struct timeval t[3];
if (argc < 3) {
printf("usage: %s upsampling_rate nof_trials\n", argv[0]);
exit(-1);
}
uint32_t M = atoi(argv[1]);
uint32_t nof_trials = atoi(argv[2]);
if (interp_init(&interp, LINEAR, N, M)) {
exit(-1);
}
cf_t *in = vec_malloc(N*sizeof(cf_t));
if(!in) {
perror("malloc");
exit(-1);
}
cf_t *out = vec_malloc(M * N*sizeof(cf_t));
if(!out) {
perror("malloc");
exit(-1);
}
cf_t *out_volk = vec_malloc(M * N*sizeof(cf_t));
if(!out_volk) {
perror("malloc");
exit(-1);
}
srand(time(NULL));
for(uint32_t i=0;i<N;i++) {
//in[i] = sin((float) i*2*M_PI/N)+10*I*cos((float) i*2*M_PI/N);
in[i] = (float) rand()/RAND_MAX-0.5 + ((float) rand()/RAND_MAX - 0.5) * I;
}
uint32_t exec_normal, exec_volk;
float mean_normal=0, mean_volk=0;
bzero(out_volk, M * N*sizeof(cf_t));
bzero(out, M * N*sizeof(cf_t));
for (int n=0;n<nof_trials;n++) {
gettimeofday(&t[1], NULL);
interp_linear_c(in, out, M, N);
gettimeofday(&t[2], NULL);
get_time_interval(t);
exec_normal = t[0].tv_usec;
gettimeofday(&t[1], NULL);
interp_run(&interp, in, out_volk);
gettimeofday(&t[2], NULL);
get_time_interval(t);
exec_volk = t[0].tv_usec;
if (n == 0) {
exec_volk = 0;
}
mean_normal = (mean_normal + exec_normal * n) / (n+1);
mean_volk = (mean_volk + exec_volk * n) / (n+1);
}
// Check interp values
float diff = 0.0;
for(int i=0;i<N*M;i++){
diff += cabsf(out_volk[i] - out[i])/N/M;
}
printf("%d: error=%f Exec: %f - %f\n", M, diff, mean_normal, mean_volk);
free(in);
free(out);
free(out_volk);
interp_free(&interp);
printf("Ok\n");
exit(0);
}

View File

@ -59,7 +59,7 @@ int ue_dl_init(ue_dl_t *q,
fprintf(stderr, "Error initiating FFT\n");
goto clean_exit;
}
if (chest_init_LTEDL(&q->chest, cell)) {
if (chest_dl_init(&q->chest, cell)) {
fprintf(stderr, "Error initiating channel estimator\n");
goto clean_exit;
}
@ -120,7 +120,7 @@ clean_exit:
void ue_dl_free(ue_dl_t *q) {
if (q) {
lte_fft_free(&q->fft);
chest_free(&q->chest);
chest_dl_free(&q->chest);
regs_free(&q->regs);
pbch_free(&q->pbch);
pcfich_free(&q->pcfich);
@ -161,7 +161,7 @@ int ue_dl_decode(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, uint32
gettimeofday(&t[1], NULL);
/* Get channel estimates for each port */
chest_ce_sf(&q->chest, q->sf_symbols, q->ce, sf_idx);
chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, sf_idx);
gettimeofday(&t[2], NULL);
get_time_interval(t);

View File

@ -64,14 +64,14 @@ int ue_mib_init(ue_mib_t * q,
bzero(q, sizeof(ue_mib_t));
q->slot1_symbols = vec_malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!q->slot1_symbols) {
q->sf_symbols = vec_malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!q->sf_symbols) {
perror("malloc");
goto clean_exit;
}
for (int i=0;i<cell.nof_ports;i++) {
q->ce[i] = vec_malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
q->ce[i] = vec_malloc(SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
if (!q->ce[i]) {
perror("malloc");
goto clean_exit;
@ -94,7 +94,7 @@ int ue_mib_init(ue_mib_t * q,
fprintf(stderr, "Error initializing FFT\n");
goto clean_exit;
}
if (chest_init_LTEDL(&q->chest, cell)) {
if (chest_dl_init(&q->chest, cell)) {
fprintf(stderr, "Error initializing reference signal\n");
goto clean_exit;
}
@ -116,8 +116,8 @@ clean_exit:
void ue_mib_free(ue_mib_t * q)
{
if (q->slot1_symbols) {
free(q->slot1_symbols);
if (q->sf_symbols) {
free(q->sf_symbols);
}
for (int i=0;i<MIB_MAX_PORTS;i++) {
if (q->ce[i]) {
@ -125,7 +125,7 @@ void ue_mib_free(ue_mib_t * q)
}
}
sync_free(&q->sfind);
chest_free(&q->chest);
chest_dl_free(&q->chest);
pbch_free(&q->pbch);
lte_fft_free(&q->fft);
}
@ -148,12 +148,13 @@ int ue_mib_decode_aligned_frame(ue_mib_t * q, cf_t *input,
uint8_t bch_payload[BCH_PAYLOAD_LEN], uint32_t *nof_tx_ports, uint32_t *sfn_offset)
{
int ret = LIBLTE_SUCCESS;
cf_t *ce_slot1[MAX_PORTS];
/* Run FFT for the slot symbols */
lte_fft_run_slot(&q->fft, input, q->slot1_symbols);
lte_fft_run_sf(&q->fft, input, q->sf_symbols);
/* Get channel estimates of slot #1 for each port */
ret = chest_ce_slot(&q->chest, q->slot1_symbols, q->ce, 1);
ret = chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, 0);
if (ret < 0) {
return LIBLTE_ERROR;
}
@ -167,8 +168,12 @@ int ue_mib_decode_aligned_frame(ue_mib_t * q, cf_t *input,
ue_mib_reset(q);
}
for (int i=0;i<MAX_PORTS;i++) {
ce_slot1[i] = &q->ce[i][SLOT_LEN_RE(q->chest.cell.nof_prb, q->chest.cell.cp)];
}
/* Decode PBCH */
ret = pbch_decode(&q->pbch, q->slot1_symbols, q->ce, bch_payload, nof_tx_ports, sfn_offset);
ret = pbch_decode(&q->pbch, &q->sf_symbols[SLOT_LEN_RE(q->chest.cell.nof_prb, q->chest.cell.cp)], ce_slot1, bch_payload, nof_tx_ports, sfn_offset);
if (ret < 0) {
fprintf(stderr, "Error decoding PBCH (%d)\n", ret);
} else if (ret == 1) {
@ -246,7 +251,9 @@ int ue_mib_sync_and_decode(ue_mib_t * q,
sync_get_sf_idx(&q->sfind) == 0)
{
INFO("Trying to decode MIB\n",0);
ret = ue_mib_decode_aligned_frame(q, &signal[nf*MIB_FRAME_SIZE_SEARCH+peak_idx], q->bch_payload, &q->nof_tx_ports, &q->sfn_offset);
printf("caution here should pass begining of frame \n");
exit(-1);
ret = ue_mib_decode_aligned_frame(q, &signal[nf*MIB_FRAME_SIZE_SEARCH+peak_idx-960], q->bch_payload, &q->nof_tx_ports, &q->sfn_offset);
counter3++;
} else if ((ret == LIBLTE_SUCCESS && peak_idx != 0) ||
(ret == 1 && nf*MIB_FRAME_SIZE_SEARCH + peak_idx + MIB_FRAME_SIZE_SEARCH/10 > nsamples))

View File

@ -91,7 +91,6 @@ uint32_t conv_cc(cf_t *input, cf_t *filter, cf_t *output, uint32_t input_len, ui
uint32_t i,j;
uint32_t output_len;
output_len=input_len+filter_len-1;
memset(output,0,output_len*sizeof(cf_t));
for (i=0;i<input_len;i++) {
for (j=0;j<filter_len;j++) {
output[i+j]+=input[i]*filter[j];
@ -99,3 +98,41 @@ uint32_t conv_cc(cf_t *input, cf_t *filter, cf_t *output, uint32_t input_len, ui
}
return output_len;
}
/* Centered convolution. Returns the same number of input elements. Equivalent to conv(x,h,'same') in matlab.
* y(n)=sum_i x(n+i-M/2)*h(i) for n=1..N with N input samples and M filter len
*/
uint32_t conv_same_cc(cf_t *input, cf_t *filter, cf_t *output, uint32_t input_len, uint32_t filter_len) {
uint32_t i;
uint32_t M = filter_len;
uint32_t N = input_len;
for (i=0;i<M/2;i++) {
output[i]=vec_dot_prod_ccc(&input[i],&filter[M/2-i],M-M/2+i);
}
for (;i<N-M/2;i++) {
output[i]=vec_dot_prod_ccc(&input[i-M/2],filter,M);
}
for (;i<N;i++) {
output[i]=vec_dot_prod_ccc(&input[i-M/2],filter,N-i+M/2);
}
return N;
}
uint32_t conv_same_cf(cf_t *input, float *filter, cf_t *output,
uint32_t input_len, uint32_t filter_len) {
uint32_t i;
uint32_t M = filter_len;
uint32_t N = input_len;
for (i=0;i<M/2;i++) {
output[i]=vec_dot_prod_cfc(&input[i],&filter[M/2-i],M-M/2+i);
}
for (;i<N-M/2;i++) {
output[i]=vec_dot_prod_cfc(&input[i-M/2],filter,M);
}
for (;i<N;i++) {
output[i]=vec_dot_prod_cfc(&input[i-M/2],filter,N-i+M/2);
}
return N;
}

View File

@ -95,11 +95,23 @@ void vec_sub_fff(float *x, float *y, float *z, uint32_t len) {
#endif
}
void vec_sum_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
void vec_sub_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
return vec_sub_fff((float*) x,(float*) y,(float*) z,2*len);
}
void vec_sum_fff(float *x, float *y, float *z, uint32_t len) {
#ifndef HAVE_VOLK_ADD_FLOAT_FUNCTION
int i;
for (i=0;i<len;i++) {
z[i] = x[i]+y[i];
}
#else
volk_32f_x2_add_32f(z,x,y,len);
#endif
}
void vec_sum_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
vec_sum_fff((float*) x,(float*) y,(float*) z,2*len);
}
void vec_sum_bbb(uint8_t *x, uint8_t *y, uint8_t *z, uint32_t len) {
@ -337,11 +349,6 @@ void vec_div_ccc(cf_t *x, cf_t *y, float *y_mod, cf_t *z, uint32_t len) {
}
}
/** If mod(y)==1, division reduces to conjugate multiplication */
void vec_div_ccc_mod1(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
vec_prod_conj_ccc(x,y,z,len);
}
void vec_div_fff(float *x, float *y, float *z, uint32_t len) {
#ifdef HAVE_VOLK_DIVIDE_FUNCTION
volk_32f_x2_divide_32f(z, x, y, len);
@ -368,6 +375,21 @@ cf_t vec_dot_prod_ccc(cf_t *x, cf_t *y, uint32_t len) {
#endif
}
cf_t vec_dot_prod_cfc(cf_t *x, float *y, uint32_t len) {
#ifdef HAVE_VOLK_DOTPROD_CFC_FUNCTION
cf_t res;
volk_32fc_32f_dot_prod_32fc(&res, x, y, len);
return res;
#else
uint32_t i;
cf_t res = 0;
for (i=0;i<len;i++) {
res += x[i]*y[i];
}
return res;
#endif
}
cf_t vec_dot_prod_conj_ccc(cf_t *x, cf_t *y, uint32_t len) {
#ifdef HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION
cf_t res;

View File

@ -4,7 +4,7 @@
clear
SNR_values_db=15;%linspace(5,20,8);
SNR_values_db=20;%linspace(5,20,8);
Nrealizations=1;
preEVM = zeros(length(SNR_values_db),Nrealizations);
@ -53,7 +53,7 @@ cec2.InterpType = 'Linear'; % Cubic interpolation
cec2.PilotAverage = 'UserDefined'; % Pilot averaging method
cec2.InterpWinSize = 3; % Interpolate up to 3 subframes
% simultaneously
cec2.InterpWindow = 'Centered'; % Interpolation windowing method
cec2.InterpWindow = 'Causal'; % Interpolation windowing method
%% Subframe Resource Grid Size
@ -137,11 +137,13 @@ rxWaveform = lteFadingChannel(cfg,txWaveform);
% Calculate noise gain
N0 = 1/(sqrt(2.0*enb.CellRefP*double(info.Nfft))*SNR);
realNoise(snr_idx)=N0;
% Create additive white Gaussian noise
noise = N0*complex(randn(size(rxWaveform)),randn(size(rxWaveform)));
% Add noise to the received time domain waveform
%rxWaveform = rxWaveform + noise;
rxWaveform = rxWaveform + noise;
%% Synchronization
@ -155,15 +157,16 @@ rxGrid = lteOFDMDemodulate(enb,rxWaveform);
addpath('../../debug/lte/phy/lib/ch_estimation/test')
%% Channel Estimation
[estChannel, noiseEst, avg_ref1, noavg_ref1] = lteDLChannelEstimate2(enb,cec2,rxGrid);
[dumm, refs] = liblte_chest(enb.NCellID,enb.CellRefP,rxGrid);
%estChannel2=reshape(estChannel2,72,[]);
[estChannel2] = lteDLChannelEstimate3(enb,cec2,rxGrid,refs);
[estChannel2, refs, noiseEst2] = liblte_chest(enb.NCellID,enb.CellRefP,rxGrid,[0.15 0.7 0.15],[0.1 0.9]);
estChannel2=reshape(estChannel2,size(estChannel));
noiseEstimation(snr_idx)=noiseEst;
noiseEstimation2(snr_idx)=noiseEst2;
%error(snr_idx,nreal) = mean(mean(abs(avg_ref-transpose(refs)),2));
%% MMSE Equalization
eqGrid_mmse = lteEqualizeMMSE(rxGrid, estChannel, noiseEst);
eqGrid_mmse2 = lteEqualizeMMSE(rxGrid, estChannel2, noiseEst);
eqGrid_mmse2 = lteEqualizeMMSE(rxGrid, estChannel2, noiseEst2);
eqGrid_zf = lteEqualizeZF(rxGrid, estChannel);
eqGrid_zf2 = lteEqualizeZF(rxGrid, estChannel2);
@ -201,7 +204,8 @@ postEVM_zf2(snr_idx,nreal) = postEqualisedEVM_zf2.RMS;
end
end
% plot(SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./realNoise),SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./noiseEstimation),SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./noiseEstimation2))
% legend('real','seu','meu')
plot(SNR_values_db, mean(preEVM,2), ...
SNR_values_db, mean(postEVM_mmse,2), ...
SNR_values_db, mean(postEVM_mmse2,2), ...