Added HARQ support on PDSCH. Small changes on API.

master
ismagom 11 years ago
parent 561a2abd53
commit 01ca2dcf28

@ -59,6 +59,7 @@ pbch_t pbch;
pcfich_t pcfich;
pdcch_t pdcch;
pdsch_t pdsch;
pdsch_harq_t harq_process;
regs_t regs;
cf_t *sf_buffer = NULL, *output_buffer = NULL;
@ -191,10 +192,16 @@ void base_init() {
fprintf(stderr, "Error creating PDSCH object\n");
exit(-1);
}
if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n");
exit(-1);
}
}
void base_free() {
pdsch_harq_free(&harq_process);
pdsch_free(&pdsch);
pdcch_free(&pdcch);
regs_free(&regs);
@ -308,6 +315,11 @@ int main(int argc, char **argv) {
}
nf = 0;
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
exit(-1);
}
while (nf < nof_frames || nof_frames == -1) {
for (sf_idx = 0; sf_idx < NSUBFRAMES_X_FRAME; sf_idx++) {
@ -339,7 +351,7 @@ int main(int argc, char **argv) {
exit(-1);
}
pdsch_encode(&pdsch, data, sf_symbols, sf_idx, ra_dl.mcs, &prb_alloc);
pdsch_encode(&pdsch, data, sf_symbols, sf_idx, &harq_process, ra_dl.rv_idx);
/* Transform to OFDM symbols */
lte_ifft_run_sf(&ifft, sf_buffer, output_buffer);

@ -89,6 +89,7 @@ pbch_t pbch;
pcfich_t pcfich;
pdcch_t pdcch;
pdsch_t pdsch;
pdsch_harq_t harq_process;
regs_t regs;
lte_fft_t fft;
chest_t chest;
@ -356,6 +357,11 @@ int mib_init(phich_resources_t phich_resources, phich_length_t phich_length) {
return -1;
}
if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n");
return -1;
}
chest_set_nof_ports(&chest, cell.nof_ports);
mib_initiated = 1;
@ -469,7 +475,13 @@ int rx_run(cf_t *input, int sf_idx) {
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports,
cell.nof_prb<10?(cfi+1):cfi, CPNORM);
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, ra_dl.mcs, &prb_alloc)) {
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
break;
}
if (pdsch_decode(&pdsch, fft_buffer, ce, data, sf_idx, &harq_process, ra_dl.rv_idx)) {
pkt_errors++;
}
pkts_total++;

@ -132,38 +132,38 @@ LIBLTE_API enum band_geographical_area {
LIBLTE_API bool lte_cell_isvalid(lte_cell_t *cell);
LIBLTE_API const int lte_symbol_sz(int nof_prb);
LIBLTE_API int lte_symbol_sz(uint32_t nof_prb);
LIBLTE_API const int lte_sampling_freq_hz(int nof_prb);
LIBLTE_API int lte_sampling_freq_hz(uint32_t nof_prb);
LIBLTE_API int lte_re_x_prb(int ns,
int symbol,
int nof_ports,
int nof_symbols);
LIBLTE_API uint32_t lte_re_x_prb(uint32_t ns,
uint32_t symbol,
uint32_t nof_ports,
uint32_t nof_symbols);
LIBLTE_API int lte_voffset(int symbol_id,
int cell_id,
int nof_ports);
LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id,
uint32_t cell_id,
uint32_t nof_ports);
LIBLTE_API int lte_cb_size(int index);
LIBLTE_API int lte_cb_size(uint32_t index);
LIBLTE_API int lte_find_cb_index(int long_cb);
LIBLTE_API int lte_find_cb_index(uint32_t long_cb);
LIBLTE_API float lte_band_fd(int earfcn);
LIBLTE_API float lte_band_fd(uint32_t earfcn);
LIBLTE_API int lte_band_get_fd_band(int band,
LIBLTE_API int lte_band_get_fd_band(uint32_t band,
lte_earfcn_t *earfcn,
int earfcn_start,
int earfcn_end,
int max_elems);
uint32_t max_elems);
LIBLTE_API int lte_band_get_fd_band_all(int band,
LIBLTE_API int lte_band_get_fd_band_all(uint32_t band,
lte_earfcn_t *earfcn,
int max_nelems);
uint32_t max_nelems);
LIBLTE_API int lte_band_get_fd_region(enum band_geographical_area region,
lte_earfcn_t *earfcn,
int max_elems);
uint32_t max_elems);
LIBLTE_API int lte_str2mimotype(char *mimo_type_str,
lte_mimo_type_t *type);

@ -40,24 +40,17 @@
#include "liblte/config.h"
typedef struct LIBLTE_API {
int buffer_len;
char *buffer;
} rm_turbo_t;
LIBLTE_API int rm_turbo_init(rm_turbo_t *q,
uint32_t max_codeblock_len);
LIBLTE_API void rm_turbo_free(rm_turbo_t *q);
LIBLTE_API int rm_turbo_tx(rm_turbo_t *q,
LIBLTE_API int rm_turbo_tx(char *w_buff,
uint32_t buff_len,
char *input,
uint32_t in_len,
char *output,
uint32_t out_len,
uint32_t rv_idx);
LIBLTE_API int rm_turbo_rx(rm_turbo_t *q,
LIBLTE_API int rm_turbo_rx(float *w_buff,
uint32_t buff_len,
float *input,
uint32_t in_len,
float *output,
@ -66,11 +59,11 @@ LIBLTE_API int rm_turbo_rx(rm_turbo_t *q,
/* High-level API */
typedef struct LIBLTE_API {
rm_turbo_t q;
struct rm_turbo_init {
int direction;
} init;
void *input; // input type may be char or float depending on hard
void *input; // input type may be char or float depending on hard
int in_len;
struct rm_turbo_ctrl_in {
int E;

@ -43,10 +43,32 @@
#include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/regs.h"
#define TDEC_ITERATIONS 1
#define TDEC_ITERATIONS 1
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;
char **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 */
typedef struct LIBLTE_API {
lte_cell_t cell;
@ -55,23 +77,21 @@ typedef struct LIBLTE_API {
uint16_t rnti;
/* buffers */
// void buffers are shared for tx and rx
cf_t *ce[MAX_PORTS];
cf_t *pdsch_symbols[MAX_PORTS];
cf_t *pdsch_x[MAX_PORTS];
cf_t *pdsch_d;
char *pdsch_e_bits;
char *cb_in_b;
char *cb_out_b;
float *pdsch_llr;
float *pdsch_rm_f;
void *cb_in;
char *cb_out;
void *pdsch_e;
/* tx & rx objects */
modem_table_t mod[4];
demod_soft_t demod;
sequence_t seq_pdsch[NSUBFRAMES_X_FRAME];
tcod_t encoder;
tdec_t decoder;
rm_turbo_t rm_turbo;
tdec_t decoder;
crc_t crc_tb;
crc_t crc_cb;
}pdsch_t;
@ -82,20 +102,29 @@ LIBLTE_API int pdsch_init(pdsch_t *q,
LIBLTE_API void pdsch_free(pdsch_t *q);
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_free(pdsch_harq_t *p);
LIBLTE_API int pdsch_encode(pdsch_t *q,
char *data,
cf_t *sf_symbols[MAX_PORTS],
uint32_t nsubframe,
ra_mcs_t mcs,
ra_prb_t *prb_alloc);
uint32_t nsubframe,
pdsch_harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int pdsch_decode(pdsch_t *q,
cf_t *sf_symbols,
cf_t *ce[MAX_PORTS],
char *data,
uint32_t nsubframe,
ra_mcs_t mcs,
ra_prb_t *prb_alloc);
uint32_t nsubframe,
pdsch_harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int pdsch_get(pdsch_t *q,
cf_t *sf_symbols,

@ -147,6 +147,8 @@ LIBLTE_API uint32_t ra_nprb_dl(ra_pdsch_t *ra,
LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra,
uint32_t nof_prb);
LIBLTE_API uint32_t ra_mod_bits_x_symbol(ra_mod_t mod);
LIBLTE_API uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t idx,

@ -68,36 +68,41 @@ bool lte_cell_isvalid(lte_cell_t *cell) {
/*
* Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index
*/
int lte_cb_size(int index) {
if (index >= 0 && index < NOF_TC_CB_SIZES) {
int lte_cb_size(uint32_t index) {
if (index < NOF_TC_CB_SIZES) {
return tc_cb_sizes[index];
} else {
return -1;
return LIBLTE_ERROR;
}
}
/*
* Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212
*/
int lte_find_cb_index(int long_cb) {
int lte_find_cb_index(uint32_t long_cb) {
int j = 0;
while (j < NOF_TC_CB_SIZES && tc_cb_sizes[j] < long_cb) {
j++;
}
if (j == NOF_TC_CB_SIZES) {
return -1;
return LIBLTE_ERROR;
} else {
return j;
}
}
const int lte_sampling_freq_hz(int nof_prb) {
return 15000 * lte_symbol_sz(nof_prb);
int lte_sampling_freq_hz(uint32_t nof_prb) {
int n = lte_symbol_sz(nof_prb);
if (n == -1) {
return LIBLTE_ERROR;
} else {
return 15000 * n;
}
}
const int lte_symbol_sz(int nof_prb) {
int lte_symbol_sz(uint32_t nof_prb) {
if (nof_prb<=0) {
return -1;
return LIBLTE_ERROR;
}
if (nof_prb<=6) {
return 128;
@ -112,10 +117,10 @@ const int lte_symbol_sz(int nof_prb) {
} else if (nof_prb<=100) {
return 2048;
}
return -1;
return LIBLTE_ERROR;
}
int lte_voffset(int symbol_id, int cell_id, int nof_ports) {
uint32_t lte_voffset(uint32_t symbol_id, uint32_t cell_id, uint32_t nof_ports) {
if (nof_ports == 1 && symbol_id==0) {
return (cell_id+3) % 6;
} else {
@ -124,7 +129,7 @@ int lte_voffset(int symbol_id, int cell_id, int nof_ports) {
}
/* Returns the number of available RE per PRB */
int lte_re_x_prb(int ns, int symbol, int nof_ports, int nof_symbols) {
uint32_t lte_re_x_prb(uint32_t ns, uint32_t symbol, uint32_t nof_ports, uint32_t nof_symbols) {
if (symbol == 0) {
if (((ns % 2) == 0) || (ns == 1)) {
return RE_X_RB - 4;
@ -156,10 +161,10 @@ int lte_re_x_prb(int ns, int symbol, int nof_ports, int nof_symbols) {
struct lte_band {
int band;
uint32_t band;
float fd_low_mhz;
int earfcn_offset;
int earfcn_max;
uint32_t earfcn_offset;
uint32_t earfcn_max;
enum band_geographical_area area;
};
@ -204,9 +209,9 @@ int lte_str2mimotype(char *mimo_type_str, lte_mimo_type_t *type) {
} else if (!strcmp(mimo_type_str, "multiplex")) {
*type = SPATIAL_MULTIPLEX;
} else {
return -1;
return LIBLTE_ERROR;
}
return 0;
return LIBLTE_SUCCESS;
}
char *lte_mimotype2str(lte_mimo_type_t type) {
@ -221,12 +226,16 @@ char *lte_mimotype2str(lte_mimo_type_t type) {
return NULL;
}
float get_fd(struct lte_band *band, int earfcn) {
return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset);
float get_fd(struct lte_band *band, uint32_t earfcn) {
if (earfcn > band->earfcn_offset) {
return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset);
} else {
return 0.0;
}
}
float lte_band_fd(int earfcn) {
int i;
float lte_band_fd(uint32_t earfcn) {
uint32_t i;
i=0;
while(i < NOF_LTE_BANDS && lte_bands[i].earfcn_offset<earfcn) {
i++;
@ -238,27 +247,27 @@ float lte_band_fd(int earfcn) {
return get_fd(&lte_bands[i], earfcn);
}
int lte_band_get_fd_band_all(int band, lte_earfcn_t *earfcn, int max_elems) {
int lte_band_get_fd_band_all(uint32_t band, lte_earfcn_t *earfcn, uint32_t max_elems) {
return lte_band_get_fd_band(band, earfcn, -1, -1, max_elems);
}
int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int start_earfcn, int end_earfcn, int max_elems) {
int i, j;
int nof_earfcn;
int lte_band_get_fd_band(uint32_t band, lte_earfcn_t *earfcn, int start_earfcn, int end_earfcn, uint32_t max_elems) {
uint32_t i, j;
uint32_t nof_earfcn;
i=0;
while(i < NOF_LTE_BANDS && lte_bands[i].band != band) {
i++;
}
if (i == NOF_LTE_BANDS) {
fprintf(stderr, "Error: Invalid band %d\n", band);
return -1;
return LIBLTE_ERROR;
}
if (end_earfcn == -1) {
end_earfcn = lte_bands[i].earfcn_max;
} else {
if (end_earfcn > lte_bands[i].earfcn_max) {
fprintf(stderr, "Error: Invalid end earfcn %d. Max is %d\n", end_earfcn, lte_bands[i].earfcn_max);
return -1;
return LIBLTE_ERROR;
}
}
if (start_earfcn == -1) {
@ -266,7 +275,7 @@ int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int start_earfcn, int e
} else {
if (start_earfcn < lte_bands[i].earfcn_offset) {
fprintf(stderr, "Error: Invalid start earfcn %d. Min is %d\n", start_earfcn, lte_bands[i].earfcn_offset);
return -1;
return LIBLTE_ERROR;
}
}
nof_earfcn = end_earfcn - start_earfcn;
@ -278,11 +287,11 @@ int lte_band_get_fd_band(int band, lte_earfcn_t *earfcn, int start_earfcn, int e
earfcn[j].id = j + start_earfcn;
earfcn[j].fd = get_fd(&lte_bands[i], earfcn[j].id);
}
return j;
return (int) j;
}
int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, int max_elems) {
int i;
int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, uint32_t max_elems) {
uint32_t i;
int n;
int nof_fd = 0;
for (i=0;i<NOF_LTE_BANDS && max_elems > 0;i++) {
@ -292,7 +301,7 @@ int lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *ear
nof_fd += n;
max_elems -= n;
} else {
return -1;
return LIBLTE_ERROR;
}
}
}

@ -40,31 +40,22 @@
uint8_t RM_PERM_TC[NCOLS] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26,
6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 };
int rm_turbo_init(rm_turbo_t *q, uint32_t buffer_len) {
q->buffer_len = buffer_len;
q->buffer = malloc(buffer_len * sizeof(float));
if (!q->buffer) {
perror("malloc");
return -1;
}
return 0;
}
void rm_turbo_free(rm_turbo_t *q) {
if (q->buffer) {
free(q->buffer);
}
}
/* Turbo Code Rate Matching.
* 3GPP TS 36.212 v10.1.0 section 5.1.4.1
*
* If rv_idx==0, the circular buffer w_buff is filled with all redundancy versions and
* the corresponding version of length out_len is saved in the output buffer.
* Otherwise, the corresponding version is directly obtained from w_buff and saved into output.
*
* Note that calling this function with rv_idx!=0 without having called it first with rv_idx=0
* will produce unwanted results.
*
* TODO: Soft buffer size limitation according to UE category
*/
int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
int rm_turbo_tx(char *w_buff, uint32_t w_buff_len, char *input, uint32_t in_len, char *output,
uint32_t out_len, uint32_t rv_idx) {
char *tmp = (char*) q->buffer;
int ndummy, kidx;
int nrows, K_p;
@ -72,10 +63,10 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1;
K_p = nrows * NCOLS;
if (3 * K_p > q->buffer_len) {
if (3 * K_p > w_buff_len) {
fprintf(stderr,
"Input too large. Max input length including dummy bits is %d (3x%dx32, in_len %d)\n",
q->buffer_len, nrows, in_len);
w_buff_len, nrows, in_len);
return -1;
}
@ -84,33 +75,35 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
ndummy = 0;
}
/* Sub-block interleaver (5.1.4.1.1) and bit collection */
k = 0;
for (s = 0; s < 2; s++) {
for (j = 0; j < NCOLS; j++) {
for (i = 0; i < nrows; i++) {
if (s == 0) {
kidx = k % K_p;
} else {
kidx = K_p + 2 * (k % K_p);
if (rv_idx == 0) {
/* Sub-block interleaver (5.1.4.1.1) and bit collection */
k = 0;
for (s = 0; s < 2; s++) {
for (j = 0; j < NCOLS; j++) {
for (i = 0; i < nrows; i++) {
if (s == 0) {
kidx = k % K_p;
} else {
kidx = K_p + 2 * (k % K_p);
}
if (i * NCOLS + RM_PERM_TC[j] < ndummy) {
w_buff[kidx] = TX_NULL;
} else {
w_buff[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s];
}
k++;
}
if (i * NCOLS + RM_PERM_TC[j] < ndummy) {
tmp[kidx] = TX_NULL;
} else {
tmp[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s];
}
k++;
}
}
}
// d_k^(2) goes through special permutation
for (k = 0; k < K_p; k++) {
kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p;
if ((kidx - ndummy) < 0) {
tmp[K_p + 2 * k + 1] = TX_NULL;
} else {
tmp[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2];
// d_k^(2) goes through special permutation
for (k = 0; k < K_p; k++) {
kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p;
if ((kidx - ndummy) < 0) {
w_buff[K_p + 2 * k + 1] = TX_NULL;
} else {
w_buff[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2];
}
}
}
@ -123,8 +116,8 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
j = 0;
while (k < out_len) {
if (tmp[(k0 + j) % N_cb] != TX_NULL) {
output[k] = tmp[(k0 + j) % N_cb];
if (w_buff[(k0 + j) % N_cb] != TX_NULL) {
output[k] = w_buff[(k0 + j) % N_cb];
k++;
}
j++;
@ -134,8 +127,11 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
/* Undoes Turbo Code Rate Matching.
* 3GPP TS 36.212 v10.1.0 section 5.1.4.1
*
* If rv_idx==0, the w_buff circular buffer is initialized. Every subsequent call
* with rv_idx!=0 will soft-combine the LLRs from input with w_buff
*/
int rm_turbo_rx(rm_turbo_t *q, 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) {
int nrows, ndummy, K_p, k0, N_cb, jp, kidx;
@ -143,14 +139,12 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
int d_i, d_j;
bool isdummy;
float *tmp = (float*) q->buffer;
nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1;
K_p = nrows * NCOLS;
if (3 * K_p > q->buffer_len) {
if (3 * K_p > w_buff_len) {
fprintf(stderr,
"Input too large. Max output length including dummy bits is %d (3x%dx32, in_len %d)\n",
q->buffer_len, nrows, out_len);
w_buff_len, nrows, out_len);
return -1;
}
@ -159,12 +153,14 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
ndummy = 0;
}
for (i = 0; i < 3 * K_p; i++) {
tmp[i] = RX_NULL;
if (rv_idx == 0) {
for (i = 0; i < 3 * K_p; i++) {
w_buff[i] = RX_NULL;
}
}
/* Undo bit collection. Account for dummy bits */
N_cb = 3 * K_p; // TODO: Soft buffer size limitation
N_cb = 3 * K_p; // TODO: Soft buffer size limitation
k0 = nrows
* (2 * (uint32_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2);
@ -197,10 +193,10 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
}
if (!isdummy) {
if (tmp[jp] == RX_NULL) {
tmp[jp] = input[k];
if (w_buff[jp] == RX_NULL) {
w_buff[jp] = input[k];
} else if (input[k] != RX_NULL) {
tmp[jp] += input[k]; /* soft combine LLRs */
w_buff[jp] += input[k]; /* soft combine LLRs */
}
k++;
}
@ -215,15 +211,14 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
if (j != 2) {
kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i);
} else {
k = (i + ndummy - 1) % K_p;
if (k < 0)
k += K_p;
kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p;
kidx = 2 * kidx + K_p + 1;
}
if (tmp[kidx] != RX_NULL) {
output[i * 3 + j] = tmp[kidx];
if (w_buff[kidx] != RX_NULL) {
output[i * 3 + j] = w_buff[kidx];
} else {
output[i * 3 + j] = 0;
}
@ -235,25 +230,15 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
/** High-level API */
int rm_turbo_initialize(rm_turbo_hl* h) {
return rm_turbo_init(&h->q, 7000);
return 0;
}
/** This function can be called in a subframe (1ms) basis */
int rm_turbo_work(rm_turbo_hl* hl) {
if (hl->init.direction) {
rm_turbo_tx(&hl->q, hl->input, hl->in_len, hl->output, hl->ctrl_in.E,
hl->ctrl_in.rv_idx);
hl->out_len = hl->ctrl_in.E;
} else {
rm_turbo_rx(&hl->q, hl->input, hl->in_len, hl->output, hl->ctrl_in.S,
hl->ctrl_in.rv_idx);
hl->out_len = hl->ctrl_in.S;
}
return 0;
}
int rm_turbo_stop(rm_turbo_hl* hl) {
rm_turbo_free(&hl->q);
return 0;
}

@ -73,10 +73,9 @@ void parse_args(int argc, char **argv) {
int main(int argc, char **argv) {
int i;
char *bits, *rm_bits;
float *rm_symbols, *unrm_symbols;
char *bits, *rm_bits, *w_buff_c;
float *rm_symbols, *unrm_symbols, *w_buff_f;
int nof_errors;
rm_turbo_t rm_turbo;
parse_args(argc, argv);
@ -85,6 +84,11 @@ int main(int argc, char **argv) {
perror("malloc");
exit(-1);
}
w_buff_c = malloc(sizeof(char) * nof_tx_bits * 10);
if (!w_buff_c) {
perror("malloc");
exit(-1);
}
rm_bits = malloc(sizeof(char) * nof_rx_bits);
if (!rm_bits) {
perror("malloc");
@ -95,6 +99,11 @@ int main(int argc, char **argv) {
perror("malloc");
exit(-1);
}
w_buff_f = malloc(sizeof(float) * nof_rx_bits * 10);
if (!w_buff_c) {
perror("malloc");
exit(-1);
}
unrm_symbols = malloc(sizeof(float) * nof_tx_bits);
if (!unrm_symbols) {
perror("malloc");
@ -105,15 +114,13 @@ int main(int argc, char **argv) {
bits[i] = rand() % 2;
}
rm_turbo_init(&rm_turbo, 2000);
rm_turbo_tx(&rm_turbo, 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);
for (i = 0; i < nof_rx_bits; i++) {
rm_symbols[i] = (float) rm_bits[i] ? 1 : -1;
}
rm_turbo_rx(&rm_turbo, 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);
nof_errors = 0;
@ -123,8 +130,6 @@ int main(int argc, char **argv) {
}
}
rm_turbo_free(&rm_turbo);
free(bits);
free(rm_bits);
free(rm_symbols);

@ -50,14 +50,7 @@ const enum modem_std modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
struct cb_segm {
int F;
int C;
int K1;
int K2;
int C1;
int C2;
};
int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc,
uint32_t nsubframe, bool put) {
@ -182,7 +175,7 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
int i;
if (q != NULL &&
if (q != NULL &&
lte_cell_isvalid(&cell))
{
@ -191,7 +184,7 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
q->cell = cell;
q->rnti = user_rnti;
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
@ -225,36 +218,24 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
if (tdec_init(&q->decoder, MAX_LONG_CB)) {
goto clean;
}
if (rm_turbo_init(&q->rm_turbo, 3 * MAX_LONG_CB)) {
goto clean;
}
q->cb_in_b = malloc(sizeof(char) * MAX_LONG_CB);
if (!q->cb_in_b) {
goto clean;
}
q->cb_out_b = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out_b) {
// Allocate floats for reception (LLRs)
q->cb_in = malloc(sizeof(float) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->pdsch_rm_f = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12));
if (!q->pdsch_rm_f) {
goto clean;
}
q->pdsch_e_bits = malloc(
sizeof(char) * q->max_symbols * q->mod[3].nbits_x_symbol);
if (!q->pdsch_e_bits) {
q->cb_out = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out) {
goto clean;
}
q->pdsch_llr = malloc(
sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol);
if (!q->pdsch_llr) {
// Allocate floats for reception (LLRs)
q->pdsch_e = malloc(sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol);
if (!q->pdsch_e) {
goto clean;
}
q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->pdsch_d) {
goto clean;
@ -287,20 +268,14 @@ int pdsch_init(pdsch_t *q, uint16_t user_rnti, lte_cell_t cell) {
void pdsch_free(pdsch_t *q) {
int i;
if (q->cb_in_b) {
free(q->cb_in_b);
}
if (q->cb_out_b) {
free(q->cb_out_b);
if (q->cb_in) {
free(q->cb_in);
}
if (q->pdsch_e_bits) {
free(q->pdsch_e_bits);
if (q->cb_out) {
free(q->cb_out);
}
if (q->pdsch_rm_f) {
free(q->pdsch_rm_f);
}
if (q->pdsch_llr) {
free(q->pdsch_llr);
if (q->pdsch_e) {
free(q->pdsch_e);
}
if (q->pdsch_d) {
free(q->pdsch_d);
@ -326,14 +301,14 @@ void pdsch_free(pdsch_t *q) {
}
tdec_free(&q->decoder);
tcod_free(&q->encoder);
rm_turbo_free(&q->rm_turbo);
}
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
void codeblock_segmentation(struct cb_segm *s, int tbs) {
int Bp, B, idx1;
int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1;
int ret;
B = tbs + 24;
/* Calculate CB sizes */
@ -341,68 +316,186 @@ void codeblock_segmentation(struct cb_segm *s, int tbs) {
s->C = 1;
Bp = B;
} else {
s->C = (int) ceilf((float) B / (6114 - 24));
s->C = (uint32_t) ceilf((float) B / (6114 - 24));
Bp = B + 24 * s->C;
}
idx1 = lte_find_cb_index(Bp / s->C);
s->K1 = lte_cb_size(idx1);
if (s->C == 1) {
s->K2 = 0;
s->C2 = 0;
s->C1 = 1;
} else {
s->K2 = lte_cb_size(idx1 - 1);
s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2);
s->C1 = s->C - s->C2;
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 / (6114 - 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(char*) * p->max_cb);
if (!p->pdsch_w_buff_c) {
perror("malloc");
return LIBLTE_ERROR;
}
// We add 50 % larger buffer to the maximum expected bits per subframe
// FIXME: Use HARQ buffer limitation based on UE category
p->w_buff_size = p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp) * 6 * 2 / p->max_cb;
for (i=0;i<p->max_cb;i++) {
p->pdsch_w_buff_f[i] = 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] = malloc(sizeof(char) * 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));
}
}
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 &&
mcs.mod != MOD_NULL)
{
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 * ra_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;
}
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;
}
/* Decode a transport block according to 36.212 5.3.2
*
*/
int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_t rv_idx) {
int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
char parity[24];
char *p_parity = parity;
uint32_t par_rx, par_tx;
int i;
int cb_len, rp, wp, rlen, F, n_e;
struct cb_segm cbs;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
float *e_bits = q->pdsch_e;
if (q != NULL &&
data != NULL &&
nb_e < q->max_symbols * q->mod[3].nbits_x_symbol)
{
/* Compute CB segmentation for this TBS */
codeblock_segmentation(&cbs, tbs);
rp = 0;
rp = 0;
wp = 0;
for (i = 0; i < cbs.C; i++) {
for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read/write lengths */
if (i < cbs.C - cbs.C2) {
cb_len = cbs.K1;
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = cbs.K2;
cb_len = harq_process->cb_segm.K2;
}
if (cbs.C == 1) {
if (harq_process->cb_segm.C == 1) {
rlen = cb_len;
} else {
rlen = cb_len - 24;
}
if (i == 0) {
F = cbs.F;
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i < cbs.C - 1) {
n_e = nb_e / cbs.C;
if (i < harq_process->cb_segm.C - 1) {
n_e = nb_e / harq_process->cb_segm.C;
} else {
n_e = nb_e - rp;
}
@ -411,27 +504,34 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_
cb_len, rlen - F, wp, rp, F, n_e);
/* Rate Unmatching */
rm_turbo_rx(&q->rm_turbo, &q->pdsch_llr[rp], n_e, q->pdsch_rm_f,
3 * cb_len + 12, rv_idx);
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_in, 3 * cb_len + 12, rv_idx)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Turbo Decoding */
tdec_run_all(&q->decoder, q->pdsch_rm_f, q->cb_in_b, TDEC_ITERATIONS,
tdec_run_all(&q->decoder, (float*) q->cb_in, q->cb_out, TDEC_ITERATIONS,
cb_len);
if (cbs.C > 1) {
/* Check Codeblock CRC */
//crc_attach(&q->crc_cb, q->pdsch_b[wp], cb_len);
if (harq_process->cb_segm.C > 1) {
/* Check Codeblock CRC and stop early if incorrect */
if (crc_checksum(&q->crc_cb, q->cb_out, cb_len)) {
INFO("Error in CB#%d\n",i);
return LIBLTE_ERROR;
}
}
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < cbs.C - 1) {
memcpy(&data[wp], &q->cb_in_b[F], (rlen - F) * sizeof(char));
if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_out[F], (rlen - F) * sizeof(char));
} else {
INFO("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_b[F], (rlen - F - 24) * sizeof(char));
memcpy(parity, &q->cb_in_b[rlen - 24], 24 * sizeof(char));
memcpy(&data[wp], &q->cb_out[F], (rlen - F - 24) * sizeof(char));
memcpy(parity, &q->cb_out[rlen - 24], 24 * sizeof(char));
}
/* Set read/write pointers */
@ -463,39 +563,29 @@ int pdsch_decode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_
/** Decodes the PDSCH from the received symbols
*/
int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
uint32_t subframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, uint32_t subframe,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
/* Set pointers for layermapping & precoding */
int i, n;
uint32_t i, n;
cf_t *x[MAX_LAYERS];
int nof_symbols, nof_bits, nof_bits_e;
uint32_t nof_symbols, nof_bits, nof_bits_e;
if (q != NULL &&
sf_symbols != NULL &&
data != NULL &&
subframe < 10 &&
prb_alloc != NULL)
if (q != NULL &&
sf_symbols != NULL &&
data != NULL &&
subframe < 10 &&
harq_process != NULL)
{
nof_bits = mcs.tbs;
nof_symbols = prb_alloc->re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol;
nof_bits = harq_process->mcs.tbs;
nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol;
if (nof_bits > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e);
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_symbols > q->max_symbols) {
fprintf(stderr,
"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("Decoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n",
subframe, mcs.mod, nof_bits, nof_symbols, nof_bits_e);
subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e);
/* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) {
@ -504,7 +594,7 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
/* extract symbols */
n = pdsch_get(q, sf_symbols, q->pdsch_symbols[0], prb_alloc, subframe);
n = pdsch_get(q, sf_symbols, q->pdsch_symbols[0], &harq_process->prb_alloc, subframe);
if (n != nof_symbols) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n);
return LIBLTE_ERROR;
@ -512,7 +602,7 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
/* extract channel estimates */
for (i = 0; i < q->cell.nof_ports; i++) {
n = pdsch_get(q, ce[i], q->ce[i], prb_alloc, subframe);
n = pdsch_get(q, ce[i], q->ce[i], &harq_process->prb_alloc, subframe);
if (n != nof_symbols) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n);
return LIBLTE_ERROR;
@ -531,15 +621,18 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
nof_symbols / q->cell.nof_ports);
}
/* demodulate symbols */
demod_soft_sigma_set(&q->demod, 2.0 / q->mod[mcs.mod - 1].nbits_x_symbol);
demod_soft_table_set(&q->demod, &q->mod[mcs.mod - 1]);
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_llr, nof_symbols);
/* 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, 2.0 / q->mod[harq_process->mcs.mod - 1].nbits_x_symbol);
demod_soft_table_set(&q->demod, &q->mod[harq_process->mcs.mod - 1]);
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_e, nof_symbols);
/* descramble */
scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_llr, 0, nof_bits_e);
scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 0, nof_bits_e);
return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, 0);
return pdsch_decode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx);
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
@ -548,63 +641,65 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data,
/* Encode a transport block according to 36.212 5.3.2
*
*/
int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_t rv_idx) {
int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
char parity[24];
char *p_parity = parity;
unsigned int par;
int i;
int cb_len, rp, wp, rlen, F, n_e;
struct cb_segm cbs;
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
char *cb_in = q->cb_in;
char *e_bits = q->pdsch_e;
if (q != NULL &&
data != NULL &&
nb_e < q->max_symbols * q->mod[3].nbits_x_symbol)
{
/* Compute CB segmentation */
codeblock_segmentation(&cbs, tbs);
/* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs);
if (rv_idx == 0) {
/* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs);
/* parity bits will be appended later */
bit_pack(par, &p_parity, 24);
/* 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);
}
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 < cbs.F; i++) {
q->cb_in_b[i] = LTE_NULL_BIT;
/* Add filler bits to the new data buffer */
for (i = 0; i < harq_process->cb_segm.F; i++) {
cb_in[i] = LTE_NULL_BIT;
}
}
wp = 0;
rp = 0;
for (i = 0; i < cbs.C; i++) {
for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read lengths */
if (i < cbs.C - cbs.C2) {
cb_len = cbs.K1;
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = cbs.K2;
cb_len = harq_process->cb_segm.K2;
}
if (cbs.C > 1) {
if (harq_process->cb_segm.C > 1) {
rlen = cb_len - 24;
} else {
rlen = cb_len;
}
if (i == 0) {
F = cbs.F;
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i < cbs.C - 1) {
n_e = nb_e / cbs.C;
if (i < harq_process->cb_segm.C - 1) {
n_e = nb_e / harq_process->cb_segm.C;
} else {
n_e = nb_e - wp;
}
@ -612,33 +707,37 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_
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);
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < cbs.C - 1) {
memcpy(&q->cb_in_b[F], &data[rp], (rlen - F) * sizeof(char));
} 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_b[F], &data[rp], (rlen - F - 24) * sizeof(char));
memcpy(&q->cb_in_b[rlen - 24], parity, 24 * sizeof(char));
}
if (cbs.C > 1) {
/* Attach Codeblock CRC */
crc_attach(&q->crc_cb, q->cb_in_b, rlen);
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d Len=%d: ", i, cb_len);
vec_fprint_b(stdout, q->cb_in_b, cb_len);
if (rv_idx == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&cb_in[F], &data[rp], (rlen - F) * sizeof(char));
} 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(&cb_in[F], &data[rp], (rlen - F - 24) * sizeof(char));
memcpy(&cb_in[rlen - 24], parity, 24 * sizeof(char));
}
if (harq_process->cb_segm.C > 1) {
/* Attach Codeblock CRC */
crc_attach(&q->crc_cb, cb_in, rlen);
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d Len=%d: ", i, cb_len);
vec_fprint_b(stdout, cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, cb_in, q->cb_out, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, q->cb_in_b, q->cb_out_b, cb_len);
/* Rate matching */
rm_turbo_tx(&q->rm_turbo, q->cb_out_b, 3 * cb_len + 12,
&q->pdsch_e_bits[wp], n_e, rv_idx);
if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
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);
@ -655,17 +754,18 @@ int pdsch_encode_tb(pdsch_t *q, char *data, uint32_t tbs, uint32_t nb_e, uint32_
/** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission
*/
int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS],
uint32_t subframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
int i;
uint32_t nof_symbols, nof_bits, nof_bits_e;
/* Set pointers for layermapping & precoding */
cf_t *x[MAX_LAYERS];
if (q != NULL &&
if (q != NULL &&
data != NULL &&
subframe < 10 &&
prb_alloc != NULL)
harq_process != NULL)
{
for (i=0;i<q->cell.nof_ports;i++) {
@ -674,10 +774,14 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS],
}
}
nof_bits = mcs.tbs;
nof_symbols = prb_alloc->re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol;
nof_bits = harq_process->mcs.tbs;
nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[harq_process->mcs.mod - 1].nbits_x_symbol;
if (harq_process->mcs.mod == MOD_NULL) {
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_bits > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e);
return LIBLTE_ERROR_INVALID_INPUTS;
@ -690,8 +794,8 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS],
return LIBLTE_ERROR_INVALID_INPUTS;
}
INFO("Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n",
subframe, mcs.mod, nof_bits, nof_symbols, nof_bits_e);
INFO("Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e, rv_idx);
/* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) {
@ -699,14 +803,14 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS],
}
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, 0)) {
if (pdsch_encode_tb(q, data, nof_bits, nof_bits_e, harq_process, rv_idx)) {
fprintf(stderr, "Error encoding TB\n");
return LIBLTE_ERROR;
}
scrambling_b_offset(&q->seq_pdsch[subframe], q->pdsch_e_bits, 0, nof_bits_e);
scrambling_b_offset(&q->seq_pdsch[subframe], (char*) q->pdsch_e, 0, nof_bits_e);
mod_modulate(&q->mod[mcs.mod - 1], q->pdsch_e_bits, q->pdsch_d, nof_bits_e);
mod_modulate(&q->mod[harq_process->mcs.mod - 1], (char*) q->pdsch_e, q->pdsch_d, nof_bits_e);
/* TODO: only diversity supported */
if (q->cell.nof_ports > 1) {
@ -719,7 +823,7 @@ int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS],
/* mapping to resource elements */
for (i = 0; i < q->cell.nof_ports; i++) {
pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], prb_alloc, subframe);
pdsch_put(q, q->pdsch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe);
}
return LIBLTE_SUCCESS;
} else {

@ -405,6 +405,23 @@ uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs) {
}
}
uint32_t ra_mod_bits_x_symbol(ra_mod_t mod) {
switch(mod) {
case BPSK:
return 1;
case QPSK:
return 2;
case QAM16:
return 4;
case QAM64:
return 6;
default:
return 0;
}
return 0;
}
/* Converts MCS index to ra_mcs_t structure for Downlink as defined inTable 7.1.7.1-1 on 36.213 */
int ra_mcs_from_idx_dl(uint32_t idx, ra_mcs_t *mcs) {
if (idx < 10) {

@ -92,6 +92,7 @@ TARGET_LINK_LIBRARIES(pdsch_re_test lte_phy)
ADD_TEST(pdsch_re_test pdsch_re_test)
ADD_TEST(pdsch_test pdsch_test -l 50000 -m 4 -n 110)
ADD_TEST(pdsch_test pdsch_test -l 500 -m 2 -n 50 -r 2)
########################################################################
# FILE TEST

@ -54,6 +54,7 @@ FILE *fmatlab = NULL;
filesource_t fsrc;
pdcch_t pdcch;
pdsch_t pdsch;
pdsch_harq_t harq_process;
cf_t *input_buffer, *fft_buffer, *ce[MAX_PORTS];
regs_t regs;
lte_fft_t fft;
@ -186,6 +187,11 @@ int base_init() {
fprintf(stderr, "Error creating PDSCH object\n");
exit(-1);
}
if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n");
exit(-1);
}
DEBUG("Memory init OK\n",0);
return 0;
@ -211,6 +217,7 @@ void base_free() {
pdcch_free(&pdcch);
pdsch_free(&pdsch);
pdsch_harq_free(&harq_process);
regs_free(&regs);
}
@ -298,7 +305,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Can't get DCI message type\n");
goto goout;
}
printf("MSG %d: ",i);
dci_msg_type_fprint(stdout, type);
switch(type.type) {
case PDSCH_SCHED:
@ -325,7 +332,11 @@ int main(int argc, char **argv) {
}
ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports, cell.nof_prb<10?(cfi+1):cfi, cell.cp);
if (pdsch_decode(&pdsch, fft_buffer, ce, data, nof_frames%10, ra_dl.mcs, &prb_alloc)) {
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto goout;
}
if (pdsch_decode(&pdsch, fft_buffer, ce, data, nof_frames%10, &harq_process, ra_dl.rv_idx)) {
fprintf(stderr, "Error decoding PDSCH\n");
goto goout;
} else {

@ -42,15 +42,17 @@ lte_cell_t cell = {
};
uint32_t cfi = 1;
uint32_t tbs = -1;
uint32_t tbs = 0;
uint32_t subframe = 1;
ra_mod_t modulation = BPSK;
uint32_t rv_idx = 0;
void usage(char *prog) {
printf("Usage: %s [cell.cpnfvmt] -l TBS \n", prog);
printf("Usage: %s [cpsrnfvmt] -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-r rv_idx [Default %d]\n", rv_idx);
printf("\t-f cfi [Default %d]\n", cfi);
printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports);
printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb);
@ -59,7 +61,7 @@ void usage(char *prog) {
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "lcell.cpnfvmts")) != -1) {
while ((opt = getopt(argc, argv, "lcpnfvmtsr")) != -1) {
switch(opt) {
case 'm':
switch(atoi(argv[optind])) {
@ -84,6 +86,9 @@ void parse_args(int argc, char **argv) {
case 's':
subframe = atoi(argv[optind]);
break;
case 'r':
rv_idx = atoi(argv[optind]);
break;
case 'l':
tbs = atoi(argv[optind]);
break;
@ -104,7 +109,7 @@ void parse_args(int argc, char **argv) {
exit(-1);
}
}
if (tbs == -1) {
if (tbs == 0) {
usage(argv[0]);
exit(-1);
}
@ -112,15 +117,17 @@ void parse_args(int argc, char **argv) {
int main(int argc, char **argv) {
pdsch_t pdsch;
int i, j;
uint32_t i, j;
char *data = NULL;
cf_t *ce[MAX_PORTS];
int nof_re;
uint32_t nof_re;
cf_t *slot_symbols[MAX_PORTS];
int ret = -1;
struct timeval t[3];
ra_mcs_t mcs;
ra_prb_t prb_alloc;
pdsch_harq_t harq_process;
uint32_t rv;
parse_args(argc,argv);
@ -163,32 +170,50 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error creating PDSCH object\n");
goto quit;
}
if (pdsch_harq_init(&harq_process, &pdsch)) {
fprintf(stderr, "Error initiating HARQ process\n");
goto quit;
}
if (pdsch_harq_setup(&harq_process, mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto quit;
}
for (i=0;i<mcs.tbs;i++) {
data[i] = rand()%2;
}
pdsch_encode(&pdsch, data, slot_symbols, subframe, mcs, &prb_alloc);
for (rv=0;rv<=rv_idx;rv++) {
printf("Encoding rv_idx=%d\n",rv);
if (pdsch_encode(&pdsch, data, slot_symbols, subframe, &harq_process, rv)) {
fprintf(stderr, "Error encoding PDSCH\n");
goto quit;
}
/* combine outputs */
for (i=0;i<cell.nof_ports;i++) {
for (j=0;j<nof_re;j++) {
if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j];
/* combine outputs */
for (i=0;i<cell.nof_ports;i++) {
for (j=0;j<nof_re;j++) {
if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j];
}
ce[i][j] = 1;
}
ce[i][j] = 1;
}
}
gettimeofday(&t[1], NULL);
int r = pdsch_decode(&pdsch, slot_symbols[0], ce, data, subframe, mcs, &prb_alloc);
gettimeofday(&t[2], NULL);
get_time_interval(t);
if (r) {
printf("Error decoding\n");
ret = -1;
} 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);
gettimeofday(&t[1], NULL);
int r = pdsch_decode(&pdsch, slot_symbols[0], ce, data, subframe, &harq_process, rv);
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:

Loading…
Cancel
Save