adding NB-IoT DL shared channel, UE DL object and PHY examples

master
Andre Puschmann 5 years ago
parent 890436e07f
commit e76e31e652

@ -35,6 +35,12 @@ if(RF_FOUND)
add_executable(pdsch_enodeb pdsch_enodeb.c)
target_link_libraries(pdsch_enodeb srslte_phy srslte_common srslte_rf pthread)
add_executable(npdsch_enodeb npdsch_enodeb.c)
target_link_libraries(npdsch_enodeb srslte_phy srslte_rf pthread)
add_executable(npdsch_ue npdsch_ue.c)
target_link_libraries(npdsch_ue srslte_common srslte_phy srslte_rf pthread)
else(RF_FOUND)
add_definitions(-DDISABLE_RF)
@ -43,11 +49,18 @@ else(RF_FOUND)
add_executable(pdsch_enodeb pdsch_enodeb.c)
target_link_libraries(pdsch_enodeb srslte_common srslte_phy pthread)
add_executable(npdsch_enodeb npdsch_enodeb.c)
target_link_libraries(npdsch_enodeb srslte_phy pthread)
add_executable(npdsch_ue npdsch_ue.c)
target_link_libraries(npdsch_ue srslte_common srslte_phy pthread)
endif(RF_FOUND)
if(SRSGUI_FOUND)
include_directories(${SRSGUI_INCLUDE_DIRS})
target_link_libraries(pdsch_ue ${SRSGUI_LIBRARIES})
target_link_libraries(npdsch_ue ${SRSGUI_LIBRARIES})
add_definitions(-DENABLE_GUI)
endif(SRSGUI_FOUND)
@ -92,3 +105,6 @@ if(RF_FOUND)
else(RF_FOUND)
message(STATUS " examples will NOT BE INSTALLED.")
endif(RF_FOUND)
# Add eNB/UE IQ test
add_subdirectory(test)

@ -0,0 +1,730 @@
/*
* Copyright 2013-2020 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 <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/select.h>
#include <unistd.h>
#include "srslte/phy/ch_estimation/chest_dl_nbiot.h"
#include "srslte/phy/channel/ch_awgn.h"
#include "srslte/phy/io/filesink.h"
#include "srslte/phy/io/filesource.h"
#include "srslte/phy/sync/npss.h"
#include "srslte/phy/sync/nsss.h"
#include "srslte/phy/ue/ue_dl_nbiot.h"
#include "srslte/phy/utils/bit.h"
#include "srslte/phy/utils/random.h"
#define UE_CRNTI 0x1234
#define HAVE_NPDSCH 1
#define NPDCCH_SF_IDX 1
const uint8_t dummy_sib1_payload[] = {0x43, 0x4d, 0xd0, 0x92, 0x22, 0x06, 0x04, 0x30, 0x28, 0x6e, 0x87, 0xd0, 0x4b,
0x13, 0x90, 0xb4, 0x12, 0xa1, 0x02, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
#ifndef DISABLE_RF
#include "srslte/phy/rf/rf.h"
srslte_rf_t rf;
#else
#warning Compiling npdsch_ue with no RF support
#endif
char* output_file_name = NULL;
srslte_nbiot_cell_t cell = {
.base = {.nof_ports = 1, .nof_prb = SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL, .cp = SRSLTE_CP_NORM, .id = 0},
.nbiot_prb = SRSLTE_NBIOT_DEFAULT_PRB_OFFSET,
.n_id_ncell = 0,
.nof_ports = 1,
.mode = SRSLTE_NBIOT_MODE_STANDALONE};
uint32_t i_tbs_val = 1, last_i_tbs_val = 1;
int nof_frames = -1;
uint32_t i_sf_val = 0;
uint32_t i_rep_val = 0;
char* rf_args = "";
float rf_amp = 0.8, rf_gain = 70.0, rf_freq = 0;
float snr = -100.0;
bool null_file_sink = false;
srslte_random_t* random_gen;
srslte_filesink_t fsink;
srslte_ofdm_t ifft;
srslte_npss_synch_t npss_sync;
srslte_nsss_synch_t nsss_sync;
srslte_npbch_t npbch;
srslte_npdcch_t npdcch;
srslte_npdsch_t npdsch;
srslte_npdsch_cfg_t sib1_npdsch_cfg;
srslte_npdsch_cfg_t npdsch_cfg;
srslte_nbiot_ue_dl_t ue_dl;
srslte_softbuffer_tx_t softbuffer;
srslte_ra_nbiot_dl_dci_t ra_dl;
srslte_ra_nbiot_dl_dci_t ra_dl_sib1;
srslte_chest_dl_nbiot_t est;
srslte_mib_nb_t mib_nb;
uint32_t sched_info_tag = 0; // according to Table 16.4.1.3-3 in 36.213, 0 means 4 NPDSCH repetitions with TBS 208
cf_t *sf_buffer = NULL, *output_buffer = NULL;
int sf_n_re = 0, sf_n_samples = 0;
void usage(char* prog)
{
printf("Usage: %s [agmiftleosncvrpu]\n", prog);
#ifndef DISABLE_RF
printf("\t-a RF args [Default %s]\n", rf_args);
printf("\t-e RF amplitude [Default %.2f]\n", rf_amp);
printf("\t-g RF TX gain [Default %.2f dB]\n", rf_gain);
printf("\t-f RF TX frequency [Default %.1f MHz]\n", rf_freq / 1000000);
#else
printf("\t RF is disabled.\n");
#endif
printf("\t-o output_file [Default use RF board]\n");
printf("\t-s SNR-10 (only if output to file) [Default %f]\n", snr);
printf("\t-t Value of schedulingInfoSIB1-NB-r13 [Default %d]\n", sched_info_tag);
printf("\t-m Value of i_tbs (translates to MCS) [Default %d]\n", i_tbs_val);
printf("\t-i Value of i_sf [Default %d]\n", i_sf_val);
printf("\t-r Value of i_rep [Default %d]\n", i_rep_val);
printf("\t-n number of frames [Default %d]\n", nof_frames);
printf("\t-l n_id_ncell [Default %d]\n", cell.n_id_ncell);
printf("\t-p NB-IoT PRB id [Default %d]\n", cell.nbiot_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, "aglfmiosncrtvpu")) != -1) {
switch (opt) {
case 'a':
rf_args = argv[optind];
break;
case 'g':
rf_gain = strtof(argv[optind], NULL);
break;
case 'e':
rf_amp = strtof(argv[optind], NULL);
break;
case 'f':
rf_freq = strtof(argv[optind], NULL);
break;
case 'o':
output_file_name = argv[optind];
break;
case 's':
snr = strtof(argv[optind], NULL);
break;
case 't':
sched_info_tag = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'm':
i_tbs_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'i':
i_sf_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
i_rep_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'n':
nof_frames = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'l':
cell.n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'p':
cell.nbiot_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
if (!output_file_name && rf_freq == 0) {
usage(argv[0]);
printf("\nError! Either RF frequency or output filename need to be specified.\n");
exit(-1);
}
#ifdef DISABLE_RF
if (!output_file_name) {
usage(argv[0]);
exit(-1);
}
#endif
}
void base_init()
{
// init memory
sf_buffer = srslte_vec_malloc(sizeof(cf_t) * sf_n_re);
if (!sf_buffer) {
perror("malloc");
exit(-1);
}
output_buffer = srslte_vec_malloc(sizeof(cf_t) * sf_n_samples);
if (!output_buffer) {
perror("malloc");
exit(-1);
}
// open file or USRP
if (output_file_name) {
if (strcmp(output_file_name, "NULL")) {
if (srslte_filesink_init(&fsink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) {
fprintf(stderr, "Error opening file %s\n", output_file_name);
exit(-1);
}
null_file_sink = false;
} else {
null_file_sink = true;
}
} else {
#ifndef DISABLE_RF
printf("Opening RF device...\n");
if (srslte_rf_open(&rf, rf_args)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
#else
printf("Error RF not available. Select an output file\n");
exit(-1);
#endif
}
if (srslte_ofdm_tx_init(&ifft, SRSLTE_CP_NORM, sf_buffer, output_buffer, cell.base.nof_prb)) {
fprintf(stderr, "Error creating iFFT object\n");
exit(-1);
}
srslte_ofdm_set_normalize(&ifft, true);
srslte_ofdm_set_freq_shift(&ifft, -SRSLTE_NBIOT_FREQ_SHIFT_FACTOR);
if (srslte_npss_synch_init(&npss_sync, sf_n_samples, srslte_symbol_sz(cell.base.nof_prb))) {
fprintf(stderr, "Error initializing NPSS object\n");
exit(-1);
}
if (srslte_nsss_synch_init(&nsss_sync, sf_n_samples, srslte_symbol_sz(cell.base.nof_prb))) {
fprintf(stderr, "Error initializing NSSS object\n");
exit(-1);
}
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 (srslte_npdcch_init(&npdcch)) {
fprintf(stderr, "Error creating NPDCCH object\n");
exit(-1);
}
if (srslte_npdcch_set_cell(&npdcch, cell)) {
fprintf(stderr, "Configuring cell in NPDCCH\n");
exit(-1);
}
if (srslte_npdsch_init(&npdsch)) {
fprintf(stderr, "Error creating NPDSCH object\n");
exit(-1);
}
if (srslte_npdsch_set_cell(&npdsch, cell)) {
fprintf(stderr, "Configuring cell in NPDSCH\n");
exit(-1);
}
srslte_npdsch_set_rnti(&npdsch, UE_CRNTI);
if (srslte_softbuffer_tx_init(&softbuffer, cell.base.nof_prb)) {
fprintf(stderr, "Error initiating soft buffer\n");
exit(-1);
}
random_gen = srslte_random_init(time(NULL));
}
void base_free()
{
srslte_random_free(random_gen);
srslte_softbuffer_tx_free(&softbuffer);
srslte_npdsch_free(&npdsch);
srslte_npdcch_free(&npdcch);
srslte_npbch_free(&npbch);
srslte_chest_dl_nbiot_free(&est);
srslte_npss_synch_free(&npss_sync);
srslte_nsss_synch_free(&nsss_sync);
srslte_ofdm_tx_free(&ifft);
if (sf_buffer) {
free(sf_buffer);
}
if (output_buffer) {
free(output_buffer);
}
if (output_file_name) {
if (!null_file_sink) {
srslte_filesink_free(&fsink);
}
} else {
#ifndef DISABLE_RF
srslte_rf_close(&rf);
#endif
}
}
bool go_exit = false;
#ifndef DISABLE_RF
void sig_int_handler(int signo)
{
printf("SIGINT received. Exiting...\n");
if (signo == SIGINT) {
go_exit = true;
}
}
#endif
int update_radl()
{
bzero(&ra_dl_sib1, sizeof(srslte_ra_nbiot_dl_dci_t));
// NB-IoT specific fields
ra_dl_sib1.alloc.has_sib1 = true;
ra_dl_sib1.alloc.sched_info_sib1 = mib_nb.sched_info_sib1;
ra_dl_sib1.mcs_idx = 1;
bzero(&ra_dl, sizeof(srslte_ra_nbiot_dl_dci_t));
ra_dl.mcs_idx = i_tbs_val;
// NB-IoT specific fields
ra_dl.format = 1; // FormatN1 DCI
ra_dl.alloc.has_sib1 = false;
ra_dl.alloc.is_ra = false;
ra_dl.alloc.i_delay = 0;
ra_dl.alloc.i_sf = i_sf_val;
ra_dl.alloc.i_rep = i_rep_val;
ra_dl.alloc.harq_ack = 1;
ra_dl.alloc.i_n_start = 0;
srslte_nbiot_dl_dci_fprint(stdout, &ra_dl);
srslte_ra_nbiot_dl_grant_t dummy_grant;
srslte_ra_nbits_t dummy_nbits;
#define DUMMY_SFIDX 1
#define DUMMY_SFN 0
srslte_ra_nbiot_dl_dci_to_grant(&ra_dl, &dummy_grant, DUMMY_SFN, DUMMY_SFIDX, DUMMY_R_MAX, false, cell.mode);
srslte_ra_nbiot_dl_grant_to_nbits(&dummy_grant, cell, 0, &dummy_nbits);
srslte_ra_nbiot_dl_grant_fprint(stdout, &dummy_grant);
printf("Type new MCS index and press Enter: ");
fflush(stdout);
return SRSLTE_SUCCESS;
}
/* Read new MCS from stdin */
int update_control()
{
char input[128];
fd_set set;
FD_ZERO(&set);
FD_SET(0, &set);
struct timeval to;
to.tv_sec = 0;
to.tv_usec = 0;
int n = select(1, &set, NULL, NULL, &to);
if (n == 1) {
// stdin ready
if (fgets(input, sizeof(input), stdin)) {
last_i_tbs_val = i_tbs_val;
i_tbs_val = atoi(input);
bzero(input, sizeof(input));
if (update_radl()) {
printf("Trying with last known MCS index\n");
i_tbs_val = last_i_tbs_val;
return update_radl();
}
}
return 0;
} else if (n < 0) {
// error
perror("select");
return -1;
} else {
return 0;
}
}
#define DATA_BUFF_SZ 1024 * 128
uint8_t data[8 * DATA_BUFF_SZ] = {};
uint8_t data2[DATA_BUFF_SZ] = {};
uint8_t data_tmp[DATA_BUFF_SZ] = {};
int main(int argc, char** argv)
{
int nf = 0, sf_idx = 0;
cf_t npss_signal[SRSLTE_NPSS_TOT_LEN];
cf_t nsss_signal[SRSLTE_NSSS_LEN * SRSLTE_NSSS_NUM_SEQ]; // for subframe 9
uint8_t bch_payload[SRSLTE_MIB_NB_LEN];
uint8_t sib1_nb_payload[SRSLTE_NPDSCH_MAX_TBS];
srslte_dci_msg_t dci_msg;
srslte_dci_location_t locations[SRSLTE_NOF_SF_X_FRAME][30];
uint32_t sfn = 0;
uint32_t hfn = 0; // Hyper frame number is incremented when sfn wraps and is also 10bit wide
#ifdef DISABLE_RF
if (argc < 3) {
usage(argv[0]);
exit(-1);
}
#endif
parse_args(argc, argv);
// adjust SNR input to allow for negative values
if (output_file_name && snr != -100.0) {
snr -= 10.0;
printf("Target SNR: %.2fdB\n", snr);
}
sf_n_re = 2 * SRSLTE_CP_NORM_NSYMB * cell.base.nof_prb * SRSLTE_NRE;
sf_n_samples = 2 * SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.base.nof_prb));
/* this *must* be called after setting slot_len_* */
base_init();
// buffer for outputting signal in RE representation (using sf_n_re)
cf_t* sf_re_symbols[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL};
sf_re_symbols[0] = sf_buffer;
// buffer for outputting actual IQ samples (using sf_n_samples)
cf_t* sf_symbols[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL};
sf_symbols[0] = output_buffer;
// construct MIB-NB
mib_nb.sched_info_sib1 = sched_info_tag;
mib_nb.sys_info_tag = 0;
mib_nb.ac_barring = false;
mib_nb.mode = SRSLTE_NBIOT_MODE_STANDALONE;
// Initialize UE DL
if (srslte_nbiot_ue_dl_init(&ue_dl, sf_symbols, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS)) {
fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1);
}
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Setting cell in UE DL\n");
return -1;
}
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib_nb);
/* Generate NPSS/NSSS signals */
srslte_npss_generate(npss_signal);
srslte_nsss_generate(nsss_signal, cell.n_id_ncell);
#ifdef NPSS_DUMP
srslte_filesink_t debug_fsink;
char fname[] = "npss.bin";
if (srslte_filesink_init(&debug_fsink, fname, SRSLTE_COMPLEX_FLOAT_BIN)) {
fprintf(stderr, "Error opening file %s\n", fname);
exit(-1);
}
srslte_filesink_write(&debug_fsink, npss_signal, SRSLTE_NPSS_LEN * 11);
srslte_filesink_free(&debug_fsink);
#endif
/* Generate CRS+NRS signals */
if (srslte_chest_dl_nbiot_init(&est, SRSLTE_NBIOT_MAX_PRB)) {
fprintf(stderr, "Error initializing equalizer\n");
exit(-1);
}
if (srslte_chest_dl_nbiot_set_cell(&est, cell) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error setting channel estimator's cell configuration\n");
return -1;
}
#ifndef DISABLE_RF
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
signal(SIGINT, sig_int_handler);
if (!output_file_name) {
int srate = srslte_sampling_freq_hz(cell.base.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srslte_rf_set_tx_srate(&rf, (double)srate);
if (srate_rf != srate) {
fprintf(stderr, "Could not set sampling rate\n");
exit(-1);
}
} else {
fprintf(stderr, "Invalid number of PRB %d\n", cell.base.nof_prb);
exit(-1);
}
printf("Set TX gain: %.1f dB\n", srslte_rf_set_tx_gain(&rf, rf_gain));
printf("Set TX freq: %.2f MHz\n", srslte_rf_set_tx_freq(&rf, 0, rf_freq) / 1000000);
}
#endif
if (update_radl(sf_idx)) {
exit(-1);
}
/* Initiate valid DCI locations */
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) {
locations[i][0].L = 1; // Agg-level 2, i.e. both NCEEs used
locations[i][0].ncce = 0;
}
nf = 0;
bool send_data = false;
bool npdsch_active = false;
srslte_softbuffer_tx_reset(&softbuffer);
bzero(&sib1_npdsch_cfg, sizeof(srslte_npdsch_cfg_t));
bzero(&npdsch_cfg, sizeof(srslte_npdsch_cfg_t));
#ifndef DISABLE_RF
bool start_of_burst = true;
#endif
while ((nf < nof_frames || nof_frames == -1) && !go_exit) {
for (sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME && (nf < nof_frames || nof_frames == -1); sf_idx++) {
srslte_vec_cf_zero(sf_buffer, sf_n_re);
// Transmit NPBCH in subframe 0
if (sf_idx == 0) {
if ((sfn % SRSLTE_NPBCH_NUM_FRAMES) == 0) {
srslte_npbch_mib_pack(hfn, sfn, mib_nb, bch_payload);
}
srslte_npbch_put_subframe(&npbch, bch_payload, sf_re_symbols, sfn);
if (SRSLTE_VERBOSE_ISDEBUG()) {
printf("MIB payload: ");
srslte_vec_fprint_hex(stdout, bch_payload, SRSLTE_MIB_NB_LEN);
}
}
// Transmit NPSS, NSSS and NRS
if (sf_idx == 5) {
// NPSS at subframe 5
srslte_npss_put_subframe(&npss_sync, npss_signal, sf_buffer, cell.base.nof_prb, cell.nbiot_prb);
} else if ((sfn % 2 == 0) && sf_idx == 9) {
// NSSS in every even numbered frame at subframe 9
srslte_nsss_put_subframe(&nsss_sync, nsss_signal, sf_buffer, sfn, cell.base.nof_prb, cell.nbiot_prb);
} else {
// NRS in all other subframes (using CSR signal intentionally)
// DEBUG("%d.%d: Putting %d NRS pilots\n", sfn, sf_idx, SRSLTE_REFSIGNAL_NUM_SF(1, cell.nof_ports));
srslte_refsignal_nrs_put_sf(cell, 0, est.nrs_signal.pilots[0][sf_idx], sf_buffer);
}
#if HAVE_NPDSCH
// only transmit in subframes not used for NPBCH, NPSS or NSSS and only use subframe 4 when there is no SIB1
// transmission
if (sf_idx != 0 && sf_idx != 5 && (!(sf_idx == 9 && sfn % 2 == 0)) &&
!srslte_nbiot_ue_dl_is_sib1_sf(&ue_dl, sfn, sf_idx)) {
send_data = true;
} else {
send_data = false;
}
// SIB1-NB content
if (hfn % 4 == 0 && sfn == 0 && sf_idx == 0) {
// copy captured SIB1
memcpy(sib1_nb_payload, dummy_sib1_payload, sizeof(dummy_sib1_payload));
// overwrite Hyper Frame Number (HFN), 8 MSB
uint8_t unpacked_hfn[4 * 8];
srslte_bit_unpack_vector(sib1_nb_payload, unpacked_hfn, 4 * 8);
uint8_t* tmp = unpacked_hfn;
tmp += 12;
srslte_bit_unpack(hfn >> 2, &tmp, 8);
uint8_t packed_hfn[4];
srslte_bit_pack_vector(unpacked_hfn, packed_hfn, 32);
memcpy(sib1_nb_payload, packed_hfn, sizeof(packed_hfn));
if (SRSLTE_VERBOSE_ISDEBUG()) {
printf("SIB1-NB payload: ");
srslte_vec_fprint_byte(stdout, sib1_nb_payload, sizeof(dummy_sib1_payload));
}
}
if (srslte_nbiot_ue_dl_is_sib1_sf(&ue_dl, sfn, sf_idx)) {
INFO("%d.%d: Transmitting SIB1-NB.\n", sfn, sf_idx);
assert(send_data == false);
// configure DL grant for SIB1-NB transmission
if (sib1_npdsch_cfg.sf_idx == 0) {
srslte_ra_nbiot_dl_grant_t grant;
srslte_ra_nbiot_dl_dci_to_grant(
&ra_dl_sib1, &grant, sfn, sf_idx, DUMMY_R_MAX, true, cell.mode);
if (srslte_npdsch_cfg(&sib1_npdsch_cfg, cell, &grant, sf_idx)) {
fprintf(stderr, "Error configuring NPDSCH\n");
exit(-1);
}
}
// Encode SIB1 content
if (srslte_npdsch_encode_rnti(
&npdsch, &sib1_npdsch_cfg, &softbuffer, sib1_nb_payload, SRSLTE_SIRNTI, sf_re_symbols)) {
fprintf(stderr, "Error encoding NPDSCH\n");
exit(-1);
}
if (sib1_npdsch_cfg.sf_idx == sib1_npdsch_cfg.grant.nof_sf) {
bzero(&sib1_npdsch_cfg, sizeof(srslte_npdsch_cfg_t));
}
}
// Update DL resource allocation from control port
if (update_control(sf_idx)) {
fprintf(stderr, "Error updating parameters from control port\n");
}
if (send_data) {
// always transmit NPDCCH on fixed positions if no transmission is going on
if (sf_idx == NPDCCH_SF_IDX && !npdsch_active) {
// Encode NPDCCH
INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L);
srslte_dci_msg_pack_npdsch(&ra_dl, SRSLTE_DCI_FORMATN1, &dci_msg, false);
if (srslte_npdcch_encode(&npdcch, &dci_msg, locations[sf_idx][0], UE_CRNTI, sf_re_symbols, sf_idx)) {
fprintf(stderr, "Error encoding DCI message\n");
exit(-1);
}
// Configure NPDSCH accordingly
srslte_ra_nbiot_dl_grant_t grant;
srslte_ra_nbiot_dl_dci_to_grant(&ra_dl, &grant, sfn, sf_idx, DUMMY_R_MAX, false, cell.mode);
if (srslte_npdsch_cfg(&npdsch_cfg, cell, &grant, sf_idx)) {
fprintf(stderr, "Error configuring NPDSCH\n");
exit(-1);
}
}
// catch start of "user" NPDSCH
if (!npdsch_active && (sf_idx == npdsch_cfg.grant.start_sfidx && sfn == npdsch_cfg.grant.start_sfn)) {
// generate data only in first sf
INFO("%d.%d: Generating %d random bits\n", sfn, sf_idx, npdsch_cfg.grant.mcs[0].tbs);
for (int i = 0; i < npdsch_cfg.grant.mcs[0].tbs / 8; i++) {
data[i] = srslte_random_uniform_int_dist(random_gen, 0, 255);
}
if (SRSLTE_VERBOSE_ISDEBUG()) {
printf("Tx payload: ");
srslte_vec_fprint_b(stdout, data, npdsch_cfg.grant.mcs[0].tbs / 8);
}
npdsch_active = true;
}
if (npdsch_active) {
DEBUG("Current sf_idx=%d, Encoding npdsch.sf_idx=%d start=%d, nof=%d\n",
sf_idx,
npdsch_cfg.sf_idx,
npdsch_cfg.grant.start_sfidx,
npdsch_cfg.grant.nof_sf);
// Encode NPDSCH
if (srslte_npdsch_encode(&npdsch, &npdsch_cfg, &softbuffer, data, sf_re_symbols)) {
fprintf(stderr, "Error encoding NPDSCH\n");
exit(-1);
}
if (npdsch_cfg.num_sf == npdsch_cfg.grant.nof_sf * npdsch_cfg.grant.nof_rep) {
INFO("Deactive current NPDSCH\n");
npdsch_active = false;
}
}
}
#endif
/* Transform to OFDM symbols */
srslte_ofdm_tx_sf(&ifft);
if (output_file_name && snr != -100.0) {
// compute average energy per symbol
float abs[sf_n_samples];
srslte_vec_abs_square_cf(output_buffer, abs, sf_n_samples);
float abs_avg = 0;
for (int i = 0; i < sf_n_samples; i++) {
abs_avg += abs[i];
}
abs_avg /= sf_n_samples;
// find the noise spectral density
float snr_lin = powf(10.0f, snr / 10);
float n0 = abs_avg / snr_lin;
float nstd = sqrtf(n0 / 2);
// add some noise to the signal
srslte_ch_awgn_c(output_buffer, output_buffer, nstd, sf_n_samples);
}
/* send to file or usrp */
if (output_file_name) {
// write to file
if (!null_file_sink) {
srslte_filesink_write(&fsink, output_buffer, sf_n_samples);
}
usleep(1000);
} else {
#ifndef DISABLE_RF
// FIXME: output scaling needed?
srslte_rf_send2(&rf, output_buffer, sf_n_samples, true, start_of_burst, false);
start_of_burst = false;
#endif
}
}
nf++;
sfn++;
if (sfn >= 1024) {
sfn = 0;
hfn = (hfn + 1) % 1024;
printf("NB-IoT HFN: %d\n", hfn);
}
}
base_free();
printf("Done\n");
return SRSLTE_SUCCESS;
}

@ -0,0 +1,999 @@
/*
* Copyright 2013-2020 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 <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#include "srslte/phy/ch_estimation/chest_dl_nbiot.h"
#include "srslte/phy/channel/ch_awgn.h"
#include "srslte/phy/io/filesink.h"
#include "srslte/phy/io/filesource.h"
#include "srslte/phy/ue/ue_dl_nbiot.h"
#include "srslte/phy/ue/ue_mib_nbiot.h"
#include "srslte/phy/ue/ue_sync_nbiot.h"
#include "srslte/phy/utils/bit.h"
#undef ENABLE_AGC_DEFAULT
#ifndef DISABLE_RF
#include "srslte/phy/rf/rf.h"
#include "srslte/phy/rf/rf_utils.h"
#define ENABLE_MANUAL_NSSS_SEARCH 0
#define HAVE_PCAP 1
#if HAVE_PCAP
#include "srslte/common/pcap.h"
#endif
cell_search_cfg_t cell_detect_config = {.max_frames_pbch = SRSLTE_DEFAULT_MAX_FRAMES_NPBCH,
.max_frames_pss = SRSLTE_DEFAULT_MAX_FRAMES_NPSS,
.nof_valid_pss_frames = SRSLTE_DEFAULT_NOF_VALID_NPSS_FRAMES,
.init_agc = 0,
.force_tdd = false};
#else
#warning Compiling npdsch_ue with no RF support
#endif
#ifdef ENABLE_GUI
#include "srsgui/srsgui.h"
void init_plots();
pthread_t plot_thread;
sem_t plot_sem;
uint32_t plot_sf_idx = 0;
bool plot_track = true;
#endif // ENABLE_GUI
#define PRINT_CHANGE_SCHEDULIGN
#define NPSS_FIND_PLOT_WIDTH 80
//#define CORRECT_SAMPLE_OFFSET
/**********************************************************************
* Program arguments processing
***********************************************************************/
typedef struct {
int nof_subframes;
bool disable_plots;
bool disable_plots_except_constellation;
bool disable_cfo;
uint32_t time_offset;
int n_id_ncell;
bool is_r14;
bool decode_sib2;
int sib2_periodicity;
int sib2_radio_frame_offset;
int sib2_repetition_pattern;
int sib2_tb;
int sib2_window_length;
uint16_t rnti;
char* input_file_name;
int file_offset_time;
float file_offset_freq;
uint32_t file_nof_prb;
uint32_t file_nof_ports;
uint32_t file_cell_id;
char* rf_args;
double rf_freq;
float rf_gain;
} prog_args_t;
void args_default(prog_args_t* args)
{
args->disable_plots = false;
args->disable_plots_except_constellation = false;
args->nof_subframes = -1;
args->rnti = SRSLTE_SIRNTI;
args->n_id_ncell = SRSLTE_CELL_ID_UNKNOWN;
args->is_r14 = false;
args->decode_sib2 = false;
args->sib2_periodicity = 512;
args->sib2_radio_frame_offset = 0;
args->sib2_repetition_pattern = 1;
args->sib2_tb = 328;
args->sib2_window_length = 960;
args->input_file_name = NULL;
args->disable_cfo = false;
args->time_offset = 0;
args->file_nof_prb = 1;
args->file_nof_ports = 0;
args->file_cell_id = 0;
args->file_offset_time = 0;
args->file_offset_freq = 0;
args->rf_args = "";
args->rf_freq = -1.0;
#ifdef ENABLE_AGC_DEFAULT
args->rf_gain = -1.0;
#else
args->rf_gain = 70.0;
#endif
}
void usage(prog_args_t* args, char* prog)
{
printf("Usage: %s [agpRPoOildtDnrBuHvqwzxc] -f rx_frequency (in Hz) | -i input_file\n", prog);
#ifndef DISABLE_RF
printf("\t-a RF args [Default %s]\n", args->rf_args);
#ifdef ENABLE_AGC_DEFAULT
printf("\t-g RF fix RX gain [Default AGC]\n");
#else
printf("\t-g Set RX gain [Default %.1f dB]\n", args->rf_gain);
#endif
#else
printf("\t RF is disabled.\n");
#endif
printf("\t-i input_file [Default use RF board]\n");
printf("\t-o offset frequency correction (in Hz) for input file [Default %.1f Hz]\n", args->file_offset_freq);
printf("\t-O offset samples for input file [Default %d]\n", args->file_offset_time);
printf("\t-p nof_prb for input file [Default %d]\n", args->file_nof_prb);
printf("\t-P nof_ports for input file [Default %d]\n", args->file_nof_ports);
printf("\t-r RNTI in Hex [Default 0x%x]\n", args->rnti);
printf("\t-l n_id_ncell [Default %d]\n", args->n_id_ncell);
printf("\t-R Is R14 cell [Default %s]\n", args->is_r14 ? "Yes" : "No");
printf("\t-B Decode SIB2 [Default %s]\n", args->decode_sib2 ? "Yes" : "No");
printf("\t-q SIB2 periodicity [Default %d]\n", args->sib2_periodicity);
printf("\t-w SIB2 radio frame offset [Default %d]\n", args->sib2_radio_frame_offset);
printf("\t-z SIB2 repetions pattern [Default %d]\n", args->sib2_repetition_pattern);
printf("\t-x SIB2 TB size [Default %d]\n", args->sib2_tb);
printf("\t-c SIB2 window length [Default %d]\n", args->sib2_window_length);
printf("\t-C Disable CFO correction [Default %s]\n", args->disable_cfo ? "Disabled" : "Enabled");
printf("\t-t Add time offset [Default %d]\n", args->time_offset);
#ifdef ENABLE_GUI
printf("\t-d disable plots [Default enabled]\n");
printf("\t-D disable all but constellation plots [Default enabled]\n");
#else
printf("\t plots are disabled. Graphics library not available\n");
#endif // ENABLE_GUI
printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes);
printf("\t-v [set srslte_verbose to debug, default none]\n");
}
void parse_args(prog_args_t* args, int argc, char** argv)
{
int opt;
args_default(args);
while ((opt = getopt(argc, argv, "aogRBlipHPOCtdDnvrfqwzxc")) != -1) {
switch (opt) {
case 'i':
args->input_file_name = argv[optind];
break;
case 'p':
args->file_nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'P':
args->file_nof_ports = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'o':
args->file_offset_freq = strtof(argv[optind], NULL);
break;
case 'O':
args->file_offset_time = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'a':
args->rf_args = argv[optind];
break;
case 'g':
args->rf_gain = strtof(argv[optind], NULL);
break;
case 'C':
args->disable_cfo = true;
break;
case 'B':
args->decode_sib2 = true;
break;
case 'q':
args->sib2_periodicity = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'w':
args->sib2_radio_frame_offset = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'z':
args->sib2_repetition_pattern = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'x':
args->sib2_tb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'c':
args->sib2_window_length = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 't':
args->time_offset = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'f':
args->rf_freq = strtod(argv[optind], NULL);
break;
case 'n':
args->nof_subframes = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
args->rnti = strtol(argv[optind], NULL, 16);
break;
case 'l':
args->n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'R':
args->is_r14 = true;
break;
case 'd':
args->disable_plots = true;
break;
case 'D':
args->disable_plots_except_constellation = true;
break;
case 'v':
srslte_verbose++;
break;
default:
usage(args, argv[0]);
exit(-1);
}
}
if (args->rf_freq < 0 && args->input_file_name == NULL) {
usage(args, argv[0]);
exit(-1);
}
}
/**********************************************************************/
static uint8_t data[20000]; // Byte buffer for rx'ed transport blocks
bool go_exit = false;
void sig_int_handler(int signo)
{
printf("SIGINT received. Exiting...\n");
if (signo == SIGINT) {
go_exit = true;
}
}
#if HAVE_PCAP
void pcap_pack_and_write(FILE* pcap_file,
uint8_t* pdu,
uint32_t pdu_len_bytes,
uint32_t reTX,
bool crc_ok,
uint32_t tti,
uint16_t crnti,
uint8_t direction,
uint8_t rnti_type)
{
MAC_Context_Info_t context = {.radioType = FDD_RADIO,
.direction = direction,
.rntiType = rnti_type,
.rnti = crnti,
.ueid = 1,
.isRetx = reTX,
.crcStatusOK = crc_ok,
.sysFrameNumber = tti / 10,
.subFrameNumber = tti % 10,
.nbiotMode = 1};
if (pdu) {
LTE_PCAP_MAC_WritePDU(pcap_file, &context, pdu, pdu_len_bytes);
}
}
#endif
#ifndef DISABLE_RF
int srslte_rf_recv_wrapper(void* h, void* data, uint32_t nsamples, srslte_timestamp_t* t)
{
DEBUG(" ---- Receive %d samples ---- \n", nsamples);
return srslte_rf_recv_with_time(h, data, nsamples, true, &t->full_secs, &t->frac_secs);
}
void srslte_rf_set_rx_gain_th_wrapper_(void* h, float f)
{
srslte_rf_set_rx_gain_th((srslte_rf_t*)h, f);
}
#endif
extern float mean_exec_time;
enum receiver_state { DECODE_MIB, DECODE_SIB, DECODE_NPDSCH } state;
srslte_nbiot_ue_dl_t ue_dl;
srslte_nbiot_ue_sync_t ue_sync;
prog_args_t prog_args;
bool have_sib1 = false;
bool have_sib2 = false;
uint32_t sfn = 0; // system frame number
uint32_t hfn = 0; // Hyper frame number
#define RSRP_TABLE_MAX_IDX 1024
float rsrp_table[RSRP_TABLE_MAX_IDX];
uint32_t rsrp_table_index = 0;
uint32_t rsrp_num_plot = RSRP_TABLE_MAX_IDX;
int main(int argc, char** argv)
{
int ret;
srslte_nbiot_cell_t cell = {};
int64_t sf_cnt;
srslte_ue_mib_nbiot_t ue_mib;
#ifndef DISABLE_RF
srslte_rf_t rf;
#endif
uint32_t nof_trials = 0;
int n;
uint8_t bch_payload[SRSLTE_MIB_NB_LEN] = {};
int sfn_offset;
float cfo = 0;
parse_args(&prog_args, argc, argv);
#if HAVE_PCAP
FILE* pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, "/tmp/npdsch.pcap");
#endif
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
signal(SIGINT, sig_int_handler);
cell.base.nof_prb = SRSLTE_NBIOT_DEFAULT_NUM_PRB_BASECELL;
cell.nbiot_prb = SRSLTE_NBIOT_DEFAULT_PRB_OFFSET;
cell.n_id_ncell = prog_args.n_id_ncell;
cell.is_r14 = prog_args.is_r14;
#ifndef DISABLE_RF
if (!prog_args.input_file_name) {
printf("Opening RF device...\n");
if (srslte_rf_open(&rf, prog_args.rf_args)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
/* Set receiver gain */
if (prog_args.rf_gain > 0) {
printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, prog_args.rf_gain));
} else {
printf("Starting AGC thread...\n");
if (srslte_rf_start_gain_thread(&rf, false)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
srslte_rf_set_rx_gain(&rf, 50);
cell_detect_config.init_agc = 50;
}
// set transceiver frequency
printf("Set RX freq: %.6f MHz\n", srslte_rf_set_rx_freq(&rf, 0, prog_args.rf_freq) / 1000000);
// set sampling frequency
int srate = srslte_sampling_freq_hz(cell.base.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srslte_rf_set_rx_srate(&rf, (double)srate);
if (srate_rf != srate) {
fprintf(stderr, "Could not set sampling rate\n");
exit(-1);
}
} else {
fprintf(stderr, "Invalid number of PRB %d\n", cell.base.nof_prb);
exit(-1);
}
INFO("Stopping RF and flushing buffer...\r");
srslte_rf_stop_rx_stream(&rf);
#if ENABLE_MANUAL_NSSS_SEARCH
// determine n_id_ncell
if (prog_args.n_id_ncell == SRSLTE_CELL_ID_UNKNOWN) {
srslte_nsss_synch_t nsss;
float nsss_peak_value;
int input_len = srate * 10 / 1000 * 2; // capture two full frames to make sure we have one NSSS
cf_t* buffer = malloc(sizeof(cf_t) * input_len * 2);
if (!buffer) {
perror("malloc");
exit(-1);
}
if (srslte_nsss_synch_init(&nsss, cell, input_len, srate / 15000)) {
fprintf(stderr, "Error initializing NSSS object\n");
exit(-1);
}
srslte_rf_start_rx_stream(&rf);
n = srslte_rf_recv(&rf, buffer, input_len, 1);
if (n != input_len) {
fprintf(stderr, "Error receiving samples\n");
exit(-1);
}
srslte_rf_stop_rx_stream(&rf);
// trying to find NSSS
printf("Detecting NSSS signal .. ");
fflush(stdout);
uint32_t sfn_partial;
srslte_nsss_sync_find(&nsss, buffer, &nsss_peak_value, (int*)&cell.n_id_ncell, &sfn_partial);
printf("done!");
srslte_nsss_synch_free(&nsss);
free(buffer);
} else {
cell.n_id_ncell = prog_args.n_id_ncell;
}
printf("\nSetting n_id_ncell to %d.\n", cell.n_id_ncell);
#else
if (cell.n_id_ncell == SRSLTE_CELL_ID_UNKNOWN) {
uint32_t ntrial = 0;
do {
ret = rf_cell_search_nbiot(&rf, &cell_detect_config, &cell, &cfo);
if (ret != SRSLTE_SUCCESS) {
printf("Cell not found after %d trials. Trying again (Press Ctrl+C to exit)\n", ntrial++);
}
} while (ret != SRSLTE_SUCCESS && !go_exit);
}
#endif
if (go_exit) {
exit(0);
}
}
#endif
/* If reading from file, go straight to PDSCH decoding. Otherwise, decode MIB first */
if (prog_args.input_file_name) {
// set file specific params
cell.base.nof_ports = prog_args.file_nof_ports;
cell.nof_ports = prog_args.file_nof_ports;
if (srslte_ue_sync_nbiot_init_file(
&ue_sync, cell, prog_args.input_file_name, prog_args.file_offset_time, prog_args.file_offset_freq)) {
fprintf(stderr, "Error initiating ue_sync\n");
exit(-1);
}
} else {
#ifndef DISABLE_RF
if (srslte_ue_sync_nbiot_init(&ue_sync, cell, srslte_rf_recv_wrapper, (void*)&rf)) {
fprintf(stderr, "Error initiating ue_sync\n");
exit(-1);
}
// reduce AGC period to every 10th frame
srslte_ue_sync_nbiot_set_agc_period(&ue_sync, 10);
#endif
}
// Allocate memory to fit a full frame (needed for time re-alignment)
cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {NULL};
buff_ptrs[0] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_PRB_NBIOT * 10);
if (srslte_ue_mib_nbiot_init(&ue_mib, buff_ptrs, SRSLTE_NBIOT_MAX_PRB)) {
fprintf(stderr, "Error initaiting UE MIB decoder\n");
exit(-1);
}
if (srslte_ue_mib_nbiot_set_cell(&ue_mib, cell) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error setting cell configuration in UE MIB decoder\n");
exit(-1);
}
// configure SIB2-NB parameters
srslte_nbiot_si_params_t sib2_params;
sib2_params.n = 1;
sib2_params.si_periodicity = prog_args.sib2_periodicity;
sib2_params.si_radio_frame_offset = prog_args.sib2_radio_frame_offset;
sib2_params.si_repetition_pattern = prog_args.sib2_repetition_pattern;
sib2_params.si_tb = prog_args.sib2_tb;
sib2_params.si_window_length = prog_args.sib2_window_length;
/* Initialize subframe counter */
sf_cnt = 0;
#ifdef ENABLE_GUI
if (!prog_args.disable_plots) {
init_plots(cell);
}
#endif // ENABLE_GUI
#ifndef DISABLE_RF
if (!prog_args.input_file_name) {
srslte_rf_start_rx_stream(&rf, false);
}
#endif
// Variables for measurements
uint32_t nframes = 0;
float rsrp = 0.0, rsrq = 0.0, noise = 0.0;
bzero(&rsrp_table, sizeof(float) * RSRP_TABLE_MAX_IDX);
#ifndef DISABLE_RF
if (prog_args.rf_gain < 0) {
srslte_ue_sync_nbiot_start_agc(&ue_sync, srslte_rf_set_rx_gain_th_wrapper_, cell_detect_config.init_agc);
}
#endif
#ifdef PRINT_CHANGE_SCHEDULIGN
srslte_ra_nbiot_dl_dci_t old_dl_dci;
bzero(&old_dl_dci, sizeof(srslte_ra_nbiot_dl_dci_t));
#endif
ue_sync.correct_cfo = !prog_args.disable_cfo;
// Set initial CFO for ue_sync
srslte_ue_sync_nbiot_set_cfo(&ue_sync, cfo);
srslte_npbch_decode_reset(&ue_mib.npbch);
INFO("\nEntering main loop...\n\n");
while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) {
ret = srslte_ue_sync_nbiot_zerocopy_multi(&ue_sync, buff_ptrs);
if (ret < 0) {
fprintf(stderr, "Error calling srslte_nbiot_ue_sync_zerocopy_multi()\n");
break;
}
#ifdef CORRECT_SAMPLE_OFFSET
float sample_offset =
(float)srslte_ue_sync_get_last_sample_offset(&ue_sync) + srslte_ue_sync_get_sfo(&ue_sync) / 1000;
srslte_ue_dl_set_sample_offset(&ue_dl, sample_offset);
#endif
// srslte_ue_sync_nbiot_zerocopy_multi() returns 1 if successfully read 1 aligned subframe
if (ret == 1) {
switch (state) {
case DECODE_MIB:
if (srslte_ue_sync_nbiot_get_sfidx(&ue_sync) == 0) {
n = srslte_ue_mib_nbiot_decode(&ue_mib, buff_ptrs[0], bch_payload, &cell.nof_ports, &sfn_offset);
if (n < 0) {
fprintf(stderr, "Error decoding UE MIB\n");
exit(-1);
} else if (n == SRSLTE_UE_MIB_FOUND) {
printf("MIB received (CFO: %+6.2f kHz)\n", srslte_ue_sync_nbiot_get_cfo(&ue_sync) / 1000);
srslte_mib_nb_t mib;
srslte_npbch_mib_unpack(bch_payload, &mib);
// update SFN and set deployment mode
sfn = (mib.sfn + sfn_offset) % 1024;
cell.mode = mib.mode;
// set number of ports of base cell to that of NB-IoT cell (FIXME: read eutra-NumCRS-Ports-r13)
cell.base.nof_ports = cell.nof_ports;
if (cell.mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI) {
cell.base.id = cell.n_id_ncell;
}
if (SRSLTE_VERBOSE_ISINFO()) {
srslte_mib_nb_printf(stdout, cell, &mib);
}
// Initialize DL
if (srslte_nbiot_ue_dl_init(&ue_dl, buff_ptrs, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS)) {
fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1);
}
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Configuring cell in UE DL\n");
exit(-1);
}
// Configure downlink receiver with the MIB params and the RNTI we use
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib);
srslte_nbiot_ue_dl_set_rnti(&ue_dl, prog_args.rnti);
#if HAVE_PCAP
// write to PCAP
srslte_bit_pack_vector(bch_payload, data, SRSLTE_MIB_NB_CRC_LEN);
pcap_pack_and_write(
pcap_file, data, SRSLTE_MIB_NB_CRC_LEN, 0, true, sfn * 10, 0, DIRECTION_DOWNLINK, NO_RNTI);
#endif
// activate SIB1 decoding
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
state = DECODE_SIB;
}
}
break;
case DECODE_SIB:
if (!have_sib1) {
int dec_ret = srslte_nbiot_ue_dl_decode_npdsch(&ue_dl,
&buff_ptrs[0][prog_args.time_offset],
data,
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
SRSLTE_SIRNTI);
if (dec_ret == SRSLTE_SUCCESS) {
printf("SIB1 received.\n");
have_sib1 = true;
#if HAVE_PCAP
pcap_pack_and_write(pcap_file,
data,
ue_dl.npdsch_cfg.grant.mcs[0].tbs / 8,
0,
true,
sfn * 10 + srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
SRSLTE_SIRNTI,
DIRECTION_DOWNLINK,
SI_RNTI);
#endif
// active SIB2 decoding if set
if (prog_args.decode_sib2) {
srslte_nbiot_ue_dl_decode_sib(&ue_dl, hfn, sfn, SRSLTE_NBIOT_SI_TYPE_SIB2, sib2_params);
} else {
have_sib2 = true;
}
// if SIB1 was decoded in this subframe, skip processing it further
break;
} else if (dec_ret == SRSLTE_ERROR) {
// reactivate SIB1 grant
if (srslte_nbiot_ue_dl_has_grant(&ue_dl) == false) {
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
}
}
} else if (!have_sib2 &&
!srslte_nbiot_ue_dl_is_sib1_sf(&ue_dl, sfn, srslte_ue_sync_nbiot_get_sfidx(&ue_sync))) {
// SIB2 is transmitted over multiple subframes, so this needs to be called more than once ..
int dec_ret = srslte_nbiot_ue_dl_decode_npdsch(&ue_dl,
&buff_ptrs[0][prog_args.time_offset],
data,
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
SRSLTE_SIRNTI);
if (dec_ret == SRSLTE_SUCCESS) {
printf("SIB2 received.\n");
have_sib2 = true;
#if HAVE_PCAP
pcap_pack_and_write(pcap_file,
data,
ue_dl.npdsch_cfg.grant.mcs[0].tbs / 8,
0,
true,
sfn * 10 + srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
SRSLTE_SIRNTI,
DIRECTION_DOWNLINK,
SI_RNTI);
#endif
} else {
// reactivate SIB2 grant
if (srslte_nbiot_ue_dl_has_grant(&ue_dl) == false) {
srslte_nbiot_ue_dl_decode_sib(&ue_dl, hfn, sfn, SRSLTE_NBIOT_SI_TYPE_SIB2, sib2_params);
}
}
}
if (have_sib1 && have_sib2) {
if (prog_args.rnti == SRSLTE_SIRNTI)
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
state = DECODE_NPDSCH;
}
break;
case DECODE_NPDSCH:
if (prog_args.rnti != SRSLTE_SIRNTI) {
if (srslte_nbiot_ue_dl_has_grant(&ue_dl)) {
// attempt to decode NPDSCH
n = srslte_nbiot_ue_dl_decode_npdsch(&ue_dl,
&buff_ptrs[0][prog_args.time_offset],
data,
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
prog_args.rnti);
if (n == SRSLTE_SUCCESS) {
INFO("NPDSCH decoded ok.\n");
}
} else {
// decode NPDCCH
srslte_dci_msg_t dci_msg;
n = srslte_nbiot_ue_dl_decode_npdcch(&ue_dl,
&buff_ptrs[0][prog_args.time_offset],
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
prog_args.rnti,
&dci_msg);
if (n == SRSLTE_NBIOT_UE_DL_FOUND_DCI) {
INFO("DCI found for rnti=%d\n", prog_args.rnti);
// convert DCI to 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_msg,
prog_args.rnti,
&dci_unpacked,
&grant,
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
64 /* fixme: remove */,
cell.mode)) {
fprintf(stderr, "Error unpacking DCI\n");
return SRSLTE_ERROR;
}
// activate grant
srslte_nbiot_ue_dl_set_grant(&ue_dl, &grant);
}
}
} else {
// decode SIB1 continously
n = srslte_nbiot_ue_dl_decode_npdsch(&ue_dl,
&buff_ptrs[0][prog_args.time_offset],
data,
sfn,
srslte_ue_sync_nbiot_get_sfidx(&ue_sync),
prog_args.rnti);
// reactivate SIB1 grant
if (srslte_nbiot_ue_dl_has_grant(&ue_dl) == false) {
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
}
}
nof_trials++;
rsrq = SRSLTE_VEC_EMA(srslte_chest_dl_nbiot_get_rsrq(&ue_dl.chest), rsrq, 0.1);
rsrp = SRSLTE_VEC_EMA(srslte_chest_dl_nbiot_get_rsrp(&ue_dl.chest), rsrp, 0.05);
noise = SRSLTE_VEC_EMA(srslte_chest_dl_nbiot_get_noise_estimate(&ue_dl.chest), noise, 0.05);
nframes++;
if (isnan(rsrq)) {
rsrq = 0;
}
if (isnan(noise)) {
noise = 0;
}
if (isnan(rsrp)) {
rsrp = 0;
}
// Plot and Printf
if (srslte_ue_sync_nbiot_get_sfidx(&ue_sync) == 5) {
float gain = prog_args.rf_gain;
if (gain < 0) {
gain = 10 * log10(srslte_agc_get_gain(&ue_sync.agc));
}
printf("CFO: %+6.2f kHz, RSRP: %4.1f dBm "
"SNR: %4.1f dB, RSRQ: %4.1f dB, "
"NPDCCH detected: %d, NPDSCH-BLER: %5.2f%% (%d of total %d), NPDSCH-Rate: %5.2f kbit/s\r",
srslte_ue_sync_nbiot_get_cfo(&ue_sync) / 1000,
10 * log10(rsrp),
10 * log10(rsrp / noise),
10 * log10(rsrq),
ue_dl.nof_detected,
(float)100 * ue_dl.pkt_errors / ue_dl.pkts_total,
ue_dl.pkt_errors,
ue_dl.pkts_total,
(ue_dl.bits_total / ((sfn * 10 + srslte_ue_sync_nbiot_get_sfidx(&ue_sync)) / 1000.0)) / 1000.0);
}
break;
}
if (srslte_ue_sync_nbiot_get_sfidx(&ue_sync) == 9) {
sfn++;
if (sfn == 1024) {
sfn = 0;
printf("\n");
// don't reset counter when reading from file to maintain complete stats
if (!prog_args.input_file_name) {
ue_dl.pkt_errors = 0;
ue_dl.pkts_total = 0;
ue_dl.nof_detected = 0;
ue_dl.bits_total = 0;
nof_trials = 0;
}
}
}
#ifdef ENABLE_GUI
if (!prog_args.disable_plots) {
if ((sfn % 4) == 0) {
plot_sf_idx = srslte_ue_sync_nbiot_get_sfidx(&ue_sync);
plot_track = true;
sem_post(&plot_sem);
}
}
#endif // ENABLE_GUI
} else if (ret == 0) {
state = DECODE_MIB;
printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r",
srslte_sync_nbiot_get_peak_value(&ue_sync.sfind),
ue_sync.frame_total_cnt,
ue_sync.state);
#ifdef ENABLE_GUI
if (!prog_args.disable_plots) {
plot_sf_idx = srslte_ue_sync_nbiot_get_sfidx(&ue_sync);
plot_track = false;
sem_post(&plot_sem);
}
#endif // ENABLE_GUI
}
sf_cnt++;
} // Main loop
// print statistics
if (prog_args.input_file_name) {
printf("pkt_total=%d\n", ue_dl.pkts_total);
printf("pkt_ok=%d\n", ue_dl.pkts_total - ue_dl.pkt_errors);
printf("pkt_errors=%d\n", ue_dl.pkt_errors);
printf("bler=%.2f\n", ue_dl.pkts_total ? (float)100 * ue_dl.pkt_errors / ue_dl.pkts_total : 0);
printf("rate=%.2f\n", ((ue_dl.bits_total / ((sf_cnt) / 1000.0)) / 1000.0));
printf("dci_detected=%d\n", ue_dl.nof_detected);
}
#ifdef ENABLE_GUI
if (!prog_args.disable_plots) {
if (!pthread_kill(plot_thread, 0)) {
pthread_kill(plot_thread, SIGHUP);
pthread_join(plot_thread, NULL);
}
}
#endif // ENABLE_GUI
srslte_nbiot_ue_dl_free(&ue_dl);
srslte_ue_sync_nbiot_free(&ue_sync);
#if HAVE_PCAP
printf("Saving PCAP file\n");
LTE_PCAP_Close(pcap_file);
#endif
#ifndef DISABLE_RF
if (!prog_args.input_file_name) {
srslte_ue_mib_nbiot_free(&ue_mib);
srslte_rf_close(&rf);
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
if (buff_ptrs[i] != NULL)
free(buff_ptrs[i]);
}
}
#endif
printf("\nBye\n");
return SRSLTE_SUCCESS;
}
/**********************************************************************
* Plotting Functions
***********************************************************************/
#ifdef ENABLE_GUI
plot_real_t p_sync, pce, rsrp_plot;
plot_scatter_t constellation_plot;
float tmp_plot[110 * 15 * 2048];
float tmp_plot2[110 * 15 * 2048];
float tmp_plot3[110 * 15 * 2048];
void* plot_thread_run(void* arg)
{
int i;
uint32_t nof_re = SRSLTE_SF_LEN_RE(ue_dl.cell.base.nof_prb, ue_dl.cell.base.cp);
float rsrp_lin = 0;
sdrgui_init();
plot_scatter_init(&constellation_plot);
plot_scatter_setTitle(&constellation_plot, "NPDSCH/NPDCCH - Equalized Symbols");
plot_scatter_setXAxisScale(&constellation_plot, -4, 4);
plot_scatter_setYAxisScale(&constellation_plot, -4, 4);
plot_scatter_addToWindowGrid(&constellation_plot, (char*)"pdsch_ue", 0, 0);
if (!prog_args.disable_plots_except_constellation) {
plot_real_init(&pce);
plot_real_setTitle(&pce, "Channel Response - Magnitude");
plot_real_setLabels(&pce, "Index", "dB");
plot_real_setYAxisScale(&pce, -40, 40);
plot_real_init(&p_sync);
plot_real_setTitle(&p_sync, "NPSS Cross-Corr abs value");
plot_real_setYAxisScale(&p_sync, 0, 1);
plot_real_init(&rsrp_plot);
plot_real_setTitle(&rsrp_plot, "RSRP");
plot_real_setLabels(&rsrp_plot, "subframe index", "dBm");
plot_real_setYAxisScale(&rsrp_plot, 20, 50);
plot_real_addToWindowGrid(&pce, (char*)"pdsch_ue", 0, 1);
plot_real_addToWindowGrid(&rsrp_plot, (char*)"pdsch_ue", 1, 0);
plot_real_addToWindowGrid(&p_sync, (char*)"pdsch_ue", 1, 1);
}
while (1) {
sem_wait(&plot_sem);
if (!prog_args.disable_plots_except_constellation) {
for (i = 0; i < nof_re; i++) {
tmp_plot[i] = 20 * log10f(cabsf(ue_dl.sf_symbols[i]));
if (isinf(tmp_plot[i])) {
tmp_plot[i] = -80;
}
}
int numpoints = SRSLTE_NRE * 2;
bzero(tmp_plot2, sizeof(float) * numpoints);
int g = (numpoints - SRSLTE_NRE) / 2;
for (i = 0; i < 12 * ue_dl.cell.base.nof_prb; i++) {
tmp_plot2[g + i] = 20 * log10(cabsf(ue_dl.ce[0][i]));
if (isinf(tmp_plot2[g + i])) {
tmp_plot2[g + i] = -80;
}
}
plot_real_setNewData(&pce, tmp_plot2, numpoints);
if (!prog_args.input_file_name) {
if (plot_track) {
srslte_npss_synch_t* pss_obj = &ue_sync.strack.npss;
int max = srslte_vec_max_fi(pss_obj->conv_output_avg, pss_obj->frame_size + pss_obj->fft_size - 1);
srslte_vec_sc_prod_fff(pss_obj->conv_output_avg,
1 / pss_obj->conv_output_avg[max],
tmp_plot2,
pss_obj->frame_size + pss_obj->fft_size - 1);
plot_real_setNewData(&p_sync, &tmp_plot2[max - NPSS_FIND_PLOT_WIDTH / 2], NPSS_FIND_PLOT_WIDTH);
} else {
int len = SRSLTE_NPSS_CORR_FILTER_LEN + ue_sync.sfind.npss.frame_size - 1;
int max = srslte_vec_max_fi(ue_sync.sfind.npss.conv_output_avg, len);
srslte_vec_sc_prod_fff(
ue_sync.sfind.npss.conv_output_avg, 1 / ue_sync.sfind.npss.conv_output_avg[max], tmp_plot2, len);
plot_real_setNewData(&p_sync, tmp_plot2, len);
}
}
// get current RSRP estimate
rsrp_lin = SRSLTE_VEC_EMA(srslte_chest_dl_nbiot_get_rsrp(&ue_dl.chest), rsrp_lin, 0.05);
rsrp_table[rsrp_table_index++] = 10 * log10(rsrp_lin);
if (rsrp_table_index == rsrp_num_plot) {
rsrp_table_index = 0;
}
plot_real_setNewData(&rsrp_plot, rsrp_table, rsrp_num_plot);
}
// check if NPDSCH or NPDCCH has been received
if (ue_dl.npdsch_cfg.nbits.nof_re) {
// plot NPDSCH
plot_scatter_setNewData(&constellation_plot, ue_dl.npdsch.d, ue_dl.npdsch_cfg.nbits.nof_re);
} else if (ue_dl.npdcch.num_decoded_symbols) {
// plot NPDCCH
plot_scatter_setNewData(&constellation_plot, ue_dl.npdcch.d, ue_dl.npdcch.num_decoded_symbols);
}
}
return NULL;
}
void init_plots()
{
if (sem_init(&plot_sem, 0, 0)) {
perror("sem_init");
exit(-1);
}
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = 0;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
pthread_attr_setschedparam(&attr, &param);
if (pthread_create(&plot_thread, NULL, plot_thread_run, NULL)) {
perror("pthread_create");
exit(-1);
}
}
#endif // ENABLE_GUI

@ -0,0 +1,70 @@
#
# Copyright 2013-2020 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/.
#
########################################################################
# NPDSCH_ENODEB/UE TESTS
########################################################################
SET(CTEST_OUTPUT_ON_FAILURE TRUE)
# Run eNB and generate DL signal
# - we generate 512 DL frames
# - schedulingInfoSIB1-NB-r13 is 2, so 16 SIB repetitions are used
# - each frame contains a NPDCCH and NPDSCH with TBS=24 for RNTI 0x1234
set(ARG "-o;/tmp/test.bin;-n;512;-t;2")
add_test(NAME enb1
COMMAND ${CMAKE_COMMAND} -DCMD=$<TARGET_FILE:npdsch_enodeb> "-DARG=${ARG}" -V -P ${CMAKE_CURRENT_SOURCE_DIR}/iqtests.cmake)
# Run npdsch_npdcch file tests
# Try to decode SIB (-k)
set(ARG "-i;/tmp/test.bin;-m;512;-t;2;-w;0;-k")
add_test(NAME npdsch_npdcch_file1
COMMAND ${CMAKE_COMMAND} -DCMD=$<TARGET_FILE:npdsch_npdcch_file_test> "-DARG=${ARG}" -V -P ${CMAKE_CURRENT_SOURCE_DIR}/iqtests.cmake)
# Specify test, SIB1 should be decoded once
set_tests_properties (npdsch_npdcch_file1 PROPERTIES PASS_REGULAR_EXPRESSION "pkt_ok=1")
# Try to decode NPDCCH+NPDSCH for user
set(ARG "-i;/tmp/test.bin;-m;512;-t;2;-w;0;-r;0x1234")
add_test(NAME npdsch_npdcch_file2
COMMAND ${CMAKE_COMMAND} -DCMD=$<TARGET_FILE:npdsch_npdcch_file_test> "-DARG=${ARG}" -V -P ${CMAKE_CURRENT_SOURCE_DIR}/iqtests.cmake)
# Specify test
set_property(TEST npdsch_npdcch_file2 PROPERTY PASS_REGULAR_EXPRESSION "pkt_ok=512")
# Run eNB and generate DL signal
# - we generate 10 DL frames
# - schedulingInfoSIB1-NB-r13 is 0, so 4 SIB repetitions are used
# - i_sf is 1, so two subframes
# - i_rep is 2, so four repetitions
# - i_mcs is 4, so TBS of 120
# - each NPDSCH lasts over 8 subframes in total, starting in sf_idx,
# so in total, two frames are needed for NPDCCH+NPDSCH, hence, 10/2=5 frames
# should be received
set(ARG "-o;/tmp/test.bin;-n;10;-t;0;-i;1;-m;4;-r;2")
add_test(NAME enb2
COMMAND ${CMAKE_COMMAND} -DCMD=$<TARGET_FILE:npdsch_enodeb> "-DARG=${ARG}" -V -P ${CMAKE_CURRENT_SOURCE_DIR}/iqtests.cmake)
# Try to decode NPDCCH+NPDSCH for user
set(ARG "-i;/tmp/test.bin;-m;512;-w;0;-r;0x1234")
add_test(NAME npdsch_npdcch_file3
COMMAND ${CMAKE_COMMAND} -DCMD=$<TARGET_FILE:npdsch_npdcch_file_test> "-DARG=${ARG}" -V -P ${CMAKE_CURRENT_SOURCE_DIR}/iqtests.cmake)
# Specify test
set_property(TEST npdsch_npdcch_file3 PROPERTY PASS_REGULAR_EXPRESSION "pkt_ok=5")

@ -0,0 +1,28 @@
#
# Copyright 2013-2020 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/.
#
message("CMD: " ${CMD})
message("ARG: " ${ARG})
execute_process(COMMAND ${CMD} ${ARG}
RESULT_VARIABLE result
OUTPUT_VARIABLE output
)
message("RESULT: " ${result})
message("OUTPUT: " ${output})

@ -0,0 +1,160 @@
/*
* Copyright 2013-2020 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_NPDSCH_H
#define SRSLTE_NPDSCH_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/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/npdsch_cfg.h"
#include "srslte/phy/phch/regs.h"
#include "srslte/phy/phch/sch.h"
#include "srslte/phy/scrambling/scrambling.h"
#define SRSLTE_NPDSCH_MAX_RE \
(SRSLTE_CP_NORM_SF_NSYMB * SRSLTE_NRE - 8) ///< Full PRB minus 8 RE for NRS (one antenna port)
#define SRSLTE_NPDSCH_MAX_TBS 680 ///< Max TBS in Rel13 NB-IoT
#define SRSLTE_NPDSCH_CRC_LEN (24)
#define SRSLTE_NPDSCH_MAX_TBS_CRC (SRSLTE_NPDSCH_MAX_TBS + SRSLTE_NPDSCH_CRC_LEN)
#define SRSLTE_NPDSCH_MAX_TBS_ENC (3 * SRSLTE_NPDSCH_MAX_TBS_CRC)
#define SRSLTE_NPDSCH_MAX_NOF_SF 10
#define SRSLTE_NPDSCH_NUM_SEQ (2 * SRSLTE_NOF_SF_X_FRAME) ///< for even and odd numbered SFNs
/* @brief Narrowband Physical Downlink shared channel (NPDSCH)
*
* Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.3
*/
typedef struct SRSLTE_API {
srslte_nbiot_cell_t cell;
uint32_t max_re;
bool rnti_is_set;
uint16_t rnti;
// buffers
uint8_t data[SRSLTE_NPDSCH_MAX_TBS_CRC];
uint8_t data_enc[SRSLTE_NPDSCH_MAX_TBS_ENC];
float rm_f[SRSLTE_NPDSCH_MAX_TBS_ENC];
cf_t* ce[SRSLTE_MAX_PORTS];
cf_t* symbols[SRSLTE_MAX_PORTS];
cf_t* sib_symbols[SRSLTE_MAX_PORTS]; // extra buffer for SIB1 symbols as they may be interleaved with other NPDSCH
cf_t* tx_syms[SRSLTE_MAX_PORTS]; // pointer to either symbols or sib1_symbols
cf_t* x[SRSLTE_MAX_PORTS];
cf_t* d;
float* llr;
uint8_t* temp;
uint8_t* rm_b;
// tx & rx objects
srslte_modem_table_t mod;
srslte_viterbi_t decoder;
srslte_sequence_t seq[SRSLTE_NPDSCH_NUM_SEQ];
srslte_crc_t crc;
srslte_convcoder_t encoder;
} srslte_npdsch_t;
typedef struct {
uint16_t hyper_sfn;
// TODO: add all other fields
} srslte_sys_info_block_type_1_nb_t;
SRSLTE_API int srslte_npdsch_init(srslte_npdsch_t* q);
SRSLTE_API void srslte_npdsch_free(srslte_npdsch_t* q);
SRSLTE_API int srslte_npdsch_set_cell(srslte_npdsch_t* q, srslte_nbiot_cell_t cell);
SRSLTE_API int srslte_npdsch_set_rnti(srslte_npdsch_t* q, uint16_t rnti);
SRSLTE_API int srslte_npdsch_cfg(srslte_npdsch_cfg_t* cfg,
srslte_nbiot_cell_t cell,
srslte_ra_nbiot_dl_grant_t* grant,
uint32_t sf_idx);
SRSLTE_API int srslte_npdsch_encode(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
cf_t* sf_symbols[SRSLTE_MAX_PORTS]);
SRSLTE_API int srslte_npdsch_encode_rnti_idx(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
uint32_t rnti_idx,
cf_t* sf_symbols[SRSLTE_MAX_PORTS]);
SRSLTE_API int srslte_npdsch_encode_rnti(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
uint16_t rnti,
cf_t* sf_symbols[SRSLTE_MAX_PORTS]);
SRSLTE_API int srslte_npdsch_encode_seq(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
srslte_sequence_t* seq,
cf_t* sf_symbols[SRSLTE_MAX_PORTS]);
SRSLTE_API int srslte_npdsch_decode(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_rx_t* softbuffer,
cf_t* sf_symbols,
cf_t* ce[SRSLTE_MAX_PORTS],
float noise_estimate,
uint32_t sfn,
uint8_t* data);
SRSLTE_API int srslte_npdsch_decode_rnti(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_rx_t* softbuffer,
cf_t* sf_symbols,
cf_t* ce[SRSLTE_MAX_PORTS],
float noise_estimate,
uint16_t rnti,
uint32_t sfn,
uint8_t* data,
uint32_t rep_counter);
SRSLTE_API int
srslte_npdsch_rm_and_decode(srslte_npdsch_t* q, srslte_npdsch_cfg_t* cfg, float* softbits, uint8_t* data);
SRSLTE_API int
srslte_npdsch_cp(srslte_npdsch_t* q, cf_t* input, cf_t* output, srslte_ra_nbiot_dl_grant_t* grant, bool put);
SRSLTE_API float srslte_npdsch_average_noi(srslte_npdsch_t* q);
SRSLTE_API uint32_t srslte_npdsch_last_noi(srslte_npdsch_t* q);
SRSLTE_API void srslte_npdsch_sib1_pack(srslte_cell_t* cell, srslte_sys_info_block_type_1_nb_t* sib, uint8_t* payload);
SRSLTE_API void srslte_npdsch_sib1_unpack(uint8_t* const msg, srslte_sys_info_block_type_1_nb_t* sib);
#endif // SRSLTE_NPDSCH_H

@ -0,0 +1,42 @@
/*
* Copyright 2013-2020 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_NPDSCH_CFG_H
#define SRSLTE_NPDSCH_CFG_H
#include "srslte/phy/phch/ra_nbiot.h"
/*
* @brief Narrowband Physical downlink shared channel configuration
*
* Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.3
*/
typedef struct SRSLTE_API {
srslte_ra_nbiot_dl_grant_t grant;
srslte_ra_nbits_t nbits;
bool is_encoded;
bool has_bcch; // Whether this NPDSCH is carrying the BCCH
uint32_t sf_idx; // The current idx within the entire NPDSCH
uint32_t rep_idx; // The current repetion within this NPDSCH
uint32_t num_sf; // Total number of subframes tx'ed in this NPDSCH
} srslte_npdsch_cfg_t;
#endif // SRSLTE_NPDSCH_CFG_H

@ -0,0 +1,190 @@
/*
* Copyright 2013-2020 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_UE_DL_NBIOT_H
#define SRSLTE_UE_DL_NBIOT_H
#include <stdbool.h>
#include "srslte/phy/ch_estimation/chest_dl_nbiot.h"
#include "srslte/phy/common/phy_common.h"
#include "srslte/phy/dft/ofdm.h"
#include "srslte/phy/phch/dci_nbiot.h"
#include "srslte/phy/phch/npdcch.h"
#include "srslte/phy/phch/npdsch.h"
#include "srslte/phy/phch/npdsch_cfg.h"
#include "srslte/phy/phch/ra_nbiot.h"
#include "srslte/phy/sync/cfo.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h"
#include "srslte/config.h"
#define SRSLTE_NBIOT_EXPECT_MORE_SF -3 // returned when expecting more subframes for successful decoding
#define SRSLTE_NBIOT_UE_DL_FOUND_DCI -4 // returned when DCI for given RNTI was found
#define SRSLTE_NBIOT_UE_DL_SKIP_SF -5
/*
* @brief Narrowband UE downlink object.
*
* This module is a frontend to all the downlink data and control
* channel processing modules.
*/
typedef struct SRSLTE_API {
srslte_npdcch_t npdcch;
srslte_npdsch_t npdsch;
srslte_ofdm_t fft;
srslte_chest_dl_nbiot_t chest;
srslte_cfo_t sfo_correct;
srslte_softbuffer_rx_t softbuffer;
srslte_nbiot_cell_t cell;
srslte_mib_nb_t mib;
bool mib_set;
cf_t* sf_symbols; // this buffer holds the symbols of the current subframe
cf_t* sf_buffer; // this buffer holds multiple subframes
cf_t* ce[SRSLTE_MAX_PORTS];
cf_t* ce_buffer[SRSLTE_MAX_PORTS];
float* llr; // Buffer to hold soft-bits for later combining repetitions
uint32_t pkt_errors;
uint32_t pkts_total;
uint32_t pkts_ok;
uint32_t nof_detected;
uint32_t bits_total;
// DL configuration for "normal" transmissions
bool has_dl_grant;
srslte_npdsch_cfg_t npdsch_cfg;
// DL configuration for SIB1 transmissions
uint32_t sib1_sfn[4 * SIB1_NB_MAX_REP]; // there are 4 SIB1 TTIs in each hyper frame
srslte_nbiot_si_params_t si_params[SRSLTE_NBIOT_SI_TYPE_NITEMS];
bool si_tti[10240]; // We trade memory consumption for speed
uint16_t current_rnti;
uint32_t last_n_cce;
srslte_dci_location_t last_location;
float sample_offset;
} srslte_nbiot_ue_dl_t;
// This function shall be called just after the initial synchronization
SRSLTE_API int srslte_nbiot_ue_dl_init(srslte_nbiot_ue_dl_t* q,
cf_t* in_buffer[SRSLTE_MAX_PORTS],
uint32_t max_prb,
uint32_t nof_rx_antennas);
SRSLTE_API void srslte_nbiot_ue_dl_free(srslte_nbiot_ue_dl_t* q);
SRSLTE_API int srslte_nbiot_ue_dl_set_cell(srslte_nbiot_ue_dl_t* q, srslte_nbiot_cell_t cell);
SRSLTE_API int srslte_nbiot_ue_dl_decode_fft_estimate(srslte_nbiot_ue_dl_t* q, uint32_t sf_idx, bool is_dl_sf);
SRSLTE_API int srslte_nbiot_ue_dl_decode_estimate(srslte_nbiot_ue_dl_t* q, uint32_t sf_idx);
SRSLTE_API int
srslte_nbiot_ue_dl_cfg_grant(srslte_nbiot_ue_dl_t* q, srslte_ra_nbiot_dl_grant_t* grant, uint32_t sf_idx);
SRSLTE_API int
srslte_nbiot_ue_dl_find_dl_dci(srslte_nbiot_ue_dl_t* q, uint32_t sf_idx, uint16_t rnti, srslte_dci_msg_t* dci_msg);
int srslte_nbiot_ue_dl_find_dl_dci_type_siprarnti(srslte_nbiot_ue_dl_t* q, uint16_t rnti, srslte_dci_msg_t* dci_msg);
int srslte_nbiot_ue_dl_find_dl_dci_type_crnti(srslte_nbiot_ue_dl_t* q,
uint32_t sf_idx,
uint16_t rnti,
srslte_dci_msg_t* dci_msg);
SRSLTE_API int
srslte_nbiot_ue_dl_find_ul_dci(srslte_nbiot_ue_dl_t* q, uint32_t tti, uint32_t rnti, srslte_dci_msg_t* dci_msg);
SRSLTE_API uint32_t srslte_nbiot_ue_dl_get_ncce(srslte_nbiot_ue_dl_t* q);
SRSLTE_API void srslte_nbiot_ue_dl_set_sample_offset(srslte_nbiot_ue_dl_t* q, float sample_offset);
SRSLTE_API int
srslte_nbiot_ue_dl_decode(srslte_nbiot_ue_dl_t* q, cf_t* input, uint8_t* data, uint32_t sfn, uint32_t sf_idx);
SRSLTE_API int srslte_nbiot_ue_dl_decode_npdcch(srslte_nbiot_ue_dl_t* q,
cf_t* input,
uint32_t sfn,
uint32_t sf_idx,
uint16_t rnti,
srslte_dci_msg_t* dci_msg);
SRSLTE_API int srslte_nbiot_ue_dl_decode_npdsch(srslte_nbiot_ue_dl_t* q,
cf_t* input,
uint8_t* data,
uint32_t sfn,
uint32_t sf_idx,
uint16_t rnti);
int srslte_nbiot_ue_dl_decode_npdsch_bcch(srslte_nbiot_ue_dl_t* q, uint8_t* data, uint32_t tti);
int srslte_nbiot_ue_dl_decode_npdsch_no_bcch(srslte_nbiot_ue_dl_t* q, uint8_t* data, uint32_t tti, uint16_t rnti);
void srslte_nbiot_ue_dl_tb_decoded(srslte_nbiot_ue_dl_t* q, uint8_t* data);
SRSLTE_API void srslte_nbiot_ue_dl_reset(srslte_nbiot_ue_dl_t* q);
SRSLTE_API void srslte_nbiot_ue_dl_set_rnti(srslte_nbiot_ue_dl_t* q, uint16_t rnti);
SRSLTE_API void srslte_nbiot_ue_dl_set_mib(srslte_nbiot_ue_dl_t* q, srslte_mib_nb_t mib);
SRSLTE_API void srslte_nbiot_ue_dl_decode_sib1(srslte_nbiot_ue_dl_t* q, uint32_t current_sfn);
SRSLTE_API void
srslte_nbiot_ue_dl_get_sib1_grant(srslte_nbiot_ue_dl_t* q, uint32_t sfn, srslte_ra_nbiot_dl_grant_t* grant);
SRSLTE_API void srslte_nbiot_ue_dl_decode_sib(srslte_nbiot_ue_dl_t* q,
uint32_t hfn,
uint32_t sfn,
srslte_nbiot_si_type_t type,
srslte_nbiot_si_params_t params);
SRSLTE_API void srslte_nbiot_ue_dl_get_next_si_sfn(uint32_t current_hfn,
uint32_t current_sfn,
srslte_nbiot_si_params_t params,
uint32_t* si_hfn,
uint32_t* si_sfn);
SRSLTE_API void
srslte_nbiot_ue_dl_set_si_params(srslte_nbiot_ue_dl_t* q, srslte_nbiot_si_type_t type, srslte_nbiot_si_params_t params);
SRSLTE_API bool srslte_nbiot_ue_dl_is_sib1_sf(srslte_nbiot_ue_dl_t* q, uint32_t sfn, uint32_t sf_idx);
SRSLTE_API void srslte_nbiot_ue_dl_set_grant(srslte_nbiot_ue_dl_t* q, srslte_ra_nbiot_dl_grant_t* grant);
SRSLTE_API void srslte_nbiot_ue_dl_flush_grant(srslte_nbiot_ue_dl_t* q);
SRSLTE_API bool srslte_nbiot_ue_dl_has_grant(srslte_nbiot_ue_dl_t* q);
SRSLTE_API void srslte_nbiot_ue_dl_check_grant(srslte_nbiot_ue_dl_t* q, srslte_ra_nbiot_dl_grant_t* grant);
SRSLTE_API void srslte_nbiot_ue_dl_save_signal(srslte_nbiot_ue_dl_t* q, cf_t* input, uint32_t tti, uint32_t sf_idx);
#endif // SRSLTE_UE_DL_NBIOT_H

@ -0,0 +1,797 @@
/*
* Copyright 2013-2020 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 <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/fec/rm_conv.h"
#include "srslte/phy/phch/npdsch.h"
#include "srslte/phy/utils/bit.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h"
#define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.base.nof_prb, q->cell.base.cp)
#define DUMP_SIGNALS 0
#define RE_EXT_DEBUG 0
int srslte_npdsch_cp(srslte_npdsch_t* q, cf_t* input, cf_t* output, srslte_ra_nbiot_dl_grant_t* grant, bool put)
{
uint32_t l, nof_lte_refs, nof_nbiot_refs;
cf_t * in_ptr = input, *out_ptr = output;
#if RE_EXT_DEBUG
int num_extracted = 0;
#endif
// sanity check
if (q == NULL || input == NULL || output == NULL || grant == NULL) {
return 0;
}
if (put) {
out_ptr += (grant->l_start * q->cell.base.nof_prb * SRSLTE_NRE) + q->cell.nbiot_prb * SRSLTE_NRE;
} else {
in_ptr += (grant->l_start * q->cell.base.nof_prb * SRSLTE_NRE) + q->cell.nbiot_prb * SRSLTE_NRE;
}
if (q->cell.nof_ports == 1) {
nof_nbiot_refs = 2;
} else {
nof_nbiot_refs = 4;
}
if (q->cell.base.nof_ports == 1) {
nof_lte_refs = 2;
} else {
nof_lte_refs = 4;
}
bool skip_crs = false;
if (q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI || q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_DIFFERENT_PCI) {
skip_crs = true;
}
if (q->cell.mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI && q->cell.n_id_ncell != q->cell.base.id) {
fprintf(stderr,
"Cell IDs must match in operation mode inband same PCI (%d != %d)\n",
q->cell.n_id_ncell,
q->cell.base.id);
return 0;
}
// start mapping at specified OFDM symbol
for (l = grant->l_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 (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 (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 {
fprintf(stderr, "Error %d NB-IoT reference symbols not supported.\n", nof_nbiot_refs);
return SRSLTE_ERROR;
}
prb_cp_ref(&in_ptr, &out_ptr, offset, nof_nbiot_refs, nof_nbiot_refs, put);
} else if ((l == 0 || l == 4 || l == 7 || l == 11) && skip_crs) {
// skip LTE's CRS (TODO: use base cell ID?)
if (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;
}
prb_cp_ref(&in_ptr, &out_ptr, offset, nof_lte_refs, nof_lte_refs, put);
} else {
// occupy entire symbol
prb_cp(&in_ptr, &out_ptr, 1);
}
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 NPDSCH in the subframe
*
* Returns the number of symbols written to sf_symbols
*
* 36.211 10.3 section 6.3.5
*/
int srslte_npdsch_put(srslte_npdsch_t* q, cf_t* symbols, cf_t* sf_symbols, srslte_ra_nbiot_dl_grant_t* grant)
{
return srslte_npdsch_cp(q, symbols, sf_symbols, grant, true);
}
/**
* Extracts NPDSCH from the subframe
*
* Returns the number of symbols read
*
* 36.211 10.3 section 6.3.5
*/
int srslte_npdsch_get(srslte_npdsch_t* q, cf_t* sf_symbols, cf_t* symbols, srslte_ra_nbiot_dl_grant_t* grant)
{
return srslte_npdsch_cp(q, sf_symbols, symbols, grant, false);
}
/** Initializes the NPDSCH transmitter and receiver */
int srslte_npdsch_init(srslte_npdsch_t* q)
{
int ret = SRSLTE_ERROR_INVALID_INPUTS;
if (q != NULL) {
bzero(q, sizeof(srslte_npdsch_t));
ret = SRSLTE_ERROR;
q->max_re = SRSLTE_NPDSCH_MAX_RE * SRSLTE_NPDSCH_MAX_NOF_SF;
q->rnti_is_set = false;
INFO("Init NPDSCH: max_re's: %d\n", q->max_re);
if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) {
goto clean;
}
int poly[3] = {0x6D, 0x4F, 0x57};
if (srslte_viterbi_init(&q->decoder, SRSLTE_VITERBI_37, poly, SRSLTE_NPDSCH_MAX_TBS_CRC, true)) {
goto clean;
}
if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC24A, SRSLTE_NPDSCH_CRC_LEN)) {
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->max_re);
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_re);
if (!q->ce[i]) {
goto clean;
}
for (int k = 0; k < q->max_re / 2; k++) {
q->ce[i][k] = 1;
}
q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->x[i]) {
goto clean;
}
q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->symbols[i]) {
goto clean;
}
q->sib_symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re);
if (!q->sib_symbols[i]) {
goto clean;
}
}
q->llr = srslte_vec_malloc(sizeof(float) * q->max_re * 2);
if (!q->llr) {
goto clean;
}
bzero(q->llr, sizeof(float) * q->max_re * 2);
q->temp = srslte_vec_malloc(sizeof(uint8_t) * SRSLTE_NPDSCH_MAX_TBS_CRC);
if (!q->temp) {
goto clean;
}
q->rm_b = srslte_vec_malloc(sizeof(float) * q->max_re * 2);
if (!q->rm_b) {
goto clean;
}
ret = SRSLTE_SUCCESS;
}
clean:
if (ret == SRSLTE_ERROR) {
srslte_npdsch_free(q);
}
return ret;
}
void srslte_npdsch_free(srslte_npdsch_t* q)
{
if (q->d) {
free(q->d);
}
if (q->temp) {
free(q->temp);
}
if (q->rm_b) {
free(q->rm_b);
}
if (q->llr) {
free(q->llr);
}
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->sib_symbols[i]) {
free(q->sib_symbols[i]);
}
}
for (uint32_t i = 0; i < SRSLTE_NPDSCH_NUM_SEQ; i++) {
srslte_sequence_free(&q->seq[i]);
}
srslte_modem_table_free(&q->mod);
srslte_viterbi_free(&q->decoder);
bzero(q, sizeof(srslte_npdsch_t));
}
int srslte_npdsch_set_cell(srslte_npdsch_t* q, srslte_nbiot_cell_t cell)
{
int ret = SRSLTE_ERROR_INVALID_INPUTS;
if (q != NULL && srslte_nbiot_cell_isvalid(&cell)) {
q->cell = cell;
INFO("NPDSCH: Cell config n_id_ncell=%d, %d ports, %d PRBs base cell, max_symbols: %d\n",
q->cell.n_id_ncell,
q->cell.nof_ports,
q->cell.base.nof_prb,
q->max_re);
ret = SRSLTE_SUCCESS;
}
return ret;
}
/* Precalculate the NPDSCH scramble sequences for a given RNTI. This function takes a while
* to execute, so shall be called once the final C-RNTI has been allocated for the session.
* It computes sequences for all subframes for both even and odd SFN's, a total of 20
*/
int srslte_npdsch_set_rnti(srslte_npdsch_t* q, uint16_t rnti)
{
for (int k = 0; k < 2; k++) {
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) {
if (srslte_sequence_npdsch(&q->seq[k * SRSLTE_NOF_SF_X_FRAME + i],
rnti,
0,
k,
2 * i,
q->cell.n_id_ncell,
q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_QPSK))) {
return SRSLTE_ERROR;
}
}
}
q->rnti_is_set = true;
q->rnti = rnti;
return SRSLTE_SUCCESS;
}
int srslte_npdsch_decode(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_rx_t* softbuffer,
cf_t* sf_symbols,
cf_t* ce[SRSLTE_MAX_PORTS],
float noise_estimate,
uint32_t sfn,
uint8_t* data)
{
if (q != NULL && sf_symbols != NULL && data != NULL && cfg != NULL) {
if (q->rnti_is_set) {
return srslte_npdsch_decode_rnti(q, cfg, softbuffer, sf_symbols, ce, noise_estimate, q->rnti, sfn, data, 0);
} else {
fprintf(stderr, "Must call srslte_npdsch_set_rnti() before calling srslte_npdsch_decode()\n");
return SRSLTE_ERROR;
}
} else {
return SRSLTE_ERROR_INVALID_INPUTS;
}
}
/** Decodes the NPDSCH from the received symbols
*/
int srslte_npdsch_decode_rnti(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_rx_t* softbuffer,
cf_t* sf_symbols,
cf_t* ce[SRSLTE_MAX_PORTS],
float noise_estimate,
uint16_t rnti,
uint32_t sfn,
uint8_t* data,
uint32_t rep_counter)
{
// Set pointers for layermapping & precoding
uint32_t n;
cf_t* x[SRSLTE_MAX_LAYERS];
if (q != NULL && sf_symbols != NULL && data != NULL && cfg != NULL) {
INFO("%d.x: Decoding NPDSCH: RNTI: 0x%x, Mod %s, TBS: %d, NofSymbols: %d * %d, NofBitsE: %d * %d\n",
sfn,
rnti,
srslte_mod_string(cfg->grant.mcs[0].mod),
cfg->grant.mcs[0].tbs,
cfg->grant.nof_sf,
cfg->nbits.nof_re,
cfg->grant.nof_sf,
cfg->nbits.nof_bits);
// 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));
// extract RE of all subframes of this grant
uint32_t total_syms = 0;
for (int i = 0; i < cfg->grant.nof_sf; i++) {
// extract symbols
n = srslte_npdsch_get(q, &sf_symbols[i * CURRENT_SFLEN_RE], &q->symbols[0][i * cfg->nbits.nof_re], &cfg->grant);
if (n != cfg->nbits.nof_re) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits.nof_re, n);
return SRSLTE_ERROR;
}
// extract channel estimates
for (int k = 0; k < q->cell.nof_ports; k++) {
n = srslte_npdsch_get(q, &ce[k][i * CURRENT_SFLEN_RE], &q->ce[k][i * cfg->nbits.nof_re], &cfg->grant);
if (n != cfg->nbits.nof_re) {
fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits.nof_re, n);
return SRSLTE_ERROR;
}
}
total_syms += cfg->nbits.nof_re;
}
assert(total_syms == cfg->grant.nof_sf * cfg->nbits.nof_re);
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_rx_mapping_output.bin: NPDSCH after extracting symbols\n", 0);
srslte_vec_save_file(
"npdsch_rx_mapping_output.bin", q->symbols[0], cfg->grant.nof_sf * cfg->nbits.nof_re * sizeof(cf_t));
}
#endif
/* TODO: 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, cfg->grant.nof_sf * cfg->nbits.nof_re, 1.0, noise_estimate);
} else {
srslte_predecoding_diversity(
q->symbols[0], q->ce, x, q->cell.nof_ports, cfg->grant.nof_sf * cfg->nbits.nof_re, 1.0);
srslte_layerdemap_diversity(
x, q->d, q->cell.nof_ports, cfg->grant.nof_sf * cfg->nbits.nof_re / q->cell.nof_ports);
}
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_rx_predecode_output.bin: NPDSCH after predecoding symbols\n", 0);
srslte_vec_save_file(
"npdsch_rx_predecode_output.bin", q->d, cfg->grant.nof_sf * cfg->nbits.nof_re * sizeof(cf_t));
}
#endif
// demodulate symbols
srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, q->llr, cfg->grant.nof_sf * cfg->nbits.nof_re);
#if 0
uint8_t demodbuf[320];
hard_qpsk_demod(q->d,demodbuf,cfg->nbits.nof_re);
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_rx_demod_output.bin: NPDSCH after (hard) de-modulation\n",0);
srslte_vec_save_file("npdsch_rx_demod_output.bin", demodbuf, cfg->nbits.nof_bits);
}
#endif
// descramble
if (q->cell.is_r14 && rnti == SRSLTE_SIRNTI) {
srslte_sequence_t seq;
if (srslte_sequence_npdsch_bcch_r14(
&seq, cfg->grant.start_sfn, q->cell.n_id_ncell, cfg->grant.nof_sf * cfg->nbits.nof_bits)) {
return SRSLTE_ERROR;
}
srslte_scrambling_f_offset(&seq, q->llr, 0, cfg->grant.nof_sf * cfg->nbits.nof_bits);
srslte_sequence_free(&seq);
} else {
if (rnti != q->rnti) {
srslte_sequence_t seq;
if (srslte_sequence_npdsch(&seq,
rnti,
0,
cfg->grant.start_sfn,
2 * cfg->grant.start_sfidx,
q->cell.n_id_ncell,
cfg->grant.nof_sf * cfg->nbits.nof_bits)) {
return SRSLTE_ERROR;
}
srslte_scrambling_f_offset(&seq, q->llr, 0, cfg->grant.nof_sf * cfg->nbits.nof_bits);
srslte_sequence_free(&seq);
} else {
// odd SFN's take the second half of the seq array
int seq_pos = ((cfg->grant.start_sfn % 2) * SRSLTE_NOF_SF_X_FRAME) + cfg->grant.start_sfidx;
srslte_scrambling_f_offset(&q->seq[seq_pos], q->llr, 0, cfg->grant.nof_sf * cfg->nbits.nof_bits);
}
}
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_rx_descramble_output.bin: NPDSCH after de-scrambling\n", 0);
srslte_vec_save_file("npdsch_rx_descramble_output.bin", q->llr, cfg->nbits.nof_bits);
}
#endif
// decode only this transmission
return srslte_npdsch_rm_and_decode(q, cfg, q->llr, data);
} else {
fprintf(stderr, "srslte_npdsch_decode_rnti() called with invalid parameters.\n");
return SRSLTE_ERROR_INVALID_INPUTS;
}
}
int srslte_npdsch_rm_and_decode(srslte_npdsch_t* q, srslte_npdsch_cfg_t* cfg, float* softbits, uint8_t* data)
{
// unrate-matching
uint32_t coded_len = 3 * (cfg->grant.mcs[0].tbs + SRSLTE_NPDSCH_CRC_LEN);
bzero(q->rm_f, sizeof(float) * SRSLTE_NPDSCH_MAX_TBS_ENC);
srslte_rm_conv_rx(softbits, cfg->grant.nof_sf * cfg->nbits.nof_bits, q->rm_f, coded_len);
// TODO: normalization needed?
// viterbi decoder
srslte_viterbi_decode_f(&q->decoder, q->rm_f, q->temp, cfg->grant.mcs[0].tbs + SRSLTE_NPDSCH_CRC_LEN);
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_rx_viterbidecode_output.bin: NPDSCH after viterbi decoding\n", 0);
srslte_vec_save_file("npdsch_rx_viterbidecode_output.bin", q->temp, cfg->grant.mcs[0].tbs + SRSLTE_NPDSCH_CRC_LEN);
}
#endif
// verify CRC sum
uint8_t* x = &q->temp[cfg->grant.mcs[0].tbs];
uint32_t tx_sum = srslte_bit_pack(&x, SRSLTE_NPDSCH_CRC_LEN);
uint32_t rx_sum = srslte_crc_checksum(&q->crc, q->temp, cfg->grant.mcs[0].tbs);
if (rx_sum == tx_sum && rx_sum != 0) {
srslte_bit_pack_vector(q->temp, data, cfg->grant.mcs[0].tbs);
return SRSLTE_SUCCESS;
} else {
return SRSLTE_ERROR;
}
}
/* This functions encodes an NPDSCH and maps it on to the provided subframe.
* It only ever writes a single subframe but it can be called multiple times
* to write more subframes of a previously encoded NPDSCH. For this purpose
* it uses the is_encoded flag in the NPDSCH config object.
* The same applies to its sister functions below.
*/
int srslte_npdsch_encode(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
{
if (q != NULL && data != NULL && cfg != NULL) {
if (q->rnti_is_set) {
return srslte_npdsch_encode_rnti(q, cfg, softbuffer, data, q->rnti, sf_symbols);
} else {
fprintf(stderr, "Must call srslte_npdsch_set_rnti() to set the encoder/decoder RNTI\n");
return SRSLTE_ERROR;
}
} else {
return SRSLTE_ERROR_INVALID_INPUTS;
}
}
/** Converts the PDSCH data bits to symbols mapped to the slot ready for transmission
*/
int srslte_npdsch_encode_rnti(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
uint16_t rnti,
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
{
if (rnti != q->rnti) {
srslte_sequence_t seq;
// FIXME: skip sequence init if cfg->is_encoded==true
if (srslte_sequence_npdsch(&seq,
rnti,
0,
cfg->grant.start_sfidx,
2 * cfg->grant.start_sfidx,
q->cell.n_id_ncell,
cfg->nbits.nof_bits * cfg->grant.nof_sf)) {
return SRSLTE_ERROR;
}
int r = srslte_npdsch_encode_seq(q, cfg, softbuffer, data, &seq, sf_symbols);
srslte_sequence_free(&seq);
return r;
} else {
int seq_pos = ((cfg->grant.start_sfn % 2) * SRSLTE_NOF_SF_X_FRAME) + cfg->grant.start_sfidx;
return srslte_npdsch_encode_seq(q, cfg, softbuffer, data, &q->seq[seq_pos], sf_symbols);
}
}
int srslte_npdsch_encode_seq(srslte_npdsch_t* q,
srslte_npdsch_cfg_t* cfg,
srslte_softbuffer_tx_t* softbuffer,
uint8_t* data,
srslte_sequence_t* seq,
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
{
/* Set pointers for layermapping & precoding */
cf_t* x[SRSLTE_MAX_LAYERS];
int ret = SRSLTE_ERROR_INVALID_INPUTS;
if (q != NULL && data != NULL && cfg != NULL) {
for (int i = 0; i < q->cell.nof_ports; i++) {
if (sf_symbols[i] == NULL) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
// Set up pointer for Tx symbols
q->tx_syms[i] = (cfg->has_bcch) ? q->sib_symbols[i] : q->symbols[i];
}
if (cfg->grant.mcs[0].tbs == 0) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
if (cfg->nbits.nof_re > q->max_re) {
fprintf(stderr,
"Error too many RE per subframe (%d). NPDSCH configured for %d RE (%d PRB)\n",
cfg->nbits.nof_re,
q->max_re,
q->cell.base.nof_prb);
return SRSLTE_ERROR_INVALID_INPUTS;
}
// make sure to run run full NPDSCH procedure only once
if (!cfg->is_encoded) {
INFO("Encoding NPDSCH: Mod %s, NofBits: %d, NofSymbols: %d * %d, NofBitsE: %d * %d\n",
srslte_mod_string(cfg->grant.mcs[0].mod),
cfg->grant.mcs[0].tbs,
cfg->grant.nof_sf,
cfg->nbits.nof_re,
cfg->grant.nof_sf,
cfg->nbits.nof_bits);
/* 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));
int len = cfg->grant.mcs[0].tbs;
// unpack input
srslte_bit_unpack_vector(data, q->data, len);
// attach CRC
srslte_crc_attach(&q->crc, q->data, len);
len += SRSLTE_NPDSCH_CRC_LEN;
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_convcoder_input.bin: NPDSCH before convolution coding\n", 0);
srslte_vec_save_file("npdsch_tx_convcoder_input.bin", q->data, len);
}
#endif
// encode
srslte_convcoder_encode(&q->encoder, q->data, q->data_enc, len);
len *= 3;
// rate-match to allocated bits and scramble output
srslte_rm_conv_tx(q->data_enc, len, q->rm_b, cfg->nbits.nof_bits * cfg->grant.nof_sf);
len = cfg->nbits.nof_bits * cfg->grant.nof_sf;
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_scramble_input.bin: NPDSCH before scrambling\n", 0);
srslte_vec_save_file("npdsch_tx_scramble_input.bin", q->rm_b, len);
}
#endif
// scramble
srslte_scrambling_b_offset(seq, q->rm_b, 0, len);
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_mod_input.bin: NPDSCH before modulation\n", 0);
srslte_vec_save_file("npdsch_tx_mod_input.bin", q->rm_b, len);
}
#endif
// modulate bits
srslte_mod_modulate(&q->mod, (uint8_t*)q->rm_b, q->d, len);
len = cfg->nbits.nof_re * cfg->grant.nof_sf;
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_precode_input.bin: NPDSCH before precoding symbols\n", 0);
srslte_vec_save_file("npdsch_tx_precode_input.bin", q->d, len * sizeof(cf_t));
}
#endif
// TODO: only diversity supported
if (q->cell.base.nof_ports > 1) {
srslte_layermap_diversity(q->d, x, q->cell.base.nof_ports, len);
srslte_precoding_diversity(x, q->tx_syms, q->cell.base.nof_ports, len / q->cell.base.nof_ports, 1.0);
} else {
memcpy(q->tx_syms[0], q->d, len * sizeof(cf_t));
}
#if DUMP_SIGNALS
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_mapping_input.bin: NPDSCH before mapping to resource elements\n", 0);
srslte_vec_save_file("npdsch_tx_mapping_input.bin", q->tx_syms[0], len * sizeof(cf_t));
}
#endif
cfg->is_encoded = true;
}
// mapping to resource elements
if (cfg->is_encoded) {
INFO("Mapping %d NPDSCH REs, sf_idx=%d/%d rep=%d/%d total=%d/%d\n",
cfg->nbits.nof_re,
cfg->sf_idx + 1,
cfg->grant.nof_sf,
cfg->rep_idx + 1,
cfg->grant.nof_rep,
cfg->num_sf + 1,
cfg->grant.nof_sf * cfg->grant.nof_rep);
for (int i = 0; i < q->cell.nof_ports; i++) {
srslte_npdsch_put(q, &q->tx_syms[i][cfg->sf_idx * cfg->nbits.nof_re], sf_symbols[i], &cfg->grant);
}
cfg->num_sf++;
// Decide whether we retransmit the same SF or the next
if (cfg->has_bcch) {
// NPDSCH with BCCH is always transmitted in sequence
cfg->sf_idx++;
if (cfg->sf_idx == cfg->grant.nof_sf) {
cfg->rep_idx++;
}
} else {
// NPDSCH without BCCH transmits up to 3 repetitions after another
cfg->rep_idx++;
int m = SRSLTE_MIN(cfg->grant.nof_rep, 4);
if (cfg->rep_idx % m == 0) {
cfg->sf_idx++;
// start with first SF again after all have been tx'ed m-times
if (cfg->sf_idx == cfg->grant.nof_sf) {
cfg->sf_idx = 0;
} else {
cfg->rep_idx -= m;
}
}
}
}
ret = SRSLTE_SUCCESS;
}
return ret;
}
/* Configures the structure srslte_npdsch_cfg_t from a DL grant.
* If grant is NULL, the grant is assumed to be already stored in cfg->grant
*/
int srslte_npdsch_cfg(srslte_npdsch_cfg_t* cfg,
srslte_nbiot_cell_t cell,
srslte_ra_nbiot_dl_grant_t* grant,
uint32_t sf_idx)
{
if (cfg) {
if (grant) {
memcpy(&cfg->grant, grant, sizeof(srslte_ra_nbiot_dl_grant_t));
}
// Compute number of RE
srslte_ra_nbiot_dl_grant_to_nbits(&cfg->grant, cell, sf_idx, &cfg->nbits);
cfg->sf_idx = 0;
cfg->rep_idx = 0;
cfg->num_sf = 0;
cfg->is_encoded = false;
cfg->has_bcch = cfg->grant.has_sib1; // The UE needs to set this to true for other SIBs too
return SRSLTE_SUCCESS;
} else {
return SRSLTE_ERROR_INVALID_INPUTS;
}
}
/** Reads the HFN from the SIB1-NB, according to TS 36.331 v13.2.0 Section 6.7.1
*
* The function assumes that the 2 LSB read from the MIB-NB are already set inside
* the argument structure. It extracts the 8 MSB from the SIB1 and updates the HFN
* accordingly.
*
* \param msg The packed SIB1-NB
* \param sib The SIB structure containing the MIB-part of the HFN
*/
void srslte_npdsch_sib1_unpack(uint8_t* const msg, srslte_sys_info_block_type_1_nb_t* sib)
{
uint8_t unpacked[SRSLTE_NPDSCH_MAX_TBS];
srslte_bit_unpack_vector(msg, unpacked, SRSLTE_NPDSCH_MAX_TBS);
uint8_t* tmp = unpacked;
if (sib) {
tmp += 12;
sib->hyper_sfn = ((srslte_bit_pack(&tmp, 8) & 0xFF) << 2 | (sib->hyper_sfn & 0x3)) & 0x3FF;
}
}

@ -312,6 +312,68 @@ add_test(pmch_test_qpsk pmch_test -m 6 -n 50)
add_test(pmch_test_qam16 pmch_test -m 15 -n 100)
add_test(pmch_test_qam64 pmch_test -m 25 -n 100)
########################################################################
# NPDSCH TEST
########################################################################
add_executable(npdsch_test npdsch_test.c)
target_link_libraries(npdsch_test srslte_phy)
add_test(npdsch_test_tbs208 npdsch_test -m 12)
add_test(npdsch_test_tbs104 npdsch_test -m 7)
add_test(npdsch_test_tbs40 npdsch_test -m 3)
add_test(npdsch_test_tbs16 npdsch_test -m 0)
# Resource element extraction for different operation modes and cell configuration
# Standalone mode with one Tx port gives maximum number of NPDSCH symbols
add_test(npdsch_test_cellid0_standalone_1port npdsch_test -l 0 -M 3 -x 160)
add_test(npdsch_test_cellid1_standalone_1port npdsch_test -l 1 -M 3 -x 160)
add_test(npdsch_test_cellid2_standalone_1port npdsch_test -l 2 -M 3 -x 160)
add_test(npdsch_test_cellid3_standalone_1port npdsch_test -l 3 -M 3 -x 160)
add_test(npdsch_test_cellid4_standalone_1port npdsch_test -l 4 -M 3 -x 160)
add_test(npdsch_test_cellid5_standalone_1port npdsch_test -l 5 -M 3 -x 160)
# Standalone mode with two Tx ports
add_test(npdsch_test_cellid0_standalone_2port npdsch_test -l 0 -M 3 -P 2 -x 152)
add_test(npdsch_test_cellid1_standalone_2port npdsch_test -l 1 -M 3 -P 2 -x 152)
add_test(npdsch_test_cellid2_standalone_2port npdsch_test -l 2 -M 3 -P 2 -x 152)
add_test(npdsch_test_cellid3_standalone_2port npdsch_test -l 3 -M 3 -P 2 -x 152)
add_test(npdsch_test_cellid4_standalone_2port npdsch_test -l 4 -M 3 -P 2 -x 152)
add_test(npdsch_test_cellid5_standalone_2port npdsch_test -l 5 -M 3 -P 2 -x 152)
# Inband same PCI with 1 LTE antenna port and two NB-IoT ports
add_test(npdsch_test_cellid0_inband_1port_2port npdsch_test -l 0 -M 0 -p 1 -P 2 -x 144)
add_test(npdsch_test_cellid1_inband_1port_2port npdsch_test -l 1 -M 0 -p 1 -P 2 -x 144)
add_test(npdsch_test_cellid2_inband_1port_2port npdsch_test -l 2 -M 0 -p 1 -P 2 -x 144)
add_test(npdsch_test_cellid3_inband_1port_2port npdsch_test -l 3 -M 0 -p 1 -P 2 -x 144)
add_test(npdsch_test_cellid4_inband_1port_2port npdsch_test -l 4 -M 0 -p 1 -P 2 -x 144)
add_test(npdsch_test_cellid5_inband_1port_2port npdsch_test -l 5 -M 0 -p 1 -P 2 -x 144)
# Inband same PCI with 2 antenna ports each
add_test(npdsch_test_cellid0_inband_2port_2port npdsch_test -l 0 -M 0 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid1_inband_2port_2port npdsch_test -l 1 -M 0 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid2_inband_2port_2port npdsch_test -l 2 -M 0 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid3_inband_2port_2port npdsch_test -l 3 -M 0 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid4_inband_2port_2port npdsch_test -l 4 -M 0 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid5_inband_2port_2port npdsch_test -l 5 -M 0 -p 2 -P 2 -x 136)
# Inband different PCI with 2 antenna ports each
add_test(npdsch_test_cellid0_inband_diffpci_2port_2port npdsch_test -l 0 -M 1 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid1_inband_diffpci_2port_2port npdsch_test -l 1 -M 1 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid2_inband_diffpci_2port_2port npdsch_test -l 2 -M 1 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid3_inband_diffpci_2port_2port npdsch_test -l 3 -M 1 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid4_inband_diffpci_2port_2port npdsch_test -l 4 -M 1 -p 2 -P 2 -x 136)
add_test(npdsch_test_cellid5_inband_diffpci_2port_2port npdsch_test -l 5 -M 1 -p 2 -P 2 -x 136)
########################################################################
# NB-IoT DCI TEST
########################################################################
add_executable(dci_nbiot_test dci_nbiot_test.c)
target_link_libraries(dci_nbiot_test srslte_phy)
add_test(dci_nbiot_test dci_nbiot_test)
########################################################################
# FILE TEST
########################################################################
@ -334,6 +396,9 @@ target_link_libraries(pdsch_pdcch_file_test srslte_phy)
add_executable(npbch_file_test npbch_file_test.c)
target_link_libraries(npbch_file_test srslte_phy)
add_executable(npdsch_npdcch_file_test npdsch_npdcch_file_test.c)
target_link_libraries(npdsch_npdcch_file_test srslte_phy)
add_test(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.dat)
add_executable(pmch_file_test pmch_file_test.c)
@ -359,6 +424,9 @@ 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)
add_test(npdsch_npdcch_dci_formatN0_test npdsch_npdcch_file_test -c 0 -s 4 -w 862 -r 0x102 -v -o FormatN0 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_dci_formatN0_L_1_nid0_tti_8624_rnti_0x102.bin)
add_test(npdsch_npdcch_dci_formatN1_test npdsch_npdcch_file_test -c 0 -s 1 -w 546 -r 0x89 -v -o FormatN1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_nbiot_dci_formatN1_nid0_tti_5461_rnti_0x89.bin)
########################################################################
# PUSCH TEST
########################################################################

@ -0,0 +1,119 @@
/*
* Copyright 2013-2020 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 <strings.h>
#include <unistd.h>
#include "srslte/phy/ue/ue_dl_nbiot.h"
#include "srslte/phy/utils/bit.h"
void usage(char* prog)
{
printf("Usage: %s [cpndv]\n", prog);
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, "cpndv")) != -1) {
switch (opt) {
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
// This timing test is supposed to test that a NPDSCH that
// is supposed to start on a SIB1 subframe is in fact postponed
// to the next valid DL subframe.
int dl_timing_test()
{
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};
cf_t rx_buff[SRSLTE_SF_LEN_PRB_NBIOT];
cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {rx_buff, NULL, NULL, NULL};
srslte_nbiot_ue_dl_t ue_dl;
if (srslte_nbiot_ue_dl_init(&ue_dl, buff_ptrs, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS)) {
fprintf(stderr, "Error initializing UE DL\n");
return SRSLTE_ERROR;
}
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Setting cell in UE DL\n");
return SRSLTE_ERROR;
}
srslte_mib_nb_t mib;
mib.sched_info_sib1 = 2;
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib);
// a dummy grant
uint8_t dci_bits_packed[] = {0x81, 0x00, 0x00};
srslte_dci_msg_t dci_tmp;
dci_tmp.format = SRSLTE_DCI_FORMATN1;
dci_tmp.nof_bits = 23;
srslte_bit_unpack_vector(dci_bits_packed, dci_tmp.payload, dci_tmp.nof_bits);
// turn DCI into grant
srslte_ra_nbiot_dl_dci_t dl_dci;
srslte_ra_nbiot_dl_grant_t dl_grant;
int sfn = 185;
int sf_idx = 9;
srslte_nbiot_dci_msg_to_dl_grant(&dci_tmp, 0x1234, &dl_dci, &dl_grant, sfn, sf_idx, 3, SRSLTE_NBIOT_MODE_STANDALONE);
// make sure NPDSCH start is not on a SIB1 subframe
srslte_nbiot_ue_dl_check_grant(&ue_dl, &dl_grant);
// print final grant
srslte_ra_nbiot_dl_grant_fprint(stdout, &dl_grant);
srslte_nbiot_ue_dl_free(&ue_dl);
// only success if starting subframe is not a SIB1 subframe
if (dl_grant.start_sfidx != 4) {
return SRSLTE_SUCCESS;
}
return SRSLTE_ERROR;
}
int main(int argc, char** argv)
{
parse_args(argc, argv);
if (dl_timing_test() != 0) {
printf("Error running DL timing test.\n");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}

@ -0,0 +1,331 @@
/*
* Copyright 2013-2020 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 <strings.h>
#include <unistd.h>
#include "srslte/phy/channel/ch_awgn.h"
#include "srslte/phy/io/filesource.h"
#include "srslte/phy/phch/npdsch.h"
#include "srslte/phy/ue/ue_dl_nbiot.h"
#include "srslte/phy/utils/debug.h"
#define DUMP_SIGNAL 0
char* input_file_name = NULL;
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};
int flen;
uint16_t rnti = SRSLTE_SIRNTI;
int sfn = 0;
int nof_frames = 0;
int max_frames = 1;
uint32_t sf_idx = 0;
uint32_t sched_info_tag = 0; // Value of schedulingInfoSIB1-NB-r13
srslte_mib_nb_t mib;
bool decode_sib1 = false;
float snr = -1.0;
srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1;
srslte_filesource_t fsrc;
srslte_nbiot_ue_dl_t ue_dl;
cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {NULL, NULL, NULL, NULL};
void usage(char* prog)
{
printf("Usage: %s [rovcnwmpstx] -i input_file\n", prog);
printf("\t-o DCI format [Default %s]\n", srslte_dci_format_string(dci_format));
printf("\t-c n_id_ncell [Default %d]\n", cell.n_id_ncell);
printf("\t-s Start subframe_idx [Default %d]\n", sf_idx);
printf("\t-w Start SFN [Default %d]\n", nof_frames);
printf("\t-t Value of schedulingInfoSIB1-NB-r13 [Default %d]\n", sched_info_tag);
printf("\t-k Decode SIB1 [Default %s]\n", decode_sib1 ? "Yes" : "No");
printf("\t-r rnti [Default 0x%x]\n", rnti);
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-m max_frames [Default %d]\n", max_frames);
printf("\t-x SNR-10 (apply noise to input file) [Default %f]\n", snr);
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, "irovcnmwpkstx")) != -1) {
switch (opt) {
case 'i':
input_file_name = argv[optind];
break;
case 'c':
cell.n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 's':
sf_idx = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 't':
sched_info_tag = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
rnti = strtoul(argv[optind], NULL, 0);
break;
case 'w':
sfn = strtoul(argv[optind], NULL, 0);
break;
case 'm':
max_frames = strtoul(argv[optind], NULL, 0);
break;
case 'n':
cell.base.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'k':
decode_sib1 = true;
break;
case 'p':
cell.base.nof_ports = (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) {
fprintf(stderr, "Error unsupported format %s\n", argv[optind]);
exit(-1);
}
break;
case 'x':
snr = atof(argv[optind]);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
if (!input_file_name) {
usage(argv[0]);
exit(-1);
}
}
int base_init()
{
if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) {
fprintf(stderr, "Error opening file %s\n", input_file_name);
exit(-1);
}
flen = 2 * (SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.base.nof_prb)));
buff_ptrs[0] = malloc(flen * sizeof(cf_t));
if (!buff_ptrs[0]) {
perror("malloc");
exit(-1);
}
if (srslte_nbiot_ue_dl_init(&ue_dl, buff_ptrs, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS)) {
fprintf(stderr, "Error initializing UE DL\n");
return -1;
}
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Configuring cell in UE DL\n");
return -1;
}
mib.sched_info_sib1 = sched_info_tag;
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib);
srslte_nbiot_ue_dl_set_rnti(&ue_dl, rnti);
// srslte_mib_nb_printf(stdout, 0, &mib);
DEBUG("Memory init OK\n");
return 0;
}
void base_free()
{
srslte_filesource_free(&fsrc);
srslte_nbiot_ue_dl_free(&ue_dl);
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
if (buff_ptrs[i] != NULL) {
free(buff_ptrs[i]);
}
}
}
int main(int argc, char** argv)
{
int dci_formatN0_detected = 0;
int dci_formatN1_detected = 0;
srslte_dci_format_t last_dci_format = SRSLTE_DCI_FORMAT0;
if (argc < 3) {
usage(argv[0]);
exit(-1);
}
parse_args(argc, argv);
if (base_init()) {
fprintf(stderr, "Error initializing memory\n");
exit(-1);
}
// adjust SNR input to allow for negative values
if (snr != -1.0) {
snr -= 10.0;
printf("Target SNR: %.2fdB\n", snr);
}
uint8_t* data = malloc(100000);
int nread = 0;
if (decode_sib1) {
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
}
do {
nread = srslte_filesource_read(&fsrc, buff_ptrs[0], flen);
if (nread > 0) {
DEBUG("%d.%d: Reading %d samples.\n", sfn, sf_idx, nread);
// add some noise to the signal
if (snr != -1.0) {
float nstd = powf(10.0f, -snr / 20.0f);
srslte_ch_awgn_c(buff_ptrs[0], buff_ptrs[0], nstd, nread);
}
// try to decode
if (srslte_ra_nbiot_is_valid_dl_sf(sfn * 10 + sf_idx)) {
int n;
if (srslte_nbiot_ue_dl_has_grant(&ue_dl)) {
// attempt to decode NPDSCH
n = srslte_nbiot_ue_dl_decode_npdsch(&ue_dl, buff_ptrs[0], data, sfn, sf_idx, rnti);
if (n == SRSLTE_SUCCESS) {
INFO("NPDSCH decoded ok.\n");
if (decode_sib1) {
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
}
} else if (n == SRSLTE_ERROR && decode_sib1) {
// reactivate SIB1 reception
srslte_nbiot_ue_dl_decode_sib1(&ue_dl, sfn);
}
} else {
// decode NPDCCH
srslte_dci_msg_t dci_msg;
n = srslte_nbiot_ue_dl_decode_npdcch(&ue_dl, buff_ptrs[0], sfn, sf_idx, rnti, &dci_msg);
if (n == SRSLTE_NBIOT_UE_DL_FOUND_DCI) {
INFO("Found %s DCI for RNTI=0x%x\n", srslte_dci_format_string(dci_msg.format), rnti);
last_dci_format = dci_msg.format;
if (dci_msg.format == SRSLTE_DCI_FORMATN0) {
// try to convert to 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_msg, &dci_unpacked, &grant, sfn * 10 + sf_idx, SRSLTE_NPUSCH_SC_SPACING_15000)) {
fprintf(stderr, "Error unpacking DCI\n");
return SRSLTE_ERROR;
}
dci_formatN0_detected++;
// we don't use UL grants any further
} else if (dci_msg.format == SRSLTE_DCI_FORMATN1) {
// try to convert DCI to 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_msg, rnti, &dci_unpacked, &grant, sfn, sf_idx, 64 /* fixme: remove */, cell.mode)) {
fprintf(stderr, "Error unpacking DCI\n");
return SRSLTE_ERROR;
}
dci_formatN1_detected++;
// activate grant
srslte_nbiot_ue_dl_set_grant(&ue_dl, &grant);
} else {
fprintf(stderr, "Unsupported DCI format.\n");
return SRSLTE_ERROR;
}
}
}
}
sf_idx++;
if (sf_idx == 10) {
sfn = (sfn + 1) % 1024;
nof_frames++;
sf_idx = 0;
}
}
} while (nread > 0 && nof_frames <= max_frames);
uint32_t pkts_ok = ue_dl.pkts_ok;
printf("pkt_total=%d\n", ue_dl.pkts_total);
printf("pkt_ok=%d\n", pkts_ok);
printf("pkt_errors=%d\n", ue_dl.pkt_errors);
printf("bler=%.2f\n", ue_dl.pkts_total ? (float)100 * ue_dl.pkt_errors / ue_dl.pkts_total : 0);
printf("rate=%.2f\n", ((ue_dl.bits_total / ((nof_frames * 10 + sf_idx) / 1000.0)) / 1000.0));
printf("dci_detected=%d\n", ue_dl.nof_detected);
printf("dci_formatN0_detected=%d\n", dci_formatN0_detected);
printf("dci_formatN1_detected=%d\n", dci_formatN1_detected);
printf("num_frames=%d\n", nof_frames);
#if DUMP_SIGNAL
printf("Saving signal...\n");
srslte_nb_ue_dl_save_signal(&ue_dl, &ue_dl.softbuffer, sf_idx);
#endif
base_free();
if (data != NULL) {
free(data);
}
// exit value depends on actual test
// check if we are testing for correct DCI deconding
if (dci_format == SRSLTE_DCI_FORMATN0 || dci_format == SRSLTE_DCI_FORMATN1 || dci_format == SRSLTE_DCI_FORMATN2) {
// this is a DCI test, check if last DCI matches requested type
if (last_dci_format == dci_format) {
return SRSLTE_SUCCESS;
} else {
return SRSLTE_ERROR;
}
}
// success if at least one NPDSCH could be decoded
if (pkts_ok > 0)
return SRSLTE_SUCCESS;
// return error by default
return SRSLTE_ERROR;
}

@ -0,0 +1,489 @@
/*
* Copyright 2013-2020 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 <sys/time.h>
#include <unistd.h>
#include "srslte/phy/ch_estimation/chest_dl_nbiot.h"
#include "srslte/phy/ch_estimation/refsignal_dl.h"
#include "srslte/phy/ch_estimation/refsignal_dl_nbiot.h"
#include "srslte/phy/dft/ofdm.h"
#include "srslte/phy/io/filesource.h"
#include "srslte/phy/phch/npdsch.h"
#include "srslte/phy/utils/debug.h"
#include "srslte/phy/utils/vector.h"
#define SRSLTE_NBIOT_SFLEN SRSLTE_SF_LEN(srslte_symbol_sz(cell.base.nof_prb))
#define SFN 0
#define SF_IDX 1
#define SRSLTE_NBIOT_NOF_RE_X_PRB (SRSLTE_CP_NORM_SF_NSYMB * SRSLTE_NRE)
// Enable to measure execution time
//#define DO_OFDM
uint32_t nof_ports_lte = 0;
uint32_t nof_ports_nbiot = 1;
srslte_nbiot_mode_t mode = SRSLTE_NBIOT_MODE_INBAND_SAME_PCI;
uint32_t expected_nof_re = 0;
uint32_t n_id_ncell = 0;
uint32_t cfi = 2;
uint32_t subframe = 1;
uint32_t rv_idx = 0;
uint16_t rnti = 1234;
uint16_t i_tbs_val = 0;
char* input_file = NULL;
void usage(char* prog)
{
printf("Usage: %s [fmMlsrRFpnv] \n", prog);
printf("\t-f read signal from file [Default generate it with pdsch_encode()]\n");
printf("\t-m i_tbs value [Default %d]\n", i_tbs_val);
printf("\t-M NB-IoT deployment mode (0=InbandSamePCI,1=InbandDifferentPCI,2=GuardBand,3=Standalone) [Default %d]\n",
mode);
printf("\t-l n_id_ncell [Default %d]\n", n_id_ncell);
printf("\t-s subframe [Default %d]\n", subframe);
printf("\t-r rv_idx [Default %d]\n", rv_idx);
printf("\t-R rnti [Default %d]\n", rnti);
printf("\t-p Base cell cell nof_ports [Default %d]\n", nof_ports_lte);
printf("\t-P NB-IoT cell nof_ports [Default %d]\n", nof_ports_nbiot);
printf("\t-x Expected number of resource elements [Default %d]\n", expected_nof_re);
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, "fmMlsrRpPvx")) != -1) {
switch (opt) {
case 'f':
input_file = argv[optind];
break;
case 's':
subframe = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
rv_idx = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'R':
rnti = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'p':
nof_ports_lte = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'P':
nof_ports_nbiot = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'M':
mode = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'm':
i_tbs_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'l':
n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'x':
expected_nof_re = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
/* fills a list with RE indices containing either NRS or CRS given a particular cell configration
* @return number of RE containing reference signals
*/
int get_ref_res(srslte_nbiot_cell_t cell, int32_t* re_with_refs)
{
bzero(re_with_refs, SRSLTE_NBIOT_MAX_NUM_RE_WITH_REFS * sizeof(uint32_t));
uint32_t num_ref = 0;
// add all RE that contain NRS
for (int p = 0; p < cell.nof_ports; p++) {
DEBUG("Adding NRS for port=%d n_id_ncell=%d\n", p, cell.n_id_ncell);
uint32_t nof_syms = srslte_refsignal_dl_nbiot_nof_symbols(p);
for (int l = 0; l < nof_syms; l++) {
uint32_t nsymbol = srslte_refsignal_nrs_nsymbol(l);
DEBUG(" - adding NRS for symbol=%d\n", nsymbol);
// two references per 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, p, m);
uint32_t re_idx = SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx);
DEBUG(" - adding NRS at re_idx=%d with fidx=%d\n", re_idx, fidx);
re_with_refs[num_ref] = re_idx;
num_ref++;
}
}
}
// add all RE that contain CRS
for (int p = 0; p < cell.base.nof_ports; p++) {
DEBUG("Adding CRS for port=%d cell_id=%d\n", p, cell.base.id);
uint32_t nof_syms = srslte_refsignal_cs_nof_symbols(NULL, NULL, p);
for (int l = 0; l < nof_syms; l++) {
uint32_t nsymbol = srslte_refsignal_cs_nsymbol(l, cell.base.cp, p);
DEBUG(" - adding CRS for symbol=%d\n", nsymbol);
// two references per symbol
for (int m = 0; m < 2; m++) {
uint32_t fidx = ((srslte_refsignal_cs_v(p, l) + (cell.base.id % 6)) % 6) + m * 6;
uint32_t re_idx = SRSLTE_RE_IDX(cell.base.nof_prb, nsymbol, fidx);
DEBUG(" - adding CRS at re_idx=%d with fidx=%d\n", re_idx, fidx);
re_with_refs[num_ref] = re_idx;
num_ref++;
}
}
}
// print all RE's that contain references
printf("RE that contain reference signals:\n");
srslte_vec_fprint_i(stdout, re_with_refs, num_ref);
return num_ref;
}
int extract_re(srslte_nbiot_cell_t cell, uint32_t l_start, uint32_t expected_nof_re)
{
srslte_npdsch_t npdsch;
bzero(&npdsch, sizeof(srslte_npdsch_t));
npdsch.cell = cell;
if (!srslte_nbiot_cell_isvalid(&npdsch.cell)) {
printf("cell is not properly configured\n");
return SRSLTE_ERROR;
}
// fill dummy values in RE
cf_t sf_syms[SRSLTE_NBIOT_NOF_RE_X_PRB];
for (int i = 0; i < SRSLTE_NBIOT_NOF_RE_X_PRB; i++) {
sf_syms[i] = i;
}
srslte_ra_nbiot_dl_grant_t dl_grant;
bzero(&dl_grant, sizeof(srslte_ra_nbiot_dl_grant_t));
dl_grant.l_start = l_start;
cf_t npdsch_syms[SRSLTE_NPDSCH_MAX_RE];
int nof_ext_syms = srslte_npdsch_cp(&npdsch, sf_syms, npdsch_syms, &dl_grant, false);
#if 0
if (SRSLTE_VERBOSE_ISDEBUG()) {
for (int i = 0; i < nof_ext_syms; i++) {
printf("re%d:", i);
srslte_vec_fprint_c(stdout, &npdsch_syms[i], 1);
}
}
#endif
// check number of extracted REs
if (nof_ext_syms != expected_nof_re) {
printf("RE extraction failed (expected %d, but got %d)!\n", expected_nof_re, nof_ext_syms);
return SRSLTE_ERROR;
}
// check that there are not REs containing NRS or CRS
int32_t re_with_refs[SRSLTE_NBIOT_MAX_NUM_RE_WITH_REFS];
int num_re_with_refs = get_ref_res(cell, re_with_refs);
// for each extracted data symbol, check if it collides with a reference signal
for (int i = 0; i < num_re_with_refs; i++) {
for (int l = 0; l < nof_ext_syms; l++) {
if (npdsch_syms[l] == re_with_refs[i]) {
printf("Extracted data RE is also a reference signal %d: ", l);
srslte_vec_fprint_c(stdout, &npdsch_syms[l], 1);
return SRSLTE_ERROR;
}
}
}
return SRSLTE_SUCCESS;
}
int re_extract_test(int argc, char** argv)
{
parse_args(argc, argv);
if (expected_nof_re == 0) {
printf("Skipping RE extraction test because number of expected REs isn't given!\n");
return SRSLTE_SUCCESS;
}
srslte_nbiot_cell_t cell = {};
// Standalone mode with l_start=0 gives the maximum number of REs
cell.mode = mode;
cell.base.nof_prb = 1;
cell.base.id = (mode == SRSLTE_NBIOT_MODE_INBAND_SAME_PCI) ? n_id_ncell : rand() % SRSLTE_NUM_PCI;
cell.n_id_ncell = n_id_ncell;
cell.nof_ports = nof_ports_nbiot;
cell.base.nof_ports = nof_ports_lte;
uint32_t l_start = 0;
if (extract_re(cell, l_start, expected_nof_re) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
int coding_test(int argc, char** argv)
{
int ret = SRSLTE_ERROR;
struct timeval t[3];
srslte_ra_nbiot_dl_grant_t grant;
srslte_npdsch_cfg_t npdsch_cfg;
srslte_npdsch_t npdsch;
uint8_t* data = NULL;
uint8_t* rx_data = NULL;
cf_t* ce[SRSLTE_MAX_PORTS] = {NULL};
cf_t* sf_symbols = NULL;
cf_t* slot_symbols[SRSLTE_MAX_PORTS] = {NULL};
srslte_ofdm_t ofdm_tx, ofdm_rx;
parse_args(argc, argv);
// setup cell config for this test
srslte_nbiot_cell_t cell = {};
cell.base.nof_prb = 1;
cell.base.cp = SRSLTE_CP_NORM;
cell.base.nof_ports = 1;
cell.nof_ports = 1;
cell.mode = SRSLTE_NBIOT_MODE_STANDALONE;
cell.n_id_ncell = n_id_ncell;
if (!srslte_nbiot_cell_isvalid(&cell)) {
printf("Cell is not properly configured\n");
return ret;
}
bzero(&npdsch, sizeof(srslte_npdsch_t));
bzero(&npdsch_cfg, sizeof(srslte_npdsch_cfg_t));
bzero(ce, sizeof(cf_t*) * SRSLTE_MAX_PORTS);
bzero(slot_symbols, sizeof(cf_t*) * SRSLTE_MAX_PORTS);
srslte_ra_nbiot_dl_dci_t dci;
bzero(&dci, sizeof(srslte_ra_nbiot_dl_dci_t));
dci.mcs_idx = i_tbs_val;
dci.rv_idx = rv_idx;
if (srslte_ra_nbiot_dl_dci_to_grant(&dci, &grant, SFN, SF_IDX, DUMMY_R_MAX, false, cell.mode)) {
fprintf(stderr, "Error computing resource allocation\n");
return ret;
}
if (!srslte_nbiot_cell_isvalid(&cell)) {
fprintf(stderr, "Cell configuration invalid.\n");
exit(-1);
}
/* Configure NPDSCH */
if (srslte_npdsch_cfg(&npdsch_cfg, cell, &grant, subframe)) {
fprintf(stderr, "Error configuring NPDSCH\n");
exit(-1);
}
/* init memory */
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
ce[i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp));
if (!ce[i]) {
perror("srslte_vec_malloc");
goto quit;
}
for (int j = 0; j < SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp); j++) {
ce[i][j] = 1;
}
slot_symbols[i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp));
if (!slot_symbols[i]) {
perror("srslte_vec_malloc");
goto quit;
}
memset(slot_symbols[i], 0, sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp));
}
sf_symbols = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_NBIOT_SFLEN);
memset(sf_symbols, 0, sizeof(cf_t) * SRSLTE_NBIOT_SFLEN);
data = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs[0].tbs / 8);
if (!data) {
perror("srslte_vec_malloc");
goto quit;
}
rx_data = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs[0].tbs / 8);
if (!rx_data) {
perror("srslte_vec_malloc");
goto quit;
}
srslte_ofdm_tx_init(&ofdm_tx, cell.base.cp, slot_symbols[0], sf_symbols, cell.base.nof_prb);
srslte_ofdm_rx_init(&ofdm_rx, cell.base.cp, sf_symbols, slot_symbols[0], cell.base.nof_prb);
if (srslte_npdsch_init(&npdsch)) {
fprintf(stderr, "Error creating NPDSCH object\n");
goto quit;
}
if (srslte_npdsch_set_cell(&npdsch, cell)) {
fprintf(stderr, "Error configuring NPDSCH object\n");
goto quit;
}
srslte_npdsch_set_rnti(&npdsch, rnti);
if (input_file) {
srslte_filesource_t fsrc;
if (srslte_filesource_init(&fsrc, input_file, SRSLTE_COMPLEX_FLOAT_BIN)) {
fprintf(stderr, "Error opening file %s\n", input_file);
exit(-1);
}
#ifdef DO_OFDM
srslte_filesource_read(&fsrc, sf_symbols, SRSLTE_SF_LEN_PRB(cell.base.nof_prb));
#else
srslte_filesource_read(&fsrc, slot_symbols[0], SRSLTE_SF_LEN_RE(cell.base.nof_prb, cell.base.cp));
#endif
srslte_chest_dl_nbiot_t chest;
if (srslte_chest_dl_nbiot_init(&chest, SRSLTE_NBIOT_MAX_PRB)) {
printf("Error initializing equalizer\n");
exit(-1);
}
if (srslte_chest_dl_nbiot_set_cell(&chest, cell) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error setting channel estimator's cell configuration\n");
return -1;
}
srslte_chest_dl_nbiot_estimate(&chest, slot_symbols[0], ce, subframe);
srslte_chest_dl_nbiot_free(&chest);
srslte_filesource_free(&fsrc);
}
// generate random data
srand(time(NULL));
for (int i = 0; i < grant.mcs[0].tbs / 8; i++) {
data[i] = rand() % 256;
}
if (!input_file) {
if (srslte_npdsch_encode(&npdsch, &npdsch_cfg, NULL, data, slot_symbols)) {
fprintf(stderr, "Error encoding NPDSCH\n");
goto quit;
}
}
#ifdef DO_OFDM
// Transform to OFDM symbols
srslte_ofdm_tx_sf(&ofdm_tx);
#endif
if (SRSLTE_VERBOSE_ISDEBUG()) {
DEBUG("SAVED FILE npdsch_tx_sf.bin: transmitted subframe symbols\n");
srslte_vec_save_file("npdsch_tx_sf.bin", sf_symbols, SRSLTE_NBIOT_SFLEN * sizeof(cf_t));
}
int M = 1;
int r = 0;
gettimeofday(&t[1], NULL);
for (int i = 0; i < M; i++) {
#ifdef DO_OFDM
srslte_ofdm_rx_sf(&ofdm_rx);
#endif
r = srslte_npdsch_decode(&npdsch, &npdsch_cfg, NULL, slot_symbols[0], ce, 0, 0, rx_data);
}
gettimeofday(&t[2], NULL);
get_time_interval(t);
// compare transmitted and received data
printf("Tx payload: ");
srslte_vec_fprint_b(stdout, data, grant.mcs[0].tbs / 8);
printf("Rx payload: ");
srslte_vec_fprint_b(stdout, rx_data, grant.mcs[0].tbs / 8);
if (memcmp(data, rx_data, sizeof(uint8_t) * grant.mcs[0].tbs / 8) == 0) {
printf("DECODED in %.2f (PHY bitrate=%.2f Mbps. Processing bitrate=%.2f Mbps)\n",
(float)t[0].tv_usec / M,
(float)grant.mcs[0].tbs / 1000,
(float)grant.mcs[0].tbs * M / t[0].tv_usec);
} else {
printf("Error!\n");
}
if (r) {
ret = SRSLTE_ERROR;
goto quit;
}
ret = SRSLTE_SUCCESS;
quit:
srslte_npdsch_free(&npdsch);
for (int i = 0; i < SRSLTE_MAX_PORTS; i++) {
if (ce[i]) {
free(ce[i]);
}
if (slot_symbols[i]) {
free(slot_symbols[i]);
}
}
if (data) {
free(data);
}
if (rx_data) {
free(rx_data);
}
if (sf_symbols) {
free(sf_symbols);
}
srslte_ofdm_tx_free(&ofdm_tx);
srslte_ofdm_rx_free(&ofdm_rx);
return ret;
}
int main(int argc, char** argv)
{
int ret = SRSLTE_ERROR;
if (re_extract_test(argc, argv) != SRSLTE_SUCCESS) {
printf("Resource element extraction test failed!\n");
return ret;
}
if (coding_test(argc, argv) != SRSLTE_SUCCESS) {
printf("NPDSCH En- and Decoding test failed!\n");
return ret;
}
ret = SRSLTE_SUCCESS;
return ret;
}

@ -30,6 +30,10 @@ add_executable(pucch_resource_test pucch_resource_test.c)
target_link_libraries(pucch_resource_test srslte_phy)
add_test(pucch_resource_test pucch_resource_test)
add_executable(ue_dl_nbiot_test ue_dl_nbiot_test.c)
target_link_libraries(ue_dl_nbiot_test srslte_phy pthread)
add_test(ue_dl_nbiot_test ue_dl_nbiot_test)
if(RF_FOUND)
add_executable(ue_mib_sync_test_nbiot_usrp ue_mib_sync_test_nbiot_usrp.c)
target_link_libraries(ue_mib_sync_test_nbiot_usrp srslte_phy srslte_rf pthread)

@ -0,0 +1,160 @@
/*
* Copyright 2013-2020 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 <strings.h>
#include <unistd.h>
#include "srslte/phy/ue/ue_dl_nbiot.h"
srslte_nbiot_cell_t cell = {
.base = {.nof_prb = 1, .nof_ports = 1, .cp = SRSLTE_CP_NORM, .id = 0},
.nbiot_prb = 0,
.nof_ports = 1,
.n_id_ncell = 0,
};
void usage(char* prog)
{
printf("Usage: %s [v]\n", prog);
printf("\t-l n_id_ncell to sync [Default %d]\n", cell.n_id_ncell);
printf("\t-v srslte_verbose\n");
}
void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "lv")) != -1) {
switch (opt) {
case 'l':
cell.n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
srslte_verbose++;
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
int dl_grant_with_sib1_test()
{
int ret = SRSLTE_ERROR;
cf_t rx_buff[SRSLTE_SF_LEN_PRB_NBIOT];
cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {rx_buff, NULL, NULL, NULL};
srslte_nbiot_ue_dl_t ue_dl;
srslte_nbiot_ue_dl_init(&ue_dl, buff_ptrs, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS);
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Setting cell in UE DL\n");
return ret;
}
// pass MIB to compute SIB1 parameters
srslte_mib_nb_t mib;
mib.sched_info_sib1 = 3;
// srslte_mib_nb_printf(stdout, cell, &mib);
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib);
// setup grant who's start collides with a SIB1 transmission
srslte_ra_nbiot_dl_grant_t grant;
srslte_nbiot_ue_dl_get_sib1_grant(&ue_dl, 0, &grant);
// call helper to fix grant
srslte_nbiot_ue_dl_check_grant(&ue_dl, &grant);
// grant must not start on sf_idx 4
if (grant.start_sfidx != 4) {
ret = SRSLTE_SUCCESS;
}
srslte_nbiot_ue_dl_free(&ue_dl);
return ret;
}
int dl_grant_with_sib2_test()
{
int ret = SRSLTE_ERROR;
cf_t rx_buff[SRSLTE_SF_LEN_PRB_NBIOT];
cf_t* buff_ptrs[SRSLTE_MAX_PORTS] = {rx_buff, NULL, NULL, NULL};
srslte_nbiot_ue_dl_t ue_dl;
srslte_nbiot_ue_dl_init(&ue_dl, buff_ptrs, SRSLTE_NBIOT_MAX_PRB, SRSLTE_NBIOT_NUM_RX_ANTENNAS);
if (srslte_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Setting cell in UE DL\n");
return ret;
}
// pass MIB to compute SIB1 parameters
srslte_mib_nb_t mib;
mib.sched_info_sib1 = 3;
srslte_nbiot_ue_dl_set_mib(&ue_dl, mib);
// configure SIB2-NB parameters
srslte_nbiot_si_params_t sib2_params;
sib2_params.n = 1;
sib2_params.si_periodicity = 128;
sib2_params.si_radio_frame_offset = 0;
sib2_params.si_repetition_pattern = 2; // Every 2nd radio frame
sib2_params.si_tb = 208;
sib2_params.si_window_length = 160;
srslte_nbiot_ue_dl_set_si_params(&ue_dl, SRSLTE_NBIOT_SI_TYPE_SIB2, sib2_params);
int dummy_tti = 0;
srslte_nbiot_ue_dl_decode_sib(&ue_dl, dummy_tti, dummy_tti, SRSLTE_NBIOT_SI_TYPE_SIB2, sib2_params);
// setup grant who's start collides with the above configured SI transmission
srslte_ra_nbiot_dl_grant_t grant;
grant.start_sfn = 898;
grant.start_sfidx = 1;
// UE DL object should automatically fix the grant
srslte_nbiot_ue_dl_check_grant(&ue_dl, &grant);
if (grant.start_sfn == 899 && grant.start_sfidx == 3) {
ret = SRSLTE_SUCCESS;
}
srslte_nbiot_ue_dl_free(&ue_dl);
return ret;
}
int main(int argc, char** argv)
{
parse_args(argc, argv);
if (dl_grant_with_sib1_test()) {
printf("DL grant with SIB1 test failed\n");
return SRSLTE_ERROR;
}
if (dl_grant_with_sib2_test()) {
printf("DL grant with SIB2 test failed\n");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save