diff --git a/CMakeLists.txt b/CMakeLists.txt index 610e96230..0be5f898b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,7 @@ INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lte/phy/include/) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lte/rrc/include/) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lte/rrc/asn/) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/cuhd/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/mex/include) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/graphics/include) ######################################################################## @@ -164,4 +165,4 @@ ADD_SUBDIRECTORY(common) ADD_SUBDIRECTORY(cuhd) ADD_SUBDIRECTORY(graphics) ADD_SUBDIRECTORY(lte) - +add_subdirectory(mex) diff --git a/cmake/modules/BuildMex.cmake b/cmake/modules/BuildMex.cmake index 124e149a8..03b4a336c 100644 --- a/cmake/modules/BuildMex.cmake +++ b/cmake/modules/BuildMex.cmake @@ -4,6 +4,7 @@ include(CMakeParseArguments) + if(NOT MATLAB_FOUND) find_package(MATLAB) endif() @@ -22,12 +23,6 @@ if(NOT MATLAB_MEX_PATH) ) endif() -IF (MATLAB_FOUND) - message(STATUS "Found MATLAB in ${MATLAB_ROOT}") -ELSE(MATLAB_FOUND) - message(STATUS "Could NOT find MATLAB. MEX files won't be compiled") -ENDIF(MATLAB_FOUND) - # # BuildMex -- arguments # MEXNAME = root of mex library name diff --git a/cuhd/lib/cuhd_imp.cpp b/cuhd/lib/cuhd_imp.cpp index 9608c94a3..33b925b97 100644 --- a/cuhd/lib/cuhd_imp.cpp +++ b/cuhd/lib/cuhd_imp.cpp @@ -147,8 +147,14 @@ int cuhd_close(void *h) double cuhd_set_rx_srate(void *h, double freq) { 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) { + printf("Got %f!=%f. setting master clock rate to %f\n",ret, freq, freq); + handler->usrp->set_master_clock_rate(freq); + handler->usrp->set_rx_rate(freq); + double ret = handler->usrp->get_rx_rate(); + } return ret; } diff --git a/lte/examples/cell_measurement.c b/lte/examples/cell_measurement.c index c054bee05..8abc905e6 100644 --- a/lte/examples/cell_measurement.c +++ b/lte/examples/cell_measurement.c @@ -42,7 +42,7 @@ #include "cell_search_utils.h" #define B210_DEFAULT_GAIN 40.0 -#define B210_DEFAULT_GAIN_CORREC 76.0 // Gain of the Rx chain when the gain is set to 40 +#define B210_DEFAULT_GAIN_CORREC 80.0 // Gain of the Rx chain when the gain is set to 40 float gain_offset = B210_DEFAULT_GAIN_CORREC; @@ -226,7 +226,7 @@ int main(int argc, char **argv) { if (n < 0) { fprintf(stderr, "Error decoding UE MIB\n"); exit(-1); - } else if (n == MIB_FOUND) { + } 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("MIB found SFN: %d, offset: %d\n", sfn, sfn_offset); diff --git a/lte/examples/cell_search_utils.c b/lte/examples/cell_search_utils.c index 2a83148ed..e50912305 100644 --- a/lte/examples/cell_search_utils.c +++ b/lte/examples/cell_search_utils.c @@ -271,6 +271,8 @@ int detect_and_decode_cell(cell_detect_cfg_t *config, void *uhd, int force_N_id_ cell->nof_ports = nof_tx_ports; bit_unpack_vector(bch_payload, bch_payload_unpacked, BCH_PAYLOAD_LEN); + printf("nof_bits: %d\n", BCH_PAYLOAD_LEN); + vec_fprint_hex(stdout, bch_payload, BCH_PAYLOAD_LEN); bcch_bch_unpack(bch_payload_unpacked, BCH_PAYLOAD_LEN, cell, NULL); /* set sampling frequency */ diff --git a/lte/phy/include/liblte/phy/ch_estimation/chest_dl.h b/lte/phy/include/liblte/phy/ch_estimation/chest_dl.h index 0feb7180e..145facc41 100644 --- a/lte/phy/include/liblte/phy/ch_estimation/chest_dl.h +++ b/lte/phy/include/liblte/phy/ch_estimation/chest_dl.h @@ -63,6 +63,7 @@ typedef struct { uint32_t filter_time_len; float filter_time[CHEST_MAX_FILTER_TIME_LEN]; + cf_t *tmp_noise; cf_t *tmp_freqavg; cf_t *tmp_timeavg[CHEST_MAX_FILTER_TIME_LEN]; diff --git a/lte/phy/include/liblte/phy/phch/pdcch.h b/lte/phy/include/liblte/phy/phch/pdcch.h index d061f7d0e..3d72295e4 100644 --- a/lte/phy/include/liblte/phy/phch/pdcch.h +++ b/lte/phy/include/liblte/phy/phch/pdcch.h @@ -65,6 +65,7 @@ typedef struct LIBLTE_API { cf_t *pdcch_x[MAX_PORTS]; cf_t *pdcch_d; uint8_t *pdcch_e; + float pdcch_rm_f[3 * (DCI_MAX_BITS + 16)]; float *pdcch_llr; /* tx & rx objects */ diff --git a/lte/phy/lib/CMakeLists.txt b/lte/phy/lib/CMakeLists.txt index bbb80299c..5b5624e5f 100644 --- a/lte/phy/lib/CMakeLists.txt +++ b/lte/phy/lib/CMakeLists.txt @@ -46,7 +46,7 @@ SET(SOURCES_ALL "") FOREACH (_module ${modules}) IF(IS_DIRECTORY ${_module}) FILE(GLOB_RECURSE tmp "${_module}/src/*.c") - LIST(APPEND SOURCES_ALL ${tmp}) + LIST(APPEND SOURCES_ALL ${tmp}) ENDIF(IS_DIRECTORY ${_module}) ENDFOREACH() @@ -57,14 +57,13 @@ LIBLTE_SET_PIC(lte_phy) IF(VOLK_FOUND) INCLUDE_DIRECTORIES(${VOLK_INCLUDE_DIRS}) - SET_TARGET_PROPERTIES(lte_phy PROPERTIES COMPILE_DEFINITIONS "${VOLK_DEFINITIONS}") + SET_TARGET_PROPERTIES(lte_phy PROPERTIES COMPILE_DEFINITIONS "${VOLK_DEFINITIONS}") TARGET_LINK_LIBRARIES(lte_phy ${VOLK_LIBRARIES}) MESSAGE(STATUS " Compiling with VOLK SIMD library.") ELSE(VOLK_FOUND) MESSAGE(STATUS " VOLK SIMD library NOT found. Using generic implementation.") ENDIF(VOLK_FOUND) - ######################################################################## # Recurse subdirectories and find all directories with a CMakeLists.txt file in it ######################################################################## diff --git a/lte/phy/lib/ch_estimation/src/chest_dl.c b/lte/phy/lib/ch_estimation/src/chest_dl.c index f56f3de15..5aab75e27 100644 --- a/lte/phy/lib/ch_estimation/src/chest_dl.c +++ b/lte/phy/lib/ch_estimation/src/chest_dl.c @@ -67,11 +67,16 @@ int chest_dl_init(chest_dl_t *q, lte_cell_t cell) goto clean_exit; } - q->tmp_freqavg = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb); + q->tmp_freqavg = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb)); if (!q->tmp_freqavg) { perror("malloc"); goto clean_exit; } + q->tmp_noise = vec_malloc(sizeof(cf_t) * REFSIGNAL_MAX_NUM_SF(cell.nof_prb)); + if (!q->tmp_noise) { + perror("malloc"); + goto clean_exit; + } for (int i=0;itmp_timeavg[i] = vec_malloc(sizeof(cf_t) * 2*cell.nof_prb); if (!q->tmp_timeavg[i]) { @@ -135,6 +140,9 @@ void chest_dl_free(chest_dl_t *q) if (q->tmp_freqavg) { free(q->tmp_freqavg); } + if (q->tmp_noise) { + free(q->tmp_noise); + } for (int i=0;itmp_timeavg[i]) { free(q->tmp_timeavg[i]); @@ -181,8 +189,22 @@ int chest_dl_set_filter_time(chest_dl_t *q, float *filter, uint32_t filter_len) } } + +static float estimate_noise_port(chest_dl_t *q, uint32_t port_id, cf_t *avg_pilots) { + /* Use difference between averaged and noisy LS pilot estimates */ + vec_sub_ccc(avg_pilots, q->pilot_estimates[port_id], + q->tmp_noise, REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)); + + float noise_var = vec_avg_power_cf(q->tmp_noise, REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)); + + /* compute noise power. Correction factor obtained through simulations */ + return 0.75 * sqrtf((float) q->filter_freq_len) * noise_var / sqrt(q->cell.nof_prb); +} + + #define pilot_est(idx) q->pilot_estimates[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)] #define pilot_avg(idx) q->pilot_estimates_average[port_id][REFSIGNAL_PILOT_IDX(idx,l,q->cell)] +#define pilot_tmp(idx) q->tmp_freqavg[REFSIGNAL_PILOT_IDX(idx,l,q->cell)] static void average_pilots(chest_dl_t *q, uint32_t port_id) { @@ -193,17 +215,24 @@ static void average_pilots(chest_dl_t *q, uint32_t port_id) for (l=0;lfilter_freq_len > 0) { /* Filter pilot estimates in frequency */ - conv_same_cf(&pilot_est(0), q->filter_freq, q->tmp_freqavg, nref, q->filter_freq_len); + conv_same_cf(&pilot_est(0), q->filter_freq, &pilot_tmp(0), nref, q->filter_freq_len); /* Adjust extremes using linear interpolation */ - q->tmp_freqavg[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->tmp_freqavg[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]; } else { - memcpy(q->tmp_freqavg, &pilot_est(0), nref * sizeof(cf_t)); + memcpy(&pilot_tmp(0), &pilot_est(0), nref * sizeof(cf_t)); } + } + + /* Compute noise estimation before time averaging. + * FIXME: Apparently the noise estimation performance is better with frequency averaging only + */ + q->noise_estimate[port_id] = estimate_noise_port(q, port_id, q->tmp_freqavg); + for (l=0;lfilter_time_len > 0) { /* Move last symbols */ @@ -211,7 +240,7 @@ static void average_pilots(chest_dl_t *q, uint32_t port_id) memcpy(q->tmp_timeavg[i], q->tmp_timeavg[i+1], nref*sizeof(cf_t)); } /* Put last symbol to buffer */ - memcpy(q->tmp_timeavg[i], q->tmp_freqavg, nref*sizeof(cf_t)); + memcpy(q->tmp_timeavg[i], &pilot_tmp(0), nref*sizeof(cf_t)); /* Multiply all symbols by filter and add them */ bzero(&pilot_avg(0), nref * sizeof(cf_t)); @@ -220,24 +249,11 @@ static void average_pilots(chest_dl_t *q, uint32_t port_id) vec_sum_ccc(q->tmp_timeavg[i], &pilot_avg(0), &pilot_avg(0), nref); } } else { - memcpy(&pilot_avg(0), q->tmp_freqavg, nref * sizeof(cf_t)); + memcpy(&pilot_avg(0), &pilot_tmp(0), nref * sizeof(cf_t)); } } } -static float estimate_noise_port(chest_dl_t *q, uint32_t port_id) { - /* Use difference between averaged and noisy LS pilot estimates */ - vec_sub_ccc(q->pilot_estimates_average[port_id], q->pilot_estimates[port_id], - q->pilot_estimates[port_id], REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)); - /* compute noise power */ - - float noiseEst = vec_dot_prod_conj_ccc(q->pilot_estimates[port_id], - q->pilot_estimates[port_id], - REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id)); - - return noiseEst * sqrtf((float) q->filter_freq_len) / REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id); -} - #define cesymb(i) ce[SAMPLE_IDX(q->cell.nof_prb,i,0)] static void interpolate_pilots(chest_dl_t *q, cf_t *ce, uint32_t port_id) @@ -303,12 +319,9 @@ int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal[port_id]); /* Compute RSRP for the references in this port */ + q->rsrp[port_id] = chest_dl_rsrp(q, port_id); if (port_id == 0) { - q->rsrp[port_id] = chest_dl_rsrp(q, port_id); - } - - /* compute rssi */ - if (port_id == 0) { + /* compute rssi only for port 0 */ q->rssi[port_id] = chest_dl_rssi(q, input, port_id); } @@ -324,8 +337,6 @@ int chest_dl_estimate_port(chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx interpolate_pilots(q, ce, port_id); } - q->noise_estimate[port_id] = estimate_noise_port(q, port_id); - return 0; } @@ -344,12 +355,13 @@ float chest_dl_get_noise_estimate(chest_dl_t *q) { } float chest_dl_get_snr(chest_dl_t *q) { - float noise = chest_dl_get_noise_estimate(q); - if (noise) { - return chest_dl_get_rssi(q)/(noise);//*2*q->cell.nof_ports*lte_symbol_sz(q->cell.nof_prb)); - } else { - return 0.0; + float snr = 0.0; + for (int i=0;icell.nof_ports;i++) { + if (q->noise_estimate[i]) { + snr += q->rsrp[i]/(q->noise_estimate[i]*sqrtf(2*q->cell.nof_ports*lte_symbol_sz(q->cell.nof_prb))); + } } + return snr/q->cell.nof_ports; } float chest_dl_get_rssi(chest_dl_t *q) { diff --git a/lte/phy/lib/ch_estimation/test/chest_test_dl_mex.c b/lte/phy/lib/ch_estimation/test/chest_test_dl_mex.c index 6d6a4768e..aacf7cbe7 100644 --- a/lte/phy/lib/ch_estimation/test/chest_test_dl_mex.c +++ b/lte/phy/lib/ch_estimation/test/chest_test_dl_mex.c @@ -114,9 +114,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) return; } } - - - + uint32_t filter_len = 0; float *filter; double *f; @@ -172,7 +170,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) outr2 = mxGetPr(plhs[2]); outi2 = mxGetPi(plhs[2]); } - + for (int sf=0;sf= 4) { + plhs[3] = mxCreateDoubleScalar(chest_dl_get_snr(&chest)); + } + return; } diff --git a/lte/phy/lib/phch/src/pdcch.c b/lte/phy/lib/phch/src/pdcch.c index 2b0c0af24..353a2c8f3 100644 --- a/lte/phy/lib/phch/src/pdcch.c +++ b/lte/phy/lib/phch/src/pdcch.c @@ -276,7 +276,6 @@ uint32_t pdcch_common_locations(pdcch_t *q, dci_location_t *c, uint32_t max_cand */ static int dci_decode(pdcch_t *q, float *e, uint8_t *data, uint32_t E, uint32_t nof_bits, uint16_t *crc) { - float tmp[3 * (DCI_MAX_BITS + 16)]; uint16_t p_bits, crc_res; uint8_t *x; @@ -292,15 +291,15 @@ static int dci_decode(pdcch_t *q, float *e, uint8_t *data, uint32_t E, uint32_t } /* unrate matching */ - rm_conv_rx(e, E, tmp, 3 * (nof_bits + 16)); + rm_conv_rx(e, E, q->pdcch_rm_f, 3 * (nof_bits + 16)); DEBUG("Viterbi input: ", 0); if (VERBOSE_ISDEBUG()) { - vec_fprint_f(stdout, tmp, 3 * (nof_bits + 16)); + vec_fprint_f(stdout, q->pdcch_rm_f, 3 * (nof_bits + 16)); } /* viterbi decoder */ - viterbi_decode_f(&q->decoder, tmp, data, nof_bits + 16); + viterbi_decode_f(&q->decoder, q->pdcch_rm_f, data, nof_bits + 16); if (VERBOSE_ISDEBUG()) { bit_fprint(stdout, data, nof_bits + 16); @@ -421,7 +420,7 @@ int pdcch_extract_llr(pdcch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float n } /* demodulate symbols */ - demod_soft_sigma_set(&q->demod, 1); + demod_soft_sigma_set(&q->demod, sqrtf(2/q->cell.nof_ports)); demod_soft_demodulate(&q->demod, q->pdcch_d, q->pdcch_llr, nof_symbols); /* descramble */ diff --git a/lte/phy/lib/phch/test/CMakeLists.txt b/lte/phy/lib/phch/test/CMakeLists.txt index 328d8128a..8a903710e 100644 --- a/lte/phy/lib/phch/test/CMakeLists.txt +++ b/lte/phy/lib/phch/test/CMakeLists.txt @@ -80,6 +80,8 @@ ADD_TEST(pdcch_test pdcch_test) ADD_EXECUTABLE(dci_unpacking dci_unpacking.c) TARGET_LINK_LIBRARIES(dci_unpacking lte_phy) +BuildMex(MEXNAME pdcch SOURCES pdcch_test_mex.c LIBRARIES lte_phy liblte_mex) + ######################################################################## # PDSCH TEST ######################################################################## diff --git a/lte/phy/lib/phch/test/pdcch_test_mex.c b/lte/phy/lib/phch/test/pdcch_test_mex.c new file mode 100644 index 000000000..a5a1f3c33 --- /dev/null +++ b/lte/phy/lib/phch/test/pdcch_test_mex.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2012, Ismael Gomez-Miguelez . + * This file is part of ALOE++ (http://flexnets.upc.edu/) + * + * ALOE++ 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. + * + * ALOE++ 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ALOE++. If not, see . + */ + +#include +#include "liblte/phy/phy.h" +#include "liblte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define RNTI prhs[1] +#define INPUT prhs[2] +#define NOF_INPUTS 3 + +#define MAX_CANDIDATES 64 + + +dci_format_t ue_formats[] = {Format1A,Format1}; // Format1B should go here also +const uint32_t nof_ue_formats = 2; + +dci_format_t common_formats[] = {Format1A,Format1C}; +const uint32_t nof_common_formats = 2; + + +void help() +{ + mexErrMsgTxt + ("[decoded_ok, llr, rm, bits] = liblte_pdcch(enbConfig, RNTI, inputSignal)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + int i; + lte_cell_t cell; + pdcch_t pdcch; + regs_t regs; + dci_location_t locations[MAX_CANDIDATES]; + uint32_t cfi, sf_idx; + uint16_t rnti; + cf_t *input_symbols; + int nof_re; + uint32_t nof_formats; + dci_format_t *formats = NULL; + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (mexutils_read_uint32_struct(ENBCFG, "CFI", &cfi)) { + help(); + return; + } + if (mexutils_read_uint32_struct(ENBCFG, "NSubframe", &sf_idx)) { + help(); + return; + } + + rnti = (uint16_t) mxGetScalar(RNTI); + + if (regs_init(®s, cell)) { + mexErrMsgTxt("Error initiating regs\n"); + return; + } + + if (regs_set_cfi(®s, cfi)) { + fprintf(stderr, "Error setting CFI\n"); + exit(-1); + } + + if (pdcch_init(&pdcch, ®s, cell)) { + mexErrMsgTxt("Error initiating channel estimator\n"); + return; + } + + /** Allocate input buffers */ + nof_re = mexutils_read_cf(INPUT, &input_symbols); + if (nof_re < 0) { + mexErrMsgTxt("Error reading input symbols\n"); + return; + } + + // Set Channel estimates to 1.0 (ignore fading) + cf_t *ce[MAX_PORTS]; + for (i=0;i= 1) { + plhs[0] = mxCreateLogicalScalar(crc_rem == rnti); + } + int nof_bits = (regs_pdcch_nregs(®s, cfi) / 9) * 72; + if (nlhs >= 2) { + mexutils_write_f(pdcch.pdcch_llr, &plhs[1], nof_bits, 1); + } + if (nlhs >= 3) { + mexutils_write_f(pdcch.pdcch_rm_f, &plhs[2], 3*(dci_msg.nof_bits+16), 1); + } + + pdcch_free(&pdcch); + regs_free(®s); + for (i=0;isss, q->N_id_2); if (q->detect_cp) { - if (peak_pos - q->fft_size - CP_EXT(q->fft_size) >= 0) { + if (peak_pos >= q->fft_size + CP_EXT(q->fft_size)) { q->cp = detect_cp(q, input, peak_pos); } else { INFO("Not enough room to detect CP length. Peak position: %d\n", peak_pos); diff --git a/lte/rrc/lib/messages/src/bcch.c b/lte/rrc/lib/messages/src/bcch.c index c57d6bb53..7fc671efc 100644 --- a/lte/rrc/lib/messages/src/bcch.c +++ b/lte/rrc/lib/messages/src/bcch.c @@ -158,7 +158,7 @@ int bcch_bch_unpack(uint8_t *buffer, uint32_t msg_nof_bits, lte_cell_t *cell, ui if (sfn) { *sfn=(sfn_i<<2); } else { - asn_fprint(stdout, &asn_DEF_MasterInformationBlock, req); + asn_fprint(stdout, &asn_DEF_MasterInformationBlock, req); } return LIBLTE_SUCCESS; } diff --git a/matlab/tests/equalizer_test.m b/matlab/tests/equalizer_test.m index 573493d7b..26e2007ff 100644 --- a/matlab/tests/equalizer_test.m +++ b/matlab/tests/equalizer_test.m @@ -4,7 +4,7 @@ clear -SNR_values_db=[0 1 2];%15;%[5 10 15];%linspace(0,20,8); +SNR_values_db=linspace(0,30,8); Nrealizations=1; preEVM = zeros(length(SNR_values_db),Nrealizations); @@ -14,7 +14,7 @@ postEVM_liblte = zeros(length(SNR_values_db),Nrealizations); enb.NDLRB = 6; % Number of resource blocks -enb.CellRefP = 1; % One transmit antenna port +enb.CellRefP = 2; % One transmit antenna port enb.NCellID = 0; % Cell ID enb.CyclicPrefix = 'Normal'; % Normal cyclic prefix enb.DuplexMode = 'FDD'; % FDD @@ -23,7 +23,7 @@ enb.DuplexMode = 'FDD'; % FDD rng(1); % Configure random number generators cfg.Seed = 2; % Random channel seed -cfg.NRxAnts = 1; % 1 receive antenna +cfg.NRxAnts = 2; % 1 receive antenna cfg.DelayProfile = 'EVA'; % EVA delay spread cfg.DopplerFreq = 5; % 120Hz Doppler frequency cfg.MIMOCorrelation = 'Low'; % Low (no) MIMO correlation @@ -119,15 +119,19 @@ cfg.SamplingRate = info.SamplingRate; % Pass data through the fading channel model rxWaveform = lteFadingChannel(cfg,txWaveform); +rxWaveform = txWaveform; %% Additive Noise +channel_gain = mean(rxWaveform(:).*conj(rxWaveform(:)))/mean(txWaveform(:).*conj(txWaveform(:))); + % Calculate noise gain N0 = 1/(sqrt(2.0*enb.CellRefP*double(info.Nfft))*SNR); % Create additive white Gaussian noise noise = N0*complex(randn(size(rxWaveform)),randn(size(rxWaveform))); +noiseTx(snr_idx) = N0; % Add noise to the received time domain waveform rxWaveform = rxWaveform + noise; @@ -142,46 +146,59 @@ rxGrid = lteOFDMDemodulate(enb,rxWaveform); addpath('../../debug/lte/phy/lib/ch_estimation/test') %% Channel Estimation -[estChannel, noiseEst] = lteDLChannelEstimate(enb,cec,rxGrid); +[estChannel, noiseEst(snr_idx)] = lteDLChannelEstimate(enb,cec,rxGrid); output=[]; +snrest = zeros(10,1); for i=0:9 - [d, a, out] = liblte_chest(enb.NCellID,enb.CellRefP,rxGrid(:,i*14+1:(i+1)*14),[0.15 0.7 0.15],[0.1 0.9],i); +% if (SNR_values_db(snr_idx) < 25) + [d, a, out, snrest(i+1)] = liblte_chest(enb.NCellID,enb.CellRefP,rxGrid(:,i*14+1:(i+1)*14),[0.15 0.7 0.15],[],i); +% else +% [d, a, out, snrest(i+1)] = liblte_chest(enb.NCellID,enb.CellRefP,rxGrid(:,i*14+1:(i+1)*14),[0.05 0.9 0.05],[],i); +% end output = [output out]; end +SNRest(snr_idx)=mean(snrest); +disp(10*log10(SNRest(snr_idx))) %% MMSE Equalization -eqGrid_mmse = lteEqualizeMMSE(rxGrid, estChannel, noiseEst); - -eqGrid_liblte = reshape(output,size(eqGrid_mmse)); - -%% Analysis - -% Compute EVM across all input values -% EVM of pre-equalized receive signal -preEqualisedEVM = lteEVM(txGrid,rxGrid); -fprintf('%d-%d: Pre-EQ: %0.3f%%\n', ... - snr_idx,nreal,preEqualisedEVM.RMS*100); - - -%EVM of post-equalized receive signal -postEqualisedEVM_mmse = lteEVM(txGrid,reshape(eqGrid_mmse,size(txGrid))); -fprintf('%d-%d: MMSE: %0.3f%%\n', ... - snr_idx,nreal,postEqualisedEVM_mmse.RMS*100); - -postEqualisedEVM_liblte = lteEVM(txGrid,reshape(eqGrid_liblte,size(txGrid))); -fprintf('%d-%d: liblte: %0.3f%%\n', ... - snr_idx,nreal,postEqualisedEVM_liblte.RMS*100); - -preEVM(snr_idx,nreal) = preEqualisedEVM.RMS; -postEVM_mmse(snr_idx,nreal) = mean([postEqualisedEVM_mmse.RMS]); -postEVM_liblte(snr_idx,nreal) = mean([postEqualisedEVM_liblte.RMS]); +% eqGrid_mmse = lteEqualizeMMSE(rxGrid, estChannel, noiseEst(snr_idx)); +% +% eqGrid_liblte = reshape(output,size(eqGrid_mmse)); +% +% % Analysis +% +% %Compute EVM across all input values EVM of pre-equalized receive signal +% preEqualisedEVM = lteEVM(txGrid,rxGrid); +% fprintf('%d-%d: Pre-EQ: %0.3f%%\n', ... +% snr_idx,nreal,preEqualisedEVM.RMS*100); +% +% +% %EVM of post-equalized receive signal +% postEqualisedEVM_mmse = lteEVM(txGrid,reshape(eqGrid_mmse,size(txGrid))); +% fprintf('%d-%d: MMSE: %0.3f%%\n', ... +% snr_idx,nreal,postEqualisedEVM_mmse.RMS*100); +% +% postEqualisedEVM_liblte = lteEVM(txGrid,reshape(eqGrid_liblte,size(txGrid))); +% fprintf('%d-%d: liblte: %0.3f%%\n', ... +% snr_idx,nreal,postEqualisedEVM_liblte.RMS*100); +% +% preEVM(snr_idx,nreal) = preEqualisedEVM.RMS; +% postEVM_mmse(snr_idx,nreal) = mean([postEqualisedEVM_mmse.RMS]); +% postEVM_liblte(snr_idx,nreal) = mean([postEqualisedEVM_liblte.RMS]); end end -% plot(SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./realNoise),SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./noiseEstimation),SNR_values_db,20*log10(1/sqrt(2.0*enb.CellRefP*double(info.Nfft))./noiseEstimation2)) -% legend('real','seu','meu') -plot(SNR_values_db, mean(preEVM,2), ... - SNR_values_db, mean(postEVM_mmse,2), ... - SNR_values_db, mean(postEVM_liblte,2)) -legend('No Eq','MMSE-cubic','MMSE-lin','MMSE-liblte') -grid on - +% subplot(1,2,1) +% plot(SNR_values_db, mean(preEVM,2), ... +% SNR_values_db, mean(postEVM_mmse,2), ... +% SNR_values_db, mean(postEVM_liblte,2)) +% legend('No Eq','MMSE-lin','MMSE-liblte') +% grid on +% +% subplot(1,2,2) +%SNR_liblte = 1./(SNRest*sqrt(2.0*enb.CellRefP*double(info.Nfft))); +SNR_liblte = SNRest; +SNR_matlab = 1./(noiseEst*sqrt(2.0*enb.CellRefP*double(info.Nfft))); + +plot(SNR_values_db, SNR_values_db, SNR_values_db, 10*log10(SNR_liblte),SNR_values_db, 10*log10(SNR_matlab)) +%plot(SNR_values_db, 10*log10(noiseTx), SNR_values_db, 10*log10(SNRest),SNR_values_db, 10*log10(noiseEst)) +legend('Theory','libLTE','Matlab') diff --git a/matlab/tests/lteDLChannelEstimate2.m b/matlab/tests/lteDLChannelEstimate2.m index e99744a50..e2d77b4d7 100644 --- a/matlab/tests/lteDLChannelEstimate2.m +++ b/matlab/tests/lteDLChannelEstimate2.m @@ -428,7 +428,7 @@ function [H_EST, NoisePowerEst, AvgEstimates, Estimates] = lteDLChannelEstimate2 % The mean of the noise power across all the transmit/receive antenna % pairs is used as the estimate of the noise power NoisePowerEst = mean(mean(noiseVec)); - + %NoisePowerEst = noiseVec(2); end % GetPilotEstimates Obtain the least squares estimates of the reference diff --git a/matlab/tests/pdcch_test.m b/matlab/tests/pdcch_test.m new file mode 100644 index 000000000..cf8d07232 --- /dev/null +++ b/matlab/tests/pdcch_test.m @@ -0,0 +1,105 @@ +%% PDCCH Blind Search and DCI Decoding + +%% Cell-Wide Settings +% A structure |enbConfig| is used to configure the eNodeB. + +Npackets = 50; +SNR_values =linspace(-5,3,8); + +enbConfig.NDLRB = 15; % No of Downlink RBs in total BW +enbConfig.CyclicPrefix = 'Normal'; % CP length +enbConfig.CFI = 3; ; % 4 PDCCH symbols as NDLRB <= 10 +enbConfig.Ng = 'Sixth'; % HICH groups +enbConfig.CellRefP = 2; % 1-antenna ports +enbConfig.NCellID = 10; % Physical layer cell identity +enbConfig.NSubframe = 0; % Subframe number 0 +enbConfig.DuplexMode = 'FDD'; % Frame structure +enbConfig.PHICHDuration = 'Normal'; + +%% DCI Message Generation +% Generate a DCI message to be mapped to the PDCCH. + +dciConfig.DCIFormat = 'Format1A'; % DCI message format +dciConfig.Allocation.RIV = 26; % Resource indication value + +% Create DCI message for given configuration +[dciMessage, dciMessageBits] = lteDCI(enbConfig, dciConfig); + +%% DCI Channel Coding + +C_RNTI = 65535; % 16-bit UE-specific mask +pdcchConfig.RNTI = C_RNTI; % Radio network temporary identifier +pdcchConfig.PDCCHFormat = 3; % PDCCH format + +% DCI message bits coding to form coded DCI bits +codedDciBits = lteDCIEncode(pdcchConfig, dciMessageBits); + +%% PDCCH Bits Generation + +pdcchDims = ltePDCCHInfo(enbConfig); + +% Initialize elements with -1 to indicate that all the bits are unused +pdcchBits = -1*ones(pdcchDims.MTot, 1); + +% Perform search space for UE-specific control channel candidates. +candidates = ltePDCCHSpace(enbConfig, pdcchConfig, {'bits', '1based'}); + +Ncad=randi(length(candidates),1,1); + +% Map PDCCH payload on available UE-specific candidate. In this example the +% first available candidate is used to map the coded DCI bits. +pdcchBits ( candidates(Ncad, 1) : candidates(Ncad, 2) ) = codedDciBits; + +%% PDCCH Complex-Valued Modulated Symbol Generation + +pdcchSymbols = ltePDCCH(enbConfig, pdcchBits); + +pdcchIndices = ltePDCCHIndices(enbConfig,{'1based'}); + +decoded = zeros(size(SNR_values)); +decoded_liblte = zeros(size(SNR_values)); + +Nports = enbConfig.CellRefP; +ueConfig.RNTI = C_RNTI; + +subframe_tx = lteDLResourceGrid(enbConfig); +subframe_tx(pdcchIndices) = pdcchSymbols; + +addpath('../../debug/lte/phy/lib/phch/test') + +parfor snr_idx=1:length(SNR_values) + SNRdB = SNR_values(snr_idx); + for i=1:Npackets + + %% Noise Addition + SNR = 10^(SNRdB/10); % Linear SNR + + N0 = 1/(sqrt(2.0*Nports)*SNR); + noise = N0*complex(randn(size(subframe_tx)), randn(size(subframe_tx))); % Generate noise + + subframe_rx = sum(subframe_tx + noise,3); % Add noise to PDCCH symbols + + pdcchSymbolsNoisy = subframe_rx(pdcchIndices(:,1)); + + %% PDCCH Decoding + recPdcchBits = ltePDCCHDecode(enbConfig, pdcchSymbolsNoisy); + + %% Blind Decoding using DCI Search + [rxDCI, rxDCIBits] = ltePDCCHSearch(enbConfig, ueConfig, recPdcchBits); + decoded(snr_idx) = decoded(snr_idx) + length(rxDCI); + + [found_liblte, llr, viterbi_in] = liblte_pdcch(enbConfig, ueConfig.RNTI, subframe_rx); + + decoded_liblte(snr_idx) = decoded_liblte(snr_idx)+found_liblte; + end + fprintf('SNR: %.1f\n',SNRdB) +end + +if (Npackets>1) + plot(SNR_values,1-decoded/Npackets,SNR_values,1-decoded_liblte/Npackets) + grid on + legend('Matlab','libLTE') +else + disp(decoded_liblte) +end + diff --git a/matlab/tests/sync_test.m b/matlab/tests/sync_test.m new file mode 100644 index 000000000..d91320aeb --- /dev/null +++ b/matlab/tests/sync_test.m @@ -0,0 +1,32 @@ +enb = lteTestModel('1.1','5MHz'); +Ntrials = 1; +SNR_values =-10;%linspace(-18,-10,8); + +tx_offset = randi(50,Ntrials,1); +tx_offset = 50; +diff=zeros(size(SNR_values)); + +tx_signal = lteTestModelTool(enb); +tx_power = mean(tx_signal.*conj(tx_signal)); + +for snr_idx=1:length(SNR_values) + SNRdB = SNR_values(snr_idx); + rx_offset = zeros(size(tx_offset)); + for i=1:Ntrials + SNR = 10^(SNRdB/10); % Linear SNR + tx = [zeros(tx_offset(i),1); tx_signal]; + N0 = tx_power/(sqrt(2.0)*SNR); + noise = N0*complex(randn(size(tx)), randn(size(tx))); % Generate noise + rx=noise+tx; + [rx_offset(i),corr] = lteDLFrameOffset(enb,rx); + end + diff(snr_idx)=sum(abs(rx_offset-tx_offset)); + disp(SNRdB) +end + +if (Ntrials == 1) + plot(corr) +else + plot(SNR_values,diff); +end + diff --git a/mex/CMakeLists.txt b/mex/CMakeLists.txt new file mode 100644 index 000000000..b87343679 --- /dev/null +++ b/mex/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# Install headers +######################################################################## +INSTALL(DIRECTORY include/ + DESTINATION "${INCLUDE_DIR}" + FILES_MATCHING PATTERN "*.h" + PATTERN ".svn" EXCLUDE +) + +######################################################################## +# Add headers to cmake project (useful for IDEs) +######################################################################## +SET(HEADERS_ALL "") +FILE(GLOB headers *) +FOREACH (_header ${headers}) + IF(IS_DIRECTORY ${_header}) + FILE(GLOB_RECURSE tmp "${_header}/*.h") + LIST(APPEND HEADERS_ALL ${tmp}) + ENDIF(IS_DIRECTORY ${_header}) +ENDFOREACH() + +ADD_CUSTOM_TARGET (add_mex_headers SOURCES ${HEADERS_ALL}) + +######################################################################## +# Add the subdirectories +######################################################################## +ADD_SUBDIRECTORY(lib) + diff --git a/mex/include/liblte/mex/mexutils.h b/mex/include/liblte/mex/mexutils.h new file mode 100644 index 000000000..c225355cd --- /dev/null +++ b/mex/include/liblte/mex/mexutils.h @@ -0,0 +1,70 @@ +/** + * + * \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 MEXUTILS_ +#define MEXUTILS_ + + +#include + +#ifdef UNDEF_BOOL +#undef bool +#endif + +#include "mex.h" + +#include "liblte/config.h" + +typedef _Complex float cf_t; + +LIBLTE_API bool mexutils_isScalar(const mxArray *ptr); + +LIBLTE_API int mexutils_read_cell(const mxArray *ptr, + lte_cell_t *cell); + +LIBLTE_API int mexutils_read_uint32_struct(const mxArray *ptr, + const char *field_name, + uint32_t *value); + +LIBLTE_API int mexutils_write_f(float *buffer, + mxArray **ptr, + uint32_t nr, + uint32_t nc); + +LIBLTE_API int mexutils_write_cf(cf_t *buffer, + mxArray **ptr, + uint32_t nr, + uint32_t nc); + +LIBLTE_API int mexutils_read_f(const mxArray *ptr, + float **buffer); + +LIBLTE_API int mexutils_read_cf(const mxArray *ptr, + cf_t **buffer); + +#endif diff --git a/mex/lib/CMakeLists.txt b/mex/lib/CMakeLists.txt new file mode 100644 index 000000000..9eaba77e5 --- /dev/null +++ b/mex/lib/CMakeLists.txt @@ -0,0 +1,63 @@ +# +# Copyright 2012-2013 The libLTE Developers. See the +# COPYRIGHT file at the top-level directory of this distribution. +# +# This file is part of the libLTE library. +# +# libLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# libLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# A copy of the GNU Lesser General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +if(NOT MATLAB_FOUND) + find_package(MATLAB) +endif() + +if(NOT OCTAVE_FOUND) + find_package(OCTAVE) +endif() + +# CMake 2.8.12 & earlier apparently don't define the +# Mex script path, so find it. +if(NOT MATLAB_MEX_PATH) + find_program( MATLAB_MEX_PATH mex + HINTS ${MATLAB_ROOT}/bin + PATHS ${MATLAB_ROOT}/bin + DOC "The mex program path" + ) +endif() + +IF (MATLAB_FOUND) + message(STATUS "Found MATLAB in ${MATLAB_ROOT}") +ENDIF(MATLAB_FOUND) +IF (OCTAVE_FOUND) + message(STATUS "Found OCTAVE in ${OCTAVE_INCLUDE_PATHS}") +ENDIF(OCTAVE_FOUND) + + +IF (MATLAB_FOUND OR OCTAVE_FOUND) + ADD_LIBRARY(liblte_mex SHARED mexutils.c) + INSTALL(TARGETS liblte_mex DESTINATION ${LIBRARY_DIR}) + LIBLTE_SET_PIC(liblte_mex) + if (MATLAB_FOUND) + target_include_directories(liblte_mex PUBLIC ${MATLAB_INCLUDE_DIR}) + endif(MATLAB_FOUND) + if (OCTAVE_FOUND) + target_include_directories(liblte_mex PUBLIC ${OCTAVE_INCLUDE_DIR}) + endif (OCTAVE_FOUND) + +ELSEIF (MATLAB_FOUND OR OCTAVE_FOUND) + message(STATUS "Could NOT find OCTAVE or MATLAB. MEX files won't be compiled") +ENDIF(MATLAB_FOUND OR OCTAVE_FOUND) + + diff --git a/mex/lib/mexutils.c b/mex/lib/mexutils.c new file mode 100644 index 000000000..fb8e96c7d --- /dev/null +++ b/mex/lib/mexutils.c @@ -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 +#include "liblte/phy/common/phy_common.h" +#include "liblte/mex/mexutils.h" +#include "liblte/phy/utils/vector.h" + + +bool mexutils_isScalar(const mxArray *ptr) { + return mxGetM(ptr) == 1 && mxGetN(ptr) == 1; +} + +int mexutils_read_uint32_struct(const mxArray *ptr, const char *field_name, uint32_t *value) +{ + mxArray *p; + p = mxGetField(ptr, 0, field_name); + if (!p) { + mexPrintf("Error field %s not found\n", field_name); + return -1; + } + *value = (uint32_t) mxGetScalar(p); + return 0; +} + +int mexutils_read_cell(const mxArray *ptr, lte_cell_t *cell) { + if (mexutils_read_uint32_struct(ptr, "NCellID", &cell->id)) { + return -1; + } + if (mexutils_read_uint32_struct(ptr, "CellRefP", &cell->nof_ports)) { + return -1; + } + if (mexutils_read_uint32_struct(ptr, "NDLRB", &cell->nof_prb)) { + return -1; + } + // TODO + cell->cp = CPNORM; + cell->phich_length = PHICH_NORM; + cell->phich_resources = R_1_6; + return 0; +} + +int mexutils_read_cf(const mxArray *ptr, cf_t **buffer) { + int numelems = mxGetNumberOfElements(ptr); + cf_t *tmp = vec_malloc(numelems * sizeof(cf_t)); + if (tmp) { + double *inr=mxGetPr(ptr); + double *ini=mxGetPi(ptr); + for (int i=0;i