Added MEX lib. Added PDCCH test

master
ismagom 10 years ago
parent f32ac7b1ab
commit 48da6746cd

@ -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)

@ -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

@ -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;
}

@ -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);

@ -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 */

@ -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];

@ -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 */

@ -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
########################################################################

@ -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;i<CHEST_MAX_FILTER_TIME_LEN;i++) {
q->tmp_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;i<CHEST_MAX_FILTER_TIME_LEN;i++) {
if (q->tmp_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;l<refsignal_cs_nof_symbols(port_id);l++) {
if (q->filter_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;l<refsignal_cs_nof_symbols(port_id);l++) {
/* Filter in time domain. */
if (q->filter_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;i<q->cell.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) {

@ -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<nsubframes;sf++) {
/* Convert input to C complex type */
for (i=0;i<nof_re;i++) {
@ -235,7 +233,11 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
}
}
}
if (nlhs >= 4) {
plhs[3] = mxCreateDoubleScalar(chest_dl_get_snr(&chest));
}
return;
}

@ -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 */

@ -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
########################################################################

@ -0,0 +1,163 @@
/*
* Copyright (c) 2012, Ismael Gomez-Miguelez <ismael.gomez@tsc.upc.edu>.
* 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 <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 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(&regs, cell)) {
mexErrMsgTxt("Error initiating regs\n");
return;
}
if (regs_set_cfi(&regs, cfi)) {
fprintf(stderr, "Error setting CFI\n");
exit(-1);
}
if (pdcch_init(&pdcch, &regs, 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<cell.nof_ports;i++) {
ce[i] = vec_malloc(nof_re * sizeof(cf_t));
for (int j=0;j<nof_re;j++) {
ce[i][j] = 1.0;
}
}
pdcch_extract_llr(&pdcch, input_symbols, ce, 0, sf_idx, cfi);
uint32_t nof_locations;
if (rnti == SIRNTI) {
nof_locations = pdcch_common_locations(&pdcch, locations, MAX_CANDIDATES, cfi);
formats = common_formats;
nof_formats = nof_common_formats;
} else {
nof_locations = pdcch_ue_locations(&pdcch, locations, MAX_CANDIDATES, sf_idx, cfi, rnti);
formats = ue_formats;
nof_formats = nof_ue_formats;
}
uint16_t crc_rem;
dci_msg_t dci_msg;
for (int f=0;f<nof_formats;f++) {
for (i=0;i<nof_locations && crc_rem != rnti;i++) {
if (pdcch_decode_msg(&pdcch, &dci_msg, &locations[i], formats[f], &crc_rem)) {
fprintf(stderr, "Error decoding DCI msg\n");
return;
}
/* mexPrintf("Trying location (%d,%d), %s, CRC: 0x%x\n",
locations[i].ncce, locations[i].L, dci_format_string(formats[f]), crc_rem);
*/
}
}
if (nlhs >= 1) {
plhs[0] = mxCreateLogicalScalar(crc_rem == rnti);
}
int nof_bits = (regs_pdcch_nregs(&regs, 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(&regs);
for (i=0;i<cell.nof_ports;i++) {
free(ce[i]);
}
free(input_symbols);
return;
}

@ -199,7 +199,7 @@ int sync_sss(sync_t *q, cf_t *input, uint32_t peak_pos) {
sss_synch_set_N_id_2(&q->sss, 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);

@ -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;
}

@ -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')

@ -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

@ -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

@ -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

@ -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)

@ -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 <stdbool.h>
#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

@ -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)

@ -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 <complex.h>
#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<numelems;i++) {
__real__ tmp[i] = (float) inr[i];
if (ini) {
__imag__ tmp[i] = (float) ini[i];
}
}
*buffer = tmp;
return numelems;
} else {
return -1;
}
}
int mexutils_read_f(const mxArray *ptr, float **buffer) {
int numelems = mxGetNumberOfElements(ptr);
float *tmp = vec_malloc(numelems * sizeof(float));
if (tmp) {
double *inr=mxGetPr(ptr);
for (int i=0;i<numelems;i++) {
tmp[i] = (float) inr[i];
}
*buffer = tmp;
return numelems;
} else {
return -1;
}
}
int mexutils_write_cf(cf_t *buffer, mxArray **ptr, uint32_t nr, uint32_t nc) {
*ptr = mxCreateDoubleMatrix(nr, nc, mxCOMPLEX);
if (*ptr) {
double *outr = mxGetPr(*ptr);
double *outi = mxGetPi(*ptr);
for (int i=0;i<nr*nc;i++) {
outr[i] = (double) crealf(buffer[i]);
outi[i] = (double) cimagf(buffer[i]);
}
return nc*nr;
} else {
return -1;
}
}
int mexutils_write_f(float *buffer, mxArray **ptr, uint32_t nr, uint32_t nc) {
*ptr = mxCreateDoubleMatrix(nr, nc, mxREAL);
if (*ptr) {
double *outr = mxGetPr(*ptr);
for (int i=0;i<nr*nc;i++) {
outr[i] = (double) buffer[i];
}
return nc*nr;
} else {
return -1;
}
}
Loading…
Cancel
Save