Implemented PDCCH DCI message correlation and updated test

This commit is contained in:
Xavier Arteaga 2021-06-04 15:31:50 +02:00 committed by Andre Puschmann
parent bcd2e59635
commit 7c78fd2050
4 changed files with 147 additions and 47 deletions

View File

@ -95,6 +95,14 @@ SRSRAN_API int srsran_pdcch_extract_llr(srsran_pdcch_t* q,
SRSRAN_API int
srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dci_cfg_t* dci_cfg, srsran_dci_msg_t* msg);
/**
* @brief Computes decoded DCI correlation. It encodes the given DCI message and compares it with the received LLRs
* @param q PDCCH object
* @param msg Previously decoded DCI message
* @return The normalized correlation between the restored symbols and the received LLRs
*/
SRSRAN_API float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg);
SRSRAN_API int
srsran_pdcch_dci_decode(srsran_pdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc);

View File

@ -10,6 +10,7 @@
*
*/
#include <complex.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
@ -412,6 +413,27 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc
return ret;
}
float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg)
{
if (q == NULL || msg == NULL) {
return 0.0f;
}
uint32_t E = PDCCH_FORMAT_NOF_BITS(msg->location.L);
uint32_t nof_llr = E / 2;
// Encode same decoded message and compute correlation
srsran_pdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, E, msg->rnti);
// Modulate
srsran_mod_modulate(&q->mod, q->e, q->d, E);
// Correlate
cf_t corr = srsran_vec_dot_prod_conj_ccc((cf_t*)&q->llr[msg->location.ncce * 72], q->d, nof_llr);
return cabsf(corr / nof_llr) * (float)M_SQRT1_2;
}
/** Performs PDCCH receiver processing to extract LLR for all control region. LLR bits are stored in srsran_pdcch_t
* object. DCI can be decoded from given locations in successive calls to srsran_pdcch_decode_msg()
*/

View File

@ -198,19 +198,20 @@ add_lte_test(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2)
add_executable(pdcch_test pdcch_test.c)
target_link_libraries(pdcch_test srsran_phy)
add_lte_test(pdcch_test_6 pdcch_test -n 6)
add_lte_test(pdcch_test_15 pdcch_test -n 15)
add_lte_test(pdcch_test_25 pdcch_test -n 25)
add_lte_test(pdcch_test_50 pdcch_test -n 50)
add_lte_test(pdcch_test_75 pdcch_test -n 75)
add_lte_test(pdcch_test_100 pdcch_test -n 100)
add_lte_test(pdcch_test_6_mimo pdcch_test -n 6 -p 2)
add_lte_test(pdcch_test_15_mimo pdcch_test -n 15 -p 2)
add_lte_test(pdcch_test_25_mimo pdcch_test -n 25 -p 2)
add_lte_test(pdcch_test_50_mimo pdcch_test -n 50 -p 2)
add_lte_test(pdcch_test_75_mimo pdcch_test -n 75 -p 2)
add_lte_test(pdcch_test_100_mimo pdcch_test -n 100 -p 2)
#add_lte_test(pdcch_test_crosscarrier pdcch_test -x)
foreach (nof_prb 6 15 25 50 75 100)
foreach (nof_ports 1 2)
foreach (cfi 1 2 3)
foreach (snr auto 15 300)
set(pdcch_test_args "")
set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1 -F)
string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args})
add_lte_test(pdcch_test${test_name_args} pdcch_test ${pdcch_test_args})
endforeach ()
endforeach ()
endforeach ()
endforeach ()
########################################################################
# PDSCH TEST

View File

@ -21,11 +21,11 @@
// Test parameters
static uint32_t pci = 1;
static uint16_t rnti = 0x46;
static uint32_t cfi = 1;
static uint32_t cfi = 2;
static uint32_t nof_ports = 1;
static srsran_dci_cfg_t dci_cfg = {};
static uint32_t nof_prb = 100;
static float snr_dB = 20.0f;
static float snr_dB = NAN;
static uint32_t repetitions = 1;
static bool false_check = false;
@ -35,9 +35,9 @@ static srsran_pdcch_t pdcch_tx = {};
static srsran_pdcch_t pdcch_rx = {};
static srsran_chest_dl_res_t chest_dl_res = {};
static srsran_channel_awgn_t awgn = {};
static cf_t* slot_symbols[SRSRAN_MAX_PORTS];
static cf_t* slot_symbols[SRSRAN_MAX_PORTS] = {};
void usage(char* prog)
static void usage(char* prog)
{
printf("Usage: %s [pfncxv]\n", prog);
printf("\t-c cell id [Default %d]\n", pci);
@ -45,13 +45,16 @@ void usage(char* prog)
printf("\t-p cell.nof_ports [Default %d]\n", nof_ports);
printf("\t-n cell.nof_prb [Default %d]\n", nof_prb);
printf("\t-x Enable/Disable Cross-scheduling [Default %s]\n", dci_cfg.cif_enabled ? "enabled" : "disabled");
printf("\t-F False detection check [Default %s]\n", false_check ? "enabled" : "disabled");
printf("\t-R Repetitions [Default %d]\n", repetitions);
printf("\t-S SNR in dB [Default %+.1f]\n", snr_dB);
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
void parse_args(int argc, char** argv)
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "pfncxv")) != -1) {
while ((opt = getopt(argc, argv, "pfncxvFRS")) != -1) {
switch (opt) {
case 'p':
nof_ports = (uint32_t)strtol(argv[optind], NULL, 10);
@ -66,7 +69,16 @@ void parse_args(int argc, char** argv)
pci = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'x':
dci_cfg.cif_enabled ^= true;
dci_cfg.cif_enabled = !dci_cfg.cif_enabled;
break;
case 'F':
false_check = !false_check;
break;
case 'R':
repetitions = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'S':
snr_dB = (float)strtof(argv[optind], NULL);
break;
case 'v':
srsran_verbose++;
@ -76,6 +88,17 @@ void parse_args(int argc, char** argv)
exit(-1);
}
}
printf("params - pci=%d; rnti=0x%04x; cfi=%d; nof_ports=%d; cif_enabled=%d; nof_prb=%d; snr_db=%+.1f; "
"repetitions=%d; false_check=%d;\n",
pci,
rnti,
cfi,
nof_ports,
dci_cfg.cif_enabled,
nof_prb,
snr_dB,
repetitions,
false_check);
}
static void print_dci_msg(const char* desc, const srsran_dci_msg_t* dci_msg)
@ -142,19 +165,32 @@ static const srsran_dci_format_t formats[] = {SRSRAN_DCI_FORMAT0,
SRSRAN_DCI_FORMAT2,
SRSRAN_DCI_NOF_FORMATS};
static float get_snr_dB(uint32_t L)
{
static const float snr_table_dB[4] = {15.0f, 6.0f, 5.0f, 0.0f};
if (isnormal(snr_dB) && L < 4) {
return snr_dB;
}
return snr_table_dB[L];
}
static int test_case1()
{
uint32_t nof_re = SRSRAN_NOF_RE(pdcch_tx.cell);
// Iterate all possible subframes
for (uint32_t f_idx = 0; formats[f_idx] != SRSRAN_DCI_NOF_FORMATS; f_idx++) {
srsran_dci_format_t format = formats[f_idx];
struct timeval t[3] = {};
uint64_t t_encode_us = 0;
uint64_t t_encode_count = 0;
uint64_t t_llr_us = 0;
uint64_t t_decode_us = 0;
uint64_t t_decode_count = 0;
// Iterate all possible subframes
for (uint32_t f_idx = 0; formats[f_idx] != SRSRAN_DCI_NOF_FORMATS; f_idx++) {
srsran_dci_format_t format = formats[f_idx];
uint32_t false_alarm_corr_count = 0;
float min_corr = INFINITY;
for (uint32_t sf_idx = 0; sf_idx < repetitions * SRSRAN_NOF_SF_X_FRAME; sf_idx++) {
srsran_dl_sf_cfg_t dl_sf_cfg = {};
@ -196,6 +232,11 @@ static int test_case1()
t_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
t_encode_count++;
// Set noise level according to aggregation level
float n0_dB = -get_snr_dB(locations[loc].L);
TESTASSERT(srsran_channel_awgn_set_n0(&awgn, n0_dB) == SRSRAN_SUCCESS);
chest_dl_res.noise_estimate = srsran_convert_dB_to_power(n0_dB);
// Apply AWGN
for (uint32_t p = 0; p < nof_ports; p++) {
srsran_channel_awgn_run_c(&awgn, slot_symbols[p], slot_symbols[p], nof_re);
@ -210,7 +251,10 @@ static int test_case1()
// Try decoding the PDCCH in all possible locations
for (uint32_t loc_rx = 0; loc_rx < locations_count; loc_rx++) {
if (!false_check && loc_rx != loc) {
// Skip location if:
// - False check is disabled and Tx/Rx dont match
// - Tx aggregation level is bigger than Rx aggregation level
if ((!false_check && loc_rx != loc) || locations[loc_rx].L < locations[loc].L) {
continue;
}
@ -227,16 +271,42 @@ static int test_case1()
t_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec);
t_decode_count++;
// Compute LLR correlation
float corr = srsran_pdcch_msg_corr(&pdcch_rx, &dci_rx);
bool rnti_match = (dci_tx.rnti == dci_rx.rnti);
bool location_match = (loc == loc_rx);
bool payload_match = (memcmp(dci_tx.payload, dci_rx.payload, dci_tx.nof_bits) == 0);
bool corr_thr = corr > 0.5f;
// Skip location if the decoding is not successful in a different location than transmitted
if (!location_match && !rnti_match) {
continue;
}
// Skip location if the correlation does not surpass the threshold
if (!location_match && !corr_thr) {
false_alarm_corr_count++;
continue;
}
// Assert correlation only if location matches
if (location_match) {
TESTASSERT(corr_thr);
if (location_match && corr < min_corr) {
min_corr = corr;
}
}
if (srsran_verbose >= SRSRAN_VERBOSE_INFO || !payload_match) {
// If payload is not match and there is no logging, set logging to info and run the decoder again
if (srsran_verbose < SRSRAN_VERBOSE_INFO) {
printf("-- Detected payload was not matched, repeating decode with INFO logs (n0: %+.1f dB, corr: %f)\n",
n0_dB,
corr);
srsran_verbose = SRSRAN_VERBOSE_INFO;
srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx);
}
print_dci_msg("Tx: ", &dci_tx);
print_dci_msg("Rx: ", &dci_rx);
}
@ -246,12 +316,16 @@ static int test_case1()
}
}
}
}
printf("test_case_1 - %.1f usec/encode; %.1f usec/llr; %.1f usec/decode;\n",
printf("test_case_1 - format %s - passed - %.1f usec/encode; %.1f usec/llr; %.1f usec/decode; min_corr=%f; "
"false_alarm_prob=%f;\n",
srsran_dci_format_string(format),
(double)t_encode_us / (double)(t_encode_count),
(double)t_llr_us / (double)(t_encode_count),
(double)t_decode_us / (double)(t_decode_count));
(double)t_decode_us / (double)(t_decode_count),
min_corr,
(double)false_alarm_corr_count / (double)t_decode_count);
}
return SRSRAN_SUCCESS;
}
@ -319,11 +393,6 @@ int main(int argc, char** argv)
goto quit;
}
if (srsran_channel_awgn_set_n0(&awgn, -snr_dB) < SRSRAN_SUCCESS) {
ERROR("Error setting n0");
goto quit;
}
// Execute actual test cases
if (test_case1() < SRSRAN_SUCCESS) {
ERROR("Test case 1 failed");