diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index 52eb305824..1e2f8164bd 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -346,6 +346,7 @@ float get_beta_dmrs(int num_cdm_groups_no_data, bool is_type2); #define CEILIDIV(a,b) ((a+b-1)/b) #define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1)) +#define BOUNDED_EVAL(a, b, c) (min(c, max(a, b))) static const char *const duplex_mode_txt[] = {"FDD", "TDD"}; diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index 7541d14436..89aba954e9 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -36,52 +36,73 @@ #define I0_SKIP_DC 1 -int nr_est_timing_advance_srs(const NR_DL_FRAME_PARMS *frame_parms, - const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * frame_parms->ofdm_symbol_size]) +void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, + const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], + uint8_t ant, + uint8_t N_ap, + uint32_t samples_per_frame, + uint16_t *ta_offset, + int16_t *ta_offset_nsec) { - int timing_advance = 0; - int max_val = 0; - - for (int i = 0; i < NR_SRS_IDFT_OVERSAMP_FACTOR * frame_parms->ofdm_symbol_size; i++) { - int temp = 0; - for (int aa = 0; aa < frame_parms->nb_antennas_rx; aa++) { - int Re = ((c16_t*)srs_estimated_channel_time[aa])[i].r; - int Im = ((c16_t*)srs_estimated_channel_time[aa])[i].i; - temp += (Re*Re/2) + (Im*Im/2); + int64_t mean_val = 0; + int64_t max_val = 0; + int32_t max_idx = 0; + int16_t srs_toa = 0; + int16_t ofdm_os_size = NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size; + for (int k = 0; k < ofdm_os_size; k++) { + int64_t abs_val = 0; + for (int p_index = 0; p_index < N_ap; p_index++) { + abs_val += squaredMod(srs_estimated_channel_time[p_index][k]); } - if (temp > max_val) { - timing_advance = i; - max_val = temp; + mean_val += abs_val; + if (abs_val > max_val) { + max_val = abs_val; + max_idx = k; } } + max_val = max_val / N_ap; + mean_val = mean_val / (N_ap * ofdm_os_size); - if (timing_advance > (NR_SRS_IDFT_OVERSAMP_FACTOR * frame_parms->ofdm_symbol_size) / 2) { - timing_advance = timing_advance - (NR_SRS_IDFT_OVERSAMP_FACTOR * frame_parms->ofdm_symbol_size); - } - timing_advance = timing_advance / NR_SRS_IDFT_OVERSAMP_FACTOR; + if (max_idx > ofdm_os_size >> 1) + max_idx = max_idx - ofdm_os_size; - LOG_I(NR_PHY, "Estimated SRS ToA %d\n", timing_advance); + // Check for detection threshold + if ((mean_val != 0) && (max_val / mean_val > NR_SRS_DETECTION_THRESHOLD)) { + srs_toa = max_idx / NR_SRS_IDFT_OVERSAMP_FACTOR; - // Scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size - const uint16_t bw_scaling = frame_parms->ofdm_symbol_size >> 7; + // restrict the computation of ta_offset to antenna 0 + if (ant == 0) { + // Scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size + const uint16_t bw_scaling = ofdm_symbol_size >> 7; - // do some integer rounding to improve TA accuracy - int sync_pos_rounded; - if (timing_advance > 0) { - sync_pos_rounded = timing_advance + (bw_scaling >> 1) - 1; + // do some integer rounding to improve TA accuracy + int sync_pos_rounded; + if (srs_toa > 0) { + sync_pos_rounded = srs_toa + (bw_scaling >> 1) - 1; + } else { + sync_pos_rounded = srs_toa - (bw_scaling >> 1) + 1; + } + + *ta_offset = sync_pos_rounded / bw_scaling; + + // put timing advance command in 0..63 range + *ta_offset = BOUNDED_EVAL(0, *ta_offset + 31, 63); + } + *ta_offset_nsec = (max_idx * 1e9) / (NR_SRS_IDFT_OVERSAMP_FACTOR * samples_per_frame * 100); } else { - sync_pos_rounded = timing_advance - (bw_scaling >> 1) + 1; + *ta_offset = 0xFFFF; + *ta_offset_nsec = 0x8000; } - int timing_advance_update = sync_pos_rounded / bw_scaling; - - // put timing advance command in 0..63 range - timing_advance_update += 31; - - if (timing_advance_update < 0) timing_advance_update = 0; - if (timing_advance_update > 63) timing_advance_update = 63; - - return timing_advance_update; + LOG_D(NR_PHY, + "SRS estimatd ToA %d [RX ant %d]: TA offset %d, TA offset ns %d (max_val %ld, mean_val %ld, max_idx %d)\n", + srs_toa, + ant, + *ta_offset, + *ta_offset_nsec, + max_val, + mean_val, + max_idx); } void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB) { diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index f04fde7b1b..21ecb39427 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -59,9 +59,13 @@ void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB); void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][9]); -int nr_est_timing_advance_srs( - const NR_DL_FRAME_PARMS *frame_parms, - const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * frame_parms->ofdm_symbol_size]); +void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, + const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], + uint8_t ant, + uint8_t N_ap, + uint32_t samples_per_frame, + uint16_t *timing_advance_offset, + int16_t *timing_advance_offset_nsec); void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index b01361ec8d..29ae5bb612 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -243,7 +243,9 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], - int16_t *snr_per_rb); + int16_t *snr_per_rb, + uint16_t *timing_advance_offset, + int16_t *timing_advance_offset_nsec); int get_nr_prach_duration(uint8_t prach_format); diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 5710b24105..f3350d707e 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -47,6 +47,7 @@ #define MAX_NUM_RU_PER_gNB 8 #define MAX_PUCCH0_NID 8 #define NR_SRS_IDFT_OVERSAMP_FACTOR 2 +#define NR_SRS_DETECTION_THRESHOLD 10 typedef struct { int nb_id; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1b04f63950..36dc8a3b5a 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -869,7 +869,9 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], - int16_t *snr_per_rb) + int16_t *snr_per_rb, + uint16_t *timing_advance_offset, + int16_t *timing_advance_offset_nsec) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; @@ -930,6 +932,18 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, snr_per_rb[rb] = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_per_rb[rb] / nb_antennas_rx, 1)); } stop_meas(&gNB->srs_channel_estimation_stats); + + start_meas(&gNB->srs_timing_advance_stats); + for (int ant_rx_ind = 0; ant_rx_ind < nb_antennas_rx; ant_rx_ind++) { + nr_est_srs_timing_advance_offset(ofdm_symbol_size, + srs_estimated_channel_time[ant_rx_ind], + ant_rx_ind, + N_ap, + frame_parms->samples_per_frame, + timing_advance_offset, + &timing_advance_offset_nsec[ant_rx_ind]); + } + stop_meas(&gNB->srs_timing_advance_stats); } T(T_GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE, @@ -1168,6 +1182,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; int16_t snr_per_rb[srs->srs_pdu.bwp_size]; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + uint16_t timing_advance_offset; + int16_t timing_advance_offset_nsec[nb_antennas_rx]; int srs_est; c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); @@ -1186,7 +1202,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, - snr_per_rb); + snr_per_rb, + &timing_advance_offset, + timing_advance_offset_nsec); if ((gNB->srs->snr * 10) < gNB->srs_thres) { srs_est = -1; @@ -1200,13 +1218,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; srs_indication->handle = srs_pdu->handle; srs_indication->rnti = srs_pdu->rnti; - start_meas(&gNB->srs_timing_advance_stats); - srs_indication->timing_advance_offset = - srs_est >= 0 ? nr_est_timing_advance_srs(frame_parms, srs_estimated_channel_time[0]) : 0xFFFF; - stop_meas(&gNB->srs_timing_advance_stats); - srs_indication->timing_advance_offset_nsec = - srs_est >= 0 ? (int16_t)((((int32_t)srs_indication->timing_advance_offset - 31) * ((int32_t)TC_NSEC_x32768)) >> 15) - : 0xFFFF; + srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; + // TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas + srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000; switch (srs_pdu->srs_parameters_v4.usage) { case 0: LOG_W(NR_PHY, "SRS report was not requested by MAC\n"); diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 9f89d29b1d..f83da5941e 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -88,9 +88,8 @@ int main(int argc, char *argv[]) sigaction(SIGINT, &sigint_action, &oldaction); double SNR, snr0 = 0, snr1 = 20.0; - double sum_srs_snr = 0; double sigma, sigma_dB; - double snr_step = 5; + double snr_step = 1; uint8_t snr1set = 0; double **s_re, **s_im, **r_re, **r_im; int trial, n_trials = 1, delay = 0; @@ -128,7 +127,7 @@ int main(int argc, char *argv[]) InitSinLUT(); int c; - while ((c = getopt(argc, argv, "--:O:a:b:c:d:e:f:g:h:i:k:l:m:n:p:s:u:y:z:A:B:C:H:PR:S:L:")) != -1) { + 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') @@ -570,7 +569,8 @@ int main(int argc, char *argv[]) reset_meas(&gNB->srs_channel_estimation_stats); reset_meas(&gNB->srs_timing_advance_stats); - sum_srs_snr = 0; + 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 sigma_dB = 10 * log10(((double)txlev_sum) * ((double)ofdm_symbol_size / (12 * srs_pdu.bwp_size))) - SNR; @@ -627,6 +627,8 @@ int main(int argc, char *argv[]) uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; uint8_t nb_antennas_rx = fp->nb_antennas_rx; int16_t snr_per_rb[srs->srs_pdu.bwp_size]; + uint16_t timing_advance_offset; + int16_t timing_advance_offset_nsec[nb_antennas_rx]; int srs_est; c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][ofdm_symbol_size] __attribute__((aligned(32))); @@ -643,16 +645,41 @@ int main(int argc, char *argv[]) &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, - snr_per_rb); + snr_per_rb, + &timing_advance_offset, + timing_advance_offset_nsec); - start_meas(&gNB->srs_timing_advance_stats); - nr_est_timing_advance_srs(fp, srs_estimated_channel_time[0]); - stop_meas(&gNB->srs_timing_advance_stats); 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 < nb_antennas_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 - sum_srs_snr /= n_trials; - printf("Actual SNR : %f, Estimated SNR from SRS %f (dB)\n", SNR, 10 * log10(sum_srs_snr)); + 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); + + 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; + } if (print_perf == 1) { printf("\ngNB RX\n"); @@ -666,6 +693,10 @@ int main(int argc, char *argv[]) } // SNR loop + printf("*************\n"); + printf("SRS test %s\n", ret == 0 ? "OK" : "FAILED"); + printf("*************\n"); + // free memory for (i = 0; i < n_tx; i++) {