Merge branch 'uplink'

master
ismagom 10 years ago
commit 0c1e2eb53d

@ -46,6 +46,11 @@ CONFIGURE_FILE(
"${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake" "${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake"
IMMEDIATE @ONLY) IMMEDIATE @ONLY)
########################################################################
# Options
########################################################################
option(DisableMEX "DisableMEX" OFF)
######################################################################## ########################################################################
# Install Dirs # Install Dirs
######################################################################## ########################################################################

@ -4,23 +4,24 @@
include(CMakeParseArguments) include(CMakeParseArguments)
if(NOT DisableMEX)
if(NOT MATLAB_FOUND)
find_package(MATLAB)
endif()
if(NOT MATLAB_FOUND) if(NOT OCTAVE_FOUND)
find_package(MATLAB) find_package(OCTAVE)
endif() endif()
if(NOT OCTAVE_FOUND)
find_package(OCTAVE)
endif()
# CMake 2.8.12 & earlier apparently don't define the # CMake 2.8.12 & earlier apparently don't define the
# Mex script path, so find it. # Mex script path, so find it.
if(NOT MATLAB_MEX_PATH) if(NOT MATLAB_MEX_PATH)
find_program( MATLAB_MEX_PATH mex find_program( MATLAB_MEX_PATH mex
HINTS ${MATLAB_ROOT}/bin HINTS ${MATLAB_ROOT}/bin
PATHS ${MATLAB_ROOT}/bin PATHS ${MATLAB_ROOT}/bin
DOC "The mex program path" DOC "The mex program path"
) )
endif()
endif() endif()
# #

@ -65,17 +65,21 @@ LIBLTE_API double cuhd_set_rx_freq_offset(void *h,
double freq, double freq,
double off); double off);
LIBLTE_API double cuhd_set_rx_freq_offset(void *h,
double freq,
double off);
LIBLTE_API int cuhd_recv(void *h, LIBLTE_API int cuhd_recv(void *h,
void *data, void *data,
uint32_t nsamples, uint32_t nsamples,
bool blocking); bool blocking);
LIBLTE_API int cuhd_recv_timed(void *h, LIBLTE_API int cuhd_recv_with_time(void *h,
void *data, void *data,
uint32_t nsamples, uint32_t nsamples,
bool blocking, bool blocking,
time_t *secs, time_t *secs,
double *frac_secs); double *frac_secs);
LIBLTE_API double cuhd_set_tx_srate(void *h, LIBLTE_API double cuhd_set_tx_srate(void *h,
double freq); double freq);
@ -86,12 +90,36 @@ LIBLTE_API double cuhd_set_tx_gain(void *h,
LIBLTE_API double cuhd_set_tx_freq(void *h, LIBLTE_API double cuhd_set_tx_freq(void *h,
double freq); double freq);
LIBLTE_API double cuhd_set_tx_freq_offset(void *h,
double freq,
double offset);
LIBLTE_API int cuhd_send(void *h,
void *data,
uint32_t nsamples,
bool blocking);
LIBLTE_API int cuhd_send(void *h, LIBLTE_API int cuhd_send(void *h,
void *data, void *data,
uint32_t nsamples, uint32_t nsamples,
bool blocking); bool blocking);
LIBLTE_API int cuhd_send_timed(void *h,
void *data,
int nsamples,
time_t secs,
double frac_secs);
LIBLTE_API int cuhd_send_timed2(void *h,
void *data,
int nsamples,
time_t secs,
double frac_secs,
bool is_start_of_burst,
bool is_end_of_burst);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -154,13 +154,6 @@ double cuhd_set_rx_srate(void *h, double freq)
{ {
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
handler->usrp->set_rx_rate(freq); handler->usrp->set_rx_rate(freq);
double ret = handler->usrp->get_rx_rate();
if ((int) ret != (int) freq) {
handler->usrp->set_master_clock_rate(freq);
handler->usrp->set_rx_rate(freq);
}
return freq; return freq;
} }
@ -178,9 +171,9 @@ double cuhd_set_rx_freq(void *h, double freq)
return freq; return freq;
} }
double cuhd_set_rx_freq_offset(void *h, double freq, double off) { double cuhd_set_rx_freq_offset(void *h, double freq, double off) {
cuhd_handler* handler = static_cast<cuhd_handler*>(h); cuhd_handler* handler = static_cast<cuhd_handler*>(h);
handler->usrp->set_rx_freq(uhd::tune_request_t(freq,off)); handler->usrp->set_rx_freq(uhd::tune_request_t(freq, off));
return handler->usrp->get_rx_freq(); return handler->usrp->get_rx_freq();
} }
@ -213,41 +206,45 @@ int cuhd_recv(void *h, void *data, uint32_t nsamples, bool blocking)
} }
} }
int cuhd_recv_timed(void *h, int cuhd_recv_with_time(void *h,
void *data, void *data,
uint32_t nsamples, uint32_t nsamples,
int blocking, bool blocking,
time_t *secs, time_t *secs,
double *frac_secs) { double *frac_secs)
cuhd_handler* handler = static_cast<cuhd_handler*>(h); {
cuhd_handler *handler = static_cast < cuhd_handler * >(h);
uhd::rx_metadata_t md; uhd::rx_metadata_t md;
*secs = -1; uint32_t nof_packets = 0;
*frac_secs = -1; int ret = -1;
int p;
if (blocking) { if (blocking) {
int n=0; int n = 0, p;
complex_t *data_c = (complex_t*) data; complex_t *data_c = (complex_t *) data;
do { do {
p=handler->rx_stream->recv(&data_c[n], nsamples-n, md); p = handler->rx_stream->recv(&data_c[n], nsamples - n, md);
if (p == -1) { if (p == -1) {
return -1; return -1;
} }
if(*secs < 0){ n += p;
*secs = md.time_spec.get_full_secs(); #ifdef METADATA_VERBOSE
*frac_secs = md.time_spec.get_frac_secs(); if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
std::cout << "\nError code: " << md.to_pp_string() << "\n\n";
} }
n+=p; #endif
} while(n<nsamples); nof_packets++;
return n; } while (n < nsamples &&
md.error_code == uhd::rx_metadata_t::ERROR_CODE_NONE &&
nof_packets < 10);
ret = nsamples;
} else { } else {
p = handler->rx_stream->recv(data, nsamples, md, 0.0); ret = handler->rx_stream->recv(data, nsamples, md, 0.0);
}
if (secs && frac_secs) {
*secs = md.time_spec.get_full_secs(); *secs = md.time_spec.get_full_secs();
*frac_secs = md.time_spec.get_frac_secs(); *frac_secs = md.time_spec.get_frac_secs();
return p;
} }
return ret;
} }
double cuhd_set_tx_gain(void *h, double gain) double cuhd_set_tx_gain(void *h, double gain)
{ {
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
@ -269,6 +266,14 @@ double cuhd_set_tx_freq(void *h, double freq)
return handler->usrp->get_tx_freq(); return handler->usrp->get_tx_freq();
} }
double cuhd_set_tx_freq_offset(void *h, double freq, double off) {
cuhd_handler* handler = static_cast<cuhd_handler*>(h);
handler->usrp->set_tx_freq(uhd::tune_request_t(freq, off));
return handler->usrp->get_tx_freq();
}
int cuhd_send(void *h, void *data, uint32_t nsamples, bool blocking) int cuhd_send(void *h, void *data, uint32_t nsamples, bool blocking)
{ {
cuhd_handler *handler = static_cast < cuhd_handler * >(h); cuhd_handler *handler = static_cast < cuhd_handler * >(h);
@ -288,3 +293,30 @@ int cuhd_send(void *h, void *data, uint32_t nsamples, bool blocking)
return handler->tx_stream->send(data, nsamples, md, 0.0); return handler->tx_stream->send(data, nsamples, md, 0.0);
} }
} }
int cuhd_send_timed(void *h,
void *data,
int nsamples,
time_t secs,
double frac_secs)
{
return cuhd_send_timed2(h, data, nsamples, secs, frac_secs, true, true);
}
int cuhd_send_timed2(void *h,
void *data,
int nsamples,
time_t secs,
double frac_secs,
bool is_start_of_burst,
bool is_end_of_burst)
{
cuhd_handler* handler = static_cast<cuhd_handler*>(h);
uhd::tx_metadata_t md;
md.start_of_burst = is_start_of_burst;
md.end_of_burst = is_end_of_burst;
md.has_time_spec = true;
md.time_spec = uhd::time_spec_t(secs, frac_secs);
return handler->tx_stream->send(data, nsamples, md);
}

@ -83,6 +83,9 @@ IF(${CUHD_FIND} GREATER -1)
add_executable(cell_search cell_search.c cuhd_utils.c) add_executable(cell_search cell_search.c cuhd_utils.c)
target_link_libraries(cell_search lte_rrc lte_phy cuhd ) target_link_libraries(cell_search lte_rrc lte_phy cuhd )
add_executable(prach_ue prach_ue.c cuhd_utils.c)
target_link_libraries(prach_ue lte_rrc lte_phy cuhd)
add_executable(cell_measurement cell_measurement.c cuhd_utils.c) add_executable(cell_measurement cell_measurement.c cuhd_utils.c)
target_link_libraries(cell_measurement cuhd lte_rrc lte_phy) target_link_libraries(cell_measurement cuhd lte_rrc lte_phy)

@ -120,13 +120,11 @@ int parse_args(prog_args_t *args, int argc, char **argv) {
/* TODO: Do something with the output data */ /* TODO: Do something with the output data */
uint8_t data[10000], data_unpacked[1000]; uint8_t data[10000], data_unpacked[1000];
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples, timestamp_t *q) {
DEBUG(" ---- Receive %d samples ---- \n", nsamples); DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return cuhd_recv(h, data, nsamples, 1); return cuhd_recv(h, data, nsamples, 1);
} }
extern float mean_exec_time;
enum receiver_state { DECODE_MIB, DECODE_SIB, MEASURE} state; enum receiver_state { DECODE_MIB, DECODE_SIB, MEASURE} state;
#define MAX_SINFO 10 #define MAX_SINFO 10
@ -196,7 +194,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error initiating ue_sync\n"); fprintf(stderr, "Error initiating ue_sync\n");
return -1; return -1;
} }
if (ue_dl_init(&ue_dl, cell, 1234)) { if (ue_dl_init(&ue_dl, cell)) {
fprintf(stderr, "Error initiating UE downlink processing module\n"); fprintf(stderr, "Error initiating UE downlink processing module\n");
return -1; return -1;
} }
@ -261,15 +259,15 @@ int main(int argc, char **argv) {
case DECODE_SIB: case DECODE_SIB:
/* We are looking for SI Blocks, search only in appropiate places */ /* We are looking for SI Blocks, search only in appropiate places */
if ((ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) { if ((ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) {
n = ue_dl_decode_sib(&ue_dl, sf_buffer, data, ue_sync_get_sfidx(&ue_sync), n = ue_dl_decode_rnti_rv(&ue_dl, sf_buffer, data, ue_sync_get_sfidx(&ue_sync), SIRNTI,
((int) ceilf((float)3*(((sfn)/2)%4)/2))%4); ((int) ceilf((float)3*(((sfn)/2)%4)/2))%4);
if (n < 0) { if (n < 0) {
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout); fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
return -1; return -1;
} else if (n == 0) { } else if (n == 0) {
printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, ExecTime: %5.1f us, NOI: %.2f, PDCCH-Det: %.3f\r", printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, NOI: %.2f, PDCCH-Det: %.3f\r",
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000, ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
mean_exec_time, pdsch_average_noi(&ue_dl.pdsch), sch_average_noi(&ue_dl.pdsch.dl_sch),
(float) ue_dl.nof_pdcch_detected/nof_trials); (float) ue_dl.nof_pdcch_detected/nof_trials);
nof_trials++; nof_trials++;
} else { } else {

@ -111,7 +111,7 @@ void parse_args(int argc, char **argv) {
} }
} }
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples, timestamp_t *t) {
DEBUG(" ---- Receive %d samples ---- \n", nsamples); DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return cuhd_recv(h, data, nsamples, 1); return cuhd_recv(h, data, nsamples, 1);
} }

@ -43,7 +43,7 @@
#ifndef DISABLE_UHD #ifndef DISABLE_UHD
#include "liblte/cuhd/cuhd.h" #include "liblte/cuhd/cuhd.h"
int cuhd_recv_wrapper_cs(void *h, void *data, uint32_t nsamples) { int cuhd_recv_wrapper_cs(void *h, void *data, uint32_t nsamples, timestamp_t *t) {
DEBUG(" ---- Receive %d samples ---- \n", nsamples); DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return cuhd_recv(h, data, nsamples, 1); return cuhd_recv(h, data, nsamples, 1);
} }

@ -74,7 +74,7 @@ pbch_t pbch;
pcfich_t pcfich; pcfich_t pcfich;
pdcch_t pdcch; pdcch_t pdcch;
pdsch_t pdsch; pdsch_t pdsch;
pdsch_harq_t harq_process; harq_t harq_process;
regs_t regs; regs_t regs;
ra_pdsch_t ra_dl; ra_pdsch_t ra_dl;
@ -220,6 +220,7 @@ void base_init() {
fprintf(stderr, "Error creating iFFT object\n"); fprintf(stderr, "Error creating iFFT object\n");
exit(-1); exit(-1);
} }
lte_fft_set_normalize(&ifft, true);
if (pbch_init(&pbch, cell)) { if (pbch_init(&pbch, cell)) {
fprintf(stderr, "Error creating PBCH object\n"); fprintf(stderr, "Error creating PBCH object\n");
exit(-1); exit(-1);
@ -252,7 +253,7 @@ void base_init() {
pdsch_set_rnti(&pdsch, 1234); pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
exit(-1); exit(-1);
} }
@ -260,7 +261,7 @@ void base_init() {
void base_free() { void base_free() {
pdsch_harq_free(&harq_process); harq_free(&harq_process);
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdcch_free(&pdcch); pdcch_free(&pdcch);
regs_free(&regs); regs_free(&regs);
@ -312,8 +313,8 @@ uint32_t prbset_to_bitmask() {
return reverse(mask)>>(32-nb); return reverse(mask)>>(32-nb);
} }
int update_radl() { int update_radl(uint32_t sf_idx) {
ra_prb_t prb_alloc; ra_dl_alloc_t prb_alloc;
bzero(&ra_dl, sizeof(ra_pdsch_t)); bzero(&ra_dl, sizeof(ra_pdsch_t));
ra_dl.harq_process = 0; ra_dl.harq_process = 0;
@ -323,15 +324,15 @@ int update_radl() {
ra_dl.alloc_type = alloc_type0; ra_dl.alloc_type = alloc_type0;
ra_dl.type0_alloc.rbg_bitmask = prbset_to_bitmask(); ra_dl.type0_alloc.rbg_bitmask = prbset_to_bitmask();
ra_prb_get_dl(&prb_alloc, &ra_dl, cell.nof_prb); ra_dl_alloc(&prb_alloc, &ra_dl, cell.nof_prb);
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, 1, cell.nof_prb<10?(cfi+1):cfi, CPNORM); ra_dl_alloc_re(&prb_alloc, cell.nof_prb, 1, cell.nof_prb<10?(cfi+1):cfi, CPNORM);
ra_mcs_from_idx_dl(mcs_idx, prb_alloc.slot[0].nof_prb, &ra_dl.mcs); ra_mcs_from_idx_dl(mcs_idx, prb_alloc.slot[0].nof_prb, &ra_dl.mcs);
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb); ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
printf("Type new MCS index and press Enter: "); fflush(stdout); printf("Type new MCS index and press Enter: "); fflush(stdout);
pdsch_harq_reset(&harq_process); harq_reset(&harq_process);
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) { if (harq_setup_dl(&harq_process, ra_dl.mcs, ra_dl.rv_idx, sf_idx, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n"); fprintf(stderr, "Error configuring HARQ process\n");
return -1; return -1;
} }
@ -340,7 +341,7 @@ int update_radl() {
} }
/* Read new MCS from stdin */ /* Read new MCS from stdin */
int update_control() { int update_control(uint32_t sf_idx) {
char input[128]; char input[128];
fd_set set; fd_set set;
@ -380,11 +381,11 @@ int update_control() {
mcs_idx = atoi(input); mcs_idx = atoi(input);
} }
bzero(input,sizeof(input)); bzero(input,sizeof(input));
if (update_radl()) { if (update_radl(sf_idx)) {
printf("Trying with last known MCS index\n"); printf("Trying with last known MCS index\n");
mcs_idx = last_mcs_idx; mcs_idx = last_mcs_idx;
prbset_num = last_prbset_num; prbset_num = last_prbset_num;
return update_radl(); return update_radl(sf_idx);
} }
} }
return 0; return 0;
@ -437,7 +438,7 @@ void *net_thread_fnc(void *arg) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
int nf, sf_idx, N_id_2; int nf=0, sf_idx=0, N_id_2=0;
cf_t pss_signal[PSS_LEN]; cf_t pss_signal[PSS_LEN];
float sss_signal0[SSS_LEN]; // for subframe 0 float sss_signal0[SSS_LEN]; // for subframe 0
float sss_signal5[SSS_LEN]; // for subframe 5 float sss_signal5[SSS_LEN]; // for subframe 5
@ -498,7 +499,7 @@ int main(int argc, char **argv) {
} }
#endif #endif
if (update_radl()) { if (update_radl(sf_idx)) {
exit(-1); exit(-1);
} }
@ -540,7 +541,7 @@ int main(int argc, char **argv) {
pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx); pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx);
/* Update DL resource allocation from control port */ /* Update DL resource allocation from control port */
if (update_control(&ra_dl)) { if (update_control(sf_idx)) {
fprintf(stderr, "Error updating parameters from control port\n"); fprintf(stderr, "Error updating parameters from control port\n");
} }
@ -570,7 +571,7 @@ int main(int argc, char **argv) {
exit(-1); exit(-1);
} }
if (pdsch_encode(&pdsch, data, sf_symbols, sf_idx, &harq_process, ra_dl.rv_idx)) { if (pdsch_encode(&pdsch, &harq_process, data, sf_symbols)) {
fprintf(stderr, "Error encoding PDSCH\n"); fprintf(stderr, "Error encoding PDSCH\n");
exit(-1); exit(-1);
} }
@ -589,6 +590,9 @@ int main(int argc, char **argv) {
/* 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);
float norm_factor = (float) cell.nof_prb/15/sqrtf(ra_dl.prb_alloc.slot[0].nof_prb);
vec_sc_prod_cfc(output_buffer, uhd_amp*norm_factor, output_buffer, SF_LEN_PRB(cell.nof_prb));
/* send to file or usrp */ /* send to file or usrp */
if (output_file_name) { if (output_file_name) {
if (!null_file_sink) { if (!null_file_sink) {

@ -83,6 +83,7 @@ typedef struct {
uint32_t file_nof_prb; uint32_t file_nof_prb;
char *uhd_args; char *uhd_args;
float uhd_freq; float uhd_freq;
float uhd_freq_offset;
float uhd_gain; float uhd_gain;
int net_port; int net_port;
char *net_address; char *net_address;
@ -98,6 +99,7 @@ void args_default(prog_args_t *args) {
args->file_nof_prb = 6; args->file_nof_prb = 6;
args->uhd_args = ""; args->uhd_args = "";
args->uhd_freq = -1.0; args->uhd_freq = -1.0;
args->uhd_freq = 8000000.0;
args->uhd_gain = 60.0; args->uhd_gain = 60.0;
args->net_port = -1; args->net_port = -1;
args->net_address = "127.0.0.1"; args->net_address = "127.0.0.1";
@ -107,9 +109,10 @@ void args_default(prog_args_t *args) {
void usage(prog_args_t *args, char *prog) { void usage(prog_args_t *args, char *prog) {
printf("Usage: %s [agildnruv] -f rx_frequency (in Hz) | -i input_file\n", prog); printf("Usage: %s [agildnruv] -f rx_frequency (in Hz) | -i input_file\n", prog);
#ifndef DISABLE_GRAPHICS #ifndef DISABLE_UHD
printf("\t-a UHD args [Default %s]\n", args->uhd_args); printf("\t-a UHD args [Default %s]\n", args->uhd_args);
printf("\t-g UHD RX gain [Default %.2f dB]\n", args->uhd_gain); printf("\t-g UHD RX gain [Default %.2f dB]\n", args->uhd_gain);
printf("\t-o UHD RX freq offset [Default %.1f MHz]\n", args->uhd_freq_offset/1000000);
#else #else
printf("\t UHD is disabled. CUHD library not available\n"); printf("\t UHD is disabled. CUHD library not available\n");
#endif #endif
@ -133,7 +136,7 @@ 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, "aglipdnvrfuUsS")) != -1) { while ((opt = getopt(argc, argv, "aoglipdnvrfuUsS")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
args->input_file_name = argv[optind]; args->input_file_name = argv[optind];
@ -147,6 +150,9 @@ void parse_args(prog_args_t *args, int argc, char **argv) {
case 'g': case 'g':
args->uhd_gain = atof(argv[optind]); args->uhd_gain = atof(argv[optind]);
break; break;
case 'o':
args->uhd_freq_offset = atof(argv[optind]);
break;
case 'f': case 'f':
args->uhd_freq = atof(argv[optind]); args->uhd_freq = atof(argv[optind]);
break; break;
@ -202,7 +208,7 @@ void sig_int_handler(int signo)
} }
#ifndef DISABLE_UHD #ifndef DISABLE_UHD
int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples) { int cuhd_recv_wrapper(void *h, void *data, uint32_t nsamples, timestamp_t *t) {
DEBUG(" ---- Receive %d samples ---- \n", nsamples); DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return cuhd_recv(h, data, nsamples, 1); return cuhd_recv(h, data, nsamples, 1);
} }
@ -261,7 +267,7 @@ int main(int argc, char **argv) {
cuhd_set_rx_gain(uhd, prog_args.uhd_gain); cuhd_set_rx_gain(uhd, prog_args.uhd_gain);
/* set receiver frequency */ /* set receiver frequency */
cuhd_set_rx_freq(uhd, (double) prog_args.uhd_freq); cuhd_set_rx_freq_offset(uhd, (double) prog_args.uhd_freq, prog_args.uhd_freq_offset);
cuhd_rx_wait_lo_locked(uhd); cuhd_rx_wait_lo_locked(uhd);
printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000); printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000);
@ -320,7 +326,7 @@ int main(int argc, char **argv) {
#endif #endif
} }
if (ue_dl_init(&ue_dl, cell, prog_args.rnti==SIRNTI?1:prog_args.rnti)) { // This is the User RNTI if (ue_dl_init(&ue_dl, cell)) { // This is the User RNTI
fprintf(stderr, "Error initiating UE downlink processing module\n"); fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1); exit(-1);
} }
@ -394,7 +400,7 @@ int main(int argc, char **argv) {
if (prog_args.rnti != SIRNTI) { if (prog_args.rnti != SIRNTI) {
n = ue_dl_decode(&ue_dl, sf_buffer, data_packed, ue_sync_get_sfidx(&ue_sync)); n = ue_dl_decode(&ue_dl, sf_buffer, data_packed, ue_sync_get_sfidx(&ue_sync));
} else { } else {
n = ue_dl_decode_sib(&ue_dl, sf_buffer, data_packed, ue_sync_get_sfidx(&ue_sync), n = ue_dl_decode_rnti_rv(&ue_dl, sf_buffer, data_packed, ue_sync_get_sfidx(&ue_sync), SIRNTI,
((int) ceilf((float)3*(((sfn)/2)%4)/2))%4); ((int) ceilf((float)3*(((sfn)/2)%4)/2))%4);
} }
if (n < 0) { if (n < 0) {
@ -422,6 +428,7 @@ int main(int argc, char **argv) {
rsrp = 0; rsrp = 0;
} }
#ifdef adjust_estimator
/* Adjust channel estimator based on SNR */ /* Adjust channel estimator based on SNR */
if (10*log10(snr) < 5.0) { if (10*log10(snr) < 5.0) {
float f_low_snr[5]={0.05, 0.15, 0.6, 0.15, 0.05}; float f_low_snr[5]={0.05, 0.15, 0.6, 0.15, 0.05};
@ -433,7 +440,7 @@ int main(int argc, char **argv) {
float f_high_snr[3]={0.05, 0.9, 0.05}; float f_high_snr[3]={0.05, 0.9, 0.05};
chest_dl_set_filter_freq(&ue_dl.chest, f_high_snr, 3); chest_dl_set_filter_freq(&ue_dl.chest, f_high_snr, 3);
} }
#endif
} }
if (ue_sync_get_sfidx(&ue_sync) != 5 && ue_sync_get_sfidx(&ue_sync) != 0) { if (ue_sync_get_sfidx(&ue_sync) != 5 && ue_sync_get_sfidx(&ue_sync) != 0) {
@ -524,7 +531,7 @@ void *plot_thread_run(void *arg) {
while(1) { while(1) {
sem_wait(&plot_sem); sem_wait(&plot_sem);
uint32_t nof_symbols = ue_dl.harq_process[0].prb_alloc.re_sf[plot_sf_idx]; uint32_t nof_symbols = ue_dl.harq_process[0].dl_alloc.re_sf[plot_sf_idx];
for (i = 0; i < nof_re; i++) { for (i = 0; i < nof_re; i++) {
tmp_plot[i] = 20 * log10f(cabsf(ue_dl.sf_symbols[i])); tmp_plot[i] = 20 * log10f(cabsf(ue_dl.sf_symbols[i]));
if (isinf(tmp_plot[i])) { if (isinf(tmp_plot[i])) {

@ -0,0 +1,686 @@
/**
*
* \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 <assert.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include "liblte/rrc/rrc.h"
#include "liblte/phy/phy.h"
#include "liblte/cuhd/cuhd.h"
#include "cuhd_utils.h"
cell_search_cfg_t cell_detect_config = {
5000,
100, // nof_frames_total
16.0 // threshold
};
#define B210_DEFAULT_GAIN 40.0
#define B210_DEFAULT_GAIN_CORREC 110.0 // Gain of the Rx chain when the gain is set to 40
float gain_offset = B210_DEFAULT_GAIN_CORREC;
/**********************************************************************
* Program arguments processing
***********************************************************************/
typedef struct {
int nof_subframes;
int force_N_id_2;
uint32_t file_nof_prb;
uint32_t preamble_idx;
float beta_prach;
float ta_usec;
float beta_pusch;
char *uhd_args;
float uhd_rx_freq;
float uhd_tx_freq;
float uhd_tx_freq_offset;
float uhd_tx_gain;
float uhd_rx_gain;
}prog_args_t;
void args_default(prog_args_t *args) {
args->nof_subframes = -1;
args->force_N_id_2 = -1; // Pick the best
args->file_nof_prb = 6;
args->beta_prach = 0.005;
args->beta_pusch = 2.0;
args->ta_usec = -1.0;
args->preamble_idx = 7;
args->uhd_args = "";
args->uhd_rx_freq = 2112500000.0;
args->uhd_tx_freq = 1922500000.0;
args->uhd_tx_freq_offset = 8000000.0;
args->uhd_tx_gain = 60.0;
args->uhd_rx_gain = 60.0;
}
void usage(prog_args_t *args, char *prog) {
printf("Usage: %s [agfFbrlpnv]\n", prog);
printf("\t-a UHD args [Default %s]\n", args->uhd_args);
printf("\t-g UHD TX/RX gain [Default %.2f dB]\n", args->uhd_rx_gain);
printf("\t-G UHD TX/RX gain [Default %.2f dB]\n", args->uhd_tx_gain);
printf("\t-f UHD RX freq [Default %.1f MHz]\n", args->uhd_rx_freq/1000000);
printf("\t-F UHD TX freq [Default %.1f MHz]\n", args->uhd_tx_freq/1000000);
printf("\t-b beta PRACH (transmission amplitude) [Default %f]\n",args->beta_prach);
printf("\t-B beta PUSCH (transmission amplitude) [Default %f]\n",args->beta_pusch);
printf("\t-t TA usec (time advance, -1 from RAR) [Default %f]\n",args->ta_usec);
printf("\t-p PRACH preamble idx [Default %d]\n",args->preamble_idx);
printf("\t-l Force N_id_2 [Default best]\n");
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
printf("\t-v [set verbose to debug, default none]\n");
}
void parse_args(prog_args_t *args, int argc, char **argv) {
int opt;
args_default(args);
while ((opt = getopt(argc, argv, "agGfFplnvbBt")) != -1) {
switch (opt) {
case 'a':
args->uhd_args = argv[optind];
break;
case 'b':
args->beta_prach = atof(argv[optind]);
break;
case 'B':
args->beta_pusch = atof(argv[optind]);
break;
case 't':
args->ta_usec = atof(argv[optind]);
break;
case 'g':
args->uhd_rx_gain = atof(argv[optind]);
break;
case 'G':
args->uhd_tx_gain = atof(argv[optind]);
break;
case 'f':
args->uhd_rx_freq = atof(argv[optind]);
break;
case 'F':
args->uhd_tx_freq = atof(argv[optind]);
break;
case 'n':
args->nof_subframes = atoi(argv[optind]);
break;
case 'p':
args->preamble_idx = atoi(argv[optind]);
break;
case 'l':
args->force_N_id_2 = atoi(argv[optind]);
break;
case 'v':
verbose++;
break;
default:
usage(args, argv[0]);
exit(-1);
}
}
if (args->uhd_tx_freq < 0 && args->uhd_rx_freq < 0) {
usage(args, argv[0]);
exit(-1);
}
}
/**********************************************************************/
/* TODO: Do something with the output data */
uint8_t data_rx[20000];
bool go_exit = false;
void sig_int_handler(int signo)
{
if (signo == SIGINT) {
go_exit = true;
}
}
int cuhd_recv_wrapper_timed(void *h, void *data, uint32_t nsamples, timestamp_t *uhd_time) {
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return cuhd_recv_with_time(h, data, nsamples, true, &uhd_time->full_secs, &uhd_time->frac_secs);
}
extern float mean_exec_time;
enum receiver_state { DECODE_MIB, SEND_PRACH, RECV_RAR, RECV_CONNSETUP} state;
#define NOF_PRACH_SEQUENCES 52
ue_dl_t ue_dl;
ue_ul_t ue_ul;
ue_sync_t ue_sync;
prach_t prach;
int prach_buffer_len;
prog_args_t prog_args;
uint32_t sfn = 0; // system frame number
cf_t *sf_buffer = NULL;
typedef enum{
rar_tpc_n6dB = 0,
rar_tpc_n4dB,
rar_tpc_n2dB,
rar_tpc_0dB,
rar_tpc_2dB,
rar_tpc_4dB,
rar_tpc_6dB,
rar_tpc_8dB,
rar_tpc_n_items,
}rar_tpc_command_t;
static const char tpc_command_text[rar_tpc_n_items][8] = {"-6dB", "-4dB", "-2dB", "0dB", "2dB", "4dB", "6dB", "8dB"};
typedef enum{
rar_header_type_bi = 0,
rar_header_type_rapid,
rar_header_type_n_items,
}rar_header_t;
static const char rar_header_text[rar_header_type_n_items][8] = {"BI", "RAPID"};
typedef struct {
rar_header_t hdr_type;
bool hopping_flag;
rar_tpc_command_t tpc_command;
bool ul_delay;
bool csi_req;
uint16_t rba;
uint16_t timing_adv_cmd;
uint16_t temp_c_rnti;
uint8_t mcs;
uint8_t RAPID;
uint8_t BI;
}rar_msg_t;
char *bool_to_string(bool x) {
if (x) {
return "Enabled";
} else {
return "Disabled";
}
}
void rar_msg_fprint(FILE *stream, rar_msg_t *msg)
{
fprintf(stream, "Header type: %s\n", rar_header_text[msg->hdr_type]);
fprintf(stream, "Hopping flag: %s\n", bool_to_string(msg->hopping_flag));
fprintf(stream, "TPC command: %s\n", tpc_command_text[msg->tpc_command]);
fprintf(stream, "UL delay: %s\n", bool_to_string(msg->ul_delay));
fprintf(stream, "CSI required: %s\n", bool_to_string(msg->csi_req));
fprintf(stream, "RBA: %d\n", msg->rba);
fprintf(stream, "TA: %d\n", msg->timing_adv_cmd);
fprintf(stream, "T-CRNTI: %d\n", msg->temp_c_rnti);
fprintf(stream, "MCS: %d\n", msg->mcs);
fprintf(stream, "RAPID: %d\n", msg->RAPID);
fprintf(stream, "BI: %d\n", msg->BI);
}
int rar_unpack(uint8_t *buffer, rar_msg_t *msg)
{
int ret = LIBLTE_ERROR;
uint8_t *ptr = buffer;
if(buffer != NULL &&
msg != NULL)
{
ptr++;
msg->hdr_type = *ptr++;
if(msg->hdr_type == rar_header_type_bi) {
ptr += 2;
msg->BI = bit_unpack(&ptr, 4);
ret = LIBLTE_SUCCESS;
} else if (msg->hdr_type == rar_header_type_rapid) {
msg->RAPID = bit_unpack(&ptr, 6);
ptr++;
msg->timing_adv_cmd = bit_unpack(&ptr, 11);
msg->hopping_flag = *ptr++;
msg->rba = bit_unpack(&ptr, 10);
msg->mcs = bit_unpack(&ptr, 4);
msg->tpc_command = (rar_tpc_command_t) bit_unpack(&ptr, 3);
msg->ul_delay = *ptr++;
msg->csi_req = *ptr++;
msg->temp_c_rnti = bit_unpack(&ptr, 16);
ret = LIBLTE_SUCCESS;
}
}
return(ret);
}
#define kk
#define use_usrp
int main(int argc, char **argv) {
int ret;
lte_cell_t cell;
int64_t sf_cnt;
ue_mib_t ue_mib;
void *uhd;
int n;
uint8_t bch_payload[BCH_PAYLOAD_LEN], bch_payload_unpacked[BCH_PAYLOAD_LEN];
uint32_t sfn_offset;
rar_msg_t rar_msg;
ra_pusch_t ra_pusch;
uint32_t rar_window_start = 0, rar_trials = 0, rar_window_stop = 0;
timestamp_t uhd_time;
timestamp_t next_tx_time;
const uint8_t conn_request_msg[] = {0x20, 0x06, 0x1F, 0x5C, 0x2C, 0x04, 0xB2, 0xAC, 0xF6, 0x00, 0x00, 0x00};
uint8_t data[1000];
cf_t *prach_buffer;
parse_args(&prog_args, argc, argv);
#ifdef use_usrp
printf("Opening UHD device...\n");
if (cuhd_open(prog_args.uhd_args, &uhd)) {
fprintf(stderr, "Error opening uhd\n");
exit(-1);
}
/* Set receiver gain */
float x = cuhd_set_rx_gain(uhd, prog_args.uhd_rx_gain);
printf("Set RX gain to %.1f dB\n", x);
x = cuhd_set_tx_gain(uhd, prog_args.uhd_tx_gain);
printf("Set TX gain to %.1f dB\n", x);
/* set receiver frequency */
cuhd_set_rx_freq(uhd, (double) prog_args.uhd_rx_freq);
cuhd_rx_wait_lo_locked(uhd);
printf("Tunning RX receiver to %.3f MHz\n", (double ) prog_args.uhd_rx_freq/1000000);
cuhd_set_tx_freq_offset(uhd, prog_args.uhd_tx_freq, prog_args.uhd_tx_freq_offset);
printf("Tunning TX receiver to %.3f MHz\n", (double ) prog_args.uhd_tx_freq/1000000);
#endif
#ifdef kk
ret = cuhd_search_and_decode_mib(uhd, &cell_detect_config, prog_args.force_N_id_2, &cell);
if (ret < 0) {
fprintf(stderr, "Error searching for cell\n");
exit(-1);
} else if (ret == 0) {
printf("Cell not found\n");
exit(0);
}
#else
cell.id = 1;
cell.nof_ports = 1;
cell.nof_prb = 25;
cell.cp = CPNORM;
cell.phich_length = PHICH_NORM;
cell.phich_resources = R_1;
#endif
#ifdef use_usrp
/* set sampling frequency */
int srate = lte_sampling_freq_hz(cell.nof_prb);
if (srate != -1) {
cuhd_set_rx_srate(uhd, (double) srate);
cuhd_set_tx_srate(uhd, (double) srate);
} else {
fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb);
exit(-1);
}
INFO("Stopping UHD and flushing buffer...\r",0);
cuhd_stop_rx_stream(uhd);
cuhd_flush_buffer(uhd);
#endif
if (ue_mib_init(&ue_mib, cell)) {
fprintf(stderr, "Error initaiting UE MIB decoder\n");
exit(-1);
}
if (prach_init(&prach, lte_symbol_sz(cell.nof_prb), 0, 0, false, 1)) {
fprintf(stderr, "Error initializing PRACH\n");
exit(-1);
}
prach_buffer_len = prach.N_seq + prach.N_cp;
prach_buffer = vec_malloc(prach_buffer_len*sizeof(cf_t));
if(!prach_buffer) {
perror("maloc");
exit(-1);
}
if(prach_gen(&prach, prog_args.preamble_idx, 0, prog_args.beta_prach, prach_buffer)){
fprintf(stderr, "Error generating prach sequence\n");
return -1;
}
if (ue_ul_init(&ue_ul, cell)) {
fprintf(stderr, "Error initiating UE UL\n");
exit(-1);
}
pusch_hopping_cfg_t hop_cfg;
bzero(&hop_cfg, sizeof(pusch_hopping_cfg_t));
refsignal_drms_pusch_cfg_t drms_cfg;
bzero(&drms_cfg, sizeof(refsignal_drms_pusch_cfg_t));
drms_cfg.beta_pusch = 1.0;
drms_cfg.group_hopping_en = false;
drms_cfg.sequence_hopping_en = false;
drms_cfg.delta_ss = 0;
drms_cfg.cyclic_shift = 0;
drms_cfg.cyclic_shift_for_drms = 0;
drms_cfg.en_drms_2 = false;
ue_ul_set_pusch_cfg(&ue_ul, &drms_cfg, &hop_cfg);
cf_t *ul_signal = vec_malloc(sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
if (!ul_signal) {
perror("malloc");
exit(-1);
}
bzero(ul_signal, sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
if (ue_dl_init(&ue_dl, cell)) {
fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1);
}
/* Initialize subframe counter */
sf_cnt = 0;
#ifdef use_usrp
if (ue_sync_init(&ue_sync, cell, cuhd_recv_wrapper_timed, uhd)) {
fprintf(stderr, "Error initiating ue_sync\n");
exit(-1);
}
cuhd_start_rx_stream(uhd);
#endif
uint16_t ra_rnti;
uint32_t conn_setup_trial = 0;
uint32_t ul_sf_idx = 0;
#ifdef kk
// Register Ctrl+C handler
signal(SIGINT, sig_int_handler);
state = DECODE_MIB;
#else
state = RECV_RAR;
#endif
/* Main loop */
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
#ifdef kk
ret = ue_sync_get_buffer(&ue_sync, &sf_buffer);
if (ret < 0) {
fprintf(stderr, "Error calling ue_sync_work()\n");
}
#else
ret = 1;
timestamp_t rx_time, tx_time;
cf_t dummy[4];
#endif
/* ue_sync_get_buffer returns 1 if successfully read 1 aligned subframe */
if (ret == 1) {
if (state != RECV_RAR) {
/* Run FFT for all subframe data */
lte_fft_run_sf(&ue_dl.fft, sf_buffer, ue_dl.sf_symbols);
/* Get channel estimates for each port */
chest_dl_estimate(&ue_dl.chest, ue_dl.sf_symbols, ue_dl.ce, ue_sync_get_sfidx(&ue_sync));
}
if (sf_cnt > 1000) {
switch (state) {
case DECODE_MIB:
if (ue_sync_get_sfidx(&ue_sync) == 0) {
pbch_decode_reset(&ue_mib.pbch);
n = ue_mib_decode(&ue_mib, sf_buffer, bch_payload_unpacked, NULL, &sfn_offset);
if (n < 0) {
fprintf(stderr, "Error decoding UE MIB\n");
exit(-1);
} else if (n == MIB_FOUND) {
bit_unpack_vector(bch_payload_unpacked, bch_payload, BCH_PAYLOAD_LEN);
bcch_bch_unpack(bch_payload, BCH_PAYLOAD_LEN, &cell, &sfn);
printf("Decoded MIB. SFN: %d, offset: %d\n", sfn, sfn_offset);
sfn = (sfn + sfn_offset)%1024;
state = SEND_PRACH;
}
}
break;
case SEND_PRACH:
#ifdef kk
if (((sfn%2) == 1) && (ue_sync_get_sfidx(&ue_sync) == 1)) {
ue_sync_get_last_timestamp(&ue_sync, &uhd_time);
timestamp_copy(&next_tx_time, &uhd_time);
timestamp_add(&next_tx_time, 0, 0.01); // send next frame (10 ms)
printf("Send prach sfn: %d. Last frame time = %.6f, send prach time = %.6f\n",
sfn, timestamp_real(&uhd_time), timestamp_real(&next_tx_time));
cuhd_send_timed(uhd, prach_buffer, prach_buffer_len,
next_tx_time.full_secs, next_tx_time.frac_secs);
ra_rnti = 2;
rar_window_start = sfn+1;
rar_window_stop = sfn+3;
state = RECV_RAR;
}
#else
cuhd_recv_with_time(uhd, dummy, 4, 1, &rx_time.full_secs, &rx_time.frac_secs);
timestamp_copy(&tx_time, &rx_time);
printf("Transmitting PRACH...\n");
vec_save_file("prach_tx", prach_buffers[7], prach_buffer_len*sizeof(cf_t));
while(1) {
timestamp_add(&tx_time, 0, 0.001); // send every (10 ms)
cuhd_send_timed(uhd, prach_buffers[7], prach_buffer_len,
tx_time.full_secs, tx_time.frac_secs);
}
#endif
break;
case RECV_RAR:
#ifdef kk
if ((sfn == rar_window_start && ue_sync_get_sfidx(&ue_sync) > 3) || sfn > rar_window_start) {
printf("Looking for RAR in sfn: %d sf_idx: %d\n", sfn, ue_sync_get_sfidx(&ue_sync));
n = ue_dl_decode_rnti(&ue_dl, sf_buffer, data_rx, ue_sync_get_sfidx(&ue_sync), ra_rnti);
if (n < 0) {
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
} else if (n > 0) {
rar_unpack(data_rx, &rar_msg);
//if (rar_msg.RAPID != prog_args.preamble_idx) {
// printf("Found RAR for sequence %d\n", rar_msg.RAPID);
//} else {
//cuhd_stop_rx_stream(uhd);
//cuhd_flush_buffer(uhd);
rar_msg_fprint(stdout, &rar_msg);
dci_rar_to_ra_ul(rar_msg.rba, rar_msg.mcs, rar_msg.hopping_flag, cell.nof_prb, &ra_pusch);
ra_pusch_fprint(stdout, &ra_pusch, cell.nof_prb);
ra_ul_alloc(&ra_pusch.prb_alloc, &ra_pusch, 0, cell.nof_prb);
ue_sync_get_last_timestamp(&ue_sync, &uhd_time);
bit_pack_vector((uint8_t*) conn_request_msg, data, ra_pusch.mcs.tbs);
uint32_t n_ta = lte_N_ta_new_rar(rar_msg.timing_adv_cmd);
printf("ta: %d, n_ta: %d\n", rar_msg.timing_adv_cmd, n_ta);
float time_adv_sec = TA_OFFSET+((float) n_ta)*LTE_TS;
if (prog_args.ta_usec >= 0) {
time_adv_sec = prog_args.ta_usec*1e-6;
}
#define N_TX 1
const uint32_t rv[N_TX]={0,2,3,1,0};
for (int i=0; i<N_TX;i++) {
ra_pusch.rv_idx = rv[i];
ul_sf_idx = (ue_sync_get_sfidx(&ue_sync)+6+i*8)%10;
n = ue_ul_pusch_encode_rnti(&ue_ul, &ra_pusch, data, ul_sf_idx, rar_msg.temp_c_rnti, ul_signal);
if (n < 0) {
fprintf(stderr, "Error encoding PUSCH\n");
exit(-1);
}
vec_sc_prod_cfc(ul_signal, prog_args.beta_pusch, ul_signal, SF_LEN_PRB(cell.nof_prb));
timestamp_copy(&next_tx_time, &uhd_time);
timestamp_add(&next_tx_time, 0, 0.006 + i*0.008 - time_adv_sec); // send after 6 sub-frames (6 ms)
printf("Send %d samples PUSCH sfn: %d. RV_idx=%d, Last frame time = %.6f "
"send PUSCH time = %.6f TA: %.1f us\n",
SF_LEN_PRB(cell.nof_prb), sfn, ra_pusch.rv_idx,
timestamp_real(&uhd_time),
timestamp_real(&next_tx_time), time_adv_sec*1000000);
cuhd_send_timed(uhd, ul_signal, SF_LEN_PRB(cell.nof_prb),
next_tx_time.full_secs, next_tx_time.frac_secs);
//cuhd_start_rx_stream(uhd);
state = RECV_CONNSETUP;
conn_setup_trial = 0;
// }
}
}
if (sfn >= rar_window_stop) {
state = SEND_PRACH;
rar_trials++;
if (rar_trials >= 1) {
go_exit = 1;
}
}
}
#else
ra_pusch.mcs.mod = LTE_QPSK;
ra_pusch.mcs.tbs = 94;
ra_pusch.rv_idx = 0;
ra_pusch.prb_alloc.freq_hopping = 0;
ra_pusch.prb_alloc.L_prb = 4;
ra_pusch.prb_alloc.n_prb[0] = 19;
ra_pusch.prb_alloc.n_prb[1] = 19;
uint32_t ul_sf_idx = 4;
printf("L: %d\n", ra_pusch.prb_alloc.L_prb);
// ue_ul_set_cfo(&ue_ul, sync_get_cfo(&ue_sync.strack));
bit_pack_vector((uint8_t*) conn_request_msg, data, ra_pusch.mcs.tbs);
n = ue_ul_pusch_encode_rnti(&ue_ul, &ra_pusch, data, ul_sf_idx, 111, ul_signal);
if (n < 0) {
fprintf(stderr, "Error encoding PUSCH\n");
exit(-1);
}
vec_save_file("pusch_tx", ul_signal, SF_LEN_PRB(cell.nof_prb)*sizeof(cf_t));
#ifdef use_usrp
cuhd_recv_with_time(uhd, dummy, 4, 1, &uhd_time.full_secs, &uhd_time.frac_secs);
timestamp_copy(&next_tx_time, &uhd_time);
while(1) {
timestamp_add(&next_tx_time, 0, 0.002); // send every 2 ms
cuhd_send_timed(uhd, ul_signal, SF_LEN_PRB(cell.nof_prb),
next_tx_time.full_secs, next_tx_time.frac_secs);
}
#else
exit(-1);
#endif
#endif
break;
case RECV_CONNSETUP:
if (ue_sync_get_sfidx(&ue_sync) == (ul_sf_idx+4)%10) {
//verbose=VERBOSE_DEBUG;
vec_save_file("connsetup",sf_buffer,SF_LEN_PRB(cell.nof_prb)*sizeof(cf_t));
} else {
//verbose=VERBOSE_NONE;
}
printf("Looking for ConnectionSetup in sfn: %d sf_idx: %d, RNTI: %d\n", sfn, ue_sync_get_sfidx(&ue_sync),rar_msg.temp_c_rnti);
n = ue_dl_decode_rnti(&ue_dl, sf_buffer, data_rx, ue_sync_get_sfidx(&ue_sync), rar_msg.temp_c_rnti);
if (n < 0) {
fprintf(stderr, "Error decoding UE DL\n");fflush(stdout);
} else if (n > 0) {
printf("Received ConnectionSetup len: %d.\n", n);
vec_fprint_hex(stdout, data_rx, n);
} else {
conn_setup_trial++;
if (conn_setup_trial == 20) {
go_exit = 1;
}
}
break;
}
if (ue_sync_get_sfidx(&ue_sync) == 9) {
sfn++;
if (sfn == 1024) {
sfn = 0;
}
}
}
} else if (ret == 0) {
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
sync_get_peak_value(&ue_sync.sfind),
ue_sync.frame_total_cnt, ue_sync.state);
}
sf_cnt++;
} // Main loop
ue_dl_free(&ue_dl);
ue_sync_free(&ue_sync);
ue_mib_free(&ue_mib);
cuhd_close(uhd);
printf("\nBye\n");
exit(0);
}

@ -66,6 +66,7 @@ typedef struct {
cf_t *tmp_noise; cf_t *tmp_noise;
cf_t *tmp_freqavg; cf_t *tmp_freqavg;
cf_t *tmp_timeavg[CHEST_MAX_FILTER_TIME_LEN]; cf_t *tmp_timeavg[CHEST_MAX_FILTER_TIME_LEN];
cf_t *tmp_timeavg_mult;
interp_linvec_t interp_linvec; interp_linvec_t interp_linvec;
interp_lin_t interp_lin; interp_lin_t interp_lin;

@ -51,7 +51,7 @@ typedef struct LIBLTE_API {
} refsignal_cs_t; } refsignal_cs_t;
LIBLTE_API int refsignal_cs_generate(refsignal_cs_t *q, LIBLTE_API int refsignal_cs_init(refsignal_cs_t *q,
lte_cell_t cell); lte_cell_t cell);
LIBLTE_API void refsignal_cs_free(refsignal_cs_t *q); LIBLTE_API void refsignal_cs_free(refsignal_cs_t *q);
@ -66,12 +66,12 @@ LIBLTE_API int refsignal_cs_get_sf(lte_cell_t cell,
cf_t *sf_symbols, cf_t *sf_symbols,
cf_t *pilots); cf_t *pilots);
LIBLTE_API uint32_t refsignal_fidx(lte_cell_t cell, LIBLTE_API uint32_t refsignal_cs_fidx(lte_cell_t cell,
uint32_t l, uint32_t l,
uint32_t port_id, uint32_t port_id,
uint32_t m); uint32_t m);
LIBLTE_API uint32_t refsignal_nsymbol(uint32_t l, LIBLTE_API uint32_t refsignal_cs_nsymbol(uint32_t l,
lte_cp_t cp, lte_cp_t cp,
uint32_t port_id); uint32_t port_id);

@ -26,58 +26,81 @@
*/ */
#ifndef REFSIGNAL_UL_ #ifndef REFSIGNAL_UL_
#define REFSIGNAL_DL_ #define REFSIGNAL_UL_
/* Object to manage Downlink reference signals for channel estimation. /* Object to manage Downlink reference signals for channel estimation.
* *
*/ */
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/phy/phch/pucch.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
typedef _Complex float cf_t; #define NOF_GROUPS_U 30
#define NOF_SEQUENCES_U 2
#define NOF_DELTA_SS 30
#define NOF_CSHIFT 8
// Number of references in a subframe: there are 2 symbols for port_id=0,1 x 2 slots x 2 refs per prb typedef _Complex float cf_t;
#define REFSIGNAL_NUM_SF(nof_prb, port_id) (((port_id)<2?8:4)*(nof_prb))
#define REFSIGNAL_MAX_NUM_SF(nof_prb) REFSIGNAL_NUM_SF(nof_prb, 0)
#define REFSIGNAL_PILOT_IDX(i,l,cell) (2*cell.nof_prb*(l)+(i)) typedef struct LIBLTE_API {
uint32_t cyclic_shift;
uint32_t cyclic_shift_for_drms;
uint32_t delta_ss;
bool en_drms_2;
float beta_pusch;
bool group_hopping_en;
bool sequence_hopping_en;
}refsignal_drms_pusch_cfg_t;
typedef struct LIBLTE_API {
float beta_pucch;
uint32_t nof_prb;
}refsignal_srs_cfg_t;
/** Cell-Specific Reference Signal */ /** Uplink DeModulation Reference Signal (DMRS) */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
lte_cell_t cell; lte_cell_t cell;
cf_t *pilots[2][NSUBFRAMES_X_FRAME]; // Saves the reference signal per subframe for ports 0,1 and ports 2,3 uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB];
} refsignal_cs_t; float *tmp_arg;
uint32_t n_prs_pusch[NOF_DELTA_SS][NSLOTS_X_FRAME]; // We precompute n_prs needed for cyclic shift alpha at refsignal_dl_init()
uint32_t f_gh[NSLOTS_X_FRAME];
LIBLTE_API int refsignal_cs_generate(refsignal_cs_t *q, uint32_t u_pucch[NSLOTS_X_FRAME];
lte_cell_t cell); uint32_t v_pusch[NSLOTS_X_FRAME][NOF_DELTA_SS];
} refsignal_ul_t;
LIBLTE_API void refsignal_cs_free(refsignal_cs_t *q);
LIBLTE_API int refsignal_cs_put_sf(lte_cell_t cell, LIBLTE_API int refsignal_ul_init(refsignal_ul_t *q,
uint32_t port_id, lte_cell_t cell);
cf_t *pilots,
cf_t *sf_symbols); LIBLTE_API void refsignal_ul_free(refsignal_ul_t *q);
LIBLTE_API int refsignal_cs_get_sf(lte_cell_t cell, LIBLTE_API bool refsignal_drms_pusch_cfg_isvalid(refsignal_ul_t *q,
uint32_t port_id, refsignal_drms_pusch_cfg_t *cfg,
cf_t *sf_symbols, uint32_t nof_prb);
cf_t *pilots);
LIBLTE_API void refsignal_drms_pusch_put(refsignal_ul_t *q,
LIBLTE_API uint32_t refsignal_fidx(lte_cell_t cell, refsignal_drms_pusch_cfg_t *cfg,
uint32_t l, cf_t *r_pusch,
uint32_t port_id, uint32_t nof_prb,
uint32_t m); uint32_t n_prb[2],
cf_t *sf_symbols);
LIBLTE_API uint32_t refsignal_nsymbol(uint32_t l,
lte_cp_t cp, LIBLTE_API int refsignal_dmrs_pusch_gen(refsignal_ul_t *q,
uint32_t port_id); refsignal_drms_pusch_cfg_t *cfg,
uint32_t nof_prb,
LIBLTE_API uint32_t refsignal_cs_v(uint32_t port_id, uint32_t sf_idx,
uint32_t ref_symbol_idx); cf_t *r_pusch);
LIBLTE_API uint32_t refsignal_cs_nof_symbols(uint32_t port_id); LIBLTE_API int refsignal_dmrs_pucch_gen(refsignal_ul_t *q,
pucch_cfg_t *cfg,
uint32_t sf_idx,
uint32_t n_rb,
cf_t *r_pucch) ;
LIBLTE_API void refsignal_srs_gen(refsignal_ul_t *q,
refsignal_srs_cfg_t *cfg,
uint32_t sf_idx,
cf_t *r_srs);
#endif #endif

@ -37,8 +37,6 @@
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/dft.h"
//#define LTE_FFT_NORMALIZE
typedef _Complex float cf_t; /* this is only a shortcut */ typedef _Complex float cf_t; /* this is only a shortcut */
/* This is common for both directions */ /* This is common for both directions */
@ -51,6 +49,9 @@ typedef struct LIBLTE_API{
uint32_t slot_sz; uint32_t slot_sz;
lte_cp_t cp; lte_cp_t cp;
cf_t *tmp; // for removing zero padding cf_t *tmp; // for removing zero padding
bool freq_shift;
cf_t *shift_buffer;
}lte_fft_t; }lte_fft_t;
LIBLTE_API int lte_fft_init(lte_fft_t *q, LIBLTE_API int lte_fft_init(lte_fft_t *q,
@ -81,4 +82,10 @@ LIBLTE_API void lte_ifft_run_sf(lte_fft_t *q,
cf_t *input, cf_t *input,
cf_t *output); cf_t *output);
LIBLTE_API int lte_fft_set_freq_shift(lte_fft_t *q,
float freq_shift);
LIBLTE_API void lte_fft_set_normalize(lte_fft_t *q,
bool normalize_enable);
#endif #endif

@ -41,10 +41,6 @@
#define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE #define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE
#define LTE_NULL_BIT 0
#define LTE_NULL_SYMBOL 2
#define LTE_NIL_SYMBOL 2
#define MAX_PORTS 4 #define MAX_PORTS 4
#define MAX_LAYERS 8 #define MAX_LAYERS 8
#define MAX_CODEWORDS 2 #define MAX_CODEWORDS 2
@ -56,9 +52,14 @@
typedef enum {CPNORM, CPEXT} lte_cp_t; typedef enum {CPNORM, CPEXT} lte_cp_t;
#define SIRNTI 0xFFFF
#define PRNTI 0xFFFE #define CRNTI_START 0x003D
#define MRNTI 0xFFFD #define CRNTI_END 0xFFF3
#define RARNTI_START 0x0001
#define RARNTI_END 0x003C
#define SIRNTI 0xFFFF
#define PRNTI 0xFFFE
#define MRNTI 0xFFFD
#define CELL_ID_UNKNOWN 1000 #define CELL_ID_UNKNOWN 1000
@ -97,6 +98,10 @@ typedef enum {CPNORM, CPEXT} lte_cp_t;
#define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp)) #define SLOT_LEN_RE(nof_prb, cp) (nof_prb*RE_X_RB*CP_NSYMB(cp))
#define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp)) #define SF_LEN_RE(nof_prb, cp) (2*SLOT_LEN_RE(nof_prb, cp))
#define TA_OFFSET (10e-6)
#define LTE_TS 1.0/(15000.0*2048)
#define SLOT_IDX_CPNORM(symbol_idx, symbol_sz) (symbol_idx==0?0:(symbol_sz + CP(symbol_sz, CPNORM_0_LEN) + \ #define SLOT_IDX_CPNORM(symbol_idx, symbol_sz) (symbol_idx==0?0:(symbol_sz + CP(symbol_sz, CPNORM_0_LEN) + \
(symbol_idx-1)*(symbol_sz+CP(symbol_sz, CPNORM_LEN)))) (symbol_idx-1)*(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)))
@ -118,12 +123,13 @@ typedef enum {CPNORM, CPEXT} lte_cp_t;
typedef _Complex float cf_t; typedef _Complex float cf_t;
typedef enum LIBLTE_API { PHICH_NORM, PHICH_EXT} phich_length_t; typedef enum LIBLTE_API { PHICH_NORM = 0, PHICH_EXT} phich_length_t;
typedef enum LIBLTE_API { R_1_6, R_1_2, R_1, R_2} phich_resources_t; typedef enum LIBLTE_API { R_1_6 = 0, R_1_2, R_1, R_2} phich_resources_t;
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
uint32_t nof_prb; uint32_t nof_prb;
uint32_t nof_ports; uint32_t nof_ports;
uint32_t bw_idx;
uint32_t id; uint32_t id;
lte_cp_t cp; lte_cp_t cp;
phich_length_t phich_length; phich_length_t phich_length;
@ -138,13 +144,12 @@ typedef enum LIBLTE_API {
LTE_BPSK = 0, LTE_QPSK = 1, LTE_QAM16 = 2, LTE_QAM64 = 3 LTE_BPSK = 0, LTE_QPSK = 1, LTE_QAM16 = 2, LTE_QAM64 = 3
} lte_mod_t; } lte_mod_t;
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
int id; int id;
float fd; float fd;
}lte_earfcn_t; } lte_earfcn_t;
LIBLTE_API enum band_geographical_area { enum band_geographical_area {
ALL, NAR, APAC, EMEA, JAPAN, CALA, NA ALL, NAR, APAC, EMEA, JAPAN, CALA, NA
}; };
@ -181,6 +186,11 @@ LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id,
uint32_t cell_id, uint32_t cell_id,
uint32_t nof_ports); uint32_t nof_ports);
LIBLTE_API uint32_t lte_N_ta_new_rar(uint32_t ta);
LIBLTE_API uint32_t lte_N_ta_new(uint32_t N_ta_old,
uint32_t ta);
LIBLTE_API int lte_cb_size(uint32_t index); LIBLTE_API int lte_cb_size(uint32_t index);
LIBLTE_API bool lte_cb_size_isvalid(uint32_t size); LIBLTE_API bool lte_cb_size_isvalid(uint32_t size);

@ -71,4 +71,10 @@ LIBLTE_API int sequence_pdsch(sequence_t *seq,
uint32_t cell_id, uint32_t cell_id,
uint32_t len); uint32_t len);
LIBLTE_API int sequence_pusch(sequence_t *seq,
unsigned short rnti,
uint32_t nslot,
uint32_t cell_id,
uint32_t len);
#endif #endif

@ -0,0 +1,53 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef TIMESTAMP_
#define TIMESTAMP_
#include <time.h>
#include "liblte/config.h"
/*!
* A simple timestamp struct with separate variables for full and frac seconds.
*
* Separate variables are used to avoid loss of precision in our frac seconds.
* Only positive timestamps are supported.
*/
typedef struct LIBLTE_API{
time_t full_secs;
double frac_secs;
}timestamp_t;
LIBLTE_API int timestamp_init(timestamp_t *t, time_t full_secs, double frac_secs);
LIBLTE_API int timestamp_copy(timestamp_t *dest, timestamp_t *src);
LIBLTE_API int timestamp_add(timestamp_t *t, time_t full_secs, double frac_secs);
LIBLTE_API int timestamp_sub(timestamp_t *t, time_t full_secs, double frac_secs);
LIBLTE_API double timestamp_real(timestamp_t *t);
#endif // TIMESTAMP_

@ -30,9 +30,13 @@
#include "liblte/config.h" #include "liblte/config.h"
#ifndef RX_NULL
#define RX_NULL 10000 #define RX_NULL 10000
#define TX_NULL 80 #endif
#ifndef TX_NULL
#define TX_NULL 100
#endif
LIBLTE_API int rm_conv_tx(uint8_t *input, LIBLTE_API int rm_conv_tx(uint8_t *input,
uint32_t in_len, uint32_t in_len,
uint8_t *output, uint8_t *output,

@ -55,7 +55,8 @@ LIBLTE_API int rm_turbo_rx(float *w_buff,
uint32_t in_len, uint32_t in_len,
float *output, float *output,
uint32_t out_len, uint32_t out_len,
uint32_t rv_idx); uint32_t rv_idx,
uint32_t nof_filler_bits);
/* High-level API */ /* High-level API */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {

@ -36,6 +36,10 @@
#define RATE 3 #define RATE 3
#define TOTALTAIL 12 #define TOTALTAIL 12
#ifndef TX_NULL
#define TX_NULL 100
#endif
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
uint32_t max_long_cb; uint32_t max_long_cb;
tc_interl_t interl; tc_interl_t interl;

@ -0,0 +1,66 @@
/**
*
* \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 DFTPREC_
#define DFTPREC_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/dft.h"
typedef _Complex float cf_t;
/* DFT-based Transform Precoding object */
typedef struct LIBLTE_API {
uint32_t max_prb;
dft_plan_t dft_plan[MAX_PRB];
dft_plan_t idft_plan[MAX_PRB];
}dft_precoding_t;
LIBLTE_API int dft_precoding_init(dft_precoding_t *q,
uint32_t max_prb);
LIBLTE_API void dft_precoding_free(dft_precoding_t *q);
LIBLTE_API bool dft_precoding_valid_prb(uint32_t nof_prb);
LIBLTE_API int dft_precoding(dft_precoding_t *q,
cf_t *input,
cf_t *output,
uint32_t nof_prb,
uint32_t nof_symbols);
LIBLTE_API int dft_predecoding(dft_precoding_t *q,
cf_t *input,
cf_t *output,
uint32_t nof_prb,
uint32_t nof_symbols);
#endif

@ -0,0 +1,94 @@
/**
*
* \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 CQI_
#define CQI_
#include <stdint.h>
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
/**
* CQI message generation according to 36.212 Sections 5.2.2.6 and 5.2.3.3
*
*/
/* Table 5.2.2.6.2-1: Fields for channel quality information feedback for higher layer configured subband
CQI reports
(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and
transmission mode 8 configured without PMI/RI reporting). */
typedef struct LIBLTE_API {
uint8_t wideband_cqi; // 4-bit width
uint32_t subband_diff_cqi; // 2N-bit width
} cqi_hl_subband_t;
/* Table 5.2.2.6.3-1: Fields for channel quality information feedback for UE selected subband CQI
reports
(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and
transmission mode 8 configured without PMI/RI reporting). */
typedef struct LIBLTE_API {
uint8_t wideband_cqi; // 4-bit width
uint8_t subband_diff_cqi; // 2-bit width
uint32_t position_subband; // L-bit width
} cqi_ue_subband_t;
/* Table 5.2.3.3.1-1: Fields for channel quality information feedback for wideband CQI reports
(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and
transmission mode 8 configured without PMI/RI reporting).
This is for PUCCH Format 2 reports
*/
typedef struct LIBLTE_API {
uint8_t wideband_cqi; // 4-bit width
} cqi_format2_wideband_t;
typedef struct LIBLTE_API {
uint8_t subband_cqi; // 4-bit width
uint8_t subband_label; // 1- or 2-bit width
} cqi_format2_subband_t;
LIBLTE_API int cqi_hl_subband_pack(cqi_hl_subband_t *msg,
uint32_t N,
uint8_t *buff,
uint32_t buff_len);
LIBLTE_API int cqi_ue_subband_pack(cqi_ue_subband_t *msg,
uint32_t L,
uint8_t *buff,
uint32_t buff_len);
LIBLTE_API int cqi_format2_wideband_pack(cqi_format2_wideband_t *msg,
uint8_t *buff,
uint32_t buff_len);
LIBLTE_API int cqi_format2_subband_pack(cqi_format2_subband_t *msg,
uint8_t *buff,
uint32_t buff_len);
#endif // CQI_

@ -76,19 +76,21 @@ typedef struct LIBLTE_API {
*/ */
LIBLTE_API int dci_msg_to_ra_dl(dci_msg_t *msg, LIBLTE_API int dci_msg_to_ra_dl(dci_msg_t *msg,
uint16_t msg_rnti, uint16_t msg_rnti,
uint16_t c_rnti,
lte_cell_t cell, lte_cell_t cell,
uint32_t cfi, uint32_t cfi,
ra_pdsch_t *ra_dl); ra_pdsch_t *ra_dl);
/* TODO
LIBLTE_API int dci_msg_to_ra_ul(dci_msg_t *msg, LIBLTE_API int dci_msg_to_ra_ul(dci_msg_t *msg,
uint16_t msg_rnti, uint32_t nof_prb,
uint16_t c_rnti, uint32_t n_rb_ho,
lte_cell_t cell,
uint32_t cfi,
ra_pusch_t *ra_ul); ra_pusch_t *ra_ul);
*/
LIBLTE_API int dci_rar_to_ra_ul(uint32_t rba,
uint32_t trunc_mcs,
bool hopping_flag,
uint32_t nof_prb,
ra_pusch_t *ra);
LIBLTE_API dci_format_t dci_format_from_string(char *str); LIBLTE_API dci_format_t dci_format_from_string(char *str);
LIBLTE_API char* dci_format_string(dci_format_t format); LIBLTE_API char* dci_format_string(dci_format_t format);
@ -102,8 +104,7 @@ LIBLTE_API bool dci_location_isvalid(dci_location_t *c);
LIBLTE_API int dci_msg_get_type(dci_msg_t *msg, LIBLTE_API int dci_msg_get_type(dci_msg_t *msg,
dci_msg_type_t *type, dci_msg_type_t *type,
uint32_t nof_prb, uint32_t nof_prb,
uint16_t msg_rnti, uint16_t msg_rnti);
uint16_t crnti);
LIBLTE_API void dci_msg_type_fprint(FILE *f, LIBLTE_API void dci_msg_type_fprint(FILE *f,
dci_msg_type_t type); dci_msg_type_t type);

@ -0,0 +1,91 @@
/**
*
* \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 HARQ_
#define HARQ_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/ra.h"
struct cb_segm {
uint32_t F;
uint32_t C;
uint32_t K1;
uint32_t K2;
uint32_t C1;
uint32_t C2;
};
typedef struct LIBLTE_API {
ra_mcs_t mcs;
uint32_t rv;
uint32_t sf_idx;
ra_dl_alloc_t dl_alloc;
ra_ul_alloc_t ul_alloc;
lte_cell_t cell;
uint32_t nof_re; // Number of RE per subframe
uint32_t nof_bits; // Number of bits per subframe
uint32_t nof_symb; // Number of symbols per subframe
uint32_t nof_prb; // Number of allocated PRB per subframe.
uint32_t max_cb;
uint32_t w_buff_size;
float **pdsch_w_buff_f;
uint8_t **pdsch_w_buff_c;
struct cb_segm cb_segm;
} harq_t;
LIBLTE_API int harq_init(harq_t * q,
lte_cell_t cell);
LIBLTE_API int harq_setup_dl(harq_t *p,
ra_mcs_t mcs,
uint32_t rv,
uint32_t sf_idx,
ra_dl_alloc_t *prb_alloc);
LIBLTE_API int harq_setup_ul(harq_t *p,
ra_mcs_t mcs,
uint32_t rv,
uint32_t sf_idx,
ra_ul_alloc_t *prb_alloc);
LIBLTE_API void harq_reset(harq_t *p);
LIBLTE_API void harq_free(harq_t *p);
LIBLTE_API int codeblock_segmentation(struct cb_segm *s,
uint32_t tbs);
#endif

@ -99,4 +99,17 @@ LIBLTE_API int pbch_encode(pbch_t *q,
LIBLTE_API void pbch_decode_reset(pbch_t *q); LIBLTE_API void pbch_decode_reset(pbch_t *q);
LIBLTE_API void pbch_mib_unpack(uint8_t *msg,
lte_cell_t *cell,
uint32_t *sfn);
LIBLTE_API void pbch_mib_pack(lte_cell_t *cell,
uint32_t sfn,
uint8_t *msg);
LIBLTE_API void pbch_mib_fprint(FILE *stream,
lte_cell_t *cell,
uint32_t sfn,
uint32_t cell_id);
#endif // PBCH_ #endif // PBCH_

@ -36,47 +36,22 @@
#include "liblte/phy/modem/mod.h" #include "liblte/phy/modem/mod.h"
#include "liblte/phy/modem/demod_soft.h" #include "liblte/phy/modem/demod_soft.h"
#include "liblte/phy/scrambling/scrambling.h" #include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/fec/rm_turbo.h"
#include "liblte/phy/fec/turbocoder.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/fec/crc.h"
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/harq.h"
#define TDEC_MAX_ITERATIONS 5 #define TDEC_MAX_ITERATIONS 5
typedef _Complex float cf_t; typedef _Complex float cf_t;
typedef struct LIBLTE_API {
ra_mcs_t mcs;
ra_prb_t prb_alloc;
lte_cell_t cell;
uint32_t max_cb;
uint32_t w_buff_size;
float **pdsch_w_buff_f;
uint8_t **pdsch_w_buff_c;
struct cb_segm {
uint32_t F;
uint32_t C;
uint32_t K1;
uint32_t K2;
uint32_t C1;
uint32_t C2;
} cb_segm;
} pdsch_harq_t;
/* PDSCH object */ /* PDSCH object */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
lte_cell_t cell; lte_cell_t cell;
uint32_t max_symbols; uint32_t max_re;
bool rnti_is_set; bool rnti_is_set;
uint16_t rnti; uint16_t rnti;
uint32_t nof_iterations;
float average_nof_iterations;
/* buffers */ /* buffers */
// void buffers are shared for tx and rx // void buffers are shared for tx and rx
@ -84,20 +59,16 @@ typedef struct LIBLTE_API {
cf_t *pdsch_symbols[MAX_PORTS]; cf_t *pdsch_symbols[MAX_PORTS];
cf_t *pdsch_x[MAX_PORTS]; cf_t *pdsch_x[MAX_PORTS];
cf_t *pdsch_d; cf_t *pdsch_d;
uint8_t *cb_in;
void *cb_out;
void *pdsch_e; void *pdsch_e;
/* tx & rx objects */ /* tx & rx objects */
modem_table_t mod[4]; modem_table_t mod[4];
demod_soft_t demod; demod_soft_t demod;
sequence_t seq_pdsch[NSUBFRAMES_X_FRAME]; sequence_t seq_pdsch[NSUBFRAMES_X_FRAME];
tcod_t encoder;
tdec_t decoder;
crc_t crc_tb;
crc_t crc_cb;
precoding_t precoding; precoding_t precoding;
sch_t dl_sch;
}pdsch_t; }pdsch_t;
LIBLTE_API int pdsch_init(pdsch_t *q, LIBLTE_API int pdsch_init(pdsch_t *q,
@ -108,41 +79,34 @@ LIBLTE_API void pdsch_free(pdsch_t *q);
LIBLTE_API int pdsch_set_rnti(pdsch_t *q, LIBLTE_API int pdsch_set_rnti(pdsch_t *q,
uint16_t rnti); uint16_t rnti);
LIBLTE_API int pdsch_harq_init(pdsch_harq_t *p,
pdsch_t *pdsch);
LIBLTE_API int pdsch_harq_setup(pdsch_harq_t *p,
ra_mcs_t mcs,
ra_prb_t *prb_alloc);
LIBLTE_API void pdsch_harq_reset(pdsch_harq_t *p);
LIBLTE_API void pdsch_harq_free(pdsch_harq_t *p);
LIBLTE_API int pdsch_encode(pdsch_t *q, LIBLTE_API int pdsch_encode(pdsch_t *q,
harq_t *harq_process,
uint8_t *data, uint8_t *data,
cf_t *sf_symbols[MAX_PORTS], cf_t *sf_symbols[MAX_PORTS]);
uint32_t nsubframe,
pdsch_harq_t *harq_process, LIBLTE_API int pdsch_encode_rnti(pdsch_t *q,
uint32_t rv_idx); harq_t *harq_process,
uint8_t *data,
uint16_t rnti,
cf_t *sf_symbols[MAX_PORTS]);
LIBLTE_API int pdsch_decode(pdsch_t *q, LIBLTE_API int pdsch_decode(pdsch_t *q,
harq_t *harq_process,
cf_t *sf_symbols, cf_t *sf_symbols,
cf_t *ce[MAX_PORTS], cf_t *ce[MAX_PORTS],
float noise_estimate, float noise_estimate,
uint8_t *data, uint8_t *data);
uint32_t nsubframe,
pdsch_harq_t *harq_process, LIBLTE_API int pdsch_decode_rnti(pdsch_t *q,
uint32_t rv_idx); harq_t *harq_process,
cf_t *sf_symbols,
cf_t *ce[MAX_PORTS],
float noise_estimate,
uint16_t rnti,
uint8_t *data);
LIBLTE_API float pdsch_average_noi(pdsch_t *q); LIBLTE_API float pdsch_average_noi(pdsch_t *q);
LIBLTE_API uint32_t pdsch_last_noi(pdsch_t *q); LIBLTE_API uint32_t pdsch_last_noi(pdsch_t *q);
LIBLTE_API int pdsch_get(pdsch_t *q,
cf_t *sf_symbols,
cf_t *pdsch_symbols,
ra_prb_t *prb_alloc,
uint32_t subframe);
#endif #endif

@ -95,6 +95,7 @@ LIBLTE_API int prach_init(prach_t *p,
LIBLTE_API int prach_gen(prach_t *p, LIBLTE_API int prach_gen(prach_t *p,
uint32_t seq_index, uint32_t seq_index,
uint32_t freq_offset, uint32_t freq_offset,
float beta_prach,
cf_t *signal); cf_t *signal);
LIBLTE_API int prach_detect(prach_t *p, LIBLTE_API int prach_detect(prach_t *p,

@ -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 PUCCH_
#define PUCCH_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/mimo/precoding.h"
#include "liblte/phy/mimo/layermap.h"
#include "liblte/phy/modem/mod.h"
#include "liblte/phy/modem/demod_soft.h"
#include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/filter/dft_precoding.h"
#define TDEC_MAX_ITERATIONS 5
typedef _Complex float cf_t;
#define PUCCH_N_SEQ 12 // Only Format 1, 1a and 1b supported
#define PUCCH_MAX_BITS 2
#define PUCCH_N_SF_MAX 4
typedef enum LIBLTE_API {
PUCCH_FORMAT_1 = 0,
PUCCH_FORMAT_1A,
PUCCH_FORMAT_1B,
PUCCH_FORMAT_2,
PUCCH_FORMAT_2A,
PUCCH_FORMAT_2B,
} pucch_format_t;
typedef struct LIBLTE_API {
pucch_format_t format;
float beta_pucch;
uint32_t delta_pucch_shift;
uint32_t n_pucch;
uint32_t N_cs;
} pucch_cfg_t;
/* PUSCH object */
typedef struct LIBLTE_API {
lte_cell_t cell;
pucch_cfg_t pucch_cfg;
uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB];
float tmp_arg[PUCCH_N_SF_MAX*PUCCH_N_SEQ];
float y[PUCCH_N_SEQ];
}pucch_t;
LIBLTE_API int pucch_init(pucch_t *q,
lte_cell_t cell);
LIBLTE_API void pucch_free(pucch_t *q);
LIBLTE_API void pucch_set_cfg(pucch_t *q,
pucch_cfg_t *cfg);
LIBLTE_API int pucch_set_rnti(pucch_t *q,
uint16_t rnti);
LIBLTE_API int pucch_encode(pucch_t *q,
pucch_cfg_t *cfg,
uint8_t bits[PUCCH_MAX_BITS],
cf_t *sf_symbols);
LIBLTE_API float pucch_get_alpha(uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB],
pucch_cfg_t *cfg,
lte_cp_t cp,
bool is_drms,
uint32_t ns,
uint32_t l,
uint32_t *n_oc);
LIBLTE_API int generate_n_cs_cell(lte_cell_t cell,
uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB]);
LIBLTE_API bool pucch_cfg_isvalid(pucch_cfg_t *cfg);
#endif

@ -0,0 +1,140 @@
/**
*
* \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 PUSCH_
#define PUSCH_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/mimo/precoding.h"
#include "liblte/phy/mimo/layermap.h"
#include "liblte/phy/modem/mod.h"
#include "liblte/phy/modem/demod_soft.h"
#include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/filter/dft_precoding.h"
#define TDEC_MAX_ITERATIONS 5
typedef _Complex float cf_t;
typedef struct {
enum {
hop_mode_inter_sf = 1,
hop_mode_intra_sf = 0
} hop_mode;
uint32_t current_tx_nb;
uint32_t hopping_offset;
uint32_t n_sb;
} pusch_hopping_cfg_t;
/* PUSCH object */
typedef struct LIBLTE_API {
lte_cell_t cell;
pusch_hopping_cfg_t hopping_cfg;
uint32_t max_re;
bool rnti_is_set;
uint16_t rnti;
dft_precoding_t dft_precoding;
precoding_t equalizer;
/* buffers */
// void buffers are shared for tx and rx
cf_t *ce;
cf_t *pusch_z;
cf_t *pusch_d;
void *pusch_q;
void *pusch_g;
/* tx & rx objects */
modem_table_t mod[4];
demod_soft_t demod;
sequence_t seq_pusch[NSUBFRAMES_X_FRAME];
sequence_t seq_type2_fo;
sch_t dl_sch;
}pusch_t;
LIBLTE_API int pusch_init(pusch_t *q,
lte_cell_t cell);
LIBLTE_API void pusch_free(pusch_t *q);
LIBLTE_API void pusch_set_hopping_cfg(pusch_t *q,
pusch_hopping_cfg_t *cfg);
LIBLTE_API int pusch_set_rnti(pusch_t *q,
uint16_t rnti);
LIBLTE_API int pusch_encode(pusch_t *q,
harq_t *harq_process,
uint8_t *data,
cf_t *sf_symbols);
LIBLTE_API int pusch_encode_rnti(pusch_t *q,
harq_t *harq_process,
uint8_t *data,
uint16_t rnti,
cf_t *sf_symbols);
LIBLTE_API int pusch_uci_encode(pusch_t *q,
harq_t *harq_process,
uint8_t *data,
uci_data_t uci_data,
cf_t *sf_symbols);
LIBLTE_API int pusch_uci_encode_rnti(pusch_t *q,
harq_t *harq,
uint8_t *data,
uci_data_t uci_data,
uint16_t rnti,
cf_t *sf_symbols);
LIBLTE_API int pusch_decode(pusch_t *q,
harq_t *harq_process,
cf_t *sf_symbols,
cf_t *ce,
float noise_estimate,
uint8_t *data);
LIBLTE_API float pusch_average_noi(pusch_t *q);
LIBLTE_API uint32_t pusch_last_noi(pusch_t *q);
#endif

@ -80,7 +80,14 @@ typedef struct LIBLTE_API {
ra_prb_slot_t slot[2]; ra_prb_slot_t slot[2];
uint32_t lstart; uint32_t lstart;
uint32_t re_sf[NSUBFRAMES_X_FRAME]; uint32_t re_sf[NSUBFRAMES_X_FRAME];
} ra_prb_t; } ra_dl_alloc_t;
typedef struct LIBLTE_API {
uint32_t n_prb[2];
uint32_t n_prb_tilde[2];
uint32_t L_prb;
uint32_t freq_hopping;
} ra_ul_alloc_t;
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
uint16_t rnti; uint16_t rnti;
@ -90,7 +97,7 @@ typedef struct LIBLTE_API {
ra_type1_t type1_alloc; ra_type1_t type1_alloc;
ra_type2_t type2_alloc; ra_type2_t type2_alloc;
}; };
ra_prb_t prb_alloc; ra_dl_alloc_t prb_alloc;
uint32_t mcs_idx; uint32_t mcs_idx;
ra_mcs_t mcs; ra_mcs_t mcs;
uint32_t harq_process; uint32_t harq_process;
@ -110,7 +117,7 @@ typedef struct LIBLTE_API {
hop_type_2 = 3 hop_type_2 = 3
} freq_hop_fl; } freq_hop_fl;
ra_prb_t prb_alloc; ra_ul_alloc_t prb_alloc;
ra_type2_t type2_alloc; ra_type2_t type2_alloc;
uint32_t mcs_idx; uint32_t mcs_idx;
@ -126,25 +133,26 @@ LIBLTE_API void ra_prb_fprint(FILE *f,
ra_prb_slot_t *prb, ra_prb_slot_t *prb,
uint32_t nof_prb); uint32_t nof_prb);
LIBLTE_API int ra_prb_get_dl(ra_prb_t *prb, LIBLTE_API int ra_dl_alloc(ra_dl_alloc_t *prb,
ra_pdsch_t *ra, ra_pdsch_t *ra,
uint32_t nof_prb); uint32_t nof_prb);
LIBLTE_API int ra_prb_get_ul(ra_prb_slot_t *prb, LIBLTE_API int ra_ul_alloc(ra_ul_alloc_t *prb,
ra_pusch_t *ra, ra_pusch_t *ra,
uint32_t nof_prb); uint32_t n_rb_ho,
uint32_t nof_prb);
LIBLTE_API void ra_prb_get_re_dl(ra_prb_t *prb_dist, LIBLTE_API void ra_dl_alloc_re(ra_dl_alloc_t *prb_dist,
uint32_t nof_prb, uint32_t nof_prb,
uint32_t nof_ports, uint32_t nof_ports,
uint32_t nof_ctrl_symbols, uint32_t nof_ctrl_symbols,
lte_cp_t cp); lte_cp_t cp);
LIBLTE_API uint32_t ra_nprb_dl(ra_pdsch_t *ra, LIBLTE_API uint32_t ra_nprb_dl(ra_pdsch_t *ra,
uint32_t nof_prb); uint32_t nof_prb);
LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra, LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra,
uint32_t nof_prb); uint32_t nof_prb);
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t mcs_idx, LIBLTE_API int ra_mcs_from_idx_dl(uint32_t mcs_idx,
uint32_t nof_prb, uint32_t nof_prb,

@ -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 SCH_
#define SCH_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/fec/rm_turbo.h"
#include "liblte/phy/fec/turbocoder.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/fec/crc.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/phch/uci.h"
#define TDEC_MAX_ITERATIONS 5
#ifndef RX_NULL
#define RX_NULL 10000
#endif
#ifndef TX_NULL
#define TX_NULL 100
#endif
/* DL-SCH AND UL-SCH common functions */
typedef struct LIBLTE_API {
uint32_t nof_iterations;
float average_nof_iterations;
/* buffers */
uint8_t *cb_in;
void *cb_out;
void *pdsch_e;
tcod_t encoder;
tdec_t decoder;
crc_t crc_tb;
crc_t crc_cb;
uci_cqi_pusch_t uci_cqi;
} sch_t;
LIBLTE_API int sch_init(sch_t *q);
LIBLTE_API void sch_free(sch_t *q);
LIBLTE_API float sch_average_noi(sch_t *q);
LIBLTE_API uint32_t sch_last_noi(sch_t *q);
LIBLTE_API int dlsch_encode(sch_t *q,
harq_t *harq_process,
uint8_t *data,
uint8_t *e_bits);
LIBLTE_API int dlsch_decode(sch_t *q,
harq_t *harq_process,
float *e_bits,
uint8_t *data);
LIBLTE_API int ulsch_encode(sch_t *q,
harq_t *harq_process,
uint8_t *data,
uint8_t *g_bits,
uint8_t *q_bits);
LIBLTE_API int ulsch_uci_encode(sch_t *q,
harq_t *harq_process,
uint8_t *data,
uci_data_t uci_data,
uint8_t *g_bits,
uint8_t *q_bits);
LIBLTE_API int ulsch_decode(sch_t *q,
harq_t *harq_process,
float *e_bits,
uint8_t *data);
#endif

@ -0,0 +1,91 @@
/**
*
* \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 UCI_
#define UCI_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/fec/crc.h"
#define MAX_CQI_LEN_PUSCH 512
#define MAX_CQI_LEN_PUCCH 13
#define CQI_CODED_PUCCH_B 20
typedef struct LIBLTE_API {
crc_t crc;
uint8_t tmp_cqi[MAX_CQI_LEN_PUSCH];
uint8_t encoded_cqi[3*MAX_CQI_LEN_PUSCH];
} uci_cqi_pusch_t;
typedef struct LIBLTE_API {
uint8_t *uci_cqi;
uint32_t uci_cqi_len;
float beta_cqi;
uint8_t uci_ri; // Only 1-bit supported for RI
uint32_t uci_ri_len;
float beta_ri;
uint8_t uci_ack; // Only 1-bit supported for HARQ
uint32_t uci_ack_len;
float beta_ack;
} uci_data_t;
LIBLTE_API int uci_cqi_init(uci_cqi_pusch_t *q);
LIBLTE_API void uci_cqi_free(uci_cqi_pusch_t *q);
LIBLTE_API int uci_encode_cqi_pusch(uci_cqi_pusch_t *q,
uint8_t *cqi_data,
uint32_t cqi_len,
float beta,
uint32_t Q_prime_ri,
harq_t *harq_process,
uint8_t *q_bits);
LIBLTE_API int uci_encode_cqi_pucch(uint8_t *cqi_data,
uint32_t cqi_len,
uint8_t b_bits[CQI_CODED_PUCCH_B]);
LIBLTE_API int uci_encode_ack(uint8_t data,
uint32_t O_cqi,
float beta,
harq_t *harq_process,
uint32_t H_prime_total,
uint8_t *q_bits);
LIBLTE_API int uci_encode_ri(uint8_t data,
uint32_t O_cqi,
float beta,
harq_t *harq_process,
uint32_t H_prime_total,
uint8_t *q_bits);
#endif

@ -26,9 +26,6 @@
*/ */
#include <complex.h>
#include <math.h>
#ifndef _LTE_ #ifndef _LTE_
#define _LTE_ #define _LTE_
@ -36,6 +33,9 @@
extern "C" { extern "C" {
#endif #endif
#include <complex.h>
#include <math.h>
#include "liblte/config.h" #include "liblte/config.h"
#include "liblte/phy/utils/bit.h" #include "liblte/phy/utils/bit.h"
@ -48,11 +48,14 @@
#include "liblte/phy/utils/pack.h" #include "liblte/phy/utils/pack.h"
#include "liblte/phy/utils/vector.h" #include "liblte/phy/utils/vector.h"
#include "liblte/phy/common/timestamp.h"
#include "liblte/phy/common/sequence.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/common/fft.h" #include "liblte/phy/common/fft.h"
#include "liblte/phy/ch_estimation/chest_dl.h" #include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/ch_estimation/refsignal_dl.h" #include "liblte/phy/ch_estimation/refsignal_dl.h"
#include "liblte/phy/ch_estimation/refsignal_ul.h"
#include "liblte/phy/resampling/interp.h" #include "liblte/phy/resampling/interp.h"
#include "liblte/phy/resampling/decim.h" #include "liblte/phy/resampling/decim.h"
@ -70,6 +73,7 @@
#include "liblte/phy/fec/rm_turbo.h" #include "liblte/phy/fec/rm_turbo.h"
#include "liblte/phy/filter/filter2d.h" #include "liblte/phy/filter/filter2d.h"
#include "liblte/phy/filter/dft_precoding.h"
#include "liblte/phy/io/binsource.h" #include "liblte/phy/io/binsource.h"
#include "liblte/phy/io/filesink.h" #include "liblte/phy/io/filesink.h"
@ -85,18 +89,26 @@
#include "liblte/phy/mimo/precoding.h" #include "liblte/phy/mimo/precoding.h"
#include "liblte/phy/mimo/layermap.h" #include "liblte/phy/mimo/layermap.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/cqi.h"
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/pdcch.h" #include "liblte/phy/phch/harq.h"
#include "liblte/phy/phch/pdsch.h"
#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/pdcch.h"
#include "liblte/phy/phch/pdsch.h"
#include "liblte/phy/phch/phich.h" #include "liblte/phy/phch/phich.h"
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/prach.h"
#include "liblte/phy/phch/ra.h"
#include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/ue/ue_sync.h" #include "liblte/phy/ue/ue_sync.h"
#include "liblte/phy/ue/ue_mib.h" #include "liblte/phy/ue/ue_mib.h"
#include "liblte/phy/ue/ue_cell_search.h" #include "liblte/phy/ue/ue_cell_search.h"
#include "liblte/phy/ue/ue_dl.h" #include "liblte/phy/ue/ue_dl.h"
#include "liblte/phy/ue/ue_ul.h"
#include "liblte/phy/scrambling/scrambling.h" #include "liblte/phy/scrambling/scrambling.h"
@ -108,6 +120,7 @@
#ifdef __cplusplus #ifdef __cplusplus
} }
#undef I // Fix complex.h #define I nastiness when using C++
#endif #endif
#endif #endif

@ -36,14 +36,34 @@
typedef _Complex float cf_t; typedef _Complex float cf_t;
/* Scrambling has no state */ /* Scrambling has no state */
LIBLTE_API void scrambling_b(sequence_t *s, uint8_t *data); LIBLTE_API void scrambling_b(sequence_t *s,
LIBLTE_API void scrambling_b_offset(sequence_t *s, uint8_t *data, int offset, int len); uint8_t *data);
LIBLTE_API void scrambling_f(sequence_t *s, float *data); LIBLTE_API void scrambling_b_offset(sequence_t *s,
LIBLTE_API void scrambling_f_offset(sequence_t *s, float *data, int offset, int len); uint8_t *data,
int offset,
int len);
LIBLTE_API void scrambling_c(sequence_t *s, cf_t *data); LIBLTE_API void scrambling_b_offset_pusch(sequence_t *s,
LIBLTE_API void scrambling_c_offset(sequence_t *s, cf_t *data, int offset, int len); uint8_t *data,
int offset,
int len);
LIBLTE_API void scrambling_f(sequence_t *s,
float *data);
LIBLTE_API void scrambling_f_offset(sequence_t *s,
float *data,
int offset,
int len);
LIBLTE_API void scrambling_c(sequence_t *s,
cf_t *data);
LIBLTE_API void scrambling_c_offset(sequence_t *s,
cf_t *data,
int offset,
int len);
/* High-level API */ /* High-level API */

@ -86,12 +86,12 @@ typedef struct LIBLTE_API {
LIBLTE_API int ue_cell_search_init(ue_cell_search_t *q, LIBLTE_API int ue_cell_search_init(ue_cell_search_t *q,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t,timestamp_t*),
void *stream_handler); void *stream_handler);
LIBLTE_API int ue_cell_search_init_max(ue_cell_search_t *q, LIBLTE_API int ue_cell_search_init_max(ue_cell_search_t *q,
uint32_t max_frames_total, uint32_t max_frames_total,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t,timestamp_t*),
void *stream_handler); void *stream_handler);
LIBLTE_API void ue_cell_search_free(ue_cell_search_t *q); LIBLTE_API void ue_cell_search_free(ue_cell_search_t *q);

@ -41,7 +41,6 @@
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/pbch.h"
#include "liblte/phy/phch/pcfich.h" #include "liblte/phy/phch/pcfich.h"
#include "liblte/phy/phch/pdcch.h" #include "liblte/phy/phch/pdcch.h"
#include "liblte/phy/phch/pdsch.h" #include "liblte/phy/phch/pdsch.h"
@ -57,15 +56,16 @@
#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;
pdsch_harq_t harq_process[NOF_HARQ_PROCESSES]; harq_t harq_process[NOF_HARQ_PROCESSES];
regs_t regs; regs_t regs;
lte_fft_t fft; lte_fft_t fft;
chest_dl_t chest; chest_dl_t chest;
ra_pdsch_t ra_dl;
lte_cell_t cell; lte_cell_t cell;
cf_t *sf_symbols; cf_t *sf_symbols;
@ -75,27 +75,51 @@ typedef struct LIBLTE_API {
uint64_t pkts_total; uint64_t pkts_total;
uint64_t nof_pdcch_detected; uint64_t nof_pdcch_detected;
uint16_t user_rnti;
uint16_t current_rnti; uint16_t current_rnti;
}ue_dl_t; }ue_dl_t;
/* This function shall be called just after the initial synchronization */ /* This function shall be called just after the initial synchronization */
LIBLTE_API int ue_dl_init(ue_dl_t *q, LIBLTE_API int ue_dl_init(ue_dl_t *q,
lte_cell_t cell, lte_cell_t cell);
uint16_t user_rnti);
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_decode_fft_estimate(ue_dl_t *q,
cf_t *input,
uint32_t sf_idx,
uint32_t *cfi);
LIBLTE_API int ue_dl_decode_rnti_rv_packet(ue_dl_t *q,
dci_msg_t *dci_msg,
uint8_t *data,
uint32_t cfi,
uint32_t sf_idx,
uint16_t rnti,
uint32_t rvidx);
LIBLTE_API int ue_dl_find_ul_dci(ue_dl_t *q,
dci_msg_t *dci_msg,
uint32_t cfi,
uint32_t sf_idx,
uint16_t rnti);
LIBLTE_API int ue_dl_decode(ue_dl_t * q, LIBLTE_API int ue_dl_decode(ue_dl_t * q,
cf_t *input, cf_t *input,
uint8_t *data, uint8_t *data,
uint32_t sf_idx); uint32_t sf_idx);
LIBLTE_API int ue_dl_decode_sib(ue_dl_t * q, LIBLTE_API int ue_dl_decode_rnti(ue_dl_t * q,
cf_t *input, cf_t *input,
uint8_t * data, uint8_t *data,
uint32_t sf_idx, uint32_t sf_idx,
uint32_t rvidx); uint16_t rnti);
LIBLTE_API int ue_dl_decode_rnti_rv(ue_dl_t * q,
cf_t *input,
uint8_t * data,
uint32_t sf_idx,
uint16_t rnti,
uint32_t rvidx);
LIBLTE_API void ue_dl_reset(ue_dl_t *q); LIBLTE_API void ue_dl_reset(ue_dl_t *q);

@ -0,0 +1,6 @@
class ue_mac_itf
{
public:
virtual void ready_to_send() = 0;
};

@ -104,7 +104,7 @@ typedef struct {
LIBLTE_API int ue_mib_sync_init(ue_mib_sync_t *q, LIBLTE_API int ue_mib_sync_init(ue_mib_sync_t *q,
uint32_t cell_id, uint32_t cell_id,
lte_cp_t cp, lte_cp_t cp,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t, timestamp_t *),
void *stream_handler); void *stream_handler);
LIBLTE_API void ue_mib_sync_free(ue_mib_sync_t *q); LIBLTE_API void ue_mib_sync_free(ue_mib_sync_t *q);

@ -0,0 +1,202 @@
/**
*
* \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 "ue_mac_itf.h"
#include "liblte/phy/utils/queue.h"
#ifndef UEPHY_H
#define UEPHY_H
typedef _Complex float cf_t;
class ue_phy
{
public:
class pdcch_ul_search : public queue::element {
public:
set_crnti(uint16_t rnti);
set_crnti_ra_procedure(uint16_t rnti);
set_sps_crnti(uint16_t rnti);
set_temporal_crnti(uint16_t rnti);
set_tpc_pusch(uint16_t rnti);
set_tpc_pucch(uint16_t rnti);
private:
uint16_t rnti;
bool is_crnti = false;
bool is_crnti_ra_procedure = false;
bool is_sps_crnti = false;
bool is_t_crnti = false;
bool is_tpc_pusch = false;
bool is_tpc_pucch = false;
};
class pdcch_dl_search : public queue::element {
public:
set_crnti(uint16_t rnti);
set_crnti_ra_procedure(uint16_t rnti);
set_sps_crnti(uint16_t rnti);
set_temporal_crnti(uint16_t rnti);
set_tpc_pusch(uint16_t rnti);
set_tpc_pucch(uint16_t rnti);
private:
uint16_t rnti;
bool is_crnti = false;
bool is_crnti_ra_procedure = false;
bool is_sps_crnti = false;
bool is_t_crnti = false;
bool is_tpc_pusch = false;
bool is_tpc_pucch = false;
};
class ul_allocation {
};
class ul_assignment : public queue::element {
public:
ul_assignment(uint32_t nof_prb, uint32_t max_ulsch_bits);
void set_allocation(ul_allocation allocation);
uint8_t* get_ulsch_buffer();
cf_t* get_signal_buffer();
void set_uci_data();
void generate_signal();
private:
cf_t* signal_buffer = NULL;
uint8_t* ulsch_buffer;
ul_allocation allocation;
};
class dl_allocation {
};
class dl_assignment : public queue::element {
public:
void set_allocation(dl_allocation allocation);
private:
dl_allocation allocation;
};
class phich_assignment {
};
class phich_assignment : public queue::element {
public:
phich_assignment();
void set_allocation(phich_assignment assignment);
private:
phich_assignment assignment;
};
class dl_tb : public queue::element {
public:
dl_tb(uint32_t max_dlsch_bits);
uint16_t get_rnti();
bool is_crc_valid();
uint8_t* get_dlsch_payload();
private:
uint8_t* dlsch_buffer;
uint16_t rnti;
bool crc_result;
};
class dl_grant : public queue::element {
public:
dl_allocation get_allocation();
private:
dl_allocation allocation;
};
class ul_grant : public queue::element {
public:
ul_allocation get_allocation();
private:
ul_allocation allocation;
};
class ul_ack : public queue::element {
public:
bool get_ack();
private:
bool ack;
};
class rx_buffer : public queue::element {
public:
rx_buffer(uint32_t nof_prb);
cf_t* get_signal_buffer();
private:
uint32_t nof_prb;
cf_t* signal_buffer;
};
ue_phy(ue_mac_itf *mac);
~ue_phy();
void measure(); // TBD
void dl_bch();
void start_rxtx();
void stop_rxtx();
void init_prach();
void send_prach(/* prach_cfg_t in prach.h with power, seq idx, etc */);
void set_param();
pdcch_ul_search* get_pdcch_ul_search(uint32_t tti);
pdcch_dl_search* get_pdcch_dl_search(uint32_t tti);
ul_assignment* get_ul_assignment(uint32_t tti);
dl_assignment* get_dl_assignment(uint32_t tti);
phich_assignment* get_phich_assignment(uint32_t tti);
dl_tb* get_dl_tb(uint32_t tti);
dl_grant* get_dl_grant(uint32_t tti);
ul_grant* get_ul_grant(uint32_t tti);
ul_ack* get_ul_ack(uint32_t tti);
private:
enum {
IDLE, MEASURE, RX_BCH, RXTX
} phy_state;
bool prach_initiated = false;
bool prach_ready_to_send = false;
queue pdcch_ul_search;
queue pdcch_dl_search;
queue ul_assignment;
queue dl_assignment;
queue phich_assignment;
queue dl_tb;
queue dl_grant;
queue ul_grant;
queue ul_grant;
queue rx_buffer;
};
#endif

@ -36,6 +36,7 @@
#include "liblte/phy/ch_estimation/chest_dl.h" #include "liblte/phy/ch_estimation/chest_dl.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/common/timestamp.h"
#include "liblte/phy/io/filesource.h" #include "liblte/phy/io/filesource.h"
/************************************************************** /**************************************************************
@ -64,7 +65,8 @@ typedef struct LIBLTE_API {
sync_t strack; sync_t strack;
void *stream; void *stream;
int (*recv_callback)(void*, void*, uint32_t); int (*recv_callback)(void*, void*, uint32_t, timestamp_t*);
timestamp_t last_timestamp;
filesource_t file_source; filesource_t file_source;
bool file_mode; bool file_mode;
@ -105,7 +107,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,
lte_cell_t cell, lte_cell_t cell,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t, timestamp_t*),
void *stream_handler); void *stream_handler);
LIBLTE_API int ue_sync_init_file(ue_sync_t *q, LIBLTE_API int ue_sync_init_file(ue_sync_t *q,
@ -135,6 +137,8 @@ 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);
LIBLTE_API void ue_sync_get_last_timestamp(ue_sync_t *q,
timestamp_t *timestamp);

@ -0,0 +1,122 @@
/**
*
* \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 UEUL_H
#define UEUL_H
/*******************************************************
*
* This module is a frontend to all the data and control channels processing
* modules.
********************************************************/
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/ch_estimation/chest_dl.h"
#include "liblte/phy/common/fft.h"
#include "liblte/phy/ch_estimation/refsignal_ul.h"
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/ra.h"
#include "liblte/phy/sync/cfo.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/config.h"
#define NOF_HARQ_PROCESSES 8
typedef struct LIBLTE_API {
lte_fft_t fft;
cfo_t cfo;
lte_cell_t cell;
bool normalize_en;
float current_cfo;
refsignal_drms_pusch_cfg_t pusch_drms_cfg;
refsignal_ul_t drms;
harq_t harq_process[NOF_HARQ_PROCESSES];
pusch_t pusch;
cf_t *refsignal;
cf_t *sf_symbols;
uint16_t current_rnti;
}ue_ul_t;
/* This function shall be called just after the initial synchronization */
LIBLTE_API int ue_ul_init(ue_ul_t *q,
lte_cell_t cell);
LIBLTE_API void ue_ul_free(ue_ul_t *q);
LIBLTE_API void ue_ul_set_cfo(ue_ul_t *q,
float cur_cfo);
LIBLTE_API void ue_ul_set_normalization(ue_ul_t *q,
bool enabled);
LIBLTE_API void ue_ul_set_pusch_cfg(ue_ul_t *q,
refsignal_drms_pusch_cfg_t *pusch_drms_cfg,
pusch_hopping_cfg_t *pusch_hopping_cfg);
LIBLTE_API int ue_ul_pusch_encode(ue_ul_t *q,
ra_pusch_t *ra_ul,
uint8_t *data,
uint32_t sf_idx,
cf_t *output_signal);
LIBLTE_API int ue_ul_pusch_encode_rnti(ue_ul_t *q,
ra_pusch_t *ra_ul,
uint8_t *data,
uint32_t sf_idx,
uint16_t rnti,
cf_t *output_signal);
LIBLTE_API int ue_ul_pusch_uci_encode(ue_ul_t *q,
ra_pusch_t *ra_ul,
uint8_t *data,
uci_data_t uci_data,
uint32_t sf_idx,
cf_t *output_signal);
LIBLTE_API int ue_ul_pusch_uci_encode_rnti(ue_ul_t *q,
ra_pusch_t *ra_ul,
uint8_t *data,
uci_data_t uci_data,
uint32_t sf_idx,
uint16_t rnti,
cf_t *output_signal);
LIBLTE_API void ue_ul_reset(ue_ul_t *q);
LIBLTE_API void ue_ul_set_rnti(ue_ul_t *q,
uint16_t rnti);
#endif

@ -0,0 +1,84 @@
/**
*
* \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 "ue_mac_itf.h"
#ifndef QUEUE_H
#define QUEUE_H
class queue
{
public:
class element
{
public:
~element();
bool release()
{
if (state == READY) {
state = RELEASED;
return true;
} else {
return false;
}
}
bool is_released()
{
return state == RELEASED;
}
bool ready_to_send() {
if (state == RELEASED) {
state = READY;
return true;
} else {
return false;
}
}
bool is_ready_to_send() {
return state == READY;
}
protected:
enum {
RELEASED, READY
} state;
}
queue(uint32_t nof_elements, uint32_t element_size);
~queue();
element* get(uint32_t idx);
private:
uint32_t nof_elements;
uint32_t element_size;
void **buffer_of_elements;
};
#endif

@ -29,6 +29,10 @@
#ifndef VECTOR_ #ifndef VECTOR_
#define VECTOR_ #define VECTOR_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include "liblte/config.h" #include "liblte/config.h"
@ -137,4 +141,8 @@ LIBLTE_API void vec_abs_square_cf(cf_t *x, float *abs_square, uint32_t len);
/* argument of each vector element */ /* argument of each vector element */
LIBLTE_API void vec_arg_cf(cf_t *x, float *arg, uint32_t len); LIBLTE_API void vec_arg_cf(cf_t *x, float *arg, uint32_t len);
#ifdef __cplusplus
}
#endif
#endif // VECTOR_ #endif // VECTOR_

@ -63,7 +63,7 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
{ {
bzero(q, sizeof(chest_dl_t)); bzero(q, sizeof(chest_dl_t));
ret = refsignal_cs_generate(&q->csr_signal, cell); ret = refsignal_cs_init(&q->csr_signal, cell);
if (ret != LIBLTE_SUCCESS) { if (ret != LIBLTE_SUCCESS) {
fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); fprintf(stderr, "Error initializing CSR signal (%d)\n",ret);
goto clean_exit; goto clean_exit;
@ -87,6 +87,12 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
} }
bzero(q->tmp_timeavg[i], sizeof(cf_t) * 2*cell.nof_prb); bzero(q->tmp_timeavg[i], sizeof(cf_t) * 2*cell.nof_prb);
} }
q->tmp_timeavg_mult = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb);
if (!q->tmp_timeavg_mult) {
perror("malloc");
goto clean_exit;
}
bzero(q->tmp_timeavg_mult, sizeof(cf_t) * 2*cell.nof_prb);
for (int i=0;i<cell.nof_ports;i++) { for (int i=0;i<cell.nof_ports;i++) {
q->pilot_estimates[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(cell.nof_prb, i)); q->pilot_estimates[i] = vec_malloc(sizeof(cf_t) * REFSIGNAL_NUM_SF(cell.nof_prb, i));
@ -117,14 +123,14 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell)
} }
/* Set default time/freq filters */ /* Set default time/freq filters */
float f[3]={0.1, 0.8, 0.1}; //float f[3]={0.1, 0.8, 0.1};
chest_dl_set_filter_freq(q, f, 3); //chest_dl_set_filter_freq(q, f, 3);
//float f[5]={0.05, 0.15, 0.6, 0.15, 0.05}; float f[5]={0.05, 0.2, 0.5, 0.2, 0.05};
//chest_dl_set_filter_freq(q, f, 5); chest_dl_set_filter_freq(q, f, 5);
float t[2]={0.1, 0.9}; float t[2]={0.1, 0.9};
chest_dl_set_filter_time(q, t, 2); chest_dl_set_filter_time(q, t, 0);
q->cell = cell; q->cell = cell;
} }
@ -153,6 +159,9 @@ void chest_dl_free(chest_dl_t *q)
free(q->tmp_timeavg[i]); free(q->tmp_timeavg[i]);
} }
} }
if (q->tmp_timeavg_mult) {
free(q->tmp_timeavg_mult);
}
interp_linear_vector_free(&q->interp_linvec); interp_linear_vector_free(&q->interp_linvec);
interp_linear_free(&q->interp_lin); interp_linear_free(&q->interp_lin);
@ -239,10 +248,11 @@ static void average_pilots(chest_dl_t *q, uint32_t port_id)
conv_same_cf(&pilot_est(0), q->filter_freq, &pilot_tmp(0), nref, q->filter_freq_len); conv_same_cf(&pilot_est(0), q->filter_freq, &pilot_tmp(0), nref, q->filter_freq_len);
/* Adjust extremes using linear interpolation */ /* Adjust extremes using linear interpolation */
pilot_tmp(0) += interp_linear_onesample(pilot_est(1), pilot_est(0)) pilot_tmp(0) += interp_linear_onesample(pilot_est(1), pilot_est(0))
* q->filter_freq[q->filter_freq_len/2-1]; * q->filter_freq[q->filter_freq_len/2-1]*1.2;
pilot_tmp(nref-1) += interp_linear_onesample(pilot_est(nref-2), pilot_est(nref-1)) pilot_tmp(nref-1) += interp_linear_onesample(pilot_est(nref-2), pilot_est(nref-1))
* q->filter_freq[q->filter_freq_len/2+1]; * q->filter_freq[q->filter_freq_len/2+1]*1.2;
} else { } else {
memcpy(&pilot_tmp(0), &pilot_est(0), nref * sizeof(cf_t)); memcpy(&pilot_tmp(0), &pilot_est(0), nref * sizeof(cf_t));
} }
@ -285,9 +295,9 @@ static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t port_id)
/* Interpolate in the frequency domain */ /* Interpolate in the frequency domain */
for (l=0;l<nsymbols;l++) { for (l=0;l<nsymbols;l++) {
uint32_t fidx_offset = refsignal_fidx(q->cell, l, port_id, 0); uint32_t fidx_offset = refsignal_cs_fidx(q->cell, l, port_id, 0);
interp_linear_offset(&q->interp_lin, &pilot_avg(0), interp_linear_offset(&q->interp_lin, &pilot_avg(0),
&ce[refsignal_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB], &ce[refsignal_cs_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB],
fidx_offset, RE_X_RB/2-fidx_offset); fidx_offset, RE_X_RB/2-fidx_offset);
} }
@ -323,7 +333,7 @@ float chest_dl_rssi(chest_dl_t *q, cf_t *input, uint32_t port_id) {
float rssi = 0; float rssi = 0;
uint32_t nsymbols = refsignal_cs_nof_symbols(port_id); uint32_t nsymbols = refsignal_cs_nof_symbols(port_id);
for (l=0;l<nsymbols;l++) { for (l=0;l<nsymbols;l++) {
cf_t *tmp = &input[refsignal_nsymbol(l, q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB]; cf_t *tmp = &input[refsignal_cs_nsymbol(l, q->cell.cp, port_id) * q->cell.nof_prb * RE_X_RB];
rssi += vec_dot_prod_conj_ccc(tmp, tmp, q->cell.nof_prb * RE_X_RB); rssi += vec_dot_prod_conj_ccc(tmp, tmp, q->cell.nof_prb * RE_X_RB);
} }
return rssi/nsymbols; return rssi/nsymbols;

@ -83,11 +83,11 @@ uint32_t refsignal_cs_nof_symbols(uint32_t port_id)
} }
} }
inline uint32_t refsignal_fidx(lte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) { inline uint32_t refsignal_cs_fidx(lte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) {
return 6*m + ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6); return 6*m + ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
} }
inline uint32_t refsignal_nsymbol(uint32_t l, lte_cp_t cp, uint32_t port_id) { inline uint32_t refsignal_cs_nsymbol(uint32_t l, lte_cp_t cp, uint32_t port_id) {
if (port_id < 2) { if (port_id < 2) {
if (l % 2) { if (l % 2) {
return (l/2+1)*CP_NSYMB(cp) - 3; return (l/2+1)*CP_NSYMB(cp) - 3;
@ -103,7 +103,7 @@ inline uint32_t refsignal_nsymbol(uint32_t l, lte_cp_t cp, uint32_t port_id) {
/** Allocates and precomputes the Cell-Specific Reference (CSR) signal for /** Allocates and precomputes the Cell-Specific Reference (CSR) signal for
* the 20 slots in a subframe * the 20 slots in a subframe
*/ */
int refsignal_cs_generate(refsignal_cs_t * q, lte_cell_t cell) int refsignal_cs_init(refsignal_cs_t * q, lte_cell_t cell)
{ {
uint32_t c_init; uint32_t c_init;
@ -146,7 +146,7 @@ int refsignal_cs_generate(refsignal_cs_t * q, lte_cell_t cell)
uint32_t nsymbols = refsignal_cs_nof_symbols(2*p)/2; uint32_t nsymbols = refsignal_cs_nof_symbols(2*p)/2;
for (l = 0; l < nsymbols; l++) { for (l = 0; l < nsymbols; l++) {
/* Compute sequence init value */ /* Compute sequence init value */
uint32_t lp = refsignal_nsymbol(l, cell.cp, 2*p); uint32_t lp = refsignal_cs_nsymbol(l, cell.cp, 2*p);
c_init = 1024 * (7 * (ns + 1) + lp + 1) * (2 * cell.id + 1) c_init = 1024 * (7 * (ns + 1) + lp + 1) * (2 * cell.id + 1)
+ 2 * cell.id + N_cp; + 2 * cell.id + N_cp;
@ -205,7 +205,7 @@ int refsignal_cs_put_sf(lte_cell_t cell, uint32_t port_id, cf_t *pilots, cf_t *s
{ {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) { for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id); uint32_t nsymbol = refsignal_cs_nsymbol(l, cell.cp, port_id);
/* Compute offset frequency index */ /* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6); fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) { for (i = 0; i < 2*cell.nof_prb; i++) {
@ -234,7 +234,7 @@ int refsignal_cs_get_sf(lte_cell_t cell, uint32_t port_id, cf_t *sf_symbols, cf_
sf_symbols != NULL) sf_symbols != NULL)
{ {
for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) { for (l=0;l<refsignal_cs_nof_symbols(port_id);l++) {
uint32_t nsymbol = refsignal_nsymbol(l, cell.cp, port_id); uint32_t nsymbol = refsignal_cs_nsymbol(l, cell.cp, port_id);
/* Compute offset frequency index */ /* Compute offset frequency index */
fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6); fidx = ((refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6);
for (i = 0; i < 2*cell.nof_prb; i++) { for (i = 0; i < 2*cell.nof_prb; i++) {

@ -25,8 +25,6 @@
* *
*/ */
#ifdef compile
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
@ -39,6 +37,7 @@
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
#include "liblte/phy/common/sequence.h" #include "liblte/phy/common/sequence.h"
#include "ul_rs_tables.h"
// n_drms_2 table 5.5.2.1.1-1 from 36.211 // n_drms_2 table 5.5.2.1.1-1 from 36.211
uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 }; uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
@ -46,180 +45,373 @@ uint32_t n_drms_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 };
// n_drms_1 table 5.5.2.1.1-2 from 36.211 // n_drms_1 table 5.5.2.1.1-2 from 36.211
uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 }; uint32_t n_drms_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 };
/* Orthogonal sequences for PUCCH formats 1a, 1b and 1c. Table 5.5.2.2.1-2
*/
float w_arg_pucch_format1_cpnorm[3][3] = {{0, 0, 0},
{0, 2*M_PI/3, 4*M_PI/3},
{0, 4*M_PI/3, 2*M_PI/3}};
/* Generation of the reference signal sequence according to Section 5.5.1 of 36.211 */ float w_arg_pucch_format1_cpext[3][2] = {{0, 0},
int rs_sequence(ref_t * refs, uint32_t len, float alpha, uint32_t ns, uint32_t cell_id, {0, M_PI},
refsignal_ul_cfg_t * cfg) {0, 0}};
{
uint32_t i;
// Calculate u and v
uint32_t u, v;
uint32_t f_ss = (((cell_id % 30) + cfg->delta_ss) % 30);
printf("f_ss: %d\n", f_ss);
if (cfg->group_hopping_en) {
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
sequence_LTE_pr(&seq, 160, cell_id / 30);
uint32_t f_gh = 0;
for (i = 0; i < 8; i++) {
f_gh += (((uint32_t) seq.c[8 * ns + i]) << i);
}
printf("f_gh: %u\n", f_gh);
sequence_free(&seq);
u = ((f_gh%30) + f_ss) % 30;
} else {
u = f_ss % 30;
}
if (len < 6 * RE_X_RB) { float w_arg_pucch_format2_cpnorm[2] = {0, 0};
v = 0; float w_arg_pucch_format2_cpext[1] = {0};
} else {
if (!cfg->group_hopping_en && cfg->sequence_hopping_en) { uint32_t pucch_symbol_format1_cpnorm[3] = {2, 3, 5};
sequence_t seq; uint32_t pucch_symbol_format1_cpext[2] = {2, 3};
bzero(&seq, sizeof(sequence_t)); uint32_t pucch_symbol_format2_cpnorm[2] = {1, 5};
sequence_LTE_pr(&seq, 20, ((cell_id / 30) << 5) + f_ss); uint32_t pucch_symbol_format2_cpext[1] = {3};
v = seq.c[ns];
sequence_free(&seq); /** Computes n_prs values used to compute alpha as defined in 5.5.2.1.1 of 36.211 */
} else { static int generate_n_prs(refsignal_ul_t * q) {
v = 0; /* Calculate n_prs */
} uint32_t c_init;
}
printf("u: %d, v: %d\n", u, v); sequence_t seq;
if (len >= 3 * RE_X_RB) { bzero(&seq, sizeof(sequence_t));
uint32_t n_sz;
uint32_t q; for (uint32_t delta_ss=0;delta_ss<NOF_DELTA_SS;delta_ss++) {
float q_hat; c_init = ((q->cell.id / 30) << 5) + (((q->cell.id % 30) + delta_ss) % 30);
/* get largest prime n_zc<len */ if (sequence_LTE_pr(&seq, 8 * CP_NSYMB(q->cell.cp) * 20, c_init)) {
for (i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
if (prime_numbers[i] < len) {
n_sz = prime_numbers[i];
break;
}
}
printf("n_sz: %d\n", n_sz);
q_hat = (float) n_sz *(u + 1) / 31;
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
q = (uint32_t) (q_hat + 0.5) + v;
} else {
q = (uint32_t) (q_hat + 0.5) - v;
}
cf_t *x_q = malloc(sizeof(cf_t) * n_sz);
if (!x_q) {
perror("malloc");
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
for (i = 0; i < n_sz; i++) { for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) {
x_q[i] = uint32_t n_prs = 0;
cexpf(-I * M_PI * (float) q * (float) i * ((float) i + 1) / n_sz); for (int i = 0; i < 8; i++) {
n_prs += (seq.c[8 * CP_NSYMB(q->cell.cp) * ns + i] << i);
}
q->n_prs_pusch[delta_ss][ns] = n_prs;
} }
}
sequence_free(&seq);
return LIBLTE_SUCCESS;
}
for (i = 0; i < len; i++) { /** Computes sequence-group pattern f_gh according to 5.5.1.3 of 36.211 */
refs[i].simbol = cfg->beta * cexpf(I * alpha * i) * x_q[i % n_sz]; static int generate_group_hopping_f_gh(refsignal_ul_t *q) {
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
if (sequence_LTE_pr(&seq, 160, q->cell.id / 30)) {
return LIBLTE_ERROR;
}
for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) {
uint32_t f_gh = 0;
for (int i = 0; i < 8; i++) {
f_gh += (((uint32_t) seq.c[8 * ns + i]) << i);
} }
free(x_q); q->f_gh[ns] = f_gh;
} else { }
if (len == RE_X_RB) {
for (i = 0; i < len; i++) { sequence_free(&seq);
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_12[u][i] * M_PI / 4 + alpha * i)); return LIBLTE_SUCCESS;
} }
} else {
for (i = 0; i < len; i++) { static int generate_sequence_hopping_v(refsignal_ul_t *q) {
refs[i].simbol = cfg->beta * cexpf(I * (phi_M_sc_24[u][i] * M_PI / 4 + alpha * i)); sequence_t seq;
bzero(&seq, sizeof(sequence_t));
for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) {
for (uint32_t delta_ss=0;delta_ss<NOF_DELTA_SS;delta_ss++) {
if (sequence_LTE_pr(&seq, 20, ((q->cell.id / 30) << 5) + ((q->cell.id%30)+delta_ss)%30)) {
return LIBLTE_ERROR;
} }
q->v_pusch[ns][delta_ss] = seq.c[ns];
} }
} }
sequence_free(&seq);
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} }
/** Initializes refsignal_t object according to 3GPP 36.211 5.5.2
/** Initializes refsignal_ul_t object according to 3GPP 36.211 5.5
* *
*/ */
int refsignal_init_LTEUL_drms_pusch(refsignal_t * q, uint32_t nof_prb, uint32_t prb_start, int refsignal_ul_init(refsignal_ul_t * q, lte_cell_t cell)
uint32_t nslot, lte_cell_t cell, refsignal_ul_cfg_t * cfg)
{ {
uint32_t i;
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t n_prs;
uint32_t M_sc;
float alpha;
if (q != NULL && nslot < NSLOTS_X_FRAME && lte_cell_isvalid(&cell)) { if (q != NULL && lte_cell_isvalid(&cell)) {
bzero(q, sizeof(refsignal_t)); bzero(q, sizeof(refsignal_ul_t));
q->cell = cell;
M_sc = nof_prb * RE_X_RB; // Allocate temporal buffer for computing signal argument
q->tmp_arg = vec_malloc(RE_X_RB * q->cell.nof_prb * sizeof(cf_t));
q->nof_refs = M_sc; if (!q->tmp_arg) {
q->nsymbols = 1;
q->voffset = cell.id % 6;
q->nof_prb = cell.nof_prb;
q->symbols_ref = malloc(sizeof(uint32_t) * 1);
if (!q->symbols_ref) {
perror("malloc"); perror("malloc");
goto free_and_exit; goto free_and_exit;
} }
if (CP_ISNORM(cell.cp)) { // Precompute n_prs
q->symbols_ref[0] = 3; if (generate_n_prs(q)) {
} else {
q->symbols_ref[0] = 2;
}
q->refs = vec_malloc(q->nof_refs * sizeof(ref_t));
if (!q->refs) {
goto free_and_exit; goto free_and_exit;
} }
q->ch_est = vec_malloc(q->nof_refs * sizeof(cf_t));
if (!q->ch_est) { // Precompute group hopping values u.
if (generate_group_hopping_f_gh(q)) {
goto free_and_exit; goto free_and_exit;
} }
/* Calculate n_prs */ // Precompute sequence hopping values v. Uses f_ss_pusch
uint32_t c_init; if (generate_sequence_hopping_v(q)) {
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
c_init = ((cell.id / 30) << 5) + (((cell.id % 30) + cfg->delta_ss) % 30);
ret = sequence_LTE_pr(&seq, 8 * CP_NSYMB(cell.cp) * 20, c_init);
if (ret != LIBLTE_SUCCESS) {
goto free_and_exit; goto free_and_exit;
} }
n_prs = 0;
for (i = 0; i < 8; i++) {
n_prs += (seq.c[8 * CP_NSYMB(cell.cp) * nslot + i] << i);
}
sequence_free(&seq);
// Calculate cyclic shift alpha
uint32_t n_cs =
(n_drms_1[cfg->cyclic_shift] +
n_drms_2[cfg->cyclic_shift_for_drms] + n_prs) % 12;
alpha = 2 * M_PI * (n_cs) / 12;
printf("alpha: %g\n", alpha);
if (rs_sequence(q->refs, M_sc, alpha, nslot, cell.id, cfg)) { if (generate_n_cs_cell(q->cell, q->n_cs_cell)) {
fprintf(stderr, "Error generating RS sequence\n");
goto free_and_exit; goto free_and_exit;
} }
/* mapping to resource elements */
for (i=0;i<M_sc;i++) {
q->refs[i].freq_idx = prb_start*RE_X_RB + i;
q->refs[i].time_idx = q->symbols_ref[0];
}
ret = LIBLTE_SUCCESS; ret = LIBLTE_SUCCESS;
} }
free_and_exit: free_and_exit:
if (ret == LIBLTE_ERROR) { if (ret == LIBLTE_ERROR) {
refsignal_free(q); refsignal_ul_free(q);
} }
return ret; return ret;
} }
#endif void refsignal_ul_free(refsignal_ul_t * q) {
if (q->tmp_arg) {
free(q->tmp_arg);
}
bzero(q, sizeof(refsignal_ul_t));
}
uint32_t largest_prime_lower_than(uint32_t x) {
/* get largest prime n_zc<len */
for (uint32_t i = NOF_PRIME_NUMBERS - 1; i > 0; i--) {
if (prime_numbers[i] < x) {
return prime_numbers[i];
}
}
return 0;
}
static void arg_r_uv_1prb(float *arg, uint32_t u) {
for (int i = 0; i < RE_X_RB; i++) {
arg[i] = phi_M_sc_12[u][i] * M_PI / 4;
}
}
static void arg_r_uv_2prb(float *arg, uint32_t u) {
for (int i = 0; i < 2*RE_X_RB; i++) {
arg[i] = phi_M_sc_24[u][i] * M_PI / 4;
}
}
static uint32_t get_q(uint32_t u, uint32_t v, uint32_t N_sz) {
float q;
float q_hat;
float n_sz = (float) N_sz;
q_hat = n_sz *(u + 1) / 31;
if ((((uint32_t) (2 * q_hat)) % 2) == 0) {
q = q_hat + 0.5 + v;
} else {
q = q_hat + 0.5 - v;
}
return (uint32_t) q;
}
static void arg_r_uv_mprb(float *arg, uint32_t M_sc, uint32_t u, uint32_t v) {
uint32_t N_sz = largest_prime_lower_than(M_sc);
float q = get_q(u,v,N_sz);
float n_sz = (float) N_sz;
for (uint32_t i = 0; i < M_sc; i++) {
float m = (float) (i%N_sz);
arg[i] = -M_PI * q * m * (m + 1) / n_sz;
}
}
/* Computes argument of r_u_v signal */
static void compute_pusch_r_uv_arg(refsignal_ul_t *q, refsignal_drms_pusch_cfg_t *cfg, uint32_t nof_prb, uint32_t u, uint32_t v) {
if (nof_prb == 1) {
arg_r_uv_1prb(q->tmp_arg, u);
} else if (nof_prb == 2) {
arg_r_uv_2prb(q->tmp_arg, u);
} else {
arg_r_uv_mprb(q->tmp_arg, RE_X_RB*nof_prb, u, v);
}
}
/* Calculates alpha according to 5.5.2.1.1 of 36.211 */
static float pusch_get_alpha(refsignal_ul_t *q, refsignal_drms_pusch_cfg_t *cfg, uint32_t ns) {
uint32_t n_drms_2_val = 0;
if (cfg->en_drms_2) {
n_drms_2_val = n_drms_2[cfg->cyclic_shift_for_drms];
}
uint32_t n_cs = (n_drms_1[cfg->cyclic_shift] + n_drms_2_val + q->n_prs_pusch[cfg->delta_ss][ns]) % 12;
return 2 * M_PI * (n_cs) / 12;
}
bool refsignal_drms_pusch_cfg_isvalid(refsignal_ul_t *q, refsignal_drms_pusch_cfg_t *cfg, uint32_t nof_prb) {
if (cfg->cyclic_shift < NOF_CSHIFT &&
cfg->cyclic_shift_for_drms < NOF_CSHIFT &&
cfg->delta_ss < NOF_DELTA_SS &&
nof_prb < q->cell.nof_prb) {
return true;
} else {
return false;
}
}
void refsignal_drms_pusch_put(refsignal_ul_t *q, refsignal_drms_pusch_cfg_t *cfg,
cf_t *r_pusch,
uint32_t nof_prb,
uint32_t n_prb[2],
cf_t *sf_symbols)
{
for (uint32_t ns_idx=0;ns_idx<2;ns_idx++) {
DEBUG("Putting DRMS to n_prb: %d, L: %d, ns_idx: %d\n", n_prb[ns_idx], nof_prb, ns_idx);
uint32_t L = (ns_idx+1)*CP_NSYMB(q->cell.cp)-4;
memcpy(&sf_symbols[RE_IDX(q->cell.nof_prb, L, n_prb[ns_idx]*RE_X_RB)],
&r_pusch[ns_idx*RE_X_RB*nof_prb], nof_prb*RE_X_RB*sizeof(cf_t));
}
}
/* Generate DRMS for PUSCH signal according to 5.5.2.1 of 36.211 */
int refsignal_dmrs_pusch_gen(refsignal_ul_t *q, refsignal_drms_pusch_cfg_t *cfg, uint32_t nof_prb, uint32_t sf_idx, cf_t *r_pusch)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (refsignal_drms_pusch_cfg_isvalid(q, cfg, nof_prb)) {
ret = LIBLTE_ERROR;
for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
// Get group hopping number u
uint32_t f_gh=0;
if (cfg->group_hopping_en) {
f_gh = q->f_gh[ns];
}
uint32_t u = (f_gh + (q->cell.id%30)+cfg->delta_ss)%30;
// Get sequence hopping number v
uint32_t v = 0;
if (nof_prb >= 6 && cfg->sequence_hopping_en) {
v = q->v_pusch[ns][cfg->delta_ss];
}
// Compute signal argument
compute_pusch_r_uv_arg(q, cfg, nof_prb, u, v);
// Add cyclic prefix alpha
float alpha = pusch_get_alpha(q, cfg, ns);
if (verbose == VERBOSE_DEBUG) {
uint32_t N_sz = largest_prime_lower_than(nof_prb*RE_X_RB);
DEBUG("Generating PUSCH DRMS sequence with parameters:\n",0);
DEBUG("\tbeta: %.1f, nof_prb: %d, u: %d, v: %d, alpha: %f, N_sc: %d, root q: %d, nprs: %d\n",
cfg->beta_pusch, nof_prb, u, v, alpha, N_sz, get_q(u,v,N_sz),q->n_prs_pusch[cfg->delta_ss][ns]);
}
// Do complex exponential and adjust amplitude
for (int i=0;i<RE_X_RB*nof_prb;i++) {
r_pusch[(ns%2)*RE_X_RB*nof_prb+i] = cfg->beta_pusch * cexpf(I*(q->tmp_arg[i] + alpha*i));
}
}
ret = 0;
}
return ret;
}
int refsignal_dmrs_pucch_gen(refsignal_ul_t *q, pucch_cfg_t *cfg, uint32_t sf_idx, uint32_t n_rb, cf_t *r_pucch)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (pucch_cfg_isvalid(cfg)) {
ret = LIBLTE_ERROR;
for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) {
uint32_t N_rs=0;
uint32_t *pucch_symbol = NULL;
switch (cfg->format) {
case PUCCH_FORMAT_1:
case PUCCH_FORMAT_1A:
case PUCCH_FORMAT_1B:
if (CP_ISNORM(q->cell.cp)) {
N_rs = 3;
pucch_symbol=pucch_symbol_format1_cpnorm;
} else {
N_rs=2;
pucch_symbol=pucch_symbol_format1_cpext;
}
break;
case PUCCH_FORMAT_2:
if (CP_ISNORM(q->cell.cp)) {
N_rs = 2;
pucch_symbol=pucch_symbol_format2_cpnorm;
} else {
N_rs=1;
pucch_symbol=pucch_symbol_format2_cpext;
}
break;
case PUCCH_FORMAT_2A:
case PUCCH_FORMAT_2B:
N_rs = 2;
pucch_symbol=pucch_symbol_format2_cpnorm;
break;
}
if (pucch_symbol) {
for (uint32_t m=0;m<N_rs;m++) {
uint32_t n_oc=0;
uint32_t l = pucch_symbol[m];
// Add cyclic prefix alpha
float alpha = pucch_get_alpha(q->n_cs_cell, cfg, q->cell.cp, true, ns, l, &n_oc);
// Choose number of symbols and orthogonal sequence from Tables 5.5.2.2.1-1 to -3
float *w=NULL;
switch (cfg->format) {
case PUCCH_FORMAT_1:
case PUCCH_FORMAT_1A:
case PUCCH_FORMAT_1B:
if (CP_ISNORM(q->cell.cp)) {
w=w_arg_pucch_format1_cpnorm[n_oc];
} else {
w=w_arg_pucch_format1_cpext[n_oc];
}
break;
case PUCCH_FORMAT_2:
if (CP_ISNORM(q->cell.cp)) {
w=w_arg_pucch_format2_cpnorm;
} else {
w=w_arg_pucch_format2_cpext;
}
break;
case PUCCH_FORMAT_2A:
case PUCCH_FORMAT_2B:
w=w_arg_pucch_format2_cpnorm;
break;
}
if (w) {
for (uint32_t n=0;n<RE_X_RB*n_rb;n++) {
r_pucch[(ns%2)*RE_X_RB*n_rb*N_rs+m*RE_X_RB*n_rb+n] = cfg->beta_pucch*cexpf(I*(w[m]+q->tmp_arg[n]+alpha*n));
}
} else {
return LIBLTE_ERROR;
}
}
} else {
return LIBLTE_ERROR;
}
}
ret = LIBLTE_SUCCESS;
}
return ret;
}
void refsignal_srs_gen(refsignal_ul_t *q, refsignal_srs_cfg_t *cfg, uint32_t ns, cf_t *r_srs)
{
}

@ -92,7 +92,7 @@ int phi_M_sc_24[30][24] = {{-1, 3, 1,-3, 3,-1, 1, 3,-3, 3, 1, 3,-3, 3, 1, 1,-1,
{ 1, 1,-1,-1,-3,-1, 3,-1, 3,-1, 1, 3, 1,-1, 3, 1, 3,-3,-3, 1,-1,-1, 1, 3}}; { 1, 1,-1,-1,-3,-1, 3,-1, 3,-1, 1, 3, 1,-1, 3, 1, 3,-3,-3, 1,-1,-1, 1, 3}};
// Prime numbers used for Section 5.5.1.1 of 36.211 // Prime numbers used for Section 5.5.1.1 of 36.211
#define NOF_PRIME_NUMBERS 309 #define NOF_PRIME_NUMBERS 196
uint32_t prime_numbers[NOF_PRIME_NUMBERS] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, uint32_t prime_numbers[NOF_PRIME_NUMBERS] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
@ -112,16 +112,5 @@ uint32_t prime_numbers[NOF_PRIME_NUMBERS] = { 2, 3, 5, 7, 11, 13, 17,
947, 953, 967, 971, 977, 983, 991, 997,1009,1013, 947, 953, 967, 971, 977, 983, 991, 997,1009,1013,
1019,1021,1031,1033,1039,1049,1051,1061,1063,1069, 1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,
1087,1091,1093,1097,1103,1109,1117,1123,1129,1151, 1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,
1153,1163,1171,1181,1187,1193,1201,1213,1217,1223, 1153,1163,1171,1181,1187,1193};
1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,
1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,
1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,
1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,
1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,
1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,
1663,1667,1669,1693,1697,1699,1709,1721,1723,1733,
1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,
1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,
1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,
1993,1997,1999,2003,2011,2017,2027,2029,2039};

@ -45,14 +45,11 @@ BuildMex(MEXNAME chest SOURCES chest_test_dl_mex.c LIBRARIES lte_phy)
# Uplink Channel Estimation TEST # Uplink Channel Estimation TEST
######################################################################## ########################################################################
#ADD_EXECUTABLE(chest_test_ul chest_test_ul.c) ADD_EXECUTABLE(refsignal_ul_test_all refsignal_ul_test.c)
#TARGET_LINK_LIBRARIES(chest_test_ul lte_phy) TARGET_LINK_LIBRARIES(refsignal_ul_test_all lte_phy)
#ADD_TEST(chest_test_ul_cellid0 chest_ul_test -c 0)
#ADD_TEST(chest_test_ul_cellid1 chest_ul_test -c 1)
#ADD_TEST(chest_test_ul_cellid2 chest_ul_test -c 2)
BuildMex(MEXNAME refsignal_pusch SOURCES refsignal_pusch_mex.c LIBRARIES lte_phy liblte_mex)

@ -1,249 +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 <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <complex.h>
#include "liblte/phy/phy.h"
lte_cell_t cell = {
6, // nof_prb
MAX_PORTS, // nof_ports
1000, // cell_id
CPNORM // cyclic prefix
};
uint8_t *output_matlab = NULL;
void usage(char *prog) {
printf("Usage: %s [recov]\n", prog);
printf("\t-r nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-e extended cyclic prefix [Default normal]\n");
printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id);
printf("\t-o output matlab file [Default %s]\n",output_matlab?output_matlab:"None");
printf("\t-v increase verbosity\n");
}
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "recov")) != -1) {
switch(opt) {
case 'r':
cell.nof_prb = atoi(argv[optind]);
break;
case 'e':
cell.cp = CPEXT;
break;
case 'c':
cell.id = atoi(argv[optind]);
break;
case 'o':
output_matlab = argv[optind];
break;
case 'v':
verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
int check_mse(float mod, float arg, int n_port) {
INFO("mod=%.4f, arg=%.4f, n_port=%d\n", mod, arg, n_port);
switch(n_port) {
case 0:
if (mod > 0.029) {
return -1;
}
if (arg > 0.029) {
return -1;
}
break;
case 1:
if (mod > 0.012) {
return -1;
}
if (arg > 0.012) {
return -1;
}
break;
case 2:
case 3:
if (mod > 3.33) {
return -1;
}
if (arg > 0.63) {
return -1;
}
break;
default:
return -1;
}
return 0;
}
int main(int argc, char **argv) {
chest_t eq;
cf_t *input = NULL, *ce = NULL, *h = NULL;
refsignal_t refs;
int i, j, n_port, n_slot, cid, num_re;
int ret = -1;
int max_cid;
FILE *fmatlab = NULL;
float mse_mag, mse_phase;
parse_args(argc,argv);
if (output_matlab) {
fmatlab=fopen(output_matlab, "w");
if (!fmatlab) {
perror("fopen");
goto do_exit;
}
}
num_re = cell.nof_prb * RE_X_RB * CP_NSYMB(cell.cp);
input = malloc(num_re * sizeof(cf_t));
if (!input) {
perror("malloc");
goto do_exit;
}
h = malloc(num_re * sizeof(cf_t));
if (!h) {
perror("malloc");
goto do_exit;
}
ce = malloc(num_re * sizeof(cf_t));
if (!ce) {
perror("malloc");
goto do_exit;
}
if (cell.id == 1000) {
cid = 0;
max_cid = 504;
} else {
cid = cell.id;
max_cid = cell.id;
}
while(cid <= max_cid) {
cell.id = cid;
if (chest_init_LTEUL(&eq, cell)) {
fprintf(stderr, "Error initializing equalizer\n");
goto do_exit;
}
for (n_slot=0;n_slot<NSLOTS_X_FRAME;n_slot++) {
for (n_port=0;n_port<cell.nof_ports;n_port++) {
if (refsignal_init_LTEDL(&refs, n_port, n_slot, cell)) {
fprintf(stderr, "Error initiating CRS slot=%d\n", i);
return -1;
}
bzero(input, sizeof(cf_t) * num_re);
for (i=0;i<num_re;i++) {
input[i] = 0.5-rand()/RAND_MAX+I*(0.5-rand()/RAND_MAX);
}
bzero(ce, sizeof(cf_t) * num_re);
bzero(h, sizeof(cf_t) * num_re);
refsignal_put(&refs, input);
for (i=0;i<CP_NSYMB(cell.cp);i++) {
for (j=0;j<cell.nof_prb * RE_X_RB;j++) {
float x = -1+(float) i/CP_NSYMB(cell.cp) + cosf(2 * M_PI * (float) j/cell.nof_prb/RE_X_RB);
h[i*cell.nof_prb * RE_X_RB+j] = (3+x) * cexpf(I * x);
input[i*cell.nof_prb * RE_X_RB+j] *= h[i*cell.nof_prb * RE_X_RB+j];
}
}
chest_ce_slot_port(&eq, input, ce, n_slot, n_port);
mse_mag = mse_phase = 0;
for (i=0;i<num_re;i++) {
mse_mag += (cabsf(h[i]) - cabsf(ce[i])) * (cabsf(h[i]) - cabsf(ce[i])) / num_re;
mse_phase += (cargf(h[i]) - cargf(ce[i])) * (cargf(h[i]) - cargf(ce[i])) / num_re;
}
if (check_mse(mse_mag, mse_phase, n_port)) {
goto do_exit;
}
if (fmatlab) {
fprintf(fmatlab, "input=");
vec_fprint_c(fmatlab, input, num_re);
fprintf(fmatlab, ";\n");
fprintf(fmatlab, "h=");
vec_fprint_c(fmatlab, h, num_re);
fprintf(fmatlab, ";\n");
fprintf(fmatlab, "ce=");
vec_fprint_c(fmatlab, ce, num_re);
fprintf(fmatlab, ";\n");
chest_fprint(&eq, fmatlab, n_slot, n_port);
}
}
}
chest_free(&eq);
cid+=10;
INFO("cid=%d\n", cid);
}
ret = 0;
do_exit:
if (ce) {
free(ce);
}
if (input) {
free(input);
}
if (h) {
free(h);
}
if (!ret) {
printf("OK\n");
} else {
printf("Error at cid=%d, slot=%d, port=%d\n",cid, n_slot, n_port);
}
exit(ret);
}

@ -0,0 +1,153 @@
/**
*
* \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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the channel estimator
*/
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define NOF_INPUTS 2
void help()
{
mexErrMsgTxt
("[seq] = liblte_refsignal_pusch(ueConfig, puschConfig)\n\n");
}
extern int indices[2048];
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
lte_cell_t cell;
refsignal_ul_t refs;
refsignal_drms_pusch_cfg_t pusch_cfg;
uint32_t sf_idx;
if (nrhs != NOF_INPUTS) {
help();
return;
}
if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) {
mexErrMsgTxt("Field NCellID not found in UE config\n");
return;
}
if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) {
mexErrMsgTxt("Field NCellID not found in UE config\n");
return;
}
cell.cp = CPNORM;
cell.nof_ports = 1;
if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) {
mexErrMsgTxt("Field NSubframe not found in UE config\n");
return;
}
bzero(&pusch_cfg, sizeof(refsignal_drms_pusch_cfg_t));
pusch_cfg.group_hopping_en = false;
pusch_cfg.sequence_hopping_en = false;
char *tmp = mexutils_get_char_struct(UECFG, "Hopping");
if (tmp) {
if (!strcmp(tmp, "Group")) {
pusch_cfg.group_hopping_en = true;
} else if (!strcmp(tmp, "Sequence")) {
pusch_cfg.sequence_hopping_en = true;
}
mxFree(tmp);
}
if (mexutils_read_uint32_struct(UECFG, "SeqGroup", &pusch_cfg.delta_ss)) {
pusch_cfg.delta_ss = 0;
}
if (mexutils_read_uint32_struct(UECFG, "CyclicShift", &pusch_cfg.cyclic_shift)) {
pusch_cfg.cyclic_shift = 0;
}
float *prbset;
mxArray *p;
p = mxGetField(PUSCHCFG, 0, "PRBSet");
if (!p) {
mexErrMsgTxt("Error field PRBSet not found in PUSCH config\n");
return;
}
uint32_t nof_prb = mexutils_read_f(p, &prbset);
if (mexutils_read_uint32_struct(PUSCHCFG, "DynCyclicShift", &pusch_cfg.cyclic_shift_for_drms)) {
pusch_cfg.cyclic_shift_for_drms = 0;
pusch_cfg.en_drms_2 = false;
} else {
pusch_cfg.en_drms_2 = true;
}
pusch_cfg.beta_pusch = 1.0;
if (refsignal_ul_init(&refs, cell)) {
mexErrMsgTxt("Error initiating refsignal_ul\n");
return;
}
mexPrintf("nof_prb: %d, ",nof_prb);
mexPrintf("cyclic_shift: %d, ",pusch_cfg.cyclic_shift);
mexPrintf("cyclic_shift_for_drms: %d, ",pusch_cfg.cyclic_shift_for_drms);
mexPrintf("delta_ss: %d, ",pusch_cfg.delta_ss);
cf_t *signal = vec_malloc(2*RE_X_RB*nof_prb*sizeof(cf_t));
if (!signal) {
perror("malloc");
return;
}
cf_t *sf_symbols = vec_malloc(SF_LEN_RE(cell.nof_prb, cell.cp)*sizeof(cf_t));
if (!sf_symbols) {
perror("malloc");
return;
}
bzero(sf_symbols, SF_LEN_RE(cell.nof_prb, cell.cp)*sizeof(cf_t));
//mexPrintf("Generating DRMS for ns=%d, nof_prb=%d\n", 2*sf_idx+i,pusch_cfg.nof_prb);
refsignal_dmrs_pusch_gen(&refs, &pusch_cfg, nof_prb, sf_idx, signal);
uint32_t n_prb[2];
n_prb[0] = prbset[0];
n_prb[1] = prbset[0];
refsignal_drms_pusch_put(&refs, &pusch_cfg, signal, nof_prb, n_prb, sf_symbols);
if (nlhs >= 1) {
mexutils_write_cf(sf_symbols, &plhs[0], SF_LEN_RE(cell.nof_prb, cell.cp), 1);
}
refsignal_ul_free(&refs);
free(signal);
free(prbset);
return;
}

@ -0,0 +1,149 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <complex.h>
#include "liblte/phy/phy.h"
lte_cell_t cell = {
100, // nof_prb
MAX_PORTS, // nof_ports
1, // cell_id
CPNORM // cyclic prefix
};
void usage(char *prog) {
printf("Usage: %s [recv]\n", prog);
printf("\t-r nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-e extended cyclic prefix [Default normal]\n");
printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id);
printf("\t-v increase verbosity\n");
}
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "recv")) != -1) {
switch(opt) {
case 'r':
cell.nof_prb = atoi(argv[optind]);
break;
case 'e':
cell.cp = CPEXT;
break;
case 'c':
cell.id = atoi(argv[optind]);
break;
case 'v':
verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
int main(int argc, char **argv) {
refsignal_ul_t refs;
refsignal_drms_pusch_cfg_t pusch_cfg;
cf_t *signal = NULL;
int ret = -1;
parse_args(argc,argv);
signal = malloc(2 * RE_X_RB * cell.nof_prb * sizeof(cf_t));
if (!signal) {
perror("malloc");
goto do_exit;
}
if (refsignal_ul_init(&refs, cell)) {
fprintf(stderr, "Error initializing UL reference signal\n");
goto do_exit;
}
printf("Running tests for %d PRB\n", cell.nof_prb);
for (int n=6;n<cell.nof_prb;n++) {
for (int delta_ss=29;delta_ss<NOF_DELTA_SS;delta_ss++) {
for (int cshift=0;cshift<NOF_CSHIFT;cshift++) {
for (int h=0;h<3;h++) {
for (int sf_idx=0;sf_idx<NSLOTS_X_FRAME;sf_idx++) {
for (int cshift_drms=0;cshift_drms<NOF_CSHIFT;cshift_drms++) {
pusch_cfg.beta_pusch = 1.0;
uint32_t nof_prb = n;
pusch_cfg.cyclic_shift = cshift;
pusch_cfg.cyclic_shift_for_drms = cshift_drms;
pusch_cfg.delta_ss = delta_ss;
if (!h) {
pusch_cfg.group_hopping_en = false;
pusch_cfg.sequence_hopping_en = false;
} else if (h == 1) {
pusch_cfg.group_hopping_en = false;
pusch_cfg.sequence_hopping_en = true;
} else if (h == 2) {
pusch_cfg.group_hopping_en = true;
pusch_cfg.sequence_hopping_en = false;
}
pusch_cfg.en_drms_2 = true;
printf("Beta: %f, ",pusch_cfg.beta_pusch);
printf("nof_prb: %d, ",nof_prb);
printf("cyclic_shift: %d, ",pusch_cfg.cyclic_shift);
printf("cyclic_shift_for_drms: %d, ",pusch_cfg.cyclic_shift_for_drms);
printf("delta_ss: %d, ",pusch_cfg.delta_ss);
printf("SF_idx: %d\n", sf_idx);
refsignal_dmrs_pusch_gen(&refs, &pusch_cfg, nof_prb, sf_idx, signal);
exit(0);
}
}
}
}
}
}
ret = 0;
do_exit:
if (signal) {
free(signal);
}
refsignal_ul_free(&refs);
if (!ret) {
printf("OK\n");
}
exit(ret);
}

@ -28,6 +28,8 @@
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <stdlib.h> #include <stdlib.h>
#include <complex.h>
#include <math.h>
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/dft.h" #include "liblte/phy/utils/dft.h"
@ -54,13 +56,11 @@ int lte_fft_init_(lte_fft_t *q, lte_cp_t cp, uint32_t nof_prb, dft_dir_t dir) {
dft_plan_set_mirror(&q->fft_plan, true); dft_plan_set_mirror(&q->fft_plan, true);
dft_plan_set_dc(&q->fft_plan, true); dft_plan_set_dc(&q->fft_plan, true);
#ifdef LTE_FFT_NORMALIZE
dft_plan_set_norm(&q->fft_plan, true);
#endif
q->symbol_sz = (uint32_t) symbol_sz; q->symbol_sz = (uint32_t) symbol_sz;
q->nof_symbols = CP_NSYMB(cp); q->nof_symbols = CP_NSYMB(cp);
q->cp = cp; q->cp = cp;
q->freq_shift = false;
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); q->slot_sz = SLOT_LEN(symbol_sz);
@ -77,6 +77,9 @@ void lte_fft_free_(lte_fft_t *q) {
if (q->tmp) { if (q->tmp) {
free(q->tmp); free(q->tmp);
} }
if (q->shift_buffer) {
free(q->shift_buffer);
}
bzero(q, sizeof(lte_fft_t)); bzero(q, sizeof(lte_fft_t));
} }
@ -106,6 +109,35 @@ int lte_ifft_init(lte_fft_t *q, lte_cp_t cp, uint32_t nof_prb) {
return ret; return ret;
} }
/* Shifts the signal after the iFFT or before the FFT.
* Freq_shift is relative to inter-carrier spacing.
* Caution: This function shall not be called during run-time
*/
int lte_fft_set_freq_shift(lte_fft_t *q, float freq_shift) {
q->shift_buffer = vec_malloc(sizeof(cf_t) * SF_LEN(q->symbol_sz));
if (!q->shift_buffer) {
perror("malloc");
return -1;
}
cf_t *ptr = q->shift_buffer;
for (uint32_t n=0;n<2;n++) {
for (uint32_t i=0;i<q->nof_symbols;i++) {
uint32_t cplen = CP_ISNORM(q->cp)?CP_NORM(i, q->symbol_sz):CP_EXT(q->symbol_sz);
for (uint32_t t=0;t<q->symbol_sz+cplen;t++) {
ptr[t] = cexpf(I*2*M_PI*((float) t-(float)cplen)*freq_shift/q->symbol_sz);
}
ptr += q->symbol_sz+cplen;
}
}
/* Disable DC carrier addition */
dft_plan_set_dc(&q->fft_plan, false);
q->freq_shift = true;
return LIBLTE_SUCCESS;
}
void lte_ifft_free(lte_fft_t *q) { void lte_ifft_free(lte_fft_t *q) {
lte_fft_free_(q); lte_fft_free_(q);
} }
@ -126,6 +158,9 @@ void lte_fft_run_slot(lte_fft_t *q, cf_t *input, cf_t *output) {
void lte_fft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) { void lte_fft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) {
uint32_t n; uint32_t n;
if (q->freq_shift) {
vec_prod_ccc(input, q->shift_buffer, input, 2*q->slot_sz);
}
for (n=0;n<2;n++) { for (n=0;n<2;n++) {
lte_fft_run_slot(q, &input[n*q->slot_sz], &output[n*q->nof_re*q->nof_symbols]); lte_fft_run_slot(q, &input[n*q->slot_sz], &output[n*q->nof_re*q->nof_symbols]);
} }
@ -147,9 +182,16 @@ void lte_ifft_run_slot(lte_fft_t *q, cf_t *input, cf_t *output) {
} }
} }
void lte_fft_set_normalize(lte_fft_t *q, bool normalize_enable) {
dft_plan_set_norm(&q->fft_plan, normalize_enable);
}
void lte_ifft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) { void lte_ifft_run_sf(lte_fft_t *q, cf_t *input, cf_t *output) {
uint32_t n; uint32_t n;
for (n=0;n<2;n++) { for (n=0;n<2;n++) {
lte_ifft_run_slot(q, &input[n*q->nof_re*q->nof_symbols], &output[n*q->slot_sz]); lte_ifft_run_slot(q, &input[n*q->nof_re*q->nof_symbols], &output[n*q->slot_sz]);
} }
if (q->freq_shift) {
vec_prod_ccc(output, q->shift_buffer, output, 2*q->slot_sz);
}
} }

@ -170,6 +170,31 @@ char *lte_cp_string(lte_cp_t cp) {
} }
} }
/* Returns the new time advance N_ta_new as specified in Section 4.2.3 of 36.213 */
uint32_t lte_N_ta_new(uint32_t N_ta_old, uint32_t ta) {
ta &= 63;
int n_ta_new = N_ta_old + ((float) ta - 31) * 16;
if (n_ta_new < 0) {
return 0;
} else {
if (n_ta_new < 20512) {
return (uint32_t) n_ta_new;
} else {
return 20512;
}
}
}
/* Returns the new time advance as indicated by the random access response
* as specified in Section 4.2.3 of 36.213 */
uint32_t lte_N_ta_new_rar(uint32_t ta) {
if (ta > 1282) {
ta = 1282;
}
return ta*16;
}
/* /*
* Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212 * Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212
*/ */

@ -0,0 +1,82 @@
/**
*
* \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/common/timestamp.h"
#include "math.h"
int timestamp_init(timestamp_t *t, time_t full_secs, double frac_secs){
int ret = LIBLTE_ERROR;
if(t != NULL && frac_secs >= 0.0){
t->full_secs = full_secs;
t->frac_secs = frac_secs;
ret = LIBLTE_SUCCESS;
}
return ret;
}
int timestamp_copy(timestamp_t *dest, timestamp_t *src){
int ret = LIBLTE_ERROR;
if(dest != NULL && src != NULL){
dest->full_secs = src->full_secs;
dest->frac_secs = src->frac_secs;
ret = LIBLTE_SUCCESS;
}
return ret;
}
int timestamp_add(timestamp_t *t, time_t full_secs, double frac_secs){
int ret = LIBLTE_ERROR;
if(t != NULL && frac_secs >= 0.0){
t->frac_secs += frac_secs;
t->full_secs += full_secs;
double r = floor(t->frac_secs);
t->full_secs += r;
t->frac_secs -= r;
ret = LIBLTE_SUCCESS;
}
return ret;
}
int timestamp_sub(timestamp_t *t, time_t full_secs, double frac_secs){
int ret = LIBLTE_ERROR;
if(t != NULL && frac_secs >= 0.0){
t->frac_secs -= frac_secs;
t->full_secs -= full_secs;
if(t->frac_secs < 0){
t->frac_secs = 1-t->frac_secs;
t->full_secs--;
}
if(t->full_secs < 0)
return LIBLTE_ERROR;
ret = LIBLTE_SUCCESS;
}
return ret;
}
double timestamp_real(timestamp_t *t){
return t->frac_secs + t->full_secs;
}

@ -61,6 +61,11 @@ int rm_turbo_tx(uint8_t *w_buff, uint32_t w_buff_len, uint8_t *input, uint32_t i
int i, j, k, s, N_cb, k0; int i, j, k, s, N_cb, k0;
if (in_len < 3) {
fprintf(stderr, "Error minimum input length for rate matching is 3\n");
return -1;
}
nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1; nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1;
K_p = nrows * NCOLS; K_p = nrows * NCOLS;
if (3 * K_p > w_buff_len) { if (3 * K_p > w_buff_len) {
@ -132,7 +137,7 @@ int rm_turbo_tx(uint8_t *w_buff, uint32_t w_buff_len, uint8_t *input, uint32_t i
* with rv_idx!=0 will soft-combine the LLRs from input with w_buff * with rv_idx!=0 will soft-combine the LLRs from input with w_buff
*/ */
int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_len, float *output, int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_len, float *output,
uint32_t out_len, uint32_t rv_idx) { uint32_t out_len, uint32_t rv_idx, uint32_t nof_filler_bits) {
int nrows, ndummy, K_p, k0, N_cb, jp, kidx; int nrows, ndummy, K_p, k0, N_cb, jp, kidx;
int i, j, k; int i, j, k;
@ -148,6 +153,12 @@ int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_le
return -1; return -1;
} }
if (out_len < 3) {
fprintf(stderr, "Error minimum input length for rate matching is 3\n");
return -1;
}
ndummy = K_p - out_len / 3; ndummy = K_p - out_len / 3;
if (ndummy < 0) { if (ndummy < 0) {
ndummy = 0; ndummy = 0;
@ -179,9 +190,13 @@ int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_le
} }
if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) {
isdummy = false; isdummy = false;
if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < nof_filler_bits) {
isdummy = true;
}
} else { } else {
isdummy = true; isdummy = true;
} }
} else { } else {
uint32_t jpp = (jp - K_p - 1) / 2; uint32_t jpp = (jp - K_p - 1) / 2;
kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p;

@ -78,9 +78,13 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
k = 0; k = 0;
for (i = 0; i < long_cb; i++) { for (i = 0; i < long_cb; i++) {
bit = input[i]; if (input[i] == TX_NULL) {
bit = 0;
} else {
bit = input[i];
}
output[k] = input[i];
output[k] = bit;
k++; k++;
in = bit ^ (reg1_2 ^ reg1_1); in = bit ^ (reg1_2 ^ reg1_1);
@ -90,10 +94,17 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
reg1_1 = reg1_0; reg1_1 = reg1_0;
reg1_0 = in; reg1_0 = in;
output[k] = out; if (input[i] == TX_NULL) {
output[k] = TX_NULL;
} else {
output[k] = out;
}
k++; k++;
bit = input[per[i]]; bit = input[per[i]];
if (bit == TX_NULL) {
bit = 0;
}
in = bit ^ (reg2_2 ^ reg2_1); in = bit ^ (reg2_2 ^ reg2_1);
out = reg2_2 ^ (reg2_0 ^ in); out = reg2_2 ^ (reg2_0 ^ in);
@ -104,6 +115,8 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
output[k] = out; output[k] = out;
k++; k++;
} }
k = 3 * long_cb; k = 3 * long_cb;

@ -33,6 +33,7 @@
#include <math.h> #include <math.h>
#include "liblte/phy/fec/turbodecoder.h" #include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/utils/vector.h"
/************************************************ /************************************************
* *
@ -157,9 +158,9 @@ 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 = vec_malloc(sizeof(llr_t) * (max_long_cb + TOTALTAIL + 1) * NUMSTATES);
if (!h->beta) { if (!h->beta) {
perror("malloc"); perror("vec_malloc");
return -1; return -1;
} }
h->max_long_cb = max_long_cb; h->max_long_cb = max_long_cb;
@ -200,29 +201,29 @@ int tdec_init(tdec_t * h, uint32_t max_long_cb)
h->max_long_cb = max_long_cb; h->max_long_cb = max_long_cb;
h->llr1 = malloc(sizeof(llr_t) * len); h->llr1 = vec_malloc(sizeof(llr_t) * len);
if (!h->llr1) { if (!h->llr1) {
perror("malloc"); perror("vec_malloc");
goto clean_and_exit; goto clean_and_exit;
} }
h->llr2 = malloc(sizeof(llr_t) * len); h->llr2 = vec_malloc(sizeof(llr_t) * len);
if (!h->llr2) { if (!h->llr2) {
perror("malloc"); perror("vec_malloc");
goto clean_and_exit; goto clean_and_exit;
} }
h->w = malloc(sizeof(llr_t) * len); h->w = vec_malloc(sizeof(llr_t) * len);
if (!h->w) { if (!h->w) {
perror("malloc"); perror("vec_malloc");
goto clean_and_exit; goto clean_and_exit;
} }
h->syst = malloc(sizeof(llr_t) * len); h->syst = vec_malloc(sizeof(llr_t) * len);
if (!h->syst) { if (!h->syst) {
perror("malloc"); perror("vec_malloc");
goto clean_and_exit; goto clean_and_exit;
} }
h->parity = malloc(sizeof(llr_t) * len); h->parity = vec_malloc(sizeof(llr_t) * len);
if (!h->parity) { if (!h->parity) {
perror("malloc"); perror("vec_malloc");
goto clean_and_exit; goto clean_and_exit;
} }

@ -38,6 +38,7 @@ ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 1)
ADD_TEST(rm_turbo_test_1 rm_turbo_test -t 480 -r 1920 -i 2) ADD_TEST(rm_turbo_test_1 rm_turbo_test -t 480 -r 1920 -i 2)
ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 3) ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 3)
BuildMex(MEXNAME rm_turbo_rx SOURCES rm_turbo_rx_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# Turbo Coder TEST # Turbo Coder TEST

@ -0,0 +1,100 @@
/**
*
* \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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the channel estimator
*/
#define INPUT prhs[0]
#define TRBLKLEN prhs[1]
#define RV prhs[2]
#define NOF_INPUTS 3
void help()
{
mexErrMsgTxt
("[out] = liblte_rm_turbo_rx(in, trblkin, rv)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
float *input;
float *output;
uint32_t in_len, trblklen, cblen, rvidx;
float *w_buff_f;
if (nrhs != NOF_INPUTS) {
help();
return;
}
// Read input symbols
in_len = mexutils_read_f(INPUT, &input);
if (in_len < 0) {
mexErrMsgTxt("Error reading input bits\n");
return;
}
trblklen = (uint32_t) mxGetScalar(TRBLKLEN);
rvidx = (uint32_t) mxGetScalar(RV);
struct cb_segm cbsegm;
codeblock_segmentation(&cbsegm, trblklen);
cblen = 3*cbsegm.K1+12;
w_buff_f = calloc(1,sizeof(float) * cblen * 10);
if (!w_buff_f) {
perror("malloc");
exit(-1);
}
// allocate memory for output bits
output = vec_malloc(cblen * sizeof(float));
rm_turbo_rx(w_buff_f, cblen * 10, input, in_len, output, cblen,
rvidx,cbsegm.F);
if (nlhs >= 1) {
mexutils_write_f(output, &plhs[0], cblen, 1);
}
if (nlhs >= 2) {
mexutils_write_f(input, &plhs[1], in_len, 1);
}
free(input);
free(output);
free(w_buff_f);
return;
}

@ -37,16 +37,20 @@
#include "liblte/phy/phy.h" #include "liblte/phy/phy.h"
int nof_tx_bits = -1, nof_rx_bits = -1; int nof_tx_bits = -1, nof_rx_bits = -1;
int nof_filler_bits = -1;
int rv_idx = 0; int rv_idx = 0;
void usage(char *prog) { void usage(char *prog) {
printf("Usage: %s -t nof_tx_bits -r nof_rx_bits [-i rv_idx]\n", prog); printf("Usage: %s -t nof_tx_bits -r nof_rx_bits [-i rv_idx -f nof_filler_bits]\n", prog);
} }
void parse_args(int argc, char **argv) { void parse_args(int argc, char **argv) {
int opt; int opt;
while ((opt = getopt(argc, argv, "tri")) != -1) { while ((opt = getopt(argc, argv, "trif")) != -1) {
switch (opt) { switch (opt) {
case 'f':
nof_filler_bits = atoi(argv[optind]);
break;
case 't': case 't':
nof_tx_bits = atoi(argv[optind]); nof_tx_bits = atoi(argv[optind]);
break; break;
@ -73,7 +77,7 @@ void parse_args(int argc, char **argv) {
int main(int argc, char **argv) { int main(int argc, char **argv) {
int i; int i;
uint8_t *bits, *rm_bits, *w_buff_c; uint8_t *bits, *bits_out, *rm_bits, *w_buff_c;
float *rm_symbols, *unrm_symbols, *w_buff_f; float *rm_symbols, *unrm_symbols, *w_buff_f;
int nof_errors; int nof_errors;
@ -84,6 +88,11 @@ int main(int argc, char **argv) {
perror("malloc"); perror("malloc");
exit(-1); exit(-1);
} }
bits_out = malloc(sizeof(uint8_t) * nof_tx_bits);
if (!bits_out) {
perror("malloc");
exit(-1);
}
w_buff_c = malloc(sizeof(uint8_t) * nof_tx_bits * 10); w_buff_c = malloc(sizeof(uint8_t) * nof_tx_bits * 10);
if (!w_buff_c) { if (!w_buff_c) {
perror("malloc"); perror("malloc");
@ -114,14 +123,36 @@ int main(int argc, char **argv) {
bits[i] = rand() % 2; bits[i] = rand() % 2;
} }
for (i=0;i<nof_filler_bits;i++) {
bits[3*i+0] = TX_NULL;
bits[3*i+1] = TX_NULL;
}
printf("BITS: ");
vec_fprint_b(stdout, bits, nof_tx_bits);
rm_turbo_tx(w_buff_c, nof_tx_bits * 10, bits, nof_tx_bits, rm_bits, nof_rx_bits, rv_idx); rm_turbo_tx(w_buff_c, nof_tx_bits * 10, bits, nof_tx_bits, rm_bits, nof_rx_bits, rv_idx);
printf("RM: ");
vec_fprint_b(stdout, rm_bits, nof_rx_bits);
for (i = 0; i < nof_rx_bits; i++) { for (i = 0; i < nof_rx_bits; i++) {
rm_symbols[i] = (float) rm_bits[i] ? 1 : -1; rm_symbols[i] = (float) rm_bits[i] ? 1 : -1;
} }
rm_turbo_rx(w_buff_f, nof_rx_bits * 10, rm_symbols, nof_rx_bits, unrm_symbols, nof_tx_bits, rm_turbo_rx(w_buff_f, nof_rx_bits * 10, rm_symbols, nof_rx_bits, unrm_symbols, nof_tx_bits,
rv_idx); rv_idx, nof_filler_bits);
printf("UMRM: ");
vec_fprint_f(stdout, unrm_symbols, nof_tx_bits);
for (i=0;i<nof_tx_bits;i++) {
bits_out[i] = unrm_symbols[i]>0?1:0;
}
printf("BITS: ");
vec_fprint_b(stdout, bits_out, nof_tx_bits);
printf("BITS: ");
vec_fprint_b(stdout, bits, nof_tx_bits);
nof_errors = 0; nof_errors = 0;
for (i = 0; i < nof_tx_bits; i++) { for (i = 0; i < nof_tx_bits; i++) {
@ -134,6 +165,7 @@ int main(int argc, char **argv) {
free(rm_bits); free(rm_bits);
free(rm_symbols); free(rm_symbols);
free(unrm_symbols); free(unrm_symbols);
free(bits_out);
if (nof_errors) { if (nof_errors) {
printf("nof_errors=%d\n", nof_errors); printf("nof_errors=%d\n", nof_errors);

@ -0,0 +1,128 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/dft.h"
#include "liblte/phy/filter/dft_precoding.h"
/* Create DFT plans for transform precoding */
int dft_precoding_init(dft_precoding_t *q, uint32_t max_prb)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
bzero(q, sizeof(dft_precoding_t));
if (max_prb <= MAX_PRB) {
ret = LIBLTE_ERROR;
for (uint32_t i=1;i<max_prb;i++) {
if(dft_precoding_valid_prb(i)) {
DEBUG("Initiating DFT precoding plan for %d PRBs\n", i);
if (dft_plan_c(&q->dft_plan[i], i*RE_X_RB, FORWARD)) {
fprintf(stderr, "Error: Creating DFT plan %d\n",i);
goto clean_exit;
}
dft_plan_set_norm(&q->dft_plan[i], true);
if (dft_plan_c(&q->idft_plan[i], i*RE_X_RB, BACKWARD)) {
fprintf(stderr, "Error: Creating DFT plan %d\n",i);
goto clean_exit;
}
dft_plan_set_norm(&q->idft_plan[i], true);
}
}
q->max_prb = max_prb;
ret = LIBLTE_SUCCESS;
}
clean_exit:
if (ret == LIBLTE_ERROR) {
dft_precoding_free(q);
}
return ret;
}
/* Free DFT plans for transform precoding */
void dft_precoding_free(dft_precoding_t *q)
{
for (uint32_t i=1;i<q->max_prb;i++) {
if(dft_precoding_valid_prb(i)) {
DEBUG("Freeing DFT precoding plan for %d PRBs\n", i);
dft_plan_free(&q->dft_plan[i]);
dft_plan_free(&q->idft_plan[i]);
}
}
bzero(q, sizeof(dft_precoding_t));
}
bool dft_precoding_valid_prb(uint32_t nof_prb) {
if (nof_prb == 1 || (nof_prb%2) == 0 || (nof_prb%3) == 0 || (nof_prb%5) == 0) {
return true;
} else {
return false;
}
}
int dft_precoding(dft_precoding_t *q, cf_t *input, cf_t *output,
uint32_t nof_prb, uint32_t nof_symbols)
{
if (!dft_precoding_valid_prb(nof_prb)) {
fprintf(stderr, "Error invalid number of PRB (%d)\n", nof_prb);
return LIBLTE_ERROR;
}
for (uint32_t i=0;i<nof_symbols;i++) {
dft_run_c(&q->dft_plan[nof_prb], &input[i*RE_X_RB*nof_prb], &output[i*RE_X_RB*nof_prb]);
}
return LIBLTE_SUCCESS;
}
int dft_predecoding(dft_precoding_t *q, cf_t *input, cf_t *output,
uint32_t nof_prb, uint32_t nof_symbols)
{
if (!dft_precoding_valid_prb(nof_prb)) {
fprintf(stderr, "Error invalid number of PRB (%d)\n", nof_prb);
return LIBLTE_ERROR;
}
for (uint32_t i=0;i<nof_symbols;i++) {
dft_run_c(&q->dft_plan[nof_prb], &input[i*RE_X_RB*nof_prb], &output[i*RE_X_RB*nof_prb]);
}
return LIBLTE_SUCCESS;
}

@ -0,0 +1,77 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/cqi.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
int cqi_hl_subband_pack(cqi_hl_subband_t *msg, uint32_t N, uint8_t *buff, uint32_t buff_len)
{
uint8_t *body_ptr = buff;
bit_pack(msg->wideband_cqi, &body_ptr, 4);
bit_pack(msg->subband_diff_cqi, &body_ptr, 2*N);
return 4+2*N;
}
int cqi_ue_subband_pack(cqi_ue_subband_t *msg, uint32_t L, uint8_t *buff, uint32_t buff_len)
{
uint8_t *body_ptr = buff;
bit_pack(msg->wideband_cqi, &body_ptr, 4);
bit_pack(msg->subband_diff_cqi, &body_ptr, 2);
bit_pack(msg->subband_diff_cqi, &body_ptr, L);
return 4+2+L;
}
int cqi_format2_wideband_pack(cqi_format2_wideband_t *msg, uint8_t *buff, uint32_t buff_len)
{
uint8_t *body_ptr = buff;
bit_pack(msg->wideband_cqi, &body_ptr, 4);
return 4;
}
int cqi_format2_subband_pack(cqi_format2_subband_t *msg, uint8_t *buff, uint32_t buff_len)
{
uint8_t *body_ptr = buff;
bit_pack(msg->subband_cqi, &body_ptr, 4);
bit_pack(msg->subband_label, &body_ptr, 1);
return 4+1;
}

@ -41,7 +41,9 @@
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti, /* Creates the DL PDSCH resource allocation grant from a DCI message
*/
int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti,
lte_cell_t cell, uint32_t cfi, lte_cell_t cell, uint32_t cfi,
ra_pdsch_t *ra_dl) ra_pdsch_t *ra_dl)
{ {
@ -56,7 +58,7 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
ret = LIBLTE_ERROR; ret = LIBLTE_ERROR;
dci_msg_type_t type; dci_msg_type_t type;
if (dci_msg_get_type(msg, &type, cell.nof_prb, msg_rnti, c_rnti)) { if (dci_msg_get_type(msg, &type, cell.nof_prb, msg_rnti)) {
fprintf(stderr, "Can't get DCI message type\n"); fprintf(stderr, "Can't get DCI message type\n");
return ret; return ret;
} }
@ -68,7 +70,11 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
if (type.type == PDSCH_SCHED) { if (type.type == PDSCH_SCHED) {
bzero(ra_dl, sizeof(ra_pdsch_t)); bzero(ra_dl, sizeof(ra_pdsch_t));
if (dci_msg_unpack_pdsch(msg, ra_dl, cell.nof_prb, msg_rnti != SIRNTI)) { bool crc_is_crnti = false;
if (msg_rnti >= CRNTI_START && msg_rnti <= CRNTI_END) {
crc_is_crnti = true;
}
if (dci_msg_unpack_pdsch(msg, ra_dl, cell.nof_prb, crc_is_crnti)) {
fprintf(stderr, "Can't unpack PDSCH message\n"); fprintf(stderr, "Can't unpack PDSCH message\n");
return ret; return ret;
} }
@ -77,12 +83,12 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb); ra_pdsch_fprint(stdout, ra_dl, cell.nof_prb);
} }
if (ra_prb_get_dl(&ra_dl->prb_alloc, ra_dl, cell.nof_prb)) { if (ra_dl_alloc(&ra_dl->prb_alloc, ra_dl, cell.nof_prb)) {
fprintf(stderr, "Error computing resource allocation\n"); fprintf(stderr, "Error computing resource allocation\n");
return ret; return ret;
} }
ra_prb_get_re_dl(&ra_dl->prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp); ra_dl_alloc_re(&ra_dl->prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
ret = LIBLTE_SUCCESS; ret = LIBLTE_SUCCESS;
} else { } else {
@ -93,6 +99,65 @@ int dci_msg_to_ra_dl(dci_msg_t *msg, uint16_t msg_rnti, uint16_t c_rnti,
return ret; return ret;
} }
/* Creates the UL PUSCH resource allocation grant from the random access respone message
*/
int dci_rar_to_ra_ul(uint32_t rba, uint32_t trunc_mcs, bool hopping_flag, uint32_t nof_prb, ra_pusch_t *ra) {
bzero(ra, sizeof(ra_pusch_t));
if (!hopping_flag) {
ra->freq_hop_fl = hop_disabled;
} else {
fprintf(stderr, "FIXME: Frequency hopping in RAR not implemented\n");
ra->freq_hop_fl = 1;
}
uint32_t riv = rba;
// Truncate resource block assignment
uint32_t b = 0;
if (nof_prb <= 44) {
b = (uint32_t) (ceilf(log2((float) nof_prb*(nof_prb+1)/2)));
riv = riv & ((1<<(b+1))-1);
}
ra->type2_alloc.riv = riv;
ra->mcs_idx = trunc_mcs;
ra_type2_from_riv(riv, &ra->type2_alloc.L_crb, &ra->type2_alloc.RB_start,
nof_prb, nof_prb);
ra_mcs_from_idx_ul(ra->mcs_idx, ra_nprb_ul(ra, nof_prb), &ra->mcs);
return LIBLTE_SUCCESS;
}
/* Creates the UL PUSCH resource allocation grant from a DCI format 0 message
*/
int dci_msg_to_ra_ul(dci_msg_t *msg, uint32_t nof_prb, uint32_t n_rb_ho, ra_pusch_t *ra_ul)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (msg != NULL &&
ra_ul != NULL)
{
ret = LIBLTE_ERROR;
bzero(ra_ul, sizeof(ra_pusch_t));
if (dci_msg_unpack_pusch(msg, ra_ul, nof_prb)) {
fprintf(stderr, "Can't unpack PDSCH message\n");
return ret;
}
if (VERBOSE_ISINFO()) {
ra_pusch_fprint(stdout, ra_ul, nof_prb);
}
if (ra_ul_alloc(&ra_ul->prb_alloc, ra_ul, n_rb_ho, nof_prb)) {
fprintf(stderr, "Error computing resource allocation\n");
return ret;
}
ret = LIBLTE_SUCCESS;
}
return ret;
}
int dci_location_set(dci_location_t *c, uint32_t L, uint32_t nCCE) { int dci_location_set(dci_location_t *c, uint32_t L, uint32_t nCCE) {
if (L <= 3) { if (L <= 3) {
c->L = L; c->L = L;
@ -303,7 +368,6 @@ int dci_format0_unpack(dci_msg_t *msg, ra_pusch_t *data, uint32_t nof_prb) {
uint32_t riv = bit_unpack(&y, riv_nbits(nof_prb) - n_ul_hop); uint32_t riv = bit_unpack(&y, riv_nbits(nof_prb) - n_ul_hop);
ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start,
nof_prb, nof_prb); nof_prb, nof_prb);
bit_pack((uint32_t) riv, &y, riv_nbits(nof_prb) - n_ul_hop);
data->type2_alloc.riv = riv; data->type2_alloc.riv = riv;
/* unpack MCS according to 8.6 of 36.213 */ /* unpack MCS according to 8.6 of 36.213 */
@ -788,9 +852,9 @@ void dci_msg_type_fprint(FILE *f, dci_msg_type_t type) {
} }
int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, uint32_t nof_prb, int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, uint32_t nof_prb,
uint16_t msg_rnti, uint16_t crnti) uint16_t msg_rnti)
{ {
DEBUG("Get message type: nof_bits=%d, msg_rnti=0x%x, crnti=0x%x\n", msg->nof_bits, msg_rnti, crnti); DEBUG("Get message type: nof_bits=%d, msg_rnti=0x%x\n", msg->nof_bits, msg_rnti);
if (msg->nof_bits == dci_format_sizeof(Format0, nof_prb) if (msg->nof_bits == dci_format_sizeof(Format0, nof_prb)
&& !msg->data[0]) { && !msg->data[0]) {
type->type = PUSCH_SCHED; type->type = PUSCH_SCHED;
@ -801,13 +865,15 @@ int dci_msg_get_type(dci_msg_t *msg, dci_msg_type_t *type, uint32_t nof_prb,
type->format = Format1; type->format = Format1;
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} else if (msg->nof_bits == dci_format_sizeof(Format1A, nof_prb)) { } else if (msg->nof_bits == dci_format_sizeof(Format1A, nof_prb)) {
if (msg_rnti == crnti) { /* The RNTI is not the only condition. Also some fields in the packet.
* if (msg_rnti >= CRNTI_START && msg_rnti <= CRNTI_END) {
type->type = RA_PROC_PDCCH; type->type = RA_PROC_PDCCH;
type->format = Format1A; type->format = Format1A;
} else { } else {
*/
type->type = PDSCH_SCHED; // only these 2 types supported type->type = PDSCH_SCHED; // only these 2 types supported
type->format = Format1A; type->format = Format1A;
} //}
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} else if (msg->nof_bits == dci_format_sizeof(Format1C, nof_prb)) { } else if (msg->nof_bits == dci_format_sizeof(Format1C, nof_prb)) {
if (msg_rnti == MRNTI) { if (msg_rnti == MRNTI) {

@ -0,0 +1,248 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/ra.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#define MAX_PDSCH_RE(cp) (2 * CP_NSYMB(cp) * 12)
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1;
int ret;
if (tbs == 0) {
bzero(s, sizeof(struct cb_segm));
ret = LIBLTE_SUCCESS;
} else {
B = tbs + 24;
/* Calculate CB sizes */
if (B <= MAX_LONG_CB) {
s->C = 1;
Bp = B;
} else {
s->C = (uint32_t) ceilf((float) B / (MAX_LONG_CB - 24));
Bp = B + 24 * s->C;
}
ret = lte_find_cb_index((Bp-1) / s->C + 1);
if (ret != LIBLTE_ERROR) {
idx1 = (uint32_t) ret;
ret = lte_cb_size(idx1);
if (ret != LIBLTE_ERROR) {
s->K1 = (uint32_t) ret;
if (idx1 > 0) {
ret = lte_cb_size(idx1 - 1);
}
if (ret != LIBLTE_ERROR) {
if (s->C == 1) {
s->K2 = 0;
s->C2 = 0;
s->C1 = 1;
} else {
s->K2 = (uint32_t) ret;
s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2);
s->C1 = s->C - s->C2;
}
s->F = s->C1 * s->K1 + s->C2 * s->K2 - Bp;
INFO("CB Segmentation: TBS: %d, C=%d, C+=%d K+=%d, C-=%d, K-=%d, F=%d, Bp=%d\n",
tbs, s->C, s->C1, s->K1, s->C2, s->K2, s->F, Bp);
}
}
}
}
return ret;
}
int harq_init(harq_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL) {
uint32_t i;
bzero(q, sizeof(harq_t));
memcpy(&q->cell, &cell, sizeof(lte_cell_t));
ret = ra_tbs_from_idx(26, cell.nof_prb);
if (ret != LIBLTE_ERROR) {
q->max_cb = (uint32_t) ret / (MAX_LONG_CB - 24) + 1;
q->pdsch_w_buff_f = vec_malloc(sizeof(float*) * q->max_cb);
if (!q->pdsch_w_buff_f) {
perror("malloc");
return LIBLTE_ERROR;
}
q->pdsch_w_buff_c = vec_malloc(sizeof(uint8_t*) * q->max_cb);
if (!q->pdsch_w_buff_c) {
perror("malloc");
return LIBLTE_ERROR;
}
// FIXME: Use HARQ buffer limitation based on UE category
q->w_buff_size = cell.nof_prb * MAX_PDSCH_RE(cell.cp) * 6 * 10;
for (i=0;i<q->max_cb;i++) {
q->pdsch_w_buff_f[i] = vec_malloc(sizeof(float) * q->w_buff_size);
if (!q->pdsch_w_buff_f[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
q->pdsch_w_buff_c[i] = vec_malloc(sizeof(uint8_t) * q->w_buff_size);
if (!q->pdsch_w_buff_c[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
bzero(q->pdsch_w_buff_c[i], sizeof(uint8_t) * q->w_buff_size);
}
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
void harq_free(harq_t *q) {
if (q) {
uint32_t i;
if (q->pdsch_w_buff_f) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_f[i]) {
free(q->pdsch_w_buff_f[i]);
}
}
free(q->pdsch_w_buff_f);
}
if (q->pdsch_w_buff_c) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_c[i]) {
free(q->pdsch_w_buff_c[i]);
}
}
free(q->pdsch_w_buff_c);
}
bzero(q, sizeof(harq_t));
}
}
void harq_reset(harq_t *q) {
int i;
if (q->pdsch_w_buff_f) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_f[i]) {
bzero(q->pdsch_w_buff_f[i], sizeof(float) * q->w_buff_size);
}
}
}
if (q->pdsch_w_buff_c) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_c[i]) {
bzero(q->pdsch_w_buff_c[i], sizeof(uint8_t) * q->w_buff_size);
}
}
}
bzero(&q->mcs, sizeof(ra_mcs_t));
bzero(&q->cb_segm, sizeof(struct cb_segm));
bzero(&q->dl_alloc, sizeof(ra_dl_alloc_t));
}
static int harq_setup_common(harq_t *q, ra_mcs_t mcs, uint32_t rv, uint32_t sf_idx) {
if (mcs.tbs != q->mcs.tbs) {
codeblock_segmentation(&q->cb_segm, mcs.tbs);
if (q->cb_segm.C > q->max_cb) {
fprintf(stderr, "Codeblock segmentation returned more CBs (%d) than allocated (%d)\n",
q->cb_segm.C, q->max_cb);
return LIBLTE_ERROR;
}
}
q->mcs = mcs;
q->sf_idx = sf_idx;
q->rv = rv;
return LIBLTE_SUCCESS;
}
int harq_setup_dl(harq_t *q, ra_mcs_t mcs, uint32_t rv, uint32_t sf_idx, ra_dl_alloc_t *dl_alloc) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
rv < 4 &&
sf_idx < 10)
{
ret = harq_setup_common(q, mcs, rv, sf_idx);
if (ret) {
return ret;
}
memcpy(&q->dl_alloc, dl_alloc, sizeof(ra_dl_alloc_t));
// Number of symbols, RE and bits per subframe for DL
q->nof_re = q->dl_alloc.re_sf[q->sf_idx];
q->nof_symb = 2*CP_NSYMB(q->cell.cp)-q->dl_alloc.lstart;
q->nof_bits = q->nof_re * lte_mod_bits_x_symbol(q->mcs.mod);
q->nof_prb = q->dl_alloc.slot[0].nof_prb;
ret = LIBLTE_SUCCESS;
}
return ret;
}
int harq_setup_ul(harq_t *q, ra_mcs_t mcs, uint32_t rv, uint32_t sf_idx, ra_ul_alloc_t *ul_alloc) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
rv < 4 &&
sf_idx < 10)
{
ret = harq_setup_common(q, mcs, rv, sf_idx);
if (ret) {
return ret;
}
memcpy(&q->ul_alloc, ul_alloc, sizeof(ra_ul_alloc_t));
// Number of symbols, RE and bits per subframe for UL
q->nof_symb = 2*(CP_NSYMB(q->cell.cp)-1);
q->nof_re = q->nof_symb*q->ul_alloc.L_prb*RE_X_RB;
q->nof_bits = q->nof_re * lte_mod_bits_x_symbol(q->mcs.mod);
q->nof_prb = q->ul_alloc.L_prb;
ret = LIBLTE_SUCCESS;
}
return ret;
}

@ -34,7 +34,7 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include "prb.h" #include "prb_dl.h"
#include "liblte/phy/phch/pbch.h" #include "liblte/phy/phch/pbch.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h" #include "liblte/phy/utils/bit.h"
@ -42,9 +42,10 @@
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
const uint8_t crc_mask[4][16] = { const uint8_t crc_mask[4][16] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 1, 1, 1, 1, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
0, 0 }, { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 } }; { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 } };
bool pbch_exists(int nframe, int nslot) { bool pbch_exists(int nframe, int nslot) {
return (!(nframe % 5) && nslot == 1); return (!(nframe % 5) && nslot == 1);
@ -166,34 +167,34 @@ int pbch_init(pbch_t *q, lte_cell_t cell) {
q->encoder.tail_biting = true; q->encoder.tail_biting = true;
memcpy(q->encoder.poly, poly, 3 * sizeof(int)); memcpy(q->encoder.poly, poly, 3 * sizeof(int));
q->pbch_d = malloc(sizeof(cf_t) * q->nof_symbols); q->pbch_d = vec_malloc(sizeof(cf_t) * q->nof_symbols);
if (!q->pbch_d) { if (!q->pbch_d) {
goto clean; goto clean;
} }
int i; int i;
for (i = 0; i < q->cell.nof_ports; i++) { for (i = 0; i < q->cell.nof_ports; i++) {
q->ce[i] = malloc(sizeof(cf_t) * q->nof_symbols); q->ce[i] = vec_malloc(sizeof(cf_t) * q->nof_symbols);
if (!q->ce[i]) { if (!q->ce[i]) {
goto clean; goto clean;
} }
q->pbch_x[i] = malloc(sizeof(cf_t) * q->nof_symbols); q->pbch_x[i] = vec_malloc(sizeof(cf_t) * q->nof_symbols);
if (!q->pbch_x[i]) { if (!q->pbch_x[i]) {
goto clean; goto clean;
} }
q->pbch_symbols[i] = malloc(sizeof(cf_t) * q->nof_symbols); q->pbch_symbols[i] = vec_malloc(sizeof(cf_t) * q->nof_symbols);
if (!q->pbch_symbols[i]) { if (!q->pbch_symbols[i]) {
goto clean; goto clean;
} }
} }
q->pbch_llr = malloc(sizeof(float) * q->nof_symbols * 4 * 2); q->pbch_llr = vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2);
if (!q->pbch_llr) { if (!q->pbch_llr) {
goto clean; goto clean;
} }
q->temp = malloc(sizeof(float) * q->nof_symbols * 4 * 2); q->temp = vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2);
if (!q->temp) { if (!q->temp) {
goto clean; goto clean;
} }
q->pbch_rm_b = malloc(sizeof(float) * q->nof_symbols * 4 * 2); q->pbch_rm_b = vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2);
if (!q->pbch_rm_b) { if (!q->pbch_rm_b) {
goto clean; goto clean;
} }
@ -241,6 +242,116 @@ void pbch_free(pbch_t *q) {
} }
/** Unpacks MIB from PBCH message.
* msg buffer must be 24 byte length at least
*/
void pbch_mib_unpack(uint8_t *msg, lte_cell_t *cell, uint32_t *sfn) {
int phich_res;
cell->bw_idx = bit_unpack(&msg, 3);
switch (cell->bw_idx) {
case 0:
cell->nof_prb = 6;
break;
case 1:
cell->nof_prb = 15;
break;
default:
cell->nof_prb = (cell->bw_idx - 1) * 25;
break;
}
if (*msg) {
cell->phich_length = PHICH_EXT;
} else {
cell->phich_length = PHICH_NORM;
}
msg++;
phich_res = bit_unpack(&msg, 2);
switch (phich_res) {
case 0:
cell->phich_resources = R_1_6;
break;
case 1:
cell->phich_resources = R_1_2;
break;
case 2:
cell->phich_resources = R_1;
break;
case 3:
cell->phich_resources = R_2;
break;
}
if (sfn) {
*sfn = bit_unpack(&msg, 8) << 2;
}
}
/** Unpacks MIB from PBCH message.
* msg buffer must be 24 byte length at least
*/
void pbch_mib_pack(lte_cell_t *cell, uint32_t sfn, uint8_t *msg) {
int bw, phich_res = 0;
bzero(msg, 24);
if (cell->nof_prb <= 6) {
bw = 0;
} else if (cell->nof_prb <= 15) {
bw = 1;
} else {
bw = 1 + cell->nof_prb / 25;
}
bit_pack(bw, &msg, 3);
*msg = cell->phich_length == PHICH_EXT;
msg++;
switch (cell->phich_resources) {
case R_1_6:
phich_res = 0;
break;
case R_1_2:
phich_res = 1;
break;
case R_1:
phich_res = 2;
break;
case R_2:
phich_res = 3;
break;
}
bit_pack(phich_res, &msg, 2);
bit_pack(sfn >> 2, &msg, 8);
}
void pbch_mib_fprint(FILE *stream, lte_cell_t *cell, uint32_t sfn, uint32_t cell_id) {
printf(" - Cell ID: %d\n", cell_id);
printf(" - Nof ports: %d\n", cell->nof_ports);
printf(" - PRB: %d\n", cell->nof_prb);
printf(" - PHICH Length: %s\n",
cell->phich_length == PHICH_EXT ? "Extended" : "Normal");
printf(" - PHICH Resources: ");
switch (cell->phich_resources) {
case R_1_6:
printf("1/6");
break;
case R_1_2:
printf("1/2");
break;
case R_1:
printf("1");
break;
case R_2:
printf("2");
break;
}
printf("\n");
printf(" - SFN: %d\n", sfn);
}
void pbch_decode_reset(pbch_t *q) { void pbch_decode_reset(pbch_t *q) {
q->frame_idx = 0; q->frame_idx = 0;
} }

@ -130,7 +130,7 @@ float pcfich_cfi_decode(pcfich_t *q, uint32_t *cfi) {
float max_corr = 0; float max_corr = 0;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
float corr = vec_dot_prod_fff(q->cfi_table_float[i], q->data_f, PCFICH_CFI_LEN); float corr = fabsf(vec_dot_prod_fff(q->cfi_table_float[i], q->data_f, PCFICH_CFI_LEN));
if (corr > max_corr) { if (corr > max_corr) {
max_corr = corr; max_corr = corr;
index = i; index = i;

@ -34,7 +34,7 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include "prb.h" #include "prb_dl.h"
#include "liblte/phy/phch/pdsch.h" #include "liblte/phy/phch/pdsch.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h" #include "liblte/phy/utils/bit.h"
@ -46,7 +46,7 @@
const lte_mod_t modulations[4] = const static lte_mod_t modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 }; { LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
//#define DEBUG_IDX //#define DEBUG_IDX
@ -58,7 +58,7 @@ extern int indices_ptr;
#endif #endif
int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc, int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_dl_alloc_t *prb_alloc,
uint32_t nsubframe, bool put) { uint32_t nsubframe, bool put) {
uint32_t s, n, l, lp, lstart, lend, nof_refs; uint32_t s, n, l, lp, lstart, lend, nof_refs;
bool is_pbch, is_sss; bool is_pbch, is_sss;
@ -179,7 +179,7 @@ int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc,
* 36.211 10.3 section 6.3.5 * 36.211 10.3 section 6.3.5
*/ */
int pdsch_put(pdsch_t *q, cf_t *pdsch_symbols, cf_t *sf_symbols, int pdsch_put(pdsch_t *q, cf_t *pdsch_symbols, cf_t *sf_symbols,
ra_prb_t *prb_alloc, uint32_t subframe) { ra_dl_alloc_t *prb_alloc, uint32_t subframe) {
return pdsch_cp(q, pdsch_symbols, sf_symbols, prb_alloc, subframe, true); return pdsch_cp(q, pdsch_symbols, sf_symbols, prb_alloc, subframe, true);
} }
@ -191,7 +191,7 @@ int pdsch_put(pdsch_t *q, cf_t *pdsch_symbols, cf_t *sf_symbols,
* 36.211 10.3 section 6.3.5 * 36.211 10.3 section 6.3.5
*/ */
int pdsch_get(pdsch_t *q, cf_t *sf_symbols, cf_t *pdsch_symbols, int pdsch_get(pdsch_t *q, cf_t *sf_symbols, cf_t *pdsch_symbols,
ra_prb_t *prb_alloc, uint32_t subframe) { ra_dl_alloc_t *prb_alloc, uint32_t subframe) {
return pdsch_cp(q, sf_symbols, pdsch_symbols, prb_alloc, subframe, false); return pdsch_cp(q, sf_symbols, pdsch_symbols, prb_alloc, subframe, false);
} }
@ -208,10 +208,10 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
ret = LIBLTE_ERROR; ret = LIBLTE_ERROR;
q->cell = cell; q->cell = cell;
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp); q->max_re = 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,
q->cell.nof_prb, q->max_symbols); q->cell.nof_prb, q->max_re);
if (precoding_init(&q->precoding, SF_LEN_RE(cell.nof_prb, cell.cp))) { if (precoding_init(&q->precoding, SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n"); fprintf(stderr, "Error initializing precoding\n");
@ -223,61 +223,35 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
goto clean; goto clean;
} }
} }
if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
demod_soft_init(&q->demod, q->max_symbols); demod_soft_init(&q->demod, q->max_re);
demod_soft_alg_set(&q->demod, APPROX); demod_soft_alg_set(&q->demod, APPROX);
q->rnti_is_set = false; sch_init(&q->dl_sch);
if (tcod_init(&q->encoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Coder\n");
goto clean;
}
if (tdec_init(&q->decoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Decoder\n");
goto clean;
}
// Allocate floats for reception (LLRs)
q->cb_in = malloc(sizeof(uint8_t) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->cb_out = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12)); q->rnti_is_set = false;
if (!q->cb_out) {
goto clean;
}
// Allocate floats for reception (LLRs) // Allocate floats for reception (LLRs)
q->pdsch_e = malloc(sizeof(float) * q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64)); q->pdsch_e = vec_malloc(sizeof(float) * q->max_re * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pdsch_e) { if (!q->pdsch_e) {
goto clean; goto clean;
} }
q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols); q->pdsch_d = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->pdsch_d) { if (!q->pdsch_d) {
goto clean; goto clean;
} }
for (i = 0; i < q->cell.nof_ports; i++) { for (i = 0; i < q->cell.nof_ports; i++) {
q->ce[i] = malloc(sizeof(cf_t) * q->max_symbols); q->ce[i] = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->ce[i]) { if (!q->ce[i]) {
goto clean; goto clean;
} }
q->pdsch_x[i] = malloc(sizeof(cf_t) * q->max_symbols); q->pdsch_x[i] = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->pdsch_x[i]) { if (!q->pdsch_x[i]) {
goto clean; goto clean;
} }
q->pdsch_symbols[i] = malloc(sizeof(cf_t) * q->max_symbols); q->pdsch_symbols[i] = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->pdsch_symbols[i]) { if (!q->pdsch_symbols[i]) {
goto clean; goto clean;
} }
@ -295,12 +269,6 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
void pdsch_free(pdsch_t *q) { void pdsch_free(pdsch_t *q) {
int i; int i;
if (q->cb_in) {
free(q->cb_in);
}
if (q->cb_out) {
free(q->cb_out);
}
if (q->pdsch_e) { if (q->pdsch_e) {
free(q->pdsch_e); free(q->pdsch_e);
} }
@ -327,19 +295,22 @@ void pdsch_free(pdsch_t *q) {
modem_table_free(&q->mod[i]); modem_table_free(&q->mod[i]);
} }
demod_soft_free(&q->demod); demod_soft_free(&q->demod);
tdec_free(&q->decoder);
tcod_free(&q->encoder);
precoding_free(&q->precoding); precoding_free(&q->precoding);
sch_free(&q->dl_sch);
bzero(q, sizeof(pdsch_t)); bzero(q, sizeof(pdsch_t));
} }
/* Precalculate the PUSCH scramble sequences for a given RNTI. This function takes a while
* to execute, so shall be called once the final C-RNTI has been allocated for the session.
* For the connection procedure, use pusch_encode_rnti() or pusch_decode_rnti() functions
*/
int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) { int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) {
uint32_t i; uint32_t i;
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) { for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
if (sequence_pdsch(&q->seq_pdsch[i], rnti, 0, 2 * i, q->cell.id, if (sequence_pdsch(&q->seq_pdsch[i], rnti, 0, 2 * i, q->cell.id,
q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64))) { q->max_re * lte_mod_bits_x_symbol(LTE_QAM64))) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
} }
@ -347,324 +318,18 @@ int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) {
q->rnti = rnti; q->rnti = rnti;
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} }
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
static int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1;
int ret;
B = tbs + 24;
/* Calculate CB sizes */
if (B < MAX_LONG_CB) {
s->C = 1;
Bp = B;
} else {
s->C = (uint32_t) ceilf((float) B / (MAX_LONG_CB - 24));
Bp = B + 24 * s->C;
}
ret = lte_find_cb_index(Bp / s->C);
if (ret != LIBLTE_ERROR) {
idx1 = (uint32_t) ret;
ret = lte_cb_size(idx1);
if (ret != LIBLTE_ERROR) {
s->K1 = (uint32_t) ret;
ret = lte_cb_size(idx1 - 1);
if (ret != LIBLTE_ERROR) {
if (s->C == 1) {
s->K2 = 0;
s->C2 = 0;
s->C1 = 1;
} else {
s->K2 = (uint32_t) ret;
s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2);
s->C1 = s->C - s->C2;
}
s->F = s->C1 * s->K1 + s->C2 * s->K2 - Bp;
INFO("CB Segmentation: TBS: %d, C=%d, C+=%d K+=%d, C-=%d, K-=%d, F=%d, Bp=%d\n",
tbs, s->C, s->C1, s->K1, s->C2, s->K2, s->F, Bp);
}
}
}
return ret;
}
int pdsch_harq_init(pdsch_harq_t *p, pdsch_t *pdsch) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (p != NULL) {
uint32_t i;
bzero(p, sizeof(pdsch_harq_t));
p->cell = pdsch->cell;
ret = ra_tbs_from_idx(26, p->cell.nof_prb);
if (ret != LIBLTE_ERROR) {
p->max_cb = (uint32_t) ret / (MAX_LONG_CB - 24) + 1;
p->pdsch_w_buff_f = malloc(sizeof(float*) * p->max_cb);
if (!p->pdsch_w_buff_f) {
perror("malloc");
return LIBLTE_ERROR;
}
p->pdsch_w_buff_c = malloc(sizeof(uint8_t*) * p->max_cb);
if (!p->pdsch_w_buff_c) {
perror("malloc");
return LIBLTE_ERROR;
}
// FIXME: Use HARQ buffer limitation based on UE category
p->w_buff_size = p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp) * 6 * 10;
for (i=0;i<p->max_cb;i++) {
p->pdsch_w_buff_f[i] = vec_malloc(sizeof(float) * p->w_buff_size);
if (!p->pdsch_w_buff_f[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
p->pdsch_w_buff_c[i] = vec_malloc(sizeof(uint8_t) * p->w_buff_size);
if (!p->pdsch_w_buff_c[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
}
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
void pdsch_harq_free(pdsch_harq_t *p) {
if (p) {
uint32_t i;
if (p->pdsch_w_buff_f) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_f[i]) {
free(p->pdsch_w_buff_f[i]);
}
}
free(p->pdsch_w_buff_f);
}
if (p->pdsch_w_buff_c) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_c[i]) {
free(p->pdsch_w_buff_c[i]);
}
}
free(p->pdsch_w_buff_c);
}
bzero(p, sizeof(pdsch_harq_t));
}
}
void pdsch_harq_reset(pdsch_harq_t *p) {
int i;
if (p->pdsch_w_buff_f) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_f[i]) {
bzero(p->pdsch_w_buff_f[i], sizeof(float) * p->w_buff_size);
}
}
}
if (p->pdsch_w_buff_c) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_c[i]) {
bzero(p->pdsch_w_buff_c[i], sizeof(uint8_t) * p->w_buff_size);
}
}
}
bzero(&p->mcs, sizeof(ra_mcs_t));
bzero(&p->cb_segm, sizeof(struct cb_segm));
bzero(&p->prb_alloc, sizeof(ra_prb_t));
}
int pdsch_harq_setup(pdsch_harq_t *p, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (p != NULL &&
mcs.tbs > 0)
{
uint32_t nof_bits, nof_bits_e, nof_symbols;
p->mcs = mcs;
memcpy(&p->prb_alloc, prb_alloc, sizeof(ra_prb_t));
codeblock_segmentation(&p->cb_segm, mcs.tbs);
nof_bits = mcs.tbs;
nof_symbols = prb_alloc->re_sf[1]; // Any subframe except 0 and 5 has maximum RE
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(mcs.mod);
if (nof_bits > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e);
return LIBLTE_ERROR;
}
if (nof_symbols > p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp)) {
fprintf(stderr,
"Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n",
nof_symbols, p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp), p->cell.nof_prb);
return LIBLTE_ERROR;
}
if (p->cb_segm.C > p->max_cb) {
fprintf(stderr, "Codeblock segmentation returned more CBs (%d) than allocated (%d)\n",
p->cb_segm.C, p->max_cb);
return LIBLTE_ERROR;
}
ret = LIBLTE_SUCCESS;
}
return ret;
}
float pdsch_average_noi(pdsch_t *q) {
return q->average_nof_iterations;
}
uint32_t pdsch_last_noi(pdsch_t *q) { int pdsch_decode(pdsch_t *q, harq_t *harq, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_estimate, uint8_t *data) {
return q->nof_iterations; if (q != NULL &&
} sf_symbols != NULL &&
data != NULL &&
harq != NULL)
/* Decode a transport block according to 36.212 5.3.2
*
*/
int pdsch_decode_tb(pdsch_t *q, uint8_t *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par_rx, par_tx;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
float *e_bits = q->pdsch_e;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL &&
data != NULL &&
harq_process != NULL &&
nb_e < q->max_symbols * Qm)
{ {
if (q->rnti_is_set) {
rp = 0; return pdsch_decode_rnti(q, harq, sf_symbols, ce, noise_estimate, q->rnti, data);
rp = 0;
wp = 0;
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
bool early_stop = true;
for (i = 0; i < harq_process->cb_segm.C && early_stop; i++) {
/* Get read/write lengths */
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = harq_process->cb_segm.K2;
}
if (harq_process->cb_segm.C == 1) {
rlen = cb_len;
} else {
rlen = cb_len - 24;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i,
cb_len, rlen - F, wp, rp, F, n_e);
/* Rate Unmatching */
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_out, 3 * cb_len + 12, rv_idx)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Turbo Decoding with CRC-based early stopping */
q->nof_iterations = 0;
uint32_t len_crc;
uint8_t *cb_in_ptr;
crc_t *crc_ptr;
early_stop = false;
tdec_reset(&q->decoder, cb_len);
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(uint8_t));
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 = VEC_EMA((float) q->nof_iterations, q->average_nof_iterations, 0.2);
// If CB CRC is not correct, early_stop will be false and wont continue with rest of CBs
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(uint8_t));
} else {
DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
rlen - F - 24, wp, F, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(uint8_t));
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(uint8_t));
}
/* Set read/write pointers */
wp += (rlen - F);
rp += n_e;
}
if (!early_stop) {
INFO("CB %d failed. TB is erroneous.\n",i-1);
return LIBLTE_ERROR;
} else { } else {
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); fprintf(stderr, "Must call pdsch_set_rnti() before calling pdsch_decode()\n");
return LIBLTE_ERROR;
// Compute transport block CRC
par_rx = crc_checksum(&q->crc_tb, data, tbs);
// check parity bits
par_tx = bit_unpack(&p_parity, 24);
if (!par_rx) {
INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0);
}
if (par_rx == par_tx) {
INFO("TB decoded OK\n",i);
return LIBLTE_SUCCESS;
} else {
INFO("Error in TB parity\n",i);
return LIBLTE_ERROR;
}
} }
} else { } else {
return LIBLTE_ERROR_INVALID_INPUTS; return LIBLTE_ERROR_INVALID_INPUTS;
@ -673,288 +338,178 @@ int pdsch_decode_tb(pdsch_t *q, uint8_t *data, uint32_t tbs, uint32_t nb_e,
/** Decodes the PDSCH from the received symbols /** Decodes the PDSCH from the received symbols
*/ */
int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_estimate, uint8_t *data, uint32_t subframe, int pdsch_decode_rnti(pdsch_t *q, harq_t *harq, cf_t *sf_symbols, cf_t *ce[MAX_PORTS],
pdsch_harq_t *harq_process, uint32_t rv_idx) float noise_estimate, uint16_t rnti, uint8_t *data)
{ {
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
uint32_t i, n; uint32_t i, n;
cf_t *x[MAX_LAYERS]; cf_t *x[MAX_LAYERS];
uint32_t nof_symbols, nof_bits, nof_bits_e;
if (q != NULL && if (q != NULL &&
sf_symbols != NULL && sf_symbols != NULL &&
data != NULL && data != NULL &&
subframe < 10 && harq != NULL)
harq_process != NULL)
{ {
if (q->rnti_is_set) { INFO("Decoding PDSCH SF: %d, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
nof_bits = harq_process->mcs.tbs; harq->sf_idx, lte_mod_string(harq->mcs.mod), harq->mcs.tbs, harq->nof_re, harq->nof_bits, harq->rv);
nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(harq_process->mcs.mod);
INFO("Decoding PDSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", /* number of layers equals number of ports */
subframe, lte_mod_string(harq_process->mcs.mod), nof_bits, nof_symbols, nof_bits_e, rv_idx); for (i = 0; i < q->cell.nof_ports; i++) {
x[i] = q->pdsch_x[i];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
/* number of layers equals number of ports */ /* extract symbols */
for (i = 0; i < q->cell.nof_ports; i++) { n = pdsch_get(q, sf_symbols, q->pdsch_symbols[0], &harq->dl_alloc, harq->sf_idx);
x[i] = q->pdsch_x[i]; if (n != harq->nof_re) {
} fprintf(stderr, "Error expecting %d symbols but got %d\n", harq->nof_re, n);
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports)); return LIBLTE_ERROR;
}
/* extract symbols */ /* extract channel estimates */
n = pdsch_get(q, sf_symbols, q->pdsch_symbols[0], &harq_process->prb_alloc, subframe); for (i = 0; i < q->cell.nof_ports; i++) {
if (n != nof_symbols) { n = pdsch_get(q, ce[i], q->ce[i], &harq->dl_alloc, harq->sf_idx);
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); if (n != harq->nof_re) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", harq->nof_re, n);
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
}
/* extract channel estimates */ /* TODO: only diversity is supported */
for (i = 0; i < q->cell.nof_ports; i++) { if (q->cell.nof_ports == 1) {
n = pdsch_get(q, ce[i], q->ce[i], &harq_process->prb_alloc, subframe); /* no need for layer demapping */
if (n != nof_symbols) { predecoding_single(&q->precoding, q->pdsch_symbols[0], q->ce[0], q->pdsch_d,
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); harq->nof_re, noise_estimate);
return LIBLTE_ERROR; } else {
} predecoding_diversity(&q->precoding, q->pdsch_symbols[0], q->ce, x, q->cell.nof_ports,
} harq->nof_re, noise_estimate);
layerdemap_diversity(x, q->pdsch_d, q->cell.nof_ports,
harq->nof_re / q->cell.nof_ports);
}
/* TODO: only diversity is supported */ /* demodulate symbols
if (q->cell.nof_ports == 1) { * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation,
/* no need for layer demapping */ * thus we don't need tot set it in the LLRs normalization
predecoding_single(&q->precoding, q->pdsch_symbols[0], q->ce[0], q->pdsch_d, */
nof_symbols, noise_estimate); demod_soft_sigma_set(&q->demod, sqrt(0.5));
} else { demod_soft_table_set(&q->demod, &q->mod[harq->mcs.mod]);
predecoding_diversity(&q->precoding, q->pdsch_symbols[0], q->ce, x, q->cell.nof_ports, demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, harq->nof_re);
nof_symbols, noise_estimate);
layerdemap_diversity(x, q->pdsch_d, q->cell.nof_ports, /* descramble */
nof_symbols / q->cell.nof_ports); if (rnti != q->rnti) {
sequence_t seq;
if (sequence_pdsch(&seq, rnti, 0, 2 * harq->sf_idx, q->cell.id, harq->nof_bits)) {
return LIBLTE_ERROR;
} }
scrambling_f_offset(&seq, q->pdsch_e, 0, harq->nof_bits);
/* demodulate symbols sequence_free(&seq);
* The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation,
* thus we don't need tot set it in the LLRs normalization
*/
demod_soft_sigma_set(&q->demod, sqrt(0.5));
demod_soft_table_set(&q->demod, &q->mod[harq_process->mcs.mod]);
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols);
/* descramble */
scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 0, nof_bits_e);
return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx);
} else { } else {
fprintf(stderr, "Must call pdsch_set_rnti() before calling pdsch_decode()\n"); scrambling_f_offset(&q->seq_pdsch[harq->sf_idx], q->pdsch_e, 0, harq->nof_bits);
return LIBLTE_ERROR;
} }
return dlsch_decode(&q->dl_sch, harq, q->pdsch_e, data);
} else { } else {
return LIBLTE_ERROR_INVALID_INPUTS; return LIBLTE_ERROR_INVALID_INPUTS;
} }
} }
/* Encode a transport block according to 36.212 5.3.2 int pdsch_encode(pdsch_t *q, harq_t *harq, uint8_t *data, cf_t *sf_symbols[MAX_PORTS])
*
*/
int pdsch_encode_tb(pdsch_t *q, uint8_t *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{ {
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
uint8_t *e_bits = q->pdsch_e;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
harq_process != NULL && harq != NULL)
nb_e < q->max_symbols * Qm)
{ {
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
if (q->rnti_is_set) { if (q->rnti_is_set) {
if (rv_idx == 0) { return pdsch_encode_rnti(q, harq, data, q->rnti, sf_symbols);
/* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs);
/* parity bits will be appended later */
bit_pack(par, &p_parity, 24);
if (VERBOSE_ISDEBUG()) {
DEBUG("DATA: ", 0);
vec_fprint_b(stdout, data, tbs);
DEBUG("PARITY: ", 0);
vec_fprint_b(stdout, parity, 24);
}
/* Add filler bits to the new data buffer */
for (i = 0; i < harq_process->cb_segm.F; i++) {
q->cb_in[i] = LTE_NULL_BIT;
}
}
wp = 0;
rp = 0;
for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read lengths */
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = harq_process->cb_segm.K2;
}
if (harq_process->cb_segm.C > 1) {
rlen = cb_len - 24;
} else {
rlen = cb_len;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
INFO("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);
if (rv_idx == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(uint8_t));
} else {
INFO("Last CB, appending parity: %d from %d and 24 to %d\n",
rlen - F - 24, rp, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&q->cb_in[F], &data[rp], (rlen - F - 24) * sizeof(uint8_t));
memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(uint8_t));
}
if (harq_process->cb_segm.C > 1) {
/* Attach Codeblock CRC */
crc_attach(&q->crc_cb, q->cb_in, rlen);
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d Len=%d: ", i, cb_len);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, q->cb_in, (uint8_t*) q->cb_out, cb_len);
}
/* Rate matching */
if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
(uint8_t*) q->cb_out, 3 * cb_len + 12,
&e_bits[wp], n_e, rv_idx))
{
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Set read/write pointers */
rp += (rlen - F);
wp += n_e;
}
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
ret = LIBLTE_SUCCESS;
} else { } else {
fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n"); fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n");
return LIBLTE_ERROR;
} }
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
} }
return ret;
} }
/** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission /** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission
*/ */
int pdsch_encode(pdsch_t *q, uint8_t *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe, int pdsch_encode_rnti(pdsch_t *q, harq_t *harq, uint8_t *data, uint16_t rnti, cf_t *sf_symbols[MAX_PORTS])
pdsch_harq_t *harq_process, uint32_t rv_idx)
{ {
int i; int i;
uint32_t nof_symbols, nof_bits, nof_bits_e;
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
cf_t *x[MAX_LAYERS]; cf_t *x[MAX_LAYERS];
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
subframe < 10 && harq != NULL)
harq_process != NULL)
{ {
if (q->rnti_is_set) { for (i=0;i<q->cell.nof_ports;i++) {
for (i=0;i<q->cell.nof_ports;i++) { if (sf_symbols[i] == NULL) {
if (sf_symbols[i] == NULL) { return LIBLTE_ERROR_INVALID_INPUTS;
return LIBLTE_ERROR_INVALID_INPUTS;
}
} }
}
nof_bits = harq_process->mcs.tbs; if (harq->mcs.tbs == 0) {
nof_symbols = harq_process->prb_alloc.re_sf[subframe]; return LIBLTE_ERROR_INVALID_INPUTS;
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(harq_process->mcs.mod); }
if (harq_process->mcs.tbs == 0) { if (harq->mcs.tbs > harq->nof_bits) {
return LIBLTE_ERROR_INVALID_INPUTS; fprintf(stderr, "Invalid code rate %.2f\n", (float) harq->mcs.tbs / harq->nof_bits);
} return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_bits > nof_bits_e) { if (harq->nof_re > q->max_re) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); fprintf(stderr,
return LIBLTE_ERROR_INVALID_INPUTS; "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n",
} harq->nof_re, q->max_re, q->cell.nof_prb);
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_symbols > q->max_symbols) { INFO("Encoding PDSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
fprintf(stderr, harq->sf_idx, lte_mod_string(harq->mcs.mod), harq->mcs.tbs, harq->nof_re, harq->nof_bits, harq->rv);
"Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n",
nof_symbols, q->max_symbols, q->cell.nof_prb);
return LIBLTE_ERROR_INVALID_INPUTS;
}
INFO("Encoding PDSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", /* number of layers equals number of ports */
subframe, lte_mod_string(harq_process->mcs.mod), nof_bits, nof_symbols, nof_bits_e, rv_idx); for (i = 0; i < q->cell.nof_ports; i++) {
x[i] = q->pdsch_x[i];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
/* number of layers equals number of ports */ if (dlsch_encode(&q->dl_sch, harq, data, q->pdsch_e)) {
for (i = 0; i < q->cell.nof_ports; i++) { fprintf(stderr, "Error encoding TB\n");
x[i] = q->pdsch_x[i]; return LIBLTE_ERROR;
} }
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
if (pdsch_encode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx)) { if (rnti != q->rnti) {
fprintf(stderr, "Error encoding TB\n"); sequence_t seq;
if (sequence_pdsch(&seq, rnti, 0, 2 * harq->sf_idx, q->cell.id, harq->nof_bits)) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
scrambling_b_offset(&seq, (uint8_t*) q->pdsch_e, 0, harq->nof_bits);
sequence_free(&seq);
} else {
scrambling_b_offset(&q->seq_pdsch[harq->sf_idx], (uint8_t*) q->pdsch_e, 0, harq->nof_bits);
}
scrambling_b_offset(&q->seq_pdsch[subframe], (uint8_t*) q->pdsch_e, 0, nof_bits_e); mod_modulate(&q->mod[harq->mcs.mod], (uint8_t*) q->pdsch_e, q->pdsch_d, harq->nof_bits);
mod_modulate(&q->mod[harq_process->mcs.mod], (uint8_t*) q->pdsch_e, q->pdsch_d, nof_bits_e);
/* TODO: only diversity supported */ /* TODO: only diversity supported */
if (q->cell.nof_ports > 1) { if (q->cell.nof_ports > 1) {
layermap_diversity(q->pdsch_d, x, q->cell.nof_ports, nof_symbols); layermap_diversity(q->pdsch_d, x, q->cell.nof_ports, harq->nof_re);
precoding_diversity(&q->precoding, x, q->pdsch_symbols, q->cell.nof_ports, precoding_diversity(&q->precoding, x, q->pdsch_symbols, q->cell.nof_ports,
nof_symbols / q->cell.nof_ports); harq->nof_re / q->cell.nof_ports);
} else {
memcpy(q->pdsch_symbols[0], q->pdsch_d, nof_symbols * sizeof(cf_t));
}
/* mapping to resource elements */
for (i = 0; i < q->cell.nof_ports; i++) {
pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe);
}
ret = LIBLTE_SUCCESS;
} else { } else {
fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n"); memcpy(q->pdsch_symbols[0], q->pdsch_d, harq->nof_re * sizeof(cf_t));
}
/* mapping to resource elements */
for (i = 0; i < q->cell.nof_ports; i++) {
pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq->dl_alloc, harq->sf_idx);
} }
ret = LIBLTE_SUCCESS;
} }
return ret; return ret;
} }

@ -34,7 +34,6 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include "prb.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/phich.h" #include "liblte/phy/phch/phich.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"

@ -29,6 +29,7 @@
#include <string.h> #include <string.h>
#include "liblte/phy/phch/prach.h" #include "liblte/phy/phch/prach.h"
#include "liblte/phy/utils/debug.h" #include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#define N_SEQS 64 // Number of prach sequences available #define N_SEQS 64 // Number of prach sequences available
#define N_RB_SC 12 // Number of subcarriers per resource block #define N_RB_SC 12 // Number of subcarriers per resource block
@ -197,10 +198,11 @@ int prach_gen_seqs(prach_t *p)
if(v > v_max){ if(v > v_max){
// Get a new root sequence // Get a new root sequence
if(4 == p->f){ if(4 == p->f){
u = prach_zc_roots_format4[p->rsi + p->N_roots]; u = prach_zc_roots_format4[(p->rsi + p->N_roots)%138];
}else{ }else{
u = prach_zc_roots[p->rsi + p->N_roots]; u = prach_zc_roots[(p->rsi + p->N_roots)%838];
} }
printf("Seq#%d, u: %3d (rsi: %d, n_roots: %d\n", i, u, p->rsi, p->N_roots);
for(int j=0;j<p->N_zc;j++){ for(int j=0;j<p->N_zc;j++){
double phase = -M_PI*u*j*(j+1)/p->N_zc; double phase = -M_PI*u*j*(j+1)/p->N_zc;
root[j] = cexp(phase*I); root[j] = cexp(phase*I);
@ -305,24 +307,24 @@ int prach_init(prach_t *p,
} }
// Set up containers // Set up containers
p->prach_bins = malloc(sizeof(cf_t)*p->N_zc); p->prach_bins = vec_malloc(sizeof(cf_t)*p->N_zc);
p->corr_spec = malloc(sizeof(cf_t)*p->N_zc); p->corr_spec = vec_malloc(sizeof(cf_t)*p->N_zc);
p->corr = malloc(sizeof(float)*p->N_zc); p->corr = vec_malloc(sizeof(float)*p->N_zc);
// Set up ZC FFTS // Set up ZC FFTS
p->zc_fft = (dft_plan_t*)malloc(sizeof(dft_plan_t)); p->zc_fft = (dft_plan_t*)vec_malloc(sizeof(dft_plan_t));
if(dft_plan(p->zc_fft, p->N_zc, FORWARD, COMPLEX)){ if(dft_plan(p->zc_fft, p->N_zc, FORWARD, COMPLEX)){
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
dft_plan_set_mirror(p->zc_fft, true); dft_plan_set_mirror(p->zc_fft, false);
dft_plan_set_norm(p->zc_fft, true); dft_plan_set_norm(p->zc_fft, false);
p->zc_ifft = (dft_plan_t*)malloc(sizeof(dft_plan_t)); p->zc_ifft = (dft_plan_t*)vec_malloc(sizeof(dft_plan_t));
if(dft_plan(p->zc_ifft, p->N_zc, BACKWARD, COMPLEX)){ if(dft_plan(p->zc_ifft, p->N_zc, BACKWARD, COMPLEX)){
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
dft_plan_set_mirror(p->zc_ifft, true); dft_plan_set_mirror(p->zc_ifft, false);
dft_plan_set_norm(p->zc_ifft, true); dft_plan_set_norm(p->zc_ifft, false);
// Generate our 64 sequences // Generate our 64 sequences
p->N_roots = 0; p->N_roots = 0;
@ -341,16 +343,16 @@ int prach_init(prach_t *p,
p->N_ifft_prach = p->N_ifft_ul * DELTA_F/DELTA_F_RA; p->N_ifft_prach = p->N_ifft_ul * DELTA_F/DELTA_F_RA;
} }
p->ifft_in = (cf_t*)malloc(p->N_ifft_prach*sizeof(cf_t)); p->ifft_in = (cf_t*)vec_malloc(p->N_ifft_prach*sizeof(cf_t));
p->ifft_out = (cf_t*)malloc(p->N_ifft_prach*sizeof(cf_t)); p->ifft_out = (cf_t*)vec_malloc(p->N_ifft_prach*sizeof(cf_t));
p->ifft = (dft_plan_t*)malloc(sizeof(dft_plan_t)); p->ifft = (dft_plan_t*)vec_malloc(sizeof(dft_plan_t));
if(dft_plan(p->ifft, p->N_ifft_prach, BACKWARD, COMPLEX)){ if(dft_plan(p->ifft, p->N_ifft_prach, BACKWARD, COMPLEX)){
return -1; return -1;
} }
dft_plan_set_mirror(p->ifft, true); dft_plan_set_mirror(p->ifft, true);
dft_plan_set_norm(p->ifft, true); dft_plan_set_norm(p->ifft, true);
p->fft = (dft_plan_t*)malloc(sizeof(dft_plan_t)); p->fft = (dft_plan_t*)vec_malloc(sizeof(dft_plan_t));
if(dft_plan(p->fft, p->N_ifft_prach, FORWARD, COMPLEX)){ if(dft_plan(p->fft, p->N_ifft_prach, FORWARD, COMPLEX)){
return -1; return -1;
} }
@ -369,6 +371,7 @@ int prach_init(prach_t *p,
int prach_gen(prach_t *p, int prach_gen(prach_t *p,
uint32_t seq_index, uint32_t seq_index,
uint32_t freq_offset, uint32_t freq_offset,
float beta_prach,
cf_t *signal) cf_t *signal)
{ {
int ret = LIBLTE_ERROR; int ret = LIBLTE_ERROR;
@ -382,23 +385,25 @@ int prach_gen(prach_t *p,
uint32_t K = DELTA_F/DELTA_F_RA; uint32_t K = DELTA_F/DELTA_F_RA;
uint32_t begin = PHI + (K*k_0) + (K/2); uint32_t begin = PHI + (K*k_0) + (K/2);
DEBUG("N_zc: %d, N_cp: %d, N_seq: %d, N_ifft_prach=%d begin: %d\n", p->N_zc, p->N_cp, p->N_seq, p->N_ifft_prach, begin);
// Map dft-precoded sequence to ifft bins // Map dft-precoded sequence to ifft bins
memset(p->ifft_in, 0, p->N_ifft_prach*sizeof(cf_t)); memset(p->ifft_in, 0, begin*sizeof(cf_t));
for(int i=0;i<p->N_zc;i++){ memcpy(&p->ifft_in[begin], p->dft_seqs[seq_index], p->N_zc * sizeof(cf_t));
p->ifft_in[begin+i] = p->dft_seqs[seq_index][i]; memset(&p->ifft_in[begin+p->N_zc], 0, (p->N_ifft_prach - begin - p->N_zc) * sizeof(cf_t));
}
dft_run(p->ifft, p->ifft_in, p->ifft_out); dft_run(p->ifft, p->ifft_in, p->ifft_out);
// Copy CP into buffer // Copy CP into buffer
for(int i=0;i<p->N_cp;i++){ memcpy(signal, &p->ifft_out[p->N_ifft_prach-p->N_cp], p->N_cp*sizeof(cf_t));
signal[i] = p->ifft_out[p->N_ifft_prach-p->N_cp+i];
}
// Copy preamble sequence into buffer // Copy preamble sequence into buffer
for(int i=0;i<p->N_seq;i++){ for(int i=0;i<p->N_seq;i++){
signal[p->N_cp+i] = p->ifft_out[i%p->N_ifft_prach]; signal[p->N_cp+i] = p->ifft_out[i%p->N_ifft_prach];
} }
// Normalize
vec_sc_prod_cfc(signal, beta_prach, signal, (p->N_cp + p->N_seq));
ret = LIBLTE_SUCCESS; ret = LIBLTE_SUCCESS;
} }
@ -489,8 +494,9 @@ int prach_detect(prach_t *p,
return ret; return ret;
} }
int prach_free(prach_t *p){ int prach_free(prach_t *p) {
free(p->prach_bins); free(p->prach_bins);
free(p->corr_spec);
free(p->corr); free(p->corr);
dft_plan_free(p->ifft); dft_plan_free(p->ifft);
free(p->ifft); free(p->ifft);

@ -29,7 +29,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include "prb.h" #include "prb_dl.h"
#include "liblte/phy/common/phy_common.h" #include "liblte/phy/common/phy_common.h"
//#define DEBUG_IDX //#define DEBUG_IDX

@ -34,5 +34,3 @@ void prb_cp(cf_t **input, cf_t **output, int nof_prb);
void prb_cp_half(cf_t **input, cf_t **output, int nof_prb); void prb_cp_half(cf_t **input, cf_t **output, int nof_prb);
void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs,
int nof_intervals); int nof_intervals);
void phch_get_prb_ref(cf_t **input, cf_t **output, int offset, int nof_refs,
int nof_intervals);

@ -0,0 +1,206 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/pucch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/filter/dft_precoding.h"
#define MAX_PUSCH_RE(cp) (2 * CP_NSYMB(cp) * 12)
bool pucch_cfg_isvalid(pucch_cfg_t *cfg) {
return true;
}
/* Generates n_cs_cell according to Sec 5.4 of 36.211 */
int generate_n_cs_cell(lte_cell_t cell, uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB])
{
sequence_t seq;
bzero(&seq, sizeof(sequence_t));
sequence_LTE_pr(&seq, 8*CP_NSYMB(cell.cp)*NSLOTS_X_FRAME, cell.id);
for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) {
for (uint32_t l=0;l<CP_NSYMB(cell.cp);l++) {
n_cs_cell[ns][l] = 0;
for (uint32_t i=0;i<8;i++) {
n_cs_cell[ns][l] += seq.c[8*CP_NSYMB(cell.cp)*ns+8*l+i]<<i;
}
}
}
sequence_free(&seq);
return LIBLTE_SUCCESS;
}
/* Calculates alpha according to 5.5.2.2.2 of 36.211 */
float pucch_get_alpha(uint32_t n_cs_cell[NSLOTS_X_FRAME][CPNORM_NSYMB],
pucch_cfg_t *cfg,
lte_cp_t cp, bool is_drms,
uint32_t ns, uint32_t l,
uint32_t *n_oc_ptr)
{
uint32_t c = CP_ISNORM(cp)?3:2;
uint32_t N_prime = (cfg->n_pucch < c*cfg->N_cs/cfg->delta_pucch_shift)?cfg->N_cs:12;
uint32_t n_prime = cfg->n_pucch;
if (cfg->n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) {
n_prime = (cfg->n_pucch-c*cfg->N_cs/cfg->delta_pucch_shift)%(cfg->N_cs/cfg->delta_pucch_shift);
}
uint32_t n_oc_div = (!is_drms && CP_ISEXT(cp))?2:1;
uint32_t n_oc = n_prime*cfg->delta_pucch_shift/N_prime;
if (!is_drms && CP_ISEXT(cp)) {
n_oc *= 2;
}
if (n_oc_ptr) {
*n_oc_ptr = n_oc;
}
uint32_t n_cs = 0;
if (CP_ISNORM(cp)) {
n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+(n_oc%cfg->delta_pucch_shift))%N_prime)%12;
} else {
n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+n_oc/n_oc_div)%N_prime)%12;
}
return 2 * M_PI * (n_cs) / 12;
}
int pucch_cp(pucch_t *q, harq_t *harq, cf_t *input, cf_t *output, bool advance_input)
{
return LIBLTE_ERROR;
}
int pucch_put(pucch_t *q, harq_t *harq, cf_t *input, cf_t *output) {
return pucch_cp(q, harq, input, output, true);
}
int pucch_get(pucch_t *q, harq_t *harq, cf_t *input, cf_t *output) {
return pucch_cp(q, harq, input, output, false);
}
/** Initializes the PDCCH transmitter and receiver */
int pucch_init(pucch_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL && lte_cell_isvalid(&cell)) {
ret = LIBLTE_ERROR;
bzero(q, sizeof(pucch_t));
q->cell = cell;
if (generate_n_cs_cell(q->cell, q->n_cs_cell)) {
return LIBLTE_ERROR;
}
ret = LIBLTE_SUCCESS;
}
return ret;
}
void pucch_free(pucch_t *q) {
bzero(q, sizeof(pucch_t));
}
/** Decodes the PUSCH from the received symbols
*/
int pucch_decode(pucch_t *q, harq_t *harq, cf_t *sf_symbols, cf_t *ce, float noise_estimate, uint8_t *data)
{
return LIBLTE_ERROR_INVALID_INPUTS;
}
static cf_t uci_encode_format1() {
return 1.0;
}
static cf_t uci_encode_format1a(uint8_t bit) {
return bit?1.0:-1.0;
}
static cf_t uci_encode_format1b(uint8_t bits[2]) {
if (bits[0] == 0) {
if (bits[1] == 0) {
return 1;
} else {
return -I;
}
} else {
if (bits[1] == 0) {
return I;
} else {
return -1.0;
}
}
}
static void uci_mod_bits(pucch_t *q, pucch_cfg_t *cfg, uint8_t bits[PUCCH_MAX_BITS])
{
cf_t d_0 = 0;
uint8_t tmp[2];
switch(cfg->format) {
case PUCCH_FORMAT_1:
d_0 = uci_encode_format1();
break;
case PUCCH_FORMAT_1A:
d_0 = uci_encode_format1a(bits[0]);
break;
case PUCCH_FORMAT_1B:
tmp[0] = bits[0];
tmp[1] = bits[1];
d_0 = uci_encode_format1b(tmp);
default:
fprintf(stderr, "PUCCH format 2 not supported\n");
return;
}
/*
for (uint32_t n=0;n<PUCCH_N_SEQ;n++) {
q->y[n] = d_0+
}
*/
}
int pucch_encode(pucch_t *q, pucch_cfg_t *cfg, uint8_t bits[PUCCH_MAX_BITS], cf_t *sf_symbols)
{
uci_mod_bits(q, cfg, bits);
return LIBLTE_ERROR;
}

@ -0,0 +1,445 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/filter/dft_precoding.h"
#define MAX_PUSCH_RE(cp) (2 * CP_NSYMB(cp) * 12)
const static lte_mod_t modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
static int f_hop_sum(pusch_t *q, uint32_t i) {
uint32_t sum = 0;
for (uint32_t k=i*10+1;k<i*10+9;i++) {
sum += (q->seq_type2_fo.c[k]<<(k-(i*10+1)));
}
return sum;
}
static int f_hop(pusch_t *q, pusch_hopping_cfg_t *hopping, int i) {
if (i == -1) {
return 0;
} else {
if (hopping->n_sb == 1) {
return 0;
} else if (hopping->n_sb == 2) {
return (f_hop(q, hopping, i-1) + f_hop_sum(q, i))%2;
} else {
return (f_hop(q, hopping, i-1) + f_hop_sum(q, i)%(hopping->n_sb-1)+1)%hopping->n_sb;
}
}
}
static int f_m(pusch_t *q, pusch_hopping_cfg_t *hopping, uint32_t i) {
if (hopping->n_sb == 1) {
if (hopping->hop_mode == hop_mode_inter_sf) {
return hopping->current_tx_nb%2;
} else {
return i%2;
}
} else {
return q->seq_type2_fo.c[i*10];
}
}
int pusch_cp(pusch_t *q, harq_t *harq, cf_t *input, cf_t *output, bool advance_input)
{
cf_t *in_ptr = input;
cf_t *out_ptr = output;
pusch_hopping_cfg_t *hopping = &q->hopping_cfg;
uint32_t L_ref = 3;
if (CP_ISEXT(q->cell.cp)) {
L_ref = 2;
}
INFO("PUSCH Freq hopping: %d\n", harq->ul_alloc.freq_hopping);
for (uint32_t slot=0;slot<2;slot++) {
uint32_t n_prb_tilde = harq->ul_alloc.n_prb[slot];
if (harq->ul_alloc.freq_hopping == 1) {
if (hopping->hop_mode == hop_mode_inter_sf) {
n_prb_tilde = harq->ul_alloc.n_prb[hopping->current_tx_nb%2];
} else {
n_prb_tilde = harq->ul_alloc.n_prb[slot];
}
}
if (harq->ul_alloc.freq_hopping == 2) {
/* Freq hopping type 2 as defined in 5.3.4 of 36.211 */
uint32_t n_vrb_tilde = harq->ul_alloc.n_prb[0];
if (hopping->n_sb > 1) {
n_vrb_tilde -= (hopping->hopping_offset-1)/2+1;
}
int i=0;
if (hopping->hop_mode == hop_mode_inter_sf) {
i = harq->sf_idx;
} else {
i = 2*harq->sf_idx+slot;
}
uint32_t n_rb_sb = q->cell.nof_prb;
if (hopping->n_sb > 1) {
n_rb_sb = (n_rb_sb-hopping->hopping_offset-hopping->hopping_offset%2)/hopping->n_sb;
}
n_prb_tilde = (n_vrb_tilde+f_hop(q, hopping, i)*n_rb_sb+
(n_rb_sb-1)-2*(n_vrb_tilde%n_rb_sb)*f_m(q, hopping, i))%(n_rb_sb*hopping->n_sb);
INFO("n_prb_tilde: %d, n_vrb_tilde: %d, n_rb_sb: %d, n_sb: %d\n",
n_prb_tilde, n_vrb_tilde, n_rb_sb, hopping->n_sb);
if (hopping->n_sb > 1) {
n_prb_tilde += (hopping->hopping_offset-1)/2+1;
}
}
harq->ul_alloc.n_prb_tilde[slot] = n_prb_tilde;
INFO("Allocating PUSCH %d PRB to index %d at slot %d\n",harq->ul_alloc.L_prb, n_prb_tilde,slot);
for (uint32_t l=0;l<CP_NSYMB(q->cell.cp);l++) {
if (l != L_ref) {
uint32_t idx = RE_IDX(q->cell.nof_prb, l+slot*CP_NSYMB(q->cell.cp),
n_prb_tilde*RE_X_RB);
if (advance_input) {
out_ptr = &output[idx];
} else {
in_ptr = &input[idx];
}
memcpy(out_ptr, in_ptr, harq->ul_alloc.L_prb * RE_X_RB * sizeof(cf_t));
if (advance_input) {
in_ptr += harq->ul_alloc.L_prb*RE_X_RB;
} else {
out_ptr += harq->ul_alloc.L_prb*RE_X_RB;
}
}
}
}
return RE_X_RB*harq->ul_alloc.L_prb;
}
int pusch_put(pusch_t *q, harq_t *harq, cf_t *input, cf_t *output) {
return pusch_cp(q, harq, input, output, true);
}
int pusch_get(pusch_t *q, harq_t *harq, cf_t *input, cf_t *output) {
return pusch_cp(q, harq, input, output, false);
}
/** Initializes the PDCCH transmitter and receiver */
int pusch_init(pusch_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
int i;
if (q != NULL &&
lte_cell_isvalid(&cell))
{
bzero(q, sizeof(pusch_t));
ret = LIBLTE_ERROR;
q->cell = cell;
q->max_re = q->cell.nof_prb * MAX_PUSCH_RE(q->cell.cp);
INFO("Init PUSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
q->cell.nof_prb, q->max_re);
for (i = 0; i < 4; i++) {
if (modem_table_lte(&q->mod[i], modulations[i], true)) {
goto clean;
}
}
/* Precompute sequence for type2 frequency hopping */
if (sequence_LTE_pr(&q->seq_type2_fo, 210, q->cell.id)) {
fprintf(stderr, "Error initiating type2 frequency hopping sequence\n");
goto clean;
}
demod_soft_init(&q->demod, q->max_re);
demod_soft_alg_set(&q->demod, APPROX);
sch_init(&q->dl_sch);
if (dft_precoding_init(&q->dft_precoding, cell.nof_prb)) {
fprintf(stderr, "Error initiating DFT transform precoding\n");
goto clean;
}
/* This is for equalization at receiver */
if (precoding_init(&q->equalizer, SF_LEN_RE(cell.nof_prb, cell.cp))) {
fprintf(stderr, "Error initializing precoding\n");
goto clean;
}
q->rnti_is_set = false;
// Allocate floats for reception (LLRs). Buffer casted to uint8_t for transmission
q->pusch_q = vec_malloc(sizeof(float) * q->max_re * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pusch_q) {
goto clean;
}
// Allocate floats for reception (LLRs). Buffer casted to uint8_t for transmission
q->pusch_g = vec_malloc(sizeof(float) * q->max_re * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pusch_g) {
goto clean;
}
q->pusch_d = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->pusch_d) {
goto clean;
}
q->ce = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->ce) {
goto clean;
}
q->pusch_z = vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->pusch_z) {
goto clean;
}
ret = LIBLTE_SUCCESS;
}
clean:
if (ret == LIBLTE_ERROR) {
pusch_free(q);
}
return ret;
}
void pusch_free(pusch_t *q) {
int i;
if (q->pusch_q) {
free(q->pusch_q);
}
if (q->pusch_d) {
free(q->pusch_d);
}
if (q->pusch_g) {
free(q->pusch_g);
}
if (q->ce) {
free(q->ce);
}
if (q->pusch_z) {
free(q->pusch_z);
}
dft_precoding_free(&q->dft_precoding);
precoding_free(&q->equalizer);
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
sequence_free(&q->seq_pusch[i]);
}
for (i = 0; i < 4; i++) {
modem_table_free(&q->mod[i]);
}
demod_soft_free(&q->demod);
sch_free(&q->dl_sch);
bzero(q, sizeof(pusch_t));
}
void pusch_set_hopping_cfg(pusch_t *q, pusch_hopping_cfg_t *cfg)
{
memcpy(&q->hopping_cfg, cfg, sizeof(pusch_hopping_cfg_t));
}
/* Precalculate the PUSCH scramble sequences for a given RNTI. This function takes a while
* to execute, so shall be called once the final C-RNTI has been allocated for the session.
* For the connection procedure, use pusch_encode_rnti() or pusch_decode_rnti() functions */
int pusch_set_rnti(pusch_t *q, uint16_t rnti) {
uint32_t i;
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
if (sequence_pusch(&q->seq_pusch[i], rnti, 2 * i, q->cell.id,
q->max_re * lte_mod_bits_x_symbol(LTE_QAM64))) {
return LIBLTE_ERROR;
}
}
q->rnti_is_set = true;
q->rnti = rnti;
return LIBLTE_SUCCESS;
}
/** Decodes the PUSCH from the received symbols
*/
int pusch_decode(pusch_t *q, harq_t *harq, cf_t *sf_symbols, cf_t *ce, float noise_estimate, uint8_t *data)
{
uint32_t n;
if (q != NULL &&
sf_symbols != NULL &&
data != NULL &&
harq != NULL)
{
if (q->rnti_is_set) {
INFO("Decoding PUSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
harq->sf_idx, lte_mod_string(harq->mcs.mod), harq->mcs.tbs, harq->nof_re, harq->nof_bits, harq->rv);
/* extract symbols */
n = pusch_get(q, harq, sf_symbols, q->pusch_d);
if (n != harq->nof_re) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", harq->nof_re, n);
return LIBLTE_ERROR;
}
/* extract channel estimates */
n = pusch_get(q, harq, ce, q->ce);
if (n != harq->nof_re) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", harq->nof_re, n);
return LIBLTE_ERROR;
}
predecoding_single(&q->equalizer, q->pusch_d, q->ce, q->pusch_z,
harq->nof_re, noise_estimate);
dft_predecoding(&q->dft_precoding, q->pusch_z, q->pusch_d,
harq->ul_alloc.L_prb, harq->nof_symb);
/* demodulate symbols
* The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation,
* thus we don't need tot set it in the LLRs normalization
*/
demod_soft_sigma_set(&q->demod, sqrt(0.5));
demod_soft_table_set(&q->demod, &q->mod[harq->mcs.mod]);
demod_soft_demodulate(&q->demod, q->pusch_d, q->pusch_q, harq->nof_re);
/* descramble */
scrambling_f_offset(&q->seq_pusch[harq->sf_idx], q->pusch_q, 0, harq->nof_bits);
return ulsch_decode(&q->dl_sch, harq, q->pusch_q, data);
} else {
fprintf(stderr, "Must call pusch_set_rnti() before calling pusch_decode()\n");
return LIBLTE_ERROR;
}
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
int pusch_encode_rnti(pusch_t *q, harq_t *harq_process, uint8_t *data, uint16_t rnti, cf_t *sf_symbols)
{
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
return pusch_uci_encode_rnti(q, harq_process, data, uci_data, rnti, sf_symbols);
}
int pusch_encode(pusch_t *q, harq_t *harq_process, uint8_t *data, cf_t *sf_symbols)
{
if (q->rnti_is_set) {
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
return pusch_uci_encode_rnti(q, harq_process, data, uci_data, q->rnti, sf_symbols);
} else {
fprintf(stderr, "Must call pusch_set_rnti() to set the encoder/decoder RNTI\n");
return LIBLTE_ERROR;
}
}
int pusch_uci_encode(pusch_t *q, harq_t *harq, uint8_t *data, uci_data_t uci_data, cf_t *sf_symbols)
{
if (q->rnti_is_set) {
return pusch_uci_encode_rnti(q, harq, data, uci_data, q->rnti, sf_symbols);
} else {
fprintf(stderr, "Must call pusch_set_rnti() to set the encoder/decoder RNTI\n");
return LIBLTE_ERROR;
}
}
/** Converts the PUSCH data bits to symbols mapped to the slot ready for transmission
*/
int pusch_uci_encode_rnti(pusch_t *q, harq_t *harq, uint8_t *data, uci_data_t uci_data, uint16_t rnti, cf_t *sf_symbols)
{
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
data != NULL &&
harq != NULL)
{
if (harq->mcs.tbs > harq->nof_bits) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) harq->mcs.tbs / harq->nof_bits);
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (harq->nof_re > q->max_re) {
fprintf(stderr, "Error too many RE per subframe (%d). PUSCH configured for %d RE (%d PRB)\n",
harq->nof_re, q->max_re, q->cell.nof_prb);
return LIBLTE_ERROR_INVALID_INPUTS;
}
INFO("Encoding PUSCH SF: %d, Mod %s, RNTI: %d, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
harq->sf_idx, lte_mod_string(harq->mcs.mod), rnti, harq->mcs.tbs, harq->nof_re, harq->nof_bits, harq->rv);
bzero(q->pusch_q, harq->nof_bits);
if (ulsch_uci_encode(&q->dl_sch, harq, data, uci_data, q->pusch_g, q->pusch_q)) {
fprintf(stderr, "Error encoding TB\n");
return LIBLTE_ERROR;
}
if (rnti != q->rnti) {
sequence_t seq;
if (sequence_pusch(&seq, rnti, 2 * harq->sf_idx, q->cell.id, harq->nof_bits)) {
return LIBLTE_ERROR;
}
scrambling_b_offset_pusch(&seq, (uint8_t*) q->pusch_q, 0, harq->nof_bits);
sequence_free(&seq);
} else {
scrambling_b_offset_pusch(&q->seq_pusch[harq->sf_idx], (uint8_t*) q->pusch_q, 0, harq->nof_bits);
}
mod_modulate(&q->mod[harq->mcs.mod], (uint8_t*) q->pusch_q, q->pusch_d, harq->nof_bits);
dft_precoding(&q->dft_precoding, q->pusch_d, q->pusch_z,
harq->ul_alloc.L_prb, harq->nof_symb);
/* mapping to resource elements */
pusch_put(q, harq, q->pusch_z, sf_symbols);
ret = LIBLTE_SUCCESS;
}
return ret;
}

@ -108,8 +108,83 @@ uint32_t ra_re_x_prb(uint32_t subframe, uint32_t slot, uint32_t prb_idx, uint32_
return re; return re;
} }
void ra_prb_fprint(FILE *f, ra_prb_slot_t *prb, uint32_t nof_prb) {
int i;
if (prb->nof_prb > 0) {
for (i=0;i<nof_prb;i++) {
if (prb->prb_idx[i]) {
fprintf(f, "%d, ", i);
}
}
fprintf(f, "\n");
}
}
/** Compute PRB allocation for Uplink as defined in 8.1 and 8.4 of 36.213 */
int ra_ul_alloc(ra_ul_alloc_t *prb_dist, ra_pusch_t *ra, uint32_t n_rb_ho, uint32_t nof_prb) {
bzero(prb_dist, sizeof(ra_ul_alloc_t));
prb_dist->L_prb = ra->type2_alloc.L_crb;
uint32_t n_prb_1 = ra->type2_alloc.RB_start;
uint32_t n_rb_pusch = 0;
if (n_rb_ho%2) {
n_rb_ho++;
}
if (ra->freq_hop_fl == hop_disabled || ra->freq_hop_fl == hop_type_2) {
/* For no freq hopping or type2 freq hopping, n_prb is the same
* n_prb_tilde is calculated during resource mapping
*/
for (uint32_t i=0;i<2;i++) {
prb_dist->n_prb[i] = n_prb_1;
}
if (ra->freq_hop_fl == hop_disabled) {
prb_dist->freq_hopping = 0;
} else {
prb_dist->freq_hopping = 2;
}
INFO("prb1: %d, prb2: %d, L: %d\n", prb_dist->n_prb[0], prb_dist->n_prb[1], prb_dist->L_prb);
} else {
/* Type1 frequency hopping as defined in 8.4.1 of 36.213
* frequency offset between 1st and 2nd slot is fixed.
*/
n_rb_pusch = nof_prb - n_rb_ho - (nof_prb%2);
// starting prb idx for slot 0 is as given by resource grant
prb_dist->n_prb[0] = n_prb_1;
if (n_prb_1 < n_rb_ho/2) {
fprintf(stderr, "Invalid Frequency Hopping parameters. Offset: %d, n_prb_1: %d\n", n_rb_ho, n_prb_1);
}
uint32_t n_prb_1_tilde = n_prb_1;
// prb idx for slot 1
switch(ra->freq_hop_fl) {
case hop_quart:
prb_dist->n_prb[1] = (n_rb_pusch/4+ n_prb_1_tilde)%n_rb_pusch;
break;
case hop_quart_neg:
if (n_prb_1 < n_rb_pusch/4) {
prb_dist->n_prb[1] = (n_rb_pusch+ n_prb_1_tilde -n_rb_pusch/4);
} else {
prb_dist->n_prb[1] = (n_prb_1_tilde -n_rb_pusch/4);
}
break;
case hop_half:
prb_dist->n_prb[1] = (n_rb_pusch/2+ n_prb_1_tilde)%n_rb_pusch;
break;
default:
break;
}
INFO("n_rb_pusch: %d, prb1: %d, prb2: %d, L: %d\n", n_rb_pusch, prb_dist->n_prb[0], prb_dist->n_prb[1], prb_dist->L_prb);
prb_dist->freq_hopping = 1;
}
return LIBLTE_SUCCESS;
}
/* Computes the number of RE for each PRB in the prb_dist structure */ /* Computes the number of RE for each PRB in the prb_dist structure */
void ra_prb_get_re_dl(ra_prb_t *prb_dist, uint32_t nof_prb, uint32_t nof_ports, void ra_dl_alloc_re(ra_dl_alloc_t *prb_dist, uint32_t nof_prb, uint32_t nof_ports,
uint32_t nof_ctrl_symbols, lte_cp_t cp) { uint32_t nof_ctrl_symbols, lte_cp_t cp) {
uint32_t i, j, s; uint32_t i, j, s;
@ -129,41 +204,14 @@ void ra_prb_get_re_dl(ra_prb_t *prb_dist, uint32_t nof_prb, uint32_t nof_ports,
} }
} }
void ra_prb_fprint(FILE *f, ra_prb_slot_t *prb, uint32_t nof_prb) {
int i;
if (prb->nof_prb > 0) {
for (i=0;i<nof_prb;i++) {
if (prb->prb_idx[i]) {
fprintf(f, "%d, ", i);
}
}
fprintf(f, "\n");
}
}
/** Compute PRB allocation for Uplink as defined in 8.1 of 36.213 */
int ra_prb_get_ul(ra_prb_slot_t *prb, ra_pusch_t *ra, uint32_t nof_prb) {
int i;
if (ra->type2_alloc.mode != t2_loc) {
fprintf(stderr, "Uplink only accepts type2 localized scheduling\n");
return LIBLTE_ERROR;
}
for (i = 0; i < ra->type2_alloc.L_crb; i++) {
prb->prb_idx[i] = i + ra->type2_alloc.RB_start;
prb->nof_prb++;
}
return LIBLTE_SUCCESS;
}
/** Compute PRB allocation for Downlink as defined in 7.1.6 of 36.213 */ /** Compute PRB allocation for Downlink as defined in 7.1.6 of 36.213 */
int ra_prb_get_dl(ra_prb_t *prb_dist, ra_pdsch_t *ra, uint32_t nof_prb) { int ra_dl_alloc(ra_dl_alloc_t *prb_dist, ra_pdsch_t *ra, uint32_t nof_prb) {
int i, j; int i, j;
uint32_t bitmask; uint32_t bitmask;
uint32_t P = ra_type0_P(nof_prb); uint32_t P = ra_type0_P(nof_prb);
uint32_t n_rb_rbg_subset, n_rb_type1; uint32_t n_rb_rbg_subset, n_rb_type1;
bzero(prb_dist, sizeof(ra_prb_t)); bzero(prb_dist, sizeof(ra_dl_alloc_t));
switch (ra->alloc_type) { switch (ra->alloc_type) {
case alloc_type0: case alloc_type0:
bitmask = ra->type0_alloc.rbg_bitmask; bitmask = ra->type0_alloc.rbg_bitmask;
@ -476,13 +524,36 @@ int ra_tbs_to_table_idx(uint32_t tbs, uint32_t n_prb) {
} }
void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, uint32_t nof_prb) { void ra_pusch_fprint(FILE *f, ra_pusch_t *ra, uint32_t nof_prb) {
fprintf(f, "Frequency Hopping:\t"); fprintf(f, " - Resource Allocation Type 2 mode :\t%s\n",
ra->type2_alloc.mode == t2_loc ? "Localized" : "Distributed");
fprintf(f, " + Frequency Hopping:\t\t\t");
if (ra->freq_hop_fl == hop_disabled) { if (ra->freq_hop_fl == hop_disabled) {
fprintf(f, "No"); fprintf(f, "No\n");
} else {
fprintf(f, "Yes\n");
}
fprintf(f, " + Resource Indicator Value:\t\t%d\n", ra->type2_alloc.riv);
if (ra->type2_alloc.mode == t2_loc) {
fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n",
ra->type2_alloc.L_crb, ra->type2_alloc.RB_start);
} else { } else {
fprintf(f, "Yes"); fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n",
ra->type2_alloc.L_crb, ra->type2_alloc.RB_start);
fprintf(f, " + VRB gap selection:\t\t\tGap %d\n",
ra->type2_alloc.n_gap == t2_ng1 ? 1 : 2);
fprintf(f, " + VRB gap:\t\t\t\t%d\n",
ra_type2_ngap(nof_prb, ra->type2_alloc.n_gap == t2_ng1));
} }
fprintf(f, " - Number of PRBs:\t\t\t%d\n", ra_nprb_ul(ra, nof_prb));
fprintf(f, " - Modulation and coding scheme index:\t%d\n", ra->mcs_idx);
fprintf(f, " - Modulation type:\t\t\t%s\n", lte_mod_string(ra->mcs.mod));
fprintf(f, " - Transport block size:\t\t%d\n", ra->mcs.tbs);
fprintf(f, " - New data indicator:\t\t\t%s\n", ra->ndi ? "Yes" : "No");
fprintf(f, " - Redundancy version:\t\t\t%d\n", ra->rv_idx);
fprintf(f, " - TPC command for PUCCH:\t\t--\n");
} }
char *ra_type_string(ra_type_t alloc_type) { char *ra_type_string(ra_type_t alloc_type) {
@ -532,8 +603,8 @@ void ra_pdsch_fprint(FILE *f, ra_pdsch_t *ra, uint32_t nof_prb) {
break; break;
} }
ra_prb_t alloc; ra_dl_alloc_t alloc;
ra_prb_get_dl(&alloc, ra, nof_prb); ra_dl_alloc(&alloc, ra, nof_prb);
for (int s = 0; s < 2; s++) { for (int s = 0; s < 2; s++) {
fprintf(f, " - PRB Bitmap Assignment %dst slot:\n", s); fprintf(f, " - PRB Bitmap Assignment %dst slot:\n", s);
ra_prb_fprint(f, &alloc.slot[s], nof_prb); ra_prb_fprint(f, &alloc.slot[s], nof_prb);

@ -0,0 +1,517 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
int sch_init(sch_t *q) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q) {
bzero(q, sizeof(sch_t));
if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (tcod_init(&q->encoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Coder\n");
goto clean;
}
if (tdec_init(&q->decoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Decoder\n");
goto clean;
}
// Allocate floats for reception (LLRs)
q->cb_in = vec_malloc(sizeof(uint8_t) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->cb_out = vec_malloc(sizeof(float) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out) {
goto clean;
}
if (uci_cqi_init(&q->uci_cqi)) {
goto clean;
}
ret = LIBLTE_SUCCESS;
}
clean:
if (ret == LIBLTE_ERROR) {
sch_free(q);
}
return ret;
}
void sch_free(sch_t *q) {
if (q->cb_in) {
free(q->cb_in);
}
if (q->cb_out) {
free(q->cb_out);
}
tdec_free(&q->decoder);
tcod_free(&q->encoder);
uci_cqi_free(&q->uci_cqi);
bzero(q, sizeof(sch_t));
}
float sch_average_noi(sch_t *q) {
return q->average_nof_iterations;
}
uint32_t sch_last_noi(sch_t *q) {
return q->nof_iterations;
}
/* Encode a transport block according to 36.212 5.3.2
*
*/
static int encode_tb(sch_t *q, harq_t *harq, uint8_t *data, uint8_t *e_bits, uint32_t nof_e_bits)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t Qm = lte_mod_bits_x_symbol(harq->mcs.mod);
if (q != NULL &&
data != NULL &&
harq != NULL)
{
uint32_t Gp = nof_e_bits / Qm;
uint32_t gamma = Gp;
if (harq->cb_segm.C > 0) {
gamma = Gp%harq->cb_segm.C;
}
if (harq->rv == 0) {
/* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, harq->mcs.tbs);
/* parity bits will be appended later */
bit_pack(par, &p_parity, 24);
if (VERBOSE_ISDEBUG()) {
DEBUG("DATA: ", 0);
vec_fprint_b(stdout, data, harq->mcs.tbs);
DEBUG("PARITY: ", 0);
vec_fprint_b(stdout, parity, 24);
}
}
wp = 0;
rp = 0;
for (i = 0; i < harq->cb_segm.C; i++) {
/* Get read lengths */
if (i < harq->cb_segm.C2) {
cb_len = harq->cb_segm.K2;
} else {
cb_len = harq->cb_segm.K1;
}
if (harq->cb_segm.C > 1) {
rlen = cb_len - 24;
} else {
rlen = cb_len;
}
if (i == 0) {
F = harq->cb_segm.F;
} else {
F = 0;
}
if (i <= harq->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq->cb_segm.C));
}
INFO("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);
if (harq->rv == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq->cb_segm.C - 1) {
// Copy data
memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(uint8_t));
} else {
INFO("Last CB, appending parity: %d from %d and 24 to %d\n",
rlen - F - 24, rp, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&q->cb_in[F], &data[rp], (rlen - 24 - F) * sizeof(uint8_t));
memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(uint8_t));
}
/* Filler bits are treated like zeros for the CB CRC calculation */
for (int j = 0; j < F; j++) {
q->cb_in[j] = 0;
}
/* Attach Codeblock CRC */
if (harq->cb_segm.C > 1) {
crc_attach(&q->crc_cb, q->cb_in, rlen);
}
/* Set the filler bits to <NULL> */
for (int j = 0; j < F; j++) {
q->cb_in[j] = TX_NULL;
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d: ", i);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, q->cb_in, (uint8_t*) q->cb_out, cb_len);
}
/* Rate matching */
if (rm_turbo_tx(harq->pdsch_w_buff_c[i], harq->w_buff_size,
(uint8_t*) q->cb_out, 3 * cb_len + 12,
&e_bits[wp], n_e, harq->rv))
{
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Set read/write pointers */
rp += (rlen - F);
wp += n_e;
}
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
ret = LIBLTE_SUCCESS;
}
return ret;
}
/* Decode a transport block according to 36.212 5.3.2
*
*/
static int decode_tb(sch_t *q, harq_t *harq, float *e_bits, uint8_t *data, uint32_t nof_e_bits)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par_rx, par_tx;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
uint32_t Qm = lte_mod_bits_x_symbol(harq->mcs.mod);
if (q != NULL &&
data != NULL &&
harq != NULL)
{
if (harq->mcs.tbs == 0 || harq->cb_segm.C == 0) {
return LIBLTE_SUCCESS;
}
rp = 0;
rp = 0;
wp = 0;
uint32_t Gp = nof_e_bits / Qm;
uint32_t gamma=Gp;
if (harq->cb_segm.C>0) {
gamma = Gp%harq->cb_segm.C;
}
bool early_stop = true;
for (i = 0; i < harq->cb_segm.C && early_stop; i++) {
/* Get read/write lengths */
if (i < harq->cb_segm.C2) {
cb_len = harq->cb_segm.K2;
} else {
cb_len = harq->cb_segm.K1;
}
if (harq->cb_segm.C == 1) {
rlen = cb_len;
} else {
rlen = cb_len - 24;
}
if (i == 0) {
F = harq->cb_segm.F;
} else {
F = 0;
}
if (i <= harq->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq->cb_segm.C));
}
INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i,
cb_len, rlen - F, wp, rp, F, n_e);
/* Rate Unmatching */
if (rm_turbo_rx(harq->pdsch_w_buff_f[i], harq->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_out, 3 * cb_len + 12, harq->rv, F)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d RMOUT: ", i);
vec_fprint_f(stdout, q->cb_out, 3*cb_len+12);
}
/* Turbo Decoding with CRC-based early stopping */
q->nof_iterations = 0;
uint32_t len_crc;
uint8_t *cb_in_ptr;
crc_t *crc_ptr;
early_stop = false;
tdec_reset(&q->decoder, cb_len);
do {
tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len);
q->nof_iterations++;
if (harq->cb_segm.C > 1) {
len_crc = cb_len;
cb_in_ptr = q->cb_in;
crc_ptr = &q->crc_cb;
} else {
len_crc = harq->mcs.tbs+24;
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 = VEC_EMA((float) q->nof_iterations, q->average_nof_iterations, 0.2);
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d IN: ", i);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
// If CB CRC is not correct, early_stop will be false and wont continue with rest of CBs
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < harq->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(uint8_t));
} else {
DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
rlen - F - 24, wp, F, rlen - 24);
/* Append Transport Block parity bits to the last CB */
memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(uint8_t));
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(uint8_t));
}
/* Set read/write pointers */
wp += (rlen - F);
rp += n_e;
}
if (!early_stop) {
INFO("CB %d failed. TB is erroneous.\n",i-1);
return LIBLTE_ERROR;
} else {
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
// Compute transport block CRC
par_rx = crc_checksum(&q->crc_tb, data, harq->mcs.tbs);
// check parity bits
par_tx = bit_unpack(&p_parity, 24);
if (!par_rx) {
INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0);
}
if (par_rx == par_tx) {
INFO("TB decoded OK\n",i);
return LIBLTE_SUCCESS;
} else {
INFO("Error in TB parity\n",i);
return LIBLTE_ERROR;
}
}
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
int dlsch_decode(sch_t *q, harq_t *harq, float *e_bits, uint8_t *data)
{
return decode_tb(q, harq, e_bits, data, harq->nof_bits);
}
int dlsch_encode(sch_t *q, harq_t *harq, uint8_t *data, uint8_t *e_bits) {
return encode_tb(q, harq, data, e_bits, harq->nof_bits);
}
int ulsch_decode(sch_t *q, harq_t *harq, float *e_bits, uint8_t *data)
{
return decode_tb(q, harq, e_bits, data, harq->nof_bits);
}
/* UL-SCH channel interleaver according to 5.5.2.8 of 36.212 */
void ulsch_interleave(uint8_t *g_bits, uint32_t Q_m, uint32_t H_prime_total, uint32_t N_pusch_symbs, uint8_t *q_bits)
{
uint32_t rows = H_prime_total/N_pusch_symbs;
uint32_t cols = N_pusch_symbs;
uint32_t idx = 0;
for(uint32_t j=0; j<rows; j++) {
for(uint32_t i=0; i<cols; i++) {
for(uint32_t k=0; k<Q_m; k++) {
if (q_bits[j*Q_m + i*rows*Q_m + k] >= 10) {
q_bits[j*Q_m + i*rows*Q_m + k] -= 10;
} else {
q_bits[j*Q_m + i*rows*Q_m + k] = g_bits[idx];
idx++;
}
}
}
}
}
int ulsch_encode(sch_t *q, harq_t *harq, uint8_t *data, uint8_t *g_bits, uint8_t *q_bits)
{
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
return ulsch_uci_encode(q, harq, data, uci_data, g_bits, q_bits);
}
int ulsch_uci_encode(sch_t *q, harq_t *harq, uint8_t *data, uci_data_t uci_data, uint8_t *g_bits, uint8_t *q_bits)
{
int ret;
uint32_t e_offset = 0;
uint32_t Q_prime_cqi = 0;
uint32_t Q_prime_ack = 0;
uint32_t Q_prime_ri = 0;
uint32_t Q_m = lte_mod_bits_x_symbol(harq->mcs.mod);
uint32_t nof_symbols = 12*harq->ul_alloc.L_prb*RE_X_RB;
uint32_t nb_q = nof_symbols * Q_m;
bzero(q_bits, sizeof(uint8_t) * nb_q);
// Encode RI
if (uci_data.uci_ri_len > 0) {
float beta = uci_data.beta_ri;
if (harq->mcs.tbs == 0) {
beta /= uci_data.beta_cqi;
}
ret = uci_encode_ri(uci_data.uci_ri, uci_data.uci_cqi_len, beta, harq, nb_q/Q_m, q_bits);
if (ret < 0) {
return ret;
}
Q_prime_ri = (uint32_t) ret;
}
// Encode CQI
if (uci_data.uci_cqi_len > 0) {
ret = uci_encode_cqi_pusch(&q->uci_cqi, uci_data.uci_cqi, uci_data.uci_cqi_len, uci_data.beta_cqi,
Q_prime_ri, harq, g_bits);
if (ret < 0) {
return ret;
}
Q_prime_cqi = (uint32_t) ret;
}
e_offset += Q_prime_cqi*Q_m;
// Encode UL-SCH
if (harq->mcs.tbs > 0) {
uint32_t G = nb_q/Q_m - Q_prime_ri - Q_prime_cqi;
ret = encode_tb(q, harq, data, &g_bits[e_offset], G*Q_m);
if (ret) {
return ret;
}
}
// Interleave UL-SCH (and RI and CQI)
ulsch_interleave(g_bits, Q_m, nb_q/Q_m, harq->nof_symb, q_bits);
// Encode (and interleave) ACK
if (uci_data.uci_ack_len > 0) {
float beta = uci_data.beta_ack;
if (harq->mcs.tbs == 0) {
beta /= uci_data.beta_cqi;
}
ret = uci_encode_ack(uci_data.uci_ack, uci_data.uci_cqi_len, beta, harq, nb_q/Q_m, q_bits);
if (ret < 0) {
return ret;
}
Q_prime_ack = (uint32_t) ret;
}
INFO("Q_prime_ack=%d, Q_prime_cqi=%d, Q_prime_ri=%d\n",Q_prime_ack, Q_prime_cqi, Q_prime_ri);
return LIBLTE_SUCCESS;
}

@ -70,3 +70,11 @@ int sequence_pdsch(sequence_t *seq, unsigned short rnti, int q, uint32_t nslot,
bzero(seq, sizeof(sequence_t)); bzero(seq, sizeof(sequence_t));
return sequence_LTE_pr(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id); return sequence_LTE_pr(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id);
} }
/**
* 36.211 5.3.1
*/
int sequence_pusch(sequence_t *seq, unsigned short rnti, uint32_t nslot, uint32_t cell_id, uint32_t len) {
bzero(seq, sizeof(sequence_t));
return sequence_LTE_pr(seq, len, (rnti<<14) + ((nslot/2)<<9) + cell_id);
}

@ -0,0 +1,358 @@
/**
*
* \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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/fec/convcoder.h"
#include "liblte/phy/fec/crc.h"
#include "liblte/phy/fec/rm_conv.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
/* Table 5.2.2.6.4-1: Basis sequence for (32, O) code */
static uint8_t M_basis_seq_pusch[32][11]={
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1 },
{1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 },
{1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1 },
{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 },
{1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1 },
{1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 },
{1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1 },
{1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1 },
{1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1 },
{1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1 },
{1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1 },
{1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1 },
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
{1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1 },
{1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1 },
{1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0 },
{1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 },
{1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
{1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 },
{1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
{1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1 },
{1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1 },
{1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0 },
{1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
{1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 },
{1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0 },
{1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0 },
{1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
static uint8_t M_basis_seq_pucch[20][13]={
{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
{1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0},
{1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1},
{1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1},
{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1},
{1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1},
{1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1},
{1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1},
{1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1},
{1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1},
{1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1},
{1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1},
{1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1},
{1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1},
{1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1},
{1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
};
int uci_cqi_init(uci_cqi_pusch_t *q) {
if (crc_init(&q->crc, LTE_CRC8, 8)) {
return LIBLTE_ERROR;
}
return LIBLTE_SUCCESS;
}
void uci_cqi_free(uci_cqi_pusch_t *q) {
}
static uint32_t Q_prime_cqi(uint32_t O, float beta, uint32_t Q_prime_ri, harq_t *harq) {
uint32_t M_sc = harq->ul_alloc.L_prb * RE_X_RB;
uint32_t K = harq->cb_segm.C1*harq->cb_segm.K1 +
harq->cb_segm.C2*harq->cb_segm.K2;
uint32_t Q_prime = 0;
if (K > 0) {
uint32_t M_sc_init = harq->nof_prb * RE_X_RB;
uint32_t L = (O<11)?0:8;
uint32_t x = (uint32_t) ceilf((float) (O+L)*M_sc_init*harq->nof_symb*beta/K);
Q_prime = MIN(x, M_sc * harq->nof_symb - Q_prime_ri);
} else {
Q_prime = 12*harq->ul_alloc.L_prb*RE_X_RB - Q_prime_ri;
}
return Q_prime;
}
/* Encode UCI CQI/PMI for payloads equal or lower to 11 bits (Sec 5.2.2.6.4)
*/
int encode_cqi_short(uci_cqi_pusch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t *q_bits, uint32_t Q)
{
if (nof_bits < MAX_CQI_LEN_PUSCH &&
q != NULL &&
data != NULL &&
q_bits != NULL)
{
for (int i=0;i<32;i++) {
q->encoded_cqi[i] = 0;
for (int n=0;n<nof_bits;n++) {
q->encoded_cqi[i] += (data[n] * M_basis_seq_pusch[i][n]);
}
}
for (int i=0;i<Q;i++) {
q_bits[i] = q->encoded_cqi[i%32]%2;
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/* Encode UCI CQI/PMI for payloads greater than 11 bits (go through CRC, conv coder and rate match)
*/
int encode_cqi_long(uci_cqi_pusch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t *q_bits, uint32_t Q)
{
convcoder_t encoder;
if (nof_bits + 8 < MAX_CQI_LEN_PUSCH &&
q != NULL &&
data != NULL &&
q_bits != NULL)
{
int poly[3] = { 0x6D, 0x4F, 0x57 };
encoder.K = 7;
encoder.R = 3;
encoder.tail_biting = true;
memcpy(encoder.poly, poly, 3 * sizeof(int));
memcpy(q->tmp_cqi, data, sizeof(uint8_t) * nof_bits);
crc_attach(&q->crc, q->tmp_cqi, nof_bits);
convcoder_encode(&encoder, q->tmp_cqi, q->encoded_cqi, nof_bits + 8);
DEBUG("CConv output: ", 0);
if (VERBOSE_ISDEBUG()) {
vec_fprint_b(stdout, q->encoded_cqi, 3 * (nof_bits + 8));
}
rm_conv_tx(q->encoded_cqi, 3 * (nof_bits + 8), q_bits, Q);
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/* Encode UCI CQI/PMI as described in 5.2.3.3 of 36.212
*/
int uci_encode_cqi_pucch(uint8_t *cqi_data, uint32_t cqi_len, uint8_t b_bits[CQI_CODED_PUCCH_B])
{
if (cqi_len <= MAX_CQI_LEN_PUCCH) {
for (uint32_t i=0;i<CQI_CODED_PUCCH_B;i++) {
uint64_t x=0;
for (uint32_t n=0;n<cqi_len;n++) {
x += cqi_data[n]*M_basis_seq_pucch[n][i];
}
b_bits[i] = (uint8_t) (x%2);
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/* Encode UCI CQI/PMI as described in 5.2.2.6 of 36.212
*/
int uci_encode_cqi_pusch(uci_cqi_pusch_t *q, uint8_t *cqi_data, uint32_t cqi_len, float beta, uint32_t Q_prime_ri,
harq_t *harq, uint8_t *q_bits)
{
uint32_t Q_prime = Q_prime_cqi(cqi_len, beta, Q_prime_ri, harq);
uint32_t Q_m = lte_mod_bits_x_symbol(harq->mcs.mod);
int ret = LIBLTE_ERROR;
if (cqi_len <= 11) {
ret = encode_cqi_short(q, cqi_data, cqi_len, q_bits, Q_prime*Q_m);
} else {
ret = encode_cqi_long(q, cqi_data, cqi_len, q_bits, Q_prime*Q_m);
}
if (ret) {
return ret;
} else {
return (int) Q_prime;
}
}
/* Inserts UCI-ACK bits into the correct positions in the g buffer before interleaving */
static int uci_ulsch_interleave_ack(uint8_t ack_coded_bits[6], uint32_t ack_q_bit_idx,
uint32_t Q_m, uint32_t H_prime_total, uint32_t N_pusch_symbs, lte_cp_t cp,
uint8_t *q_bits) {
const uint32_t ack_column_set_norm[4] = {2, 3, 8, 9};
const uint32_t ack_column_set_ext[4] = {1, 2, 6, 7};
if (H_prime_total/N_pusch_symbs >= 1+ack_q_bit_idx/4) {
uint32_t row = H_prime_total/N_pusch_symbs-1-ack_q_bit_idx/4;
uint32_t colidx = (3*ack_q_bit_idx)%4;
uint32_t col = CP_ISNORM(cp)?ack_column_set_norm[colidx]:ack_column_set_ext[colidx];
for(uint32_t k=0; k<Q_m; k++) {
q_bits[row *Q_m +
(H_prime_total/N_pusch_symbs)*col*Q_m + k] = ack_coded_bits[k];
}
return LIBLTE_SUCCESS;
} else {
fprintf(stderr, "Error interleaving UCI-ACK bit idx %d for H_prime_total=%d and N_pusch_symbs=%d\n",
ack_q_bit_idx, H_prime_total, N_pusch_symbs);
return LIBLTE_ERROR;
}
}
/* Inserts UCI-RI bits into the correct positions in the g buffer before interleaving */
static int uci_ulsch_interleave_ri(uint8_t ri_coded_bits[6], uint32_t ri_q_bit_idx,
uint32_t Q_m, uint32_t H_prime_total, uint32_t N_pusch_symbs, lte_cp_t cp,
uint8_t *q_bits) {
static uint32_t ri_column_set_norm[4] = {1, 4, 7, 10};
static uint32_t ri_column_set_ext[4] = {0, 3, 5, 8};
if (H_prime_total/N_pusch_symbs >= 1+ri_q_bit_idx/4) {
uint32_t row = H_prime_total/N_pusch_symbs-1-ri_q_bit_idx/4;
uint32_t colidx = (3*ri_q_bit_idx)%4;
uint32_t col = CP_ISNORM(cp)?ri_column_set_norm[colidx]:ri_column_set_ext[colidx];
printf("r=%d-%d\n",H_prime_total/N_pusch_symbs,1+ri_q_bit_idx/4);
for(uint32_t k=0; k<Q_m; k++) {
q_bits[row *Q_m + (H_prime_total/N_pusch_symbs)*col*Q_m + k] = 10+ri_coded_bits[k];
}
return LIBLTE_SUCCESS;
} else {
fprintf(stderr, "Error interleaving UCI-RI bit idx %d for H_prime_total=%d and N_pusch_symbs=%d\n",
ri_q_bit_idx, H_prime_total, N_pusch_symbs);
return LIBLTE_ERROR;
}
}
static uint32_t Q_prime_ri_ack(uint32_t O, uint32_t O_cqi, float beta, harq_t *harq) {
uint32_t M_sc = harq->ul_alloc.L_prb * RE_X_RB;
uint32_t K = harq->cb_segm.C1*harq->cb_segm.K1 +
harq->cb_segm.C2*harq->cb_segm.K2;
// If not carrying UL-SCH, get Q_prime according to 5.2.4.1
if (K == 0) {
if (O_cqi <= 11) {
K = O_cqi;
} else {
K = O_cqi+8;
}
}
uint32_t M_sc_init = harq->nof_prb * RE_X_RB;
uint32_t x = (uint32_t) ceilf((float) O*M_sc_init*harq->nof_symb*beta/K);
uint32_t Q_prime = MIN(x, 4*M_sc);
return Q_prime;
}
static void encode_ri_ack(uint8_t data, uint8_t q_encoded_bits[6], uint8_t Q_m) {
q_encoded_bits[0] = data;
q_encoded_bits[1] = 2;
for (uint32_t i=2;i<Q_m;i++) {
q_encoded_bits[i] = 3;
}
}
/* Encode UCI HARQ/ACK bits as described in 5.2.2.6 of 36.212
* Currently only supporting 1-bit HARQ
*/
int uci_encode_ack(uint8_t data, uint32_t O_cqi, float beta, harq_t *harq, uint32_t H_prime_total, uint8_t *q_bits)
{
uint32_t Q_m = lte_mod_bits_x_symbol(harq->mcs.mod);
uint32_t Qprime = Q_prime_ri_ack(1, O_cqi, beta, harq);
uint8_t q_encoded_bits[6];
encode_ri_ack(data, q_encoded_bits, Q_m);
for (uint32_t i=0;i<Qprime;i++) {
uci_ulsch_interleave_ack(q_encoded_bits, i, Q_m, H_prime_total, harq->nof_symb, harq->cell.cp, q_bits);
}
return (int) Qprime;
}
/* Encode UCI RI bits as described in 5.2.2.6 of 36.212
* Currently only supporting 1-bit RI
*/
int uci_encode_ri(uint8_t data, uint32_t O_cqi, float beta, harq_t *harq, uint32_t H_prime_total, uint8_t *q_bits)
{
uint32_t Q_m = lte_mod_bits_x_symbol(harq->mcs.mod);
uint32_t Qprime = Q_prime_ri_ack(1, O_cqi, beta, harq);
uint8_t q_encoded_bits[6];
encode_ri_ack(data, q_encoded_bits, Q_m);
for (uint32_t i=0;i<Qprime;i++) {
uci_ulsch_interleave_ri(q_encoded_bits, i, Q_m, H_prime_total, harq->nof_symb, harq->cell.cp, q_bits);
}
return (int) Qprime;
}

@ -99,6 +99,7 @@ ADD_TEST(pdsch_test_qam16 pdsch_test -l 50000 -m 4 -n 100)
ADD_TEST(pdsch_test_qam64 pdsch_test -l 61664 -m 6 -n 100 -r 0) ADD_TEST(pdsch_test_qam64 pdsch_test -l 61664 -m 6 -n 100 -r 0)
BuildMex(MEXNAME pdsch SOURCES pdsch_test_mex.c LIBRARIES lte_phy liblte_mex) BuildMex(MEXNAME pdsch SOURCES pdsch_test_mex.c LIBRARIES lte_phy liblte_mex)
BuildMex(MEXNAME dlsch_encode SOURCES dlsch_encode_test_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# FILE TEST # FILE TEST
@ -125,6 +126,19 @@ ADD_TEST(phich_file_test phich_file_test -c 150 -n 50 -p 2 -i ${CMAKE_CURRENT_SO
ADD_TEST(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) ADD_TEST(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat)
ADD_TEST(pdsch_file_test pdsch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) ADD_TEST(pdsch_file_test pdsch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat)
########################################################################
# PUSCH TEST
########################################################################
ADD_EXECUTABLE(pusch_test pusch_test.c)
TARGET_LINK_LIBRARIES(pusch_test lte_phy)
BuildMex(MEXNAME ulsch_encode SOURCES ulsch_encode_test_mex.c LIBRARIES lte_phy liblte_mex)
BuildMex(MEXNAME pusch_encode SOURCES pusch_encode_test_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# PRACH TEST # PRACH TEST
######################################################################## ########################################################################
@ -163,3 +177,6 @@ ADD_TEST(prach_test_multi_n16 prach_test_multi -n 16)
ADD_TEST(prach_test_multi_n8 prach_test_multi -n 8) ADD_TEST(prach_test_multi_n8 prach_test_multi -n 8)
ADD_TEST(prach_test_multi_n4 prach_test_multi -n 4) ADD_TEST(prach_test_multi_n4 prach_test_multi -n 4)
BuildMex(MEXNAME prach SOURCES prach_test_mex.c LIBRARIES lte_phy liblte_mex)

@ -82,7 +82,7 @@ int main(int argc, char **argv) {
dci_msg_type_t dci_type; dci_msg_type_t dci_type;
msg.nof_bits = len; msg.nof_bits = len;
if (dci_msg_get_type(&msg, &dci_type, nof_prb, SIRNTI, 1234)) { if (dci_msg_get_type(&msg, &dci_type, nof_prb, SIRNTI)) {
fprintf(stderr, "Can't obtain DCI message type\n"); fprintf(stderr, "Can't obtain DCI message type\n");
exit(-1); exit(-1);
} }

@ -0,0 +1,124 @@
/**
*
* \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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define OUTLEN prhs[2]
#define TRBLKIN prhs[3]
#define NOF_INPUTS 4
void help()
{
mexErrMsgTxt
("[cwout] = liblte_dlsch_encode(ue, chs, outlen, trblkin)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
sch_t dlsch;
uint8_t *trblkin;
ra_mcs_t mcs;
ra_dl_alloc_t prb_alloc;
harq_t harq_process;
uint32_t rv;
if (nrhs < NOF_INPUTS) {
help();
return;
}
if (sch_init(&dlsch)) {
mexErrMsgTxt("Error initiating DL-SCH\n");
return;
}
lte_cell_t cell;
cell.nof_prb = 100;
cell.id=1;
if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ\n");
return;
}
mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin);
if (mcs.tbs == 0) {
mexErrMsgTxt("Error trblklen is zero\n");
return;
}
if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) {
mexErrMsgTxt("Field RV not found in dlsch config\n");
return;
}
char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation");
if (!strcmp(mod_str, "QPSK")) {
mcs.mod = LTE_QPSK;
} else if (!strcmp(mod_str, "16QAM")) {
mcs.mod = LTE_QAM16;
} else if (!strcmp(mod_str, "64QAM")) {
mcs.mod = LTE_QAM64;
} else {
mexErrMsgTxt("Unknown modulation\n");
return;
}
mxFree(mod_str);
if (harq_setup_dl(&harq_process, mcs, rv, 0, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
harq_process.nof_bits = mxGetScalar(OUTLEN);
uint8_t *e_bits = vec_malloc(harq_process.nof_bits* sizeof(uint8_t));
if (!e_bits) {
return;
}
if (dlsch_encode(&dlsch, &harq_process, trblkin, e_bits)) {
mexErrMsgTxt("Error encoding TB\n");
return;
}
if (nlhs >= 1) {
mexutils_write_uint8(e_bits, &plhs[0], harq_process.nof_bits, 1);
}
sch_free(&dlsch);
free(trblkin);
free(e_bits);
return;
}

@ -255,7 +255,7 @@ int main(int argc, char **argv) {
if (crc_rem == rnti) { if (crc_rem == rnti) {
dci_msg_type_t type; dci_msg_type_t type;
if (dci_msg_get_type(&dci_msg, &type, cell.nof_prb, rnti, 1234)) { if (dci_msg_get_type(&dci_msg, &type, cell.nof_prb, rnti)) {
fprintf(stderr, "Can't get DCI message type\n"); fprintf(stderr, "Can't get DCI message type\n");
exit(-1); exit(-1);
} }

@ -58,7 +58,7 @@ dci_format_t dci_format = Format1A;
filesource_t fsrc; filesource_t fsrc;
pdcch_t pdcch; pdcch_t pdcch;
pdsch_t pdsch; pdsch_t pdsch;
pdsch_harq_t harq_process; harq_t harq_process;
cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS]; cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
regs_t regs; regs_t regs;
lte_fft_t fft; lte_fft_t fft;
@ -191,7 +191,7 @@ int base_init() {
} }
pdsch_set_rnti(&pdsch, rnti); pdsch_set_rnti(&pdsch, rnti);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
exit(-1); exit(-1);
} }
@ -217,7 +217,7 @@ void base_free() {
pdcch_free(&pdcch); pdcch_free(&pdcch);
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdsch_harq_free(&harq_process); harq_free(&harq_process);
regs_free(&regs); regs_free(&regs);
} }
@ -279,17 +279,16 @@ int main(int argc, char **argv) {
} }
if (crc_rem == rnti) { if (crc_rem == rnti) {
if (dci_msg_to_ra_dl(&dci_msg, rnti, 1234, cell, cfi, &ra_dl)) { if (dci_msg_to_ra_dl(&dci_msg, rnti, cell, cfi, &ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n"); fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
goto goout; goto goout;
} }
if (ra_dl.mcs.tbs > 0) { if (ra_dl.mcs.tbs > 0) {
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) { if (harq_setup_dl(&harq_process, ra_dl.mcs, ra_dl.rv_idx, sf_idx, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n"); fprintf(stderr, "Error configuring HARQ process\n");
goto goout; goto goout;
} }
if (pdsch_decode(&pdsch, fft_buffer, ce, chest_dl_get_noise_estimate(&chest), if (pdsch_decode(&pdsch, &harq_process, fft_buffer, ce, chest_dl_get_noise_estimate(&chest), data)) {
data, sf_idx, &harq_process, ra_dl.rv_idx)) {
fprintf(stderr, "Error decoding PDSCH\n"); fprintf(stderr, "Error decoding PDSCH\n");
goto goout; goto goout;
} else { } else {

@ -45,12 +45,14 @@ lte_cell_t cell = {
uint32_t cfi = 2; uint32_t cfi = 2;
uint32_t tbs = 0; uint32_t tbs = 0;
uint32_t nof_tbs = 0;
uint32_t subframe = 1; uint32_t subframe = 1;
lte_mod_t modulation = LTE_BPSK; lte_mod_t modulation = LTE_BPSK;
uint32_t rv_idx = 0; uint32_t rv_idx = 0;
void usage(char *prog) { void usage(char *prog) {
printf("Usage: %s [cpsrnfvmt] -l TBS \n", prog); printf("Usage: %s [Lcpsrnfvmt] -l TBS \n", prog);
printf("\t-L number of consequent TBS [Default 0]\n");
printf("\t-m modulation (1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64) [Default BPSK]\n"); printf("\t-m modulation (1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64) [Default BPSK]\n");
printf("\t-c cell id [Default %d]\n", cell.id); printf("\t-c cell id [Default %d]\n", cell.id);
printf("\t-s subframe [Default %d]\n", subframe); printf("\t-s subframe [Default %d]\n", subframe);
@ -63,7 +65,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, "lcpnfvmtsr")) != -1) { while ((opt = getopt(argc, argv, "lLcpnfvmtsr")) != -1) {
switch(opt) { switch(opt) {
case 'm': case 'm':
switch(atoi(argv[optind])) { switch(atoi(argv[optind])) {
@ -94,6 +96,9 @@ void parse_args(int argc, char **argv) {
case 'l': case 'l':
tbs = atoi(argv[optind]); tbs = atoi(argv[optind]);
break; break;
case 'L':
nof_tbs = atoi(argv[optind]);
break;
case 'p': case 'p':
cell.nof_ports = atoi(argv[optind]); cell.nof_ports = atoi(argv[optind]);
break; break;
@ -127,15 +132,19 @@ int main(int argc, char **argv) {
int ret = -1; int ret = -1;
struct timeval t[3]; struct timeval t[3];
ra_mcs_t mcs; ra_mcs_t mcs;
ra_prb_t prb_alloc; ra_dl_alloc_t prb_alloc;
pdsch_harq_t harq_process; harq_t harq_process;
uint32_t rv; uint32_t rv;
parse_args(argc,argv); parse_args(argc,argv);
bzero(&pdsch, sizeof(pdsch_t));
bzero(&harq_process, sizeof(harq_t));
bzero(ce, sizeof(cf_t*)*MAX_PORTS);
bzero(slot_symbols, sizeof(cf_t*)*MAX_PORTS);
nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB; nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB;
mcs.tbs = tbs;
mcs.mod = modulation; mcs.mod = modulation;
prb_alloc.slot[0].nof_prb = cell.nof_prb; prb_alloc.slot[0].nof_prb = cell.nof_prb;
@ -144,7 +153,7 @@ int main(int argc, char **argv) {
} }
memcpy(&prb_alloc.slot[1], &prb_alloc.slot[0], sizeof(ra_prb_slot_t)); memcpy(&prb_alloc.slot[1], &prb_alloc.slot[0], sizeof(ra_prb_slot_t));
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp); ra_dl_alloc_re(&prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
/* init memory */ /* init memory */
for (i=0;i<cell.nof_ports;i++) { for (i=0;i<cell.nof_ports;i++) {
@ -163,7 +172,7 @@ int main(int argc, char **argv) {
} }
} }
data = malloc(sizeof(uint8_t) * mcs.tbs); data = malloc(sizeof(uint8_t) * (tbs+nof_tbs));
if (!data) { if (!data) {
perror("malloc"); perror("malloc");
goto quit; goto quit;
@ -176,54 +185,59 @@ int main(int argc, char **argv) {
pdsch_set_rnti(&pdsch, 1234); pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
goto quit; goto quit;
} }
if (pdsch_harq_setup(&harq_process, mcs, &prb_alloc)) { for (mcs.tbs = tbs;mcs.tbs<=tbs+nof_tbs;mcs.tbs++) {
fprintf(stderr, "Error configuring HARQ process\n"); if (VERBOSE_ISNONE()) {
goto quit; printf("Decoding TBS: %d\r",mcs.tbs);
} }
for (i=0;i<mcs.tbs;i++) {
data[i] = rand()%2;
}
for (i=0;i<mcs.tbs;i++) { for (rv=0;rv<=rv_idx;rv++) {
data[i] = rand()%2; if (harq_setup_dl(&harq_process, mcs, rv, subframe, &prb_alloc)) {
} fprintf(stderr, "Error configuring HARQ process\n");
goto quit;
}
for (rv=0;rv<=rv_idx;rv++) { if (pdsch_encode(&pdsch, &harq_process, data, slot_symbols)) {
printf("Encoding rv_idx=%d\n",rv); fprintf(stderr, "Error encoding PDSCH\n");
if (pdsch_encode(&pdsch, data, slot_symbols, subframe, &harq_process, rv)) { goto quit;
fprintf(stderr, "Error encoding PDSCH\n"); }
goto quit;
}
/* combine outputs */ /* combine outputs */
for (i=0;i<cell.nof_ports;i++) { for (i=0;i<cell.nof_ports;i++) {
for (j=0;j<nof_re;j++) { for (j=0;j<nof_re;j++) {
if (i > 0) { if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j]; slot_symbols[0][j] += slot_symbols[i][j];
}
ce[i][j] = 1;
} }
ce[i][j] = 1;
} }
}
gettimeofday(&t[1], NULL); gettimeofday(&t[1], NULL);
int r = pdsch_decode(&pdsch, slot_symbols[0], ce, 0, data, subframe, &harq_process, rv); int r = pdsch_decode(&pdsch, &harq_process, slot_symbols[0], ce, 0, data);
gettimeofday(&t[2], NULL); gettimeofday(&t[2], NULL);
get_time_interval(t); get_time_interval(t);
if (r) { if (r) {
printf("Error decoding\n"); printf("Error decoding TBS: %d\n", mcs.tbs);
ret = -1; ret = -1;
goto quit; goto quit;
} else { } else {
printf("DECODED OK in %d:%d (%.2f Mbps)\n", (int) t[0].tv_sec, (int) t[0].tv_usec, (float) mcs.tbs/t[0].tv_usec); if (nof_tbs == 0) {
printf("DECODED OK in %d:%d (%.2f Mbps)\n", (int) t[0].tv_sec, (int) t[0].tv_usec, (float) mcs.tbs/t[0].tv_usec);
}
}
} }
} }
ret = 0; ret = 0;
quit: quit:
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdsch_harq_free(&harq_process); harq_free(&harq_process);
for (i=0;i<cell.nof_ports;i++) { for (i=0;i<cell.nof_ports;i++) {
if (ce[i]) { if (ce[i]) {

@ -58,8 +58,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
cf_t *input_fft, *input_signal; cf_t *input_fft, *input_signal;
int nof_re; int nof_re;
ra_mcs_t mcs; ra_mcs_t mcs;
ra_prb_t prb_alloc; ra_dl_alloc_t dl_alloc;
pdsch_harq_t harq_process; harq_t harq_process;
uint32_t rv; uint32_t rv;
uint32_t rnti32; uint32_t rnti32;
@ -93,7 +93,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
} }
pdsch_set_rnti(&pdsch, (uint16_t) (rnti32 & 0xffff)); pdsch_set_rnti(&pdsch, (uint16_t) (rnti32 & 0xffff));
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ process\n"); mexErrMsgTxt("Error initiating HARQ process\n");
return; return;
} }
@ -146,23 +146,23 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
} }
// Only localized PRB supported // Only localized PRB supported
prb_alloc.slot[0].nof_prb = mexutils_read_f(p, &prbset); dl_alloc.slot[0].nof_prb = mexutils_read_f(p, &prbset);
for (i=0;i<cell.nof_prb;i++) { for (i=0;i<cell.nof_prb;i++) {
prb_alloc.slot[0].prb_idx[i] = false; dl_alloc.slot[0].prb_idx[i] = false;
for (int j=0;j<prb_alloc.slot[0].nof_prb && !prb_alloc.slot[0].prb_idx[i];j++) { for (int j=0;j<dl_alloc.slot[0].nof_prb && !dl_alloc.slot[0].prb_idx[i];j++) {
if ((int) prbset[j] == i) { if ((int) prbset[j] == i) {
prb_alloc.slot[0].prb_idx[i] = true; dl_alloc.slot[0].prb_idx[i] = true;
} }
} }
} }
memcpy(&prb_alloc.slot[1], &prb_alloc.slot[0], sizeof(ra_prb_slot_t)); memcpy(&dl_alloc.slot[1], &dl_alloc.slot[0], sizeof(ra_prb_slot_t));
free(prbset); free(prbset);
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp); ra_dl_alloc_re(&dl_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
if (pdsch_harq_setup(&harq_process, mcs, &prb_alloc)) { if (harq_setup_dl(&harq_process, mcs, rv, sf_idx, &dl_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n"); mexErrMsgTxt("Error configuring HARQ process\n");
return; return;
} }
@ -209,7 +209,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
return; return;
} }
int r = pdsch_decode(&pdsch, input_fft, ce, noise_power, data, sf_idx, &harq_process, rv); int r = pdsch_decode(&pdsch, &harq_process, input_fft, ce, noise_power, data);
if (nlhs >= 1) { if (nlhs >= 1) {
@ -219,13 +219,13 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
mexutils_write_uint8(data, &plhs[1], mcs.tbs, 1); mexutils_write_uint8(data, &plhs[1], mcs.tbs, 1);
} }
if (nlhs >= 3) { if (nlhs >= 3) {
mexutils_write_cf(pdsch.pdsch_symbols[0], &plhs[2], harq_process.prb_alloc.re_sf[sf_idx], 1); mexutils_write_cf(pdsch.pdsch_symbols[0], &plhs[2], harq_process.dl_alloc.re_sf[sf_idx], 1);
} }
if (nlhs >= 4) { if (nlhs >= 4) {
mexutils_write_cf(pdsch.pdsch_d, &plhs[3], harq_process.prb_alloc.re_sf[sf_idx], 1); mexutils_write_cf(pdsch.pdsch_d, &plhs[3], harq_process.dl_alloc.re_sf[sf_idx], 1);
} }
if (nlhs >= 5) { if (nlhs >= 5) {
mexutils_write_f(pdsch.pdsch_e, &plhs[4], harq_process.prb_alloc.re_sf[sf_idx] * lte_mod_bits_x_symbol(mcs.mod), 1); mexutils_write_f(pdsch.pdsch_e, &plhs[4], harq_process.dl_alloc.re_sf[sf_idx] * lte_mod_bits_x_symbol(mcs.mod), 1);
} }
chest_dl_free(&chest); chest_dl_free(&chest);

@ -77,6 +77,7 @@ int main(int argc, char **argv) {
prach_gen(p, prach_gen(p,
seq_index, seq_index,
frequency_offset, frequency_offset,
0.2,
preamble); preamble);
uint32_t prach_len = p->N_seq; uint32_t prach_len = p->N_seq;
@ -91,6 +92,6 @@ int main(int argc, char **argv) {
prach_free(p); prach_free(p);
free(p); free(p);
printf("Done\n"); printf("Done\n");
exit(0); exit(0);
} }

@ -0,0 +1,116 @@
/**
*
* \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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the channel estimator
*/
#define UECFG prhs[0]
#define PRACHCFG prhs[1]
#define NOF_INPUTS 2
void help()
{
mexErrMsgTxt
("waveform = liblte_prach(ueConfig, prachConfig)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (nrhs != NOF_INPUTS) {
help();
return;
}
uint32_t n_ul_rb = 0;
if (mexutils_read_uint32_struct(UECFG, "NULRB", &n_ul_rb)) {
mexErrMsgTxt("Field NULRB not found in UE config\n");
return;
}
int r = lte_symbol_sz(n_ul_rb);
if (r < 0) {
mexErrMsgTxt("Invalid NULRB\n");
return;
}
uint32_t N_ifft_ul = (uint32_t) r;
uint32_t sf_idx = 0;
mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx);
uint32_t nframe = 0;
mexutils_read_uint32_struct(UECFG, "NFrame", &nframe);
uint32_t preamble_format = 0;
mexutils_read_uint32_struct(PRACHCFG, "Format", &preamble_format);
uint32_t root_seq_idx = 0;
mexutils_read_uint32_struct(PRACHCFG, "SeqIdx", &root_seq_idx);
uint32_t seq_idx = 0;
mexutils_read_uint32_struct(PRACHCFG, "PreambleIdx", &seq_idx);
uint32_t zero_corr_zone = 0;
mexutils_read_uint32_struct(PRACHCFG, "CyclicShiftIdx", &zero_corr_zone);
uint32_t high_speed_flag = 0;
mexutils_read_uint32_struct(PRACHCFG, "HighSpeed", &high_speed_flag);
uint32_t timing_offset = 0;
mexutils_read_uint32_struct(PRACHCFG, "TimingOffset", &timing_offset);
uint32_t frequency_offset = 0;
mexutils_read_uint32_struct(PRACHCFG, "FreqOffset", &frequency_offset);
prach_t prach;
if (prach_init(&prach, N_ifft_ul, preamble_format, root_seq_idx, high_speed_flag, zero_corr_zone)) {
mexErrMsgTxt("Error initiating PRACH\n");
return;
}
uint32_t nof_samples = lte_sampling_freq_hz(n_ul_rb) * 0.003;
cf_t *signal = vec_malloc(sizeof(cf_t) * nof_samples);
if (!signal) {
mexErrMsgTxt("malloc");
return;
}
bzero(signal, sizeof(cf_t) * nof_samples);
if (prach_gen(&prach, seq_idx, frequency_offset, 0.2, signal)) {
mexErrMsgTxt("Error generating PRACH\n");
return;
}
if (nlhs >= 0) {
mexutils_write_cf(signal, &plhs[0], nof_samples, 1);
}
free(signal);
prach_free(&prach);
return;
}

@ -84,6 +84,7 @@ int main(int argc, char **argv) {
prach_gen(p, prach_gen(p,
seq_index, seq_index,
frequency_offset, frequency_offset,
0.2,
preamble); preamble);
for(int i=0;i<p->N_cp+p->N_seq;i++) for(int i=0;i<p->N_cp+p->N_seq;i++)

@ -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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the channel estimator
*/
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define TRBLKIN prhs[2]
#define CQI prhs[3]
#define RI prhs[4]
#define ACK prhs[5]
#define NOF_INPUTS 6
void help()
{
mexErrMsgTxt
("sym=liblte_pusch_encode(ue, chs, trblkin, cqi, ri, ack)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (nrhs != NOF_INPUTS) {
help();
return;
}
lte_cell_t cell;
bzero(&cell, sizeof(lte_cell_t));
cell.nof_ports = 1;
if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) {
mexErrMsgTxt("Field NCellID not found in UE config\n");
return;
}
if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) {
mexErrMsgTxt("Field NULRB not found in UE config\n");
return;
}
pusch_t pusch;
if (pusch_init(&pusch, cell)) {
mexErrMsgTxt("Error initiating PUSCH\n");
return;
}
uint32_t rnti32=0;
if (mexutils_read_uint32_struct(UECFG, "RNTI", &rnti32)) {
mexErrMsgTxt("Field RNTI not found in pusch config\n");
return;
}
pusch_set_rnti(&pusch, (uint16_t) (rnti32 & 0xffff));
uint32_t sf_idx=0;
if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) {
mexErrMsgTxt("Field NSubframe not found in UE config\n");
return;
}
ra_mcs_t mcs;
char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation");
if (!strcmp(mod_str, "QPSK")) {
mcs.mod = LTE_QPSK;
} else if (!strcmp(mod_str, "16QAM")) {
mcs.mod = LTE_QAM16;
} else if (!strcmp(mod_str, "64QAM")) {
mcs.mod = LTE_QAM64;
} else {
mexErrMsgTxt("Unknown modulation\n");
return;
}
mxFree(mod_str);
float *prbset = NULL;
mxArray *p;
p = mxGetField(PUSCHCFG, 0, "PRBSet");
if (!p) {
mexErrMsgTxt("Error field PRBSet not found\n");
return;
}
ra_ul_alloc_t prb_alloc;
bzero(&prb_alloc, sizeof(ra_ul_alloc_t));
prb_alloc.L_prb = mexutils_read_f(p, &prbset);
prb_alloc.n_prb[0] = prbset[0];
prb_alloc.n_prb[1] = prbset[0];
free(prbset);
mexPrintf("L_prb: %d, n_prb: %d\n", prb_alloc.L_prb, prb_alloc.n_prb[2*sf_idx]);
uint8_t *trblkin = NULL;
mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin);
harq_t harq_process;
if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ process\n");
return;
}
if (harq_setup_ul(&harq_process, mcs, 0, sf_idx, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
uint32_t nof_re = RE_X_RB*cell.nof_prb*2*CP_NSYMB(cell.cp);
cf_t *sf_symbols = vec_malloc(sizeof(cf_t) * nof_re);
if (!sf_symbols) {
mexErrMsgTxt("malloc");
return;
}
bzero(sf_symbols, sizeof(cf_t) * nof_re);
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
uci_data.uci_cqi_len = mexutils_read_uint8(CQI, &uci_data.uci_cqi);
uint8_t *tmp;
uci_data.uci_ri_len = mexutils_read_uint8(RI, &tmp);
if (uci_data.uci_ri_len > 0) {
uci_data.uci_ri = *tmp;
}
free(tmp);
uci_data.uci_ack_len = mexutils_read_uint8(ACK, &tmp);
if (uci_data.uci_ack_len > 0) {
uci_data.uci_ack = *tmp;
}
free(tmp);
if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &uci_data.beta_cqi)) {
uci_data.beta_cqi = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &uci_data.beta_ri)) {
uci_data.beta_ri = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &uci_data.beta_ack)) {
uci_data.beta_ack = 2.0;
}
mexPrintf("Beta_CQI: %.1f, Beta_ACK: %.1f, Beta_RI: %.1f\n",
uci_data.beta_cqi, uci_data.beta_ack, uci_data.beta_ri);
mexPrintf("TRBL_len: %d, CQI_len: %d, ACK_len: %d (%d), RI_len: %d (%d)\n", mcs.tbs,
uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ack, uci_data.uci_ri_len, uci_data.uci_ri);
mexPrintf("NofRE: %d, NofBits: %d, TBS: %d\n", harq_process.nof_re, harq_process.nof_bits, harq_process.mcs.tbs);
int r = pusch_uci_encode(&pusch, &harq_process, trblkin, uci_data, sf_symbols);
if (r < 0) {
mexErrMsgTxt("Error encoding PUSCH\n");
return;
}
uint32_t rv=0;
if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) {
mexErrMsgTxt("Field RV not found in pdsch config\n");
return;
}
if (rv > 0) {
if (harq_setup_ul(&harq_process, mcs, rv, sf_idx, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
r = pusch_uci_encode(&pusch, &harq_process, trblkin, uci_data, sf_symbols);
if (r < 0) {
mexErrMsgTxt("Error encoding PUSCH\n");
return;
}
}
cf_t *scfdma = vec_malloc(sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
bzero(scfdma, sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
lte_fft_t fft;
lte_ifft_init(&fft, CPNORM, cell.nof_prb);
lte_fft_set_normalize(&fft, true);
lte_fft_set_freq_shift(&fft, 0.5);
lte_ifft_run_sf(&fft, sf_symbols, scfdma);
// Matlab toolbox expects further normalization
vec_sc_prod_cfc(scfdma, 1.0/sqrtf(lte_symbol_sz(cell.nof_prb)), scfdma, SF_LEN_PRB(cell.nof_prb));
if (nlhs >= 1) {
mexutils_write_cf(scfdma, &plhs[0], SF_LEN_PRB(cell.nof_prb), 1);
}
if (nlhs >= 2) {
mexutils_write_cf(sf_symbols, &plhs[1], nof_re, 1);
}
if (nlhs >= 3) {
mexutils_write_cf(pusch.pusch_z, &plhs[2], harq_process.nof_re, 1);
}
pusch_free(&pusch);
free(trblkin);
free(uci_data.uci_cqi);
free(sf_symbols);
free(scfdma);
return;
}

@ -0,0 +1,274 @@
/**
*
* \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 <sys/time.h>
#include "liblte/phy/phy.h"
lte_cell_t cell = {
6, // nof_prb
1, // nof_ports
0, // cell_id
CPNORM, // cyclic prefix
R_1_6, // PHICH resources
PHICH_NORM // PHICH length
};
uint32_t cfi = 2;
uint32_t tbs = 0;
uint32_t subframe = 1;
lte_mod_t modulation = LTE_QPSK;
uint32_t rv_idx = 0;
uint32_t L_prb = 2;
uint32_t n_prb = 0;
int freq_hop = -1;
int riv = -1;
void usage(char *prog) {
printf("Usage: %s [csrnfvmtLNF] -l TBS \n", prog);
printf("\t-m modulation (1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64) [Default BPSK]\n");
printf("\t-c cell id [Default %d]\n", cell.id);
printf("\t-s subframe [Default %d]\n", subframe);
printf("\t-L L_prb [Default %d]\n", L_prb);
printf("\t-N n_prb [Default %d]\n", n_prb);
printf("\t-F frequency hopping [Default %d]\n", freq_hop);
printf("\t-R RIV [Default %d]\n", riv);
printf("\t-r rv_idx [Default %d]\n", rv_idx);
printf("\t-f cfi [Default %d]\n", cfi);
printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb);
printf("\t-v [set verbose to debug, default none]\n");
}
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "lcnfvmtsrLNFR")) != -1) {
switch(opt) {
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, 3: QAM16, 4: QAM64)\n", atoi(argv[optind]));
break;
}
break;
case 's':
subframe = atoi(argv[optind]);
break;
case 'L':
L_prb = atoi(argv[optind]);
break;
case 'N':
n_prb = atoi(argv[optind]);
break;
case 'R':
riv = atoi(argv[optind]);
break;
case 'F':
freq_hop = atoi(argv[optind]);
break;
case 'r':
rv_idx = atoi(argv[optind]);
break;
case 'l':
tbs = atoi(argv[optind]);
break;
case 'n':
cell.nof_prb = atoi(argv[optind]);
break;
case 'c':
cell.id = atoi(argv[optind]);
break;
case 'v':
verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
int main(int argc, char **argv) {
pusch_t pusch;
uint8_t *data = NULL;
cf_t *sf_symbols = NULL;
int ret = -1;
struct timeval t[3];
ra_mcs_t mcs;
ra_ul_alloc_t prb_alloc;
harq_t harq_process;
parse_args(argc,argv);
mcs.tbs = tbs;
mcs.mod = modulation;
bzero(&prb_alloc, sizeof(ra_ul_alloc_t));
if (pusch_init(&pusch, cell)) {
fprintf(stderr, "Error creating PDSCH object\n");
goto quit;
}
pusch_set_rnti(&pusch, 1234);
if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n");
goto quit;
}
printf("Encoding rv_idx=%d\n",rv_idx);
uint8_t tmp[20];
for (uint32_t i=0;i<20;i++) {
tmp[i] = 1;
}
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
uci_data.beta_cqi = 2.0;
uci_data.beta_ri = 2.0;
uci_data.beta_ack = 2.0;
uci_data.uci_cqi_len = 0;
uci_data.uci_ri_len = 0;
uci_data.uci_ack_len = 0;
uci_data.uci_cqi = tmp;
uci_data.uci_ri = 1;
uci_data.uci_ack = 1;
ra_pusch_t pusch_dci;
pusch_dci.freq_hop_fl = freq_hop;
if (riv < 0) {
pusch_dci.type2_alloc.L_crb = L_prb;
pusch_dci.type2_alloc.RB_start = n_prb;
} else {
ra_type2_from_riv((uint32_t) riv, &pusch_dci.type2_alloc.L_crb, &pusch_dci.type2_alloc.RB_start, cell.nof_prb, cell.nof_prb);
}
ra_ul_alloc(&prb_alloc, &pusch_dci, 0, cell.nof_prb);
if (harq_setup_ul(&harq_process, mcs, 0, subframe, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto quit;
}
pusch_hopping_cfg_t ul_hopping;
ul_hopping.n_sb = 1;
ul_hopping.hopping_offset = 0;
ul_hopping.hop_mode = hop_mode_inter_sf;
ul_hopping.current_tx_nb = 0;
pusch_set_hopping_cfg(&pusch, &ul_hopping);
uint32_t nof_re = RE_X_RB*cell.nof_prb*2*CP_NSYMB(cell.cp);
sf_symbols = vec_malloc(sizeof(cf_t) * nof_re);
if (!sf_symbols) {
perror("malloc");
goto quit;
}
data = malloc(sizeof(uint8_t) * mcs.tbs);
if (!data) {
perror("malloc");
goto quit;
}
for (uint32_t i=0;i<mcs.tbs;i++) {
data[i] = 1;
}
if (pusch_uci_encode(&pusch, &harq_process, data, uci_data, sf_symbols)) {
fprintf(stderr, "Error encoding TB\n");
exit(-1);
}
if (rv_idx > 0) {
if (harq_setup_ul(&harq_process, mcs, rv_idx, subframe, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto quit;
}
if (pusch_uci_encode(&pusch, &harq_process, data, uci_data, sf_symbols)) {
fprintf(stderr, "Error encoding TB\n");
exit(-1);
}
}
cf_t *scfdma = vec_malloc(sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
bzero(scfdma, sizeof(cf_t) * SF_LEN_PRB(cell.nof_prb));
lte_fft_t fft;
lte_ifft_init(&fft, CPNORM, cell.nof_prb);
lte_fft_set_freq_shift(&fft, 0.5);
lte_ifft_run_sf(&fft, sf_symbols, scfdma);
gettimeofday(&t[1], NULL);
//int r = pusch_decode(&pusch, slot_symbols[0], ce, 0, data, subframe, &harq_process, rv);
int r = 0;
gettimeofday(&t[2], NULL);
get_time_interval(t);
if (r) {
printf("Error decoding\n");
ret = -1;
goto quit;
} else {
printf("DECODED OK in %d:%d (%.2f Mbps)\n", (int) t[0].tv_sec, (int) t[0].tv_usec, (float) mcs.tbs/t[0].tv_usec);
}
ret = 0;
quit:
pusch_free(&pusch);
harq_free(&harq_process);
if (sf_symbols) {
free(sf_symbols);
}
if (data) {
free(data);
}
if (ret) {
printf("Error\n");
} else {
printf("Ok\n");
}
exit(ret);
}

@ -0,0 +1,184 @@
/**
*
* \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 <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define TRBLKIN prhs[2]
#define CQI prhs[3]
#define RI prhs[4]
#define ACK prhs[5]
#define NOF_INPUTS 6
void help()
{
mexErrMsgTxt
("[cwout] = liblte_pusch_encode(ue, chs, trblkin, cqi, ri, ack)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
sch_t ulsch;
uint8_t *trblkin;
ra_mcs_t mcs;
ra_ul_alloc_t prb_alloc;
harq_t harq_process;
uint32_t rv;
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
if (nrhs < NOF_INPUTS) {
help();
return;
}
if (sch_init(&ulsch)) {
mexErrMsgTxt("Error initiating ULSCH\n");
return;
}
lte_cell_t cell;
cell.nof_prb = 100;
cell.id=1;
cell.cp=CPNORM;
if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ\n");
return;
}
mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin);
uci_data.uci_cqi_len = mexutils_read_uint8(CQI, &uci_data.uci_cqi);
uint8_t *tmp;
uci_data.uci_ri_len = mexutils_read_uint8(RI, &tmp);
if (uci_data.uci_ri_len > 0) {
uci_data.uci_ri = *tmp;
}
free(tmp);
uci_data.uci_ack_len = mexutils_read_uint8(ACK, &tmp);
if (uci_data.uci_ack_len > 0) {
uci_data.uci_ack = *tmp;
}
free(tmp);
mexPrintf("TRBL_len: %d, CQI_len: %d, ACK_len: %d, RI_len: %d\n", mcs.tbs,
uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ri_len);
if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) {
mexErrMsgTxt("Field RV not found in pdsch config\n");
return;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &uci_data.beta_cqi)) {
uci_data.beta_cqi = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &uci_data.beta_ri)) {
uci_data.beta_ri = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &uci_data.beta_ack)) {
uci_data.beta_ack = 2.0;
}
mexPrintf("Beta_CQI: %.1f, Beta_ACK: %.1f, Beta_RI: %.1f\n",
uci_data.beta_cqi, uci_data.beta_ack, uci_data.beta_ri);
char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation");
if (!strcmp(mod_str, "QPSK")) {
mcs.mod = LTE_QPSK;
} else if (!strcmp(mod_str, "16QAM")) {
mcs.mod = LTE_QAM16;
} else if (!strcmp(mod_str, "64QAM")) {
mcs.mod = LTE_QAM64;
} else {
mexErrMsgTxt("Unknown modulation\n");
return;
}
mxFree(mod_str);
float *prbset;
mxArray *p;
p = mxGetField(PUSCHCFG, 0, "PRBSet");
if (!p) {
mexErrMsgTxt("Error field PRBSet not found\n");
return;
}
prb_alloc.L_prb = mexutils_read_f(p, &prbset);
prb_alloc.n_prb[0] = prbset[0];
prb_alloc.n_prb[1] = prbset[0];
free(prbset);
mexPrintf("Q_m: %d, NPRB: %d, RV: %d\n", lte_mod_bits_x_symbol(mcs.mod), prb_alloc.L_prb, rv);
if (harq_setup_ul(&harq_process, mcs, 0, 0, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
uint8_t *q_bits = vec_malloc(harq_process.nof_bits * sizeof(uint8_t));
if (!q_bits) {
return;
}
uint8_t *g_bits = vec_malloc(harq_process.nof_bits * sizeof(uint8_t));
if (!g_bits) {
return;
}
if (ulsch_uci_encode(&ulsch, &harq_process, trblkin, uci_data, g_bits, q_bits))
{
mexErrMsgTxt("Error encoding TB\n");
return;
}
if (rv > 0) {
if (harq_setup_ul(&harq_process, mcs, rv, 0, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
if (ulsch_uci_encode(&ulsch, &harq_process, trblkin, uci_data, g_bits, q_bits)) {
mexErrMsgTxt("Error encoding TB\n");
return;
}
}
if (nlhs >= 1) {
mexutils_write_uint8(q_bits, &plhs[0], harq_process.nof_bits, 1);
}
sch_free(&ulsch);
harq_free(&harq_process);
free(trblkin);
free(g_bits);
free(q_bits);
free(uci_data.uci_cqi);
return;
}

@ -37,14 +37,7 @@
/*************** STATIC FUNCTIONS ***********************/ /*************** STATIC FUNCTIONS ***********************/
cf_t interp_linear_onesample(cf_t input0, cf_t input1) { cf_t interp_linear_onesample(cf_t input0, cf_t input1) {
float mag0=0, mag1=0, arg0=0, arg1=0, mag=0, arg=0; return 2*input1-input0;
mag0 = cabsf(input0);
mag1 = cabsf(input1);
arg0 = cargf(input0);
arg1 = cargf(input1);
mag = 2*mag1 -mag0;
arg = 2*arg1-arg0;
return mag * cexpf(I * arg);
} }
cf_t interp_linear_onesample_cabs(cf_t input0, cf_t input1) { cf_t interp_linear_onesample_cabs(cf_t input0, cf_t input1) {

@ -73,6 +73,23 @@ void scrambling_b_offset(sequence_t *s, uint8_t *data, int offset, int len) {
} }
} }
/* As defined in 36.211 5.3.1 */
void scrambling_b_offset_pusch(sequence_t *s, uint8_t *data, int offset, int len) {
int i;
assert (len + offset <= s->len);
for (i = 0; i < len; i++) {
if (data[i] == 3) {
data[i] = 1;
} else if (data[i] == 2) {
if (i > 1) {
data[i] = data[i-1];
}
} else {
data[i] = (data[i] + s->c[i + offset]) % 2;
}
}
}
/** High-level API */ /** High-level API */
int compute_sequences(scrambling_hl* h) { int compute_sequences(scrambling_hl* h) {

@ -149,15 +149,7 @@ int sss_synch_N_id_1(sss_synch_t *q, uint32_t m0, uint32_t m1) {
} else { } else {
N_id_1 = q->N_id_1_table[m1][m0 - 1]; N_id_1 = q->N_id_1_table[m1][m0 - 1];
} }
if (N_id_1 == 0) { return N_id_1;
if (m0 == 0 && m1 == 1) {
return N_id_1;
} else {
return LIBLTE_ERROR;
}
} else {
return N_id_1;
}
} }
/** High-level API */ /** High-level API */

@ -397,4 +397,3 @@ void do_plots_sss(float *corr_m0, float *corr_m1) {
} }
#endif #endif

@ -39,13 +39,13 @@
float tmp_pss_corr[32*10000]; float tmp_pss_corr[32*10000];
float tmp_sss_corr[31*10000]; float tmp_sss_corr[31*10000];
int ue_cell_search_init(ue_cell_search_t * q, int (recv_callback)(void*, void*, uint32_t), void *stream_handler) int ue_cell_search_init(ue_cell_search_t * q, int (recv_callback)(void*, void*, uint32_t,timestamp_t*), void *stream_handler)
{ {
return ue_cell_search_init_max(q, CS_DEFAULT_MAXFRAMES_TOTAL, recv_callback, stream_handler); return ue_cell_search_init_max(q, CS_DEFAULT_MAXFRAMES_TOTAL, recv_callback, stream_handler);
} }
int ue_cell_search_init_max(ue_cell_search_t * q, uint32_t max_frames, int ue_cell_search_init_max(ue_cell_search_t * q, uint32_t max_frames,
int (recv_callback)(void*, void*, uint32_t), void *stream_handler) int (recv_callback)(void*, void*, uint32_t,timestamp_t*), void *stream_handler)
{ {
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
@ -270,9 +270,6 @@ int ue_cell_search_scan_N_id_2(ue_cell_search_t * q, uint32_t N_id_2, ue_cell_se
ret = 1; // A cell has been found. ret = 1; // A cell has been found.
if (found_cell) { if (found_cell) {
get_cell(q, nof_detected_frames, found_cell); get_cell(q, nof_detected_frames, found_cell);
printf("Found CELL PHYID: %d, CP: %s, PSR: %.1f, Absolute Peak: %.1f dBm, Reliability: %.0f \%\n",
found_cell->cell_id, lte_cp_string(found_cell->cp),
found_cell->psr, 10*log10(found_cell->peak*1000), 100*found_cell->mode);
} }
} else { } else {
ret = 0; // A cell was not found. ret = 0; // A cell was not found.

@ -31,7 +31,7 @@
#include <math.h> #include <math.h>
#define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb) #define CURRENT_FFTSIZE lte_symbol_sz(q->cell.nof_prb)
#define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE, q->cell.cp) #define CURRENT_SFLEN SF_LEN(CURRENT_FFTSIZE)
#define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) #define CURRENT_SLOTLEN_RE SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp)
#define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp) #define CURRENT_SFLEN_RE SF_LEN_RE(q->cell.nof_prb, q->cell.cp)
@ -39,8 +39,7 @@
#define MAX_CANDIDATES 64 #define MAX_CANDIDATES 64
int ue_dl_init(ue_dl_t *q, int ue_dl_init(ue_dl_t *q,
lte_cell_t cell, lte_cell_t cell)
uint16_t user_rnti)
{ {
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
@ -52,7 +51,6 @@ int ue_dl_init(ue_dl_t *q,
bzero(q, sizeof(ue_dl_t)); bzero(q, sizeof(ue_dl_t));
q->cell = cell; q->cell = cell;
q->user_rnti = user_rnti;
q->pkt_errors = 0; q->pkt_errors = 0;
q->pkts_total = 0; q->pkts_total = 0;
@ -68,10 +66,6 @@ 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;
@ -87,7 +81,7 @@ int ue_dl_init(ue_dl_t *q,
goto clean_exit; goto clean_exit;
} }
for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) { for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) {
if (pdsch_harq_init(&q->harq_process[i], &q->pdsch)) { if (harq_init(&q->harq_process[i], q->cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
goto clean_exit; goto clean_exit;
} }
@ -123,12 +117,11 @@ void ue_dl_free(ue_dl_t *q) {
lte_fft_free(&q->fft); lte_fft_free(&q->fft);
chest_dl_free(&q->chest); chest_dl_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);
for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) { for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) {
pdsch_harq_free(&q->harq_process[i]); harq_free(&q->harq_process[i]);
} }
if (q->sf_symbols) { if (q->sf_symbols) {
free(q->sf_symbols); free(q->sf_symbols);
@ -144,17 +137,19 @@ void ue_dl_free(ue_dl_t *q) {
} }
} }
/* Precalculate the PDSCH scramble sequences for a given RNTI. This function takes a while
* to execute, so shall be called once the final C-RNTI has been allocated for the session.
* For the connection procedure, use pusch_encode_rnti() or pusch_decode_rnti() functions
*/
void ue_dl_set_rnti(ue_dl_t *q, uint16_t rnti) { void ue_dl_set_rnti(ue_dl_t *q, uint16_t rnti) {
q->current_rnti = rnti; q->current_rnti = rnti;
pdsch_set_rnti(&q->pdsch, rnti); pdsch_set_rnti(&q->pdsch, rnti);
} }
void ue_dl_reset(ue_dl_t *q) { void ue_dl_reset(ue_dl_t *q) {
pdsch_harq_reset(&q->harq_process[0]); harq_reset(&q->harq_process[0]);
} }
LIBLTE_API float mean_exec_time=0;
dci_format_t ue_formats[] = {Format1,Format1A}; // Format1B should go here also dci_format_t ue_formats[] = {Format1,Format1A}; // Format1B should go here also
const uint32_t nof_ue_formats = 2; const uint32_t nof_ue_formats = 2;
@ -169,69 +164,133 @@ const uint32_t nof_common_formats = 2;
* - PDSCH decoding: Decode TB scrambling with RNTI given by ue_dl_set_rnti() * - PDSCH decoding: Decode TB scrambling with RNTI given by ue_dl_set_rnti()
*/ */
int ue_dl_decode(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx) { int ue_dl_decode(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx) {
return ue_dl_decode_sib(q, input, data, sf_idx, 0); return ue_dl_decode_rnti_rv(q, input, data, sf_idx, q->current_rnti, 0);
} }
int ue_dl_decode_sib(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, uint32_t rvidx) int ue_dl_decode_rnti(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, uint16_t rnti) {
{ return ue_dl_decode_rnti_rv(q, input, data, sf_idx, rnti, 0);
uint32_t cfi, i; }
int ue_dl_decode_fft_estimate(ue_dl_t *q, cf_t *input, uint32_t sf_idx, uint32_t *cfi) {
float cfi_corr; float cfi_corr;
ra_pdsch_t ra_dl; if (input && q && cfi && sf_idx < NSUBFRAMES_X_FRAME) {
dci_location_t locations[MAX_CANDIDATES];
dci_msg_t dci_msg;
uint32_t nof_locations;
uint16_t crc_rem;
int ret = LIBLTE_ERROR;
struct timeval t[3];
uint32_t nof_formats;
dci_format_t *formats = NULL;
/* Run FFT for all subframe data */ /* Run FFT for all subframe data */
lte_fft_run_sf(&q->fft, input, q->sf_symbols); lte_fft_run_sf(&q->fft, input, q->sf_symbols);
gettimeofday(&t[1], NULL); /* Get channel estimates for each port */
chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, sf_idx);
/* Get channel estimates for each port */ /* First decode PCFICH and obtain CFI */
chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, sf_idx); if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce,
chest_dl_get_noise_estimate(&q->chest), sf_idx, cfi, &cfi_corr)<0) {
fprintf(stderr, "Error decoding PCFICH\n");
return LIBLTE_ERROR;
}
/* First decode PCFICH and obtain CFI */ INFO("Decoded CFI=%d with correlation %.2f\n", *cfi, cfi_corr);
if (pcfich_decode(&q->pcfich, q->sf_symbols, q->ce,
chest_dl_get_noise_estimate(&q->chest), sf_idx, &cfi, &cfi_corr)<0) { if (regs_set_cfi(&q->regs, *cfi)) {
fprintf(stderr, "Error decoding PCFICH\n"); fprintf(stderr, "Error setting CFI\n");
return LIBLTE_ERROR; return LIBLTE_ERROR;
}
/* Extract all PDCCH symbols and get LLRs */
if (pdcch_extract_llr(&q->pdcch, q->sf_symbols, q->ce, chest_dl_get_noise_estimate(&q->chest), sf_idx, *cfi)) {
fprintf(stderr, "Error extracting LLRs\n");
return LIBLTE_ERROR;
}
return LIBLTE_SUCCESS;
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
} }
}
INFO("Decoded CFI=%d with correlation %.2f\n", cfi, cfi_corr); int ue_dl_decode_rnti_rv_packet(ue_dl_t *q, dci_msg_t *dci_msg, uint8_t *data,
uint32_t cfi, uint32_t sf_idx, uint16_t rnti, uint32_t rvidx)
{
int ret = LIBLTE_ERROR;
q->nof_pdcch_detected++;
if (dci_msg_to_ra_dl(dci_msg, rnti, q->cell, cfi, &q->ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
return LIBLTE_ERROR;
}
if (regs_set_cfi(&q->regs, cfi)) { if (rnti != SIRNTI) {
fprintf(stderr, "Error setting CFI\n"); rvidx = q->ra_dl.rv_idx;
}
if (harq_setup_dl(&q->harq_process[0], q->ra_dl.mcs, rvidx, sf_idx, &q->ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
if (q->harq_process[0].mcs.mod > 0 && q->harq_process[0].mcs.tbs >= 0) {
ret = pdsch_decode_rnti(&q->pdsch, &q->harq_process[0], q->sf_symbols,
q->ce, chest_dl_get_noise_estimate(&q->chest),
rnti, data);
if (ret == LIBLTE_ERROR) {
q->pkt_errors++;
} else if (ret == LIBLTE_ERROR_INVALID_INPUTS) {
fprintf(stderr, "Error calling pdsch_decode()\n");
} else if (ret == LIBLTE_SUCCESS) {
if (VERBOSE_ISINFO()) {
INFO("Decoded Message: ", 0);
vec_fprint_hex(stdout, data, q->ra_dl.mcs.tbs);
}
}
q->pkts_total++;
}
return ret;
}
int ue_dl_find_ul_dci(ue_dl_t *q, dci_msg_t *dci_msg, uint32_t cfi, uint32_t sf_idx, uint16_t rnti)
{
dci_location_t locations[MAX_CANDIDATES];
uint32_t nof_locations = pdcch_ue_locations(&q->pdcch, locations, MAX_CANDIDATES, sf_idx, cfi, rnti);
uint16_t crc_rem = 0;
for (uint32_t i=0;i<nof_locations && crc_rem != rnti;i++) {
if (pdcch_decode_msg(&q->pdcch, dci_msg, &locations[i], Format0, &crc_rem)) {
fprintf(stderr, "Error decoding DCI msg\n");
return LIBLTE_ERROR;
}
INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem);
}
return crc_rem == rnti;
}
int ue_dl_decode_rnti_rv(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, uint16_t rnti, uint32_t rvidx)
{
uint32_t cfi, i;
dci_msg_t dci_msg;
dci_location_t locations[MAX_CANDIDATES];
uint32_t nof_locations;
uint16_t crc_rem;
int ret = LIBLTE_ERROR;
uint32_t nof_formats;
dci_format_t *formats = NULL;
if ((ret = ue_dl_decode_fft_estimate(q, input, sf_idx, &cfi)) < 0) {
return ret;
}
/* Generate PDCCH candidates */ /* Generate PDCCH candidates */
if (q->current_rnti == SIRNTI) { if (rnti == SIRNTI) {
nof_locations = pdcch_common_locations(&q->pdcch, locations, MAX_CANDIDATES, cfi); nof_locations = pdcch_common_locations(&q->pdcch, locations, MAX_CANDIDATES, cfi);
formats = common_formats; formats = common_formats;
nof_formats = nof_common_formats; nof_formats = nof_common_formats;
} else { } else {
nof_locations = pdcch_ue_locations(&q->pdcch, locations, MAX_CANDIDATES, sf_idx, cfi, q->current_rnti); nof_locations = pdcch_ue_locations(&q->pdcch, locations, MAX_CANDIDATES, sf_idx, cfi, rnti);
formats = ue_formats; formats = ue_formats;
nof_formats = nof_ue_formats; nof_formats = nof_ue_formats;
if (q->current_rnti == 1234) {
nof_locations = 1;
nof_formats = 1;
}
} }
/* Extract all PDCCH symbols and get LLRs */
if (pdcch_extract_llr(&q->pdcch, q->sf_symbols, q->ce, chest_dl_get_noise_estimate(&q->chest), sf_idx, cfi)) {
fprintf(stderr, "Error extracting LLRs\n");
return LIBLTE_ERROR;
}
/* For all possible locations, try to decode a DCI message */ /* For all possible locations, try to decode a DCI message */
crc_rem = 0; crc_rem = 0;
uint32_t found_dci = 0; uint32_t found_dci = 0;
for (int f=0;f<nof_formats && !found_dci;f++) { for (int f=0;f<nof_formats && !found_dci;f++) {
INFO("Trying format %s\n", dci_format_string(formats[f]));
for (i=0;i<nof_locations && !found_dci;i++) { for (i=0;i<nof_locations && !found_dci;i++) {
if (pdcch_decode_msg(&q->pdcch, &dci_msg, &locations[i], formats[f], &crc_rem)) { if (pdcch_decode_msg(&q->pdcch, &dci_msg, &locations[i], formats[f], &crc_rem)) {
fprintf(stderr, "Error decoding DCI msg\n"); fprintf(stderr, "Error decoding DCI msg\n");
@ -239,53 +298,15 @@ int ue_dl_decode_sib(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, ui
} }
INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem); INFO("Decoded DCI message RNTI: 0x%x\n", crc_rem);
if (crc_rem == q->current_rnti) { if (crc_rem == rnti) {
found_dci++; found_dci++;
q->nof_pdcch_detected++; ret = ue_dl_decode_rnti_rv_packet(q, &dci_msg, data, cfi, sf_idx, rnti, rvidx);
if (dci_msg_to_ra_dl(&dci_msg, q->current_rnti, q->user_rnti, q->cell, cfi, &ra_dl)) {
fprintf(stderr, "Error unpacking PDSCH scheduling DCI message\n");
return LIBLTE_ERROR;
}
if (q->current_rnti != SIRNTI) {
rvidx = ra_dl.rv_idx;
}
if (rvidx == 0) {
if (pdsch_harq_setup(&q->harq_process[0], ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
return LIBLTE_ERROR;
}
}
if (q->harq_process[0].mcs.mod > 0) {
ret = pdsch_decode(&q->pdsch, q->sf_symbols, q->ce,
chest_dl_get_noise_estimate(&q->chest),
data, sf_idx,
&q->harq_process[0], rvidx);
if (ret == LIBLTE_ERROR) {
q->pkt_errors++;
} else if (ret == LIBLTE_ERROR_INVALID_INPUTS) {
fprintf(stderr, "Error calling pdsch_decode()\n");
return LIBLTE_ERROR;
} else if (ret == LIBLTE_SUCCESS) {
if (VERBOSE_ISINFO()) {
INFO("Decoded Message: ", 0);
vec_fprint_hex(stdout, data, ra_dl.mcs.tbs);
}
}
q->pkts_total++;
}
} }
} }
} }
gettimeofday(&t[2], NULL);
get_time_interval(t);
mean_exec_time = (float) VEC_EMA((float) t[0].tv_usec, mean_exec_time, 0.01);
if (found_dci > 0 && ret == LIBLTE_SUCCESS) { if (found_dci > 0 && ret == LIBLTE_SUCCESS) {
return ra_dl.mcs.tbs; return q->ra_dl.mcs.tbs;
} else { } else {
return 0; return 0;
} }

@ -121,7 +121,7 @@ int ue_mib_decode(ue_mib_t * q, cf_t *input,
/* Run FFT for the slot symbols */ /* Run FFT for the slot symbols */
lte_fft_run_sf(&q->fft, input, q->sf_symbols); lte_fft_run_sf(&q->fft, input, q->sf_symbols);
/* Get channel estimates of slot #1 for each port */ /* Get channel estimates of sf idx #0 for each port */
ret = chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, 0); ret = chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, 0);
if (ret < 0) { if (ret < 0) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
@ -159,7 +159,7 @@ int ue_mib_decode(ue_mib_t * q, cf_t *input,
int ue_mib_sync_init(ue_mib_sync_t *q, int ue_mib_sync_init(ue_mib_sync_t *q,
uint32_t cell_id, uint32_t cell_id,
lte_cp_t cp, lte_cp_t cp,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t, timestamp_t*),
void *stream_handler) void *stream_handler)
{ {
lte_cell_t cell; lte_cell_t cell;
@ -214,9 +214,7 @@ int ue_mib_sync_decode(ue_mib_sync_t * q,
break; break;
} else if (ue_sync_get_sfidx(&q->ue_sync) == 0) { } else if (ue_sync_get_sfidx(&q->ue_sync) == 0) {
if (ret == 1) { if (ret == 1) {
ue_mib_reset(&q->ue_mib);
mib_ret = ue_mib_decode(&q->ue_mib, sf_buffer, bch_payload, nof_tx_ports, sfn_offset); mib_ret = ue_mib_decode(&q->ue_mib, sf_buffer, bch_payload, nof_tx_ports, sfn_offset);
} else { } else {
INFO("Resetting PBCH decoder after %d frames\n", q->ue_mib.frame_cnt); INFO("Resetting PBCH decoder after %d frames\n", q->ue_mib.frame_cnt);
ue_mib_reset(&q->ue_mib); ue_mib_reset(&q->ue_mib);

@ -42,7 +42,7 @@
#define MAX_TIME_OFFSET 128 #define MAX_TIME_OFFSET 128
cf_t dummy[MAX_TIME_OFFSET]; cf_t dummy[MAX_TIME_OFFSET];
#define TRACK_MAX_LOST 10 #define TRACK_MAX_LOST 4
#define TRACK_FRAME_SIZE 32 #define TRACK_FRAME_SIZE 32
#define FIND_NOF_AVG_FRAMES 2 #define FIND_NOF_AVG_FRAMES 2
@ -82,7 +82,7 @@ clean_exit:
int ue_sync_init(ue_sync_t *q, int ue_sync_init(ue_sync_t *q,
lte_cell_t cell, lte_cell_t cell,
int (recv_callback)(void*, void*, uint32_t), int (recv_callback)(void*, void*, uint32_t,timestamp_t*),
void *stream_handler) void *stream_handler)
{ {
int ret = LIBLTE_ERROR_INVALID_INPUTS; int ret = LIBLTE_ERROR_INVALID_INPUTS;
@ -140,7 +140,7 @@ int ue_sync_init(ue_sync_t *q,
sync_set_threshold(&q->sfind, 1.3); sync_set_threshold(&q->sfind, 1.3);
sync_set_em_alpha(&q->sfind, 0.01); sync_set_em_alpha(&q->sfind, 0.01);
q->nof_avg_find_frames = FIND_NOF_AVG_FRAMES; q->nof_avg_find_frames = FIND_NOF_AVG_FRAMES;
sync_set_threshold(&q->strack, 1.2); sync_set_threshold(&q->strack, 1.0);
} else { } else {
sync_set_N_id_2(&q->sfind, cell.id%3); sync_set_N_id_2(&q->sfind, cell.id%3);
@ -155,8 +155,8 @@ int ue_sync_init(ue_sync_t *q,
*/ */
sync_set_em_alpha(&q->sfind, 1); sync_set_em_alpha(&q->sfind, 1);
q->nof_avg_find_frames = 1; q->nof_avg_find_frames = 1;
sync_set_threshold(&q->sfind, 2.0); sync_set_threshold(&q->sfind, 4.0);
sync_set_threshold(&q->strack, 1.2); sync_set_threshold(&q->strack, 1.3);
/* Correct CFO in the find state but not in the track state, since is called only /* Correct CFO in the find state but not in the track state, since is called only
* 1 every 5 subframes. Will do it in the ue_sync_get_buffer() function. * 1 every 5 subframes. Will do it in the ue_sync_get_buffer() function.
@ -201,6 +201,10 @@ void ue_sync_free(ue_sync_t *q) {
bzero(q, sizeof(ue_sync_t)); bzero(q, sizeof(ue_sync_t));
} }
void ue_sync_get_last_timestamp(ue_sync_t *q, timestamp_t *timestamp) {
memcpy(timestamp, &q->last_timestamp, sizeof(timestamp_t));
}
uint32_t ue_sync_peak_idx(ue_sync_t *q) { uint32_t ue_sync_peak_idx(ue_sync_t *q) {
return q->peak_idx; return q->peak_idx;
} }
@ -250,7 +254,7 @@ static int find_peak_ok(ue_sync_t *q) {
if (q->frame_find_cnt >= q->nof_avg_find_frames || q->peak_idx < 2*q->fft_size) { if (q->frame_find_cnt >= q->nof_avg_find_frames || q->peak_idx < 2*q->fft_size) {
INFO("Realigning frame, reading %d samples\n", q->peak_idx+q->sf_len/2); INFO("Realigning frame, reading %d samples\n", q->peak_idx+q->sf_len/2);
/* Receive the rest of the subframe so that we are subframe aligned*/ /* Receive the rest of the subframe so that we are subframe aligned*/
if (q->recv_callback(q->stream, q->input_buffer, q->peak_idx+q->sf_len/2) < 0) { if (q->recv_callback(q->stream, q->input_buffer, q->peak_idx+q->sf_len/2, &q->last_timestamp) < 0) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
@ -282,33 +286,33 @@ static int track_peak_ok(ue_sync_t *q, uint32_t track_idx) {
q->strack.m0, q->strack.m0_value, q->strack.m1, q->strack.m1_value); q->strack.m0, q->strack.m0_value, q->strack.m1, q->strack.m1_value);
q->sf_idx = sync_get_sf_idx(&q->strack); q->sf_idx = sync_get_sf_idx(&q->strack);
} }
} else { }
// Adjust time offset
q->time_offset = ((int) track_idx - (int) q->strack.frame_size/2 - (int) q->strack.fft_size);
if (q->time_offset) { // Adjust time offset
INFO("Time offset adjustment: %d samples\n", q->time_offset); q->time_offset = ((int) track_idx - (int) q->strack.frame_size/2 - (int) q->strack.fft_size);
}
/* compute cumulative moving average time offset */ if (q->time_offset) {
q->mean_time_offset = (float) VEC_CMA((float) q->time_offset, q->mean_time_offset, q->frame_total_cnt); INFO("Time offset adjustment: %d samples\n", q->time_offset);
}
/* If the PSS peak is beyond the frame (we sample too slowly), /* compute cumulative moving average time offset */
discard the offseted samples to align next frame */ q->mean_time_offset = (float) VEC_CMA((float) q->time_offset, q->mean_time_offset, q->frame_total_cnt);
if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) {
INFO("\nPositive time offset %d samples. Mean time offset %f.\n", q->time_offset, q->mean_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;
}
q->time_offset = 0;
}
q->peak_idx = q->sf_len/2 + q->time_offset; /* If the PSS peak is beyond the frame (we sample too slowly),
q->frame_ok_cnt++; discard the offseted samples to align next frame */
q->frame_no_cnt = 0; if (q->time_offset > 0 && q->time_offset < MAX_TIME_OFFSET) {
INFO("Positive time offset %d samples. Mean time offset %f.\n", q->time_offset, q->mean_time_offset);
if (q->recv_callback(q->stream, dummy, (uint32_t) q->time_offset, &q->last_timestamp) < 0) {
fprintf(stderr, "Error receiving from USRP\n");
return LIBLTE_ERROR;
}
q->time_offset = 0;
} }
q->peak_idx = q->sf_len/2 + q->time_offset;
q->frame_ok_cnt++;
q->frame_no_cnt = 0;
return 1; return 1;
} }
@ -337,7 +341,7 @@ static int receive_samples(ue_sync_t *q) {
} }
/* Get N subframes from the USRP getting more samples and keeping the previous samples, if any */ /* Get N subframes 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], q->frame_len - q->time_offset) < 0) { if (q->recv_callback(q->stream, &q->input_buffer[q->time_offset], q->frame_len - q->time_offset, &q->last_timestamp) < 0) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
@ -452,6 +456,13 @@ int ue_sync_get_buffer(ue_sync_t *q, cf_t **sf_symbols) {
-sync_get_cfo(&q->strack) / q->fft_size); -sync_get_cfo(&q->strack) / q->fft_size);
} }
/*
if (track_idx > q->fft_size + q->strack.frame_size/2) {
*sf_symbols = &q->input_buffer[track_idx - q->fft_size - q->strack.frame_size/2];
} else {
*sf_symbols = q->input_buffer;
}
*/
*sf_symbols = q->input_buffer; *sf_symbols = q->input_buffer;
break; break;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save