From e05ecdb13970124a786e7899a76b5e131b24aafa Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 24 Oct 2019 17:53:09 +0200 Subject: [PATCH] adding NB-IoT DL channel estamiation and NPBCH code --- .../srslte/phy/ch_estimation/chest_dl_nbiot.h | 97 +++ .../phy/ch_estimation/refsignal_dl_nbiot.h | 64 ++ lib/include/srslte/phy/phch/npbch.h | 151 +++++ lib/include/srslte/phy/phch/ra_nbiot.h | 286 ++++++++ lib/src/phy/ch_estimation/chest_dl_nbiot.c | 355 ++++++++++ lib/src/phy/ch_estimation/refsignal_dl.c | 6 +- .../phy/ch_estimation/refsignal_dl_nbiot.c | 265 ++++++++ lib/src/phy/ch_estimation/test/CMakeLists.txt | 12 +- .../ch_estimation/test/chest_nbiot_test_dl.c | 256 +++++++ lib/src/phy/common/phy_common.c | 2 +- lib/src/phy/phch/npbch.c | 627 ++++++++++++++++++ lib/src/phy/phch/ra_nbiot.c | 548 +++++++++++++++ lib/src/phy/phch/tbs_tables_nbiot.h | 60 ++ lib/src/phy/phch/test/CMakeLists.txt | 19 +- lib/src/phy/phch/test/npbch_file_test.c | 285 ++++++++ lib/src/phy/phch/test/npbch_test.c | 242 +++++++ ...f0.bin => signal_nbiot_nid256_r14_sf0.bin} | Bin ...f0.bin => signal_nbiot_nid257_r14_sf0.bin} | Bin 18 files changed, 3270 insertions(+), 5 deletions(-) create mode 100644 lib/include/srslte/phy/ch_estimation/chest_dl_nbiot.h create mode 100644 lib/include/srslte/phy/ch_estimation/refsignal_dl_nbiot.h create mode 100644 lib/include/srslte/phy/phch/npbch.h create mode 100644 lib/include/srslte/phy/phch/ra_nbiot.h create mode 100644 lib/src/phy/ch_estimation/chest_dl_nbiot.c create mode 100644 lib/src/phy/ch_estimation/refsignal_dl_nbiot.c create mode 100644 lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c create mode 100644 lib/src/phy/phch/npbch.c create mode 100644 lib/src/phy/phch/ra_nbiot.c create mode 100644 lib/src/phy/phch/tbs_tables_nbiot.h create mode 100644 lib/src/phy/phch/test/npbch_file_test.c create mode 100644 lib/src/phy/phch/test/npbch_test.c rename lib/src/phy/phch/test/{signal_nbiot_vodafone_nid256_r14_sf0.bin => signal_nbiot_nid256_r14_sf0.bin} (100%) rename lib/src/phy/phch/test/{signal_nbiot_vodafone_nid257_r14_sf0.bin => signal_nbiot_nid257_r14_sf0.bin} (100%) diff --git a/lib/include/srslte/phy/ch_estimation/chest_dl_nbiot.h b/lib/include/srslte/phy/ch_estimation/chest_dl_nbiot.h new file mode 100644 index 000000000..90b3fec75 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/chest_dl_nbiot.h @@ -0,0 +1,97 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_CHEST_DL_NBIOT_H +#define SRSLTE_CHEST_DL_NBIOT_H + +#include + +#include "srslte/config.h" + +#include "srslte/phy/ch_estimation/chest_common.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/ch_estimation/refsignal_dl_nbiot.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/resampling/interp.h" + +/*! \brief Downlink channel estimation for NB-IoT + * + * Estimates the channel in the resource elements transmitting references and + * interpolates for the rest of the resource grid. + * The equalizer uses the channel estimates to produce an estimation of the + * transmitted symbol. + * This object depends on the srslte_refsignal_t object for creating the LTE CSR signal. + */ +typedef struct { + srslte_nbiot_cell_t cell; + srslte_refsignal_dl_nbiot_t nrs_signal; + + cf_t* pilot_estimates; + cf_t* pilot_estimates_average; + cf_t* pilot_recv_signal; + cf_t* tmp_noise; + + uint32_t smooth_filter_len; + float smooth_filter[SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN]; + + srslte_interp_linsrslte_vec_t srslte_interp_linvec; + srslte_interp_lin_t srslte_interp_lin; + + float rssi[SRSLTE_MAX_PORTS]; + float rsrp[SRSLTE_MAX_PORTS]; + float noise_estimate[SRSLTE_MAX_PORTS]; + + srslte_chest_dl_noise_alg_t noise_alg; + +} srslte_chest_dl_nbiot_t; + +SRSLTE_API int srslte_chest_dl_nbiot_init(srslte_chest_dl_nbiot_t* q, uint32_t max_prb); + +SRSLTE_API void srslte_chest_dl_nbiot_free(srslte_chest_dl_nbiot_t* q); + +SRSLTE_API int srslte_chest_dl_nbiot_set_cell(srslte_chest_dl_nbiot_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API void srslte_chest_dl_nbiot_set_smooth_filter(srslte_chest_dl_nbiot_t* q, float* filter, uint32_t filter_len); + +SRSLTE_API void srslte_chest_dl_nbiot_set_smooth_filter3_coeff(srslte_chest_dl_nbiot_t* q, float w); + +SRSLTE_API void srslte_chest_dl_nbiot_set_noise_alg(srslte_chest_dl_nbiot_t* q, + srslte_chest_dl_noise_alg_t noise_estimation_alg); + +SRSLTE_API int srslte_chest_dl_nbiot_estimate(srslte_chest_dl_nbiot_t* q, cf_t* input, cf_t** ce, uint32_t sf_idx); + +SRSLTE_API int srslte_chest_dl_nbiot_estimate_port(srslte_chest_dl_nbiot_t* q, + cf_t* input, + cf_t* ce, + uint32_t sf_idx, + uint32_t port_id); + +SRSLTE_API float srslte_chest_dl_nbiot_get_noise_estimate(srslte_chest_dl_nbiot_t* q); + +SRSLTE_API float srslte_chest_dl_nbiot_get_snr(srslte_chest_dl_nbiot_t* q); + +SRSLTE_API float srslte_chest_dl_nbiot_get_rssi(srslte_chest_dl_nbiot_t* q); + +SRSLTE_API float srslte_chest_dl_nbiot_get_rsrq(srslte_chest_dl_nbiot_t* q); + +SRSLTE_API float srslte_chest_dl_nbiot_get_rsrp(srslte_chest_dl_nbiot_t* q); + +#endif // SRSLTE_CHEST_DL_NBIOT_H diff --git a/lib/include/srslte/phy/ch_estimation/refsignal_dl_nbiot.h b/lib/include/srslte/phy/ch_estimation/refsignal_dl_nbiot.h new file mode 100644 index 000000000..0d4de4a16 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/refsignal_dl_nbiot.h @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_REFSIGNAL_DL_NBIOT_H +#define SRSLTE_REFSIGNAL_DL_NBIOT_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +// Number of references in a subframe: there are 2 symbols for port_id=0,1 x 2 slots x 2 refs per prb +#define SRSLTE_NBIOT_NUM_NRS_X_SYM_X_PORT 2 + +// Max number of symbols with RE in one PRB (4 ports LTE plus 2 port NB-IoT) +#define SRSLTE_NBIOT_MAX_NUM_RE_WITH_REFS 40 + +#define SRSLTE_NBIOT_REFSIGNAL_NUM_SF(nof_prb, port_id) (((port_id) < 2 ? 8 : 4) * (nof_prb)) +#define SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(i, l, nof_prb) (2 * nof_prb * (l) + (i)) + +/** Brief: Narrowband Reference Signal (NRS) + * + * Object to manage downlink reference signals for channel estimation. (3GPP TS 36.211 version 13.3 Sec. 10.2.6) + */ +typedef struct SRSLTE_API { + srslte_nbiot_cell_t cell; + cf_t* pilots[2][SRSLTE_NOF_SF_X_FRAME]; // Saves the reference signal per subframe for ports 0 and 1 +} srslte_refsignal_dl_nbiot_t; + +SRSLTE_API int srslte_refsignal_dl_nbiot_init(srslte_refsignal_dl_nbiot_t* q); + +SRSLTE_API void srslte_refsignal_dl_nbiot_free(srslte_refsignal_dl_nbiot_t* q); + +SRSLTE_API uint32_t srslte_refsignal_dl_nbiot_nof_symbols(uint32_t port_id); + +SRSLTE_API int srslte_refsignal_dl_nbiot_set_cell(srslte_refsignal_dl_nbiot_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API uint32_t srslte_refsignal_dl_nbiot_fidx(srslte_nbiot_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m); + +SRSLTE_API uint32_t srslte_refsignal_nrs_nsymbol(uint32_t l); + +SRSLTE_API uint32_t srslte_refsignal_nbiot_cs_nof_re(srslte_nbiot_cell_t* cell, uint32_t port_id); + +SRSLTE_API int srslte_refsignal_nrs_put_sf(srslte_nbiot_cell_t cell, uint32_t port_id, cf_t* pilots, cf_t* sf_symbols); + +SRSLTE_API int srslte_refsignal_nrs_get_sf(srslte_nbiot_cell_t cell, uint32_t port_id, cf_t* sf_symbols, cf_t* pilots); + +#endif // SRSLTE_REFSIGNAL_DL_NBIOT_H diff --git a/lib/include/srslte/phy/phch/npbch.h b/lib/include/srslte/phy/phch/npbch.h new file mode 100644 index 000000000..f1bd94af6 --- /dev/null +++ b/lib/include/srslte/phy/phch/npbch.h @@ -0,0 +1,151 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_NPBCH_H +#define SRSLTE_NPBCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +#include "srslte/phy/fec/convcoder.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/rm_conv.h" +#include "srslte/phy/fec/viterbi.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/scrambling/scrambling.h" + +#define SRSLTE_MIB_NB_LEN 34 +#define SRSLTE_MIB_NB_CRC_LEN (SRSLTE_MIB_NB_LEN + 16) +#define SRSLTE_MIB_NB_ENC_LEN (3 * SRSLTE_MIB_NB_CRC_LEN) + +#define SRSLTE_NPBCH_NUM_RE (12 * 11 - 4 * 8) // 100 RE, entire PRB minus 3 symbols minus 4 times NRS=CRS REs +#define SRSLTE_NPBCH_NUM_BLOCKS 8 // MIB-NB is split across 8 blocks +#define SRSLTE_NPBCH_NUM_REP 8 // number of repetitions per block +#define SRSLTE_NPBCH_NUM_FRAMES (SRSLTE_NPBCH_NUM_BLOCKS * SRSLTE_NPBCH_NUM_REP) + +typedef struct { + uint16_t sfn; + uint16_t hfn; + uint8_t sched_info_sib1; + uint8_t sys_info_tag; + bool ac_barring; + srslte_nbiot_mode_t mode; +} srslte_mib_nb_t; + +/** + * \brief Narrowband Physical broadcast channel (NPBCH) + * + * Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.4 + */ +typedef struct SRS_API { + srslte_nbiot_cell_t cell; + + uint32_t frame_idx; + uint32_t nof_symbols; + + // buffers + cf_t* ce[SRSLTE_MAX_PORTS]; + cf_t* symbols[SRSLTE_MAX_PORTS]; + cf_t* x[SRSLTE_MAX_PORTS]; + cf_t* d; + float* llr; + float* temp; + float rm_f[SRSLTE_MIB_NB_ENC_LEN]; + uint8_t* rm_b; + uint8_t data[SRSLTE_MIB_NB_CRC_LEN]; + uint8_t data_enc[SRSLTE_MIB_NB_ENC_LEN]; + + srslte_nbiot_mode_t op_mode; + + // tx & rx objects + srslte_modem_table_t mod; + srslte_sequence_t seq; + srslte_viterbi_t decoder; + srslte_crc_t crc; + srslte_convcoder_t encoder; + bool search_all_ports; +} srslte_npbch_t; + +SRSLTE_API int srslte_npbch_init(srslte_npbch_t* q); + +SRSLTE_API void srslte_npbch_free(srslte_npbch_t* q); + +SRSLTE_API int srslte_npbch_set_cell(srslte_npbch_t* q, srslte_nbiot_cell_t cell); + +SRSLTE_API void srslte_npbch_mib_pack(uint32_t sfn, uint32_t hfn, srslte_mib_nb_t mib, uint8_t* msg); + +SRSLTE_API void srslte_npbch_mib_unpack(uint8_t* msg, srslte_mib_nb_t* mib); + +SRSLTE_API void srslte_mib_nb_printf(FILE* stream, srslte_nbiot_cell_t cell, srslte_mib_nb_t* mib); + +SRSLTE_API int srslte_npbch_put_subframe(srslte_npbch_t* q, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + cf_t* sf[SRSLTE_MAX_PORTS], + uint32_t frame_idx); + +SRSLTE_API int srslte_npbch_encode(srslte_npbch_t* q, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + cf_t* sf[SRSLTE_MAX_PORTS], + uint32_t frame_idx); + +int srslte_npbch_rotate(srslte_npbch_t* q, + uint32_t nf, + cf_t* input_signal, + cf_t* output_signal, + int num_samples, + bool back); + +SRSLTE_API int srslte_npbch_decode(srslte_npbch_t* q, + cf_t* slot1_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + uint32_t* nof_tx_ports, + int* sfn_offset); + +SRSLTE_API int srslte_npbch_decode_nf(srslte_npbch_t* q, + cf_t* slot1_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + uint32_t* nof_tx_ports, + int* sfn_offset, + int nf); + +SRSLTE_API void srslte_npbch_decode_reset(srslte_npbch_t* q); + +SRSLTE_API int srslte_npbch_decode_frame(srslte_npbch_t* q, + uint32_t src, + uint32_t dst, + uint32_t n, + uint32_t nof_bits, + uint32_t nof_ports); + +SRSLTE_API uint32_t srslte_npbch_crc_check(srslte_npbch_t* q, uint8_t* bits, uint32_t nof_ports); + +SRSLTE_API void srslte_npbch_crc_set_mask(uint8_t* data, int nof_ports); + +SRSLTE_API int srslte_npbch_cp(cf_t* input, cf_t* output, srslte_nbiot_cell_t cell, bool put); + +#endif // SRSLTE_NPBCH_H diff --git a/lib/include/srslte/phy/phch/ra_nbiot.h b/lib/include/srslte/phy/phch/ra_nbiot.h new file mode 100644 index 000000000..b18a9fef0 --- /dev/null +++ b/lib/include/srslte/phy/phch/ra_nbiot.h @@ -0,0 +1,286 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/** + * \brief Structures and utility functions for DL/UL resource allocation for NB-IoT. + * + * Reference: 3GPP TS 36.213 version 13.2.0 Release 13 + */ + +#ifndef SRSLTE_RA_NBIOT_H +#define SRSLTE_RA_NBIOT_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/phch/ra.h" + +#define SRSLTE_NPUSCH_MAX_SC 12 +#define SRSLTE_NPUSCH_MAX_SLOTS (16 * SRSLTE_NPUSCH_N_RU_MAX) +#define SRSLTE_NPUSCH_MAX_SF (SRSLTE_NPUSCH_MAX_SLOTS / 2) +#define SRSLTE_NPUSCH_N_REP_MAX 128 +#define SRSLTE_NPUSCH_N_RU_MAX 10 +#define SRSLTE_NPUSCH_MAX_NOF_RU_SLOTS_PROD 24 +#define SRSLTE_NPUSCH_FORMAT2_MAX_SLOTS 4 + +#define DUMMY_R_MAX 64 +#define SIB1_NB_TTI 256 +#define SIB1_NB_MAX_REP 16 + +/// All System Information types defined for NB-IoT +typedef enum SRSLTE_API { + SRSLTE_NBIOT_SI_TYPE_MIB = 0, ///< Essential information required to receive further sys information + SRSLTE_NBIOT_SI_TYPE_SIB1, ///< Cell access and selection, other SIB scheduling + SRSLTE_NBIOT_SI_TYPE_SIB2, ///< Radio resource configuration information + SRSLTE_NBIOT_SI_TYPE_SIB3, ///< Cell re-selection information for intra-frequency, inter-frequency + SRSLTE_NBIOT_SI_TYPE_SIB4, ///< Neighboring cell related information relevant for intra-frequency cell re-selection + SRSLTE_NBIOT_SI_TYPE_SIB5, ///< Neighboring cell related information relevant for inter-frequency cell re-selection + SRSLTE_NBIOT_SI_TYPE_SIB14, ///< Access Barring parameters + SRSLTE_NBIOT_SI_TYPE_SIB16, ///< Information related to GPS time and Coordinated Universal Time (UTC) + SRSLTE_NBIOT_SI_TYPE_NITEMS +} srslte_nbiot_si_type_t; + +typedef struct SRSLTE_API { + bool has_sib1; ///< Whether this NPDSCH carries SystemInformationBlockType1-NB + bool is_ra; ///< Order is set to 1 for random access procedure + uint32_t nprach_start; ///< Starting number for NPRACH repetitions + uint32_t nprach_sc; ///< Subcarrier indication of NPRACH + uint32_t i_delay; ///< Scheduling delay + uint32_t i_sf; ///< Resource assignment, i.e. number of subframes + uint32_t i_rep; ///< Repetition number + uint32_t i_n_start; ///< The starting OFDM symbol signalled through RRC + uint32_t harq_ack; ///< HARQ-ACK resource + uint32_t dci_sf_rep_num; ///< DCI subframe repetition number + uint32_t sched_info_sib1; ///< broadcasted through MIB-NB for TBS calculation +} srslte_ra_nbiot_t; + +/// This shall be replaced/moved by proper structures and packing/unpacking based on ASN1 +typedef struct SRSLTE_API { + uint32_t n; ///< Index of entry in schedulingInfoList + uint32_t si_periodicity; + uint32_t si_radio_frame_offset; + uint32_t si_repetition_pattern; + uint32_t si_tb; + uint32_t si_window_length; +} srslte_nbiot_si_params_t; + +typedef enum SRSLTE_API { + SRSLTE_NPUSCH_FORMAT1 = 0, + SRSLTE_NPUSCH_FORMAT2, + SRSLTE_NPUSCH_FORMAT_NITEMS +} srslte_npusch_format_t; +static const char srslte_npusch_format_text[SRSLTE_NPUSCH_FORMAT_NITEMS][20] = {"Format 1 (UL-SCH)", "Format 2 (UCI)"}; + +typedef enum SRSLTE_API { + SRSLTE_NPUSCH_SC_SPACING_15000 = 0, + SRSLTE_NPUSCH_SC_SPACING_3750 = 1, + SRSLTE_NPUSCH_SC_SPACING_NITEMS +} srslte_npusch_sc_spacing_t; +static const char srslte_npusch_sc_spacing_text[SRSLTE_NPUSCH_SC_SPACING_NITEMS][20] = {"15kHz", "3.75kHz"}; + +/************************************************** + * Structures used for Downlink Resource Allocation + **************************************************/ + +typedef struct SRSLTE_API { + uint32_t Qm; + srslte_ra_tb_t mcs[SRSLTE_MAX_TB]; + uint32_t start_hfn; + uint32_t start_sfn; + uint32_t start_sfidx; + uint32_t k0; + uint32_t nof_sf; + uint32_t nof_rep; + uint32_t l_start; + uint32_t ack_nack_resource; + + srslte_nbiot_mode_t mode; ///< needed to compute starting symbol for NPDSCH + bool has_sib1; +} srslte_ra_nbiot_dl_grant_t; + +/// Unpacked DCI message for DL grant +typedef struct SRSLTE_API { + uint32_t format; ///< Flag for format N0/format N1 differentiation – 1 bit, 0 for format N0 and value 1 for format N1 + srslte_ra_nbiot_t alloc; + + uint32_t mcs_idx; + int rv_idx; + bool ndi; + + // Format N2 specifics + bool dci_is_n2; + uint8_t dir_indication_info; +} srslte_ra_nbiot_dl_dci_t; + +/// Structures used for Uplink Resource Allocation + +/// Unpacked DCI message +typedef struct SRSLTE_API { + srslte_npusch_sc_spacing_t sc_spacing; + uint32_t format; ///< Flag for format N0/format N1 differentiation – 1 bit, 0 for format N0 and value 1 for format N1 + uint32_t i_sc; ///< Subcarrier indication field + uint32_t i_delay; ///< Scheduling delay + uint32_t i_ru; ///< Resource assignment, i.e. number of resource units + uint32_t i_mcs; ///< MCS field + uint32_t i_rv; ///< Redundency version + uint32_t i_rep; ///< Repetition number + bool ndi; ///< New data indicator + uint32_t dci_sf_rep_num; ///< DCI subframe repetition number +} srslte_ra_nbiot_ul_dci_t; + +typedef struct SRSLTE_API { + uint32_t Qm; + srslte_ra_tb_t mcs; + uint32_t k0; ///< FIXME: consider removing k0 and compute tx_tti directly + uint32_t nof_rep; ///< number of repetitions + uint32_t nof_ru; ///< Number of resource units + uint32_t nof_slots; + uint32_t nof_sc; ///< The total number of Subcarriers (N_sc_RU) + uint32_t sc_alloc_set[SRSLTE_NPUSCH_MAX_SC]; ///< The set of subcarriers + uint32_t rv_idx; + uint32_t tx_tti; + srslte_npusch_sc_spacing_t sc_spacing; + srslte_npusch_format_t format; +} srslte_ra_nbiot_ul_grant_t; + +typedef union { + srslte_ra_nbiot_ul_grant_t ul; + srslte_ra_nbiot_dl_grant_t dl; +} srslte_nbiot_phy_grant_t; + +#define SRSLTE_NBIOT_PHY_GRANT_LEN sizeof(srslte_nbiot_phy_grant_t) + +/// Structure that gives the number of encoded bits and RE for a UL/DL grant +typedef struct { + uint32_t lstart; + uint32_t nof_symb; + uint32_t nof_bits; + uint32_t nof_re; +} srslte_ra_nbits_t; + +/// According to Section 16.3.3 in TS 36.213 v13.3 +typedef struct SRSLTE_API { + uint32_t sc_spacing; ///< SC spacing (1bit) + uint32_t i_sc; ///< Subcarrier indication field (6bits) + uint32_t i_delay; ///< Scheduling delay (2bits) + uint32_t n_rep; ///< Repetition number (3bits) (Is this i_rep or N_rep?) + uint32_t i_mcs; ///< MCS field (3bits), according to 16.3.3-1 +} srslte_nbiot_dci_rar_grant_t; + +/// Functions +SRSLTE_API int srslte_ra_nbiot_dl_dci_to_grant(srslte_ra_nbiot_dl_dci_t* dci, + uint16_t msg_rnti, + srslte_ra_nbiot_dl_grant_t* grant, + uint32_t sfn, + uint32_t sf_idx, + uint32_t r_max, + bool is_prescheduled, + srslte_nbiot_mode_t mode); + +SRSLTE_API void srslte_ra_nbiot_dl_grant_to_nbits(srslte_ra_nbiot_dl_grant_t* grant, + srslte_nbiot_cell_t cell, + uint32_t sf_idx, + srslte_ra_nbits_t* nbits); + +SRSLTE_API void +srslte_ra_nbiot_ul_get_uci_grant(srslte_ra_nbiot_ul_grant_t* grant, const uint8_t resource_field, const uint32_t tti); + +SRSLTE_API int srslte_ra_nbiot_ul_dci_to_grant(srslte_ra_nbiot_ul_dci_t* dci, + srslte_ra_nbiot_ul_grant_t* grant, + uint32_t rx_tti, + srslte_npusch_sc_spacing_t spacing); + +SRSLTE_API void srslte_ra_nbiot_ul_grant_to_nbits(srslte_ra_nbiot_ul_grant_t* grant, srslte_ra_nbits_t* nbits); + +SRSLTE_API int +srslte_ra_nbiot_ul_rar_dci_to_grant(srslte_ra_nbiot_ul_dci_t* dci, srslte_ra_nbiot_ul_grant_t* grant, uint32_t rx_tti); + +SRSLTE_API void srslte_ra_npdsch_fprint(FILE* f, srslte_ra_nbiot_dl_dci_t* ra, uint32_t nof_prb); + +SRSLTE_API void srslte_ra_npusch_fprint(FILE* f, srslte_ra_nbiot_ul_dci_t* dci); + +SRSLTE_API void srslte_nbiot_dl_dci_fprint(FILE* f, srslte_ra_nbiot_dl_dci_t* dci); + +/*! + * Checks if TTI contains reference signal + * In safe mode only those subframes that are guaranteed to contain + * NRS are considered. + * + * \param tti the TTI in question + * \return true if TTI contains DL ref signal + */ +SRSLTE_API bool srslte_ra_nbiot_dl_has_ref_signal(uint32_t tti); + +/*! + * Checks if TTI contains reference signal for standalone deployment + * + * \param tti the TTI in question + * \return true if TTI contains DL ref signal + */ +SRSLTE_API bool srslte_ra_nbiot_dl_has_ref_signal_standalone(uint32_t tti); + +/*! + * Checks if TTI contains reference signal for inband deployments + * + * \param tti the TTI in question + * \return true if TTI contains DL ref signal + */ +SRSLTE_API bool srslte_ra_nbiot_dl_has_ref_signal_inband(uint32_t tti); + +/*! + * Checks if TTI is a valid downlink TTI, i.e., can carry either a NPDCCH or NPDSCH + * + * \param tti the TTI in question + * \return true if TTI is valid DL TTI + */ +SRSLTE_API bool srslte_ra_nbiot_is_valid_dl_sf(uint32_t tti); + +/*! + * Calculate the number of resource elements per subframe that carry data + * + * \param cell the cell structure + * \param l_start the starting OFDM symbols that carries data + * \return the number of resource elements + */ +SRSLTE_API uint32_t srslte_ra_nbiot_dl_grant_nof_re(srslte_nbiot_cell_t cell, uint32_t l_start); + +SRSLTE_API void srslte_ra_nbiot_dl_grant_fprint(FILE* f, srslte_ra_nbiot_dl_grant_t* grant); + +SRSLTE_API void srslte_ra_nbiot_ul_grant_fprint(FILE* f, srslte_ra_nbiot_ul_grant_t* grant); + +SRSLTE_API int srslte_ra_n_rep_sib1_nb(srslte_mib_nb_t* mib); + +SRSLTE_API int srslte_ra_nbiot_get_sib1_tbs(srslte_mib_nb_t* mib); + +SRSLTE_API int srslte_ra_nbiot_get_npdsch_tbs(uint32_t i_tbs, uint32_t i_sf); + +SRSLTE_API int srslte_ra_nbiot_get_npusch_tbs(uint32_t i_tbs, uint32_t i_ru); + +SRSLTE_API int srslte_ra_nbiot_get_starting_sib1_frame(uint32_t cell_id, srslte_mib_nb_t* mib); + +SRSLTE_API int srslte_ra_nbiot_sib1_start(uint32_t n_id_ncell, srslte_mib_nb_t* mib); + +SRSLTE_API float srslte_ra_nbiot_get_delta_f(srslte_npusch_sc_spacing_t spacing); + +#endif // SRSLTE_RA_NBIOT_H diff --git a/lib/src/phy/ch_estimation/chest_dl_nbiot.c b/lib/src/phy/ch_estimation/chest_dl_nbiot.c new file mode 100644 index 000000000..591d5cd31 --- /dev/null +++ b/lib/src/phy/ch_estimation/chest_dl_nbiot.c @@ -0,0 +1,355 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/utils/debug.h" + +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/utils/convolution.h" +#include "srslte/phy/utils/vector.h" + +int srslte_chest_dl_nbiot_init(srslte_chest_dl_nbiot_t* q, uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) { + bzero(q, sizeof(srslte_chest_dl_nbiot_t)); + + ret = srslte_refsignal_dl_nbiot_init(&q->nrs_signal); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing CSR signal (%d)\n", ret); + goto clean_exit; + } + + q->tmp_noise = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb)); + if (!q->tmp_noise) { + perror("malloc"); + goto clean_exit; + } + q->pilot_estimates = + srslte_vec_malloc(sizeof(cf_t) * SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb) + SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN); + if (!q->pilot_estimates) { + perror("malloc"); + goto clean_exit; + } + q->pilot_estimates_average = + srslte_vec_malloc(sizeof(cf_t) * SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb) + SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN); + if (!q->pilot_estimates_average) { + perror("malloc"); + goto clean_exit; + } + for (int i = 0; i < SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb); i++) { + q->pilot_estimates_average[i] = 1; + } + + q->pilot_recv_signal = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb)); + if (!q->pilot_recv_signal) { + perror("malloc"); + goto clean_exit; + } + + if (srslte_interp_linear_vector_init(&q->srslte_interp_linvec, SRSLTE_NRE * max_prb)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + goto clean_exit; + } + + if (srslte_interp_linear_init(&q->srslte_interp_lin, 2 * max_prb, SRSLTE_NRE / 2)) { + fprintf(stderr, "Error initializing interpolator\n"); + goto clean_exit; + } + + q->noise_alg = SRSLTE_NOISE_ALG_REFS; + srslte_chest_dl_nbiot_set_smooth_filter3_coeff(q, 0.1); + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret != SRSLTE_SUCCESS) { + srslte_chest_dl_nbiot_free(q); + } + return ret; +} + +void srslte_chest_dl_nbiot_free(srslte_chest_dl_nbiot_t* q) +{ + srslte_refsignal_dl_nbiot_free(&q->nrs_signal); + + if (q->tmp_noise) { + free(q->tmp_noise); + } + srslte_interp_linear_vector_free(&q->srslte_interp_linvec); + srslte_interp_linear_free(&q->srslte_interp_lin); + + if (q->pilot_estimates) { + free(q->pilot_estimates); + } + if (q->pilot_estimates_average) { + free(q->pilot_estimates_average); + } + if (q->pilot_recv_signal) { + free(q->pilot_recv_signal); + } +} + +int srslte_chest_dl_nbiot_set_cell(srslte_chest_dl_nbiot_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + // set ports configuration + if (cell.nof_ports == 0) { + cell.nof_ports = SRSLTE_NBIOT_MAX_PORTS; + } + + if (q != NULL && srslte_nbiot_cell_isvalid(&cell)) { + if (q->cell.n_id_ncell != cell.n_id_ncell || q->cell.base.nof_prb == 0) { + q->cell = cell; + + ret = srslte_refsignal_dl_nbiot_set_cell(&q->nrs_signal, cell); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing NRS signal (%d)\n", ret); + return SRSLTE_ERROR; + } + + if (srslte_interp_linear_vector_resize(&q->srslte_interp_linvec, SRSLTE_NRE * cell.base.nof_prb)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + return SRSLTE_ERROR; + } + + if (srslte_interp_linear_resize(&q->srslte_interp_lin, 2 * cell.base.nof_prb, SRSLTE_NRE / 2)) { + fprintf(stderr, "Error initializing interpolator\n"); + return SRSLTE_ERROR; + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Uses the difference between the averaged and non-averaged pilot estimates */ +static float estimate_noise_pilots(srslte_chest_dl_nbiot_t* q, uint32_t port_id) +{ + int nref = srslte_refsignal_cs_nof_re(NULL, NULL, port_id); + + // Substract noisy pilot estimates + srslte_vec_sub_ccc(q->pilot_estimates_average, q->pilot_estimates, q->tmp_noise, nref); + + // Compute average power. Normalized for filter len 3 using ML + float norm = 1; + if (q->smooth_filter_len == 3) { + float a = q->smooth_filter[0]; + float norm3 = 6.143 * a * a + 0.04859 * a - 0.002774; + norm /= norm3; + } + float power = norm * q->cell.nof_ports * srslte_vec_avg_power_cf(q->tmp_noise, nref); + return power; +} + +#define cesymb(i) ce[SRSLTE_RE_IDX(q->cell.base.nof_prb, i, 0)] + +static void interpolate_pilots(srslte_chest_dl_nbiot_t* q, cf_t* pilot_estimates, cf_t* ce, uint32_t port_id) +{ + uint32_t nsymbols = srslte_refsignal_cs_nof_symbols(NULL, NULL, port_id); + int num_ces = q->cell.base.nof_prb * SRSLTE_NRE; + cf_t ce_avg[2][num_ces]; + + // interpolate the symbols with references in the freq domain + DEBUG("Interpolating %d pilots in %d symbols at port %d.\n", nsymbols * 2, nsymbols, port_id); + for (int l = 0; l < nsymbols; l++) { + uint32_t fidx_offset = srslte_refsignal_dl_nbiot_fidx(q->cell, l, port_id, 0); + // points to the RE of the beginning of the l'th symbol containing a ref + uint32_t ce_idx = srslte_refsignal_nrs_nsymbol(l) * q->cell.base.nof_prb * SRSLTE_NRE; + ce_idx += q->cell.nbiot_prb * SRSLTE_NRE; + DEBUG(" - freq.-dmn interp. in sym %d at RE %d\n", srslte_refsignal_nrs_nsymbol(l), ce_idx + fidx_offset); + srslte_interp_linear_offset( + &q->srslte_interp_lin, &pilot_estimates[2 * l], &ce[ce_idx], fidx_offset, SRSLTE_NRE / 2 - fidx_offset); + } + + // average the two neighbor subcarriers containing pilots + for (int l = 0; l < nsymbols / 2; l++) { + int sym_idx = srslte_refsignal_nrs_nsymbol(l * 2); + srslte_vec_sum_ccc(&cesymb(sym_idx), &cesymb(sym_idx + 1), ce_avg[l], num_ces); + // FIXME: use vector operation for this + for (int k = 0; k < num_ces; k++) { + ce_avg[l][k] /= 2; + } + // use avarage for both symbols + memcpy(&cesymb(sym_idx), ce_avg[l], num_ces * sizeof(cf_t)); + memcpy(&cesymb(sym_idx + 1), ce_avg[l], num_ces * sizeof(cf_t)); + } + + // now interpolate in the time domain between symbols + srslte_interp_linear_vector(&q->srslte_interp_linvec, ce_avg[0], ce_avg[1], &cesymb(0), 5, 5); + srslte_interp_linear_vector(&q->srslte_interp_linvec, ce_avg[0], ce_avg[1], &cesymb(7), 5, 5); +} + +void srslte_chest_dl_nbiot_set_smooth_filter(srslte_chest_dl_nbiot_t* q, float* filter, uint32_t filter_len) +{ + if (filter_len < SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN) { + if (filter) { + memcpy(q->smooth_filter, filter, filter_len * sizeof(float)); + q->smooth_filter_len = filter_len; + } else { + q->smooth_filter_len = 0; + } + } else { + fprintf(stderr, + "Error setting smoothing filter: filter len exceeds maximum (%d>%d)\n", + filter_len, + SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN); + } +} + +void srslte_chest_dl_nbiot_set_noise_alg(srslte_chest_dl_nbiot_t* q, srslte_chest_dl_noise_alg_t noise_estimation_alg) +{ + q->noise_alg = noise_estimation_alg; +} + +void srslte_chest_dl_nbiot_set_smooth_filter3_coeff(srslte_chest_dl_nbiot_t* q, float w) +{ + q->smooth_filter_len = 3; + q->smooth_filter[0] = w; + q->smooth_filter[2] = w; + q->smooth_filter[1] = 1 - 2 * w; +} + +static void average_pilots(srslte_chest_dl_nbiot_t* q, cf_t* input, cf_t* output, uint32_t port_id) +{ + uint32_t nsymbols = srslte_refsignal_cs_nof_symbols(NULL, NULL, port_id); + uint32_t nref = 2; + + // Average in the frequency domain + for (int l = 0; l < nsymbols; l++) { + srslte_conv_same_cf(&input[l * nref], q->smooth_filter, &output[l * nref], nref, q->smooth_filter_len); + } +} + +float srslte_chest_nbiot_dl_rssi(srslte_chest_dl_nbiot_t* q, cf_t* input, uint32_t port_id) +{ + float rssi = 0; + uint32_t nsymbols = srslte_refsignal_cs_nof_symbols(NULL, NULL, port_id); + for (uint32_t l = 0; l < nsymbols; l++) { + cf_t* tmp = &input[srslte_refsignal_nrs_nsymbol(l) * SRSLTE_NRE]; + rssi += srslte_vec_dot_prod_conj_ccc(tmp, tmp, SRSLTE_NRE); + } + return rssi / nsymbols; +} + +int srslte_chest_dl_nbiot_estimate_port(srslte_chest_dl_nbiot_t* q, + cf_t* input, + cf_t* ce, + uint32_t sf_idx, + uint32_t port_id) +{ + DEBUG("x.%d: Estimating DL channel on port %d.\n", sf_idx, port_id); + int nref = srslte_refsignal_nbiot_cs_nof_re(&q->cell, port_id); + + // Get references from the input signal + srslte_refsignal_nrs_get_sf(q->cell, port_id, input, q->pilot_recv_signal); + + // Use the known NRS signal to compute Least-squares estimates + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->nrs_signal.pilots[port_id][sf_idx], q->pilot_estimates, nref); + + if (SRSLTE_VERBOSE_ISDEBUG()) { + printf("Rx pilots:\n"); + srslte_vec_fprint_c(stdout, q->pilot_recv_signal, nref); + printf("Tx pilots on port=%d for sf_idx=%d:\n", port_id, sf_idx); + srslte_vec_fprint_c(stdout, q->nrs_signal.pilots[port_id][sf_idx], nref); + printf("Estimates:\n"); + srslte_vec_fprint_c(stdout, q->pilot_estimates, nref); + printf("Average estimates:\n"); + srslte_vec_fprint_c(stdout, q->pilot_estimates_average, nref); + } + + if (ce != NULL) { + // Smooth estimates (if applicable) and interpolate + if (q->smooth_filter_len == 0 || (q->smooth_filter_len == 3 && q->smooth_filter[0] == 0)) { + interpolate_pilots(q, q->pilot_estimates, ce, port_id); + } else { + average_pilots(q, q->pilot_estimates, q->pilot_estimates_average, port_id); + interpolate_pilots(q, q->pilot_estimates_average, ce, port_id); + } + + // Estimate noise power + if (q->noise_alg == SRSLTE_NOISE_ALG_REFS && q->smooth_filter_len > 0) { + q->noise_estimate[port_id] = estimate_noise_pilots(q, port_id); + } + } + + if (SRSLTE_VERBOSE_ISDEBUG()) { + printf("Updated Average estimates:\n"); + srslte_vec_fprint_c(stdout, q->pilot_estimates_average, nref); + } + + // Compute RSRP for the channel estimates in this port + q->rsrp[port_id] = srslte_vec_avg_power_cf(q->pilot_recv_signal, nref); + if (port_id == 0) { + // compute rssi only for port 0 + q->rssi[port_id] = srslte_chest_nbiot_dl_rssi(q, input, port_id); + } + + return 0; +} + +int srslte_chest_dl_nbiot_estimate(srslte_chest_dl_nbiot_t* q, cf_t* input, cf_t** ce, uint32_t sf_idx) +{ + for (uint32_t port_id = 0; port_id < q->cell.nof_ports; port_id++) { + srslte_chest_dl_nbiot_estimate_port(q, input, ce[port_id], sf_idx, port_id); + } + return SRSLTE_SUCCESS; +} + +float srslte_chest_dl_nbiot_get_noise_estimate(srslte_chest_dl_nbiot_t* q) +{ + return srslte_vec_acc_ff(q->noise_estimate, q->cell.nof_ports) / q->cell.nof_ports; +} + +float srslte_chest_dl_nbiot_get_snr(srslte_chest_dl_nbiot_t* q) +{ + return srslte_chest_dl_nbiot_get_rsrp(q) / srslte_chest_dl_nbiot_get_noise_estimate(q); +} + +float srslte_chest_dl_nbiot_get_rssi(srslte_chest_dl_nbiot_t* q) +{ + return 4 * q->rssi[0] / SRSLTE_NRE; +} + +/* q->rssi[0] is the average power in all RE in all symbol containing references for port 0. + * q->rssi[0]/q->cell.nof_prb is the average power per PRB + * q->rsrp[0] is the average power of RE containing references only (for port 0). + */ +float srslte_chest_dl_nbiot_get_rsrq(srslte_chest_dl_nbiot_t* q) +{ + return q->rsrp[0] / q->rssi[0]; +} + +float srslte_chest_dl_nbiot_get_rsrp(srslte_chest_dl_nbiot_t* q) +{ + // return sum of power received from all tx ports + return srslte_vec_acc_ff(q->rsrp, q->cell.nof_ports); +} \ No newline at end of file diff --git a/lib/src/phy/ch_estimation/refsignal_dl.c b/lib/src/phy/ch_estimation/refsignal_dl.c index 80806e81a..052089a00 100644 --- a/lib/src/phy/ch_estimation/refsignal_dl.c +++ b/lib/src/phy/ch_estimation/refsignal_dl.c @@ -228,7 +228,11 @@ inline uint32_t srslte_refsignal_cs_nof_symbols(srslte_refsignal_t* q, srslte_dl inline uint32_t srslte_refsignal_cs_nof_re(srslte_refsignal_t* q, srslte_dl_sf_cfg_t* sf, uint32_t port_id) { - return srslte_refsignal_cs_nof_symbols(q, sf, port_id) * q->cell.nof_prb * 2; // 2 RE per PRB + uint32_t nof_re = srslte_refsignal_cs_nof_symbols(q, sf, port_id); + if (q != NULL) { + nof_re *= q->cell.nof_prb * 2; // 2 RE per PRB + } + return nof_re; } inline uint32_t srslte_refsignal_cs_fidx(srslte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) { diff --git a/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c b/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c new file mode 100644 index 000000000..0896d5274 --- /dev/null +++ b/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c @@ -0,0 +1,265 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/refsignal_dl_nbiot.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define EXTRA_DEBUG 0 + +uint32_t srslte_nbiot_refsignal_dl_v(uint32_t port_id, uint32_t ref_symbol_idx) +{ + uint32_t v = 0; + switch (port_id) { + case 0: + if (!(ref_symbol_idx % 2)) { + v = 0; + } else { + v = 3; + } + break; + case 1: + if (!(ref_symbol_idx % 2)) { + v = 3; + } else { + v = 0; + } + break; + } + return v; +} + +uint32_t srslte_refsignal_dl_nbiot_nof_symbols(uint32_t port_id) +{ + if (port_id < 2) { + return 4; + } else { + return 2; + } +} + +uint32_t srslte_refsignal_dl_nbiot_fidx(srslte_nbiot_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) +{ + return (6 * m + ((srslte_nbiot_refsignal_dl_v(port_id, l) + (cell.n_id_ncell % 6)) % 6)); +} + +inline uint32_t srslte_nbiot_refsignal_dl_nsymbol(uint32_t l, srslte_cp_t cp, uint32_t port_id) +{ + if (port_id < 2) { + if (l % 2) { + return (l / 2 + 1) * SRSLTE_CP_NSYMB(cp) - 3; + } else { + return (l / 2) * SRSLTE_CP_NSYMB(cp); + } + } else { + return 1 + l * SRSLTE_CP_NSYMB(cp); + } +} + +inline uint32_t srslte_refsignal_nbiot_cs_nof_re(srslte_nbiot_cell_t* cell, uint32_t port_id) +{ + return srslte_refsignal_cs_nof_symbols(NULL, NULL, port_id) * cell->base.nof_prb * 2; // 2 RE per PRB +} + +/** Allocates and precomputes the Narrowband Reference Signal (NRS) signal for + * the 20 slots in a subframe + */ +int srslte_refsignal_dl_nbiot_init(srslte_refsignal_dl_nbiot_t* q) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_refsignal_dl_nbiot_t)); + + for (uint32_t p = 0; p < 2; p++) { + for (uint32_t i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { + q->pilots[p][i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_NBIOT_REFSIGNAL_NUM_SF(SRSLTE_NBIOT_MAX_PRB, p)); + if (!q->pilots[p][i]) { + perror("malloc"); + goto free_and_exit; + } + } + } + + ret = SRSLTE_SUCCESS; + } +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_dl_nbiot_free(q); + } + return ret; +} + +int srslte_refsignal_dl_nbiot_set_cell(srslte_refsignal_dl_nbiot_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + INFO("Generating NRS for n_id_ncell=%d\n", cell.n_id_ncell); + + if (q != NULL && srslte_nbiot_cell_isvalid(&cell)) { + ret = SRSLTE_ERROR; + + q->cell = cell; + + srslte_sequence_t seq; + bzero(&seq, sizeof(srslte_sequence_t)); + if (srslte_sequence_init(&seq, 2 * 2 * SRSLTE_MAX_PRB)) { + goto free_and_exit; + } + + for (uint32_t ns = 0; ns < SRSLTE_NSLOTS_X_FRAME; ns++) { + for (uint32_t p = 0; p < 2; p++) { + uint32_t nsymbols = srslte_refsignal_dl_nbiot_nof_symbols(p) / 2; + for (uint32_t l = 0; l < nsymbols; l++) { + /* Compute sequence init value */ + uint32_t lp = srslte_refsignal_nrs_nsymbol(l); + uint32_t N_cp = 1; + uint32_t c_init = 1024 * (7 * (ns + 1) + lp + 1) * (2 * cell.n_id_ncell + 1) + 2 * cell.n_id_ncell + N_cp; + + /* generate sequence for this symbol and slot */ + srslte_sequence_set_LTE_pr(&seq, 2 * 2 * SRSLTE_MAX_PRB, c_init); + + /* Compute signal */ + for (uint32_t i = 0; i < 2; i++) { + uint32_t mp = i + SRSLTE_MAX_PRB - 1; + /* save signal */ + int idx = + SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(i, (ns % 2) * nsymbols + l, SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL); + DEBUG("Ref port=%d, lp=%d, ns=%d, ns/2=%d, idx=%d, i=%d, nsymbols=%d, l=%d\n", + p, + lp, + ns, + ns / 2, + idx, + i, + nsymbols, + l); + q->pilots[p][ns / 2][idx] = + (1 - 2 * (float)seq.c[2 * mp]) / sqrt(2) + _Complex_I * (1 - 2 * (float)seq.c[2 * mp + 1]) / sqrt(2); + } + } + } + } + srslte_sequence_free(&seq); + ret = SRSLTE_SUCCESS; + } +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_dl_nbiot_free(q); + } + return ret; +} + +// Deallocates a srslte_refsignal_cs_t object allocated with srslte_refsignal_cs_init +void srslte_refsignal_dl_nbiot_free(srslte_refsignal_dl_nbiot_t* q) +{ + for (int p = 0; p < 2; p++) { + for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { + if (q->pilots[p][i]) { + free(q->pilots[p][i]); + } + } + } + bzero(q, sizeof(srslte_refsignal_dl_nbiot_t)); +} + +/** Regardless of the number of antenna ports, NRS are always transmitted + * in the last two symbols of each slot + */ +inline uint32_t srslte_refsignal_nrs_nsymbol(uint32_t l) +{ + if (l % 2 == 0) { + return ((l / 2) * SRSLTE_CP_NORM_NSYMB + 5); + } else { + return ((l / 2) * SRSLTE_CP_NORM_NSYMB + 6); + } +} + +/** Maps the NRS reference signal initialized with srslte_refsignal_cs_init() into an array of subframe symbols + Note that the NRS signal is identical to the CRS, but is initalized with NCellID + */ +int srslte_refsignal_nrs_put_sf(srslte_nbiot_cell_t cell, uint32_t port_id, cf_t* pilots, cf_t* sf_symbols) +{ + if (srslte_nbiot_cell_isvalid(&cell) && srslte_portid_isvalid(port_id) && pilots != NULL && sf_symbols != NULL) { + for (int l = 0; l < srslte_refsignal_dl_nbiot_nof_symbols(port_id); l++) { + uint32_t nsymbol = srslte_refsignal_nrs_nsymbol(l); + // Two reference symbols per OFDM symbol + for (int m = 0; m < SRSLTE_NBIOT_NUM_NRS_X_SYM_X_PORT; m++) { + uint32_t fidx = srslte_refsignal_dl_nbiot_fidx(cell, l, port_id, m) + cell.nbiot_prb * SRSLTE_NRE; + sf_symbols[SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx)] = pilots[SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(m, l, 1)]; +#if EXTRA_DEBUG + DEBUG("port: %d, re_idx: %d, pilot_idx: %d, %+2.2f%+2.2fi\n", port_id, SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx), SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(i,l, 1), + __real__ sf_symbols[SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx)], __imag__ sf_symbols[SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx)]); +#endif + } + } + +#if EXTRA_DEBUG + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE chest_nbiot_tx_after_mapping.bin: NRS after mapping\n", 0); + srslte_vec_save_file( + "chest_nbiot_tx_after_mapping.bin", pilots, SRSLTE_REFSIGNAL_NUM_SF(1, port_id) * sizeof(cf_t)); + } +#endif + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/** Copies the RE containing references from an array of subframe symbols to the pilots array. */ +int srslte_refsignal_nrs_get_sf(srslte_nbiot_cell_t cell, uint32_t port_id, cf_t* sf_symbols, cf_t* pilots) +{ + if (srslte_nbiot_cell_isvalid(&cell) && srslte_portid_isvalid(port_id) && pilots != NULL && sf_symbols != NULL) { + for (int l = 0; l < srslte_refsignal_dl_nbiot_nof_symbols(port_id); l++) { + uint32_t nsymbol = srslte_refsignal_nrs_nsymbol(l); + // read both pilots of this symbol + for (int m = 0; m < SRSLTE_NBIOT_NUM_NRS_X_SYM_X_PORT; m++) { + uint32_t fidx = srslte_refsignal_dl_nbiot_fidx(cell, l, port_id, m) + cell.nbiot_prb * SRSLTE_NRE; + pilots[SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(m, l, 1)] = sf_symbols[SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx)]; +#if EXTRA_DEBUG + DEBUG("port: %d, pilot_idx: %d, re_idx: %d, %+2.2f%+2.2fi\n", port_id, SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(m, l, 1), SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx), + __real__ pilots[SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(m, l, 1)], __imag__ pilots[SRSLTE_NBIOT_REFSIGNAL_PILOT_IDX(m, l, 1)]); +#endif + } + } + +#if EXTRA_DEBUG + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE chest_nbiot_rx_after_demapping.bin: NRS after demapping\n",0); + srslte_vec_save_file("chest_nbiot_rx_after_demapping.bin", pilots, SRSLTE_REFSIGNAL_NUM_SF(1, port_id) * sizeof(cf_t)); + } +#endif + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} diff --git a/lib/src/phy/ch_estimation/test/CMakeLists.txt b/lib/src/phy/ch_estimation/test/CMakeLists.txt index 089a8e63b..0d0bada33 100644 --- a/lib/src/phy/ch_estimation/test/CMakeLists.txt +++ b/lib/src/phy/ch_estimation/test/CMakeLists.txt @@ -46,4 +46,14 @@ target_link_libraries(refsignal_ul_test_all srslte_phy srslte_common) add_test(chest_test_ul_cellid0 chest_test_ul -c 0 -r 50) add_test(chest_test_ul_cellid1 chest_test_ul -c 1 -r 50) -add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) \ No newline at end of file +add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) + + +######################################################################## +# Downlink Channel Estimation for NB-IoT TEST +######################################################################## + +add_executable(chest_nbiot_test_dl chest_nbiot_test_dl.c) +target_link_libraries(chest_nbiot_test_dl srslte_phy) + +add_test(chest_nbiot_test_dl chest_nbiot_test_dl) diff --git a/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c b/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c new file mode 100644 index 000000000..0230884f5 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c @@ -0,0 +1,256 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/channel/ch_awgn.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define TESTASSERT(cond) \ + { \ + if (!(cond)) { \ + fprintf(stderr, "[%s][Line %d]: FAIL %s\n", __FUNCTION__, __LINE__, (#cond)); \ + return -1; \ + } \ + } + +srslte_nbiot_cell_t cell = {.base = {.nof_prb = 1, .cp = SRSLTE_CP_NORM, .id = 0}, + .base.nof_ports = 1, + .base.nof_prb = 1, + .nbiot_prb = 0, + .n_id_ncell = 0}; + +bool have_ofdm = false; +bool have_channel = false; +float snr_db = 100.0f; +char* output_matlab = NULL; + +void usage(char* prog) +{ + printf("Usage: %s [recovnm]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", cell.base.nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c n_id_ncell (1000 tests all). [Default %d]\n", cell.n_id_ncell); + + printf("\t-n Enable channel. [Default %s]\n", have_channel ? "Yes" : "No"); + printf("\t-s SNR in dB [Default %.1fdB]*\n", snr_db); + printf("\t-m Enable OFDM. [Default %s]\n", have_ofdm ? "Yes" : "No"); + + printf("\t-o output matlab file [Default %s]\n", output_matlab ? output_matlab : "None"); + printf("\t-v increase verbosity\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "recosvnm")) != -1) { + switch (opt) { + case 'r': + cell.base.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.n_id_ncell = atoi(argv[optind]); + break; + case 'n': + have_channel = true; + break; + case 'm': + have_ofdm = true; + break; + case 's': + snr_db = (float)atof(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + srslte_chest_dl_nbiot_t est = {}; + int ret = SRSLTE_ERROR; + cf_t * input = NULL, *ce = NULL, *h = NULL, *output = NULL, *sf_buffer = NULL; + + parse_args(argc, argv); + + int num_re = 2 * cell.base.nof_prb * SRSLTE_NRE * SRSLTE_CP_NSYMB(cell.base.cp); + + input = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!input) { + perror("srslte_vec_malloc"); + goto do_exit; + } + output = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!output) { + perror("srslte_vec_malloc"); + goto do_exit; + } + sf_buffer = malloc(2 * SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.base.nof_prb)) * sizeof(cf_t)); + if (!sf_buffer) { + perror("malloc"); + return -1; + } + h = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!h) { + perror("srslte_vec_malloc"); + goto do_exit; + } + ce = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!ce) { + perror("srslte_vec_malloc"); + goto do_exit; + } + for (int j = 0; j < num_re; j++) { + ce[j] = 1; + } + + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE chest_start.bin: channel estimates start\n"); + srslte_vec_save_file("chest_start.bin", ce, num_re * sizeof(cf_t)); + } + + if (srslte_chest_dl_nbiot_init(&est, SRSLTE_NBIOT_MAX_PRB)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + if (srslte_chest_dl_nbiot_set_cell(&est, cell) != SRSLTE_SUCCESS) { + fprintf(stderr, "Error setting channel estimator's cell configuration\n"); + return -1; + } + + for (int sf_idx = 0; sf_idx < 1; sf_idx++) { + for (int n_port = 0; n_port < cell.base.nof_ports; n_port++) { + bzero(input, sizeof(cf_t) * num_re); + for (int i = 0; i < num_re; i++) { + input[i] = 0.5 - rand() / RAND_MAX + I * (0.5 - rand() / RAND_MAX); + } + + bzero(ce, sizeof(cf_t) * num_re); + bzero(h, sizeof(cf_t) * num_re); + + srslte_ofdm_t ifft, fft; + if (have_ofdm) { + if (srslte_ofdm_tx_init(&ifft, cell.base.cp, input, sf_buffer, cell.base.nof_prb)) { + fprintf(stderr, "Error initializing IFFT\n"); + return -1; + } + if (srslte_ofdm_rx_init(&fft, cell.base.cp, sf_buffer, input, cell.base.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return -1; + } + srslte_ofdm_set_normalize(&ifft, true); + srslte_ofdm_set_normalize(&fft, true); + } + + srslte_refsignal_nrs_put_sf(cell, n_port, est.nrs_signal.pilots[0][0], input); + + if (have_channel) { + // Add noise + float std_dev = powf(10, -(snr_db + 3.0f) / 20.0f) * 0.1f; + srslte_ch_awgn_c(est.pilot_recv_signal, est.pilot_recv_signal, std_dev, SRSLTE_REFSIGNAL_MAX_NUM_SF(1)); + } + + if (have_ofdm) { + srslte_ofdm_tx_sf(&ifft); + srslte_ofdm_rx_sf(&fft); + } + + // check length of LTE CSR signal + for (int i = 0; i < SRSLTE_NBIOT_MAX_PORTS; i++) { + TESTASSERT(srslte_refsignal_nbiot_cs_nof_re(&cell, i) == 8); + } + + srslte_chest_dl_nbiot_estimate_port(&est, input, ce, sf_idx, n_port); + + float rsrq = srslte_chest_dl_nbiot_get_rsrq(&est); + float rsrp = srslte_chest_dl_nbiot_get_rsrp(&est); + float noise = srslte_chest_dl_nbiot_get_noise_estimate(&est); + float snr = srslte_chest_dl_nbiot_get_snr(&est); + DEBUG("rsrq=%4.2f, rsrp=%4.2f, noise=%4.2f, snr=%4.2f\n", rsrq, rsrp, noise, snr); + + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE chest_final.bin: channel after estimation\n"); + srslte_vec_save_file("chest_final.bin", ce, num_re * sizeof(cf_t)); + } + + // use ZF equalizer + srslte_predecoding_single(input, ce, output, NULL, num_re, 1.0, 0); + + if (!have_channel) { + if (memcmp(est.nrs_signal.pilots[0][0], est.pilot_recv_signal, 8) == 0) { + printf("ok\n"); + } else { + printf("nok\n"); + goto do_exit; + } + } + + if (have_ofdm) { + srslte_ofdm_tx_free(&ifft); + srslte_ofdm_rx_free(&fft); + } + } + } + + srslte_chest_dl_nbiot_free(&est); + + ret = SRSLTE_SUCCESS; + +do_exit: + + if (output) { + free(output); + } + if (ce) { + free(ce); + } + if (input) { + free(input); + } + if (h) { + free(h); + } + if (sf_buffer) { + free(sf_buffer); + } + + return ret; +} diff --git a/lib/src/phy/common/phy_common.c b/lib/src/phy/common/phy_common.c index 11dede0e3..49f7175b6 100644 --- a/lib/src/phy/common/phy_common.c +++ b/lib/src/phy/common/phy_common.c @@ -46,7 +46,7 @@ bool srslte_cellid_isvalid(uint32_t cell_id) { } bool srslte_nofprb_isvalid(uint32_t nof_prb) { - if (nof_prb >= 6 && nof_prb <= 100) { + if (nof_prb == 1 || (nof_prb >= 6 && nof_prb <= SRSLTE_MAX_PRB)) { return true; } else { return false; diff --git a/lib/src/phy/phch/npbch.c b/lib/src/phy/phch/npbch.c new file mode 100644 index 000000000..31c290e00 --- /dev/null +++ b/lib/src/phy/phch/npbch.c @@ -0,0 +1,627 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prb_dl.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/phch/ra_nbiot.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define SRSLTE_NBIOT_NPBCH_NUM_REFS_PER_SYMB 4 + +// Table 5.3.1.1-1 in 36.212 v13.3.0 +const uint8_t srslte_npbch_crc_mask[4][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}; + +/** Initializes the NPBCH transmitter and receiver. + * At the receiver, the field nof_ports in the cell structure indicates the + * maximum number of BS transmitter ports to look for. + */ +int srslte_npbch_init(srslte_npbch_t* q) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + bzero(q, sizeof(srslte_npbch_t)); + + ret = SRSLTE_ERROR; + + q->nof_symbols = SRSLTE_NPBCH_NUM_RE; + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + fprintf(stderr, "Error initiating modem table.\n"); + goto clean; + } + + int poly[3] = {0x6D, 0x4F, 0x57}; + if (srslte_viterbi_init(&q->decoder, SRSLTE_VITERBI_37, poly, SRSLTE_MIB_NB_CRC_LEN, true)) { + fprintf(stderr, "Error initiating Viterbi.\n"); + goto clean; + } + if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC16, 16)) { + fprintf(stderr, "Error initiating CRC object.\n"); + goto clean; + } + q->encoder.K = 7; + q->encoder.R = 3; + q->encoder.tail_biting = true; + memcpy(q->encoder.poly, poly, 3 * sizeof(int)); + + q->d = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->d) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->ce[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->ce[i]) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->x[i]) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols * SRSLTE_NPBCH_NUM_FRAMES); + if (!q->symbols[i]) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + } + q->llr = srslte_vec_malloc(sizeof(float) * q->nof_symbols * SRSLTE_NPBCH_NUM_FRAMES * 2); + if (!q->llr) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + + q->temp = srslte_vec_malloc(sizeof(float) * q->nof_symbols * SRSLTE_NPBCH_NUM_FRAMES * 2); + if (!q->temp) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + q->rm_b = srslte_vec_malloc(sizeof(float) * q->nof_symbols * SRSLTE_NPBCH_NUM_FRAMES * 2); + if (!q->rm_b) { + fprintf(stderr, "Error allocating memory.\n"); + goto clean; + } + ret = SRSLTE_SUCCESS; + } +clean: + if (ret == SRSLTE_ERROR) { + srslte_npbch_free(q); + } + return ret; +} + +void srslte_npbch_free(srslte_npbch_t* q) +{ + if (q->d) { + free(q->d); + } + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->ce[i]) { + free(q->ce[i]); + } + if (q->x[i]) { + free(q->x[i]); + } + if (q->symbols[i]) { + free(q->symbols[i]); + } + } + if (q->llr) { + free(q->llr); + } + if (q->temp) { + free(q->temp); + } + if (q->rm_b) { + free(q->rm_b); + } + srslte_sequence_free(&q->seq); + srslte_modem_table_free(&q->mod); + srslte_viterbi_free(&q->decoder); +} + +int srslte_npbch_set_cell(srslte_npbch_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_nbiot_cell_isvalid(&cell)) { + // set ports configuration + if (cell.nof_ports == 0) { + q->search_all_ports = true; + cell.nof_ports = SRSLTE_NBIOT_MAX_PORTS; + } else { + q->search_all_ports = false; + } + + if (q->cell.n_id_ncell != cell.n_id_ncell || q->cell.base.nof_prb == 0) { + q->cell = cell; + if (srslte_sequence_npbch(&q->seq, q->cell.base.cp, q->cell.n_id_ncell)) { + fprintf(stderr, "Error initiating NPBCH sequence.\n"); + return SRSLTE_ERROR; + } + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/** Packs the MIB-NB, according to TS 36.331 v13.2.0 Section 6.7.2 + */ +void srslte_npbch_mib_pack(uint32_t hfn, uint32_t sfn, srslte_mib_nb_t mib, uint8_t* payload) +{ + uint8_t* msg = payload; + bzero(msg, SRSLTE_MIB_NB_LEN); + + // System Frame Number (SFN), 4 MSB + srslte_bit_unpack(sfn >> 6, &msg, 4); + + // Hyper Frame Number (HFB), 2 LSB + srslte_bit_unpack(hfn, &msg, 2); + + // schedulingInfoSIB1, integer 0-15, 4 bits + srslte_bit_unpack(mib.sched_info_sib1, &msg, 4); + + // system info value tag, integer 0-31, 5 bits + srslte_bit_unpack(mib.sys_info_tag, &msg, 5); + + // access barring enabled, 1 bit + srslte_bit_unpack(mib.ac_barring, &msg, 1); + + // operation mode info, 2 for mode + 5 for config, 7 bits in total + srslte_bit_unpack(mib.mode, &msg, 2); + + // 11 spare bits +} + +/** Unpacks MIB-NB from NPBCH message. + * msg buffer must be 34 byte length at least + */ +void srslte_npbch_mib_unpack(uint8_t* msg, srslte_mib_nb_t* mib) +{ + if (mib) { + mib->sfn = (srslte_bit_pack(&msg, 4) << 6) & 0x3C0; + mib->hfn = srslte_bit_pack(&msg, 2) & 0x3; + mib->sched_info_sib1 = srslte_bit_pack(&msg, 4) & 0x0000ffff; + mib->sys_info_tag = srslte_bit_pack(&msg, 5) & 0x0001ffff; + mib->ac_barring = srslte_bit_pack(&msg, 1) & 0x1; + mib->mode = srslte_bit_pack(&msg, 2) & 0x0000000B; + } +} + +void srslte_mib_nb_printf(FILE* stream, srslte_nbiot_cell_t cell, srslte_mib_nb_t* mib) +{ + fprintf(stream, " - N_id_ncell: %d\n", cell.n_id_ncell); + fprintf(stream, " - Release: %s\n", cell.is_r14 ? "r14" : "r13"); + fprintf(stream, " - Nof ports: %d\n", cell.nof_ports); + fprintf(stream, " - SFN: %d\n", mib->sfn); + fprintf(stream, " - HFN (2 LSB): %d\n", mib->hfn); + fprintf(stream, " - Sched. Info SIB1 %d\n", mib->sched_info_sib1); + fprintf(stream, " - First frame %d\n", srslte_ra_nbiot_get_starting_sib1_frame(cell.n_id_ncell, mib)); + fprintf(stream, " - #repetitions %d\n", srslte_ra_n_rep_sib1_nb(mib)); + fprintf(stream, " - TBS %d\n", srslte_ra_nbiot_get_sib1_tbs(mib)); + fprintf(stream, " - System Info Val %d\n", mib->sys_info_tag); + fprintf(stream, " - AC barring %s\n", mib->ac_barring ? "Yes" : "No"); + fprintf(stream, " - Operating mode %s\n", srslte_nbiot_mode_string(mib->mode)); +} + +int srslte_npbch_put_subframe(srslte_npbch_t* q, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + cf_t* sf[SRSLTE_MAX_PORTS], + uint32_t frame_idx) +{ + return srslte_npbch_encode(q, bch_payload, sf, frame_idx); +} + +/** Converts the MIB-NB message to symbols mapped to the first subframe, + * The MIB-NB is split over 8 blocks, each of which is repeated 8 times, always in SF0, + * it therefore lasts for 640ms. + */ +int srslte_npbch_encode(srslte_npbch_t* q, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + cf_t* sf[SRSLTE_MAX_PORTS], + uint32_t frame_idx) +{ + int nof_bits; + int block_idx = (frame_idx / SRSLTE_NPBCH_NUM_REP) % SRSLTE_NPBCH_NUM_BLOCKS; + cf_t* x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && bch_payload != NULL && q->cell.nof_ports != 0) { + for (int i = 0; i < q->cell.nof_ports; i++) { + if (sf[i] == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + // Set pointers for layermapping & precoding + nof_bits = 2 * q->nof_symbols; + + // number of layers equals number of ports + for (int i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + // generate new BCH message every 64 frames + if ((frame_idx % SRSLTE_NPBCH_NUM_FRAMES) == 0) { + INFO("Encoding new NPBCH signal in frame %d.\n", frame_idx); + + memcpy(q->data, bch_payload, sizeof(uint8_t) * SRSLTE_MIB_NB_LEN); + + // encode and rate-match + srslte_crc_attach(&q->crc, q->data, SRSLTE_MIB_NB_LEN); + srslte_npbch_crc_set_mask(q->data, q->cell.nof_ports); + + srslte_convcoder_encode(&q->encoder, q->data, q->data_enc, SRSLTE_MIB_NB_CRC_LEN); + + srslte_rm_conv_tx(q->data_enc, SRSLTE_MIB_NB_ENC_LEN, q->rm_b, SRSLTE_NPBCH_NUM_BLOCKS * nof_bits); + } + + // Scramble and modulate a new block every 8 frames + if (frame_idx % SRSLTE_NPBCH_NUM_REP == 0) { + INFO("Modulating MIB-NB block %d in frame %d.\n", block_idx, frame_idx); + srslte_scrambling_b_offset(&q->seq, &q->rm_b[block_idx * nof_bits], block_idx * nof_bits, nof_bits); + srslte_mod_modulate(&q->mod, &q->rm_b[block_idx * nof_bits], q->d, nof_bits); + + // layer mapping & precoding + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d, x, q->cell.nof_ports, q->nof_symbols); + srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports, q->nof_symbols / q->cell.nof_ports, 1.0); + } else { + memcpy(q->symbols[0], q->d, q->nof_symbols * sizeof(cf_t)); + } + } + + // Write exactly SRSLTE_NPBCH_NUM_RE (assumes symbols have been modulated before) + for (int i = 0; i < q->cell.nof_ports; i++) { + if (q->cell.is_r14) { + DEBUG("Applying phase rotattion on port %d in frame %d.\n", i, frame_idx); + srslte_npbch_rotate(q, frame_idx, q->symbols[i], q->symbols[i], q->nof_symbols, false); + } + DEBUG("Putting MIB-NB block %d on port %d in frame %d.\n", block_idx, i, frame_idx); + if (srslte_npbch_cp(q->symbols[i], sf[i], q->cell, true) != SRSLTE_NPBCH_NUM_RE) { + INFO("Error while mapping NPBCH symbols.\n"); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_npbch_rotate(srslte_npbch_t* q, + uint32_t nf, + cf_t* input_signal, + cf_t* output_signal, + int num_samples, + bool back) +{ + // Generate frame specific scrambling sequence for symbol rotation + DEBUG("%sotating NPBCH in SFN=%d\n", back ? "De-R" : "R", nf); + + srslte_sequence_t seq; + if (srslte_sequence_npbch_r14(&seq, q->cell.n_id_ncell, nf)) { + return SRSLTE_ERROR; + } + + for (int i = 0; i < num_samples; i++) { + int c_2i = seq.c[2 * i]; + int c_2ip1 = seq.c[2 * i + 1]; + +#if 1 + cf_t phi_f = 0; + if (c_2i == 0 && c_2ip1 == 0) + phi_f = 1; + else if (c_2i == 0 && c_2ip1 == 1) + phi_f = -1; + else if (c_2i == 1 && c_2ip1 == 0) + phi_f = _Complex_I; + else if (c_2i == 1 && c_2ip1 == 1) + phi_f = -_Complex_I; +#else + cf_t phi_f = (c_2i == 0) ? 1 : _Complex_I; + if (c_2ip1 == 0) + phi_f *= -1; +#endif + + output_signal[i] = back ? input_signal[i] / phi_f : input_signal[i] * phi_f; + + if (SRSLTE_VERBOSE_ISDEBUG()) { + printf("i=%d c_2i=%d, c_2i+1=%d -> phi_f=", i, c_2i, c_2ip1); + srslte_vec_fprint_c(stdout, &phi_f, 1); + printf("input:\n"); + srslte_vec_fprint_c(stdout, &input_signal[i], 1); + printf("output:\n"); + srslte_vec_fprint_c(stdout, &output_signal[i], 1); + printf("\n\n"); + } + } + + srslte_sequence_free(&seq); + return SRSLTE_SUCCESS; +} + +/* Decodes the NPBCH channel + * + * The NPBCH spans over 640 ms. This function is called every 10 ms. It tries to decode the MIB + * given the symbols of a subframe (1 ms). Successive calls will use more subframes + * to help the decoding process. + * + * Returns 1 if successfully decoded MIB-NB, 0 if not and -1 on error + */ +int srslte_npbch_decode_nf(srslte_npbch_t* q, + cf_t* sf_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + uint32_t* nof_tx_ports, + int* sfn_offset, + int nf) +{ + uint32_t nant; + int i; + int nof_bits; + cf_t* x[SRSLTE_MAX_LAYERS]; + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && sf_symbols != NULL && q->cell.nof_ports != 0) { + for (i = 0; i < q->cell.nof_ports; i++) { + if (ce[i] == NULL) { + return ret; + } + } + + ret = SRSLTE_ERROR; + + // Set pointers for layermapping & precoding + nof_bits = 2 * q->nof_symbols; + + // number of layers equals number of ports + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + x[i] = q->x[i]; + } + memset(&x[SRSLTE_MAX_PORTS], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - SRSLTE_MAX_PORTS)); + + // extract symbols + int nof_ext_syms = srslte_npbch_cp(sf_symbols, q->symbols[0], q->cell, false); + if (q->nof_symbols != nof_ext_syms) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return ret; + } + + if (q->cell.is_r14) { + // de-rotate symbols + srslte_npbch_rotate(q, nf, q->symbols[0], q->symbols[0], q->nof_symbols, true); + } + + // extract channel estimates + for (i = 0; i < q->cell.nof_ports; i++) { + if (q->nof_symbols != srslte_npbch_cp(ce[i], q->ce[i], q->cell, false)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return ret; + } + } + + q->frame_idx++; + ret = 0; + + // Try decoding for 1 to cell.nof_ports antennas + if (q->search_all_ports) { + nant = 1; + } else { + nant = q->cell.nof_ports; + } + do { + if (nant != 3) { + DEBUG("Trying %d TX antennas with %d frames\n", nant, q->frame_idx); + + // in control channels, only diversity is supported + if (nant == 1) { + // no need for layer demapping + srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, NULL, q->nof_symbols, 1.0, noise_estimate); + } else { + srslte_predecoding_diversity(q->symbols[0], q->ce, x, nant, q->nof_symbols, 1.0); + srslte_layerdemap_diversity(x, q->d, nant, q->nof_symbols / nant); + } + + // demodulate symbols + srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, &q->llr[nof_bits * (q->frame_idx - 1)], q->nof_symbols); + + // only one subframe + DEBUG("Trying to decode NPBCH ..\n"); + + // FIXME: simplified decoding only using first MIB block + ret = srslte_npbch_decode_frame(q, 0, nf, 1, nof_bits, nant); + if (ret == SRSLTE_SUCCESS) { + if (sfn_offset) { + *sfn_offset = (int)0; + } + if (nof_tx_ports) { + *nof_tx_ports = nant; + } + if (bch_payload) { + memcpy(bch_payload, q->data, sizeof(uint8_t) * SRSLTE_MIB_NB_LEN); + } + INFO("Successfully decoded NPBCH sfn_offset=%d\n", q->frame_idx - 1); + q->frame_idx = 0; + return ret; + } + } + nant++; + } while (nant <= q->cell.nof_ports); + + q->frame_idx = 0; + } + return ret; +} + +int srslte_npbch_decode(srslte_npbch_t* q, + cf_t* sf_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint8_t bch_payload[SRSLTE_MIB_NB_LEN], + uint32_t* nof_tx_ports, + int* sfn_offset) +{ + return srslte_npbch_decode_nf(q, sf_symbols, ce, noise_estimate, bch_payload, nof_tx_ports, sfn_offset, 0); +} + +void srslte_npbch_decode_reset(srslte_npbch_t* q) +{ + q->frame_idx = 0; +} + +int srslte_npbch_decode_frame(srslte_npbch_t* q, + uint32_t src, + uint32_t dst, + uint32_t n, + uint32_t nof_bits, + uint32_t nof_ports) +{ + memcpy(&q->temp[dst * nof_bits], &q->llr[src * nof_bits], n * nof_bits * sizeof(float)); + + // descramble + srslte_scrambling_f_offset(&q->seq, &q->temp[dst * nof_bits], dst * nof_bits, n * nof_bits); + + for (int j = 0; j < dst * nof_bits; j++) { + q->temp[j] = SRSLTE_RX_NULL; + } + for (int j = (dst + n) * nof_bits; j < SRSLTE_NPBCH_NUM_BLOCKS * nof_bits; j++) { + q->temp[j] = SRSLTE_RX_NULL; + } + + // unrate matching + srslte_rm_conv_rx(q->temp, SRSLTE_NPBCH_NUM_BLOCKS * nof_bits, q->rm_f, SRSLTE_MIB_NB_ENC_LEN); + + // Normalize LLR + srslte_vec_sc_prod_fff(q->rm_f, 1.0 / ((float)2 * n), q->rm_f, SRSLTE_MIB_NB_ENC_LEN); + + // decode + srslte_viterbi_decode_f(&q->decoder, q->rm_f, q->data, SRSLTE_MIB_NB_CRC_LEN); + + if (srslte_npbch_crc_check(q, q->data, nof_ports) == 0) { + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +void srslte_npbch_crc_set_mask(uint8_t* data, int nof_ports) +{ + int i; + for (i = 0; i < 16; i++) { + data[SRSLTE_MIB_NB_LEN + i] = (data[SRSLTE_MIB_NB_LEN + i] + srslte_npbch_crc_mask[nof_ports - 1][i]) % 2; + } +} + +/* Checks CRC after applying the mask for the given number of ports. + * + * The bits buffer size must be at least 50 bytes. + * + * Returns 0 if the data is correct, -1 otherwise + */ +uint32_t srslte_npbch_crc_check(srslte_npbch_t* q, uint8_t* bits, uint32_t nof_ports) +{ + uint8_t data[SRSLTE_MIB_NB_CRC_LEN]; + memcpy(data, bits, SRSLTE_MIB_NB_CRC_LEN * sizeof(uint8_t)); + srslte_npbch_crc_set_mask(data, nof_ports); + int ret = srslte_crc_checksum(&q->crc, data, SRSLTE_MIB_NB_CRC_LEN); + if (ret == 0) { + uint32_t chkzeros = 0; + for (int i = 0; i < SRSLTE_MIB_NB_LEN; i++) { + chkzeros += data[i]; + } + if (chkzeros) { + return 0; + } else { + return SRSLTE_ERROR; + } + } else { + return ret; + } +} + +/* NPBCH starts at the fourth symbol of the sub-frame, + * We need to assume two antenna ports for NRS and + * four ports for CRS + * Returns the number of symbols written/read + */ +int srslte_npbch_cp(cf_t* input, cf_t* output, srslte_nbiot_cell_t cell, bool put) +{ + cf_t * in_ptr = input, *out_ptr = output; + uint32_t offset = 0; // the number of REs left out before start of the REF signal RE + uint32_t delta = 3 * cell.base.nof_prb * SRSLTE_NRE + cell.nbiot_prb * SRSLTE_NRE; + + if (put) { + out_ptr += delta; + } else { + in_ptr += delta; + } + + for (uint32_t l = 3; l < SRSLTE_CP_NORM_SF_NSYMB; l++) { + delta = 0; + if (l == 3 || l == 9 || l == 10) { + // copy entire symbol + prb_cp(&in_ptr, &out_ptr, 1); + } else { + // skip LTE CRS and NRS and always assume 4 reference symbols per OFDM symbol + offset = cell.n_id_ncell % 3; + prb_cp_ref( + &in_ptr, &out_ptr, offset, SRSLTE_NBIOT_NPBCH_NUM_REFS_PER_SYMB, SRSLTE_NBIOT_NPBCH_NUM_REFS_PER_SYMB, put); + delta = (cell.n_id_ncell % 3 == 2 ? 1 : 0); + } + + if (put) { + out_ptr += delta; + } else { + in_ptr += delta; + } + } + + int r; + if (put) { + r = abs((int)(input - in_ptr)); + } else { + r = abs((int)(output - out_ptr)); + } + + return r; +} diff --git a/lib/src/phy/phch/ra_nbiot.c b/lib/src/phy/phch/ra_nbiot.c new file mode 100644 index 000000000..caab20820 --- /dev/null +++ b/lib/src/phy/phch/ra_nbiot.c @@ -0,0 +1,548 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srslte/phy/phch/ra_nbiot.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include "tbs_tables_nbiot.h" +#include +#include +#include +#include +#include + +#define MAX_I_TBS_VAL 12 +#define MAX_I_TBS_VAL_SIB 11 +#define MAX_I_SF_VAL 7 +#define EUTRA_CONTROL_REGION_SIZE 3 // FIXME: Needs to be set by SIB1 + +#define min(a, b) (a < b ? a : b) + +/// Number of repetitions according to Table 16.4.1.3-2 in TS 36.213 13.2.0 +const int n_rep_table[16] = {1, 2, 4, 8, 16, 32, 64, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048}; + +/// Number of repetitions for NPDSCH carrying SystemInformationBlockType1-NB according to Table 16.4.1.3-3 in +/// TS 36.213 13.2.0 +const int n_rep_table_sib1[16] = {4, 8, 16, 4, 8, 16, 4, 8, 16, 4, 8, 16, 0, 0, 0, 0}; + +/// Number of repetitions for NPUSCH according to Table 16.5.1.1-3 in TS 36.213 13.2.0 +const int n_rep_table_npusch[8] = {1, 2, 4, 8, 16, 32, 64, 128}; + +/// Number of resource units (RU) for NPUSCH according to Table 16.5.1.1-2 in TS 36.213 13.2.0 +const int n_ru_table_npusch[8] = {1, 2, 3, 4, 5, 6, 8, 10}; + +/// k0 value for DCI format N1 for R_max smaller than 128 according to Table 16.4.1-1 in TS 36.213 13.2.0 +const int k0_table_formatN1_r_st_128[8] = {0, 4, 8, 12, 16, 32, 64, 128}; + +/// k0 value for DCI format N1 for R_max greater or equal than 128 according to Table 16.4.1-1 in TS 36.213 13.2.0 +const int k0_table_formatN1_r_geq_128[8] = {0, 16, 32, 64, 128, 256, 512, 1024}; + +/// k0 value for DCI format N0 according to Table 16.5.1-1 in TS 36.213 13.2.0 +const int k0_table_formatN0[4] = {8, 16, 32, 64}; + +/// k0 value contained in RAR grant according to Table 16.5.1-1 in TS 36.213 13.2.0, but with changes mentioned +/// in 16.3.3 +const int k0_table_rar_grant[4] = {12, 16, 32, 64}; + +/// Starting radio frame for the first transmission of the NPDSCH carrying SystemInformationBlockType1-NB according to +/// Table 16.4.1.3-4 in TS 36.213 13.2.0 +const int first_frame_sib1[3][4] = {{0, 16, 32, 48}, {0, 16, -1, -1}, {0, 1, -1, -1}}; + +/// Modulation and TBS index table for NPUSCH with N_sc_RU == 1 according to Table 16.5.1.2-1 in TS 36.213 13.2.0 +const int i_mcs_to_i_tbs_npusch[11] = {0, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10}; + +/// ACK/NACH resource computation TS 36.213 Section 16.4.2, for 15kHz and 3.75kHz +const int ack_nack_resource_field_to_sc[SRSLTE_NPUSCH_SC_SPACING_NITEMS][16] = { + {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, + {38, 39, 40, 41, 42, 43, 44, 45, 38, 39, 40, 41, 42, 43, 44, 45}}; + +const int ack_nack_resource_field_to_k0[SRSLTE_NPUSCH_SC_SPACING_NITEMS][16] = { + {13, 13, 13, 13, 15, 15, 15, 15, 17, 17, 17, 17, 18, 18, 18, 18}, + {13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, 21, 21}}; + +/// Calculate the number of resource elements per subframe that carry data +uint32_t srslte_ra_nbiot_dl_grant_nof_re(srslte_nbiot_cell_t cell, uint32_t l_start) +{ + /// start with one full PRB + uint32_t re = SRSLTE_CP_NORM_SF_NSYMB * SRSLTE_NRE; + + /// remove lstart number of symbols + re -= l_start * SRSLTE_NRE; + + /// remove NRS + switch (cell.nof_ports) { + case 1: + case 2: + re -= 8 * cell.nof_ports; + break; + case 4: + printf("ERROR: 4 ports are not supported for NB-IoT\n"); + break; + } + + /// remove CRS for inband deployments + if (cell.mode <= SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI) { + switch (cell.base.nof_ports) { + case 1: + case 2: + re -= 8 * cell.base.nof_ports; + // first two symbols used for CRS + if (l_start >= 2) + re += (4 * cell.base.nof_ports) / 2; + break; + case 4: + re -= 8 * 3; + break; + } + } + return re; +} + +void srslte_ra_nbiot_dl_grant_to_nbits(srslte_ra_nbiot_dl_grant_t* grant, + srslte_nbiot_cell_t cell, + uint32_t sf_idx, + srslte_ra_nbits_t* nbits) +{ + /// Compute number of RE + nbits->lstart = grant->l_start; + nbits->nof_re = srslte_ra_nbiot_dl_grant_nof_re(cell, grant->l_start); + nbits->nof_symb = 2 * SRSLTE_CP_NSYMB(cell.base.cp) - nbits->lstart; + nbits->nof_bits = nbits->nof_re * grant->Qm; +} + +/// Transport block size determination 16.4.1.5 in 36.213 v13.2.0 +static int nbiot_dl_dci_to_grant_mcs(srslte_ra_nbiot_dl_dci_t* dci, srslte_ra_nbiot_dl_grant_t* grant) +{ + int tbs = -1; + uint32_t i_tbs = 0, i_sf = 0; + + grant->mcs[0].mod = SRSLTE_MOD_QPSK; + + // limit config values in DCI + dci->alloc.sched_info_sib1 = min(dci->alloc.sched_info_sib1, MAX_I_TBS_VAL_SIB); + dci->mcs_idx = min(dci->mcs_idx, MAX_I_TBS_VAL); + dci->alloc.i_sf = min(dci->alloc.i_sf, MAX_I_SF_VAL); + + if (dci->alloc.has_sib1) { + i_tbs = dci->alloc.sched_info_sib1; + tbs = tbs_table_nbiot_sib1[i_tbs]; + } else { + i_tbs = dci->mcs_idx; + i_sf = dci->alloc.i_sf; + tbs = tbs_table_nbiot[i_tbs][i_sf]; + } + + if (tbs <= 0) { + INFO("Unsupported resource allocation specified: i_tbs=%d [0,12], i_sf=%d [0,7]\n", i_tbs, i_sf); + return SRSLTE_ERROR; + } else { + grant->mcs[0].tbs = (uint32_t)tbs; + return SRSLTE_SUCCESS; + } +} + +int srslte_ra_n_rep_from_dci(srslte_ra_nbiot_dl_dci_t* dci) +{ + return (dci->alloc.has_sib1 ? n_rep_table_sib1[dci->alloc.sched_info_sib1] : n_rep_table[dci->alloc.i_rep]); +} + +int srslte_ra_n_rep_sib1_nb(srslte_mib_nb_t* mib) +{ + return n_rep_table_sib1[mib->sched_info_sib1]; +} + +int srslte_ra_nbiot_get_sib1_tbs(srslte_mib_nb_t* mib) +{ + uint32_t i_tbs = min(mib->sched_info_sib1, MAX_I_TBS_VAL_SIB); + return tbs_table_nbiot_sib1[i_tbs]; +} + +int srslte_ra_nbiot_get_npdsch_tbs(uint32_t i_tbs, uint32_t i_sf) +{ + if (i_tbs <= 12 && i_sf <= 7) { + return tbs_table_nbiot[i_tbs][i_sf]; + } else { + return 0; + } +} + +int srslte_ra_nbiot_get_npusch_tbs(uint32_t i_tbs, uint32_t i_ru) +{ + if (i_tbs <= 12 && i_ru <= 7) { + return tbs_table_npusch[i_tbs][i_ru]; + } else { + return 0; + } +} + +int srslte_ra_n_rep_sib1_nb_idx(srslte_mib_nb_t* mib) +{ + switch (srslte_ra_n_rep_sib1_nb(mib)) { + case 4: + return 0; + case 8: + return 1; + case 16: + return 2; + default: + return SRSLTE_ERROR; + } +} + +int srslte_ra_nbiot_get_starting_sib1_frame(uint32_t cell_id, srslte_mib_nb_t* mib) +{ + return first_frame_sib1[srslte_ra_n_rep_sib1_nb_idx(mib)][cell_id % (srslte_ra_n_rep_sib1_nb(mib) == 4 ? 4 : 2)]; +} + +int srslte_ra_nbiot_sib1_start(uint32_t n_id_ncell, srslte_mib_nb_t* mib) +{ + return ((srslte_ra_n_rep_sib1_nb(mib) == 16 && n_id_ncell % 2 == 1) ? 1 : 0); +} + +/// Section 16.4.1 in 36.213 v13.3.0 +int srslte_ra_k0_from_dci(srslte_ra_nbiot_dl_dci_t* dci, uint32_t r_max) +{ + if (dci->dci_is_n2) { + return 0; + } + + if (r_max < 128) { + return k0_table_formatN1_r_st_128[dci->alloc.i_delay]; + } else { + return k0_table_formatN1_r_geq_128[dci->alloc.i_delay]; + } +} + +int srslte_ra_n_sf_from_dci(srslte_ra_nbiot_dl_dci_t* dci) +{ + if (dci->alloc.i_sf < 6) { + return dci->alloc.i_sf + 1; + } else if (dci->alloc.i_sf == 6) { + return 8; + } else if (dci->alloc.i_sf == 7) { + return 10; + } + // Invalid DCI config, default to 1 subframe in this case + return 1; +} + +/// According to TS 36.211 Sec 10.2.6 before obtaining operationModeInfo, +/// only sf_idx 0, 4 and 9 if no NSSS is sent carry NRS +bool srslte_ra_nbiot_dl_has_ref_signal(uint32_t tti) +{ + return (tti % 10 == 0 || tti % 10 == 4 || (tti % 10 == 9 && (tti / 10 % 2 != 0))); +} + +/// According to TS 36.211 Sec 10.2.6 in opMode standalone and guardband, +/// only sf_idx 0, 1, 3, 4, and 9 if no NSSS is sent carry NRS +bool srslte_ra_nbiot_dl_has_ref_signal_standalone(uint32_t tti) +{ + return (tti % 10 == 0 || tti % 10 == 1 || tti % 10 == 3 || tti % 10 == 4 || (tti % 10 == 9 && (tti / 10 % 2 != 0))); +} + +/// According to TS 36.211 Sec 10.2.6 before optaining operationModeInfo +/// Only sf_idx 0, 4 and 9 if no NSSS is sent carry NRS +bool srslte_ra_nbiot_dl_has_ref_signal_inband(uint32_t tti) +{ + return srslte_ra_nbiot_dl_has_ref_signal(tti); +} + +bool srslte_ra_nbiot_is_valid_dl_sf(uint32_t tti) +{ + return (tti % 10 == 0 || tti % 10 == 5 || (tti % 10 == 9 && (tti / 10 % 2 == 0))); +} + +int srslte_ra_nbiot_dl_dci_to_grant(srslte_ra_nbiot_dl_dci_t* dci, + uint16_t msg_rnti, + srslte_ra_nbiot_dl_grant_t* grant, + uint32_t sfn, + uint32_t sf_idx, + uint32_t r_max, + bool is_prescheduled, + srslte_nbiot_mode_t mode) +{ + if (!nbiot_dl_dci_to_grant_mcs(dci, grant)) { + /// Fill rest of grant structure + grant->mcs[0].mcs_idx = dci->mcs_idx; + grant->Qm = srslte_mod_bits_x_symbol(grant->mcs[0].mod); + grant->k0 = srslte_ra_k0_from_dci(dci, r_max); + grant->nof_sf = dci->alloc.has_sib1 ? 8 : srslte_ra_n_sf_from_dci(dci); + grant->nof_rep = srslte_ra_n_rep_from_dci(dci); + grant->has_sib1 = dci->alloc.has_sib1; + grant->ack_nack_resource = dci->alloc.harq_ack; + + if (mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI || mode == SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI) { + grant->l_start = dci->alloc.has_sib1 ? 3 : EUTRA_CONTROL_REGION_SIZE; + } else { + grant->l_start = 0; + } + + /// compute grant duration + int offset = (is_prescheduled == true) ? 0 : 5; + uint32_t rx_tti = sfn * 10 + sf_idx; + uint32_t tx_tti = (rx_tti + offset + grant->k0) % 10240; + + /// make sure tx_tti is a valid DL sf + while (!srslte_ra_nbiot_is_valid_dl_sf(tx_tti)) { + tx_tti = (tx_tti + 1) % 10240; + } + grant->start_hfn = 0; // not handling HFN + grant->start_sfn = tx_tti / 10; + grant->start_sfidx = tx_tti % 10; + } else { + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +void srslte_nbiot_dl_dci_fprint(FILE* f, srslte_ra_nbiot_dl_dci_t* dci) +{ + fprintf(f, "NB-IoT DL DCI:\n"); + fprintf(f, " - Format flag:\t\t\t\t%d\n", dci->format); + fprintf(f, " + FormatN%d DCI:\t\t\t%s\n", dci->format ? 1 : 0, dci->format ? "Downlink" : "Uplink"); + fprintf(f, " - PDCCH Order:\t\t\t\t%d\n", dci->alloc.is_ra); + fprintf(f, " - Scheduling delay:\t\t\t%d (%d subframes)\n", dci->alloc.i_delay, srslte_ra_k0_from_dci(dci, 64)); + fprintf(f, " - Resource assignment:\t\t\t%d\n", dci->alloc.i_sf); + fprintf(f, " + Number of subframes:\t\t%d\n", srslte_ra_n_sf_from_dci(dci)); + fprintf(f, " - Modulation and coding scheme index:\t%d\n", dci->mcs_idx); + fprintf(f, " - Repetition number:\t\t\t%d\n", dci->alloc.i_rep); + fprintf(f, " + Number of repetitions:\t\t%d\n", srslte_ra_n_rep_from_dci(dci)); + fprintf(f, " - New data indicator:\t\t\t%d\n", dci->ndi); + fprintf(f, " - HARQ-ACK resource:\t\t\t%d\n", dci->alloc.harq_ack); + fprintf(f, " - DCI subframe repetition number:\t%d\n", dci->alloc.dci_sf_rep_num); +} + +void srslte_ra_npusch_fprint(FILE* f, srslte_ra_nbiot_ul_dci_t* dci) +{ + fprintf(f, "NB-IoT UL DCI:\n"); + fprintf(f, " - Format flag:\t\t\t\t%d\n", dci->format); + fprintf(f, " - Subcarrier indication field:\t\t%d\n", dci->i_sc); + fprintf(f, " - Scheduling delay:\t\t\t%d\n", dci->i_delay); + fprintf(f, " - Resource assignment:\t\t\t%d\n", dci->i_ru); + fprintf(f, " - Modulation and coding scheme index:\t%d\n", dci->i_mcs); + fprintf(f, " - Redundency version:\t\t\t%d\n", dci->i_rv); + fprintf(f, " - Repetition number:\t\t\t%d\n", dci->i_rep); + fprintf(f, " - New data indicator:\t\t\t%d\n", dci->ndi); + fprintf(f, " - DCI subframe repetition number:\t%d\n", dci->dci_sf_rep_num); +} + +void srslte_ra_nbiot_dl_grant_fprint(FILE* f, srslte_ra_nbiot_dl_grant_t* grant) +{ + fprintf(f, "DL grant config:\n"); + fprintf(f, " - Number of subframes:\t\t\t%d\n", grant->nof_sf); + fprintf(f, " - Number of repetitions:\t\t%d\n", grant->nof_rep); + fprintf(f, " - Total number of subframes:\t\t%d\n", grant->nof_sf * grant->nof_rep); + fprintf(f, " - Starting SFN:\t\t\t%d\n", grant->start_sfn); + fprintf(f, " - Starting SF index:\t\t\t%d\n", grant->start_sfidx); + fprintf(f, " - Modulation type:\t\t\t%s\n", srslte_mod_string(grant->mcs[0].mod)); + fprintf(f, " - Transport block size:\t\t%d\n", grant->mcs[0].tbs); +} + +void srslte_ra_nbiot_ul_grant_fprint(FILE* f, srslte_ra_nbiot_ul_grant_t* grant) +{ + fprintf(f, "UL grant config:\n"); + fprintf(f, " - NPUSCH format:\t\t%s\n", srslte_npusch_format_text[grant->format]); + fprintf(f, " - Delay:\t\t\t%d subframes\n", grant->k0); + fprintf(f, " - Tx TTI:\t\t\t%d\n", grant->tx_tti); + fprintf(f, " - Subcarriers:\t\t\t%d (%s)\n", grant->nof_sc, srslte_npusch_sc_spacing_text[grant->sc_spacing]); + fprintf(f, " - Number of slots:\t\t%d\n", grant->nof_slots); + fprintf(f, " - Number of resource units:\t%d\n", grant->nof_ru); + fprintf(f, " - Number of repetitions:\t%d\n", grant->nof_rep); + fprintf(f, " - Modulation type:\t\t%s\n", srslte_mod_string(grant->mcs.mod)); + fprintf(f, " - Transport block size:\t%d\n", grant->mcs.tbs); +} + +/// UL RA for Msg3, i.e. RAR grant +int srslte_ra_nbiot_ul_rar_dci_to_grant(srslte_ra_nbiot_ul_dci_t* dci, + srslte_ra_nbiot_ul_grant_t* grant, + uint32_t rx_tti) +{ + /// use DCI to fill default UL grant values + if (srslte_ra_nbiot_ul_dci_to_grant(dci, grant, rx_tti, SRSLTE_NPUSCH_SC_SPACING_15000)) { + fprintf(stderr, "Error while reading UL DCI for RAR grant.\n"); + return SRSLTE_ERROR; + } + + /// now update all RAR specific fields + grant->sc_spacing = dci->sc_spacing; + assert(dci->i_delay <= 3); + grant->k0 = k0_table_rar_grant[dci->i_delay]; + grant->tx_tti = (rx_tti + grant->k0 + 1) % 10240; + + /// set number of RU + switch (dci->i_mcs) { + case 0: + grant->nof_ru = 4; + break; + case 1: + grant->nof_ru = 3; + break; + case 2: + grant->nof_ru = 1; + break; + default: + fprintf(stderr, "Invalid i_mcs value in UL DCI for RAR grant.\n"); + return SRSLTE_ERROR; + } + + /// set modulation + grant->mcs.tbs = 88; + if (grant->sc_spacing == SRSLTE_NPUSCH_SC_SPACING_15000 && dci->i_sc > 11) { + grant->mcs.mod = SRSLTE_MOD_QPSK; + } else if (dci->i_sc <= 11) { + // FIXME: Use SRSLTE_MOD_PI2_BPSK and SRSLTE_MOD_PI4_QPSK + grant->mcs.mod = (dci->i_mcs == 0) ? SRSLTE_MOD_BPSK : SRSLTE_MOD_QPSK; + } + return 0; +} + +/// Fill a grant for NPUSCH without UL-SCH data but for UL control information +void srslte_ra_nbiot_ul_get_uci_grant(srslte_ra_nbiot_ul_grant_t* grant, + const uint8_t resource_field, + const uint32_t tti) +{ + bzero(grant, sizeof(srslte_ra_nbiot_ul_grant_t)); + grant->format = SRSLTE_NPUSCH_FORMAT2; + grant->sc_spacing = SRSLTE_NPUSCH_SC_SPACING_15000; + grant->sc_alloc_set[0] = ack_nack_resource_field_to_sc[grant->sc_spacing][resource_field]; + grant->nof_sc = 1; + grant->k0 = ack_nack_resource_field_to_k0[grant->sc_spacing][resource_field]; + grant->tx_tti = (tti + grant->k0) % 10240; + grant->mcs.mcs_idx = 1; + grant->mcs.mod = SRSLTE_MOD_BPSK; + grant->mcs.tbs = 16; + grant->Qm = 1; + grant->nof_ru = 1; + grant->nof_slots = 4; + grant->nof_rep = 0; // FIXME: set appropiatly + grant->rv_idx = 0; +} + +/// Transport block size determination 16.5.1.2 in 36.213 v13.2.0 +int srslte_ra_nbiot_ul_dci_to_grant(srslte_ra_nbiot_ul_dci_t* dci, + srslte_ra_nbiot_ul_grant_t* grant, + uint32_t rx_tti, + srslte_npusch_sc_spacing_t spacing) +{ + bzero(grant, sizeof(srslte_ra_nbiot_ul_grant_t)); + grant->format = SRSLTE_NPUSCH_FORMAT1; + grant->sc_spacing = SRSLTE_NPUSCH_SC_SPACING_15000; + + int tbs = -1; + uint32_t i_tbs = 0; + + /// calculate actual values + if (dci->i_sc <= 11) { + /// the value of i_sc + grant->sc_alloc_set[0] = dci->i_sc; + grant->nof_sc = 1; + } else { + printf("UL i_sc > 11 not implemented yet!\n"); + return SRSLTE_ERROR; + } + grant->nof_ru = n_ru_table_npusch[dci->i_ru]; + grant->nof_rep = n_rep_table_npusch[dci->i_rep]; + if (grant->nof_rep != 1) { + printf("NPUSCH repetitions are currently not supported!\n"); + return SRSLTE_ERROR; + } + + /// Compute number of slots according to Table Table 10.1.2.3-1 in 36.211 + switch (grant->format) { + case SRSLTE_NPUSCH_FORMAT1: + if (grant->nof_sc == 1) { + grant->nof_slots = 16; + } else if (grant->nof_sc == 3) { + grant->nof_slots = 8; + } else if (grant->nof_sc == 6) { + grant->nof_slots = 4; + } else if (grant->nof_sc == 12) { + grant->nof_slots = 2; + } else { + DEBUG("Unsupported value for N_sc_RU=%d\n", grant->nof_sc); + } + break; + case SRSLTE_NPUSCH_FORMAT2: + grant->nof_slots = 4; + break; + default: + fprintf(stderr, "Invalid NPUSCH format.\n"); + return SRSLTE_ERROR; + } + grant->nof_slots *= grant->nof_ru; + + /// set TBS and Qm (according to table Table 10.1.3.2-1) + if (grant->nof_sc == 1) { + assert(dci->i_mcs < 11); + grant->Qm = (dci->i_mcs <= 1) ? 1 : 2; + i_tbs = i_mcs_to_i_tbs_npusch[dci->i_mcs]; + } else if (grant->nof_sc > 1) { + assert(dci->i_mcs <= 12); + grant->Qm = 2; + i_tbs = dci->i_mcs; + } + tbs = tbs_table_npusch[i_tbs][dci->i_ru]; + + /// Redundency version according to TS 36 212 Section 6.3.2 + /// And TS 36 213 Sec. 16.5.1.2 + /// FIXME: implement NPUSCH repetitions + /// int L = (grant->nof_sc == 1) ? 1 : min(4, grant->nof_rep/2); + /// int B = L * grant->nof_ru * grant->nof_slots; + int j = 0; + grant->rv_idx = 2 * ((dci->i_rv + j) % 2); + + /// set Tx TTI + assert(dci->i_delay <= 3); + grant->k0 = k0_table_formatN0[dci->i_delay]; + grant->tx_tti = (rx_tti + grant->k0 + 1) % 10240; + + /// set fixed values + grant->sc_spacing = spacing; + grant->mcs.mcs_idx = dci->i_mcs; + grant->mcs.mod = (grant->Qm == 1) ? SRSLTE_MOD_BPSK : SRSLTE_MOD_QPSK; + + if (tbs < 0) { + return SRSLTE_ERROR; + } else { + grant->mcs.tbs = (uint32_t)tbs; + return SRSLTE_SUCCESS; + } +} + +void srslte_ra_nbiot_ul_grant_to_nbits(srslte_ra_nbiot_ul_grant_t* grant, srslte_ra_nbits_t* nbits) +{ + /// set DMRS symbols according to TS 36.211 v13.3 Table 10.1.4.2-1 + int num_dmrs_syms = (grant->format == SRSLTE_NPUSCH_FORMAT1) ? 1 : 3; + + /// computer number of RUs + nbits->nof_symb = 7; + nbits->nof_re = (nbits->nof_symb - num_dmrs_syms) * grant->nof_slots * + grant->nof_sc; // Here, a RE is a Resource Unit (RU) which is N_symb_UL*N_slots_UL*N_sc_RU, one symbol + // per slot is used for DMRS + nbits->nof_bits = nbits->nof_re * grant->Qm; +} + +float srslte_ra_nbiot_get_delta_f(srslte_npusch_sc_spacing_t spacing) +{ + return ((spacing == SRSLTE_NPUSCH_SC_SPACING_15000) ? 15000 : 3750); +} diff --git a/lib/src/phy/phch/tbs_tables_nbiot.h b/lib/src/phy/phch/tbs_tables_nbiot.h new file mode 100644 index 000000000..1ec3ec9eb --- /dev/null +++ b/lib/src/phy/phch/tbs_tables_nbiot.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_TBS_TABLES_NBIOT_H +#define SRSLTE_TBS_TABLES_NBIOT_H + +// Transport Block Size from 3GPP TS 36.213 v13.2.0 table 16.4.1.5.1-1 +const int tbs_table_nbiot[13][8] = {{16, 32, 56, 88, 120, 152, 208, 256}, + {24, 56, 88, 144, 176, 208, 256, 344}, + {32, 72, 144, 176, 208, 256, 328, 424}, + {40, 104, 176, 208, 256, 328, 440, 568}, + {56, 120, 208, 256, 328, 408, 552, 680}, + {72, 144, 224, 328, 424, 504, 680, 0}, + {88, 176, 256, 392, 504, 600, 0, 0}, + {104, 224, 328, 472, 584, 680, 0, 0}, + {120, 256, 392, 536, 680, 0, 0, 0}, + {136, 296, 456, 616, 0, 0, 0, 0}, + {144, 328, 504, 680, 0, 0, 0, 0}, + {176, 376, 584, 0, 0, 0, 0, 0}, + {208, 440, 680, 0, 0, 0, 0, 0}}; + +// Transport Block Size for NPDSCH carrying SystemInformationBlockType1-NB +// from 3GPP TS 36.213 v13.2.0 table 16.4.1.5.2-1 +const int tbs_table_nbiot_sib1[16] = {208, 208, 208, 328, 328, 328, 440, 440, 440, 680, 680, 680, 0, 0, 0, 0}; + +// Transport Block Size for NPUSCH +// from 3GPP TS 36.213 v13.2.0 table 16.5.1.2-2 +const int tbs_table_npusch[13][8] = {{16, 32, 56, 88, 120, 152, 208, 256}, + {24, 56, 88, 144, 176, 208, 256, 344}, + {32, 72, 144, 176, 208, 256, 328, 424}, + {40, 104, 176, 208, 256, 328, 440, 568}, + {56, 120, 208, 256, 328, 408, 552, 680}, + {72, 144, 224, 328, 424, 504, 680, 872}, + {88, 176, 256, 392, 504, 600, 808, 1000}, + {104, 224, 328, 472, 584, 712, 1000, 0}, + {120, 256, 392, 536, 680, 808, 0, 0}, + {136, 296, 456, 616, 776, 936, 0, 0}, + {144, 328, 504, 680, 872, 1000, 0, 0}, + {176, 376, 584, 776, 1000, 0, 0, 0}, + {208, 440, 680, 1000, 0, 0, 0, 0}}; + +#endif // SRSLTE_TBS_TABLES_NBIOT_H \ No newline at end of file diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index a2391c6d9..eefcf7563 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -32,6 +32,18 @@ add_test(pbch_test_50 pbch_test -p 1 -n 50 -c 50) add_test(pbch_test_502 pbch_test -p 2 -n 50 -c 50) add_test(pbch_test_504 pbch_test -p 4 -n 50 -c 50) +######################################################################## +# NPBCH TEST +######################################################################## + +add_executable(npbch_test npbch_test.c) +target_link_libraries(npbch_test srslte_phy) +add_test(npbch_test npbch_test) + +add_test(npbch_file_test_r13 npbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_amari_nid0_sfn514_sib2.bin) +add_test(npbch_file_test_r14 npbch_file_test -l 256 -R -r 0 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_nid256_r14_sf0.bin) +add_test(npbch_file_test_nid257_r13 npbch_file_test -l 257 -r 4 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_nid257_r13_sf0.bin) +add_test(npbch_file_test_nid257_r14 npbch_file_test -l 257 -R -r 7 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_nid257_r14_sf0.bin) ######################################################################## # PCFICH TEST @@ -199,7 +211,6 @@ add_test(pdsch_test_multiplex2cw_p1_100 pdsch_test -x 4 -a 2 -t 0 -p 1 -n 100) # PMCH TEST ######################################################################## - add_executable(pmch_test pmch_test.c) target_link_libraries(pmch_test srslte_phy) @@ -207,7 +218,6 @@ add_test(pmch_test_qpsk pmch_test -m 6 -n 50) add_test(pmch_test_qam16 pmch_test -m 15 -n 100) add_test(pmch_test_qam64 pmch_test -m 25 -n 100) - ######################################################################## # FILE TEST ######################################################################## @@ -227,6 +237,11 @@ target_link_libraries(pdcch_file_test srslte_phy) add_executable(pdsch_pdcch_file_test pdsch_pdcch_file_test.c) target_link_libraries(pdsch_pdcch_file_test srslte_phy) +add_executable(npbch_file_test npbch_file_test.c) +target_link_libraries(npbch_file_test srslte_phy) + +add_test(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.dat) + add_executable(pmch_file_test pmch_file_test.c) target_link_libraries(pmch_file_test srslte_phy) diff --git a/lib/src/phy/phch/test/npbch_file_test.c b/lib/src/phy/phch/test/npbch_file_test.c new file mode 100644 index 000000000..bbe081742 --- /dev/null +++ b/lib/src/phy/phch/test/npbch_file_test.c @@ -0,0 +1,285 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +char* input_file_name = NULL; + +srslte_nbiot_cell_t cell = {.base = {.nof_prb = 1, .nof_ports = 2, .cp = SRSLTE_CP_NORM, .id = 0}, + .nbiot_prb = 0, + .n_id_ncell = 0, + .nof_ports = 0, + .is_r14 = false}; + +int nof_frames = 128; // two MIB periods +bool do_chest = true; +int nf = 0; +int sf_idx = 0; + +#define SFLEN (1 * SRSLTE_SF_LEN(srslte_symbol_sz(cell.base.nof_prb))) + +srslte_filesource_t fsrc; +cf_t * input_buffer, *fft_buffer, *ce[SRSLTE_MAX_PORTS]; +srslte_npbch_t npbch; +srslte_ofdm_t fft; +srslte_chest_dl_nbiot_t chest; + +void usage(char* prog) +{ + printf("Usage: %s [vrslRtoe] -i input_file\n", prog); + printf("\t-l n_id_ncell [Default %d]\n", cell.n_id_ncell); + printf("\t-p nof_prb [Default %d]\n", cell.base.nof_prb); + printf("\t-t do channel estimation [Default %d]\n", do_chest); + printf("\t-s Initial value of sf_idx [Default %d]\n", sf_idx); + printf("\t-r NPBCH repetition number within block [Default %d]\n", nf); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-R Whether this is a R14 signal [Default %s]\n", cell.is_r14 ? "Yes" : "No"); + + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "ilvrstneR")) != -1) { + switch (opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'l': + cell.n_id_ncell = atoi(argv[optind]); + break; + case 'p': + cell.base.nof_prb = atoi(argv[optind]); + break; + case 't': + do_chest = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 's': + sf_idx = atoi(argv[optind]) % 10; + break; + case 'v': + srslte_verbose++; + break; + case 'r': + nf = atoi(argv[optind]); + break; + case 'R': + cell.is_r14 = true; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() +{ + srand(0); + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + input_buffer = malloc(SFLEN * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(SRSLTE_SF_LEN(srslte_symbol_sz(cell.base.nof_prb)) * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (int i = 0; i < cell.base.nof_ports; i++) { + ce[i] = malloc(SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp) * sizeof(cf_t)); + if (!ce[i]) { + perror("malloc"); + return -1; + } + for (int j = 0; j < SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp); j++) { + ce[i][j] = 1.0; + } + } + + if (srslte_chest_dl_nbiot_init(&chest, SRSLTE_NBIOT_MAX_PRB)) { + fprintf(stderr, "Error initializing equalizer\n"); + return -1; + } + if (srslte_chest_dl_nbiot_set_cell(&chest, cell) != SRSLTE_SUCCESS) { + fprintf(stderr, "Error setting equalizer cell configuration\n"); + return -1; + } + + if (srslte_ofdm_rx_init(&fft, cell.base.cp, input_buffer, fft_buffer, cell.base.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return -1; + } + srslte_ofdm_set_freq_shift(&fft, SRSLTE_NBIOT_FREQ_SHIFT_FACTOR); + + if (srslte_npbch_init(&npbch)) { + fprintf(stderr, "Error initiating NPBCH\n"); + return -1; + } + if (srslte_npbch_set_cell(&npbch, cell)) { + fprintf(stderr, "Error setting cell in NPBCH object\n"); + exit(-1); + } + + // setting ports to 2 to make test not fail + cell.nof_ports = 2; + if (!srslte_nbiot_cell_isvalid(&cell)) { + fprintf(stderr, "Invalid cell properties\n"); + return -1; + } + + DEBUG("Memory init OK\n"); + return 0; +} + +void base_free() +{ + srslte_filesource_free(&fsrc); + + free(input_buffer); + free(fft_buffer); + + srslte_filesource_free(&fsrc); + for (int i = 0; i < cell.base.nof_ports; i++) { + free(ce[i]); + } + srslte_chest_dl_nbiot_free(&chest); + srslte_ofdm_rx_free(&fft); + + srslte_npbch_free(&npbch); +} + +int main(int argc, char** argv) +{ + uint8_t bch_payload[SRSLTE_MIB_NB_LEN] = {}; + int ret = SRSLTE_ERROR; + uint32_t nof_tx_ports = 0; + int sfn_offset = 0; + + if (argc < 3) { + usage(argv[0]); + return ret; + } + + parse_args(argc, argv); + + printf("Subframe length: %d\n", SFLEN); + + if (base_init()) { + fprintf(stderr, "Error initializing receiver\n"); + return ret; + } + + int frame_cnt = 0; + int nof_decoded_mibs = 0; + int nread = 0; + + do { + nread = srslte_filesource_read(&fsrc, input_buffer, SFLEN); + if (nread == SFLEN) { + // do IFFT and channel estimation only on subframes that are known to contain NRS + if (sf_idx == 0 || sf_idx == 4) { + INFO("%d.%d: Estimating channel.\n", frame_cnt, sf_idx); + srslte_ofdm_rx_sf(&fft); + // srslte_ofdm_set_normalize(&fft, true); + + if (do_chest) { + srslte_chest_dl_nbiot_estimate(&chest, fft_buffer, ce, sf_idx); + } + } + + // but NPBCH processing only for 1st subframe + if (sf_idx == 0) { + float noise_est = (do_chest) ? srslte_chest_dl_nbiot_get_noise_estimate(&chest) : 0.0; + if (frame_cnt % 8 == 0) { + DEBUG("Reseting NPBCH decoder.\n"); + srslte_npbch_decode_reset(&npbch); + } + INFO("%d.0: Calling NPBCH decoder (noise_est=%.2f)\n", frame_cnt, noise_est); + ret = srslte_npbch_decode_nf(&npbch, fft_buffer, ce, noise_est, bch_payload, &nof_tx_ports, NULL, nf); + + if (ret == SRSLTE_SUCCESS) { + printf("MIB-NB decoded OK. Nof ports: %d. SFN offset: %d Payload: ", nof_tx_ports, sfn_offset); + srslte_vec_fprint_hex(stdout, bch_payload, SRSLTE_MIB_NB_LEN); + srslte_mib_nb_t mib_nb; + srslte_npbch_mib_unpack(bch_payload, &mib_nb); + srslte_mib_nb_printf(stdout, cell, &mib_nb); + nof_decoded_mibs++; + } + + if (SRSLTE_VERBOSE_ISDEBUG()) { + if (do_chest) { + DEBUG("SAVED FILE npbch_rx_chest_on.bin: NPBCH with chest\n"); + srslte_vec_save_file("npbch_rx_chest_on.bin", npbch.d, npbch.nof_symbols * sizeof(cf_t)); + } else { + DEBUG("SAVED FILE npbch_rx_chest_off.bin: NPBCH without chest\n"); + srslte_vec_save_file("npbch_rx_chest_off.bin", npbch.d, npbch.nof_symbols * sizeof(cf_t)); + } + } + } + sf_idx++; + if (sf_idx == 10) { + sf_idx = 0; + frame_cnt++; + } + + } else if (nread < 0) { + fprintf(stderr, "Error reading from file\n"); + return ret; + } + } while (nread > 0 && frame_cnt < nof_frames); + + base_free(); + + printf("nof_decoded_mibs=%d\n", nof_decoded_mibs); + + ret = (nof_decoded_mibs > 0) ? SRSLTE_SUCCESS : SRSLTE_ERROR; + + return ret; +} diff --git a/lib/src/phy/phch/test/npbch_test.c b/lib/src/phy/phch/test/npbch_test.c new file mode 100644 index 000000000..9a01a202a --- /dev/null +++ b/lib/src/phy/phch/test/npbch_test.c @@ -0,0 +1,242 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/npbch.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define HAVE_OFDM 0 +#define HAVE_MIB_NB 1 +srslte_nbiot_cell_t cell = {}; + +void usage(char* prog) +{ + printf("Usage: %s [cpv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.base.id); + printf("\t-p cell.nof_ports [Default %d]\n", cell.base.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.base.nof_prb); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "cpnv")) != -1) { + switch (opt) { + case 'p': + cell.base.nof_ports = atoi(argv[optind]); + break; + case 'n': + cell.base.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.base.id = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int re_extract_test() +{ + int ret = SRSLTE_ERROR; + + srslte_nbiot_cell_t cell2 = {}; + cell2.base.id = 257; + cell2.base.nof_ports = 1; + cell2.n_id_ncell = 257; + cell2.nof_ports = 1; + cell2.base.nof_prb = 1; + + if (!srslte_nbiot_cell_isvalid(&cell2)) { + printf("cell is not properly configured\n"); + return ret; + } + + cf_t sf_syms[168]; + for (int i = 0; i < 168; i++) { + sf_syms[i] = i + 1; + } + + cf_t npbch_syms[100]; + int nof_ext_syms = srslte_npbch_cp(sf_syms, npbch_syms, cell2, false); + + if (SRSLTE_VERBOSE_ISINFO()) { + for (int i = 0; i < nof_ext_syms; i++) { + printf("re%d:", i); + srslte_vec_fprint_c(stdout, &npbch_syms[i], 1); + } + } + + if (nof_ext_syms == 100) { + ret = SRSLTE_SUCCESS; + printf("RE extraction ok!\n"); + } + return ret; +} + +int main(int argc, char** argv) +{ + int ret = SRSLTE_ERROR; + srslte_npbch_t npbch; + uint8_t bch_payload_tx[SRSLTE_MIB_NB_LEN], bch_payload_rx[SRSLTE_MIB_NB_LEN]; + cf_t* ce[SRSLTE_MAX_PORTS]; + int nof_re; + cf_t* sf_symbols[SRSLTE_MAX_PORTS]; + uint32_t nof_rx_ports; + + parse_args(argc, argv); + + if (re_extract_test() != SRSLTE_SUCCESS) { + return ret; + } + + // set essential cell params + cell.base.nof_prb = 1; + cell.nof_ports = 1; + + nof_re = SRSLTE_SF_LEN_RE(cell.base.nof_prb, SRSLTE_CP_NORM); + + /* init memory */ + for (int i = 0; i < cell.nof_ports; i++) { + ce[i] = malloc(sizeof(cf_t) * nof_re); + if (!ce[i]) { + perror("malloc"); + exit(-1); + } + for (int j = 0; j < nof_re; j++) { + ce[i][j] = 1; + } + sf_symbols[i] = malloc(sizeof(cf_t) * nof_re); + if (!sf_symbols[i]) { + perror("malloc"); + exit(-1); + } + memset(sf_symbols[i], 0, sizeof(cf_t) * nof_re); + } + + if (srslte_npbch_init(&npbch)) { + fprintf(stderr, "Error creating NPBCH object\n"); + exit(-1); + } + if (srslte_npbch_set_cell(&npbch, cell)) { + fprintf(stderr, "Error setting cell in NPBCH object\n"); + exit(-1); + } + +#if HAVE_MIB_NB + srslte_mib_nb_t mib_nb = {}; + mib_nb.sched_info_sib1 = 3; // according to Table 16.4.1.3-3 in 36.213 that means 4 NPDSCH repetitions + mib_nb.sys_info_tag = 1; + mib_nb.ac_barring = false; + mib_nb.mode = SRSLTE_NBIOT_MODE_STANDALONE; + uint32_t hfn = 82; + uint32_t sfn = 0; + srslte_npbch_mib_pack(hfn, sfn, mib_nb, bch_payload_tx); +#else + // srand(0); + srand(time(NULL)); + for (i = 0; i < SRSLTE_MIB_NB_LEN; i++) { + bch_payload_tx[i] = rand() % 2; + } +#endif + + if (srslte_npbch_put_subframe(&npbch, bch_payload_tx, sf_symbols, 0)) { + fprintf(stderr, "Error encoding NPBCH\n"); + exit(-1); + } +#if 0 + /* combine outputs */ + for (i=1;i