mirror of https://github.com/pvnis/srsRAN_4G.git
Added UL SCH interleaver. Fixed issue with rate matching and filler bits
parent
9a614ec9d1
commit
a47461a6f5
@ -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
|
@ -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
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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
|
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
|
Loading…
Reference in New Issue