1400 lines
45 KiB
C
1400 lines
45 KiB
C
/*
|
|
* Copyright 2013-2020 Software Radio Systems Limited
|
|
*
|
|
* This file is part of srsLTE.
|
|
*
|
|
* srsLTE is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* srsLTE 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 Affero General Public License for more details.
|
|
*
|
|
* A copy of the GNU Affero 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 "srslte/srslte.h"
|
|
#include <assert.h>
|
|
#include <complex.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
#include "srslte/phy/ch_estimation/refsignal_ul.h"
|
|
#include "srslte/phy/common/phy_common.h"
|
|
#include "srslte/phy/common/sequence.h"
|
|
#include "srslte/phy/mimo/precoding.h"
|
|
#include "srslte/phy/modem/demod_soft.h"
|
|
#include "srslte/phy/phch/pucch.h"
|
|
#include "srslte/phy/scrambling/scrambling.h"
|
|
#include "srslte/phy/utils/debug.h"
|
|
#include "srslte/phy/utils/vector.h"
|
|
|
|
#define MAX_PUSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12)
|
|
|
|
/** Initializes the PUCCH transmitter and receiver */
|
|
int srslte_pucch_init_(srslte_pucch_t* q, bool is_ue)
|
|
{
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
if (q != NULL) {
|
|
ret = SRSLTE_ERROR;
|
|
bzero(q, sizeof(srslte_pucch_t));
|
|
|
|
if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
q->is_ue = is_ue;
|
|
|
|
q->users = calloc(sizeof(srslte_pucch_user_t*), q->is_ue ? 1 : (1 + SRSLTE_SIRNTI));
|
|
if (!q->users) {
|
|
perror("malloc");
|
|
goto clean_exit;
|
|
}
|
|
|
|
if (srslte_sequence_init(&q->tmp_seq, 20)) {
|
|
goto clean_exit;
|
|
}
|
|
|
|
srslte_uci_cqi_pucch_init(&q->cqi);
|
|
|
|
q->z = srslte_vec_cf_malloc(SRSLTE_PUCCH_MAX_SYMBOLS);
|
|
q->z_tmp = srslte_vec_cf_malloc(SRSLTE_PUCCH_MAX_SYMBOLS);
|
|
|
|
if (!q->is_ue) {
|
|
q->ce = srslte_vec_cf_malloc(SRSLTE_PUCCH_MAX_SYMBOLS);
|
|
}
|
|
|
|
ret = SRSLTE_SUCCESS;
|
|
}
|
|
clean_exit:
|
|
if (ret == SRSLTE_ERROR) {
|
|
srslte_pucch_free(q);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int srslte_pucch_init_ue(srslte_pucch_t* q)
|
|
{
|
|
return srslte_pucch_init_(q, true);
|
|
}
|
|
|
|
int srslte_pucch_init_enb(srslte_pucch_t* q)
|
|
{
|
|
return srslte_pucch_init_(q, false);
|
|
}
|
|
|
|
void srslte_pucch_free(srslte_pucch_t* q)
|
|
{
|
|
if (q->users) {
|
|
if (q->is_ue) {
|
|
srslte_pucch_free_rnti(q, 0);
|
|
} else {
|
|
for (int rnti = 0; rnti <= SRSLTE_SIRNTI; rnti++) {
|
|
srslte_pucch_free_rnti(q, rnti);
|
|
}
|
|
}
|
|
free(q->users);
|
|
}
|
|
|
|
srslte_sequence_free(&q->tmp_seq);
|
|
|
|
srslte_uci_cqi_pucch_free(&q->cqi);
|
|
if (q->z) {
|
|
free(q->z);
|
|
}
|
|
if (q->z_tmp) {
|
|
free(q->z_tmp);
|
|
}
|
|
if (q->ce) {
|
|
free(q->ce);
|
|
}
|
|
|
|
srslte_modem_table_free(&q->mod);
|
|
bzero(q, sizeof(srslte_pucch_t));
|
|
}
|
|
|
|
int srslte_pucch_set_cell(srslte_pucch_t* q, srslte_cell_t cell)
|
|
{
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
if (q != NULL && srslte_cell_isvalid(&cell)) {
|
|
|
|
if (cell.id != q->cell.id || q->cell.nof_prb == 0) {
|
|
q->cell = cell;
|
|
|
|
// Precompute group hopping values u.
|
|
if (srslte_group_hopping_f_gh(q->f_gh, q->cell.id)) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
if (srslte_pucch_n_cs_cell(q->cell, q->n_cs_cell)) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
}
|
|
|
|
ret = SRSLTE_SUCCESS;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void srslte_pucch_free_rnti(srslte_pucch_t* q, uint16_t rnti)
|
|
{
|
|
uint32_t rnti_idx = q->is_ue ? 0 : rnti;
|
|
|
|
if (q->users[rnti_idx]) {
|
|
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) {
|
|
srslte_sequence_free(&q->users[rnti_idx]->seq_f2[i]);
|
|
}
|
|
free(q->users[rnti_idx]);
|
|
q->users[rnti_idx] = NULL;
|
|
q->ue_rnti = 0;
|
|
}
|
|
}
|
|
|
|
int srslte_pucch_set_rnti(srslte_pucch_t* q, uint16_t rnti)
|
|
{
|
|
uint32_t rnti_idx = q->is_ue ? 0 : rnti;
|
|
|
|
// Decide whether re-generating the sequence
|
|
if (!q->users[rnti_idx]) {
|
|
// If the sequence is not allocated generate
|
|
q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t));
|
|
if (!q->users[rnti_idx]) {
|
|
ERROR("Alocating PDSCH user\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
} else if (q->users[rnti_idx]->sequence_generated && q->users[rnti_idx]->cell_id == q->cell.id && !q->is_ue) {
|
|
// The sequence was generated, cell has not changed and it is eNb, save any efforts
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
// Set sequence as not generated
|
|
q->users[rnti_idx]->sequence_generated = false;
|
|
|
|
// For each subframe
|
|
for (int sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) {
|
|
if (srslte_sequence_pucch(
|
|
&q->users[rnti_idx]->seq_f2[sf_idx], rnti, SRSLTE_NOF_SLOTS_PER_SF * sf_idx, q->cell.id)) {
|
|
ERROR("Error initializing PUCCH scrambling sequence\n");
|
|
srslte_pucch_free_rnti(q, rnti);
|
|
return SRSLTE_ERROR;
|
|
}
|
|
}
|
|
|
|
// Save generation states
|
|
q->ue_rnti = rnti;
|
|
q->users[rnti_idx]->cell_id = q->cell.id;
|
|
q->users[rnti_idx]->sequence_generated = true;
|
|
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
static cf_t uci_encode_format1()
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
static cf_t uci_encode_format1a(uint8_t bit)
|
|
{
|
|
return bit ? -1.0 : 1.0;
|
|
}
|
|
|
|
static cf_t uci_encode_format1b(uint8_t bits[2])
|
|
{
|
|
if (bits[0] == 0) {
|
|
if (bits[1] == 0) {
|
|
return 1;
|
|
} else {
|
|
return -I;
|
|
}
|
|
} else {
|
|
if (bits[1] == 0) {
|
|
return I;
|
|
} else {
|
|
return -1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static srslte_sequence_t* get_user_sequence(srslte_pucch_t* q, uint16_t rnti, uint32_t sf_idx)
|
|
{
|
|
uint32_t rnti_idx = q->is_ue ? 0 : rnti;
|
|
|
|
// The scrambling sequence is pregenerated for all RNTIs in the eNodeB but only for C-RNTI in the UE
|
|
if (rnti >= SRSLTE_CRNTI_START && rnti < SRSLTE_CRNTI_END) {
|
|
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)) {
|
|
return &q->users[rnti_idx]->seq_f2[sf_idx];
|
|
} else {
|
|
if (srslte_sequence_pucch(&q->tmp_seq, rnti, 2 * sf_idx, q->cell.id)) {
|
|
ERROR("Error computing PUCCH Format 2 scrambling sequence\n");
|
|
return NULL;
|
|
}
|
|
return &q->tmp_seq;
|
|
}
|
|
} else {
|
|
ERROR("Invalid RNTI=0x%x\n", rnti);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Encode PUCCH bits according to Table 5.4.1-1 in Section 5.4.1 of 36.211 */
|
|
static int
|
|
uci_mod_bits(srslte_pucch_t* q, srslte_ul_sf_cfg_t* sf, srslte_pucch_cfg_t* cfg, uint8_t bits[SRSLTE_PUCCH_MAX_BITS])
|
|
{
|
|
uint8_t tmp[2];
|
|
srslte_sequence_t* seq;
|
|
switch (cfg->format) {
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
q->d[0] = uci_encode_format1();
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
q->d[0] = uci_encode_format1a(bits[0]);
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
tmp[0] = bits[0];
|
|
tmp[1] = bits[1];
|
|
q->d[0] = uci_encode_format1b(tmp);
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
seq = get_user_sequence(q, cfg->rnti, sf->tti % 10);
|
|
if (seq) {
|
|
memcpy(q->bits_scram, bits, SRSLTE_PUCCH2_NOF_BITS * sizeof(uint8_t));
|
|
srslte_scrambling_b_offset(seq, q->bits_scram, 0, SRSLTE_PUCCH2_NOF_BITS);
|
|
srslte_mod_modulate(&q->mod, q->bits_scram, q->d, SRSLTE_PUCCH2_NOF_BITS);
|
|
} else {
|
|
ERROR("Error modulating PUCCH2 bits: could not generate sequence\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
seq = get_user_sequence(q, cfg->rnti, sf->tti % 10);
|
|
if (seq) {
|
|
memcpy(q->bits_scram, bits, SRSLTE_PUCCH3_NOF_BITS * sizeof(uint8_t));
|
|
srslte_scrambling_b_offset(seq, q->bits_scram, 0, SRSLTE_PUCCH3_NOF_BITS);
|
|
srslte_mod_modulate(&q->mod, q->bits_scram, q->d, SRSLTE_PUCCH3_NOF_BITS);
|
|
} else {
|
|
ERROR("Error modulating PUCCH3 bits: rnti not set\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
break;
|
|
default:
|
|
ERROR("PUCCH format %s not supported\n", srslte_pucch_format_text(cfg->format));
|
|
return SRSLTE_ERROR;
|
|
}
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
// Declare this here, since we can not include refsignal_ul.h
|
|
void srslte_refsignal_r_uv_arg_1prb(float* arg, uint32_t u);
|
|
|
|
/* 3GPP 36211 Table 5.5.2.2.2-1: Demodulation reference signal location for different PUCCH formats. */
|
|
static const uint32_t pucch_symbol_format1_cpnorm[4] = {0, 1, 5, 6};
|
|
static const uint32_t pucch_symbol_format1_cpext[4] = {0, 1, 4, 5};
|
|
static const uint32_t pucch_symbol_format2_3_cpnorm[5] = {0, 2, 3, 4, 6};
|
|
static const uint32_t pucch_symbol_format2_3_cpext[5] = {0, 1, 2, 4, 5};
|
|
|
|
static const float w_n_oc[2][3][4] = {
|
|
// Table 5.4.1-2 Orthogonal sequences w for N_sf=4 (complex argument)
|
|
{{0, 0, 0, 0}, {0, M_PI, 0, M_PI}, {0, M_PI, M_PI, 0}},
|
|
// Table 5.4.1-3 Orthogonal sequences w for N_sf=3
|
|
{{0, 0, 0, 0}, {0, 2 * M_PI / 3, 4 * M_PI / 3, 0}, {0, 4 * M_PI / 3, 2 * M_PI / 3, 0}},
|
|
|
|
};
|
|
|
|
#if defined(__clang__)
|
|
|
|
/* Precomputed constants printed with printf %a specifier (hexadecimal notation) for maximum precision
|
|
* cf_t val = cexpf(I * 2 * M_PI / 5));
|
|
* printf("%a + %aI\n", str, creal(val), cimag(val));
|
|
*
|
|
* clang expects compile-time contant expressions in the initializer list and using cexpf results
|
|
* in the following error
|
|
* error: initializer element is not a compile-time constant
|
|
*/
|
|
static const cf_t cexpf_i_2_mpi_5 = 0x1.3c6ef2p-2 + 0x1.e6f0e2p-1I;
|
|
static const cf_t cexpf_i_4_mpi_5 = -0x1.9e377cp-1 + 0x1.2cf22ep-1I;
|
|
static const cf_t cexpf_i_6_mpi_5 = -0x1.9e3778p-1 + -0x1.2cf234p-1I;
|
|
static const cf_t cexpf_i_8_mpi_5 = 0x1.3c6efcp-2 + -0x1.e6f0ep-1I;
|
|
|
|
static cf_t pucch3_w_n_oc_5[5][5] = {
|
|
{1, 1, 1, 1, 1},
|
|
{1, cexpf_i_2_mpi_5, cexpf_i_4_mpi_5, cexpf_i_6_mpi_5, cexpf_i_8_mpi_5},
|
|
{1, cexpf_i_4_mpi_5, cexpf_i_8_mpi_5, cexpf_i_2_mpi_5, cexpf_i_6_mpi_5},
|
|
{1, cexpf_i_6_mpi_5, cexpf_i_2_mpi_5, cexpf_i_8_mpi_5, cexpf_i_4_mpi_5},
|
|
{1, cexpf_i_8_mpi_5, cexpf_i_6_mpi_5, cexpf_i_4_mpi_5, cexpf_i_2_mpi_5},
|
|
};
|
|
|
|
#else // defined(__clang__)
|
|
|
|
static const cf_t pucch3_w_n_oc_5[5][5] = {
|
|
{1, 1, 1, 1, 1},
|
|
{1, cexpf(I * 2 * M_PI / 5), cexpf(I * 4 * M_PI / 5), cexpf(I * 6 * M_PI / 5), cexpf(I * 8 * M_PI / 5)},
|
|
{1, cexpf(I * 4 * M_PI / 5), cexpf(I * 8 * M_PI / 5), cexpf(I * 2 * M_PI / 5), cexpf(I * 6 * M_PI / 5)},
|
|
{1, cexpf(I * 6 * M_PI / 5), cexpf(I * 2 * M_PI / 5), cexpf(I * 8 * M_PI / 5), cexpf(I * 4 * M_PI / 5)},
|
|
{1, cexpf(I * 8 * M_PI / 5), cexpf(I * 6 * M_PI / 5), cexpf(I * 4 * M_PI / 5), cexpf(I * 2 * M_PI / 5)}};
|
|
|
|
#endif // defined(__clang__)
|
|
|
|
static const cf_t pucch3_w_n_oc_4[4][4] = {{+1, +1, +1, +1}, {+1, -1, +1, -1}, {+1, +1, -1, -1}, {+1, -1, -1, +1}};
|
|
|
|
static uint32_t get_N_sf(srslte_pucch_format_t format, uint32_t slot_idx, bool shortened)
|
|
{
|
|
switch (format) {
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
if (!slot_idx) {
|
|
return 4;
|
|
} else {
|
|
return shortened ? 3 : 4;
|
|
}
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
return SRSLTE_PUCCH2_N_SF;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
if (!slot_idx) {
|
|
return 5;
|
|
} else {
|
|
return shortened ? 4 : 5;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t get_pucch_symbol(uint32_t m, srslte_pucch_format_t format, srslte_cp_t cp)
|
|
{
|
|
switch (format) {
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
if (m < 4) {
|
|
if (SRSLTE_CP_ISNORM(cp)) {
|
|
return pucch_symbol_format1_cpnorm[m];
|
|
} else {
|
|
return pucch_symbol_format1_cpext[m];
|
|
}
|
|
}
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
if (m < 5) {
|
|
if (SRSLTE_CP_ISNORM(cp)) {
|
|
return pucch_symbol_format2_3_cpnorm[m];
|
|
} else {
|
|
return pucch_symbol_format2_3_cpext[m];
|
|
}
|
|
}
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
if (SRSLTE_CP_ISNORM(cp)) {
|
|
return pucch_symbol_format2_3_cpnorm[m % 5];
|
|
} else {
|
|
return pucch_symbol_format2_3_cpext[m % 5];
|
|
}
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Map PUCCH symbols to physical resources according to 5.4.3 in 36.211 */
|
|
static int pucch_cp(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
cf_t* source,
|
|
cf_t* dest,
|
|
bool source_is_grid)
|
|
{
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
if (q && source && dest) {
|
|
ret = SRSLTE_ERROR;
|
|
uint32_t nsymbols = SRSLTE_CP_ISNORM(q->cell.cp) ? SRSLTE_CP_NORM_NSYMB : SRSLTE_CP_EXT_NSYMB;
|
|
|
|
uint32_t n_re = 0;
|
|
uint32_t N_sf_0 = get_N_sf(cfg->format, 0, sf->shortened);
|
|
for (uint32_t ns = 0; ns < 2; ns++) {
|
|
uint32_t N_sf = get_N_sf(cfg->format, ns % 2, sf->shortened);
|
|
|
|
// Determine n_prb
|
|
uint32_t n_prb = srslte_pucch_n_prb(&q->cell, cfg, ns);
|
|
if (n_prb < q->cell.nof_prb) {
|
|
for (uint32_t i = 0; i < N_sf; i++) {
|
|
uint32_t l = get_pucch_symbol(i, cfg->format, q->cell.cp);
|
|
if (!source_is_grid) {
|
|
memcpy(&dest[SRSLTE_RE_IDX(q->cell.nof_prb, l + ns * nsymbols, n_prb * SRSLTE_NRE)],
|
|
&source[i * SRSLTE_NRE + ns * N_sf_0 * SRSLTE_NRE],
|
|
SRSLTE_NRE * sizeof(cf_t));
|
|
} else {
|
|
memcpy(&dest[i * SRSLTE_NRE + ns * N_sf_0 * SRSLTE_NRE],
|
|
&source[SRSLTE_RE_IDX(q->cell.nof_prb, l + ns * nsymbols, n_prb * SRSLTE_NRE)],
|
|
SRSLTE_NRE * sizeof(cf_t));
|
|
}
|
|
n_re += SRSLTE_NRE;
|
|
}
|
|
} else {
|
|
ERROR("Invalid PUCCH n_prb=%d\n", n_prb);
|
|
return SRSLTE_ERROR;
|
|
}
|
|
}
|
|
ret = n_re;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int pucch_put(srslte_pucch_t* q, srslte_ul_sf_cfg_t* sf, srslte_pucch_cfg_t* cfg, cf_t* z, cf_t* output)
|
|
{
|
|
return pucch_cp(q, sf, cfg, z, output, false);
|
|
}
|
|
|
|
static int pucch_get(srslte_pucch_t* q, srslte_ul_sf_cfg_t* sf, srslte_pucch_cfg_t* cfg, cf_t* input, cf_t* z)
|
|
{
|
|
return pucch_cp(q, sf, cfg, input, z, true);
|
|
}
|
|
|
|
static int encode_signal_format12(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
uint8_t bits[SRSLTE_PUCCH_MAX_BITS],
|
|
cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS],
|
|
bool signal_only)
|
|
{
|
|
if (!signal_only) {
|
|
if (uci_mod_bits(q, sf, cfg, bits)) {
|
|
ERROR("Error encoding PUCCH bits\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
} else {
|
|
// Set all ones
|
|
for (uint32_t i = 0; i < SRSLTE_PUCCH_MAX_BITS / 2; i++) {
|
|
q->d[i] = 1.0f;
|
|
}
|
|
}
|
|
uint32_t N_sf_0 = get_N_sf(cfg->format, 0, sf->shortened);
|
|
uint32_t sf_idx = sf->tti % SRSLTE_NOF_SF_X_FRAME;
|
|
for (uint32_t ns = SRSLTE_NOF_SLOTS_PER_SF * sf_idx; ns < SRSLTE_NOF_SLOTS_PER_SF * (sf_idx + 1); ns++) {
|
|
uint32_t N_sf = get_N_sf(cfg->format, ns % 2, sf->shortened);
|
|
DEBUG("ns=%d, N_sf=%d\n", ns, N_sf);
|
|
// Get group hopping number u
|
|
uint32_t f_gh = 0;
|
|
if (cfg->group_hopping_en) {
|
|
f_gh = q->f_gh[ns];
|
|
}
|
|
uint32_t u = (f_gh + (q->cell.id % 30)) % 30;
|
|
|
|
srslte_refsignal_r_uv_arg_1prb(q->tmp_arg, u);
|
|
uint32_t N_sf_widx = N_sf == 3 ? 1 : 0;
|
|
for (uint32_t m = 0; m < N_sf; m++) {
|
|
uint32_t l = get_pucch_symbol(m, cfg->format, q->cell.cp);
|
|
float alpha = 0;
|
|
if (cfg->format >= SRSLTE_PUCCH_FORMAT_2) {
|
|
alpha = srslte_pucch_alpha_format2(q->n_cs_cell, cfg, ns, l);
|
|
for (uint32_t n = 0; n < SRSLTE_PUCCH_N_SEQ; n++) {
|
|
z[(ns % 2) * N_sf * SRSLTE_PUCCH_N_SEQ + m * SRSLTE_PUCCH_N_SEQ + n] =
|
|
q->d[(ns % 2) * N_sf + m] * cexpf(I * (q->tmp_arg[n] + alpha * n));
|
|
}
|
|
} else {
|
|
uint32_t n_prime_ns = 0;
|
|
uint32_t n_oc = 0;
|
|
alpha = srslte_pucch_alpha_format1(q->n_cs_cell, cfg, q->cell.cp, true, ns, l, &n_oc, &n_prime_ns);
|
|
float S_ns = 0;
|
|
if (n_prime_ns % 2) {
|
|
S_ns = M_PI / 2;
|
|
}
|
|
DEBUG("PUCCH d_0: %.1f+%.1fi, alpha: %.1f, n_oc: %d, n_prime_ns: %d, n_rb_2=%d\n",
|
|
__real__ q->d[0],
|
|
__imag__ q->d[0],
|
|
alpha,
|
|
n_oc,
|
|
n_prime_ns,
|
|
cfg->n_rb_2);
|
|
|
|
for (uint32_t n = 0; n < SRSLTE_PUCCH_N_SEQ; n++) {
|
|
z[(ns % 2) * N_sf_0 * SRSLTE_PUCCH_N_SEQ + m * SRSLTE_PUCCH_N_SEQ + n] =
|
|
q->d[0] * cexpf(I * (w_n_oc[N_sf_widx][n_oc % 3][m] + q->tmp_arg[n] + alpha * n + S_ns));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
static int encode_signal_format3(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
uint8_t bits[SRSLTE_PUCCH_MAX_BITS],
|
|
cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS],
|
|
bool signal_only)
|
|
{
|
|
if (!signal_only) {
|
|
if (uci_mod_bits(q, sf, cfg, bits)) {
|
|
ERROR("Error encoding PUCCH bits\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < SRSLTE_PUCCH_MAX_BITS / 2; i++) {
|
|
q->d[i] = 1.0;
|
|
}
|
|
}
|
|
|
|
uint32_t N_sf_0 = get_N_sf(cfg->format, 0, sf->shortened);
|
|
uint32_t N_sf_1 = get_N_sf(cfg->format, 1, sf->shortened);
|
|
|
|
uint32_t n_oc_0 = cfg->n_pucch % N_sf_1;
|
|
uint32_t n_oc_1 = (N_sf_1 == 5) ? ((3 * cfg->n_pucch) % N_sf_1) : (n_oc_0 % N_sf_1);
|
|
|
|
cf_t* w_n_oc_0 = (cf_t*)pucch3_w_n_oc_5[n_oc_0];
|
|
cf_t* w_n_oc_1 = (cf_t*)((N_sf_1 == 5) ? pucch3_w_n_oc_5[n_oc_1] : pucch3_w_n_oc_4[n_oc_1]);
|
|
|
|
for (uint32_t n = 0; n < N_sf_0 + N_sf_1; n++) {
|
|
uint32_t l = get_pucch_symbol(n, cfg->format, q->cell.cp);
|
|
uint32_t n_cs_cell = q->n_cs_cell[(2 * (sf->tti % 10) + ((n < N_sf_0) ? 0 : 1)) % SRSLTE_NSLOTS_X_FRAME][l];
|
|
|
|
cf_t y_n[SRSLTE_NRE];
|
|
srslte_vec_cf_zero(y_n, SRSLTE_NRE);
|
|
|
|
cf_t h;
|
|
if (n < N_sf_0) {
|
|
h = w_n_oc_0[n] * cexpf(I * M_PI * floorf(n_cs_cell / 64.0f) / 2);
|
|
for (uint32_t i = 0; i < SRSLTE_NRE; i++) {
|
|
y_n[i] = h * q->d[(i + n_cs_cell) % SRSLTE_NRE];
|
|
}
|
|
} else {
|
|
h = w_n_oc_1[n - N_sf_0] * cexpf(I * M_PI * floorf(n_cs_cell / 64.0f) / 2);
|
|
for (uint32_t i = 0; i < SRSLTE_NRE; i++) {
|
|
y_n[i] = h * q->d[((i + n_cs_cell) % SRSLTE_NRE) + SRSLTE_NRE];
|
|
}
|
|
}
|
|
|
|
for (int k = 0; k < SRSLTE_NRE; k++) {
|
|
cf_t acc = 0.0f;
|
|
for (int i = 0; i < SRSLTE_NRE; i++) {
|
|
acc += y_n[i] * cexpf(-I * 2.0 * M_PI * i * k / (float)SRSLTE_NRE);
|
|
}
|
|
z[n * SRSLTE_NRE + k] = acc / sqrtf(SRSLTE_NRE);
|
|
}
|
|
}
|
|
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
static int decode_signal_format3(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
uint8_t bits[SRSLTE_PUCCH_MAX_BITS],
|
|
cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS])
|
|
{
|
|
uint32_t N_sf_0 = get_N_sf(cfg->format, 0, sf->shortened);
|
|
uint32_t N_sf_1 = get_N_sf(cfg->format, 1, sf->shortened);
|
|
|
|
uint32_t n_oc_0 = cfg->n_pucch % N_sf_1;
|
|
uint32_t n_oc_1 = (N_sf_1 == 5) ? ((3 * cfg->n_pucch) % N_sf_1) : (n_oc_0 % N_sf_1);
|
|
|
|
cf_t* w_n_oc_0 = (cf_t*)pucch3_w_n_oc_5[n_oc_0];
|
|
cf_t* w_n_oc_1 = (cf_t*)((N_sf_1 == 5) ? pucch3_w_n_oc_5[n_oc_1] : pucch3_w_n_oc_4[n_oc_1]);
|
|
|
|
memset(q->d, 0, sizeof(cf_t) * 2 * SRSLTE_NRE);
|
|
|
|
for (uint32_t n = 0; n < N_sf_0 + N_sf_1; n++) {
|
|
uint32_t l = get_pucch_symbol(n, cfg->format, q->cell.cp);
|
|
uint32_t n_cs_cell = q->n_cs_cell[(2 * (sf->tti % 10) + ((n < N_sf_0) ? 0 : 1)) % SRSLTE_NSLOTS_X_FRAME][l];
|
|
|
|
cf_t y_n[SRSLTE_NRE] = {};
|
|
|
|
// Do FFT
|
|
for (int k = 0; k < SRSLTE_NRE; k++) {
|
|
cf_t acc = 0.0f;
|
|
for (int i = 0; i < SRSLTE_NRE; i++) {
|
|
acc += z[n * SRSLTE_NRE + i] * cexpf(I * 2.0 * M_PI * i * k / (float)SRSLTE_NRE);
|
|
}
|
|
y_n[k] = acc / sqrtf(SRSLTE_NRE);
|
|
}
|
|
|
|
if (n < N_sf_0) {
|
|
cf_t h = w_n_oc_0[n] * cexpf(-I * M_PI * floorf(n_cs_cell / 64.0f) / 2);
|
|
for (uint32_t i = 0; i < SRSLTE_NRE; i++) {
|
|
q->d[(i + n_cs_cell) % SRSLTE_NRE] += h * y_n[i];
|
|
}
|
|
} else {
|
|
cf_t h = w_n_oc_1[n - N_sf_0] * cexpf(-I * M_PI * floorf(n_cs_cell / 64.0f) / 2);
|
|
for (uint32_t i = 0; i < SRSLTE_NRE; i++) {
|
|
q->d[((i + n_cs_cell) % SRSLTE_NRE) + SRSLTE_NRE] += h * y_n[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
srslte_vec_sc_prod_cfc(q->d, 2.0f / (N_sf_0 + N_sf_1), q->d, SRSLTE_NRE * 2);
|
|
|
|
srslte_sequence_t* seq = get_user_sequence(q, cfg->rnti, sf->tti % 10);
|
|
if (seq) {
|
|
srslte_demod_soft_demodulate_s(SRSLTE_MOD_QPSK, q->d, q->llr, SRSLTE_PUCCH3_NOF_BITS);
|
|
|
|
srslte_scrambling_s_offset(seq, q->llr, 0, SRSLTE_PUCCH3_NOF_BITS);
|
|
|
|
return (int)srslte_uci_decode_m_basis_bits(q->llr, SRSLTE_PUCCH3_NOF_BITS, bits, SRSLTE_UCI_MAX_ACK_SR_BITS);
|
|
} else {
|
|
ERROR("Error modulating PUCCH3 bits: rnti not set\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
static int encode_signal(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
uint8_t bits[SRSLTE_PUCCH_MAX_BITS],
|
|
cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS])
|
|
{
|
|
if (cfg->format == SRSLTE_PUCCH_FORMAT_3) {
|
|
return encode_signal_format3(q, sf, cfg, bits, z, false);
|
|
} else {
|
|
return encode_signal_format12(q, sf, cfg, bits, z, false);
|
|
}
|
|
}
|
|
|
|
// Encode bits from uci_data
|
|
static int encode_bits(srslte_pucch_cfg_t* cfg,
|
|
srslte_uci_value_t* uci_data,
|
|
srslte_pucch_format_t format,
|
|
uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS],
|
|
uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS])
|
|
{
|
|
if (format < SRSLTE_PUCCH_FORMAT_2) {
|
|
srslte_vec_u8_copy(pucch_bits, uci_data->ack.ack_value, srslte_uci_cfg_total_ack(&cfg->uci_cfg));
|
|
} else if (format >= SRSLTE_PUCCH_FORMAT_2 && format < SRSLTE_PUCCH_FORMAT_3) {
|
|
/* Put RI (goes alone) */
|
|
if (cfg->uci_cfg.cqi.ri_len) {
|
|
uint8_t temp[2] = {uci_data->ri, 0};
|
|
srslte_uci_encode_cqi_pucch(temp, cfg->uci_cfg.cqi.ri_len, pucch_bits);
|
|
} else {
|
|
/* Put CQI Report*/
|
|
uint8_t buff[SRSLTE_CQI_MAX_BITS];
|
|
int uci_cqi_len = srslte_cqi_value_pack(&cfg->uci_cfg.cqi, &uci_data->cqi, buff);
|
|
if (uci_cqi_len < 0) {
|
|
ERROR("Error encoding CQI\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
srslte_uci_encode_cqi_pucch(buff, (uint32_t)uci_cqi_len, pucch_bits);
|
|
}
|
|
if (format > SRSLTE_PUCCH_FORMAT_2) {
|
|
pucch2_bits[0] = uci_data->ack.ack_value[0];
|
|
pucch2_bits[1] = uci_data->ack.ack_value[1]; // this will be ignored in format 2a
|
|
}
|
|
} else if (format == SRSLTE_PUCCH_FORMAT_3) {
|
|
uint8_t temp[SRSLTE_UCI_MAX_ACK_BITS + 1];
|
|
uint32_t k = 0;
|
|
for (; k < srslte_uci_cfg_total_ack(&cfg->uci_cfg); k++) {
|
|
temp[k] = (uint8_t)((uci_data->ack.ack_value[k] == 1) ? 1 : 0);
|
|
}
|
|
if (cfg->uci_cfg.is_scheduling_request_tti) {
|
|
temp[k] = (uint8_t)(uci_data->scheduling_request ? 1 : 0);
|
|
k++;
|
|
}
|
|
srslte_uci_encode_m_basis_bits(temp, k, pucch_bits, SRSLTE_PUCCH3_NOF_BITS);
|
|
}
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
static bool decode_signal(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
uint8_t pucch_bits[SRSLTE_CQI_MAX_BITS],
|
|
uint32_t nof_re,
|
|
uint32_t nof_uci_bits,
|
|
float* correlation)
|
|
{
|
|
int16_t llr_pucch2[SRSLTE_CQI_MAX_BITS];
|
|
bool detected = false;
|
|
float corr = 0, corr_max = -1e9;
|
|
uint8_t b_max = 0, b2_max = 0; // default bit value, eg. HI is NACK
|
|
|
|
srslte_sequence_t* seq;
|
|
cf_t ref[SRSLTE_PUCCH_MAX_SYMBOLS];
|
|
|
|
switch (cfg->format) {
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
encode_signal(q, sf, cfg, pucch_bits, q->z_tmp);
|
|
corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re);
|
|
if (corr >= cfg->threshold_format1) {
|
|
detected = true;
|
|
}
|
|
DEBUG("format1 corr=%f, nof_re=%d, th=%f\n", corr, nof_re, cfg->threshold_format1);
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
detected = 0;
|
|
for (uint8_t b = 0; b < 2; b++) {
|
|
pucch_bits[0] = b;
|
|
encode_signal(q, sf, cfg, pucch_bits, q->z_tmp);
|
|
corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re);
|
|
if (corr > corr_max) {
|
|
corr_max = corr;
|
|
b_max = b;
|
|
}
|
|
if (corr_max > cfg->threshold_format1) { // check with format1 in case ack+sr because ack only is binary
|
|
detected = true;
|
|
}
|
|
DEBUG("format1a b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re);
|
|
}
|
|
corr = corr_max;
|
|
pucch_bits[0] = b_max;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
detected = 0;
|
|
for (uint8_t b = 0; b < 2; b++) {
|
|
for (uint8_t b2 = 0; b2 < 2; b2++) {
|
|
pucch_bits[0] = b;
|
|
pucch_bits[1] = b2;
|
|
encode_signal(q, sf, cfg, pucch_bits, q->z_tmp);
|
|
corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re);
|
|
if (corr > corr_max) {
|
|
corr_max = corr;
|
|
b_max = b;
|
|
b2_max = b2;
|
|
}
|
|
if (corr_max > cfg->threshold_format1) { // check with format1 in case ack+sr because ack only is binary
|
|
detected = true;
|
|
}
|
|
DEBUG("format1b b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re);
|
|
}
|
|
}
|
|
corr = corr_max;
|
|
pucch_bits[0] = b_max;
|
|
pucch_bits[1] = b2_max;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
seq = get_user_sequence(q, cfg->rnti, sf->tti % SRSLTE_NOF_SF_X_FRAME);
|
|
if (seq) {
|
|
encode_signal_format12(q, sf, cfg, NULL, ref, true);
|
|
srslte_vec_prod_conj_ccc(q->z, ref, q->z_tmp, SRSLTE_PUCCH_MAX_SYMBOLS);
|
|
for (int i = 0; i < (SRSLTE_PUCCH2_N_SF * SRSLTE_NOF_SLOTS_PER_SF); i++) {
|
|
q->z[i] = srslte_vec_acc_cc(&q->z_tmp[i * SRSLTE_NRE], SRSLTE_NRE) / SRSLTE_NRE;
|
|
}
|
|
srslte_demod_soft_demodulate_s(SRSLTE_MOD_QPSK, q->z, llr_pucch2, SRSLTE_PUCCH2_NOF_BITS / 2);
|
|
srslte_scrambling_s_offset(seq, llr_pucch2, 0, SRSLTE_PUCCH2_NOF_BITS);
|
|
|
|
// Calculate the LLR RMS for normalising
|
|
float llr_pow = srslte_vec_avg_power_sf(llr_pucch2, SRSLTE_PUCCH2_NOF_BITS);
|
|
|
|
if (isnormal(llr_pow)) {
|
|
float llr_rms = sqrtf(llr_pow) * SRSLTE_PUCCH2_NOF_BITS;
|
|
corr = ((float)srslte_uci_decode_cqi_pucch(&q->cqi, llr_pucch2, pucch_bits, nof_uci_bits)) / (llr_rms);
|
|
} else {
|
|
corr = 0;
|
|
}
|
|
detected = true;
|
|
} else {
|
|
ERROR("Decoding PUCCH2: could not generate sequence\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
corr = (float)decode_signal_format3(q, sf, cfg, pucch_bits, q->z) / 4800.0f;
|
|
detected = true;
|
|
break;
|
|
default:
|
|
ERROR("PUCCH format %d not implemented\n", cfg->format);
|
|
return SRSLTE_ERROR;
|
|
}
|
|
if (correlation) {
|
|
*correlation = corr;
|
|
}
|
|
return detected;
|
|
}
|
|
|
|
static void decode_bits(srslte_pucch_cfg_t* cfg,
|
|
bool pucch_found,
|
|
uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS],
|
|
uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS],
|
|
srslte_uci_value_t* uci_data)
|
|
{
|
|
if (cfg->format == SRSLTE_PUCCH_FORMAT_3) {
|
|
uint32_t nof_ack = srslte_uci_cfg_total_ack(&cfg->uci_cfg);
|
|
memcpy(uci_data->ack.ack_value, pucch_bits, nof_ack);
|
|
uci_data->scheduling_request = (pucch_bits[nof_ack] == 1);
|
|
uci_data->ack.valid = true;
|
|
} else {
|
|
// If was looking for scheduling request, update value
|
|
if (cfg->uci_cfg.is_scheduling_request_tti) {
|
|
uci_data->scheduling_request = pucch_found;
|
|
}
|
|
|
|
// Save ACK bits
|
|
for (uint32_t a = 0; a < srslte_pucch_nof_ack_format(cfg->format); a++) {
|
|
if (cfg->uci_cfg.cqi.data_enable || cfg->uci_cfg.cqi.ri_len) {
|
|
uci_data->ack.ack_value[a] = pucch2_bits[a];
|
|
} else {
|
|
uci_data->ack.ack_value[a] = pucch_bits[a];
|
|
}
|
|
}
|
|
|
|
// PUCCH2 CQI bits are already decoded
|
|
if (cfg->uci_cfg.cqi.data_enable) {
|
|
srslte_cqi_value_unpack(&cfg->uci_cfg.cqi, pucch_bits, &uci_data->cqi);
|
|
}
|
|
|
|
if (cfg->uci_cfg.cqi.ri_len) {
|
|
uci_data->ri = pucch_bits[0]; /* Assume only one bit of RI */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Encode, modulate and resource mapping of UCI data over PUCCH */
|
|
int srslte_pucch_encode(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
srslte_uci_value_t* uci_data,
|
|
cf_t* sf_symbols)
|
|
{
|
|
uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS];
|
|
srslte_vec_u8_zero(pucch_bits, SRSLTE_PUCCH_MAX_BITS);
|
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
if (q != NULL && sf_symbols != NULL) {
|
|
// Encode bits from UCI data for this format
|
|
encode_bits(cfg, uci_data, cfg->format, pucch_bits, cfg->pucch2_drs_bits);
|
|
|
|
if (encode_signal(q, sf, cfg, pucch_bits, q->z)) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
if (pucch_put(q, sf, cfg, q->z, sf_symbols) < 0) {
|
|
ERROR("Error putting PUCCH symbols\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
ret = SRSLTE_SUCCESS;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Equalize, demodulate and decode PUCCH bits according to Section 5.4.1 of 36.211 */
|
|
int srslte_pucch_decode(srslte_pucch_t* q,
|
|
srslte_ul_sf_cfg_t* sf,
|
|
srslte_pucch_cfg_t* cfg,
|
|
srslte_chest_ul_res_t* channel,
|
|
cf_t* sf_symbols,
|
|
srslte_pucch_res_t* data)
|
|
{
|
|
uint8_t pucch_bits[SRSLTE_CQI_MAX_BITS];
|
|
bzero(pucch_bits, SRSLTE_CQI_MAX_BITS * sizeof(uint8_t));
|
|
|
|
int ret = SRSLTE_ERROR_INVALID_INPUTS;
|
|
|
|
if (q != NULL && cfg != NULL && channel != NULL && data != NULL) {
|
|
|
|
uint32_t nof_cqi_bits = srslte_cqi_size(&cfg->uci_cfg.cqi);
|
|
uint32_t nof_uci_bits = cfg->uci_cfg.cqi.ri_len ? cfg->uci_cfg.cqi.ri_len : nof_cqi_bits;
|
|
|
|
int nof_re = pucch_get(q, sf, cfg, sf_symbols, q->z_tmp);
|
|
if (nof_re < 0) {
|
|
ERROR("Error getting PUCCH symbols\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
if (pucch_get(q, sf, cfg, channel->ce, q->ce) < 0) {
|
|
ERROR("Error getting PUCCH symbols\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
// Equalization
|
|
srslte_predecoding_single(q->z_tmp, q->ce, q->z, NULL, nof_re, 1.0f, channel->noise_estimate);
|
|
|
|
// Perform DMRS Detection, if enabled
|
|
if (isnormal(cfg->threshold_dmrs_detection)) {
|
|
cf_t _dmrs_corr = srslte_vec_acc_cc(q->ce, SRSLTE_NRE) / SRSLTE_NRE;
|
|
float rms = __real__(conjf(_dmrs_corr) * _dmrs_corr);
|
|
float power = srslte_vec_avg_power_cf(q->ce, SRSLTE_NRE);
|
|
data->dmrs_correlation = rms / power;
|
|
|
|
// Return not detected if the ratio is 0, NAN, +/- Infinity or below threshold
|
|
if (!isnormal(data->dmrs_correlation) || data->dmrs_correlation < cfg->threshold_dmrs_detection) {
|
|
data->correlation = 0.0f;
|
|
data->detected = false;
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Perform ML-decoding
|
|
bool pucch_found = decode_signal(q, sf, cfg, pucch_bits, nof_re, nof_uci_bits, &data->correlation);
|
|
|
|
// Convert bits to UCI data
|
|
decode_bits(cfg, pucch_found, pucch_bits, cfg->pucch2_drs_bits, &data->uci_data);
|
|
|
|
data->detected = pucch_found;
|
|
|
|
// Accept ACK and CQI only if correlation above threshold
|
|
switch (cfg->format) {
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
data->uci_data.ack.valid = data->correlation > cfg->threshold_data_valid_format1a;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
data->detected = data->correlation > cfg->threshold_data_valid_format2;
|
|
data->uci_data.ack.valid = data->detected;
|
|
data->uci_data.cqi.data_crc = data->detected;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
default:; // Not considered, do nothing
|
|
}
|
|
|
|
ret = SRSLTE_SUCCESS;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char* srslte_pucch_format_text(srslte_pucch_format_t format)
|
|
{
|
|
char* ret = NULL;
|
|
|
|
switch (format) {
|
|
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
ret = "Format 1";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
ret = "Format 1A";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
ret = "Format 1B";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
ret = "Format 2";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
ret = "Format 2A";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
ret = "Format 2B";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
ret = "Format 3";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_ERROR:
|
|
default:
|
|
ret = "Format Error";
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char* srslte_pucch_format_text_short(srslte_pucch_format_t format)
|
|
{
|
|
char* ret = NULL;
|
|
|
|
switch (format) {
|
|
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
ret = "1";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
ret = "1a";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
ret = "1b";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
ret = "2";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
ret = "2a";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
ret = "2b";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
ret = "3";
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_ERROR:
|
|
default:
|
|
ret = "Err";
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint32_t srslte_pucch_nof_ack_format(srslte_pucch_format_t format)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
switch (format) {
|
|
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
ret = 1;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
ret = 2;
|
|
break;
|
|
default:
|
|
// Keep default
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Verify PUCCH configuration as given in Section 5.4 36.211 */
|
|
bool srslte_pucch_cfg_isvalid(srslte_pucch_cfg_t* cfg, uint32_t nof_prb)
|
|
{
|
|
if (cfg->delta_pucch_shift > 0 && cfg->delta_pucch_shift < 4 && cfg->N_cs < 8 &&
|
|
(cfg->N_cs % cfg->delta_pucch_shift) == 0 && cfg->n_rb_2 <= nof_prb) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint32_t srslte_pucch_n_prb(srslte_cell_t* cell, srslte_pucch_cfg_t* cfg, uint32_t ns)
|
|
{
|
|
uint32_t m = srslte_pucch_m(cfg, cell->cp);
|
|
// Determine n_prb
|
|
uint32_t n_prb = m / 2;
|
|
if ((m + ns) % 2) {
|
|
n_prb = cell->nof_prb - 1 - m / 2;
|
|
}
|
|
return n_prb;
|
|
}
|
|
|
|
// Compute m according to Section 5.4.3 of 36.211
|
|
uint32_t srslte_pucch_m(const srslte_pucch_cfg_t* cfg, srslte_cp_t cp)
|
|
{
|
|
uint32_t m = 0;
|
|
switch (cfg->format) {
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
m = cfg->n_rb_2;
|
|
|
|
uint32_t c = SRSLTE_CP_ISNORM(cp) ? 3 : 2;
|
|
if (cfg->n_pucch >= c * cfg->N_cs / cfg->delta_pucch_shift) {
|
|
m = (cfg->n_pucch - c * cfg->N_cs / cfg->delta_pucch_shift) / (c * SRSLTE_NRE / cfg->delta_pucch_shift) +
|
|
cfg->n_rb_2 + (uint32_t)ceilf((float)cfg->N_cs / 8);
|
|
}
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
m = cfg->n_pucch / SRSLTE_NRE;
|
|
break;
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
m = cfg->n_pucch / 5;
|
|
break;
|
|
default:
|
|
m = 0;
|
|
break;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/* Generates n_cs_cell according to Sec 5.4 of 36.211 */
|
|
int srslte_pucch_n_cs_cell(srslte_cell_t cell, uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB])
|
|
{
|
|
srslte_sequence_t seq;
|
|
bzero(&seq, sizeof(srslte_sequence_t));
|
|
|
|
srslte_sequence_LTE_pr(&seq, 8 * SRSLTE_CP_NSYMB(cell.cp) * SRSLTE_NSLOTS_X_FRAME, cell.id);
|
|
|
|
for (uint32_t ns = 0; ns < SRSLTE_NSLOTS_X_FRAME; ns++) {
|
|
for (uint32_t l = 0; l < SRSLTE_CP_NSYMB(cell.cp); l++) {
|
|
n_cs_cell[ns][l] = 0;
|
|
for (uint32_t i = 0; i < 8; i++) {
|
|
n_cs_cell[ns][l] += seq.c[8 * SRSLTE_CP_NSYMB(cell.cp) * ns + 8 * l + i] << i;
|
|
}
|
|
}
|
|
}
|
|
srslte_sequence_free(&seq);
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
int srslte_pucch_collision(const srslte_cell_t* cell, const srslte_pucch_cfg_t* cfg1, const srslte_pucch_cfg_t* cfg2)
|
|
{
|
|
// Invalid inputs, return false
|
|
if (!cell || !cfg1 || !cfg2) {
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
|
}
|
|
|
|
// Different formats, not possible to compute collision
|
|
if (cfg1->format != cfg2->format) {
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
// If resources are the same, return collision and do not compute more
|
|
if (cfg1->n_pucch == cfg2->n_pucch) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
// Calculate frequency domain resource
|
|
uint32_t m1 = srslte_pucch_m(cfg1, cell->cp);
|
|
uint32_t m2 = srslte_pucch_m(cfg2, cell->cp);
|
|
|
|
// Check if they are different, no collison
|
|
if (m1 != m2) {
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB] = {};
|
|
srslte_pucch_n_cs_cell(*cell, n_cs_cell);
|
|
|
|
float alpha1, alpha2;
|
|
uint32_t n_oc1 = 0;
|
|
uint32_t n_oc2 = 0;
|
|
uint32_t n_prime1 = 0;
|
|
uint32_t n_prime2 = 0;
|
|
|
|
switch (cfg1->format) {
|
|
|
|
case SRSLTE_PUCCH_FORMAT_1:
|
|
case SRSLTE_PUCCH_FORMAT_1A:
|
|
case SRSLTE_PUCCH_FORMAT_1B:
|
|
srslte_pucch_alpha_format1(n_cs_cell, cfg1, cell->cp, false, 0, 0, &n_oc1, &n_prime1);
|
|
srslte_pucch_alpha_format1(n_cs_cell, cfg2, cell->cp, false, 0, 0, &n_oc2, &n_prime2);
|
|
return ((n_oc1 == n_oc2) && (n_prime1 % 2 == n_prime2 % 2)) ? SRSLTE_ERROR : SRSLTE_SUCCESS;
|
|
|
|
case SRSLTE_PUCCH_FORMAT_2:
|
|
case SRSLTE_PUCCH_FORMAT_2A:
|
|
case SRSLTE_PUCCH_FORMAT_2B:
|
|
alpha1 = srslte_pucch_alpha_format2(n_cs_cell, cfg1, 0, 0);
|
|
alpha2 = srslte_pucch_alpha_format2(n_cs_cell, cfg2, 0, 0);
|
|
return (alpha1 == alpha2) ? SRSLTE_ERROR : SRSLTE_SUCCESS;
|
|
|
|
case SRSLTE_PUCCH_FORMAT_3:
|
|
return (cfg1->n_pucch % 5 == cfg2->n_pucch % 5) ? SRSLTE_ERROR : SRSLTE_SUCCESS;
|
|
|
|
case SRSLTE_PUCCH_FORMAT_ERROR:
|
|
default:; // Do nothing
|
|
}
|
|
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
int srslte_pucch_cfg_assert(const srslte_cell_t* cell, const srslte_pucch_cfg_t* cfg)
|
|
{
|
|
// Invalid inouts, return error
|
|
if (!cell || !cfg) {
|
|
return SRSLTE_ERROR_INVALID_INPUTS;
|
|
}
|
|
|
|
// Load base configuration
|
|
srslte_pucch_cfg_t cfg1 = *cfg;
|
|
srslte_pucch_cfg_t cfg2 = *cfg;
|
|
|
|
// Set Format 1b
|
|
cfg1.format = SRSLTE_PUCCH_FORMAT_1B;
|
|
cfg2.format = SRSLTE_PUCCH_FORMAT_1B;
|
|
|
|
// Check collision with N_pucch_1 Vs n_pucch_sr
|
|
cfg1.n_pucch = cfg->N_pucch_1;
|
|
cfg2.n_pucch = cfg->n_pucch_sr;
|
|
if (srslte_pucch_collision(cell, &cfg1, &cfg2) != SRSLTE_SUCCESS) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
if (cfg->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) {
|
|
// Checks channel selection resources do not collide with N_pucch_1
|
|
for (uint32_t i = 0; i < SRSLTE_PUCCH_SIZE_AN_CS; i++) {
|
|
for (uint32_t j = 0; j < SRSLTE_PUCCH_NOF_AN_CS; j++) {
|
|
cfg2.n_pucch = cfg2.n1_pucch_an_cs[i][j];
|
|
|
|
// Check collision with N_pucch_1
|
|
cfg1.n_pucch = cfg->N_pucch_1;
|
|
if (srslte_pucch_collision(cell, &cfg1, &cfg2) != SRSLTE_SUCCESS) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
// Check collision with n_pucch_sr
|
|
cfg1.n_pucch = cfg->n_pucch_sr;
|
|
if (srslte_pucch_collision(cell, &cfg1, &cfg2) != SRSLTE_SUCCESS) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
|
|
// Check collision with j + 1
|
|
cfg1.n_pucch = cfg2.n1_pucch_an_cs[i][(j + 1) % SRSLTE_PUCCH_NOF_AN_CS];
|
|
if (srslte_pucch_collision(cell, &cfg1, &cfg2) != SRSLTE_SUCCESS) {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
/* Calculates alpha for format 1/a/b according to 5.5.2.2.2 (is_dmrs=true) or 5.4.1 (is_dmrs=false) of 36.211 */
|
|
float srslte_pucch_alpha_format1(const uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB],
|
|
const srslte_pucch_cfg_t* cfg,
|
|
srslte_cp_t cp,
|
|
bool is_dmrs,
|
|
uint32_t ns,
|
|
uint32_t l,
|
|
uint32_t* n_oc_ptr,
|
|
uint32_t* n_prime_ns)
|
|
{
|
|
uint32_t c = SRSLTE_CP_ISNORM(cp) ? 3 : 2;
|
|
uint32_t N_prime = (cfg->n_pucch < c * cfg->N_cs / cfg->delta_pucch_shift) ? cfg->N_cs : SRSLTE_NRE;
|
|
|
|
uint32_t n_prime = cfg->n_pucch;
|
|
if (cfg->n_pucch >= c * cfg->N_cs / cfg->delta_pucch_shift) {
|
|
n_prime = (cfg->n_pucch - c * cfg->N_cs / cfg->delta_pucch_shift) % (c * SRSLTE_NRE / cfg->delta_pucch_shift);
|
|
}
|
|
if (ns % 2) {
|
|
if (cfg->n_pucch >= c * cfg->N_cs / cfg->delta_pucch_shift) {
|
|
n_prime = (c * (n_prime + 1)) % (c * SRSLTE_NRE / cfg->delta_pucch_shift + 1) - 1;
|
|
} else {
|
|
uint32_t d = SRSLTE_CP_ISNORM(cp) ? 2 : 0;
|
|
uint32_t h = (n_prime + d) % (c * N_prime / cfg->delta_pucch_shift);
|
|
n_prime = (h / c) + (h % c) * N_prime / cfg->delta_pucch_shift;
|
|
}
|
|
}
|
|
|
|
if (n_prime_ns) {
|
|
*n_prime_ns = n_prime;
|
|
}
|
|
|
|
uint32_t n_oc_div = (!is_dmrs && SRSLTE_CP_ISEXT(cp)) ? 2 : 1;
|
|
|
|
uint32_t n_oc = (n_prime * cfg->delta_pucch_shift) / N_prime;
|
|
if (!is_dmrs && SRSLTE_CP_ISEXT(cp)) {
|
|
n_oc *= 2;
|
|
}
|
|
if (n_oc_ptr) {
|
|
*n_oc_ptr = n_oc;
|
|
}
|
|
uint32_t n_cs = 0;
|
|
if (SRSLTE_CP_ISNORM(cp)) {
|
|
n_cs = (n_cs_cell[ns][l] + (n_prime * cfg->delta_pucch_shift + (n_oc % cfg->delta_pucch_shift)) % N_prime) %
|
|
SRSLTE_NRE;
|
|
} else {
|
|
n_cs = (n_cs_cell[ns][l] + (n_prime * cfg->delta_pucch_shift + n_oc / n_oc_div) % N_prime) % SRSLTE_NRE;
|
|
}
|
|
|
|
DEBUG("n_cs=%d, N_prime=%d, delta_pucch=%d, n_prime=%d, ns=%d, l=%d, ns_cs_cell=%d\n",
|
|
n_cs,
|
|
N_prime,
|
|
cfg->delta_pucch_shift,
|
|
n_prime,
|
|
ns,
|
|
l,
|
|
n_cs_cell[ns][l]);
|
|
|
|
return 2 * M_PI * (n_cs) / SRSLTE_NRE;
|
|
}
|
|
|
|
/* Calculates alpha for format 2/a/b according to 5.4.2 of 36.211 */
|
|
float srslte_pucch_alpha_format2(const uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB],
|
|
const srslte_pucch_cfg_t* cfg,
|
|
uint32_t ns,
|
|
uint32_t l)
|
|
{
|
|
uint32_t n_prime = cfg->n_pucch % SRSLTE_NRE;
|
|
if (cfg->n_pucch >= SRSLTE_NRE * cfg->n_rb_2) {
|
|
n_prime = (cfg->n_pucch + cfg->N_cs + 1) % SRSLTE_NRE;
|
|
}
|
|
if (ns % 2) {
|
|
n_prime = (SRSLTE_NRE * (n_prime + 1)) % (SRSLTE_NRE + 1) - 1;
|
|
if (cfg->n_pucch >= SRSLTE_NRE * cfg->n_rb_2) {
|
|
int x = (SRSLTE_NRE - 2 - (int)cfg->n_pucch) % SRSLTE_NRE;
|
|
if (x >= 0) {
|
|
n_prime = (uint32_t)x;
|
|
} else {
|
|
n_prime = SRSLTE_NRE + x;
|
|
}
|
|
}
|
|
}
|
|
uint32_t n_cs = (n_cs_cell[ns][l] + n_prime) % SRSLTE_NRE;
|
|
float alpha = 2 * M_PI * (n_cs) / SRSLTE_NRE;
|
|
DEBUG("n_pucch: %d, ns: %d, l: %d, n_prime: %d, n_cs: %d, alpha=%f\n", cfg->n_pucch, ns, l, n_prime, n_cs, alpha);
|
|
return alpha;
|
|
}
|
|
|
|
/* Modulates bit 20 and 21 for Formats 2a and 2b as in Table 5.4.2-1 in 36.211 */
|
|
int srslte_pucch_format2ab_mod_bits(srslte_pucch_format_t format, uint8_t bits[2], cf_t* d_10)
|
|
{
|
|
if (d_10) {
|
|
if (format == SRSLTE_PUCCH_FORMAT_2A) {
|
|
*d_10 = bits[0] ? -1.0 : 1.0;
|
|
return SRSLTE_SUCCESS;
|
|
} else if (format == SRSLTE_PUCCH_FORMAT_2B) {
|
|
if (bits[0] == 0) {
|
|
if (bits[1] == 0) {
|
|
*d_10 = 1.0;
|
|
} else {
|
|
*d_10 = -I;
|
|
}
|
|
} else {
|
|
if (bits[1] == 0) {
|
|
*d_10 = I;
|
|
} else {
|
|
*d_10 = -1.0;
|
|
}
|
|
}
|
|
return SRSLTE_SUCCESS;
|
|
} else {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
} else {
|
|
return SRSLTE_ERROR;
|
|
}
|
|
}
|
|
|
|
void srslte_pucch_tx_info(srslte_pucch_cfg_t* cfg, srslte_uci_value_t* uci_data, char* str, uint32_t str_len)
|
|
{
|
|
uint32_t n = srslte_print_check(str,
|
|
str_len,
|
|
0,
|
|
"rnti=0x%x, f=%s, n_pucch=%d",
|
|
cfg->rnti,
|
|
srslte_pucch_format_text_short(cfg->format),
|
|
cfg->n_pucch);
|
|
|
|
if (uci_data) {
|
|
srslte_uci_data_info(&cfg->uci_cfg, uci_data, &str[n], str_len - n);
|
|
}
|
|
}
|
|
|
|
void srslte_pucch_rx_info(srslte_pucch_cfg_t* cfg, srslte_pucch_res_t* pucch_res, char* str, uint32_t str_len)
|
|
{
|
|
uint32_t n = srslte_print_check(str,
|
|
str_len,
|
|
0,
|
|
"rnti=0x%x, f=%s, n_pucch=%d",
|
|
cfg->rnti,
|
|
srslte_pucch_format_text_short(cfg->format),
|
|
cfg->n_pucch);
|
|
|
|
if (pucch_res) {
|
|
if (isnormal(cfg->threshold_dmrs_detection)) {
|
|
n = srslte_print_check(str, str_len, n, ", dmrs_corr=%.3f", pucch_res->dmrs_correlation);
|
|
}
|
|
|
|
n = srslte_print_check(str, str_len, n, ", corr=%.3f", pucch_res->correlation);
|
|
|
|
srslte_uci_data_info(&cfg->uci_cfg, &pucch_res->uci_data, &str[n], str_len - n);
|
|
}
|
|
}
|