2019-04-26 12:27:38 -07:00
|
|
|
/*
|
|
|
|
* Copyright 2013-2019 Software Radio Systems Limited
|
2014-06-17 03:44:42 -07:00
|
|
|
*
|
2019-04-26 12:27:38 -07:00
|
|
|
* This file is part of srsLTE.
|
2014-06-17 03:44:42 -07:00
|
|
|
*
|
2015-03-20 05:01:08 -07:00
|
|
|
* srsLTE is free software: you can redistribute it and/or modify
|
2015-05-08 08:05:40 -07:00
|
|
|
* it under the terms of the GNU Affero General Public License as
|
2014-06-17 03:44:42 -07:00
|
|
|
* published by the Free Software Foundation, either version 3 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
2015-03-20 05:01:08 -07:00
|
|
|
* srsLTE is distributed in the hope that it will be useful,
|
2014-06-17 03:44:42 -07:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2015-05-08 08:05:40 -07:00
|
|
|
* GNU Affero General Public License for more details.
|
2014-06-17 03:44:42 -07:00
|
|
|
*
|
2015-05-08 08:05:40 -07:00
|
|
|
* A copy of the GNU Affero General Public License can be found in
|
2014-06-17 03:44:42 -07:00
|
|
|
* the LICENSE file in the top-level directory of this distribution
|
|
|
|
* and at http://www.gnu.org/licenses/.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
#include "srslte/srslte.h"
|
|
|
|
#include <stdbool.h>
|
2014-06-17 03:44:42 -07:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2019-04-23 01:53:11 -07:00
|
|
|
#include <string.h>
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2018-06-06 07:59:00 -07:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <semaphore.h>
|
|
|
|
|
2017-09-01 10:32:57 -07:00
|
|
|
#include "prb_dl.h"
|
2017-09-05 06:26:36 -07:00
|
|
|
#include "srslte/phy/phch/pdsch.h"
|
2017-05-18 00:48:24 -07:00
|
|
|
#include "srslte/phy/utils/debug.h"
|
|
|
|
#include "srslte/phy/utils/vector.h"
|
2014-06-17 03:44:42 -07:00
|
|
|
|
|
|
|
|
2018-09-17 03:32:37 -07:00
|
|
|
#ifdef LV_HAVE_SSE
|
|
|
|
#include <immintrin.h>
|
|
|
|
#endif /* LV_HAVE_SSE */
|
|
|
|
|
2015-03-18 05:59:29 -07:00
|
|
|
#define MAX_PDSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12)
|
2014-06-17 03:44:42 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
/* 3GPP 36.213 Table 5.2-1: The cell-specific ratio rho_B / rho_A for 1, 2, or 4 cell specific antenna ports */
|
|
|
|
const static float pdsch_cfg_cell_specific_ratio_table[2][4] = {
|
|
|
|
/* One antenna port */ {1.0f / 1.0f, 4.0f / 5.0f, 3.0f / 5.0f, 2.0f / 5.0f},
|
|
|
|
/* Two or more antenna port */ {5.0f / 4.0f, 1.0f / 1.0f, 3.0f / 4.0f, 1.0f / 2.0f}};
|
2014-06-29 10:51:25 -07:00
|
|
|
|
2019-06-19 08:58:01 -07:00
|
|
|
const static srslte_mod_t modulations[5] = {
|
|
|
|
SRSLTE_MOD_BPSK, SRSLTE_MOD_QPSK, SRSLTE_MOD_16QAM, SRSLTE_MOD_64QAM, SRSLTE_MOD_256QAM};
|
2018-06-06 07:59:00 -07:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
/* Thread identifier: they must set before thread creation */
|
|
|
|
pthread_t pthread;
|
|
|
|
uint32_t tb_idx;
|
|
|
|
void *pdsch_ptr;
|
|
|
|
bool *ack;
|
|
|
|
|
|
|
|
/* Configuration Encoder/Decoder: they must be set before posting start semaphore */
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_dl_sf_cfg_t* sf;
|
|
|
|
srslte_pdsch_cfg_t* cfg;
|
|
|
|
srslte_sch_t dl_sch;
|
2018-06-06 07:59:00 -07:00
|
|
|
|
|
|
|
/* Encoder/Decoder data pointers: they must be set before posting start semaphore */
|
|
|
|
uint8_t *data;
|
|
|
|
|
|
|
|
/* Execution status */
|
|
|
|
int ret_status;
|
|
|
|
|
|
|
|
/* Semaphores */
|
|
|
|
sem_t start;
|
|
|
|
sem_t finish;
|
|
|
|
|
2018-06-11 04:12:46 -07:00
|
|
|
/* Thread flags */
|
|
|
|
bool started;
|
2018-06-06 07:59:00 -07:00
|
|
|
bool quit;
|
|
|
|
} srslte_pdsch_coworker_t;
|
|
|
|
|
|
|
|
static void *srslte_pdsch_decode_thread (void *arg);
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_cp(srslte_pdsch_t* q,
|
|
|
|
cf_t* input,
|
|
|
|
cf_t* output,
|
|
|
|
srslte_pdsch_grant_t* grant,
|
|
|
|
uint32_t lstart_grant,
|
|
|
|
uint32_t sf_idx,
|
|
|
|
bool put)
|
2015-06-09 10:21:52 -07:00
|
|
|
{
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t s, n, l, lp, lstart, nof_refs;
|
|
|
|
bool skip_symbol;
|
2014-06-17 07:32:19 -07:00
|
|
|
cf_t *in_ptr = input, *out_ptr = output;
|
2014-07-05 11:26:09 -07:00
|
|
|
uint32_t offset = 0;
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
#ifdef DEBUG_IDX
|
|
|
|
indices_ptr = 0;
|
2014-12-27 14:09:22 -08:00
|
|
|
if (put) {
|
|
|
|
offset_original = output;
|
|
|
|
} else {
|
|
|
|
offset_original = input;
|
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
#endif
|
|
|
|
|
2014-06-29 10:51:25 -07:00
|
|
|
if (q->cell.nof_ports == 1) {
|
2014-06-17 07:32:19 -07:00
|
|
|
nof_refs = 2;
|
|
|
|
} else {
|
|
|
|
nof_refs = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (s = 0; s < 2; s++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
if (s == 0) {
|
|
|
|
lstart = lstart_grant;
|
|
|
|
} else {
|
|
|
|
lstart = 0;
|
|
|
|
}
|
|
|
|
for (l = lstart; l < grant->nof_symb_slot[s]; l++) {
|
2014-11-16 11:40:41 -08:00
|
|
|
for (n = 0; n < q->cell.nof_prb; n++) {
|
|
|
|
|
|
|
|
// If this PRB is assigned
|
2015-04-27 09:14:28 -07:00
|
|
|
if (grant->prb_idx[s][n]) {
|
2019-04-23 01:53:11 -07:00
|
|
|
|
|
|
|
skip_symbol = false;
|
|
|
|
|
|
|
|
// Skip center block signals
|
|
|
|
if ((n >= q->cell.nof_prb / 2 - 3 && n < q->cell.nof_prb / 2 + 3 + (q->cell.nof_prb % 2))) {
|
|
|
|
if (q->cell.frame_type == SRSLTE_FDD) {
|
|
|
|
// FDD PSS/SSS
|
|
|
|
if (s == 0 && (sf_idx == 0 || sf_idx == 5) && (l >= grant->nof_symb_slot[s] - 2)) {
|
|
|
|
skip_symbol = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TDD SSS
|
|
|
|
if (s == 1 && (sf_idx == 0 || sf_idx == 5) && (l >= grant->nof_symb_slot[s] - 1)) {
|
|
|
|
skip_symbol = true;
|
|
|
|
}
|
|
|
|
// TDD PSS
|
|
|
|
if (s == 0 && (sf_idx == 1 || sf_idx == 6) && (l == 2)) {
|
|
|
|
skip_symbol = true;
|
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
// PBCH same in FDD and TDD
|
|
|
|
if (s == 1 && sf_idx == 0 && l < 4) {
|
|
|
|
skip_symbol = true;
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
lp = l + s * grant->nof_symb_slot[0];
|
2014-11-16 11:40:41 -08:00
|
|
|
if (put) {
|
|
|
|
out_ptr = &output[(lp * q->cell.nof_prb + n)
|
2015-03-18 05:59:29 -07:00
|
|
|
* SRSLTE_NRE];
|
2014-06-17 07:32:19 -07:00
|
|
|
} else {
|
2014-11-16 11:40:41 -08:00
|
|
|
in_ptr = &input[(lp * q->cell.nof_prb + n)
|
2015-03-18 05:59:29 -07:00
|
|
|
* SRSLTE_NRE];
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
// This is a symbol in a normal PRB with or without references
|
2019-04-23 01:53:11 -07:00
|
|
|
if (!skip_symbol) {
|
2015-03-18 05:59:29 -07:00
|
|
|
if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) {
|
2015-12-02 12:57:57 -08:00
|
|
|
if (nof_refs == 2) {
|
|
|
|
if (l == 0) {
|
|
|
|
offset = q->cell.id % 6;
|
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
offset = (q->cell.id + 3) % 6;
|
2015-12-02 12:57:57 -08:00
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
} else {
|
|
|
|
offset = q->cell.id % 3;
|
|
|
|
}
|
|
|
|
prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs, put);
|
2014-06-27 09:36:25 -07:00
|
|
|
} else {
|
2014-11-16 11:40:41 -08:00
|
|
|
prb_cp(&in_ptr, &out_ptr, 1);
|
2014-06-27 09:36:25 -07:00
|
|
|
}
|
2014-11-16 11:40:41 -08:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
// This is a symbol in a PRB with PBCH or Synch signals (SS).
|
2014-11-16 11:40:41 -08:00
|
|
|
// If the number or total PRB is odd, half of the the PBCH or SS will fall into the symbol
|
2019-04-23 01:53:11 -07:00
|
|
|
if ((q->cell.nof_prb % 2) && skip_symbol) {
|
2014-11-16 11:40:41 -08:00
|
|
|
if (n == q->cell.nof_prb / 2 - 3) {
|
2015-03-18 05:59:29 -07:00
|
|
|
if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) {
|
2014-11-16 11:40:41 -08:00
|
|
|
prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put);
|
|
|
|
} else {
|
|
|
|
prb_cp_half(&in_ptr, &out_ptr, 1);
|
|
|
|
}
|
|
|
|
} else if (n == q->cell.nof_prb / 2 + 3) {
|
|
|
|
if (put) {
|
|
|
|
out_ptr += 6;
|
|
|
|
} else {
|
|
|
|
in_ptr += 6;
|
|
|
|
}
|
2015-03-18 05:59:29 -07:00
|
|
|
if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) {
|
2014-11-16 11:40:41 -08:00
|
|
|
prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put);
|
|
|
|
} else {
|
|
|
|
prb_cp_half(&in_ptr, &out_ptr, 1);
|
|
|
|
}
|
2014-06-27 09:36:25 -07:00
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
|
|
|
|
int r;
|
2014-06-17 07:32:19 -07:00
|
|
|
if (put) {
|
2014-12-27 14:09:22 -08:00
|
|
|
r = abs((int) (input - in_ptr));
|
2014-06-17 07:32:19 -07:00
|
|
|
} else {
|
2014-12-27 14:09:22 -08:00
|
|
|
r = abs((int) (output - out_ptr));
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2014-12-27 14:09:22 -08:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
return r;
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Puts PDSCH in slot number 1
|
|
|
|
*
|
|
|
|
* Returns the number of symbols written to sf_symbols
|
|
|
|
*
|
|
|
|
* 36.211 10.3 section 6.3.5
|
|
|
|
*/
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_put(
|
|
|
|
srslte_pdsch_t* q, cf_t* symbols, cf_t* sf_symbols, srslte_pdsch_grant_t* grant, uint32_t lstart, uint32_t subframe)
|
2015-06-09 10:21:52 -07:00
|
|
|
{
|
|
|
|
return srslte_pdsch_cp(q, symbols, sf_symbols, grant, lstart, subframe, true);
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extracts PDSCH from slot number 1
|
|
|
|
*
|
|
|
|
* Returns the number of symbols written to PDSCH
|
|
|
|
*
|
|
|
|
* 36.211 10.3 section 6.3.5
|
|
|
|
*/
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_get(
|
|
|
|
srslte_pdsch_t* q, cf_t* sf_symbols, cf_t* symbols, srslte_pdsch_grant_t* grant, uint32_t lstart, uint32_t subframe)
|
2015-06-09 10:21:52 -07:00
|
|
|
{
|
|
|
|
return srslte_pdsch_cp(q, sf_symbols, symbols, grant, lstart, subframe, false);
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
/** Initializes the PDSCH transmitter and receiver */
|
|
|
|
static int pdsch_init(srslte_pdsch_t *q, uint32_t max_prb, bool is_ue, uint32_t nof_antennas)
|
2017-02-07 11:04:15 -08:00
|
|
|
{
|
2015-03-18 05:31:13 -07:00
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
if (q != NULL)
|
|
|
|
{
|
2014-06-29 10:51:25 -07:00
|
|
|
|
2015-03-18 11:14:24 -07:00
|
|
|
bzero(q, sizeof(srslte_pdsch_t));
|
2015-03-18 05:31:13 -07:00
|
|
|
ret = SRSLTE_ERROR;
|
2014-06-29 10:51:25 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
q->max_re = max_prb * MAX_PDSCH_RE(q->cell.cp);
|
|
|
|
q->is_ue = is_ue;
|
|
|
|
q->nof_rx_antennas = nof_antennas;
|
|
|
|
|
2017-09-01 04:29:11 -07:00
|
|
|
INFO("Init PDSCH: %d PRBs, max_symbols: %d\n", max_prb, q->max_re);
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2019-06-19 08:58:01 -07:00
|
|
|
for (int i = 0; i < 5; i++) {
|
2015-10-05 00:58:44 -07:00
|
|
|
if (srslte_modem_table_lte(&q->mod[i], modulations[i])) {
|
2014-06-29 10:51:25 -07:00
|
|
|
goto clean;
|
|
|
|
}
|
2015-09-22 13:20:36 -07:00
|
|
|
srslte_modem_table_bytes(&q->mod[i]);
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
if (srslte_sch_init(&q->dl_sch)) {
|
|
|
|
ERROR("Initiating DL SCH");
|
2014-06-17 07:32:19 -07:00
|
|
|
goto clean;
|
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
|
|
|
// Allocate int16_t for reception (LLRs)
|
2019-06-19 08:58:01 -07:00
|
|
|
q->e[i] = srslte_vec_malloc(sizeof(int16_t) * q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_256QAM));
|
2017-09-05 06:26:36 -07:00
|
|
|
if (!q->e[i]) {
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->d[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
|
|
|
|
if (!q->d[i]) {
|
|
|
|
goto clean;
|
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
|
2015-03-18 11:14:24 -07:00
|
|
|
q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
|
|
|
|
if (!q->x[i]) {
|
2014-06-29 10:51:25 -07:00
|
|
|
goto clean;
|
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
|
|
|
|
if (!q->symbols[i]) {
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
if (q->is_ue) {
|
2017-09-19 01:32:29 -07:00
|
|
|
for (int j = 0; j < SRSLTE_MAX_PORTS; j++) {
|
2017-09-01 04:29:11 -07:00
|
|
|
q->ce[i][j] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
|
|
|
|
if (!q->ce[i][j]) {
|
|
|
|
goto clean;
|
|
|
|
}
|
2017-02-07 11:04:15 -08:00
|
|
|
}
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
q->users = calloc(sizeof(srslte_pdsch_user_t*), q->is_ue?1:(1+SRSLTE_SIRNTI));
|
2016-11-20 08:36:21 -08:00
|
|
|
if (!q->users) {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("malloc");
|
2016-11-20 08:36:21 -08:00
|
|
|
goto clean;
|
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2019-06-19 08:58:01 -07:00
|
|
|
if (srslte_sequence_init(&q->tmp_seq, q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_256QAM))) {
|
2017-09-01 04:29:11 -07:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
|
|
|
if (!q->csi[i]) {
|
|
|
|
q->csi[i] = srslte_vec_malloc(sizeof(float) * q->max_re * 2);
|
|
|
|
if (!q->csi[i]) {
|
|
|
|
return SRSLTE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 05:31:13 -07:00
|
|
|
ret = SRSLTE_SUCCESS;
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
clean:
|
2015-03-18 05:31:13 -07:00
|
|
|
if (ret == SRSLTE_ERROR) {
|
2015-03-18 11:14:24 -07:00
|
|
|
srslte_pdsch_free(q);
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
|
|
|
return ret;
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
int srslte_pdsch_init_ue(srslte_pdsch_t *q, uint32_t max_prb, uint32_t nof_antennas)
|
2017-09-01 04:29:11 -07:00
|
|
|
{
|
2017-09-05 06:26:36 -07:00
|
|
|
return pdsch_init(q, max_prb, true, nof_antennas);
|
2017-09-01 04:29:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int srslte_pdsch_init_enb(srslte_pdsch_t *q, uint32_t max_prb)
|
|
|
|
{
|
2017-09-05 06:26:36 -07:00
|
|
|
return pdsch_init(q, max_prb, false, 0);
|
2017-09-01 04:29:11 -07:00
|
|
|
}
|
|
|
|
|
2018-06-06 07:59:00 -07:00
|
|
|
static void srslte_pdsch_disable_coworker(srslte_pdsch_t *q) {
|
|
|
|
srslte_pdsch_coworker_t *h = (srslte_pdsch_coworker_t *) q->coworker_ptr;
|
|
|
|
if (h) {
|
|
|
|
/* Stop threads */
|
|
|
|
h->quit = true;
|
|
|
|
sem_post(&h->start);
|
|
|
|
|
|
|
|
pthread_join(h->pthread, NULL);
|
|
|
|
pthread_detach(h->pthread);
|
|
|
|
|
|
|
|
srslte_sch_free(&h->dl_sch);
|
|
|
|
|
|
|
|
free(h);
|
|
|
|
|
|
|
|
q->coworker_ptr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_enable_coworker(srslte_pdsch_t* q)
|
|
|
|
{
|
|
|
|
int ret = SRSLTE_SUCCESS;
|
|
|
|
|
|
|
|
if (!q->coworker_ptr) {
|
|
|
|
srslte_pdsch_coworker_t* h = calloc(sizeof(srslte_pdsch_coworker_t), 1);
|
|
|
|
|
|
|
|
if (!h) {
|
|
|
|
ERROR("Allocating coworker");
|
|
|
|
ret = SRSLTE_ERROR;
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
q->coworker_ptr = h;
|
|
|
|
|
|
|
|
if (srslte_sch_init(&h->dl_sch)) {
|
|
|
|
ERROR("Initiating DL SCH");
|
|
|
|
ret = SRSLTE_ERROR;
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sem_init(&h->start, 0, 0)) {
|
|
|
|
ERROR("Creating semaphore");
|
|
|
|
ret = SRSLTE_ERROR;
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
if (sem_init(&h->finish, 0, 0)) {
|
|
|
|
ERROR("Creating semaphore");
|
|
|
|
ret = SRSLTE_ERROR;
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
pthread_create(&h->pthread, NULL, srslte_pdsch_decode_thread, (void*)h);
|
|
|
|
}
|
|
|
|
|
|
|
|
clean:
|
|
|
|
if (ret) {
|
|
|
|
srslte_pdsch_disable_coworker(q);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
void srslte_pdsch_free(srslte_pdsch_t *q) {
|
2018-06-06 07:59:00 -07:00
|
|
|
srslte_pdsch_disable_coworker(q);
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
if (q->e[i]) {
|
|
|
|
free(q->e[i]);
|
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
if (q->d[i]) {
|
|
|
|
free(q->d[i]);
|
|
|
|
}
|
2018-02-28 03:02:17 -08:00
|
|
|
|
|
|
|
if (q->csi[i]) {
|
|
|
|
free(q->csi[i]);
|
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
/* Free sch objects */
|
|
|
|
srslte_sch_free(&q->dl_sch);
|
|
|
|
|
|
|
|
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
|
2015-03-18 11:14:24 -07:00
|
|
|
if (q->x[i]) {
|
|
|
|
free(q->x[i]);
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
if (q->symbols[i]) {
|
|
|
|
free(q->symbols[i]);
|
|
|
|
}
|
|
|
|
if (q->is_ue) {
|
2017-09-19 01:32:29 -07:00
|
|
|
for (int j = 0; j < SRSLTE_MAX_PORTS; j++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
if (q->ce[i][j]) {
|
|
|
|
free(q->ce[i][j]);
|
|
|
|
}
|
2017-02-07 11:04:15 -08:00
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
|
|
|
}
|
2016-11-20 08:36:21 -08:00
|
|
|
if (q->users) {
|
2017-09-01 04:29:11 -07:00
|
|
|
if (q->is_ue) {
|
|
|
|
srslte_pdsch_free_rnti(q, 0);
|
|
|
|
} else {
|
2017-09-05 07:51:44 -07:00
|
|
|
for (int u=0;u<=SRSLTE_SIRNTI;u++) {
|
2017-09-01 04:29:11 -07:00
|
|
|
if (q->users[u]) {
|
|
|
|
srslte_pdsch_free_rnti(q, u);
|
|
|
|
}
|
2016-11-20 08:36:21 -08:00
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
}
|
2016-11-20 08:36:21 -08:00
|
|
|
free(q->users);
|
2016-05-06 02:03:42 -07:00
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
|
|
|
srslte_sequence_free(&q->tmp_seq);
|
|
|
|
|
2019-06-19 08:58:01 -07:00
|
|
|
for (int i = 0; i < 5; i++) {
|
2015-03-18 11:14:24 -07:00
|
|
|
srslte_modem_table_free(&q->mod[i]);
|
2014-06-17 07:32:19 -07:00
|
|
|
}
|
2016-04-08 08:04:01 -07:00
|
|
|
|
2015-03-18 11:14:24 -07:00
|
|
|
bzero(q, sizeof(srslte_pdsch_t));
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
|
|
|
|
2017-09-01 04:29:11 -07:00
|
|
|
int srslte_pdsch_set_cell(srslte_pdsch_t *q, srslte_cell_t cell)
|
|
|
|
{
|
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (q != NULL && srslte_cell_isvalid(&cell)) {
|
|
|
|
q->cell = cell;
|
2017-09-01 04:29:11 -07:00
|
|
|
q->max_re = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp);
|
|
|
|
|
2018-05-14 07:10:01 -07:00
|
|
|
INFO("PDSCH: Cell config PCI=%d, %d ports, %d PRBs, max_symbols: %d\n",
|
|
|
|
q->cell.id, q->cell.nof_ports, q->cell.nof_prb, q->max_re);
|
2017-09-01 04:29:11 -07:00
|
|
|
|
|
|
|
ret = SRSLTE_SUCCESS;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
/* Precalculate the PDSCH scramble sequences for a given RNTI. This function takes a while
|
2015-03-02 02:11:44 -08:00
|
|
|
* to execute, so shall be called once the final C-RNTI has been allocated for the session.
|
|
|
|
*/
|
2015-03-18 11:14:24 -07:00
|
|
|
int srslte_pdsch_set_rnti(srslte_pdsch_t *q, uint16_t rnti) {
|
2017-09-01 04:29:11 -07:00
|
|
|
uint32_t rnti_idx = q->is_ue?0:rnti;
|
|
|
|
|
2017-09-04 03:26:58 -07:00
|
|
|
if (!q->users[rnti_idx] || q->is_ue) {
|
|
|
|
if (!q->users[rnti_idx]) {
|
|
|
|
q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t));
|
2019-04-23 01:53:11 -07:00
|
|
|
if (!q->users[rnti_idx]) {
|
|
|
|
ERROR("calloc");
|
2017-09-04 03:26:58 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2018-07-12 09:55:19 -07:00
|
|
|
q->users[rnti_idx]->sequence_generated = false;
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
if (srslte_sequence_pdsch(&q->users[rnti_idx]->seq[j][i],
|
|
|
|
rnti,
|
|
|
|
j,
|
|
|
|
2 * i,
|
|
|
|
q->cell.id,
|
2019-06-19 08:58:01 -07:00
|
|
|
q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_256QAM))) {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Error initializing PDSCH scrambling sequence\n");
|
2017-09-05 06:26:36 -07:00
|
|
|
srslte_pdsch_free_rnti(q, rnti);
|
|
|
|
return SRSLTE_ERROR;
|
|
|
|
}
|
2016-05-06 02:03:42 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
q->ue_rnti = rnti;
|
2017-09-04 03:26:58 -07:00
|
|
|
q->users[rnti_idx]->cell_id = q->cell.id;
|
|
|
|
q->users[rnti_idx]->sequence_generated = true;
|
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Error generating PDSCH sequence: rnti=0x%x already generated\n", rnti);
|
2016-05-06 02:03:42 -07:00
|
|
|
}
|
2016-11-20 08:36:21 -08:00
|
|
|
return SRSLTE_SUCCESS;
|
2016-05-06 02:03:42 -07:00
|
|
|
}
|
|
|
|
|
2016-11-20 08:36:21 -08:00
|
|
|
void srslte_pdsch_free_rnti(srslte_pdsch_t* q, uint16_t rnti)
|
2015-04-27 09:14:28 -07:00
|
|
|
{
|
2017-09-01 04:29:11 -07:00
|
|
|
uint32_t rnti_idx = q->is_ue?0:rnti;
|
|
|
|
if (q->users[rnti_idx]) {
|
2019-04-23 01:53:11 -07:00
|
|
|
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) {
|
|
|
|
srslte_sequence_free(&q->users[rnti_idx]->seq[j][i]);
|
|
|
|
}
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
free(q->users[rnti_idx]);
|
|
|
|
q->users[rnti_idx] = NULL;
|
2017-09-07 04:19:53 -07:00
|
|
|
q->ue_rnti = 0;
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
static float apply_power_allocation(srslte_pdsch_t* q, srslte_pdsch_cfg_t* cfg, cf_t* sf_symbols_m[SRSLTE_MAX_PORTS])
|
2014-07-07 11:42:10 -07:00
|
|
|
{
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t nof_symbols_slot = cfg->grant.nof_symb_slot[0];
|
|
|
|
uint32_t nof_re_symbol = SRSLTE_NRE * q->cell.nof_prb;
|
|
|
|
|
|
|
|
/* Set power allocation according to 3GPP 36.213 clause 5.2 Downlink power allocation */
|
2019-11-28 05:27:31 -08:00
|
|
|
float rho_a = srslte_convert_dB_to_amplitude(cfg->p_a) * ((q->cell.nof_ports == 1) ? 1.0f : M_SQRT2);
|
2019-04-23 01:53:11 -07:00
|
|
|
|
|
|
|
uint32_t idx0 = (q->cell.nof_ports == 1) ? 0 : 1;
|
|
|
|
float cell_specific_ratio = pdsch_cfg_cell_specific_ratio_table[idx0][cfg->p_b];
|
|
|
|
float rho_b = sqrtf(cell_specific_ratio);
|
|
|
|
|
|
|
|
/* Apply rho_b if required according to 3GPP 36.213 Table 5.2-2 */
|
|
|
|
if (rho_b != 0.0f && rho_b != 1.0f) {
|
|
|
|
float scaling = 1.0f / rho_b;
|
|
|
|
for (uint32_t i = 0; i < q->nof_rx_antennas; i++) {
|
|
|
|
for (uint32_t j = 0; j < 2; j++) {
|
|
|
|
cf_t* ptr;
|
|
|
|
ptr = sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 0);
|
|
|
|
srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol);
|
|
|
|
if (q->cell.cp == SRSLTE_CP_NORM) {
|
|
|
|
ptr = sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 4);
|
|
|
|
srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol);
|
2017-09-05 04:17:33 -07:00
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
ptr = sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 3);
|
|
|
|
srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
if (q->cell.nof_ports == 4) {
|
|
|
|
ptr = sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 1);
|
|
|
|
srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
return rho_a;
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static srslte_sequence_t *get_user_sequence(srslte_pdsch_t *q, uint16_t rnti,
|
|
|
|
uint32_t codeword_idx, uint32_t sf_idx, uint32_t len)
|
2017-09-01 04:29:11 -07:00
|
|
|
{
|
|
|
|
uint32_t rnti_idx = q->is_ue?0:rnti;
|
2017-09-04 03:26:58 -07:00
|
|
|
|
|
|
|
// The scrambling sequence is pregenerated for all RNTIs in the eNodeB but only for C-RNTI in the UE
|
2018-09-19 07:41:59 -07:00
|
|
|
if (q->users[rnti_idx] &&
|
|
|
|
q->users[rnti_idx]->sequence_generated &&
|
|
|
|
q->users[rnti_idx]->cell_id == q->cell.id &&
|
|
|
|
(!q->is_ue || q->ue_rnti == rnti))
|
2017-09-04 03:26:58 -07:00
|
|
|
{
|
2017-09-05 06:26:36 -07:00
|
|
|
return &q->users[rnti_idx]->seq[codeword_idx][sf_idx];
|
2017-09-01 04:29:11 -07:00
|
|
|
} else {
|
2017-09-05 06:26:36 -07:00
|
|
|
srslte_sequence_pdsch(&q->tmp_seq, rnti, codeword_idx, 2 * sf_idx, q->cell.id, len);
|
2017-09-01 04:29:11 -07:00
|
|
|
return &q->tmp_seq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-04 08:51:35 -07:00
|
|
|
static void csi_correction(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, uint32_t codeword_idx, uint32_t tb_idx, void *e)
|
|
|
|
{
|
|
|
|
|
|
|
|
uint32_t qm = 0;
|
2019-04-23 01:53:11 -07:00
|
|
|
switch (cfg->grant.tb[tb_idx].mod) {
|
2018-09-04 08:51:35 -07:00
|
|
|
|
|
|
|
case SRSLTE_MOD_BPSK:
|
|
|
|
qm = 1;
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_QPSK:
|
|
|
|
qm = 2;
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_16QAM:
|
|
|
|
qm = 4;
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_64QAM:
|
|
|
|
qm = 6;
|
|
|
|
break;
|
2019-06-19 08:58:01 -07:00
|
|
|
case SRSLTE_MOD_256QAM:
|
|
|
|
qm = 8;
|
|
|
|
break;
|
2018-09-04 08:51:35 -07:00
|
|
|
default:
|
|
|
|
ERROR("No modulation");
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
const uint32_t csi_max_idx = srslte_vec_max_fi(q->csi[codeword_idx], cfg->grant.tb[tb_idx].nof_bits / qm);
|
2018-09-04 08:51:35 -07:00
|
|
|
float csi_max = 1.0f;
|
2019-04-23 01:53:11 -07:00
|
|
|
if (csi_max_idx < cfg->grant.tb[tb_idx].nof_bits / qm) {
|
2018-09-04 08:51:35 -07:00
|
|
|
csi_max = q->csi[codeword_idx][csi_max_idx];
|
|
|
|
}
|
|
|
|
int8_t *e_b = e;
|
|
|
|
int16_t *e_s = e;
|
2019-04-23 01:53:11 -07:00
|
|
|
float* csi_v = q->csi[codeword_idx];
|
2018-09-17 03:32:37 -07:00
|
|
|
if (q->llr_is_8bit) {
|
2019-04-23 01:53:11 -07:00
|
|
|
for (int i = 0; i < cfg->grant.tb[tb_idx].nof_bits / qm; i++) {
|
2018-09-17 03:32:37 -07:00
|
|
|
const float csi = *(csi_v++) / csi_max;
|
2018-09-04 08:51:35 -07:00
|
|
|
for (int k = 0; k < qm; k++) {
|
2018-09-17 03:32:37 -07:00
|
|
|
*e_b = (int8_t) ((float) *e_b * csi);
|
|
|
|
e_b++;
|
2018-09-04 08:51:35 -07:00
|
|
|
}
|
2018-09-17 03:32:37 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int i = 0;
|
|
|
|
|
2019-07-12 05:11:26 -07:00
|
|
|
#ifdef LV_HAVE_SSE
|
2018-09-17 03:32:37 -07:00
|
|
|
__m128 _csi_scale = _mm_set1_ps(INT16_MAX / csi_max);
|
2019-04-23 01:53:11 -07:00
|
|
|
__m64* _e = (__m64*)e;
|
2018-09-17 03:32:37 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
switch (cfg->grant.tb[tb_idx].mod) {
|
2018-09-17 03:32:37 -07:00
|
|
|
case SRSLTE_MOD_QPSK:
|
2019-04-23 01:53:11 -07:00
|
|
|
for (; i < cfg->grant.tb[tb_idx].nof_bits - 3; i += 4) {
|
2018-09-17 03:32:37 -07:00
|
|
|
__m128 _csi1 = _mm_set1_ps(*(csi_v++));
|
|
|
|
__m128 _csi2 = _mm_set1_ps(*(csi_v++));
|
|
|
|
_csi1 = _mm_blend_ps(_csi1, _csi2, 3);
|
|
|
|
|
|
|
|
_csi1 = _mm_mul_ps(_csi1, _csi_scale);
|
|
|
|
|
|
|
|
_e[0] = _mm_mulhi_pi16(_e[0], _mm_cvtps_pi16(_csi1));
|
|
|
|
_e += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_16QAM:
|
2019-04-23 01:53:11 -07:00
|
|
|
for (; i < cfg->grant.tb[tb_idx].nof_bits - 3; i += 4) {
|
2018-09-17 03:32:37 -07:00
|
|
|
__m128 _csi = _mm_set1_ps(*(csi_v++));
|
|
|
|
|
|
|
|
_csi = _mm_mul_ps(_csi, _csi_scale);
|
|
|
|
|
|
|
|
_e[0] = _mm_mulhi_pi16(_e[0], _mm_cvtps_pi16(_csi));
|
|
|
|
_e += 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_64QAM:
|
2019-04-23 01:53:11 -07:00
|
|
|
for (; i < cfg->grant.tb[tb_idx].nof_bits - 11; i += 12) {
|
2018-09-17 03:32:37 -07:00
|
|
|
__m128 _csi1 = _mm_set1_ps(*(csi_v++));
|
|
|
|
__m128 _csi3 = _mm_set1_ps(*(csi_v++));
|
|
|
|
|
|
|
|
_csi1 = _mm_mul_ps(_csi1, _csi_scale);
|
|
|
|
_csi3 = _mm_mul_ps(_csi3, _csi_scale);
|
|
|
|
__m128 _csi2 = _mm_blend_ps(_csi1, _csi3, 3);
|
|
|
|
|
|
|
|
_e[0] = _mm_mulhi_pi16(_e[0], _mm_cvtps_pi16(_csi1));
|
|
|
|
_e[1] = _mm_mulhi_pi16(_e[1], _mm_cvtps_pi16(_csi2));
|
|
|
|
_e[2] = _mm_mulhi_pi16(_e[2], _mm_cvtps_pi16(_csi3));
|
|
|
|
_e += 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SRSLTE_MOD_BPSK:
|
|
|
|
break;
|
2019-06-19 08:58:01 -07:00
|
|
|
case SRSLTE_MOD_256QAM:
|
|
|
|
for (; i < cfg->grant.tb[tb_idx].nof_bits - 7; i += 8) {
|
|
|
|
__m128 _csi = _mm_set1_ps(*(csi_v++));
|
|
|
|
|
|
|
|
_csi = _mm_mul_ps(_csi, _csi_scale);
|
|
|
|
|
|
|
|
_e[0] = _mm_mulhi_pi16(_e[0], _mm_cvtps_pi16(_csi));
|
2019-07-12 05:11:26 -07:00
|
|
|
_e[1] = _mm_mulhi_pi16(_e[1], _mm_cvtps_pi16(_csi));
|
|
|
|
_e += 2;
|
2019-06-19 08:58:01 -07:00
|
|
|
}
|
|
|
|
break;
|
2018-09-17 03:32:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
i /= qm;
|
|
|
|
#endif /* LV_HAVE_SSE */
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
for (; i < cfg->grant.tb[tb_idx].nof_bits / qm; i++) {
|
2018-09-17 03:32:37 -07:00
|
|
|
const float csi = q->csi[codeword_idx][i] / csi_max;
|
2018-09-04 08:51:35 -07:00
|
|
|
for (int k = 0; k < qm; k++) {
|
|
|
|
e_s[qm * i + k] = (int16_t) ((float) e_s[qm * i + k] * csi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
static void pdsch_decode_debug(srslte_pdsch_t* q,
|
|
|
|
srslte_pdsch_cfg_t* cfg,
|
|
|
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
|
|
|
|
cf_t* ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS])
|
|
|
|
{
|
|
|
|
if (SRSLTE_VERBOSE_ISDEBUG()) {
|
|
|
|
char filename[FILENAME_MAX];
|
|
|
|
for (int j = 0; j < q->nof_rx_antennas; j++) {
|
|
|
|
if (snprintf(filename, FILENAME_MAX, "subframe_p%d.dat", j) < 0) {
|
|
|
|
ERROR("Generating file name");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DEBUG("SAVED FILE %s: received subframe symbols\n", filename);
|
|
|
|
srslte_vec_save_file(filename, sf_symbols[j], SRSLTE_NOF_RE(q->cell) * sizeof(cf_t));
|
|
|
|
|
|
|
|
for (int i = 0; i < q->cell.nof_ports; i++) {
|
|
|
|
if (snprintf(filename, FILENAME_MAX, "hest_%d%d.dat", i, j) < 0) {
|
|
|
|
ERROR("Generating file name");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DEBUG("SAVED FILE %s: channel estimates for Tx %d and Rx %d\n", filename, j, i);
|
|
|
|
srslte_vec_save_file(filename, ce[i][j], SRSLTE_NOF_RE(q->cell) * sizeof(cf_t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < cfg->grant.nof_layers; i++) {
|
|
|
|
if (snprintf(filename, FILENAME_MAX, "pdsch_symbols_%d.dat", i) < 0) {
|
|
|
|
ERROR("Generating file name");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DEBUG("SAVED FILE %s: symbols after equalization\n", filename);
|
|
|
|
srslte_vec_save_file(filename, q->d[i], cfg->grant.nof_re * sizeof(cf_t));
|
|
|
|
|
|
|
|
if (snprintf(filename, FILENAME_MAX, "llr_%d.dat", i) < 0) {
|
|
|
|
ERROR("Generating file name");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DEBUG("SAVED FILE %s: LLR estimates after demodulation and descrambling\n", filename);
|
|
|
|
srslte_vec_save_file(filename, q->e[i], cfg->grant.tb[0].nof_bits * sizeof(int16_t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int srslte_pdsch_codeword_decode(srslte_pdsch_t* q,
|
|
|
|
srslte_dl_sf_cfg_t* sf,
|
|
|
|
srslte_pdsch_cfg_t* cfg,
|
|
|
|
srslte_sch_t* dl_sch,
|
|
|
|
uint8_t* data,
|
|
|
|
uint32_t tb_idx,
|
|
|
|
bool* ack)
|
|
|
|
{
|
|
|
|
srslte_ra_tb_t* mcs = &cfg->grant.tb[tb_idx];
|
|
|
|
uint32_t rv = mcs->rv;
|
|
|
|
uint32_t codeword_idx = mcs->cw_idx;
|
|
|
|
uint32_t nof_layers = cfg->grant.nof_layers;
|
|
|
|
srslte_softbuffer_rx_t* softbuffer = cfg->softbuffers.rx[tb_idx];
|
|
|
|
|
2017-09-06 06:25:12 -07:00
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (softbuffer && data && ack && cfg->grant.tb[tb_idx].nof_bits && cfg->grant.nof_re) {
|
2017-11-08 08:10:16 -08:00
|
|
|
INFO("Decoding PDSCH SF: %d (CW%d -> TB%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
|
2019-04-23 01:53:11 -07:00
|
|
|
sf->tti % 10,
|
|
|
|
codeword_idx,
|
|
|
|
tb_idx,
|
|
|
|
srslte_mod_string(mcs->mod),
|
|
|
|
mcs->tbs,
|
|
|
|
cfg->grant.nof_re,
|
|
|
|
cfg->grant.tb[tb_idx].nof_bits,
|
|
|
|
rv);
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
*/
|
2018-09-04 08:51:35 -07:00
|
|
|
if (q->llr_is_8bit) {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_demod_soft_demodulate_b(mcs->mod, q->d[codeword_idx], q->e[codeword_idx], cfg->grant.nof_re);
|
2018-09-04 08:51:35 -07:00
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_demod_soft_demodulate_s(mcs->mod, q->d[codeword_idx], q->e[codeword_idx], cfg->grant.nof_re);
|
2018-09-04 08:51:35 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
/* Select scrambling sequence */
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_sequence_t* seq =
|
|
|
|
get_user_sequence(q, cfg->rnti, codeword_idx, sf->tti % 10, cfg->grant.tb[tb_idx].nof_bits);
|
|
|
|
if (!seq) {
|
|
|
|
ERROR("Error getting user sequence for rnti=0x%x\n", cfg->rnti);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
/* Bit scrambling */
|
2018-09-04 08:51:35 -07:00
|
|
|
if (q->llr_is_8bit) {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_scrambling_sb_offset(seq, q->e[codeword_idx], 0, cfg->grant.tb[tb_idx].nof_bits);
|
2018-09-04 08:51:35 -07:00
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_scrambling_s_offset(seq, q->e[codeword_idx], 0, cfg->grant.tb[tb_idx].nof_bits);
|
2018-02-28 03:02:17 -08:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->csi_enable) {
|
2018-09-04 08:51:35 -07:00
|
|
|
csi_correction(q, cfg, codeword_idx, tb_idx, q->e[codeword_idx]);
|
2018-02-28 03:02:17 -08:00
|
|
|
}
|
|
|
|
|
2017-09-06 06:25:12 -07:00
|
|
|
/* Return */
|
2019-04-23 01:53:11 -07:00
|
|
|
ret = srslte_dlsch_decode2(dl_sch, cfg, q->e[codeword_idx], data, tb_idx, nof_layers);
|
2017-09-26 05:48:59 -07:00
|
|
|
|
2017-09-06 06:25:12 -07:00
|
|
|
if (ret == SRSLTE_SUCCESS) {
|
|
|
|
*ack = true;
|
|
|
|
} else if (ret == SRSLTE_ERROR) {
|
|
|
|
*ack = false;
|
|
|
|
ret = SRSLTE_SUCCESS;
|
2017-11-08 08:10:16 -08:00
|
|
|
} else if (ret == SRSLTE_ERROR_INVALID_INPUTS) {
|
|
|
|
*ack = false;
|
|
|
|
ret = SRSLTE_ERROR;
|
2017-09-06 06:25:12 -07:00
|
|
|
}
|
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Invalid parameters in TB%d &softbuffer=%p &data=%p &ack=%p, nbits=%d, nof_re=%d\n",
|
|
|
|
codeword_idx,
|
|
|
|
softbuffer,
|
|
|
|
(void*)data,
|
|
|
|
ack,
|
|
|
|
cfg->grant.tb[tb_idx].nof_bits,
|
|
|
|
cfg->grant.nof_re);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
|
|
|
|
2017-09-06 06:25:12 -07:00
|
|
|
return ret;
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
|
|
|
|
2018-06-06 07:59:00 -07:00
|
|
|
static void *srslte_pdsch_decode_thread(void *arg) {
|
|
|
|
srslte_pdsch_coworker_t *q = (srslte_pdsch_coworker_t *) arg;
|
|
|
|
|
|
|
|
INFO("[PDSCH Coworker] waiting for data\n");
|
|
|
|
|
|
|
|
sem_wait(&q->start);
|
|
|
|
while (!q->quit) {
|
2019-04-23 01:53:11 -07:00
|
|
|
q->ret_status = srslte_pdsch_codeword_decode(q->pdsch_ptr, q->sf, q->cfg, &q->dl_sch, q->data, q->tb_idx, q->ack);
|
2018-06-06 07:59:00 -07:00
|
|
|
|
|
|
|
/* Post finish semaphore */
|
|
|
|
sem_post(&q->finish);
|
|
|
|
|
|
|
|
/* Wait for next loop */
|
|
|
|
sem_wait(&q->start);
|
|
|
|
}
|
2018-06-11 04:12:46 -07:00
|
|
|
sem_post(&q->finish);
|
2018-06-06 07:59:00 -07:00
|
|
|
|
|
|
|
pthread_exit(NULL);
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
2017-02-07 11:04:15 -08:00
|
|
|
/** Decodes the PDSCH from the received symbols
|
|
|
|
*/
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_decode(srslte_pdsch_t* q,
|
|
|
|
srslte_dl_sf_cfg_t* sf,
|
|
|
|
srslte_pdsch_cfg_t* cfg,
|
|
|
|
srslte_chest_dl_res_t* channel,
|
|
|
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
|
|
|
|
srslte_pdsch_res_t data[SRSLTE_MAX_CODEWORDS])
|
2017-02-07 11:04:15 -08:00
|
|
|
{
|
2014-06-17 07:32:19 -07:00
|
|
|
|
|
|
|
/* Set pointers for layermapping & precoding */
|
2017-09-05 06:26:36 -07:00
|
|
|
uint32_t i;
|
2019-04-23 01:53:11 -07:00
|
|
|
cf_t* x[SRSLTE_MAX_LAYERS];
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (q != NULL && sf_symbols != NULL && data != NULL && cfg != NULL) {
|
|
|
|
|
|
|
|
struct timeval t[3];
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
gettimeofday(&t[1], NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t nof_tb = cfg->grant.nof_tb;
|
|
|
|
|
|
|
|
float pdsch_scaling = 1.0f;
|
|
|
|
if (cfg->power_scale) {
|
|
|
|
float rho_a = apply_power_allocation(q, cfg, sf_symbols);
|
|
|
|
if (rho_a != 0.0f && isnormal(rho_a)) {
|
|
|
|
pdsch_scaling = rho_a;
|
|
|
|
}
|
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->max_nof_iterations) {
|
|
|
|
srslte_sch_set_max_noi(&q->dl_sch, cfg->max_nof_iterations);
|
|
|
|
}
|
|
|
|
|
|
|
|
float noise_estimate = cfg->decoder_type == SRSLTE_MIMO_DECODER_ZF ? 0 : channel->noise_estimate;
|
|
|
|
|
|
|
|
INFO("Decoding PDSCH SF: %d, RNTI: 0x%x, NofSymbols: %d, C_prb=%d, mod=%s, nof_layers=%d, nof_tb=%d\n",
|
|
|
|
sf->tti % 10,
|
|
|
|
cfg->rnti,
|
|
|
|
cfg->grant.nof_re,
|
|
|
|
cfg->grant.nof_prb,
|
|
|
|
srslte_mod_string(cfg->grant.tb[0].mod),
|
|
|
|
cfg->grant.nof_layers,
|
|
|
|
nof_tb);
|
2015-04-27 09:14:28 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
// Extract Symbols and Channel Estimates
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t lstart = SRSLTE_NOF_CTRL_SYMBOLS(q->cell, sf->cfi);
|
2017-02-07 11:04:15 -08:00
|
|
|
for (int j=0;j<q->nof_rx_antennas;j++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
int n = srslte_pdsch_get(q, sf_symbols[j], q->symbols[j], &cfg->grant, lstart, sf->tti % 10);
|
|
|
|
if (n != cfg->grant.nof_re) {
|
|
|
|
ERROR("Error expecting %d symbols but got %d\n", cfg->grant.nof_re, n);
|
2015-03-18 05:31:13 -07:00
|
|
|
return SRSLTE_ERROR;
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2017-02-07 11:04:15 -08:00
|
|
|
for (i = 0; i < q->cell.nof_ports; i++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
n = srslte_pdsch_get(q, channel->ce[i][j], q->ce[i][j], &cfg->grant, lstart, sf->tti % 10);
|
|
|
|
if (n != cfg->grant.nof_re) {
|
|
|
|
ERROR("Error expecting %d symbols but got %d\n", cfg->grant.nof_re, n);
|
2017-02-07 11:04:15 -08:00
|
|
|
return SRSLTE_ERROR;
|
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
// Prepare layers
|
|
|
|
int nof_symbols [SRSLTE_MAX_CODEWORDS];
|
2019-04-23 01:53:11 -07:00
|
|
|
nof_symbols[0] = cfg->grant.nof_re * nof_tb / cfg->grant.nof_layers;
|
|
|
|
nof_symbols[1] = cfg->grant.nof_re * nof_tb / cfg->grant.nof_layers;
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.nof_layers == nof_tb) {
|
2017-09-05 06:26:36 -07:00
|
|
|
/* Skip layer demap */
|
2019-04-23 01:53:11 -07:00
|
|
|
for (i = 0; i < cfg->grant.nof_layers; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
x[i] = q->d[i];
|
|
|
|
}
|
2014-06-29 10:51:25 -07:00
|
|
|
} else {
|
2017-09-05 06:26:36 -07:00
|
|
|
/* number of layers equals number of ports */
|
2019-04-23 01:53:11 -07:00
|
|
|
for (i = 0; i < cfg->grant.nof_layers; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
x[i] = q->x[i];
|
2016-06-09 02:58:06 -07:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
memset(&x[cfg->grant.nof_layers], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - cfg->grant.nof_layers));
|
2017-11-21 01:14:09 -08:00
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
// Pre-decoder
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t codebook_idx = nof_tb == 1 ? cfg->grant.pmi : (cfg->grant.pmi + 1);
|
|
|
|
if (srslte_predecoding_type(q->symbols,
|
|
|
|
q->ce,
|
|
|
|
x,
|
|
|
|
q->csi,
|
|
|
|
q->nof_rx_antennas,
|
|
|
|
q->cell.nof_ports,
|
|
|
|
cfg->grant.nof_layers,
|
|
|
|
codebook_idx,
|
|
|
|
cfg->grant.nof_re,
|
|
|
|
cfg->grant.tx_scheme,
|
|
|
|
pdsch_scaling,
|
|
|
|
noise_estimate) < 0) {
|
|
|
|
ERROR("Error predecoding\n");
|
2018-01-22 02:59:10 -08:00
|
|
|
return SRSLTE_ERROR;
|
2017-11-08 08:10:16 -08:00
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
// Layer demapping only if necessary
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.nof_layers != nof_tb) {
|
|
|
|
srslte_layerdemap_type(x, q->d, cfg->grant.nof_layers, nof_tb, nof_symbols[0], nof_symbols, cfg->grant.tx_scheme);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2017-11-08 08:10:16 -08:00
|
|
|
|
|
|
|
/* Codeword decoding: Implementation of 3GPP 36.212 Table 5.3.3.1.5-1 and Table 5.3.3.1.5-2 */
|
|
|
|
for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
|
2017-09-15 06:59:40 -07:00
|
|
|
/* Decode only if transport block is enabled and the default ACK is not true */
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.tb[tb_idx].enabled) {
|
|
|
|
if (!data[tb_idx].crc) {
|
2018-06-06 07:59:00 -07:00
|
|
|
int ret = SRSLTE_SUCCESS;
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.nof_tb > 1 && tb_idx == 0 && q->coworker_ptr) {
|
2018-06-06 07:59:00 -07:00
|
|
|
srslte_pdsch_coworker_t *h = (srslte_pdsch_coworker_t *) q->coworker_ptr;
|
|
|
|
|
|
|
|
h->pdsch_ptr = q;
|
|
|
|
h->cfg = cfg;
|
2019-04-23 01:53:11 -07:00
|
|
|
h->sf = sf;
|
|
|
|
h->data = data[tb_idx].payload;
|
2018-06-06 07:59:00 -07:00
|
|
|
h->tb_idx = tb_idx;
|
2019-04-23 01:53:11 -07:00
|
|
|
h->ack = &data[tb_idx].crc;
|
2018-06-06 07:59:00 -07:00
|
|
|
h->dl_sch.max_iterations = q->dl_sch.max_iterations;
|
2018-06-11 04:12:46 -07:00
|
|
|
h->started = true;
|
2018-06-06 07:59:00 -07:00
|
|
|
sem_post(&h->start);
|
|
|
|
|
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
ret = srslte_pdsch_codeword_decode(q, sf, cfg, &q->dl_sch, data[tb_idx].payload, tb_idx, &data[tb_idx].crc);
|
|
|
|
|
|
|
|
data[tb_idx].avg_iterations_block = srslte_sch_last_noi(&q->dl_sch);
|
2018-06-06 07:59:00 -07:00
|
|
|
}
|
2017-09-06 06:25:12 -07:00
|
|
|
|
2017-11-08 08:10:16 -08:00
|
|
|
/* Check if there has been any execution error */
|
|
|
|
if (ret) {
|
2018-06-11 04:12:46 -07:00
|
|
|
/* Do Nothing */
|
2017-11-08 08:10:16 -08:00
|
|
|
}
|
2017-09-06 06:25:12 -07:00
|
|
|
}
|
2017-09-05 04:17:33 -07:00
|
|
|
}
|
2015-12-03 16:39:35 -08:00
|
|
|
}
|
|
|
|
|
2018-06-11 04:12:46 -07:00
|
|
|
if (q->coworker_ptr) {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_pdsch_coworker_t* h = (srslte_pdsch_coworker_t*)q->coworker_ptr;
|
2018-06-11 04:12:46 -07:00
|
|
|
if (h->started) {
|
|
|
|
int err = sem_wait(&h->finish);
|
|
|
|
if (err) {
|
2019-04-23 01:53:11 -07:00
|
|
|
printf("SCH coworker: %s (nof_tb=%d)\n", strerror(errno), cfg->grant.nof_tb);
|
2018-06-11 04:12:46 -07:00
|
|
|
}
|
|
|
|
if (h->ret_status) {
|
|
|
|
ERROR("PDSCH Coworker Decoder: Error decoding");
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
data[h->tb_idx].avg_iterations_block = srslte_sch_last_noi(&q->dl_sch);
|
|
|
|
h->started = false;
|
2018-06-06 07:59:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
pdsch_decode_debug(q, cfg, sf_symbols, channel->ce);
|
|
|
|
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
gettimeofday(&t[2], NULL);
|
|
|
|
get_time_interval(t);
|
|
|
|
cfg->meas_time_value = t[0].tv_usec;
|
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
return SRSLTE_SUCCESS;
|
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Invalid inputs\n");
|
2017-09-05 06:26:36 -07:00
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
static int srslte_pdsch_codeword_encode(srslte_pdsch_t* q,
|
|
|
|
srslte_dl_sf_cfg_t* sf,
|
|
|
|
srslte_pdsch_cfg_t* cfg,
|
|
|
|
srslte_softbuffer_tx_t* softbuffer,
|
|
|
|
uint8_t* data,
|
|
|
|
uint32_t tb_idx,
|
|
|
|
uint32_t nof_layers)
|
|
|
|
{
|
|
|
|
srslte_ra_tb_t* mcs = &cfg->grant.tb[tb_idx];
|
|
|
|
uint32_t rv = cfg->grant.tb[tb_idx].rv;
|
|
|
|
|
|
|
|
uint32_t codeword_idx = cfg->grant.tb[tb_idx].cw_idx;
|
|
|
|
|
|
|
|
if (!softbuffer) {
|
|
|
|
ERROR("Error encoding (TB%d -> CW%d), softbuffer=NULL", tb_idx, codeword_idx);
|
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->grant.tb[tb_idx].enabled) {
|
|
|
|
if (cfg->rnti != SRSLTE_SIRNTI) {
|
|
|
|
INFO("Encoding PDSCH SF: %d (TB%d -> CW%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n",
|
|
|
|
sf->tti % 10,
|
|
|
|
tb_idx,
|
|
|
|
codeword_idx,
|
|
|
|
srslte_mod_string(mcs->mod),
|
|
|
|
mcs->tbs,
|
|
|
|
cfg->grant.nof_re,
|
|
|
|
cfg->grant.tb[tb_idx].nof_bits,
|
|
|
|
rv);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2017-09-19 01:32:29 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
/* Channel coding */
|
|
|
|
if (srslte_dlsch_encode2(&q->dl_sch, cfg, data, q->e[codeword_idx], tb_idx, nof_layers)) {
|
|
|
|
ERROR("Error encoding (TB%d -> CW%d)", tb_idx, codeword_idx);
|
|
|
|
return SRSLTE_ERROR;
|
2017-09-19 01:32:29 -07:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
|
|
|
|
/* Select scrambling sequence */
|
|
|
|
srslte_sequence_t* seq =
|
|
|
|
get_user_sequence(q, cfg->rnti, codeword_idx, sf->tti % 10, cfg->grant.tb[tb_idx].nof_bits);
|
|
|
|
if (!seq) {
|
|
|
|
ERROR("Error getting user sequence for rnti=0x%x\n", cfg->rnti);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bit scrambling */
|
|
|
|
srslte_scrambling_bytes(seq, (uint8_t*)q->e[codeword_idx], cfg->grant.tb[tb_idx].nof_bits);
|
|
|
|
|
|
|
|
/* Bit mapping */
|
|
|
|
srslte_mod_modulate_bytes(
|
|
|
|
&q->mod[mcs->mod], (uint8_t*)q->e[codeword_idx], q->d[codeword_idx], cfg->grant.tb[tb_idx].nof_bits);
|
|
|
|
|
2014-06-29 10:51:25 -07:00
|
|
|
} else {
|
2015-03-18 05:31:13 -07:00
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
return SRSLTE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_encode(srslte_pdsch_t* q,
|
|
|
|
srslte_dl_sf_cfg_t* sf,
|
|
|
|
srslte_pdsch_cfg_t* cfg,
|
|
|
|
uint8_t* data[SRSLTE_MAX_CODEWORDS],
|
|
|
|
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
|
2014-07-07 11:42:10 -07:00
|
|
|
{
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2014-06-17 07:32:19 -07:00
|
|
|
int i;
|
|
|
|
/* Set pointers for layermapping & precoding */
|
2019-04-23 01:53:11 -07:00
|
|
|
cf_t* x[SRSLTE_MAX_LAYERS];
|
2017-09-05 06:26:36 -07:00
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (q != NULL && cfg != NULL) {
|
|
|
|
struct timeval t[3];
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
gettimeofday(&t[1], NULL);
|
|
|
|
}
|
2017-09-05 04:17:33 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t nof_tb = cfg->grant.nof_tb;
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
for (i = 0; i < q->cell.nof_ports; i++) {
|
2015-03-02 02:11:44 -08:00
|
|
|
if (sf_symbols[i] == NULL) {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Error NULL pointer in sf_symbols[%d]\n", i);
|
2015-03-18 05:31:13 -07:00
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
2014-07-21 07:19:17 -07:00
|
|
|
}
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
|
|
|
/* If both transport block size is zero return error */
|
2017-11-08 08:10:16 -08:00
|
|
|
if (!nof_tb) {
|
2019-04-23 01:53:11 -07:00
|
|
|
ERROR("Error number of TB is zero\n");
|
2015-03-18 05:31:13 -07:00
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.nof_re > q->max_re || cfg->grant.nof_re > q->max_re) {
|
|
|
|
ERROR("Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n",
|
|
|
|
cfg->grant.nof_re,
|
|
|
|
q->max_re,
|
|
|
|
q->cell.nof_prb);
|
2017-09-05 06:26:36 -07:00
|
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2014-06-17 07:32:19 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
float rho_a = apply_power_allocation(q, cfg, sf_symbols);
|
|
|
|
|
2017-11-08 08:10:16 -08:00
|
|
|
/* Implementation of 3GPP 36.212 Table 5.3.3.1.5-1 and Table 5.3.3.1.5-2 */
|
|
|
|
for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.tb[tb_idx].enabled) {
|
|
|
|
ret |= srslte_pdsch_codeword_encode(
|
|
|
|
q, sf, cfg, cfg->softbuffers.tx[tb_idx], data[tb_idx], tb_idx, cfg->grant.nof_layers);
|
2017-09-05 04:17:33 -07:00
|
|
|
}
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
|
|
|
|
2017-11-22 09:01:13 -08:00
|
|
|
/* Set scaling configured by Power Allocation */
|
|
|
|
float scaling = 1.0f;
|
2019-04-23 01:53:11 -07:00
|
|
|
if (rho_a != 0.0f) {
|
|
|
|
scaling = rho_a;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->rnti != SRSLTE_SIRNTI) {
|
|
|
|
INFO("Encoding PDSCH SF: %d rho_a=%f, nof_ports=%d, nof_layers=%d, nof_tb=%d, pmi=%d, tx_scheme=%s\n",
|
|
|
|
sf->tti % 10,
|
|
|
|
rho_a,
|
|
|
|
q->cell.nof_ports,
|
|
|
|
cfg->grant.nof_layers,
|
|
|
|
nof_tb,
|
|
|
|
cfg->grant.pmi,
|
|
|
|
srslte_mimotype2str(cfg->grant.tx_scheme));
|
2017-11-22 09:01:13 -08:00
|
|
|
}
|
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
// Layer mapping & precode if necessary
|
|
|
|
if (q->cell.nof_ports > 1) {
|
|
|
|
int nof_symbols;
|
|
|
|
/* If number of layers is equal to transport blocks (codewords) skip layer mapping */
|
2019-04-23 01:53:11 -07:00
|
|
|
if (cfg->grant.nof_layers == nof_tb) {
|
|
|
|
for (i = 0; i < cfg->grant.nof_layers; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
x[i] = q->d[i];
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
nof_symbols = cfg->grant.nof_re;
|
2017-09-05 06:26:36 -07:00
|
|
|
} else {
|
|
|
|
/* Initialise layer map pointers */
|
2019-04-23 01:53:11 -07:00
|
|
|
for (i = 0; i < cfg->grant.nof_layers; i++) {
|
2017-09-05 06:26:36 -07:00
|
|
|
x[i] = q->x[i];
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
memset(&x[cfg->grant.nof_layers], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - cfg->grant.nof_layers));
|
|
|
|
|
|
|
|
nof_symbols = srslte_layermap_type(q->d,
|
|
|
|
x,
|
|
|
|
nof_tb,
|
|
|
|
cfg->grant.nof_layers,
|
|
|
|
(int[SRSLTE_MAX_CODEWORDS]){cfg->grant.nof_re, cfg->grant.nof_re},
|
|
|
|
cfg->grant.tx_scheme);
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
2017-09-01 04:29:11 -07:00
|
|
|
|
2017-09-05 06:26:36 -07:00
|
|
|
/* Precode */
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t codebook_idx = nof_tb == 1 ? cfg->grant.pmi : (cfg->grant.pmi + 1);
|
|
|
|
srslte_precoding_type(x,
|
|
|
|
q->symbols,
|
|
|
|
cfg->grant.nof_layers,
|
|
|
|
q->cell.nof_ports,
|
|
|
|
codebook_idx,
|
|
|
|
nof_symbols,
|
|
|
|
scaling,
|
|
|
|
cfg->grant.tx_scheme);
|
2014-06-29 10:51:25 -07:00
|
|
|
} else {
|
2017-11-22 09:01:13 -08:00
|
|
|
if (scaling == 1.0f) {
|
2019-04-23 01:53:11 -07:00
|
|
|
memcpy(q->symbols[0], q->d[0], cfg->grant.nof_re * sizeof(cf_t));
|
2017-11-22 09:01:13 -08:00
|
|
|
} else {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_vec_sc_prod_cfc(q->d[0], scaling, q->symbols[0], cfg->grant.nof_re);
|
2017-11-22 09:01:13 -08:00
|
|
|
}
|
2014-06-29 10:51:25 -07:00
|
|
|
}
|
2015-03-02 02:11:44 -08:00
|
|
|
|
|
|
|
/* mapping to resource elements */
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t lstart = SRSLTE_NOF_CTRL_SYMBOLS(q->cell, sf->cfi);
|
2015-03-02 02:11:44 -08:00
|
|
|
for (i = 0; i < q->cell.nof_ports; i++) {
|
2019-04-23 01:53:11 -07:00
|
|
|
srslte_pdsch_put(q, q->symbols[i], sf_symbols[i], &cfg->grant, lstart, sf->tti % 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
gettimeofday(&t[2], NULL);
|
|
|
|
get_time_interval(t);
|
|
|
|
cfg->meas_time_value = t[0].tv_usec;
|
2015-03-02 02:11:44 -08:00
|
|
|
}
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2015-03-18 05:31:13 -07:00
|
|
|
ret = SRSLTE_SUCCESS;
|
2017-09-05 06:26:36 -07:00
|
|
|
}
|
|
|
|
return ret;
|
2014-06-17 03:44:42 -07:00
|
|
|
}
|
2015-10-05 00:58:44 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_select_pmi(srslte_pdsch_t* q,
|
|
|
|
srslte_chest_dl_res_t* channel,
|
|
|
|
uint32_t nof_layers,
|
|
|
|
uint32_t* best_pmi,
|
|
|
|
float sinr[SRSLTE_MAX_CODEBOOKS])
|
|
|
|
{
|
|
|
|
uint32_t nof_ce = SRSLTE_NOF_RE(q->cell);
|
|
|
|
uint32_t pmi = 0;
|
2017-09-05 06:26:36 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
if (srslte_precoding_pmi_select(channel->ce, nof_ce, channel->noise_estimate, nof_layers, &pmi, sinr) < 0) {
|
|
|
|
ERROR("PMI Select for %d layers", nof_layers);
|
|
|
|
return SRSLTE_ERROR;
|
2018-06-04 04:14:27 -07:00
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
|
|
|
|
if (best_pmi) {
|
|
|
|
*best_pmi = pmi;
|
2018-06-04 04:14:27 -07:00
|
|
|
}
|
2015-10-05 00:58:44 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
return SRSLTE_SUCCESS;
|
|
|
|
}
|
2018-06-06 07:59:00 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
int srslte_pdsch_compute_cn(srslte_pdsch_t* q, srslte_chest_dl_res_t* channel, float* cn)
|
|
|
|
{
|
|
|
|
return srslte_precoding_cn(channel->ce, q->cell.nof_ports, q->nof_rx_antennas, SRSLTE_NOF_RE(q->cell), cn);
|
|
|
|
}
|
2018-06-06 07:59:00 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t srslte_pdsch_grant_rx_info(srslte_pdsch_grant_t* grant,
|
|
|
|
srslte_pdsch_res_t res[SRSLTE_MAX_CODEWORDS],
|
|
|
|
char* str,
|
|
|
|
uint32_t str_len)
|
|
|
|
{
|
2018-06-06 07:59:00 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t len = srslte_ra_dl_info(grant, str, str_len);
|
2018-06-06 07:59:00 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
len = srslte_print_check(str, str_len, len, ", crc={", 0);
|
|
|
|
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
|
|
|
|
if (grant->tb[i].enabled) {
|
|
|
|
len = srslte_print_check(str, str_len, len, "%s", res[i].crc ? "OK" : "KO");
|
|
|
|
if (i < SRSLTE_MAX_CODEWORDS - 1) {
|
|
|
|
if (grant->tb[i + 1].enabled) {
|
|
|
|
len = srslte_print_check(str, str_len, len, "/", 0);
|
|
|
|
}
|
|
|
|
}
|
2018-06-06 07:59:00 -07:00
|
|
|
}
|
|
|
|
}
|
2019-04-23 01:53:11 -07:00
|
|
|
len = srslte_print_check(str, str_len, len, "}", 0);
|
2018-06-06 07:59:00 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
// Average iterations between nof TB and divide by 2 to get full decoder iterations
|
|
|
|
len = srslte_print_check(
|
|
|
|
str, str_len, len, ", it=%.1f", (res[0].avg_iterations_block + res[1].avg_iterations_block) / grant->nof_tb / 2);
|
|
|
|
|
|
|
|
return len;
|
2018-06-06 07:59:00 -07:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t
|
|
|
|
srslte_pdsch_rx_info(srslte_pdsch_cfg_t* cfg, srslte_pdsch_res_t res[SRSLTE_MAX_CODEWORDS], char* str, uint32_t str_len)
|
|
|
|
{
|
|
|
|
|
|
|
|
uint32_t len = srslte_print_check(str, str_len, 0, "rnti=0x%x", cfg->rnti);
|
|
|
|
len += srslte_pdsch_grant_rx_info(&cfg->grant, res, &str[len], str_len - len);
|
|
|
|
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
len = srslte_print_check(str, str_len, len, ", t=%d us\n", cfg->meas_time_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
2015-10-05 00:58:44 -07:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t srslte_pdsch_tx_info(srslte_pdsch_cfg_t* cfg, char* str, uint32_t str_len)
|
|
|
|
{
|
2015-10-05 00:58:44 -07:00
|
|
|
|
2019-04-23 01:53:11 -07:00
|
|
|
uint32_t len = srslte_print_check(str, str_len, 0, "rnti=0x%x", cfg->rnti);
|
|
|
|
len += srslte_ra_dl_info(&cfg->grant, &str[len], str_len);
|
|
|
|
|
|
|
|
if (cfg->meas_time_en) {
|
|
|
|
len = srslte_print_check(str, str_len, len, ", t=%d us", cfg->meas_time_value);
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|