You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
22 KiB
C

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#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 "srsran/phy/ch_estimation/chest_dl_nbiot.h"
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/io/filesink.h"
#include "srsran/phy/io/filesource.h"
#include "srsran/phy/sync/npss.h"
#include "srsran/phy/sync/nsss.h"
#include "srsran/phy/ue/ue_dl_nbiot.h"
#include "srsran/phy/utils/bit.h"
#include "srsran/phy/utils/random.h"
#define UE_CRNTI 0x1234
#define HAVE_NPDSCH 1
#define NPDCCH_SF_IDX 1
#define NOF_TX_ANT 1
#define MAX_SRATE_DELTA 2 // allowable delta (in Hz) between requested and actual sample rate
static 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 "srsran/phy/rf/rf.h"
static srsran_rf_t radio;
#else
#pragma message "Compiling npdsch_ue with no RF support"
#endif
static char* output_file_name = NULL;
static srsran_nbiot_cell_t cell = {
.base = {.nof_ports = 1, .nof_prb = SRSRAN_NBIOT_DEFAULT_NUM_PRB_BASECELL, .cp = SRSRAN_CP_NORM, .id = 0},
.nbiot_prb = SRSRAN_NBIOT_DEFAULT_PRB_OFFSET,
.n_id_ncell = 0,
.nof_ports = 1,
.mode = SRSRAN_NBIOT_MODE_STANDALONE,
.is_r14 = true};
static uint32_t i_tbs_val = 1, last_i_tbs_val = 1;
static int nof_frames = -1;
static uint32_t i_sf_val = 0;
static uint32_t i_rep_val = 0;
static char* rf_args = "";
static float rf_amp = 0.8, rf_gain = 70.0, rf_freq = 0;
static float file_snr = -100.0;
static char* rf_dev = "";
static bool null_file_sink = false;
static srsran_random_t* random_gen;
static srsran_filesink_t fsink;
static srsran_ofdm_t ifft;
static srsran_npss_synch_t npss_sync;
static srsran_nsss_synch_t nsss_sync;
static srsran_npbch_t npbch;
static srsran_npdcch_t npdcch;
static srsran_npdsch_t npdsch;
static srsran_npdsch_cfg_t sib1_npdsch_cfg;
static srsran_npdsch_cfg_t npdsch_cfg;
static srsran_nbiot_ue_dl_t ue_dl;
static srsran_softbuffer_tx_t softbuffer;
static srsran_ra_nbiot_dl_dci_t ra_dl;
static srsran_ra_nbiot_dl_dci_t ra_dl_sib1;
static srsran_chest_dl_nbiot_t ch_est;
static srsran_mib_nb_t mib_nb;
static 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
static cf_t *sf_buffer = NULL, *output_buffer = NULL;
static int sf_n_re = 0, sf_n_samples = 0;
void usage(char* prog)
{
printf("Usage: %s [aeOgfostmirnlRpv]\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-O RF device [Default use RF board]\n");
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", file_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-R Is R14 cell [Default %s]\n", cell.is_r14 ? "Yes" : "No");
printf("\t-p NB-IoT PRB id [Default %d]\n", cell.nbiot_prb);
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "aeOgfostmirnlRpv")) != -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 'O':
rf_dev = argv[optind];
break;
case 'f':
rf_freq = strtof(argv[optind], NULL);
break;
case 'o':
output_file_name = argv[optind];
break;
case 's':
file_snr = strtof(argv[optind], NULL);
break;
case 't':
sched_info_tag = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'i':
i_sf_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'm':
i_tbs_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 'R':
cell.is_r14 = !cell.is_r14;
break;
case 'p':
cell.nbiot_prb = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'v':
increase_srsran_verbose_level();
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 needs 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 = srsran_vec_cf_malloc(sf_n_re);
if (!sf_buffer) {
perror("Error: malloc for sf_buffer");
exit(-1);
}
output_buffer = srsran_vec_cf_malloc(sf_n_samples);
if (!output_buffer) {
perror("Error: malloc for output buffer");
exit(-1);
}
// open file or USRP
if (output_file_name) {
if (strcmp(output_file_name, "NULL")) {
if (srsran_filesink_init(&fsink, output_file_name, SRSRAN_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
if (strlen(rf_dev) > 0) {
if (srsran_rf_open_devname(&radio, rf_dev, rf_args, NOF_TX_ANT)) {
fprintf(stderr, "Error opening RF device %s\n", rf_dev);
exit(-1);
}
} else if (srsran_rf_open(&radio, rf_args)) {
fprintf(stderr, "Error opening RF default device\n");
exit(-1);
}
#else
printf("Error: RF not available - select an output file\n");
exit(-1);
#endif
}
if (srsran_ofdm_tx_init(&ifft, SRSRAN_CP_NORM, sf_buffer, output_buffer, cell.base.nof_prb)) {
fprintf(stderr, "Error creating iFFT object\n");
exit(-1);
}
srsran_ofdm_set_normalize(&ifft, true);
srsran_ofdm_set_freq_shift(&ifft, -SRSRAN_NBIOT_FREQ_SHIFT_FACTOR);
if (srsran_npss_synch_init(&npss_sync, sf_n_samples, srsran_symbol_sz(cell.base.nof_prb))) {
fprintf(stderr, "Error initializing NPSS object\n");
exit(-1);
}
if (srsran_nsss_synch_init(&nsss_sync, sf_n_samples, srsran_symbol_sz(cell.base.nof_prb))) {
fprintf(stderr, "Error initializing NSSS object\n");
exit(-1);
}
if (srsran_npbch_init(&npbch)) {
fprintf(stderr, "Error creating NPBCH object\n");
exit(-1);
}
if (srsran_npbch_set_cell(&npbch, cell)) {
fprintf(stderr, "Error setting cell in NPBCH object\n");
exit(-1);
}
if (srsran_npdcch_init(&npdcch)) {
fprintf(stderr, "Error creating NPDCCH object\n");
exit(-1);
}
if (srsran_npdcch_set_cell(&npdcch, cell)) {
fprintf(stderr, "Configuring cell in NPDCCH\n");
exit(-1);
}
if (srsran_npdsch_init(&npdsch)) {
fprintf(stderr, "Error creating NPDSCH object\n");
exit(-1);
}
if (srsran_npdsch_set_cell(&npdsch, cell)) {
fprintf(stderr, "Configuring cell in NPDSCH\n");
exit(-1);
}
srsran_npdsch_set_rnti(&npdsch, UE_CRNTI);
if (srsran_softbuffer_tx_init(&softbuffer, cell.base.nof_prb)) {
fprintf(stderr, "Error initiating soft buffer\n");
exit(-1);
}
random_gen = srsran_random_init(time(NULL));
}
void base_free()
{
srsran_random_free(random_gen);
srsran_softbuffer_tx_free(&softbuffer);
srsran_npdsch_free(&npdsch);
srsran_npdcch_free(&npdcch);
srsran_npbch_free(&npbch);
srsran_chest_dl_nbiot_free(&ch_est);
srsran_npss_synch_free(&npss_sync);
srsran_nsss_synch_free(&nsss_sync);
srsran_ofdm_tx_free(&ifft);
if (sf_buffer) {
free(sf_buffer);
}
if (output_buffer) {
free(output_buffer);
}
if (output_file_name) {
if (!null_file_sink) {
srsran_filesink_free(&fsink);
}
} else {
#ifndef DISABLE_RF
srsran_rf_close(&radio);
#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
static int update_radl(void)
{
bzero(&ra_dl_sib1, sizeof(srsran_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(srsran_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;
srsran_nbiot_dl_dci_fprint(stdout, &ra_dl);
srsran_ra_nbiot_dl_grant_t dummy_grant;
srsran_ra_nbits_t dummy_nbits;
#define DUMMY_SFIDX 1
#define DUMMY_SFN 0
srsran_ra_nbiot_dl_dci_to_grant(&ra_dl, &dummy_grant, DUMMY_SFN, DUMMY_SFIDX, DUMMY_R_MAX, false, cell.mode);
srsran_ra_nbiot_dl_grant_to_nbits(&dummy_grant, cell, 0, &dummy_nbits);
srsran_ra_nbiot_dl_grant_fprint(stdout, &dummy_grant);
printf("Enter new MCS index: ");
fflush(stdout);
return SRSRAN_SUCCESS;
}
/* Read new MCS from stdin */
static int update_control(void)
{
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 last known MCS index\n");
i_tbs_val = last_i_tbs_val;
return update_radl();
}
}
return 0;
} else if (n < 0) {
// error
perror("Error: select for MCS entry");
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[SRSRAN_NPSS_TOT_LEN];
cf_t nsss_signal[SRSRAN_NSSS_LEN * SRSRAN_NSSS_NUM_SEQ]; // for subframe 9
uint8_t bch_payload[SRSRAN_MIB_NB_LEN];
uint8_t sib1_nb_payload[SRSRAN_NPDSCH_MAX_TBS];
srsran_dci_msg_t dci_msg;
srsran_dci_location_t locations[SRSRAN_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 && file_snr != -100.0) {
file_snr -= 10.0;
printf("Target SNR: %.2fdB\n", file_snr);
}
sf_n_re = 2 * SRSRAN_CP_NORM_NSYMB * cell.base.nof_prb * SRSRAN_NRE;
sf_n_samples = 2 * SRSRAN_SLOT_LEN(srsran_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[SRSRAN_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[SRSRAN_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 = SRSRAN_NBIOT_MODE_STANDALONE;
// Initialize UE DL
if (srsran_nbiot_ue_dl_init(&ue_dl, sf_symbols, SRSRAN_NBIOT_MAX_PRB, SRSRAN_NBIOT_NUM_RX_ANTENNAS)) {
fprintf(stderr, "Error initiating UE downlink processing module\n");
exit(-1);
}
if (srsran_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Error setting cell in UE DL\n");
return -1;
}
srsran_nbiot_ue_dl_set_mib(&ue_dl, mib_nb);
/* Generate NPSS/NSSS signals */
srsran_npss_generate(npss_signal);
srsran_nsss_generate(nsss_signal, cell.n_id_ncell);
#ifdef NPSS_DUMP
srsran_filesink_t debug_fsink;
char fname[] = "npss.bin";
if (srsran_filesink_init(&debug_fsink, fname, SRSRAN_COMPLEX_FLOAT_BIN)) {
fprintf(stderr, "Error opening file %s\n", fname);
exit(-1);
}
srsran_filesink_write(&debug_fsink, npss_signal, SRSRAN_NPSS_LEN * 11);
srsran_filesink_free(&debug_fsink);
#endif
/* Generate CRS+NRS signals */
if (srsran_chest_dl_nbiot_init(&ch_est, SRSRAN_NBIOT_MAX_PRB)) {
fprintf(stderr, "Error initializing equalizer\n");
exit(-1);
}
if (srsran_chest_dl_nbiot_set_cell(&ch_est, cell) != SRSRAN_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) {
/* set sampling frequency */
int srate = srsran_sampling_freq_hz(cell.base.nof_prb);
if (srate != -1) {
printf("Setting tx sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srsran_rf_set_tx_srate(&radio, (double)srate);
if (abs(srate - (int)srate_rf) > MAX_SRATE_DELTA) {
ERROR("Could not set tx sampling rate : wanted %d got %f", srate, srate_rf);
exit(-1);
}
} else {
fprintf(stderr, "Error: invalid number of PRB %d\n", cell.base.nof_prb);
exit(-1);
}
srsran_rf_set_tx_gain(&radio, rf_gain);
printf("Set TX gain: %.1f dB\n", srsran_rf_get_tx_gain(&radio));
printf("Set TX freq: %.2f MHz\n", srsran_rf_set_tx_freq(&radio, 0, rf_freq) / 1000000);
}
#endif
if (update_radl()) {
fprintf(stderr, "Error updating radl\n");
exit(-1);
}
/* Initiate valid DCI locations */
for (int i = 0; i < SRSRAN_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;
srsran_softbuffer_tx_reset(&softbuffer);
bzero(&sib1_npdsch_cfg, sizeof(srsran_npdsch_cfg_t));
bzero(&npdsch_cfg, sizeof(srsran_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 < SRSRAN_NOF_SF_X_FRAME && (nf < nof_frames || nof_frames == -1); sf_idx++) {
srsran_vec_cf_zero(sf_buffer, sf_n_re);
// Transmit NPBCH in subframe 0
if (sf_idx == 0) {
if ((sfn % SRSRAN_NPBCH_NUM_FRAMES) == 0) {
srsran_npbch_mib_pack(hfn, sfn, mib_nb, bch_payload);
}
srsran_npbch_put_subframe(&npbch, bch_payload, sf_re_symbols, sfn);
if (SRSRAN_VERBOSE_ISDEBUG()) {
printf("MIB payload: ");
srsran_vec_fprint_hex(stdout, bch_payload, SRSRAN_MIB_NB_LEN);
}
}
// Transmit NPSS, NSSS and NRS
if (sf_idx == 5) {
// NPSS at subframe 5
srsran_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
srsran_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", sfn, sf_idx, SRSRAN_REFSIGNAL_NUM_SF(1, cell.nof_ports));
srsran_refsignal_nrs_put_sf(cell, 0, ch_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)) &&
!srsran_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];
srsran_bit_unpack_vector(sib1_nb_payload, unpacked_hfn, 4 * 8);
uint8_t* tmp = unpacked_hfn;
tmp += 12;
srsran_bit_unpack(hfn >> 2, &tmp, 8);
uint8_t packed_hfn[4];
srsran_bit_pack_vector(unpacked_hfn, packed_hfn, 32);
memcpy(sib1_nb_payload, packed_hfn, sizeof(packed_hfn));
if (SRSRAN_VERBOSE_ISDEBUG()) {
printf("SIB1-NB payload: ");
srsran_vec_fprint_byte(stdout, sib1_nb_payload, sizeof(dummy_sib1_payload));
}
}
if (srsran_nbiot_ue_dl_is_sib1_sf(&ue_dl, sfn, sf_idx)) {
INFO("%d.%d: Transmitting SIB1-NB", sfn, sf_idx);
assert(send_data == false);
// configure DL grant for SIB1-NB transmission
if (sib1_npdsch_cfg.sf_idx == 0) {
srsran_ra_nbiot_dl_grant_t grant;
srsran_ra_nbiot_dl_dci_to_grant(&ra_dl_sib1, &grant, sfn, sf_idx, DUMMY_R_MAX, true, cell.mode);
if (srsran_npdsch_cfg(&sib1_npdsch_cfg, cell, &grant, sf_idx)) {
fprintf(stderr, "Error configuring NPDSCH\n");
exit(-1);
}
}
// Encode SIB1 content
if (srsran_npdsch_encode_rnti(
&npdsch, &sib1_npdsch_cfg, &softbuffer, sib1_nb_payload, SRSRAN_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(srsran_npdsch_cfg_t));
}
}
// Update DL resource allocation from control port
if (update_control()) {
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", locations[sf_idx][0].ncce, locations[sf_idx][0].L);
srsran_dci_msg_pack_npdsch(&ra_dl, SRSRAN_DCI_FORMATN1, &dci_msg, false);
if (srsran_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
srsran_ra_nbiot_dl_grant_t grant;
srsran_ra_nbiot_dl_dci_to_grant(&ra_dl, &grant, sfn, sf_idx, DUMMY_R_MAX, false, cell.mode);
if (srsran_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", sfn, sf_idx, npdsch_cfg.grant.mcs[0].tbs);
for (int i = 0; i < npdsch_cfg.grant.mcs[0].tbs / 8; i++) {
data[i] = srsran_random_uniform_int_dist(random_gen, 0, 255);
}
if (SRSRAN_VERBOSE_ISDEBUG()) {
printf("Tx payload: ");
srsran_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",
sf_idx,
npdsch_cfg.sf_idx,
npdsch_cfg.grant.start_sfidx,
npdsch_cfg.grant.nof_sf);
// Encode NPDSCH
if (srsran_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");
npdsch_active = false;
}
}
}
#endif
/* Transform to OFDM symbols */
srsran_ofdm_tx_sf(&ifft);
if (output_file_name && file_snr != -100.0) {
// compute average energy per symbol
float abs_avg = srsran_vec_avg_power_cf(output_buffer, sf_n_samples);
// find the noise spectral density
float snr_lin = srsran_convert_dB_to_power(file_snr);
float n0 = abs_avg / snr_lin;
// add some noise to the signal
srsran_ch_awgn_c(output_buffer, output_buffer, n0, sf_n_samples);
}
/* send to file or usrp */
if (output_file_name) {
// write to file
if (!null_file_sink) {
srsran_filesink_write(&fsink, output_buffer, sf_n_samples);
}
usleep(1000);
} else {
#ifndef DISABLE_RF
// TODO: output scaling needed?
srsran_rf_send2(&radio, 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("%s done\n", argv[0]);
return SRSRAN_SUCCESS;
}