|
|
|
/**
|
|
|
|
* Copyright 2013-2021 Software Radio Systems Limited
|
|
|
|
*
|
|
|
|
* This file is part of srsRAN.
|
|
|
|
*
|
|
|
|
* srsRAN 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.
|
|
|
|
*
|
|
|
|
* srsRAN 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 "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
|
|
|
|
|
|
|
|
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 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 [agmiftlReosncvrpu]\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", 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, "aglfmiosncrtvpuR")) != -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':
|
|
|
|
file_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 'R':
|
|
|
|
cell.is_r14 = !cell.is_r14;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
cell.nbiot_prb = (uint32_t)strtol(argv[optind], NULL, 10);
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
srsran_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 = srsran_vec_cf_malloc(sf_n_re);
|
|
|
|
if (!sf_buffer) {
|
|
|
|
perror("malloc");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
output_buffer = srsran_vec_cf_malloc(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 (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
|
|
|
|
printf("Opening RF device...\n");
|
|
|
|
if (srsran_rf_open(&radio, 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 (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("Type new MCS index and press Enter: ");
|
|
|
|
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 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[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, "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) {
|
|
|
|
int srate = srsran_sampling_freq_hz(cell.base.nof_prb);
|
|
|
|
if (srate != -1) {
|
|
|
|
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
|
|
|
|
float srate_rf = srsran_rf_set_tx_srate(&radio, (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);
|
|
|
|
}
|
|
|
|
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()) {
|
|
|
|
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;
|
|
|
|
float nstd = sqrtf(n0 / 2);
|
|
|
|
|
|
|
|
// add some noise to the signal
|
|
|
|
srsran_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) {
|
|
|
|
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("Done\n");
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|