From 1f762844ee7d6c955a72eb6018bbc98a3c72ee88 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Sat, 1 Feb 2020 00:51:53 +0100 Subject: [PATCH] Initial PUCCH format 3 decoder --- lib/include/srslte/phy/phch/pucch.h | 1 + lib/include/srslte/phy/phch/uci.h | 2 + lib/src/phy/phch/pucch.c | 111 +++++++++++++++--- lib/src/phy/phch/uci.c | 72 +++++++++++- lib/src/phy/ue/ue_ul.c | 22 ---- lib/test/phy/CMakeLists.txt | 6 +- .../phy/{pucch_cs_test.c => pucch_ca_test.c} | 55 +++++---- 7 files changed, 201 insertions(+), 68 deletions(-) rename lib/test/phy/{pucch_cs_test.c => pucch_ca_test.c} (67%) diff --git a/lib/include/srslte/phy/phch/pucch.h b/lib/include/srslte/phy/phch/pucch.h index 46fb77a44..db0ec2647 100644 --- a/lib/include/srslte/phy/phch/pucch.h +++ b/lib/include/srslte/phy/phch/pucch.h @@ -69,6 +69,7 @@ typedef struct SRSLTE_API { uint16_t ue_rnti; bool is_ue; + int16_t llr[SRSLTE_PUCCH3_NOF_BITS]; uint8_t bits_scram[SRSLTE_PUCCH_MAX_BITS]; cf_t d[SRSLTE_PUCCH_MAX_BITS / 2]; uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB]; diff --git a/lib/include/srslte/phy/phch/uci.h b/lib/include/srslte/phy/phch/uci.h index 2b600bda5..df953cfd3 100644 --- a/lib/include/srslte/phy/phch/uci.h +++ b/lib/include/srslte/phy/phch/uci.h @@ -76,6 +76,8 @@ SRSLTE_API int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t* q, SRSLTE_API void srslte_uci_encode_ack_sr_pucch3(uint8_t* data, uint32_t nof_bits, uint8_t output[32]); +SRSLTE_API int16_t srslte_uci_decode_ack_sr_pucch3(const int16_t llr[48], uint8_t* data); + SRSLTE_API int srslte_uci_cqi_init(srslte_uci_cqi_pusch_t* q); SRSLTE_API void srslte_uci_cqi_free(srslte_uci_cqi_pusch_t* q); diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index d7053dfdf..33717bbdf 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -562,12 +562,12 @@ static int encode_signal_format3(srslte_pucch_t* q, cf_t h; if (n < N_sf_0) { - h = w_n_oc_0[n] * cexpf(I * M_PI * floorf(n_cs_cell / 64) / 2); + 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) / 2); + 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]; } @@ -585,6 +585,69 @@ static int encode_signal_format3(srslte_pucch_t* q, 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]; + bzero(y_n, sizeof(cf_t) * 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_ack_sr_pucch3(q->llr, bits); + } else { + fprintf(stderr, "Error modulating PUCCH3 bits: rnti not set\n"); + return -1; + } + + return SRSLTE_SUCCESS; +} + static int encode_signal(srslte_pucch_t* q, srslte_ul_sf_cfg_t* sf, srslte_pucch_cfg_t* cfg, @@ -732,6 +795,10 @@ static bool decode_signal(srslte_pucch_t* q, 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; @@ -748,27 +815,33 @@ static void decode_bits(srslte_pucch_cfg_t* cfg, uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS], srslte_uci_value_t* uci_data) { - // If was looking for scheduling request, update value - if (cfg->uci_cfg.is_scheduling_request_tti) { - uci_data->scheduling_request = pucch_found; - } + if (cfg->format == SRSLTE_PUCCH_FORMAT_3) { + memcpy(uci_data->ack.ack_value, pucch_bits, srslte_uci_cfg_total_ack(&cfg->uci_cfg)); + uci_data->scheduling_request = pucch_bits[srslte_uci_cfg_total_ack(&cfg->uci_cfg)] == 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]; + // 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); - } + // 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 */ + if (cfg->uci_cfg.cqi.ri_len) { + uci_data->ri = pucch_bits[0]; /* Assume only one bit of RI */ + } } } diff --git a/lib/src/phy/phch/uci.c b/lib/src/phy/phch/uci.c index c60789dfc..31a9dafa6 100644 --- a/lib/src/phy/phch/uci.c +++ b/lib/src/phy/phch/uci.c @@ -52,6 +52,13 @@ static uint8_t M_basis_seq[32][11] = { {1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0}, {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0}, {1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }; +static const uint16_t M_basis_seq_b[32] = { + 0b11000000001, 0b11100000011, 0b10010010111, 0b10110000101, 0b11110001001, 0b11001011101, 0b10101010111, + 0b10011001101, 0b11011001011, 0b10111010011, 0b10100111011, 0b11100110101, 0b10010101111, 0b11010101011, + 0b10001101001, 0b11001111011, 0b11101110010, 0b10011100100, 0b11011111000, 0b10000110000, 0b10100010001, + 0b11010000011, 0b10001001101, 0b11101000111, 0b11111011110, 0b11000111001, 0b10110100110, 0b11110101110, + 0b10101110100, 0b10111111100, 0b11111111111, 0b10000000000, +}; static uint8_t M_basis_seq_pucch[20][13] = { {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, {1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0}, @@ -167,7 +174,7 @@ int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t* q, } } -void encode_cqi_pusch_block(uint8_t* data, uint32_t nof_bits, uint8_t output[32]) +void encode_cqi_pusch_block(const uint8_t* data, uint32_t nof_bits, uint8_t output[32]) { for (int i = 0; i < 32; i++) { output[i] = 0; @@ -182,6 +189,69 @@ void srslte_uci_encode_ack_sr_pucch3(uint8_t* data, uint32_t nof_bits, uint8_t o encode_cqi_pusch_block(data, nof_bits, output); } +int16_t srslte_uci_decode_ack_sr_pucch3(const int16_t llr[48], uint8_t* data) +{ + int16_t max_corr = 0; + int16_t max_data = 0; + + // Brute force all sequences backwards + for (int16_t guess = 2047; guess >= 0; guess--) { + int16_t corr = 0; + + for (uint8_t i = 0; i < 48; i++) { + // + uint16_t d = (uint16_t)guess & M_basis_seq_b[i % 32]; + d ^= (uint16_t)(d >> 8U); + d ^= (uint16_t)(d >> 4U); + d &= 0xf; + d = (0x6996U >> d) & 1U; + + corr += (d ? 1 : -1) * llr[i]; + } + + if (corr > max_corr) { + max_corr = corr; + max_data = guess; + } + } + + for (int8_t i = 0; i < 11; i++) { + data[i] = (uint8_t)(max_data >> (10U - i)) & 1U; + } + + return max_corr; +} + +void srslte_uci_decode_ack_sr_pucch3x(const int16_t llr[48], uint8_t* data, uint32_t nof_bits) +{ + // Limit maximum of bits + nof_bits = SRSLTE_MIN(nof_bits, 11); + nof_bits = SRSLTE_MAX(nof_bits, 1); + + int16_t max_corr = 0; + uint16_t max_data = 0; + for (uint16_t guess = 0; guess < (1U << (nof_bits - 1)); guess++) { + int16_t corr = 0; + + for (uint8_t i = 0; i < 48; i++) { + uint8_t d = 0; + for (uint8_t n = 0; n < nof_bits; n++) { + d ^= (uint8_t)((uint8_t)(guess >> n) & M_basis_seq[i % 32][n]); + } + corr += (d ? 1 : -1) * llr[i]; + } + + if (corr > max_corr) { + max_corr = corr; + max_data = guess; + } + } + + for (uint8_t i = 0; i < nof_bits; i++) { + data[i] = (uint8_t)(max_data >> i) & 1U; + } +} + void cqi_pusch_pregen(srslte_uci_cqi_pusch_t* q) { uint8_t word[11]; diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index c0800b37b..5f680337c 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -832,28 +832,6 @@ void srslte_ue_ul_pucch_resource_selection(srslte_cell_t* cell, // Get PUCCH Resources cfg->format = srslte_pucch_select_format(cfg, uci_cfg, cell->cp); cfg->n_pucch = get_npucch(cfg, uci_cfg, uci_value, cell); - - if (uci_value) { - if (cfg->format == SRSLTE_PUCCH_FORMAT_3) { - fprintf(stderr, "Warning: PUCCH3 under development\n"); - uint8_t* b = uci_value->ack.ack_value; - uint8_t temp[SRSLTE_UCI_MAX_ACK_BITS + 1]; - - uint32_t k = uci_cfg->ack[0].nof_acks; - for (; k < uci_cfg->ack[0].nof_acks; k++) { - temp[k] = (uint8_t)((b[k] == 1) ? 1 : 0); - } - memcpy(temp, uci_value->ack.ack_value, uci_cfg->ack[0].nof_acks); - if (uci_cfg->is_scheduling_request_tti) { - temp[uci_cfg->ack[0].nof_acks] = (uint8_t)(uci_value->scheduling_request ? 1 : 0); - k++; - } - srslte_uci_encode_ack_sr_pucch3(temp, k, b); - for (k = 32; k < SRSLTE_PUCCH3_NOF_BITS; k++) { - b[k] = b[k % 32]; - } - } - } } /* Choose PUCCH format as in Sec 10.1 of 36.213 and generate PUCCH signal diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index 14c615101..5ea484136 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -66,7 +66,7 @@ foreach (cell_n_prb 6 15 25 50 75 100) endforeach (allow_256 0 1) endforeach (cell_n_prb) -add_executable(pucch_cs_test pucch_cs_test.c) -target_link_libraries(pucch_cs_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -add_test(pucch_cs_test pucch_cs_test) +add_executable(pucch_ca_test pucch_ca_test.c) +target_link_libraries(pucch_ca_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +add_test(pucch_ca_test pucch_ca_test) diff --git a/lib/test/phy/pucch_cs_test.c b/lib/test/phy/pucch_ca_test.c similarity index 67% rename from lib/test/phy/pucch_cs_test.c rename to lib/test/phy/pucch_ca_test.c index 305936a6b..2cfc9c848 100644 --- a/lib/test/phy/pucch_cs_test.c +++ b/lib/test/phy/pucch_ca_test.c @@ -30,7 +30,10 @@ #include "srslte/srslte.h" -static int test_pucch_cs(uint32_t nof_prb, uint32_t nof_tb[SRSLTE_MAX_CARRIERS], uint16_t nof_carriers) +static int test_pucch_cs(srslte_ack_nack_feedback_mode_t ack_nack_feedback_mode, + uint32_t nof_prb, + const uint32_t nof_tb[SRSLTE_MAX_CARRIERS], + uint16_t nof_carriers) { srslte_pucch_cfg_t pucch_cfg = {}; uint16_t rnti = 0x1234; @@ -54,11 +57,11 @@ static int test_pucch_cs(uint32_t nof_prb, uint32_t nof_tb[SRSLTE_MAX_CARRIERS], srslte_pusch_data_t pusch_data = {}; // Basic default args - pucch_cfg.delta_pucch_shift = 1; // 1, 2, 3 - pucch_cfg.n_rb_2 = 1; // 0, 1, ..., 98 - pucch_cfg.N_cs = 1; // 0, 1, ..., 7 - pucch_cfg.N_pucch_1 = 1; // 0, 1, ..., 2047 - pucch_cfg.ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS; // Normal, CS, PUCCH3 + pucch_cfg.delta_pucch_shift = 1; // 1, 2, 3 + pucch_cfg.n_rb_2 = 1; // 0, 1, ..., 98 + pucch_cfg.N_cs = 1; // 0, 1, ..., 7 + pucch_cfg.N_pucch_1 = 1; // 0, 1, ..., 2047 + pucch_cfg.ack_nack_feedback_mode = ack_nack_feedback_mode; // Normal, CS, PUCCH3 // Set Channel Selection resources for (uint32_t i = 0, k = 6; i < SRSLTE_PUCCH_SIZE_AN_CS; i++) { @@ -92,13 +95,13 @@ static int test_pucch_cs(uint32_t nof_prb, uint32_t nof_tb[SRSLTE_MAX_CARRIERS], TESTASSERT(!srslte_enb_ul_add_rnti(&enb_ul, rnti)); // The test itself starts here - for (ul_sf.tti = 0; ul_sf.tti < (1U << (nof_carriers * 2)); ul_sf.tti++) { + for (ul_sf.tti = 0; ul_sf.tti < (1U << (nof_carriers * 2U)); ul_sf.tti++) { // Generate new data pusch_data.uci.ack.valid = true; for (uint32_t i = 0, k = 0; i < nof_carriers; i++) { for (uint32_t j = 0; j < nof_tb[i]; j++, k++) { - pusch_data.uci.ack.ack_value[k] = (ul_sf.tti >> k) & 0x01; + pusch_data.uci.ack.ack_value[k] = (ul_sf.tti >> k) & 1U; } } @@ -144,21 +147,27 @@ int main(int argc, char** argv) uint32_t nof_tb_1[SRSLTE_MAX_CARRIERS] = {1, 1, 1, 1, 0}; uint32_t nof_tb_2[SRSLTE_MAX_CARRIERS] = {2, 1, 1, 0, 0}; - uint32_t nof_tb_3[SRSLTE_MAX_CARRIERS] = {2, 2, 0, 0, 0}; - - TESTASSERT(!test_pucch_cs(6, nof_tb_1, 2)); - TESTASSERT(!test_pucch_cs(6, nof_tb_1, 3)); - TESTASSERT(!test_pucch_cs(6, nof_tb_1, 4)); - TESTASSERT(!test_pucch_cs(6, nof_tb_2, 3)); - TESTASSERT(!test_pucch_cs(6, nof_tb_2, 3)); - TESTASSERT(!test_pucch_cs(6, nof_tb_3, 2)); - - TESTASSERT(!test_pucch_cs(100, nof_tb_1, 2)); - TESTASSERT(!test_pucch_cs(100, nof_tb_1, 3)); - TESTASSERT(!test_pucch_cs(100, nof_tb_1, 4)); - TESTASSERT(!test_pucch_cs(100, nof_tb_2, 3)); - TESTASSERT(!test_pucch_cs(100, nof_tb_2, 3)); - TESTASSERT(!test_pucch_cs(100, nof_tb_3, 2)); + uint32_t nof_tb_3[SRSLTE_MAX_CARRIERS] = {2, 2, 2, 2, 2}; + + for (srslte_ack_nack_feedback_mode_t ack_nack_feedback_mode = SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS; + ack_nack_feedback_mode < SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_ERROR; + ack_nack_feedback_mode++) { + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_1, 2)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_1, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_1, 4)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_3, 2)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_1, 2)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_1, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_1, 4)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_2, 3)); + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 100, nof_tb_3, 2)); + if (ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3) { + TESTASSERT(!test_pucch_cs(ack_nack_feedback_mode, 6, nof_tb_3, 5)); + } + } printf("Ok\n");