diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index eb3eb6c2e..cd7d10020 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -64,9 +64,11 @@ public: virtual int sr_detected(uint32_t tti, uint16_t rnti) = 0; virtual int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv) = 0; + virtual int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) = 0; + virtual int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) = 0; virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) = 0; virtual int snr_info(uint32_t tti, uint16_t rnti, float snr_db) = 0; - virtual int ack_info(uint32_t tti, uint16_t rnti, bool ack) = 0; + virtual int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) = 0; virtual int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res) = 0; virtual int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) = 0; @@ -93,7 +95,8 @@ public: class phy_interface_rrc { public: - virtual void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) = 0; + virtual void set_conf_dedicated_ack(uint16_t rnti, bool rrc_completed) = 0; + virtual void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) = 0; }; @@ -111,6 +114,7 @@ public: /* Manages UE bearers and associated configuration */ virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg) = 0; virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; + virtual int set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) = 0; virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0; }; diff --git a/lib/include/srslte/interfaces/sched_interface.h b/lib/include/srslte/interfaces/sched_interface.h index a717b4293..562499dfb 100644 --- a/lib/include/srslte/interfaces/sched_interface.h +++ b/lib/include/srslte/interfaces/sched_interface.h @@ -131,13 +131,14 @@ public: typedef struct { uint32_t rnti; + srslte_dci_format_t dci_format; srslte_ra_dl_dci_t dci; srslte_dci_location_t dci_location; - uint32_t tbs; + uint32_t tbs[SRSLTE_MAX_TB]; bool mac_ce_ta; bool mac_ce_rnti; - uint32_t nof_pdu_elems; - dl_sched_pdu_t pdu[MAX_RLC_PDU_LIST]; + uint32_t nof_pdu_elems[SRSLTE_MAX_TB]; + dl_sched_pdu_t pdu[SRSLTE_MAX_TB][MAX_RLC_PDU_LIST]; } dl_sched_data_t; typedef struct { @@ -225,8 +226,10 @@ public: virtual int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code) = 0; /* DL information */ - virtual int dl_ack_info(uint32_t tti, uint16_t rnti, bool ack) = 0; + virtual int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) = 0; virtual int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size) = 0; + virtual int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) = 0; + virtual int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) = 0; virtual int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) = 0; /* UL information */ diff --git a/lib/include/srslte/phy/enb/enb_dl.h b/lib/include/srslte/phy/enb/enb_dl.h index 8d4f3a73a..f326d4b09 100644 --- a/lib/include/srslte/phy/enb/enb_dl.h +++ b/lib/include/srslte/phy/enb/enb_dl.h @@ -95,10 +95,11 @@ typedef struct SRSLTE_API { typedef struct { uint16_t rnti; + srslte_dci_format_t dci_format; srslte_ra_dl_dci_t grant; srslte_dci_location_t location; - srslte_softbuffer_tx_t *softbuffer; - uint8_t *data; + srslte_softbuffer_tx_t *softbuffers[SRSLTE_MAX_TB]; + uint8_t *data[SRSLTE_MAX_TB]; } srslte_enb_dl_pdsch_t; typedef struct { @@ -162,8 +163,7 @@ SRSLTE_API int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, int rv_idx[SRSLTE_MAX_CODEWORDS], uint32_t sf_idx, uint8_t *data[SRSLTE_MAX_CODEWORDS], - srslte_mimo_type_t mimo_type, - uint32_t pmi); + srslte_mimo_type_t mimo_type); SRSLTE_API int srslte_enb_dl_put_pdcch_dl(srslte_enb_dl_t *q, srslte_ra_dl_dci_t *grant, diff --git a/lib/include/srslte/phy/enb/enb_ul.h b/lib/include/srslte/phy/enb/enb_ul.h index 0957ccbc0..8699a2a37 100644 --- a/lib/include/srslte/phy/enb/enb_ul.h +++ b/lib/include/srslte/phy/enb/enb_ul.h @@ -125,8 +125,7 @@ SRSLTE_API int srslte_enb_ul_cfg_ue(srslte_enb_ul_t *q, uint16_t rnti, srslte_refsignal_srs_cfg_t *srs_cfg); -SRSLTE_API void srslte_enb_ul_fft(srslte_enb_ul_t *q, - cf_t *signal_buffer); +SRSLTE_API void srslte_enb_ul_fft(srslte_enb_ul_t *q); SRSLTE_API int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, uint16_t rnti, @@ -141,6 +140,7 @@ SRSLTE_API int srslte_enb_ul_get_pusch(srslte_enb_ul_t *q, uint32_t rv_idx, uint32_t current_tx_nb, uint8_t *data, + srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data, uint32_t tti); diff --git a/lib/include/srslte/phy/phch/cqi.h b/lib/include/srslte/phy/phch/cqi.h index cfc9e92e7..0d0c17fec 100644 --- a/lib/include/srslte/phy/phch/cqi.h +++ b/lib/include/srslte/phy/phch/cqi.h @@ -55,13 +55,22 @@ typedef struct { } srslte_cqi_periodic_cfg_t; /* Table 5.2.2.6.2-1: Fields for channel quality information feedback for higher layer configured subband -CQI reports -(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and -transmission mode 8 configured without PMI/RI reporting). */ + CQI reports (transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and + transmission mode 8 configured without PMI/RI reporting). */ + +/* Table 5.2.2.6.2-2: Fields for channel quality information (CQI) feedback for higher layer configured subband CQI + reports (transmission mode 4, transmission mode 5 and transmission mode 6). */ + typedef struct SRSLTE_API { - uint8_t wideband_cqi; // 4-bit width - uint32_t subband_diff_cqi; // 2N-bit width - uint32_t N; + uint8_t wideband_cqi_cw0; // 4-bit width + uint32_t subband_diff_cqi_cw0; // 2N-bit width + uint8_t wideband_cqi_cw1; // if RI > 1 then 4-bit width otherwise 0-bit width + uint32_t subband_diff_cqi_cw1; // if RI > 1 then 2N-bit width otherwise 0-bit width + uint32_t pmi; // if RI > 1 then 2-bit width otherwise 1-bit width + uint32_t N; + bool pmi_present; + bool four_antenna_ports; // If cell has 4 antenna ports then true otherwise false + bool rank_is_not_one; // If rank > 1 then true otherwise false } srslte_cqi_hl_subband_t; /* Table 5.2.2.6.3-1: Fields for channel quality information feedback for UE selected subband CQI diff --git a/lib/include/srslte/phy/phch/pucch.h b/lib/include/srslte/phy/phch/pucch.h index 8aa1495c8..29785c09a 100644 --- a/lib/include/srslte/phy/phch/pucch.h +++ b/lib/include/srslte/phy/phch/pucch.h @@ -157,7 +157,8 @@ SRSLTE_API int srslte_pucch_decode(srslte_pucch_t *q, cf_t *sf_symbols, cf_t *ce, float noise_estimate, - uint8_t bits[SRSLTE_PUCCH_MAX_BITS]); + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], + uint32_t nof_bits); SRSLTE_API float srslte_pucch_alpha_format1(uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB], srslte_pucch_cfg_t *cfg, diff --git a/lib/include/srslte/phy/phch/pusch.h b/lib/include/srslte/phy/phch/pusch.h index 2328c8ff3..01db068a5 100644 --- a/lib/include/srslte/phy/phch/pusch.h +++ b/lib/include/srslte/phy/phch/pusch.h @@ -142,6 +142,7 @@ SRSLTE_API int srslte_pusch_decode(srslte_pusch_t *q, float noise_estimate, uint16_t rnti, uint8_t *data, + srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data); SRSLTE_API float srslte_pusch_average_noi(srslte_pusch_t *q); diff --git a/lib/include/srslte/phy/phch/uci.h b/lib/include/srslte/phy/phch/uci.h index ff315384e..6ea00c5d1 100644 --- a/lib/include/srslte/phy/phch/uci.h +++ b/lib/include/srslte/phy/phch/uci.h @@ -57,8 +57,8 @@ typedef struct SRSLTE_API { } srslte_uci_cqi_pusch_t; typedef struct SRSLTE_API { - uint8_t *cqi_table[16]; - int16_t *cqi_table_s[16]; + uint8_t **cqi_table; + int16_t **cqi_table_s; } srslte_uci_cqi_pucch_t; typedef struct SRSLTE_API { @@ -73,6 +73,7 @@ typedef struct SRSLTE_API { uint8_t uci_ack; // 1st codeword bit for HARQ-ACK uint8_t uci_ack_2; // 2st codeword bit for HARQ-ACK uint32_t uci_ack_len; + bool ri_periodic_report; bool scheduling_request; bool channel_selection; bool cqi_ack; @@ -95,6 +96,11 @@ SRSLTE_API int srslte_uci_encode_cqi_pucch(uint8_t *cqi_data, uint32_t cqi_len, uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]); +SRSLTE_API int srslte_uci_encode_cqi_pucch_from_table(srslte_uci_cqi_pucch_t *q, + uint8_t *cqi_data, + uint32_t cqi_len, + uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]); + SRSLTE_API int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t *q, int16_t b_bits[32], // aligned for simd uint8_t *cqi_data, @@ -130,31 +136,25 @@ SRSLTE_API int srslte_uci_encode_ack(srslte_pusch_cfg_t *cfg, uint32_t H_prime_total, srslte_uci_bit_t *ri_bits); -SRSLTE_API int srslte_uci_decode_ack(srslte_pusch_cfg_t *cfg, - int16_t *q_bits, - uint8_t *c_seq, - float beta, - uint32_t H_prime_total, - uint32_t O_cqi, - srslte_uci_bit_t *ack_bits, - uint8_t acks[2], - uint32_t nof_acks); - -SRSLTE_API int srslte_uci_encode_ri(srslte_pusch_cfg_t *cfg, - uint8_t data, +SRSLTE_API int srslte_uci_encode_ack_ri(srslte_pusch_cfg_t *cfg, + uint8_t *data, + uint32_t data_len, uint32_t O_cqi, float beta, uint32_t H_prime_total, - srslte_uci_bit_t *ri_bits); - -SRSLTE_API int srslte_uci_decode_ri(srslte_pusch_cfg_t *cfg, - int16_t *q_bits, - uint8_t *c_seq, - float beta, - uint32_t H_prime_total, - uint32_t O_cqi, srslte_uci_bit_t *ri_bits, - uint8_t *data); + bool is_ri); + +SRSLTE_API int srslte_uci_decode_ack_ri(srslte_pusch_cfg_t *cfg, + int16_t *q_bits, + uint8_t *c_seq, + float beta, + uint32_t H_prime_total, + uint32_t O_cqi, + srslte_uci_bit_t *ack_ri_bits, + uint8_t data[2], + uint32_t nof_bits, + bool is_ri); #endif diff --git a/lib/include/srslte/phy/rf/rf.h b/lib/include/srslte/phy/rf/rf.h index afafc5cf2..44bde9943 100644 --- a/lib/include/srslte/phy/rf/rf.h +++ b/lib/include/srslte/phy/rf/rf.h @@ -73,16 +73,12 @@ SRSLTE_API int srslte_rf_open(srslte_rf_t *h, SRSLTE_API int srslte_rf_open_multi(srslte_rf_t *h, char *args, - uint32_t nof_rx_antennas); + uint32_t nof_channels); SRSLTE_API int srslte_rf_open_devname(srslte_rf_t *h, char *devname, - char *args); - -SRSLTE_API int srslte_rf_open_devname_multi(srslte_rf_t *h, - char *devname, - char *args, - uint32_t nof_rx_antennas); + char *args, + uint32_t nof_channels); SRSLTE_API const char *srslte_rf_name(srslte_rf_t *h); diff --git a/lib/include/srslte/phy/utils/debug.h b/lib/include/srslte/phy/utils/debug.h index 11c7a23df..9405d0865 100644 --- a/lib/include/srslte/phy/utils/debug.h +++ b/lib/include/srslte/phy/utils/debug.h @@ -65,7 +65,7 @@ SRSLTE_API extern int srslte_verbose; #if CMAKE_BUILD_TYPE==Debug /* In debug mode, it prints out the */ -#define ERROR(_fmt, ...) fprintf(stderr, "%s.%d: " _fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define ERROR(_fmt, ...) fprintf(stderr, "\e[31m%s.%d: " _fmt "\e[0m\n", __FILE__, __LINE__, ##__VA_ARGS__) #else #define ERROR(_fmt, ...) fprintf(stderr, "[ERROR in %s]:" _fmt "\n", __FUNCTION__, ##__VA_ARGS__) #endif /* CMAKE_BUILD_TYPE==Debug */ diff --git a/lib/include/srslte/radio/radio.h b/lib/include/srslte/radio/radio.h index 3f961e2aa..6ff0c5100 100644 --- a/lib/include/srslte/radio/radio.h +++ b/lib/include/srslte/radio/radio.h @@ -74,7 +74,7 @@ namespace srslte { agc_enabled = false; }; - bool init(char *args = NULL, char *devname = NULL); + bool init(char *args = NULL, char *devname = NULL, uint32_t nof_channels = 1); void stop(); void reset(); bool start_agc(bool tx_gain_same_rx); @@ -86,9 +86,10 @@ namespace srslte { void set_manual_calibration(rf_cal_t *calibration); void get_time(srslte_timestamp_t *now); - bool tx(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + bool tx_single(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + bool tx(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time); void tx_end(); - bool rx_now(void *buffer, uint32_t nof_samples, srslte_timestamp_t *rxd_time); + bool rx_now(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t *rxd_time); bool rx_at(void *buffer, uint32_t nof_samples, srslte_timestamp_t rx_time); void set_tx_gain(float gain); @@ -166,6 +167,7 @@ namespace srslte { uint32_t tti; bool agc_enabled; + uint32_t saved_nof_channels; char saved_args[128]; char saved_devname[128]; diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c index 98224d740..1e9044202 100644 --- a/lib/src/phy/enb/enb_dl.c +++ b/lib/src/phy/enb/enb_dl.c @@ -31,7 +31,6 @@ #include #include #include -#include #define CURRENT_FFTSIZE srslte_symbol_sz(q->cell.nof_prb) @@ -224,15 +223,19 @@ void srslte_enb_dl_clear_sf(srslte_enb_dl_t *q) void srslte_enb_dl_put_sync(srslte_enb_dl_t *q, uint32_t sf_idx) { if (sf_idx == 0 || sf_idx == 5) { - srslte_pss_put_slot(q->pss_signal, q->sf_symbols[0], q->cell.nof_prb, q->cell.cp); - srslte_sss_put_slot(sf_idx ? q->sss_signal5 : q->sss_signal0, q->sf_symbols[0], - q->cell.nof_prb, SRSLTE_CP_NORM); + for (int p = 0; p < q->cell.nof_ports; p++) { + srslte_pss_put_slot(q->pss_signal, q->sf_symbols[p], q->cell.nof_prb, q->cell.cp); + srslte_sss_put_slot(sf_idx ? q->sss_signal5 : q->sss_signal0, q->sf_symbols[p], + q->cell.nof_prb, SRSLTE_CP_NORM); + } } } void srslte_enb_dl_put_refs(srslte_enb_dl_t *q, uint32_t sf_idx) { - srslte_refsignal_cs_put_sf(q->cell, 0, q->csr_signal.pilots[0][sf_idx], q->sf_symbols[0]); + for (int p = 0; p < q->cell.nof_ports; p++) { + srslte_refsignal_cs_put_sf(q->cell, (uint32_t) p, q->csr_signal.pilots[p / 2][sf_idx], q->sf_symbols[p]); + } } void srslte_enb_dl_put_mib(srslte_enb_dl_t *q, uint32_t tti) @@ -327,11 +330,41 @@ int srslte_enb_dl_put_pdcch_ul(srslte_enb_dl_t *q, srslte_ra_ul_dci_t *grant, int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, srslte_ra_dl_grant_t *grant, srslte_softbuffer_tx_t *softbuffer[SRSLTE_MAX_CODEWORDS], uint16_t rnti, int rv_idx[SRSLTE_MAX_CODEWORDS], uint32_t sf_idx, - uint8_t *data[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type, uint32_t pmi) + uint8_t *data[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type) { + uint32_t pmi = 0; + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(grant); + + /* Translates Precoding Information (pinfo) to Precoding matrix Index (pmi) as 3GPP 36.212 Table 5.3.3.1.5-4 */ + if (mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + switch(nof_tb) { + case 1: + if (grant->pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else if (grant->pinfo > 0 && grant->pinfo < 5) { + pmi = grant->pinfo - 1; + } else { + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + break; + case 2: + if (grant->pinfo < 2) { + pmi = grant->pinfo; + } else { + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + break; + default: + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + } + /* Configure pdsch_cfg parameters */ if (srslte_pdsch_cfg_mimo(&q->pdsch_cfg, q->cell, grant, q->cfi, sf_idx, rv_idx, mimo_type, pmi)) { - fprintf(stderr, "Error configuring PDSCH\n"); + ERROR("Error configuring PDSCH (rnti=0x%04x)", rnti); return SRSLTE_ERROR; } diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index f94eb0277..8ea5dd3e4 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -253,18 +253,22 @@ int srslte_enb_ul_cfg_ue(srslte_enb_ul_t *q, uint16_t rnti, } } -void srslte_enb_ul_fft(srslte_enb_ul_t *q, cf_t *signal_buffer) +void srslte_enb_ul_fft(srslte_enb_ul_t *q) { srslte_ofdm_rx_sf(&q->fft); } int get_pucch(srslte_enb_ul_t *q, uint16_t rnti, uint32_t pdcch_n_cce, uint32_t sf_rx, - srslte_uci_data_t *uci_data, uint8_t bits[SRSLTE_PUCCH_MAX_BITS]) + srslte_uci_data_t *uci_data, uint8_t bits[SRSLTE_PUCCH_MAX_BITS], uint32_t nof_bits) { float noise_power = srslte_chest_ul_get_noise_estimate(&q->chest); srslte_pucch_format_t format = srslte_pucch_get_format(uci_data, q->cell.cp); + if (format == SRSLTE_PUCCH_FORMAT_ERROR) { + fprintf(stderr,"Error getting format\n"); + return SRSLTE_ERROR; + } uint32_t n_pucch = srslte_pucch_get_npucch(pdcch_n_cce, format, uci_data->scheduling_request, &q->users[rnti]->pucch_sched); @@ -273,7 +277,7 @@ int get_pucch(srslte_enb_ul_t *q, uint16_t rnti, return SRSLTE_ERROR; } - int ret_val = srslte_pucch_decode(&q->pucch, format, n_pucch, sf_rx, rnti, q->sf_symbols, q->ce, noise_power, bits); + int ret_val = srslte_pucch_decode(&q->pucch, format, n_pucch, sf_rx, rnti, q->sf_symbols, q->ce, noise_power, bits, nof_bits); if (ret_val < 0) { fprintf(stderr,"Error decoding PUCCH\n"); return SRSLTE_ERROR; @@ -286,16 +290,18 @@ int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, uint16_t rnti, srslte_uci_data_t *uci_data) { uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS]; - - if (q->users[rnti]) { - int ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits); + if (q->users[rnti]) { + uint32_t nof_uci_bits = uci_data->ri_periodic_report ? uci_data->uci_ri_len : (uci_data->uci_cqi_len + + uci_data->uci_dif_cqi_len + + uci_data->uci_pmi_len); + int ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits, nof_uci_bits); // If we are looking for SR and ACK at the same time and ret=0, means there is no SR. // try again to decode ACK only if (uci_data->scheduling_request && uci_data->uci_ack_len && ret_val != 1) { uci_data->scheduling_request = false; - ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits); + ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits, nof_uci_bits); } // update schedulign request @@ -305,12 +311,32 @@ int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, uint16_t rnti, // Save ACK bits if (uci_data->uci_ack_len > 0) { - uci_data->uci_ack = pucch_bits[0]; + uci_data->uci_ack = pucch_bits[0]; + } + + if (uci_data->uci_ack_len > 1) { + uci_data->uci_ack_2 = pucch_bits[1]; } // PUCCH2 CQI bits are decoded inside srslte_pucch_decode() if (uci_data->uci_cqi_len) { memcpy(uci_data->uci_cqi, pucch_bits, uci_data->uci_cqi_len*sizeof(uint8_t)); + } + + if (uci_data->uci_dif_cqi_len) { + memcpy(uci_data->uci_dif_cqi, pucch_bits + uci_data->uci_cqi_len, uci_data->uci_dif_cqi_len*sizeof(uint8_t)); + } + + if (uci_data->uci_pmi_len) { + memcpy(uci_data->uci_pmi, pucch_bits + uci_data->uci_cqi_len + uci_data->uci_dif_cqi_len, + uci_data->uci_pmi_len*sizeof(uint8_t)); + } + + if (uci_data->uci_ri_len) { + uci_data->uci_ri = pucch_bits[0]; /* Assume only one bit of RI */ + } + + if (uci_data->uci_cqi_len || uci_data->uci_ri_len) { if (uci_data->uci_ack_len >= 1) { uci_data->uci_ack = pucch_bits[20]; } @@ -328,7 +354,7 @@ int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, uint16_t rnti, int srslte_enb_ul_get_pusch(srslte_enb_ul_t *q, srslte_ra_ul_grant_t *grant, srslte_softbuffer_rx_t *softbuffer, uint16_t rnti, uint32_t rv_idx, uint32_t current_tx_nb, - uint8_t *data, srslte_uci_data_t *uci_data, uint32_t tti) + uint8_t *data, srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data, uint32_t tti) { if (q->users[rnti]) { if (srslte_pusch_cfg(&q->pusch, @@ -364,6 +390,7 @@ int srslte_enb_ul_get_pusch(srslte_enb_ul_t *q, srslte_ra_ul_grant_t *grant, srs softbuffer, q->sf_symbols, q->ce, noise_power, rnti, data, + cqi_value, uci_data); } diff --git a/lib/src/phy/phch/cqi.c b/lib/src/phy/phch/cqi.c index 0041c3fcb..8589ee51a 100644 --- a/lib/src/phy/phch/cqi.c +++ b/lib/src/phy/phch/cqi.c @@ -44,11 +44,38 @@ *******************************************************/ int srslte_cqi_hl_subband_pack(srslte_cqi_hl_subband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) { - uint8_t *body_ptr = buff; - srslte_bit_unpack(msg->wideband_cqi, &body_ptr, 4); - srslte_bit_unpack(msg->subband_diff_cqi, &body_ptr, 2*msg->N); - - return 4+2*msg->N; + uint8_t *body_ptr = buff; + uint32_t bit_count = 0; + + /* Unpack codeword 0, common for 3GPP 36.212 Tables 5.2.2.6.2-1 and 5.2.2.6.2-2 */ + srslte_bit_unpack(msg->wideband_cqi_cw0, &body_ptr, 4); + srslte_bit_unpack(msg->subband_diff_cqi_cw0, &body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + + /* Unpack codeword 1, 3GPP 36.212 Table 5.2.2.6.2-2 */ + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->wideband_cqi_cw1, &body_ptr, 4); + srslte_bit_unpack(msg->subband_diff_cqi_cw1, &body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + } + + /* If PMI is present, unpack it */ + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + srslte_bit_unpack(msg->pmi, &body_ptr, 4); + bit_count += 4; + } else { + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->pmi, &body_ptr, 1); + bit_count += 1; + } else { + srslte_bit_unpack(msg->pmi, &body_ptr, 2); + bit_count += 2; + } + } + } + + return bit_count; } int srslte_cqi_ue_subband_pack(srslte_cqi_ue_subband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) @@ -98,11 +125,37 @@ int srslte_cqi_value_pack(srslte_cqi_value_t *value, uint8_t buff[SRSLTE_CQI_MAX int srslte_cqi_hl_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_hl_subband_t *msg) { - uint8_t *body_ptr = buff; - msg->wideband_cqi = srslte_bit_pack(&body_ptr, 4); - msg->subband_diff_cqi = srslte_bit_pack(&body_ptr, 2*msg->N); - - return 4+2*msg->N; + uint8_t *body_ptr = buff; + uint32_t bit_count = 0; + + msg->wideband_cqi_cw0 = (uint8_t) srslte_bit_pack(&body_ptr, 4); + msg->subband_diff_cqi_cw0 = srslte_bit_pack(&body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + + /* Unpack codeword 1, 3GPP 36.212 Table 5.2.2.6.2-2 */ + if (msg->rank_is_not_one) { + msg->wideband_cqi_cw1 = (uint8_t) srslte_bit_pack(&body_ptr, 4); + msg->subband_diff_cqi_cw1 = srslte_bit_pack(&body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + } + + /* If PMI is present, unpack it */ + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 4); + bit_count += 4; + } else { + if (msg->rank_is_not_one) { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 1); + bit_count += 1; + } else { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 2); + bit_count += 2; + } + } + } + + return bit_count; } int srslte_cqi_ue_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_ue_subband_t *msg) @@ -146,17 +199,44 @@ int srslte_cqi_value_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_value_ } int srslte_cqi_size(srslte_cqi_value_t *value) { + int size = 0; + switch(value->type) { case SRSLTE_CQI_TYPE_WIDEBAND: - return 4; + size = 4; + break; case SRSLTE_CQI_TYPE_SUBBAND: - return 4+(value->subband.subband_label_2_bits)?2:1; + size = 4 + (value->subband.subband_label_2_bits) ? 2 : 1; + break; case SRSLTE_CQI_TYPE_SUBBAND_UE: - return 4+2+value->subband_ue.L; + size = 4 + 2 + value->subband_ue.L; + break; case SRSLTE_CQI_TYPE_SUBBAND_HL: - return 4+2*value->subband_hl.N; + /* First codeword */ + size += 4 + 2 * value->subband_hl.N; + + /* Add Second codeword if required */ + if (value->subband_hl.rank_is_not_one && value->subband_hl.pmi_present) { + size += 4 + 2 * value->subband_hl.N; + } + + /* Add PMI if required*/ + if (value->subband_hl.pmi_present) { + if (value->subband_hl.four_antenna_ports) { + size += 4; + } else { + if (value->subband_hl.rank_is_not_one) { + size += 1; + } else { + size += 2; + } + } + } + break; + default: + size = SRSLTE_ERROR; } - return -1; + return size; } static bool srslte_cqi_get_N(uint32_t I_cqi_pmi, uint32_t *N_p, uint32_t *N_offset) { diff --git a/lib/src/phy/phch/pdsch.c b/lib/src/phy/phch/pdsch.c index f9899ef99..07249fb54 100644 --- a/lib/src/phy/phch/pdsch.c +++ b/lib/src/phy/phch/pdsch.c @@ -473,7 +473,7 @@ int srslte_pdsch_cfg_mimo(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra for (int cw = 0; cw < SRSLTE_MAX_CODEWORDS; cw++) { if (grant->tb_en[cw]) { if (srslte_cbsegm(&cfg->cb_segm[cw], (uint32_t) cfg->grant.mcs[cw].tbs)) { - fprintf(stderr, "Error computing Codeblock (1) segmentation for TBS=%d\n", cfg->grant.mcs[cw].tbs); + fprintf(stderr, "Error computing Codeword (%d) segmentation for TBS=%d\n", cw, cfg->grant.mcs[cw].tbs); return SRSLTE_ERROR; } } @@ -554,8 +554,14 @@ static int srslte_pdsch_codeword_encode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *c srslte_ra_nbits_t *nbits = &cfg->nbits[tb_idx]; srslte_ra_mcs_t *mcs = &cfg->grant.mcs[tb_idx]; uint32_t rv = cfg->rv[tb_idx]; + bool valid_inputs = true; - if (nbits->nof_bits) { + if (!softbuffer) { + ERROR("Error encoding (TB%d -> CW%d), softbuffer=NULL", tb_idx, codeword_idx); + valid_inputs = false; + } + + if (nbits->nof_bits && valid_inputs) { INFO("Encoding PDSCH SF: %d (TB%d -> CW%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", cfg->sf_idx, tb_idx, codeword_idx, srslte_mod_string(mcs->mod), mcs->tbs, nbits->nof_re, nbits->nof_bits, rv); @@ -577,6 +583,8 @@ static int srslte_pdsch_codeword_encode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *c (uint8_t *) q->e[codeword_idx], q->d[codeword_idx], nbits->nof_bits); + } else { + return SRSLTE_ERROR_INVALID_INPUTS; } return SRSLTE_SUCCESS; diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index f9c6bebc1..b1317de3e 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -173,7 +173,7 @@ srslte_pucch_format_t srslte_pucch_get_format(srslte_uci_data_t *uci_data, srslt { srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR; // No CQI data - if (uci_data->uci_cqi_len == 0) { + if (uci_data->uci_cqi_len == 0 && uci_data->uci_ri_len == 0) { // 1-bit ACK + optional SR if (uci_data->uci_ack_len == 1) { format = SRSLTE_PUCCH_FORMAT_1A; @@ -750,7 +750,7 @@ float srslte_pucch_get_last_corr(srslte_pucch_t* q) /* Equalize, demodulate and decode PUCCH bits according to Section 5.4.1 of 36.211 */ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti, cf_t *sf_symbols, cf_t *ce, float noise_estimate, - uint8_t bits[SRSLTE_PUCCH_MAX_BITS]) + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], uint32_t nof_bits) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && @@ -791,7 +791,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, // Perform ML-decoding float corr=0, corr_max=-1e9; - int b_max = 0; // default bit value, eg. HI is NACK + uint8_t b_max = 0, b2_max = 0; // default bit value, eg. HI is NACK switch(format) { case SRSLTE_PUCCH_FORMAT_1: bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); @@ -808,7 +808,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, case SRSLTE_PUCCH_FORMAT_1A: bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); ret = 0; - for (int b=0;b<2;b++) { + for (uint8_t b=0;b<2;b++) { bits[0] = b; pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp); corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re); @@ -824,6 +824,30 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, q->last_corr = corr_max; bits[0] = b_max; break; + case SRSLTE_PUCCH_FORMAT_1B: + bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); + ret = 0; + for (uint8_t b=0;b<2;b++) { + for (uint8_t b2 = 0; b2 < 2; b2++) { + bits[0] = b; + bits[1] = b2; + pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp); + corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re); + if (corr > corr_max) { + corr_max = corr; + b_max = b; + b2_max = b2; + } + if (corr_max > q->threshold_format1) { // check with format1 in case ack+sr because ack only is binary + ret = 1; + } + DEBUG("format1b b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re); + } + } + q->last_corr = corr_max; + bits[0] = b_max; + bits[1] = b2_max; + break; case SRSLTE_PUCCH_FORMAT_2: case SRSLTE_PUCCH_FORMAT_2A: case SRSLTE_PUCCH_FORMAT_2B: @@ -838,7 +862,7 @@ int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, } srslte_demod_soft_demodulate_s(SRSLTE_MOD_QPSK, q->z, llr_pucch2, SRSLTE_PUCCH2_NOF_BITS/2); srslte_scrambling_s(&q->users[rnti]->seq_f2[sf_idx], llr_pucch2); - q->last_corr = (float) srslte_uci_decode_cqi_pucch(&q->cqi, llr_pucch2, bits, 4)/2000; + q->last_corr = (float) srslte_uci_decode_cqi_pucch(&q->cqi, llr_pucch2, bits, nof_bits)/2000; ret = 1; } else { fprintf(stderr, "Decoding PUCCH2: rnti not set\n"); diff --git a/lib/src/phy/phch/pusch.c b/lib/src/phy/phch/pusch.c index a8c6064fc..7876a4f86 100644 --- a/lib/src/phy/phch/pusch.c +++ b/lib/src/phy/phch/pusch.c @@ -566,9 +566,9 @@ int srslte_pusch_decode(srslte_pusch_t *q, srslte_pusch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, cf_t *sf_symbols, cf_t *ce, float noise_estimate, uint16_t rnti, - uint8_t *data, srslte_uci_data_t *uci_data) + uint8_t *data, srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data) { - + int ret = SRSLTE_ERROR_INVALID_INPUTS; uint32_t n; if (q != NULL && @@ -607,19 +607,42 @@ int srslte_pusch_decode(srslte_pusch_t *q, // Generate scrambling sequence if not pre-generated srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits); + // Set CQI len assuming RI = 1 (3GPP 36.212 Clause 5.2.4.1. Uplink control information on PUSCH without UL-SCH data) + if (cqi_value) { + if (cqi_value->type == SRSLTE_CQI_TYPE_SUBBAND_HL) { + cqi_value->subband_hl.rank_is_not_one = false; + } + uci_data->uci_cqi_len = (uint32_t) srslte_cqi_size(cqi_value); + uci_data->uci_ri_len = (q->cell.nof_ports == 4) ? 2 : 1; + } + // Decode RI/HARQ bits before descrambling if (srslte_ulsch_uci_decode_ri_ack(&q->ul_sch, cfg, softbuffer, q->q, seq->c, uci_data)) { fprintf(stderr, "Error decoding RI/HARQ bits\n"); return SRSLTE_ERROR; } + + // Set CQI len with corresponding RI + if (cqi_value) { + if (cqi_value->type == SRSLTE_CQI_TYPE_SUBBAND_HL) { + cqi_value->subband_hl.rank_is_not_one = (uci_data->uci_ri != 0); + } + uci_data->uci_cqi_len = (uint32_t) srslte_cqi_size(cqi_value); + } // Descrambling srslte_scrambling_s_offset(seq, q->q, 0, cfg->nbits.nof_bits); - - return srslte_ulsch_uci_decode(&q->ul_sch, cfg, softbuffer, q->q, q->g, data, uci_data); - } else { - return SRSLTE_ERROR_INVALID_INPUTS; + + // Decode + ret = srslte_ulsch_uci_decode(&q->ul_sch, cfg, softbuffer, q->q, q->g, data, uci_data); + + // Unpack CQI value if available + if (cqi_value) { + srslte_cqi_value_unpack(uci_data->uci_cqi, cqi_value); + } } + + return ret; } uint32_t srslte_pusch_last_noi(srslte_pusch_t *q) { diff --git a/lib/src/phy/phch/ra.c b/lib/src/phy/phch/ra.c index 17866f5d3..6c2920167 100644 --- a/lib/src/phy/phch/ra.c +++ b/lib/src/phy/phch/ra.c @@ -559,16 +559,16 @@ void srslte_ra_dl_grant_to_nbits(srslte_ra_dl_grant_t *grant, uint32_t cfi, srsl srslte_ra_nbits_t nbits [SRSLTE_MAX_CODEWORDS]) { // Compute number of RE - for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + /* Compute number of RE for first transport block */ nbits[i].nof_re = srslte_ra_dl_grant_nof_re(grant, cell, sf_idx, cell.nof_prb < 10 ? (cfi + 1) : cfi); nbits[i].lstart = cell.nof_prb < 10 ? (cfi + 1) : cfi; + if (SRSLTE_SF_NORM == grant->sf_type) { + nbits[i].nof_symb = 2 * SRSLTE_CP_NSYMB(cell.cp) - nbits[0].lstart; + } else if (SRSLTE_SF_MBSFN == grant->sf_type) { + nbits[i].nof_symb = 2 * SRSLTE_CP_EXT_NSYMB - nbits[0].lstart; + } if (grant->tb_en[i]) { - /* Compute number of RE for first transport block */ - if (SRSLTE_SF_NORM == grant->sf_type) { - nbits[i].nof_symb = 2 * SRSLTE_CP_NSYMB(cell.cp) - nbits[0].lstart; - } else if (SRSLTE_SF_MBSFN == grant->sf_type) { - nbits[i].nof_symb = 2 * SRSLTE_CP_EXT_NSYMB - nbits[0].lstart; - } nbits[i].nof_bits = nbits[i].nof_re * grant->Qm[i]; } } diff --git a/lib/src/phy/phch/sch.c b/lib/src/phy/phch/sch.c index 930364725..1e972594d 100644 --- a/lib/src/phy/phch/sch.c +++ b/lib/src/phy/phch/sch.c @@ -658,7 +658,7 @@ int srslte_ulsch_uci_decode_ri_ack(srslte_sch_t *q, srslte_pusch_cfg_t *cfg, srs if (cfg->cb_segm.tbs == 0) { beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; } - ret = srslte_uci_decode_ack(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, acks, uci_data->uci_ack_len); + ret = srslte_uci_decode_ack_ri(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, acks, uci_data->uci_ack_len, false); if (ret < 0) { return ret; } @@ -678,7 +678,7 @@ int srslte_ulsch_uci_decode_ri_ack(srslte_sch_t *q, srslte_pusch_cfg_t *cfg, srs if (cfg->cb_segm.tbs == 0) { beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; } - ret = srslte_uci_decode_ri(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, &uci_data->uci_ri); + ret = srslte_uci_decode_ack_ri(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, &uci_data->uci_ri, uci_data->uci_ri_len, true); if (ret < 0) { return ret; } @@ -756,13 +756,18 @@ int srslte_ulsch_uci_encode(srslte_sch_t *q, uint32_t nb_q = cfg->nbits.nof_bits; uint32_t Qm = cfg->grant.Qm; - // Encode RI - if (uci_data.uci_ri_len > 0) { + // Encode RI if CQI enabled + if (uci_data.uci_ri_len > 0 || uci_data.uci_cqi_len > 0) { + /* If no RI is reported set it to zero as specified in 3GPP 36.213 clause 7.2.1 */ + if (uci_data.uci_ri_len == 0) { + uci_data.uci_ri = 0; + } float beta = beta_ri_offset[cfg->uci_cfg.I_offset_ri]; if (cfg->cb_segm.tbs == 0) { beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; } - ret = srslte_uci_encode_ri(cfg, uci_data.uci_ri, uci_data.uci_cqi_len, beta, nb_q/Qm, q->ack_ri_bits); + uint8_t ri[2] = {uci_data.uci_ri, 0}; + ret = srslte_uci_encode_ack_ri(cfg, ri, uci_data.uci_ri_len, uci_data.uci_cqi_len, beta, nb_q/Qm, q->ack_ri_bits, true); if (ret < 0) { return ret; } @@ -809,8 +814,8 @@ int srslte_ulsch_uci_encode(srslte_sch_t *q, if (cfg->cb_segm.tbs == 0) { beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; } - ret = srslte_uci_encode_ack(cfg, acks, uci_data.uci_ack_len, uci_data.uci_cqi_len, - beta, nb_q / Qm, &q->ack_ri_bits[Q_prime_ri * Qm]); + ret = srslte_uci_encode_ack_ri(cfg, acks, uci_data.uci_ack_len, uci_data.uci_cqi_len, + beta, nb_q / Qm, &q->ack_ri_bits[Q_prime_ri * Qm], false); if (ret < 0) { return ret; } diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index f1a2bccee..33f236061 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -234,6 +234,7 @@ add_executable(pucch_test pucch_test.c) target_link_libraries(pucch_test srslte_phy) add_test(pucch_test pucch_test) +add_test(pucch_test_uci_cqi_decoder pucch_test -q) ######################################################################## # PRACH TEST diff --git a/lib/src/phy/phch/test/pucch_test.c b/lib/src/phy/phch/test/pucch_test.c index 43aaca953..b1d66c013 100644 --- a/lib/src/phy/phch/test/pucch_test.c +++ b/lib/src/phy/phch/test/pucch_test.c @@ -43,18 +43,20 @@ srslte_cell_t cell = { }; uint32_t subframe = 0; +bool test_cqi_only = false; void usage(char *prog) { printf("Usage: %s [csNnv]\n", prog); printf("\t-c cell id [Default %d]\n", cell.id); printf("\t-s subframe [Default %d]\n", subframe); printf("\t-n nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-q Test CQI encoding/decoding only [Default %s].\n", test_cqi_only?"yes":"no"); printf("\t-v [set verbose to debug, default none]\n"); } void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "csNnv")) != -1) { + while ((opt = getopt(argc, argv, "csNnqv")) != -1) { switch(opt) { case 's': subframe = atoi(argv[optind]); @@ -65,6 +67,9 @@ void parse_args(int argc, char **argv) { case 'c': cell.id = atoi(argv[optind]); break; + case 'q': + test_cqi_only = true; + break; case 'v': srslte_verbose++; break; @@ -75,6 +80,59 @@ void parse_args(int argc, char **argv) { } } +int test_uci_cqi_pucch(void) { + int ret = SRSLTE_SUCCESS; + __attribute__((aligned(256))) uint8_t o_bits[SRSLTE_UCI_MAX_CQI_LEN_PUCCH] = {0}; + __attribute__((aligned(256))) uint8_t e_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) int16_t e_symb[SRSLTE_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) uint8_t d_bits[SRSLTE_UCI_MAX_CQI_LEN_PUCCH] = {0}; + + srslte_uci_cqi_pucch_t uci_cqi_pucch = {0}; + + srslte_uci_cqi_pucch_init(&uci_cqi_pucch); + + for (uint32_t nof_bits = 1; nof_bits <= SRSLTE_UCI_MAX_CQI_LEN_PUCCH-1; nof_bits++) { + for (uint32_t cqi = 0; cqi < (1 <cqi_table[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B*sizeof(int8_t)); - q->cqi_table_s[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B*sizeof(int16_t)); + uint8_t word[16]; + + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; + q->cqi_table = srslte_vec_malloc(nwords * sizeof(int8_t *)); + q->cqi_table_s = srslte_vec_malloc(nwords * sizeof(int16_t *)); + + for (uint32_t w = 0; w < nwords; w++) { + q->cqi_table[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B * sizeof(int8_t)); + q->cqi_table_s[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B * sizeof(int16_t)); uint8_t *ptr = word; - srslte_bit_unpack(w, &ptr, 4); - srslte_uci_encode_cqi_pucch(word, 4, q->cqi_table[w]); - for (int j=0;jcqi_table_s[w][j] = 2*q->cqi_table[w][j]-1; + srslte_bit_unpack(w, &ptr, SRSLTE_UCI_MAX_CQI_LEN_PUCCH); + srslte_uci_encode_cqi_pucch(word, SRSLTE_UCI_MAX_CQI_LEN_PUCCH, q->cqi_table[w]); + for (int j = 0; j < SRSLTE_UCI_CQI_CODED_PUCCH_B; j++) { + q->cqi_table_s[w][j] = (int16_t)(2 * q->cqi_table[w][j] - 1); } } } void srslte_uci_cqi_pucch_free(srslte_uci_cqi_pucch_t *q) { - uint32_t nwords = 16; + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; for (uint32_t w=0;wcqi_table[w]) { free(q->cqi_table[w]); @@ -131,6 +134,8 @@ void srslte_uci_cqi_pucch_free(srslte_uci_cqi_pucch_t *q) { free(q->cqi_table_s[w]); } } + free(q->cqi_table); + free(q->cqi_table_s); } /* Encode UCI CQI/PMI as described in 5.2.3.3 of 36.212 @@ -151,17 +156,32 @@ int srslte_uci_encode_cqi_pucch(uint8_t *cqi_data, uint32_t cqi_len, uint8_t b_b } } +int srslte_uci_encode_cqi_pucch_from_table(srslte_uci_cqi_pucch_t *q, uint8_t *cqi_data, uint32_t cqi_len, uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]) +{ + if (cqi_len <= SRSLTE_UCI_MAX_CQI_LEN_PUCCH) { + bzero(&cqi_data[cqi_len], SRSLTE_UCI_MAX_CQI_LEN_PUCCH - cqi_len); + uint8_t *ptr = cqi_data; + uint32_t packed = srslte_bit_pack(&ptr, SRSLTE_UCI_MAX_CQI_LEN_PUCCH); + memcpy(b_bits, q->cqi_table[packed], SRSLTE_UCI_CQI_CODED_PUCCH_B); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + /* Decode UCI CQI/PMI over PUCCH */ int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t *q, int16_t b_bits[32], uint8_t *cqi_data, uint32_t cqi_len) { - if (cqi_len == 4 && + if (cqi_len < SRSLTE_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) { uint32_t max_w = 0; - int32_t max_corr = INT32_MIN; - for (uint32_t w=0;w<16;w++) { + int32_t max_corr = INT32_MIN; + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; + for (uint32_t w=0;wcqi_table_s[w], b_bits, SRSLTE_UCI_CQI_CODED_PUCCH_B); @@ -172,7 +192,7 @@ int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t *q, int16_t b_bits[32 } // Convert word to bits again uint8_t *ptr = cqi_data; - srslte_bit_unpack(max_w, &ptr, cqi_len); + srslte_bit_unpack(max_w, &ptr, SRSLTE_UCI_MAX_CQI_LEN_PUCCH); INFO("Decoded CQI: w=%d, corr=%d\n", max_w, max_corr); return max_corr; @@ -586,9 +606,7 @@ static uint32_t encode_ri_ack(uint8_t data[2], uint32_t data_len, srslte_uci_bit /* Decode UCI HARQ/ACK bits as described in 5.2.2.6 of 36.212 * Currently only supporting 1-bit HARQ */ -#ifndef MIMO_ENB - -static int32_t decode_ri_ack(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t *pos) +static int32_t decode_ri_ack_1bit(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t *pos) { uint32_t p0 = pos[0].position; uint32_t p1 = pos[1].position; @@ -598,33 +616,8 @@ static int32_t decode_ri_ack(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t * return -(q0+q1); } -int srslte_uci_decode_ack(srslte_pusch_cfg_t *cfg, int16_t *q_bits, uint8_t *c_seq, - float beta, uint32_t H_prime_total, - uint32_t O_cqi, srslte_uci_bit_t *ack_bits, uint8_t acks[2], uint32_t nof_acks) -{ - int32_t rx_ack = 0; - if (beta < 0) { - fprintf(stderr, "Error beta is reserved\n"); - return -1; - } - - uint32_t Qprime = Q_prime_ri_ack(cfg, 1, O_cqi, beta); - - // Use the same interleaver function to get the HARQ bit position - for (uint32_t i=0;igrant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ack_bits[cfg->grant.Qm*i]); - rx_ack += (int32_t) decode_ri_ack(q_bits, c_seq, &ack_bits[cfg->grant.Qm*i]); - } - - if (acks) { - acks[0] = rx_ack>0; - } - return (int) Qprime; -} -#else - -static void decode_ri_ack(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t *pos, uint32_t Qm, int32_t data[3]) +static void decode_ri_ack_2bits(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t *pos, uint32_t Qm, int32_t data[3]) { uint32_t p0 = pos[Qm * 0 + 0].position; uint32_t p1 = pos[Qm * 0 + 1].position; @@ -645,118 +638,91 @@ static void decode_ri_ack(int16_t *q_bits, uint8_t *c_seq, srslte_uci_bit_t *pos data[2] -= q2 + q5; } -int srslte_uci_decode_ack(srslte_pusch_cfg_t *cfg, int16_t *q_bits, uint8_t *c_seq, - float beta, uint32_t H_prime_total, - uint32_t O_cqi, srslte_uci_bit_t *ack_bits, uint8_t acks[2], uint32_t nof_acks) -{ - int32_t acks_sum[3] = {0, 0, 0}; - +/* Encode UCI ACK/RI bits as described in 5.2.2.6 of 36.212 + * Currently only supporting 1-bit RI + */ +int srslte_uci_encode_ack_ri(srslte_pusch_cfg_t *cfg, + uint8_t *data, uint32_t data_len, + uint32_t O_cqi, float beta, uint32_t H_prime_total, + srslte_uci_bit_t *bits, bool ack_ri) { if (beta < 0) { fprintf(stderr, "Error beta is reserved\n"); - return -1; + return -1; } + uint32_t Qprime = Q_prime_ri_ack(cfg, data_len, O_cqi, beta); + srslte_uci_bit_type_t q_encoded_bits[18]; - uint32_t Qprime = Q_prime_ri_ack(cfg, nof_acks, O_cqi, beta); + uint32_t nof_encoded_bits = encode_ri_ack(data, data_len, q_encoded_bits, cfg->grant.Qm); - // Use the same interleaver function to get the HARQ bit position for (uint32_t i = 0; i < Qprime; i++) { - uci_ulsch_interleave_ack_gen(i, cfg->grant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ack_bits[cfg->grant.Qm*i]); - if ((i % 3 == 0) && i > 0) { - decode_ri_ack(q_bits, &c_seq[0], &ack_bits[cfg->grant.Qm*(i-3)], cfg->grant.Qm, acks_sum); + if (ack_ri) { + uci_ulsch_interleave_ri_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &bits[cfg->grant.Qm * i]); + } else { + uci_ulsch_interleave_ack_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &bits[cfg->grant.Qm * i]); } + uci_ulsch_interleave_put(&q_encoded_bits[(i * cfg->grant.Qm) % nof_encoded_bits], + cfg->grant.Qm, + &bits[cfg->grant.Qm * i]); } - if (acks) { - acks[0] = (uint8_t)(acks_sum[0] > 0); - acks[1] = (uint8_t)(acks_sum[1] > 0); - // TODO: Do something with acks_sum[2] - } - return (int) Qprime; -} -#endif - -/* Encode UCI HARQ/ACK bits as described in 5.2.2.6 of 36.212 - * Currently only supporting 1-bit HARQ - */ -int srslte_uci_encode_ack(srslte_pusch_cfg_t *cfg, uint8_t acks[2], uint32_t nof_acks, - uint32_t O_cqi, float beta, uint32_t H_prime_total, - srslte_uci_bit_t *ack_bits) -{ - if (beta < 0) { - fprintf(stderr, "Error beta is reserved\n"); - return -1; - } - - uint32_t Qprime = Q_prime_ri_ack(cfg, nof_acks, O_cqi, beta); - srslte_uci_bit_type_t q_encoded_bits[18]; - - uint32_t nof_encoded_bits = encode_ri_ack(acks, nof_acks, q_encoded_bits, cfg->grant.Qm); - - for (uint32_t i=0;igrant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ack_bits[cfg->grant.Qm*i]); - uci_ulsch_interleave_put(&q_encoded_bits[(i*cfg->grant.Qm)%nof_encoded_bits], cfg->grant.Qm, &ack_bits[cfg->grant.Qm*i]); - } - return (int) Qprime; } -/* Encode UCI RI bits as described in 5.2.2.6 of 36.212 +/* Decode UCI ACK/RI bits as described in 5.2.2.6 of 36.212 * Currently only supporting 1-bit RI */ -int srslte_uci_decode_ri(srslte_pusch_cfg_t *cfg, int16_t *q_bits, uint8_t *c_seq, - float beta, uint32_t H_prime_total, - uint32_t O_cqi, srslte_uci_bit_t *ri_bits, uint8_t *data) +int srslte_uci_decode_ack_ri(srslte_pusch_cfg_t *cfg, int16_t *q_bits, uint8_t *c_seq, + float beta, uint32_t H_prime_total, + uint32_t O_cqi, srslte_uci_bit_t *ack_ri_bits, uint8_t data[2], uint32_t nof_bits, bool is_ri) { - int32_t ri_sum[3] = {0, 0, 0}; - + int32_t sum[3] = {0, 0, 0}; + if (beta < 0) { fprintf(stderr, "Error beta is reserved\n"); - return -1; + return -1; } - uint32_t Qprime = Q_prime_ri_ack(cfg, 1, O_cqi, beta); + uint32_t Qprime = Q_prime_ri_ack(cfg, nof_bits, O_cqi, beta); - // Use the same interleaver function to get the HARQ bit position - for (uint32_t i=0;igrant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ri_bits[cfg->grant.Qm*i]); - if ((i % 3 == 0) && i > 0) { - //decode_ri_ack(q_bits, &c_seq[0], &ri_bits[cfg->grant.Qm*(i-3)], cfg->grant.Qm, ri_sum); - } - } + for (uint32_t i = 0; i < Qprime; i++) { + if (is_ri) { + uci_ulsch_interleave_ri_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &ack_ri_bits[cfg->grant.Qm * i]); + } else { + uci_ulsch_interleave_ack_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &ack_ri_bits[cfg->grant.Qm * i]); - if (data) { - *data = (uint8_t) ((ri_sum[0] + ri_sum[1] + ri_sum[2]) > 0); + } + if (nof_bits == 2 && (i % 3 == 0) && i > 0) { + decode_ri_ack_2bits(q_bits, &c_seq[0], &ack_ri_bits[cfg->grant.Qm * (i - 3)], cfg->grant.Qm, sum); + } else if (nof_bits == 1) { + sum[0] += (int32_t) decode_ri_ack_1bit(q_bits, c_seq, &ack_ri_bits[cfg->grant.Qm * i]); + } } - return (int) Qprime; -} - - -/* Encode UCI RI bits as described in 5.2.2.6 of 36.212 - * Currently only supporting 1-bit RI - */ -int srslte_uci_encode_ri(srslte_pusch_cfg_t *cfg, - uint8_t ri, - uint32_t O_cqi, float beta, uint32_t H_prime_total, - srslte_uci_bit_t *ri_bits) -{ - // FIXME: It supports RI of 1 bit only - uint8_t data[2] = {ri, 0}; - if (beta < 0) { - fprintf(stderr, "Error beta is reserved\n"); - return -1; + data[0] = (uint8_t) (sum[0] > 0); + if (nof_bits == 2) { + data[1] = (uint8_t) (sum[1] > 0); } - uint32_t Qprime = Q_prime_ri_ack(cfg, 1, O_cqi, beta); - srslte_uci_bit_type_t q_encoded_bits[18]; - uint32_t nof_encoded_bits = encode_ri_ack(data, 1, q_encoded_bits, cfg->grant.Qm); - - for (uint32_t i=0;igrant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ri_bits[cfg->grant.Qm*i]); - uci_ulsch_interleave_put(&q_encoded_bits[(i*cfg->grant.Qm)%nof_encoded_bits], cfg->grant.Qm, &ri_bits[cfg->grant.Qm*i]); - } - return (int) Qprime; } - diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index 267370b52..1de66e066 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -99,11 +99,7 @@ const char* srslte_rf_get_devname(srslte_rf_t *rf) { return ((rf_dev_t*) rf->dev)->name; } -int srslte_rf_open_devname(srslte_rf_t *rf, char *devname, char *args) { - return srslte_rf_open_devname_multi(rf, devname, args, 1); -} - -int srslte_rf_open_devname_multi(srslte_rf_t *rf, char *devname, char *args, uint32_t nof_channels) { +int srslte_rf_open_devname(srslte_rf_t *rf, char *devname, char *args, uint32_t nof_channels) { /* Try to open the device if name is provided */ if (devname) { if (devname[0] != '\0') { @@ -187,12 +183,12 @@ void srslte_rf_register_error_handler(srslte_rf_t *rf, srslte_rf_error_handler_t int srslte_rf_open(srslte_rf_t *h, char *args) { - return srslte_rf_open_devname_multi(h, NULL, args, 1); + return srslte_rf_open_devname(h, NULL, args, 1); } -int srslte_rf_open_multi(srslte_rf_t *h, char *args, uint32_t nof_rx_antennas) +int srslte_rf_open_multi(srslte_rf_t *h, char *args, uint32_t nof_channels) { - return srslte_rf_open_devname_multi(h, NULL, args, nof_rx_antennas); + return srslte_rf_open_devname(h, NULL, args, nof_channels); } int srslte_rf_close(srslte_rf_t *rf) diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index 134c19ca6..4183016e0 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -725,7 +725,8 @@ int srslte_ue_dl_ri_pmi_select(srslte_ue_dl_t *q, uint8_t *ri, uint8_t *pmi, flo /* Select the best Rank indicator (RI) and Precoding Matrix Indicator (PMI) */ for (uint32_t nof_layers = 1; nof_layers <= SRSLTE_MAX_LAYERS; nof_layers++) { float _sinr = q->sinr[nof_layers - 1][q->pmi[nof_layers - 1]] * nof_layers * nof_layers; - if (_sinr > best_sinr + 0.1) { + /* Find best SINR, force maximum number of layers if SNR is higher than 30 dB */ + if (_sinr > best_sinr + 0.1 || _sinr > 1.0e+3) { best_sinr = _sinr; best_pmi = (uint8_t) q->pmi[nof_layers - 1]; best_ri = (uint8_t) (nof_layers - 1); @@ -740,6 +741,7 @@ int srslte_ue_dl_ri_pmi_select(srslte_ue_dl_t *q, uint8_t *ri, uint8_t *pmi, flo } /* Set RI */ + q->ri = best_ri; if (ri != NULL) { *ri = best_ri; } @@ -775,9 +777,10 @@ int srslte_ue_dl_ri_select(srslte_ue_dl_t *q, uint8_t *ri, float *cn) { *cn = _cn; } + q->ri = (uint8_t)((_cn < 17.0f)? 1:0); /* Set rank indicator */ if (!ret && ri) { - *ri = (uint8_t)((_cn < 17.0f)? 1:0); + *ri = (uint8_t) q->ri; } return ret; diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index bb4fd57ae..736fc3470 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -273,21 +273,33 @@ int srslte_ue_ul_cfg_grant(srslte_ue_ul_t *q, srslte_ra_ul_grant_t *grant, void pucch_encode_bits(srslte_uci_data_t *uci_data, srslte_pucch_format_t format, uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS], uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS]) -{ +{ + uint8_t uci_buffer[SRSLTE_CQI_MAX_BITS]; + uint8_t uci_buffer_len = 0; + if (format == SRSLTE_PUCCH_FORMAT_1A || format == SRSLTE_PUCCH_FORMAT_1B) { pucch_bits[0] = uci_data->uci_ack; pucch_bits[1] = uci_data->uci_ack_2; // this will be ignored in format 1a } if (format >= SRSLTE_PUCCH_FORMAT_2) { - /* Append Differential CQI */ - memcpy(&uci_data->uci_cqi[uci_data->uci_cqi_len], uci_data->uci_dif_cqi, uci_data->uci_dif_cqi_len); - uci_data->uci_cqi_len += uci_data->uci_dif_cqi_len; + /* Put RI (goes alone) */ + if (uci_data->ri_periodic_report) { + uci_buffer[0] = uci_data->uci_ri; // It assumes only 1 bit of RI + uci_buffer_len += uci_data->uci_ri_len; + } else { + /* Append CQI */ + memcpy(&uci_buffer[uci_buffer_len], uci_data->uci_cqi, uci_data->uci_cqi_len); + uci_buffer_len += uci_data->uci_cqi_len; - /* Append PMI */ - memcpy(&uci_data->uci_cqi[uci_data->uci_cqi_len], uci_data->uci_pmi, uci_data->uci_pmi_len); - uci_data->uci_cqi_len += uci_data->uci_pmi_len; + /* Append Differential CQI */ + memcpy(&uci_buffer[uci_buffer_len], uci_data->uci_dif_cqi, uci_data->uci_dif_cqi_len); + uci_buffer_len += uci_data->uci_dif_cqi_len; - srslte_uci_encode_cqi_pucch(uci_data->uci_cqi, uci_data->uci_cqi_len, pucch_bits); + /* Append PMI */ + memcpy(&uci_buffer[uci_buffer_len], uci_data->uci_pmi, uci_data->uci_pmi_len); + uci_buffer_len += uci_data->uci_pmi_len; + } + srslte_uci_encode_cqi_pucch(uci_buffer, uci_buffer_len, pucch_bits); if (format > SRSLTE_PUCCH_FORMAT_2) { pucch2_bits[0] = uci_data->uci_ack; pucch2_bits[1] = uci_data->uci_ack_2; // this will be ignored in format 2a diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 00272a6a4..f592db71f 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -34,9 +34,9 @@ extern "C" { namespace srslte { -bool radio::init(char *args, char *devname) +bool radio::init(char *args, char *devname, uint32_t nof_channels) { - if (srslte_rf_open_devname(&rf_device, devname, args)) { + if (srslte_rf_open_devname(&rf_device, devname, args, nof_channels)) { fprintf(stderr, "Error opening RF device\n"); return false; } @@ -69,6 +69,7 @@ bool radio::init(char *args, char *devname) if (devname) { strncpy(saved_devname, devname, 127); } + saved_nof_channels = nof_channels; return true; } @@ -83,7 +84,7 @@ void radio::reset() printf("Resetting Radio...\n"); srslte_rf_close(&rf_device); sleep(3); - if (srslte_rf_open_devname(&rf_device, saved_devname, saved_args)) { + if (srslte_rf_open_devname(&rf_device, saved_devname, saved_args, saved_nof_channels)) { fprintf(stderr, "Error opening RF device\n"); } } @@ -138,9 +139,9 @@ bool radio::rx_at(void* buffer, uint32_t nof_samples, srslte_timestamp_t rx_time return false; } -bool radio::rx_now(void* buffer, uint32_t nof_samples, srslte_timestamp_t* rxd_time) +bool radio::rx_now(void* buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t* rxd_time) { - if (srslte_rf_recv_with_time(&rf_device, buffer, nof_samples, true, + if (srslte_rf_recv_with_time_multi(&rf_device, buffer, nof_samples, true, rxd_time?&rxd_time->full_secs:NULL, rxd_time?&rxd_time->frac_secs:NULL) > 0) { return true; } else { @@ -187,10 +188,18 @@ bool radio::is_first_of_burst() { #define BLOCKING_TX true -bool radio::tx(void* buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) -{ - void *iq_samples[4] = {(void *) zeros, (void *) zeros, (void *) zeros, (void *) zeros}; +bool radio::tx_single(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) { + void *_buffer[SRSLTE_MAX_PORTS]; + + _buffer[0] = buffer; + for (int p = 1; p < SRSLTE_MAX_PORTS; p++) { + _buffer[p] = zeros; + } + + return this->tx(_buffer, nof_samples, tx_time); +} +bool radio::tx(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time) { if (!tx_adv_negative) { srslte_timestamp_sub(&tx_time, 0, tx_adv_sec); } else { @@ -203,7 +212,7 @@ bool radio::tx(void* buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) srslte_timestamp_copy(&tx_time_pad, &tx_time); srslte_timestamp_sub(&tx_time_pad, 0, burst_preamble_time_rounded); save_trace(1, &tx_time_pad); - srslte_rf_send_timed_multi(&rf_device, iq_samples, burst_preamble_samples, tx_time_pad.full_secs, tx_time_pad.frac_secs, true, true, false); + srslte_rf_send_timed_multi(&rf_device, buffer, burst_preamble_samples, tx_time_pad.full_secs, tx_time_pad.frac_secs, true, true, false); is_start_of_burst = false; } } @@ -213,8 +222,7 @@ bool radio::tx(void* buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) srslte_timestamp_add(&end_of_burst_time, 0, (double) nof_samples/cur_tx_srate); save_trace(0, &tx_time); - iq_samples[0] = buffer; - int ret = srslte_rf_send_timed_multi(&rf_device, (void**) iq_samples, nof_samples, + int ret = srslte_rf_send_timed_multi(&rf_device, buffer, nof_samples, tx_time.full_secs, tx_time.frac_secs, BLOCKING_TX, is_start_of_burst, false); is_start_of_burst = false; diff --git a/lib/src/radio/radio_multi.cc b/lib/src/radio/radio_multi.cc index 3888a77f3..664458c75 100644 --- a/lib/src/radio/radio_multi.cc +++ b/lib/src/radio/radio_multi.cc @@ -4,7 +4,7 @@ namespace srslte { bool radio_multi::init_multi(uint32_t nof_rx_antennas, char* args, char* devname) { - if (srslte_rf_open_devname_multi(&rf_device, devname, args, nof_rx_antennas)) { + if (srslte_rf_open_devname(&rf_device, devname, args, nof_rx_antennas)) { fprintf(stderr, "Error opening RF device\n"); return false; } diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 7b1681cd6..c40fcebf4 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -66,6 +66,8 @@ typedef struct { s1ap_args_t s1ap; uint32_t n_prb; uint32_t pci; + uint32_t nof_ports; + uint32_t transmission_mode; }enb_args_t; typedef struct { diff --git a/srsenb/hdr/mac/mac.h b/srsenb/hdr/mac/mac.h index d71245e89..4e37d439f 100644 --- a/srsenb/hdr/mac/mac.h +++ b/srsenb/hdr/mac/mac.h @@ -72,9 +72,13 @@ public: int sr_detected(uint32_t tti, uint16_t rnti); int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv); + int set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info); + + int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); + int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value); int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); int snr_info(uint32_t tti, uint16_t rnti, float snr); - int ack_info(uint32_t tti, uint16_t rnti, bool ack); + int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack); int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res); int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res); diff --git a/srsenb/hdr/mac/mac_metrics.h b/srsenb/hdr/mac/mac_metrics.h index 35a65f8ae..e11eda350 100644 --- a/srsenb/hdr/mac/mac_metrics.h +++ b/srsenb/hdr/mac/mac_metrics.h @@ -44,6 +44,8 @@ struct mac_metrics_t int ul_buffer; int dl_buffer; float dl_cqi; + float dl_ri; + float dl_pmi; float phr; }; diff --git a/srsenb/hdr/mac/scheduler.h b/srsenb/hdr/mac/scheduler.h index 231239285..c3ca7d624 100644 --- a/srsenb/hdr/mac/scheduler.h +++ b/srsenb/hdr/mac/scheduler.h @@ -103,8 +103,11 @@ public: int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue); int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code); - int dl_ack_info(uint32_t tti, uint16_t rnti, bool ack); + int dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dedicated); + int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack); int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size); + int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); + int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value); int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc); diff --git a/srsenb/hdr/mac/scheduler_harq.h b/srsenb/hdr/mac/scheduler_harq.h index 2e09136f9..211ec7d8d 100644 --- a/srsenb/hdr/mac/scheduler_harq.h +++ b/srsenb/hdr/mac/scheduler_harq.h @@ -38,50 +38,50 @@ class harq_proc public: void config(uint32_t id, uint32_t max_retx, srslte::log* log_h); void set_max_retx(uint32_t max_retx); - void reset(); + void reset(uint32_t tb_idx); uint32_t get_id(); - bool is_empty(); + bool is_empty(uint32_t tb_idx); - void new_retx(uint32_t tti, int *mcs, int *tbs); + void new_retx(uint32_t tb_idx, uint32_t tti, int *mcs, int *tbs); - bool get_ack(); - void set_ack(bool ack); + bool get_ack(uint32_t tb_idx); + void set_ack(uint32_t tb_idx, bool ack); - uint32_t nof_tx(); - uint32_t nof_retx(); + uint32_t nof_tx(uint32_t tb_idx); + uint32_t nof_retx(uint32_t tb_idx); uint32_t get_tti(); - bool get_ndi(); + bool get_ndi(uint32_t tb_idx); protected: - void new_tx_common(uint32_t tti, int mcs, int tbs); - bool has_pending_retx_common(); + void new_tx_common(uint32_t tb_idx, uint32_t tti, int mcs, int tbs); + bool has_pending_retx_common(uint32_t tb_idx); - bool ack; - bool active; - bool ndi; + bool ack[SRSLTE_MAX_TB]; + bool active[SRSLTE_MAX_TB]; + bool ndi[SRSLTE_MAX_TB]; uint32_t id; uint32_t max_retx; - uint32_t n_rtx; - uint32_t tx_cnt; + uint32_t n_rtx[SRSLTE_MAX_TB]; + uint32_t tx_cnt[SRSLTE_MAX_TB]; int tti; - int last_mcs; - int last_tbs; + int last_mcs[SRSLTE_MAX_TB]; + int last_tbs[SRSLTE_MAX_TB]; srslte::log* log_h; private: - bool ack_received; + bool ack_received[SRSLTE_MAX_TB]; }; class dl_harq_proc : public harq_proc { public: - void new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce); + void new_tx(uint32_t tb_idx, uint32_t tti, int mcs, int tbs, uint32_t n_cce); uint32_t get_rbgmask(); void set_rbgmask(uint32_t new_mask); - bool has_pending_retx(uint32_t tti); - int get_tbs(); + bool has_pending_retx(uint32_t tb_idx, uint32_t tti); + int get_tbs(uint32_t tb_idx); uint32_t get_n_cce(); private: uint32_t rbgmask; diff --git a/srsenb/hdr/mac/scheduler_ue.h b/srsenb/hdr/mac/scheduler_ue.h index c163ed570..448e01954 100644 --- a/srsenb/hdr/mac/scheduler_ue.h +++ b/srsenb/hdr/mac/scheduler_ue.h @@ -68,9 +68,12 @@ public: void ul_phr(int phr); void mac_buffer_state(uint32_t ce_code); void ul_recv_len(uint32_t lcid, uint32_t len); + void set_dl_ant_info(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dedicated); void set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code); + void set_dl_ri(uint32_t tti, uint32_t ri); + void set_dl_pmi(uint32_t tti, uint32_t ri); void set_dl_cqi(uint32_t tti, uint32_t cqi); - int set_ack_info(uint32_t tti, bool ack); + int set_ack_info(uint32_t tti, uint32_t tb_idx, bool ack); void set_ul_crc(uint32_t tti, bool crc_res); /******************************************************* @@ -106,9 +109,12 @@ public: void set_sr(); void unset_sr(); - int generate_format1(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); - int generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request); - + int generate_format1(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format2a(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format2(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request); + + srslte_dci_format_t get_dci_format(); uint32_t get_aggr_level(uint32_t nof_bits); sched_dci_cce_t *get_locations(uint32_t current_cfi, uint32_t sf_idx); @@ -155,6 +161,10 @@ private: ue_bearer_t lch[sched_interface::MAX_LC]; int power_headroom; + uint32_t dl_ri; + uint32_t dl_ri_tti; + uint32_t dl_pmi; + uint32_t dl_pmi_tti; uint32_t dl_cqi; uint32_t dl_cqi_tti; uint32_t cqi_request_tti; @@ -177,7 +187,8 @@ private: ul_harq_proc ul_harq[SCHED_MAX_HARQ_PROC]; bool phy_config_dedicated_enabled; - + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT dl_ant_info; + }; } diff --git a/srsenb/hdr/mac/ue.h b/srsenb/hdr/mac/ue.h index 0f95a1144..3550519e8 100644 --- a/srsenb/hdr/mac/ue.h +++ b/srsenb/hdr/mac/ue.h @@ -75,7 +75,7 @@ public: uint8_t* generate_pdu(sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], uint32_t nof_pdu_elems, uint32_t grant_size); - srslte_softbuffer_tx_t* get_tx_softbuffer(uint32_t harq_process); + srslte_softbuffer_tx_t* get_tx_softbuffer(uint32_t harq_process, uint32_t tb_idx); srslte_softbuffer_rx_t* get_rx_softbuffer(uint32_t tti); bool process_pdus(); @@ -93,6 +93,8 @@ public: void metrics_rx(bool crc, uint32_t tbs); void metrics_tx(bool crc, uint32_t tbs); void metrics_phr(float phr); + void metrics_dl_ri(uint32_t dl_cqi); + void metrics_dl_pmi(uint32_t dl_cqi); void metrics_dl_cqi(uint32_t dl_cqi); @@ -108,6 +110,8 @@ private: uint32_t phr_counter; uint32_t dl_cqi_counter; + uint32_t dl_ri_counter; + uint32_t dl_pmi_counter; mac_metrics_t metrics; srslte::mac_pcap* pcap; @@ -118,9 +122,9 @@ private: uint32_t last_tti; - uint32_t nof_failures; - - const static int NOF_HARQ_PROCESSES = 2*HARQ_DELAY_MS; + uint32_t nof_failures; + + const static int NOF_HARQ_PROCESSES = 2 * HARQ_DELAY_MS * SRSLTE_MAX_TB; srslte_softbuffer_tx_t softbuffer_tx[NOF_HARQ_PROCESSES]; srslte_softbuffer_rx_t softbuffer_rx[NOF_HARQ_PROCESSES]; diff --git a/srsenb/hdr/phy/phch_common.h b/srsenb/hdr/phy/phch_common.h index 6c7c064cf..950fd8fa1 100644 --- a/srsenb/hdr/phy/phch_common.h +++ b/srsenb/hdr/phy/phch_common.h @@ -65,7 +65,7 @@ public: void set_nof_mutex(uint32_t nof_mutex); - void worker_end(uint32_t tx_mutex_cnt, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + void worker_end(uint32_t tx_mutex_cnt, cf_t *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time); // Common objects srslte_cell_t cell; @@ -83,7 +83,7 @@ public: // Map of pending ACKs for each user typedef struct { - bool is_pending[TTIMOD_SZ]; + bool is_pending[TTIMOD_SZ][SRSLTE_MAX_TB]; uint16_t n_pdcch[TTIMOD_SZ]; } pending_ack_t; std::map pending_ack; @@ -91,8 +91,8 @@ public: void ack_add_rnti(uint16_t rnti); void ack_rem_rnti(uint16_t rnti); void ack_clear(uint32_t sf_idx); - void ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t n_pdcch); - bool ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t *last_n_pdcch = NULL); + void ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t tb_idx, uint32_t n_pdcch); + bool ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t tb_idx, uint32_t *last_n_pdcch = NULL); private: std::vector tx_mutex; diff --git a/srsenb/hdr/phy/phch_worker.h b/srsenb/hdr/phy/phch_worker.h index 523fefeec..383c9d583 100644 --- a/srsenb/hdr/phy/phch_worker.h +++ b/srsenb/hdr/phy/phch_worker.h @@ -45,7 +45,7 @@ public: void stop(); void reset(); - cf_t *get_buffer_rx(); + cf_t *get_buffer_rx(uint32_t antenna_idx); void set_time(uint32_t tti, uint32_t tx_mutex_cnt, srslte_timestamp_t tx_time); int add_rnti(uint16_t rnti); @@ -56,13 +56,15 @@ public: int read_ce_abs(float *ce_abs); int read_pusch_d(cf_t *pusch_d); void start_plot(); - + + void set_conf_dedicated_ack(uint16_t rnti, + bool rrc_completed); void set_config_dedicated(uint16_t rnti, srslte_uci_cfg_t *uci_cfg, srslte_pucch_sched_t *pucch_sched, srslte_refsignal_srs_cfg_t *srs_cfg, - uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack); + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated); uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); @@ -87,7 +89,7 @@ private: bool initiated; bool running; - cf_t *signal_buffer_rx; + cf_t *signal_buffer_rx[SRSLTE_MAX_PORTS]; cf_t *signal_buffer_tx[SRSLTE_MAX_PORTS]; uint32_t tti_rx, tti_tx_dl, tti_tx_ul; uint32_t sf_rx, sf_tx, tx_mutex_cnt; @@ -100,13 +102,18 @@ private: // Class to store user information class ue { public: - ue() : I_sr(0), I_sr_en(false), cqi_en(false), pucch_cqi_ack(false), pmi_idx(0), has_grant_tti(0) {bzero(&metrics, sizeof(phy_metrics_t));} + ue() : I_sr(0), I_sr_en(false), cqi_en(false), pucch_cqi_ack(false), pmi_idx(0), has_grant_tti(0), + dedicated_ack(false) {bzero(&metrics, sizeof(phy_metrics_t));} uint32_t I_sr; uint32_t pmi_idx; + uint32_t ri_idx; bool I_sr_en; bool cqi_en; + bool ri_en; bool pucch_cqi_ack; int has_grant_tti; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bool dedicated_ack; uint32_t rnti; srslte_enb_ul_phich_info_t phich_info; void metrics_read(phy_metrics_t *metrics); diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index 9ab1efd74..a1616c268 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -66,6 +66,7 @@ public: static uint32_t tti_to_subf(uint32_t tti); void start_plot(); + void set_conf_dedicated_ack(uint16_t rnti, bool dedicated_ack); void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated); void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); diff --git a/srsenb/hdr/upper/rrc.h b/srsenb/hdr/upper/rrc.h index e0b9dd158..2ea07a749 100644 --- a/srsenb/hdr/upper/rrc.h +++ b/srsenb/hdr/upper/rrc.h @@ -79,6 +79,7 @@ typedef struct { LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB]; LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT mac_cnfg; LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cfg; + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT antenna_info; rrc_cfg_sr_t sr_cfg; rrc_cfg_cqi_t cqi_cfg; rrc_cfg_qci_t qci_cfg[MAX_NOF_QCI]; diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index 82b4e8145..fad7e1528 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -136,7 +136,7 @@ bool enb::init(all_args_t *args_) dev_args = (char*) args->rf.device_args.c_str(); } - if(!radio.init(dev_args, dev_name)) + if(!radio.init(dev_args, dev_name, args->enb.nof_ports)) { printf("Failed to find device %s with args %s\n", args->rf.device_name.c_str(), args->rf.device_args.c_str()); diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 00f01d498..dea3866a7 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -37,7 +37,7 @@ namespace srsenb { int enb::parse_cell_cfg(all_args_t *args, srslte_cell_t *cell) { cell->id = args->enb.pci; cell->cp = SRSLTE_CP_NORM; - cell->nof_ports = 1; + cell->nof_ports = args->enb.nof_ports; cell->nof_prb = args->enb.n_prb; LIBLTE_RRC_PHICH_CONFIG_STRUCT phichcfg; @@ -842,6 +842,30 @@ bool enb::sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t int enb::parse_rr(all_args_t* args, rrc_cfg_t* rrc_cfg) { + + /* Transmission mode config section */ + if (args->enb.transmission_mode < 0 || args->enb.transmission_mode > LIBLTE_RRC_TRANSMISSION_MODE_N_ITEMS) { + ERROR("Invalid transmission mode (%d). Only indexes 1-4 are implemented.\n", args->enb.transmission_mode); + return SRSLTE_ERROR; + } + + bzero(&rrc_cfg->antenna_info, sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + rrc_cfg->antenna_info.tx_mode = (LIBLTE_RRC_TRANSMISSION_MODE_ENUM) (args->enb.transmission_mode - 1); + + if (rrc_cfg->antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3) { + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup = LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_OPEN_LOOP; + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup_present = true; + + rrc_cfg->antenna_info.codebook_subset_restriction_choice = LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM3; + } else if (rrc_cfg->antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup = LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_CLOSED_LOOP; + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup_present = true; + + rrc_cfg->antenna_info.codebook_subset_restriction_choice = LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM4; + rrc_cfg->antenna_info.codebook_subset_restriction = 0b111111; + rrc_cfg->antenna_info.codebook_subset_restriction_present = true; + } + /* MAC config section */ parser::section mac_cnfg("mac_cnfg"); diff --git a/srsenb/src/mac/mac.cc b/srsenb/src/mac/mac.cc index 03e192034..80c5e1ddf 100644 --- a/srsenb/src/mac/mac.cc +++ b/srsenb/src/mac/mac.cc @@ -262,10 +262,10 @@ void mac::rl_ok(uint16_t rnti) } } -int mac::ack_info(uint32_t tti, uint16_t rnti, bool ack) +int mac::ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) { log_h->step(tti); - uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, ack); + uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, tb_idx, ack); ue_db[rnti]->metrics_tx(ack, nof_bytes); if (ack) { @@ -305,11 +305,51 @@ int mac::crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc) } } -int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +int mac::set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) { + log_h->step(tti); + + if (ue_db.count(rnti)) { + scheduler.dl_ant_info(rnti, dl_ant_info); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) { log_h->step(tti); if (ue_db.count(rnti)) { + scheduler.dl_ri_info(tti, rnti, ri_value); + ue_db[rnti]->metrics_dl_ri(ri_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { + scheduler.dl_pmi_info(tti, rnti, pmi_value); + ue_db[rnti]->metrics_dl_pmi(pmi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { scheduler.dl_cqi_info(tti, rnti, cqi_value); ue_db[rnti]->metrics_dl_cqi(cqi_value); } else { @@ -431,43 +471,54 @@ int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) // Copy grant info dl_sched_res->sched_grants[n].rnti = rnti; + dl_sched_res->sched_grants[n].dci_format = sched_result.data[i].dci_format; memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.data[i].dci, sizeof(srslte_ra_dl_dci_t)); memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.data[i].dci_location, sizeof(srslte_dci_location_t)); - - dl_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.harq_process); - - // Get PDU if it's a new transmission - if (sched_result.data[i].nof_pdu_elems > 0) { - dl_sched_res->sched_grants[n].data = ue_db[rnti]->generate_pdu(sched_result.data[i].pdu, - sched_result.data[i].nof_pdu_elems, - sched_result.data[i].tbs); - if (pcap) { - pcap->write_dl_crnti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, rnti, true, tti); + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + dl_sched_res->sched_grants[n].softbuffers[tb] = + ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.harq_process, tb); + + if (sched_result.data[i].nof_pdu_elems[tb] > 0) { + /* Get PDU if it's a new transmission */ + dl_sched_res->sched_grants[n].data[tb] = ue_db[rnti]->generate_pdu(sched_result.data[i].pdu[tb], + sched_result.data[i].nof_pdu_elems[tb], + sched_result.data[i].tbs[tb]); + + if (!dl_sched_res->sched_grants[n].data[tb]) { + Error("Error! PDU was not generated (rnti=0x%04x, tb=%d)\n", rnti, tb); + sched_result.data[i].dci.tb_en[tb] = false; + } + + if (pcap) { + pcap->write_dl_crnti(dl_sched_res->sched_grants[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti); + } + + } else { + /* TB not enabled OR no data to send: set pointers to NULL */ + dl_sched_res->sched_grants[n].data[tb] = NULL; } - - } else { - dl_sched_res->sched_grants[n].data = NULL; } n++; } - + // Copy RAR grants for (uint32_t i=0;isched_grants[n].rnti = sched_result.rar[i].rarnti; + dl_sched_res->sched_grants[n].rnti = sched_result.rar[i].rarnti; + dl_sched_res->sched_grants[n].dci_format = SRSLTE_DCI_FORMAT1A; // Force Format 1A memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.rar[i].dci, sizeof(srslte_ra_dl_dci_t)); memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.rar[i].dci_location, sizeof(srslte_dci_location_t)); // Set softbuffer (there are no retx in RAR but a softbuffer is required) - dl_sched_res->sched_grants[n].softbuffer = &rar_softbuffer_tx; + dl_sched_res->sched_grants[n].softbuffers[0] = &rar_softbuffer_tx; // Assemble PDU - dl_sched_res->sched_grants[n].data = assemble_rar(sched_result.rar[i].grants, sched_result.rar[i].nof_grants, i, sched_result.rar[i].tbs); + dl_sched_res->sched_grants[n].data[0] = assemble_rar(sched_result.rar[i].grants, sched_result.rar[i].nof_grants, i, sched_result.rar[i].tbs); if (pcap) { - pcap->write_dl_ranti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, dl_sched_res->sched_grants[n].rnti, true, tti); + pcap->write_dl_ranti(dl_sched_res->sched_grants[n].data[0], sched_result.data[i].tbs[0], dl_sched_res->sched_grants[n].rnti, true, tti); } n++; @@ -476,26 +527,27 @@ int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) // Copy SI and Paging grants for (uint32_t i=0;isched_grants[n].rnti = (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH ) ? SRSLTE_SIRNTI : SRSLTE_PRNTI; + dl_sched_res->sched_grants[n].rnti = (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH ) ? SRSLTE_SIRNTI : SRSLTE_PRNTI; + dl_sched_res->sched_grants[n].dci_format = SRSLTE_DCI_FORMAT1A; // Force Format 1A memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.bc[i].dci, sizeof(srslte_ra_dl_dci_t)); memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.bc[i].dci_location, sizeof(srslte_dci_location_t)); // Set softbuffer if (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH) { - dl_sched_res->sched_grants[n].softbuffer = &bcch_softbuffer_tx[sched_result.bc[i].index]; - dl_sched_res->sched_grants[n].data = assemble_si(sched_result.bc[i].index); + dl_sched_res->sched_grants[n].softbuffers[0] = &bcch_softbuffer_tx[sched_result.bc[i].index]; + dl_sched_res->sched_grants[n].data[0] = assemble_si(sched_result.bc[i].index); #ifdef WRITE_SIB_PCAP if (pcap) { pcap->write_dl_sirnti(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti); } #endif } else { - dl_sched_res->sched_grants[n].softbuffer = &pcch_softbuffer_tx; - dl_sched_res->sched_grants[n].data = pcch_payload_buffer; + dl_sched_res->sched_grants[n].softbuffers[0] = &pcch_softbuffer_tx; + dl_sched_res->sched_grants[n].data[0] = pcch_payload_buffer; rlc_h->read_pdu_pcch(pcch_payload_buffer, pcch_payload_buffer_len); if (pcap) { - pcap->write_dl_pch(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti); + pcap->write_dl_pch(dl_sched_res->sched_grants[n].data[0], sched_result.bc[i].tbs, true, tti); } } diff --git a/srsenb/src/mac/scheduler.cc b/srsenb/src/mac/scheduler.cc index f477895b5..8d216bba0 100644 --- a/srsenb/src/mac/scheduler.cc +++ b/srsenb/src/mac/scheduler.cc @@ -238,12 +238,25 @@ int sched::dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code) return ret; } -int sched::dl_ack_info(uint32_t tti, uint16_t rnti, bool ack) +int sched::dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) { + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_ant_info(dl_ant_info); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) { pthread_mutex_lock(&mutex); int ret = 0; if (ue_db.count(rnti)) { - ret = ue_db[rnti].set_ack_info(tti, ack); + ret = ue_db[rnti].set_ack_info(tti, tb_idx, ack); } else { Error("User rnti=0x%x not found\n", rnti); ret = -1; @@ -266,12 +279,12 @@ int sched::ul_crc_info(uint32_t tti, uint16_t rnti, bool crc) return ret; } -int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +int sched::dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) { pthread_mutex_lock(&mutex); int ret = 0; if (ue_db.count(rnti)) { - ue_db[rnti].set_dl_cqi(tti, cqi_value); + ue_db[rnti].set_dl_ri(tti, cqi_value); } else { Error("User rnti=0x%x not found\n", rnti); ret = -1; @@ -280,6 +293,34 @@ int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) return ret; } +int sched::dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_pmi(tti, pmi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_cqi(tti, cqi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + int sched::dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size) { for (int i=0;ifirst; dl_harq_proc *h = dl_metric->get_user_allocation(user); + srslte_dci_format_t dci_format = user->get_dci_format(); + data[nof_data_elems].dci_format = dci_format; uint32_t aggr_level = user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, cfg.cell.nof_prb, cfg.cell.nof_ports)); if (h) { @@ -605,13 +648,27 @@ int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]) user->get_locations(current_cfi, sf_idx), aggr_level, user)) { - bool is_newtx = h->is_empty(); - int tbs = user->generate_format1(h, &data[nof_data_elems], current_tti, current_cfi); + bool is_newtx = h->is_empty(0); + int tbs = 0; + switch(dci_format) { + case SRSLTE_DCI_FORMAT1: + tbs = user->generate_format1(h, &data[nof_data_elems], current_tti, current_cfi); + break; + case SRSLTE_DCI_FORMAT2: + tbs = user->generate_format2(h, &data[nof_data_elems], current_tti, current_cfi); + break; + case SRSLTE_DCI_FORMAT2A: + tbs = user->generate_format2a(h, &data[nof_data_elems], current_tti, current_cfi); + break; + default: + Error("DCI format (%d) not implemented\n", dci_format); + } if (tbs >= 0) { - log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, n_rtx=%d, tbs=%d, buffer=%d\n", + log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, n_rtx=%d, tbs=%d, buffer=%d, tb_en={%s,%s}\n", !is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(), - data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, h->nof_retx(), - tbs, user->get_pending_dl_new_data(current_tti)); + data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, h->nof_retx(0) + h->nof_retx(1), + tbs, user->get_pending_dl_new_data(current_tti), data[nof_data_elems].dci.tb_en[0]?"y":"n", + data[nof_data_elems].dci.tb_en[1]?"y":"n"); nof_data_elems++; } else { log_h->warning("SCHED: Error DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, tbs=%d, buffer=%d\n", @@ -620,9 +677,10 @@ int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]) tbs, user->get_pending_dl_new_data(current_tti)); } } else { - h->reset(); - Warning("SCHED: Could not schedule DL DCI for rnti=0x%x, pid=%d, L=%d, nof_candidates=%d\n", - rnti, h->get_id(), aggr_level, user->get_locations(current_cfi, sf_idx)->nof_loc[aggr_level] ); + for(uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + h->reset(tb); + } + Warning("SCHED: Could not schedule DL DCI for rnti=0x%x, pid=%d\n", rnti, h->get_id()); } } } @@ -709,7 +767,7 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched /* Indicate PHICH acknowledgment if needed */ if (h->has_pending_ack()) { - sched_result->phich[nof_phich_elems].phich = h->get_ack()?ul_sched_phich_t::ACK:ul_sched_phich_t::NACK; + sched_result->phich[nof_phich_elems].phich = h->get_ack(0)?ul_sched_phich_t::ACK:ul_sched_phich_t::NACK; sched_result->phich[nof_phich_elems].rnti = rnti; nof_phich_elems++; } @@ -766,8 +824,8 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched if (h) { ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); - bool is_newtx = h->is_empty(); - bool needs_pdcch = h->is_adaptive_retx() && !is_rar; + bool is_newtx = h->is_empty(0); + bool needs_pdcch = !h->is_adaptive_retx() && !is_rar; // Set number of retx if (is_newtx) { @@ -785,7 +843,7 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched user->get_locations(current_cfi, sf_idx), aggr_level)) { - h->reset(); + h->reset(0); log_h->warning("SCHED: Could not schedule UL DCI rnti=0x%x, pid=%d, L=%d\n", rnti, h->get_id(), aggr_level); sched_result->pusch[nof_dci_elems].needs_pdcch = false; @@ -812,7 +870,7 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched is_newtx?"tx":"retx", rnti, h->get_id(), sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce, - alloc.RB_start, alloc.RB_start+alloc.L, h->nof_retx(), sched_result->pusch[nof_dci_elems].tbs, + alloc.RB_start, alloc.RB_start+alloc.L, h->nof_retx(0), sched_result->pusch[nof_dci_elems].tbs, user->get_pending_ul_new_data(current_tti),pending_data_before, user->get_pending_ul_old_data()); nof_dci_elems++; diff --git a/srsenb/src/mac/scheduler_harq.cc b/srsenb/src/mac/scheduler_harq.cc index 88ae6b74d..6e1596850 100644 --- a/srsenb/src/mac/scheduler_harq.cc +++ b/srsenb/src/mac/scheduler_harq.cc @@ -50,7 +50,9 @@ void harq_proc::config(uint32_t id_, uint32_t max_retx_, srslte::log* log_h_) log_h = log_h_; id = id_; max_retx = max_retx_; - ndi = false; + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + ndi[i] = false; + } } void harq_proc::set_max_retx(uint32_t max_retx_) { @@ -63,101 +65,101 @@ uint32_t harq_proc::get_id() return id; } -void harq_proc::reset() +void harq_proc::reset(uint32_t tb_idx) { - active = false; - ack = true; - ack_received = false; - n_rtx = 0; + active[tb_idx] = false; + ack[tb_idx] = true; + ack_received[tb_idx] = false; + n_rtx[tb_idx] = 0; tti = 0; - last_mcs = -1; - last_tbs = -1; - tx_cnt = 0; + last_mcs[tb_idx] = -1; + last_tbs[tb_idx] = -1; + tx_cnt[tb_idx] = 0; } -bool harq_proc::is_empty() +bool harq_proc::is_empty(uint32_t tb_idx) { - return !active || (active && ack && ack_received); + return !active[tb_idx] || (active[tb_idx] && ack[tb_idx] && ack_received[tb_idx]); } -bool harq_proc::has_pending_retx_common() +bool harq_proc::has_pending_retx_common(uint32_t tb_idx) { - return !ack && n_rtx < max_retx; + return !ack[tb_idx] && n_rtx[tb_idx] < max_retx; } uint32_t harq_proc::get_tti() { - return tti; + return (uint32_t) tti; } -bool harq_proc::get_ack() +bool harq_proc::get_ack(uint32_t tb_idx) { - return ack; + return ack[tb_idx]; } -void harq_proc::set_ack(bool ack_) +void harq_proc::set_ack(uint32_t tb_idx, bool ack_) { - ack = ack_; - ack_received = true; - log_h->debug("ACK=%d received pid=%d, n_rtx=%d, max_retx=%d\n", ack_, id, n_rtx, max_retx); - if (n_rtx + 1 >= max_retx) { - Warning("SCHED: discarting TB pid=%d, tti=%d, maximum number of retx exceeded (%d)\n", id, tti, max_retx); - active = false; + ack[tb_idx] = ack_; + ack_received[tb_idx] = true; + log_h->debug("ACK=%d received pid=%d, tb_idx=%d, n_rtx=%d, max_retx=%d\n", ack_, id, tb_idx, n_rtx[tb_idx], max_retx); + if (n_rtx[tb_idx] + 1 >= max_retx) { + Warning("SCHED: discarting TB %d pid=%d, tti=%d, maximum number of retx exceeded (%d)\n", tb_idx, id, tti, max_retx); + active[tb_idx] = false; } } -void harq_proc::new_tx_common(uint32_t tti_, int mcs, int tbs) +void harq_proc::new_tx_common(uint32_t tb_idx, uint32_t tti_, int mcs, int tbs) { - reset(); - ndi = !ndi; + reset(tb_idx); + ndi[tb_idx] = !ndi[tb_idx]; tti = tti_; - tx_cnt++; - last_mcs = mcs; - last_tbs = tbs; + tx_cnt[tb_idx]++; + last_mcs[tb_idx] = mcs; + last_tbs[tb_idx] = tbs; if (max_retx) { - active = true; + active[tb_idx] = true; } else { - active = false; // Can reuse this process if no retx are allowed + active[tb_idx] = false; // Can reuse this process if no retx are allowed } } -void harq_proc::new_retx(uint32_t tti_, int *mcs, int *tbs) +void harq_proc::new_retx(uint32_t tb_idx, uint32_t tti_, int *mcs, int *tbs) { - ack_received = false; + ack_received[tb_idx] = false; tti = tti_; - n_rtx++; + n_rtx[tb_idx]++; if (mcs) { - *mcs = last_mcs; + *mcs = last_mcs[tb_idx]; } if (tbs) { - *tbs = last_tbs; + *tbs = last_tbs[tb_idx]; } } -uint32_t harq_proc::nof_tx() +uint32_t harq_proc::nof_tx(uint32_t tb_idx) { - return tx_cnt; + return tx_cnt[tb_idx]; } -uint32_t harq_proc::nof_retx() +uint32_t harq_proc::nof_retx(uint32_t tb_idx) { - return n_rtx; + return n_rtx[tb_idx]; } -bool harq_proc::get_ndi() +bool harq_proc::get_ndi(uint32_t tb_idx) { - return ndi; + return ndi[tb_idx]; } /****************************************************** * UE::DL HARQ class * ******************************************************/ -void dl_harq_proc::new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce_) +void dl_harq_proc::new_tx(uint32_t tb_idx, uint32_t tti, int mcs, int tbs, uint32_t n_cce_) { n_cce = n_cce_; - new_tx_common(tti, mcs, tbs); + new_tx_common(tb_idx, tti, mcs, tbs); } uint32_t dl_harq_proc::get_n_cce() @@ -175,14 +177,14 @@ void dl_harq_proc::set_rbgmask(uint32_t new_mask) rbgmask = new_mask; } -bool dl_harq_proc::has_pending_retx(uint32_t current_tti) +bool dl_harq_proc::has_pending_retx(uint32_t tb_idx, uint32_t current_tti) { - return srslte_tti_interval(current_tti, tti) >= (2*HARQ_DELAY_MS) && has_pending_retx_common(); + return srslte_tti_interval(current_tti, tti) >= (2*HARQ_DELAY_MS) && has_pending_retx_common(tb_idx); } -int dl_harq_proc::get_tbs() +int dl_harq_proc::get_tbs(uint32_t tb_idx) { - return last_tbs; + return last_tbs[tb_idx]; } @@ -198,13 +200,13 @@ ul_harq_proc::ul_alloc_t ul_harq_proc::get_alloc() void ul_harq_proc::set_alloc(ul_harq_proc::ul_alloc_t alloc) { - is_adaptive = true; + is_adaptive = false; memcpy(&allocation, &alloc, sizeof(ul_alloc_t)); } void ul_harq_proc::same_alloc() { - is_adaptive = false; + is_adaptive = true; } bool ul_harq_proc::is_adaptive_retx() @@ -215,7 +217,7 @@ bool ul_harq_proc::is_adaptive_retx() void ul_harq_proc::new_tx(uint32_t tti_, int mcs, int tbs) { need_ack = true; - new_tx_common(tti_, mcs, tbs); + new_tx_common(0, tti_, mcs, tbs); pending_data = tbs; } @@ -225,10 +227,10 @@ bool ul_harq_proc::has_pending_ack() bool ret = need_ack; // Reset if already received a positive ACK - if (active && ack) { - active = false; + if (active[0] && ack[0]) { + active[0] = false; } - if (!active) { + if (!active[0]) { need_ack = false; } return ret; @@ -238,14 +240,15 @@ bool ul_harq_proc::has_pending_ack() void ul_harq_proc::reset_pending_data() { - if (!active) { + if (!active[0]) { pending_data = 0; } } + uint32_t ul_harq_proc::get_pending_data() { - return pending_data; + return (uint32_t) pending_data; } void ul_harq_proc::set_rar_mcs(uint32_t mcs) diff --git a/srsenb/src/mac/scheduler_metric.cc b/srsenb/src/mac/scheduler_metric.cc index 6c50009f7..4e03d396c 100644 --- a/srsenb/src/mac/scheduler_metric.cc +++ b/srsenb/src/mac/scheduler_metric.cc @@ -224,7 +224,7 @@ void ul_metric_rr::new_tti(std::map &ue_db, uint32_t nof_rb_, nof_users_with_data = 0; for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { sched_ue *user = (sched_ue*) &iter->second; - if (user->get_pending_ul_new_data(current_tti) || !user->get_ul_harq(current_tti)->is_empty()) { + if (user->get_pending_ul_new_data(current_tti) || !user->get_ul_harq(current_tti)->is_empty(0)) { user->ue_idx = nof_users_with_data; nof_users_with_data++; } @@ -296,7 +296,7 @@ ul_harq_proc* ul_metric_rr::get_user_allocation(sched_ue *user) uint32_t pending_data = user->get_pending_ul_new_data(current_tti); ul_harq_proc *h = user->get_ul_harq(current_tti); - if (pending_data || !h->is_empty()) { + if (pending_data || !h->is_empty(0)) { if (nof_users_with_data) { if ((current_tti%nof_users_with_data) != user->ue_idx) { return NULL; @@ -306,7 +306,7 @@ ul_harq_proc* ul_metric_rr::get_user_allocation(sched_ue *user) // Schedule retx if we have space - if (!h->is_empty()) { + if (!h->is_empty(0)) { ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); @@ -325,7 +325,7 @@ ul_harq_proc* ul_metric_rr::get_user_allocation(sched_ue *user) } } // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID - if (h->is_empty()) { + if (h->is_empty(0)) { // Allocate resources based on pending data if (pending_data) { uint32_t pending_rb = user->get_required_prb_ul(pending_data); diff --git a/srsenb/src/mac/scheduler_ue.cc b/srsenb/src/mac/scheduler_ue.cc index 87949179e..84c8f8760 100644 --- a/srsenb/src/mac/scheduler_ue.cc +++ b/srsenb/src/mac/scheduler_ue.cc @@ -104,8 +104,10 @@ void sched_ue::reset() ul_cqi_tti = 0; cqi_request_tti = 0; for (int i=0;iset_ack(crc_res); + get_ul_harq(tti)->set_ack(0, crc_res); +} + +void sched_ue::set_dl_ri(uint32_t tti, uint32_t ri) +{ + dl_ri = ri; + dl_ri_tti = tti; +} + +void sched_ue::set_dl_pmi(uint32_t tti, uint32_t pmi) +{ + dl_pmi = pmi; + dl_pmi_tti = tti; } void sched_ue::set_dl_cqi(uint32_t tti, uint32_t cqi) { - dl_cqi = cqi; - dl_cqi_tti = tti; + dl_cqi = cqi; + dl_cqi_tti = tti; +} + +void sched_ue::set_dl_ant_info(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *d) +{ + memcpy(&dl_ant_info, d, sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); } void sched_ue::set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code) @@ -352,10 +371,10 @@ void sched_ue::tpc_dec() { // Generates a Format1 grant -int sched_ue::generate_format1(dl_harq_proc *h, - sched_interface::dl_sched_data_t *data, - uint32_t tti, - uint32_t cfi) +int sched_ue::generate_format1(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) { srslte_ra_dl_dci_t *dci = &data->dci; bzero(dci, sizeof(srslte_ra_dl_dci_t)); @@ -374,7 +393,7 @@ int sched_ue::generate_format1(dl_harq_proc *h, if (is_first_dl_tx()) { need_conres_ce = true; } - if (h->is_empty()) { + if (h->is_empty(0)) { uint32_t req_bytes = get_pending_dl_new_data(tti); @@ -390,28 +409,28 @@ int sched_ue::generate_format1(dl_harq_proc *h, mcs = fixed_mcs_dl; } - h->new_tx(tti, mcs, tbs, data->dci_location.ncce); + h->new_tx(0, tti, mcs, tbs, data->dci_location.ncce); // Allocate MAC ConRes CE if (need_conres_ce) { - data->pdu[0].lcid = srslte::sch_subh::CON_RES_ID; - data->nof_pdu_elems++; + data->pdu[0][0].lcid = srslte::sch_subh::CON_RES_ID; + data->nof_pdu_elems[0]++; Info("SCHED: Added MAC Contention Resolution CE for rnti=0x%x\n", rnti); } int rem_tbs = tbs; int x = 0; do { - x = alloc_pdu(rem_tbs, &data->pdu[data->nof_pdu_elems]); + x = alloc_pdu(rem_tbs, &data->pdu[0][data->nof_pdu_elems[0]]); rem_tbs -= x; if (x) { - data->nof_pdu_elems++; + data->nof_pdu_elems[0]++; } } while(rem_tbs > 0 && x > 0); Debug("SCHED: Alloc format1 new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes); } else { - h->new_retx(tti, &mcs, &tbs); + h->new_retx(0, tti, &mcs, &tbs); Debug("SCHED: Alloc format1 previous mcs=%d, tbs=%d\n", mcs, tbs); } @@ -419,20 +438,152 @@ int sched_ue::generate_format1(dl_harq_proc *h, if (tbs > 0) { dci->harq_process = h->get_id(); - dci->mcs_idx = mcs; - dci->rv_idx = sched::get_rvidx(h->nof_retx()); - dci->ndi = h->get_ndi(); - dci->tpc_pucch = next_tpc_pucch; + dci->mcs_idx = (uint32_t) mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx(0)); + dci->ndi = h->get_ndi(0); + dci->tpc_pucch = (uint8_t) next_tpc_pucch; next_tpc_pucch = 1; - data->tbs = tbs; - dci->tb_en[0] = true; + data->tbs[0] = (uint32_t) tbs; + data->tbs[1] = 0; + dci->tb_en[0] = true; dci->tb_en[1] = false; } return tbs; } +// Generates a Format2a grant +int sched_ue::generate_format2a(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + bool tb_en[SRSLTE_MAX_TB] = {false}; + srslte_ra_dl_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_dl_dci_t)); + + uint32_t sf_idx = tti%10; + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci->type0_alloc.rbg_bitmask = h->get_rbgmask(); + + uint32_t nof_prb = format1_count_prb(h->get_rbgmask(), cell.nof_prb); + uint32_t nof_ctrl_symbols = cfi + (cell.nof_prb < 10 ? 1 : 0); + srslte_ra_dl_grant_t grant; + srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb); + uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols); + uint32_t req_bytes = get_pending_dl_new_data(tti); + + if (dl_ri == 0) { + if (h->is_empty(1)) { + /* One layer, tb1 buffer is empty, send tb0 only */ + tb_en[0] = true; + } else { + /* One layer, tb1 buffer is not empty, send tb1 only */ + tb_en[1] = true; + } + } else { + /* Two layers, retransmit what TBs that have not been Acknowledged */ + bool no_retx = true; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (!h->is_empty(tb)) { + tb_en[tb] = true; + no_retx = false; + } + } + /* Two layers, no retransmissions... */ + if (no_retx) { + tb_en[0] = true; + tb_en[1] = true; + } + } + + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + int mcs = 0; + int tbs = 0; + + if (tb_en[tb]) { + if (h->is_empty(tb)) { + if (fixed_mcs_dl < 0) { + tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx((uint32_t) srslte_ra_tbs_idx_from_mcs((uint32_t) fixed_mcs_dl), nof_prb) / 8; + mcs = fixed_mcs_dl; + } + + h->new_tx(tb, tti, mcs, tbs, data->dci_location.ncce); + + int rem_tbs = tbs; + int x = 0; + do { + x = alloc_pdu(rem_tbs, &data->pdu[tb][data->nof_pdu_elems[tb]]); + rem_tbs -= x; + if (x) { + data->nof_pdu_elems[tb]++; + } + } while (rem_tbs > 0 && x > 0); + + Debug("SCHED: Alloc format2/2a new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes); + } else { + h->new_retx(tb, tti, &mcs, &tbs); + Debug("SCHED: Alloc format2/2a previous mcs=%d, tbs=%d\n", mcs, tbs); + } + } + + /* Fill DCI TB dedicated fields */ + if (tbs > 0) { + if (tb == 0) { + dci->mcs_idx = (uint32_t) mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx(tb)); + dci->ndi = h->get_ndi(tb); + } else { + dci->mcs_idx_1 = (uint32_t) mcs; + dci->rv_idx_1 = sched::get_rvidx(h->nof_retx(tb)); + dci->ndi_1 = h->get_ndi(tb); + } + data->tbs[tb] = (uint32_t) tbs; + dci->tb_en[tb] = true; + } else { + data->tbs[tb] = 0; + dci->tb_en[tb] = false; + } + + if ( req_bytes > (uint32_t) tbs) { + req_bytes -= tbs; + } else { + req_bytes = 0; + } + } + + /* Fill common fields */ + data->rnti = rnti; + dci->harq_process = h->get_id(); + dci->tpc_pucch = (uint8_t) next_tpc_pucch; + next_tpc_pucch = 1; + + return data->tbs[0] + data->tbs[1]; +} + +// Generates a Format2 grant +int sched_ue::generate_format2(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + /* Call Format 2a (common) */ + int ret = generate_format2a(h, data, tti, cfi); + + /* Compute precoding information */ + if (SRSLTE_RA_DL_GRANT_NOF_TB(&data->dci) == 1) { + data->dci.pinfo = (uint8_t) (dl_pmi + 1) % (uint8_t) 5; + } else { + data->dci.pinfo = (uint8_t) (dl_pmi & 1); + } + + return ret; +} + -int sched_ue::generate_format0(ul_harq_proc *h, +int sched_ue::generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request) @@ -444,12 +595,11 @@ int sched_ue::generate_format0(ul_harq_proc *h, int tbs = 0; ul_harq_proc::ul_alloc_t allocation = h->get_alloc(); - - bool is_newtx = true; + if (h->get_rar_mcs(&mcs)) { tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; h->new_tx(tti, mcs, tbs); - } else if (h->is_empty()) { + } else if (h->is_empty(0)) { uint32_t req_bytes = get_pending_ul_new_data(tti); @@ -464,9 +614,8 @@ int sched_ue::generate_format0(ul_harq_proc *h, h->new_tx(tti, mcs, tbs); - } else { - is_newtx = false; - h->new_retx(tti, &mcs, NULL); + } else { + h->new_retx(0, tti, &mcs, NULL); tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; } @@ -476,13 +625,9 @@ int sched_ue::generate_format0(ul_harq_proc *h, if (tbs > 0) { dci->type2_alloc.L_crb = allocation.L; dci->type2_alloc.RB_start = allocation.RB_start; - dci->rv_idx = sched::get_rvidx(h->nof_retx()); - if (!is_newtx && h->is_adaptive_retx()) { - dci->mcs_idx = 28+dci->rv_idx; - } else { - dci->mcs_idx = mcs; - } - dci->ndi = h->get_ndi(); + dci->mcs_idx = mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx(0)); + dci->ndi = h->get_ndi(0); dci->cqi_request = cqi_request; dci->freq_hop_fl = srslte_ra_ul_dci_t::SRSLTE_RA_PUSCH_HOP_DISABLED; dci->tpc_pusch = next_tpc_pusch; @@ -513,7 +658,7 @@ uint32_t sched_ue::get_max_retx() { bool sched_ue::is_first_dl_tx() { for (int i=0;i 0) { + if (dl_harq[i].nof_tx(0) > 0) { return false; } } @@ -662,7 +807,7 @@ dl_harq_proc* sched_ue::get_pending_dl_harq(uint32_t tti) int oldest_idx=-1; uint32_t oldest_tti = 0; for (int i=0;i oldest_tti) { oldest_idx = i; @@ -683,7 +828,7 @@ dl_harq_proc* sched_ue::get_pending_dl_harq(uint32_t tti) dl_harq_proc* sched_ue::get_empty_dl_harq() { for (int i=0;i(&mnc)->default_value("01"), "Mobile Network Code") ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") - ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") + ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") - + ("enb.nof_ports", bpo::value(&args->enb.nof_ports)->default_value(1), "Number of ports") + ("enb.tm", bpo::value(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)") + ("enb_files.sib_config", bpo::value(&args->enb_files.sib_config)->default_value("sib.conf"), "SIB configuration files") ("enb_files.rr_config", bpo::value(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files") ("enb_files.drb_config", bpo::value(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files") diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc index ec55b0dcf..efea243f1 100644 --- a/srsenb/src/metrics_stdout.cc +++ b/srsenb/src/metrics_stdout.cc @@ -108,8 +108,8 @@ void metrics_stdout::print_metrics() { n_reports = 0; cout << endl; - cout << "------DL-------------------------UL-------------------------------" << endl; - cout << "rnti cqi mcs brate bler snr phr mcs brate bler bsr" << endl; + cout << "------DL-------------------------------UL--------------------------------" << endl; + cout << "rnti cqi ri mcs brate bler snr phr mcs brate bler bsr" << endl; } if (metrics.rrc.n_ues > 0) { @@ -123,6 +123,7 @@ void metrics_stdout::print_metrics() cout << std::hex << metrics.mac[i].rnti << " "; cout << float_to_string(metrics.mac[i].dl_cqi, 2); + cout << float_to_string(metrics.mac[i].dl_ri + 1, 3); cout << float_to_string(metrics.phy[i].dl.mcs, 2); if (metrics.mac[i].tx_brate > 0 && metrics_report_period) { cout << float_to_eng_string((float) metrics.mac[i].tx_brate/metrics_report_period, 2); diff --git a/srsenb/src/phy/phch_common.cc b/srsenb/src/phy/phch_common.cc index 061743274..87184321c 100644 --- a/srsenb/src/phy/phch_common.cc +++ b/srsenb/src/phy/phch_common.cc @@ -30,7 +30,6 @@ #include "phy/txrx.h" #include -#include #define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) #define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) @@ -74,7 +73,7 @@ void phch_common::stop() { } } -void phch_common::worker_end(uint32_t tx_mutex_cnt, cf_t* buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) +void phch_common::worker_end(uint32_t tx_mutex_cnt, cf_t* buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time) { // Wait previous TTIs to be transmitted @@ -84,8 +83,8 @@ void phch_common::worker_end(uint32_t tx_mutex_cnt, cf_t* buffer, uint32_t nof_s pthread_mutex_lock(&tx_mutex[tx_mutex_cnt%nof_mutex]); } - radio->set_tti(tx_mutex_cnt); - radio->tx(buffer, nof_samples, tx_time); + radio->set_tti(tx_mutex_cnt); + radio->tx((void **) buffer, nof_samples, tx_time); // Trigger next transmission pthread_mutex_unlock(&tx_mutex[(tx_mutex_cnt+1)%nof_mutex]); @@ -98,14 +97,18 @@ void phch_common::ack_clear(uint32_t sf_idx) { for(std::map::iterator iter=pending_ack.begin(); iter!=pending_ack.end(); ++iter) { pending_ack_t *p = (pending_ack_t*) &iter->second; - p->is_pending[sf_idx] = false; + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + p->is_pending[sf_idx][tb_idx] = false; + } } } void phch_common::ack_add_rnti(uint16_t rnti) { for (int sf_idx=0;sf_idxcell.nof_prb)*sizeof(cf_t)); - if (!signal_buffer_rx) { - fprintf(stderr, "Error allocating memory\n"); - return; - } - bzero(&signal_buffer_tx, sizeof(cf_t *) * SRSLTE_MAX_PORTS); - signal_buffer_tx[0] = (cf_t*) srslte_vec_malloc(2*SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t)); - if (!signal_buffer_tx[0]) { - fprintf(stderr, "Error allocating memory\n"); - return; + for(int p = 0; p < SRSLTE_MAX_PORTS; p++) { + signal_buffer_rx[p] = (cf_t *) srslte_vec_malloc(2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + if (!signal_buffer_rx[p]) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + bzero(signal_buffer_rx[p], 2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + signal_buffer_tx[p] = (cf_t *) srslte_vec_malloc(2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + if (!signal_buffer_tx[p]) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + bzero(signal_buffer_tx[p], 2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); } if (srslte_enb_dl_init(&enb_dl, signal_buffer_tx, phy->cell.nof_prb)) { fprintf(stderr, "Error initiating ENB DL\n"); @@ -107,7 +110,7 @@ void phch_worker::init(phch_common* phy_, srslte::log *log_h_) fprintf(stderr, "Error initiating ENB DL\n"); return; } - if (srslte_enb_ul_init(&enb_ul, signal_buffer_rx, phy->cell.nof_prb)) { + if (srslte_enb_ul_init(&enb_ul, signal_buffer_rx[0], phy->cell.nof_prb)) { fprintf(stderr, "Error initiating ENB UL\n"); return; } @@ -154,12 +157,12 @@ void phch_worker::stop() srslte_enb_dl_free(&enb_dl); srslte_enb_ul_free(&enb_ul); - if (signal_buffer_rx) { - free(signal_buffer_rx); - } - for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { - if (signal_buffer_tx[i]) { - free(signal_buffer_tx[i]); + for (int p = 0; p < SRSLTE_MAX_PORTS; p++) { + if (signal_buffer_rx[p]) { + free(signal_buffer_rx[p]); + } + if (signal_buffer_tx[p]) { + free(signal_buffer_tx[p]); } } pthread_mutex_unlock(&mutex); @@ -171,9 +174,9 @@ void phch_worker::reset() ue_db.clear(); } -cf_t* phch_worker::get_buffer_rx() +cf_t* phch_worker::get_buffer_rx(uint32_t antenna_idx) { - return signal_buffer_rx; + return signal_buffer_rx[antenna_idx]; } void phch_worker::set_time(uint32_t tti_, uint32_t tx_mutex_cnt_, srslte_timestamp_t tx_time_) @@ -214,12 +217,29 @@ uint32_t phch_worker::get_nof_rnti() { return ue_db.size(); } +void phch_worker::set_conf_dedicated_ack(uint16_t rnti, bool ack){ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + ue_db[rnti].dedicated_ack = ack; + } else { + Error("Setting dedicated ack: rnti=0x%x does not exist\n"); + } + pthread_mutex_unlock(&mutex); +} + void phch_worker::set_config_dedicated(uint16_t rnti, srslte_uci_cfg_t *uci_cfg, srslte_pucch_sched_t *pucch_sched, - srslte_refsignal_srs_cfg_t *srs_cfg, - uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack) + srslte_refsignal_srs_cfg_t *srs_cfg, + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) { + uint32_t I_sr = dedicated->sched_request_cnfg.sr_cnfg_idx; + bool pucch_cqi = dedicated->cqi_report_cnfg.report_periodic_setup_present; + uint32_t pmi_idx = dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx; + bool pucch_cqi_ack = dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi; + bool pucch_ri = dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present; + uint32_t ri_idx = dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx; + pthread_mutex_lock(&mutex); if (ue_db.count(rnti)) { pucch_sched->N_pucch_1 = phy->pucch_cfg.n1_pucch_an; @@ -237,6 +257,16 @@ void phch_worker::set_config_dedicated(uint16_t rnti, ue_db[rnti].cqi_en = false; } + if (pucch_ri) { + ue_db[rnti].ri_idx = ri_idx; + ue_db[rnti].ri_en = true; + } else { + ue_db[rnti].ri_idx = 0; + ue_db[rnti].ri_en = false; + } + + /* Copy all dedicated RRC configuration to UE */ + memcpy(&ue_db[rnti].dedicated, dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); } else { Error("Setting config dedicated: rnti=0x%x does not exist\n"); } @@ -278,7 +308,7 @@ void phch_worker::work_imp() } pthread_mutex_lock(&mutex); - + mac_interface_phy::ul_sched_t *ul_grants = phy->ul_grants; mac_interface_phy::dl_sched_t *dl_grants = phy->dl_grants; mac_interface_phy *mac = phy->mac; @@ -293,7 +323,7 @@ void phch_worker::work_imp() } // Process UL signal - srslte_enb_ul_fft(&enb_ul, signal_buffer_rx); + srslte_enb_ul_fft(&enb_ul); // Decode pending UL grants for the tti they were scheduled decode_pusch(ul_grants[t_rx].sched_grants, ul_grants[t_rx].nof_grants); @@ -335,15 +365,22 @@ void phch_worker::work_imp() phy->ack_clear(TTIMOD(TTI_TX(t_tx_dl))); for (uint32_t i=0;i= SRSLTE_CRNTI_START && dl_grants[t_tx_dl].sched_grants[i].rnti <= SRSLTE_CRNTI_END) { - phy->ack_set_pending(TTIMOD(TTI_TX(t_tx_dl)), dl_grants[t_tx_dl].sched_grants[i].rnti, dl_grants[t_tx_dl].sched_grants[i].location.ncce); + uint16_t rnti = dl_grants[t_tx_dl].sched_grants[i].rnti; + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + /* For each TB */ + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + /* If TB enabled, set pending ACK */ + if (dl_grants[t_tx_dl].sched_grants[i].grant.tb_en[tb_idx]) { + phy->ack_set_pending(TTIMOD(TTI_TX(t_tx_dl)), rnti, tb_idx, dl_grants[t_tx_dl].sched_grants[i].location.ncce); + } + } } } // Generate signal and transmit srslte_enb_dl_gen_signal(&enb_dl); Debug("Sending to radio\n"); - phy->worker_end(tx_mutex_cnt, signal_buffer_tx[0], SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time); + phy->worker_end(tx_mutex_cnt, signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time); #ifdef DEBUG_WRITE_FILE fwrite(signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t), 1, f); @@ -387,24 +424,39 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) gettimeofday(&t[1], NULL); #endif + bool acks_pending[SRSLTE_MAX_TB] = {false}; + // Get pending ACKs with an associated PUSCH transmission - if (phy->ack_is_pending(t_rx, rnti)) { - uci_data.uci_ack_len = 1; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + acks_pending[tb] = phy->ack_is_pending(t_rx, rnti, tb); + if (acks_pending[tb]) { + uci_data.uci_ack_len++; + } } + // Configure PUSCH CQI channel - srslte_cqi_value_t cqi_value; + srslte_cqi_value_t cqi_value = {0}; bool cqi_enabled = false; - if (ue_db[rnti].cqi_en && srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { +#if 0 + if (ue_db[rnti].cqi_en && ue_db[rnti].ri_en && srslte_ri_send(ue_db[rnti].pmi_idx, ue_db[rnti].ri_idx, tti_rx) ) { + uci_data.uci_ri_len = 1; /* Asumes only 1 bit for RI */ + ri_enabled = true; + } else if (ue_db[rnti].cqi_en && srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; cqi_enabled = true; - } else if (grants[i].grant.cqi_request) { + if (ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + //uci_data.uci_dif_cqi_len = 3; + uci_data.uci_pmi_len = 2; + } + } else +#endif + if (grants[i].grant.cqi_request) { cqi_value.type = SRSLTE_CQI_TYPE_SUBBAND_HL; cqi_value.subband_hl.N = (phy->cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(phy->cell.nof_prb) : 0; + cqi_value.subband_hl.four_antenna_ports = (phy->cell.nof_ports == 4); + cqi_value.subband_hl.pmi_present = (ue_db[rnti].dedicated.cqi_report_cnfg.report_mode_aperiodic == LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31); cqi_enabled = true; } - if (cqi_enabled) { - uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value); - } // mark this tti as having an ul grant to avoid pucch ue_db[rnti].has_grant_tti = tti_rx; @@ -436,6 +488,7 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) rnti, grants[i].rv_idx, grants[i].current_tx_nb, grants[i].data, + (cqi_enabled) ? &cqi_value : NULL, &uci_data, sf_rx); } else { @@ -457,11 +510,24 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) char cqi_str[64]; if (cqi_enabled) { - srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); if (ue_db[rnti].cqi_en) { wideband_cqi_value = cqi_value.wideband.wideband_cqi; } else if (grants[i].grant.cqi_request) { - wideband_cqi_value = cqi_value.subband_hl.wideband_cqi; + wideband_cqi_value = cqi_value.subband_hl.wideband_cqi_cw0; + if (cqi_value.subband_hl.pmi_present) { + if (cqi_value.subband_hl.rank_is_not_one) { + Info("PUSCH: Aperiodic ri~1, CQI=%02d/%02d, pmi=%d for %d subbands\n", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.wideband_cqi_cw1, + cqi_value.subband_hl.pmi, cqi_value.subband_hl.N); + } else { + Info("PUSCH: Aperiodic ri=1, CQI=%02d, pmi=%d for %d subbands\n", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.pmi, cqi_value.subband_hl.N); + } + } else { + Info("PUSCH: Aperiodic ri%s, CQI=%02d for %d subbands\n", + cqi_value.subband_hl.rank_is_not_one?"~1":"=1", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.N); + } } snprintf(cqi_str, 64, ", cqi=%d", wideband_cqi_value); } @@ -481,14 +547,16 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) } */ log_h->info_hex(grants[i].data, phy_grant.mcs.tbs/8, - "PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s\n", + "PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s%s\n", rnti, phy_grant.n_prb[0], phy_grant.n_prb[0]+phy_grant.L_prb, phy_grant.mcs.tbs/8, phy_grant.mcs.idx, grants[i].grant.rv_idx, snr_db, srslte_pusch_last_noi(&enb_ul.pusch), crc_res?"OK":"KO", - uci_data.uci_ack_len>0?(uci_data.uci_ack?", ack=1":", ack=0"):"", + (uci_data.uci_ack_len)?(uci_data.uci_ack?"1":"0"):"", + (uci_data.uci_ack_len > 1)?(uci_data.uci_ack_2?"1":"0"):"", uci_data.uci_cqi_len>0?cqi_str:"", + uci_data.uci_ri_len>0?(uci_data.uci_ri?", ri=0":", ri=1"):"", timestr); // Notify MAC of RL status @@ -503,17 +571,28 @@ int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) // Notify MAC new received data and HARQ Indication value phy->mac->crc_info(tti_rx, rnti, phy_grant.mcs.tbs/8, crc_res); - if (uci_data.uci_ack_len) { - phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (crc_res || snr_db > PUSCH_RL_SNR_DB_TH)); + uint32_t ack_idx = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (acks_pending[tb]) { + bool ack = ((ack_idx++ == 0) ? uci_data.uci_ack : uci_data.uci_ack_2); + bool valid = (crc_res || snr_db > PUSCH_RL_SNR_DB_TH); + phy->mac->ack_info(tti_rx, rnti, tb, ack && valid); + } } - // Notify MAC of UL SNR and DL CQI + // Notify MAC of UL SNR, DL CQI and DL RI if (snr_db >= PUSCH_RL_SNR_DB_TH) { phy->mac->snr_info(tti_rx, rnti, snr_db); } if (uci_data.uci_cqi_len>0 && crc_res) { phy->mac->cqi_info(tti_rx, rnti, wideband_cqi_value); } + if (uci_data.uci_ri_len > 0 && crc_res) { + phy->mac->ri_info(tti_rx, rnti, uci_data.uci_ri); + } + if (cqi_value.subband_hl.pmi_present && crc_res) { + phy->mac->pmi_info(tti_rx, rnti, cqi_value.subband_hl.pmi); + } // Save metrics stats ue_db[rnti].metrics_ul(phy_grant.mcs.idx, 0, snr_db, srslte_pusch_last_noi(&enb_ul.pusch)); @@ -532,7 +611,8 @@ int phch_worker::decode_pucch() if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END && ue_db[rnti].has_grant_tti != (int) tti_rx) { // Check if user needs to receive PUCCH - bool needs_pucch = false, needs_ack=false, needs_sr=false, needs_cqi=false; + bool needs_pucch = false, needs_ack[SRSLTE_MAX_TB] = {false}, needs_sr = false, needs_cqi = false, + needs_ri = false; uint32_t last_n_pdcch = 0; bzero(&uci_data, sizeof(srslte_uci_data_t)); @@ -544,18 +624,32 @@ int phch_worker::decode_pucch() } } - if (phy->ack_is_pending(t_rx, rnti, &last_n_pdcch)) { - needs_pucch = true; - needs_ack = true; - uci_data.uci_ack_len = 1; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + needs_ack[tb] = phy->ack_is_pending(t_rx, rnti, tb, &last_n_pdcch); + if (needs_ack[tb]) { + needs_pucch = true; + uci_data.uci_ack_len++; + } } srslte_cqi_value_t cqi_value; - if (ue_db[rnti].cqi_en && (ue_db[rnti].pucch_cqi_ack || !needs_ack)) { - if (srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated = &ue_db[rnti].dedicated; + LIBLTE_RRC_TRANSMISSION_MODE_ENUM tx_mode = dedicated->antenna_info_explicit_value.tx_mode; + + if (ue_db[rnti].cqi_en && (ue_db[rnti].pucch_cqi_ack || !needs_ack[0] || !needs_ack[1])) { + if (ue_db[rnti].ri_en && srslte_ri_send(ue_db[rnti].pmi_idx, ue_db[rnti].ri_idx, tti_rx)) { + needs_pucch = true; + needs_ri = true; + uci_data.uci_ri_len = 1; + uci_data.ri_periodic_report = true; + } else if (srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { needs_pucch = true; needs_cqi = true; cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value); + if (tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + //uci_data.uci_dif_cqi_len = 3; + uci_data.uci_pmi_len = 2; + } } } @@ -564,26 +658,48 @@ int phch_worker::decode_pucch() fprintf(stderr, "Error getting PUCCH\n"); return SRSLTE_ERROR; } - if (uci_data.uci_ack_len > 0) { - phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (srslte_pucch_get_last_corr(&enb_ul.pucch) >= PUCCH_RL_CORR_TH)); + /* If only one ACK is required, it can be for TB0 or TB1 */ + uint32_t ack_idx = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (needs_ack[tb]) { + bool ack = ((ack_idx++ == 0) ? uci_data.uci_ack : uci_data.uci_ack_2); + bool valid = srslte_pucch_get_last_corr(&enb_ul.pucch) >= PUCCH_RL_CORR_TH; + phy->mac->ack_info(tti_rx, rnti, tb, ack && valid); + } } if (uci_data.scheduling_request) { phy->mac->sr_detected(tti_rx, rnti); } + + char cqi_ri_str[64]; + if (srslte_pucch_get_last_corr(&enb_ul.pucch) > PUCCH_RL_CORR_TH) { + if (uci_data.uci_ri_len && needs_ri) { + phy->mac->ri_info(tti_rx, rnti, uci_data.uci_ri); + sprintf(cqi_ri_str, ", ri=%d", uci_data.uci_ri); + } else if (uci_data.uci_cqi_len && needs_cqi) { + srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); + phy->mac->cqi_info(tti_rx, rnti, cqi_value.wideband.wideband_cqi); + sprintf(cqi_ri_str, ", cqi=%d", cqi_value.wideband.wideband_cqi); + + if (uci_data.uci_pmi_len) { + uint32_t packed_pmi = uci_data.uci_pmi[0]; + if (uci_data.uci_pmi_len > 1) { + packed_pmi = (packed_pmi << 1) + uci_data.uci_pmi[1]; + } + phy->mac->pmi_info(tti_rx, rnti, packed_pmi); + sprintf(cqi_ri_str, "%s, pmi=%c", cqi_ri_str, packed_pmi + 0x30); + } - char cqi_str[64]; - if (uci_data.uci_cqi_len) { - srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); - phy->mac->cqi_info(tti_rx, rnti, cqi_value.wideband.wideband_cqi); - sprintf(cqi_str, ", cqi=%d", cqi_value.wideband.wideband_cqi); + } } - log_h->info("PUCCH: rnti=0x%x, corr=%.2f, n_pucch=%d, n_prb=%d%s%s%s\n", + log_h->info("PUCCH: rnti=0x%x, corr=%.2f, n_pucch=%d, n_prb=%d%s%s%s%s\n", rnti, srslte_pucch_get_last_corr(&enb_ul.pucch), enb_ul.pucch.last_n_pucch, enb_ul.pucch.last_n_prb, - needs_ack?(uci_data.uci_ack?", ack=1":", ack=0"):"", + (uci_data.uci_ack_len)?(uci_data.uci_ack?", ack=1":", ack=0"):"", + (uci_data.uci_ack_len > 1)?(uci_data.uci_ack_2?"1":"0"):"", needs_sr?(uci_data.scheduling_request?", sr=yes":", sr=no"):"", - needs_cqi?cqi_str:""); + (needs_cqi || needs_ri)?cqi_ri_str:""); // Notify MAC of RL status @@ -642,25 +758,16 @@ int phch_worker::encode_pdcch_ul(srslte_enb_ul_pusch_t *grants, uint32_t nof_gra int phch_worker::encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants) { for (uint32_t i=0;irnti; if (rnti) { - srslte_dci_format_t format = SRSLTE_DCI_FORMAT1; - switch(grants[i].grant.alloc_type) { - case SRSLTE_RA_ALLOC_TYPE0: - case SRSLTE_RA_ALLOC_TYPE1: - format = SRSLTE_DCI_FORMAT1; - break; - case SRSLTE_RA_ALLOC_TYPE2: - format = SRSLTE_DCI_FORMAT1A; - break; - } - if (srslte_enb_dl_put_pdcch_dl(&enb_dl, &grants[i].grant, format, grants[i].location, rnti, sf_tx)) { + if (srslte_enb_dl_put_pdcch_dl(&enb_dl, &grants[i].grant, grant->dci_format, grants[i].location, rnti, sf_tx)) { fprintf(stderr, "Error putting PDCCH %d\n",i); return SRSLTE_ERROR; } if (LOG_THIS(rnti)) { - Info("PDCCH: DL DCI %s rnti=0x%x, cce_index=%d, L=%d, tti_tx_dl=%d\n", srslte_dci_format_string(format), + Info("PDCCH: DL DCI %s rnti=0x%x, cce_index=%d, L=%d, tti_tx_dl=%d\n", srslte_dci_format_string(grant->dci_format), rnti, grants[i].location.ncce, (1<info_hex(ptr, len, - "PDSCH: rnti=0x%x, l_crb=%2d, %s, harq=%d, tbs=%d, mcs=%d, rv=%d, tti_tx_dl=%d\n", - rnti, phy_grant.nof_prb, grant_str, grants[i].grant.harq_process, - phy_grant.mcs[0].tbs/8, phy_grant.mcs[0].idx, grants[i].grant.rv_idx, tti_tx_dl); + "PDSCH: rnti=0x%x, l_crb=%2d, %s, harq=%d, tti_tx_dl=%d, tx_scheme=%s%s%s%s\n", + rnti, phy_grant.nof_prb, grant_str, grants[i].grant.harq_process, tti_tx_dl, + srslte_mimotype2str(mimo_type), pinfo_str, tbstr[0], tbstr[1]); } - srslte_softbuffer_tx_t *sb[SRSLTE_MAX_CODEWORDS] = {grants[i].softbuffer, NULL}; - uint8_t *d[SRSLTE_MAX_CODEWORDS] = {grants[i].data, NULL}; - int rv[SRSLTE_MAX_CODEWORDS] = {grants[i].grant.rv_idx, 0}; - + int rv[SRSLTE_MAX_CODEWORDS] = {grants[i].grant.rv_idx, grants[i].grant.rv_idx_1}; - if (srslte_enb_dl_put_pdsch(&enb_dl, &phy_grant, sb, rnti, rv, sf_tx, d, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA, 0)) - { - fprintf(stderr, "Error putting PDSCH %d\n",i); - return SRSLTE_ERROR; + if (srslte_enb_dl_put_pdsch(&enb_dl, &phy_grant, grants[i].softbuffers, rnti, rv, sf_tx, grants[i].data, mimo_type)) { + fprintf(stderr, "Error putting PDSCH %d\n", i); + return SRSLTE_ERROR; } - // Save metrics stats + // Save metrics stats ue_db[rnti].metrics_dl(phy_grant.mcs[0].idx); } } - return SRSLTE_SUCCESS; + return SRSLTE_SUCCESS; } diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index c0d0a73c9..4f8c5fb4d 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -207,6 +207,13 @@ void phy::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]) /***** RRC->PHY interface **********/ +void phy::set_conf_dedicated_ack(uint16_t rnti, bool ack) +{ + for (uint32_t i = 0; i < nof_workers; i++) { + workers[i].set_conf_dedicated_ack(rnti, ack); + } +} + void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) { // Parse RRC config @@ -225,11 +232,7 @@ void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICAT pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx; for (uint32_t i=0;isched_request_cnfg.sr_cnfg_idx, - dedicated->cqi_report_cnfg.report_periodic_setup_present, - dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, - dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi); + workers[i].set_config_dedicated(rnti, &uci_cfg, &pucch_sched, NULL, dedicated); } } diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index fa14b0b82..acedd36c3 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -77,7 +77,7 @@ void txrx::stop() void txrx::run_thread() { phch_worker *worker = NULL; - cf_t *buffer = NULL; + cf_t *buffer[SRSLTE_MAX_PORTS] = {NULL}; srslte_timestamp_t rx_time, tx_time; uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->cell.nof_prb); @@ -108,10 +108,12 @@ void txrx::run_thread() while (running) { tti = (tti+1)%10240; worker = (phch_worker*) workers_pool->wait_worker(tti); - if (worker) { - buffer = worker->get_buffer_rx(); + if (worker) { + for (int p = 0; p < SRSLTE_MAX_PORTS; p++){ + buffer[p] = worker->get_buffer_rx(p); + } - radio_h->rx_now(buffer, sf_len, &rx_time); + radio_h->rx_now((void **) buffer, sf_len, &rx_time); /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ srslte_timestamp_copy(&tx_time, &rx_time); @@ -129,7 +131,7 @@ void txrx::run_thread() workers_pool->start_worker(worker); // Trigger prach worker execution - prach->new_tti(tti, buffer); + prach->new_tti(tti, buffer[0]); } else { // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index e176e8ac5..6bf5bc698 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -866,6 +866,10 @@ void rrc::ue::handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE memcpy(pdu->msg, msg->dedicated_info_nas.msg, msg->dedicated_info_nas.N_bytes); pdu->N_bytes = msg->dedicated_info_nas.N_bytes; + // Acknowledge Dedicated Configuration + parent->phy->set_conf_dedicated_ack(rnti, true); + parent->mac->phy_config_enabled(rnti, true); + if(has_tmsi) { parent->s1ap->initial_ue(rnti, pdu, m_tmsi, mmec); } else { @@ -1110,14 +1114,21 @@ void rrc::ue::send_connection_setup(bool is_setup) mac_cfg->time_alignment_timer = parent->cfg.mac_cnfg.time_alignment_timer; // physicalConfigDedicated - rr_cfg->phy_cnfg_ded_present = true; + rr_cfg->phy_cnfg_ded_present = true; LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &rr_cfg->phy_cnfg_ded; bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); phy_cfg->pusch_cnfg_ded_present = true; memcpy(&phy_cfg->pusch_cnfg_ded, &parent->cfg.pusch_cfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT)); phy_cfg->sched_request_cnfg_present = true; phy_cfg->sched_request_cnfg.setup_present = true; - phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + + if (parent->cfg.antenna_info.tx_mode > LIBLTE_RRC_TRANSMISSION_MODE_1) { + memcpy(&phy_cfg->antenna_info_explicit_value, &parent->cfg.antenna_info, + sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + phy_cfg->antenna_info_present = true; + phy_cfg->antenna_info_default_value = false; + } if (is_setup) { if (sr_allocate(parent->cfg.sr_cfg.period, &phy_cfg->sched_request_cnfg.sr_cnfg_idx, &phy_cfg->sched_request_cnfg.sr_pucch_resource_idx)) { @@ -1142,13 +1153,23 @@ void rrc::ue::send_connection_setup(bool is_setup) phy_cfg->cqi_report_cnfg_present = true; if(parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) { phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; - phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + if (phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31; + } else { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + } } else { phy_cfg->cqi_report_cnfg.report_periodic_present = true; phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; - phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; - phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; + if (phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3 || + phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx = 483; + } else { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + } if (is_setup) { if (cqi_allocate(parent->cfg.cqi_cfg.period, &phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, @@ -1198,7 +1219,9 @@ void rrc::ue::send_connection_setup(bool is_setup) // Configure PHY layer parent->phy->set_config_dedicated(rnti, phy_cfg); - parent->mac->phy_config_enabled(rnti, true); + parent->phy->set_conf_dedicated_ack(rnti, false); + parent->mac->set_dl_ant_info(rnti, &phy_cfg->antenna_info_explicit_value); + parent->mac->phy_config_enabled(rnti, false); rr_cfg->drb_to_add_mod_list_size = 0; rr_cfg->drb_to_release_list_size = 0; diff --git a/srsue/hdr/phy/phch_worker.h b/srsue/hdr/phy/phch_worker.h index 4f8df27db..5378a9428 100644 --- a/srsue/hdr/phy/phch_worker.h +++ b/srsue/hdr/phy/phch_worker.h @@ -76,7 +76,8 @@ private: /* Internal methods */ bool extract_fft_and_pdcch_llr(); - + void compute_ri(); + /* ... for DL */ bool decode_pdcch_ul(mac_interface_phy::mac_grant_t *grant); bool decode_pdcch_dl(mac_interface_phy::mac_grant_t *grant); diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h index 67ccc0c94..2872c374c 100644 --- a/srsue/hdr/phy/prach.h +++ b/srsue/hdr/phy/prach.h @@ -45,11 +45,11 @@ namespace srsue { args = NULL; config = NULL; - signal_buffer = NULL; transmitted_tti = 0; target_power_dbm = 0; mem_initiated = false; cell_initiated = false; + bzero(signal_buffer, sizeof(signal_buffer)); } ~prach(); void init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config, uint32_t max_prb, phy_args_t *args, srslte::log *log_h); @@ -78,7 +78,7 @@ namespace srsue { srslte_prach_t prach_obj; int transmitted_tti; srslte_cell_t cell; - cf_t *signal_buffer; + cf_t *signal_buffer[SRSLTE_MAX_PORTS]; srslte_cfo_t cfo_h; float target_power_dbm; diff --git a/srsue/src/phy/phch_common.cc b/srsue/src/phy/phch_common.cc index 2a7044acd..59db2f546 100644 --- a/srsue/src/phy/phch_common.cc +++ b/srsue/src/phy/phch_common.cc @@ -241,12 +241,12 @@ void phch_common::worker_end(uint32_t tti, bool tx_enable, radio_h->set_tti(tti); if (tx_enable) { - radio_h->tx(buffer, nof_samples, tx_time); + radio_h->tx_single(buffer, nof_samples, tx_time); is_first_of_burst = false; } else { if (TX_MODE_CONTINUOUS) { if (!is_first_of_burst) { - radio_h->tx(zeros, nof_samples, tx_time); + radio_h->tx_single(zeros, nof_samples, tx_time); } } else { if (!is_first_of_burst) { diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc index 27503c9f7..a5ccc9982 100644 --- a/srsue/src/phy/phch_worker.cc +++ b/srsue/src/phy/phch_worker.cc @@ -272,39 +272,6 @@ void phch_worker::work_imp() if (dl_action.generate_ack) { set_uci_ack(dl_ack, dl_mac_grant.tb_en); } - - /* Select Rank Indicator by computing Condition Number */ - if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3) { - if (ue_dl.nof_rx_antennas > 1) { - /* If 2 ort more receiving antennas, select RI */ - float cn = 0.0f; - srslte_ue_dl_ri_select(&ue_dl, &uci_data.uci_ri, &cn); - uci_data.uci_ri_len = 1; - } else { - /* If only one receiving antenna, force RI for 1 layer */ - uci_data.uci_ri = 0; - uci_data.uci_ri_len = 1; - Warning("Only one receiving antenna with TM3. Forcing RI=1 layer.\n"); - } - } else if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4){ - float sinr = 0.0f; - uint8 packed_pmi = 0; - srslte_ue_dl_ri_pmi_select(&ue_dl, &uci_data.uci_ri, &packed_pmi, &sinr); - srslte_bit_unpack_vector(&packed_pmi, uci_data.uci_pmi, 2); - uci_data.uci_ri_len = 1; - if (uci_data.uci_ri == 0) { - uci_data.uci_pmi_len = 2; - uci_data.uci_dif_cqi_len = 0; - } else { - uci_data.uci_pmi_len = 1; - uci_data.uci_dif_cqi_len = 3; - } - - /* If only one antenna in TM4 print limitation warning */ - if (ue_dl.nof_rx_antennas < 2) { - Warning("Only one receiving antenna with TM4. Forcing RI=1 layer (PMI=%d).\n", packed_pmi); - } - } } } @@ -360,7 +327,7 @@ void phch_worker::work_imp() phy->set_pending_ack(TTI_RX_ACK(tti), ue_ul.pusch_cfg.grant.n_prb_tilde[0], ul_action.phy_grant.ul.ncs_dmrs); } - } else if (dl_action.generate_ack || uci_data.scheduling_request || uci_data.uci_cqi_len > 0) { + } else if (dl_action.generate_ack || uci_data.scheduling_request || uci_data.uci_cqi_len > 0 || uci_data.uci_ri_len > 0) { encode_pucch(); signal_ready = true; } else if (srs_is_ready_to_send()) { @@ -410,6 +377,42 @@ void phch_worker::work_imp() #endif } +void phch_worker::compute_ri() { + if (uci_data.uci_ri_len > 0) { + /* Do nothing */ + } else if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3) { + if (ue_dl.nof_rx_antennas > 1) { + /* If 2 ort more receiving antennas, select RI */ + float cn = 0.0f; + srslte_ue_dl_ri_select(&ue_dl, &uci_data.uci_ri, &cn); + Info("RI select %d layers, κ=%fdB\n", uci_data.uci_ri + 1, cn); + } else { + /* If only one receiving antenna, force RI for 1 layer */ + uci_data.uci_ri = 0; + Warning("Only one receiving antenna with TM3. Forcing RI=1 layer.\n"); + } + uci_data.uci_ri_len = 1; + } else if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + float sinr = 0.0f; + uint8 packed_pmi = 0; + srslte_ue_dl_ri_pmi_select(&ue_dl, &uci_data.uci_ri, &packed_pmi, &sinr); + if (uci_data.uci_ri == 0) { + uci_data.uci_pmi_len = 2; + uci_data.uci_dif_cqi_len = 0; + } else { + uci_data.uci_pmi_len = 1; + uci_data.uci_dif_cqi_len = 3; + } + srslte_bit_unpack_vector(&packed_pmi, uci_data.uci_pmi, uci_data.uci_pmi_len); + Info("ri=%d; pmi=%d; SINR=%.1fdB\n", ue_dl.ri, ue_dl.pmi[ue_dl.ri], 10*log10f(ue_dl.sinr[ue_dl.ri][ue_dl.pmi[ue_dl.ri]])); + + /* If only one antenna in TM4 print limitation warning */ + if (ue_dl.nof_rx_antennas < 2) { + Warning("Only one receiving antenna with TM4. Forcing RI=1 layer (PMI=%d).\n", packed_pmi); + } + uci_data.uci_ri_len = 1; + } +} bool phch_worker::extract_fft_and_pdcch_llr() { bool decode_pdcch = true; @@ -634,7 +637,8 @@ int phch_worker::decode_pdsch(srslte_ra_dl_grant_t *grant, uint8_t *payload[SRSL /* Setup PDSCH configuration for this CFI, SFIDX and RVIDX */ if (valid_config) { if (!srslte_ue_dl_cfg_grant(&ue_dl, grant, cfi, tti%10, rv, mimo_type)) { - if (ue_dl.pdsch_cfg.grant.mcs[0].mod > 0 && ue_dl.pdsch_cfg.grant.mcs[0].tbs >= 0) { + if ((ue_dl.pdsch_cfg.grant.mcs[0].mod > 0 && ue_dl.pdsch_cfg.grant.mcs[0].tbs >= 0) || + (ue_dl.pdsch_cfg.grant.mcs[1].mod > 0 && ue_dl.pdsch_cfg.grant.mcs[1].tbs >= 0)) { float noise_estimate = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); @@ -663,8 +667,13 @@ int phch_worker::decode_pdsch(srslte_ra_dl_grant_t *grant, uint8_t *payload[SRSL snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); #endif - snprintf(commonstr, 128, "PDSCH: l_crb=%2d, harq=%d, snr=%.1f dB", grant->nof_prb, harq_pid, - 10 * log10(srslte_chest_dl_get_snr(&ue_dl.chest))); + char pinfo_str[16] = {0}; + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + snprintf(pinfo_str, 15, ", pinfo=%x", grant->pinfo); + } + + snprintf(commonstr, 128, "PDSCH: l_crb=%2d, harq=%d, snr=%.1f dB, tx_scheme=%s%s", grant->nof_prb, harq_pid, + 10 * log10(srslte_chest_dl_get_snr(&ue_dl.chest)), srslte_mimotype2str(mimo_type), pinfo_str); for (int i=0;itb_en[i]) { @@ -823,21 +832,15 @@ void phch_worker::reset_uci() void phch_worker::set_uci_ack(bool ack[SRSLTE_MAX_CODEWORDS], bool tb_en[SRSLTE_MAX_CODEWORDS]) { - uint32_t nof_tb = 0; - if (tb_en[0]) { - uci_data.uci_ack = (uint8_t) ((ack[0]) ? 1 : 0); - nof_tb = 1; - } else { - uci_data.uci_ack = 1; - } - - if (tb_en[1]) { - uci_data.uci_ack_2 = (uint8_t) ((ack[1]) ? 1 : 0); - nof_tb = 2; + /* Map ACK according to 3GPP 36.212 clause 5.2.3.1 */ + uint32_t nof_ack = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + if (tb_en[tb]) { + ((nof_ack == 0)?uci_data.uci_ack:uci_data.uci_ack_2) = (uint8_t)(ack[tb]?1:0); + nof_ack++; + } } - - uci_data.uci_ack_len = nof_tb; - + uci_data.uci_ack_len = nof_ack; } void phch_worker::set_uci_sr() @@ -862,14 +865,11 @@ void phch_worker::set_uci_periodic_cqi() if (period_cqi.configured && rnti_is_set) { if (period_cqi.ri_idx_present && srslte_ri_send(period_cqi.pmi_idx, period_cqi.ri_idx, TTI_TX(tti))) { - if (uci_data.uci_ri_len) { - uci_data.uci_cqi[0] = uci_data.uci_ri; - uci_data.uci_cqi_len = uci_data.uci_ri_len; - uci_data.uci_ri_len = 0; - uci_data.uci_dif_cqi_len = 0; - uci_data.uci_pmi_len = 0; - Info("PUCCH: Periodic RI=%d\n", uci_data.uci_cqi[0]); - } + /* Compute RI, PMI and SINR */ + compute_ri(); + + uci_data.ri_periodic_report = true; + Info("PUCCH: Periodic RI=%d\n", uci_data.uci_ri); } else if (srslte_cqi_send(period_cqi.pmi_idx, TTI_TX(tti))) { srslte_cqi_value_t cqi_report; if (period_cqi.format_is_subband) { @@ -891,6 +891,16 @@ void phch_worker::set_uci_periodic_cqi() } Info("PUCCH: Periodic CQI=%d, SNR=%.1f dB\n", cqi_report.wideband.wideband_cqi, phy->avg_snr_db); } + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + if (ue_dl.ri == 0) { + uci_data.uci_pmi_len = 2; + } else { + uci_data.uci_pmi_len = 1; + uci_data.uci_dif_cqi_len = 3; + } + uint8_t *ptr = uci_data.uci_pmi; + srslte_bit_unpack(ue_dl.pmi[ue_dl.ri], &ptr, uci_data.uci_pmi_len); + } uci_data.uci_cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); rar_cqi_request = false; } @@ -900,6 +910,9 @@ void phch_worker::set_uci_periodic_cqi() void phch_worker::set_uci_aperiodic_cqi() { if (phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic_present) { + /* Compute RI, PMI and SINR */ + compute_ri(); + switch(phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic) { case LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30: /* only Higher Layer-configured subband feedback support right now, according to TS36.213 section 7.2.1 @@ -912,18 +925,68 @@ void phch_worker::set_uci_aperiodic_cqi() reported RI. For other transmission modes they are reported conditioned on rank 1. */ if (rnti_is_set) { - srslte_cqi_value_t cqi_report; + srslte_cqi_value_t cqi_report = {0}; cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND_HL; - cqi_report.subband_hl.wideband_cqi = srslte_cqi_from_snr(phy->avg_snr_db); + cqi_report.subband_hl.wideband_cqi_cw0 = srslte_cqi_from_snr(phy->avg_snr_db); // TODO: implement subband CQI properly - cqi_report.subband_hl.subband_diff_cqi = 0; // Always report zero offset on all subbands + cqi_report.subband_hl.subband_diff_cqi_cw0 = 0; // Always report zero offset on all subbands cqi_report.subband_hl.N = (cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(cell.nof_prb) : 0; Info("PUSCH: Aperiodic CQI=%d, SNR=%.1f dB, for %d subbands\n", cqi_report.wideband.wideband_cqi, phy->avg_snr_db, cqi_report.subband_hl.N); uci_data.uci_cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); } break; + case LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31: + /* only Higher Layer-configured subband feedback support right now, according to TS36.213 section 7.2.1 + - A single precoding matrix is selected from the codebook subset assuming transmission on set S subbands + - A UE shall report one subband CQI value per codeword for each set S subband which are calculated assuming + the use of the single precoding matrix in all subbands and assuming transmission in the corresponding + subband. + - A UE shall report a wideband CQI value per codeword which is calculated assuming the use of the single + precoding matrix in all subbands and transmission on set S subbands + - The UE shall report the single selected precoding matrix indicator. + - For transmission mode 4 the reported PMI and CQI values are calculated conditioned on the reported RI. For + other transmission modes they are reported conditioned on rank 1. + */ + if (rnti_is_set) { + /* Select RI, PMI and SINR */ + uint32_t ri = ue_dl.ri; // Select RI (0: 1 layer, 1: 2 layer, otherwise: not implemented) + uint32_t pmi = ue_dl.pmi[ri]; // Select PMI + float sinr_db = 10 * log10(ue_dl.sinr[ri][pmi]); + + /* Fill CQI Report */ + srslte_cqi_value_t cqi_report = {0}; + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + + cqi_report.subband_hl.wideband_cqi_cw0 = srslte_cqi_from_snr(sinr_db); + cqi_report.subband_hl.subband_diff_cqi_cw0 = 0; // Always report zero offset on all subbands + + if (ri > 0) { + cqi_report.subband_hl.rank_is_not_one = true; + cqi_report.subband_hl.wideband_cqi_cw1 = srslte_cqi_from_snr(sinr_db); + cqi_report.subband_hl.subband_diff_cqi_cw1 = 0; // Always report zero offset on all subbands + } + + cqi_report.subband_hl.pmi = pmi; + cqi_report.subband_hl.pmi_present = true; + cqi_report.subband_hl.four_antenna_ports = (cell.nof_ports == 4); + + // TODO: implement subband CQI properly + cqi_report.subband_hl.N = (uint32_t) ((cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(cell.nof_prb) : 0); + + if (cqi_report.subband_hl.rank_is_not_one) { + Info("PUSCH: Aperiodic ri~1, CQI=%02d/%02d, SINR=%2.1f/%2.1fdB, pmi=%d for %d subbands\n", + cqi_report.subband_hl.wideband_cqi_cw0, cqi_report.subband_hl.wideband_cqi_cw1, + sinr_db, sinr_db, pmi, cqi_report.subband_hl.N); + } else { + Info("PUSCH: Aperiodic ri=1, CQI=%02d, SINR=%2.1f, pmi=%d for %d subbands\n", + cqi_report.subband_hl.wideband_cqi_cw0, + sinr_db, pmi, cqi_report.subband_hl.N); + } + uci_data.uci_cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + } + break; default: Warning("Received CQI request but mode %s is not supported\n", liblte_rrc_cqi_report_mode_aperiodic_text[phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic]); @@ -1006,7 +1069,7 @@ void phch_worker::encode_pucch() char timestr[64]; timestr[0]='\0'; - if (uci_data.scheduling_request || uci_data.uci_ack_len > 0 || uci_data.uci_cqi_len > 0) + if (uci_data.scheduling_request || uci_data.uci_ack_len > 0 || uci_data.uci_cqi_len > 0 || uci_data.uci_ri_len > 0) { // Drop CQI if there is collision with ACK diff --git a/srsue/src/phy/prach.cc b/srsue/src/phy/prach.cc index 7c693fe7a..bd7550457 100644 --- a/srsue/src/phy/prach.cc +++ b/srsue/src/phy/prach.cc @@ -75,10 +75,12 @@ void prach::init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config_, uint32_t max_prb, return; } srslte_cfo_set_tol(&cfo_h, 0); - signal_buffer = (cf_t*) srslte_vec_malloc(SRSLTE_PRACH_MAX_LEN*sizeof(cf_t)); - if (!signal_buffer) { - perror("malloc"); - return; + for (int p = 0; p < SRSLTE_MAX_PORTS; p++) { + signal_buffer[p] = (cf_t *) srslte_vec_malloc(SRSLTE_PRACH_MAX_LEN * sizeof(cf_t)); + if (!signal_buffer[p]) { + perror("malloc"); + return; + } } if (srslte_prach_init(&prach_obj, srslte_symbol_sz(max_prb))) { Error("Initiating PRACH library\n"); @@ -181,8 +183,8 @@ void prach::send(srslte::radio *radio_handler, float cfo, float pathloss, srslte // Get current TX gain float old_gain = radio_handler->get_tx_gain(); - // Correct CFO before transmission - srslte_cfo_correct(&cfo_h, buffer[preamble_idx], signal_buffer, cfo / srslte_symbol_sz(cell.nof_prb)); + // Correct CFO before transmission FIXME: UL SISO Only + srslte_cfo_correct(&cfo_h, buffer[preamble_idx], signal_buffer[0], cfo / srslte_symbol_sz(cell.nof_prb)); // If power control is enabled, choose amplitude and power if (args->ul_pwr_ctrl_en) { @@ -193,10 +195,10 @@ void prach::send(srslte::radio *radio_handler, float cfo, float pathloss, srslte radio_handler->set_tx_power(tx_power); // Scale signal - float digital_power = srslte_vec_avg_power_cf(signal_buffer, len); + float digital_power = srslte_vec_avg_power_cf(signal_buffer[0], len); float scale = sqrtf(pow(10,tx_power/10)/digital_power); - srslte_vec_sc_prod_cfc(signal_buffer, scale, signal_buffer, len); + srslte_vec_sc_prod_cfc(signal_buffer[0], scale, signal_buffer[0], len); log_h->console("PRACH: Pathloss=%.2f dB, Target power %.2f dBm, TX_power %.2f dBm, TX_gain %.1f dB\n", pathloss, target_power_dbm, tx_power, radio_handler->get_tx_gain(), scale); @@ -207,8 +209,8 @@ void prach::send(srslte::radio *radio_handler, float cfo, float pathloss, srslte } Debug("TX PRACH: Power control for PRACH is disabled, setting gain to %.0f dB\n", prach_gain); } - - radio_handler->tx(signal_buffer, len, tx_time); + + radio_handler->tx((void **) signal_buffer, len, tx_time); radio_handler->tx_end(); Info("PRACH: Transmitted preamble=%d, CFO=%.2f KHz, tx_time=%f\n", diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index 55c39cfe0..d6ba7d5df 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -1866,7 +1866,9 @@ void rrc::apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT if (phy_cnfg->antenna_info_present) { if (!phy_cnfg->antenna_info_default_value) { if (phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_1 && - phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_2) { + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_2 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_3 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_4) { rrc_log->error("Transmission mode TM%s not currently supported by srsUE\n", liblte_rrc_transmission_mode_text[phy_cnfg->antenna_info_explicit_value.tx_mode]); }