diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h index b1964f65e..c0751687f 100644 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_nr_interface_types.h @@ -161,6 +161,63 @@ struct phy_cfg_nr_t { pdcch.search_space[2].nof_candidates[4] = 0; pdcch.search_space[2].type = srsran_search_space_type_ue; pdcch.search_space_present[2] = true; + // pdsch-Config: setup (1) + // setup + // dmrs-DownlinkForPDSCH-MappingTypeA: setup (1) + // setup + // dmrs-AdditionalPosition: pos1 (1) + // tci-StatesToAddModList: 1 item + // Item 0 + // TCI-State + // tci-StateId: 0 + // qcl-Type1 + // referenceSignal: ssb (1) + // ssb: 0 + // qcl-Type: typeD (3) + // resourceAllocation: resourceAllocationType1 (1) + // rbg-Size: config1 (0) + // prb-BundlingType: staticBundling (0) + // staticBundling + // bundleSize: wideband (1) + // zp-CSI-RS-ResourceToAddModList: 1 item + // Item 0 + // ZP-CSI-RS-Resource + // zp-CSI-RS-ResourceId: 0 + // resourceMapping + // frequencyDomainAllocation: row4 (2) + // row4: 80 [bit length 3, 5 LSB pad bits, 100. .... + // decimal value 4] + // nrofPorts: p4 (2) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: fd-CDM2 (1) + // density: one (1) + // one: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // periodicityAndOffset: slots80 (9) + // slots80: 1 + // p-ZP-CSI-RS-ResourceSet: setup (1) + // setup + // zp-CSI-RS-ResourceSetId: 0 + // zp-CSI-RS-ResourceIdList: 1 item + // Item 0 + // ZP-CSI-RS-ResourceId: 0 + srsran_csi_rs_zp_resource_t zp_csi_rs_resource0 = {}; + zp_csi_rs_resource0.resource_mapping.row = srsran_csi_rs_resource_mapping_row_4; + zp_csi_rs_resource0.resource_mapping.frequency_domain_alloc[0] = true; + zp_csi_rs_resource0.resource_mapping.frequency_domain_alloc[1] = false; + zp_csi_rs_resource0.resource_mapping.frequency_domain_alloc[2] = false; + zp_csi_rs_resource0.resource_mapping.nof_ports = 4; + zp_csi_rs_resource0.resource_mapping.first_symbol_idx = 8; + zp_csi_rs_resource0.resource_mapping.cdm = srsran_csi_rs_cdm_fd_cdm2; + zp_csi_rs_resource0.resource_mapping.density = srsran_csi_rs_resource_mapping_density_one; + zp_csi_rs_resource0.resource_mapping.freq_band.start_rb = 0; + zp_csi_rs_resource0.resource_mapping.freq_band.nof_rb = 52; + zp_csi_rs_resource0.periodicity.period = 80; + zp_csi_rs_resource0.periodicity.offset = 1; + pdsch.p_zp_csi_rs_set.data[0] = zp_csi_rs_resource0; + pdsch.p_zp_csi_rs_set.count = 1; // pdsch-ConfigCommon: setup (1) // setup @@ -648,6 +705,230 @@ struct phy_cfg_nr_t { harq_ack.dl_data_to_ul_ack[6] = 11; harq_ack.nof_dl_data_to_ul_ack = 7; + // nzp-CSI-RS-ResourceToAddModList: 5 items + // Item 0 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 0 + // resourceMapping + // frequencyDomainAllocation: row2 (1) + // row2: 8000 [bit length 12, 4 LSB pad bits, 1000 0000 0000 .... decimal value 2048] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: one (1) + // one: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots80 (9) + // slots80: 1 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t nzp_resource_0 = {}; + nzp_resource_0.resource_mapping.row = srsran_csi_rs_resource_mapping_row_2; + nzp_resource_0.resource_mapping.frequency_domain_alloc[0] = true; + nzp_resource_0.resource_mapping.frequency_domain_alloc[1] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[2] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[3] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[4] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[5] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[6] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[7] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[8] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[9] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[10] = false; + nzp_resource_0.resource_mapping.frequency_domain_alloc[11] = false; + nzp_resource_0.resource_mapping.nof_ports = 1; + nzp_resource_0.resource_mapping.first_symbol_idx = 4; + nzp_resource_0.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + nzp_resource_0.resource_mapping.density = srsran_csi_rs_resource_mapping_density_one; + nzp_resource_0.resource_mapping.freq_band.start_rb = 0; + nzp_resource_0.resource_mapping.freq_band.nof_rb = 52; + nzp_resource_0.power_control_offset = 0; + nzp_resource_0.power_control_offset_ss = 0; + nzp_resource_0.scrambling_id = 0; + nzp_resource_0.periodicity.period = 80; + nzp_resource_0.periodicity.offset = 1; + + // Item 1 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 1 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t nzp_resource_1 = {}; + nzp_resource_1.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + nzp_resource_1.resource_mapping.frequency_domain_alloc[0] = false; + nzp_resource_1.resource_mapping.frequency_domain_alloc[1] = false; + nzp_resource_1.resource_mapping.frequency_domain_alloc[2] = false; + nzp_resource_1.resource_mapping.frequency_domain_alloc[3] = true; + nzp_resource_1.resource_mapping.nof_ports = 1; + nzp_resource_1.resource_mapping.first_symbol_idx = 4; + nzp_resource_1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + nzp_resource_1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + nzp_resource_1.resource_mapping.freq_band.start_rb = 0; + nzp_resource_1.resource_mapping.freq_band.nof_rb = 52; + nzp_resource_1.power_control_offset = 0; + nzp_resource_1.power_control_offset_ss = 0; + nzp_resource_1.scrambling_id = 0; + nzp_resource_1.periodicity.period = 40; + nzp_resource_1.periodicity.offset = 11; + // Item 2 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 2 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t nzp_resource_2 = {}; + nzp_resource_2.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + nzp_resource_2.resource_mapping.frequency_domain_alloc[0] = false; + nzp_resource_2.resource_mapping.frequency_domain_alloc[1] = false; + nzp_resource_2.resource_mapping.frequency_domain_alloc[2] = false; + nzp_resource_2.resource_mapping.frequency_domain_alloc[3] = true; + nzp_resource_2.resource_mapping.nof_ports = 1; + nzp_resource_2.resource_mapping.first_symbol_idx = 8; + nzp_resource_2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + nzp_resource_2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + nzp_resource_2.resource_mapping.freq_band.start_rb = 0; + nzp_resource_2.resource_mapping.freq_band.nof_rb = 52; + nzp_resource_2.power_control_offset = 0; + nzp_resource_2.power_control_offset_ss = 0; + nzp_resource_2.scrambling_id = 0; + nzp_resource_2.periodicity.period = 40; + nzp_resource_2.periodicity.offset = 11; + // Item 3 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 3 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t nzp_resource_3 = {}; + nzp_resource_3.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + nzp_resource_3.resource_mapping.frequency_domain_alloc[0] = false; + nzp_resource_3.resource_mapping.frequency_domain_alloc[1] = false; + nzp_resource_3.resource_mapping.frequency_domain_alloc[2] = false; + nzp_resource_3.resource_mapping.frequency_domain_alloc[3] = true; + nzp_resource_3.resource_mapping.nof_ports = 1; + nzp_resource_3.resource_mapping.first_symbol_idx = 4; + nzp_resource_3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + nzp_resource_3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + nzp_resource_3.resource_mapping.freq_band.start_rb = 0; + nzp_resource_3.resource_mapping.freq_band.nof_rb = 52; + nzp_resource_3.power_control_offset = 0; + nzp_resource_3.power_control_offset_ss = 0; + nzp_resource_3.scrambling_id = 0; + nzp_resource_3.periodicity.period = 40; + nzp_resource_3.periodicity.offset = 12; + // Item 4 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 4 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t nzp_resource_4 = {}; + nzp_resource_4.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + nzp_resource_4.resource_mapping.frequency_domain_alloc[0] = false; + nzp_resource_4.resource_mapping.frequency_domain_alloc[1] = false; + nzp_resource_4.resource_mapping.frequency_domain_alloc[2] = false; + nzp_resource_4.resource_mapping.frequency_domain_alloc[3] = true; + nzp_resource_4.resource_mapping.nof_ports = 1; + nzp_resource_4.resource_mapping.first_symbol_idx = 8; + nzp_resource_4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + nzp_resource_4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + nzp_resource_4.resource_mapping.freq_band.start_rb = 0; + nzp_resource_4.resource_mapping.freq_band.nof_rb = 52; + nzp_resource_4.power_control_offset = 0; + nzp_resource_4.power_control_offset_ss = 0; + nzp_resource_4.scrambling_id = 0; + nzp_resource_4.periodicity.period = 40; + nzp_resource_4.periodicity.offset = 12; + // zp-CSI-RS-ResourceSetToAddModList: 2 items + // Item 0 + // NZP-CSI-RS-ResourceSet + // nzp-CSI-ResourceSetId: 0 + // nzp-CSI-RS-Resources: 1 item + // Item 0 + // NZP-CSI-RS-ResourceId: 0 + pdsch.nzp_csi_rs_sets[0].data[0] = nzp_resource_0; + pdsch.nzp_csi_rs_sets[0].count = 1; + pdsch.nzp_csi_rs_sets[0].trs_info = false; + // Item 1 + // NZP-CSI-RS-ResourceSet + // nzp-CSI-ResourceSetId: 1 + // nzp-CSI-RS-Resources: 4 items + // Item 0 + // NZP-CSI-RS-ResourceId: 1 + // Item 1 + // NZP-CSI-RS-ResourceId: 2 + // Item 2 + // NZP-CSI-RS-ResourceId: 3 + // Item 3 + // NZP-CSI-RS-ResourceId: 4 + // trs-Info: true (0) + pdsch.nzp_csi_rs_sets[1].data[0] = nzp_resource_1; + pdsch.nzp_csi_rs_sets[1].data[1] = nzp_resource_2; + pdsch.nzp_csi_rs_sets[1].data[2] = nzp_resource_3; + pdsch.nzp_csi_rs_sets[1].data[3] = nzp_resource_4; + pdsch.nzp_csi_rs_sets[1].count = 4; + pdsch.nzp_csi_rs_sets[1].trs_info = true; // csi-ReportConfigToAddModList: 1 item // Item 0 // CSI-ReportConfig diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs.h b/lib/include/srsran/phy/ch_estimation/csi_rs.h index ab7219dd3..7f7e659c9 100644 --- a/lib/include/srsran/phy/ch_estimation/csi_rs.h +++ b/lib/include/srsran/phy/ch_estimation/csi_rs.h @@ -13,95 +13,35 @@ #ifndef SRSRAN_CSI_RS_H_ #define SRSRAN_CSI_RS_H_ -#include "srsran/config.h" -#include "srsran/phy/common/phy_common_nr.h" +#include "csi_rs_cfg.h" +#include "srsran/phy/phch/phch_cfg_nr.h" #include #include #include +/** + * @brief Number of frequency domain elements for Row 1 + */ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1 4 + +/** + * @brief Number of frequency domain elements for Row 2 + */ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2 12 + +/** + * @brief Number of frequency domain elements for Row 4 + */ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4 3 + +/** + * @brief Number of frequency domain elements for other rows + */ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX 12 - -typedef enum SRSRAN_API { - srsran_csi_rs_resource_mapping_row_1 = 0, - srsran_csi_rs_resource_mapping_row_2, - srsran_csi_rs_resource_mapping_row_4, - srsran_csi_rs_resource_mapping_row_other, -} srsran_csi_rs_resource_mapping_row_t; - -typedef enum SRSRAN_API { - srsran_csi_rs_resource_mapping_density_three = 0, - srsran_csi_rs_resource_mapping_density_dot5_even, - srsran_csi_rs_resource_mapping_density_dot5_odd, - srsran_csi_rs_resource_mapping_density_one, - srsran_csi_rs_resource_mapping_density_spare -} srsran_csi_rs_density_t; - -typedef enum SRSRAN_API { - srsran_csi_rs_cdm_nocdm = 0, - srsran_csi_rs_cdm_fd_cdm2, - srsran_csi_rs_cdm_cdm4_fd2_td2, - srsran_csi_rs_cdm_cdm8_fd2_td4 -} srsran_csi_rs_cdm_t; /** - * @brief Contains CSI-FrequencyOccupation flattened configuration + * @brief Measurement structure */ -typedef struct SRSRAN_API { - uint32_t start_rb; ///< PRB where this CSI resource starts in relation to common resource block #0 (CRB#0) on the - ///< common resource block grid. Only multiples of 4 are allowed (0, 4, ..., 274) - - uint32_t nof_rb; ///< Number of PRBs across which this CSI resource spans. Only multiples of 4 are allowed. The - ///< smallest configurable number is the minimum of 24 and the width of the associated BWP. If the - ///< configured value is larger than the width of the corresponding BWP, the UE shall assume that the - ///< actual CSI-RS bandwidth is equal to the width of the BWP. -} srsran_csi_rs_freq_occupation_t; - -/** - * @brief Contains CSI-ResourcePeriodicityAndOffset flattened configuration - */ -typedef struct SRSRAN_API { - uint32_t period; // 4,5,8,10,16,20,32,40,64,80,160,320,640 - uint32_t offset; // 0..period-1 -} srsran_csi_rs_period_and_offset_t; - -/** - * @brief Contains CSI-RS-ResourceMapping flattened configuration - */ -typedef struct SRSRAN_API { - srsran_csi_rs_resource_mapping_row_t row; - bool frequency_domain_alloc[SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX]; - uint32_t ports; // 1, 2, 4, 8, 12, 16, 24, 32 - uint32_t first_symbol_idx; // 0..13 - uint32_t first_symbol_idx2; // 2..12 (set to 0 for disabled) - srsran_csi_rs_cdm_t cdm; - srsran_csi_rs_density_t density; - srsran_csi_rs_freq_occupation_t freq_band; -} srsran_csi_rs_resource_mapping_t; - -/** - * @brief Contains NZP-CSI-RS-Resource flattened configuration - */ -typedef struct SRSRAN_API { - srsran_csi_rs_resource_mapping_t resource_mapping; - - float power_control_offset; // -8..15 dB - float power_control_offset_ss; // -3, 0, 3, 6 dB - - uint32_t scrambling_id; // 0..1023 - - srsran_csi_rs_period_and_offset_t periodicity; - -} srsran_csi_rs_nzp_resource_t; - -SRSRAN_API int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - cf_t* grid); - typedef struct SRSRAN_API { float rsrp; float rsrp_dB; @@ -113,6 +53,35 @@ typedef struct SRSRAN_API { uint32_t nof_re; } srsran_csi_rs_measure_t; +/** + * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot + * @remark Described in TS 36.211 section 7.4.1.5.3 Mapping to physical resources + * @param periodicity Periodicity configuration + * @param slot_cfg Slot configuration + * @return True if the periodicity configuration matches with the slot, false otherwise + */ +SRSRAN_API bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, + const srsran_slot_cfg_t* slot_cfg); + +/** + * @brief Adds to a RE pattern list the RE used in a CSI-RS resource for all CDM grops. This is intended for generating + * reserved RE pattern for PDSCH transmission. + * @param carrier Provides carrier configuration + * @param resource Provides a CSI-RS resource + * @param nof_resources Provides the number of ZP-CSI-RS resources + * @param l Symbol index in the slot + * @param[out] rvd_mask Provides the reserved mask + * @return SRSRAN_SUCCESS if the provided data is valid, and SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t* carrier, + const srsran_csi_rs_resource_mapping_t* resource, + srsran_re_pattern_list_t* re_pattern_list); + +SRSRAN_API int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + cf_t* grid); + SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h b/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h new file mode 100644 index 000000000..de52f62bc --- /dev/null +++ b/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 SRSRAN_CSI_RS_CFG_H +#define SRSRAN_CSI_RS_CFG_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" + +/** + * @brief Maximum number of ZP CSI-RS resources per set defined in + * - TS 38.214 clause 5.1.4.2 PDSCH resource mapping with RE level granularity + * - TS 38.331 constant maxNrofZP-CSI-RS-ResourcesPerSet + */ +#define SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET 16 + +/** + * @brief Maximum number of ZP CSI-RS Sets defined in TS 38.331 constant maxNrofZP-CSI-RS-ResourceSets + */ +#define SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS 16 + +/** + * @brief Maximum number of CSI-RS frequency domain allocation bits + */ +#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX 12 + +typedef enum SRSRAN_API { + srsran_csi_rs_resource_mapping_row_1 = 0, + srsran_csi_rs_resource_mapping_row_2, + srsran_csi_rs_resource_mapping_row_4, + srsran_csi_rs_resource_mapping_row_other, +} srsran_csi_rs_resource_mapping_row_t; + +typedef enum SRSRAN_API { + srsran_csi_rs_resource_mapping_density_three = 0, + srsran_csi_rs_resource_mapping_density_dot5_even, + srsran_csi_rs_resource_mapping_density_dot5_odd, + srsran_csi_rs_resource_mapping_density_one, + srsran_csi_rs_resource_mapping_density_spare +} srsran_csi_rs_density_t; + +typedef enum SRSRAN_API { + srsran_csi_rs_cdm_nocdm = 0, + srsran_csi_rs_cdm_fd_cdm2, + srsran_csi_rs_cdm_cdm4_fd2_td2, + srsran_csi_rs_cdm_cdm8_fd2_td4 +} srsran_csi_rs_cdm_t; + +/** + * @brief Contains CSI-FrequencyOccupation flattened configuration + */ +typedef struct SRSRAN_API { + uint32_t start_rb; ///< PRB where this CSI resource starts in relation to common resource block #0 (CRB#0) on the + ///< common resource block grid. Only multiples of 4 are allowed (0, 4, ..., 274) + + uint32_t nof_rb; ///< Number of PRBs across which this CSI resource spans. Only multiples of 4 are allowed. The + ///< smallest configurable number is the minimum of 24 and the width of the associated BWP. If the + ///< configured value is larger than the width of the corresponding BWP, the UE shall assume that the + ///< actual CSI-RS bandwidth is equal to the width of the BWP. +} srsran_csi_rs_freq_occupation_t; + +/** + * @brief Contains CSI-ResourcePeriodicityAndOffset flattened configuration + */ +typedef struct SRSRAN_API { + uint32_t period; // 4,5,8,10,16,20,32,40,64,80,160,320,640 + uint32_t offset; // 0..period-1 +} srsran_csi_rs_period_and_offset_t; + +/** + * @brief Contains CSI-RS-ResourceMapping flattened configuration + */ +typedef struct SRSRAN_API { + srsran_csi_rs_resource_mapping_row_t row; + bool frequency_domain_alloc[SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX]; + uint32_t nof_ports; // 1, 2, 4, 8, 12, 16, 24, 32 + uint32_t first_symbol_idx; // 0..13 + uint32_t first_symbol_idx2; // 2..12 (set to 0 for disabled) + srsran_csi_rs_cdm_t cdm; + srsran_csi_rs_density_t density; + srsran_csi_rs_freq_occupation_t freq_band; +} srsran_csi_rs_resource_mapping_t; + +/** + * @brief Contains TS 38.331 NZP-CSI-RS-Resource flattened configuration + */ +typedef struct SRSRAN_API { + srsran_csi_rs_resource_mapping_t resource_mapping; ///< CSI-RS time/frequency mapping + float power_control_offset; ///< -8..15 dB + float power_control_offset_ss; ///< -3, 0, 3, 6 dB + uint32_t scrambling_id; ///< 0..1023 + srsran_csi_rs_period_and_offset_t periodicity; ///< Periodicity +} srsran_csi_rs_nzp_resource_t; + +/** + * @brief Non-Zero-Power CSI resource set + */ +typedef struct SRSRAN_API { + srsran_csi_rs_nzp_resource_t data[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; ///< Resources + uint32_t count; ///< Set to zero for not present + bool trs_info; ///< Indicates that the antenna port for all NZP-CSI-RS resources in the CSI-RS resource set is same. +} srsran_csi_rs_nzp_set_t; + +/** + * @brief Contains TS 38.331 ZP-CSI-RS-Resource flattened configuration + */ +typedef struct { + srsran_csi_rs_resource_mapping_t resource_mapping; ///< CSI-RS time/frequency mapping + srsran_csi_rs_period_and_offset_t periodicity; +} srsran_csi_rs_zp_resource_t; + +/** + * @brief Zero-Power CSI resource set + */ +typedef struct SRSRAN_API { + srsran_csi_rs_zp_resource_t data[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; ///< Resources + uint32_t count; ///< Number of resources in the set +} srsran_csi_rs_zp_set_t; + +#endif // SRSRAN_CSI_RS_CFG_H diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h index a5aed07fe..93a031cf6 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h @@ -63,15 +63,14 @@ SRSRAN_API int srsran_dmrs_sch_get_symbols_idx(const srsran_dmrs_sch_cfg_t* dmrs uint32_t symbols_idx[SRSRAN_DMRS_SCH_MAX_SYMBOLS]); /** - * @brief Computes the sub-carrier indexes carrying DMRS - * + * @brief Computes the resource element pattern of resource elements reserved for DMRS * @param cfg PDSCH DMRS configuration provided by upper layers - * @param max_count is the number of sub-carriers to generate - * @param sc_idx is the destination pointer where the sub-carrier indexes are stored - * - * @return It returns the number of sub-carriers if inputs are valid, otherwise, it returns SRSRAN_ERROR code. + * @param[out] pattern Provides the RE pattern to fill + * @return SRSRAN_SUCCESS if computation is successful, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_dmrs_sch_get_sc_idx(const srsran_dmrs_sch_cfg_t* cfg, uint32_t max_count, uint32_t* sc_idx); +SRSRAN_API int srsran_dmrs_sch_rvd_re_pattern(const srsran_dmrs_sch_cfg_t* cfg, + const srsran_sch_grant_nr_t* grant, + srsran_re_pattern_t* pattern); /** * @brief Calculates the number of resource elements taken by a PDSCH-DMRS for a given PDSCH transmission @@ -143,8 +142,8 @@ SRSRAN_API int srsran_dmrs_sch_put_sf(srsran_dmrs_sch_t* q, * @attention Current implementation supports only type1 PDSCH DMRS (1 pilot every 2 RE) * * @param q DMRS-PDSCH object - * @param slot_cfg Slot configuration - * @param pdsch_cfg PDSCH configuration provided by upper layers + * @param slot Slot configuration + * @param cfg PDSCH configuration provided by upper layers * @param grant PDSCH information provided by a DCI * @param sf_symbols Received resource grid * @param[out] ce Channel estimates @@ -152,8 +151,8 @@ SRSRAN_API int srsran_dmrs_sch_put_sf(srsran_dmrs_sch_t* q, * @return it returns SRSRAN_ERROR code if an error occurs, otherwise it returns SRSRAN_SUCCESS */ SRSRAN_API int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_sch_cfg_nr_t* pdsch_cfg, + const srsran_slot_cfg_t* slot, + const srsran_sch_cfg_nr_t* cfg, const srsran_sch_grant_nr_t* grant, const cf_t* sf_symbols, srsran_chest_dl_res_t* chest_res); diff --git a/lib/include/srsran/phy/phch/csi_cfg.h b/lib/include/srsran/phy/phch/csi_cfg.h index 81cf88f88..40787c6be 100644 --- a/lib/include/srsran/phy/phch/csi_cfg.h +++ b/lib/include/srsran/phy/phch/csi_cfg.h @@ -26,6 +26,7 @@ * @brief Maximum number of CSI-RS resources defined in TS 38.331 maxNrofCSI-ResourceConfigurations */ #define SRSRAN_CSI_MAX_NOF_RESOURCES 112 + /** * @brief CSI report types defined in TS 38.331 CSI-ReportConfig */ diff --git a/lib/include/srsran/phy/phch/pdsch_nr.h b/lib/include/srsran/phy/phch/pdsch_nr.h index 0cd163afc..63190ee03 100644 --- a/lib/include/srsran/phy/phch/pdsch_nr.h +++ b/lib/include/srsran/phy/phch/pdsch_nr.h @@ -55,6 +55,8 @@ typedef struct SRSRAN_API { srsran_evm_buffer_t* evm_buffer; bool meas_time_en; uint32_t meas_time_us; + srsran_re_pattern_t dmrs_re_pattern; + uint32_t nof_rvd_re; } srsran_pdsch_nr_t; /** diff --git a/lib/include/srsran/phy/phch/phch_cfg_nr.h b/lib/include/srsran/phy/phch/phch_cfg_nr.h index b464907d9..95b49d934 100644 --- a/lib/include/srsran/phy/phch/phch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/phch_cfg_nr.h @@ -21,9 +21,17 @@ #ifndef SRSRAN_PHCH_CFG_NR_H #define SRSRAN_PHCH_CFG_NR_H +#include "srsran/phy/ch_estimation/csi_rs_cfg.h" #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/phch/sch_cfg_nr.h" #include "srsran/phy/phch/uci_cfg_nr.h" +#include "srsran/phy/utils/re_pattern.h" + +/** + * @brief Specifies the maximum number of ZP-CSI-RS resources configured per slot. It is not implicitly specified in the + * TS. + */ +#define SRSRAN_PHCH_CFG_MAX_NOF_ZP_CSI_RS_RES_PER_SLOT 16 /** * @brief PDSCH DMRS type @@ -203,6 +211,12 @@ typedef struct SRSRAN_API { srsran_sch_cfg_t sch_cfg; ///< Common shared channel parameters + /// PDSCH Periodic ZP-CSI-RS set + srsran_csi_rs_zp_set_t p_zp_csi_rs_set; + + /// PDSCH Periodic NZP-CSI-RS set, indexed by nzp-CSI-ResourceSetId + srsran_csi_rs_nzp_set_t nzp_csi_rs_sets[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS]; + /// PUSCH only srsran_beta_offsets_t beta_offsets; /// Semi-static only. float scaling; /// Indicates a scaling factor to limit the number of resource elements assigned to UCI on PUSCH. @@ -213,12 +227,12 @@ typedef struct SRSRAN_API { */ typedef struct SRSRAN_API { bool scrambling_id_present; - uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023) + uint32_t scambling_id; ///< Identifier used to initialize data scrambling (0-1023) - srsran_dmrs_sch_cfg_t dmrs; - srsran_sch_grant_nr_t grant; - - srsran_sch_cfg_t sch_cfg; ///< Common shared channel parameters + srsran_dmrs_sch_cfg_t dmrs; ///< DMRS configuration for this transmission + srsran_sch_grant_nr_t grant; ///< Actual SCH grant + srsran_sch_cfg_t sch_cfg; ///< Common shared channel parameters + srsran_re_pattern_list_t rvd_re; ///< Reserved resource elements, as pattern /// PUSCH only parameters srsran_uci_cfg_nr_t uci; ///< Uplink Control Information configuration diff --git a/lib/include/srsran/phy/phch/ra_nr.h b/lib/include/srsran/phy/phch/ra_nr.h index 546d20eca..344ef060d 100644 --- a/lib/include/srsran/phy/phch/ra_nr.h +++ b/lib/include/srsran/phy/phch/ra_nr.h @@ -90,12 +90,14 @@ SRSRAN_API int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, * Note: Only TypeA PDSCH mapping type is supported * * @param carrier Carrier information struct + * @param slot Slot configuration * @param pdsch_cfg PDSCH configuration indicated by higher layers * @param dci_dl DCI downlink (format 1_0 or 1_1) * @param pdsch_grant Generated PDSCH grant * @return 0 on success, -1 on error */ SRSRAN_API int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot, const srsran_sch_hl_cfg_nr_t* pdsch_cfg, const srsran_dci_dl_nr_t* dci_dl, srsran_sch_cfg_nr_t* cfg, diff --git a/lib/include/srsran/phy/utils/re_pattern.h b/lib/include/srsran/phy/utils/re_pattern.h new file mode 100644 index 000000000..00ab2a865 --- /dev/null +++ b/lib/include/srsran/phy/utils/re_pattern.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 SRSRAN_RE_PATTERN_H +#define SRSRAN_RE_PATTERN_H + +#include "../common/phy_common_nr.h" + +/** + * @brief Maximum number of elements in a pattern list + */ +#define SRSRAN_RE_PATTERN_LIST_SIZE 4 + +/** + * @brief Characterizes a pattern in frequency-time domain in an NR slot resource grid + */ +typedef struct SRSRAN_API { + uint32_t rb_begin; ///< RB where the pattern begins in frequency domain + uint32_t rb_end; ///< RB where the pattern ends in frequency domain (excluded) + uint32_t rb_stride; ///< RB index jump + bool sc[SRSRAN_NRE]; ///< Frequency-domain pattern + bool symbol[SRSRAN_NSYMB_PER_SLOT_NR]; ///< Indicates OFDM symbols where the pattern is present +} srsran_re_pattern_t; + +/** + * @brief List of RE patterns + */ +typedef struct SRSRAN_API { + srsran_re_pattern_t data[SRSRAN_RE_PATTERN_LIST_SIZE]; ///< Actual patterns + uint32_t count; ///< Number of RE patterns +} srsran_re_pattern_list_t; + +/** + * @brief Calculates if a pattern matches a RE given a symbol l and a subcarrier k + * @param list Provides a list of patterns + * @param l OFDM symbol index + * @param k Subcarrier index + * @return True if pattern is valid and there is a match, false otherwise + */ +SRSRAN_API bool srsran_re_pattern_to_mask(const srsran_re_pattern_list_t* list, uint32_t l, uint32_t k); + +/** + * @brief Calculates the pattern mask for an entire symbol from a RE pattern list + * @param list Provides a list of patterns + * @param l OFDM symbol index + * @param[out] mask Mask vector + * @return SRSRAN_SUCCESS if the mask is computed successfully, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_re_pattern_to_symbol_mask(const srsran_re_pattern_t* pattern, uint32_t l, bool* mask); + +/** + * @brief Calculates the pattern mask for an entire symbol from a RE pattern + * @param list Provides a list of patterns + * @param l OFDM symbol index + * @param[out] mask Mask vector + * @return SRSRAN_SUCCESS if the mask is computed successfully, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_re_pattern_list_to_symbol_mask(const srsran_re_pattern_list_t* list, uint32_t l, bool* mask); + +/** + * @brief Merges a pattern into the pattern list, it either merges subcarrier or symbol mask or simply appends a new + * pattern + * @param patterns Provides a list of patterns + * @param p Provides pattern to merge + * @return SRSRAN_SUCCESS if merging is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_re_pattern_merge(srsran_re_pattern_list_t* list, const srsran_re_pattern_t* p); + +/** + * @brief Checks collision between a RE pattern list and a RE pattern + * @param list Provides pattern list + * @param p Provides a pattern + * @return SRSLTE_SUCCESS if no collision is detected, SRSLTE_ERROR code otherwise + */ +SRSRAN_API int srsran_re_pattern_check_collision(const srsran_re_pattern_list_t* list, const srsran_re_pattern_t* p); + +/** + * @brief Initialises a given pattern list + * @param patterns Provides a list of patterns + */ +SRSRAN_API void srsran_re_pattern_reset(srsran_re_pattern_list_t* list); + +/** + * @brief Writes a RE pattern information into a string + * @param pattern Provides the pattern + * @param str Provides string pointer + * @param str_len Maximum string length + * @return The number of characters writen into the string + */ +SRSRAN_API uint32_t srsran_re_pattern_info(const srsran_re_pattern_t* pattern, char* str, uint32_t str_len); + +/** + * @brief Writes a RE pattern list information into a string + * @param pattern Provides the pattern list + * @param str Provides string pointer + * @param str_len Maximum string length + * @return The number of characters writen into the string + */ +SRSRAN_API uint32_t srsran_re_pattern_list_info(const srsran_re_pattern_list_t* pattern, char* str, uint32_t str_len); + +/** + * @brief Counts the number of RE in a transmission characterised by initial and final symbol indexes and a PRB mask. + * @param list RE pattern list + * @param symbol_begin First transmission symbol + * @param symbol_end Last (excluded) transmission symbol + * @param prb_mask Frequency domain resource block mask + * @return The number of RE occupied by the pattern list in the transmission + */ +SRSRAN_API uint32_t srsran_re_pattern_list_count(const srsran_re_pattern_list_t* list, + uint32_t symbol_begin, + uint32_t symbol_end, + const bool prb_mask[SRSRAN_MAX_PRB_NR]); + +#endif // SRSRAN_RE_PATTERN_H diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index 5ea401301..c0022c6a8 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -181,6 +181,7 @@ SRSRAN_API void srsran_vec_prod_sss(const int16_t* x, const int16_t* y, int16_t* // Negate sign (scrambling) SRSRAN_API void srsran_vec_neg_sss(const int16_t* x, const int16_t* y, int16_t* z, const uint32_t len); SRSRAN_API void srsran_vec_neg_bbb(const int8_t* x, const int8_t* y, int8_t* z, const uint32_t len); +SRSRAN_API void srsran_vec_neg_bb(const int8_t* x, int8_t* z, const uint32_t len); /* Dot-product */ SRSRAN_API cf_t srsran_vec_dot_prod_cfc(const cf_t* x, const float* y, const uint32_t len); diff --git a/lib/src/phy/ch_estimation/csi_rs.c b/lib/src/phy/ch_estimation/csi_rs.c index c035ebec3..6a8f1c9e4 100644 --- a/lib/src/phy/ch_estimation/csi_rs.c +++ b/lib/src/phy/ch_estimation/csi_rs.c @@ -12,23 +12,27 @@ #include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/common/sequence.h" +#include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" #include #include #include -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1 4 -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2 12 -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4 3 -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 -#define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX 12 +/** + * @brief Maximum number of subcarriers occupied by a CSI-RS resource as defined in TS 38.211 Table 7.4.1.5.3-1 + */ +#define CSI_RS_MAX_SUBC_PRB 4 -#define CSI_RS_MAX_CDM_GROUP 16 +/** + * @brief Maximum number of symbols occupied by a CSI-RS resource as defined in TS 38.211 Table 7.4.1.5.3-1 + */ +#define CSI_RS_MAX_SYMBOLS_SLOT 4 static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, uint32_t i) { uint32_t count = 0; uint32_t nof_freq_domain = 0; + uint32_t mul = 1; switch (resource->row) { case srsran_csi_rs_resource_mapping_row_1: nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; @@ -38,28 +42,32 @@ static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, u break; case srsran_csi_rs_resource_mapping_row_4: nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4; + mul = 4; break; case srsran_csi_rs_resource_mapping_row_other: nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER; + mul = 2; break; } for (uint32_t j = 0; j < nof_freq_domain; j++) { - if (resource->frequency_domain_alloc[j]) { + if (resource->frequency_domain_alloc[nof_freq_domain - 1 - j]) { count++; } if (count == i) { - return i; + return j * mul; } } + ERROR("Unhandled configuration"); return SRSRAN_ERROR; } // Table 7.4.1.5.3-1: CSI-RS locations within a slot static int csi_rs_location_get_k_list(const srsran_csi_rs_resource_mapping_t* resource, - uint32_t k_list[CSI_RS_MAX_CDM_GROUP]) + uint32_t j, + uint32_t k_list[CSI_RS_MAX_SUBC_PRB]) { int k0 = csi_rs_location_f(resource, 1); // int k1 = csi_rs_location_f(resource, 2); @@ -71,20 +79,49 @@ static int csi_rs_location_get_k_list(const srsran_csi_rs_resource_mapping_t* re } // Row 1 - if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->ports == 1 && - resource->density == srsran_csi_rs_resource_mapping_density_three && resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->nof_ports == 1 && + resource->density == srsran_csi_rs_resource_mapping_density_three && resource->cdm == srsran_csi_rs_cdm_nocdm && + j == 0) { k_list[0] = k0; k_list[1] = k0 + 4; k_list[2] = k0 + 8; return 3; } + // Row 2 + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && + resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->density == srsran_csi_rs_resource_mapping_density_one || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { + k_list[0] = k0; + return 1; + } + } + + // Row 4 + if (resource->row == srsran_csi_rs_resource_mapping_row_4 && resource->nof_ports == 4 && + resource->density == srsran_csi_rs_resource_mapping_density_one && resource->cdm == srsran_csi_rs_cdm_fd_cdm2) { + if (j == 0) { + k_list[0] = k0; + k_list[1] = k0 + 1; + return 2; + } + if (j == 1) { + k_list[0] = k0 + 2; + k_list[1] = k0 + 2 + 1; + return 2; + } + } + + ERROR("Unhandled configuration"); return SRSRAN_ERROR; } // Table 7.4.1.5.3-1: CSI-RS locations within a slot static int csi_rs_location_get_l_list(const srsran_csi_rs_resource_mapping_t* resource, - uint32_t l_list[CSI_RS_MAX_CDM_GROUP]) + uint32_t j, + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]) { uint32_t l0 = resource->first_symbol_idx; @@ -98,19 +135,44 @@ static int csi_rs_location_get_l_list(const srsran_csi_rs_resource_mapping_t* re // } // Row 1 - if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->ports == 1 && + if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->nof_ports == 1 && resource->density == srsran_csi_rs_resource_mapping_density_three && resource->cdm == srsran_csi_rs_cdm_nocdm) { l_list[0] = l0; return 1; } + // Row 2 + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && + resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->density == srsran_csi_rs_resource_mapping_density_one || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { + l_list[0] = l0; + return 1; + } + } + + // Row 4 + if (resource->row == srsran_csi_rs_resource_mapping_row_4 && resource->nof_ports == 4 && + resource->density == srsran_csi_rs_resource_mapping_density_one && resource->cdm == srsran_csi_rs_cdm_fd_cdm2) { + if (j == 0) { + l_list[0] = l0; + return 1; + } + if (j == 1) { + l_list[0] = l0; + return 1; + } + } + + ERROR("Unhandled configuration"); return SRSRAN_ERROR; } -uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - uint32_t l) +static uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + uint32_t l) { uint32_t n = SRSRAN_SLOT_NR_MOD(carrier->numerology, slot_cfg->idx); uint32_t n_id = resource->scrambling_id; @@ -118,7 +180,7 @@ uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, return ((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id) << 10UL) + n_id; } -bool srsran_csi_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg) +bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg) { if (periodicity == NULL || slot_cfg == NULL) { return false; @@ -133,6 +195,43 @@ bool srsran_csi_send(const srsran_csi_rs_period_and_offset_t* periodicity, const return n == 0; } +static int csi_rs_nof_cdm_groups(const srsran_csi_rs_resource_mapping_t* resource) +{ + if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->nof_ports == 1 && + resource->density == srsran_csi_rs_resource_mapping_density_three && resource->cdm == srsran_csi_rs_cdm_nocdm) { + return 1; + } + + // Row 1 + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && + resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->density == srsran_csi_rs_resource_mapping_density_one || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { + return 1; + } + } + + // Row 2 + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && + resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->density == srsran_csi_rs_resource_mapping_density_one || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || + resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { + return 1; + } + } + + // Row 3 + if (resource->row == srsran_csi_rs_resource_mapping_row_4 && resource->nof_ports == 4 && + resource->density == srsran_csi_rs_resource_mapping_density_one && resource->cdm == srsran_csi_rs_cdm_fd_cdm2) { + return 2; + } + + ERROR("Unhandled configuration"); + return SRSRAN_ERROR; +} + uint32_t csi_rs_count(srsran_csi_rs_density_t density, uint32_t nprb) { switch (density) { @@ -150,7 +249,7 @@ uint32_t csi_rs_count(srsran_csi_rs_density_t density, uint32_t nprb) return 0; } -uint32_t csi_rs_rb_begin(const srsran_carrier_nr_t* carrier, const srsran_csi_rs_resource_mapping_t* m) +static uint32_t csi_rs_rb_begin(const srsran_carrier_nr_t* carrier, const srsran_csi_rs_resource_mapping_t* m) { uint32_t ret = SRSRAN_MAX(carrier->start, m->freq_band.start_rb); @@ -162,12 +261,12 @@ uint32_t csi_rs_rb_begin(const srsran_carrier_nr_t* carrier, const srsran_csi_rs return ret; } -uint32_t csi_rs_rb_end(const srsran_carrier_nr_t* carrier, const srsran_csi_rs_resource_mapping_t* m) +static uint32_t csi_rs_rb_end(const srsran_carrier_nr_t* carrier, const srsran_csi_rs_resource_mapping_t* m) { return SRSRAN_MIN(carrier->start + carrier->nof_prb, m->freq_band.start_rb + m->freq_band.nof_rb); } -uint32_t csi_rs_rb_stride(const srsran_csi_rs_resource_mapping_t* m) +static uint32_t csi_rs_rb_stride(const srsran_csi_rs_resource_mapping_t* m) { uint32_t ret = 1; @@ -181,6 +280,65 @@ uint32_t csi_rs_rb_stride(const srsran_csi_rs_resource_mapping_t* m) return ret; } +int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t* carrier, + const srsran_csi_rs_resource_mapping_t* resource, + srsran_re_pattern_list_t* re_pattern_list) +{ + // Check inputs + if (resource == NULL || re_pattern_list == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Create temporal pattern + srsran_re_pattern_t pattern = {}; + pattern.rb_begin = csi_rs_rb_begin(carrier, resource); + pattern.rb_end = csi_rs_rb_end(carrier, resource); + pattern.rb_stride = csi_rs_rb_stride(resource); + + // Calculate number of CDM groups + int nof_cdm_groups = csi_rs_nof_cdm_groups(resource); + if (nof_cdm_groups < SRSRAN_SUCCESS) { + ERROR("Error getting number of CDM groups"); + return SRSRAN_ERROR; + } + + // Iterate over all CDM groups + for (int j = 0; j < nof_cdm_groups; j++) { + // Get SC indexes + uint32_t k_list[CSI_RS_MAX_SUBC_PRB] = {}; + int nof_k = csi_rs_location_get_k_list(resource, j, k_list); + if (nof_k < SRSRAN_SUCCESS) { + ERROR("Error getting indexes for CSI-RS"); + return SRSRAN_ERROR; + } + + // Fill subcarrier mask + for (int k = 0; k < nof_k; k++) { + pattern.sc[k_list[k]] = true; + } + + // Get OFDM symbol indexes + uint32_t l_list[CSI_RS_MAX_SUBC_PRB] = {}; + int nof_l = csi_rs_location_get_l_list(resource, j, l_list); + if (nof_l < SRSRAN_SUCCESS) { + ERROR("Error getting indexes for CSI-RS"); + return SRSRAN_ERROR; + } + + // Fill OFDM symbol mask + for (int l = 0; l < nof_l; l++) { + pattern.symbol[l_list[l]] = true; + } + + if (srsran_re_pattern_merge(re_pattern_list, &pattern) < SRSRAN_SUCCESS) { + ERROR("Error merging pattern"); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, @@ -190,14 +348,17 @@ int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR; } - uint32_t k_list[CSI_RS_MAX_CDM_GROUP]; - int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, k_list); + // Force CDM group to 0 + uint32_t j = 0; + + uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; + int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); if (nof_k <= 0) { return SRSRAN_ERROR; } - uint32_t l_list[CSI_RS_MAX_CDM_GROUP]; - int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, l_list); + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; + int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); if (nof_l <= 0) { return SRSRAN_ERROR; } @@ -261,14 +422,17 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR; } - uint32_t k_list[CSI_RS_MAX_CDM_GROUP]; - int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, k_list); + // Force CDM group to 0 + uint32_t j = 0; + + uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; + int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); if (nof_k <= 0) { return SRSRAN_ERROR; } - uint32_t l_list[CSI_RS_MAX_CDM_GROUP]; - int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, l_list); + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; + int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); if (nof_l <= 0) { return SRSRAN_ERROR; } diff --git a/lib/src/phy/ch_estimation/dmrs_pdcch.c b/lib/src/phy/ch_estimation/dmrs_pdcch.c index 161da0e86..800160a15 100644 --- a/lib/src/phy/ch_estimation/dmrs_pdcch.c +++ b/lib/src/phy/ch_estimation/dmrs_pdcch.c @@ -25,6 +25,7 @@ #define DMRS_PDCCH_INFO_TX(...) INFO("PDCCH DMRS Tx: " __VA_ARGS__) #define DMRS_PDCCH_INFO_RX(...) INFO("PDCCH DMRS Rx: " __VA_ARGS__) +#define DMRS_PDCCH_DEBUG_RX(...) DEBUG("PDCCH DMRS Rx: " __VA_ARGS__) /// @brief Enables interpolation at CCE frequency bandwidth to avoid interference with adjacent PDCCH DMRS #define DMRS_PDCCH_INTERPOLATE_GROUP 1 @@ -347,8 +348,6 @@ int srsran_dmrs_pdcch_estimate(srsran_dmrs_pdcch_estimator_t* q, // Calculate PRN sequence initial state uint32_t cinit = dmrs_pdcch_get_cinit(slot_idx, l, n_id); - DMRS_PDCCH_INFO_RX("n=%d; l=%d; cinit=%08x", slot_idx, l, cinit); - // Extract pilots least square estimates srsran_dmrs_pdcch_extract(q, cinit, &sf_symbols[l * q->carrier.nof_prb * SRSRAN_NRE], q->lse[l]); } @@ -410,8 +409,8 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q, float sync_err = 0.0f; cf_t corr[SRSRAN_CORESET_DURATION_MAX] = {}; for (uint32_t l = 0; l < q->coreset.duration; l++) { - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { - DMRS_PDCCH_INFO_RX("Measuring PDCCH l=%d; lse=", l); + if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + DMRS_PDCCH_DEBUG_RX("Measuring PDCCH l=%d; lse=", l); srsran_vec_fprint_c(stdout, &q->lse[l][pilot_idx], nof_pilots); } diff --git a/lib/src/phy/ch_estimation/dmrs_sch.c b/lib/src/phy/ch_estimation/dmrs_sch.c index 5114b14c3..3724b126a 100644 --- a/lib/src/phy/ch_estimation/dmrs_sch.c +++ b/lib/src/phy/ch_estimation/dmrs_sch.c @@ -11,6 +11,7 @@ */ #include "srsran/phy/ch_estimation/dmrs_sch.h" +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/common/sequence.h" #include #include @@ -399,26 +400,56 @@ int srsran_dmrs_sch_get_symbols_idx(const srsran_dmrs_sch_cfg_t* dmrs_cfg, return SRSRAN_ERROR; } -int srsran_dmrs_sch_get_sc_idx(const srsran_dmrs_sch_cfg_t* cfg, uint32_t max_count, uint32_t* sc_idx) +int srsran_dmrs_sch_rvd_re_pattern(const srsran_dmrs_sch_cfg_t* cfg, + const srsran_sch_grant_nr_t* grant, + srsran_re_pattern_t* pattern) { - int count = 0; - uint32_t delta = 0; + if (cfg == NULL || pattern == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + // Initialise pattern with zeros + SRSRAN_MEM_ZERO(pattern, srsran_re_pattern_t, 1); + + // Fill RB bounds + pattern->rb_begin = 0; + pattern->rb_end = SRSRAN_MAX_PRB_NR; + pattern->rb_stride = 1; + + // Fill subcarrier mask if (cfg->type == srsran_dmrs_sch_type_1) { - for (uint32_t n = 0; count < max_count; n += 4) { - for (uint32_t k_prime = 0; k_prime < 2 && count < max_count; k_prime++) { - sc_idx[count++] = n + 2 * k_prime + delta; + for (uint32_t n = 0; n < 3; n++) { + for (uint32_t k_prime = 0; k_prime < 2; k_prime++) { + for (uint32_t delta = 0; delta < grant->nof_dmrs_cdm_groups_without_data; delta++) { + pattern->sc[(4 * n + 2 * k_prime + delta) % SRSRAN_NRE] = true; + } } } } else { - for (uint32_t n = 0; count < max_count; n += 6) { - for (uint32_t k_prime = 0; k_prime < 2 && count < max_count; k_prime++) { - sc_idx[count++] = n + k_prime + delta; + for (uint32_t n = 0; n < 2; n++) { + for (uint32_t k_prime = 0; k_prime < 2; k_prime++) { + for (uint32_t delta = 0; delta < grant->nof_dmrs_cdm_groups_without_data; delta++) { + pattern->sc[(6 * n + k_prime + 2 * delta) % SRSRAN_NRE] = true; + } } } } - return count; + // Calculate OFDM symbols + uint32_t symbols[SRSRAN_DMRS_SCH_MAX_SYMBOLS]; + int nof_l = srsran_dmrs_sch_get_symbols_idx(cfg, grant, symbols); + if (nof_l < SRSRAN_SUCCESS) { + ERROR("Error calculating OFDM symbols"); + return SRSRAN_ERROR; + } + + // Set OFDM symbol mask + for (int i = 0; i < nof_l; i++) { + uint32_t l = symbols[i]; + pattern->symbol[l % SRSRAN_NSYMB_PER_SLOT_NR] = true; + } + + return SRSRAN_SUCCESS; } int srsran_dmrs_sch_get_N_prb(const srsran_dmrs_sch_cfg_t* dmrs_cfg, const srsran_sch_grant_nr_t* grant) @@ -666,26 +697,26 @@ static int srsran_dmrs_sch_get_symbol(srsran_dmrs_sch_t* q, } int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_sch_cfg_nr_t* pdsch_cfg, + const srsran_slot_cfg_t* slot, + const srsran_sch_cfg_nr_t* cfg, const srsran_sch_grant_nr_t* grant, const cf_t* sf_symbols, srsran_chest_dl_res_t* chest_res) { const uint32_t delta = 0; - if (q == NULL || slot_cfg == NULL || sf_symbols == NULL || chest_res == NULL) { + if (q == NULL || slot == NULL || sf_symbols == NULL || chest_res == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - const srsran_dmrs_sch_cfg_t* dmrs_cfg = &pdsch_cfg->dmrs; + const srsran_dmrs_sch_cfg_t* dmrs_cfg = &cfg->dmrs; cf_t* ce = q->temp; uint32_t symbol_sz = q->carrier.nof_prb * SRSRAN_NRE; // Symbol size in resource elements // Get symbols indexes uint32_t symbols[SRSRAN_DMRS_SCH_MAX_SYMBOLS] = {}; - int nof_symbols = srsran_dmrs_sch_get_symbols_idx(&pdsch_cfg->dmrs, grant, symbols); + int nof_symbols = srsran_dmrs_sch_get_symbols_idx(&cfg->dmrs, grant, symbols); if (nof_symbols <= SRSRAN_SUCCESS) { ERROR("Error getting symbol indexes"); return SRSRAN_ERROR; @@ -697,11 +728,11 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, for (uint32_t i = 0; i < nof_symbols; i++) { uint32_t l = symbols[i]; // Symbol index inside the slot - uint32_t cinit = srsran_dmrs_sch_seed( - &q->carrier, pdsch_cfg, grant, SRSRAN_SLOT_NR_MOD(q->carrier.numerology, slot_cfg->idx), l); + uint32_t cinit = + srsran_dmrs_sch_seed(&q->carrier, cfg, grant, SRSRAN_SLOT_NR_MOD(q->carrier.numerology, slot->idx), l); nof_pilots_x_symbol = srsran_dmrs_sch_get_symbol( - q, pdsch_cfg, grant, cinit, delta, &sf_symbols[symbol_sz * l], &q->pilot_estimates[nof_pilots_x_symbol * i]); + q, cfg, grant, cinit, delta, &sf_symbols[symbol_sz * l], &q->pilot_estimates[nof_pilots_x_symbol * i]); if (nof_pilots_x_symbol == 0) { ERROR("Error, no pilots extracted (i=%d, l=%d)", i, l); @@ -784,6 +815,26 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, symbol_idx++; } + // Initialise reserved mask + bool rvd_mask_wb[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + + // Compute reserved RE + if (srsran_re_pattern_list_to_symbol_mask(&cfg->rvd_re, l, rvd_mask_wb) < SRSRAN_SUCCESS) { + ERROR("Error generating reserved RE mask"); + return SRSRAN_ERROR; + } + + // Narrow reserved subcarriers to the ones used in the transmission + bool rvd_mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + for (uint32_t i = 0, k = 0; i < q->carrier.nof_prb; i++) { + if (grant->prb_idx[i]) { + for (uint32_t j = 0; j < SRSRAN_NRE; j++) { + rvd_mask[k++] = rvd_mask_wb[i * SRSRAN_NRE + j]; + } + } + } + + // Check if it s DMRS symbol if (symbols[symbol_idx] == l) { switch (dmrs_cfg->type) { case srsran_dmrs_sch_type_1: @@ -792,8 +843,9 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, continue; } for (uint32_t i = 1; i < nof_re_x_symbol; i += 2) { - chest_res->ce[0][0][count] = ce[i]; - count++; + if (!rvd_mask[i]) { + chest_res->ce[0][0][count++] = ce[i]; + } } break; case srsran_dmrs_sch_type_2: @@ -803,14 +855,20 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, } for (uint32_t i = grant->nof_dmrs_cdm_groups_without_data * 2; i < nof_re_x_symbol; i += 6) { uint32_t nof_re = (3 - grant->nof_dmrs_cdm_groups_without_data) * 2; - srsran_vec_cf_copy(&chest_res->ce[0][0][count], &ce[i], nof_re); - count += nof_re; + for (uint32_t j = 0; j < nof_re; j++) { + if (!rvd_mask[i + j]) { + chest_res->ce[0][0][count++] = ce[i + j]; + } + } } break; } } else { - srsran_vec_cf_copy(&chest_res->ce[0][0][count], ce, nof_re_x_symbol); - count += nof_re_x_symbol; + for (uint32_t i = 0; i < nof_re_x_symbol; i++) { + if (!rvd_mask[i]) { + chest_res->ce[0][0][count++] = ce[i]; + } + } } } // Set other values in the estimation result diff --git a/lib/src/phy/ch_estimation/test/CMakeLists.txt b/lib/src/phy/ch_estimation/test/CMakeLists.txt index 8e2fcca68..a272e84b5 100644 --- a/lib/src/phy/ch_estimation/test/CMakeLists.txt +++ b/lib/src/phy/ch_estimation/test/CMakeLists.txt @@ -77,7 +77,7 @@ add_lte_test(chest_test_sl_psbch chest_test_sl) add_executable(dmrs_pdsch_test dmrs_pdsch_test.c) target_link_libraries(dmrs_pdsch_test srsran_phy) -add_lte_test(dmrs_pdsch_test dmrs_pdsch_test) +add_nr_test(dmrs_pdsch_test dmrs_pdsch_test) ######################################################################## @@ -87,7 +87,7 @@ add_lte_test(dmrs_pdsch_test dmrs_pdsch_test) add_executable(dmrs_pdcch_test dmrs_pdcch_test.c) target_link_libraries(dmrs_pdcch_test srsran_phy) -add_lte_test(dmrs_pdcch_test dmrs_pdcch_test) +add_nr_test(dmrs_pdcch_test dmrs_pdcch_test) ######################################################################## @@ -97,5 +97,15 @@ add_lte_test(dmrs_pdcch_test dmrs_pdcch_test) add_executable(csi_rs_test csi_rs_test.c) target_link_libraries(csi_rs_test srsran_phy) -add_lte_test(csi_rs_test csi_rs_test -o 3 -S 0 -L 150 -f 3 -p 15) +add_nr_test(csi_rs_test csi_rs_test -o 3 -S 0 -L 150 -f 3 -p 15) + + +######################################################################## +# NR CSI RS Pattern test +######################################################################## + +add_executable(csi_rs_pattern_test csi_rs_pattern_test.c) +target_link_libraries(csi_rs_pattern_test srsran_phy) + +add_nr_test(csi_rs_pattern_test csi_rs_pattern_test) diff --git a/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c b/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c new file mode 100644 index 000000000..2bd11ff82 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c @@ -0,0 +1,229 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 "srsran/common/test_common.h" +#include "srsran/phy/ch_estimation/csi_rs.h" + +static srsran_carrier_nr_t carrier = {}; + +static int test_row1() +{ + for (uint32_t k = 0; k < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; k++) { + // Create CSI-RS mapping + srsran_csi_rs_resource_mapping_t m; // Dont initialise for detecting not initialised memory + m.row = srsran_csi_rs_resource_mapping_row_1; + for (uint32_t j = 0; j < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; j++) { + m.frequency_domain_alloc[j] = (k == j); + } + m.nof_ports = 1; + m.first_symbol_idx = 0; + m.cdm = srsran_csi_rs_cdm_nocdm; + m.density = srsran_csi_rs_resource_mapping_density_three; + m.freq_band.start_rb = carrier.start; + m.freq_band.nof_rb = carrier.nof_prb; + + // Create Pattern list and initialise + srsran_re_pattern_list_t patterns; + srsran_re_pattern_reset(&patterns); + + // Generate pattern list from CSI-RS mapping + TESTASSERT(srsran_csi_rs_append_resource_to_pattern(&carrier, &m, &patterns) == SRSRAN_SUCCESS); + + // Assert generated pattern + TESTASSERT(patterns.count == 1); + TESTASSERT(patterns.data[0].rb_begin == m.freq_band.start_rb); + TESTASSERT(patterns.data[0].rb_end == m.freq_band.start_rb + m.freq_band.nof_rb); + TESTASSERT(patterns.data[0].rb_stride == 1); + for (uint32_t j = 0; j < SRSRAN_NRE; j++) { + TESTASSERT(patterns.data[0].sc[j] == + (j % SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1 == (SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1 - 1 - k))); + } + for (uint32_t j = 0; j < SRSRAN_NSYMB_PER_SLOT_NR; j++) { + TESTASSERT(patterns.data[0].symbol[j] == (j == m.first_symbol_idx)); + } + } + + return SRSRAN_SUCCESS; +} + +static int test_row2() +{ + for (srsran_csi_rs_density_t density = srsran_csi_rs_resource_mapping_density_dot5_even; + density <= srsran_csi_rs_resource_mapping_density_one; + density++) { + for (uint32_t k = 0; k < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2; k++) { + // Create CSI-RS mapping + srsran_csi_rs_resource_mapping_t m; // Dont initialise for detecting not initialised memory + m.row = srsran_csi_rs_resource_mapping_row_2; + for (uint32_t j = 0; j < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2; j++) { + m.frequency_domain_alloc[j] = (k == j); + } + m.nof_ports = 1; + m.first_symbol_idx = 0; + m.cdm = srsran_csi_rs_cdm_nocdm; + m.density = density; + m.freq_band.start_rb = carrier.start; + m.freq_band.nof_rb = carrier.nof_prb; + + // Create Pattern list and initialise + srsran_re_pattern_list_t patterns; + srsran_re_pattern_reset(&patterns); + + // Generate pattern list from CSI-RS mapping + TESTASSERT(srsran_csi_rs_append_resource_to_pattern(&carrier, &m, &patterns) == SRSRAN_SUCCESS); + + // Assert generated pattern + uint32_t rb_stride = (density == srsran_csi_rs_resource_mapping_density_one) ? 1 : 2; + uint32_t rb_start = carrier.start; + if ((rb_start % 2 == 0 && density == srsran_csi_rs_resource_mapping_density_dot5_odd) || + (rb_start % 2 == 1 && density == srsran_csi_rs_resource_mapping_density_dot5_even)) { + rb_start++; + } + TESTASSERT(patterns.count == 1); + TESTASSERT(patterns.data[0].rb_begin == rb_start); + TESTASSERT(patterns.data[0].rb_end == m.freq_band.start_rb + m.freq_band.nof_rb); + TESTASSERT(patterns.data[0].rb_stride == rb_stride); + for (uint32_t j = 0; j < SRSRAN_NRE; j++) { + TESTASSERT(patterns.data[0].sc[j] == (j == (SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2 - 1 - k))); + } + for (uint32_t j = 0; j < SRSRAN_NSYMB_PER_SLOT_NR; j++) { + TESTASSERT(patterns.data[0].symbol[j] == (j == m.first_symbol_idx)); + } + } + } + + return SRSRAN_SUCCESS; +} + +static int test_row4() +{ + for (uint32_t k = 0; k < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4; k++) { + // Create CSI-RS mapping + srsran_csi_rs_resource_mapping_t m; // Dont initialise for detecting not initialised memory + m.row = srsran_csi_rs_resource_mapping_row_4; + for (uint32_t j = 0; j < SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4; j++) { + m.frequency_domain_alloc[j] = (k == j); + } + m.nof_ports = 4; + m.first_symbol_idx = 0; + m.cdm = srsran_csi_rs_cdm_fd_cdm2; + m.density = srsran_csi_rs_resource_mapping_density_one; + m.freq_band.start_rb = carrier.start; + m.freq_band.nof_rb = carrier.nof_prb; + + // Create Pattern list and initialise + srsran_re_pattern_list_t patterns; + srsran_re_pattern_reset(&patterns); + + // Generate pattern list from CSI-RS mapping + TESTASSERT(srsran_csi_rs_append_resource_to_pattern(&carrier, &m, &patterns) == SRSRAN_SUCCESS); + + // Assert generated pattern + TESTASSERT(patterns.count == 1); + TESTASSERT(patterns.data[0].rb_begin == m.freq_band.start_rb); + TESTASSERT(patterns.data[0].rb_end == m.freq_band.start_rb + m.freq_band.nof_rb); + TESTASSERT(patterns.data[0].rb_stride == 1); + for (uint32_t j = 0; j < SRSRAN_NRE; j++) { + uint32_t k_begin = (SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4 - 1 - k) * 4; + uint32_t k_end = k_begin + 4; + TESTASSERT(patterns.data[0].sc[j] == (j >= k_begin && j < k_end)); + } + for (uint32_t j = 0; j < SRSRAN_NSYMB_PER_SLOT_NR; j++) { + TESTASSERT(patterns.data[0].symbol[j] == (j == m.first_symbol_idx)); + } + } + + return SRSRAN_SUCCESS; +} + +static int test_mix() +{ + srsran_csi_rs_resource_mapping_t resource0 = {}; + resource0.row = srsran_csi_rs_resource_mapping_row_4; + resource0.frequency_domain_alloc[0] = true; + resource0.frequency_domain_alloc[1] = false; + resource0.frequency_domain_alloc[2] = false; + resource0.nof_ports = 4; + resource0.first_symbol_idx = 8; + resource0.cdm = srsran_csi_rs_cdm_fd_cdm2; + resource0.density = srsran_csi_rs_resource_mapping_density_one; + resource0.freq_band.start_rb = 0; + resource0.freq_band.nof_rb = 52; + + srsran_csi_rs_resource_mapping_t resource1 = {}; + resource1.row = srsran_csi_rs_resource_mapping_row_2; + resource1.frequency_domain_alloc[0] = true; + resource1.frequency_domain_alloc[1] = false; + resource1.frequency_domain_alloc[2] = false; + resource1.frequency_domain_alloc[3] = false; + resource1.frequency_domain_alloc[4] = false; + resource1.frequency_domain_alloc[5] = false; + resource1.frequency_domain_alloc[6] = false; + resource1.frequency_domain_alloc[7] = false; + resource1.frequency_domain_alloc[8] = false; + resource1.frequency_domain_alloc[9] = false; + resource1.frequency_domain_alloc[10] = false; + resource1.frequency_domain_alloc[11] = false; + resource1.nof_ports = 1; + resource1.first_symbol_idx = 4; + resource1.cdm = srsran_csi_rs_cdm_nocdm; + resource1.density = srsran_csi_rs_resource_mapping_density_one; + resource1.freq_band.start_rb = 0; + resource1.freq_band.nof_rb = 52; + + // Initialise pattern list + srsran_re_pattern_list_t patterns; + srsran_re_pattern_reset(&patterns); + + // Generate pattern list from CSI-RS mapping + TESTASSERT(srsran_csi_rs_append_resource_to_pattern(&carrier, &resource0, &patterns) == SRSRAN_SUCCESS); + TESTASSERT(srsran_csi_rs_append_resource_to_pattern(&carrier, &resource1, &patterns) == SRSRAN_SUCCESS); + + // Assert generated pattern + TESTASSERT(patterns.count == 2); + + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { + bool mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + + TESTASSERT(srsran_re_pattern_list_to_symbol_mask(&patterns, l, mask) == SRSRAN_SUCCESS); + + if (l == resource0.first_symbol_idx) { + for (uint32_t k = 0; k < SRSRAN_NRE * SRSRAN_MAX_PRB_NR; k++) { + TESTASSERT(mask[k] == ((k < 52 * SRSRAN_NRE) && (k % SRSRAN_NRE >= 8))); + } + } else if (l == resource1.first_symbol_idx) { + for (uint32_t k = 0; k < SRSRAN_NRE * SRSRAN_MAX_PRB_NR; k++) { + TESTASSERT(mask[k] == ((k < 52 * SRSRAN_NRE) && (k % SRSRAN_NRE == 11))); + } + } else { + for (uint32_t k = 0; k < SRSRAN_NRE * SRSRAN_MAX_PRB_NR; k++) { + TESTASSERT(mask[k] == false); + } + } + } + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + // Initialise carrier + carrier.start = 0; + carrier.nof_prb = 52; + + TESTASSERT(test_row1() == SRSRAN_SUCCESS); + TESTASSERT(test_row2() == SRSRAN_SUCCESS); + TESTASSERT(test_row4() == SRSRAN_SUCCESS); + TESTASSERT(test_mix() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/ch_estimation/test/csi_rs_test.c b/lib/src/phy/ch_estimation/test/csi_rs_test.c index c3cacb175..2a8b7da05 100644 --- a/lib/src/phy/ch_estimation/test/csi_rs_test.c +++ b/lib/src/phy/ch_estimation/test/csi_rs_test.c @@ -136,10 +136,10 @@ int main(int argc, char** argv) } // Fixed parameters, other params are not implemented - resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; - resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; - resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; - resource.resource_mapping.ports = 1; + resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + resource.resource_mapping.nof_ports = 1; // Row 1 supported only! uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; diff --git a/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c b/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c index 1da99b177..d3137d8a9 100644 --- a/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c +++ b/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c @@ -164,41 +164,44 @@ static int assert_cfg(const srsran_sch_cfg_nr_t* pdsch_cfg, const srsran_sch_gra continue; } - if (grant->mapping != gold[i].mapping_type) { + // Skip golden sample if one of the parameters does not match + if (grant->mapping != gold[i].mapping_type || pdsch_cfg->dmrs.typeA_pos != gold[i].typeA_pos || + pdsch_cfg->dmrs.additional_pos != gold[i].additional_pos || pdsch_cfg->dmrs.length != gold[i].max_length || + pdsch_cfg->dmrs.type != gold[i].type) { continue; } - if (pdsch_cfg->dmrs.typeA_pos != gold[i].typeA_pos) { - continue; + // Generate subcarrier mask from golden sample + bool sc_mask[SRSRAN_NRE] = {}; + if (grant->nof_dmrs_cdm_groups_without_data == 1) { + for (uint32_t j = 0; j < gold[i].nof_sc; j++) { + sc_mask[gold[i].sc_idx[j] % SRSRAN_NRE] = true; + } + } else if (pdsch_cfg->dmrs.type == srsran_dmrs_sch_type_1) { + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + sc_mask[k] = true; + } + } else if (pdsch_cfg->dmrs.type == srsran_dmrs_sch_type_2) { + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + sc_mask[k] = ((k % 6) < grant->nof_dmrs_cdm_groups_without_data * 2); + } } - if (pdsch_cfg->dmrs.additional_pos != gold[i].additional_pos) { - continue; - } - - if (pdsch_cfg->dmrs.length != gold[i].max_length) { - continue; - } - - if (pdsch_cfg->dmrs.type != gold[i].type) { - continue; - } - - uint32_t symbols[SRSRAN_DMRS_SCH_MAX_SYMBOLS] = {}; - int nof_symbols = srsran_dmrs_sch_get_symbols_idx(&pdsch_cfg->dmrs, grant, symbols); - - TESTASSERT(nof_symbols == gold[i].nof_symbols); - + // Generate symbol mask from golden sample + bool symbol_mask[SRSRAN_NSYMB_PER_SLOT_NR] = {}; for (uint32_t j = 0; j < gold[i].nof_symbols; j++) { - TESTASSERT(symbols[j] == gold[i].symbol_idx[j]); + symbol_mask[gold[i].symbol_idx[j] % SRSRAN_NSYMB_PER_SLOT_NR] = true; } - uint32_t sc[SRSRAN_NRE] = {}; - srsran_dmrs_sch_get_sc_idx(&pdsch_cfg->dmrs, SRSRAN_NRE, sc); + // Generate DMRS pattern + srsran_re_pattern_t pattern = {}; + TESTASSERT(srsran_dmrs_sch_rvd_re_pattern(&pdsch_cfg->dmrs, grant, &pattern) == SRSRAN_SUCCESS); - for (uint32_t j = 0; j < gold[i].nof_sc; j++) { - TESTASSERT(sc[j] == gold[i].sc_idx[j]); - } + // Assert subcarrier mask + TESTASSERT(memcmp(pattern.sc, sc_mask, sizeof(bool) * SRSRAN_NRE) == 0); + + // Assert symbol mask + TESTASSERT(memcmp(pattern.symbol, symbol_mask, sizeof(bool) * SRSRAN_NSYMB_PER_SLOT_NR) == 0); return SRSRAN_SUCCESS; } @@ -307,7 +310,15 @@ int main(int argc, char** argv) for (grant.nof_dmrs_cdm_groups_without_data = 1; grant.nof_dmrs_cdm_groups_without_data <= 3; grant.nof_dmrs_cdm_groups_without_data++) { // Load default type A grant - srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &grant); + if (srsran_ra_dl_nr_time_default_A(m, pdsch_cfg.dmrs.typeA_pos, &grant) < SRSRAN_SUCCESS) { + ERROR("Error loading time resource"); + continue; + } + + // Mapping type B is not supported + if (grant.mapping == srsran_sch_mapping_type_B) { + continue; + } int n = run_test(&dmrs_pdsch, &pdsch_cfg, &grant, sf_symbols, &chest_dl_res); diff --git a/lib/src/phy/phch/pdsch_nr.c b/lib/src/phy/phch/pdsch_nr.c index c60e86c13..30fff7475 100644 --- a/lib/src/phy/phch/pdsch_nr.c +++ b/lib/src/phy/phch/pdsch_nr.c @@ -11,6 +11,7 @@ */ #include "srsran/phy/phch/pdsch_nr.h" +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/mimo/layermap.h" #include "srsran/phy/mimo/precoding.h" @@ -174,171 +175,25 @@ void srsran_pdsch_nr_free(srsran_pdsch_nr_t* q) } } -/** - * @brief copies a number of countiguous Resource Elements - * @param sf_symbols slot symbols in frequency domain - * @param symbols resource elements - * @param count number of resource elements to copy - * @param put Direction, symbols are copied into sf_symbols if put is true, otherwise sf_symbols are copied into symbols - */ -static void srsran_pdsch_re_cp(cf_t* sf_symbols, cf_t* symbols, uint32_t count, bool put) -{ - if (put) { - srsran_vec_cf_copy(sf_symbols, symbols, count); - } else { - srsran_vec_cf_copy(symbols, sf_symbols, count); - } -} - -/* - * As a RB is 12 RE wide, positions marked as 1 will be used for the 1st CDM group, and the same with group 2: - * - * +---+---+---+---+---+---+---+---+---+---+---+---+ - * | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | - * +---+---+---+---+---+---+---+---+---+---+---+---+ - * -- k --> - * - * If the number of DMRS CDM groups without data is set to: - * - 1, data is mapped in RE marked as 2 - * - Otherwise, no data is mapped in this symbol - */ -static uint32_t srsran_pdsch_nr_cp_dmrs_type1(const srsran_pdsch_nr_t* q, - const srsran_sch_grant_nr_t* grant, - cf_t* symbols, - cf_t* sf_symbols, - bool put) +static inline uint32_t pdsch_nr_put_rb(cf_t* dst, cf_t* src, bool* rvd_mask) { uint32_t count = 0; - uint32_t delta = 0; - - if (grant->nof_dmrs_cdm_groups_without_data != 1) { - return count; - } - - for (uint32_t i = 0; i < q->carrier.nof_prb; i++) { - if (grant->prb_idx[i]) { - for (uint32_t j = 0; j < SRSRAN_NRE; j += 2) { - if (put) { - sf_symbols[i * SRSRAN_NRE + delta + j + 1] = symbols[count++]; - } else { - symbols[count++] = sf_symbols[i * SRSRAN_NRE + delta + j + 1]; - } - } + for (uint32_t i = 0; i < SRSRAN_NRE; i++) { + if (!rvd_mask[i]) { + dst[i] = src[count++]; } } - return count; } -/* - * As a RB is 12 RE wide, positions marked as 1 will be used for the 1st CDM group, and the same with groups 2 and 3: - * - * +---+---+---+---+---+---+---+---+---+---+---+---+ - * | 1 | 1 | 2 | 2 | 3 | 3 | 1 | 1 | 2 | 2 | 3 | 3 | - * +---+---+---+---+---+---+---+---+---+---+---+---+ - * -- k --> - * - * If the number of DMRS CDM groups without data is set to: - * - 1, data is mapped in RE marked as 2 and 3 - * - 2, data is mapped in RE marked as 3 - * - otherwise, no data is mapped in this symbol - */ -static uint32_t srsran_pdsch_nr_cp_dmrs_type2(const srsran_pdsch_nr_t* q, - const srsran_sch_grant_nr_t* grant, - cf_t* symbols, - cf_t* sf_symbols, - bool put) +static inline uint32_t pdsch_nr_get_rb(cf_t* dst, cf_t* src, bool* rvd_mask) { uint32_t count = 0; - - if (grant->nof_dmrs_cdm_groups_without_data != 1 && grant->nof_dmrs_cdm_groups_without_data != 2) { - return count; - } - - uint32_t re_offset = (grant->nof_dmrs_cdm_groups_without_data == 1) ? 2 : 4; - uint32_t re_count = (grant->nof_dmrs_cdm_groups_without_data == 1) ? 4 : 2; - - for (uint32_t i = 0; i < q->carrier.nof_prb; i++) { - if (grant->prb_idx[i]) { - // Copy RE between pilot pairs - srsran_pdsch_re_cp(&sf_symbols[i * SRSRAN_NRE + re_offset], &symbols[count], re_count, put); - count += re_count; - - // Copy RE after second pilot - srsran_pdsch_re_cp(&sf_symbols[(i + 1) * SRSRAN_NRE - re_count], &symbols[count], re_count, put); - count += re_count; + for (uint32_t i = 0; i < SRSRAN_NRE; i++) { + if (!rvd_mask[i]) { + dst[count++] = src[i]; } } - - return count; -} - -static uint32_t srsran_pdsch_nr_cp_dmrs(const srsran_pdsch_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_sch_grant_nr_t* grant, - cf_t* symbols, - cf_t* sf_symbols, - bool put) -{ - uint32_t count = 0; - - const srsran_dmrs_sch_cfg_t* dmrs_cfg = &cfg->dmrs; - - switch (dmrs_cfg->type) { - case srsran_dmrs_sch_type_1: - count = srsran_pdsch_nr_cp_dmrs_type1(q, grant, symbols, sf_symbols, put); - break; - case srsran_dmrs_sch_type_2: - count = srsran_pdsch_nr_cp_dmrs_type2(q, grant, symbols, sf_symbols, put); - break; - } - - return count; -} - -static uint32_t srsran_pdsch_nr_cp_clean(const srsran_pdsch_nr_t* q, - const srsran_sch_grant_nr_t* grant, - cf_t* symbols, - cf_t* sf_symbols, - bool put) -{ - uint32_t count = 0; - uint32_t start = 0; // Index of the start of continuous data - uint32_t length = 0; // End of continuous RE - - for (uint32_t i = 0; i < q->carrier.nof_prb; i++) { - if (grant->prb_idx[i]) { - // If fist continuous block, save start - if (length == 0) { - start = i * SRSRAN_NRE; - } - length += SRSRAN_NRE; - } else { - // Consecutive block is finished - if (put) { - srsran_vec_cf_copy(&sf_symbols[start], &symbols[count], length); - } else { - srsran_vec_cf_copy(&symbols[count], &sf_symbols[start], length); - } - - // Increase RE count - count += length; - - // Reset consecutive block - length = 0; - } - } - - // Copy last contiguous block - if (length > 0) { - if (put) { - srsran_vec_cf_copy(&sf_symbols[start], &symbols[count], length); - } else { - srsran_vec_cf_copy(&symbols[count], &sf_symbols[start], length); - } - count += length; - } - return count; } @@ -349,35 +204,40 @@ static int srsran_pdsch_nr_cp(const srsran_pdsch_nr_t* q, cf_t* sf_symbols, bool put) { - uint32_t count = 0; - uint32_t dmrs_l_idx[SRSRAN_DMRS_SCH_MAX_SYMBOLS] = {}; - uint32_t dmrs_l_count = 0; - - // Get symbol indexes carrying DMRS - int32_t nof_dmrs_symbols = srsran_dmrs_sch_get_symbols_idx(&cfg->dmrs, grant, dmrs_l_idx); - if (nof_dmrs_symbols < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { - DEBUG("dmrs_l_idx="); - srsran_vec_fprint_i(stdout, (int32_t*)dmrs_l_idx, nof_dmrs_symbols); - } + uint32_t count = 0; for (uint32_t l = grant->S; l < grant->S + grant->L; l++) { - // Advance DMRS symbol counter until: - // - the current DMRS symbol index is greater or equal than current symbol l - // - no more DMRS symbols - while (dmrs_l_idx[dmrs_l_count] < l && dmrs_l_count < nof_dmrs_symbols) { - dmrs_l_count++; + // Initialise reserved RE mask to all false + bool rvd_mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + + // Reserve DMRS + if (srsran_re_pattern_to_symbol_mask(&q->dmrs_re_pattern, l, rvd_mask) < SRSRAN_SUCCESS) { + ERROR("Error generating DMRS reserved RE mask"); + return SRSRAN_ERROR; } - if (l == dmrs_l_idx[dmrs_l_count]) { - count += srsran_pdsch_nr_cp_dmrs( - q, cfg, grant, &symbols[count], &sf_symbols[l * q->carrier.nof_prb * SRSRAN_NRE], put); - } else { - count += - srsran_pdsch_nr_cp_clean(q, grant, &symbols[count], &sf_symbols[l * q->carrier.nof_prb * SRSRAN_NRE], put); + // Reserve RE from configuration + if (srsran_re_pattern_list_to_symbol_mask(&cfg->rvd_re, l, rvd_mask) < SRSRAN_SUCCESS) { + ERROR("Error generating reserved RE mask"); + return SRSRAN_ERROR; + } + + // Actual copy + for (uint32_t rb = 0; rb < q->carrier.nof_prb; rb++) { + // Skip PRB if not available in grant + if (!grant->prb_idx[rb]) { + continue; + } + + // Calculate RE index at the begin of the symbol + uint32_t re_idx = (q->carrier.nof_prb * l + rb) * SRSRAN_NRE; + + // Put or get + if (put) { + count += pdsch_nr_put_rb(&sf_symbols[re_idx], &symbols[count], &rvd_mask[rb * SRSRAN_NRE]); + } else { + count += pdsch_nr_get_rb(&symbols[count], &sf_symbols[re_idx], &rvd_mask[rb * SRSRAN_NRE]); + } } } @@ -487,6 +347,12 @@ int srsran_pdsch_nr_encode(srsran_pdsch_nr_t* q, return SRSRAN_ERROR; } + // Compute DMRS pattern + if (srsran_dmrs_sch_rvd_re_pattern(&cfg->dmrs, grant, &q->dmrs_re_pattern) < SRSRAN_SUCCESS) { + ERROR("Error computing DMRS pattern"); + return SRSRAN_ERROR; + } + // 7.3.1.1 and 7.3.1.2 uint32_t nof_cw = 0; for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { @@ -571,10 +437,8 @@ static inline int pdsch_nr_decode_codeword(srsran_pdsch_nr_t* q, res->evm = srsran_evm_run_b(q->evm_buffer, &q->modem_tables[tb->mod], q->d[tb->cw_idx], llr, tb->nof_bits); } - // Change LLR sign - for (uint32_t i = 0; i < tb->nof_bits; i++) { - llr[i] = -llr[i]; - } + // Change LLR sign and set to zero the LLR that are not used + srsran_vec_neg_bb(llr, llr, tb->nof_bits); // Descrambling srsran_sequence_apply_c(llr, llr, tb->nof_bits, pdsch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx)); @@ -610,12 +474,18 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, gettimeofday(&t[1], NULL); } + // Compute DMRS pattern + if (srsran_dmrs_sch_rvd_re_pattern(&cfg->dmrs, grant, &q->dmrs_re_pattern) < SRSRAN_SUCCESS) { + ERROR("Error computing DMRS pattern"); + return SRSRAN_ERROR; + } + uint32_t nof_cw = 0; for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { nof_cw += grant->tb[tb].enabled ? 1 : 0; } - uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(cfg, grant); + uint32_t nof_re = grant->tb[0].nof_re; if (channel->nof_re != nof_re) { ERROR("Inconsistent number of RE (%d!=%d)", channel->nof_re, nof_re); @@ -723,6 +593,12 @@ uint32_t srsran_pdsch_nr_rx_info(const srsran_pdsch_nr_t* q, len += srsran_pdsch_nr_grant_info(cfg, grant, &str[len], str_len - len); + if (cfg->rvd_re.count != 0) { + len = srsran_print_check(str, str_len, len, ", Reserved={"); + len += srsran_re_pattern_list_info(&cfg->rvd_re, &str[len], str_len - len); + len = srsran_print_check(str, str_len, len, "}"); + } + if (q->evm_buffer != NULL) { len = srsran_print_check(str, str_len, len, ",evm={", 0); for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { @@ -766,13 +642,5 @@ uint32_t srsran_pdsch_nr_tx_info(const srsran_pdsch_nr_t* q, char* str, uint32_t str_len) { - uint32_t len = 0; - - len += srsran_pdsch_nr_grant_info(cfg, grant, &str[len], str_len - len); - - if (q->meas_time_en) { - len = srsran_print_check(str, str_len, len, ", t=%d us", q->meas_time_us); - } - - return len; + return srsran_pdsch_nr_rx_info(q, cfg, grant, NULL, str, str_len); } diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index a4ab97397..53caf96c3 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -11,6 +11,7 @@ */ #include "srsran/phy/phch/ra_nr.h" +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/pdsch_nr.h" #include "srsran/phy/phch/ra_dl_nr.h" @@ -245,6 +246,27 @@ static ra_nr_table_t ra_nr_select_table(srsran_mcs_table_t mcs_table, return ra_nr_select_table_pdsch(mcs_table, dci_format, search_space_type, rnti_type); } +static int ra_nr_dmrs_power_offset(srsran_sch_grant_nr_t* grant) +{ + if (grant == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Defined by TS 38.214 V15.10.0: + // - Table 4.1-1: The ratio of PDSCH EPRE to DM-RS EPRE + // - Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE + float ratio_dB[3] = {0, -3, -4.77}; + + if (grant->nof_dmrs_cdm_groups_without_data < 1 || grant->nof_dmrs_cdm_groups_without_data > 3) { + ERROR("Invalid number of DMRS CDM groups without data (%d)", grant->nof_dmrs_cdm_groups_without_data); + return SRSRAN_ERROR; + } + + grant->beta_dmrs = srsran_convert_dB_to_amplitude(-ratio_dB[grant->nof_dmrs_cdm_groups_without_data - 1]); + + return SRSRAN_SUCCESS; +} + double srsran_ra_nr_R_from_mcs(srsran_mcs_table_t mcs_table, srsran_dci_format_nr_t dci_format, srsran_search_space_type_t search_space_type, @@ -405,6 +427,34 @@ uint32_t srsran_ra_nr_tbs(uint32_t N_re, double S, double R, uint32_t Qm, uint32 return ra_nr_tbs_from_n_info4(n_info, R); } +static int ra_nr_assert_csi_rs_dmrs_collision(const srsran_sch_cfg_nr_t* pdsch_cfg) +{ + // Generate DMRS pattern + srsran_re_pattern_t dmrs_re_pattern = {}; + if (srsran_dmrs_sch_rvd_re_pattern(&pdsch_cfg->dmrs, &pdsch_cfg->grant, &dmrs_re_pattern) < SRSRAN_SUCCESS) { + ERROR("Error computing DMRS pattern"); + return SRSRAN_ERROR; + } + + // Check for collision + if (srsran_re_pattern_check_collision(&pdsch_cfg->rvd_re, &dmrs_re_pattern) < SRSRAN_SUCCESS) { + // Create reserved info string + char str_rvd[512] = {}; + srsran_re_pattern_list_info(&pdsch_cfg->rvd_re, str_rvd, (uint32_t)sizeof(str_rvd)); + + // Create DMRS info string + char str_dmrs[512] = {}; + srsran_re_pattern_info(&dmrs_re_pattern, str_dmrs, (uint32_t)sizeof(str_dmrs)); + + ERROR("Error. The UE is not expected to receive CSI-RS (%s) and DM-RS (%s) on the same resource elements.", + str_rvd, + str_dmrs); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, const srsran_sch_grant_nr_t* grant, uint32_t mcs_idx, @@ -451,7 +501,7 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, // 1) The UE shall first determine the number of REs (N RE ) within the slot. int N_re = srsran_ra_dl_nr_slot_nof_re(pdsch_cfg, grant); if (N_re <= SRSRAN_SUCCESS) { - ERROR("Invalid number of RE"); + ERROR("Invalid number of RE (%d)", N_re); return SRSRAN_ERROR; } @@ -461,55 +511,141 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, uint32_t nof_layers_cw2 = grant->nof_layers - nof_layers_cw1; tb->N_L = (cw_idx == 0) ? nof_layers_cw1 : nof_layers_cw2; + // Check DMRS and CSI-RS collision according to TS 38.211 7.4.1.5.3 Mapping to physical resources + // If there was a collision, the number of RE in the grant would be wrong + if (ra_nr_assert_csi_rs_dmrs_collision(pdsch_cfg) < SRSRAN_SUCCESS) { + ERROR("Error: CSI-RS and DMRS collision detected"); + return SRSRAN_ERROR; + } + + // Calculate reserved RE + uint32_t N_re_rvd = srsran_re_pattern_list_count(&pdsch_cfg->rvd_re, grant->S, grant->S + grant->L, grant->prb_idx); + // Steps 2,3,4 tb->tbs = (int)srsran_ra_nr_tbs(N_re, S, R, Qm, tb->N_L); tb->R = R; tb->mod = m; - tb->nof_re = N_re * grant->nof_layers; + tb->nof_re = (N_re - N_re_rvd) * grant->nof_layers; tb->nof_bits = tb->nof_re * Qm; tb->enabled = true; return SRSRAN_SUCCESS; } -static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* pdsch_hl_cfg, - srsran_sch_grant_nr_t* pdsch_grant, - srsran_dmrs_sch_cfg_t* dmrs_cfg) +static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_t* grant, srsran_sch_cfg_nr_t* cfg) { - const bool dedicated_dmrs_present = (pdsch_grant->mapping == srsran_sch_mapping_type_A) - ? pdsch_hl_cfg->dmrs_typeA.present - : pdsch_hl_cfg->dmrs_typeB.present; + const bool dedicated_dmrs_present = + (grant->mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present; - if (pdsch_grant->dci_format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { - if (pdsch_grant->mapping == srsran_sch_mapping_type_A) { + if (grant->dci_format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { + if (grant->mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig - dmrs_cfg->additional_pos = srsran_dmrs_sch_add_pos_2; - dmrs_cfg->type = srsran_dmrs_sch_type_1; - dmrs_cfg->length = srsran_dmrs_sch_len_1; - dmrs_cfg->scrambling_id0_present = false; - dmrs_cfg->scrambling_id1_present = false; - - if (pdsch_grant->dci_format == srsran_dci_format_nr_1_0) { - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(dmrs_cfg, pdsch_grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups"); - return SRSRAN_ERROR; - } - } else { - ERROR("Invalid case"); - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; + cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; + cfg->dmrs.type = srsran_dmrs_sch_type_1; + cfg->dmrs.length = srsran_dmrs_sch_len_1; + cfg->dmrs.scrambling_id0_present = false; + cfg->dmrs.scrambling_id1_present = false; + } else { + ERROR("Unsupported configuration"); + return SRSRAN_ERROR; } + } else { + if (grant->mapping == srsran_sch_mapping_type_A) { + cfg->dmrs.additional_pos = hl_cfg->dmrs_typeA.additional_pos; + cfg->dmrs.type = hl_cfg->dmrs_typeA.type; + cfg->dmrs.length = hl_cfg->dmrs_typeA.length; + cfg->dmrs.scrambling_id0_present = false; + cfg->dmrs.scrambling_id1_present = false; + } else { + cfg->dmrs.additional_pos = hl_cfg->dmrs_typeB.additional_pos; + cfg->dmrs.type = hl_cfg->dmrs_typeB.type; + cfg->dmrs.length = hl_cfg->dmrs_typeB.length; + cfg->dmrs.scrambling_id0_present = false; + cfg->dmrs.scrambling_id1_present = false; + } + } - ERROR("Unsupported configuration"); + // Set number of DMRS CDM groups without data + if (grant->dci_format == srsran_dci_format_nr_1_0) { + if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&cfg->dmrs, grant) < SRSRAN_SUCCESS) { + ERROR("Error loading number of DMRS CDM groups"); + return SRSRAN_ERROR; + } + } else { + ERROR("Invalid case"); return SRSRAN_ERROR; } - return SRSRAN_ERROR; + // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE + if (ra_nr_dmrs_power_offset(grant) < SRSRAN_SUCCESS) { + ERROR("Error setting DMRS power offset"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static int ra_dl_resource_mapping(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot, + const srsran_sch_hl_cfg_nr_t* pdsch_hl_cfg, + srsran_sch_cfg_nr_t* pdsch_cfg) +{ + // SS/PBCH block transmission resources not available for PDSCH + // ... Not implemented + + // 5.1.4.1 PDSCH resource mapping with RB symbol level granularity + // rateMatchPatternToAddModList ... Not implemented + + // 5.1.4.2 PDSCH resource mapping with RE level granularity + // RateMatchingPatternLTE-CRS ... Not implemented + + // Append periodic ZP-CSI-RS + for (uint32_t i = 0; i < pdsch_hl_cfg->p_zp_csi_rs_set.count; i++) { + // Select resource + const srsran_csi_rs_zp_resource_t* resource = &pdsch_hl_cfg->p_zp_csi_rs_set.data[i]; + + // Check if the periodic ZP-CSI is transmitted + if (srsran_csi_rs_send(&resource->periodicity, slot)) { + INFO("Tx/Rx ZP-CSI-RS @slot=%d\n", slot->idx); + if (srsran_csi_rs_append_resource_to_pattern(carrier, &resource->resource_mapping, &pdsch_cfg->rvd_re)) { + ERROR("Error appending ZP-CSI-RS as RE pattern"); + return SRSRAN_ERROR; + } + } + } + + // Append semi-persistent ZP-CSI-RS here + // ... not implemented + + // Append aperiodic ZP-CSI-RS here + // ... not implemented + + // Append periodic NZP-CSI-RS according to TS 38.211 clause 7.3.1.5 Mapping to virtual resource blocks + // Only aplicable if CRC is scrambled by C-RNTI, MCS-C-RNTI, CS-RNTI, or PDSCH with SPS + bool nzp_rvd_valid = pdsch_cfg->grant.rnti_type == srsran_rnti_type_c || + pdsch_cfg->grant.rnti_type == srsran_rnti_type_mcs_c || + pdsch_cfg->grant.rnti_type == srsran_rnti_type_cs; + for (uint32_t set_id = 0; set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS && nzp_rvd_valid; set_id++) { + for (uint32_t res_id = 0; res_id < pdsch_hl_cfg->nzp_csi_rs_sets[set_id].count; res_id++) { + // Select resource + const srsran_csi_rs_nzp_resource_t* resource = &pdsch_hl_cfg->nzp_csi_rs_sets[set_id].data[res_id]; + + // Check if the periodic ZP-CSI is transmitted + if (srsran_csi_rs_send(&resource->periodicity, slot)) { + INFO("Tx/Rx NZP-CSI-RS set_id=%d; res=%d; @slot=%d\n", set_id, res_id, slot->idx); + if (srsran_csi_rs_append_resource_to_pattern(carrier, &resource->resource_mapping, &pdsch_cfg->rvd_re)) { + ERROR("Error appending ZP-CSI-RS as RE pattern"); + return SRSRAN_ERROR; + } + } + } + } + + return SRSRAN_SUCCESS; } int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot, const srsran_sch_hl_cfg_nr_t* pdsch_hl_cfg, const srsran_dci_dl_nr_t* dci_dl, srsran_sch_cfg_nr_t* pdsch_cfg, @@ -541,8 +677,14 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, pdsch_grant->rnti_type = dci_dl->rnti_type; pdsch_grant->tb[0].rv = dci_dl->rv; + // 5.1.4 PDSCH resource mapping + if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) { + ERROR("Error in resource mapping"); + return SRSRAN_ERROR; + } + // 5.1.6.2 DM-RS reception procedure - if (ra_dl_dmrs(pdsch_hl_cfg, pdsch_grant, &pdsch_cfg->dmrs) < SRSRAN_SUCCESS) { + if (ra_dl_dmrs(pdsch_hl_cfg, pdsch_grant, pdsch_cfg) < SRSRAN_SUCCESS) { ERROR("Error selecting DMRS configuration"); return SRSRAN_ERROR; } @@ -603,7 +745,7 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu } // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE - if (srsran_ra_ul_nr_dmrs_power_offset(pusch_grant) < SRSRAN_SUCCESS) { + if (ra_nr_dmrs_power_offset(pusch_grant) < SRSRAN_SUCCESS) { ERROR("Error setting DMRS power offset"); return SRSRAN_ERROR; } @@ -746,4 +888,4 @@ int srsran_ra_ul_set_grant_uci_nr(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, pusch_cfg->uci = *uci_cfg; return SRSRAN_SUCCESS; -} \ No newline at end of file +} diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 41fe9ed30..8960f2903 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -222,24 +222,6 @@ int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch return SRSRAN_SUCCESS; } -int srsran_ra_ul_nr_dmrs_power_offset(srsran_sch_grant_nr_t* grant) -{ - if (grant == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - float ratio_dB[3] = {0, -3, -4.77}; - - if (grant->nof_dmrs_cdm_groups_without_data < 1 || grant->nof_dmrs_cdm_groups_without_data > 3) { - ERROR("Invalid number of DMRS CDM groups without data (%d)", grant->nof_dmrs_cdm_groups_without_data); - return SRSRAN_ERROR; - } - - grant->beta_dmrs = srsran_convert_dB_to_amplitude(-ratio_dB[grant->nof_dmrs_cdm_groups_without_data - 1]); - - return SRSRAN_SUCCESS; -} - #define RA_UL_PUCCH_CODE_RATE_N 8 #define RA_UL_PUCCH_CODE_RATE_RESERVED NAN diff --git a/lib/src/phy/phch/test/pdsch_nr_test.c b/lib/src/phy/phch/test/pdsch_nr_test.c index ca34bb90f..42a58b169 100644 --- a/lib/src/phy/phch/test/pdsch_nr_test.c +++ b/lib/src/phy/phch/test/pdsch_nr_test.c @@ -28,11 +28,10 @@ static srsran_carrier_nr_t carrier = { 1 // max_mimo_layers }; -static uint32_t n_prb = 0; // Set to 0 for steering -static uint32_t mcs = 30; // Set to 30 for steering -static srsran_sch_cfg_nr_t pdsch_cfg = {}; -static srsran_sch_grant_nr_t pdsch_grant = {}; -static uint16_t rnti = 0x1234; +static uint32_t n_prb = 0; // Set to 0 for steering +static uint32_t mcs = 30; // Set to 30 for steering +static srsran_sch_cfg_nr_t pdsch_cfg = {}; +static uint16_t rnti = 0x1234; void usage(char* prog) { @@ -153,20 +152,20 @@ int main(int argc, char** argv) } // Use grant default A time resources with m=0 - if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_grant) < SRSRAN_SUCCESS) { + if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { ERROR("Error loading default grant"); goto clean_exit; } // Load number of DMRS CDM groups without data - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_grant) < SRSRAN_SUCCESS) { + if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { ERROR("Error loading number of DMRS CDM groups without data"); goto clean_exit; } - pdsch_grant.nof_layers = carrier.max_mimo_layers; - pdsch_grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_grant.rnti = rnti; + pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pdsch_cfg.grant.rnti = rnti; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; @@ -190,10 +189,10 @@ int main(int argc, char** argv) for (n_prb = n_prb_start; n_prb < n_prb_end; n_prb++) { for (mcs = mcs_start; mcs < mcs_end; mcs++) { for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { - pdsch_grant.prb_idx[n] = (n < n_prb); + pdsch_cfg.grant.prb_idx[n] = (n < n_prb); } - if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_grant, mcs, &pdsch_grant.tb[0]) < SRSRAN_SUCCESS) { + if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_cfg.grant, mcs, &pdsch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) { ERROR("Error filing tb"); goto clean_exit; } @@ -204,28 +203,29 @@ int main(int argc, char** argv) continue; } - for (uint32_t i = 0; i < pdsch_grant.tb[tb].tbs; i++) { + for (uint32_t i = 0; i < pdsch_cfg.grant.tb[tb].tbs; i++) { data_tx[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); } - pdsch_grant.tb[tb].softbuffer.tx = &softbuffer_tx; + pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx; } - if (srsran_pdsch_nr_encode(&pdsch_tx, &pdsch_cfg, &pdsch_grant, data_tx, sf_symbols) < SRSRAN_SUCCESS) { + if (srsran_pdsch_nr_encode(&pdsch_tx, &pdsch_cfg, &pdsch_cfg.grant, data_tx, sf_symbols) < SRSRAN_SUCCESS) { ERROR("Error encoding"); goto clean_exit; } for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { - pdsch_grant.tb[tb].softbuffer.rx = &softbuffer_rx; - srsran_softbuffer_rx_reset(pdsch_grant.tb[tb].softbuffer.rx); + pdsch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx; + srsran_softbuffer_rx_reset(pdsch_cfg.grant.tb[tb].softbuffer.rx); } - for (uint32_t i = 0; i < pdsch_grant.tb->nof_re; i++) { + for (uint32_t i = 0; i < pdsch_cfg.grant.tb->nof_re; i++) { chest.ce[0][0][i] = 1.0f; } - chest.nof_re = pdsch_grant.tb->nof_re; + chest.nof_re = pdsch_cfg.grant.tb->nof_re; - if (srsran_pdsch_nr_decode(&pdsch_rx, &pdsch_cfg, &pdsch_grant, &chest, sf_symbols, pdsch_res) < SRSRAN_SUCCESS) { + if (srsran_pdsch_nr_decode(&pdsch_rx, &pdsch_cfg, &pdsch_cfg.grant, &chest, sf_symbols, pdsch_res) < + SRSRAN_SUCCESS) { ERROR("Error encoding"); goto clean_exit; } @@ -236,18 +236,18 @@ int main(int argc, char** argv) } float mse = 0.0f; - uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(&pdsch_cfg, &pdsch_grant); - for (uint32_t i = 0; i < pdsch_grant.nof_layers; i++) { + uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(&pdsch_cfg, &pdsch_cfg.grant); + for (uint32_t i = 0; i < pdsch_cfg.grant.nof_layers; i++) { for (uint32_t j = 0; j < nof_re; j++) { mse += cabsf(pdsch_tx.d[i][j] - pdsch_rx.d[i][j]); } } - if (nof_re * pdsch_grant.nof_layers > 0) { - mse = mse / (nof_re * pdsch_grant.nof_layers); + if (nof_re * pdsch_cfg.grant.nof_layers > 0) { + mse = mse / (nof_re * pdsch_cfg.grant.nof_layers); } if (mse > 0.001) { ERROR("MSE error (%f) is too high", mse); - for (uint32_t i = 0; i < pdsch_grant.nof_layers; i++) { + for (uint32_t i = 0; i < pdsch_cfg.grant.nof_layers; i++) { printf("d_tx[%d]=", i); srsran_vec_fprint_c(stdout, pdsch_tx.d[i], nof_re); printf("d_rx[%d]=", i); @@ -257,20 +257,20 @@ int main(int argc, char** argv) } if (!pdsch_res[0].crc) { - ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_grant.tb[0].tbs); + ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); goto clean_exit; } - if (memcmp(data_tx[0], data_rx[0], pdsch_grant.tb[0].tbs / 8) != 0) { - ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_grant.tb[0].tbs); + if (memcmp(data_tx[0], data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8) != 0) { + ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); printf("Tx data: "); - srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_grant.tb[0].tbs / 8); + srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_cfg.grant.tb[0].tbs / 8); printf("Rx data: "); - srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_grant.tb[0].tbs / 8); + srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8); goto clean_exit; } - printf("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pdsch_grant.tb[0].tbs, pdsch_res[0].evm); + INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!\n", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res[0].evm); } } diff --git a/lib/src/phy/utils/re_pattern.c b/lib/src/phy/utils/re_pattern.c new file mode 100644 index 000000000..d390ae2ad --- /dev/null +++ b/lib/src/phy/utils/re_pattern.c @@ -0,0 +1,320 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 "srsran/phy/utils/re_pattern.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" + +bool srsran_re_pattern_to_mask(const srsran_re_pattern_list_t* list, uint32_t l, uint32_t k) +{ + uint32_t rb_idx = k % SRSRAN_NRE; + uint32_t sc_idx = k / SRSRAN_NRE; + + // Check pattern list is valid + if (list == NULL) { + return false; + } + + // Iterate all given patterns + for (uint32_t i = 0; i < list->count; i++) { + const srsran_re_pattern_t* pattern = &list->data[i]; + + // Skip pattern if it is not active in this OFDM symbol + if (!pattern->symbol[l]) { + continue; + } + + // Skip pattern if RB index is put of the pattern bounds + if (rb_idx < pattern->rb_begin || rb_idx >= pattern->rb_end) { + continue; + } + + // Matched SC, early return + if (pattern->sc[sc_idx]) { + return true; + } + } + + // If reached here, no pattern was matched + return false; +} + +int srsran_re_pattern_to_symbol_mask(const srsran_re_pattern_t* pattern, uint32_t l, bool* mask) +{ + // Check inputs + if (pattern == NULL || mask == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Check symbol index is in range + if (l >= SRSRAN_NSYMB_PER_SLOT_NR) { + ERROR("Symbol index is out of range"); + return SRSRAN_ERROR; + } + + // Skip pattern if it is not active in this OFDM symbol + if (!pattern->symbol[l]) { + return SRSRAN_SUCCESS; + } + + // Make sure RB end is bounded + if (pattern->rb_end > SRSRAN_MAX_PRB_NR) { + return SRSRAN_ERROR; + } + + // Add mask for pattern + for (uint32_t rb_idx = pattern->rb_begin; rb_idx < pattern->rb_end; rb_idx += pattern->rb_stride) { + for (uint32_t sc_idx = 0; sc_idx < SRSRAN_NRE; sc_idx++) { + mask[rb_idx * SRSRAN_NRE + sc_idx] |= pattern->sc[sc_idx]; + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_re_pattern_list_to_symbol_mask(const srsran_re_pattern_list_t* list, uint32_t l, bool* mask) +{ + // Check inputs + if (list == NULL || mask == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Iterate all given patterns + for (uint32_t i = 0; i < list->count; i++) { + if (srsran_re_pattern_to_symbol_mask(&list->data[i], l, mask) < SRSRAN_SUCCESS) { + ERROR("Error calculating mask"); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_re_pattern_merge(srsran_re_pattern_list_t* list, const srsran_re_pattern_t* p) +{ + // Check inputs are valid + if (list == NULL || p == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Count number of subcarrier mask + uint32_t kcount = 0; + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + kcount += p->sc[k] ? 1 : 0; + } + + // Count number of symbol mask + uint32_t lcount = 0; + for (uint32_t l = 0; l < SRSRAN_NRE; l++) { + lcount += p->symbol[l] ? 1 : 0; + } + + // If any mask is empty, ignore + if (kcount == 0 || lcount == 0) { + return SRSRAN_SUCCESS; + } + + // Iterate all given patterns + for (uint32_t i = 0; i < list->count; i++) { + srsran_re_pattern_t* pattern = &list->data[i]; + + // Skip if RB parameters dont match + if (pattern->rb_begin != p->rb_begin || pattern->rb_end != p->rb_end || pattern->rb_stride != p->rb_stride) { + continue; + } + + // Check if symbol mask matches + bool lmatch = (memcmp(pattern->symbol, p->symbol, SRSRAN_NSYMB_PER_SLOT_NR) == 0); + + // Check if sc mask matches + bool kmatch = (memcmp(pattern->sc, p->sc, SRSRAN_NRE) == 0); + + // If OFDM symbols and subcarriers mask match, it means that the patterns are completely overlapped and no merging + // is required + if (kmatch && lmatch) { + return SRSRAN_SUCCESS; + } + + // If OFDM symbols mask matches, merge subcarrier mask + if (lmatch) { + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + pattern->sc[k] |= p->sc[k]; + } + return SRSRAN_SUCCESS; + } + + // If subcarriers mask matches, merge OFDM symbols mask + if (kmatch) { + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { + pattern->symbol[l] |= p->symbol[l]; + } + return SRSRAN_SUCCESS; + } + } + + // If reached here, no pattern was matched. Try appending + if (list->count >= SRSRAN_RE_PATTERN_LIST_SIZE) { + ERROR("Insufficient number of available RE patterns in list"); + return SRSRAN_ERROR; + } + + // Append + list->data[list->count] = *p; + list->count++; + + return SRSRAN_SUCCESS; +} + +int srsran_re_pattern_check_collision(const srsran_re_pattern_list_t* list, const srsran_re_pattern_t* p) +{ + // Check inputs are valid + if (list == NULL || p == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Count number of subcarrier mask + uint32_t kcount = 0; + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + kcount += p->sc[k] ? 1 : 0; + } + + // Count number of symbol mask + uint32_t lcount = 0; + for (uint32_t l = 0; l < SRSRAN_NRE; l++) { + lcount += p->symbol[l] ? 1 : 0; + } + + // If any mask is empty, no collision + if (kcount == 0 || lcount == 0) { + return SRSRAN_SUCCESS; + } + + // Iterate all given patterns + for (uint32_t i = 0; i < list->count; i++) { + const srsran_re_pattern_t* pattern = &list->data[i]; + + // Skip if RB do not overlap + if (pattern->rb_begin > p->rb_end || p->rb_begin > pattern->rb_end) { + continue; + } + + // Check if symbol are matched + bool lmatch = false; + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR && !lmatch; l++) { + // Consider match if both patterns have a positive symbol in common + lmatch = (p->symbol[l] && pattern->symbol[l]); + } + + // If the symbols are not matched, skip pattern + if (!lmatch) { + continue; + } + + // Check if any subcarrier mask matches + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + // Consider a collision if both subcarrier mask are true + if (p->sc[k] && pattern->sc[k]) { + return SRSRAN_ERROR; + } + } + } + + // If reached here, means no collision + return SRSRAN_SUCCESS; +} + +void srsran_re_pattern_reset(srsran_re_pattern_list_t* list) +{ + if (list == NULL) { + return; + } + SRSRAN_MEM_ZERO(list, srsran_re_pattern_list_t, 1); +} + +uint32_t srsran_re_pattern_info(const srsran_re_pattern_t* pattern, char* str, uint32_t str_len) +{ + if (pattern == NULL || str == NULL || str_len == 0) { + return 0; + } + + char subc[SRSRAN_NRE + 1] = {}; + srsran_vec_sprint_bin(subc, SRSRAN_NRE + 1, (uint8_t*)pattern->sc, SRSRAN_NRE); + + char symb[SRSRAN_NSYMB_PER_SLOT_NR + 1] = {}; + srsran_vec_sprint_bin(symb, SRSRAN_NSYMB_PER_SLOT_NR + 1, (uint8_t*)pattern->symbol, SRSRAN_NSYMB_PER_SLOT_NR); + + return srsran_print_check(str, + str_len, + 0, + "begin=%d end=%d stride=%d sc=%s symb=%s ", + pattern->rb_begin, + pattern->rb_end, + pattern->rb_stride, + subc, + symb); +} + +uint32_t srsran_re_pattern_list_info(const srsran_re_pattern_list_t* list, char* str, uint32_t str_len) +{ + uint32_t len = 0; + if (list == NULL || str == NULL || str_len == 0) { + return 0; + } + + for (uint32_t i = 0; i < list->count; i++) { + len = srsran_print_check(str, str_len, len, "RE%d: ", i); + len += srsran_re_pattern_info(&list->data[i], &str[len], str_len - len); + } + + return len; +} + +uint32_t srsran_re_pattern_list_count(const srsran_re_pattern_list_t* list, + uint32_t symbol_begin, + uint32_t symbol_end, + const bool prb_mask[SRSRAN_MAX_PRB_NR]) +{ + uint32_t count = 0; + if (list == NULL || prb_mask == NULL) { + return 0; + } + + // Iterate over all symbols and create a symbol mask + for (uint32_t l = symbol_begin; l < symbol_end; l++) { + // Entire symbol mask to prevent overlapped RE to count twice + bool symbol_mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + + // For each pattern, compute symbol mask + for (uint32_t i = 0; i < list->count; i++) { + if (srsran_re_pattern_to_symbol_mask(&list->data[i], l, symbol_mask) < SRSRAN_SUCCESS) { + ERROR("Error calculating symbol mask"); + return SRSRAN_ERROR; + } + } + + // Count number of masked elements + for (uint32_t rb = 0; rb < SRSRAN_MAX_PRB_NR; rb++) { + // Skip PRB if disabled + if (!prb_mask[rb]) { + continue; + } + + // Iterate all subcarriers in the PRB + for (uint32_t k = rb * SRSRAN_NRE; k < (rb + 1) * SRSRAN_NRE; k++) { + // Count only the true masked RE + count += (uint32_t)symbol_mask[k]; + } + } + } + + return count; +} \ No newline at end of file diff --git a/lib/src/phy/utils/test/CMakeLists.txt b/lib/src/phy/utils/test/CMakeLists.txt index 4a5f062f4..095945c7b 100644 --- a/lib/src/phy/utils/test/CMakeLists.txt +++ b/lib/src/phy/utils/test/CMakeLists.txt @@ -36,10 +36,19 @@ target_link_libraries(vector_test srsran_phy) add_test(vector_test vector_test) +######################################################################## +# Ring-Buffer TEST ######################################################################## add_executable(ringbuffer_test ring_buffer_test.c) target_link_libraries(ringbuffer_test srsran_phy) add_test(ringbuffer_tester ringbuffer_test) + ######################################################################## +# RE-Pattern TEST +######################################################################## +add_executable(re_pattern_test re_pattern_test.c) +target_link_libraries(re_pattern_test srsran_phy) + +add_test(re_pattern_test re_pattern_test) \ No newline at end of file diff --git a/lib/src/phy/utils/test/re_pattern_test.c b/lib/src/phy/utils/test/re_pattern_test.c new file mode 100644 index 000000000..ffed8bfb7 --- /dev/null +++ b/lib/src/phy/utils/test/re_pattern_test.c @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 "srsran/common/test_common.h" +#include "srsran/phy/utils/re_pattern.h" + +int main(int argc, char** argv) +{ + srsran_re_pattern_list_t pattern_list; + + // Reset list + srsran_re_pattern_reset(&pattern_list); + + // Create first pattern and merge + srsran_re_pattern_t pattern_1 = {}; + pattern_1.rb_begin = 1; + pattern_1.rb_end = 50; + pattern_1.rb_stride = 1; + for (uint32_t k = 0; k < SRSRAN_NRE; k++) { + pattern_1.sc[k] = (k % 2 == 0); // Only even subcarriers + } + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { + pattern_1.symbol[l] = (l % 2 == 0); // Only even symbols + } + TESTASSERT(srsran_re_pattern_merge(&pattern_list, &pattern_1) == SRSRAN_SUCCESS); + TESTASSERT(pattern_list.count == 1); + + // Create second pattern and merge + srsran_re_pattern_t pattern_2 = pattern_1; + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { + pattern_2.symbol[l] = (l % 2 == 1); // Only odd symbols + } + TESTASSERT(srsran_re_pattern_merge(&pattern_list, &pattern_2) == SRSRAN_SUCCESS); + TESTASSERT(pattern_list.count == 1); + + // Assert generated mask + for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { + bool mask[SRSRAN_NRE * SRSRAN_MAX_PRB_NR] = {}; + TESTASSERT(srsran_re_pattern_list_to_symbol_mask(&pattern_list, l, mask) == SRSRAN_SUCCESS); + for (uint32_t k = 0; k < SRSRAN_NRE * SRSRAN_MAX_PRB_NR; k++) { + if (k >= pattern_1.rb_begin * SRSRAN_NRE && k < pattern_1.rb_end * SRSRAN_NRE && + (k / SRSRAN_NRE - pattern_1.rb_begin) % pattern_1.rb_stride == 0) { + TESTASSERT(mask[k] == (k % 2 == 0)); + } else { + TESTASSERT(mask[k] == false); + } + } + } + + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/utils/test/vector_test.c b/lib/src/phy/utils/test/vector_test.c index 0d184e1b3..45eca840d 100644 --- a/lib/src/phy/utils/test/vector_test.c +++ b/lib/src/phy/utils/test/vector_test.c @@ -211,6 +211,44 @@ TEST( free(y); free(z);) +TEST( + srsran_vec_neg_bbb, MALLOC(int8_t, x); MALLOC(int8_t, y); MALLOC(int8_t, z); + + int16_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_B(); + do { + y[i] = RANDOM_B(); + } while (!y[i]); + } + + TEST_CALL(srsran_vec_neg_bbb(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = y[i] < 0 ? -x[i] : x[i]; + mse += abs(gold - z[i]); + } + + free(x); + free(y); + free(z);) + +TEST( + srsran_vec_neg_bb, MALLOC(int8_t, x); MALLOC(int8_t, z); + + int16_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { x[i] = RANDOM_B(); } + + TEST_CALL(srsran_vec_neg_bb(x, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = -x[i]; + mse += abs(gold - z[i]); + } + + free(x); + free(z);) + TEST( srsran_vec_acc_cc, MALLOC(cf_t, x); cf_t z = 0.0f; @@ -836,6 +874,14 @@ int main(int argc, char** argv) test_srsran_vec_neg_sss(func_names[func_count], &timmings[func_count][size_count], block_size); func_count++; + passed[func_count][size_count] = + test_srsran_vec_neg_bbb(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = + test_srsran_vec_neg_bb(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + passed[func_count][size_count] = test_srsran_vec_acc_cc(func_names[func_count], &timmings[func_count][size_count], block_size); func_count++; diff --git a/lib/src/phy/utils/vector.c b/lib/src/phy/utils/vector.c index 8128ed537..8db09fd02 100644 --- a/lib/src/phy/utils/vector.c +++ b/lib/src/phy/utils/vector.c @@ -465,11 +465,19 @@ void srsran_vec_neg_sss(const int16_t* x, const int16_t* y, int16_t* z, const ui { srsran_vec_neg_sss_simd(x, y, z, len); } + void srsran_vec_neg_bbb(const int8_t* x, const int8_t* y, int8_t* z, const uint32_t len) { srsran_vec_neg_bbb_simd(x, y, z, len); } +void srsran_vec_neg_bb(const int8_t* x, int8_t* z, const uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) { + z[i] = -x[i]; + } +} + // CFO and OFDM processing void srsran_vec_prod_ccc(const cf_t* x, const cf_t* y, cf_t* z, const uint32_t len) { diff --git a/lib/test/adt/circular_map_test.cc b/lib/test/adt/circular_map_test.cc index aed0dd88a..9bbb25dae 100644 --- a/lib/test/adt/circular_map_test.cc +++ b/lib/test/adt/circular_map_test.cc @@ -2,7 +2,7 @@ * * \section COPYRIGHT * - * Copyright 2013-2020 Software Radio Systems Limited + * Copyright 2013-2021 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 diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index e280225cb..2a99abea6 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# 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 @@ -52,4 +52,7 @@ add_lte_test(pucch_ca_test pucch_ca_test) add_executable(phy_dl_nr_test phy_dl_nr_test.c) target_link_libraries(phy_dl_nr_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -add_nr_test(phy_dl_nr_test phy_dl_nr_test -p 100 -m 28) \ No newline at end of file +add_nr_test(phy_dl_nr_test phy_dl_nr_test -p 100 -m 28) +add_nr_test(phy_dl_nr_test_rvd phy_dl_nr_test -P 52 -p 52 -m 0 + -R 0 52 1 010010010010 00000000010000 + -R 0 52 1 100100100100 00000010000000) \ No newline at end of file diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index 10898dc08..4f8f958e7 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -20,20 +20,18 @@ #include static srsran_carrier_nr_t carrier = { - 0, // cell_id + 501, // cell_id 0, // numerology - 100, // nof_prb + 52, // nof_prb 0, // start 1 // max_mimo_layers }; -static uint32_t n_prb = 0; // Set to 0 for steering -static uint32_t mcs = 30; // Set to 30 for steering -static srsran_sch_cfg_nr_t pdsch_cfg = {}; -static srsran_sch_grant_nr_t pdsch_grant = {}; -static uint16_t rnti = 0x1234; -static uint32_t nof_slots = 10; +static uint32_t n_prb = 0; // Set to 0 for steering +static uint32_t mcs = 30; // Set to 30 for steering +static srsran_sch_cfg_nr_t pdsch_cfg = {}; +static uint32_t nof_slots = 10; static void usage(char* prog) { @@ -44,6 +42,7 @@ static void usage(char* prog) printf("\t-m MCS PRB, set to >28 for steering [Default %d]\n", mcs); printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n", srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table)); + printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n"); printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers); printf("\t-v [set srsran_verbose to debug, default none]\n"); } @@ -51,7 +50,7 @@ static void usage(char* prog) static int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "PpmnTLv")) != -1) { + while ((opt = getopt(argc, argv, "RPpmnTLv")) != -1) { switch (opt) { case 'P': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -68,6 +67,24 @@ static int parse_args(int argc, char** argv) case 'T': pdsch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(argv[optind]); break; + case 'R': { + srsran_re_pattern_t pattern = {}; + pattern.rb_begin = (uint32_t)strtol(argv[optind++], NULL, 10); + pattern.rb_end = (uint32_t)strtol(argv[optind++], NULL, 10); + pattern.rb_stride = (uint32_t)strtol(argv[optind++], NULL, 10); + uint32_t sc_mask = (uint32_t)strtol(argv[optind++], NULL, 2); + for (uint32_t i = 0; i < SRSRAN_NRE; i++) { + pattern.sc[i] = ((sc_mask >> (SRSRAN_NRE - 1U - i)) & 0x1) == 0x1; + } + uint32_t symbol_mask = (uint32_t)strtol(argv[optind], NULL, 2); + for (uint32_t i = 0; i < SRSRAN_NSYMB_PER_SLOT_NR; i++) { + pattern.symbol[i] = ((symbol_mask >> (SRSRAN_NSYMB_PER_SLOT_NR - 1U - i)) & 0x1) == 0x1; + } + if (srsran_re_pattern_merge(&pdsch_cfg.rvd_re, &pattern) < SRSRAN_ERROR) { + ERROR("Error adding pattern"); + return SRSRAN_ERROR; + } + } break; case 'L': carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10); break; @@ -86,7 +103,6 @@ static int parse_args(int argc, char** argv) static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, srsran_slot_cfg_t* slot, srsran_search_space_t* search_space, - srsran_dci_dl_nr_t* dci_dl, srsran_dci_location_t* dci_location, uint8_t** data_tx) { @@ -96,20 +112,26 @@ static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, } // Hard-coded values - dci_dl->format = srsran_dci_format_nr_1_0; - dci_dl->rnti_type = srsran_rnti_type_c; - dci_dl->location = *dci_location; - dci_dl->search_space = search_space->type; - dci_dl->rnti = rnti; + srsran_dci_dl_nr_t dci_dl = {}; + dci_dl.rnti = pdsch_cfg.grant.rnti; + dci_dl.rnti_type = pdsch_cfg.grant.rnti_type; + dci_dl.format = srsran_dci_format_nr_1_0; + dci_dl.location = *dci_location; + dci_dl.search_space = search_space->type; + dci_dl.coreset_id = 1; + dci_dl.freq_domain_assigment = 0; + dci_dl.time_domain_assigment = 0; + dci_dl.vrb_to_prb_mapping = 0; + dci_dl.mcs = mcs; + dci_dl.rv = 0; // Put actual DCI - if (srsran_enb_dl_nr_pdcch_put(enb_dl, slot, dci_dl) < SRSRAN_SUCCESS) { + if (srsran_enb_dl_nr_pdcch_put(enb_dl, slot, &dci_dl) < SRSRAN_SUCCESS) { ERROR("Error putting PDCCH"); return SRSRAN_ERROR; } // Put PDSCH transmission - pdsch_cfg.grant = pdsch_grant; if (srsran_enb_dl_nr_pdsch_put(enb_dl, slot, &pdsch_cfg, data_tx) < SRSRAN_SUCCESS) { ERROR("Error putting PDSCH"); return SRSRAN_ERROR; @@ -124,8 +146,9 @@ static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot, srsran_ { srsran_ue_dl_nr_estimate_fft(ue_dl, slot); - srsran_dci_dl_nr_t dci_dl_rx = {}; - int nof_found_dci = srsran_ue_dl_nr_find_dl_dci(ue_dl, slot, rnti, srsran_rnti_type_c, &dci_dl_rx, 1); + srsran_dci_dl_nr_t dci_dl_rx = {}; + int nof_found_dci = + srsran_ue_dl_nr_find_dl_dci(ue_dl, slot, pdsch_cfg.grant.rnti, pdsch_cfg.grant.rnti_type, &dci_dl_rx, 1); if (nof_found_dci < SRSRAN_SUCCESS) { ERROR("Error decoding"); return SRSRAN_ERROR; @@ -136,7 +159,6 @@ static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot, srsran_ return SRSRAN_ERROR; } - pdsch_cfg.grant = pdsch_grant; if (srsran_ue_dl_nr_decode_pdsch(ue_dl, slot, &pdsch_cfg, pdsch_res) < SRSRAN_SUCCESS) { ERROR("Error decoding"); return SRSRAN_ERROR; @@ -192,8 +214,8 @@ int main(int argc, char** argv) srsran_ue_dl_nr_pdcch_cfg_t pdcch_cfg = {}; // Configure CORESET - srsran_coreset_t* coreset = &pdcch_cfg.coreset[0]; - pdcch_cfg.coreset_present[0] = true; + srsran_coreset_t* coreset = &pdcch_cfg.coreset[1]; + pdcch_cfg.coreset_present[1] = true; coreset->duration = 2; for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { coreset->freq_resources[i] = i < carrier.nof_prb / 6; @@ -202,7 +224,9 @@ int main(int argc, char** argv) // Configure Search Space srsran_search_space_t* search_space = &pdcch_cfg.search_space[0]; pdcch_cfg.search_space_present[0] = true; - search_space->type = srsran_search_space_type_ue; + search_space->id = 0; + search_space->coreset_id = 1; + search_space->type = srsran_search_space_type_common_3; for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) { search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L); } @@ -264,13 +288,16 @@ int main(int argc, char** argv) } // Use grant default A time resources with m=0 - if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_grant) < SRSRAN_SUCCESS) { + pdsch_cfg.dmrs.typeA_pos = srsran_dmrs_sch_typeA_pos_2; + if (srsran_ra_dl_nr_time_default_A(0, pdsch_cfg.dmrs.typeA_pos, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { ERROR("Error loading default grant"); goto clean_exit; } - pdsch_grant.nof_layers = carrier.max_mimo_layers; - pdsch_grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_grant.nof_dmrs_cdm_groups_without_data = 1; + pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; + pdsch_cfg.grant.rnti_type = srsran_rnti_type_c; + pdsch_cfg.grant.rnti = 0x4601; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; @@ -291,10 +318,11 @@ int main(int argc, char** argv) for (n_prb = n_prb_start; n_prb < n_prb_end; n_prb++) { for (mcs = mcs_start; mcs < mcs_end; mcs++, slot_count++) { for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { - pdsch_grant.prb_idx[n] = (n < n_prb); + pdsch_cfg.grant.prb_idx[n] = (n < n_prb); } + pdsch_cfg.grant.nof_prb = n_prb; - if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_grant, mcs, &pdsch_grant.tb[0]) < SRSRAN_SUCCESS) { + if (srsran_ra_nr_fill_tb(&pdsch_cfg, &pdsch_cfg.grant, mcs, &pdsch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) { ERROR("Error filing tb"); goto clean_exit; } @@ -305,17 +333,17 @@ int main(int argc, char** argv) continue; } - for (uint32_t i = 0; i < pdsch_grant.tb[tb].tbs; i++) { + for (uint32_t i = 0; i < pdsch_cfg.grant.tb[tb].tbs; i++) { data_tx[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); } - pdsch_grant.tb[tb].softbuffer.tx = &softbuffer_tx; + pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx; } // Compute PDCCH candidate locations uint32_t L = 0; uint32_t ncce_candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; - int nof_candidates = - srsran_pdcch_nr_locations_coreset(coreset, search_space, rnti, L, slot.idx, ncce_candidates); + int nof_candidates = srsran_pdcch_nr_locations_coreset( + coreset, search_space, pdsch_cfg.grant.rnti, L, slot.idx, ncce_candidates); if (nof_candidates < SRSRAN_SUCCESS) { ERROR("Error getting PDCCH candidates"); goto clean_exit; @@ -326,11 +354,8 @@ int main(int argc, char** argv) dci_location.ncce = ncce_candidates[0]; dci_location.L = L; - // Setup DCI - srsran_dci_dl_nr_t dci_dl = {}; - gettimeofday(&t[1], NULL); - if (work_gnb_dl(&enb_dl, &slot, search_space, &dci_dl, &dci_location, data_tx) < SRSRAN_ERROR) { + if (work_gnb_dl(&enb_dl, &slot, search_space, &dci_location, data_tx) < SRSRAN_ERROR) { ERROR("Error running eNb DL"); goto clean_exit; } @@ -339,8 +364,8 @@ int main(int argc, char** argv) pdsch_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { - pdsch_grant.tb[tb].softbuffer.rx = &softbuffer_rx; - srsran_softbuffer_rx_reset(pdsch_grant.tb[tb].softbuffer.rx); + pdsch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx; + srsran_softbuffer_rx_reset(pdsch_cfg.grant.tb[tb].softbuffer.rx); } gettimeofday(&t[1], NULL); @@ -358,23 +383,23 @@ int main(int argc, char** argv) } if (!pdsch_res[0].crc) { - ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_grant.tb[0].tbs); + ERROR("Failed to match CRC; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); goto clean_exit; } - if (memcmp(data_tx[0], data_rx[0], pdsch_grant.tb[0].tbs / 8) != 0) { - ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_grant.tb[0].tbs); + if (memcmp(data_tx[0], data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8) != 0) { + ERROR("Failed to match Tx/Rx data; n_prb=%d; mcs=%d; TBS=%d;", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs); printf("Tx data: "); - srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_grant.tb[0].tbs / 8); + srsran_vec_fprint_byte(stdout, data_tx[0], pdsch_cfg.grant.tb[0].tbs / 8); printf("Rx data: "); - srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_grant.tb[0].tbs / 8); + srsran_vec_fprint_byte(stdout, data_rx[0], pdsch_cfg.grant.tb[0].tbs / 8); goto clean_exit; } - INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!", n_prb, mcs, pdsch_grant.tb[0].tbs, pdsch_res[0].evm); + INFO("n_prb=%d; mcs=%d; TBS=%d; EVM=%f; PASSED!", n_prb, mcs, pdsch_cfg.grant.tb[0].tbs, pdsch_res[0].evm); // Count the Tx/Rx'd number of bits - nof_bits += pdsch_grant.tb[0].tbs; + nof_bits += pdsch_cfg.grant.tb[0].tbs; } } } diff --git a/srsenb/src/phy/nr/cc_worker.cc b/srsenb/src/phy/nr/cc_worker.cc index aaaa2b52b..85f3827ea 100644 --- a/srsenb/src/phy/nr/cc_worker.cc +++ b/srsenb/src/phy/nr/cc_worker.cc @@ -125,8 +125,11 @@ int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, ui srsran_sch_cfg_nr_t pdsch_cfg = {}; // Compute DL grant - if (srsran_ra_dl_dci_to_grant_nr(&enb_dl.carrier, &pdsch_hl_cfg, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant)) { + if (srsran_ra_dl_dci_to_grant_nr( + &enb_dl.carrier, &dl_slot_cfg, &pdsch_hl_cfg, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant) < + SRSRAN_SUCCESS) { ERROR("Computing DL grant"); + return false; } // Set soft buffer diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 2b0996ad5..90a70fd42 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -142,11 +142,11 @@ public: * @param tti_rx The TTI in which the grant was received * @param dci_dl The DL DCI message to store */ - void set_dl_pending_grant(uint32_t tti_rx, const srsran_dci_dl_nr_t& dci_dl) + void set_dl_pending_grant(const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl) { // Convert DL DCI to grant srsran_sch_cfg_nr_t pdsch_cfg = {}; - if (srsran_ra_dl_dci_to_grant_nr(&carrier, &cfg.pdsch, &dci_dl, &pdsch_cfg, &pdsch_cfg.grant)) { + if (srsran_ra_dl_dci_to_grant_nr(&carrier, &slot, &cfg.pdsch, &dci_dl, &pdsch_cfg, &pdsch_cfg.grant)) { ERROR("Computing UL grant"); return; } @@ -159,7 +159,7 @@ public: } // Calculate Receive TTI - tti_rx = TTI_ADD(tti_rx, pdsch_cfg.grant.k); + uint32_t tti_rx = TTI_ADD(slot.idx, pdsch_cfg.grant.k); // Scope mutex to protect read/write the list std::lock_guard lock(pending_dl_grant_mutex); diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 685cf7055..e12f8e230 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -143,7 +143,7 @@ void cc_worker::decode_pdcch_dl() } // Enqueue UL grants - phy->set_dl_pending_grant(dl_slot_cfg.idx, dci_rx[i]); + phy->set_dl_pending_grant(dl_slot_cfg, dci_rx[i]); } if (logger.debug.enabled()) {