Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for SRS jobs, clarifying the design, and making it thread-safe. Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
663 lines
24 KiB
C
663 lines
24 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "common/utils/nr/nr_common.h"
|
|
#include "common/utils/var_array.h"
|
|
#define inMicroS(a) (((double)(a)) / (get_cpu_freq_GHz() * 1000.0))
|
|
#include "SIMULATION/LTE_PHY/common_sim.h"
|
|
#include "common/utils/assertions.h"
|
|
#include "PHY/INIT/nr_phy_init.h"
|
|
#include "PHY/MODULATION/nr_modulation.h"
|
|
#include "PHY/MODULATION/modulation_common.h"
|
|
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
|
|
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
|
|
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
|
#include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h"
|
|
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
|
|
#include "PHY/NR_ESTIMATION/nr_ul_estimation.h"
|
|
#include "PHY/phy_vars_nr_ue.h"
|
|
#include "e1ap_messages_types.h"
|
|
#include "executables/nr-uesoftmodem.h"
|
|
#include "SIMULATION/NR_PHY/nr_unitary_defs.h"
|
|
#include "SIMULATION/TOOLS/sim.h"
|
|
#include "common/ran_context.h"
|
|
#include "time_meas.h"
|
|
#include "SCHED_NR/phy_frame_config_nr.h"
|
|
|
|
PHY_VARS_gNB *gNB;
|
|
PHY_VARS_NR_UE *UE;
|
|
RAN_CONTEXT_t RC;
|
|
char *uecap_file = NULL;
|
|
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
|
|
|
|
double cpuf;
|
|
uint64_t downlink_frequency[MAX_NUM_CCs][4];
|
|
uint64_t uplink_frequency[MAX_NUM_CCs][4];
|
|
THREAD_STRUCT thread_struct;
|
|
|
|
// needed for some functions
|
|
uint16_t n_rnti = 0x1234;
|
|
openair0_config_t openair0_cfg[MAX_CARDS];
|
|
nrUE_params_t nrUE_params;
|
|
|
|
nrUE_params_t *get_nrUE_params(void)
|
|
{
|
|
return &nrUE_params;
|
|
}
|
|
|
|
channel_desc_t *UE2gNB[MAX_MOBILES_PER_GNB][NUMBER_OF_gNB_MAX];
|
|
configmodule_interface_t *uniqCfg = NULL;
|
|
|
|
NR_IF_Module_t *NR_IF_Module_init(int Mod_id) { return (NULL); }
|
|
|
|
void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
|
|
{
|
|
abort();
|
|
}
|
|
void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
|
{
|
|
abort();
|
|
}
|
|
void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
stop = false;
|
|
__attribute__((unused)) struct sigaction oldaction;
|
|
sigaction(SIGINT, &sigint_action, &oldaction);
|
|
|
|
double SNR, snr0 = 0, snr1 = 20.0;
|
|
double snr_step = 1;
|
|
uint8_t snr1set = 0;
|
|
double **s_re, **s_im, **r_re, **r_im;
|
|
int trial, n_trials = 1, delay = 0;
|
|
double maxDoppler = 0.0;
|
|
uint8_t n_tx = 1, n_rx = 1;
|
|
channel_desc_t *UE2gNB;
|
|
uint8_t extended_prefix_flag = 0;
|
|
SCM_t channel_model = AWGN;
|
|
corr_level_t corr_level = CORR_LEVEL_LOW;
|
|
uint16_t N_RB_DL = 106, N_RB_UL = 106, mu = 1;
|
|
int srs_start_symbol = 13;
|
|
int srs_comb_size = 0;
|
|
int srs_comb_offset = 0;
|
|
int srs_cyclic_shift = 0;
|
|
int nb_symb_srs = 0;
|
|
int i;
|
|
int loglvl = OAILOG_WARNING;
|
|
int print_perf = 0;
|
|
int slot = 8;
|
|
int frame = 1;
|
|
cpuf = get_cpu_freq_GHz();
|
|
double DS_TDL = .03;
|
|
int threequarter_fs = 0;
|
|
uint64_t SSB_positions = 0x01;
|
|
uint16_t Nid_cell = 0;
|
|
|
|
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
|
|
exit_fun("[NR_SRSSIM] Error, configuration module init failed\n");
|
|
}
|
|
|
|
randominit();
|
|
|
|
/* initialize the sin-cos table */
|
|
InitSinLUT();
|
|
|
|
int c;
|
|
while ((c = getopt(argc, argv, "--:O:a:b:c:d:e:f:g:h:i:kl:m:n:p:s:u:y:z:A:B:C:H:PR:S:L:")) != -1) {
|
|
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
|
|
/* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */
|
|
if (c == 1 || c == '-' || c == 'O')
|
|
continue;
|
|
|
|
printf("handling optarg %c\n", c);
|
|
switch (c) {
|
|
case 'a':
|
|
srs_start_symbol = atoi(optarg);
|
|
AssertFatal(srs_start_symbol >= 8 && srs_start_symbol <= 13, "start_symbol %d is not in 8..13\n", srs_start_symbol);
|
|
break;
|
|
|
|
case 'b':
|
|
nb_symb_srs = atoi(optarg);
|
|
AssertFatal(nb_symb_srs == 1 || nb_symb_srs == 2 || nb_symb_srs == 4,
|
|
"number of srs symbols %d is not 1,2,4\n",
|
|
nb_symb_srs);
|
|
nb_symb_srs >>= 1; // Value: 0 = 1 symbol, 1 = 2 symbols, 2 = 4 symbols
|
|
break;
|
|
|
|
case 'c':
|
|
n_rnti = atoi(optarg);
|
|
AssertFatal(n_rnti > 0 && n_rnti <= 65535, "Illegal n_rnti %x\n", n_rnti);
|
|
break;
|
|
|
|
case 'd':
|
|
delay = atoi(optarg);
|
|
break;
|
|
|
|
case 'e':
|
|
srs_comb_size = atoi(optarg);
|
|
AssertFatal(srs_comb_size >= 0 && srs_comb_size < 2, "comb_size %d is not in 0 or 1\n", srs_comb_size);
|
|
break;
|
|
|
|
case 'f':
|
|
srs_comb_offset = atoi(optarg);
|
|
if (srs_comb_size == 0) {
|
|
AssertFatal(srs_comb_offset >= 0 && srs_comb_offset <= 1, "comb_offset %d is not in 0...1\n", srs_comb_offset);
|
|
} else if (srs_comb_size == 1) {
|
|
AssertFatal(srs_comb_offset >= 0 && srs_comb_offset <= 3, "comb_offset %d is not in 0...3\n", srs_comb_offset);
|
|
}
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
switch ((char)*optarg) {
|
|
case 'A':
|
|
channel_model = TDL_A;
|
|
DS_TDL = 0.030; // 30 ns
|
|
printf("Channel model: TDLA30\n");
|
|
break;
|
|
case 'B':
|
|
channel_model = TDL_B;
|
|
DS_TDL = 0.100; // 100ns
|
|
printf("Channel model: TDLB100\n");
|
|
break;
|
|
case 'C':
|
|
channel_model = TDL_C;
|
|
DS_TDL = 0.300; // 300 ns
|
|
printf("Channel model: TDLC300\n");
|
|
break;
|
|
default:
|
|
printf("Unsupported channel model!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (optarg[1] == ',') {
|
|
switch (optarg[2]) {
|
|
case 'l':
|
|
corr_level = CORR_LEVEL_LOW;
|
|
break;
|
|
case 'm':
|
|
corr_level = CORR_LEVEL_MEDIUM;
|
|
break;
|
|
case 'h':
|
|
corr_level = CORR_LEVEL_HIGH;
|
|
break;
|
|
default:
|
|
printf("Invalid correlation level!\n");
|
|
}
|
|
}
|
|
|
|
if (optarg[3] == ',') {
|
|
maxDoppler = atoi(&optarg[4]);
|
|
printf("Maximum Doppler Frequency: %.0f Hz\n", maxDoppler);
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
srs_cyclic_shift = atoi(optarg);
|
|
if (srs_comb_size == 0) {
|
|
AssertFatal(srs_cyclic_shift >= 0 && srs_cyclic_shift <= 7, "comb_shift %d is not in 0...7\n", srs_cyclic_shift);
|
|
} else if (srs_comb_size == 1) {
|
|
AssertFatal(srs_cyclic_shift >= 0 && srs_cyclic_shift <= 11, "comb_shift %d is not in 0...11\n", srs_cyclic_shift);
|
|
}
|
|
break;
|
|
|
|
case 'k':
|
|
printf("Setting threequarter_fs_flag\n");
|
|
threequarter_fs = 1;
|
|
break;
|
|
|
|
case 'n':
|
|
n_trials = atoi(optarg);
|
|
break;
|
|
|
|
case 'p':
|
|
extended_prefix_flag = 1;
|
|
break;
|
|
|
|
case 's':
|
|
snr0 = atof(optarg);
|
|
printf("Setting SNR0 to %f\n", snr0);
|
|
break;
|
|
|
|
case 'u':
|
|
mu = atoi(optarg);
|
|
break;
|
|
|
|
case 'y':
|
|
n_tx = atoi(optarg);
|
|
if ((n_tx == 0) || (n_tx > 4)) {
|
|
printf("Unsupported number of tx antennas %d\n", n_tx);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
|
|
case 'z':
|
|
n_rx = atoi(optarg);
|
|
if ((n_rx == 0) || (n_rx > 8)) {
|
|
printf("Unsupported number of rx antennas %d\n", n_rx);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
|
|
case 'H':
|
|
slot = atoi(optarg);
|
|
break;
|
|
|
|
case 'R':
|
|
N_RB_DL = atoi(optarg);
|
|
N_RB_UL = N_RB_DL;
|
|
break;
|
|
|
|
case 'S':
|
|
snr1 = atof(optarg);
|
|
snr1set = 1;
|
|
printf("Setting SNR1 to %f\n", snr1);
|
|
break;
|
|
|
|
case 'P':
|
|
print_perf = 1;
|
|
cpu_meas_enabled = 1;
|
|
break;
|
|
|
|
case 'L':
|
|
loglvl = atoi(optarg);
|
|
break;
|
|
|
|
default:
|
|
case 'h':
|
|
printf("%s -h(elp)\n", argv[0]);
|
|
printf("-a SRS starting symbol (8 .. 13)\n");
|
|
printf("-b SRS number of symbols (1, 2, 4)\n");
|
|
printf("-c RNTI\n");
|
|
printf("-d Introduce delay in terms of number of samples\n");
|
|
printf("-e SRS comb size, 0 (comb size 2), 1 (comb size 4) \n");
|
|
printf("-f SRS comb offset, 0 .. 1 (comb size 2), 0 .. 3 (comb size 4) \n");
|
|
printf(
|
|
"-g Channel model configuration. Arguments list: Number of arguments = 3, {Channel model: [A] TDLA30, [B] TDLB100, [C] "
|
|
"TDLC300}, {Correlation: [l] Low, [m] Medium, [h] High}, {Maximum Doppler shift} e.g. -g A,l,10\n");
|
|
printf("-h This message\n");
|
|
printf("-i SRS cyclic shift, 0 .. 7 (comb size 2), 0 .. 11 (comb size 4) \n");
|
|
printf("-k 3/4 sampling\n");
|
|
printf("-n Number of trials to simulate\n");
|
|
printf("-p Use extended prefix mode\n");
|
|
printf("-s Starting SNR, runs from SNR0 to SNR0 + 10 dB if ending SNR isn't given\n");
|
|
printf("-S Ending SNR, runs from SNR0 to SNR1\n");
|
|
printf("-u Set the numerology\n");
|
|
printf("-y Number of TX antennas used at UE\n");
|
|
printf("-z Number of RX antennas used at gNB\n");
|
|
printf("-H Slot number\n");
|
|
printf("-L <log level, 0(errors), 1(warning), 2(info) 3(debug) 4 (trace)>\n");
|
|
printf("-P Print SRS performances\n");
|
|
printf("-R Maximum number of available resorce blocks (N_RB_DL)\n");
|
|
exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
logInit();
|
|
set_glog(loglvl);
|
|
|
|
int ret = 1;
|
|
|
|
if (snr1set == 0)
|
|
snr1 = snr0 + 10;
|
|
|
|
double sampling_rate, tx_bandwidth, rx_bandwidth;
|
|
get_samplerate_and_bw(mu, N_RB_DL, threequarter_fs, &sampling_rate, &tx_bandwidth, &rx_bandwidth);
|
|
|
|
// Initialize gNB
|
|
gNB = calloc_or_fail(1, sizeof(PHY_VARS_gNB));
|
|
gNB->ofdm_offset_divisor = UINT_MAX;
|
|
gNB->RU_list[0] = calloc_or_fail(1, sizeof(**gNB->RU_list));
|
|
gNB->RU_list[0]->rfdevice.openair0_cfg = openair0_cfg;
|
|
|
|
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;
|
|
fp->N_RB_DL = N_RB_DL;
|
|
fp->N_RB_UL = N_RB_UL;
|
|
fp->Ncp = extended_prefix_flag ? NR_EXTENDED : NR_NORMAL;
|
|
fp->nb_antennas_tx = n_tx;
|
|
fp->nb_antennas_rx = n_rx;
|
|
fp->threequarter_fs = threequarter_fs;
|
|
nr_phy_config_request_sim(gNB, N_RB_UL, N_RB_UL, mu, Nid_cell, SSB_positions);
|
|
printf("dl freq %" PRIu64 " , ul freq %" PRIu64 " \n", fp->dl_CarrierFreq, fp->ul_CarrierFreq);
|
|
do_tdd_config_sim(gNB, mu);
|
|
phy_init_nr_gNB(gNB);
|
|
|
|
// Initialize UE
|
|
UE = calloc_or_fail(1, sizeof(PHY_VARS_NR_UE));
|
|
memcpy(&UE->frame_parms, fp, sizeof(NR_DL_FRAME_PARMS)); // setting same frame parameters as gNB
|
|
|
|
/* RU handles rxdataF, and gNB just has a pointer. Here, we don't have an RU,
|
|
* so we need to allocate that memory as well. First index in rxdataF[0] index refers to beams*/
|
|
for (i = 0; i < n_rx; i++)
|
|
gNB->common_vars.rxdataF[0][i] = malloc16_clear(fp->samples_per_frame_wCP * sizeof(int32_t));
|
|
|
|
/* no RU: need to have rxdata */
|
|
c16_t **rxdata;
|
|
rxdata = malloc_or_fail(n_rx * sizeof(*rxdata));
|
|
for (i = 0; i < n_rx; i++)
|
|
rxdata[i] = malloc16_clear(fp->samples_per_frame * sizeof(c16_t));
|
|
|
|
/* no memory allocated for UE common_vars: need to have txdata and txdataF */
|
|
c16_t **txdata;
|
|
txdata = malloc_or_fail(n_tx * sizeof(*txdata));
|
|
for (i = 0; i < n_tx; i++)
|
|
txdata[i] = malloc16_clear(fp->samples_per_frame * sizeof(c16_t));
|
|
|
|
c16_t **txdataF;
|
|
txdataF = malloc_or_fail(n_tx * sizeof(*txdataF));
|
|
for (i = 0; i < n_tx; i++)
|
|
txdataF[i] = malloc16_clear(fp->samples_per_frame_wCP * sizeof(c16_t));
|
|
|
|
// Configure channel model
|
|
UE2gNB = new_channel_desc_scm(n_tx,
|
|
n_rx,
|
|
channel_model,
|
|
sampling_rate / 1e6,
|
|
fp->ul_CarrierFreq,
|
|
tx_bandwidth,
|
|
DS_TDL,
|
|
maxDoppler,
|
|
corr_level,
|
|
0,
|
|
delay,
|
|
0,
|
|
0);
|
|
|
|
if (UE2gNB == NULL) {
|
|
printf("Problem generating channel model. Exiting.\n");
|
|
exit(-1);
|
|
}
|
|
|
|
// Allocate memory to intermediate variables to apply channel
|
|
int slot_length = get_samples_per_slot(slot, fp);
|
|
|
|
s_re = malloc_or_fail(n_tx * sizeof(double *));
|
|
s_im = malloc_or_fail(n_tx * sizeof(double *));
|
|
r_re = malloc_or_fail(n_rx * sizeof(double *));
|
|
r_im = malloc_or_fail(n_rx * sizeof(double *));
|
|
|
|
for (int aatx = 0; aatx < n_tx; aatx++) {
|
|
s_re[aatx] = calloc_or_fail(1, slot_length * sizeof(double));
|
|
s_im[aatx] = calloc_or_fail(1, slot_length * sizeof(double));
|
|
}
|
|
|
|
for (int aarx = 0; aarx < n_rx; aarx++) {
|
|
r_re[aarx] = calloc_or_fail(1, slot_length * sizeof(double));
|
|
r_im[aarx] = calloc_or_fail(1, slot_length * sizeof(double));
|
|
}
|
|
|
|
//----------- configure SRS ----------------------------
|
|
long locationAndBandwidth = PRBalloc_to_locationandbandwidth(N_RB_DL, 0);
|
|
|
|
// Configure SRS parameters at gNB
|
|
const uint16_t m_SRS[64] = {4, 8, 12, 16, 16, 20, 24, 24, 28, 32, 36, 40, 48, 48, 52, 56,
|
|
60, 64, 72, 72, 76, 80, 88, 96, 96, 104, 112, 120, 120, 120, 128, 128,
|
|
128, 132, 136, 144, 144, 144, 144, 152, 160, 160, 160, 168, 176, 184, 192, 192,
|
|
192, 192, 208, 216, 224, 240, 240, 240, 240, 256, 256, 256, 264, 272, 272, 272};
|
|
nfapi_nr_srs_pdu_t srs_pdu = {.rnti = n_rnti,
|
|
.bwp_size = NRRIV2BW(locationAndBandwidth, 275),
|
|
.bwp_start = NRRIV2PRBOFFSET(locationAndBandwidth, 275),
|
|
.subcarrier_spacing = fp->numerology_index,
|
|
.cyclic_prefix = extended_prefix_flag,
|
|
.num_ant_ports = n_tx == 4 ? 2
|
|
: n_tx == 2 ? 1
|
|
: 0,
|
|
.num_symbols = nb_symb_srs,
|
|
.num_repetitions = 0, // Value: 0 = 1, 1 = 2, 2 = 4
|
|
.time_start_position = fp->symbols_per_slot - 1 - srs_start_symbol,
|
|
.bandwidth_index = 0,
|
|
.config_index = rrc_get_max_nr_csrs(srs_pdu.bwp_size, srs_pdu.bandwidth_index),
|
|
.sequence_id = 40,
|
|
.comb_size = srs_comb_size,
|
|
.comb_offset = srs_comb_offset,
|
|
.cyclic_shift = srs_cyclic_shift,
|
|
.frequency_position = 0,
|
|
.frequency_shift = 0,
|
|
.frequency_hopping = 0,
|
|
.group_or_sequence_hopping = 0,
|
|
.resource_type = NR_SRS_Resource__resourceType_PR_periodic,
|
|
.t_srs = 1,
|
|
.t_offset = 0,
|
|
.srs_parameters_v4.srs_bandwidth_size = m_SRS[srs_pdu.config_index],
|
|
.srs_parameters_v4.usage = 1 << NR_SRS_ResourceSet__usage_codebook,
|
|
.srs_parameters_v4.report_type[0] = 1,
|
|
.srs_parameters_v4.iq_representation = 1,
|
|
.srs_parameters_v4.prg_size = 1,
|
|
.srs_parameters_v4.num_total_ue_antennas = 1 << srs_pdu.num_ant_ports,
|
|
.beamforming.num_prgs = m_SRS[srs_pdu.config_index],
|
|
.beamforming.prg_size = 1};
|
|
|
|
NR_gNB_SRS_job_t srs_job = {.frame = frame, .slot = slot, .srs_pdu = srs_pdu};
|
|
|
|
// Configure SRS parameters at UE
|
|
fapi_nr_ul_config_srs_pdu srs_config_pdu = {.rnti = srs_pdu.rnti,
|
|
.bwp_size = srs_pdu.bwp_size,
|
|
.bwp_start = srs_pdu.bwp_start,
|
|
.subcarrier_spacing = srs_pdu.subcarrier_spacing,
|
|
.cyclic_prefix = srs_pdu.cyclic_prefix,
|
|
.num_ant_ports = srs_pdu.num_ant_ports,
|
|
.num_symbols = srs_pdu.num_symbols,
|
|
.num_repetitions = srs_pdu.num_repetitions,
|
|
.time_start_position = srs_pdu.time_start_position,
|
|
.bandwidth_index = srs_pdu.bandwidth_index,
|
|
.config_index = srs_pdu.config_index,
|
|
.sequence_id = srs_pdu.sequence_id,
|
|
.comb_size = srs_pdu.comb_size,
|
|
.comb_offset = srs_pdu.comb_offset,
|
|
.cyclic_shift = srs_pdu.cyclic_shift,
|
|
.frequency_position = srs_pdu.frequency_position,
|
|
.frequency_shift = srs_pdu.frequency_shift,
|
|
.frequency_hopping = srs_pdu.frequency_hopping,
|
|
.group_or_sequence_hopping = srs_pdu.group_or_sequence_hopping,
|
|
.resource_type = srs_pdu.resource_type,
|
|
.t_srs = srs_pdu.t_srs,
|
|
.t_offset = srs_pdu.t_offset,
|
|
.beamforming.num_prgs = srs_pdu.beamforming.num_prgs,
|
|
.beamforming.prg_size = srs_pdu.beamforming.prg_size};
|
|
|
|
//----------- UE TX SRS procedures ---------------------
|
|
|
|
UE_nr_rxtx_proc_t proc;
|
|
proc.frame_rx = frame;
|
|
proc.frame_tx = frame;
|
|
proc.nr_slot_tx = slot;
|
|
proc.nr_slot_rx = slot;
|
|
bool was_symbol_used[NR_SYMBOLS_PER_SLOT] = {0};
|
|
int slot_offset = get_samples_slot_timestamp(fp, slot);
|
|
uint16_t ofdm_symbol_size = fp->ofdm_symbol_size;
|
|
int slot_offsetF = (slot % RU_RX_SLOT_DEPTH) * fp->symbols_per_slot * ofdm_symbol_size;
|
|
|
|
c16_t *txF[n_tx];
|
|
for (i = 0; i < n_tx; i++)
|
|
txF[i] = txdataF[i] + slot_offsetF;
|
|
|
|
c16_t *txd[n_tx];
|
|
for (i = 0; i < n_tx; i++)
|
|
txd[i] = txdata[i] + slot_offset;
|
|
|
|
ue_srs_procedures_nr(UE, &proc, (c16_t **)txF, &srs_config_pdu, was_symbol_used);
|
|
|
|
//------------ TX rotation and OFDM Modulation --------------------------
|
|
nr_tx_rotation_and_ofdm_mod(slot, fp, n_tx, txF, txd, link_type_ul, was_symbol_used, false);
|
|
|
|
//----------- Apply propagation channel and add noise ----------------------------
|
|
|
|
double ts = 1.0 / (fp->subcarrier_spacing * ofdm_symbol_size);
|
|
|
|
for (i = 0; i < slot_length; i++) {
|
|
for (int aa = 0; aa < n_tx; aa++) {
|
|
s_re[aa][i] = (double)txdata[aa][slot_offset + i].r;
|
|
s_im[aa][i] = (double)txdata[aa][slot_offset + i].i;
|
|
}
|
|
}
|
|
|
|
// Compute SRS symbol offset and length
|
|
int symbol_offset = slot_offset;
|
|
int abs_first_symbol = slot * fp->symbols_per_slot;
|
|
int idx_sym;
|
|
for (idx_sym = abs_first_symbol; idx_sym < abs_first_symbol + srs_start_symbol; idx_sym++)
|
|
symbol_offset += (idx_sym % (0x7 << fp->numerology_index)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
|
|
|
|
symbol_offset += ofdm_symbol_size * srs_start_symbol;
|
|
int symbol_length = ofdm_symbol_size + (idx_sym % (0x7 << fp->numerology_index)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
|
|
|
|
// Compute transmitter energy level
|
|
double txlev_sum = compute_tx_energy_level(txdata, n_tx, symbol_offset, symbol_length, n_trials);
|
|
|
|
for (SNR = snr0; SNR <= snr1 && !stop; SNR += snr_step) {
|
|
varArray_t *table_rx = initVarArray(1000, sizeof(double));
|
|
reset_meas(&gNB->rx_srs_stats);
|
|
reset_meas(&gNB->generate_srs_stats);
|
|
reset_meas(&gNB->get_srs_signal_stats);
|
|
reset_meas(&gNB->srs_channel_estimation_stats);
|
|
reset_meas(&gNB->srs_timing_advance_stats);
|
|
|
|
double sum_srs_snr = 0;
|
|
int tao_ns_count = 0;
|
|
for (trial = 0; trial < n_trials && !stop; trial++) {
|
|
// Estimate noise power from the transmitter level and SNR
|
|
double sigma = compute_noise_variance(txlev_sum, ofdm_symbol_size, srs_pdu.bwp_size, 1, SNR, n_trials);
|
|
|
|
//----------- Apply propagation channel ----------------------------
|
|
multipath_channel(UE2gNB, s_re, s_im, r_re, r_im, slot_length, 0, (n_trials == 1) ? 1 : 0);
|
|
|
|
//----------- Add noise ----------------------------
|
|
add_noise(rxdata,
|
|
(const double **)r_re,
|
|
(const double **)r_im,
|
|
sigma, // noise power
|
|
slot_length,
|
|
slot_offset,
|
|
ts,
|
|
0, // delay
|
|
0, // pdu_bit_map
|
|
0, // PTRS_BITMAP,
|
|
n_rx);
|
|
|
|
//----------- OFDM Demodulation and RX rotation--------------------------
|
|
nr_ofdm_demod_and_rx_rotation(rxdata,
|
|
gNB->common_vars.rxdataF[0],
|
|
fp,
|
|
n_rx,
|
|
slot,
|
|
slot_offsetF,
|
|
link_type_ul,
|
|
was_symbol_used);
|
|
|
|
//----------- UE RX SRS procedures ---------------------
|
|
|
|
start_meas(&gNB->rx_srs_stats);
|
|
uint8_t N_symb_SRS = 1 << srs_pdu.num_symbols;
|
|
uint8_t N_ap = 1 << srs_pdu.num_ant_ports;
|
|
int16_t snr_per_rb[srs_pdu.bwp_size];
|
|
uint16_t timing_advance_offset;
|
|
int16_t timing_advance_offset_nsec[n_rx];
|
|
int srs_est;
|
|
c16_t srs_estimated_channel_freq[n_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32)));
|
|
|
|
int8_t snr;
|
|
nr_srs_rx_procedures(gNB,
|
|
frame,
|
|
slot,
|
|
n_rx,
|
|
N_ap,
|
|
N_symb_SRS,
|
|
ofdm_symbol_size,
|
|
&srs_job,
|
|
&srs_est,
|
|
&snr,
|
|
srs_estimated_channel_freq,
|
|
snr_per_rb,
|
|
&timing_advance_offset,
|
|
timing_advance_offset_nsec);
|
|
|
|
sum_srs_snr += pow(10, (double)snr / 10.0);
|
|
|
|
int16_t delay_ns = delay * 1e9 / (fp->samples_per_frame * 100);
|
|
for (int ant_idx = 0; ant_idx < n_rx; ant_idx++) {
|
|
if (n_trials == 1)
|
|
printf("[RX ant %d] SRS estimated: TA offset %d, TA offset ns %d \n",
|
|
ant_idx,
|
|
timing_advance_offset,
|
|
timing_advance_offset_nsec[ant_idx]);
|
|
if (timing_advance_offset_nsec[ant_idx] == delay_ns)
|
|
tao_ns_count++;
|
|
}
|
|
stop_meas(&gNB->rx_srs_stats);
|
|
} // trail loop
|
|
float tao_ns_rate = (float)tao_ns_count / (n_trials * n_rx);
|
|
float SRS_SNR_dB = 10 * log10(sum_srs_snr / n_trials);
|
|
printf("Actual SNR : %f, Estimated SNR from SRS %f (dB), TA offset success rate %f %%\n", SNR, SRS_SNR_dB, tao_ns_rate * 100);
|
|
|
|
if (print_perf == 1) {
|
|
printf("\ngNB RX\n");
|
|
printDistribution(&gNB->rx_srs_stats, table_rx, "RX SRS time");
|
|
printStatIndent(&gNB->generate_srs_stats, "Generate SRS sequence time");
|
|
printStatIndent(&gNB->get_srs_signal_stats, "Get SRS signal time");
|
|
printStatIndent(&gNB->srs_channel_estimation_stats, "SRS channel estimation time");
|
|
printStatIndent(&gNB->srs_timing_advance_stats, "SRS timing advance estimation time");
|
|
printf("\n");
|
|
}
|
|
|
|
free(table_rx);
|
|
|
|
int srs_ret = 1;
|
|
if (SNR > 30 && SRS_SNR_dB > 30) {
|
|
srs_ret = 0;
|
|
} else if (SNR >= SRS_SNR_dB) {
|
|
srs_ret = SRS_SNR_dB >= 0.7 * SNR ? 0 : 1;
|
|
} else if (SRS_SNR_dB > SNR) {
|
|
srs_ret = SNR >= 0.7 * SRS_SNR_dB ? 0 : 1;
|
|
}
|
|
|
|
if (tao_ns_rate > 0.9 && srs_ret == 0) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
} // SNR loop
|
|
|
|
printf("*************\n");
|
|
printf("SRS test %s\n", ret == 0 ? "OK" : "FAILED");
|
|
printf("*************\n");
|
|
|
|
// free memory
|
|
|
|
for (i = 0; i < n_tx; i++) {
|
|
free(s_re[i]);
|
|
free(s_im[i]);
|
|
free(txdata[i]);
|
|
free(txdataF[i]);
|
|
}
|
|
free(s_re);
|
|
free(s_im);
|
|
free(txdata);
|
|
free(txdataF);
|
|
|
|
for (i = 0; i < n_rx; i++) {
|
|
free(r_re[i]);
|
|
free(r_im[i]);
|
|
free(rxdata[i]);
|
|
free(gNB->common_vars.rxdataF[0][i]);
|
|
}
|
|
|
|
free(r_re);
|
|
free(r_im);
|
|
free(rxdata);
|
|
|
|
phy_free_nr_gNB(gNB);
|
|
free_channel_desc_scm(UE2gNB);
|
|
free(gNB->RU_list[0]);
|
|
free(UE);
|
|
|
|
return ret;
|
|
}
|