mirror of https://github.com/PentHertz/srsLTE.git
Merge pull request #47 from ismagom/master
Fixed problems with PDSCH UE example and synchronization.
This commit is contained in:
commit
750a23f1df
|
@ -42,14 +42,22 @@ CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_dot_prod_32f HAVE_VOLK_DOTPROD_F_FUNCTION
|
||||||
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32f_atan2_32f HAVE_VOLK_ATAN_FUNCTION)
|
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_s32f_atan2_32f HAVE_VOLK_ATAN_FUNCTION)
|
||||||
CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_convert_16i HAVE_VOLK_CONVERT_FI_FUNCTION)
|
CHECK_FUNCTION_EXISTS_MATH(volk_32f_s32f_convert_16i HAVE_VOLK_CONVERT_FI_FUNCTION)
|
||||||
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_32f_x2 HAVE_VOLK_DEINTERLEAVE_FUNCTION)
|
CHECK_FUNCTION_EXISTS_MATH(volk_32fc_deinterleave_32f_x2 HAVE_VOLK_DEINTERLEAVE_FUNCTION)
|
||||||
|
|
||||||
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_subtract_32f HAVE_VOLK_SUB_FLOAT_FUNCTION)
|
CHECK_FUNCTION_EXISTS_MATH(volk_32f_x2_subtract_32f HAVE_VOLK_SUB_FLOAT_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")
|
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})
|
IF(${HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION})
|
||||||
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION")
|
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DOTPROD_CONJ_FC_FUNCTION")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
IF(${HAVE_VOLK_DEINTERLEAVE_FUNCTION})
|
||||||
|
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_DEINTERLEAVE_FUNCTION")
|
||||||
|
ENDIF()
|
||||||
IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION})
|
IF(${HAVE_VOLK_SUB_FLOAT_FUNCTION})
|
||||||
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION")
|
SET(VOLK_DEFINITIONS "${VOLK_DEFINITIONS}; HAVE_VOLK_SUB_FLOAT_FUNCTION")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
|
@ -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 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 bool cuhd_rx_wait_lo_locked(void *h);
|
||||||
|
|
||||||
LIBLTE_API double cuhd_set_rx_srate(void *h,
|
LIBLTE_API double cuhd_set_rx_srate(void *h,
|
||||||
|
|
|
@ -95,6 +95,15 @@ int cuhd_stop_rx_stream(void *h)
|
||||||
return 0;
|
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)
|
int cuhd_start_rx_stream_nsamples(void *h, uint32_t nsamples)
|
||||||
{
|
{
|
||||||
cuhd_handler *handler = static_cast < cuhd_handler * >(h);
|
cuhd_handler *handler = static_cast < cuhd_handler * >(h);
|
||||||
|
@ -112,6 +121,7 @@ int cuhd_open(char *args, void **h)
|
||||||
std::string _args = std::string(args);
|
std::string _args = std::string(args);
|
||||||
handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=30720000" + ", num_recv_frames=512");
|
handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=30720000" + ", num_recv_frames=512");
|
||||||
|
|
||||||
|
// handler->usrp = uhd::usrp::multi_usrp::make(_args + ", master_clock_rate=50000000" + ", num_recv_frames=512");
|
||||||
handler->usrp->set_clock_source("internal");
|
handler->usrp->set_clock_source("internal");
|
||||||
|
|
||||||
std::string otw, cpu;
|
std::string otw, cpu;
|
||||||
|
|
|
@ -51,7 +51,7 @@ LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND)
|
||||||
# These two can be compiled without UHD or graphics support
|
# These two can be compiled without UHD or graphics support
|
||||||
#################################################################
|
#################################################################
|
||||||
|
|
||||||
add_executable(pdsch_ue pdsch_ue.c iodev.c)
|
add_executable(pdsch_ue pdsch_ue.c iodev.c cell_search_utils.c)
|
||||||
target_link_libraries(pdsch_ue lte_phy)
|
target_link_libraries(pdsch_ue lte_phy)
|
||||||
|
|
||||||
add_executable(pdsch_enodeb pdsch_enodeb.c)
|
add_executable(pdsch_enodeb pdsch_enodeb.c)
|
||||||
|
@ -81,8 +81,8 @@ ENDIF(${GRAPHICS_FIND} EQUAL -1)
|
||||||
|
|
||||||
IF(${CUHD_FIND} GREATER -1)
|
IF(${CUHD_FIND} GREATER -1)
|
||||||
|
|
||||||
add_executable(scan_mib scan_mib.c)
|
add_executable(cell_search cell_search.c cell_search_utils.c)
|
||||||
target_link_libraries(scan_mib lte_phy cuhd )
|
target_link_libraries(cell_search lte_phy cuhd )
|
||||||
|
|
||||||
MESSAGE(STATUS " UHD examples will be installed.")
|
MESSAGE(STATUS " UHD examples will be installed.")
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,14 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "liblte/phy/phy.h"
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
#include "cell_search_utils.h"
|
||||||
|
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
#include "liblte/cuhd/cuhd.h"
|
#include "liblte/cuhd/cuhd.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,32 +49,34 @@
|
||||||
#define FLEN 9600
|
#define FLEN 9600
|
||||||
#define FLEN_PERIOD 0.005
|
#define FLEN_PERIOD 0.005
|
||||||
|
|
||||||
|
#define MAX_EARFCN 1000
|
||||||
|
|
||||||
|
|
||||||
int band = -1;
|
int band = -1;
|
||||||
int earfcn_start=-1, earfcn_end = -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;
|
float uhd_gain = 60.0;
|
||||||
char *uhd_args="";
|
char *uhd_args="";
|
||||||
|
|
||||||
#define MAX_EARFCN 1000
|
|
||||||
lte_earfcn_t channels[MAX_EARFCN];
|
|
||||||
|
|
||||||
|
|
||||||
void usage(char *prog) {
|
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-a UHD args [Default %s]\n", uhd_args);
|
||||||
printf("\t-g UHD gain [Default %.2f dB]\n", uhd_gain);
|
printf("\t-g UHD gain [Default %.2f dB]\n", uhd_gain);
|
||||||
printf("\t-s earfcn_start [Default All]\n");
|
printf("\t-s earfcn_start [Default All]\n");
|
||||||
printf("\t-e earfcn_end [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");
|
printf("\t-v [set verbose to debug, default none]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_args(int argc, char **argv) {
|
void parse_args(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "asefgvb")) != -1) {
|
while ((opt = getopt(argc, argv, "agsendtvb")) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'a':
|
case 'a':
|
||||||
uhd_args = argv[optind];
|
uhd_args = argv[optind];
|
||||||
|
@ -84,8 +90,14 @@ void parse_args(int argc, char **argv) {
|
||||||
case 'e':
|
case 'e':
|
||||||
earfcn_end = atoi(argv[optind]);
|
earfcn_end = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'n':
|
||||||
nof_frames_find = atoi(argv[optind]);
|
nof_frames_total = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
nof_frames_detected = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
threshold = atof(argv[optind]);
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
uhd_gain = atof(argv[optind]);
|
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 main(int argc, char **argv) {
|
||||||
int ret;
|
int n;
|
||||||
int frame_cnt;
|
|
||||||
int nof_freqs;
|
|
||||||
uint32_t freq;
|
|
||||||
ue_sync_t uesync;
|
|
||||||
void *uhd;
|
void *uhd;
|
||||||
|
ue_celldetect_t s;
|
||||||
|
ue_celldetect_result_t found_cells[3];
|
||||||
cf_t *buffer;
|
cf_t *buffer;
|
||||||
|
int nof_freqs;
|
||||||
if (argc < 3) {
|
lte_earfcn_t channels[MAX_EARFCN];
|
||||||
usage(argv[0]);
|
uint32_t freq;
|
||||||
exit(-1);
|
pbch_mib_t mib;
|
||||||
}
|
|
||||||
|
parse_args(argc, argv);
|
||||||
parse_args(argc,argv);
|
|
||||||
|
|
||||||
printf("Opening UHD device...\n");
|
printf("Opening UHD device...\n");
|
||||||
if (cuhd_open(uhd_args, &uhd)) {
|
if (cuhd_open(uhd_args, &uhd)) {
|
||||||
fprintf(stderr, "Error opening uhd\n");
|
fprintf(stderr, "Error opening uhd\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set uhd_gain */
|
|
||||||
cuhd_set_rx_gain(uhd, uhd_gain);
|
cuhd_set_rx_gain(uhd, uhd_gain);
|
||||||
|
|
||||||
nof_freqs = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN);
|
nof_freqs = lte_band_get_fd_band(band, channels, earfcn_start, earfcn_end, MAX_EARFCN);
|
||||||
if (nof_freqs < 0) {
|
if (nof_freqs < 0) {
|
||||||
fprintf(stderr, "Error getting EARFCN list\n");
|
fprintf(stderr, "Error getting EARFCN list\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (freq=0;freq<nof_freqs;freq++) {
|
|
||||||
/* set freq */
|
|
||||||
cuhd_stop_rx_stream(uhd);
|
|
||||||
cuhd_set_rx_freq(uhd, (double) channels[freq].fd * MHZ);
|
|
||||||
cuhd_rx_wait_lo_locked(uhd);
|
|
||||||
|
|
||||||
if (ue_sync_init(&uesync, cuhd_set_rx_srate, cuhd_recv_wrapper, uhd)) {
|
|
||||||
fprintf(stderr, "Error initiating UE sync\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
ue_sync_set_nof_pbch_decodes(&uesync, 1);
|
|
||||||
ue_sync_decode_sss_on_track(&uesync, true);
|
|
||||||
ue_sync_change_srate(&uesync, false);
|
|
||||||
|
|
||||||
DEBUG("Starting receiver...\n",0);
|
|
||||||
cuhd_start_rx_stream(uhd);
|
|
||||||
usleep(10000);
|
|
||||||
|
|
||||||
|
buffer = vec_malloc(sizeof(cf_t) * 96000);
|
||||||
/* Receive up to a maximum of nof_frames_find */
|
if (!buffer) {
|
||||||
frame_cnt = 0;
|
perror("malloc");
|
||||||
ret = 0;
|
return LIBLTE_ERROR;
|
||||||
ue_sync_reset(&uesync);
|
|
||||||
agc_reset(&uesync.agc);
|
|
||||||
|
|
||||||
while(frame_cnt < nof_frames_find && ret == 0) {
|
|
||||||
ret = ue_sync_get_buffer(&uesync, &buffer);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error calling ue_sync_work()\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
frame_cnt++;
|
|
||||||
printf("[%3d/%d]: EARFCN %d Freq. %.2f MHz looking for PSS. RSSI: %+2.2f dB...\r", freq, nof_freqs,
|
|
||||||
channels[freq].id, channels[freq].fd, 20*log10f(agc_get_rssi(&uesync.agc)));fflush(stdout);
|
|
||||||
if (VERBOSE_ISINFO()) {
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret == 1) {
|
|
||||||
if (VERBOSE_ISINFO()) {
|
|
||||||
printf("[%3d/%d]: EARFCN %d Freq. %.2f MHz FOUND MIB ", freq, nof_freqs,
|
|
||||||
channels[freq].id, channels[freq].fd);
|
|
||||||
}
|
|
||||||
printf("RSSI: %+.2f dBm, CFO: %+.4f KHz\n",
|
|
||||||
20*log10f(agc_get_rssi(&uesync.agc)), ue_sync_get_cfo(&uesync));
|
|
||||||
printf("\n");fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
ue_sync_free(&uesync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<nof_freqs;freq+=10) {
|
||||||
|
|
||||||
|
/* set uhd_freq */
|
||||||
|
cuhd_set_rx_freq(uhd, (double) channels[freq].fd * MHZ);
|
||||||
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
|
usleep(10000);
|
||||||
|
INFO("Set uhd_freq to %.3f MHz\n", (double) channels[freq].fd * MHZ/1000000);
|
||||||
|
|
||||||
|
printf("[%3d/%d]: EARFCN %d Freq. %.2f MHz looking for PSS. \r", freq, nof_freqs,
|
||||||
|
channels[freq].id, channels[freq].fd);fflush(stdout);
|
||||||
|
|
||||||
|
if (VERBOSE_ISINFO()) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
n = find_cell(uhd, &s, buffer, found_cells);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error searching cell\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
if (n == CS_CELL_DETECTED) {
|
||||||
|
for (int i=0;i<3;i++) {
|
||||||
|
if (found_cells[i].peak > 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);
|
cuhd_close(uhd);
|
||||||
|
|
||||||
|
|
||||||
printf("\n\nDone\n");
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -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]);
|
|
@ -29,11 +29,12 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "iodev.h"
|
#include "iodev.h"
|
||||||
|
|
||||||
#include "liblte/phy/io/filesource.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/debug.h"
|
||||||
#include "liblte/phy/utils/vector.h"
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
|
||||||
|
@ -41,6 +42,8 @@
|
||||||
#include "liblte/cuhd/cuhd.h"
|
#include "liblte/cuhd/cuhd.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "cell_search_utils.h"
|
||||||
|
|
||||||
|
|
||||||
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) {
|
||||||
DEBUG(" ---- Receive %d samples ---- \n", 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 */
|
/* Setup USRP or input file */
|
||||||
int iodev_init(iodev_t *q, iodev_cfg_t *config) {
|
int iodev_init(iodev_t *q, iodev_cfg_t *config, lte_cell_t *cell, pbch_mib_t *mib) {
|
||||||
|
|
||||||
if (config->input_file_name) {
|
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)) {
|
if (filesource_init(&q->fsrc, config->input_file_name, COMPLEX_FLOAT_BIN)) {
|
||||||
return LIBLTE_ERROR;
|
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) {
|
if (!q->input_buffer_file) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
return LIBLTE_ERROR;
|
return LIBLTE_ERROR;
|
||||||
}
|
}
|
||||||
|
q->sf_idx = 9;
|
||||||
q->mode = FILESOURCE;
|
|
||||||
q->sf_len = 1920;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
|
@ -71,17 +90,76 @@ int iodev_init(iodev_t *q, iodev_cfg_t *config) {
|
||||||
return LIBLTE_ERROR;
|
return LIBLTE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set uhd_freq */
|
|
||||||
cuhd_set_rx_gain(q->uhd, config->uhd_gain);
|
cuhd_set_rx_gain(q->uhd, config->uhd_gain);
|
||||||
|
|
||||||
|
/* set receiver frequency */
|
||||||
cuhd_set_rx_freq(q->uhd, (double) config->uhd_freq);
|
cuhd_set_rx_freq(q->uhd, (double) config->uhd_freq);
|
||||||
|
|
||||||
cuhd_rx_wait_lo_locked(q->uhd);
|
cuhd_rx_wait_lo_locked(q->uhd);
|
||||||
DEBUG("Set uhd_freq to %.3f MHz\n", (double ) config->uhd_freq);
|
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);
|
DEBUG("Starting receiver...\n", 0);
|
||||||
cuhd_start_rx_stream(q->uhd);
|
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
|
// Here, the subframe length and input buffer is managed by ue_sync
|
||||||
q->mode = UHD;
|
q->mode = UHD;
|
||||||
|
@ -113,7 +191,7 @@ void iodev_free(iodev_t *q) {
|
||||||
int iodev_receive(iodev_t *q, cf_t **buffer) {
|
int iodev_receive(iodev_t *q, cf_t **buffer) {
|
||||||
int n;
|
int n;
|
||||||
if (q->mode == FILESOURCE) {
|
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);
|
n = filesource_read(&q->fsrc, q->input_buffer_file, q->sf_len);
|
||||||
*buffer = q->input_buffer_file;
|
*buffer = q->input_buffer_file;
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
|
@ -132,6 +210,11 @@ int iodev_receive(iodev_t *q, cf_t **buffer) {
|
||||||
} else {
|
} else {
|
||||||
n = 1;
|
n = 1;
|
||||||
}
|
}
|
||||||
|
q->sf_idx++;
|
||||||
|
if (q->sf_idx == 10) {
|
||||||
|
q->sf_idx = 0;
|
||||||
|
}
|
||||||
|
usleep(5000);
|
||||||
} else {
|
} else {
|
||||||
/* Use ue_sync_work which returns a synchronized buffer of subframe samples */
|
/* Use ue_sync_work which returns a synchronized buffer of subframe samples */
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
|
@ -160,4 +243,12 @@ bool iodev_isUSRP(iodev_t *q) {
|
||||||
return q->mode == UHD;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
#include "liblte/config.h"
|
#include "liblte/config.h"
|
||||||
|
|
||||||
#include "liblte/phy/phch/ue_sync.h"
|
#include "liblte/phy/ue/ue_sync.h"
|
||||||
#include "liblte/phy/io/filesource.h"
|
#include "liblte/phy/io/filesource.h"
|
||||||
|
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
|
@ -54,6 +54,10 @@ typedef _Complex float cf_t;
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
typedef struct LIBLTE_API {
|
||||||
char *input_file_name;
|
char *input_file_name;
|
||||||
|
uint32_t cell_id_file;
|
||||||
|
uint32_t nof_prb_file;
|
||||||
|
uint32_t nof_ports_file;
|
||||||
|
|
||||||
float uhd_freq;
|
float uhd_freq;
|
||||||
float uhd_gain;
|
float uhd_gain;
|
||||||
char *uhd_args;
|
char *uhd_args;
|
||||||
|
@ -66,6 +70,7 @@ typedef struct LIBLTE_API {
|
||||||
ue_sync_t sframe;
|
ue_sync_t sframe;
|
||||||
#endif
|
#endif
|
||||||
uint32_t sf_len;
|
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
|
cf_t *input_buffer_file; // for UHD mode, the input buffer is managed by sync_frame_t
|
||||||
filesource_t fsrc;
|
filesource_t fsrc;
|
||||||
iodev_cfg_t config;
|
iodev_cfg_t config;
|
||||||
|
@ -74,7 +79,9 @@ typedef struct LIBLTE_API {
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int iodev_init(iodev_t *q,
|
LIBLTE_API int iodev_init(iodev_t *q,
|
||||||
iodev_cfg_t *config);
|
iodev_cfg_t *config,
|
||||||
|
lte_cell_t *cell,
|
||||||
|
pbch_mib_t *mib);
|
||||||
|
|
||||||
LIBLTE_API void iodev_free(iodev_t *q);
|
LIBLTE_API void iodev_free(iodev_t *q);
|
||||||
|
|
||||||
|
@ -83,6 +90,8 @@ LIBLTE_API int iodev_receive(iodev_t *q,
|
||||||
|
|
||||||
LIBLTE_API void* iodev_get_cuhd(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_isfile(iodev_t *q);
|
||||||
|
|
||||||
LIBLTE_API bool iodev_isUSRP(iodev_t *q);
|
LIBLTE_API bool iodev_isUSRP(iodev_t *q);
|
||||||
|
|
|
@ -52,7 +52,7 @@ uint32_t mcs_idx = 12;
|
||||||
int nof_frames = -1;
|
int nof_frames = -1;
|
||||||
|
|
||||||
char *uhd_args = "";
|
char *uhd_args = "";
|
||||||
float uhd_amp = 0.25, uhd_gain = 10.0, uhd_freq = 2400000000;
|
float uhd_amp = 0.01, uhd_gain = 10.0, uhd_freq = 2400000000;
|
||||||
|
|
||||||
filesink_t fsink;
|
filesink_t fsink;
|
||||||
lte_fft_t ifft;
|
lte_fft_t ifft;
|
||||||
|
@ -70,6 +70,7 @@ void usage(char *prog) {
|
||||||
printf("Usage: %s [agmfoncvp]\n", prog);
|
printf("Usage: %s [agmfoncvp]\n", prog);
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
printf("\t-a UHD args [Default %s]\n", uhd_args);
|
||||||
|
printf("\t-l UHD amplitude [Default %.2f]\n", uhd_amp);
|
||||||
printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain);
|
printf("\t-g UHD TX gain [Default %.2f dB]\n", uhd_gain);
|
||||||
printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq / 1000000);
|
printf("\t-f UHD TX frequency [Default %.1f MHz]\n", uhd_freq / 1000000);
|
||||||
#else
|
#else
|
||||||
|
@ -85,7 +86,7 @@ void usage(char *prog) {
|
||||||
|
|
||||||
void parse_args(int argc, char **argv) {
|
void parse_args(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "agfmoncpv")) != -1) {
|
while ((opt = getopt(argc, argv, "aglfmoncpv")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'a':
|
case 'a':
|
||||||
uhd_args = argv[optind];
|
uhd_args = argv[optind];
|
||||||
|
@ -93,6 +94,9 @@ void parse_args(int argc, char **argv) {
|
||||||
case 'g':
|
case 'g':
|
||||||
uhd_gain = atof(argv[optind]);
|
uhd_gain = atof(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
uhd_amp = atof(argv[optind]);
|
||||||
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
uhd_freq = atof(argv[optind]);
|
uhd_freq = atof(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
@ -253,7 +257,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
N_id_2 = cell.id % 3;
|
N_id_2 = cell.id % 3;
|
||||||
sf_n_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB;
|
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_* */
|
/* this *must* be called after setting slot_len_* */
|
||||||
base_init();
|
base_init();
|
||||||
|
@ -355,7 +359,10 @@ int main(int argc, char **argv) {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdsch_encode(&pdsch, data, sf_symbols, sf_idx, &harq_process, ra_dl.rv_idx);
|
if (pdsch_encode(&pdsch, data, sf_symbols, sf_idx, &harq_process, ra_dl.rv_idx)) {
|
||||||
|
fprintf(stderr, "Error encoding PDSCH\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Transform to OFDM symbols */
|
/* Transform to OFDM symbols */
|
||||||
lte_ifft_run_sf(&ifft, sf_buffer, output_buffer);
|
lte_ifft_run_sf(&ifft, sf_buffer, output_buffer);
|
||||||
|
|
|
@ -53,8 +53,6 @@ void init_plots();
|
||||||
* Program arguments processing
|
* Program arguments processing
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t cell_id_file;
|
|
||||||
uint32_t nof_prb_file;
|
|
||||||
uint16_t rnti;
|
uint16_t rnti;
|
||||||
int nof_subframes;
|
int nof_subframes;
|
||||||
bool disable_plots;
|
bool disable_plots;
|
||||||
|
@ -62,8 +60,9 @@ typedef struct {
|
||||||
}prog_args_t;
|
}prog_args_t;
|
||||||
|
|
||||||
void args_default(prog_args_t *args) {
|
void args_default(prog_args_t *args) {
|
||||||
args->cell_id_file = 1;
|
args->io_config.cell_id_file = 195;
|
||||||
args->nof_prb_file = 6;
|
args->io_config.nof_prb_file = 50;
|
||||||
|
args->io_config.nof_ports_file = 2;
|
||||||
args->rnti = SIRNTI;
|
args->rnti = SIRNTI;
|
||||||
args->nof_subframes = -1;
|
args->nof_subframes = -1;
|
||||||
args->disable_plots = false;
|
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.input_file_name = NULL;
|
||||||
args->io_config.uhd_args = "";
|
args->io_config.uhd_args = "";
|
||||||
args->io_config.uhd_freq = -1.0;
|
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) {
|
void usage(prog_args_t *args, char *prog) {
|
||||||
printf("Usage: %s [cargfndvtb] [-i input_file | -f rx_frequency (in Hz)]\n", 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-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->nof_prb_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);
|
printf("\t-r RNTI to look for [Default 0x%x]\n", args->rnti);
|
||||||
#ifndef DISABLE_UHD
|
#ifndef DISABLE_UHD
|
||||||
printf("\t-a UHD args [Default %s]\n", args->io_config.uhd_args);
|
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) {
|
void parse_args(prog_args_t *args, int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
args_default(args);
|
args_default(args);
|
||||||
while ((opt = getopt(argc, argv, "icagfndvtbp")) != -1) {
|
while ((opt = getopt(argc, argv, "icagfndvtbpro")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'i':
|
case 'i':
|
||||||
args->io_config.input_file_name = argv[optind];
|
args->io_config.input_file_name = argv[optind];
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
args->cell_id_file = atoi(argv[optind]);
|
args->io_config.cell_id_file = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
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;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
args->io_config.uhd_args = argv[optind];
|
args->io_config.uhd_args = argv[optind];
|
||||||
|
@ -125,6 +128,9 @@ void parse_args(prog_args_t *args, int argc, char **argv) {
|
||||||
case 'n':
|
case 'n':
|
||||||
args->nof_subframes = atoi(argv[optind]);
|
args->nof_subframes = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
args->rnti= atoi(argv[optind]);
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
args->disable_plots = true;
|
args->disable_plots = true;
|
||||||
break;
|
break;
|
||||||
|
@ -149,6 +155,8 @@ void sigintHandler(int x) {
|
||||||
/* TODO: Do something with the output data */
|
/* TODO: Do something with the output data */
|
||||||
char data[10000];
|
char data[10000];
|
||||||
|
|
||||||
|
extern float mean_exec_time;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int ret;
|
int ret;
|
||||||
cf_t *sf_buffer;
|
cf_t *sf_buffer;
|
||||||
|
@ -156,20 +164,13 @@ int main(int argc, char **argv) {
|
||||||
prog_args_t prog_args;
|
prog_args_t prog_args;
|
||||||
lte_cell_t cell;
|
lte_cell_t cell;
|
||||||
ue_dl_t ue_dl;
|
ue_dl_t ue_dl;
|
||||||
bool ue_dl_initiated = false;
|
|
||||||
int64_t sf_cnt;
|
int64_t sf_cnt;
|
||||||
uint32_t sf_idx;
|
|
||||||
pbch_mib_t mib;
|
pbch_mib_t mib;
|
||||||
bool printed_sib = false;
|
bool printed_sib = false;
|
||||||
int rlen;
|
int rlen;
|
||||||
|
|
||||||
parse_args(&prog_args, argc, argv);
|
parse_args(&prog_args, argc, argv);
|
||||||
|
|
||||||
if (iodev_init(&iodev, &prog_args.io_config)) {
|
|
||||||
fprintf(stderr, "Error initiating input device\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
#ifndef DISABLE_GRAPHICS
|
||||||
if (!prog_args.disable_plots) {
|
if (!prog_args.disable_plots) {
|
||||||
init_plots();
|
init_plots();
|
||||||
|
@ -180,19 +181,22 @@ int main(int argc, char **argv) {
|
||||||
printf("\n --- Press Ctrl+C to exit --- \n");
|
printf("\n --- Press Ctrl+C to exit --- \n");
|
||||||
signal(SIGINT, sigintHandler);
|
signal(SIGINT, sigintHandler);
|
||||||
|
|
||||||
/* Initialize frame and subframe counters */
|
/* Initialize subframe counter */
|
||||||
sf_cnt = 0;
|
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 */
|
if (iodev_init(&iodev, &prog_args.io_config, &cell, &mib)) {
|
||||||
ue_sync_decode_sss_on_track(&iodev.sframe, true);
|
exit(-1);
|
||||||
|
}
|
||||||
/* 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 (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 */
|
/* Main loop */
|
||||||
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
|
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
|
||||||
|
|
||||||
ret = iodev_receive(&iodev, &sf_buffer);
|
ret = iodev_receive(&iodev, &sf_buffer);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Error reading from input device (%d)\n", ret);
|
fprintf(stderr, "Error reading from input device (%d)\n", ret);
|
||||||
|
@ -200,79 +204,41 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* iodev_receive returns 1 if successfully read 1 aligned subframe */
|
/* iodev_receive returns 1 if successfully read 1 aligned subframe */
|
||||||
if (ret == 0) {
|
if (ret == 1) {
|
||||||
printf("Finding PSS... Peak: %8.1f, Output level: %+.2f dB\r",
|
rlen = ue_dl_decode(&ue_dl, sf_buffer, data, iodev_get_sfidx(&iodev), prog_args.rnti);
|
||||||
sync_get_peak_value(&iodev.sframe.s), 10*log10f(agc_get_gain(&iodev.sframe.agc)));
|
if (rlen < 0) {
|
||||||
} else if (ret == 1) {
|
fprintf(stderr, "\nError running receiver\n");fflush(stdout);
|
||||||
if (!ue_dl_initiated) {
|
exit(-1);
|
||||||
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, TimeOffset: %4d, 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, iodev.sframe.peak_idx,
|
|
||||||
(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 (iodev_isfile(&iodev)) {
|
if (prog_args.rnti == SIRNTI && !printed_sib && rlen > 0) {
|
||||||
sf_idx++;
|
printf("\n\nDecoded SIB1 Message: ");
|
||||||
if (sf_idx == NSUBFRAMES_X_FRAME) {
|
vec_fprint_hex(stdout, data, rlen);
|
||||||
sf_idx = 0;
|
printf("\n");fflush(stdout);
|
||||||
}
|
printed_sib = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (prog_args.nof_subframes > 0) {
|
// Plot and Printf
|
||||||
sf_cnt++;
|
if (!(sf_cnt % 10)) {
|
||||||
}
|
printf("CFO: %+.4f KHz, SFO: %+.4f Khz, NOI: %.2f Errors: %4d/%4d, BLER: %.1e, Texec: %.2f\r",
|
||||||
if (iodev_isfile(&iodev)) {
|
ue_sync_get_cfo(&iodev.sframe)/1000, ue_sync_get_sfo(&iodev.sframe)/1000,
|
||||||
usleep(5000);
|
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);
|
iodev_free(&iodev);
|
||||||
|
|
||||||
printf("\nBye\n");
|
printf("\nBye\n");
|
||||||
|
|
|
@ -109,7 +109,6 @@ int main(int argc, char **argv) {
|
||||||
int peak_pos[3];
|
int peak_pos[3];
|
||||||
float *cfo;
|
float *cfo;
|
||||||
float peak_value[3];
|
float peak_value[3];
|
||||||
float mean_value[3];
|
|
||||||
int frame_cnt;
|
int frame_cnt;
|
||||||
cf_t *input;
|
cf_t *input;
|
||||||
uint32_t m0, m1;
|
uint32_t m0, m1;
|
||||||
|
@ -201,10 +200,10 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
if (force_N_id_2 != -1) {
|
if (force_N_id_2 != -1) {
|
||||||
N_id_2 = force_N_id_2;
|
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 {
|
} else {
|
||||||
for (N_id_2=0;N_id_2<3;N_id_2++) {
|
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;
|
float max_value=-99999;
|
||||||
N_id_2=-1;
|
N_id_2=-1;
|
||||||
|
@ -218,7 +217,7 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If peak detected */
|
/* 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));
|
sss_idx = peak_pos[N_id_2]-2*(symbol_sz+CP(symbol_sz,CPNORM_LEN));
|
||||||
if (sss_idx >= 0) {
|
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]);
|
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",
|
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),
|
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,
|
peak_pos[N_id_2], m0, m1,
|
||||||
cfo[frame_cnt]);
|
cfo[frame_cnt]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
|
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "liblte/config.h"
|
#include "liblte/config.h"
|
||||||
|
|
||||||
#ifndef CH_AWGN_
|
#ifndef CH_AWGN_
|
||||||
|
@ -37,13 +39,15 @@ typedef _Complex float cf_t;
|
||||||
LIBLTE_API void ch_awgn_c(const cf_t* input,
|
LIBLTE_API void ch_awgn_c(const cf_t* input,
|
||||||
cf_t* output,
|
cf_t* output,
|
||||||
float variance,
|
float variance,
|
||||||
int buff_sz);
|
uint32_t len);
|
||||||
|
|
||||||
LIBLTE_API void ch_awgn_f(const float* x,
|
LIBLTE_API void ch_awgn_f(const float* x,
|
||||||
float* y,
|
float* y,
|
||||||
float variance,
|
float variance,
|
||||||
int buff_sz);
|
uint32_t len);
|
||||||
|
|
||||||
|
LIBLTE_API float ch_awgn_get_variance(float ebno_db,
|
||||||
|
float rate);
|
||||||
|
|
||||||
/* High-level API */
|
/* High-level API */
|
||||||
|
|
||||||
|
|
|
@ -65,12 +65,12 @@ typedef enum {CPNORM, CPEXT} lte_cp_t;
|
||||||
#define SYMBOL_SZ_MAX 2048
|
#define SYMBOL_SZ_MAX 2048
|
||||||
|
|
||||||
#define CPNORM_NSYMB 7
|
#define CPNORM_NSYMB 7
|
||||||
#define CPNORM_SF_NSYMB 2*CPNORM_NSYMB
|
#define CPNORM_SF_NSYMB (2*CPNORM_NSYMB)
|
||||||
#define CPNORM_0_LEN 160
|
#define CPNORM_0_LEN 160
|
||||||
#define CPNORM_LEN 144
|
#define CPNORM_LEN 144
|
||||||
|
|
||||||
#define CPEXT_NSYMB 6
|
#define CPEXT_NSYMB 6
|
||||||
#define CPEXT_SF_NSYMB 2*CPEXT_NSYMB
|
#define CPEXT_SF_NSYMB (2*CPEXT_NSYMB)
|
||||||
#define CPEXT_LEN 512
|
#define CPEXT_LEN 512
|
||||||
#define CPEXT_7_5_LEN 1024
|
#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_ISEXT(cp) (cp==CPEXT)
|
||||||
#define CP_NSYMB(cp) (CP_ISNORM(cp)?CPNORM_NSYMB:CPEXT_NSYMB)
|
#define CP_NSYMB(cp) (CP_ISNORM(cp)?CPNORM_NSYMB:CPEXT_NSYMB)
|
||||||
|
|
||||||
#define CP(symbol_sz, c) (c*symbol_sz/2048)
|
#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_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_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(symbol_sz) (480*((symbol_sz)/64))
|
||||||
#define SLOT_LEN_CPEXT(symbol_sz) (CPEXT_NSYMB*(symbol_sz+CP(symbol_sz, CPEXT_LEN)))
|
#define SF_LEN(symbol_sz) (2*SLOT_LEN(symbol_sz))
|
||||||
#define SLOT_LEN(symbol_sz, cp) (CP_ISNORM(cp)?SLOT_LEN_CPNORM(symbol_sz):SLOT_LEN_CPEXT(symbol_sz))
|
#define SF_LEN_MAX (SF_LEN(SYMBOL_SZ_MAX))
|
||||||
|
|
||||||
#define SF_LEN_CPNORM(symbol_sz) (2*SLOT_LEN_CPNORM(symbol_sz))
|
#define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp))
|
||||||
#define SF_LEN_CPEXT(symbol_sz) (2*SLOT_LEN_CPEXT(symbol_sz))
|
#define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp))
|
||||||
#define SF_LEN(symbol_sz, cp) (2*SLOT_LEN(symbol_sz, cp))
|
|
||||||
#define SF_LEN_MAX SF_LEN(SYMBOL_SZ_MAX, CPNORM)
|
|
||||||
|
|
||||||
#define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp))
|
|
||||||
#define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp))
|
|
||||||
|
|
||||||
#define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN))))
|
#define SLOT_IDX_CPNORM(idx, symbol_sz) (idx==0?(CP(symbol_sz, CPNORM_0_LEN)):(CP(symbol_sz, CPNORM_0_LEN)+idx*(symbol_sz+CP(symbol_sz, CPNORM_LEN))))
|
||||||
#define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN)))
|
#define SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+CP(symbol_sz, CPEXT_LEN)))
|
||||||
|
|
|
@ -89,13 +89,13 @@ LIBLTE_API int pbch_init(pbch_t *q,
|
||||||
|
|
||||||
LIBLTE_API void pbch_free(pbch_t *q);
|
LIBLTE_API void pbch_free(pbch_t *q);
|
||||||
LIBLTE_API int pbch_decode(pbch_t *q,
|
LIBLTE_API int pbch_decode(pbch_t *q,
|
||||||
cf_t *sf_symbols,
|
cf_t *slot1_symbols,
|
||||||
cf_t *ce[MAX_PORTS],
|
cf_t *ce_slot1[MAX_PORTS],
|
||||||
pbch_mib_t *mib);
|
pbch_mib_t *mib);
|
||||||
|
|
||||||
LIBLTE_API int pbch_encode(pbch_t *q,
|
LIBLTE_API int pbch_encode(pbch_t *q,
|
||||||
pbch_mib_t *mib,
|
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);
|
LIBLTE_API void pbch_decode_reset(pbch_t *q);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
#include "liblte/phy/phch/dci.h"
|
#include "liblte/phy/phch/dci.h"
|
||||||
#include "liblte/phy/phch/regs.h"
|
#include "liblte/phy/phch/regs.h"
|
||||||
|
|
||||||
#define TDEC_ITERATIONS 6
|
#define TDEC_MAX_ITERATIONS 6
|
||||||
|
|
||||||
typedef _Complex float cf_t;
|
typedef _Complex float cf_t;
|
||||||
|
|
||||||
|
@ -75,6 +75,9 @@ typedef struct LIBLTE_API {
|
||||||
uint32_t max_symbols;
|
uint32_t max_symbols;
|
||||||
bool rnti_is_set;
|
bool rnti_is_set;
|
||||||
uint16_t rnti;
|
uint16_t rnti;
|
||||||
|
uint32_t nof_iterations;
|
||||||
|
uint64_t average_nof_iterations_n;
|
||||||
|
float average_nof_iterations;
|
||||||
|
|
||||||
/* buffers */
|
/* buffers */
|
||||||
// void buffers are shared for tx and rx
|
// void buffers are shared for tx and rx
|
||||||
|
@ -128,6 +131,10 @@ LIBLTE_API int pdsch_decode(pdsch_t *q,
|
||||||
pdsch_harq_t *harq_process,
|
pdsch_harq_t *harq_process,
|
||||||
uint32_t rv_idx);
|
uint32_t rv_idx);
|
||||||
|
|
||||||
|
LIBLTE_API float pdsch_average_noi(pdsch_t *q);
|
||||||
|
|
||||||
|
LIBLTE_API uint32_t pdsch_last_noi(pdsch_t *q);
|
||||||
|
|
||||||
LIBLTE_API int pdsch_get(pdsch_t *q,
|
LIBLTE_API int pdsch_get(pdsch_t *q,
|
||||||
cf_t *sf_symbols,
|
cf_t *sf_symbols,
|
||||||
cf_t *pdsch_symbols,
|
cf_t *pdsch_symbols,
|
||||||
|
|
|
@ -92,8 +92,11 @@
|
||||||
#include "liblte/phy/phch/pbch.h"
|
#include "liblte/phy/phch/pbch.h"
|
||||||
#include "liblte/phy/phch/pcfich.h"
|
#include "liblte/phy/phch/pcfich.h"
|
||||||
#include "liblte/phy/phch/phich.h"
|
#include "liblte/phy/phch/phich.h"
|
||||||
#include "liblte/phy/phch/ue_sync.h"
|
|
||||||
#include "liblte/phy/phch/ue_dl.h"
|
#include "liblte/phy/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"
|
#include "liblte/phy/scrambling/scrambling.h"
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ typedef struct LIBLTE_API {
|
||||||
|
|
||||||
cf_t *pss_signal_freq[3]; // One sequence for each N_id_2
|
cf_t *pss_signal_freq[3]; // One sequence for each N_id_2
|
||||||
cf_t *tmp_input;
|
cf_t *tmp_input;
|
||||||
float *conv_abs;
|
|
||||||
cf_t *conv_output;
|
cf_t *conv_output;
|
||||||
|
|
||||||
}pss_synch_t;
|
}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,
|
LIBLTE_API int pss_synch_find_pss(pss_synch_t *q,
|
||||||
cf_t *input,
|
cf_t *input,
|
||||||
float *corr_peak_value,
|
float *corr_peak_value);
|
||||||
float *corr_mean_value);
|
|
||||||
|
|
||||||
LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q,
|
LIBLTE_API float pss_synch_cfo_compute(pss_synch_t* q,
|
||||||
cf_t *pss_recv);
|
cf_t *pss_recv);
|
||||||
|
|
|
@ -51,75 +51,60 @@
|
||||||
* functions sync_pss_det_absolute() and sync_pss_det_peakmean().
|
* functions sync_pss_det_absolute() and sync_pss_det_peakmean().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum sync_pss_det { ABSOLUTE, PEAK_MEAN};
|
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
typedef struct LIBLTE_API {
|
||||||
pss_synch_t pss_find;
|
pss_synch_t pss;
|
||||||
pss_synch_t pss_track;
|
|
||||||
sss_synch_t sss;
|
sss_synch_t sss;
|
||||||
enum sync_pss_det pss_mode;
|
float threshold;
|
||||||
float find_threshold;
|
float mean_energy;
|
||||||
float track_threshold;
|
|
||||||
float peak_value;
|
float peak_value;
|
||||||
|
float mean_peak_value;
|
||||||
uint32_t N_id_2;
|
uint32_t N_id_2;
|
||||||
uint32_t N_id_1;
|
uint32_t N_id_1;
|
||||||
uint32_t slot_id;
|
uint32_t sf_idx;
|
||||||
uint32_t fft_size;
|
uint32_t fft_size;
|
||||||
uint32_t find_frame_size;
|
uint32_t frame_size;
|
||||||
|
uint64_t frame_cnt;
|
||||||
float cfo;
|
float cfo;
|
||||||
bool detect_cp;
|
bool detect_cp;
|
||||||
bool sss_en;
|
bool sss_en;
|
||||||
|
bool normalize_en;
|
||||||
lte_cp_t cp;
|
lte_cp_t cp;
|
||||||
}sync_t;
|
}sync_t;
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int sync_init(sync_t *q,
|
LIBLTE_API int sync_init(sync_t *q,
|
||||||
uint32_t find_frame_size,
|
uint32_t frame_size,
|
||||||
uint32_t track_frame_size,
|
|
||||||
uint32_t fft_size);
|
uint32_t fft_size);
|
||||||
|
|
||||||
LIBLTE_API void sync_free(sync_t *q);
|
LIBLTE_API void sync_free(sync_t *q);
|
||||||
|
|
||||||
LIBLTE_API int sync_realloc(sync_t *q,
|
LIBLTE_API void sync_reset(sync_t *q);
|
||||||
uint32_t find_frame_size,
|
|
||||||
uint32_t track_frame_size,
|
|
||||||
uint32_t fft_size);
|
|
||||||
|
|
||||||
/* Finds a correlation peak in the input signal. The signal must be sampled at 1.92 MHz and should be
|
/* Finds a correlation peak in the input signal around position find_offset */
|
||||||
subframe_size long at least */
|
|
||||||
LIBLTE_API int sync_find(sync_t *q,
|
LIBLTE_API int sync_find(sync_t *q,
|
||||||
cf_t *input,
|
cf_t *input,
|
||||||
|
uint32_t find_offset,
|
||||||
uint32_t *peak_position);
|
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 */
|
/* Sets the threshold for peak comparison */
|
||||||
LIBLTE_API void sync_set_threshold(sync_t *q,
|
LIBLTE_API void sync_set_threshold(sync_t *q,
|
||||||
float find_threshold,
|
float threshold);
|
||||||
float track_threshold);
|
|
||||||
|
|
||||||
/* Set peak comparison to absolute value */
|
/* Gets the subframe idx (0 or 5) */
|
||||||
LIBLTE_API void sync_pss_det_absolute(sync_t *q);
|
LIBLTE_API uint32_t sync_get_sf_idx(sync_t *q);
|
||||||
|
|
||||||
/* Set peak comparison to relative to the mean */
|
/* Gets the last peak value */
|
||||||
LIBLTE_API void sync_pss_det_peak_to_avg(sync_t *q);
|
LIBLTE_API float sync_get_last_peak_value(sync_t *q);
|
||||||
|
|
||||||
/* Gets the slot id (0 or 10) */
|
/* Gets the mean peak value */
|
||||||
LIBLTE_API uint32_t sync_get_slot_id(sync_t *q);
|
|
||||||
|
|
||||||
/* Gets the last peak-to-average ratio */
|
|
||||||
LIBLTE_API float sync_get_peak_value(sync_t *q);
|
LIBLTE_API float sync_get_peak_value(sync_t *q);
|
||||||
|
|
||||||
/* Gets the N_id_2 from the last call to synch_run() */
|
/* Gets the last input signal energy estimation value */
|
||||||
LIBLTE_API uint32_t sync_get_N_id_2(sync_t *q);
|
LIBLTE_API float sync_get_input_energy(sync_t *q);
|
||||||
|
|
||||||
/* Gets the N_id_1 from the last call to synch_run() */
|
/* Sets the N_id_2 to search for */
|
||||||
LIBLTE_API uint32_t sync_get_N_id_1(sync_t *q);
|
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() */
|
/* Gets the Physical CellId from the last call to synch_run() */
|
||||||
LIBLTE_API int sync_get_cell_id(sync_t *q);
|
LIBLTE_API int sync_get_cell_id(sync_t *q);
|
||||||
|
@ -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() */
|
/* Gets the CP length estimation from the last call to synch_run() */
|
||||||
LIBLTE_API lte_cp_t sync_get_cp(sync_t *q);
|
LIBLTE_API lte_cp_t sync_get_cp(sync_t *q);
|
||||||
|
|
||||||
|
/* Enables/Disables energy normalization every frame. If disabled, uses the mean */
|
||||||
|
LIBLTE_API void sync_normalize_en(sync_t *q,
|
||||||
|
bool enable);
|
||||||
|
|
||||||
/* Enables/Disables SSS detection */
|
/* Enables/Disables SSS detection */
|
||||||
LIBLTE_API void sync_sss_en(sync_t *q,
|
LIBLTE_API void sync_sss_en(sync_t *q,
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
|
@ -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 <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/sync/sync.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
#include "liblte/phy/ch_estimation/chest.h"
|
||||||
|
#include "liblte/phy/phch/pbch.h"
|
||||||
|
#include "liblte/phy/common/fft.h"
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
*
|
||||||
|
* This object 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_
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#define NOF_HARQ_PROCESSES 8
|
#define NOF_HARQ_PROCESSES 8
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
typedef struct LIBLTE_API {
|
||||||
|
pbch_t pbch;
|
||||||
pcfich_t pcfich;
|
pcfich_t pcfich;
|
||||||
pdcch_t pdcch;
|
pdcch_t pdcch;
|
||||||
pdsch_t pdsch;
|
pdsch_t pdsch;
|
||||||
|
@ -74,6 +75,9 @@ typedef struct LIBLTE_API {
|
||||||
uint64_t pkts_total;
|
uint64_t pkts_total;
|
||||||
uint64_t nof_trials;
|
uint64_t nof_trials;
|
||||||
|
|
||||||
|
uint32_t sfn;
|
||||||
|
bool pbch_decoded;
|
||||||
|
|
||||||
uint16_t user_rnti;
|
uint16_t user_rnti;
|
||||||
}ue_dl_t;
|
}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 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,
|
cf_t *sf_buffer,
|
||||||
char *data,
|
char *data,
|
||||||
uint32_t sf_idx,
|
uint32_t sf_idx,
|
||||||
uint32_t sfn,
|
|
||||||
uint16_t rnti);
|
uint16_t rnti);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -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 <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/config.h"
|
||||||
|
#include "liblte/phy/sync/sync.h"
|
||||||
|
#include "liblte/phy/sync/cfo.h"
|
||||||
|
#include "liblte/phy/ch_estimation/chest.h"
|
||||||
|
#include "liblte/phy/phch/pbch.h"
|
||||||
|
#include "liblte/phy/common/fft.h"
|
||||||
|
|
||||||
|
#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_
|
||||||
|
|
|
@ -36,57 +36,36 @@
|
||||||
#include "liblte/phy/ch_estimation/chest.h"
|
#include "liblte/phy/ch_estimation/chest.h"
|
||||||
#include "liblte/phy/phch/pbch.h"
|
#include "liblte/phy/phch/pbch.h"
|
||||||
#include "liblte/phy/common/fft.h"
|
#include "liblte/phy/common/fft.h"
|
||||||
#include "liblte/phy/agc/agc.h"
|
|
||||||
|
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
*
|
*
|
||||||
* This object automatically manages the cell association and
|
* This object automatically manages the cell synchronization procedure.
|
||||||
* synchronization procedure. By default, it associates with the
|
|
||||||
* CELL whose correlation peak to average ratio is the highest.
|
|
||||||
*
|
|
||||||
* TODO: Associate with arbitrary CELL ID
|
|
||||||
*
|
*
|
||||||
* The main function is ue_sync_get_buffer(), which returns a pointer
|
* The main function is ue_sync_get_buffer(), which returns a pointer
|
||||||
* to the aligned subframe of samples (before FFT). This function
|
* to the aligned subframe of samples (before FFT). This function
|
||||||
* should be called regularly, returning every 1 ms. It reads from the
|
* should be called regularly, returning every 1 ms. It reads from the
|
||||||
* USRP, aligns the samples to the subframe and performs time/freq synch.
|
* 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
|
* The function returns 1 when the signal is correctly acquired and the
|
||||||
* returned buffer is aligned with the subframe.
|
* returned buffer is aligned with the subframe.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
||||||
typedef enum LIBLTE_API { SF_AGC, SF_FIND, SF_TRACK} ue_sync_state_t;
|
typedef enum LIBLTE_API { SF_FIND, SF_TRACK} ue_sync_state_t;
|
||||||
|
|
||||||
#define SYNC_PBCH_NOF_PRB 6
|
|
||||||
#define SYNC_PBCH_NOF_PORTS 2
|
|
||||||
|
|
||||||
#define TRACK_MAX_LOST 10
|
#define TRACK_MAX_LOST 10
|
||||||
|
|
||||||
#define DEFAULT_NOF_MIB_DECODES 10
|
|
||||||
|
|
||||||
#define AGC_NOF_FRAMES 100
|
|
||||||
|
|
||||||
#define MEASURE_EXEC_TIME
|
#define MEASURE_EXEC_TIME
|
||||||
|
|
||||||
typedef struct LIBLTE_API {
|
typedef struct LIBLTE_API {
|
||||||
sync_t s;
|
sync_t sfind;
|
||||||
|
sync_t strack;
|
||||||
|
|
||||||
void *stream;
|
void *stream;
|
||||||
double (*set_rate_callback)(void*, double);
|
|
||||||
int (*recv_callback)(void*, void*, uint32_t);
|
int (*recv_callback)(void*, void*, uint32_t);
|
||||||
|
|
||||||
ue_sync_state_t state;
|
ue_sync_state_t state;
|
||||||
|
|
||||||
cf_t *input_buffer;
|
cf_t *input_buffer;
|
||||||
cf_t *receive_buffer;
|
|
||||||
cf_t *sf_symbols;
|
|
||||||
cf_t *ce[SYNC_PBCH_NOF_PORTS];
|
|
||||||
|
|
||||||
/* These count half frames (5ms) */
|
/* These count half frames (5ms) */
|
||||||
uint64_t frame_ok_cnt;
|
uint64_t frame_ok_cnt;
|
||||||
|
@ -101,21 +80,7 @@ typedef struct LIBLTE_API {
|
||||||
|
|
||||||
cfo_t cfocorr;
|
cfo_t cfocorr;
|
||||||
float cur_cfo;
|
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;
|
bool decode_sss_on_track;
|
||||||
|
|
||||||
uint32_t peak_idx;
|
uint32_t peak_idx;
|
||||||
|
@ -128,7 +93,7 @@ typedef struct LIBLTE_API {
|
||||||
|
|
||||||
|
|
||||||
LIBLTE_API int ue_sync_init(ue_sync_t *q,
|
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),
|
int (recv_callback)(void*, void*, uint32_t),
|
||||||
void *stream_handler);
|
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,
|
LIBLTE_API int ue_sync_get_buffer(ue_sync_t *q,
|
||||||
cf_t **sf_symbols);
|
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_reset(ue_sync_t *q);
|
||||||
|
|
||||||
LIBLTE_API void ue_sync_decode_sss_on_track(ue_sync_t *q,
|
LIBLTE_API void ue_sync_decode_sss_on_track(ue_sync_t *q,
|
||||||
bool enabled);
|
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 ue_sync_state_t ue_sync_get_state(ue_sync_t *q);
|
||||||
|
|
||||||
LIBLTE_API uint32_t ue_sync_get_sfidx(ue_sync_t *q);
|
LIBLTE_API uint32_t ue_sync_get_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_cfo(ue_sync_t *q);
|
||||||
|
|
||||||
LIBLTE_API float ue_sync_get_sfo(ue_sync_t *q);
|
LIBLTE_API float ue_sync_get_sfo(ue_sync_t *q);
|
|
@ -35,6 +35,9 @@
|
||||||
|
|
||||||
typedef _Complex float cf_t;
|
typedef _Complex float cf_t;
|
||||||
|
|
||||||
|
#define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1))
|
||||||
|
|
||||||
|
|
||||||
/** Return the sum of all the elements */
|
/** Return the sum of all the elements */
|
||||||
LIBLTE_API int vec_acc_ii(int *x, uint32_t len);
|
LIBLTE_API int vec_acc_ii(int *x, uint32_t len);
|
||||||
LIBLTE_API float vec_acc_ff(float *x, uint32_t len);
|
LIBLTE_API float vec_acc_ff(float *x, uint32_t len);
|
||||||
|
@ -69,6 +72,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_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_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) */
|
/* vector product (element-wise) */
|
||||||
LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
|
LIBLTE_API void vec_prod_ccc(cf_t *x, cf_t *y, cf_t *z, uint32_t len);
|
||||||
|
@ -95,6 +99,7 @@ LIBLTE_API float vec_avg_power_cf(cf_t *x, uint32_t len);
|
||||||
|
|
||||||
/* return the index of the maximum value in the vector */
|
/* return the index of the maximum value in the vector */
|
||||||
LIBLTE_API uint32_t vec_max_fi(float *x, uint32_t len);
|
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 */
|
/* 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);
|
LIBLTE_API void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len);
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
|
#define SLOT_SZ(q) (q->nof_symbols * q->symbol_sz)
|
||||||
#define SF_SZ(q) (2 * SLOT_SZ(q))
|
#define SF_SZ(q) (2 * SLOT_SZ(q))
|
||||||
|
|
||||||
//#define VOLK_INTERP
|
#define VOLK_INTERP
|
||||||
|
|
||||||
void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
void chest_fprint(chest_t *q, FILE *stream, uint32_t nslot, uint32_t port_id) {
|
||||||
chest_ref_fprint(q, stream, nslot, port_id);
|
chest_ref_fprint(q, stream, nslot, port_id);
|
||||||
|
@ -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];
|
channel_ref = input[tidx * q->nof_re + fidx];
|
||||||
q->refsignal[port_id][nslot].refs[nref].recv_simbol = channel_ref;
|
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,
|
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)),
|
10*log10f(cabsf(channel_ref/known_ref)),
|
||||||
cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,
|
cargf(channel_ref)/M_PI,cargf(known_ref)/M_PI,
|
||||||
cargf(channel_ref/known_ref)/M_PI);
|
cargf(channel_ref/known_ref)/M_PI);
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: compare with threshold */
|
/* FIXME: compare with threshold */
|
||||||
if (channel_ref != 0) {
|
if (channel_ref != 0) {
|
||||||
q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref;
|
q->refsignal[port_id][nslot].ch_est[nref] = channel_ref/known_ref;
|
||||||
} else {
|
} else {
|
||||||
q->refsignal[port_id][nslot].ch_est[nref] = 0;
|
q->refsignal[port_id][nslot].ch_est[nref] = 1e-6;
|
||||||
}
|
}
|
||||||
ret = LIBLTE_SUCCESS;
|
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) {
|
if (q->refsignal[port_id][nslot].nsymbols <= 2) {
|
||||||
refsignal_t *r = &q->refsignal[port_id][nslot];
|
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);
|
nslot, port_id, r->nof_refs);
|
||||||
|
|
||||||
for (i=0;i<r->nof_refs;i++) {
|
for (i=0;i<r->nof_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;j<MAX_NSYMB;j++) {
|
for (j=0;j<MAX_NSYMB;j++) {
|
||||||
y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
|
y[j] = ce[r->symbols_ref[0] * q->nof_re + i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (j=0;j<q->nof_symbols;j++) {
|
for (j=0;j<q->nof_symbols;j++) {
|
||||||
ce[j * q->nof_re + i] = y[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);
|
ret = refsignal_init_LTEDL(&q->refsignal[port_id][nslot], port_id, nslot, cell);
|
||||||
|
|
||||||
|
#ifdef VOLK_INTERP
|
||||||
if (ret == LIBLTE_SUCCESS) {
|
if (ret == LIBLTE_SUCCESS) {
|
||||||
if (nslot == 0) {
|
if (nslot == 0) {
|
||||||
ret = interp_init(&q->interp_freq[port_id], LINEAR, q->refsignal[port_id][nslot].nof_refs/2, RE_X_RB/2);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -318,6 +322,12 @@ void chest_free(chest_t *q) {
|
||||||
refsignal_free(&q->refsignal[p][n]);
|
refsignal_free(&q->refsignal[p][n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef VOLK_INTERP
|
||||||
|
for (p=0;p<MAX_PORTS;p++) {
|
||||||
|
interp_free(&q->interp_freq[p]);
|
||||||
|
interp_free(&q->interp_time[p]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bzero(q, sizeof(chest_t));
|
bzero(q, sizeof(chest_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,25 +29,31 @@
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "gauss.h"
|
#include "gauss.h"
|
||||||
#include "liblte/phy/channel/ch_awgn.h"
|
#include "liblte/phy/channel/ch_awgn.h"
|
||||||
|
|
||||||
void ch_awgn_c(const cf_t* x, cf_t* y, float variance, int buff_sz) {
|
float ch_awgn_get_variance(float ebno_db, float rate) {
|
||||||
_Complex float tmp;
|
float esno_db = ebno_db + 10 * log10f(rate);
|
||||||
int i;
|
return sqrtf(1 / (powf(10, esno_db / 10)));
|
||||||
|
}
|
||||||
|
|
||||||
for (i=0;i<buff_sz;i++) {
|
void ch_awgn_c(const cf_t* x, cf_t* y, float variance, uint32_t len) {
|
||||||
|
cf_t tmp;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i=0;i<len;i++) {
|
||||||
__real__ tmp = rand_gauss();
|
__real__ tmp = rand_gauss();
|
||||||
__imag__ tmp = rand_gauss();
|
__imag__ tmp = rand_gauss();
|
||||||
tmp *= variance;
|
tmp *= variance;
|
||||||
y[i] = tmp + x[i];
|
y[i] = tmp + x[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ch_awgn_f(const float* x, float* y, float variance, int buff_sz) {
|
void ch_awgn_f(const float* x, float* y, float variance, uint32_t len) {
|
||||||
int i;
|
uint32_t i;
|
||||||
|
|
||||||
for (i=0;i<buff_sz;i++) {
|
for (i=0;i<len;i++) {
|
||||||
y[i] = x[i] + variance * rand_gauss();
|
y[i] = x[i] + variance * rand_gauss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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->cp = cp;
|
||||||
q->nof_re = nof_prb * RE_X_RB;
|
q->nof_re = nof_prb * RE_X_RB;
|
||||||
q->nof_guards = ((symbol_sz - q->nof_re) / 2);
|
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",
|
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,
|
dir==FORWARD?"FFT":"iFFT", q->symbol_sz, q->nof_symbols,
|
||||||
|
|
|
@ -87,7 +87,7 @@ int main(int argc, char **argv) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(-1);
|
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) {
|
if (!outfft) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "liblte/phy/fec/turbodecoder.h"
|
#include "liblte/phy/fec/turbodecoder.h"
|
||||||
|
|
||||||
|
@ -39,7 +40,9 @@
|
||||||
* Decoder
|
* 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 m_b[8], new[8], old[8];
|
||||||
llr_t x, y, xy;
|
llr_t x, y, xy;
|
||||||
int k;
|
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,
|
void map_gen_alpha(map_gen_t * s, llr_t * input, llr_t * parity, llr_t * output,
|
||||||
uint32_t long_cb) {
|
uint32_t long_cb)
|
||||||
|
{
|
||||||
llr_t m_b[8], new[8], old[8], max1[8], max0[8];
|
llr_t m_b[8], new[8], old[8], max1[8], max0[8];
|
||||||
llr_t m1, m0;
|
llr_t m1, m0;
|
||||||
llr_t x, y, xy;
|
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));
|
bzero(h, sizeof(map_gen_t));
|
||||||
h->beta = malloc(sizeof(llr_t) * (max_long_cb + TOTALTAIL + 1) * NUMSTATES);
|
h->beta = malloc(sizeof(llr_t) * (max_long_cb + TOTALTAIL + 1) * NUMSTATES);
|
||||||
if (!h->beta) {
|
if (!h->beta) {
|
||||||
|
@ -161,15 +166,17 @@ int map_gen_init(map_gen_t *h, int max_long_cb) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_gen_free(map_gen_t *h) {
|
void map_gen_free(map_gen_t * h)
|
||||||
|
{
|
||||||
if (h->beta) {
|
if (h->beta) {
|
||||||
free(h->beta);
|
free(h->beta);
|
||||||
}
|
}
|
||||||
bzero(h, sizeof(map_gen_t));
|
bzero(h, sizeof(map_gen_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_gen_dec(map_gen_t *h, llr_t *input, llr_t *parity, llr_t *output,
|
void map_gen_dec(map_gen_t * h, llr_t * input, llr_t * parity, llr_t * output,
|
||||||
uint32_t long_cb) {
|
uint32_t long_cb)
|
||||||
|
{
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
|
|
||||||
h->beta[(long_cb + TAIL) * NUMSTATES] = 0;
|
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
|
* 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;
|
int ret = -1;
|
||||||
bzero(h, sizeof(tdec_t));
|
bzero(h, sizeof(tdec_t));
|
||||||
uint32_t len = max_long_cb + TOTALTAIL;
|
uint32_t len = max_long_cb + TOTALTAIL;
|
||||||
|
@ -227,13 +235,14 @@ int tdec_init(tdec_t *h, uint32_t max_long_cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
clean_and_exit: if (ret == -1) {
|
clean_and_exit:if (ret == -1) {
|
||||||
tdec_free(h);
|
tdec_free(h);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tdec_free(tdec_t *h) {
|
void tdec_free(tdec_t * h)
|
||||||
|
{
|
||||||
if (h->llr1) {
|
if (h->llr1) {
|
||||||
free(h->llr1);
|
free(h->llr1);
|
||||||
}
|
}
|
||||||
|
@ -257,7 +266,8 @@ void tdec_free(tdec_t *h) {
|
||||||
bzero(h, sizeof(tdec_t));
|
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;
|
uint32_t i;
|
||||||
|
|
||||||
// Prepare systematic and parity bits for MAP DEC #1
|
// 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
|
// Prepare systematic and parity bits for MAP DEC #1
|
||||||
for (i = 0; i < long_cb; i++) {
|
for (i = 0; i < long_cb; i++) {
|
||||||
h->syst[i] = h->llr1[h->interleaver.forward[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];
|
h->parity[i] = input[RATE * i + 2];
|
||||||
}
|
}
|
||||||
for (i = long_cb; i < long_cb + RATE; i++) {
|
for (i = long_cb; i < long_cb + RATE; i++) {
|
||||||
h->syst[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
|
h->parity[i] = input[RATE * long_cb + NINPUTS * RATE
|
||||||
+ NINPUTS * (i - long_cb) + 1];
|
+ NINPUTS * (i - long_cb) + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run MAP DEC #1
|
// Run MAP DEC #1
|
||||||
map_gen_dec(&h->dec, h->syst, h->parity, h->llr2, long_cb);
|
map_gen_dec(&h->dec, h->syst, h->parity, h->llr2, long_cb);
|
||||||
|
|
||||||
// Update a-priori LLR from the last iteration
|
// Update a-priori LLR from the last iteration
|
||||||
for (i = 0; i < long_cb; i++) {
|
for (i = 0; i < long_cb; i++) {
|
||||||
h->w[i] += h->llr2[h->interleaver.reverse[i]] - h->llr1[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) {
|
int tdec_reset(tdec_t * h, uint32_t long_cb)
|
||||||
memset(h->w, 0, sizeof(llr_t) * long_cb);
|
{
|
||||||
if (long_cb > h->max_long_cb) {
|
if (long_cb > h->max_long_cb) {
|
||||||
fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n",
|
fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n",
|
||||||
h->max_long_cb);
|
h->max_long_cb);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
memset(h->w, 0, sizeof(llr_t) * long_cb);
|
||||||
return tc_interl_LTE_gen(&h->interleaver, 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;
|
uint32_t i;
|
||||||
for (i = 0; i < long_cb; 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,
|
void tdec_run_all(tdec_t * h, llr_t * input, char *output,
|
||||||
uint32_t long_cb) {
|
uint32_t nof_iterations, uint32_t long_cb)
|
||||||
|
{
|
||||||
uint32_t iter = 0;
|
uint32_t iter = 0;
|
||||||
|
|
||||||
tdec_reset(h, long_cb);
|
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);
|
tdec_decision(h, output, long_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,11 @@ int precoding_type(cf_t *x[MAX_LAYERS], cf_t *y[MAX_PORTS], int nof_layers,
|
||||||
|
|
||||||
/* ZF detector */
|
/* ZF detector */
|
||||||
int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols) {
|
int predecoding_single_zf(cf_t *y, cf_t *ce, cf_t *x, int nof_symbols) {
|
||||||
|
for (int i=0;i<nof_symbols;i++) {
|
||||||
|
if (ce[i] == 0) {
|
||||||
|
ce[i] = 0.01;
|
||||||
|
}
|
||||||
|
}
|
||||||
vec_div_ccc(y, ce, x, nof_symbols);
|
vec_div_ccc(y, ce, x, nof_symbols);
|
||||||
return nof_symbols;
|
return nof_symbols;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +148,9 @@ int predecoding_diversity_zf(cf_t *y, cf_t *ce[MAX_PORTS], cf_t *x[MAX_LAYERS],
|
||||||
+ crealf(h1) * crealf(h1) + cimagf(h1) * cimagf(h1);
|
+ crealf(h1) * crealf(h1) + cimagf(h1) * cimagf(h1);
|
||||||
r0 = y[2 * i];
|
r0 = y[2 * i];
|
||||||
r1 = y[2 * i + 1];
|
r1 = y[2 * i + 1];
|
||||||
|
if (hh == 0) {
|
||||||
|
hh = 1e-2;
|
||||||
|
}
|
||||||
x[0][i] = (conjf(h0) * r0 + h1 * conjf(r1)) / hh * sqrt(2);
|
x[0][i] = (conjf(h0) * r0 + h1 * conjf(r1)) / hh * sqrt(2);
|
||||||
x[1][i] = (-h1 * conj(r0) + conj(h0) * r1) / hh * sqrt(2);
|
x[1][i] = (-h1 * conj(r0) + conj(h0) * r1) / hh * sqrt(2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
void demod_soft_init(demod_soft_t *q) {
|
void demod_soft_init(demod_soft_t *q) {
|
||||||
bzero((void*)q,sizeof(demod_soft_t));
|
bzero((void*)q,sizeof(demod_soft_t));
|
||||||
|
q->sigma = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void demod_soft_table_set(demod_soft_t *q, modem_table_t *table) {
|
void demod_soft_table_set(demod_soft_t *q, modem_table_t *table) {
|
||||||
|
|
|
@ -34,6 +34,54 @@
|
||||||
|
|
||||||
#include "soft_algs.h"
|
#include "soft_algs.h"
|
||||||
|
|
||||||
|
#define LLR_APPROX_USE_VOLK
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LLR_APPROX_USE_VOLK
|
||||||
|
void
|
||||||
|
llr_approx(const _Complex float *in, float *out, int N, int M, int B,
|
||||||
|
_Complex float *symbols, uint32_t(*S)[6][32], float sigma2)
|
||||||
|
{
|
||||||
|
int i, s, b;
|
||||||
|
float num, den;
|
||||||
|
int change_sign = -1;
|
||||||
|
float x, y, d[64];
|
||||||
|
|
||||||
|
for (s = 0; s < N; s++) { /* recevied symbols */
|
||||||
|
/* Compute the distances squared d[i] between the received symbol and all constellation points */
|
||||||
|
for (i = 0; i < M; i++) {
|
||||||
|
x = __real__ in[s] - __real__ symbols[i];
|
||||||
|
y = __imag__ in[s] - __imag__ symbols[i];
|
||||||
|
d[i] = x * x + y * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (b = 0; b < B; b++) { /* bits per symbol */
|
||||||
|
/* initiate num[b] and den[b] */
|
||||||
|
num = d[S[0][b][0]];
|
||||||
|
den = d[S[1][b][0]];
|
||||||
|
|
||||||
|
/* Minimum distance squared search between recevied symbol and a constellation point with a
|
||||||
|
'1' and a '0' for each bit position */
|
||||||
|
for (i = 1; i < M / 2; i++) { /* half the constellation points have '1'|'0' at any given bit position */
|
||||||
|
if (d[S[0][b][i]] < num) {
|
||||||
|
num = d[S[0][b][i]];
|
||||||
|
}
|
||||||
|
if (d[S[1][b][i]] < den) {
|
||||||
|
den = d[S[1][b][i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Theoretical LLR and approximate LLR values are positive if
|
||||||
|
* symbol(s) with '0' is/are closer and negative if symbol(s)
|
||||||
|
* with '1' are closer.
|
||||||
|
* Change sign if mapping negative to '0' and positive to '1' */
|
||||||
|
out[s * B + b] = change_sign * (den - num) / sigma2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Soft Modulation Demapping based on the approximate
|
* @ingroup Soft Modulation Demapping based on the approximate
|
||||||
* log-likelihood algorithm
|
* log-likelihood algorithm
|
||||||
|
@ -50,46 +98,50 @@
|
||||||
* \param S Soft demapping auxiliary matrix
|
* \param S Soft demapping auxiliary matrix
|
||||||
* \param sigma2 Noise vatiance
|
* \param sigma2 Noise vatiance
|
||||||
*/
|
*/
|
||||||
void llr_approx(const _Complex float *in, float *out, int N, int M, int B,
|
void
|
||||||
_Complex float *symbols, uint32_t (*S)[6][32], float sigma2) {
|
llr_approx(const _Complex float *in, float *out, int N, int M, int B,
|
||||||
|
_Complex float *symbols, uint32_t(*S)[6][32], float sigma2)
|
||||||
|
{
|
||||||
int i, s, b;
|
int i, s, b;
|
||||||
float num, den;
|
float num, den;
|
||||||
int change_sign = -1;
|
int change_sign = -1;
|
||||||
float x, y, d[64];
|
float x, y, d[64];
|
||||||
|
|
||||||
for (s=0; s<N; s++) { /* recevied symbols */
|
for (s = 0; s < N; s++) { /* recevied symbols */
|
||||||
/* Compute the distances squared d[i] between the received symbol and all constellation points */
|
/* Compute the distances squared d[i] between the received symbol and all constellation points */
|
||||||
for (i=0; i<M; i++) {
|
for (i = 0; i < M; i++) {
|
||||||
x = __real__ in[s] - __real__ symbols[i];
|
x = __real__ in[s] - __real__ symbols[i];
|
||||||
y = __imag__ in[s] - __imag__ symbols[i];
|
y = __imag__ in[s] - __imag__ symbols[i];
|
||||||
d[i] = x*x + y*y;
|
d[i] = x * x + y * y;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (b=0; b<B; b++) {/* bits per symbol*/
|
for (b = 0; b < B; b++) { /* bits per symbol */
|
||||||
/* initiate num[b] and den[b] */
|
/* initiate num[b] and den[b] */
|
||||||
num = d[S[0][b][0]];
|
num = d[S[0][b][0]];
|
||||||
den = d[S[1][b][0]];
|
den = d[S[1][b][0]];
|
||||||
|
|
||||||
/* Minimum distance squared search between recevied symbol and a constellation point with a
|
/* Minimum distance squared search between recevied symbol and a constellation point with a
|
||||||
'1' and a '0' for each bit position */
|
'1' and a '0' for each bit position */
|
||||||
for (i=1; i<M/2; i++) { /* half the constellation points have '1'|'0' at any given bit position */
|
for (i = 1; i < M / 2; i++) { /* half the constellation points have '1'|'0' at any given bit position */
|
||||||
if (d[S[0][b][i]] < num) {
|
if (d[S[0][b][i]] < num) {
|
||||||
num = d[S[0][b][i]];
|
num = d[S[0][b][i]];
|
||||||
}
|
}
|
||||||
if (d[S[1][b][i]] < den) {
|
if (d[S[1][b][i]] < den) {
|
||||||
den = d[S[1][b][i]];
|
den = d[S[1][b][i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Theoretical LLR and approximate LLR values are positive if
|
/* Theoretical LLR and approximate LLR values are positive if
|
||||||
* symbol(s) with '0' is/are closer and negative if symbol(s)
|
* symbol(s) with '0' is/are closer and negative if symbol(s)
|
||||||
* with '1' are closer.
|
* with '1' are closer.
|
||||||
* Change sign if mapping negative to '0' and positive to '1' */
|
* Change sign if mapping negative to '0' and positive to '1' */
|
||||||
out[s*B+b] = change_sign*(den-num)/sigma2;
|
out[s * B + b] = change_sign * (den - num) / sigma2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Soft Modulation Demapping based on the approximate
|
* @ingroup Soft Modulation Demapping based on the approximate
|
||||||
* log-likelihood ratio algorithm
|
* log-likelihood ratio algorithm
|
||||||
|
@ -107,35 +159,36 @@ void llr_approx(const _Complex float *in, float *out, int N, int M, int B,
|
||||||
* \param sigma2 Noise vatiance
|
* \param sigma2 Noise vatiance
|
||||||
*/
|
*/
|
||||||
void llr_exact(const _Complex float *in, float *out, int N, int M, int B,
|
void llr_exact(const _Complex float *in, float *out, int N, int M, int B,
|
||||||
_Complex float *symbols, uint32_t (*S)[6][32], float sigma2) {
|
_Complex float *symbols, uint32_t(*S)[6][32], float sigma2)
|
||||||
|
{
|
||||||
int i, s, b;
|
int i, s, b;
|
||||||
float num, den;
|
float num, den;
|
||||||
int change_sign = -1;
|
int change_sign = -1;
|
||||||
float x, y, d[64];
|
float x, y, d[64];
|
||||||
|
|
||||||
for (s=0; s<N; s++) { /* recevied symbols */
|
for (s = 0; s < N; s++) { /* recevied symbols */
|
||||||
/* Compute exp{·} of the distances squared d[i] between the received symbol and all constellation points */
|
/* Compute exp{·} of the distances squared d[i] between the received symbol and all constellation points */
|
||||||
for (i=0; i<M; i++) {
|
for (i = 0; i < M; i++) {
|
||||||
x = __real__ in[s] - __real__ symbols[i];
|
x = __real__ in[s] - __real__ symbols[i];
|
||||||
y = __imag__ in[s] - __imag__ symbols[i];
|
y = __imag__ in[s] - __imag__ symbols[i];
|
||||||
d[i] = exp(-1*(x*x + y*y)/sigma2);
|
d[i] = exp(-1 * (x * x + y * y) / sigma2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sum up the corresponding d[i]'s for each bit position */
|
/* Sum up the corresponding d[i]'s for each bit position */
|
||||||
for (b=0; b<B; b++) {/* bits per symbol*/
|
for (b = 0; b < B; b++) { /* bits per symbol */
|
||||||
/* initiate num[b] and den[b] */
|
/* initiate num[b] and den[b] */
|
||||||
num = 0;
|
num = 0;
|
||||||
den = 0;
|
den = 0;
|
||||||
|
|
||||||
for (i=0; i<M/2; i++) { /* half the constellation points have '1'|'0' at any given bit position */
|
for (i = 0; i < M / 2; i++) { /* half the constellation points have '1'|'0' at any given bit position */
|
||||||
num += d[S[0][b][i]];
|
num += d[S[0][b][i]];
|
||||||
den += d[S[1][b][i]];
|
den += d[S[1][b][i]];
|
||||||
}
|
}
|
||||||
/* Theoretical LLR and approximate LLR values are positive if
|
/* Theoretical LLR and approximate LLR values are positive if
|
||||||
* symbol(s) with '0' is/are closer and negative if symbol(s)
|
* symbol(s) with '0' is/are closer and negative if symbol(s)
|
||||||
* with '1' are closer.
|
* with '1' are closer.
|
||||||
* Change sign if mapping negative to '0' and positive to '1' */
|
* Change sign if mapping negative to '0' and positive to '1' */
|
||||||
out[s*B+b] = change_sign*log(num/den);
|
out[s * B + b] = change_sign * log(num / den);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,13 @@ ADD_TEST(modem_qpsk_soft modem_test -n 1020 -m 2 -s)
|
||||||
ADD_TEST(modem_qam16_soft modem_test -n 1020 -m 4 -s)
|
ADD_TEST(modem_qam16_soft modem_test -n 1020 -m 4 -s)
|
||||||
ADD_TEST(modem_qam64_soft modem_test -n 1020 -m 6 -s)
|
ADD_TEST(modem_qam64_soft modem_test -n 1020 -m 6 -s)
|
||||||
|
|
||||||
|
ADD_EXECUTABLE(soft_demod_test soft_demod_test.c)
|
||||||
|
TARGET_LINK_LIBRARIES(soft_demod_test lte_phy)
|
||||||
|
|
||||||
|
ADD_TEST(modem_bpsk_soft_approx soft_demod_test -n 1020 -m 1)
|
||||||
|
ADD_TEST(modem_qpsk_soft_approx soft_demod_test -n 1020 -m 2)
|
||||||
|
ADD_TEST(modem_qam16_soft_approx soft_demod_test -n 1020 -m 4)
|
||||||
|
ADD_TEST(modem_qam64_soft_approx soft_demod_test -n 1020 -m 6)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2014 The libLTE Developers. See the
|
||||||
|
* COPYRIGHT file at the top-level directory of this distribution.
|
||||||
|
*
|
||||||
|
* \section LICENSE
|
||||||
|
*
|
||||||
|
* This file is part of the libLTE library.
|
||||||
|
*
|
||||||
|
* libLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* libLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Lesser General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
|
int nof_frames = 10;
|
||||||
|
int num_bits = 1000;
|
||||||
|
lte_mod_t modulation = 0;
|
||||||
|
|
||||||
|
void usage(char *prog) {
|
||||||
|
printf("Usage: %s [nfv] -m modulation (1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", prog);
|
||||||
|
printf("\t-n num_bits [Default %d]\n", num_bits);
|
||||||
|
printf("\t-f nof_frames [Default %d]\n", nof_frames);
|
||||||
|
printf("\t-v verbose [Default None]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "nmvf")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'n':
|
||||||
|
num_bits = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
nof_frames = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
switch(atoi(argv[optind])) {
|
||||||
|
case 1:
|
||||||
|
modulation = LTE_BPSK;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
modulation = LTE_QPSK;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
modulation = LTE_QAM16;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
modulation = LTE_QAM64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Invalid modulation %d. Possible values: "
|
||||||
|
"(1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", atoi(argv[optind]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modulation == 0) {
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float mse_threshold() {
|
||||||
|
switch(modulation) {
|
||||||
|
case LTE_BPSK:
|
||||||
|
return 1.0e-6;
|
||||||
|
case LTE_QPSK:
|
||||||
|
return 1.0e-6;
|
||||||
|
case LTE_QAM16:
|
||||||
|
return 0.11;
|
||||||
|
case LTE_QAM64:
|
||||||
|
return 0.18;
|
||||||
|
default:
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int i;
|
||||||
|
modem_table_t mod;
|
||||||
|
demod_soft_t demod_soft;
|
||||||
|
char *input, *output;
|
||||||
|
cf_t *symbols;
|
||||||
|
float *llr_exact, *llr_approx;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
/* initialize objects */
|
||||||
|
if (modem_table_lte(&mod, modulation, true)) {
|
||||||
|
fprintf(stderr, "Error initializing modem table\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that num_bits is multiple of num_bits x symbol */
|
||||||
|
num_bits = mod.nbits_x_symbol * (num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
demod_soft_init(&demod_soft);
|
||||||
|
demod_soft_table_set(&demod_soft, &mod);
|
||||||
|
demod_soft_sigma_set(&demod_soft, 2.0 / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
/* allocate buffers */
|
||||||
|
input = malloc(sizeof(char) * num_bits);
|
||||||
|
if (!input) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
output = malloc(sizeof(char) * num_bits);
|
||||||
|
if (!output) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
symbols = malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol);
|
||||||
|
if (!symbols) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
llr_exact = malloc(sizeof(float) * num_bits);
|
||||||
|
if (!llr_exact) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
llr_approx = malloc(sizeof(float) * num_bits);
|
||||||
|
if (!llr_approx) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generate random data */
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
double mse;
|
||||||
|
struct timeval t[3];
|
||||||
|
float mean_texec = 0.0;
|
||||||
|
for (int n=0;n<nof_frames;n++) {
|
||||||
|
for (i=0;i<num_bits;i++) {
|
||||||
|
input[i] = rand()%2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* modulate */
|
||||||
|
mod_modulate(&mod, input, symbols, num_bits);
|
||||||
|
|
||||||
|
/* add noise */
|
||||||
|
ch_awgn_c(symbols, symbols, ch_awgn_get_variance(5.0, mod.nbits_x_symbol), num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
/* Compare exact with approximation algorithms */
|
||||||
|
demod_soft_alg_set(&demod_soft, EXACT);
|
||||||
|
demod_soft_demodulate(&demod_soft, symbols, llr_exact, num_bits / mod.nbits_x_symbol);
|
||||||
|
|
||||||
|
demod_soft_alg_set(&demod_soft, APPROX);
|
||||||
|
gettimeofday(&t[1], NULL);
|
||||||
|
demod_soft_demodulate(&demod_soft, symbols, llr_approx, num_bits / mod.nbits_x_symbol);
|
||||||
|
gettimeofday(&t[2], NULL);
|
||||||
|
get_time_interval(t);
|
||||||
|
|
||||||
|
/* compute exponentially averaged execution time */
|
||||||
|
mean_texec = EXPAVERAGE((float) t[0].tv_usec, mean_texec, n);
|
||||||
|
|
||||||
|
/* check MSE */
|
||||||
|
mse = 0.0;
|
||||||
|
for (i=0;i<num_bits;i++) {
|
||||||
|
float e = llr_exact[i] - llr_approx[i];
|
||||||
|
mse += e*e;
|
||||||
|
}
|
||||||
|
mse/=num_bits;
|
||||||
|
|
||||||
|
if (VERBOSE_ISDEBUG()) {
|
||||||
|
printf("exact=");
|
||||||
|
vec_fprint_f(stdout, llr_exact, num_bits);
|
||||||
|
|
||||||
|
printf("approx=");
|
||||||
|
vec_fprint_f(stdout, llr_approx, num_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mse > mse_threshold()) {
|
||||||
|
goto clean_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
clean_exit:
|
||||||
|
free(llr_exact);
|
||||||
|
free(llr_approx);
|
||||||
|
free(symbols);
|
||||||
|
free(output);
|
||||||
|
free(input);
|
||||||
|
|
||||||
|
modem_table_free(&mod);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
printf("Ok Mean Throughput: %.2f. Mbps ExTime: %.2f us\n", num_bits/mean_texec, mean_texec);
|
||||||
|
} else {
|
||||||
|
printf("Error: MSE too large (%f > %f)\n", mse, mse_threshold());
|
||||||
|
}
|
||||||
|
exit(ret);
|
||||||
|
}
|
|
@ -453,30 +453,25 @@ int pbch_decode_frame(pbch_t *q, pbch_mib_t *mib, uint32_t src, uint32_t dst, ui
|
||||||
*
|
*
|
||||||
* Returns 1 if successfully decoded MIB, 0 if not and -1 on error
|
* Returns 1 if successfully decoded MIB, 0 if not and -1 on error
|
||||||
*/
|
*/
|
||||||
int pbch_decode(pbch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], pbch_mib_t *mib) {
|
int pbch_decode(pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[MAX_PORTS], pbch_mib_t *mib) {
|
||||||
uint32_t src, dst, nb;
|
uint32_t src, dst, nb;
|
||||||
uint32_t nant_[3] = { 1, 2, 4 };
|
uint32_t nant_[3] = { 1, 2, 4 };
|
||||||
uint32_t na, nant;
|
uint32_t na, nant;
|
||||||
cf_t *slot1_symbols;
|
|
||||||
int i;
|
int i;
|
||||||
int nof_bits;
|
int nof_bits;
|
||||||
cf_t *x[MAX_LAYERS];
|
cf_t *x[MAX_LAYERS];
|
||||||
cf_t *ce_slot[MAX_PORTS];
|
|
||||||
|
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
sf_symbols != NULL &&
|
slot1_symbols != NULL &&
|
||||||
mib != NULL)
|
mib != NULL)
|
||||||
{
|
{
|
||||||
for (i=0;i<q->cell.nof_ports;i++) {
|
for (i=0;i<q->cell.nof_ports;i++) {
|
||||||
if (ce[i] == NULL) {
|
if (ce_slot1[i] == NULL) {
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
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 */
|
/* Set pointers for layermapping & precoding */
|
||||||
nof_bits = 2 * q->nof_symbols;
|
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 */
|
/* extract channel estimates */
|
||||||
for (i = 0; i < q->cell.nof_ports; i++) {
|
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");
|
fprintf(stderr, "There was an error getting the PBCH symbols\n");
|
||||||
return LIBLTE_ERROR;
|
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++) {
|
for (na = 0; na < q->cell.nof_ports && !ret; na++) {
|
||||||
nant = nant_[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 */
|
/* in conctrol channels, only diversity is supported */
|
||||||
if (nant == 1) {
|
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
|
/** 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 i;
|
||||||
int nof_bits;
|
int nof_bits;
|
||||||
cf_t *slot1_symbols[MAX_PORTS];
|
|
||||||
cf_t *x[MAX_LAYERS];
|
cf_t *x[MAX_LAYERS];
|
||||||
|
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
mib != NULL)
|
mib != NULL)
|
||||||
{
|
{
|
||||||
for (i=0;i<q->cell.nof_ports;i++) {
|
for (i=0;i<q->cell.nof_ports;i++) {
|
||||||
if (sf_symbols[i] == NULL) {
|
if (slot1_symbols[i] == NULL) {
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
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 */
|
/* Set pointers for layermapping & precoding */
|
||||||
nof_bits = 2 * q->nof_symbols;
|
nof_bits = 2 * q->nof_symbols;
|
||||||
|
|
|
@ -183,7 +183,7 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
|
||||||
ret = LIBLTE_ERROR;
|
ret = LIBLTE_ERROR;
|
||||||
|
|
||||||
q->cell = cell;
|
q->cell = cell;
|
||||||
|
q->average_nof_iterations_n = 0;
|
||||||
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
|
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
|
||||||
|
|
||||||
INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
|
INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
|
||||||
|
@ -459,6 +459,15 @@ int pdsch_harq_setup(pdsch_harq_t *p, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float pdsch_average_noi(pdsch_t *q) {
|
||||||
|
return q->average_nof_iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pdsch_last_noi(pdsch_t *q) {
|
||||||
|
return q->nof_iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Decode a transport block according to 36.212 5.3.2
|
/* Decode a transport block according to 36.212 5.3.2
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -508,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,
|
DEBUG("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i,
|
||||||
cb_len, rlen - F, wp, rp, F, n_e);
|
cb_len, rlen - F, wp, rp, F, n_e);
|
||||||
|
|
||||||
|
|
||||||
/* Rate Unmatching */
|
/* Rate Unmatching */
|
||||||
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
|
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
|
||||||
&e_bits[rp], n_e,
|
&e_bits[rp], n_e,
|
||||||
|
@ -516,17 +526,43 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
|
||||||
return LIBLTE_ERROR;
|
return LIBLTE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turbo Decoding */
|
/* Turbo Decoding with CRC-based early stopping */
|
||||||
tdec_run_all(&q->decoder, (float*) q->cb_out, q->cb_in, TDEC_ITERATIONS,
|
q->nof_iterations = 0;
|
||||||
cb_len);
|
bool early_stop = false;
|
||||||
|
uint32_t len_crc;
|
||||||
if (harq_process->cb_segm.C > 1) {
|
char *cb_in_ptr;
|
||||||
/* Check Codeblock CRC and stop early if incorrect */
|
crc_t *crc_ptr;
|
||||||
if (crc_checksum(&q->crc_cb, q->cb_in, cb_len)) {
|
tdec_reset(&q->decoder, cb_len);
|
||||||
INFO("Error in CB#%d\n",i);
|
|
||||||
return LIBLTE_ERROR;
|
do {
|
||||||
|
|
||||||
|
tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len);
|
||||||
|
q->nof_iterations++;
|
||||||
|
|
||||||
|
if (harq_process->cb_segm.C > 1) {
|
||||||
|
len_crc = cb_len;
|
||||||
|
cb_in_ptr = q->cb_in;
|
||||||
|
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);
|
||||||
|
|
||||||
|
q->average_nof_iterations = EXPAVERAGE((float) q->nof_iterations,
|
||||||
|
q->average_nof_iterations,
|
||||||
|
q->average_nof_iterations_n);
|
||||||
|
q->average_nof_iterations_n++;
|
||||||
|
|
||||||
/* Copy data to another buffer, removing the Codeblock CRC */
|
/* Copy data to another buffer, removing the Codeblock CRC */
|
||||||
if (i < harq_process->cb_segm.C - 1) {
|
if (i < harq_process->cb_segm.C - 1) {
|
||||||
|
@ -534,6 +570,7 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
|
||||||
} else {
|
} else {
|
||||||
DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
|
DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
|
||||||
rlen - F - 24, wp, F, rlen - 24);
|
rlen - F - 24, wp, F, rlen - 24);
|
||||||
|
|
||||||
/* Append Transport Block parity bits to the last CB */
|
/* Append Transport Block parity bits to the last CB */
|
||||||
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char));
|
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(char));
|
||||||
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(char));
|
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(char));
|
||||||
|
@ -636,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_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_table_set(&q->demod, &q->mod[harq_process->mcs.mod - 1]);
|
||||||
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols);
|
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols);
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (int j=0;j<nof_symbols;j++) {
|
||||||
|
if (isnan(crealf(q->pdsch_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 */
|
/* descramble */
|
||||||
scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 0, nof_bits_e);
|
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);
|
return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx);
|
||||||
} else {
|
} else {
|
||||||
return LIBLTE_ERROR_INVALID_INPUTS;
|
return LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
|
@ -1,628 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* \section COPYRIGHT
|
|
||||||
*
|
|
||||||
* Copyright 2013-2014 The libLTE Developers. See the
|
|
||||||
* COPYRIGHT file at the top-level directory of this distribution.
|
|
||||||
*
|
|
||||||
* \section LICENSE
|
|
||||||
*
|
|
||||||
* This file is part of the libLTE library.
|
|
||||||
*
|
|
||||||
* libLTE is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* libLTE is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Lesser General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "liblte/phy/phch/ue_sync.h"
|
|
||||||
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
|
||||||
#include "liblte/phy/utils/vector.h"
|
|
||||||
|
|
||||||
#define MAX_TIME_OFFSET 128
|
|
||||||
cf_t dummy[MAX_TIME_OFFSET];
|
|
||||||
|
|
||||||
#define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1))
|
|
||||||
|
|
||||||
#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb)
|
|
||||||
#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE, q->cell.cp)
|
|
||||||
|
|
||||||
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
|
||||||
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
|
|
||||||
|
|
||||||
#define MAXIMUM_SFLEN SF_LEN(2048, CPNORM)
|
|
||||||
#define MAXIMUM_SFLEN_RE SF_LEN_RE(110, CPNORM)
|
|
||||||
|
|
||||||
static int mib_decoder_initialize(ue_sync_t *q);
|
|
||||||
static void mib_decoder_free(ue_sync_t *q);
|
|
||||||
|
|
||||||
|
|
||||||
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, 20000, 2000);
|
|
||||||
break;
|
|
||||||
case 256:
|
|
||||||
sync_set_threshold(&q->s, 25000, 2500);
|
|
||||||
break;
|
|
||||||
case 512:
|
|
||||||
sync_set_threshold(&q->s, 38000, 3800);
|
|
||||||
break;
|
|
||||||
case 1024:
|
|
||||||
sync_set_threshold(&q->s, 50000, 5000);
|
|
||||||
break;
|
|
||||||
case 2048:
|
|
||||||
sync_set_threshold(&q->s, 80000, 4000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;i<SYNC_PBCH_NOF_PORTS;i++) {
|
|
||||||
q->ce[i] = vec_malloc(MAXIMUM_SFLEN_RE * sizeof(cf_t));
|
|
||||||
if (!q->ce[i]) {
|
|
||||||
perror("malloc");
|
|
||||||
goto clean_exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;i<SYNC_PBCH_NOF_PORTS;i++) {
|
|
||||||
if (q->ce[i]) {
|
|
||||||
free(q->ce[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mib_decoder_free(q);
|
|
||||||
cfo_free(&q->cfocorr);
|
|
||||||
sync_free(&q->s);
|
|
||||||
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;
|
|
||||||
q->cell.nof_prb = q->mib.nof_prb;
|
|
||||||
|
|
||||||
if (!q->pbch_decoded) {
|
|
||||||
printf("\n\nMIB decoded:\n");
|
|
||||||
pbch_mib_fprint(stdout, &q->mib, q->cell.id);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,23 +19,6 @@
|
||||||
# and at http://www.gnu.org/licenses/.
|
# and at http://www.gnu.org/licenses/.
|
||||||
#
|
#
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# UE SYNC TEST (Only compiled if CUHD is available)
|
|
||||||
########################################################################
|
|
||||||
LIST(FIND OPTIONAL_LIBS cuhd CUHD_FIND)
|
|
||||||
LIST(FIND OPTIONAL_LIBS graphics GRAPHICS_FIND)
|
|
||||||
IF(${CUHD_FIND} GREATER -1)
|
|
||||||
ADD_EXECUTABLE(ue_sync_usrp ue_sync_usrp.c)
|
|
||||||
TARGET_LINK_LIBRARIES(ue_sync_usrp lte_phy cuhd)
|
|
||||||
ENDIF(${CUHD_FIND} GREATER -1)
|
|
||||||
|
|
||||||
IF(${GRAPHICS_FIND} EQUAL -1)
|
|
||||||
SET_TARGET_PROPERTIES(ue_sync_usrp PROPERTIES COMPILE_DEFINITIONS "DISABLE_GRAPHICS")
|
|
||||||
ELSE(${GRAPHICS_FIND} EQUAL -1)
|
|
||||||
target_link_libraries(ue_sync_usrp graphics)
|
|
||||||
ENDIF(${GRAPHICS_FIND} EQUAL -1)
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# PBCH TEST
|
# PBCH TEST
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
|
@ -117,14 +117,14 @@ int base_init() {
|
||||||
exit(-1);
|
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) {
|
if (!fft_buffer) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0;i<cell.nof_ports;i++) {
|
for (i=0;i<cell.nof_ports;i++) {
|
||||||
ce[i] = malloc(2 * CP_NSYMB(cell.cp) * cell.nof_prb * RE_X_RB * sizeof(cf_t));
|
ce[i] = malloc(SLOT_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));
|
||||||
if (!ce[i]) {
|
if (!ce[i]) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -194,7 +194,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
n = filesource_read(&fsrc, input_buffer, FLEN);
|
n = filesource_read(&fsrc, input_buffer, FLEN);
|
||||||
|
|
||||||
lte_fft_run_sf(&fft, input_buffer, fft_buffer);
|
lte_fft_run_slot(&fft, &input_buffer[960], fft_buffer);
|
||||||
|
|
||||||
if (fmatlab) {
|
if (fmatlab) {
|
||||||
fprintf(fmatlab, "outfft=");
|
fprintf(fmatlab, "outfft=");
|
||||||
|
@ -205,7 +205,7 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get channel estimates for each port */
|
/* Get channel estimates for each port */
|
||||||
chest_ce_sf(&chest, fft_buffer, ce, 0);
|
chest_ce_slot(&chest, fft_buffer, ce, 1);
|
||||||
|
|
||||||
INFO("Decoding PBCH\n", 0);
|
INFO("Decoding PBCH\n", 0);
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,11 @@ int main(int argc, char **argv) {
|
||||||
int i, j;
|
int i, j;
|
||||||
cf_t *ce[MAX_PORTS];
|
cf_t *ce[MAX_PORTS];
|
||||||
int nof_re;
|
int nof_re;
|
||||||
cf_t *sf_symbols[MAX_PORTS];
|
cf_t *slot1_symbols[MAX_PORTS];
|
||||||
|
|
||||||
parse_args(argc,argv);
|
parse_args(argc,argv);
|
||||||
|
|
||||||
nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB;
|
nof_re = SLOT_LEN_RE(cell.nof_prb, CPNORM);
|
||||||
|
|
||||||
/* init memory */
|
/* init memory */
|
||||||
for (i=0;i<cell.nof_ports;i++) {
|
for (i=0;i<cell.nof_ports;i++) {
|
||||||
|
@ -94,8 +94,8 @@ int main(int argc, char **argv) {
|
||||||
for (j=0;j<nof_re;j++) {
|
for (j=0;j<nof_re;j++) {
|
||||||
ce[i][j] = 1;
|
ce[i][j] = 1;
|
||||||
}
|
}
|
||||||
sf_symbols[i] = malloc(sizeof(cf_t) * nof_re);
|
slot1_symbols[i] = malloc(sizeof(cf_t) * nof_re);
|
||||||
if (!sf_symbols[i]) {
|
if (!slot1_symbols[i]) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -112,17 +112,17 @@ int main(int argc, char **argv) {
|
||||||
mib_tx.phich_resources = R_1_6;
|
mib_tx.phich_resources = R_1_6;
|
||||||
mib_tx.sfn = 124;
|
mib_tx.sfn = 124;
|
||||||
|
|
||||||
pbch_encode(&pbch, &mib_tx, sf_symbols);
|
pbch_encode(&pbch, &mib_tx, slot1_symbols);
|
||||||
|
|
||||||
/* combine outputs */
|
/* combine outputs */
|
||||||
for (i=1;i<cell.nof_ports;i++) {
|
for (i=1;i<cell.nof_ports;i++) {
|
||||||
for (j=0;j<nof_re;j++) {
|
for (j=0;j<nof_re;j++) {
|
||||||
sf_symbols[0][j] += sf_symbols[i][j];
|
slot1_symbols[0][j] += slot1_symbols[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pbch_decode_reset(&pbch);
|
pbch_decode_reset(&pbch);
|
||||||
if (1 != pbch_decode(&pbch, sf_symbols[0], ce, &mib_rx)) {
|
if (1 != pbch_decode(&pbch, slot1_symbols[0], ce, &mib_rx)) {
|
||||||
printf("Error decoding\n");
|
printf("Error decoding\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
for (i=0;i<cell.nof_ports;i++) {
|
for (i=0;i<cell.nof_ports;i++) {
|
||||||
free(ce[i]);
|
free(ce[i]);
|
||||||
free(sf_symbols[i]);
|
free(slot1_symbols[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memcmp(&mib_tx, &mib_rx, sizeof(pbch_mib_t))) {
|
if (!memcmp(&mib_tx, &mib_rx, sizeof(pbch_mib_t))) {
|
||||||
|
|
|
@ -119,7 +119,7 @@ int base_init() {
|
||||||
fmatlab = NULL;
|
fmatlab = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
flen = SLOT_LEN(lte_symbol_sz(cell.nof_prb), cell.cp);
|
flen = SLOT_LEN(lte_symbol_sz(cell.nof_prb));
|
||||||
|
|
||||||
input_buffer = malloc(flen * sizeof(cf_t));
|
input_buffer = malloc(flen * sizeof(cf_t));
|
||||||
if (!input_buffer) {
|
if (!input_buffer) {
|
||||||
|
|
|
@ -133,7 +133,7 @@ int base_init() {
|
||||||
fmatlab = NULL;
|
fmatlab = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
flen = 2 * (SLOT_LEN(lte_symbol_sz(cell.nof_prb), cell.cp));
|
flen = 2 * (SLOT_LEN(lte_symbol_sz(cell.nof_prb)));
|
||||||
|
|
||||||
input_buffer = malloc(flen * sizeof(cf_t));
|
input_buffer = malloc(flen * sizeof(cf_t));
|
||||||
if (!input_buffer) {
|
if (!input_buffer) {
|
||||||
|
|
|
@ -136,7 +136,7 @@ int base_init() {
|
||||||
fmatlab = NULL;
|
fmatlab = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
flen = 2 * (SLOT_LEN(lte_symbol_sz(cell.nof_prb), cell.cp));
|
flen = 2 * (SLOT_LEN(lte_symbol_sz(cell.nof_prb)));
|
||||||
|
|
||||||
input_buffer = malloc(flen * sizeof(cf_t));
|
input_buffer = malloc(flen * sizeof(cf_t));
|
||||||
if (!input_buffer) {
|
if (!input_buffer) {
|
||||||
|
|
|
@ -144,7 +144,7 @@ int base_init() {
|
||||||
fmatlab = NULL;
|
fmatlab = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
flen = SLOT_LEN(lte_symbol_sz(cell.nof_prb), cell.cp);
|
flen = SLOT_LEN(lte_symbol_sz(cell.nof_prb));
|
||||||
|
|
||||||
input_buffer = malloc(flen * sizeof(cf_t));
|
input_buffer = malloc(flen * sizeof(cf_t));
|
||||||
if (!input_buffer) {
|
if (!input_buffer) {
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "liblte/phy/utils/dft.h"
|
#include "liblte/phy/utils/dft.h"
|
||||||
#include "liblte/phy/utils/vector.h"
|
#include "liblte/phy/utils/vector.h"
|
||||||
#include "liblte/phy/utils/convolution.h"
|
#include "liblte/phy/utils/convolution.h"
|
||||||
|
#include "liblte/phy/utils/debug.h"
|
||||||
|
|
||||||
|
|
||||||
int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_size) {
|
int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_size) {
|
||||||
|
@ -45,7 +46,7 @@ int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_s
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
if (lte_N_id_2_isvalid(N_id_2) &&
|
if (lte_N_id_2_isvalid(N_id_2) &&
|
||||||
fft_size < 2048)
|
fft_size <= 2048)
|
||||||
{
|
{
|
||||||
|
|
||||||
pss_generate(pss_signal_time, N_id_2);
|
pss_generate(pss_signal_time, N_id_2);
|
||||||
|
@ -60,10 +61,11 @@ int pss_synch_init_N_id_2(cf_t *pss_signal_freq, uint32_t N_id_2, uint32_t fft_s
|
||||||
|
|
||||||
dft_plan_set_mirror(&plan, true);
|
dft_plan_set_mirror(&plan, true);
|
||||||
dft_plan_set_dc(&plan, true);
|
dft_plan_set_dc(&plan, true);
|
||||||
|
dft_plan_set_norm(&plan, true);
|
||||||
dft_run_c(&plan, pss_signal_pad, pss_signal_freq);
|
dft_run_c(&plan, pss_signal_pad, pss_signal_freq);
|
||||||
|
|
||||||
vec_sc_prod_cfc(pss_signal_freq, (float) 1 / (fft_size), pss_signal_pad, fft_size);
|
vec_conj_cc(pss_signal_freq, pss_signal_freq, fft_size);
|
||||||
vec_conj_cc(pss_signal_pad, pss_signal_freq, fft_size);
|
vec_sc_prod_cfc(pss_signal_freq, 1.0/62.0, pss_signal_freq, fft_size);
|
||||||
|
|
||||||
dft_plan_free(&plan);
|
dft_plan_free(&plan);
|
||||||
|
|
||||||
|
@ -97,11 +99,6 @@ int pss_synch_init_fft(pss_synch_t *q, uint32_t frame_size, uint32_t fft_size) {
|
||||||
|
|
||||||
buffer_size = fft_size + frame_size + 1;
|
buffer_size = fft_size + frame_size + 1;
|
||||||
|
|
||||||
q->conv_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));
|
q->tmp_input = vec_malloc(buffer_size * sizeof(cf_t));
|
||||||
if (!q->tmp_input) {
|
if (!q->tmp_input) {
|
||||||
fprintf(stderr, "Error allocating memory\n");
|
fprintf(stderr, "Error allocating memory\n");
|
||||||
|
@ -160,9 +157,6 @@ void pss_synch_free(pss_synch_t *q) {
|
||||||
if (q->conv_output) {
|
if (q->conv_output) {
|
||||||
free(q->conv_output);
|
free(q->conv_output);
|
||||||
}
|
}
|
||||||
if (q->conv_abs) {
|
|
||||||
free(q->conv_abs);
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero(q, sizeof(pss_synch_t));
|
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.
|
* Input buffer must be subframe_size long.
|
||||||
*/
|
*/
|
||||||
int pss_synch_find_pss(pss_synch_t *q, cf_t *input,
|
int pss_synch_find_pss(pss_synch_t *q, cf_t *input, float *corr_peak_value)
|
||||||
float *corr_peak_value, float *corr_mean_value)
|
|
||||||
{
|
{
|
||||||
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
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));
|
memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t));
|
||||||
bzero(&q->tmp_input[q->frame_size], q->fft_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
|
#ifdef CONVOLUTION_FFT
|
||||||
conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input,
|
conv_output_len = conv_fft_cc_run(&q->conv_fft, q->tmp_input,
|
||||||
q->pss_signal_freq[q->N_id_2], q->conv_output);
|
q->pss_signal_freq[q->N_id_2], q->conv_output);
|
||||||
|
@ -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);
|
conv_output_len = conv_cc(input, q->pss_signal_freq[q->N_id_2], q->conv_output, q->frame_size, q->fft_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vec_abs_cf(q->conv_output, q->conv_abs, conv_output_len);
|
/* Find maximum of the absolute value of the correlation */
|
||||||
corr_peak_pos = vec_max_fi(q->conv_abs, conv_output_len);
|
corr_peak_pos = vec_max_abs_ci(q->conv_output, conv_output_len);
|
||||||
if (corr_peak_value) {
|
if (corr_peak_value) {
|
||||||
*corr_peak_value = q->conv_abs[corr_peak_pos];
|
*corr_peak_value = cabsf(q->conv_output[corr_peak_pos]);
|
||||||
}
|
}
|
||||||
if (corr_mean_value) {
|
|
||||||
*corr_mean_value = vec_acc_ff(q->conv_abs, conv_output_len)
|
|
||||||
/ conv_output_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = (int) corr_peak_pos;
|
ret = (int) corr_peak_pos;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -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) {
|
int sss_synch_init(sss_synch_t *q, uint32_t fft_size) {
|
||||||
|
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
fft_size < 2048)
|
fft_size <= 2048)
|
||||||
{
|
{
|
||||||
uint32_t N_id_2;
|
uint32_t N_id_2;
|
||||||
struct sss_tables sss_tables;
|
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) {
|
int sss_synch_realloc(sss_synch_t *q, uint32_t fft_size) {
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
fft_size < 2048)
|
fft_size <= 2048)
|
||||||
{
|
{
|
||||||
dft_plan_free(&q->dftp_input);
|
dft_plan_free(&q->dftp_input);
|
||||||
if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) {
|
if (dft_plan(&q->dftp_input, fft_size, FORWARD, COMPLEX)) {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "liblte/phy/utils/debug.h"
|
#include "liblte/phy/utils/debug.h"
|
||||||
#include "liblte/phy/common/phy_common.h"
|
#include "liblte/phy/common/phy_common.h"
|
||||||
|
@ -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;
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
if (q != NULL &&
|
if (q != NULL &&
|
||||||
find_frame_size > fft_size &&
|
frame_size >= fft_size &&
|
||||||
find_frame_size < 307200 &&
|
frame_size <= 307200 &&
|
||||||
fft_size_isvalid(fft_size))
|
fft_size_isvalid(fft_size))
|
||||||
{
|
{
|
||||||
bzero(q, sizeof(sync_t));
|
bzero(q, sizeof(sync_t));
|
||||||
q->pss_mode = PEAK_MEAN;
|
|
||||||
q->detect_cp = true;
|
q->detect_cp = true;
|
||||||
|
q->normalize_en = true;
|
||||||
q->sss_en = true;
|
q->sss_en = true;
|
||||||
q->N_id_2 = 1000;
|
q->N_id_2 = 1000;
|
||||||
q->N_id_1 = 1000;
|
q->N_id_1 = 1000;
|
||||||
q->fft_size = fft_size;
|
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");
|
fprintf(stderr, "Error initializing PSS object\n");
|
||||||
return LIBLTE_ERROR;
|
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");
|
fprintf(stderr, "Error initializing SSS object\n");
|
||||||
return LIBLTE_ERROR;
|
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);
|
DEBUG("SYNC init with frame_size=%d and fft_size=%d\n", 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);
|
|
||||||
|
|
||||||
ret = LIBLTE_SUCCESS;
|
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) {
|
void sync_free(sync_t *q) {
|
||||||
if (q) {
|
if (q) {
|
||||||
pss_synch_free(&q->pss_track);
|
pss_synch_free(&q->pss);
|
||||||
pss_synch_free(&q->pss_find);
|
|
||||||
sss_synch_free(&q->sss);
|
sss_synch_free(&q->sss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sync_pss_det_absolute(sync_t *q) {
|
void sync_set_threshold(sync_t *q, float threshold) {
|
||||||
q->pss_mode = ABSOLUTE;
|
q->threshold = threshold;
|
||||||
}
|
|
||||||
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_sss_en(sync_t *q, bool enabled) {
|
void sync_sss_en(sync_t *q, bool enabled) {
|
||||||
q->sss_en = 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) {
|
bool sync_sss_detected(sync_t *q) {
|
||||||
return lte_N_id_1_isvalid(q->N_id_1);
|
return lte_N_id_1_isvalid(q->N_id_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sync_get_cell_id(sync_t *q) {
|
int sync_get_cell_id(sync_t *q) {
|
||||||
if (q->N_id_2 != 10) {
|
if (lte_N_id_2_isvalid(q->N_id_2) && lte_N_id_1_isvalid(q->N_id_1)) {
|
||||||
if (lte_N_id_2_isvalid(q->N_id_2) && lte_N_id_1_isvalid(q->N_id_1)) {
|
return q->N_id_1*3 + q->N_id_2;
|
||||||
return q->N_id_1*3 + q->N_id_2;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error getting cell_id, invalid N_id_1 or N_id_2\n");
|
|
||||||
return LIBLTE_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
} 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;
|
return LIBLTE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sync_get_N_id_1(sync_t *q) {
|
int sync_set_N_id_2(sync_t *q, uint32_t N_id_2) {
|
||||||
return q->N_id_1;
|
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_N_id_2(sync_t *q) {
|
uint32_t sync_get_sf_idx(sync_t *q) {
|
||||||
return q->N_id_2;
|
return q->sf_idx;
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t sync_get_slot_id(sync_t *q) {
|
|
||||||
return q->slot_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float sync_get_cfo(sync_t *q) {
|
float sync_get_cfo(sync_t *q) {
|
||||||
return q->cfo;
|
return q->cfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
float sync_get_peak_value(sync_t *q) {
|
float sync_get_last_peak_value(sync_t *q) {
|
||||||
return q->peak_value;
|
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) {
|
void sync_cp_en(sync_t *q, bool enabled) {
|
||||||
q->detect_cp = enabled;
|
q->detect_cp = enabled;
|
||||||
}
|
}
|
||||||
|
@ -188,129 +145,134 @@ lte_cp_t sync_get_cp(sync_t *q) {
|
||||||
return q->cp;
|
return q->cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos, bool en_cp) {
|
/* 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 (R_norm > R_ext) {
|
||||||
|
return CPNORM;
|
||||||
|
} else {
|
||||||
|
return CPEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos) {
|
||||||
uint32_t m0, m1;
|
uint32_t m0, m1;
|
||||||
int sss_idx_n, sss_idx_e, ret;
|
int sss_idx, ret;
|
||||||
float m0_value_e, m1_value_e,m0_value_n, m1_value_n;
|
float m0_value, m1_value;
|
||||||
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);
|
sss_synch_set_N_id_2(&q->sss, q->N_id_2);
|
||||||
|
|
||||||
|
if (q->detect_cp) {
|
||||||
|
q->cp = detect_cp(q, input, peak_pos);
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure we have enough room to find SSS sequence */
|
/* 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 = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, q->cp));
|
||||||
sss_idx_e = (int) peak_pos - 2*(q->fft_size + CP(q->fft_size, CPEXT_LEN));
|
|
||||||
|
|
||||||
if (en_cp) {
|
if (sss_idx < 0) {
|
||||||
if (sss_idx_n < 0 || sss_idx_e < 0) {
|
INFO("Not enough room to decode CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx, peak_pos);
|
||||||
INFO("Not enough room to decode SSS (%d, %d)\n", sss_idx_n, sss_idx_e);
|
return LIBLTE_SUCCESS;
|
||||||
return LIBLTE_SUCCESS;
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slot_id_n = 0;
|
|
||||||
slot_id_e = 0;
|
|
||||||
N_id_1_n = 0;
|
|
||||||
N_id_1_e = 0;
|
|
||||||
|
|
||||||
/* try Normal CP length */
|
/* try Normal CP length */
|
||||||
if (en_cp || CP_ISNORM(q->cp)) {
|
sss_synch_m0m1(&q->sss, &input[sss_idx], &m0, &m0_value, &m1, &m1_value);
|
||||||
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);
|
q->sf_idx = sss_synch_subframe(m0, m1);
|
||||||
ret = sss_synch_N_id_1(&q->sss, m0, m1);
|
ret = sss_synch_N_id_1(&q->sss, m0, m1);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
N_id_1_n = (uint32_t) ret;
|
q->N_id_1 = (uint32_t) ret;
|
||||||
} else {
|
|
||||||
N_id_1_n = 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
} else {
|
} else {
|
||||||
q->cp = CPNORM;
|
q->N_id_1 = 1000;
|
||||||
q->slot_id = slot_id_n;
|
|
||||||
q->N_id_1 = N_id_1_n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("SSS detected N_id_1=%d, slot_idx=%d, position=%d/%d %s CP\n",
|
DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n",
|
||||||
q->N_id_1, q->slot_id, sss_idx_n, sss_idx_e, CP_ISNORM(q->cp)?"Normal":"Extended");
|
q->N_id_1, q->sf_idx, CP_ISNORM(q->cp)?"Normal":"Extended");
|
||||||
|
|
||||||
return 1;
|
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;
|
int ret = LIBLTE_ERROR_INVALID_INPUTS;
|
||||||
|
|
||||||
if (q != NULL &&
|
float peak_unnormalized, energy;
|
||||||
input != NULL &&
|
|
||||||
|
if (q != NULL &&
|
||||||
|
input != NULL &&
|
||||||
|
lte_N_id_2_isvalid(q->N_id_2) &&
|
||||||
fft_size_isvalid(q->fft_size))
|
fft_size_isvalid(q->fft_size))
|
||||||
{
|
{
|
||||||
float peak_value, mean_value, *mean_ptr;
|
|
||||||
uint32_t peak_pos;
|
uint32_t peak_pos;
|
||||||
|
|
||||||
pss_synch_set_N_id_2(&q->pss_track, q->N_id_2);
|
|
||||||
|
|
||||||
if (q->pss_mode == ABSOLUTE) {
|
if (peak_position) {
|
||||||
mean_ptr = NULL;
|
*peak_position = 0;
|
||||||
} else {
|
|
||||||
mean_ptr = &mean_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
peak_pos = pss_synch_find_pss(&q->pss_track, &input[offset], &peak_value, mean_ptr);
|
|
||||||
|
|
||||||
if (q->pss_mode == ABSOLUTE) {
|
pss_synch_set_N_id_2(&q->pss, q->N_id_2);
|
||||||
q->peak_value = peak_value;
|
|
||||||
} else {
|
peak_pos = pss_synch_find_pss(&q->pss, &input[find_offset], &peak_unnormalized);
|
||||||
q->peak_value = peak_value / mean_value;
|
|
||||||
|
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++;
|
||||||
|
|
||||||
DEBUG("PSS possible tracking peak pos=%d peak=%.2f threshold=%.2f\n",
|
/* If peak is over threshold, compute CFO and SSS */
|
||||||
peak_pos, peak_value, q->track_threshold);
|
if (q->peak_value >= q->threshold) {
|
||||||
|
if (find_offset + peak_pos >= q->fft_size) {
|
||||||
if (q->peak_value > q->track_threshold) {
|
q->cfo = pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]);
|
||||||
if (offset + peak_pos > q->fft_size) {
|
|
||||||
q->cfo = pss_synch_cfo_compute(&q->pss_track, &input[offset+peak_pos-q->fft_size]);
|
|
||||||
if (q->sss_en) {
|
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");
|
fprintf(stderr, "Error synchronizing with SSS\n");
|
||||||
return LIBLTE_ERROR;
|
return LIBLTE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("Warning: no space for CFO computation\n");
|
INFO("Warning: no space for CFO computation\n",0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peak_position) {
|
if (peak_position) {
|
||||||
*peak_position = peak_pos;
|
*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 {
|
} else {
|
||||||
ret = LIBLTE_SUCCESS;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sync_find(sync_t *q, cf_t *input, uint32_t *peak_position) {
|
void sync_reset(sync_t *q) {
|
||||||
uint32_t N_id_2, peak_pos[3];
|
q->frame_cnt = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,15 @@
|
||||||
# and at http://www.gnu.org/licenses/.
|
# 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
|
# SYNC TEST
|
||||||
|
@ -27,11 +36,16 @@
|
||||||
ADD_EXECUTABLE(sync_test sync_test.c)
|
ADD_EXECUTABLE(sync_test sync_test.c)
|
||||||
TARGET_LINK_LIBRARIES(sync_test lte_phy)
|
TARGET_LINK_LIBRARIES(sync_test lte_phy)
|
||||||
|
|
||||||
ADD_TEST(sync_test_100 sync_test -o 100)
|
ADD_TEST(sync_test_100 sync_test -o 100 -c 501)
|
||||||
ADD_TEST(sync_test_400 sync_test -o 400)
|
ADD_TEST(sync_test_400 sync_test -o 400 -c 2)
|
||||||
ADD_TEST(sync_test_100_e sync_test -o 100 -e)
|
ADD_TEST(sync_test_100_e sync_test -o 100 -e -c 150)
|
||||||
ADD_TEST(sync_test_400_e sync_test -o 400 -e)
|
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
|
# CFO TEST
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
|
@ -32,18 +32,21 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "liblte/phy/phy.h"
|
#include "liblte/phy/phy.h"
|
||||||
|
|
||||||
int cell_id = -1, offset = 0;
|
int cell_id = -1, offset = 0;
|
||||||
lte_cp_t cp = CPNORM;
|
lte_cp_t cp = CPNORM;
|
||||||
|
uint32_t nof_prb=6;
|
||||||
|
|
||||||
#define FLEN 9600
|
#define FLEN SF_LEN(fft_size)
|
||||||
|
|
||||||
void usage(char *prog) {
|
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-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-o offset [Default %d]\n", offset);
|
||||||
printf("\t-e extended CP [Default normal]\n");
|
printf("\t-e extended CP [Default normal]\n");
|
||||||
printf("\t-v verbose\n");
|
printf("\t-v verbose\n");
|
||||||
|
@ -51,11 +54,14 @@ void usage(char *prog) {
|
||||||
|
|
||||||
void parse_args(int argc, char **argv) {
|
void parse_args(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "coev")) != -1) {
|
while ((opt = getopt(argc, argv, "cpoev")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'c':
|
case 'c':
|
||||||
cell_id = atoi(argv[optind]);
|
cell_id = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
nof_prb = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
offset = atoi(argv[optind]);
|
offset = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
@ -82,32 +88,40 @@ int main(int argc, char **argv) {
|
||||||
uint32_t find_idx;
|
uint32_t find_idx;
|
||||||
sync_t sync;
|
sync_t sync;
|
||||||
lte_fft_t ifft;
|
lte_fft_t ifft;
|
||||||
|
int fft_size;
|
||||||
|
|
||||||
parse_args(argc, argv);
|
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);
|
buffer = malloc(sizeof(cf_t) * FLEN);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fft_buffer = malloc(sizeof(cf_t) * 2 * FLEN);
|
fft_buffer = malloc(sizeof(cf_t) * FLEN);
|
||||||
if (!fft_buffer) {
|
if (!fft_buffer) {
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lte_ifft_init(&ifft, cp, 6)) {
|
if (lte_ifft_init(&ifft, cp, nof_prb)) {
|
||||||
fprintf(stderr, "Error creating iFFT object\n");
|
fprintf(stderr, "Error creating iFFT object\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync_init(&sync, FLEN, 128, 128)) {
|
if (sync_init(&sync, FLEN, fft_size)) {
|
||||||
fprintf(stderr, "Error initiating PSS/SSS\n");
|
fprintf(stderr, "Error initiating PSS/SSS\n");
|
||||||
return -1;
|
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) {
|
if (cell_id == -1) {
|
||||||
cid = 0;
|
cid = 0;
|
||||||
|
@ -123,21 +137,28 @@ int main(int argc, char **argv) {
|
||||||
pss_generate(pss_signal, N_id_2);
|
pss_generate(pss_signal, N_id_2);
|
||||||
sss_generate(sss_signal0, sss_signal5, cid);
|
sss_generate(sss_signal0, sss_signal5, cid);
|
||||||
|
|
||||||
|
sync_set_N_id_2(&sync, N_id_2);
|
||||||
|
|
||||||
for (ns=0;ns<2;ns++) {
|
for (ns=0;ns<2;ns++) {
|
||||||
memset(buffer, 0, sizeof(cf_t) * FLEN);
|
memset(buffer, 0, sizeof(cf_t) * FLEN);
|
||||||
pss_put_slot(pss_signal, buffer, 6, cp);
|
pss_put_slot(pss_signal, buffer, nof_prb, cp);
|
||||||
sss_put_slot(ns?sss_signal5:sss_signal0, buffer, 6, cp);
|
sss_put_slot(ns?sss_signal5:sss_signal0, buffer, nof_prb, cp);
|
||||||
|
|
||||||
/* Transform to OFDM symbols */
|
/* 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]);
|
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);
|
if (sync_find(&sync, fft_buffer, 0, &find_idx) < 0) {
|
||||||
find_ns = sync_get_slot_id(&sync);
|
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,
|
printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset,
|
||||||
ns, find_ns);
|
ns, find_ns);
|
||||||
if (find_idx != offset + 960) {
|
if (find_idx != offset + FLEN/2) {
|
||||||
printf("offset != find_offset: %d != %d\n", find_idx, offset + 960);
|
printf("offset != find_offset: %d != %d\n", find_idx, offset + FLEN/2);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
if (ns*10 != find_ns) {
|
if (ns*10 != find_ns) {
|
||||||
|
|
|
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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;j<q->nof_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;i<q->nof_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;i<q->nof_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;nf<nof_input_frames;nf++) {
|
||||||
|
sync_set_N_id_2(&q->sfind, 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;
|
||||||
|
}
|
|
@ -25,8 +25,10 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "liblte/phy/phch/ue_dl.h"
|
#include "liblte/phy/ue/ue_dl.h"
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#define EXPAVERAGE(data, average, nframes) ((data + average * nframes) / (nframes + 1))
|
#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->pkt_errors = 0;
|
||||||
q->pkts_total = 0;
|
q->pkts_total = 0;
|
||||||
q->nof_trials = 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)) {
|
if (lte_fft_init(&q->fft, q->cell.cp, q->cell.nof_prb)) {
|
||||||
fprintf(stderr, "Error initiating FFT\n");
|
fprintf(stderr, "Error initiating FFT\n");
|
||||||
|
@ -67,7 +71,10 @@ int ue_dl_init(ue_dl_t *q,
|
||||||
fprintf(stderr, "Error initiating REGs\n");
|
fprintf(stderr, "Error initiating REGs\n");
|
||||||
goto clean_exit;
|
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)) {
|
if (pcfich_init(&q->pcfich, &q->regs, q->cell)) {
|
||||||
fprintf(stderr, "Error creating PCFICH object\n");
|
fprintf(stderr, "Error creating PCFICH object\n");
|
||||||
goto clean_exit;
|
goto clean_exit;
|
||||||
|
@ -119,6 +126,7 @@ void ue_dl_free(ue_dl_t *q) {
|
||||||
lte_fft_free(&q->fft);
|
lte_fft_free(&q->fft);
|
||||||
chest_free(&q->chest);
|
chest_free(&q->chest);
|
||||||
regs_free(&q->regs);
|
regs_free(&q->regs);
|
||||||
|
pbch_free(&q->pbch);
|
||||||
pcfich_free(&q->pcfich);
|
pcfich_free(&q->pcfich);
|
||||||
pdcch_free(&q->pdcch);
|
pdcch_free(&q->pdcch);
|
||||||
pdsch_free(&q->pdsch);
|
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;
|
uint32_t cfi, cfi_distance, i;
|
||||||
ra_pdsch_t ra_dl;
|
ra_pdsch_t ra_dl;
|
||||||
|
@ -145,18 +156,48 @@ int ue_dl_receive(ue_dl_t *q, cf_t *input, char *data, uint32_t sf_idx, uint32_t
|
||||||
uint32_t nof_locations;
|
uint32_t nof_locations;
|
||||||
uint16_t crc_rem;
|
uint16_t crc_rem;
|
||||||
dci_format_t format;
|
dci_format_t format;
|
||||||
|
pbch_mib_t mib;
|
||||||
int ret = LIBLTE_ERROR;
|
int ret = LIBLTE_ERROR;
|
||||||
|
cf_t *ce_slot1[MAX_PORTS];
|
||||||
/* If we are looking for SI Blocks, search only in appropiate places */
|
struct timeval t[3];
|
||||||
if ((rnti == SIRNTI && (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 */
|
/* Run FFT for all subframe data */
|
||||||
chest_ce_sf(&q->chest, q->sf_symbols, q->ce, sf_idx);
|
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;i<MAX_PORTS;i++) {
|
||||||
|
ce_slot1[i] = &q->ce[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 && (q->sfn % 2) == 0 && sf_idx == 5) ||
|
||||||
|
rnti != SIRNTI))
|
||||||
|
{
|
||||||
|
|
||||||
/* First decode PCFICH and obtain CFI */
|
/* First decode PCFICH and obtain CFI */
|
||||||
if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce, sf_idx, &cfi, &cfi_distance)<0) {
|
if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce, sf_idx, &cfi, &cfi_distance)<0) {
|
||||||
|
@ -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;
|
format = Format1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
crc_rem = 0;
|
crc_rem = 0;
|
||||||
for (i=0;i<nof_locations && crc_rem != rnti;i++) {
|
for (i=0;i<nof_locations && crc_rem != rnti;i++) {
|
||||||
if (pdcch_extract_llr(&q->pdcch, q->sf_symbols, q->ce, locations[i], sf_idx, cfi)) {
|
if (pdcch_extract_llr(&q->pdcch, 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);
|
INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (crc_rem == rnti) {
|
if (crc_rem == rnti) {
|
||||||
if (dci_msg_to_ra_dl(&dci_msg, rnti, q->user_rnti, q->cell, cfi, &ra_dl)) {
|
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");
|
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;
|
uint32_t rvidx;
|
||||||
if (rnti == SIRNTI) {
|
if (rnti == SIRNTI) {
|
||||||
switch((sfn%8)/2) {
|
switch((q->sfn%8)/2) {
|
||||||
case 0:
|
case 0:
|
||||||
rvidx = 0;
|
rvidx = 0;
|
||||||
break;
|
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++;
|
q->nof_trials++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crc_rem == rnti && ret == LIBLTE_SUCCESS) {
|
if (crc_rem == rnti && ret == LIBLTE_SUCCESS) {
|
||||||
return ra_dl.mcs.tbs;
|
return ra_dl.mcs.tbs;
|
||||||
} else {
|
} else {
|
|
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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;i<MIB_NOF_PORTS;i++) {
|
||||||
|
q->ce[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;i<MIB_NOF_PORTS;i++) {
|
||||||
|
if (q->ce[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;nf<nof_input_frames;nf++) {
|
||||||
|
|
||||||
|
/* Find peak and cell id */
|
||||||
|
ret = sync_find(&q->sfind, 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;
|
||||||
|
}
|
|
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,16 +49,19 @@ plot_real_t poutfft;
|
||||||
|
|
||||||
int nof_frames = -1;
|
int nof_frames = -1;
|
||||||
float threshold = -1.0;
|
float threshold = -1.0;
|
||||||
|
int N_id_2 = -1;
|
||||||
|
uint32_t nof_prb = 6;
|
||||||
|
|
||||||
float uhd_freq = 0.0, uhd_gain = 20.0;
|
float uhd_freq = 0.0, uhd_gain = 20.0;
|
||||||
char *uhd_args = "";
|
char *uhd_args = "";
|
||||||
int disable_plots = 0;
|
int disable_plots = 0;
|
||||||
|
|
||||||
void usage(char *prog) {
|
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-a UHD args [Default %s]\n", uhd_args);
|
||||||
printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain);
|
printf("\t-g UHD RX gain [Default %.2f dB]\n", uhd_gain);
|
||||||
printf("\t-n nof_frames [Default infinite]\n");
|
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);
|
printf("\t-t threshold [Default %.2f]\n",threshold);
|
||||||
|
|
||||||
#ifndef DISABLE_GRAPHICS
|
#ifndef DISABLE_GRAPHICS
|
||||||
|
@ -69,8 +72,14 @@ void usage(char *prog) {
|
||||||
|
|
||||||
void parse_args(int argc, char **argv) {
|
void parse_args(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "agntdvf")) != -1) {
|
while ((opt = getopt(argc, argv, "agntdvfip")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
case 'i':
|
||||||
|
N_id_2 = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
nof_prb = atoi(argv[optind]);
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
nof_frames = atoi(argv[optind]);
|
nof_frames = atoi(argv[optind]);
|
||||||
break;
|
break;
|
||||||
|
@ -97,7 +106,7 @@ void parse_args(int argc, char **argv) {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (uhd_freq == 0.0) {
|
if (uhd_freq == 0.0 || N_id_2 == -1) {
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +126,12 @@ void input_init() {
|
||||||
cuhd_rx_wait_lo_locked(uhd);
|
cuhd_rx_wait_lo_locked(uhd);
|
||||||
DEBUG("Set uhd_freq to %.3f MHz\n", (double ) uhd_freq/1000000);
|
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);
|
DEBUG("Starting receiver...\n", 0);
|
||||||
cuhd_start_rx_stream(uhd);
|
cuhd_start_rx_stream(uhd);
|
||||||
|
|
||||||
|
@ -152,7 +167,6 @@ int main(int argc, char **argv) {
|
||||||
float peak;
|
float peak;
|
||||||
struct timeval t[3];
|
struct timeval t[3];
|
||||||
float mean_ce_time=0;
|
float mean_ce_time=0;
|
||||||
bool signal_detected;
|
|
||||||
lte_fft_t fft;
|
lte_fft_t fft;
|
||||||
lte_cell_t cell;
|
lte_cell_t cell;
|
||||||
|
|
||||||
|
@ -167,15 +181,28 @@ int main(int argc, char **argv) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
input_init();
|
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");
|
fprintf(stderr, "Error initiating UE sync module\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ue_sync_pbch_enable(&s, true);
|
pss_synch_init_fft(&pss,
|
||||||
|
SF_LEN(lte_symbol_sz(cell.nof_prb)),
|
||||||
signal_detected = true;
|
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;
|
frame_cnt = 0;
|
||||||
mean_ce_time=0;
|
mean_ce_time=0;
|
||||||
uint32_t valid_frames=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 (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);
|
mean_ce_time = (float) (mean_ce_time + (float) t[0].tv_usec * valid_frames) / (valid_frames+1);
|
||||||
valid_frames++;
|
valid_frames++;
|
||||||
|
|
||||||
|
@ -227,13 +238,9 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pos = pss_synch_find_pss(&pss, input_buffer, &peak, NULL);
|
pos = pss_synch_find_pss(&pss, input_buffer, &peak);
|
||||||
/*if (pos > 962 || pos < 958) {
|
|
||||||
unaligned++;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
printf("CELL_ID: %3d CFO: %+.4f KHz, SFO: %+.4f Khz, TimeOffset: %4d, Exec: %3.2f\r",
|
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);
|
s.mean_exec_time);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (VERBOSE_ISINFO()) {
|
if (VERBOSE_ISINFO()) {
|
|
@ -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)) {
|
if (dft_plan(&q->output_plan,q->output_len,BACKWARD,COMPLEX)) {
|
||||||
return LIBLTE_ERROR;
|
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;
|
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);
|
dft_run_c(&q->output_plan, q->output_fft, output);
|
||||||
|
|
||||||
return q->output_len;
|
return q->output_len-1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "liblte/phy/utils/dft.h"
|
#include "liblte/phy/utils/dft.h"
|
||||||
|
#include "liblte/phy/utils/vector.h"
|
||||||
|
|
||||||
#define dft_ceil(a,b) ((a-1)/b+1)
|
#define dft_ceil(a,b) ((a-1)/b+1)
|
||||||
#define dft_floor(a,b) (a/b)
|
#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);
|
plan->forward, plan->mirror, plan->dc);
|
||||||
fftwf_execute(plan->p);
|
fftwf_execute(plan->p);
|
||||||
if (plan->norm) {
|
if (plan->norm) {
|
||||||
/**FIXME: Use VOLK */
|
norm = 1.0/sqrtf(plan->size);
|
||||||
norm = sqrtf(plan->size);
|
vec_sc_prod_cfc(f_out, norm, f_out, plan->size);
|
||||||
for (i=0;i<plan->size;i++) {
|
|
||||||
f_out[i] /= norm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (plan->db) {
|
if (plan->db) {
|
||||||
for (i=0;i<plan->size;i++) {
|
for (i=0;i<plan->size;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);
|
memcpy(plan->in,in,sizeof(dft_r_t)*plan->size);
|
||||||
fftwf_execute(plan->p);
|
fftwf_execute(plan->p);
|
||||||
if (plan->norm) {
|
if (plan->norm) {
|
||||||
norm = plan->size;
|
norm = 1.0/plan->size;
|
||||||
for (i=0;i<len;i++) {
|
vec_sc_prod_fff(f_out, norm, f_out, plan->size);
|
||||||
f_out[i] /= norm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (plan->db) {
|
if (plan->db) {
|
||||||
for (i=0;i<len;i++) {
|
for (i=0;i<len;i++) {
|
||||||
|
|
|
@ -155,6 +155,17 @@ void vec_deinterleave_cf(cf_t *x, float *real, float *imag, uint32_t len) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vec_deinterleave_real_cf(cf_t *x, float *real, uint32_t len) {
|
||||||
|
#ifdef HAVE_VOLK_DEINTERLEAVE_REAL_FUNCTION
|
||||||
|
volk_32fc_deinterleave_real_32f(real, x, len);
|
||||||
|
#else
|
||||||
|
int i;
|
||||||
|
for (i=0;i<len;i++) {
|
||||||
|
real[i] = __real__ x[i];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void *vec_malloc(uint32_t size) {
|
void *vec_malloc(uint32_t size) {
|
||||||
#ifndef HAVE_VOLK
|
#ifndef HAVE_VOLK
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
|
@ -413,6 +424,29 @@ uint32_t vec_max_fi(float *x, uint32_t len) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t vec_max_abs_ci(cf_t *x, uint32_t len) {
|
||||||
|
#ifdef HAVE_VOLK_MAX_ABS_FUNCTION
|
||||||
|
uint32_t target=0;
|
||||||
|
volk_32fc_index_max_16u(&target,x,len);
|
||||||
|
return target;
|
||||||
|
|
||||||
|
#else
|
||||||
|
uint32_t i;
|
||||||
|
float m=-FLT_MAX;
|
||||||
|
uint32_t p=0;
|
||||||
|
float tmp;
|
||||||
|
for (i=0;i<len;i++) {
|
||||||
|
tmp = crealf(x[i])*crealf(x[i]) + cimagf(x[i])*cimagf(x[i]);
|
||||||
|
if (tmp>m) {
|
||||||
|
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) {
|
void vec_quant_fuc(float *in, unsigned char *out, float gain, float offset, float clip, uint32_t len) {
|
||||||
int i;
|
int i;
|
||||||
int tmp;
|
int tmp;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
c=lte_pss_zc(N_id_2);
|
||||||
cc=[zeros(fft_size/2-31,1); c; zeros(fft_size/2-31,1)];
|
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)];
|
ccd=[0; cc(fft_size/2+1:fft_size); cc(2:fft_size/2)];
|
||||||
ccf=conj(ifft(cc));
|
ccf=sqrt(fft_size)*conj(ifft(ccd));
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
fprintf('Frame starts at %d, m=%g, p=%g, p/m=%g dB\n',i, ...
|
w2=abs(conv(x,ccf/62)).^2/var(x,1)/sqrt(2);
|
||||||
mean(abs(w2)), m, 10*log10(m/mean(abs(w2))));
|
plot(w2)
|
||||||
|
[m, idx]=max(w2);
|
||||||
|
|
||||||
|
%fprintf('Frame starts at %d, energy=%g, p=%g, p/en=%g dB\n',i, ...
|
||||||
|
% en, m, m/en);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue