mirror of https://github.com/pvnis/srsRAN_4G.git
adding NB-IoT DL channel estamiation and NPBCH code
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,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);
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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
|
@ -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…
Reference in New Issue