diff --git a/cmake/modules/FindVolk.cmake b/cmake/modules/FindVolk.cmake index 184e792a2..5861f1232 100644 --- a/cmake/modules/FindVolk.cmake +++ b/cmake/modules/FindVolk.cmake @@ -44,15 +44,24 @@ CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_convert_16i HAVE_VOLK_CONVERT_FI_FUNCTI CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_32f_x2 HAVE_VOLK_DEINTERLEAVE_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_subtract_32f HAVE_VOLK_SUB_FLOAT_FUNCTION) CHECK_FUNCTION_EXISTS_MATH(volk_32fc_x2_square_dist_32f HAVE_VOLK_SQUARE_DIST_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_real_32f HAVE_VOLK_DEINTERLEAVE_FUNCTION) +CHECK_FUNCTION_EXISTS_MATH(volk_32fc_index_max_16u HAVE_VOLK_MAX_ABS_FUNCTION) + SET(VOLK_DEFINITIONS "HAVE_VOLK") +IF(${HAVE_VOLK_MAX_ABS_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_MAX_ABS_FUNCTION") +ENDIF() IF(${HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION}) SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION") ENDIF() IF(${HAVE_VOLK_SQUARE_DIST_FUNCTION}) SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SQUARE_DIST_FUNCTION") ENDIF() +IF(${HAVE_VOLK_DEINTERLEAVE_FUNCTION}) + SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DEINTERLEAVE_FUNCTION") +ENDIF() IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION}) SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION") ENDIF() diff --git a/cuhd/include/liblte/cuhd/cuhd.h b/cuhd/include/liblte/cuhd/cuhd.h index 48da5883d..e1604b93d 100644 --- a/cuhd/include/liblte/cuhd/cuhd.h +++ b/cuhd/include/liblte/cuhd/cuhd.h @@ -48,6 +48,8 @@ LIBLTE_API int cuhd_start_rx_stream_nsamples(void *h, LIBLTE_API int cuhd_stop_rx_stream(void *h); +LIBLTE_API void cuhd_flush_buffer(void *h); + LIBLTE_API bool cuhd_rx_wait_lo_locked(void *h); LIBLTE_API double cuhd_set_rx_srate(void *h, diff --git a/cuhd/lib/cuhd_imp.cpp b/cuhd/lib/cuhd_imp.cpp index b0f9baf61..9608c94a3 100644 --- a/cuhd/lib/cuhd_imp.cpp +++ b/cuhd/lib/cuhd_imp.cpp @@ -95,6 +95,15 @@ int cuhd_stop_rx_stream(void *h) return 0; } +void cuhd_flush_buffer(void *h) +{ + int n; + _Complex float tmp[1024]; + do { + n = cuhd_recv(h, tmp, 1024, 0); + } while (n > 0); +} + int cuhd_start_rx_stream_nsamples(void *h, uint32_t nsamples) { cuhd_handler *handler = static_cast < cuhd_handler * >(h); diff --git a/lte/phy/examples/CMakeLists.txt b/lte/phy/examples/CMakeLists.txt index 988815d29..a5fe5d474 100644 --- a/lte/phy/examples/CMakeLists.txt +++ b/lte/phy/examples/CMakeLists.txt @@ -51,7 +51,7 @@ LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND) # These two can be compiled without UHD or graphics support ################################################################# -add_executable(pdsch_ue pdsch_ue.c iodev.c) +add_executable(pdsch_ue pdsch_ue.c iodev.c cell_search_utils.c) target_link_libraries(pdsch_ue lte_phy) add_executable(pdsch_enodeb pdsch_enodeb.c) @@ -81,8 +81,8 @@ ENDIF(${GRAPHICS_FIND} EQUAL -1) IF(${CUHD_FIND} GREATER -1) - add_executable(scan_mib scan_mib.c) - target_link_libraries(scan_mib lte_phy cuhd ) + add_executable(cell_search cell_search.c cell_search_utils.c) + target_link_libraries(cell_search lte_phy cuhd ) MESSAGE(STATUS " UHD examples will be installed.") diff --git a/lte/phy/examples/scan_mib.c b/lte/phy/examples/cell_search.c similarity index 58% rename from lte/phy/examples/scan_mib.c rename to lte/phy/examples/cell_search.c index d0670df32..5f3306bd8 100644 --- a/lte/phy/examples/scan_mib.c +++ b/lte/phy/examples/cell_search.c @@ -32,10 +32,14 @@ #include #include #include + #include #include "liblte/phy/phy.h" +#include "cell_search_utils.h" + + #ifndef DISABLE_UHD #include "liblte/cuhd/cuhd.h" #endif @@ -45,32 +49,34 @@ #define FLEN 9600 #define FLEN_PERIOD 0.005 +#define MAX_EARFCN 1000 + int band = -1; int earfcn_start=-1, earfcn_end = -1; -int nof_frames_find=200; +int nof_frames_total = 50; +int nof_frames_detected = 10; +float threshold = CS_FIND_THRESHOLD; float uhd_gain = 60.0; char *uhd_args=""; -#define MAX_EARFCN 1000 -lte_earfcn_t channels[MAX_EARFCN]; - - void usage(char *prog) { - printf("Usage: %s [asefgv] -b band\n", prog); + printf("Usage: %s [agsendtvb] -b band\n", prog); printf("\t-a UHD args [Default %s]\n", uhd_args); printf("\t-g UHD gain [Default %.2f dB]\n", uhd_gain); printf("\t-s earfcn_start [Default All]\n"); printf("\t-e earfcn_end [Default All]\n"); - printf("\t-f nof_frames_find [Default %d]\n", nof_frames_find); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-d nof_frames_detected [Default 10]\n"); + printf("\t-t threshold [Default %.2f]\n",threshold); printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "asefgvb")) != -1) { + while ((opt = getopt(argc, argv, "agsendtvb")) != -1) { switch(opt) { case 'a': uhd_args = argv[optind]; @@ -84,8 +90,14 @@ void parse_args(int argc, char **argv) { case 'e': earfcn_end = atoi(argv[optind]); break; - case 'f': - nof_frames_find = atoi(argv[optind]); + case 'n': + nof_frames_total = atoi(argv[optind]); + break; + case 'd': + nof_frames_detected = atoi(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); break; case 'g': uhd_gain = atof(argv[optind]); @@ -104,99 +116,88 @@ void parse_args(int argc, char **argv) { } } -int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { - DEBUG(" ---- Receive %d samples ---- \n", nsamples); - return cuhd_recv(h, data, nsamples, 1); -} - - int main(int argc, char **argv) { - int ret; - int frame_cnt; - int nof_freqs; - uint32_t freq; - ue_sync_t uesync; + int n; void *uhd; + ue_celldetect_t s; + ue_celldetect_result_t found_cells[3]; cf_t *buffer; - - if (argc < 3) { - usage(argv[0]); - exit(-1); - } - - parse_args(argc,argv); - + int nof_freqs; + lte_earfcn_t channels[MAX_EARFCN]; + uint32_t freq; + pbch_mib_t mib; + + parse_args(argc, argv); + printf("Opening UHD device...\n"); if (cuhd_open(uhd_args, &uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); - } - - /* set uhd_gain */ + } cuhd_set_rx_gain(uhd, uhd_gain); - + nof_freqs = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN); if (nof_freqs < 0) { fprintf(stderr, "Error getting EARFCN list\n"); exit(-1); } + + buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + + if (ue_celldetect_init(&s)) { + fprintf(stderr, "Error initiating UE sync module\n"); + exit(-1); + } + if (threshold > 0) { + ue_celldetect_set_threshold(&s, threshold); + } + if (nof_frames_total > 0) { + ue_celldetect_set_nof_frames_total(&s, nof_frames_total); + } + if (nof_frames_detected > 0) { + ue_celldetect_set_nof_frames_detected(&s, nof_frames_detected); + } - for (freq=0;freq threshold/2) { + if (decode_pbch(uhd, buffer, &found_cells[i], nof_frames_total, &mib)) { + fprintf(stderr, "Error decoding PBCH\n"); + exit(-1); + } + } + } + } } - + + ue_celldetect_free(&s); cuhd_close(uhd); - - - printf("\n\nDone\n"); exit(0); } + diff --git a/lte/phy/examples/cell_search_utils.c b/lte/phy/examples/cell_search_utils.c new file mode 100644 index 000000000..a5bb30adc --- /dev/null +++ b/lte/phy/examples/cell_search_utils.c @@ -0,0 +1,157 @@ +/** + * + * \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 +#include +#include +#include +#include +#include +#include + +#include + +#include "liblte/phy/phy.h" + +#ifndef DISABLE_UHD +#include "liblte/cuhd/cuhd.h" + +int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell, uint32_t nof_frames_total, pbch_mib_t *mib) +{ + ue_mib_t uemib; + int n; + + bzero(mib, sizeof(pbch_mib_t)); + + uint32_t nof_frames = 0; + uint32_t flen = MIB_FRAME_SIZE; + + if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) { + fprintf(stderr, "Error initiating PBCH decoder\n"); + return LIBLTE_ERROR; + } + + INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0); + cuhd_set_rx_srate(uhd, 1920000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + do { + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total); + + n = ue_mib_decode(&uemib, buffer, flen, mib); + if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling ue_mib_decode()\n"); + return LIBLTE_ERROR; + } + if (n == MIB_FRAME_UNALIGNED) { + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + nof_frames++; + } while (n != MIB_FOUND && nof_frames < 2*nof_frames_total); + if (n == MIB_FOUND) { + printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames); + pbch_mib_fprint(stdout, mib, found_cell->cell_id); + } else { + printf("\nCould not decode MIB\n"); + } + + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + ue_mib_free(&uemib); + + return LIBLTE_SUCCESS; +} + +int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t found_cell[3]) +{ + int n; + + INFO("Setting sampling frequency 960 KHz for PSS search\n", 0); + cuhd_set_rx_srate(uhd, 960000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + uint32_t nof_scanned_cells = 0; + uint32_t flen = 4800; + int nof_detected_cells = 0; + + do { + + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + n = ue_celldetect_scan(s, buffer, flen, &found_cell[nof_scanned_cells]); + switch(n) { + case CS_FRAME_UNALIGNED: + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + return LIBLTE_ERROR; + case CS_CELL_DETECTED: + nof_detected_cells++; + if (found_cell[nof_scanned_cells].peak > 0) { + printf("\n\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n", + found_cell[nof_scanned_cells].cell_id, + lte_cp_string(found_cell[nof_scanned_cells].cp), + found_cell[nof_scanned_cells].peak, found_cell[nof_scanned_cells].mode, + s->nof_frames_detected); + } + + nof_scanned_cells++; + break; + case CS_CELL_NOT_DETECTED: + nof_scanned_cells++; + break; + case LIBLTE_ERROR: + case LIBLTE_ERROR_INVALID_INPUTS: + fprintf(stderr, "Error calling cellsearch_scan()\n"); + return LIBLTE_ERROR; + } + } while(nof_scanned_cells < 3); + + INFO("Stopping receiver...\n", 0); + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + return nof_detected_cells; +} +#endif diff --git a/lte/phy/examples/cell_search_utils.h b/lte/phy/examples/cell_search_utils.h new file mode 100644 index 000000000..2fefed105 --- /dev/null +++ b/lte/phy/examples/cell_search_utils.h @@ -0,0 +1,40 @@ +/** + * + * \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 "liblte/phy/phy.h" + +int decode_pbch(void *uhd, + cf_t *buffer, + ue_celldetect_result_t *found_cell, + uint32_t nof_frames_total, + pbch_mib_t *mib); + +int find_cell(void *uhd, + ue_celldetect_t *s, + cf_t *buffer, + ue_celldetect_result_t found_cell[3]); \ No newline at end of file diff --git a/lte/phy/examples/iodev.c b/lte/phy/examples/iodev.c index 41d6e6a48..ce55b3a3a 100644 --- a/lte/phy/examples/iodev.c +++ b/lte/phy/examples/iodev.c @@ -29,11 +29,12 @@ #include #include #include +#include #include "iodev.h" #include "liblte/phy/io/filesource.h" -#include "liblte/phy/phch/ue_sync.h" +#include "liblte/phy/ue/ue_sync.h" #include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/vector.h" @@ -41,6 +42,8 @@ #include "liblte/cuhd/cuhd.h" #endif +#include "cell_search_utils.h" + int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { DEBUG(" ---- Receive %d samples ---- \n", nsamples); @@ -48,20 +51,36 @@ int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { } /* Setup USRP or input file */ -int iodev_init(iodev_t *q, iodev_cfg_t *config, uint32_t file_sf_len) { +int iodev_init(iodev_t *q, iodev_cfg_t *config, lte_cell_t *cell, pbch_mib_t *mib) { if (config->input_file_name) { + + mib->phich_resources = R_1; + mib->phich_length = PHICH_NORM; + + cell->id = config->cell_id_file; + cell->cp = CPNORM; + cell->nof_ports = config->nof_ports_file; + cell->nof_prb = config->nof_prb_file; + if (filesource_init(&q->fsrc, config->input_file_name, COMPLEX_FLOAT_BIN)) { return LIBLTE_ERROR; } - q->input_buffer_file = vec_malloc(SF_LEN_MAX * sizeof(cf_t)); + q->mode = FILESOURCE; + int symbol_sz = lte_symbol_sz(cell->nof_prb); + if (symbol_sz > 0) { + q->sf_len = SF_LEN(symbol_sz); + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb); + return LIBLTE_ERROR; + } + + q->input_buffer_file = vec_malloc(q->sf_len * sizeof(cf_t)); if (!q->input_buffer_file) { perror("malloc"); return LIBLTE_ERROR; } - - q->mode = FILESOURCE; - q->sf_len = file_sf_len; + q->sf_idx = 9; } else { #ifndef DISABLE_UHD @@ -71,17 +90,76 @@ int iodev_init(iodev_t *q, iodev_cfg_t *config, uint32_t file_sf_len) { return LIBLTE_ERROR; } - /* set uhd_freq */ cuhd_set_rx_gain(q->uhd, config->uhd_gain); + + /* set receiver frequency */ cuhd_set_rx_freq(q->uhd, (double) config->uhd_freq); cuhd_rx_wait_lo_locked(q->uhd); DEBUG("Set uhd_freq to %.3f MHz\n", (double ) config->uhd_freq); + int n; + ue_celldetect_t cd; + ue_celldetect_result_t found_cells[3]; + + cf_t *buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + if (ue_celldetect_init(&cd)) { + fprintf(stderr, "Error initiating UE cell detect\n"); + exit(-1); + } + n = find_cell(q->uhd, &cd, buffer, found_cells); + if (n < 0) { + fprintf(stderr, "Error searching cell\n"); + exit(-1); + } + + int max_peak_cell = 0; + float max_peak_value = -1.0; + if (n > 0) { + for (int i=0;i<3;i++) { + if (found_cells[i].peak > max_peak_value) { + max_peak_value = found_cells[i].peak; + max_peak_cell = i; + } + } + if (decode_pbch(q->uhd, buffer, &found_cells[max_peak_cell], 400, mib)) { + fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", found_cells[max_peak_cell].cell_id); + return LIBLTE_ERROR; + } + } else { + fprintf(stderr, "Could not find any cell in this frequency\n"); + return LIBLTE_ERROR; + } + + free(buffer); + cell->cp = found_cells[max_peak_cell].cp; + cell->id = found_cells[max_peak_cell].cell_id; + cell->nof_prb = mib->nof_prb; + cell->nof_ports = mib->nof_ports; + + /* set sampling frequency */ + int srate = lte_sampling_freq_hz(cell->nof_prb); + if (srate != -1) { + cuhd_set_rx_srate(q->uhd, (double) srate); + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell->nof_prb); + return LIBLTE_ERROR; + } + DEBUG("Starting receiver...\n", 0); cuhd_start_rx_stream(q->uhd); - - ue_sync_init(&q->sframe, cuhd_set_rx_srate, cuhd_recv_wrapper, q->uhd); + + if (ue_sync_init(&q->sframe, *cell, cuhd_recv_wrapper, q->uhd)) { + fprintf(stderr, "Error initiating ue_sync\n"); + return LIBLTE_ERROR; + } + + /* Decodes the SSS signal during the tracking phase. Extra overhead, but makes sure we are in the correct subframe */ + ue_sync_decode_sss_on_track(&q->sframe, true); // Here, the subframe length and input buffer is managed by ue_sync q->mode = UHD; @@ -113,7 +191,7 @@ void iodev_free(iodev_t *q) { int iodev_receive(iodev_t *q, cf_t **buffer) { int n; if (q->mode == FILESOURCE) { - DEBUG(" ----- READING %d SAMPLES ---- \n", q->sf_len); + INFO(" ----- READING %d SAMPLES ---- \n", q->sf_len); n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len); *buffer = q->input_buffer_file; if (n == -1) { @@ -132,6 +210,11 @@ int iodev_receive(iodev_t *q, cf_t **buffer) { } else { n = 1; } + q->sf_idx++; + if (q->sf_idx == 10) { + q->sf_idx = 0; + } + usleep(5000); } else { /* Use ue_sync_work which returns a synchronized buffer of subframe samples */ #ifndef DISABLE_UHD @@ -160,4 +243,12 @@ bool iodev_isUSRP(iodev_t *q) { return q->mode == UHD; } +uint32_t iodev_get_sfidx(iodev_t *q) { + if (iodev_isfile(q)) { + return q->sf_idx; + } else { + return ue_sync_get_sfidx(&q->sframe); + } +} + diff --git a/lte/phy/examples/iodev.h b/lte/phy/examples/iodev.h index 3164f91c5..bc020222f 100644 --- a/lte/phy/examples/iodev.h +++ b/lte/phy/examples/iodev.h @@ -30,7 +30,7 @@ #include "liblte/config.h" -#include "liblte/phy/phch/ue_sync.h" +#include "liblte/phy/ue/ue_sync.h" #include "liblte/phy/io/filesource.h" #ifndef DISABLE_UHD @@ -54,6 +54,10 @@ typedef _Complex float cf_t; typedef struct LIBLTE_API { char *input_file_name; + uint32_t cell_id_file; + uint32_t nof_prb_file; + uint32_t nof_ports_file; + float uhd_freq; float uhd_gain; char *uhd_args; @@ -66,6 +70,7 @@ typedef struct LIBLTE_API { ue_sync_t sframe; #endif uint32_t sf_len; + uint32_t sf_idx; cf_t *input_buffer_file; // for UHD mode, the input buffer is managed by sync_frame_t filesource_t fsrc; iodev_cfg_t config; @@ -75,7 +80,8 @@ typedef struct LIBLTE_API { LIBLTE_API int iodev_init(iodev_t *q, iodev_cfg_t *config, - uint32_t file_sf_len); + lte_cell_t *cell, + pbch_mib_t *mib); LIBLTE_API void iodev_free(iodev_t *q); @@ -84,6 +90,8 @@ LIBLTE_API int iodev_receive(iodev_t *q, LIBLTE_API void* iodev_get_cuhd(iodev_t *q); +LIBLTE_API uint32_t iodev_get_sfidx(iodev_t *q); + LIBLTE_API bool iodev_isfile(iodev_t *q); LIBLTE_API bool iodev_isUSRP(iodev_t *q); diff --git a/lte/phy/examples/pdsch_enodeb.c b/lte/phy/examples/pdsch_enodeb.c index f761b043b..b0b9ffc95 100644 --- a/lte/phy/examples/pdsch_enodeb.c +++ b/lte/phy/examples/pdsch_enodeb.c @@ -257,7 +257,7 @@ int main(int argc, char **argv) { N_id_2 = cell.id % 3; sf_n_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB; - sf_n_samples = 2 * SLOT_LEN_CPNORM(lte_symbol_sz(cell.nof_prb)); + sf_n_samples = 2 * SLOT_LEN(lte_symbol_sz(cell.nof_prb)); /* this *must* be called after setting slot_len_* */ base_init(); diff --git a/lte/phy/examples/pdsch_ue.c b/lte/phy/examples/pdsch_ue.c index 2af627e65..09ff59c3f 100644 --- a/lte/phy/examples/pdsch_ue.c +++ b/lte/phy/examples/pdsch_ue.c @@ -53,8 +53,6 @@ void init_plots(); * Program arguments processing ***********************************************************************/ typedef struct { - uint32_t cell_id_file; - uint32_t nof_prb_file; uint16_t rnti; int nof_subframes; bool disable_plots; @@ -62,8 +60,9 @@ typedef struct { }prog_args_t; void args_default(prog_args_t *args) { - args->cell_id_file = 1; - args->nof_prb_file = 6; + args->io_config.cell_id_file = 195; + args->io_config.nof_prb_file = 50; + args->io_config.nof_ports_file = 2; args->rnti = SIRNTI; args->nof_subframes = -1; args->disable_plots = false; @@ -71,13 +70,14 @@ void args_default(prog_args_t *args) { 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; + args->io_config.uhd_gain = 60.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-c cell_id if reading from file [Default %d]\n", args->io_config.cell_id_file); + printf("\t-p nof_prb if reading from file [Default %d]\n", args->io_config.nof_prb_file); + printf("\t-o nof_ports if reading from file [Default %d]\n", args->io_config.nof_ports_file); printf("\t-r RNTI to look for [Default 0x%x]\n", args->rnti); #ifndef DISABLE_UHD printf("\t-a UHD args [Default %s]\n", args->io_config.uhd_args); @@ -99,16 +99,19 @@ void usage(prog_args_t *args, char *prog) { void parse_args(prog_args_t *args, int argc, char **argv) { int opt; args_default(args); - while ((opt = getopt(argc, argv, "icagfndvtbpr")) != -1) { + while ((opt = getopt(argc, argv, "icagfndvtbpro")) != -1) { switch (opt) { case 'i': args->io_config.input_file_name = argv[optind]; break; case 'c': - args->cell_id_file = atoi(argv[optind]); + args->io_config.cell_id_file = atoi(argv[optind]); break; case 'p': - args->nof_prb_file = atoi(argv[optind]); + args->io_config.nof_prb_file = atoi(argv[optind]); + break; + case 'o': + args->io_config.nof_ports_file = atoi(argv[optind]); break; case 'a': args->io_config.uhd_args = argv[optind]; @@ -152,6 +155,8 @@ void sigintHandler(int x) { /* TODO: Do something with the output data */ char data[10000]; +extern float mean_exec_time; + int main(int argc, char **argv) { int ret; cf_t *sf_buffer; @@ -159,27 +164,13 @@ int main(int argc, char **argv) { 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; bool printed_sib = false; int rlen; - int symbol_sz; parse_args(&prog_args, argc, argv); - symbol_sz = lte_symbol_sz(prog_args.nof_prb_file); - if (symbol_sz > 0) { - if (iodev_init(&iodev, &prog_args.io_config, SF_LEN(symbol_sz, CPNORM))) { - fprintf(stderr, "Error initiating input device\n"); - exit(-1); - } - } else { - fprintf(stderr, "Invalid number of PRB %d\n", prog_args.nof_prb_file); - exit(-1); - } - #ifndef DISABLE_GRAPHICS if (!prog_args.disable_plots) { init_plots(); @@ -190,19 +181,22 @@ int main(int argc, char **argv) { printf("\n --- Press Ctrl+C to exit --- \n"); signal(SIGINT, sigintHandler); - /* Initialize frame and subframe counters */ + /* Initialize subframe counter */ sf_cnt = 0; - sf_idx = 0; - /* Decodes the SSS signal during the tracking phase. Extra overhead, but makes sure we are in the correct subframe */ - ue_sync_decode_sss_on_track(&iodev.sframe, true); - - /* Decodes the PBCH on each frame. Around 10% more overhead, but makes sure we are in the current System Frame Number (SFN) */ - ue_sync_pbch_always(&iodev.sframe, false); + if (iodev_init(&iodev, &prog_args.io_config, &cell, &mib)) { + exit(-1); + } + + 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); /* Main loop */ while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) { - + ret = iodev_receive(&iodev, &sf_buffer); if (ret < 0) { fprintf(stderr, "Error reading from input device (%d)\n", ret); @@ -210,81 +204,41 @@ int main(int argc, char **argv) { } /* iodev_receive returns 1 if successfully read 1 aligned subframe */ - if (ret == 0) { - printf("Finding PSS... Peak: %8.1f, Output level: %+.2f dB FrameCnt: %d, State: %d\r", - sync_get_peak_value(&iodev.sframe.s), 20*log10f(agc_get_output_level(&iodev.sframe.agc)), - iodev.sframe.frame_total_cnt, iodev.sframe.state); - } else if (ret == 1) { - if (!ue_dl_initiated) { - if (iodev_isUSRP(&iodev)) { - cell = ue_sync_get_cell(&iodev.sframe); - mib = ue_sync_get_mib(&iodev.sframe); - } else { - cell.id = prog_args.cell_id_file; - cell.cp = CPNORM; - cell.nof_ports = 1; // TODO: Use prog_args - cell.nof_prb = prog_args.nof_prb_file; - mib.phich_resources = R_1; - mib.phich_length = PHICH_NORM; - } - 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); - ue_dl_initiated = true; - } else { - if (iodev_isUSRP(&iodev)) { - sf_idx = ue_sync_get_sfidx(&iodev.sframe); - } - rlen = ue_dl_receive(&ue_dl, sf_buffer, data, sf_idx, ue_sync_get_mib(&iodev.sframe).sfn, prog_args.rnti); - if (rlen < 0) { - fprintf(stderr, "\nError running receiver\n");fflush(stdout); - exit(-1); - } - if (prog_args.rnti == SIRNTI && !printed_sib && rlen > 0) { - printf("\n\nDecoded SIB1 Message: "); - vec_fprint_hex(stdout, data, rlen); - printf("\n");fflush(stdout); - printed_sib = true; - } - if (!(sf_cnt % 10)) { - printf("RSSI: %+.2f dBm, CFO: %+.4f KHz, SFO: %+.4f Khz, NOI: %.2f Errors: %4d/%4d, BLER: %.1e\r", - 20*log10f(agc_get_rssi(&iodev.sframe.agc))+30, - ue_sync_get_cfo(&iodev.sframe)/1000, ue_sync_get_sfo(&iodev.sframe)/1000, - pdsch_average_noi(&ue_dl.pdsch), - (int) ue_dl.pkt_errors, (int) ue_dl.pkts_total, (float) ue_dl.pkt_errors / ue_dl.pkts_total); - - fflush(stdout); - if (VERBOSE_ISINFO()) { - printf("\n"); - } - } - #ifndef DISABLE_GRAPHICS - if (!prog_args.disable_plots && sf_idx == 5) { - do_plots(&ue_dl, sf_idx); - } - #endif + if (ret == 1) { + rlen = ue_dl_decode(&ue_dl, sf_buffer, data, iodev_get_sfidx(&iodev), prog_args.rnti); + if (rlen < 0) { + fprintf(stderr, "\nError running receiver\n");fflush(stdout); + exit(-1); } - if (iodev_isfile(&iodev)) { - sf_idx++; - if (sf_idx == NSUBFRAMES_X_FRAME) { - sf_idx = 0; - } + if (prog_args.rnti == SIRNTI && !printed_sib && rlen > 0) { + printf("\n\nDecoded SIB1 Message: "); + vec_fprint_hex(stdout, data, rlen); + printf("\n");fflush(stdout); + printed_sib = true; } - } - if (prog_args.nof_subframes > 0) { - sf_cnt++; - } - if (iodev_isfile(&iodev)) { - usleep(5000); + // Plot and Printf + if (!(sf_cnt % 10)) { + printf("CFO: %+.4f KHz, SFO: %+.4f Khz, NOI: %.2f Errors: %4d/%4d, BLER: %.1e, Texec: %.2f\r", + ue_sync_get_cfo(&iodev.sframe)/1000, ue_sync_get_sfo(&iodev.sframe)/1000, + pdsch_average_noi(&ue_dl.pdsch), + (int) ue_dl.pkt_errors, (int) ue_dl.pkts_total, (float) ue_dl.pkt_errors / ue_dl.pkts_total, + mean_exec_time); + } + #ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots && iodev_get_sfidx(&iodev) == 5) { + do_plots(&ue_dl, 5); + } + #endif + } else if (ret == 0) { + printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r", + sync_get_peak_value(&iodev.sframe.sfind), + iodev.sframe.frame_total_cnt, iodev.sframe.state); } - } + sf_cnt++; + } // Main loop - if (ue_dl_initiated) { - ue_dl_free(&ue_dl); - } + ue_dl_free(&ue_dl); iodev_free(&iodev); printf("\nBye\n"); diff --git a/lte/phy/examples/synch_file.c b/lte/phy/examples/synch_file.c index 4bd746f87..fad7478f8 100644 --- a/lte/phy/examples/synch_file.c +++ b/lte/phy/examples/synch_file.c @@ -109,7 +109,6 @@ int main(int argc, char **argv) { int peak_pos[3]; float *cfo; float peak_value[3]; - float mean_value[3]; int frame_cnt; cf_t *input; uint32_t m0, m1; @@ -201,10 +200,10 @@ int main(int argc, char **argv) { if (force_N_id_2 != -1) { N_id_2 = force_N_id_2; - peak_pos[N_id_2] = pss_synch_find_pss(&pss[N_id_2], input, &peak_value[N_id_2], &mean_value[N_id_2]); + peak_pos[N_id_2] = pss_synch_find_pss(&pss[N_id_2], input, &peak_value[N_id_2]); } else { for (N_id_2=0;N_id_2<3;N_id_2++) { - peak_pos[N_id_2] = pss_synch_find_pss(&pss[N_id_2], input, &peak_value[N_id_2], &mean_value[N_id_2]); + peak_pos[N_id_2] = pss_synch_find_pss(&pss[N_id_2], input, &peak_value[N_id_2]); } float max_value=-99999; N_id_2=-1; @@ -218,7 +217,7 @@ int main(int argc, char **argv) { } /* If peak detected */ - if (peak_value[N_id_2]/mean_value[N_id_2] > corr_peak_threshold) { + if (peak_value[N_id_2] > corr_peak_threshold) { sss_idx = peak_pos[N_id_2]-2*(symbol_sz+CP(symbol_sz,CPNORM_LEN)); if (sss_idx >= 0) { @@ -228,7 +227,7 @@ int main(int argc, char **argv) { cfo[frame_cnt] = pss_synch_cfo_compute(&pss[N_id_2], &input[peak_pos[N_id_2]-128]); printf("\t%d\t%d\t%d\t%d\t%.3f\t\t%3d\t%d\t%d\t%.3f\n", frame_cnt,N_id_2, sss_synch_N_id_1(&sss[N_id_2], m0, m1), - sss_synch_subframe(m0, m1), peak_value[N_id_2]/mean_value[N_id_2], + sss_synch_subframe(m0, m1), peak_value[N_id_2], peak_pos[N_id_2], m0, m1, cfo[frame_cnt]); } diff --git a/lte/phy/include/liblte/phy/common/phy_common.h b/lte/phy/include/liblte/phy/common/phy_common.h index 29e8c233e..4f7efc742 100644 --- a/lte/phy/include/liblte/phy/common/phy_common.h +++ b/lte/phy/include/liblte/phy/common/phy_common.h @@ -65,12 +65,12 @@ typedef enum {CPNORM, CPEXT} lte_cp_t; #define SYMBOL_SZ_MAX 2048 #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_LEN 144 #define CPEXT_NSYMB 6 -#define CPEXT_SF_NSYMB 2*CPEXT_NSYMB +#define CPEXT_SF_NSYMB (2*CPEXT_NSYMB) #define CPEXT_LEN 512 #define CPEXT_7_5_LEN 1024 @@ -78,21 +78,16 @@ typedef enum {CPNORM, CPEXT} lte_cp_t; #define CP_ISEXT(cp) (cp==CPEXT) #define CP_NSYMB(cp) (CP_ISNORM(cp)?CPNORM_NSYMB:CPEXT_NSYMB) -#define CP(symbol_sz, c) (c*symbol_sz/2048) -#define CP_NORM(symbol, symbol_sz) (symbol==0)?CP(symbol_sz,CPNORM_0_LEN):CP(symbol_sz,CPNORM_LEN) -#define CP_EXT(symbol_sz) CP(symbol_sz,CPEXT_LEN) +#define CP(symbol_sz, c) ((c*symbol_sz)/2048) +#define CP_NORM(symbol, symbol_sz) ((symbol==0)?CP((symbol_sz),CPNORM_0_LEN):CP((symbol_sz),CPNORM_LEN)) +#define CP_EXT(symbol_sz) (CP((symbol_sz),CPEXT_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(symbol_sz, cp) (CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz)) +#define SLOT_LEN(symbol_sz) (480*((symbol_sz)/64)) +#define SF_LEN(symbol_sz) (2*SLOT_LEN(symbol_sz)) +#define SF_LEN_MAX (SF_LEN(SYMBOL_SZ_MAX)) -#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(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_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_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN))) diff --git a/lte/phy/include/liblte/phy/phch/pbch.h b/lte/phy/include/liblte/phy/phch/pbch.h index 9769f66cf..e8a79b513 100644 --- a/lte/phy/include/liblte/phy/phch/pbch.h +++ b/lte/phy/include/liblte/phy/phch/pbch.h @@ -89,13 +89,13 @@ LIBLTE_API int pbch_init(pbch_t *q, LIBLTE_API void pbch_free(pbch_t *q); LIBLTE_API int pbch_decode(pbch_t *q, - cf_t *sf_symbols, - cf_t *ce[MAX_PORTS], + cf_t *slot1_symbols, + cf_t *ce_slot1[MAX_PORTS], pbch_mib_t *mib); LIBLTE_API int pbch_encode(pbch_t *q, pbch_mib_t *mib, - cf_t *sf_symbols[MAX_PORTS]); + cf_t *slot1_symbols[MAX_PORTS]); LIBLTE_API void pbch_decode_reset(pbch_t *q); diff --git a/lte/phy/include/liblte/phy/phy.h b/lte/phy/include/liblte/phy/phy.h index 3de436895..eef9eccc1 100644 --- a/lte/phy/include/liblte/phy/phy.h +++ b/lte/phy/include/liblte/phy/phy.h @@ -92,8 +92,11 @@ #include "liblte/phy/phch/pbch.h" #include "liblte/phy/phch/pcfich.h" #include "liblte/phy/phch/phich.h" -#include "liblte/phy/phch/ue_sync.h" -#include "liblte/phy/phch/ue_dl.h" + +#include "liblte/phy/ue/ue_sync.h" +#include "liblte/phy/ue/ue_mib.h" +#include "liblte/phy/ue/ue_celldetect.h" +#include "liblte/phy/ue/ue_dl.h" #include "liblte/phy/scrambling/scrambling.h" diff --git a/lte/phy/include/liblte/phy/sync/pss.h b/lte/phy/include/liblte/phy/sync/pss.h index a27f92f6b..22828240e 100644 --- a/lte/phy/include/liblte/phy/sync/pss.h +++ b/lte/phy/include/liblte/phy/sync/pss.h @@ -72,7 +72,6 @@ typedef struct LIBLTE_API { cf_t *pss_signal_freq[3]; // One sequence for each N_id_2 cf_t *tmp_input; - float *conv_abs; cf_t *conv_output; }pss_synch_t; @@ -102,8 +101,7 @@ LIBLTE_API int pss_synch_set_N_id_2(pss_synch_t *q, LIBLTE_API int pss_synch_find_pss(pss_synch_t *q, cf_t *input, - float *corr_peak_value, - float *corr_mean_value); + float *corr_peak_value); LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q, cf_t *pss_recv); diff --git a/lte/phy/include/liblte/phy/sync/sync.h b/lte/phy/include/liblte/phy/sync/sync.h index 8dddbc7b5..0cf86b7c9 100644 --- a/lte/phy/include/liblte/phy/sync/sync.h +++ b/lte/phy/include/liblte/phy/sync/sync.h @@ -51,75 +51,60 @@ * functions sync_pss_det_absolute() and sync_pss_det_peakmean(). */ -enum sync_pss_det { ABSOLUTE, PEAK_MEAN}; - typedef struct LIBLTE_API { - pss_synch_t pss_find; - pss_synch_t pss_track; + pss_synch_t pss; sss_synch_t sss; - enum sync_pss_det pss_mode; - float find_threshold; - float track_threshold; + float threshold; + float mean_energy; float peak_value; + float mean_peak_value; uint32_t N_id_2; uint32_t N_id_1; - uint32_t slot_id; + uint32_t sf_idx; uint32_t fft_size; - uint32_t find_frame_size; + uint32_t frame_size; + uint64_t frame_cnt; float cfo; bool detect_cp; bool sss_en; + bool normalize_en; lte_cp_t cp; }sync_t; LIBLTE_API int sync_init(sync_t *q, - uint32_t find_frame_size, - uint32_t track_frame_size, + uint32_t frame_size, uint32_t fft_size); 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); +LIBLTE_API void sync_reset(sync_t *q); -/* 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 */ +/* Finds a correlation peak in the input signal around position find_offset */ LIBLTE_API int sync_find(sync_t *q, cf_t *input, + uint32_t find_offset, uint32_t *peak_position); -/* 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 */ -LIBLTE_API int sync_track(sync_t *q, - cf_t *input, - uint32_t offset, - uint32_t *peak_position); - /* Sets the threshold for peak comparison */ LIBLTE_API void sync_set_threshold(sync_t *q, - float find_threshold, - float track_threshold); + float threshold); -/* Set peak comparison to absolute value */ -LIBLTE_API void sync_pss_det_absolute(sync_t *q); +/* Gets the subframe idx (0 or 5) */ +LIBLTE_API uint32_t sync_get_sf_idx(sync_t *q); -/* Set peak comparison to relative to the mean */ -LIBLTE_API void sync_pss_det_peak_to_avg(sync_t *q); +/* Gets the last peak value */ +LIBLTE_API float sync_get_last_peak_value(sync_t *q); -/* Gets the slot id (0 or 10) */ -LIBLTE_API uint32_t sync_get_slot_id(sync_t *q); - -/* Gets the last peak-to-average ratio */ +/* Gets the mean peak value */ LIBLTE_API float sync_get_peak_value(sync_t *q); -/* Gets the N_id_2 from the last call to synch_run() */ -LIBLTE_API uint32_t sync_get_N_id_2(sync_t *q); +/* Gets the last input signal energy estimation value */ +LIBLTE_API float sync_get_input_energy(sync_t *q); -/* Gets the N_id_1 from the last call to synch_run() */ -LIBLTE_API uint32_t sync_get_N_id_1(sync_t *q); +/* Sets the N_id_2 to search for */ +LIBLTE_API int sync_set_N_id_2(sync_t *q, + uint32_t N_id_2); /* Gets the Physical CellId from the last call to synch_run() */ LIBLTE_API int sync_get_cell_id(sync_t *q); @@ -130,6 +115,10 @@ LIBLTE_API float sync_get_cfo(sync_t *q); /* Gets the CP length estimation from the last call to synch_run() */ LIBLTE_API lte_cp_t sync_get_cp(sync_t *q); +/* Enables/Disables energy normalization every frame. If disabled, uses the mean */ +LIBLTE_API void sync_normalize_en(sync_t *q, + bool enable); + /* Enables/Disables SSS detection */ LIBLTE_API void sync_sss_en(sync_t *q, bool enabled); diff --git a/lte/phy/include/liblte/phy/ue/ue_celldetect.h b/lte/phy/include/liblte/phy/ue/ue_celldetect.h new file mode 100644 index 000000000..4cc10c7a3 --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_celldetect.h @@ -0,0 +1,131 @@ +/** + * + * \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_CELLSEARCH_ +#define UE_CELLSEARCH_ + +#include + +#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 scans a signal for LTE cells using the known PSS + * and SSS sequences. + * + * The function ue_celldetect_scan() shall be called multiple times, + * each passing a number of samples multiple of 4800, sampled at 960 KHz + * (that is, 5 ms of samples). + * + * The function returns 0 until a signal is found nof_frames_detected times or + * after nof_frames_total with no signal detected. + * + * See ue_cell_detect.c for an example. + * + ************************************************************/ + +/** + * TODO: Check also peak offset + */ + +#define CS_DEFAULT_MAXFRAMES_TOTAL 300 +#define CS_DEFAULT_MAXFRAMES_DETECTED 30 + +#define CS_DEFAULT_NOFFRAMES_TOTAL 100 +#define CS_DEFAULT_NOFFRAMES_DETECTED 10 + +#define CS_FIND_THRESHOLD 0.6 + +#define CS_FRAME_UNALIGNED -3 +#define CS_CELL_DETECTED 2 +#define CS_CELL_NOT_DETECTED 1 + +typedef struct LIBLTE_API { + uint32_t cell_id; + lte_cp_t cp; + float peak; + uint32_t mode; +} ue_celldetect_result_t; + + +typedef struct LIBLTE_API { + sync_t sfind; + uint32_t max_frames_total; + uint32_t max_frames_detected; + uint32_t nof_frames_total; + uint32_t nof_frames_detected; + + uint32_t current_nof_detected; + uint32_t current_nof_total; + + uint32_t current_N_id_2; + + uint32_t *mode_ntimes; + char *mode_counted; + + ue_celldetect_result_t *candidates; +} ue_celldetect_t; + + +LIBLTE_API int ue_celldetect_init(ue_celldetect_t *q); + +LIBLTE_API int ue_celldetect_init_max(ue_celldetect_t *q, + uint32_t max_frames_total, + uint32_t max_frames_detected); + +LIBLTE_API void ue_celldetect_free(ue_celldetect_t *q); + +LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q); + +LIBLTE_API int ue_celldetect_scan(ue_celldetect_t *q, + cf_t *signal, + uint32_t nsamples, + ue_celldetect_result_t *found_cell); + +LIBLTE_API int ue_celldetect_set_nof_frames_total(ue_celldetect_t *q, + uint32_t nof_frames); + +LIBLTE_API int ue_celldetect_set_nof_frames_detected(ue_celldetect_t *q, + uint32_t nof_frames); + +LIBLTE_API void ue_celldetect_set_threshold(ue_celldetect_t *q, + float threshold); + +LIBLTE_API void ue_celldetect_reset(ue_celldetect_t *q); + + + + + + +#endif // SYNC_FRAME_ + diff --git a/lte/phy/include/liblte/phy/phch/ue_dl.h b/lte/phy/include/liblte/phy/ue/ue_dl.h similarity index 94% rename from lte/phy/include/liblte/phy/phch/ue_dl.h rename to lte/phy/include/liblte/phy/ue/ue_dl.h index 22ecd718d..e38d0ebf0 100644 --- a/lte/phy/include/liblte/phy/phch/ue_dl.h +++ b/lte/phy/include/liblte/phy/ue/ue_dl.h @@ -57,6 +57,7 @@ #define NOF_HARQ_PROCESSES 8 typedef struct LIBLTE_API { + pbch_t pbch; pcfich_t pcfich; pdcch_t pdcch; pdsch_t pdsch; @@ -74,6 +75,9 @@ typedef struct LIBLTE_API { uint64_t pkts_total; uint64_t nof_trials; + uint32_t sfn; + bool pbch_decoded; + uint16_t user_rnti; }ue_dl_t; @@ -86,11 +90,10 @@ LIBLTE_API int ue_dl_init(ue_dl_t *q, LIBLTE_API void ue_dl_free(ue_dl_t *q); -LIBLTE_API int ue_dl_receive(ue_dl_t *q, +LIBLTE_API int ue_dl_decode(ue_dl_t *q, cf_t *sf_buffer, char *data, - uint32_t sf_idx, - uint32_t sfn, + uint32_t sf_idx, uint16_t rnti); #endif \ No newline at end of file diff --git a/lte/phy/include/liblte/phy/ue/ue_mib.h b/lte/phy/include/liblte/phy/ue/ue_mib.h new file mode 100644 index 000000000..31624d6f9 --- /dev/null +++ b/lte/phy/include/liblte/phy/ue/ue_mib.h @@ -0,0 +1,109 @@ +/** + * + * \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_MIB_ +#define UE_MIB_ + + +/************************************************************ + * + * This object decodes the MIB from the PBCH of an LTE signal. + * + * The function ue_mib_decode() shall be called multiple times, + * each passing a number of samples multiple of 19200, sampled at 1.92 MHz + * (that is, 10 ms of samples). + * + * The function uses the sync_t object to find the PSS sequence and + * decode the PBCH to obtain the MIB. + * + * The function returns 0 until the MIB is decoded. + * + * See ue_cell_detect.c for an example. + * + ************************************************************/ + +#include + +#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" + +#define MIB_FIND_THRESHOLD 0.6 + +#define MIB_NOF_PORTS 2 + +#define MIB_FRAME_SIZE 9600 + +#define MIB_FRAME_UNALIGNED -3 +#define MIB_FOUND 1 +#define MIB_NOTFOUND 0 + +typedef struct LIBLTE_API { + sync_t sfind; + + uint32_t cell_id; + + cf_t *slot1_symbols; + cf_t *ce[MIB_NOF_PORTS]; + + lte_fft_t fft; + chest_t chest; + pbch_t pbch; + + uint32_t frame_cnt; + uint32_t last_frame_trial; +} ue_mib_t; + + +LIBLTE_API int ue_mib_init(ue_mib_t *q, + uint32_t cell_id, + lte_cp_t cp); + +LIBLTE_API void ue_mib_free(ue_mib_t *q); + +LIBLTE_API void ue_mib_reset(ue_mib_t *q); + +LIBLTE_API int ue_mib_decode(ue_mib_t *q, + cf_t *signal, + uint32_t nsamples, + pbch_mib_t *mib); + +LIBLTE_API void ue_mib_set_threshold(ue_mib_t *q, + float threshold); + +LIBLTE_API void ue_mib_reset(ue_mib_t *q); + + + + + + +#endif // SYNC_FRAME_ + diff --git a/lte/phy/include/liblte/phy/phch/ue_sync.h b/lte/phy/include/liblte/phy/ue/ue_sync.h similarity index 62% rename from lte/phy/include/liblte/phy/phch/ue_sync.h rename to lte/phy/include/liblte/phy/ue/ue_sync.h index d26b9a8b2..1e608ebbf 100644 --- a/lte/phy/include/liblte/phy/phch/ue_sync.h +++ b/lte/phy/include/liblte/phy/ue/ue_sync.h @@ -36,57 +36,36 @@ #include "liblte/phy/ch_estimation/chest.h" #include "liblte/phy/phch/pbch.h" #include "liblte/phy/common/fft.h" -#include "liblte/phy/agc/agc.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 + * This object automatically manages the cell synchronization procedure. * * 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_AGC, SF_FIND, SF_TRACK} ue_sync_state_t; - -#define SYNC_PBCH_NOF_PRB 6 -#define SYNC_PBCH_NOF_PORTS 2 +typedef enum LIBLTE_API { SF_FIND, SF_TRACK} ue_sync_state_t; #define TRACK_MAX_LOST 10 - -#define DEFAULT_NOF_MIB_DECODES 10 - -#define AGC_NOF_FRAMES 100 - #define MEASURE_EXEC_TIME typedef struct LIBLTE_API { - sync_t s; + sync_t sfind; + sync_t strack; 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 *receive_buffer; - cf_t *sf_symbols; - cf_t *ce[SYNC_PBCH_NOF_PORTS]; /* These count half frames (5ms) */ uint64_t frame_ok_cnt; @@ -101,21 +80,7 @@ typedef struct LIBLTE_API { cfo_t cfocorr; float cur_cfo; - - /* Variables for PBCH decoding */ - agc_t agc; - 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 change_srate; - uint32_t nof_mib_decodes; - + bool decode_sss_on_track; uint32_t peak_idx; @@ -128,7 +93,7 @@ typedef struct LIBLTE_API { LIBLTE_API int ue_sync_init(ue_sync_t *q, - double (set_rate_callback)(void*, double), + lte_cell_t cell, int (recv_callback)(void*, void*, uint32_t), void *stream_handler); @@ -137,34 +102,15 @@ 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_set_nof_pbch_decodes(ue_sync_t *q, - uint32_t nof_pbch_decodes); - - 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_change_srate(ue_sync_t *q, - bool enabled); - -LIBLTE_API void ue_sync_pbch_always(ue_sync_t *q, - bool enabled); - 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 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); diff --git a/lte/phy/include/liblte/phy/utils/vector.h b/lte/phy/include/liblte/phy/utils/vector.h index edbb2c7fc..3989dd69d 100644 --- a/lte/phy/include/liblte/phy/utils/vector.h +++ b/lte/phy/include/liblte/phy/utils/vector.h @@ -75,6 +75,7 @@ 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); +LIBLTE_API void vec_deinterleave_real_cf(cf_t *x, float *real, uint32_t len); /* vector product (element-wise) */ LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len); @@ -101,6 +102,7 @@ LIBLTE_API float vec_avg_power_cf(cf_t *x, uint32_t len); /* return the index of the maximum value in the vector */ LIBLTE_API uint32_t vec_max_fi(float *x, uint32_t len); +LIBLTE_API uint32_t vec_max_abs_ci(cf_t *x, uint32_t len); /* 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, uint32_t len); diff --git a/lte/phy/lib/ch_estimation/src/chest.c b/lte/phy/lib/ch_estimation/src/chest.c index 843266d7f..293d3fe2b 100644 --- a/lte/phy/lib/ch_estimation/src/chest.c +++ b/lte/phy/lib/ch_estimation/src/chest.c @@ -38,7 +38,7 @@ #define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz) #define SF_SZ(q) (2 * SLOT_SZ(q)) -//#define VOLK_INTERP +#define VOLK_INTERP void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) { chest_ref_fprint(q, stream, nslot, port_id); @@ -111,16 +111,18 @@ int chest_ce_ref(chest_t *q, cf_t *input, uint32_t nslot, uint32_t port_id, uint channel_ref = input[tidx * q->nof_re + fidx]; q->refsignal[port_id][nslot].refs[nref].recv_simbol = channel_ref; + DEBUG("Reference %2d pos (%2d,%2d)=%3d %.2f dB %.2f/%.2f=%.2f\n", nref, tidx, fidx, tidx * q->nof_re + fidx, 10*log10f(cabsf(channel_ref/known_ref)), cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI, cargf(channel_ref/known_ref)/M_PI); - + + /* FIXME: compare with threshold */ if (channel_ref != 0) { q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref; } else { - q->refsignal[port_id][nslot].ch_est[nref] = 0; + q->refsignal[port_id][nslot].ch_est[nref] = 1e-6; } ret = LIBLTE_SUCCESS; } @@ -145,7 +147,7 @@ int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32 if (q->refsignal[port_id][nslot].nsymbols <= 2) { refsignal_t *r = &q->refsignal[port_id][nslot]; - INFO("Estimating channel slot=%d port=%d using %d reference signals\n", + DEBUG("Estimating channel slot=%d port=%d using %d reference signals\n", nslot, port_id, r->nof_refs); for (i=0;inof_refs;i++) { @@ -182,7 +184,7 @@ int chest_ce_slot_port(chest_t *q, cf_t *input, cf_t *ce, uint32_t nslot, uint32 for (j=0;jsymbols_ref[0] * q->nof_re + i]; } - } + } for (j=0;jnof_symbols;j++) { ce[j * q->nof_re + i] = y[j]; } @@ -276,6 +278,7 @@ int chest_ref_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_ { ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell); +#ifdef VOLK_INTERP if (ret == LIBLTE_SUCCESS) { if (nslot == 0) { ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2); @@ -285,6 +288,7 @@ int chest_ref_LTEDL_slot_port(chest_t *q, uint32_t nslot, uint32_t port_id, lte_ } } } +#endif } return ret; } @@ -318,6 +322,12 @@ void chest_free(chest_t *q) { refsignal_free(&q->refsignal[p][n]); } } +#ifdef VOLK_INTERP + for (p=0;pinterp_freq[p]); + interp_free(&q->interp_time[p]); + } +#endif bzero(q, sizeof(chest_t)); } diff --git a/lte/phy/lib/common/src/fft.c b/lte/phy/lib/common/src/fft.c index a21aa2c15..b5818c979 100644 --- a/lte/phy/lib/common/src/fft.c +++ b/lte/phy/lib/common/src/fft.c @@ -61,7 +61,7 @@ int lte_fft_init_(lte_fft_t *q, lte_cp_t cp, uint32_t nof_prb, dft_dir_t dir) { q->cp = cp; q->nof_re = nof_prb * RE_X_RB; q->nof_guards = ((symbol_sz - q->nof_re) / 2); - q->slot_sz = SLOT_LEN(symbol_sz, cp); + q->slot_sz = SLOT_LEN(symbol_sz); DEBUG("Init %s symbol_sz=%d, nof_symbols=%d, cp=%s, nof_re=%d, nof_guards=%d\n", dir==FORWARD?"FFT":"iFFT", q->symbol_sz, q->nof_symbols, diff --git a/lte/phy/lib/common/test/fft_test.c b/lte/phy/lib/common/test/fft_test.c index 8d6f883bf..74f14f311 100644 --- a/lte/phy/lib/common/test/fft_test.c +++ b/lte/phy/lib/common/test/fft_test.c @@ -87,7 +87,7 @@ int main(int argc, char **argv) { perror("malloc"); exit(-1); } - outfft = malloc(sizeof(cf_t) * SLOT_LEN_CPNORM(lte_symbol_sz(n_prb))); + outfft = malloc(sizeof(cf_t) * SLOT_LEN(lte_symbol_sz(n_prb))); if (!outfft) { perror("malloc"); exit(-1); diff --git a/lte/phy/lib/fec/src/turbodecoder.c b/lte/phy/lib/fec/src/turbodecoder.c index 250b078fd..171eefde3 100644 --- a/lte/phy/lib/fec/src/turbodecoder.c +++ b/lte/phy/lib/fec/src/turbodecoder.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "liblte/phy/fec/turbodecoder.h" @@ -39,7 +40,9 @@ * Decoder * ************************************************/ -void map_gen_beta(map_gen_t *s, llr_t *input, llr_t *parity, uint32_t long_cb) { +void map_gen_beta(map_gen_t * s, llr_t * input, llr_t * parity, + uint32_t long_cb) +{ llr_t m_b[8], new[8], old[8]; llr_t x, y, xy; int k; @@ -84,8 +87,9 @@ void map_gen_beta(map_gen_t *s, llr_t *input, llr_t *parity, uint32_t long_cb) { } } -void map_gen_alpha(map_gen_t *s, llr_t *input, llr_t *parity, llr_t *output, - uint32_t long_cb) { +void map_gen_alpha(map_gen_t * s, llr_t * input, llr_t * parity, llr_t * output, + uint32_t long_cb) +{ llr_t m_b[8], new[8], old[8], max1[8], max0[8]; llr_t m1, m0; llr_t x, y, xy; @@ -150,7 +154,8 @@ void map_gen_alpha(map_gen_t *s, llr_t *input, llr_t *parity, llr_t *output, } } -int map_gen_init(map_gen_t *h, int max_long_cb) { +int map_gen_init(map_gen_t * h, int max_long_cb) +{ bzero(h, sizeof(map_gen_t)); h->beta = malloc(sizeof(llr_t) * (max_long_cb + TOTALTAIL + 1) * NUMSTATES); if (!h->beta) { @@ -161,15 +166,17 @@ int map_gen_init(map_gen_t *h, int max_long_cb) { return 0; } -void map_gen_free(map_gen_t *h) { +void map_gen_free(map_gen_t * h) +{ if (h->beta) { free(h->beta); } bzero(h, sizeof(map_gen_t)); } -void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output, - uint32_t long_cb) { +void map_gen_dec(map_gen_t * h, llr_t * input, llr_t * parity, llr_t * output, + uint32_t long_cb) +{ uint32_t k; h->beta[(long_cb + TAIL) * NUMSTATES] = 0; @@ -185,7 +192,8 @@ void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output, * TURBO DECODER INTERFACE * ************************************************/ -int tdec_init(tdec_t *h, uint32_t max_long_cb) { +int tdec_init(tdec_t * h, uint32_t max_long_cb) +{ int ret = -1; bzero(h, sizeof(tdec_t)); uint32_t len = max_long_cb + TOTALTAIL; @@ -227,13 +235,14 @@ int tdec_init(tdec_t *h, uint32_t max_long_cb) { } ret = 0; - clean_and_exit: if (ret == -1) { +clean_and_exit:if (ret == -1) { tdec_free(h); } return ret; } -void tdec_free(tdec_t *h) { +void tdec_free(tdec_t * h) +{ if (h->llr1) { free(h->llr1); } @@ -257,7 +266,8 @@ void tdec_free(tdec_t *h) { bzero(h, sizeof(tdec_t)); } -void tdec_iteration(tdec_t *h, llr_t *input, uint32_t long_cb) { +void tdec_iteration(tdec_t * h, llr_t * input, uint32_t long_cb) +{ uint32_t i; // Prepare systematic and parity bits for MAP DEC #1 @@ -276,19 +286,19 @@ void tdec_iteration(tdec_t *h, llr_t *input, uint32_t long_cb) { // Prepare systematic and parity bits for MAP DEC #1 for (i = 0; i < long_cb; i++) { h->syst[i] = h->llr1[h->interleaver.forward[i]] - - h->w[h->interleaver.forward[i]]; + - h->w[h->interleaver.forward[i]]; h->parity[i] = input[RATE * i + 2]; } for (i = long_cb; i < long_cb + RATE; i++) { h->syst[i] = - input[RATE * long_cb + NINPUTS * RATE + NINPUTS * (i - long_cb)]; + input[RATE * long_cb + NINPUTS * RATE + NINPUTS * (i - long_cb)]; h->parity[i] = input[RATE * long_cb + NINPUTS * RATE - + NINPUTS * (i - long_cb) + 1]; + + NINPUTS * (i - long_cb) + 1]; } // Run MAP DEC #1 map_gen_dec(&h->dec, h->syst, h->parity, h->llr2, long_cb); - + // Update a-priori LLR from the last iteration for (i = 0; i < long_cb; i++) { h->w[i] += h->llr2[h->interleaver.reverse[i]] - h->llr1[i]; @@ -296,25 +306,28 @@ void tdec_iteration(tdec_t *h, llr_t *input, uint32_t long_cb) { } -int tdec_reset(tdec_t *h, uint32_t long_cb) { - memset(h->w, 0, sizeof(llr_t) * long_cb); +int tdec_reset(tdec_t * h, uint32_t long_cb) +{ if (long_cb > h->max_long_cb) { fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n", - h->max_long_cb); + h->max_long_cb); return -1; } + memset(h->w, 0, sizeof(llr_t) * long_cb); return tc_interl_LTE_gen(&h->interleaver, long_cb); } -void tdec_decision(tdec_t *h, char *output, uint32_t long_cb) { +void tdec_decision(tdec_t * h, char *output, uint32_t long_cb) +{ uint32_t i; for (i = 0; i < long_cb; i++) { - output[i] = (h->llr2[h->interleaver.reverse[i]] > 0) ? 1 : 0; + output[i] = (h->llr2[h->interleaver.reverse[i]] > 0) ? 1 : 0; } } -void tdec_run_all(tdec_t *h, llr_t *input, char *output, uint32_t nof_iterations, - uint32_t long_cb) { +void tdec_run_all(tdec_t * h, llr_t * input, char *output, + uint32_t nof_iterations, uint32_t long_cb) +{ uint32_t iter = 0; tdec_reset(h, long_cb); @@ -326,4 +339,3 @@ void tdec_run_all(tdec_t *h, llr_t *input, char *output, uint32_t nof_iterations tdec_decision(h, output, long_cb); } - diff --git a/lte/phy/lib/mimo/src/precoding.c b/lte/phy/lib/mimo/src/precoding.c index 036de038d..6c21ff21e 100644 --- a/lte/phy/lib/mimo/src/precoding.c +++ b/lte/phy/lib/mimo/src/precoding.c @@ -124,6 +124,11 @@ int precoding_type(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_layers, /* ZF detector */ int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols) { + for (int i=0;icell.nof_ports;i++) { - if (ce[i] == NULL) { + if (ce_slot1[i] == NULL) { return LIBLTE_ERROR_INVALID_INPUTS; - } else { - ce_slot[i] = &ce[i][q->cell.nof_prb * RE_X_RB * CP_NSYMB(q->cell.cp)]; - } + } } - slot1_symbols = &sf_symbols[q->cell.nof_prb * RE_X_RB * CP_NSYMB(q->cell.cp)]; /* Set pointers for layermapping & precoding */ nof_bits = 2 * q->nof_symbols; @@ -495,7 +490,7 @@ int pbch_decode(pbch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], pbch_mib_t *mi /* extract channel estimates */ for (i = 0; i < q->cell.nof_ports; i++) { - if (q->nof_symbols != pbch_get(ce_slot[i], q->ce[i], q->cell)) { + if (q->nof_symbols != pbch_get(ce_slot1[i], q->ce[i], q->cell)) { fprintf(stderr, "There was an error getting the PBCH symbols\n"); return LIBLTE_ERROR; } @@ -508,7 +503,7 @@ int pbch_decode(pbch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], pbch_mib_t *mi for (na = 0; na < q->cell.nof_ports && !ret; na++) { nant = nant_[na]; - INFO("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); + DEBUG("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); /* in conctrol channels, only diversity is supported */ if (nant == 1) { @@ -554,21 +549,18 @@ int pbch_decode(pbch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], pbch_mib_t *mi /** Converts the MIB message to symbols mapped to SLOT #1 ready for transmission */ -int pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *sf_symbols[MAX_PORTS]) { +int pbch_encode(pbch_t *q, pbch_mib_t *mib, cf_t *slot1_symbols[MAX_PORTS]) { int i; int nof_bits; - cf_t *slot1_symbols[MAX_PORTS]; cf_t *x[MAX_LAYERS]; if (q != NULL && mib != NULL) { for (i=0;icell.nof_ports;i++) { - if (sf_symbols[i] == NULL) { + if (slot1_symbols[i] == NULL) { return LIBLTE_ERROR_INVALID_INPUTS; - } else { - slot1_symbols[i] = &sf_symbols[i][q->cell.nof_prb * RE_X_RB * CP_NSYMB(q->cell.cp)]; - } + } } /* Set pointers for layermapping & precoding */ nof_bits = 2 * q->nof_symbols; diff --git a/lte/phy/lib/phch/src/pdsch.c b/lte/phy/lib/phch/src/pdsch.c index 54892bd72..2326b8f23 100644 --- a/lte/phy/lib/phch/src/pdsch.c +++ b/lte/phy/lib/phch/src/pdsch.c @@ -517,6 +517,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, 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); + /* Rate Unmatching */ if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size, &e_bits[rp], n_e, @@ -532,7 +533,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, char *cb_in_ptr; crc_t *crc_ptr; tdec_reset(&q->decoder, cb_len); - + do { tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len); @@ -544,19 +545,20 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, crc_ptr = &q->crc_cb; } else { len_crc = tbs+24; + bzero(q->cb_in, F*sizeof(char)); cb_in_ptr = &q->cb_in[F]; crc_ptr = &q->crc_tb; } - + + tdec_decision(&q->decoder, q->cb_in, cb_len); + /* Check Codeblock CRC and stop early if incorrect */ if (!crc_checksum(crc_ptr, cb_in_ptr, len_crc)) { early_stop = true; } } while (q->nof_iterations < TDEC_MAX_ITERATIONS && !early_stop); - - tdec_decision(&q->decoder, q->cb_in, cb_len); - + q->average_nof_iterations = EXPAVERAGE((float) q->nof_iterations, q->average_nof_iterations, q->average_nof_iterations_n); @@ -588,7 +590,6 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, par_tx = bit_unpack(&p_parity, 24); if (!par_rx) { - vec_fprint_hex(stdout, data, tbs); printf("\n\tCAUTION!! Received all-zero transport block\n\n"); } @@ -672,10 +673,23 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, demod_soft_sigma_set(&q->demod, 2.0 / q->mod[harq_process->mcs.mod - 1].nbits_x_symbol); demod_soft_table_set(&q->demod, &q->mod[harq_process->mcs.mod - 1]); demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols); + + /* + for (int j=0;jpdsch_d[j])) || isnan(cimagf(q->pdsch_d[j]))) { + printf("\nerror in d[%d]=%f+%f symbols:%f+%f ce0:%f+%f ce1:%f+%f\n",j, + crealf(q->pdsch_d[j]), cimagf(q->pdsch_d[j]), + crealf(q->pdsch_symbols[0][j]), cimagf(q->pdsch_symbols[0][j]), + crealf(q->ce[0][j]), cimagf(q->ce[0][j]), + crealf(q->ce[1][j]), cimagf(q->ce[1][j]) + ); + } + } + */ /* descramble */ scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 0, nof_bits_e); - + return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx); } else { return LIBLTE_ERROR_INVALID_INPUTS; diff --git a/lte/phy/lib/phch/src/ue_sync.c b/lte/phy/lib/phch/src/ue_sync.c deleted file mode 100644 index 22f55f0f4..000000000 --- a/lte/phy/lib/phch/src/ue_sync.c +++ /dev/null @@ -1,629 +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 -#include -#include -#include -#include - - -#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 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); - - -static void update_threshold(ue_sync_t *q) { - int symbol_sz = lte_symbol_sz(q->cell.nof_prb); - if (symbol_sz > 0) { - switch (symbol_sz) { - case 128: - sync_set_threshold(&q->s, 10000, 1000); - break; - case 256: - sync_set_threshold(&q->s, 20000, 2000); - break; - case 512: - sync_set_threshold(&q->s, 30000, 3000); - break; - case 1024: - sync_set_threshold(&q->s, 40000, 4000); - break; - case 2048: - sync_set_threshold(&q->s, 50000, 5000); - } - } -} - -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 && - set_rate_callback != NULL && - recv_callback != 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->change_srate = true; - q->nof_mib_decodes = DEFAULT_NOF_MIB_DECODES; - 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 (agc_init(&q->agc)) { - goto clean_exit; - } - - if(sync_init(&q->s, CURRENT_SFLEN, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) { - goto clean_exit; - } - - sync_pss_det_absolute(&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->receive_buffer = vec_malloc(3 * MAXIMUM_SFLEN * sizeof(cf_t)); - if (!q->receive_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;ice[i] = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t)); - if (!q->ce[i]) { - perror("malloc"); - goto clean_exit; - } - } - - update_threshold(q); - - 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->receive_buffer) { - free(q->receive_buffer); - } - if (q->sf_symbols) { - free(q->sf_symbols); - } - for (int i=0;ice[i]) { - free(q->ce[i]); - } - } - mib_decoder_free(q); - cfo_free(&q->cfocorr); - sync_free(&q->s); - agc_free(&q->agc); -} - -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; -} - -void ue_sync_change_srate(ue_sync_t *q, bool enabled) { - q->change_srate = enabled; -} - -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 (q->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)); - - update_threshold(q); - - ue_sync_reset(q); - INFO("Set sampling rate %.2f MHz, fft_size=%d, sf_len=%d Threshold=%.2f/%.2f Texec=%d us\n", - (float) lte_sampling_freq_hz(q->cell.nof_prb)/1000000, - CURRENT_FFTSIZE, CURRENT_SFLEN, q->s.find_threshold, q->s.track_threshold, (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; -} - -void ue_sync_set_nof_pbch_decodes(ue_sync_t *q, uint32_t nof_pbch_decodes) { - q->nof_mib_decodes = nof_pbch_decodes; -} - -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; - - if (!q->pbch_decoded) { - printf("\n\nMIB decoded:\n"); - pbch_mib_fprint(stdout, &q->mib, q->cell.id); - - if (q->cell.nof_prb != q->mib.nof_prb) { - q->cell.nof_prb = q->mib.nof_prb; - if (q->change_srate) { - 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/2); - 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 at %d, value %.3f, SF_idx: %d, Cell_id: %d CP: %s\n", - q->peak_idx, sync_get_peak_value(&q->s), 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) { - INFO("\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. Peak %.3f, %d lost\n", sync_get_peak_value(&q->s), (int) q->frame_no_cnt); - } - - return LIBLTE_SUCCESS; -} - -static int receive_samples(ue_sync_t *q) { - - if (q->cell.nof_prb >= 6 && q->cell.nof_prb <= 100) { - /* 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) */ - memcpy(q->receive_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->receive_buffer[q->time_offset], CURRENT_SFLEN - q->time_offset) < 0) { - return LIBLTE_ERROR; - } - - /* reset time offset */ - q->time_offset = 0; - - return LIBLTE_SUCCESS; - } else { - return LIBLTE_ERROR_INVALID_INPUTS; - } -} - -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; - } - - agc_process(&q->agc, q->receive_buffer, q->input_buffer, CURRENT_SFLEN); - - switch (q->state) { - case SF_AGC: - q->frame_total_cnt++; - if (q->frame_total_cnt >= AGC_NOF_FRAMES) { - q->state = SF_FIND; - q->frame_total_cnt = 0; - } - ret = 0; - break; - 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_value(&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"); - } - } - } else if (q->peak_idx != 0) { - uint32_t rlen; - if (q->peak_idx < CURRENT_SFLEN/2) { - rlen = CURRENT_SFLEN/2-q->peak_idx; - } else { - rlen = q->peak_idx; - } - if (q->recv_callback(q->stream, q->receive_buffer, rlen) < 0) { - return LIBLTE_ERROR; - } - } - 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: Value=%.3f SF=%d Track_idx=%d Offset=%d CFO: %f\n", - (int) q->frame_total_cnt, sync_get_peak_value(&q->s), 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"); - q->state = SF_FIND; - 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 < q->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 >= q->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_AGC; - - 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 - - pbch_decode_reset(&q->pbch); -} - diff --git a/lte/phy/lib/phch/test/CMakeLists.txt b/lte/phy/lib/phch/test/CMakeLists.txt index eb8b8dd1e..328d8128a 100644 --- a/lte/phy/lib/phch/test/CMakeLists.txt +++ b/lte/phy/lib/phch/test/CMakeLists.txt @@ -19,23 +19,6 @@ # 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 ######################################################################## diff --git a/lte/phy/lib/phch/test/pbch_file_test.c b/lte/phy/lib/phch/test/pbch_file_test.c index 5a243856f..78e2b982a 100644 --- a/lte/phy/lib/phch/test/pbch_file_test.c +++ b/lte/phy/lib/phch/test/pbch_file_test.c @@ -117,14 +117,14 @@ int base_init() { exit(-1); } - fft_buffer = malloc(2 * CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t)); + fft_buffer = malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); if (!fft_buffer) { perror("malloc"); return -1; } for (i=0;iconv_abs = vec_malloc(buffer_size * sizeof(float)); - if (!q->conv_abs) { - fprintf(stderr, "Error allocating memory\n"); - goto clean_and_exit; - } q->tmp_input = vec_malloc(buffer_size * sizeof(cf_t)); if (!q->tmp_input) { fprintf(stderr, "Error allocating memory\n"); @@ -160,9 +157,6 @@ void pss_synch_free(pss_synch_t *q) { if (q->conv_output) { free(q->conv_output); } - if (q->conv_abs) { - free(q->conv_abs); - } bzero(q, sizeof(pss_synch_t)); } @@ -231,8 +225,7 @@ int pss_synch_set_N_id_2(pss_synch_t *q, uint32_t N_id_2) { * * Input buffer must be subframe_size long. */ -int pss_synch_find_pss(pss_synch_t *q, cf_t *input, - float *corr_peak_value, float *corr_mean_value) +int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value) { int ret = LIBLTE_ERROR_INVALID_INPUTS; @@ -252,6 +245,7 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); bzero(&q->tmp_input[q->frame_size], q->fft_size * sizeof(cf_t)); + /* Correlate input with PSS sequence */ #ifdef CONVOLUTION_FFT conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input, q->pss_signal_freq[q->N_id_2], q->conv_output); @@ -259,16 +253,11 @@ int pss_synch_find_pss(pss_synch_t *q, cf_t *input, conv_output_len = conv_cc(input, q->pss_signal_freq[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); #endif - vec_abs_cf(q->conv_output, q->conv_abs, conv_output_len); - corr_peak_pos = vec_max_fi(q->conv_abs, conv_output_len); + /* Find maximum of the absolute value of the correlation */ + corr_peak_pos = vec_max_abs_ci(q->conv_output, conv_output_len); if (corr_peak_value) { - *corr_peak_value = q->conv_abs[corr_peak_pos]; - } - if (corr_mean_value) { - *corr_mean_value = vec_acc_ff(q->conv_abs, conv_output_len) - / conv_output_len; + *corr_peak_value = cabsf(q->conv_output[corr_peak_pos]); } - ret = (int) corr_peak_pos; } return ret; diff --git a/lte/phy/lib/sync/src/sss.c b/lte/phy/lib/sync/src/sss.c index 6feef89da..5755ea18f 100644 --- a/lte/phy/lib/sync/src/sss.c +++ b/lte/phy/lib/sync/src/sss.c @@ -44,7 +44,7 @@ void generate_N_id_1_table(uint32_t table[30][30]); int sss_synch_init(sss_synch_t *q, uint32_t fft_size) { if (q != NULL && - fft_size < 2048) + fft_size <= 2048) { uint32_t N_id_2; struct sss_tables sss_tables; @@ -73,7 +73,7 @@ int sss_synch_init(sss_synch_t *q, uint32_t fft_size) { int sss_synch_realloc(sss_synch_t *q, uint32_t fft_size) { if (q != NULL && - fft_size < 2048) + fft_size <= 2048) { dft_plan_free(&q->dftp_input); if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) { diff --git a/lte/phy/lib/sync/src/sync.c b/lte/phy/lib/sync/src/sync.c index 1370dd066..34e4bd28e 100644 --- a/lte/phy/lib/sync/src/sync.c +++ b/lte/phy/lib/sync/src/sync.c @@ -26,6 +26,8 @@ */ #include +#include +#include #include "liblte/phy/utils/debug.h" #include "liblte/phy/common/phy_common.h" @@ -41,25 +43,25 @@ static bool fft_size_isvalid(uint32_t fft_size) { } } -int sync_init(sync_t *q, uint32_t find_frame_size, uint32_t track_frame_size, uint32_t fft_size) { +int sync_init(sync_t *q, uint32_t frame_size, uint32_t fft_size) { int ret = LIBLTE_ERROR_INVALID_INPUTS; if (q != NULL && - find_frame_size > fft_size && - find_frame_size < 307200 && + frame_size >= fft_size && + frame_size <= 307200 && fft_size_isvalid(fft_size)) { bzero(q, sizeof(sync_t)); - q->pss_mode = PEAK_MEAN; q->detect_cp = true; + q->normalize_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; + q->frame_size = frame_size; - if (pss_synch_init_fft(&q->pss_find, find_frame_size, fft_size)) { + if (pss_synch_init_fft(&q->pss, frame_size, fft_size)) { fprintf(stderr, "Error initializing PSS object\n"); return LIBLTE_ERROR; } @@ -67,119 +69,74 @@ int sync_init(sync_t *q, uint32_t find_frame_size, uint32_t track_frame_size, ui fprintf(stderr, "Error initializing SSS object\n"); return LIBLTE_ERROR; } - if (pss_synch_init_fft(&q->pss_track, track_frame_size, fft_size)) { - fprintf(stderr, "Error initializing PSS track 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; -} - -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); + DEBUG("SYNC init with frame_size=%d and fft_size=%d\n", frame_size, fft_size); ret = LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters frame_size: %d, fft_size: %d\n", frame_size, fft_size); } - return ret; + return ret; } void sync_free(sync_t *q) { if (q) { - pss_synch_free(&q->pss_track); - pss_synch_free(&q->pss_find); + pss_synch_free(&q->pss); sss_synch_free(&q->sss); } } -void sync_pss_det_absolute(sync_t *q) { - q->pss_mode = ABSOLUTE; -} -void sync_pss_det_peak_to_avg(sync_t *q) { - q->pss_mode = PEAK_MEAN; -} - -void sync_set_threshold(sync_t *q, float find_threshold, float track_threshold) { - q->find_threshold = find_threshold; - q->track_threshold = track_threshold; +void sync_set_threshold(sync_t *q, float threshold) { + q->threshold = threshold; } void sync_sss_en(sync_t *q, bool enabled) { q->sss_en = enabled; } +void sync_normalize_en(sync_t *q, bool enable) { + q->normalize_en = enable; +} + 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) { - 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; - } else { - fprintf(stderr, "Error getting cell_id, invalid N_id_1 or N_id_2\n"); - return LIBLTE_ERROR; - } + 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; } else { - fprintf(stderr, "Error getting cell_id, N_id_2 not set\n"); + fprintf(stderr, "Error getting cell_id, invalid N_id_1 or N_id_2\n"); return LIBLTE_ERROR; } } -uint32_t sync_get_N_id_1(sync_t *q) { - return q->N_id_1; -} - -uint32_t sync_get_N_id_2(sync_t *q) { - return q->N_id_2; +int sync_set_N_id_2(sync_t *q, uint32_t N_id_2) { + if (lte_N_id_2_isvalid(N_id_2)) { + q->N_id_2 = N_id_2; + return LIBLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid N_id_2=%d\n", N_id_2); + return LIBLTE_ERROR_INVALID_INPUTS; + } } -uint32_t sync_get_slot_id(sync_t *q) { - return q->slot_id; +uint32_t sync_get_sf_idx(sync_t *q) { + return q->sf_idx; } float sync_get_cfo(sync_t *q) { return q->cfo; } -float sync_get_peak_value(sync_t *q) { +float sync_get_last_peak_value(sync_t *q) { return q->peak_value; } +float sync_get_peak_value(sync_t *q) { + return q->mean_peak_value; +} + void sync_cp_en(sync_t *q, bool enabled) { q->detect_cp = enabled; } @@ -188,129 +145,134 @@ lte_cp_t sync_get_cp(sync_t *q) { return q->cp; } -int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos, bool en_cp) { - 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; - uint32_t slot_id_e, N_id_1_e, slot_id_n, N_id_1_n; - - sss_synch_set_N_id_2(&q->sss, q->N_id_2); - - /* Make sure we have enough room to find SSS sequence */ - sss_idx_n = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, CPNORM_LEN)); - sss_idx_e = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, CPEXT_LEN)); - - if (en_cp) { - 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); - return LIBLTE_SUCCESS; - } +/* CP detection algorithm taken from: + * "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" + * by Jung-In Kim et al. + */ +static lte_cp_t detect_cp(sync_t *q, cf_t *input, uint32_t peak_pos) +{ + float R_norm, R_ext, C_norm, C_ext; + float M_norm, M_ext; + + R_norm = crealf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + &input[peak_pos-CP_NORM(7, q->fft_size)], + CP_NORM(7, q->fft_size))); + C_norm = cabsf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + &input[peak_pos-q->fft_size-CP_NORM(7, q->fft_size)], + CP_NORM(7, q->fft_size))); + R_ext = crealf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + &input[peak_pos-CP_EXT(q->fft_size)], + CP_EXT(q->fft_size))); + C_ext = cabsf(vec_dot_prod_conj_ccc(&input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + &input[peak_pos-q->fft_size-CP_EXT(q->fft_size)], + CP_EXT(q->fft_size))); + M_norm = R_norm/C_norm; + M_ext = R_ext/C_ext; + + if (M_norm > M_ext) { + return CPNORM; + } else if (M_norm < M_ext) { + return CPEXT; } else { - if (CP_ISNORM(q->cp)) { - if (sss_idx_n < 0) { - INFO("Not enough room to decode normal CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx_n, peak_pos); - return LIBLTE_SUCCESS; - } + if (R_norm > R_ext) { + return CPNORM; } else { - if (sss_idx_e < 0) { - INFO("Not enough room to decode extended CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx_e, peak_pos); - return LIBLTE_SUCCESS; - } + return CPEXT; } } - - slot_id_n = 0; - slot_id_e = 0; - N_id_1_n = 0; - N_id_1_e = 0; - - /* try Normal CP length */ - if (en_cp || CP_ISNORM(q->cp)) { - sss_synch_m0m1(&q->sss, &input[sss_idx_n], &m0, &m0_value_n, &m1, &m1_value_n); +} - slot_id_n = 2 * sss_synch_subframe(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; - } - } +int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos) { + uint32_t m0, m1; + int sss_idx, ret; + float m0_value, m1_value; - if (en_cp || CP_ISEXT(q->cp)) { - /* Now try Extended CP length */ - sss_synch_m0m1(&q->sss, &input[sss_idx_e], &m0, &m0_value_e, &m1, &m1_value_e); + sss_synch_set_N_id_2(&q->sss, q->N_id_2); - slot_id_e = 2 * sss_synch_subframe(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; - } + if (q->detect_cp) { + q->cp = detect_cp(q, input, peak_pos); } - /* Correlation with extended CP hypoteshis is greater than with normal? */ - if ((en_cp && m0_value_e * m1_value_e > m0_value_n * m1_value_n) - || CP_ISEXT(q->cp)) { - q->cp = CPEXT; - q->slot_id = slot_id_e; - q->N_id_1 = N_id_1_e; - /* otherwise is normal CP */ + /* Make sure we have enough room to find SSS sequence */ + sss_idx = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, q->cp)); + + if (sss_idx < 0) { + INFO("Not enough room to decode CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx, peak_pos); + return LIBLTE_SUCCESS; + } + + /* try Normal CP length */ + sss_synch_m0m1(&q->sss, &input[sss_idx], &m0, &m0_value, &m1, &m1_value); + + q->sf_idx = sss_synch_subframe(m0, m1); + ret = sss_synch_N_id_1(&q->sss, m0, m1); + if (ret >= 0) { + q->N_id_1 = (uint32_t) ret; } else { - q->cp = CPNORM; - q->slot_id = slot_id_n; - q->N_id_1 = N_id_1_n; + q->N_id_1 = 1000; } - - DEBUG("SSS detected N_id_1=%d, slot_idx=%d, position=%d/%d %s CP\n", - q->N_id_1, q->slot_id, sss_idx_n, sss_idx_e, CP_ISNORM(q->cp)?"Normal":"Extended"); + + DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", + q->N_id_1, q->sf_idx, CP_ISNORM(q->cp)?"Normal":"Extended"); return 1; } -int sync_track(sync_t *q, cf_t *input, uint32_t offset, uint32_t *peak_position) { +int sync_find(sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position) +{ int ret = LIBLTE_ERROR_INVALID_INPUTS; - if (q != NULL && - input != NULL && + float peak_unnormalized, energy; + + if (q != NULL && + input != NULL && + lte_N_id_2_isvalid(q->N_id_2) && fft_size_isvalid(q->fft_size)) { - float peak_value, mean_value, *mean_ptr; uint32_t peak_pos; - - pss_synch_set_N_id_2(&q->pss_track, q->N_id_2); - if (q->pss_mode == ABSOLUTE) { - mean_ptr = NULL; - } else { - mean_ptr = &mean_value; + if (peak_position) { + *peak_position = 0; } - - peak_pos = pss_synch_find_pss(&q->pss_track, &input[offset], &peak_value, mean_ptr); - if (q->pss_mode == ABSOLUTE) { - q->peak_value = peak_value; - } else { - q->peak_value = peak_value / mean_value; - } + pss_synch_set_N_id_2(&q->pss, q->N_id_2); + + peak_pos = pss_synch_find_pss(&q->pss, &input[find_offset], &peak_unnormalized); - DEBUG("PSS possible tracking peak pos=%d peak=%.2f threshold=%.2f\n", - peak_pos, peak_value, q->track_threshold); + if (q->normalize_en && + peak_pos + find_offset >= q->fft_size) + { + /* Compute the energy of the received PSS sequence to normalize */ + cf_t *pss_ptr = &input[find_offset+peak_pos-q->fft_size]; + energy = sqrtf(crealf(vec_dot_prod_conj_ccc(pss_ptr, pss_ptr, q->fft_size)) / (q->fft_size)); + q->mean_energy = EXPAVERAGE(energy, q->mean_energy, q->frame_cnt); + } else { + if (q->mean_energy == 0.0) { + q->mean_energy = 1.0; + } + energy = q->mean_energy; + } + + /* Normalize and compute mean peak value */ + q->peak_value = peak_unnormalized/energy; + q->mean_peak_value = EXPAVERAGE(q->peak_value, q->mean_peak_value, q->frame_cnt); + q->frame_cnt++; - if (q->peak_value > q->track_threshold) { - if (offset + peak_pos > q->fft_size) { - q->cfo = pss_synch_cfo_compute(&q->pss_track, &input[offset+peak_pos-q->fft_size]); + /* If peak is over threshold, compute CFO and SSS */ + if (q->peak_value >= q->threshold) { + if (find_offset + peak_pos >= q->fft_size) { + q->cfo = pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]); if (q->sss_en) { - if (sync_sss(q, input, offset + peak_pos, false) < 0) { + if (sync_sss(q, input, find_offset + peak_pos) < 0) { fprintf(stderr, "Error synchronizing with SSS\n"); return LIBLTE_ERROR; } } } else { - printf("Warning: no space for CFO computation\n"); + INFO("Warning: no space for CFO computation\n",0); } + if (peak_position) { *peak_position = peak_pos; } @@ -318,81 +280,17 @@ int sync_track(sync_t *q, cf_t *input, uint32_t offset, uint32_t *peak_position) } else { ret = LIBLTE_SUCCESS; } + + INFO("SYNC ret=%d N_id_2=%d pos=%d peak=%.2f energy=%.3f threshold=%.2f sf_idx=%d\n", + ret, q->N_id_2, peak_pos, q->peak_value, energy, q->threshold, q->sf_idx); + + } else if (lte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Must call sync_set_N_id_2() first!\n"); } return ret; } -int sync_find(sync_t *q, cf_t *input, uint32_t *peak_position) { - uint32_t N_id_2, peak_pos[3]; - float peak_value[3]; - float mean_value[3]; - float max=-999; - uint32_t i; - int ret; - float *mean_ptr; - - for (N_id_2=0;N_id_2<3;N_id_2++) { - if (q->pss_mode == ABSOLUTE) { - mean_ptr = NULL; - } else { - mean_ptr = &mean_value[N_id_2]; - } - pss_synch_set_N_id_2(&q->pss_find, N_id_2); - ret = pss_synch_find_pss(&q->pss_find, input, &peak_value[N_id_2], mean_ptr); - 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++) { - if (peak_value[i] > max) { - max = peak_value[i]; - N_id_2 = i; - } - } - - if (q->pss_mode == ABSOLUTE) { - q->peak_value = peak_value[N_id_2]; - } else { - q->peak_value = peak_value[N_id_2] / mean_value[N_id_2]; - } - - if (peak_position) { - *peak_position = 0; - } - - DEBUG("PSS possible peak N_id_2=%d, pos=%d peak=%.2f threshold=%.2f\n", - N_id_2, peak_pos[N_id_2], peak_value[N_id_2], q->find_threshold); - - /* If peak detected */ - if (q->peak_value > q->find_threshold) { - if (peak_pos[N_id_2] > q->fft_size && - peak_pos[N_id_2] + q->fft_size < q->find_frame_size) - { - q->N_id_2 = N_id_2; - pss_synch_set_N_id_2(&q->pss_find, q->N_id_2); - q->cfo = pss_synch_cfo_compute(&q->pss_find, &input[peak_pos[N_id_2]-q->fft_size]); - - 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_value, q->find_threshold, q->cfo); - - if (q->sss_en) { - if (sync_sss(q, input, peak_pos[q->N_id_2], q->detect_cp) < 0) { - fprintf(stderr, "Error synchronizing with SSS\n"); - return LIBLTE_ERROR; - } - } - } - - if (peak_position) { - *peak_position = peak_pos[N_id_2]; - } - - return 1; - } else { - return LIBLTE_SUCCESS; - } +void sync_reset(sync_t *q) { + q->frame_cnt = 0; } diff --git a/lte/phy/lib/sync/test/CMakeLists.txt b/lte/phy/lib/sync/test/CMakeLists.txt index 72b2ccb6a..d9f8c49b3 100644 --- a/lte/phy/lib/sync/test/CMakeLists.txt +++ b/lte/phy/lib/sync/test/CMakeLists.txt @@ -19,6 +19,15 @@ # and at http://www.gnu.org/licenses/. # +######################################################################## +# PROGRAM TO DEBUG PSS FROM USRP +######################################################################## + +LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND) +IF(${CUHD_FIND} GREATER -1) + ADD_EXECUTABLE(pss_usrp pss_usrp.c) + TARGET_LINK_LIBRARIES(pss_usrp lte_phy cuhd) +ENDIF(${CUHD_FIND} GREATER -1) ######################################################################## # SYNC TEST @@ -27,11 +36,16 @@ ADD_EXECUTABLE(sync_test sync_test.c) TARGET_LINK_LIBRARIES(sync_test lte_phy) -ADD_TEST(sync_test_100 sync_test -o 100) -ADD_TEST(sync_test_400 sync_test -o 400) -ADD_TEST(sync_test_100_e sync_test -o 100 -e) -ADD_TEST(sync_test_400_e sync_test -o 400 -e) - +ADD_TEST(sync_test_100 sync_test -o 100 -c 501) +ADD_TEST(sync_test_400 sync_test -o 400 -c 2) +ADD_TEST(sync_test_100_e sync_test -o 100 -e -c 150) +ADD_TEST(sync_test_400_e sync_test -o 400 -e -c 151) + +ADD_TEST(sync_test_100 sync_test -o 100 -p 50 -c 501) +ADD_TEST(sync_test_400 sync_test -o 400 -p 50 -c 500) +ADD_TEST(sync_test_100_e sync_test -o 100 -e -p 50 -c 133) +ADD_TEST(sync_test_400_e sync_test -o 400 -e -p 50 -c 123) + ######################################################################## # CFO TEST ######################################################################## diff --git a/lte/phy/lib/sync/test/pss_usrp.c b/lte/phy/lib/sync/test/pss_usrp.c new file mode 100644 index 000000000..176780edb --- /dev/null +++ b/lte/phy/lib/sync/test/pss_usrp.c @@ -0,0 +1,208 @@ +/** + * + * \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 +#include +#include +#include +#include +#include +#include + +#include + +#include "liblte/phy/phy.h" +#include "liblte/cuhd/cuhd.h" + +uint32_t N_id_2 = 100; +char *uhd_args=""; +float uhd_gain=40.0, uhd_freq=-1.0; +int nof_frames = -1; +uint32_t fft_size=64; +float threshold = 0.4; + +void usage(char *prog) { + printf("Usage: %s [agtvnp] -f rx_frequency_hz -i N_id_2\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD Gain [Default %.2f dB]\n", uhd_gain); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); + printf("\t-v verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agtvsfi")) != -1) { + switch (opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'i': + N_id_2 = atoi(argv[optind]); + break; + case 's': + fft_size = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (N_id_2 > 2 || uhd_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + cf_t *buffer; + int frame_cnt, n; + void *uhd; + pss_synch_t pss; + int32_t flen; + int peak_idx, last_peak; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + + parse_args(argc, argv); + + flen = 4800*(fft_size/64); + + buffer = malloc(sizeof(cf_t) * flen); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (pss_synch_init_fft(&pss, flen, fft_size)) { + fprintf(stderr, "Error initiating PSS\n"); + exit(-1); + } + if (pss_synch_set_N_id_2(&pss, N_id_2)) { + fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2); + exit(-1); + } + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + printf("Set RX rate: %.2f MHz\n", cuhd_set_rx_srate(uhd, flen*2*100) / 1000000); + printf("Set RX gain: %.1f dB\n", cuhd_set_rx_gain(uhd, uhd_gain)); + printf("Set RX freq: %.2f MHz\n", cuhd_set_rx_freq(uhd, uhd_freq) / 1000000); + cuhd_rx_wait_lo_locked(uhd); + cuhd_start_rx_stream(uhd); + + printf("Frame length %d samples\n", flen); + printf("PSS detection threshold: %.2f\n", threshold); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + last_peak = 0; + mean_peak = 0; + while(frame_cnt < nof_frames || nof_frames == -1) { + n = cuhd_recv(uhd, buffer, flen, 1); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + peak_idx = pss_synch_find_pss(&pss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding PSS peak\n"); + exit(-1); + } + + float y = sqrtf(crealf(vec_dot_prod_conj_ccc(&buffer[peak_idx-fft_size], + &buffer[peak_idx-fft_size], + fft_size)) / + fft_size); + float x = peak_value/y; + + mean_peak = EXPAVERAGE(x, mean_peak, frame_cnt); + + if (x >= threshold) { + nof_det++; + } else { + nof_nodet++; + } + + if (frame_cnt > 100) { + if (abs(last_peak-peak_idx) > 10) { + if (x >= threshold) { + nof_nopeakdet++; + } else { + if (nof_nodet > 0) { + nof_nodet--; + } + } + nof_nopeak++; + } + } + + frame_cnt++; + + printf("[%5d]: Pos: %5d, En: %.4f Val: %.3f MeanVal: %.3f, Det: %.3f, No-Det: %.3f, NoPeak: %.3f, NoPeakDet: %.3f\r", + frame_cnt, + peak_idx, y, x, mean_peak, + (float) nof_det/frame_cnt, (float) nof_nodet/frame_cnt, + (float) nof_nopeak/frame_cnt, (float) nof_nopeakdet/nof_nopeak); + + if (VERBOSE_ISINFO()) { + printf("\n"); + } + + last_peak = peak_idx; + + } + + pss_synch_free(&pss); + free(buffer); + cuhd_close(uhd); + + printf("Ok\n"); + exit(0); +} + diff --git a/lte/phy/lib/sync/test/sync_test.c b/lte/phy/lib/sync/test/sync_test.c index b9414a304..b3f3133ae 100644 --- a/lte/phy/lib/sync/test/sync_test.c +++ b/lte/phy/lib/sync/test/sync_test.c @@ -32,18 +32,21 @@ #include #include #include + #include #include "liblte/phy/phy.h" int cell_id = -1, offset = 0; lte_cp_t cp = CPNORM; +uint32_t nof_prb=6; -#define FLEN 9600 +#define FLEN SF_LEN(fft_size) void usage(char *prog) { - printf("Usage: %s [coev]\n", prog); + printf("Usage: %s [cpoev]\n", prog); printf("\t-c cell_id [Default check for all]\n"); + printf("\t-p nof_prb [Default %d]\n", nof_prb); printf("\t-o offset [Default %d]\n", offset); printf("\t-e extended CP [Default normal]\n"); printf("\t-v verbose\n"); @@ -51,11 +54,14 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "coev")) != -1) { + while ((opt = getopt(argc, argv, "cpoev")) != -1) { switch (opt) { case 'c': cell_id = atoi(argv[optind]); break; + case 'p': + nof_prb = atoi(argv[optind]); + break; case 'o': offset = atoi(argv[optind]); break; @@ -82,32 +88,40 @@ int main(int argc, char **argv) { uint32_t find_idx; sync_t sync; lte_fft_t ifft; - + int fft_size; + parse_args(argc, argv); + fft_size = lte_symbol_sz(nof_prb); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", nof_prb); + exit(-1); + } + buffer = malloc(sizeof(cf_t) * FLEN); if (!buffer) { perror("malloc"); exit(-1); } - fft_buffer = malloc(sizeof(cf_t) * 2 * FLEN); + fft_buffer = malloc(sizeof(cf_t) * FLEN); if (!fft_buffer) { perror("malloc"); exit(-1); } - - if (lte_ifft_init(&ifft, cp, 6)) { + + if (lte_ifft_init(&ifft, cp, nof_prb)) { fprintf(stderr, "Error creating iFFT object\n"); exit(-1); } - if (sync_init(&sync, FLEN, 128, 128)) { + if (sync_init(&sync, FLEN, fft_size)) { fprintf(stderr, "Error initiating PSS/SSS\n"); return -1; } - sync_set_threshold(&sync, 1, 1); + /* Set a very high threshold to make sure the correlation is ok */ + sync_set_threshold(&sync, 1.4); if (cell_id == -1) { cid = 0; @@ -123,21 +137,28 @@ int main(int argc, char **argv) { pss_generate(pss_signal, N_id_2); sss_generate(sss_signal0, sss_signal5, cid); + sync_set_N_id_2(&sync, N_id_2); + for (ns=0;ns<2;ns++) { memset(buffer, 0, sizeof(cf_t) * FLEN); - pss_put_slot(pss_signal, buffer, 6, cp); - sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, cp); + pss_put_slot(pss_signal, buffer, nof_prb, cp); + sss_put_slot(ns?sss_signal5:sss_signal0, buffer, nof_prb, cp); /* Transform to OFDM symbols */ - memset(fft_buffer, 0, sizeof(cf_t) * 2 * FLEN); + memset(fft_buffer, 0, sizeof(cf_t) * FLEN); lte_ifft_run_slot(&ifft, buffer, &fft_buffer[offset]); + + vec_save_file("input", fft_buffer, sizeof(cf_t) * FLEN); - sync_find(&sync, fft_buffer, &find_idx); - find_ns = sync_get_slot_id(&sync); + if (sync_find(&sync, fft_buffer, 0, &find_idx) < 0) { + fprintf(stderr, "Error running sync_find\n"); + exit(-1); + } + find_ns = 2*sync_get_sf_idx(&sync); printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset, ns, find_ns); - if (find_idx != offset + 960) { - printf("offset != find_offset: %d != %d\n", find_idx, offset + 960); + if (find_idx != offset + FLEN/2) { + printf("offset != find_offset: %d != %d\n", find_idx, offset + FLEN/2); exit(-1); } if (ns*10 != find_ns) { diff --git a/lte/phy/lib/ue/src/ue_celldetect.c b/lte/phy/lib/ue/src/ue_celldetect.c new file mode 100644 index 000000000..27cd1b5f8 --- /dev/null +++ b/lte/phy/lib/ue/src/ue_celldetect.c @@ -0,0 +1,268 @@ +/** + * + * \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 +#include +#include +#include +#include + +#include "liblte/phy/ue/ue_celldetect.h" + +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#define FIND_FFTSIZE 64 +#define FIND_SFLEN 5*SF_LEN(FIND_FFTSIZE) + +int ue_celldetect_init(ue_celldetect_t * q) { + return ue_celldetect_init_max(q, CS_DEFAULT_MAXFRAMES_TOTAL, CS_DEFAULT_MAXFRAMES_DETECTED); +} + +int ue_celldetect_init_max(ue_celldetect_t * q, uint32_t max_frames_total, uint32_t max_frames_detected) { + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = LIBLTE_ERROR; + + bzero(q, sizeof(ue_celldetect_t)); + + q->candidates = malloc(sizeof(ue_celldetect_result_t) * max_frames_detected); + if (!q->candidates) { + perror("malloc"); + goto clean_exit; + } + if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) { + goto clean_exit; + } + q->mode_ntimes = malloc(sizeof(uint32_t) * max_frames_detected); + if (!q->mode_ntimes) { + perror("malloc"); + goto clean_exit; + } + q->mode_counted = malloc(sizeof(char) * max_frames_detected); + if (!q->mode_counted) { + perror("malloc"); + goto clean_exit; + } + + sync_set_threshold(&q->sfind, CS_FIND_THRESHOLD); + sync_sss_en(&q->sfind, true); + + q->max_frames_total = max_frames_total; + q->max_frames_detected = max_frames_detected; + q->nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL; + q->nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED; + + ue_celldetect_reset(q); + + ret = LIBLTE_SUCCESS; + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_celldetect_free(q); + } + return ret; +} + +void ue_celldetect_free(ue_celldetect_t * q) +{ + if (q->candidates) { + free(q->candidates); + } + if (q->mode_counted) { + free(q->mode_counted); + } + if (q->mode_ntimes) { + free(q->mode_ntimes); + } + sync_free(&q->sfind); +} + + +void ue_celldetect_reset(ue_celldetect_t * q) +{ + q->current_nof_detected = 0; + q->current_nof_total = 0; + q->current_N_id_2 = 0; +} + +void ue_celldetect_set_threshold(ue_celldetect_t * q, float threshold) +{ + sync_set_threshold(&q->sfind, threshold); +} + +int ue_celldetect_set_nof_frames_total(ue_celldetect_t * q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames_total) { + q->nof_frames_total = nof_frames; + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR; + } +} + +int ue_celldetect_set_nof_frames_detected(ue_celldetect_t * q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames_detected) { + q->nof_frames_detected = nof_frames; + return LIBLTE_SUCCESS; + } else { + return LIBLTE_ERROR; + } +} + +/* Decide the most likely cell based on the mode */ +void decide_cell(ue_celldetect_t * q, ue_celldetect_result_t *found_cell) +{ + uint32_t i, j; + + bzero(q->mode_counted, q->nof_frames_detected); + bzero(q->mode_ntimes, sizeof(uint32_t) * q->nof_frames_detected); + + /* First find mode of CELL IDs */ + for (i = 0; i < q->nof_frames_detected; i++) { + uint32_t cnt = 1; + for (j=i+1;jnof_frames_detected;j++) { + if (q->candidates[j].cell_id == q->candidates[i].cell_id && !q->mode_counted[j]) { + q->mode_counted[j]=1; + cnt++; + } + } + q->mode_ntimes[i] = cnt; + } + uint32_t max_times=0, mode_pos=0; + for (i=0;inof_frames_detected;i++) { + if (q->mode_ntimes[i] > 0) { + DEBUG("ntimes[%d]=%d (CID: %d)\n",i,q->mode_ntimes[i],q->candidates[i].cell_id); + } + if (q->mode_ntimes[i] > max_times) { + max_times = q->mode_ntimes[i]; + mode_pos = i; + } + } + found_cell->cell_id = q->candidates[mode_pos].cell_id; + /* Now in all these cell IDs, find most frequent CP */ + uint32_t nof_normal = 0; + found_cell->peak = 0; + for (i=0;inof_frames_detected;i++) { + if (q->candidates[i].cell_id == found_cell->cell_id) { + if (CP_ISNORM(q->candidates[i].cp)) { + nof_normal++; + } + found_cell->peak += q->candidates[i].peak/q->mode_ntimes[mode_pos]; + } + } + if (nof_normal > q->mode_ntimes[mode_pos]/2) { + found_cell->cp = CPNORM; + } else { + found_cell->cp = CPEXT; + } + found_cell->mode = q->mode_ntimes[mode_pos]; +} + +int ue_celldetect_scan(ue_celldetect_t * q, + cf_t *signal, + uint32_t nsamples, + ue_celldetect_result_t *found_cell) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t peak_idx; + uint32_t nof_input_frames; + + + if (q != NULL && + signal != NULL && + nsamples >= 4800) + { + ret = LIBLTE_SUCCESS; + + if (nsamples % 4800) { + printf("Warning: nsamples must be a multiple of 4800. Some samples will be ignored\n"); + nsamples = (nsamples/4800) * 4800; + } + nof_input_frames = nsamples/4800; + + for (uint32_t nf=0;nfsfind, q->current_N_id_2); + + DEBUG("[%3d/%3d]: Searching cells with N_id_2=%d. %d frames\n", + q->current_nof_detected, q->current_nof_total, q->current_N_id_2, nof_input_frames); + + /* Find peak and cell id */ + ret = sync_find(&q->sfind, &signal[nf*4800], 0, &peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + /* If peak position does not allow to read SSS, return error -3 */ + if (ret == LIBLTE_SUCCESS && peak_idx != 0) { + return CS_FRAME_UNALIGNED; + } + + /* Process the peak result */ + if (ret == 1) { + if (sync_sss_detected(&q->sfind)) { + ret = sync_get_cell_id(&q->sfind); + if (ret >= 0) { + /* Save cell id, cp and peak */ + q->candidates[q->current_nof_detected].cell_id = (uint32_t) ret; + q->candidates[q->current_nof_detected].cp = sync_get_cp(&q->sfind); + q->candidates[q->current_nof_detected].peak = sync_get_last_peak_value(&q->sfind); + } + INFO + ("[%3d/%3d]: Found peak at %4d, value %.3f, Cell_id: %d CP: %s\n", + q->current_nof_detected, q->current_nof_total, peak_idx, + q->candidates[q->current_nof_detected].peak, q->candidates[q->current_nof_detected].cell_id, + lte_cp_string(q->candidates[q->current_nof_detected].cp)); + q->current_nof_detected++; + } + } + q->current_nof_total++; + + /* Decide cell ID and CP if we detected up to nof_frames_detected */ + if (q->current_nof_detected == q->nof_frames_detected) { + decide_cell(q, found_cell); + q->current_N_id_2++; + q->current_nof_detected = q->current_nof_total = 0; + ret = CS_CELL_DETECTED; + /* Or go to the next N_id_2 if we didn't detect the cell */ + } else if (q->current_nof_total == q->nof_frames_total) { + q->current_N_id_2++; + q->current_nof_detected = q->current_nof_total = 0; + ret = CS_CELL_NOT_DETECTED; + } + if (q->current_N_id_2 == 3) { + q->current_N_id_2 = 0; + } + } + } + + return ret; +} diff --git a/lte/phy/lib/phch/src/ue_dl.c b/lte/phy/lib/ue/src/ue_dl.c similarity index 81% rename from lte/phy/lib/phch/src/ue_dl.c rename to lte/phy/lib/ue/src/ue_dl.c index e9aad4520..203011776 100644 --- a/lte/phy/lib/phch/src/ue_dl.c +++ b/lte/phy/lib/ue/src/ue_dl.c @@ -25,8 +25,10 @@ * */ -#include "liblte/phy/phch/ue_dl.h" +#include "liblte/phy/ue/ue_dl.h" +#include +#include #define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1)) @@ -54,6 +56,8 @@ int ue_dl_init(ue_dl_t *q, q->pkt_errors = 0; q->pkts_total = 0; q->nof_trials = 0; + q->sfn = 0; + q->pbch_decoded = false; if (lte_fft_init(&q->fft, q->cell.cp, q->cell.nof_prb)) { fprintf(stderr, "Error initiating FFT\n"); @@ -67,7 +71,10 @@ int ue_dl_init(ue_dl_t *q, fprintf(stderr, "Error initiating REGs\n"); goto clean_exit; } - + if (pbch_init(&q->pbch, q->cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + goto clean_exit; + } if (pcfich_init(&q->pcfich, &q->regs, q->cell)) { fprintf(stderr, "Error creating PCFICH object\n"); goto clean_exit; @@ -119,6 +126,7 @@ void ue_dl_free(ue_dl_t *q) { lte_fft_free(&q->fft); chest_free(&q->chest); regs_free(&q->regs); + pbch_free(&q->pbch); pcfich_free(&q->pcfich); pdcch_free(&q->pdcch); pdsch_free(&q->pdsch); @@ -136,7 +144,10 @@ void ue_dl_free(ue_dl_t *q) { } } -int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t sfn, uint16_t rnti) +LIBLTE_API float mean_exec_time=0; +int frame_cnt=0; + +int ue_dl_decode(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint16_t rnti) { uint32_t cfi, cfi_distance, i; ra_pdsch_t ra_dl; @@ -145,19 +156,49 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t uint32_t nof_locations; uint16_t crc_rem; dci_format_t format; + pbch_mib_t mib; int ret = LIBLTE_ERROR; + cf_t *ce_slot1[MAX_PORTS]; + struct timeval t[3]; + + /* Run FFT for all subframe data */ + lte_fft_run_sf(&q->fft, input, q->sf_symbols); + + gettimeofday(&t[1], NULL); + + /* Get channel estimates for each port */ + chest_ce_sf(&q->chest, q->sf_symbols, q->ce, sf_idx); + + gettimeofday(&t[2], NULL); + get_time_interval(t); + mean_exec_time = (float) EXPAVERAGE((float) t[0].tv_usec, mean_exec_time, frame_cnt); + frame_cnt++; + for (int i=0;ice[i][SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)]; + } + + /* Decode PBCH if not yet decoded to obtain the System Frame Number (SFN) */ + if (sf_idx == 0) { + // FIXME: There is no need to do this every frame! + pbch_decode_reset(&q->pbch); + if (pbch_decode(&q->pbch, &q->sf_symbols[SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)], ce_slot1, &mib) == 1) { + q->sfn = mib.sfn; + q->pbch_decoded = true; + INFO("Decoded SFN: %d\n", q->sfn); + } else { + INFO("Not decoded MIB (SFN: %d)\n", q->sfn); + q->sfn++; + if (q->sfn == 1024) { + q->sfn = 0; + } + } + } /* If we are looking for SI Blocks, search only in appropiate places */ - if ((rnti == SIRNTI && (sfn % 2) == 0 && sf_idx == 5) || - rnti != SIRNTI) + if (((rnti == SIRNTI && (q->sfn % 2) == 0 && sf_idx == 5) || + rnti != SIRNTI)) { - /* Run FFT for all subframe data */ - lte_fft_run_sf(&q->fft, input, q->sf_symbols); - - /* Get channel estimates for each port */ - chest_ce_sf(&q->chest, q->sf_symbols, q->ce, sf_idx); - /* First decode PCFICH and obtain CFI */ if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce, sf_idx, &cfi, &cfi_distance)<0) { fprintf(stderr, "Error decoding PCFICH\n"); @@ -180,6 +221,7 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t format = Format1; } + crc_rem = 0; for (i=0;ipdcch, q->sf_symbols, q->ce, locations[i], sf_idx, cfi)) { @@ -192,8 +234,7 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t } INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem); } - - + if (crc_rem == rnti) { if (dci_msg_to_ra_dl(&dci_msg, rnti, q->user_rnti, q->cell, cfi, &ra_dl)) { fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n"); @@ -202,7 +243,7 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t uint32_t rvidx; if (rnti == SIRNTI) { - switch((sfn%8)/2) { + switch((q->sfn%8)/2) { case 0: rvidx = 0; break; @@ -251,11 +292,11 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t } } } - if (rnti == SIRNTI && (sfn%8) == 0) { + if (rnti == SIRNTI && (q->sfn%8) == 0) { q->nof_trials++; } } - + if (crc_rem == rnti && ret == LIBLTE_SUCCESS) { return ra_dl.mcs.tbs; } else { diff --git a/lte/phy/lib/ue/src/ue_mib.c b/lte/phy/lib/ue/src/ue_mib.c new file mode 100644 index 000000000..9652e75c8 --- /dev/null +++ b/lte/phy/lib/ue/src/ue_mib.c @@ -0,0 +1,225 @@ +/** + * + * \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 +#include +#include +#include +#include + +#include "liblte/phy/ue/ue_mib.h" + +#include "liblte/phy/utils/debug.h" +#include "liblte/phy/utils/vector.h" + +#define FIND_FFTSIZE 128 +#define FIND_SFLEN 10*SF_LEN(FIND_FFTSIZE) + +int ue_mib_init(ue_mib_t * q, + uint32_t cell_id, + lte_cp_t cp) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + + ret = LIBLTE_ERROR; + + lte_cell_t cell; + cell.nof_ports = MIB_NOF_PORTS; + cell.nof_prb = 6; + cell.id = cell_id; + cell.cp = cp; + + q->cell_id = cell_id; + + bzero(q, sizeof(ue_mib_t)); + + q->slot1_symbols = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t)); + if (!q->slot1_symbols) { + perror("malloc"); + goto clean_exit; + } + + for (int i=0;ice[i] = malloc(SLOT_LEN_RE(6, cp) * sizeof(cf_t)); + if (!q->ce[i]) { + perror("malloc"); + goto clean_exit; + } + } + + if (sync_init(&q->sfind, FIND_SFLEN, FIND_FFTSIZE)) { + goto clean_exit; + } + + sync_set_threshold(&q->sfind, MIB_FIND_THRESHOLD); + sync_sss_en(&q->sfind, true); + sync_set_N_id_2(&q->sfind, cell_id % 3); + + if (lte_fft_init(&q->fft, cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + goto clean_exit; + } + if (chest_init_LTEDL(&q->chest, cell)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto clean_exit; + } + if (pbch_init(&q->pbch, cell)) { + fprintf(stderr, "Error initiating PBCH\n"); + goto clean_exit; + } + ue_mib_reset(q); + + ret = LIBLTE_SUCCESS; + } + +clean_exit: + if (ret == LIBLTE_ERROR) { + ue_mib_free(q); + } + return ret; +} + +void ue_mib_free(ue_mib_t * q) +{ + if (q->slot1_symbols) { + free(q->slot1_symbols); + } + for (int i=0;ice[i]) { + free(q->ce[i]); + } + } + sync_free(&q->sfind); + chest_free(&q->chest); + pbch_free(&q->pbch); + lte_fft_free(&q->fft); +} + + +void ue_mib_reset(ue_mib_t * q) +{ + q->frame_cnt = 0; + q->last_frame_trial = 0; + + pbch_decode_reset(&q->pbch); +} + +void ue_mib_set_threshold(ue_mib_t * q, float threshold) +{ + sync_set_threshold(&q->sfind, threshold); +} + +static int mib_decoder_run(ue_mib_t * q, cf_t *input, pbch_mib_t *mib) +{ + int ret; + + /* Run FFT for the slot symbols */ + lte_fft_run_slot(&q->fft, input, q->slot1_symbols); + + /* Get channel estimates of slot #1 for each port */ + ret = chest_ce_slot(&q->chest, q->slot1_symbols, q->ce, 1); + if (ret == LIBLTE_SUCCESS) { + + /* Reset decoder if we missed a frame */ + if ((q->last_frame_trial && (q->frame_cnt - q->last_frame_trial > 2)) || + q->frame_cnt > 10) + { + ue_mib_reset(q); + INFO("Resetting PBCH decoder: last trial %u, now is %u\n", + q->last_frame_trial, q->frame_cnt); + } + + /* Decode PBCH */ + ret = pbch_decode(&q->pbch, q->slot1_symbols, q->ce, mib); + if (ret < 0) { + fprintf(stderr, "Error decoding PBCH\n"); + } else if (ret == 1) { + INFO("MIB decoded: %u\n", q->frame_cnt/2); + ue_mib_reset(q); + ret = 1; + } else { + INFO("MIB not decoded: %u\n", q->frame_cnt / 2); + q->last_frame_trial = q->frame_cnt; + } + } + return ret; +} + +int ue_mib_decode(ue_mib_t * q, + cf_t *signal, + uint32_t nsamples, + pbch_mib_t *mib) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + uint32_t peak_idx; + uint32_t nof_input_frames; + + + if (q != NULL && + signal != NULL) + { + ret = LIBLTE_SUCCESS; + + if (nsamples % MIB_FRAME_SIZE) { + printf("Warning: nsamples must be a multiple of %d. Some samples will be ignored\n", MIB_FRAME_SIZE); + nsamples = (nsamples/MIB_FRAME_SIZE) * MIB_FRAME_SIZE; + } + nof_input_frames = nsamples/MIB_FRAME_SIZE; + + for (uint32_t nf=0;nfsfind, signal, nf*MIB_FRAME_SIZE, &peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + /* If peak position does not allow to read SSS, return error -3 */ + if (ret == 1 && + nf*MIB_FRAME_SIZE + peak_idx + 960 <= nsamples && + sync_sss_detected(&q->sfind) && + sync_get_sf_idx(&q->sfind) == 0) + { + + ret = mib_decoder_run(q, &signal[nf*MIB_FRAME_SIZE+peak_idx], mib); + + } else if ((ret == LIBLTE_SUCCESS && peak_idx != 0) || + (ret == 1 && nf*MIB_FRAME_SIZE + peak_idx + 960 > nsamples)) + { + ret = MIB_FRAME_UNALIGNED; + } else { + ret = 0; + } + + q->frame_cnt++; + } + } + return ret; +} diff --git a/lte/phy/lib/ue/src/ue_sync.c b/lte/phy/lib/ue/src/ue_sync.c new file mode 100644 index 000000000..714a6212f --- /dev/null +++ b/lte/phy/lib/ue/src/ue_sync.c @@ -0,0 +1,353 @@ +/** + * + * \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 +#include +#include +#include +#include + + +#include "liblte/phy/ue/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 CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE) + +#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 FIND_THRESHOLD 1.0 +#define TRACK_THRESHOLD 0.2 + + +int ue_sync_init(ue_sync_t *q, + lte_cell_t cell, + int (recv_callback)(void*, void*, uint32_t), + void *stream_handler) +{ + int ret = LIBLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + stream_handler != NULL && + lte_cell_isvalid(&cell) && + recv_callback != NULL) + { + ret = LIBLTE_ERROR; + + bzero(q, sizeof(ue_sync_t)); + + ue_sync_reset(q); + + q->decode_sss_on_track = false; + q->stream = stream_handler; + q->recv_callback = recv_callback; + q->cell = cell; + + if(sync_init(&q->sfind, CURRENT_SFLEN, CURRENT_FFTSIZE)) { + fprintf(stderr, "Error initiating sync find\n"); + goto clean_exit; + } + if(sync_init(&q->strack, CURRENT_FFTSIZE, CURRENT_FFTSIZE)) { + fprintf(stderr, "Error initiating sync track\n"); + goto clean_exit; + } + + sync_set_N_id_2(&q->sfind, cell.id%3); + sync_set_threshold(&q->sfind, FIND_THRESHOLD); + q->sfind.cp = cell.cp; + sync_cp_en(&q->sfind, false); + + sync_set_N_id_2(&q->strack, cell.id%3); + sync_set_threshold(&q->strack, TRACK_THRESHOLD); + q->strack.cp = cell.cp; + sync_cp_en(&q->strack, false); + + if (cfo_init(&q->cfocorr, CURRENT_SFLEN)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + q->input_buffer = vec_malloc(5 * CURRENT_SFLEN * sizeof(cf_t)); + if (!q->input_buffer) { + perror("malloc"); + goto clean_exit; + } + + 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); + } + cfo_free(&q->cfocorr); + sync_free(&q->sfind); + sync_free(&q->strack); +} + +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; +} +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; +} + +void ue_sync_decode_sss_on_track(ue_sync_t *q, bool enabled) { + q->decode_sss_on_track = enabled; +} + + +static int find_peak_ok(ue_sync_t *q) { + + /* Receive the rest of the next subframe */ + if (q->recv_callback(q->stream, q->input_buffer, q->peak_idx+CURRENT_SFLEN/2) < 0) { + return LIBLTE_ERROR; + } + + if (sync_sss_detected(&q->sfind)) { + + /* Get the subframe index (0 or 5) */ + q->sf_idx = sync_get_sf_idx(&q->sfind) + 1; + + /* Reset variables */ + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + + /* Goto Tracking state */ + q->state = SF_TRACK; + + INFO("Found peak at %d, value %.3f, SF_idx: %d, Cell_id: %d CP: %s\n", + q->peak_idx, sync_get_peak_value(&q->sfind), q->sf_idx, q->cell.id, lte_cp_string(q->cell.cp)); + + } 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) { + + /* Make sure subframe idx is what we expect */ + if ((q->sf_idx != sync_get_sf_idx(&q->strack)) && q->decode_sss_on_track) { + INFO("Warning: Expected SF idx %d but got %d!\n", + q->sf_idx, sync_get_sf_idx(&q->strack)); + q->sf_idx = sync_get_sf_idx(&q->strack); + q->state = SF_TRACK; + } 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) { + if (q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset) < 0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + + /* compute cumulative moving average CFO */ + q->cur_cfo = EXPAVERAGE(sync_get_cfo(&q->strack), 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; + } + + return 1; +} + +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. Peak %.3f, %d lost\n", + sync_get_peak_value(&q->strack), (int) q->frame_no_cnt); + } + + return 1; +} + +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) */ + //memcpy(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: + ret = sync_find(&q->sfind, q->input_buffer, 0, &q->peak_idx); + if (ret < 0) { + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return -1; + } + + if (ret == 1) { + ret = find_peak_ok(q); + } else if (q->peak_idx != 0) { + uint32_t rlen; + if (q->peak_idx < CURRENT_SFLEN/2) { + rlen = CURRENT_SFLEN/2-q->peak_idx; + } else { + rlen = q->peak_idx; + } + if (q->recv_callback(q->stream, q->input_buffer, rlen) < 0) { + return LIBLTE_ERROR; + } + } + break; + case SF_TRACK: + ret = 1; + + q->strack.sss_en = q->decode_sss_on_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) { + + #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_find(&q->strack, 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); + } + if (ret == LIBLTE_ERROR) { + fprintf(stderr, "Error processing tracking peak\n"); + q->state = SF_FIND; + return LIBLTE_SUCCESS; + } + + q->frame_total_cnt++; + } + + /* 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; + + break; + } + } + return ret; +} + +void ue_sync_reset(ue_sync_t *q) { + q->state = SF_FIND; + + 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 +} + diff --git a/lte/phy/lib/ue/test/CMakeLists.txt b/lte/phy/lib/ue/test/CMakeLists.txt new file mode 100644 index 000000000..dc376a5de --- /dev/null +++ b/lte/phy/lib/ue/test/CMakeLists.txt @@ -0,0 +1,41 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# 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/. +# + +######################################################################## +# 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) + + ADD_EXECUTABLE(ue_celldetect_mib_test ue_celldetect_mib_test.c) + TARGET_LINK_LIBRARIES(ue_celldetect_mib_test 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) + diff --git a/lte/phy/lib/ue/test/ue_celldetect_mib_test.c b/lte/phy/lib/ue/test/ue_celldetect_mib_test.c new file mode 100644 index 000000000..c3921aba4 --- /dev/null +++ b/lte/phy/lib/ue/test/ue_celldetect_mib_test.c @@ -0,0 +1,265 @@ +/** + * + * \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liblte/phy/phy.h" + +#include "liblte/cuhd/cuhd.h" + +int nof_frames_total = CS_DEFAULT_NOFFRAMES_TOTAL; +int nof_frames_detected = CS_DEFAULT_NOFFRAMES_DETECTED; +float threshold = -1; + +float uhd_freq = 0.0, uhd_gain = 20.0; +char *uhd_args = ""; + +void usage(char *prog) { + printf("Usage: %s [agntdv] -f uhd_freq\n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-d nof_frames_detected [Default 10]\n"); + printf("\t-t threshold [Default %.2f]\n",threshold); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agndtvf")) != -1) { + switch (opt) { + case 'n': + nof_frames_total = atoi(argv[optind]); + break; + case 'd': + nof_frames_detected = atoi(argv[optind]); + break; + case 'a': + uhd_args = argv[optind]; + break; + case 'g': + uhd_gain = atof(argv[optind]); + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (uhd_freq == 0.0) { + usage(argv[0]); + exit(-1); + } +} + +int decode_pbch(void *uhd, cf_t *buffer, ue_celldetect_result_t *found_cell) +{ + ue_mib_t uemib; + pbch_mib_t mib; + int n; + + uint32_t nof_frames = 0; + uint32_t flen = MIB_FRAME_SIZE; + + if (ue_mib_init(&uemib, found_cell->cell_id, found_cell->cp)) { + fprintf(stderr, "Error initiating PBCH decoder\n"); + return LIBLTE_ERROR; + } + + INFO("Setting sampling frequency 1.92 MHz for PBCH decoding\n", 0); + cuhd_set_rx_srate(uhd, 1920000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + do { + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + INFO("Calling ue_mib_decode() %d/%d\n", nof_frames, nof_frames_total); + + n = ue_mib_decode(&uemib, buffer, flen, &mib); + if (n == LIBLTE_ERROR || n == LIBLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling ue_mib_decode()\n"); + return LIBLTE_ERROR; + } + if (n == MIB_FRAME_UNALIGNED) { + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + } + nof_frames++; + } while (n != MIB_FOUND && nof_frames < nof_frames_total); + if (n == MIB_FOUND) { + printf("\n\nMIB decoded in %d ms (%d half frames)\n", nof_frames*5, nof_frames); + pbch_mib_fprint(stdout, &mib, found_cell->cell_id); + } else { + printf("\nCould not decode MIB\n"); + } + + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + ue_mib_free(&uemib); + + return LIBLTE_SUCCESS; +} + +int find_cell(void *uhd, ue_celldetect_t *s, cf_t *buffer, ue_celldetect_result_t *found_cell) +{ + int n; + + INFO("Setting sampling frequency 960 KHz for PSS search\n", 0); + cuhd_set_rx_srate(uhd, 960000.0); + INFO("Starting receiver...\n", 0); + cuhd_start_rx_stream(uhd); + + uint32_t nof_scanned_cells = 0; + uint32_t flen = 4800; + + do { + + if (cuhd_recv(uhd, buffer, flen, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + + n = ue_celldetect_scan(s, buffer, flen, found_cell); + switch(n) { + case CS_FRAME_UNALIGNED: + printf("Realigning frame\n"); + if (cuhd_recv(uhd, buffer, flen/2, 1)<0) { + fprintf(stderr, "Error receiving from USRP\n"); + return LIBLTE_ERROR; + } + return LIBLTE_ERROR; + case CS_CELL_DETECTED: + if (found_cell->peak > 0) { + printf("\tCELL ID: %d, CP: %s, Peak: %.2f, Mode: %d/%d\n", + found_cell->cell_id, lte_cp_string(found_cell->cp), + found_cell->peak, found_cell->mode, s->nof_frames_detected); + } + nof_scanned_cells++; + break; + case CS_CELL_NOT_DETECTED: + nof_scanned_cells++; + break; + case LIBLTE_ERROR: + case LIBLTE_ERROR_INVALID_INPUTS: + fprintf(stderr, "Error calling cellsearch_scan()\n"); + return LIBLTE_ERROR; + } + } while(nof_scanned_cells < 3 && n != CS_CELL_DETECTED); + + INFO("Stopping receiver...\n", 0); + cuhd_stop_rx_stream(uhd); + cuhd_flush_buffer(uhd); + + return n; +} + +int main(int argc, char **argv) { + int n; + void *uhd; + ue_celldetect_t s; + ue_celldetect_result_t found_cell; + cf_t *buffer; + + parse_args(argc, argv); + + printf("Opening UHD device...\n"); + if (cuhd_open(uhd_args, &uhd)) { + fprintf(stderr, "Error opening uhd\n"); + exit(-1); + } + 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/1000000); + + buffer = vec_malloc(sizeof(cf_t) * 96000); + if (!buffer) { + perror("malloc"); + return LIBLTE_ERROR; + } + + if (ue_celldetect_init(&s)) { + fprintf(stderr, "Error initiating UE sync module\n"); + exit(-1); + } + if (threshold > 0) { + ue_celldetect_set_threshold(&s, threshold); + } + + if (nof_frames_total > 0) { + ue_celldetect_set_nof_frames_total(&s, nof_frames_total); + } + if (nof_frames_detected > 0) { + ue_celldetect_set_nof_frames_detected(&s, nof_frames_detected); + } + + n = find_cell(uhd, &s, buffer, &found_cell); + if (n < 0) { + fprintf(stderr, "Error searching cell\n"); + exit(-1); + } + if (n == CS_CELL_DETECTED) { + if (decode_pbch(uhd, buffer, &found_cell)) { + fprintf(stderr, "Error decoding PBCH\n"); + exit(-1); + } + } + + ue_celldetect_free(&s); + cuhd_close(uhd); + exit(0); +} + + diff --git a/lte/phy/lib/phch/test/ue_sync_usrp.c b/lte/phy/lib/ue/test/ue_sync_usrp.c similarity index 80% rename from lte/phy/lib/phch/test/ue_sync_usrp.c rename to lte/phy/lib/ue/test/ue_sync_usrp.c index 115d2649b..62bb1d7c5 100644 --- a/lte/phy/lib/phch/test/ue_sync_usrp.c +++ b/lte/phy/lib/ue/test/ue_sync_usrp.c @@ -49,16 +49,19 @@ plot_real_t poutfft; int nof_frames = -1; float threshold = -1.0; +int N_id_2 = -1; +uint32_t nof_prb = 6; float uhd_freq = 0.0, uhd_gain = 20.0; char *uhd_args = ""; int disable_plots = 0; void usage(char *prog) { - printf("Usage: %s [agntdv] -f uhd_freq\n", prog); + printf("Usage: %s [agntdpv] -f uhd_freq -i N_id_2\n", prog); printf("\t-a UHD args [Default %s]\n", uhd_args); printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain); printf("\t-n nof_frames [Default infinite]\n"); + printf("\t-p nof_prb [Default %d]\n", nof_prb); printf("\t-t threshold [Default %.2f]\n",threshold); #ifndef DISABLE_GRAPHICS @@ -69,8 +72,14 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "agntdvf")) != -1) { + while ((opt = getopt(argc, argv, "agntdvfip")) != -1) { switch (opt) { + case 'i': + N_id_2 = atoi(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + break; case 'n': nof_frames = atoi(argv[optind]); break; @@ -97,7 +106,7 @@ void parse_args(int argc, char **argv) { exit(-1); } } - if (uhd_freq == 0.0) { + if (uhd_freq == 0.0 || N_id_2 == -1) { usage(argv[0]); exit(-1); } @@ -117,6 +126,12 @@ void input_init() { cuhd_rx_wait_lo_locked(uhd); DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq/1000000); + int srate = lte_sampling_freq_hz(nof_prb); + if (srate > 0) { + cuhd_set_rx_srate(uhd, (double) srate); + } else { + fprintf(stderr, "Error invalid nof_prb=%d\n",nof_prb); + } DEBUG("Starting receiver...\n", 0); cuhd_start_rx_stream(uhd); @@ -152,7 +167,6 @@ int main(int argc, char **argv) { float peak; struct timeval t[3]; float mean_ce_time=0; - bool signal_detected; lte_fft_t fft; lte_cell_t cell; @@ -167,15 +181,28 @@ int main(int argc, char **argv) { #endif input_init(); + + cell.cp = CPNORM; + cell.id = N_id_2; + cell.nof_ports = 1; + cell.nof_prb = nof_prb; - if (ue_sync_init(&s, cuhd_set_rx_srate, cuhd_recv_wrapper, uhd)) { + if (ue_sync_init(&s, cell, cuhd_recv_wrapper, uhd)) { fprintf(stderr, "Error initiating UE sync module\n"); exit(-1); } - - ue_sync_pbch_enable(&s, true); - - signal_detected = true; + + pss_synch_init_fft(&pss, + SF_LEN(lte_symbol_sz(cell.nof_prb)), + lte_symbol_sz(cell.nof_prb)); + pss_synch_set_N_id_2(&pss, cell.id%3); + sf_symbols = vec_malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!sf_symbols) { + perror("malloc"); + exit(-1); + } + lte_fft_init(&fft, cell.cp, cell.nof_prb); + frame_cnt = 0; mean_ce_time=0; uint32_t valid_frames=0; @@ -190,22 +217,6 @@ int main(int argc, char **argv) { if (n == 1 && ue_sync_get_sfidx(&s) == 0) { - if (signal_detected) { - cell = ue_sync_get_cell(&s); - pss_synch_init_fft(&pss, - SF_LEN(lte_symbol_sz(cell.nof_prb), cell.cp), - lte_symbol_sz(cell.nof_prb)); - pss_synch_set_N_id_2(&pss, cell.id%3); - - sf_symbols = vec_malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); - if (!sf_symbols) { - perror("malloc"); - exit(-1); - } - lte_fft_init(&fft, cell.cp, cell.nof_prb); - signal_detected = false; - } - mean_ce_time = (float) (mean_ce_time + (float) t[0].tv_usec * valid_frames) / (valid_frames+1); valid_frames++; @@ -227,13 +238,9 @@ int main(int argc, char **argv) { } #endif - pos = pss_synch_find_pss(&pss, input_buffer, &peak, NULL); - /*if (pos > 962 || pos < 958) { - unaligned++; - } - */ + pos = pss_synch_find_pss(&pss, input_buffer, &peak); printf("CELL_ID: %3d CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Exec: %3.2f\r", - cell.id, ue_sync_get_cfo(&s)/1000, ue_sync_get_sfo(&s)/1000, pos, + sync_get_cell_id(&s.sfind), ue_sync_get_cfo(&s)/1000, ue_sync_get_sfo(&s)/1000, pos, s.mean_exec_time); fflush(stdout); if (VERBOSE_ISINFO()) { diff --git a/lte/phy/lib/utils/src/convolution.c b/lte/phy/lib/utils/src/convolution.c index 721595f80..76a26bdee 100644 --- a/lte/phy/lib/utils/src/convolution.c +++ b/lte/phy/lib/utils/src/convolution.c @@ -53,6 +53,9 @@ int conv_fft_cc_init(conv_fft_cc_t *q, uint32_t input_len, uint32_t filter_len) if (dft_plan(&q->output_plan,q->output_len,BACKWARD,COMPLEX)) { return LIBLTE_ERROR; } + dft_plan_set_norm(&q->input_plan, true); + dft_plan_set_norm(&q->filter_plan, true); + dft_plan_set_norm(&q->output_plan, false); return LIBLTE_SUCCESS; } @@ -80,7 +83,7 @@ uint32_t conv_fft_cc_run(conv_fft_cc_t *q, cf_t *input, cf_t *filter, cf_t *outp dft_run_c(&q->output_plan, q->output_fft, output); - return q->output_len; + return q->output_len-1; } diff --git a/lte/phy/lib/utils/src/dft.c b/lte/phy/lib/utils/src/dft.c index 6b4ddad48..babd0220b 100644 --- a/lte/phy/lib/utils/src/dft.c +++ b/lte/phy/lib/utils/src/dft.c @@ -32,6 +32,7 @@ #include #include "liblte/phy/utils/dft.h" +#include "liblte/phy/utils/vector.h" #define dft_ceil(a,b) ((a-1)/b+1) #define dft_floor(a,b) (a/b) @@ -144,11 +145,8 @@ void dft_run_c(dft_plan_t *plan, dft_c_t *in, dft_c_t *out) { plan->forward, plan->mirror, plan->dc); fftwf_execute(plan->p); if (plan->norm) { - /**FIXME: Use VOLK */ - norm = sqrtf(plan->size); - for (i=0;isize;i++) { - f_out[i] /= norm; - } + norm = 1.0/sqrtf(plan->size); + vec_sc_prod_cfc(f_out, norm, f_out, plan->size); } if (plan->db) { for (i=0;isize;i++) { @@ -168,10 +166,8 @@ void dft_run_r(dft_plan_t *plan, dft_r_t *in, dft_r_t *out) { memcpy(plan->in,in,sizeof(dft_r_t)*plan->size); fftwf_execute(plan->p); if (plan->norm) { - norm = plan->size; - for (i=0;isize; + vec_sc_prod_fff(f_out, norm, f_out, plan->size); } if (plan->db) { for (i=0;im) { + m=tmp; + p=i; + } + } + return p; +#endif +} + void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len) { uint32_t i; uint32_t tmp; diff --git a/matlab/sync/addnoise.m b/matlab/sync/addnoise.m new file mode 100644 index 000000000..56f49c4f8 --- /dev/null +++ b/matlab/sync/addnoise.m @@ -0,0 +1,6 @@ +function [ y ] = addnoise( x, snr_db ) +v = 10^(-snr_db/10); +y=x+sqrt(v)*(randn(size(x))+1i*randn(size(x)))/sqrt(2); +y=y/sqrt(mean(y.*conj(y))); +end + diff --git a/matlab/sync/find_peaks.m b/matlab/sync/find_peaks.m new file mode 100644 index 000000000..7f9b27bf5 --- /dev/null +++ b/matlab/sync/find_peaks.m @@ -0,0 +1,13 @@ +function [peaks] = find_peaks(x, N_id_2, fft_size) + +flen=4800*(ceil(fft_size/64)); + +n=floor(length(x)/flen)*flen; +xf=reshape(x(1:n),flen,[]); + +[n m] = size(xf); + +peaks=zeros(m,1); +for i=1:m + [w, peaks(i)]= find_pss2(xf(:,i),N_id_2,fft_size); +end diff --git a/matlab/sync/find_pss2.m b/matlab/sync/find_pss2.m index a4b5b8d3d..478a47cdb 100644 --- a/matlab/sync/find_pss2.m +++ b/matlab/sync/find_pss2.m @@ -1,18 +1,14 @@ -function [ fs eps p_m w2] = find_pss( x, N_id_2, fft_size) +function [w2, m, idx] = find_pss2( x, N_id_2, fft_size) c=lte_pss_zc(N_id_2); cc=[zeros(fft_size/2-31,1); c; zeros(fft_size/2-31,1)]; - cc=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)]; - ccf=conj(ifft(cc)); - - w2=conv(x,ccf); - %plot(10*log10(abs(w2)));%./mean(abs(w2)))); - plot(abs(w2)) - %axis([0 length(w2) 0 20]) - [m i]=max(abs(w2)); - p_m = m/mean(abs(w2)); + ccd=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)]; + ccf=sqrt(fft_size)*conj(ifft(ccd)); + + w2=abs(conv(x,ccf/62)).^2/var(x,1)/sqrt(2); + plot(w2) + [m, idx]=max(w2); - fprintf('Frame starts at %d, m=%g, p=%g, p/m=%g dB\n',i, ... - mean(abs(w2)), m, 10*log10(m/mean(abs(w2)))); - + %fprintf('Frame starts at %d, energy=%g, p=%g, p/en=%g dB\n',i, ... + % en, m, m/en); end