From 53f6ac118e6f2fda0c26cf769fa74503713ea79e Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 27 Nov 2020 10:23:26 +0100 Subject: [PATCH] Initial PDCCH encode/decode --- .../srslte/phy/ch_estimation/dmrs_pdcch.h | 81 +++-- lib/include/srslte/phy/common/phy_common.h | 1 + lib/include/srslte/phy/common/phy_common_nr.h | 17 +- lib/include/srslte/phy/fec/polar/polar_code.h | 5 + lib/include/srslte/phy/phch/dci_nr.h | 19 +- lib/include/srslte/phy/phch/pdcch_nr.h | 71 ++++ lib/src/phy/ch_estimation/dmrs_pdcch.c | 123 ++++--- .../phy/ch_estimation/test/dmrs_pdcch_test.c | 84 ++--- lib/src/phy/phch/dci_nr.c | 4 +- lib/src/phy/phch/pdcch_nr.c | 319 ++++++++++++++++++ 10 files changed, 595 insertions(+), 129 deletions(-) diff --git a/lib/include/srslte/phy/ch_estimation/dmrs_pdcch.h b/lib/include/srslte/phy/ch_estimation/dmrs_pdcch.h index 59f9d2ad0..ffcff4b60 100644 --- a/lib/include/srslte/phy/ch_estimation/dmrs_pdcch.h +++ b/lib/include/srslte/phy/ch_estimation/dmrs_pdcch.h @@ -28,12 +28,18 @@ extern "C" { * * @attention it expects sf_symbols to be size SRSLTE_SLOT_LEN_RE_NR(cfg->carrier.nof_prb) * - * @param cfg Configuration that includes Carrier, CORESET, Search Space and PDCCH candidate - * @param slot_idx Slot index in the frame + * @param[in] carrier Provides carrier configuration + * @param[in] coreset Provides higher layer CORSET configuration + * @param[in] slot_cfg Provides DL slot configuration + * @param[in] dci_location Provides DCI location * @param sf_symbols is the resource grid where the DMRS resource elements will be written * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code */ -SRSLTE_API int srslte_dmrs_pdcch_put(const srslte_pdcch_cfg_nr_t* cfg, uint32_t slot_idx, cf_t* sf_symbols); +SRSLTE_API int srslte_dmrs_pdcch_put(const srslte_carrier_nr_t* carrier, + const srslte_coreset_t* coreset, + const srslte_dl_slot_cfg_t* slot_cfg, + const srslte_dci_location_t* dci_location, + cf_t* sf_symbols); /** * @brief PDCCH DMRS channel estimator object @@ -65,9 +71,6 @@ typedef struct SRSLTE_API { /// Channel estimates, size coreset_sz cf_t* ce; - - /// Stores latest slot index in frame - uint32_t slot_idx; } srslte_dmrs_pdcch_estimator_t; /** @@ -77,9 +80,9 @@ typedef struct SRSLTE_API { * \attention Initialization is required every time the carrier and/or CORESET changes. No free is required between * initializations. * - * @param q provides PDCCH DMRS estimator object - * @param carrier provides the required carrier configuration for some estimation - * @param coreset provides the required configuration for initialising the object + * @param[in,out] q provides PDCCH DMRS estimator object + * @param[in] carrier Provides carrier configuration + * @param[in] coreset Provides higher layer CORSET configuration * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code */ SRSLTE_API int srslte_dmrs_pdcch_estimator_init(srslte_dmrs_pdcch_estimator_t* q, @@ -101,13 +104,14 @@ SRSLTE_API void srslte_dmrs_pdcch_estimator_free(srslte_dmrs_pdcch_estimator_t* * The channel estimate measurements are performed at PDCCH candidate level through the function * srslte_dmrs_pdcch_estimator_measure. * - * @param cfg Configuration that includes Carrier, CORESET and the Search Space - * @param slot_idx Slot index in the frame - * @param sf_symbols Received resource grid. - * @param estimates CORESET Resource grid with the estimated channel + * @param[in,out] cfg Configuration that includes Carrier, CORESET and the Search Space + * @param[in] slot_cfg Slot index in the frame + * @param[in] sf_symbols Received resource grid. * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code */ -SRSLTE_API int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_idx, const cf_t* sf_symbols); +SRSLTE_API int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, + const srslte_dl_slot_cfg_t* slot_cfg, + const cf_t* sf_symbols); /** * @brief PDSCH DMRS measurement results @@ -129,28 +133,45 @@ typedef struct SRSLTE_API { } srslte_dmrs_pdcch_measure_t; /** - * @brief Performs PDCCH DMRS measurements of a given PDCCH candidate for an aggregation level + * @brief PDSCH DMRS Channel estimates structure * - * @note The measurement is useful for determining whether there is a PDCCH transmission in the given candidate. + * @see srslte_dmrs_pdcch_get_ce + */ +typedef struct SRSLTE_API { + /// Channel estimates, subtract 1 DMRS for every 4 RE, a maximum of L=16 and 6 PRB per CCE + cf_t ce[SRSLTE_PDCCH_MAX_RE]; + uint32_t nof_re; + float noise_var; +} srslte_dmrs_pdcch_ce_t; + +/** + * @brief Performs PDCCH DMRS measurements of a given DCI location + * + * @note The measurement is useful for determining whether there is a PDCCH transmission in the given DCI location. + * + * @param[in] q provides PDCCH DMRS estimator object + * @param[in] dci_location provides the search space + * @param[in] location Provides the aggregation level and CCE resource + * @param[out] measure Provides the structure for storing the channel estimate measurements + * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code + */ +SRSLTE_API int srslte_dmrs_pdcch_get_measure(const srslte_dmrs_pdcch_estimator_t* q, + const srslte_dci_location_t* location, + srslte_dmrs_pdcch_measure_t* measure); + +/** + * @brief Extracts PDCCH DMRS channel estimates of a given PDCCH candidate for an aggregation level * * @attention The provided aggregation level and candidate need to be according to the search space. * - * @param q provides PDCCH DMRS estimator object - * @param search_space provides the search space - * @param slot_idx Slot index in the frame - * @param aggregation_level Indicates the aggregation level of the candidate to examine - * @param candidate Indicates the candidate index of the available - * @param rnti Indicates the UE RNTI (only used for UE search space type) - * @param measure Provides the structure for storing the channel estimate measurements + * @param[in] q provides PDCCH DMRS estimator object + * @param[in] location Provides the aggregation level and CCE resource + * @param[out] ce Provides the structure for storing the channel estimates * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code */ -int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q, - const srslte_search_space_t* search_space, - uint32_t slot_idx, - uint32_t aggregation_level, - uint32_t candidate, - uint16_t rnti, - srslte_dmrs_pdcch_measure_t* measure); +SRSLTE_API int srslte_dmrs_pdcch_get_ce(const srslte_dmrs_pdcch_estimator_t* q, + const srslte_dci_location_t* location, + srslte_dmrs_pdcch_ce_t* ce); #ifdef __cplusplus } diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 1e073326d..7640beabe 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -62,6 +62,7 @@ extern "C" { #define SRSLTE_LTE_CRC24A 0x1864CFB #define SRSLTE_LTE_CRC24B 0X1800063 +#define SRSLTE_LTE_CRC24C 0X1B2B117 #define SRSLTE_LTE_CRC16 0x11021 #define SRSLTE_LTE_CRC8 0x19B diff --git a/lib/include/srslte/phy/common/phy_common_nr.h b/lib/include/srslte/phy/common/phy_common_nr.h index e07983a95..331296ddb 100644 --- a/lib/include/srslte/phy/common/phy_common_nr.h +++ b/lib/include/srslte/phy/common/phy_common_nr.h @@ -75,6 +75,11 @@ extern "C" { */ #define SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR 5 +/** + * @brief defines the maximum number of RE + */ +#define SRSLTE_PDCCH_MAX_RE ((SRSLTE_NRE - 3U) * (1U << (SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR - 1U)) * 6U) + /** * @brief defines the maximum number of candidates for a given Aggregation level */ @@ -236,18 +241,6 @@ typedef struct SRSLTE_API { uint32_t nof_candidates[SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR]; } srslte_search_space_t; -/** - * @brief PDCCH configuration - */ -typedef struct SRSLTE_API { - srslte_carrier_nr_t carrier; - uint16_t rnti; - srslte_coreset_t coreset; - srslte_search_space_t search_space; - uint32_t aggregation_level; - uint32_t n_cce; -} srslte_pdcch_cfg_nr_t; - /** * @brief Calculates the bandwidth of a given CORESET in physical resource blocks (PRB) . This function uses the * frequency domain resources bit-map for counting the number of PRB. diff --git a/lib/include/srslte/phy/fec/polar/polar_code.h b/lib/include/srslte/phy/fec/polar/polar_code.h index 4ca08df4d..5500eb676 100644 --- a/lib/include/srslte/phy/fec/polar/polar_code.h +++ b/lib/include/srslte/phy/fec/polar/polar_code.h @@ -44,6 +44,11 @@ static const uint16_t EMAX = 8192; */ static const uint16_t NMAX = 1024; +/*! + * \brief Base 2 logarithm of maximum codeword length + */ +static const uint16_t NMAX_LOG = 10; + /*! * \brief \f$log_2(EMAX)\f$ */ diff --git a/lib/include/srslte/phy/phch/dci_nr.h b/lib/include/srslte/phy/phch/dci_nr.h index caf818d3e..845aec18c 100644 --- a/lib/include/srslte/phy/phch/dci_nr.h +++ b/lib/include/srslte/phy/phch/dci_nr.h @@ -13,6 +13,7 @@ #ifndef SRSLTE_DCI_NR_H #define SRSLTE_DCI_NR_H +#include "dci.h" #include "srslte/phy/common/phy_common_nr.h" #include "srslte/phy/phch/pdsch_cfg_nr.h" @@ -25,11 +26,13 @@ typedef enum SRSLTE_API { } srslte_rnti_type_t; typedef struct SRSLTE_API { - uint8_t payload[32]; - srslte_rnti_type_t rnti_type; - uint32_t nof_bits; - srslte_dci_format_nr_t format; - uint16_t rnti; + srslte_dci_location_t location; + srslte_search_space_type_t search_space; + uint8_t payload[32]; + srslte_rnti_type_t rnti_type; + uint32_t nof_bits; + srslte_dci_format_nr_t format; + uint16_t rnti; } srslte_dci_msg_nr_t; typedef struct SRSLTE_API { @@ -59,7 +62,7 @@ typedef struct SRSLTE_API { // SI-RNTI specific fields uint32_t sii; ///< System information indicator -} srslte_dci_dl_t; +} srslte_dci_dl_nr_t; SRSLTE_API SRSLTE_API int srslte_dci_nr_format_1_0_sizeof(const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset, @@ -67,12 +70,12 @@ SRSLTE_API SRSLTE_API int srslte_dci_nr_format_1_0_sizeof(const srslte_carrier_n SRSLTE_API int srslte_dci_nr_format_1_0_pack(const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset, - const srslte_dci_dl_t* dci, + const srslte_dci_dl_nr_t* dci, srslte_dci_msg_nr_t* msg); SRSLTE_API int srslte_dci_nr_format_1_0_unpack(const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset, srslte_dci_msg_nr_t* msg, - srslte_dci_dl_t* dci); + srslte_dci_dl_nr_t* dci); #endif // SRSLTE_DCI_NR_H diff --git a/lib/include/srslte/phy/phch/pdcch_nr.h b/lib/include/srslte/phy/phch/pdcch_nr.h index 2df2a8b23..657aa7f08 100644 --- a/lib/include/srslte/phy/phch/pdcch_nr.h +++ b/lib/include/srslte/phy/phch/pdcch_nr.h @@ -21,8 +21,51 @@ #ifndef SRSLTE_PDCCH_NR_H #define SRSLTE_PDCCH_NR_H +#include "dci_nr.h" #include "srslte/config.h" +#include "srslte/phy/ch_estimation/dmrs_pdcch.h" #include "srslte/phy/common/phy_common_nr.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/polar/polar_code.h" +#include "srslte/phy/fec/polar/polar_decoder.h" +#include "srslte/phy/fec/polar/polar_encoder.h" +#include "srslte/phy/fec/polar/polar_rm.h" + +/** + * @brief PDCCH configuration initialization arguments + */ +typedef struct { + bool disable_simd; + bool measure_evm; +} srslte_pdcch_nr_args_t; + +/** + * @brief PDCCH Attributes and objects required to encode/decode NR PDCCH + */ +typedef struct SRSLTE_API { + bool is_tx; + srslte_polar_code_t code; + srslte_polar_encoder_t encoder; + srslte_polar_decoder_t decoder; + srslte_polar_rm_t rm; + srslte_carrier_nr_t carrier; + srslte_coreset_t coreset; + srslte_crc_t crc24c; + uint8_t* c; // Message bits with attached CRC + uint8_t* d; // encoded bits + uint8_t* f; // bits at the Rate matching output + cf_t* symbols; + srslte_modem_table_t modem_table; + srslte_evm_buffer_t* evm_buffer; +} srslte_pdcch_nr_t; + +/** + * @brief NR PDCCH decoder result + */ +typedef struct SRSLTE_API { + float evm; + bool crc; +} srslte_pdcch_nr_res_t; /** * @brief Function for generating NR PDCCH candidate locations n_cce for a given CORESET, search space, aggregation @@ -45,4 +88,32 @@ int srslte_pdcch_nr_locations_coreset(const srslte_coreset_t* coreset, uint32_t slot_idx, uint32_t locations[SRSLTE_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR]); +SRSLTE_API int srslte_pdcch_nr_init_tx(srslte_pdcch_nr_t* q, const srslte_pdcch_nr_args_t* args); + +SRSLTE_API int srslte_pdcch_nr_init_rx(srslte_pdcch_nr_t* q, const srslte_pdcch_nr_args_t* args); + +SRSLTE_API void srslte_pdcch_nr_init_free(srslte_pdcch_nr_t* q); + +SRSLTE_API int +srslte_pdcch_nr_set_carrier(srslte_pdcch_nr_t* q, const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset); + +SRSLTE_API int srslte_pdcch_nr_encode(srslte_pdcch_nr_t* q, const srslte_dci_msg_nr_t* dci_msg, cf_t* slot_symbols); + +/** + * @brief Decodes a DCI + * + * @param[in,out] q provides PDCCH encoder/decoder object + * @param[in] slot_symbols provides slot resource grid + * @param[in] ce provides channel estimated resource elements + * @param[in,out] dci_msg Provides with the DCI message location, RNTI, RNTI type and so on. Also, the message data + * buffer + * @param[out] res Provides the PDCCH result information + * @return SRSLTE_SUCCESS if the configurations are valid, otherwise it returns an SRSLTE_ERROR code + */ +SRSLTE_API int srslte_pdcch_nr_decode(srslte_pdcch_nr_t* q, + cf_t* slot_symbols, + srslte_dmrs_pdcch_ce_t* ce, + srslte_dci_msg_nr_t* dci_msg, + srslte_pdcch_nr_res_t* res); + #endif // SRSLTE_PDCCH_NR_H diff --git a/lib/src/phy/ch_estimation/dmrs_pdcch.c b/lib/src/phy/ch_estimation/dmrs_pdcch.c index 72eee4858..69e326d6c 100644 --- a/lib/src/phy/ch_estimation/dmrs_pdcch.c +++ b/lib/src/phy/ch_estimation/dmrs_pdcch.c @@ -22,10 +22,14 @@ static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uin (uint64_t)INT32_MAX); } -static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_pdcch_cfg_nr_t* cfg, uint32_t cinit, cf_t* sf_symbol) +static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_carrier_nr_t* carrier, + const srslte_coreset_t* coreset, + const srslte_dci_location_t* dci_location, + uint32_t cinit, + cf_t* sf_symbol) { - uint32_t L = 1U << cfg->aggregation_level; - uint32_t nof_freq_res = SRSLTE_MIN(cfg->carrier.nof_prb / 6, SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE); + uint32_t L = 1U << dci_location->L; + uint32_t nof_freq_res = SRSLTE_MIN(carrier->nof_prb / 6, SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE); // Initialise sequence for this symbol srslte_sequence_state_t sequence_state = {}; @@ -33,14 +37,14 @@ static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_pdcch_cfg_nr_t* cf uint32_t sequence_skip = 0; // Accumulates pilot locations to skip // Calculate Resource block indexes range, every CCE is 6 REG, 1 REG is 6 RE in resource blocks - uint32_t rb_coreset_idx_begin = (cfg->n_cce * 6) / cfg->coreset.duration; - uint32_t rb_coreset_idx_end = ((cfg->n_cce + L) * 6) / cfg->coreset.duration; + uint32_t rb_coreset_idx_begin = (dci_location->ncce * 6) / coreset->duration; + uint32_t rb_coreset_idx_end = ((dci_location->ncce + L) * 6) / coreset->duration; // CORESET Resource Block counter uint32_t rb_coreset_idx = 0; for (uint32_t i = 0; i < nof_freq_res; i++) { // Skip frequency resource if outside of the CORESET - if (!cfg->coreset.freq_resources[i]) { + if (!coreset->freq_resources[i]) { // Skip possible DMRS locations in this region sequence_skip += NOF_PILOTS_X_FREQ_RES; continue; @@ -97,37 +101,42 @@ static void dmrs_pdcch_put_symbol_noninterleaved(const srslte_pdcch_cfg_nr_t* cf } } -int srslte_dmrs_pdcch_put(const srslte_pdcch_cfg_nr_t* cfg, uint32_t slot_idx, cf_t* sf_symbols) +int srslte_dmrs_pdcch_put(const srslte_carrier_nr_t* carrier, + const srslte_coreset_t* coreset, + const srslte_dl_slot_cfg_t* slot_cfg, + const srslte_dci_location_t* dci_location, + cf_t* sf_symbols) { - if (cfg == NULL || sf_symbols == NULL) { + if (carrier == NULL || coreset == NULL || slot_cfg == NULL || dci_location == NULL || sf_symbols == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; } - if (cfg->coreset.mapping_type == srslte_coreset_mapping_type_interleaved) { + if (coreset->mapping_type == srslte_coreset_mapping_type_interleaved) { ERROR("Error interleaved CORESET mapping is not currently implemented\n"); return SRSLTE_ERROR; } - if (cfg->coreset.duration < SRSLTE_CORESET_DURATION_MIN || cfg->coreset.duration > SRSLTE_CORESET_DURATION_MAX) { + if (coreset->duration < SRSLTE_CORESET_DURATION_MIN || coreset->duration > SRSLTE_CORESET_DURATION_MAX) { ERROR("Error CORESET duration %d is out-of-bounds (%d,%d)\n", - cfg->coreset.duration, + coreset->duration, SRSLTE_CORESET_DURATION_MIN, SRSLTE_CORESET_DURATION_MAX); return SRSLTE_ERROR; } // Use cell id if the DMR scrambling id is not provided by higher layers - uint32_t n_id = cfg->carrier.id; - if (cfg->coreset.dmrs_scrambling_id_present) { - n_id = cfg->coreset.dmrs_scrambling_id; + uint32_t n_id = carrier->id; + if (coreset->dmrs_scrambling_id_present) { + n_id = coreset->dmrs_scrambling_id; } - for (uint32_t l = 0; l < cfg->coreset.duration; l++) { + for (uint32_t l = 0; l < coreset->duration; l++) { // Get Cin - uint32_t cinit = dmrs_pdcch_get_cinit(slot_idx, l, n_id); + uint32_t cinit = dmrs_pdcch_get_cinit(slot_cfg->idx, l, n_id); // Put data - dmrs_pdcch_put_symbol_noninterleaved(cfg, cinit, &sf_symbols[cfg->carrier.nof_prb * SRSLTE_NRE * l]); + dmrs_pdcch_put_symbol_noninterleaved( + carrier, coreset, dci_location, cinit, &sf_symbols[carrier->nof_prb * SRSLTE_NRE * l]); } return SRSLTE_SUCCESS; @@ -165,15 +174,15 @@ int srslte_dmrs_pdcch_estimator_init(srslte_dmrs_pdcch_estimator_t* q, srslte_interp_linear_free(&q->interpolator); } - if (srslte_interp_linear_init(&q->interpolator, srslte_coreset_get_bw(coreset) * 3, 4)) { - ERROR("Initiating interpolator\n"); - return SRSLTE_ERROR; - } - // Calculate new bandwidth and size uint32_t coreset_bw = srslte_coreset_get_bw(coreset); uint32_t coreset_sz = srslte_coreset_get_sz(coreset); + if (srslte_interp_linear_init(&q->interpolator, coreset_bw * 3, 4)) { + ERROR("Initiating interpolator\n"); + return SRSLTE_ERROR; + } + // Reallocate only if the CORESET size or bandwidth changed if (q->coreset_bw != coreset_bw || q->coreset_sz != coreset_sz) { // Iterate all possible symbols @@ -277,15 +286,14 @@ srslte_dmrs_pdcch_extract(srslte_dmrs_pdcch_estimator_t* q, uint32_t cinit, cons } } -int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_idx, const cf_t* sf_symbols) +int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, + const srslte_dl_slot_cfg_t* slot_cfg, + const cf_t* sf_symbols) { if (q == NULL || sf_symbols == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; } - // Saves slot index for posterior use - q->slot_idx = slot_idx; - // Use cell id if the DMR scrambling id is not provided by higher layers uint32_t n_id = q->carrier.id; if (q->coreset.dmrs_scrambling_id_present) { @@ -295,7 +303,7 @@ int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_i // Extract pilots for (uint32_t l = 0; l < q->coreset.duration; l++) { // Calculate PRN sequence initial state - uint32_t cinit = dmrs_pdcch_get_cinit(slot_idx, l, n_id); + uint32_t cinit = dmrs_pdcch_get_cinit(slot_cfg->idx, l, n_id); // Extract pilots least square estimates srslte_dmrs_pdcch_extract(q, cinit, &sf_symbols[l * q->carrier.nof_prb * SRSLTE_NRE], q->lse[l]); @@ -312,19 +320,15 @@ int srslte_dmrs_pdcch_estimate(srslte_dmrs_pdcch_estimator_t* q, uint32_t slot_i return SRSLTE_SUCCESS; } -int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q, - const srslte_search_space_t* search_space, - uint32_t slot_idx, - uint32_t aggregation_level, - uint32_t ncce, - uint16_t rnti, - srslte_dmrs_pdcch_measure_t* measure) +int srslte_dmrs_pdcch_get_measure(const srslte_dmrs_pdcch_estimator_t* q, + const srslte_dci_location_t* dci_location, + srslte_dmrs_pdcch_measure_t* measure) { - if (q == NULL || search_space == NULL || measure == NULL) { + if (q == NULL || dci_location == NULL || measure == NULL) { return SRSLTE_ERROR_INVALID_INPUTS; } - uint32_t L = 1U << aggregation_level; + uint32_t L = 1U << dci_location->L; if (q->coreset.mapping_type == srslte_coreset_mapping_type_interleaved) { ERROR("Error interleaved mapping not implemented\n"); return SRSLTE_ERROR; @@ -337,7 +341,7 @@ int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q, } // Get base pilot; - uint32_t pilot_idx = (ncce * 18) / q->coreset.duration; + uint32_t pilot_idx = (dci_location->ncce * 18) / q->coreset.duration; uint32_t nof_pilots = (L * 18) / q->coreset.duration; float rsrp = 0.0f; @@ -378,4 +382,47 @@ int srslte_dmrs_pdcch_get_measure(srslte_dmrs_pdcch_estimator_t* q, (float)SRSLTE_SUBC_SPACING_NR(q->carrier.numerology) * sync_err / (4.0e-6f * (float)q->coreset.duration); return SRSLTE_SUCCESS; -} \ No newline at end of file +} + +int srslte_dmrs_pdcch_get_ce(const srslte_dmrs_pdcch_estimator_t* q, + const srslte_dci_location_t* dci_location, + srslte_dmrs_pdcch_ce_t* ce) +{ + if (q == NULL || dci_location == NULL || ce == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + uint32_t L = 1U << dci_location->L; + if (q->coreset.mapping_type == srslte_coreset_mapping_type_interleaved) { + ERROR("Error interleaved mapping not implemented\n"); + return SRSLTE_ERROR; + } + + // Check that CORESET duration is not less than minimum + if (q->coreset.duration < SRSLTE_CORESET_DURATION_MIN) { + ERROR("Invalid CORESET duration\n"); + return SRSLTE_ERROR; + } + + // Calculate begin and end sub-carrier index for the selected candidate + uint32_t k_begin = (dci_location->ncce * SRSLTE_NRE * 6) / q->coreset.duration; + uint32_t k_end = k_begin + (L * 6 * SRSLTE_NRE) / q->coreset.duration; + + // Extract CE for PDCCH + uint32_t count = 0; + for (uint32_t l = 0; l < q->coreset.duration; l++) { + for (uint32_t k = k_begin; k < k_end; k++) { + if (k % 4 != 1) { + ce->ce[count++] = q->ce[q->coreset_bw * SRSLTE_NRE * l + k]; + } + } + } + + // Double check extracted RE match ideal count + ce->nof_re = (SRSLTE_NRE - 3) * 6 * L; + if (count != ce->nof_re) { + ERROR("Incorrect number of extracted resources (%d != %d)\n", count, ce->nof_re); + } + + return SRSLTE_SUCCESS; +} diff --git a/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c b/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c index 3e3119e78..7789e210a 100644 --- a/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c +++ b/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c @@ -19,9 +19,9 @@ #include #include -static srslte_carrier_nr_t carrier = {}; - -static uint16_t rnti = 0x1234; +static srslte_carrier_nr_t carrier = {}; +static srslte_dmrs_pdcch_ce_t pdcch_ce = {}; +static uint16_t rnti = 0x1234; void usage(char* prog) { @@ -56,31 +56,38 @@ static void parse_args(int argc, char** argv) } } -static int run_test(srslte_dmrs_pdcch_estimator_t* estimator, srslte_pdcch_cfg_nr_t* cfg, cf_t* sf_symbols, cf_t* h) +static int run_test(srslte_dmrs_pdcch_estimator_t* estimator, + const srslte_coreset_t* coreset, + const srslte_search_space_t* search_space, + uint32_t aggregation_level, + cf_t* sf_symbols, + srslte_dmrs_pdcch_ce_t* ce) { - for (uint32_t slot_idx = 0; slot_idx < SRSLTE_NSLOTS_PER_FRAME_NR(cfg->carrier.numerology); slot_idx++) { + srslte_dl_slot_cfg_t slot_cfg = {}; + + srslte_dci_location_t dci_location = {}; + dci_location.L = aggregation_level; + + for (slot_cfg.idx = 0; slot_cfg.idx < SRSLTE_NSLOTS_PER_FRAME_NR(carrier.numerology); slot_cfg.idx++) { uint32_t locations[SRSLTE_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; - int nof_locations = srslte_pdcch_nr_locations_coreset( - &cfg->coreset, &cfg->search_space, cfg->rnti, cfg->aggregation_level, slot_idx, locations); + int nof_locations = + srslte_pdcch_nr_locations_coreset(coreset, search_space, rnti, aggregation_level, slot_cfg.idx, locations); - TESTASSERT(nof_locations == cfg->search_space.nof_candidates[cfg->aggregation_level]); + TESTASSERT(nof_locations == search_space->nof_candidates[aggregation_level]); for (uint32_t candidate = 0; candidate < nof_locations; candidate++) { - cfg->n_cce = locations[candidate]; + dci_location.ncce = locations[candidate]; uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR; srslte_vec_cf_zero(sf_symbols, nof_re); - TESTASSERT(srslte_dmrs_pdcch_put(cfg, slot_idx, sf_symbols) == SRSLTE_SUCCESS); + TESTASSERT(srslte_dmrs_pdcch_put(&carrier, coreset, &slot_cfg, &dci_location, sf_symbols) == SRSLTE_SUCCESS); - TESTASSERT(srslte_dmrs_pdcch_estimate(estimator, slot_idx, sf_symbols) == SRSLTE_SUCCESS); + TESTASSERT(srslte_dmrs_pdcch_estimate(estimator, &slot_cfg, sf_symbols) == SRSLTE_SUCCESS); srslte_dmrs_pdcch_measure_t measure = {}; - TESTASSERT( - srslte_dmrs_pdcch_get_measure( - estimator, &cfg->search_space, slot_idx, cfg->aggregation_level, cfg->n_cce, cfg->rnti, &measure) == - SRSLTE_SUCCESS); + TESTASSERT(srslte_dmrs_pdcch_get_measure(estimator, &dci_location, &measure) == SRSLTE_SUCCESS); if (fabsf(measure.rsrp - 1.0f) > 1e-2) { printf("EPRE=%f; RSRP=%f; CFO=%f; SYNC_ERR=%f;\n", @@ -93,6 +100,12 @@ static int run_test(srslte_dmrs_pdcch_estimator_t* estimator, srslte_pdcch_cfg_n TESTASSERT(fabsf(measure.rsrp - 1.0f) < 1e-3f); TESTASSERT(fabsf(measure.cfo_hz) < 1e-3f); TESTASSERT(fabsf(measure.sync_error_us) < 1e-3f); + + TESTASSERT(srslte_dmrs_pdcch_get_ce(estimator, &dci_location, ce) == SRSLTE_SUCCESS); + float avg_pow = srslte_vec_avg_power_cf(ce->ce, ce->nof_re); + float avg_pow_err = fabsf(avg_pow - 1.0f); + TESTASSERT(ce->nof_re == ((SRSLTE_NRE - 3) * (1U << aggregation_level) * 6U)); + TESTASSERT(avg_pow_err < 0.1f); } } @@ -107,48 +120,45 @@ int main(int argc, char** argv) parse_args(argc, argv); - srslte_pdcch_cfg_nr_t cfg = {}; - srslte_dmrs_pdcch_estimator_t estimator = {}; + srslte_coreset_t coreset = {}; + srslte_search_space_t search_space = {}; + srslte_dmrs_pdcch_estimator_t estimator = {}; uint32_t nof_re = carrier.nof_prb * SRSLTE_NRE * SRSLTE_NSYMB_PER_SLOT_NR; cf_t* sf_symbols = srslte_vec_cf_malloc(nof_re); - cf_t* h = srslte_vec_cf_malloc(nof_re); uint32_t test_counter = 0; uint32_t test_passed = 0; - cfg.carrier = carrier; - cfg.rnti = rnti; - cfg.coreset.mapping_type = srslte_coreset_mapping_type_non_interleaved; + coreset.mapping_type = srslte_coreset_mapping_type_non_interleaved; uint32_t nof_frequency_resource = SRSLTE_MIN(SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6); for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources++) { uint32_t nof_freq_resources = 0; for (uint32_t i = 0; i < nof_frequency_resource; i++) { - uint32_t mask = ((frequency_resources >> i) & 1U); - cfg.coreset.freq_resources[i] = (mask == 1); + uint32_t mask = ((frequency_resources >> i) & 1U); + coreset.freq_resources[i] = (mask == 1); nof_freq_resources += mask; } - for (cfg.coreset.duration = 1; cfg.coreset.duration <= 3; cfg.coreset.duration++) { + for (coreset.duration = 1; coreset.duration <= 3; coreset.duration++) { - for (cfg.search_space.type = srslte_search_space_type_common; - cfg.search_space.type <= srslte_search_space_type_ue; - cfg.search_space.type++) { + for (search_space.type = srslte_search_space_type_common; search_space.type <= srslte_search_space_type_ue; + search_space.type++) { for (uint32_t i = 0; i < SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; i++) { - uint32_t L = 1U << i; - uint32_t nof_reg = cfg.coreset.duration * nof_freq_resources * 6; - uint32_t nof_cce = nof_reg / 6; - cfg.search_space.nof_candidates[i] = SRSLTE_MIN(nof_cce / L, SRSLTE_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); + uint32_t L = 1U << i; + uint32_t nof_reg = coreset.duration * nof_freq_resources * 6; + uint32_t nof_cce = nof_reg / 6; + search_space.nof_candidates[i] = SRSLTE_MIN(nof_cce / L, SRSLTE_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); } - for (cfg.aggregation_level = 0; cfg.aggregation_level < SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; - cfg.aggregation_level++) { + for (uint32_t aggregation_level = 0; aggregation_level < SRSLTE_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; + aggregation_level++) { - srslte_dmrs_pdcch_estimator_init(&estimator, &cfg.carrier, &cfg.coreset); + srslte_dmrs_pdcch_estimator_init(&estimator, &carrier, &coreset); - if (run_test(&estimator, &cfg, sf_symbols, h)) { + if (run_test(&estimator, &coreset, &search_space, aggregation_level, sf_symbols, &pdcch_ce)) { ERROR("Test %d failed\n", test_counter); } else { test_passed++; @@ -165,10 +175,6 @@ int main(int argc, char** argv) free(sf_symbols); } - if (h) { - free(h); - } - ret = test_passed == test_counter ? SRSLTE_SUCCESS : SRSLTE_ERROR; printf("%s, %d of %d test passed successfully.\n", ret ? "Failed" : "Passed", test_passed, test_counter); diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index 791c1df67..6e07cf10b 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -37,7 +37,7 @@ static int dci_nr_format_1_0_freq_resource_size(const srslte_carrier_nr_t* carri int srslte_dci_nr_format_1_0_pack(const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset, - const srslte_dci_dl_t* dci, + const srslte_dci_dl_nr_t* dci, srslte_dci_msg_nr_t* msg) { uint8_t* y = msg->payload; @@ -146,7 +146,7 @@ int srslte_dci_nr_format_1_0_pack(const srslte_carrier_nr_t* carrier, int srslte_dci_nr_format_1_0_unpack(const srslte_carrier_nr_t* carrier, const srslte_coreset_t* coreset, srslte_dci_msg_nr_t* msg, - srslte_dci_dl_t* dci) + srslte_dci_dl_nr_t* dci) { uint8_t* y = msg->payload; srslte_rnti_type_t rnti_type = msg->rnti_type; diff --git a/lib/src/phy/phch/pdcch_nr.c b/lib/src/phy/phch/pdcch_nr.c index de8350db6..5808c1718 100644 --- a/lib/src/phy/phch/pdcch_nr.c +++ b/lib/src/phy/phch/pdcch_nr.c @@ -100,3 +100,322 @@ int srslte_pdcch_nr_locations_coreset(const srslte_coreset_t* coreset, return nof_candidates; } + +static int pdcch_nr_init_common(srslte_pdcch_nr_t* q, const srslte_pdcch_nr_args_t* args) +{ + if (q == NULL || args == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + q->c = srslte_vec_u8_malloc(SRSLTE_PDCCH_MAX_RE * 2); + if (q->c == NULL) { + return SRSLTE_ERROR; + } + + q->d = srslte_vec_u8_malloc(SRSLTE_PDCCH_MAX_RE * 2); + if (q->d == NULL) { + return SRSLTE_ERROR; + } + + q->f = srslte_vec_u8_malloc(SRSLTE_PDCCH_MAX_RE * 2); + if (q->f == NULL) { + return SRSLTE_ERROR; + } + + q->symbols = srslte_vec_cf_malloc(SRSLTE_PDCCH_MAX_RE); + if (q->symbols == NULL) { + return SRSLTE_ERROR; + } + + if (srslte_crc_init(&q->crc24c, SRSLTE_LTE_CRC24C, 24) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_polar_code_init(&q->code) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + srslte_modem_table_lte(&q->modem_table, SRSLTE_MOD_QPSK); + + return SRSLTE_SUCCESS; +} + +int srslte_pdcch_nr_init_tx(srslte_pdcch_nr_t* q, const srslte_pdcch_nr_args_t* args) +{ + if (pdcch_nr_init_common(q, args) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + q->is_tx = true; + + srslte_polar_encoder_type_t encoder_type = + (args->disable_simd) ? SRSLTE_POLAR_ENCODER_PIPELINED : SRSLTE_POLAR_ENCODER_AVX2; + + if (srslte_polar_encoder_init(&q->encoder, encoder_type, NMAX_LOG) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_polar_rm_tx_init(&q->rm) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int srslte_pdcch_nr_init_rx(srslte_pdcch_nr_t* q, const srslte_pdcch_nr_args_t* args) +{ + if (pdcch_nr_init_common(q, args) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + srslte_polar_decoder_type_t decoder_type = + (args->disable_simd) ? SRSLTE_POLAR_DECODER_SSC_C : SRSLTE_POLAR_DECODER_SSC_C_AVX2; + + if (srslte_polar_decoder_init(&q->decoder, decoder_type, NMAX_LOG) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (srslte_polar_rm_rx_init_c(&q->rm) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + if (args->measure_evm) { + q->evm_buffer = srslte_evm_buffer_alloc(SRSLTE_PDCCH_MAX_RE * 2); + } + + return SRSLTE_SUCCESS; +} + +void srslte_pdcch_nr_init_free(srslte_pdcch_nr_t* q) +{ + if (q == NULL) { + return; + } + + srslte_polar_code_free(&q->code); + + if (q->is_tx) { + srslte_polar_encoder_free(&q->encoder); + srslte_polar_rm_tx_free(&q->rm); + } else { + srslte_polar_decoder_free(&q->decoder); + srslte_polar_rm_rx_free_c(&q->rm); + } + + if (q->c) { + free(q->c); + } + + if (q->d) { + free(q->d); + } + + if (q->f) { + free(q->f); + } + + srslte_modem_table_free(&q->modem_table); + + SRSLTE_MEM_ZERO(q, srslte_pdcch_nr_t, 1); +} + +int srslte_pdcch_nr_set_carrier(srslte_pdcch_nr_t* q, + const srslte_carrier_nr_t* carrier, + const srslte_coreset_t* coreset) +{ + if (q == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (carrier != NULL) { + q->carrier = *carrier; + } + + if (coreset != NULL) { + q->coreset = *coreset; + } + + return SRSLTE_SUCCESS; +} + +static uint32_t pdcch_nr_cp(const srslte_pdcch_nr_t* q, + const srslte_dci_location_t* dci_location, + cf_t* slot_grid, + cf_t* symbols, + bool put) +{ + uint32_t L = 1U << dci_location->L; + + // Calculate begin and end sub-carrier index for the selected candidate + uint32_t k_begin = (dci_location->ncce * SRSLTE_NRE * 6) / q->coreset.duration; + uint32_t k_end = k_begin + (L * 6 * SRSLTE_NRE) / q->coreset.duration; + + uint32_t count = 0; + + // Iterate over symbols + for (uint32_t l = 0; l < q->coreset.duration; l++) { + // Iterate over frequency resource groups + uint32_t k = 0; + for (uint32_t r = 0; r < SRSLTE_CORESET_FREQ_DOMAIN_RES_SIZE; r++) { + if (q->coreset.freq_resources[r]) { + for (uint32_t i = r * 6 * SRSLTE_NRE; i < (r + 1) * 6 * SRSLTE_NRE; i++, k++) { + if (k >= k_begin && k < k_end && k % 4 != 1) { + if (put) { + slot_grid[q->carrier.nof_prb * SRSLTE_NRE * l + i] = symbols[count++]; + } else { + symbols[count++] = slot_grid[q->carrier.nof_prb * SRSLTE_NRE * l + i]; + } + } + } + } + } + } + + return count; +} + +uint32_t pdcch_nr_c_init(const srslte_pdcch_nr_t* q, const srslte_dci_msg_nr_t* dci_msg) +{ + uint32_t n_id = (dci_msg->search_space == srslte_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) + ? q->coreset.dmrs_scrambling_id + : q->carrier.id; + uint32_t n_rnti = (dci_msg->search_space == srslte_search_space_type_ue && q->coreset.dmrs_scrambling_id_present) + ? dci_msg->rnti + : 0U; + return ((n_rnti << 16U) + n_id) & 0x7fffffffU; +} + +int srslte_pdcch_nr_encode(srslte_pdcch_nr_t* q, const srslte_dci_msg_nr_t* dci_msg, cf_t* slot_symbols) +{ + + if (q == NULL || dci_msg == NULL || slot_symbols == NULL) { + return SRSLTE_ERROR; + } + + // Calculate... + uint32_t K = dci_msg->nof_bits + 24U; // Payload size including CRC + uint32_t M = (1U << dci_msg->location.L) * (SRSLTE_NRE - 3U) * 6U; // Number of RE + uint32_t E = M * 2; // Number of Rate-Matched bits + + // Get polar code + if (srslte_polar_code_get(&q->code, K, E, 9U) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Copy DCI message + srslte_vec_u8_copy(q->c, dci_msg->payload, dci_msg->nof_bits); + + // Append CRC + srslte_crc_attach(&q->crc24c, q->c, dci_msg->nof_bits); + + // Unpack RNTI + uint8_t unpacked_rnti[16] = {}; + uint8_t* ptr = unpacked_rnti; + srslte_bit_unpack(dci_msg->rnti, &ptr, 16); + + // Scramble CRC with RNTI + srslte_vec_xor_bbb(unpacked_rnti, &q->c[K - 16], &q->c[K - 16], 16); + + // Encode bits + if (srslte_polar_encoder_encode(&q->encoder, q->c, q->d, q->code.n) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Rate matching + srslte_polar_rm_tx(&q->rm, q->d, q->f, q->code.n, E, K, 0); + + // Scrambling + srslte_sequence_apply_bit(q->f, q->f, E, pdcch_nr_c_init(q, dci_msg)); + + // Modulation + srslte_mod_modulate(&q->modem_table, q->f, q->symbols, E); + + // Put symbols in grid + uint32_t m = pdcch_nr_cp(q, &dci_msg->location, slot_symbols, q->symbols, true); + if (M != m) { + ERROR("Unmatch number of RE (%d != %d)\n", m, M); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int srslte_pdcch_nr_decode(srslte_pdcch_nr_t* q, + cf_t* slot_symbols, + srslte_dmrs_pdcch_ce_t* ce, + srslte_dci_msg_nr_t* dci_msg, + srslte_pdcch_nr_res_t* res) +{ + if (q == NULL || dci_msg == NULL || ce == NULL || slot_symbols == NULL || res == NULL) { + return SRSLTE_ERROR; + } + + // Calculate... + uint32_t K = dci_msg->nof_bits + 24U; // Payload size including CRC + uint32_t M = (1U << dci_msg->location.L) * (SRSLTE_NRE - 3U) * 6U; // Number of RE + uint32_t E = M * 2; // Number of Rate-Matched bits + + // Check number of estimates is correct + if (ce->nof_re != M) { + ERROR("Invalid number of channel estimates (%d != %d)\n", M, ce->nof_re); + return SRSLTE_ERROR; + } + + // Get polar code + if (srslte_polar_code_get(&q->code, K, E, 9U) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Get symbols from grid + uint32_t m = pdcch_nr_cp(q, &dci_msg->location, slot_symbols, q->symbols, false); + if (M != m) { + ERROR("Unmatch number of RE (%d != %d)\n", m, M); + } + + // Equalise + srslte_predecoding_single(q->symbols, ce->ce, q->symbols, NULL, M, 1.0f, ce->noise_var); + + // Demodulation + int8_t* llr = (int8_t*)q->f; + srslte_demod_soft_demodulate_b(SRSLTE_MOD_QPSK, q->symbols, llr, M); + + // Measure EVM if configured + if (q->evm_buffer != NULL) { + res->evm = srslte_evm_run_b(q->evm_buffer, &q->modem_table, q->symbols, llr, E); + } else { + res->evm = NAN; + } + + // Descrambling + srslte_sequence_apply_c(llr, llr, E, pdcch_nr_c_init(q, dci_msg)); + + // Un-rate matching + int8_t* d = (int8_t*)q->d; + if (srslte_polar_rm_rx_c(&q->rm, llr, d, E, q->code.n, K, 0) < SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Decode + if (srslte_polar_decoder_decode_c(&q->decoder, d, q->c, q->code.n, q->code.F_set, q->code.F_set_size) < + SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + + // Unpack RNTI + uint8_t unpacked_rnti[16] = {}; + uint8_t* ptr = unpacked_rnti; + srslte_bit_unpack(dci_msg->rnti, &ptr, 16); + + // De-Scramble CRC with RNTI + ptr = &q->c[K - 24]; + srslte_vec_xor_bbb(unpacked_rnti, &q->c[K - 16], &q->c[K - 16], 16); + + // Check CRC + uint32_t checksum1 = srslte_crc_checksum(&q->crc24c, q->c, dci_msg->nof_bits); + uint32_t checksum2 = srslte_bit_pack(&ptr, 24); + res->crc = checksum1 == checksum2; + + // Copy DCI message + srslte_vec_u8_copy(dci_msg->payload, q->c, dci_msg->nof_bits); + + return SRSLTE_SUCCESS; +} \ No newline at end of file