Added UL SCH interleaver. Fixed issue with rate matching and filler bits

master
ismagom 10 years ago
parent 9a614ec9d1
commit a47461a6f5

@ -269,7 +269,7 @@ int main(int argc, char **argv) {
} else if (n == 0) { } else if (n == 0) {
printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, ExecTime: %5.1f us, NOI: %.2f, PDCCH-Det: %.3f\r", printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, ExecTime: %5.1f us, NOI: %.2f, PDCCH-Det: %.3f\r",
ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000, ue_sync_get_cfo(&ue_sync)/1000, ue_sync_get_sfo(&ue_sync)/1000,
mean_exec_time, pdsch_average_noi(&ue_dl.pdsch), mean_exec_time, sch_average_noi(&ue_dl.pdsch.dl_sch),
(float) ue_dl.nof_pdcch_detected/nof_trials); (float) ue_dl.nof_pdcch_detected/nof_trials);
nof_trials++; nof_trials++;
} else { } else {

@ -74,7 +74,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; harq_t harq_process;
regs_t regs; regs_t regs;
ra_pdsch_t ra_dl; ra_pdsch_t ra_dl;
@ -252,7 +252,7 @@ void base_init() {
pdsch_set_rnti(&pdsch, 1234); pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
exit(-1); exit(-1);
} }
@ -260,7 +260,7 @@ void base_init() {
void base_free() { void base_free() {
pdsch_harq_free(&harq_process); harq_free(&harq_process);
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdcch_free(&pdcch); pdcch_free(&pdcch);
regs_free(&regs); regs_free(&regs);
@ -330,8 +330,8 @@ int update_radl() {
ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb); ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);
printf("Type new MCS index and press Enter: "); fflush(stdout); printf("Type new MCS index and press Enter: "); fflush(stdout);
pdsch_harq_reset(&harq_process); harq_reset(&harq_process);
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) { if (harq_setup(&harq_process, ra_dl.mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n"); fprintf(stderr, "Error configuring HARQ process\n");
return -1; return -1;
} }

@ -41,10 +41,6 @@
#define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE #define LTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE
#define LTE_NULL_BIT 0
#define LTE_NULL_SYMBOL 2
#define LTE_NIL_SYMBOL 2
#define MAX_PORTS 4 #define MAX_PORTS 4
#define MAX_LAYERS 8 #define MAX_LAYERS 8
#define MAX_CODEWORDS 2 #define MAX_CODEWORDS 2

@ -71,4 +71,10 @@ LIBLTE_API int sequence_pdsch(sequence_t *seq,
uint32_t cell_id, uint32_t cell_id,
uint32_t len); uint32_t len);
LIBLTE_API int sequence_pusch(sequence_t *seq,
unsigned short rnti,
uint32_t nslot,
uint32_t cell_id,
uint32_t len);
#endif #endif

@ -30,9 +30,13 @@
#include "liblte/config.h" #include "liblte/config.h"
#ifndef RX_NULL
#define RX_NULL 10000 #define RX_NULL 10000
#define TX_NULL 80 #endif
#ifndef TX_NULL
#define TX_NULL 100
#endif
LIBLTE_API int rm_conv_tx(uint8_t *input, LIBLTE_API int rm_conv_tx(uint8_t *input,
uint32_t in_len, uint32_t in_len,
uint8_t *output, uint8_t *output,

@ -55,7 +55,8 @@ LIBLTE_API int rm_turbo_rx(float *w_buff,
uint32_t in_len, uint32_t in_len,
float *output, float *output,
uint32_t out_len, uint32_t out_len,
uint32_t rv_idx); uint32_t rv_idx,
uint32_t nof_filler_bits);
/* High-level API */ /* High-level API */
typedef struct LIBLTE_API { typedef struct LIBLTE_API {

@ -36,6 +36,10 @@
#define RATE 3 #define RATE 3
#define TOTALTAIL 12 #define TOTALTAIL 12
#ifndef TX_NULL
#define TX_NULL 100
#endif
typedef struct LIBLTE_API { typedef struct LIBLTE_API {
uint32_t max_long_cb; uint32_t max_long_cb;
tc_interl_t interl; tc_interl_t interl;

@ -0,0 +1,78 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef HARQ_
#define HARQ_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/ra.h"
struct cb_segm {
uint32_t F;
uint32_t C;
uint32_t K1;
uint32_t K2;
uint32_t C1;
uint32_t C2;
};
typedef struct LIBLTE_API {
ra_mcs_t mcs;
ra_prb_t prb_alloc;
lte_cell_t cell;
uint32_t N_symb_ul; // Number of symbols for PUSCH transmission
uint32_t nof_prb_pusch_init; // Initial resource allocation for PUSCH.
uint32_t max_cb;
uint32_t w_buff_size;
float **pdsch_w_buff_f;
uint8_t **pdsch_w_buff_c;
struct cb_segm cb_segm;
} harq_t;
LIBLTE_API int harq_init(harq_t * q,
lte_cell_t cell);
LIBLTE_API int harq_setup(harq_t *p,
ra_mcs_t mcs,
ra_prb_t *prb_alloc);
LIBLTE_API void harq_reset(harq_t *p);
LIBLTE_API void harq_free(harq_t *p);
LIBLTE_API int codeblock_segmentation(struct cb_segm *s,
uint32_t tbs);
#endif

@ -36,38 +36,15 @@
#include "liblte/phy/modem/mod.h" #include "liblte/phy/modem/mod.h"
#include "liblte/phy/modem/demod_soft.h" #include "liblte/phy/modem/demod_soft.h"
#include "liblte/phy/scrambling/scrambling.h" #include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/fec/rm_turbo.h"
#include "liblte/phy/fec/turbocoder.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/fec/crc.h"
#include "liblte/phy/phch/dci.h" #include "liblte/phy/phch/dci.h"
#include "liblte/phy/phch/regs.h" #include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/harq.h"
#define TDEC_MAX_ITERATIONS 5 #define TDEC_MAX_ITERATIONS 5
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;
uint8_t **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;
@ -75,8 +52,6 @@ typedef struct LIBLTE_API {
uint32_t max_symbols; uint32_t max_symbols;
bool rnti_is_set; bool rnti_is_set;
uint16_t rnti; uint16_t rnti;
uint32_t nof_iterations;
float average_nof_iterations;
/* buffers */ /* buffers */
// void buffers are shared for tx and rx // void buffers are shared for tx and rx
@ -84,20 +59,16 @@ typedef struct LIBLTE_API {
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;
uint8_t *cb_in;
void *cb_out;
void *pdsch_e; void *pdsch_e;
/* 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;
tdec_t decoder;
crc_t crc_tb;
crc_t crc_cb;
precoding_t precoding; precoding_t precoding;
sch_t dl_sch;
}pdsch_t; }pdsch_t;
LIBLTE_API int pdsch_init(pdsch_t *q, LIBLTE_API int pdsch_init(pdsch_t *q,
@ -108,22 +79,11 @@ LIBLTE_API void pdsch_free(pdsch_t *q);
LIBLTE_API int pdsch_set_rnti(pdsch_t *q, LIBLTE_API int pdsch_set_rnti(pdsch_t *q,
uint16_t rnti); uint16_t rnti);
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_reset(pdsch_harq_t *p);
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,
uint8_t *data, uint8_t *data,
cf_t *sf_symbols[MAX_PORTS], cf_t *sf_symbols[MAX_PORTS],
uint32_t nsubframe, uint32_t nsubframe,
pdsch_harq_t *harq_process, harq_t *harq_process,
uint32_t rv_idx); uint32_t rv_idx);
LIBLTE_API int pdsch_decode(pdsch_t *q, LIBLTE_API int pdsch_decode(pdsch_t *q,
@ -132,7 +92,7 @@ LIBLTE_API int pdsch_decode(pdsch_t *q,
float noise_estimate, float noise_estimate,
uint8_t *data, uint8_t *data,
uint32_t nsubframe, uint32_t nsubframe,
pdsch_harq_t *harq_process, harq_t *harq_process,
uint32_t rv_idx); uint32_t rv_idx);
LIBLTE_API float pdsch_average_noi(pdsch_t *q); LIBLTE_API float pdsch_average_noi(pdsch_t *q);

@ -0,0 +1,120 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef PUCH_
#define PUSCH_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/mimo/precoding.h"
#include "liblte/phy/mimo/layermap.h"
#include "liblte/phy/modem/mod.h"
#include "liblte/phy/modem/demod_soft.h"
#include "liblte/phy/scrambling/scrambling.h"
#include "liblte/phy/phch/regs.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/harq.h"
#define TDEC_MAX_ITERATIONS 5
typedef _Complex float cf_t;
/* PDSCH object */
typedef struct LIBLTE_API {
lte_cell_t cell;
uint32_t max_symbols;
bool rnti_is_set;
uint16_t rnti;
/* buffers */
// void buffers are shared for tx and rx
cf_t *ce[MAX_PORTS];
cf_t *pusch_symbols[MAX_PORTS];
cf_t *pusch_x[MAX_PORTS];
cf_t *pusch_d;
void *pusch_e;
uint8_t *pusch_q_ri;
uint8_t *pusch_q_ack;
/* tx & rx objects */
modem_table_t mod[4];
demod_soft_t demod;
sequence_t seq_pusch[NSUBFRAMES_X_FRAME];
sch_t dl_sch;
}pusch_t;
LIBLTE_API int pusch_init(pusch_t *q,
lte_cell_t cell);
LIBLTE_API void pusch_free(pusch_t *q);
LIBLTE_API int pusch_set_rnti(pusch_t *q,
uint16_t rnti);
LIBLTE_API int pusch_encode(pusch_t *q,
uint8_t *data,
cf_t *sf_symbols[MAX_PORTS],
uint32_t nsubframe,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int pusch_uci_encode(pusch_t *q,
uint8_t *data,
uci_data_t uci_data,
cf_t *sf_symbols[MAX_PORTS],
uint32_t subframe,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int pusch_decode(pusch_t *q,
cf_t *sf_symbols,
cf_t *ce[MAX_PORTS],
float noise_estimate,
uint8_t *data,
uint32_t nsubframe,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API float pusch_average_noi(pusch_t *q);
LIBLTE_API uint32_t pusch_last_noi(pusch_t *q);
LIBLTE_API int pusch_get(pusch_t *q,
cf_t *sf_symbols,
cf_t *pusch_symbols,
ra_prb_t *prb_alloc,
uint32_t subframe);
#endif

@ -0,0 +1,112 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SCH_
#define SCH_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/fec/rm_turbo.h"
#include "liblte/phy/fec/turbocoder.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/fec/crc.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/phch/uci.h"
#define TDEC_MAX_ITERATIONS 5
#ifndef RX_NULL
#define RX_NULL 10000
#endif
#ifndef TX_NULL
#define TX_NULL 100
#endif
/* DL-SCH AND UL-SCH common functions */
typedef struct LIBLTE_API {
uint32_t nof_iterations;
float average_nof_iterations;
/* buffers */
uint8_t *cb_in;
void *cb_out;
void *pdsch_e;
tcod_t encoder;
tdec_t decoder;
crc_t crc_tb;
crc_t crc_cb;
} sch_t;
LIBLTE_API int sch_init(sch_t *q);
LIBLTE_API void sch_free(sch_t *q);
LIBLTE_API float sch_average_noi(sch_t *q);
LIBLTE_API uint32_t sch_last_noi(sch_t *q);
LIBLTE_API int dlsch_encode(sch_t *q,
uint8_t *data,
uint8_t *e_bits,
uint32_t tbs,
uint32_t nb_e,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int dlsch_decode(sch_t *q,
float *e_bits,
uint8_t *data,
uint32_t tbs,
uint32_t nb_e,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int ulsch_encode(sch_t *q,
uint8_t *data,
uci_data_t uci_data,
uint8_t *q_bits,
uint32_t nb_q,
uint8_t *q_bits_ack,
uint8_t *q_bits_ri,
harq_t *harq_process,
uint32_t rv_idx);
LIBLTE_API int ulsch_decode(sch_t *q,
float *e_bits,
uint8_t *data,
uint32_t tbs,
uint32_t nb_e,
harq_t *harq_process,
uint32_t rv_idx);
#endif

@ -0,0 +1,60 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef UCI_
#define UCI_
#include "liblte/config.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/harq.h"
typedef struct LIBLTE_API {
uint8_t *uci_cqi;
uint32_t uci_cqi_len;
float beta_cqi;
uint8_t uci_ri; // Only 1-bit supported for RI
uint32_t uci_ri_len;
float beta_ri;
uint8_t uci_ack; // Only 1-bit supported for HARQ
uint32_t uci_ack_len;
float beta_ack;
} uci_data_t;
LIBLTE_API int uci_encode_cqi(uint8_t *data,
uint8_t *e_bits,
uint32_t tbs,
uint32_t nb_e);
/* Encode UCI RI and HARQ ACK/NACK bits */
LIBLTE_API uint32_t uci_encode_ri_ack(uint8_t data,
float beta,
uint8_t q_bits[6],
harq_t *harq_process);
#endif

@ -94,6 +94,11 @@
#include "liblte/phy/phch/pcfich.h" #include "liblte/phy/phch/pcfich.h"
#include "liblte/phy/phch/phich.h" #include "liblte/phy/phch/phich.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/ue/ue_sync.h" #include "liblte/phy/ue/ue_sync.h"
#include "liblte/phy/ue/ue_mib.h" #include "liblte/phy/ue/ue_mib.h"
#include "liblte/phy/ue/ue_cell_search.h" #include "liblte/phy/ue/ue_cell_search.h"

@ -36,14 +36,34 @@
typedef _Complex float cf_t; typedef _Complex float cf_t;
/* Scrambling has no state */ /* Scrambling has no state */
LIBLTE_API void scrambling_b(sequence_t *s, uint8_t *data); LIBLTE_API void scrambling_b(sequence_t *s,
LIBLTE_API void scrambling_b_offset(sequence_t *s, uint8_t *data, int offset, int len); uint8_t *data);
LIBLTE_API void scrambling_f(sequence_t *s, float *data); LIBLTE_API void scrambling_b_offset(sequence_t *s,
LIBLTE_API void scrambling_f_offset(sequence_t *s, float *data, int offset, int len); uint8_t *data,
int offset,
int len);
LIBLTE_API void scrambling_c(sequence_t *s, cf_t *data); LIBLTE_API void scrambling_b_offset_pusch(sequence_t *s,
LIBLTE_API void scrambling_c_offset(sequence_t *s, cf_t *data, int offset, int len); uint8_t *data,
int offset,
int len);
LIBLTE_API void scrambling_f(sequence_t *s,
float *data);
LIBLTE_API void scrambling_f_offset(sequence_t *s,
float *data,
int offset,
int len);
LIBLTE_API void scrambling_c(sequence_t *s,
cf_t *data);
LIBLTE_API void scrambling_c_offset(sequence_t *s,
cf_t *data,
int offset,
int len);
/* High-level API */ /* High-level API */

@ -61,7 +61,7 @@ typedef struct LIBLTE_API {
pcfich_t pcfich; pcfich_t pcfich;
pdcch_t pdcch; pdcch_t pdcch;
pdsch_t pdsch; pdsch_t pdsch;
pdsch_harq_t harq_process[NOF_HARQ_PROCESSES]; harq_t harq_process[NOF_HARQ_PROCESSES];
regs_t regs; regs_t regs;
lte_fft_t fft; lte_fft_t fft;
chest_dl_t chest; chest_dl_t chest;

@ -99,7 +99,7 @@ static int generate_sequence_hopping_v(refsignal_ul_t *q) {
for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) { for (uint32_t ns=0;ns<NSLOTS_X_FRAME;ns++) {
for (uint32_t delta_ss=0;delta_ss<NOF_DELTA_SS;delta_ss++) { for (uint32_t delta_ss=0;delta_ss<NOF_DELTA_SS;delta_ss++) {
if (sequence_LTE_pr(&seq, 20, ((q->cell.id / 30) << 5) + (q->cell.id%30)+delta_ss)) { if (sequence_LTE_pr(&seq, 20, ((q->cell.id / 30) << 5) + ((q->cell.id%30)+delta_ss)%30)) {
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }
q->v_pusch[ns][delta_ss] = seq.c[ns]; q->v_pusch[ns][delta_ss] = seq.c[ns];

@ -34,9 +34,9 @@
#include "liblte/phy/phy.h" #include "liblte/phy/phy.h"
lte_cell_t cell = { lte_cell_t cell = {
6, // nof_prb 100, // nof_prb
MAX_PORTS, // nof_ports MAX_PORTS, // nof_ports
0, // cell_id 1, // cell_id
CPNORM // cyclic prefix CPNORM // cyclic prefix
}; };
@ -74,13 +74,12 @@ void parse_args(int argc, char **argv) {
} }
} }
lte_hopping_method_t hopping_modes[3]={HOPPING_OFF, HOPPING_GROUP, HOPPING_SEQUENCE}; lte_hopping_method_t hopping_modes[3]={HOPPING_OFF, HOPPING_SEQUENCE, HOPPING_GROUP};
int main(int argc, char **argv) { int main(int argc, char **argv) {
refsignal_ul_t refs; refsignal_ul_t refs;
refsignal_drms_pusch_cfg_t pusch_cfg; refsignal_drms_pusch_cfg_t pusch_cfg;
cf_t *signal = NULL; cf_t *signal = NULL;
int i, j;
int ret = -1; int ret = -1;
parse_args(argc,argv); parse_args(argc,argv);
@ -98,12 +97,12 @@ int main(int argc, char **argv) {
printf("Running tests for %d PRB\n", cell.nof_prb); printf("Running tests for %d PRB\n", cell.nof_prb);
for (int n=3;n<cell.nof_prb;n++) { for (int n=6;n<cell.nof_prb;n++) {
for (int delta_ss=0;delta_ss<NOF_DELTA_SS;delta_ss++) { for (int delta_ss=29;delta_ss<NOF_DELTA_SS;delta_ss++) {
for (int cshift=0;cshift<NOF_CSHIFT;cshift++) { for (int cshift=0;cshift<NOF_CSHIFT;cshift++) {
for (int h=0;h<3;h++) { for (int h=1;h<3;h++) {
for (int ns=0;ns<NSLOTS_X_FRAME;ns++) { for (int ns=0;ns<NSLOTS_X_FRAME;ns++) {
for (int cshift_drms=5;cshift_drms<NOF_CSHIFT;cshift_drms++) { for (int cshift_drms=0;cshift_drms<NOF_CSHIFT;cshift_drms++) {
pusch_cfg.beta_pusch = 1.0; pusch_cfg.beta_pusch = 1.0;
pusch_cfg.nof_prb = n; pusch_cfg.nof_prb = n;
pusch_cfg.common.cyclic_shift = cshift; pusch_cfg.common.cyclic_shift = cshift;

@ -61,6 +61,11 @@ int rm_turbo_tx(uint8_t *w_buff, uint32_t w_buff_len, uint8_t *input, uint32_t i
int i, j, k, s, N_cb, k0; int i, j, k, s, N_cb, k0;
if (in_len < 3) {
fprintf(stderr, "Error minimum input length for rate matching is 3\n");
return -1;
}
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 > w_buff_len) { if (3 * K_p > w_buff_len) {
@ -132,7 +137,7 @@ int rm_turbo_tx(uint8_t *w_buff, uint32_t w_buff_len, uint8_t *input, uint32_t i
* with rv_idx!=0 will soft-combine the LLRs from input with w_buff * with rv_idx!=0 will soft-combine the LLRs from input with w_buff
*/ */
int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, 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, uint32_t nof_filler_bits) {
int nrows, ndummy, K_p, k0, N_cb, jp, kidx; int nrows, ndummy, K_p, k0, N_cb, jp, kidx;
int i, j, k; int i, j, k;
@ -148,6 +153,12 @@ int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_le
return -1; return -1;
} }
if (out_len < 3) {
fprintf(stderr, "Error minimum input length for rate matching is 3\n");
return -1;
}
ndummy = K_p - out_len / 3; ndummy = K_p - out_len / 3;
if (ndummy < 0) { if (ndummy < 0) {
ndummy = 0; ndummy = 0;
@ -179,9 +190,13 @@ int rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_le
} }
if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) {
isdummy = false; isdummy = false;
if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < nof_filler_bits) {
isdummy = true;
}
} else { } else {
isdummy = true; isdummy = true;
} }
} else { } else {
uint32_t jpp = (jp - K_p - 1) / 2; uint32_t jpp = (jp - K_p - 1) / 2;
kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p;

@ -78,9 +78,13 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
k = 0; k = 0;
for (i = 0; i < long_cb; i++) { for (i = 0; i < long_cb; i++) {
bit = input[i]; if (input[i] == TX_NULL) {
bit = 0;
} else {
bit = input[i];
}
output[k] = input[i];
output[k] = bit;
k++; k++;
in = bit ^ (reg1_2 ^ reg1_1); in = bit ^ (reg1_2 ^ reg1_1);
@ -90,10 +94,17 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
reg1_1 = reg1_0; reg1_1 = reg1_0;
reg1_0 = in; reg1_0 = in;
output[k] = out; if (input[i] == TX_NULL) {
output[k] = TX_NULL;
} else {
output[k] = out;
}
k++; k++;
bit = input[per[i]]; bit = input[per[i]];
if (bit == TX_NULL) {
bit = 0;
}
in = bit ^ (reg2_2 ^ reg2_1); in = bit ^ (reg2_2 ^ reg2_1);
out = reg2_2 ^ (reg2_0 ^ in); out = reg2_2 ^ (reg2_0 ^ in);
@ -104,6 +115,8 @@ int tcod_encode(tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) {
output[k] = out; output[k] = out;
k++; k++;
} }
k = 3 * long_cb; k = 3 * long_cb;

@ -38,6 +38,7 @@ ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 1)
ADD_TEST(rm_turbo_test_1 rm_turbo_test -t 480 -r 1920 -i 2) ADD_TEST(rm_turbo_test_1 rm_turbo_test -t 480 -r 1920 -i 2)
ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 3) ADD_TEST(rm_turbo_test_2 rm_turbo_test -t 1920 -r 480 -i 3)
BuildMex(MEXNAME rm_turbo_rx SOURCES rm_turbo_rx_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# Turbo Coder TEST # Turbo Coder TEST

@ -0,0 +1,100 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
/** MEX function to be called from MATLAB to test the channel estimator
*/
#define INPUT prhs[0]
#define TRBLKLEN prhs[1]
#define RV prhs[2]
#define NOF_INPUTS 3
void help()
{
mexErrMsgTxt
("[out] = liblte_rm_turbo_rx(in, trblkin, rv)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
float *input;
float *output;
uint32_t in_len, trblklen, cblen, rvidx;
float *w_buff_f;
if (nrhs != NOF_INPUTS) {
help();
return;
}
// Read input symbols
in_len = mexutils_read_f(INPUT, &input);
if (in_len < 0) {
mexErrMsgTxt("Error reading input bits\n");
return;
}
trblklen = (uint32_t) mxGetScalar(TRBLKLEN);
rvidx = (uint32_t) mxGetScalar(RV);
struct cb_segm cbsegm;
codeblock_segmentation(&cbsegm, trblklen);
cblen = 3*cbsegm.K1+12;
w_buff_f = calloc(1,sizeof(float) * cblen * 10);
if (!w_buff_f) {
perror("malloc");
exit(-1);
}
// allocate memory for output bits
output = vec_malloc(cblen * sizeof(float));
rm_turbo_rx(w_buff_f, cblen * 10, input, in_len, output, cblen,
rvidx,cbsegm.F);
if (nlhs >= 1) {
mexutils_write_f(output, &plhs[0], cblen, 1);
}
if (nlhs >= 2) {
mexutils_write_f(input, &plhs[1], in_len, 1);
}
free(input);
free(output);
free(w_buff_f);
return;
}

@ -37,16 +37,20 @@
#include "liblte/phy/phy.h" #include "liblte/phy/phy.h"
int nof_tx_bits = -1, nof_rx_bits = -1; int nof_tx_bits = -1, nof_rx_bits = -1;
int nof_filler_bits = -1;
int rv_idx = 0; int rv_idx = 0;
void usage(char *prog) { void usage(char *prog) {
printf("Usage: %s -t nof_tx_bits -r nof_rx_bits [-i rv_idx]\n", prog); printf("Usage: %s -t nof_tx_bits -r nof_rx_bits [-i rv_idx -f nof_filler_bits]\n", prog);
} }
void parse_args(int argc, char **argv) { void parse_args(int argc, char **argv) {
int opt; int opt;
while ((opt = getopt(argc, argv, "tri")) != -1) { while ((opt = getopt(argc, argv, "trif")) != -1) {
switch (opt) { switch (opt) {
case 'f':
nof_filler_bits = atoi(argv[optind]);
break;
case 't': case 't':
nof_tx_bits = atoi(argv[optind]); nof_tx_bits = atoi(argv[optind]);
break; break;
@ -73,7 +77,7 @@ void parse_args(int argc, char **argv) {
int main(int argc, char **argv) { int main(int argc, char **argv) {
int i; int i;
uint8_t *bits, *rm_bits, *w_buff_c; uint8_t *bits, *bits_out, *rm_bits, *w_buff_c;
float *rm_symbols, *unrm_symbols, *w_buff_f; float *rm_symbols, *unrm_symbols, *w_buff_f;
int nof_errors; int nof_errors;
@ -84,6 +88,11 @@ int main(int argc, char **argv) {
perror("malloc"); perror("malloc");
exit(-1); exit(-1);
} }
bits_out = malloc(sizeof(uint8_t) * nof_tx_bits);
if (!bits_out) {
perror("malloc");
exit(-1);
}
w_buff_c = malloc(sizeof(uint8_t) * nof_tx_bits * 10); w_buff_c = malloc(sizeof(uint8_t) * nof_tx_bits * 10);
if (!w_buff_c) { if (!w_buff_c) {
perror("malloc"); perror("malloc");
@ -114,14 +123,36 @@ int main(int argc, char **argv) {
bits[i] = rand() % 2; bits[i] = rand() % 2;
} }
for (i=0;i<nof_filler_bits;i++) {
bits[3*i+0] = TX_NULL;
bits[3*i+1] = TX_NULL;
}
printf("BITS: ");
vec_fprint_b(stdout, bits, nof_tx_bits);
rm_turbo_tx(w_buff_c, nof_tx_bits * 10, 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);
printf("RM: ");
vec_fprint_b(stdout, rm_bits, nof_rx_bits);
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(w_buff_f, nof_rx_bits * 10, 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_filler_bits);
printf("UMRM: ");
vec_fprint_f(stdout, unrm_symbols, nof_tx_bits);
for (i=0;i<nof_tx_bits;i++) {
bits_out[i] = unrm_symbols[i]>0?1:0;
}
printf("BITS: ");
vec_fprint_b(stdout, bits_out, nof_tx_bits);
printf("BITS: ");
vec_fprint_b(stdout, bits, nof_tx_bits);
nof_errors = 0; nof_errors = 0;
for (i = 0; i < nof_tx_bits; i++) { for (i = 0; i < nof_tx_bits; i++) {
@ -134,6 +165,7 @@ int main(int argc, char **argv) {
free(rm_bits); free(rm_bits);
free(rm_symbols); free(rm_symbols);
free(unrm_symbols); free(unrm_symbols);
free(bits_out);
if (nof_errors) { if (nof_errors) {
printf("nof_errors=%d\n", nof_errors); printf("nof_errors=%d\n", nof_errors);

@ -0,0 +1,200 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/phch/ra.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/fec/turbodecoder.h"
#include "liblte/phy/utils/vector.h"
#include "liblte/phy/utils/debug.h"
#define MAX_PDSCH_RE(cp) (2 * CP_NSYMB(cp) * 12)
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1;
int ret;
B = tbs + 24;
/* Calculate CB sizes */
if (B <= MAX_LONG_CB) {
s->C = 1;
Bp = B;
} else {
s->C = (uint32_t) ceilf((float) B / (MAX_LONG_CB - 24));
Bp = B + 24 * s->C;
}
ret = lte_find_cb_index((Bp-1) / s->C + 1);
if (ret != LIBLTE_ERROR) {
idx1 = (uint32_t) ret;
ret = lte_cb_size(idx1);
if (ret != LIBLTE_ERROR) {
s->K1 = (uint32_t) ret;
if (idx1 > 0) {
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 harq_init(harq_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL) {
uint32_t i;
bzero(q, sizeof(harq_t));
memcpy(&q->cell, &cell, sizeof(lte_cell_t));
ret = ra_tbs_from_idx(26, cell.nof_prb);
if (ret != LIBLTE_ERROR) {
q->max_cb = (uint32_t) ret / (MAX_LONG_CB - 24) + 1;
q->pdsch_w_buff_f = malloc(sizeof(float*) * q->max_cb);
if (!q->pdsch_w_buff_f) {
perror("malloc");
return LIBLTE_ERROR;
}
q->pdsch_w_buff_c = malloc(sizeof(uint8_t*) * q->max_cb);
if (!q->pdsch_w_buff_c) {
perror("malloc");
return LIBLTE_ERROR;
}
// FIXME: Use HARQ buffer limitation based on UE category
q->w_buff_size = cell.nof_prb * MAX_PDSCH_RE(cell.cp) * 6 * 10;
for (i=0;i<q->max_cb;i++) {
q->pdsch_w_buff_f[i] = vec_malloc(sizeof(float) * q->w_buff_size);
if (!q->pdsch_w_buff_f[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
q->pdsch_w_buff_c[i] = vec_malloc(sizeof(uint8_t) * q->w_buff_size);
if (!q->pdsch_w_buff_c[i]) {
perror("malloc");
return LIBLTE_ERROR;
}
}
ret = LIBLTE_SUCCESS;
}
}
return ret;
}
void harq_free(harq_t *q) {
if (q) {
uint32_t i;
if (q->pdsch_w_buff_f) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_f[i]) {
free(q->pdsch_w_buff_f[i]);
}
}
free(q->pdsch_w_buff_f);
}
if (q->pdsch_w_buff_c) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_c[i]) {
free(q->pdsch_w_buff_c[i]);
}
}
free(q->pdsch_w_buff_c);
}
bzero(q, sizeof(harq_t));
}
}
void harq_reset(harq_t *q) {
int i;
if (q->pdsch_w_buff_f) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_f[i]) {
bzero(q->pdsch_w_buff_f[i], sizeof(float) * q->w_buff_size);
}
}
}
if (q->pdsch_w_buff_c) {
for (i=0;i<q->max_cb;i++) {
if (q->pdsch_w_buff_c[i]) {
bzero(q->pdsch_w_buff_c[i], sizeof(uint8_t) * q->w_buff_size);
}
}
}
bzero(&q->mcs, sizeof(ra_mcs_t));
bzero(&q->cb_segm, sizeof(struct cb_segm));
bzero(&q->prb_alloc, sizeof(ra_prb_t));
}
int harq_setup(harq_t *q, ra_mcs_t mcs, ra_prb_t *prb_alloc) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
mcs.tbs > 0)
{
q->mcs = mcs;
memcpy(&q->prb_alloc, prb_alloc, sizeof(ra_prb_t));
q->N_symb_ul = 2*(CP_NSYMB(q->cell.cp)-1);
q->nof_prb_pusch_init = q->prb_alloc.slot[0].nof_prb;
codeblock_segmentation(&q->cb_segm, mcs.tbs);
if (q->cb_segm.C > q->max_cb) {
fprintf(stderr, "Codeblock segmentation returned more CBs (%d) than allocated (%d)\n",
q->cb_segm.C, q->max_cb);
return LIBLTE_ERROR;
}
ret = LIBLTE_SUCCESS;
}
return ret;
}

@ -46,7 +46,7 @@
const lte_mod_t modulations[4] = const static lte_mod_t modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 }; { LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
//#define DEBUG_IDX //#define DEBUG_IDX
@ -223,39 +223,13 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
goto clean; goto clean;
} }
} }
if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
demod_soft_init(&q->demod, q->max_symbols); demod_soft_init(&q->demod, q->max_symbols);
demod_soft_alg_set(&q->demod, APPROX); demod_soft_alg_set(&q->demod, APPROX);
q->rnti_is_set = false; sch_init(&q->dl_sch);
if (tcod_init(&q->encoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Coder\n");
goto clean;
}
if (tdec_init(&q->decoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Decoder\n");
goto clean;
}
// Allocate floats for reception (LLRs)
q->cb_in = malloc(sizeof(uint8_t) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->cb_out = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12)); q->rnti_is_set = false;
if (!q->cb_out) {
goto clean;
}
// Allocate floats for reception (LLRs) // Allocate floats for reception (LLRs)
q->pdsch_e = malloc(sizeof(float) * q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64)); q->pdsch_e = malloc(sizeof(float) * q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64));
@ -295,12 +269,6 @@ int pdsch_init(pdsch_t *q, lte_cell_t cell) {
void pdsch_free(pdsch_t *q) { void pdsch_free(pdsch_t *q) {
int i; int i;
if (q->cb_in) {
free(q->cb_in);
}
if (q->cb_out) {
free(q->cb_out);
}
if (q->pdsch_e) { if (q->pdsch_e) {
free(q->pdsch_e); free(q->pdsch_e);
} }
@ -327,9 +295,8 @@ void pdsch_free(pdsch_t *q) {
modem_table_free(&q->mod[i]); modem_table_free(&q->mod[i]);
} }
demod_soft_free(&q->demod); demod_soft_free(&q->demod);
tdec_free(&q->decoder);
tcod_free(&q->encoder);
precoding_free(&q->precoding); precoding_free(&q->precoding);
sch_free(&q->dl_sch);
bzero(q, sizeof(pdsch_t)); bzero(q, sizeof(pdsch_t));
@ -347,334 +314,12 @@ int pdsch_set_rnti(pdsch_t *q, uint16_t rnti) {
q->rnti = rnti; q->rnti = rnti;
return LIBLTE_SUCCESS; return LIBLTE_SUCCESS;
} }
/* Calculate Codeblock Segmentation as in Section 5.1.2 of 36.212 */
static int codeblock_segmentation(struct cb_segm *s, uint32_t tbs) {
uint32_t Bp, B, idx1;
int ret;
B = tbs + 24;
/* Calculate CB sizes */
if (B < MAX_LONG_CB) {
s->C = 1;
Bp = B;
} else {
s->C = (uint32_t) ceilf((float) B / (MAX_LONG_CB - 24));
Bp = B + 24 * s->C;
}
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 / (MAX_LONG_CB - 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(uint8_t*) * p->max_cb);
if (!p->pdsch_w_buff_c) {
perror("malloc");
return LIBLTE_ERROR;
}
// FIXME: Use HARQ buffer limitation based on UE category
p->w_buff_size = p->cell.nof_prb * MAX_PDSCH_RE(p->cell.cp) * 6 * 10;
for (i=0;i<p->max_cb;i++) {
p->pdsch_w_buff_f[i] = vec_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] = vec_malloc(sizeof(uint8_t) * 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));
}
}
void pdsch_harq_reset(pdsch_harq_t *p) {
int i;
if (p->pdsch_w_buff_f) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_f[i]) {
bzero(p->pdsch_w_buff_f[i], sizeof(float) * p->w_buff_size);
}
}
}
if (p->pdsch_w_buff_c) {
for (i=0;i<p->max_cb;i++) {
if (p->pdsch_w_buff_c[i]) {
bzero(p->pdsch_w_buff_c[i], sizeof(uint8_t) * p->w_buff_size);
}
}
}
bzero(&p->mcs, sizeof(ra_mcs_t));
bzero(&p->cb_segm, sizeof(struct cb_segm));
bzero(&p->prb_alloc, sizeof(ra_prb_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)
{
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 * lte_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;
}
return ret;
}
float pdsch_average_noi(pdsch_t *q) {
return q->average_nof_iterations;
}
uint32_t pdsch_last_noi(pdsch_t *q) {
return q->nof_iterations;
}
/* Decode a transport block according to 36.212 5.3.2
*
*/
int pdsch_decode_tb(pdsch_t *q, uint8_t *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par_rx, par_tx;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
float *e_bits = q->pdsch_e;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL &&
data != NULL &&
harq_process != NULL &&
nb_e < q->max_symbols * Qm)
{
rp = 0;
rp = 0;
wp = 0;
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
bool early_stop = true;
for (i = 0; i < harq_process->cb_segm.C && early_stop; i++) {
/* Get read/write lengths */
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = harq_process->cb_segm.K2;
}
if (harq_process->cb_segm.C == 1) {
rlen = cb_len;
} else {
rlen = cb_len - 24;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
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);
/* Rate Unmatching */
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_out, 3 * cb_len + 12, rv_idx)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
/* Turbo Decoding with CRC-based early stopping */
q->nof_iterations = 0;
uint32_t len_crc;
uint8_t *cb_in_ptr;
crc_t *crc_ptr;
early_stop = false;
tdec_reset(&q->decoder, cb_len);
do {
tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len);
q->nof_iterations++;
if (harq_process->cb_segm.C > 1) {
len_crc = cb_len;
cb_in_ptr = q->cb_in;
crc_ptr = &q->crc_cb;
} else {
len_crc = tbs+24;
bzero(q->cb_in, F*sizeof(uint8_t));
cb_in_ptr = &q->cb_in[F];
crc_ptr = &q->crc_tb;
}
tdec_decision(&q->decoder, q->cb_in, cb_len);
/* Check Codeblock CRC and stop early if incorrect */
if (!crc_checksum(crc_ptr, cb_in_ptr, len_crc)) {
early_stop = true;
}
} while (q->nof_iterations < TDEC_MAX_ITERATIONS && !early_stop);
q->average_nof_iterations = VEC_EMA((float) q->nof_iterations, q->average_nof_iterations, 0.2);
// If CB CRC is not correct, early_stop will be false and wont continue with rest of CBs
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(uint8_t));
} else {
DEBUG("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[F], (rlen - F - 24) * sizeof(uint8_t));
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(uint8_t));
}
/* Set read/write pointers */
wp += (rlen - F);
rp += n_e;
}
if (!early_stop) {
INFO("CB %d failed. TB is erroneous.\n",i-1);
return LIBLTE_ERROR;
} else {
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
// Compute transport block CRC
par_rx = crc_checksum(&q->crc_tb, data, tbs);
// check parity bits
par_tx = bit_unpack(&p_parity, 24);
if (!par_rx) {
INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0);
}
if (par_rx == par_tx) {
INFO("TB decoded OK\n",i);
return LIBLTE_SUCCESS;
} else {
INFO("Error in TB parity\n",i);
return LIBLTE_ERROR;
}
}
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
/** 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], float noise_estimate, uint8_t *data, uint32_t subframe, int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_estimate, uint8_t *data, uint32_t subframe,
pdsch_harq_t *harq_process, uint32_t rv_idx) harq_t *harq_process, uint32_t rv_idx)
{ {
/* Set pointers for layermapping & precoding */ /* Set pointers for layermapping & precoding */
@ -720,7 +365,6 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_
} }
} }
/* TODO: only diversity is supported */ /* TODO: only diversity is supported */
if (q->cell.nof_ports == 1) { if (q->cell.nof_ports == 1) {
/* no need for layer demapping */ /* no need for layer demapping */
@ -744,7 +388,7 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_
/* descramble */ /* descramble */
scrambling_f_offset(&q->seq_pdsch[subframe], q->pdsch_e, 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, harq_process, rv_idx); return dlsch_decode(&q->dl_sch, q->pdsch_e, data, nof_bits, nof_bits_e, harq_process, rv_idx);
} else { } else {
fprintf(stderr, "Must call pdsch_set_rnti() before calling pdsch_decode()\n"); fprintf(stderr, "Must call pdsch_set_rnti() before calling pdsch_decode()\n");
return LIBLTE_ERROR; return LIBLTE_ERROR;
@ -755,131 +399,11 @@ int pdsch_decode(pdsch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_
} }
} }
/* Encode a transport block according to 36.212 5.3.2
*
*/
int pdsch_encode_tb(pdsch_t *q, uint8_t *data, uint32_t tbs, uint32_t nb_e,
pdsch_harq_t *harq_process, uint32_t rv_idx)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
uint8_t *e_bits = q->pdsch_e;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL &&
data != NULL &&
harq_process != NULL &&
nb_e < q->max_symbols * Qm)
{
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
if (q->rnti_is_set) {
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);
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 < harq_process->cb_segm.F; i++) {
q->cb_in[i] = LTE_NULL_BIT;
}
}
wp = 0;
rp = 0;
for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read lengths */
if (i < harq_process->cb_segm.C - harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K1;
} else {
cb_len = harq_process->cb_segm.K2;
}
if (harq_process->cb_segm.C > 1) {
rlen = cb_len - 24;
} else {
rlen = cb_len;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
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);
if (rv_idx == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(uint8_t));
} 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[F], &data[rp], (rlen - F - 24) * sizeof(uint8_t));
memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(uint8_t));
}
if (harq_process->cb_segm.C > 1) {
/* Attach Codeblock CRC */
crc_attach(&q->crc_cb, q->cb_in, rlen);
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d Len=%d: ", i, cb_len);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, q->cb_in, (uint8_t*) q->cb_out, cb_len);
}
/* Rate matching */
if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
(uint8_t*) 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);
wp += n_e;
}
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
ret = LIBLTE_SUCCESS;
} else {
fprintf(stderr, "Must call pdsch_set_rnti() to set the encoder/decoder RNTI\n");
}
}
return ret;
}
/** 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, uint8_t *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe, int pdsch_encode(pdsch_t *q, uint8_t *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe,
pdsch_harq_t *harq_process, uint32_t rv_idx) 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;
@ -929,7 +453,7 @@ int pdsch_encode(pdsch_t *q, uint8_t *data, cf_t *sf_symbols[MAX_PORTS], uint32_
} }
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, harq_process, rv_idx)) { if (dlsch_encode(&q->dl_sch, data, q->pdsch_e, 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;
} }

@ -0,0 +1,375 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "prb.h"
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
#define MAX_PUSCH_RE(cp) (2 * CP_NSYMB(cp) * 12)
const static lte_mod_t modulations[4] =
{ LTE_BPSK, LTE_QPSK, LTE_QAM16, LTE_QAM64 };
//#define DEBUG_IDX
#ifdef DEBUG_IDX
cf_t *offset_original=NULL;
extern int indices[100000];
extern int indices_ptr;
#endif
int pusch_cp(pusch_t *q, cf_t *input, cf_t *output, ra_prb_t *prb_alloc,
uint32_t nsubframe, bool put)
{
return -1;
}
/**
* Puts PUSCH in slot number 1
*
* Returns the number of symbols written to sf_symbols
*
* 36.211 10.3 section 6.3.5
*/
int pusch_put(pusch_t *q, cf_t *pusch_symbols, cf_t *sf_symbols,
ra_prb_t *prb_alloc, uint32_t subframe) {
return pusch_cp(q, pusch_symbols, sf_symbols, prb_alloc, subframe, true);
}
/**
* Extracts PUSCH from slot number 1
*
* Returns the number of symbols written to PUSCH
*
* 36.211 10.3 section 6.3.5
*/
int pusch_get(pusch_t *q, cf_t *sf_symbols, cf_t *pusch_symbols,
ra_prb_t *prb_alloc, uint32_t subframe) {
return pusch_cp(q, sf_symbols, pusch_symbols, prb_alloc, subframe, false);
}
/** Initializes the PDCCH transmitter and receiver */
int pusch_init(pusch_t *q, lte_cell_t cell) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
int i;
if (q != NULL &&
lte_cell_isvalid(&cell))
{
bzero(q, sizeof(pusch_t));
ret = LIBLTE_ERROR;
q->cell = cell;
q->max_symbols = q->cell.nof_prb * MAX_PUSCH_RE(q->cell.cp);
INFO("Init PUSCH: %d ports %d PRBs, max_symbols: %d\n", q->cell.nof_ports,
q->cell.nof_prb, q->max_symbols);
for (i = 0; i < 4; i++) {
if (modem_table_lte(&q->mod[i], modulations[i], true)) {
goto clean;
}
}
demod_soft_init(&q->demod, q->max_symbols);
demod_soft_alg_set(&q->demod, APPROX);
sch_init(&q->dl_sch);
q->rnti_is_set = false;
// Allocate floats for reception (LLRs)
q->pusch_e = malloc(sizeof(float) * q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pusch_e) {
goto clean;
}
// Allocate buffers for q bits for coded RI and ACK bits
q->pusch_q_ack = malloc(sizeof(uint8_t) * 4 * q->cell.nof_prb * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pusch_q_ack) {
goto clean;
}
q->pusch_q_ri = malloc(sizeof(uint8_t) * 4 * q->cell.nof_prb * lte_mod_bits_x_symbol(LTE_QAM64));
if (!q->pusch_q_ri) {
goto clean;
}
q->pusch_d = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->pusch_d) {
goto clean;
}
for (i = 0; i < q->cell.nof_ports; i++) {
q->ce[i] = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->ce[i]) {
goto clean;
}
q->pusch_x[i] = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->pusch_x[i]) {
goto clean;
}
q->pusch_symbols[i] = malloc(sizeof(cf_t) * q->max_symbols);
if (!q->pusch_symbols[i]) {
goto clean;
}
}
ret = LIBLTE_SUCCESS;
}
clean:
if (ret == LIBLTE_ERROR) {
pusch_free(q);
}
return ret;
}
void pusch_free(pusch_t *q) {
int i;
if (q->pusch_e) {
free(q->pusch_e);
}
if (q->pusch_d) {
free(q->pusch_d);
}
for (i = 0; i < q->cell.nof_ports; i++) {
if (q->ce[i]) {
free(q->ce[i]);
}
if (q->pusch_x[i]) {
free(q->pusch_x[i]);
}
if (q->pusch_symbols[i]) {
free(q->pusch_symbols[i]);
}
}
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
sequence_free(&q->seq_pusch[i]);
}
for (i = 0; i < 4; i++) {
modem_table_free(&q->mod[i]);
}
demod_soft_free(&q->demod);
sch_free(&q->dl_sch);
bzero(q, sizeof(pusch_t));
}
int pusch_set_rnti(pusch_t *q, uint16_t rnti) {
uint32_t i;
for (i = 0; i < NSUBFRAMES_X_FRAME; i++) {
if (sequence_pusch(&q->seq_pusch[i], rnti, 2 * i, q->cell.id,
q->max_symbols * lte_mod_bits_x_symbol(LTE_QAM64))) {
return LIBLTE_ERROR;
}
}
q->rnti_is_set = true;
q->rnti = rnti;
return LIBLTE_SUCCESS;
}
/** Decodes the PUSCH from the received symbols
*/
int pusch_decode(pusch_t *q, cf_t *sf_symbols, cf_t *ce[MAX_PORTS], float noise_estimate, uint8_t *data, uint32_t subframe,
harq_t *harq_process, uint32_t rv_idx)
{
/* Set pointers for layermapping & precoding */
uint32_t i, n;
cf_t *x[MAX_LAYERS];
uint32_t nof_symbols, nof_bits, nof_bits_e;
if (q != NULL &&
sf_symbols != NULL &&
data != NULL &&
subframe < 10 &&
harq_process != NULL)
{
if (q->rnti_is_set) {
nof_bits = harq_process->mcs.tbs;
nof_symbols = harq_process->prb_alloc.re_sf[subframe];
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(harq_process->mcs.mod);
INFO("Decoding PUSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
subframe, lte_mod_string(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++) {
x[i] = q->pusch_x[i];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
/* extract symbols */
n = pusch_get(q, sf_symbols, q->pusch_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;
}
/* extract channel estimates */
for (i = 0; i < q->cell.nof_ports; i++) {
n = pusch_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;
}
}
/* 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, sqrt(0.5));
demod_soft_table_set(&q->demod, &q->mod[harq_process->mcs.mod]);
demod_soft_demodulate(&q->demod, q->pusch_d, q->pusch_e, nof_symbols);
/* descramble */
scrambling_f_offset(&q->seq_pusch[subframe], q->pusch_e, 0, nof_bits_e);
return ulsch_decode(&q->dl_sch, q->pusch_e, data, nof_bits, nof_bits_e, harq_process, rv_idx);
} else {
fprintf(stderr, "Must call pusch_set_rnti() before calling pusch_decode()\n");
return LIBLTE_ERROR;
}
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
int pusch_encode(pusch_t *q, uint8_t *data, cf_t *sf_symbols[MAX_PORTS], uint32_t subframe,
harq_t *harq_process, uint32_t rv_idx)
{
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
return pusch_uci_encode(q, data, uci_data, sf_symbols, subframe, harq_process, rv_idx);
}
/** Converts the PUSCH data bits to symbols mapped to the slot ready for transmission
*/
int pusch_uci_encode(pusch_t *q, uint8_t *data, uci_data_t uci_data,
cf_t *sf_symbols[MAX_PORTS], uint32_t subframe,
harq_t *harq_process, uint32_t rv_idx)
{
int i;
uint32_t nof_symbols, nof_bits_ulsch, nof_bits_e;
/* Set pointers for layermapping & precoding */
cf_t *x[MAX_LAYERS];
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q != NULL &&
data != NULL &&
subframe < 10 &&
harq_process != NULL)
{
if (q->rnti_is_set) {
for (i=0;i<q->cell.nof_ports;i++) {
if (sf_symbols[i] == NULL) {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
nof_bits_ulsch = harq_process->mcs.tbs;
nof_symbols = 2*harq_process->prb_alloc.slot[0].nof_prb*RE_X_RB*(CP_NSYMB(q->cell.cp)-1);
nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (harq_process->mcs.tbs == 0) {
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_bits_ulsch > nof_bits_e) {
fprintf(stderr, "Invalid code rate %.2f\n", (float) nof_bits_ulsch / nof_bits_e);
return LIBLTE_ERROR_INVALID_INPUTS;
}
if (nof_symbols > q->max_symbols) {
fprintf(stderr,
"Error too many RE per subframe (%d). PUSCH configured for %d RE (%d PRB)\n",
nof_symbols, q->max_symbols, q->cell.nof_prb);
return LIBLTE_ERROR_INVALID_INPUTS;
}
INFO("Encoding PUSCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
subframe, lte_mod_string(harq_process->mcs.mod), nof_bits_ulsch, nof_symbols, nof_bits_e, rv_idx);
/* number of layers equals number of ports */
for (i = 0; i < q->cell.nof_ports; i++) {
x[i] = q->pusch_x[i];
}
memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (MAX_LAYERS - q->cell.nof_ports));
if (ulsch_encode(&q->dl_sch, data, uci_data, q->pusch_e, nof_bits_e,
q->pusch_q_ack, q->pusch_q_ri, harq_process, rv_idx))
{
fprintf(stderr, "Error encoding TB\n");
return LIBLTE_ERROR;
}
scrambling_b_offset_pusch(&q->seq_pusch[subframe], (uint8_t*) q->pusch_e, 0, nof_bits_e);
mod_modulate(&q->mod[harq_process->mcs.mod], (uint8_t*) q->pusch_e, q->pusch_d, nof_bits_e);
/* mapping to resource elements */
for (i = 0; i < q->cell.nof_ports; i++) {
pusch_put(q, q->pusch_symbols[i], sf_symbols[i], &harq_process->prb_alloc, subframe);
}
ret = LIBLTE_SUCCESS;
} else {
fprintf(stderr, "Must call pusch_set_rnti() to set the encoder/decoder RNTI\n");
}
}
return ret;
}

@ -0,0 +1,547 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/pusch.h"
#include "liblte/phy/phch/sch.h"
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/bit.h"
#include "liblte/phy/utils/debug.h"
#include "liblte/phy/utils/vector.h"
int sch_init(sch_t *q) {
int ret = LIBLTE_ERROR_INVALID_INPUTS;
if (q) {
bzero(q, sizeof(sch_t));
if (crc_init(&q->crc_tb, LTE_CRC24A, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (crc_init(&q->crc_cb, LTE_CRC24B, 24)) {
fprintf(stderr, "Error initiating CRC\n");
goto clean;
}
if (tcod_init(&q->encoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Coder\n");
goto clean;
}
if (tdec_init(&q->decoder, MAX_LONG_CB)) {
fprintf(stderr, "Error initiating Turbo Decoder\n");
goto clean;
}
// Allocate floats for reception (LLRs)
q->cb_in = malloc(sizeof(uint8_t) * MAX_LONG_CB);
if (!q->cb_in) {
goto clean;
}
q->cb_out = malloc(sizeof(float) * (3 * MAX_LONG_CB + 12));
if (!q->cb_out) {
goto clean;
}
ret = LIBLTE_SUCCESS;
}
clean:
if (ret == LIBLTE_ERROR) {
sch_free(q);
}
return ret;
}
void sch_free(sch_t *q) {
if (q->cb_in) {
free(q->cb_in);
}
if (q->cb_out) {
free(q->cb_out);
}
tdec_free(&q->decoder);
tcod_free(&q->encoder);
bzero(q, sizeof(sch_t));
}
float sch_average_noi(sch_t *q) {
return q->average_nof_iterations;
}
uint32_t sch_last_noi(sch_t *q) {
return q->nof_iterations;
}
/* Encode a transport block according to 36.212 5.3.2
*
*/
static int encode_tb(sch_t *q, uint8_t *data, uint8_t *e_bits, uint32_t tbs, uint32_t nb_e,
harq_t *harq_process, uint32_t rv_idx)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
int ret = LIBLTE_ERROR_INVALID_INPUTS;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL &&
data != NULL &&
harq_process != NULL)
{
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
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);
if (VERBOSE_ISDEBUG()) {
DEBUG("DATA: ", 0);
vec_fprint_b(stdout, data, tbs);
DEBUG("PARITY: ", 0);
vec_fprint_b(stdout, parity, 24);
}
}
wp = 0;
rp = 0;
for (i = 0; i < harq_process->cb_segm.C; i++) {
/* Get read lengths */
if (i < harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K2;
} else {
cb_len = harq_process->cb_segm.K1;
}
if (harq_process->cb_segm.C > 1) {
rlen = cb_len - 24;
} else {
rlen = cb_len;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
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);
if (rv_idx == 0) {
/* Copy data to another buffer, making space for the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
// Copy data
memcpy(&q->cb_in[F], &data[rp], (rlen - F) * sizeof(uint8_t));
} 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[F], &data[rp], (rlen - 24 - F) * sizeof(uint8_t));
memcpy(&q->cb_in[rlen - 24], parity, 24 * sizeof(uint8_t));
}
/* Filler bits are treated like zeros for the CB CRC calculation */
for (int j = 0; j < F; j++) {
q->cb_in[j] = 0;
}
/* Attach Codeblock CRC */
if (harq_process->cb_segm.C > 1) {
crc_attach(&q->crc_cb, q->cb_in, rlen);
}
/* Set the filler bits to <NULL> */
for (int j = 0; j < F; j++) {
q->cb_in[j] = TX_NULL;
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d: ", i);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
/* Turbo Encoding */
tcod_encode(&q->encoder, q->cb_in, (uint8_t*) q->cb_out, cb_len);
}
/* Rate matching */
if (rm_turbo_tx(harq_process->pdsch_w_buff_c[i], harq_process->w_buff_size,
(uint8_t*) 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);
wp += n_e;
}
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
ret = LIBLTE_SUCCESS;
}
return ret;
}
/* Decode a transport block according to 36.212 5.3.2
*
*/
static int decode_tb(sch_t *q, float *e_bits, uint8_t *data, uint32_t tbs, uint32_t nb_e,
harq_t *harq_process, uint32_t rv_idx)
{
uint8_t parity[24];
uint8_t *p_parity = parity;
uint32_t par_rx, par_tx;
uint32_t i;
uint32_t cb_len, rp, wp, rlen, F, n_e;
uint32_t Qm = lte_mod_bits_x_symbol(harq_process->mcs.mod);
if (q != NULL &&
data != NULL &&
harq_process != NULL)
{
rp = 0;
rp = 0;
wp = 0;
uint32_t Gp = nb_e / Qm;
uint32_t gamma = Gp%harq_process->cb_segm.C;
bool early_stop = true;
for (i = 0; i < harq_process->cb_segm.C && early_stop; i++) {
/* Get read/write lengths */
if (i < harq_process->cb_segm.C2) {
cb_len = harq_process->cb_segm.K2;
} else {
cb_len = harq_process->cb_segm.K1;
}
if (harq_process->cb_segm.C == 1) {
rlen = cb_len;
} else {
rlen = cb_len - 24;
}
if (i == 0) {
F = harq_process->cb_segm.F;
} else {
F = 0;
}
if (i <= harq_process->cb_segm.C - gamma - 1) {
n_e = Qm * (Gp/harq_process->cb_segm.C);
} else {
n_e = Qm * ((uint32_t) ceilf((float) Gp/harq_process->cb_segm.C));
}
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);
/* Rate Unmatching */
if (rm_turbo_rx(harq_process->pdsch_w_buff_f[i], harq_process->w_buff_size,
&e_bits[rp], n_e,
(float*) q->cb_out, 3 * cb_len + 12, rv_idx, F)) {
fprintf(stderr, "Error in rate matching\n");
return LIBLTE_ERROR;
}
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d RMOUT: ", i);
vec_fprint_f(stdout, q->cb_out, 3*cb_len+12);
}
/* Turbo Decoding with CRC-based early stopping */
q->nof_iterations = 0;
uint32_t len_crc;
uint8_t *cb_in_ptr;
crc_t *crc_ptr;
early_stop = false;
tdec_reset(&q->decoder, cb_len);
do {
tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len);
q->nof_iterations++;
if (harq_process->cb_segm.C > 1) {
len_crc = cb_len;
cb_in_ptr = q->cb_in;
crc_ptr = &q->crc_cb;
} else {
len_crc = tbs+24;
cb_in_ptr = &q->cb_in[F];
crc_ptr = &q->crc_tb;
}
tdec_decision(&q->decoder, q->cb_in, cb_len);
/* Check Codeblock CRC and stop early if incorrect */
if (!crc_checksum(crc_ptr, cb_in_ptr, len_crc)) {
early_stop = true;
}
} while (q->nof_iterations < TDEC_MAX_ITERATIONS && !early_stop);
q->average_nof_iterations = VEC_EMA((float) q->nof_iterations, q->average_nof_iterations, 0.2);
if (VERBOSE_ISDEBUG()) {
DEBUG("CB#%d IN: ", i);
vec_fprint_b(stdout, q->cb_in, cb_len);
}
// If CB CRC is not correct, early_stop will be false and wont continue with rest of CBs
/* Copy data to another buffer, removing the Codeblock CRC */
if (i < harq_process->cb_segm.C - 1) {
memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(uint8_t));
} else {
DEBUG("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[F], (rlen - F - 24) * sizeof(uint8_t));
memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(uint8_t));
}
/* Set read/write pointers */
wp += (rlen - F);
rp += n_e;
}
if (!early_stop) {
INFO("CB %d failed. TB is erroneous.\n",i-1);
return LIBLTE_ERROR;
} else {
INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp);
// Compute transport block CRC
par_rx = crc_checksum(&q->crc_tb, data, tbs);
// check parity bits
par_tx = bit_unpack(&p_parity, 24);
if (!par_rx) {
INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0);
}
if (par_rx == par_tx) {
INFO("TB decoded OK\n",i);
return LIBLTE_SUCCESS;
} else {
INFO("Error in TB parity\n",i);
return LIBLTE_ERROR;
}
}
} else {
return LIBLTE_ERROR_INVALID_INPUTS;
}
}
int dlsch_decode(sch_t *q, float *e_bits, uint8_t *data, uint32_t tbs, uint32_t nb_e,
harq_t *harq_process, uint32_t rv_idx)
{
return decode_tb(q, e_bits, data, tbs, nb_e, harq_process, rv_idx);
}
int dlsch_encode(sch_t *q, uint8_t *data, uint8_t *e_bits, uint32_t tbs, uint32_t nb_e,
harq_t *harq_process, uint32_t rv_idx) {
return encode_tb(q, data, e_bits, tbs, nb_e, harq_process, rv_idx);
}
int ulsch_decode(sch_t *q, float *e_bits, uint8_t *data, uint32_t tbs, uint32_t nb_e,
harq_t *harq_process, uint32_t rv_idx)
{
return decode_tb(q, e_bits, data, tbs, nb_e, harq_process, rv_idx);
}
uint8_t ulsch_y_idx[10000];
uint8_t ulsch_y_mat[10000];
/* UL-SCH channel interleaver according to 5.5.2.8 of 36.212 */
void ulsch_interleave(uint8_t *q_bits, uint32_t nb_q,
uint8_t q_bits_ack[6], uint32_t Q_ack,
uint8_t q_bits_ri[6], uint32_t Q_ri,
uint32_t Q_m)
{
uint32_t C_mux;
uint32_t H_prime;
uint32_t H_prime_total;
uint32_t R_mux;
uint32_t R_prime_mux;
uint32_t i;
uint32_t j;
uint32_t k;
uint32_t r;
uint32_t idx;
uint32_t ri_column_set[4] = {1, 4, 7, 10};
uint32_t ack_column_set[4] = {2, 3, 8, 9};
uint32_t C_ri;
uint32_t C_ack;
uint32_t N_pusch_symbs = 12;
// Step 1: Define C_mux
C_mux = N_pusch_symbs;
// Step 2: Define R_mux and R_prime_mux
H_prime = nb_q;
H_prime_total = H_prime + Q_ri;
R_mux = (H_prime_total*Q_m)/C_mux;
R_prime_mux = R_mux/Q_m;
// Initialize the matricies
//printf("Cmux*R_prime=%d*%d=%d\n",C_mux, R_prime_mux, C_mux*R_prime_mux);
for(i=0; i<C_mux*R_prime_mux; i++) {
ulsch_y_idx[i] = 100;
}
for(i=0; i<C_mux*R_mux; i++) {
ulsch_y_mat[i] = 0;
}
// Step 3: Interleave the RI control bits
i = 0;
j = 0;
r = R_prime_mux-1;
while(i < Q_ri) {
C_ri = ri_column_set[j];
ulsch_y_idx[r*C_mux + C_ri] = 1;
for(k=0; k<Q_m; k++) {
ulsch_y_mat[(C_mux*r*Q_m) + C_ri*Q_m + k] = q_bits_ri[Q_m*i+k];
}
i++;
r = R_prime_mux - 1 - i/4;
j = (j + 3) % 4;
}
// Step 4: Interleave the data bits
i = 0;
k = 0;
while(k < H_prime) {
if(ulsch_y_idx[i] == 100) {
ulsch_y_idx[i] = 1;
for(j=0; j<Q_m; j++) {
ulsch_y_mat[i*Q_m + j] = q_bits[Q_m*k+j];
}
k++;
}
i++;
}
// Step 5: Interleave the ACK control bits
i = 0;
j = 0;
r = R_prime_mux-1;
while(i < Q_ack/Q_m) {
C_ack = ack_column_set[j];
ulsch_y_idx[r*C_mux + C_ack] = 2;
for(k=0; k<Q_m; k++) {
ulsch_y_mat[(C_mux*r*Q_m) + C_ack*Q_m + k] = q_bits_ack[Q_m*i+k];
}
i++;
r = R_prime_mux - 1 - i/4;
j = (j + 3) % 4;
}
// Step 6: Read out the bits
idx = 0;
for(i=0; i<C_mux; i++) {
for(j=0; j<R_prime_mux; j++) {
for(k=0; k<Q_m; k++) {
q_bits[idx++] = ulsch_y_mat[j*C_mux*Q_m + i*Q_m + k];
}
}
}
}
int ulsch_encode(sch_t *q, uint8_t *data, uci_data_t uci_data, uint8_t *q_bits, uint32_t nb_q,
uint8_t *q_bits_ack, uint8_t *q_bits_ri,
harq_t *harq_process, uint32_t rv_idx)
{
int ret;
uint32_t e_offset = 0;
uint32_t Q_prime_ack = 0;
uint32_t Q_prime_ri = 0;
uint32_t Q_m = lte_mod_bits_x_symbol(harq_process->mcs.mod);
// Encode CQI
if (uci_data.uci_cqi_len > 0) {
ret = uci_encode_cqi(uci_data.uci_cqi, q_bits, uci_data.uci_cqi_len, nb_q);
if (ret) {
return ret;
}
}
e_offset += uci_data.uci_cqi_len;
// Encode UL-SCH
ret = encode_tb(q, data, &q_bits[e_offset], harq_process->mcs.tbs,
nb_q, harq_process, rv_idx);
if (ret) {
return ret;
}
// Encode ACK
if (uci_data.uci_ack_len > 0) {
Q_prime_ack = uci_encode_ri_ack(uci_data.uci_ack, uci_data.beta_ack, q_bits_ack, harq_process);
}
// Encode RI
if (uci_data.uci_ri_len > 0) {
Q_prime_ri = uci_encode_ri_ack(uci_data.uci_ri, uci_data.beta_ri, q_bits_ri, harq_process);
}
// Multiplexing and Interleaving
ulsch_interleave(q_bits, nb_q/Q_m,
q_bits_ack, Q_prime_ack,
q_bits_ri, Q_prime_ri,
Q_m);
return LIBLTE_SUCCESS;
}

@ -70,3 +70,11 @@ int sequence_pdsch(sequence_t *seq, unsigned short rnti, int q, uint32_t nslot,
bzero(seq, sizeof(sequence_t)); bzero(seq, sizeof(sequence_t));
return sequence_LTE_pr(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id); return sequence_LTE_pr(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id);
} }
/**
* 36.211 5.3.1
*/
int sequence_pusch(sequence_t *seq, unsigned short rnti, uint32_t nslot, uint32_t cell_id, uint32_t len) {
bzero(seq, sizeof(sequence_t));
return sequence_LTE_pr(seq, len, (rnti<<14) + ((nslot/2)<<9) + cell_id);
}

@ -0,0 +1,89 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#include "liblte/phy/phch/uci.h"
#include "liblte/phy/phch/harq.h"
#include "liblte/phy/common/phy_common.h"
#include "liblte/phy/utils/vector.h"
/* Encode UCI-CQI */
int uci_encode_cqi(uint8_t *data, uint8_t *e_bits, uint32_t tbs, uint32_t nb_e)
{
fprintf(stderr, "Not implemented\n");
return -1;
}
static uint32_t Q_prime(uint32_t O, float beta, harq_t *harq_process) {
uint32_t M_sc = harq_process->prb_alloc.slot[0].nof_prb * RE_X_RB;
uint32_t K = harq_process->cb_segm.C1*harq_process->cb_segm.K1 +
harq_process->cb_segm.C2*harq_process->cb_segm.K2;
uint32_t M_sc_init = harq_process->nof_prb_pusch_init * RE_X_RB;
uint32_t x = (uint32_t) ceilf((float) O*M_sc_init*harq_process->N_symb_ul*beta/K);
printf("%d=%d*%d*%d*%f/%d\n",x,O,M_sc_init,harq_process->N_symb_ul,beta,K);
uint32_t Q_prime = MIN(x, 4*M_sc);
return Q_prime;
}
/* Encode UCI RI and HARQ bits
* Currently only supporting 1-bit RI or 1-bit HARQ
*/
uint32_t uci_encode_ri_ack(uint8_t data, float beta, uint8_t *q_bits, harq_t *harq_process)
{
uint8_t Q_m = lte_mod_bits_x_symbol(harq_process->mcs.mod);
q_bits[0] = data;
q_bits[1] = 2;
for (int i=2;i<Q_m;i++) {
q_bits[i] = 3;
}
uint32_t Qprime = Q_prime(1, beta, harq_process);
for (int i=1;i<Qprime;i++) {
memcpy(&q_bits[i*Q_m], q_bits, Q_m*sizeof(uint8_t));
}
printf("Q_m: %d, Qprime: %d, beta: %f\n", Q_m, Qprime, beta);
return Qprime * Q_m;
}

@ -99,6 +99,7 @@ ADD_TEST(pdsch_test_qam16 pdsch_test -l 50000 -m 4 -n 100)
ADD_TEST(pdsch_test_qam64 pdsch_test -l 61664 -m 6 -n 100 -r 0) ADD_TEST(pdsch_test_qam64 pdsch_test -l 61664 -m 6 -n 100 -r 0)
BuildMex(MEXNAME pdsch SOURCES pdsch_test_mex.c LIBRARIES lte_phy liblte_mex) BuildMex(MEXNAME pdsch SOURCES pdsch_test_mex.c LIBRARIES lte_phy liblte_mex)
BuildMex(MEXNAME dlsch_encode SOURCES dlsch_encode_test_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# FILE TEST # FILE TEST
@ -125,6 +126,18 @@ ADD_TEST(phich_file_test phich_file_test -c 150 -n 50 -p 2 -i ${CMAKE_CURRENT_SO
ADD_TEST(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) ADD_TEST(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat)
ADD_TEST(pdsch_file_test pdsch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) ADD_TEST(pdsch_file_test pdsch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat)
########################################################################
# PUSCH TEST
########################################################################
ADD_EXECUTABLE(pusch_test pusch_test.c)
TARGET_LINK_LIBRARIES(pusch_test lte_phy)
BuildMex(MEXNAME ulsch_encode SOURCES ulsch_encode_test_mex.c LIBRARIES lte_phy liblte_mex)
######################################################################## ########################################################################
# PRACH TEST # PRACH TEST
######################################################################## ########################################################################

@ -0,0 +1,124 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define OUTLEN prhs[2]
#define TRBLKIN prhs[3]
#define NOF_INPUTS 4
void help()
{
mexErrMsgTxt
("[cwout] = liblte_dlsch_encode(ue, chs, outlen, trblkin)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
sch_t dlsch;
uint8_t *trblkin;
ra_mcs_t mcs;
ra_prb_t prb_alloc;
harq_t harq_process;
uint32_t rv;
if (nrhs < NOF_INPUTS) {
help();
return;
}
if (sch_init(&dlsch)) {
mexErrMsgTxt("Error initiating DL-SCH\n");
return;
}
lte_cell_t cell;
cell.nof_prb = 100;
cell.id=1;
if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ\n");
return;
}
mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin);
if (mcs.tbs == 0) {
mexErrMsgTxt("Error trblklen is zero\n");
return;
}
if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) {
mexErrMsgTxt("Field RV not found in dlsch config\n");
return;
}
char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation");
if (!strcmp(mod_str, "QPSK")) {
mcs.mod = LTE_QPSK;
} else if (!strcmp(mod_str, "16QAM")) {
mcs.mod = LTE_QAM16;
} else if (!strcmp(mod_str, "64QAM")) {
mcs.mod = LTE_QAM64;
} else {
mexErrMsgTxt("Unknown modulation\n");
return;
}
mxFree(mod_str);
if (harq_setup(&harq_process, mcs, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
uint32_t nof_bits_e = (uint32_t) mxGetScalar(OUTLEN);
uint8_t *e_bits = vec_malloc(nof_bits_e * sizeof(uint8_t));
if (!e_bits) {
return;
}
if (dlsch_encode(&dlsch, trblkin, e_bits, mcs.tbs, nof_bits_e, &harq_process, rv)) {
mexErrMsgTxt("Error encoding TB\n");
return;
}
if (nlhs >= 1) {
mexutils_write_uint8(e_bits, &plhs[0], nof_bits_e, 1);
}
sch_free(&dlsch);
free(trblkin);
free(e_bits);
return;
}

@ -58,7 +58,7 @@ dci_format_t dci_format = Format1A;
filesource_t fsrc; filesource_t fsrc;
pdcch_t pdcch; pdcch_t pdcch;
pdsch_t pdsch; pdsch_t pdsch;
pdsch_harq_t harq_process; 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;
@ -191,7 +191,7 @@ int base_init() {
} }
pdsch_set_rnti(&pdsch, rnti); pdsch_set_rnti(&pdsch, rnti);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
exit(-1); exit(-1);
} }
@ -217,7 +217,7 @@ void base_free() {
pdcch_free(&pdcch); pdcch_free(&pdcch);
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdsch_harq_free(&harq_process); harq_free(&harq_process);
regs_free(&regs); regs_free(&regs);
} }
@ -284,7 +284,7 @@ int main(int argc, char **argv) {
goto goout; goto goout;
} }
if (ra_dl.mcs.tbs > 0) { if (ra_dl.mcs.tbs > 0) {
if (pdsch_harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) { if (harq_setup(&harq_process, ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n"); fprintf(stderr, "Error configuring HARQ process\n");
goto goout; goto goout;
} }

@ -45,12 +45,14 @@ lte_cell_t cell = {
uint32_t cfi = 2; uint32_t cfi = 2;
uint32_t tbs = 0; uint32_t tbs = 0;
uint32_t nof_tbs = 0;
uint32_t subframe = 1; uint32_t subframe = 1;
lte_mod_t modulation = LTE_BPSK; lte_mod_t modulation = LTE_BPSK;
uint32_t rv_idx = 0; uint32_t rv_idx = 0;
void usage(char *prog) { void usage(char *prog) {
printf("Usage: %s [cpsrnfvmt] -l TBS \n", prog); printf("Usage: %s [Lcpsrnfvmt] -l TBS \n", prog);
printf("\t-L number of consequent TBS [Default 0]\n");
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);
@ -63,7 +65,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, "lcpnfvmtsr")) != -1) { while ((opt = getopt(argc, argv, "lLcpnfvmtsr")) != -1) {
switch(opt) { switch(opt) {
case 'm': case 'm':
switch(atoi(argv[optind])) { switch(atoi(argv[optind])) {
@ -94,6 +96,9 @@ void parse_args(int argc, char **argv) {
case 'l': case 'l':
tbs = atoi(argv[optind]); tbs = atoi(argv[optind]);
break; break;
case 'L':
nof_tbs = atoi(argv[optind]);
break;
case 'p': case 'p':
cell.nof_ports = atoi(argv[optind]); cell.nof_ports = atoi(argv[optind]);
break; break;
@ -128,14 +133,18 @@ int main(int argc, char **argv) {
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; harq_t harq_process;
uint32_t rv; uint32_t rv;
parse_args(argc,argv); parse_args(argc,argv);
bzero(&pdsch, sizeof(pdsch_t));
bzero(&harq_process, sizeof(harq_t));
bzero(ce, sizeof(cf_t*)*MAX_PORTS);
bzero(slot_symbols, sizeof(cf_t*)*MAX_PORTS);
nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB; nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB;
mcs.tbs = tbs;
mcs.mod = modulation; mcs.mod = modulation;
prb_alloc.slot[0].nof_prb = cell.nof_prb; prb_alloc.slot[0].nof_prb = cell.nof_prb;
@ -163,7 +172,7 @@ int main(int argc, char **argv) {
} }
} }
data = malloc(sizeof(uint8_t) * mcs.tbs); data = malloc(sizeof(uint8_t) * (tbs+nof_tbs));
if (!data) { if (!data) {
perror("malloc"); perror("malloc");
goto quit; goto quit;
@ -176,54 +185,59 @@ int main(int argc, char **argv) {
pdsch_set_rnti(&pdsch, 1234); pdsch_set_rnti(&pdsch, 1234);
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
goto quit; goto quit;
} }
if (pdsch_harq_setup(&harq_process, mcs, &prb_alloc)) { for (mcs.tbs = tbs;mcs.tbs<=tbs+nof_tbs;mcs.tbs++) {
fprintf(stderr, "Error configuring HARQ process\n"); if (VERBOSE_ISNONE()) {
goto quit; printf("Decoding TBS: %d\r",mcs.tbs);
} }
if (harq_setup(&harq_process, mcs, &prb_alloc)) {
for (i=0;i<mcs.tbs;i++) { fprintf(stderr, "Error configuring HARQ process\n");
data[i] = rand()%2;
}
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; goto quit;
} }
/* combine outputs */ for (i=0;i<mcs.tbs;i++) {
for (i=0;i<cell.nof_ports;i++) { data[i] = rand()%2;
for (j=0;j<nof_re;j++) { }
if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j]; for (rv=0;rv<=rv_idx;rv++) {
if (pdsch_encode(&pdsch, data, slot_symbols, subframe, &harq_process, rv)) {
fprintf(stderr, "Error encoding PDSCH\n");
goto quit;
}
/* combine outputs */
for (i=0;i<cell.nof_ports;i++) {
for (j=0;j<nof_re;j++) {
if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j];
}
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, 0, data, subframe, &harq_process, rv); int r = pdsch_decode(&pdsch, slot_symbols[0], ce, 0, data, subframe, &harq_process, rv);
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 TBS: %d\n", mcs.tbs);
ret = -1; ret = -1;
goto quit; 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); if (nof_tbs == 0) {
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:
pdsch_free(&pdsch); pdsch_free(&pdsch);
pdsch_harq_free(&harq_process); harq_free(&harq_process);
for (i=0;i<cell.nof_ports;i++) { for (i=0;i<cell.nof_ports;i++) {
if (ce[i]) { if (ce[i]) {

@ -59,7 +59,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
int nof_re; int nof_re;
ra_mcs_t mcs; ra_mcs_t mcs;
ra_prb_t prb_alloc; ra_prb_t prb_alloc;
pdsch_harq_t harq_process; harq_t harq_process;
uint32_t rv; uint32_t rv;
uint32_t rnti32; uint32_t rnti32;
@ -93,7 +93,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
} }
pdsch_set_rnti(&pdsch, (uint16_t) (rnti32 & 0xffff)); pdsch_set_rnti(&pdsch, (uint16_t) (rnti32 & 0xffff));
if (pdsch_harq_init(&harq_process, &pdsch)) { if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ process\n"); mexErrMsgTxt("Error initiating HARQ process\n");
return; return;
} }
@ -162,7 +162,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
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_harq_setup(&harq_process, mcs, &prb_alloc)) { if (harq_setup(&harq_process, mcs, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n"); mexErrMsgTxt("Error configuring HARQ process\n");
return; return;
} }

@ -0,0 +1,259 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/time.h>
#include "liblte/phy/phy.h"
lte_cell_t cell = {
6, // nof_prb
1, // nof_ports
0, // cell_id
CPNORM, // cyclic prefix
R_1_6, // PHICH resources
PHICH_NORM // PHICH length
};
uint32_t cfi = 2;
uint32_t tbs = 0;
uint32_t subframe = 1;
lte_mod_t modulation = LTE_QPSK;
uint32_t rv_idx = 0;
void usage(char *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);
printf("\t-v [set verbose to debug, default none]\n");
}
void parse_args(int argc, char **argv) {
int opt;
while ((opt = getopt(argc, argv, "lcpnfvmtsr")) != -1) {
switch(opt) {
case 'm':
switch(atoi(argv[optind])) {
case 1:
modulation = LTE_BPSK;
break;
case 2:
modulation = LTE_QPSK;
break;
case 4:
modulation = LTE_QAM16;
break;
case 6:
modulation = LTE_QAM64;
break;
default:
fprintf(stderr, "Invalid modulation %d. Possible values: "
"(1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64)\n", atoi(argv[optind]));
break;
}
break;
case 's':
subframe = atoi(argv[optind]);
break;
case 'r':
rv_idx = atoi(argv[optind]);
break;
case 'l':
tbs = atoi(argv[optind]);
break;
case 'p':
cell.nof_ports = atoi(argv[optind]);
break;
case 'n':
cell.nof_prb = atoi(argv[optind]);
break;
case 'c':
cell.id = atoi(argv[optind]);
break;
case 'v':
verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
if (tbs == 0) {
usage(argv[0]);
exit(-1);
}
}
int main(int argc, char **argv) {
pusch_t pusch;
uint32_t i, j;
uint8_t *data = NULL;
cf_t *ce[MAX_PORTS];
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;
harq_t harq_process;
uint32_t rv;
parse_args(argc,argv);
nof_re = 2 * CPNORM_NSYMB * cell.nof_prb * RE_X_RB;
mcs.tbs = tbs;
mcs.mod = modulation;
prb_alloc.slot[0].nof_prb = 1;
//for (i=0;i<prb_alloc.slot[0].nof_prb;i++) {
// prb_alloc.slot[0].prb_idx[i] = true;
//}
memcpy(&prb_alloc.slot[1], &prb_alloc.slot[0], sizeof(ra_prb_slot_t));
/* init memory */
for (i=0;i<cell.nof_ports;i++) {
ce[i] = malloc(sizeof(cf_t) * nof_re);
if (!ce[i]) {
perror("malloc");
goto quit;
}
for (j=0;j<nof_re;j++) {
ce[i][j] = 1;
}
slot_symbols[i] = calloc(sizeof(cf_t) , nof_re);
if (!slot_symbols[i]) {
perror("malloc");
goto quit;
}
}
data = malloc(sizeof(uint8_t) * mcs.tbs);
if (!data) {
perror("malloc");
goto quit;
}
if (pusch_init(&pusch, cell)) {
fprintf(stderr, "Error creating PDSCH object\n");
goto quit;
}
pusch_set_rnti(&pusch, 1234);
if (harq_init(&harq_process, cell)) {
fprintf(stderr, "Error initiating HARQ process\n");
goto quit;
}
if (harq_setup(&harq_process, mcs, &prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n");
goto quit;
}
for (i=0;i<mcs.tbs;i++) {
data[i] = rand()%2;
}
vec_fprint_b(stdout, data, mcs.tbs);
for (rv=0;rv<=rv_idx;rv++) {
printf("Encoding rv_idx=%d\n",rv);
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
uci_data.beta_ack = 2.0;
uci_data.uci_ack = 1;
uci_data.uci_ack_len = 1;
uint32_t nof_symbols = 12*harq_process.prb_alloc.slot[0].nof_prb*RE_X_RB;
uint32_t nof_bits_e = nof_symbols * lte_mod_bits_x_symbol(harq_process.mcs.mod);
if (ulsch_encode(&pusch.dl_sch, data, uci_data, pusch.pusch_e, nof_bits_e,
pusch.pusch_q_ack, pusch.pusch_q_ri, &harq_process, rv))
{
fprintf(stderr, "Error encoding TB\n");
exit(-1);
}
vec_fprint_b(stdout, pusch.pusch_e, 288);
/* combine outputs */
for (i=0;i<cell.nof_ports;i++) {
for (j=0;j<nof_re;j++) {
if (i > 0) {
slot_symbols[0][j] += slot_symbols[i][j];
}
ce[i][j] = 1;
}
}
gettimeofday(&t[1], NULL);
//int r = pusch_decode(&pusch, slot_symbols[0], ce, 0, data, subframe, &harq_process, rv);
int r = 0;
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:
pusch_free(&pusch);
harq_free(&harq_process);
for (i=0;i<cell.nof_ports;i++) {
if (ce[i]) {
free(ce[i]);
}
if (slot_symbols[i]) {
free(slot_symbols[i]);
}
}
if (data) {
free(data);
}
if (ret) {
printf("Error\n");
} else {
printf("Ok\n");
}
exit(ret);
}

@ -0,0 +1,190 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2014 The libLTE Developers. See the
* COPYRIGHT file at the top-level directory of this distribution.
*
* \section LICENSE
*
* This file is part of the libLTE library.
*
* libLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* A copy of the GNU Lesser General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <string.h>
#include "liblte/phy/phy.h"
#include "liblte/mex/mexutils.h"
#define UECFG prhs[0]
#define PUSCHCFG prhs[1]
#define TRBLKIN prhs[2]
#define CQI prhs[3]
#define RI prhs[4]
#define ACK prhs[5]
#define NOF_INPUTS 6
void help()
{
mexErrMsgTxt
("[cwout] = liblte_pusch_encode(ue, chs, trblkin, cqi, ri, ack)\n\n");
}
/* the gateway function */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
int i;
sch_t ulsch;
uint8_t *trblkin;
ra_mcs_t mcs;
ra_prb_t prb_alloc;
harq_t harq_process;
uint32_t rv;
uci_data_t uci_data;
bzero(&uci_data, sizeof(uci_data_t));
if (nrhs < NOF_INPUTS) {
help();
return;
}
if (sch_init(&ulsch)) {
mexErrMsgTxt("Error initiating ULSCH\n");
return;
}
lte_cell_t cell;
cell.nof_prb = 100;
cell.id=1;
if (harq_init(&harq_process, cell)) {
mexErrMsgTxt("Error initiating HARQ\n");
return;
}
mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin);
if (mcs.tbs == 0) {
mexErrMsgTxt("Error trblklen is zero\n");
return;
}
uci_data.uci_cqi_len = mexutils_read_uint8(CQI, &uci_data.uci_cqi);
uint8_t *tmp;
uci_data.uci_ri_len = mexutils_read_uint8(RI, &tmp);
if (uci_data.uci_ri_len > 0) {
uci_data.uci_ri = *tmp;
}
free(tmp);
uci_data.uci_ack_len = mexutils_read_uint8(ACK, &tmp);
if (uci_data.uci_ack_len > 0) {
uci_data.uci_ack = *tmp;
}
free(tmp);
mexPrintf("TRBL_len: %d, CQI_len: %d, ACK_len: %d, RI_len: %d\n", mcs.tbs,
uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ri_len);
if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) {
mexErrMsgTxt("Field RV not found in pdsch config\n");
return;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &uci_data.beta_cqi)) {
uci_data.beta_cqi = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &uci_data.beta_ri)) {
uci_data.beta_ri = 2.0;
}
if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &uci_data.beta_ack)) {
uci_data.beta_ack = 2.0;
}
mexPrintf("Beta_CQI: %.1f, Beta_ACK: %.1f, Beta_RI: %.1f\n",
uci_data.beta_cqi, uci_data.beta_ack, uci_data.beta_ri);
char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation");
if (!strcmp(mod_str, "QPSK")) {
mcs.mod = LTE_QPSK;
} else if (!strcmp(mod_str, "16QAM")) {
mcs.mod = LTE_QAM16;
} else if (!strcmp(mod_str, "64QAM")) {
mcs.mod = LTE_QAM64;
} else {
mexErrMsgTxt("Unknown modulation\n");
return;
}
mxFree(mod_str);
float *prbset;
mxArray *p;
p = mxGetField(PUSCHCFG, 0, "PRBSet");
if (!p) {
mexErrMsgTxt("Error field PRBSet not found\n");
return;
}
prb_alloc.slot[0].nof_prb = mexutils_read_f(p, &prbset);
for (i=0;i<MAX_PRB;i++) {
prb_alloc.slot[0].prb_idx[i] = false;
for (int j=0;j<prb_alloc.slot[0].nof_prb && !prb_alloc.slot[0].prb_idx[i];j++) {
if ((int) prbset[j] == i) {
prb_alloc.slot[0].prb_idx[i] = true;
}
}
}
memcpy(&prb_alloc.slot[1], &prb_alloc.slot[0], sizeof(ra_prb_slot_t));
free(prbset);
if (harq_setup(&harq_process, mcs, &prb_alloc)) {
mexErrMsgTxt("Error configuring HARQ process\n");
return;
}
uint32_t nof_symbols = 12*harq_process.prb_alloc.slot[0].nof_prb*RE_X_RB;
uint32_t nof_q_bits = nof_symbols * lte_mod_bits_x_symbol(harq_process.mcs.mod);
uint8_t *q_bits = vec_malloc(nof_q_bits * sizeof(uint8_t));
if (!q_bits) {
return;
}
uint8_t *q_bits_ack = vec_malloc(nof_q_bits * sizeof(uint8_t));
if (!q_bits_ack) {
return;
}
uint8_t *q_bits_ri = vec_malloc(nof_q_bits * sizeof(uint8_t));
if (!q_bits_ri) {
return;
}
if (ulsch_encode(&ulsch, trblkin, uci_data, q_bits, nof_q_bits,
q_bits_ack, q_bits_ri, &harq_process, rv))
{
fprintf(stderr, "Error encoding TB\n");
return;
}
if (nlhs >= 1) {
mexutils_write_uint8(q_bits, &plhs[0], nof_q_bits, 1);
}
sch_free(&ulsch);
free(trblkin);
free(q_bits);
free(q_bits_ack);
free(q_bits_ri);
return;
}

@ -73,6 +73,23 @@ void scrambling_b_offset(sequence_t *s, uint8_t *data, int offset, int len) {
} }
} }
/* As defined in 36.211 5.3.1 */
void scrambling_b_offset_pusch(sequence_t *s, uint8_t *data, int offset, int len) {
int i;
assert (len + offset <= s->len);
for (i = 0; i < len; i++) {
if (data[i] == 'x') {
data[i] = 1;
} else if (data[i] == 'y') {
if (i > 1) {
data[i] = data[i-1];
}
} else {
data[i] = (data[i] + s->c[i + offset]) % 2;
}
}
}
/** High-level API */ /** High-level API */
int compute_sequences(scrambling_hl* h) { int compute_sequences(scrambling_hl* h) {

@ -87,7 +87,7 @@ int ue_dl_init(ue_dl_t *q,
goto clean_exit; goto clean_exit;
} }
for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) { for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) {
if (pdsch_harq_init(&q->harq_process[i], &q->pdsch)) { if (harq_init(&q->harq_process[i], q->cell)) {
fprintf(stderr, "Error initiating HARQ process\n"); fprintf(stderr, "Error initiating HARQ process\n");
goto clean_exit; goto clean_exit;
} }
@ -128,7 +128,7 @@ void ue_dl_free(ue_dl_t *q) {
pdcch_free(&q->pdcch); pdcch_free(&q->pdcch);
pdsch_free(&q->pdsch); pdsch_free(&q->pdsch);
for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) { for (uint32_t i=0;i<NOF_HARQ_PROCESSES; i++) {
pdsch_harq_free(&q->harq_process[i]); harq_free(&q->harq_process[i]);
} }
if (q->sf_symbols) { if (q->sf_symbols) {
free(q->sf_symbols); free(q->sf_symbols);
@ -150,7 +150,7 @@ void ue_dl_set_rnti(ue_dl_t *q, uint16_t rnti) {
} }
void ue_dl_reset(ue_dl_t *q) { void ue_dl_reset(ue_dl_t *q) {
pdsch_harq_reset(&q->harq_process[0]); harq_reset(&q->harq_process[0]);
} }
LIBLTE_API float mean_exec_time=0; LIBLTE_API float mean_exec_time=0;
@ -251,7 +251,7 @@ int ue_dl_decode_sib(ue_dl_t *q, cf_t *input, uint8_t *data, uint32_t sf_idx, ui
rvidx = ra_dl.rv_idx; rvidx = ra_dl.rv_idx;
} }
if (rvidx == 0) { if (rvidx == 0) {
if (pdsch_harq_setup(&q->harq_process[0], ra_dl.mcs, &ra_dl.prb_alloc)) { if (harq_setup(&q->harq_process[0], ra_dl.mcs, &ra_dl.prb_alloc)) {
fprintf(stderr, "Error configuring HARQ process\n"); fprintf(stderr, "Error configuring HARQ process\n");
return LIBLTE_ERROR; return LIBLTE_ERROR;
} }

@ -0,0 +1,27 @@
enbConfig=struct('NCellID',1,'CyclicPrefix','Normal','CellRefP',1);
pdschConfig=struct('Modulation','QPSK','RV',0,'TxScheme','Port0');
addpath('../../debug/lte/phy/lib/phch/test')
TBs=1:111:15000;
e_bits=10000;
error=zeros(size(TBs));
for i=1:length(TBs)
trblkin=randi(2,TBs(i),1)-1;
%trblkin=ones(104,1);
%trblkin=[1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, ];
%trblkin=[1, 0];
[mat, info]=lteDLSCH(enbConfig,pdschConfig,e_bits,trblkin);
lib=liblte_dlsch_encode(enbConfig,pdschConfig,e_bits,trblkin);
error(i)=mean(abs(double(mat)-double(lib)));
end
if (length(TBs) == 1)
%disp(info)
disp(error)
n=1:length(mat);
plot(abs(double(mat)-double(lib)))
else
plot(error)
end

@ -6,7 +6,7 @@ addpath('../../debug/lte/phy/lib/ch_estimation/test')
Hopping={'Off','Sequence','Group'}; Hopping={'Off','Sequence','Group'};
k=1; k=1;
for prb=3:6 for prb=6:6
for ncell=0:2 for ncell=0:2
for ns=0:9 for ns=0:9
for h=1:3 for h=1:3
@ -28,7 +28,7 @@ for prb=3:6
error(k)=mean(abs(mat-lib)); error(k)=mean(abs(mat-lib));
disp(error(k)) disp(error(k))
if (error(k) > 10^-4) if (error(k) > 10^-3)
k=1; k=1;
end end
k=k+1; k=k+1;

Binary file not shown.

File diff suppressed because one or more lines are too long

@ -0,0 +1,33 @@
ueConfig=struct('NCellID',1,'CyclicPrefixUL','Normal','NTxAnts',1);
puschConfig=struct('NLayers',1,'OrthCover','Off','PRBSet',0,'Modulation','QPSK','RV',0,'Shortened',0);
addpath('../../debug/lte/phy/lib/phch/test')
TBs=104;
error=zeros(size(TBs));
for i=1:length(error)
trblkin=randi(2,TBs(i),1)-1;
%trblkin=ones(104,1);
%trblkin=[1, 0];
puschConfig.BetaCQI = 2.0;
puschConfig.BetaRI = 2.0;
puschConfig.BetaACK = 2.0;
[mat, info]=lteULSCH(ueConfig,puschConfig,trblkin,[],[],[1],[]);
mat(mat==-2)=2;
[lib]=liblte_ulsch_encode(ueConfig,puschConfig,trblkin,[],[],[1]);
error(i)=sum(abs(double(mat)-double(lib)));
if (length(TBs) == 1)
disp(error(i))
end
end
if (length(TBs) == 1)
%disp(info)
n=1:length(mat);
plot(abs(double(mat)-double(lib)))
else
plot(error)
disp(sum(error))
end

@ -54,6 +54,10 @@ LIBLTE_API int mexutils_read_uint32_struct(const mxArray *ptr,
const char *field_name, const char *field_name,
uint32_t *value); uint32_t *value);
LIBLTE_API int mexutils_read_float_struct(const mxArray *ptr,
const char *field_name,
float *value);
LIBLTE_API int mexutils_write_f(float *buffer, LIBLTE_API int mexutils_write_f(float *buffer,
mxArray **ptr, mxArray **ptr,
uint32_t nr, uint32_t nr,
@ -74,6 +78,9 @@ LIBLTE_API int mexutils_write_int(int *buffer,
uint32_t nr, uint32_t nr,
uint32_t nc); uint32_t nc);
LIBLTE_API int mexutils_read_uint8(const mxArray *ptr,
uint8_t **buffer);
LIBLTE_API int mexutils_read_f(const mxArray *ptr, LIBLTE_API int mexutils_read_f(const mxArray *ptr,
float **buffer); float **buffer);

@ -61,6 +61,17 @@ int mexutils_read_uint32_struct(const mxArray *ptr, const char *field_name, uint
return 0; return 0;
} }
int mexutils_read_float_struct(const mxArray *ptr, const char *field_name, float *value)
{
mxArray *p;
p = mxGetField(ptr, 0, field_name);
if (!p) {
return -1;
}
*value = (float) mxGetScalar(p);
return 0;
}
int mexutils_read_cell(const mxArray *ptr, lte_cell_t *cell) { int mexutils_read_cell(const mxArray *ptr, lte_cell_t *cell) {
if (mexutils_read_uint32_struct(ptr, "NCellID", &cell->id)) { if (mexutils_read_uint32_struct(ptr, "NCellID", &cell->id)) {
return -1; return -1;
@ -112,6 +123,21 @@ int mexutils_read_f(const mxArray *ptr, float **buffer) {
} }
} }
int mexutils_read_uint8(const mxArray *ptr, uint8_t **buffer) {
int numelems = mxGetNumberOfElements(ptr);
uint8_t *tmp = vec_malloc(numelems * sizeof(uint8_t));
if (tmp) {
double *inr=mxGetPr(ptr);
for (int i=0;i<numelems;i++) {
tmp[i] = (uint8_t) inr[i];
}
*buffer = tmp;
return numelems;
} else {
return -1;
}
}
int mexutils_write_cf(cf_t *buffer, mxArray **ptr, uint32_t nr, uint32_t nc) { int mexutils_write_cf(cf_t *buffer, mxArray **ptr, uint32_t nr, uint32_t nc) {
*ptr = mxCreateDoubleMatrix(nr, nc, mxCOMPLEX); *ptr = mxCreateDoubleMatrix(nr, nc, mxCOMPLEX);
if (*ptr) { if (*ptr) {

Loading…
Cancel
Save