adding NB-IoT DL channel estamiation and NPBCH code

master
Andre Puschmann 5 years ago
parent 2401a2982b
commit e05ecdb139

@ -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 <stdio.h>
#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

@ -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

@ -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

@ -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 <stdbool.h>
#include <stdint.h>
#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

@ -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 <assert.h>
#include <complex.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#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);
}

@ -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) 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) { inline uint32_t srslte_refsignal_cs_fidx(srslte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) {

@ -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 <complex.h>
#include <math.h>
#include <srslte/phy/ch_estimation/refsignal_dl.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#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;
}
}

@ -47,3 +47,13 @@ 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_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 1 -r 50)
add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) 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)

@ -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 <complex.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#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;
}

@ -46,7 +46,7 @@ bool srslte_cellid_isvalid(uint32_t cell_id) {
} }
bool srslte_nofprb_isvalid(uint32_t nof_prb) { 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; return true;
} else { } else {
return false; return false;

@ -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 <assert.h>
#include <complex.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#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;
}

@ -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 <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#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);
}

@ -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

@ -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_502 pbch_test -p 2 -n 50 -c 50)
add_test(pbch_test_504 pbch_test -p 4 -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 # 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 # PMCH TEST
######################################################################## ########################################################################
add_executable(pmch_test pmch_test.c) add_executable(pmch_test pmch_test.c)
target_link_libraries(pmch_test srslte_phy) 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_qam16 pmch_test -m 15 -n 100)
add_test(pmch_test_qam64 pmch_test -m 25 -n 100) add_test(pmch_test_qam64 pmch_test -m 25 -n 100)
######################################################################## ########################################################################
# FILE TEST # 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) add_executable(pdsch_pdcch_file_test pdsch_pdcch_file_test.c)
target_link_libraries(pdsch_pdcch_file_test srslte_phy) 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) add_executable(pmch_file_test pmch_file_test.c)
target_link_libraries(pmch_file_test srslte_phy) target_link_libraries(pmch_file_test srslte_phy)

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#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;
}

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#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<cell.base.nof_ports;i++) {
for (j=0;j<nof_re;j++) {
sf_symbols[0][j] += sf_symbols[i][j];
}
}
#endif
#if HAVE_OFDM
cf_t td_signal[nof_re * 2];
srslte_ofdm_t ofdm_tx;
srslte_ofdm_t ofdm_rx;
if (srslte_ofdm_tx_init(&ofdm_tx, SRSLTE_CP_NORM, cell.base.nof_prb)) {
fprintf(stderr, "Error creating iFFT object\n");
exit(-1);
}
// srslte_ofdm_set_normalize(&ofdm_tx, true);
if (srslte_ofdm_rx_init(&ofdm_rx, SRSLTE_CP_NORM, cell.base.nof_prb)) {
fprintf(stderr, "Error initializing FFT\n");
return -1;
}
// transfer into time doamin and back
srslte_ofdm_tx_sf(&ofdm_tx, sf_symbols[0], td_signal);
srslte_ofdm_rx_sf(&ofdm_rx, td_signal, sf_symbols[0]);
srslte_ofdm_tx_free(&ofdm_tx);
srslte_ofdm_rx_free(&ofdm_rx);
#endif
srslte_npbch_decode_reset(&npbch);
if (srslte_npbch_decode(&npbch, sf_symbols[0], ce, 0, bch_payload_rx, &nof_rx_ports, NULL)) {
printf("Error decoding\n");
exit(-1);
}
srslte_npbch_free(&npbch);
for (int i = 0; i < cell.nof_ports; i++) {
free(ce[i]);
free(sf_symbols[i]);
}
printf("Tx ports: %d - Rx ports: %d\n", cell.nof_ports, nof_rx_ports);
printf("Tx payload: ");
srslte_vec_fprint_hex(stdout, bch_payload_tx, SRSLTE_MIB_NB_LEN);
printf("Rx payload: ");
srslte_vec_fprint_hex(stdout, bch_payload_rx, SRSLTE_MIB_NB_LEN);
#if HAVE_MIB_NB
srslte_mib_nb_t mib_nb_rx;
srslte_npbch_mib_unpack(bch_payload_rx, &mib_nb_rx);
srslte_mib_nb_printf(stdout, cell, &mib_nb_rx);
#endif
if (nof_rx_ports == cell.nof_ports && !memcmp(bch_payload_rx, bch_payload_tx, sizeof(uint8_t) * SRSLTE_MIB_NB_LEN)) {
printf("OK\n");
exit(0);
} else {
printf("Error\n");
exit(-1);
}
}
Loading…
Cancel
Save