Implement NR-PUCCH Format 2 DMRS put/Estimate

This commit is contained in:
Xavier Arteaga 2021-01-25 19:59:44 +01:00 committed by Xavier Arteaga
parent 958afaee60
commit 4c6944b883
8 changed files with 265 additions and 26 deletions

View File

@ -65,4 +65,40 @@ SRSLTE_API int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t*
const cf_t* slot_symbols,
srslte_chest_ul_res_t* res);
/**
* @brief Puts NR-PUCCH format 2 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 2 resource
* @param[out] slot_symbols Resource grid of the given slot
* @return SRSLTE_SUCCESS if successful, SRSLTE_ERROR code otherwise
*/
int srslte_dmrs_pucch_format2_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_t* resource,
cf_t* slot_symbols);
/**
* @brief Estimates NR-PUCCH format 2 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 2 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
*/
int srslte_dmrs_pucch_format2_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_t* resource,
const cf_t* slot_symbols,
srslte_chest_ul_res_t* res);
#endif // SRSLTE_DMRS_PUCCH_H

View File

@ -23,7 +23,7 @@ extern "C" {
/**
* @brief Defines the number of symbols per slot. Defined by TS 38.211 v15.8.0 Table 4.3.2-1.
*/
#define SRSLTE_NSYMB_PER_SLOT_NR 14
#define SRSLTE_NSYMB_PER_SLOT_NR 14U
/**
* @brief Defines the resource grid size in physical resource elements (frequency and time domain)

View File

@ -25,6 +25,8 @@
#include "srslte/config.h"
#include "srslte/phy/common/phy_common.h"
#define SRSLTE_SEQUENCE_MOD(X) ((X) & (uint32_t)INT32_MAX)
typedef struct SRSLTE_API {
uint32_t x1;
uint32_t x2;

View File

@ -54,6 +54,7 @@ typedef struct SRSLTE_API {
srslte_uci_nr_t uci;
uint8_t* b;
cf_t* d;
cf_t* ce;
} srslte_pucch_nr_t;
/**

View File

@ -31,9 +31,8 @@
static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uint32_t n_id)
{
return (uint32_t)(
((1UL << 17UL) * (SRSLTE_NSYMB_PER_SLOT_NR * slot_idx + symbol_idx + 1UL) * (2UL * n_id + 1UL) + 2UL * n_id) %
INT32_MAX);
return SRSLTE_SEQUENCE_MOD((((SRSLTE_NSYMB_PER_SLOT_NR * slot_idx + symbol_idx + 1UL) * (2UL * n_id + 1UL)) << 17U) +
2UL * n_id);
}
static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_carrier_nr_t* carrier,

View File

@ -11,6 +11,7 @@
*/
#include "srslte/phy/ch_estimation/dmrs_pucch.h"
#include "srslte/phy/common/sequence.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h"
@ -160,7 +161,8 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
srslte_chest_ul_res_t* res)
{
if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL) {
if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL ||
res == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
@ -223,7 +225,7 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
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);
cf_t corr = srslte_vec_acc_cc(ce[m], SRSLTE_NRE) / 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);
@ -236,10 +238,14 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
// Set power measures
rsrp = SRSLTE_MIN(rsrp, epre);
res->rsrp = rsrp;
res->rsrp_dBfs = srslte_convert_power_to_dB(rsrp);
res->epre = epre;
res->epre_dBfs = srslte_convert_power_to_dB(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);
res->snr_db = srslte_convert_power_to_dB(res->snr);
// Compute Time Aligment error in microseconds
if (isnormal(ta_err)) {
@ -280,6 +286,152 @@ int srslte_dmrs_pucch_format1_estimate(const srslte_pucch_nr_t* q,
return SRSLTE_SUCCESS;
}
static uint32_t dmrs_pucch_format2_cinit(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 n = SRSLTE_SLOT_NR_MOD(slot->idx, carrier->numerology);
uint32_t n_id = (cfg->scrambling_id_present) ? cfg->scambling_id : carrier->id;
return SRSLTE_SEQUENCE_MOD((((SRSLTE_NSYMB_PER_SLOT_NR * n + l + 1U) * (2U * n_id + 1U)) << 17U) + 2U * n_id);
}
int srslte_dmrs_pucch_format2_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_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_cfg_resource_valid(resource) < SRSLTE_SUCCESS) {
ERROR("Invalid PUCCH format 1 resource\n");
return SRSLTE_ERROR;
}
uint32_t l_start = resource->start_symbol_idx;
uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols;
uint32_t k_start = SRSLTE_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSLTE_NRE + 1;
uint32_t k_end = SRSLTE_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSLTE_NRE;
for (uint32_t l = l_start; l < l_end; l++) {
// Compute sequence initial state
uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l);
srslte_sequence_state_t sequence = {};
srslte_sequence_state_init(&sequence, cinit);
// Skip PRBs to start
srslte_sequence_state_advance(&sequence, 2 * 4 * resource->starting_prb);
// Generate sequence
cf_t r_l[SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4];
srslte_sequence_state_gen_f(&sequence, M_SQRT1_2, (float*)r_l, 2 * 4 * resource->nof_prb);
// Put sequence in k = 3 * m + 1
for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) {
slot_symbols[l * carrier->nof_prb * SRSLTE_NRE + k] = r_l[i];
}
}
return SRSLTE_SUCCESS;
}
int srslte_dmrs_pucch_format2_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_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 ||
res == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
if (srslte_pucch_nr_cfg_resource_valid(resource) < SRSLTE_SUCCESS) {
ERROR("Invalid PUCCH format 1 resource\n");
return SRSLTE_ERROR;
}
cf_t ce[SRSLTE_PUCCH_NR_FORMAT2_MAX_NSYMB][SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4];
uint32_t l_start = resource->start_symbol_idx;
uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols;
uint32_t k_start = SRSLTE_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSLTE_NRE + 1;
uint32_t k_end = SRSLTE_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSLTE_NRE;
uint32_t nof_ref = 4 * resource->nof_prb;
for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) {
// Compute sequence initial state
uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l);
srslte_sequence_state_t sequence = {};
srslte_sequence_state_init(&sequence, cinit);
// Skip PRBs to start
srslte_sequence_state_advance(&sequence, 2 * 4 * resource->starting_prb);
// Generate sequence
cf_t r_l[SRSLTE_PUCCH_NR_FORMAT2_MAX_NPRB * 4];
srslte_sequence_state_gen_f(&sequence, M_SQRT1_2, (float*)r_l, 2 * nof_ref);
// Put sequence in k = 3 * m + 1
for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) {
ce[j][i] = slot_symbols[l * carrier->nof_prb * SRSLTE_NRE + k];
}
srslte_vec_prod_conj_ccc(ce[j], r_l, ce[j], nof_ref);
}
// Perform measurements
float epre = 0.0f;
float rsrp = 0.0f;
float ta_err = 0.0f;
for (uint32_t i = 0; i < resource->nof_symbols; i++) {
cf_t corr = srslte_vec_acc_cc(ce[i], nof_ref) / nof_ref;
rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr;
epre += srslte_vec_avg_power_cf(ce[i], nof_ref);
ta_err += srslte_vec_estimate_frequency(ce[i], nof_ref);
}
epre /= resource->nof_symbols;
rsrp /= resource->nof_symbols;
ta_err /= resource->nof_symbols;
// Set power measures
rsrp = SRSLTE_MIN(rsrp, epre);
res->rsrp = rsrp;
res->rsrp_dBfs = srslte_convert_power_to_dB(rsrp);
res->epre = epre;
res->epre_dBfs = srslte_convert_power_to_dB(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);
// Compute Time Aligment error in microseconds
if (isnormal(ta_err)) {
ta_err /= 15e3f * (float)(1U << carrier->numerology) * 3; // 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;
}
// Perform averaging
// ...
// Zero order hold
for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) {
for (uint32_t k = k_start - 1, i = 0; k < k_end; k++, i++) {
res->ce[l * carrier->nof_prb * SRSLTE_NRE + k] = ce[j][i / 3];
}
}
return SRSLTE_SUCCESS;
}
int srslte_dmrs_pucch_format_3_4_get_symbol_idx(const srslte_pucch_nr_resource_t* resource,
uint32_t idx[SRSLTE_DMRS_PUCCH_FORMAT_3_4_MAX_NSYMB])
{

View File

@ -165,6 +165,13 @@ int srslte_pucch_nr_init(srslte_pucch_nr_t* q, const srslte_pucch_nr_args_t* arg
return SRSLTE_ERROR;
}
// Allocate temporal channel estimates
q->ce = srslte_vec_cf_malloc(max_encoded_bits / 2);
if (q->ce == NULL) {
ERROR("Malloc\n");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
@ -187,6 +194,10 @@ void srslte_pucch_nr_free(srslte_pucch_nr_t* q)
free(q->d);
}
if (q->ce != NULL) {
free(q->ce);
}
SRSLTE_MEM_ZERO(q, srslte_pucch_nr_t, 1);
}
@ -582,14 +593,26 @@ static int pucch_nr_format2_decode(srslte_pucch_nr_t* q,
uint32_t k_end = (resource->starting_prb + resource->nof_prb) * SRSLTE_NRE;
for (uint32_t l = l_start, i = 0; l < l_end; l++) {
cf_t* symbol_ptr = &slot_symbols[l * carrier->nof_prb * SRSLTE_NRE];
cf_t* ce_ptr = &chest_res->ce[l * carrier->nof_prb * SRSLTE_NRE];
for (uint32_t k = k_start; k < k_end; k += 3) {
q->d[i++] = symbol_ptr[k];
q->d[i++] = symbol_ptr[k + 2];
q->d[i] = symbol_ptr[k];
q->ce[i] = ce_ptr[k];
i++;
q->d[i] = symbol_ptr[k + 2];
q->ce[i] = ce_ptr[k + 2];
i++;
}
}
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_INFO && !handler_registered) {
INFO("d=");
srslte_vec_fprint_c(stdout, q->d, resource->nof_symbols * resource->nof_prb * (SRSLTE_NRE - 4));
INFO("ce=");
srslte_vec_fprint_c(stdout, q->ce, resource->nof_symbols * resource->nof_prb * (SRSLTE_NRE - 4));
}
// Equalise
if (srslte_predecoding_single(q->d, chest_res->ce, q->d, NULL, E, 1.0f, chest_res->noise_estimate) < SRSLTE_SUCCESS) {
if (srslte_predecoding_single(q->d, q->ce, q->d, NULL, E, 1.0f, chest_res->noise_estimate) < SRSLTE_SUCCESS) {
ERROR("Error Pre-decoding\n");
return SRSLTE_ERROR;
}

View File

@ -12,16 +12,14 @@
#include "srslte/common/test_common.h"
#include "srslte/phy/ch_estimation/dmrs_pucch.h"
#include "srslte/phy/channel/ch_awgn.h"
#include "srslte/phy/phch/pucch_nr.h"
#include "srslte/phy/phch/ra_ul_nr.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/random.h"
#include "srslte/phy/utils/vector.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
static srslte_carrier_nr_t carrier = {
@ -32,10 +30,12 @@ static srslte_carrier_nr_t carrier = {
1 // max_mimo_layers
};
static uint32_t starting_prb_stride = 4;
static uint32_t starting_symbol_stride = 4;
static srslte_random_t random_gen = NULL;
static int format = -1;
static uint32_t starting_prb_stride = 4;
static uint32_t starting_symbol_stride = 4;
static srslte_random_t random_gen = NULL;
static int format = -1;
static float snr_db = 30.0f;
static srslte_channel_awgn_t awgn = {};
static int test_pucch_format0(srslte_pucch_nr_t* pucch, const srslte_pucch_nr_common_cfg_t* cfg, cf_t* slot_symbols)
{
@ -122,10 +122,18 @@ static int test_pucch_format1(srslte_pucch_nr_t* pucch,
TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) ==
SRSLTE_SUCCESS);
// Apply AWGN
srslte_channel_awgn_run_c(
&awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR);
// Estimate channel
TESTASSERT(srslte_dmrs_pucch_format1_estimate(
pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS);
TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f);
// Decode PUCCH
uint8_t b_rx[SRSLTE_PUCCH_NR_FORMAT1_MAX_NOF_BITS];
TESTASSERT(srslte_pucch_nr_format1_decode(
@ -199,15 +207,19 @@ static int test_pucch_format2(srslte_pucch_nr_t* pucch,
SRSLTE_SUCCESS);
// Put DMRS
// TESTASSERT(srslte_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource,
// slot_symbols) ==
// SRSLTE_SUCCESS);
TESTASSERT(srslte_dmrs_pucch_format2_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) ==
SRSLTE_SUCCESS);
// Apply AWGN
srslte_channel_awgn_run_c(
&awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR);
// Estimate channel
// TESTASSERT(srslte_dmrs_pucch_format1_estimate(
// pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) ==
// SRSLTE_SUCCESS);
srslte_chest_ul_res_set_identity(chest_res);
TESTASSERT(srslte_dmrs_pucch_format2_estimate(
pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSLTE_SUCCESS);
TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f);
TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f);
// Decode PUCCH
srslte_uci_value_nr_t uci_value_rx = {};
@ -238,13 +250,14 @@ static void usage(char* prog)
printf("\t-c cell id [Default %d]\n", carrier.id);
printf("\t-n nof_prb [Default %d]\n", carrier.nof_prb);
printf("\t-f format [Default %d]\n", format);
printf("\t-s SNR in dB [Default %.2f]\n", snr_db);
printf("\t-v [set verbose to debug, default none]\n");
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "cnfv")) != -1) {
while ((opt = getopt(argc, argv, "cnfsv")) != -1) {
switch (opt) {
case 'c':
carrier.id = (uint32_t)strtol(argv[optind], NULL, 10);
@ -255,6 +268,9 @@ static void parse_args(int argc, char** argv)
case 'f':
format = (int)strtol(argv[optind], NULL, 10);
break;
case 's':
snr_db = strtof(argv[optind], NULL);
break;
case 'v':
srslte_verbose++;
break;
@ -297,6 +313,16 @@ int main(int argc, char** argv)
goto clean_exit;
}
if (srslte_channel_awgn_init(&awgn, 1234) < SRSLTE_SUCCESS) {
ERROR("AWGN init\n");
goto clean_exit;
}
if (srslte_channel_awgn_set_n0(&awgn, -snr_db) < SRSLTE_SUCCESS) {
ERROR("AWGN set N0\n");
goto clean_exit;
}
srslte_pucch_nr_common_cfg_t common_cfg = {};
// Test Format 0
@ -331,7 +357,7 @@ clean_exit:
srslte_pucch_nr_free(&pucch);
srslte_chest_ul_res_free(&chest_res);
srslte_channel_awgn_free(&awgn);
srslte_random_free(random_gen);
if (ret == SRSLTE_SUCCESS) {