Fixed PDSCH UE example. Added ue_dl and ue_sync modules. Fixed other minor bugs

master
ismagom 10 years ago
parent 09243c7996
commit 7372d3a386

@ -77,7 +77,10 @@ IF(CMAKE_COMPILER_IS_GNUCXX)
ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(CMAKE_COMPILER_IS_GNUCC) IF(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -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")
# 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")
IF(NOT WIN32) IF(NOT WIN32)
ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN)
ENDIF(NOT WIN32) ENDIF(NOT WIN32)

@ -25,16 +25,35 @@ FIND_LIBRARY(
) )
# Some functions are not defined in old volk versions # Some functions are not defined in old volk versions
SET(CMAKE_REQUIRED_LIBRARIES volk m) SET(CMAKE_REQUIRED_LIBRARIES ${VOLK_LIBRARIES} m)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_index_max_16u HAVE_VOLK_MAX_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32f_index_max_16u HAVE_VOLK_MAX_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_accumulator_s32f HAVE_VOLK_ACC_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32f_accumulator_s32f HAVE_VOLK_ACC_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32fc_multiply_32fc HAVE_VOLK_MULT_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32fc_multiply_32fc HAVE_VOLK_MULT_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_conjugate_32fc HAVE_VOLK_CONJ_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_conjugate_32fc HAVE_VOLK_CONJ_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_multiply_32fc HAVE_VOLK_MULT2_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_multiply_32fc HAVE_VOLK_MULT2_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_32f_multiply_32fc HAVE_VOLK_MULT_REAL_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_multiply_32f HAVE_VOLK_MULT_FLOAT_FUNCTION)
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_magnitude_32f HAVE_VOLK_MAG_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_magnitude_32f HAVE_VOLK_MAG_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_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)
SET(VOLK_DEFINITIONS "HAVE_VOLK") SET(VOLK_DEFINITIONS "HAVE_VOLK")
IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_DEINTERLEAVE_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DEINTERLEAVE_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_CONVERT_FI_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_CONVERT_FI_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_MAX_FUNCTION}) IF(${HAVE_VOLK_MAX_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_FUNCTION") SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_FUNCTION")
ENDIF() ENDIF()
@ -50,10 +69,27 @@ ENDIF()
IF(${HAVE_VOLK_MULT2_FUNCTION}) IF(${HAVE_VOLK_MULT2_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_FUNCTION") SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT2_FUNCTION")
ENDIF() ENDIF()
IF(${HAVE_VOLK_MULT_FLOAT_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_FLOAT_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_MULT_REAL_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MULT_REAL_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_MAG_FUNCTION}) IF(${HAVE_VOLK_MAG_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAG_FUNCTION") SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAG_FUNCTION")
ENDIF() ENDIF()
IF(${HAVE_VOLK_DIVIDE_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DIVIDE_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_DOTPROD_FC_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_FC_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_DOTPROD_F_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_F_FUNCTION")
ENDIF()
IF(${HAVE_VOLK_ATAN_FUNCTION})
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_ATAN_FUNCTION")
ENDIF()
INCLUDE(FindPackageHandleStandardArgs) INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(VOLK DEFAULT_MSG VOLK_LIBRARIES VOLK_INCLUDE_DIRS) FIND_PACKAGE_HANDLE_STANDARD_ARGS(VOLK DEFAULT_MSG VOLK_LIBRARIES VOLK_INCLUDE_DIRS)
MARK_AS_ADVANCED(VOLK_LIBRARIES VOLK_INCLUDE_DIRS VOLK_DEFINITIONS) MARK_AS_ADVANCED(VOLK_LIBRARIES VOLK_INCLUDE_DIRS VOLK_DEFINITIONS)

@ -31,26 +31,63 @@
extern "C" { extern "C" {
#endif #endif
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/cuhd/cuhd_utils.h" #include "liblte/cuhd/cuhd_utils.h"
LIBLTE_API int cuhd_open(char *args, void **handler); LIBLTE_API int cuhd_open(char *args,
void **handler);
LIBLTE_API int cuhd_close(void *h); LIBLTE_API int cuhd_close(void *h);
LIBLTE_API int cuhd_start_rx_stream(void *h); LIBLTE_API int cuhd_start_rx_stream(void *h);
LIBLTE_API int cuhd_start_rx_stream_nsamples(void *h, int nsamples);
LIBLTE_API int cuhd_start_rx_stream_nsamples(void *h,
uint32_t nsamples);
LIBLTE_API int cuhd_stop_rx_stream(void *h); LIBLTE_API int cuhd_stop_rx_stream(void *h);
LIBLTE_API bool cuhd_rx_wait_lo_locked(void *h); LIBLTE_API bool cuhd_rx_wait_lo_locked(void *h);
LIBLTE_API double cuhd_set_rx_srate(void *h, double freq);
LIBLTE_API double cuhd_set_rx_gain(void *h, double gain); LIBLTE_API double cuhd_set_rx_srate(void *h,
LIBLTE_API double cuhd_set_rx_freq(void *h, double freq); double freq);
LIBLTE_API int cuhd_recv(void *h, void *data, int nsamples, int blocking);
LIBLTE_API double cuhd_set_rx_gain(void *h,
LIBLTE_API double cuhd_set_tx_srate(void *h, double freq); double gain);
LIBLTE_API double cuhd_set_tx_gain(void *h, double gain);
LIBLTE_API double cuhd_set_tx_freq(void *h, double freq); LIBLTE_API double cuhd_set_rx_freq(void *h,
LIBLTE_API int cuhd_send(void *h, void *data, int nsamples, int blocking); double freq);
LIBLTE_API double cuhd_set_rx_freq_offset(void *h,
double freq,
double off);
LIBLTE_API int cuhd_recv(void *h,
void *data,
uint32_t nsamples,
bool blocking);
LIBLTE_API int cuhd_recv_timed(void *h,
void *data,
uint32_t nsamples,
bool blocking,
time_t *secs,
double *frac_secs);
LIBLTE_API double cuhd_set_tx_srate(void *h,
double freq);
LIBLTE_API double cuhd_set_tx_gain(void *h,
double gain);
LIBLTE_API double cuhd_set_tx_freq(void *h,
double freq);
LIBLTE_API int cuhd_send(void *h,
void *data,
uint32_t nsamples,
bool blocking);
#ifdef __cplusplus #ifdef __cplusplus

@ -35,7 +35,8 @@
#include "liblte/cuhd/cuhd.h" #include "liblte/cuhd/cuhd.h"
void my_handler(uhd::msg::type_t type, const std::string &msg){ void my_handler(uhd::msg::type_t type, const std::string & msg)
{
//handle the message... //handle the message...
} }
@ -46,15 +47,17 @@ typedef _Complex float complex_t;
bool isLocked(void *h) bool isLocked(void *h)
{ {
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
std::vector<std::string> mb_sensors = handler->usrp->get_mboard_sensor_names(); std::vector < std::string > mb_sensors =
std::vector<std::string> rx_sensors = handler->usrp->get_rx_sensor_names(0); handler->usrp->get_mboard_sensor_names();
if(std::find(rx_sensors.begin(), rx_sensors.end(), "lo_locked") != rx_sensors.end()) { std::vector < std::string > rx_sensors =
handler->usrp->get_rx_sensor_names(0);
if (std::find(rx_sensors.begin(), rx_sensors.end(), "lo_locked") !=
rx_sensors.end()) {
return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool(); return handler->usrp->get_rx_sensor("lo_locked", 0).to_bool();
} } else if (std::find(mb_sensors.begin(), mb_sensors.end(), "ref_locked") !=
else if(std::find(mb_sensors.begin(), mb_sensors.end(), "ref_locked") != mb_sensors.end()) { mb_sensors.end()) {
return handler->usrp->get_mboard_sensor("ref_locked", 0).to_bool(); return handler->usrp->get_mboard_sensor("ref_locked", 0).to_bool();
} } else {
else {
usleep(500); usleep(500);
return true; return true;
} }
@ -64,15 +67,15 @@ bool cuhd_rx_wait_lo_locked(void *h)
{ {
double report = 0.0; double report = 0.0;
while(isLocked(h) && report < 3.0) while (isLocked(h) && report < 3.0) {
{
report += 0.1; report += 0.1;
usleep(1000); usleep(1000);
} }
return isLocked(h); return isLocked(h);
} }
int cuhd_start_rx_stream(void *h) { int cuhd_start_rx_stream(void *h)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
cmd.time_spec = handler->usrp->get_time_now(); cmd.time_spec = handler->usrp->get_time_now();
@ -81,7 +84,8 @@ int cuhd_start_rx_stream(void *h) {
return 0; return 0;
} }
int cuhd_stop_rx_stream(void *h) { int cuhd_stop_rx_stream(void *h)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
cmd.time_spec = handler->usrp->get_time_now(); cmd.time_spec = handler->usrp->get_time_now();
@ -90,7 +94,8 @@ int cuhd_stop_rx_stream(void *h) {
return 0; return 0;
} }
int cuhd_start_rx_stream_nsamples(void *h, int nsamples) { int cuhd_start_rx_stream_nsamples(void *h, uint32_t nsamples)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE);
cmd.time_spec = handler->usrp->get_time_now(); cmd.time_spec = handler->usrp->get_time_now();
@ -100,13 +105,11 @@ int cuhd_start_rx_stream_nsamples(void *h, int nsamples) {
return 0; return 0;
} }
int cuhd_open(char *args, void **h) { int cuhd_open(char *args, void **h)
{
cuhd_handler *handler = new cuhd_handler(); cuhd_handler *handler = new cuhd_handler();
std::string _args = std::string(args); std::string _args = std::string(args);
handler->usrp = uhd::usrp::multi_usrp::make(_args); handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=30720000");
// Try to set LTE clock
handler->usrp->set_master_clock_rate(30720000);
handler->usrp->set_clock_source("internal"); handler->usrp->set_clock_source("internal");
@ -122,33 +125,44 @@ int cuhd_open(char *args, void **h) {
return 0; return 0;
} }
int cuhd_close(void *h) { int cuhd_close(void *h)
{
cuhd_stop_rx_stream(h); cuhd_stop_rx_stream(h);
/** Something else to close the USRP?? */ /** Something else to close the USRP?? */
return 0; return 0;
} }
double cuhd_set_rx_srate(void *h, double freq) { double cuhd_set_rx_srate(void *h, double freq)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_rx_rate(freq); handler->usrp->set_rx_rate(freq);
double ret = handler->usrp->get_rx_rate(); double ret = handler->usrp->get_rx_rate();
return ret; return ret;
} }
double cuhd_set_rx_gain(void *h, double gain) { double cuhd_set_rx_gain(void *h, double gain)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_rx_gain(gain); handler->usrp->set_rx_gain(gain);
return handler->usrp->get_rx_gain(); return handler->usrp->get_rx_gain();
} }
double cuhd_set_rx_freq(void *h, double freq) { double cuhd_set_rx_freq(void *h, double freq)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_rx_freq(freq); handler->usrp->set_rx_freq(freq);
return handler->usrp->get_rx_freq(); return handler->usrp->get_rx_freq();
} }
int cuhd_recv(void *h, void *data, int nsamples, int blocking) { double cuhd_set_rx_freq_offset(void *h, double freq, double off) {
cuhd_handler* handler = static_cast<cuhd_handler*>(h);
handler->usrp->set_rx_freq(uhd::tune_request_t(freq,off));
return handler->usrp->get_rx_freq();
}
int cuhd_recv(void *h, void *data, uint32_t nsamples, bool blocking)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::rx_metadata_t md; uhd::rx_metadata_t md;
if (blocking) { if (blocking) {
@ -160,6 +174,9 @@ int cuhd_recv(void *h, void *data, int nsamples, int blocking) {
return -1; return -1;
} }
n += p; n += p;
if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
std::cout << "\nError code: " << md.to_pp_string() << "\n\n";
}
} while (n < nsamples); } while (n < nsamples);
return nsamples; return nsamples;
} else { } else {
@ -167,25 +184,64 @@ int cuhd_recv(void *h, void *data, int nsamples, int blocking) {
} }
} }
double cuhd_set_tx_gain(void *h, double gain) { int cuhd_recv_timed(void *h,
void *data,
uint32_t nsamples,
int blocking,
time_t *secs,
double *frac_secs) {
cuhd_handler* handler = static_cast<cuhd_handler*>(h);
uhd::rx_metadata_t md;
*secs = -1;
*frac_secs = -1;
int p;
if (blocking) {
int n=0;
complex_t *data_c = (complex_t*) data;
do {
p=handler->rx_stream->recv(&data_c[n], nsamples-n, md);
if (p == -1) {
return -1;
}
if(*secs < 0){
*secs = md.time_spec.get_full_secs();
*frac_secs = md.time_spec.get_frac_secs();
}
n+=p;
} while(n<nsamples);
return n;
} else {
p = handler->rx_stream->recv(data, nsamples, md, 0.0);
*secs = md.time_spec.get_full_secs();
*frac_secs = md.time_spec.get_frac_secs();
return p;
}
}
double cuhd_set_tx_gain(void *h, double gain)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_tx_gain(gain); handler->usrp->set_tx_gain(gain);
return handler->usrp->get_tx_gain(); return handler->usrp->get_tx_gain();
} }
double cuhd_set_tx_srate(void *h, double freq) { double cuhd_set_tx_srate(void *h, double freq)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_tx_rate(freq); handler->usrp->set_tx_rate(freq);
return handler->usrp->get_tx_rate(); return handler->usrp->get_tx_rate();
} }
double cuhd_set_tx_freq(void *h, double freq) { double cuhd_set_tx_freq(void *h, double freq)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_tx_freq(freq); handler->usrp->set_tx_freq(freq);
return handler->usrp->get_tx_freq(); return handler->usrp->get_tx_freq();
} }
int cuhd_send(void *h, void *data, int nsamples, int blocking) { int cuhd_send(void *h, void *data, uint32_t nsamples, bool blocking)
{
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::tx_metadata_t md; uhd::tx_metadata_t md;
if (blocking) { if (blocking) {

@ -51,7 +51,7 @@ LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND)
# These two can be compiled without UHD or graphics support # These two can be compiled without UHD or graphics support
################################################################# #################################################################
add_executable(pdsch_ue pdsch_ue.c) add_executable(pdsch_ue pdsch_ue.c iodev.c)
target_link_libraries(pdsch_ue lte_phy) target_link_libraries(pdsch_ue lte_phy)
add_executable(pdsch_enodeb pdsch_enodeb.c) add_executable(pdsch_enodeb pdsch_enodeb.c)

@ -189,11 +189,13 @@ void base_init() {
exit(-1); exit(-1);
} }
if (pdsch_init(&pdsch, 1234, cell)) { if (pdsch_init(&pdsch, cell)) {
fprintf(stderr, "Error creating PDSCH object\n"); fprintf(stderr, "Error creating PDSCH object\n");
exit(-1); exit(-1);
} }
pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
exit(-1); exit(-1);
@ -365,7 +367,7 @@ int main(int argc, char **argv) {
} else { } else {
#ifndef DISABLE_UHD #ifndef DISABLE_UHD
vec_sc_prod_cfc(output_buffer, uhd_amp, output_buffer, sf_n_samples); vec_sc_prod_cfc(output_buffer, uhd_amp, output_buffer, sf_n_samples);
cuhd_send(uhd, output_buffer, sf_n_samples, 1); cuhd_send(uhd, output_buffer, sf_n_samples, true);
#endif #endif
} }
nf++; nf++;

@ -37,83 +37,59 @@
#include <signal.h> #include <signal.h>
#include "liblte/phy/phy.h" #include "liblte/phy/phy.h"
#include "iodev.h"
#ifndef DISABLE_UHD
#include "liblte/cuhd/cuhd.h"
void *uhd;
#endif
#ifndef DISABLE_GRAPHICS #ifndef DISABLE_GRAPHICS
#include "liblte/graphics/plot.h" void init_plots();
plot_real_t poutfft; void do_plots(ue_dl_t *q, uint32_t sf_idx);
plot_complex_t pce;
plot_scatter_t pscatrecv, pscatequal;
#endif #endif
#define MHZ 1000000
#define SAMP_FREQ 1920000
#define NOF_PORTS 2
float find_threshold = 9.0;
int nof_frames = -1;
int pkt_errors = 0, pkts_total = 0;
int frame_cnt;
char *input_file_name = NULL;
int disable_plots = 0;
/* These are the number of PRBs used during the SYNC procedure */
int sampling_nof_prb = 6;
/* Number of samples in a subframe */
int sf_n_samples;
lte_cell_t cell;
uint32_t cell_id_file = 1;
int cell_id_initated = 0, mib_initiated = 0;
int frame_number;
bool pbch_only = false;
int go_exit = 0; int go_exit = 0;
float uhd_freq = 2600000000.0, uhd_gain = 20.0; /* Local function definitions */
char *uhd_args = ""; void init_plots();
filesource_t fsrc; /**********************************************************************
cf_t *input_buffer, *sf_buffer, *fft_buffer, *input_decim_buffer, *ce[MAX_PORTS]; * Program arguments processing
float *tmp_plot; ***********************************************************************/
pbch_t pbch; typedef struct {
pcfich_t pcfich; uint32_t cell_id_file;
pdcch_t pdcch; uint32_t nof_prb_file;
pdsch_t pdsch; uint16_t rnti;
pdsch_harq_t harq_process; int nof_subframes;
regs_t regs; bool disable_plots;
lte_fft_t fft; bool pbch_only;
chest_t chest; iodev_cfg_t io_config;
sync_frame_t sframe; }prog_args_t;
#define CLRSTDOUT printf("\r\n"); fflush(stdout); printf("\r\n") void args_default(prog_args_t *args) {
args->cell_id_file = 1;
#define DOWNSAMPLE_FACTOR(x, y) lte_symbol_sz(x) / lte_symbol_sz(y) args->nof_prb_file = 6;
args->rnti = SIRNTI;
void usage(char *prog) { args->nof_subframes = -1;
printf("Usage: %s [icagfndvtpb]\n", prog); args->disable_plots = false;
printf("\t-i input_file [Default use USRP]\n"); args->pbch_only = false;
printf("\t-c cell_id if reading from file [Default %d]\n", cell_id_file); args->io_config.find_threshold = -1.0;
args->io_config.input_file_name = NULL;
args->io_config.uhd_args = "";
args->io_config.uhd_freq = -1.0;
args->io_config.uhd_gain = 20.0;
}
void usage(prog_args_t *args, char *prog) {
printf("Usage: %s [cargfndvtb] [-i input_file | -f rx_frequency (in Hz)]\n", prog);
printf("\t-c cell_id if reading from file [Default %d]\n", args->cell_id_file);
printf("\t-p nof_prb if reading from file [Default %d]\n", args->nof_prb_file);
printf("\t-r RNTI to look for [Default 0x%x]\n", args->rnti);
#ifndef DISABLE_UHD #ifndef DISABLE_UHD
printf("\t-a UHD args [Default %s]\n", uhd_args); printf("\t-a UHD args [Default %s]\n", args->io_config.uhd_args);
printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); printf("\t-g UHD RX gain [Default %.2f dB]\n", args->io_config.uhd_gain);
printf("\t-f UHD RX frequency [Default %.1f MHz]\n", uhd_freq / 1000000);
#else #else
printf("\t UHD is disabled. CUHD library not available\n"); printf("\t UHD is disabled. CUHD library not available\n");
#endif #endif
printf("\t-b Decode PBCH only [Default All]\n"); printf("\t-b Decode PBCH only [Default All channels]\n");
printf("\t-p sampling_nof_prb [Default %d]\n", sampling_nof_prb); printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
printf("\t-n nof_frames [Default %d]\n", nof_frames); printf("\t-t PSS threshold [Default %f]\n", args->io_config.find_threshold);
printf("\t-t PSS threshold [Default %f]\n", find_threshold);
#ifndef DISABLE_GRAPHICS #ifndef DISABLE_GRAPHICS
printf("\t-d disable plots [Default enabled]\n"); printf("\t-d disable plots [Default enabled]\n");
#else #else
@ -122,585 +98,219 @@ void usage(char *prog) {
printf("\t-v [set verbose to debug, default none]\n"); printf("\t-v [set verbose to debug, default none]\n");
} }
void parse_args(int argc, char **argv) { void parse_args(prog_args_t *args, int argc, char **argv) {
int opt; int opt;
while ((opt = getopt(argc, argv, "icagfndvtpb")) != -1) { args_default(args);
while ((opt = getopt(argc, argv, "icagfndvtbp")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
input_file_name = argv[optind]; args->io_config.input_file_name = argv[optind];
break; break;
case 'c': case 'c':
cell_id_file = atoi(argv[optind]); args->cell_id_file = atoi(argv[optind]);
break;
case 'p':
args->nof_prb_file = atoi(argv[optind]);
break; break;
case 'a': case 'a':
uhd_args = argv[optind]; args->io_config.uhd_args = argv[optind];
break; break;
case 'g': case 'g':
uhd_gain = atof(argv[optind]); args->io_config.uhd_gain = atof(argv[optind]);
break; break;
case 'f': case 'f':
uhd_freq = atof(argv[optind]); args->io_config.uhd_freq = atof(argv[optind]);
break; break;
case 'b': case 'b':
pbch_only = true; args->pbch_only = true;
break; break;
case 't': case 't':
find_threshold = atof(argv[optind]); args->io_config.find_threshold = atof(argv[optind]);
break;
case 'p':
sampling_nof_prb = atof(argv[optind]);
break; break;
case 'n': case 'n':
nof_frames = atoi(argv[optind]); args->nof_subframes = atoi(argv[optind]);
break; break;
case 'd': case 'd':
disable_plots = 1; args->disable_plots = true;
break; break;
case 'v': case 'v':
verbose++; verbose++;
break; break;
default: default:
usage(argv[0]); usage(args, argv[0]);
exit(-1); exit(-1);
} }
} }
if (args->io_config.uhd_freq < 0 && args->io_config.input_file_name == NULL) {
usage(args, argv[0]);
} }
#ifndef DISABLE_GRAPHICS
void init_plots() {
plot_init();
plot_real_init(&poutfft);
plot_real_setTitle(&poutfft, "Output FFT - Magnitude");
plot_real_setLabels(&poutfft, "Index", "dB");
plot_real_setYAxisScale(&poutfft, -60, 0);
plot_real_setXAxisScale(&poutfft, 1, 504);
plot_complex_init(&pce);
plot_complex_setTitle(&pce, "Channel Estimates");
plot_complex_setYAxisScale(&pce, Ip, -0.01, 0.01);
plot_complex_setYAxisScale(&pce, Q, -0.01, 0.01);
plot_complex_setYAxisScale(&pce, Magnitude, 0, 0.01);
plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI);
plot_scatter_init(&pscatrecv);
plot_scatter_setTitle(&pscatrecv, "Received Symbols");
plot_scatter_setXAxisScale(&pscatrecv, -0.01, 0.01);
plot_scatter_setYAxisScale(&pscatrecv, -0.01, 0.01);
plot_scatter_init(&pscatequal);
plot_scatter_setTitle(&pscatequal, "Equalized Symbols");
plot_scatter_setXAxisScale(&pscatequal, -1, 1);
plot_scatter_setYAxisScale(&pscatequal, -1, 1);
}
#endif
/* This function initializes the objects defined as global variables */
int base_init(int nof_prb) {
int i;
int sf_n_re = 2 * CPNORM_NSYMB * nof_prb * RE_X_RB;
int sf_n_samples = 2 * SLOT_LEN_CPNORM(lte_symbol_sz(nof_prb));
#ifndef DISABLE_GRAPHICS
if (!disable_plots) {
tmp_plot = malloc(sizeof(cf_t) * sf_n_re);
if (!tmp_plot) {
perror("malloc");
return -1;
}
init_plots();
}
#else
printf("-- PLOTS are disabled. Graphics library not available --\n\n");
#endif
if (input_file_name) {
if (filesource_init(&fsrc, input_file_name, COMPLEX_FLOAT_BIN)) {
return -1;
}
} else {
/* open UHD device */
#ifndef DISABLE_UHD
printf("Opening UHD device...\n");
if (cuhd_open(uhd_args, &uhd)) {
fprintf(stderr, "Error opening uhd\n");
return -1;
}
#else
printf("Error UHD not available. Select an input file\n");
return -1;
#endif
} }
/**********************************************************************/
/* For the input buffer, we allocate space for 1 ms of samples */ void sigintHandler(int x) {
input_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t)); go_exit = 1;
if (!input_buffer) {
perror("malloc");
return -1;
} }
input_decim_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t)); int main(int argc, char **argv) {
if (!input_decim_buffer) { int ret;
perror("malloc"); cf_t *sf_buffer;
return -1; iodev_t iodev;
} prog_args_t prog_args;
lte_cell_t cell;
ue_dl_t ue_dl;
bool ue_dl_initiated = false;
int64_t sf_cnt;
uint32_t sf_idx;
pbch_mib_t mib;
/* This buffer is the aligned version of input_buffer */ parse_args(&prog_args, argc, argv);
sf_buffer = (cf_t*) malloc(sf_n_samples * sizeof(cf_t));
if (!sf_buffer) {
perror("malloc");
return -1;
}
/* For the rest of the buffers, we allocate for the number of RE in one subframe */ if (iodev_init(&iodev, &prog_args.io_config)) {
fft_buffer = (cf_t*) malloc(sf_n_re * sizeof(cf_t)); fprintf(stderr, "Error initiating input device\n");
if (!fft_buffer) { exit(-1);
perror("malloc");
return -1;
} }
for (i = 0; i < MAX_PORTS; i++) { if (!prog_args.disable_plots) {
ce[i] = (cf_t*) malloc(sf_n_re * sizeof(cf_t)); init_plots();
if (!ce[i]) {
perror("malloc");
return -1;
}
} }
if (sync_frame_init(&sframe, DOWNSAMPLE_FACTOR(nof_prb,6))) { /* Setup SIGINT handler */
fprintf(stderr, "Error initiating PSS/SSS\n"); printf("\n --- Press Ctrl+C to exit --- \n");
return -1; signal(SIGINT, sigintHandler);
}
if (chest_init(&chest, LINEAR, nof_prb * RE_X_RB, CPNORM_NSYMB, NOF_PORTS)) { /* Initialize frame and subframe counters */
fprintf(stderr, "Error initializing equalizer\n"); sf_cnt = 0;
return -1; sf_idx = 0;
}
if (lte_fft_init(&fft, CPNORM, nof_prb)) { /* Main loop */
fprintf(stderr, "Error initializing FFT\n"); while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
return -1;
}
return 0; ret = iodev_receive(&iodev, &sf_buffer);
if (ret < 0) {
fprintf(stderr, "Error reading from input device (%d)\n", ret);
break;
} }
void base_free() { /* iodev_receive returns 1 if successfully read 1 aligned subframe */
int i; if (ret == 1) {
if (!ue_dl_initiated) {
if (input_file_name) { if (iodev_isUSRP(&iodev)) {
filesource_free(&fsrc); cell = ue_sync_get_cell(&iodev.sframe);
mib = ue_sync_get_mib(&iodev.sframe);
} else { } else {
#ifndef DISABLE_UHD cell.id = prog_args.cell_id_file;
cuhd_close(uhd); cell.cp = CPNORM;
#endif cell.nof_ports = 1; // TODO: Use prog_args
} cell.nof_prb = prog_args.nof_prb_file;
mib.phich_resources = R_1;
#ifndef DISABLE_GRAPHICS mib.phich_length = PHICH_NORM;
if (!disable_plots) {
if (tmp_plot) {
free(tmp_plot);
}
plot_exit();
}
#endif
pbch_free(&pbch);
pdsch_free(&pdsch);
pdcch_free(&pdcch);
regs_free(&regs);
sync_frame_free(&sframe);
lte_fft_free(&fft);
chest_free(&chest);
free(input_buffer);
free(input_decim_buffer);
free(fft_buffer);
for (i = 0; i < MAX_PORTS; i++) {
free(ce[i]);
} }
if (ue_dl_init(&ue_dl, cell, mib.phich_resources, mib.phich_length, 1234)) {
fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1);
} }
pdsch_set_rnti(&ue_dl.pdsch, prog_args.rnti);
int mib_init(phich_resources_t phich_resources, phich_length_t phich_length) { ue_dl_initiated = true;
} else {
if (!lte_cell_isvalid(&cell)) { if (iodev_isUSRP(&iodev)) {
fprintf(stderr, "Invalid cell properties: Id=%d, Ports=%d, PRBs=%d\n", sf_idx = ue_sync_get_sfidx(&iodev.sframe);
cell.id, cell.nof_ports, cell.nof_prb);
return -1;
} }
if (cell.nof_prb > sampling_nof_prb) { if (ue_dl_process(&ue_dl, sf_buffer, sf_idx, ue_sync_get_mib(&iodev.sframe).sfn, prog_args.rnti)) {
fprintf(stderr, "Error sampling frequency is %.2f Mhz but captured signal has %d PRB\n", fprintf(stderr, "\nError running receiver\n");fflush(stdout);
(float) lte_sampling_freq_hz(sampling_nof_prb)/MHZ, cell.nof_prb); exit(-1);
return -1;
} }
if (regs_init(&regs, phich_resources, phich_length, cell)) { if (!(sf_cnt % 10)) {
fprintf(stderr, "Error initiating REGs\n"); printf("Cell ID: %3d, CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Errors: %4d/%4d/%d, BLER: %.1e\r",
return -1; cell.id, iodev.sframe.cur_cfo * 15, iodev.sframe.mean_time_offset / 5, iodev.sframe.peak_idx,
(int) ue_dl.pkt_errors, (int) ue_dl.pkts_total, (int) ue_dl.nof_trials, (float) ue_dl.pkt_errors / ue_dl.pkts_total);
fflush(stdout);
if (VERBOSE_ISINFO()) {
printf("\n");
} }
if (pcfich_init(&pcfich, &regs, cell)) {
fprintf(stderr, "Error creating PCFICH object\n");
return -1;
} }
if (!prog_args.disable_plots && sf_idx == 5) {
if (pdcch_init(&pdcch, &regs, cell)) { do_plots(&ue_dl, sf_idx);
fprintf(stderr, "Error creating PDCCH object\n");
return -1;
} }
if (pdsch_init(&pdsch, 1234, cell)) {
fprintf(stderr, "Error creating PDSCH object\n");
return -1;
} }
if (iodev_isfile(&iodev)) {
if (pdsch_harq_init(&harq_process, &pdsch)) { sf_idx++;
fprintf(stderr, "Error initiating HARQ process\n"); if (sf_idx == NSUBFRAMES_X_FRAME) {
return -1; sf_idx = 0;
} }
chest_set_nof_ports(&chest, cell.nof_ports);
mib_initiated = 1;
DEBUG("Receiver initiated cell.id=%d nof_prb=%d\n", cell.id, cell.nof_prb);
return 0;
} }
int cell_id_init(int nof_prb, int cell_id) {
lte_cell_t cell;
cell.id = cell_id;
cell.nof_prb = nof_prb;
cell.nof_ports = 2;
cell.cp = CPNORM;
if (chest_ref_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing reference signal\n");
return -1;
} }
if (prog_args.nof_subframes > 0) {
if (pbch_init(&pbch, cell)) { sf_cnt++;
fprintf(stderr, "Error initiating PBCH\n");
return -1;
} }
if (iodev_isfile(&iodev)) {
cell_id_initated = 1; usleep(5000);
DEBUG("PBCH initiated cell_id=%d\n", cell_id);
return 0;
} }
char data[10000];
int pdsch_run(cf_t *input, uint32_t sf_idx) {
uint32_t cfi, cfi_distance, i;
cf_t *input_decim;
ra_pdsch_t ra_dl;
dci_location_t locations[10];
dci_msg_t dci_msg;
uint32_t nof_locations;
/* Downsample if the signal bandwith is shorter */
if (sampling_nof_prb > cell.nof_prb) {
decim_c(input, input_decim_buffer, sf_n_samples, DOWNSAMPLE_FACTOR(sampling_nof_prb, cell.nof_prb));
input_decim = input_decim_buffer;
} else {
input_decim = input;
} }
lte_fft_run_sf(&fft, input_decim, fft_buffer); if (ue_dl_initiated) {
ue_dl_free(&ue_dl);
/* Get channel estimates for each port */
chest_ce_sf(&chest, fft_buffer, ce, sf_idx);
/* First decode PCFICH and obtain CFI */
if (pcfich_decode(&pcfich, fft_buffer, ce, sf_idx, &cfi, &cfi_distance)<0) {
fprintf(stderr, "Error decoding PCFICH\n");
return -1;
} }
iodev_free(&iodev);
INFO("Decoded CFI=%d with distance %d\n", cfi, cfi_distance); printf("\nBye\n");
exit(0);
if (regs_set_cfi(&regs, cfi)) {
fprintf(stderr, "Error setting CFI\n");
return -1;
}
/* Search only UE-specific locations */
nof_locations = pdcch_ue_locations(&pdcch, locations, 10, sf_idx, cfi, 1234);
uint16_t crc_rem = 0;
for (i=0;i<nof_locations && crc_rem != 1234;i++) {
if (pdcch_extract_llr(&pdcch, fft_buffer, ce, locations[i], sf_idx, cfi)) {
fprintf(stderr, "Error extracting LLRs\n");
return -1;
}
if (pdcch_decode_msg(&pdcch, &dci_msg, Format1, &crc_rem)) {
fprintf(stderr, "Error decoding DCI msg\n");
return -1;
}
} }
if (crc_rem == 1234) {
if (dci_msg_to_ra_dl(&dci_msg, 1234, 1234, cell, cfi, &ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
return -1;
}
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
return -1;
}
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, &harq_process, ra_dl.rv_idx)) {
pkt_errors++;
}
pkts_total++;
}
/**********************************************************************
* Plotting Functions
***********************************************************************/
#ifndef DISABLE_GRAPHICS #ifndef DISABLE_GRAPHICS
if (!disable_plots && crc_rem == 1234) {
int n_re = 2 * RE_X_RB * CPNORM_NSYMB * cell.nof_prb;
for (i = 0; i < n_re; i++) {
tmp_plot[i] = 10 * log10f(cabsf(fft_buffer[i]));
if (isinf(tmp_plot[i])) {
tmp_plot[i] = -80;
}
}
plot_real_setNewData(&poutfft, tmp_plot, n_re);
plot_complex_setNewData(&pce, ce[0], n_re);
plot_scatter_setNewData(&pscatrecv, pdsch.pdsch_symbols[0], ra_dl.prb_alloc.re_sf[sf_idx]);
plot_scatter_setNewData(&pscatequal, pdsch.pdsch_d, ra_dl.prb_alloc.re_sf[sf_idx]);
}
#endif
return 0;
}
int mib_decoder_run(cf_t *input, pbch_mib_t *mib) {
lte_fft_run_sf(&fft, input, fft_buffer);
/* Get channel estimates for each port */ #include "liblte/graphics/plot.h"
chest_ce_sf(&chest, fft_buffer, ce, 0); plot_real_t poutfft;
plot_complex_t pce;
plot_scatter_t pscatrecv, pscatequal;
DEBUG("Decoding PBCH\n", 0); float tmp_plot[SLOT_LEN_RE(MAX_PRB, CPNORM)];
return pbch_decode(&pbch, fft_buffer, ce, mib);
}
int run_receiver(cf_t *input, uint32_t cell_id, uint32_t sf_idx) { void init_plots() {
pbch_mib_t mib; plot_init();
plot_real_init(&poutfft);
plot_real_setTitle(&poutfft, "Output FFT - Magnitude");
plot_real_setLabels(&poutfft, "Index", "dB");
plot_real_setYAxisScale(&poutfft, -60, 0);
if (!cell_id_initated) { plot_complex_init(&pce);
cell_id_init(sampling_nof_prb, cell_id); plot_complex_setTitle(&pce, "Channel Estimates");
} plot_complex_setYAxisScale(&pce, Ip, -0.01, 0.01);
if (!cell.nof_prb || pbch_only) { plot_complex_setYAxisScale(&pce, Q, -0.01, 0.01);
plot_complex_setYAxisScale(&pce, Magnitude, 0, 0.01);
plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI);
if (!sf_idx) { plot_scatter_init(&pscatrecv);
if (mib_decoder_run(input, &mib)) { plot_scatter_setTitle(&pscatrecv, "Received Symbols");
INFO("MIB decoded!\n", 0); plot_scatter_setXAxisScale(&pscatrecv, -0.01, 0.01);
cell.id = cell_id; plot_scatter_setYAxisScale(&pscatrecv, -0.01, 0.01);
cell.cp = CPNORM;
cell.nof_ports = mib.nof_ports;
cell.nof_prb = mib.nof_prb;
frame_number = mib.sfn;
if (!mib_initiated) { plot_scatter_init(&pscatequal);
if (mib_init(mib.phich_resources, mib.phich_length)) { plot_scatter_setTitle(&pscatequal, "Equalized Symbols");
return -1; plot_scatter_setXAxisScale(&pscatequal, -1, 1);
} plot_scatter_setYAxisScale(&pscatequal, -1, 1);
}
if (VERBOSE_ISINFO() || !frame_cnt) {
CLRSTDOUT;
printf(" - Phy. CellId:\t %d\n", cell_id);
pbch_mib_fprint(stdout, &mib);
}
} else if (pbch_only) {
pkt_errors++;
} }
if (pbch_only) {
#ifndef DISABLE_GRAPHICS void do_plots(ue_dl_t *q, uint32_t sf_idx) {
if (!disable_plots) {
int i; int i;
int n_re = 2 * RE_X_RB * CPNORM_NSYMB * sampling_nof_prb; uint32_t nof_re = SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp);
for (i = 0; i < n_re; i++) { uint32_t nof_symbols = q->harq_process[0].prb_alloc.re_sf[sf_idx];
tmp_plot[i] = 10 * log10f(cabsf(fft_buffer[i])); for (i = 0; i < nof_re; i++) {
tmp_plot[i] = 10 * log10f(cabsf(q->sf_symbols[i]));
if (isinf(tmp_plot[i])) { if (isinf(tmp_plot[i])) {
tmp_plot[i] = -80; tmp_plot[i] = -80;
} }
} }
plot_real_setNewData(&poutfft, tmp_plot, n_re); plot_real_setNewData(&poutfft, tmp_plot, nof_re);
plot_complex_setNewData(&pce, ce[0], n_re); plot_complex_setNewData(&pce, q->ce[0], nof_re);
plot_scatter_setNewData(&pscatrecv, pbch.pbch_symbols[0], pbch.nof_symbols); plot_scatter_setNewData(&pscatrecv, q->pdsch.pdsch_symbols[0], nof_symbols);
plot_scatter_setNewData(&pscatequal, pbch.pbch_d, pbch.nof_symbols); plot_scatter_setNewData(&pscatequal, q->pdsch.pdsch_d, nof_symbols);
} }
#endif
pkts_total++;
}
}
}
if (cell.nof_prb && !pbch_only) {
if (pdsch_run(input, sf_idx)) {
fprintf(stderr, "\nError running PDSCH decoder\n");
return -1;
}
}
return 0;
}
void sigintHandler(int sig_num) {
go_exit = 1;
}
void setup_uhd() {
double samp_freq;
#ifndef DISABLE_UHD
/* Get the sampling rate from the number of PRB */
samp_freq = lte_sampling_freq_hz(sampling_nof_prb);
INFO("Setting sampling frequency %.2f MHz\n", (float) samp_freq/MHZ);
cuhd_set_rx_srate(uhd, samp_freq);
cuhd_set_rx_gain(uhd, uhd_gain);
/* set uhd_freq */
cuhd_set_rx_freq(uhd, (double) uhd_freq);
cuhd_rx_wait_lo_locked(uhd);
DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq);
DEBUG("Starting receiver...\n", 0);
cuhd_start_rx_stream(uhd);
#endif #endif
}
void read_io(cf_t *buffer, int nsamples) {
int n;
DEBUG(" ----- RECEIVING %d SAMPLES ---- \n", nsamples);
if (input_file_name) {
n = filesource_read(&fsrc, buffer, nsamples);
if (n == -1) {
fprintf(stderr, "Error reading file\n");
exit(-1);
/* wrap file if arrive to end */
} else if (n < nsamples) {
DEBUG("Read %d from file. Seeking to 0\n",n);
filesource_seek(&fsrc, 0);
filesource_read(&fsrc, buffer, nsamples);
}
} else {
#ifndef DISABLE_UHD
cuhd_recv(uhd, buffer, nsamples, 1);
#endif
}
}
int main(int argc, char **argv) {
int ret;
uint32_t sf_idx;
uint32_t cell_id;
cf_t *in_ptr;
#ifdef DISABLE_UHD
if (argc < 3) {
usage(argv[0]);
exit(-1);
}
#endif
parse_args(argc, argv);
if (base_init(sampling_nof_prb)) {
fprintf(stderr, "Error initializing memory\n");
exit(-1);
}
/* If input_file_name is NULL, we read from the USRP */
if (!input_file_name) {
setup_uhd();
}
printf("\n --- Press Ctrl+C to exit --- \n");
signal(SIGINT, sigintHandler);
/* Initialize variables */
frame_cnt = 0;
frame_number = -1;
/* The number of samples read from the USRP or file corresponds to 1 ms (subframe) */
sf_n_samples = 1920 * lte_symbol_sz(sampling_nof_prb)/128;
sync_frame_set_threshold(&sframe, find_threshold);
sf_idx = 0;
cell_id = cell_id_file;
if (input_file_name) {
in_ptr = input_buffer;
} else {
in_ptr = sf_buffer;
}
while (!go_exit && (frame_cnt < nof_frames || nof_frames == -1)) {
read_io(input_buffer, sf_n_samples);
if (!input_file_name) {
ret = sync_frame_push(&sframe, input_buffer, sf_buffer);
} else {
ret = 1;
}
switch(ret ) {
case 0:
/* not yet synched */
break;
case 1:
/* sf_buffer is aligned to the subframe */
if (!(frame_cnt%10)) {
frame_number++;
}
if (!input_file_name) {
sf_idx = sync_frame_sfidx(&sframe);
cell_id = sync_frame_cell_id(&sframe);
}
/* synch'd and tracking */
if (run_receiver(in_ptr, cell_id, sf_idx)) {
fprintf(stderr, "\nError running receiver\n");fflush(stdout);
exit(-1);
}
if (!(frame_cnt % 10)) {
printf("SFN: %4d, CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Errors: %4d/%4d, BLER: %.1e\r",
frame_number, sframe.cur_cfo * 15, sframe.timeoffset / 5, sframe.peak_idx,
pkt_errors, pkts_total,
(float) pkt_errors / pkts_total);
fflush(stdout);
}
if (input_file_name) {
sf_idx++;
}
break;
default:
fprintf(stderr, "Error running automatic synchronization\n");
exit(-1);
}
frame_cnt++;
if (input_file_name) {
usleep(5000);
}
}
base_free();
printf("\nBye\n");
exit(0);
}

@ -162,11 +162,11 @@ int base_init(int frame_length) {
return -1; return -1;
} }
} }
if (sync_init(&ssync, FLEN)) { if (sync_init(&ssync, FLEN, 128, 128)) {
fprintf(stderr, "Error initiating PSS/SSS\n"); fprintf(stderr, "Error initiating PSS/SSS\n");
return -1; return -1;
} }
if (chest_init(&chest, LINEAR, CPNORM, 6, MAX_PORTS)) { if (chest_init(&chest, CPNORM, 6, MAX_PORTS)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }
@ -349,12 +349,14 @@ int main(int argc, char **argv) {
int cell_id; int cell_id;
float max_peak_to_avg; float max_peak_to_avg;
float sfo; float sfo;
int find_idx, track_idx, last_found; uint32_t track_idx, find_idx;
int last_found;
enum sync_state state; enum sync_state state;
int n; int n;
int mib_attempts; int mib_attempts;
int nslot; int nslot;
pbch_mib_t mib; pbch_mib_t mib;
int ret;
if (argc < 3) { if (argc < 3) {
usage(argv[0]); usage(argv[0]);
@ -434,14 +436,14 @@ int main(int argc, char **argv) {
cuhd_recv(uhd, input_buffer, FLEN, 1); cuhd_recv(uhd, input_buffer, FLEN, 1);
#endif #endif
/* set find_threshold and go to FIND state */ /* set find_threshold and go to FIND state */
sync_set_threshold(&ssync, find_threshold); sync_set_threshold(&ssync, find_threshold, find_threshold/2);
state = FIND; state = FIND;
break; break;
case FIND: case FIND:
/* find peak in all frame */ /* find peak in all frame */
find_idx = sync_find(&ssync, &input_buffer[FLEN]); ret = sync_find(&ssync, &input_buffer[FLEN], &find_idx);
DEBUG("[%3d/%d]: PAR=%.2f\n", freq, nof_bands, sync_get_peak_to_avg(&ssync)); DEBUG("[%3d/%d]: PAR=%.2f\n", freq, nof_bands, sync_get_peak_to_avg(&ssync));
if (find_idx != -1) { if (ret == 1) {
/* if found peak, go to track and set lower threshold */ /* if found peak, go to track and set lower threshold */
frame_cnt = -1; frame_cnt = -1;
last_found = 0; last_found = 0;
@ -462,7 +464,7 @@ int main(int argc, char **argv) {
case TRACK: case TRACK:
INFO("Tracking PSS find_idx %d offset %d\n", find_idx, find_idx - track_len); INFO("Tracking PSS find_idx %d offset %d\n", find_idx, find_idx - track_len);
track_idx = sync_track(&ssync, &input_buffer[FLEN + find_idx - track_len]); ret = sync_track(&ssync, input_buffer, FLEN + find_idx - track_len, &track_idx);
p2a_v[frame_cnt] = sync_get_peak_to_avg(&ssync); p2a_v[frame_cnt] = sync_get_peak_to_avg(&ssync);
/* save cell id for the best peak-to-avg */ /* save cell id for the best peak-to-avg */
@ -470,7 +472,7 @@ int main(int argc, char **argv) {
max_peak_to_avg = p2a_v[frame_cnt]; max_peak_to_avg = p2a_v[frame_cnt];
cell_id = sync_get_cell_id(&ssync); cell_id = sync_get_cell_id(&ssync);
} }
if (track_idx != -1) { if (ret == 1) {
cfo_v[frame_cnt] = sync_get_cfo(&ssync); cfo_v[frame_cnt] = sync_get_cfo(&ssync);
last_found = frame_cnt; last_found = frame_cnt;
find_idx += track_idx - track_len; find_idx += track_idx - track_len;
@ -506,7 +508,7 @@ int main(int argc, char **argv) {
// Correct CFO // Correct CFO
INFO("Correcting CFO=%.4f\n", cfo[freq]); INFO("Correcting CFO=%.4f\n", cfo[freq]);
cfo_correct(&cfocorr, &input_buffer[FLEN], (-cfo[freq])/128); cfo_correct(&cfocorr, &input_buffer[FLEN], &input_buffer[FLEN], (-cfo[freq])/128);
if (nslot == 0) { if (nslot == 0) {
if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) { if (mib_decoder_run(&input_buffer[FLEN+find_idx], &mib)) {

@ -261,10 +261,12 @@ int main(int argc, char **argv) {
sync_t sfind, strack; sync_t sfind, strack;
float max_peak_to_avg; float max_peak_to_avg;
float sfo; float sfo;
int find_idx, track_idx, last_found; uint32_t find_idx, track_idx;
int last_found;
enum sync_state state; enum sync_state state;
int n; int n;
filesink_t fs; filesink_t fs;
int ret;
if (argc < 3) { if (argc < 3) {
usage(argv[0]); usage(argv[0]);
@ -278,13 +280,13 @@ int main(int argc, char **argv) {
exit(-1); exit(-1);
} }
if (sync_init(&sfind, FLEN)) { if (sync_init(&sfind, FLEN, 128, 128)) {
fprintf(stderr, "Error initiating PSS/SSS\n"); fprintf(stderr, "Error initiating PSS/SSS\n");
exit(-1); exit(-1);
} }
sync_pss_det_peak_to_avg(&sfind); sync_pss_det_peak_to_avg(&sfind);
if (sync_init(&strack, track_len)) { if (sync_init(&strack, track_len, 128, 128)) {
fprintf(stderr, "Error initiating PSS/SSS\n"); fprintf(stderr, "Error initiating PSS/SSS\n");
exit(-1); exit(-1);
} }
@ -315,6 +317,9 @@ int main(int argc, char **argv) {
max_peak_to_avg = 0; max_peak_to_avg = 0;
last_found = 0; last_found = 0;
frame_cnt = 0; frame_cnt = 0;
sync_set_threshold(&sfind, find_threshold, track_threshold);
while(freq<nof_bands) { while(freq<nof_bands) {
/* scan only bands above rssi_threshold */ /* scan only bands above rssi_threshold */
if (!IS_SIGNAL(freq)) { if (!IS_SIGNAL(freq)) {
@ -346,19 +351,16 @@ int main(int argc, char **argv) {
/* receive first frame */ /* receive first frame */
cuhd_recv(uhd, input_buffer, FLEN, 1); cuhd_recv(uhd, input_buffer, FLEN, 1);
/* set find_threshold and go to FIND state */
sync_set_threshold(&sfind, find_threshold);
state = FIND; state = FIND;
break; break;
case FIND: case FIND:
/* find peak in all frame */ /* find peak in all frame */
find_idx = sync_find(&sfind, &input_buffer[FLEN]); ret = sync_find(&sfind, &input_buffer[FLEN], &find_idx);
DEBUG("[%3d/%d]: PAR=%.2f\n", freq, nof_bands, sync_get_peak_to_avg(&sfind)); DEBUG("[%3d/%d]: PAR=%.2f\n", freq, nof_bands, sync_get_peak_to_avg(&sfind));
if (find_idx != -1) { if (ret == 1) {
/* if found peak, go to track and set lower threshold */ /* if found peak, go to track and set lower threshold */
frame_cnt = -1; frame_cnt = -1;
last_found = 0; last_found = 0;
sync_set_threshold(&strack, track_threshold);
state = TRACK; state = TRACK;
INFO("[%3d/%d]: EARFCN %d Freq. %.2f MHz PSS found PAR %.2f dB\n", freq, nof_bands, INFO("[%3d/%d]: EARFCN %d Freq. %.2f MHz PSS found PAR %.2f dB\n", freq, nof_bands,
channels[freq].id, channels[freq].fd, channels[freq].id, channels[freq].fd,
@ -380,7 +382,7 @@ int main(int argc, char **argv) {
filesink_write(&fs, &input_buffer[FLEN+find_idx+track_len], track_len); filesink_write(&fs, &input_buffer[FLEN+find_idx+track_len], track_len);
track_idx = sync_find(&strack, &input_buffer[FLEN + find_idx - track_len]); ret = sync_find(&strack, &input_buffer[FLEN + find_idx - track_len], &track_idx);
p2a_v[frame_cnt] = sync_get_peak_to_avg(&strack); p2a_v[frame_cnt] = sync_get_peak_to_avg(&strack);
/* save cell id for the best peak-to-avg */ /* save cell id for the best peak-to-avg */
@ -388,7 +390,7 @@ int main(int argc, char **argv) {
max_peak_to_avg = p2a_v[frame_cnt]; max_peak_to_avg = p2a_v[frame_cnt];
cell_id = sync_get_cell_id(&strack); cell_id = sync_get_cell_id(&strack);
} }
if (track_idx != -1) { if (ret == 1) {
cfo_v[frame_cnt] = sync_get_cfo(&strack); cfo_v[frame_cnt] = sync_get_cfo(&strack);
last_found = frame_cnt; last_found = frame_cnt;
find_idx += track_idx - track_len; find_idx += track_idx - track_len;

@ -112,10 +112,10 @@ int main(int argc, char **argv) {
float mean_value[3]; float mean_value[3];
int frame_cnt; int frame_cnt;
cf_t *input; cf_t *input;
int m0, m1; uint32_t m0, m1;
float m0_value, m1_value; float m0_value, m1_value;
int N_id_2; uint32_t N_id_2;
int sss_idx; uint32_t sss_idx;
struct timeval tdata[3]; struct timeval tdata[3];
int *exec_time; int *exec_time;
@ -173,7 +173,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error initializing N_id_2\n"); fprintf(stderr, "Error initializing N_id_2\n");
exit(-1); exit(-1);
} }
if (sss_synch_init(&sss[N_id_2])) { if (sss_synch_init(&sss[N_id_2], 128)) {
fprintf(stderr, "Error initializing SSS object\n"); fprintf(stderr, "Error initializing SSS object\n");
exit(-1); exit(-1);
} }
@ -196,7 +196,7 @@ int main(int argc, char **argv) {
gettimeofday(&tdata[1], NULL); gettimeofday(&tdata[1], NULL);
if (force_cfo != CFO_AUTO) { if (force_cfo != CFO_AUTO) {
cfo_correct(&cfocorr, input, -force_cfo/128); cfo_correct(&cfocorr, input, input, -force_cfo/128);
} }
if (force_N_id_2 != -1) { if (force_N_id_2 != -1) {

@ -33,19 +33,19 @@
#include <stdio.h> #include <stdio.h>
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/ch_estimation/refsignal.h" #include "liblte/phy/ch_estimation/refsignal.h"
#include "liblte/phy/filter/filter2d.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
typedef _Complex float cf_t; /* this is only a shortcut */ typedef _Complex float cf_t; /* this is only a shortcut */
typedef enum {LINEAR} chest_interp_t;
typedef void (*interpolate_fnc_t) (cf_t *input, typedef void (*interpolate_fnc_t) (cf_t *input,
cf_t *output, cf_t *output,
int M, uint32_t M,
int len, uint32_t len,
int off_st, uint32_t off_st,
int off_end); uint32_t off_end);
/** This is an OFDM channel estimator. /** This is an OFDM channel estimator.
* It works with any reference signal pattern, provided by the object * It works with any reference signal pattern, provided by the object
@ -61,11 +61,12 @@ typedef struct LIBLTE_API {
uint32_t nof_symbols; uint32_t nof_symbols;
refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME]; refsignal_t refsignal[MAX_PORTS][NSLOTS_X_FRAME];
interpolate_fnc_t interp; interp_t interp_time[MAX_PORTS];
interp_t interp_freq[MAX_PORTS];
}chest_t; }chest_t;
LIBLTE_API int chest_init(chest_t *q, LIBLTE_API int chest_init(chest_t *q,
chest_interp_t interp,
uint32_t nof_re, uint32_t nof_re,
uint32_t nof_symbols, uint32_t nof_symbols,
uint32_t nof_ports); uint32_t nof_ports);
@ -76,7 +77,6 @@ LIBLTE_API int chest_set_nof_ports(chest_t *q,
uint32_t nof_ports); uint32_t nof_ports);
LIBLTE_API int chest_init_LTEDL(chest_t *q, LIBLTE_API int chest_init_LTEDL(chest_t *q,
chest_interp_t interp,
lte_cell_t cell); lte_cell_t cell);
LIBLTE_API int chest_ref_LTEDL_slot_port(chest_t *q, LIBLTE_API int chest_ref_LTEDL_slot_port(chest_t *q,

@ -59,6 +59,11 @@ typedef enum {CPNORM, CPEXT} lte_cp_t;
#define MAX_NSYMB 7 #define MAX_NSYMB 7
#define MAX_PRB 110
#define RE_X_RB 12
#define SYMBOL_SZ_MAX 2048
#define CPNORM_NSYMB 7 #define CPNORM_NSYMB 7
#define CPNORM_SF_NSYMB 2*CPNORM_NSYMB #define CPNORM_SF_NSYMB 2*CPNORM_NSYMB
#define CPNORM_0_LEN 160 #define CPNORM_0_LEN 160
@ -79,20 +84,21 @@ typedef enum {CPNORM, CPEXT} lte_cp_t;
#define SLOT_LEN_CPNORM(symbol_sz) (symbol_sz+CP(symbol_sz,CPNORM_0_LEN)+(CPNORM_NSYMB-1)*(symbol_sz+CP(symbol_sz,CPNORM_LEN))) #define SLOT_LEN_CPNORM(symbol_sz) (symbol_sz+CP(symbol_sz,CPNORM_0_LEN)+(CPNORM_NSYMB-1)*(symbol_sz+CP(symbol_sz,CPNORM_LEN)))
#define SLOT_LEN_CPEXT(symbol_sz) (CPEXT_NSYMB*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) #define SLOT_LEN_CPEXT(symbol_sz) (CPEXT_NSYMB*(symbol_sz+CP(symbol_sz, CPEXT_LEN)))
#define SLOT_LEN(symbol_sz, cp) CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz) #define SLOT_LEN(symbol_sz, cp) (CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz))
#define SF_LEN_CPNORM(symbol_sz) 2*SLOT_LEN_CPNORM(symbol_sz) #define SF_LEN_CPNORM(symbol_sz) (2*SLOT_LEN_CPNORM(symbol_sz))
#define SF_LEN_CPEXT(symbol_sz) 2*SLOT_LEN_CPEXT(symbol_sz) #define SF_LEN_CPEXT(symbol_sz) (2*SLOT_LEN_CPEXT(symbol_sz))
#define SF_LEN(symbol_sz, cp) (2*SLOT_LEN(symbol_sz, cp)) #define SF_LEN(symbol_sz, cp) (2*SLOT_LEN(symbol_sz, cp))
#define SF_LEN_MAX SF_LEN(SYMBOL_SZ_MAX, CPNORM)
#define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp))
#define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp))
#define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) #define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN))))
#define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) #define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN)))
#define SAMPLE_IDX(nof_prb, symbol_idx, sample_idx) (symbol_idx*nof_prb*RE_X_RB + sample_idx) #define SAMPLE_IDX(nof_prb, symbol_idx, sample_idx) (symbol_idx*nof_prb*RE_X_RB + sample_idx)
#define MAX_PRB 110
#define RE_X_RB 12
#define RS_VSHIFT(cell_id) (cell_id%6) #define RS_VSHIFT(cell_id) (cell_id%6)
#define GUARD_RE(nof_prb) ((lte_symbol_sz(nof_prb)-nof_prb*RE_X_RB)/2) #define GUARD_RE(nof_prb) ((lte_symbol_sz(nof_prb)-nof_prb*RE_X_RB)/2)
@ -136,8 +142,15 @@ LIBLTE_API enum band_geographical_area {
LIBLTE_API bool lte_cell_isvalid(lte_cell_t *cell); LIBLTE_API bool lte_cell_isvalid(lte_cell_t *cell);
LIBLTE_API bool lte_N_id_2_isvalid(uint32_t N_id_2);
LIBLTE_API bool lte_N_id_1_isvalid(uint32_t N_id_1);
LIBLTE_API bool lte_symbol_sz_isvalid(uint32_t symbol_sz);
LIBLTE_API int lte_symbol_sz(uint32_t nof_prb); LIBLTE_API int lte_symbol_sz(uint32_t nof_prb);
LIBLTE_API int lte_sampling_freq_hz(uint32_t nof_prb); LIBLTE_API int lte_sampling_freq_hz(uint32_t nof_prb);
LIBLTE_API uint32_t lte_re_x_prb(uint32_t ns, LIBLTE_API uint32_t lte_re_x_prb(uint32_t ns,
@ -151,6 +164,8 @@ LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id,
LIBLTE_API int lte_cb_size(uint32_t index); LIBLTE_API int lte_cb_size(uint32_t index);
LIBLTE_API char *lte_cp_string(lte_cp_t cp);
LIBLTE_API char *lte_mod_string(lte_mod_t mod); LIBLTE_API char *lte_mod_string(lte_mod_t mod);
LIBLTE_API uint32_t lte_mod_bits_x_symbol(lte_mod_t mod); LIBLTE_API uint32_t lte_mod_bits_x_symbol(lte_mod_t mod);

@ -44,12 +44,6 @@
typedef _Complex float cf_t; typedef _Complex float cf_t;
#define NOF_COMMON_FORMATS 2
const dci_format_t common_formats[NOF_COMMON_FORMATS] = { Format1A, Format1C };
#define NOF_UE_FORMATS 2
const dci_format_t ue_formats[NOF_UE_FORMATS] = { Format0, Format1 }; // 1A has the same payload as 0
typedef enum LIBLTE_API { typedef enum LIBLTE_API {
SEARCH_UE, SEARCH_COMMON SEARCH_UE, SEARCH_COMMON

@ -43,7 +43,7 @@
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/regs.h"
#define TDEC_ITERATIONS 1 #define TDEC_ITERATIONS 6
typedef _Complex float cf_t; typedef _Complex float cf_t;
@ -73,6 +73,7 @@ typedef struct LIBLTE_API {
lte_cell_t cell; lte_cell_t cell;
uint32_t max_symbols; uint32_t max_symbols;
bool rnti_is_set;
uint16_t rnti; uint16_t rnti;
/* buffers */ /* buffers */
@ -96,11 +97,13 @@ typedef struct LIBLTE_API {
}pdsch_t; }pdsch_t;
LIBLTE_API int pdsch_init(pdsch_t *q, LIBLTE_API int pdsch_init(pdsch_t *q,
uint16_t user_rnti,
lte_cell_t cell); lte_cell_t cell);
LIBLTE_API void pdsch_free(pdsch_t *q); LIBLTE_API void pdsch_free(pdsch_t *q);
LIBLTE_API int pdsch_set_rnti(pdsch_t *q,
uint16_t rnti);
LIBLTE_API int pdsch_harq_init(pdsch_harq_t *p, LIBLTE_API int pdsch_harq_init(pdsch_harq_t *p,
pdsch_t *pdsch); pdsch_t *pdsch);

@ -0,0 +1,168 @@
/**
*
* \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 UE_SYNC_
#define UE_SYNC_
#include <stdbool.h>
#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/phch/pbch.h"
#include "liblte/phy/common/fft.h"
/**************************************************************
*
* This object automatically manages the cell association and
* synchronization procedure. By default, it associates with the
* CELL whose correlation peak to average ratio is the highest.
*
* TODO: Associate with arbitrary CELL ID
*
* The main function is ue_sync_get_buffer(), which returns a pointer
* to the aligned subframe of samples (before FFT). This function
* should be called regularly, returning every 1 ms. It reads from the
* USRP, aligns the samples to the subframe and performs time/freq synch.
*
* The function returns 0 during the cell association procedure, which includes
* PSS/SSS synchronization, MIB decoding from the PBCH and sampling frequency
* adjustment (according to signal bandwidth) and resynchronization.
*
* The function returns 1 when the signal is correctly acquired and the
* returned buffer is aligned with the subframe.
*
*
*************************************************************/
typedef enum LIBLTE_API { SF_FIND, SF_TRACK} ue_sync_state_t;
#define SYNC_PBCH_NOF_PRB 6
#define SYNC_PBCH_NOF_PORTS 2
#define TRACK_MAX_LOST 10
#define PAR_THRESHOLD_FIND 20
#define NOF_MIB_DECODES 10
#define MEASURE_EXEC_TIME
typedef struct LIBLTE_API {
sync_t s;
void *stream;
double (*set_rate_callback)(void*, double);
int (*recv_callback)(void*, void*, uint32_t);
ue_sync_state_t state;
cf_t *input_buffer;
cf_t *sf_symbols;
cf_t *ce[SYNC_PBCH_NOF_PORTS];
/* These count half frames (5ms) */
uint64_t frame_ok_cnt;
uint32_t frame_no_cnt;
uint32_t frame_total_cnt;
/* this is the system frame number (SFN) */
uint32_t frame_number;
lte_cell_t cell;
uint32_t sf_idx;
cfo_t cfocorr;
float cur_cfo;
/* Variables for PBCH decoding */
pbch_mib_t mib;
lte_fft_t fft;
chest_t chest;
pbch_t pbch;
bool pbch_initialized;
uint32_t pbch_decoded;
bool pbch_decode_always;
bool pbch_decoder_enabled;
uint32_t pbch_last_trial;
bool decode_sss_on_track;
uint32_t peak_idx;
int time_offset;
float mean_time_offset;
#ifdef MEASURE_EXEC_TIME
float mean_exec_time;
#endif
} ue_sync_t;
LIBLTE_API int ue_sync_init(ue_sync_t *q,
double (set_rate_callback)(void*, double),
int (recv_callback)(void*, void*, uint32_t),
void *stream_handler);
LIBLTE_API void ue_sync_free(ue_sync_t *q);
LIBLTE_API int ue_sync_get_buffer(ue_sync_t *q,
cf_t **sf_symbols);
LIBLTE_API void ue_sync_reset(ue_sync_t *q);
LIBLTE_API void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled);
LIBLTE_API void ue_sync_pbch_enable(ue_sync_t *q, bool enabled);
LIBLTE_API void ue_sync_pbch_always(ue_sync_t *q, bool enabled);
LIBLTE_API void ue_sync_set_threshold(ue_sync_t *q,
float threshold);
LIBLTE_API ue_sync_state_t ue_sync_get_state(ue_sync_t *q);
LIBLTE_API uint32_t ue_sync_get_sfidx(ue_sync_t *q);
LIBLTE_API uint32_t ue_sync_get_peak_idx(ue_sync_t *q);
LIBLTE_API lte_cell_t ue_sync_get_cell(ue_sync_t *q);
LIBLTE_API pbch_mib_t ue_sync_get_mib(ue_sync_t *q);
LIBLTE_API bool ue_sync_is_mib_decoded(ue_sync_t *q);
LIBLTE_API float ue_sync_get_cfo(ue_sync_t *q);
LIBLTE_API float ue_sync_get_sfo(ue_sync_t *q);
#endif // SYNC_FRAME_

@ -54,6 +54,10 @@
#include "liblte/phy/ch_estimation/chest.h" #include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/ch_estimation/refsignal.h" #include "liblte/phy/ch_estimation/refsignal.h"
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/resampling/decim.h"
#include "liblte/phy/resampling/resample_arb.h"
#include "liblte/phy/channel/ch_awgn.h" #include "liblte/phy/channel/ch_awgn.h"
#include "liblte/phy/fec/viterbi.h" #include "liblte/phy/fec/viterbi.h"
@ -88,18 +92,15 @@
#include "liblte/phy/phch/pbch.h" #include "liblte/phy/phch/pbch.h"
#include "liblte/phy/phch/pcfich.h" #include "liblte/phy/phch/pcfich.h"
#include "liblte/phy/phch/phich.h" #include "liblte/phy/phch/phich.h"
#include "liblte/phy/phch/ue_sync.h"
#include "liblte/phy/phch/ue_dl.h"
#include "liblte/phy/scrambling/scrambling.h" #include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/resampling/decim.h"
#include "liblte/phy/resampling/resample_arb.h"
#include "liblte/phy/sync/pss.h" #include "liblte/phy/sync/pss.h"
#include "liblte/phy/sync/sfo.h" #include "liblte/phy/sync/sfo.h"
#include "liblte/phy/sync/sss.h" #include "liblte/phy/sync/sss.h"
#include "liblte/phy/sync/sync.h" #include "liblte/phy/sync/sync.h"
#include "liblte/phy/sync/sync_frame.h"
#include "liblte/phy/sync/cfo.h" #include "liblte/phy/sync/cfo.h"
#ifdef __cplusplus #ifdef __cplusplus

@ -26,15 +26,73 @@
*/ */
#ifndef INTERP_H #ifndef INTERP_H
#define INTERP_H_ #define INTERP_H
#include <stdint.h>
#include "liblte/config.h" #include "liblte/config.h"
typedef _Complex float cf_t; 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;
LIBLTE_API int interp_init(interp_t *q,
interp_type_t type,
uint32_t len,
uint32_t M);
LIBLTE_API void interp_free(interp_t *q);
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 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(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end); LIBLTE_API void interp_linear_f(float *input,
LIBLTE_API void interp_linear_c(cf_t *input, cf_t *output, int M, int len); float *output,
LIBLTE_API void interp_linear_f(float *input, float *output, int M, int len); uint32_t M,
uint32_t len);
#endif // INTERP_H #endif // INTERP_H

@ -49,10 +49,20 @@ typedef struct LIBLTE_API {
cf_t *cur_cexp; cf_t *cur_cexp;
}cfo_t; }cfo_t;
LIBLTE_API int cfo_init(cfo_t *h, int nsamples); LIBLTE_API int cfo_init(cfo_t *h,
uint32_t nsamples);
LIBLTE_API void cfo_free(cfo_t *h); LIBLTE_API void cfo_free(cfo_t *h);
LIBLTE_API void cfo_set_tol(cfo_t *h, float tol); LIBLTE_API int cfo_realloc(cfo_t *h,
LIBLTE_API void cfo_correct(cfo_t *h, cf_t *x, float freq); uint32_t samples);
LIBLTE_API void cfo_set_tol(cfo_t *h,
float tol);
LIBLTE_API void cfo_correct(cfo_t *h,
cf_t *input,
cf_t *output,
float freq);
#endif // CFO_ #endif // CFO_

@ -42,7 +42,6 @@ typedef _Complex float cf_t; /* this is only a shortcut */
#define DEFAULT_CORRELATION_TH 10000 #define DEFAULT_CORRELATION_TH 10000
#define DEFAULT_NOSYNC_TIMEOUT 5 #define DEFAULT_NOSYNC_TIMEOUT 5
#define PSS_LEN_FREQ 129 // FFT-based convolution removes 1 leaving it in 128
#define PSS_LEN 62 #define PSS_LEN 62
#define PSS_RE 6*12 #define PSS_RE 6*12
@ -67,50 +66,47 @@ typedef struct LIBLTE_API {
conv_fft_cc_t conv_fft; conv_fft_cc_t conv_fft;
#endif #endif
int frame_size; uint32_t frame_size;
int N_id_2; uint32_t N_id_2;
uint32_t fft_size;
cf_t *pss_signal_freq[3]; // One sequence for each N_id_2 cf_t *pss_signal_freq[3]; // One sequence for each N_id_2
cf_t *tmp_input; cf_t *tmp_input;
float *conv_abs; float *conv_abs;
cf_t *conv_output; cf_t *conv_output;
#ifdef ENABLE_HL
cf_t *frame_buffer;
cf_t *tmp_nco;
float current_cfo;
bool cfo_auto;
int nof_nosync_frames;
int nosync_timeout_frames;
float correlation_threshold;
int frame_start_idx;
int fb_wp;
#endif
}pss_synch_t; }pss_synch_t;
typedef enum { PSS_TX, PSS_RX } pss_direction_t; typedef enum { PSS_TX, PSS_RX } pss_direction_t;
/* Basic functionality */ /* Basic functionality */
LIBLTE_API int pss_synch_init(pss_synch_t *q, int frame_size); LIBLTE_API int pss_synch_init_fft(pss_synch_t *q,
uint32_t frame_size,
uint32_t fft_size);
LIBLTE_API int pss_synch_init(pss_synch_t *q,
uint32_t frame_size);
LIBLTE_API void pss_synch_free(pss_synch_t *q); LIBLTE_API void pss_synch_free(pss_synch_t *q);
LIBLTE_API int pss_generate(cf_t *signal, int N_id_2);
LIBLTE_API void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp);
LIBLTE_API int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2); LIBLTE_API int pss_generate(cf_t *signal,
LIBLTE_API int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, float *corr_mean_value); uint32_t N_id_2);
LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv);
LIBLTE_API void pss_put_slot(cf_t *pss_signal,
cf_t *slot,
uint32_t nof_prb,
lte_cp_t cp);
LIBLTE_API int pss_synch_set_N_id_2(pss_synch_t *q,
uint32_t N_id_2);
/* Automatic frame management functions (for periodic calling) */ LIBLTE_API int pss_synch_find_pss(pss_synch_t *q,
LIBLTE_API int pss_synch_periodic(pss_synch_t *q, cf_t *input, cf_t *output, int nsamples); cf_t *input,
LIBLTE_API void pss_synch_set_timeout(pss_synch_t *q, int nof_frames); float *corr_peak_value,
LIBLTE_API void pss_synch_set_threshold(pss_synch_t *q, float threshold); float *corr_mean_value);
LIBLTE_API void pss_synch_set_cfo_mode(pss_synch_t *q, bool cfo_auto);
LIBLTE_API float pss_synch_get_cfo(pss_synch_t *q);
LIBLTE_API int pss_synch_get_frame_start_idx(pss_synch_t *q);
LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q,
cf_t *pss_recv);
/* High-level API */ /* High-level API */

@ -38,17 +38,12 @@
typedef _Complex float cf_t; /* this is only a shortcut */ typedef _Complex float cf_t; /* this is only a shortcut */
/** gives the beginning of the SSS symbol (to be passed to sss_synch_m0m1).
* subframe_sz is the length of the subframe, e.g. 1920 for the 1.9 MHz
* symbol_sz is the OFDM symbol size (including CP), e.g. 137 for the 1.9 MHz
*/
#define SSS_SYMBOL_ST(subframe_sz, symbol_sz) (subframe_sz/2-2*symbol_sz)
#define SSS_POS_SYMBOL 33
#define SSS_DFT_LEN 128
#define N_SSS 31 #define N_SSS 31
#define SSS_LEN 2*N_SSS #define SSS_LEN 2*N_SSS
#define SSS_MAX_FFT_LEN 2048
struct sss_tables{ struct sss_tables{
int z1[N_SSS][N_SSS]; int z1[N_SSS][N_SSS];
int c[2][N_SSS]; int c[2][N_SSS];
@ -56,7 +51,7 @@ struct sss_tables{
}; };
/* Allocate 32 complex to make it multiple of 32-byte AVX instructions alignment requirement. /* Allocate 32 complex to make it multiple of 32-byte AVX instructions alignment requirement.
* Should use vect_malloc() to make it platform agnostic. * Should use vec_malloc() to make it platform agnostic.
*/ */
struct fc_tables{ struct fc_tables{
cf_t z1[N_SSS+1][N_SSS+1]; cf_t z1[N_SSS+1][N_SSS+1];
@ -70,34 +65,67 @@ typedef struct LIBLTE_API {
dft_plan_t dftp_input; dft_plan_t dftp_input;
uint32_t fft_size;
float corr_peak_threshold; float corr_peak_threshold;
int symbol_sz; uint32_t symbol_sz;
int subframe_sz; uint32_t subframe_sz;
int N_id_2; uint32_t N_id_2;
int N_id_1_table[30][30]; uint32_t N_id_1_table[30][30];
struct fc_tables fc_tables[3]; // one for each N_id_2 struct fc_tables fc_tables[3]; // one for each N_id_2
}sss_synch_t; }sss_synch_t;
/* Basic functionality */ /* Basic functionality */
LIBLTE_API int sss_synch_init(sss_synch_t *q); LIBLTE_API int sss_synch_init(sss_synch_t *q,
uint32_t fft_size);
LIBLTE_API int sss_synch_realloc(sss_synch_t *q,
uint32_t fft_size);
LIBLTE_API void sss_synch_free(sss_synch_t *q); LIBLTE_API void sss_synch_free(sss_synch_t *q);
LIBLTE_API void sss_generate(float *signal0, float *signal5, int cell_id);
LIBLTE_API void sss_put_slot(float *sss, cf_t *symbol, int nof_prb, lte_cp_t cp);
LIBLTE_API int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2); LIBLTE_API void sss_generate(float *signal0,
float *signal5,
uint32_t cell_id);
LIBLTE_API void sss_put_slot(float *sss,
cf_t *symbol,
uint32_t nof_prb,
lte_cp_t cp);
LIBLTE_API int sss_synch_set_N_id_2(sss_synch_t *q,
uint32_t N_id_2);
LIBLTE_API int sss_synch_m0m1(sss_synch_t *q,
cf_t *input,
uint32_t *m0,
float *m0_value,
uint32_t *m1,
float *m1_value);
LIBLTE_API uint32_t sss_synch_subframe(uint32_t m0,
uint32_t m1);
LIBLTE_API int sss_synch_N_id_1(sss_synch_t *q,
uint32_t m0,
uint32_t m1);
LIBLTE_API int sss_synch_frame(sss_synch_t *q,
cf_t *input,
uint32_t *subframe_idx,
uint32_t *N_id_1);
LIBLTE_API void sss_synch_set_threshold(sss_synch_t *q,
float threshold);
LIBLTE_API void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, LIBLTE_API void sss_synch_set_symbol_sz(sss_synch_t *q,
int *m1, float *m1_value); uint32_t symbol_sz);
LIBLTE_API int sss_synch_subframe(int m0, int m1);
LIBLTE_API int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1);
LIBLTE_API int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, int *N_id_1); LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q,
LIBLTE_API void sss_synch_set_threshold(sss_synch_t *q, float threshold); uint32_t subframe_sz);
LIBLTE_API void sss_synch_set_symbol_sz(sss_synch_t *q, int symbol_sz);
LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz);
/* High-level API */ /* High-level API */
@ -105,18 +133,18 @@ LIBLTE_API void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz);
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
sss_synch_t obj; sss_synch_t obj;
struct sss_synch_init { struct sss_synch_init {
int N_id_2; uint32_t N_id_2;
} init; } init;
cf_t *input; cf_t *input;
int in_len; uint32_t in_len;
struct sss_synch_ctrl_in { struct sss_synch_ctrl_in {
int symbol_sz; uint32_t symbol_sz;
int subframe_sz; uint32_t subframe_sz;
int correlation_threshold; uint32_t correlation_threshold;
} ctrl_in; } ctrl_in;
struct sss_synch_ctrl_out { struct sss_synch_ctrl_out {
int subframe_idx; uint32_t subframe_idx;
int N_id_1; uint32_t N_id_1;
} ctrl_out; } ctrl_out;
}sss_synch_hl; }sss_synch_hl;

@ -30,11 +30,15 @@
#define SYNC_ #define SYNC_
#include <stdbool.h> #include <stdbool.h>
#include <math.h>
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/phy/sync/pss.h" #include "liblte/phy/sync/pss.h"
#include "liblte/phy/sync/sss.h" #include "liblte/phy/sync/sss.h"
#define FFT_SIZE_MIN 64
#define FFT_SIZE_MAX 2048
/** /**
* *
* This object performs time and frequency synchronization using the PSS and SSS signals. * This object performs time and frequency synchronization using the PSS and SSS signals.
@ -49,19 +53,19 @@
enum sync_pss_det { ABSOLUTE, PEAK_MEAN}; enum sync_pss_det { ABSOLUTE, PEAK_MEAN};
#define TRACK_THRESHOLD 10.0
#define TRACK_LEN 300
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
pss_synch_t pss; pss_synch_t pss_find;
pss_synch_t pss_track; pss_synch_t pss_track;
sss_synch_t sss; sss_synch_t sss;
enum sync_pss_det pss_mode; enum sync_pss_det pss_mode;
float threshold; float find_threshold;
float track_threshold;
float peak_to_avg; float peak_to_avg;
int N_id_2; uint32_t N_id_2;
int N_id_1; uint32_t N_id_1;
int slot_id; uint32_t slot_id;
uint32_t fft_size;
uint32_t find_frame_size;
float cfo; float cfo;
bool detect_cp; bool detect_cp;
bool sss_en; bool sss_en;
@ -69,42 +73,72 @@ typedef struct LIBLTE_API {
}sync_t; }sync_t;
LIBLTE_API int sync_init(sync_t *q, int frame_size); LIBLTE_API int sync_init(sync_t *q,
uint32_t find_frame_size,
uint32_t track_frame_size,
uint32_t fft_size);
LIBLTE_API void sync_free(sync_t *q); LIBLTE_API void sync_free(sync_t *q);
LIBLTE_API int sync_realloc(sync_t *q,
uint32_t find_frame_size,
uint32_t track_frame_size,
uint32_t fft_size);
/* Finds a correlation peak in the input signal. The signal must be sampled at 1.92 MHz and should be /* Finds a correlation peak in the input signal. The signal must be sampled at 1.92 MHz and should be
subframe_size long at least */ subframe_size long at least */
LIBLTE_API int sync_find(sync_t *q, cf_t *input); LIBLTE_API int sync_find(sync_t *q,
cf_t *input,
uint32_t *peak_position);
/* Tracks the correlation peak in the input signal. The signal must be sampled at 1.92 MHz and should be /* Tracks the correlation peak in the input signal. The signal must be sampled at 1.92 MHz and should be
TRACK_LEN long at least */ TRACK_LEN long at least */
LIBLTE_API int sync_track(sync_t *q, cf_t *input); LIBLTE_API int sync_track(sync_t *q,
cf_t *input,
uint32_t offset,
uint32_t *peak_position);
/* Sets the threshold for peak comparison */ /* Sets the threshold for peak comparison */
LIBLTE_API void sync_set_threshold(sync_t *q, float threshold); LIBLTE_API void sync_set_threshold(sync_t *q,
float find_threshold,
float track_threshold);
/* Set peak comparison to absolute value */ /* Set peak comparison to absolute value */
LIBLTE_API void sync_pss_det_absolute(sync_t *q); LIBLTE_API void sync_pss_det_absolute(sync_t *q);
/* Set peak comparison to relative to the mean */ /* Set peak comparison to relative to the mean */
LIBLTE_API void sync_pss_det_peak_to_avg(sync_t *q); LIBLTE_API void sync_pss_det_peak_to_avg(sync_t *q);
/* Gets the slot id (0 or 10) */ /* Gets the slot id (0 or 10) */
LIBLTE_API int sync_get_slot_id(sync_t *q); LIBLTE_API uint32_t sync_get_slot_id(sync_t *q);
/* Gets the last peak-to-average ratio */ /* Gets the last peak-to-average ratio */
LIBLTE_API float sync_get_peak_to_avg(sync_t *q); LIBLTE_API float sync_get_peak_to_avg(sync_t *q);
/* Gets the N_id_2 from the last call to synch_run() */ /* Gets the N_id_2 from the last call to synch_run() */
LIBLTE_API int sync_get_N_id_2(sync_t *q); LIBLTE_API uint32_t sync_get_N_id_2(sync_t *q);
/* Gets the N_id_1 from the last call to synch_run() */ /* Gets the N_id_1 from the last call to synch_run() */
LIBLTE_API int sync_get_N_id_1(sync_t *q); LIBLTE_API uint32_t sync_get_N_id_1(sync_t *q);
/* Gets the Physical CellId from the last call to synch_run() */ /* Gets the Physical CellId from the last call to synch_run() */
LIBLTE_API int sync_get_cell_id(sync_t *q); LIBLTE_API int sync_get_cell_id(sync_t *q);
/* Gets the CFO estimation from the last call to synch_run() */ /* Gets the CFO estimation from the last call to synch_run() */
LIBLTE_API float sync_get_cfo(sync_t *q); LIBLTE_API float sync_get_cfo(sync_t *q);
/* Gets the CP length estimation from the last call to synch_run() */ /* Gets the CP length estimation from the last call to synch_run() */
LIBLTE_API lte_cp_t sync_get_cp(sync_t *q); LIBLTE_API lte_cp_t sync_get_cp(sync_t *q);
/* Enables/Disables SSS detection */ /* Enables/Disables SSS detection */
LIBLTE_API void sync_sss_en(sync_t *q, bool enabled); LIBLTE_API void sync_sss_en(sync_t *q,
bool enabled);
LIBLTE_API bool sync_sss_detected(sync_t *q);
/* Enables/Disables CP detection */ /* Enables/Disables CP detection */
LIBLTE_API void sync_cp_en(sync_t *q, bool enabled); LIBLTE_API void sync_cp_en(sync_t *q,
bool enabled);
#endif // SYNC_ #endif // SYNC_

@ -1,111 +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 SYNC_FRAME_
#define SYNC_FRAME_
#include <stdbool.h>
#include "liblte/config.h"
#include "liblte/phy/sync/sync.h"
#include "liblte/phy/sync/cfo.h"
/**
*
* Uses sync object to automatically manage the FIND and TRACKING states.
* It is suposed to work on a subframe basis. The input signal must be sampled at
* a frequency integer multiple of 1.92 MHz. The signal is internally downsampled
* and fed to the sync object.
*
* This object also deals with frame alignment and CFO correction, returning an
* output signal aligned both in time and frequency.
*/
enum sync_frame_state { SF_FIND, SF_TRACK };
#define SYNC_SF_LEN 1920 // 1ms at 1.92 MHz
#define TRACK_MAX_LOST 10
typedef struct LIBLTE_API {
sync_t s;
enum sync_frame_state state;
uint32_t downsampling;
resample_arb_t resample;
unsigned long frame_cnt;
bool fb_wp;
uint32_t frame_size;
cf_t *input_buffer;
cf_t *input_downsampled;
cfo_t cfocorr;
float cur_cfo;
uint32_t peak_idx;
uint32_t cell_id;
float timeoffset;
uint32_t last_found;
uint32_t sf_idx;
}sync_frame_t;
/* Initializes the automatic tracker, setting the downsampling ratio for the input signal.
* downsampling is the ratio of the provided signal sampling frequency to 1.92 Mhz. E.g. if input is sampled at 3.84 Mhz,
* downsampling should be 2.
*/
LIBLTE_API int sync_frame_init(sync_frame_t *q,
uint32_t downsampling);
LIBLTE_API void sync_frame_free(sync_frame_t *q);
LIBLTE_API void sync_frame_set_threshold(sync_frame_t *q,
float threshold);
LIBLTE_API uint32_t sync_frame_cell_id(sync_frame_t *q);
LIBLTE_API uint32_t sync_frame_sfidx(sync_frame_t *q);
/* Automatically time/freq synchronizes the input signal. Returns 1 if the signal is synched and locked,
* and fills the output buffer with the time and frequency aligned version of the signal.
* If 0 is returned, the PSS was not found. -1 is returned in case of error.
*
* The provided signal can be sampled at an integer multiple of 1.92 Mhz.
* The sampling ratio is provided when calling the sync_auto_reset() function.
*
* The buffer input must have subframe_size samples (used in sync_init)
*/
LIBLTE_API int sync_frame_push(sync_frame_t *q,
cf_t *input,
cf_t *output);
/* Resets the automatic tracker */
LIBLTE_API void sync_frame_reset(sync_frame_t *q);
#endif // SYNC_FRAME_

@ -30,19 +30,28 @@
#define CEXPTAB_ #define CEXPTAB_
#include <complex.h> #include <complex.h>
#include <stdint.h>
#include "liblte/config.h" #include "liblte/config.h"
typedef _Complex float cf_t; typedef _Complex float cf_t;
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
int size; uint32_t size;
cf_t *tab; cf_t *tab;
}cexptab_t; }cexptab_t;
LIBLTE_API int cexptab_init(cexptab_t *nco, int size); LIBLTE_API int cexptab_init(cexptab_t *nco,
uint32_t size);
LIBLTE_API void cexptab_free(cexptab_t *nco); LIBLTE_API void cexptab_free(cexptab_t *nco);
LIBLTE_API void cexptab_gen(cexptab_t *nco, cf_t *x, float freq, int len); LIBLTE_API void cexptab_gen(cexptab_t *nco,
LIBLTE_API void cexptab_gen_direct(cf_t *x, float freq, int len); cf_t *x,
float freq,
uint32_t len);
LIBLTE_API void cexptab_gen_direct(cf_t *x,
float freq,
uint32_t len);
#endif // CEXPTAB_ #endif // CEXPTAB_

@ -32,23 +32,36 @@
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/dft.h"
typedef _Complex float cf_t;
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
_Complex float *input_fft; cf_t *input_fft;
_Complex float *filter_fft; cf_t *filter_fft;
_Complex float *output_fft; cf_t *output_fft;
_Complex float *output_fft2; cf_t *output_fft2;
int input_len; uint32_t input_len;
int filter_len; uint32_t filter_len;
int output_len; uint32_t output_len;
dft_plan_t input_plan; dft_plan_t input_plan;
dft_plan_t filter_plan; dft_plan_t filter_plan;
dft_plan_t output_plan; dft_plan_t output_plan;
}conv_fft_cc_t; }conv_fft_cc_t;
LIBLTE_API int conv_fft_cc_init(conv_fft_cc_t *state, int input_len, int filter_len); LIBLTE_API int conv_fft_cc_init(conv_fft_cc_t *q,
LIBLTE_API void conv_fft_cc_free(conv_fft_cc_t *state); uint32_t input_len,
LIBLTE_API int conv_fft_cc_run(conv_fft_cc_t *state, _Complex float *input, _Complex float *filter, _Complex float *output); uint32_t filter_len);
LIBLTE_API void conv_fft_cc_free(conv_fft_cc_t *q);
LIBLTE_API uint32_t conv_fft_cc_run(conv_fft_cc_t *q,
cf_t *input,
cf_t *filter,
cf_t *output);
LIBLTE_API int conv_cc(_Complex float *input, _Complex float *filter, _Complex float *output, int input_len, int filter_len); LIBLTE_API uint32_t conv_cc(cf_t *input,
cf_t *filter,
cf_t *output,
uint32_t input_len,
uint32_t filter_len);
#endif // CONVOLUTION_H_ #endif // CONVOLUTION_H_

@ -30,51 +30,74 @@
#define VECTOR_ #define VECTOR_
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include "liblte/config.h" #include "liblte/config.h"
typedef _Complex float cf_t; typedef _Complex float cf_t;
/** Return the sum of all the elements */ /** Return the sum of all the elements */
LIBLTE_API int vec_acc_ii(int *x, int len); LIBLTE_API int vec_acc_ii(int *x, uint32_t len);
LIBLTE_API float vec_acc_ff(float *x, int len); LIBLTE_API float vec_acc_ff(float *x, uint32_t len);
LIBLTE_API cf_t vec_acc_cc(cf_t *x, int len); LIBLTE_API cf_t vec_acc_cc(cf_t *x, uint32_t len);
LIBLTE_API void *vec_malloc(int size); LIBLTE_API void *vec_malloc(uint32_t size);
LIBLTE_API void *vec_realloc(void *ptr, uint32_t old_size, uint32_t new_size);
/* print vectors */ /* print vectors */
LIBLTE_API void vec_fprint_c(FILE *stream, cf_t *x, int len); LIBLTE_API void vec_fprint_c(FILE *stream, cf_t *x, uint32_t len);
LIBLTE_API void vec_fprint_f(FILE *stream, float *x, int len); LIBLTE_API void vec_fprint_f(FILE *stream, float *x, uint32_t len);
LIBLTE_API void vec_fprint_b(FILE *stream, char *x, int len); LIBLTE_API void vec_fprint_b(FILE *stream, char *x, uint32_t len);
LIBLTE_API void vec_fprint_i(FILE *stream, int *x, int len); LIBLTE_API void vec_fprint_i(FILE *stream, int *x, uint32_t len);
LIBLTE_API void vec_fprint_hex(FILE *stream, char *x, uint32_t len);
/* Saves a vector to a file */
LIBLTE_API void vec_save_file(char *filename, void *buffer, uint32_t len);
/* sum two vectors */ /* sum two vectors */
LIBLTE_API void vec_sum_ch(char *z, char *x, char *y, int len); LIBLTE_API void vec_sum_ch(char *x, char *y, char *z, uint32_t len);
LIBLTE_API void vec_sum_ccc(cf_t *z, cf_t *x, cf_t *y, int 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);
/* scalar product */ /* scalar product */
LIBLTE_API void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len); LIBLTE_API void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, uint32_t len);
LIBLTE_API void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len); LIBLTE_API void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, uint32_t len);
LIBLTE_API void vec_sc_prod_fff(float *x, float h, float *z, uint32_t len);
LIBLTE_API void vec_convert_fi(float *x, int16_t *z, float scale, uint32_t len);
LIBLTE_API void vec_deinterleave_cf(cf_t *x, float *real, float *imag, uint32_t len);
/* vector product (element-wise) */ /* vector product (element-wise) */
LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, int len); LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
LIBLTE_API void vec_prod_ccc_unalign(cf_t *x, cf_t *y, cf_t *z, int len);
/* vector product (element-wise) */
LIBLTE_API void vec_prod_cfc(cf_t *x, float *y, cf_t *z, uint32_t len);
LIBLTE_API cf_t vec_dot_prod_ccc(cf_t *x, cf_t *y, uint32_t len);
LIBLTE_API float vec_dot_prod_fff(float *x, float *y, uint32_t len);
/* z=x/y vector division (element-wise) */ /* z=x/y vector division (element-wise) */
LIBLTE_API void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, int len); LIBLTE_API void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
/* conjugate */ /* conjugate */
LIBLTE_API void vec_conj_cc(cf_t *x, cf_t *y, int len); LIBLTE_API void vec_conj_cc(cf_t *x, cf_t *y, uint32_t len);
/* average vector power */ /* average vector power */
LIBLTE_API float vec_avg_power_cf(cf_t *x, int len); LIBLTE_API float vec_avg_power_cf(cf_t *x, uint32_t len);
/* return the index of the maximum value in the vector */ /* return the index of the maximum value in the vector */
LIBLTE_API int vec_max_fi(float *x, int len); LIBLTE_API uint32_t vec_max_fi(float *x, uint32_t len);
/* quantify vector of floats and convert to unsigned char */ /* quantify vector of floats and convert to unsigned char */
LIBLTE_API void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, int len); LIBLTE_API void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len);
/* magnitude of each vector element */ /* magnitude of each vector element */
LIBLTE_API void vec_abs_cf(cf_t *x, float *abs, int len); LIBLTE_API void vec_abs_cf(cf_t *x, float *abs, uint32_t len);
/* argument of each vector element */
LIBLTE_API void vec_arg_cf(cf_t *x, float *arg, uint32_t len);
#endif // VECTOR_ #endif // VECTOR_

@ -32,13 +32,14 @@
#include <math.h> #include <math.h>
#include "liblte/phy/ch_estimation/chest.h" #include "liblte/phy/ch_estimation/chest.h"
#include "liblte/phy/resampling/interp.h"
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz) #define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
#define SF_SZ(q) (2 * SLOT_SZ(q)) #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) { void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
chest_ref_fprint(q, stream, nslot, port_id); chest_ref_fprint(q, stream, nslot, port_id);
chest_recvsig_fprint(q, stream, nslot, port_id); chest_recvsig_fprint(q, stream, nslot, port_id);
@ -102,6 +103,7 @@ int chest_ce_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint
port_id < q->nof_ports) port_id < q->nof_ports)
{ {
if (nref < q->refsignal[port_id][nslot].nof_refs) { if (nref < q->refsignal[port_id][nslot].nof_refs) {
fidx = q->refsignal[port_id][nslot].refs[nref].freq_idx; // reference frequency index 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 tidx = q->refsignal[port_id][nslot].refs[nref].time_idx; // reference time index
@ -153,10 +155,15 @@ int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32
/* interpolate the symbols with references /* interpolate the symbols with references
* in the freq domain */ * in the freq domain */
for (i=0;i<r->nsymbols;i++) { 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], interp_linear_offset(&r->ch_est[i * r->nof_refs/2],
&ce[r->symbols_ref[i] * q->nof_re], RE_X_RB/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); r->nof_refs/2, r->voffset, RE_X_RB/2-r->voffset);
#endif
} }
/* now interpolate in the time domain */ /* now interpolate in the time domain */
for (i=0;i<q->nof_re; i++) { for (i=0;i<q->nof_re; i++) {
@ -164,8 +171,13 @@ int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32
for (j=0;j<r->nsymbols;j++) { for (j=0;j<r->nsymbols;j++) {
x[j] = ce[r->symbols_ref[j] * q->nof_re + i]; 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], interp_linear_offset(x, y, r->symbols_ref[1]-r->symbols_ref[0],
2, r->symbols_ref[0], 3); 2, r->symbols_ref[0], 3);
#endif
} else { } else {
for (j=0;j<MAX_NSYMB;j++) { for (j=0;j<MAX_NSYMB;j++) {
y[j] = ce[r->symbols_ref[0] * q->nof_re + i]; y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
@ -225,7 +237,7 @@ int chest_ce_sf(chest_t *q, cf_t *input, cf_t *ce[MAX_PORTS], uint32_t sf_idx) {
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} }
int chest_init(chest_t *q, chest_interp_t interp, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) { int chest_init(chest_t *q, uint32_t nof_re, uint32_t nof_symbols, uint32_t nof_ports) {
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && if (q != NULL &&
@ -237,11 +249,6 @@ int chest_init(chest_t *q, chest_interp_t interp, uint32_t nof_re, uint32_t nof_
q->nof_symbols = nof_symbols; q->nof_symbols = nof_symbols;
q->nof_re = nof_re; q->nof_re = nof_re;
switch(interp) {
case LINEAR:
q->interp = interp_linear_offset;
}
INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n", INFO("Initializing channel estimator size %dx%d, nof_ports=%d\n",
q->nof_symbols, q->nof_re, nof_ports); q->nof_symbols, q->nof_re, nof_ports);
@ -250,9 +257,9 @@ int chest_init(chest_t *q, chest_interp_t interp, uint32_t nof_re, uint32_t nof_
return ret; return ret;
} }
int chest_init_LTEDL(chest_t *q, chest_interp_t interp, lte_cell_t cell) { int chest_init_LTEDL(chest_t *q, lte_cell_t cell) {
int ret; int ret;
ret = chest_init(q, interp, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports); ret = chest_init(q, cell.nof_prb * RE_X_RB, CP_NSYMB(cell.cp), cell.nof_ports);
if (ret != LIBLTE_SUCCESS) { if (ret != LIBLTE_SUCCESS) {
return ret; return ret;
} else { } else {
@ -268,6 +275,16 @@ int chest_ref_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_
nslot < NSLOTS_X_FRAME) nslot < NSLOTS_X_FRAME)
{ {
ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell); ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell);
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]);
}
}
}
} }
return ret; return ret;
} }
@ -339,7 +356,7 @@ int chest_initialize(chest_hl* h) {
cell.nof_prb = h->init.nof_prb; cell.nof_prb = h->init.nof_prb;
cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT; cell.cp = h->init.nof_symbols == CPNORM_NSYMB ? CPNORM : CPEXT;
if (chest_init_LTEDL(&h->obj, LINEAR, cell)) { if (chest_init_LTEDL(&h->obj, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }

@ -162,7 +162,7 @@ int main(int argc, char **argv) {
while(cid <= max_cid) { while(cid <= max_cid) {
cell.id = cid; cell.id = cid;
if (chest_init_LTEDL(&eq, LINEAR, cell)) { if (chest_init_LTEDL(&eq, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
goto do_exit; goto do_exit;
} }

@ -65,6 +65,23 @@ bool lte_cell_isvalid(lte_cell_t *cell) {
} }
} }
bool lte_N_id_2_isvalid(uint32_t N_id_2) {
if (N_id_2 < 3) {
return true;
} else {
return false;
}
}
bool lte_N_id_1_isvalid(uint32_t N_id_1) {
if (N_id_1 < 169) {
return true;
} else {
return false;
}
}
/* /*
* Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index * Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index
*/ */
@ -106,6 +123,14 @@ uint32_t lte_mod_bits_x_symbol(lte_mod_t mod) {
} }
} }
char *lte_cp_string(lte_cp_t cp) {
if (cp == CPNORM) {
return "Normal";
} else {
return "Extended";
}
}
/* /*
* Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212 * Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212
*/ */
@ -130,6 +155,7 @@ int lte_sampling_freq_hz(uint32_t nof_prb) {
return 15000 * n; return 15000 * n;
} }
} }
int lte_symbol_sz(uint32_t nof_prb) { int lte_symbol_sz(uint32_t nof_prb) {
if (nof_prb<=0) { if (nof_prb<=0) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
@ -150,6 +176,18 @@ int lte_symbol_sz(uint32_t nof_prb) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
bool lte_symbol_sz_isvalid(uint32_t symbol_sz) {
if (symbol_sz == 128 ||
symbol_sz == 256 ||
symbol_sz == 512 ||
symbol_sz == 1024 ||
symbol_sz == 2048) {
return true;
} else {
return false;
}
}
uint32_t lte_voffset(uint32_t symbol_id, uint32_t cell_id, uint32_t nof_ports) { uint32_t lte_voffset(uint32_t symbol_id, uint32_t cell_id, uint32_t nof_ports) {
if (nof_ports == 1 && symbol_id==0) { if (nof_ports == 1 && symbol_id==0) {
return (cell_id+3) % 6; return (cell_id+3) % 6;

@ -61,7 +61,7 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
return ret; return ret;
} }
if (VERBOSE_ISINFO()) { if (VERBOSE_ISDEBUG()) {
dci_msg_type_fprint(stdout, type); dci_msg_type_fprint(stdout, type);
} }
if (type.type == PDSCH_SCHED) { if (type.type == PDSCH_SCHED) {
@ -72,7 +72,7 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
return ret; return ret;
} }
if (VERBOSE_ISINFO()) { if (VERBOSE_ISDEBUG()) {
ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb); ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb);
} }

@ -50,6 +50,13 @@
#define MIN(a,b) ((a>b)?b:a) #define MIN(a,b) ((a>b)?b:a)
#define NOF_COMMON_FORMATS 2
const dci_format_t common_formats[NOF_COMMON_FORMATS] = { Format1A, Format1C };
#define NOF_UE_FORMATS 2
const dci_format_t ue_formats[NOF_UE_FORMATS] = { Format0, Format1 }; // 1A has the same payload as 0
static void set_cfi(pdcch_t *q, uint32_t cfi) { static void set_cfi(pdcch_t *q, uint32_t cfi) {
if (cfi > 0 && cfi < 4) { if (cfi > 0 && cfi < 4) {
q->nof_regs = (regs_pdcch_nregs(q->regs, cfi) / 9) * 9; q->nof_regs = (regs_pdcch_nregs(q->regs, cfi) / 9) * 9;
@ -228,7 +235,8 @@ uint32_t pdcch_ue_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidat
* Returns the number of candidates saved in the array c. * Returns the number of candidates saved in the array c.
*/ */
uint32_t pdcch_common_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidates, uint32_t pdcch_common_locations(pdcch_t *q, dci_location_t *c, uint32_t max_candidates,
uint32_t cfi) { uint32_t cfi)
{
uint32_t i, l, L, k; uint32_t i, l, L, k;
set_cfi(q, cfi); set_cfi(q, cfi);
@ -271,8 +279,8 @@ static int dci_decode(pdcch_t *q, float *e, char *data, uint32_t E, uint32_t nof
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
E < q->max_bits && E <= q->max_bits &&
nof_bits < DCI_MAX_BITS) nof_bits <= DCI_MAX_BITS)
{ {
/* unrate matching */ /* unrate matching */
@ -301,6 +309,7 @@ static int dci_decode(pdcch_t *q, float *e, char *data, uint32_t E, uint32_t nof
} }
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} else { } else {
fprintf(stderr, "Invalid parameters: E: %d, max_bits: %d, nof_bits: %d\n", E, q->max_bits, nof_bits);
return LIBLTE_ERROR_INVALID_INPUTS; return LIBLTE_ERROR_INVALID_INPUTS;
} }
} }

@ -171,7 +171,7 @@ int pdsch_get(pdsch_t *q, cf_t *sf_symbols, cf_t *pdsch_symbols,
} }
/** Initializes the PDCCH transmitter and receiver */ /** Initializes the PDCCH transmitter and receiver */
int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) { int pdsch_init(pdsch_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
int i; int i;
@ -183,7 +183,6 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
ret = LIBLTE_ERROR; ret = LIBLTE_ERROR;
q->cell = cell; q->cell = cell;
q->rnti = user_rnti;
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp); q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
@ -205,12 +204,7 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
demod_soft_init(&q->demod); demod_soft_init(&q->demod);
demod_soft_alg_set(&q->demod, APPROX); demod_soft_alg_set(&q->demod, APPROX);
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { q->rnti_is_set = false;
if (sequence_pdsch(&q->seq_pdsch[i], q->rnti, 0, 2 * i, q->cell.id,
q->max_symbols * q->mod[3].nbits_x_symbol)) {
goto clean;
}
}
if (tcod_init(&q->encoder, MAX_LONG_CB)) { if (tcod_init(&q->encoder, MAX_LONG_CB)) {
goto clean; goto clean;
@ -304,8 +298,20 @@ void pdsch_free(pdsch_t *q) {
} }
int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) {
uint32_t i;
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
if (sequence_pdsch(&q->seq_pdsch[i], rnti, 0, 2 * i, q->cell.id,
q->max_symbols * q->mod[3].nbits_x_symbol)) {
return LIBLTE_ERROR;
}
}
q->rnti_is_set = true;
q->rnti = rnti;
return LIBLTE_SUCCESS;
}
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */ /* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) { static int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1; uint32_t Bp, B, idx1;
int ret; int ret;
@ -499,7 +505,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
n_e = nb_e - rp; n_e = nb_e - rp;
} }
INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, DEBUG("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i,
cb_len, rlen - F, wp, rp, F, n_e); cb_len, rlen - F, wp, rp, F, n_e);
/* Rate Unmatching */ /* Rate Unmatching */
@ -526,7 +532,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
if (i < harq_process->cb_segm.C - 1) { if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(char)); memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(char));
} else { } else {
INFO("Last CB, appending parity: %d to %d from %d and 24 from %d\n", DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
rlen - F - 24, wp, F, rlen - 24); rlen - F - 24, wp, F, rlen - 24);
/* Append Transport Block parity bits to the last CB */ /* Append Transport Block parity bits to the last CB */
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char)); memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char));
@ -538,7 +544,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
rp += n_e; rp += n_e;
} }
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); DEBUG("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
// Compute transport block CRC // Compute transport block CRC
par_rx = crc_checksum(&q->crc_tb, data, tbs); par_rx = crc_checksum(&q->crc_tb, data, tbs);
@ -577,7 +583,8 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
sf_symbols != NULL && sf_symbols != NULL &&
data != NULL && data != NULL &&
subframe < 10 && subframe < 10 &&
harq_process != NULL) harq_process != NULL &&
harq_process->mcs.mod > 0)
{ {
nof_bits = harq_process->mcs.tbs; nof_bits = harq_process->mcs.tbs;
@ -585,8 +592,8 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol; nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol;
INFO("Decoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n", INFO("Decoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e); subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e, rv_idx);
/* number of layers equals number of ports */ /* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) { for (i = 0; i < q->cell.nof_ports; i++) {
@ -651,12 +658,14 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
uint32_t i; uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e; uint32_t cb_len, rp, wp, rlen, F, n_e;
char *e_bits = q->pdsch_e; char *e_bits = q->pdsch_e;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
nb_e < q->max_symbols * q->mod[3].nbits_x_symbol) nb_e < q->max_symbols * q->mod[3].nbits_x_symbol)
{ {
if (q->rnti_is_set) {
if (rv_idx == 0) { if (rv_idx == 0) {
/* Compute transport block CRC */ /* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs); par = crc_checksum(&q->crc_tb, data, tbs);
@ -746,11 +755,13 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
return LIBLTE_SUCCESS; ret = LIBLTE_SUCCESS;
} else { } else {
return LIBLTE_ERROR_INVALID_INPUTS; fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n");
} }
} }
return ret;
}
/** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission /** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission
*/ */
@ -761,6 +772,7 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
uint32_t nof_symbols, nof_bits, nof_bits_e; uint32_t nof_symbols, nof_bits, nof_bits_e;
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
cf_t *x[MAX_LAYERS]; cf_t *x[MAX_LAYERS];
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
@ -768,6 +780,7 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
harq_process != NULL) harq_process != NULL)
{ {
if (q->rnti_is_set) {
for (i=0;i<q->cell.nof_ports;i++) { for (i=0;i<q->cell.nof_ports;i++) {
if (sf_symbols[i] == NULL) { if (sf_symbols[i] == NULL) {
return LIBLTE_ERROR_INVALID_INPUTS; return LIBLTE_ERROR_INVALID_INPUTS;
@ -808,7 +821,6 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
scrambling_b_offset(&q->seq_pdsch[subframe], (char*) q->pdsch_e, 0, nof_bits_e); scrambling_b_offset(&q->seq_pdsch[subframe], (char*) q->pdsch_e, 0, nof_bits_e);
mod_modulate(&q->mod[harq_process->mcs.mod - 1], (char*) q->pdsch_e, q->pdsch_d, nof_bits_e); mod_modulate(&q->mod[harq_process->mcs.mod - 1], (char*) q->pdsch_e, q->pdsch_d, nof_bits_e);
@ -826,9 +838,11 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t s
for (i = 0; i < q->cell.nof_ports; i++) { for (i = 0; i < q->cell.nof_ports; i++) {
pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe); pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe);
} }
return LIBLTE_SUCCESS; ret = LIBLTE_SUCCESS;
} else { } else {
return LIBLTE_ERROR_INVALID_INPUTS; fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n");
}
} }
return ret;
} }

@ -0,0 +1,555 @@
/**
*
* \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 <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <unistd.h>
#include "liblte/phy/phch/ue_sync.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#define MAX_TIME_OFFSET 128
cf_t dummy[MAX_TIME_OFFSET];
#define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1))
#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb)
#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE, q->cell.cp)
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
#define MAXIMUM_SFLEN SF_LEN(2048, CPNORM)
#define MAXIMUM_SFLEN_RE SF_LEN_RE(110, CPNORM)
static int mib_decoder_initialize(ue_sync_t *q);
static void mib_decoder_free(ue_sync_t *q);
int ue_sync_init(ue_sync_t *q,
double (set_rate_callback)(void*, double),
int (recv_callback)(void*, void*, uint32_t),
void *stream_handler)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
stream_handler != NULL)
{
ret = LIBLTE_ERROR;
bzero(q, sizeof(ue_sync_t));
ue_sync_reset(q);
q->cell.nof_prb = SYNC_PBCH_NOF_PRB;
q->cell.nof_ports = SYNC_PBCH_NOF_PORTS;
q->cell.id = 0;
q->cell.cp = CPNORM;
q->pbch_decoded = false;
q->pbch_initialized = false;
q->pbch_decoder_enabled = true;
q->pbch_decode_always = false;
q->decode_sss_on_track = false;
q->stream = stream_handler;
q->recv_callback = recv_callback;
q->set_rate_callback = set_rate_callback;
INFO("Setting sampling frequency 1.92 MHz\n",0);
q->set_rate_callback(q->stream, 1920000.0);
if(sync_init(&q->s, CURRENT_SFLEN, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) {
goto clean_exit;
}
sync_pss_det_peak_to_avg(&q->s);
if (cfo_init(&q->cfocorr, MAXIMUM_SFLEN)) {
fprintf(stderr, "Error initiating CFO\n");
goto clean_exit;
}
q->input_buffer = vec_malloc(3 * MAXIMUM_SFLEN * sizeof(cf_t));
if (!q->input_buffer) {
perror("malloc");
goto clean_exit;
}
q->sf_symbols = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t));
if (!q->sf_symbols) {
perror("malloc");
goto clean_exit;
}
for (int i=0;i<SYNC_PBCH_NOF_PORTS;i++) {
q->ce[i] = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t));
if (!q->ce[i]) {
perror("malloc");
goto clean_exit;
}
}
//float th = PAR_THRESHOLD_FIND * (1+(float) CURRENT_FFTSIZE/128/10);
sync_set_threshold(&q->s, PAR_THRESHOLD_FIND, PAR_THRESHOLD_FIND/4);
ret = LIBLTE_SUCCESS;
}
clean_exit:
if (ret == LIBLTE_ERROR) {
ue_sync_free(q);
}
return ret;
}
void ue_sync_free(ue_sync_t *q) {
if (q->input_buffer) {
free(q->input_buffer);
}
if (q->sf_symbols) {
free(q->sf_symbols);
}
for (int i=0;i<SYNC_PBCH_NOF_PORTS;i++) {
if (q->ce[i]) {
free(q->ce[i]);
}
}
mib_decoder_free(q);
cfo_free(&q->cfocorr);
sync_free(&q->s);
}
void ue_sync_set_threshold(ue_sync_t *q, float threshold) {
sync_set_threshold(&q->s, threshold, threshold/2);
}
lte_cell_t ue_sync_get_cell(ue_sync_t *q) {
return q->cell;
}
pbch_mib_t ue_sync_get_mib(ue_sync_t *q) {
return q->mib;
}
uint32_t ue_sync_peak_idx(ue_sync_t *q) {
return q->peak_idx;
}
ue_sync_state_t ue_sync_get_state(ue_sync_t *q) {
return q->state;
}
static int update_srate(ue_sync_t *q) {
struct timeval t[3];
gettimeofday(&t[1], NULL);
if (sync_realloc(&q->s, CURRENT_SFLEN, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) {
fprintf(stderr, "Error realloc'ing SYNC\n");
return LIBLTE_ERROR;
}
gettimeofday(&t[2], NULL);
get_time_interval(t);
if (NOF_MIB_DECODES > 1) {
mib_decoder_free(q);
if (mib_decoder_initialize(q)) {
fprintf(stderr, "Error reinitializing MIB decoder\n");
return LIBLTE_ERROR;
}
}
// Finally set the new sampling rate
q->set_rate_callback(q->stream, (float) lte_sampling_freq_hz(q->cell.nof_prb));
ue_sync_reset(q);
printf("Set sampling rate %.2f MHz, fft_size=%d, sf_len=%d Texec=%d us\n",
(float) lte_sampling_freq_hz(q->cell.nof_prb)/1000000,
CURRENT_FFTSIZE, CURRENT_SFLEN, (int) t[0].tv_usec);
return LIBLTE_SUCCESS;
}
uint32_t ue_sync_get_sfidx(ue_sync_t *q) {
return q->sf_idx;
}
float ue_sync_get_cfo(ue_sync_t *q) {
return 15000 * q->cur_cfo;
}
float ue_sync_get_sfo(ue_sync_t *q) {
return 1000*q->mean_time_offset/5;
}
bool ue_sync_is_mib_decoded(ue_sync_t *q) {
return q->pbch_decoded;
}
void ue_sync_pbch_enable(ue_sync_t *q, bool enabled) {
q->pbch_decoder_enabled = enabled;
}
void ue_sync_pbch_always(ue_sync_t *q, bool enabled) {
q->pbch_decode_always = enabled;
}
void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) {
q->decode_sss_on_track = enabled;
}
static int mib_decoder_initialize(ue_sync_t *q) {
if (lte_fft_init(&q->fft, q->cell.cp, q->cell.nof_prb)) {
fprintf(stderr, "Error initializing FFT\n");
return -1;
}
if (chest_init_LTEDL(&q->chest, q->cell)) {
fprintf(stderr, "Error initializing reference signal\n");
return -1;
}
if (pbch_init(&q->pbch, q->cell)) {
fprintf(stderr, "Error initiating PBCH\n");
return -1;
}
q->pbch_initialized = 1;
DEBUG("PBCH initiated cell_id=%d\n", q->cell.id);
return 0;
}
static void mib_decoder_free(ue_sync_t *q) {
chest_free(&q->chest);
pbch_free(&q->pbch);
lte_fft_free(&q->fft);
}
static int mib_decoder_run(ue_sync_t *q) {
int ret;
/* Run FFT for the second slot */
lte_fft_run_sf(&q->fft, q->input_buffer, q->sf_symbols);
/* Get channel estimates of slot #1 for each port */
chest_ce_sf(&q->chest, q->sf_symbols, q->ce, 0);
if (q->pbch_last_trial &&
(q->frame_total_cnt - q->pbch_last_trial > 2))
{
pbch_decode_reset(&q->pbch);
INFO("Resetting PBCH decoder: last trial %d, now is %d\n",
q->pbch_last_trial, q->frame_total_cnt);
q->pbch_last_trial = 0;
}
if (pbch_decode(&q->pbch, q->sf_symbols, q->ce, &q->mib) == 1) {
q->frame_number = q->mib.sfn;
q->cell.nof_ports = q->mib.nof_ports;
q->cell.nof_prb = q->mib.nof_prb;
if (!q->pbch_decoded) {
printf("MIB decoded:\n");
pbch_mib_fprint(stdout, &q->mib);
ret = update_srate(q);
} else {
INFO("MIB decoded #%d SFN: %d\n", q->pbch_decoded, q->mib.sfn);
}
q->pbch_decoded++;
pbch_decode_reset(&q->pbch);
} else {
INFO("MIB not decoded: %d\n", q->frame_total_cnt);
q->pbch_last_trial = q->frame_total_cnt;
}
return ret;
}
static int find_peak_ok(ue_sync_t *q) {
int ret;
if (q->peak_idx < CURRENT_SFLEN) {
/* Receive the rest of the next subframe */
if (q->recv_callback(q->stream, &q->input_buffer[CURRENT_SFLEN], q->peak_idx+CURRENT_SFLEN/2) < 0) {
return LIBLTE_ERROR;
}
}
if (sync_sss_detected(&q->s)) {
ret = sync_get_cell_id(&q->s);
if (ret >= 0) {
q->cell.id = (uint32_t) ret;
q->cell.cp = sync_get_cp(&q->s);
}
/* Get the subframe index (0 or 5) */
q->sf_idx = sync_get_slot_id(&q->s)/2;
/* Reset variables */
q->frame_ok_cnt = 0;
q->frame_no_cnt = 0;
q->frame_total_cnt = 0;
/* Goto Tracking state */
q->state = SF_TRACK;
ret = LIBLTE_SUCCESS;
INFO("Found peak %d, SF_idx: %d, Cell_id: %d CP: %s\n",
q->peak_idx, q->sf_idx, q->cell.id, lte_cp_string(q->cell.cp));
if (q->peak_idx < CURRENT_SFLEN) {
q->sf_idx++;
}
return ret;
} else {
INFO("Found peak at %d, SSS not detected\n", q->peak_idx);
return 0;
}
}
int track_peak_ok(ue_sync_t *q, uint32_t track_idx) {
int ret = LIBLTE_SUCCESS;
/* Make sure subframe idx is what we expect */
if ((q->sf_idx != sync_get_slot_id(&q->s)/2) && q->decode_sss_on_track) {
printf("\nWarning: Expected SF idx %d but got %d!\n",
q->sf_idx, sync_get_slot_id(&q->s)/2);
q->sf_idx = sync_get_slot_id(&q->s)/2;
} else {
q->time_offset = ((int) track_idx - (int) CURRENT_FFTSIZE);
/* If the PSS peak is beyond the frame (we sample too slowly),
discard the offseted samples to align next frame */
if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) {
ret = q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset);
} else {
ret = LIBLTE_SUCCESS;
}
/* compute cumulative moving average CFO */
q->cur_cfo = EXPAVERAGE(sync_get_cfo(&q->s), q->cur_cfo, q->frame_ok_cnt);
/* compute cumulative moving average time offset */
q->mean_time_offset = (float) EXPAVERAGE((float) q->time_offset, q->mean_time_offset, q->frame_ok_cnt);
q->peak_idx = CURRENT_SFLEN/2 + q->time_offset;
q->frame_ok_cnt++;
q->frame_no_cnt = 0;
if (ret >= LIBLTE_SUCCESS) {
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
int track_peak_no(ue_sync_t *q) {
/* if we missed too many PSS go back to FIND */
q->frame_no_cnt++;
if (q->frame_no_cnt >= TRACK_MAX_LOST) {
printf("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt);
q->state = SF_FIND;
} else {
INFO("Tracking peak not found, %d lost\n", (int) q->frame_no_cnt);
}
return LIBLTE_SUCCESS;
}
static int receive_samples(ue_sync_t *q) {
/* A negative time offset means there are samples in our buffer for the next subframe,
because we are sampling too fast.
*/
if (q->time_offset < 0) {
q->time_offset = -q->time_offset;
}
/* copy last part of the last subframe (use move since there could be overlapping) */
memmove(q->input_buffer, &q->input_buffer[CURRENT_SFLEN-q->time_offset], q->time_offset*sizeof(cf_t));
/* Get 1 subframe from the USRP getting more samples and keeping the previous samples, if any */
if (q->recv_callback(q->stream, &q->input_buffer[q->time_offset], CURRENT_SFLEN - q->time_offset) < 0) {
return LIBLTE_ERROR;
}
/* reset time offset */
q->time_offset = 0;
return LIBLTE_SUCCESS;
}
int ue_sync_get_buffer(ue_sync_t *q, cf_t **sf_symbols) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t track_idx;
struct timeval t[3];
if (q != NULL &&
sf_symbols != NULL &&
q->input_buffer != NULL)
{
if (receive_samples(q)) {
fprintf(stderr, "Error receiving samples\n");
return -1;
}
switch (q->state) {
case SF_FIND:
q->s.sss_en = true;
/* Find peak and cell id */
ret = sync_find(&q->s, q->input_buffer, &q->peak_idx);
if (ret < 0) {
fprintf(stderr, "Error finding correlation peak (%d)\n", ret);
return -1;
}
DEBUG("Find PAR=%.2f\n", sync_get_peak_to_avg(&q->s));
if (ret == 1) {
ret = find_peak_ok(q);
/* Initialize PBCH decoder */
if (ret == LIBLTE_SUCCESS) {
if (!q->pbch_initialized && q->pbch_decoder_enabled) {
ret = mib_decoder_initialize(q);
if (ret < 0) {
fprintf(stderr, "Error initializing MIB decoder\n");
}
}
} else if (ret < 0) {
if (ret < 0) {
fprintf(stderr, "Error processing find peak \n");
}
}
}
break;
case SF_TRACK:
ret = LIBLTE_SUCCESS;
q->s.sss_en = q->decode_sss_on_track;
q->sf_idx = (q->sf_idx + 1) % 10;
DEBUG("TRACK: SF=%d FrameCNT: %d\n", q->sf_idx, q->frame_total_cnt);
/* Every SF idx 0 and 5, find peak around known position q->peak_idx */
if (q->sf_idx == 0 || q->sf_idx == 5) {
#ifdef MEASURE_EXEC_TIME
gettimeofday(&t[1], NULL);
#endif
track_idx = 0;
/* track pss around the middle of the subframe, where the PSS is */
ret = sync_track(&q->s, q->input_buffer, CURRENT_SFLEN/2-CURRENT_FFTSIZE, &track_idx);
if (ret < 0) {
fprintf(stderr, "Error tracking correlation peak\n");
return -1;
}
#ifdef MEASURE_EXEC_TIME
gettimeofday(&t[2], NULL);
get_time_interval(t);
q->mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, q->mean_exec_time, q->frame_total_cnt);
#endif
if (ret == 1) {
ret = track_peak_ok(q, track_idx);
} else {
ret = track_peak_no(q);
}
INFO("TRACK %3d: SF=%d Track_idx=%d Offset=%d CFO: %f\n",
(int) q->frame_total_cnt, q->sf_idx, track_idx, q->time_offset, sync_get_cfo(&q->s));
q->frame_total_cnt++;
if (ret == LIBLTE_ERROR) {
fprintf(stderr, "Error processing tracking peak\n");
ue_sync_reset(q);
return LIBLTE_SUCCESS;
}
}
/* Do CFO Correction and deliver the frame */
cfo_correct(&q->cfocorr, q->input_buffer, q->input_buffer, -q->cur_cfo / CURRENT_FFTSIZE);
*sf_symbols = q->input_buffer;
/* At subframe 0, try to decode PBCH if not yet decoded */
if (q->sf_idx == 0) {
if(q->pbch_decoder_enabled &&
(q->pbch_decoded < NOF_MIB_DECODES || q->pbch_decode_always))
{
mib_decoder_run(q);
} else {
q->mib.sfn = (q->mib.sfn + 1) % 1024;
}
}
if (ret == LIBLTE_SUCCESS) {
if (q->pbch_decoder_enabled) {
if (q->pbch_decoded >= NOF_MIB_DECODES) {
ret = 1;
} else {
ret = 0;
}
} else {
ret = 1;
}
}
break;
}
}
DEBUG("UE SYNC returns %d\n", ret);
return ret;
}
void ue_sync_reset(ue_sync_t *q) {
q->state = SF_FIND;
q->pbch_last_trial = 0;
q->frame_ok_cnt = 0;
q->frame_no_cnt = 0;
q->frame_total_cnt = 0;
q->cur_cfo = 0;
q->mean_time_offset = 0;
q->time_offset = 0;
#ifdef MEASURE_EXEC_TIME
q->mean_exec_time = 0;
#endif
}

@ -19,6 +19,23 @@
# and at http://www.gnu.org/licenses/. # and at http://www.gnu.org/licenses/.
# #
########################################################################
# UE SYNC TEST (Only compiled if CUHD is available)
########################################################################
LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND)
LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND)
IF(${CUHD_FIND} GREATER -1)
ADD_EXECUTABLE(ue_sync_usrp ue_sync_usrp.c)
TARGET_LINK_LIBRARIES(ue_sync_usrp lte_phy cuhd)
ENDIF(${CUHD_FIND} GREATER -1)
IF(${GRAPHICS_FIND} EQUAL -1)
SET_TARGET_PROPERTIES(ue_sync_usrp PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS")
ELSE(${GRAPHICS_FIND} EQUAL -1)
target_link_libraries(ue_sync_usrp graphics)
ENDIF(${GRAPHICS_FIND} EQUAL -1)
######################################################################## ########################################################################
# PBCH TEST # PBCH TEST
######################################################################## ########################################################################

@ -136,7 +136,7 @@ int base_init() {
return -1; return -1;
} }
if (chest_init_LTEDL(&chest, LINEAR, cell)) { if (chest_init_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }

@ -141,7 +141,7 @@ int base_init() {
} }
} }
if (chest_init_LTEDL(&chest, LINEAR, cell)) { if (chest_init_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }

@ -155,7 +155,7 @@ int base_init() {
} }
} }
if (chest_init_LTEDL(&chest, LINEAR, cell)) { if (chest_init_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }

@ -158,7 +158,7 @@ int base_init() {
} }
} }
if (chest_init_LTEDL(&chest, LINEAR, cell)) { if (chest_init_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }
@ -183,10 +183,11 @@ int base_init() {
exit(-1); exit(-1);
} }
if (pdsch_init(&pdsch, rnti, cell)) { if (pdsch_init(&pdsch, cell)) {
fprintf(stderr, "Error creating PDSCH object\n"); fprintf(stderr, "Error creating PDSCH object\n");
exit(-1); exit(-1);
} }
pdsch_set_rnti(&pdsch, rnti);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");

@ -78,7 +78,8 @@ int main(int argc, char **argv) {
cell.nof_ports = test_re_ports[i]; cell.nof_ports = test_re_ports[i];
cell.cp = test_re_cp[i]; cell.cp = test_re_cp[i];
pdsch_init(&pdsch, 0, cell); pdsch_init(&pdsch, cell);
pdsch_set_rnti(&pdsch, 0);
memset(prb_alloc.re_sf, 0, sizeof(uint32_t) * 10); memset(prb_alloc.re_sf, 0, sizeof(uint32_t) * 10);
prb_alloc.slot[0].nof_prb = test_re_prb[i]; prb_alloc.slot[0].nof_prb = test_re_prb[i];

@ -166,11 +166,13 @@ int main(int argc, char **argv) {
goto quit; goto quit;
} }
if (pdsch_init(&pdsch, 1234, cell)) { if (pdsch_init(&pdsch, cell)) {
fprintf(stderr, "Error creating PDSCH object\n"); fprintf(stderr, "Error creating PDSCH object\n");
goto quit; goto quit;
} }
pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
goto quit; goto quit;

@ -166,7 +166,7 @@ int base_init() {
} }
} }
if (chest_init_LTEDL(&chest, LINEAR, cell)) { if (chest_init_LTEDL(&chest, cell)) {
fprintf(stderr, "Error initializing equalizer\n"); fprintf(stderr, "Error initializing equalizer\n");
return -1; return -1;
} }

@ -27,12 +27,173 @@
#include <complex.h> #include <complex.h>
#include <math.h> #include <math.h>
#include <stdlib.h>
#include "liblte/phy/resampling/interp.h" #include "liblte/phy/resampling/interp.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
#define TABLE_SIZE 1024
#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) {
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);
}
/* Performs 1st order linear interpolation with out-of-bound interpolation */ /* Performs 1st order linear interpolation with out-of-bound interpolation */
void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st, int off_end) { void interp_linear_offset(cf_t *input, cf_t *output, uint32_t M, uint32_t len, uint32_t off_st, uint32_t off_end) {
int i, j; uint32_t i, j;
float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0; float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0;
for (i=0;i<len-1;i++) { for (i=0;i<len-1;i++) {
@ -63,14 +224,14 @@ void interp_linear_offset(cf_t *input, cf_t *output, int M, int len, int off_st,
} }
/* Performs 1st order linear interpolation */ /* Performs 1st order linear interpolation */
void interp_linear_c(cf_t *input, cf_t *output, int M, int len) { void interp_linear_c(cf_t *input, cf_t *output, uint32_t M, uint32_t len) {
interp_linear_offset(input, output, M, len, 0, 0); interp_linear_offset(input, output, M, len, 0, 1);
} }
/* Performs 1st order integer linear interpolation */ /* Performs 1st order uint32_teger linear interpolation */
void interp_linear_f(float *input, float *output, int M, int len) { void interp_linear_f(float *input, float *output, uint32_t M, uint32_t len) {
int i, j; uint32_t i, j;
for (i=0;i<len-1;i++) { for (i=0;i<len-1;i++) {
for (j=0;j<M;j++) { for (j=0;j<M;j++) {
output[i*M+j] = input[i] + j * (input[i+1]-input[i]) / M; output[i*M+j] = input[i] + j * (input[i+1]-input[i]) / M;

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

@ -34,14 +34,14 @@
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
int cfo_init(cfo_t *h, int nsamples) { int cfo_init(cfo_t *h, uint32_t nsamples) {
int ret = -1; int ret = LIBLTE_ERROR;
bzero(h, sizeof(cfo_t)); bzero(h, sizeof(cfo_t));
if (cexptab_init(&h->tab, CFO_CEXPTAB_SIZE)) { if (cexptab_init(&h->tab, CFO_CEXPTAB_SIZE)) {
goto clean; goto clean;
} }
h->cur_cexp = malloc(sizeof(cf_t) * nsamples); h->cur_cexp = vec_malloc(sizeof(cf_t) * nsamples);
if (!h->cur_cexp) { if (!h->cur_cexp) {
goto clean; goto clean;
} }
@ -50,9 +50,9 @@ int cfo_init(cfo_t *h, int nsamples) {
h->nsamples = nsamples; h->nsamples = nsamples;
cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples);
ret = 0; ret = LIBLTE_SUCCESS;
clean: clean:
if (ret == -1) { if (ret == LIBLTE_ERROR) {
cfo_free(h); cfo_free(h);
} }
return ret; return ret;
@ -70,11 +70,23 @@ void cfo_set_tol(cfo_t *h, float tol) {
h->tol = tol; h->tol = tol;
} }
void cfo_correct(cfo_t *h, cf_t *x, float freq) { int cfo_realloc(cfo_t *h, uint32_t samples) {
h->cur_cexp = realloc(h->cur_cexp, sizeof(cf_t) * samples);
if (!h->cur_cexp) {
perror("realloc");
return LIBLTE_ERROR;
}
cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, samples);
h->nsamples = samples;
return LIBLTE_SUCCESS;
}
void cfo_correct(cfo_t *h, cf_t *input, cf_t *output, float freq) {
if (fabs(h->last_freq - freq) > h->tol) { if (fabs(h->last_freq - freq) > h->tol) {
h->last_freq = freq; h->last_freq = freq;
cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples);
INFO("CFO generating new table for frequency %.4f\n", freq); DEBUG("CFO generating new table for frequency %.4f\n", freq);
} }
vec_prod_ccc(h->cur_cexp, x, x, h->nsamples); vec_prod_ccc(h->cur_cexp, input, output, h->nsamples);
} }

@ -31,18 +31,10 @@
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#include "liblte/phy/sync/sss.h" #include "liblte/phy/sync/sss.h"
cf_t corr_sz(cf_t *z, cf_t *s) { void corr_all_zs(cf_t *z, cf_t s[N_SSS+1][N_SSS+1], cf_t *output) {
cf_t sum; uint32_t m;
cf_t zsprod[32];
vec_prod_ccc(z, s, zsprod, N_SSS - 1);
sum = vec_acc_cc(zsprod, N_SSS - 1);
return sum;
}
void corr_all_zs(cf_t *z, cf_t s[32][32], cf_t *output) {
int m;
for (m = 0; m < N_SSS; m++) { for (m = 0; m < N_SSS; m++) {
output[m] = corr_sz(z, s[m]); output[m] = vec_dot_prod_ccc(z, s[m], N_SSS - 1);
} }
} }
@ -58,22 +50,30 @@ void corr_all_zs(cf_t *z, cf_t s[32][32], cf_t *output) {
* *
*/ */
void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value, int sss_synch_m0m1(sss_synch_t *q, cf_t *input, uint32_t *m0, float *m0_value,
int *m1, float *m1_value) { uint32_t *m1, float *m1_value)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
input != NULL &&
m0 != NULL &&
m1 != NULL)
{
/* This is aprox 3-4 kbytes of stack. Consider moving to sss_synch_t?? */ /* Consider moving to sss_synch_t?? */
cf_t zdelay[N_SSS+1],zconj[N_SSS+1],zprod[N_SSS+1]; cf_t zdelay[N_SSS+1],zconj[N_SSS+1],zprod[N_SSS+1];
cf_t y[2][N_SSS+1], z[N_SSS+1], tmp[N_SSS+1]; cf_t y[2][N_SSS+1], z[N_SSS+1], tmp[N_SSS+1];
float tmp_real[N_SSS+1]; float tmp_real[N_SSS+1];
cf_t input_fft[SSS_DFT_LEN]; cf_t input_fft[SSS_MAX_FFT_LEN];
uint32_t i;
int i;
dft_run_c(&q->dftp_input, input, input_fft); dft_run_c(&q->dftp_input, input, input_fft);
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
y[0][i] = input_fft[SSS_POS_SYMBOL + 2 * i]; y[0][i] = input_fft[q->fft_size/2-N_SSS + 2 * i];
y[1][i] = input_fft[SSS_POS_SYMBOL + 2 * i + 1]; y[1][i] = input_fft[q->fft_size/2-N_SSS + 2 * i + 1];
} }
vec_prod_ccc(y[0], q->fc_tables[q->N_id_2].c[0], z, N_SSS); vec_prod_ccc(y[0], q->fc_tables[q->N_id_2].c[0], z, N_SSS);
@ -100,12 +100,14 @@ void sss_synch_m0m1(sss_synch_t *q, cf_t *input, int *m0, float *m0_value,
if (m1_value) { if (m1_value) {
*m1_value = tmp_real[*m1]; *m1_value = tmp_real[*m1];
} }
ret = LIBLTE_SUCCESS;
}
return ret;
} }
void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in) { void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in) {
int i, j; uint32_t i, j;
bzero(fc_tables, sizeof(struct fc_tables));
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
for (j = 0; j < N_SSS; j++) { for (j = 0; j < N_SSS; j++) {
__real__ fc_tables->z1[i][j] = (float) in->z1[i][j]; __real__ fc_tables->z1[i][j] = (float) in->z1[i][j];

@ -58,19 +58,19 @@ void generate_zsc_tilde(int *z_tilde, int *s_tilde, int *c_tilde) {
z_tilde[i] = 1 - 2 * x[i]; z_tilde[i] = 1 - 2 * x[i];
} }
void generate_m0m1(int N_id_1, int *m0, int *m1) { void generate_m0m1(uint32_t N_id_1, uint32_t *m0, uint32_t *m1) {
int q_prime = N_id_1 / (N_SSS - 1); uint32_t q_prime = N_id_1 / (N_SSS - 1);
int q = (N_id_1 + (q_prime * (q_prime + 1) / 2)) / (N_SSS - 1); uint32_t q = (N_id_1 + (q_prime * (q_prime + 1) / 2)) / (N_SSS - 1);
int m_prime = N_id_1 + (q * (q + 1) / 2); uint32_t m_prime = N_id_1 + (q * (q + 1) / 2);
*m0 = m_prime % N_SSS; *m0 = m_prime % N_SSS;
*m1 = (*m0 + m_prime / N_SSS + 1) % N_SSS; *m1 = (*m0 + m_prime / N_SSS + 1) % N_SSS;
} }
/* table[m0][m1-1]=N_id_1 */ /* table[m0][m1-1]=N_id_1 */
void generate_N_id_1_table(int table[30][30]) { void generate_N_id_1_table(uint32_t table[30][30]) {
int m0, m1; uint32_t m0, m1;
int N_id_1; uint32_t N_id_1;
for (N_id_1=0;N_id_1<168;N_id_1++) { for (N_id_1=0;N_id_1<168;N_id_1++) {
generate_m0m1(N_id_1, &m0, &m1); generate_m0m1(N_id_1, &m0, &m1);
table[m0][m1-1] = N_id_1; table[m0][m1-1] = N_id_1;
@ -78,60 +78,60 @@ void generate_N_id_1_table(int table[30][30]) {
} }
void generate_s(int *s, int *s_tilde, int m0_m1) { void generate_s(int *s, int *s_tilde, uint32_t m0_m1) {
int i; uint32_t i;
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
s[i] = s_tilde[(i + m0_m1) % N_SSS]; s[i] = s_tilde[(i + m0_m1) % N_SSS];
} }
} }
void generate_s_all(int s[N_SSS][N_SSS], int *s_tilde) { void generate_s_all(int s[N_SSS][N_SSS], int *s_tilde) {
int i; uint32_t i;
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
generate_s(s[i], s_tilde, i); generate_s(s[i], s_tilde, i);
} }
} }
void generate_c(int *c, int *c_tilde, int N_id_2, int is_c0) { void generate_c(int *c, int *c_tilde, uint32_t N_id_2, bool is_c0) {
int i; uint32_t i;
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
c[i] = c_tilde[(i + N_id_2 + (is_c0 > 0 ? 3 : 0)) % N_SSS]; c[i] = c_tilde[(i + N_id_2 + (is_c0 ? 3 : 0)) % N_SSS];
} }
} }
void generate_z(int *z, int *z_tilde, int m0_m1) { void generate_z(int *z, int *z_tilde, uint32_t m0_m1) {
int i; uint32_t i;
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
z[i] = z_tilde[(i + (m0_m1 % 8)) % N_SSS]; z[i] = z_tilde[(i + (m0_m1 % 8)) % N_SSS];
} }
} }
void generate_z_all(int z[N_SSS][N_SSS], int *z_tilde) { void generate_z_all(int z[N_SSS][N_SSS], int *z_tilde) {
int i; uint32_t i;
for (i = 0; i < N_SSS; i++) { for (i = 0; i < N_SSS; i++) {
generate_z(z[i], z_tilde, i); generate_z(z[i], z_tilde, i);
} }
} }
void generate_sss_all_tables(struct sss_tables *tables, int N_id_2) { void generate_sss_all_tables(struct sss_tables *tables, uint32_t N_id_2) {
int i; uint32_t i;
int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS]; int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS];
generate_zsc_tilde(z_t, s_t, c_t); generate_zsc_tilde(z_t, s_t, c_t);
generate_s_all(tables->s, s_t); generate_s_all(tables->s, s_t);
generate_z_all(tables->z1, z_t); generate_z_all(tables->z1, z_t);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
generate_c(tables->c[i], c_t, N_id_2, i); generate_c(tables->c[i], c_t, N_id_2, i > 0);
} }
} }
void sss_generate(float *signal0, float *signal5, int cell_id) { void sss_generate(float *signal0, float *signal5, uint32_t cell_id) {
int i; uint32_t i;
int id1 = cell_id / 3; uint32_t id1 = cell_id / 3;
int id2 = cell_id % 3; uint32_t id2 = cell_id % 3;
int m0; uint32_t m0;
int m1; uint32_t m1;
int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS]; int s_t[N_SSS], c_t[N_SSS], z_t[N_SSS];
int s0[N_SSS], s1[N_SSS], c0[N_SSS], c1[N_SSS], z1_0[N_SSS], z1_1[N_SSS]; int s0[N_SSS], s1[N_SSS], c0[N_SSS], c1[N_SSS], z1_0[N_SSS], z1_1[N_SSS];

@ -37,144 +37,142 @@
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/convolution.h" #include "liblte/phy/utils/convolution.h"
#define NOT_SYNC 0xF0F0F0F0
int pss_synch_init_N_id_2(pss_synch_t *q, int N_id_2) {
q->N_id_2 = N_id_2;
int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_size) {
dft_plan_t plan; dft_plan_t plan;
cf_t pss_signal_pad[PSS_LEN_FREQ]; cf_t pss_signal_pad[2048];
cf_t pss_signal_time[PSS_LEN]; cf_t pss_signal_time[PSS_LEN];
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (N_id_2 < 0 || N_id_2 > 2) { if (lte_N_id_2_isvalid(N_id_2) &&
fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); fft_size < 2048)
return -1; {
}
pss_generate(pss_signal_time, N_id_2); pss_generate(pss_signal_time, N_id_2);
memset(pss_signal_pad, 0, PSS_LEN_FREQ * sizeof(cf_t)); bzero(pss_signal_pad, fft_size * sizeof(cf_t));
memset(q->pss_signal_freq[N_id_2], 0, PSS_LEN_FREQ * sizeof(cf_t)); bzero(pss_signal_freq, fft_size * sizeof(cf_t));
memcpy(&pss_signal_pad[33], pss_signal_time, PSS_LEN * sizeof(cf_t)); memcpy(&pss_signal_pad[(fft_size-PSS_LEN)/2], pss_signal_time, PSS_LEN * sizeof(cf_t));
if (dft_plan(&plan, PSS_LEN_FREQ - 1, BACKWARD, COMPLEX)) { if (dft_plan(&plan, fft_size, BACKWARD, COMPLEX)) {
return -1; return LIBLTE_ERROR;
} }
dft_plan_set_mirror(&plan, true); dft_plan_set_mirror(&plan, true);
dft_plan_set_dc(&plan, true); dft_plan_set_dc(&plan, true);
dft_run_c(&plan, pss_signal_pad, q->pss_signal_freq[q->N_id_2]); dft_run_c(&plan, pss_signal_pad, pss_signal_freq);
vec_sc_prod_cfc(q->pss_signal_freq[q->N_id_2], (float) 1 / (PSS_LEN_FREQ - 1), vec_sc_prod_cfc(pss_signal_freq, (float) 1 / (fft_size), pss_signal_pad, fft_size);
pss_signal_pad, PSS_LEN_FREQ); vec_conj_cc(pss_signal_pad, pss_signal_freq, fft_size);
vec_conj_cc(pss_signal_pad, q->pss_signal_freq[q->N_id_2], PSS_LEN_FREQ);
dft_plan_free(&plan); dft_plan_free(&plan);
return 0; ret = LIBLTE_SUCCESS;
}
return ret;
} }
/* Initializes the object. subframe_size is the size, in samples, of the 1ms subframe /* Initializes the PSS synchronization object with fft_size=128
*/
int pss_synch_init(pss_synch_t *q, uint32_t frame_size) {
return pss_synch_init_fft(q, frame_size, 128);
}
/* Initializes the PSS synchronization object.
* *
* It correlates a signal of frame_size samples with the PSS sequence in the frequency
* domain. The PSS sequence is transformed using fft_size samples.
*/ */
int pss_synch_init(pss_synch_t *q, int frame_size) { int pss_synch_init_fft(pss_synch_t *q, uint32_t frame_size, uint32_t fft_size) {
int ret = -1; int ret = LIBLTE_ERROR_INVALID_INPUTS;
int N_id_2;
if (q != NULL) {
uint32_t N_id_2;
uint32_t buffer_size;
bzero(q, sizeof(pss_synch_t)); bzero(q, sizeof(pss_synch_t));
q->conv_abs = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(float)); q->N_id_2 = 10;
q->fft_size = fft_size;
q->frame_size = frame_size;
buffer_size = fft_size + frame_size + 1;
q->conv_abs = vec_malloc(buffer_size * sizeof(float));
if (!q->conv_abs) { if (!q->conv_abs) {
fprintf(stderr, "Error allocating memory\n"); fprintf(stderr, "Error allocating memory\n");
goto clean_and_exit; goto clean_and_exit;
} }
q->tmp_input = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); q->tmp_input = vec_malloc(buffer_size * sizeof(cf_t));
if (!q->tmp_input) { if (!q->tmp_input) {
fprintf(stderr, "Error allocating memory\n"); fprintf(stderr, "Error allocating memory\n");
goto clean_and_exit; goto clean_and_exit;
} }
q->conv_output = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); q->conv_output = vec_malloc(buffer_size * sizeof(cf_t));
if (!q->conv_output) { if (!q->conv_output) {
fprintf(stderr, "Error allocating memory\n"); fprintf(stderr, "Error allocating memory\n");
goto clean_and_exit; goto clean_and_exit;
} }
for (N_id_2=0;N_id_2<3;N_id_2++) { for (N_id_2=0;N_id_2<3;N_id_2++) {
q->pss_signal_freq[N_id_2] = vec_malloc((PSS_LEN_FREQ + frame_size) * sizeof(cf_t)); q->pss_signal_freq[N_id_2] = vec_malloc(buffer_size * sizeof(cf_t));
if (!q->pss_signal_freq[N_id_2]) { if (!q->pss_signal_freq[N_id_2]) {
fprintf(stderr, "Error allocating memory\n"); fprintf(stderr, "Error allocating memory\n");
goto clean_and_exit; goto clean_and_exit;
} }
if (pss_synch_init_N_id_2(q, N_id_2)) { /* The PSS is translated into the frequency domain for each N_id_2 */
fprintf(stderr, "Error initiating PSS detector for N_id_2=%d\n", N_id_2); if (pss_synch_init_N_id_2(q->pss_signal_freq[N_id_2], N_id_2, fft_size)) {
fprintf(stderr, "Error initiating PSS detector for N_id_2=%d fft_size=%d\n", N_id_2, fft_size);
goto clean_and_exit; goto clean_and_exit;
} }
} }
#ifdef ENABLE_SF
q->frame_buffer = vec_malloc(4 * frame_size * sizeof(cf_t));
if (!q->frame_buffer) {
fprintf(stderr, "Error allocating memory\n");
goto clean_and_exit;
}
q->correlation_threshold = DEFAULT_CORRELATION_TH;
q->nosync_timeout_frames = DEFAULT_NOSYNC_TIMEOUT;
q->cfo_auto = true;
q->frame_start_idx = NOT_SYNC;
q->fb_wp = 0;
#endif
#ifdef CONVOLUTION_FFT #ifdef CONVOLUTION_FFT
if (conv_fft_cc_init(&q->conv_fft, frame_size, PSS_LEN_FREQ)) { if (conv_fft_cc_init(&q->conv_fft, frame_size, fft_size)) {
fprintf(stderr, "Error initiating convolution FFT\n"); fprintf(stderr, "Error initiating convolution FFT\n");
goto clean_and_exit; goto clean_and_exit;
} }
#endif #endif
q->N_id_2 = -1; ret = LIBLTE_SUCCESS;
q->frame_size = frame_size; }
ret = 0; clean_and_exit:
clean_and_exit: if (ret == -1) { if (ret == LIBLTE_ERROR) {
pss_synch_free(q); pss_synch_free(q);
} }
return ret; return ret;
} }
void pss_synch_free(pss_synch_t *q) { void pss_synch_free(pss_synch_t *q) {
int i; uint32_t i;
if (q) {
for (i=0;i<3;i++) { for (i=0;i<3;i++) {
if (q->pss_signal_freq[i]) { if (q->pss_signal_freq[i]) {
free(q->pss_signal_freq[i]); free(q->pss_signal_freq[i]);
} }
} }
if (q->conv_abs) { #ifdef CONVOLUTION_FFT
free(q->conv_abs); conv_fft_cc_free(&q->conv_fft);
}
#endif
if (q->tmp_input) { if (q->tmp_input) {
free(q->tmp_input); free(q->tmp_input);
} }
if (q->conv_output) { if (q->conv_output) {
free(q->conv_output); free(q->conv_output);
} }
if (q->conv_abs) {
#ifdef ENABLE_SF free(q->conv_abs);
if (q->frame_buffer) {
free(q->frame_buffer);
} }
#endif
#ifdef CONVOLUTION_FFT
conv_fft_cc_free(&q->conv_fft);
#endif
bzero(q, sizeof(pss_synch_t)); bzero(q, sizeof(pss_synch_t));
} }
}
/** /**
* This function calculates the Zadoff-Chu sequence. * This function calculates the Zadoff-Chu sequence.
* @param signal Output array. * @param signal Output array.
*/ */
int pss_generate(cf_t *signal, int N_id_2) { int pss_generate(cf_t *signal, uint32_t N_id_2) {
int i; int i;
float arg; float arg;
const float root_value[] = { 25.0, 29.0, 34.0 }; const float root_value[] = { 25.0, 29.0, 34.0 };
@ -182,7 +180,7 @@ int pss_generate(cf_t *signal, int N_id_2) {
int sign = -1; int sign = -1;
if (N_id_2 < 0 || N_id_2 > 2) { if (N_id_2 > 2) {
fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2);
return -1; return -1;
} }
@ -206,7 +204,7 @@ int pss_generate(cf_t *signal, int N_id_2) {
/** 36.211 10.3 section 6.11.1.2 /** 36.211 10.3 section 6.11.1.2
*/ */
void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp) { void pss_put_slot(cf_t *pss_signal, cf_t *slot, uint32_t nof_prb, lte_cp_t cp) {
int k; int k;
k = (CP_NSYMB(cp) - 1) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; k = (CP_NSYMB(cp) - 1) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31;
memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); memset(&slot[k - 5], 0, 5 * sizeof(cf_t));
@ -217,8 +215,8 @@ void pss_put_slot(cf_t *pss_signal, cf_t *slot, int nof_prb, lte_cp_t cp) {
/** Sets the current N_id_2 value. Returns -1 on error, 0 otherwise /** Sets the current N_id_2 value. Returns -1 on error, 0 otherwise
*/ */
int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) { int pss_synch_set_N_id_2(pss_synch_t *q, uint32_t N_id_2) {
if (N_id_2 < 0 || N_id_2 > 2) { if (!lte_N_id_2_isvalid((N_id_2))) {
fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2);
return -1; return -1;
} else { } else {
@ -227,27 +225,38 @@ int pss_synch_set_N_id_2(pss_synch_t *q, int N_id_2) {
} }
} }
/** Returns the index of the PSS correlation peak in a subframe. /** Returns the index of the PSS correlation peak in a subframe.
* The frame starts at corr_peak_pos-subframe_size/2. * The frame starts at corr_peak_pos-subframe_size/2.
* The value of the correlation is stored in corr_peak_value. * The value of the correlation is stored in corr_peak_value.
* *
* Input buffer must be subframe_size long. * Input buffer must be subframe_size long.
*/ */
int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value, int pss_synch_find_pss(pss_synch_t *q, cf_t *input,
float *corr_mean_value) { float *corr_peak_value, float *corr_mean_value)
int corr_peak_pos; {
int conv_output_len; int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
input != NULL)
{
uint32_t corr_peak_pos;
uint32_t conv_output_len;
if (!lte_N_id_2_isvalid(q->N_id_2)) {
fprintf(stderr, "Error finding PSS peak, N_id_2 not set\n");
return LIBLTE_ERROR;
}
memset(&q->pss_signal_freq[q->N_id_2][PSS_LEN_FREQ], 0, q->frame_size * sizeof(cf_t)); bzero(&q->pss_signal_freq[q->N_id_2][q->fft_size], q->frame_size * sizeof(cf_t));
memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t));
memset(&q->tmp_input[q->frame_size], 0, PSS_LEN_FREQ * sizeof(cf_t)); bzero(&q->tmp_input[q->frame_size], q->fft_size * sizeof(cf_t));
#ifdef CONVOLUTION_FFT #ifdef CONVOLUTION_FFT
conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input,
q->pss_signal_freq[q->N_id_2], q->conv_output); q->pss_signal_freq[q->N_id_2], q->conv_output);
#else #else
conv_output_len = conv_cc(input, q->pss_signal_freq[q->N_id_2], q->conv_output, q->frame_size, PSS_LEN_FREQ); conv_output_len = conv_cc(input, q->pss_signal_freq[q->N_id_2], q->conv_output, q->frame_size, q->fft_size);
#endif #endif
vec_abs_cf(q->conv_output, q->conv_abs, conv_output_len); vec_abs_cf(q->conv_output, q->conv_abs, conv_output_len);
@ -260,7 +269,9 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value,
/ conv_output_len; / conv_output_len;
} }
return (int) corr_peak_pos; ret = (int) corr_peak_pos;
}
return ret;
} }
/* Returns the CFO estimation given a PSS received sequence /* Returns the CFO estimation given a PSS received sequence
@ -270,159 +281,12 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value,
*/ */
float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv) { float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv) {
cf_t y0, y1, yr; cf_t y0, y1, yr;
cf_t y[PSS_LEN_FREQ - 1];
vec_prod_ccc_unalign(q->pss_signal_freq[q->N_id_2], pss_recv, y, PSS_LEN_FREQ - 1); y0 = vec_dot_prod_ccc(q->pss_signal_freq[q->N_id_2], pss_recv, q->fft_size/2);
y1 = vec_dot_prod_ccc(&q->pss_signal_freq[q->N_id_2][q->fft_size/2], &pss_recv[q->fft_size/2], q->fft_size/2);
y0 = vec_acc_cc(y, (PSS_LEN_FREQ - 1) / 2);
y1 = vec_acc_cc(&y[(PSS_LEN_FREQ - 1) / 2], (PSS_LEN_FREQ - 1) / 2);
yr = conjf(y0) * y1; yr = conjf(y0) * y1;
return atan2f(__imag__ yr, __real__ yr) / M_PI; return atan2f(__imag__ yr, __real__ yr) / M_PI;
} }
#ifdef ENABLE_SF
void pss_synch_set_timeout(pss_synch_t *q, int nof_frames) {
q->nosync_timeout_frames = nof_frames;
}
void pss_synch_set_threshold(pss_synch_t *q, float threshold) {
q->correlation_threshold = threshold;
}
void pss_synch_set_cfo_mode(pss_synch_t *q, bool cfo_auto) {
q->cfo_auto = cfo_auto;
}
float pss_synch_get_cfo(pss_synch_t *q) {
return q->current_cfo;
}
int pss_synch_get_frame_start_idx(pss_synch_t *q) {
return q->frame_start_idx;
}
/** This function is designed to be called periodically on a subframe basis.
* The function finds the PSS correlation peak and computes (does not adjust) CFO automatically as defined by
* pss_synch_set_cfo_mode().
*
* If the PSS sequence is not found, returns 0 writes nothing to the output buffer.
* If the PSS sequence is found, aligns the beginning of the subframe to the output buffer and returns the number of samples
* written to the output buffer.
* If synchronized, subsequent calls to this function align the input buffer to the subframe beginning.
*/
int pss_synch_frame(pss_synch_t *q, cf_t *input, cf_t *output, int nsamples) {
int max_idx, tmp_start_idx;
int retval;
float max_value;
if (nsamples != q->frame_size) {
fprintf(stderr, "Configured for frame size %d but got %d samples\n",
q->frame_size, nsamples);
return -1;
}
if (q->N_id_2 < 0) {
fprintf(stderr,
"N_id_2 must be configured before calling pss_synch()\n");
return -1;
}
max_idx = pss_synch_find_pss(q, input, &max_value, NULL);
if (max_value > q->correlation_threshold) {
tmp_start_idx = max_idx - nsamples / 2;
if (q->frame_start_idx != tmp_start_idx) {
printf("Re-synchronizing: new index is %d, old was %d\n",
tmp_start_idx, q->frame_start_idx);
}
q->frame_start_idx = tmp_start_idx;
} else {
if (q->nosync_timeout_frames > 0) {
q->nof_nosync_frames++;
if (q->nof_nosync_frames >= q->nosync_timeout_frames) {
q->frame_start_idx = NOT_SYNC;
}
}
}
if (q->frame_start_idx == NOT_SYNC) {
memcpy(q->frame_buffer, input, nsamples * sizeof(cf_t));
retval = 0;
} else if (q->frame_start_idx > 0) {
if (q->fb_wp) {
memcpy(&q->frame_buffer[(nsamples - q->frame_start_idx)], input,
q->frame_start_idx * sizeof(cf_t));
memcpy(output, q->frame_buffer, nsamples * sizeof(cf_t));
retval = nsamples;
} else {
retval = 0;
}
memcpy(q->frame_buffer, &input[q->frame_start_idx],
(nsamples - q->frame_start_idx) * sizeof(cf_t));
q->fb_wp = 1;
} else {
memcpy(output, &q->frame_buffer[nsamples + q->frame_start_idx],
(-q->frame_start_idx) * sizeof(cf_t));
memcpy(&output[-q->frame_start_idx], input,
(nsamples + q->frame_start_idx) * sizeof(cf_t));
memcpy(&q->frame_buffer[nsamples + q->frame_start_idx],
&input[nsamples + q->frame_start_idx],
(-q->frame_start_idx) * sizeof(cf_t));
retval = nsamples;
}
if (q->frame_start_idx != NOT_SYNC && q->cfo_auto && retval) {
q->current_cfo = pss_synch_cfo_compute(q,
&output[q->frame_size / 2 - PSS_LEN_FREQ + 1]);
}
return retval;
}
/** High-level API */
int pss_synch_initialize(pss_synch_hl* h) {
int fs = h->init.frame_size;
if (!fs) {
fs = DEFAULT_FRAME_SIZE;
}
if (pss_synch_init(&h->obj, fs)) {
return -1;
}
if (h->init.unsync_nof_pkts) {
pss_synch_set_timeout(&h->obj, h->init.unsync_nof_pkts);
}
pss_synch_set_N_id_2(&h->obj, h->init.N_id_2);
if (h->init.do_cfo) {
pss_synch_set_cfo_mode(&h->obj, true);
} else {
pss_synch_set_cfo_mode(&h->obj, false);
}
return 0;
}
int pss_synch_work(pss_synch_hl* hl) {
if (hl->ctrl_in.correlation_threshold) {
pss_synch_set_threshold(&hl->obj, hl->ctrl_in.correlation_threshold);
}
hl->out_len = pss_synch_frame(&hl->obj, hl->input, hl->output, hl->in_len);
return 0;
}
int pss_synch_stop(pss_synch_hl* hl) {
pss_synch_free(&hl->obj);
return 0;
}
#endif

@ -35,29 +35,58 @@
#include "liblte/phy/sync/sss.h" #include "liblte/phy/sync/sss.h"
#include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/dft.h"
#include "liblte/phy/utils/convolution.h" #include "liblte/phy/utils/convolution.h"
#include "liblte/phy/utils/vector.h"
void generate_sss_all_tables(struct sss_tables *tables, int N_id_2); void generate_sss_all_tables(struct sss_tables *tables, uint32_t N_id_2);
void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in); void convert_tables(struct fc_tables *fc_tables, struct sss_tables *in);
void generate_N_id_1_table(int table[30][30]); void generate_N_id_1_table(uint32_t table[30][30]);
int sss_synch_init(sss_synch_t *q) { int sss_synch_init(sss_synch_t *q, uint32_t fft_size) {
int N_id_2;
if (q != NULL &&
fft_size < 2048)
{
uint32_t N_id_2;
struct sss_tables sss_tables; struct sss_tables sss_tables;
bzero(q, sizeof(sss_synch_t)); bzero(q, sizeof(sss_synch_t));
if (dft_plan(&q->dftp_input, SSS_DFT_LEN, FORWARD, COMPLEX)) { if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) {
return -1; sss_synch_free(q);
return LIBLTE_ERROR;
} }
q->fft_size = fft_size;
generate_N_id_1_table(q->N_id_1_table); generate_N_id_1_table(q->N_id_1_table);
dft_plan_set_mirror(&q->dftp_input, true); dft_plan_set_mirror(&q->dftp_input, true);
dft_plan_set_dc(&q->dftp_input, true); dft_plan_set_dc(&q->dftp_input, true);
for (N_id_2=0;N_id_2<3;N_id_2++) { for (N_id_2=0;N_id_2<3;N_id_2++) {
generate_sss_all_tables(&sss_tables, N_id_2); generate_sss_all_tables(&sss_tables, N_id_2);
convert_tables(&q->fc_tables[N_id_2], &sss_tables); convert_tables(&q->fc_tables[N_id_2], &sss_tables);
} }
q->N_id_2 = 0; q->N_id_2 = 0;
return 0; return LIBLTE_SUCCESS;
}
return LIBLTE_ERROR_INVALID_INPUTS;
}
int sss_synch_realloc(sss_synch_t *q, uint32_t fft_size) {
if (q != NULL &&
fft_size < 2048)
{
dft_plan_free(&q->dftp_input);
if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) {
sss_synch_free(q);
return LIBLTE_ERROR;
}
dft_plan_set_mirror(&q->dftp_input, true);
dft_plan_set_dc(&q->dftp_input, true);
q->fft_size = fft_size;
return LIBLTE_SUCCESS;
}
return LIBLTE_ERROR_INVALID_INPUTS;
} }
void sss_synch_free(sss_synch_t *q) { void sss_synch_free(sss_synch_t *q) {
@ -66,22 +95,24 @@ void sss_synch_free(sss_synch_t *q) {
} }
/** Sets the N_id_2 to search for */ /** Sets the N_id_2 to search for */
int sss_synch_set_N_id_2(sss_synch_t *q, int N_id_2) { int sss_synch_set_N_id_2(sss_synch_t *q, uint32_t N_id_2) {
if (N_id_2 < 0 || N_id_2 > 2) { if (!lte_N_id_2_isvalid(N_id_2)) {
fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2);
return -1; return LIBLTE_ERROR;
} else { } else {
q->N_id_2 = N_id_2; q->N_id_2 = N_id_2;
return 0; return LIBLTE_SUCCESS;
} }
} }
/** 36.211 10.3 section 6.11.2.2 /** 36.211 10.3 section 6.11.2.2
*/ */
void sss_put_slot(float *sss, cf_t *slot, int nof_prb, lte_cp_t cp) { void sss_put_slot(float *sss, cf_t *slot, uint32_t nof_prb, lte_cp_t cp) {
int i, k; uint32_t i, k;
k = (CP_NSYMB(cp) - 2) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31; k = (CP_NSYMB(cp) - 2) * nof_prb * RE_X_RB + nof_prb * RE_X_RB / 2 - 31;
if (k > 5) {
memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); memset(&slot[k - 5], 0, 5 * sizeof(cf_t));
for (i = 0; i < SSS_LEN; i++) { for (i = 0; i < SSS_LEN; i++) {
__real__ slot[k + i] = sss[i]; __real__ slot[k + i] = sss[i];
@ -89,48 +120,6 @@ void sss_put_slot(float *sss, cf_t *slot, int nof_prb, lte_cp_t cp) {
} }
memset(&slot[k + SSS_LEN], 0, 5 * sizeof(cf_t)); memset(&slot[k + SSS_LEN], 0, 5 * sizeof(cf_t));
} }
/* In this function, input points to the beginning of the subframe. Saves result in subframe_idx and N_id_1
* Return 1 if the sequence was found, 0 if the peak is not found, -1 if the subframe_sz or symbol_sz are
* invalid or not configured.
* Before calling this function, the correlation threshold and symbol size duration need to be set
* using sss_synch_set_threshold() and sss_synch_set_symbol_sz().
*/
int sss_synch_frame(sss_synch_t *q, cf_t *input, int *subframe_idx, int *N_id_1) {
int m0, m1;
float m0_value, m1_value;
if (q->subframe_sz <= 0 || q->symbol_sz <= 0) {
return -1;
}
sss_synch_m0m1(q, &input[SSS_SYMBOL_ST(q->subframe_sz, q->symbol_sz)], &m0,
&m0_value, &m1, &m1_value);
if (m0_value > q->corr_peak_threshold
&& m1_value > q->corr_peak_threshold) {
if (subframe_idx) {
*subframe_idx = sss_synch_subframe(m0, m1);
}
if (N_id_1) {
*N_id_1 = sss_synch_N_id_1(q, m0, m1);
}
return 1;
} else {
return 0;
}
}
/** Used by sss_synch_frame() to compute the beginning of the SSS symbol
* symbol_sz MUST INCLUDE THE CYCLIC PREFIX SIZE
*/
void sss_synch_set_symbol_sz(sss_synch_t *q, int symbol_sz) {
q->symbol_sz = symbol_sz;
}
/** Used by sss_synch_frame() to compute the beginning of the SSS symbol */
void sss_synch_set_subframe_sz(sss_synch_t *q, int subframe_sz) {
q->subframe_sz = subframe_sz;
} }
/** Sets the SSS correlation peak detection threshold */ /** Sets the SSS correlation peak detection threshold */
@ -139,7 +128,7 @@ void sss_synch_set_threshold(sss_synch_t *q, float threshold) {
} }
/** Returns the subframe index based on the m0 and m1 values */ /** Returns the subframe index based on the m0 and m1 values */
int sss_synch_subframe(int m0, int m1) { uint32_t sss_synch_subframe(uint32_t m0, uint32_t m1) {
if (m1 > m0) { if (m1 > m0) {
return 0; return 0;
} else { } else {
@ -148,9 +137,9 @@ int sss_synch_subframe(int m0, int m1) {
} }
/** Returns the N_id_1 value based on the m0 and m1 values */ /** Returns the N_id_1 value based on the m0 and m1 values */
int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1) { int sss_synch_N_id_1(sss_synch_t *q, uint32_t m0, uint32_t m1) {
if (m0 < 0 || m0 > 29 || m1 < 0 || m1 > 29) { if (m0==m1 || m0 > 29 || m1 > 29) {
return -1; return LIBLTE_ERROR;
} }
if (m1 > m0) { if (m1 > m0) {
return q->N_id_1_table[m0][m1 - 1]; return q->N_id_1_table[m0][m1 - 1];
@ -163,12 +152,12 @@ int sss_synch_N_id_1(sss_synch_t *q, int m0, int m1) {
int sss_synch_initialize(sss_synch_hl* h) { int sss_synch_initialize(sss_synch_hl* h) {
if (sss_synch_init(&h->obj)) { if (sss_synch_init(&h->obj, 128)) {
return -1; return LIBLTE_ERROR;
} }
sss_synch_set_N_id_2(&h->obj, h->init.N_id_2); sss_synch_set_N_id_2(&h->obj, h->init.N_id_2);
return 0; return LIBLTE_SUCCESS;
} }
int sss_synch_work(sss_synch_hl* hl) { int sss_synch_work(sss_synch_hl* hl) {
@ -176,20 +165,12 @@ int sss_synch_work(sss_synch_hl* hl) {
if (hl->ctrl_in.correlation_threshold) { if (hl->ctrl_in.correlation_threshold) {
sss_synch_set_threshold(&hl->obj, hl->ctrl_in.correlation_threshold); sss_synch_set_threshold(&hl->obj, hl->ctrl_in.correlation_threshold);
} }
if (hl->ctrl_in.subframe_sz) {
sss_synch_set_subframe_sz(&hl->obj, hl->ctrl_in.subframe_sz);
}
if (hl->ctrl_in.symbol_sz) {
sss_synch_set_symbol_sz(&hl->obj, hl->ctrl_in.symbol_sz);
}
sss_synch_frame(&hl->obj, hl->input, &hl->ctrl_out.subframe_idx,
&hl->ctrl_out.N_id_1);
return 0; return LIBLTE_SUCCESS;
} }
int sss_synch_stop(sss_synch_hl* hl) { int sss_synch_stop(sss_synch_hl* hl) {
sss_synch_free(&hl->obj); sss_synch_free(&hl->obj);
return 0; return LIBLTE_SUCCESS;
} }

@ -25,44 +25,106 @@
* *
*/ */
#include <strings.h> #include <strings.h>
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/sync/sync.h" #include "liblte/phy/sync/sync.h"
#include "liblte/phy/utils/vector.h"
static bool fft_size_isvalid(uint32_t fft_size) {
if (fft_size >= FFT_SIZE_MIN && fft_size <= FFT_SIZE_MAX && (fft_size%64) == 0) {
return true;
} else {
return false;
}
}
int sync_init(sync_t *q, int frame_size) { int sync_init(sync_t *q, uint32_t find_frame_size, uint32_t track_frame_size, uint32_t fft_size) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
find_frame_size > fft_size &&
find_frame_size < 307200 &&
fft_size_isvalid(fft_size))
{
bzero(q, sizeof(sync_t)); bzero(q, sizeof(sync_t));
q->threshold = 1.5;
q->pss_mode = PEAK_MEAN; q->pss_mode = PEAK_MEAN;
q->detect_cp = true; q->detect_cp = true;
q->sss_en = true; q->sss_en = true;
q->N_id_2 = 1000;
q->N_id_1 = 1000;
q->fft_size = fft_size;
q->find_frame_size = find_frame_size;
if (pss_synch_init(&q->pss, frame_size)) { if (pss_synch_init_fft(&q->pss_find, find_frame_size, fft_size)) {
fprintf(stderr, "Error initializing PSS object\n"); fprintf(stderr, "Error initializing PSS object\n");
return -1; return LIBLTE_ERROR;
}
if (sss_synch_init(&q->sss, fft_size)) {
fprintf(stderr, "Error initializing SSS object\n");
return LIBLTE_ERROR;
} }
if (pss_synch_init(&q->pss_track, TRACK_LEN)) { if (pss_synch_init_fft(&q->pss_track, track_frame_size, fft_size)) {
fprintf(stderr, "Error initializing PSS track object\n"); fprintf(stderr, "Error initializing PSS track object\n");
return -1; return LIBLTE_ERROR;
} }
if (sss_synch_init(&q->sss)) {
fprintf(stderr, "Error initializing SSS object\n"); DEBUG("SYNC init with find_frame_size=%d and fft_size=%d\n", find_frame_size, fft_size);
return -1;
ret = LIBLTE_SUCCESS;
}
return ret;
} }
DEBUG("PSS and SSS initiated\n",0);
return 0; int sync_realloc(sync_t *q, uint32_t find_frame_size, uint32_t track_frame_size,
uint32_t fft_size)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
find_frame_size > fft_size &&
find_frame_size < 307200 &&
fft_size_isvalid(fft_size))
{
q->N_id_2 = 1000;
q->N_id_1 = 1000;
q->fft_size = fft_size;
q->find_frame_size = find_frame_size;
pss_synch_free(&q->pss_find);
if (pss_synch_init_fft(&q->pss_find, find_frame_size, fft_size)) {
fprintf(stderr, "Error initializing PSS object\n");
return LIBLTE_ERROR;
}
pss_synch_free(&q->pss_track);
if (pss_synch_init_fft(&q->pss_track, track_frame_size, fft_size)) {
fprintf(stderr, "Error initializing PSS track object\n");
return LIBLTE_ERROR;
}
if (sss_synch_realloc(&q->sss, fft_size)) {
fprintf(stderr, "Error realloc'ing SSS object\n");
return LIBLTE_ERROR;
}
DEBUG("SYNC init with find_frame_size=%d and fft_size=%d\n", find_frame_size, fft_size);
ret = LIBLTE_SUCCESS;
}
return ret;
} }
void sync_free(sync_t *q) { void sync_free(sync_t *q) {
pss_synch_free(&q->pss); if (q) {
pss_synch_free(&q->pss_track); pss_synch_free(&q->pss_track);
pss_synch_free(&q->pss_find);
sss_synch_free(&q->sss); sss_synch_free(&q->sss);
} }
}
void sync_pss_det_absolute(sync_t *q) { void sync_pss_det_absolute(sync_t *q) {
q->pss_mode = ABSOLUTE; q->pss_mode = ABSOLUTE;
@ -71,31 +133,42 @@ void sync_pss_det_peak_to_avg(sync_t *q) {
q->pss_mode = PEAK_MEAN; q->pss_mode = PEAK_MEAN;
} }
void sync_set_threshold(sync_t *q, float threshold) { void sync_set_threshold(sync_t *q, float find_threshold, float track_threshold) {
q->threshold = threshold; q->find_threshold = find_threshold;
q->track_threshold = track_threshold;
} }
void sync_sss_en(sync_t *q, bool enabled) { void sync_sss_en(sync_t *q, bool enabled) {
q->sss_en = enabled; q->sss_en = enabled;
} }
bool sync_sss_detected(sync_t *q) {
return lte_N_id_1_isvalid(q->N_id_1);
}
int sync_get_cell_id(sync_t *q) { int sync_get_cell_id(sync_t *q) {
if (q->N_id_1 >=0 && q->N_id_2 >= 0) { if (q->N_id_2 != 10) {
if (lte_N_id_2_isvalid(q->N_id_2) && lte_N_id_1_isvalid(q->N_id_1)) {
return q->N_id_1*3 + q->N_id_2; return q->N_id_1*3 + q->N_id_2;
} else { } else {
return -1; fprintf(stderr, "Error getting cell_id, invalid N_id_1 or N_id_2\n");
return LIBLTE_ERROR;
}
} else {
fprintf(stderr, "Error getting cell_id, N_id_2 not set\n");
return LIBLTE_ERROR;
} }
} }
int sync_get_N_id_1(sync_t *q) { uint32_t sync_get_N_id_1(sync_t *q) {
return q->N_id_1; return q->N_id_1;
} }
int sync_get_N_id_2(sync_t *q) { uint32_t sync_get_N_id_2(sync_t *q) {
return q->N_id_2; return q->N_id_2;
} }
int sync_get_slot_id(sync_t *q) { uint32_t sync_get_slot_id(sync_t *q) {
return q->slot_id; return q->slot_id;
} }
@ -115,57 +188,66 @@ lte_cp_t sync_get_cp(sync_t *q) {
return q->cp; return q->cp;
} }
int sync_sss(sync_t *q, cf_t *input, int N_id_2, int peak_pos, bool en_cp) { int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos, bool en_cp) {
int m0, m1, sss_idx_n, sss_idx_e; uint32_t m0, m1;
int sss_idx_n, sss_idx_e, ret;
float m0_value_e, m1_value_e,m0_value_n, m1_value_n; float m0_value_e, m1_value_e,m0_value_n, m1_value_n;
int slot_id_e, N_id_1_e, slot_id_n, N_id_1_n; uint32_t slot_id_e, N_id_1_e, slot_id_n, N_id_1_n;
sss_synch_set_N_id_2(&q->sss, N_id_2); sss_synch_set_N_id_2(&q->sss, q->N_id_2);
/* Make sure we have enough room to find SSS sequence */ /* Make sure we have enough room to find SSS sequence */
sss_idx_n = peak_pos-2*(128+CP(128,CPNORM_LEN)); sss_idx_n = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, CPNORM_LEN));
sss_idx_e = peak_pos-2*(128+CP(128,CPEXT_LEN)); sss_idx_e = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, CPEXT_LEN));
if (en_cp) { if (en_cp) {
if (sss_idx_n < 0 || sss_idx_e < 0) { if (sss_idx_n < 0 || sss_idx_e < 0) {
INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e); INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e);
return -1; return LIBLTE_SUCCESS;
} }
} else { } else {
if (CP_ISNORM(q->cp)) { if (CP_ISNORM(q->cp)) {
if (sss_idx_n < 0) { if (sss_idx_n < 0) {
INFO("Not enough room to decode SSS (%d)\n", sss_idx_n); INFO("Not enough room to decode normal CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx_n, peak_pos);
return -1; return LIBLTE_SUCCESS;
} }
} else { } else {
if (sss_idx_e < 0) { if (sss_idx_e < 0) {
INFO("Not enough room to decode SSS (%d)\n", sss_idx_e); INFO("Not enough room to decode extended CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx_e, peak_pos);
return -1; return LIBLTE_SUCCESS;
} }
} }
} }
N_id_1_e = -1; slot_id_n = 0;
N_id_1_n = -1; slot_id_e = 0;
slot_id_e = -1; N_id_1_n = 0;
slot_id_n = -1; N_id_1_e = 0;
/* try Normal CP length */ /* try Normal CP length */
if (en_cp || CP_ISNORM(q->cp)) { if (en_cp || CP_ISNORM(q->cp)) {
sss_synch_m0m1(&q->sss, &input[sss_idx_n], sss_synch_m0m1(&q->sss, &input[sss_idx_n], &m0, &m0_value_n, &m1, &m1_value_n);
&m0, &m0_value_n, &m1, &m1_value_n);
slot_id_n = 2 * sss_synch_subframe(m0, m1); slot_id_n = 2 * sss_synch_subframe(m0, m1);
N_id_1_n = sss_synch_N_id_1(&q->sss, m0, m1); ret = sss_synch_N_id_1(&q->sss, m0, m1);
if (ret >= 0) {
N_id_1_n = (uint32_t) ret;
} else {
N_id_1_n = 1000;
}
} }
if (en_cp || CP_ISEXT(q->cp)) { if (en_cp || CP_ISEXT(q->cp)) {
/* Now try Extended CP length */ /* Now try Extended CP length */
sss_synch_m0m1(&q->sss, &input[sss_idx_e], sss_synch_m0m1(&q->sss, &input[sss_idx_e], &m0, &m0_value_e, &m1, &m1_value_e);
&m0, &m0_value_e, &m1, &m1_value_e);
slot_id_e = 2 * sss_synch_subframe(m0, m1); slot_id_e = 2 * sss_synch_subframe(m0, m1);
N_id_1_e = sss_synch_N_id_1(&q->sss, m0, m1); ret = sss_synch_N_id_1(&q->sss, m0, m1);
if (ret >= 0) {
N_id_1_e = (uint32_t) ret;
} else {
N_id_1_e = 1000;
}
} }
/* Correlation with extended CP hypoteshis is greater than with normal? */ /* Correlation with extended CP hypoteshis is greater than with normal? */
@ -174,56 +256,93 @@ int sync_sss(sync_t *q, cf_t *input, int N_id_2, int peak_pos, bool en_cp) {
q->cp = CPEXT; q->cp = CPEXT;
q->slot_id = slot_id_e; q->slot_id = slot_id_e;
q->N_id_1 = N_id_1_e; q->N_id_1 = N_id_1_e;
/* then is normal CP */ /* otherwise is normal CP */
} else { } else {
q->cp = CPNORM; q->cp = CPNORM;
q->slot_id = slot_id_n; q->slot_id = slot_id_n;
q->N_id_1 = N_id_1_n; q->N_id_1 = N_id_1_n;
} }
INFO("SSS detected N_id_1=%d, slot_idx=%d, %s CP\n", DEBUG("SSS detected N_id_1=%d, slot_idx=%d, position=%d/%d %s CP\n",
q->N_id_1, q->slot_id, CP_ISNORM(q->cp)?"Normal":"Extended"); q->N_id_1, q->slot_id, sss_idx_n, sss_idx_e, CP_ISNORM(q->cp)?"Normal":"Extended");
return 0; return 1;
} }
int sync_track(sync_t *q, cf_t *input) { int sync_track(sync_t *q, cf_t *input, uint32_t offset, uint32_t *peak_position) {
float peak_value, mean_value;
int peak_detected = 0; int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
input != NULL &&
fft_size_isvalid(q->fft_size))
{
float peak_value, mean_value, *mean_ptr;
bool peak_detected;
uint32_t peak_pos;
pss_synch_set_N_id_2(&q->pss_track, q->N_id_2); pss_synch_set_N_id_2(&q->pss_track, q->N_id_2);
int peak_pos = pss_synch_find_pss(&q->pss, input, &peak_value, &mean_value); if (q->pss_mode == ABSOLUTE) {
mean_ptr = NULL;
} else {
mean_ptr = &mean_value;
}
peak_pos = pss_synch_find_pss(&q->pss_track, &input[offset], &peak_value, mean_ptr);
if (q->peak_to_avg > TRACK_THRESHOLD) { peak_detected = false;
peak_detected = 1; if (q->pss_mode == ABSOLUTE) {
if (peak_value > q->track_threshold) {
peak_detected = true;
} }
} else {
q->peak_to_avg = peak_value / mean_value;
if (q->peak_to_avg > q->track_threshold) {
peak_detected = true;
}
}
DEBUG("PSS possible tracking peak pos=%d peak=%.2f par=%.2f threshold=%.2f\n",
peak_pos, peak_value, q->peak_to_avg, q->track_threshold);
if (peak_detected) { if (peak_detected) {
q->cfo = pss_synch_cfo_compute(&q->pss_track, &input[offset+peak_pos-q->fft_size]);
q->cfo = pss_synch_cfo_compute(&q->pss, &input[peak_pos-128]);
if (q->sss_en) { if (q->sss_en) {
if (sync_sss(q, input, q->N_id_2, peak_pos, false)) { if (sync_sss(q, input, offset + peak_pos, false) < 0) {
return -1; fprintf(stderr, "Error synchronizing with SSS\n");
return LIBLTE_ERROR;
} }
} }
return peak_pos; if (peak_position) {
*peak_position = peak_pos;
}
ret = 1;
} else { } else {
return -1; ret = LIBLTE_SUCCESS;
}
} }
return ret;
} }
int sync_find(sync_t *q, cf_t *input) { int sync_find(sync_t *q, cf_t *input, uint32_t *peak_position) {
int N_id_2, peak_pos[3]; uint32_t N_id_2, peak_pos[3];
float peak_value[3]; float peak_value[3];
float mean_value[3]; float mean_value[3];
float max=-999; float max=-999;
int i; uint32_t i;
int peak_detected; int ret;
bool peak_detected;
for (N_id_2=0;N_id_2<3;N_id_2++) { for (N_id_2=0;N_id_2<3;N_id_2++) {
pss_synch_set_N_id_2(&q->pss, N_id_2); pss_synch_set_N_id_2(&q->pss_find, N_id_2);
peak_pos[N_id_2] = pss_synch_find_pss(&q->pss, input, &peak_value[N_id_2], &mean_value[N_id_2]); ret = pss_synch_find_pss(&q->pss_find, input, &peak_value[N_id_2], &mean_value[N_id_2]);
if (ret < 0) {
fprintf(stderr, "Error finding PSS for N_id_2=%d\n", N_id_2);
return LIBLTE_ERROR;
}
peak_pos[N_id_2] = (uint32_t) ret;
} }
for (i=0;i<3;i++) { for (i=0;i<3;i++) {
if (peak_value[i] > max) { if (peak_value[i] > max) {
@ -235,38 +354,43 @@ int sync_find(sync_t *q, cf_t *input) {
q->peak_to_avg = peak_value[N_id_2] / mean_value[N_id_2]; q->peak_to_avg = peak_value[N_id_2] / mean_value[N_id_2];
DEBUG("PSS possible peak N_id_2=%d, pos=%d peak=%.2f par=%.2f threshold=%.2f\n", DEBUG("PSS possible peak N_id_2=%d, pos=%d peak=%.2f par=%.2f threshold=%.2f\n",
N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold); N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->find_threshold);
/* If peak detected */ /* If peak detected */
peak_detected = 0; peak_detected = false;
if (peak_pos[N_id_2] - 128 >= 0) { if (peak_pos[N_id_2] > q->fft_size) {
if (q->pss_mode == ABSOLUTE) { if (q->pss_mode == ABSOLUTE) {
if (peak_value[N_id_2] > q->threshold) { if (peak_value[N_id_2] > q->find_threshold) {
peak_detected = 1; peak_detected = true;
} }
} else { } else {
if (q->peak_to_avg > q->threshold) { if (q->peak_to_avg > q->find_threshold) {
peak_detected = 1; peak_detected = true;
} }
} }
} }
if (peak_detected) { if (peak_detected) {
q->N_id_2 = N_id_2; q->N_id_2 = N_id_2;
pss_synch_set_N_id_2(&q->pss, q->N_id_2); pss_synch_set_N_id_2(&q->pss_find, q->N_id_2);
q->cfo = pss_synch_cfo_compute(&q->pss, &input[peak_pos[q->N_id_2]-128]); q->cfo = pss_synch_cfo_compute(&q->pss_find, &input[peak_pos[N_id_2]-q->fft_size]);
INFO("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2, DEBUG("PSS peak detected N_id_2=%d, pos=%d peak=%.2f par=%.2f th=%.2f cfo=%.4f\n", N_id_2,
peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->threshold, q->cfo); peak_pos[N_id_2], peak_value[N_id_2], q->peak_to_avg, q->find_threshold, q->cfo);
if (q->sss_en) { if (q->sss_en) {
if (sync_sss(q, input, q->N_id_2, peak_pos[q->N_id_2], q->detect_cp)) { if (sync_sss(q, input, peak_pos[q->N_id_2], q->detect_cp) < 0) {
return -1; fprintf(stderr, "Error synchronizing with SSS\n");
return LIBLTE_ERROR;
} }
} }
return peak_pos[N_id_2]; if (peak_position) {
*peak_position = peak_pos[N_id_2];
}
return 1;
} else { } else {
return -1; return LIBLTE_SUCCESS;
} }
} }

@ -1,244 +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 <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include "liblte/phy/resampling/decim.h"
#include "liblte/phy/resampling/resample_arb.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/sync/sync_frame.h"
int sync_frame_init(sync_frame_t *q, uint32_t downsampling) {
int ret = -1;
bzero(q, sizeof(sync_frame_t));
if(sync_init(&q->s, SYNC_SF_LEN)) {
goto clean_exit;
}
sync_pss_det_peak_to_avg(&q->s);
if (cfo_init(&q->cfocorr, SYNC_SF_LEN * downsampling)) {
fprintf(stderr, "Error initiating CFO\n");
goto clean_exit;
}
q->input_buffer = malloc(2 * SYNC_SF_LEN * downsampling * sizeof(cf_t));
if (!q->input_buffer) {
perror("malloc");
goto clean_exit;
}
q->input_downsampled = malloc(SYNC_SF_LEN * sizeof(cf_t));
if (!q->input_downsampled) {
perror("malloc");
goto clean_exit;
}
resample_arb_init(&q->resample, (float) 1/downsampling);
q->downsampling = downsampling;
sync_frame_reset(q);
ret = 0;
clean_exit:
if (ret == -1) {
sync_frame_free(q);
}
return ret;
}
void sync_frame_free(sync_frame_t *q) {
if (q->input_buffer) {
free(q->input_buffer);
}
if (q->input_downsampled) {
free(q->input_downsampled);
}
cfo_free(&q->cfocorr);
sync_free(&q->s);
}
void sync_frame_run(sync_frame_t *q, cf_t *input) {
uint32_t track_idx;
switch (q->state) {
case SF_FIND:
q->peak_idx = sync_find(&q->s, input);
q->cell_id = sync_get_cell_id(&q->s);
INFO("FIND %3d:\tPAR=%.2f\n", (int) q->frame_cnt, sync_get_peak_to_avg(&q->s));
if (q->peak_idx != -1 && q->cell_id != -1) {
/* Get the subframe index (0 or 5) */
q->sf_idx = sync_get_slot_id(&q->s)/2;
/* Reset variables */
q->last_found = 0;
q->timeoffset = 0;
q->frame_cnt = 0;
/* Goto Tracking state */
q->state = SF_TRACK;
}
break;
case SF_TRACK:
q->sf_idx = (q->sf_idx + 1) % 10;
/* Every SF idx 0 and 5, find peak around known position q->peak_idx */
if (q->sf_idx != 0 && q->sf_idx != 5) {
break;
}
assert(q->peak_idx < TRACK_LEN);
track_idx = sync_track(&q->s, &input[q->peak_idx - TRACK_LEN]);
INFO("TRACK %3d: SF=%d. Previous idx is %d New Offset is %d\n",
(int) q->frame_cnt, q->sf_idx, q->peak_idx, track_idx - TRACK_LEN);
if (track_idx != -1) {
INFO("Expected SF idx %d but got %d. Going back to FIND\n", q->sf_idx,
sync_get_slot_id(&q->s)/2);
/* Make sure subframe idx is what we expect */
if (q->sf_idx != sync_get_slot_id(&q->s)/2) {
INFO("Expected SF idx %d but got %d. Going back to FIND\n", q->sf_idx,
sync_get_slot_id(&q->s)/2);
q->state = SF_FIND;
}
/* compute cumulative moving average CFO */
q->cur_cfo = (sync_get_cfo(&q->s) + q->frame_cnt * q->cur_cfo) / (q->frame_cnt + 1);
/* compute cumulative moving average time offset */
q->timeoffset = (float) ((float) track_idx - TRACK_LEN + q->timeoffset * q->frame_cnt)
/ (q->frame_cnt + 1);
q->last_found = q->frame_cnt;
q->peak_idx = (q->peak_idx + track_idx - TRACK_LEN) % SYNC_SF_LEN;
if (q->peak_idx < 0) {
INFO("PSS lost (peak_idx=%d). Going back to FIND\n", q->peak_idx);
q->state = SF_FIND;
}
} else {
/* if sync not found, adjust time offset with the averaged value */
q->peak_idx = (q->peak_idx + (uint32_t) q->timeoffset) % SYNC_SF_LEN;
/* if we missed too many PSS go back to FIND */
if (q->frame_cnt - q->last_found > TRACK_MAX_LOST) {
INFO("%d frames lost. Going back to FIND", (int) q->frame_cnt - q->last_found);
q->state = SF_FIND;
}
}
q->frame_cnt++;
break;
}
}
void sync_frame_set_threshold(sync_frame_t *q, float threshold) {
sync_set_threshold(&q->s, threshold);
}
uint32_t sync_frame_cell_id(sync_frame_t *q) {
return q->cell_id;
}
uint32_t sync_frame_sfidx(sync_frame_t *q) {
return q->sf_idx;
}
int sync_frame_push(sync_frame_t *q, cf_t *input, cf_t *output) {
int retval = 0;
int frame_start;
cf_t *input_ds;
uint32_t sf_len;
if (q->downsampling == 1) {
input_ds = input;
} else {
//resample_arb_compute(&q->resample, input, q->input_downsampled, SYNC_SF_LEN * q->downsampling);
decim_c(input, q->input_downsampled, q->downsampling, SYNC_SF_LEN * q->downsampling);
input_ds = q->input_downsampled;
}
sync_frame_run(q, input_ds);
sf_len = q->downsampling * SYNC_SF_LEN;
if (q->state == SF_FIND) {
memcpy(q->input_buffer, input, sf_len * sizeof(cf_t));
} else {
frame_start = q->downsampling * q->peak_idx - sf_len/2;
DEBUG("Peak_idx=%d, frame_start=%d cfo=%.3f\n",q->peak_idx,
frame_start, q->cur_cfo);
if (frame_start > 0) {
if (q->fb_wp) {
memcpy(&q->input_buffer[(sf_len - frame_start)], input, frame_start * sizeof(cf_t));
memcpy(output, q->input_buffer, sf_len * sizeof(cf_t));
retval = 1;
}
memcpy(q->input_buffer, &input[frame_start], (sf_len - frame_start) * sizeof(cf_t));
q->fb_wp = true;
} else {
memcpy(output, &q->input_buffer[sf_len + frame_start], (-frame_start) * sizeof(cf_t));
memcpy(&output[-frame_start], input, (sf_len + frame_start) * sizeof(cf_t));
memcpy(&q->input_buffer[sf_len + frame_start], &input[sf_len + frame_start], (-frame_start) * sizeof(cf_t));
retval = 1;
}
}
/* Frequency Synchronization */
if (retval) {
cfo_correct(&q->cfocorr, output, -q->cur_cfo / 128);
}
if (!retval) {
DEBUG("Frame Buffered\n",0);
}
return retval;
}
void sync_frame_reset(sync_frame_t *q) {
q->state = SF_FIND;
q->frame_cnt = 0;
q->fb_wp = false;
q->cur_cfo = 0;
}

@ -19,6 +19,7 @@
# and at http://www.gnu.org/licenses/. # and at http://www.gnu.org/licenses/.
# #
######################################################################## ########################################################################
# SYNC TEST # SYNC TEST
######################################################################## ########################################################################

@ -96,8 +96,8 @@ int main(int argc, char **argv) {
return -1; return -1;
} }
cfo_correct(&cfocorr, output, freq); cfo_correct(&cfocorr, output, output, freq);
cfo_correct(&cfocorr, output, -freq); cfo_correct(&cfocorr, output, output, -freq);
mse = 0; mse = 0;
for (i=0;i<num_samples;i++) { for (i=0;i<num_samples;i++) {

@ -78,7 +78,8 @@ int main(int argc, char **argv) {
cf_t pss_signal[PSS_LEN]; cf_t pss_signal[PSS_LEN];
float sss_signal0[SSS_LEN]; // for subframe 0 float sss_signal0[SSS_LEN]; // for subframe 0
float sss_signal5[SSS_LEN]; // for subframe 5 float sss_signal5[SSS_LEN]; // for subframe 5
int cid, max_cid, find_idx; int cid, max_cid;
uint32_t find_idx;
sync_t sync; sync_t sync;
lte_fft_t ifft; lte_fft_t ifft;
@ -101,12 +102,12 @@ int main(int argc, char **argv) {
exit(-1); exit(-1);
} }
if (sync_init(&sync, FLEN)) { if (sync_init(&sync, FLEN, 128, 128)) {
fprintf(stderr, "Error initiating PSS/SSS\n"); fprintf(stderr, "Error initiating PSS/SSS\n");
return -1; return -1;
} }
sync_set_threshold(&sync, 20); sync_set_threshold(&sync, 20, 10);
if (cell_id == -1) { if (cell_id == -1) {
cid = 0; cid = 0;
@ -131,7 +132,7 @@ int main(int argc, char **argv) {
memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN); memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN);
lte_ifft_run_slot(&ifft, buffer, &fft_buffer[offset]); lte_ifft_run_slot(&ifft, buffer, &fft_buffer[offset]);
find_idx = sync_find(&sync, fft_buffer); sync_find(&sync, fft_buffer, &find_idx);
find_ns = sync_get_slot_id(&sync); find_ns = sync_get_slot_id(&sync);
printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset, printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset,
ns, find_ns); ns, find_ns);

@ -33,8 +33,8 @@
#include "liblte/phy/utils/cexptab.h" #include "liblte/phy/utils/cexptab.h"
int cexptab_init(cexptab_t *h, int size) { int cexptab_init(cexptab_t *h, uint32_t size) {
int i; uint32_t i;
h->size = size; h->size = size;
h->tab = malloc(sizeof(cf_t) * size); h->tab = malloc(sizeof(cf_t) * size);
@ -42,9 +42,9 @@ int cexptab_init(cexptab_t *h, int size) {
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
h->tab[i] = cexpf(_Complex_I * 2 * M_PI * (float) i / size); h->tab[i] = cexpf(_Complex_I * 2 * M_PI * (float) i / size);
} }
return 0; return LIBLTE_SUCCESS;
} else { } else {
return -1; return LIBLTE_ERROR;
} }
} }
@ -55,9 +55,9 @@ void cexptab_free(cexptab_t *h) {
bzero(h, sizeof(cexptab_t)); bzero(h, sizeof(cexptab_t));
} }
void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) { void cexptab_gen(cexptab_t *h, cf_t *x, float freq, uint32_t len) {
int i; uint32_t i;
unsigned int idx; uint32_t idx;
float phase_inc = freq * h->size; float phase_inc = freq * h->size;
float phase=0; float phase=0;
@ -68,15 +68,15 @@ void cexptab_gen(cexptab_t *h, cf_t *x, float freq, int len) {
while (phase < 0) { while (phase < 0) {
phase += (float) h->size; phase += (float) h->size;
} }
idx = (unsigned int) phase; idx = (uint32_t) phase;
x[i] = h->tab[idx]; x[i] = h->tab[idx];
phase += phase_inc; phase += phase_inc;
} }
} }
void cexptab_gen_direct(cf_t *x, float freq, int len) { void cexptab_gen_direct(cf_t *x, float freq, uint32_t len) {
int i; uint32_t i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
x[i] = cexpf(_Complex_I * 2 * M_PI * freq * i); x[i] = cexpf(_Complex_I * 2 * M_PI * freq * i);
} }

@ -34,61 +34,61 @@
#include "liblte/phy/utils/convolution.h" #include "liblte/phy/utils/convolution.h"
int conv_fft_cc_init(conv_fft_cc_t *state, int input_len, int filter_len) { int conv_fft_cc_init(conv_fft_cc_t *q, uint32_t input_len, uint32_t filter_len) {
state->input_len = input_len; q->input_len = input_len;
state->filter_len = filter_len; q->filter_len = filter_len;
state->output_len = input_len+filter_len-1; q->output_len = input_len+filter_len;
state->input_fft = vec_malloc(sizeof(_Complex float)*state->output_len); q->input_fft = vec_malloc(sizeof(cf_t)*q->output_len);
state->filter_fft = vec_malloc(sizeof(_Complex float)*state->output_len); q->filter_fft = vec_malloc(sizeof(cf_t)*q->output_len);
state->output_fft = vec_malloc(sizeof(_Complex float)*state->output_len); q->output_fft = vec_malloc(sizeof(cf_t)*q->output_len);
if (!state->input_fft || !state->filter_fft || !state->output_fft) { if (!q->input_fft || !q->filter_fft || !q->output_fft) {
return -1; return LIBLTE_ERROR;
} }
if (dft_plan(&state->input_plan,state->output_len,FORWARD,COMPLEX)) { if (dft_plan(&q->input_plan,q->output_len,FORWARD,COMPLEX)) {
return -2; return LIBLTE_ERROR;
} }
if (dft_plan(&state->filter_plan,state->output_len,FORWARD,COMPLEX)) { if (dft_plan(&q->filter_plan,q->output_len,FORWARD,COMPLEX)) {
return -3; return LIBLTE_ERROR;
} }
if (dft_plan(&state->output_plan,state->output_len,BACKWARD,COMPLEX)) { if (dft_plan(&q->output_plan,q->output_len,BACKWARD,COMPLEX)) {
return -4; return LIBLTE_ERROR;
} }
return 0; return LIBLTE_SUCCESS;
} }
void conv_fft_cc_free(conv_fft_cc_t *state) { void conv_fft_cc_free(conv_fft_cc_t *q) {
if (state->input_fft) { if (q->input_fft) {
free(state->input_fft); free(q->input_fft);
} }
if (state->filter_fft) { if (q->filter_fft) {
free(state->filter_fft); free(q->filter_fft);
} }
if (state->output_fft) { if (q->output_fft) {
free(state->output_fft); free(q->output_fft);
} }
dft_plan_free(&state->input_plan); dft_plan_free(&q->input_plan);
dft_plan_free(&state->filter_plan); dft_plan_free(&q->filter_plan);
dft_plan_free(&state->output_plan); dft_plan_free(&q->output_plan);
} }
int conv_fft_cc_run(conv_fft_cc_t *state, _Complex float *input, _Complex float *filter, _Complex float *output) { uint32_t conv_fft_cc_run(conv_fft_cc_t *q, cf_t *input, cf_t *filter, cf_t *output) {
dft_run_c(&state->input_plan, input, state->input_fft); dft_run_c(&q->input_plan, input, q->input_fft);
dft_run_c(&state->filter_plan, filter, state->filter_fft); dft_run_c(&q->filter_plan, filter, q->filter_fft);
vec_prod_ccc(state->input_fft,state->filter_fft,state->output_fft,state->output_len); vec_prod_ccc(q->input_fft,q->filter_fft,q->output_fft,q->output_len);
dft_run_c(&state->output_plan, state->output_fft, output); dft_run_c(&q->output_plan, q->output_fft, output);
return state->output_len; return q->output_len;
} }
int conv_cc(_Complex float *input, _Complex float *filter, _Complex float *output, int input_len, int filter_len) { uint32_t conv_cc(cf_t *input, cf_t *filter, cf_t *output, uint32_t input_len, uint32_t filter_len) {
int i,j; uint32_t i,j;
int output_len; uint32_t output_len;
output_len=input_len+filter_len-1; output_len=input_len+filter_len-1;
memset(output,0,output_len*sizeof(_Complex float)); memset(output,0,output_len*sizeof(cf_t));
for (i=0;i<input_len;i++) { for (i=0;i<input_len;i++) {
for (j=0;j<filter_len;j++) { for (j=0;j<filter_len;j++) {
output[i+j]+=input[i]*filter[j]; output[i+j]+=input[i]*filter[j];

@ -26,16 +26,19 @@
*/ */
#include "liblte/phy/utils/vector.h"
#include <float.h> #include <float.h>
#include <complex.h> #include <complex.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/bit.h"
#ifdef HAVE_VOLK #ifdef HAVE_VOLK
#include "volk/volk.h" #include "volk/volk.h"
#endif #endif
int vec_acc_ii(int *x, int len) { int vec_acc_ii(int *x, uint32_t len) {
int i; int i;
int z=0; int z=0;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -44,10 +47,10 @@ int vec_acc_ii(int *x, int len) {
return z; return z;
} }
float vec_acc_ff(float *x, int len) { float vec_acc_ff(float *x, uint32_t len) {
#ifdef HAVE_VOLK_ACC_FUNCTION #ifdef HAVE_VOLK_ACC_FUNCTION
float result; float result;
volk_32f_accumulator_s32f_u(&result,x,(unsigned int) len); volk_32f_accumulator_s32f(&result,x,len);
return result; return result;
#else #else
int i; int i;
@ -59,7 +62,7 @@ float vec_acc_ff(float *x, int len) {
#endif #endif
} }
cf_t vec_acc_cc(cf_t *x, int len) { cf_t vec_acc_cc(cf_t *x, uint32_t len) {
int i; int i;
cf_t z=0; cf_t z=0;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -68,21 +71,43 @@ cf_t vec_acc_cc(cf_t *x, int len) {
return z; return z;
} }
void vec_sum_ccc(cf_t *z, cf_t *x, cf_t *y, int len) { void vec_sub_fff(float *x, float *y, float *z, uint32_t len) {
#ifndef HAVE_VOLK_SUB_FLOAT_FUNCTION
int i;
for (i=0;i<len;i++) {
z[i] = x[i]-y[i];
}
#else
volk_32f_x2_subtract_32f(z,x,y,len);
#endif
}
void vec_sum_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i]+y[i]; z[i] = x[i]+y[i];
} }
} }
void vec_sum_bbb(char *z, char *x, char *y, int len) { void vec_sum_bbb(char *x, char *y, char *z, uint32_t len) {
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i]+y[i]; z[i] = x[i]+y[i];
} }
} }
void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len) { void vec_sc_prod_fff(float *x, float h, float *z, uint32_t len) {
#ifndef HAVE_VOLK_MULT_FLOAT_FUNCTION
int i;
for (i=0;i<len;i++) {
z[i] = x[i]*h;
}
#else
volk_32f_s32f_multiply_32f(z,x,h,len);
#endif
}
void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, uint32_t len) {
#ifndef HAVE_VOLK_MULT_FUNCTION #ifndef HAVE_VOLK_MULT_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -92,29 +117,50 @@ void vec_sc_prod_cfc(cf_t *x, float h, cf_t *z, int len) {
cf_t hh; cf_t hh;
__real__ hh = h; __real__ hh = h;
__imag__ hh = 0; __imag__ hh = 0;
volk_32fc_s32fc_multiply_32fc_u(z,x,hh,(unsigned int) len); volk_32fc_s32fc_multiply_32fc(z,x,hh,len);
#endif #endif
} }
void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, int len) { void vec_sc_prod_ccc(cf_t *x, cf_t h, cf_t *z, uint32_t len) {
#ifndef HAVE_VOLK_MULT_FUNCTION #ifndef HAVE_VOLK_MULT_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i]*h; z[i] = x[i]*h;
} }
#else #else
volk_32fc_s32fc_multiply_32fc_u(z,x,h,(unsigned int) len); volk_32fc_s32fc_multiply_32fc(z,x,h,len);
#endif #endif
} }
void vec_convert_fi(float *x, int16_t *z, float scale, uint32_t len) {
#ifdef HAVE_VOLK_CONVERT_FI_FUNCTION
volk_32f_s32f_convert_16i(z, x, scale, len);
#else
int i;
for (i=0;i<len;i++) {
z[i] = (int16_t) (x[i]*scale);
}
#endif
}
void vec_deinterleave_cf(cf_t *x, float *real, float *imag, uint32_t len) {
#ifdef HAVE_VOLK_DEINTERLEAVE_FUNCTION
volk_32fc_deinterleave_32f_x2(real, imag, x, len);
#else
int i;
for (i=0;i<len;i++) {
real[i] = __real__ x[i];
imag[i] = __imag__ x[i];
}
#endif
}
void *vec_malloc(int size) { void *vec_malloc(uint32_t size) {
#ifndef HAVE_VOLK #ifndef HAVE_VOLK
return malloc(size); return malloc(size);
#else #else
void *ptr; void *ptr;
if (posix_memalign(&ptr,64,size)) { if (posix_memalign(&ptr,volk_get_alignment(),size)) {
return NULL; return NULL;
} else { } else {
return ptr; return ptr;
@ -122,18 +168,32 @@ void *vec_malloc(int size) {
#endif #endif
} }
void vec_fprint_c(FILE *stream, cf_t *x, int len) { void *vec_realloc(void *ptr, uint32_t old_size, uint32_t new_size) {
#ifndef HAVE_VOLK
return realloc(ptr, new_size);
#else
void *new_ptr;
if (posix_memalign(&new_ptr,volk_get_alignment(),new_size)) {
return NULL;
} else {
memcpy(new_ptr, ptr, old_size);
free(ptr);
return new_ptr;
}
#endif
}
void vec_fprint_c(FILE *stream, cf_t *x, uint32_t len) {
int i; int i;
fprintf(stream, "["); fprintf(stream, "[");
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
fprintf(stream, "%+2.2f%+2.2fi, ", __real__ x[i], __imag__ x[i]); fprintf(stream, "%+2.2f%+2.2fi, ", __real__ x[i], __imag__ x[i]);
//if (!((i+1)%10))
// fprintf(stream, "\n");
} }
fprintf(stream, "];\n"); fprintf(stream, "];\n");
} }
void vec_fprint_f(FILE *stream, float *x, int len) { void vec_fprint_f(FILE *stream, float *x, uint32_t len) {
int i; int i;
fprintf(stream, "["); fprintf(stream, "[");
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -143,7 +203,7 @@ void vec_fprint_f(FILE *stream, float *x, int len) {
} }
void vec_fprint_b(FILE *stream, char *x, int len) { void vec_fprint_b(FILE *stream, char *x, uint32_t len) {
int i; int i;
fprintf(stream, "["); fprintf(stream, "[");
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -152,7 +212,7 @@ void vec_fprint_b(FILE *stream, char *x, int len) {
fprintf(stream, "];\n"); fprintf(stream, "];\n");
} }
void vec_fprint_i(FILE *stream, int *x, int len) { void vec_fprint_i(FILE *stream, int *x, uint32_t len) {
int i; int i;
fprintf(stream, "["); fprintf(stream, "[");
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
@ -161,36 +221,116 @@ void vec_fprint_i(FILE *stream, int *x, int len) {
fprintf(stream, "];\n"); fprintf(stream, "];\n");
} }
void vec_conj_cc(cf_t *x, cf_t *y, int len) { void vec_fprint_hex(FILE *stream, char *x, uint32_t len) {
uint32_t i, nbytes, byte;
nbytes = len/8;
fprintf(stream, "[", len);
for (i=0;i<nbytes;i++) {
byte = bit_unpack(&x, 8);
fprintf(stream, "%02x ", byte);
}
if (len%8) {
byte = bit_unpack(&x, len%8);
fprintf(stream, "%02x ", byte);
}
fprintf(stream, "];\n");
}
void vec_save_file(char *filename, void *buffer, uint32_t len) {
FILE *f;
f = fopen(filename, "w");
if (f) {
fwrite(buffer, len, 1, f);
fclose(f);
} else {
perror("fopen");
}
}
void vec_conj_cc(cf_t *x, cf_t *y, uint32_t len) {
#ifndef HAVE_VOLK_CONJ_FUNCTION #ifndef HAVE_VOLK_CONJ_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
y[i] = conjf(x[i]); y[i] = conjf(x[i]);
} }
#else #else
volk_32fc_conjugate_32fc_u(y,x,(unsigned int) len); volk_32fc_conjugate_32fc(y,x,len);
#endif
}
void vec_prod_cfc(cf_t *x, float *y, cf_t *z, uint32_t len) {
#ifndef HAVE_VOLK_MULT_REAL_FUNCTION
int i;
for (i=0;i<len;i++) {
z[i] = x[i]*y[i];
}
#else
volk_32fc_32f_multiply_32fc(z,x,y,len);
#endif #endif
} }
void vec_prod_ccc(cf_t *x,cf_t *y, cf_t *z, int len) {
void vec_prod_ccc(cf_t *x,cf_t *y, cf_t *z, uint32_t len) {
#ifndef HAVE_VOLK_MULT2_FUNCTION #ifndef HAVE_VOLK_MULT2_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i]*y[i]; z[i] = x[i]*y[i];
} }
#else #else
volk_32fc_x2_multiply_32fc_u(z,x,y,(unsigned int) len); volk_32fc_x2_multiply_32fc(z,x,y,len);
#endif #endif
} }
void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, int len) { void vec_div_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len) {
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i] / y[i]; z[i] = x[i] / y[i];
} }
} }
float vec_avg_power_cf(cf_t *x, int 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);
#else
int i;
for (i=0;i<len;i++) {
z[i] = x[i] / y[i];
}
#endif
}
cf_t vec_dot_prod_ccc(cf_t *x, cf_t *y, uint32_t len) {
#ifdef HAVE_VOLK_DOTPROD_FC_FUNCTION
cf_t res;
volk_32fc_x2_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
}
float vec_dot_prod_fff(float *x, float *y, uint32_t len) {
#ifdef HAVE_VOLK_DOTPROD_F_FUNCTION
float res;
volk_32f_x2_dot_prod_32f(&res, x, y, len);
return res;
#else
uint32_t i;
float res = 0;
for (i=0;i<len;i++) {
res += x[i]*y[i];
}
return res;
#endif
}
float vec_avg_power_cf(cf_t *x, uint32_t len) {
int j; int j;
float power = 0; float power = 0;
for (j=0;j<len;j++) { for (j=0;j<len;j++) {
@ -200,40 +340,42 @@ float vec_avg_power_cf(cf_t *x, int len) {
return power / len; return power / len;
} }
void vec_prod_ccc_unalign(cf_t *x,cf_t *y, cf_t *z, int len) { void vec_abs_cf(cf_t *x, float *abs, uint32_t len) {
#ifndef HAVE_VOLK_MULT_FUNCTION #ifndef HAVE_VOLK_MAG_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
z[i] = x[i]*y[i]; abs[i] = cabsf(x[i]);
} }
#else #else
volk_32fc_x2_multiply_32fc_u(z,x,y,(unsigned int) len); volk_32fc_magnitude_32f(abs,x,len);
#endif #endif
} }
void vec_abs_cf(cf_t *x, float *abs, int len) { void vec_arg_cf(cf_t *x, float *arg, uint32_t len) {
#ifndef HAVE_VOLK_MAG_FUNCTION #ifndef HAVE_VOLK_ATAN_FUNCTION
int i; int i;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
abs[i] = cabsf(x[i]); arg[i] = cargf(x[i]);
} }
#else #else
volk_32fc_magnitude_32f_u(abs,x,(unsigned int) len); volk_32fc_s32f_atan2_32f(arg,x,1,len);
#endif #endif
} }
int vec_max_fi(float *x, int len) { uint32_t vec_max_fi(float *x, uint32_t len) {
#ifdef HAVE_VOLK_MAX_FUNCTION #ifdef HAVE_VOLK_MAX_FUNCTION
unsigned int target=0; uint32_t target=0;
volk_32f_index_max_16u_u(&target,x,(unsigned int) len); volk_32f_index_max_16u(&target,x,len);
return (int) target; return target;
#else #else
int i; uint32_t i;
float m=-FLT_MAX; float m=-FLT_MAX;
int p=0; uint32_t p=0;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {
if (x[i]>m) { if (x[i]>m) {
m=x[i]; m=x[i];
@ -244,7 +386,7 @@ int vec_max_fi(float *x, int len) {
#endif #endif
} }
void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, int len) { void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len) {
int i; int i;
int tmp; int tmp;
for (i=0;i<len;i++) { for (i=0;i<len;i++) {

@ -27,17 +27,5 @@ function [ fs eps p_m w2] = find_pss( x, N_id_2, doplot, threshold)
mean(abs(w2)), m, 10*log10(m/mean(abs(w2)))); mean(abs(w2)), m, 10*log10(m/mean(abs(w2))));
end end
% Estimate PSS-aided CFO
if (i > 128 && i<length(x)&& p_m > threshold)
y=ccf.*x(i-128:i-1);
y0=y(1:64);
y1=y(65:length(y));
eps=angle(conj(sum(y0))*sum(y1))/pi;
else
eps = NaN;
fs = NaN;
end
end end

@ -1,123 +0,0 @@
%
% Copyright 2011-2012 Ben Wojtowicz
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU Affero General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program 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 Affero General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
% Function: lte_generate_sss
% Description: Generates LTE secondary synchronization signals
% Inputs: N_id_1 - Physical layer cell identity group
% N_id_2 - Physical layer identity
% Outputs: sss_d_u_0 - The sequence d(n) used for the secondary
% synchronization signal, an interleaved
% concatenation of two length-31 binary
% sequences for subframe 0
% Outputs: sss_d_u_5 - The sequence d(n) used for the secondary
% synchronization signal, an interleaved
% concatenation of two length-31 binary
% sequences for subframe 5
% Spec: 3GPP TS 36.211 section 6.11.2.1 v10.1.0
% Notes: None
% Rev History: Ben Wojtowicz 10/28/2011 Created
% Ben Wojtowicz 01/29/2012 Fixed license statement
%
function [sss_d_u_0, sss_d_u_5 c0 c1 m0 m1] = lte_generate_sss(N_id_1, N_id_2)
% Validate N_id_1
if(~(N_id_1 >= 0 && N_id_1 <= 167))
fprintf('ERROR: Invalid N_id_1 (%u)\n', N_id_1);
sss_d_u_0 = 0;
sss_d_u_5 = 0;
return;
end
% Validate N_id_2
if(~(N_id_2 >= 0 && N_id_2 <= 2))
fprintf('ERROR: Invalid N_id_2 (%u)\n', N_id_2);
sss_d_u_0 = 0;
sss_d_u_5 = 0;
return;
end
% Generate m0 and m1
q_prime = floor(N_id_1/30);
q = floor((N_id_1 + (q_prime*(q_prime+1)/2))/30);
m_prime = N_id_1 + (q*(q+1)/2);
m0 = mod(m_prime, 31);
m1 = mod((m0 + floor(m_prime/31) + 1), 31);
% Generate s_tilda
x_s_tilda(0+1) = 0;
x_s_tilda(1+1) = 0;
x_s_tilda(2+1) = 0;
x_s_tilda(3+1) = 0;
x_s_tilda(4+1) = 1;
for(i_hat=0:25)
x_s_tilda(i_hat+5+1) = mod((x_s_tilda(i_hat+2+1) + x_s_tilda(i_hat+1)), 2);
end
for(idx=0:30)
s_tilda(idx+1) = 1 - 2*x_s_tilda(idx+1);
end
% Generate c_tilda
x_c_tilda(0+1) = 0;
x_c_tilda(1+1) = 0;
x_c_tilda(2+1) = 0;
x_c_tilda(3+1) = 0;
x_c_tilda(4+1) = 1;
for(i_hat=0:25)
x_c_tilda(i_hat+5+1) = mod((x_c_tilda(i_hat+3+1) + x_c_tilda(i_hat+1)), 2);
end
for(idx=0:30)
c_tilda(idx+1) = 1 - 2*x_c_tilda(idx+1);
end
% Generate z_tilda
x_z_tilda(0+1) = 0;
x_z_tilda(1+1) = 0;
x_z_tilda(2+1) = 0;
x_z_tilda(3+1) = 0;
x_z_tilda(4+1) = 1;
for(i_hat=0:25)
x_z_tilda(i_hat+5+1) = mod((x_z_tilda(i_hat+4+1) + x_z_tilda(i_hat+2+1) + x_z_tilda(i_hat+1+1) + x_z_tilda(i_hat+1)), 2);
end
for(idx=0:30)
z_tilda(idx+1) = 1 - 2*x_z_tilda(idx+1);
end
% Generate s0_m0 and s1_m1
for(n=0:30)
s0_m0(n+1) = s_tilda(mod(n + m0, 31)+1);
s1_m1(n+1) = s_tilda(mod(n + m1, 31)+1);
end
% Generate c0 and c1
for(n=0:30)
c0(n+1) = c_tilda(mod(n + N_id_2, 31)+1);
c1(n+1) = c_tilda(mod(n + N_id_2 + 3, 31)+1);
end
% Generate z1_m0 and z1_m1
for(n=0:30)
z1_m0(n+1) = z_tilda(mod(n + mod(m0, 8), 31)+1);
z1_m1(n+1) = z_tilda(mod(n + mod(m1, 8), 31)+1);
end
% Generate SSS
for(n=0:30)
sss_d_u_0(2*n+1) = s0_m0(n+1) * c0(n+1);
sss_d_u_5(2*n+1) = s1_m1(n+1) * c0(n+1);
sss_d_u_0(2*n+1+1) = s1_m1(n+1) * c1(n+1) * z1_m0(n+1);
sss_d_u_5(2*n+1+1) = s0_m0(n+1) * c1(n+1) * z1_m1(n+1);
end
end

@ -1,35 +0,0 @@
N=128; %128 subcarries
M=16; %QAM order
cp=9; %length of the cyclic prefix... Is increasing the cyclic prefix size gonna increase the efficiency?
scale = 1/sqrt(10);
hMod = modem.qammod(M); %QAM Modulator
hDemod = modem.qamdemod(hMod); %QAM demodulator
loops = 10;
SNR =0:5:35;
t1= cputime ;
% transmited signal. Contains N data points ranging from 0 to M-1
ber=zeros(5,length(SNR));
%% Creating the Rayleigh Multipath Channels
Ch = rayleighchan(1/1000,10);
Ch.ResetBeforeFiltering = 0;
sig = 1i*ones(loops,1);
h1 = filter(Ch,sig);
h2 = 0.1072*filter(Ch,sig);
h3 = 0.0120*filter(Ch,sig);
h4 = 0.0052*filter(Ch,sig);
% Delay Values
l1 = 4;
l2 = 7;
l3= 16;
%%
ofdm_cp=[];
%tx=transmited_data;
for ik=1:loops%number of loops
tx = randi([0 M-1],1,N); % generate random data
sig=modulate(hMod, tx)*scale; % Modulate QAM modulated signal, devide by the square root of 10 to bring the average power of the signal to 1
ofdm=sqrt(N).*ifft(sig,N); % generate OFDM signal IFFT on the parrellel data,multiply by sqrt(N) to adjust to the matlab computation ,
ofdm_cp = [ofdm_cp ofdm(N-cp+1:N) ofdm]; % Add cyclic prefix
end
Loading…
Cancel
Save