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

@ -89,6 +89,7 @@ pbch_t pbch;
pcfich_t pcfich; pcfich_t pcfich;
pdcch_t pdcch; pdcch_t pdcch;
pdsch_t pdsch; pdsch_t pdsch;
pdsch_harq_t harq_process;
regs_t regs; regs_t regs;
lte_fft_t fft; lte_fft_t fft;
chest_t chest; chest_t chest;
@ -356,6 +357,11 @@ int mib_init(phich_resources_t phich_resources, phich_length_t phich_length) {
return -1; 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); chest_set_nof_ports(&chest, cell.nof_ports);
mib_initiated = 1; 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, ra_prb_get_re_dl(&prb_alloc, cell.nof_prb, cell.nof_ports,
cell.nof_prb<10?(cfi+1):cfi, CPNORM); 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++; pkt_errors++;
} }
pkts_total++; pkts_total++;

@ -132,38 +132,38 @@ LIBLTE_API enum band_geographical_area {
LIBLTE_API bool lte_cell_isvalid(lte_cell_t *cell); 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, LIBLTE_API uint32_t lte_re_x_prb(uint32_t ns,
int symbol, uint32_t symbol,
int nof_ports, uint32_t nof_ports,
int nof_symbols); uint32_t nof_symbols);
LIBLTE_API int lte_voffset(int symbol_id, LIBLTE_API uint32_t lte_voffset(uint32_t symbol_id,
int cell_id, uint32_t cell_id,
int nof_ports); 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, lte_earfcn_t *earfcn,
int earfcn_start, int earfcn_start,
int earfcn_end, 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, lte_earfcn_t *earfcn,
int max_nelems); uint32_t max_nelems);
LIBLTE_API int lte_band_get_fd_region(enum band_geographical_area region, LIBLTE_API int lte_band_get_fd_region(enum band_geographical_area region,
lte_earfcn_t *earfcn, lte_earfcn_t *earfcn,
int max_elems); uint32_t max_elems);
LIBLTE_API int lte_str2mimotype(char *mimo_type_str, LIBLTE_API int lte_str2mimotype(char *mimo_type_str,
lte_mimo_type_t *type); lte_mimo_type_t *type);

@ -40,24 +40,17 @@
#include "liblte/config.h" #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, char *input,
uint32_t in_len, uint32_t in_len,
char *output, char *output,
uint32_t out_len, uint32_t out_len,
uint32_t rv_idx); 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, float *input,
uint32_t in_len, uint32_t in_len,
float *output, float *output,
@ -66,11 +59,11 @@ LIBLTE_API int rm_turbo_rx(rm_turbo_t *q,
/* High-level API */ /* High-level API */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
rm_turbo_t q;
struct rm_turbo_init { struct rm_turbo_init {
int direction; int direction;
} init; } 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; int in_len;
struct rm_turbo_ctrl_in { struct rm_turbo_ctrl_in {
int E; int E;

@ -43,10 +43,32 @@
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/regs.h"
#define TDEC_ITERATIONS 1 #define TDEC_ITERATIONS 1
typedef _Complex float cf_t; typedef _Complex float cf_t;
typedef struct LIBLTE_API {
ra_mcs_t mcs;
ra_prb_t prb_alloc;
lte_cell_t cell;
uint32_t max_cb;
uint32_t w_buff_size;
float **pdsch_w_buff_f;
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 */ /* PDSCH object */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
lte_cell_t cell; lte_cell_t cell;
@ -55,23 +77,21 @@ typedef struct LIBLTE_API {
uint16_t rnti; uint16_t rnti;
/* buffers */ /* buffers */
// void buffers are shared for tx and rx
cf_t *ce[MAX_PORTS]; cf_t *ce[MAX_PORTS];
cf_t *pdsch_symbols[MAX_PORTS]; cf_t *pdsch_symbols[MAX_PORTS];
cf_t *pdsch_x[MAX_PORTS]; cf_t *pdsch_x[MAX_PORTS];
cf_t *pdsch_d; cf_t *pdsch_d;
char *pdsch_e_bits; void *cb_in;
char *cb_in_b; char *cb_out;
char *cb_out_b; void *pdsch_e;
float *pdsch_llr;
float *pdsch_rm_f;
/* tx & rx objects */ /* tx & rx objects */
modem_table_t mod[4]; modem_table_t mod[4];
demod_soft_t demod; demod_soft_t demod;
sequence_t seq_pdsch[NSUBFRAMES_X_FRAME]; sequence_t seq_pdsch[NSUBFRAMES_X_FRAME];
tcod_t encoder; tcod_t encoder;
tdec_t decoder; tdec_t decoder;
rm_turbo_t rm_turbo;
crc_t crc_tb; crc_t crc_tb;
crc_t crc_cb; crc_t crc_cb;
}pdsch_t; }pdsch_t;
@ -82,20 +102,29 @@ LIBLTE_API int pdsch_init(pdsch_t *q,
LIBLTE_API void pdsch_free(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, LIBLTE_API int pdsch_encode(pdsch_t *q,
char *data, char *data,
cf_t *sf_symbols[MAX_PORTS], cf_t *sf_symbols[MAX_PORTS],
uint32_t nsubframe, uint32_t nsubframe,
ra_mcs_t mcs, pdsch_harq_t *harq_process,
ra_prb_t *prb_alloc); uint32_t rv_idx);
LIBLTE_API int pdsch_decode(pdsch_t *q, LIBLTE_API int pdsch_decode(pdsch_t *q,
cf_t *sf_symbols, cf_t *sf_symbols,
cf_t *ce[MAX_PORTS], cf_t *ce[MAX_PORTS],
char *data, char *data,
uint32_t nsubframe, uint32_t nsubframe,
ra_mcs_t mcs, pdsch_harq_t *harq_process,
ra_prb_t *prb_alloc); uint32_t rv_idx);
LIBLTE_API int pdsch_get(pdsch_t *q, LIBLTE_API int pdsch_get(pdsch_t *q,
cf_t *sf_symbols, 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, LIBLTE_API uint32_t ra_nprb_ul(ra_pusch_t *ra,
uint32_t nof_prb); 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 uint32_t ra_mcs_to_table_idx(ra_mcs_t *mcs);
LIBLTE_API int ra_mcs_from_idx_dl(uint32_t idx, 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 * Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index
*/ */
int lte_cb_size(int index) { int lte_cb_size(uint32_t index) {
if (index >= 0 && index < NOF_TC_CB_SIZES) { if (index < NOF_TC_CB_SIZES) {
return tc_cb_sizes[index]; return tc_cb_sizes[index];
} else { } else {
return -1; return LIBLTE_ERROR;
} }
} }
/* /*
* Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212 * Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212
*/ */
int lte_find_cb_index(int long_cb) { int lte_find_cb_index(uint32_t long_cb) {
int j = 0; int j = 0;
while (j < NOF_TC_CB_SIZES && tc_cb_sizes[j] < long_cb) { while (j < NOF_TC_CB_SIZES && tc_cb_sizes[j] < long_cb) {
j++; j++;
} }
if (j == NOF_TC_CB_SIZES) { if (j == NOF_TC_CB_SIZES) {
return -1; return LIBLTE_ERROR;
} else { } else {
return j; return j;
} }
} }
const int lte_sampling_freq_hz(int nof_prb) { int lte_sampling_freq_hz(uint32_t nof_prb) {
return 15000 * lte_symbol_sz(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) { if (nof_prb<=0) {
return -1; return LIBLTE_ERROR;
} }
if (nof_prb<=6) { if (nof_prb<=6) {
return 128; return 128;
@ -112,10 +117,10 @@ const int lte_symbol_sz(int nof_prb) {
} else if (nof_prb<=100) { } else if (nof_prb<=100) {
return 2048; 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) { if (nof_ports == 1 && symbol_id==0) {
return (cell_id+3) % 6; return (cell_id+3) % 6;
} else { } 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 */ /* 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 (symbol == 0) {
if (((ns % 2) == 0) || (ns == 1)) { if (((ns % 2) == 0) || (ns == 1)) {
return RE_X_RB - 4; 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 { struct lte_band {
int band; uint32_t band;
float fd_low_mhz; float fd_low_mhz;
int earfcn_offset; uint32_t earfcn_offset;
int earfcn_max; uint32_t earfcn_max;
enum band_geographical_area area; 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")) { } else if (!strcmp(mimo_type_str, "multiplex")) {
*type = SPATIAL_MULTIPLEX; *type = SPATIAL_MULTIPLEX;
} else { } else {
return -1; return LIBLTE_ERROR;
} }
return 0; return LIBLTE_SUCCESS;
} }
char *lte_mimotype2str(lte_mimo_type_t type) { char *lte_mimotype2str(lte_mimo_type_t type) {
@ -221,12 +226,16 @@ char *lte_mimotype2str(lte_mimo_type_t type) {
return NULL; return NULL;
} }
float get_fd(struct lte_band *band, int earfcn) { float get_fd(struct lte_band *band, uint32_t earfcn) {
return band->fd_low_mhz + 0.1*(earfcn - band->earfcn_offset); 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) { float lte_band_fd(uint32_t earfcn) {
int i; uint32_t i;
i=0; i=0;
while(i < NOF_LTE_BANDS && lte_bands[i].earfcn_offset<earfcn) { while(i < NOF_LTE_BANDS && lte_bands[i].earfcn_offset<earfcn) {
i++; i++;
@ -238,27 +247,27 @@ float lte_band_fd(int earfcn) {
return get_fd(&lte_bands[i], 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); 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 lte_band_get_fd_band(uint32_t band, lte_earfcn_t *earfcn, int start_earfcn, int end_earfcn, uint32_t max_elems) {
int i, j; uint32_t i, j;
int nof_earfcn; uint32_t nof_earfcn;
i=0; i=0;
while(i < NOF_LTE_BANDS && lte_bands[i].band != band) { while(i < NOF_LTE_BANDS && lte_bands[i].band != band) {
i++; i++;
} }
if (i == NOF_LTE_BANDS) { if (i == NOF_LTE_BANDS) {
fprintf(stderr, "Error: Invalid band %d\n", band); fprintf(stderr, "Error: Invalid band %d\n", band);
return -1; return LIBLTE_ERROR;
} }
if (end_earfcn == -1) { if (end_earfcn == -1) {
end_earfcn = lte_bands[i].earfcn_max; end_earfcn = lte_bands[i].earfcn_max;
} else { } else {
if (end_earfcn > lte_bands[i].earfcn_max) { 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); 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) { 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 { } else {
if (start_earfcn < lte_bands[i].earfcn_offset) { 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); 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; 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].id = j + start_earfcn;
earfcn[j].fd = get_fd(&lte_bands[i], earfcn[j].id); 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 lte_band_get_fd_region(enum band_geographical_area region, lte_earfcn_t *earfcn, uint32_t max_elems) {
int i; uint32_t i;
int n; int n;
int nof_fd = 0; int nof_fd = 0;
for (i=0;i<NOF_LTE_BANDS && max_elems > 0;i++) { 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; nof_fd += n;
max_elems -= n; max_elems -= n;
} else { } 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, 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 }; 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. /* Turbo Code Rate Matching.
* 3GPP TS 36.212 v10.1.0 section 5.1.4.1 * 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 * 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) { uint32_t out_len, uint32_t rv_idx) {
char *tmp = (char*) q->buffer;
int ndummy, kidx; int ndummy, kidx;
int nrows, K_p; 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; nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1;
K_p = nrows * NCOLS; K_p = nrows * NCOLS;
if (3 * K_p > q->buffer_len) { if (3 * K_p > w_buff_len) {
fprintf(stderr, fprintf(stderr,
"Input too large. Max input length including dummy bits is %d (3x%dx32, in_len %d)\n", "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; return -1;
} }
@ -84,33 +75,35 @@ int rm_turbo_tx(rm_turbo_t *q, char *input, uint32_t in_len, char *output,
ndummy = 0; ndummy = 0;
} }
/* Sub-block interleaver (5.1.4.1.1) and bit collection */ if (rv_idx == 0) {
k = 0; /* Sub-block interleaver (5.1.4.1.1) and bit collection */
for (s = 0; s < 2; s++) { k = 0;
for (j = 0; j < NCOLS; j++) { for (s = 0; s < 2; s++) {
for (i = 0; i < nrows; i++) { for (j = 0; j < NCOLS; j++) {
if (s == 0) { for (i = 0; i < nrows; i++) {
kidx = k % K_p; if (s == 0) {
} else { kidx = k % K_p;
kidx = K_p + 2 * (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 // d_k^(2) goes through special permutation
for (k = 0; k < K_p; k++) { for (k = 0; k < K_p; k++) {
kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p; kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p;
if ((kidx - ndummy) < 0) { if ((kidx - ndummy) < 0) {
tmp[K_p + 2 * k + 1] = TX_NULL; w_buff[K_p + 2 * k + 1] = TX_NULL;
} else { } else {
tmp[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2]; 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; j = 0;
while (k < out_len) { while (k < out_len) {
if (tmp[(k0 + j) % N_cb] != TX_NULL) { if (w_buff[(k0 + j) % N_cb] != TX_NULL) {
output[k] = tmp[(k0 + j) % N_cb]; output[k] = w_buff[(k0 + j) % N_cb];
k++; k++;
} }
j++; 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. /* Undoes Turbo Code Rate Matching.
* 3GPP TS 36.212 v10.1.0 section 5.1.4.1 * 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) { uint32_t out_len, uint32_t rv_idx) {
int nrows, ndummy, K_p, k0, N_cb, jp, kidx; 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; int d_i, d_j;
bool isdummy; bool isdummy;
float *tmp = (float*) q->buffer;
nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1; nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1;
K_p = nrows * NCOLS; K_p = nrows * NCOLS;
if (3 * K_p > q->buffer_len) { if (3 * K_p > w_buff_len) {
fprintf(stderr, fprintf(stderr,
"Input too large. Max output length including dummy bits is %d (3x%dx32, in_len %d)\n", "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; return -1;
} }
@ -159,12 +153,14 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
ndummy = 0; ndummy = 0;
} }
for (i = 0; i < 3 * K_p; i++) { if (rv_idx == 0) {
tmp[i] = RX_NULL; for (i = 0; i < 3 * K_p; i++) {
w_buff[i] = RX_NULL;
}
} }
/* Undo bit collection. Account for dummy bits */ /* 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 k0 = nrows
* (2 * (uint32_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); * (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 (!isdummy) {
if (tmp[jp] == RX_NULL) { if (w_buff[jp] == RX_NULL) {
tmp[jp] = input[k]; w_buff[jp] = input[k];
} else if (input[k] != RX_NULL) { } else if (input[k] != RX_NULL) {
tmp[jp] += input[k]; /* soft combine LLRs */ w_buff[jp] += input[k]; /* soft combine LLRs */
} }
k++; k++;
} }
@ -215,15 +211,14 @@ int rm_turbo_rx(rm_turbo_t *q, float *input, uint32_t in_len, float *output,
if (j != 2) { if (j != 2) {
kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i);
} else { } else {
k = (i + ndummy - 1) % K_p; k = (i + ndummy - 1) % K_p;
if (k < 0) if (k < 0)
k += K_p; k += K_p;
kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p;
kidx = 2 * kidx + K_p + 1; kidx = 2 * kidx + K_p + 1;
} }
if (tmp[kidx] != RX_NULL) { if (w_buff[kidx] != RX_NULL) {
output[i * 3 + j] = tmp[kidx]; output[i * 3 + j] = w_buff[kidx];
} else { } else {
output[i * 3 + j] = 0; 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 */ /** High-level API */
int rm_turbo_initialize(rm_turbo_hl* h) { 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 */ /** This function can be called in a subframe (1ms) basis */
int rm_turbo_work(rm_turbo_hl* hl) { 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; return 0;
} }
int rm_turbo_stop(rm_turbo_hl* hl) { int rm_turbo_stop(rm_turbo_hl* hl) {
rm_turbo_free(&hl->q);
return 0; return 0;
} }

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

@ -50,14 +50,7 @@ const enum modem_std modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 }; { 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, int pdsch_cp(pdsch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc,
uint32_t nsubframe, bool put) { 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 ret = LIBLTE_ERROR_INVALID_INPUTS;
int i; int i;
if (q != NULL && if (q != NULL &&
lte_cell_isvalid(&cell)) 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->cell = cell;
q->rnti = user_rnti; q->rnti = user_rnti;
q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp); q->max_symbols = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports, INFO("Init PDSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
@ -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)) { if (tdec_init(&q->decoder, MAX_LONG_CB)) {
goto clean; 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); // Allocate floats for reception (LLRs)
if (!q->cb_in_b) { q->cb_in = malloc(sizeof(float) * MAX_LONG_CB);
goto clean; if (!q->cb_in) {
}
q->cb_out_b = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out_b) {
goto clean; goto clean;
} }
q->pdsch_rm_f = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12)); q->cb_out = malloc(sizeof(char) * (3 * MAX_LONG_CB + 12));
if (!q->pdsch_rm_f) { if (!q->cb_out) {
goto clean;
}
q->pdsch_e_bits = malloc(
sizeof(char) * q->max_symbols * q->mod[3].nbits_x_symbol);
if (!q->pdsch_e_bits) {
goto clean; goto clean;
} }
q->pdsch_llr = malloc( // Allocate floats for reception (LLRs)
sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol); q->pdsch_e = malloc(sizeof(float) * q->max_symbols * q->mod[3].nbits_x_symbol);
if (!q->pdsch_llr) { if (!q->pdsch_e) {
goto clean; goto clean;
} }
q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols); q->pdsch_d = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->pdsch_d) { if (!q->pdsch_d) {
goto clean; 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) { void pdsch_free(pdsch_t *q) {
int i; int i;
if (q->cb_in_b) { if (q->cb_in) {
free(q->cb_in_b); free(q->cb_in);
}
if (q->cb_out_b) {
free(q->cb_out_b);
} }
if (q->pdsch_e_bits) { if (q->cb_out) {
free(q->pdsch_e_bits); free(q->cb_out);
} }
if (q->pdsch_rm_f) { if (q->pdsch_e) {
free(q->pdsch_rm_f); free(q->pdsch_e);
}
if (q->pdsch_llr) {
free(q->pdsch_llr);
} }
if (q->pdsch_d) { if (q->pdsch_d) {
free(q->pdsch_d); free(q->pdsch_d);
@ -326,14 +301,14 @@ void pdsch_free(pdsch_t *q) {
} }
tdec_free(&q->decoder); tdec_free(&q->decoder);
tcod_free(&q->encoder); tcod_free(&q->encoder);
rm_turbo_free(&q->rm_turbo);
} }
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */ /* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
void codeblock_segmentation(struct cb_segm *s, int tbs) { int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
int Bp, B, idx1; uint32_t Bp, B, idx1;
int ret;
B = tbs + 24; B = tbs + 24;
/* Calculate CB sizes */ /* Calculate CB sizes */
@ -341,68 +316,186 @@ void codeblock_segmentation(struct cb_segm *s, int tbs) {
s->C = 1; s->C = 1;
Bp = B; Bp = B;
} else { } else {
s->C = (int) ceilf((float) B / (6114 - 24)); s->C = (uint32_t) ceilf((float) B / (6114 - 24));
Bp = B + 24 * s->C; Bp = B + 24 * s->C;
} }
idx1 = lte_find_cb_index(Bp / s->C); ret = lte_find_cb_index(Bp / s->C);
s->K1 = lte_cb_size(idx1); if (ret != LIBLTE_ERROR) {
if (s->C == 1) { idx1 = (uint32_t) ret;
s->K2 = 0; ret = lte_cb_size(idx1);
s->C2 = 0; if (ret != LIBLTE_ERROR) {
s->C1 = 1; s->K1 = (uint32_t) ret;
} else { ret = lte_cb_size(idx1 - 1);
s->K2 = lte_cb_size(idx1 - 1); if (ret != LIBLTE_ERROR) {
s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2); if (s->C == 1) {
s->C1 = s->C - s->C2; 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; return ret;
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);
} }
/* Decode a transport block according to 36.212 5.3.2 /* 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 parity[24];
char *p_parity = parity; char *p_parity = parity;
uint32_t par_rx, par_tx; uint32_t par_rx, par_tx;
int i; uint32_t i;
int cb_len, rp, wp, rlen, F, n_e; uint32_t cb_len, rp, wp, rlen, F, n_e;
struct cb_segm cbs; float *e_bits = q->pdsch_e;
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
nb_e < q->max_symbols * q->mod[3].nbits_x_symbol) 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;
rp = 0; rp = 0;
wp = 0; wp = 0;
for (i = 0; i < cbs.C; i++) { for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read/write lengths */ /* Get read/write lengths */
if (i < cbs.C - cbs.C2) { if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = cbs.K1; cb_len = harq_process->cb_segm.K1;
} else { } 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; rlen = cb_len;
} else { } else {
rlen = cb_len - 24; rlen = cb_len - 24;
} }
if (i == 0) { if (i == 0) {
F = cbs.F; F = harq_process->cb_segm.F;
} else { } else {
F = 0; F = 0;
} }
if (i < cbs.C - 1) { if (i < harq_process->cb_segm.C - 1) {
n_e = nb_e / cbs.C; n_e = nb_e / harq_process->cb_segm.C;
} else { } else {
n_e = nb_e - rp; 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); cb_len, rlen - F, wp, rp, F, n_e);
/* Rate Unmatching */ /* Rate Unmatching */
rm_turbo_rx(&q->rm_turbo, &q->pdsch_llr[rp], n_e, q->pdsch_rm_f, if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
3 * cb_len + 12, rv_idx); &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 */ /* 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); cb_len);
if (cbs.C > 1) { if (harq_process->cb_segm.C > 1) {
/* Check Codeblock CRC */ /* Check Codeblock CRC and stop early if incorrect */
//crc_attach(&q->crc_cb, q->pdsch_b[wp], cb_len); 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 */ /* Copy data to another buffer, removing the Codeblock CRC */
if (i < cbs.C - 1) { if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in_b[F], (rlen - F) * sizeof(char)); memcpy(&data[wp], &q->cb_out[F], (rlen - F) * sizeof(char));
} else { } else {
INFO("Last CB, appending parity: %d to %d from %d and 24 from %d\n", INFO("Last CB, appending parity: %d to %d from %d and 24 from %d\n",
rlen - F - 24, wp, F, rlen - 24); rlen - F - 24, wp, F, rlen - 24);
/* Append Transport Block parity bits to the last CB */ /* Append Transport Block parity bits to the last CB */
memcpy(&data[wp], &q->cb_in_b[F], (rlen - F - 24) * sizeof(char)); memcpy(&data[wp], &q->cb_out[F], (rlen - F - 24) * sizeof(char));
memcpy(parity, &q->cb_in_b[rlen - 24], 24 * sizeof(char)); memcpy(parity, &q->cb_out[rlen - 24], 24 * sizeof(char));
} }
/* Set read/write pointers */ /* 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 /** Decodes the PDSCH from the received symbols
*/ */
int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], char *data, uint32_t subframe,
uint32_t subframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) { pdsch_harq_t *harq_process, uint32_t rv_idx)
{
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
int i, n; uint32_t i, n;
cf_t *x[MAX_LAYERS]; 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 && if (q != NULL &&
sf_symbols != NULL && sf_symbols != NULL &&
data != NULL && data != NULL &&
subframe < 10 && subframe < 10 &&
prb_alloc != NULL) harq_process != NULL)
{ {
nof_bits = mcs.tbs; nof_bits = harq_process->mcs.tbs;
nof_symbols = prb_alloc->re_sf[subframe]; nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol; 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", 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 */ /* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) { 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)); memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
/* extract symbols */ /* 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) { if (n != nof_symbols) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n);
return LIBLTE_ERROR; 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 */ /* extract channel estimates */
for (i = 0; i < q->cell.nof_ports; i++) { 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) { if (n != nof_symbols) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n); fprintf(stderr, "Error expecting %d symbols but got %d\n", nof_symbols, n);
return LIBLTE_ERROR; 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); nof_symbols / q->cell.nof_ports);
} }
/* demodulate symbols */ /* demodulate symbols
demod_soft_sigma_set(&q->demod, 2.0 / q->mod[mcs.mod - 1].nbits_x_symbol); * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation,
demod_soft_table_set(&q->demod, &q->mod[mcs.mod - 1]); * thus we don't need tot set it in the LLRs normalization
demod_soft_demodulate(&q->demod, q->pdsch_d, q->pdsch_llr, nof_symbols); */
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 */ /* 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 { } else {
return LIBLTE_ERROR_INVALID_INPUTS; 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 /* 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 parity[24];
char *p_parity = parity; char *p_parity = parity;
unsigned int par; uint32_t par;
int i; uint32_t i;
int cb_len, rp, wp, rlen, F, n_e; uint32_t cb_len, rp, wp, rlen, F, n_e;
struct cb_segm cbs; char *cb_in = q->cb_in;
char *e_bits = q->pdsch_e;
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
nb_e < q->max_symbols * q->mod[3].nbits_x_symbol) nb_e < q->max_symbols * q->mod[3].nbits_x_symbol)
{ {
/* Compute CB segmentation */ if (rv_idx == 0) {
codeblock_segmentation(&cbs, tbs); /* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs);
/* Compute transport block CRC */
par = crc_checksum(&q->crc_tb, data, tbs);
/* parity bits will be appended later */ /* parity bits will be appended later */
bit_pack(par, &p_parity, 24); bit_pack(par, &p_parity, 24);
if (VERBOSE_ISDEBUG()) { if (VERBOSE_ISDEBUG()) {
DEBUG("DATA: ", 0); DEBUG("DATA: ", 0);
vec_fprint_b(stdout, data, tbs); vec_fprint_b(stdout, data, tbs);
DEBUG("PARITY: ", 0); DEBUG("PARITY: ", 0);
vec_fprint_b(stdout, parity, 24); vec_fprint_b(stdout, parity, 24);
} }
/* Add filler bits to the new data buffer */ /* Add filler bits to the new data buffer */
for (i = 0; i < cbs.F; i++) { for (i = 0; i < harq_process->cb_segm.F; i++) {
q->cb_in_b[i] = LTE_NULL_BIT; cb_in[i] = LTE_NULL_BIT;
}
} }
wp = 0; wp = 0;
rp = 0; rp = 0;
for (i = 0; i < cbs.C; i++) { for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read lengths */ /* Get read lengths */
if (i < cbs.C - cbs.C2) { if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = cbs.K1; cb_len = harq_process->cb_segm.K1;
} else { } 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; rlen = cb_len - 24;
} else { } else {
rlen = cb_len; rlen = cb_len;
} }
if (i == 0) { if (i == 0) {
F = cbs.F; F = harq_process->cb_segm.F;
} else { } else {
F = 0; F = 0;
} }
if (i < cbs.C - 1) { if (i < harq_process->cb_segm.C - 1) {
n_e = nb_e / cbs.C; n_e = nb_e / harq_process->cb_segm.C;
} else { } else {
n_e = nb_e - wp; 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, 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); cb_len, rlen - F, wp, rp, F, n_e);
/* Copy data to another buffer, making space for the Codeblock CRC */ if (rv_idx == 0) {
if (i < cbs.C - 1) { /* Copy data to another buffer, making space for the Codeblock CRC */
memcpy(&q->cb_in_b[F], &data[rp], (rlen - F) * sizeof(char)); if (i < harq_process->cb_segm.C - 1) {
} else { memcpy(&cb_in[F], &data[rp], (rlen - F) * sizeof(char));
INFO("Last CB, appending parity: %d from %d and 24 to %d\n", } else {
rlen - F - 24, rp, rlen - 24); INFO("Last CB, appending parity: %d from %d and 24 to %d\n",
/* Append Transport Block parity bits to the last CB */ rlen - F - 24, rp, rlen - 24);
memcpy(&q->cb_in_b[F], &data[rp], (rlen - F - 24) * sizeof(char)); /* Append Transport Block parity bits to the last CB */
memcpy(&q->cb_in_b[rlen - 24], parity, 24 * sizeof(char)); memcpy(&cb_in[F], &data[rp], (rlen - F - 24) * sizeof(char));
} memcpy(&cb_in[rlen - 24], parity, 24 * sizeof(char));
}
if (cbs.C > 1) { if (harq_process->cb_segm.C > 1) {
/* Attach Codeblock CRC */ /* Attach Codeblock CRC */
crc_attach(&q->crc_cb, q->cb_in_b, rlen); crc_attach(&q->crc_cb, cb_in, rlen);
} }
if (VERBOSE_ISDEBUG()) {
if (VERBOSE_ISDEBUG()) { DEBUG("CB#%d Len=%d: ", i, cb_len);
DEBUG("CB#%d Len=%d: ", i, cb_len); vec_fprint_b(stdout, cb_in, cb_len);
vec_fprint_b(stdout, q->cb_in_b, 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 */ /* Rate matching */
rm_turbo_tx(&q->rm_turbo, q->cb_out_b, 3 * cb_len + 12, if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
&q->pdsch_e_bits[wp], n_e, rv_idx); 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 */ /* Set read/write pointers */
rp += (rlen - F); 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 /** 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], int pdsch_encode(pdsch_t *q, char *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe,
uint32_t subframe, ra_mcs_t mcs, ra_prb_t *prb_alloc) { pdsch_harq_t *harq_process, uint32_t rv_idx)
{
int i; int i;
uint32_t nof_symbols, nof_bits, nof_bits_e; uint32_t nof_symbols, nof_bits, nof_bits_e;
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
cf_t *x[MAX_LAYERS]; cf_t *x[MAX_LAYERS];
if (q != NULL && if (q != NULL &&
data != NULL && data != NULL &&
subframe < 10 && subframe < 10 &&
prb_alloc != NULL) harq_process != NULL)
{ {
for (i=0;i<q->cell.nof_ports;i++) { 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_bits = harq_process->mcs.tbs;
nof_symbols = prb_alloc->re_sf[subframe]; nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * q->mod[mcs.mod - 1].nbits_x_symbol; 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) { if (nof_bits > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e); fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits / nof_bits_e);
return LIBLTE_ERROR_INVALID_INPUTS; 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; return LIBLTE_ERROR_INVALID_INPUTS;
} }
INFO("Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d\n", INFO("Encoding PDSCH SF: %d, Mod %d, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
subframe, mcs.mod, nof_bits, nof_symbols, nof_bits_e); subframe, harq_process->mcs.mod, nof_bits, nof_symbols, nof_bits_e, rv_idx);
/* number of layers equals number of ports */ /* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) { 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)); 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"); fprintf(stderr, "Error encoding TB\n");
return LIBLTE_ERROR; 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 */ /* TODO: only diversity supported */
if (q->cell.nof_ports > 1) { 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 */ /* mapping to resource elements */
for (i = 0; i < q->cell.nof_ports; i++) { 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; return LIBLTE_SUCCESS;
} else { } 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 */ /* 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) { int ra_mcs_from_idx_dl(uint32_t idx, ra_mcs_t *mcs) {
if (idx < 10) { 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_re_test pdsch_re_test)
ADD_TEST(pdsch_test pdsch_test -l 50000 -m 4 -n 110) 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 # FILE TEST

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

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

Loading…
Cancel
Save