Merge remote-tracking branch 'origin/srs_phy_simulator' into integration_2026_w03 (!3718)

Introduce SRS physical simulator and improvements

This MR consists of two parts:

- Introduces SRS physical simulator to test different configurations of
  SRS
- Improvements in the SRS channel estimation (Addressing the comments
  from old, unfinished MR !2875)
  - rewrite nr_srs_channel_estimation() to loop over rx antennas and
    antenna ports to be able to parallelize
  - Improvements in timing_advance_offset and timing_advance_offset_nsec
    computation
  - Introduce SRS multi-symbol channel estimation
    - The channel impulse response is estimated from the averaged
      channel frequency response over multiple symbols
    - The channel frequency estimates are left as they are to be able to
      estimate doppler in the future
  - Simplify SRS channel estimation to handle memory alignment in the
    case of different SRS configurations (eg: comboffset)

To use multiple symbols when running gNB, the following parameters needs
to be edited in the function get_srs_resource()

- srs_res->resourceMapping.startPosition with a range from {0,1,..,5}
  corresponds to the start symbol {13, 12, .. 8} with in a slot
- srs_res->resourceMapping.nrofSymbols indicates the number of symbols
  {n1, n2, and n4}

Make sure the that start symbol + number of symbols is < 14

The timing results for SRS in develop and srs_phy_simulator are as follows:

Command used: sudo ./nr_ulsim -n1000 -s50 -S50 -R273 -W2 -y2 -z2 -E 1 -P

develop

|__ RX SRS time                            124.94 us (1000 trials)		(124.94 total [ms])
    |__ Generate SRS sequence time           0.05 us (1000 trials)		(  0.05 total [ms])
    |__ Get SRS signal time                 19.41 us (1000 trials)		( 19.41 total [ms])
    |__ SRS channel estimation time         89.11 us (1000 trials)		( 89.11 total [ms])
    |__ SRS timing advance estimation time  13.10 us (1000 trials)		( 13.10 total [ms])
    |__ SRS report TLV build time            2.80 us (1000 trials)		(  2.80 total [ms])
        |__ SRS beam report build time       0.00 us (  0 trials)
        |__ SRS IQ matrix build time         2.73 us (1000 trials)

srs_phy_simulator

|__ RX SRS time                            211.93 us (1000 trials)		(211.93 total [ms])
    |__ Generate SRS sequence time           0.04 us (1000 trials)		(  0.04 total [ms])
    |__ Get SRS signal time                 19.55 us (1000 trials)		( 19.55 total [ms])
    |__ SRS channel estimation time        152.05 us (1000 trials)		(152.05 total [ms])
    |__ SRS timing advance estimation time  36.67 us (1000 trials)		( 36.67 total [ms])
    |__ SRS report TLV build time            2.96 us (1000 trials)		(  2.96 total [ms])
        |__ SRS beam report build time       0.00 us (  0 trials)
        |__ SRS IQ matrix build time         2.88 us (1000 trials)

Note that in srs_phy_simulator, to compute timing offset ns, an
oversampling factor of 2 is performed. Also, for peak detection, we
average the channel estimates for every port.
This commit is contained in:
Robert Schmidt
2026-01-13 13:39:32 +01:00
36 changed files with 1493 additions and 535 deletions
+11 -14
View File
@@ -324,7 +324,7 @@ int main(int argc, char **argv)
setbuf(stdout, NULL);
int c;
int i,aa;//,l;
double sigma2, sigma2_dB=10, SNR, snr0=-2.0, snr1=2.0;
double SNR, snr0 = -2.0, snr1 = 2.0;
uint8_t snr1set=0;
double effRate;
//float psnr;
@@ -1172,15 +1172,12 @@ int main(int argc, char **argv)
}
}
int txlev[n_tx];
int txlev_sum = 0;
// Compute transmitter energy level
int l_ofdm = 6;
for (aa=0; aa<n_tx; aa++) {
txlev[aa] = signal_energy((int32_t *)&txdata[aa][slot_offset +l_ofdm*frame_parms->ofdm_symbol_size + (l_ofdm-1)*frame_parms->nb_prefix_samples + frame_parms->nb_prefix_samples0],
frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples);
txlev_sum += txlev[aa];
if (n_trials==1) printf("txlev[%d] = %d (%f dB) txlev_sum %d\n",aa,txlev[aa],10*log10((double)txlev[aa]),txlev_sum);
}
int symbol_offset = slot_offset + l_ofdm * frame_parms->ofdm_symbol_size + (l_ofdm - 1) * frame_parms->nb_prefix_samples
+ frame_parms->nb_prefix_samples0;
int symbol_length = frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples;
double txlev_sum = compute_tx_energy_level(txdata, n_tx, symbol_offset, symbol_length, n_trials);
for (i = 0; i < slot_length; i++) {
for (aa=0; aa<frame_parms->nb_antennas_tx; aa++) {
@@ -1189,11 +1186,11 @@ int main(int argc, char **argv)
}
}
double ts = 1.0/(frame_parms->subcarrier_spacing * frame_parms->ofdm_symbol_size);
//Compute AWGN variance
sigma2_dB = 10 * log10((double)txlev_sum * ((double)UE->frame_parms.ofdm_symbol_size/(12*pdsch_pdu_rel15->rbSize))) - SNR;
sigma2 = pow(10, sigma2_dB/10);
if (n_trials==1) printf("sigma2 %f (%f dB), txlev_sum %f (factor %f)\n",sigma2,sigma2_dB,10*log10((double)txlev_sum),(double)(double)UE->frame_parms.ofdm_symbol_size/(12*pdsch_pdu_rel15->rbSize));
double ts = 1.0/(frame_parms->subcarrier_spacing * frame_parms->ofdm_symbol_size);
// Estimate noise power from the transmitter level and SNR
double sigma2 =
compute_noise_variance(txlev_sum, UE->frame_parms.ofdm_symbol_size, pdsch_pdu_rel15->rbSize, 1, SNR, n_trials);
for (aa = 0; aa < n_rx; aa++) {
bzero(r_re[aa], slot_length * sizeof(double));
+2 -9
View File
@@ -131,7 +131,7 @@ int main(int argc, char **argv){
sigaction(SIGINT, &sigint_action, &oldaction);
get_softmodem_params()->sl_mode = 0;
double sigma2, sigma2_dB = 0, SNR, snr0 = -2.0, snr1 = 0.0, ue_speed0 = 0.0, ue_speed1 = 0.0;
double SNR, snr0 = -2.0, snr1 = 0.0, ue_speed0 = 0.0, ue_speed1 = 0.0;
double **s_re, **s_im, **r_re, **r_im, iqim = 0.0, delay_avg = 0, ue_speed = 0, fs=-1, bw;
int i, l, aa, aarx, trial, n_frames = 1, rx_prach_start; //, ntrials=1;
c16_t **txdata;
@@ -728,14 +728,7 @@ int main(int argc, char **argv){
for (trial = 0; trial < n_frames && !stop; trial++) {
if (input_fd == NULL) {
sigma2_dB = 10*log10((double)tx_lev) - SNR - 10*log10(N_RB_UL*12/N_ZC);
if (n_frames==1)
printf("sigma2_dB %f (SNR %f dB) tx_lev_dB %f\n",sigma2_dB,SNR,10*log10((double)tx_lev));
//AWGN
sigma2 = pow(10,sigma2_dB/10);
// printf("Sigma2 %f (sigma2_dB %f)\n",sigma2,sigma2_dB);
double sigma2 = compute_noise_variance(tx_lev, N_ZC, N_RB_UL, 1, SNR, n_frames);
if (awgn_flag == 0) {
multipath_tv_channel(UE2gNB, s_re, s_im, r_re, r_im, frame_parms->samples_per_frame, 0);
+17 -7
View File
@@ -96,7 +96,7 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sigint_action, &oldaction);
int i;//,l;
double sigma2, sigma2_dB=10,SNR,snr0=-2.0,snr1=2.0;
double SNR, snr0 = -2.0, snr1 = 2.0;
double cfo=0;
uint8_t snr1set=0;
double **s_re,**s_im,**r_re,**r_im;
@@ -566,15 +566,16 @@ int main(int argc, char **argv)
// SNR Computation
// standard says: SNR = S / N, where S is the total signal energy, N is the noise energy in the transmission bandwidth (i.e. N_RB_DL resource blocks)
// txlev = S.
int txlev = signal_energy((int32_t *)&txdataF[0][startingSymbolIndex*frame_parms->ofdm_symbol_size], frame_parms->ofdm_symbol_size);
// Compute transmitter energy level
int symbol_offset = startingSymbolIndex * frame_parms->ofdm_symbol_size;
int symbol_length = frame_parms->ofdm_symbol_size;
double txlev = compute_tx_energy_level(txdataF, 1, symbol_offset, symbol_length, n_trials);
// sigma2 is variance per dimension, so N/(N_RB*12)
// so, sigma2 = N/(N_RB_DL*12) => (S/SNR)/(N_RB*12)
int N_RB = (format == 0 || format == 1) ? 1 : nrofPRB;
sigma2_dB = 10*log10(txlev*(N_RB_DL/N_RB))-SNR;
sigma2 = pow(10.0,sigma2_dB/10.0);
if (n_trials==1) printf("txlev %d (%f dB), offset %d, sigma2 %f ( %f dB)\n",txlev,10*log10(txlev),startingSymbolIndex*frame_parms->ofdm_symbol_size,sigma2,sigma2_dB);
double sigma2 = compute_noise_variance(txlev, N_RB_DL * 12, N_RB, 1, SNR, n_trials);
for (int symb=0; symb<gNB->frame_parms.symbols_per_slot;symb++) {
if (symb<startingSymbolIndex || symb >= startingSymbolIndex+nrofSymbols) {
@@ -648,7 +649,16 @@ int main(int argc, char **argv)
gNB_I0_measurements(gNB, nr_slot_tx, 0, gNB->frame_parms.symbols_per_slot, rb_mask_ul);
start_meas(&gNB->phy_proc_rx);
if (n_trials==1) printf("noise rxlev %d (%d dB), rxlev pucch %d dB sigma2 %f dB, SNR %f, TX %f, I0 (pucch) %d, I0 (avg) %d\n",rxlev,dB_fixed(rxlev),dB_fixed(rxlev_pucch),sigma2_dB,SNR,10*log10((double)txlev*UE->frame_parms.ofdm_symbol_size/12),gNB->measurements.n0_subband_power_tot_dB[startingPRB],gNB->measurements.n0_subband_power_avg_dB);
if (n_trials == 1)
printf("noise rxlev %d (%d dB), rxlev pucch %d dB sigma2 %f dB, SNR %f, TX %f, I0 (pucch) %d, I0 (avg) %d\n",
rxlev,
dB_fixed(rxlev),
dB_fixed(rxlev_pucch),
10 * log10(sigma2),
SNR,
10 * log10((double)txlev * UE->frame_parms.ofdm_symbol_size / 12),
gNB->measurements.n0_subband_power_tot_dB[startingPRB],
gNB->measurements.n0_subband_power_avg_dB);
if(format==0){
nfapi_nr_uci_pucch_pdu_format_0_1_t uci_pdu;
nfapi_nr_pucch_pdu_t pucch_pdu;
+690
View File
@@ -0,0 +1,690 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#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;
int32_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;
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;
uint32_t samples;
get_samplerate_and_bw(mu, N_RB_DL, threequarter_fs, &sampling_rate, &samples, &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 ? EXTENDED : 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};
gNB->srs->srs_pdu = srs_pdu;
gNB->srs->active = true;
gNB->srs->beam_nb = 0;
gNB->srs->frame = frame;
gNB->srs->slot = slot;
// 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};
nr_phy_data_tx_t phy_data = {0};
phy_data.srs_vars.active = true;
phy_data.srs_vars.srs_config_pdu = srs_config_pdu;
UE->nr_srs_info = malloc16_clear(sizeof(nr_srs_info_t));
//----------- 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_NUMBER_OF_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, &phy_data, 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);
NR_gNB_SRS_t *srs = &gNB->srs[0];
uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols;
uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports;
int16_t snr_per_rb[srs->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)));
c16_t srs_estimated_channel_time[n_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32)));
nr_srs_rx_procedures(gNB,
frame,
slot,
n_rx,
N_ap,
N_symb_SRS,
ofdm_symbol_size,
srs,
gNB->nr_srs_info[0],
&srs_est,
srs_estimated_channel_freq,
srs_estimated_channel_time,
snr_per_rb,
&timing_advance_offset,
timing_advance_offset_nsec);
sum_srs_snr += pow(10, (double)gNB->srs->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_and_zero(UE->nr_srs_info);
free(gNB->RU_list[0]);
free(UE);
return ret;
}
+26 -46
View File
@@ -281,7 +281,6 @@ int main(int argc, char *argv[])
char *filename_csv = NULL;
int i;
double SNR, snr0 = -2.0, snr1 = 2.0;
double sigma, sigma_dB;
double snr_step = .2;
uint8_t snr1set = 0;
int slot = 8, frame = 1;
@@ -308,7 +307,7 @@ int main(int argc, char *argv[])
int Imcs = 9;
uint8_t precod_nbr_layers = 1;
int tx_offset;
int32_t txlev_sum = 0, atxlev[4];
double txlev_sum = 0;
int start_rb = 0;
int UE_id = 0;
int print_perf = 0;
@@ -1273,7 +1272,7 @@ int main(int argc, char *argv[])
srs_pdu->subcarrier_spacing = gNB->frame_parms.subcarrier_spacing;
srs_pdu->num_ant_ports = n_tx == 4 ? 2 : n_tx == 2 ? 1 : 0;
srs_pdu->sequence_id = 40;
srs_pdu->time_start_position = gNB->frame_parms.symbols_per_slot - 1;
srs_pdu->time_start_position = 0;
srs_pdu->config_index = rrc_get_max_nr_csrs(srs_pdu->bwp_size, srs_pdu->bandwidth_index);
srs_pdu->resource_type = NR_SRS_Resource__resourceType_PR_periodic;
srs_pdu->t_srs = 1;
@@ -1371,6 +1370,7 @@ int main(int argc, char *argv[])
srs_config_pdu->sequence_id = 40;
srs_config_pdu->resource_type = NR_SRS_Resource__resourceType_PR_periodic;
srs_config_pdu->t_srs = 1;
srs_config_pdu->time_start_position = 0;
}
for (int i = 0; i < (TBS / 8); i++)
@@ -1400,36 +1400,22 @@ int main(int argc, char *argv[])
}
///////////
////////////////////////////////////////////////////
// Compute transmitter energy level
tx_offset = get_samples_slot_timestamp(&gNB->frame_parms, slot);
txlev_sum = 0;
for (int aa = 0; aa < UE->frame_parms.nb_antennas_tx; aa++) {
atxlev[aa] =
signal_energy((int32_t *)&UE->common_vars
.txData[aa][tx_offset + 5 * gNB->frame_parms.ofdm_symbol_size
+ 4 * gNB->frame_parms.nb_prefix_samples + gNB->frame_parms.nb_prefix_samples0],
gNB->frame_parms.ofdm_symbol_size + gNB->frame_parms.nb_prefix_samples);
txlev_sum += atxlev[aa];
if (n_trials == 1)
printf("txlev[%d] = %d (%f dB) txlev_sum %d\n", aa, atxlev[aa], 10 * log10((double)atxlev[aa]), txlev_sum);
}
int symbol_offset = tx_offset + 5 * gNB->frame_parms.ofdm_symbol_size + 4 * gNB->frame_parms.nb_prefix_samples
+ gNB->frame_parms.nb_prefix_samples0;
int symbol_length = gNB->frame_parms.ofdm_symbol_size + gNB->frame_parms.nb_prefix_samples;
txlev_sum = compute_tx_energy_level(UE->common_vars.txData,
UE->frame_parms.nb_antennas_tx,
symbol_offset,
symbol_length,
n_trials);
} else
n_trials = 1;
if (input_fd == NULL) {
// Justification of division by precod_nbr_layers:
// When the channel is the identity matrix, the results in terms of SNR should be almost equal for 2x2 and 4x4.
sigma_dB =
10 * log10((double)txlev_sum / precod_nbr_layers * ((double)gNB->frame_parms.ofdm_symbol_size / (12 * nb_rb))) - SNR;
sigma = pow(10, sigma_dB / 10);
if (n_trials == 1)
printf("sigma %f (%f dB), txlev_sum %f (factor %f)\n",
sigma,
sigma_dB,
10 * log10((double)txlev_sum),
(double)(double)gNB->frame_parms.ofdm_symbol_size / (12 * nb_rb));
double sigma =
compute_noise_variance(txlev_sum, gNB->frame_parms.ofdm_symbol_size, nb_rb, precod_nbr_layers, SNR, n_trials);
for (i = 0; i < slot_length; i++) {
for (int aa = 0; aa < UE->frame_parms.nb_antennas_tx; aa++) {
@@ -1460,26 +1446,20 @@ int main(int argc, char *argv[])
UL_INFO.crc_ind.number_crcs = 0;
UL_INFO.srs_ind.number_of_pdus = 0;
for(uint8_t symbol = 0; symbol < (gNB->frame_parms.Ncp == EXTENDED ? 12 : 14); symbol++) {
for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++)
nr_slot_fep_ul(&gNB->frame_parms,
(int32_t *)rxdata[aa],
(int32_t *)gNB->common_vars.rxdataF[0][aa],
symbol,
slot,
0);
}
//----------- OFDM Demodulation and RX rotation--------------------------
bool was_symbol_used[14] = {0};
int offset = (slot & 3) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size;
for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++) {
const unsigned int max_symb = (gNB->frame_parms.Ncp == EXTENDED) ? 12 : 14;
for (int sym = 0; sym < max_symb; sym++)
apply_nr_rotation_symbol_RX(&gNB->frame_parms,
gNB->common_vars.rxdataF[0][aa] + offset + sym * gNB->frame_parms.ofdm_symbol_size,
gNB->frame_parms.symbol_rotation[1],
gNB->frame_parms.N_RB_UL,
slot,
sym);
for (int i = 0; i < 14; i++) {
was_symbol_used[i] = true;
}
nr_ofdm_demod_and_rx_rotation(rxdata,
gNB->common_vars.rxdataF[0],
&gNB->frame_parms,
gNB->frame_parms.nb_antennas_rx,
slot,
offset,
link_type_ul,
was_symbol_used);
ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot, &UL_INFO);
@@ -68,7 +68,7 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
#ifdef DEBUG_CH
for (l = 0; l<(int)desc->channel_length; l++) {
for (int l = 0; l<(int)desc->channel_length; l++) {
printf("%p (%f,%f) ",desc->ch[0],desc->ch[0][l].x,desc->ch[0][l].y);
}
@@ -168,7 +168,7 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
}
#ifdef DEBUG_CH
for (l = 0; l<(int)desc->channel_length; l++) {
for (int l = 0; l<(int)desc->channel_length; l++) {
printf("ch[%i] = (%f, %f)\n", l, desc->ch[0][l].r, desc->ch[0][l].i);
}
#endif
+21
View File
@@ -341,6 +341,27 @@ add_physim_test(physim.5g.nr_prachsim.test8 "15kHz SCS, 25 PRBs" nr_prachsim -a
add_physim_test(physim.5g.nr_psbchsim.test1 "SLSS Search" nr_psbchsim -I)
add_physim_test(physim.5g.nr_psbchsim.test2 "PSBCH TxRx" nr_psbchsim -n 30)
####################################################################################
###### nr_srssim unit test ######
####################################################################################
add_physim_test(physim.5g.nr_srssim.test1 "SRS: 30KHz SCS, 106 PRBs, ToA 24, Combsize 2" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 24)
add_physim_test(physim.5g.nr_srssim.test2 "SRS: 30KHz SCS, 106 PRBs, ToA 15, Combsize 2, Threequarter sampling" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 15 -k)
add_physim_test(physim.5g.nr_srssim.test3 "SRS: 30KHz SCS, 217 PRBs, ToA 8, Combsize 2" nr_srssim -n300 -R217 -s0 -S5 -y2 -z2 -d 8)
add_physim_test(physim.5g.nr_srssim.test4 "SRS: 30KHz SCS, 273 PRBs, TOA 30, Combsize 2" nr_srssim -n300 -R273 -s0 -S5 -y2 -z2 -d 30)
add_physim_test(physim.5g.nr_srssim.test5 "SRS: 30KHz SCS, 106 PRBs, ToA 16, Combsize 2, Cyclic shift 3" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 16 -i 1)
add_physim_test(physim.5g.nr_srssim.test6 "SRS: 30KHz SCS, 106 PRBs, ToA 23, Combsize 2, Comb offset 1" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 23 -f 1)
add_physim_test(physim.5g.nr_srssim.test7 "SRS: 30KHz SCS, 106 PRBs, ToA 8, Combsize 2, Symbol start 9" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 8 -a 9)
add_physim_test(physim.5g.nr_srssim.test8 "SRS: 120KHz SCS, 32 PRBs, ToA 19, Combsize 2" nr_srssim -n300 -R32 -s0 -S5 -y2 -z2 -d 19 -u 3)
add_physim_test(physim.5g.nr_srssim.test9 "SRS: 30KHz SCS, 106 PRBs, ToA 11, Combsize 4" nr_srssim -n300 -R106 -s5 -S10 -y2 -z2 -d 11 -e 1)
add_physim_test(physim.5g.nr_srssim.test10 "SRS: 30KHz SCS, 106 PRBs, ToA 7, Combsize 4, Comb offset 1" nr_srssim -n300 -R106 -s5 -S10 -y2 -z2 -d 7 -e 1 -f 1)
add_physim_test(physim.5g.nr_srssim.test11 "SRS: 30KHz SCS, 106 PRBs, ToA 24, Combsize 2, SRS symbols 2, SRS start symbol 8" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 24 -b 2 -a 8)
add_physim_test(physim.5g.nr_srssim.test12 "SRS: 30KHz SCS, 106 PRBs, ToA 24, Combsize 2, SRS symbols 4, SRS start symbol 8" nr_srssim -n300 -R106 -s0 -S5 -y2 -z2 -d 24 -b 4 -a 8)
add_physim_test(physim.5g.nr_srssim.test13 "SRS: 30KHz SCS, 106 PRBs, ToA 24, Combsize 4, SRS symbols 2, SRS start symbol 8" nr_srssim -n300 -R106 -s5 -S10 -y2 -z2 -d 24 -b 2 -e1 -a 8)
add_physim_test(physim.5g.nr_srssim.test14 "SRS: 30KHz SCS, 106 PRBs, ToA 24, Combsize 4, SRS symbols 4, SRS start symbol 8" nr_srssim -n300 -R106 -s5 -S10 -y2 -z2 -d 24 -b 4 -e1 -a 8)
add_physim_test(physim.5g.nr_srssim.test15 "SRS: 30KHz SCS, 217 PRBs, ToA 8, Combsize 2, SRS symbols 4, SRS start symbol 8" nr_srssim -n300 -R217 -s0 -S5 -y2 -z2 -d 8 -b 4 -a 8)
add_physim_test(physim.5g.nr_srssim.test16 "SRS: 30KHz SCS, 106 PRBs, SNR 2 dB to 30 dB in steps of 5 dB, ToA 24, Combsize 2" nr_srssim -n300 -R106 -s2 -S30 -y2 -z2 -d 24)
####################################################################################
###### offload-specific tests ######
####################################################################################