mirror of https://github.com/pvnis/srsRAN_4G.git
adding NB-IoT DL shared channel, UE DL object and PHY examples
parent
890436e07f
commit
e76e31e652
@ -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, ¶m);
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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…
Reference in New Issue