Added NR-PUCCH Format 1 encoder, decoder and DMRS

This commit is contained in:
Xavier Arteaga 2021-01-18 16:08:39 +01:00 committed by Xavier Arteaga
parent 33bb387f52
commit 697bdb4d6d
5 changed files with 688 additions and 61 deletions

View File

@ -0,0 +1,56 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSLTE_DMRS_PUCCH_H
#define SRSLTE_DMRS_PUCCH_H
#include "srslte/config.h"
#include "srslte/phy/ch_estimation/chest_ul.h"
#include "srslte/phy/phch/pucch_nr.h"
/**
* @brief Puts NR-PUCCH format 1 DMRS in the provided resource grid
* @param[in] q NR-PUCCH encoder/decoder object
* @param[in] carrier Carrier configuration
* @param[in] cfg PUCCH common configuration
* @param[in] slot slot configuration
* @param[in] resource PUCCH format 1 resource
* @param[out] slot_symbols Resource grid of the given slot
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_dmrs_pucch_format1_put(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
cf_t* slot_symbols);
/**
* @brief Estimates NR-PUCCH format 1 resource elements from their DMRS in the provided resource grid
* @param[in] q NR-PUCCH encoder/decoder object
* @param[in] carrier Carrier configuration
* @param[in] cfg PUCCH common configuration
* @param[in] slot slot configuration
* @param[in] resource PUCCH format 1 resource
* @param[in] slot_symbols Resource grid of the given slot
* @param[out] res UL Channel estimator result
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
const cf_t* slot_symbols,
srslte_chest_ul_res_t* res);
#endif // SRSLTE_DMRS_PUCCH_H

View File

@ -17,14 +17,20 @@
#include "srslte/phy/common/phy_common_nr.h" #include "srslte/phy/common/phy_common_nr.h"
#include "srslte/phy/common/zc_sequence.h" #include "srslte/phy/common/zc_sequence.h"
#include "srslte/phy/modem/modem_table.h" #include "srslte/phy/modem/modem_table.h"
#include <srslte/srslte.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
/** /**
* @brief Maximum number of NR-PUCCH format 1 symbols (without DMRS) * @brief Maximum number of symbols (without DMRS) that NR-PUCCH format 1 can transmit
*/ */
#define SRSLTE_PUCCH_NR_FORMAT1_N_MAX 7 #define SRSLTE_PUCCH_NR_FORMAT1_N_MAX 7
/**
* @brief Maximum number of bit that NR-PUCCH format 1 can carry
*/
#define SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS 2
typedef enum SRSLTE_API { typedef enum SRSLTE_API {
SRSLTE_PUCCH_NR_FORMAT_0 = 0, SRSLTE_PUCCH_NR_FORMAT_0 = 0,
SRSLTE_PUCCH_NR_FORMAT_1, SRSLTE_PUCCH_NR_FORMAT_1,
@ -124,16 +130,58 @@ typedef struct SRSLTE_API {
* @param q Object * @param q Object
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/ */
int srslte_pucch_nr_init(srslte_pucch_nr_t* q); SRSLTE_API int srslte_pucch_nr_init(srslte_pucch_nr_t* q);
/** /**
* @brief Deallocates an NR-PUCCH encoder/decoder object * @brief Deallocates an NR-PUCCH encoder/decoder object
* @param q Object * @param q Object
*/ */
void srslte_pucch_nr_free(srslte_pucch_nr_t* q); SRSLTE_API void srslte_pucch_nr_free(srslte_pucch_nr_t* q);
/** /**
* @brief Puts NR-PUCCH format 0 in the resource grid * @brief Computes the NR-PUCCH group sequence
* @remark Implemented according to TS 38.211 clause 6.3.2.2.1 Group and sequence hopping
* @param[in] carrier Serving cell and UL BWP configuration
* @param[in] cfg PUCCH common configuration
* @param[out] u_ Group sequence (u)
* @param[out] v_ Base sequence (v)
* @return SRSLTE_SUCCESS if provide arguments are right, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
uint32_t* u_,
uint32_t* v_);
/**
* @brief Computes the NR alpha index (1-NRE)
* @param[in] carrier Serving cell and UL BWP configuration
* @param[in] cfg PUCCH common configuration
* @param[in] slot slot configuration
* @param[in] l OFDM Symbol, relative to the NR-PUCCH transmission start
* @param[in] l_prime Initial OFDM symbol, relative to the transmission slot start
* @param[in] m0 Initial cyclic shift
* @param[in] m_cs Set to zero expect for format 0
* @param[out] alpha_idx Computed alpha index
* @return SRSLTE_SUCCESS if provide arguments are right, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_alpha_idx(const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
uint32_t l,
uint32_t l_prime,
uint32_t m0,
uint32_t m_cs,
uint32_t* alpha_idx);
/**
* @brief Validates a PUCCH format 1 resource configuration provided by upper layers
* @param resource Resource configuration to validate
* @return SRSLTE_SUCCESS if valid, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource);
/**
* @brief Encode and writes NR-PUCCH format 0 in the resource grid
* @remark Described in TS 38.211 clause 6.3.2.3 PUCCH format 0 * @remark Described in TS 38.211 clause 6.3.2.3 PUCCH format 0
* @param[in] q NR-PUCCH encoder/decoder object * @param[in] q NR-PUCCH encoder/decoder object
* @param[in] carrier Carrier configuration * @param[in] carrier Carrier configuration
@ -144,13 +192,13 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q);
* @param[out] slot_symbols Resource grid of the given slot * @param[out] slot_symbols Resource grid of the given slot
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/ */
int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q, SRSLTE_API int srslte_pucch_nr_format0_encode(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier, const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot, const srslte_dl_slot_cfg_t* slot,
srslte_pucch_nr_resource_format0_t* resource, srslte_pucch_nr_resource_format0_t* resource,
uint32_t m_cs, uint32_t m_cs,
cf_t* slot_symbols); cf_t* slot_symbols);
/** /**
* @brief Measures PUCCH format 0 in the resource grid * @brief Measures PUCCH format 0 in the resource grid
@ -164,13 +212,76 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q,
* @param[out] measure Measurement structure * @param[out] measure Measurement structure
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise * @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/ */
int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q, SRSLTE_API int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier, const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot, const srslte_dl_slot_cfg_t* slot,
srslte_pucch_nr_resource_format0_t* resource, srslte_pucch_nr_resource_format0_t* resource,
uint32_t m_cs, uint32_t m_cs,
const cf_t* slot_symbols, const cf_t* slot_symbols,
srslte_pucch_nr_measure_t* measure); srslte_pucch_nr_measure_t* measure);
/**
* @brief Validates a PUCCH format 1 resource configuration provided by upper layers
* @param resource Resource configuration to validate
* @return SRSLTE_SUCCESS if valid, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource);
/**
* @brief Get NR-PUCCH orthogonal sequence w
* @remark Defined by TS 38.211 Table 6.3.2.4.1-2: Orthogonal sequences ... for PUCCH format 1
* @param[in] q NR-PUCCH encoder/decoder object
* @param[in] n_pucch Number of PUCCH symbols
* @param[in] i sequence index
* @param m OFDM symbol index
* @return Orthogonal sequence complex number if valid, NAN otherwise
*/
SRSLTE_API cf_t srslte_pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m);
/**
* @brief Encodes and puts NR-PUCCH format 1 in the resource grid
* @remark Described in TS 38.211 clause 6.3.2.4 PUCCH format 1
* @param[in] q NR-PUCCH encoder/decoder object
* @param[in] carrier Carrier configuration
* @param[in] cfg PUCCH common configuration
* @param[in] slot slot configuration
* @param[in] resource PUCCH format 1 resource
* @param[in] b Bits to encode in the message
* @param[in] nof_bits Number of bits to encode in the message
* @param[out] slot_symbols Resource grid of the given slot
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_format1_encode(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
uint8_t* b,
uint32_t nof_bits,
cf_t* slot_symbols);
/**
* @brief Decodes NR-PUCCH format 1
* @param[in] q NR-PUCCH encoder/decoder object
* @param[in] carrier Carrier configuration
* @param[in] cfg PUCCH common configuration
* @param[in] slot slot configuration
* @param[in] resource PUCCH format 1 resource
* @param[in] chest_res Channel estimator result
* @param[in] slot_symbols Resource grid of the given slot
* @param[out] b Bits to decode
* @param[in] nof_bits Number of bits to decode in the message
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/
SRSLTE_API int srslte_pucch_nr_format1_decode(srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
srslte_chest_ul_res_t* chest_res,
cf_t* slot_symbols,
uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS],
uint32_t nof_bits);
#endif // SRSLTE_PUCCH_NR_H #endif // SRSLTE_PUCCH_NR_H

View File

@ -0,0 +1,281 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srslte/phy/ch_estimation/dmrs_pucch.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h"
// Implements TS 38.211 table 6.4.1.3.1.1-1: Number of DM-RS symbols and the corresponding N_PUCCH...
static uint32_t dmrs_pucch_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime)
{
if (resource->intra_slot_hopping) {
if (m_prime == 0) {
switch (resource->nof_symbols) {
case 4:
case 5:
return 1;
case 6:
case 7:
case 8:
case 9:
return 2;
case 10:
case 11:
case 12:
case 13:
return 3;
case 14:
return 4;
default:; // Do nothing
}
} else {
switch (resource->nof_symbols) {
case 4:
case 6:
return 1;
case 5:
case 7:
case 8:
case 10:
return 2;
case 9:
case 11:
case 12:
case 14:
return 3;
case 13:
return 4;
default:; // Do nothing
}
}
} else if (m_prime == 0) {
switch (resource->nof_symbols) {
case 4:
return 2;
case 5:
case 6:
return 3;
case 7:
case 8:
return 4;
case 9:
case 10:
return 5;
case 11:
case 12:
return 6;
case 13:
case 14:
return 7;
default:; // Do nothing
}
}
ERROR("Invalid case nof_symbols=%d and m_prime=%d\n", resource->nof_symbols, m_prime);
return 0;
}
int srslte_dmrs_pucch_format1_put(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
cf_t* slot_symbols)
{
if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
if (srslte_pucch_nr_format1_resource_valid(resource) < SRSLTE_SUCCESS) {
ERROR("Invalid PUCCH format 1 resource\n");
return SRSLTE_ERROR;
}
// Get group sequence
uint32_t u = 0;
uint32_t v = 0;
if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n");
return SRSLTE_ERROR;
}
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0);
if (n_pucch == 0) {
ERROR("Error getting number of symbols\n");
return SRSLTE_ERROR;
}
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t m = 0; m < n_pucch; m++) {
// Clause 6.4.1.3.1.2 specifies l=0,2,4...
uint32_t l = m * 2;
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSLTE_SUCCESS) {
ERROR("Calculating alpha\n");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence\n");
return SRSLTE_ERROR;
}
// Get w_i_m
cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSLTE_NRE];
srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE);
// Put z in the grid
srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE);
}
return SRSLTE_SUCCESS;
}
int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
const cf_t* slot_symbols,
srslte_chest_ul_res_t* res)
{
if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
if (srslte_pucch_nr_format1_resource_valid(resource) < SRSLTE_SUCCESS) {
ERROR("Invalid PUCCH format 1 resource\n");
return SRSLTE_ERROR;
}
// Get group sequence
uint32_t u = 0;
uint32_t v = 0;
if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n");
return SRSLTE_ERROR;
}
uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0);
if (n_pucch == 0) {
ERROR("Error getting number of symbols\n");
return SRSLTE_ERROR;
}
cf_t ce[SRSLTE_PUCCH_NR_FORMAT1_N_MAX][SRSLTE_NRE];
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t m = 0; m < n_pucch; m++) {
// Clause 6.4.1.3.1.2 specifies l=0,2,4...
uint32_t l = m * 2;
// Get start of the sequence in resource grid
const cf_t* slot_symbols_ptr =
&slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
// Get Alpha index
uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) <
SRSLTE_SUCCESS) {
ERROR("Calculating alpha\n");
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence\n");
return SRSLTE_ERROR;
}
// Get w_i_m
cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSLTE_NRE];
srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE);
// Calculate least square estimates for this symbol
srslte_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[m], SRSLTE_NRE);
}
// Perform measurements
float rsrp = 0.0f;
float epre = 0.0f;
float ta_err = 0.0f;
for (uint32_t m = 0; m < n_pucch; m++) {
cf_t corr = srslte_vec_acc_cc(ce[m], SRSLTE_NRE);
rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
epre += srslte_vec_avg_power_cf(ce[m], SRSLTE_NRE);
ta_err += srslte_vec_estimate_frequency(ce[m], SRSLTE_NRE);
}
// Average measurements
rsrp /= n_pucch;
epre /= n_pucch;
ta_err /= n_pucch;
// Set power measures
rsrp = SRSLTE_MIN(rsrp, epre);
res->noise_estimate = epre - rsrp;
res->noise_estimate_dbm = srslte_convert_power_to_dB(res->noise_estimate);
res->snr = rsrp / res->noise_estimate;
res->snr_db = srslte_convert_power_to_dB(res->snr_db);
// Compute Time Aligment error in microseconds
if (isnormal(ta_err)) {
ta_err /= 15e3f * (float)(1U << carrier->numerology); // Convert from normalized frequency to seconds
ta_err *= 1e6f; // Convert to micro-seconds
ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second
res->ta_us = ta_err;
} else {
res->ta_us = 0.0f;
}
// Measure CFO
res->cfo = NAN; // Not implemented
// Do averaging here
// ... Not implemented
// Interpolates between DMRS symbols
for (uint32_t m = 0; m < n_pucch; m++) {
uint32_t l = m * 2 + 1;
cf_t* ce_ptr = &res->ce[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
if (m != n_pucch - 1) {
// If it is not the last symbol with DMRS, average between
srslte_vec_sum_ccc(ce[m], ce[m + 1], ce_ptr, SRSLTE_NRE);
srslte_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSLTE_NRE);
} else if (m != 0) {
// Extrapolate for the last if more than 1 are provided
srslte_vec_sc_prod_cfc(ce[m], 3.0f, ce_ptr, SRSLTE_NRE);
srslte_vec_sub_ccc(ce_ptr, ce[m - 1], ce_ptr, SRSLTE_NRE);
srslte_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSLTE_NRE);
} else {
// Simply copy the
srslte_vec_cf_copy(ce_ptr, ce[m], SRSLTE_NRE);
}
}
return SRSLTE_SUCCESS;
}

View File

@ -18,9 +18,9 @@
#include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h" #include "srslte/phy/utils/vector.h"
#include <complex.h> #include <complex.h>
#include <srslte/srslte.h>
// Implements TS 38.211 clause 6.3.2.2.1 Group and sequence hopping int srslte_pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier,
static int pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
uint32_t* u_, uint32_t* u_,
uint32_t* v_) uint32_t* v_)
@ -57,14 +57,19 @@ static int pucch_nr_group_sequence(const srslte_carrier_nr_t* carrier,
} }
// Implements TS 38.211 clause 6.3.2.2.2 Cyclic shift hopping // Implements TS 38.211 clause 6.3.2.2.2 Cyclic shift hopping
static uint32_t pucch_nr_alpha_idx(const srslte_carrier_nr_t* carrier, int srslte_pucch_nr_alpha_idx(const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot, const srslte_dl_slot_cfg_t* slot,
uint32_t l, uint32_t l,
uint32_t l_prime, uint32_t l_prime,
uint32_t m0, uint32_t m0,
uint32_t m_cs) uint32_t m_cs,
uint32_t* alpha_idx)
{ {
if (carrier == NULL || cfg == NULL || slot == NULL || alpha_idx == NULL) {
return SRSLTE_ERROR;
}
// Compute number of slot // Compute number of slot
uint32_t n_slot = slot->idx % SRSLTE_NSLOTS_PER_FRAME_NR(carrier->numerology); uint32_t n_slot = slot->idx % SRSLTE_NSLOTS_PER_FRAME_NR(carrier->numerology);
@ -80,10 +85,12 @@ static uint32_t pucch_nr_alpha_idx(const srslte_carrier_nr_t* carrier,
n_cs += cs[SRSLTE_NSYMB_PER_SLOT_NR * n_slot + (l + l_prime) * 8 + m] << m; n_cs += cs[SRSLTE_NSYMB_PER_SLOT_NR * n_slot + (l + l_prime) * 8 + m] << m;
} }
return (m0 + m_cs + n_cs) % SRSLTE_NRE; *alpha_idx = (m0 + m_cs + n_cs) % SRSLTE_NRE;
return SRSLTE_SUCCESS;
} }
static int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource) int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource_format0_t* resource)
{ {
if (resource == NULL) { if (resource == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS; return SRSLTE_ERROR_INVALID_INPUTS;
@ -107,7 +114,7 @@ static int srslte_pucch_nr_format0_resource_valid(const srslte_pucch_nr_resource
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
static int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource) int srslte_pucch_nr_format1_resource_valid(const srslte_pucch_nr_resource_format1_t* resource)
{ {
if (resource == NULL) { if (resource == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS; return SRSLTE_ERROR_INVALID_INPUTS;
@ -152,8 +159,6 @@ static uint32_t
{{}, {}, {}, {}, {}, {0, 5, 4, 3, 2, 1}, {0, 5, 3, 1, 6, 4, 2}}, {{}, {}, {}, {}, {}, {0, 5, 4, 3, 2, 1}, {0, 5, 3, 1, 6, 4, 2}},
{{}, {}, {}, {}, {}, {}, {0, 6, 5, 4, 3, 2, 1}}}; {{}, {}, {}, {}, {}, {}, {0, 6, 5, 4, 3, 2, 1}}};
#define SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS 2
int srslte_pucch_nr_init(srslte_pucch_nr_t* q) int srslte_pucch_nr_init(srslte_pucch_nr_t* q)
{ {
if (q == NULL) { if (q == NULL) {
@ -205,13 +210,13 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q)
SRSLTE_MEM_ZERO(q, srslte_pucch_nr_t, 1); SRSLTE_MEM_ZERO(q, srslte_pucch_nr_t, 1);
} }
int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q, int srslte_pucch_nr_format0_encode(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier, const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot, const srslte_dl_slot_cfg_t* slot,
srslte_pucch_nr_resource_format0_t* resource, srslte_pucch_nr_resource_format0_t* resource,
uint32_t m_cs, uint32_t m_cs,
cf_t* slot_symbols) cf_t* slot_symbols)
{ {
if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) { if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS; return SRSLTE_ERROR_INVALID_INPUTS;
@ -224,7 +229,7 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q,
uint32_t u = 0; uint32_t u = 0;
uint32_t v = 0; uint32_t v = 0;
if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n"); ERROR("Error getting group sequence\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
@ -232,7 +237,11 @@ int srslte_pucch_nr_format0_put(const srslte_pucch_nr_t* q,
uint32_t l_prime = resource->start_symbol_idx; uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t l = 0; l < resource->nof_symbols; l++) { for (uint32_t l = 0; l < resource->nof_symbols; l++) {
// Get Alpha index // Get Alpha index
uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) <
SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
// get r_uv sequence from LUT object // get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
@ -271,7 +280,7 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q,
uint32_t u = 0; uint32_t u = 0;
uint32_t v = 0; uint32_t v = 0;
if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n"); ERROR("Error getting group sequence\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
@ -281,7 +290,11 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q,
float rsrp = 0.0f; float rsrp = 0.0f;
for (uint32_t l = 0; l < resource->nof_symbols; l++) { for (uint32_t l = 0; l < resource->nof_symbols; l++) {
// Get Alpha index // Get Alpha index
uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) <
SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
// get r_uv sequence from LUT object // get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
@ -320,7 +333,7 @@ int srslte_pucch_nr_format0_measure(const srslte_pucch_nr_t* q,
} }
// Implements TS 38.211 table 6.3.2.4.1-1 Number of PUCCH symbols and the corresponding N_PUC... // Implements TS 38.211 table 6.3.2.4.1-1 Number of PUCCH symbols and the corresponding N_PUC...
uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime) static uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* resource, uint32_t m_prime)
{ {
if (resource->intra_slot_hopping) { if (resource->intra_slot_hopping) {
if (m_prime == 0) { if (m_prime == 0) {
@ -337,7 +350,7 @@ uint32_t pucch_nr_format1_n_pucch(const srslte_pucch_nr_resource_format1_t* reso
return resource->nof_symbols / 2; return resource->nof_symbols / 2;
} }
static cf_t pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m) cf_t srslte_pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uint32_t i, uint32_t m)
{ {
if (n_pucch < 1 || n_pucch > SRSLTE_PUCCH_NR_FORMAT1_N_MAX) { if (n_pucch < 1 || n_pucch > SRSLTE_PUCCH_NR_FORMAT1_N_MAX) {
ERROR("Invalid n_pucch\n"); ERROR("Invalid n_pucch\n");
@ -356,14 +369,14 @@ static cf_t pucch_nr_format1_w(const srslte_pucch_nr_t* q, uint32_t n_pucch, uin
return q->format1_w_i_m[i][n_pucch - 1][m]; return q->format1_w_i_m[i][n_pucch - 1][m];
} }
int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q, int srslte_pucch_nr_format1_encode(const srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier, const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg, const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot, const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource, const srslte_pucch_nr_resource_format1_t* resource,
uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS], uint8_t* b,
uint32_t nof_bits, uint32_t nof_bits,
cf_t* slot_symbols) cf_t* slot_symbols)
{ {
uint32_t m_cs = 0; uint32_t m_cs = 0;
@ -392,7 +405,7 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q,
// Get group sequence // Get group sequence
uint32_t u = 0; uint32_t u = 0;
uint32_t v = 0; uint32_t v = 0;
if (pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) { if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n"); ERROR("Error getting group sequence\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
@ -406,7 +419,11 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q,
cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE]; cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
// Get Alpha index // Get Alpha index
uint32_t alpha_idx = pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs); uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) <
SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
// get r_uv sequence from LUT object // get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
@ -420,7 +437,7 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q,
srslte_vec_sc_prod_ccc(r_uv, d, y, SRSLTE_NRE); srslte_vec_sc_prod_ccc(r_uv, d, y, SRSLTE_NRE);
// Get w_i_m // Get w_i_m
cf_t w_i_m = pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * y(n) // Compute z(n) = w(i) * y(n)
cf_t z[SRSLTE_NRE]; cf_t z[SRSLTE_NRE];
@ -430,5 +447,91 @@ int srslte_pucch_nr_put_format1(srslte_pucch_nr_t* q,
srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE); srslte_vec_cf_copy(slot_symbols_ptr, z, SRSLTE_NRE);
} }
return SRSLTE_SUCCESS;
}
int srslte_pucch_nr_format1_decode(srslte_pucch_nr_t* q,
const srslte_carrier_nr_t* carrier,
const srslte_pucch_nr_common_cfg_t* cfg,
const srslte_dl_slot_cfg_t* slot,
const srslte_pucch_nr_resource_format1_t* resource,
srslte_chest_ul_res_t* chest_res,
cf_t* slot_symbols,
uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS],
uint32_t nof_bits)
{
uint32_t m_cs = 0;
if (carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || b == NULL || slot_symbols == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
if (srslte_pucch_nr_format1_resource_valid(resource) < SRSLTE_SUCCESS) {
ERROR("Invalid PUCCH format 1 resource\n");
return SRSLTE_SUCCESS;
}
if (nof_bits > 2) {
ERROR("Invalid number of bits (%d)\n", nof_bits);
return SRSLTE_ERROR;
}
// Received symbol d
cf_t d = 0;
// Get group sequence
uint32_t u = 0;
uint32_t v = 0;
if (srslte_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSLTE_SUCCESS) {
ERROR("Error getting group sequence\n");
return SRSLTE_ERROR;
}
// Calculate number of symbols carrying PUCCH (No DMRS)
uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0);
uint32_t l_prime = resource->start_symbol_idx;
for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) {
// Get start of the sequence in resource grid
cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
cf_t* ce_ptr = &chest_res->ce[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSLTE_NRE];
// Equalise x = w(i) * d' * r_uv(n)
cf_t x[SRSLTE_NRE];
srslte_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSLTE_NRE, 1.0f, chest_res->noise_estimate);
// Get Alpha index
uint32_t alpha_idx = 0;
if (srslte_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) <
SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
// get r_uv sequence from LUT object
const cf_t* r_uv = srslte_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx);
if (r_uv == NULL) {
ERROR("Getting r_uv sequence\n");
return SRSLTE_ERROR;
}
// Get w_i_m
cf_t w_i_m = srslte_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m);
// Compute z(n) = w(i) * r_uv(n)
cf_t z[SRSLTE_NRE];
srslte_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSLTE_NRE);
// Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d'
d += srslte_vec_dot_prod_conj_ccc(x, z, SRSLTE_NRE);
}
// Demodulate d
float llr[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
srslte_demod_soft_demodulate((nof_bits == 1) ? SRSLTE_MOD_BPSK : SRSLTE_MOD_QPSK, &d, llr, 1);
// Hard decision
for (uint32_t i = 0; i < nof_bits; i++) {
b[i] = llr[i] > 0.0f ? 1 : 0;
}
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }

View File

@ -11,6 +11,7 @@
*/ */
#include "srslte/common/test_common.h" #include "srslte/common/test_common.h"
#include "srslte/phy/ch_estimation/dmrs_pucch.h"
#include "srslte/phy/phch/pucch_nr.h" #include "srslte/phy/phch/pucch_nr.h"
#include "srslte/phy/utils/debug.h" #include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h" #include "srslte/phy/utils/vector.h"
@ -47,7 +48,7 @@ static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_co
for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= 11; for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= 11;
resource.initial_cyclic_shift++) { resource.initial_cyclic_shift++) {
for (uint32_t m_cs = 0; m_cs <= 6; m_cs += 2) { for (uint32_t m_cs = 0; m_cs <= 6; m_cs += 2) {
TESTASSERT(srslte_pucch_nr_format0_put(pucch, &carrier, cfg, &slot, &resource, m_cs, slot_symbols) == TESTASSERT(srslte_pucch_nr_format0_encode(pucch, &carrier, cfg, &slot, &resource, m_cs, slot_symbols) ==
SRSLTE_SUCCESS); SRSLTE_SUCCESS);
// Measure PUCCH format 0 for all possible values of m_cs // Measure PUCCH format 0 for all possible values of m_cs
@ -77,6 +78,66 @@ static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_co
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
static int test_pucch_format1(srslte_pucch_nr_t* pucch,
const srslte_pucch_nr_common_cfg_t* cfg,
srslte_chest_ul_res_t* chest_res,
cf_t* slot_symbols)
{
srslte_dl_slot_cfg_t slot = {};
srslte_pucch_nr_resource_format1_t resource = {};
for (slot.idx = 0; slot.idx < SRSLTE_NSLOTS_PER_FRAME_NR(carrier.numerology); slot.idx++) {
for (resource.starting_prb = 0; resource.starting_prb < carrier.nof_prb;
resource.starting_prb += starting_prb_stride) {
for (resource.nof_symbols = 4; resource.nof_symbols <= 14; resource.nof_symbols++) {
for (resource.start_symbol_idx = 0;
resource.start_symbol_idx <= SRSLTE_NSYMB_PER_SLOT_NR - resource.nof_symbols;
resource.start_symbol_idx += starting_symbol_stride) {
for (resource.time_domain_occ = 0; resource.time_domain_occ <= 6; resource.time_domain_occ++) {
for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= 11;
resource.initial_cyclic_shift++) {
for (uint32_t nof_bits = 1; nof_bits <= SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) {
for (uint32_t word = 0; word < (1U << nof_bits); word++) {
// Generate bits
uint8_t b[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {};
for (uint32_t i = 0; i < nof_bits; i++) {
b[i] = (word >> i) & 1U;
}
// Encode PUCCH
TESTASSERT(srslte_pucch_nr_format1_encode(
pucch, &carrier, cfg, &slot, &resource, b, nof_bits, slot_symbols) == SRSLTE_SUCCESS);
// Put DMRS
TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) ==
SRSLTE_SUCCESS);
// Estimate channel
TESTASSERT(srslte_dmrs_pucch_format1_estimate(
pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS);
// Decode PUCCH
uint8_t b_rx[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
TESTASSERT(srslte_pucch_nr_format1_decode(
pucch, &carrier, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits) ==
SRSLTE_SUCCESS);
// Check received bits
for (uint32_t i = 0; i < nof_bits; i++) {
TESTASSERT(b[i] == b_rx[i]);
}
}
}
}
}
}
}
}
}
return SRSLTE_SUCCESS;
}
static void usage(char* prog) static void usage(char* prog)
{ {
printf("Usage: %s [csNnv]\n", prog); printf("Usage: %s [csNnv]\n", prog);
@ -111,9 +172,10 @@ int main(int argc, char** argv)
int ret = SRSLTE_ERROR; int ret = SRSLTE_ERROR;
parse_args(argc, argv); parse_args(argc, argv);
uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR; uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR;
cf_t* slot_symb = srslte_vec_cf_malloc(nof_re); cf_t* slot_symb = srslte_vec_cf_malloc(nof_re);
srslte_pucch_nr_t pucch = {}; srslte_pucch_nr_t pucch = {};
srslte_chest_ul_res_t chest_res = {};
if (slot_symb == NULL) { if (slot_symb == NULL) {
ERROR("Alloc\n"); ERROR("Alloc\n");
@ -125,12 +187,25 @@ int main(int argc, char** argv)
goto clean_exit; goto clean_exit;
} }
if (srslte_chest_ul_res_init(&chest_res, carrier.nof_prb)) {
ERROR("Chest UL\n");
goto clean_exit;
}
srslte_pucch_nr_common_cfg_t common_cfg = {}; srslte_pucch_nr_common_cfg_t common_cfg = {};
// Test Format 0
if (test_pucch_format0(&pucch, &common_cfg, slot_symb) < SRSLTE_SUCCESS) { if (test_pucch_format0(&pucch, &common_cfg, slot_symb) < SRSLTE_SUCCESS) {
ERROR("Failed PUCCH format 0\n"); ERROR("Failed PUCCH format 0\n");
goto clean_exit; goto clean_exit;
} }
// Test Format 1
if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb) < SRSLTE_SUCCESS) {
ERROR("Failed PUCCH format 1\n");
goto clean_exit;
}
ret = SRSLTE_SUCCESS; ret = SRSLTE_SUCCESS;
clean_exit: clean_exit:
if (slot_symb) { if (slot_symb) {
@ -138,6 +213,7 @@ clean_exit:
} }
srslte_pucch_nr_free(&pucch); srslte_pucch_nr_free(&pucch);
srslte_chest_ul_res_free(&chest_res);
if (ret == SRSLTE_SUCCESS) { if (ret == SRSLTE_SUCCESS) {
printf("Test passed!\n"); printf("Test passed!\n");