diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 7bb07cdfc..c825aa990 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -45,7 +45,9 @@ #define SRSLTE_PC_MAX 23 // Maximum TX power for Category 1 UE (in dBm) -#define SRSLTE_NUM_PCI 504 +#define SRSLTE_NOF_NID_1 (168) +#define SRSLTE_NOF_NID_2 (3) +#define SRSLTE_NUM_PCI (SRSLTE_NOF_NID_1 * SRSLTE_NOF_NID_2) #define SRSLTE_MAX_RADIOS 3 // Maximum number of supported RF devices #define SRSLTE_MAX_CARRIERS 5 // Maximum number of supported simultaneous carriers diff --git a/lib/include/srslte/phy/phch/dci_nbiot.h b/lib/include/srslte/phy/phch/dci_nbiot.h new file mode 100644 index 000000000..2e2a2fb48 --- /dev/null +++ b/lib/include/srslte/phy/phch/dci_nbiot.h @@ -0,0 +1,83 @@ +/* + * 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/. + * + */ + +/** + * + * @file dci_nbiot.h + * + * @brief Downlink control information (DCI) for NB-IoT. + * + * Packing/Unpacking functions to convert between bit streams + * and packed DCI UL/DL grants defined in ra_nbiot.h + * + * Reference: 3GPP TS 36.212 version 13.2.0 Release 13 Sec. 6.4.3 + * + */ + +#ifndef SRSLTE_DCI_NBIOT_H +#define SRSLTE_DCI_NBIOT_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/ra_nbiot.h" + +#define SRSLTE_DCI_MAX_BITS 128 +#define SRSLTE_NBIOT_RAR_GRANT_LEN 15 + +SRSLTE_API void srslte_nbiot_dci_rar_grant_unpack(srslte_nbiot_dci_rar_grant_t* rar, + const uint8_t grant[SRSLTE_NBIOT_RAR_GRANT_LEN]); + +SRSLTE_API int srslte_nbiot_dci_msg_to_dl_grant(const srslte_dci_msg_t* msg, + const uint16_t msg_rnti, + srslte_ra_nbiot_dl_dci_t* dl_dci, + srslte_ra_nbiot_dl_grant_t* grant, + const uint32_t sfn, + const uint32_t sf_idx, + const uint32_t r_max, + const srslte_nbiot_mode_t mode); + +SRSLTE_API int srslte_nbiot_dci_msg_to_ul_grant(const srslte_dci_msg_t* msg, + srslte_ra_nbiot_ul_dci_t* ul_dci, + srslte_ra_nbiot_ul_grant_t* grant, + const uint32_t rx_tti, + const srslte_npusch_sc_spacing_t spacing); + +SRSLTE_API int +srslte_nbiot_dci_rar_to_ul_grant(srslte_nbiot_dci_rar_grant_t* rar, srslte_ra_nbiot_ul_grant_t* grant, uint32_t rx_tti); + +SRSLTE_API bool srslte_nbiot_dci_location_isvalid(const srslte_dci_location_t* c); + +SRSLTE_API int srslte_dci_msg_pack_npdsch(const srslte_ra_nbiot_dl_dci_t* data, + const srslte_dci_format_t format, + srslte_dci_msg_t* msg, + const bool crc_is_crnti); + +SRSLTE_API int +srslte_dci_msg_unpack_npdsch(const srslte_dci_msg_t* msg, srslte_ra_nbiot_dl_dci_t* data, const bool crc_is_crnti); + +SRSLTE_API int srslte_dci_msg_unpack_npusch(const srslte_dci_msg_t* msg, srslte_ra_nbiot_ul_dci_t* data); + +SRSLTE_API uint32_t srslte_dci_nbiot_format_sizeof(srslte_dci_format_t format); + +#endif // SRSLTE_DCI_NBIOT_H diff --git a/lib/include/srslte/phy/phch/npdcch.h b/lib/include/srslte/phy/phch/npdcch.h new file mode 100644 index 000000000..fe5a5dfd8 --- /dev/null +++ b/lib/include/srslte/phy/phch/npdcch.h @@ -0,0 +1,133 @@ +/* + * 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_NPDCCH_H +#define SRSLTE_NPDCCH_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/phch/dci.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/scrambling/scrambling.h" + +#define SRSLTE_RARNTI_END_NBIOT 0x0100 +#define SRSLTE_NBIOT_NUM_NRS_SYMS 8 +#define SRSLTE_NPDCCH_MAX_RE (SRSLTE_NRE * SRSLTE_CP_NORM_SF_NSYMB - SRSLTE_NBIOT_NUM_NRS_SYMS) + +#define SRSLTE_NBIOT_DCI_MAX_SIZE 23 +#define SRSLTE_AL_REPETITION_USS 64 // Higher layer configured parameter al-Repetition-USS + +typedef enum SRSLTE_API { + SRSLTE_NPDCCH_FORMAT1 = 0, + SRSLTE_NPDCCH_FORMAT0_LOWER_HALF, + SRSLTE_NPDCCH_FORMAT0_UPPER_HALF, + SRSLTE_NPDCCH_FORMAT_NITEMS +} srslte_npdcch_format_t; +static const char srslte_npdcch_format_text[SRSLTE_NPDCCH_FORMAT_NITEMS][30] = {"Format 1", + "Format 0 (Lower Half)", + "Format 0 (Upper Half)"}; + +/** + * @brief Narrowband Physical downlink control channel (NPDCCH) + * + * Reference: 3GPP TS 36.211 version 13.2.0 Release 11 Sec. 6.8 and 10.2.5 + */ +typedef struct SRSLTE_API { + srslte_nbiot_cell_t cell; + uint32_t nof_cce; + uint32_t ncce_bits; + uint32_t max_bits; + uint32_t i_n_start; /// start of the first OFDM symbol (signalled through NB-SIB1) + uint32_t nof_nbiot_refs; /// number of NRS symbols per OFDM symbol + uint32_t nof_lte_refs; /// number of CRS symbols per OFDM symbol + uint32_t num_decoded_symbols; + + /* buffers */ + cf_t* ce[SRSLTE_MAX_PORTS]; + cf_t* symbols[SRSLTE_MAX_PORTS]; + cf_t* x[SRSLTE_MAX_PORTS]; + cf_t* d; + uint8_t* e; + float rm_f[3 * (SRSLTE_DCI_MAX_BITS + 16)]; + float* llr[2]; // Two layers of LLRs for Format0 and Format1 NPDCCH + + /* tx & rx objects */ + srslte_modem_table_t mod; + srslte_sequence_t seq[SRSLTE_NOF_SF_X_FRAME]; + srslte_viterbi_t decoder; + srslte_crc_t crc; + +} srslte_npdcch_t; + +SRSLTE_API int srslte_npdcch_init(srslte_npdcch_t* q); + +SRSLTE_API void srslte_npdcch_free(srslte_npdcch_t* q); + +SRSLTE_API int srslte_npdcch_set_cell(srslte_npdcch_t* q, srslte_nbiot_cell_t cell); + +/// Encoding function +SRSLTE_API int srslte_npdcch_encode(srslte_npdcch_t* q, + srslte_dci_msg_t* msg, + srslte_dci_location_t location, + uint16_t rnti, + cf_t* sf_symbols[SRSLTE_MAX_PORTS], + uint32_t nsubframe); + +/// Decoding functions: Extract the LLRs and save them in the srslte_npdcch_t object +SRSLTE_API int srslte_npdcch_extract_llr(srslte_npdcch_t* q, + cf_t* sf_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t sf_idx); + +/// Decoding functions: Try to decode a DCI message after calling srslte_npdcch_extract_llr +SRSLTE_API int srslte_npdcch_decode_msg(srslte_npdcch_t* q, + srslte_dci_msg_t* msg, + srslte_dci_location_t* location, + srslte_dci_format_t format, + uint16_t* crc_rem); + +SRSLTE_API int +srslte_npdcch_dci_decode(srslte_npdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc); + +SRSLTE_API int +srslte_npdcch_dci_encode(srslte_npdcch_t* q, uint8_t* data, uint8_t* e, uint32_t nof_bits, uint32_t E, uint16_t rnti); + +SRSLTE_API void +srslte_npdcch_dci_encode_conv(srslte_npdcch_t* q, uint8_t* data, uint32_t nof_bits, uint8_t* coded_data, uint16_t rnti); + +SRSLTE_API uint32_t srslte_npdcch_ue_locations(srslte_dci_location_t* c, uint32_t max_candidates); + +SRSLTE_API uint32_t srslte_npdcch_common_locations(srslte_dci_location_t* c, uint32_t max_candidates); + +int srslte_npdcch_cp(srslte_npdcch_t* q, cf_t* input, cf_t* output, bool put, srslte_npdcch_format_t format); +int srslte_npdcch_put(srslte_npdcch_t* q, cf_t* symbols, cf_t* sf_symbols, srslte_npdcch_format_t format); +int srslte_npdcch_get(srslte_npdcch_t* q, cf_t* symbols, cf_t* sf_symbols, srslte_npdcch_format_t format); + +#endif // SRSLTE_NPDCCH_H diff --git a/lib/include/srslte/phy/phch/ra_nbiot.h b/lib/include/srslte/phy/phch/ra_nbiot.h index bdbde9df4..cf77b0bfc 100644 --- a/lib/include/srslte/phy/phch/ra_nbiot.h +++ b/lib/include/srslte/phy/phch/ra_nbiot.h @@ -190,7 +190,6 @@ typedef struct SRSLTE_API { /// Functions SRSLTE_API int srslte_ra_nbiot_dl_dci_to_grant(srslte_ra_nbiot_dl_dci_t* dci, - uint16_t msg_rnti, srslte_ra_nbiot_dl_grant_t* grant, uint32_t sfn, uint32_t sf_idx, diff --git a/lib/include/srslte/phy/rf/rf_utils.h b/lib/include/srslte/phy/rf/rf_utils.h index c7b1ac2f7..a38777328 100644 --- a/lib/include/srslte/phy/rf/rf_utils.h +++ b/lib/include/srslte/phy/rf/rf_utils.h @@ -52,4 +52,6 @@ SRSLTE_API int rf_search_and_decode_mib(srslte_rf_t* rf, srslte_cell_t* cell, float* cfo); +SRSLTE_API int rf_cell_search_nbiot(srslte_rf_t* rf, cell_search_cfg_t* config, srslte_nbiot_cell_t* cell, float* cfo); + #endif // SRSLTE_RF_UTILS_H diff --git a/lib/src/phy/phch/dci.c b/lib/src/phy/phch/dci.c index 51fa8a027..129d83c53 100644 --- a/lib/src/phy/phch/dci.c +++ b/lib/src/phy/phch/dci.c @@ -1446,6 +1446,12 @@ char* srslte_dci_format_string(srslte_dci_format_t format) return "Format2A"; case SRSLTE_DCI_FORMAT2B: return "Format2B"; + case SRSLTE_DCI_FORMATN0: + return "FormatN0"; + case SRSLTE_DCI_FORMATN1: + return "FormatN1"; + case SRSLTE_DCI_FORMATN2: + return "FormatN2"; default: return "N/A"; // fatal error } diff --git a/lib/src/phy/phch/dci_nbiot.c b/lib/src/phy/phch/dci_nbiot.c new file mode 100644 index 000000000..f66bfe9e4 --- /dev/null +++ b/lib/src/phy/phch/dci_nbiot.c @@ -0,0 +1,474 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/dci_nbiot.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +/* Creates the UL NPUSCH resource allocation grant from the random access respone message + */ +int srslte_nbiot_dci_rar_to_ul_grant(srslte_nbiot_dci_rar_grant_t* rar, + srslte_ra_nbiot_ul_grant_t* grant, + uint32_t rx_tti) +{ + // create DCI from rar grant + srslte_ra_nbiot_ul_dci_t dci; + bzero(&dci, sizeof(srslte_ra_nbiot_ul_dci_t)); + dci.i_rep = rar->n_rep; + dci.i_sc = rar->i_sc; + dci.i_mcs = rar->i_mcs; + dci.i_delay = rar->i_delay; + dci.sc_spacing = (rar->sc_spacing == 1) ? SRSLTE_NPUSCH_SC_SPACING_15000 : SRSLTE_NPUSCH_SC_SPACING_3750; + + // use DCI to fill default UL grant values + grant->format = SRSLTE_NPUSCH_FORMAT1; // UL-SCH is always format 1 + if (srslte_ra_nbiot_ul_rar_dci_to_grant(&dci, grant, rx_tti)) { + fprintf(stderr, "Error converting RAR DCI to grant.\n"); + return SRSLTE_ERROR; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_nbiot_ul_grant_fprint(stdout, grant); + } + return SRSLTE_SUCCESS; +} + +/* Unpack RAR UL grant as defined in Section 16.3.3 of 36.213 */ +void srslte_nbiot_dci_rar_grant_unpack(srslte_nbiot_dci_rar_grant_t* rar, + const uint8_t grant[SRSLTE_NBIOT_RAR_GRANT_LEN]) +{ + uint8_t* grant_ptr = (uint8_t*)grant; + rar->sc_spacing = srslte_bit_pack(&grant_ptr, 1); + rar->i_sc = srslte_bit_pack(&grant_ptr, 6); + rar->i_delay = srslte_bit_pack(&grant_ptr, 2); + rar->n_rep = srslte_bit_pack(&grant_ptr, 3); + rar->i_mcs = srslte_bit_pack(&grant_ptr, 3); +} + +// Creates the UL NPUSCH resource allocation grant from a DCI format N0 message +int srslte_nbiot_dci_msg_to_ul_grant(const srslte_dci_msg_t* msg, + srslte_ra_nbiot_ul_dci_t* ul_dci, + srslte_ra_nbiot_ul_grant_t* grant, + const uint32_t rx_tti, + const srslte_npusch_sc_spacing_t spacing) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && ul_dci != NULL && grant != NULL) { + ret = SRSLTE_ERROR; + + bzero(ul_dci, sizeof(srslte_ra_nbiot_ul_dci_t)); + bzero(grant, sizeof(srslte_ra_nbiot_ul_grant_t)); + + if (srslte_dci_msg_unpack_npusch(msg, ul_dci)) { + return ret; + } + + if (srslte_ra_nbiot_ul_dci_to_grant(ul_dci, grant, rx_tti, spacing)) { + return ret; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_npusch_fprint(stdout, ul_dci); + srslte_ra_nbiot_ul_grant_fprint(stdout, grant); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Unpacks a NB-IoT DCI message and configures the DL grant object + */ +int srslte_nbiot_dci_msg_to_dl_grant(const srslte_dci_msg_t* msg, + const uint16_t msg_rnti, + srslte_ra_nbiot_dl_dci_t* dl_dci, + srslte_ra_nbiot_dl_grant_t* grant, + const uint32_t sfn, + const uint32_t sf_idx, + const uint32_t r_max, + const srslte_nbiot_mode_t mode) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && grant != NULL && dl_dci != NULL) { + ret = SRSLTE_ERROR; + + bzero(dl_dci, sizeof(srslte_ra_nbiot_dl_dci_t)); + bzero(grant, sizeof(srslte_ra_nbiot_dl_grant_t)); + + bool crc_is_crnti = false; + if (msg_rnti >= SRSLTE_CRNTI_START && msg_rnti <= SRSLTE_CRNTI_END) { + crc_is_crnti = true; + } + srslte_dci_format_t tmp = msg->format; + ret = srslte_dci_msg_unpack_npdsch(msg, dl_dci, crc_is_crnti); + if (ret) { + fprintf(stderr, "Can't unpack DCI message %s (%d)\n", srslte_dci_format_string(tmp), tmp); + return ret; + } + ret = srslte_ra_nbiot_dl_dci_to_grant(dl_dci, grant, sfn, sf_idx, r_max, false, mode); + if (ret) { + fprintf(stderr, "Can't convert DCI %s to grant (%d)\n", srslte_dci_format_string(tmp), tmp); + return ret; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_nbiot_dl_dci_fprint(stdout, dl_dci); + srslte_ra_nbiot_dl_grant_fprint(stdout, grant); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +// For NB-IoT there are only three possible combinations, i.e. +// L' L nNCCE +// #1 1 0 0 +// #2 1 0 1 +// #3 2 1 0 +bool srslte_nbiot_dci_location_isvalid(const srslte_dci_location_t* c) +{ + if ((c->L == 1 && c->ncce <= 1) || (c->L == 2 && c->ncce == 0)) { + return true; + } else { + return false; + } +} + +uint32_t dci_formatN0_sizeof() +{ + return 23; +} + +uint32_t dci_formatN1_sizeof() +{ + // same as for formatN0 + return dci_formatN0_sizeof(); +} + +uint32_t dci_formatN2_sizeof() +{ + return 15; +} + +/* Packs DCI format N0 data to a sequence of bits and store them in msg according + * to 36.212 13.2.0 clause 6.4.3.1 + * + * TODO: TPC and cyclic shift for DM RS not implemented + */ +int dci_formatN0_pack(srslte_ra_nbiot_ul_dci_t* data, srslte_dci_msg_t* msg, uint32_t nof_prb) +{ + // pack bits + uint8_t* y = msg->payload; + + *y++ = 0; // format differentiation + + // TODO: Implement packing + + // Subcarrier indication – 6 bits + + // Resource assignment – 3 bits + + // Scheduling delay – 2 bits + + // Modulation and coding scheme – 4 bits + + // Redundancy version – 1 bit + + // Repetition number – 3 bits + + // New data indicator – 1 bit + + // DCI subframe repetition number – 2 bits + + return SRSLTE_ERROR; +} + +/* Packs DCI format N1 data to a sequence of bits and store them in msg according + * to 36.212 v13.2.0 clause 6.4.3.2 + * + * TODO: implement packing for NPRACH case + */ +int dci_formatN1_pack(const srslte_ra_nbiot_dl_dci_t* data, srslte_dci_msg_t* msg, bool crc_is_crnti) +{ + int last_bits_val = 0; + uint8_t* y = (uint8_t*)msg->payload; + + *y++ = 1; // format differentiation + + // NPDCCH order indicator – 1 bit + *y++ = data->alloc.is_ra; + if (data->alloc.is_ra) { + // Starting number of NPRACH repetitions – 2 bits + + // Subcarrier indication of NPRACH – 6 bits + + // All the remaining bits in format N1 + last_bits_val = 1; + } else { + // default NPDSCH scheduling + + // Scheduling delay – 3 bits + srslte_bit_unpack(data->alloc.i_delay, &y, 3); + + // Resource assignment – 3 bits + srslte_bit_unpack(data->alloc.i_sf, &y, 3); + + // Modulation and coding scheme – 4 bits + srslte_bit_unpack(data->mcs_idx, &y, 4); + + // Repetition number – 4 bits + srslte_bit_unpack(data->alloc.i_rep, &y, 4); + + // New data indicator – 1 bit + if (crc_is_crnti) { + *y++ = 0; + } else { + *y++ = data->ndi; + } + + // HARQ-ACK resource – 4 bits + if (crc_is_crnti) { + // reserved + y += 4; + } else { + srslte_bit_unpack(data->alloc.harq_ack, &y, 4); + } + } + + // Padding with zeros until reaching final size + uint32_t n = dci_formatN1_sizeof(); + while (y - msg->payload < n) { + *y++ = last_bits_val; + } + msg->nof_bits = (y - msg->payload); + + return SRSLTE_SUCCESS; +} + +// According to Section 6.4.3.1 in TS36.212 v13.2.0 +int dci_formatN0_unpack(const srslte_dci_msg_t* msg, srslte_ra_nbiot_ul_dci_t* data) +{ + uint8_t* y = (uint8_t*)msg->payload; + + // make sure it has the expected length + if (msg->nof_bits != dci_formatN1_sizeof()) { + fprintf(stderr, "Invalid message length for format N1\n"); + return SRSLTE_ERROR; + } + + // Format differentiation - 1 bit + data->format = srslte_bit_pack(&y, 1); + + // Subcarrier indication – 6 bits + data->i_sc = srslte_bit_pack(&y, 6); + + // Resource assignment – 3 bits + data->i_ru = srslte_bit_pack(&y, 3); + + // Scheduling delay – 2 bits + data->i_delay = srslte_bit_pack(&y, 2); + + // Modulation and coding scheme – 4 + data->i_mcs = srslte_bit_pack(&y, 4); + + // Redundancy version – 1 bit + data->i_rv = srslte_bit_pack(&y, 1); + + // Repetition number – 3 bits + data->i_rep = srslte_bit_pack(&y, 3); + + // New data indicator – 1 bit + data->ndi = srslte_bit_pack(&y, 1); + + // DCI subframe repetition number – 2 bits + data->dci_sf_rep_num = srslte_bit_pack(&y, 2); + + // According to 16.5.1.1, SC spacing is determined by RAR grant + // TODO: Add support for 3.75kHz + data->sc_spacing = SRSLTE_NPUSCH_SC_SPACING_15000; + + return SRSLTE_SUCCESS; +} + +int dci_formatN1_unpack(const srslte_dci_msg_t* msg, srslte_ra_nbiot_dl_dci_t* data, bool crc_is_crnti) +{ + uint8_t* y = (uint8_t*)msg->payload; + + // make sure it has the expected length + if (msg->nof_bits != dci_formatN1_sizeof()) { + fprintf(stderr, "Invalid message length for format N1\n"); + return SRSLTE_ERROR; + } + + // Format differentiation - 1 bit + data->format = srslte_bit_pack(&y, 1); + data->dci_is_n2 = false; + + // The NPDCCH order indicator (if bit is one, this is for RA procedure) + data->alloc.is_ra = srslte_bit_pack(&y, 1); + + if (data->alloc.is_ra) { + // This is a RA precedure, set field according to Section 6.4.3.2 in TS36.212 + data->alloc.nprach_start = srslte_bit_pack(&y, 2); + data->alloc.nprach_sc = srslte_bit_pack(&y, 6); + // set remaining field to 1 + data->alloc.i_delay = 0xffff; + data->alloc.i_sf = 0xffff; + data->mcs_idx = 0xffff; + data->alloc.i_rep = 0xffff; + data->ndi = true; + data->alloc.harq_ack = 0xffff; + data->alloc.dci_sf_rep_num = 0xffff; + } else { + // default NPDSCH scheduling + + // Scheduling delay – 3 bits + data->alloc.i_delay = srslte_bit_pack(&y, 3); + + // Resource assignment – 3 bits + data->alloc.i_sf = srslte_bit_pack(&y, 3); + + // Modulation and coding scheme – 4 bits + data->mcs_idx = srslte_bit_pack(&y, 4); + + // Repetition number – 4 bits + data->alloc.i_rep = srslte_bit_pack(&y, 4); + + // New data indicator – 1 bit + if (crc_is_crnti) { + data->ndi = *y++ ? true : false; + } else { + y++; // NDI reserved + } + + // HARQ-ACK resource – 4 bits + data->alloc.harq_ack = srslte_bit_pack(&y, 4); + + // DCI subframe repetition number – 2 bits + data->alloc.dci_sf_rep_num = srslte_bit_pack(&y, 2); + } + return SRSLTE_SUCCESS; +} + +int dci_formatN2_unpack(const srslte_dci_msg_t* msg, srslte_ra_nbiot_dl_dci_t* data) +{ + uint8_t* y = (uint8_t*)msg->payload; + + // make sure it has the expected length + if (msg->nof_bits != dci_formatN2_sizeof()) { + fprintf(stderr, "Invalid message length for format N2\n"); + return SRSLTE_ERROR; + } + + data->dci_is_n2 = true; + + // Flag for paging/direct indication differentiation – 1 bit, with value 0 for direct indication and value 1 for + // paging + data->format = srslte_bit_pack(&y, 1); + + if (data->format == 0) { + // Direct Indication information – 8 bits provide direct indication of system information update and other fields + data->dir_indication_info = srslte_bit_pack(&y, 8); + } else { + // Paging + + // Resource assignment – 3 bits + data->alloc.i_sf = srslte_bit_pack(&y, 3); + + // Modulation and coding scheme – 4 bits + data->mcs_idx = srslte_bit_pack(&y, 4); + + // Repetition number – 4 bits + data->alloc.i_rep = srslte_bit_pack(&y, 4); + + // DCI subframe repetition number – 3 bits + data->alloc.dci_sf_rep_num = srslte_bit_pack(&y, 3); + } + return SRSLTE_SUCCESS; +} + +uint32_t srslte_dci_nbiot_format_sizeof(const srslte_dci_format_t format) +{ + switch (format) { + case SRSLTE_DCI_FORMATN0: + return dci_formatN0_sizeof(); + case SRSLTE_DCI_FORMATN1: + return dci_formatN1_sizeof(); + case SRSLTE_DCI_FORMATN2: + return dci_formatN2_sizeof(); + default: + printf("Error computing DCI bits: Unknown format %d\n", format); + return 0; + } +} + +int srslte_dci_msg_unpack_npdsch(const srslte_dci_msg_t* msg, srslte_ra_nbiot_dl_dci_t* data, const bool crc_is_crnti) +{ + switch (msg->format) { + case SRSLTE_DCI_FORMATN1: + return dci_formatN1_unpack(msg, data, crc_is_crnti); + case SRSLTE_DCI_FORMATN2: + return dci_formatN2_unpack(msg, data); + default: + fprintf(stderr, "DCI unpack npdsch: Invalid DCI format %s\n", srslte_dci_format_string(msg->format)); + return SRSLTE_ERROR; + } +} + +int srslte_dci_msg_unpack_npusch(const srslte_dci_msg_t* msg, srslte_ra_nbiot_ul_dci_t* data) +{ + switch (msg->format) { + case SRSLTE_DCI_FORMATN0: + return dci_formatN0_unpack(msg, data); + default: + fprintf(stderr, "DCI unpack npusch: Invalid DCI format %s\n", srslte_dci_format_string(msg->format)); + return SRSLTE_ERROR; + } +} + +int srslte_dci_msg_pack_npdsch(const srslte_ra_nbiot_dl_dci_t* data, + const srslte_dci_format_t format, + srslte_dci_msg_t* msg, + const bool crc_is_crnti) +{ + msg->format = format; + switch (format) { + case SRSLTE_DCI_FORMATN1: + return dci_formatN1_pack(data, msg, crc_is_crnti); + default: + fprintf(stderr, "DCI pack npdsch: Invalid DCI format %s in \n", srslte_dci_format_string(format)); + return SRSLTE_ERROR; + } +} diff --git a/lib/src/phy/phch/npdcch.c b/lib/src/phy/phch/npdcch.c new file mode 100644 index 000000000..8c74437e7 --- /dev/null +++ b/lib/src/phy/phch/npdcch.c @@ -0,0 +1,800 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "prb_dl.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/dci_nbiot.h" +#include "srslte/phy/phch/npdcch.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define DUMP_SIGNALS 0 +#define RE_EXT_DEBUG 0 + +/** Initializes the NPDCCH transmitter and receiver */ +int srslte_npdcch_init(srslte_npdcch_t* q) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_npdcch_t)); + q->nof_cce = 2; // One Format1 NPDCCH occupying both NCCEs + + // Allocate memory for the maximum number of NPDCCH bits, i.e. one full PRB + q->max_bits = SRSLTE_CP_NORM_SF_NSYMB * SRSLTE_NRE * 2; + q->ncce_bits = q->max_bits / 2; + + INFO("Init NPDCCH: Max bits: %d, %d ports.\n", q->max_bits, q->cell.nof_ports); + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + goto clean; + } + if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC16, 16)) { + goto clean; + } + + int poly[3] = {0x6D, 0x4F, 0x57}; + if (srslte_viterbi_init(&q->decoder, SRSLTE_VITERBI_37, poly, SRSLTE_NBIOT_DCI_MAX_SIZE + 16, true)) { + goto clean; + } + + q->e = srslte_vec_malloc(sizeof(uint8_t) * q->max_bits); + if (!q->e) { + goto clean; + } + + for (int i = 0; i < 2; i++) { + q->llr[i] = srslte_vec_malloc(sizeof(float) * q->max_bits); + if (!q->llr[i]) { + goto clean; + } + bzero(q->llr[i], sizeof(float) * q->max_bits); + } + + q->d = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->d) { + goto clean; + } + + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->ce[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->ce[i]) { + goto clean; + } + for (uint32_t k = 0; k < q->max_bits / 2; k++) { + q->ce[i][k] = 1; + } + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->x[i]) { + goto clean; + } + q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->symbols[i]) { + goto clean; + } + memset(q->symbols[i], 0, sizeof(cf_t) * q->max_bits / 2); + } + + ret = SRSLTE_SUCCESS; + } +clean: + if (ret == SRSLTE_ERROR) { + srslte_npdcch_free(q); + } + return ret; +} + +void srslte_npdcch_free(srslte_npdcch_t* q) +{ + if (q->e) { + free(q->e); + } + + for (uint32_t i = 0; i < 2; i++) { + if (q->llr[i]) { + free(q->llr[i]); + } + } + + 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]); + } + } + + for (uint32_t i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { + srslte_sequence_free(&q->seq[i]); + } + + srslte_modem_table_free(&q->mod); + srslte_viterbi_free(&q->decoder); + + bzero(q, sizeof(srslte_npdcch_t)); +} + +int srslte_npdcch_set_cell(srslte_npdcch_t* q, srslte_nbiot_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_nbiot_cell_isvalid(&cell)) { + ret = SRSLTE_ERROR; + + if (q->cell.n_id_ncell != cell.n_id_ncell || q->cell.base.nof_prb == 0) { + q->cell = cell; + + if (q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI || q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI) { + q->i_n_start = 3; + } else { + q->i_n_start = 0; + } + + // FIXME: Add case for LTE cell with 4 ports + if (q->cell.nof_ports == 1) { + q->nof_nbiot_refs = 2; + } else { + q->nof_nbiot_refs = 4; + } + + if (q->cell.base.nof_ports == 1) { + q->nof_lte_refs = 2; + } else { + q->nof_lte_refs = 4; + } + + // Update the maximum number of NPDCCH bits, i.e. one PRB minus the starting offset minus the reference symbols + q->max_bits = srslte_ra_nbiot_dl_grant_nof_re(q->cell, q->i_n_start) * 2; + q->ncce_bits = q->max_bits / 2; + + for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { + if (srslte_sequence_npdcch(&q->seq[i], 2 * i, q->cell.n_id_ncell, q->max_bits)) { + return SRSLTE_ERROR; + } + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_npdcch_dci_decode(srslte_npdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc) +{ + uint16_t p_bits, crc_res; + uint8_t* x; + + if (q != NULL) { + if (data != NULL && E <= q->max_bits && nof_bits <= SRSLTE_DCI_MAX_BITS) { + bzero(q->rm_f, sizeof(float) * 3 * (SRSLTE_DCI_MAX_BITS + 16)); + + uint32_t coded_len = 3 * (nof_bits + 16); + + // unrate matching + srslte_rm_conv_rx(e, E, q->rm_f, coded_len); + + // viterbi decoder + srslte_viterbi_decode_f(&q->decoder, q->rm_f, data, nof_bits + 16); + + x = &data[nof_bits]; + p_bits = (uint16_t)srslte_bit_pack(&x, 16); + crc_res = ((uint16_t)srslte_crc_checksum(&q->crc, data, nof_bits) & 0xffff); + + if (crc) { + *crc = p_bits ^ crc_res; + } + + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters: E: %d, max_bits: %d, nof_bits: %d\n", E, q->max_bits, nof_bits); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/** Tries to decode a DCI message from the LLRs stored in the srslte_npdcch_t structure by the function + * srslte_npdcch_extract_llr(). This function can be called multiple times. + * The decoded message is stored in msg and the CRC remainder in crc_rem pointer + * + */ +int srslte_npdcch_decode_msg(srslte_npdcch_t* q, + srslte_dci_msg_t* msg, + srslte_dci_location_t* location, + srslte_dci_format_t format, + uint16_t* crc_rem) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + int num_decoded_symbols = 0; + if (q != NULL && msg != NULL && srslte_nbiot_dci_location_isvalid(location)) { + uint32_t nof_bits = (format == SRSLTE_DCI_FORMATN2) ? 15 : 23; + uint32_t e_bits = q->ncce_bits * location->L; + + // Get the right softbits for this aggregation level + float* llr = (location->L == 1) ? q->llr[0] : q->llr[1]; + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("LLR:\n"); + srslte_vec_fprint_f(stdout, llr, q->max_bits); + } + + double mean = 0; + for (int i = 0; i < e_bits; i++) { + mean += fabsf(llr[location->ncce * q->ncce_bits + i]); + } + mean /= e_bits; + if (mean > 0.3) { + ret = srslte_npdcch_dci_decode(q, &llr[location->ncce * q->ncce_bits], msg->payload, e_bits, nof_bits, crc_rem); + if (ret == SRSLTE_SUCCESS) { + num_decoded_symbols = e_bits / 2; + msg->nof_bits = nof_bits; + // Check format differentiation + if (format == SRSLTE_DCI_FORMATN0 || format == SRSLTE_DCI_FORMATN1) { + msg->format = (msg->payload[0] == 0) ? SRSLTE_DCI_FORMATN0 : SRSLTE_DCI_FORMATN1; + } else { + msg->format = format; + } + } else { + fprintf(stderr, "Error calling npdcch_dci_decode\n"); + } + if (crc_rem) { + DEBUG("Decoded DCI: nCCE=%d, L=%d, format=%s, msg_len=%d, mean=%f, crc_rem=0x%x\n", + location->ncce, + location->L, + srslte_dci_format_string(msg->format), + nof_bits, + mean, + *crc_rem); + } + } else { + DEBUG("Skipping DCI: nCCE=%d, L=%d, msg_len=%d, mean=%f\n", location->ncce, location->L, nof_bits, mean); + } + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters, location=%d,%d\n", location->ncce, location->L); + } + + q->num_decoded_symbols = num_decoded_symbols; + return ret; +} + +/** Extracts the LLRs from srslte_dci_location_t location of the subframe and stores them in the srslte_npdcch_t + * structure. DCI messages can be extracted from this location calling the function srslte_npdcch_decode_msg(). Every + * time this function is called (with a different location), the last demodulated symbols are overwritten and new + * messages from other locations can be decoded + */ +int srslte_npdcch_extract_llr(srslte_npdcch_t* q, + cf_t* sf_symbols, + cf_t* ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t sf_idx) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + /* Set pointers for layermapping & precoding */ + uint32_t num_symbols, e_bits; + float* llr; + cf_t* x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && sf_idx < 10) { + ret = SRSLTE_ERROR; + + for (int i = 0; i < SRSLTE_NPDCCH_FORMAT_NITEMS; i++) { + // set parameters according to NPDCCH format + switch (i) { + case SRSLTE_NPDCCH_FORMAT0_LOWER_HALF: + e_bits = q->ncce_bits; + llr = q->llr[0]; + break; + case SRSLTE_NPDCCH_FORMAT0_UPPER_HALF: + e_bits = q->ncce_bits; + llr = &q->llr[0][q->ncce_bits]; + break; + case SRSLTE_NPDCCH_FORMAT1: + e_bits = q->ncce_bits * 2; + llr = q->llr[1]; + break; + default: + return ret; + } + num_symbols = e_bits / 2; + + DEBUG("Extracting LLRs for NPDCCH %s: E: %d, SF: %d\n", srslte_npdcch_format_text[i], e_bits, sf_idx); + + if (i != SRSLTE_NPDCCH_FORMAT0_UPPER_HALF) { + // don't overwrite lower half LLRs + bzero(llr, sizeof(float) * q->max_bits); + } + + // number of layers equals number of ports + for (int f = 0; f < q->cell.nof_ports; f++) { + x[f] = q->x[f]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + // extract symbols + int n = srslte_npdcch_get(q, sf_symbols, q->symbols[0], i); + if (num_symbols != n) { + fprintf(stderr, "Expected %d NPDCCH symbols but got %d symbols\n", num_symbols, n); + return ret; + } + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_rx_mapping_output.bin: NPDCCH after extracting symbols\n"); + srslte_vec_save_file("npdcch_rx_mapping_output.bin", q->symbols[0], n * sizeof(cf_t)); + } +#endif + + // extract channel estimates + for (int p = 0; p < q->cell.nof_ports; p++) { + n = srslte_npdcch_get(q, ce[p], q->ce[p], i); + if (num_symbols != n) { + fprintf(stderr, "Expected %d NPDCCH symbols but got %d symbols\n", num_symbols, n); + return ret; + } + } + + // in control channels, only diversity is supported + if (q->cell.nof_ports == 1) { + // no need for layer demapping + srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, NULL, num_symbols, 1.0, noise_estimate); + } else { + srslte_predecoding_diversity(q->symbols[0], q->ce, x, q->cell.nof_ports, num_symbols, 1.0); + srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports, num_symbols / q->cell.nof_ports); + } + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_rx_predecode_output.bin: NPDCCH after predecoding symbols\n"); + srslte_vec_save_file("npdcch_rx_predecode_output.bin", q->d, q->num_decoded_symbols * sizeof(cf_t)); + } +#endif + + // demodulate symbols + srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, llr, num_symbols); + + // descramble + srslte_scrambling_f_offset(&q->seq[sf_idx], llr, 0, e_bits); + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_rx_descramble_output.bin: NPDCCH after de-scrambling\n"); + srslte_vec_save_file("npdcch_rx_descramble_output.bin", llr, e_bits); + } +#endif + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +static void crc_set_mask_rnti(uint8_t* crc, uint16_t rnti) +{ + uint8_t mask[16] = {}; + uint8_t* r = mask; + + DEBUG("Mask CRC with RNTI 0x%x\n", rnti); + + srslte_bit_unpack(rnti, &r, 16); + for (uint32_t i = 0; i < 16; i++) { + crc[i] = (crc[i] + mask[i]) % 2; + } +} + +void srslte_npdcch_dci_encode_conv(srslte_npdcch_t* q, + uint8_t* data, + uint32_t nof_bits, + uint8_t* coded_data, + uint16_t rnti) +{ + srslte_convcoder_t encoder; + int poly[3] = {0x6D, 0x4F, 0x57}; + encoder.K = 7; + encoder.R = 3; + encoder.tail_biting = true; + memcpy(encoder.poly, poly, 3 * sizeof(int)); + + srslte_crc_attach(&q->crc, data, nof_bits); + crc_set_mask_rnti(&data[nof_bits], rnti); + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_tx_convcoder_input.bin: NPDCCH before convolution coding\n"); + srslte_vec_save_file("npdcch_tx_convcoder_input.bin", data, nof_bits + 16); + } +#endif + + srslte_convcoder_encode(&encoder, data, coded_data, nof_bits + 16); +} + +/** 36.212 5.3.3.2 to 5.3.3.4 + * TODO: UE transmit antenna selection CRC mask + */ +int srslte_npdcch_dci_encode(srslte_npdcch_t* q, + uint8_t* data, + uint8_t* e, + uint32_t nof_bits, + uint32_t E, + uint16_t rnti) +{ + uint8_t tmp[3 * (SRSLTE_DCI_MAX_BITS + 16)]; + + if (q != NULL && data != NULL && e != NULL && nof_bits < SRSLTE_DCI_MAX_BITS && E <= q->max_bits) { + srslte_npdcch_dci_encode_conv(q, data, nof_bits, tmp, rnti); + + DEBUG("CConv output: "); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, tmp, 3 * (nof_bits + 16)); + } + + srslte_rm_conv_tx(tmp, 3 * (nof_bits + 16), e, E); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_npdcch_encode(srslte_npdcch_t* q, + srslte_dci_msg_t* msg, + srslte_dci_location_t location, + uint16_t rnti, + cf_t* sf_symbols[SRSLTE_MAX_PORTS], + uint32_t nsubframe) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && sf_symbols != NULL && nsubframe < 10 && srslte_nbiot_dci_location_isvalid(&location)) { + ret = SRSLTE_ERROR; + uint32_t e_bits = q->nof_cce * q->ncce_bits; + uint32_t nof_symbols = e_bits / 2; + + if (msg->nof_bits < SRSLTE_DCI_MAX_BITS - 16) { + DEBUG("Encoding DCI: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x, sf_idx: %d\n", + msg->nof_bits, + e_bits, + location.ncce, + location.L, + rnti, + nsubframe); + + if (srslte_npdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, e_bits, rnti) != SRSLTE_SUCCESS) { + fprintf(stderr, "Error encoding DCI\n"); + return SRSLTE_ERROR; + } + + // number of layers equals number of ports + cf_t* x[SRSLTE_MAX_LAYERS] = {NULL}; + 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)); + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_tx_scramble_input.bin: NPDCCH before scrambling\n"); + srslte_vec_save_file("npdcch_tx_scramble_input.bin", q->e, e_bits); + } +#endif + + srslte_scrambling_b_offset(&q->seq[nsubframe], q->e, 72 * location.ncce, e_bits); + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_tx_mod_input.bin: NPDCCH before modulation\n"); + srslte_vec_save_file("npdcch_tx_mod_input.bin", q->e, e_bits); + } +#endif + + srslte_mod_modulate(&q->mod, q->e, q->d, e_bits); + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_tx_precode_input.bin: NPDCCH before precoding symbols\n"); + srslte_vec_save_file("npdcch_tx_precode_input.bin", q->d, nof_symbols * sizeof(cf_t)); + } +#endif + + // layer mapping & precoding + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d, x, q->cell.nof_ports, nof_symbols); + srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports, 1.0); + } else { + memcpy(q->symbols[0], q->d, nof_symbols * sizeof(cf_t)); + } + +#if DUMP_SIGNALS + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE npdcch_tx_mapping_input.bin: NPDCCH before mapping to resource elements\n"); + srslte_vec_save_file("npdcch_tx_mapping_input.bin", q->symbols[0], nof_symbols * sizeof(cf_t)); + } +#endif + + // mapping to resource elements + for (int i = 0; i < q->cell.nof_ports; i++) { + srslte_npdcch_put(q, q->symbols[i], sf_symbols[i], SRSLTE_NPDCCH_FORMAT1); + } + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Illegal DCI message nCCE: %d, L: %d, nof_cce: %d\n", location.ncce, location.L, q->nof_cce); + } + } else { + fprintf(stderr, "Invalid parameters: L=%d, nCCE=%d\n", location.L, location.ncce); + } + return ret; +} + +/** 36.213 v9.1.1 + * Computes up to max_candidates UE-specific candidates for DCI messages and saves them + * in the structure pointed by c. + * Returns the number of candidates saved in the array c. + */ +uint32_t srslte_npdcch_ue_locations(srslte_dci_location_t* c, uint32_t max_candidates) +{ + // NPDCCH format 1 takes both NCCE + c[0].L = 2; + c[0].ncce = 0; + + // NPDCCH format 0 only takes one NCCE so two of them may be transmitted in one subframe + c[1].L = 1; + c[1].ncce = 0; + + c[2].L = 1; + c[2].ncce = 1; + + return max_candidates; +} + +uint32_t srslte_npdcch_common_locations(srslte_dci_location_t* c, uint32_t max_candidates) +{ + return srslte_npdcch_ue_locations(c, max_candidates); +} + +int srslte_npdcch_cp(srslte_npdcch_t* q, cf_t* input, cf_t* output, bool put, srslte_npdcch_format_t format) +{ + // sanity check + if (q == NULL || input == NULL || output == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + +#if RE_EXT_DEBUG + int num_extracted = 0; +#endif + + cf_t *in_ptr = input, *out_ptr = output; + bool skip_crs = false; + + if (put) { + out_ptr += (q->i_n_start * q->cell.base.nof_prb * SRSLTE_NRE) + q->cell.nbiot_prb * SRSLTE_NRE; + } else { + in_ptr += (q->i_n_start * q->cell.base.nof_prb * SRSLTE_NRE) + q->cell.nbiot_prb * SRSLTE_NRE; + } + + if (q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI || q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI) { + skip_crs = true; + } + + // start mapping at specified OFDM symbol + for (int l = q->i_n_start; l < SRSLTE_CP_NORM_SF_NSYMB; l++) { + uint32_t delta = (q->cell.base.nof_prb - 1) * SRSLTE_NRE; // the number of REs skipped in each OFDM symbol + uint32_t offset = 0; // the number of REs left out before start of the REF signal RE + if (l == 5 || l == 6 || l == 12 || l == 13) { + // always skip NRS + if (q->nof_nbiot_refs == 2) { + if (l == 5 || l == 12) { + offset = q->cell.n_id_ncell % 6; + delta = q->cell.n_id_ncell % 6 == 5 ? 1 : 0; + } else { + offset = (q->cell.n_id_ncell + 3) % 6; + delta = (q->cell.n_id_ncell + 3) % 6 == 5 ? 1 : 0; + } + } else if (q->nof_nbiot_refs == 4) { + offset = q->cell.n_id_ncell % 3; + delta = (q->cell.n_id_ncell + ((q->cell.n_id_ncell >= 5) ? 0 : 3)) % 6 == 5 ? 1 : 0; + } else { + // FIXME: not handled right now + return SRSLTE_ERROR; + } + + switch (format) { + case SRSLTE_NPDCCH_FORMAT0_LOWER_HALF: + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put); + // we have copied too much, rewind ptr + if (put) { + in_ptr -= (SRSLTE_NRE - q->nof_nbiot_refs) / 2; + } else { + out_ptr -= (SRSLTE_NRE - q->nof_nbiot_refs) / 2; + } + break; + case SRSLTE_NPDCCH_FORMAT0_UPPER_HALF: + // FIXME: this causes valgrind to detect an invalid memory access +#if 0 + // skip lower half + if (put) { + out_ptr += SRSLTE_NRE / 2; + } else { + in_ptr += SRSLTE_NRE / 2; + } +#endif + // copy REs + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put); + // we have copied too much, rewind ptr + if (put) { + in_ptr -= (SRSLTE_NRE - q->nof_nbiot_refs) / 2; + } else { + out_ptr -= (SRSLTE_NRE - q->nof_nbiot_refs) / 2; + } + break; + case SRSLTE_NPDCCH_FORMAT1: + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_nbiot_refs, q->nof_nbiot_refs, put); + break; + default: + printf("Wrong NPDCCH format!\n"); + return SRSLTE_ERROR; + } + } else if ((l == 0 || l == 4 || l == 7 || l == 11) && skip_crs) { + // skip LTE's CRS (FIXME: use base cell ID?) + if (q->nof_lte_refs == 2) { + if (l == 0 || l == 7) { + offset = q->cell.base.id % 6; + delta = (q->cell.base.id + 3) % 6 == 2 ? 1 : 0; + } else if (l == 4 || l == 11) { + offset = (q->cell.base.id + 3) % 6; + delta = (q->cell.base.id + ((q->cell.base.id <= 5) ? 3 : 0)) % 6 == 5 ? 1 : 0; + } + } else { + offset = q->cell.base.id % 3; + delta = q->cell.base.id % 3 == 2 ? 1 : 0; + } + + switch (format) { + case SRSLTE_NPDCCH_FORMAT0_LOWER_HALF: + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put); + // we have copied too much, rewind ptr + if (put) { + in_ptr -= (SRSLTE_NRE - q->nof_lte_refs) / 2; + } else { + out_ptr -= (SRSLTE_NRE - q->nof_lte_refs) / 2; + } + break; + case SRSLTE_NPDCCH_FORMAT0_UPPER_HALF: + // skip lower half + if (put) { + out_ptr += SRSLTE_NRE / 2; + } else { + in_ptr += SRSLTE_NRE / 2; + } + // copy REs + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put); + // we have copied too much, rewind ptr + if (put) { + in_ptr -= (SRSLTE_NRE - q->nof_lte_refs) / 2; + } else { + out_ptr -= (SRSLTE_NRE - q->nof_lte_refs) / 2; + } + break; + case SRSLTE_NPDCCH_FORMAT1: + prb_cp_ref(&in_ptr, &out_ptr, offset, q->nof_lte_refs, q->nof_lte_refs, put); + break; + default: + printf("Wrong NPDCCH format!\n"); + return SRSLTE_ERROR; + } + } else { + switch (format) { + case SRSLTE_NPDCCH_FORMAT0_LOWER_HALF: + prb_cp_half(&in_ptr, &out_ptr, 1); + // skip upper half + if (put) { + out_ptr += SRSLTE_NRE / 2; + } else { + in_ptr += SRSLTE_NRE / 2; + } + break; + case SRSLTE_NPDCCH_FORMAT0_UPPER_HALF: + // skip lower half + if (put) { + out_ptr += SRSLTE_NRE / 2; + } else { + in_ptr += SRSLTE_NRE / 2; + } + prb_cp_half(&in_ptr, &out_ptr, 1); + break; + case SRSLTE_NPDCCH_FORMAT1: + // occupy entire symbol + prb_cp(&in_ptr, &out_ptr, 1); + break; + default: + printf("Wrong NPDCCH format!\n"); + return SRSLTE_ERROR; + } + } + + if (put) { + out_ptr += delta; + } else { + in_ptr += delta; + } + +#if RE_EXT_DEBUG + printf("\nl=%d, delta=%d offset=%d\n", l, delta, offset); + uint32_t num_extracted_this_sym = abs((int)(output - out_ptr)) - num_extracted; + printf(" - extracted total of %d RE after symbol %d (this symbol=%d)\n", + abs((int)(output - out_ptr)), + l, + num_extracted_this_sym); + srslte_vec_fprint_c(stdout, &output[num_extracted], num_extracted_this_sym); + num_extracted = abs((int)(output - out_ptr)); +#endif + } + + int r; + if (put) { + r = abs((int)(input - in_ptr)); + } else { + r = abs((int)(output - out_ptr)); + } + + return r; +} + +/** + * Puts NPDCCH in the subframe + * + * Returns the number of symbols written to sf_symbols + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_npdcch_put(srslte_npdcch_t* q, cf_t* symbols, cf_t* sf_symbols, srslte_npdcch_format_t format) +{ + return srslte_npdcch_cp(q, symbols, sf_symbols, true, format); +} + +/** + * Extracts NPDCCH from the subframe + * + * Returns the number of symbols read + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_npdcch_get(srslte_npdcch_t* q, cf_t* sf_symbols, cf_t* symbols, srslte_npdcch_format_t format) +{ + return srslte_npdcch_cp(q, sf_symbols, symbols, false, format); +} diff --git a/lib/src/phy/phch/ra_nbiot.c b/lib/src/phy/phch/ra_nbiot.c index f5f229db9..9dd10a0c0 100644 --- a/lib/src/phy/phch/ra_nbiot.c +++ b/lib/src/phy/phch/ra_nbiot.c @@ -265,13 +265,16 @@ bool srslte_ra_nbiot_dl_has_ref_signal_inband(uint32_t tti) return srslte_ra_nbiot_dl_has_ref_signal(tti); } +/// Valid NB-IoT DL subframes are subframes that DON'T carry: +/// - NPBCH (subframe 0) +/// - NPSS (subframe 5) +/// - NSSS (subframe 9 in all even frames) 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))); + 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, diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 205d66874..c18b18861 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -282,6 +282,19 @@ add_test(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_ add_test(pdsch_pdcch_file_test pdsch_pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) add_test(pmch_file_test pmch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/pmch_100prbs_MCS2_SR0.bin) +######################################################################## +# NPDCCH TEST +######################################################################## + +add_executable(npdcch_test npdcch_test.c) +target_link_libraries(npdcch_test srslte_phy) +add_test(npdcch_formatN1_test npdcch_test -o FormatN1) + +add_executable(npdcch_file_test npdcch_file_test.c) +target_link_libraries(npdcch_file_test srslte_phy) +add_test(npdcch_formatN0_file_test npdcch_file_test -c 0 -t 8624 -r 258 -L 1 -l 0 -v -o FormatN0 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_dci_formatN0_L_1_nid0_tti_8624_rnti_0x102.bin) +add_test(npdcch_formatN1_file_test npdcch_file_test -c 0 -t 5461 -r 137 -L 2 -l 0 -v -o FormatN1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_dci_formatN1_nid0_tti_5461_rnti_0x89.bin) + ######################################################################## # PUSCH TEST ######################################################################## diff --git a/lib/src/phy/phch/test/npdcch_file_test.c b/lib/src/phy/phch/test/npdcch_file_test.c new file mode 100644 index 000000000..9a08c8d0c --- /dev/null +++ b/lib/src/phy/phch/test/npdcch_file_test.c @@ -0,0 +1,260 @@ +/* + * 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/ch_estimation/chest_dl_nbiot.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/phch/dci_nbiot.h" +#include "srslte/phy/phch/npdcch.h" +#include "srslte/phy/phch/ra_nbiot.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include +#include +#include +#include + +char* input_file_name = NULL; +srslte_dci_format_t dci_format = SRSLTE_DCI_FORMATN0; +uint16_t rnti = 0; +uint32_t tti = 0; +int nof_frames = 1; +srslte_dci_location_t dci_location = {}; + +srslte_nbiot_cell_t cell = {.base = {.nof_prb = 1, .nof_ports = 1, .cp = SRSLTE_CP_NORM, .id = 0}, + .nbiot_prb = 0, + .n_id_ncell = 0, + .nof_ports = 1, + .mode = SRSLTE_NBIOT_MODE_STANDALONE}; + +void usage(char* prog) +{ + printf("Usage: %s [cprndv] -i input_file\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-o DCI Format [Default %s]\n", srslte_dci_format_string(dci_format)); + printf("\t-L DCI location L value [Default %d]\n", dci_location.L); + printf("\t-l DCI location ncee value [Default %d]\n", dci_location.ncce); + printf("\t-r rnti [Default %d]\n", rnti); + printf("\t-t tti [Default %d]\n", tti); + 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, "icplLrontv")) != -1) { + switch (opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'p': + cell.base.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'c': + cell.base.id = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'l': + dci_location.ncce = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'L': + dci_location.L = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'r': + rnti = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'o': + dci_format = srslte_dci_format_from_string(argv[optind]); + if (dci_format == SRSLTE_DCI_NOF_FORMATS) { + ERROR("Error unsupported format %s\n", argv[optind]); + exit(-1); + } + break; + case 't': + tti = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char** argv) +{ + cf_t * input_buffer = NULL, *fft_buffer = NULL, *ce[SRSLTE_MAX_PORTS] = {NULL}; + srslte_filesource_t fsrc; + srslte_chest_dl_nbiot_t chest; + srslte_ofdm_t fft; + srslte_npdcch_t npdcch = {}; + srslte_dci_msg_t dci_rx = {}; + int ret = SRSLTE_ERROR; + int frame_cnt = 0; + int nof_decoded_dcis = 0; + int nread = 0; + + parse_args(argc, argv); + + // we need to allocate RE's for a full 6 PRB cell + int nof_re = 6 * SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp); + + // init memory + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + ce[i] = srslte_vec_cf_malloc(nof_re); + if (!ce[i]) { + perror("malloc"); + goto quit; + } + for (int j = 0; j < nof_re; j++) { + ce[i][j] = 1; + } + } + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + goto quit; + } + + uint32_t sf_len = SRSLTE_SF_LEN(srslte_symbol_sz(cell.base.nof_prb)); + + input_buffer = srslte_vec_cf_malloc(sf_len); + if (!input_buffer) { + perror("malloc"); + goto quit; + } + + fft_buffer = srslte_vec_cf_malloc(sf_len); + if (!fft_buffer) { + perror("malloc"); + goto quit; + } + + if (srslte_chest_dl_nbiot_init(&chest, SRSLTE_NBIOT_MAX_PRB)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto quit; + } + if (srslte_chest_dl_nbiot_set_cell(&chest, cell) != SRSLTE_SUCCESS) { + fprintf(stderr, "Error setting equalizer cell configuration\n"); + goto quit; + } + + if (srslte_ofdm_rx_init(&fft, cell.base.cp, input_buffer, fft_buffer, cell.base.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + goto quit; + } + srslte_ofdm_set_freq_shift(&fft, SRSLTE_NBIOT_FREQ_SHIFT_FACTOR); + + if (srslte_npdcch_init(&npdcch)) { + fprintf(stderr, "Error creating NPDCCH object\n"); + goto quit; + } + + if (srslte_npdcch_set_cell(&npdcch, cell)) { + fprintf(stderr, "Error configuring NPDCCH object\n"); + goto quit; + } + + do { + nread = srslte_filesource_read(&fsrc, input_buffer, sf_len); + + if (nread == sf_len) { + // Run FFT and estimate channel + srslte_ofdm_rx_sf(&fft); + + INFO("%d.%d: Estimating channel.\n", frame_cnt, tti % 10); + srslte_chest_dl_nbiot_estimate(&chest, fft_buffer, ce, tti % 10); + + // Extract LLR + float noise_est = srslte_chest_dl_nbiot_get_noise_estimate(&chest); + if (srslte_npdcch_extract_llr(&npdcch, fft_buffer, ce, noise_est, tti % 10)) { + fprintf(stderr, "Error extracting LLRs\n"); + goto quit; + } + + uint16_t crc_rem = 0; + if (srslte_npdcch_decode_msg(&npdcch, &dci_rx, &dci_location, dci_format, &crc_rem)) { + fprintf(stderr, "Error decoding DCI message\n"); + goto quit; + } + if (crc_rem != rnti) { + printf("Received invalid DCI CRC 0x%x\n", crc_rem); + goto quit; + } else { + if (dci_format == SRSLTE_DCI_FORMATN0) { + // process as UL grant + srslte_ra_nbiot_ul_dci_t dci_unpacked; + srslte_ra_nbiot_ul_grant_t grant; + // Creates the UL NPUSCH resource allocation grant from a DCI format N0 message + if (srslte_nbiot_dci_msg_to_ul_grant(&dci_rx, &dci_unpacked, &grant, tti, SRSLTE_NPUSCH_SC_SPACING_15000)) { + fprintf(stderr, "Error unpacking DCI\n"); + goto quit; + } + } else { + // process as DL grant + srslte_ra_nbiot_dl_dci_t dci_unpacked; + srslte_ra_nbiot_dl_grant_t grant; + if (srslte_nbiot_dci_msg_to_dl_grant( + &dci_rx, rnti, &dci_unpacked, &grant, tti / 10, tti % 10, 64 /* TODO: remove */, cell.mode)) { + fprintf(stderr, "Error unpacking DCI\n"); + goto quit; + } + } + nof_decoded_dcis++; + } + + tti++; + if (tti == 10240) { + tti = 0; + frame_cnt++; + } + } + + } while (nread > 0 && frame_cnt < nof_frames); + +quit: + srslte_npdcch_free(&npdcch); + srslte_filesource_free(&fsrc); + free(input_buffer); + free(fft_buffer); + srslte_chest_dl_nbiot_free(&chest); + srslte_ofdm_rx_free(&fft); + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (ce[i]) { + free(ce[i]); + } + } + if (nof_decoded_dcis > 0) { + printf("Ok\n"); + ret = SRSLTE_SUCCESS; + } else { + printf("Error\n"); + } + + return ret; +} diff --git a/lib/src/phy/phch/test/npdcch_test.c b/lib/src/phy/phch/test/npdcch_test.c new file mode 100644 index 000000000..6e1429b2f --- /dev/null +++ b/lib/src/phy/phch/test/npdcch_test.c @@ -0,0 +1,261 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/dci_nbiot.h" +#include "srslte/phy/phch/npdcch.h" +#include "srslte/phy/phch/ra_nbiot.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define RNTI (0x1234) +#define HAVE_OFDM 0 + +srslte_dci_format_t dci_format = SRSLTE_DCI_FORMATN0; + +srslte_nbiot_cell_t cell = {.base = {.nof_prb = 1, .nof_ports = 1, .cp = SRSLTE_CP_NORM, .id = 0}, + .nbiot_prb = 0, + .n_id_ncell = 0, + .nof_ports = 1, + .mode = SRSLTE_NBIOT_MODE_STANDALONE}; + +void usage(char* prog) +{ + printf("Usage: %s [cpndv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.base.id); + printf("\t-o DCI Format [Default %s]\n", srslte_dci_format_string(dci_format)); + 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, "cpnov")) != -1) { + switch (opt) { + case 'p': + cell.base.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'n': + cell.base.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'c': + cell.base.id = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'o': + dci_format = srslte_dci_format_from_string(argv[optind]); + if (dci_format == SRSLTE_DCI_NOF_FORMATS) { + ERROR("Error unsupported format %s\n", argv[optind]); + exit(-1); + } + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + srslte_npdcch_t npdcch = {}; + srslte_dci_msg_t dci_tx = {}, dci_rx = {}; + srslte_dci_location_t dci_location = {}; + + uint32_t tti = 6521; + uint16_t rnti = 0x1234; + + cf_t* ce[SRSLTE_MAX_PORTS] = {NULL}; + cf_t* slot_symbols[SRSLTE_MAX_PORTS] = {NULL}; + int ret = SRSLTE_ERROR; + + parse_args(argc, argv); + + // we need to allocate RE's for a full 6 PRB cell + int nof_re = 6 * SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp); + + // init memory + for (int i = 0; i < SRSLTE_MAX_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; + } + slot_symbols[i] = malloc(sizeof(cf_t) * nof_re); + if (!slot_symbols[i]) { + perror("malloc"); + exit(-1); + } + } + +#if HAVE_OFDM + cf_t td_signal[nof_re * 2]; +#endif + + if (srslte_npdcch_init(&npdcch)) { + fprintf(stderr, "Error creating NPDCCH object\n"); + exit(-1); + } + + if (srslte_npdcch_set_cell(&npdcch, cell)) { + fprintf(stderr, "Error configuring NPDCCH object\n"); + exit(-1); + } + + if (dci_format == SRSLTE_DCI_FORMATN0) { + // UL grant + srslte_ra_nbiot_ul_dci_t dci = {}; + dci.format = dci_format; + + srslte_ra_nbiot_ul_grant_t grant; + if (srslte_ra_nbiot_ul_dci_to_grant(&dci, &grant, tti, SRSLTE_NPUSCH_SC_SPACING_15000)) { + fprintf(stderr, "Error converting DCI message\n"); + } + + fprintf(stderr, "FormatN0 packing not supported\n"); + return SRSLTE_ERROR; + } else if (dci_format == SRSLTE_DCI_FORMATN1) { + // DL grant + srslte_ra_nbiot_dl_dci_t ra_dl = {}; + ra_dl.mcs_idx = 5; + ra_dl.ndi = 0; + ra_dl.rv_idx = 0; + + // NB-IoT specific fields + ra_dl.alloc.has_sib1 = false; + ra_dl.alloc.is_ra = false; + ra_dl.alloc.i_delay = 4; + ra_dl.alloc.i_sf = 0; + ra_dl.alloc.i_rep = 0; + ra_dl.alloc.harq_ack = 1; + ra_dl.alloc.i_n_start = 0; + srslte_nbiot_dl_dci_fprint(stdout, &ra_dl); + + // pack DCI + srslte_dci_msg_pack_npdsch(&ra_dl, dci_format, &dci_tx, false); + srslte_dci_location_set(&dci_location, 2, 0); + } else { + fprintf(stderr, "FormatN2 packing not supported\n"); + return SRSLTE_ERROR; + } + + if (srslte_npdcch_encode(&npdcch, &dci_tx, dci_location, RNTI, slot_symbols, 0)) { + fprintf(stderr, "Error encoding DCI message\n"); + goto quit; + } + + // combine outputs + for (int i = 1; i < cell.base.nof_ports; i++) { + for (int j = 0; j < nof_re; j++) { + slot_symbols[0][j] += slot_symbols[i][j]; + } + } + +#if HAVE_OFDM + srslte_ofdm_t ofdm_tx; + srslte_ofdm_t ofdm_rx; + + if (srslte_ofdm_tx_init(&ofdm_tx, SRSLTE_CP_NORM, slot_symbols[0], td_signal, 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, td_signal, slot_symbols[0], cell.base.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return -1; + } + + // transfer into time doamin and back + srslte_ofdm_tx_sf(&ofdm_tx); + srslte_ofdm_rx_sf(&ofdm_rx); + + srslte_ofdm_tx_free(&ofdm_tx); + srslte_ofdm_rx_free(&ofdm_rx); +#endif + + if (srslte_npdcch_extract_llr(&npdcch, slot_symbols[0], ce, 0, 0)) { + fprintf(stderr, "Error extracting LLRs\n"); + goto quit; + } + uint16_t crc_rem = 0; + if (srslte_npdcch_decode_msg(&npdcch, &dci_rx, &dci_location, dci_format, &crc_rem)) { + fprintf(stderr, "Error decoding DCI message\n"); + goto quit; + } + if (crc_rem != RNTI) { + printf("Received invalid DCI CRC 0x%x\n", crc_rem); + goto quit; + } + + // compare DCIs + if (memcmp(dci_tx.payload, dci_rx.payload, dci_tx.nof_bits)) { + printf("Error in DCI: Received data does not match\n"); + goto quit; + } + + if (dci_format == SRSLTE_DCI_FORMATN0) { + // UL grant + // .. + } else { + // DL grant + srslte_ra_nbiot_dl_dci_t dci_unpacked; + srslte_ra_nbiot_dl_grant_t grant; + if (srslte_nbiot_dci_msg_to_dl_grant( + &dci_rx, rnti, &dci_unpacked, &grant, tti / 10, tti % 10, 64 /* fixme: remove */, cell.mode)) { + fprintf(stderr, "Error unpacking DCI\n"); + return SRSLTE_ERROR; + } + srslte_nbiot_dl_dci_fprint(stdout, &dci_unpacked); + } + + ret = SRSLTE_SUCCESS; + +quit: + srslte_npdcch_free(&npdcch); + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (ce[i]) { + free(ce[i]); + } + if (slot_symbols[i]) { + free(slot_symbols[i]); + } + } + if (ret) { + printf("Error\n"); + } else { + printf("Ok\n"); + } + exit(ret); +} diff --git a/lib/src/phy/rf/rf_utils.c b/lib/src/phy/rf/rf_utils.c index 6f8178635..b3a0c565b 100644 --- a/lib/src/phy/rf/rf_utils.c +++ b/lib/src/phy/rf/rf_utils.c @@ -33,6 +33,7 @@ #include "srslte/phy/rf/rf.h" #include "srslte/phy/rf/rf_utils.h" +#include "srslte/phy/ue/ue_cell_search_nbiot.h" int rf_rssi_scan(srslte_rf_t* rf, float* freqs, float* rssi, int nof_bands, double fs, int nsamp) { @@ -88,9 +89,9 @@ int srslte_rf_recv_wrapper_cs(void* h, cf_t* data[SRSLTE_MAX_PORTS], uint32_t ns return srslte_rf_recv_with_time_multi(h, ptr, nsamples, 1, NULL, NULL); } -double srslte_rf_set_rx_gain_th_wrapper(void* h, double f) +static SRSLTE_AGC_CALLBACK(srslte_rf_set_rx_gain_wrapper) { - return srslte_rf_set_rx_gain_th((srslte_rf_t*)h, f); + srslte_rf_set_rx_gain((srslte_rf_t*)h, gain_db); } /** This function is simply a wrapper to the ue_cell_search module for rf devices @@ -264,3 +265,82 @@ int rf_search_and_decode_mib(srslte_rf_t* rf, } return ret; } + +int rf_cell_search_nbiot(srslte_rf_t* rf, cell_search_cfg_t* config, srslte_nbiot_cell_t* cell, float* cfo) +{ + int ret = SRSLTE_ERROR; + srslte_ue_cellsearch_nbiot_t cs = {}; + srslte_nbiot_ue_cellsearch_result_t found_cells[3] = {}; + + if (srslte_ue_cellsearch_nbiot_init(&cs, config->max_frames_pss, srslte_rf_recv_wrapper_cs, (void*)rf)) { + fprintf(stderr, "Error initiating UE cell detect\n"); + return SRSLTE_ERROR; + } + + if (config->nof_valid_pss_frames) { + srslte_ue_cellsearch_nbiot_set_nof_valid_frames(&cs, config->nof_valid_pss_frames); + } + if (config->init_agc > 0) { + srslte_ue_sync_nbiot_start_agc(&cs.ue_sync, srslte_rf_set_rx_gain_wrapper, config->init_agc); + } + + DEBUG("Setting sampling frequency %.2f MHz for NPSS search\n", SRSLTE_CS_SAMP_FREQ / 1000000); + srslte_rf_set_rx_srate(rf, SRSLTE_CS_SAMP_FREQ); + + INFO("Starting receiver...\n"); + srslte_rf_start_rx_stream(rf, false); + + ret = srslte_ue_cellsearch_nbiot_scan(&cs); + if (ret == SRSLTE_ERROR) { + fprintf(stderr, "Could not find any cell in this frequency\n"); + goto clean_exit; + } + + INFO("Stopping receiver...\n"); + srslte_rf_stop_rx_stream(rf); + + // Find a cell + INFO("Running N_id_ncell detection\n"); + + uint32_t max_peak_cell = 0; + ret = srslte_ue_cellsearch_nbiot_detect(&cs, found_cells); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Could not detect cell ID\n"); + goto clean_exit; + } + + // Only show first cell + for (int i = 0; i < 1; i++) { + if (i == max_peak_cell) { + printf("*"); + } else { + printf(" "); + } + printf("Found n_id_ncell: %3d DetectRatio=%2.0f%% PSR=%.2f, Power=%.1f dBm\n", + found_cells[i].n_id_ncell, + found_cells[i].mode * 100, + found_cells[i].psr, + 20 * log10(found_cells[i].peak * 1000)); + } + + // Save result + if (cell) { + cell->n_id_ncell = found_cells[max_peak_cell].n_id_ncell; + } + + // Save CFO + if (cfo) { + *cfo = found_cells[max_peak_cell].cfo; + } + + // Save AGC value for MIB decoding + if (config->init_agc > 0) { + config->init_agc = srslte_agc_get_gain(&cs.ue_sync.agc); + } + +clean_exit: + srslte_rf_stop_rx_stream(rf); + srslte_ue_cellsearch_nbiot_free(&cs); + + return ret; +} diff --git a/lib/src/phy/ue/ue_sync_nbiot.c b/lib/src/phy/ue/ue_sync_nbiot.c index 8211291d5..927ebe28d 100644 --- a/lib/src/phy/ue/ue_sync_nbiot.c +++ b/lib/src/phy/ue/ue_sync_nbiot.c @@ -130,11 +130,9 @@ int srslte_ue_sync_nbiot_start_agc(srslte_nbiot_ue_sync_t* q, SRSLTE_AGC_CALLBACK(set_gain_callback), float init_gain_value) { - uint32_t nframes; + uint32_t nframes = 0; if (q->nof_recv_sf == 1) { nframes = 10; - } else { - nframes = 0; } int n = srslte_agc_init_uhd(&q->agc, SRSLTE_AGC_MODE_PEAK_AMPLITUDE, nframes, set_gain_callback, q->stream); q->do_agc = n == 0 ? true : false;