From 01ca2dcf28ceebcd310e4c13abb54acc1e58dc47 Mon Sep 17 00:00:00 2001 From: ismagom Date: Mon, 7 Jul 2014 20:42:10 +0200 Subject: [PATCH] Added HARQ support on PDSCH. Small changes on API. --- lte/phy/examples/pdsch_enodeb.c | 14 +- lte/phy/examples/pdsch_ue.c | 14 +- .../include/liblte/phy/common/phy_common.h | 34 +- lte/phy/include/liblte/phy/fec/rm_turbo.h | 19 +- lte/phy/include/liblte/phy/phch/pdsch.h | 57 ++- lte/phy/include/liblte/phy/phch/ra.h | 2 + lte/phy/lib/common/src/phy_common.c | 73 +-- lte/phy/lib/fec/src/rm_turbo.c | 123 ++--- lte/phy/lib/fec/test/rm_turbo_test.c | 23 +- lte/phy/lib/phch/src/pdsch.c | 480 +++++++++++------- lte/phy/lib/phch/src/ra.c | 17 + lte/phy/lib/phch/test/CMakeLists.txt | 1 + lte/phy/lib/phch/test/pdsch_file_test.c | 15 +- lte/phy/lib/phch/test/pdsch_test.c | 73 ++- 14 files changed, 575 insertions(+), 370 deletions(-) diff --git a/lte/phy/examples/pdsch_enodeb.c b/lte/phy/examples/pdsch_enodeb.c index 511a0c3a0..d8263e739 100644 --- a/lte/phy/examples/pdsch_enodeb.c +++ b/lte/phy/examples/pdsch_enodeb.c @@ -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(®s); @@ -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); diff --git a/lte/phy/examples/pdsch_ue.c b/lte/phy/examples/pdsch_ue.c index f56da5ccb..f4628db6c 100644 --- a/lte/phy/examples/pdsch_ue.c +++ b/lte/phy/examples/pdsch_ue.c @@ -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++; diff --git a/lte/phy/include/liblte/phy/common/phy_common.h b/lte/phy/include/liblte/phy/common/phy_common.h index 900941a07..fdda782d2 100644 --- a/lte/phy/include/liblte/phy/common/phy_common.h +++ b/lte/phy/include/liblte/phy/common/phy_common.h @@ -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); diff --git a/lte/phy/include/liblte/phy/fec/rm_turbo.h b/lte/phy/include/liblte/phy/fec/rm_turbo.h index 97bc8c90d..0b5fad70e 100644 --- a/lte/phy/include/liblte/phy/fec/rm_turbo.h +++ b/lte/phy/include/liblte/phy/fec/rm_turbo.h @@ -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; diff --git a/lte/phy/include/liblte/phy/phch/pdsch.h b/lte/phy/include/liblte/phy/phch/pdsch.h index c48fa1c5c..c25acd191 100644 --- a/lte/phy/include/liblte/phy/phch/pdsch.h +++ b/lte/phy/include/liblte/phy/phch/pdsch.h @@ -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, diff --git a/lte/phy/include/liblte/phy/phch/ra.h b/lte/phy/include/liblte/phy/phch/ra.h index 7b9ed8362..0e578a14d 100644 --- a/lte/phy/include/liblte/phy/phch/ra.h +++ b/lte/phy/include/liblte/phy/phch/ra.h @@ -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, diff --git a/lte/phy/lib/common/src/phy_common.c b/lte/phy/lib/common/src/phy_common.c index 5cbdae6da..7987b9034 100644 --- a/lte/phy/lib/common/src/phy_common.c +++ b/lte/phy/lib/common/src/phy_common.c @@ -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 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(<e_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 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; } } } diff --git a/lte/phy/lib/fec/src/rm_turbo.c b/lte/phy/lib/fec/src/rm_turbo.c index 6e677b0d4..ae52d9593 100644 --- a/lte/phy/lib/fec/src/rm_turbo.c +++ b/lte/phy/lib/fec/src/rm_turbo.c @@ -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; } diff --git a/lte/phy/lib/fec/test/rm_turbo_test.c b/lte/phy/lib/fec/test/rm_turbo_test.c index aa42ca832..42db72696 100644 --- a/lte/phy/lib/fec/test/rm_turbo_test.c +++ b/lte/phy/lib/fec/test/rm_turbo_test.c @@ -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); diff --git a/lte/phy/lib/phch/src/pdsch.c b/lte/phy/lib/phch/src/pdsch.c index 524477730..c43b6c03b 100644 --- a/lte/phy/lib/phch/src/pdsch.c +++ b/lte/phy/lib/phch/src/pdsch.c @@ -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;imax_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;imax_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;imax_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;icell.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 { diff --git a/lte/phy/lib/phch/src/ra.c b/lte/phy/lib/phch/src/ra.c index f53ffafdd..cf703ef63 100644 --- a/lte/phy/lib/phch/src/ra.c +++ b/lte/phy/lib/phch/src/ra.c @@ -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) { diff --git a/lte/phy/lib/phch/test/CMakeLists.txt b/lte/phy/lib/phch/test/CMakeLists.txt index 36ed64f2a..328d8128a 100644 --- a/lte/phy/lib/phch/test/CMakeLists.txt +++ b/lte/phy/lib/phch/test/CMakeLists.txt @@ -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 diff --git a/lte/phy/lib/phch/test/pdsch_file_test.c b/lte/phy/lib/phch/test/pdsch_file_test.c index e436a83a1..fa5a1a822 100644 --- a/lte/phy/lib/phch/test/pdsch_file_test.c +++ b/lte/phy/lib/phch/test/pdsch_file_test.c @@ -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(®s); } @@ -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 { diff --git a/lte/phy/lib/phch/test/pdsch_test.c b/lte/phy/lib/phch/test/pdsch_test.c index de6b9f5ce..40a5b01dd 100644 --- a/lte/phy/lib/phch/test/pdsch_test.c +++ b/lte/phy/lib/phch/test/pdsch_test.c @@ -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 0) { - slot_symbols[0][j] += slot_symbols[i][j]; + /* combine outputs */ + for (i=0;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: